peaks-cli 1.4.1 → 1.4.2

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 (57) hide show
  1. package/README.md +0 -53
  2. package/dist/src/cli/commands/core-artifact-commands.js +21 -0
  3. package/dist/src/cli/commands/memory-commands.d.ts +13 -0
  4. package/dist/src/cli/commands/memory-commands.js +60 -0
  5. package/dist/src/cli/commands/retrospective-commands.d.ts +9 -0
  6. package/dist/src/cli/commands/retrospective-commands.js +58 -0
  7. package/dist/src/cli/program.js +16 -22
  8. package/dist/src/services/fuzzy-matching/fuzzy-match-service.d.ts +15 -0
  9. package/dist/src/services/fuzzy-matching/fuzzy-match-service.js +56 -0
  10. package/dist/src/services/fuzzy-matching/types.d.ts +20 -0
  11. package/dist/src/services/fuzzy-matching/types.js +1 -0
  12. package/dist/src/services/memory/memory-search-service.d.ts +61 -0
  13. package/dist/src/services/memory/memory-search-service.js +80 -0
  14. package/dist/src/services/recommendations/capability-seed-items.js +0 -1
  15. package/dist/src/services/recommendations/capability-seed-mappings.js +0 -1
  16. package/dist/src/services/recommendations/capability-seed-sources.js +0 -1
  17. package/dist/src/services/retrospective/retrospective-search-service.d.ts +37 -0
  18. package/dist/src/services/retrospective/retrospective-search-service.js +75 -0
  19. package/dist/src/services/standards/project-context.d.ts +1 -1
  20. package/dist/src/services/standards/project-context.js +0 -4
  21. package/dist/src/services/standards/project-standards-service.js +1 -3
  22. package/dist/src/services/workspace/migrate-1-4-1-service.js +1 -1
  23. package/dist/src/shared/version.d.ts +1 -1
  24. package/dist/src/shared/version.js +1 -1
  25. package/package.json +3 -7
  26. package/skills/peaks-solo/SKILL.md +1 -1
  27. package/skills/peaks-solo/references/completion-handoff.md +3 -1
  28. package/dist/src/cli/commands/shadcn-commands.d.ts +0 -3
  29. package/dist/src/cli/commands/shadcn-commands.js +0 -35
  30. package/dist/src/cli/commands/skill-context-stats-command.d.ts +0 -40
  31. package/dist/src/cli/commands/skill-context-stats-command.js +0 -96
  32. package/dist/src/cli/commands/skill-scope-commands.d.ts +0 -51
  33. package/dist/src/cli/commands/skill-scope-commands.js +0 -310
  34. package/dist/src/services/shadcn/shadcn-service.d.ts +0 -27
  35. package/dist/src/services/shadcn/shadcn-service.js +0 -128
  36. package/dist/src/services/skill-scope/adapters/_stub-helper.d.ts +0 -39
  37. package/dist/src/services/skill-scope/adapters/_stub-helper.js +0 -98
  38. package/dist/src/services/skill-scope/adapters/claude-code.d.ts +0 -59
  39. package/dist/src/services/skill-scope/adapters/claude-code.js +0 -304
  40. package/dist/src/services/skill-scope/adapters/codex.d.ts +0 -2
  41. package/dist/src/services/skill-scope/adapters/codex.js +0 -12
  42. package/dist/src/services/skill-scope/adapters/cursor.d.ts +0 -2
  43. package/dist/src/services/skill-scope/adapters/cursor.js +0 -13
  44. package/dist/src/services/skill-scope/adapters/qoder.d.ts +0 -2
  45. package/dist/src/services/skill-scope/adapters/qoder.js +0 -13
  46. package/dist/src/services/skill-scope/adapters/tongyi.d.ts +0 -2
  47. package/dist/src/services/skill-scope/adapters/tongyi.js +0 -13
  48. package/dist/src/services/skill-scope/adapters/trae.d.ts +0 -2
  49. package/dist/src/services/skill-scope/adapters/trae.js +0 -12
  50. package/dist/src/services/skill-scope/detect.d.ts +0 -81
  51. package/dist/src/services/skill-scope/detect.js +0 -513
  52. package/dist/src/services/skill-scope/registry.d.ts +0 -41
  53. package/dist/src/services/skill-scope/registry.js +0 -83
  54. package/dist/src/services/skill-scope/source-of-truth.d.ts +0 -44
  55. package/dist/src/services/skill-scope/source-of-truth.js +0 -118
  56. package/dist/src/services/skill-scope/types.d.ts +0 -195
  57. package/dist/src/services/skill-scope/types.js +0 -97
@@ -0,0 +1,75 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { join, resolve } from 'node:path';
3
+ import { fuzzyMatchWithKey } from '../fuzzy-matching/fuzzy-match-service.js';
4
+ const DEFAULT_LIMIT = 6;
5
+ /**
6
+ * Read `.peaks/retrospective/index.json` and return the entries.
7
+ * Throws structured errors with stable codes that the CLI converts
8
+ * to the peaks envelope.
9
+ */
10
+ function readIndex(projectRoot) {
11
+ const resolvedRoot = resolve(projectRoot);
12
+ const indexPath = join(resolvedRoot, '.peaks', 'retrospective', 'index.json');
13
+ if (!existsSync(indexPath)) {
14
+ const err = new Error(`INDEX_MISSING: retrospective index not found at ${indexPath}`);
15
+ err.code = 'INDEX_MISSING';
16
+ throw err;
17
+ }
18
+ let raw;
19
+ try {
20
+ raw = readFileSync(indexPath, 'utf8');
21
+ }
22
+ catch (cause) {
23
+ const err = new Error(`INDEX_INVALID: failed to read retrospective index at ${indexPath}: ${cause.message}`);
24
+ err.code = 'INDEX_INVALID';
25
+ throw err;
26
+ }
27
+ let parsed;
28
+ try {
29
+ parsed = JSON.parse(raw);
30
+ }
31
+ catch (cause) {
32
+ const err = new Error(`INDEX_INVALID: malformed retrospective index at ${indexPath}: ${cause.message}`);
33
+ err.code = 'INDEX_INVALID';
34
+ throw err;
35
+ }
36
+ const index = parsed;
37
+ return Array.isArray(index.entries) ? index.entries : [];
38
+ }
39
+ /**
40
+ * Run the generic fuzzy kernel against the on-disk retrospective index.
41
+ * Searchable text is `title + " " + summary` per spec §Component Details.
42
+ * `--type` and `--outcome` filters compose with AND before the kernel
43
+ * runs (cheaper to filter, then fuzzy on a smaller set).
44
+ */
45
+ export function searchRetrospective(input) {
46
+ if (input.query === '') {
47
+ const err = new Error('EMPTY_QUERY: searchRetrospective requires a non-empty query (use `peaks retrospective index` to list all)');
48
+ err.code = 'EMPTY_QUERY';
49
+ throw err;
50
+ }
51
+ const projectRoot = input.projectRoot ?? process.cwd();
52
+ const limit = input.limit ?? DEFAULT_LIMIT;
53
+ let candidates = readIndex(projectRoot);
54
+ if (input.type !== undefined) {
55
+ candidates = candidates.filter((e) => e.type === input.type);
56
+ }
57
+ if (input.outcome !== undefined) {
58
+ candidates = candidates.filter((e) => e.outcome === input.outcome);
59
+ }
60
+ const matches = fuzzyMatchWithKey(input.query, candidates, { keyFn: (e) => `${e.title} ${e.summary}`, limit, caseSensitive: false });
61
+ return matches.map((m) => {
62
+ const entry = m.item;
63
+ return {
64
+ id: entry.id,
65
+ sessionId: entry.sessionId,
66
+ type: entry.type,
67
+ title: entry.title,
68
+ summary: entry.summary,
69
+ outcome: entry.outcome,
70
+ artifactPaths: entry.artifactPaths,
71
+ score: m.score,
72
+ positions: m.positions,
73
+ };
74
+ });
75
+ }
@@ -1,6 +1,6 @@
1
1
  export type BuildTool = 'umi' | 'next' | 'vite' | 'rsbuild' | 'rspack' | 'farm' | 'craco' | 'webpack' | 'gulp' | 'angular' | 'custom' | 'unknown';
2
2
  export type ComponentLibrary = {
3
- readonly name: 'antd' | 'antd-pro' | 'mui' | 'shadcn' | 'element-plus' | 'element-ui' | 'arco' | 'tdesign' | 'semi' | 'nextui' | 'chakra' | 'vant' | 'none';
3
+ readonly name: 'antd' | 'antd-pro' | 'mui' | 'element-plus' | 'element-ui' | 'arco' | 'tdesign' | 'semi' | 'nextui' | 'chakra' | 'vant' | 'none';
4
4
  readonly majorVersion?: string;
5
5
  readonly hasProSuite?: boolean;
6
6
  };
@@ -64,8 +64,6 @@ function detectComponentLibrary(deps) {
64
64
  }
65
65
  if ('@mui/material' in deps)
66
66
  return { name: 'mui', ...(majorOf(deps['@mui/material']) !== undefined ? { majorVersion: majorOf(deps['@mui/material']) } : {}) };
67
- if ('tailwindcss' in deps && Object.keys(deps).some((d) => d.startsWith('@radix-ui/')))
68
- return { name: 'shadcn' };
69
67
  if ('element-plus' in deps)
70
68
  return { name: 'element-plus' };
71
69
  if ('element-ui' in deps)
@@ -279,8 +277,6 @@ export function componentLibraryLabel(lib) {
279
277
  return 'Ant Design + Ant Design Pro';
280
278
  case 'mui':
281
279
  return 'Material UI';
282
- case 'shadcn':
283
- return 'shadcn/ui (Tailwind + Radix)';
284
280
  case 'element-plus':
285
281
  return 'Element Plus';
286
282
  case 'element-ui':
@@ -159,8 +159,6 @@ function renderCommonCodingStyle(ctx) {
159
159
  }
160
160
  if (lib === 'mui')
161
161
  stackRules.push('- Style MUI via `sx`, `styled()`, and `theme`. Do NOT apply TailwindCSS utility classes directly to MUI components.');
162
- if (lib === 'shadcn')
163
- stackRules.push('- Use existing shadcn component variants and Tailwind utility classes. Do not introduce a competing component library.');
164
162
  if (ctx.cssFrameworks.includes('tailwind') && (lib === 'antd' || lib === 'antd-pro' || lib === 'mui')) {
165
163
  stackRules.push('- TailwindCSS is for layout/utility only; component-library tokens own component styling.');
166
164
  }
@@ -185,7 +183,7 @@ function renderCodeReview(ctx) {
185
183
  const extra = [];
186
184
  const lib = ctx.componentLibrary.name;
187
185
  if (lib === 'antd' || lib === 'antd-pro') {
188
- extra.push('- Block PRs that introduce a second component library (MUI/shadcn/Chakra) alongside antd.');
186
+ extra.push('- Block PRs that introduce a second component library (MUI/Chakra) alongside antd.');
189
187
  extra.push('- Block PRs that import antd v3/v4 APIs in this v5 project, or vice versa.');
190
188
  }
191
189
  if (ctx.cssFrameworks.includes('tailwind') && (lib === 'antd' || lib === 'antd-pro' || lib === 'mui')) {
@@ -41,7 +41,7 @@ function sha256(content) {
41
41
  function enumerateLegacySessions(projectRoot) {
42
42
  // Legacy per-session dirs: `.peaks/<sid>/` (NOT `.peaks/_runtime/<sid>/`).
43
43
  // We skip well-known non-session entries.
44
- const SKIP = new Set(['memory', 'PROJECT.md', 'retrospective', 'scope', 'skill-scope', '.peaks-init-hooks-decision.json', 'session.json', '.session.json', '_runtime', '_sub_agents', 'change', 'caller', 'callers', 'sop-state', 'system', 'active-skill.json', '.active-skill.json']);
44
+ const SKIP = new Set(['memory', 'PROJECT.md', 'retrospective', 'scope', '.peaks-init-hooks-decision.json', 'session.json', '.session.json', '_runtime', '_sub_agents', 'change', 'caller', 'callers', 'sop-state', 'system', 'active-skill.json', '.active-skill.json']);
45
45
  const peaksRoot = join(projectRoot, '.peaks');
46
46
  if (!existsSync(peaksRoot))
47
47
  return [];
@@ -1 +1 @@
1
- export declare const CLI_VERSION = "1.4.1";
1
+ export declare const CLI_VERSION = "1.4.2";
@@ -1 +1 @@
1
- export const CLI_VERSION = "1.4.1";
1
+ export const CLI_VERSION = "1.4.2";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "peaks-cli",
3
- "version": "1.4.1",
3
+ "version": "1.4.2",
4
4
  "description": "Cross-AI-IDE workflow-gating CLI + skill family (Claude Code shipped, Trae in progress; Codex / Cursor / Qoder / Tongyi Lingma on the roadmap).",
5
5
  "author": "SquabbyZ",
6
6
  "license": "MIT",
@@ -55,15 +55,11 @@
55
55
  },
56
56
  "dependencies": {
57
57
  "@colbymchenry/codegraph": "0.7.10",
58
- "chalk": "^5.6.2",
59
58
  "commander": "^12.1.0",
60
- "headroom-ai": "0.22.4",
61
- "ora": "^8.2.0",
62
- "shadcn": "4.7.0",
63
- "terminal-kit": "^3.1.2"
59
+ "fzf": "^0.5.2",
60
+ "headroom-ai": "0.22.4"
64
61
  },
65
62
  "devDependencies": {
66
- "@types/chalk": "^2.2.4",
67
63
  "@types/node": "^22.10.2",
68
64
  "@vitest/coverage-v8": "^2.1.8",
69
65
  "tsx": "^4.19.2",
@@ -185,7 +185,7 @@ Five CLI commands harden the workflow against silent skips: `peaks request lint`
185
185
 
186
186
  ## Peaks-Cli Completion handoff
187
187
 
188
- After final validation, refresh project-local standards via `peaks standards init/update` (never hand-write). Use Peaks-Cli TXT for the compact handoff capsule: mode, validated decisions, artifact paths, standards deltas, open questions, next action. Do NOT call `peaks skill presence:clear --project <repo>` at workflow end (presence remains active for follow-ups).
188
+ After final validation, refresh project-local standards via `peaks standards init/update` (never hand-write). Use Peaks-Cli TXT for the compact handoff capsule: mode, validated decisions, artifact paths, standards deltas, open questions, next action. **Presence management is delegated to the last downstream skill in the workflow** — peaks-solo does not call `peaks skill presence:clear` itself, and does not enforce a "no clear" rule. The downstream skills (peaks-rd, peaks-qa, peaks-txt) each manage their own presence per their respective SKILL.md.
189
189
 
190
190
  → see `references/completion-handoff.md` for the full handoff + "no auto-exit" rule.
191
191
 
@@ -8,7 +8,9 @@ Use Peaks-Cli TXT for the compact handoff capsule: mode, validated decisions, ar
8
8
 
9
9
  ## Workflow completion (no auto-exit)
10
10
 
11
- Do NOT call `peaks skill presence:clear --project <repo>` at workflow end. The presence file and header remain active so the user stays inside the workflow context. The user can continue with follow-up requirements naturally no need to re-invoke `/peaks-solo`. The header continues to display the active skill and current gate.
11
+ peaks-solo does NOT itself call `peaks skill presence:clear --project <repo>` at workflow end. Presence management is delegated to the last downstream skill in the workflow (peaks-rd, peaks-qa, peaks-txt); each of those skills owns its own presence:clear step per its SKILL.md. peaks-solo only sets presence: it does not unset it.
12
+
13
+ The user can continue with follow-up requirements naturally — no need to re-invoke `/peaks-solo` to do so. The header continues to display whatever skill is active; the user can `/peaks-solo` again to re-anchor.
12
14
 
13
15
  Before ending, extract durable memories from this session:
14
16
  ```bash
@@ -1,3 +0,0 @@
1
- import { Command } from 'commander';
2
- import { type ProgramIO } from '../cli-helpers.js';
3
- export declare function registerShadcnCommands(program: Command, io: ProgramIO): void;
@@ -1,35 +0,0 @@
1
- import { createShadcnInvocation, executeShadcnInvocation } from '../../services/shadcn/shadcn-service.js';
2
- import { fail } from '../../shared/result.js';
3
- import { getErrorMessage, printResult, redactSensitiveErrorMessage } from '../cli-helpers.js';
4
- function printShadcnFailure(io, error, exitCode = 1) {
5
- printResult(io, fail('shadcn', 'SHADCN_COMMAND_FAILED', redactSensitiveErrorMessage(getErrorMessage(error)), {}, ['Check the shadcn command arguments before retrying']), false);
6
- process.exitCode = exitCode;
7
- }
8
- async function runShadcnCommand(io, args) {
9
- try {
10
- const invocation = createShadcnInvocation({ args });
11
- const result = await executeShadcnInvocation(invocation);
12
- const didFail = result.exitCode !== null && result.exitCode !== 0;
13
- if (result.stdout.length > 0) {
14
- io.stdout((didFail ? redactSensitiveErrorMessage(result.stdout) : result.stdout).trimEnd());
15
- }
16
- if (result.stderr.length > 0) {
17
- io.stderr((didFail ? redactSensitiveErrorMessage(result.stderr) : result.stderr).trimEnd());
18
- }
19
- if (didFail) {
20
- process.exitCode = result.exitCode;
21
- }
22
- }
23
- catch (error) {
24
- printShadcnFailure(io, error);
25
- }
26
- }
27
- export function registerShadcnCommands(program, io) {
28
- program
29
- .command('shadcn')
30
- .description('Run the pinned shadcn CLI bundled with Peaks')
31
- .allowUnknownOption(true)
32
- .helpOption(false)
33
- .argument('<args...>', 'arguments forwarded to shadcn')
34
- .action((args) => runShadcnCommand(io, args));
35
- }
@@ -1,40 +0,0 @@
1
- /**
2
- * `peaks skill context-stats` — R003.2
3
- *
4
- * Reports the per-project skill context footprint for the LLM:
5
- * - Total bytes of allowed skills (full SKILL.md)
6
- * - Total bytes of denied skills (shadow stubs in the project-local mirror)
7
- * - Estimated token counts (chars/4 for full skills, bytes*0.25 for stubs)
8
- * - Shadow-stub reduction percentage vs. the original full SKILL.md bytes
9
- *
10
- * If no scope is applied (no `.peaks/scope/skills.json`), returns the
11
- * "no-scope" branch with a recommended command.
12
- */
13
- import { type ResultEnvelope } from '../../shared/result.js';
14
- export interface RunContextStatsInput {
15
- readonly projectRoot: string;
16
- readonly json: boolean;
17
- /** Average bytes-per-skill estimate for denied skills without a real SKILL.md (default 7000). */
18
- readonly estimatedDeniedOriginalBytes?: number;
19
- }
20
- export interface ContextStatsData {
21
- readonly scope: unknown;
22
- readonly totals: {
23
- readonly allowedCount: number;
24
- readonly deniedCount: number;
25
- readonly allowedBytes: number;
26
- readonly stubBytes: number;
27
- readonly originalDeniedBytes: number;
28
- readonly shadowReductionPct: number;
29
- readonly totalBytes: number;
30
- };
31
- readonly estimatedTokens: {
32
- readonly allowed: number;
33
- readonly denied: number;
34
- readonly total: number;
35
- };
36
- readonly message?: string;
37
- readonly recommendedCommand?: string;
38
- readonly human?: string;
39
- }
40
- export declare function runContextStats(input: RunContextStatsInput): Promise<ResultEnvelope<ContextStatsData>>;
@@ -1,96 +0,0 @@
1
- /**
2
- * `peaks skill context-stats` — R003.2
3
- *
4
- * Reports the per-project skill context footprint for the LLM:
5
- * - Total bytes of allowed skills (full SKILL.md)
6
- * - Total bytes of denied skills (shadow stubs in the project-local mirror)
7
- * - Estimated token counts (chars/4 for full skills, bytes*0.25 for stubs)
8
- * - Shadow-stub reduction percentage vs. the original full SKILL.md bytes
9
- *
10
- * If no scope is applied (no `.peaks/scope/skills.json`), returns the
11
- * "no-scope" branch with a recommended command.
12
- */
13
- import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
14
- import { join } from 'node:path';
15
- import { ok, fail } from '../../shared/result.js';
16
- export async function runContextStats(input) {
17
- const projectRoot = input.projectRoot;
18
- const scopePath = join(projectRoot, '.peaks', 'scope', 'skills.json');
19
- if (!existsSync(scopePath)) {
20
- const noScopeData = {
21
- scope: null,
22
- totals: {
23
- allowedCount: 0,
24
- deniedCount: 0,
25
- allowedBytes: 0,
26
- stubBytes: 0,
27
- originalDeniedBytes: 0,
28
- shadowReductionPct: 0,
29
- totalBytes: 0,
30
- },
31
- estimatedTokens: { allowed: 0, denied: 0, total: 0 },
32
- message: 'No scope applied. Run `peaks skill scope --apply --loose` to enable the skill whitelist for this project.',
33
- recommendedCommand: 'peaks skill scope --apply --loose',
34
- };
35
- return fail('skill.context-stats', 'NO_SCOPE', 'No scope applied to this project.', noScopeData);
36
- }
37
- const raw = JSON.parse(readFileSync(scopePath, 'utf8'));
38
- const allowlist = raw.allowlist;
39
- const denied = [];
40
- // Walk .claude/skills/ to find shadow stubs (denied skills not in allowlist).
41
- const skillsMirror = join(projectRoot, '.claude', 'skills');
42
- let stubBytes = 0;
43
- let originalDeniedBytes = 0;
44
- const estimatedDeniedOriginalBytes = input.estimatedDeniedOriginalBytes ?? 7000;
45
- if (existsSync(skillsMirror)) {
46
- for (const entry of readdirSync(skillsMirror)) {
47
- const skillMd = join(skillsMirror, entry, 'SKILL.md');
48
- if (!existsSync(skillMd))
49
- continue;
50
- if (allowlist.includes(entry))
51
- continue; // allowed skills are NOT in the mirror
52
- denied.push(entry);
53
- const stat = statSync(skillMd);
54
- stubBytes += stat.size;
55
- // Heuristic: a denied skill would have loaded its full body (~7000 bytes) without shadow-fallback.
56
- // We use this as the "original" to compute the reduction.
57
- originalDeniedBytes += estimatedDeniedOriginalBytes;
58
- }
59
- }
60
- // For allowed skills, compute the sum of their original SKILL.md bytes from the global catalog.
61
- // We don't know the global catalog path here; estimate at 364 KB / 44 = 8272 bytes per allowed skill.
62
- const allowedBytes = allowlist.length * 8272; // matches the R002 measurement
63
- const totalBytes = allowedBytes + stubBytes;
64
- const shadowReductionPct = originalDeniedBytes > 0 ? 1 - stubBytes / originalDeniedBytes : 0;
65
- // Token estimation: chars / 4 for full skills; bytes * 0.25 for stubs (YAML is denser).
66
- const allowedTokens = Math.round(allowedBytes / 4);
67
- const deniedTokens = Math.round(stubBytes * 0.25);
68
- const totalTokens = allowedTokens + deniedTokens;
69
- const totals = {
70
- allowedCount: allowlist.length,
71
- deniedCount: denied.length,
72
- allowedBytes,
73
- stubBytes,
74
- originalDeniedBytes,
75
- shadowReductionPct,
76
- totalBytes,
77
- };
78
- const estimatedTokens = {
79
- allowed: allowedTokens,
80
- denied: deniedTokens,
81
- total: totalTokens,
82
- };
83
- const data = {
84
- scope: raw,
85
- totals,
86
- estimatedTokens,
87
- };
88
- if (!input.json) {
89
- data.human = [
90
- `Allowed: ${allowlist.length} skills, ${(allowedBytes / 1024).toFixed(1)} KB / ${(allowedTokens / 1000).toFixed(1)}K tokens.`,
91
- `Denied: ${denied.length} skills, ${(stubBytes / 1024).toFixed(1)} KB / ${(deniedTokens / 1000).toFixed(1)}K tokens (${(shadowReductionPct * 100).toFixed(1)}% shadow-stub reduction).`,
92
- `Total: ${(totalBytes / 1024).toFixed(1)} KB / ${(totalTokens / 1000).toFixed(1)}K tokens.`,
93
- ].join('\n');
94
- }
95
- return ok('skill.context-stats', data);
96
- }
@@ -1,51 +0,0 @@
1
- /**
2
- * `peaks skill scope` CLI surface (slice 025.1).
3
- *
4
- * Four subcommands (mutually exclusive):
5
- * - `--detect` — dry-run; prints the relevance matrix, never touches files.
6
- * - `--apply` — writes the source-of-truth + IDE-native config.
7
- * - `--show` — reads the source-of-truth + native config back.
8
- * - `--reset` — removes the source-of-truth + IDE-native config.
9
- *
10
- * Exit code matrix (tech-doc §6.3):
11
- * 0 success
12
- * 1 uncaught error
13
- * 2 invalid usage (missing/incompatible flags)
14
- * 3 source-of-truth written but adapter returned NOT_SUPPORTED
15
- * 4 adapter failure other than NOT_SUPPORTED
16
- */
17
- import { Command } from 'commander';
18
- import { type ResultEnvelope } from '../../shared/result.js';
19
- import { type ProgramIO } from '../cli-helpers.js';
20
- export type SkillScopeAction = 'detect' | 'apply' | 'show' | 'reset';
21
- export interface RunSkillScopeInput {
22
- readonly subcommand: SkillScopeAction;
23
- readonly project: string;
24
- readonly strict?: boolean;
25
- readonly loose?: boolean;
26
- readonly ide?: string;
27
- readonly shadowFallback?: boolean;
28
- readonly json?: boolean;
29
- /** Test seam: override the detected allowlist (CLI re-adds peaks-* per G6). */
30
- readonly overrideAllowlist?: readonly string[];
31
- /** Test seam: force the source-of-truth write to fail (simulates atomicity test). */
32
- readonly simulateSourceOfTruthWriteFailure?: boolean;
33
- }
34
- export interface RunSkillScopeResult {
35
- readonly exitCode: number;
36
- readonly envelope: ResultEnvelope<unknown> | null;
37
- readonly stdout: string;
38
- readonly stderr: string;
39
- }
40
- /** Run the --apply subcommand. R003.3: `runApply` is exported for direct testing. */
41
- export declare function runApply(input: RunSkillScopeInput): Promise<RunSkillScopeResult>;
42
- /**
43
- * Programmatic entry point for `peaks skill scope`. Used by the CLI shim
44
- * AND by the unit tests.
45
- */
46
- export declare function runSkillScopeCommand(input: RunSkillScopeInput): Promise<RunSkillScopeResult>;
47
- /**
48
- * Register the `peaks skill scope` subcommand on the `skill` command group.
49
- * Mutually-exclusive flags: exactly one of --detect / --apply / --show / --reset.
50
- */
51
- export declare function registerSkillScopeCommands(program: Command, io: ProgramIO): void;