peaks-loop 4.0.0-beta.5 → 4.0.0-beta.6

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 (39) hide show
  1. package/dist/src/cli/commands/adapter-commands-s2a.d.ts +34 -0
  2. package/dist/src/cli/commands/adapter-commands-s2a.js +92 -0
  3. package/dist/src/cli/commands/polyrepo-commands.d.ts +37 -0
  4. package/dist/src/cli/commands/polyrepo-commands.js +155 -0
  5. package/dist/src/cli/commands/runtime-commands.d.ts +52 -0
  6. package/dist/src/cli/commands/runtime-commands.js +150 -0
  7. package/dist/src/cli/program.js +16 -0
  8. package/dist/src/services/adapter/adapter-registry.d.ts +48 -0
  9. package/dist/src/services/adapter/adapter-registry.js +186 -0
  10. package/dist/src/services/polyrepo/polyrepo-dispatcher.d.ts +34 -0
  11. package/dist/src/services/polyrepo/polyrepo-dispatcher.js +119 -0
  12. package/dist/src/services/polyrepo/polyrepo-scanner.d.ts +9 -0
  13. package/dist/src/services/polyrepo/polyrepo-scanner.js +94 -0
  14. package/dist/src/services/polyrepo/polyrepo-service.d.ts +34 -0
  15. package/dist/src/services/polyrepo/polyrepo-service.js +49 -0
  16. package/dist/src/services/polyrepo/polyrepo-types.d.ts +55 -0
  17. package/dist/src/services/polyrepo/polyrepo-types.js +1 -0
  18. package/dist/src/services/runtime/runtime-detector.d.ts +15 -0
  19. package/dist/src/services/runtime/runtime-detector.js +62 -0
  20. package/dist/src/services/runtime/runtime-service.d.ts +59 -0
  21. package/dist/src/services/runtime/runtime-service.js +51 -0
  22. package/dist/src/services/runtime/vendor-adapter.d.ts +59 -0
  23. package/dist/src/services/runtime/vendor-adapter.js +1 -0
  24. package/dist/src/services/runtime/vendors/claude-code.d.ts +7 -0
  25. package/dist/src/services/runtime/vendors/claude-code.js +39 -0
  26. package/dist/src/services/runtime/vendors/codex.d.ts +7 -0
  27. package/dist/src/services/runtime/vendors/codex.js +43 -0
  28. package/dist/src/services/runtime/vendors/copilot.d.ts +7 -0
  29. package/dist/src/services/runtime/vendors/copilot.js +40 -0
  30. package/dist/src/shared/version.d.ts +1 -1
  31. package/dist/src/shared/version.js +1 -1
  32. package/package.json +1 -1
  33. package/skills/peaks-code/SKILL.md +1 -8
  34. package/skills/peaks-code/references/external-references.md +1 -3
  35. package/skills/peaks-code/references/external-skill-invocation.md +1 -1
  36. package/skills/peaks-code/references/runbook.md +1 -3
  37. package/skills/peaks-code/references/step-0-55-1x-detection.md +2 -1
  38. package/skills/peaks-code/references/workflow-gates-and-types.md +1 -1
  39. package/skills/peaks-code/references/openspec-workflow.md +0 -81
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Runtime service — slice S2-a of RD-2.
3
+ *
4
+ * Orchestrator that wires the runtime detector + vendor adapter
5
+ * registry into a small surface that the CLI commands call. The
6
+ * service owns the canonical list of built-in adapters (claude-code /
7
+ * codex / copilot) and decides which adapter handles a given
8
+ * `--via <id>` request.
9
+ *
10
+ * Design notes (Karpathy #2 Simplicity First):
11
+ *
12
+ * - The service is a thin orchestrator — it does NOT contain any
13
+ * vendor verb strings (claude / codex / copilot). All vendor verbs
14
+ * live in `src/services/runtime/vendors/<vendor>.ts`. Verified by
15
+ * AC-1.
16
+ * - When `compactVia(id)` is called with an unknown id, the service
17
+ * returns a "no-op" result with exitCode=0 and a warning instead of
18
+ * throwing — vendor-neutrality means peaks-loop MUST keep working
19
+ * even when the requested vendor adapter isn't registered.
20
+ * - The service is the SOLE owner of the built-in adapter list. Tests
21
+ * and CLI commands should never instantiate adapters directly;
22
+ * they should call `listBuiltInAdapters()` or `getAdapter(id)`.
23
+ */
24
+ import type { VendorAdapter, VendorCompactResult } from './vendor-adapter.js';
25
+ export interface RuntimeServiceOptions {
26
+ /** Override the built-in adapter list (used by tests + the
27
+ * adapter-registry's `register()` flow). */
28
+ readonly builtIns?: VendorAdapter[];
29
+ /** Override the home dir used by adapter detect() helpers. */
30
+ readonly home?: string;
31
+ }
32
+ /** Pure orchestrator. The runtime service holds built-in adapters as
33
+ * the baseline registry; the AdapterRegistry layer (`src/services/
34
+ * adapter/adapter-registry.ts`) extends this baseline at runtime via
35
+ * user-registered adapters persisted to `.peaks/runtime/adapters.json`.
36
+ * The two layers are intentionally separate:
37
+ * - runtime-service: built-in vendor adapters for the runtime-detect
38
+ * compact flow (S2-a).
39
+ * - adapter-registry: persisted user-registered adapters (S2-a).
40
+ * runtime-service itself does NOT consult the adapter registry —
41
+ * callers (`peaks runtime compact --via <id>`) layer them: first look
42
+ * up the id in the persisted registry, fall back to the built-in list.
43
+ */
44
+ export declare class RuntimeService {
45
+ private readonly builtIns;
46
+ constructor(opts?: RuntimeServiceOptions);
47
+ /** Return all built-in adapters. Order: claude-code, codex, copilot. */
48
+ listBuiltInAdapters(): VendorAdapter[];
49
+ /** Resolve a built-in adapter by id. Returns undefined when not
50
+ * found — caller should fall back to adapter registry or unknown. */
51
+ getBuiltInAdapter(id: string): VendorAdapter | undefined;
52
+ /** Compact via a built-in adapter. When the id is unknown, returns
53
+ * a no-op result with a warning rather than throwing — vendor
54
+ * neutrality demands peaks-loop stay alive even when a vendor is
55
+ * not wired up. */
56
+ compactVia(id: string, force: boolean | undefined): Promise<VendorCompactResult & {
57
+ warning?: string;
58
+ }>;
59
+ }
@@ -0,0 +1,51 @@
1
+ import { ClaudeCodeAdapter } from './vendors/claude-code.js';
2
+ import { CodexAdapter } from './vendors/codex.js';
3
+ import { CopilotAdapter } from './vendors/copilot.js';
4
+ const DEFAULT_BUILT_INS = [
5
+ new ClaudeCodeAdapter(),
6
+ new CodexAdapter(),
7
+ new CopilotAdapter()
8
+ ];
9
+ /** Pure orchestrator. The runtime service holds built-in adapters as
10
+ * the baseline registry; the AdapterRegistry layer (`src/services/
11
+ * adapter/adapter-registry.ts`) extends this baseline at runtime via
12
+ * user-registered adapters persisted to `.peaks/runtime/adapters.json`.
13
+ * The two layers are intentionally separate:
14
+ * - runtime-service: built-in vendor adapters for the runtime-detect
15
+ * compact flow (S2-a).
16
+ * - adapter-registry: persisted user-registered adapters (S2-a).
17
+ * runtime-service itself does NOT consult the adapter registry —
18
+ * callers (`peaks runtime compact --via <id>`) layer them: first look
19
+ * up the id in the persisted registry, fall back to the built-in list.
20
+ */
21
+ export class RuntimeService {
22
+ builtIns;
23
+ constructor(opts = {}) {
24
+ this.builtIns = opts.builtIns ?? [...DEFAULT_BUILT_INS];
25
+ }
26
+ /** Return all built-in adapters. Order: claude-code, codex, copilot. */
27
+ listBuiltInAdapters() {
28
+ return [...this.builtIns];
29
+ }
30
+ /** Resolve a built-in adapter by id. Returns undefined when not
31
+ * found — caller should fall back to adapter registry or unknown. */
32
+ getBuiltInAdapter(id) {
33
+ return this.builtIns.find((a) => a.id === id);
34
+ }
35
+ /** Compact via a built-in adapter. When the id is unknown, returns
36
+ * a no-op result with a warning rather than throwing — vendor
37
+ * neutrality demands peaks-loop stay alive even when a vendor is
38
+ * not wired up. */
39
+ async compactVia(id, force) {
40
+ const adapter = this.getBuiltInAdapter(id);
41
+ if (adapter === undefined) {
42
+ return {
43
+ exitCode: 0,
44
+ stdout: '',
45
+ stderr: '',
46
+ warning: `no built-in adapter registered for id="${id}" — falling back to no-op; use \`peaks adapter register --id ${id} --binary <cmd>\` to wire one`
47
+ };
48
+ }
49
+ return adapter.compact({ force: force === true });
50
+ }
51
+ }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Vendor adapter contract — slice S2-a of RD-2 (2026-07-08 session).
3
+ *
4
+ * Each `VendorAdapter` is a thin wrapper around ONE AI runtime (Claude
5
+ * Code / Codex / Copilot / custom) that knows how to:
6
+ *
7
+ * 1. Detect whether the current environment is that vendor
8
+ * (env vars, sentinel binaries, environment shape).
9
+ * 2. Issue a compact command through that vendor without forcing
10
+ * peaks-loop's core code to know vendor-specific verbs.
11
+ *
12
+ * The interface is intentionally MINIMAL (Karpathy #2 Simplicity First):
13
+ * two methods (`detect`, `compact`) plus an `id` + `displayName`.
14
+ * New verbs (status, refactor, etc.) are NOT in scope — they would
15
+ * bloat the surface and force every vendor to implement every verb
16
+ * whether the vendor supports it or not. When a future verb is needed
17
+ * it can either be added to the interface (with all vendors forced to
18
+ * implement) or — preferred — handled through a separate
19
+ * verb-specific adapter family.
20
+ *
21
+ * Vendor-specific verb strings (`claude --compact`, `codex --compact`,
22
+ * `copilot compact`) MUST live ONLY in adapter implementations under
23
+ * `src/services/runtime/vendors/<vendor>.ts`. The runtime service that
24
+ * orchestrates these adapters (runtime-service.ts) and the CLI commands
25
+ * (`peaks runtime compact --via <id>`) MUST NOT contain vendor verbs
26
+ * — verified by AC-1: `rg -n "claude --compact|codex --compact|copilot
27
+ * compact" src/services/code/` must return 0 matches.
28
+ *
29
+ * See `.peaks/_runtime/2026-07-08-session-17918f/prd/002-adapter-runtime-and-polyrepo.md`
30
+ * for the source PRD.
31
+ */
32
+ export interface VendorCompactArgs {
33
+ /** When `true`, the vendor is asked to compact unconditionally even
34
+ * if its auto-detector says it would not normally do so. */
35
+ readonly force?: boolean;
36
+ }
37
+ export interface VendorCompactResult {
38
+ readonly exitCode: number;
39
+ readonly stdout: string;
40
+ readonly stderr: string;
41
+ }
42
+ export interface VendorAdapter {
43
+ /** Stable identifier used by `peaks runtime compact --via <id>` and
44
+ * by the adapter registry. Convention: lowercase, dash-separated,
45
+ * e.g. `claude-code`, `codex`, `copilot`. */
46
+ readonly id: string;
47
+ /** Human-readable name shown in `peaks runtime list` output. */
48
+ readonly displayName: string;
49
+ /** Whether the CURRENT environment is this vendor. Detectors MUST
50
+ * be pure functions of env + filesystem; they MUST NOT spawn
51
+ * processes or call vendor CLIs (a detect that itself runs the
52
+ * vendor would defeat the purpose of asking whether the vendor is
53
+ * present). */
54
+ detect(): Promise<boolean>;
55
+ /** Issue a compact command for this vendor. The adapter chooses
56
+ * which binary + args + env to invoke; callers MUST NOT pass
57
+ * vendor verbs through this method. */
58
+ compact(args?: VendorCompactArgs): Promise<VendorCompactResult>;
59
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,7 @@
1
+ import type { VendorAdapter, VendorCompactArgs, VendorCompactResult } from '../vendor-adapter.js';
2
+ export declare class ClaudeCodeAdapter implements VendorAdapter {
3
+ readonly id = "claude-code";
4
+ readonly displayName = "Claude Code";
5
+ detect(): Promise<boolean>;
6
+ compact(args?: VendorCompactArgs): Promise<VendorCompactResult>;
7
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Claude Code vendor adapter — slice S2-a.
3
+ *
4
+ * Stubs the compact verb to `claude --compact` (the canonical vendor
5
+ * verb for Claude Code). The actual binary is invoked via spawn so
6
+ * the adapter does NOT block peaks-loop. When the `claude` binary is
7
+ * not on PATH the adapter returns exitCode=127 + stderr explaining
8
+ * the missing binary, which the runtime service surfaces as a warning
9
+ * + no-op rather than a fatal error (vendor-neutrality: peaks-loop
10
+ * should still work even when the host vendor CLI is absent).
11
+ */
12
+ import { spawn } from 'node:child_process';
13
+ export class ClaudeCodeAdapter {
14
+ id = 'claude-code';
15
+ displayName = 'Claude Code';
16
+ async detect() {
17
+ return process.env.CLAUDE_CODE === '1'
18
+ || process.env.CLAUDE_CODE === 'true'
19
+ || (process.env.CLAUDE_CODE_ENTRYPOINT?.length ?? 0) > 0;
20
+ }
21
+ async compact(args = {}) {
22
+ const argv = ['--compact'];
23
+ if (args.force === true)
24
+ argv.push('--force');
25
+ return new Promise((resolveRun) => {
26
+ const proc = spawn('claude', argv, { stdio: ['ignore', 'pipe', 'pipe'] });
27
+ let stdout = '';
28
+ let stderr = '';
29
+ proc.stdout.on('data', (chunk) => { stdout += chunk.toString('utf8'); });
30
+ proc.stderr.on('data', (chunk) => { stderr += chunk.toString('utf8'); });
31
+ proc.on('error', (err) => {
32
+ resolveRun({ exitCode: 127, stdout, stderr: stderr + (stderr.length > 0 ? '\n' : '') + err.message });
33
+ });
34
+ proc.on('close', (code) => {
35
+ resolveRun({ exitCode: code ?? 0, stdout, stderr });
36
+ });
37
+ });
38
+ }
39
+ }
@@ -0,0 +1,7 @@
1
+ import type { VendorAdapter, VendorCompactArgs, VendorCompactResult } from '../vendor-adapter.js';
2
+ export declare class CodexAdapter implements VendorAdapter {
3
+ readonly id = "codex";
4
+ readonly displayName = "Codex";
5
+ detect(): Promise<boolean>;
6
+ compact(args?: VendorCompactArgs): Promise<VendorCompactResult>;
7
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Codex vendor adapter — slice S2-a.
3
+ *
4
+ * Stub adapter (PRD out-of-scope: full implementation lands in a
5
+ * future slice). The detect() heuristic is intentionally permissive:
6
+ * if CODEX_HOME or ~/.codex is present we report detected=true so
7
+ * `peaks runtime detect` exercises the codex branch. compact() uses
8
+ * the canonical `codex --compact` invocation; missing-binary is
9
+ * reported as exitCode=127, not a thrown error (the runtime service
10
+ * surfaces that as a warning + no-op so a missing vendor CLI does
11
+ * not break peaks-loop).
12
+ */
13
+ import { spawn } from 'node:child_process';
14
+ import { existsSync } from 'node:fs';
15
+ import { join } from 'node:path';
16
+ export class CodexAdapter {
17
+ id = 'codex';
18
+ displayName = 'Codex';
19
+ async detect() {
20
+ if (process.env.CODEX_HOME !== undefined && process.env.CODEX_HOME.length > 0)
21
+ return true;
22
+ const home = process.env.HOME ?? process.env.USERPROFILE ?? '';
23
+ return home.length > 0 && existsSync(join(home, '.codex'));
24
+ }
25
+ async compact(args = {}) {
26
+ const argv = ['--compact'];
27
+ if (args.force === true)
28
+ argv.push('--force');
29
+ return new Promise((resolveRun) => {
30
+ const proc = spawn('codex', argv, { stdio: ['ignore', 'pipe', 'pipe'] });
31
+ let stdout = '';
32
+ let stderr = '';
33
+ proc.stdout.on('data', (chunk) => { stdout += chunk.toString('utf8'); });
34
+ proc.stderr.on('data', (chunk) => { stderr += chunk.toString('utf8'); });
35
+ proc.on('error', (err) => {
36
+ resolveRun({ exitCode: 127, stdout, stderr: stderr + (stderr.length > 0 ? '\n' : '') + err.message });
37
+ });
38
+ proc.on('close', (code) => {
39
+ resolveRun({ exitCode: code ?? 0, stdout, stderr });
40
+ });
41
+ });
42
+ }
43
+ }
@@ -0,0 +1,7 @@
1
+ import type { VendorAdapter, VendorCompactArgs, VendorCompactResult } from '../vendor-adapter.js';
2
+ export declare class CopilotAdapter implements VendorAdapter {
3
+ readonly id = "copilot";
4
+ readonly displayName = "GitHub Copilot";
5
+ detect(): Promise<boolean>;
6
+ compact(args?: VendorCompactArgs): Promise<VendorCompactResult>;
7
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Copilot vendor adapter — slice S2-a.
3
+ *
4
+ * Stub adapter (PRD out-of-scope: full implementation lands in a
5
+ * future slice). Copilot's compact verb is `copilot compact`
6
+ * (subcommand form, not `--compact` flag — different from Claude
7
+ * Code + Codex). The adapter encapsulates that verb here so the
8
+ * runtime service + CLI never see it.
9
+ */
10
+ import { spawn } from 'node:child_process';
11
+ import { existsSync } from 'node:fs';
12
+ import { join } from 'node:path';
13
+ export class CopilotAdapter {
14
+ id = 'copilot';
15
+ displayName = 'GitHub Copilot';
16
+ async detect() {
17
+ if (process.env.GITHUB_COPILOT === '1' || process.env.GITHUB_COPILOT === 'true')
18
+ return true;
19
+ const home = process.env.HOME ?? process.env.USERPROFILE ?? '';
20
+ return home.length > 0 && existsSync(join(home, '.copilot'));
21
+ }
22
+ async compact(args = {}) {
23
+ const argv = ['compact'];
24
+ if (args.force === true)
25
+ argv.push('--force');
26
+ return new Promise((resolveRun) => {
27
+ const proc = spawn('copilot', argv, { stdio: ['ignore', 'pipe', 'pipe'] });
28
+ let stdout = '';
29
+ let stderr = '';
30
+ proc.stdout.on('data', (chunk) => { stdout += chunk.toString('utf8'); });
31
+ proc.stderr.on('data', (chunk) => { stderr += chunk.toString('utf8'); });
32
+ proc.on('error', (err) => {
33
+ resolveRun({ exitCode: 127, stdout, stderr: stderr + (stderr.length > 0 ? '\n' : '') + err.message });
34
+ });
35
+ proc.on('close', (code) => {
36
+ resolveRun({ exitCode: code ?? 0, stdout, stderr });
37
+ });
38
+ });
39
+ }
40
+ }
@@ -1 +1 @@
1
- export declare const CLI_VERSION = "4.0.0-beta.5";
1
+ export declare const CLI_VERSION = "4.0.0-beta.6";
@@ -1 +1 @@
1
- export const CLI_VERSION = "4.0.0-beta.5";
1
+ export const CLI_VERSION = "4.0.0-beta.6";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "peaks-loop",
3
- "version": "4.0.0-beta.5",
3
+ "version": "4.0.0-beta.6",
4
4
  "description": "Loop Engineering CLI — workflow primitive / loop guards / evaluators / slice orchestration",
5
5
  "author": "SquabbyZ",
6
6
  "license": "MIT",
@@ -18,7 +18,7 @@ The boundary is closed under this slice:
18
18
 
19
19
  ## Single-scope-axis naming convention (2.7.1)
20
20
 
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.
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 *`. Test `skills-skill-md-naming.test.ts` enforces (a) zero bare `<sid>`, (b) axis labels, (c) this callout.
22
22
 
23
23
  ## Karpathy guidance (Slice 1/6 — karpathy prompt-injection-lift)
24
24
 
@@ -58,12 +58,6 @@ When a NEW conversation opens on a session whose `lastActivity` is from today AN
58
58
 
59
59
  Proactive context-overflow defense. CLI: `peaks session checkpoint [--reason <r>] [...flags]`. Fire `--reason periodic` every 20 tool calls (G1 hard-coded), `--reason artifact-written` per PRD/RD/QA/TXT write. See `references/periodic-checkpoint.md` (otherwise `tests/unit/code/checkpoint-periodic-frequency.test.ts` fails).
60
60
 
61
- ### Peaks-Loop Step 0.5: OpenSpec first-run opt-in (conditional)
62
-
63
- Run when `openspec/` is absent and `.peaks/.peaks-openspec-opt-in.json` is missing. Asks once and persists the decision.
64
-
65
- → see `references/openspec-workflow.md` for the full opt-in flow + lifecycle.
66
-
67
61
  ### Peaks-Loop Step 0.6: Audit + Goal (NEW)
68
62
 
69
63
  Invoke peaks-audit after need expressed; store approved goal at `.peaks/_runtime/<sessionId>/audit-goal/<rid>.json`. **All autonomous work requires approved goal.** → `references/audit-goal-gate.md`.
@@ -288,7 +282,6 @@ Index of every `references/` file. Read on demand.
288
282
  | `references/local-artifact-workspace.md` | Workspace tree + root-prohibition. |
289
283
  | `references/micro-cycle.md` | RD micro-cycle + repair loop. |
290
284
  | `references/mode-selection.md` | Step 1 mode + `--mode`. |
291
- | `references/openspec-workflow.md` | Step 0.5 OpenSpec. |
292
285
  | `references/playwright-mcp-multi-terminal.md` | Multi-terminal Playwright MCP. |
293
286
  | `references/project-memory-loading.md` | Step 2.3 memories. |
294
287
  | `references/project-scan-checklist.md` | Pre-RD scan + template. |
@@ -10,8 +10,6 @@ Do not run upstream installer flows, mutate agent settings, or commit `.codegrap
10
10
 
11
11
  **External skills**: All external skill references (`mattpocock/skills`, `awesome-design-md`, `taste-skill`, `shadcn/ui`, `Chrome DevTools MCP`, `Figma Context MCP`, `Context7`, etc.) follow the three-stage pattern: capability discovery via `peaks capabilities` before naming, references only (no execute/install/persist), Peaks-Loop CLI for all side effects. Do not execute upstream installers, do not install upstream resources, do not persist sensitive examples — Peaks-Loop gates remain authoritative. External skills inform, they do not approve.
12
12
 
13
- **OpenSpec lifecycle**: `render → validate → show → to-rd → validate → archive`. Code's default runbook handles the exit gate (validate → archive after QA pass). Entry-gate validation (to-rd before slicing) is available when `openspec/` exists pre-workflow; Code delegates it to `peaks-rd` during implementation.
14
-
15
13
  **MCP lifecycle**: `list → plan → apply --yes → call → rollback`. `apply` backs up settings and refuses non-peaks entries unless `--claim` is passed.
16
14
 
17
- Detailed rules: `references/external-skill-invocation.md`, `references/openspec-mcp-workflow.md`, `references/workflow.md`, `references/existing-system-extraction.md`. For an informational mapping of peaks artefact paths to the A2A (Agent2Agent) protocol's Task / Artifact / Part / Message / AgentCard vocabulary (no A2A implementation, just a shared naming layer), see `references/a2a-artifact-mapping.md`.
15
+ Detailed rules: `references/external-skill-invocation.md`, `references/workflow.md`, `references/existing-system-extraction.md`. For an informational mapping of peaks artefact paths to the A2A (Agent2Agent) protocol's Task / Artifact / Part / Message / AgentCard vocabulary (no A2A implementation, just a shared naming layer), see `references/a2a-artifact-mapping.md`.
@@ -40,7 +40,7 @@ The skill body must not silently:
40
40
  - commit or sync intermediate artifacts;
41
41
  - create remote repositories.
42
42
 
43
- All of these must route through the Peaks CLI under the appropriate command (`peaks artifacts …`, `peaks memory …`, `peaks openspec …`, `peaks standards …`, `peaks codegraph …`, `peaks capabilities …`), with dry-run preview where supported and `--yes` / `--apply` where a real write is required. The `peaks mcp …` command tree was retired in slice #016; MCP install / dispatch is the LLM runtime's job, not the CLI's.
43
+ All of these must route through the Peaks CLI under the appropriate command (`peaks artifacts …`, `peaks memory …`, `peaks standards …`, `peaks codegraph …`, `peaks capabilities …`), with dry-run preview where supported and `--yes` / `--apply` where a real write is required. The `peaks mcp …` command tree was retired in slice #016; MCP install / dispatch is the LLM runtime's job, not the CLI's. Note: peaks-code's 11-step workflow does NOT route through any external artifact lifecycle (OpenSpec or otherwise) — all reads/writes are confined to the LLM-authored artifact workspace under `.peaks/_runtime/<sessionId>/<role>/`. External CLI surfaces remain available for users who call them directly, but Code never calls them on the user's behalf as of the 2026-07-08 RR decoupling slice.
44
44
 
45
45
  ## Allowed in-process references
46
46
 
@@ -149,9 +149,7 @@ peaks sc retention --slice-id <rid> --prd <prd> --rd <rd> --qa <qa> --json
149
149
  peaks sc validate --slice-id <rid> --json
150
150
  peaks sc boundary --slice-id <rid> --artifact <artifact> --code <file> --json
151
151
 
152
- # 9. Peaks-Loop OpenSpec archive (exit gate; only after QA pass, when openspec/ exists)
153
- peaks openspec validate <cid> --project <repo> --json
154
- peaks openspec archive <cid> --project <repo> --apply --json
152
+ # 9. Peaks-Loop Workspace reconcile (slate cleanup)
155
153
  peaks workspace reconcile --project <repo> --apply --older-than 7
156
154
 
157
155
  # 10. Peaks-Loop TXT handoff — invoke peaks-txt which embeds memory markers (BLOCKING)
@@ -73,10 +73,11 @@ The decision is persisted via `peaks preferences set --project <root> --key auto
73
73
 
74
74
  ## How this integrates with the rest of the workflow
75
75
 
76
- - Step 0.5 (OpenSpec opt-in) — runs first if `openspec/` is missing.
77
76
  - Step 0 (anchor) — runs always.
78
77
  - Step 0.7 (resume detection) — runs after Step 0.
79
78
  - **Step 0.55 (1.x detection) — runs after Step 0.7, only when the project is not on a 2.0 layout.**
80
79
  - Step 1 (mode selection) — runs after Step 0.55.
81
80
 
82
81
  The 1.x detection is intentionally placed AFTER Step 0.7 because the user might already have a 1.x-converted in-flight slice; the resume flow takes precedence over the upgrade prompt.
82
+
83
+ > **Historical note (prior to 2026-07-08 RR slice):** an OpenSpec first-run opt-in step (Step 0.5) used to sit between Step 0 and Step 0.7. peaks-code is now decoupled from OpenSpec and does not surface that step; this reference still preserves the historical ordering for context.
@@ -44,7 +44,7 @@ When Peaks-Loop Code coordinates development in a code repository, keep this ord
44
44
  6. **Peaks-Loop QA validation** (auto-proceed from RD in full-auto) — execute test cases + API checks + Playwright MCP headed browser E2E for frontend + security/perf checks + test report;
45
45
  7. **Peaks-Loop RD↔QA repair loop** — if QA verdict is `return-to-rd`, loop back to step 4 (RD implementation) and re-run through QA; max 3 repair cycles, then emit blocked TXT regardless;
46
46
  8. **Peaks-Loop SC phase** — change-control evidence: impact, retention, validate, boundary;
47
- 9. **Peaks-Loop OpenSpec archive** — exit gate: validate → archive only after QA verdict=pass (when `openspec/` exists);
47
+ 9. **Peaks-Loop Workspace reconcile** — slate cleanup;
48
48
  10. **Peaks-Loop TXT handoff capsule** — mode, validated decisions, artifact paths, standards deltas, open questions, next action;
49
49
  11. **Peaks-Loop Final snapshot** — `peaks project dashboard` + `peaks skill doctor` to confirm the workflow closed cleanly.
50
50
 
@@ -1,81 +0,0 @@
1
- # OpenSpec Lifecycle for Peaks Code
2
-
3
- Peaks Code orchestrates RD, QA, and SC. When the target repository uses OpenSpec, Code must drive the full lifecycle through the Peaks CLI so each role works against the same stable surface.
4
-
5
- > **Slice #016 (2026-06-09)**: this document used to live at `openspec-mcp-workflow.md` and contained a section on the `peaks mcp *` lifecycle. The MCP subsystem was retired in slice #016; that section is gone. The OpenSpec lifecycle described below is unchanged.
6
-
7
- ## OpenSpec change lifecycle
8
-
9
- ```text
10
- peaks openspec render → RD authors a change pack (dry-run, then --apply)
11
- peaks openspec validate → Code gates RD output before slicing starts
12
- peaks openspec show → any role reads parsed proposal/tasks state
13
- peaks openspec to-rd → RD projects the pack into refactor slice input
14
- SC projects it into commit boundary candidates
15
- peaks openspec validate → QA gates the final state before sign-off
16
- peaks openspec archive → Code moves the change under changes/archive/<id>/
17
- ```
18
-
19
- Rules Code applies:
20
-
21
- - `render --apply` is the only Peaks-managed way to write a change pack. Other roles must not hand-edit `openspec/changes/**`.
22
- - `validate` is run twice per change in a refactor flow: once before slicing (RD entry gate) and once before archive (QA exit gate). Both must end with `data.valid === true`.
23
- - `archive --apply` is the lifecycle terminator; Code only invokes it after QA acceptance and SC commit.
24
-
25
- ## Refactor workflow wiring
26
-
27
- For `peaks-code refactor` runs against a repository with `openspec/`:
28
-
29
- 1. RD entry gate — `peaks openspec validate <id>` must pass and `peaks openspec to-rd <id>` must return `acceptance.length > 0`.
30
- 2. Each slice must reference one OpenSpec tasks section as its commit boundary (per `references/openspec-commit-boundaries.md` in peaks-sc).
31
- 3. QA exit gate — re-run `peaks openspec validate <id>` after implementation; record the result in the QA validation report.
32
- 4. Archive — `peaks openspec archive <id> --apply` only after QA passes the exit gate and SC closes the final commit.
33
-
34
- If the consuming LLM needs an MCP server for docs lookup or research (e.g. Context7), it checks its own tool list for `mcp__<server>__*` and tells the user the install command if absent. peaks-loop is no longer in the install path; the LLM is the executor, the IDE is the dispatcher.
35
-
36
- ## Boundary
37
-
38
- Code must not write `openspec/changes/**` or `~/.claude/settings.json` directly. Every mutation goes through the CLI commands above. The CLI returns stable envelopes; Code captures them as artifact links rather than re-explaining their content in the handoff.
39
-
40
- ---
41
-
42
- # Step 0.5 — OpenSpec first-run opt-in (conditional)
43
-
44
- > Body of `### Peaks-Loop Step 0.5`. After the workspace is anchored, before project scan, Code checks whether
45
- the project already has an `openspec/` directory. The lifecycle
46
- (`render → validate → show → to-rd → validate → archive`) only applies
47
- when `openspec/` exists; without it, RD/QA/SC silently skip the
48
- openspec-aware paths and you lose change-proposal tracking, commit
49
- boundaries from `tasks.md`, and the historical archive.
50
-
51
- To make that opt-in visible instead of silent, Code runs:
52
-
53
- ```bash
54
- # 1. Detect whether the project already has openspec/.
55
- ls <repo>/openspec/changes 2>&1
56
- # 2. If absent, ask the user once — only on the first Code run in this
57
- # project. The decision is sticky: write it to .peaks/.peaks-openspec-opt-in.json
58
- # so subsequent Code invocations do not re-ask.
59
- test -f <repo>/.peaks/.peaks-openspec-opt-in.json || \
60
- echo "{\"enabled\": <bool>}" > <repo>/.peaks/.peaks-openspec-opt-in.json
61
- ```
62
-
63
- **AskUserQuestion** (only when `openspec/` is absent and the opt-in
64
- file is missing):
65
-
66
- | Option | What it does |
67
- |---|---|
68
- | Enable OpenSpec for this project (Recommended) | Run `peaks openspec init --project <repo> --apply`. After that, every Code run uses the change-proposal lifecycle for the same project. |
69
- | Skip for now | Do nothing. Code proceeds without openspec; the question is re-asked on the next first-run detection. |
70
- | Never ask again for this project | Write `{enabled: false, sticky: true}`. Code stops asking. The user can re-enable later by removing `.peaks/.peaks-openspec-opt-in.json` and re-running. |
71
-
72
- The first option is the recommended default because it gives Code the
73
- full change-proposal lifecycle (proposal / tasks / design / specs
74
- deltas, archive on ship, commit boundaries from `tasks.md`). It costs
75
- only a single scaffolded directory and pays back the first time the
76
- project needs a real review trail.
77
-
78
- If the user picks "Enable", the only required follow-up is to make
79
- sure `openspec/changes/` is added to git (it is part of the project
80
- repo, not a tool-managed artefact). Code does not run `git add` for
81
- the user; that is the user's commit boundary.