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.
- package/dist/src/cli/commands/adapter-commands-s2a.d.ts +34 -0
- package/dist/src/cli/commands/adapter-commands-s2a.js +92 -0
- package/dist/src/cli/commands/polyrepo-commands.d.ts +37 -0
- package/dist/src/cli/commands/polyrepo-commands.js +155 -0
- package/dist/src/cli/commands/runtime-commands.d.ts +52 -0
- package/dist/src/cli/commands/runtime-commands.js +150 -0
- package/dist/src/cli/program.js +16 -0
- package/dist/src/services/adapter/adapter-registry.d.ts +48 -0
- package/dist/src/services/adapter/adapter-registry.js +186 -0
- package/dist/src/services/polyrepo/polyrepo-dispatcher.d.ts +34 -0
- package/dist/src/services/polyrepo/polyrepo-dispatcher.js +119 -0
- package/dist/src/services/polyrepo/polyrepo-scanner.d.ts +9 -0
- package/dist/src/services/polyrepo/polyrepo-scanner.js +94 -0
- package/dist/src/services/polyrepo/polyrepo-service.d.ts +34 -0
- package/dist/src/services/polyrepo/polyrepo-service.js +49 -0
- package/dist/src/services/polyrepo/polyrepo-types.d.ts +55 -0
- package/dist/src/services/polyrepo/polyrepo-types.js +1 -0
- package/dist/src/services/runtime/runtime-detector.d.ts +15 -0
- package/dist/src/services/runtime/runtime-detector.js +62 -0
- package/dist/src/services/runtime/runtime-service.d.ts +59 -0
- package/dist/src/services/runtime/runtime-service.js +51 -0
- package/dist/src/services/runtime/vendor-adapter.d.ts +59 -0
- package/dist/src/services/runtime/vendor-adapter.js +1 -0
- package/dist/src/services/runtime/vendors/claude-code.d.ts +7 -0
- package/dist/src/services/runtime/vendors/claude-code.js +39 -0
- package/dist/src/services/runtime/vendors/codex.d.ts +7 -0
- package/dist/src/services/runtime/vendors/codex.js +43 -0
- package/dist/src/services/runtime/vendors/copilot.d.ts +7 -0
- package/dist/src/services/runtime/vendors/copilot.js +40 -0
- package/dist/src/shared/version.d.ts +1 -1
- package/dist/src/shared/version.js +1 -1
- package/package.json +1 -1
- package/skills/peaks-code/SKILL.md +1 -8
- package/skills/peaks-code/references/external-references.md +1 -3
- package/skills/peaks-code/references/external-skill-invocation.md +1 -1
- package/skills/peaks-code/references/runbook.md +1 -3
- package/skills/peaks-code/references/step-0-55-1x-detection.md +2 -1
- package/skills/peaks-code/references/workflow-gates-and-types.md +1 -1
- 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.
|
|
1
|
+
export declare const CLI_VERSION = "4.0.0-beta.6";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const CLI_VERSION = "4.0.0-beta.
|
|
1
|
+
export const CLI_VERSION = "4.0.0-beta.6";
|
package/package.json
CHANGED
|
@@ -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 *`.
|
|
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/
|
|
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
|
|
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
|
|
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
|
|
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.
|