peaks-cli 1.4.1 → 2.0.0

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 (219) hide show
  1. package/.claude-plugin/marketplace.json +51 -0
  2. package/CHANGELOG.md +238 -0
  3. package/README-en.md +226 -0
  4. package/README.md +142 -165
  5. package/dist/src/cli/commands/agent-commands.d.ts +20 -0
  6. package/dist/src/cli/commands/agent-commands.js +48 -0
  7. package/dist/src/cli/commands/audit-commands.d.ts +18 -0
  8. package/dist/src/cli/commands/audit-commands.js +138 -0
  9. package/dist/src/cli/commands/classify-classify-commands.d.ts +19 -0
  10. package/dist/src/cli/commands/classify-classify-commands.js +151 -0
  11. package/dist/src/cli/commands/code-review-commands.d.ts +34 -0
  12. package/dist/src/cli/commands/code-review-commands.js +83 -0
  13. package/dist/src/cli/commands/config-commands.js +90 -0
  14. package/dist/src/cli/commands/context-commands.d.ts +21 -0
  15. package/dist/src/cli/commands/context-commands.js +167 -0
  16. package/dist/src/cli/commands/core-artifact-commands.js +81 -2
  17. package/dist/src/cli/commands/hook-handle.js +50 -0
  18. package/dist/src/cli/commands/loop-commands.d.ts +21 -0
  19. package/dist/src/cli/commands/loop-commands.js +128 -0
  20. package/dist/src/cli/commands/memory-commands.d.ts +13 -0
  21. package/dist/src/cli/commands/memory-commands.js +60 -0
  22. package/dist/src/cli/commands/openspec-commands.js +37 -0
  23. package/dist/src/cli/commands/preferences-commands.d.ts +2 -0
  24. package/dist/src/cli/commands/preferences-commands.js +147 -0
  25. package/dist/src/cli/commands/retrospective-commands.d.ts +9 -0
  26. package/dist/src/cli/commands/retrospective-commands.js +58 -0
  27. package/dist/src/cli/commands/skill-conformance-commands.d.ts +9 -0
  28. package/dist/src/cli/commands/skill-conformance-commands.js +39 -0
  29. package/dist/src/cli/commands/understand-commands.js +34 -0
  30. package/dist/src/cli/commands/upgrade-commands.d.ts +23 -0
  31. package/dist/src/cli/commands/upgrade-commands.js +57 -0
  32. package/dist/src/cli/commands/workflow-commands.js +70 -0
  33. package/dist/src/cli/commands/workspace-commands.js +86 -0
  34. package/dist/src/cli/program.js +46 -22
  35. package/dist/src/services/agent/ecc-agent-service.d.ts +47 -0
  36. package/dist/src/services/agent/ecc-agent-service.js +143 -0
  37. package/dist/src/services/artifacts/request-artifact-service.js +14 -0
  38. package/dist/src/services/audit/backing-detector.d.ts +24 -0
  39. package/dist/src/services/audit/backing-detector.js +59 -0
  40. package/dist/src/services/audit/classifier.d.ts +38 -0
  41. package/dist/src/services/audit/classifier.js +127 -0
  42. package/dist/src/services/audit/enforcers/active-skill-resolver.d.ts +29 -0
  43. package/dist/src/services/audit/enforcers/active-skill-resolver.js +71 -0
  44. package/dist/src/services/audit/enforcers/design-draft-confirm.d.ts +25 -0
  45. package/dist/src/services/audit/enforcers/design-draft-confirm.js +54 -0
  46. package/dist/src/services/audit/enforcers/lint-audit-regression.d.ts +21 -0
  47. package/dist/src/services/audit/enforcers/lint-audit-regression.js +86 -0
  48. package/dist/src/services/audit/enforcers/lint-catalog-governance.d.ts +27 -0
  49. package/dist/src/services/audit/enforcers/lint-catalog-governance.js +38 -0
  50. package/dist/src/services/audit/enforcers/lint-cli-back.d.ts +16 -0
  51. package/dist/src/services/audit/enforcers/lint-cli-back.js +35 -0
  52. package/dist/src/services/audit/enforcers/lint-output-style.d.ts +11 -0
  53. package/dist/src/services/audit/enforcers/lint-output-style.js +94 -0
  54. package/dist/src/services/audit/enforcers/lint-reference-integrity.d.ts +6 -0
  55. package/dist/src/services/audit/enforcers/lint-reference-integrity.js +83 -0
  56. package/dist/src/services/audit/enforcers/lint-reference-shape.d.ts +30 -0
  57. package/dist/src/services/audit/enforcers/lint-reference-shape.js +272 -0
  58. package/dist/src/services/audit/enforcers/lint-style.d.ts +49 -0
  59. package/dist/src/services/audit/enforcers/lint-style.js +173 -0
  60. package/dist/src/services/audit/enforcers/lint-workflow-shape.d.ts +5 -0
  61. package/dist/src/services/audit/enforcers/lint-workflow-shape.js +141 -0
  62. package/dist/src/services/audit/enforcers/login-gate.d.ts +23 -0
  63. package/dist/src/services/audit/enforcers/login-gate.js +40 -0
  64. package/dist/src/services/audit/enforcers/mock-placement.d.ts +25 -0
  65. package/dist/src/services/audit/enforcers/mock-placement.js +48 -0
  66. package/dist/src/services/audit/enforcers/no-root-pollution.d.ts +21 -0
  67. package/dist/src/services/audit/enforcers/no-root-pollution.js +56 -0
  68. package/dist/src/services/audit/enforcers/pre-rd-scan.d.ts +22 -0
  69. package/dist/src/services/audit/enforcers/pre-rd-scan.js +23 -0
  70. package/dist/src/services/audit/enforcers/prototype-fidelity.d.ts +25 -0
  71. package/dist/src/services/audit/enforcers/prototype-fidelity.js +75 -0
  72. package/dist/src/services/audit/enforcers/resume-detection.d.ts +21 -0
  73. package/dist/src/services/audit/enforcers/resume-detection.js +52 -0
  74. package/dist/src/services/audit/enforcers/solo-code-ban.d.ts +23 -0
  75. package/dist/src/services/audit/enforcers/solo-code-ban.js +27 -0
  76. package/dist/src/services/audit/enforcers/sub-agent-sid.d.ts +25 -0
  77. package/dist/src/services/audit/enforcers/sub-agent-sid.js +63 -0
  78. package/dist/src/services/audit/enforcers/tech-doc-presence.d.ts +28 -0
  79. package/dist/src/services/audit/enforcers/tech-doc-presence.js +35 -0
  80. package/dist/src/services/audit/red-line-catalog-p2-a.d.ts +21 -0
  81. package/dist/src/services/audit/red-line-catalog-p2-a.js +233 -0
  82. package/dist/src/services/audit/red-line-catalog-p2-b.d.ts +19 -0
  83. package/dist/src/services/audit/red-line-catalog-p2-b.js +225 -0
  84. package/dist/src/services/audit/red-line-catalog.d.ts +51 -0
  85. package/dist/src/services/audit/red-line-catalog.js +210 -0
  86. package/dist/src/services/audit/red-lines-service.d.ts +23 -0
  87. package/dist/src/services/audit/red-lines-service.js +486 -0
  88. package/dist/src/services/audit/scanners/openspec-scanner.d.ts +15 -0
  89. package/dist/src/services/audit/scanners/openspec-scanner.js +55 -0
  90. package/dist/src/services/audit/scanners/rules-tree-scanner.d.ts +16 -0
  91. package/dist/src/services/audit/scanners/rules-tree-scanner.js +56 -0
  92. package/dist/src/services/audit/scanners/skills-tree-scanner.d.ts +17 -0
  93. package/dist/src/services/audit/scanners/skills-tree-scanner.js +46 -0
  94. package/dist/src/services/audit/static-service.d.ts +57 -0
  95. package/dist/src/services/audit/static-service.js +125 -0
  96. package/dist/src/services/audit/types.d.ts +69 -0
  97. package/dist/src/services/audit/types.js +13 -0
  98. package/dist/src/services/classify/classify-service.d.ts +42 -0
  99. package/dist/src/services/classify/classify-service.js +122 -0
  100. package/dist/src/services/classify/classify-types.d.ts +79 -0
  101. package/dist/src/services/classify/classify-types.js +90 -0
  102. package/dist/src/services/code-review/ocr-service.d.ts +129 -0
  103. package/dist/src/services/code-review/ocr-service.js +362 -0
  104. package/dist/src/services/config/config-migration.d.ts +32 -0
  105. package/dist/src/services/config/config-migration.js +92 -0
  106. package/dist/src/services/config/config-restore.d.ts +10 -0
  107. package/dist/src/services/config/config-restore.js +47 -0
  108. package/dist/src/services/config/config-rollback.d.ts +13 -0
  109. package/dist/src/services/config/config-rollback.js +26 -0
  110. package/dist/src/services/config/config-service.d.ts +35 -2
  111. package/dist/src/services/config/config-service.js +81 -0
  112. package/dist/src/services/config/config-types.d.ts +58 -0
  113. package/dist/src/services/config/config-types.js +6 -0
  114. package/dist/src/services/doctor/doctor-service.js +96 -0
  115. package/dist/src/services/fuzzy-matching/fuzzy-match-service.d.ts +15 -0
  116. package/dist/src/services/fuzzy-matching/fuzzy-match-service.js +56 -0
  117. package/dist/src/services/fuzzy-matching/types.d.ts +20 -0
  118. package/dist/src/services/fuzzy-matching/types.js +1 -0
  119. package/dist/src/services/ide/adapters/hermes-adapter.d.ts +21 -0
  120. package/dist/src/services/ide/adapters/hermes-adapter.js +51 -0
  121. package/dist/src/services/ide/adapters/openclaw-adapter.d.ts +14 -0
  122. package/dist/src/services/ide/adapters/openclaw-adapter.js +42 -0
  123. package/dist/src/services/ide/ide-registry.js +7 -0
  124. package/dist/src/services/ide/ide-types.d.ts +1 -1
  125. package/dist/src/services/memory/memory-search-service.d.ts +61 -0
  126. package/dist/src/services/memory/memory-search-service.js +80 -0
  127. package/dist/src/services/openspec/openspec-propose-from-doctor-service.d.ts +31 -0
  128. package/dist/src/services/openspec/openspec-propose-from-doctor-service.js +95 -0
  129. package/dist/src/services/preferences/preferences-service.d.ts +6 -0
  130. package/dist/src/services/preferences/preferences-service.js +43 -0
  131. package/dist/src/services/preferences/preferences-types.d.ts +90 -0
  132. package/dist/src/services/preferences/preferences-types.js +38 -0
  133. package/dist/src/services/recommendations/capability-seed-items.js +0 -1
  134. package/dist/src/services/recommendations/capability-seed-mappings.js +0 -1
  135. package/dist/src/services/recommendations/capability-seed-sources.js +0 -1
  136. package/dist/src/services/retrospective/retrospective-search-service.d.ts +37 -0
  137. package/dist/src/services/retrospective/retrospective-search-service.js +75 -0
  138. package/dist/src/services/skills/skill-conformance-service.d.ts +40 -0
  139. package/dist/src/services/skills/skill-conformance-service.js +136 -0
  140. package/dist/src/services/skills/skill-runbook-service.js +44 -10
  141. package/dist/src/services/skills/sync-service.d.ts +43 -0
  142. package/dist/src/services/skills/sync-service.js +99 -0
  143. package/dist/src/services/slice/slice-check-service.js +166 -13
  144. package/dist/src/services/slice/slice-check-types.d.ts +1 -1
  145. package/dist/src/services/standards/migrate-claude-rules-service.d.ts +19 -0
  146. package/dist/src/services/standards/migrate-claude-rules-service.js +193 -0
  147. package/dist/src/services/standards/project-context.d.ts +1 -1
  148. package/dist/src/services/standards/project-context.js +0 -4
  149. package/dist/src/services/standards/project-standards-service.js +1 -3
  150. package/dist/src/services/understand/understand-scan-service.js +15 -2
  151. package/dist/src/services/understand/understand-types.d.ts +26 -0
  152. package/dist/src/services/upgrade/1x-detector-service.d.ts +7 -0
  153. package/dist/src/services/upgrade/1x-detector-service.js +94 -0
  154. package/dist/src/services/upgrade/gitignore-migrate-service.d.ts +56 -0
  155. package/dist/src/services/upgrade/gitignore-migrate-service.js +170 -0
  156. package/dist/src/services/upgrade/upgrade-service.d.ts +47 -0
  157. package/dist/src/services/upgrade/upgrade-service.js +381 -0
  158. package/dist/src/services/workspace/migrate-1-4-1-service.js +1 -1
  159. package/dist/src/services/workspace/sid-naming-guard.d.ts +14 -0
  160. package/dist/src/services/workspace/sid-naming-guard.js +31 -0
  161. package/dist/src/services/workspace/workspace-archive-service.d.ts +19 -0
  162. package/dist/src/services/workspace/workspace-archive-service.js +32 -0
  163. package/dist/src/services/workspace/workspace-clean-service.d.ts +41 -0
  164. package/dist/src/services/workspace/workspace-clean-service.js +86 -0
  165. package/dist/src/services/workspace/workspace-state-service.d.ts +7 -0
  166. package/dist/src/services/workspace/workspace-state-service.js +43 -0
  167. package/dist/src/shared/change-id.js +4 -1
  168. package/dist/src/shared/version.d.ts +1 -1
  169. package/dist/src/shared/version.js +1 -1
  170. package/package.json +10 -8
  171. package/schemas/doctor-report.schema.json +1 -1
  172. package/scripts/install-skills.mjs +296 -12
  173. package/skills/peaks-doctor/SKILL.md +59 -0
  174. package/skills/peaks-doctor/references/doctor-check-catalog.md +31 -0
  175. package/skills/peaks-doctor/references/from-doctor-flow.md +64 -0
  176. package/skills/peaks-doctor/test_prompts.json +17 -0
  177. package/skills/peaks-ide/SKILL.md +2 -0
  178. package/skills/peaks-qa/SKILL.md +9 -7
  179. package/skills/peaks-qa/references/artifact-per-request.md +19 -5
  180. package/skills/peaks-qa/references/qa-perf-test-plan.md +6 -6
  181. package/skills/peaks-qa/references/qa-runbook.md +1 -1
  182. package/skills/peaks-rd/SKILL.md +25 -10
  183. package/skills/peaks-rd/references/ocr-integration.md +214 -0
  184. package/skills/peaks-rd/references/rd-fanout-contracts.md +70 -0
  185. package/skills/peaks-rd/references/rd-runbook.md +1 -1
  186. package/skills/peaks-solo/SKILL.md +11 -5
  187. package/skills/peaks-solo/references/completion-handoff.md +3 -1
  188. package/skills/peaks-solo/references/step-0-55-1x-detection.md +82 -0
  189. package/skills/peaks-solo/references/workflow-gates-and-types.md +9 -0
  190. package/dist/src/cli/commands/shadcn-commands.d.ts +0 -3
  191. package/dist/src/cli/commands/shadcn-commands.js +0 -35
  192. package/dist/src/cli/commands/skill-context-stats-command.d.ts +0 -40
  193. package/dist/src/cli/commands/skill-context-stats-command.js +0 -96
  194. package/dist/src/cli/commands/skill-scope-commands.d.ts +0 -51
  195. package/dist/src/cli/commands/skill-scope-commands.js +0 -310
  196. package/dist/src/services/shadcn/shadcn-service.d.ts +0 -27
  197. package/dist/src/services/shadcn/shadcn-service.js +0 -128
  198. package/dist/src/services/skill-scope/adapters/_stub-helper.d.ts +0 -39
  199. package/dist/src/services/skill-scope/adapters/_stub-helper.js +0 -98
  200. package/dist/src/services/skill-scope/adapters/claude-code.d.ts +0 -59
  201. package/dist/src/services/skill-scope/adapters/claude-code.js +0 -304
  202. package/dist/src/services/skill-scope/adapters/codex.d.ts +0 -2
  203. package/dist/src/services/skill-scope/adapters/codex.js +0 -12
  204. package/dist/src/services/skill-scope/adapters/cursor.d.ts +0 -2
  205. package/dist/src/services/skill-scope/adapters/cursor.js +0 -13
  206. package/dist/src/services/skill-scope/adapters/qoder.d.ts +0 -2
  207. package/dist/src/services/skill-scope/adapters/qoder.js +0 -13
  208. package/dist/src/services/skill-scope/adapters/tongyi.d.ts +0 -2
  209. package/dist/src/services/skill-scope/adapters/tongyi.js +0 -13
  210. package/dist/src/services/skill-scope/adapters/trae.d.ts +0 -2
  211. package/dist/src/services/skill-scope/adapters/trae.js +0 -12
  212. package/dist/src/services/skill-scope/detect.d.ts +0 -81
  213. package/dist/src/services/skill-scope/detect.js +0 -513
  214. package/dist/src/services/skill-scope/registry.d.ts +0 -41
  215. package/dist/src/services/skill-scope/registry.js +0 -83
  216. package/dist/src/services/skill-scope/source-of-truth.d.ts +0 -44
  217. package/dist/src/services/skill-scope/source-of-truth.js +0 -118
  218. package/dist/src/services/skill-scope/types.d.ts +0 -195
  219. package/dist/src/services/skill-scope/types.js +0 -97
@@ -0,0 +1,61 @@
1
+ export type { ProjectMemoryKind } from './project-memory-service.js';
2
+ import type { ProjectMemoryKind } from './project-memory-service.js';
3
+ /**
4
+ * One entry in `.peaks/memory/index.json` after the on-disk `hot[]` +
5
+ * `cold[]` arrays are flattened. Mirrors the field shape that the
6
+ * existing `project-memory-service.ts` writer emits.
7
+ */
8
+ export interface MemoryIndexEntry {
9
+ name: string;
10
+ kind: ProjectMemoryKind;
11
+ description: string;
12
+ sourcePath: string;
13
+ sourceArtifact: string | null;
14
+ updatedAt: string;
15
+ }
16
+ /**
17
+ * The full snapshot of `.peaks/memory/index.json`.
18
+ */
19
+ export interface MemoryIndexSnapshot {
20
+ indexPath: string;
21
+ version: number;
22
+ updatedAt: string;
23
+ entries: MemoryIndexEntry[];
24
+ }
25
+ /**
26
+ * Input to `searchMemory`. `projectRoot` defaults to the resolved
27
+ * peaks project root (CLI resolves this before calling). `query` is
28
+ * required and non-empty.
29
+ */
30
+ export interface MemorySearchInput {
31
+ query: string;
32
+ projectRoot?: string;
33
+ limit?: number;
34
+ kind?: ProjectMemoryKind;
35
+ }
36
+ /**
37
+ * One hit returned by `searchMemory`. Mirrors the entry shape with a
38
+ * normalized score in [0, 1] (top of batch = 1.0) and the char indices
39
+ * in the searchable text that contributed to the match.
40
+ */
41
+ export interface MemorySearchResult {
42
+ name: string;
43
+ kind: ProjectMemoryKind;
44
+ description: string;
45
+ sourcePath: string;
46
+ score: number;
47
+ positions: number[];
48
+ }
49
+ /**
50
+ * Read `.peaks/memory/index.json` and flatten the on-disk `hot[<kind>][]`
51
+ * + `cold[]` shape into a single `entries[]` array. Throws structured
52
+ * errors with stable `code` markers that the CLI converts to the
53
+ * peaks envelope.
54
+ */
55
+ export declare function loadMemoryIndex(projectRoot: string): MemoryIndexSnapshot;
56
+ /**
57
+ * Run the generic fuzzy kernel against the on-disk memory index. The
58
+ * searchable text is `name + " " + description` for each entry (per
59
+ * spec §Component Details).
60
+ */
61
+ export declare function searchMemory(input: MemorySearchInput): MemorySearchResult[];
@@ -0,0 +1,80 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { fuzzyMatchWithKey } from '../fuzzy-matching/fuzzy-match-service.js';
4
+ const DEFAULT_LIMIT = 6;
5
+ /**
6
+ * Read `.peaks/memory/index.json` and flatten the on-disk `hot[<kind>][]`
7
+ * + `cold[]` shape into a single `entries[]` array. Throws structured
8
+ * errors with stable `code` markers that the CLI converts to the
9
+ * peaks envelope.
10
+ */
11
+ export function loadMemoryIndex(projectRoot) {
12
+ const indexPath = join(projectRoot, '.peaks', 'memory', 'index.json');
13
+ if (!existsSync(indexPath)) {
14
+ const err = new Error(`INDEX_MISSING: memory 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 memory 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 memory index at ${indexPath}: ${cause.message}`);
33
+ err.code = 'INDEX_INVALID';
34
+ throw err;
35
+ }
36
+ const index = parsed;
37
+ const hot = index.hot ?? {};
38
+ const flatFromHot = Object.values(hot).flat();
39
+ const flatFromCold = (index.cold ?? []);
40
+ const entries = [...flatFromHot, ...flatFromCold];
41
+ return {
42
+ indexPath,
43
+ version: index.version ?? 1,
44
+ updatedAt: index.updatedAt ?? '',
45
+ entries,
46
+ };
47
+ }
48
+ /**
49
+ * Run the generic fuzzy kernel against the on-disk memory index. The
50
+ * searchable text is `name + " " + description` for each entry (per
51
+ * spec §Component Details).
52
+ */
53
+ export function searchMemory(input) {
54
+ if (input.query === '') {
55
+ const err = new Error('EMPTY_QUERY: searchMemory requires a non-empty query (use `peaks memory index` to list all)');
56
+ err.code = 'EMPTY_QUERY';
57
+ throw err;
58
+ }
59
+ const projectRoot = input.projectRoot ?? process.cwd();
60
+ const limit = input.limit ?? DEFAULT_LIMIT;
61
+ const snapshot = loadMemoryIndex(projectRoot);
62
+ let candidates = snapshot.entries;
63
+ if (input.kind !== undefined) {
64
+ candidates = candidates.filter((e) => e.kind === input.kind);
65
+ }
66
+ // Per spec: searchable text is name + " " + description.
67
+ // The keyFn is invoked once per item per call.
68
+ const matches = fuzzyMatchWithKey(input.query, candidates, { keyFn: (e) => `${e.name} ${e.description}`, limit, caseSensitive: false });
69
+ return matches.map((m) => {
70
+ const entry = m.item;
71
+ return {
72
+ name: entry.name,
73
+ kind: entry.kind,
74
+ description: entry.description,
75
+ sourcePath: entry.sourcePath,
76
+ score: m.score,
77
+ positions: m.positions,
78
+ };
79
+ });
80
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * openspec-propose-from-doctor-service (Slice L3.3) — generates a draft
3
+ * OpenSpec change record (proposal.md) from a doctor CRITICAL finding.
4
+ *
5
+ * Per L1+L2+L3 redesign §5.4: "CRITICAL → proposal 草稿生成". The doctor
6
+ * scans the project for issues; when a CRITICAL finding is surfaced,
7
+ * peaks-cli generates a draft `openspec/changes/<id>/proposal.md` so the
8
+ * LLM doesn't have to start from scratch.
9
+ *
10
+ * The draft proposal is INFORMATIONAL — it requires the LLM to review +
11
+ * edit before `peaks openspec validate` will accept it.
12
+ */
13
+ export interface DoctorFinding {
14
+ readonly id: string;
15
+ readonly rule: string;
16
+ readonly detail: string;
17
+ readonly severity: 'pass' | 'warn' | 'fail';
18
+ }
19
+ export interface ProposeFromDoctorInput {
20
+ readonly projectRoot: string;
21
+ readonly finding: DoctorFinding;
22
+ /** Optional clock for testability. */
23
+ readonly clock?: () => string;
24
+ }
25
+ export interface ProposeFromDoctorResult {
26
+ readonly changeId: string;
27
+ readonly changeDir: string;
28
+ readonly proposalPath: string;
29
+ readonly created: boolean;
30
+ }
31
+ export declare function proposeFromDoctor(input: ProposeFromDoctorInput): ProposeFromDoctorResult;
@@ -0,0 +1,95 @@
1
+ /**
2
+ * openspec-propose-from-doctor-service (Slice L3.3) — generates a draft
3
+ * OpenSpec change record (proposal.md) from a doctor CRITICAL finding.
4
+ *
5
+ * Per L1+L2+L3 redesign §5.4: "CRITICAL → proposal 草稿生成". The doctor
6
+ * scans the project for issues; when a CRITICAL finding is surfaced,
7
+ * peaks-cli generates a draft `openspec/changes/<id>/proposal.md` so the
8
+ * LLM doesn't have to start from scratch.
9
+ *
10
+ * The draft proposal is INFORMATIONAL — it requires the LLM to review +
11
+ * edit before `peaks openspec validate` will accept it.
12
+ */
13
+ import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
14
+ import { join } from 'node:path';
15
+ import { getErrorMessage } from '../../shared/result.js';
16
+ const SLUGIFY = /[^a-z0-9]+/g;
17
+ function slugify(text) {
18
+ return text
19
+ .toLowerCase()
20
+ .replace(SLUGIFY, '-')
21
+ .replace(/^-+|-+$/g, '')
22
+ .slice(0, 50);
23
+ }
24
+ function formatProposal(input) {
25
+ return `# Doctor Finding: ${input.finding.rule}
26
+
27
+ **Date**: ${input.date}
28
+ **Change ID**: ${input.changeId}
29
+ **Source**: \`peaks doctor\` finding (CRITICAL severity: ${input.finding.severity})
30
+
31
+ ## Why
32
+
33
+ A \`peaks doctor\` scan flagged the following issue at \`${input.finding.id}\`:
34
+
35
+ > ${input.finding.detail}
36
+
37
+ The current behavior is broken or degraded. This proposal outlines a fix.
38
+
39
+ ## What Changes
40
+
41
+ - Address the doctor finding at \`${input.finding.id}\`.
42
+ - See the Why section above for the original error message.
43
+ - Acceptance criteria below describe the success conditions.
44
+
45
+ ## Acceptance Criteria
46
+
47
+ - \`peaks doctor --json\` returns \`ok: true\` for the \`${input.finding.id}\` check.
48
+ - Re-running the audit does not regress other findings.
49
+
50
+ ## Out of Scope
51
+
52
+ - Other doctor findings (each is tracked in its own OpenSpec change).
53
+ - Refactors that don't fix this specific issue.
54
+
55
+ ## Risks
56
+
57
+ - Low: this is a doctor-flagged issue with a clear acceptance criterion.
58
+
59
+ ## Status
60
+
61
+ - created: ${input.date}
62
+ - last update: ${input.date}
63
+ - state: draft
64
+ - state reason: auto-generated from peaks doctor; LLM must review + edit before validate
65
+ `;
66
+ }
67
+ function makeChangeId(finding, date) {
68
+ return `${date}-fix-${slugify(finding.id)}`;
69
+ }
70
+ export function proposeFromDoctor(input) {
71
+ const date = (input.clock ?? (() => new Date().toISOString()))().slice(0, 10);
72
+ const changeId = makeChangeId(input.finding, date);
73
+ const changeDir = join(input.projectRoot, 'openspec/changes', changeId);
74
+ const proposalPath = join(changeDir, 'proposal.md');
75
+ let created = false;
76
+ try {
77
+ if (!existsSync(changeDir)) {
78
+ mkdirSync(changeDir, { recursive: true });
79
+ }
80
+ if (!existsSync(proposalPath)) {
81
+ const content = formatProposal({
82
+ changeId,
83
+ finding: input.finding,
84
+ title: `Fix ${input.finding.rule}`,
85
+ date,
86
+ });
87
+ writeFileSync(proposalPath, content);
88
+ created = true;
89
+ }
90
+ }
91
+ catch (error) {
92
+ throw new Error(`proposeFromDoctor: ${getErrorMessage(error)}`);
93
+ }
94
+ return { changeId, changeDir, proposalPath, created };
95
+ }
@@ -0,0 +1,6 @@
1
+ import { DEFAULT_PREFERENCES, PREFERENCES_SCHEMA_VERSION, type ProjectPreferences } from './preferences-types.js';
2
+ export { DEFAULT_PREFERENCES, PREFERENCES_SCHEMA_VERSION };
3
+ export type { ProjectPreferences } from './preferences-types.js';
4
+ export declare function preferencesPath(projectRoot: string): string;
5
+ export declare function loadPreferences(projectRoot: string): ProjectPreferences;
6
+ export declare function savePreferences(projectRoot: string, overrides: Partial<ProjectPreferences>): ProjectPreferences;
@@ -0,0 +1,43 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { dirname, join } from 'node:path';
3
+ import { DEFAULT_PREFERENCES, PREFERENCES_SCHEMA_VERSION, } from './preferences-types.js';
4
+ const PREFS_REL_PATH = '.peaks/preferences.json';
5
+ export { DEFAULT_PREFERENCES, PREFERENCES_SCHEMA_VERSION };
6
+ export function preferencesPath(projectRoot) {
7
+ return join(projectRoot, PREFS_REL_PATH);
8
+ }
9
+ export function loadPreferences(projectRoot) {
10
+ const filePath = preferencesPath(projectRoot);
11
+ if (!existsSync(filePath)) {
12
+ return structuredClone(DEFAULT_PREFERENCES);
13
+ }
14
+ let raw;
15
+ try {
16
+ raw = JSON.parse(readFileSync(filePath, 'utf8'));
17
+ }
18
+ catch (err) {
19
+ throw new Error(`PREFERENCES_JSON_INVALID: failed to parse ${filePath}: ${err.message}`);
20
+ }
21
+ if (typeof raw !== 'object' ||
22
+ raw === null ||
23
+ raw.schema_version !== PREFERENCES_SCHEMA_VERSION) {
24
+ throw new Error(`PREFERENCES_SCHEMA_MISMATCH: expected schema_version=${PREFERENCES_SCHEMA_VERSION} in ${filePath}, got ${raw?.schema_version}`);
25
+ }
26
+ return mergePreferences(DEFAULT_PREFERENCES, raw);
27
+ }
28
+ export function savePreferences(projectRoot, overrides) {
29
+ const filePath = preferencesPath(projectRoot);
30
+ const current = loadPreferences(projectRoot);
31
+ const merged = mergePreferences(current, overrides);
32
+ mkdirSync(dirname(filePath), { recursive: true });
33
+ writeFileSync(filePath, JSON.stringify(merged, null, 2) + '\n', 'utf8');
34
+ return merged;
35
+ }
36
+ function mergePreferences(base, overrides) {
37
+ const definedEntries = Object.entries(overrides).filter(([, value]) => value !== undefined);
38
+ return {
39
+ ...base,
40
+ ...Object.fromEntries(definedEntries),
41
+ schema_version: PREFERENCES_SCHEMA_VERSION,
42
+ };
43
+ }
@@ -0,0 +1,90 @@
1
+ /**
2
+ * peaks-cli 2.0 project-local preferences schema.
3
+ * Per spec §8.4 — per-project state lives in `.peaks/preferences.json`,
4
+ * NOT in `~/.peaks/config.json` (which is slim global).
5
+ *
6
+ * Spec reference: docs/superpowers/specs/2026-06-11-peaks-cli-l1-l2-l3-redesign.md §8.4
7
+ */
8
+ export declare const PREFERENCES_SCHEMA_VERSION = "2.0.0";
9
+ /**
10
+ * Per-task-level UA install UX prompt decision.
11
+ * Values:
12
+ * - 'unset' — ask every session (default)
13
+ * - 'skip-this-session' — skip prompt for current session only
14
+ * - 'skip-forever' — never ask, never install
15
+ */
16
+ export type UaPromptDecision = 'unset' | 'skip-this-session' | 'skip-forever';
17
+ /**
18
+ * L1a task classification conservatism.
19
+ * Values:
20
+ * - 'default' — use default signal thresholds
21
+ * - 'strict' — always upgrade to next level (slower, safer)
22
+ * - 'lax' — always downgrade to previous level (faster, riskier)
23
+ */
24
+ export type ClassifyConservatism = 'default' | 'strict' | 'lax';
25
+ /**
26
+ * Per-touchpoint headroom-AI mode override.
27
+ * Spec §7.4 — default 'balanced'.
28
+ */
29
+ export type HeadroomMode = 'balanced' | 'aggressive' | 'conservative';
30
+ export interface HeadroomPreferences {
31
+ /** Whether headroom integration is enabled globally. Default: true */
32
+ readonly enabled: boolean;
33
+ /** Default mode if a touchpoint doesn't override. Default: 'balanced' */
34
+ readonly defaultMode: HeadroomMode;
35
+ /** Per-touchpoint mode overrides */
36
+ readonly perTouchpoint: {
37
+ memorySearch: HeadroomMode;
38
+ retrospectiveSearch: HeadroomMode;
39
+ doctorScan: HeadroomMode;
40
+ doctorRoute: HeadroomMode;
41
+ };
42
+ }
43
+ export interface ClassifyRuleOverrides {
44
+ /** File count threshold above which a task is promoted to 'feature' */
45
+ readonly feature_threshold_files?: number;
46
+ /** Line count threshold above which a task is promoted to 'feature' */
47
+ readonly feature_threshold_lines?: number;
48
+ /** Whether to require a 24h grace before cleaning recently-active sessions */
49
+ readonly runtime_clean_grace_hours?: number;
50
+ }
51
+ export interface SwarmSpeculativePreferences {
52
+ /** Whether speculative dispatch is enabled. Default: true */
53
+ readonly enabled: boolean;
54
+ /** Max concurrent speculative sub-agents. Default: 2 */
55
+ readonly maxConcurrent: number;
56
+ /** Min hit rate below which speculative auto-disables. Default: 0.5 */
57
+ readonly minHitRate: number;
58
+ }
59
+ export interface ProjectPreferences {
60
+ /**
61
+ * On-disk schema version. The JSON key is `schema_version` (snake_case) — this matches the
62
+ * raw on-disk key in `.peaks/preferences.json`, NOT the camelCase used by the rest of this
63
+ * interface. preferences-service.ts validates this value against PREFERENCES_SCHEMA_VERSION
64
+ * on load and writes the current value on save. Any mismatch throws PREFERENCES_SCHEMA_MISMATCH.
65
+ */
66
+ readonly schema_version: typeof PREFERENCES_SCHEMA_VERSION;
67
+ readonly economyMode: boolean;
68
+ readonly swarmMode: boolean;
69
+ readonly uaPrompt: UaPromptDecision;
70
+ readonly agentShieldPrompt: UaPromptDecision;
71
+ readonly classifyConservatism: ClassifyConservatism;
72
+ readonly classifyRules: ClassifyRuleOverrides;
73
+ readonly headroom: HeadroomPreferences;
74
+ readonly swarmSpeculative: SwarmSpeculativePreferences;
75
+ /** Loop Autonomous (L4 14.5) toggle. Default: false — never auto-enable. */
76
+ readonly loopAutonomousEnabled: boolean;
77
+ /**
78
+ * L2.3 P2-a: ECC AgentShield subprocess toggle. Default: false.
79
+ *
80
+ * When true, `peaks audit static` spawns `npx ecc-agentshield scan --json`
81
+ * and merges its findings into the audit report. When false (default),
82
+ * the audit runs peaks-cli-only and the subprocess is never spawned.
83
+ *
84
+ * The preference is independent of whether ECC is installed — i.e.
85
+ * `agentShieldEnabled: true` with ECC missing surfaces a soft
86
+ * "ECC not installed" warning and the audit still completes.
87
+ */
88
+ readonly agentShieldEnabled: boolean;
89
+ }
90
+ export declare const DEFAULT_PREFERENCES: ProjectPreferences;
@@ -0,0 +1,38 @@
1
+ /**
2
+ * peaks-cli 2.0 project-local preferences schema.
3
+ * Per spec §8.4 — per-project state lives in `.peaks/preferences.json`,
4
+ * NOT in `~/.peaks/config.json` (which is slim global).
5
+ *
6
+ * Spec reference: docs/superpowers/specs/2026-06-11-peaks-cli-l1-l2-l3-redesign.md §8.4
7
+ */
8
+ export const PREFERENCES_SCHEMA_VERSION = '2.0.0';
9
+ export const DEFAULT_PREFERENCES = {
10
+ schema_version: PREFERENCES_SCHEMA_VERSION,
11
+ economyMode: true,
12
+ swarmMode: true,
13
+ uaPrompt: 'unset',
14
+ agentShieldPrompt: 'unset',
15
+ classifyConservatism: 'default',
16
+ classifyRules: {
17
+ feature_threshold_files: 10,
18
+ feature_threshold_lines: 100,
19
+ runtime_clean_grace_hours: 24,
20
+ },
21
+ headroom: {
22
+ enabled: true,
23
+ defaultMode: 'balanced',
24
+ perTouchpoint: {
25
+ memorySearch: 'balanced',
26
+ retrospectiveSearch: 'balanced',
27
+ doctorScan: 'balanced',
28
+ doctorRoute: 'conservative',
29
+ },
30
+ },
31
+ swarmSpeculative: {
32
+ enabled: true,
33
+ maxConcurrent: 2,
34
+ minHitRate: 0.5,
35
+ },
36
+ loopAutonomousEnabled: false,
37
+ agentShieldEnabled: false,
38
+ };
@@ -95,7 +95,6 @@ export const seedCapabilityItems = [
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
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
97
  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
- capability('shadcn-ui.component-system', 'shadcn-ui', 'shadcn/ui Component System', 'doc', 'ui-components', ['engineer', 'designer'], 'low', 'project-local-ui-patterns', 'Use existing project components and design tokens.', 'Component System Reference', '组件系统参考', 'Provides component and design-system references for UI planning.', '为 UI 规划提供组件和设计系统参考。'),
99
98
  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.', '支持规格优先的产品与工程治理。'),
100
99
  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 代理边界再使用。'),
101
100
  capability('claude-code-best-practice.workflow-guidance', 'claude-code-best-practice', 'Claude Code Best Practice', 'doc', 'workflow-guidance', ['engineer'], 'low', 'peaks-built-in-rules', 'Use Peaks built-in workflow and review rules.', 'Claude Code Best Practice', 'Claude Code 最佳实践', 'Guidance for Claude Code engineering workflows.', 'Claude Code 工程工作流指导。'),
@@ -33,7 +33,6 @@ export const seedCapabilityLandingMappings = [
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
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
35
  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
- mapping({ capabilityId: 'shadcn-ui.component-system', sourceId: 'shadcn-ui', sourceGroup: 'mcp-server', landingKind: 'skill', target: 'peaks-ui', skillName: 'peaks-ui', guidance: 'Use as a component-system reference, not as an unreviewed generated UI default.' }),
37
36
  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.' }),
38
37
  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.' }),
39
38
  mapping({ capabilityId: 'openspec.spec-workflow', sourceId: 'openspec', sourceGroup: 'mcp-server', landingKind: 'skill', target: 'peaks-prd', skillName: 'peaks-prd', guidance: 'Use for spec-first product and engineering artifact structure.' }),
@@ -17,7 +17,6 @@ export const seedCapabilitySources = [
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
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
19
  { 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
- { sourceId: 'shadcn-ui', sourceType: 'repo', sourceGroup: 'mcp-server', title: 'shadcn/ui', url: 'https://github.com/shadcn-ui/ui', discoveryStatus: 'indexed', items: ['shadcn-ui.component-system'] },
21
20
  { 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'] },
22
21
  { 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'] },
23
22
  { sourceId: 'openspec', sourceType: 'repo', sourceGroup: 'mcp-server', title: 'OpenSpec', url: 'https://github.com/Fission-AI/OpenSpec', discoveryStatus: 'indexed', items: ['openspec.spec-workflow'] },
@@ -0,0 +1,37 @@
1
+ export type { RetrospectiveType, RetrospectiveOutcome, RetrospectiveEntry } from './retrospective-index.js';
2
+ import type { RetrospectiveType, RetrospectiveOutcome } from './retrospective-index.js';
3
+ /**
4
+ * Input to `searchRetrospective`. `projectRoot` defaults to `process.cwd()`.
5
+ * `query` is required and non-empty. `type` and `outcome` are optional
6
+ * structured filters that compose with AND.
7
+ */
8
+ export interface RetrospectiveSearchInput {
9
+ query: string;
10
+ projectRoot?: string;
11
+ limit?: number;
12
+ type?: RetrospectiveType;
13
+ outcome?: RetrospectiveOutcome;
14
+ }
15
+ /**
16
+ * One hit returned by `searchRetrospective`. The `artifactPaths` are
17
+ * preserved so the LLM can follow up with `peaks retrospective show
18
+ * <id>` (or read the artifact directly).
19
+ */
20
+ export interface RetrospectiveSearchResult {
21
+ id: string;
22
+ sessionId: string;
23
+ type: RetrospectiveType;
24
+ title: string;
25
+ summary: string;
26
+ outcome: RetrospectiveOutcome;
27
+ artifactPaths: string[];
28
+ score: number;
29
+ positions: number[];
30
+ }
31
+ /**
32
+ * Run the generic fuzzy kernel against the on-disk retrospective index.
33
+ * Searchable text is `title + " " + summary` per spec §Component Details.
34
+ * `--type` and `--outcome` filters compose with AND before the kernel
35
+ * runs (cheaper to filter, then fuzzy on a smaller set).
36
+ */
37
+ export declare function searchRetrospective(input: RetrospectiveSearchInput): RetrospectiveSearchResult[];
@@ -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
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Skill-conformance audit (Slice #12) — checks every peaks-* SKILL.md
3
+ * against the 5 standards from the L1+L2+L3 redesign §5.4 Slice #12:
4
+ *
5
+ * 1. task-level frontmatter (all 12 skills must declare one)
6
+ * 2. CLI-back 注解 coverage (each skill body must document which
7
+ * `peaks <cmd>` commands it composes; the absence is a SKILL.md
8
+ * anti-pattern — it means the LLM has to discover the CLI
9
+ * primitives by accident)
10
+ * 3. loadStrategy on-demand 标注 (skills should declare when they
11
+ * load — `eager` for always-loaded, `on-demand` for invoked)
12
+ * 4. 800-line cap (Karpathy limit per spec §2.3)
13
+ * 5. outputStyle: peaks-concise-v1 frontmatter (peak-cli display
14
+ * style for the skill's user-visible output)
15
+ *
16
+ * Plus 1 derived check:
17
+ * 6. CLI primitives declared in references/audit/ (Skill must
18
+ * surface every peaks <cmd> it composes in the references/ subdir;
19
+ * this is the "CLI-back 注解 100% 覆盖" check)
20
+ */
21
+ export type LoadStrategy = 'eager' | 'on-demand';
22
+ export type ConformanceLevel = 'pass' | 'warn' | 'fail';
23
+ export interface ConformanceCheck {
24
+ readonly id: string;
25
+ readonly skill: string;
26
+ readonly level: ConformanceLevel;
27
+ readonly message: string;
28
+ }
29
+ export interface ConformanceReport {
30
+ readonly checked: number;
31
+ readonly passed: number;
32
+ readonly warned: number;
33
+ readonly failed: number;
34
+ readonly checks: readonly ConformanceCheck[];
35
+ readonly summary: string;
36
+ }
37
+ export interface SkillConformanceInput {
38
+ readonly projectRoot: string;
39
+ }
40
+ export declare function auditSkillConformance(input: SkillConformanceInput): ConformanceReport;