Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 13 additions & 6 deletions apps/opencode-plugin/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
*/

import { type Plugin, tool } from "@opencode-ai/plugin";
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "fs";
import { homedir } from "os";
import path from "path";

// OpenCode's @hono/node-server patches global.Response with a polyfill that
Expand Down Expand Up @@ -67,6 +68,7 @@ import { composeImproveContext } from "@plannotator/shared/pfm-reminder";
import {
stripConflictingPlanModeRules,
} from "./plan-mode";
import { sanitizeTag } from "@plannotator/shared/project";
import {
applyWorkflowConfig,
isPlanningAgent,
Expand Down Expand Up @@ -127,12 +129,13 @@ interface PlanEdit {
}

/**
* Backing file for the current plan. Managed entirely by the plugin;
* Backing file for the current plan. Stored outside the workspace in
* `~/.plannotator/active/{project}/_active-plan.md` so it never appears
* in git status or editor file trees. Managed entirely by the plugin;
* the agent never sees or touches this file directly.
*/
export function getPlanBackingPath(directory: string): string {
const planDir = path.join(directory, ".opencode", "plans");
return path.join(planDir, "_active-plan.md");
export function getPlanBackingPath(project: string): string {
return path.join(homedir(), ".plannotator", "active", project, "_active-plan.md");
}

/**
Expand Down Expand Up @@ -560,7 +563,8 @@ Use /plannotator-last or /plannotator-annotate for manual review, or set workflo
}

// Read existing backing file (empty on first call)
const backingPath = getPlanBackingPath(ctx.directory);
const project = sanitizeTag(path.basename(ctx.directory)) || "_unknown";
const backingPath = getPlanBackingPath(project);
const backingDir = path.dirname(backingPath);
mkdirSync(backingDir, { recursive: true });

Expand Down Expand Up @@ -636,6 +640,9 @@ Use /plannotator-last or /plannotator-annotate for manual review, or set workflo
server.stop();

if (result.approved) {
// Clean up backing file after approval
try { unlinkSync(backingPath); } catch { /* already gone */ }

const shouldSwitchAgent = result.agentSwitch && result.agentSwitch !== 'disabled';
const targetAgent = result.agentSwitch || 'build';

Expand Down
14 changes: 8 additions & 6 deletions apps/opencode-plugin/submit-plan.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { describe, expect, test } from "bun:test";
import { homedir } from "os";
import path from "path";
import {
applyEdits,
formatWithLineNumbers,
Expand Down Expand Up @@ -162,14 +164,14 @@ describe("formatWithLineNumbers", () => {
// ── getPlanBackingPath ─────────────────────────────────────────────────────

describe("getPlanBackingPath", () => {
test("returns path inside .opencode/plans within the given directory", () => {
const result = getPlanBackingPath("/some/project");
expect(result).toBe("/some/project/.opencode/plans/_active-plan.md");
test("returns path inside ~/.plannotator/active/{project}/_active-plan.md", () => {
const result = getPlanBackingPath("myproject");
expect(result).toBe(path.join(homedir(), ".plannotator", "active", "myproject", "_active-plan.md"));
});

test("uses the provided directory as the root", () => {
const result = getPlanBackingPath("/home/user/myproject");
expect(result).toContain("/home/user/myproject");
test("uses the provided project name as the directory segment", () => {
const result = getPlanBackingPath("some-project");
expect(result).toContain(path.join(".plannotator", "active", "some-project"));
expect(result).toContain("_active-plan.md");
});
});
Loading