peaks-cli 1.3.3 → 1.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/cli/commands/core-artifact-commands.js +6 -3
- package/dist/src/cli/commands/project-commands.js +8 -4
- package/dist/src/cli/commands/workflow-commands.js +2 -1
- package/dist/src/services/dashboard/project-dashboard-service.d.ts +23 -0
- package/dist/src/services/dashboard/project-dashboard-service.js +21 -0
- package/dist/src/services/ide/adapters/claude-code-adapter.js +27 -0
- package/dist/src/services/ide/adapters/trae-adapter.d.ts +19 -11
- package/dist/src/services/ide/adapters/trae-adapter.js +43 -15
- package/dist/src/services/ide/hook-protocol.d.ts +7 -4
- package/dist/src/services/ide/hook-protocol.js +7 -4
- package/dist/src/services/ide/ide-types.d.ts +60 -0
- package/dist/src/services/ide/resource-profile.d.ts +52 -0
- package/dist/src/services/ide/resource-profile.js +33 -0
- package/dist/src/services/memory/project-context-service.js +2 -1
- package/dist/src/services/memory/project-memory-service.js +4 -3
- package/dist/src/services/perf/perf-baseline-service.js +2 -1
- package/dist/src/services/session/getSessionDir.d.ts +1 -0
- package/dist/src/services/session/getSessionDir.js +27 -0
- package/dist/src/services/session/index.d.ts +1 -0
- package/dist/src/services/session/index.js +1 -0
- package/dist/src/services/standards/ide-aware-standards-service.d.ts +94 -0
- package/dist/src/services/standards/ide-aware-standards-service.js +89 -0
- package/dist/src/services/standards/project-standards-service.d.ts +1 -2
- 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 +112 -2
- package/skills/peaks-ide/SKILL.md +1 -1
- package/skills/peaks-ide/references/audit-log-helper.md +52 -0
- package/skills/peaks-qa/SKILL.md +104 -62
- package/skills/peaks-rd/SKILL.md +88 -58
- package/skills/peaks-solo/SKILL.md +52 -22
- package/skills/peaks-solo/references/browser-workflow.md +22 -20
- package/skills/peaks-solo/references/sub-agent-dispatch.md +44 -1
- package/skills/peaks-ui/SKILL.md +18 -9
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical session-directory resolver.
|
|
3
|
+
*
|
|
4
|
+
* As of slice 2026-06-05-peaks-runtime-layer the per-session workspace
|
|
5
|
+
* lives at `<root>/.peaks/_runtime/<sessionId>/` (NOT at the legacy
|
|
6
|
+
* `<root>/.peaks/<sessionId>/` location). All **write** paths MUST route
|
|
7
|
+
* through this helper. The legacy top-level path is preserved as a
|
|
8
|
+
* back-compat **read** fallback only (see
|
|
9
|
+
* `src/services/artifacts/request-artifact-service.ts:662` etc.).
|
|
10
|
+
*
|
|
11
|
+
* The corresponding test in
|
|
12
|
+
* `tests/unit/services/session/session-dir-canonical.test.ts` enforces
|
|
13
|
+
* two invariants:
|
|
14
|
+
*
|
|
15
|
+
* (a) `getSessionDir(root, sid)` returns `<root>/.peaks/_runtime/<sid>`.
|
|
16
|
+
* (b) A static scan of `src/` flags any direct join of `.peaks` +
|
|
17
|
+
* `sessionId` that does NOT route through this resolver. The
|
|
18
|
+
* back-compat **read** sites are excluded by explicit allow-list.
|
|
19
|
+
*
|
|
20
|
+
* @param projectRoot - Absolute path to the project root.
|
|
21
|
+
* @param sessionId - The session identifier (e.g. `2026-06-06-session-5b1095`).
|
|
22
|
+
* @returns Absolute path to the canonical session directory.
|
|
23
|
+
*/
|
|
24
|
+
import { join } from 'node:path';
|
|
25
|
+
export function getSessionDir(projectRoot, sessionId) {
|
|
26
|
+
return join(projectRoot, '.peaks', '_runtime', sessionId);
|
|
27
|
+
}
|
|
@@ -1 +1,2 @@
|
|
|
1
1
|
export { ensureSession, getSessionId, getCurrentSessionDir, listSessions, getSessionMeta, setSessionMeta, setSessionTitle, listSessionMetas, getProjectScanPath, hasProjectScan, setCurrentSessionBinding, rotateSessionBinding, type SessionInfo, type SessionMeta } from './session-manager.js';
|
|
2
|
+
export { getSessionDir } from './getSessionDir.js';
|
|
@@ -1 +1,2 @@
|
|
|
1
1
|
export { ensureSession, getSessionId, getCurrentSessionDir, listSessions, getSessionMeta, setSessionMeta, setSessionTitle, listSessionMetas, getProjectScanPath, hasProjectScan, setCurrentSessionBinding, rotateSessionBinding } from './session-manager.js';
|
|
2
|
+
export { getSessionDir } from './getSessionDir.js';
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IDE-aware wrapper for `peaks standards init` / `peaks standards update`.
|
|
3
|
+
*
|
|
4
|
+
* Slice #011-2026-06-07-ide-adapter-resource-profile: the original
|
|
5
|
+
* `executeProjectStandardsInit` / `executeProjectStandardsUpdate` always
|
|
6
|
+
* wrote to `CLAUDE.md` + `.claude/rules/**` regardless of which IDE
|
|
7
|
+
* the user was running. This wrapper dispatches on the IDE detected
|
|
8
|
+
* (or explicitly requested via `--ide`) and falls back to the legacy
|
|
9
|
+
* Claude Code path with a stderr warning when the detected IDE has
|
|
10
|
+
* no `standardsProfile` declared (Trae in slice 1.3.2).
|
|
11
|
+
*
|
|
12
|
+
* Two entry points:
|
|
13
|
+
*
|
|
14
|
+
* - `executeProjectStandardsInitIdeAware` — same signature as the
|
|
15
|
+
* underlying `executeProjectStandardsInit`, plus an optional
|
|
16
|
+
* `ideId` override that bypasses detection.
|
|
17
|
+
* - `executeProjectStandardsUpdateIdeAware` — same shape, for the
|
|
18
|
+
* `update` flow.
|
|
19
|
+
*
|
|
20
|
+
* Detection precedence:
|
|
21
|
+
* 1. Explicit `options.ideId` (CLI `--ide` flag)
|
|
22
|
+
* 2. `IdeRegistry.detect()` from cwd (or the `projectRoot` if given)
|
|
23
|
+
* 3. `null` (no IDE detected) → fall back to the legacy Claude Code path
|
|
24
|
+
*
|
|
25
|
+
* Fallback behavior: when the resolved IDE has no `standardsProfile`
|
|
26
|
+
* declared, the wrapper STILL calls the legacy Claude Code writer
|
|
27
|
+
* (so the user gets the files they would have gotten before slice #011)
|
|
28
|
+
* and emits a stderr warning with the IDE id and the fact that the
|
|
29
|
+
* adapter is UNVERIFIED for the standards profile. This keeps the
|
|
30
|
+
* "Trae is UNVERIFIED, ship a working file tree, surface the gap"
|
|
31
|
+
* contract intact.
|
|
32
|
+
*/
|
|
33
|
+
import type { IdeId } from '../ide/ide-types.js';
|
|
34
|
+
import { detectAllResourceTargets, getStandardsProfile } from '../ide/resource-profile.js';
|
|
35
|
+
import { type ProjectStandardsInitOptions, type ProjectStandardsInitResult, type ProjectStandardsUpdateResult } from './project-standards-service.js';
|
|
36
|
+
export type { ProjectStandardsInitResult, ProjectStandardsUpdateResult };
|
|
37
|
+
export type ProjectStandardsIdeAwareOptions = ProjectStandardsInitOptions & {
|
|
38
|
+
/**
|
|
39
|
+
* Explicit IDE override. When set, bypasses `IdeRegistry.detect()`
|
|
40
|
+
* (cwd + env heuristics) and uses the provided IDE id directly.
|
|
41
|
+
* Mirrors the `peaks hooks install --ide <id>` pattern.
|
|
42
|
+
*/
|
|
43
|
+
readonly ideId?: IdeId;
|
|
44
|
+
};
|
|
45
|
+
export type ProjectStandardsUpdateIdeAwareOptions = ProjectStandardsInitOptions & {
|
|
46
|
+
/** Explicit IDE override. See {@link ProjectStandardsIdeAwareOptions}. */
|
|
47
|
+
readonly ideId?: IdeId;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Resolve the active IDE id for a standards call. Order of precedence:
|
|
51
|
+
* 1. explicit `options.ideId`
|
|
52
|
+
* 2. `IdeRegistry.detect()` from `options.projectRoot`
|
|
53
|
+
* 3. `null` (no detected IDE — caller falls back to legacy)
|
|
54
|
+
*/
|
|
55
|
+
export declare function resolveStandardsIdeId(options: {
|
|
56
|
+
readonly projectRoot: string;
|
|
57
|
+
readonly ideId?: IdeId;
|
|
58
|
+
}): IdeId | null;
|
|
59
|
+
/**
|
|
60
|
+
* Run `peaks standards init` with IDE-aware dispatch.
|
|
61
|
+
*
|
|
62
|
+
* When the resolved IDE has a `standardsProfile`, the call still
|
|
63
|
+
* delegates to the existing `executeProjectStandardsInit` (the
|
|
64
|
+
* profile maps to the Claude Code path; future per-IDE writers
|
|
65
|
+
* plug in here). When the IDE is unregistered for the standards
|
|
66
|
+
* profile, the call delegates to the legacy path + emits a stderr
|
|
67
|
+
* warning.
|
|
68
|
+
*/
|
|
69
|
+
export declare function executeProjectStandardsInitIdeAware(options: ProjectStandardsIdeAwareOptions): ProjectStandardsInitResult;
|
|
70
|
+
/**
|
|
71
|
+
* Run `peaks standards update` with IDE-aware dispatch.
|
|
72
|
+
*
|
|
73
|
+
* Same dispatch rules as `executeProjectStandardsInitIdeAware`.
|
|
74
|
+
*/
|
|
75
|
+
export declare function executeProjectStandardsUpdateIdeAware(options: ProjectStandardsUpdateIdeAwareOptions): ProjectStandardsUpdateResult;
|
|
76
|
+
/**
|
|
77
|
+
* Test seam + integration-test helper: returns the resolved IDE id
|
|
78
|
+
* for the call, plus the active standards profile. Exported for
|
|
79
|
+
* the integration test in `tests/unit/standards/ide-aware-standards-service.test.ts`
|
|
80
|
+
* to assert the dispatch decision without running the full write.
|
|
81
|
+
*/
|
|
82
|
+
export declare function inspectStandardsDispatch(options: {
|
|
83
|
+
readonly projectRoot: string;
|
|
84
|
+
readonly ideId?: IdeId;
|
|
85
|
+
}): {
|
|
86
|
+
readonly ideId: IdeId | null;
|
|
87
|
+
readonly profile: ReturnType<typeof getStandardsProfile>;
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* Test seam: the resource-profile accessor exposes
|
|
91
|
+
* `detectAllResourceTargets` for callers that need to enumerate
|
|
92
|
+
* across all registered IDEs. Re-exported here for convenience.
|
|
93
|
+
*/
|
|
94
|
+
export { detectAllResourceTargets };
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { detectAllResourceTargets, getStandardsProfile, } from '../ide/resource-profile.js';
|
|
2
|
+
import { executeProjectStandardsInit, executeProjectStandardsUpdate, } from './project-standards-service.js';
|
|
3
|
+
import { detectInstalledIde } from '../ide/ide-detector.js';
|
|
4
|
+
function warnUnregisteredIde(ideId, projectRoot) {
|
|
5
|
+
process.stderr.write(`peaks standards: IDE '${ideId}' has no standardsProfile declared; ` +
|
|
6
|
+
`falling back to the legacy Claude Code path (CLAUDE.md + .claude/rules/**) ` +
|
|
7
|
+
`for project '${projectRoot}'. This is a slice #011 follow-up gap; ` +
|
|
8
|
+
`see .peaks/memory/ide-adapter-resource-profile-framework.md.\n`);
|
|
9
|
+
}
|
|
10
|
+
function warnNoIdeDetected(projectRoot) {
|
|
11
|
+
process.stderr.write(`peaks standards: no IDE detected in '${projectRoot}'; ` +
|
|
12
|
+
`writing to the legacy Claude Code path (CLAUDE.md + .claude/rules/**). ` +
|
|
13
|
+
`Pass --ide <id> to bypass detection.\n`);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Resolve the active IDE id for a standards call. Order of precedence:
|
|
17
|
+
* 1. explicit `options.ideId`
|
|
18
|
+
* 2. `IdeRegistry.detect()` from `options.projectRoot`
|
|
19
|
+
* 3. `null` (no detected IDE — caller falls back to legacy)
|
|
20
|
+
*/
|
|
21
|
+
export function resolveStandardsIdeId(options) {
|
|
22
|
+
if (options.ideId !== undefined) {
|
|
23
|
+
return options.ideId;
|
|
24
|
+
}
|
|
25
|
+
return detectInstalledIde(options.projectRoot);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Run `peaks standards init` with IDE-aware dispatch.
|
|
29
|
+
*
|
|
30
|
+
* When the resolved IDE has a `standardsProfile`, the call still
|
|
31
|
+
* delegates to the existing `executeProjectStandardsInit` (the
|
|
32
|
+
* profile maps to the Claude Code path; future per-IDE writers
|
|
33
|
+
* plug in here). When the IDE is unregistered for the standards
|
|
34
|
+
* profile, the call delegates to the legacy path + emits a stderr
|
|
35
|
+
* warning.
|
|
36
|
+
*/
|
|
37
|
+
export function executeProjectStandardsInitIdeAware(options) {
|
|
38
|
+
const ideId = resolveStandardsIdeId(options);
|
|
39
|
+
if (ideId === null) {
|
|
40
|
+
warnNoIdeDetected(options.projectRoot);
|
|
41
|
+
return executeProjectStandardsInit(options);
|
|
42
|
+
}
|
|
43
|
+
const profile = getStandardsProfile(ideId);
|
|
44
|
+
if (profile === null) {
|
|
45
|
+
warnUnregisteredIde(ideId, options.projectRoot);
|
|
46
|
+
return executeProjectStandardsInit(options);
|
|
47
|
+
}
|
|
48
|
+
// Claude Code path: profile matches the legacy writer. Future per-IDE
|
|
49
|
+
// writers (markdown+frontmatter, multiple rule roots, etc.) plug in
|
|
50
|
+
// here by branching on `profile.format` / `profile.rulesDir`.
|
|
51
|
+
return executeProjectStandardsInit(options);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Run `peaks standards update` with IDE-aware dispatch.
|
|
55
|
+
*
|
|
56
|
+
* Same dispatch rules as `executeProjectStandardsInitIdeAware`.
|
|
57
|
+
*/
|
|
58
|
+
export function executeProjectStandardsUpdateIdeAware(options) {
|
|
59
|
+
const ideId = resolveStandardsIdeId(options);
|
|
60
|
+
if (ideId === null) {
|
|
61
|
+
warnNoIdeDetected(options.projectRoot);
|
|
62
|
+
return executeProjectStandardsUpdate(options);
|
|
63
|
+
}
|
|
64
|
+
const profile = getStandardsProfile(ideId);
|
|
65
|
+
if (profile === null) {
|
|
66
|
+
warnUnregisteredIde(ideId, options.projectRoot);
|
|
67
|
+
return executeProjectStandardsUpdate(options);
|
|
68
|
+
}
|
|
69
|
+
return executeProjectStandardsUpdate(options);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Test seam + integration-test helper: returns the resolved IDE id
|
|
73
|
+
* for the call, plus the active standards profile. Exported for
|
|
74
|
+
* the integration test in `tests/unit/standards/ide-aware-standards-service.test.ts`
|
|
75
|
+
* to assert the dispatch decision without running the full write.
|
|
76
|
+
*/
|
|
77
|
+
export function inspectStandardsDispatch(options) {
|
|
78
|
+
const ideId = resolveStandardsIdeId(options);
|
|
79
|
+
if (ideId === null) {
|
|
80
|
+
return { ideId: null, profile: null };
|
|
81
|
+
}
|
|
82
|
+
return { ideId, profile: getStandardsProfile(ideId) };
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Test seam: the resource-profile accessor exposes
|
|
86
|
+
* `detectAllResourceTargets` for callers that need to enumerate
|
|
87
|
+
* across all registered IDEs. Re-exported here for convenience.
|
|
88
|
+
*/
|
|
89
|
+
export { detectAllResourceTargets };
|
|
@@ -68,7 +68,7 @@ export type ProjectStandardsUpdateSummary = {
|
|
|
68
68
|
readonly reviewSuggestions: string[];
|
|
69
69
|
};
|
|
70
70
|
};
|
|
71
|
-
type ProjectStandardsInitOptions = {
|
|
71
|
+
export type ProjectStandardsInitOptions = {
|
|
72
72
|
readonly projectRoot: string;
|
|
73
73
|
readonly language?: string;
|
|
74
74
|
readonly apply?: boolean;
|
|
@@ -79,4 +79,3 @@ export declare function executeProjectStandardsInit(options: ProjectStandardsIni
|
|
|
79
79
|
export declare function executeProjectStandardsUpdate(options: ProjectStandardsInitOptions): ProjectStandardsUpdateResult;
|
|
80
80
|
export declare function summarizeProjectStandardsInitResult(result: ProjectStandardsInitResult): ProjectStandardsInitSummary;
|
|
81
81
|
export declare function summarizeProjectStandardsUpdateResult(result: ProjectStandardsUpdateResult): ProjectStandardsUpdateSummary;
|
|
82
|
-
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const CLI_VERSION = "1.3.
|
|
1
|
+
export declare const CLI_VERSION = "1.3.4";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const CLI_VERSION = "1.3.
|
|
1
|
+
export const CLI_VERSION = "1.3.4";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "peaks-cli",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.4",
|
|
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",
|
|
@@ -363,6 +363,78 @@ function resolveProjectRoot(options) {
|
|
|
363
363
|
return projectRoot ? resolve(projectRoot) : null;
|
|
364
364
|
}
|
|
365
365
|
|
|
366
|
+
/**
|
|
367
|
+
* Slice #011: Detect the installed IDE for the postinstall dispatch layer.
|
|
368
|
+
*
|
|
369
|
+
* Mirrors `src/services/ide/ide-detector.ts:detectInstalledIde` (cwd heuristic)
|
|
370
|
+
* but inlined here because `install-skills.mjs` is a plain `.mjs` script and
|
|
371
|
+
* cannot import the TS service at runtime. The dispatch:
|
|
372
|
+
* - Look for `.claude`, `.trae`, `.codex`, `.cursor`, `.qoder`,
|
|
373
|
+
* `.tongyi-lingma` in the project root in that insertion order
|
|
374
|
+
* (matches the registry's adapter order in `src/services/ide/ide-registry.ts`).
|
|
375
|
+
* - Returns the first match, or `null` if no adapter's directory is present.
|
|
376
|
+
*
|
|
377
|
+
* If the resolved IDE has no `skillInstall` declared (Trae in slice 1.3.2),
|
|
378
|
+
* the caller falls back to the legacy `~/.claude/{skills,output-styles}` path
|
|
379
|
+
* + emits a stderr warning. The dispatch is conservative: the env-var
|
|
380
|
+
* overrides `PEAKS_CLAUDE_SKILLS_DIR` / `PEAKS_CLAUDE_OUTPUT_STYLES_DIR`
|
|
381
|
+
* continue to work, and the legacy default is preserved.
|
|
382
|
+
*/
|
|
383
|
+
const IDE_DETECTION_DIRS = [
|
|
384
|
+
{ id: 'claude-code', dir: '.claude' },
|
|
385
|
+
{ id: 'trae', dir: '.trae' },
|
|
386
|
+
{ id: 'codex', dir: '.codex' },
|
|
387
|
+
{ id: 'cursor', dir: '.cursor' },
|
|
388
|
+
{ id: 'qoder', dir: '.qoder' },
|
|
389
|
+
{ id: 'tongyi-lingma', dir: '.tongyi-lingma' },
|
|
390
|
+
];
|
|
391
|
+
|
|
392
|
+
const IDE_SKILL_INSTALL_PROFILES = {
|
|
393
|
+
'claude-code': {
|
|
394
|
+
skillsDir: join(homedir(), '.claude', 'skills'),
|
|
395
|
+
outputStylesDir: join(homedir(), '.claude', 'output-styles'),
|
|
396
|
+
envVar: 'PEAKS_CLAUDE_SKILLS_DIR',
|
|
397
|
+
outputStylesEnvVar: 'PEAKS_CLAUDE_OUTPUT_STYLES_DIR',
|
|
398
|
+
},
|
|
399
|
+
'trae': null,
|
|
400
|
+
'codex': null,
|
|
401
|
+
'cursor': null,
|
|
402
|
+
'qoder': null,
|
|
403
|
+
'tongyi-lingma': null,
|
|
404
|
+
};
|
|
405
|
+
|
|
406
|
+
function detectInstalledIdeId(projectRoot) {
|
|
407
|
+
if (!projectRoot) return null;
|
|
408
|
+
for (const { id, dir } of IDE_DETECTION_DIRS) {
|
|
409
|
+
if (existsSync(join(projectRoot, dir))) {
|
|
410
|
+
return id;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
return null;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
function resolveIdeSkillInstallProfile(ideId) {
|
|
417
|
+
if (ideId === null) return null;
|
|
418
|
+
return IDE_SKILL_INSTALL_PROFILES[ideId] ?? null;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
function warnUnverifiedIde(ideId, projectRoot) {
|
|
422
|
+
process.stderr.write(
|
|
423
|
+
`peaks install-skills: IDE '${ideId}' has no skillInstall profile declared; ` +
|
|
424
|
+
`falling back to the legacy Claude Code path (~/.claude/skills + ~/.claude/output-styles) ` +
|
|
425
|
+
`for project '${projectRoot}'. This is a slice #011 follow-up gap; ` +
|
|
426
|
+
`see .peaks/memory/ide-adapter-resource-profile-framework.md.\n`
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
function warnNoIdeDetected(projectRoot) {
|
|
431
|
+
process.stderr.write(
|
|
432
|
+
`peaks install-skills: no IDE detected in '${projectRoot ?? '(project root unknown)'}'; ` +
|
|
433
|
+
`installing to the legacy Claude Code path (~/.claude/skills + ~/.claude/output-styles). ` +
|
|
434
|
+
`Set PEAKS_CLAUDE_SKILLS_DIR / PEAKS_CLAUDE_OUTPUT_STYLES_DIR to override.\n`
|
|
435
|
+
);
|
|
436
|
+
}
|
|
437
|
+
|
|
366
438
|
function writeMergedConfig(configPath, label, defaults, writeConfig) {
|
|
367
439
|
const existing = readConfigFile(configPath, label);
|
|
368
440
|
const next = { ...(existing === null ? defaults : mergeMissingConfigValues(existing, defaults)), version: defaults.version };
|
|
@@ -424,12 +496,37 @@ export function installProjectConfig(options = {}) {
|
|
|
424
496
|
export function installBundledSkills(options = {}) {
|
|
425
497
|
const packageRoot = resolvePackageRoot(options);
|
|
426
498
|
const skillsRoot = join(packageRoot, 'skills');
|
|
427
|
-
const targetRoot = resolve(options.targetRoot ?? process.env.PEAKS_CLAUDE_SKILLS_DIR ?? join(homedir(), '.claude', 'skills'));
|
|
428
499
|
|
|
429
500
|
if (process.env.PEAKS_SKIP_SKILL_INSTALL === '1' || !existsSync(skillsRoot)) {
|
|
430
501
|
return createInstallResult();
|
|
431
502
|
}
|
|
432
503
|
|
|
504
|
+
// Slice #011: IDE-aware dispatch. Precedence (highest first):
|
|
505
|
+
// 1. explicit options.targetRoot (test / hook override)
|
|
506
|
+
// 2. options.ideId skillInstall.skillsDir (per-IDE dispatch)
|
|
507
|
+
// 3. PEAKS_CLAUDE_SKILLS_DIR env var (legacy back-compat)
|
|
508
|
+
// 4. detected IDE's skillInstall.skillsDir (auto-detect from projectRoot)
|
|
509
|
+
// 5. legacy default (~/.claude/skills) (no-IDE fallback)
|
|
510
|
+
const projectRoot = resolveProjectRoot(options);
|
|
511
|
+
const detectedIdeId = detectInstalledIdeId(projectRoot);
|
|
512
|
+
const detectedProfile = resolveIdeSkillInstallProfile(detectedIdeId);
|
|
513
|
+
|
|
514
|
+
if (options.targetRoot === undefined && options.ideId === undefined && detectedProfile === null && detectedIdeId !== null) {
|
|
515
|
+
warnUnverifiedIde(detectedIdeId, projectRoot ?? '(project root unknown)');
|
|
516
|
+
}
|
|
517
|
+
if (options.targetRoot === undefined && options.ideId === undefined && detectedIdeId === null && projectRoot !== null) {
|
|
518
|
+
warnNoIdeDetected(projectRoot);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
const profileSkillsDir = detectedProfile?.skillsDir ?? null;
|
|
522
|
+
const targetRoot = resolve(
|
|
523
|
+
options.targetRoot
|
|
524
|
+
?? (options.ideId !== undefined ? resolveIdeSkillInstallProfile(options.ideId)?.skillsDir ?? null : null)
|
|
525
|
+
?? process.env.PEAKS_CLAUDE_SKILLS_DIR
|
|
526
|
+
?? profileSkillsDir
|
|
527
|
+
?? join(homedir(), '.claude', 'skills')
|
|
528
|
+
);
|
|
529
|
+
|
|
433
530
|
const installed = [];
|
|
434
531
|
const skipped = [];
|
|
435
532
|
mkdirSync(targetRoot, { recursive: true });
|
|
@@ -485,12 +582,25 @@ export function installBundledSkills(options = {}) {
|
|
|
485
582
|
export function installBundledOutputStyles(options = {}) {
|
|
486
583
|
const packageRoot = resolvePackageRoot(options);
|
|
487
584
|
const outputStylesRoot = join(packageRoot, 'output-styles');
|
|
488
|
-
const targetRoot = resolve(options.targetRoot ?? process.env.PEAKS_CLAUDE_OUTPUT_STYLES_DIR ?? join(homedir(), '.claude', 'output-styles'));
|
|
489
585
|
|
|
490
586
|
if (process.env.PEAKS_SKIP_SKILL_INSTALL === '1' || !existsSync(outputStylesRoot)) {
|
|
491
587
|
return createInstallResult();
|
|
492
588
|
}
|
|
493
589
|
|
|
590
|
+
// Slice #011: IDE-aware dispatch. Same precedence as installBundledSkills.
|
|
591
|
+
const projectRoot = resolveProjectRoot(options);
|
|
592
|
+
const detectedIdeId = detectInstalledIdeId(projectRoot);
|
|
593
|
+
const detectedProfile = resolveIdeSkillInstallProfile(detectedIdeId);
|
|
594
|
+
|
|
595
|
+
const profileOutputStylesDir = detectedProfile?.outputStylesDir ?? null;
|
|
596
|
+
const targetRoot = resolve(
|
|
597
|
+
options.targetRoot
|
|
598
|
+
?? (options.ideId !== undefined ? resolveIdeSkillInstallProfile(options.ideId)?.outputStylesDir ?? null : null)
|
|
599
|
+
?? process.env.PEAKS_CLAUDE_OUTPUT_STYLES_DIR
|
|
600
|
+
?? profileOutputStylesDir
|
|
601
|
+
?? join(homedir(), '.claude', 'output-styles')
|
|
602
|
+
);
|
|
603
|
+
|
|
494
604
|
const installed = [];
|
|
495
605
|
const skipped = [];
|
|
496
606
|
mkdirSync(targetRoot, { recursive: true });
|
|
@@ -109,7 +109,7 @@ Every successful execution writes one JSON line to `.peaks/_runtime/<sid>/ide-on
|
|
|
109
109
|
{"timestamp":"2026-06-06T19:55:00Z","intent":"first-time-install","detected_ide":"trae","cli_invocations":["peaks hooks install --project <repo>","peaks statusline install --project <repo>"],"outcome":"success","session_id":"2026-06-06-session-22f08c"}
|
|
110
110
|
```
|
|
111
111
|
|
|
112
|
-
The audit log is **machine-readable** (so `peaks project dashboard` can read it and surface "you installed peaks for Trae on 2026-06-06") and **human-readable** (so the user can `cat` it to see the install history). The skill does NOT write the log file itself — it delegates to `peaks project dashboard` (the canonical log writer, per the dev-preference red line "skill-first for workflow, CLI-backed for gates / side effects"). The audit log writer is a CLI primitive, not a per-skill helper; the skill body MUST NOT introduce a new `peaks <cmd>` for the log writer.
|
|
112
|
+
The audit log is **machine-readable** (so `peaks project dashboard` can read it and surface "you installed peaks for Trae on 2026-06-06") and **human-readable** (so the user can `cat` it to see the install history). The skill does NOT write the log file itself — it delegates to `peaks project dashboard` (the canonical log writer, per the dev-preference red line "skill-first for workflow, CLI-backed for gates / side effects"). The audit log writer is a CLI primitive, not a per-skill helper; the skill body MUST NOT introduce a new `peaks <cmd>` for the log writer. The thin helper that writes the log is `scripts/peaks-ide-audit-log.mjs`; see [audit-log-helper.md](references/audit-log-helper.md) for its CLI surface and contract.
|
|
113
113
|
|
|
114
114
|
## Boundaries
|
|
115
115
|
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: peaks-ide-audit-log-helper
|
|
3
|
+
description: Thin Node helper that writes a single JSONL line to `.peaks/audit/peaks-ide-<UTC-date>.log`. Reachable from the peaks-ide SKILL.md Step 5 escape hatch. NOT a `peaks <cmd>` CLI primitive (slice #2 closeout + dev-preference red line).
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# peaks-ide audit log helper
|
|
7
|
+
|
|
8
|
+
The peaks-ide skill's Step 5 (`audit log`) delegates the JSONL write to
|
|
9
|
+
`scripts/peaks-ide-audit-log.mjs`. The helper is a thin Node script, not a
|
|
10
|
+
new `peaks <cmd>`, and is the single source of truth for the audit log
|
|
11
|
+
shape (machine + human readable, one JSON object per line).
|
|
12
|
+
|
|
13
|
+
## Why a thin helper, not a CLI primitive
|
|
14
|
+
|
|
15
|
+
Per `.peaks/memory/peaks-ide-skill-ac-10-audit-log-writer-is-a-thin-helper-not-a-separate-cli-primitive.md`
|
|
16
|
+
(slice #2 closeout), the audit log writer is reachable from the skill's
|
|
17
|
+
Step 5 escape hatch but is NOT a new top-level `peaks <cmd>`. The
|
|
18
|
+
`peaks project dashboard` command reads the log via the `audit` scan; the
|
|
19
|
+
helper writes the log. The dev-preference red line "Default-no on new CLI
|
|
20
|
+
commands" still applies — if the audit-trail becomes critical in a future
|
|
21
|
+
slice, the helper can be promoted to a CLI primitive at that point.
|
|
22
|
+
|
|
23
|
+
## CLI surface
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
node scripts/peaks-ide-audit-log.mjs --project <repo> --event <name> --adapter <id> [--ok true|false] [--detail <json>] [--dry-run]
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
| Flag | Required | Description |
|
|
30
|
+
|---|---|---|
|
|
31
|
+
| `--project <path>` | yes | Target project root (parent of `.peaks/`) |
|
|
32
|
+
| `--event <name>` | yes | Event identifier (`install`, `statusline`, `hook-handle`, ...) |
|
|
33
|
+
| `--adapter <id>` | yes | Adapter id (`claude-code`, `trae`, ...) |
|
|
34
|
+
| `--ok <bool>` | no, default `true` | Outcome flag — `false` to record a failure |
|
|
35
|
+
| `--detail <json>` | no | Free-form JSON object attached to the entry |
|
|
36
|
+
| `--dry-run` | no | Print the would-be line; do not write |
|
|
37
|
+
|
|
38
|
+
## Log line shape
|
|
39
|
+
|
|
40
|
+
```json
|
|
41
|
+
{"timestamp":"2026-06-07T16:00:00.000Z","event":"install","adapter":"trae","ok":true}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The log file path is `<projectRoot>/.peaks/audit/peaks-ide-<UTC-date>.log`
|
|
45
|
+
and is gitignored per the repo root `.gitignore` (`.peaks/audit/`).
|
|
46
|
+
|
|
47
|
+
## Contract pinned by tests
|
|
48
|
+
|
|
49
|
+
`tests/unit/skills/peaks-ide/audit-log-helper.test.ts` pins 4 sub-cases
|
|
50
|
+
(per AC-5): helper is at the documented path, write emits one JSONL line
|
|
51
|
+
with `timestamp + event + adapter + ok`, the log path is in `.gitignore`,
|
|
52
|
+
and `--dry-run` returns the would-be line without writing.
|