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
@@ -1,130 +1,51 @@
1
1
  /**
2
2
  * Static audit service — `peaks audit static`.
3
3
  *
4
- * Slice #6 L2.3 P2-a: soft-optional ECC AgentShield integration.
4
+ * Slice 3 of 4.0.0-beta.11 dropped the ECC AgentShield subprocess
5
+ * integration entirely. The pre-Slice-3 implementation probed
6
+ * `npx ecc-agentshield --version` and shelled out to
7
+ * `npx ecc-agentshield scan --json --target <path>` on every audit
8
+ * call, but the upstream `affaan-m/everything-claude-code` v2.0.0
9
+ * release ships no `ecc-agentshield` binary — the repo is
10
+ * `agents/*.md` flat files plus SKILL.md descriptors.
5
11
  *
6
- * Per spec §5.3, the audit framework integrates with ECC AgentShield
7
- * (102 lint rules) on a soft-optional basis. When ECC is installed
8
- * AND `agentShieldEnabled: true`, the audit subprocess spawns the
9
- * ECC scanner and merges its findings; when either is missing, the
10
- * audit completes with peaks-loop-only findings.
12
+ * The new contract: `peaks audit static` runs the peaks-loop lint
13
+ * layer only. There is no subprocess probe, no `installed` flag,
14
+ * no agent-shield reason code. The `agentShield` field is preserved
15
+ * on the result type as a frozen "always disabled" stub so any
16
+ * downstream consumer keeps compiling, but the merged findings list
17
+ * is always the peaks-loop findings list (no ECC findings).
11
18
  *
12
- * The subprocess is **observability enhancement**, not a structural
13
- * gate — every soft-fail path returns a well-formed audit report.
19
+ * The subprocess-runner seam (`SubprocessRunner`, `defaultSubprocessRunner`)
20
+ * is preserved as an EXPORTED TYPE so legacy tests can still
21
+ * mock it; the runner itself is never invoked from production code.
14
22
  */
15
- import { spawnSync } from 'node:child_process';
16
23
  import { runRedLinesAudit } from './red-lines-service.js';
17
24
  import { loadPreferences } from '../preferences/preferences-service.js';
18
25
  import { computeProseRatio } from './prose-ratio-calculator.js';
19
- const ECC_DETECT_TIMEOUT_MS = 5000;
20
- const ECC_SCAN_TIMEOUT_MS = 30000;
21
- const defaultSubprocessRunner = {
22
- run(command, args, timeoutMs) {
23
- try {
24
- const r = spawnSync(command, args, {
25
- timeout: timeoutMs,
26
- encoding: 'utf8',
27
- stdio: ['ignore', 'pipe', 'pipe'],
28
- maxBuffer: 16 * 1024 * 1024,
29
- });
30
- return {
31
- status: r.status,
32
- stdout: r.stdout ?? '',
33
- stderr: r.stderr ?? '',
34
- };
35
- }
36
- catch (err) {
37
- return {
38
- status: null,
39
- stdout: '',
40
- stderr: '',
41
- error: err,
42
- };
43
- }
44
- },
45
- };
46
- function isEccInstalled(runner) {
47
- const result = runner.run('npx', ['ecc-agentshield', '--version'], ECC_DETECT_TIMEOUT_MS);
48
- if (result.error)
49
- return false;
50
- return result.status === 0;
51
- }
52
- function runEccScan(projectRoot, runner) {
53
- try {
54
- // Per spec §7.2 line 828: the canonical ECC subprocess call uses
55
- // `--target <path>`, NOT `--project`. The peaks-loop CLI flag
56
- // (`peaks audit static --project <path>`) follows peaks-loop
57
- // convention; the inner ecc-agentshield call follows the
58
- // upstream ECC contract.
59
- const result = runner.run('npx', ['ecc-agentshield', 'scan', '--json', '--target', projectRoot], ECC_SCAN_TIMEOUT_MS);
60
- if (result.error || result.status !== 0)
61
- return [];
62
- const stdout = result.stdout;
63
- let parsed;
64
- try {
65
- parsed = JSON.parse(stdout);
66
- }
67
- catch {
68
- return [];
69
- }
70
- if (!parsed.findings)
71
- return [];
72
- return parsed.findings.map((f, idx) => ({
73
- enforcerId: `ecc-agentshield:${f.ruleId ?? `unknown-${idx}`}`,
74
- rule: f.rule ?? 'ECC AgentShield rule',
75
- severity: f.severity ?? 'warn',
76
- file: f.file ?? '(unknown)',
77
- detail: f.detail ?? '',
78
- }));
79
- }
80
- catch {
81
- return [];
82
- }
83
- }
84
26
  export function runStaticAudit(input) {
85
- // Read the project-local preferences; the CLI flag overrides.
86
- const prefs = loadPreferences(input.projectRoot);
87
- const prefEnabled = prefs.agentShieldEnabled;
88
- const flagEnabled = input.enableAgentShield;
89
- const runner = input.subprocessRunner ?? defaultSubprocessRunner;
90
- // Resolve the effective "should spawn" decision.
91
- // flagEnabled (CLI override) > preference > false
92
- const shouldSpawn = flagEnabled ?? prefEnabled;
93
- // Detect ECC.
94
- const installed = isEccInstalled(runner);
95
- const warnings = [];
96
- let state;
97
- if (!shouldSpawn) {
98
- // Disabled path — no subprocess, no warnings.
99
- const reason = flagEnabled === false
100
- ? 'flag-disabled'
101
- : 'disabled-by-preference';
102
- state = { spawned: false, installed, reason, findings: [] };
103
- }
104
- else if (!installed) {
105
- // Enabled but ECC missing — soft-fail with a warning.
106
- warnings.push('agentShieldEnabled is true but `npx ecc-agentshield --version` failed. ' +
107
- 'Run `npx ecc-agentshield --help` to install. Audit ran with peaks-loop findings only.');
108
- state = { spawned: false, installed: false, reason: 'flag-enabled-but-ecc-missing', findings: [] };
109
- }
110
- else {
111
- // Enabled and installed — spawn the subprocess.
112
- const findings = runEccScan(input.projectRoot, runner);
113
- state = { spawned: true, installed: true, reason: 'enabled-and-installed', findings };
114
- }
115
- // Always run the peaks-loop lint layer; merge ECC findings.
27
+ // Slice 3: agent-shield integration removed. The CLI flag and
28
+ // preference are still read for backward-compat (a user with a
29
+ // stale preferences.json that sets `agentShieldEnabled: true`
30
+ // does not get a hard error) but neither spawns a subprocess.
31
+ loadPreferences(input.projectRoot);
32
+ // Always run the peaks-loop lint layer; no ECC findings merged.
116
33
  const peaksResult = runRedLinesAudit({ projectRoot: input.projectRoot });
117
34
  const mergedAudit = {
118
35
  ...peaksResult.audit,
119
- enforcerFindings: [...peaksResult.audit.enforcerFindings, ...state.findings],
36
+ enforcerFindings: [...peaksResult.audit.enforcerFindings],
37
+ };
38
+ const state = {
39
+ spawned: false,
40
+ installed: false,
41
+ reason: 'disabled-and-ecc-missing',
42
+ findings: [],
120
43
  };
121
- // v2.14.0 Slice C: compute prose-only ratio with informational
122
- // exclusion. Surfaced in the JSON envelope so CI can gate on it.
123
44
  const proseRatio = computeProseRatio(mergedAudit.audit);
124
45
  return {
125
46
  audit: mergedAudit,
126
47
  agentShield: state,
127
- warnings,
48
+ warnings: [],
128
49
  proseRatio,
129
50
  };
130
51
  }
@@ -1,4 +1,4 @@
1
- import type { ConfigGetOptions, ConfigLayer, ConfigSetOptions, ConfigV2, MiniMaxProviderConfig, OcrAuthHeader, OcrConfig, OcrLlmConfig, PeaksConfig, TokenRef, WorkspaceConfig } from './config-types.js';
1
+ import type { ConfigGetOptions, ConfigLayer, ConfigSetOptions, ConfigV2, OcrAuthHeader, OcrConfig, OcrLlmConfig, PeaksConfig, TokenRef, WorkspaceConfig } from './config-types.js';
2
2
  export { resolveProjectRootForConfig, resolveCanonicalProjectRoot } from './config-safety.js';
3
3
  /**
4
4
  * Load the slim 2.0 `~/.peaks/config.json` file. Returns the parsed
@@ -22,17 +22,6 @@ export type RedactedConfigValue = string | number | boolean | null | RedactedCon
22
22
  [key: string]: RedactedConfigValue;
23
23
  };
24
24
  export declare function redactConfigSecrets(value: unknown, path?: string): RedactedConfigValue;
25
- export type MiniMaxProviderStatus = {
26
- provider: 'minimax';
27
- configured: boolean;
28
- baseUrlConfigured: boolean;
29
- apiKeyConfigured: boolean;
30
- storage: 'user-plaintext-v1';
31
- nextActions: string[];
32
- };
33
- export declare function getMiniMaxProviderConfig(): MiniMaxProviderConfig;
34
- export declare function getMiniMaxProviderStatus(): MiniMaxProviderStatus;
35
- export declare function setMiniMaxProviderConfig(input: MiniMaxProviderConfig): MiniMaxProviderStatus;
36
25
  /**
37
26
  * Read the ocr LLM endpoint config from the user-layer
38
27
  * `~/.peaks/config.json`. The user populates this themselves by
@@ -221,7 +221,6 @@ function sanitizeBaseUrlForDisplay(value) {
221
221
  return '[invalid-url-redacted]';
222
222
  }
223
223
  }
224
- const MINIMAX_API_HOST = 'api.minimaxi.com';
225
224
  function isProviderBaseUrlPath(path) {
226
225
  return /^providers\.[^.]+\.baseUrl$/.test(path);
227
226
  }
@@ -234,37 +233,11 @@ function isValidProviderBaseUrl(value) {
234
233
  return false;
235
234
  }
236
235
  }
237
- function isValidMiniMaxBaseUrl(value) {
238
- try {
239
- const url = new URL(value);
240
- 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;
241
- }
242
- catch {
243
- return false;
244
- }
245
- }
246
- function getMiniMaxBaseUrlCandidate(key, value) {
247
- if (key === 'providers.minimax.baseUrl') {
248
- return value;
249
- }
250
- if (key === 'providers.minimax' && value !== null && typeof value === 'object' && !Array.isArray(value)) {
251
- return value.baseUrl;
252
- }
253
- if (key === 'providers' && value !== null && typeof value === 'object' && !Array.isArray(value)) {
254
- return value.minimax?.baseUrl;
255
- }
256
- return undefined;
257
- }
258
236
  function validateProviderBaseUrl(value) {
259
237
  if (value !== undefined && (typeof value !== 'string' || !isValidProviderBaseUrl(value))) {
260
238
  throw new Error('Provider base URL must be HTTPS without embedded credentials, query, or fragment');
261
239
  }
262
240
  }
263
- function validateMiniMaxBaseUrl(value) {
264
- if (value !== undefined && (typeof value !== 'string' || !isValidMiniMaxBaseUrl(value))) {
265
- throw new Error('MiniMax base URL must be the MiniMax HTTPS endpoint without embedded credentials');
266
- }
267
- }
268
241
  function getProxyUrlCandidate(key, value) {
269
242
  if (key === 'proxy.httpProxy') {
270
243
  return value;
@@ -278,11 +251,8 @@ function isProxyConfigPath(path) {
278
251
  return path === 'proxy' || path.startsWith('proxy.');
279
252
  }
280
253
  function validateModelProviderConfig(providers) {
281
- validateMiniMaxBaseUrl(providers.minimax?.baseUrl);
282
254
  for (const [providerId, provider] of Object.entries(providers)) {
283
- if (providerId !== 'minimax') {
284
- validateProviderBaseUrl(provider?.baseUrl);
285
- }
255
+ validateProviderBaseUrl(provider?.baseUrl);
286
256
  }
287
257
  }
288
258
  function validateProviderConfig(partial) {
@@ -363,9 +333,6 @@ function toProviderModelConfig(value) {
363
333
  ...(typeof value.apiKey === 'string' ? { apiKey: value.apiKey } : {})
364
334
  };
365
335
  }
366
- function toMiniMaxProviderConfig(value) {
367
- return toProviderModelConfig(value);
368
- }
369
336
  const TOKEN_CONFIG_KEYS = new Set(['AnthropicApiKey', 'OpenAiApiKey', 'GitHubToken', 'GitLabToken']);
370
337
  function toTokenRef(value) {
371
338
  if (!isRecord(value))
@@ -445,41 +412,6 @@ export function redactConfigSecrets(value, path = '') {
445
412
  return [key, redactConfigSecrets(entry, nextPath)];
446
413
  }));
447
414
  }
448
- function createMiniMaxProviderStatus(config) {
449
- const baseUrl = config.baseUrl?.trim();
450
- const apiKey = config.apiKey?.trim();
451
- const baseUrlConfigured = typeof baseUrl === 'string' && baseUrl.length > 0 && isValidMiniMaxBaseUrl(baseUrl);
452
- const apiKeyConfigured = typeof apiKey === 'string' && apiKey.length > 0;
453
- return {
454
- provider: 'minimax',
455
- configured: baseUrlConfigured && apiKeyConfigured,
456
- baseUrlConfigured,
457
- apiKeyConfigured,
458
- storage: 'user-plaintext-v1',
459
- nextActions: baseUrlConfigured && apiKeyConfigured ? [] : ['Export MINIMAX_API_KEY and rerun peaks config provider minimax set --base-url <url>']
460
- };
461
- }
462
- export function getMiniMaxProviderConfig() {
463
- return toMiniMaxProviderConfig(readUserJsonFile()?.providers?.minimax);
464
- }
465
- export function getMiniMaxProviderStatus() {
466
- return createMiniMaxProviderStatus(getMiniMaxProviderConfig());
467
- }
468
- export function setMiniMaxProviderConfig(input) {
469
- validateMiniMaxBaseUrl(input.baseUrl);
470
- const userConfig = readUserJsonFile() ?? {};
471
- const existingProviders = toModelProviderConfig(userConfig.providers);
472
- const providers = {
473
- ...existingProviders,
474
- minimax: {
475
- ...existingProviders.minimax,
476
- ...input
477
- }
478
- };
479
- validateMiniMaxBaseUrl(providers.minimax?.baseUrl);
480
- writeConfig({ providers }, 'user');
481
- return createMiniMaxProviderStatus(providers.minimax ?? {});
482
- }
483
415
  const OCR_AUTH_HEADERS = new Set(['authorization', 'x-api-key', 'bearer']);
484
416
  function toOcrLlmConfig(value) {
485
417
  if (!isRecord(value))
@@ -552,7 +484,7 @@ function toPeaksConfig(value) {
552
484
  return {
553
485
  ...(typeof value.version === 'string' ? { version: value.version } : {}),
554
486
  ...(typeof value.language === 'string' ? { language: value.language } : {}),
555
- ...(typeof value.model === 'string' && ['haiku', 'sonnet', 'opus', 'minimax'].includes(value.model) ? { model: value.model } : {}),
487
+ ...(typeof value.model === 'string' && ['haiku', 'sonnet', 'opus'].includes(value.model) ? { model: value.model } : {}),
556
488
  ...(typeof value.economyMode === 'boolean' ? { economyMode: value.economyMode } : {}),
557
489
  ...(typeof value.swarmMode === 'boolean' ? { swarmMode: value.swarmMode } : {}),
558
490
  ...(isRecord(value.tokens) ? { tokens: toTokenConfig(value.tokens) } : {}),
@@ -644,11 +576,10 @@ export function setConfig(options) {
644
576
  if (layer === 'project' && (isProviderConfigPath(options.key) || isProxyConfigPath(options.key) || isSensitiveConfigPath(options.key) || containsSensitiveConfigValue(options.value))) {
645
577
  throw new Error('Sensitive config keys must be stored in the user config layer');
646
578
  }
647
- validateMiniMaxBaseUrl(getMiniMaxBaseUrlCandidate(options.key, options.value));
648
579
  if (options.key === 'providers') {
649
580
  validateModelProviderConfig(toModelProviderConfig(options.value));
650
581
  }
651
- else if (options.key.startsWith('providers.') && !options.key.startsWith('providers.minimax.')) {
582
+ else if (options.key.startsWith('providers.')) {
652
583
  const providerId = getNestedPathParts(options.key)[1];
653
584
  if (options.key === `providers.${providerId}`) {
654
585
  validateModelProviderConfig({ [providerId]: toProviderModelConfig(options.value) });
@@ -1,14 +1,12 @@
1
- export type ModelPreference = 'haiku' | 'sonnet' | 'opus' | 'minimax';
2
- export type ModelProviderId = 'minimax' | string;
1
+ export type ModelPreference = 'haiku' | 'sonnet' | 'opus';
2
+ export type ModelProviderId = string;
3
3
  export type ExecutionModelId = string;
4
4
  export type ProviderModelConfig = {
5
5
  model?: ExecutionModelId;
6
6
  baseUrl?: string;
7
7
  apiKey?: string;
8
8
  };
9
- export type MiniMaxProviderConfig = ProviderModelConfig;
10
9
  export type ModelProviderConfig = {
11
- minimax?: MiniMaxProviderConfig;
12
10
  [providerId: string]: ProviderModelConfig | undefined;
13
11
  };
14
12
  export type ProxyConfig = {
@@ -280,8 +280,8 @@ export declare const ContextJsonSchema: z.ZodObject<{
280
280
  }>;
281
281
  }, "strip", z.ZodTypeAny, {
282
282
  version: "1.0";
283
- goal: string;
284
283
  sha256: string;
284
+ goal: string;
285
285
  generatedAt: string;
286
286
  collector: {
287
287
  deps: Record<string, {
@@ -347,8 +347,8 @@ export declare const ContextJsonSchema: z.ZodObject<{
347
347
  };
348
348
  }, {
349
349
  version: "1.0";
350
- goal: string;
351
350
  sha256: string;
351
+ goal: string;
352
352
  generatedAt: string;
353
353
  collector: {
354
354
  deps: Record<string, {
@@ -190,8 +190,8 @@ export declare const JobStateSchema: z.ZodObject<{
190
190
  blockedReason?: string | undefined;
191
191
  }>, "many">;
192
192
  }, "strip", z.ZodTypeAny, {
193
- sessionId: string;
194
193
  startedAt: string;
194
+ sessionId: string;
195
195
  jobId: string;
196
196
  slices: {
197
197
  status: "pending" | "skipped" | "done" | "failed" | "blocked" | "in-progress";
@@ -216,8 +216,8 @@ export declare const JobStateSchema: z.ZodObject<{
216
216
  to: "single";
217
217
  } | undefined;
218
218
  }, {
219
- sessionId: string;
220
219
  startedAt: string;
220
+ sessionId: string;
221
221
  jobId: string;
222
222
  slices: {
223
223
  status: "pending" | "skipped" | "done" | "failed" | "blocked" | "in-progress";
@@ -344,8 +344,8 @@ export declare const JobInitInputSchema: z.ZodObject<{
344
344
  json: z.ZodDefault<z.ZodBoolean>;
345
345
  }, "strip", z.ZodTypeAny, {
346
346
  project: string;
347
- sessionId: string;
348
347
  json: boolean;
348
+ sessionId: string;
349
349
  jobId: string;
350
350
  parallelismHint: "serial" | "llm-decides";
351
351
  exitPolicy: "strict" | "best-effort";
@@ -32,3 +32,19 @@ export type ApplyRetentionOptions = {
32
32
  * process between the `readdir` and the `unlink`).
33
33
  */
34
34
  export declare function applyRetention(opts?: ApplyRetentionOptions): string[];
35
+ /**
36
+ * Slice 3 (on-demand-ecc) — 7-day TTL sweep over `~/.peaks/cache/ecc-<sha>/`
37
+ * directories. Delegates to `cleanupStaleCache` in the ECC cache
38
+ * service so the retention policy lives next to the cache it
39
+ * governs.
40
+ *
41
+ * Mirrors the `applyRetention` signature for symmetry: tests pass
42
+ * `nowMs` + `dirOverride`; production callers leave both unset.
43
+ */
44
+ export declare function cleanupEccCache(options: {
45
+ retentionDays: number;
46
+ nowMs?: number;
47
+ dirOverride?: string;
48
+ }): {
49
+ removed: string[];
50
+ };
@@ -16,6 +16,7 @@
16
16
  import { readdirSync, statSync, unlinkSync, existsSync } from 'node:fs';
17
17
  import { join } from 'node:path';
18
18
  import { resolveLogDir } from './logger.js';
19
+ import { cleanupStaleCache } from '../agent/ecc-cache-service.js';
19
20
  const LOG_FILE_NAME_PATTERN = /^peaks-loop-(\d{4}-\d{2}-\d{2})\.log$/;
20
21
  function dayDiffUtc(nowUtcMidnightMs, fileDateUtcMs) {
21
22
  const dayMs = 24 * 60 * 60 * 1000;
@@ -76,3 +77,19 @@ export function applyRetention(opts = {}) {
76
77
  }
77
78
  return removed;
78
79
  }
80
+ /**
81
+ * Slice 3 (on-demand-ecc) — 7-day TTL sweep over `~/.peaks/cache/ecc-<sha>/`
82
+ * directories. Delegates to `cleanupStaleCache` in the ECC cache
83
+ * service so the retention policy lives next to the cache it
84
+ * governs.
85
+ *
86
+ * Mirrors the `applyRetention` signature for symmetry: tests pass
87
+ * `nowMs` + `dirOverride`; production callers leave both unset.
88
+ */
89
+ export function cleanupEccCache(options) {
90
+ return cleanupStaleCache({
91
+ retentionDays: options.retentionDays,
92
+ nowMs: options.nowMs ?? Date.now(),
93
+ ...(options.dirOverride !== undefined ? { dirOverride: options.dirOverride } : {}),
94
+ });
95
+ }
@@ -23,16 +23,16 @@ export declare const MigrateResultSchema: z.ZodObject<{
23
23
  skipped: z.ZodArray<z.ZodString, "many">;
24
24
  errors: z.ZodArray<z.ZodString, "many">;
25
25
  }, "strip", z.ZodTypeAny, {
26
- ok: boolean;
27
26
  skipped: string[];
27
+ ok: boolean;
28
28
  scannedFiles: number;
29
29
  modifiedFiles: number;
30
30
  keyValueReplacements: number;
31
31
  stringReplacements: number;
32
32
  errors: string[];
33
33
  }, {
34
- ok: boolean;
35
34
  skipped: string[];
35
+ ok: boolean;
36
36
  scannedFiles: number;
37
37
  modifiedFiles: number;
38
38
  keyValueReplacements: number;
@@ -9,18 +9,17 @@ import { getConfiguredExecutionModelId, getStrongestModelId } from '../config/mo
9
9
  * 2.0.1-bug1: the slim 2.0 `~/.peaks/config.json` no longer carries a
10
10
  * `providers` block (legacy model config lives in
11
11
  * `.peaks/preferences.json` per spec §10.4). `buildPlan` is a pure
12
- * planner function and historically took its execution model from
13
- * `DEFAULT_CONFIG.providers.minimax.model`; with the slim default
14
- * that field is `undefined`, so `getConfiguredExecutionModelId`
15
- * would throw. We retain the pre-2.0 default here as a literal so
16
- * the planner remains usable when the caller has not passed an
17
- * explicit `executionModelId` (unit tests, dry-run previews, the
18
- * `peaks swarm plan` onboarding path). Production callers that
19
- * have a real `ocr.llm.model` configured pass it via
20
- * `request.executionModelId` (or via the legacy preferences.json
21
- * bridge) and bypass this fallback.
12
+ * planner function and historically took its execution model from a
13
+ * legacy provider config; with the slim default that field is
14
+ * `undefined`, so `getConfiguredExecutionModelId` would throw. We
15
+ * retain a sensible default here as a literal so the planner remains
16
+ * usable when the caller has not passed an explicit `executionModelId`
17
+ * (unit tests, dry-run previews, the `peaks swarm plan` onboarding
18
+ * path). Production callers that have a real `ocr.llm.model` configured
19
+ * pass it via `request.executionModelId` (or via the legacy
20
+ * preferences.json bridge) and bypass this fallback.
22
21
  */
23
- const DEFAULT_EXECUTION_MODEL_ID = 'minimax-2.7';
22
+ const DEFAULT_EXECUTION_MODEL_ID = 'claude-opus-4-7';
24
23
  import { getTechStatus, TECH_REQUIRED_ARTIFACTS } from '../tech/tech-service.js';
25
24
  import { resolveRdStartupStandardsCheck } from './standards-diagnostic.js';
26
25
  import { runStrategicStage } from './strategic-stage.js';
@@ -25,8 +25,8 @@ export declare const StrategyOutputSchema: z.ZodObject<{
25
25
  }>>;
26
26
  }, "strip", z.ZodTypeAny, {
27
27
  version: "1.0";
28
- goal: string;
29
28
  sha256: string;
29
+ goal: string;
30
30
  generatedAt: string;
31
31
  rootCauseAnalysis: string;
32
32
  impactSurface: string[];
@@ -37,8 +37,8 @@ export declare const StrategyOutputSchema: z.ZodObject<{
37
37
  } | undefined;
38
38
  }, {
39
39
  version: "1.0";
40
- goal: string;
41
40
  sha256: string;
41
+ goal: string;
42
42
  generatedAt: string;
43
43
  rootCauseAnalysis: string;
44
44
  impactSurface: string[];
@@ -93,7 +93,6 @@ export const seedCapabilityItems = [
93
93
  capability('searchcode-mcp.code-search', 'searchcode-mcp', 'SearchCode MCP', 'mcp', 'code-search', ['engineer'], 'medium', 'local-code-search', 'Use local Grep/Glob and ask before sending private code externally.', 'External Code Search', '外部代码搜索', 'Searches code examples or repositories outside the current workspace.', '搜索当前工作区外部的代码示例或仓库。'),
94
94
  capability('mysql-mcp.database-inspection', 'mysql-mcp-server', 'MySQL Database Inspection', 'mcp', 'database-inspection', ['engineer'], 'high', 'schema-excerpt-input', 'Ask the user for schema excerpts or sanitized query plans.', 'MySQL Inspection', 'MySQL 检查', 'Inspects MySQL schema and data through an MCP boundary.', '通过 MCP 边界检查 MySQL schema 与数据。'),
95
95
  capability('agent-browser.browser-agent', 'agent-browser', 'Agent Browser', 'agent', 'browser-agent', ['engineer', 'qa'], 'medium', 'manual-browser-walkthrough', 'Use screenshots and manual test steps if agent browser is unavailable.', 'Agent Browser', '浏览器代理', 'Supports browser-based validation and interaction planning.', '支持基于浏览器的验证和交互规划。'),
96
- capability('minimax-skills.worker-guidance', 'minimax-skills', 'MiniMax Worker Guidance', 'skill', 'worker-guidance', ['engineer'], 'medium', 'peaks-worker-contract', 'Use Peaks built-in minimax-worker contract and review handoff.', 'MiniMax Worker Guidance', 'MiniMax Worker 指南', 'Guides MiniMax coding/test worker delegation.', '指导 MiniMax 编码/测试 worker 委托。'),
97
96
  capability('claude-mem.memory-persistence', 'claude-mem', 'Claude Memory Persistence', 'skill', 'memory', ['engineer'], 'medium', 'peaks-txt-context-capsule', 'Use peaks-txt context capsules without storing secrets.', 'Memory Persistence', '记忆持久化', 'Persists reusable context when explicitly approved.', '在明确授权时持久化可复用上下文。'),
98
97
  capability('openspec.spec-workflow', 'openspec', 'OpenSpec Workflow', 'workflow', 'spec-workflow', ['product', 'engineer'], 'low', 'peaks-prd-rd-qa-artifacts', 'Use Peaks built-in PRD/RD/QA artifact flow.', 'OpenSpec Workflow', 'OpenSpec 规格流程', 'Supports spec-first product and engineering governance.', '支持规格优先的产品与工程治理。'),
99
98
  capability('gitnexus.repo-intelligence', 'gitnexus', 'GitNexus Repository Intelligence', 'cli', 'repo-intelligence', ['engineer'], 'medium', 'local-repo-scan', 'Use local project scanning through Peaks RD.', 'Repository Intelligence', '仓库智能分析', 'Repository intelligence should be proxied through Peaks before use.', '仓库智能分析应先通过 Peaks 代理边界再使用。'),
@@ -31,7 +31,6 @@ export const seedCapabilityLandingMappings = [
31
31
  mapping({ capabilityId: 'impeccable.quality-guidance', sourceId: 'impeccable', sourceGroup: 'mcp-server', landingKind: 'catalog', target: 'quality reference catalog', guidance: 'Use as quality inspiration; Peaks review gates remain authoritative.' }),
32
32
  mapping({ capabilityId: 'vercel-agent-skills.skill-pack', sourceId: 'vercel-agent-skills', sourceGroup: 'mcp-server', landingKind: 'catalog', target: 'external skill catalog', guidance: 'Catalog only until individual skills are inspected and approved.' }),
33
33
  mapping({ capabilityId: 'agent-browser.browser-agent', sourceId: 'agent-browser', sourceGroup: 'mcp-server', landingKind: 'skill', target: 'peaks-qa', skillName: 'peaks-qa', guidance: 'Use for browser validation; never submit forms or mutate authenticated state without explicit permission.' }),
34
- mapping({ capabilityId: 'minimax-skills.worker-guidance', sourceId: 'minimax-skills', sourceGroup: 'mcp-server', landingKind: 'cli', target: 'peaks minimax-worker', commandPreview: 'peaks minimax-worker --json', guidance: 'Use Peaks worker command only after reviewing inputs; add --confirm manually when explicit external-provider approval exists.' }),
35
34
  mapping({ capabilityId: 'claude-mem.memory-persistence', sourceId: 'claude-mem', sourceGroup: 'mcp-server', landingKind: 'skill', target: 'peaks-txt', skillName: 'peaks-txt', guidance: 'Use only with explicit durable-memory consent and never store secrets.' }),
36
35
  mapping({ capabilityId: 'darwin-skill.external-skill', sourceId: 'darwin-skill', sourceGroup: 'mcp-server', landingKind: 'catalog', target: 'external skill catalog', guidance: 'Catalog only until inspected for project fit and safety.' }),
37
36
  mapping({ capabilityId: 'claude-code-best-practice.workflow-guidance', sourceId: 'claude-code-best-practice', sourceGroup: 'mcp-server', landingKind: 'skill', target: 'peaks-rd', skillName: 'peaks-rd', guidance: 'Use as Claude Code workflow reference while preserving Peaks gates.' }),
@@ -15,7 +15,6 @@ export const seedCapabilitySources = [
15
15
  { sourceId: 'impeccable', sourceType: 'repo', sourceGroup: 'mcp-server', title: 'impeccable', url: 'https://github.com/pbakaus/impeccable', discoveryStatus: 'unscanned', items: ['impeccable.quality-guidance'] },
16
16
  { sourceId: 'vercel-agent-skills', sourceType: 'skills-package', sourceGroup: 'mcp-server', title: 'Vercel Agent Skills', url: 'https://github.com/vercel-labs/agent-skills', discoveryStatus: 'unscanned', items: ['vercel-agent-skills.skill-pack'] },
17
17
  { sourceId: 'agent-browser', sourceType: 'repo', sourceGroup: 'mcp-server', title: 'Agent Browser', url: 'https://github.com/vercel-labs/agent-browser', discoveryStatus: 'indexed', items: ['agent-browser.browser-agent'] },
18
- { sourceId: 'minimax-skills', sourceType: 'skills-package', sourceGroup: 'mcp-server', title: 'MiniMax Skills', url: 'https://github.com/MiniMax-AI/skills', discoveryStatus: 'indexed', items: ['minimax-skills.worker-guidance'] },
19
18
  { sourceId: 'claude-mem', sourceType: 'repo', sourceGroup: 'mcp-server', title: 'claude-mem', url: 'https://github.com/thedotmack/claude-mem', discoveryStatus: 'indexed', items: ['claude-mem.memory-persistence'] },
20
19
  { sourceId: 'darwin-skill', sourceType: 'skills-package', sourceGroup: 'mcp-server', title: 'darwin-skill', url: 'https://github.com/alchaincyf/darwin-skill', discoveryStatus: 'unscanned', items: ['darwin-skill.external-skill'] },
21
20
  { sourceId: 'claude-code-best-practice', sourceType: 'repo', sourceGroup: 'mcp-server', title: 'Claude Code Best Practice', url: 'https://github.com/shanraisshan/claude-code-best-practice', discoveryStatus: 'indexed', items: ['claude-code-best-practice.workflow-guidance'] },
@@ -29,8 +29,10 @@ export declare const SkillSearchInputSchema: z.ZodEffects<z.ZodObject<{
29
29
  tag: z.ZodOptional<z.ZodString>;
30
30
  domain: z.ZodOptional<z.ZodEnum<["code", "content", "doctor", "research", "triage", "sop", "audit", "final-review", "resume", "status", "test", "ide", "slice-decompose", "issue-fix-orchestrator", "perf-audit", "security-audit", "reviewer"]>>;
31
31
  limit: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
32
+ includeInternal: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
32
33
  }, "strip", z.ZodTypeAny, {
33
34
  limit: number;
35
+ includeInternal: boolean;
34
36
  query?: string | undefined;
35
37
  tag?: string | undefined;
36
38
  domain?: "code" | "status" | "content" | "test" | "doctor" | "resume" | "ide" | "research" | "triage" | "sop" | "audit" | "final-review" | "slice-decompose" | "issue-fix-orchestrator" | "perf-audit" | "security-audit" | "reviewer" | undefined;
@@ -39,8 +41,10 @@ export declare const SkillSearchInputSchema: z.ZodEffects<z.ZodObject<{
39
41
  query?: string | undefined;
40
42
  tag?: string | undefined;
41
43
  domain?: "code" | "status" | "content" | "test" | "doctor" | "resume" | "ide" | "research" | "triage" | "sop" | "audit" | "final-review" | "slice-decompose" | "issue-fix-orchestrator" | "perf-audit" | "security-audit" | "reviewer" | undefined;
44
+ includeInternal?: boolean | undefined;
42
45
  }>, {
43
46
  limit: number;
47
+ includeInternal: boolean;
44
48
  query?: string | undefined;
45
49
  tag?: string | undefined;
46
50
  domain?: "code" | "status" | "content" | "test" | "doctor" | "resume" | "ide" | "research" | "triage" | "sop" | "audit" | "final-review" | "slice-decompose" | "issue-fix-orchestrator" | "perf-audit" | "security-audit" | "reviewer" | undefined;
@@ -49,6 +53,7 @@ export declare const SkillSearchInputSchema: z.ZodEffects<z.ZodObject<{
49
53
  query?: string | undefined;
50
54
  tag?: string | undefined;
51
55
  domain?: "code" | "status" | "content" | "test" | "doctor" | "resume" | "ide" | "research" | "triage" | "sop" | "audit" | "final-review" | "slice-decompose" | "issue-fix-orchestrator" | "perf-audit" | "security-audit" | "reviewer" | undefined;
56
+ includeInternal?: boolean | undefined;
52
57
  }>;
53
58
  export type SkillSearchInput = z.input<typeof SkillSearchInputSchema>;
54
59
  export declare const SkillSearchResultSchema: z.ZodObject<{
@@ -51,7 +51,8 @@ export const SkillSearchInputSchema = z
51
51
  query: z.string().min(1).max(500).optional(),
52
52
  tag: z.string().min(1).max(100).optional(),
53
53
  domain: z.enum(SKILL_DOMAINS).optional(),
54
- limit: z.number().int().min(1).max(100).optional().default(20)
54
+ limit: z.number().int().min(1).max(100).optional().default(20),
55
+ includeInternal: z.boolean().optional().default(false)
55
56
  })
56
57
  .refine((v) => v.query !== undefined || v.tag !== undefined || v.domain !== undefined, { message: 'At least one of --query / --tag / --domain is required' });
57
58
  export const SkillSearchResultSchema = z.object({
@@ -130,7 +131,8 @@ async function loadEnrichedSkills() {
130
131
  triggers: extractTriggers(s.description),
131
132
  tags: meta.tags,
132
133
  domain: meta.domain,
133
- skillPath: s.skillPath
134
+ skillPath: s.skillPath,
135
+ ...(s.visibility !== undefined ? { visibility: s.visibility } : {})
134
136
  };
135
137
  }));
136
138
  }
@@ -187,6 +189,8 @@ export async function searchSkills(rawInput) {
187
189
  const input = SkillSearchInputSchema.parse(rawInput);
188
190
  const skills = await loadEnrichedSkills();
189
191
  const filtered = skills.filter((skill) => {
192
+ if (!input.includeInternal && skill.visibility === 'internal')
193
+ return false;
190
194
  if (input.tag !== undefined) {
191
195
  const wanted = input.tag.toLowerCase();
192
196
  const has = skill.tags.some((t) => t.toLowerCase() === wanted);
@@ -3,6 +3,7 @@ export type SkillMetadata = {
3
3
  description: string;
4
4
  directory: string;
5
5
  skillPath: string;
6
+ visibility?: string;
6
7
  };
7
8
  export type SkillLoadFailure = {
8
9
  directory: string;
@@ -31,7 +31,8 @@ export async function loadSkillRegistry(baseDir = skillsDir) {
31
31
  name: frontmatter.name,
32
32
  description: frontmatter.description,
33
33
  directory: subDir,
34
- skillPath
34
+ skillPath,
35
+ ...(frontmatter.visibility !== undefined ? { visibility: frontmatter.visibility } : {})
35
36
  });
36
37
  }
37
38
  catch (error) {
@@ -50,7 +51,8 @@ export async function loadSkillRegistry(baseDir = skillsDir) {
50
51
  name: frontmatter.name,
51
52
  description: frontmatter.description,
52
53
  directory,
53
- skillPath
54
+ skillPath,
55
+ ...(frontmatter.visibility !== undefined ? { visibility: frontmatter.visibility } : {})
54
56
  });
55
57
  }
56
58
  catch (error) {