@jwilger/pi-development-system 0.27.1 → 0.29.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
package/prompts/devsys-review.md
CHANGED
|
@@ -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
|
|
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.
|
package/src/core/review-flow.ts
CHANGED
|
@@ -30,11 +30,16 @@ export type DiffNow = {
|
|
|
30
30
|
readonly files?: Readonly<Record<string, string>>;
|
|
31
31
|
};
|
|
32
32
|
|
|
33
|
+
// The departure log is the system's own record (devsys_record_departure writes it), not work under review.
|
|
34
|
+
const DECISION_LOG = /^docs\/decisions\/\d{4}-\d{2}\.md$/;
|
|
35
|
+
|
|
33
36
|
/** Every file in the current diff was in the last round's diff, byte for byte: a part of what was reviewed. */
|
|
34
37
|
const coveredByLastRound = (review: ReviewState, now: DiffNow): boolean => {
|
|
35
38
|
const reviewed = review.rounds.at(-1)?.files;
|
|
36
39
|
if (reviewed === undefined || now.files === undefined) return false;
|
|
37
|
-
return Object.entries(now.files).every(
|
|
40
|
+
return Object.entries(now.files).every(
|
|
41
|
+
([path, digest]) => reviewed[path] === digest || DECISION_LOG.test(path),
|
|
42
|
+
);
|
|
38
43
|
};
|
|
39
44
|
|
|
40
45
|
export function reviewGap(state: DevsysState, slice: SliceRef, now: DiffNow): string | undefined {
|
|
@@ -73,10 +78,13 @@ export function splitDiffByFile(diff: string): Record<string, string> {
|
|
|
73
78
|
// git ends its output with a newline: drop it so the last section digests like any other.
|
|
74
79
|
for (const line of diff.replace(/\n$/, "").split("\n")) {
|
|
75
80
|
// Git quotes unusual paths ("b/na\303\257ve.md"); strip the quotes rather than lose the section.
|
|
76
|
-
|
|
77
|
-
|
|
81
|
+
// Without renames both sides are the same path, which pins a path that contains " b/".
|
|
82
|
+
const header =
|
|
83
|
+
/^diff --git ("?)a\/(.*)\1 \1b\/\2\1$/.exec(line)?.[2] ??
|
|
84
|
+
/^diff --git "?a\/.*?"? "?b\/(.*?)"?$/.exec(line)?.[1];
|
|
85
|
+
if (header !== undefined) {
|
|
78
86
|
flush();
|
|
79
|
-
path = unquote(header
|
|
87
|
+
path = unquote(header);
|
|
80
88
|
lines = [];
|
|
81
89
|
}
|
|
82
90
|
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
|
|
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
|
},
|