@jwilger/pi-development-system 0.27.0 → 0.28.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jwilger/pi-development-system",
3
- "version": "0.27.0",
3
+ "version": "0.28.0",
4
4
  "description": "A pi extension package representing a seasoned approach to software development using a full AI SDLC.",
5
5
  "keywords": [
6
6
  "pi-package"
@@ -4,7 +4,7 @@ argument-hint: "[slice] [diffRange]"
4
4
  ---
5
5
  Review the work in progress with the code-review skill.
6
6
 
7
- 1. Call `devsys_review_start` (slice and diff range: $ARGUMENTS; omit either to use the active slice and `HEAD`).
7
+ 1. Call `devsys_review_start` (slice and diff range: $ARGUMENTS; omit either to use the active slice and `HEAD`; only the `HEAD` range clears the commit gate).
8
8
  2. Run the `agent_spawn` payload it returns, unchanged.
9
9
  3. Pass the reviewer's packet verbatim to `devsys_review_record` with the same `diffDigest` and `diffRange` the start reply gave.
10
10
  4. Report `review: N/R clean` and the next action. If the next action is `fix-findings`, list each blocking and should-fix finding with its `path:line`.
@@ -12,7 +12,8 @@ Review is a soft gate (`review.unsatisfied`): skipping it needs a recorded depar
12
12
  ## Run a round
13
13
 
14
14
  1. `devsys_review_start` (slice defaults to the active slice; `diffRange` defaults
15
- to `HEAD`: everything uncommitted, untracked files included). It computes the
15
+ to `HEAD`: everything uncommitted, untracked files included; the commit gate
16
+ checks that range, so use it for a review meant to clear a commit). It computes the
16
17
  diff digest, lets Jev choose lenses, and returns an exact `agent_spawn` payload.
17
18
  2. Run that `agent_spawn` unchanged. The reviewer is a top-level agent, not a
18
19
  child of this conversation, so it does not inherit your context.
@@ -70,12 +70,16 @@ export function splitDiffByFile(diff: string): Record<string, string> {
70
70
  const flush = () => {
71
71
  if (path !== undefined) out[path] = lines.join("\n");
72
72
  };
73
- for (const line of diff.split("\n")) {
73
+ // git ends its output with a newline: drop it so the last section digests like any other.
74
+ for (const line of diff.replace(/\n$/, "").split("\n")) {
74
75
  // Git quotes unusual paths ("b/na\303\257ve.md"); strip the quotes rather than lose the section.
75
- const header = /^diff --git "?a\/.*?"? "?b\/(.*?)"?$/.exec(line);
76
- if (header !== null) {
76
+ // Without renames both sides are the same path, which pins a path that contains " b/".
77
+ const header =
78
+ /^diff --git ("?)a\/(.*)\1 \1b\/\2\1$/.exec(line)?.[2] ??
79
+ /^diff --git "?a\/.*?"? "?b\/(.*?)"?$/.exec(line)?.[1];
80
+ if (header !== undefined) {
77
81
  flush();
78
- path = unquote(header[1] ?? line);
82
+ path = unquote(header);
79
83
  lines = [];
80
84
  }
81
85
  lines.push(line);
@@ -38,6 +38,9 @@ export type ReviewToolDeps = {
38
38
  now?: () => Date;
39
39
  };
40
40
 
41
+ const GATE_RANGE_NOTE =
42
+ "Note: the commit gate checks the uncommitted diff (range HEAD). A review of another range does not clear it; review the default range before committing.";
43
+
41
44
  const reply = (payload: string, isError = false) => ({
42
45
  content: [{ type: "text" as const, text: payload }],
43
46
  details: undefined,
@@ -123,7 +126,7 @@ export function createReviewStartTool(
123
126
  const action = nextAction(review, snap.value.digest);
124
127
  if (action === "done") {
125
128
  return reply(
126
- `${reviewLabel(review)}. Review of ${slice} is already satisfied on this diff; nothing to run.`,
129
+ `${reviewLabel(review)}. Review of ${slice} is already satisfied on this diff; nothing to run.${range === "HEAD" ? "" : ` ${GATE_RANGE_NOTE}`}`,
127
130
  );
128
131
  }
129
132
 
@@ -230,7 +233,8 @@ export function createReviewRecordTool(
230
233
  }
231
234
  const config = await loadConfig(ctx.cwd);
232
235
  if (!config.ok) return reply(`${CONFIG_FILE}: ${config.error.message}`, true);
233
- const snap = await snapshotDiff(deps.exec, ctx.cwd, params.diffRange?.trim() || "HEAD");
236
+ const range = params.diffRange?.trim() || "HEAD";
237
+ const snap = await snapshotDiff(deps.exec, ctx.cwd, range);
234
238
  if (!snap.ok) return reply(`cannot read the diff: ${snap.error}`, true);
235
239
 
236
240
  if (params.diffDigest !== snap.value.digest) {
@@ -295,6 +299,7 @@ export function createReviewRecordTool(
295
299
  reviewLabel(review),
296
300
  ...notes,
297
301
  `next: ${nextAction(review, reviewed.digest)}`,
302
+ ...(range === "HEAD" ? [] : [GATE_RANGE_NOTE]),
298
303
  ].join("\n"),
299
304
  );
300
305
  },