cap-pro 1.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 (275) hide show
  1. package/.claude-plugin/README.md +26 -0
  2. package/.claude-plugin/marketplace.json +24 -0
  3. package/.claude-plugin/plugin.json +24 -0
  4. package/LICENSE +21 -0
  5. package/README.ja-JP.md +834 -0
  6. package/README.ko-KR.md +823 -0
  7. package/README.md +806 -0
  8. package/README.pt-BR.md +452 -0
  9. package/README.zh-CN.md +800 -0
  10. package/agents/cap-architect.md +269 -0
  11. package/agents/cap-brainstormer.md +207 -0
  12. package/agents/cap-curator.md +276 -0
  13. package/agents/cap-debugger.md +365 -0
  14. package/agents/cap-designer.md +246 -0
  15. package/agents/cap-historian.md +464 -0
  16. package/agents/cap-migrator.md +291 -0
  17. package/agents/cap-prototyper.md +197 -0
  18. package/agents/cap-validator.md +308 -0
  19. package/bin/install.js +5433 -0
  20. package/cap/bin/cap-tools.cjs +853 -0
  21. package/cap/bin/lib/arc-scanner.cjs +344 -0
  22. package/cap/bin/lib/cap-affinity-engine.cjs +862 -0
  23. package/cap/bin/lib/cap-anchor.cjs +228 -0
  24. package/cap/bin/lib/cap-annotation-writer.cjs +340 -0
  25. package/cap/bin/lib/cap-checkpoint.cjs +434 -0
  26. package/cap/bin/lib/cap-cluster-detect.cjs +945 -0
  27. package/cap/bin/lib/cap-cluster-display.cjs +52 -0
  28. package/cap/bin/lib/cap-cluster-format.cjs +245 -0
  29. package/cap/bin/lib/cap-cluster-helpers.cjs +295 -0
  30. package/cap/bin/lib/cap-cluster-io.cjs +212 -0
  31. package/cap/bin/lib/cap-completeness.cjs +540 -0
  32. package/cap/bin/lib/cap-deps.cjs +583 -0
  33. package/cap/bin/lib/cap-design-families.cjs +332 -0
  34. package/cap/bin/lib/cap-design.cjs +966 -0
  35. package/cap/bin/lib/cap-divergence-detector.cjs +400 -0
  36. package/cap/bin/lib/cap-doctor.cjs +752 -0
  37. package/cap/bin/lib/cap-feature-map-internals.cjs +19 -0
  38. package/cap/bin/lib/cap-feature-map-migrate.cjs +335 -0
  39. package/cap/bin/lib/cap-feature-map-monorepo.cjs +885 -0
  40. package/cap/bin/lib/cap-feature-map-shard.cjs +315 -0
  41. package/cap/bin/lib/cap-feature-map.cjs +1943 -0
  42. package/cap/bin/lib/cap-fitness-score.cjs +1075 -0
  43. package/cap/bin/lib/cap-impact-analysis.cjs +652 -0
  44. package/cap/bin/lib/cap-learn-review.cjs +1072 -0
  45. package/cap/bin/lib/cap-learning-signals.cjs +627 -0
  46. package/cap/bin/lib/cap-loader.cjs +227 -0
  47. package/cap/bin/lib/cap-logger.cjs +57 -0
  48. package/cap/bin/lib/cap-memory-bridge.cjs +764 -0
  49. package/cap/bin/lib/cap-memory-confidence.cjs +452 -0
  50. package/cap/bin/lib/cap-memory-dir.cjs +987 -0
  51. package/cap/bin/lib/cap-memory-engine.cjs +698 -0
  52. package/cap/bin/lib/cap-memory-extends.cjs +398 -0
  53. package/cap/bin/lib/cap-memory-graph.cjs +790 -0
  54. package/cap/bin/lib/cap-memory-migrate.cjs +2015 -0
  55. package/cap/bin/lib/cap-memory-pin.cjs +183 -0
  56. package/cap/bin/lib/cap-memory-platform.cjs +490 -0
  57. package/cap/bin/lib/cap-memory-prune.cjs +707 -0
  58. package/cap/bin/lib/cap-memory-schema.cjs +812 -0
  59. package/cap/bin/lib/cap-migrate-tags.cjs +309 -0
  60. package/cap/bin/lib/cap-migrate.cjs +540 -0
  61. package/cap/bin/lib/cap-pattern-apply.cjs +1203 -0
  62. package/cap/bin/lib/cap-pattern-pipeline.cjs +1034 -0
  63. package/cap/bin/lib/cap-plugin-manifest.cjs +80 -0
  64. package/cap/bin/lib/cap-realtime-affinity.cjs +399 -0
  65. package/cap/bin/lib/cap-reconcile.cjs +570 -0
  66. package/cap/bin/lib/cap-research-gate.cjs +218 -0
  67. package/cap/bin/lib/cap-scope-filter.cjs +402 -0
  68. package/cap/bin/lib/cap-semantic-pipeline.cjs +1038 -0
  69. package/cap/bin/lib/cap-session-extract.cjs +987 -0
  70. package/cap/bin/lib/cap-session.cjs +445 -0
  71. package/cap/bin/lib/cap-snapshot-linkage.cjs +963 -0
  72. package/cap/bin/lib/cap-stack-docs.cjs +646 -0
  73. package/cap/bin/lib/cap-tag-observer.cjs +371 -0
  74. package/cap/bin/lib/cap-tag-scanner.cjs +1766 -0
  75. package/cap/bin/lib/cap-telemetry.cjs +466 -0
  76. package/cap/bin/lib/cap-test-audit.cjs +1438 -0
  77. package/cap/bin/lib/cap-thread-migrator.cjs +307 -0
  78. package/cap/bin/lib/cap-thread-synthesis.cjs +545 -0
  79. package/cap/bin/lib/cap-thread-tracker.cjs +519 -0
  80. package/cap/bin/lib/cap-trace.cjs +399 -0
  81. package/cap/bin/lib/cap-trust-mode.cjs +336 -0
  82. package/cap/bin/lib/cap-ui-design-editor.cjs +642 -0
  83. package/cap/bin/lib/cap-ui-mind-map.cjs +712 -0
  84. package/cap/bin/lib/cap-ui-thread-nav.cjs +693 -0
  85. package/cap/bin/lib/cap-ui.cjs +1245 -0
  86. package/cap/bin/lib/cap-upgrade.cjs +1028 -0
  87. package/cap/bin/lib/cli/arg-helpers.cjs +49 -0
  88. package/cap/bin/lib/cli/frontmatter-router.cjs +31 -0
  89. package/cap/bin/lib/cli/init-router.cjs +68 -0
  90. package/cap/bin/lib/cli/phase-router.cjs +102 -0
  91. package/cap/bin/lib/cli/state-router.cjs +61 -0
  92. package/cap/bin/lib/cli/template-router.cjs +37 -0
  93. package/cap/bin/lib/cli/uat-router.cjs +29 -0
  94. package/cap/bin/lib/cli/validation-router.cjs +26 -0
  95. package/cap/bin/lib/cli/verification-router.cjs +31 -0
  96. package/cap/bin/lib/cli/workstream-router.cjs +39 -0
  97. package/cap/bin/lib/commands.cjs +961 -0
  98. package/cap/bin/lib/config.cjs +467 -0
  99. package/cap/bin/lib/convention-reader.cjs +258 -0
  100. package/cap/bin/lib/core.cjs +1241 -0
  101. package/cap/bin/lib/feature-aggregator.cjs +423 -0
  102. package/cap/bin/lib/frontmatter.cjs +337 -0
  103. package/cap/bin/lib/init.cjs +1443 -0
  104. package/cap/bin/lib/manifest-generator.cjs +383 -0
  105. package/cap/bin/lib/milestone.cjs +253 -0
  106. package/cap/bin/lib/model-profiles.cjs +69 -0
  107. package/cap/bin/lib/monorepo-context.cjs +226 -0
  108. package/cap/bin/lib/monorepo-migrator.cjs +509 -0
  109. package/cap/bin/lib/phase.cjs +889 -0
  110. package/cap/bin/lib/profile-output.cjs +989 -0
  111. package/cap/bin/lib/profile-pipeline.cjs +540 -0
  112. package/cap/bin/lib/roadmap.cjs +330 -0
  113. package/cap/bin/lib/security.cjs +394 -0
  114. package/cap/bin/lib/session-manager.cjs +292 -0
  115. package/cap/bin/lib/skeleton-generator.cjs +179 -0
  116. package/cap/bin/lib/state.cjs +1032 -0
  117. package/cap/bin/lib/template.cjs +231 -0
  118. package/cap/bin/lib/test-detector.cjs +62 -0
  119. package/cap/bin/lib/uat.cjs +283 -0
  120. package/cap/bin/lib/verify.cjs +889 -0
  121. package/cap/bin/lib/workspace-detector.cjs +371 -0
  122. package/cap/bin/lib/workstream.cjs +492 -0
  123. package/cap/commands/gsd/workstreams.md +63 -0
  124. package/cap/references/arc-standard.md +315 -0
  125. package/cap/references/cap-agent-architecture.md +101 -0
  126. package/cap/references/cap-gitignore-template +9 -0
  127. package/cap/references/cap-zero-deps.md +158 -0
  128. package/cap/references/checkpoints.md +778 -0
  129. package/cap/references/continuation-format.md +249 -0
  130. package/cap/references/contract-test-templates.md +312 -0
  131. package/cap/references/feature-map-template.md +25 -0
  132. package/cap/references/git-integration.md +295 -0
  133. package/cap/references/git-planning-commit.md +38 -0
  134. package/cap/references/model-profiles.md +174 -0
  135. package/cap/references/phase-numbering.md +126 -0
  136. package/cap/references/planning-config.md +202 -0
  137. package/cap/references/property-test-templates.md +316 -0
  138. package/cap/references/security-test-templates.md +347 -0
  139. package/cap/references/session-template.json +8 -0
  140. package/cap/references/tdd.md +263 -0
  141. package/cap/references/user-profiling.md +681 -0
  142. package/cap/references/verification-patterns.md +612 -0
  143. package/cap/templates/UAT.md +265 -0
  144. package/cap/templates/claude-md.md +175 -0
  145. package/cap/templates/codebase/architecture.md +255 -0
  146. package/cap/templates/codebase/concerns.md +310 -0
  147. package/cap/templates/codebase/conventions.md +307 -0
  148. package/cap/templates/codebase/integrations.md +280 -0
  149. package/cap/templates/codebase/stack.md +186 -0
  150. package/cap/templates/codebase/structure.md +285 -0
  151. package/cap/templates/codebase/testing.md +480 -0
  152. package/cap/templates/config.json +44 -0
  153. package/cap/templates/context.md +352 -0
  154. package/cap/templates/continue-here.md +78 -0
  155. package/cap/templates/copilot-instructions.md +7 -0
  156. package/cap/templates/debug-subagent-prompt.md +91 -0
  157. package/cap/templates/discussion-log.md +63 -0
  158. package/cap/templates/milestone-archive.md +123 -0
  159. package/cap/templates/milestone.md +115 -0
  160. package/cap/templates/phase-prompt.md +610 -0
  161. package/cap/templates/planner-subagent-prompt.md +117 -0
  162. package/cap/templates/project.md +186 -0
  163. package/cap/templates/requirements.md +231 -0
  164. package/cap/templates/research-project/ARCHITECTURE.md +204 -0
  165. package/cap/templates/research-project/FEATURES.md +147 -0
  166. package/cap/templates/research-project/PITFALLS.md +200 -0
  167. package/cap/templates/research-project/STACK.md +120 -0
  168. package/cap/templates/research-project/SUMMARY.md +170 -0
  169. package/cap/templates/research.md +552 -0
  170. package/cap/templates/roadmap.md +202 -0
  171. package/cap/templates/state.md +176 -0
  172. package/cap/templates/summary.md +364 -0
  173. package/cap/templates/user-preferences.md +498 -0
  174. package/cap/templates/verification-report.md +322 -0
  175. package/cap/workflows/add-phase.md +112 -0
  176. package/cap/workflows/add-tests.md +351 -0
  177. package/cap/workflows/add-todo.md +158 -0
  178. package/cap/workflows/audit-milestone.md +340 -0
  179. package/cap/workflows/audit-uat.md +109 -0
  180. package/cap/workflows/autonomous.md +891 -0
  181. package/cap/workflows/check-todos.md +177 -0
  182. package/cap/workflows/cleanup.md +152 -0
  183. package/cap/workflows/complete-milestone.md +767 -0
  184. package/cap/workflows/diagnose-issues.md +231 -0
  185. package/cap/workflows/discovery-phase.md +289 -0
  186. package/cap/workflows/discuss-phase-assumptions.md +653 -0
  187. package/cap/workflows/discuss-phase.md +1049 -0
  188. package/cap/workflows/do.md +104 -0
  189. package/cap/workflows/execute-phase.md +846 -0
  190. package/cap/workflows/execute-plan.md +514 -0
  191. package/cap/workflows/fast.md +105 -0
  192. package/cap/workflows/forensics.md +265 -0
  193. package/cap/workflows/health.md +181 -0
  194. package/cap/workflows/help.md +660 -0
  195. package/cap/workflows/insert-phase.md +130 -0
  196. package/cap/workflows/list-phase-assumptions.md +178 -0
  197. package/cap/workflows/list-workspaces.md +56 -0
  198. package/cap/workflows/manager.md +362 -0
  199. package/cap/workflows/map-codebase.md +377 -0
  200. package/cap/workflows/milestone-summary.md +223 -0
  201. package/cap/workflows/new-milestone.md +486 -0
  202. package/cap/workflows/new-project.md +1250 -0
  203. package/cap/workflows/new-workspace.md +237 -0
  204. package/cap/workflows/next.md +97 -0
  205. package/cap/workflows/node-repair.md +92 -0
  206. package/cap/workflows/note.md +156 -0
  207. package/cap/workflows/pause-work.md +176 -0
  208. package/cap/workflows/plan-milestone-gaps.md +273 -0
  209. package/cap/workflows/plan-phase.md +857 -0
  210. package/cap/workflows/plant-seed.md +169 -0
  211. package/cap/workflows/pr-branch.md +129 -0
  212. package/cap/workflows/profile-user.md +449 -0
  213. package/cap/workflows/progress.md +507 -0
  214. package/cap/workflows/quick.md +757 -0
  215. package/cap/workflows/remove-phase.md +155 -0
  216. package/cap/workflows/remove-workspace.md +90 -0
  217. package/cap/workflows/research-phase.md +82 -0
  218. package/cap/workflows/resume-project.md +326 -0
  219. package/cap/workflows/review.md +228 -0
  220. package/cap/workflows/session-report.md +146 -0
  221. package/cap/workflows/settings.md +283 -0
  222. package/cap/workflows/ship.md +228 -0
  223. package/cap/workflows/stats.md +60 -0
  224. package/cap/workflows/transition.md +671 -0
  225. package/cap/workflows/ui-phase.md +298 -0
  226. package/cap/workflows/ui-review.md +161 -0
  227. package/cap/workflows/update.md +323 -0
  228. package/cap/workflows/validate-phase.md +170 -0
  229. package/cap/workflows/verify-phase.md +254 -0
  230. package/cap/workflows/verify-work.md +637 -0
  231. package/commands/cap/annotate.md +165 -0
  232. package/commands/cap/brainstorm.md +393 -0
  233. package/commands/cap/checkpoint.md +106 -0
  234. package/commands/cap/completeness.md +94 -0
  235. package/commands/cap/continue.md +72 -0
  236. package/commands/cap/debug.md +588 -0
  237. package/commands/cap/deps.md +169 -0
  238. package/commands/cap/design.md +479 -0
  239. package/commands/cap/init.md +354 -0
  240. package/commands/cap/iterate.md +249 -0
  241. package/commands/cap/learn.md +459 -0
  242. package/commands/cap/memory.md +275 -0
  243. package/commands/cap/migrate-feature-map.md +91 -0
  244. package/commands/cap/migrate-memory.md +108 -0
  245. package/commands/cap/migrate-tags.md +91 -0
  246. package/commands/cap/migrate.md +131 -0
  247. package/commands/cap/prototype.md +510 -0
  248. package/commands/cap/reconcile.md +121 -0
  249. package/commands/cap/review.md +360 -0
  250. package/commands/cap/save.md +72 -0
  251. package/commands/cap/scan.md +404 -0
  252. package/commands/cap/start.md +356 -0
  253. package/commands/cap/status.md +118 -0
  254. package/commands/cap/test-audit.md +262 -0
  255. package/commands/cap/test.md +394 -0
  256. package/commands/cap/trace.md +133 -0
  257. package/commands/cap/ui.md +167 -0
  258. package/hooks/dist/cap-check-update.js +115 -0
  259. package/hooks/dist/cap-context-monitor.js +185 -0
  260. package/hooks/dist/cap-learn-review-hook.js +114 -0
  261. package/hooks/dist/cap-learning-hook.js +192 -0
  262. package/hooks/dist/cap-memory.js +299 -0
  263. package/hooks/dist/cap-prompt-guard.js +97 -0
  264. package/hooks/dist/cap-statusline.js +157 -0
  265. package/hooks/dist/cap-tag-observer.js +115 -0
  266. package/hooks/dist/cap-version-check.js +112 -0
  267. package/hooks/dist/cap-workflow-guard.js +175 -0
  268. package/hooks/hooks.json +55 -0
  269. package/package.json +58 -0
  270. package/scripts/base64-scan.sh +262 -0
  271. package/scripts/build-hooks.js +93 -0
  272. package/scripts/cap-removal-checklist.md +202 -0
  273. package/scripts/prompt-injection-scan.sh +199 -0
  274. package/scripts/run-tests.cjs +181 -0
  275. package/scripts/secret-scan.sh +227 -0
@@ -0,0 +1,752 @@
1
+ // @cap-context CAP v2.0 doctor utility -- checks all external dependencies CAP needs at runtime.
2
+ // @cap-decision Checks are split into required (Node.js, npm, git) and optional (ctx7, c8, vitest, fast-check).
3
+ // @cap-decision Project-specific checks only run when projectRoot is provided and package.json exists.
4
+ // @cap-constraint Zero external dependencies -- uses only Node.js built-ins (child_process, fs, path, os).
5
+
6
+ 'use strict';
7
+
8
+ // @cap-feature(feature:F-005) Doctor Health Check — verify required and optional external dependencies
9
+ // @cap-feature(feature:F-019) Module Integrity Verification — verify CAP CJS modules exist and load correctly
10
+ // @cap-feature(feature:F-058) Claude-Code Plugin Manifest — detect npx vs plugin install modes and surface coexistence
11
+
12
+ // @cap-history(sessions:8, edits:22, since:2026-04-20, learned:2026-05-08) Frequently modified — 8 sessions, 22 edits
13
+ const { execSync } = require('node:child_process');
14
+ const fs = require('node:fs');
15
+ const path = require('node:path');
16
+ const os = require('node:os');
17
+
18
+ // Tolerant require: the install-hardening test fixture copies cap-doctor.cjs
19
+ // alone to verify missing-module detection. If we require cap-plugin-manifest
20
+ // eagerly, the load of doctor itself throws and the integrity checker reports
21
+ // a single "cannot load manifest" failure instead of the 30+ expected misses.
22
+ // A tolerant null fallback keeps CAP_MODULE_MANIFEST loadable in isolation;
23
+ // detectInstallMode guards its plugin-name use against the null case below.
24
+ let pluginManifest;
25
+ try { pluginManifest = require('./cap-plugin-manifest.cjs'); } catch (_e) { pluginManifest = null; }
26
+
27
+ // @cap-todo(ac:F-019/AC-5) Module manifest — authoritative list of expected CAP modules
28
+ // @cap-decision Manifest is a flat array of filenames maintained manually. When a new module is added to
29
+ // cap/bin/lib/, it must be added here. This is intentional: an explicit manifest catches accidental deletions.
30
+ const CAP_MODULE_MANIFEST = [
31
+ 'arc-scanner.cjs',
32
+ 'cap-affinity-engine.cjs',
33
+ 'cap-anchor.cjs',
34
+ 'cap-annotation-writer.cjs',
35
+ 'cap-checkpoint.cjs',
36
+ 'cap-cluster-detect.cjs',
37
+ 'cap-cluster-display.cjs',
38
+ 'cap-cluster-format.cjs',
39
+ 'cap-cluster-helpers.cjs',
40
+ 'cap-cluster-io.cjs',
41
+ 'cap-completeness.cjs',
42
+ 'cap-deps.cjs',
43
+ 'cap-design.cjs',
44
+ 'cap-design-families.cjs',
45
+ 'cap-divergence-detector.cjs',
46
+ 'cap-doctor.cjs',
47
+ 'cap-feature-map.cjs',
48
+ // @cap-feature(feature:F-083) Shared internals module — hosts constants/primitives both
49
+ // cap-feature-map.cjs and cap-feature-map-monorepo.cjs need without forcing a lazy-require
50
+ // just to read a string literal.
51
+ // @cap-decision(F-083/followup) F-083-FIX-A: Bumped 85 -> 86 when cap-feature-map-internals.cjs
52
+ // was added (de-duplicates `FEATURE_MAP_FILE` between core and monorepo modules).
53
+ 'cap-feature-map-internals.cjs',
54
+ // @cap-feature(feature:F-089) Sharded Feature Map migration — monolithic → sharded layout.
55
+ // @cap-decision(F-089) Bumped manifest count when cap-feature-map-migrate.cjs was added.
56
+ 'cap-feature-map-migrate.cjs',
57
+ // @cap-feature(feature:F-083) Monorepo aggregation module extracted from cap-feature-map.cjs.
58
+ // @cap-decision(F-083) Bumped 84 -> 85 when cap-feature-map-monorepo.cjs was added.
59
+ 'cap-feature-map-monorepo.cjs',
60
+ // @cap-feature(feature:F-089) Sharded Feature Map — pure shard helpers (ID validator, index parse/serialize).
61
+ // @cap-decision(F-089) Bumped manifest count when cap-feature-map-shard.cjs was added.
62
+ 'cap-feature-map-shard.cjs',
63
+ // @cap-feature(feature:F-072) Compute Two-Layer Fitness Score — pure-compute scorer driving F-074 unlearn.
64
+ // @cap-decision(F-072) Bumped 77 -> 78 when cap-fitness-score.cjs was added (Two-Layer Fitness Score for Pattern Unlearn).
65
+ 'cap-fitness-score.cjs',
66
+ 'cap-impact-analysis.cjs',
67
+ // @cap-feature(feature:F-073) Review Patterns via Learn Command — board renderer + Stop-hook gate.
68
+ // @cap-decision(F-073) Bumped 79 -> 80 when cap-learn-review.cjs was added (closes the V5 self-learning loop).
69
+ 'cap-learn-review.cjs',
70
+ // @cap-feature(feature:F-070) Collect Learning Signals — override/memory-ref/regret JSONL collectors + getSignals API.
71
+ 'cap-learning-signals.cjs',
72
+ 'cap-loader.cjs',
73
+ 'cap-logger.cjs',
74
+ // @cap-feature(feature:F-080) Bridge to Claude-native Memory — read-only consumer of ~/.claude/projects/<slug>/memory/.
75
+ // @cap-decision(F-083/followup) Manifest sync: cap-memory-bridge.cjs (F-080) was on disk but missing from the manifest;
76
+ // added during the F-083-FIX-A internals extraction work to keep the on-disk-vs-manifest contract green.
77
+ 'cap-memory-bridge.cjs',
78
+ 'cap-memory-confidence.cjs',
79
+ 'cap-memory-dir.cjs',
80
+ 'cap-memory-engine.cjs',
81
+ // @cap-feature(feature:F-078) Extends-Chain Resolver — resolves `extends: platform/<topic>` chains in a single pass.
82
+ // @cap-decision(F-078) Bumped 82 -> 83 when cap-memory-extends.cjs was added (Platform-Bucket reader path).
83
+ 'cap-memory-extends.cjs',
84
+ 'cap-memory-graph.cjs',
85
+ // @cap-feature(feature:F-077) V6 Memory Migration Tool — one-shot migration from V5 monolith to V6 per-feature layout.
86
+ // @cap-decision(F-077) Bumped 81 -> 82 when cap-memory-migrate.cjs was added (V6 migration tool with hybrid classifier).
87
+ 'cap-memory-migrate.cjs',
88
+ 'cap-memory-pin.cjs',
89
+ // @cap-feature(feature:F-078) Platform-Bucket for Cross-Cutting Decisions — explicit-only platform-topic file IO + classifier.
90
+ // @cap-decision(F-078) Bumped 83 -> 84 when cap-memory-platform.cjs was added (Platform-Bucket file IO + writer).
91
+ 'cap-memory-platform.cjs',
92
+ 'cap-memory-prune.cjs',
93
+ // @cap-feature(feature:F-076) V6 Per-Feature Memory Format — schema + validator + round-trip-safe parser/serializer.
94
+ // @cap-decision(F-076) Bumped 80 -> 81 when cap-memory-schema.cjs was added (V6 memory-format pivot foundation).
95
+ 'cap-memory-schema.cjs',
96
+ 'cap-migrate-tags.cjs',
97
+ 'cap-migrate.cjs',
98
+ // @cap-feature(feature:F-074) Enable Pattern Unlearn and Auto-Retract — apply audit + reverse patch + retract list.
99
+ // @cap-decision(F-074) Bumped 78 -> 79 when cap-pattern-apply.cjs was added (F-074 closes the V5 self-learning loop).
100
+ 'cap-pattern-apply.cjs',
101
+ // @cap-feature(feature:F-071) Pattern Pipeline — heuristic Stage 1 + LLM-briefing Stage 2.
102
+ // @cap-decision(F-071) Bumped 76 -> 77 when cap-pattern-pipeline.cjs was added.
103
+ 'cap-pattern-pipeline.cjs',
104
+ 'cap-plugin-manifest.cjs',
105
+ 'cap-realtime-affinity.cjs',
106
+ 'cap-reconcile.cjs',
107
+ 'cap-research-gate.cjs',
108
+ // @cap-feature(feature:F-085) Scope filter shared by cap-tag-scanner and cap-migrate-tags.
109
+ 'cap-scope-filter.cjs',
110
+ 'cap-semantic-pipeline.cjs',
111
+ 'cap-session-extract.cjs',
112
+ 'cap-session.cjs',
113
+ // @cap-feature(feature:F-079) Wire Snapshot Linkage to Features and Platform — resolveLinkageOptions + processSnapshots.
114
+ // @cap-decision(F-083/followup) Manifest sync: cap-snapshot-linkage.cjs (F-079) was on disk but missing from the manifest;
115
+ // added during the F-083-FIX-A internals extraction work to keep the on-disk-vs-manifest contract green.
116
+ 'cap-snapshot-linkage.cjs',
117
+ 'cap-stack-docs.cjs',
118
+ 'cap-tag-observer.cjs',
119
+ 'cap-tag-scanner.cjs',
120
+ // @cap-feature(feature:F-061) Token Telemetry — LLM-call metrics + per-session aggregates.
121
+ 'cap-telemetry.cjs',
122
+ 'cap-test-audit.cjs',
123
+ 'cap-thread-migrator.cjs',
124
+ 'cap-thread-synthesis.cjs',
125
+ 'cap-thread-tracker.cjs',
126
+ 'cap-trace.cjs',
127
+ // @cap-feature(feature:F-075) Trust-Mode Configuration Slot — open-closed extension point for B/C activation.
128
+ 'cap-trust-mode.cjs',
129
+ // @cap-feature(feature:F-065) CAP-UI Core module entry.
130
+ 'cap-ui.cjs',
131
+ // @cap-feature(feature:F-068) CAP-UI Design Editor (DESIGN.md-only edit surface).
132
+ 'cap-ui-design-editor.cjs',
133
+ // @cap-feature(feature:F-066) CAP-UI Tag Mind-Map module (extracted from cap-ui.cjs during F-068 hand-off).
134
+ 'cap-ui-mind-map.cjs',
135
+ // @cap-feature(feature:F-067) CAP-UI Thread + Cluster Navigator module (extracted from cap-ui.cjs during F-068 hand-off).
136
+ 'cap-ui-thread-nav.cjs',
137
+ // @cap-feature(feature:F-084) Project Onboarding & Migration Orchestrator —
138
+ // planner + state-manager for /cap:upgrade. Companion markdown command at
139
+ // commands/cap/upgrade.md and SessionStart-hook at hooks/cap-version-check.js.
140
+ // @cap-decision(F-084) Bumped 88 -> 89 when cap-upgrade.cjs was added.
141
+ 'cap-upgrade.cjs',
142
+ 'commands.cjs',
143
+ 'config.cjs',
144
+ 'convention-reader.cjs',
145
+ 'core.cjs',
146
+ 'feature-aggregator.cjs',
147
+ 'frontmatter.cjs',
148
+ 'init.cjs',
149
+ 'manifest-generator.cjs',
150
+ 'milestone.cjs',
151
+ 'model-profiles.cjs',
152
+ 'monorepo-context.cjs',
153
+ 'monorepo-migrator.cjs',
154
+ 'phase.cjs',
155
+ 'profile-output.cjs',
156
+ 'profile-pipeline.cjs',
157
+ 'roadmap.cjs',
158
+ 'security.cjs',
159
+ 'session-manager.cjs',
160
+ 'skeleton-generator.cjs',
161
+ 'state.cjs',
162
+ 'template.cjs',
163
+ 'test-detector.cjs',
164
+ 'uat.cjs',
165
+ 'verify.cjs',
166
+ 'workspace-detector.cjs',
167
+ 'workstream.cjs',
168
+ ];
169
+
170
+ /**
171
+ * @typedef {Object} ToolCheck
172
+ * @property {string} name - Tool name
173
+ * @property {string} version - Detected version (or 'not found')
174
+ * @property {boolean} ok - Whether the tool is available
175
+ * @property {boolean} required - Whether CAP requires it
176
+ * @property {string} purpose - What CAP uses it for
177
+ * @property {string} installHint - How to install if missing
178
+ */
179
+
180
+ /**
181
+ * @typedef {Object} DoctorReport
182
+ * @property {ToolCheck[]} tools - All checked tools
183
+ * @property {number} requiredOk - Count of required tools that are OK
184
+ * @property {number} requiredTotal - Total required tools
185
+ * @property {number} optionalOk - Count of optional tools that are OK
186
+ * @property {number} optionalTotal - Total optional tools
187
+ * @property {boolean} healthy - True if all required tools are OK
188
+ * @property {string[]} installCommands - Commands to install missing tools
189
+ * @property {ModuleCheck[]} [modules] - Module integrity check results
190
+ * @property {PlatformPathCheck} [platformPaths] - Platform path resolution results
191
+ * @property {number} [modulesOk] - Count of modules that passed integrity check
192
+ * @property {number} [modulesTotal] - Total modules checked
193
+ */
194
+
195
+ /**
196
+ * @typedef {Object} ModuleCheck
197
+ * @property {string} name - Module filename (e.g., 'cap-doctor.cjs')
198
+ * @property {string} fullPath - Absolute path to the module
199
+ * @property {boolean} exists - Whether the file exists on disk
200
+ * @property {boolean} loads - Whether require() succeeds
201
+ * @property {boolean} ok - True if both exists and loads
202
+ * @property {string} [error] - Error message if exists or loads failed
203
+ */
204
+
205
+ /**
206
+ * @typedef {Object} PlatformPathCheck
207
+ * @property {string} envHome - Value of process.env.HOME
208
+ * @property {string} osHomedir - Value of os.homedir()
209
+ * @property {boolean} homeMatch - Whether envHome and osHomedir agree
210
+ * @property {string} installDir - Resolved install directory
211
+ * @property {boolean} isSymlink - Whether the install directory is a symlink
212
+ * @property {string} [symlinkTarget] - Real path if installDir is a symlink
213
+ * @property {boolean} ok - True if no discrepancies
214
+ * @property {string[]} warnings - Any platform path warnings
215
+ */
216
+
217
+ /**
218
+ * Check if a CLI tool is available and get its version.
219
+ * @param {string} command - Version check command (e.g., 'node --version')
220
+ * @param {number} [timeout=10000] - Timeout in ms
221
+ * @returns {{ ok: boolean, version: string }}
222
+ */
223
+ function checkTool(command, timeout = 10000) {
224
+ try {
225
+ const output = execSync(command, {
226
+ encoding: 'utf8',
227
+ timeout,
228
+ stdio: ['pipe', 'pipe', 'pipe'],
229
+ }).trim();
230
+ // Extract version number (strip 'v' prefix if present)
231
+ const version = output.replace(/^v/, '').split('\n')[0].trim();
232
+ return { ok: true, version };
233
+ } catch (_e) {
234
+ return { ok: false, version: 'not found' };
235
+ }
236
+ }
237
+
238
+ /**
239
+ * Detect the CAP install directory.
240
+ * @cap-decision Auto-detection tries the global install path first ($HOME/.claude/cap/cap/bin/lib),
241
+ * then falls back to the directory containing this file. This handles both global npx installs
242
+ * and local development (running from the repo checkout).
243
+ * @returns {string} Absolute path to the cap/bin/lib directory
244
+ */
245
+ function detectInstallDir() {
246
+ // Global install path: $HOME/.claude/cap/cap/bin/lib/
247
+ const homeDir = process.env.HOME || os.homedir();
248
+ const globalDir = path.join(homeDir, '.claude', 'cap', 'cap', 'bin', 'lib');
249
+ if (fs.existsSync(globalDir)) {
250
+ return globalDir;
251
+ }
252
+ // Fallback: this file's own directory (local dev / repo checkout)
253
+ return __dirname;
254
+ }
255
+
256
+ // @cap-todo(ac:F-019/AC-1) Verify every required CJS module exists at the expected install path
257
+ // @cap-todo(ac:F-019/AC-2) Attempt require() on each module and report load failures
258
+ // @cap-todo(ac:F-019/AC-3) Report clear PASS/FAIL summary per module with error reason
259
+ /**
260
+ * Check integrity of all CAP CJS modules.
261
+ * Verifies each module in the manifest exists on disk and can be loaded via require().
262
+ * @param {string} [installDir] - Directory containing the CJS modules (auto-detected if omitted)
263
+ * @returns {{ modules: ModuleCheck[], modulesOk: number, modulesTotal: number }}
264
+ */
265
+ function checkModuleIntegrity(installDir) {
266
+ const dir = installDir || detectInstallDir();
267
+ const modules = [];
268
+
269
+ for (const name of CAP_MODULE_MANIFEST) {
270
+ const fullPath = path.join(dir, name);
271
+ const check = { name, fullPath, exists: false, loads: false, ok: false };
272
+
273
+ // Step 1: Check file existence
274
+ if (!fs.existsSync(fullPath)) {
275
+ check.error = `File not found: ${fullPath}`;
276
+ modules.push(check);
277
+ continue;
278
+ }
279
+ check.exists = true;
280
+
281
+ // Step 2: Attempt require() to verify the module parses and loads
282
+ try {
283
+ require(path.resolve(fullPath));
284
+ check.loads = true;
285
+ check.ok = true;
286
+ } catch (err) {
287
+ check.error = `Load error: ${err.message.split('\n')[0]}`;
288
+ }
289
+
290
+ modules.push(check);
291
+ }
292
+
293
+ return {
294
+ modules,
295
+ modulesOk: modules.filter(m => m.ok).length,
296
+ modulesTotal: modules.length,
297
+ };
298
+ }
299
+
300
+ // @cap-todo(ac:F-019/AC-6) Test platform-specific path resolution (Linux vs macOS, symlinks)
301
+ /**
302
+ * Check platform-specific path resolution for the CAP install directory.
303
+ * Compares process.env.HOME with os.homedir() and checks for symlinks.
304
+ * @param {string} [installDir] - Directory to check (auto-detected if omitted)
305
+ * @returns {PlatformPathCheck}
306
+ */
307
+ function checkPlatformPaths(installDir) {
308
+ const dir = installDir || detectInstallDir();
309
+ const envHome = process.env.HOME || '';
310
+ const osHome = os.homedir();
311
+ const warnings = [];
312
+
313
+ // Check HOME consistency
314
+ const homeMatch = envHome === osHome;
315
+ if (!homeMatch) {
316
+ warnings.push(
317
+ `$HOME (${envHome}) differs from os.homedir() (${osHome}). ` +
318
+ 'This can happen under sudo, in Docker containers, or with nvm. ' +
319
+ 'CAP uses $HOME for install paths — ensure it points to the correct user directory.'
320
+ );
321
+ }
322
+
323
+ // Check if install dir is a symlink
324
+ let isSymlink = false;
325
+ let symlinkTarget;
326
+ try {
327
+ const stat = fs.lstatSync(dir);
328
+ isSymlink = stat.isSymbolicLink();
329
+ if (isSymlink) {
330
+ symlinkTarget = fs.realpathSync(dir);
331
+ warnings.push(
332
+ `Install directory is a symlink: ${dir} -> ${symlinkTarget}`
333
+ );
334
+ }
335
+ } catch (_e) {
336
+ // Directory doesn't exist — the module integrity check will catch this
337
+ }
338
+
339
+ return {
340
+ envHome,
341
+ osHomedir: osHome,
342
+ homeMatch,
343
+ installDir: dir,
344
+ isSymlink,
345
+ symlinkTarget,
346
+ ok: warnings.length === 0,
347
+ warnings,
348
+ };
349
+ }
350
+
351
+ // @cap-todo(ac:F-058/AC-5) cap-doctor shall detect both install modes (npx vs plugin) and show the active mode.
352
+ // @cap-todo(ac:F-058/AC-6) Coexistence check — surface warning when both npx and plugin install modes are active.
353
+ /**
354
+ * @typedef {Object} InstallModeReport
355
+ * @property {boolean} npx - True when npx install footprint ($HOME/.claude/cap/) is present.
356
+ * @property {boolean} plugin - True when plugin install footprint is detected.
357
+ * @property {boolean} coexist - True when both npx and plugin are active simultaneously.
358
+ * @property {string} active - Primary active mode: 'npx', 'plugin', 'both', or 'none'.
359
+ * @property {string[]} pluginPaths - Absolute paths where plugin footprint was detected.
360
+ * @property {string} [npxPath] - Absolute path to the npx install dir when present.
361
+ * @property {string[]} warnings - Human-readable warnings (coexistence, missing, etc).
362
+ */
363
+
364
+ /**
365
+ * @cap-decision Plugin footprint detection uses two filesystem-only heuristics:
366
+ * (1) presence of a Claude plugin cache entry under $HOME/.claude/plugins/cache/cap@*
367
+ * (any plugin installed via /plugin install or marketplace), and
368
+ * (2) presence of .claude-plugin/plugin.json in cwd with name === PLUGIN_NAME
369
+ * (local-dev checkout). The cwd manifest's name gate prevents foreign plugins
370
+ * living in the same repo from false-positive-registering as a CAP install.
371
+ * We do NOT read the CLAUDE_PLUGIN_ROOT env var. That variable is only set when
372
+ * Claude Code spawns a hook inside a plugin, never during a plain `npx cap:doctor`
373
+ * run from a shell, so relying on it would systematically false-negative on the
374
+ * CLI path where this function gets exercised most.
375
+ * @cap-decision npx footprint is the existing detection used by detectInstallDir() — $HOME/.claude/cap/
376
+ * written by the installer. No change to that detection.
377
+ *
378
+ * Detect which install mode(s) of CAP are active on this machine.
379
+ * @param {Object} [opts]
380
+ * @param {string} [opts.homeDir] - Override HOME for testing.
381
+ * @param {string} [opts.cwd] - Override cwd for testing.
382
+ * @returns {InstallModeReport}
383
+ */
384
+ function detectInstallMode(opts) {
385
+ const options = opts || {};
386
+ const homeDir = options.homeDir || process.env.HOME || os.homedir();
387
+ const cwd = options.cwd || process.cwd();
388
+ const warnings = [];
389
+
390
+ // npx footprint: installer writes to $HOME/.claude/cap/
391
+ const npxPath = path.join(homeDir, '.claude', 'cap');
392
+ const npxPresent = fs.existsSync(npxPath);
393
+
394
+ // Plugin footprint: Claude Code caches installed plugins at $HOME/.claude/plugins/cache/<name>@<source>/
395
+ const pluginPaths = [];
396
+ // Fallback to the hard-coded plugin name when cap-plugin-manifest.cjs is not
397
+ // loadable (install-hardening fixture path). The name hasn't changed since
398
+ // F-058 and is covered by a dedicated contract test there.
399
+ const pluginName = (pluginManifest && pluginManifest.PLUGIN_NAME) || 'cap-pro';
400
+ const legacyPluginName = (pluginManifest && pluginManifest.LEGACY_PLUGIN_NAME) || 'cap';
401
+ // Accept both the current plugin manifest (cap-pro) and the legacy one (cap)
402
+ // so a user with a leftover `code-as-plan@7.x` install is still detected.
403
+ const isCapManifest = (pluginManifest && pluginManifest.isCapPluginManifest)
404
+ || ((m) => !!(m && typeof m === 'object' && (m.name === pluginName || m.name === legacyPluginName)));
405
+ const pluginCacheDir = path.join(homeDir, '.claude', 'plugins', 'cache');
406
+ if (fs.existsSync(pluginCacheDir)) {
407
+ try {
408
+ const entries = fs.readdirSync(pluginCacheDir);
409
+ const capPrefix = `${pluginName}@`;
410
+ const legacyPrefix = `${legacyPluginName}@`;
411
+ for (const entry of entries) {
412
+ if (
413
+ entry === pluginName || entry.startsWith(capPrefix) ||
414
+ entry === legacyPluginName || entry.startsWith(legacyPrefix)
415
+ ) {
416
+ pluginPaths.push(path.join(pluginCacheDir, entry));
417
+ }
418
+ }
419
+ } catch (err) {
420
+ // Surface unreadable cache directories so the user learns why detection came up empty
421
+ // instead of silently reporting "no plugin installed".
422
+ const code = err && err.code ? err.code : 'unknown';
423
+ warnings.push(`Plugin cache directory is unreadable (${code}): ${pluginCacheDir}`);
424
+ }
425
+ }
426
+
427
+ // Local-dev plugin footprint: .claude-plugin/plugin.json in cwd with name === PLUGIN_NAME.
428
+ // Foreign manifests (a different plugin living in the same repo) must not register as a CAP install.
429
+ const localManifest = path.join(cwd, '.claude-plugin', 'plugin.json');
430
+ if (fs.existsSync(localManifest)) {
431
+ try {
432
+ const raw = fs.readFileSync(localManifest, 'utf8');
433
+ const parsed = JSON.parse(raw);
434
+ if (isCapManifest(parsed)) {
435
+ pluginPaths.push(localManifest);
436
+ }
437
+ } catch (_err) {
438
+ // Malformed manifest JSON: do not count as a CAP footprint. The other doctor checks will
439
+ // separately flag the broken file via module/config validation when in scope.
440
+ }
441
+ }
442
+
443
+ const pluginPresent = pluginPaths.length > 0;
444
+ const coexist = npxPresent && pluginPresent;
445
+
446
+ let active;
447
+ if (coexist) {
448
+ active = 'both';
449
+ } else if (npxPresent) {
450
+ active = 'npx';
451
+ } else if (pluginPresent) {
452
+ active = 'plugin';
453
+ } else {
454
+ active = 'none';
455
+ }
456
+
457
+ // @cap-todo(ac:F-058/AC-6) Emit warning (not hard failure) when both modes coexist.
458
+ if (coexist) {
459
+ warnings.push(
460
+ 'Both npx and plugin install modes are active. ' +
461
+ 'Commands and hooks may be registered twice. ' +
462
+ 'Recommended: pick one install path (npx is primary) and remove the other to avoid duplicate registration.'
463
+ );
464
+ }
465
+
466
+ const report = {
467
+ npx: npxPresent,
468
+ plugin: pluginPresent,
469
+ coexist,
470
+ active,
471
+ pluginPaths,
472
+ warnings,
473
+ };
474
+ if (npxPresent) {
475
+ report.npxPath = npxPath;
476
+ }
477
+ return report;
478
+ }
479
+
480
+ /**
481
+ * Run full doctor check.
482
+ * @param {string} [projectRoot] - Optional project root for project-specific checks
483
+ * @returns {DoctorReport}
484
+ */
485
+ function runDoctor(projectRoot) {
486
+ const tools = [];
487
+
488
+ // Required tools
489
+ const node = checkTool('node --version');
490
+ tools.push({
491
+ name: 'Node.js',
492
+ ...node,
493
+ required: true,
494
+ purpose: 'CAP runtime (>= 20.0.0 required)',
495
+ installHint: 'https://nodejs.org/ or: curl -fsSL https://fnm.vercel.app/install | bash && fnm install --lts',
496
+ });
497
+
498
+ const npm = checkTool('npm --version');
499
+ tools.push({
500
+ name: 'npm',
501
+ ...npm,
502
+ required: true,
503
+ purpose: 'Package management and npx tool execution',
504
+ installHint: 'Comes with Node.js',
505
+ });
506
+
507
+ const git = checkTool('git --version');
508
+ // git --version returns "git version 2.45.0"
509
+ if (git.ok) git.version = git.version.replace('git version ', '');
510
+ tools.push({
511
+ name: 'git',
512
+ ...git,
513
+ required: true,
514
+ purpose: 'Version control, commit history for /cap:report and /cap:review',
515
+ installHint: 'https://git-scm.com/downloads or: brew install git',
516
+ });
517
+
518
+ // Optional tools -- CAP works without them but with reduced functionality
519
+ const ctx7 = checkTool('npx ctx7@latest --version', 15000);
520
+ tools.push({
521
+ name: 'Context7 (ctx7)',
522
+ ...ctx7,
523
+ required: false,
524
+ purpose: 'Library documentation fetching for /cap:init and /cap:refresh-docs',
525
+ installHint: 'npm install -g ctx7 (or CAP uses npx ctx7@latest on demand)',
526
+ });
527
+
528
+ // @cap-todo(ac:F-053/AC-5) Skip c8 check on Node >= 20 — native --experimental-test-coverage covers /cap:test-audit's needs.
529
+ const nodeMajor = parseInt((process.versions.node || '0').split('.')[0], 10) || 0;
530
+ if (nodeMajor < 20) {
531
+ const c8 = checkTool('npx c8 --version');
532
+ tools.push({
533
+ name: 'c8',
534
+ ...c8,
535
+ required: false,
536
+ purpose: 'Code coverage for /cap:test-audit (Node < 20; Node 20+ uses native coverage)',
537
+ installHint: 'npm install -D c8',
538
+ });
539
+ }
540
+
541
+ // Project-specific checks (only if projectRoot provided)
542
+ if (projectRoot) {
543
+ const pkgPath = path.join(projectRoot, 'package.json');
544
+ if (fs.existsSync(pkgPath)) {
545
+ try {
546
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
547
+ const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
548
+
549
+ // Check vitest if project uses it
550
+ if (allDeps.vitest || fs.existsSync(path.join(projectRoot, 'vitest.config.ts')) || fs.existsSync(path.join(projectRoot, 'vitest.config.js'))) {
551
+ const vitest = checkTool('npx vitest --version');
552
+ tools.push({
553
+ name: 'vitest',
554
+ ...vitest,
555
+ required: false,
556
+ purpose: 'TypeScript/SDK test runner (detected in this project)',
557
+ installHint: 'npm install -D vitest',
558
+ });
559
+ }
560
+
561
+ // Check fast-check if property tests are desired
562
+ if (allDeps['fast-check']) {
563
+ tools.push({
564
+ name: 'fast-check',
565
+ ok: true,
566
+ version: allDeps['fast-check'].replace('^', '').replace('~', ''),
567
+ required: false,
568
+ purpose: 'Property-based testing for business logic invariants',
569
+ installHint: 'npm install -D fast-check',
570
+ });
571
+ } else {
572
+ tools.push({
573
+ name: 'fast-check',
574
+ ok: false,
575
+ version: 'not installed',
576
+ required: false,
577
+ purpose: 'Property-based testing for business logic invariants (recommended)',
578
+ installHint: 'npm install -D fast-check',
579
+ });
580
+ }
581
+ } catch (_e) { /* malformed package.json -- skip project-specific checks */ }
582
+ }
583
+ }
584
+
585
+ // @cap-todo(ac:F-019/AC-4) Module integrity check runs automatically as part of /cap:doctor
586
+ const moduleResult = checkModuleIntegrity();
587
+ const platformResult = checkPlatformPaths();
588
+ // @cap-todo(ac:F-058/AC-5) Install-mode detection runs automatically and is surfaced in the doctor report.
589
+ const installMode = detectInstallMode();
590
+
591
+ // Compute summary
592
+ const requiredTools = tools.filter(t => t.required);
593
+ const optionalTools = tools.filter(t => !t.required);
594
+
595
+ const report = {
596
+ tools,
597
+ requiredOk: requiredTools.filter(t => t.ok).length,
598
+ requiredTotal: requiredTools.length,
599
+ optionalOk: optionalTools.filter(t => t.ok).length,
600
+ optionalTotal: optionalTools.length,
601
+ healthy: requiredTools.every(t => t.ok) && moduleResult.modulesOk === moduleResult.modulesTotal,
602
+ installCommands: [],
603
+ modules: moduleResult.modules,
604
+ modulesOk: moduleResult.modulesOk,
605
+ modulesTotal: moduleResult.modulesTotal,
606
+ platformPaths: platformResult,
607
+ installMode,
608
+ };
609
+
610
+ // Build install commands for missing tools
611
+ const missingOptional = optionalTools.filter(t => !t.ok);
612
+ const npmInstallDev = missingOptional
613
+ .filter(t => t.installHint.startsWith('npm install -D'))
614
+ .map(t => t.name.toLowerCase().replace('context7 (ctx7)', 'ctx7'));
615
+
616
+ if (npmInstallDev.length > 0) {
617
+ report.installCommands.push(`npm install -D ${npmInstallDev.join(' ')}`);
618
+ }
619
+
620
+ const globalInstall = missingOptional
621
+ .filter(t => t.installHint.startsWith('npm install -g'));
622
+ for (const t of globalInstall) {
623
+ report.installCommands.push(t.installHint);
624
+ }
625
+
626
+ return report;
627
+ }
628
+
629
+ /**
630
+ * Format the doctor report as readable terminal output.
631
+ * @param {DoctorReport} report
632
+ * @returns {string}
633
+ */
634
+ function formatReport(report) {
635
+ const lines = [];
636
+ lines.push('cap:doctor\n');
637
+
638
+ // Required tools
639
+ lines.push(' Required:');
640
+ for (const t of report.tools.filter(t => t.required)) {
641
+ const icon = t.ok ? ' ✓' : ' ✗';
642
+ const pad = ' '.repeat(Math.max(0, 18 - t.name.length));
643
+ lines.push(` ${icon} ${t.name}${pad}${t.version}${t.ok ? '' : ' ← ' + t.purpose}`);
644
+ }
645
+
646
+ lines.push('');
647
+ lines.push(' Optional:');
648
+ for (const t of report.tools.filter(t => !t.required)) {
649
+ const icon = t.ok ? ' ✓' : ' -';
650
+ const pad = ' '.repeat(Math.max(0, 18 - t.name.length));
651
+ const hint = t.ok ? '' : ` ← ${t.purpose}`;
652
+ lines.push(` ${icon} ${t.name}${pad}${t.version}${hint}`);
653
+ }
654
+
655
+ // Module integrity section
656
+ if (report.modules && report.modules.length > 0) {
657
+ lines.push('');
658
+ lines.push(' Module Integrity:');
659
+ const failedModules = report.modules.filter(m => !m.ok);
660
+ const passedCount = report.modulesOk || 0;
661
+ const totalCount = report.modulesTotal || 0;
662
+
663
+ if (failedModules.length === 0) {
664
+ lines.push(` ✓ All ${totalCount} CAP modules verified (exist + loadable)`);
665
+ } else {
666
+ lines.push(` ${passedCount}/${totalCount} modules OK — ${failedModules.length} failed:`);
667
+ for (const m of failedModules) {
668
+ lines.push(` ✗ ${m.name} ← ${m.error}`);
669
+ }
670
+ }
671
+ }
672
+
673
+ // Platform path section
674
+ if (report.platformPaths) {
675
+ const pp = report.platformPaths;
676
+ if (!pp.ok) {
677
+ lines.push('');
678
+ lines.push(' Platform Paths:');
679
+ for (const w of pp.warnings) {
680
+ lines.push(` ⚠ ${w}`);
681
+ }
682
+ }
683
+ }
684
+
685
+ // @cap-todo(ac:F-058/AC-5) Render active install mode in the doctor output.
686
+ // @cap-todo(ac:F-058/AC-6) Render coexistence warning when both modes are active.
687
+ if (report.installMode) {
688
+ const im = report.installMode;
689
+ lines.push('');
690
+ lines.push(' Install mode:');
691
+ let modeLine;
692
+ if (im.active === 'both') {
693
+ modeLine = ' ⚠ npx (primary) + plugin (secondary) — coexistence detected';
694
+ } else if (im.active === 'npx') {
695
+ modeLine = ' ✓ npx (primary)';
696
+ } else if (im.active === 'plugin') {
697
+ modeLine = ' ✓ plugin';
698
+ } else {
699
+ modeLine = ' - none detected';
700
+ }
701
+ lines.push(` ${modeLine}`);
702
+ for (const w of im.warnings) {
703
+ lines.push(` ⚠ ${w}`);
704
+ }
705
+ }
706
+
707
+ lines.push('');
708
+ lines.push(` Required: ${report.requiredOk}/${report.requiredTotal} OK`);
709
+ lines.push(` Optional: ${report.optionalOk}/${report.optionalTotal} OK`);
710
+ if (report.modulesTotal != null) {
711
+ lines.push(` Modules: ${report.modulesOk}/${report.modulesTotal} OK`);
712
+ }
713
+
714
+ if (!report.healthy) {
715
+ lines.push('');
716
+ const toolsMissing = report.requiredOk < report.requiredTotal;
717
+ const modulesMissing = report.modulesOk != null && report.modulesOk < report.modulesTotal;
718
+ if (toolsMissing && modulesMissing) {
719
+ lines.push(' ✗ UNHEALTHY — required tools missing and module integrity failures detected.');
720
+ } else if (modulesMissing) {
721
+ lines.push(' ✗ UNHEALTHY — module integrity failures detected. Try: npx cap-pro@latest --force');
722
+ } else {
723
+ lines.push(' ✗ UNHEALTHY — required tools missing. CAP cannot function correctly.');
724
+ }
725
+ }
726
+
727
+ if (report.installCommands.length > 0) {
728
+ lines.push('');
729
+ lines.push(' To install missing tools:');
730
+ for (const cmd of report.installCommands) {
731
+ lines.push(` ${cmd}`);
732
+ }
733
+ }
734
+
735
+ const allModulesOk = report.modulesOk == null || report.modulesOk === report.modulesTotal;
736
+ if (report.healthy && report.optionalOk === report.optionalTotal && allModulesOk) {
737
+ lines.push('');
738
+ lines.push(' All tools and modules verified. CAP is fully operational.');
739
+ }
740
+
741
+ return lines.join('\n');
742
+ }
743
+
744
+ module.exports = {
745
+ checkTool,
746
+ runDoctor,
747
+ formatReport,
748
+ checkModuleIntegrity,
749
+ checkPlatformPaths,
750
+ detectInstallMode,
751
+ CAP_MODULE_MANIFEST,
752
+ };