peaks-loop 4.0.28 → 4.0.30

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 (74) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/dist/cli/commands/_register.js +4 -0
  3. package/dist/cli/commands/dispatch-commands.d.ts +5 -41
  4. package/dist/cli/commands/dispatch-commands.js +59 -185
  5. package/dist/cli/commands/evidence-commands.d.ts +12 -0
  6. package/dist/cli/commands/evidence-commands.js +43 -0
  7. package/dist/cli/commands/fresh-context-commands.d.ts +15 -0
  8. package/dist/cli/commands/fresh-context-commands.js +24 -0
  9. package/dist/cli/commands/job-commands.js +14 -1
  10. package/dist/cli/commands/request-commands.js +15 -97
  11. package/dist/cli/commands/request-format-helpers.d.ts +19 -0
  12. package/dist/cli/commands/request-format-helpers.js +102 -0
  13. package/dist/cli/commands/worktree-auth-commands.js +2 -472
  14. package/dist/cli/commands/worktree-lease-commands.d.ts +21 -0
  15. package/dist/cli/commands/worktree-lease-commands.js +500 -0
  16. package/dist/services/artifacts/request-artifact-service.js +13 -3
  17. package/dist/services/code/auto-compact-lifecycle.d.ts +102 -0
  18. package/dist/services/code/auto-compact-lifecycle.js +235 -0
  19. package/dist/services/code/auto-compact-orchestrator.d.ts +1 -1
  20. package/dist/services/code/auto-compact-orchestrator.js +1 -222
  21. package/dist/services/codegraph/codegraph-autorefresh.d.ts +22 -0
  22. package/dist/services/codegraph/codegraph-autorefresh.js +91 -0
  23. package/dist/services/codegraph/codegraph-preflight-service.d.ts +53 -0
  24. package/dist/services/codegraph/codegraph-preflight-service.js +226 -0
  25. package/dist/services/context/build-dispatch-system-prompt.d.ts +52 -0
  26. package/dist/services/context/build-dispatch-system-prompt.js +70 -3
  27. package/dist/services/dispatch/dispatch-record-types.d.ts +278 -0
  28. package/dist/services/dispatch/dispatch-record-types.js +15 -0
  29. package/dist/services/dispatch/dispatch-record-upgrade.d.ts +5 -0
  30. package/dist/services/dispatch/dispatch-record-upgrade.js +230 -0
  31. package/dist/services/dispatch/dispatch-record-writer.d.ts +3 -278
  32. package/dist/services/dispatch/dispatch-record-writer.js +3 -245
  33. package/dist/services/dispatch/dispatch-sub-agent.d.ts +43 -0
  34. package/dist/services/dispatch/dispatch-sub-agent.js +55 -0
  35. package/dist/services/dispatch/isolation-lease.d.ts +43 -0
  36. package/dist/services/dispatch/isolation-lease.js +129 -0
  37. package/dist/services/evidence/evidence-generator.d.ts +26 -0
  38. package/dist/services/evidence/evidence-generator.js +349 -0
  39. package/dist/services/fresh-context/config.d.ts +1 -0
  40. package/dist/services/fresh-context/config.js +13 -0
  41. package/dist/services/fresh-context/fresh-context-block.d.ts +12 -0
  42. package/dist/services/fresh-context/fresh-context-block.js +40 -0
  43. package/dist/services/fresh-context/trigger-scan.d.ts +30 -0
  44. package/dist/services/fresh-context/trigger-scan.js +29 -0
  45. package/dist/services/skills/hooks-codegate-superpowers.d.ts +65 -0
  46. package/dist/services/skills/hooks-codegate-superpowers.js +204 -0
  47. package/dist/services/skills/hooks-settings-service.d.ts +2 -64
  48. package/dist/services/skills/hooks-settings-service.js +2 -215
  49. package/dist/services/skills/skill-statusline-renderer.d.ts +2 -34
  50. package/dist/services/skills/skill-statusline-renderer.js +1 -184
  51. package/dist/services/skills/statusline-palette.d.ts +62 -0
  52. package/dist/services/skills/statusline-palette.js +190 -0
  53. package/dist/services/slice/slice-decompose-import-edges.d.ts +8 -0
  54. package/dist/services/slice/slice-decompose-import-edges.js +102 -0
  55. package/dist/services/slice/slice-decompose-service.js +3 -198
  56. package/dist/services/slice/slice-decompose-tarjan.d.ts +8 -0
  57. package/dist/services/slice/slice-decompose-tarjan.js +108 -0
  58. package/dist/services/standards/project-standards-service.d.ts +1 -9
  59. package/dist/services/standards/project-standards-service.js +3 -232
  60. package/dist/services/standards/standards-render.d.ts +23 -0
  61. package/dist/services/standards/standards-render.js +238 -0
  62. package/dist/services/standards/ui-library-dispatch-block.d.ts +27 -0
  63. package/dist/services/standards/ui-library-dispatch-block.js +48 -0
  64. package/dist/services/workspace/reconcile-migrate.d.ts +77 -0
  65. package/dist/services/workspace/reconcile-migrate.js +230 -0
  66. package/dist/services/workspace/reconcile-service.d.ts +0 -72
  67. package/dist/services/workspace/reconcile-service.js +3 -220
  68. package/dist/services/workspace/workspace-service.js +13 -2
  69. package/dist/shared/incrementing-number.d.ts +11 -0
  70. package/dist/shared/incrementing-number.js +16 -3
  71. package/package.json +5 -5
  72. package/skills/peaks-code/SKILL.md +6 -2
  73. package/skills/peaks-code/references/fresh-context-preflight.md +81 -0
  74. package/skills/peaks-code/references/sub-agent-dispatch.md +27 -8
@@ -0,0 +1,204 @@
1
+ /**
2
+ * Code-gate hook entries + superpowers-deny surface for the Peaks-managed
3
+ * hooks. Extracted from `hooks-settings-service.ts` to keep that file
4
+ * under the 800 LOC cap (mechanical verbatim move). Also carries the
5
+ * per-IDE hook-spec table (`resolveHookSpec` / `HOOK_COMMAND_BY_IDE`)
6
+ * and `HOOK_ENFORCE_SENTINEL`, which the entry/sentinel resolvers depend
7
+ * on, so this module is self-contained apart from the `PeaksHookEntry`
8
+ * type contract re-exported by the caller.
9
+ */
10
+ import { getAdapter } from '../ide/ide-registry.js';
11
+ import { HOOK_OUTER_CACHE_COMMAND, HOOK_OUTER_CACHE_EVENT, HOOK_OUTER_CACHE_SENTINEL, HOOK_WORKSPACE_INIT_COMMAND, HOOK_WORKSPACE_INIT_EVENT, HOOK_WORKSPACE_INIT_SENTINEL } from './session-start-hook-constants.js';
12
+ /** Sentinel substring identifying a Claude-Code gate-enforce hook entry. */
13
+ export const HOOK_ENFORCE_SENTINEL = 'peaks gate enforce';
14
+ /**
15
+ * Per-IDE hook command + sentinel. The default (Claude Code) uses the
16
+ * legacy `peaks gate enforce` surface; Trae / Cursor / Codex (Cursor-style
17
+ * siblings with `before*` / `pre_*` hook events) use `peaks hook handle`,
18
+ * the new dispatcher. New adapters register by adding a single line here.
19
+ *
20
+ * Slice #12 + #13 (2.4.0) honor the framework's "fill the table" promise
21
+ * — adding a new IDE does NOT require editing this function's control
22
+ * flow, only adding a line to this table.
23
+ */
24
+ const HOOK_COMMAND_BY_IDE = {
25
+ 'claude-code': { command: 'peaks gate enforce', sentinel: 'peaks gate enforce' },
26
+ 'trae': { command: 'peaks hook handle', sentinel: 'peaks hook handle' },
27
+ 'cursor': { command: 'peaks hook handle', sentinel: 'peaks hook handle' },
28
+ 'codex': { command: 'peaks hook handle', sentinel: 'peaks hook handle' },
29
+ 'hermes': { command: 'peaks gate enforce', sentinel: 'peaks gate enforce' },
30
+ 'openclaw': { command: 'peaks gate enforce', sentinel: 'peaks gate enforce' },
31
+ // qoder / tongyi-lingma are reserved IdeIds (slice #1) but not yet
32
+ // registered. When a slice adds them, add a HOOK_COMMAND_BY_IDE entry
33
+ // here — the function below fail-closes on missing entries.
34
+ };
35
+ export function resolveHookSpec(ide) {
36
+ // getAdapter throws on unregistered IDEs — the registry is the source of truth.
37
+ const adapter = getAdapter(ide);
38
+ const spec = HOOK_COMMAND_BY_IDE[ide];
39
+ if (!spec) {
40
+ // Defensive fallback: if an adapter is added to the registry without a
41
+ // HOOK_COMMAND_BY_IDE entry, fail-closed with a clear error instead of
42
+ // silently writing a Claude-shaped entry to a non-Claude settings.json.
43
+ throw new Error(`peaks hooks install: unsupported IDE '${ide}' (no HOOK_COMMAND_BY_IDE entry; add one to hooks-settings-service.ts)`);
44
+ }
45
+ return {
46
+ hookEnforceCommand: `${spec.command} --project "\${${adapter.envVar}}"`,
47
+ hookEnforceSentinel: spec.sentinel,
48
+ hookEnforceMatcher: adapter.toolMatcher,
49
+ hookEnforceEvent: adapter.hookEvent
50
+ };
51
+ }
52
+ /**
53
+ * Slice 2026-08-06-codegate-vendor-neutral — code-gate hook entry.
54
+ * Vendor-neutral command (`peaks code-gate --json`); the CLI adapter
55
+ * lives in `src/cli/commands/code-gate-command.ts` and the decision
56
+ * logic lives in `src/services/hooks/pre-tool-code-gate.ts`. The
57
+ * shell-script sibling (`src/services/hooks/pre-tool-code-gate.sh`)
58
+ * encodes the same logic for non-Node harnesses.
59
+ */
60
+ export const HOOK_CODE_GATE_SENTINEL = 'peaks code-gate';
61
+ export const HOOK_CODE_GATE_MATCHER = 'Edit|Write|MultiEdit';
62
+ export const HOOK_CODE_GATE_EVENT = 'PreToolUse';
63
+ export const HOOK_CODE_GATE_COMMAND = `peaks code-gate --json`;
64
+ export function resolveHookEntries(ide, _skipProgress = false) {
65
+ const spec = resolveHookSpec(ide);
66
+ const entries = [
67
+ { sentinel: spec.hookEnforceSentinel, matcher: spec.hookEnforceMatcher, command: spec.hookEnforceCommand, event: spec.hookEnforceEvent }
68
+ ];
69
+ if (ide === 'claude-code') {
70
+ entries.push({
71
+ sentinel: HOOK_OUTER_CACHE_SENTINEL,
72
+ matcher: '',
73
+ command: HOOK_OUTER_CACHE_COMMAND,
74
+ event: HOOK_OUTER_CACHE_EVENT
75
+ });
76
+ // Slice rid-statusline-stale-ux AC-2: SessionStart outer-cache
77
+ // write is followed by `peaks session primer` entry so that
78
+ // rotation + presence cleanup fire BEFORE the first statusline
79
+ // render of a fresh session. The new subcommand is idempotent
80
+ // and short-circuits when the binding already matches — safe to
81
+ // call on every SessionStart. Order matters conceptually:
82
+ // outer-cache write must come first (the primer's
83
+ // ensureSessionWithRotation reads the current outer session id).
84
+ entries.push({
85
+ sentinel: HOOK_WORKSPACE_INIT_SENTINEL,
86
+ matcher: '',
87
+ command: HOOK_WORKSPACE_INIT_COMMAND,
88
+ event: HOOK_WORKSPACE_INIT_EVENT
89
+ });
90
+ // Slice 2026-08-06-codegate-vendor-neutral: code-gate entry on
91
+ // Edit|Write|MultiEdit matcher. Vendor-neutral command
92
+ // (`peaks code-gate --json`); the CLI adapter lives in
93
+ // `src/cli/commands/code-gate-command.ts` and the underlying
94
+ // decision logic is in `src/services/hooks/pre-tool-code-gate.ts`.
95
+ entries.push({
96
+ sentinel: HOOK_CODE_GATE_SENTINEL,
97
+ matcher: HOOK_CODE_GATE_MATCHER,
98
+ command: HOOK_CODE_GATE_COMMAND,
99
+ event: HOOK_CODE_GATE_EVENT
100
+ });
101
+ }
102
+ return entries;
103
+ }
104
+ /**
105
+ * Legacy sentinel set used by uninstall + status to find and remove stale
106
+ * progress-start entries written by pre-#014 installs. The progress-start
107
+ * sentinel is the literal substring that older installs emitted.
108
+ */
109
+ const LEGACY_PROGRESS_START_SENTINEL = 'peaks progress start';
110
+ export function resolveLegacySentinels(ide) {
111
+ if (ide === 'trae') {
112
+ return ['peaks hook handle', LEGACY_PROGRESS_START_SENTINEL];
113
+ }
114
+ // Slice 2026-08-06-session-outer-cache: include the SessionStart outer-cache
115
+ // sentinel so uninstall strips it alongside the gate-enforce entry.
116
+ // Slice 2026-08-06-codegate-vendor-neutral: also include the code-gate
117
+ // sentinel so uninstall strips the Edit|Write|MultiEdit entry alongside
118
+ // the rest of the peaks-managed entries.
119
+ const base = [HOOK_ENFORCE_SENTINEL, LEGACY_PROGRESS_START_SENTINEL, HOOK_CODE_GATE_SENTINEL];
120
+ if (ide === 'claude-code') {
121
+ // Slice rid-statusline-stale-ux AC-2: include the SessionStart
122
+ // workspace-init primer sentinel so uninstall strips it alongside
123
+ // the gate-enforce entry, and so hand-added entries matching this
124
+ // sentinel are recognized as peaks-managed (not stripped as
125
+ // non-Peaks).
126
+ return [...base, HOOK_OUTER_CACHE_SENTINEL, HOOK_WORKSPACE_INIT_SENTINEL];
127
+ }
128
+ return base;
129
+ }
130
+ // --- Slice 2026-07-29-worktree-layer3-deny -----------------------------------
131
+ // Layer 3 of the Worktree Governance 3-layer design (see
132
+ // .peaks/memory/2026-07-29-worktree-layer3-deny.md). The gateway is the IDE
133
+ // native `permissions.deny` list — Claude Code refuses to invoke any Skill
134
+ // listed there BEFORE the LLM even sees it. This is the strongest of the three
135
+ // layers because it bypasses the LLM's tool-call decision entirely: the IDE
136
+ // matches the deny entry against the Skill's namespace and returns a
137
+ // permission error, so the LLM observes "skill not available" rather than
138
+ // "skill denied" — true fail-closed.
139
+ //
140
+ // The list is intentionally tiny: only skills that the superpowers chain
141
+ // *forces* downstream from peaks-loop work paths. We do NOT deny every
142
+ // superpowers skill — only the one whose `description` field or SKILL.md body
143
+ // recommends `git worktree add` / native worktree tools as a step. Anything
144
+ // else stays available as a reference (peaks-code's
145
+ // references/external-skill-invocation.md already describes every other
146
+ // superpowers skill as informational).
147
+ //
148
+ // The IDE expects the deny entry in the form `UseSkill(<skill-id>)`. The
149
+ // prefix is fixed; the skill id is the namespace-qualified name. We render
150
+ // the entries at install time and at uninstall time so the sentinel list is
151
+ // the single source of truth.
152
+ /**
153
+ * The set of Skills whose use is denied at the IDE permissions layer for
154
+ * every `peaks hooks install` call. Each entry is the namespace-qualified
155
+ * skill id; the helper `formatSuperpowersDenyEntry` wraps it in the IDE's
156
+ * `UseSkill(...)` envelope.
157
+ *
158
+ * Slice 2026-07-29-worktree-layer3-deny: the list is empty-aware. To deny a
159
+ * new skill, append its id here and re-run `peaks hooks install`. The list
160
+ * is also the inverse for uninstall: `removeHookInstall` rebuilds the deny
161
+ * list by filtering out anything whose wrapped form matches one of these
162
+ * ids.
163
+ *
164
+ * Slice rid-skill-persistence-001 (2026-08-12): four additional skills
165
+ * denied so the superpowers chain cannot silently override peaks-code.
166
+ * All four are referenced by `superpowers:using-superpowers` as the
167
+ * "bug → systematic-debugging / TDD / verification-before-completion /
168
+ * recursion via using-superpowers" auto-routing pattern — denying the
169
+ * trigger removes the auto-routing surface while leaving all four
170
+ * still readable as reference material from
171
+ * `references/external-skill-invocation.md`.
172
+ */
173
+ export const SUPERPOWERS_DENIED_SKILLS = [
174
+ // superpowers:using-git-worktrees is the chain's "ground zero" — its
175
+ // SKILL.md (lines 47-99) literally instructs the LLM to run
176
+ // `git worktree add`. Denying this Skill at the IDE layer prevents the
177
+ // LLM from ever receiving that prompt.
178
+ 'superpowers:using-git-worktrees',
179
+ // Slice rid-skill-persistence-001: deny the "bug → systematic-debugging"
180
+ // auto-route. Without this entry the chain can preempt the current bee
181
+ // any time a bug report surfaces. Reference-only via
182
+ // references/external-skill-invocation.md §systematic-debugging.
183
+ 'superpowers:systematic-debugging',
184
+ // Slice rid-skill-persistence-001: deny the TDD trigger that the chain
185
+ // pairs with bug reports (write-a-failing-test-first). Reference-only
186
+ // via references/external-skill-invocation.md §test-driven-development.
187
+ 'superpowers:test-driven-development',
188
+ // Slice rid-skill-persistence-001: deny the verification-before-
189
+ // completion trigger that the chain uses to override "declare done"
190
+ // claims from peaks-code. Reference-only via
191
+ // references/external-skill-invocation.md §verification-before-completion.
192
+ 'superpowers:verification-before-completion',
193
+ // Slice rid-skill-persistence-001: deny the recursive chain entrypoint
194
+ // itself. Without this entry the chain auto-spawns another
195
+ // "using-superpowers" Skill call right after the previous chain step,
196
+ // creating the recursive override pattern that peaks-loop's L1 guard
197
+ // cannot see through. Reference-only via
198
+ // references/external-skill-invocation.md §using-superpowers.
199
+ 'superpowers:using-superpowers'
200
+ ];
201
+ export function formatSuperpowersDenyEntry(skillId) {
202
+ return `UseSkill(${skillId})`;
203
+ }
204
+ export const SUPERPOWERS_DENY_SENTINELS = new Set(SUPERPOWERS_DENIED_SKILLS.map(formatSuperpowersDenyEntry));
@@ -1,5 +1,7 @@
1
1
  import type { IdeId } from '../ide/ide-types.js';
2
2
  import type { HookScope } from '../ide/shared/safe-path.js';
3
+ import { type PeaksHookEntry } from './hooks-codegate-superpowers.js';
4
+ export { HOOK_ENFORCE_SENTINEL, HOOK_CODE_GATE_SENTINEL, HOOK_CODE_GATE_MATCHER, HOOK_CODE_GATE_EVENT, HOOK_CODE_GATE_COMMAND, SUPERPOWERS_DENIED_SKILLS, type PeaksHookEntry } from './hooks-codegate-superpowers.js';
3
5
  /**
4
6
  * Install (and remove) the Peaks-managed hooks in an IDE's settings.json.
5
7
  *
@@ -64,8 +66,6 @@ export type HookInstallOptions = {
64
66
  */
65
67
  readonly skipProgress?: boolean;
66
68
  };
67
- /** Sentinel substring identifying a Claude-Code gate-enforce hook entry. */
68
- export declare const HOOK_ENFORCE_SENTINEL = "peaks gate enforce";
69
69
  /** Default (claude-code) hook command — kept as a stable export for tests. */
70
70
  export declare const HOOK_ENFORCE_COMMAND = "peaks gate enforce --project \"${CLAUDE_PROJECT_DIR}\" --json";
71
71
  export type HookInstallPlan = {
@@ -122,68 +122,6 @@ export declare function readInstalledEntriesFromSettings(settings: Record<string
122
122
  matcher: string;
123
123
  sentinel: string;
124
124
  }>;
125
- /** A typed descriptor for a single peaks-managed hook entry. */
126
- export type PeaksHookEntry = {
127
- sentinel: string;
128
- matcher: string;
129
- command: string;
130
- event: string;
131
- };
132
- /**
133
- * Compute the per-IDE peaks hook entries to merge into the settings file.
134
- * Replaces the slice #1 hardcoded `PEAKS_HOOK_ENTRIES` constant; the constant
135
- * remains exported (computed for claude-code) for backward compat.
136
- *
137
- * Slice #013 (`--no-progress` flag): when `skipProgress` is true, the
138
- * progress-start entry is omitted from the returned list. Install with this
139
- * flag will (a) NOT emit the progress hook entry, and (b) will idempotently
140
- * remove any previously-installed progress entry (sentinel-based merge).
141
- *
142
- * Slice #014 (refactor — full removal): only the gate-enforce entry is
143
- * ever emitted. The `skipProgress` parameter is kept for API stability
144
- * but is a no-op — the returned list is always single-entry. The progress
145
- * entry's sentinel is included in the legacy sentinel set so uninstall
146
- * can find + remove any stale progress-start entry that an older
147
- * `peaks hooks install` may have written before this slice.
148
- *
149
- * Slice 2026-08-06-session-outer-cache: a SessionStart outer-cache primer
150
- * entry is appended for Claude Code only (SessionStart is a Claude-Code-
151
- * specific event key today).
152
- */
153
- /**
154
- * Slice 2026-08-06-codegate-vendor-neutral — code-gate hook entry.
155
- * Vendor-neutral command (`peaks code-gate --json`); the CLI adapter
156
- * lives in `src/cli/commands/code-gate-command.ts` and the decision
157
- * logic lives in `src/services/hooks/pre-tool-code-gate.ts`. The
158
- * shell-script sibling (`src/services/hooks/pre-tool-code-gate.sh`)
159
- * encodes the same logic for non-Node harnesses.
160
- */
161
- export declare const HOOK_CODE_GATE_SENTINEL = "peaks code-gate";
162
- export declare const HOOK_CODE_GATE_MATCHER = "Edit|Write|MultiEdit";
163
- export declare const HOOK_CODE_GATE_EVENT = "PreToolUse";
164
- export declare const HOOK_CODE_GATE_COMMAND = "peaks code-gate --json";
165
- /**
166
- * The set of Skills whose use is denied at the IDE permissions layer for
167
- * every `peaks hooks install` call. Each entry is the namespace-qualified
168
- * skill id; the helper `formatSuperpowersDenyEntry` wraps it in the IDE's
169
- * `UseSkill(...)` envelope.
170
- *
171
- * Slice 2026-07-29-worktree-layer3-deny: the list is empty-aware. To deny a
172
- * new skill, append its id here and re-run `peaks hooks install`. The list
173
- * is also the inverse for uninstall: `removeHookInstall` rebuilds the deny
174
- * list by filtering out anything whose wrapped form matches one of these
175
- * ids.
176
- *
177
- * Slice rid-skill-persistence-001 (2026-08-12): four additional skills
178
- * denied so the superpowers chain cannot silently override peaks-code.
179
- * All four are referenced by `superpowers:using-superpowers` as the
180
- * "bug → systematic-debugging / TDD / verification-before-completion /
181
- * recursion via using-superpowers" auto-routing pattern — denying the
182
- * trigger removes the auto-routing surface while leaving all four
183
- * still readable as reference material from
184
- * `references/external-skill-invocation.md`.
185
- */
186
- export declare const SUPERPOWERS_DENIED_SKILLS: ReadonlyArray<string>;
187
125
  /**
188
126
  * Merge the peaks-managed Skill denylist into the existing settings object.
189
127
  * Preserves every other `permissions.deny` entry the user (or another tool)
@@ -4,53 +4,14 @@ import { resolve } from 'node:path';
4
4
  import { assertSafeSettingsFile } from '../ide/shared/safe-path.js';
5
5
  import { atomicWriteJson, readJsonObjectFile } from '../ide/shared/atomic-json.js';
6
6
  import { getAdapter } from '../ide/ide-registry.js';
7
- import { HOOK_OUTER_CACHE_COMMAND, HOOK_OUTER_CACHE_EVENT, HOOK_OUTER_CACHE_SENTINEL, HOOK_WORKSPACE_INIT_COMMAND, HOOK_WORKSPACE_INIT_EVENT, HOOK_WORKSPACE_INIT_SENTINEL } from './session-start-hook-constants.js';
7
+ import { resolveHookSpec, resolveHookEntries, resolveLegacySentinels, SUPERPOWERS_DENIED_SKILLS, formatSuperpowersDenyEntry, SUPERPOWERS_DENY_SENTINELS } from './hooks-codegate-superpowers.js';
8
+ export { HOOK_ENFORCE_SENTINEL, HOOK_CODE_GATE_SENTINEL, HOOK_CODE_GATE_MATCHER, HOOK_CODE_GATE_EVENT, HOOK_CODE_GATE_COMMAND, SUPERPOWERS_DENIED_SKILLS } from './hooks-codegate-superpowers.js';
8
9
  // --- Module-level defaults (claude-code) -----------------------------------
9
10
  // These exports remain for backward compat — tests and downstream callers
10
11
  // that only care about Claude Code can keep importing them. The per-IDE
11
12
  // values are computed lazily inside each public function call.
12
- /** Sentinel substring identifying a Claude-Code gate-enforce hook entry. */
13
- export const HOOK_ENFORCE_SENTINEL = 'peaks gate enforce';
14
13
  /** Default (claude-code) hook command — kept as a stable export for tests. */
15
14
  export const HOOK_ENFORCE_COMMAND = `peaks gate enforce --project "\${CLAUDE_PROJECT_DIR}" --json`;
16
- /**
17
- * Per-IDE hook command + sentinel. The default (Claude Code) uses the
18
- * legacy `peaks gate enforce` surface; Trae / Cursor / Codex (Cursor-style
19
- * siblings with `before*` / `pre_*` hook events) use `peaks hook handle`,
20
- * the new dispatcher. New adapters register by adding a single line here.
21
- *
22
- * Slice #12 + #13 (2.4.0) honor the framework's "fill the table" promise
23
- * — adding a new IDE does NOT require editing this function's control
24
- * flow, only adding a line to this table.
25
- */
26
- const HOOK_COMMAND_BY_IDE = {
27
- 'claude-code': { command: 'peaks gate enforce', sentinel: 'peaks gate enforce' },
28
- 'trae': { command: 'peaks hook handle', sentinel: 'peaks hook handle' },
29
- 'cursor': { command: 'peaks hook handle', sentinel: 'peaks hook handle' },
30
- 'codex': { command: 'peaks hook handle', sentinel: 'peaks hook handle' },
31
- 'hermes': { command: 'peaks gate enforce', sentinel: 'peaks gate enforce' },
32
- 'openclaw': { command: 'peaks gate enforce', sentinel: 'peaks gate enforce' },
33
- // qoder / tongyi-lingma are reserved IdeIds (slice #1) but not yet
34
- // registered. When a slice adds them, add a HOOK_COMMAND_BY_IDE entry
35
- // here — the function below fail-closes on missing entries.
36
- };
37
- function resolveHookSpec(ide) {
38
- // getAdapter throws on unregistered IDEs — the registry is the source of truth.
39
- const adapter = getAdapter(ide);
40
- const spec = HOOK_COMMAND_BY_IDE[ide];
41
- if (!spec) {
42
- // Defensive fallback: if an adapter is added to the registry without a
43
- // HOOK_COMMAND_BY_IDE entry, fail-closed with a clear error instead of
44
- // silently writing a Claude-shaped entry to a non-Claude settings.json.
45
- throw new Error(`peaks hooks install: unsupported IDE '${ide}' (no HOOK_COMMAND_BY_IDE entry; add one to hooks-settings-service.ts)`);
46
- }
47
- return {
48
- hookEnforceCommand: `${spec.command} --project "\${${adapter.envVar}}"`,
49
- hookEnforceSentinel: spec.sentinel,
50
- hookEnforceMatcher: adapter.toolMatcher,
51
- hookEnforceEvent: adapter.hookEvent
52
- };
53
- }
54
15
  function resolveIde(options) {
55
16
  return options?.ide ?? 'claude-code';
56
17
  }
@@ -166,180 +127,6 @@ export function readInstalledEntriesFromSettings(settings, ide) {
166
127
  }
167
128
  return result;
168
129
  }
169
- /**
170
- * Compute the per-IDE peaks hook entries to merge into the settings file.
171
- * Replaces the slice #1 hardcoded `PEAKS_HOOK_ENTRIES` constant; the constant
172
- * remains exported (computed for claude-code) for backward compat.
173
- *
174
- * Slice #013 (`--no-progress` flag): when `skipProgress` is true, the
175
- * progress-start entry is omitted from the returned list. Install with this
176
- * flag will (a) NOT emit the progress hook entry, and (b) will idempotently
177
- * remove any previously-installed progress entry (sentinel-based merge).
178
- *
179
- * Slice #014 (refactor — full removal): only the gate-enforce entry is
180
- * ever emitted. The `skipProgress` parameter is kept for API stability
181
- * but is a no-op — the returned list is always single-entry. The progress
182
- * entry's sentinel is included in the legacy sentinel set so uninstall
183
- * can find + remove any stale progress-start entry that an older
184
- * `peaks hooks install` may have written before this slice.
185
- *
186
- * Slice 2026-08-06-session-outer-cache: a SessionStart outer-cache primer
187
- * entry is appended for Claude Code only (SessionStart is a Claude-Code-
188
- * specific event key today).
189
- */
190
- /**
191
- * Slice 2026-08-06-codegate-vendor-neutral — code-gate hook entry.
192
- * Vendor-neutral command (`peaks code-gate --json`); the CLI adapter
193
- * lives in `src/cli/commands/code-gate-command.ts` and the decision
194
- * logic lives in `src/services/hooks/pre-tool-code-gate.ts`. The
195
- * shell-script sibling (`src/services/hooks/pre-tool-code-gate.sh`)
196
- * encodes the same logic for non-Node harnesses.
197
- */
198
- export const HOOK_CODE_GATE_SENTINEL = 'peaks code-gate';
199
- export const HOOK_CODE_GATE_MATCHER = 'Edit|Write|MultiEdit';
200
- export const HOOK_CODE_GATE_EVENT = 'PreToolUse';
201
- export const HOOK_CODE_GATE_COMMAND = `peaks code-gate --json`;
202
- function resolveHookEntries(ide, _skipProgress = false) {
203
- const spec = resolveHookSpec(ide);
204
- const entries = [
205
- { sentinel: spec.hookEnforceSentinel, matcher: spec.hookEnforceMatcher, command: spec.hookEnforceCommand, event: spec.hookEnforceEvent }
206
- ];
207
- if (ide === 'claude-code') {
208
- entries.push({
209
- sentinel: HOOK_OUTER_CACHE_SENTINEL,
210
- matcher: '',
211
- command: HOOK_OUTER_CACHE_COMMAND,
212
- event: HOOK_OUTER_CACHE_EVENT
213
- });
214
- // Slice rid-statusline-stale-ux AC-2: SessionStart outer-cache
215
- // write is followed by `peaks session primer` entry so that
216
- // rotation + presence cleanup fire BEFORE the first statusline
217
- // render of a fresh session. The new subcommand is idempotent
218
- // and short-circuits when the binding already matches — safe to
219
- // call on every SessionStart. Order matters conceptually:
220
- // outer-cache write must come first (the primer's
221
- // ensureSessionWithRotation reads the current outer session id).
222
- entries.push({
223
- sentinel: HOOK_WORKSPACE_INIT_SENTINEL,
224
- matcher: '',
225
- command: HOOK_WORKSPACE_INIT_COMMAND,
226
- event: HOOK_WORKSPACE_INIT_EVENT
227
- });
228
- // Slice 2026-08-06-codegate-vendor-neutral: code-gate entry on
229
- // Edit|Write|MultiEdit matcher. Vendor-neutral command
230
- // (`peaks code-gate --json`); the CLI adapter lives in
231
- // `src/cli/commands/code-gate-command.ts` and the underlying
232
- // decision logic is in `src/services/hooks/pre-tool-code-gate.ts`.
233
- entries.push({
234
- sentinel: HOOK_CODE_GATE_SENTINEL,
235
- matcher: HOOK_CODE_GATE_MATCHER,
236
- command: HOOK_CODE_GATE_COMMAND,
237
- event: HOOK_CODE_GATE_EVENT
238
- });
239
- }
240
- return entries;
241
- }
242
- /**
243
- * Legacy sentinel set used by uninstall + status to find and remove stale
244
- * progress-start entries written by pre-#014 installs. The progress-start
245
- * sentinel is the literal substring that older installs emitted.
246
- */
247
- const LEGACY_PROGRESS_START_SENTINEL = 'peaks progress start';
248
- function resolveLegacySentinels(ide) {
249
- if (ide === 'trae') {
250
- return ['peaks hook handle', LEGACY_PROGRESS_START_SENTINEL];
251
- }
252
- // Slice 2026-08-06-session-outer-cache: include the SessionStart outer-cache
253
- // sentinel so uninstall strips it alongside the gate-enforce entry.
254
- // Slice 2026-08-06-codegate-vendor-neutral: also include the code-gate
255
- // sentinel so uninstall strips the Edit|Write|MultiEdit entry alongside
256
- // the rest of the peaks-managed entries.
257
- const base = [HOOK_ENFORCE_SENTINEL, LEGACY_PROGRESS_START_SENTINEL, HOOK_CODE_GATE_SENTINEL];
258
- if (ide === 'claude-code') {
259
- // Slice rid-statusline-stale-ux AC-2: include the SessionStart
260
- // workspace-init primer sentinel so uninstall strips it alongside
261
- // the gate-enforce entry, and so hand-added entries matching this
262
- // sentinel are recognized as peaks-managed (not stripped as
263
- // non-Peaks).
264
- return [...base, HOOK_OUTER_CACHE_SENTINEL, HOOK_WORKSPACE_INIT_SENTINEL];
265
- }
266
- return base;
267
- }
268
- // --- Slice 2026-07-29-worktree-layer3-deny -----------------------------------
269
- // Layer 3 of the Worktree Governance 3-layer design (see
270
- // .peaks/memory/2026-07-29-worktree-layer3-deny.md). The gateway is the IDE
271
- // native `permissions.deny` list — Claude Code refuses to invoke any Skill
272
- // listed there BEFORE the LLM even sees it. This is the strongest of the three
273
- // layers because it bypasses the LLM's tool-call decision entirely: the IDE
274
- // matches the deny entry against the Skill's namespace and returns a
275
- // permission error, so the LLM observes "skill not available" rather than
276
- // "skill denied" — true fail-closed.
277
- //
278
- // The list is intentionally tiny: only skills that the superpowers chain
279
- // *forces* downstream from peaks-loop work paths. We do NOT deny every
280
- // superpowers skill — only the one whose `description` field or SKILL.md body
281
- // recommends `git worktree add` / native worktree tools as a step. Anything
282
- // else stays available as a reference (peaks-code's
283
- // references/external-skill-invocation.md already describes every other
284
- // superpowers skill as informational).
285
- //
286
- // The IDE expects the deny entry in the form `UseSkill(<skill-id>)`. The
287
- // prefix is fixed; the skill id is the namespace-qualified name. We render
288
- // the entries at install time and at uninstall time so the sentinel list is
289
- // the single source of truth.
290
- /**
291
- * The set of Skills whose use is denied at the IDE permissions layer for
292
- * every `peaks hooks install` call. Each entry is the namespace-qualified
293
- * skill id; the helper `formatSuperpowersDenyEntry` wraps it in the IDE's
294
- * `UseSkill(...)` envelope.
295
- *
296
- * Slice 2026-07-29-worktree-layer3-deny: the list is empty-aware. To deny a
297
- * new skill, append its id here and re-run `peaks hooks install`. The list
298
- * is also the inverse for uninstall: `removeHookInstall` rebuilds the deny
299
- * list by filtering out anything whose wrapped form matches one of these
300
- * ids.
301
- *
302
- * Slice rid-skill-persistence-001 (2026-08-12): four additional skills
303
- * denied so the superpowers chain cannot silently override peaks-code.
304
- * All four are referenced by `superpowers:using-superpowers` as the
305
- * "bug → systematic-debugging / TDD / verification-before-completion /
306
- * recursion via using-superpowers" auto-routing pattern — denying the
307
- * trigger removes the auto-routing surface while leaving all four
308
- * still readable as reference material from
309
- * `references/external-skill-invocation.md`.
310
- */
311
- export const SUPERPOWERS_DENIED_SKILLS = [
312
- // superpowers:using-git-worktrees is the chain's "ground zero" — its
313
- // SKILL.md (lines 47-99) literally instructs the LLM to run
314
- // `git worktree add`. Denying this Skill at the IDE layer prevents the
315
- // LLM from ever receiving that prompt.
316
- 'superpowers:using-git-worktrees',
317
- // Slice rid-skill-persistence-001: deny the "bug → systematic-debugging"
318
- // auto-route. Without this entry the chain can preempt the current bee
319
- // any time a bug report surfaces. Reference-only via
320
- // references/external-skill-invocation.md §systematic-debugging.
321
- 'superpowers:systematic-debugging',
322
- // Slice rid-skill-persistence-001: deny the TDD trigger that the chain
323
- // pairs with bug reports (write-a-failing-test-first). Reference-only
324
- // via references/external-skill-invocation.md §test-driven-development.
325
- 'superpowers:test-driven-development',
326
- // Slice rid-skill-persistence-001: deny the verification-before-
327
- // completion trigger that the chain uses to override "declare done"
328
- // claims from peaks-code. Reference-only via
329
- // references/external-skill-invocation.md §verification-before-completion.
330
- 'superpowers:verification-before-completion',
331
- // Slice rid-skill-persistence-001: deny the recursive chain entrypoint
332
- // itself. Without this entry the chain auto-spawns another
333
- // "using-superpowers" Skill call right after the previous chain step,
334
- // creating the recursive override pattern that peaks-loop's L1 guard
335
- // cannot see through. Reference-only via
336
- // references/external-skill-invocation.md §using-superpowers.
337
- 'superpowers:using-superpowers'
338
- ];
339
- function formatSuperpowersDenyEntry(skillId) {
340
- return `UseSkill(${skillId})`;
341
- }
342
- const SUPERPOWERS_DENY_SENTINELS = new Set(SUPERPOWERS_DENIED_SKILLS.map(formatSuperpowersDenyEntry));
343
130
  /**
344
131
  * Merge the peaks-managed Skill denylist into the existing settings object.
345
132
  * Preserves every other `permissions.deny` entry the user (or another tool)
@@ -2,6 +2,8 @@ import type { StatusLineModel, TwentyFourHourOverlay } from './skill-statusline-
2
2
  import { computeRootSuffix as computeRootSuffixImpl } from './skill-statusline-sid-suffix.js';
3
3
  export { formatShortSid } from './skill-statusline-sid-suffix.js';
4
4
  export declare const computeRootSuffix: typeof computeRootSuffixImpl;
5
+ import { type StatusLineCapability, type StatusPalette } from './statusline-palette.js';
6
+ export type { StatusLineCapability } from './statusline-palette.js';
5
7
  /**
6
8
  * Pure formatting layer for the Peaks statusLine. Takes the read-only status
7
9
  * model and produces the single line Claude Code paints at the bottom of the
@@ -13,43 +15,9 @@ export declare const computeRootSuffix: typeof computeRootSuffixImpl;
13
15
  * concrete string happens in one place. Adding a fourth capability (e.g. for
14
16
  * a future IDE) is a one-table change.
15
17
  */
16
- export type StatusLineCapability = 'ansi-unicode' | 'unicode' | 'ascii';
17
18
  export interface StatusLineRenderOptions {
18
19
  readonly capability: StatusLineCapability;
19
20
  }
20
- /**
21
- * Glyph palette per capability. Status glyphs (active / idle / warning),
22
- * separators (· / › / ▸ / ASCII variants), and the diagnostic marker
23
- * (`presence unreadable`). Unicode glyphs use widely-supported characters;
24
- * ASCII keeps byte-identical shape across encodings.
25
- *
26
- * Compact glyphs are the unicode quadrant marks ◐◑◒◓, the check ✓, and the
27
- * ballot ✕. ASCII uses bracket/plus/star/x. Bar characters are █/░ in
28
- * unicode and #/- in ASCII. Ratios are joined with `·` (` . ` for ASCII)
29
- * and the before→after arrow is `→` (-> for ASCII).
30
- */
31
- interface StatusPalette {
32
- readonly active: string;
33
- readonly idleStale: string;
34
- readonly idle: string;
35
- readonly warning: string;
36
- readonly inlineSeparator: string;
37
- readonly trailSeparator: string;
38
- readonly idleLabel: string;
39
- readonly invalidMessage: string;
40
- readonly compact: CompactPalette;
41
- readonly barFilled: string;
42
- readonly barEmpty: string;
43
- readonly ratioArrow: string;
44
- }
45
- interface CompactPalette {
46
- readonly queued: string;
47
- readonly preparing: string;
48
- readonly compacting: string;
49
- readonly verifying: string;
50
- readonly completed: string;
51
- readonly failed: string;
52
- }
53
21
  export declare function formatHumanAge(ageMs: number | null): string;
54
22
  /**
55
23
  * Slice rid-statusline-24h-overlay (2026-08-10): format the 24h-mode