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,258 @@
1
+ // @cap-context(phase:11) Convention reader utility -- discovers existing project conventions for architecture mode
2
+ // @cap-decision Implemented as a standalone CJS module (not inline in the agent) so it can be tested independently
3
+ // @cap-ref(ref:ARCH-03) gsd-prototyper reads existing project conventions before generating skeleton
4
+ // @cap-constraint Zero external dependencies -- uses only Node.js built-ins (fs, path)
5
+
6
+ 'use strict';
7
+
8
+ // @cap-feature(feature:F-013) Convention & Skeleton Generation — project convention discovery
9
+ // @cap-feature(feature:F-044) Audit and Right-Size Agent Behaviors for Opus 4.7 — minimal two-anchor probe
10
+ // @cap-decision(F-044/AC-3) Two-anchor probe replaces 6-7 file reads. CLAUDE.md + package.json are the
11
+ // highest-signal inputs for project context. Everything else (eslint config, tsconfig, naming
12
+ // convention, test pattern, build tool) can be inferred by Opus 4.7 from those two anchors plus a
13
+ // small handful of sample source files chosen on demand. The legacy readProjectConventions() is
14
+ // preserved for backwards compatibility -- see probeProjectAnchors() below for the right-sized API.
15
+
16
+ const fs = require('node:fs');
17
+ const path = require('node:path');
18
+
19
+ // @cap-api readProjectConventions(projectRoot) -- returns ConventionReport object describing discovered patterns
20
+ // @cap-pattern Convention reader returns a structured report that the agent prompt can serialize into context
21
+
22
+ /**
23
+ * @typedef {Object} ConventionReport
24
+ * @property {string} moduleType - 'esm' | 'cjs' | 'unknown'
25
+ * @property {string} namingConvention - 'kebab-case' | 'camelCase' | 'PascalCase' | 'snake_case' | 'unknown'
26
+ * @property {string} testPattern - 'colocated' | 'separate-dir' | 'unknown'
27
+ * @property {string|null} testRunner - detected test runner name or null
28
+ * @property {Object} pathAliases - e.g., { '@/*': ['src/*'] }
29
+ * @property {string|null} buildTool - detected build tool or null
30
+ * @property {string|null} linter - detected linter or null
31
+ * @property {string[]} existingDirs - list of existing directories (max depth 3)
32
+ * @property {Object} packageJson - parsed package.json or null
33
+ */
34
+
35
+ /**
36
+ * Reads existing project conventions from config files and directory structure.
37
+ * Used by gsd-prototyper in architecture mode to match generated skeleton
38
+ * to the project's established patterns.
39
+ *
40
+ * @param {string} projectRoot - absolute path to project root
41
+ * @returns {ConventionReport}
42
+ */
43
+ function readProjectConventions(projectRoot) {
44
+ // @cap-todo(ref:AC-3) Implement full convention discovery: package.json parsing, tsconfig reading, directory pattern detection, linter config extraction
45
+ const report = {
46
+ moduleType: 'unknown',
47
+ namingConvention: 'unknown',
48
+ testPattern: 'unknown',
49
+ testRunner: null,
50
+ pathAliases: {},
51
+ buildTool: null,
52
+ linter: null,
53
+ existingDirs: [],
54
+ packageJson: null,
55
+ };
56
+
57
+ // --- package.json detection ---
58
+ // @cap-context Reads package.json for module type, naming conventions, and dependency-based framework detection
59
+ const pkgPath = path.join(projectRoot, 'package.json');
60
+ if (fs.existsSync(pkgPath)) {
61
+ try {
62
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
63
+ report.packageJson = pkg;
64
+ report.moduleType = pkg.type === 'module' ? 'esm' : 'cjs';
65
+
66
+ // @cap-decision Detect test runner from devDependencies keys rather than config files -- faster and covers most cases
67
+ if (pkg.devDependencies) {
68
+ if (pkg.devDependencies.vitest) report.testRunner = 'vitest';
69
+ else if (pkg.devDependencies.jest) report.testRunner = 'jest';
70
+ else if (pkg.devDependencies.mocha) report.testRunner = 'mocha';
71
+ else if (pkg.devDependencies.ava) report.testRunner = 'ava';
72
+ }
73
+
74
+ // @cap-decision Detect build tool from devDependencies -- covers esbuild, webpack, vite, rollup
75
+ if (pkg.devDependencies) {
76
+ if (pkg.devDependencies.esbuild) report.buildTool = 'esbuild';
77
+ else if (pkg.devDependencies.vite) report.buildTool = 'vite';
78
+ else if (pkg.devDependencies.webpack) report.buildTool = 'webpack';
79
+ else if (pkg.devDependencies.rollup) report.buildTool = 'rollup';
80
+ }
81
+ } catch (_e) {
82
+ // @cap-risk Malformed package.json silently ignored -- could produce incorrect convention report
83
+ }
84
+ }
85
+
86
+ // --- tsconfig.json / jsconfig.json detection ---
87
+ // @cap-context Reads TypeScript/JavaScript config for path aliases and module resolution
88
+ const tsconfigPath = path.join(projectRoot, 'tsconfig.json');
89
+ const jsconfigPath = path.join(projectRoot, 'jsconfig.json');
90
+ const configPath = fs.existsSync(tsconfigPath) ? tsconfigPath : (fs.existsSync(jsconfigPath) ? jsconfigPath : null);
91
+
92
+ if (configPath) {
93
+ try {
94
+ // Strip JS-style comments (// and /* */) before parsing — handles JSONC tsconfig files
95
+ let raw = fs.readFileSync(configPath, 'utf8');
96
+ raw = raw.replace(/\/\/.*$/gm, '').replace(/\/\*[\s\S]*?\*\//g, '');
97
+ const config = JSON.parse(raw);
98
+ if (config.compilerOptions && config.compilerOptions.paths) {
99
+ report.pathAliases = config.compilerOptions.paths;
100
+ }
101
+ } catch (_e) {
102
+ // Malformed config silently ignored
103
+ }
104
+ }
105
+
106
+ // --- Directory structure detection ---
107
+ // @cap-context Reads directory names to detect naming convention (kebab-case vs camelCase etc.)
108
+ report.existingDirs = discoverDirectories(projectRoot, 3);
109
+ report.namingConvention = detectNamingConvention(report.existingDirs);
110
+
111
+ // --- Test pattern detection ---
112
+ // @cap-decision Check for tests/ or __tests__/ directory first, then fall back to checking for colocated .test. files
113
+ const hasTestsDir = report.existingDirs.some(d => d === 'tests' || d === '__tests__' || d.endsWith('/tests') || d.endsWith('/__tests__'));
114
+ if (hasTestsDir) {
115
+ report.testPattern = 'separate-dir';
116
+ }
117
+ // @cap-todo Detect colocated test pattern by scanning for *.test.* files alongside source files
118
+
119
+ // --- Linter detection ---
120
+ // @cap-context Checks for linter config files to match code style in generated skeleton
121
+ const linterFiles = ['.eslintrc', '.eslintrc.js', '.eslintrc.json', '.eslintrc.cjs', 'biome.json', 'biome.jsonc'];
122
+ for (const f of linterFiles) {
123
+ if (fs.existsSync(path.join(projectRoot, f))) {
124
+ report.linter = f.includes('biome') ? 'biome' : 'eslint';
125
+ break;
126
+ }
127
+ }
128
+
129
+ return report;
130
+ }
131
+
132
+ /**
133
+ * Recursively discovers directories up to maxDepth.
134
+ * @param {string} dir
135
+ * @param {number} maxDepth
136
+ * @param {number} [currentDepth=0]
137
+ * @returns {string[]}
138
+ */
139
+ function discoverDirectories(dir, maxDepth, currentDepth = 0) {
140
+ // @cap-constraint Uses readdirSync (not glob) per project zero-dep constraint
141
+ if (currentDepth >= maxDepth) return [];
142
+
143
+ const results = [];
144
+ try {
145
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
146
+ for (const entry of entries) {
147
+ if (!entry.isDirectory()) continue;
148
+ if (entry.name === 'node_modules' || entry.name === '.git' || entry.name === '.planning') continue;
149
+
150
+ const fullPath = path.join(dir, entry.name);
151
+ const relativePath = path.relative(dir, fullPath);
152
+ results.push(relativePath);
153
+ const children = discoverDirectories(fullPath, maxDepth, currentDepth + 1);
154
+ results.push(...children.map(c => path.join(entry.name, c)));
155
+ }
156
+ } catch (_e) {
157
+ // Permission errors etc.
158
+ }
159
+
160
+ return results;
161
+ }
162
+
163
+ /**
164
+ * Detects naming convention from directory names.
165
+ * @param {string[]} dirs
166
+ * @returns {string}
167
+ */
168
+ function detectNamingConvention(dirs) {
169
+ // @cap-decision Simple heuristic: check if majority of directory names match a pattern
170
+ // @cap-risk Heuristic may misclassify projects with mixed naming -- returns 'unknown' when ambiguous
171
+ const leafNames = dirs.map(d => path.basename(d)).filter(n => n.length > 1);
172
+ if (leafNames.length === 0) return 'unknown';
173
+
174
+ const kebab = leafNames.filter(n => /^[a-z][a-z0-9]*(-[a-z0-9]+)+$/.test(n)).length;
175
+ const camel = leafNames.filter(n => /^[a-z][a-zA-Z0-9]+$/.test(n) && /[A-Z]/.test(n)).length;
176
+ const pascal = leafNames.filter(n => /^[A-Z][a-zA-Z0-9]+$/.test(n)).length;
177
+ const snake = leafNames.filter(n => /^[a-z][a-z0-9]*(_[a-z0-9]+)+$/.test(n)).length;
178
+
179
+ const max = Math.max(kebab, camel, pascal, snake);
180
+ if (max === 0) return 'unknown';
181
+ if (kebab === max) return 'kebab-case';
182
+ if (camel === max) return 'camelCase';
183
+ if (pascal === max) return 'PascalCase';
184
+ if (snake === max) return 'snake_case';
185
+ return 'unknown';
186
+ }
187
+
188
+ // @cap-feature(feature:F-044) Two-anchor probe -- right-sized convention discovery for Opus 4.7
189
+ // @cap-todo(ac:F-044/AC-3) Replace 6-7 file reads with a single high-signal probe (CLAUDE.md + package.json)
190
+ // @cap-risk(F-044) Edge case: projects without CLAUDE.md or with non-JSON package.json get null fields.
191
+ // Mitigation: caller must handle null-valued anchors by either falling back to readProjectConventions()
192
+ // for the legacy multi-file probe or letting the agent infer conventions from sample source files.
193
+
194
+ /**
195
+ * @typedef {Object} ProjectAnchors
196
+ * @property {string|null} rawClaudeMd - raw contents of CLAUDE.md, or null if absent
197
+ * @property {string|null} rawPackageJson - raw contents of package.json (string, NOT parsed), or null if absent
198
+ * @property {Object|null} parsedPackageJson - parsed JSON of package.json, or null if absent or invalid
199
+ * @property {string} projectRoot - the absolute path probed
200
+ * @property {string[]} filesProbed - the relative paths actually read (for token-cost auditability)
201
+ */
202
+
203
+ /**
204
+ * Reads the two highest-signal anchor files for project context: CLAUDE.md and package.json.
205
+ *
206
+ * This replaces readProjectConventions() in the right-sized Opus 4.7 workflow. The agent
207
+ * infers the rest (linter, naming convention, test pattern, build tool) from these two
208
+ * anchors plus a small number of sample source files chosen on demand at the call site,
209
+ * rather than eagerly probing 6-7 config files up front.
210
+ *
211
+ * @param {string} projectRoot - absolute path to project root
212
+ * @returns {ProjectAnchors}
213
+ */
214
+ function probeProjectAnchors(projectRoot) {
215
+ // @cap-todo(ac:F-044/AC-3) Implementation of the two-anchor probe -- exactly two file reads
216
+ const result = {
217
+ rawClaudeMd: null,
218
+ rawPackageJson: null,
219
+ parsedPackageJson: null,
220
+ projectRoot,
221
+ filesProbed: [],
222
+ };
223
+
224
+ // @cap-decision(F-044) Anchor 1: CLAUDE.md is the project's intent document. When present it
225
+ // captures conventions, tech stack, and constraints in one place -- higher signal than parsing
226
+ // .eslintrc + .prettierrc + tsconfig + biome.json individually.
227
+ const claudePath = path.join(projectRoot, 'CLAUDE.md');
228
+ if (fs.existsSync(claudePath)) {
229
+ try {
230
+ result.rawClaudeMd = fs.readFileSync(claudePath, 'utf8');
231
+ result.filesProbed.push('CLAUDE.md');
232
+ } catch (_e) {
233
+ // @cap-risk(F-044) Permission errors silently ignored -- caller treats null rawClaudeMd as "no project intent doc"
234
+ }
235
+ }
236
+
237
+ // @cap-decision(F-044) Anchor 2: package.json is the deterministic structural anchor (module type,
238
+ // scripts, dependencies). We expose BOTH the raw string (for audit/log purposes) and the parsed
239
+ // object (for programmatic use) so consumers don't need to re-parse.
240
+ const pkgPath = path.join(projectRoot, 'package.json');
241
+ if (fs.existsSync(pkgPath)) {
242
+ try {
243
+ result.rawPackageJson = fs.readFileSync(pkgPath, 'utf8');
244
+ result.filesProbed.push('package.json');
245
+ try {
246
+ result.parsedPackageJson = JSON.parse(result.rawPackageJson);
247
+ } catch (_parseErr) {
248
+ // @cap-risk(F-044) Malformed package.json -- raw is preserved, parsed stays null. Caller must check.
249
+ }
250
+ } catch (_e) {
251
+ // Permission errors silently ignored
252
+ }
253
+ }
254
+
255
+ return result;
256
+ }
257
+
258
+ module.exports = { readProjectConventions, discoverDirectories, detectNamingConvention, probeProjectAnchors };