@plannotator/opencode 0.27.10 → 0.27.11

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/embedded.js CHANGED
@@ -443,6 +443,13 @@ function resolveAnnotateHistory(config) {
443
443
  }
444
444
  return coerceConfigBoolean(config.annotateHistory, true);
445
445
  }
446
+ function resolveFeedbackHistory(config) {
447
+ const envVal = process.env.PLANNOTATOR_FEEDBACK_HISTORY;
448
+ if (envVal !== undefined) {
449
+ return envVal === "1" || envVal.toLowerCase() === "true";
450
+ }
451
+ return coerceConfigBoolean(config.feedbackHistory, true);
452
+ }
446
453
  function resolveGuideHistory(config) {
447
454
  const envVal = process.env.PLANNOTATOR_GUIDE_HISTORY;
448
455
  if (envVal !== undefined) {
@@ -2045,14 +2052,210 @@ var init_config2 = __esm(() => {
2045
2052
  init_config();
2046
2053
  });
2047
2054
 
2048
- // ../../packages/shared/improvement-hooks.ts
2055
+ // ../../packages/shared/feedback-archive.ts
2056
+ import { appendFileSync, mkdirSync as mkdirSync4, writeFileSync as writeFileSync3 } from "fs";
2049
2057
  import { join as join7 } from "path";
2058
+ function normalizeFeedbackProject(project2) {
2059
+ if (!project2)
2060
+ return "_unknown";
2061
+ if (project2 === "_unknown")
2062
+ return project2;
2063
+ return sanitizeTag(project2) ?? "_unknown";
2064
+ }
2065
+ function deriveFeedbackProject(cwd) {
2066
+ if (!cwd)
2067
+ return "_unknown";
2068
+ return extractDirName(cwd) ?? extractRepoName(cwd) ?? "_unknown";
2069
+ }
2070
+ function feedbackProjectDir(project2) {
2071
+ return join7(getPlannotatorDataDir(), "feedback", normalizeFeedbackProject(project2));
2072
+ }
2073
+ function stamp(now) {
2074
+ return now.toISOString().replace(/[:.]/g, "-");
2075
+ }
2076
+ function asString(value) {
2077
+ return typeof value === "string" && value.length > 0 ? value : undefined;
2078
+ }
2079
+ function asNumber(value) {
2080
+ return typeof value === "number" && Number.isFinite(value) ? value : undefined;
2081
+ }
2082
+ function normalizeAnnotation(raw) {
2083
+ if (typeof raw !== "object" || raw === null)
2084
+ return {};
2085
+ const a = raw;
2086
+ const record = {};
2087
+ const id = asString(a.id);
2088
+ if (id)
2089
+ record.id = id;
2090
+ const type = asString(a.type);
2091
+ if (type)
2092
+ record.type = type;
2093
+ const text = asString(a.text);
2094
+ if (text)
2095
+ record.text = text;
2096
+ const originalText = asString(a.originalText) ?? asString(a.selectedText) ?? asString(a.tokenText);
2097
+ if (originalText)
2098
+ record.originalText = originalText;
2099
+ const file = asString(a.filePath) ?? asString(a.file);
2100
+ if (file)
2101
+ record.file = file;
2102
+ const lineStart = asNumber(a.lineStart);
2103
+ if (lineStart !== undefined)
2104
+ record.lineStart = lineStart;
2105
+ const lineEnd = asNumber(a.lineEnd);
2106
+ if (lineEnd !== undefined)
2107
+ record.lineEnd = lineEnd;
2108
+ const side = asString(a.side);
2109
+ if (side)
2110
+ record.side = side;
2111
+ const blockId = asString(a.blockId);
2112
+ if (blockId)
2113
+ record.blockId = blockId;
2114
+ const diffContext = asString(a.diffContext);
2115
+ if (diffContext)
2116
+ record.diffContext = diffContext;
2117
+ const severity = asString(a.severity);
2118
+ if (severity)
2119
+ record.severity = severity;
2120
+ const inReplyTo = asString(a.inReplyTo);
2121
+ if (inReplyTo)
2122
+ record.inReplyTo = inReplyTo;
2123
+ const source = asString(a.source);
2124
+ if (source)
2125
+ record.source = source;
2126
+ const author = asString(a.author);
2127
+ if (author)
2128
+ record.author = author;
2129
+ if (Array.isArray(a.images) && a.images.length > 0)
2130
+ record.images = a.images.length;
2131
+ return record;
2132
+ }
2133
+ function renderFeedbackRecordMarkdown(record) {
2134
+ const lines = [`# ${SURFACE_TITLES[record.surface] ?? "Feedback"}`, ""];
2135
+ lines.push(`- Submitted: ${record.ts}`);
2136
+ lines.push(`- Surface: ${record.surface}`);
2137
+ lines.push(`- Decision: ${record.decision}`);
2138
+ lines.push(`- Project: ${record.project}`);
2139
+ if (record.origin)
2140
+ lines.push(`- Origin: ${record.origin}`);
2141
+ const target = record.target;
2142
+ if (target?.filePath)
2143
+ lines.push(`- File: ${target.filePath}`);
2144
+ if (target?.url)
2145
+ lines.push(`- URL: ${target.url}`);
2146
+ if (target?.slug) {
2147
+ lines.push(`- Plan: ${target.slug}${target.planVersion ? ` (version ${target.planVersion})` : ""}`);
2148
+ }
2149
+ if (target?.planVersionFile)
2150
+ lines.push(`- Plan version file: ${target.planVersionFile}`);
2151
+ const review = target?.review;
2152
+ if (review) {
2153
+ const bits = [review.diffType, review.base ? `base ${review.base}` : null, review.gitRef ? `ref ${review.gitRef}` : null].filter(Boolean).join(", ");
2154
+ if (bits)
2155
+ lines.push(`- Diff: ${bits}`);
2156
+ if (review.cwd)
2157
+ lines.push(`- Repository: ${review.cwd}`);
2158
+ if (review.pr)
2159
+ lines.push(`- Pull request: ${review.pr.provider} ${review.pr.repo}#${review.pr.number}`);
2160
+ }
2161
+ lines.push(`- Annotations: ${record.counts.annotations}${record.counts.external > 0 ? ` (${record.counts.external} external)` : ""}`);
2162
+ lines.push("", "---", "");
2163
+ lines.push(record.feedback && record.feedback.trim() ? record.feedback : "_No feedback text submitted._");
2164
+ lines.push("");
2165
+ return lines.join(`
2166
+ `);
2167
+ }
2168
+ function appendFeedbackRecord(input) {
2169
+ try {
2170
+ const now = input.now ?? new Date;
2171
+ const project2 = normalizeFeedbackProject(input.project);
2172
+ const feedback = typeof input.feedback === "string" ? input.feedback : "";
2173
+ const rawAnnotations = Array.isArray(input.annotations) ? input.annotations : [];
2174
+ const annotations = rawAnnotations.map(normalizeAnnotation);
2175
+ const counts = {
2176
+ annotations: annotations.length,
2177
+ external: annotations.filter((a) => a.source !== undefined).length,
2178
+ images: annotations.reduce((sum, a) => sum + (a.images ?? 0), 0)
2179
+ };
2180
+ const hasContent = feedback.trim().length > 0 || annotations.length > 0;
2181
+ const record = {
2182
+ v: FEEDBACK_RECORD_VERSION,
2183
+ ts: now.toISOString(),
2184
+ client: FEEDBACK_RECORD_CLIENT,
2185
+ project: project2,
2186
+ ...input.origin ? { origin: input.origin } : {},
2187
+ surface: input.surface,
2188
+ decision: input.decision,
2189
+ ...input.target ? { target: input.target } : {},
2190
+ ...feedback ? { feedback } : {},
2191
+ ...annotations.length > 0 ? { annotations } : {},
2192
+ counts
2193
+ };
2194
+ const projectDir = feedbackProjectDir(project2);
2195
+ mkdirSync4(projectDir, { recursive: true });
2196
+ if (hasContent) {
2197
+ const recordsDir = join7(projectDir, "records");
2198
+ mkdirSync4(recordsDir, { recursive: true });
2199
+ const base = `${stamp(now)}-${input.surface}-${input.decision}`;
2200
+ let name = `${base}.md`;
2201
+ const body = renderFeedbackRecordMarkdown(record);
2202
+ for (let n = 2;; n++) {
2203
+ try {
2204
+ writeFileSync3(join7(recordsDir, name), body, { encoding: "utf-8", flag: "wx" });
2205
+ break;
2206
+ } catch (err) {
2207
+ if (err?.code !== "EEXIST")
2208
+ throw err;
2209
+ if (n > 100) {
2210
+ throw new Error(`sidecar name collision exceeded 100 attempts for ${base}.md (clock stuck or runaway writer)`);
2211
+ }
2212
+ name = `${base}-${n}.md`;
2213
+ }
2214
+ }
2215
+ record.recordFile = `records/${name}`;
2216
+ }
2217
+ const indexPath = join7(projectDir, "index.jsonl");
2218
+ appendFileSync(indexPath, `${JSON.stringify(record)}
2219
+ `, "utf-8");
2220
+ return indexPath;
2221
+ } catch (error) {
2222
+ console.error(`[plannotator] warning: could not archive submitted feedback (${error instanceof Error ? error.message : String(error)}); keeping the annotation draft as the recovery copy`);
2223
+ return null;
2224
+ }
2225
+ }
2226
+ function countChangedFiles(patch) {
2227
+ if (!patch)
2228
+ return 0;
2229
+ const files = new Set;
2230
+ const re = /^diff --git a\/(.+?) b\/(.+)$/gm;
2231
+ let match;
2232
+ while ((match = re.exec(patch)) !== null)
2233
+ files.add(match[2]);
2234
+ return files.size;
2235
+ }
2236
+ var FEEDBACK_RECORD_VERSION = 1, FEEDBACK_RECORD_CLIENT = "plannotator", SURFACE_TITLES;
2237
+ var init_feedback_archive = __esm(() => {
2238
+ init_data_dir();
2239
+ init_project();
2240
+ SURFACE_TITLES = {
2241
+ plan: "Plan review feedback",
2242
+ review: "Code review feedback",
2243
+ annotate: "Annotate feedback",
2244
+ "annotate-url": "Annotate feedback (URL)",
2245
+ "annotate-app": "Annotate feedback (live app)",
2246
+ "annotate-last": "Annotate feedback (agent message)",
2247
+ "annotate-folder": "Annotate feedback (folder)"
2248
+ };
2249
+ });
2250
+
2251
+ // ../../packages/shared/improvement-hooks.ts
2252
+ import { join as join8 } from "path";
2050
2253
  import { readFileSync as readFileSync4, statSync as statSync4 } from "fs";
2051
2254
  function getImprovementHookExpectedPath(hookName) {
2052
2255
  const entry = KNOWN_HOOKS[hookName];
2053
2256
  if (!entry)
2054
2257
  return null;
2055
- return join7(HOOKS_BASE_DIR, entry.path);
2258
+ return join8(HOOKS_BASE_DIR, entry.path);
2056
2259
  }
2057
2260
  function fileExists2(path) {
2058
2261
  try {
@@ -2079,18 +2282,18 @@ function readImprovementHook(hookName) {
2079
2282
  const entry = KNOWN_HOOKS[hookName];
2080
2283
  if (!entry)
2081
2284
  return null;
2082
- const newPath = join7(HOOKS_BASE_DIR, entry.path);
2285
+ const newPath = join8(HOOKS_BASE_DIR, entry.path);
2083
2286
  if (fileExists2(newPath)) {
2084
2287
  return tryReadHookFile(newPath, hookName);
2085
2288
  }
2086
- const legacyFilePath = join7(LEGACY_BASE_DIR, entry.legacyPath);
2289
+ const legacyFilePath = join8(LEGACY_BASE_DIR, entry.legacyPath);
2087
2290
  return tryReadHookFile(legacyFilePath, hookName);
2088
2291
  }
2089
2292
  var DATA_DIR2, HOOKS_BASE_DIR, LEGACY_BASE_DIR, MAX_FILE_SIZE, KNOWN_HOOKS;
2090
2293
  var init_improvement_hooks = __esm(() => {
2091
2294
  init_data_dir();
2092
2295
  DATA_DIR2 = getPlannotatorDataDir();
2093
- HOOKS_BASE_DIR = join7(DATA_DIR2, "hooks");
2296
+ HOOKS_BASE_DIR = join8(DATA_DIR2, "hooks");
2094
2297
  LEGACY_BASE_DIR = DATA_DIR2;
2095
2298
  MAX_FILE_SIZE = 50 * 1024;
2096
2299
  KNOWN_HOOKS = {
@@ -2975,7 +3178,7 @@ var init_qr = __esm(() => {
2975
3178
  });
2976
3179
 
2977
3180
  // ../../packages/server/image.ts
2978
- import { resolve as resolve4, join as join8 } from "path";
3181
+ import { resolve as resolve4, join as join9 } from "path";
2979
3182
  import { tmpdir } from "os";
2980
3183
  function getExtension(filePath) {
2981
3184
  const lastDot = filePath.lastIndexOf(".");
@@ -3023,26 +3226,26 @@ var init_image = __esm(() => {
3023
3226
  "tif",
3024
3227
  "avif"
3025
3228
  ]);
3026
- UPLOAD_DIR = join8(tmpdir(), "plannotator");
3229
+ UPLOAD_DIR = join9(tmpdir(), "plannotator");
3027
3230
  });
3028
3231
 
3029
3232
  // ../../packages/shared/draft.ts
3030
- import { join as join9 } from "path";
3031
- import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync3, readFileSync as readFileSync5, renameSync as renameSync2, unlinkSync as unlinkSync2, existsSync as existsSync7 } from "fs";
3233
+ import { join as join10 } from "path";
3234
+ import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync4, readFileSync as readFileSync5, renameSync as renameSync2, unlinkSync as unlinkSync2, existsSync as existsSync7 } from "fs";
3032
3235
  import { createHash } from "crypto";
3033
3236
  function getDraftDir() {
3034
- const dir = join9(getPlannotatorDataDir(), "drafts");
3035
- mkdirSync4(dir, { recursive: true });
3237
+ const dir = join10(getPlannotatorDataDir(), "drafts");
3238
+ mkdirSync5(dir, { recursive: true });
3036
3239
  return dir;
3037
3240
  }
3038
3241
  function contentHash(content) {
3039
3242
  return createHash("sha256").update(content).digest("hex").slice(0, 16);
3040
3243
  }
3041
3244
  function draftPath(key) {
3042
- return join9(getDraftDir(), `${key}.json`);
3245
+ return join10(getDraftDir(), `${key}.json`);
3043
3246
  }
3044
3247
  function tombstonePath(key) {
3045
- return join9(getDraftDir(), `${key}.deleted.json`);
3248
+ return join10(getDraftDir(), `${key}.deleted.json`);
3046
3249
  }
3047
3250
  function readGeneration(value) {
3048
3251
  return typeof value === "number" && Number.isInteger(value) && value >= 0 ? value : null;
@@ -3078,7 +3281,7 @@ function getDraftGeneration(key) {
3078
3281
  }
3079
3282
  function writeTombstoneGeneration(key, draftGeneration) {
3080
3283
  const filePath = tombstonePath(key);
3081
- writeFileSync3(filePath, JSON.stringify({ draftGeneration }), "utf-8");
3284
+ writeFileSync4(filePath, JSON.stringify({ draftGeneration }), "utf-8");
3082
3285
  }
3083
3286
  function clearTombstone(key) {
3084
3287
  const filePath = tombstonePath(key);
@@ -3099,7 +3302,7 @@ function saveDraft(key, data) {
3099
3302
  }
3100
3303
  const finalPath = draftPath(key);
3101
3304
  const tmpPath = `${finalPath}.tmp`;
3102
- writeFileSync3(tmpPath, JSON.stringify(data), "utf-8");
3305
+ writeFileSync4(tmpPath, JSON.stringify(data), "utf-8");
3103
3306
  renameSync2(tmpPath, finalPath);
3104
3307
  if (draftGeneration === null) {
3105
3308
  clearTombstone(key);
@@ -3196,27 +3399,27 @@ import {
3196
3399
  closeSync as closeSync2,
3197
3400
  existsSync as existsSync8,
3198
3401
  fstatSync,
3199
- mkdirSync as mkdirSync5,
3402
+ mkdirSync as mkdirSync6,
3200
3403
  openSync as openSync2,
3201
3404
  readdirSync as readdirSync3,
3202
3405
  readFileSync as readFileSync6,
3203
3406
  readSync,
3204
3407
  realpathSync as realpathSync2,
3205
3408
  statSync as statSync5,
3206
- writeFileSync as writeFileSync4
3409
+ writeFileSync as writeFileSync5
3207
3410
  } from "fs";
3208
3411
  import { homedir as homedir3 } from "os";
3209
- import { join as join10 } from "path";
3412
+ import { join as join11 } from "path";
3210
3413
  function resolveGlobalSkillRoots() {
3211
3414
  const home = process.env.HOME?.trim() || homedir3();
3212
- const claudeHome = process.env.CLAUDE_CONFIG_DIR?.trim() || join10(home, ".claude");
3213
- const codexHome = process.env.CODEX_HOME?.trim() || join10(home, ".codex");
3214
- const configHome = process.env.XDG_CONFIG_HOME?.trim() || join10(home, ".config");
3415
+ const claudeHome = process.env.CLAUDE_CONFIG_DIR?.trim() || join11(home, ".claude");
3416
+ const codexHome = process.env.CODEX_HOME?.trim() || join11(home, ".codex");
3417
+ const configHome = process.env.XDG_CONFIG_HOME?.trim() || join11(home, ".config");
3215
3418
  const candidates = [
3216
- { dir: join10(claudeHome, "skills"), root: "claude" },
3217
- { dir: join10(codexHome, "skills"), root: "codex" },
3218
- { dir: join10(home, ".agents", "skills"), root: "universal" },
3219
- { dir: join10(configHome, "agents", "skills"), root: "universal" }
3419
+ { dir: join11(claudeHome, "skills"), root: "claude" },
3420
+ { dir: join11(codexHome, "skills"), root: "codex" },
3421
+ { dir: join11(home, ".agents", "skills"), root: "universal" },
3422
+ { dir: join11(configHome, "agents", "skills"), root: "universal" }
3220
3423
  ];
3221
3424
  const seen = new Set;
3222
3425
  const roots = [];
@@ -3236,7 +3439,7 @@ function resolveGlobalSkillRoots() {
3236
3439
  }
3237
3440
  function hasSkillMd(dir) {
3238
3441
  try {
3239
- return statSync5(join10(dir, "SKILL.md")).isFile();
3442
+ return statSync5(join11(dir, "SKILL.md")).isFile();
3240
3443
  } catch {
3241
3444
  return false;
3242
3445
  }
@@ -3256,7 +3459,7 @@ function listSubdirs(dir) {
3256
3459
  return true;
3257
3460
  if (e.isSymbolicLink()) {
3258
3461
  try {
3259
- return statSync5(join10(dir, e.name)).isDirectory();
3462
+ return statSync5(join11(dir, e.name)).isDirectory();
3260
3463
  } catch {
3261
3464
  return false;
3262
3465
  }
@@ -3273,7 +3476,7 @@ function discoverSkills() {
3273
3476
  byName.set(name, {
3274
3477
  name,
3275
3478
  sourcePath: dir,
3276
- skillMdPath: join10(dir, "SKILL.md"),
3479
+ skillMdPath: join11(dir, "SKILL.md"),
3277
3480
  root
3278
3481
  });
3279
3482
  };
@@ -3281,13 +3484,13 @@ function discoverSkills() {
3281
3484
  if (!existsSync8(rootDir))
3282
3485
  continue;
3283
3486
  for (const childName of listSubdirs(rootDir)) {
3284
- const childDir = join10(rootDir, childName);
3487
+ const childDir = join11(rootDir, childName);
3285
3488
  if (hasSkillMd(childDir)) {
3286
3489
  add(childDir, root);
3287
3490
  continue;
3288
3491
  }
3289
3492
  for (const grandName of listSubdirs(childDir)) {
3290
- const grandDir = join10(childDir, grandName);
3493
+ const grandDir = join11(childDir, grandName);
3291
3494
  if (hasSkillMd(grandDir))
3292
3495
  add(grandDir, root);
3293
3496
  }
@@ -3438,7 +3641,7 @@ function readReferenceSkillContent(name) {
3438
3641
  };
3439
3642
  }
3440
3643
  function readCuratedSkillNames() {
3441
- const path2 = join10(getPlannotatorDataDir(), "review-skills.json");
3644
+ const path2 = join11(getPlannotatorDataDir(), "review-skills.json");
3442
3645
  if (!existsSync8(path2))
3443
3646
  return null;
3444
3647
  let parsed;
@@ -3481,8 +3684,8 @@ function enableReviewSkill(name) {
3481
3684
  current.add(name);
3482
3685
  const enabled = [...current];
3483
3686
  const dir = getPlannotatorDataDir();
3484
- mkdirSync5(dir, { recursive: true });
3485
- writeFileSync4(join10(dir, "review-skills.json"), JSON.stringify({ version: 1, enabled }, null, 2) + `
3687
+ mkdirSync6(dir, { recursive: true });
3688
+ writeFileSync5(join11(dir, "review-skills.json"), JSON.stringify({ version: 1, enabled }, null, 2) + `
3486
3689
  `);
3487
3690
  return { enabled };
3488
3691
  }
@@ -3554,7 +3757,7 @@ var init_review_skill_loader = __esm(() => {
3554
3757
  });
3555
3758
 
3556
3759
  // ../../packages/server/shared-handlers.ts
3557
- import { appendFileSync, mkdirSync as mkdirSync6 } from "fs";
3760
+ import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync7 } from "fs";
3558
3761
  import { dirname } from "path";
3559
3762
  function normalizeDraftGeneration(value) {
3560
3763
  if (typeof value !== "number")
@@ -3617,7 +3820,7 @@ async function handleUpload(req) {
3617
3820
  if (!extResult.valid) {
3618
3821
  return Response.json({ error: extResult.error }, { status: 400 });
3619
3822
  }
3620
- mkdirSync6(UPLOAD_DIR, { recursive: true });
3823
+ mkdirSync7(UPLOAD_DIR, { recursive: true });
3621
3824
  const tempPath = `${UPLOAD_DIR}/${crypto.randomUUID()}.${extResult.ext}`;
3622
3825
  await Bun.write(tempPath, file);
3623
3826
  return Response.json({ path: tempPath, originalName: file.name });
@@ -3696,8 +3899,8 @@ function handleFavicon() {
3696
3899
  });
3697
3900
  }
3698
3901
  function writeServerReadyMetadata(readyFile, metadata) {
3699
- mkdirSync6(dirname(readyFile), { recursive: true });
3700
- appendFileSync(readyFile, `${JSON.stringify(metadata)}
3902
+ mkdirSync7(dirname(readyFile), { recursive: true });
3903
+ appendFileSync2(readyFile, `${JSON.stringify(metadata)}
3701
3904
  `, "utf8");
3702
3905
  }
3703
3906
  function isCodexDesktopHost(env = process.env) {
@@ -20458,14 +20661,14 @@ function process2(parentNode) {
20458
20661
  } else if (node.nodeType === 1) {
20459
20662
  replacement = replacementForNode.call(self2, node);
20460
20663
  }
20461
- return join11(output, replacement);
20664
+ return join12(output, replacement);
20462
20665
  }, "");
20463
20666
  }
20464
20667
  function postProcess(output) {
20465
20668
  var self2 = this;
20466
20669
  this.rules.forEach(function(rule) {
20467
20670
  if (typeof rule.append === "function") {
20468
- output = join11(output, rule.append(self2.options));
20671
+ output = join12(output, rule.append(self2.options));
20469
20672
  }
20470
20673
  });
20471
20674
  return output.replace(/^[\t\r\n]+/, "").replace(/[\t\r\n\s]+$/, "");
@@ -20478,7 +20681,7 @@ function replacementForNode(node) {
20478
20681
  content = content.trim();
20479
20682
  return whitespace.leading + rule.replacement(content, node, this.options) + whitespace.trailing;
20480
20683
  }
20481
- function join11(output, replacement) {
20684
+ function join12(output, replacement) {
20482
20685
  var s1 = trimTrailingNewlines(output);
20483
20686
  var s2 = trimLeadingNewlines(replacement);
20484
20687
  var nls = Math.max(output.length - s1.length, replacement.length - s2.length);
@@ -21291,9 +21494,9 @@ import {
21291
21494
  renameSync as renameSync3,
21292
21495
  statSync as statSync7,
21293
21496
  unlinkSync as unlinkSync3,
21294
- writeFileSync as writeFileSync5
21497
+ writeFileSync as writeFileSync6
21295
21498
  } from "fs";
21296
- import { basename, dirname as dirname2, join as join12 } from "path";
21499
+ import { basename, dirname as dirname2, join as join13 } from "path";
21297
21500
  function hashSourceBytes(bytes) {
21298
21501
  return `sha256:${createHash2("sha256").update(bytes).digest("hex")}`;
21299
21502
  }
@@ -21379,7 +21582,7 @@ function resolveFolderSourceFileForSave(filePath, folderPath) {
21379
21582
  const realParent = realpathSync4(dirname2(candidate));
21380
21583
  if (!isWithinProjectRoot(realParent, root2))
21381
21584
  return null;
21382
- const resolvedMissingLeaf = join12(realParent, basename(candidate));
21585
+ const resolvedMissingLeaf = join13(realParent, basename(candidate));
21383
21586
  if (!isWithinProjectRoot(resolvedMissingLeaf, root2))
21384
21587
  return null;
21385
21588
  return resolvedMissingLeaf;
@@ -21441,7 +21644,7 @@ function createSourceSaveCapabilityFromText(scope, filePath, text, folderPath) {
21441
21644
  return enabledSourceSave(scope, real, sourceFileSnapshotFromText(text));
21442
21645
  }
21443
21646
  const realParent = realpathSync4(dirname2(resolved));
21444
- return enabledSourceSave(scope, join12(realParent, basename(resolved)), sourceFileSnapshotFromText(text));
21647
+ return enabledSourceSave(scope, join13(realParent, basename(resolved)), sourceFileSnapshotFromText(text));
21445
21648
  } catch {
21446
21649
  return disabledSourceSave("missing-file");
21447
21650
  }
@@ -21534,7 +21737,7 @@ function saveSourceFileAtomic(filePath, text, baseHash, options = {}) {
21534
21737
  }
21535
21738
  try {
21536
21739
  const realParent = realpathSync4(dirname2(filePath));
21537
- filePath = join12(realParent, basename(filePath));
21740
+ filePath = join13(realParent, basename(filePath));
21538
21741
  if (allowedRoot && !isWithinProjectRoot(filePath, allowedRoot)) {
21539
21742
  return {
21540
21743
  ok: false,
@@ -21573,9 +21776,9 @@ function saveSourceFileAtomic(filePath, text, baseHash, options = {}) {
21573
21776
  }
21574
21777
  const output = applySourceEolPolicy(text, outputEol);
21575
21778
  const dir = dirname2(filePath);
21576
- const tmp = join12(dir, `.plannotator-${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}.tmp`);
21779
+ const tmp = join13(dir, `.plannotator-${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}.tmp`);
21577
21780
  try {
21578
- writeFileSync5(tmp, output, { encoding: "utf8", mode });
21781
+ writeFileSync6(tmp, output, { encoding: "utf8", mode });
21579
21782
  if (mode !== undefined)
21580
21783
  chmodSync(tmp, mode);
21581
21784
  if (recreateMissingBase) {
@@ -42529,7 +42732,7 @@ var init_ssr = __esm(() => {
42529
42732
  // ../../packages/server/reference-handlers.ts
42530
42733
  import { existsSync as existsSync10, statSync as statSync8 } from "fs";
42531
42734
  import { readdir as readdir2 } from "fs/promises";
42532
- import { join as join13, relative as relative2, resolve as resolve6 } from "path";
42735
+ import { join as join14, relative as relative2, resolve as resolve6 } from "path";
42533
42736
  function getAllowedRootPaths(options) {
42534
42737
  const rawRoots = options?.rootPaths?.length ? options.rootPaths : [options?.rootPath ?? process.cwd()];
42535
42738
  const roots = [];
@@ -42945,7 +43148,7 @@ async function walkFileBrowserFiles(dir, root3, state) {
42945
43148
  for (const entry of entries) {
42946
43149
  if (state.truncated)
42947
43150
  return;
42948
- const fullPath = join13(dir, entry.name);
43151
+ const fullPath = join14(dir, entry.name);
42949
43152
  const relativePath = relative2(root3, fullPath).replace(/\\/g, "/");
42950
43153
  if (entry.isDirectory()) {
42951
43154
  if (isFileBrowserExcludedPath(relativePath))
@@ -76866,7 +77069,8 @@ class OpenCodeProvider {
76866
77069
  server = null;
76867
77070
  client = null;
76868
77071
  startPromise = null;
76869
- lastAttachError = null;
77072
+ exitHandler = null;
77073
+ startEpoch = 0;
76870
77074
  constructor(config) {
76871
77075
  this.config = config;
76872
77076
  }
@@ -76880,50 +77084,40 @@ class OpenCodeProvider {
76880
77084
  return this.startPromise;
76881
77085
  }
76882
77086
  async doStart() {
76883
- this.lastAttachError = null;
77087
+ const epoch = this.startEpoch;
76884
77088
  const { createOpencodeServer: createOpencodeServer2, createOpencodeClient: createOpencodeClient2 } = await getSDK();
76885
- const attachedClient = await this.tryAttachExistingServer(createOpencodeClient2);
76886
- if (attachedClient) {
76887
- this.client = attachedClient;
76888
- return;
77089
+ const server2 = await createOpencodeServer2({
77090
+ hostname: this.config.hostname ?? "127.0.0.1",
77091
+ port: this.config.port ?? 0,
77092
+ timeout: 15000
77093
+ });
77094
+ const exitHandler = () => {
77095
+ try {
77096
+ server2.close();
77097
+ } catch {}
77098
+ };
77099
+ process.once("exit", exitHandler);
77100
+ const reap = () => {
77101
+ process.removeListener("exit", exitHandler);
77102
+ try {
77103
+ server2.close();
77104
+ } catch {}
77105
+ };
77106
+ if (epoch !== this.startEpoch) {
77107
+ reap();
77108
+ throw new Error("OpenCode provider was disposed during startup.");
76889
77109
  }
76890
77110
  try {
76891
- this.server = await createOpencodeServer2({
76892
- hostname: this.config.hostname ?? "127.0.0.1",
76893
- ...this.config.port != null && { port: this.config.port },
76894
- timeout: 15000
77111
+ this.client = createOpencodeClient2({
77112
+ baseUrl: server2.url,
77113
+ directory: this.config.cwd ?? process.cwd()
76895
77114
  });
76896
77115
  } catch (err) {
76897
- const spawnMessage = err instanceof Error ? err.message : String(err);
76898
- if (this.lastAttachError) {
76899
- throw new Error(`${this.lastAttachError}
76900
- Fallback startup also failed: ${spawnMessage}`);
76901
- }
77116
+ reap();
76902
77117
  throw err;
76903
77118
  }
76904
- this.client = createOpencodeClient2({
76905
- baseUrl: this.server.url,
76906
- directory: this.config.cwd ?? process.cwd()
76907
- });
76908
- }
76909
- async tryAttachExistingServer(createOpencodeClient2) {
76910
- const cwd = this.config.cwd ?? process.cwd();
76911
- const baseUrl = `http://${this.config.hostname ?? "127.0.0.1"}:${this.config.port ?? 4096}`;
76912
- const client3 = createOpencodeClient2({
76913
- baseUrl,
76914
- directory: cwd
76915
- });
76916
- try {
76917
- await client3.config.get({
76918
- throwOnError: true,
76919
- signal: AbortSignal.timeout(1000)
76920
- });
76921
- return client3;
76922
- } catch (err) {
76923
- const message = err instanceof Error ? err.message : String(err);
76924
- this.lastAttachError = `Failed to attach to existing OpenCode server at ${baseUrl}: ${message}`;
76925
- return null;
76926
- }
77119
+ this.server = server2;
77120
+ this.exitHandler = exitHandler;
76927
77121
  }
76928
77122
  getClient() {
76929
77123
  if (!this.client) {
@@ -76985,6 +77179,11 @@ Fallback startup also failed: ${spawnMessage}`);
76985
77179
  });
76986
77180
  }
76987
77181
  dispose() {
77182
+ this.startEpoch++;
77183
+ if (this.exitHandler) {
77184
+ process.removeListener("exit", this.exitHandler);
77185
+ this.exitHandler = null;
77186
+ }
76988
77187
  if (this.server) {
76989
77188
  this.server.close();
76990
77189
  this.server = null;
@@ -77292,17 +77491,17 @@ async function createAIRuntime(options = {}) {
77292
77491
  }
77293
77492
  } catch {}
77294
77493
  try {
77295
- const { OpenCodeProvider: OpenCodeProvider2 } = await Promise.resolve().then(() => (init_opencode_sdk(), exports_opencode_sdk));
77494
+ await Promise.resolve().then(() => (init_opencode_sdk(), exports_opencode_sdk));
77296
77495
  const opencodePath = Bun.which("opencode");
77297
77496
  if (opencodePath) {
77298
77497
  const provider = await createProvider({
77299
77498
  type: "opencode-sdk",
77300
77499
  cwd
77301
77500
  });
77302
- if (provider instanceof OpenCodeProvider2) {
77303
- modelDiscovery.push(provider.fetchModels().catch(() => {}));
77501
+ const providerId = registry2.register(provider);
77502
+ if ("fetchModels" in provider) {
77503
+ providerInitializers.set(providerId, createBestEffortOnce(() => provider.fetchModels()));
77304
77504
  }
77305
- registry2.register(provider);
77306
77505
  }
77307
77506
  } catch {}
77308
77507
  const endpoints = createAIEndpoints({
@@ -77427,6 +77626,26 @@ async function startPlannotatorServer(options) {
77427
77626
  } else {
77428
77627
  decisionPromise = new Promise(() => {});
77429
77628
  }
77629
+ const archivePlanDecision = (decision, feedback) => {
77630
+ if (mode === "archive")
77631
+ return;
77632
+ if (!resolveFeedbackHistory(loadConfig()))
77633
+ return;
77634
+ appendFeedbackRecord({
77635
+ project: project2,
77636
+ origin,
77637
+ surface: "plan",
77638
+ decision,
77639
+ target: {
77640
+ slug,
77641
+ ...versionInfo.version > 0 ? {
77642
+ planVersion: versionInfo.version,
77643
+ planVersionFile: getPlanVersionPath(project2, slug, versionInfo.version) ?? undefined
77644
+ } : {}
77645
+ },
77646
+ feedback
77647
+ });
77648
+ };
77430
77649
  const server2 = await startBunServerOnAvailablePort((port2) => Bun.serve({
77431
77650
  hostname: getServerHostname(),
77432
77651
  port: port2,
@@ -77691,6 +77910,7 @@ async function startPlannotatorServer(options) {
77691
77910
  }
77692
77911
  savedPath = saveFinalSnapshot(slug, "approved", plan, annotations, planSaveCustomPath);
77693
77912
  }
77913
+ archivePlanDecision(typeof feedback === "string" && feedback.trim() ? "approved-with-notes" : "approved", feedback);
77694
77914
  deleteDraft(draftKey, draftGeneration);
77695
77915
  const effectivePermissionMode = requestedPermissionMode || permissionMode;
77696
77916
  resolveDecision({ approved: true, feedback, savedPath, agentSwitch, permissionMode: effectivePermissionMode });
@@ -77715,6 +77935,7 @@ async function startPlannotatorServer(options) {
77715
77935
  saveAnnotations(slug, feedback, planSaveCustomPath);
77716
77936
  savedPath = saveFinalSnapshot(slug, "denied", plan, feedback, planSaveCustomPath);
77717
77937
  }
77938
+ archivePlanDecision("denied", feedback);
77718
77939
  deleteDraft(draftKey, draftGeneration);
77719
77940
  resolveDecision({ approved: false, feedback, savedPath });
77720
77941
  return Response.json({ ok: true, savedPath });
@@ -77772,6 +77993,7 @@ var init_server2 = __esm(() => {
77772
77993
  init_repo();
77773
77994
  init_project2();
77774
77995
  init_config2();
77996
+ init_feedback_archive();
77775
77997
  init_favicon2();
77776
77998
  init_improvement_hooks();
77777
77999
  init_shared_handlers();
@@ -80231,7 +80453,7 @@ var init_gitbutler_core = __esm(() => {
80231
80453
  // ../../packages/shared/vcs-core.ts
80232
80454
  import { mkdtemp } from "fs/promises";
80233
80455
  import { tmpdir as tmpdir2 } from "os";
80234
- import { isAbsolute as isAbsolute6, join as join16, relative as relative6, resolve as resolve11 } from "path";
80456
+ import { isAbsolute as isAbsolute6, join as join17, relative as relative6, resolve as resolve11 } from "path";
80235
80457
  import { rmSync as rmSync2 } from "fs";
80236
80458
  function selectNearestProvider(candidates, cwd) {
80237
80459
  if (candidates.length === 0)
@@ -80612,8 +80834,8 @@ function removeDirectoryBestEffort(path2) {
80612
80834
  } catch {}
80613
80835
  }
80614
80836
  async function createSyntheticSnapshot(runtime, sourceCwd, baseCommit, patches) {
80615
- const tempRoot = await mkdtemp(join16(tmpdir2(), "plannotator-review-snapshot-"));
80616
- const snapshotCwd = join16(tempRoot, "repo");
80837
+ const tempRoot = await mkdtemp(join17(tmpdir2(), "plannotator-review-snapshot-"));
80838
+ const snapshotCwd = join17(tempRoot, "repo");
80617
80839
  const cleanup = () => removeDirectoryBestEffort(tempRoot);
80618
80840
  try {
80619
80841
  await git(runtime, sourceCwd, ["clone", "--shared", "--no-checkout", "--quiet", "--", sourceCwd, snapshotCwd]);
@@ -80718,8 +80940,8 @@ async function materializeJjSnapshot(jjRuntime, gitRuntime, options) {
80718
80940
  const fromRevision = await resolveJjSnapshotEndpoint(jjRuntime, endpoints.from, options.cwd);
80719
80941
  const toRevision = await resolveJjSnapshotEndpoint(jjRuntime, endpoints.to, options.cwd);
80720
80942
  const filesets = options.includedExtensions.map((extension) => `root-glob-i:"**/*${extension}"`);
80721
- const tempRoot = await mkdtemp(join16(tmpdir2(), "plannotator-review-jj-snapshot-"));
80722
- const snapshotCwd = join16(tempRoot, "repo");
80943
+ const tempRoot = await mkdtemp(join17(tmpdir2(), "plannotator-review-jj-snapshot-"));
80944
+ const snapshotCwd = join17(tempRoot, "repo");
80723
80945
  const cleanup = () => removeDirectoryBestEffort(tempRoot);
80724
80946
  try {
80725
80947
  await git(gitRuntime, tempRoot, ["init", "--quiet", "--", snapshotCwd]);
@@ -81438,9 +81660,9 @@ var init_worktree_pool = () => {};
81438
81660
  // ../../packages/shared/semantic-diff.ts
81439
81661
  import { spawn as spawn4 } from "child_process";
81440
81662
  import { createHash as createHash3 } from "crypto";
81441
- import { existsSync as existsSync14, mkdirSync as mkdirSync8 } from "fs";
81663
+ import { existsSync as existsSync14, mkdirSync as mkdirSync9 } from "fs";
81442
81664
  import { tmpdir as tmpdir3 } from "os";
81443
- import { delimiter, join as join17 } from "path";
81665
+ import { delimiter, join as join18 } from "path";
81444
81666
  function defaultRunCommand(command, args, options = {}) {
81445
81667
  return new Promise((resolveResult) => {
81446
81668
  let settled = false;
@@ -81531,17 +81753,17 @@ function semBinaryName(platform) {
81531
81753
  return platform === "win32" ? "sem.exe" : "sem";
81532
81754
  }
81533
81755
  function getManagedSemBinaryPath(dataDir = getPlannotatorDataDir(), platform = process.platform) {
81534
- return join17(dataDir, "vendor", "sem", PLANNOTATOR_SEM_VERSION, semBinaryName(platform));
81756
+ return join18(dataDir, "vendor", "sem", PLANNOTATOR_SEM_VERSION, semBinaryName(platform));
81535
81757
  }
81536
81758
  function getSemanticDiffScratchCwd(dataDir = getPlannotatorDataDir()) {
81537
- const primary = join17(dataDir, "semantic-diff", "patch-only");
81759
+ const primary = join18(dataDir, "semantic-diff", "patch-only");
81538
81760
  try {
81539
- mkdirSync8(primary, { recursive: true });
81761
+ mkdirSync9(primary, { recursive: true });
81540
81762
  return primary;
81541
81763
  } catch {
81542
- const fallback = join17(tmpdir3(), "plannotator-semantic-diff");
81764
+ const fallback = join18(tmpdir3(), "plannotator-semantic-diff");
81543
81765
  try {
81544
- mkdirSync8(fallback, { recursive: true });
81766
+ mkdirSync9(fallback, { recursive: true });
81545
81767
  return fallback;
81546
81768
  } catch {
81547
81769
  return tmpdir3();
@@ -81556,7 +81778,7 @@ function pathCandidates(runtime4) {
81556
81778
  const pathext = (runtime4.env.PATHEXT || ".EXE;.CMD;.BAT;.COM").split(";").map((ext) => ext.trim()).filter(Boolean);
81557
81779
  for (const dir of (runtime4.env.PATH || "").split(runtime4.pathDelimiter)) {
81558
81780
  for (const ext of pathext) {
81559
- const candidate = join17(dir, `sem${ext.toLowerCase()}`);
81781
+ const candidate = join18(dir, `sem${ext.toLowerCase()}`);
81560
81782
  if (runtime4.fileExists(candidate)) {
81561
81783
  return [{ command: candidate, source: "path", explicit: false }];
81562
81784
  }
@@ -83610,14 +83832,14 @@ var init_call_flow_pack_locks = __esm(() => {
83610
83832
  import { randomUUID } from "crypto";
83611
83833
  import {
83612
83834
  closeSync as closeSync4,
83613
- mkdirSync as mkdirSync9,
83835
+ mkdirSync as mkdirSync10,
83614
83836
  openSync as openSync4,
83615
83837
  readFileSync as readFileSync10,
83616
83838
  rmSync as rmSync3,
83617
83839
  statSync as statSync12,
83618
- writeFileSync as writeFileSync6
83840
+ writeFileSync as writeFileSync7
83619
83841
  } from "fs";
83620
- import { dirname as dirname6, join as join18 } from "path";
83842
+ import { dirname as dirname6, join as join19 } from "path";
83621
83843
  function hasErrorCode(error, code) {
83622
83844
  return typeof error === "object" && error !== null && "code" in error && error.code === code;
83623
83845
  }
@@ -83628,7 +83850,7 @@ function removeOwnedLock(lockPath, token2) {
83628
83850
  } catch {}
83629
83851
  }
83630
83852
  async function withCallFlowInstallLock(work, options = {}) {
83631
- const lockPath = options.lockPath ?? join18(getPlannotatorDataDir(), "vendor", "call-flow", ".install.lock");
83853
+ const lockPath = options.lockPath ?? join19(getPlannotatorDataDir(), "vendor", "call-flow", ".install.lock");
83632
83854
  const staleAfterMs = options.staleAfterMs ?? DEFAULT_STALE_AFTER_MS;
83633
83855
  const waitTimeoutMs = options.waitTimeoutMs ?? DEFAULT_WAIT_TIMEOUT_MS;
83634
83856
  const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
@@ -83636,12 +83858,12 @@ async function withCallFlowInstallLock(work, options = {}) {
83636
83858
  const sleep = options.sleep ?? ((milliseconds) => new Promise((resolve12) => setTimeout(resolve12, milliseconds)));
83637
83859
  const token2 = `${process.pid}:${randomUUID()}`;
83638
83860
  const deadline = now() + waitTimeoutMs;
83639
- mkdirSync9(dirname6(lockPath), { recursive: true });
83861
+ mkdirSync10(dirname6(lockPath), { recursive: true });
83640
83862
  while (true) {
83641
83863
  try {
83642
83864
  const descriptor = openSync4(lockPath, "wx", 384);
83643
83865
  try {
83644
- writeFileSync6(descriptor, token2, "utf8");
83866
+ writeFileSync7(descriptor, token2, "utf8");
83645
83867
  } finally {
83646
83868
  closeSync4(descriptor);
83647
83869
  }
@@ -83961,24 +84183,24 @@ var init_call_flow_types = __esm(() => {
83961
84183
 
83962
84184
  // ../../packages/shared/call-flow.ts
83963
84185
  import { createHash as createHash4 } from "crypto";
83964
- import { chmodSync as chmodSync2, cpSync, existsSync as existsSync15, mkdirSync as mkdirSync10, readFileSync as readFileSync11, readdirSync as readdirSync5, renameSync as renameSync5, rmSync as rmSync4, statSync as statSync13, writeFileSync as writeFileSync7 } from "fs";
84186
+ import { chmodSync as chmodSync2, cpSync, existsSync as existsSync15, mkdirSync as mkdirSync11, readFileSync as readFileSync11, readdirSync as readdirSync5, renameSync as renameSync5, rmSync as rmSync4, statSync as statSync13, writeFileSync as writeFileSync8 } from "fs";
83965
84187
  import { mkdtemp as mkdtemp2 } from "fs/promises";
83966
84188
  import { tmpdir as tmpdir4 } from "os";
83967
- import { basename as basename7, dirname as dirname7, isAbsolute as isAbsolute7, join as join19 } from "path";
84189
+ import { basename as basename7, dirname as dirname7, isAbsolute as isAbsolute7, join as join20 } from "path";
83968
84190
  import { spawn as spawn5, spawnSync as spawnSync5 } from "child_process";
83969
84191
  function getCallFlowManagedRuntimeDir(dataDir = getPlannotatorDataDir()) {
83970
- return join19(dataDir, "vendor", "call-flow", `calldiff-${CALLDIFF_VERSION}`);
84192
+ return join20(dataDir, "vendor", "call-flow", `calldiff-${CALLDIFF_VERSION}`);
83971
84193
  }
83972
84194
  function getCallFlowGrammarCacheDir(runtimeDir) {
83973
- return join19(runtimeDir, "grammar-cache");
84195
+ return join20(runtimeDir, "grammar-cache");
83974
84196
  }
83975
84197
  function getLanguagePackageRoot(runtimeDir, language) {
83976
84198
  if (language.kind === "core")
83977
- return join19(runtimeDir, "node_modules", "tree-sitter-typescript");
83978
- return join19(getCallFlowGrammarCacheDir(runtimeDir), "node_modules", ...(language.packageName ?? "").split("/"));
84199
+ return join20(runtimeDir, "node_modules", "tree-sitter-typescript");
84200
+ return join20(getCallFlowGrammarCacheDir(runtimeDir), "node_modules", ...(language.packageName ?? "").split("/"));
83979
84201
  }
83980
84202
  function getLanguageLockMarker(runtimeDir, id) {
83981
- return join19(getCallFlowGrammarCacheDir(runtimeDir), ".plannotator-locks", `${id}.json`);
84203
+ return join20(getCallFlowGrammarCacheDir(runtimeDir), ".plannotator-locks", `${id}.json`);
83982
84204
  }
83983
84205
  function packHasCommittedLock(runtimeDir, id) {
83984
84206
  try {
@@ -83990,7 +84212,7 @@ function packHasCommittedLock(runtimeDir, id) {
83990
84212
  }
83991
84213
  function installDirHasCommittedPackLock(installDir, id) {
83992
84214
  try {
83993
- const installed = JSON.parse(readFileSync11(join19(installDir, "package-lock.json"), "utf8"));
84215
+ const installed = JSON.parse(readFileSync11(join20(installDir, "package-lock.json"), "utf8"));
83994
84216
  return JSON.stringify(installed) === JSON.stringify(CALL_FLOW_PACK_LOCKS[id]);
83995
84217
  } catch {
83996
84218
  return false;
@@ -83999,21 +84221,21 @@ function installDirHasCommittedPackLock(installDir, id) {
83999
84221
  function isLanguageInstalled(runtimeDir, language, managed) {
84000
84222
  if (language.kind === "core")
84001
84223
  return true;
84002
- const packageRoot = managed ? getLanguagePackageRoot(runtimeDir, language) : join19(runtimeDir, "node_modules", ...(language.packageName ?? "").split("/"));
84224
+ const packageRoot = managed ? getLanguagePackageRoot(runtimeDir, language) : join20(runtimeDir, "node_modules", ...(language.packageName ?? "").split("/"));
84003
84225
  if (readPackageVersion(packageRoot) !== language.packageVersion)
84004
84226
  return false;
84005
84227
  return !managed || packHasCommittedLock(runtimeDir, language.id);
84006
84228
  }
84007
84229
  function readPackageVersion(packageRoot) {
84008
84230
  try {
84009
- const parsed = JSON.parse(readFileSync11(join19(packageRoot, "package.json"), "utf8"));
84231
+ const parsed = JSON.parse(readFileSync11(join20(packageRoot, "package.json"), "utf8"));
84010
84232
  return typeof parsed.version === "string" ? parsed.version : null;
84011
84233
  } catch {
84012
84234
  return null;
84013
84235
  }
84014
84236
  }
84015
84237
  function resolvePackageEntry(packageRoot) {
84016
- const entry = join19(packageRoot, "dist", "run.js");
84238
+ const entry = join20(packageRoot, "dist", "run.js");
84017
84239
  return existsSync15(entry) ? entry : null;
84018
84240
  }
84019
84241
  function runtimeJson(value) {
@@ -84022,7 +84244,7 @@ function runtimeJson(value) {
84022
84244
  }
84023
84245
  function hasCommittedRuntimeLock(runtimeDir) {
84024
84246
  try {
84025
- const lock = JSON.parse(readFileSync11(join19(runtimeDir, "package-lock.json"), "utf8"));
84247
+ const lock = JSON.parse(readFileSync11(join20(runtimeDir, "package-lock.json"), "utf8"));
84026
84248
  return JSON.stringify(lock) === JSON.stringify(package_lock_default);
84027
84249
  } catch {
84028
84250
  return false;
@@ -84046,7 +84268,7 @@ function findExecutable(name) {
84046
84268
  if (!directory)
84047
84269
  continue;
84048
84270
  for (const extension of extensions) {
84049
- const candidate = join19(directory, `${name}${extension}`);
84271
+ const candidate = join20(directory, `${name}${extension}`);
84050
84272
  if (existsSync15(candidate))
84051
84273
  return candidate;
84052
84274
  }
@@ -84079,7 +84301,7 @@ async function resolveCallFlowRuntime() {
84079
84301
  }
84080
84302
  const runtimeDir = override ?? getCallFlowManagedRuntimeDir();
84081
84303
  const managed = !override;
84082
- const packageRoot = override ? runtimeDir : join19(runtimeDir, "node_modules", "calldiff");
84304
+ const packageRoot = override ? runtimeDir : join20(runtimeDir, "node_modules", "calldiff");
84083
84305
  const version = readPackageVersion(packageRoot);
84084
84306
  const packageEntry = resolvePackageEntry(packageRoot);
84085
84307
  if (!version || !packageEntry) {
@@ -84093,7 +84315,7 @@ async function resolveCallFlowRuntime() {
84093
84315
  return { ok: false, reason: "version-mismatch", message: `CallDiff ${version} is installed; Plannotator requires ${CALLDIFF_VERSION}.` };
84094
84316
  }
84095
84317
  if (!override) {
84096
- const revisionPath = join19(runtimeDir, ".calldiff-revision");
84318
+ const revisionPath = join20(runtimeDir, ".calldiff-revision");
84097
84319
  const installedRevision = existsSync15(revisionPath) ? readFileSync11(revisionPath, "utf8").trim() : "";
84098
84320
  if (installedRevision !== CALLDIFF_COMMIT) {
84099
84321
  return { ok: false, reason: "revision-mismatch", message: "The managed Call flow core is stale and needs reinstalling." };
@@ -84102,7 +84324,7 @@ async function resolveCallFlowRuntime() {
84102
84324
  return { ok: false, reason: "runtime-lock-mismatch", message: "The managed Call flow core dependency lock is stale and needs reinstalling." };
84103
84325
  }
84104
84326
  }
84105
- const invalidCore = CORE_RUNTIME_DEPENDENCIES.filter(({ packageName, version: expected }) => readPackageVersion(join19(runtimeDir, "node_modules", packageName)) !== expected);
84327
+ const invalidCore = CORE_RUNTIME_DEPENDENCIES.filter(({ packageName, version: expected }) => readPackageVersion(join20(runtimeDir, "node_modules", packageName)) !== expected);
84106
84328
  if (invalidCore.length > 0) {
84107
84329
  return {
84108
84330
  ok: false,
@@ -84125,8 +84347,8 @@ async function resolveCallFlowRuntime() {
84125
84347
  };
84126
84348
  }
84127
84349
  function writeRuntimeManifest(runtimeDir) {
84128
- writeFileSync7(join19(runtimeDir, "package.json"), runtimeJson(package_default), "utf8");
84129
- writeFileSync7(join19(runtimeDir, "package-lock.json"), runtimeJson(package_lock_default), "utf8");
84350
+ writeFileSync8(join20(runtimeDir, "package.json"), runtimeJson(package_default), "utf8");
84351
+ writeFileSync8(join20(runtimeDir, "package-lock.json"), runtimeJson(package_lock_default), "utf8");
84130
84352
  }
84131
84353
  function removeDirectoryBestEffort2(path2) {
84132
84354
  try {
@@ -84164,7 +84386,7 @@ async function downloadVerifiedCallDiffArchive(destination, onStage) {
84164
84386
  if (integrity !== CALLDIFF_SOURCE_INTEGRITY) {
84165
84387
  throw new Error("CallDiff source archive failed its repository-pinned SHA-512 check.");
84166
84388
  }
84167
- writeFileSync7(destination, Buffer.concat(chunks, totalBytes));
84389
+ writeFileSync8(destination, Buffer.concat(chunks, totalBytes));
84168
84390
  }
84169
84391
  async function downloadVerifiedLockArtifacts(lock, destination, onStage) {
84170
84392
  const packages = lock.packages;
@@ -84208,21 +84430,21 @@ async function downloadVerifiedLockArtifacts(lock, destination, onStage) {
84208
84430
  downloaded.push({ ...spec, bytes: Buffer.concat(chunks, artifactBytes) });
84209
84431
  }
84210
84432
  onStage("verifying");
84211
- mkdirSync10(destination, { recursive: true });
84433
+ mkdirSync11(destination, { recursive: true });
84212
84434
  const verifiedPaths = [];
84213
84435
  for (const [index, artifact] of downloaded.entries()) {
84214
84436
  const integrity = `sha512-${createHash4("sha512").update(artifact.bytes).digest("base64")}`;
84215
84437
  if (integrity !== artifact.integrity) {
84216
84438
  throw new Error(`Pinned grammar package failed its repository-owned SHA-512 check: ${artifact.url}`);
84217
84439
  }
84218
- const path2 = join19(destination, `artifact-${index}.tgz`);
84219
- writeFileSync7(path2, artifact.bytes);
84440
+ const path2 = join20(destination, `artifact-${index}.tgz`);
84441
+ writeFileSync8(path2, artifact.bytes);
84220
84442
  verifiedPaths.push(path2);
84221
84443
  }
84222
84444
  return verifiedPaths;
84223
84445
  }
84224
84446
  async function seedVerifiedNpmCache(npmPath, installDir, artifacts) {
84225
- const cacheDir = join19(installDir, ".verified-npm-cache");
84447
+ const cacheDir = join20(installDir, ".verified-npm-cache");
84226
84448
  for (const artifact of artifacts) {
84227
84449
  const cached2 = await runCommand(npmPath, [
84228
84450
  "cache",
@@ -84257,26 +84479,26 @@ function assertPathRemoved(path2, description) {
84257
84479
  throw new Error(`Call-flow pruning did not remove ${description}.`);
84258
84480
  }
84259
84481
  function prunePrebuilds(packageRoot) {
84260
- const prebuilds = join19(packageRoot, "prebuilds");
84482
+ const prebuilds = join20(packageRoot, "prebuilds");
84261
84483
  if (!existsSync15(prebuilds))
84262
84484
  return;
84263
84485
  const currentPlatform = `${process.platform}-${process.arch}`;
84264
84486
  for (const entry of readdirSync5(prebuilds)) {
84265
84487
  if (entry !== currentPlatform)
84266
- removeDirectoryStrict(join19(prebuilds, entry));
84488
+ removeDirectoryStrict(join20(prebuilds, entry));
84267
84489
  }
84268
84490
  }
84269
84491
  function assertNativePackagePruned(packageRoot) {
84270
84492
  for (const directory of PRUNED_PACKAGE_DIRECTORIES) {
84271
84493
  if (directory !== "build") {
84272
- assertPathRemoved(join19(packageRoot, directory), `${basename7(packageRoot)}/${directory}`);
84494
+ assertPathRemoved(join20(packageRoot, directory), `${basename7(packageRoot)}/${directory}`);
84273
84495
  }
84274
84496
  }
84275
- assertPathRemoved(join19(packageRoot, "node_modules"), `${basename7(packageRoot)}/node_modules`);
84497
+ assertPathRemoved(join20(packageRoot, "node_modules"), `${basename7(packageRoot)}/node_modules`);
84276
84498
  for (const file of ["binding.gyp", "grammar.js", "tree-sitter.json"]) {
84277
- assertPathRemoved(join19(packageRoot, file), `${basename7(packageRoot)}/${file}`);
84499
+ assertPathRemoved(join20(packageRoot, file), `${basename7(packageRoot)}/${file}`);
84278
84500
  }
84279
- const prebuilds = join19(packageRoot, "prebuilds");
84501
+ const prebuilds = join20(packageRoot, "prebuilds");
84280
84502
  if (existsSync15(prebuilds)) {
84281
84503
  const currentPlatform = `${process.platform}-${process.arch}`;
84282
84504
  const foreign = readdirSync5(prebuilds).filter((entry) => entry !== currentPlatform);
@@ -84288,7 +84510,7 @@ function assertNativePackagePruned(packageRoot) {
84288
84510
  if (!existsSync15(directory))
84289
84511
  return;
84290
84512
  for (const entry of readdirSync5(directory, { withFileTypes: true })) {
84291
- const path2 = join19(directory, entry.name);
84513
+ const path2 = join20(directory, entry.name);
84292
84514
  if (entry.isDirectory())
84293
84515
  assertOnlyNativeAddons(path2);
84294
84516
  else if (!entry.isFile() || !entry.name.endsWith(".node")) {
@@ -84296,8 +84518,8 @@ function assertNativePackagePruned(packageRoot) {
84296
84518
  }
84297
84519
  }
84298
84520
  };
84299
- assertOnlyNativeAddons(join19(packageRoot, "build"));
84300
- const bindings = join19(packageRoot, "bindings", "node");
84521
+ assertOnlyNativeAddons(join20(packageRoot, "build"));
84522
+ const bindings = join20(packageRoot, "bindings", "node");
84301
84523
  if (existsSync15(bindings)) {
84302
84524
  const retainedSource = readdirSync5(bindings).find((file) => file === "binding.cc" || file.endsWith("_test.js"));
84303
84525
  if (retainedSource)
@@ -84315,7 +84537,7 @@ function pruneCallFlowNativePackage(packageRoot) {
84315
84537
  if (!existsSync15(directory))
84316
84538
  return;
84317
84539
  for (const entry of readdirSync5(directory, { withFileTypes: true })) {
84318
- const path2 = join19(directory, entry.name);
84540
+ const path2 = join20(directory, entry.name);
84319
84541
  if (entry.isDirectory())
84320
84542
  collectBuiltAddons(path2);
84321
84543
  else if (entry.isFile() && entry.name.endsWith(".node")) {
@@ -84323,56 +84545,56 @@ function pruneCallFlowNativePackage(packageRoot) {
84323
84545
  }
84324
84546
  }
84325
84547
  };
84326
- collectBuiltAddons(join19(packageRoot, "build"));
84548
+ collectBuiltAddons(join20(packageRoot, "build"));
84327
84549
  prunePrebuilds(packageRoot);
84328
84550
  for (const directory of PRUNED_PACKAGE_DIRECTORIES) {
84329
- removeDirectoryStrict(join19(packageRoot, directory));
84551
+ removeDirectoryStrict(join20(packageRoot, directory));
84330
84552
  }
84331
- removeDirectoryStrict(join19(packageRoot, "node_modules"));
84332
- const bindings = join19(packageRoot, "bindings", "node");
84553
+ removeDirectoryStrict(join20(packageRoot, "node_modules"));
84554
+ const bindings = join20(packageRoot, "bindings", "node");
84333
84555
  if (existsSync15(bindings)) {
84334
84556
  for (const file of readdirSync5(bindings)) {
84335
84557
  if (file === "binding.cc" || file.endsWith("_test.js"))
84336
- rmSync4(join19(bindings, file), { force: true });
84558
+ rmSync4(join20(bindings, file), { force: true });
84337
84559
  }
84338
84560
  }
84339
84561
  for (const file of ["binding.gyp", "grammar.js", "tree-sitter.json"]) {
84340
- rmSync4(join19(packageRoot, file), { force: true });
84562
+ rmSync4(join20(packageRoot, file), { force: true });
84341
84563
  }
84342
84564
  if (builtAddons.length > 0) {
84343
- const releaseDir = join19(packageRoot, "build", "Release");
84344
- mkdirSync10(releaseDir, { recursive: true });
84565
+ const releaseDir = join20(packageRoot, "build", "Release");
84566
+ mkdirSync11(releaseDir, { recursive: true });
84345
84567
  for (const addon of builtAddons)
84346
- writeFileSync7(join19(releaseDir, addon.name), addon.bytes);
84568
+ writeFileSync8(join20(releaseDir, addon.name), addon.bytes);
84347
84569
  }
84348
84570
  if (existsSync15(packageRoot)) {
84349
84571
  for (const file of readdirSync5(packageRoot)) {
84350
84572
  if (file.endsWith(".wasm"))
84351
- rmSync4(join19(packageRoot, file), { force: true });
84573
+ rmSync4(join20(packageRoot, file), { force: true });
84352
84574
  }
84353
84575
  }
84354
84576
  assertNativePackagePruned(packageRoot);
84355
84577
  }
84356
84578
  function pruneCoreRuntime(runtimeDir) {
84357
84579
  for (const packageName of ["tree-sitter", ...CORE_GRAMMAR_PACKAGES]) {
84358
- pruneCallFlowNativePackage(join19(runtimeDir, "node_modules", packageName));
84580
+ pruneCallFlowNativePackage(join20(runtimeDir, "node_modules", packageName));
84359
84581
  }
84360
- const packageRoot = join19(runtimeDir, "node_modules", "calldiff");
84361
- removeDirectoryStrict(join19(packageRoot, "src"));
84362
- for (const file of readdirSync5(join19(packageRoot, "dist"))) {
84582
+ const packageRoot = join20(runtimeDir, "node_modules", "calldiff");
84583
+ removeDirectoryStrict(join20(packageRoot, "src"));
84584
+ for (const file of readdirSync5(join20(packageRoot, "dist"))) {
84363
84585
  if (file.startsWith("cli.") || file === "index.js" || file === "index.d.ts") {
84364
- rmSync4(join19(packageRoot, "dist", file), { force: true });
84586
+ rmSync4(join20(packageRoot, "dist", file), { force: true });
84365
84587
  }
84366
84588
  }
84367
84589
  for (const packagePath of CORE_PRUNED_PACKAGES) {
84368
- removeDirectoryStrict(join19(runtimeDir, "node_modules", ...packagePath.split("/")));
84590
+ removeDirectoryStrict(join20(runtimeDir, "node_modules", ...packagePath.split("/")));
84369
84591
  }
84370
- removeDirectoryStrict(join19(runtimeDir, "node_modules", ".bin"));
84371
- assertPathRemoved(join19(packageRoot, "src"), "calldiff/src");
84592
+ removeDirectoryStrict(join20(runtimeDir, "node_modules", ".bin"));
84593
+ assertPathRemoved(join20(packageRoot, "src"), "calldiff/src");
84372
84594
  for (const packagePath of CORE_PRUNED_PACKAGES) {
84373
- assertPathRemoved(join19(runtimeDir, "node_modules", ...packagePath.split("/")), `node_modules/${packagePath}`);
84595
+ assertPathRemoved(join20(runtimeDir, "node_modules", ...packagePath.split("/")), `node_modules/${packagePath}`);
84374
84596
  }
84375
- assertPathRemoved(join19(runtimeDir, "node_modules", ".bin"), "node_modules/.bin");
84597
+ assertPathRemoved(join20(runtimeDir, "node_modules", ".bin"), "node_modules/.bin");
84376
84598
  }
84377
84599
  function validationFilesForLanguage(id) {
84378
84600
  if (id === CALL_FLOW_CORE_LANGUAGE_ID) {
@@ -84382,7 +84604,7 @@ function validationFilesForLanguage(id) {
84382
84604
  return [`validation${extension}`];
84383
84605
  }
84384
84606
  async function validatePrunedLanguage(nodePath, runtimeDir, grammarCacheDir, ids) {
84385
- const extractEntry = join19(runtimeDir, "node_modules", "calldiff", "dist", "extract.js");
84607
+ const extractEntry = join20(runtimeDir, "node_modules", "calldiff", "dist", "extract.js");
84386
84608
  const script = String.raw`
84387
84609
  import { pathToFileURL } from "node:url";
84388
84610
  const request = JSON.parse(process.argv[1]);
@@ -84416,8 +84638,8 @@ for (const file of request.files) mod.extractFunctions(file, "");
84416
84638
  }
84417
84639
  }
84418
84640
  function ensureGrammarCacheManifest(cacheDir) {
84419
- mkdirSync10(cacheDir, { recursive: true });
84420
- writeFileSync7(join19(cacheDir, "package.json"), runtimeJson({
84641
+ mkdirSync11(cacheDir, { recursive: true });
84642
+ writeFileSync8(join20(cacheDir, "package.json"), runtimeJson({
84421
84643
  name: "plannotator-calldiff-grammar-cache",
84422
84644
  private: true
84423
84645
  }), "utf8");
@@ -84431,13 +84653,13 @@ async function preserveValidatedLanguagePacks(nodePath, currentRuntimeDir, candi
84431
84653
  const packId = language.id;
84432
84654
  const currentRoot = getLanguagePackageRoot(currentRuntimeDir, language);
84433
84655
  const candidateRoot = getLanguagePackageRoot(candidateRuntimeDir, language);
84434
- mkdirSync10(dirname7(candidateRoot), { recursive: true });
84656
+ mkdirSync11(dirname7(candidateRoot), { recursive: true });
84435
84657
  cpSync(currentRoot, candidateRoot, { recursive: true, force: true });
84436
84658
  try {
84437
84659
  await validatePrunedLanguage(nodePath, candidateRuntimeDir, candidateCache, [language.id]);
84438
84660
  const marker = getLanguageLockMarker(candidateRuntimeDir, language.id);
84439
- mkdirSync10(dirname7(marker), { recursive: true });
84440
- writeFileSync7(marker, runtimeJson(CALL_FLOW_PACK_LOCKS[packId]), "utf8");
84661
+ mkdirSync11(dirname7(marker), { recursive: true });
84662
+ writeFileSync8(marker, runtimeJson(CALL_FLOW_PACK_LOCKS[packId]), "utf8");
84441
84663
  } catch {
84442
84664
  removeDirectoryStrict(candidateRoot);
84443
84665
  }
@@ -84449,7 +84671,7 @@ function measureCallFlowDirectoryBytes(path2) {
84449
84671
  const stat5 = statSync13(path2);
84450
84672
  if (!stat5.isDirectory())
84451
84673
  return stat5.size;
84452
- return readdirSync5(path2).reduce((total, entry) => total + measureCallFlowDirectoryBytes(join19(path2, entry)), 0);
84674
+ return readdirSync5(path2).reduce((total, entry) => total + measureCallFlowDirectoryBytes(join20(path2, entry)), 0);
84453
84675
  }
84454
84676
  async function installCallFlowRuntime(onStage = () => {}) {
84455
84677
  return await withCallFlowInstallLock(() => installCallFlowRuntimeUnlocked(onStage));
@@ -84469,12 +84691,12 @@ async function installCallFlowRuntimeUnlocked(onStage) {
84469
84691
  return { ok: true, status: "already-installed", runtimeDir, languageId: CALL_FLOW_CORE_LANGUAGE_ID, message: `Call-flow core already installed at ${runtimeDir}.` };
84470
84692
  }
84471
84693
  const runtimeParent = dirname7(runtimeDir);
84472
- mkdirSync10(runtimeParent, { recursive: true });
84473
- const installDir = await mkdtemp2(join19(runtimeParent, ".calldiff-install-"));
84694
+ mkdirSync11(runtimeParent, { recursive: true });
84695
+ const installDir = await mkdtemp2(join20(runtimeParent, ".calldiff-install-"));
84474
84696
  const backupDir = `${runtimeDir}.previous-${process.pid}-${Date.now()}`;
84475
84697
  try {
84476
84698
  writeRuntimeManifest(installDir);
84477
- await downloadVerifiedCallDiffArchive(join19(installDir, CALLDIFF_ARCHIVE_FILE), onStage);
84699
+ await downloadVerifiedCallDiffArchive(join20(installDir, CALLDIFF_ARCHIVE_FILE), onStage);
84478
84700
  onStage("installing-deps");
84479
84701
  const install = await runCommand(npmPath, [
84480
84702
  "ci",
@@ -84508,8 +84730,8 @@ async function installCallFlowRuntimeUnlocked(onStage) {
84508
84730
  throw new Error(rebuild.stderr.trim() || rebuild.stdout.trim() || "grammar rebuild failed");
84509
84731
  }
84510
84732
  onStage("building");
84511
- const packageRoot = join19(installDir, "node_modules", "calldiff");
84512
- const tscPath = process.platform === "win32" ? join19(installDir, "node_modules", ".bin", "tsc.cmd") : join19(installDir, "node_modules", ".bin", "tsc");
84733
+ const packageRoot = join20(installDir, "node_modules", "calldiff");
84734
+ const tscPath = process.platform === "win32" ? join20(installDir, "node_modules", ".bin", "tsc.cmd") : join20(installDir, "node_modules", ".bin", "tsc");
84513
84735
  const build = await runCommand(tscPath, ["-p", "tsconfig.json"], {
84514
84736
  cwd: packageRoot,
84515
84737
  timeoutMs: 120000,
@@ -84518,8 +84740,8 @@ async function installCallFlowRuntimeUnlocked(onStage) {
84518
84740
  if (build.exitCode !== 0) {
84519
84741
  throw new Error(build.stderr.trim() || build.stdout.trim() || "CallDiff TypeScript build failed");
84520
84742
  }
84521
- rmSync4(join19(installDir, CALLDIFF_ARCHIVE_FILE), { force: true });
84522
- writeFileSync7(join19(installDir, ".calldiff-revision"), `${CALLDIFF_COMMIT}
84743
+ rmSync4(join20(installDir, CALLDIFF_ARCHIVE_FILE), { force: true });
84744
+ writeFileSync8(join20(installDir, ".calldiff-revision"), `${CALLDIFF_COMMIT}
84523
84745
  `, "utf8");
84524
84746
  if (readPackageVersion(packageRoot) !== CALLDIFF_VERSION || !resolvePackageEntry(packageRoot)) {
84525
84747
  throw new Error("The verified CallDiff package did not produce the expected runtime entry.");
@@ -84588,16 +84810,16 @@ async function installCallFlowLanguagePackUnlocked(id, onStage) {
84588
84810
  return { ok: true, status: "already-installed", runtimeDir, languageId: id, message: `${language.label} support is already installed.` };
84589
84811
  }
84590
84812
  const runtimeParent = dirname7(runtimeDir);
84591
- const installDir = await mkdtemp2(join19(runtimeParent, `.calldiff-${id}-install-`));
84592
- const stagingCache = await mkdtemp2(join19(runtimeDir, `.grammar-cache-${id}-`));
84593
- const packageRoot = join19(installDir, "node_modules", ...(language.packageName ?? "").split("/"));
84594
- const stagedPackageRoot = join19(stagingCache, "node_modules", ...(language.packageName ?? "").split("/"));
84813
+ const installDir = await mkdtemp2(join20(runtimeParent, `.calldiff-${id}-install-`));
84814
+ const stagingCache = await mkdtemp2(join20(runtimeDir, `.grammar-cache-${id}-`));
84815
+ const packageRoot = join20(installDir, "node_modules", ...(language.packageName ?? "").split("/"));
84816
+ const stagedPackageRoot = join20(stagingCache, "node_modules", ...(language.packageName ?? "").split("/"));
84595
84817
  const finalPackageRoot = getLanguagePackageRoot(runtimeDir, language);
84596
84818
  const backupPackageRoot = `${finalPackageRoot}.previous-${process.pid}-${Date.now()}`;
84597
84819
  try {
84598
- writeFileSync7(join19(installDir, "package.json"), runtimeJson(packManifest(id)), "utf8");
84599
- writeFileSync7(join19(installDir, "package-lock.json"), runtimeJson(CALL_FLOW_PACK_LOCKS[id]), "utf8");
84600
- const verifiedArtifacts = await downloadVerifiedLockArtifacts(CALL_FLOW_PACK_LOCKS[id], join19(installDir, ".verified-artifacts"), onStage);
84820
+ writeFileSync8(join20(installDir, "package.json"), runtimeJson(packManifest(id)), "utf8");
84821
+ writeFileSync8(join20(installDir, "package-lock.json"), runtimeJson(CALL_FLOW_PACK_LOCKS[id]), "utf8");
84822
+ const verifiedArtifacts = await downloadVerifiedLockArtifacts(CALL_FLOW_PACK_LOCKS[id], join20(installDir, ".verified-artifacts"), onStage);
84601
84823
  const npmCacheDir = await seedVerifiedNpmCache(npmPath, installDir, verifiedArtifacts);
84602
84824
  onStage("installing-deps");
84603
84825
  const install = await runCommand(npmPath, [
@@ -84648,18 +84870,18 @@ async function installCallFlowLanguagePackUnlocked(id, onStage) {
84648
84870
  const sizeBeforePruneBytes = measureCallFlowDirectoryBytes(packageRoot);
84649
84871
  pruneCallFlowNativePackage(packageRoot);
84650
84872
  const sizeAfterPruneBytes = measureCallFlowDirectoryBytes(packageRoot);
84651
- mkdirSync10(dirname7(stagedPackageRoot), { recursive: true });
84873
+ mkdirSync11(dirname7(stagedPackageRoot), { recursive: true });
84652
84874
  cpSync(packageRoot, stagedPackageRoot, { recursive: true });
84653
84875
  ensureGrammarCacheManifest(stagingCache);
84654
84876
  await validatePrunedLanguage(nodePath, runtimeDir, stagingCache, [id]);
84655
84877
  ensureGrammarCacheManifest(getCallFlowGrammarCacheDir(runtimeDir));
84656
- mkdirSync10(dirname7(finalPackageRoot), { recursive: true });
84878
+ mkdirSync11(dirname7(finalPackageRoot), { recursive: true });
84657
84879
  if (existsSync15(finalPackageRoot))
84658
84880
  renameSync5(finalPackageRoot, backupPackageRoot);
84659
84881
  try {
84660
84882
  renameSync5(stagedPackageRoot, finalPackageRoot);
84661
- mkdirSync10(dirname7(getLanguageLockMarker(runtimeDir, id)), { recursive: true });
84662
- writeFileSync7(getLanguageLockMarker(runtimeDir, id), runtimeJson(CALL_FLOW_PACK_LOCKS[id]), "utf8");
84883
+ mkdirSync11(dirname7(getLanguageLockMarker(runtimeDir, id)), { recursive: true });
84884
+ writeFileSync8(getLanguageLockMarker(runtimeDir, id), runtimeJson(CALL_FLOW_PACK_LOCKS[id]), "utf8");
84663
84885
  } catch (error) {
84664
84886
  removeDirectoryBestEffort2(finalPackageRoot);
84665
84887
  if (existsSync15(backupPackageRoot))
@@ -84772,15 +84994,15 @@ async function runCommand(executable, args, options = {}) {
84772
84994
  }
84773
84995
  function grammarChecksForLanguages(runtime4, ids) {
84774
84996
  const checks2 = new Map;
84775
- const callDiffRoot = runtime4.managed ? join19(runtime4.runtimeDir, "node_modules", "calldiff") : runtime4.runtimeDir;
84997
+ const callDiffRoot = runtime4.managed ? join20(runtime4.runtimeDir, "node_modules", "calldiff") : runtime4.runtimeDir;
84776
84998
  checks2.set("calldiff", {
84777
- packageJson: join19(callDiffRoot, "package.json"),
84999
+ packageJson: join20(callDiffRoot, "package.json"),
84778
85000
  version: CALLDIFF_VERSION,
84779
85001
  message: `CallDiff ${CALLDIFF_VERSION} is not installed.`
84780
85002
  });
84781
85003
  for (const dependency of CORE_RUNTIME_DEPENDENCIES) {
84782
85004
  checks2.set(dependency.packageName, {
84783
- packageJson: join19(runtime4.runtimeDir, "node_modules", dependency.packageName, "package.json"),
85005
+ packageJson: join20(runtime4.runtimeDir, "node_modules", dependency.packageName, "package.json"),
84784
85006
  version: dependency.version,
84785
85007
  message: `${dependency.label} support is not installed at the required version.`
84786
85008
  });
@@ -84789,9 +85011,9 @@ function grammarChecksForLanguages(runtime4, ids) {
84789
85011
  const language = getCallFlowLanguage(id);
84790
85012
  if (language.kind !== "pack" || !language.packageName || !language.packageVersion)
84791
85013
  continue;
84792
- const packageRoot = runtime4.managed ? getLanguagePackageRoot(runtime4.runtimeDir, language) : join19(runtime4.runtimeDir, "node_modules", ...language.packageName.split("/"));
85014
+ const packageRoot = runtime4.managed ? getLanguagePackageRoot(runtime4.runtimeDir, language) : join20(runtime4.runtimeDir, "node_modules", ...language.packageName.split("/"));
84793
85015
  checks2.set(language.packageName, {
84794
- packageJson: join19(packageRoot, "package.json"),
85016
+ packageJson: join20(packageRoot, "package.json"),
84795
85017
  version: language.packageVersion,
84796
85018
  message: `${language.label} support is not installed.`
84797
85019
  });
@@ -84799,15 +85021,15 @@ function grammarChecksForLanguages(runtime4, ids) {
84799
85021
  return [...checks2.values()];
84800
85022
  }
84801
85023
  async function createNpmBlocker() {
84802
- const root3 = await mkdtemp2(join19(tmpdir4(), "plannotator-call-flow-path-"));
85024
+ const root3 = await mkdtemp2(join20(tmpdir4(), "plannotator-call-flow-path-"));
84803
85025
  if (process.platform === "win32") {
84804
- writeFileSync7(join19(root3, "npm.cmd"), `@echo CallDiff package installation is disabled during analysis. 1>&2\r
85026
+ writeFileSync8(join20(root3, "npm.cmd"), `@echo CallDiff package installation is disabled during analysis. 1>&2\r
84805
85027
  @exit /b 91\r
84806
85028
  `, "utf8");
84807
- writeFileSync7(join19(root3, "npm.exe"), "", "utf8");
85029
+ writeFileSync8(join20(root3, "npm.exe"), "", "utf8");
84808
85030
  } else {
84809
- const blocker = join19(root3, "npm");
84810
- writeFileSync7(blocker, `#!/bin/sh
85031
+ const blocker = join20(root3, "npm");
85032
+ writeFileSync8(blocker, `#!/bin/sh
84811
85033
  echo 'CallDiff package installation is disabled during analysis.' >&2
84812
85034
  exit 91
84813
85035
  `, "utf8");
@@ -86892,10 +87114,10 @@ var init_guide2 = __esm(() => {
86892
87114
  });
86893
87115
 
86894
87116
  // ../../packages/shared/guide-instructions-store.ts
86895
- import { mkdirSync as mkdirSync11, readFileSync as readFileSync12, unlinkSync as unlinkSync5, writeFileSync as writeFileSync8 } from "fs";
86896
- import { dirname as dirname8, join as join20 } from "path";
87117
+ import { mkdirSync as mkdirSync12, readFileSync as readFileSync12, unlinkSync as unlinkSync5, writeFileSync as writeFileSync9 } from "fs";
87118
+ import { dirname as dirname8, join as join21 } from "path";
86897
87119
  function instructionsPath() {
86898
- return join20(getPlannotatorDataDir(), "guide-instructions.md");
87120
+ return join21(getPlannotatorDataDir(), "guide-instructions.md");
86899
87121
  }
86900
87122
  function bound(value) {
86901
87123
  const trimmed = value.trim();
@@ -86917,8 +87139,8 @@ function writeGuideInstructions(value) {
86917
87139
  } catch {}
86918
87140
  return "";
86919
87141
  }
86920
- mkdirSync11(dirname8(path2), { recursive: true });
86921
- writeFileSync8(path2, bounded + `
87142
+ mkdirSync12(dirname8(path2), { recursive: true });
87143
+ writeFileSync9(path2, bounded + `
86922
87144
  `, "utf8");
86923
87145
  return bounded;
86924
87146
  }
@@ -87469,7 +87691,7 @@ var init_agent_jobs2 = __esm(() => {
87469
87691
  });
87470
87692
 
87471
87693
  // ../../packages/server/codex-review.ts
87472
- import { join as join21 } from "path";
87694
+ import { join as join22 } from "path";
87473
87695
  import { tmpdir as tmpdir5 } from "os";
87474
87696
  import { appendFile, mkdir, unlink, writeFile, readFile as readFile2 } from "fs/promises";
87475
87697
  import { existsSync as existsSync16 } from "fs";
@@ -87525,7 +87747,7 @@ async function buildCodexCommand(options) {
87525
87747
  return command;
87526
87748
  }
87527
87749
  function generateOutputPath() {
87528
- return join21(tmpdir5(), `plannotator-codex-${crypto.randomUUID()}.json`);
87750
+ return join22(tmpdir5(), `plannotator-codex-${crypto.randomUUID()}.json`);
87529
87751
  }
87530
87752
  async function parseCodexOutput(outputPath) {
87531
87753
  await debugLog("PARSE_OUTPUT_START", { outputPath });
@@ -87659,7 +87881,7 @@ var init_codex_review = __esm(() => {
87659
87881
  init_external_annotation2();
87660
87882
  DATA_DIR3 = getPlannotatorDataDir();
87661
87883
  DEBUG_ENABLED = !!process.env.PLANNOTATOR_DEBUG;
87662
- DEBUG_LOG_PATH = join21(DATA_DIR3, "codex-review-debug.log");
87884
+ DEBUG_LOG_PATH = join22(DATA_DIR3, "codex-review-debug.log");
87663
87885
  CODEX_REVIEW_SCHEMA = JSON.stringify({
87664
87886
  type: "object",
87665
87887
  properties: {
@@ -87702,7 +87924,7 @@ var init_codex_review = __esm(() => {
87702
87924
  additionalProperties: false
87703
87925
  });
87704
87926
  SCHEMA_DIR = DATA_DIR3;
87705
- SCHEMA_FILE = join21(SCHEMA_DIR, "codex-review-schema.json");
87927
+ SCHEMA_FILE = join22(SCHEMA_DIR, "codex-review-schema.json");
87706
87928
  });
87707
87929
 
87708
87930
  // ../../packages/server/agent-review-message.ts
@@ -87903,7 +88125,7 @@ var init_agent_review_message = __esm(() => {
87903
88125
  });
87904
88126
 
87905
88127
  // ../../packages/server/tour/tour-review.ts
87906
- import { join as join22 } from "path";
88128
+ import { join as join23 } from "path";
87907
88129
  import { tmpdir as tmpdir6 } from "os";
87908
88130
  import { mkdir as mkdir2, writeFile as writeFile2, readFile as readFile3, unlink as unlink2 } from "fs/promises";
87909
88131
  function buildTourUserMessage(patch, diffType, options, prMetadata) {
@@ -88055,7 +88277,7 @@ async function ensureTourSchemaFile() {
88055
88277
  return TOUR_SCHEMA_FILE;
88056
88278
  }
88057
88279
  function generateTourOutputPath() {
88058
- return join22(tmpdir6(), `plannotator-tour-${crypto.randomUUID()}.json`);
88280
+ return join23(tmpdir6(), `plannotator-tour-${crypto.randomUUID()}.json`);
88059
88281
  }
88060
88282
  async function buildTourCodexCommand(options) {
88061
88283
  const { cwd, outputPath, prompt, model, reasoningEffort, fastMode } = options;
@@ -88458,11 +88680,11 @@ var init_tour_review = __esm(() => {
88458
88680
  additionalProperties: false
88459
88681
  });
88460
88682
  TOUR_SCHEMA_DIR = getPlannotatorDataDir();
88461
- TOUR_SCHEMA_FILE = join22(TOUR_SCHEMA_DIR, "tour-schema.json");
88683
+ TOUR_SCHEMA_FILE = join23(TOUR_SCHEMA_DIR, "tour-schema.json");
88462
88684
  });
88463
88685
 
88464
88686
  // ../../packages/server/guide/guide-review.ts
88465
- import { join as join23 } from "path";
88687
+ import { join as join24 } from "path";
88466
88688
  import { tmpdir as tmpdir7 } from "os";
88467
88689
  import { mkdir as mkdir3, writeFile as writeFile3, readFile as readFile4, unlink as unlink3 } from "fs/promises";
88468
88690
  function composeGuideMethodology(extraInstructions) {
@@ -88656,7 +88878,7 @@ async function ensureGuideSchemaFile() {
88656
88878
  return GUIDE_SCHEMA_FILE;
88657
88879
  }
88658
88880
  function generateGuideOutputPath() {
88659
- return join23(tmpdir7(), `plannotator-guide-${crypto.randomUUID()}.json`);
88881
+ return join24(tmpdir7(), `plannotator-guide-${crypto.randomUUID()}.json`);
88660
88882
  }
88661
88883
  async function buildGuideCodexCommand(options) {
88662
88884
  const { cwd, outputPath, prompt, model, reasoningEffort, fastMode } = options;
@@ -89480,7 +89702,7 @@ var init_guide_review = __esm(() => {
89480
89702
  additionalProperties: false
89481
89703
  });
89482
89704
  GUIDE_SCHEMA_DIR = getPlannotatorDataDir();
89483
- GUIDE_SCHEMA_FILE = join23(GUIDE_SCHEMA_DIR, "guide-schema.json");
89705
+ GUIDE_SCHEMA_FILE = join24(GUIDE_SCHEMA_DIR, "guide-schema.json");
89484
89706
  });
89485
89707
 
89486
89708
  // ../../packages/core/compress.ts
@@ -90111,8 +90333,8 @@ function parsePRUrl(url) {
90111
90333
  }
90112
90334
 
90113
90335
  // ../../packages/shared/guide-store.ts
90114
- import { join as join24, resolve as resolve12, basename as basename8 } from "path";
90115
- import { mkdirSync as mkdirSync12, writeFileSync as writeFileSync9, readFileSync as readFileSync13, readdirSync as readdirSync6, renameSync as renameSync6, unlinkSync as unlinkSync6, existsSync as existsSync17 } from "fs";
90336
+ import { join as join25, resolve as resolve12, basename as basename8 } from "path";
90337
+ import { mkdirSync as mkdirSync13, writeFileSync as writeFileSync10, readFileSync as readFileSync13, readdirSync as readdirSync6, renameSync as renameSync6, unlinkSync as unlinkSync6, existsSync as existsSync17 } from "fs";
90116
90338
  import { createHash as createHash5 } from "crypto";
90117
90339
  function sanitizeKeySegment(segment) {
90118
90340
  const cleaned = segment.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^[.\-]+|[.\-]+$/g, "");
@@ -90151,13 +90373,13 @@ function makeGuideId(title, now = Date.now()) {
90151
90373
  return `${now}-${slug || "guide"}`;
90152
90374
  }
90153
90375
  function guidesDir(repoKey) {
90154
- return join24(getPlannotatorDataDir(), "guides", repoKey);
90376
+ return join25(getPlannotatorDataDir(), "guides", repoKey);
90155
90377
  }
90156
90378
  function guidePath(repoKey, id) {
90157
- return join24(guidesDir(repoKey), `${id}.json`);
90379
+ return join25(guidesDir(repoKey), `${id}.json`);
90158
90380
  }
90159
90381
  function guidePatchPath(repoKey, id) {
90160
- return join24(guidesDir(repoKey), `${id}.patch`);
90382
+ return join25(guidesDir(repoKey), `${id}.patch`);
90161
90383
  }
90162
90384
  function coerceReviewed(value, sectionCount) {
90163
90385
  const out = new Array(sectionCount).fill(false);
@@ -90255,10 +90477,10 @@ function saveGuide(repoKey, id, envelope) {
90255
90477
  if (!isValidGuideId(id))
90256
90478
  return false;
90257
90479
  try {
90258
- mkdirSync12(guidesDir(repoKey), { recursive: true });
90480
+ mkdirSync13(guidesDir(repoKey), { recursive: true });
90259
90481
  const finalPath = guidePath(repoKey, id);
90260
90482
  const tmpPath = `${finalPath}.tmp`;
90261
- writeFileSync9(tmpPath, JSON.stringify(envelope, null, 2), "utf-8");
90483
+ writeFileSync10(tmpPath, JSON.stringify(envelope, null, 2), "utf-8");
90262
90484
  renameSync6(tmpPath, finalPath);
90263
90485
  return true;
90264
90486
  } catch (e10) {
@@ -90270,10 +90492,10 @@ function saveGuidePatch(repoKey, id, rawPatch) {
90270
90492
  if (!isValidGuideId(id))
90271
90493
  return false;
90272
90494
  try {
90273
- mkdirSync12(guidesDir(repoKey), { recursive: true });
90495
+ mkdirSync13(guidesDir(repoKey), { recursive: true });
90274
90496
  const finalPath = guidePatchPath(repoKey, id);
90275
90497
  const tmpPath = `${finalPath}.tmp`;
90276
- writeFileSync9(tmpPath, rawPatch, "utf-8");
90498
+ writeFileSync10(tmpPath, rawPatch, "utf-8");
90277
90499
  renameSync6(tmpPath, finalPath);
90278
90500
  return true;
90279
90501
  } catch (e10) {
@@ -90285,7 +90507,7 @@ function loadGuidePatch(repoKey, envelope) {
90285
90507
  if (!envelope.review)
90286
90508
  return null;
90287
90509
  try {
90288
- const filePath = join24(guidesDir(repoKey), envelope.review.patchFile);
90510
+ const filePath = join25(guidesDir(repoKey), envelope.review.patchFile);
90289
90511
  if (!existsSync17(filePath))
90290
90512
  return null;
90291
90513
  return readFileSync13(filePath, "utf-8");
@@ -91310,8 +91532,8 @@ var init_pr_github = __esm(() => {
91310
91532
  });
91311
91533
 
91312
91534
  // ../../packages/shared/pr-gitlab.ts
91313
- import { join as join25 } from "path";
91314
- import { mkdirSync as mkdirSync13, writeFileSync as writeFileSync10 } from "fs";
91535
+ import { join as join26 } from "path";
91536
+ import { mkdirSync as mkdirSync14, writeFileSync as writeFileSync11 } from "fs";
91315
91537
  function encodeProject(projectPath) {
91316
91538
  return encodeURIComponent(projectPath);
91317
91539
  }
@@ -91786,11 +92008,11 @@ async function submitGlMRReview(runtime4, ref, headSha, action, body3, fileComme
91786
92008
  const failed = failedFileComments.map((failure) => failure.comment);
91787
92009
  let savedTo = null;
91788
92010
  try {
91789
- const dir = join25(getPlannotatorDataDir(), "failed-comments");
91790
- mkdirSync13(dir, { recursive: true });
92011
+ const dir = join26(getPlannotatorDataDir(), "failed-comments");
92012
+ mkdirSync14(dir, { recursive: true });
91791
92013
  const slug = `${ref.host}-${ref.projectPath.replace(/\//g, "_")}-mr${ref.iid}-${Date.now()}`;
91792
- savedTo = join25(dir, `${slug}.json`);
91793
- writeFileSync10(savedTo, JSON.stringify({ ref, headSha, baseSha, startSha, errors, failedComments: failed }, null, 2));
92014
+ savedTo = join26(dir, `${slug}.json`);
92015
+ writeFileSync11(savedTo, JSON.stringify({ ref, headSha, baseSha, startSha, errors, failedComments: failed }, null, 2));
91794
92016
  } catch (writeErr) {
91795
92017
  console.error(`[plannotator] Failed to persist unposted comments: ${writeErr instanceof Error ? writeErr.message : String(writeErr)}`);
91796
92018
  }
@@ -101625,6 +101847,49 @@ async function startReviewServer(options) {
101625
101847
  };
101626
101848
  let currentContextRevision = getGitButlerContextRevision(clientGitContext) ?? "";
101627
101849
  const currentSnapshotId = () => `${draftKey}:${currentDiffType}${isPRMode ? `:${currentPRDiffScope}` : ""}${currentContextRevision ? `:${currentContextRevision}` : ""}`;
101850
+ const feedbackProject = () => options.project?.trim() ? options.project : deriveFeedbackProject(gitContext?.cwd ?? options.agentCwd ?? process.cwd());
101851
+ const feedbackReviewTarget = () => {
101852
+ const target = {
101853
+ diffType: String(currentDiffType),
101854
+ base: currentBase,
101855
+ gitRef: currentGitRef,
101856
+ snapshotId: currentSnapshotId(),
101857
+ changedFiles: countChangedFiles(currentPatch),
101858
+ patchBytes: currentPatch.length
101859
+ };
101860
+ if (sessionVcsType)
101861
+ target.vcsType = sessionVcsType;
101862
+ else if (workspace)
101863
+ target.vcsType = "workspace";
101864
+ const cwd = gitContext?.cwd ?? options.agentCwd;
101865
+ if (cwd)
101866
+ target.cwd = cwd;
101867
+ if (prMetadata) {
101868
+ target.pr = {
101869
+ provider: prMetadata.platform,
101870
+ repo: prMetadata.platform === "github" ? `${prMetadata.owner}/${prMetadata.repo}` : prMetadata.projectPath,
101871
+ number: prMetadata.platform === "github" ? prMetadata.number : prMetadata.iid
101872
+ };
101873
+ }
101874
+ return target;
101875
+ };
101876
+ const archiveReviewSubmission = (feedback, annotations, decision) => {
101877
+ if (!resolveFeedbackHistory(loadConfig()))
101878
+ return true;
101879
+ const feedbackText = typeof feedback === "string" ? feedback : "";
101880
+ const annotationList = Array.isArray(annotations) ? annotations : [];
101881
+ const hasContent = feedbackText.trim().length > 0 || annotationList.length > 0;
101882
+ const written = appendFeedbackRecord({
101883
+ project: feedbackProject(),
101884
+ origin,
101885
+ surface: "review",
101886
+ decision,
101887
+ target: { review: feedbackReviewTarget() },
101888
+ feedback: feedbackText,
101889
+ annotations: annotationList
101890
+ });
101891
+ return written !== null || !hasContent;
101892
+ };
101628
101893
  const buildCurrentAiReviewContext = (patch = currentPatch, base = currentBase, diffType = currentDiffType) => {
101629
101894
  if (!aiEnabled)
101630
101895
  return;
@@ -103285,6 +103550,7 @@ async function startReviewServer(options) {
103285
103550
  if (agentResponse)
103286
103551
  return agentResponse;
103287
103552
  if (url.pathname === "/api/exit" && req.method === "POST") {
103553
+ archiveReviewSubmission("", [], "dismissed");
103288
103554
  deleteDraft(draftKey, readDraftGenerationFromUrl(req));
103289
103555
  resolveDecision({ approved: false, feedback: "", annotations: [], exit: true });
103290
103556
  return Response.json({ ok: true });
@@ -103292,11 +103558,17 @@ async function startReviewServer(options) {
103292
103558
  if (url.pathname === "/api/feedback" && req.method === "POST") {
103293
103559
  try {
103294
103560
  const body3 = await req.json();
103295
- deleteDraft(draftKey, readDraftGenerationFromBody(body3));
103561
+ const approved = body3.approved ?? false;
103562
+ const feedbackValue = body3.feedback || "";
103563
+ const annotationsValue = body3.annotations || [];
103564
+ const hasContent = typeof feedbackValue === "string" && feedbackValue.trim().length > 0 || Array.isArray(annotationsValue) && annotationsValue.length > 0;
103565
+ const durable = archiveReviewSubmission(feedbackValue, annotationsValue, approved ? hasContent ? "approved-with-notes" : "lgtm" : "feedback");
103566
+ if (durable)
103567
+ deleteDraft(draftKey, readDraftGenerationFromBody(body3));
103296
103568
  resolveDecision({
103297
- approved: body3.approved ?? false,
103298
- feedback: body3.feedback || "",
103299
- annotations: body3.annotations || [],
103569
+ approved,
103570
+ feedback: feedbackValue,
103571
+ annotations: annotationsValue,
103300
103572
  agentSwitch: body3.agentSwitch
103301
103573
  });
103302
103574
  return Response.json({ ok: true });
@@ -103463,6 +103735,7 @@ var init_review = __esm(() => {
103463
103735
  init_guide_viewer_manifest2();
103464
103736
  init_marker_review();
103465
103737
  init_config2();
103738
+ init_feedback_archive();
103466
103739
  init_favicon2();
103467
103740
  init_pr();
103468
103741
  init_pr_artifact_document();
@@ -104280,11 +104553,11 @@ function shutdown() {
104280
104553
  var init_agent_terminal_node_sidecar = () => {};
104281
104554
 
104282
104555
  // ../../packages/server/agent-terminal-runtime.ts
104283
- import { existsSync as existsSync20, mkdirSync as mkdirSync14, readFileSync as readFileSync15, writeFileSync as writeFileSync11 } from "fs";
104284
- import { join as join26 } from "path";
104556
+ import { existsSync as existsSync20, mkdirSync as mkdirSync15, readFileSync as readFileSync15, writeFileSync as writeFileSync12 } from "fs";
104557
+ import { join as join27 } from "path";
104285
104558
  import { fileURLToPath } from "url";
104286
104559
  function getAgentTerminalManagedRuntimeDir(dataDir = getPlannotatorDataDir()) {
104287
- return join26(dataDir, "vendor", "agent-terminal", `webtui-${AGENT_TERMINAL_WEBTUI_VERSION}`);
104560
+ return join27(dataDir, "vendor", "agent-terminal", `webtui-${AGENT_TERMINAL_WEBTUI_VERSION}`);
104288
104561
  }
104289
104562
  function isAgentTerminalRemoteEnabled(env = process.env) {
104290
104563
  return isTruthy(env.PLANNOTATOR_AGENT_TERMINAL_REMOTE);
@@ -104364,9 +104637,9 @@ async function resolveManagedAgentTerminalRuntime(nodePath) {
104364
104637
  };
104365
104638
  }
104366
104639
  function materializeAgentTerminalSidecar(runtimeDir) {
104367
- mkdirSync14(runtimeDir, { recursive: true });
104368
- const sidecarPath = join26(runtimeDir, "agent-terminal-node-sidecar.mjs");
104369
- writeFileSync11(sidecarPath, agent_terminal_node_sidecar_default, "utf8");
104640
+ mkdirSync15(runtimeDir, { recursive: true });
104641
+ const sidecarPath = join27(runtimeDir, "agent-terminal-node-sidecar.mjs");
104642
+ writeFileSync12(sidecarPath, agent_terminal_node_sidecar_default, "utf8");
104370
104643
  return sidecarPath;
104371
104644
  }
104372
104645
  function tryMaterializeAgentTerminalSidecar(runtimeDir) {
@@ -104381,7 +104654,7 @@ function tryMaterializeAgentTerminalSidecar(runtimeDir) {
104381
104654
  }
104382
104655
  }
104383
104656
  function readInstalledWebTuiVersion(runtimeDir) {
104384
- const packageJsonPath = join26(runtimeDir, "node_modules", "@plannotator", "webtui", "package.json");
104657
+ const packageJsonPath = join27(runtimeDir, "node_modules", "@plannotator", "webtui", "package.json");
104385
104658
  if (!existsSync20(packageJsonPath))
104386
104659
  return null;
104387
104660
  try {
@@ -106088,23 +106361,40 @@ async function startAnnotateServer(options) {
106088
106361
  }
106089
106362
  const draftSource = mode === "annotate-app" && liveApp ? `annotate-app\x00${liveAppDraftIdentity(liveApp.targetUrl)}` : mode === "annotate-folder" && folderPath ? `folder:${resolvePath4(folderPath)}` : renderHtml && rawHtml ? rawHtml : markdown;
106090
106363
  const draftKey = contentHash(draftSource);
106091
- const persistSubmittedDecision = (feedback, annotations, approved) => {
106092
- const feedbackText = typeof feedback === "string" ? feedback : "";
106093
- const annotationList = Array.isArray(annotations) ? annotations : [];
106094
- if (!feedbackText.trim() && annotationList.length === 0)
106364
+ const annotateFeedbackSurface = mode === "annotate-app" ? "annotate-app" : mode === "annotate-last" ? "annotate-last" : mode === "annotate-folder" ? "annotate-folder" : singleFileLocalAnnotate ? "annotate" : "annotate-url";
106365
+ const archiveAnnotateDecision = (feedbackText, annotationList, decision2) => {
106366
+ if (!resolveFeedbackHistory(loadConfig()))
106095
106367
  return true;
106096
106368
  if (!annotateHistoryEnabled)
106097
106369
  return true;
106098
- if (!singleFileLocalAnnotate)
106099
- return true;
106100
- return persistAnnotateSubmission({
106370
+ const isUrlTarget = /^https?:\/\//i.test(filePath);
106371
+ return appendFeedbackRecord({
106101
106372
  project: annotateProjectName,
106102
- sessionPath: resolvePath4(filePath),
106373
+ origin,
106374
+ surface: annotateFeedbackSurface,
106375
+ decision: decision2,
106376
+ target: mode === "annotate-app" && liveApp ? { url: liveApp.targetUrl } : isUrlTarget ? { url: filePath } : mode === "annotate-last" ? { filePath } : { filePath: resolvePath4(filePath) },
106103
106377
  feedback: feedbackText,
106104
- annotations: annotationList,
106105
- approved
106378
+ annotations: annotationList
106106
106379
  }) !== null;
106107
106380
  };
106381
+ const persistSubmittedDecision = (feedback, annotations, approved) => {
106382
+ const feedbackText = typeof feedback === "string" ? feedback : "";
106383
+ const annotationList = Array.isArray(annotations) ? annotations : [];
106384
+ const hasContent = feedbackText.trim().length > 0 || annotationList.length > 0;
106385
+ const archived = archiveAnnotateDecision(feedbackText, annotationList, approved ? hasContent ? "approved-with-notes" : "approved" : "feedback");
106386
+ let legacyDurable = true;
106387
+ if (hasContent && annotateHistoryEnabled && singleFileLocalAnnotate) {
106388
+ legacyDurable = persistAnnotateSubmission({
106389
+ project: annotateProjectName,
106390
+ sessionPath: resolvePath4(filePath),
106391
+ feedback: feedbackText,
106392
+ annotations: annotationList,
106393
+ approved
106394
+ }) !== null;
106395
+ }
106396
+ return legacyDurable && (archived || !hasContent);
106397
+ };
106108
106398
  const externalAnnotations = createExternalAnnotationHandler("plan");
106109
106399
  const aiRuntime = resolveAIEnabled() ? await createAIRuntime() : null;
106110
106400
  const htmlAssets = createHtmlAssetRegistry();
@@ -106587,6 +106877,7 @@ async function startAnnotateServer(options) {
106587
106877
  if (!decision.settle({ feedback: "", annotations: [], exit: true })) {
106588
106878
  return alreadyDecided();
106589
106879
  }
106880
+ archiveAnnotateDecision("", [], "dismissed");
106590
106881
  deleteDraft(draftKey, readDraftGenerationFromUrl(req));
106591
106882
  clientLease.cancel();
106592
106883
  return Response.json({ ok: true });
@@ -106726,6 +107017,7 @@ var init_annotate = __esm(() => {
106726
107017
  init_source_save_node();
106727
107018
  init_external_annotations();
106728
107019
  init_config2();
107020
+ init_feedback_archive();
106729
107021
  init_favicon2();
106730
107022
  init_html_assets_node();
106731
107023
  init_browser();
@@ -107911,6 +108203,7 @@ async function handleReviewCommand(event, deps) {
107911
108203
  gitRef,
107912
108204
  error: diffError,
107913
108205
  origin: "opencode",
108206
+ project: await detectProjectName() ?? undefined,
107914
108207
  diffType: isPRMode ? undefined : userDiffType,
107915
108208
  gitContext,
107916
108209
  initialFingerprint,