peaks-loop 4.0.0-beta.2 → 4.0.0-beta.4

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 (72) hide show
  1. package/CHANGELOG.md +81 -0
  2. package/README-en.md +16 -3
  3. package/README.md +16 -3
  4. package/dist/src/cli/commands/asset-commands.d.ts +45 -0
  5. package/dist/src/cli/commands/asset-commands.js +404 -0
  6. package/dist/src/cli/commands/bee-commands.d.ts +26 -0
  7. package/dist/src/cli/commands/bee-commands.js +169 -0
  8. package/dist/src/cli/commands/evolution-commands.d.ts +27 -0
  9. package/dist/src/cli/commands/evolution-commands.js +393 -0
  10. package/dist/src/cli/commands/loop-commands.d.ts +19 -13
  11. package/dist/src/cli/commands/loop-commands.js +147 -13
  12. package/dist/src/cli/commands/sediment-commands.js +9 -0
  13. package/dist/src/cli/commands/skill-loop-engineering-readiness-commands.d.ts +67 -0
  14. package/dist/src/cli/commands/skill-loop-engineering-readiness-commands.js +173 -0
  15. package/dist/src/cli/program.js +17 -0
  16. package/dist/src/services/crystallization/crystallization-service.d.ts +239 -0
  17. package/dist/src/services/crystallization/crystallization-service.js +397 -0
  18. package/dist/src/services/crystallization/crystallization-store.d.ts +75 -0
  19. package/dist/src/services/crystallization/crystallization-store.js +193 -0
  20. package/dist/src/services/crystallization/crystallization-types.d.ts +313 -0
  21. package/dist/src/services/crystallization/crystallization-types.js +299 -0
  22. package/dist/src/services/crystallization/evidence-brief-builder.d.ts +237 -0
  23. package/dist/src/services/crystallization/evidence-brief-builder.js +240 -0
  24. package/dist/src/services/evolution/evolution-service.d.ts +137 -0
  25. package/dist/src/services/evolution/evolution-service.js +281 -0
  26. package/dist/src/services/evolution/evolution-store.d.ts +57 -0
  27. package/dist/src/services/evolution/evolution-store.js +232 -0
  28. package/dist/src/services/evolution/evolution-types.d.ts +706 -0
  29. package/dist/src/services/evolution/evolution-types.js +286 -0
  30. package/dist/src/services/evolution/independent-evaluator-runner.d.ts +90 -0
  31. package/dist/src/services/evolution/independent-evaluator-runner.js +106 -0
  32. package/dist/src/services/evolution/regression-skeptic-runner.d.ts +64 -0
  33. package/dist/src/services/evolution/regression-skeptic-runner.js +113 -0
  34. package/dist/src/services/loop/loop-bee-relation-service.d.ts +107 -0
  35. package/dist/src/services/loop/loop-bee-relation-service.js +211 -0
  36. package/dist/src/services/loop/loop-bee-relation-store.d.ts +53 -0
  37. package/dist/src/services/loop/loop-bee-relation-store.js +130 -0
  38. package/dist/src/services/loop/loop-bee-relation-types.d.ts +118 -0
  39. package/dist/src/services/loop/loop-bee-relation-types.js +114 -0
  40. package/dist/src/services/loop/loop-release-service.d.ts +46 -0
  41. package/dist/src/services/loop/loop-release-service.js +66 -0
  42. package/dist/src/services/loop/loop-release-store.d.ts +55 -0
  43. package/dist/src/services/loop/loop-release-store.js +134 -0
  44. package/dist/src/services/loop/loop-release-types.d.ts +250 -0
  45. package/dist/src/services/loop/loop-release-types.js +162 -0
  46. package/dist/src/services/share/bundle-reader.d.ts +100 -0
  47. package/dist/src/services/share/bundle-reader.js +434 -0
  48. package/dist/src/services/share/bundle-types.d.ts +392 -0
  49. package/dist/src/services/share/bundle-types.js +318 -0
  50. package/dist/src/services/share/bundle-writer.d.ts +97 -0
  51. package/dist/src/services/share/bundle-writer.js +429 -0
  52. package/dist/src/services/share/run-state-contract.d.ts +133 -0
  53. package/dist/src/services/share/run-state-contract.js +138 -0
  54. package/dist/src/services/skillhub/release-import.js +7 -2
  55. package/dist/src/services/skillhub/release-retain.js +6 -1
  56. package/dist/src/services/skillhub/sqlite-store.js +17 -1
  57. package/dist/src/services/skillhub/types.d.ts +10 -0
  58. package/dist/src/services/standards/loop-engineering-lint.d.ts +38 -0
  59. package/dist/src/services/standards/loop-engineering-lint.js +101 -0
  60. package/dist/src/services/standards/loop-engineering-readiness-lint.d.ts +111 -0
  61. package/dist/src/services/standards/loop-engineering-readiness-lint.js +248 -0
  62. package/dist/src/shared/version.d.ts +1 -1
  63. package/dist/src/shared/version.js +1 -1
  64. package/examples/video-demo/README.md +57 -0
  65. package/examples/video-demo/out/en-closing-960.png +0 -0
  66. package/examples/video-demo/out/zh-closing-960.png +0 -0
  67. package/examples/video-demo/preview/peaks-loop-demo-en.mp4 +0 -0
  68. package/examples/video-demo/preview/peaks-loop-demo.mp4 +0 -0
  69. package/package.json +6 -2
  70. package/skills/peaks-code/SKILL.md +13 -0
  71. package/skills/peaks-content/SKILL.md +272 -0
  72. package/skills/peaks-issue-fix-orchestrator/SKILL.md +307 -0
@@ -0,0 +1,248 @@
1
+ /**
2
+ * Loop Engineering Readiness linter (M6 — peak-spec §7.5 + §8.4).
3
+ *
4
+ * Pure function: takes the raw markdown text of a peaks-* SKILL.md
5
+ * file and returns either
6
+ * { ok: true, findings: [] }
7
+ * or
8
+ * { ok: false, findings: string[] }.
9
+ *
10
+ * The lint asserts three structural properties the spec binds every
11
+ * Loop-Engineering-participating peaks-* skill to:
12
+ *
13
+ * 1. The skill references the shared guideline file
14
+ * `.peaks/standards/loop-engineering-guidelines.md` (or its
15
+ * alias `.peaks/standards/loop-engineering-guidelines.md`).
16
+ * Reference may appear anywhere in the body — code fence,
17
+ * plain prose, or list item — as long as the literal token is
18
+ * present.
19
+ *
20
+ * 2. The skill does NOT introduce a CLI verb that bypasses the
21
+ * LLM-coordinated model. We flag lines that look like
22
+ * "Run `peaks <verb>` to …" where `<verb>` is not in the
23
+ * allowlist of LLM-owned sediment / asset / evolution verbs.
24
+ * The user types NL; the LLM runs the CLI on the user's behalf
25
+ * (RL-1 / Human-NL-Choice-Only).
26
+ *
27
+ * 3. The skill does NOT introduce a JSON / manifest
28
+ * hand-authoring surface. We flag lines containing phrases
29
+ * like "Edit the manifest", "write the JSON", "fill the JSON
30
+ * file", etc., when they are not part of an explicit allowlist
31
+ * (allowlist only references; never an instructional phrase).
32
+ *
33
+ * Each finding is a single line of the form:
34
+ * "<rule>: <offending excerpt>"
35
+ * so a downstream CLI can render it directly without further
36
+ * parsing.
37
+ *
38
+ * The lint intentionally does NOT touch any application-level
39
+ * code; it only parses a SKILL.md text. The peaks CLI command
40
+ * `peaks skill lint --category loop-engineering-readiness
41
+ * --path <skill-dir>`
42
+ * (registered in src/cli/commands/skill-loop-engineering-readiness.ts)
43
+ * reads the SKILL.md from disk and calls this function.
44
+ *
45
+ * Per RL-1 / Human-NL-Choice-Only and
46
+ * `.peaks/memory/two-forms-only-rule.md`, the user only ever picks
47
+ * via AskUserQuestion or describes in NL; the LLM runs the CLI.
48
+ * This lint is what stops a future peaks-* skill from regressing
49
+ * that rule at the textual layer.
50
+ */
51
+ /**
52
+ * The shared guideline file that every Loop-Engineering-participating
53
+ * peaks-* skill must reference. Both the canonical path and the
54
+ * common alias are accepted.
55
+ *
56
+ * The alias is intentionally identical to the canonical path here:
57
+ * the spec sometimes renders it as
58
+ * `.peaks/standards/loop-engineering-guidelines.md`
59
+ * with a slash prefix and sometimes without. We accept both.
60
+ */
61
+ export const LOOP_ENGINEERING_GUIDELINE_PATHS = [
62
+ '.peaks/standards/loop-engineering-guidelines.md',
63
+ ];
64
+ /**
65
+ * The allowlist of CLI verbs that a peaks-* SKILL.md may legitimately
66
+ * instruct an LLM to invoke on the user's behalf. These are the
67
+ * sediment / asset / evolution surface verbs introduced in
68
+ * 2026-07-07-loop-engineering-crystallization-design §7.4 plus the
69
+ * 18-verb sediment pool from 2026-07-04-peaks-maker-dynamic-skill-
70
+ * sediment-design §4.2.
71
+ *
72
+ * Any other `peaks <verb>` token that appears in the form
73
+ * "Run `peaks <verb>` to …"
74
+ * is treated as a CLI-verb-bypass attempt and flagged.
75
+ */
76
+ export const ALLOWED_CLI_VERBS = new Set([
77
+ // sediment pool verbs (M6 reference; peak-maker's owned surface)
78
+ 'add-segment',
79
+ 'add-bee',
80
+ 'refine-bee',
81
+ 'clone-bee',
82
+ 'promote',
83
+ 'retire',
84
+ 'dispose',
85
+ 'releases',
86
+ 'release-show',
87
+ 'release-diff',
88
+ 'export',
89
+ 'import',
90
+ 'gc-blobs',
91
+ 'list',
92
+ 'show',
93
+ 'search',
94
+ 'recent',
95
+ 'rebuild-index',
96
+ // loop crystallizer surface (§7.4)
97
+ 'loop',
98
+ 'crystallize',
99
+ // evolution ratchet surface (§7.4)
100
+ 'evolution',
101
+ 'propose',
102
+ 'evaluate',
103
+ 'revert',
104
+ 'mark-keep',
105
+ // umbrella asset surface (§7.4)
106
+ 'asset',
107
+ 'status',
108
+ // adapter surface (peaks-maker's owned skill runtime)
109
+ 'adapter',
110
+ // lint surface (this command's own kind)
111
+ 'lint',
112
+ 'standards',
113
+ 'skill',
114
+ 'ready',
115
+ ]);
116
+ /**
117
+ * Phrases that constitute a JSON / manifest hand-authoring surface.
118
+ * If a SKILL.md instructs the user to perform one of these actions,
119
+ * it violates RL-1 / Human-NL-Choice-Only and is flagged.
120
+ *
121
+ * Each entry is a lowercase substring matched against each line of
122
+ * the SKILL.md. The match is case-insensitive; the surrounding
123
+ * `<verb>` placeholders in the original spec are matched literally.
124
+ */
125
+ export const JSON_HAND_AUTHORING_PHRASES = [
126
+ 'edit the manifest',
127
+ 'edit your manifest',
128
+ 'edit the json',
129
+ 'edit the .json file',
130
+ 'write the json',
131
+ 'write the json manifest',
132
+ 'write the manifest json',
133
+ 'fill the json',
134
+ 'fill in the json',
135
+ 'fill in the manifest',
136
+ 'fill in the manifest.json',
137
+ 'hand-author the manifest',
138
+ 'hand-author the json',
139
+ 'open the manifest and',
140
+ 'open the manifest.json and',
141
+ 'manually edit the manifest',
142
+ 'manually edit the json',
143
+ ];
144
+ /**
145
+ * Phrases that explicitly reference a JSON / manifest surface but
146
+ * in an ALLOWED way — e.g. "the manifest is written by the CLI" or
147
+ * "see manifest schema for details". A line that contains one of
148
+ * these allowlisted phrases is NOT flagged even if it contains a
149
+ * JSON-hand-authoring phrase elsewhere on the same line.
150
+ *
151
+ * The allowlist is intentionally narrow; it must mention either
152
+ * the CLI as the writer OR a schema reference, NOT an instructional
153
+ * verb targeting the user.
154
+ */
155
+ export const JSON_REFERENCE_ALLOWLIST = [
156
+ 'the cli writes',
157
+ 'written by the cli',
158
+ 'written by peaks',
159
+ 'manifest schema',
160
+ 'see the manifest schema',
161
+ 'see manifest schema',
162
+ 'schema_version',
163
+ 'peaks.bundle/1',
164
+ 'peaks.loop/1',
165
+ 'peaks.bee/1',
166
+ ];
167
+ // ---------------------------------------------------------------------------
168
+ // Pure function
169
+ // ---------------------------------------------------------------------------
170
+ /**
171
+ * Lint a SKILL.md text for Loop Engineering readiness.
172
+ *
173
+ * @param skillMdText raw markdown text of a peaks-* SKILL.md
174
+ * @returns a ReadinessLintResult
175
+ */
176
+ export function lintSkillLoopEngineeringReadiness(skillMdText) {
177
+ const findings = [];
178
+ if (typeof skillMdText !== 'string' || skillMdText.trim().length === 0) {
179
+ return {
180
+ ok: false,
181
+ findings: ['skill-md-empty: SKILL.md text is empty or missing'],
182
+ };
183
+ }
184
+ checkGuidelineReference(skillMdText, findings);
185
+ checkCliVerbBypass(skillMdText, findings);
186
+ checkJsonHandAuthoring(skillMdText, findings);
187
+ if (findings.length > 0) {
188
+ return { ok: false, findings };
189
+ }
190
+ return { ok: true, findings: [] };
191
+ }
192
+ // ---------------------------------------------------------------------------
193
+ // Individual checks
194
+ // ---------------------------------------------------------------------------
195
+ function checkGuidelineReference(text, findings) {
196
+ // Strip code fences before checking — references inside ``` blocks
197
+ // are still valid references (the lint accepts them), but we strip
198
+ // so the substring match is unambiguous.
199
+ const stripped = text;
200
+ for (const ref of LOOP_ENGINEERING_GUIDELINE_PATHS) {
201
+ if (stripped.includes(ref)) {
202
+ return;
203
+ }
204
+ }
205
+ findings.push(`missing-guideline-reference: SKILL.md must reference ${LOOP_ENGINEERING_GUIDELINE_PATHS[0]} (spec §7.5 / §8.4 / RL-8)`);
206
+ }
207
+ function checkCliVerbBypass(text, findings) {
208
+ // Match lines that contain `peaks <verb>` in a run-this-command
209
+ // pattern: "Run `peaks <verb>` to …" or "`peaks <verb>` to …" or
210
+ // "use `peaks <verb>` to …".
211
+ //
212
+ // The regex is intentionally narrow: it requires backticks around
213
+ // `peaks <verb>` so prose that mentions "peaks <verb>" as a noun
214
+ // (e.g. "peaks skill sediment list") is not flagged. The verb
215
+ // token is captured as the first `[a-z][a-z0-9-]*` chunk after
216
+ // "peaks "; everything between the verb and the closing backtick
217
+ // is allowed (e.g. `peaks custom-evolve my-bee` — verb is
218
+ // "custom-evolve", args are "my-bee").
219
+ const lineRegex = /^\s*[-*]?\s*(?:Run|Use|Type|Execute)\s+`peaks\s+([a-z][a-z0-9-]*)[^`]*`/gim;
220
+ const lines = text.split(/\r?\n/);
221
+ for (let i = 0; i < lines.length; i++) {
222
+ const line = lines[i];
223
+ let m;
224
+ lineRegex.lastIndex = 0;
225
+ while ((m = lineRegex.exec(line)) !== null) {
226
+ const verb = m[1];
227
+ if (!ALLOWED_CLI_VERBS.has(verb)) {
228
+ const excerpt = line.trim().slice(0, 120);
229
+ findings.push(`cli-verb-bypass: line ${i + 1} introduces CLI verb \`peaks ${verb}\` to be typed by the user (${excerpt}) — only LLM-coordinated verbs from the sediment/asset/evolution surface are allowed (RL-1)`);
230
+ }
231
+ }
232
+ }
233
+ }
234
+ function checkJsonHandAuthoring(text, findings) {
235
+ const lines = text.split(/\r?\n/);
236
+ for (let i = 0; i < lines.length; i++) {
237
+ const line = lines[i];
238
+ const lowered = line.toLowerCase();
239
+ const matchedPhrase = JSON_HAND_AUTHORING_PHRASES.find((p) => lowered.includes(p));
240
+ if (!matchedPhrase)
241
+ continue;
242
+ const isAllowlisted = JSON_REFERENCE_ALLOWLIST.some((p) => lowered.includes(p));
243
+ if (isAllowlisted)
244
+ continue;
245
+ const excerpt = line.trim().slice(0, 120);
246
+ findings.push(`json-hand-authoring: line ${i + 1} introduces JSON / manifest hand-authoring surface ("${matchedPhrase}") — ${excerpt} (RL-1: user only picks or describes in NL)`);
247
+ }
248
+ }
@@ -1 +1 @@
1
- export declare const CLI_VERSION = "4.0.0-beta.2";
1
+ export declare const CLI_VERSION = "4.0.0-beta.4";
@@ -1 +1 @@
1
- export const CLI_VERSION = "4.0.0-beta.2";
1
+ export const CLI_VERSION = "4.0.0-beta.4";
@@ -0,0 +1,57 @@
1
+ # peaks-loop video demo
2
+
3
+ Remotion-based 30-second brand walk-through for peaks-loop 4.0. Mirrors `README.md`.
4
+
5
+ ## Composition
6
+
7
+ - Two compositions, one per locale:
8
+ - `peaks-loop-demo` — 30 seconds @ 30fps (900 frames), 1920×1080 — Chinese primary.
9
+ - `peaks-loop-demo-en` — same frame budget, English primary.
10
+ - Scene timeline (frozen in `src/Root.tsx`):
11
+ - 0–90 — **TitleScene** (`peaks-loop`, no ·, no emoji)
12
+ - 90–240 — **PhilosophyScene** — 5 hero philosophy items, bilingual
13
+ - 240–540 — **DemoScene × 3** — `写长任务代码` / `修 bug 当天发` / `接一个长跑的需求`
14
+ - 540–780 — **SedimentScene** — 3-beat `NL → MANIFEST → BEE`
15
+ - 780–900 — **ClosingScene** — `npm i -g peaks-loop` + repo-name vs skill-name arc + 5,439 tests stats
16
+
17
+ ## Visual style
18
+
19
+ - Background: `#0f172a` (slate-900) via `bg-brand-bg`
20
+ - Primary text: `#f8fafc` (slate-50) via `text-brand-fg`
21
+ - Accent: `#6366f1` (indigo-500) via `text-brand-accent`
22
+ - Stable badge: `#22c55e` (green-500)
23
+ - Mono font for kickers / install chip / chat bubbles; sans font for headlines
24
+
25
+ ## Public preview files (tracked)
26
+
27
+ The READMEs embed `<video>` tags that point at:
28
+
29
+ - `preview/peaks-loop-demo.mp4` (zh, 4.9 MB)
30
+ - `preview/peaks-loop-demo-en.mp4` (en, 5.0 MB)
31
+
32
+ These are produced by the render command below and committed to git so a clone (and the GitHub rendering) sees the same path. The shell snippet under **Render** keeps `preview/` in sync after a fresh build.
33
+
34
+ ## Render
35
+
36
+ ```bash
37
+ cd examples/video-demo
38
+ pnpm install
39
+ npx remotion render peaks-loop-demo out/peaks-loop-demo.mp4
40
+ npx remotion render peaks-loop-demo-en out/peaks-loop-demo-en.mp4
41
+
42
+ mkdir -p preview
43
+ cp out/peaks-loop-demo.mp4 preview/peaks-loop-demo.mp4
44
+ cp out/peaks-loop-demo-en.mp4 preview/peaks-loop-demo-en.mp4
45
+ ```
46
+
47
+ Fallback (no Chromium available):
48
+
49
+ ```bash
50
+ npx remotion still peaks-loop-demo out/still.png --frame=120
51
+ ```
52
+
53
+ ## Preview during dev
54
+
55
+ ```bash
56
+ npx remotion studio # http://localhost:3000
57
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "peaks-loop",
3
- "version": "4.0.0-beta.2",
3
+ "version": "4.0.0-beta.4",
4
4
  "description": "Loop Engineering CLI — workflow primitive / loop guards / evaluators / slice orchestration",
5
5
  "author": "SquabbyZ",
6
6
  "license": "MIT",
@@ -39,7 +39,11 @@
39
39
  "README.md",
40
40
  "README-en.md",
41
41
  "CHANGELOG.md",
42
- "LICENSE"
42
+ "LICENSE",
43
+ "examples/video-demo/preview/peaks-loop-demo.mp4",
44
+ "examples/video-demo/preview/peaks-loop-demo-en.mp4",
45
+ "examples/video-demo/out/zh-closing-960.png",
46
+ "examples/video-demo/out/en-closing-960.png"
43
47
  ],
44
48
  "scripts": {
45
49
  "build": "node ./scripts/sync-version.mjs && node ./scripts/clean-dist.mjs && tsc -p tsconfig.json",
@@ -3,6 +3,19 @@ name: peaks-code
3
3
  description: Code-domain loop engineering orchestrator for the Peaks-Loop skill family. Use when the user asks Peaks-Loop to handle a code-repo workflow end-to-end (端到端/全流程/需求开发), especially from a product document (PRD/飞书文档/Feishu doc) through implementation and validation. Coordinates peaks-prd, peaks-rd, peaks-qa, peaks-ui, peaks-sc, and peaks-txt while preserving user confirmation gates. Triggers on `/peaks-code`, "peaks code", "全流程开发", "端到端迭代". General primitives (peaks-resume / peaks-status / peaks-test) are sibling skills, not children.
4
4
  ---
5
5
 
6
+ ## Scope (RL-8 — red line, locked 2026-07-08)
7
+
8
+ `peaks-code` is a **code-domain long-task loop engineering orchestrator; not a general-purpose orchestrator.**
9
+
10
+ This is RL-8 from the Loop Engineering crystallization design
11
+ (`docs/superpowers/specs/2026-07-07-peaks-loop-loop-engineering-crystallization-design.md` §0.4 and §10 RL-8).
12
+ The boundary is closed under this slice:
13
+
14
+ - **In scope:** end-to-end code-domain workflows — repository scanning, RD planning, code implementation via RD, QA verification, UI changes inside a code repo, source-control handoff, and code-repo context packaging. Coordinated role skills: `peaks-prd`, `peaks-rd`, `peaks-qa`, `peaks-ui`, `peaks-sc`, `peaks-txt`.
15
+ - **Out of scope:** research / content / product / medical / non-code domains. Each of those ships as an independent `peaks-*` skill that imports `.peaks/standards/loop-engineering-guidelines.md` and passes `peaks skill lint --category loop-engineering-readiness`. They are **not** subclasses or variants of `peaks-code`.
16
+ - **Failure modes this rule prevents:** (a) `peaks-code` widening into a general orchestrator; (b) non-code capabilities being smuggled into `peaks-code`; (c) other domains being expressed as "peaks-code variants".
17
+ - **Self-check:** before any new peak-* capability is added here, ask "is this code-domain?" If the answer is no, the right move is a new `peaks-*` skill, not an extension of `peaks-code`.
18
+
6
19
  ## Single-scope-axis naming convention (2.7.1)
7
20
 
8
21
  The `.peaks/` workspace has a **single scope axis** (session-id) plus a nested **sub-agent axis** under `.peaks/_sub_agents/<sessionId>/...`. Use `<sessionId>` (NEVER bare `<sid>`). Reviewable artifacts live at `.peaks/_runtime/<sessionId>/<role>/...` (gitignored); the change-id is an optional filename slug and does NOT route filesystem writes. CLI: session-id → `peaks session *`; sub-agent → `peaks sub-agent *`. OpenSpec's `openspec/changes/<change-id>/` vocabulary (L4) is preserved. Test `skills-skill-md-naming.test.ts` enforces (a) zero bare `<sid>`, (b) axis labels, (c) this callout.
@@ -0,0 +1,272 @@
1
+ ---
2
+ name: peaks-content
3
+ description: Non-code orchestrator for content workflows (draft / edit / publish / archive). Use when a user wants to drive an end-to-end content-production loop (blog post, newsletter, marketing copy, social thread, doc page) where the LLM must coordinate draft / edit / tone / publish / archive stages with explicit gates between them, and where the user only wants to speak natural language and pick options. NOT for free-form chatting; NOT for code review. Reuses the peaks-loop Loop Engineering primitives (Loop Engineering Asset, Bee Asset, Workflow Trace, Evolution Evaluation) but does NOT import peaks-code internals. Triggers on `/peaks-content`, "peaks content", "content workflow", "publish this to <channel>", "edit the draft", "archive the previous version".
4
+ ---
5
+
6
+ # peaks-content
7
+
8
+ `peaks-content` is the **content-domain** orchestrator for peaks-loop. It drives an end-to-end content-production loop — draft, edit, publish, archive — through LLM-mediated sub-agents (the "bees") so a non-technical user can produce a polished, channel-ready piece of writing by speaking natural language and picking from multi-choice options. The user never types a CLI verb; the user never hand-authors markdown or JSON; the LLM runs every structured operation on the user's behalf.
9
+
10
+ This is a **content-domain** orchestrator. It is NOT code (peaks-code does that), NOT research (a future `peaks-research` skill does that), NOT data, NOT medical, and NOT legal. It does NOT import peaks-code internals — it reuses the same Loop Engineering primitives (Loop Engineering Asset, Bee Asset, Workflow Trace, Evolution Evaluation) defined in the shared guideline file at `.peaks/standards/loop-engineering-guidelines.md`.
11
+
12
+ ## Loop Engineering role
13
+
14
+ `peaks-content` is a **non-crystallizing orchestrator** for the content domain. The skill drives LLM-mediated sub-agents (the "bees" — see "Sub-agents (the bees)") that perform the actual draft / edit / publish work. The skill does NOT crystallize `loop_release` / `bee_release` / `crystallization_event` rows by itself. A user may optionally invoke `peaks asset crystallize` AFTER a run to persist the workflow as a Loop Engineering asset, but that is a separate, post-run step owned by `peaks-maker` (not this skill).
15
+
16
+ This non-crystallizing shape mirrors the M6 `peaks-maker` pattern: the durable artifact of a content run is the published piece + the archive history, not a peaks-loop SkillHub release. Crystallizing the orchestrator itself would conflate "I shipped a blog post" with "I want this run to become a reusable skill" — two different user intents, kept on two different timelines.
17
+
18
+ Reference: the karpathy-engineered red lines that govern every Loop-Engineering-participating peaks-* skill live at `.peaks/standards/loop-engineering-guidelines.md`. `peaks-content` honors `RL-0` (karpathy × darwin co-equal), `RL-1` (Human-NL-Choice-Only), `RL-2` (no durable change pre-run), `RL-8` (peaks-code domain boundary), and `RL-9` (desktop + share go through the peaks CLI).
19
+
20
+ ## When to use
21
+
22
+ Concrete triggers:
23
+
24
+ - The user says `/peaks-content` or "peaks content" or "content workflow".
25
+ - "Draft a blog post about `<topic>`" / "write a post on `<topic>`".
26
+ - "Edit the draft" / "tighten the tone" / "check the claims" / "shorten this".
27
+ - "Publish to `<channel>`" / "post this to Medium" / "send the newsletter" / "schedule the thread".
28
+ - "Archive the previous version" / "supersede the old post".
29
+ - "Take this topic from draft to publish" (full-pipeline run).
30
+ - "What stage is `<draft>` in?" (inspect-pipeline-state run).
31
+ - "Save this draft → edit → publish loop as a Loop Engineering asset" (crystallization request — routed to `peaks asset crystallize`).
32
+
33
+ ## When NOT to use
34
+
35
+ The rubric-banned surfaces — `peaks-content` MUST refuse these targets:
36
+
37
+ - **Medical advice** — anything that contains (or appears to contain) clinical recommendations, dosage, diagnosis, or treatment. The skill refuses and routes the user to a different peaks-* skill with stricter medical gates.
38
+ - **Legal advice** — anything that contains statutory interpretation, contract review, or jurisdictional guidance. The skill refuses and routes the user to a legal-domain peaks-* skill.
39
+ - **Financial advice** — anything that contains investment guidance, tax planning, or securities recommendations. The skill refuses and routes the user to a finance-domain peaks-* skill.
40
+ - **User-generated content moderation** — bulk review, flagging, takedown, or appeal handling for user-submitted content is a different policy with different gates; the user must use a moderation-specific peaks-* skill.
41
+ - **Personal data extraction** — drafts that ingest emails, phone numbers, addresses, government IDs, or other PII into the piece. The skill refuses to ingest PII (see RL-2).
42
+ - **Code-coupled content** — drafts that require the LLM to write or modify source code (e.g. "show me the API response in this post") are routed to peaks-code for the code part; the content part stays here, but only after the code piece is stable.
43
+ - **Hand-authored markdown / JSON** — anything that requires the user to type markdown structure or fill a JSON file. The LLM authors the markdown; the user describes the intent in NL.
44
+
45
+ If the only available candidate falls in a banned surface, the skill surfaces the skip list with reasons and asks the user to expand the topic source — it does not bypass the rubric.
46
+
47
+ ## Domain boundary (RL-8 echo)
48
+
49
+ `peaks-content` is **content-domain only**. It does NOT do code, research, data, medical, or legal. Cross-domain work goes to:
50
+
51
+ - `peaks-code` for code.
52
+ - `peaks-research` (future) for research.
53
+ - `peaks-product` (future) for product artifacts.
54
+ - A medical-domain peaks-* skill for medical content.
55
+ - A legal-domain peaks-* skill for legal content.
56
+
57
+ Each of these is a separate orchestrator skill, NOT a subclass of `peaks-content` and NOT a subclass of `peaks-code`. Each must import `.peaks/standards/loop-engineering-guidelines.md` and pass `peaks skill lint --category loop-engineering-readiness`.
58
+
59
+ ## Inputs (NL or choice only — RL-1)
60
+
61
+ The skill accepts exactly **four** trigger forms. Every form resolves via `AskUserQuestion` multi-choice or free-form NL; the user never types a CLI verb or hand-authors JSON.
62
+
63
+ 1. **A. Run a stage on a piece** — "draft a post about `<topic>`" / "edit the draft" / "publish to `<channel>`" / "archive `<old-version>`". The user names the stage; the LLM finds the piece in `.peaks/content/drafts/` and runs only that stage.
64
+ 2. **B. Run a full pipeline** — "take this topic from draft to publish". The skill proposes the 4-stage chain (draft → edit → publish → archive) and asks for one confirm pick.
65
+ 3. **C. Inspect pipeline state** — "what stage is `<draft>` in?" The skill reads the frontmatter and returns a one-screen status.
66
+ 4. **D. Crystallize the workflow** — "save this draft → edit → publish loop as a Loop Engineering asset". The skill routes to `peaks asset crystallize` after a successful run; the crystallization prompt itself is owned by `peaks-maker`, not this skill.
67
+
68
+ The user also provides: (a) the topic or piece slug (NL — "hermes-agent launch post", "Q3 newsletter", "developer-tool thread"); (b) the target channel (NL — "blog", "Medium", "Twitter thread", "email newsletter", "internal doc"); (c) the tone (NL — "casual", "editorial", "technical"); (d) the length target (NL — "500 words", "a long-form post"); (e) the publish deadline (ISO or NL — "next Friday" → the LLM derives an ISO date).
69
+
70
+ ## Default runbook
71
+
72
+ The 6-step procedure the LLM must follow on every run. The LLM runs the CLI on the user's behalf — the user only describes and picks.
73
+
74
+ ### 1. Anchor + skill marker
75
+
76
+ ```bash
77
+ peaks workspace init --project .
78
+ peaks skill presence:set peaks-content --gate startup
79
+ ```
80
+
81
+ ### 2. NL interview (one question at a time)
82
+
83
+ The LLM asks ONE question at a time (the "test user does not know the system" scenario). The interview collects: the piece slug (or the LLM looks it up in `.peaks/content/drafts/`), the target channel, the tone, the length target, and the publish deadline. The user never sees a JSON form or a CLI prompt — every input is a multi-choice pick or free-form NL.
84
+
85
+ ### 3. Draft stage — `bee-content-draft`
86
+
87
+ The LLM dispatches to the draft bee. The draft is a plain markdown file at `.peaks/content/drafts/<slug>.md`. The frontmatter captures the 4-section brief (see "PR / artifact format"). The body is the actual content.
88
+
89
+ ### 4. Edit stage — `bee-content-edit`
90
+
91
+ A separate edit bee runs. The 4-section gate fires between draft and edit:
92
+
93
+ - **Tone gate** — the edit bee confirms the piece matches the user-confirmed tone.
94
+ - **Factual-claims gate** — the edit bee flags any claim that lacks a citation. Drafts with unflagged claims cannot advance to publish (RL-3).
95
+ - **Length gate** — the edit bee confirms the piece is within the user-confirmed length target.
96
+ - **No-PII gate** — the edit bee scans for emails, phone numbers, addresses, government IDs. If any are present, the bee refuses to advance and tells the user which lines to remove (RL-2).
97
+
98
+ ### 5. Publish stage — `bee-content-publish` (GATED on user pick)
99
+
100
+ A separate publish bee runs. The gate fires between edit and publish: the user (Human-NL-Choice-Only) confirms the **final draft + target channel** via `AskUserQuestion`. The skill MUST ask the user to pick the final channel + final tone + final length before any external write. Only after the user's pick is captured does the publish bee run.
101
+
102
+ **Hard rule (RL-1 enforcement):** the skill MUST refuse to write to any external channel (Twitter / Medium / email / Discord / etc.) without an explicit user pick. The user is the only actor who can authorize an external write. The LLM may prepare the payload (markdown, tags, schedule), but the actual outbound call is gated on the user's choice.
103
+
104
+ ### 6. Archive stage — `bee-content-archive`
105
+
106
+ The previous version of the piece (if any) is moved to `.peaks/content/archive/<channel>/<slug>-<version>.md`. A `crystallization_event` is recorded for the full run only if the user picked option D in the inputs (and even then, the crystallization itself is owned by `peaks-maker` via `peaks asset crystallize`).
107
+
108
+ ## Sub-agents (the bees)
109
+
110
+ The skill orchestrates four bees. Each bee is a sub-agent dispatch; the skill is the orchestrator that schedules them. Each bee has its own prompt + context; none of them touch the user's content directly without the orchestrator passing them the artifact path.
111
+
112
+ - `bee-content-draft` — generates the first draft from a brief. Consumes: topic + tone + length + audience. Produces: `.peaks/content/drafts/<slug>.md` with frontmatter + body.
113
+ - `bee-content-edit` — runs tone / factual / length / PII checks. Consumes: the draft path + the frontmatter brief. Produces: an edit report (line-level findings) + an advance/blocked verdict.
114
+ - `bee-content-publish` — formats + posts to the channel (gated on user pick). Consumes: the final-draft path + the user-confirmed channel. Produces: the outbound payload + the publish receipt.
115
+ - `bee-content-archive` — moves the previous version to the archive dir. Consumes: the current-version path + the new-version path. Produces: the archived copy + a manifest update.
116
+
117
+ ## PR / artifact format
118
+
119
+ The markdown file header used in `.peaks/content/drafts/<slug>.md`:
120
+
121
+ ```markdown
122
+ ---
123
+ title: <piece title>
124
+ channel: <target channel>
125
+ status: draft | edit | publish | archive
126
+ version: <semver>
127
+ created_at: <ISO>
128
+ updated_at: <ISO>
129
+ author_intent_raw: <the user's original NL request>
130
+ tone: <user-confirmed tone>
131
+ length_target: <user-confirmed length>
132
+ publish_deadline: <ISO or null>
133
+ crystallization_event: <id or null>
134
+ ---
135
+ ```
136
+
137
+ The 4-section brief is stored as the frontmatter; the body is the actual content. This mirrors M4/M5 evidence-brief conventions. The `crystallization_event` field is null unless the user picked option D in the inputs and a crystallization event was recorded by `peaks-maker`.
138
+
139
+ ## Red lines (karpathy 4-section form)
140
+
141
+ Each red line below is written in the karpathy 4-section form (Failure modes / Rewrite / Self-check / Out-of-scope). The skill refuses to advance past any red line violation.
142
+
143
+ ## RL-1 — Human-NL-Choice-Only (echo; the publish gate)
144
+
145
+ **Failure modes.** User is asked to hand-fill JSON or type a CLI verb; user pushed into schema decisions; user accepts opaque publish recommendations by default; the publish bee writes to an external channel without an explicit user pick.
146
+
147
+ **Rewrite.** Every user input is either an `AskUserQuestion` multi-choice pick or free-form NL. The LLM runs the CLI. The publish bee is gated on an explicit `AskUserQuestion` confirm of channel + tone + length; the LLM may prepare the payload, but the outbound call only fires after the pick.
148
+
149
+ **Self-check.** Did any step require the user to type a verb? Is the publish bee blocked until the user picks the channel? Does every recommendation include an evidence brief? Does the new skill still gate every CLI invocation through the LLM?
150
+
151
+ **Out-of-scope.** Machine-driven CI flows; emergency security gates (LLM + red lines take over; user is informed in NL).
152
+
153
+ ## RL-2 — No personal data (PII) in any draft
154
+
155
+ **Failure modes.** A draft contains an email, phone number, postal address, or government ID; the edit bee advances the piece anyway; the piece publishes with PII and the user's privacy is exposed.
156
+
157
+ **Rewrite.** The edit bee MUST scan the draft for emails, phone numbers, addresses, and government IDs. If any are present, the bee refuses to advance and the user must explicitly remove them before the next gate. Forbidden patterns: `\b\S+@\S+\.\S+\b`, `\b\d{3}[-.\s]?\d{3}[-.\s]?\d{4}\b`, any 9-digit numeric run, any line containing "SSN" / "passport" / "driver's license".
158
+
159
+ **Self-check.** Does the edit bee's report include a PII scan section? Are any of the forbidden patterns matched in the draft? If matched, did the bee refuse to advance?
160
+
161
+ **Out-of-scope.** Public-figure bylines (the user explicitly opted in to a public byline at draft time), branded content that names the user's company (not personal data).
162
+
163
+ ## RL-3 — No fabricated citations / facts
164
+
165
+ **Failure modes.** A draft makes a claim ("X% of users prefer Y") with no citation; the edit bee advances the piece anyway; the piece publishes with unverified claims and the user's credibility is exposed.
166
+
167
+ **Rewrite.** The edit bee MUST flag any claim that lacks a citation. Drafts with unflagged claims cannot advance to publish. A "claim" is any sentence containing a number, a percentage, a date, a named study, or a comparative ("X is faster than Y"). A "citation" is a URL, a paper title, or a user-supplied source pointer.
168
+
169
+ **Self-check.** Does the edit bee's report include a claims-without-citations list? Is the list empty? If non-empty, did the bee refuse to advance?
170
+
171
+ **Out-of-scope.** Subjective opinions ("I think X"), user-supplied anecdotes ("in my experience"), or branded opinions ("we believe X") — these are not factual claims.
172
+
173
+ ## RL-4 — One piece = one decision
174
+
175
+ **Failure modes.** The LLM batch-publishes multiple pieces in one user pick; the user did not see each piece's final draft; an embarrassing piece slips through with the good ones.
176
+
177
+ **Rewrite.** Each piece gets its own confirm pick. The publish bee is invoked once per piece. The user sees the final draft of THIS piece only and confirms THIS piece's channel + tone + length. No "publish all 5 of these" UI affordance is offered.
178
+
179
+ **Self-check.** Did the user see a confirm pick for THIS piece? Did the publish bee run for THIS piece only? Is the count of pieces the user confirmed equal to the count of pieces the publish bee ran for?
180
+
181
+ **Out-of-scope.** A user-explicit "publish the whole batch" — the skill surfaces the list, asks the user to confirm each one, and refuses to bulk-publish.
182
+
183
+ ## RL-5 — Tone + length + channel are user-confirmed before publish
184
+
185
+ **Failure modes.** The LLM infers tone / length / channel from the brief alone; the user did not confirm; the piece publishes with the wrong shape.
186
+
187
+ **Rewrite.** The publish bee refuses to run without these three fields set in the frontmatter. The user-confirmed values come from the `tone`, `length_target`, and `channel` fields of `.peaks/content/drafts/<slug>.md`. If any of the three is null or empty, the publish bee returns `BLOCKED_NEEDS_USER_CONFIRM`.
188
+
189
+ **Self-check.** Are all three frontmatter fields non-null at publish time? Did the user confirm each of the three in the publish-gate pick? If any field changed during edit, did the user re-confirm?
190
+
191
+ **Out-of-scope.** Auto-correction at edit time — the edit bee may propose a tone tweak, but the user must re-confirm before the publish bee runs.
192
+
193
+ ## RL-6 — Drafts are immutable once published
194
+
195
+ **Failure modes.** A live edit modifies a published piece without archiving the previous version; the user loses the audit trail; the piece's history is untraceable.
196
+
197
+ **Rewrite.** Once a piece is published, the only path to update it is the archive step. A "live edit" without archive is forbidden. The archive step moves the previous version to `.peaks/content/archive/<channel>/<slug>-<version>.md` before the new version is written.
198
+
199
+ **Self-check.** Is the previous version present in the archive dir? Does the new version have a semver greater than the archived one? Did the archive step run BEFORE the new write?
200
+
201
+ **Out-of-scope.** Typo fixes — those go through the same archive step (typo fixes are version bumps, not silent edits).
202
+
203
+ ## RL-7 — No medical / legal / financial advice
204
+
205
+ **Failure modes.** The LLM drafts a piece that contains medical dosage, legal interpretation, or investment guidance; the user ships it; the user is exposed to liability.
206
+
207
+ **Rewrite.** The skill MUST refuse to draft any piece that contains (or appears to contain) advice in those domains. The trigger phrases are checked at draft time: any sentence containing "should take", "is safe to", "prescribe", "diagnose", "statute", "case law", "investment recommendation", or similar domain markers → the skill refuses and routes the user to the appropriate peaks-* skill.
208
+
209
+ **Self-check.** Does the draft contain any of the forbidden trigger phrases? If yes, did the skill refuse to advance? Did the skill surface the skip list with reasons?
210
+
211
+ **Out-of-scope.** General-audience health explainers ("how sleep works"), legal news summaries ("the EU passed a law"), financial education ("how index funds work") — these are not advice in the actionable sense.
212
+
213
+ ## RL-8 — Cross-domain work is a different skill
214
+
215
+ **Failure modes.** The skill smuggles code-writing into a blog post; the user gets a code-coupled draft that the LLM cannot reliably produce; the content piece relies on broken code.
216
+
217
+ **Rewrite.** If the user asks for a content piece that requires code (e.g. "show me the API response in this post"), the skill routes the code part to `peaks-code`; the content part stays here, but only after the code piece is stable and reviewed. The skill never writes or modifies source code.
218
+
219
+ **Self-check.** Does the draft reference code that the LLM authored? If yes, did that code go through `peaks-code` first? Is the code piece reviewed and stable?
220
+
221
+ **Out-of-scope.** Documentation pages that link to existing code snippets in the user's repo (read-only references, no code authoring).
222
+
223
+ ## RL-9 — Crystallization is opt-in
224
+
225
+ **Failure modes.** The skill auto-crystallizes a run, polluting the user's SkillHub with one-off content runs; the user did not opt in; the asset pool fills with low-signal content loops.
226
+
227
+ **Rewrite.** The skill does NOT auto-crystallize. The user must pick option D in the inputs to crystallize a run as a Loop Engineering asset. Even then, the crystallization itself is owned by `peaks-maker` via `peaks asset crystallize`; the skill only records the run in the frontmatter `crystallization_event` field.
228
+
229
+ **Self-check.** Is the `crystallization_event` field null unless the user picked option D? Did the skill refuse to call `peaks asset crystallize` without the user pick?
230
+
231
+ **Out-of-scope.** Replay of a previously crystallized content run — the replay is a new run, not a new crystallization; the frontmatter `crystallization_event` field points to the existing asset, not a new one.
232
+
233
+ ## Boundaries
234
+
235
+ What this skill MUST NOT do:
236
+
237
+ - **Publish to any external channel without an explicit user pick** (RL-1 / RL-5).
238
+ - **Touch the user's repo** (write to source code) — `peaks-code` does that.
239
+ - **Hand-author markdown / JSON on the user's behalf** when the user is just describing intent.
240
+ - **Ingest personal data** into the draft (RL-2).
241
+ - **Run a publish** without the user-confirmed tone / length / channel (RL-5).
242
+ - **Live-edit a published piece** without archiving the previous version (RL-6).
243
+ - **Draft medical / legal / financial advice** (RL-7).
244
+ - **Smuggle code-writing into a content piece** (RL-8).
245
+ - **Auto-crystallize a run** without the user's option D pick (RL-9).
246
+ - **Batch-publish multiple pieces in one user pick** (RL-4).
247
+
248
+ ## Audit (reproducibility)
249
+
250
+ The 5-line verification block the user can run after a content run:
251
+
252
+ ```bash
253
+ cat .peaks/content/drafts/<slug>.md # piece exists, frontmatter is complete
254
+ git status .peaks/content/ # archive step committed
255
+ cat .peaks/content/archive/<channel>/<slug>-<version>.md 2>/dev/null # previous version archived
256
+ .peaks/standards/loop-engineering-guidelines.md -c "RL-1|RL-5|RL-7" # ensure RLs are referenced
257
+ .peaks/_runtime/<sessionId>/crystallization-event.json 2>/dev/null # crystallization event if user picked D
258
+ ```
259
+
260
+ If any line returns an unexpected value, the run has been mutated externally; the per-piece frontmatter + the archive history are the source of truth.
261
+
262
+ ## First-class outputs
263
+
264
+ The skill produces the following files for the user to verify:
265
+
266
+ - `.peaks/content/drafts/<slug>.md` — the working draft (frontmatter + body).
267
+ - `.peaks/content/drafts/<slug>.edit-report.md` — the edit bee's report (tone / factual / length / PII findings).
268
+ - `.peaks/content/archive/<channel>/<slug>-<version>.md` — the previous version (after archive step).
269
+ - `.peaks/content/published/<channel>/<slug>-<version>.md` — a copy of the published piece (for audit; the real publish lives on the channel).
270
+ - `.peaks/_runtime/<sessionId>/peaks-content/run-summary.md` — the LLM's run summary (stage sequence, gate verdicts, user picks).
271
+ - `.peaks/_runtime/<sessionId>/peaks-content/publish-receipt.json` — the publish bee's receipt (channel, post URL, timestamp).
272
+ - `.peaks/_runtime/<sessionId>/crystallization-event.json` — the crystallization event (only if the user picked option D).