peaks-loop 4.0.0-beta.5 → 4.0.0-beta.7
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/CHANGELOG.md +62 -0
- 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/ide-commands.d.ts +25 -0
- package/dist/src/cli/commands/ide-commands.js +42 -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 +23 -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/config/model-routing.d.ts +55 -2
- package/dist/src/services/config/model-routing.js +79 -3
- package/dist/src/services/ide/adapters/zcode-adapter.d.ts +40 -0
- package/dist/src/services/ide/adapters/zcode-adapter.js +220 -0
- package/dist/src/services/ide/current-model-detector.d.ts +14 -0
- package/dist/src/services/ide/current-model-detector.js +60 -0
- package/dist/src/services/ide/ide-registry.js +3 -0
- package/dist/src/services/ide/ide-types.d.ts +24 -1
- 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/preferences/preferences-types.d.ts +1 -1
- package/dist/src/services/providers/minimax-worker-service.d.ts +1 -1
- package/dist/src/services/providers/minimax-worker-service.js +1 -1
- package/dist/src/services/rd/rd-service.js +7 -4
- 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/services/workflow/workflow-router-service.d.ts +1 -1
- package/dist/src/services/workflow/workflow-router-service.js +37 -31
- package/dist/src/shared/version.d.ts +1 -1
- package/dist/src/shared/version.js +1 -1
- package/package.json +1 -1
- package/scripts/install-skills.mjs +13 -10
- package/skills/peaks-code/SKILL.md +26 -12
- 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-slice-decompose/SKILL.md +61 -1
- package/skills/peaks-code/references/openspec-workflow.md +0 -81
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,68 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## 4.0.0-beta.7 — 2026-07-09
|
|
6
|
+
|
|
7
|
+
### Added — zcode adapter (9th IDE) + runtime model detection
|
|
8
|
+
|
|
9
|
+
- **`peaks ide model --current` CLI** — `src/cli/commands/ide-commands.ts` (80 lines) + register in `src/cli/program.ts`. Outputs `{ modelId, detected, registeredAdapters }`. Available as a top-level primitive (not adapter-specific).
|
|
10
|
+
- **`detectCurrentIdeModel()` service** — `src/services/ide/current-model-detector.ts` (43 lines). Walks the adapter registry in insertion order, calls each adapter's optional `detectCurrentModel()` with try/catch isolation — one bad adapter cannot poison the chain.
|
|
11
|
+
- **zcode-adapter `detectCurrentModel()` method** — `src/services/ide/adapters/zcode-adapter.ts`. Reads `~/.zcode/v2/config.json`, resolves active provider via 4-tier priority chain (env override → non-`builtin:` provider → first enabled → first provider), returns the model's id. Live test: `peaks ide model --current` against a real z-code installation → `"modelId": "M3"`.
|
|
12
|
+
- **`IdeAdapter` interface optional field** — `readonly detectCurrentModel?: () => Promise<string | undefined>` in `src/services/ide/ide-types.ts`. Back-compat: 8 existing adapters without the field are unaffected.
|
|
13
|
+
- **`getStrongestModelIdAsync()` async variant** — `src/services/config/model-routing.ts`. Sync `getStrongestModelId()` is unchanged (rd-service compat); new async variant lets future async callers fall back to runtime probe instead of `'claude-opus-4-7'`.
|
|
14
|
+
|
|
15
|
+
### Changed — install no longer writes hardcoded default model
|
|
16
|
+
|
|
17
|
+
- **`scripts/install-skills.mjs`** — removed `model: 'sonnet'` default and `providers.minimax.model` default from `createConfigDefaults()`. After install, `~/.peaks/config.json` no longer contains a Claude model recommendation; users set their own via `peaks config set model <id>` (or leave unset and the back-compat fallback applies).
|
|
18
|
+
- **`STRONGEST_MODEL_ID` constant removed** — replaced by `getStrongestModelId(config?)` function in `src/services/config/model-routing.ts`. Resolution order: `config.model` → `PEAKS_STRONGEST_MODEL_DEFAULT` env var → back-compat `'claude-opus-4-7'`.
|
|
19
|
+
- **`workflow-router-service.ts` / `minimax-worker-service.ts` / `rd-service.ts`** — threaded dynamic modelId through 16 call sites in `workflow-router-service.ts`, replaced literal `'claude-opus-4-7'` in `minimax-worker-service.ts` (×2) and `rd-service.ts`.
|
|
20
|
+
|
|
21
|
+
### Added — zcode IDE (peaks-loop's 9th)
|
|
22
|
+
|
|
23
|
+
- **`IdeId` union type** — `'zcode'` added in `src/services/ide/ide-types.ts`.
|
|
24
|
+
- **`ZCODE_ADAPTER`** — new `src/services/ide/adapters/zcode-adapter.ts` (98 lines). Template: `claude-code-adapter.ts` with `compact.compactCommand` / `hookEvent` / `toolMatcher` / `envVar` degraded (z-code is a desktop application, no CLI binary; UNVERIFIED sentinel strings used where the interface requires non-optional types).
|
|
25
|
+
- **`IDE_DETECTION_DIRS` + `IDE_SKILL_INSTALL_PROFILES`** — `scripts/install-skills.mjs`. New `{ id: 'zcode', dir: '.zcode' }` detection entry + zcode profile (skillsDir `~/.zcode/skills`, env override `PEAKS_ZCODE_SKILLS_DIR`).
|
|
26
|
+
- **`ide-registry.ts`** — registers `ZCODE_ADAPTER` alongside the existing 8 adapters (claude-code / trae / trae-cn / codex / cursor / qoder / tongyi-lingma / hermes / openclaw).
|
|
27
|
+
- **`tests/unit/ide/zcode-adapter.test.ts`** — 10 cases (T-1 id, T-2 dirName, T-3 standards rootFile = `CLAUDE.md`, T-4 rulesDir = `.claude/rules`, T-5 skillsDir, T-6 compactCommand undefined, T-7 hookEvent is string, T-8 type test, T-9 IDE_DETECTION_DIRS contains, T-10 IDE_SKILL_INSTALL_PROFILES contains).
|
|
28
|
+
- **`tests/unit/ide/zcode-adapter-detect-model.test.ts`** — 14 cases (pure resolver + env-fixture file IO + path helper).
|
|
29
|
+
- **`tests/unit/cli/ide-commands.test.ts`** — 5 cases (CLI + top-level command registration).
|
|
30
|
+
- **Fixture sync** — `tests/unit/install-skills-script.test.ts` + `tests/unit/cli-program.workflow.test.ts` (Strategy A: env-var override, no hardcoded literals) + `tests/unit/ide/ide-registry.test.ts` (whitelist 6 → 7 adapters).
|
|
31
|
+
|
|
32
|
+
### Documentation — SKILL.md sync with 4.0.0-beta.6 CLI reality
|
|
33
|
+
|
|
34
|
+
- **`skills/peaks-code/SKILL.md`** — added inline `> CLI reality check` blocks at §Step 0.8 (D-001 `peaks code detect-job` is `peaks job init`), §Step 0.8 third paragraph (D-003 `JOB_SHAPE_NOT_DECIDED` is now passive `done: 0` warning, not exception), §Step 2.5 (D-002 `peaks session title` takes positional sid, not `--session-id` flag), §Step 11c + 11d (D-010 `<!-- peaks-memory:start -->` block requires YAML frontmatter `title:` + `kind:` + `---` + closing `<!-- peaks-memory:end -->`, otherwise `extractedCount: 0` silently). New **§CLI Drift Index** section after Boundaries provides single landing page for D-001/002/003/010 with inline-anchor table.
|
|
35
|
+
- **`skills/peaks-slice-decompose/SKILL.md`** — new **§SC template hard rules (sediment 2026-07-09)** section with 3 rules: Rule 1 ESM/CJS pre-flight before specifying export syntax (D-009c), Rule 2 whitelist fixture sync is a primary task not collateral (D-009a), Rule 3 adapter-interface optional fields > required (D-009b reciprocal).
|
|
36
|
+
|
|
37
|
+
### Verification (4.0.0-beta.7)
|
|
38
|
+
|
|
39
|
+
- `pnpm tsc -p tsconfig.json --noEmit` — 0 errors
|
|
40
|
+
- `pnpm vitest run tests/unit/ide/ tests/unit/cli/ide-commands.test.ts` — 15 files / 211 tests / 0 failed
|
|
41
|
+
- `peaks workflow verify-pipeline --rid 003-add-zcode-adapter` — `ok=true, complete=true`, 9/9 gate PASS
|
|
42
|
+
- `node bin/peaks.js ide model --current` (against real z-code installation) — `modelId: "M3"`, `detected: true`
|
|
43
|
+
|
|
44
|
+
### Sediment (Step 11) — 7 lessons in `.peaks/memory/`
|
|
45
|
+
|
|
46
|
+
| File | Topic |
|
|
47
|
+
|---|---|
|
|
48
|
+
| `z-code-peaks-loop-9-ide-adapter-vendor-neutrality-adapter.md` | z-code 9th IDE + vendor-neutrality adapter pattern |
|
|
49
|
+
| `peaks-loop-install-model-getstrongestmodelid-fallback.md` | install no longer hardcodes default model |
|
|
50
|
+
| `desktop-application-ide-adapter-z-code-cli.md` | desktop-app IDE adapter field-degradation decision |
|
|
51
|
+
| `peaks-code-runbook-4-0-0-beta-6-skill-md-cli-d-001-d-002-d-003-d-010.md` | 4 SKILL.md / CLI drift points |
|
|
52
|
+
| `2026-07-09-zcode-adapter-overview.md` | RID 003 overall summary (project record) |
|
|
53
|
+
| `peaks-ide-runtime-detect-zcode-only.md` | `peaks ide model --current` z-code 4-tier priority chain |
|
|
54
|
+
| `ide-adapter-detectcurrentmodel-optional-interface-pattern.md` | optional interface field extension pattern |
|
|
55
|
+
|
|
56
|
+
### Known follow-ups (S3-cleanup backlog)
|
|
57
|
+
|
|
58
|
+
- **Slice C extension** — other 8 IDEs' `detectCurrentModel()` (claude-code / trae / cursor / codex / qoder / tongyi-lingma / hermes / openclaw). Currently only z-code has a working detector; all 8 still fall through to the back-compat `'claude-opus-4-7'`.
|
|
59
|
+
- **zai / GLM provider entry** — `D-008`: peaks-loop has no built-in provider entry for z-code's default `builtin:zai` (GLM) or `builtin:bigmodel` providers. Users on those providers must add custom `provider.<uuid>` entries to their `~/.peaks/config.json` themselves.
|
|
60
|
+
- **zcode-adapter UNVERIFIED sentinel values** — `hookEvent: 'PreToolUse'`, `toolMatcher: 'Bash'`, `envVar: 'ZCODE_PROJECT_DIR'` are placeholder guesses based on Anthropic-compatible protocol. Real z-code desktop application dogfood will reveal the actual values; replace once known.
|
|
61
|
+
- **`IdeAdapter` interface optional fields** — `D-009b` proposes converting `hookEvent` / `toolMatcher` / `envVar` to optional types, or adding an `unverified: boolean` flag, so future adapters with unknown protocols don't need to fabricate sentinel values.
|
|
62
|
+
|
|
63
|
+
### Breaking changes
|
|
64
|
+
|
|
65
|
+
None. All 8 pre-existing adapters continue to load without changes. CLI subcommands `peaks ide *` and `peaks ide model --current` are additive.
|
|
66
|
+
|
|
5
67
|
## 4.0.0-beta.5 — 2026-07-08
|
|
6
68
|
|
|
7
69
|
### Added — peaks-solo dispatcher (分诊员)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `peaks adapter *` — vendor adapter registry CLI surface.
|
|
3
|
+
*
|
|
4
|
+
* Slice S2-a of RD-2 (2026-07-08 session). Two subcommands:
|
|
5
|
+
*
|
|
6
|
+
* list — list registered user adapters (persisted at
|
|
7
|
+
* `.peaks/runtime/adapters.json`).
|
|
8
|
+
* register — register a new adapter. Vendor verb strings live in
|
|
9
|
+
* the binary the user picks, NOT in peaks-loop source.
|
|
10
|
+
*
|
|
11
|
+
* This file coexists with the older `adapter-commands.ts` (slice
|
|
12
|
+
* 2026-07-04-cli-15a) which exposes `peaks skill adapter <verb>` —
|
|
13
|
+
* a different surface for skill materialization (resolveScratchDir /
|
|
14
|
+
* materialize / publish). The two surfaces do NOT collide because
|
|
15
|
+
* one is `peaks skill adapter` and the other is `peaks adapter`.
|
|
16
|
+
*/
|
|
17
|
+
import type { Command } from 'commander';
|
|
18
|
+
import { type ProgramIO } from '../cli-helpers.js';
|
|
19
|
+
export interface AdapterListOptions {
|
|
20
|
+
json?: boolean;
|
|
21
|
+
project?: string;
|
|
22
|
+
}
|
|
23
|
+
export interface AdapterRegisterOptions {
|
|
24
|
+
json?: boolean;
|
|
25
|
+
id?: string;
|
|
26
|
+
/** Display name override (defaults to the id). */
|
|
27
|
+
name?: string;
|
|
28
|
+
binary?: string;
|
|
29
|
+
/** Repeated --arg values. */
|
|
30
|
+
arg?: string[];
|
|
31
|
+
force?: boolean;
|
|
32
|
+
project?: string;
|
|
33
|
+
}
|
|
34
|
+
export declare function registerAdapterS2ACommands(program: Command, io: ProgramIO): void;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { addJsonOption, printResult } from '../cli-helpers.js';
|
|
3
|
+
import { fail, ok, getErrorMessage } from '../../shared/result.js';
|
|
4
|
+
import { AdapterRegistry } from '../../services/adapter/adapter-registry.js';
|
|
5
|
+
import { registryFileFor } from './runtime-commands.js';
|
|
6
|
+
function resolveProjectRoot(explicit) {
|
|
7
|
+
if (explicit !== undefined && explicit.length > 0)
|
|
8
|
+
return explicit;
|
|
9
|
+
return process.cwd();
|
|
10
|
+
}
|
|
11
|
+
export function registerAdapterS2ACommands(program, io) {
|
|
12
|
+
const adapter = program
|
|
13
|
+
.command('adapter')
|
|
14
|
+
.description('Vendor adapter registry (user-registered, persisted to .peaks/runtime/adapters.json)');
|
|
15
|
+
// -----------------------------------------------------------------
|
|
16
|
+
// 1. peaks adapter list [--json] [--project <root>]
|
|
17
|
+
// -----------------------------------------------------------------
|
|
18
|
+
addJsonOption(adapter
|
|
19
|
+
.command('list')
|
|
20
|
+
.description('List user-registered vendor adapters')
|
|
21
|
+
.option('--project <path>', 'project root (defaults to cwd)')).action((options) => {
|
|
22
|
+
try {
|
|
23
|
+
const projectRoot = resolveProjectRoot(options.project);
|
|
24
|
+
const file = registryFileFor(projectRoot);
|
|
25
|
+
const reg = new AdapterRegistry();
|
|
26
|
+
if (existsSync(file))
|
|
27
|
+
reg.load(file);
|
|
28
|
+
const records = reg.list();
|
|
29
|
+
printResult(io, ok('adapter.list', { projectRoot, file, records, count: records.length }, [], [
|
|
30
|
+
records.length === 0
|
|
31
|
+
? 'No adapters registered yet. Run `peaks adapter register --id <vendor> --binary <cmd>` to add one.'
|
|
32
|
+
: `Run \`peaks runtime compact --via <id>\` to invoke a registered adapter.`
|
|
33
|
+
]), options.json);
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
printResult(io, fail('adapter.list', 'ADAPTER_LIST_FAILED', getErrorMessage(error), {}, []), options.json);
|
|
37
|
+
process.exitCode = 1;
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
// -----------------------------------------------------------------
|
|
41
|
+
// 2. peaks adapter register --id <id> --binary <cmd> [--arg <a>]... [--force]
|
|
42
|
+
// -----------------------------------------------------------------
|
|
43
|
+
addJsonOption(adapter
|
|
44
|
+
.command('register')
|
|
45
|
+
.description('Register a new vendor adapter; persists to .peaks/runtime/adapters.json')
|
|
46
|
+
.requiredOption('--id <id>', 'vendor adapter id (lowercase, e.g. my-cli)')
|
|
47
|
+
.option('--name <name>', 'human-readable display name (defaults to <id>)')
|
|
48
|
+
.requiredOption('--binary <cmd>', 'binary name to invoke for compact (e.g. my-cli)')
|
|
49
|
+
.option('--arg <value>', 'extra arg appended before --force (repeatable)', (value, previous = []) => [...previous, value])
|
|
50
|
+
.option('--force', 'overwrite an existing adapter with the same id')
|
|
51
|
+
.option('--project <path>', 'project root (defaults to cwd)')).action((options) => {
|
|
52
|
+
try {
|
|
53
|
+
const projectRoot = resolveProjectRoot(options.project);
|
|
54
|
+
const file = registryFileFor(projectRoot);
|
|
55
|
+
const reg = new AdapterRegistry();
|
|
56
|
+
if (existsSync(file))
|
|
57
|
+
reg.load(file);
|
|
58
|
+
const id = options.id ?? '';
|
|
59
|
+
const record = options.arg !== undefined
|
|
60
|
+
? { id, displayName: options.name ?? id, binary: options.binary ?? '', args: options.arg }
|
|
61
|
+
: { id, displayName: options.name ?? id, binary: options.binary ?? '' };
|
|
62
|
+
let result;
|
|
63
|
+
try {
|
|
64
|
+
result = reg.register(record, { force: options.force === true });
|
|
65
|
+
}
|
|
66
|
+
catch (validationError) {
|
|
67
|
+
printResult(io, fail('adapter.register', 'INVALID_ADAPTER_RECORD', getErrorMessage(validationError), { id }, [
|
|
68
|
+
'Use --id with /^[a-z0-9][a-z0-9._-]*$/, --binary as a binary name (no path separators).'
|
|
69
|
+
]), options.json);
|
|
70
|
+
process.exitCode = 1;
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
reg.persist(file);
|
|
74
|
+
const warnings = [];
|
|
75
|
+
if (!result.created && options.force !== true) {
|
|
76
|
+
warnings.push(`adapter "${id}" already registered; pass --force to overwrite`);
|
|
77
|
+
}
|
|
78
|
+
printResult(io, ok('adapter.register', {
|
|
79
|
+
projectRoot,
|
|
80
|
+
file,
|
|
81
|
+
adapter: result.record,
|
|
82
|
+
created: result.created
|
|
83
|
+
}, warnings, [
|
|
84
|
+
`Run \`peaks runtime compact --via ${id}\` to invoke it.`
|
|
85
|
+
]), options.json);
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
printResult(io, fail('adapter.register', 'ADAPTER_REGISTER_FAILED', getErrorMessage(error), {}, []), options.json);
|
|
89
|
+
process.exitCode = 1;
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* peaks ide * CLI (Slice 2026-07-09 add-zcode-adapter, Slice C).
|
|
3
|
+
*
|
|
4
|
+
* Slice C adds ONE verb:
|
|
5
|
+
*
|
|
6
|
+
* peaks ide model --current
|
|
7
|
+
*
|
|
8
|
+
* Outputs the currently-active model id detected from the host
|
|
9
|
+
* environment (so far: only the `zcode` adapter opts in via
|
|
10
|
+
* `IdeAdapter.detectCurrentModel?`). The CLI is the read-only
|
|
11
|
+
* debugging surface for runtime model probing. It does NOT set any
|
|
12
|
+
* state — `peaks config set model` remains the canonical write path.
|
|
13
|
+
*
|
|
14
|
+
* Future slices will extend this surface with `peaks ide list`,
|
|
15
|
+
* `peaks ide detect`, etc. — this slice keeps the surface minimal
|
|
16
|
+
* (per Karpathy guideline #2 — Simplicity First).
|
|
17
|
+
*
|
|
18
|
+
* Vendor-neutrality: this file delegates to
|
|
19
|
+
* `src/services/ide/current-model-detector.ts` which walks every
|
|
20
|
+
* registered adapter. This file does NOT import any specific
|
|
21
|
+
* adapter.
|
|
22
|
+
*/
|
|
23
|
+
import { Command } from 'commander';
|
|
24
|
+
import { type ProgramIO } from '../cli-helpers.js';
|
|
25
|
+
export declare function registerIdeCommands(program: Command, io: ProgramIO): void;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { detectCurrentIdeModel } from '../../services/ide/current-model-detector.js';
|
|
2
|
+
import { addJsonOption, printResult } from '../cli-helpers.js';
|
|
3
|
+
import { fail, ok } from '../../shared/result.js';
|
|
4
|
+
import { listAdapterIds } from '../../services/ide/ide-registry.js';
|
|
5
|
+
export function registerIdeCommands(program, io) {
|
|
6
|
+
const ide = program.command('ide').description('Read-only introspection helpers for the registered IDE adapter layer (slice 2026-07-09 add-zcode-adapter, Slice C)');
|
|
7
|
+
addJsonOption(ide
|
|
8
|
+
.command('model')
|
|
9
|
+
.description('Inspect the IDE adapter model surface. Slice C supports `--current` only.')
|
|
10
|
+
.option('--current', 'Print the model id the active IDE adapter reports as currently configured')
|
|
11
|
+
.action(async (options) => {
|
|
12
|
+
if (options.current) {
|
|
13
|
+
await runIdeModelCurrent(io, options);
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
// No verb supplied — Commander would normally show help; we
|
|
17
|
+
// emit a structured error instead so the JSON envelope stays
|
|
18
|
+
// consistent for LLM callers.
|
|
19
|
+
printResult(io, fail('ide.model', 'MISSING_VERB', 'Specify a verb: --current. Future slices may add --configured / --default.', { registeredAdapters: listAdapterIds() }, ['Rerun with --current to read the runtime-detected model id.']), options.json);
|
|
20
|
+
}));
|
|
21
|
+
}
|
|
22
|
+
async function runIdeModelCurrent(io, options) {
|
|
23
|
+
try {
|
|
24
|
+
const modelId = await detectCurrentIdeModel();
|
|
25
|
+
if (modelId === undefined) {
|
|
26
|
+
printResult(io, ok('ide.model.current', {
|
|
27
|
+
modelId: null,
|
|
28
|
+
detected: false,
|
|
29
|
+
registeredAdapters: listAdapterIds(),
|
|
30
|
+
}), options.json);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
printResult(io, ok('ide.model.current', {
|
|
34
|
+
modelId,
|
|
35
|
+
detected: true,
|
|
36
|
+
registeredAdapters: listAdapterIds(),
|
|
37
|
+
}), options.json);
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
printResult(io, fail('ide.model.current', 'DETECTION_FAILED', 'Runtime model detection failed; falling back to configured model. See logs.', { error: error instanceof Error ? error.message : String(error) }, ['Check `PEAKS_LOG_LEVEL=debug peaks ide model --current` for the underlying error.']), options.json);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `peaks polyrepo *` — polyrepo workflow CLI surface.
|
|
3
|
+
*
|
|
4
|
+
* Slice S2-b of RD-2 (2026-07-08 session). Three subcommands:
|
|
5
|
+
*
|
|
6
|
+
* init — scan the parent dir for child git repos + write a
|
|
7
|
+
* polyrepo manifest to `.peaks/polyrepo.json`.
|
|
8
|
+
* status — read the persisted manifest + report children.
|
|
9
|
+
* dispatch — mirror a PRD/RD/QA artifact from the parent into
|
|
10
|
+
* each target child's
|
|
11
|
+
* `.peaks/_runtime/<sid>/<role>/` directory.
|
|
12
|
+
*
|
|
13
|
+
* Per the project's two-axis convention, all reviewable artifacts
|
|
14
|
+
* live under `.peaks/_runtime/<sid>/<role>/` — never as top-level
|
|
15
|
+
* siblings of `.peaks/_runtime/`.
|
|
16
|
+
*/
|
|
17
|
+
import type { Command } from 'commander';
|
|
18
|
+
import { type ProgramIO } from '../cli-helpers.js';
|
|
19
|
+
export interface PolyrepoInitOptions {
|
|
20
|
+
json?: boolean;
|
|
21
|
+
root?: string;
|
|
22
|
+
children?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface PolyrepoStatusOptions {
|
|
25
|
+
json?: boolean;
|
|
26
|
+
root?: string;
|
|
27
|
+
}
|
|
28
|
+
export interface PolyrepoDispatchOptions {
|
|
29
|
+
json?: boolean;
|
|
30
|
+
root?: string;
|
|
31
|
+
rid?: string;
|
|
32
|
+
to?: string;
|
|
33
|
+
role?: 'prd' | 'rd' | 'qa';
|
|
34
|
+
artifact?: string;
|
|
35
|
+
sid?: string;
|
|
36
|
+
}
|
|
37
|
+
export declare function registerPolyrepoCommands(program: Command, io: ProgramIO): void;
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { addJsonOption, printResult } from '../cli-helpers.js';
|
|
3
|
+
import { fail, ok, getErrorMessage } from '../../shared/result.js';
|
|
4
|
+
import { PolyrepoService } from '../../services/polyrepo/polyrepo-service.js';
|
|
5
|
+
function parseChildren(raw) {
|
|
6
|
+
if (raw === undefined || raw.length === 0)
|
|
7
|
+
return undefined;
|
|
8
|
+
return raw.split(',').map((s) => s.trim()).filter((s) => s.length > 0);
|
|
9
|
+
}
|
|
10
|
+
function resolveRoot(explicit) {
|
|
11
|
+
if (explicit !== undefined && explicit.length > 0)
|
|
12
|
+
return explicit;
|
|
13
|
+
return process.cwd();
|
|
14
|
+
}
|
|
15
|
+
export function registerPolyrepoCommands(program, io) {
|
|
16
|
+
const polyrepo = program
|
|
17
|
+
.command('polyrepo')
|
|
18
|
+
.description('Polyrepo workflow (parent dir + child git repos with their own .peaks/ subsets)');
|
|
19
|
+
// -----------------------------------------------------------------
|
|
20
|
+
// 1. peaks polyrepo init [--children <dir1,dir2>] [--root <dir>]
|
|
21
|
+
// -----------------------------------------------------------------
|
|
22
|
+
addJsonOption(polyrepo
|
|
23
|
+
.command('init')
|
|
24
|
+
.description('Scan the parent dir for child git repos and write .peaks/polyrepo.json')
|
|
25
|
+
.option('--children <list>', 'comma-separated child directory names (defaults to auto-discovery)')
|
|
26
|
+
.option('--root <path>', 'parent root (defaults to cwd)')).action((options) => {
|
|
27
|
+
try {
|
|
28
|
+
const root = resolveRoot(options.root);
|
|
29
|
+
const svc = new PolyrepoService();
|
|
30
|
+
const children = parseChildren(options.children);
|
|
31
|
+
const result = children === undefined
|
|
32
|
+
? svc.init({ root })
|
|
33
|
+
: svc.init({ root, children });
|
|
34
|
+
printResult(io, ok('polyrepo.init', {
|
|
35
|
+
root: result.manifest.root,
|
|
36
|
+
detectedAt: result.manifest.detectedAt,
|
|
37
|
+
children: result.manifest.children,
|
|
38
|
+
manifestPath: `${result.manifest.root}/.peaks/polyrepo.json`,
|
|
39
|
+
created: result.created
|
|
40
|
+
}, [], [
|
|
41
|
+
result.manifest.children.length === 0
|
|
42
|
+
? 'No child git repos detected. Pass --children <dir1,dir2> to override.'
|
|
43
|
+
: `Run \`peaks polyrepo dispatch --rid <rid> --to <child-id>\` to mirror an artifact into one or more children.`
|
|
44
|
+
]), options.json);
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
printResult(io, fail('polyrepo.init', 'POLYREPO_INIT_FAILED', getErrorMessage(error), {}, ['Verify --root exists and is a directory']), options.json);
|
|
48
|
+
process.exitCode = 1;
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
// -----------------------------------------------------------------
|
|
52
|
+
// 2. peaks polyrepo status [--root <dir>]
|
|
53
|
+
// -----------------------------------------------------------------
|
|
54
|
+
addJsonOption(polyrepo
|
|
55
|
+
.command('status')
|
|
56
|
+
.description('Report the persisted polyrepo manifest + children')
|
|
57
|
+
.option('--root <path>', 'parent root (defaults to cwd)')).action((options) => {
|
|
58
|
+
try {
|
|
59
|
+
const root = resolveRoot(options.root);
|
|
60
|
+
const svc = new PolyrepoService();
|
|
61
|
+
const result = svc.status(root);
|
|
62
|
+
if (!result.manifestExists) {
|
|
63
|
+
printResult(io, ok('polyrepo.status', { ...result, initialized: false }, [], [
|
|
64
|
+
'No polyrepo manifest found. Run `peaks polyrepo init [--children <list>]` first.'
|
|
65
|
+
]), options.json);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
printResult(io, ok('polyrepo.status', { ...result, initialized: true }, [], [
|
|
69
|
+
result.children.length === 0
|
|
70
|
+
? 'Manifest exists but has no children. Re-run `peaks polyrepo init --children <list>`.'
|
|
71
|
+
: `Run \`peaks polyrepo dispatch --rid <rid> --to <child-id>\` to mirror an artifact.`
|
|
72
|
+
]), options.json);
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
printResult(io, fail('polyrepo.status', 'POLYREPO_STATUS_FAILED', getErrorMessage(error), {}, []), options.json);
|
|
76
|
+
process.exitCode = 1;
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
// -----------------------------------------------------------------
|
|
80
|
+
// 3. peaks polyrepo dispatch --rid <rid> [--to <child-id>] --role <prd|rd|qa> --artifact <path>
|
|
81
|
+
// -----------------------------------------------------------------
|
|
82
|
+
addJsonOption(polyrepo
|
|
83
|
+
.command('dispatch')
|
|
84
|
+
.description('Mirror a PRD/RD/QA artifact from the parent into one or more children')
|
|
85
|
+
.requiredOption('--rid <rid>', 'request id (used in the on-disk filename)')
|
|
86
|
+
.option('--to <list>', 'comma-separated child ids (defaults to all children in the manifest)')
|
|
87
|
+
.requiredOption('--role <role>', 'artifact role: prd | rd | qa')
|
|
88
|
+
.requiredOption('--artifact <path>', 'source artifact path (relative to --root or absolute)')
|
|
89
|
+
.option('--sid <sid>', 'session id override (defaults to the canonical binding)')
|
|
90
|
+
.option('--root <path>', 'parent root (defaults to cwd)')).action((options) => {
|
|
91
|
+
try {
|
|
92
|
+
const root = resolveRoot(options.root);
|
|
93
|
+
const sid = options.sid ?? 'default';
|
|
94
|
+
const rid = options.rid ?? '';
|
|
95
|
+
const role = options.role;
|
|
96
|
+
if (role !== 'prd' && role !== 'rd' && role !== 'qa') {
|
|
97
|
+
printResult(io, fail('polyrepo.dispatch', 'INVALID_ROLE', `--role must be prd | rd | qa (got "${role}")`, {}, [
|
|
98
|
+
'Re-run with --role prd (or rd / qa).'
|
|
99
|
+
]), options.json);
|
|
100
|
+
process.exitCode = 1;
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
const artifact = options.artifact ?? '';
|
|
104
|
+
if (!existsSync(artifact)) {
|
|
105
|
+
printResult(io, fail('polyrepo.dispatch', 'ARTIFACT_NOT_FOUND', `source artifact does not exist: ${artifact}`, {}, [
|
|
106
|
+
'Verify the path or pass an absolute path.'
|
|
107
|
+
]), options.json);
|
|
108
|
+
process.exitCode = 1;
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
// Resolve targets from the manifest first so we can default to
|
|
112
|
+
// "all children" when --to is omitted.
|
|
113
|
+
const svc = new PolyrepoService();
|
|
114
|
+
const status = svc.status(root);
|
|
115
|
+
if (!status.manifestExists) {
|
|
116
|
+
printResult(io, fail('polyrepo.dispatch', 'NO_MANIFEST', `no polyrepo manifest at ${root}/.peaks/polyrepo.json`, {}, [
|
|
117
|
+
'Run `peaks polyrepo init` first.'
|
|
118
|
+
]), options.json);
|
|
119
|
+
process.exitCode = 1;
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
let targets;
|
|
123
|
+
if (options.to !== undefined && options.to.length > 0) {
|
|
124
|
+
targets = options.to.split(',').map((s) => s.trim()).filter((s) => s.length > 0);
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
targets = status.children.map((c) => c.id);
|
|
128
|
+
}
|
|
129
|
+
const result = svc.dispatch(root, {
|
|
130
|
+
sid, rid, targets, role, artifactPath: artifact
|
|
131
|
+
});
|
|
132
|
+
const allOk = result.perChild.every((c) => c.ok);
|
|
133
|
+
printResult(io, ok('polyrepo.dispatch', {
|
|
134
|
+
root,
|
|
135
|
+
sid,
|
|
136
|
+
rid,
|
|
137
|
+
role,
|
|
138
|
+
artifact,
|
|
139
|
+
dispatch: result.dispatch,
|
|
140
|
+
perChild: result.perChild,
|
|
141
|
+
allOk
|
|
142
|
+
}, result.warnings, [
|
|
143
|
+
allOk
|
|
144
|
+
? `Mirrored artifact to ${result.perChild.length} child(ren).`
|
|
145
|
+
: `One or more child mirrors failed; inspect perChild[].ok.`
|
|
146
|
+
]), options.json);
|
|
147
|
+
if (!allOk)
|
|
148
|
+
process.exitCode = 1;
|
|
149
|
+
}
|
|
150
|
+
catch (error) {
|
|
151
|
+
printResult(io, fail('polyrepo.dispatch', 'POLYREPO_DISPATCH_FAILED', getErrorMessage(error), {}, []), options.json);
|
|
152
|
+
process.exitCode = 1;
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `peaks runtime *` — vendor-runtime detection + compact CLI surface.
|
|
3
|
+
*
|
|
4
|
+
* Slice S2-a of RD-2 (2026-07-08 session). Four subcommands:
|
|
5
|
+
*
|
|
6
|
+
* detect — print which vendor is the active AI runtime
|
|
7
|
+
* (claude-code / codex / copilot / unknown).
|
|
8
|
+
* list — list the built-in vendor adapters registered by
|
|
9
|
+
* RuntimeService (Claude Code / Codex / Copilot).
|
|
10
|
+
* compact — invoke a vendor's compact verb via the adapter. The
|
|
11
|
+
* adapter (NOT this file) chooses the vendor verb;
|
|
12
|
+
* see src/services/runtime/vendors/<vendor>.ts.
|
|
13
|
+
*
|
|
14
|
+
* Vendor verb strings (`claude --compact`, `codex --compact`,
|
|
15
|
+
* `copilot compact`) MUST live ONLY in `src/services/runtime/vendors/`
|
|
16
|
+
* — verified by AC-1: `rg -n "claude --compact|codex --compact|copilot
|
|
17
|
+
* compact" src/services/code/` returns 0 matches.
|
|
18
|
+
*
|
|
19
|
+
* `peaks runtime compact --via <id>` first consults the user
|
|
20
|
+
* adapter registry (`.peaks/runtime/adapters.json`) for the id, then
|
|
21
|
+
* falls back to the built-in adapter list. Missing vendor CLI
|
|
22
|
+
* surfaces as exitCode=127 + warning, NOT as a fatal error — vendor
|
|
23
|
+
* neutrality demands peaks-loop stay alive.
|
|
24
|
+
*/
|
|
25
|
+
import type { Command } from 'commander';
|
|
26
|
+
import { join } from 'node:path';
|
|
27
|
+
import { type ProgramIO } from '../cli-helpers.js';
|
|
28
|
+
export interface RuntimeDetectOptions {
|
|
29
|
+
json?: boolean;
|
|
30
|
+
}
|
|
31
|
+
export interface RuntimeListOptions {
|
|
32
|
+
json?: boolean;
|
|
33
|
+
}
|
|
34
|
+
export interface RuntimeCompactOptions {
|
|
35
|
+
json?: boolean;
|
|
36
|
+
/** Vendor adapter id (built-in or registered). */
|
|
37
|
+
via?: string;
|
|
38
|
+
force?: boolean;
|
|
39
|
+
/** Override the project root (defaults to .peaks runtime lookup). */
|
|
40
|
+
project?: string;
|
|
41
|
+
}
|
|
42
|
+
export declare function registerRuntimeCommands(program: Command, io: ProgramIO): void;
|
|
43
|
+
/** Internal helper exposed for tests + future programmatic callers:
|
|
44
|
+
* resolve the registry file location, ensuring the parent dir
|
|
45
|
+
* exists. Idempotent. */
|
|
46
|
+
export declare function ensureRegistryDir(registryFile: string): void;
|
|
47
|
+
/** Internal helper for tests: locate the registry file under the
|
|
48
|
+
* given project root. Thin wrapper so tests can stub the resolution. */
|
|
49
|
+
export declare function registryFileFor(projectRoot: string): string;
|
|
50
|
+
/** Re-export the path join helper for completeness; not strictly
|
|
51
|
+
* needed by callers but keeps the module self-contained. */
|
|
52
|
+
export { join };
|