@difflab/pi 0.2.0-rc.202609191250.90d35bd → 0.2.0-rc.202609191441.52cd715

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/README.md CHANGED
@@ -38,11 +38,11 @@ Standard agents come from the same global and trusted-project directories used b
38
38
 
39
39
  The skill delegates mechanics to `review_context`, `review_new`, `review_edit`, `review_diff`, `review_gates`, `review_submit`, `review_add_comment`, `review_comments`, `review_respond`, `review_publish`, `review_complete`, `review_merge`, and `review_launch_ui`. Inline `auto` and `address` activate Reviewer on Sol; lifecycle verbs activate Orchestrator on Luna. Reviewer classifies address threads and delegates bounded edits to lightweight workers. `--bg` leaves the current chat mode unchanged and launches a tracked Orchestrator child. The generic `diffpi_template` tool loads bundled templates or user overrides. GitHub and GitLab support review creation and publication. Merge is intentionally GitHub-only and remains separate from publish and complete.
40
40
 
41
- `--local` selects the `tuicr` working-tree review backend. Without it, targets are a PR/MR number, URL, or branch; no target uses the current branch. Local address flows apply fixes without commits; remote address flows use the bundled `/git commit --no-push` workflow before posting draft responses for changed threads. Local reply overlays preserve remote thread IDs until `publish --local` promotes comments and replies to the forge. Remote comments carry a generated-review notice with the exact provider/model route; local comments use `Agent: <provider/model>` as the author.
41
+ `--local` selects the `tuicr` working-tree review backend. Without it, targets are a PR/MR number, URL, or branch; no target uses the current branch. Local address flows apply fixes without commits; remote address flows use the upstream `/git commit --no-push` workflow before posting draft responses for changed threads. Local reply overlays preserve remote thread IDs until `publish --local` promotes comments and replies to the forge. Remote comments carry a generated-review notice with the exact provider/model route; local comments use `Agent: <provider/model>` as the author.
42
42
 
43
43
  Local artifacts live in `.diffpi/review/` and use `YYMMDD-<short-head-sha>.md` or `YYMMDD-uncommitted.md` names. `.diffpi` links to `~/.difflab/diffpi/projects/<repository-name>-<identity-hash>/`, so all worktrees for one remote share records while unrelated same-named repositories remain isolated. The launcher opens a repository-scoped mux tab when zellij, tmux, or screen is detected; without a mux, Zed lazily gets a `diffpi: tuicr review` task, and other environments receive the command to run.
44
44
 
45
- Draft PR bodies use the bundled `review/draft-pr.md` template. Override it at `~/.difflab/diffpi/templates/review/draft-pr.md`. The bundled `/git` skill provides conventional commit and safe rebase workflows. During package development, run `mise watch //packages/pi:dev`; the task builds and installs the package when its sources change. Run `/reload` in Pi after each successful install.
45
+ Draft PR bodies use the bundled `review/draft-pr.md` template. Override it at `~/.difflab/diffpi/templates/review/draft-pr.md`. Setup installs the upstream Codevoyant `/git` skill for conventional commit and safe rebase workflows. During package development, run `mise watch //packages/pi:dev`; the task builds and installs the package when its sources change. Run `/reload` in Pi after each successful install.
46
46
 
47
47
  The package root exports environment and forge lifecycle adapters, review backends, gate checks, review schemas and artifact helpers, template helpers, store helpers, tuicr helpers, setup operations, and inline-mode control. `@difflab/pi/tools` exports `createReviewTools` and the complete tool catalog.
48
48
 
@@ -13699,7 +13699,7 @@ function isModeBaseline(value) {
13699
13699
  }
13700
13700
 
13701
13701
  // src/commands/mode.ts
13702
- var MODE_USAGE = `/mode [agent|clear|reset]
13702
+ var MODE_USAGE = `/mode [agent|default|clear|reset]
13703
13703
 
13704
13704
  Use /mode to pick a profile. Available profiles: copilot, orchestrator, reviewer, tutor, worker.`;
13705
13705
  function registerModeCommand(pi, modes) {
@@ -13727,7 +13727,7 @@ async function handleModeCommand(args, ctx, modes) {
13727
13727
  ctx.ui.notify(MODE_USAGE, "error");
13728
13728
  return;
13729
13729
  }
13730
- const result = requested === "clear" || requested === "reset" ? await modes.unset(ctx) : await modes.set(requested, ctx);
13730
+ const result = requested === "default" || requested === "clear" || requested === "reset" ? await modes.unset(ctx) : await modes.set(requested, ctx);
13731
13731
  ctx.ui.notify(`${result.message} Changes apply on the next turn.`, result.ok ? "info" : "error");
13732
13732
  }
13733
13733
 
@@ -14032,30 +14032,37 @@ import { homedir as homedir4 } from "node:os";
14032
14032
  import { dirname as dirname3, join as join8 } from "node:path";
14033
14033
  var ZED_REVIEW_TASK_NAME = "diffpi: tuicr review";
14034
14034
  var REVIEW_KEYBINDING = "cmd-alt-r";
14035
- var REVIEW_TASK = {
14036
- label: ZED_REVIEW_TASK_NAME,
14037
- command: "tuicr",
14038
- cwd: "$ZED_WORKTREE_ROOT",
14039
- use_new_terminal: true,
14040
- reveal: "always",
14041
- reveal_target: "center"
14042
- };
14035
+ function reviewTask(command = ["tuicr"]) {
14036
+ const [executable, ...args] = command;
14037
+ if (!executable)
14038
+ throw new Error("The Zed review task requires a command.");
14039
+ return {
14040
+ label: ZED_REVIEW_TASK_NAME,
14041
+ command: executable,
14042
+ args: args.length > 0 ? args : undefined,
14043
+ cwd: "$ZED_WORKTREE_ROOT",
14044
+ use_new_terminal: true,
14045
+ reveal: "always",
14046
+ reveal_target: "center"
14047
+ };
14048
+ }
14043
14049
  function zedTasksPath(homeDir = homedir4()) {
14044
14050
  return join8(homeDir, ".config", "zed", "tasks.json");
14045
14051
  }
14046
14052
  function zedKeymapPath(homeDir = homedir4()) {
14047
14053
  return join8(homeDir, ".config", "zed", "keymap.json");
14048
14054
  }
14049
- async function ensureZedReviewTask(homeDir = homedir4()) {
14055
+ async function ensureZedReviewTask(homeDir = homedir4(), command = ["tuicr"]) {
14050
14056
  const path = zedTasksPath(homeDir);
14051
14057
  const currentText = await readOptional(path);
14052
14058
  const tasks = parseJsonArray(currentText, path);
14053
14059
  const index = tasks.findIndex((task) => task.label === ZED_REVIEW_TASK_NAME);
14054
14060
  const next = [...tasks];
14061
+ const task = reviewTask(command);
14055
14062
  if (index >= 0)
14056
- next[index] = { ...tasks[index], ...REVIEW_TASK };
14063
+ next[index] = { ...tasks[index], ...task };
14057
14064
  else
14058
- next.push(REVIEW_TASK);
14065
+ next.push(task);
14059
14066
  const changed = JSON.stringify(tasks) !== JSON.stringify(next);
14060
14067
  if (changed)
14061
14068
  await writeJson(path, next);
@@ -14169,7 +14176,7 @@ async function openInNewTab(command, opts) {
14169
14176
  }
14170
14177
  if (detectIde(env) === "zed") {
14171
14178
  try {
14172
- await ensureZedReviewTask(opts.homeDir);
14179
+ await ensureZedReviewTask(opts.homeDir, command);
14173
14180
  return {
14174
14181
  launched: false,
14175
14182
  configured: true,
@@ -15706,6 +15713,9 @@ function modelRoute(ctx) {
15706
15713
  throw new Error("Cannot record review provenance because Pi did not provide an active model route.");
15707
15714
  return `${ctx.model.provider}/${ctx.model.id}`;
15708
15715
  }
15716
+ function hasReviewDraft(comments, body) {
15717
+ return comments.length > 0 || body.trim().length > 0;
15718
+ }
15709
15719
  async function workingTreeDiff(cwd) {
15710
15720
  const tracked = await run("git", ["-C", cwd, "diff", "HEAD"], { capture: "unbounded" });
15711
15721
  if (tracked.code !== 0)
@@ -15890,6 +15900,8 @@ ${openMessage}`, {
15890
15900
  notVerified: params.notVerified ?? []
15891
15901
  }), "utf8");
15892
15902
  const comments = toReviewComments(findings, useLocalBackend ? undefined : model);
15903
+ const body = (params.overallIssues ?? []).join(`
15904
+ `);
15893
15905
  if (useLocalBackend) {
15894
15906
  const session = await resolveTuicrSession(review, Boolean(params.local || params.workingTree));
15895
15907
  if (!session) {
@@ -15903,16 +15915,16 @@ ${openMessage}`, {
15903
15915
  artifactPath: artifact,
15904
15916
  author: localReviewAuthor(model)
15905
15917
  });
15906
- await backend.stage({ comments, body: (params.overallIssues ?? []).join(`
15907
- `) });
15918
+ await backend.stage({ comments, body });
15908
15919
  return result(`Local review staged in tuicr: ${artifact}`, { artifact, count: findings.length, session });
15909
15920
  }
15910
15921
  if (!review.pr)
15911
15922
  return result(`Review written: ${artifact}. No PR/MR matches this remote target.`, { artifact });
15912
- if (comments.length > 0) {
15913
- await createRemoteReviewBackend(review.vcs, review.pr.number).stage({ comments, body: "" });
15923
+ const hasDraft = hasReviewDraft(comments, body);
15924
+ if (hasDraft) {
15925
+ await createRemoteReviewBackend(review.vcs, review.pr.number).stage({ comments, body });
15914
15926
  }
15915
- return result(`${comments.length > 0 ? "Pending review staged" : "Clean review recorded"} on #${review.pr.number}. Artifact: ${artifact}`, {
15927
+ return result(`${hasDraft ? "Pending review staged" : "Clean review recorded"} on #${review.pr.number}. Artifact: ${artifact}`, {
15916
15928
  artifact,
15917
15929
  pr: review.pr,
15918
15930
  count: findings.length
@@ -16069,7 +16081,7 @@ ${params.body}`, {
16069
16081
  label: "review complete",
16070
16082
  description: "Approve, reject, or abandon a remote review, or archive a local review artifact.",
16071
16083
  promptSnippet: "Call review_complete to finish a review without merging it.",
16072
- promptGuidelines: ["A local completion archives the Diffpi artifact but leaves the tuicr session intact."],
16084
+ promptGuidelines: ["A local completion archives the Diffpi artifact and deletes the matching tuicr session."],
16073
16085
  parameters: parameters5(completeSchema),
16074
16086
  executionMode: "sequential",
16075
16087
  async execute(_id, input, _signal, _onUpdate, ctx) {
@@ -16558,7 +16570,8 @@ var PI_PACKAGES = [
16558
16570
  ];
16559
16571
  var PI_SKILL_SOURCES = [
16560
16572
  { repository: "arabold/docs-mcp-server", skills: ["docs-manage", "docs-search", "fetch-url"] },
16561
- { repository: "AminBlg/SimpleEnglish", skills: ["simple-english"] }
16573
+ { repository: "AminBlg/SimpleEnglish", skills: ["simple-english"] },
16574
+ { repository: "cloudvoyant/codevoyant", skills: ["git"] }
16562
16575
  ];
16563
16576
  var MCP_ADAPTER_PACKAGE = "npm:pi-mcp-adapter";
16564
16577
  var BUNDLED_AGENTS_DIR2 = resolveBundledAgentsDir();
@@ -16806,7 +16819,8 @@ ${source}`;
16806
16819
  }
16807
16820
  async function gitlabMcpHost(projectDir) {
16808
16821
  try {
16809
- return (await detectVcs(projectDir)).host || "gitlab.com";
16822
+ const vcs = await detectVcs(projectDir);
16823
+ return vcs.provider === "gitlab" && vcs.host ? vcs.host : "gitlab.com";
16810
16824
  } catch {
16811
16825
  return "gitlab.com";
16812
16826
  }
package/dist/index.js CHANGED
@@ -177,30 +177,37 @@ import { homedir as homedir2 } from "node:os";
177
177
  import { dirname, join as join3 } from "node:path";
178
178
  var ZED_REVIEW_TASK_NAME = "diffpi: tuicr review";
179
179
  var REVIEW_KEYBINDING = "cmd-alt-r";
180
- var REVIEW_TASK = {
181
- label: ZED_REVIEW_TASK_NAME,
182
- command: "tuicr",
183
- cwd: "$ZED_WORKTREE_ROOT",
184
- use_new_terminal: true,
185
- reveal: "always",
186
- reveal_target: "center"
187
- };
180
+ function reviewTask(command = ["tuicr"]) {
181
+ const [executable, ...args] = command;
182
+ if (!executable)
183
+ throw new Error("The Zed review task requires a command.");
184
+ return {
185
+ label: ZED_REVIEW_TASK_NAME,
186
+ command: executable,
187
+ args: args.length > 0 ? args : undefined,
188
+ cwd: "$ZED_WORKTREE_ROOT",
189
+ use_new_terminal: true,
190
+ reveal: "always",
191
+ reveal_target: "center"
192
+ };
193
+ }
188
194
  function zedTasksPath(homeDir = homedir2()) {
189
195
  return join3(homeDir, ".config", "zed", "tasks.json");
190
196
  }
191
197
  function zedKeymapPath(homeDir = homedir2()) {
192
198
  return join3(homeDir, ".config", "zed", "keymap.json");
193
199
  }
194
- async function ensureZedReviewTask(homeDir = homedir2()) {
200
+ async function ensureZedReviewTask(homeDir = homedir2(), command = ["tuicr"]) {
195
201
  const path = zedTasksPath(homeDir);
196
202
  const currentText = await readOptional(path);
197
203
  const tasks = parseJsonArray(currentText, path);
198
204
  const index = tasks.findIndex((task) => task.label === ZED_REVIEW_TASK_NAME);
199
205
  const next = [...tasks];
206
+ const task = reviewTask(command);
200
207
  if (index >= 0)
201
- next[index] = { ...tasks[index], ...REVIEW_TASK };
208
+ next[index] = { ...tasks[index], ...task };
202
209
  else
203
- next.push(REVIEW_TASK);
210
+ next.push(task);
204
211
  const changed = JSON.stringify(tasks) !== JSON.stringify(next);
205
212
  if (changed)
206
213
  await writeJson(path, next);
@@ -314,7 +321,7 @@ async function openInNewTab(command, opts) {
314
321
  }
315
322
  if (detectIde(env) === "zed") {
316
323
  try {
317
- await ensureZedReviewTask(opts.homeDir);
324
+ await ensureZedReviewTask(opts.homeDir, command);
318
325
  return {
319
326
  launched: false,
320
327
  configured: true,
@@ -2191,7 +2198,8 @@ var PI_PACKAGES = [
2191
2198
  ];
2192
2199
  var PI_SKILL_SOURCES = [
2193
2200
  { repository: "arabold/docs-mcp-server", skills: ["docs-manage", "docs-search", "fetch-url"] },
2194
- { repository: "AminBlg/SimpleEnglish", skills: ["simple-english"] }
2201
+ { repository: "AminBlg/SimpleEnglish", skills: ["simple-english"] },
2202
+ { repository: "cloudvoyant/codevoyant", skills: ["git"] }
2195
2203
  ];
2196
2204
  var MCP_ADAPTER_PACKAGE = "npm:pi-mcp-adapter";
2197
2205
  var BUNDLED_AGENTS_DIR2 = resolveBundledAgentsDir();
@@ -2439,7 +2447,8 @@ ${source}`;
2439
2447
  }
2440
2448
  async function gitlabMcpHost(projectDir) {
2441
2449
  try {
2442
- return (await detectVcs(projectDir)).host || "gitlab.com";
2450
+ const vcs = await detectVcs(projectDir);
2451
+ return vcs.provider === "gitlab" && vcs.host ? vcs.host : "gitlab.com";
2443
2452
  } catch {
2444
2453
  return "gitlab.com";
2445
2454
  }
package/dist/setup.d.ts CHANGED
@@ -43,4 +43,5 @@ export declare function ensureMcpAdapters(miseExecutable: string, options?: Setu
43
43
  export declare function setupPi(options?: SetupOptions): Promise<SetupResult>;
44
44
  export declare function setupRequiresRestart(actions: readonly SetupAction[]): boolean;
45
45
  export declare function ensureZedIntegration(options?: SetupOptions): Promise<SetupAction[]>;
46
+ export declare function gitlabMcpHost(projectDir: string): Promise<string>;
46
47
  //# sourceMappingURL=setup.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../src/setup.ts"],"names":[],"mappings":"AAqDA,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,CAAC;AACtD,MAAM,MAAM,KAAK,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,CAAC;AACjD,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,WAAW,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;AAEpF,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,WAAW,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,0FAA0F;IAC1F,MAAM,CAAC,EAAE,SAAS,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,CAAC;IAC3C,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,SAAS;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC9D,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACxC;AASD,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,SAAS,EAAE,OAAO,CAAC;CACpB;AAID,wBAAsB,UAAU,CAAC,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,WAAW,CAAA;CAAE,CAAC,CAgBjH;AAED,wBAAsB,eAAe,CAAC,cAAc,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,WAAW,CAAC,CAU9G;AAED,wBAAsB,cAAc,CAAC,cAAc,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAsB/G;AAED,wBAAsB,eAAe,CAAC,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAqBxF;AAED,wBAAsB,cAAc,CAAC,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAkBvF;AAED,wBAAsB,cAAc,CAAC,cAAc,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAsB/G;AAED,wBAAsB,iBAAiB,CAAC,cAAc,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAwClH;AAID,wBAAsB,OAAO,CAAC,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,WAAW,CAAC,CAgB9E;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,SAAS,WAAW,EAAE,GAAG,OAAO,CAW7E;AA6CD,wBAAsB,oBAAoB,CAAC,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CA0B7F"}
1
+ {"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../src/setup.ts"],"names":[],"mappings":"AAsDA,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,CAAC;AACtD,MAAM,MAAM,KAAK,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,CAAC;AACjD,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,WAAW,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;AAEpF,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,WAAW,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,0FAA0F;IAC1F,MAAM,CAAC,EAAE,SAAS,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,CAAC;IAC3C,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,SAAS;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC9D,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACxC;AASD,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,SAAS,EAAE,OAAO,CAAC;CACpB;AAID,wBAAsB,UAAU,CAAC,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,WAAW,CAAA;CAAE,CAAC,CAgBjH;AAED,wBAAsB,eAAe,CAAC,cAAc,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,WAAW,CAAC,CAU9G;AAED,wBAAsB,cAAc,CAAC,cAAc,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAsB/G;AAED,wBAAsB,eAAe,CAAC,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAqBxF;AAED,wBAAsB,cAAc,CAAC,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAkBvF;AAED,wBAAsB,cAAc,CAAC,cAAc,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAsB/G;AAED,wBAAsB,iBAAiB,CAAC,cAAc,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAwClH;AAID,wBAAsB,OAAO,CAAC,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,WAAW,CAAC,CAgB9E;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,SAAS,WAAW,EAAE,GAAG,OAAO,CAW7E;AA6CD,wBAAsB,oBAAoB,CAAC,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CA0B7F;AA2BD,wBAAsB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAOvE"}
@@ -214,30 +214,37 @@ import { homedir } from "node:os";
214
214
  import { dirname, join as join2 } from "node:path";
215
215
  var ZED_REVIEW_TASK_NAME = "diffpi: tuicr review";
216
216
  var REVIEW_KEYBINDING = "cmd-alt-r";
217
- var REVIEW_TASK = {
218
- label: ZED_REVIEW_TASK_NAME,
219
- command: "tuicr",
220
- cwd: "$ZED_WORKTREE_ROOT",
221
- use_new_terminal: true,
222
- reveal: "always",
223
- reveal_target: "center"
224
- };
217
+ function reviewTask(command = ["tuicr"]) {
218
+ const [executable, ...args] = command;
219
+ if (!executable)
220
+ throw new Error("The Zed review task requires a command.");
221
+ return {
222
+ label: ZED_REVIEW_TASK_NAME,
223
+ command: executable,
224
+ args: args.length > 0 ? args : undefined,
225
+ cwd: "$ZED_WORKTREE_ROOT",
226
+ use_new_terminal: true,
227
+ reveal: "always",
228
+ reveal_target: "center"
229
+ };
230
+ }
225
231
  function zedTasksPath(homeDir = homedir()) {
226
232
  return join2(homeDir, ".config", "zed", "tasks.json");
227
233
  }
228
234
  function zedKeymapPath(homeDir = homedir()) {
229
235
  return join2(homeDir, ".config", "zed", "keymap.json");
230
236
  }
231
- async function ensureZedReviewTask(homeDir = homedir()) {
237
+ async function ensureZedReviewTask(homeDir = homedir(), command = ["tuicr"]) {
232
238
  const path = zedTasksPath(homeDir);
233
239
  const currentText = await readOptional(path);
234
240
  const tasks = parseJsonArray(currentText, path);
235
241
  const index = tasks.findIndex((task) => task.label === ZED_REVIEW_TASK_NAME);
236
242
  const next = [...tasks];
243
+ const task = reviewTask(command);
237
244
  if (index >= 0)
238
- next[index] = { ...tasks[index], ...REVIEW_TASK };
245
+ next[index] = { ...tasks[index], ...task };
239
246
  else
240
- next.push(REVIEW_TASK);
247
+ next.push(task);
241
248
  const changed = JSON.stringify(tasks) !== JSON.stringify(next);
242
249
  if (changed)
243
250
  await writeJson(path, next);
@@ -351,7 +358,7 @@ async function openInNewTab(command, opts) {
351
358
  }
352
359
  if (detectIde(env) === "zed") {
353
360
  try {
354
- await ensureZedReviewTask(opts.homeDir);
361
+ await ensureZedReviewTask(opts.homeDir, command);
355
362
  return {
356
363
  launched: false,
357
364
  configured: true,
@@ -1906,6 +1913,9 @@ function modelRoute(ctx) {
1906
1913
  throw new Error("Cannot record review provenance because Pi did not provide an active model route.");
1907
1914
  return `${ctx.model.provider}/${ctx.model.id}`;
1908
1915
  }
1916
+ function hasReviewDraft(comments, body) {
1917
+ return comments.length > 0 || body.trim().length > 0;
1918
+ }
1909
1919
  async function workingTreeDiff(cwd) {
1910
1920
  const tracked = await run("git", ["-C", cwd, "diff", "HEAD"], { capture: "unbounded" });
1911
1921
  if (tracked.code !== 0)
@@ -2090,6 +2100,8 @@ ${openMessage}`, {
2090
2100
  notVerified: params.notVerified ?? []
2091
2101
  }), "utf8");
2092
2102
  const comments = toReviewComments(findings, useLocalBackend ? undefined : model);
2103
+ const body = (params.overallIssues ?? []).join(`
2104
+ `);
2093
2105
  if (useLocalBackend) {
2094
2106
  const session = await resolveTuicrSession(review, Boolean(params.local || params.workingTree));
2095
2107
  if (!session) {
@@ -2103,16 +2115,16 @@ ${openMessage}`, {
2103
2115
  artifactPath: artifact,
2104
2116
  author: localReviewAuthor(model)
2105
2117
  });
2106
- await backend.stage({ comments, body: (params.overallIssues ?? []).join(`
2107
- `) });
2118
+ await backend.stage({ comments, body });
2108
2119
  return result(`Local review staged in tuicr: ${artifact}`, { artifact, count: findings.length, session });
2109
2120
  }
2110
2121
  if (!review.pr)
2111
2122
  return result(`Review written: ${artifact}. No PR/MR matches this remote target.`, { artifact });
2112
- if (comments.length > 0) {
2113
- await createRemoteReviewBackend(review.vcs, review.pr.number).stage({ comments, body: "" });
2123
+ const hasDraft = hasReviewDraft(comments, body);
2124
+ if (hasDraft) {
2125
+ await createRemoteReviewBackend(review.vcs, review.pr.number).stage({ comments, body });
2114
2126
  }
2115
- return result(`${comments.length > 0 ? "Pending review staged" : "Clean review recorded"} on #${review.pr.number}. Artifact: ${artifact}`, {
2127
+ return result(`${hasDraft ? "Pending review staged" : "Clean review recorded"} on #${review.pr.number}. Artifact: ${artifact}`, {
2116
2128
  artifact,
2117
2129
  pr: review.pr,
2118
2130
  count: findings.length
@@ -2269,7 +2281,7 @@ ${params.body}`, {
2269
2281
  label: "review complete",
2270
2282
  description: "Approve, reject, or abandon a remote review, or archive a local review artifact.",
2271
2283
  promptSnippet: "Call review_complete to finish a review without merging it.",
2272
- promptGuidelines: ["A local completion archives the Diffpi artifact but leaves the tuicr session intact."],
2284
+ promptGuidelines: ["A local completion archives the Diffpi artifact and deletes the matching tuicr session."],
2273
2285
  parameters: parameters(completeSchema),
2274
2286
  executionMode: "sequential",
2275
2287
  async execute(_id, input, _signal, _onUpdate, ctx) {
@@ -2846,7 +2858,8 @@ var PI_PACKAGES = [
2846
2858
  ];
2847
2859
  var PI_SKILL_SOURCES = [
2848
2860
  { repository: "arabold/docs-mcp-server", skills: ["docs-manage", "docs-search", "fetch-url"] },
2849
- { repository: "AminBlg/SimpleEnglish", skills: ["simple-english"] }
2861
+ { repository: "AminBlg/SimpleEnglish", skills: ["simple-english"] },
2862
+ { repository: "cloudvoyant/codevoyant", skills: ["git"] }
2850
2863
  ];
2851
2864
  var MCP_ADAPTER_PACKAGE = "npm:pi-mcp-adapter";
2852
2865
  var BUNDLED_AGENTS_DIR = resolveBundledAgentsDir();
@@ -3094,7 +3107,8 @@ ${source}`;
3094
3107
  }
3095
3108
  async function gitlabMcpHost(projectDir) {
3096
3109
  try {
3097
- return (await detectVcs(projectDir)).host || "gitlab.com";
3110
+ const vcs = await detectVcs(projectDir);
3111
+ return vcs.provider === "gitlab" && vcs.host ? vcs.host : "gitlab.com";
3098
3112
  } catch {
3099
3113
  return "gitlab.com";
3100
3114
  }
@@ -1,5 +1,6 @@
1
1
  import { type ToolDefinition } from '@earendil-works/pi-coding-agent';
2
- import { type ReviewThreadRecord } from '../review';
2
+ import { type ReviewComment, type ReviewThreadRecord } from '../review';
3
+ export declare function hasReviewDraft(comments: readonly ReviewComment[], body: string): boolean;
3
4
  export declare function workingTreeDiff(cwd: string): Promise<string>;
4
5
  export declare function createReviewTools(): readonly ToolDefinition[];
5
6
  export declare function syncLocalThreadArtifact(path: string, title: string, sessionSlug: string, threads: readonly ReviewThreadRecord[]): Promise<ReviewThreadRecord[]>;
@@ -1 +1 @@
1
- {"version":3,"file":"review.d.ts","sourceRoot":"","sources":["../../src/tools/review.ts"],"names":[],"mappings":"AAGA,OAAO,EAAqC,KAAK,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAMzG,OAAO,EAuBL,KAAK,kBAAkB,EACxB,MAAM,WAAW,CAAC;AA+EnB,wBAAsB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAalE;AAID,wBAAgB,iBAAiB,IAAI,SAAS,cAAc,EAAE,CA+a7D;AAyKD,wBAAsB,uBAAuB,CAC3C,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,SAAS,kBAAkB,EAAE,GACrC,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAwB/B"}
1
+ {"version":3,"file":"review.d.ts","sourceRoot":"","sources":["../../src/tools/review.ts"],"names":[],"mappings":"AAGA,OAAO,EAAqC,KAAK,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAMzG,OAAO,EAsBL,KAAK,aAAa,EAClB,KAAK,kBAAkB,EACxB,MAAM,WAAW,CAAC;AA+EnB,wBAAgB,cAAc,CAAC,QAAQ,EAAE,SAAS,aAAa,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAExF;AAED,wBAAsB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAalE;AAID,wBAAgB,iBAAiB,IAAI,SAAS,cAAc,EAAE,CAib7D;AAyKD,wBAAsB,uBAAuB,CAC3C,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,SAAS,kBAAkB,EAAE,GACrC,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAwB/B"}
package/dist/zed.d.ts CHANGED
@@ -6,6 +6,6 @@ export interface ZedEnsureResult {
6
6
  }
7
7
  export declare function zedTasksPath(homeDir?: string): string;
8
8
  export declare function zedKeymapPath(homeDir?: string): string;
9
- export declare function ensureZedReviewTask(homeDir?: string): Promise<ZedEnsureResult>;
9
+ export declare function ensureZedReviewTask(homeDir?: string, command?: readonly string[]): Promise<ZedEnsureResult>;
10
10
  export declare function ensureZedReviewKeybinding(homeDir?: string): Promise<ZedEnsureResult>;
11
11
  //# sourceMappingURL=zed.d.ts.map
package/dist/zed.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"zed.d.ts","sourceRoot":"","sources":["../src/zed.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,oBAAoB,yBAAyB,CAAC;AAmB3D,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;CAClB;AAWD,wBAAgB,YAAY,CAAC,OAAO,SAAY,GAAG,MAAM,CAExD;AAED,wBAAgB,aAAa,CAAC,OAAO,SAAY,GAAG,MAAM,CAEzD;AAED,wBAAsB,mBAAmB,CAAC,OAAO,SAAY,GAAG,OAAO,CAAC,eAAe,CAAC,CAWvF;AAED,wBAAsB,yBAAyB,CAAC,OAAO,SAAY,GAAG,OAAO,CAAC,eAAe,CAAC,CAgB7F"}
1
+ {"version":3,"file":"zed.d.ts","sourceRoot":"","sources":["../src/zed.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,oBAAoB,yBAAyB,CAAC;AAmB3D,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;CAClB;AAgBD,wBAAgB,YAAY,CAAC,OAAO,SAAY,GAAG,MAAM,CAExD;AAED,wBAAgB,aAAa,CAAC,OAAO,SAAY,GAAG,MAAM,CAEzD;AAED,wBAAsB,mBAAmB,CACvC,OAAO,SAAY,EACnB,OAAO,GAAE,SAAS,MAAM,EAAc,GACrC,OAAO,CAAC,eAAe,CAAC,CAY1B;AAED,wBAAsB,yBAAyB,CAAC,OAAO,SAAY,GAAG,OAAO,CAAC,eAAe,CAAC,CAgB7F"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@difflab/pi",
3
- "version": "0.2.0-rc.202609191250.90d35bd",
3
+ "version": "0.2.0-rc.202609191441.52cd715",
4
4
  "description": "Tools and skills for the pi coding agent",
5
5
  "repository": {
6
6
  "type": "git",
@@ -5,7 +5,7 @@
5
5
  3. Call `review_comments`. With `--local`, it synchronizes every comment in the selected tuicr session to `.diffpi/review/<session-slug>.md`. Existing replies and statuses remain; source comments removed by the user become resolved ledger entries.
6
6
  4. Classify every thread, group justified code changes into bounded non-overlapping tasks, and delegate those tasks to lightweight worker agents. Keep questions, outcome decisions, integration, and response text in the reviewer coordinator. With `--local`, workers modify the current working tree without committing. Without `--local`, apply a fix only when the comment requests a change.
7
7
  5. Respond to every thread with its outcome. Answer questions and leave them open. For substantive requests, state what changed and whether it is resolved, but leave the thread open for reviewer confirmation. For a trivial request, reply `Resolved`; remote threads may close, while the user must delete the local tuicr source comment before the next sync marks it resolved.
8
- 6. When a remote address flow changes code, invoke the bundled `/git commit --no-push` workflow after checks pass and before drafting responses. Use `--atomic` when the fixes form separate logical commits.
8
+ 6. When a remote address flow changes code, invoke the upstream `/git commit --no-push` workflow after checks pass and before drafting responses. Use `--atomic` when the fixes form separate logical commits.
9
9
  7. Call `review_respond` for each thread. With `--local`, every response is posted to tuicr and recorded in the session ledger. Without `--local`, replies post to the forge immediately.
10
10
  8. With `--local`, call `review_launch_ui` with `local: true` after replies are recorded so the user can inspect the working-tree review in tuicr.
11
11
  9. Report fixed, committed, answered, addressed, resolved, and unresolved counts. Do not publish, complete, or merge in this workflow.
@@ -1,9 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) codevoyant contributors
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
-
7
- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
-
9
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -1,47 +0,0 @@
1
- ---
2
- name: git
3
- description: 'Git version control workflows: create conventional commits or run interactive rebase. Triggers on: "git commit", "git rebase", "git help", "create a commit", "conventional commit", "commit my changes", "interactive rebase", "squash commits".'
4
- license: MIT
5
- allowed-tools: ask_user_question
6
- compatibility: Works on Claude Code, OpenCode, GitHub Copilot (VS Code), and Codex. No platform-specific features used.
7
- ---
8
-
9
- # git
10
-
11
- Git workflow skill dispatcher.
12
-
13
- ## Critical Rules
14
-
15
- - **Never execute workflow logic here** — this file only parses args and dispatches
16
- - **Step 0 always runs first** — no exceptions
17
- - **Unknown verb → run `help.md`** — never error silently
18
- - **Pass all remaining args through** — workflow receives `$REMAINING_ARGS` unchanged
19
- - **Markdown output: soft-wrap prose, never hard-wrap** — when writing markdown, write each paragraph as one continuous line; do not insert manual newlines to wrap prose at a fixed column width. Newlines still separate paragraphs, list items, headings, and code fences.
20
-
21
- ## Step 0: Parse Arguments
22
-
23
- The raw invocation args (filled by Claude Code / OpenCode slash commands): `$ARGUMENTS`. If this line is not filled in, read the verb and remaining args from the user's current message.
24
-
25
- ```bash
26
- VERB="[first non-flag argument, or empty]"
27
- REMAINING_ARGS="[everything after VERB, preserving order and flags]"
28
-
29
- case "$VERB" in
30
- "") VERB="help" ;;
31
- esac
32
- ```
33
-
34
- ## Step 1: Dispatch to Workflow
35
-
36
- Read and execute `references/workflows/{VERB}.md`, passing `$REMAINING_ARGS` as the argument string.
37
-
38
- If `references/workflows/{VERB}.md` does not exist, fall back to `references/workflows/help.md` and note the unknown verb.
39
-
40
- ## Workflow Index
41
-
42
- - **commit** (`references/workflows/commit.md`) — create a conventional commit with optional CI monitoring
43
- - **exclude** (`references/workflows/exclude.md`) — hide files/dirs from git via `.git/info/exclude` (no `.gitignore` change)
44
- - **hooks** (`references/workflows/hooks.md`) — install/manage the commit-msg hook that strips agent self-attribution
45
- - **help** (`references/workflows/help.md`) — print command reference
46
- - **rebase** (`references/workflows/rebase.md`) — interactive rebase helper
47
- - **worktree** (`references/workflows/worktree.md`) — create/switch a branch and/or create a worktree (shared routine reused by spec)
@@ -1,46 +0,0 @@
1
- #!/usr/bin/env bash
2
- #
3
- # codevoyant git skill — commit-msg hook
4
- # Strips agent self-attribution from EVERY commit message, no matter which
5
- # tool or agent created the commit. This is the robust backstop for the rule
6
- # in skills/git/references/workflows/commit.md: commits must never carry
7
- # "Co-Authored-By: <agent>", "Generated with <agent>", or a 🤖 line.
8
- #
9
- # Install with: /git hooks install (or copy to .git/hooks/commit-msg, chmod +x)
10
- #
11
- set -euo pipefail
12
-
13
- MSG_FILE="${1:-}"
14
- [ -n "$MSG_FILE" ] && [ -f "$MSG_FILE" ] || exit 0
15
-
16
- # Identifiers that mark a trailer/line as AGENT self-attribution (case-insensitive).
17
- # Human co-authors are preserved — only lines naming a coding agent are removed.
18
- AGENT='claude|anthropic|noreply@anthropic\.com|chatgpt|openai|gpt-[0-9]|copilot|codex|cursor|devin|sourcegraph|amp code|ai assistant|ai agent'
19
-
20
- before="$(cat "$MSG_FILE")"
21
- tmp="$(mktemp)"
22
-
23
- # 1. Drop "Co-authored-by:" trailers that name an agent.
24
- # 2. Drop "Generated with/by <agent>" (and "Generated with Claude Code") lines.
25
- # 3. Drop any line containing the robot emoji.
26
- grep -ivaE "^[[:space:]]*Co-authored-by:.*(${AGENT})" "$MSG_FILE" \
27
- | grep -ivaE "generated (with|by).*(${AGENT}|claude code)" \
28
- | grep -vaF '🤖' \
29
- > "$tmp" || true
30
-
31
- # Did the filters actually remove an attribution line? (Compare before the
32
- # cosmetic whitespace trim, so blank-line tidying alone never trips the notice.)
33
- stripped="$(cat "$tmp")"
34
-
35
- # Collapse any trailing blank lines the removals left behind.
36
- awk '{ lines[NR]=$0 }
37
- END { last=NR
38
- while (last>0 && lines[last] ~ /^[[:space:]]*$/) last--
39
- for (i=1;i<=last;i++) print lines[i] }' "$tmp" > "$MSG_FILE"
40
- rm -f "$tmp"
41
-
42
- if [ "$before" != "$stripped" ]; then
43
- echo "git hook: stripped agent self-attribution from commit message" >&2
44
- fi
45
-
46
- exit 0