@difflab/pi 0.7.2 → 0.8.0-rc.202610020810.d27e7fc

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.
Files changed (81) hide show
  1. package/README.md +11 -11
  2. package/agents/diffpi-orchestrator.md +1 -1
  3. package/agents/diffpi-planner.md +2 -2
  4. package/agents/diffpi-reviewer.md +2 -2
  5. package/agents/diffpi-worker.md +6 -6
  6. package/dist/commands/dispatch.d.ts +1 -1
  7. package/dist/commands/dispatch.d.ts.map +1 -1
  8. package/dist/commands/plan.d.ts +1 -1
  9. package/dist/commands/plan.d.ts.map +1 -1
  10. package/dist/commands/review.d.ts +1 -1
  11. package/dist/commands/review.d.ts.map +1 -1
  12. package/dist/doctor.d.ts +9 -0
  13. package/dist/doctor.d.ts.map +1 -0
  14. package/dist/environment.d.ts +0 -1
  15. package/dist/environment.d.ts.map +1 -1
  16. package/dist/extensions/index.js +1830 -2899
  17. package/dist/index.d.ts +5 -9
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +140 -759
  20. package/dist/log.d.ts +6 -11
  21. package/dist/log.d.ts.map +1 -1
  22. package/dist/plan/verify.d.ts.map +1 -1
  23. package/dist/review/index.d.ts +2 -3
  24. package/dist/review/index.d.ts.map +1 -1
  25. package/dist/review/review-backend.d.ts +2 -3
  26. package/dist/review/review-backend.d.ts.map +1 -1
  27. package/dist/setup.d.ts +0 -2
  28. package/dist/setup.d.ts.map +1 -1
  29. package/dist/tools/ci.d.ts +1 -1
  30. package/dist/tools/doctor.d.ts +3 -0
  31. package/dist/tools/doctor.d.ts.map +1 -0
  32. package/dist/tools/index.d.ts +1 -0
  33. package/dist/tools/index.d.ts.map +1 -1
  34. package/dist/tools/index.js +1824 -2892
  35. package/dist/tools/log.d.ts.map +1 -1
  36. package/dist/tools/review.d.ts +1 -7
  37. package/dist/tools/review.d.ts.map +1 -1
  38. package/dist/tools/setup.d.ts.map +1 -1
  39. package/package.json +1 -5
  40. package/skills/diffpi-doctor/SKILL.md +10 -0
  41. package/skills/diffpi-setup/SKILL.md +3 -3
  42. package/skills/plan/SKILL.md +4 -5
  43. package/skills/plan/references/design-quality.md +1 -1
  44. package/skills/plan/references/workflows/finalize.md +2 -2
  45. package/skills/plan/references/workflows/go.md +7 -5
  46. package/skills/plan/references/workflows/help.md +1 -2
  47. package/skills/plan/references/workflows/init.md +2 -2
  48. package/skills/plan/references/workflows/inline-fallback.md +3 -3
  49. package/skills/plan/references/workflows/new.md +3 -3
  50. package/skills/plan/references/workflows/update.md +6 -4
  51. package/skills/plan/references/workflows/validate.md +5 -5
  52. package/skills/review/SKILL.md +6 -6
  53. package/skills/review/references/review-standards.md +9 -1
  54. package/skills/review/references/workflows/address.md +9 -15
  55. package/skills/review/references/workflows/auto.md +9 -17
  56. package/skills/review/references/workflows/complete.md +2 -2
  57. package/skills/review/references/workflows/edit.md +7 -5
  58. package/skills/review/references/workflows/help.md +12 -20
  59. package/skills/review/references/workflows/new.md +14 -13
  60. package/skills/review/references/workflows/publish.md +2 -2
  61. package/skills/review/references/workflows/status.md +1 -1
  62. package/skills/review/templates/REVIEW.md +39 -0
  63. package/templates/plan/PLAN.md +9 -1
  64. package/templates/review/draft-pr.md +3 -9
  65. package/dist/cli/plan.d.ts +0 -17
  66. package/dist/cli/plan.d.ts.map +0 -1
  67. package/dist/cli.d.ts +0 -4
  68. package/dist/cli.d.ts.map +0 -1
  69. package/dist/cli.js +0 -2258
  70. package/dist/extensions/tuicrx.d.ts +0 -66
  71. package/dist/extensions/tuicrx.d.ts.map +0 -1
  72. package/dist/extensions/zedx.d.ts +0 -18
  73. package/dist/extensions/zedx.d.ts.map +0 -1
  74. package/dist/review/local-review-backend.d.ts +0 -3
  75. package/dist/review/local-review-backend.d.ts.map +0 -1
  76. package/dist/review/local-reviews.d.ts +0 -31
  77. package/dist/review/local-reviews.d.ts.map +0 -1
  78. package/dist/review/review-markdown.d.ts +0 -6
  79. package/dist/review/review-markdown.d.ts.map +0 -1
  80. package/skills/plan/references/workflows/annotate.md +0 -13
  81. package/skills/review/references/workflows/open.md +0 -11
package/dist/index.js CHANGED
@@ -95,30 +95,13 @@ function normalizeModelReference(value) {
95
95
  return value.toLowerCase().replace(/^~/, "").replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
96
96
  }
97
97
  // src/environment.ts
98
- import { readFile as readFile3 } from "node:fs/promises";
99
- import { basename, join as join5 } from "node:path";
100
-
101
- // src/assets.ts
102
- import { existsSync } from "node:fs";
103
- import { dirname, join as join2 } from "node:path";
104
- import { fileURLToPath } from "node:url";
105
- function resolveBundledAssetDir(name, moduleUrl = import.meta.url) {
106
- const moduleDir = dirname(fileURLToPath(moduleUrl));
107
- const candidates = [join2(moduleDir, name), join2(moduleDir, "..", name), join2(moduleDir, "..", "..", name)];
108
- return candidates.find((path) => existsSync(path)) ?? candidates[1];
109
- }
110
- function resolveBundledAgentsDir(moduleUrl = import.meta.url) {
111
- return resolveBundledAssetDir("agents", moduleUrl);
112
- }
113
- function resolveBundledTemplatesDir(moduleUrl = import.meta.url) {
114
- return resolveBundledAssetDir("templates", moduleUrl);
115
- }
98
+ import { basename } from "node:path";
116
99
 
117
100
  // src/extensions/processx.ts
118
101
  import { spawn } from "node:child_process";
119
102
  import { constants } from "node:fs";
120
103
  import { access } from "node:fs/promises";
121
- import { delimiter, join as join3 } from "node:path";
104
+ import { delimiter, join as join2 } from "node:path";
122
105
  async function findExecutable(name) {
123
106
  if (name.includes("/")) {
124
107
  try {
@@ -131,7 +114,7 @@ async function findExecutable(name) {
131
114
  for (const directory of (process.env.PATH ?? "").split(delimiter)) {
132
115
  if (!directory)
133
116
  continue;
134
- const candidate = join3(directory, name);
117
+ const candidate = join2(directory, name);
135
118
  try {
136
119
  await access(candidate, constants.X_OK);
137
120
  return candidate;
@@ -195,175 +178,6 @@ function appendBounded(current, next) {
195
178
  return combined.length <= MAX_CAPTURED_OUTPUT_LENGTH ? combined : combined.slice(-MAX_CAPTURED_OUTPUT_LENGTH);
196
179
  }
197
180
 
198
- // src/extensions/zedx.ts
199
- import { mkdir, readFile as readFile2, writeFile } from "node:fs/promises";
200
- import { homedir as homedir2 } from "node:os";
201
- import { dirname as dirname2, join as join4 } from "node:path";
202
- var ZED_LOCAL_REVIEW_TASK_NAME = "diffpi: tuicr local review";
203
- var ZED_REVIEW_TASK_NAME = ZED_LOCAL_REVIEW_TASK_NAME;
204
- var ZED_PR_REVIEW_TASK_NAME = "diffpi: tuicr PR review";
205
- var ZED_PLAN_ANNOTATE_TASK_NAME = "diffpi: annotate plan";
206
- var LEGACY_ZED_REVIEW_TASK_NAME = "diffpi: tuicr review";
207
- var REVIEW_KEYBINDING = "cmd-alt-r";
208
- function zedTasksPath(homeDir = homedir2()) {
209
- return join4(homeDir, ".config", "zed", "tasks.json");
210
- }
211
- function zedKeymapPath(homeDir = homedir2()) {
212
- return join4(homeDir, ".config", "zed", "keymap.json");
213
- }
214
- async function ensureZedReviewTask(homeDir = homedir2(), _command = ["tuicr", "-w", "-r", "main..HEAD"]) {
215
- const path = zedTasksPath(homeDir);
216
- const currentText = await readOptional(path);
217
- const tasks = parseJsonArray(currentText, path);
218
- const migrated = tasks.filter((task) => task.label !== LEGACY_ZED_REVIEW_TASK_NAME);
219
- const next = [...migrated];
220
- for (const task of [localReviewTask(), prReviewTask()]) {
221
- const index = next.findIndex((existing) => existing.label === task.label);
222
- if (index >= 0)
223
- next[index] = { ...next[index], ...task };
224
- else
225
- next.push(task);
226
- }
227
- const changed = JSON.stringify(tasks) !== JSON.stringify(next);
228
- if (changed)
229
- await writeJson(path, next);
230
- return { path, changed, existed: currentText !== undefined };
231
- }
232
- async function ensureZedPlanTask(packageVersion, homeDir = homedir2()) {
233
- if (!/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/.test(packageVersion)) {
234
- throw new Error(`Invalid @difflab/pi package version: ${packageVersion}.`);
235
- }
236
- const path = zedTasksPath(homeDir);
237
- const currentText = await readOptional(path);
238
- const tasks = parseJsonArray(currentText, path);
239
- const task = {
240
- label: ZED_PLAN_ANNOTATE_TASK_NAME,
241
- command: "npx",
242
- args: ["--yes", `@difflab/pi@${packageVersion}`, "plan", "annotate", "$ZED_FILE", "--cwd", "$ZED_WORKTREE_ROOT"],
243
- cwd: "$ZED_WORKTREE_ROOT",
244
- use_new_terminal: true,
245
- reveal: "always",
246
- reveal_target: "center"
247
- };
248
- const next = [...tasks];
249
- const index = next.findIndex((candidate) => candidate.label === task.label);
250
- if (index >= 0)
251
- next[index] = { ...next[index], ...task };
252
- else
253
- next.push(task);
254
- const changed = JSON.stringify(tasks) !== JSON.stringify(next);
255
- if (changed)
256
- await writeJson(path, next);
257
- return { path, changed, existed: currentText !== undefined };
258
- }
259
- async function ensureZedReviewKeybinding(homeDir = homedir2()) {
260
- const path = zedKeymapPath(homeDir);
261
- const currentText = await readOptional(path);
262
- const entries = parseJsonArray(currentText, path);
263
- const migrated = entries.map(migrateReviewKeymapEntry);
264
- const alreadyBound = migrated.some((entry) => Object.values(entry.bindings ?? {}).some((action) => Array.isArray(action) && action[0] === "task::Spawn" && bindsReviewTask(action[1])));
265
- const next = alreadyBound ? migrated : [
266
- ...migrated,
267
- {
268
- context: "Workspace",
269
- bindings: { [REVIEW_KEYBINDING]: ["task::Spawn", { task_name: ZED_LOCAL_REVIEW_TASK_NAME }] }
270
- }
271
- ];
272
- const changed = JSON.stringify(entries) !== JSON.stringify(next);
273
- if (changed)
274
- await writeJson(path, next);
275
- return { path, changed, existed: currentText !== undefined };
276
- }
277
- function zedReviewTaskName(command) {
278
- if (command.some((value, index) => value === "plan" && command[index + 1] === "annotate")) {
279
- return ZED_PLAN_ANNOTATE_TASK_NAME;
280
- }
281
- return command[0] === "tuicr" && command[1] === "pr" ? ZED_PR_REVIEW_TASK_NAME : ZED_LOCAL_REVIEW_TASK_NAME;
282
- }
283
- var LOCAL_REVIEW_SCRIPT = `set -eu
284
- remote=$(git remote get-url origin 2>/dev/null || true)
285
- branch=$(git branch --show-current)
286
- base=
287
- case "$remote" in
288
- *github.com*)
289
- base=$(gh pr list --head "$branch" --state open --limit 100 --json baseRefName,createdAt --jq 'sort_by(.createdAt) | reverse | .[0].baseRefName // empty' 2>/dev/null || true)
290
- [ -n "$base" ] || base=$(gh repo view --json defaultBranchRef --jq .defaultBranchRef.name 2>/dev/null || true)
291
- ;;
292
- *gitlab*)
293
- mr=$(glab mr list --source-branch "$branch" --order created_at --sort desc --per-page 100 --output json 2>/dev/null || true)
294
- base=$(printf '%s' "$mr" | node -e 'let s=""; process.stdin.on("data",d=>s+=d).on("end",()=>{try{let a=JSON.parse(s);process.stdout.write(a[0]?.target_branch||"")}catch{}})')
295
- [ -n "$base" ] || base=$(glab repo view --output json 2>/dev/null | node -e 'let s=""; process.stdin.on("data",d=>s+=d).on("end",()=>{try{process.stdout.write(JSON.parse(s).default_branch||"")}catch{}})' || true)
296
- ;;
297
- esac
298
- [ -n "$base" ] || { echo 'diffpi: could not resolve a GitHub/GitLab review base' >&2; exit 1; }
299
- exec tuicr -w -r "$base..HEAD"`;
300
- var PR_REVIEW_SCRIPT = `set -eu
301
- remote=$(git remote get-url origin 2>/dev/null || true)
302
- branch=$(git branch --show-current)
303
- case "$remote" in
304
- *github.com*) number=$(gh pr list --head "$branch" --state open --limit 100 --json number,createdAt --jq 'sort_by(.createdAt) | reverse | .[0].number // empty' 2>/dev/null || true) ;;
305
- *gitlab*) number=$(glab mr list --source-branch "$branch" --order created_at --sort desc --per-page 100 --output json 2>/dev/null | node -e 'let s=""; process.stdin.on("data",d=>s+=d).on("end",()=>{try{let a=JSON.parse(s);process.stdout.write(String(a[0]?.iid||a[0]?.number||""))}catch{}})' || true) ;;
306
- *) number= ;;
307
- esac
308
- [ -n "$number" ] || { echo 'diffpi: could not resolve the current GitHub PR or GitLab MR' >&2; exit 1; }
309
- exec tuicr pr "$number"`;
310
- function staticReviewTask(label, script) {
311
- return {
312
- label,
313
- command: "sh",
314
- args: ["-lc", script],
315
- cwd: "$ZED_WORKTREE_ROOT",
316
- use_new_terminal: true,
317
- reveal: "always",
318
- reveal_target: "center"
319
- };
320
- }
321
- function localReviewTask() {
322
- return staticReviewTask(ZED_LOCAL_REVIEW_TASK_NAME, LOCAL_REVIEW_SCRIPT);
323
- }
324
- function prReviewTask() {
325
- return staticReviewTask(ZED_PR_REVIEW_TASK_NAME, PR_REVIEW_SCRIPT);
326
- }
327
- function bindsReviewTask(payload) {
328
- return typeof payload === "object" && payload !== null && payload.task_name === ZED_LOCAL_REVIEW_TASK_NAME;
329
- }
330
- function migrateReviewKeymapEntry(entry) {
331
- const bindings = Object.fromEntries(Object.entries(entry.bindings ?? {}).map(([key, action]) => {
332
- if (Array.isArray(action) && action[0] === "task::Spawn" && typeof action[1] === "object" && action[1] !== null && action[1].task_name === LEGACY_ZED_REVIEW_TASK_NAME) {
333
- return [key, ["task::Spawn", { ...action[1], task_name: ZED_LOCAL_REVIEW_TASK_NAME }]];
334
- }
335
- return [key, action];
336
- }));
337
- return entry.bindings ? { ...entry, bindings } : entry;
338
- }
339
- async function readOptional(path) {
340
- try {
341
- return await readFile2(path, "utf8");
342
- } catch (error) {
343
- if (error instanceof Error && "code" in error && error.code === "ENOENT")
344
- return;
345
- throw error;
346
- }
347
- }
348
- function parseJsonArray(content, path) {
349
- if (!content?.trim())
350
- return [];
351
- let value;
352
- try {
353
- value = JSON.parse(content);
354
- } catch {
355
- throw new Error(`Cannot safely edit ${path}: not strict JSON (it may contain JSONC comments).`);
356
- }
357
- if (!Array.isArray(value))
358
- throw new Error(`Expected a JSON array in ${path}.`);
359
- return value;
360
- }
361
- async function writeJson(path, value) {
362
- await mkdir(dirname2(path), { recursive: true });
363
- await writeFile(path, `${JSON.stringify(value, null, 2)}
364
- `, "utf8");
365
- }
366
-
367
181
  // src/environment.ts
368
182
  function detectIde(env = process.env) {
369
183
  const program = (env.TERM_PROGRAM ?? "").toLowerCase();
@@ -441,35 +255,6 @@ async function openFileAdjacent(path, opts) {
441
255
  }
442
256
  return { launched: false, via: "print", command: path, reason: "No supported IDE or multiplexer was available." };
443
257
  }
444
- async function openInNewTab(command, opts) {
445
- const env = opts.env ?? process.env;
446
- const name = opts.name ?? "review";
447
- const printable = command.join(" ");
448
- const mux = detectMux(env);
449
- if (mux !== "none") {
450
- const opened = await openMuxTab(mux, command, opts.cwd, name, printable);
451
- if (opened)
452
- return opened;
453
- }
454
- if (detectIde(env) === "zed") {
455
- try {
456
- const taskName = zedReviewTaskName(command);
457
- if (taskName === ZED_PLAN_ANNOTATE_TASK_NAME)
458
- await ensureZedPlanTask(await packageVersion(), opts.homeDir);
459
- else
460
- await ensureZedReviewTask(opts.homeDir, command);
461
- return {
462
- launched: false,
463
- configured: true,
464
- via: "zed-task",
465
- command: printable,
466
- taskName,
467
- instruction: `Run the Zed task "${taskName}".`
468
- };
469
- } catch {}
470
- }
471
- return { launched: false, via: "print", command: printable };
472
- }
473
258
  function screenWindowArgs(command, cwd, name) {
474
259
  return ["-X", "screen", "-t", name, "sh", "-lc", 'cd -- "$1" && shift && exec "$@"', "sh", cwd, ...command];
475
260
  }
@@ -487,17 +272,6 @@ async function resolveEditor(env) {
487
272
  return candidate;
488
273
  return;
489
274
  }
490
- async function packageVersion() {
491
- let value;
492
- try {
493
- value = JSON.parse(await readFile3(join5(resolveBundledAgentsDir(), "..", "package.json"), "utf8"));
494
- } catch (error) {
495
- throw new Error("Cannot parse the installed @difflab/pi package metadata.", { cause: error });
496
- }
497
- if (typeof value.version !== "string")
498
- throw new Error("Cannot resolve the installed @difflab/pi version.");
499
- return value.version;
500
- }
501
275
  async function openMuxTab(mux, command, cwd, name, printable, tmuxCommand) {
502
276
  if (mux === "zellij" && await findExecutable("zellij")) {
503
277
  const result = await run("zellij", ["action", "new-tab", "--cwd", cwd, "--name", name, "--", ...command]);
@@ -521,7 +295,7 @@ async function openMuxTab(mux, command, cwd, name, printable, tmuxCommand) {
521
295
  }
522
296
  // src/extensions/gitx.ts
523
297
  import { realpath } from "node:fs/promises";
524
- import { basename as basename2, isAbsolute, join as join6, resolve } from "node:path";
298
+ import { basename as basename2, isAbsolute, join as join3, resolve } from "node:path";
525
299
  async function gitToplevel(cwd) {
526
300
  const result = await run("git", ["-C", cwd, "rev-parse", "--show-toplevel"]);
527
301
  const top = result.stdout.trim();
@@ -533,7 +307,7 @@ async function inspectGitRepository(cwd) {
533
307
  const remote = remoteResult.code === 0 ? remoteResult.stdout.trim() : "";
534
308
  const commonResult = await run("git", ["-C", root, "rev-parse", "--git-common-dir"]);
535
309
  const common = commonResult.stdout.trim();
536
- const commonPath = commonResult.code === 0 && common ? resolve(isAbsolute(common) ? common : join6(root, common)) : root;
310
+ const commonPath = commonResult.code === 0 && common ? resolve(isAbsolute(common) ? common : join3(root, common)) : root;
537
311
  const commonDir = await canonicalPath(commonPath);
538
312
  return {
539
313
  root,
@@ -870,9 +644,9 @@ function createForgeBackend(vcs) {
870
644
  var createVcsBackend = createForgeBackend;
871
645
  var createForge = createForgeBackend;
872
646
  // src/extensions/misex.ts
873
- import { mkdir as mkdir2, readFile as readFile4, writeFile as writeFile2 } from "node:fs/promises";
874
- import { homedir as homedir3 } from "node:os";
875
- import { basename as basename3, dirname as dirname3, join as join7 } from "node:path";
647
+ import { mkdir, readFile as readFile2, writeFile } from "node:fs/promises";
648
+ import { homedir as homedir2 } from "node:os";
649
+ import { basename as basename3, dirname, join as join4 } from "node:path";
876
650
  var MISE_HOOK_START = "# >>> @difflab/pi mise >>>";
877
651
  var MISE_HOOK_END = "# <<< @difflab/pi mise <<<";
878
652
  var mise = {
@@ -883,11 +657,11 @@ var mise = {
883
657
  return run("mise", args, options);
884
658
  },
885
659
  async install(options = {}) {
886
- const homeDir = options.homeDir ?? homedir3();
660
+ const homeDir = options.homeDir ?? homedir2();
887
661
  const platform = options.platform ?? process.platform;
888
662
  if (platform === "win32")
889
663
  throw new Error("Automatic mise installation supports macOS and Linux only.");
890
- const installedPath = join7(homeDir, ".local", "bin", "mise");
664
+ const installedPath = join4(homeDir, ".local", "bin", "mise");
891
665
  if (options.dryRun)
892
666
  return installedPath;
893
667
  await runChecked("sh", ["-c", "curl -fsSL https://mise.run | sh"]);
@@ -897,7 +671,7 @@ var mise = {
897
671
  return executable;
898
672
  },
899
673
  async hookEnsure(executable, options = {}) {
900
- const homeDir = options.homeDir ?? homedir3();
674
+ const homeDir = options.homeDir ?? homedir2();
901
675
  const hook = getShellHook(basename3(options.shell ?? process.env.SHELL ?? ""), executable, homeDir);
902
676
  const current = await getOptionalFile(hook.path);
903
677
  if (current.includes(MISE_HOOK_START))
@@ -907,8 +681,8 @@ var mise = {
907
681
  const separator = current.length === 0 || current.endsWith(`
908
682
  `) ? "" : `
909
683
  `;
910
- await mkdir2(dirname3(hook.path), { recursive: true });
911
- await writeFile2(hook.path, `${current}${separator}${hook.content}`, "utf8");
684
+ await mkdir(dirname(hook.path), { recursive: true });
685
+ await writeFile(hook.path, `${current}${separator}${hook.content}`, "utf8");
912
686
  return { path: hook.path, changed: true, planned: false };
913
687
  },
914
688
  async toolCheckGlobal(executable, tool, minimumVersion) {
@@ -925,7 +699,7 @@ var mise = {
925
699
  async toolInstallLocal(executable, specification, cwd = process.cwd()) {
926
700
  await runChecked(executable, ["use", "--path", cwd, specification], { cwd });
927
701
  },
928
- async toolUpdateAllGlobal(executable, homeDir = homedir3()) {
702
+ async toolUpdateAllGlobal(executable, homeDir = homedir2()) {
929
703
  await runChecked(executable, ["upgrade"], { cwd: homeDir });
930
704
  }
931
705
  };
@@ -934,7 +708,7 @@ function getShellHook(shell, executable, homeDir) {
934
708
  switch (shell.toLowerCase()) {
935
709
  case "zsh":
936
710
  return {
937
- path: join7(homeDir, ".zshrc"),
711
+ path: join4(homeDir, ".zshrc"),
938
712
  content: `${MISE_HOOK_START}
939
713
  eval "$(${command} activate zsh)"
940
714
  ${MISE_HOOK_END}
@@ -942,7 +716,7 @@ ${MISE_HOOK_END}
942
716
  };
943
717
  case "fish":
944
718
  return {
945
- path: join7(homeDir, ".config", "fish", "config.fish"),
719
+ path: join4(homeDir, ".config", "fish", "config.fish"),
946
720
  content: `${MISE_HOOK_START}
947
721
  ${command} activate fish | source
948
722
  ${MISE_HOOK_END}
@@ -951,7 +725,7 @@ ${MISE_HOOK_END}
951
725
  case "nu":
952
726
  case "nushell":
953
727
  return {
954
- path: join7(homeDir, ".config", "nushell", "config.nu"),
728
+ path: join4(homeDir, ".config", "nushell", "config.nu"),
955
729
  content: `${MISE_HOOK_START}
956
730
  let mise_bin = ${command}
957
731
  let mise_path = $nu.default-config-dir | path join mise.nu
@@ -962,7 +736,7 @@ ${MISE_HOOK_END}
962
736
  };
963
737
  case "xonsh":
964
738
  return {
965
- path: join7(homeDir, ".xonshrc"),
739
+ path: join4(homeDir, ".xonshrc"),
966
740
  content: `${MISE_HOOK_START}
967
741
  execx($(${command} activate xonsh))
968
742
  ${MISE_HOOK_END}
@@ -970,7 +744,7 @@ ${MISE_HOOK_END}
970
744
  };
971
745
  case "elvish":
972
746
  return {
973
- path: join7(homeDir, ".config", "elvish", "rc.elv"),
747
+ path: join4(homeDir, ".config", "elvish", "rc.elv"),
974
748
  content: `${MISE_HOOK_START}
975
749
  var mise: = (ns [&])
976
750
  eval (${command} activate elvish | slurp) &ns=$mise: &on-end={|ns| set mise: = $ns }
@@ -981,7 +755,7 @@ ${MISE_HOOK_END}
981
755
  case "pwsh":
982
756
  case "powershell":
983
757
  return {
984
- path: join7(homeDir, ".config", "powershell", "Microsoft.PowerShell_profile.ps1"),
758
+ path: join4(homeDir, ".config", "powershell", "Microsoft.PowerShell_profile.ps1"),
985
759
  content: `${MISE_HOOK_START}
986
760
  (& ${command} activate pwsh) | Out-String | Invoke-Expression
987
761
  ${MISE_HOOK_END}
@@ -990,7 +764,7 @@ ${MISE_HOOK_END}
990
764
  case "bash":
991
765
  default:
992
766
  return {
993
- path: join7(homeDir, ".bashrc"),
767
+ path: join4(homeDir, ".bashrc"),
994
768
  content: `${MISE_HOOK_START}
995
769
  eval "$(${command} activate bash)"
996
770
  ${MISE_HOOK_END}
@@ -1029,7 +803,7 @@ function isVersionAtLeast(version, minimumVersion) {
1029
803
  }
1030
804
  async function getOptionalFile(path) {
1031
805
  try {
1032
- return await readFile4(path, "utf8");
806
+ return await readFile2(path, "utf8");
1033
807
  } catch (error) {
1034
808
  if (error instanceof Error && "code" in error && error.code === "ENOENT")
1035
809
  return "";
@@ -1121,9 +895,6 @@ function yymmdd(date = new Date) {
1121
895
  function reviewRecordName(target, date = new Date) {
1122
896
  return `${yymmdd(date)}-${reviewSlug(target) || "uncommitted"}`;
1123
897
  }
1124
- function isLocalResponse(body) {
1125
- return /^<!-- diffpi(?:-local-response:[A-Za-z0-9_-]+)? -->\n/m.test(body);
1126
- }
1127
898
  function dedupeFindings(findings) {
1128
899
  const rank = { BLOCKING: 3, CONSIDER: 2, NOTE: 1 };
1129
900
  const byKey = new Map;
@@ -1168,142 +939,6 @@ function renderCommentBody(finding) {
1168
939
  > **Reference:** ${finding.reference}` : "";
1169
940
  return `${prefix}${finding.body}${reference}`;
1170
941
  }
1171
- // src/review/review-markdown.ts
1172
- import { z as z4 } from "zod";
1173
- var reviewThreadRecordSchema = z4.object({
1174
- id: z4.string().min(1),
1175
- file: z4.string().nullable().optional().transform((value) => value ?? undefined),
1176
- line: z4.number().int().positive().nullable().optional().transform((value) => value ?? undefined),
1177
- body: z4.string(),
1178
- author: z4.string().optional(),
1179
- resolved: z4.boolean(),
1180
- addressed: z4.boolean().optional(),
1181
- question: z4.boolean(),
1182
- replies: z4.array(z4.string()).optional()
1183
- });
1184
- function renderReviewDoc(input) {
1185
- const anchored = dedupeFindings(input.findings).filter((finding) => finding.line > 0);
1186
- const lines = [`# Review: ${input.title}`, "", "## Metadata"];
1187
- if (input.number !== undefined)
1188
- lines.push(`- **PR/MR**: #${input.number}${input.url ? ` — ${input.url}` : ""}`);
1189
- if (input.author)
1190
- lines.push(`- **Author**: ${input.author}`);
1191
- if (input.model)
1192
- lines.push(`- **Review agent**: ${input.model}`);
1193
- if (input.headRef && input.baseRef)
1194
- lines.push(`- **Branch**: ${input.headRef} → ${input.baseRef}`);
1195
- if (input.additions !== undefined)
1196
- lines.push(`- **Stats**: +${input.additions} -${input.deletions ?? 0} across ${input.changedFiles ?? 0} files`);
1197
- lines.push(`- **Reviewed**: ${input.timestamp ?? new Date().toISOString()}`, "");
1198
- if (input.overallIssues.length > 0) {
1199
- lines.push("## Overall issues", "");
1200
- for (const issue of input.overallIssues)
1201
- lines.push(`- ${issue}`);
1202
- lines.push("");
1203
- }
1204
- lines.push("## Verification", "");
1205
- for (const gate of input.gates)
1206
- lines.push(`- ${gate.name}: ${gate.status} — ${gate.detail}`);
1207
- lines.push("");
1208
- if (input.notVerified.length > 0) {
1209
- lines.push("## What was NOT verified", "");
1210
- for (const item of input.notVerified)
1211
- lines.push(`- ${item}`);
1212
- lines.push("");
1213
- }
1214
- lines.push("## Inline Comments", "");
1215
- for (const finding of anchored) {
1216
- lines.push(`### ${finding.file}:${finding.line} — ${finding.severity}`, "", finding.body, "");
1217
- if (finding.reference)
1218
- lines.push(`> **Reference:** ${finding.reference}`, "");
1219
- lines.push("---", "");
1220
- }
1221
- return `${lines.join(`
1222
- `).trimEnd()}
1223
- `;
1224
- }
1225
- function renderThreadArtifact(title, target, threads, options = {}) {
1226
- const records = threads.map((thread) => ({
1227
- id: thread.id,
1228
- file: thread.file,
1229
- line: thread.line,
1230
- body: thread.body,
1231
- author: thread.author,
1232
- resolved: thread.resolved,
1233
- addressed: thread.addressed,
1234
- question: thread.question,
1235
- replies: thread.replies
1236
- }));
1237
- const payload = Buffer.from(JSON.stringify(records), "utf8").toString("base64url");
1238
- const lines = [
1239
- `<!-- diffpi-threads:${payload} -->`,
1240
- `# Review threads: ${title}`,
1241
- "",
1242
- "## Metadata",
1243
- "",
1244
- `- Target: ${target}`,
1245
- `- Pulled: ${options.timestamp ?? new Date().toISOString()}`
1246
- ];
1247
- if (options.number !== undefined)
1248
- lines.push(`- PR/MR: #${options.number}${options.url ? ` — ${options.url}` : ""}`);
1249
- lines.push("", "## Replies", "");
1250
- for (const thread of threads) {
1251
- const id = Buffer.from(thread.id, "utf8").toString("base64url");
1252
- lines.push(`### ${thread.file ?? "review"}:${thread.line ?? "n/a"} (${thread.id})`, "", `<!-- diffpi-reply-start:${id} -->`, thread.reply ?? "", `<!-- diffpi-reply-end:${id} -->`, "");
1253
- }
1254
- lines.push("## Source comments", "");
1255
- for (const thread of threads) {
1256
- lines.push(`### ${thread.file ?? "review"}:${thread.line ?? "n/a"} — ${thread.author ?? "unknown"}`, "", `Thread: ${thread.id}`, `Status: ${thread.resolved ? "resolved" : thread.addressed ? "addressed" : "open"}`, "", "**Source comment**", "", thread.body, "", "**Recorded response**", "", thread.reply?.trim() || "_No response recorded._", "", "---", "");
1257
- }
1258
- return `${lines.join(`
1259
- `).trimEnd()}
1260
- `;
1261
- }
1262
- function parseThreadArtifact(content) {
1263
- const payload = content.match(/^<!-- diffpi-threads:([A-Za-z0-9_-]+) -->$/m)?.[1];
1264
- if (!payload)
1265
- throw new Error("This file is not a Diffpi thread artifact.");
1266
- let threads;
1267
- try {
1268
- const decoded = JSON.parse(Buffer.from(payload, "base64url").toString("utf8"));
1269
- threads = z4.array(reviewThreadRecordSchema).parse(decoded);
1270
- } catch {
1271
- throw new Error("Cannot parse the Diffpi thread artifact payload.");
1272
- }
1273
- const replies = content.split(/^## Source comments$/m, 1)[0] ?? "";
1274
- return threads.map((thread) => {
1275
- const id = Buffer.from(thread.id, "utf8").toString("base64url");
1276
- const startMarker = `<!-- diffpi-reply-start:${id} -->
1277
- `;
1278
- const endMarker = `
1279
- <!-- diffpi-reply-end:${id} -->`;
1280
- const start = replies.indexOf(startMarker);
1281
- const end = start < 0 ? -1 : replies.indexOf(endMarker, start + startMarker.length);
1282
- const reply = start >= 0 && end >= 0 ? replies.slice(start + startMarker.length, end).trim() : "";
1283
- return reply ? { ...thread, reply } : thread;
1284
- });
1285
- }
1286
- function upsertThreadReply(content, threadId, body, question, resolved) {
1287
- const threads = parseThreadArtifact(content);
1288
- const thread = threads.find((candidate) => candidate.id === threadId);
1289
- if (!thread)
1290
- throw new Error(`Review thread ${threadId} was not found in the local artifact.`);
1291
- thread.reply = body;
1292
- thread.addressed = true;
1293
- if (question !== undefined)
1294
- thread.question = question;
1295
- if (resolved !== undefined)
1296
- thread.resolved = resolved;
1297
- const title = content.match(/^# Review threads: (.+)$/m)?.[1] ?? "review";
1298
- const target = content.match(/^- Target: (.+)$/m)?.[1] ?? "local";
1299
- const timestamp = content.match(/^- Pulled: (.+)$/m)?.[1];
1300
- const pr = content.match(/^- PR\/MR: #(\d+)(?: — (.+))?$/m);
1301
- return renderThreadArtifact(title, target, threads, {
1302
- timestamp,
1303
- number: pr ? Number.parseInt(pr[1], 10) : undefined,
1304
- url: pr?.[2]
1305
- });
1306
- }
1307
942
  // src/review/github-review-backend.ts
1308
943
  function GitHubReviewBackend(vcs, number) {
1309
944
  const pendingReview = async () => {
@@ -1683,210 +1318,6 @@ function hasGitlabDraftNotes(input) {
1683
1318
  }
1684
1319
  }
1685
1320
 
1686
- // src/review/local-review-backend.ts
1687
- import { readFile as readFile6, writeFile as writeFile3 } from "node:fs/promises";
1688
-
1689
- // src/extensions/tuicrx.ts
1690
- import { readFile as readFile5, realpath as realpath2 } from "node:fs/promises";
1691
- import { resolve as resolve2 } from "node:path";
1692
- async function tuicrAvailable() {
1693
- return Boolean(await findExecutable("tuicr"));
1694
- }
1695
- async function listSessions(repo = ".") {
1696
- await requireTuicr();
1697
- const result = await run("tuicr", ["review", "list", "--repo", repo]);
1698
- if (result.code !== 0 || !result.stdout.trim())
1699
- return [];
1700
- try {
1701
- const raw = JSON.parse(result.stdout);
1702
- return raw.map((entry) => ({
1703
- slug: entry.slug,
1704
- kind: entry.kind,
1705
- path: entry.path,
1706
- updatedAt: entry.updated_at,
1707
- commentCount: entry.comment_count,
1708
- anchor: entry.anchor,
1709
- active: entry.active
1710
- }));
1711
- } catch {
1712
- throw new Error("Cannot parse tuicr review list output.");
1713
- }
1714
- }
1715
- async function addComment(session, body, opts = {}) {
1716
- await requireTuicr();
1717
- const args = ["review", "add", "--session", session, body];
1718
- if (opts.targetFile)
1719
- args.push("--target-file", opts.targetFile);
1720
- if (opts.line !== undefined)
1721
- args.push("--line", String(opts.line));
1722
- if (opts.side)
1723
- args.push("--side", opts.side);
1724
- if (opts.username)
1725
- args.push("--username", opts.username);
1726
- await runChecked("tuicr", args);
1727
- }
1728
- async function launch(cwd, pr, localBase) {
1729
- let command;
1730
- if (pr !== undefined)
1731
- command = ["tuicr", "pr", String(pr)];
1732
- else if (localBase)
1733
- command = ["tuicr", "-w", "-r", `${localBase}..HEAD`];
1734
- else
1735
- throw new Error("Cannot launch a local tuicr review: no base branch could be determined.");
1736
- if (!await tuicrAvailable()) {
1737
- return {
1738
- launched: false,
1739
- via: "print",
1740
- command: command.join(" "),
1741
- reason: "tuicr is not installed or is not available on PATH.",
1742
- instruction: `Install tuicr, then run: ${command.join(" ")}`
1743
- };
1744
- }
1745
- const name = diffpiLaunchName(cwd, pr === undefined ? "local review" : `PR #${pr}`);
1746
- return openInNewTab(command, { cwd, name });
1747
- }
1748
- async function resolveSession(cwd, branch) {
1749
- const sessions = await listSessions(cwd);
1750
- return findMatchingSession(sessions, cwd, branch);
1751
- }
1752
- async function resolveReviewSession(cwd, target) {
1753
- if (!target.workingTree && target.owner && target.repo && target.number !== undefined) {
1754
- return resolvePrSession(cwd, target.owner, target.repo, target.number);
1755
- }
1756
- return resolveSession(cwd, target.branch);
1757
- }
1758
- async function resolvePrSession(cwd, owner, repo, number) {
1759
- const coordinate = `${owner}/${repo}`.toLowerCase();
1760
- const sessions = await listSessions(cwd);
1761
- return sessions.find((session) => {
1762
- const slug = session.slug.toLowerCase();
1763
- return session.kind === "pr" && slug.includes(coordinate) && (slug.endsWith(`/pr/${number}`) || slug.endsWith(`/mr/${number}`));
1764
- });
1765
- }
1766
- async function findMatchingSession(sessions, cwd, branch) {
1767
- const repository = await canonicalPath2(await gitToplevel(cwd));
1768
- for (const session of sessions) {
1769
- if (session.kind !== "local")
1770
- continue;
1771
- try {
1772
- const data = await readSession(session.path);
1773
- if (data.branch_name !== branch || !data.repo_path)
1774
- continue;
1775
- if (await canonicalPath2(data.repo_path) === repository)
1776
- return session;
1777
- } catch {}
1778
- }
1779
- return;
1780
- }
1781
- async function readSession(path) {
1782
- const content = await readFile5(path, "utf8");
1783
- try {
1784
- return JSON.parse(content);
1785
- } catch {
1786
- throw new Error(`Cannot parse tuicr session JSON: ${path}`);
1787
- }
1788
- }
1789
- function toFindings(session, options = {}) {
1790
- const comments = [];
1791
- const include = (comment) => (!options.agentOnly || commentAuthor(comment)?.startsWith("Agent: ")) && (!options.excludeLocalResponses || !isLocalResponse(comment.content));
1792
- const publishableBody = (comment) => {
1793
- const author = commentAuthor(comment);
1794
- const route = author?.match(/^Agent:\s*(.+)$/)?.[1];
1795
- return route ? withRemoteProvenance(comment.content, route) : comment.content;
1796
- };
1797
- const bodyParts = (session.review_comments ?? []).flatMap((comment) => include(comment) ? [publishableBody(comment)] : []);
1798
- for (const [file, entry] of Object.entries(session.files ?? {})) {
1799
- for (const fileComment of entry.file_comments ?? []) {
1800
- if (!include(fileComment))
1801
- continue;
1802
- const comment = {
1803
- file,
1804
- body: fileComment.content,
1805
- ...fileComment.id ? { sourceCommentId: String(fileComment.id) } : {}
1806
- };
1807
- const author = commentAuthor(fileComment);
1808
- if (author)
1809
- comment.author = author;
1810
- comments.push(comment);
1811
- }
1812
- for (const [lineKey, lineComments] of Object.entries(entry.line_comments ?? {})) {
1813
- const line = Number.parseInt(lineKey, 10);
1814
- if (!Number.isFinite(line))
1815
- continue;
1816
- for (const lineComment of lineComments) {
1817
- if (!include(lineComment))
1818
- continue;
1819
- const comment = {
1820
- file,
1821
- line,
1822
- side: lineComment.side === "old" ? "LEFT" : "RIGHT",
1823
- body: lineComment.content,
1824
- ...lineComment.id ? { sourceCommentId: String(lineComment.id) } : {}
1825
- };
1826
- const author = commentAuthor(lineComment);
1827
- if (author)
1828
- comment.author = author;
1829
- comments.push(comment);
1830
- }
1831
- }
1832
- }
1833
- return { comments, body: bodyParts.join(`
1834
-
1835
- `) };
1836
- }
1837
- async function requireTuicr() {
1838
- if (!await tuicrAvailable())
1839
- throw new Error("tuicr is not installed or is not available on PATH.");
1840
- }
1841
- function commentAuthor(comment) {
1842
- return comment.username ?? comment.author;
1843
- }
1844
- async function canonicalPath2(path) {
1845
- try {
1846
- return await realpath2(path);
1847
- } catch {
1848
- return resolve2(path);
1849
- }
1850
- }
1851
-
1852
- // src/review/local-review-backend.ts
1853
- function LocalReviewBackend(options) {
1854
- return {
1855
- kind: "local",
1856
- async stage(draft) {
1857
- for (const comment of draft.comments) {
1858
- await addComment(options.session, comment.body, {
1859
- targetFile: comment.file,
1860
- line: comment.line,
1861
- side: comment.side === "LEFT" ? "old" : "new",
1862
- username: options.author
1863
- });
1864
- }
1865
- if (draft.body.trim())
1866
- await addComment(options.session, draft.body, { username: options.author });
1867
- },
1868
- async readDraft() {
1869
- return toFindings(await readSession(options.session), { excludeLocalResponses: true });
1870
- },
1871
- async listThreads() {
1872
- try {
1873
- return parseThreadArtifact(await readFile6(options.artifactPath, "utf8"));
1874
- } catch (error) {
1875
- if (error.code === "ENOENT")
1876
- return [];
1877
- throw error;
1878
- }
1879
- },
1880
- async reply(input) {
1881
- const content = await readFile6(options.artifactPath, "utf8");
1882
- await writeFile3(options.artifactPath, upsertThreadReply(content, input.threadId, input.body, input.question, input.resolve), "utf8");
1883
- },
1884
- async publish() {
1885
- throw new Error("Promote a local draft through a remote review backend before publishing it.");
1886
- }
1887
- };
1888
- }
1889
-
1890
1321
  // src/review/review-backend.ts
1891
1322
  function createRemoteReviewBackend(vcs, number) {
1892
1323
  if (vcs.provider === "github")
@@ -1895,35 +1326,32 @@ function createRemoteReviewBackend(vcs, number) {
1895
1326
  return GitLabReviewBackend(vcs, number);
1896
1327
  throw new Error("A remote review backend requires a GitHub or GitLab repository.");
1897
1328
  }
1898
- function createLocalReviewBackend(options) {
1899
- return LocalReviewBackend(options);
1900
- }
1901
1329
  // src/review/review-state.ts
1902
1330
  import { createHash as createHash2 } from "node:crypto";
1903
- import { readFile as readFile7, rename, writeFile as writeFile4 } from "node:fs/promises";
1904
- import { join as join9 } from "node:path";
1905
- import { z as z5 } from "zod";
1331
+ import { readFile as readFile3, rename, writeFile as writeFile2 } from "node:fs/promises";
1332
+ import { join as join6 } from "node:path";
1333
+ import { z as z4 } from "zod";
1906
1334
 
1907
1335
  // src/store.ts
1908
1336
  import { createHash } from "node:crypto";
1909
- import { lstat, mkdir as mkdir3, readlink, realpath as realpath3, symlink, unlink } from "node:fs/promises";
1910
- import { homedir as homedir4 } from "node:os";
1911
- import { dirname as dirname4, isAbsolute as isAbsolute2, join as join8, resolve as resolve3 } from "node:path";
1337
+ import { lstat, mkdir as mkdir2, readlink, realpath as realpath2, symlink, unlink } from "node:fs/promises";
1338
+ import { homedir as homedir3 } from "node:os";
1339
+ import { dirname as dirname2, isAbsolute as isAbsolute2, join as join5, resolve as resolve2 } from "node:path";
1912
1340
  var STORE_LINK = ".diffpi";
1913
- var LEGACY_STORE_LINK = join8(".pi", "diffpi");
1341
+ var LEGACY_STORE_LINK = join5(".pi", "diffpi");
1914
1342
  async function computeProjectSlug(cwd) {
1915
1343
  return projectSlug(await inspectGitRepository(cwd));
1916
1344
  }
1917
- function storeGlobalRoot(homeDir = homedir4()) {
1918
- return join8(homeDir, ".difflab", "diffpi", "projects");
1345
+ function storeGlobalRoot(homeDir = homedir3()) {
1346
+ return join5(homeDir, ".difflab", "diffpi", "projects");
1919
1347
  }
1920
- async function ensureStore(cwd, homeDir = homedir4()) {
1348
+ async function ensureStore(cwd, homeDir = homedir3()) {
1921
1349
  const repository = await inspectGitRepository(cwd);
1922
1350
  const root = repository.root;
1923
1351
  const slug = projectSlug(repository);
1924
- const dest = join8(storeGlobalRoot(homeDir), slug);
1925
- const link = join8(root, STORE_LINK);
1926
- await mkdir3(dest, { recursive: true });
1352
+ const dest = join5(storeGlobalRoot(homeDir), slug);
1353
+ const link = join5(root, STORE_LINK);
1354
+ await mkdir2(dest, { recursive: true });
1927
1355
  try {
1928
1356
  await assertStoreLink(link, dest);
1929
1357
  } catch (error) {
@@ -1931,29 +1359,29 @@ async function ensureStore(cwd, homeDir = homedir4()) {
1931
1359
  throw error;
1932
1360
  await symlink(dest, link);
1933
1361
  }
1934
- await removeLegacyStoreLink(join8(root, LEGACY_STORE_LINK), dest);
1362
+ await removeLegacyStoreLink(join5(root, LEGACY_STORE_LINK), dest);
1935
1363
  return { slug, root, dest, link, linked: true };
1936
1364
  }
1937
- async function storeDir(cwd, homeDir = homedir4()) {
1365
+ async function storeDir(cwd, homeDir = homedir3()) {
1938
1366
  const store = await ensureStore(cwd, homeDir);
1939
1367
  return store.dest;
1940
1368
  }
1941
- async function plansDir(cwd, homeDir = homedir4()) {
1369
+ async function plansDir(cwd, homeDir = homedir3()) {
1942
1370
  const store = await ensureStore(cwd, homeDir);
1943
- const dir = join8(store.link, "plan");
1944
- await mkdir3(dir, { recursive: true });
1371
+ const dir = join5(store.link, "plan");
1372
+ await mkdir2(dir, { recursive: true });
1945
1373
  return dir;
1946
1374
  }
1947
- async function reviewsDir(cwd, homeDir = homedir4()) {
1375
+ async function reviewsDir(cwd, homeDir = homedir3()) {
1948
1376
  const store = await ensureStore(cwd, homeDir);
1949
- const dir = join8(store.link, "review");
1950
- await mkdir3(dir, { recursive: true });
1377
+ const dir = join5(store.link, "review");
1378
+ await mkdir2(dir, { recursive: true });
1951
1379
  return dir;
1952
1380
  }
1953
- async function sessionsDir(cwd, homeDir = homedir4()) {
1381
+ async function sessionsDir(cwd, homeDir = homedir3()) {
1954
1382
  const store = await ensureStore(cwd, homeDir);
1955
- const dir = join8(store.link, "sessions");
1956
- await mkdir3(dir, { recursive: true });
1383
+ const dir = join5(store.link, "sessions");
1384
+ await mkdir2(dir, { recursive: true });
1957
1385
  return dir;
1958
1386
  }
1959
1387
  async function assertStoreLink(path, dest) {
@@ -1961,7 +1389,7 @@ async function assertStoreLink(path, dest) {
1961
1389
  if (!entry.isSymbolicLink())
1962
1390
  throw new Error(`${path} exists and is not a symlink.`);
1963
1391
  const target = await symlinkTarget(path);
1964
- if (target !== await canonicalPath3(dest))
1392
+ if (target !== await canonicalPath2(dest))
1965
1393
  throw new Error(`${path} points to ${target}, not ${dest}.`);
1966
1394
  }
1967
1395
  async function removeLegacyStoreLink(path, dest) {
@@ -1970,7 +1398,7 @@ async function removeLegacyStoreLink(path, dest) {
1970
1398
  if (!entry.isSymbolicLink())
1971
1399
  return;
1972
1400
  const target = await symlinkTarget(path);
1973
- if (target === await canonicalPath3(dest))
1401
+ if (target === await canonicalPath2(dest))
1974
1402
  await unlink(path);
1975
1403
  } catch (error) {
1976
1404
  if (error.code !== "ENOENT")
@@ -1979,13 +1407,13 @@ async function removeLegacyStoreLink(path, dest) {
1979
1407
  }
1980
1408
  async function symlinkTarget(path) {
1981
1409
  const target = await readlink(path);
1982
- return canonicalPath3(isAbsolute2(target) ? target : resolve3(dirname4(path), target));
1410
+ return canonicalPath2(isAbsolute2(target) ? target : resolve2(dirname2(path), target));
1983
1411
  }
1984
- async function canonicalPath3(path) {
1412
+ async function canonicalPath2(path) {
1985
1413
  try {
1986
- return await realpath3(path);
1414
+ return await realpath2(path);
1987
1415
  } catch {
1988
- return resolve3(path);
1416
+ return resolve2(path);
1989
1417
  }
1990
1418
  }
1991
1419
  function projectSlug(repository) {
@@ -1995,13 +1423,13 @@ function projectSlug(repository) {
1995
1423
  }
1996
1424
 
1997
1425
  // src/review/review-state.ts
1998
- var reviewPublicationStateSchema = z5.object({
1999
- target: z5.string().optional(),
2000
- bodies: z5.array(z5.string()).default([]),
2001
- comments: z5.array(z5.string()).default([]),
2002
- stagedComments: z5.array(z5.string()).default([]),
2003
- replies: z5.array(z5.string()).default([]),
2004
- overlayPath: z5.string().optional()
1426
+ var reviewPublicationStateSchema = z4.object({
1427
+ target: z4.string().optional(),
1428
+ bodies: z4.array(z4.string()).default([]),
1429
+ comments: z4.array(z4.string()).default([]),
1430
+ stagedComments: z4.array(z4.string()).default([]),
1431
+ replies: z4.array(z4.string()).default([]),
1432
+ overlayPath: z4.string().optional()
2005
1433
  });
2006
1434
  function reviewBodyFingerprint(body) {
2007
1435
  return digest(body);
@@ -2017,15 +1445,15 @@ function unpublishedReviewComments(comments, knownFingerprints) {
2017
1445
  }
2018
1446
  async function loadReviewPublicationState(cwd, vcs, number, homeDir) {
2019
1447
  const target = `${vcs.provider}:${vcs.owner}/${vcs.repo}#${number}`;
2020
- const path = join9(await sessionsDir(cwd, homeDir), `review-publish-${digest(target).slice(0, 16)}.json`);
1448
+ const path = join6(await sessionsDir(cwd, homeDir), `review-publish-${digest(target).slice(0, 16)}.json`);
2021
1449
  try {
2022
- const parsed = reviewPublicationStateSchema.parse(JSON.parse(await readFile7(path, "utf8")));
1450
+ const parsed = reviewPublicationStateSchema.parse(JSON.parse(await readFile3(path, "utf8")));
2023
1451
  return { path, state: { ...parsed, target } };
2024
1452
  } catch (error) {
2025
1453
  if (error.code === "ENOENT") {
2026
1454
  return { path, state: { target, bodies: [], comments: [], stagedComments: [], replies: [] } };
2027
1455
  }
2028
- if (error instanceof SyntaxError || error instanceof z5.ZodError) {
1456
+ if (error instanceof SyntaxError || error instanceof z4.ZodError) {
2029
1457
  throw new Error(`Cannot parse review publication state: ${path}`);
2030
1458
  }
2031
1459
  throw error;
@@ -2033,7 +1461,7 @@ async function loadReviewPublicationState(cwd, vcs, number, homeDir) {
2033
1461
  }
2034
1462
  async function saveReviewPublicationState(path, state) {
2035
1463
  const temp = `${path}.${process.pid}.tmp`;
2036
- await writeFile4(temp, `${JSON.stringify(state, null, 2)}
1464
+ await writeFile2(temp, `${JSON.stringify(state, null, 2)}
2037
1465
  `, "utf8");
2038
1466
  await rename(temp, path);
2039
1467
  }
@@ -2041,12 +1469,12 @@ function digest(value) {
2041
1469
  return createHash2("sha256").update(value).digest("hex");
2042
1470
  }
2043
1471
  // src/mcp.ts
2044
- import { mkdir as mkdir4, readFile as readFile8, writeFile as writeFile5 } from "node:fs/promises";
2045
- import { homedir as homedir5 } from "node:os";
2046
- import { dirname as dirname5, join as join10 } from "node:path";
1472
+ import { mkdir as mkdir3, readFile as readFile4, writeFile as writeFile3 } from "node:fs/promises";
1473
+ import { homedir as homedir4 } from "node:os";
1474
+ import { dirname as dirname3, join as join7 } from "node:path";
2047
1475
  var mcp = {
2048
- globalConfigPath(homeDir = homedir5()) {
2049
- return join10(homeDir, ".config", "mcp", "mcp.json");
1476
+ globalConfigPath(homeDir = homedir4()) {
1477
+ return join7(homeDir, ".config", "mcp", "mcp.json");
2050
1478
  },
2051
1479
  async serversEnsure(servers, options = {}) {
2052
1480
  const path = options.path ?? mcp.globalConfigPath();
@@ -2059,8 +1487,8 @@ var mcp = {
2059
1487
  const next = { ...current, mcpServers: nextServers };
2060
1488
  const changed = JSON.stringify(current) !== JSON.stringify(next);
2061
1489
  if (changed && !options.dryRun) {
2062
- await mkdir4(dirname5(path), { recursive: true });
2063
- await writeFile5(path, `${JSON.stringify(next, null, 2)}
1490
+ await mkdir3(dirname3(path), { recursive: true });
1491
+ await writeFile3(path, `${JSON.stringify(next, null, 2)}
2064
1492
  `, "utf8");
2065
1493
  }
2066
1494
  return { path, changed, existed: currentText !== undefined, planned: changed && options.dryRun === true };
@@ -2089,7 +1517,7 @@ function getParsedConfig(content, path) {
2089
1517
  }
2090
1518
  async function getOptionalFile2(path) {
2091
1519
  try {
2092
- return await readFile8(path, "utf8");
1520
+ return await readFile4(path, "utf8");
2093
1521
  } catch (error) {
2094
1522
  if (error instanceof Error && "code" in error && error.code === "ENOENT")
2095
1523
  return;
@@ -2100,9 +1528,9 @@ function isRecord(value) {
2100
1528
  return value !== null && typeof value === "object" && !Array.isArray(value);
2101
1529
  }
2102
1530
  // src/pi.ts
2103
- import { mkdir as mkdir5, writeFile as writeFile6 } from "node:fs/promises";
2104
- import { homedir as homedir6 } from "node:os";
2105
- import { dirname as dirname6, join as join11 } from "node:path";
1531
+ import { mkdir as mkdir4, writeFile as writeFile4 } from "node:fs/promises";
1532
+ import { homedir as homedir5 } from "node:os";
1533
+ import { dirname as dirname4, join as join8 } from "node:path";
2106
1534
  var pi = {
2107
1535
  executableCheck: findPiExecutable,
2108
1536
  packageList: listPiPackages,
@@ -2115,19 +1543,19 @@ var pi = {
2115
1543
  configEnsure: ensurePiConfig
2116
1544
  };
2117
1545
  async function ensurePiAgent(filename, content, agentDir = resolvePiAgentDir(), dryRun = false) {
2118
- const path = join11(agentDir, "agents", filename);
1546
+ const path = join8(agentDir, "agents", filename);
2119
1547
  const currentText = await readTextIfExists(path);
2120
1548
  const changed = currentText !== content;
2121
1549
  if (changed && !dryRun) {
2122
- await mkdir5(dirname6(path), { recursive: true });
2123
- await writeFile6(path, content, "utf8");
1550
+ await mkdir4(dirname4(path), { recursive: true });
1551
+ await writeFile4(path, content, "utf8");
2124
1552
  }
2125
1553
  return { path, changed, existed: currentText !== undefined, planned: changed && dryRun };
2126
1554
  }
2127
- async function checkGlobalPiSkill(name, agentDir = resolvePiAgentDir(), sharedSkillsDir = join11(homedir6(), ".agents", "skills")) {
2128
- const roots = [join11(agentDir, "skills"), sharedSkillsDir];
1555
+ async function checkGlobalPiSkill(name, agentDir = resolvePiAgentDir(), sharedSkillsDir = join8(homedir5(), ".agents", "skills")) {
1556
+ const roots = [join8(agentDir, "skills"), sharedSkillsDir];
2129
1557
  for (const root of roots) {
2130
- if (await readTextIfExists(join11(root, name, "SKILL.md")) !== undefined)
1558
+ if (await readTextIfExists(join8(root, name, "SKILL.md")) !== undefined)
2131
1559
  return true;
2132
1560
  }
2133
1561
  return false;
@@ -2156,8 +1584,8 @@ async function ensurePiConfig(path, update, dryRun = false) {
2156
1584
  const next = update(current);
2157
1585
  const changed = JSON.stringify(current) !== JSON.stringify(next);
2158
1586
  if (changed && !dryRun) {
2159
- await mkdir5(dirname6(path), { recursive: true });
2160
- await writeFile6(path, `${JSON.stringify(next, null, 2)}
1587
+ await mkdir4(dirname4(path), { recursive: true });
1588
+ await writeFile4(path, `${JSON.stringify(next, null, 2)}
2161
1589
  `, "utf8");
2162
1590
  }
2163
1591
  return { path, changed, existed: currentText !== undefined, planned: changed && dryRun };
@@ -2176,8 +1604,8 @@ function hasPiPackage(listOutput, source) {
2176
1604
  async function installPiPackage(executable, source) {
2177
1605
  await runChecked(executable, ["install", source]);
2178
1606
  }
2179
- function resolvePiAgentDir(homeDir = homedir6()) {
2180
- return process.env.PI_CODING_AGENT_DIR ?? (process.env.XDG_CONFIG_HOME ? join11(process.env.XDG_CONFIG_HOME, "pi") : join11(homeDir, ".pi", "agent"));
1607
+ function resolvePiAgentDir(homeDir = homedir5()) {
1608
+ return process.env.PI_CODING_AGENT_DIR ?? (process.env.XDG_CONFIG_HOME ? join8(process.env.XDG_CONFIG_HOME, "pi") : join8(homeDir, ".pi", "agent"));
2181
1609
  }
2182
1610
  function parseJsonObject(content, path) {
2183
1611
  if (!content?.trim())
@@ -2190,27 +1618,27 @@ function parseJsonObject(content, path) {
2190
1618
  throw new Error(`Expected valid JSON object in ${path}.`);
2191
1619
  }
2192
1620
  // src/plan/verify.ts
2193
- import { readFile as readFile9, readdir as readdir2, stat } from "node:fs/promises";
2194
- import { basename as basename4, dirname as dirname7, join as join12, resolve as resolve4 } from "node:path";
1621
+ import { lstat as lstat2, readFile as readFile5, readdir as readdir2, stat } from "node:fs/promises";
1622
+ import { basename as basename4, dirname as dirname5, join as join9, resolve as resolve3 } from "node:path";
2195
1623
  async function verifyLivePlan(requested, cwd = process.cwd()) {
2196
- const path = resolve4(cwd, requested);
1624
+ const path = resolve3(cwd, requested);
2197
1625
  let planPath = path;
2198
1626
  try {
2199
1627
  if ((await stat(path)).isDirectory())
2200
- planPath = join12(path, "PLAN.md");
1628
+ planPath = join9(path, "PLAN.md");
2201
1629
  } catch {}
2202
1630
  const issues = [];
2203
1631
  const briefPaths = [];
2204
- const planDir = dirname7(planPath);
1632
+ const planDir = dirname5(planPath);
2205
1633
  if (basename4(planPath) !== "PLAN.md") {
2206
1634
  addIssue(issues, planPath, 0, "path", "Select PLAN.md or its directory.");
2207
1635
  return { planPath, briefPaths, ok: false, issues };
2208
1636
  }
2209
- if (!/^\d{6}(?:-[a-z0-9]+)+$/.test(basename4(planDir)) || basename4(dirname7(planDir)) !== "plan" || basename4(dirname7(dirname7(planDir))) !== ".diffpi")
1637
+ if (!/^\d{6}(?:-[a-z0-9]+)+$/.test(basename4(planDir)) || basename4(dirname5(planDir)) !== "plan" || basename4(dirname5(dirname5(planDir))) !== ".diffpi")
2210
1638
  addIssue(issues, planPath, 0, "layout", "Place PLAN.md under .diffpi/plan/<YYMMDD[-ticket]-short-slug>/.");
2211
1639
  let source;
2212
1640
  try {
2213
- source = await readFile9(planPath, "utf8");
1641
+ source = await readFile5(planPath, "utf8");
2214
1642
  } catch {
2215
1643
  addIssue(issues, planPath, 0, "missing-plan", "PLAN.md is not readable.");
2216
1644
  return { planPath, briefPaths, ok: false, issues };
@@ -2224,7 +1652,7 @@ async function verifyLivePlan(requested, cwd = process.cwd()) {
2224
1652
  if (!planLines[line]?.startsWith(`### Phase ${index + 1}: `))
2225
1653
  addIssue(issues, planPath, line + 1, "phase-order", "Number phase headings from 1 without gaps.");
2226
1654
  }
2227
- const implementation = join12(planDir, "implementation");
1655
+ const implementation = join9(planDir, "implementation");
2228
1656
  let rootEntries = [];
2229
1657
  let briefEntries = [];
2230
1658
  try {
@@ -2232,19 +1660,28 @@ async function verifyLivePlan(requested, cwd = process.cwd()) {
2232
1660
  briefEntries = await readdir2(implementation);
2233
1661
  } catch {}
2234
1662
  for (const entry of rootEntries) {
2235
- if (entry !== "PLAN.md" && entry !== "implementation")
2236
- addIssue(issues, join12(planDir, entry), 0, "layout", "Only PLAN.md and implementation/ belong in the plan directory.");
1663
+ const directory = ["implementation", "revisions", "logs"].includes(entry);
1664
+ const logFile = entry === "logs.jsonl";
1665
+ let valid = entry === "PLAN.md";
1666
+ if (directory || logFile) {
1667
+ try {
1668
+ const details = await lstat2(join9(planDir, entry));
1669
+ valid = !details.isSymbolicLink() && (directory ? details.isDirectory() : details.isFile());
1670
+ } catch {}
1671
+ }
1672
+ if (!valid)
1673
+ addIssue(issues, join9(planDir, entry), 0, "layout", "Only PLAN.md, implementation/, revisions/, logs.jsonl, and historical logs/ belong in the plan directory.");
2237
1674
  }
2238
1675
  for (const entry of briefEntries) {
2239
1676
  const ordinal = /^phase-(\d+)\.md$/.exec(entry)?.[1];
2240
1677
  if (!ordinal || !Number(ordinal) || Number(ordinal) > phases.length || entry !== `phase-${Number(ordinal)}.md`)
2241
- addIssue(issues, join12(implementation, entry), 0, "extra-brief", "Only matching numbered phase briefs belong in implementation/.");
1678
+ addIssue(issues, join9(implementation, entry), 0, "extra-brief", "Only matching numbered phase briefs belong in implementation/.");
2242
1679
  }
2243
1680
  for (let index = 0;index < phases.length; index++) {
2244
- const brief = join12(implementation, `phase-${index + 1}.md`);
1681
+ const brief = join9(implementation, `phase-${index + 1}.md`);
2245
1682
  briefPaths.push(brief);
2246
1683
  try {
2247
- const lines = visibleLines(await readFile9(brief, "utf8"));
1684
+ const lines = visibleLines(await readFile5(brief, "utf8"));
2248
1685
  if (!/^# Phase \d+:\s*\S/.test(lines[0] ?? "") || !lines[0]?.startsWith(`# Phase ${index + 1}: `))
2249
1686
  addIssue(issues, brief, 1, "phase-title", `Start with # Phase ${index + 1}: <title>.`);
2250
1687
  checkHeadings(lines, brief, ["Objective", "Files Affected", "Tasks", "Implementation Constraints"], issues);
@@ -2277,19 +1714,31 @@ function checkHeadings(lines, file, names, issues) {
2277
1714
  }
2278
1715
  // src/setup.ts
2279
1716
  import { parseFrontmatter as parseFrontmatter2 } from "@earendil-works/pi-coding-agent";
2280
- import { readdir as readdir3, readFile as readFile10 } from "node:fs/promises";
2281
- import { homedir as homedir7 } from "node:os";
2282
- import { basename as basename5, join as join13 } from "node:path";
1717
+ import { readdir as readdir3, readFile as readFile6 } from "node:fs/promises";
1718
+ import { homedir as homedir6 } from "node:os";
1719
+ import { basename as basename5, join as join11 } from "node:path";
1720
+
1721
+ // src/assets.ts
1722
+ import { existsSync } from "node:fs";
1723
+ import { dirname as dirname6, join as join10 } from "node:path";
1724
+ import { fileURLToPath } from "node:url";
1725
+ function resolveBundledAssetDir(name, moduleUrl = import.meta.url) {
1726
+ const moduleDir = dirname6(fileURLToPath(moduleUrl));
1727
+ const candidates = [join10(moduleDir, name), join10(moduleDir, "..", name), join10(moduleDir, "..", "..", name)];
1728
+ return candidates.find((path) => existsSync(path)) ?? candidates[1];
1729
+ }
1730
+ function resolveBundledAgentsDir(moduleUrl = import.meta.url) {
1731
+ return resolveBundledAssetDir("agents", moduleUrl);
1732
+ }
1733
+ function resolveBundledTemplatesDir(moduleUrl = import.meta.url) {
1734
+ return resolveBundledAssetDir("templates", moduleUrl);
1735
+ }
1736
+
1737
+ // src/setup.ts
2283
1738
  var MISE_DEPENDENCIES = [
2284
1739
  { name: "node", tool: "node", spec: "node@22", minimumVersion: "22.19.0" },
2285
1740
  { name: "zellij", tool: "zellij", spec: "zellij@latest", minimumVersion: undefined },
2286
1741
  { name: "helix", tool: "helix", spec: "helix@latest", minimumVersion: undefined },
2287
- {
2288
- name: "tuicr",
2289
- tool: "github:agavra/tuicr",
2290
- spec: "github:agavra/tuicr@latest",
2291
- minimumVersion: undefined
2292
- },
2293
1742
  {
2294
1743
  name: "context-mode",
2295
1744
  tool: "npm:context-mode",
@@ -2318,8 +1767,8 @@ var FORGE_DEPENDENCIES = {
2318
1767
  gitlab: { name: "glab", tool: "glab", spec: "glab@latest", minimumVersion: undefined }
2319
1768
  };
2320
1769
  async function ensureMise(options = {}) {
2321
- const homeDir = options.homeDir ?? homedir7();
2322
- const current = await mise.executableCheck() ?? await mise.executableCheck(join13(homeDir, ".local", "bin", "mise"));
1770
+ const homeDir = options.homeDir ?? homedir6();
1771
+ const current = await mise.executableCheck() ?? await mise.executableCheck(join11(homeDir, ".local", "bin", "mise"));
2323
1772
  if (current)
2324
1773
  return { executable: current, action: createSetupAction("mise", "ready", current) };
2325
1774
  reportProgress(options, "Installing mise");
@@ -2368,9 +1817,9 @@ async function ensureMiseDeps(miseExecutable, options = {}) {
2368
1817
  async function ensurePiPlugins(options = {}) {
2369
1818
  const actions = await ensurePiPackages(PI_PACKAGES, options);
2370
1819
  const agentDir = options.agentDir ?? pi.agentDir(options.homeDir);
2371
- const webSearch = await pi.configEnsure(join13(agentDir, "web-search.json"), (config) => ({ ...config, workflow: "auto-summary" }), options.dryRun);
1820
+ const webSearch = await pi.configEnsure(join11(agentDir, "web-search.json"), (config) => ({ ...config, workflow: "auto-summary" }), options.dryRun);
2372
1821
  actions.push(getConfigSetupAction("web search settings", webSearch));
2373
- const lsp = await pi.configEnsure(join13(agentDir, "pi-lsp.json"), (config) => ({
1822
+ const lsp = await pi.configEnsure(join11(agentDir, "pi-lsp.json"), (config) => ({
2374
1823
  ...config,
2375
1824
  progressive: { ...getRecord(config.progressive), enabled: true, inject: "none" }
2376
1825
  }), options.dryRun);
@@ -2385,7 +1834,7 @@ async function ensurePiAgents(options = {}) {
2385
1834
  const actions = [];
2386
1835
  for (const entry of entries) {
2387
1836
  const id = basename5(entry.name, ".md").replace(/^diffpi-/, "");
2388
- const source = await readFile10(join13(bundledAgentsDir, entry.name), "utf8");
1837
+ const source = await readFile6(join11(bundledAgentsDir, entry.name), "utf8");
2389
1838
  const content = materializeAgentModels(source, id, userConfig.config, options.availableModels);
2390
1839
  const result = await pi.agentEnsure(entry.name, content, agentDir, options.dryRun);
2391
1840
  actions.push(getConfigSetupAction(`pi agent ${id}`, result));
@@ -2394,7 +1843,7 @@ async function ensurePiAgents(options = {}) {
2394
1843
  }
2395
1844
  async function ensurePiSkills(miseExecutable, options = {}) {
2396
1845
  const agentDir = options.agentDir ?? pi.agentDir(options.homeDir);
2397
- const sharedSkillsDir = join13(options.homeDir ?? homedir7(), ".agents", "skills");
1846
+ const sharedSkillsDir = join11(options.homeDir ?? homedir6(), ".agents", "skills");
2398
1847
  const actions = [];
2399
1848
  for (const source of PI_SKILL_SOURCES) {
2400
1849
  const missing = [];
@@ -2462,8 +1911,6 @@ async function setupPi(options = {}) {
2462
1911
  actions.push(...await ensurePiAgents(options));
2463
1912
  actions.push(...await ensurePiSkills(miseResult.executable, options));
2464
1913
  actions.push(...await ensureMcpAdapters(miseResult.executable, options));
2465
- if (options.bindZedKey)
2466
- actions.push(...await ensureZedIntegration(options));
2467
1914
  return {
2468
1915
  actions,
2469
1916
  restartPi: setupRequiresRestart(actions)
@@ -2472,35 +1919,6 @@ async function setupPi(options = {}) {
2472
1919
  function setupRequiresRestart(actions) {
2473
1920
  return actions.some((item) => (item.status === "installed" || item.status === "updated") && (item.name.startsWith("pi package ") || item.name.startsWith("pi agent ") || item.name.startsWith("pi skill ") || item.name === "MCP configuration" || item.name === "web search settings" || item.name === "pi-lsp settings"));
2474
1921
  }
2475
- async function ensureZedIntegration(options = {}) {
2476
- if (options.dryRun) {
2477
- const actions = [
2478
- createSetupAction("Zed review tasks", "planned", "global static runtime-resolver tasks in tasks.json"),
2479
- createSetupAction("Zed plan task", "planned", "pinned package CLI task in tasks.json")
2480
- ];
2481
- if (options.bindZedKey)
2482
- actions.push(createSetupAction("Zed review keybinding", "planned", "keymap.json"));
2483
- return actions;
2484
- }
2485
- const actions = [];
2486
- try {
2487
- const tasks = await ensureZedReviewTask(options.homeDir);
2488
- actions.push(createSetupAction("Zed review tasks", tasks.changed ? "installed" : "ready", tasks.path));
2489
- const planTask = await ensureZedPlanTask(await packageVersion2(), options.homeDir);
2490
- actions.push(createSetupAction("Zed plan task", planTask.changed ? "installed" : "ready", planTask.path));
2491
- } catch (error) {
2492
- actions.push(createSetupAction("Zed review tasks", "skipped", error instanceof Error ? error.message : String(error)));
2493
- }
2494
- if (options.bindZedKey) {
2495
- try {
2496
- const key = await ensureZedReviewKeybinding(options.homeDir);
2497
- actions.push(createSetupAction("Zed review keybinding", key.changed ? "installed" : "ready", key.path));
2498
- } catch (error) {
2499
- actions.push(createSetupAction("Zed review keybinding", "skipped", error instanceof Error ? error.message : String(error)));
2500
- }
2501
- }
2502
- return actions;
2503
- }
2504
1922
  async function gitlabMcpHost(projectDir) {
2505
1923
  try {
2506
1924
  const vcs = await detectVcs(projectDir);
@@ -2589,29 +2007,17 @@ function createSetupAction(name, status, detail) {
2589
2007
  function reportProgress(options, message) {
2590
2008
  options.onProgress?.(message);
2591
2009
  }
2592
- async function packageVersion2() {
2593
- const source = await readFile10(join13(resolveBundledAgentsDir(), "..", "package.json"), "utf8");
2594
- let value;
2595
- try {
2596
- value = JSON.parse(source);
2597
- } catch (error) {
2598
- throw new Error("Cannot parse the installed @difflab/pi package metadata.", { cause: error });
2599
- }
2600
- if (typeof value.version !== "string")
2601
- throw new Error("Cannot resolve the installed @difflab/pi version.");
2602
- return value.version;
2603
- }
2604
2010
  function getRecord(value) {
2605
2011
  return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
2606
2012
  }
2607
2013
  // src/templates.ts
2608
- import { readFile as readFile11 } from "node:fs/promises";
2609
- import { homedir as homedir8 } from "node:os";
2610
- import { join as join14, normalize } from "node:path";
2014
+ import { readFile as readFile7 } from "node:fs/promises";
2015
+ import { homedir as homedir7 } from "node:os";
2016
+ import { join as join12, normalize } from "node:path";
2611
2017
  async function loadTemplate(name, options = {}) {
2612
2018
  const relative = templateRelativePath(name);
2613
- const userPath = join14(options.homeDir ?? homedir8(), ".difflab", "diffpi", "templates", relative);
2614
- const bundledPath = join14(options.bundledDir ?? resolveBundledTemplatesDir(), relative);
2019
+ const userPath = join12(options.homeDir ?? homedir7(), ".difflab", "diffpi", "templates", relative);
2020
+ const bundledPath = join12(options.bundledDir ?? resolveBundledTemplatesDir(), relative);
2615
2021
  const user = await readOptionalFile(userPath);
2616
2022
  if (user !== undefined)
2617
2023
  return { name, path: userPath, source: "user", content: user };
@@ -2632,7 +2038,7 @@ function templateRelativePath(name) {
2632
2038
  }
2633
2039
  async function readOptionalFile(path) {
2634
2040
  try {
2635
- return await readFile11(path, "utf8");
2041
+ return await readFile7(path, "utf8");
2636
2042
  } catch (error) {
2637
2043
  if (error.code === "ENOENT")
2638
2044
  return;
@@ -2643,18 +2049,12 @@ export {
2643
2049
  CONVENTIONAL_COMMIT,
2644
2050
  GitHubVcsBackend,
2645
2051
  GitLabVcsBackend,
2646
- ZED_LOCAL_REVIEW_TASK_NAME,
2647
- ZED_PLAN_ANNOTATE_TASK_NAME,
2648
- ZED_PR_REVIEW_TASK_NAME,
2649
- ZED_REVIEW_TASK_NAME,
2650
- addComment,
2651
2052
  assertGitHubMergeReady,
2652
2053
  assertReviewEventSupported,
2653
2054
  checkConventionalSubject,
2654
2055
  computeProjectSlug,
2655
2056
  createForge,
2656
2057
  createForgeBackend,
2657
- createLocalReviewBackend,
2658
2058
  createRemoteReviewBackend,
2659
2059
  createVcsBackend,
2660
2060
  dedupeFindings,
@@ -2672,17 +2072,12 @@ export {
2672
2072
  ensurePiPlugins,
2673
2073
  ensurePiSkills,
2674
2074
  ensureStore,
2675
- ensureZedPlanTask,
2676
- ensureZedReviewKeybinding,
2677
- ensureZedReviewTask,
2678
2075
  findPreferredModel,
2679
2076
  findingSchema,
2680
2077
  findingsSchema,
2681
2078
  gitToplevel,
2682
2079
  githubReviewSubmissionEndpoint,
2683
2080
  hasGitlabDraftNotes,
2684
- launch,
2685
- listSessions,
2686
2081
  loadDiffpiConfig,
2687
2082
  loadReviewPublicationState,
2688
2083
  loadTemplate,
@@ -2690,20 +2085,12 @@ export {
2690
2085
  mcp,
2691
2086
  mise,
2692
2087
  openFileAdjacent,
2693
- openInNewTab,
2694
2088
  parseGitlabDiffRefs,
2695
2089
  parseRemote,
2696
- parseThreadArtifact,
2697
2090
  pi,
2698
2091
  plansDir,
2699
- readSession,
2700
- renderReviewDoc,
2701
2092
  renderTemplate,
2702
- renderThreadArtifact,
2703
2093
  resolveAgentModelPreferences,
2704
- resolvePrSession,
2705
- resolveReviewSession,
2706
- resolveSession,
2707
2094
  reviewBodyFingerprint,
2708
2095
  reviewCommentFingerprint,
2709
2096
  reviewRecordName,
@@ -2718,15 +2105,9 @@ export {
2718
2105
  storeDir,
2719
2106
  storeGlobalRoot,
2720
2107
  templateRelativePath,
2721
- toFindings,
2722
2108
  toReviewComments,
2723
- tuicrAvailable,
2724
2109
  unpublishedReviewComments,
2725
- upsertThreadReply,
2726
2110
  verifyLivePlan,
2727
2111
  withRemoteProvenance,
2728
- yymmdd,
2729
- zedKeymapPath,
2730
- zedReviewTaskName,
2731
- zedTasksPath
2112
+ yymmdd
2732
2113
  };