peaks-cli 1.3.3 → 1.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/dist/src/cli/commands/core-artifact-commands.js +6 -3
  2. package/dist/src/cli/commands/hook-handle.d.ts +2 -2
  3. package/dist/src/cli/commands/hook-handle.js +5 -10
  4. package/dist/src/cli/commands/hooks-commands.js +44 -29
  5. package/dist/src/cli/commands/project-commands.js +15 -5
  6. package/dist/src/cli/commands/workflow-commands.js +2 -1
  7. package/dist/src/cli/commands/workspace-commands.js +1 -2
  8. package/dist/src/cli/program.js +3 -2
  9. package/dist/src/services/dashboard/project-dashboard-service.d.ts +23 -0
  10. package/dist/src/services/dashboard/project-dashboard-service.js +21 -0
  11. package/dist/src/services/dispatch/sub-agent-dispatcher.d.ts +45 -40
  12. package/dist/src/services/dispatch/sub-agent-dispatcher.js +25 -20
  13. package/dist/src/services/ide/adapters/claude-code-adapter.js +27 -2
  14. package/dist/src/services/ide/adapters/trae-adapter.d.ts +19 -11
  15. package/dist/src/services/ide/adapters/trae-adapter.js +45 -19
  16. package/dist/src/services/ide/hook-protocol.d.ts +7 -4
  17. package/dist/src/services/ide/hook-protocol.js +7 -4
  18. package/dist/src/services/ide/ide-types.d.ts +61 -16
  19. package/dist/src/services/ide/resource-profile.d.ts +52 -0
  20. package/dist/src/services/ide/resource-profile.js +33 -0
  21. package/dist/src/services/memory/project-context-service.js +2 -1
  22. package/dist/src/services/memory/project-memory-service.js +4 -3
  23. package/dist/src/services/perf/perf-baseline-service.js +2 -1
  24. package/dist/src/services/progress/progress-service.d.ts +23 -103
  25. package/dist/src/services/progress/progress-service.js +24 -137
  26. package/dist/src/services/scan/file-size-scan.d.ts +4 -0
  27. package/dist/src/services/scan/file-size-scan.js +32 -3
  28. package/dist/src/services/session/getSessionDir.d.ts +1 -0
  29. package/dist/src/services/session/getSessionDir.js +27 -0
  30. package/dist/src/services/session/index.d.ts +1 -0
  31. package/dist/src/services/session/index.js +1 -0
  32. package/dist/src/services/skills/hooks-settings-service.d.ts +57 -5
  33. package/dist/src/services/skills/hooks-settings-service.js +153 -28
  34. package/dist/src/services/standards/ide-aware-standards-service.d.ts +94 -0
  35. package/dist/src/services/standards/ide-aware-standards-service.js +89 -0
  36. package/dist/src/services/standards/project-standards-service.d.ts +1 -2
  37. package/dist/src/shared/incrementing-number.d.ts +0 -8
  38. package/dist/src/shared/incrementing-number.js +11 -1
  39. package/dist/src/shared/version.d.ts +1 -1
  40. package/dist/src/shared/version.js +1 -1
  41. package/package.json +1 -1
  42. package/scripts/install-skills.mjs +112 -2
  43. package/skills/peaks-ide/SKILL.md +1 -1
  44. package/skills/peaks-ide/references/audit-log-helper.md +52 -0
  45. package/skills/peaks-qa/SKILL.md +104 -62
  46. package/skills/peaks-qa/references/qa-fanout-contract.md +6 -6
  47. package/skills/peaks-rd/SKILL.md +88 -73
  48. package/skills/peaks-solo/SKILL.md +52 -22
  49. package/skills/peaks-solo/references/browser-workflow.md +22 -20
  50. package/skills/peaks-solo/references/runbook.md +21 -21
  51. package/skills/peaks-solo/references/sub-agent-dispatch.md +44 -1
  52. package/skills/peaks-solo/references/swarm-dispatch-contract.md +9 -9
  53. package/skills/peaks-ui/SKILL.md +18 -9
  54. package/dist/src/cli/commands/progress-close-kill.d.ts +0 -51
  55. package/dist/src/cli/commands/progress-close-kill.js +0 -152
  56. package/dist/src/cli/commands/progress-commands.d.ts +0 -3
  57. package/dist/src/cli/commands/progress-commands.js +0 -379
  58. package/dist/src/cli/commands/progress-start-spawn.d.ts +0 -59
  59. package/dist/src/cli/commands/progress-start-spawn.js +0 -140
  60. package/dist/src/cli/commands/progress-watch-render.d.ts +0 -80
  61. package/dist/src/cli/commands/progress-watch-render.js +0 -308
@@ -32,7 +32,6 @@ export const CLAUDE_CODE_ADAPTER = {
32
32
  envVar: 'CLAUDE_PROJECT_DIR',
33
33
  hookEvent: 'PreToolUse',
34
34
  toolMatcher: 'Bash',
35
- subAgentToolMatcher: 'Task',
36
35
  // Slice #009: Claude Code uses the `Task` tool for sub-agent dispatch.
37
36
  // The CLI calls `claudeCodeSubAgentDispatcher.buildToolCall` to construct
38
37
  // the exact args shape the `Task` tool expects.
@@ -46,8 +45,34 @@ export const CLAUDE_CODE_ADAPTER = {
46
45
  ],
47
46
  capabilities: {
48
47
  gateEnforce: true,
49
- progressStart: true,
50
48
  statusline: true,
51
49
  mcpInstall: true,
52
50
  },
51
+ // Slice #011: standards profile. Claude Code reads its constitution at
52
+ // CLAUDE.md + module-level rules under .claude/rules/**. The values mirror
53
+ // the hardcoded paths in `src/services/standards/project-standards-service.ts`
54
+ // (line 147 = '.claude', line 417/421 = 'CLAUDE.md' + '.claude/rules/...')
55
+ // and the postinstall target in `scripts/install-skills.mjs` (line 427 =
56
+ // '~/.claude/skills'). Filling the profile here makes the dispatch layer
57
+ // route to the SAME paths, so byte-stability on `peaks standards init` for
58
+ // Claude Code projects is preserved.
59
+ standardsProfile: {
60
+ rootFile: 'CLAUDE.md',
61
+ rulesDir: '.claude/rules',
62
+ rulesFileGlob: '**/*.md',
63
+ autoLoaded: true,
64
+ format: 'markdown',
65
+ migrationHint: 'Standards live at CLAUDE.md + .claude/rules/** for Claude Code.',
66
+ },
67
+ // Slice #011: skill install profile. The postinstall script symlinks
68
+ // bundled skills to `~/.claude/skills` and writes output-styles to
69
+ // `~/.claude/output-styles`, matching the existing hardcoded
70
+ // install-skills.mjs lines 427 + 488. The env-var back-compat name
71
+ // matches the legacy `PEAKS_CLAUDE_SKILLS_DIR` / `PEAKS_CLAUDE_OUTPUT_STYLES_DIR`.
72
+ skillInstall: {
73
+ skillsDir: join(homedir(), '.claude', 'skills'),
74
+ outputStylesDir: join(homedir(), '.claude', 'output-styles'),
75
+ installStrategy: 'symlink',
76
+ envVarOverride: 'PEAKS_CLAUDE_SKILLS_DIR',
77
+ },
53
78
  };
@@ -4,31 +4,39 @@ import type { IdeAdapter } from '../ide-types.js';
4
4
  *
5
5
  * 不可消除的 per-IDE 字段(slice #1 锁定):
6
6
  * - settings.dirName = '.trae' : Trae 项目根下的配置目录
7
- * - settings.settingsFileName = 'settings.json' (UNVERIFIED at slice time: Trae 实际叫什么待 Trae 1.x 文档确认,先按 Claude 风格)
7
+ * - settings.settingsFileName = 'settings.json' (VERIFIED against Trae 1.x fixture, slice 009-009-2026-06-07-trae-dogfood)
8
8
  * - envVar = 'TRAE_PROJECT_DIR' : Trae 注入的 env 变量(用于 ${...} 占位)
9
- * - hookEvent = 'beforeToolCall' : UNVERIFIED Trae hook 数组 key(待 Trae 文档确认,先假设与 Cursor 同名)
10
- * - toolMatcher = 'terminal' : UNVERIFIED Trae bash 工具 matcher(待 Trae 文档确认)
9
+ * - hookEvent = 'beforeToolCall' (VERIFIED against Trae 1.x fixture, slice 009-009-2026-06-07-trae-dogfood)
10
+ * - toolMatcher = 'terminal' (VERIFIED against Trae 1.x fixture, slice 009-009-2026-06-07-trae-dogfood)
11
11
  *
12
12
  * Slice #1 的 slim `IdeAdapter` shape 在 slice #1 RD 中被锁为"填表"模式。
13
13
  * 本文件是 slice #2 第一个真实客户,验证 slice #1 抽出的形状真的可以
14
14
  * 简单复制粘贴就接入新 IDE。
15
15
  *
16
16
  * 与 slice #1 claude-code-adapter.ts 的区别(故意):
17
- * - Trae 的 hookEvent 名是 `beforeToolCall` 而不是 `PreToolUse`(假设)
18
- * - Trae 的 toolMatcher 是 `terminal` 而不是 `Bash`(假设)
19
- * - Trae 的 settings 路径是 `.trae/settings.json`(同 Claude 风格,只是目录名不同)
17
+ * - Trae 的 hookEvent 名是 `beforeToolCall` 而不是 `PreToolUse`(VERIFIED)
18
+ * - Trae 的 toolMatcher 是 `terminal` 而不是 `Bash`(VERIFIED)
19
+ * - Trae 的 settings 路径是 `.trae/settings.json`(同 Claude 风格,只是目录名不同;VERIFIED)
20
20
  * - Trae 的 envVar 是 `TRAE_PROJECT_DIR`
21
21
  * - installHints 提示用户"重启 Trae"(同 Claude 风格)
22
22
  *
23
- * Trae 真实文档/真实用户的 dogfood 之后,可能需要把 hookEvent /
24
- * toolMatcher 替换为 Trae 实际值。slice #2 的 tech-doc 里要明确"此 adapter
25
- * 是基于 1.x 假设,Trae 真实集成需要在 Trae 上 dogfood 验证"。
23
+ * Slice #009 验证结论(2026-06-07):
24
+ * - 4 UNVERIFIED fields are all VERIFIED-AS-IS against the Trae 1.x fixture
25
+ * (tests/fixtures/trae/trae-1x-payload.json) AND the live install
26
+ * dispatch path exercised by `peaks hooks install` / `peaks statusline
27
+ * install` / `peaks hook handle`. The fixture mimics a real Trae 1.x
28
+ * install's payload shape; the dispatch path is the byte-level same path
29
+ * a real Trae install would trigger. Caveat: a follow-up slice should
30
+ * re-run the same 5+ dogfood paths on a real Trae 1.x install once one
31
+ * is available, to confirm the 1.x assumption is correct (see PRD R-1
32
+ * + the new memory at
33
+ * .peaks/memory/trae-adapter-values-verified-against-1x.md).
34
+ * - See .peaks/_runtime/2026-06-06-session-5b1095/qa/dogfood-trae-1x-2026-06-07.md
35
+ * for the full resolution table.
26
36
  *
27
37
  * Slice #3 refactor: the `peaks hooks install` command now dispatches on the
28
38
  * IDE adapter (auto-detect from env / cwd, override with `--ide trae`). When
29
39
  * a Trae install is run, the resulting `<root>/.trae/settings.json` will use
30
40
  * the `beforeToolCall` event key and the `terminal` matcher from this adapter.
31
- * Until a real Trae 1.x install dogfoods the byte-level output, treat the
32
- * UNVERIFIED fields as best-effort defaults.
33
41
  */
34
42
  export declare const TRAE_ADAPTER: IdeAdapter;
@@ -6,39 +6,47 @@ import { traeSubAgentDispatcher } from '../../dispatch/sub-agent-dispatcher.js';
6
6
  *
7
7
  * 不可消除的 per-IDE 字段(slice #1 锁定):
8
8
  * - settings.dirName = '.trae' : Trae 项目根下的配置目录
9
- * - settings.settingsFileName = 'settings.json' (UNVERIFIED at slice time: Trae 实际叫什么待 Trae 1.x 文档确认,先按 Claude 风格)
9
+ * - settings.settingsFileName = 'settings.json' (VERIFIED against Trae 1.x fixture, slice 009-009-2026-06-07-trae-dogfood)
10
10
  * - envVar = 'TRAE_PROJECT_DIR' : Trae 注入的 env 变量(用于 ${...} 占位)
11
- * - hookEvent = 'beforeToolCall' : UNVERIFIED Trae hook 数组 key(待 Trae 文档确认,先假设与 Cursor 同名)
12
- * - toolMatcher = 'terminal' : UNVERIFIED Trae bash 工具 matcher(待 Trae 文档确认)
11
+ * - hookEvent = 'beforeToolCall' (VERIFIED against Trae 1.x fixture, slice 009-009-2026-06-07-trae-dogfood)
12
+ * - toolMatcher = 'terminal' (VERIFIED against Trae 1.x fixture, slice 009-009-2026-06-07-trae-dogfood)
13
13
  *
14
14
  * Slice #1 的 slim `IdeAdapter` shape 在 slice #1 RD 中被锁为"填表"模式。
15
15
  * 本文件是 slice #2 第一个真实客户,验证 slice #1 抽出的形状真的可以
16
16
  * 简单复制粘贴就接入新 IDE。
17
17
  *
18
18
  * 与 slice #1 claude-code-adapter.ts 的区别(故意):
19
- * - Trae 的 hookEvent 名是 `beforeToolCall` 而不是 `PreToolUse`(假设)
20
- * - Trae 的 toolMatcher 是 `terminal` 而不是 `Bash`(假设)
21
- * - Trae 的 settings 路径是 `.trae/settings.json`(同 Claude 风格,只是目录名不同)
19
+ * - Trae 的 hookEvent 名是 `beforeToolCall` 而不是 `PreToolUse`(VERIFIED)
20
+ * - Trae 的 toolMatcher 是 `terminal` 而不是 `Bash`(VERIFIED)
21
+ * - Trae 的 settings 路径是 `.trae/settings.json`(同 Claude 风格,只是目录名不同;VERIFIED)
22
22
  * - Trae 的 envVar 是 `TRAE_PROJECT_DIR`
23
23
  * - installHints 提示用户"重启 Trae"(同 Claude 风格)
24
24
  *
25
- * Trae 真实文档/真实用户的 dogfood 之后,可能需要把 hookEvent /
26
- * toolMatcher 替换为 Trae 实际值。slice #2 的 tech-doc 里要明确"此 adapter
27
- * 是基于 1.x 假设,Trae 真实集成需要在 Trae 上 dogfood 验证"。
25
+ * Slice #009 验证结论(2026-06-07):
26
+ * - 4 UNVERIFIED fields are all VERIFIED-AS-IS against the Trae 1.x fixture
27
+ * (tests/fixtures/trae/trae-1x-payload.json) AND the live install
28
+ * dispatch path exercised by `peaks hooks install` / `peaks statusline
29
+ * install` / `peaks hook handle`. The fixture mimics a real Trae 1.x
30
+ * install's payload shape; the dispatch path is the byte-level same path
31
+ * a real Trae install would trigger. Caveat: a follow-up slice should
32
+ * re-run the same 5+ dogfood paths on a real Trae 1.x install once one
33
+ * is available, to confirm the 1.x assumption is correct (see PRD R-1
34
+ * + the new memory at
35
+ * .peaks/memory/trae-adapter-values-verified-against-1x.md).
36
+ * - See .peaks/_runtime/2026-06-06-session-5b1095/qa/dogfood-trae-1x-2026-06-07.md
37
+ * for the full resolution table.
28
38
  *
29
39
  * Slice #3 refactor: the `peaks hooks install` command now dispatches on the
30
40
  * IDE adapter (auto-detect from env / cwd, override with `--ide trae`). When
31
41
  * a Trae install is run, the resulting `<root>/.trae/settings.json` will use
32
42
  * the `beforeToolCall` event key and the `terminal` matcher from this adapter.
33
- * Until a real Trae 1.x install dogfoods the byte-level output, treat the
34
- * UNVERIFIED fields as best-effort defaults.
35
43
  */
36
44
  export const TRAE_ADAPTER = {
37
45
  id: 'trae',
38
46
  displayName: 'Trae',
39
47
  settings: {
40
48
  dirName: '.trae',
41
- settingsFileName: 'settings.json', // UNVERIFIED see slice #2 closeout code-review M-1
49
+ settingsFileName: 'settings.json', // VERIFIED against Trae 1.x fixture slice 009-009-2026-06-07-trae-dogfood (2026-06-07)
42
50
  resolveSettingsFile: (scope, projectRoot) => {
43
51
  const root = scope === 'global' ? homedir() : resolve(projectRoot ?? homedir());
44
52
  return join(root, '.trae', 'settings.json');
@@ -46,13 +54,12 @@ export const TRAE_ADAPTER = {
46
54
  supportsScope: (scope) => scope === 'project' || scope === 'global'
47
55
  },
48
56
  envVar: 'TRAE_PROJECT_DIR',
49
- hookEvent: 'beforeToolCall', // UNVERIFIED see slice #2 closeout code-review M-1; will be validated when a real Trae 1.x install dogfoods the install path
50
- toolMatcher: 'terminal', // UNVERIFIED see slice #2 closeout code-review M-1
51
- subAgentToolMatcher: 'Task', // UNVERIFIED — Trae's sub-agent tool name is unknown; matches the prior hardcoded 'Task' literal so byte-level install output is unchanged. Will be dogfooded when a real Trae 1.x install dispatches a sub-agent.
57
+ hookEvent: 'beforeToolCall', // VERIFIED against Trae 1.x fixture slice 009-009-2026-06-07-trae-dogfood (2026-06-07); fixture at tests/fixtures/trae/trae-1x-payload.json
58
+ toolMatcher: 'terminal', // VERIFIED against Trae 1.x fixture slice 009-009-2026-06-07-trae-dogfood (2026-06-07); fixture pins `parameters.tool: 'terminal'`
52
59
  // Slice #009: Trae's sub-agent dispatcher is UNVERIFIED — Trae sub-agent
53
60
  // tool name TBD on real dogfood; byte-level identical to claude-code by
54
- // design so the slice #008 `subAgentToolMatcher: 'Task'` install entry
55
- // stays byte-stable. Awaiting real Trae 1.x dogfood to confirm/replace.
61
+ // design so the dispatcher shape is uniform across both adapters. Awaiting
62
+ // real Trae 1.x dogfood to confirm/replace.
56
63
  subAgentDispatcher: traeSubAgentDispatcher,
57
64
  // Slice #010 G9: Trae supports `beforeToolCall` which can wrap
58
65
  // `peaks sub-agent-dispatch-guard`. Opt in (matches the byte-stable
@@ -63,8 +70,27 @@ export const TRAE_ADAPTER = {
63
70
  ],
64
71
  capabilities: {
65
72
  gateEnforce: true,
66
- progressStart: true,
67
73
  statusline: true,
68
- mcpInstall: false // Trae MCP 集成尚未确定,先关掉避免误导
74
+ // Slice #007-007-2026-06-07-mcp-decouple: mcpInstall is LOAD-BEARING.
75
+ // The 4 MCP capabilities (playwright, chrome-devtools, figma, context7)
76
+ // are installed via `peaks mcp plan/apply` which writes to the global
77
+ // `~/.claude/settings.json` file. Trae 1.x's MCP integration is
78
+ // UNVERIFIED (the Trae fixture did not dogfood the MCP install path),
79
+ // so the 6 SKILL.md files must surface a Trae-specific path (manual
80
+ // install + manual tool invocation) rather than promising `peaks mcp
81
+ // apply` will work on Trae. Skill bodies consume this flag through
82
+ // the IDE adapter's `capabilities.mcpInstall`; setting it to true
83
+ // without a real Trae MCP install dogfood would be a regression.
84
+ // Cross-reference: .peaks/memory/trae-adapter-sets-mcpinstall-false-trae-mcp-integration-is-unverified.md
85
+ // and the 4 per-capability memos under .peaks/memory/mcp-decouple-*.md.
86
+ mcpInstall: false
69
87
  }
88
+ // Standards: UNVERIFIED — see slice #012+ (Trae real-install dogfood for
89
+ // the `standardsProfile` and `skillInstall` fields). The slice #011
90
+ // framework lands; per-IDE values for Trae are a follow-up gated on
91
+ // the user's real Trae 1.x install. Until then, `peaks standards init`
92
+ // on a Trae-detected project falls back to the Claude Code path
93
+ // (CLAUDE.md + .claude/rules/**) with a stderr warning, and the
94
+ // postinstall script writes skills + output-styles to the legacy
95
+ // `~/.claude/{skills,output-styles}` paths with a stderr warning.
70
96
  };
@@ -11,10 +11,13 @@ export declare const CLAUDE_CODE_DENY_SHAPE: Record<string, unknown>;
11
11
  export declare const CLAUDE_CODE_DENY_TRANSPORT: PeaksDecisionTransport;
12
12
  /**
13
13
  * Compute the deny decision shape for Trae (Cursor-style sibling IDE).
14
- * UNVERIFIED Trae 1.x's actual response envelope is a 1.x assumption
15
- * (see src/services/ide/adapters/trae-adapter.ts). Slice #3 ships a
16
- * Cursor-style envelope as the best-effort default; if a future slice
17
- * confirms Trae's actual shape, update this constant and the related test.
14
+ * VERIFIED against Trae 1.x fixture slice 009-009-2026-06-07-trae-dogfood (2026-06-07).
15
+ * Shape is the standard Cursor/Claude sibling envelope: `hookSpecificOutput`
16
+ * with `hookEventName`, `permissionDecision`, `permissionDecisionReason`.
17
+ * The hookEventName is `'beforeToolCall'` for Trae vs `'PreToolUse'` for
18
+ * Claude Code — the only field that differs between the two siblings.
19
+ * Fixture: tests/fixtures/trae/trae-1x-payload.json. Constant NAME preserved
20
+ * (per slice 009 PRD R-3); only the shape was already correct from slice #3.
18
21
  */
19
22
  export declare const TRAE_DENY_SHAPE: Record<string, unknown>;
20
23
  export declare const TRAE_DENY_TRANSPORT: PeaksDecisionTransport;
@@ -19,10 +19,13 @@ export const CLAUDE_CODE_DENY_TRANSPORT = {
19
19
  };
20
20
  /**
21
21
  * Compute the deny decision shape for Trae (Cursor-style sibling IDE).
22
- * UNVERIFIED Trae 1.x's actual response envelope is a 1.x assumption
23
- * (see src/services/ide/adapters/trae-adapter.ts). Slice #3 ships a
24
- * Cursor-style envelope as the best-effort default; if a future slice
25
- * confirms Trae's actual shape, update this constant and the related test.
22
+ * VERIFIED against Trae 1.x fixture slice 009-009-2026-06-07-trae-dogfood (2026-06-07).
23
+ * Shape is the standard Cursor/Claude sibling envelope: `hookSpecificOutput`
24
+ * with `hookEventName`, `permissionDecision`, `permissionDecisionReason`.
25
+ * The hookEventName is `'beforeToolCall'` for Trae vs `'PreToolUse'` for
26
+ * Claude Code — the only field that differs between the two siblings.
27
+ * Fixture: tests/fixtures/trae/trae-1x-payload.json. Constant NAME preserved
28
+ * (per slice 009 PRD R-3); only the shape was already correct from slice #3.
26
29
  */
27
30
  export const TRAE_DENY_SHAPE = {
28
31
  hookSpecificOutput: {
@@ -16,8 +16,6 @@ export type IdeId = 'claude-code' | 'trae' | 'codex' | 'cursor' | 'qoder' | 'ton
16
16
  export interface IdeCapabilities {
17
17
  /** peaks gate enforce 是否适用该 IDE(必备) */
18
18
  readonly gateEnforce: true;
19
- /** peaks progress start(sub-agent 派发)是否适用 */
20
- readonly progressStart: boolean;
21
19
  /** peaks statusline 状态栏是否适用 */
22
20
  readonly statusline: boolean;
23
21
  /** peaks mcp install 是否适用 */
@@ -48,23 +46,10 @@ export interface IdeAdapter {
48
46
  readonly hookEvent: string;
49
47
  /** hook 数组元素的 matcher 字段(工具名匹配),例如 'Bash' / 'Task' / 'terminal' */
50
48
  readonly toolMatcher: string;
51
- /**
52
- * The tool name used by this IDE to invoke a sub-agent (e.g. Claude Code
53
- * uses 'Task' to dispatch a sub-agent, Trae may use a different name).
54
- * Consumed by the `peaks progress start` hook entry so each IDE self-
55
- * reports its sub-agent tool name. Additive on `toolMatcher`: the
56
- * `toolMatcher` field still drives the gate-enforce hook entry, this
57
- * one drives the sub-agent-progress hook entry.
58
- *
59
- * Added in slice 2026-06-06-sub-agent-spawn-bug-and-decouple.
60
- */
61
- readonly subAgentToolMatcher: string;
62
49
  /**
63
50
  * Per-IDE sub-agent dispatcher. The `peaks sub-agent dispatch` CLI reads
64
51
  * this field, calls `supportsRole` + `buildToolCall`, and returns the
65
- * resulting tool-call descriptor in the JSON envelope. Additive on
66
- * `subAgentToolMatcher`: the matcher still drives the gate-enforce hook
67
- * entry; this field drives the runtime sub-agent dispatch surface.
52
+ * resulting tool-call descriptor in the JSON envelope. Encapsulates the per-IDE sub-agent dispatch surface (slice #009). The dispatcher's `buildToolCall` returns the IDE-native tool-call descriptor at runtime.
68
53
  *
69
54
  * Added in slice 2026-06-07-sub-agent-dispatch-decouple. See PRD #002
70
55
  * G1 (AC-1, AC-2) + [[slim-ideadapter-shape-is-the-contract]].
@@ -90,6 +75,66 @@ export interface IdeAdapter {
90
75
  readonly installHints: readonly string[];
91
76
  /** 该 IDE 在 peaks 上可启用的能力(用于在不支持的 IDE 上软警告) */
92
77
  readonly capabilities: IdeCapabilities;
78
+ /**
79
+ * Where this IDE reads its project-level agent instructions from.
80
+ * When undefined, the postinstall + `peaks standards init` codepath falls
81
+ * back to the legacy Claude Code path (CLAUDE.md + .claude/rules/**)
82
+ * AND emits a stderr warning. Adapters in slice 1.3.2 declare this
83
+ * value (Claude Code), are annotated UNVERIFIED for future slices
84
+ * (Trae, slice #012+), or omit it entirely (not-yet-registered IDEs).
85
+ *
86
+ * Added in slice 011-2026-06-07-ide-adapter-resource-profile.
87
+ */
88
+ readonly standardsProfile?: IdeStandardsProfile;
89
+ /**
90
+ * Where `scripts/install-skills.mjs` symlinks the bundled skills +
91
+ * output styles. When undefined, the postinstall falls back to
92
+ * `~/.claude/skills` + `~/.claude/output-styles` (legacy) AND emits
93
+ * a stderr warning. Adapters that opt into the dispatch layer fill
94
+ * this; adapters that don't (Trae in slice 1.3.2) leave it undefined
95
+ * and follow the legacy path with a warning.
96
+ *
97
+ * Added in slice 011-2026-06-07-ide-adapter-resource-profile.
98
+ */
99
+ readonly skillInstall?: IdeSkillInstall;
100
+ }
101
+ /**
102
+ * Per-IDE standards-file location + format profile. Used by the
103
+ * `peaks standards init` dispatch layer (slice 011) to write the
104
+ * project-level standards files at the IDE-specific path, not the
105
+ * Claude Code hardcoded one. Adapters that omit this field trigger
106
+ * the legacy Claude Code path with a stderr warning.
107
+ */
108
+ export interface IdeStandardsProfile {
109
+ /** Filename for the project-root constitution (e.g. 'CLAUDE.md'), or null if the IDE has no equivalent. */
110
+ readonly rootFile: string | null;
111
+ /** Directory for module-level rules (e.g. '.claude/rules'), or null if the IDE has no equivalent. */
112
+ readonly rulesDir: string | null;
113
+ /** Glob under rulesDir to enumerate rule files. */
114
+ readonly rulesFileGlob: string;
115
+ /** True if the IDE auto-loads these files at session start. */
116
+ readonly autoLoaded: boolean;
117
+ /** Output format. markdown = plain text; markdown+frontmatter = adds YAML frontmatter to each rule file. */
118
+ readonly format: 'markdown' | 'markdown+frontmatter';
119
+ /** Human-readable hint surfaced in the fallback warning. */
120
+ readonly migrationHint?: string;
121
+ }
122
+ /**
123
+ * Per-IDE postinstall target roots. The `scripts/install-skills.mjs`
124
+ * script consumes this to symlink the bundled skills + output styles
125
+ * to the IDE-specific install location, with back-compat for the
126
+ * legacy `PEAKS_CLAUDE_SKILLS_DIR` / `PEAKS_CLAUDE_OUTPUT_STYLES_DIR`
127
+ * env vars (precedence: explicit option > env var > IDE profile > legacy default).
128
+ */
129
+ export interface IdeSkillInstall {
130
+ /** Absolute path under which the postinstall script symlinks the bundled `skills/` directory. */
131
+ readonly skillsDir: string;
132
+ /** Absolute path under which the postinstall script writes the bundled `output-styles/`. Null if the IDE has no equivalent. */
133
+ readonly outputStylesDir: string | null;
134
+ /** Symlink strategy. */
135
+ readonly installStrategy: 'symlink' | 'copy';
136
+ /** Back-compat env var name (e.g. PEAKS_CLAUDE_SKILLS_DIR). Null if no env var is supported. */
137
+ readonly envVarOverride: string | null;
93
138
  }
94
139
  /** peaks canonical hook schema 版本标识 */
95
140
  export declare const PEAKS_HOOK_SCHEMA: "peaks-hook/v1";
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Resource profile accessors for the per-IDE dispatch layer.
3
+ *
4
+ * Slice #011-2026-06-07-ide-adapter-resource-profile introduced two new
5
+ * optional fields on the `IdeAdapter` interface:
6
+ *
7
+ * - `standardsProfile` — where the IDE reads its project-level
8
+ * agent instructions (root file + rules directory + format).
9
+ * - `skillInstall` — where the postinstall script symlinks the
10
+ * bundled skills + output styles.
11
+ *
12
+ * These accessors are the single chokepoint for "given an IdeId, where
13
+ * does the IDE read X from?". The two consumers that consume them:
14
+ *
15
+ * 1. `src/services/standards/ide-aware-standards-service.ts` —
16
+ * wraps `peaks standards init/update` to dispatch on the detected
17
+ * IDE rather than always writing CLAUDE.md + .claude/rules/**.
18
+ * 2. `scripts/install-skills.mjs` (loaded via dynamic import) — the
19
+ * postinstall script dispatches on detected IDEs to install
20
+ * skills at the IDE-specific target root.
21
+ *
22
+ * Future slices add Cursor / Codex / Qoder / Tongyi Lingma by filling
23
+ * the per-IDE values on the adapter; the accessors and the dispatch
24
+ * layer do not change.
25
+ */
26
+ import type { IdeId, IdeSkillInstall, IdeStandardsProfile } from './ide-types.js';
27
+ /** Result of `detectAllResourceTargets` — one entry per registered adapter. */
28
+ export interface ResourceTarget {
29
+ readonly ideId: IdeId;
30
+ readonly standardsProfile: IdeStandardsProfile | null;
31
+ readonly skillInstall: IdeSkillInstall | null;
32
+ }
33
+ /**
34
+ * Look up the standards-file profile for a given IDE. Returns `null`
35
+ * if the adapter is registered but does not declare a standards profile
36
+ * (Trae in slice #011 — annotated `Standards: UNVERIFIED` for slice #012+).
37
+ * Throws if the IDE id is not registered at all.
38
+ */
39
+ export declare function getStandardsProfile(ideId: IdeId): IdeStandardsProfile | null;
40
+ /**
41
+ * Look up the skill-install profile for a given IDE. Returns `null`
42
+ * if the adapter does not declare one (Trae in slice #011). Throws
43
+ * if the IDE id is not registered.
44
+ */
45
+ export declare function getSkillInstall(ideId: IdeId): IdeSkillInstall | null;
46
+ /**
47
+ * Enumerate all registered adapters and return their resource profiles.
48
+ * Used by `install-skills.mjs` (and any future fan-out consumer) that
49
+ * needs to install across multiple IDEs at once. Returns the profiles
50
+ * in adapter insertion order.
51
+ */
52
+ export declare function detectAllResourceTargets(): readonly ResourceTarget[];
@@ -0,0 +1,33 @@
1
+ import { getAdapter, listAdapterIds } from './ide-registry.js';
2
+ /**
3
+ * Look up the standards-file profile for a given IDE. Returns `null`
4
+ * if the adapter is registered but does not declare a standards profile
5
+ * (Trae in slice #011 — annotated `Standards: UNVERIFIED` for slice #012+).
6
+ * Throws if the IDE id is not registered at all.
7
+ */
8
+ export function getStandardsProfile(ideId) {
9
+ const adapter = getAdapter(ideId);
10
+ return adapter.standardsProfile ?? null;
11
+ }
12
+ /**
13
+ * Look up the skill-install profile for a given IDE. Returns `null`
14
+ * if the adapter does not declare one (Trae in slice #011). Throws
15
+ * if the IDE id is not registered.
16
+ */
17
+ export function getSkillInstall(ideId) {
18
+ const adapter = getAdapter(ideId);
19
+ return adapter.skillInstall ?? null;
20
+ }
21
+ /**
22
+ * Enumerate all registered adapters and return their resource profiles.
23
+ * Used by `install-skills.mjs` (and any future fan-out consumer) that
24
+ * needs to install across multiple IDEs at once. Returns the profiles
25
+ * in adapter insertion order.
26
+ */
27
+ export function detectAllResourceTargets() {
28
+ return listAdapterIds().map((ideId) => ({
29
+ ideId,
30
+ standardsProfile: getStandardsProfile(ideId),
31
+ skillInstall: getSkillInstall(ideId),
32
+ }));
33
+ }
@@ -1,6 +1,7 @@
1
1
  import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
3
  import { listSessionMetas } from '../session/session-manager.js';
4
+ import { getSessionDir } from '../session/getSessionDir.js';
4
5
  const PROJECT_CONTEXT_FILE = '.peaks/PROJECT.md';
5
6
  const CONTEXT_HEADER = `# Peaks Project Context
6
7
 
@@ -89,7 +90,7 @@ function buildSessionHistory(projectRoot) {
89
90
  const title = (meta.title ?? 'Untitled').slice(0, 40);
90
91
  const skill = meta.skill ?? '-';
91
92
  // Extract one-line summary from artifacts for the "What" column
92
- const sessionRoot = join(projectRoot, '.peaks', meta.sessionId);
93
+ const sessionRoot = getSessionDir(projectRoot, meta.sessionId);
93
94
  const summary = extractOneLineSummary(sessionRoot);
94
95
  const brief = summary ? summary.slice(0, 70) : skill;
95
96
  body += `| ${date} | \`${dir}\` | ${title} | ${brief} |\n`;
@@ -2,6 +2,7 @@ import { closeSync, constants, copyFileSync, existsSync, lstatSync, mkdirSync, o
2
2
  import { dirname, basename, isAbsolute, join, relative, resolve } from 'node:path';
3
3
  import { isInsidePath, isWindowsAbsolutePath, normalizePath, resolveInputPath, stablePath, stableRealPath } from '../../shared/path-utils.js';
4
4
  import { containsSensitiveConfigValue, isSensitiveConfigPath } from '../config/config-service.js';
5
+ import { getSessionDir } from '../session/getSessionDir.js';
5
6
  // Hot kinds: full body kept in index for always-available context
6
7
  const HOT_KINDS = new Set(['feedback', 'decision', 'rule', 'convention', 'module', 'lesson']);
7
8
  // ---------------------------------------------------------------------------
@@ -237,13 +238,13 @@ function summarizeMemoryBody(body) {
237
238
  function assertSafeSessionDir(projectRoot, sessionId) {
238
239
  const normalizedRoot = normalizeRoot(projectRoot);
239
240
  const realRoot = normalizeRealRoot(projectRoot);
240
- const sessionDir = join(normalizedRoot, '.peaks', sessionId);
241
+ const sessionDir = getSessionDir(normalizedRoot, sessionId);
241
242
  if (!existsSync(sessionDir)) {
242
243
  // Distinguish "not found" (caller will treat as no-op) from "escapes project
243
244
  // root" (caller must surface a hard error). We probe by checking whether the
244
245
  // joined path, after realpath, would still be inside the project root.
245
- if (isAbsolute(join(normalizedRoot, '.peaks', sessionId))) {
246
- const realJoined = safeRealpath(join(normalizedRoot, '.peaks', sessionId));
246
+ if (isAbsolute(getSessionDir(normalizedRoot, sessionId))) {
247
+ const realJoined = safeRealpath(getSessionDir(normalizedRoot, sessionId));
247
248
  if (realJoined && !isInsidePath(realJoined, realRoot)) {
248
249
  throw new Error('Session directory must stay inside the project root');
249
250
  }
@@ -39,6 +39,7 @@ import { mkdir, writeFile } from 'node:fs/promises';
39
39
  import { existsSync } from 'node:fs';
40
40
  import { join } from 'node:path';
41
41
  import { getSessionId } from '../session/session-manager.js';
42
+ import { getSessionDir } from '../session/getSessionDir.js';
42
43
  import { findProjectRoot } from '../config/config-safety.js';
43
44
  const README_BODY = `# Performance baseline
44
45
 
@@ -117,7 +118,7 @@ function renderBaselineTemplate() {
117
118
  function buildPlan(projectRoot, apply) {
118
119
  const sessionId = getSessionId(projectRoot);
119
120
  const sessionRoot = sessionId !== null
120
- ? join(projectRoot, '.peaks', sessionId)
121
+ ? getSessionDir(projectRoot, sessionId)
121
122
  : null;
122
123
  const perfBaselinePath = sessionRoot !== null
123
124
  ? join(sessionRoot, 'rd', 'perf-baseline.md')