peaks-loop 4.0.32 → 4.0.34

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 (111) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/README-en.md +3 -4
  3. package/README.md +20 -28
  4. package/dist/cli/commands/_register.js +4 -3
  5. package/dist/cli/commands/audit-commands.js +12 -39
  6. package/dist/cli/commands/capability-commands.d.ts +6 -0
  7. package/dist/cli/commands/capability-commands.js +20 -2
  8. package/dist/cli/commands/code-commands.js +2 -0
  9. package/dist/cli/commands/code-mode-gate-commands.js +3 -3
  10. package/dist/cli/commands/code-mode-status-command.d.ts +17 -0
  11. package/dist/cli/commands/code-mode-status-command.js +51 -0
  12. package/dist/cli/commands/code-run-command.js +8 -1
  13. package/dist/cli/commands/code-runtime-commands.js +13 -5
  14. package/dist/cli/commands/core/skill-command.js +1 -1
  15. package/dist/cli/commands/ecc-commands.js +3 -2
  16. package/dist/cli/commands/project-commands.js +1 -1
  17. package/dist/cli/commands/request-commands.js +3 -7
  18. package/dist/cli/commands/session-24h-mode.js +9 -1
  19. package/dist/cli/commands/shadcn-commands.d.ts +32 -0
  20. package/dist/cli/commands/shadcn-commands.js +120 -0
  21. package/dist/cli/commands/slice-commands.js +1 -4
  22. package/dist/services/24h-mode/auto-engage.d.ts +39 -0
  23. package/dist/services/24h-mode/auto-engage.js +46 -0
  24. package/dist/services/24h-mode/decider.d.ts +11 -5
  25. package/dist/services/24h-mode/decider.js +13 -13
  26. package/dist/services/audit/enforcers/active-skill-resolver.d.ts +3 -1
  27. package/dist/services/audit/enforcers/active-skill-resolver.js +3 -5
  28. package/dist/services/audit/static-service.d.ts +1 -1
  29. package/dist/services/audit/static-service.js +1 -1
  30. package/dist/services/code/auto-compact-lifecycle.d.ts +1 -1
  31. package/dist/services/code/auto-compact-lifecycle.js +1 -1
  32. package/dist/services/code/auto-compact-modes.d.ts +1 -1
  33. package/dist/services/code/auto-compact-modes.js +1 -1
  34. package/dist/services/code/auto-compact-orchestrator.js +18 -22
  35. package/dist/services/code/mode-gate.d.ts +6 -2
  36. package/dist/services/code/mode-gate.js +9 -5
  37. package/dist/services/code/orchestrator-can-do.js +2 -2
  38. package/dist/services/code-review/ecc-bridge.d.ts +94 -15
  39. package/dist/services/code-review/ecc-bridge.js +133 -13
  40. package/dist/services/config/config-service.d.ts +17 -0
  41. package/dist/services/config/config-service.js +34 -0
  42. package/dist/services/context/auto-compact-dispatcher.d.ts +2 -2
  43. package/dist/services/context/auto-compact-dispatcher.js +4 -4
  44. package/dist/services/context/auto-compact-reader.js +8 -1
  45. package/dist/services/context/auto-compact-types.d.ts +13 -1
  46. package/dist/services/context/build-dispatch-system-prompt.js +2 -2
  47. package/dist/services/context/context-schema.d.ts +1 -1
  48. package/dist/services/dashboard/project-dashboard-service.d.ts +0 -7
  49. package/dist/services/dashboard/project-dashboard-service.js +1 -9
  50. package/dist/services/hooks/auto-compact-hook-install.d.ts +2 -2
  51. package/dist/services/hooks/auto-compact-hook-install.js +2 -2
  52. package/dist/services/ide/adapters/claude-code-adapter.d.ts +51 -0
  53. package/dist/services/ide/adapters/claude-code-adapter.js +84 -14
  54. package/dist/services/ide/ide-types.d.ts +10 -1
  55. package/dist/services/mode/mode-enforcement.d.ts +4 -1
  56. package/dist/services/mode/mode-enforcement.js +26 -28
  57. package/dist/services/mode/mode-status-service.d.ts +81 -0
  58. package/dist/services/mode/mode-status-service.js +108 -0
  59. package/dist/services/preferences/preferences-types.d.ts +16 -7
  60. package/dist/services/recommendations/capability-seed-items.js +0 -1
  61. package/dist/services/recommendations/installed-capability-detector.d.ts +33 -0
  62. package/dist/services/recommendations/installed-capability-detector.js +76 -0
  63. package/dist/services/scan/orphan-service.js +1 -1
  64. package/dist/services/skills/presence-lease-service.d.ts +21 -0
  65. package/dist/services/skills/presence-lease-service.js +26 -0
  66. package/dist/services/skills/skill-presence-service.d.ts +10 -1
  67. package/dist/services/skills/skill-presence-service.js +32 -4
  68. package/dist/services/skills/skill-statusline-service.js +6 -3
  69. package/dist/services/slice/multi-pass-orchestrator.js +0 -13
  70. package/dist/services/slice/slice-decompose-import-edges.d.ts +2 -2
  71. package/dist/services/slice/slice-decompose-import-edges.js +1 -29
  72. package/dist/services/slice/slice-decompose-runners.d.ts +2 -4
  73. package/dist/services/slice/slice-decompose-runners.js +1 -23
  74. package/dist/services/slice/slice-decompose-service.d.ts +3 -4
  75. package/dist/services/slice/slice-decompose-service.js +10 -29
  76. package/dist/services/slice/slice-decompose-types.d.ts +6 -47
  77. package/dist/services/slice/slice-decompose-types.js +3 -2
  78. package/dist/services/slice/slice-topology-types.d.ts +2 -3
  79. package/package.json +5 -5
  80. package/schemas/decomposition-v1.json +0 -12
  81. package/schemas/decomposition-v2.json +1 -15
  82. package/skills/bee/peaks-prd/SKILL.md +0 -1
  83. package/skills/bee/peaks-rd/SKILL.md +1 -1
  84. package/skills/bee/peaks-rd/references/external-references.md +1 -3
  85. package/skills/bee/peaks-rd/references/parallel-review-fanout.md +7 -2
  86. package/skills/bee/peaks-rd/references/rd-fanout-contracts.md +22 -8
  87. package/skills/bee/peaks-rd/references/rd-runbook.md +0 -2
  88. package/skills/bee/peaks-txt/SKILL.md +0 -12
  89. package/skills/peaks-code/SKILL.md +9 -9
  90. package/skills/peaks-code/references/mode-selection.md +7 -3
  91. package/skills/peaks-code/references/periodic-checkpoint.md +4 -4
  92. package/skills/peaks-code/references/runbook.md +10 -5
  93. package/skills/peaks-code/references/session-overload-signal-index.md +2 -2
  94. package/skills/peaks-code/references/standards-preflight.md +1 -1
  95. package/skills/peaks-code/references/startup-sequence.md +1 -1
  96. package/skills/peaks-code/references/step-0-8-gate.md +1 -1
  97. package/skills/peaks-code/references/swarm-dispatch-contract.md +2 -2
  98. package/skills/peaks-code/references/workflow-gates-and-types.md +1 -1
  99. package/skills/peaks-doctor/SKILL.md +1 -1
  100. package/skills/peaks-slice-decompose/SKILL.md +0 -1
  101. package/skills/peaks-slice-decompose/references/v2-schema.md +1 -12
  102. package/dist/cli/commands/understand-commands.d.ts +0 -3
  103. package/dist/cli/commands/understand-commands.js +0 -148
  104. package/dist/services/understand/hybrid-types.d.ts +0 -32
  105. package/dist/services/understand/hybrid-types.js +0 -1
  106. package/dist/services/understand/understand-hybrid-service.d.ts +0 -10
  107. package/dist/services/understand/understand-hybrid-service.js +0 -100
  108. package/dist/services/understand/understand-scan-service.d.ts +0 -28
  109. package/dist/services/understand/understand-scan-service.js +0 -170
  110. package/dist/services/understand/understand-types.d.ts +0 -50
  111. package/dist/services/understand/understand-types.js +0 -1
@@ -158,7 +158,7 @@ export function registerRequestCommands(program, io) {
158
158
  .option('--session-id <session>', 'restrict to a specific session id')
159
159
  .option('--reason <text>', 'reason appended as a transition note; required when --allow-incomplete is set')
160
160
  .option('--allow-incomplete', 'bypass artifact prerequisite checks; requires --reason and records the bypass in the artifact')
161
- .option('--confirm', 'skip interactive confirmation prompt (for non-interactive / LLM contexts)')
161
+ .option('--confirm', 'skip the confirmation gate (for non-interactive / LLM contexts)')
162
162
  .option('--force-confirm', 'bypass mode-enforced confirmation (use with caution)')).action(async (requestId, options) => {
163
163
  try {
164
164
  const role = options.role;
@@ -186,7 +186,7 @@ export function registerRequestCommands(program, io) {
186
186
  const presence = getSkillPresence(options.project);
187
187
  if (presence?.mode === 'assisted' || presence?.mode === 'strict') {
188
188
  if (options.confirm !== true) {
189
- printResult(io, fail('request.transition', 'ALLOW_INCOMPLETE_RESTRICTED', `--allow-incomplete requires --confirm in ${presence.mode} mode`, { role, requestId, mode: presence.mode }, ['Add --confirm to proceed non-interactively, or run in an interactive terminal.']), options.json);
189
+ printResult(io, fail('request.transition', 'ALLOW_INCOMPLETE_RESTRICTED', `--allow-incomplete requires --confirm in ${presence.mode} mode`, { role, requestId, mode: presence.mode }, ['Ask the user via AskUserQuestion whether to proceed, then re-run with --confirm if they approve.']), options.json);
190
190
  process.exitCode = 1;
191
191
  return;
192
192
  }
@@ -387,11 +387,7 @@ export function registerRequestCommands(program, io) {
387
387
  return;
388
388
  }
389
389
  if (error instanceof ConfirmationRequiredError) {
390
- printResult(io, fail('request.transition', 'CONFIRMATION_REQUIRED', error.message, { role: options.role, requestId }, [
391
- 'Add --confirm to proceed non-interactively.',
392
- 'Or run in an interactive terminal.',
393
- 'In assisted/strict mode, major workflow boundaries require explicit user approval.'
394
- ]), options.json);
390
+ printResult(io, fail('request.transition', 'CONFIRMATION_REQUIRED', error.message, { role: options.role, requestId, transitionKey: error.transitionKey, mode: error.mode }, [...error.nextActions]), options.json);
395
391
  process.exitCode = 1;
396
392
  return;
397
393
  }
@@ -20,6 +20,7 @@
20
20
  import { resolveCanonicalProjectRoot } from '../../services/config/config-service.js';
21
21
  import { getErrorMessage } from '../cli-helpers.js';
22
22
  import { HANDOFF_EXIT_CONDITIONS, STATES, emptySnapshot, isHandoffExitCondition, isState, read24hState, write24hState } from '../../services/24h-mode/index.js';
23
+ import { applyAutoEngagePresenceMode } from '../../services/24h-mode/auto-engage.js';
23
24
  function splitList(value) {
24
25
  if (!value)
25
26
  return [];
@@ -135,7 +136,14 @@ export function registerSession24hModeCommand(session, io) {
135
136
  exitCondition: target === 'HANDOFF' ? (merged.exitCondition ?? null) : current.exitCondition
136
137
  };
137
138
  const result = write24hState(projectRoot, sid, next);
138
- printJson(io, { ok: true, data: { ...next, path: result.path } }, merged);
139
+ // Slice 2026-09-09-mode-consolidation (Slice B): entering 24H_ACTIVE
140
+ // is an auto-engage of the `24h` mode. Stamp it onto the presence
141
+ // lease; every other target state leaves the mode untouched (the
142
+ // guard in `applyAutoEngagePresenceMode` refuses non-24h modes).
143
+ const presenceMode = target === '24H_ACTIVE'
144
+ ? applyAutoEngagePresenceMode({ projectRoot, sessionId: sid })
145
+ : null;
146
+ printJson(io, { ok: true, data: { ...next, path: result.path, presenceMode } }, merged);
139
147
  }
140
148
  catch (error) {
141
149
  printError(io, '24H_STATE_WRITE_FAILED', getErrorMessage(error), merged);
@@ -0,0 +1,32 @@
1
+ import { Command } from 'commander';
2
+ import { type ProgramIO } from '../cli-helpers.js';
3
+ /** Pinned upstream version — bump deliberately, never float on `latest`. */
4
+ export declare const SHADCN_PACKAGE = "shadcn@4.21.0";
5
+ export type ShadcnInitState = 'ok' | 'shadcn-missing' | 'execution-failed';
6
+ export type ShadcnInitOptions = {
7
+ readonly cwd: string;
8
+ readonly preset: string;
9
+ readonly template?: string;
10
+ readonly yes?: boolean;
11
+ readonly timeoutMs?: number;
12
+ };
13
+ export type ShadcnInitResult = {
14
+ readonly state: ShadcnInitState;
15
+ readonly argv: readonly string[];
16
+ readonly stdout: string;
17
+ readonly stderr: string;
18
+ readonly exitCode: number | null;
19
+ readonly durationMs: number;
20
+ };
21
+ /**
22
+ * Pure argv builder. `--package <pin> --` makes npx resolve the pinned
23
+ * upstream CLI without installing it into the project.
24
+ */
25
+ export declare function buildShadcnInitArgs(options: Pick<ShadcnInitOptions, 'preset' | 'template' | 'yes'>): string[];
26
+ /**
27
+ * Spawn the upstream CLI through the cross-platform npx resolver (on Windows
28
+ * `npx` is a `.cmd` shim that `spawnSync` refuses to invoke directly).
29
+ * Fail-soft: a missing network / missing npx returns a state, never throws.
30
+ */
31
+ export declare function runShadcnInit(options: ShadcnInitOptions): ShadcnInitResult;
32
+ export declare function registerShadcnCommands(program: Command, io: ProgramIO): void;
@@ -0,0 +1,120 @@
1
+ /**
2
+ * `peaks shadcn init` — dynamic wrapper around the upstream `shadcn` CLI.
3
+ *
4
+ * Slice B2 of 2026-09-09-ecc-dynamic-and-cleanup: `skills/bee/peaks-rd/SKILL.md`
5
+ * and `references/frontend-project-generation.md` already tell RD to scaffold
6
+ * a frontend with `peaks shadcn init --preset [CODE] --template vite`, but the
7
+ * command did not exist and `shadcn` was not a dependency. Rather than add a
8
+ * hard dependency, this mirrors `src/services/lint/detect-ocr-18.ts` +
9
+ * `npx-resolver.ts`: the tool is obtained on demand via
10
+ * `npx --package shadcn@<pin> -- shadcn ...`, so the skill reference is true
11
+ * and the dependency stays dynamic.
12
+ *
13
+ * Per the "Enhancement, not new AI CLI" tenet this is a thin pass-through:
14
+ * peaks-loop resolves the upstream binary and forwards the user's intent. It
15
+ * does not reimplement scaffolding and does not inject its own prompts.
16
+ */
17
+ import { spawnSync } from 'node:child_process';
18
+ import { resolveNpxInvocation } from '../../services/lint/npx-resolver.js';
19
+ import { fail, ok } from 'peaks-loop-shared/result';
20
+ import { addJsonOption, getErrorMessage, printResult } from '../cli-helpers.js';
21
+ /** Pinned upstream version — bump deliberately, never float on `latest`. */
22
+ export const SHADCN_PACKAGE = 'shadcn@4.21.0';
23
+ /**
24
+ * Pure argv builder. `--package <pin> --` makes npx resolve the pinned
25
+ * upstream CLI without installing it into the project.
26
+ */
27
+ export function buildShadcnInitArgs(options) {
28
+ const args = ['--package', SHADCN_PACKAGE, '--', 'shadcn', 'init', '--preset', options.preset];
29
+ if (options.template !== undefined && options.template.length > 0) {
30
+ args.push('--template', options.template);
31
+ }
32
+ if (options.yes === true) {
33
+ args.push('--yes');
34
+ }
35
+ return args;
36
+ }
37
+ /**
38
+ * Spawn the upstream CLI through the cross-platform npx resolver (on Windows
39
+ * `npx` is a `.cmd` shim that `spawnSync` refuses to invoke directly).
40
+ * Fail-soft: a missing network / missing npx returns a state, never throws.
41
+ */
42
+ export function runShadcnInit(options) {
43
+ const start = Date.now();
44
+ const args = buildShadcnInitArgs(options);
45
+ const invocation = resolveNpxInvocation(args);
46
+ const result = spawnSync(invocation.command, [...invocation.args], {
47
+ cwd: options.cwd,
48
+ encoding: 'utf8',
49
+ timeout: options.timeoutMs ?? 300_000,
50
+ maxBuffer: 32 * 1024 * 1024,
51
+ env: invocation.baseEnv
52
+ });
53
+ const stdout = typeof result.stdout === 'string' ? result.stdout : '';
54
+ const stderr = typeof result.stderr === 'string' ? result.stderr : '';
55
+ if (result.error !== undefined && result.error !== null) {
56
+ return {
57
+ state: 'shadcn-missing',
58
+ argv: args,
59
+ stdout,
60
+ stderr: stderr.length > 0 ? stderr : getErrorMessage(result.error),
61
+ exitCode: null,
62
+ durationMs: Date.now() - start
63
+ };
64
+ }
65
+ if (result.status !== 0) {
66
+ return {
67
+ state: 'execution-failed',
68
+ argv: args,
69
+ stdout,
70
+ stderr,
71
+ exitCode: result.status,
72
+ durationMs: Date.now() - start
73
+ };
74
+ }
75
+ return {
76
+ state: 'ok',
77
+ argv: args,
78
+ stdout,
79
+ stderr,
80
+ exitCode: 0,
81
+ durationMs: Date.now() - start
82
+ };
83
+ }
84
+ export function registerShadcnCommands(program, io) {
85
+ const shadcn = program
86
+ .command('shadcn')
87
+ .description('Dynamic wrapper for the upstream shadcn CLI (obtained via `npx --package`; no hard dependency).');
88
+ addJsonOption(shadcn
89
+ .command('init')
90
+ .description('Scaffold/init a shadcn/ui project by forwarding to upstream `shadcn init`.')
91
+ .requiredOption('--preset <code>', 'shadcn registry preset code (resolve it before scaffolding when unknown)')
92
+ .option('--template <name>', 'project template, e.g. vite or next')
93
+ .option('--project <path>', 'target project root', '.')
94
+ .option('--yes', 'forward --yes to upstream (skip its confirmation prompts)', false)).action((options) => {
95
+ const asJson = options.json === true;
96
+ const result = runShadcnInit({
97
+ cwd: options.project,
98
+ preset: options.preset,
99
+ ...(options.template !== undefined && options.template.length > 0 ? { template: options.template } : {}),
100
+ ...(options.yes === true ? { yes: true } : {})
101
+ });
102
+ if (result.state === 'ok') {
103
+ printResult(io, ok('shadcn.init', result, [], [
104
+ `Upstream argv: npx ${result.argv.join(' ')}`,
105
+ 'Generated projects must be TypeScript-only (no JavaScript source or config files).'
106
+ ]), asJson);
107
+ return;
108
+ }
109
+ const code = result.state === 'shadcn-missing' ? 'SHADCN_UNAVAILABLE' : 'SHADCN_INIT_FAILED';
110
+ const message = result.state === 'shadcn-missing'
111
+ ? `could not resolve ${SHADCN_PACKAGE} via npx`
112
+ : `upstream shadcn init exited with code ${result.exitCode}`;
113
+ printResult(io, fail('shadcn.init', code, message, result, [
114
+ `Pinned package: ${SHADCN_PACKAGE}`,
115
+ 'Ensure Node.js >= 20 with npm is on PATH and the network allows the npm registry.',
116
+ `Retry with: npx --package ${SHADCN_PACKAGE} -- shadcn init --preset <code>`
117
+ ]), asJson);
118
+ process.exitCode = 1;
119
+ });
120
+ }
@@ -134,7 +134,7 @@ export function registerSliceCommands(program, io) {
134
134
  addJsonOption(slice
135
135
  .command('decompose <rid>')
136
136
  .description('Run the 6-stage slice-decomposition algorithm on a PRD. ' +
137
- 'Inputs: PRD body + peaks codegraph + .understand-anything/knowledge-graph.json (optional). ' +
137
+ 'Inputs: PRD body + peaks codegraph (project analysis is codegraph-first; structural import edges are the fallback). ' +
138
138
  'Outputs: .peaks/sc/slice-decomposition/<rid>.json with critical-path, ' +
139
139
  'parallel-batches, and per-slice work estimates. ' +
140
140
  'Algorithm is fzf-free. Replay vs hand-derived 2.1.0 dry-run: +-10% on p50. ' +
@@ -202,9 +202,6 @@ export function registerSliceCommands(program, io) {
202
202
  `Next: peaks slice pick ${rid} (requires fzf >= 0.38)`,
203
203
  `Or manually craft -picked.json from the JSON output, then peaks slice plan ${rid}`
204
204
  ];
205
- if (result.understandAnything.fallback === 'structural-only') {
206
- nextActions.push('Note: understand-anything not indexed; cuts are structural-only. Run /understand in your IDE to improve semantic-cut quality.');
207
- }
208
205
  if (result.pickHint) {
209
206
  nextActions.push(result.pickHint);
210
207
  }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Slice 2026-09-09-mode-consolidation (Slice B): auto-engage may set the
3
+ * 24h MODE — and ONLY the 24h mode.
4
+ *
5
+ * `24h` is the single mode that may be set without an explicit user pick
6
+ * (the T1–T5 triggers, and `peaks code run --24h`). Every other mode
7
+ * still requires the Step-1 hard-pause AskUserQuestion
8
+ * (`mode-gate.shouldPauseAtGate` keeps `step-1-mode-select` in
9
+ * `HARD_PAUSE_STEPS`).
10
+ *
11
+ * Before this slice the auto-engage paths wrote only
12
+ * `.peaks/_runtime/<sid>/24h-state.json`; the presence lease kept
13
+ * whatever mode it had (or none), so `24h` was invisible to the mode
14
+ * gate and `strict` + `24H_ACTIVE` still paused every gate. This module
15
+ * is the single place that closes that gap, and the single guard that
16
+ * refuses to stamp any other mode.
17
+ */
18
+ export declare const AUTO_ENGAGE_PRESENCE_MODE: "24h";
19
+ export type AutoEngagePresenceResult = {
20
+ applied: true;
21
+ mode: typeof AUTO_ENGAGE_PRESENCE_MODE;
22
+ updated: number;
23
+ } | {
24
+ applied: false;
25
+ mode: string;
26
+ reason: 'mode-not-auto-settable' | 'no-in-flight-lease' | 'stamp-failed';
27
+ };
28
+ /**
29
+ * Stamp the 24h mode onto every in-flight presence lease of the bound
30
+ * session. Fail-soft: never throws — a missing lease / unreadable store
31
+ * degrades to `{ applied: false }` so the auto-engage caller can still
32
+ * report the 24h-state transition.
33
+ */
34
+ export declare function applyAutoEngagePresenceMode(input: {
35
+ projectRoot: string;
36
+ sessionId: string;
37
+ /** Defaults to `24h`; any other value is refused. */
38
+ mode?: string;
39
+ }): AutoEngagePresenceResult;
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Slice 2026-09-09-mode-consolidation (Slice B): auto-engage may set the
3
+ * 24h MODE — and ONLY the 24h mode.
4
+ *
5
+ * `24h` is the single mode that may be set without an explicit user pick
6
+ * (the T1–T5 triggers, and `peaks code run --24h`). Every other mode
7
+ * still requires the Step-1 hard-pause AskUserQuestion
8
+ * (`mode-gate.shouldPauseAtGate` keeps `step-1-mode-select` in
9
+ * `HARD_PAUSE_STEPS`).
10
+ *
11
+ * Before this slice the auto-engage paths wrote only
12
+ * `.peaks/_runtime/<sid>/24h-state.json`; the presence lease kept
13
+ * whatever mode it had (or none), so `24h` was invisible to the mode
14
+ * gate and `strict` + `24H_ACTIVE` still paused every gate. This module
15
+ * is the single place that closes that gap, and the single guard that
16
+ * refuses to stamp any other mode.
17
+ */
18
+ import { stampPresenceLeaseMode } from '../skills/presence-lease-service.js';
19
+ export const AUTO_ENGAGE_PRESENCE_MODE = '24h';
20
+ /**
21
+ * Stamp the 24h mode onto every in-flight presence lease of the bound
22
+ * session. Fail-soft: never throws — a missing lease / unreadable store
23
+ * degrades to `{ applied: false }` so the auto-engage caller can still
24
+ * report the 24h-state transition.
25
+ */
26
+ export function applyAutoEngagePresenceMode(input) {
27
+ const mode = input.mode ?? AUTO_ENGAGE_PRESENCE_MODE;
28
+ if (mode !== AUTO_ENGAGE_PRESENCE_MODE) {
29
+ return { applied: false, mode, reason: 'mode-not-auto-settable' };
30
+ }
31
+ let updated;
32
+ try {
33
+ updated = stampPresenceLeaseMode({
34
+ projectRoot: input.projectRoot,
35
+ sessionId: input.sessionId,
36
+ mode: AUTO_ENGAGE_PRESENCE_MODE
37
+ }).updated;
38
+ }
39
+ catch {
40
+ return { applied: false, mode, reason: 'stamp-failed' };
41
+ }
42
+ if (updated === 0) {
43
+ return { applied: false, mode, reason: 'no-in-flight-lease' };
44
+ }
45
+ return { applied: true, mode: AUTO_ENGAGE_PRESENCE_MODE, updated };
46
+ }
@@ -95,9 +95,15 @@ export type HandoffExitCondition = (typeof HANDOFF_EXIT_CONDITIONS)[number];
95
95
  export declare function isHandoffExitCondition(s: string): s is HandoffExitCondition;
96
96
  export declare function isHandoffState(s: State): boolean;
97
97
  /**
98
- * Slice 2026-07-28 (rid-027): resolve the auto-compact mode from
99
- * 24h-mode awareness. Returns `'partial'` when the session is
100
- * `24H_ACTIVE`, otherwise `'standard'`. This is the CLI-side default;
101
- * the explicit `--mode` flag in `peaks compact auto` overrides it.
98
+ * Slice 2026-07-28 (rid-027), re-keyed by slice
99
+ * 2026-09-09-mode-consolidation: resolve the auto-compact mode from
100
+ * the PRESENCE MODE, not the 24h state machine. Returns `'partial'`
101
+ * when the mode is `24h`, otherwise `'standard'`. This is the CLI-side
102
+ * default; the explicit `--mode` flag in `peaks code auto-compact`
103
+ * overrides it.
104
+ *
105
+ * `sessionId` is retained in the signature for callers that still pass
106
+ * it; the resolution itself is project-scoped (the presence lease is
107
+ * per project + caller).
102
108
  */
103
- export declare function getAutoCompactMode(projectRoot: string, sessionId: string): AutoCompactMode;
109
+ export declare function getAutoCompactMode(projectRoot: string, _sessionId?: string): AutoCompactMode;
@@ -11,7 +11,7 @@
11
11
  * WAITING_USER — proposal §1 T3/T4 exception).
12
12
  */
13
13
  import { B3_THRESHOLD, isDecisionKey } from './state.js';
14
- import { read24hState } from './store.js';
14
+ import { resolveAutoCompactProfile } from '../mode/mode-status-service.js';
15
15
  export const B3_TRIGGER_REASONS = [
16
16
  'prd_direction_change',
17
17
  'blocker_3_consecutive_slices',
@@ -128,17 +128,17 @@ export function isHandoffState(s) {
128
128
  return s === 'HANDOFF';
129
129
  }
130
130
  /**
131
- * Slice 2026-07-28 (rid-027): resolve the auto-compact mode from
132
- * 24h-mode awareness. Returns `'partial'` when the session is
133
- * `24H_ACTIVE`, otherwise `'standard'`. This is the CLI-side default;
134
- * the explicit `--mode` flag in `peaks compact auto` overrides it.
131
+ * Slice 2026-07-28 (rid-027), re-keyed by slice
132
+ * 2026-09-09-mode-consolidation: resolve the auto-compact mode from
133
+ * the PRESENCE MODE, not the 24h state machine. Returns `'partial'`
134
+ * when the mode is `24h`, otherwise `'standard'`. This is the CLI-side
135
+ * default; the explicit `--mode` flag in `peaks code auto-compact`
136
+ * overrides it.
137
+ *
138
+ * `sessionId` is retained in the signature for callers that still pass
139
+ * it; the resolution itself is project-scoped (the presence lease is
140
+ * per project + caller).
135
141
  */
136
- export function getAutoCompactMode(projectRoot, sessionId) {
137
- try {
138
- const snap = read24hState(projectRoot, sessionId);
139
- return snap.state === '24H_ACTIVE' ? 'partial' : 'standard';
140
- }
141
- catch {
142
- return 'standard';
143
- }
142
+ export function getAutoCompactMode(projectRoot, _sessionId) {
143
+ return resolveAutoCompactProfile(projectRoot);
144
144
  }
@@ -30,7 +30,9 @@ export interface ActiveSkillResolution {
30
30
  readonly sessionId: string | null;
31
31
  /**
32
32
  * Mode token recorded on the canonical lease (e.g. `full-auto`,
33
- * `assisted`, `swarm`, `strict`). `null` when the source is not
33
+ * `assisted`, `strict`, `24h`). Normalized on read: a legacy
34
+ * on-disk `'swarm'` surfaces as `'full-auto'` (slice
35
+ * 2026-09-09-mode-consolidation). `null` when the source is not
34
36
  * `canonical` (the legacy `active-skill-*.json` files do not
35
37
  * surface a mode field, and the `env` / `none` cases are test
36
38
  * overrides). Slice 2026-08-04-rid-005 surfaced this so the
@@ -29,7 +29,7 @@ import { join } from 'node:path';
29
29
  import { getSessionIdCanonical } from '../../session/session-manager.js';
30
30
  import { getSessionDir } from '../../session/getSessionDir.js';
31
31
  import { readPresenceLease, listPresenceLeases } from '../../skills/presence-lease-service.js';
32
- import { getCurrentSessionId } from '../../skills/skill-presence-service.js';
32
+ import { getCurrentSessionId, normalizeSkillPresenceMode } from '../../skills/skill-presence-service.js';
33
33
  const ACTIVE_SKILL_PREFIX = 'active-skill-';
34
34
  /**
35
35
  * Resolve the active peak skill for the current hook invocation.
@@ -99,7 +99,7 @@ export function resolveActiveSkillForCaller(projectRoot, opts) {
99
99
  skill: projection.lease.skill,
100
100
  callerId: lease.callerId,
101
101
  sessionId,
102
- mode: typeof projection.mode === 'string' && projection.mode.length > 0 ? projection.mode : null,
102
+ mode: normalizeSkillPresenceMode(typeof projection.mode === 'string' ? projection.mode : null) ?? null,
103
103
  source: 'canonical',
104
104
  };
105
105
  }
@@ -127,9 +127,7 @@ export function resolveActiveSkillForCaller(projectRoot, opts) {
127
127
  const raw = readFileSync(filePath, 'utf8');
128
128
  const parsed = JSON.parse(raw);
129
129
  if (typeof parsed.skill === 'string' && parsed.skill.length > 0) {
130
- const legacyMode = typeof parsed.mode === 'string' && parsed.mode.length > 0
131
- ? parsed.mode
132
- : null;
130
+ const legacyMode = normalizeSkillPresenceMode(typeof parsed.mode === 'string' ? parsed.mode : null) ?? null;
133
131
  return { skill: parsed.skill, callerId, sessionId, mode: legacyMode, source: 'file' };
134
132
  }
135
133
  }
@@ -5,7 +5,7 @@
5
5
  * integration entirely. The pre-Slice-3 implementation probed
6
6
  * `npx ecc-agentshield --version` and shelled out to
7
7
  * `npx ecc-agentshield scan --json --target <path>` on every audit
8
- * call, but the upstream `affaan-m/everything-claude-code` v2.0.0
8
+ * call, but the upstream `affaan-m/everything-claude-code` (now `affaan-m/ECC`) v2.0.0
9
9
  * release ships no `ecc-agentshield` binary — the repo is
10
10
  * `agents/*.md` flat files plus SKILL.md descriptors.
11
11
  *
@@ -5,7 +5,7 @@
5
5
  * integration entirely. The pre-Slice-3 implementation probed
6
6
  * `npx ecc-agentshield --version` and shelled out to
7
7
  * `npx ecc-agentshield scan --json --target <path>` on every audit
8
- * call, but the upstream `affaan-m/everything-claude-code` v2.0.0
8
+ * call, but the upstream `affaan-m/everything-claude-code` (now `affaan-m/ECC`) v2.0.0
9
9
  * release ships no `ecc-agentshield` binary — the repo is
10
10
  * `agents/*.md` flat files plus SKILL.md descriptors.
11
11
  *
@@ -76,7 +76,7 @@ export declare function summarizeLifecycleError(error: unknown): string;
76
76
  *
77
77
  * This is the integration with the actual post-compact detection path.
78
78
  * The claude-code adapter's `postCompactDetectCommand` is
79
- * `peaks compact auto --json` — i.e. the next probe through this very
79
+ * `peaks code auto-compact --json` — i.e. the next probe through this very
80
80
  * function. So when a probe finds a run still sitting at `compacting`
81
81
  * and MEASURES a ratio that has dropped below the auto-fire threshold,
82
82
  * that measurement is the proof the compact landed. Only then do we
@@ -168,7 +168,7 @@ export function summarizeLifecycleError(error) {
168
168
  *
169
169
  * This is the integration with the actual post-compact detection path.
170
170
  * The claude-code adapter's `postCompactDetectCommand` is
171
- * `peaks compact auto --json` — i.e. the next probe through this very
171
+ * `peaks code auto-compact --json` — i.e. the next probe through this very
172
172
  * function. So when a probe finds a run still sitting at `compacting`
173
173
  * and MEASURES a ratio that has dropped below the auto-fire threshold,
174
174
  * that measurement is the proof the compact landed. Only then do we
@@ -8,7 +8,7 @@
8
8
  *
9
9
  * Slice 2026-07-29-context-evaluation-accuracy Part 22: a new
10
10
  * `autoFire` tier is added between soft-warn and preCompact. peaks-loop
11
- * preempts and runs `peaks compact auto --execute` itself when
11
+ * preempts and runs `peaks code auto-compact` itself when
12
12
  * context crosses `autoFire`; the LLM is NOT asked to "decide"
13
13
  * (the LLM-judged 0.85–0.95 zone was the source of false-positive
14
14
  * "context too low" reports at 60%+ free — the LLM was guessing).
@@ -8,7 +8,7 @@
8
8
  *
9
9
  * Slice 2026-07-29-context-evaluation-accuracy Part 22: a new
10
10
  * `autoFire` tier is added between soft-warn and preCompact. peaks-loop
11
- * preempts and runs `peaks compact auto --execute` itself when
11
+ * preempts and runs `peaks code auto-compact` itself when
12
12
  * context crosses `autoFire`; the LLM is NOT asked to "decide"
13
13
  * (the LLM-judged 0.85–0.95 zone was the source of false-positive
14
14
  * "context too low" reports at 60%+ free — the LLM was guessing).
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Auto-compact orchestrator (v2.13.0 AC-2 + AC-3 + AC-4).
3
3
  *
4
- * Closes the loop between `peaks compact auto` (AC-1) and the IDE's
4
+ * Closes the loop between `peaks code auto-compact` (AC-1) and the IDE's
5
5
  * native compact capability (AC-3). peaks-loop is project-aware: it
6
6
  * knows the current plan, open questions, recent decisions, in-flight
7
7
  * batches, todo state, git status, and active skills. That context is
@@ -16,7 +16,7 @@
16
16
  * 3. If 0.85 ≤ ratio < 0.95 (pre-compact zone): peaks-loop prepares
17
17
  * the convergence toolkit (checkpoint + auto-decisions log +
18
18
  * IDE-dispatch handle) and surfaces it to the LLM. The LLM
19
- * DECIDES when to fire `peaks compact auto --execute`;
19
+ * DECIDES when to fire `peaks code auto-compact`;
20
20
  * peaks-loop does NOT auto-fire. The toolkit is ready so the
21
21
  * LLM doesn't lose context to a last-second `/compact` panic.
22
22
  * 4. If ratio < 0.85: skip — return a one-line info row.
@@ -34,7 +34,7 @@ import { getSessionIdCanonical } from '../session/session-manager.js';
34
34
  import { resolveOuterSessionId } from '../session/binding-status-service.js';
35
35
  import { AUTO_COMPACT_PRE_COMPACT_RATIO, AUTO_COMPACT_THRESHOLD_RATIO } from '../context/auto-compact-types.js';
36
36
  import { describeMode, thresholdFor } from './auto-compact-modes.js';
37
- import { read24hState } from '../24h-mode/store.js';
37
+ import { resolveAutoCompactProfile } from '../mode/mode-status-service.js';
38
38
  import { CompactLifecyclePublisher, newCompactRunId, settleOpenLifecycleRun, summarizeLifecycleError } from './auto-compact-lifecycle.js';
39
39
  const PRE_COMPACT_REASON = 'pre-compact-auto';
40
40
  /**
@@ -72,7 +72,7 @@ export function evaluateCompactTrigger(ratio, mode = 'standard') {
72
72
  }
73
73
  if (ratio < preCompact) {
74
74
  // Part 22: auto-fire zone (0.80 ≤ ratio < 0.85). peaks-loop
75
- // preempts and runs `peaks compact auto --execute` itself
75
+ // preempts and runs `peaks code auto-compact` itself
76
76
  // without LLM involvement. The LLM is not asked to "decide";
77
77
  // the toolkit is applied synchronously. Closes the
78
78
  // LLM-misjudges-context window that previously let the
@@ -255,20 +255,15 @@ function writePreCompactCheckpoint(input) {
255
255
  return path;
256
256
  }
257
257
  /**
258
- * Slice 2026-07-28 (rid-027): resolve the auto-compact mode from
259
- * 24h-mode awareness. Returns `'partial'` when the session is
260
- * `24H_ACTIVE`, otherwise `'standard'`. The CLI flag `--mode` takes
261
- * precedence (caller passes `input.mode` directly), so this helper is
262
- * only consulted when the flag is absent.
258
+ * Slice 2026-07-28 (rid-027), re-keyed by slice
259
+ * 2026-09-09-mode-consolidation: resolve the auto-compact mode from
260
+ * the PRESENCE MODE, not the 24h state machine. Returns `'partial'`
261
+ * when the mode is `24h`, otherwise `'standard'`. The CLI flag
262
+ * `--mode` takes precedence (caller passes `input.mode` directly), so
263
+ * this helper is only consulted when the flag is absent.
263
264
  */
264
- function resolveAutoCompactMode(projectRoot, sessionId) {
265
- try {
266
- const snap = read24hState(projectRoot, sessionId);
267
- return snap.state === '24H_ACTIVE' ? 'partial' : 'standard';
268
- }
269
- catch {
270
- return 'standard';
271
- }
265
+ function resolveAutoCompactMode(projectRoot) {
266
+ return resolveAutoCompactProfile(projectRoot);
272
267
  }
273
268
  /**
274
269
  * Execute the auto-compact flow.
@@ -300,9 +295,10 @@ export async function runAutoCompact(input) {
300
295
  };
301
296
  }
302
297
  // Slice 2026-07-28 (rid-027): resolve mode. CLI flag `--mode` wins;
303
- // 24h-mode awareness yields 'partial' when 24h_ACTIVE; default
304
- // 'standard' preserves v2.13.0 zero-pause contract.
305
- const mode = input.mode ?? resolveAutoCompactMode(input.projectRoot, sessionId);
298
+ // slice 2026-09-09-mode-consolidation keys the default off the
299
+ // presence MODE ('24h' → 'partial'); 'standard' preserves the
300
+ // v2.13.0 zero-pause contract.
301
+ const mode = input.mode ?? resolveAutoCompactMode(input.projectRoot);
306
302
  // Lazy import to avoid the AC-1 module depending on the orchestrator.
307
303
  const { readContextPercent } = await import('../context/auto-compact-reader.js');
308
304
  const outerSessionId = resolveOuterSessionId(input.projectRoot, sessionId, input.env ?? process.env);
@@ -398,12 +394,12 @@ export async function runAutoCompact(input) {
398
394
  ? [
399
395
  'RED-LINE compact dispatched — further sub-agent dispatch BLOCKED until ratio < 0.85',
400
396
  'Post-compact resume picks up the convergence plan from auto-decisions.md',
401
- 'Next `peaks compact auto` probe will confirm ratio dropped below 0.85'
397
+ 'Next `peaks code auto-compact` probe will confirm ratio dropped below 0.85'
402
398
  ]
403
399
  : [
404
400
  'Pre-compact dispatched — IDE compact in progress (async)',
405
401
  'Post-compact resume picks up the convergence plan from auto-decisions.md',
406
- 'Next `peaks compact auto` probe will confirm ratio dropped below 0.85'
402
+ 'Next `peaks code auto-compact` probe will confirm ratio dropped below 0.85'
407
403
  ];
408
404
  plan = buildConvergencePlan({
409
405
  sessionId,
@@ -5,7 +5,7 @@
5
5
  * pause for an AskUserQuestion round-trip?" The D5 design (per
6
6
  * `.peaks/memory/2026-06-26-v2-11-full-auto-self-decision.md`) requires:
7
7
  *
8
- * - `full-auto` and `swarm` modes → auto-proceed (recommended = chosen)
8
+ * - `full-auto` and `24h` modes → auto-proceed (recommended = chosen)
9
9
  * - `assisted` and `strict` modes → pause for confirmation
10
10
  * - 3 hard-floor categories ALWAYS ask, regardless of mode:
11
11
  * 1. Irreversible external side effects (git push, npm publish, …)
@@ -77,7 +77,11 @@ export declare function isHardFloorCategory(value: string): value is HardFloorCa
77
77
  /**
78
78
  * `true` when the current mode should auto-proceed (skip the
79
79
  * AskUserQuestion round-trip). Mirrors D5.a: "recommended = chosen
80
- * in full-auto / swarm; always log, never silently skip".
80
+ * in full-auto / 24h; always log, never silently skip".
81
+ *
82
+ * Slice 2026-09-09-mode-consolidation: `swarm` was removed as a mode
83
+ * (parallel fan-out is now the default execution strategy in every
84
+ * mode); `24h` replaces it as the second auto-proceed peer.
81
85
  */
82
86
  export declare function shouldAutoProceed(mode: CodeMode): boolean;
83
87
  /**
@@ -5,7 +5,7 @@
5
5
  * pause for an AskUserQuestion round-trip?" The D5 design (per
6
6
  * `.peaks/memory/2026-06-26-v2-11-full-auto-self-decision.md`) requires:
7
7
  *
8
- * - `full-auto` and `swarm` modes → auto-proceed (recommended = chosen)
8
+ * - `full-auto` and `24h` modes → auto-proceed (recommended = chosen)
9
9
  * - `assisted` and `strict` modes → pause for confirmation
10
10
  * - 3 hard-floor categories ALWAYS ask, regardless of mode:
11
11
  * 1. Irreversible external side effects (git push, npm publish, …)
@@ -22,8 +22,8 @@
22
22
  export const CODE_MODES = [
23
23
  'full-auto',
24
24
  'assisted',
25
- 'swarm',
26
- 'strict'
25
+ 'strict',
26
+ '24h'
27
27
  ];
28
28
  export const HARD_FLOOR_CATEGORIES = [
29
29
  'irreversible-external-side-effect',
@@ -56,10 +56,14 @@ export function isHardFloorCategory(value) {
56
56
  /**
57
57
  * `true` when the current mode should auto-proceed (skip the
58
58
  * AskUserQuestion round-trip). Mirrors D5.a: "recommended = chosen
59
- * in full-auto / swarm; always log, never silently skip".
59
+ * in full-auto / 24h; always log, never silently skip".
60
+ *
61
+ * Slice 2026-09-09-mode-consolidation: `swarm` was removed as a mode
62
+ * (parallel fan-out is now the default execution strategy in every
63
+ * mode); `24h` replaces it as the second auto-proceed peer.
60
64
  */
61
65
  export function shouldAutoProceed(mode) {
62
- return mode === 'full-auto' || mode === 'swarm';
66
+ return mode === 'full-auto' || mode === '24h';
63
67
  }
64
68
  export const COMMIT_BOUNDARY_ACTIONS = [
65
69
  'git-push',