@plannotator/opencode 0.19.2 → 0.19.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -41684,6 +41684,24 @@ import { homedir as homedir3 } from "os";
41684
41684
  import { join as join5 } from "path";
41685
41685
  import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync3, existsSync as existsSync5 } from "fs";
41686
41686
  import { execSync } from "child_process";
41687
+ var PROMPT_SECTIONS = ["review", "plan", "annotate"];
41688
+ function mergePromptConfig(current, partial) {
41689
+ if (!current && !partial)
41690
+ return;
41691
+ const result = { ...current, ...partial };
41692
+ for (const section of PROMPT_SECTIONS) {
41693
+ const cur = current?.[section];
41694
+ const par = partial?.[section];
41695
+ if (cur || par) {
41696
+ result[section] = {
41697
+ ...cur,
41698
+ ...par,
41699
+ runtimes: cur?.runtimes || par?.runtimes ? { ...cur?.runtimes, ...par?.runtimes } : undefined
41700
+ };
41701
+ }
41702
+ }
41703
+ return result;
41704
+ }
41687
41705
  var CONFIG_DIR = join5(homedir3(), ".plannotator");
41688
41706
  var CONFIG_PATH = join5(CONFIG_DIR, "config.json");
41689
41707
  function loadConfig() {
@@ -41703,7 +41721,13 @@ function saveConfig(partial) {
41703
41721
  try {
41704
41722
  const current = loadConfig();
41705
41723
  const mergedDiffOptions = current.diffOptions || partial.diffOptions ? { ...current.diffOptions, ...partial.diffOptions } : undefined;
41706
- const merged = { ...current, ...partial, diffOptions: mergedDiffOptions };
41724
+ const mergedPrompts = mergePromptConfig(current.prompts, partial.prompts);
41725
+ const merged = {
41726
+ ...current,
41727
+ ...partial,
41728
+ diffOptions: mergedDiffOptions,
41729
+ prompts: mergedPrompts
41730
+ };
41707
41731
  mkdirSync3(CONFIG_DIR, { recursive: true });
41708
41732
  writeFileSync2(CONFIG_PATH, JSON.stringify(merged, null, 2) + `
41709
41733
  `, "utf-8");
@@ -46477,7 +46501,7 @@ function stackPRQuery(kind) {
46477
46501
  query($owner: String!, $repo: String!, $${varName}: String!) {
46478
46502
  repository(owner: $owner, name: $repo) {
46479
46503
  pullRequests(first: ${first}, ${varName}: $${varName}, states: [OPEN, MERGED]) {
46480
- nodes { number title url baseRefName headRefName }
46504
+ nodes { number title url baseRefName headRefName state }
46481
46505
  }
46482
46506
  }
46483
46507
  }`;
@@ -46534,7 +46558,8 @@ async function fetchGhPRStack(runtime2, ref, metadata) {
46534
46558
  title: pr.title,
46535
46559
  url: pr.url,
46536
46560
  isCurrent: false,
46537
- isDefaultBranch: false
46561
+ isDefaultBranch: false,
46562
+ state: pr.state === "MERGED" ? "merged" : pr.state === "CLOSED" ? "closed" : "open"
46538
46563
  });
46539
46564
  nextHead = pr.baseRefName;
46540
46565
  }
@@ -46551,7 +46576,8 @@ async function fetchGhPRStack(runtime2, ref, metadata) {
46551
46576
  title: pr.title,
46552
46577
  url: pr.url,
46553
46578
  isCurrent: false,
46554
- isDefaultBranch: false
46579
+ isDefaultBranch: false,
46580
+ state: pr.state === "MERGED" ? "merged" : pr.state === "CLOSED" ? "closed" : "open"
46555
46581
  });
46556
46582
  nextBase = pr.headRefName;
46557
46583
  }
@@ -48288,6 +48314,175 @@ async function startAnnotateServer(options) {
48288
48314
  };
48289
48315
  }
48290
48316
 
48317
+ // ../../packages/shared/prompts.ts
48318
+ function resolveTemplate(template, vars) {
48319
+ return template.replace(/\{\{(\w+)\}\}/g, (match, key) => {
48320
+ const val = vars[key];
48321
+ return val !== undefined ? val : match;
48322
+ });
48323
+ }
48324
+ var PLAN_TOOL_NAMES = {
48325
+ "claude-code": "ExitPlanMode",
48326
+ opencode: "submit_plan",
48327
+ "copilot-cli": "exit_plan_mode",
48328
+ pi: "plannotator_submit_plan",
48329
+ codex: "ExitPlanMode",
48330
+ "gemini-cli": "exit_plan_mode"
48331
+ };
48332
+ function getPlanToolName(runtime3) {
48333
+ return runtime3 && PLAN_TOOL_NAMES[runtime3] || "ExitPlanMode";
48334
+ }
48335
+ function buildPlanFileRule(toolName, planFilePath) {
48336
+ if (!planFilePath)
48337
+ return "";
48338
+ return `- Your plan is saved at: ${planFilePath}
48339
+ You can edit this file to make targeted changes, then pass its path to ${toolName}.
48340
+ `;
48341
+ }
48342
+ var DEFAULT_REVIEW_APPROVED_PROMPT = `# Code Review
48343
+
48344
+ Code review completed \u2014 no changes requested.`;
48345
+ var DEFAULT_REVIEW_DENIED_SUFFIX = `
48346
+ The reviewer has identified issues above. You must address all of them.`;
48347
+ var DEFAULT_PLAN_DENIED_PROMPT = `YOUR PLAN WAS NOT APPROVED.
48348
+
48349
+ You MUST revise the plan to address ALL of the feedback below before calling {{toolName}} again.
48350
+
48351
+ Rules:
48352
+ {{planFileRule}}- Do not resubmit the same plan unchanged.
48353
+ - Do NOT change the plan title (first # heading) unless the user explicitly asks you to.
48354
+
48355
+ {{feedback}}`;
48356
+ var DEFAULT_PLAN_APPROVED_PROMPT = "Plan approved. You now have full tool access (read, bash, edit, write). Execute the plan in {{planFilePath}}. {{doneMsg}}";
48357
+ var DEFAULT_PLAN_APPROVED_WITH_NOTES_PROMPT = `Plan approved with notes! You now have full tool access (read, bash, edit, write). Execute the plan in {{planFilePath}}. {{doneMsg}}
48358
+
48359
+ ## Implementation Notes
48360
+
48361
+ The user approved your plan but added the following notes to consider during implementation:
48362
+
48363
+ {{feedback}}
48364
+
48365
+ Proceed with implementation, incorporating these notes where applicable.`;
48366
+ var DEFAULT_ANNOTATE_FILE_FEEDBACK_PROMPT = `# Markdown Annotations
48367
+
48368
+ {{fileHeader}}: {{filePath}}
48369
+
48370
+ {{feedback}}
48371
+
48372
+ Please address the annotation feedback above.`;
48373
+ var DEFAULT_ANNOTATE_MESSAGE_FEEDBACK_PROMPT = `# Message Annotations
48374
+
48375
+ {{feedback}}
48376
+
48377
+ Please address the annotation feedback above.`;
48378
+ function normalizePrompt(prompt) {
48379
+ if (typeof prompt !== "string")
48380
+ return;
48381
+ return prompt.trim() ? prompt : undefined;
48382
+ }
48383
+ function getConfiguredPrompt(options) {
48384
+ const resolvedConfig = options.config ?? loadConfig();
48385
+ const section = resolvedConfig.prompts?.[options.section];
48386
+ const runtimePrompt = options.runtime ? normalizePrompt(section?.runtimes?.[options.runtime]?.[options.key]) : undefined;
48387
+ const genericPrompt = normalizePrompt(section?.[options.key]);
48388
+ const runtimeFallback = options.runtime ? options.runtimeFallbacks?.[options.runtime] : undefined;
48389
+ return runtimePrompt ?? genericPrompt ?? runtimeFallback ?? options.fallback;
48390
+ }
48391
+ function getReviewApprovedPrompt(runtime3, config) {
48392
+ return getConfiguredPrompt({
48393
+ section: "review",
48394
+ key: "approved",
48395
+ runtime: runtime3,
48396
+ config,
48397
+ fallback: DEFAULT_REVIEW_APPROVED_PROMPT
48398
+ });
48399
+ }
48400
+ var REVIEW_DENIED_RUNTIME_DEFAULTS = {
48401
+ opencode: `
48402
+
48403
+ Please address this feedback.`,
48404
+ pi: `
48405
+
48406
+ Please address this feedback.`
48407
+ };
48408
+ function getReviewDeniedSuffix(runtime3, config) {
48409
+ return getConfiguredPrompt({
48410
+ section: "review",
48411
+ key: "denied",
48412
+ runtime: runtime3,
48413
+ config,
48414
+ fallback: DEFAULT_REVIEW_DENIED_SUFFIX,
48415
+ runtimeFallbacks: REVIEW_DENIED_RUNTIME_DEFAULTS
48416
+ });
48417
+ }
48418
+ function getPlanDeniedPrompt(runtime3, config, vars) {
48419
+ const template = getConfiguredPrompt({
48420
+ section: "plan",
48421
+ key: "denied",
48422
+ runtime: runtime3,
48423
+ config,
48424
+ fallback: DEFAULT_PLAN_DENIED_PROMPT
48425
+ });
48426
+ return resolveTemplate(template, vars ?? {});
48427
+ }
48428
+ var PLAN_APPROVED_RUNTIME_DEFAULTS = {
48429
+ opencode: "Plan approved!{{doneMsg}}"
48430
+ };
48431
+ function getPlanApprovedPrompt(runtime3, config, vars) {
48432
+ const template = getConfiguredPrompt({
48433
+ section: "plan",
48434
+ key: "approved",
48435
+ runtime: runtime3,
48436
+ config,
48437
+ fallback: DEFAULT_PLAN_APPROVED_PROMPT,
48438
+ runtimeFallbacks: PLAN_APPROVED_RUNTIME_DEFAULTS
48439
+ });
48440
+ return resolveTemplate(template, vars ?? {});
48441
+ }
48442
+ var PLAN_APPROVED_WITH_NOTES_RUNTIME_DEFAULTS = {
48443
+ opencode: `Plan approved with notes!
48444
+ {{doneMsg}}
48445
+
48446
+ ## Implementation Notes
48447
+
48448
+ The user approved your plan but added the following notes to consider during implementation:
48449
+
48450
+ {{feedback}}
48451
+
48452
+ Proceed with implementation, incorporating these notes where applicable.`
48453
+ };
48454
+ function getPlanApprovedWithNotesPrompt(runtime3, config, vars) {
48455
+ const template = getConfiguredPrompt({
48456
+ section: "plan",
48457
+ key: "approvedWithNotes",
48458
+ runtime: runtime3,
48459
+ config,
48460
+ fallback: DEFAULT_PLAN_APPROVED_WITH_NOTES_PROMPT,
48461
+ runtimeFallbacks: PLAN_APPROVED_WITH_NOTES_RUNTIME_DEFAULTS
48462
+ });
48463
+ return resolveTemplate(template, vars ?? {});
48464
+ }
48465
+ function getAnnotateFileFeedbackPrompt(runtime3, config, vars) {
48466
+ const template = getConfiguredPrompt({
48467
+ section: "annotate",
48468
+ key: "fileFeedback",
48469
+ runtime: runtime3,
48470
+ config,
48471
+ fallback: DEFAULT_ANNOTATE_FILE_FEEDBACK_PROMPT
48472
+ });
48473
+ return resolveTemplate(template, vars ?? {});
48474
+ }
48475
+ function getAnnotateMessageFeedbackPrompt(runtime3, config, vars) {
48476
+ const template = getConfiguredPrompt({
48477
+ section: "annotate",
48478
+ key: "messageFeedback",
48479
+ runtime: runtime3,
48480
+ config,
48481
+ fallback: DEFAULT_ANNOTATE_MESSAGE_FEEDBACK_PROMPT
48482
+ });
48483
+ return resolveTemplate(template, vars ?? {});
48484
+ }
48485
+
48291
48486
  // ../../packages/shared/at-reference.ts
48292
48487
  function stripAtPrefix(input) {
48293
48488
  const unquoted = stripWrappingQuotes(input);
@@ -48637,11 +48832,7 @@ async function handleReviewCommand(event, deps) {
48637
48832
  if (sessionId) {
48638
48833
  const shouldSwitchAgent = result.agentSwitch && result.agentSwitch !== "disabled";
48639
48834
  const targetAgent = result.agentSwitch || "build";
48640
- const message = result.approved ? `# Code Review
48641
-
48642
- Code review completed \u2014 no changes requested.` : isPRMode ? result.feedback : `${result.feedback}
48643
-
48644
- Please address this feedback.`;
48835
+ const message = result.approved ? getReviewApprovedPrompt("opencode") : isPRMode ? result.feedback : `${result.feedback}${getReviewDeniedSuffix("opencode")}`;
48645
48836
  try {
48646
48837
  await client3.session.prompt({
48647
48838
  path: { id: sessionId },
@@ -48666,6 +48857,7 @@ async function handleAnnotateCommand(event, deps) {
48666
48857
  let absolutePath;
48667
48858
  let folderPath;
48668
48859
  let annotateMode = "annotate";
48860
+ let isFolder = false;
48669
48861
  let sourceInfo;
48670
48862
  let sourceConverted = false;
48671
48863
  const isUrl = /^https?:\/\//i.test(filePath);
@@ -48685,7 +48877,6 @@ async function handleAnnotateCommand(event, deps) {
48685
48877
  } else {
48686
48878
  const projectRoot = directory || process.cwd();
48687
48879
  const resolvedArg = resolveUserPath(filePath, projectRoot);
48688
- let isFolder = false;
48689
48880
  try {
48690
48881
  isFolder = statSync5(resolvedArg).isDirectory();
48691
48882
  } catch {}
@@ -48771,13 +48962,11 @@ ${resolved.matches.map((m3) => ` ${m3}`).join(`
48771
48962
  body: {
48772
48963
  parts: [{
48773
48964
  type: "text",
48774
- text: `# Markdown Annotations
48775
-
48776
- File: ${absolutePath}
48777
-
48778
- ${result.feedback}
48779
-
48780
- Please address the annotation feedback above.`
48965
+ text: getAnnotateFileFeedbackPrompt("opencode", undefined, {
48966
+ fileHeader: isFolder ? "Folder" : "File",
48967
+ filePath: absolutePath,
48968
+ feedback: result.feedback
48969
+ })
48781
48970
  }]
48782
48971
  }
48783
48972
  });
@@ -48857,22 +49046,6 @@ async function handleArchiveCommand(event, deps) {
48857
49046
  server2.stop();
48858
49047
  }
48859
49048
 
48860
- // ../../packages/shared/feedback-templates.ts
48861
- var planDenyFeedback = (feedback, toolName = "ExitPlanMode", options) => {
48862
- const planFileRule = options?.planFilePath ? `- Your plan is saved at: ${options.planFilePath}
48863
- You can edit this file to make targeted changes, then pass its path to ${toolName}.
48864
- ` : "";
48865
- return `YOUR PLAN WAS NOT APPROVED.
48866
-
48867
- You MUST revise the plan to address ALL of the feedback below before calling ${toolName} again.
48868
-
48869
- Rules:
48870
- ${planFileRule}- Do not resubmit the same plan unchanged.
48871
- - Do NOT change the plan title (first # heading) unless the user explicitly asks you to.
48872
-
48873
- ${feedback || "Plan changes requested"}`;
48874
- };
48875
-
48876
49049
  // plan-mode.ts
48877
49050
  function normalizeEditPermission(edit) {
48878
49051
  if (typeof edit === "string") {
@@ -49261,11 +49434,7 @@ Do NOT proceed with implementation until your plan is approved.`);
49261
49434
  body: {
49262
49435
  parts: [{
49263
49436
  type: "text",
49264
- text: `# Message Annotations
49265
-
49266
- ${feedback}
49267
-
49268
- Please address the annotation feedback above.`
49437
+ text: getAnnotateMessageFeedbackPrompt("opencode", undefined, { feedback })
49269
49438
  }]
49270
49439
  }
49271
49440
  });
@@ -49363,21 +49532,21 @@ Use /plannotator-last or /plannotator-annotate for manual review, or set workflo
49363
49532
  } catch {}
49364
49533
  }
49365
49534
  if (result.feedback) {
49366
- return `Plan approved with notes!
49367
- ${result.savedPath ? `Saved to: ${result.savedPath}` : ""}
49368
-
49369
- ## Implementation Notes
49370
-
49371
- The user approved your plan but added the following notes to consider during implementation:
49372
-
49373
- ${result.feedback}
49374
-
49375
- Proceed with implementation, incorporating these notes where applicable.`;
49535
+ return getPlanApprovedWithNotesPrompt("opencode", undefined, {
49536
+ planFilePath: sourceFilePath,
49537
+ doneMsg: result.savedPath ? `Saved to: ${result.savedPath}` : "",
49538
+ feedback: result.feedback
49539
+ });
49376
49540
  }
49377
- return `Plan approved!${result.savedPath ? ` Saved to: ${result.savedPath}` : ""}`;
49541
+ return getPlanApprovedPrompt("opencode", undefined, {
49542
+ planFilePath: sourceFilePath,
49543
+ doneMsg: result.savedPath ? ` Saved to: ${result.savedPath}` : ""
49544
+ });
49378
49545
  } else {
49379
- return planDenyFeedback(result.feedback || "", "submit_plan", {
49380
- planFilePath: sourceFilePath
49546
+ return getPlanDeniedPrompt("opencode", undefined, {
49547
+ toolName: getPlanToolName("opencode"),
49548
+ planFileRule: buildPlanFileRule(getPlanToolName("opencode"), sourceFilePath),
49549
+ feedback: result.feedback || "Plan changes requested"
49381
49550
  }) + "\n\nAfter making your revisions, call `submit_plan` again to resubmit for review.";
49382
49551
  }
49383
49552
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plannotator/opencode",
3
- "version": "0.19.2",
3
+ "version": "0.19.3",
4
4
  "description": "Plannotator plugin for OpenCode - interactive plan review with visual annotation",
5
5
  "author": "backnotprop",
6
6
  "license": "MIT OR Apache-2.0",
@@ -38,7 +38,7 @@
38
38
  "@opencode-ai/plugin": "^1.1.10"
39
39
  },
40
40
  "devDependencies": {
41
- "@plannotator/server": "0.19.2",
41
+ "@plannotator/server": "0.19.3",
42
42
  "@plannotator/shared": "0.0.1"
43
43
  },
44
44
  "peerDependencies": {