@difflab/pi 0.2.0-rc.202609191250.90d35bd → 0.2.0-rc.202609191446.bc3a2e9

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,
@@ -15582,10 +15589,14 @@ function projectSlug(repository) {
15582
15589
  // src/review/review-state.ts
15583
15590
  var reviewPublicationStateSchema = z6.object({
15584
15591
  target: z6.string().optional(),
15592
+ bodies: z6.array(z6.string()).default([]),
15585
15593
  comments: z6.array(z6.string()).default([]),
15586
15594
  replies: z6.array(z6.string()).default([]),
15587
15595
  overlayPath: z6.string().optional()
15588
15596
  });
15597
+ function reviewBodyFingerprint(body) {
15598
+ return digest(body);
15599
+ }
15589
15600
  function reviewCommentFingerprint(comment) {
15590
15601
  return digest([comment.file, String(comment.line), comment.side ?? "RIGHT", comment.body].join("\x00"));
15591
15602
  }
@@ -15603,7 +15614,7 @@ async function loadReviewPublicationState(cwd, vcs, number, homeDir) {
15603
15614
  return { path, state: { ...parsed, target } };
15604
15615
  } catch (error) {
15605
15616
  if (error.code === "ENOENT") {
15606
- return { path, state: { target, comments: [], replies: [] } };
15617
+ return { path, state: { target, bodies: [], comments: [], replies: [] } };
15607
15618
  }
15608
15619
  if (error instanceof SyntaxError || error instanceof z6.ZodError) {
15609
15620
  throw new Error(`Cannot parse review publication state: ${path}`);
@@ -15706,6 +15717,9 @@ function modelRoute(ctx) {
15706
15717
  throw new Error("Cannot record review provenance because Pi did not provide an active model route.");
15707
15718
  return `${ctx.model.provider}/${ctx.model.id}`;
15708
15719
  }
15720
+ function hasReviewDraft(comments, body) {
15721
+ return comments.length > 0 || body.trim().length > 0;
15722
+ }
15709
15723
  async function workingTreeDiff(cwd) {
15710
15724
  const tracked = await run("git", ["-C", cwd, "diff", "HEAD"], { capture: "unbounded" });
15711
15725
  if (tracked.code !== 0)
@@ -15890,6 +15904,8 @@ ${openMessage}`, {
15890
15904
  notVerified: params.notVerified ?? []
15891
15905
  }), "utf8");
15892
15906
  const comments = toReviewComments(findings, useLocalBackend ? undefined : model);
15907
+ const body = (params.overallIssues ?? []).join(`
15908
+ `);
15893
15909
  if (useLocalBackend) {
15894
15910
  const session = await resolveTuicrSession(review, Boolean(params.local || params.workingTree));
15895
15911
  if (!session) {
@@ -15903,16 +15919,16 @@ ${openMessage}`, {
15903
15919
  artifactPath: artifact,
15904
15920
  author: localReviewAuthor(model)
15905
15921
  });
15906
- await backend.stage({ comments, body: (params.overallIssues ?? []).join(`
15907
- `) });
15922
+ await backend.stage({ comments, body });
15908
15923
  return result(`Local review staged in tuicr: ${artifact}`, { artifact, count: findings.length, session });
15909
15924
  }
15910
15925
  if (!review.pr)
15911
15926
  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: "" });
15927
+ const hasDraft = hasReviewDraft(comments, body);
15928
+ if (hasDraft) {
15929
+ await createRemoteReviewBackend(review.vcs, review.pr.number).stage({ comments, body });
15914
15930
  }
15915
- return result(`${comments.length > 0 ? "Pending review staged" : "Clean review recorded"} on #${review.pr.number}. Artifact: ${artifact}`, {
15931
+ return result(`${hasDraft ? "Pending review staged" : "Clean review recorded"} on #${review.pr.number}. Artifact: ${artifact}`, {
15916
15932
  artifact,
15917
15933
  pr: review.pr,
15918
15934
  count: findings.length
@@ -16069,7 +16085,7 @@ ${params.body}`, {
16069
16085
  label: "review complete",
16070
16086
  description: "Approve, reject, or abandon a remote review, or archive a local review artifact.",
16071
16087
  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."],
16088
+ promptGuidelines: ["A local completion archives the Diffpi artifact and deletes the matching tuicr session."],
16073
16089
  parameters: parameters5(completeSchema),
16074
16090
  executionMode: "sequential",
16075
16091
  async execute(_id, input, _signal, _onUpdate, ctx) {
@@ -16147,6 +16163,9 @@ async function publishResolvedReview(review, params, model) {
16147
16163
  await review.forge.markReady(review.pr.number);
16148
16164
  await remote.publish(event);
16149
16165
  if (promotion) {
16166
+ promotion.publication.state.bodies = [
16167
+ ...new Set([...promotion.publication.state.bodies, ...promotion.bodyFingerprints])
16168
+ ];
16150
16169
  promotion.publication.state.comments = [
16151
16170
  ...new Set([...promotion.publication.state.comments, ...promotion.commentFingerprints])
16152
16171
  ];
@@ -16155,9 +16174,10 @@ async function publishResolvedReview(review, params, model) {
16155
16174
  if (status === "CLOSE")
16156
16175
  await review.forge.closePr(review.pr.number);
16157
16176
  const finalPr = await review.forge.viewPr(String(review.pr.number));
16177
+ const promotedBodies = promotion?.promotedBodies ?? 0;
16158
16178
  const promotedComments = promotion?.promotedComments ?? 0;
16159
16179
  const promotedReplies = promotion?.promotedReplies ?? 0;
16160
- return result(`Published #${review.pr.number} (${status}); promoted ${promotedComments} comments and ${promotedReplies} replies.`, { pr: finalPr ?? review.pr, status, promotedComments, promotedReplies });
16180
+ return result(`Published #${review.pr.number} (${status}); promoted ${promotedBodies} review bodies, ${promotedComments} comments, and ${promotedReplies} replies.`, { pr: finalPr ?? review.pr, status, promotedBodies, promotedComments, promotedReplies });
16161
16181
  }
16162
16182
  async function promoteLocalReview(review, remote, model, workingTree) {
16163
16183
  const publication = await loadReviewPublicationState(review.cwd, review.vcs, review.pr.number);
@@ -16174,16 +16194,24 @@ async function promoteLocalReview(review, remote, model, workingTree) {
16174
16194
  ...comment,
16175
16195
  body: withRemoteProvenance(comment.body, comment.author?.replace(/^Agent:\s*/, "") || model)
16176
16196
  }));
16197
+ const body = draft.body.trim() ? withRemoteProvenance(draft.body, model) : "";
16198
+ const bodyFingerprints = body ? [reviewBodyFingerprint(body)] : [];
16177
16199
  const commentFingerprints = comments.map(reviewCommentFingerprint);
16178
16200
  const remoteDraft = await remote.readDraft();
16201
+ const knownBodies = new Set(publication.state.bodies);
16202
+ if (remoteDraft.body.trim())
16203
+ knownBodies.add(reviewBodyFingerprint(remoteDraft.body));
16204
+ const unpublishedBody = body && !knownBodies.has(reviewBodyFingerprint(body)) ? body : "";
16179
16205
  const known = new Set([...publication.state.comments, ...remoteDraft.comments.map(reviewCommentFingerprint)]);
16180
16206
  const unpublished = unpublishedReviewComments(comments, known);
16181
- if (unpublished.length > 0)
16182
- await remote.stage({ comments: unpublished, body: "" });
16207
+ if (unpublished.length > 0 || unpublishedBody)
16208
+ await remote.stage({ comments: unpublished, body: unpublishedBody });
16183
16209
  const promotedReplies = await promoteLocalReplies(review, remote, publication, model, workingTree);
16184
16210
  return {
16185
16211
  publication,
16212
+ bodyFingerprints,
16186
16213
  commentFingerprints,
16214
+ promotedBodies: unpublishedBody ? 1 : 0,
16187
16215
  promotedComments: unpublished.length,
16188
16216
  promotedReplies
16189
16217
  };
@@ -16558,7 +16586,8 @@ var PI_PACKAGES = [
16558
16586
  ];
16559
16587
  var PI_SKILL_SOURCES = [
16560
16588
  { repository: "arabold/docs-mcp-server", skills: ["docs-manage", "docs-search", "fetch-url"] },
16561
- { repository: "AminBlg/SimpleEnglish", skills: ["simple-english"] }
16589
+ { repository: "AminBlg/SimpleEnglish", skills: ["simple-english"] },
16590
+ { repository: "cloudvoyant/codevoyant", skills: ["git"] }
16562
16591
  ];
16563
16592
  var MCP_ADAPTER_PACKAGE = "npm:pi-mcp-adapter";
16564
16593
  var BUNDLED_AGENTS_DIR2 = resolveBundledAgentsDir();
@@ -16806,7 +16835,8 @@ ${source}`;
16806
16835
  }
16807
16836
  async function gitlabMcpHost(projectDir) {
16808
16837
  try {
16809
- return (await detectVcs(projectDir)).host || "gitlab.com";
16838
+ const vcs = await detectVcs(projectDir);
16839
+ return vcs.provider === "gitlab" && vcs.host ? vcs.host : "gitlab.com";
16810
16840
  } catch {
16811
16841
  return "gitlab.com";
16812
16842
  }
package/dist/index.d.ts CHANGED
@@ -8,7 +8,7 @@ export { mcp } from './mcp';
8
8
  export { mise } from './extensions/misex';
9
9
  export { createModeController, discoverAgentModes, resolveAgentMode } from './modes';
10
10
  export { pi } from './pi';
11
- export { loadReviewPublicationState, reviewCommentFingerprint, reviewReplyFingerprint, saveReviewPublicationState, unpublishedReviewComments, } from './review';
11
+ export { loadReviewPublicationState, reviewBodyFingerprint, reviewCommentFingerprint, reviewReplyFingerprint, saveReviewPublicationState, unpublishedReviewComments, } from './review';
12
12
  export { computeProjectSlug, ensureStore, reviewsDir, sessionsDir, storeDir, storeGlobalRoot } from './store';
13
13
  export { addComment, launch, listSessions, readSession, resolvePrSession, resolveReviewSession, resolveSession, toFindings, tuicrAvailable, } from './tuicr';
14
14
  export { ensureZedReviewKeybinding, ensureZedReviewTask, zedKeymapPath, zedTasksPath, ZED_REVIEW_TASK_NAME, } from './zed';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
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,
@@ -1695,10 +1702,14 @@ function projectSlug(repository) {
1695
1702
  // src/review/review-state.ts
1696
1703
  var reviewPublicationStateSchema = z4.object({
1697
1704
  target: z4.string().optional(),
1705
+ bodies: z4.array(z4.string()).default([]),
1698
1706
  comments: z4.array(z4.string()).default([]),
1699
1707
  replies: z4.array(z4.string()).default([]),
1700
1708
  overlayPath: z4.string().optional()
1701
1709
  });
1710
+ function reviewBodyFingerprint(body) {
1711
+ return digest(body);
1712
+ }
1702
1713
  function reviewCommentFingerprint(comment) {
1703
1714
  return digest([comment.file, String(comment.line), comment.side ?? "RIGHT", comment.body].join("\x00"));
1704
1715
  }
@@ -1716,7 +1727,7 @@ async function loadReviewPublicationState(cwd, vcs, number, homeDir) {
1716
1727
  return { path, state: { ...parsed, target } };
1717
1728
  } catch (error) {
1718
1729
  if (error.code === "ENOENT") {
1719
- return { path, state: { target, comments: [], replies: [] } };
1730
+ return { path, state: { target, bodies: [], comments: [], replies: [] } };
1720
1731
  }
1721
1732
  if (error instanceof SyntaxError || error instanceof z4.ZodError) {
1722
1733
  throw new Error(`Cannot parse review publication state: ${path}`);
@@ -2191,7 +2202,8 @@ var PI_PACKAGES = [
2191
2202
  ];
2192
2203
  var PI_SKILL_SOURCES = [
2193
2204
  { repository: "arabold/docs-mcp-server", skills: ["docs-manage", "docs-search", "fetch-url"] },
2194
- { repository: "AminBlg/SimpleEnglish", skills: ["simple-english"] }
2205
+ { repository: "AminBlg/SimpleEnglish", skills: ["simple-english"] },
2206
+ { repository: "cloudvoyant/codevoyant", skills: ["git"] }
2195
2207
  ];
2196
2208
  var MCP_ADAPTER_PACKAGE = "npm:pi-mcp-adapter";
2197
2209
  var BUNDLED_AGENTS_DIR2 = resolveBundledAgentsDir();
@@ -2439,7 +2451,8 @@ ${source}`;
2439
2451
  }
2440
2452
  async function gitlabMcpHost(projectDir) {
2441
2453
  try {
2442
- return (await detectVcs(projectDir)).host || "gitlab.com";
2454
+ const vcs = await detectVcs(projectDir);
2455
+ return vcs.provider === "gitlab" && vcs.host ? vcs.host : "gitlab.com";
2443
2456
  } catch {
2444
2457
  return "gitlab.com";
2445
2458
  }
@@ -2564,6 +2577,7 @@ export {
2564
2577
  resolvePrSession,
2565
2578
  resolveReviewSession,
2566
2579
  resolveSession,
2580
+ reviewBodyFingerprint,
2567
2581
  reviewCommentFingerprint,
2568
2582
  reviewRecordName,
2569
2583
  reviewReplyFingerprint,
@@ -3,6 +3,6 @@ export type { Finding, LocalReviewBackendOptions, ReviewBackend, ReviewComment,
3
3
  export { parseThreadArtifact, renderReviewDoc, renderThreadArtifact, upsertThreadReply } from './review-markdown';
4
4
  export { assertReviewEventSupported, createLocalReviewBackend, createRemoteReviewBackend, githubReviewSubmissionEndpoint, hasGitlabDraftNotes, parseGitlabDiffRefs, } from './review-backend';
5
5
  export type { GitlabDiffRefs } from './review-backend';
6
- export { loadReviewPublicationState, reviewCommentFingerprint, reviewReplyFingerprint, saveReviewPublicationState, unpublishedReviewComments, } from './review-state';
6
+ export { loadReviewPublicationState, reviewBodyFingerprint, reviewCommentFingerprint, reviewReplyFingerprint, saveReviewPublicationState, unpublishedReviewComments, } from './review-state';
7
7
  export type { ReviewPublicationState } from './review-state';
8
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/review/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,aAAa,EACb,cAAc,EACd,eAAe,EACf,mBAAmB,EACnB,iBAAiB,EACjB,gBAAgB,EAChB,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,oBAAoB,EACpB,MAAM,GACP,MAAM,SAAS,CAAC;AACjB,YAAY,EACV,OAAO,EACP,yBAAyB,EACzB,aAAa,EACb,aAAa,EACb,cAAc,EACd,WAAW,EACX,WAAW,EACX,WAAW,EACX,UAAU,EACV,2BAA2B,EAC3B,kBAAkB,EAClB,QAAQ,GACT,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAClH,OAAO,EACL,0BAA0B,EAC1B,wBAAwB,EACxB,yBAAyB,EACzB,8BAA8B,EAC9B,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EACL,0BAA0B,EAC1B,wBAAwB,EACxB,sBAAsB,EACtB,0BAA0B,EAC1B,yBAAyB,GAC1B,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/review/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,aAAa,EACb,cAAc,EACd,eAAe,EACf,mBAAmB,EACnB,iBAAiB,EACjB,gBAAgB,EAChB,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,oBAAoB,EACpB,MAAM,GACP,MAAM,SAAS,CAAC;AACjB,YAAY,EACV,OAAO,EACP,yBAAyB,EACzB,aAAa,EACb,aAAa,EACb,cAAc,EACd,WAAW,EACX,WAAW,EACX,WAAW,EACX,UAAU,EACV,2BAA2B,EAC3B,kBAAkB,EAClB,QAAQ,GACT,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAClH,OAAO,EACL,0BAA0B,EAC1B,wBAAwB,EACxB,yBAAyB,EACzB,8BAA8B,EAC9B,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EACL,0BAA0B,EAC1B,qBAAqB,EACrB,wBAAwB,EACxB,sBAAsB,EACtB,0BAA0B,EAC1B,yBAAyB,GAC1B,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC"}
@@ -2,10 +2,12 @@ import type { VcsInfo } from '../environment';
2
2
  import type { ReviewComment } from './types';
3
3
  export interface ReviewPublicationState {
4
4
  target: string;
5
+ bodies: string[];
5
6
  comments: string[];
6
7
  replies: string[];
7
8
  overlayPath?: string;
8
9
  }
10
+ export declare function reviewBodyFingerprint(body: string): string;
9
11
  export declare function reviewCommentFingerprint(comment: ReviewComment): string;
10
12
  export declare function reviewReplyFingerprint(threadId: string, body: string): string;
11
13
  export declare function unpublishedReviewComments(comments: readonly ReviewComment[], knownFingerprints: ReadonlySet<string>): ReviewComment[];
@@ -1 +1 @@
1
- {"version":3,"file":"review-state.d.ts","sourceRoot":"","sources":["../../src/review/review-state.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAU7C,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,CAEvE;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAE7E;AAED,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,SAAS,aAAa,EAAE,EAClC,iBAAiB,EAAE,WAAW,CAAC,MAAM,CAAC,GACrC,aAAa,EAAE,CAEjB;AAED,wBAAsB,0BAA0B,CAC9C,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,OAAO,EACZ,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,sBAAsB,CAAA;CAAE,CAAC,CAe1D;AAED,wBAAsB,0BAA0B,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAI3G"}
1
+ {"version":3,"file":"review-state.d.ts","sourceRoot":"","sources":["../../src/review/review-state.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAW7C,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,CAEvE;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAE7E;AAED,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,SAAS,aAAa,EAAE,EAClC,iBAAiB,EAAE,WAAW,CAAC,MAAM,CAAC,GACrC,aAAa,EAAE,CAEjB;AAED,wBAAsB,0BAA0B,CAC9C,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,OAAO,EACZ,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,sBAAsB,CAAA;CAAE,CAAC,CAe1D;AAED,wBAAsB,0BAA0B,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAI3G"}
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,
@@ -1764,10 +1771,14 @@ function projectSlug(repository) {
1764
1771
  // src/review/review-state.ts
1765
1772
  var reviewPublicationStateSchema = z5.object({
1766
1773
  target: z5.string().optional(),
1774
+ bodies: z5.array(z5.string()).default([]),
1767
1775
  comments: z5.array(z5.string()).default([]),
1768
1776
  replies: z5.array(z5.string()).default([]),
1769
1777
  overlayPath: z5.string().optional()
1770
1778
  });
1779
+ function reviewBodyFingerprint(body) {
1780
+ return digest(body);
1781
+ }
1771
1782
  function reviewCommentFingerprint(comment) {
1772
1783
  return digest([comment.file, String(comment.line), comment.side ?? "RIGHT", comment.body].join("\x00"));
1773
1784
  }
@@ -1785,7 +1796,7 @@ async function loadReviewPublicationState(cwd, vcs, number, homeDir) {
1785
1796
  return { path, state: { ...parsed, target } };
1786
1797
  } catch (error) {
1787
1798
  if (error.code === "ENOENT") {
1788
- return { path, state: { target, comments: [], replies: [] } };
1799
+ return { path, state: { target, bodies: [], comments: [], replies: [] } };
1789
1800
  }
1790
1801
  if (error instanceof SyntaxError || error instanceof z5.ZodError) {
1791
1802
  throw new Error(`Cannot parse review publication state: ${path}`);
@@ -1906,6 +1917,9 @@ function modelRoute(ctx) {
1906
1917
  throw new Error("Cannot record review provenance because Pi did not provide an active model route.");
1907
1918
  return `${ctx.model.provider}/${ctx.model.id}`;
1908
1919
  }
1920
+ function hasReviewDraft(comments, body) {
1921
+ return comments.length > 0 || body.trim().length > 0;
1922
+ }
1909
1923
  async function workingTreeDiff(cwd) {
1910
1924
  const tracked = await run("git", ["-C", cwd, "diff", "HEAD"], { capture: "unbounded" });
1911
1925
  if (tracked.code !== 0)
@@ -2090,6 +2104,8 @@ ${openMessage}`, {
2090
2104
  notVerified: params.notVerified ?? []
2091
2105
  }), "utf8");
2092
2106
  const comments = toReviewComments(findings, useLocalBackend ? undefined : model);
2107
+ const body = (params.overallIssues ?? []).join(`
2108
+ `);
2093
2109
  if (useLocalBackend) {
2094
2110
  const session = await resolveTuicrSession(review, Boolean(params.local || params.workingTree));
2095
2111
  if (!session) {
@@ -2103,16 +2119,16 @@ ${openMessage}`, {
2103
2119
  artifactPath: artifact,
2104
2120
  author: localReviewAuthor(model)
2105
2121
  });
2106
- await backend.stage({ comments, body: (params.overallIssues ?? []).join(`
2107
- `) });
2122
+ await backend.stage({ comments, body });
2108
2123
  return result(`Local review staged in tuicr: ${artifact}`, { artifact, count: findings.length, session });
2109
2124
  }
2110
2125
  if (!review.pr)
2111
2126
  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: "" });
2127
+ const hasDraft = hasReviewDraft(comments, body);
2128
+ if (hasDraft) {
2129
+ await createRemoteReviewBackend(review.vcs, review.pr.number).stage({ comments, body });
2114
2130
  }
2115
- return result(`${comments.length > 0 ? "Pending review staged" : "Clean review recorded"} on #${review.pr.number}. Artifact: ${artifact}`, {
2131
+ return result(`${hasDraft ? "Pending review staged" : "Clean review recorded"} on #${review.pr.number}. Artifact: ${artifact}`, {
2116
2132
  artifact,
2117
2133
  pr: review.pr,
2118
2134
  count: findings.length
@@ -2269,7 +2285,7 @@ ${params.body}`, {
2269
2285
  label: "review complete",
2270
2286
  description: "Approve, reject, or abandon a remote review, or archive a local review artifact.",
2271
2287
  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."],
2288
+ promptGuidelines: ["A local completion archives the Diffpi artifact and deletes the matching tuicr session."],
2273
2289
  parameters: parameters(completeSchema),
2274
2290
  executionMode: "sequential",
2275
2291
  async execute(_id, input, _signal, _onUpdate, ctx) {
@@ -2347,6 +2363,9 @@ async function publishResolvedReview(review, params, model) {
2347
2363
  await review.forge.markReady(review.pr.number);
2348
2364
  await remote.publish(event);
2349
2365
  if (promotion) {
2366
+ promotion.publication.state.bodies = [
2367
+ ...new Set([...promotion.publication.state.bodies, ...promotion.bodyFingerprints])
2368
+ ];
2350
2369
  promotion.publication.state.comments = [
2351
2370
  ...new Set([...promotion.publication.state.comments, ...promotion.commentFingerprints])
2352
2371
  ];
@@ -2355,9 +2374,10 @@ async function publishResolvedReview(review, params, model) {
2355
2374
  if (status === "CLOSE")
2356
2375
  await review.forge.closePr(review.pr.number);
2357
2376
  const finalPr = await review.forge.viewPr(String(review.pr.number));
2377
+ const promotedBodies = promotion?.promotedBodies ?? 0;
2358
2378
  const promotedComments = promotion?.promotedComments ?? 0;
2359
2379
  const promotedReplies = promotion?.promotedReplies ?? 0;
2360
- return result(`Published #${review.pr.number} (${status}); promoted ${promotedComments} comments and ${promotedReplies} replies.`, { pr: finalPr ?? review.pr, status, promotedComments, promotedReplies });
2380
+ return result(`Published #${review.pr.number} (${status}); promoted ${promotedBodies} review bodies, ${promotedComments} comments, and ${promotedReplies} replies.`, { pr: finalPr ?? review.pr, status, promotedBodies, promotedComments, promotedReplies });
2361
2381
  }
2362
2382
  async function promoteLocalReview(review, remote, model, workingTree) {
2363
2383
  const publication = await loadReviewPublicationState(review.cwd, review.vcs, review.pr.number);
@@ -2374,16 +2394,24 @@ async function promoteLocalReview(review, remote, model, workingTree) {
2374
2394
  ...comment,
2375
2395
  body: withRemoteProvenance(comment.body, comment.author?.replace(/^Agent:\s*/, "") || model)
2376
2396
  }));
2397
+ const body = draft.body.trim() ? withRemoteProvenance(draft.body, model) : "";
2398
+ const bodyFingerprints = body ? [reviewBodyFingerprint(body)] : [];
2377
2399
  const commentFingerprints = comments.map(reviewCommentFingerprint);
2378
2400
  const remoteDraft = await remote.readDraft();
2401
+ const knownBodies = new Set(publication.state.bodies);
2402
+ if (remoteDraft.body.trim())
2403
+ knownBodies.add(reviewBodyFingerprint(remoteDraft.body));
2404
+ const unpublishedBody = body && !knownBodies.has(reviewBodyFingerprint(body)) ? body : "";
2379
2405
  const known = new Set([...publication.state.comments, ...remoteDraft.comments.map(reviewCommentFingerprint)]);
2380
2406
  const unpublished = unpublishedReviewComments(comments, known);
2381
- if (unpublished.length > 0)
2382
- await remote.stage({ comments: unpublished, body: "" });
2407
+ if (unpublished.length > 0 || unpublishedBody)
2408
+ await remote.stage({ comments: unpublished, body: unpublishedBody });
2383
2409
  const promotedReplies = await promoteLocalReplies(review, remote, publication, model, workingTree);
2384
2410
  return {
2385
2411
  publication,
2412
+ bodyFingerprints,
2386
2413
  commentFingerprints,
2414
+ promotedBodies: unpublishedBody ? 1 : 0,
2387
2415
  promotedComments: unpublished.length,
2388
2416
  promotedReplies
2389
2417
  };
@@ -2846,7 +2874,8 @@ var PI_PACKAGES = [
2846
2874
  ];
2847
2875
  var PI_SKILL_SOURCES = [
2848
2876
  { repository: "arabold/docs-mcp-server", skills: ["docs-manage", "docs-search", "fetch-url"] },
2849
- { repository: "AminBlg/SimpleEnglish", skills: ["simple-english"] }
2877
+ { repository: "AminBlg/SimpleEnglish", skills: ["simple-english"] },
2878
+ { repository: "cloudvoyant/codevoyant", skills: ["git"] }
2850
2879
  ];
2851
2880
  var MCP_ADAPTER_PACKAGE = "npm:pi-mcp-adapter";
2852
2881
  var BUNDLED_AGENTS_DIR = resolveBundledAgentsDir();
@@ -3094,7 +3123,8 @@ ${source}`;
3094
3123
  }
3095
3124
  async function gitlabMcpHost(projectDir) {
3096
3125
  try {
3097
- return (await detectVcs(projectDir)).host || "gitlab.com";
3126
+ const vcs = await detectVcs(projectDir);
3127
+ return vcs.provider === "gitlab" && vcs.host ? vcs.host : "gitlab.com";
3098
3128
  } catch {
3099
3129
  return "gitlab.com";
3100
3130
  }
@@ -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,EAuBL,KAAK,aAAa,EAClB,KAAK,kBAAkB,EACxB,MAAM,WAAW,CAAC;AAiFnB,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;AAoLD,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"}