peaks-loop 4.0.0-beta.10 → 4.0.0-beta.11

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 (65) hide show
  1. package/CHANGELOG.md +18 -11
  2. package/dist/cli/cli-helpers.d.ts +0 -5
  3. package/dist/cli/cli-helpers.js +0 -24
  4. package/dist/cli/commands/audit-commands.js +1 -1
  5. package/dist/cli/commands/capability-worker-config-sc-commands.js +0 -2
  6. package/dist/cli/commands/code-commands.js +1 -1
  7. package/dist/cli/commands/code-review-commands.js +1 -1
  8. package/dist/cli/commands/config-commands.js +3 -74
  9. package/dist/cli/commands/core/skill-command.js +5 -2
  10. package/dist/cli/commands/dispatch-commands.js +14 -0
  11. package/dist/cli/commands/ecc-commands.d.ts +30 -0
  12. package/dist/cli/commands/ecc-commands.js +119 -0
  13. package/dist/cli/commands/perf-audit-commands.js +1 -1
  14. package/dist/cli/commands/prd-commands.js +3 -1
  15. package/dist/cli/commands/qa-commands.js +1 -1
  16. package/dist/cli/commands/sc-commands.js +3 -1
  17. package/dist/cli/commands/security-audit-commands.js +1 -1
  18. package/dist/cli/commands/skill-search-commands.js +3 -0
  19. package/dist/cli/commands/upgrade-commands.js +1 -1
  20. package/dist/cli/program.js +12 -4
  21. package/dist/services/agent/ecc-cache-service.d.ts +97 -0
  22. package/dist/services/agent/ecc-cache-service.js +491 -0
  23. package/dist/services/audit/static-service.d.ts +32 -25
  24. package/dist/services/audit/static-service.js +30 -109
  25. package/dist/services/config/config-service.d.ts +1 -12
  26. package/dist/services/config/config-service.js +3 -72
  27. package/dist/services/config/config-types.d.ts +2 -4
  28. package/dist/services/context/context-schema.d.ts +2 -2
  29. package/dist/services/job/job-types.d.ts +3 -3
  30. package/dist/services/log/retention.d.ts +16 -0
  31. package/dist/services/log/retention.js +17 -0
  32. package/dist/services/migrate-skill-name/schema.d.ts +2 -2
  33. package/dist/services/rd/rd-service.js +10 -11
  34. package/dist/services/rd/types.d.ts +2 -2
  35. package/dist/services/recommendations/capability-seed-items.js +0 -1
  36. package/dist/services/recommendations/capability-seed-mappings.js +0 -1
  37. package/dist/services/recommendations/capability-seed-sources.js +0 -1
  38. package/dist/services/skill/skill-search-service.d.ts +5 -0
  39. package/dist/services/skill/skill-search-service.js +6 -2
  40. package/dist/services/skills/skill-registry.d.ts +1 -0
  41. package/dist/services/skills/skill-registry.js +4 -2
  42. package/dist/services/workflow/workflow-router-service.js +7 -7
  43. package/dist/shared/version.d.ts +1 -1
  44. package/dist/shared/version.js +1 -1
  45. package/package.json +1 -1
  46. package/skills/bee/peaks-perf-audit/SKILL.md +1 -0
  47. package/skills/bee/peaks-rd/SKILL.md +1 -24
  48. package/skills/bee/peaks-reviewer/SKILL.md +1 -0
  49. package/skills/bee/peaks-security-audit/SKILL.md +1 -0
  50. package/skills/peaks-code/SKILL.md +1 -99
  51. package/skills/peaks-code/references/startup-sequence.md +110 -0
  52. package/skills/peaks-doctor/SKILL.md +1 -1
  53. package/skills/peaks-ide/SKILL.md +1 -1
  54. package/dist/cli/commands/agent-commands.d.ts +0 -20
  55. package/dist/cli/commands/agent-commands.js +0 -48
  56. package/dist/cli/commands/worker-commands.d.ts +0 -3
  57. package/dist/cli/commands/worker-commands.js +0 -52
  58. package/dist/services/agent/ecc-agent-service.d.ts +0 -47
  59. package/dist/services/agent/ecc-agent-service.js +0 -143
  60. package/dist/services/config/provider-service.d.ts +0 -48
  61. package/dist/services/config/provider-service.js +0 -143
  62. package/dist/services/providers/minimax-provider-service.d.ts +0 -24
  63. package/dist/services/providers/minimax-provider-service.js +0 -143
  64. package/dist/services/providers/minimax-worker-service.d.ts +0 -21
  65. package/dist/services/providers/minimax-worker-service.js +0 -83
package/CHANGELOG.md CHANGED
@@ -2,20 +2,27 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
- ## 4.0.0-beta.10 — 2026-07-16
5
+ ## 4.0.0-beta.11 — 2026-07-16
6
6
 
7
- ### Status: PRE-IMPLEMENTATION
7
+ ### Status: RELEASED (release of `4.0.0-beta.10` contract; same source tree)
8
8
 
9
- > **This CHANGELOG entry documents the CONTRACT for 4.0.0-beta.10.
10
- > The 3 slices below are NOT YET IMPLEMENTED in this branch.**
11
- > See `docs/release/4.0.0-beta.10.md` for the user-facing runbook
12
- > and `peaks workflow verify-pipeline --rid 2026-07-15-cli-surface-cleanup`
13
- > for the pre-implementation gate (9/9 gates PASS).
9
+ > **This CHANGELOG entry documents the SHIPPED 4.0.0-beta.11.
10
+ > It is the post-implementation release of the 4.0.0-beta.10 contract
11
+ > documented at `docs/release/4.0.0-beta.10.md` (runbook filename
12
+ > preserved for cross-reference stability).
14
13
  >
15
- > **Ship path**: user implements Slices 1+2+3 per the runbook,
16
- > runs `pnpm test:full` + manual AC verification, then runs
17
- > `npm publish --tag beta`. No code change is included in the
18
- > sediment/version-bump commit that produced this CHANGELOG.
14
+ > The 3 slices (del-minimax-worker, hide-role-skills, on-demand-ecc)
15
+ > all landed PASS in this branch — see `peaks workflow verify-pipeline
16
+ > --rid 2026-07-15-cli-surface-cleanup` and the Slice 1/2/3 sediment
17
+ > memories in `.peaks/memory/`.
18
+ >
19
+ > **Version bump rationale** (D-016): 4.0.0-beta.10 was the pre-impl
20
+ > contract name; 4.0.0-beta.11 is the actual release of that contract.
21
+ > One-line summary: **"CLI surface cleanup + on-demand ECC"**.
22
+ >
23
+ > **Verdict**: 10/12 AC PASS + 2 PASS-WITH-DEFERRED (D-013 wrapper
24
+ > exit-code bug, separate follow-up slice after publish).
25
+ > Ice-cola baseline gate: 27/27 AC functionally pass.
19
26
 
20
27
  ### Removed — `peaks minimax-worker` (Slice 1)
21
28
 
@@ -2,8 +2,6 @@ import { Command } from 'commander';
2
2
  import { getErrorMessage, ok, redactSensitiveErrorMessage, type ResultEnvelope } from '../shared/result.js';
3
3
  import type { ArtifactProvider, GuidedArtifactSetup } from '../services/artifacts/artifact-service.js';
4
4
  import type { ConfigLayer } from '../services/config/config-service.js';
5
- import type { MiniMaxProviderSmokeResult } from '../services/providers/minimax-provider-service.js';
6
- import type { MiniMaxWorkerResult } from '../services/providers/minimax-worker-service.js';
7
5
  import type { RecommendationWorkflow } from '../services/recommendations/recommendation-service.js';
8
6
  export type ProgramIO = {
9
7
  stdout: (text: string) => void;
@@ -34,10 +32,7 @@ export declare function isRecommendationWorkflow(value: string): value is Recomm
34
32
  export declare function isArtifactProvider(value: string): value is ArtifactProvider;
35
33
  export declare function isArtifactSetupStep(value: string): value is GuidedArtifactSetup['step'];
36
34
  export declare function isArtifactRepoSegment(value: string): boolean;
37
- export declare function isMiniMaxHttpsUrl(value: string): boolean;
38
35
  export declare function parseConfigLayer(value: string | undefined): ConfigLayer | undefined | null;
39
36
  export declare function printInvalidConfigLayer(io: ProgramIO, command: string, asJson?: boolean): void;
40
37
  export declare function multipleOption(value: string, previous: string[]): string[];
41
- export declare function summarizeMiniMaxSmokeResult(result: MiniMaxProviderSmokeResult): MiniMaxProviderSmokeResult;
42
- export declare function summarizeMiniMaxWorkerResult(result: MiniMaxWorkerResult): MiniMaxWorkerResult;
43
38
  export { getErrorMessage, ok, redactSensitiveErrorMessage };
@@ -1,5 +1,4 @@
1
1
  import { fail, getErrorMessage, ok, redactSensitiveErrorMessage } from '../shared/result.js';
2
- const MINIMAX_API_HOST = 'api.minimaxi.com';
3
2
  export function printResult(io, result, asJson = false) {
4
3
  if (asJson) {
5
4
  io.stdout(JSON.stringify(result, null, 2));
@@ -47,15 +46,6 @@ export function isArtifactSetupStep(value) {
47
46
  export function isArtifactRepoSegment(value) {
48
47
  return /^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(value) && !value.includes('..') && !value.endsWith('.');
49
48
  }
50
- export function isMiniMaxHttpsUrl(value) {
51
- try {
52
- const url = new URL(value);
53
- return url.protocol === 'https:' && url.hostname === MINIMAX_API_HOST && url.username.length === 0 && url.password.length === 0 && url.search.length === 0 && url.hash.length === 0;
54
- }
55
- catch {
56
- return false;
57
- }
58
- }
59
49
  export function parseConfigLayer(value) {
60
50
  if (value === undefined) {
61
51
  return undefined;
@@ -69,18 +59,4 @@ export function printInvalidConfigLayer(io, command, asJson) {
69
59
  export function multipleOption(value, previous) {
70
60
  return [...(previous || []), value];
71
61
  }
72
- export function summarizeMiniMaxSmokeResult(result) {
73
- return { ...result, responseText: null, summary: null };
74
- }
75
- export function summarizeMiniMaxWorkerResult(result) {
76
- const provider = summarizeMiniMaxSmokeResult(result.provider);
77
- return {
78
- ...result,
79
- provider,
80
- reviewHandoff: {
81
- model: result.reviewHandoff.model,
82
- prompt: '[redacted]'
83
- }
84
- };
85
- }
86
62
  export { getErrorMessage, ok, redactSensitiveErrorMessage };
@@ -49,7 +49,7 @@ function validateProjectRoot(projectArg) {
49
49
  }
50
50
  export function registerAuditCommands(program, io) {
51
51
  const audit = program
52
- .command('audit')
52
+ .command('audit', { hidden: true })
53
53
  .description('Audit a project for compliance with peaks-loop red lines (P0 / P1 / P2 tiers)');
54
54
  addJsonOption(audit
55
55
  .command('red-lines')
@@ -1,10 +1,8 @@
1
1
  import { registerCapabilityCommands } from './capability-commands.js';
2
2
  import { registerConfigCommands } from './config-commands.js';
3
3
  import { registerSCCommands } from './sc-commands.js';
4
- import { registerWorkerCommands } from './worker-commands.js';
5
4
  export function registerCapabilityWorkerConfigAndSCCommands(program, io) {
6
5
  registerCapabilityCommands(program, io);
7
- registerWorkerCommands(program, io);
8
6
  registerConfigCommands(program, io);
9
7
  registerSCCommands(program, io);
10
8
  }
@@ -108,7 +108,7 @@ export async function runCodeFast(opts) {
108
108
  */
109
109
  export function registerCodeCommands(program, io) {
110
110
  const code = program
111
- .command('code')
111
+ .command('code', { hidden: true })
112
112
  .description('peaks-code LLM-side workflow planner (slice 2 fast mode)');
113
113
  code
114
114
  .command('plan')
@@ -4,7 +4,7 @@ import { addJsonOption, getErrorMessage, printResult } from '../cli-helpers.js';
4
4
  import { fail, ok } from '../../shared/result.js';
5
5
  export function registerCodeReviewCommands(program, io) {
6
6
  const codeReview = program
7
- .command('code-review')
7
+ .command('code-review', { hidden: true })
8
8
  .description('Code-review primitives for peaks-rd Gate B3. Wraps the soft-optional `@alibaba-group/open-code-review` (ocr) tool when it is installed + configured; peaks-rd uses the structured JSON output as a second-opinion review alongside its own LLM review. ocr is a peerDependency of peaks-loop 2.8.2+ (was briefly promoted to a hard dependency in 2.0.1/2.0.2, then demoted to optionalDependencies 2.0.3+ — peer in 2.8.2 because its postinstall downloads a Go binary via HTTPS and would otherwise slow `npm i -g peaks-loop` in restricted environments). Install manually with `npm i -g @alibaba-group/open-code-review` if you want second-opinion reviews. LLM endpoint config lives under `peaksConfig.ocr.llm` in the user config — run `peaks code-review config-template` to see the JSON snippet to paste.');
9
9
  addJsonOption(codeReview
10
10
  .command('detect-ocr')
@@ -1,15 +1,13 @@
1
1
  import { executeMigration, planMigration } from '../../services/config/config-migration.js';
2
- import { getConfig, getMiniMaxProviderConfig, getMiniMaxProviderStatus, isSensitiveConfigPath, redactConfigSecrets, setConfig, setMiniMaxProviderConfig } from '../../services/config/config-service.js';
2
+ import { getConfig, isSensitiveConfigPath, redactConfigSecrets, setConfig } from '../../services/config/config-service.js';
3
3
  import { listAvailableFields, restoreField } from '../../services/config/config-restore.js';
4
4
  import { executeRollback, planRollback } from '../../services/config/config-rollback.js';
5
- import { testMiniMaxProvider } from '../../services/providers/minimax-provider-service.js';
6
5
  import { fail, ok } from '../../shared/result.js';
7
- import { addJsonOption, getErrorMessage, isMiniMaxHttpsUrl, parseConfigLayer, printInvalidConfigLayer, printResult, redactSensitiveErrorMessage, summarizeMiniMaxSmokeResult } from '../cli-helpers.js';
6
+ import { addJsonOption, getErrorMessage, parseConfigLayer, printInvalidConfigLayer, printResult } from '../cli-helpers.js';
8
7
  export function registerConfigCommands(program, io) {
9
8
  const config = program.command('config').description('Manage Peaks configuration');
10
9
  registerConfigGetSetCommands(config, io);
11
10
  registerConfigMigrationCommands(config, io);
12
- registerMiniMaxProviderCommands(config, io);
13
11
  }
14
12
  function registerConfigGetSetCommands(config, io) {
15
13
  addJsonOption(config.command('get').description('Get current config or a specific key').option('--key <path>', 'dot-notation key path').option('--layer <layer>', 'user or project')).action((options) => {
@@ -137,7 +135,7 @@ function registerConfigMigrationCommands(config, io) {
137
135
  function printConfigSetError(io, error, asJson) {
138
136
  const message = getErrorMessage(error);
139
137
  if (message === 'Sensitive config keys must be stored in the user config layer') {
140
- printResult(io, fail('config.set', 'SECRET_CONFIG_REQUIRES_USER_LAYER', message, {}, ['Use --layer user or peaks config provider minimax set']), asJson);
138
+ printResult(io, fail('config.set', 'SECRET_CONFIG_REQUIRES_USER_LAYER', message, {}, ['Use --layer user for sensitive config keys']), asJson);
141
139
  process.exitCode = 1;
142
140
  return;
143
141
  }
@@ -146,75 +144,6 @@ function printConfigSetError(io, error, asJson) {
146
144
  process.exitCode = 1;
147
145
  return;
148
146
  }
149
- if (message === 'MiniMax base URL must be the MiniMax HTTPS endpoint without embedded credentials') {
150
- printResult(io, fail('config.set', 'INVALID_MINIMAX_BASE_URL', message, {}, ['Use a MiniMax Anthropic-compatible HTTPS endpoint']), asJson);
151
- process.exitCode = 1;
152
- return;
153
- }
154
147
  printResult(io, fail('config.set', 'CONFIG_SET_FAILED', message, {}, ['Check the config key and layer, then retry']), asJson);
155
148
  process.exitCode = 1;
156
149
  }
157
- function registerMiniMaxProviderCommands(config, io) {
158
- const minimaxProvider = config.command('provider').description('Manage model provider settings').command('minimax').description('Manage MiniMax provider settings');
159
- addJsonOption(minimaxProvider.command('set').description('Set MiniMax provider settings in user config').option('--base-url <url>', 'MiniMax Anthropic-compatible base URL')).action((options) => {
160
- const baseUrl = options.baseUrl?.trim();
161
- const apiKey = process.env.MINIMAX_API_KEY?.trim();
162
- if (!baseUrl && !apiKey) {
163
- printResult(io, fail('config.provider.minimax.set', 'MINIMAX_PROVIDER_NO_VALUES', 'Provide --base-url and set MINIMAX_API_KEY', {}, ['Export MINIMAX_API_KEY and rerun peaks config provider minimax set --base-url <url>']), options.json);
164
- process.exitCode = 1;
165
- return;
166
- }
167
- if (baseUrl && !isMiniMaxHttpsUrl(baseUrl)) {
168
- printResult(io, fail('config.provider.minimax.set', 'INVALID_MINIMAX_BASE_URL', 'MiniMax base URL must be the MiniMax HTTPS endpoint without embedded credentials', {}, ['Use a MiniMax Anthropic-compatible HTTPS endpoint']), options.json);
169
- process.exitCode = 1;
170
- return;
171
- }
172
- try {
173
- const input = {};
174
- if (baseUrl)
175
- input.baseUrl = baseUrl;
176
- if (apiKey)
177
- input.apiKey = apiKey;
178
- const status = setMiniMaxProviderConfig(input);
179
- printResult(io, ok('config.provider.minimax.set', status), options.json);
180
- }
181
- catch (error) {
182
- printMiniMaxProviderSetError(io, error, options.json);
183
- }
184
- });
185
- addJsonOption(minimaxProvider.command('get').description('Show redacted MiniMax provider settings')).action((options) => {
186
- const configValue = getMiniMaxProviderConfig();
187
- const status = getMiniMaxProviderStatus();
188
- printResult(io, ok('config.provider.minimax.get', { ...redactConfigSecrets(configValue, 'providers.minimax'), ...status }), options.json);
189
- });
190
- addJsonOption(minimaxProvider.command('status').description('Show MiniMax provider configuration status')).action((options) => {
191
- printResult(io, ok('config.provider.minimax.status', getMiniMaxProviderStatus()), options.json);
192
- });
193
- addJsonOption(minimaxProvider.command('test').description('Run a redacted MiniMax provider smoke test').option('--model <model>', 'model name for the smoke test', 'MiniMax-M2.7')).action(async (options) => {
194
- try {
195
- const result = await testMiniMaxProvider(getMiniMaxProviderConfig(), { model: options.model });
196
- const safeResult = summarizeMiniMaxSmokeResult(result);
197
- if (!result.configured) {
198
- printResult(io, fail('config.provider.minimax.test', 'MINIMAX_PROVIDER_NOT_CONFIGURED', 'MiniMax provider requires baseUrl and apiKey in user config', safeResult, ['Export MINIMAX_API_KEY or set MINIMAX_API_KEY']), options.json);
199
- process.exitCode = 1;
200
- return;
201
- }
202
- printResult(io, result.ok ? ok('config.provider.minimax.test', safeResult) : fail('config.provider.minimax.test', 'MINIMAX_PROVIDER_TEST_FAILED', 'MiniMax provider smoke test failed', safeResult, ['Check the MiniMax base URL, API key, and model name']), options.json);
203
- if (!result.ok)
204
- process.exitCode = 1;
205
- }
206
- catch (error) {
207
- printResult(io, fail('config.provider.minimax.test', 'MINIMAX_PROVIDER_TEST_FAILED', redactSensitiveErrorMessage(getErrorMessage(error)), {}, ['Check network connectivity and MiniMax provider settings']), options.json);
208
- process.exitCode = 1;
209
- }
210
- });
211
- }
212
- function printMiniMaxProviderSetError(io, error, asJson) {
213
- if (getErrorMessage(error) === 'MiniMax base URL must be the MiniMax HTTPS endpoint without embedded credentials') {
214
- printResult(io, fail('config.provider.minimax.set', 'INVALID_MINIMAX_BASE_URL', 'MiniMax base URL must be the MiniMax HTTPS endpoint without embedded credentials', {}, ['Use a MiniMax Anthropic-compatible HTTPS endpoint']), asJson);
215
- process.exitCode = 1;
216
- return;
217
- }
218
- printResult(io, fail('config.provider.minimax.set', 'MINIMAX_PROVIDER_SET_FAILED', getErrorMessage(error), {}, ['Check MiniMax provider settings and retry']), asJson);
219
- process.exitCode = 1;
220
- }
@@ -20,8 +20,11 @@ import { addJsonOption, getErrorMessage, printResult } from '../../cli-helpers.j
20
20
  import { registerSkillSearchCommand } from '../skill-search-commands.js';
21
21
  export function registerSkillCommand(program, io) {
22
22
  const skill = program.command('skill').description('Manage Peaks skills');
23
- addJsonOption(skill.command('list').description('List skills derived from skills/*/SKILL.md')).action(async (options) => {
24
- const skills = await listSkills();
23
+ addJsonOption(skill.command('list').description('List skills derived from skills/*/SKILL.md').option('--include-internal', 'include skills with visibility: internal (default: hide them)')).action(async (options) => {
24
+ let skills = await listSkills();
25
+ if (options.includeInternal !== true) {
26
+ skills = skills.filter((s) => s.visibility !== 'internal');
27
+ }
25
28
  if (options.json === true) {
26
29
  printResult(io, ok('skill.list', { skills }), true);
27
30
  }
@@ -78,6 +78,20 @@ export function registerDispatchCommand(parent, io) {
78
78
  process.exitCode = 1;
79
79
  return;
80
80
  }
81
+ // Slice 3 (on-demand-ecc) D-012: the `agent` role was removed in
82
+ // 4.0.0-beta.11 — there is no longer a subprocess path for it
83
+ // (the upstream ECC v2.0.0 ships no `ecc` binary). This guard
84
+ // sits AFTER role validation but BEFORE the missing-prompt
85
+ // check so an action-path dispatch with a valid prompt still
86
+ // returns a clear ROLE_REMOVED envelope + exit 1. Note that
87
+ // Commander short-circuits `--help` BEFORE `.action()` fires,
88
+ // so `peaks sub-agent dispatch agent --help` continues to
89
+ // exit 0 with the help text — that is intentional, not a bug.
90
+ if (role === 'agent') {
91
+ printResult(io, fail('sub-agent.dispatch', 'ROLE_REMOVED', 'The agent role was removed in Slice 3', { role, reason: 'role-removed-in-slice-3', toolCall: null, dispatchRecordPath: null }, []), asJson);
92
+ process.exitCode = 1;
93
+ return;
94
+ }
81
95
  if (!options.prompt || options.prompt.length === 0) {
82
96
  printResult(io, fail('sub-agent.dispatch', 'MISSING_PROMPT', '--prompt is required when --from-dag is not provided', { role, toolCall: null, dispatchRecordPath: null }, [
83
97
  'Re-run with either:',
@@ -0,0 +1,30 @@
1
+ /**
2
+ * `peaks ecc install|status|ls|show` — Slice 3 of 4.0.0-beta.11.
3
+ *
4
+ * Drop-in replacement for the deleted `peaks agent run|list`
5
+ * surface. The pre-Slice-3 implementation shelled out to
6
+ * `npx ecc agent run ...`, but the upstream `affaan-m/everything-claude-code`
7
+ * v2.0.0 release has NO `ecc` binary — the repo is `agents/*.md`
8
+ * flat files plus SKILL.md descriptors.
9
+ *
10
+ * The new flow:
11
+ * - `peaks ecc install [--ref <tag>]` downloads the ECC tarball
12
+ * into `~/.peaks/cache/ecc-<sha>/agents/` (selective extract,
13
+ * agents/*.md only).
14
+ * - `peaks ecc status` reports the cache manifest state.
15
+ * - `peaks ecc ls` lists cached agents from `<cache>/agents/*.md`,
16
+ * parsing YAML frontmatter (with D-009 fallback to filename +
17
+ * first body line when frontmatter is malformed).
18
+ * - `peaks ecc show <name> [--section H] [--max-lines N]` prints
19
+ * the SKILL.md body to stdout — this is the Skill-first path
20
+ * the LLM consumes directly.
21
+ *
22
+ * The `--section` filter extracts a single H1 (`# heading`)
23
+ * through the next H1 — useful when the SKILL.md is large.
24
+ *
25
+ * Per the "Enhancement, not new AI CLI" tenet: this command is a
26
+ * download + read-only access layer. There is no `peaks ecc run`.
27
+ */
28
+ import { Command } from 'commander';
29
+ import { type ProgramIO } from '../cli-helpers.js';
30
+ export declare function registerEccCommands(program: Command, io: ProgramIO): void;
@@ -0,0 +1,119 @@
1
+ import { downloadToCache, listCachedAgents, readAgentSkill, readCacheManifest, } from '../../services/agent/ecc-cache-service.js';
2
+ import { addJsonOption, getErrorMessage, printResult } from '../cli-helpers.js';
3
+ import { fail, ok } from '../../shared/result.js';
4
+ export function registerEccCommands(program, io) {
5
+ const ecc = program
6
+ .command('ecc')
7
+ .description('affaan-m/everything-claude-code cache: download + read-only access for the LLM (no subprocess; no peaks agent run).');
8
+ addJsonOption(ecc
9
+ .command('install')
10
+ .description('Download affaan-m/ECC to ~/.peaks/cache/ecc-<sha>/ (selective extract: agents/ subtree only).')
11
+ .option('--ref <tag>', 'release tag (default: latest)')).action(async (options) => {
12
+ const asJson = options.json === true;
13
+ try {
14
+ const result = await downloadToCache(options.ref !== undefined && options.ref.length > 0 ? { ref: options.ref } : {});
15
+ const envelope = ok('ecc.install', result, [], [
16
+ `Cache landed at ~/.peaks/cache/ecc-${result.sha}/agents/`,
17
+ `Inspect with: peaks ecc ls`,
18
+ `Consume one agent with: peaks ecc show <name>`,
19
+ ]);
20
+ printResult(io, envelope, asJson);
21
+ }
22
+ catch (error) {
23
+ const message = getErrorMessage(error);
24
+ printResult(io, fail('ecc.install', 'FETCH_FAILED', message, { ref: options.ref ?? 'latest' }, [
25
+ 'Network failure during ECC download. Manual fallback:',
26
+ ' git clone https://github.com/affaan-m/everything-claude-code.git',
27
+ ' Copy <repo>/agents/*.md into ~/.peaks/cache/ecc-<sha>/agents/.',
28
+ ' Drop a minimal ecc-installed.json manifest into ~/.peaks/cache/.',
29
+ ]), asJson);
30
+ process.exitCode = 1;
31
+ }
32
+ });
33
+ addJsonOption(ecc
34
+ .command('status')
35
+ .description('Show ECC cache state (version, sha, fetchedAt, agent count).')).action((options) => {
36
+ const asJson = options.json === true;
37
+ const manifest = readCacheManifest();
38
+ if (manifest === null) {
39
+ printResult(io, fail('ecc.status', 'NO_CACHE', 'No ECC cache found. Run `peaks ecc install` first.', { installed: false }, ['Run `peaks ecc install [--ref <tag>]` to populate ~/.peaks/cache/ecc-<sha>/agents/.']), asJson);
40
+ process.exitCode = 1;
41
+ return;
42
+ }
43
+ printResult(io, ok('ecc.status', manifest, [], [
44
+ `Inspect agents with: peaks ecc ls`,
45
+ `Print one agent with: peaks ecc show <name>`,
46
+ ]), asJson);
47
+ });
48
+ addJsonOption(ecc
49
+ .command('ls')
50
+ .description('List cached agents from <cache>/agents/*.md with parsed frontmatter.')).action((options) => {
51
+ const asJson = options.json === true;
52
+ const agents = listCachedAgents();
53
+ printResult(io, ok('ecc.ls', { agents }, [], [
54
+ 'Print one with: `peaks ecc show <name>`',
55
+ ]), asJson);
56
+ });
57
+ addJsonOption(ecc
58
+ .command('show <name>')
59
+ .description('Print agent SKILL.md to stdout (LLM-consumable; Skill-first path).')
60
+ .option('--section <heading>', 'extract only the named H1 section (# <heading> through next # )')
61
+ .option('--max-lines <n>', 'cap stdout at N lines (default: unlimited)')).action((name, options) => {
62
+ const asJson = options.json === true;
63
+ if (!/^[a-z][a-z0-9-]*$/.test(name)) {
64
+ printResult(io, fail('ecc.show', 'INVALID_NAME', `agent name must match ^[a-z][a-z0-9-]*$ (got "${name}")`, { name }, [
65
+ 'Run `peaks ecc ls` to see valid agent names.',
66
+ ]), asJson);
67
+ process.exitCode = 1;
68
+ return;
69
+ }
70
+ const body = readAgentSkill(name);
71
+ if (body === null) {
72
+ printResult(io, fail('ecc.show', 'NOT_FOUND', `agent "${name}" is not in the cache`, { name }, [
73
+ 'Run `peaks ecc ls` to see available agents.',
74
+ 'Or run `peaks ecc install` to (re-)populate the cache.',
75
+ ]), asJson);
76
+ process.exitCode = 1;
77
+ return;
78
+ }
79
+ let filtered = body;
80
+ if (typeof options.section === 'string' && options.section.length > 0) {
81
+ const heading = options.section.trim();
82
+ const lines = body.split(/\r?\n/);
83
+ const startIdx = lines.findIndex((line) => new RegExp(`^#\\s+${escapeRegExp(heading)}\\s*$`).test(line));
84
+ if (startIdx === -1) {
85
+ printResult(io, fail('ecc.show', 'SECTION_NOT_FOUND', `section "# ${heading}" not found in ${name}.md`, { name, section: heading }, [
86
+ 'Open the file directly to see section names.',
87
+ ]), asJson);
88
+ process.exitCode = 1;
89
+ return;
90
+ }
91
+ let endIdx = lines.length;
92
+ for (let i = startIdx + 1; i < lines.length; i += 1) {
93
+ const line = lines[i] ?? '';
94
+ if (/^#\s+/.test(line)) {
95
+ endIdx = i;
96
+ break;
97
+ }
98
+ }
99
+ filtered = lines.slice(startIdx, endIdx).join('\n');
100
+ }
101
+ if (typeof options.maxLines === 'string' && options.maxLines.length > 0) {
102
+ const cap = Number.parseInt(options.maxLines, 10);
103
+ if (Number.isInteger(cap) && cap > 0) {
104
+ const lines = filtered.split(/\r?\n/);
105
+ if (lines.length > cap)
106
+ filtered = lines.slice(0, cap).join('\n');
107
+ }
108
+ }
109
+ if (asJson) {
110
+ printResult(io, ok('ecc.show', { name, body: filtered }, [], []), asJson);
111
+ return;
112
+ }
113
+ // Human-readable path: raw SKILL.md body to stdout (LLM-consumable).
114
+ io.stdout(filtered);
115
+ });
116
+ }
117
+ function escapeRegExp(value) {
118
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
119
+ }
@@ -36,7 +36,7 @@ function readStdinSync() {
36
36
  }
37
37
  export function registerPerfAuditCommands(program, io) {
38
38
  const perfAudit = program
39
- .command('perf-audit')
39
+ .command('perf-audit', { hidden: true })
40
40
  .description('Independent performance audit skill driver. Reads the immutable peaks-prd handoff (sha256-locked) + the project-level perf-template.md, then writes a structured perf-<rid>.md artifact. Decoupled from peaks-rd 5-way fan-out per slice v2.12.0 (Group A, Tier 3). See skills/bee/peaks-perf-audit/SKILL.md for the full workflow.');
41
41
  addJsonOption(perfAudit
42
42
  .command('detect')
@@ -37,7 +37,9 @@ async function resolveBody(bodyArg) {
37
37
  return bodyArg;
38
38
  }
39
39
  export function registerPrdCommands(program, io) {
40
- const prd = program.command('prd').description('peaks-prd role: artifact + handoff primitives');
40
+ const prd = program
41
+ .command('prd', { hidden: true })
42
+ .description('peaks-prd role: artifact + handoff primitives');
41
43
  const handoff = prd.command('handoff').description('Write / verify / show the immutable PRD handoff (sha256-locked, schemaVersion: 2)');
42
44
  // peaks prd handoff init
43
45
  addJsonOption(handoff
@@ -200,7 +200,7 @@ export function readQaRunOptions(options) {
200
200
  }
201
201
  export function registerQaCommands(program, io) {
202
202
  const qa = program
203
- .command('qa')
203
+ .command('qa', { hidden: true })
204
204
  .description('peaks-qa slice: run QA gates (functional / security / browser E2E / mutation) for the active project');
205
205
  addJsonOption(qa
206
206
  .command('run')
@@ -2,7 +2,9 @@ import { createArtifactRetentionReport, createChangeImpact, getChangeTraceabilit
2
2
  import { ok } from '../../shared/result.js';
3
3
  import { addJsonOption, multipleOption, printResult } from '../cli-helpers.js';
4
4
  export function registerSCCommands(program, io) {
5
- const sc = program.command('sc').description('Source control and change traceability (peaks-sc integration)');
5
+ const sc = program
6
+ .command('sc', { hidden: true })
7
+ .description('Source control and change traceability (peaks-sc integration)');
6
8
  registerSCStatusCommands(sc, io);
7
9
  registerSCArtifactCommands(sc, io);
8
10
  }
@@ -36,7 +36,7 @@ function readStdinSync() {
36
36
  }
37
37
  export function registerSecurityAuditCommands(program, io) {
38
38
  const securityAudit = program
39
- .command('security-audit')
39
+ .command('security-audit', { hidden: true })
40
40
  .description('Independent security audit skill driver. Reads the immutable peaks-prd handoff (sha256-locked) + the project-level security-template.md, then writes a structured security-<rid>.md artifact. Decoupled from peaks-rd 5-way fan-out per slice v2.12.0 (Group A, Tier 2). See skills/bee/peaks-security-audit/SKILL.md for the full workflow.');
41
41
  addJsonOption(securityAudit
42
42
  .command('detect')
@@ -28,6 +28,7 @@ export function registerSkillSearchCommand(program, io) {
28
28
  .option('-t, --tag <tag>', 'exact match on metadata.tags (e.g. "orchestrator", "loop-engineering")')
29
29
  .option('-d, --domain <domain>', 'exact match on metadata.domain (locked enum: code | content | doctor | research | triage | sop | audit | final-review | resume | status | test | ide | slice-decompose | issue-fix-orchestrator | perf-audit | security-audit | reviewer)')
30
30
  .option('-l, --limit <n>', 'max results returned (1..100, default 20)', (v) => Number.parseInt(v, 10))
31
+ .option('--include-internal', 'include skills with visibility: internal (default: hide them)')
31
32
  .action(async (options) => {
32
33
  // 1) Coerce + validate input via the service's Zod schema.
33
34
  // The schema's refine rejects empty input — that is the
@@ -41,6 +42,8 @@ export function registerSkillSearchCommand(program, io) {
41
42
  raw['domain'] = options.domain;
42
43
  if (options.limit !== undefined)
43
44
  raw['limit'] = options.limit;
45
+ if (options.includeInternal === true)
46
+ raw['includeInternal'] = true;
44
47
  const parsed = SkillSearchInputSchema.safeParse(raw);
45
48
  if (!parsed.success) {
46
49
  // Exit code 1 = invalid args. Emit a human-readable message
@@ -7,7 +7,7 @@ import { addJsonOption, getErrorMessage, printResult } from '../cli-helpers.js';
7
7
  import { fail, ok } from '../../shared/result.js';
8
8
  export function registerUpgradeCommands(program, io) {
9
9
  addJsonOption(program
10
- .command('upgrade')
10
+ .command('upgrade', { hidden: true })
11
11
  .description('Upgrade a peaks-loop 1.x project to 2.0. Umbrella that orquestrates 7 sub-commands: config-migrate / standards-migrate / memory-extract / hooks-install / skill-sync / audit-verify + write-upgrade-record. Per the "one-key completion" tenet, prefer letting `npm i -g peaks-loop@2.0` postinstall run this for you. Use `--detect-1x` for a read-only probe (no file writes) that the peaks-code skill uses to gate the 1.x → 2.0 AskUserQuestion.')
12
12
  .option('--to <version>', 'target version (only "2.0" supported)', '2.0')
13
13
  .option('--project <path>', 'project root to upgrade (default: cwd)')
@@ -58,7 +58,7 @@ import { registerWorkflowEvalCommands } from './commands/loop-eval-commands.js';
58
58
  import { registerEvolutionCommands } from './commands/evolution-commands.js';
59
59
  import { registerAssetCommands } from './commands/asset-commands.js';
60
60
  import { registerBeeCommands } from './commands/bee-commands.js';
61
- import { registerAgentCommands } from './commands/agent-commands.js';
61
+ import { registerEccCommands } from './commands/ecc-commands.js';
62
62
  import { registerUpgradeCommands } from './commands/upgrade-commands.js';
63
63
  import { registerCodeReviewCommands } from './commands/code-review-commands.js';
64
64
  import { registerSecurityAuditCommands } from './commands/security-audit-commands.js';
@@ -99,7 +99,7 @@ import { registerAdapterS2ACommands } from './commands/adapter-commands-s2a.js';
99
99
  // Slice RD-2 S2-b: `peaks polyrepo *` (init / status / dispatch).
100
100
  import { registerPolyrepoCommands } from './commands/polyrepo-commands.js';
101
101
  import { registerSkillVisibilityCommand } from './commands/skill-visibility.js';
102
- import { applyRetention } from '../services/log/retention.js';
102
+ import { applyRetention, cleanupEccCache } from '../services/log/retention.js';
103
103
  import { writeLogEntry, maybeWriteStderr } from '../services/log/logger.js';
104
104
  export { printResult } from './cli-helpers.js';
105
105
  /**
@@ -120,6 +120,13 @@ function bootstrapLogger(verbose) {
120
120
  catch { // TODO(g2): legacy silent catch — grace: 1 minor release (v2.14.0)
121
121
  /* best-effort retention sweep; never block the CLI */
122
122
  }
123
+ // Slice 3 (on-demand-ecc): 7-day TTL sweep over ecc-<sha>/ cache dirs.
124
+ try {
125
+ cleanupEccCache({ retentionDays: 7, nowMs: Date.now() });
126
+ }
127
+ catch { // TODO(g2): legacy silent catch — grace: 1 minor release (v2.14.0)
128
+ /* best-effort ECC retention sweep; never block the CLI */
129
+ }
123
130
  const dateOverride = process.env.PEAKS_LOG_DATE_OVERRIDE;
124
131
  const entry = {
125
132
  ts: new Date().toISOString(),
@@ -329,8 +336,9 @@ Run peaks (no arguments) for a quickstart. You likely want one of:
329
336
  // M7 (2026-07-07 spec §7A.2 / §10 RL-9): `peaks bee export|import`
330
337
  // — cross-user share surface for bee_release rows.
331
338
  registerBeeCommands(program, io);
332
- // Slice: ECC 64 agents soft-optional (per spec §7.2 line 818).
333
- registerAgentCommands(program, io);
339
+ // Slice 3 (on-demand-ecc): `peaks ecc install|status|ls|show` replaces the
340
+ // deleted `peaks agent run|list` subprocess surface.
341
+ registerEccCommands(program, io);
334
342
  // Slice: 1.x → 2.0 umbrella (per "one-key completion" + "minimal-user-operation" tenets).
335
343
  registerUpgradeCommands(program, io);
336
344
  // Slice: ocr soft-optional integration (peaks-rd Gate B3 augmentation).