@tianhai/pi-workflow-kit 1.5.0 → 1.6.1

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
@@ -20,6 +20,8 @@ For Pi delegation providers that discover project agents, install the canonical
20
20
 
21
21
  This creates the five role definitions under `.agents/agents/`. It does not install or configure a provider. Existing customized files are preserved; use `/pwk-setup --force` only when you explicitly want to replace differing role files. Run setup before `/skill:pwk-brainstorming`; the command is refused during brainstorm and plan phases.
22
22
 
23
+ Optionally set a fast-tier model for the smell/hazard reviewers (they carry `thinking: low` and a turn budget by default): `/pwk-setup --fast-model <model>` — or pick one interactively when setup offers — and `--all-roles` to apply it to all four reviewers. The hint is advisory; hosts that cannot honor it keep the default model. Bare re-runs keep the recorded choice; only `--force` replaces local edits beyond the model line.
24
+
23
25
  **Want to try before committing?**
24
26
 
25
27
  ```bash
@@ -73,7 +75,7 @@ A **design doc is one PR**; a **requirement is one testable slice within it**. A
73
75
  | **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
76
  | **Plan** | `/skill:pwk-writing-plans` | Turn each requirement into **acceptance criteria + integration tests** — a behavioral spec (no implementation code) |
75
77
  | **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. |
78
+ | **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
79
  | **Finalize** | `/skill:pwk-finalizing` | Delete consumed plan docs, update README/CHANGELOG, create PR |
78
80
  | **Diagnose** | `/skill:pwk-diagnose` | Debugging loop: reproduce → hypothesise → instrument → fix → cleanup. **Exits the gated phase** (debugging writes tests/instrumentation) |
79
81
  | **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 Hazard Reviewer
11
+ # PWK Reviewer
9
12
 
10
- You are a production-hazards reviewer. Execute the task instructions below faithfully using the host’s read-only tools. **Report findings only — do not modify files.** Flag hazards and non-trivial issues for the main agent or human to decide.
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
- ## Checklist — audit each changed file
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 Smell Reviewer
11
+ # PWK Reviewer
9
12
 
10
- You are a code-smell reviewer. Execute the task instructions below faithfully using the host’s read-only tools. **Report findings only — do not modify files.** Flag only smells that require large refactors risky to the requirement; everything else is for the main agent to fix.
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
- ## Review scope
19
+ ## Reporting contract
17
20
 
18
- Review the changed code and affected files against the assigned requirement and feature scope.
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
- ## Checklist report what you find
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 Spec Reviewer
9
+ # PWK Reviewer
9
10
 
10
- You are a spec-alignment reviewer. Execute the task instructions below faithfully using the host’s read-only tools. **Report findings only — do not modify files.**
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
- ## Checklist
17
+ ## Reporting contract
17
18
 
18
- 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. Include the relevant file and line for each finding. If there are no findings, report `No findings` explicitly.
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 Trace Reviewer
9
+ # PWK Reviewer
9
10
 
10
- You are a code-tracing reviewer. Execute the task instructions below faithfully using the host’s read-only tools. **Report findings only — do not modify files.**
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
- ## Checklist
17
+ ## Reporting contract
17
18
 
18
- 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. Include file and line evidence. If there are no findings, report `No findings` explicitly.
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
 
@@ -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 just the requirement scope + diff.
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.
@@ -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 normalized = args.trim();
81
- if (!normalized) return { force: false };
82
- if (normalized === "--force") return { force: true };
83
- throw setupUsageError();
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(cwd: string, force: boolean): { installed: string[]; skipped: string[] } {
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 content = readFileSync(sourcePath, "utf8");
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 (!force) {
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
- const result = installRoleFiles(ctx.cwd, force);
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tianhai/pi-workflow-kit",
3
- "version": "1.5.0",
3
+ "version": "1.6.1",
4
4
  "description": "Enforce structured brainstorm→plan→execute→finalize workflow with TDD discipline in AI coding agents",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -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's diff. With `skip`, no per-requirement review; the feature-level review covers it.
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-r<N>.md` (requirement-suffixed, so per-requirement packets never overwrite the feature packet or each other). 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
- - **`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`. Gather scope (the plan's acceptance criteria + Feature acceptance, `git log --oneline && git diff <merge-base>...HEAD`) and provide it to every role. 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.
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>"