peaks-cli 1.1.2 → 1.2.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.
Files changed (37) hide show
  1. package/README.md +97 -3
  2. package/dist/src/cli/commands/core-artifact-commands.js +47 -3
  3. package/dist/src/cli/commands/gate-commands.d.ts +3 -0
  4. package/dist/src/cli/commands/gate-commands.js +103 -0
  5. package/dist/src/cli/commands/hooks-commands.d.ts +3 -0
  6. package/dist/src/cli/commands/hooks-commands.js +75 -0
  7. package/dist/src/cli/commands/request-commands.js +1 -1
  8. package/dist/src/cli/commands/sop-commands.d.ts +3 -0
  9. package/dist/src/cli/commands/sop-commands.js +215 -0
  10. package/dist/src/cli/index.js +12 -0
  11. package/dist/src/cli/program.js +47 -2
  12. package/dist/src/services/dashboard/project-dashboard-service.js +5 -3
  13. package/dist/src/services/mode/mode-enforcement.js +2 -1
  14. package/dist/src/services/skills/hooks-settings-service.d.ts +45 -0
  15. package/dist/src/services/skills/hooks-settings-service.js +167 -0
  16. package/dist/src/services/sop/gate-enforce-service.d.ts +33 -0
  17. package/dist/src/services/sop/gate-enforce-service.js +168 -0
  18. package/dist/src/services/sop/sop-advance-service.d.ts +74 -0
  19. package/dist/src/services/sop/sop-advance-service.js +115 -0
  20. package/dist/src/services/sop/sop-check-service.d.ts +29 -0
  21. package/dist/src/services/sop/sop-check-service.js +148 -0
  22. package/dist/src/services/sop/sop-paths.d.ts +62 -0
  23. package/dist/src/services/sop/sop-paths.js +92 -0
  24. package/dist/src/services/sop/sop-registry-service.d.ts +46 -0
  25. package/dist/src/services/sop/sop-registry-service.js +89 -0
  26. package/dist/src/services/sop/sop-service.d.ts +47 -0
  27. package/dist/src/services/sop/sop-service.js +211 -0
  28. package/dist/src/services/sop/sop-types.d.ts +88 -0
  29. package/dist/src/services/sop/sop-types.js +11 -0
  30. package/dist/src/shared/paths.d.ts +1 -1
  31. package/dist/src/shared/paths.js +2 -1
  32. package/dist/src/shared/version.d.ts +1 -1
  33. package/dist/src/shared/version.js +1 -1
  34. package/package.json +1 -1
  35. package/schemas/sop-manifest.schema.json +52 -0
  36. package/skills/peaks-sop/SKILL.md +192 -0
  37. package/skills/peaks-sop/references/sop-authoring.md +161 -0
@@ -0,0 +1,161 @@
1
+ # SOP Authoring Reference
2
+
3
+ Concrete reference for the `peaks-sop` skill: manifest shape, gate cookbook, the
4
+ interview → generate → debug loop, and security notes. The skill drives the
5
+ `peaks sop` CLI on the user's behalf — this file is the detail behind that.
6
+
7
+ ## Where files live
8
+
9
+ SOP **definitions** live in one of two layers:
10
+ - **Global** `~/.peaks/sops/<sop-id>/sop.json` (+ `SKILL.md`) — personal, reusable across every project. `init` / `lint` / `register` default here.
11
+ - **Project** `<project>/.peaks/sops/<sop-id>/sop.json` — committed into the repo and team-shared. Pass `--project <repo>` to `init` / `lint` / `register` to use this layer. The project layer **wins** over global for the same id; execution reads (`check`/`advance`/enforce) and `sop registry --project` see the merged view.
12
+
13
+ A SOP's **run-state** is always per-project: `<project>/.peaks/sop-state/<sop-id>/state.json` (git-ignored — runtime, not shared). `check` / `advance` take `--project` (default: current directory) — that says which project the gate paths resolve against, whose progress advances, and which definition layer wins.
14
+
15
+ Use the **project layer** when you want a workflow's gates to bind the whole team (commit the SOP, install the hook in the repo's `.claude/settings.json`); use **global** for your own repeatable procedures across many repos.
16
+
17
+ ## Manifest shape (`~/.peaks/sops/<sop-id>/sop.json`)
18
+
19
+ ```json
20
+ {
21
+ "id": "team-release",
22
+ "name": "Team Release",
23
+ "description": "Gates that must pass before we ship a release.",
24
+ "phases": ["draft", "review", "ship"],
25
+ "gates": [
26
+ { "id": "changelog", "phase": "ship", "check": { "type": "file-exists", "path": "CHANGELOG.md" } },
27
+ { "id": "no-fixme", "phase": "review", "check": { "type": "grep", "file": "src/index.ts", "pattern": "FIXME" } },
28
+ { "id": "tests", "phase": "ship", "check": { "type": "command", "run": ["npm", "test"] } }
29
+ ]
30
+ }
31
+ ```
32
+
33
+ Rules the lint enforces (so generate the manifest to satisfy them):
34
+
35
+ - `id` — lowercase kebab, starts alphanumeric; must NOT collide with the reserved `peaks-` / `peaks` namespace; must match the directory name.
36
+ - `phases` — at least one; no duplicates.
37
+ - each gate `id` — lowercase kebab, unique within the SOP.
38
+ - each gate `phase` — must be one of the declared `phases`.
39
+ - each gate `check` — a known type with its required fields present.
40
+
41
+ ## Gate cookbook
42
+
43
+ | intent | check |
44
+ |--------|-------|
45
+ | a file must exist before a phase | `{ "type": "file-exists", "path": "CHANGELOG.md" }` |
46
+ | a doc must contain a marker | `{ "type": "grep", "file": "README.md", "pattern": "## Release notes" }` |
47
+ | there must be NO leftover markers | `{ "type": "grep", "file": "post.md", "pattern": "TODO", "absent": true }` — passes only when the pattern is absent. Pure text, no `--allow-commands`, cross-platform. Prefer this over a `! grep` command gate. |
48
+ | tests must pass | `{ "type": "command", "run": ["npm", "test"] }` (requires `--allow-commands`) |
49
+ | a build must succeed | `{ "type": "command", "run": ["npm", "run", "build"] }` (requires `--allow-commands`) |
50
+ | a command must FAIL to pass the gate | add `"expectExitZero": false` to the command check |
51
+
52
+ Gate verdicts:
53
+
54
+ - `pass` — the condition is met.
55
+ - `fail` — evaluated, condition not met (e.g. file missing, pattern absent, command exited wrong).
56
+ - `blocked` — could not evaluate: path escaped the project root, target file unreadable, command not permitted (`--allow-commands` missing) or failed to spawn / timed out.
57
+
58
+ ## Cross-domain examples (SOPs are not just for code)
59
+
60
+ The release example above is one domain. The same engine governs any gated workflow — often the higher-value use. Lead the interview with the user's own domain.
61
+
62
+ **Content publishing** (`~/.peaks/sops/blog-publish/sop.json`):
63
+
64
+ ```json
65
+ {
66
+ "id": "blog-publish",
67
+ "name": "Blog Publish",
68
+ "phases": ["draft", "edit", "publish"],
69
+ "gates": [
70
+ { "id": "draft-exists", "phase": "edit", "check": { "type": "file-exists", "path": "posts/current.md" } },
71
+ { "id": "no-placeholders", "phase": "publish", "check": { "type": "grep", "file": "posts/current.md", "pattern": "TODO|TKTK", "absent": true } }
72
+ ]
73
+ }
74
+ ```
75
+
76
+ Note `no-placeholders` uses `grep` with `absent: true` — "must not contain a placeholder" — so it needs no `--allow-commands` and works on any OS. This is the single most common non-engineering gate.
77
+
78
+ **Compliance / approval** (`~/.peaks/sops/vendor-approval/sop.json`):
79
+
80
+ ```json
81
+ {
82
+ "id": "vendor-approval",
83
+ "name": "Vendor Approval",
84
+ "phases": ["submitted", "reviewed", "approved"],
85
+ "gates": [
86
+ { "id": "review-notes", "phase": "reviewed", "check": { "type": "file-exists", "path": "vendors/acme/review.md" } },
87
+ { "id": "signed-off", "phase": "approved", "check": { "type": "grep", "file": "vendors/acme/review.md", "pattern": "Status:\\s*Approved" } }
88
+ ]
89
+ }
90
+ ```
91
+
92
+ **Data pipeline** (`~/.peaks/sops/dataset-release/sop.json`):
93
+
94
+ ```json
95
+ {
96
+ "id": "dataset-release",
97
+ "name": "Dataset Release",
98
+ "phases": ["raw", "cleaned", "validated"],
99
+ "gates": [
100
+ { "id": "schema-valid", "phase": "validated", "check": { "type": "command", "run": ["python", "scripts/validate.py", "data/cleaned.csv"] } }
101
+ ]
102
+ }
103
+ ```
104
+
105
+ These reuse the same three gate types — only the phases and the file/command targets differ. A human-judgment step ("the editor approved") is reified into a file/grep signal (an `approval.md` file, or a status line matching "Approved"), as shown above.
106
+
107
+ ## Guards: un-bypassable enforcement (optional)
108
+
109
+ A gate normally only blocks `peaks sop advance`. To make it physically un-bypassable by the agent, add **guards** that bind an irreversible Bash action to a phase, and have the user install the PreToolUse hook.
110
+
111
+ ```json
112
+ {
113
+ "id": "team-release",
114
+ "name": "Team Release",
115
+ "phases": ["draft", "review", "ship"],
116
+ "gates": [
117
+ { "id": "no-fixme", "phase": "ship", "check": { "type": "grep", "file": "src/index.ts", "pattern": "FIXME", "absent": true } }
118
+ ],
119
+ "guards": [
120
+ { "phase": "ship", "bash": "git +push" },
121
+ { "phase": "ship", "bash": "npm +publish" }
122
+ ]
123
+ }
124
+ ```
125
+
126
+ Semantics: a Bash command matching a guard's `bash` regex = entering that phase, so the phase's gates must pass first. With the hook installed (`peaks hooks install --project <repo>`), the agent literally cannot run `git push` / `npm publish` while `no-fixme` fails — Claude Code receives `permissionDecision: "deny"` before any permission check (holds even under `--dangerously-skip-permissions`).
127
+
128
+ `bash` rules:
129
+ - It is a **JS regex written inside JSON**. Escape backslashes: `"git\\s+push"`, or sidestep escaping with `"git +push"` (one-or-more spaces). `peaks sop lint` rejects an uncompilable regex (`GUARD_INVALID_PATTERN`) and a guard on an undeclared phase (`GUARD_PHASE_UNKNOWN`).
130
+ - Keep patterns specific to the irreversible action (`git +push`, `npm +publish`, `gh +release`, a deploy script name) — not broad verbs.
131
+
132
+ Override once (hotfix): `peaks gate bypass --sop team-release --phase ship --reason "<why>" --project <repo>` — consumed by the next blocked command, capped per project per SOP.
133
+
134
+ `command`-type gates run during enforcement with commands enabled (installing the hook is the consent); each gate keeps its 30s timeout. Enforcement **fails open** on any internal error — only a real gate failure denies.
135
+
136
+ ## Interview → generate → debug loop
137
+
138
+ 1. **Interview.** Ask: what are the ordered stages of this workflow? For each stage, what must be true before you're allowed to enter it? Translate "must be true" into file-exists / grep / command checks. For "must NOT contain X", reach for `grep` + `absent: true`.
139
+ 2. **Scaffold.** `peaks sop init --id <id> --name "<name>" --apply --json`. Writes a starter `sop.json` + `SKILL.md` into the global `~/.peaks/sops/<id>/` (no `--project`).
140
+ 3. **Write the real manifest.** Replace the scaffold's example phases/gates with the interviewed ones by editing `sop.json` directly.
141
+ 4. **Lint loop.** `peaks sop lint --id <id> --json` → read findings → fix in `sop.json` → re-lint until `ok:true`. Add `--allow-commands` when the SOP uses command gates.
142
+ 5. **Gate test.** `peaks sop check --id <id> --gate <gate-id> --project <repo> --json` for each gate, in both the good state (expect `pass`) and a bad state (expect `fail`/`blocked`). `--project` is the project the gate paths resolve against (default: current directory).
143
+ 6. **Dry-run the flow.** `peaks sop advance --id <id> --to <phase> --project <repo> --dry-run --json` — confirms a failing gate (or a forward phase skip) truly blocks, without recording anything.
144
+ 7. **Register.** `peaks sop register --id <id> --json` (preview with `--dry-run` first). Now the SOP is enumerable in the global registry and can be set as the active skill via presence.
145
+
146
+ ## Security notes (always surface to the user)
147
+
148
+ - `command` gates run user-defined commands. They are refused unless the user explicitly passes `--allow-commands` to `lint` / `register` / `check` / `advance`. Tell the user a SOP needs `--allow-commands` and what the command does before running it.
149
+ - Commands run with an argv array (no shell, no injection), a timeout cap, and cwd pinned to the project root. The command executable itself is not sandboxed — the trust boundary is whoever authored the SOP, the same as an npm script or Makefile target.
150
+ - `file-exists` / `grep` paths are confined inside the project root; an out-of-bounds path returns `blocked`, never reads outside the project.
151
+ - Side-effecting commands (`init` / `register` / `advance`) support `--dry-run` to preview without writing.
152
+
153
+ ## Bypassing a blocked advance
154
+
155
+ If the user must move forward despite a failing gate **or a forward phase skip** (e.g. a hotfix that skips review), advancement can be bypassed explicitly:
156
+
157
+ ```bash
158
+ peaks sop advance --id <id> --to <phase> --project <repo> --allow-incomplete --reason "<why>" --json
159
+ ```
160
+
161
+ `--allow-incomplete` bypasses both the gate checks and the phase-order check. In assisted/strict mode this also requires `--confirm`, and each SOP has a per-project bypass cap. Always record a real reason — the bypass is logged in that project's SOP history.