peaks-cli 1.3.1 → 1.3.2
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/bin/peaks.js +0 -0
- package/dist/src/cli/commands/core-artifact-commands.js +49 -11
- package/dist/src/cli/commands/slice-commands.js +4 -2
- package/dist/src/cli/commands/workspace-commands.js +67 -14
- package/dist/src/services/artifacts/artifact-prerequisites.d.ts +12 -0
- package/dist/src/services/artifacts/artifact-prerequisites.js +39 -8
- package/dist/src/services/artifacts/request-artifact-service.js +116 -76
- package/dist/src/services/doctor/doctor-service.d.ts +62 -0
- package/dist/src/services/doctor/doctor-service.js +276 -1
- package/dist/src/services/session/session-manager.d.ts +22 -1
- package/dist/src/services/session/session-manager.js +137 -28
- package/dist/src/services/slice/slice-check-service.js +20 -1
- package/dist/src/services/slice/slice-check-types.d.ts +9 -0
- package/dist/src/services/workspace/migrate-service.js +124 -2
- package/dist/src/services/workspace/migrate-types.d.ts +50 -7
- package/dist/src/services/workspace/reconcile-service.d.ts +33 -0
- package/dist/src/services/workspace/reconcile-service.js +160 -42
- package/dist/src/services/workspace/reconcile-types.d.ts +25 -0
- package/dist/src/services/workspace/workspace-service.js +29 -62
- package/dist/src/shared/version.d.ts +1 -1
- package/dist/src/shared/version.js +1 -1
- package/package.json +1 -1
- package/schemas/doctor-report.schema.json +2 -2
- package/skills/peaks-qa/SKILL.md +1 -0
- package/skills/peaks-rd/SKILL.md +2 -1
- package/skills/peaks-solo/SKILL.md +6 -0
- package/skills/peaks-txt/SKILL.md +2 -0
- package/skills/peaks-ui/SKILL.md +1 -0
|
@@ -1,36 +1,8 @@
|
|
|
1
1
|
import { mkdir } from 'node:fs/promises';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import { isDirectory } from '../../shared/fs.js';
|
|
4
|
-
import { getSessionId, setCurrentSessionBinding } from '../session/session-manager.js';
|
|
4
|
+
import { getSessionId, setCurrentSessionBinding, setSessionMeta } from '../session/session-manager.js';
|
|
5
5
|
import { setCurrentChangeId } from '../../shared/change-id.js';
|
|
6
|
-
/**
|
|
7
|
-
* Per-slice subdirectories created **inside the change-id dir**
|
|
8
|
-
* (`.peaks/<change-id>/...`). These are the reviewable
|
|
9
|
-
* artifacts and are tracked in git. The `system/` subdir is
|
|
10
|
-
* intentionally NOT in this list — it lives under the session
|
|
11
|
-
* dir (`.peaks/_runtime/<session-id>/system/`), since it holds
|
|
12
|
-
* live sub-agent progress and spawn records, which are ephemeral.
|
|
13
|
-
*/
|
|
14
|
-
const CHANGE_ARTIFACT_SUBDIRECTORIES = [
|
|
15
|
-
'prd/source',
|
|
16
|
-
'prd/requests',
|
|
17
|
-
'ui/requests',
|
|
18
|
-
'rd/requests',
|
|
19
|
-
'qa/test-cases',
|
|
20
|
-
'qa/test-reports',
|
|
21
|
-
'qa/requests',
|
|
22
|
-
'qa/screenshots',
|
|
23
|
-
'sc',
|
|
24
|
-
'txt'
|
|
25
|
-
];
|
|
26
|
-
/**
|
|
27
|
-
* Per-session subdirectories created **inside the session dir**
|
|
28
|
-
* (`.peaks/_runtime/<session-id>/...`). These are the ephemeral
|
|
29
|
-
* state and are gitignored.
|
|
30
|
-
*/
|
|
31
|
-
const SESSION_EPHEMERAL_SUBDIRECTORIES = [
|
|
32
|
-
'system'
|
|
33
|
-
];
|
|
34
6
|
const SESSION_ID_PATTERN = /^\d{4}-\d{2}-\d{2}-[a-z][a-z0-9-]*[a-z0-9]$/;
|
|
35
7
|
const PROHIBITED_SUFFIXES = ['session', 'work', 'task', 'test', 'temp', 'tmp'];
|
|
36
8
|
// Auto-generated session ID pattern: YYYY-MM-DD-session-<6位hex>
|
|
@@ -77,14 +49,21 @@ export function validateSessionId(sessionId) {
|
|
|
77
49
|
}
|
|
78
50
|
export async function initWorkspace(options) {
|
|
79
51
|
validateSessionId(options.sessionId);
|
|
80
|
-
// Phase 6 refactor (slice 2026-06-05-change-id-as-unit-of-work)
|
|
81
|
-
//
|
|
52
|
+
// Phase 6 refactor (slice 2026-06-05-change-id-as-unit-of-work) +
|
|
53
|
+
// slice 006 (2026-06-06-change-folder-simplify-and-lazy-role-subdirs):
|
|
54
|
+
// - Reviewable artifacts (rd/, qa/, prd/, txt/) live at
|
|
82
55
|
// `.peaks/<change-id>/<role>/` (tracked in git) when a change-id
|
|
83
|
-
// is given.
|
|
56
|
+
// is given. The role subdirs are NOT pre-created — the writer
|
|
57
|
+
// (e.g. `peaks request init`, `peaks rd`) creates the parent
|
|
58
|
+
// dirs on demand via `mkdirSync(..., { recursive: true })`.
|
|
84
59
|
// - The session dir `.peaks/_runtime/<session-id>/` (gitignored)
|
|
85
|
-
// holds
|
|
86
|
-
//
|
|
87
|
-
//
|
|
60
|
+
// now holds ONLY the canonical `session.json` metadata. The
|
|
61
|
+
// F3-introduced `system/` subdir is gone — slice 006 removes
|
|
62
|
+
// it via `peaks workspace reconcile`; new init calls do not
|
|
63
|
+
// pre-create it.
|
|
64
|
+
// - The change-id dir is created when `--change-id` is given,
|
|
65
|
+
// but its role subdirs (prd/, qa/, rd/, sc/, txt/) are NOT
|
|
66
|
+
// pre-created either — same lazy-mkdir rule.
|
|
88
67
|
//
|
|
89
68
|
// The CLI accepts `--change-id <id>` to bind the change. The legacy
|
|
90
69
|
// session-scoped layout (`.peaks/<session-id>/<role>/<file>`) is
|
|
@@ -95,8 +74,8 @@ export async function initWorkspace(options) {
|
|
|
95
74
|
const created = [];
|
|
96
75
|
const alreadyExisted = [];
|
|
97
76
|
// 1. Create the session dir (canonical location `.peaks/_runtime/<sid>/`)
|
|
98
|
-
// with
|
|
99
|
-
//
|
|
77
|
+
// with NO subdirs. The session dir is gitignored; the role
|
|
78
|
+
// subdirs and the `system/` subdir are gone entirely (slice 006).
|
|
100
79
|
if (await isDirectory(sessionRoot)) {
|
|
101
80
|
alreadyExisted.push('.');
|
|
102
81
|
}
|
|
@@ -104,22 +83,20 @@ export async function initWorkspace(options) {
|
|
|
104
83
|
await mkdir(sessionRoot, { recursive: true });
|
|
105
84
|
created.push('.');
|
|
106
85
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
await mkdir(full, { recursive: true });
|
|
114
|
-
created.push(sub);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
86
|
+
// 1a. Write the per-session metadata file. Slice 006 makes
|
|
87
|
+
// `.peaks/_runtime/<sid>/session.json` the durable session
|
|
88
|
+
// metadata (the body's source of truth, the `peaks workspace
|
|
89
|
+
// reconcile` discovery source). The file is created on first
|
|
90
|
+
// init and refreshed on every subsequent init. Idempotent.
|
|
91
|
+
setSessionMeta(options.projectRoot, options.sessionId, {});
|
|
117
92
|
// 2. If a change-id is given, also create the change-id dir at
|
|
118
|
-
// `.peaks/<change-id>/` (tracked) with
|
|
119
|
-
//
|
|
120
|
-
//
|
|
121
|
-
//
|
|
122
|
-
//
|
|
93
|
+
// `.peaks/<change-id>/` (tracked) with NO role subdirs. The
|
|
94
|
+
// role subdirs (prd/, qa/, rd/, sc/, txt/) are created on demand
|
|
95
|
+
// by the writer at the write site. When the caller did NOT
|
|
96
|
+
// specify a change-id, this step is skipped — reviewable writes
|
|
97
|
+
// for this session are then blocked until a change-id is bound
|
|
98
|
+
// (or the user re-runs init with `--change-id`). Surfaces in
|
|
99
|
+
// `changeIdAction: 'none'`.
|
|
123
100
|
let resolvedChangeId = null;
|
|
124
101
|
let changeIdAction = 'none';
|
|
125
102
|
if (options.changeId !== undefined && options.changeId.length > 0) {
|
|
@@ -132,16 +109,6 @@ export async function initWorkspace(options) {
|
|
|
132
109
|
await mkdir(changeDir, { recursive: true });
|
|
133
110
|
created.push(resolvedChangeId);
|
|
134
111
|
}
|
|
135
|
-
for (const sub of CHANGE_ARTIFACT_SUBDIRECTORIES) {
|
|
136
|
-
const full = join(changeDir, sub);
|
|
137
|
-
if (await isDirectory(full)) {
|
|
138
|
-
alreadyExisted.push(sub);
|
|
139
|
-
}
|
|
140
|
-
else {
|
|
141
|
-
await mkdir(full, { recursive: true });
|
|
142
|
-
created.push(sub);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
112
|
// 3. Bind the change-id so RD/QA/PRD services know where to write
|
|
146
113
|
// reviewable artifacts. The binding is a symlink at
|
|
147
114
|
// `.peaks/_runtime/current-change` pointing at the change-id dir.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const CLI_VERSION = "1.3.
|
|
1
|
+
export declare const CLI_VERSION = "1.3.2";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const CLI_VERSION = "1.3.
|
|
1
|
+
export const CLI_VERSION = "1.3.2";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "peaks-cli",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.2",
|
|
4
4
|
"description": "Cross-AI-IDE workflow-gating CLI + skill family (Claude Code shipped, Trae in progress; Codex / Cursor / Qoder / Tongyi Lingma on the roadmap).",
|
|
5
5
|
"author": "SquabbyZ",
|
|
6
6
|
"license": "MIT",
|
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
"properties": {
|
|
14
14
|
"id": {
|
|
15
15
|
"type": "string",
|
|
16
|
-
"pattern": "^(skill|skill-name|skill-parse|skill-runbook|skill-apply-note|skill-presence|statusline|schema|config|doctor-self|capability):[A-Za-z0-9][A-Za-z0-9._-]*$",
|
|
17
|
-
"description": "Stable check id. Known prefixes: skill:<name> (required skill present), skill-name:<dir> (directory matches declared name), skill-parse:<dir> (skill metadata parsed), skill-runbook:<name> (Default runbook section exists), skill-apply-note:<name> (destructive --apply lines carry an authorization/--dry-run note), skill-presence:<topic> (status of .peaks/.active-skill.json — current/freshness/workspace), statusline:<topic> (out-of-band Claude Code statusLine — install/runtime), schema:<file> (schema file exists and is valid JSON), config:<scope> (optional config locations), doctor-self:<topic> (doctor validates its own output against this schema), capability:<name> (third-party capability is resolvable at the pinned version)."
|
|
16
|
+
"pattern": "^(skill|skill-name|skill-parse|skill-runbook|skill-apply-note|skill-presence|statusline|schema|config|doctor-self|capability|build):[A-Za-z0-9][A-Za-z0-9._-]*$",
|
|
17
|
+
"description": "Stable check id. Known prefixes: skill:<name> (required skill present), skill-name:<dir> (directory matches declared name), skill-parse:<dir> (skill metadata parsed), skill-runbook:<name> (Default runbook section exists), skill-apply-note:<name> (destructive --apply lines carry an authorization/--dry-run note), skill-presence:<topic> (status of .peaks/.active-skill.json — current/freshness/workspace), statusline:<topic> (out-of-band Claude Code statusLine — install/runtime), schema:<file> (schema file exists and is valid JSON), config:<scope> (optional config locations), doctor-self:<topic> (doctor validates its own output against this schema), capability:<name> (third-party capability is resolvable at the pinned version), build:<topic> (build-hygiene checks — dist/source version consistency)."
|
|
18
18
|
},
|
|
19
19
|
"ok": { "type": "boolean" },
|
|
20
20
|
"message": { "type": "string", "minLength": 1 }
|
package/skills/peaks-qa/SKILL.md
CHANGED
|
@@ -62,6 +62,7 @@ This is the hard-block replacement for the previous "wait for the user" prose. W
|
|
|
62
62
|
|
|
63
63
|
When this skill is launched as a sub-agent via `Task(subagent_type="general-purpose", ...)` from `peaks-solo`, the following sections of THIS skill are **suspended** for the sub-agent run:
|
|
64
64
|
|
|
65
|
+
- **Session id** — use the parent's sid (read `.peaks/_runtime/session.json` or pass `--session-id <parent-sid>` to any session-creating CLI). Do NOT spawn your own session. The new `peaks session info --active` reads the canonical binding for you.
|
|
65
66
|
- **Skill presence (MANDATORY first action)** — do NOT call `peaks skill presence:set peaks-qa`. The sub-agent must not overwrite `.peaks/.active-skill.json`; the main Solo loop owns that file. If you need to mark your own state, write a marker file at `.peaks/<session-id>/system/sub-agent-qa.json` and only that.
|
|
66
67
|
- **Workspace initialization** — Solo has already run `peaks workspace init` before fan-out. Do not re-run it.
|
|
67
68
|
- **Mode selection** — Solo has already chosen the mode.
|
package/skills/peaks-rd/SKILL.md
CHANGED
|
@@ -39,6 +39,7 @@ The full hard-block contract is defined in `peaks-qa` (see "Hard contracts for b
|
|
|
39
39
|
|
|
40
40
|
When this skill is launched as a sub-agent via `Task(subagent_type="general-purpose", ...)` from `peaks-solo`, the following sections of THIS skill are **suspended** for the sub-agent run:
|
|
41
41
|
|
|
42
|
+
- **Session id** — use the parent's sid (read `.peaks/_runtime/session.json` or pass `--session-id <parent-sid>` to any session-creating CLI). Do NOT spawn your own session. The new `peaks session info --active` reads the canonical binding for you.
|
|
42
43
|
- **Skill presence (MANDATORY first action)** — do NOT call `peaks skill presence:set peaks-rd`. The sub-agent must not overwrite `.peaks/.active-skill.json`; the main Solo loop owns that file. If you need to mark your own state, write a marker file at `.peaks/<session-id>/system/sub-agent-rd.json` and only that.
|
|
43
44
|
- **Workspace initialization** — Solo has already run `peaks workspace init` before fan-out. Do not re-run it.
|
|
44
45
|
- **Mode selection** — Solo has already chosen the mode. Read it from the prompt arguments (or from `.peaks/.active-skill.json` if you can, but do not write it).
|
|
@@ -46,7 +47,7 @@ When this skill is launched as a sub-agent via `Task(subagent_type="general-purp
|
|
|
46
47
|
|
|
47
48
|
What the sub-agent **MUST** still do, from this skill's contract:
|
|
48
49
|
|
|
49
|
-
0. **Do NOT call `peaks request init`** — Solo has already initialised the request artefact slot in the main loop before fan-out (the runbook has the exact `peaks request init --role rd --id <rid> --project <repo> --apply --type <type> --json` call). The sub-agent reads the slot via `peaks request show <rid> --role rd --project <repo> --json` if it needs to.
|
|
50
|
+
0. **Do NOT call `peaks request init`** — Solo has already initialised the request artefact slot in the main loop before fan-out (the runbook has the exact `peaks request init --role rd --id <rid> --project <repo> --apply --type <type> --json` call). The sub-agent reads the slot via `peaks request show <rid> --role rd --project <repo> --json` if it needs to. Note: `peaks request init` is **dry-run by default**. Pass `--apply` to actually create the artifact.
|
|
50
51
|
2. `peaks request show <rid> --role prd --project <repo> --json` (and `--role ui` if UI is in the swarm plan).
|
|
51
52
|
3. Standards preflight (dry-run only; Solo owns the apply step).
|
|
52
53
|
4. Project-scan read; create `rd/project-scan.md` only if Solo flagged it missing in the dispatch prompt.
|
|
@@ -262,6 +262,12 @@ peaks session title $(cat .peaks/.session.json | python3 -c "import sys,json; pr
|
|
|
262
262
|
|
|
263
263
|
If the session directory already has a title (check via `peaks session list --json`), skip this step — the title is already set.
|
|
264
264
|
|
|
265
|
+
## Sub-agent session sharing (MANDATORY — one conversation = one sid)
|
|
266
|
+
|
|
267
|
+
When peaks-solo dispatches a sub-agent (peaks-rd, peaks-qa, peaks-ui, peaks-txt, peaks-sc), the sub-agent prompt MUST include the parent's session id. The sub-agent then passes `--session-id <parent-sid>` for any session-creating CLI call (e.g. `peaks request init --session-id <parent-sid>`). The sub-agent MUST NOT call `peaks workspace init` — that would create a new session dir and orphan the parent's binding. The sub-agent reads `.peaks/_runtime/session.json` to discover the parent's sid (or the orchestrator passes it explicitly). Sub-agents also accept the parent's sid via the new `peaks session info --active` primitive when they need a one-shot read.
|
|
268
|
+
|
|
269
|
+
Note: `peaks request init` is **dry-run by default** — the JSON response has `applied: false` and no file is written unless `--apply` is passed. This is the same safe-by-default pattern as `peaks workspace migrate --apply`. Sub-agents that need to actually create a slice must add `--apply`.
|
|
270
|
+
|
|
265
271
|
## Boundaries
|
|
266
272
|
|
|
267
273
|
Peaks-Cli Solo may:
|
|
@@ -15,6 +15,8 @@ Before any analysis or tool call, immediately run:
|
|
|
15
15
|
peaks skill presence:set peaks-txt --project <repo> --mode <mode> --gate startup
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
+
**When invoked as a sub-agent (peaks-solo swarm):** do NOT call `peaks skill presence:set` (Solo owns the active-skill marker) and do NOT spawn your own session. Use the parent's sid — read `.peaks/_runtime/session.json` or pass `--session-id <parent-sid>` to any session-creating CLI. The new `peaks session info --active` reads the canonical binding for you.
|
|
19
|
+
|
|
18
20
|
On the first presence:set in a project, ensure the out-of-band status bar is installed so the user can see at a glance that Peaks is orchestrating — it renders the active skill in Claude Code's terminal status line, independent of model output:
|
|
19
21
|
|
|
20
22
|
```bash
|
package/skills/peaks-ui/SKILL.md
CHANGED
|
@@ -30,6 +30,7 @@ UI's headed-browser inspection hits the same auth walls. The flow is identical t
|
|
|
30
30
|
|
|
31
31
|
When this skill is launched as a sub-agent via `Task(subagent_type="general-purpose", ...)` from `peaks-solo`, the following sections of THIS skill are **suspended** for the sub-agent run:
|
|
32
32
|
|
|
33
|
+
- **Session id** — use the parent's sid (read `.peaks/_runtime/session.json` or pass `--session-id <parent-sid>` to any session-creating CLI). Do NOT spawn your own session. The new `peaks session info --active` reads the canonical binding for you.
|
|
33
34
|
- **Skill presence (MANDATORY first action)** — do NOT call `peaks skill presence:set peaks-ui`. The sub-agent must not overwrite `.peaks/.active-skill.json`; the main Solo loop owns that file. If you need to mark your own state, write a marker file at `.peaks/<session-id>/system/sub-agent-ui.json` and only that.
|
|
34
35
|
- **Workspace initialization** — Solo has already run `peaks workspace init` before fan-out. Do not re-run it.
|
|
35
36
|
- **Mode selection** — Solo has already chosen the mode.
|