@tianhai/pi-workflow-kit 0.17.1 → 1.0.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 +91 -64
- package/agents/pwk-hazard-reviewer.md +24 -0
- package/agents/pwk-smell-reviewer.md +17 -0
- package/agents/pwk-spec-reviewer.md +14 -0
- package/agents/pwk-tracing-reviewer.md +14 -0
- package/docs/developer-usage-guide.md +49 -37
- package/docs/lessons.md +13 -0
- package/docs/oversight-model.md +19 -9
- package/docs/workflow-phases.md +58 -29
- package/extensions/workflow-guard.ts +190 -91
- package/package.json +19 -3
- package/skills/pwk-brainstorming/SKILL.md +60 -0
- package/skills/pwk-code-review/SKILL.md +46 -0
- package/skills/{diagnose → pwk-diagnose}/SKILL.md +3 -2
- package/skills/pwk-executing-tasks/SKILL.md +154 -0
- package/skills/pwk-finalizing/SKILL.md +78 -0
- package/skills/pwk-status/SKILL.md +28 -0
- package/skills/pwk-writing-plans/SKILL.md +83 -0
- package/docs/plans/2026-06-03-karpathy-guidelines-ab-comparison.md +0 -166
- package/docs/plans/completed/2026-04-11-checkpoint-review-gates-design.md +0 -50
- package/docs/plans/completed/2026-04-11-checkpoint-review-gates-implementation.md +0 -98
- package/docs/plans/completed/2026-04-11-finalizing-merge-options-design.md +0 -33
- package/docs/plans/completed/2026-04-11-finalizing-merge-options-implementation.md +0 -75
- package/docs/plans/completed/2026-04-11-workspace-setup-design.md +0 -28
- package/docs/plans/completed/2026-04-11-workspace-setup-implementation.md +0 -57
- package/docs/plans/completed/2026-04-15-bash-guard-design.md +0 -39
- package/docs/plans/completed/2026-04-15-bash-guard-implementation.md +0 -229
- package/docs/plans/completed/2026-04-21-workflow-guard-safe-commands-design.md +0 -172
- package/docs/plans/completed/2026-04-21-workflow-guard-safe-commands-implementation.md +0 -168
- package/docs/plans/completed/2026-04-22-go-readonly-safe-commands-implementation.md +0 -54
- package/docs/plans/completed/2026-04-28-executing-tasks-redesign-design.md +0 -171
- package/docs/plans/completed/2026-04-28-executing-tasks-redesign-implementation.md +0 -208
- package/docs/plans/completed/2026-04-28-executing-tasks-redesign-progress.md +0 -14
- package/docs/plans/completed/2026-05-01-incorporate-mattpocock-skills-design.md +0 -154
- package/docs/plans/completed/2026-05-01-incorporate-mattpocock-skills-implementation.md +0 -315
- package/docs/plans/completed/2026-05-01-incorporate-mattpocock-skills-progress.md +0 -15
- package/docs/plans/completed/2026-05-08-checkpoint-gates-design.md +0 -235
- package/docs/plans/completed/2026-05-08-checkpoint-gates-implementation.md +0 -83
- package/docs/plans/completed/2026-05-08-checkpoint-gates-progress.md +0 -11
- package/docs/plans/completed/2026-05-08-lessons-learned-design.md +0 -76
- package/docs/plans/completed/2026-05-08-lessons-learned-implementation.md +0 -219
- package/docs/plans/completed/2026-05-08-lessons-learned-progress.md +0 -15
- package/docs/plans/completed/2026-05-08-migrate-earendil-works-design.md +0 -39
- package/docs/plans/completed/2026-05-08-migrate-earendil-works-implementation.md +0 -45
- package/docs/plans/completed/2026-05-08-migrate-earendil-works-progress.md +0 -10
- package/docs/plans/completed/2026-05-08-worktree-handoff-design.md +0 -118
- package/docs/plans/completed/2026-05-08-worktree-handoff-implementation.md +0 -140
- package/docs/plans/completed/2026-05-08-worktree-handoff-progress.md +0 -10
- package/docs/plans/completed/2026-05-20-generic-lessons-design.md +0 -70
- package/docs/plans/completed/2026-05-20-generic-lessons-implementation.md +0 -114
- package/docs/plans/completed/2026-05-20-generic-lessons-progress.md +0 -11
- package/docs/plans/completed/2026-05-22-agentic-agile-enhancements-design.md +0 -77
- package/docs/plans/completed/2026-05-22-agentic-agile-enhancements-implementation.md +0 -473
- package/docs/plans/completed/2026-05-25-design-review-split-implementation.md +0 -622
- package/docs/plans/completed/2026-05-25-design-review-split-progress.md +0 -16
- package/docs/plans/completed/2026-05-25-pr5-improvements-implementation.md +0 -273
- package/docs/plans/completed/2026-05-25-pr5-improvements-progress.md +0 -17
- package/docs/plans/completed/2026-06-03-add-verify-skill-design.md +0 -51
- package/docs/plans/completed/2026-06-03-add-verify-skill-implementation.md +0 -111
- package/docs/plans/completed/2026-06-03-add-verify-skill-progress.md +0 -11
- package/docs/plans/completed/2026-06-03-verify-skill-design.md +0 -176
- package/skills/brainstorming/SKILL.md +0 -48
- package/skills/design-review/SKILL.md +0 -113
- package/skills/executing-tasks/SKILL.md +0 -350
- package/skills/finalizing/SKILL.md +0 -119
- package/skills/verify/SKILL.md +0 -170
- package/skills/writing-plans/SKILL.md +0 -343
package/docs/workflow-phases.md
CHANGED
|
@@ -1,69 +1,98 @@
|
|
|
1
1
|
# Workflow Phases
|
|
2
2
|
|
|
3
|
-
`pi-workflow-kit` has
|
|
3
|
+
`pi-workflow-kit` has 5 pipeline skills plus 2 utility skills. You invoke each one explicitly with `/skill:`.
|
|
4
4
|
|
|
5
5
|
```
|
|
6
|
-
brainstorm →
|
|
6
|
+
brainstorm → writing-plans → executing-tasks → finalizing
|
|
7
|
+
(per requirement: tests → ⏸ checkpoint → implement → ⏸ checkpoint → code-review)
|
|
7
8
|
```
|
|
8
9
|
|
|
10
|
+
A design doc is one PR; a requirement is one testable slice within it. For multi-design work (a large issue split into several design docs — each its own PR), run the pipeline once per design doc.
|
|
11
|
+
|
|
9
12
|
## brainstorm
|
|
10
13
|
|
|
11
14
|
```
|
|
12
|
-
/skill:brainstorming
|
|
15
|
+
/skill:pwk-brainstorming
|
|
13
16
|
```
|
|
14
17
|
|
|
15
|
-
- Explore requirements and shape the design
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
+
- Explore requirements and shape the design.
|
|
19
|
+
- Produce `docs/plans/YYYY-MM-DD-<topic>-design.md` — descriptive, opening with a `## Requirements` list.
|
|
20
|
+
- May split a large issue into multiple design docs (human-approved).
|
|
21
|
+
- ADRs go to `docs/adr/` (permanent, never archived).
|
|
18
22
|
|
|
19
23
|
Write boundary: only `docs/plans/` is writable. Source files are hard-blocked.
|
|
20
24
|
|
|
21
|
-
##
|
|
25
|
+
## writing-plans
|
|
22
26
|
|
|
23
27
|
```
|
|
24
|
-
/skill:writing-plans
|
|
28
|
+
/skill:pwk-writing-plans
|
|
25
29
|
```
|
|
26
30
|
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
-
|
|
30
|
-
- Produce `docs/plans/YYYY-MM-DD-<topic>-implementation.md`
|
|
31
|
+
- Creates the feature branch first (`git checkout -b <topic>`), so design + plan docs live on the branch, not `main`.
|
|
32
|
+
- Reads the design doc's `## Requirements`; for each, derives **acceptance criteria + integration-test cases** (a behavioral spec, no implementation code), lists requirements in build order (dependencies positioned earlier), and challenges the design when `## Production-risk areas` is present.
|
|
33
|
+
- Produce `docs/plans/YYYY-MM-DD-<topic>-implementation.md`.
|
|
31
34
|
|
|
32
|
-
Write boundary: only `docs/plans/` is writable.
|
|
35
|
+
Write boundary: only `docs/plans/` is writable.
|
|
33
36
|
|
|
34
|
-
##
|
|
37
|
+
## executing-tasks
|
|
35
38
|
|
|
36
39
|
```
|
|
37
|
-
/skill:executing-tasks
|
|
40
|
+
/skill:pwk-executing-tasks
|
|
38
41
|
```
|
|
39
42
|
|
|
40
|
-
-
|
|
41
|
-
-
|
|
42
|
-
-
|
|
43
|
+
- Per requirement: write the integration tests (red) → **⏸ checkpoint: tests** → implement to green (full autonomy — the executor chooses structure/signatures/internals) → **⏸ checkpoint: complete** → commit → **per-requirement review** (four parallel reviewers via the `subagent` tool; falls back to inline `/skill:pwk-code-review` when `pi-subagents` is absent — see [code-review](#code-review)).
|
|
44
|
+
- Two **mandatory** human checkpoints per requirement.
|
|
45
|
+
- Progress tracked in `docs/plans/*-progress.md`.
|
|
46
|
+
- After all requirements: **integration gate** — run the full suite and confirm the requirements compose into the feature before `/skill:pwk-finalizing`.
|
|
43
47
|
|
|
44
48
|
No write restrictions. All tools available.
|
|
45
49
|
|
|
46
|
-
##
|
|
50
|
+
## code-review
|
|
47
51
|
|
|
48
52
|
```
|
|
49
|
-
/skill:
|
|
53
|
+
/skill:pwk-code-review
|
|
50
54
|
```
|
|
51
55
|
|
|
52
|
-
|
|
53
|
-
- Update CHANGELOG, README if needed
|
|
54
|
-
- Create PR
|
|
55
|
-
- Clean up worktree if one was used
|
|
56
|
+
The **inline reviewer**: code tracing, spec alignment (vs acceptance criteria), code smells (applies fixes), production hazard check. Unlocked — may modify code to fix smells.
|
|
56
57
|
|
|
57
|
-
|
|
58
|
+
**Not a phase you drive manually.** During `pwk-executing-tasks`, per-requirement review runs **four specialized reviewers in parallel** via the `subagent` tool (spec, tracing, smell, hazard — each fresh-context, read-only reporters); this skill is the **fallback** when [`pi-subagents`](https://pi.dev/packages/pi-subagents) is not installed. You can also invoke `/skill:pwk-code-review` standalone for an ad-hoc review of any diff.
|
|
59
|
+
|
|
60
|
+
No write restrictions.
|
|
61
|
+
|
|
62
|
+
## finalizing
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
/skill:pwk-finalizing
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
- **Pre-check: run the full test suite** — don't ship a red suite (resume spans sessions; don't trust the last execute session).
|
|
69
|
+
- Delete consumed plan docs (per-`<topic>`) — code + tests are the source of truth; ADRs stay at `docs/adr/`.
|
|
70
|
+
- Curate `docs/lessons.md`, update README/CHANGELOG, create PR or merge.
|
|
71
|
+
|
|
72
|
+
No write restrictions.
|
|
73
|
+
|
|
74
|
+
## status
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
/skill:pwk-status
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Read-only overview of all active pipeline topics (phase + progress) when several designs are in flight. Not a pipeline phase.
|
|
58
81
|
|
|
59
82
|
## diagnose
|
|
60
83
|
|
|
61
84
|
```
|
|
62
|
-
/skill:diagnose
|
|
85
|
+
/skill:pwk-diagnose
|
|
63
86
|
```
|
|
64
87
|
|
|
65
88
|
Not a pipeline phase. A utility skill invoked on demand when debugging is needed.
|
|
66
89
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
90
|
+
No write restrictions.
|
|
91
|
+
|
|
92
|
+
## Manual override
|
|
93
|
+
|
|
94
|
+
`/pwk-guard on|off|auto` overrides the guard regardless of phase: `on` forces a read-only lock, `off` disables the guard entirely, `auto` (default) returns to skill-driven phases. Subcommands autocomplete. Use it as an escape hatch when the guard blocks something you genuinely need; phase transitions otherwise happen only via `/skill:` commands.
|
|
95
|
+
|
|
96
|
+
## Continuity across sessions
|
|
97
|
+
|
|
98
|
+
A new session resumes by invoking the skill for the phase to continue. The skill globs `docs/plans/` for its artifact (progress file / plan doc), resumes the single match, or asks if several. Each resumption skill reports what it found on entry — no registry file needed; the `<topic>` slug in the filenames is the identity.
|
|
@@ -4,14 +4,15 @@ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
|
4
4
|
/**
|
|
5
5
|
* Workflow Guard extension.
|
|
6
6
|
*
|
|
7
|
-
* Blocks write/edit outside docs/plans/ and
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* Blocks write/edit outside docs/plans/ and destructive bash during brainstorm and plan phases.
|
|
8
|
+
* Bash uses a simple common-blacklist (DESTRUCTIVE_PATTERNS) — a command is allowed unless it matches
|
|
9
|
+
* a destructive pattern. A short phase reminder is appended after the user's message each turn via
|
|
10
|
+
* before_agent_start. You control phases explicitly via /skill: commands — no auto-detection, no prompts.
|
|
10
11
|
*/
|
|
11
12
|
|
|
12
13
|
type Phase = "brainstorm" | "plan" | null;
|
|
13
14
|
|
|
14
|
-
// Destructive commands blocked in brainstorm/plan phases
|
|
15
|
+
// Destructive commands blocked in brainstorm/plan phases (simple common blacklist)
|
|
15
16
|
const DESTRUCTIVE_PATTERNS = [
|
|
16
17
|
/\brm\b/i,
|
|
17
18
|
/\brmdir\b/i,
|
|
@@ -27,15 +28,22 @@ const DESTRUCTIVE_PATTERNS = [
|
|
|
27
28
|
/\btruncate\b/i,
|
|
28
29
|
/\bdd\b/i,
|
|
29
30
|
/\bshred\b/i,
|
|
30
|
-
/(^|[^<])>(?!>)/,
|
|
31
|
-
/>>/,
|
|
32
31
|
/\bnpm\s+(install|uninstall|update|ci|link|publish)/i,
|
|
33
32
|
/\byarn\s+(add|remove|install|publish)/i,
|
|
34
33
|
/\bpnpm\s+(add|remove|install|publish)/i,
|
|
35
34
|
/\bpip\s+(install|uninstall)/i,
|
|
36
35
|
/\bapt(-get)?\s+(install|remove|purge|update|upgrade)/i,
|
|
37
36
|
/\bbrew\s+(install|uninstall|upgrade)/i,
|
|
38
|
-
|
|
37
|
+
// git add/commit/apply merge files and are blocked below. Plain `git branch`/`checkout`/`switch`
|
|
38
|
+
// only create or move between branches (no source-file changes), so they are intentionally allowed
|
|
39
|
+
// during gated phases — pwk-writing-plans creates the feature branch before authoring the plan.
|
|
40
|
+
/\bgit\s+(add|commit|push|pull|merge|rebase|reset|branch\s+-[dD]|stash(?!\s+list)|cherry-pick|revert|tag(?!\s+(-l|--list))|init|clone|apply)/i,
|
|
41
|
+
// Edit-via-bash vectors: in-place editors, patch appliers, find-delete (bypass the write/edit tool block)
|
|
42
|
+
/\bsed\b.*\s-i\b/i,
|
|
43
|
+
/\bperl\b.*\s-[a-z]*i\b/i,
|
|
44
|
+
/\bawk\b.*-i\s+inplace\b/i,
|
|
45
|
+
/^\s*patch\b/i, // command-position only — avoids FP on "patch" in paths/searches (grep/cat/cd)
|
|
46
|
+
/\bfind\b.*\s-delete\b/i,
|
|
39
47
|
/\bsudo\b/i,
|
|
40
48
|
/\bsu\b/i,
|
|
41
49
|
/\bkill\b/i,
|
|
@@ -48,73 +56,9 @@ const DESTRUCTIVE_PATTERNS = [
|
|
|
48
56
|
/^\s*(vim?|nano|emacs|code|subl)\b/i,
|
|
49
57
|
];
|
|
50
58
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
/^\s*tail\b/,
|
|
55
|
-
/^\s*less\b/,
|
|
56
|
-
/^\s*more\b/,
|
|
57
|
-
/^\s*grep\b/,
|
|
58
|
-
/^\s*find\b/,
|
|
59
|
-
/^\s*ls\b/,
|
|
60
|
-
/^\s*pwd\b/,
|
|
61
|
-
/^\s*echo\b/,
|
|
62
|
-
/^\s*printf\b/,
|
|
63
|
-
/^\s*wc\b/,
|
|
64
|
-
/^\s*sort\b/,
|
|
65
|
-
/^\s*uniq\b/,
|
|
66
|
-
/^\s*diff\b/,
|
|
67
|
-
/^\s*file\b/,
|
|
68
|
-
/^\s*stat\b/,
|
|
69
|
-
/^\s*du\b/,
|
|
70
|
-
/^\s*df\b/,
|
|
71
|
-
/^\s*tree\b/,
|
|
72
|
-
/^\s*which\b/,
|
|
73
|
-
/^\s*whereis\b/,
|
|
74
|
-
/^\s*type\b/,
|
|
75
|
-
/^\s*env\b/,
|
|
76
|
-
/^\s*printenv\b/,
|
|
77
|
-
/^\s*uname\b/,
|
|
78
|
-
/^\s*whoami\b/,
|
|
79
|
-
/^\s*id\b/,
|
|
80
|
-
/^\s*date\b/,
|
|
81
|
-
/^\s*cal\b/,
|
|
82
|
-
/^\s*uptime\b/,
|
|
83
|
-
/^\s*ps\b/,
|
|
84
|
-
/^\s*top\b/,
|
|
85
|
-
/^\s*htop\b/,
|
|
86
|
-
/^\s*free\b/,
|
|
87
|
-
/^\s*git\s+(status|log|diff|show|branch|remote|config\s+--get)/i,
|
|
88
|
-
/^\s*git\s+ls-/i,
|
|
89
|
-
/^\s*npm\s+(list|ls|view|info|search|outdated|audit)/i,
|
|
90
|
-
/^\s*yarn\s+(list|info|why|audit)/i,
|
|
91
|
-
/^\s*node\s+--version/i,
|
|
92
|
-
/^\s*python\s+--version/i,
|
|
93
|
-
/^\s*curl\s/i,
|
|
94
|
-
/^\s*wget\s+-O\s*-/i,
|
|
95
|
-
/^\s*jq\b/,
|
|
96
|
-
/^\s*sed\s+-n/i,
|
|
97
|
-
/^\s*awk\b/,
|
|
98
|
-
/^\s*rg\b/,
|
|
99
|
-
/^\s*fd\b/,
|
|
100
|
-
/^\s*bat\b/,
|
|
101
|
-
/^\s*eza\b/,
|
|
102
|
-
/^\s*cd\b/,
|
|
103
|
-
/^\s*gh\s+pr\s+(view|list|diff|checks|status)\b/i,
|
|
104
|
-
/^\s*gh\s+issue\s+(view|list)\b/i,
|
|
105
|
-
/^\s*gh\s+repo\s+(view|fork|list)\b/i,
|
|
106
|
-
/^\s*gh\s+release\s+(view|list|download)\b/i,
|
|
107
|
-
/^\s*gh\s+run\s+(view|list)\b/i,
|
|
108
|
-
/^\s*git\s+blame\b/,
|
|
109
|
-
/^\s*git\s+shortlog\b/,
|
|
110
|
-
/^\s*git\s+stash\s+list\b/i,
|
|
111
|
-
/^\s*git\s+tag\s+(-l|--list)\b/i,
|
|
112
|
-
/^\s*git\s+describe\b/,
|
|
113
|
-
/^\s*go\s+doc\b/,
|
|
114
|
-
/^\s*go\s+list\b/,
|
|
115
|
-
/^\s*go\s+version\b/,
|
|
116
|
-
/^\s*go\s+env\b/,
|
|
117
|
-
];
|
|
59
|
+
// Redirect operators — tested on a quote-stripped command so '>' inside quoted
|
|
60
|
+
// arguments (e.g. grep 'x > y') doesn't false-positive.
|
|
61
|
+
const REDIRECT_PATTERNS = [/(^|[^<])>(?!>)/, />>/];
|
|
118
62
|
|
|
119
63
|
/** Split a compound command into individual sub-commands.
|
|
120
64
|
* Splits on &&, ||, and ; operators, ignoring leading whitespace.
|
|
@@ -134,19 +78,63 @@ function stripHarmlessRedirects(cmd: string): string {
|
|
|
134
78
|
return cmd.replace(/\s*2\s*>\s*(\/dev\/null|&1)\b/g, "");
|
|
135
79
|
}
|
|
136
80
|
|
|
81
|
+
/** Blank out single- and double-quoted substrings so operators (>,
|
|
82
|
+
* >>, &&) inside quoted arguments don't trigger destructive-pattern
|
|
83
|
+
* false-positives. Replaces quoted content and quote chars with spaces,
|
|
84
|
+
* preserving length so operators outside quotes still match.
|
|
85
|
+
* Advisory only — not a full shell parser. */
|
|
86
|
+
function stripQuoted(cmd: string): string {
|
|
87
|
+
let out = "";
|
|
88
|
+
let i = 0;
|
|
89
|
+
let inQuote = false;
|
|
90
|
+
while (i < cmd.length) {
|
|
91
|
+
const ch = cmd[i];
|
|
92
|
+
const code = cmd.charCodeAt(i);
|
|
93
|
+
if (inQuote) {
|
|
94
|
+
if (code === 0x5c) {
|
|
95
|
+
out += " ";
|
|
96
|
+
i += 2;
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
out += " ";
|
|
100
|
+
if (code === 0x22 || code === 0x27) {
|
|
101
|
+
inQuote = false;
|
|
102
|
+
}
|
|
103
|
+
i++;
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
if (code === 0x22 || code === 0x27) {
|
|
107
|
+
inQuote = true;
|
|
108
|
+
out += " ";
|
|
109
|
+
i++;
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
out += ch;
|
|
113
|
+
i++;
|
|
114
|
+
}
|
|
115
|
+
return out;
|
|
116
|
+
}
|
|
117
|
+
|
|
137
118
|
export function isSafeCommand(command: string): boolean {
|
|
138
|
-
|
|
139
|
-
return parts.every((part) => {
|
|
119
|
+
return splitCompoundCommand(command).every((part) => {
|
|
140
120
|
const cleaned = stripHarmlessRedirects(part);
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
return !isDestructive && isSafe;
|
|
121
|
+
if (REDIRECT_PATTERNS.some((p) => p.test(stripQuoted(cleaned)))) return false;
|
|
122
|
+
return !DESTRUCTIVE_PATTERNS.some((p) => p.test(cleaned));
|
|
144
123
|
});
|
|
145
124
|
}
|
|
146
125
|
|
|
147
126
|
const SKILL_TO_PHASE: Record<string, Phase> = {
|
|
148
|
-
brainstorming: "brainstorm",
|
|
149
|
-
"writing-plans": "plan",
|
|
127
|
+
"pwk-brainstorming": "brainstorm",
|
|
128
|
+
"pwk-writing-plans": "plan",
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
/** Phase-aware reminder appended after the user's message each turn while a gated phase is active.
|
|
132
|
+
* Returned as a message (not a system-prompt change) so it sits at the tail of the request and
|
|
133
|
+
* never invalidates the cached prefix. */
|
|
134
|
+
const PHASE_REMINDERS: Record<Exclude<Phase, null>, string> = {
|
|
135
|
+
brainstorm:
|
|
136
|
+
"[pi-workflow-kit] BRAINSTORM phase: read-only. No source edits; writes only under docs/plans/. No mutations.",
|
|
137
|
+
plan: "[pi-workflow-kit] PLAN phase: read-only. No source edits; writes only under docs/plans/. No mutations.",
|
|
150
138
|
};
|
|
151
139
|
|
|
152
140
|
/** Determine if a write/edit to filePath should be blocked during the given phase.
|
|
@@ -163,10 +151,84 @@ export function getCurrentPhase(): Phase {
|
|
|
163
151
|
}
|
|
164
152
|
|
|
165
153
|
let phase: Phase = null;
|
|
154
|
+
// True on the turn a gated phase is entered; consumed once by before_agent_start so the reminder
|
|
155
|
+
// is shown only on the first turn of the phase (and re-armed on any later phase change into it).
|
|
156
|
+
let pendingPhaseReminder = false;
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Manual guard override set by `/pwk-guard`.
|
|
160
|
+
* - null → "auto": enforcement follows the skill-driven phase (default).
|
|
161
|
+
* - "on" → force a read-only lock (enforce regardless of phase).
|
|
162
|
+
* - "off" → disable the guard entirely (escape hatch).
|
|
163
|
+
* Skill transitions still update `phase` while an override is active, so returning
|
|
164
|
+
* to `auto` recovers the correct state; enforcement itself ignores `phase`.
|
|
165
|
+
*/
|
|
166
|
+
let guardOverride: "on" | "off" | null = null;
|
|
167
|
+
|
|
168
|
+
/** Is the guard actively enforcing read-only right now? */
|
|
169
|
+
function enforceActive(): boolean {
|
|
170
|
+
if (guardOverride === "off") return false;
|
|
171
|
+
if (guardOverride === "on") return true;
|
|
172
|
+
return phase !== null;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** Label for the current enforcement context, used in block reasons and reminders. */
|
|
176
|
+
function enforceLabel(): string {
|
|
177
|
+
return guardOverride === "on" ? "GUARD ON" : phase ? phase.toUpperCase() : "";
|
|
178
|
+
}
|
|
166
179
|
|
|
167
180
|
export default function (pi: ExtensionAPI) {
|
|
168
181
|
pi.on("session_start", () => {
|
|
169
182
|
phase = null;
|
|
183
|
+
pendingPhaseReminder = false;
|
|
184
|
+
guardOverride = null;
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
// --- Manual override (escape hatch) -----------------------------------
|
|
188
|
+
// Phases are driven by `/skill:` commands; `/pwk-guard` lets the user pin the
|
|
189
|
+
// guard regardless of phase. `/pwk-guard auto` returns control to skill transitions.
|
|
190
|
+
pi.registerCommand("pwk-guard", {
|
|
191
|
+
description:
|
|
192
|
+
"Manual guard override: /pwk-guard on (force read-only lock) | off (disable guard) | auto (skill-driven phases, default).",
|
|
193
|
+
getArgumentCompletions: (prefix) => {
|
|
194
|
+
const p = (prefix ?? "").trim().toLowerCase();
|
|
195
|
+
const opts = [
|
|
196
|
+
{ value: "on", label: "on", description: "Force read-only lock (ignore skill phases)" },
|
|
197
|
+
{ value: "off", label: "off", description: "Disable the guard entirely" },
|
|
198
|
+
{ value: "auto", label: "auto", description: "Follow skill-driven phases (default)" },
|
|
199
|
+
];
|
|
200
|
+
const matched = opts.filter((o) => o.value.startsWith(p));
|
|
201
|
+
return matched.length ? matched : opts;
|
|
202
|
+
},
|
|
203
|
+
handler: async (args, ctx) => {
|
|
204
|
+
const arg = (args ?? "").trim().toLowerCase();
|
|
205
|
+
if (arg === "auto") {
|
|
206
|
+
guardOverride = null;
|
|
207
|
+
if (phase) pendingPhaseReminder = true; // re-announce the active gated phase, if any
|
|
208
|
+
ctx.ui.notify("Guard AUTO — enforcement follows skill-driven phases.", "info");
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
if (arg !== "on" && arg !== "off") {
|
|
212
|
+
ctx.ui.notify("Usage: /pwk-guard on | off | auto", "info");
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
guardOverride = arg;
|
|
216
|
+
pendingPhaseReminder = false; // override announces itself via the message below
|
|
217
|
+
pi.sendMessage({
|
|
218
|
+
customType: "pwk-guard:override",
|
|
219
|
+
content:
|
|
220
|
+
arg === "on"
|
|
221
|
+
? "[pi-workflow-kit] GUARD ON (manual override): read-only lock active — no source edits; writes only under docs/plans/. No mutations. Use /pwk-guard auto to resume skill-driven phases."
|
|
222
|
+
: "[pi-workflow-kit] GUARD OFF (manual override): enforcement disabled — writes and bash are unrestricted. Use /pwk-guard auto to resume skill-driven phases.",
|
|
223
|
+
display: false,
|
|
224
|
+
});
|
|
225
|
+
ctx.ui.notify(
|
|
226
|
+
arg === "on"
|
|
227
|
+
? "Guard ON — read-only lock active (skill phases ignored)."
|
|
228
|
+
: "Guard OFF — enforcement disabled (skill phases ignored).",
|
|
229
|
+
arg === "on" ? "warning" : "info",
|
|
230
|
+
);
|
|
231
|
+
},
|
|
170
232
|
});
|
|
171
233
|
|
|
172
234
|
pi.on("input", (event) => {
|
|
@@ -175,27 +237,65 @@ export default function (pi: ExtensionAPI) {
|
|
|
175
237
|
if (match) {
|
|
176
238
|
const skill = match[1];
|
|
177
239
|
if (skill in SKILL_TO_PHASE) {
|
|
178
|
-
|
|
240
|
+
const nextPhase = SKILL_TO_PHASE[skill];
|
|
241
|
+
if (phase !== nextPhase) {
|
|
242
|
+
phase = nextPhase;
|
|
243
|
+
// The reminder is a phase-entry cue; only relevant when the guard is auto-driven.
|
|
244
|
+
if (guardOverride === null) pendingPhaseReminder = true;
|
|
245
|
+
}
|
|
179
246
|
return;
|
|
180
247
|
}
|
|
181
248
|
}
|
|
182
|
-
|
|
249
|
+
// Phase transitions happen only via skills — no message keyword unlocks the plan phase.
|
|
250
|
+
// Run /skill:pwk-executing-tasks (or any non-gated skill) to leave a gated phase.
|
|
251
|
+
if (
|
|
252
|
+
text.startsWith("/skill:pwk-executing-tasks") ||
|
|
253
|
+
text.startsWith("/skill:pwk-finalizing") ||
|
|
254
|
+
text.startsWith("/skill:pwk-code-review") ||
|
|
255
|
+
text.startsWith("/skill:pwk-diagnose") ||
|
|
256
|
+
text.startsWith("/skill:pwk-status")
|
|
257
|
+
) {
|
|
183
258
|
phase = null;
|
|
184
259
|
}
|
|
185
260
|
});
|
|
186
261
|
|
|
262
|
+
// Show the phase reminder exactly once: on the first turn of a gated phase (before_agent_start
|
|
263
|
+
// fires before the LLM is called). It is NOT repeated every turn. Re-armed on any phase change.
|
|
264
|
+
// Returned as a tail-appended custom message so it never touches the cached system-prompt prefix.
|
|
265
|
+
//
|
|
266
|
+
// Note: a soft reminder on every *permitted* bash call is not possible — tool_call can only
|
|
267
|
+
// return { block, reason }, not a message. So at bash time the reminder surfaces only when a
|
|
268
|
+
// destructive command is actually blocked (the reactive `reason` in the tool_call handler).
|
|
269
|
+
pi.on("before_agent_start", async () => {
|
|
270
|
+
if (!phase || !pendingPhaseReminder) return {};
|
|
271
|
+
pendingPhaseReminder = false;
|
|
272
|
+
// Override states announce themselves via the /pwk-guard handler; this reminder
|
|
273
|
+
// is the auto-mode phase-entry cue only.
|
|
274
|
+
if (guardOverride !== null) return {};
|
|
275
|
+
return {
|
|
276
|
+
message: {
|
|
277
|
+
customType: "pwk-phase-reminder",
|
|
278
|
+
content: PHASE_REMINDERS[phase],
|
|
279
|
+
display: false,
|
|
280
|
+
},
|
|
281
|
+
};
|
|
282
|
+
});
|
|
283
|
+
|
|
187
284
|
pi.on("tool_call", (event, ctx) => {
|
|
188
|
-
if (!
|
|
285
|
+
if (!enforceActive()) return;
|
|
286
|
+
const label = enforceLabel();
|
|
287
|
+
const manual = guardOverride === "on";
|
|
288
|
+
const scope = manual ? "manual read-only lock" : `${label.toLowerCase()} phase`;
|
|
189
289
|
|
|
190
290
|
if (event.toolName === "bash") {
|
|
191
291
|
const command = (event.input as { command?: string }).command ?? "";
|
|
192
292
|
if (!isSafeCommand(command)) {
|
|
193
293
|
if (ctx.hasUI) {
|
|
194
|
-
ctx.ui.notify(`Blocked bash command
|
|
294
|
+
ctx.ui.notify(`Blocked bash command (${scope}): ${command}`, "warning");
|
|
195
295
|
}
|
|
196
296
|
return {
|
|
197
297
|
block: true,
|
|
198
|
-
reason: `⚠️ ${
|
|
298
|
+
reason: `⚠️ ${label}: read-only — no source writes or destructive bash. Only read-only commands are permitted.\nBlocked command: ${command}`,
|
|
199
299
|
};
|
|
200
300
|
}
|
|
201
301
|
return;
|
|
@@ -209,15 +309,14 @@ export default function (pi: ExtensionAPI) {
|
|
|
209
309
|
if (!shouldBlockFilePath(filePath, ctx.cwd)) return;
|
|
210
310
|
|
|
211
311
|
if (ctx.hasUI) {
|
|
212
|
-
ctx.ui.notify(
|
|
213
|
-
`Blocked ${event.toolName} to ${filePath} during ${phase} phase. Only docs/plans/ is writable.`,
|
|
214
|
-
"warning",
|
|
215
|
-
);
|
|
312
|
+
ctx.ui.notify(`Blocked ${event.toolName} to ${filePath} (${scope}). Only docs/plans/ is writable.`, "warning");
|
|
216
313
|
}
|
|
217
314
|
|
|
218
315
|
return {
|
|
219
316
|
block: true,
|
|
220
|
-
reason: `⚠️ ${
|
|
317
|
+
reason: `⚠️ ${label}: Cannot ${event.toolName} to ${filePath}. Only docs/plans/ is writable${
|
|
318
|
+
manual ? " under the manual read-only lock" : " during brainstorming and planning"
|
|
319
|
+
}.`,
|
|
221
320
|
};
|
|
222
321
|
});
|
|
223
322
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tianhai/pi-workflow-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Enforce structured brainstorm→plan→execute→finalize workflow with TDD discipline in AI coding agents",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
@@ -27,7 +27,11 @@
|
|
|
27
27
|
"files": [
|
|
28
28
|
"extensions/",
|
|
29
29
|
"skills/",
|
|
30
|
-
"
|
|
30
|
+
"agents/",
|
|
31
|
+
"docs/developer-usage-guide.md",
|
|
32
|
+
"docs/workflow-phases.md",
|
|
33
|
+
"docs/oversight-model.md",
|
|
34
|
+
"docs/lessons.md",
|
|
31
35
|
"LICENSE",
|
|
32
36
|
"README.md"
|
|
33
37
|
],
|
|
@@ -39,13 +43,25 @@
|
|
|
39
43
|
"skills"
|
|
40
44
|
]
|
|
41
45
|
},
|
|
46
|
+
"pi-subagents": {
|
|
47
|
+
"agents": [
|
|
48
|
+
"./agents"
|
|
49
|
+
]
|
|
50
|
+
},
|
|
42
51
|
"peerDependencies": {
|
|
43
52
|
"@earendil-works/pi-coding-agent": "*",
|
|
44
|
-
"@sinclair/typebox": "*"
|
|
53
|
+
"@sinclair/typebox": "*",
|
|
54
|
+
"pi-subagents": "*"
|
|
55
|
+
},
|
|
56
|
+
"peerDependenciesMeta": {
|
|
57
|
+
"pi-subagents": {
|
|
58
|
+
"optional": true
|
|
59
|
+
}
|
|
45
60
|
},
|
|
46
61
|
"devDependencies": {
|
|
47
62
|
"@biomejs/biome": "^2.3.15",
|
|
48
63
|
"@earendil-works/pi-coding-agent": "*",
|
|
64
|
+
"@types/node": "^25.9.2",
|
|
49
65
|
"vitest": "^4.0.18"
|
|
50
66
|
}
|
|
51
67
|
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pwk-brainstorming
|
|
3
|
+
description: "Use this before any creative work — creating features, building components, adding functionality, or modifying behavior. Explores intent and design before implementation. Use this skill whenever the user describes something they want to build, change, or improve, even if they don't say 'brainstorm' — phrases like 'I want to add X', 'let's build Y', 'we need a way to Z', or 'help me design' all apply."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Brainstorming
|
|
7
|
+
|
|
8
|
+
Read-only exploration. You may **not** edit or create any files except under `docs/plans/`.
|
|
9
|
+
|
|
10
|
+
## Granularity
|
|
11
|
+
|
|
12
|
+
**A design doc is one pull request; a requirement is one testable slice within it.** Two decomposition levels answer two different questions:
|
|
13
|
+
|
|
14
|
+
- **How many PRs?** — split a large issue into **multiple design docs** only when each part could ship as its own PR (independently reviewable and mergeable). Each design doc runs its own plan → execute → finalize pipeline → its own PR. Example: "add OAuth" and "add dark mode" are unrelated → two design docs.
|
|
15
|
+
- **How to build one PR incrementally?** — within a design doc, decompose the work into **requirements**, each "one testable behavior." The plan covers all of them; the executor builds them one at a time. Example: "add OAuth" = R1 redirect flow, R2 token refresh, R3 error states → one design doc, three requirements, one PR.
|
|
16
|
+
|
|
17
|
+
Requirements are the leaf unit — never split further. Decision rule: **could this part be reviewed and merged on its own?** Yes → separate design doc. No → one design doc, multiple requirements. Most work is a single design doc; splitting is opt-in.
|
|
18
|
+
|
|
19
|
+
## Process
|
|
20
|
+
|
|
21
|
+
1. **Check git state** — run `git status` and `git log --oneline -5`. If there's uncommitted work, ask the user what to do with it first.
|
|
22
|
+
2. **Discovery** *(skip if this is a brand-new repo with no `docs/plans/`)* — glob `docs/plans/*-design.md`. Report active topics found (e.g. `In-flight: auth (plan), billing (brainstorm)`). Multiple designs may run in parallel. If the new idea continues an existing topic, ask the human whether to extend it or start fresh before designing.
|
|
23
|
+
3. **Understand the idea** — read existing code, docs, and recent commits. Grep for related functionality, check package.json/dependencies and module structure. **Check `docs/lessons.md`** if it exists — known constraints and patterns may affect the design. Read only what's necessary to ground the design — don't read the entire codebase. Ask questions to refine the idea. Prefer multiple choice when possible. After each question, check: can you clearly articulate (a) what the user wants to build, (b) why, and (c) key constraints? If yes, present your understanding as a short summary and ask: "Should I proceed with this, or is there more to add?" The human decides when to move on.
|
|
24
|
+
4. **Explore approaches** — propose 2-3 approaches. For each approach, sketch the concrete interface (types, method signatures, example caller code) so the comparison is grounded in actual code, not abstract descriptions. Lead with your recommendation.
|
|
25
|
+
5. **Present the design** — break it into focused sections. Each section should be one screen of reading. Present each section to the human and wait for approval before continuing. Cover: architecture, components, data flow, error handling, testing. On feedback, incorporate it and re-present the revised section.
|
|
26
|
+
|
|
27
|
+
When a significant architectural decision is identified, offer to write a lightweight ADR to `docs/adr/`. Only write an ADR when all three are true:
|
|
28
|
+
|
|
29
|
+
1. **Hard to reverse** — changing your mind later has meaningful cost
|
|
30
|
+
2. **Surprising without context** — a future reader will wonder "why?"
|
|
31
|
+
3. **A real trade-off** — there were genuine alternatives
|
|
32
|
+
|
|
33
|
+
ADR format — a title and 1-3 sentences covering context, decision, and why:
|
|
34
|
+
|
|
35
|
+
```markdown
|
|
36
|
+
# <Short title of the decision>
|
|
37
|
+
|
|
38
|
+
<1-3 sentences: context, decision, and why.>
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
ADRs live under `docs/adr/` permanently — they are institutional memory, never archived.
|
|
42
|
+
|
|
43
|
+
6. **Write the design doc** — save it to `docs/plans/YYYY-MM-DD-<topic>-design.md` as a descriptive document (not a task list). **Open with a `## Requirements` list** — each requirement one testable behavior the user will get (`pwk-writing-plans` derives acceptance criteria + integration tests per requirement). Then cover: problem, approaches considered, architecture, components, data flow, error handling, and testing.
|
|
44
|
+
|
|
45
|
+
If the design touches any production-risk area — database schema changes or migrations, authentication or authorization, external API or service integrations, concurrency or batch processing, file uploads or large data flows, Redis/caching/message queues — add a short `## Production-risk areas` section noting them. `pwk-writing-plans` carries these notes into the plan, and `pwk-code-review` audits them after each requirement.
|
|
46
|
+
|
|
47
|
+
**Splitting large issues:** if the work is large enough to be multiple PRs, propose splitting it into multiple design docs (one per sub-issue, each with its own `<topic>`) and get the human's approval first — see [Granularity](#granularity) for the decision rule. Each design doc then runs its own pipeline.
|
|
48
|
+
|
|
49
|
+
Branch creation and workspace setup happen at the end of `/skill:pwk-writing-plans` (after the plan is approved); the design + plan docs are committed at the start of `pwk-executing-tasks`. Until then the session is read-only and uncommitted.
|
|
50
|
+
|
|
51
|
+
## Principles
|
|
52
|
+
|
|
53
|
+
- One question at a time
|
|
54
|
+
- YAGNI — remove unnecessary features
|
|
55
|
+
- Design for testability
|
|
56
|
+
- Always explore alternatives before settling
|
|
57
|
+
|
|
58
|
+
## After the design
|
|
59
|
+
|
|
60
|
+
Ask: "Ready to plan? Run `/skill:pwk-writing-plans`"
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pwk-code-review
|
|
3
|
+
description: "Review a completed requirement's code — trace the logic, check spec alignment against the acceptance criteria, fix code smells, and run a production hazard check. Runs after each requirement in pwk-executing-tasks. Unlocked: may edit code to apply smell fixes."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Code Review
|
|
7
|
+
|
|
8
|
+
Review the code just implemented for a requirement. **Unlocked** — you may edit code to apply smell fixes; flag everything else as a follow-up.
|
|
9
|
+
|
|
10
|
+
## Process
|
|
11
|
+
|
|
12
|
+
1. **Identify the scope** — the requirement just completed (from the progress file / most recent commit). Read its acceptance criteria and integration tests from the plan doc. Run `git log --oneline -5` and `git diff` to see exactly what changed for this requirement.
|
|
13
|
+
|
|
14
|
+
2. **🔍 Code tracing** — trace the new/changed code paths end-to-end against the integration tests. For each path: does data flow correctly from entry to the asserted outcome? Note any branch the tests don't exercise, any dead branch, any path where the trace breaks.
|
|
15
|
+
|
|
16
|
+
3. **📐 Spec alignment** — 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.
|
|
17
|
+
|
|
18
|
+
4. **🧹 Code smells — fix these directly:**
|
|
19
|
+
- Shallow modules (interface nearly as complex as the implementation)
|
|
20
|
+
- Duplication
|
|
21
|
+
- Missing seams / premature abstraction
|
|
22
|
+
- Poor naming, magic values, dead code
|
|
23
|
+
Apply the fix, re-run the integration tests (must stay green), and commit. If a smell needs a refactor large enough to risk the requirement, **flag** it instead of applying.
|
|
24
|
+
|
|
25
|
+
5. **⚠️ Production hazard check** — audit the changed code against the high-risk hazards. For each, write `[SAFE]` (1-line justification) or `[TRIGGERED]` (concrete mitigation):
|
|
26
|
+
1. **Unbounded operations** — multi-key deletions/scans (`KEYS`, raw `SCAN` loops), or full-table loads filtered in memory.
|
|
27
|
+
2. **Missing indexes** — hot queries on unindexed columns (table scans under load).
|
|
28
|
+
3. **Unbounded concurrency** — unthrottled fan-out (`Promise.all` without batch limits).
|
|
29
|
+
4. **Long-running transactions** — holding DB connections/locks across slow external calls.
|
|
30
|
+
5. **Query/command interpolation** — raw variables merged into SQL or shell (injection).
|
|
31
|
+
6. **Unrestricted uploads / temp flooding** — uploads to local temp without limits or `finally` cleanup.
|
|
32
|
+
7. **Silent swallowing loops** — background workers catching and suppressing exceptions without logging/back-off.
|
|
33
|
+
Also check the design's `## Production-risk areas`, if any.
|
|
34
|
+
|
|
35
|
+
6. **Report** — summarize: tracing findings, spec gaps, smells fixed (with commits), hazards `[TRIGGERED]`. Non-trivial findings become follow-up items — the user decides whether to address now or defer.
|
|
36
|
+
7. **Mark done** — update the requirement's progress-file row from `🔎 review` to `✅ done`. Done means reviewed, not just committed.
|
|
37
|
+
|
|
38
|
+
## Principles
|
|
39
|
+
|
|
40
|
+
- **Tracing and spec alignment are the core** — they catch what tests miss: untested branches, missing criteria, scope creep.
|
|
41
|
+
- **Fix smells in place; flag everything else.** Don't redesign.
|
|
42
|
+
- **Be proportional** — a one-function change doesn't need the depth of a batch pipeline.
|
|
43
|
+
|
|
44
|
+
## After the review
|
|
45
|
+
|
|
46
|
+
Return to `/skill:pwk-executing-tasks` for the next requirement, or `/skill:pwk-finalizing` if all requirements are done.
|