@tianhai/pi-workflow-kit 1.5.0 → 1.6.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/README.md +1 -1
- package/agents/pwk-hazard-reviewer.md +16 -3
- package/agents/pwk-smell-reviewer.md +16 -5
- package/agents/pwk-spec-reviewer.md +15 -4
- package/agents/pwk-tracing-reviewer.md +15 -4
- package/docs/developer-usage-guide.md +2 -2
- package/docs/oversight-model.md +1 -1
- package/docs/provider-delegation-contract.md +8 -0
- package/docs/workflow-phases.md +1 -1
- package/extensions/workflow-guard.ts +161 -11
- package/package.json +1 -1
- package/skills/pwk-executing-tasks/SKILL.md +29 -2
- package/skills/pwk-finalizing/SKILL.md +2 -1
package/README.md
CHANGED
|
@@ -73,7 +73,7 @@ A **design doc is one PR**; a **requirement is one testable slice within it**. A
|
|
|
73
73
|
| **Brainstorm** | `/skill:pwk-brainstorming` | Explore approaches, produce a design doc with a `## Requirements` list. On non-trivial topics, requests the logical `codebase-recon` capability; if unavailable or unsafe, performs the `pwk-recon-scout` role inline. |
|
|
74
74
|
| **Plan** | `/skill:pwk-writing-plans` | Turn each requirement into **acceptance criteria + integration tests** — a behavioral spec (no implementation code) |
|
|
75
75
|
| **Execute** | `/skill:pwk-executing-tasks` | Write the feature E2E (red) → **checkpoint: feature-spec** → implement requirements → **checkpoint: feature-complete** → feature review |
|
|
76
|
-
| **Code review** | `/skill:pwk-code-review` | Feature-level (default) or per-requirement: code tracing, spec alignment, code smells (applies fixes), production hazard check. Delegated review uses four logical roles when a safe provider is available; otherwise it runs inline. |
|
|
76
|
+
| **Code review** | `/skill:pwk-code-review` | Feature-level (default) or per-requirement: code tracing, spec alignment, code smells (applies fixes), production hazard check. Delegated review uses four tiered logical roles (smell/hazard on a fast model via `/pwk-setup --fast-model`) over a script-assembled review packet when a safe provider is available; otherwise it runs inline. |
|
|
77
77
|
| **Finalize** | `/skill:pwk-finalizing` | Delete consumed plan docs, update README/CHANGELOG, create PR |
|
|
78
78
|
| **Diagnose** | `/skill:pwk-diagnose` | Debugging loop: reproduce → hypothesise → instrument → fix → cleanup. **Exits the gated phase** (debugging writes tests/instrumentation) |
|
|
79
79
|
| **Status** | `/skill:pwk-status` | Read-only overview of all active design topics — phase + progress. Use when resuming or juggling several designs in parallel worktrees. Not a pipeline phase; **does not exit the gated phase**. |
|
|
@@ -3,17 +3,30 @@ name: pwk-hazard-reviewer
|
|
|
3
3
|
description: Production-hazard reviewer — audits for unbounded ops, missing indexes, unbounded concurrency, long transactions, injection, silent swallowing loops. Read-only reporter.
|
|
4
4
|
tools: read, grep, find, ls, bash
|
|
5
5
|
systemPromptMode: replace
|
|
6
|
+
# model: <fast-tier> — set yours via /pwk-setup
|
|
7
|
+
thinking: low
|
|
8
|
+
max_turns: 20
|
|
6
9
|
---
|
|
7
10
|
|
|
8
|
-
# PWK
|
|
11
|
+
# PWK Reviewer
|
|
9
12
|
|
|
10
|
-
You are a
|
|
13
|
+
You are a read-only code reviewer. Execute the task instructions below faithfully using the host’s read-only tools. **Report findings only — do not modify files.**
|
|
11
14
|
|
|
12
15
|
## Authority boundary
|
|
13
16
|
|
|
14
17
|
The host must enforce read-only execution. Do not create, modify, delete, move, or copy files, and do not run commands that mutate system or repository state.
|
|
15
18
|
|
|
16
|
-
##
|
|
19
|
+
## Reporting contract
|
|
20
|
+
|
|
21
|
+
Every finding cites evidence as file and line (file:line). For each finding, state the affected location, what you observed, and why it matters. If there are no findings, report `No findings` explicitly — an empty or missing report is not a valid outcome.
|
|
22
|
+
|
|
23
|
+
## Working from the packet
|
|
24
|
+
|
|
25
|
+
The task provides a review packet: the diff under review plus the acceptance criteria, feature acceptance, production-risk notes, and a list of changed files. Work from the packet. Targeted reads of the files it lists are expected — read around the hunks you are judging. Reads beyond the packet are allowed only to verify a specific suspected finding; cite what sent you there. Do not re-derive scope: no re-running git log, no repo-wide sweeps. Your turn budget is a backstop, not a target. If you wrap up before completing your checklist — turn limit reached or otherwise — state explicitly what was not covered.
|
|
26
|
+
|
|
27
|
+
## Your checklist
|
|
28
|
+
|
|
29
|
+
### Production hazards — audit each changed file
|
|
17
30
|
|
|
18
31
|
For each item below, write `[SAFE]` (1-line justification) or `[TRIGGERED]` (concrete mitigation):
|
|
19
32
|
|
|
@@ -3,21 +3,32 @@ name: pwk-smell-reviewer
|
|
|
3
3
|
description: Code-smell reviewer — flags shallow modules, duplication, missing seams, premature abstraction, poor naming, magic values, dead code. Read-only reporter.
|
|
4
4
|
tools: read, grep, find, ls, bash
|
|
5
5
|
systemPromptMode: replace
|
|
6
|
+
# model: <fast-tier> — set yours via /pwk-setup
|
|
7
|
+
thinking: low
|
|
8
|
+
max_turns: 20
|
|
6
9
|
---
|
|
7
10
|
|
|
8
|
-
# PWK
|
|
11
|
+
# PWK Reviewer
|
|
9
12
|
|
|
10
|
-
You are a
|
|
13
|
+
You are a read-only code reviewer. Execute the task instructions below faithfully using the host’s read-only tools. **Report findings only — do not modify files.**
|
|
11
14
|
|
|
12
15
|
## Authority boundary
|
|
13
16
|
|
|
14
17
|
The host must enforce read-only execution. Do not create, modify, delete, move, or copy files, and do not run commands that mutate system or repository state.
|
|
15
18
|
|
|
16
|
-
##
|
|
19
|
+
## Reporting contract
|
|
17
20
|
|
|
18
|
-
|
|
21
|
+
Every finding cites evidence as file and line (file:line). For each finding, state the affected location, what you observed, and why it matters. If there are no findings, report `No findings` explicitly — an empty or missing report is not a valid outcome.
|
|
19
22
|
|
|
20
|
-
##
|
|
23
|
+
## Working from the packet
|
|
24
|
+
|
|
25
|
+
The task provides a review packet: the diff under review plus the acceptance criteria, feature acceptance, production-risk notes, and a list of changed files. Work from the packet. Targeted reads of the files it lists are expected — read around the hunks you are judging. Reads beyond the packet are allowed only to verify a specific suspected finding; cite what sent you there. Do not re-derive scope: no re-running git log, no repo-wide sweeps. Your turn budget is a backstop, not a target. If you wrap up before completing your checklist — turn limit reached or otherwise — state explicitly what was not covered.
|
|
26
|
+
|
|
27
|
+
## Your checklist
|
|
28
|
+
|
|
29
|
+
### Code smells
|
|
30
|
+
|
|
31
|
+
Review the changed code and affected files against the assigned requirement and feature scope. Flag only smells that require large refactors risky to the requirement; everything else is for the main agent to fix.
|
|
21
32
|
|
|
22
33
|
- Shallow modules (interface nearly as complex as implementation)
|
|
23
34
|
- Duplication
|
|
@@ -3,16 +3,27 @@ name: pwk-spec-reviewer
|
|
|
3
3
|
description: Spec-alignment reviewer — checks each acceptance criterion has covering code and tests; flags gaps and scope creep. Read-only reporter.
|
|
4
4
|
tools: read, grep, find, ls, bash
|
|
5
5
|
systemPromptMode: replace
|
|
6
|
+
max_turns: 40
|
|
6
7
|
---
|
|
7
8
|
|
|
8
|
-
# PWK
|
|
9
|
+
# PWK Reviewer
|
|
9
10
|
|
|
10
|
-
You are a
|
|
11
|
+
You are a read-only code reviewer. Execute the task instructions below faithfully using the host’s read-only tools. **Report findings only — do not modify files.**
|
|
11
12
|
|
|
12
13
|
## Authority boundary
|
|
13
14
|
|
|
14
15
|
The host must enforce read-only execution. Do not create, modify, delete, move, or copy files, and do not run commands that mutate system or repository state.
|
|
15
16
|
|
|
16
|
-
##
|
|
17
|
+
## Reporting contract
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
Every finding cites evidence as file and line (file:line). For each finding, state the affected location, what you observed, and why it matters. If there are no findings, report `No findings` explicitly — an empty or missing report is not a valid outcome.
|
|
20
|
+
|
|
21
|
+
## Working from the packet
|
|
22
|
+
|
|
23
|
+
The task provides a review packet: the diff under review plus the acceptance criteria, feature acceptance, production-risk notes, and a list of changed files. Work from the packet. Targeted reads of the files it lists are expected — read around the hunks you are judging. Reads beyond the packet are allowed only to verify a specific suspected finding; cite what sent you there. Do not re-derive scope: no re-running git log, no repo-wide sweeps. Your turn budget is a backstop, not a target. If you wrap up before completing your checklist — turn limit reached or otherwise — state explicitly what was not covered.
|
|
24
|
+
|
|
25
|
+
## Your checklist
|
|
26
|
+
|
|
27
|
+
### Spec alignment
|
|
28
|
+
|
|
29
|
+
For each acceptance criterion, point to the code and the test that satisfy it. A criterion with no covering code or no test is a **gap**. Code that does more than the criteria specify is **scope creep** — flag it.
|
|
@@ -3,16 +3,27 @@ name: pwk-tracing-reviewer
|
|
|
3
3
|
description: Code-tracing reviewer — traces new/changed paths end-to-end against tests; flags untested branches, dead branches, and broken traces. Read-only reporter.
|
|
4
4
|
tools: read, grep, find, ls, bash
|
|
5
5
|
systemPromptMode: replace
|
|
6
|
+
max_turns: 40
|
|
6
7
|
---
|
|
7
8
|
|
|
8
|
-
# PWK
|
|
9
|
+
# PWK Reviewer
|
|
9
10
|
|
|
10
|
-
You are a
|
|
11
|
+
You are a read-only code reviewer. Execute the task instructions below faithfully using the host’s read-only tools. **Report findings only — do not modify files.**
|
|
11
12
|
|
|
12
13
|
## Authority boundary
|
|
13
14
|
|
|
14
15
|
The host must enforce read-only execution. Do not create, modify, delete, move, or copy files, and do not run commands that mutate system or repository state.
|
|
15
16
|
|
|
16
|
-
##
|
|
17
|
+
## Reporting contract
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
Every finding cites evidence as file and line (file:line). For each finding, state the affected location, what you observed, and why it matters. If there are no findings, report `No findings` explicitly — an empty or missing report is not a valid outcome.
|
|
20
|
+
|
|
21
|
+
## Working from the packet
|
|
22
|
+
|
|
23
|
+
The task provides a review packet: the diff under review plus the acceptance criteria, feature acceptance, production-risk notes, and a list of changed files. Work from the packet. Targeted reads of the files it lists are expected — read around the hunks you are judging. Reads beyond the packet are allowed only to verify a specific suspected finding; cite what sent you there. Do not re-derive scope: no re-running git log, no repo-wide sweeps. Your turn budget is a backstop, not a target. If you wrap up before completing your checklist — turn limit reached or otherwise — state explicitly what was not covered.
|
|
24
|
+
|
|
25
|
+
## Your checklist
|
|
26
|
+
|
|
27
|
+
### Code tracing
|
|
28
|
+
|
|
29
|
+
Trace the new or changed code paths end-to-end against the integration tests. For each path, determine whether data flows correctly from entry to the asserted outcome. Note any branch the tests do not exercise, any dead branch, or any path where the trace breaks.
|
|
@@ -76,9 +76,9 @@ Implement via the **feature-gate flow** with full autonomy: write the feature-ac
|
|
|
76
76
|
|
|
77
77
|
### 4. Code review (feature level)
|
|
78
78
|
|
|
79
|
-
The `pwk-executing-tasks` skill requests the `parallel-review` capability for four logical roles over the whole feature diff: spec alignment, code tracing, code smells, and production hazards. The roles are independent, fresh-context, read-only reporters; the main agent collects their results, applies smell fixes itself, runs the tests, and flags other findings for the human.
|
|
79
|
+
The `pwk-executing-tasks` skill requests the `parallel-review` capability for four logical roles over the whole feature diff: spec alignment, code tracing, code smells, and production hazards. The scope is a script-assembled review packet (diff + acceptance criteria verbatim) handed to every role via a one-liner pointer — the packet never rides in spawn arguments. The roles are independent, fresh-context, read-only reporters; the main agent collects their results, applies smell fixes itself, runs the tests, and flags other findings for the human.
|
|
80
80
|
|
|
81
|
-
In Pi, `/pwk-setup` installs the canonical role definitions into `.agents/agents/`, where compatible providers such as `@tintinweb/pi-subagents` can discover them. Tintinweb may run the roles through its native `Agent` mechanism or map recon to its built-in read-only `Explore` type. The core kit does not require Tintinweb or any other provider.
|
|
81
|
+
In Pi, `/pwk-setup` installs the canonical role definitions into `.agents/agents/`, where compatible providers such as `@tintinweb/pi-subagents` can discover them. `/pwk-setup --fast-model <model>` (or the interactive picker) sets the fast-tier model for the smell/hazard reviewers — an advisory hint hosts may honor. Tintinweb may run the roles through its native `Agent` mechanism or map recon to its built-in read-only `Explore` type. The core kit does not require Tintinweb or any other provider.
|
|
82
82
|
|
|
83
83
|
*Fallback:* if no host/provider can guarantee the requested capabilities, the skill performs the missing recon or review work inline. Other Pi extensions are supported only when they expose the documented capabilities or have a separate adapter; arbitrary extensions are not automatically compatible. See `docs/provider-delegation-contract.md` for the integration contract.
|
|
84
84
|
|
package/docs/oversight-model.md
CHANGED
|
@@ -29,7 +29,7 @@ The agent can still use `read` and `bash` for investigation. During those gated
|
|
|
29
29
|
|
|
30
30
|
During executing-tasks, code-review, finalizing, **and diagnose**, nothing is restricted (diagnosis needs to write failing tests and debug instrumentation, so it exits the gate). `pwk-status` stays inside the gate.
|
|
31
31
|
|
|
32
|
-
Canonical role contracts live in `agents/pwk-*.md` (single source of truth) and can be installed into `.agents/agents/` with `/pwk-setup`. `pwk-executing-tasks` requests logical review roles through the host’s delegation capabilities and passes each role
|
|
32
|
+
Canonical role contracts live in `agents/pwk-*.md` (single source of truth) and can be installed into `.agents/agents/` with `/pwk-setup`. `pwk-executing-tasks` requests logical review roles through the host’s delegation capabilities and passes each role a one-liner pointer to a script-assembled review packet — the packet defines the scope per review level (feature review: the whole feature diff; per-requirement: just that slice).
|
|
33
33
|
|
|
34
34
|
Phases follow the skill you invoke — there is no message-keyword unlock. Invoking `/skill:pwk-executing-tasks`, `pwk-finalizing`, `pwk-code-review`, or `pwk-diagnose` exits the gated phase (those skills write source); `pwk-status` deliberately does **not** (read-only orientation). `/pwk-guard on|off|auto` manually overrides the guard.
|
|
35
35
|
|
|
@@ -27,6 +27,14 @@ A provider advertises capabilities independently from its name:
|
|
|
27
27
|
|
|
28
28
|
A provider must not claim `read-only-enforcement` when it only adds a prompt instruction. Providers may support `codebase-recon` without supporting `parallel-review`.
|
|
29
29
|
|
|
30
|
+
## Role resource hints
|
|
31
|
+
|
|
32
|
+
Role definition frontmatter may declare optional resource hints: `model` (a host-resolvable model name), `thinking` (a reasoning-effort level), and `max_turns` (a turn budget — the per-role instance of `bounded-execution`). Hints are advisory:
|
|
33
|
+
|
|
34
|
+
- Hosts that support per-role resources SHOULD honor them; hosts that do not ignore them without failing the operation.
|
|
35
|
+
- If a `model` hint cannot be resolved to an available model, the host runs the role on its default model — an unresolvable hint MUST NOT fail the review.
|
|
36
|
+
- `max_turns` is a graceful backstop: the role wraps up and reports rather than running unbounded. A capped role still follows the normalized outcome rules — a non-empty report is required for completion.
|
|
37
|
+
|
|
30
38
|
## Request shape
|
|
31
39
|
|
|
32
40
|
The following TypeScript is illustrative. Implementations may use Pi events, tool calls, CLI processes, native task APIs, or another transport. A reference implementation of the outcome normalization lives in `extensions/workflow-guard.ts` (`assessDelegationCoverage`), exported as a pure helper so future adapters and tests share one definition of complete coverage.
|
package/docs/workflow-phases.md
CHANGED
|
@@ -56,7 +56,7 @@ The **feature-gate flow** is the default: write the feature E2E first, implement
|
|
|
56
56
|
|
|
57
57
|
- **Checkpoints** — `none` (no per-requirement stop, **default**) | `full` (both stops) | `spec` (tests stop only — cheap spec-correctness gate, implementation covered by review). Test-first is preserved either way: even `none` writes a meaningful test first (red) and implements to green; only the human *stops* are optional. `spec` requires at least `inline` review (never combine with `skip`).
|
|
58
58
|
- **Review** — `skip` (no per-requirement review, **default**) | `parallel` (four fresh-context reviewers) | `inline` (single `pwk-code-review` pass).
|
|
59
|
-
- **Feature review** — `parallel` (four reviewers over the whole feature diff, **default**) | `inline` (one pass, small features). Always on.
|
|
59
|
+
- **Feature review** — `parallel` (four reviewers over the whole feature diff, **default**) | `inline` (one pass, small features). Always on. The review scope is a script-assembled review packet (diff + criteria verbatim), so reviewers never re-derive scope; smell/hazard reviewers run on the fast tier set via `/pwk-setup --fast-model` (advisory hint).
|
|
60
60
|
|
|
61
61
|
Flag a requirement for a checkpoint when it has complex logic or is the main part of the feature; for a review when it touches production-risk. A trivial fix can also skip the multi-turn brainstorm dialogue via the brainstorming trivial fast-path (compress to one turn, minimal design doc) — the guard still enforces read-only.
|
|
62
62
|
|
|
@@ -70,17 +70,91 @@ export const ROLE_NAMES = [
|
|
|
70
70
|
"pwk-hazard-reviewer",
|
|
71
71
|
] as const;
|
|
72
72
|
|
|
73
|
+
const REVIEWER_ROLES = ROLE_NAMES.filter((role) => role !== "pwk-recon-scout");
|
|
74
|
+
const FAST_TIER_ROLES = ["pwk-smell-reviewer", "pwk-hazard-reviewer"];
|
|
75
|
+
|
|
76
|
+
const FAST_MODEL_PLACEHOLDER = "# model: <fast-tier> — set yours via /pwk-setup";
|
|
77
|
+
|
|
78
|
+
/** Split a role file into its frontmatter block (with fences) and body. */
|
|
79
|
+
function splitFrontmatter(content: string): { frontmatter: string; rest: string } {
|
|
80
|
+
const match = content.match(/^(---\n[\s\S]*?\n---\n)([\s\S]*)$/);
|
|
81
|
+
return match ? { frontmatter: match[1], rest: match[2] } : { frontmatter: "", rest: content };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** A model hint is a `model:` key line — one definition shared by every consumer
|
|
85
|
+
* (apply, detect, conflict-compare) so the three can never drift apart. Scoped to
|
|
86
|
+
* frontmatter: a body line that happens to start `model: ` is content, not config. */
|
|
87
|
+
const MODEL_HINT_LINE = /^model: /m;
|
|
88
|
+
|
|
89
|
+
/** Apply a fast-tier model hint to a role definition.
|
|
90
|
+
*
|
|
91
|
+
* Replaces an existing `model:` line; else swaps the commented placeholder for
|
|
92
|
+
* `model: <model>`; else inserts after `systemPromptMode:` when `insertIfAbsent`
|
|
93
|
+
* (the all-four path for judgment roles, which ship no placeholder). Pure: same
|
|
94
|
+
* input always yields the same output, so install comparisons stay byte-exact.
|
|
95
|
+
*/
|
|
96
|
+
export function applyFastModelHint(content: string, model: string, opts?: { insertIfAbsent?: boolean }): string {
|
|
97
|
+
const trimmed = model.trim();
|
|
98
|
+
if (!trimmed || /\s/.test(trimmed)) throw new Error(`Invalid fast model name: ${JSON.stringify(model)}`);
|
|
99
|
+
const { frontmatter, rest } = splitFrontmatter(content);
|
|
100
|
+
if (!frontmatter) return content;
|
|
101
|
+
const hintedFrontmatter = MODEL_HINT_LINE.test(frontmatter)
|
|
102
|
+
? frontmatter.replace(/^model: .*$/m, `model: ${trimmed}`)
|
|
103
|
+
: frontmatter.includes(FAST_MODEL_PLACEHOLDER)
|
|
104
|
+
? frontmatter.replace(FAST_MODEL_PLACEHOLDER, `model: ${trimmed}`)
|
|
105
|
+
: opts?.insertIfAbsent
|
|
106
|
+
? frontmatter.replace("systemPromptMode: replace\n", `systemPromptMode: replace\nmodel: ${trimmed}\n`)
|
|
107
|
+
: frontmatter;
|
|
108
|
+
return hintedFrontmatter + rest;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** True when the only difference between two contents is the model hint line
|
|
112
|
+
* (an uncommented `model:` line or the commented placeholder). Such deltas are
|
|
113
|
+
* kit-managed config and auto-update without --force; anything else conflicts.
|
|
114
|
+
*/
|
|
115
|
+
function differsOnlyByHint(a: string, b: string): boolean {
|
|
116
|
+
const strip = (content: string) => {
|
|
117
|
+
const { frontmatter, rest } = splitFrontmatter(content);
|
|
118
|
+
const stripped = frontmatter
|
|
119
|
+
.split("\n")
|
|
120
|
+
.filter((line) => !MODEL_HINT_LINE.test(line) && line !== FAST_MODEL_PLACEHOLDER)
|
|
121
|
+
.join("\n");
|
|
122
|
+
return stripped + rest;
|
|
123
|
+
};
|
|
124
|
+
return strip(a) === strip(b);
|
|
125
|
+
}
|
|
126
|
+
|
|
73
127
|
const CANONICAL_AGENTS_DIR = resolve(dirname(fileURLToPath(import.meta.url)), "..", "agents");
|
|
74
128
|
|
|
75
129
|
function setupUsageError(): Error {
|
|
76
|
-
return new Error("Usage: /pwk-setup [--force]");
|
|
130
|
+
return new Error("Usage: /pwk-setup [--force] [--fast-model <model>] [--all-roles]");
|
|
77
131
|
}
|
|
78
132
|
|
|
79
|
-
function parseSetupArgs(args: string): { force: boolean } {
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
133
|
+
function parseSetupArgs(args: string): { force: boolean; fastModel?: string; allRoles: boolean } {
|
|
134
|
+
const tokens = args.trim().split(/\s+/).filter(Boolean);
|
|
135
|
+
let force = false;
|
|
136
|
+
let allRoles = false;
|
|
137
|
+
let fastModel: string | undefined;
|
|
138
|
+
for (let i = 0; i < tokens.length; i += 1) {
|
|
139
|
+
const token = tokens[i];
|
|
140
|
+
if (token === "--force") {
|
|
141
|
+
force = true;
|
|
142
|
+
} else if (token === "--all-roles") {
|
|
143
|
+
allRoles = true;
|
|
144
|
+
} else if (token.startsWith("--fast-model=")) {
|
|
145
|
+
fastModel = token.slice("--fast-model=".length);
|
|
146
|
+
} else if (token === "--fast-model") {
|
|
147
|
+
const next = tokens[i + 1];
|
|
148
|
+
if (next === undefined || next.startsWith("--")) throw setupUsageError();
|
|
149
|
+
fastModel = next;
|
|
150
|
+
i += 1;
|
|
151
|
+
} else {
|
|
152
|
+
throw setupUsageError();
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
if (fastModel !== undefined && !fastModel.trim()) throw setupUsageError();
|
|
156
|
+
if (allRoles && fastModel === undefined) throw setupUsageError(); // --all-roles pairs with --fast-model
|
|
157
|
+
return { force, fastModel, allRoles };
|
|
84
158
|
}
|
|
85
159
|
|
|
86
160
|
function ensureDirectory(path: string): void {
|
|
@@ -134,7 +208,10 @@ function writeNewFile(path: string, content: string): void {
|
|
|
134
208
|
}
|
|
135
209
|
}
|
|
136
210
|
|
|
137
|
-
function installRoleFiles(
|
|
211
|
+
function installRoleFiles(
|
|
212
|
+
cwd: string,
|
|
213
|
+
opts: { force: boolean; hint?: { model: string; allRoles: boolean } },
|
|
214
|
+
): { installed: string[]; skipped: string[] } {
|
|
138
215
|
const projectAgentsDir = join(cwd, ".agents");
|
|
139
216
|
const targetDir = join(projectAgentsDir, "agents");
|
|
140
217
|
ensureDirectory(projectAgentsDir);
|
|
@@ -152,7 +229,11 @@ function installRoleFiles(cwd: string, force: boolean): { installed: string[]; s
|
|
|
152
229
|
try {
|
|
153
230
|
// Read inside the try: one broken canonical source becomes a per-role failure
|
|
154
231
|
// instead of aborting the whole install and hiding other roles' results.
|
|
155
|
-
const
|
|
232
|
+
const canonical = readFileSync(sourcePath, "utf8");
|
|
233
|
+
const hintApplies =
|
|
234
|
+
opts.hint !== undefined && (opts.hint.allRoles ? REVIEWER_ROLES : FAST_TIER_ROLES).includes(roleName);
|
|
235
|
+
const content =
|
|
236
|
+
hintApplies && opts.hint ? applyFastModelHint(canonical, opts.hint.model, { insertIfAbsent: true }) : canonical;
|
|
156
237
|
|
|
157
238
|
// Open the existing target (if any) once and do every check/read/write through
|
|
158
239
|
// that single fd — the fd names one fixed inode, so nothing swapped in on the
|
|
@@ -186,7 +267,14 @@ function installRoleFiles(cwd: string, force: boolean): { installed: string[]; s
|
|
|
186
267
|
skipped.push(roleName);
|
|
187
268
|
continue;
|
|
188
269
|
}
|
|
189
|
-
if (
|
|
270
|
+
if (opts.hint !== undefined && differsOnlyByHint(existing, content)) {
|
|
271
|
+
// Kit-managed only while a hint choice is active this run: bare runs treat
|
|
272
|
+
// any delta (including hand-added model lines) as content — conflict rules.
|
|
273
|
+
overwriteFd(fd, content, targetPath);
|
|
274
|
+
installed.push(roleName);
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
if (!opts.force) {
|
|
190
278
|
failures.push(`${targetPath}: conflict (use /pwk-setup --force to replace it)`);
|
|
191
279
|
continue;
|
|
192
280
|
}
|
|
@@ -211,6 +299,57 @@ function installRoleFiles(cwd: string, force: boolean): { installed: string[]; s
|
|
|
211
299
|
return { installed, skipped };
|
|
212
300
|
}
|
|
213
301
|
|
|
302
|
+
/** Minimal structural view of the command context the fast-model prompt needs. */
|
|
303
|
+
interface FastModelPromptContext {
|
|
304
|
+
cwd: string;
|
|
305
|
+
hasUI?: boolean;
|
|
306
|
+
scopedModels?: { model?: string }[];
|
|
307
|
+
ui?: {
|
|
308
|
+
select?: (title: string, options: { value: string; label: string; description: string }[]) => Promise<string>;
|
|
309
|
+
confirm?: (title: string, message: string) => Promise<boolean>;
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/** True when an installed fast-tier role already carries a model hint. */
|
|
314
|
+
function installedHint(cwd: string): { model: string; allRoles: boolean } | undefined {
|
|
315
|
+
const frontmatterOf = (role: string): string => {
|
|
316
|
+
try {
|
|
317
|
+
return splitFrontmatter(readFileSync(join(cwd, ".agents", "agents", `${role}.md`), "utf8")).frontmatter;
|
|
318
|
+
} catch {
|
|
319
|
+
return "";
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
const modelOf = (frontmatter: string): string | undefined =>
|
|
323
|
+
frontmatter.match(/^model: (\S.*)$/m)?.[1]?.trim() || undefined;
|
|
324
|
+
const model = FAST_TIER_ROLES.map(frontmatterOf).map(modelOf).find(Boolean);
|
|
325
|
+
if (!model) return undefined;
|
|
326
|
+
const allRoles = REVIEWER_ROLES.some((role) => modelOf(frontmatterOf(role)) !== undefined);
|
|
327
|
+
return { model, allRoles };
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/** Ask for the fast-tier model once, only when a picker is available, no hint is
|
|
331
|
+
* installed, and no --fast-model argument was given. Headless hosts skip silently
|
|
332
|
+
* and reviewers stay on default models. */
|
|
333
|
+
async function promptFastModelChoice(
|
|
334
|
+
ctx: FastModelPromptContext,
|
|
335
|
+
): Promise<{ model: string; allRoles: boolean } | undefined> {
|
|
336
|
+
const ui = ctx.ui;
|
|
337
|
+
if (typeof ui?.select !== "function" || ctx.hasUI === false) return undefined;
|
|
338
|
+
if (installedHint(ctx.cwd) !== undefined) return undefined;
|
|
339
|
+
const scoped = Array.isArray(ctx.scopedModels) ? ctx.scopedModels : [];
|
|
340
|
+
const models = scoped
|
|
341
|
+
.map((entry) => (typeof entry?.model === "string" ? entry.model : undefined))
|
|
342
|
+
.filter((model): model is string => model !== undefined && model.length > 0);
|
|
343
|
+
const options = [
|
|
344
|
+
...models.map((model) => ({ value: model, label: model, description: "fast-tier reviewer model" })),
|
|
345
|
+
{ value: "skip", label: "skip", description: "reviewers run on default models" },
|
|
346
|
+
];
|
|
347
|
+
const choice = await ui.select("Fast-tier model for smell/hazard reviewers", options);
|
|
348
|
+
if (!choice || choice === "skip") return undefined;
|
|
349
|
+
const allRoles = (await ui.confirm?.("Apply to all four reviewers?", "No = smell+hazard only")) ?? false;
|
|
350
|
+
return { model: choice, allRoles };
|
|
351
|
+
}
|
|
352
|
+
|
|
214
353
|
// Destructive commands blocked in brainstorm/plan phases (simple common blacklist)
|
|
215
354
|
const DESTRUCTIVE_PATTERNS = [
|
|
216
355
|
/\brm\b/i,
|
|
@@ -416,12 +555,23 @@ export default function (pi: ExtensionAPI) {
|
|
|
416
555
|
throw new Error(message);
|
|
417
556
|
}
|
|
418
557
|
|
|
419
|
-
const { force } = parseSetupArgs(args ?? "");
|
|
558
|
+
const { force, fastModel, allRoles } = parseSetupArgs(args ?? "");
|
|
420
559
|
try {
|
|
421
|
-
|
|
560
|
+
// Explicit arg wins; else the picker (only while no hint is installed);
|
|
561
|
+
// else re-apply the installed hint — the recorded choice — so bare runs
|
|
562
|
+
// are no-ops rather than stripping or conflicting on kit-managed lines.
|
|
563
|
+
const hint =
|
|
564
|
+
fastModel !== undefined
|
|
565
|
+
? { model: fastModel, allRoles }
|
|
566
|
+
: ((await promptFastModelChoice(ctx)) ?? installedHint(ctx.cwd));
|
|
567
|
+
const result = installRoleFiles(ctx.cwd, { force, hint });
|
|
422
568
|
const parts = [`PWK setup complete: ${result.installed.length} installed`];
|
|
423
569
|
if (result.skipped.length > 0) parts.push(`${result.skipped.length} skipped`);
|
|
424
570
|
if (force) parts.push("forced conflicts replaced");
|
|
571
|
+
if (hint) {
|
|
572
|
+
const scope = hint.allRoles ? "all four reviewers" : "smell+hazard reviewers";
|
|
573
|
+
parts.push(`fast model ${hint.model} (${scope})`);
|
|
574
|
+
}
|
|
425
575
|
ctx.ui.notify(`${parts.join(", ")}. Providers may require /reload to discover updated roles.`, "info");
|
|
426
576
|
} catch (error) {
|
|
427
577
|
const message = error instanceof Error ? error.message : String(error);
|
package/package.json
CHANGED
|
@@ -68,7 +68,7 @@ Set `Feature phase: implementing (0/N)` and work the requirements in listed orde
|
|
|
68
68
|
|
|
69
69
|
### Per-requirement review (opt-in)
|
|
70
70
|
|
|
71
|
-
If the requirement's `### Review` tag is `parallel` or `inline` (default `skip`), review that slice now — same mechanics as the [feature review](#feature-review), scoped to the requirement
|
|
71
|
+
If the requirement's `### Review` tag is `parallel` or `inline` (default `skip`), review that slice now — same mechanics as the [feature review](#feature-review), with a requirement-scoped packet: the same recipe limited to the commits and criteria sections of that requirement, written to `docs/plans/<dated-stem>-review-packet.md`. With `skip`, no per-requirement review; the feature-level review covers it.
|
|
72
72
|
|
|
73
73
|
`Checkpoints: spec` requires at least `inline` review — dropping the complete checkpoint is only safe when review covers implementation quality; never combine `spec` with `Review: skip` (use `Checkpoints: none` instead).
|
|
74
74
|
|
|
@@ -93,7 +93,34 @@ The old "integration gate" is gone — the feature E2E at `feature-complete` *is
|
|
|
93
93
|
|
|
94
94
|
After `feature-complete` is approved, run **one** review over the **whole feature diff**, driven by the plan's feature-level `### Feature review` tag. This is the single thorough review — per-requirement reviews, if any, only saw slices in isolation.
|
|
95
95
|
|
|
96
|
-
|
|
96
|
+
**Assemble the review packet first** — once, by script, so that no packet byte passes through model output (spawn arguments are model output; file reads are not). If commits land while the review is in flight, re-run the recipe before spawning any replacement role so the packet matches HEAD:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
PACKET="docs/plans/<dated-stem>-review-packet.md" # same dated stem as the plan docs
|
|
100
|
+
{
|
|
101
|
+
echo "# Review packet: <topic> — feature review"
|
|
102
|
+
echo
|
|
103
|
+
echo "## Commits"
|
|
104
|
+
git log --oneline <merge-base>..HEAD
|
|
105
|
+
echo
|
|
106
|
+
echo "## Changed files"
|
|
107
|
+
git diff --stat <merge-base>...HEAD
|
|
108
|
+
echo
|
|
109
|
+
echo "## Acceptance criteria (verbatim from the plan)"
|
|
110
|
+
sed -n '/^## Requirement 1/,/^## Feature acceptance/p' docs/plans/<dated-stem>-implementation.md | sed '/^## Feature acceptance/,$d'
|
|
111
|
+
echo
|
|
112
|
+
echo "## Feature acceptance (verbatim)"
|
|
113
|
+
sed -n '/^## Feature acceptance/,/^### Feature review/p' docs/plans/<dated-stem>-implementation.md | sed '/^### Feature review/,$d'
|
|
114
|
+
echo
|
|
115
|
+
echo "## Production-risk notes (verbatim, if any)"
|
|
116
|
+
sed -n '/^### Production-risk notes/,/^## /p' docs/plans/<dated-stem>-implementation.md | sed '/^## /d'
|
|
117
|
+
echo
|
|
118
|
+
echo "## Diff"
|
|
119
|
+
git diff <merge-base>...HEAD
|
|
120
|
+
} > "$PACKET"
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
- **`parallel`** (default) — request the host’s `parallel-review` capability for four fresh-context, read-only logical roles: `pwk-spec-reviewer`, `pwk-tracing-reviewer`, `pwk-smell-reviewer`, and `pwk-hazard-reviewer`. Spawn each role with a **one-liner** — a pointer to the packet file with the role framing appended last: the checklist name of the role (`spec alignment`, `code tracing`, `code smells`, or `production hazards`). For example: `Read docs/plans/<dated-stem>-review-packet.md. Your role: spec alignment.` The packet never appears in spawn arguments. Require independent execution and one collected outcome per role. The reviewer role contracts live in `agents/pwk-*-reviewer.md`; do not duplicate their checklists in the workflow instructions. Reviewers are read-only reporters; you apply smell fixes yourself (full suite + E2E must stay green, commit) and flag trace/spec/hazard findings as follow-ups for the human.
|
|
97
124
|
|
|
98
125
|
- **`inline`** — perform `/skill:pwk-code-review` over the whole diff as a single pass.
|
|
99
126
|
- **Fallback** — if the host has no compatible parallel-review capability, cannot prove the requested read-only/fresh-context/bounded constraints, or delegation fails, perform the missing review work inline. Retain successful delegated reports and do not mark the feature fully reviewed while a required role is missing.
|
|
@@ -27,7 +27,7 @@ Ship the completed work.
|
|
|
27
27
|
|
|
28
28
|
```bash
|
|
29
29
|
# for each <topic> in the set:
|
|
30
|
-
rm -f docs/plans/????-??-??-<topic>-design.md docs/plans/????-??-??-<topic>-implementation.md docs/plans/????-??-??-<topic>-progress.md
|
|
30
|
+
rm -f docs/plans/????-??-??-<topic>-design.md docs/plans/????-??-??-<topic>-implementation.md docs/plans/????-??-??-<topic>-progress.md docs/plans/????-??-??-<topic>-review-packet.md
|
|
31
31
|
# umbrella only:
|
|
32
32
|
rm -f docs/plans/????-??-??-<umbrella>-overview.md
|
|
33
33
|
git add -A docs/plans/ && git commit -m "chore: delete planning docs for <topic-or-umbrella>"
|
|
@@ -41,6 +41,7 @@ Ship the completed work.
|
|
|
41
41
|
mv docs/plans/????-??-??-<topic>-design.md docs/plans/completed/ 2>/dev/null || true
|
|
42
42
|
mv docs/plans/????-??-??-<topic>-implementation.md docs/plans/completed/ 2>/dev/null || true
|
|
43
43
|
mv docs/plans/????-??-??-<topic>-progress.md docs/plans/completed/ 2>/dev/null || true
|
|
44
|
+
mv docs/plans/????-??-??-<topic>-review-packet.md docs/plans/completed/ 2>/dev/null || true
|
|
44
45
|
# umbrella only:
|
|
45
46
|
mv docs/plans/????-??-??-<umbrella>-overview.md docs/plans/completed/ 2>/dev/null || true
|
|
46
47
|
git add docs/plans/ && git commit -m "chore: archive planning docs for <topic-or-umbrella>"
|