@nathapp/nax 0.50.3 → 0.51.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 (352) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/dist/nax.js +393 -197
  3. package/package.json +1 -3
  4. package/bin/nax.ts +0 -1195
  5. package/src/acceptance/fix-generator.ts +0 -322
  6. package/src/acceptance/generator.ts +0 -415
  7. package/src/acceptance/index.ts +0 -42
  8. package/src/acceptance/refinement.ts +0 -224
  9. package/src/acceptance/templates/cli.ts +0 -47
  10. package/src/acceptance/templates/component.ts +0 -78
  11. package/src/acceptance/templates/e2e.ts +0 -43
  12. package/src/acceptance/templates/index.ts +0 -21
  13. package/src/acceptance/templates/snapshot.ts +0 -50
  14. package/src/acceptance/templates/unit.ts +0 -48
  15. package/src/acceptance/types.ts +0 -138
  16. package/src/agents/acp/adapter.ts +0 -888
  17. package/src/agents/acp/cost.ts +0 -9
  18. package/src/agents/acp/index.ts +0 -7
  19. package/src/agents/acp/interaction-bridge.ts +0 -126
  20. package/src/agents/acp/parser.ts +0 -119
  21. package/src/agents/acp/spawn-client.ts +0 -373
  22. package/src/agents/acp/types.ts +0 -22
  23. package/src/agents/aider/adapter.ts +0 -135
  24. package/src/agents/claude/adapter.ts +0 -258
  25. package/src/agents/claude/complete.ts +0 -80
  26. package/src/agents/claude/cost.ts +0 -16
  27. package/src/agents/claude/execution.ts +0 -215
  28. package/src/agents/claude/index.ts +0 -3
  29. package/src/agents/claude/interactive.ts +0 -77
  30. package/src/agents/claude/plan.ts +0 -179
  31. package/src/agents/codex/adapter.ts +0 -153
  32. package/src/agents/cost/calculate.ts +0 -154
  33. package/src/agents/cost/index.ts +0 -10
  34. package/src/agents/cost/parse.ts +0 -97
  35. package/src/agents/cost/pricing.ts +0 -59
  36. package/src/agents/cost/types.ts +0 -45
  37. package/src/agents/gemini/adapter.ts +0 -177
  38. package/src/agents/index.ts +0 -18
  39. package/src/agents/opencode/adapter.ts +0 -106
  40. package/src/agents/registry.ts +0 -136
  41. package/src/agents/shared/decompose.ts +0 -154
  42. package/src/agents/shared/model-resolution.ts +0 -43
  43. package/src/agents/shared/types-extended.ts +0 -164
  44. package/src/agents/shared/validation.ts +0 -69
  45. package/src/agents/shared/version-detection.ts +0 -109
  46. package/src/agents/types.ts +0 -205
  47. package/src/analyze/classifier.ts +0 -282
  48. package/src/analyze/index.ts +0 -16
  49. package/src/analyze/scanner.ts +0 -171
  50. package/src/analyze/types.ts +0 -51
  51. package/src/cli/accept.ts +0 -108
  52. package/src/cli/agents.ts +0 -87
  53. package/src/cli/analyze-parser.ts +0 -291
  54. package/src/cli/analyze.ts +0 -352
  55. package/src/cli/config-descriptions.ts +0 -219
  56. package/src/cli/config-diff.ts +0 -103
  57. package/src/cli/config-display.ts +0 -285
  58. package/src/cli/config-get.ts +0 -55
  59. package/src/cli/config.ts +0 -14
  60. package/src/cli/constitution.ts +0 -17
  61. package/src/cli/diagnose-analysis.ts +0 -159
  62. package/src/cli/diagnose-formatter.ts +0 -87
  63. package/src/cli/diagnose.ts +0 -203
  64. package/src/cli/generate.ts +0 -250
  65. package/src/cli/index.ts +0 -42
  66. package/src/cli/init-context.ts +0 -405
  67. package/src/cli/init-detect.ts +0 -303
  68. package/src/cli/init.ts +0 -296
  69. package/src/cli/interact.ts +0 -295
  70. package/src/cli/plan.ts +0 -509
  71. package/src/cli/plugins.ts +0 -122
  72. package/src/cli/prompts-export.ts +0 -58
  73. package/src/cli/prompts-init.ts +0 -200
  74. package/src/cli/prompts-main.ts +0 -183
  75. package/src/cli/prompts-shared.ts +0 -70
  76. package/src/cli/prompts-tdd.ts +0 -88
  77. package/src/cli/prompts.ts +0 -17
  78. package/src/cli/runs.ts +0 -174
  79. package/src/cli/status-cost.ts +0 -151
  80. package/src/cli/status-features.ts +0 -405
  81. package/src/cli/status.ts +0 -13
  82. package/src/commands/common.ts +0 -171
  83. package/src/commands/diagnose.ts +0 -17
  84. package/src/commands/index.ts +0 -9
  85. package/src/commands/logs-formatter.ts +0 -201
  86. package/src/commands/logs-reader.ts +0 -171
  87. package/src/commands/logs.ts +0 -103
  88. package/src/commands/precheck.ts +0 -86
  89. package/src/commands/runs.ts +0 -220
  90. package/src/commands/unlock.ts +0 -96
  91. package/src/config/defaults.ts +0 -218
  92. package/src/config/index.ts +0 -22
  93. package/src/config/loader.ts +0 -143
  94. package/src/config/merge.ts +0 -106
  95. package/src/config/merger.ts +0 -147
  96. package/src/config/path-security.ts +0 -121
  97. package/src/config/paths.ts +0 -27
  98. package/src/config/permissions.ts +0 -63
  99. package/src/config/runtime-types.ts +0 -522
  100. package/src/config/schema-types.ts +0 -53
  101. package/src/config/schema.ts +0 -60
  102. package/src/config/schemas.ts +0 -426
  103. package/src/config/test-strategy.ts +0 -71
  104. package/src/config/types.ts +0 -57
  105. package/src/config/validate.ts +0 -103
  106. package/src/constitution/generator.ts +0 -158
  107. package/src/constitution/generators/aider.ts +0 -41
  108. package/src/constitution/generators/claude.ts +0 -35
  109. package/src/constitution/generators/cursor.ts +0 -36
  110. package/src/constitution/generators/opencode.ts +0 -38
  111. package/src/constitution/generators/types.ts +0 -33
  112. package/src/constitution/generators/windsurf.ts +0 -36
  113. package/src/constitution/index.ts +0 -11
  114. package/src/constitution/loader.ts +0 -121
  115. package/src/constitution/types.ts +0 -31
  116. package/src/context/auto-detect.ts +0 -228
  117. package/src/context/builder.ts +0 -299
  118. package/src/context/elements.ts +0 -122
  119. package/src/context/formatter.ts +0 -107
  120. package/src/context/generator.ts +0 -343
  121. package/src/context/generators/aider.ts +0 -34
  122. package/src/context/generators/claude.ts +0 -28
  123. package/src/context/generators/codex.ts +0 -28
  124. package/src/context/generators/cursor.ts +0 -28
  125. package/src/context/generators/gemini.ts +0 -28
  126. package/src/context/generators/opencode.ts +0 -30
  127. package/src/context/generators/windsurf.ts +0 -28
  128. package/src/context/greenfield.ts +0 -114
  129. package/src/context/index.ts +0 -34
  130. package/src/context/injector.ts +0 -279
  131. package/src/context/parent-context.ts +0 -39
  132. package/src/context/test-scanner.ts +0 -370
  133. package/src/context/types.ts +0 -98
  134. package/src/decompose/apply.ts +0 -50
  135. package/src/decompose/builder.ts +0 -181
  136. package/src/decompose/index.ts +0 -8
  137. package/src/decompose/sections/codebase.ts +0 -26
  138. package/src/decompose/sections/constraints.ts +0 -32
  139. package/src/decompose/sections/index.ts +0 -4
  140. package/src/decompose/sections/sibling-stories.ts +0 -25
  141. package/src/decompose/sections/target-story.ts +0 -31
  142. package/src/decompose/types.ts +0 -55
  143. package/src/decompose/validators/complexity.ts +0 -45
  144. package/src/decompose/validators/coverage.ts +0 -134
  145. package/src/decompose/validators/dependency.ts +0 -91
  146. package/src/decompose/validators/index.ts +0 -35
  147. package/src/decompose/validators/overlap.ts +0 -128
  148. package/src/errors.ts +0 -67
  149. package/src/execution/batching.ts +0 -157
  150. package/src/execution/crash-heartbeat.ts +0 -77
  151. package/src/execution/crash-recovery.ts +0 -79
  152. package/src/execution/crash-signals.ts +0 -165
  153. package/src/execution/crash-writer.ts +0 -154
  154. package/src/execution/deferred-review.ts +0 -105
  155. package/src/execution/dry-run.ts +0 -81
  156. package/src/execution/escalation/escalation.ts +0 -46
  157. package/src/execution/escalation/index.ts +0 -13
  158. package/src/execution/escalation/tier-escalation.ts +0 -346
  159. package/src/execution/escalation/tier-outcome.ts +0 -143
  160. package/src/execution/executor-types.ts +0 -73
  161. package/src/execution/helpers.ts +0 -38
  162. package/src/execution/index.ts +0 -27
  163. package/src/execution/iteration-runner.ts +0 -160
  164. package/src/execution/lifecycle/acceptance-loop.ts +0 -309
  165. package/src/execution/lifecycle/headless-formatter.ts +0 -83
  166. package/src/execution/lifecycle/index.ts +0 -11
  167. package/src/execution/lifecycle/parallel-lifecycle.ts +0 -101
  168. package/src/execution/lifecycle/precheck-runner.ts +0 -140
  169. package/src/execution/lifecycle/run-cleanup.ts +0 -81
  170. package/src/execution/lifecycle/run-completion.ts +0 -247
  171. package/src/execution/lifecycle/run-initialization.ts +0 -187
  172. package/src/execution/lifecycle/run-regression.ts +0 -305
  173. package/src/execution/lifecycle/run-setup.ts +0 -240
  174. package/src/execution/lifecycle/story-size-prompts.ts +0 -123
  175. package/src/execution/lock.ts +0 -129
  176. package/src/execution/parallel-coordinator.ts +0 -281
  177. package/src/execution/parallel-executor-rectification-pass.ts +0 -117
  178. package/src/execution/parallel-executor-rectify.ts +0 -136
  179. package/src/execution/parallel-executor.ts +0 -330
  180. package/src/execution/parallel-worker.ts +0 -149
  181. package/src/execution/parallel.ts +0 -13
  182. package/src/execution/pid-registry.ts +0 -275
  183. package/src/execution/pipeline-result-handler.ts +0 -221
  184. package/src/execution/progress.ts +0 -27
  185. package/src/execution/queue-handler.ts +0 -109
  186. package/src/execution/runner-completion.ts +0 -171
  187. package/src/execution/runner-execution.ts +0 -243
  188. package/src/execution/runner-setup.ts +0 -86
  189. package/src/execution/runner.ts +0 -265
  190. package/src/execution/sequential-executor.ts +0 -219
  191. package/src/execution/status-file.ts +0 -264
  192. package/src/execution/status-writer.ts +0 -181
  193. package/src/execution/story-context.ts +0 -266
  194. package/src/execution/story-selector.ts +0 -76
  195. package/src/execution/test-output-parser.ts +0 -14
  196. package/src/execution/timeout-handler.ts +0 -100
  197. package/src/hooks/index.ts +0 -2
  198. package/src/hooks/runner.ts +0 -280
  199. package/src/hooks/types.ts +0 -79
  200. package/src/interaction/chain.ts +0 -170
  201. package/src/interaction/index.ts +0 -61
  202. package/src/interaction/init.ts +0 -84
  203. package/src/interaction/plugins/auto.ts +0 -243
  204. package/src/interaction/plugins/cli.ts +0 -300
  205. package/src/interaction/plugins/telegram.ts +0 -384
  206. package/src/interaction/plugins/webhook.ts +0 -286
  207. package/src/interaction/state.ts +0 -171
  208. package/src/interaction/triggers.ts +0 -250
  209. package/src/interaction/types.ts +0 -170
  210. package/src/logger/formatters.ts +0 -84
  211. package/src/logger/index.ts +0 -16
  212. package/src/logger/logger.ts +0 -296
  213. package/src/logger/types.ts +0 -48
  214. package/src/logging/formatter.ts +0 -355
  215. package/src/logging/index.ts +0 -22
  216. package/src/logging/types.ts +0 -93
  217. package/src/metrics/aggregator.ts +0 -191
  218. package/src/metrics/index.ts +0 -14
  219. package/src/metrics/tracker.ts +0 -200
  220. package/src/metrics/types.ts +0 -115
  221. package/src/optimizer/index.ts +0 -63
  222. package/src/optimizer/noop.optimizer.ts +0 -24
  223. package/src/optimizer/rule-based.optimizer.ts +0 -248
  224. package/src/optimizer/types.ts +0 -53
  225. package/src/pipeline/event-bus.ts +0 -297
  226. package/src/pipeline/events.ts +0 -130
  227. package/src/pipeline/index.ts +0 -19
  228. package/src/pipeline/runner.ts +0 -149
  229. package/src/pipeline/stages/acceptance-setup.ts +0 -144
  230. package/src/pipeline/stages/acceptance.ts +0 -215
  231. package/src/pipeline/stages/autofix.ts +0 -262
  232. package/src/pipeline/stages/completion.ts +0 -110
  233. package/src/pipeline/stages/constitution.ts +0 -63
  234. package/src/pipeline/stages/context.ts +0 -122
  235. package/src/pipeline/stages/execution.ts +0 -359
  236. package/src/pipeline/stages/index.ts +0 -86
  237. package/src/pipeline/stages/optimizer.ts +0 -74
  238. package/src/pipeline/stages/prompt.ts +0 -79
  239. package/src/pipeline/stages/queue-check.ts +0 -103
  240. package/src/pipeline/stages/rectify.ts +0 -101
  241. package/src/pipeline/stages/regression.ts +0 -99
  242. package/src/pipeline/stages/review.ts +0 -94
  243. package/src/pipeline/stages/routing.ts +0 -276
  244. package/src/pipeline/stages/verify.ts +0 -286
  245. package/src/pipeline/subscribers/events-writer.ts +0 -135
  246. package/src/pipeline/subscribers/hooks.ts +0 -179
  247. package/src/pipeline/subscribers/interaction.ts +0 -103
  248. package/src/pipeline/subscribers/registry.ts +0 -73
  249. package/src/pipeline/subscribers/reporters.ts +0 -174
  250. package/src/pipeline/types.ts +0 -220
  251. package/src/plugins/extensions.ts +0 -225
  252. package/src/plugins/index.ts +0 -33
  253. package/src/plugins/loader.ts +0 -352
  254. package/src/plugins/plugin-logger.ts +0 -41
  255. package/src/plugins/registry.ts +0 -168
  256. package/src/plugins/types.ts +0 -206
  257. package/src/plugins/validator.ts +0 -352
  258. package/src/prd/index.ts +0 -220
  259. package/src/prd/schema.ts +0 -268
  260. package/src/prd/types.ts +0 -273
  261. package/src/prd/validate.ts +0 -41
  262. package/src/precheck/checks-agents.ts +0 -63
  263. package/src/precheck/checks-blockers.ts +0 -23
  264. package/src/precheck/checks-cli.ts +0 -68
  265. package/src/precheck/checks-config.ts +0 -102
  266. package/src/precheck/checks-git.ts +0 -117
  267. package/src/precheck/checks-system.ts +0 -101
  268. package/src/precheck/checks-warnings.ts +0 -221
  269. package/src/precheck/checks.ts +0 -36
  270. package/src/precheck/index.ts +0 -374
  271. package/src/precheck/story-size-gate.ts +0 -144
  272. package/src/precheck/types.ts +0 -31
  273. package/src/prompts/builder.ts +0 -166
  274. package/src/prompts/index.ts +0 -2
  275. package/src/prompts/loader.ts +0 -43
  276. package/src/prompts/sections/conventions.ts +0 -19
  277. package/src/prompts/sections/hermetic.ts +0 -41
  278. package/src/prompts/sections/index.ts +0 -12
  279. package/src/prompts/sections/isolation.ts +0 -70
  280. package/src/prompts/sections/role-task.ts +0 -182
  281. package/src/prompts/sections/story.ts +0 -55
  282. package/src/prompts/sections/verdict.ts +0 -70
  283. package/src/prompts/types.ts +0 -21
  284. package/src/queue/index.ts +0 -2
  285. package/src/queue/manager.ts +0 -254
  286. package/src/queue/types.ts +0 -54
  287. package/src/review/index.ts +0 -8
  288. package/src/review/orchestrator.ts +0 -154
  289. package/src/review/runner.ts +0 -303
  290. package/src/review/types.ts +0 -70
  291. package/src/routing/batch-route.ts +0 -35
  292. package/src/routing/builder.ts +0 -81
  293. package/src/routing/chain.ts +0 -75
  294. package/src/routing/content-hash.ts +0 -25
  295. package/src/routing/index.ts +0 -20
  296. package/src/routing/loader.ts +0 -62
  297. package/src/routing/router.ts +0 -305
  298. package/src/routing/strategies/adaptive.ts +0 -215
  299. package/src/routing/strategies/index.ts +0 -8
  300. package/src/routing/strategies/keyword.ts +0 -180
  301. package/src/routing/strategies/llm-prompts.ts +0 -224
  302. package/src/routing/strategies/llm.ts +0 -320
  303. package/src/routing/strategies/manual.ts +0 -50
  304. package/src/routing/strategy.ts +0 -102
  305. package/src/tdd/cleanup.ts +0 -120
  306. package/src/tdd/index.ts +0 -22
  307. package/src/tdd/isolation.ts +0 -117
  308. package/src/tdd/orchestrator.ts +0 -406
  309. package/src/tdd/prompts.ts +0 -40
  310. package/src/tdd/rectification-gate.ts +0 -274
  311. package/src/tdd/session-runner.ts +0 -263
  312. package/src/tdd/types.ts +0 -84
  313. package/src/tdd/verdict-reader.ts +0 -266
  314. package/src/tdd/verdict.ts +0 -152
  315. package/src/tui/App.tsx +0 -265
  316. package/src/tui/components/AgentPanel.tsx +0 -75
  317. package/src/tui/components/CostOverlay.tsx +0 -118
  318. package/src/tui/components/HelpOverlay.tsx +0 -107
  319. package/src/tui/components/StatusBar.tsx +0 -63
  320. package/src/tui/components/StoriesPanel.tsx +0 -177
  321. package/src/tui/hooks/useKeyboard.ts +0 -142
  322. package/src/tui/hooks/useLayout.ts +0 -137
  323. package/src/tui/hooks/usePipelineEvents.ts +0 -183
  324. package/src/tui/hooks/usePty.ts +0 -189
  325. package/src/tui/index.tsx +0 -38
  326. package/src/tui/types.ts +0 -76
  327. package/src/utils/errors.ts +0 -12
  328. package/src/utils/git.ts +0 -245
  329. package/src/utils/json-file.ts +0 -72
  330. package/src/utils/log-test-output.ts +0 -25
  331. package/src/utils/path-security.ts +0 -73
  332. package/src/utils/queue-writer.ts +0 -54
  333. package/src/verification/crash-detector.ts +0 -34
  334. package/src/verification/executor.ts +0 -250
  335. package/src/verification/index.ts +0 -12
  336. package/src/verification/orchestrator-types.ts +0 -154
  337. package/src/verification/orchestrator.ts +0 -76
  338. package/src/verification/parser.ts +0 -220
  339. package/src/verification/rectification-loop.ts +0 -172
  340. package/src/verification/rectification.ts +0 -108
  341. package/src/verification/runners.ts +0 -129
  342. package/src/verification/smart-runner.ts +0 -307
  343. package/src/verification/strategies/acceptance.ts +0 -136
  344. package/src/verification/strategies/regression.ts +0 -90
  345. package/src/verification/strategies/scoped.ts +0 -154
  346. package/src/verification/types.ts +0 -117
  347. package/src/version.ts +0 -40
  348. package/src/worktree/dispatcher.ts +0 -6
  349. package/src/worktree/index.ts +0 -2
  350. package/src/worktree/manager.ts +0 -193
  351. package/src/worktree/merge.ts +0 -302
  352. package/src/worktree/types.ts +0 -4
@@ -1,405 +0,0 @@
1
- /**
2
- * Context.md Generation (INIT-002)
3
- *
4
- * Generates context.md from filesystem scan with optional LLM enhancement.
5
- * Default mode: template from scan (zero LLM cost)
6
- * AI mode (--ai flag): LLM-powered narrative context
7
- */
8
-
9
- import { existsSync } from "node:fs";
10
- import { mkdir } from "node:fs/promises";
11
- import { basename, join } from "node:path";
12
- import { getLogger } from "../logger";
13
-
14
- /** Project scan results */
15
- export interface ProjectScan {
16
- projectName: string;
17
- fileTree: string[];
18
- packageManifest: {
19
- name?: string;
20
- description?: string;
21
- scripts?: Record<string, string>;
22
- dependencies?: Record<string, string>;
23
- } | null;
24
- readmeSnippet: string | null;
25
- entryPoints: string[];
26
- configFiles: string[];
27
- }
28
-
29
- /** Package manifest structure */
30
- interface PackageManifest {
31
- name?: string;
32
- description?: string;
33
- scripts?: Record<string, string>;
34
- dependencies?: Record<string, string>;
35
- }
36
-
37
- /** initContext options */
38
- export interface InitContextOptions {
39
- ai?: boolean;
40
- force?: boolean;
41
- }
42
-
43
- /** Dependency injection for testing */
44
- export const _deps = {
45
- callLLM: async (_prompt: string): Promise<string> => {
46
- // Placeholder implementation
47
- // In production, this would call the nax LLM infrastructure
48
- throw new Error("callLLM not implemented");
49
- },
50
- };
51
-
52
- /**
53
- * Recursively find all files in a directory, excluding certain paths.
54
- * Returns relative paths, limited to maxFiles entries.
55
- */
56
- async function findFiles(dir: string, maxFiles = 200): Promise<string[]> {
57
- // Use find command to locate files, excluding common directories
58
- try {
59
- const proc = Bun.spawnSync(
60
- [
61
- "find",
62
- dir,
63
- "-type",
64
- "f",
65
- "-not",
66
- "-path",
67
- "*/node_modules/*",
68
- "-not",
69
- "-path",
70
- "*/.git/*",
71
- "-not",
72
- "-path",
73
- "*/dist/*",
74
- ],
75
- { stdio: ["pipe", "pipe", "pipe"] },
76
- );
77
-
78
- if (proc.success) {
79
- const output = new TextDecoder().decode(proc.stdout);
80
- const files = output
81
- .trim()
82
- .split("\n")
83
- .filter((f) => f.length > 0)
84
- .map((f) => f.replace(`${dir}/`, ""))
85
- .slice(0, maxFiles);
86
- return files;
87
- }
88
- } catch {
89
- // find command failed, use fallback
90
- }
91
-
92
- return [];
93
- }
94
-
95
- /**
96
- * Read and parse package.json if it exists
97
- */
98
- async function readPackageManifest(projectRoot: string): Promise<PackageManifest | null> {
99
- const packageJsonPath = join(projectRoot, "package.json");
100
-
101
- if (!existsSync(packageJsonPath)) {
102
- return null;
103
- }
104
-
105
- try {
106
- const content = await Bun.file(packageJsonPath).text();
107
- const manifest = JSON.parse(content) as PackageManifest;
108
- return {
109
- name: manifest.name,
110
- description: manifest.description,
111
- scripts: manifest.scripts,
112
- dependencies: manifest.dependencies,
113
- };
114
- } catch {
115
- return null;
116
- }
117
- }
118
-
119
- /**
120
- * Read first 100 lines of README.md if it exists
121
- */
122
- async function readReadmeSnippet(projectRoot: string): Promise<string | null> {
123
- const readmePath = join(projectRoot, "README.md");
124
-
125
- if (!existsSync(readmePath)) {
126
- return null;
127
- }
128
-
129
- try {
130
- const content = await Bun.file(readmePath).text();
131
- const lines = content.split("\n");
132
- return lines.slice(0, 100).join("\n");
133
- } catch {
134
- return null;
135
- }
136
- }
137
-
138
- /**
139
- * Detect entry points in the project
140
- */
141
- async function detectEntryPoints(projectRoot: string): Promise<string[]> {
142
- const candidates = ["src/index.ts", "src/main.ts", "main.go", "src/lib.rs"];
143
- const found: string[] = [];
144
-
145
- for (const candidate of candidates) {
146
- const path = join(projectRoot, candidate);
147
- if (existsSync(path)) {
148
- found.push(candidate);
149
- }
150
- }
151
-
152
- return found;
153
- }
154
-
155
- /**
156
- * Detect config files in the project
157
- */
158
- async function detectConfigFiles(projectRoot: string): Promise<string[]> {
159
- const candidates = ["tsconfig.json", "biome.json", "turbo.json", ".env.example"];
160
- const found: string[] = [];
161
-
162
- for (const candidate of candidates) {
163
- const path = join(projectRoot, candidate);
164
- if (existsSync(path)) {
165
- found.push(candidate);
166
- }
167
- }
168
-
169
- return found;
170
- }
171
-
172
- /**
173
- * Scan a project for context information
174
- */
175
- export async function scanProject(projectRoot: string): Promise<ProjectScan> {
176
- const fileTree = await findFiles(projectRoot, 200);
177
- const packageManifest = await readPackageManifest(projectRoot);
178
- const readmeSnippet = await readReadmeSnippet(projectRoot);
179
- const entryPoints = await detectEntryPoints(projectRoot);
180
- const configFiles = await detectConfigFiles(projectRoot);
181
-
182
- // Determine project name from package.json or directory basename
183
- const projectName = packageManifest?.name || basename(projectRoot);
184
-
185
- return {
186
- projectName,
187
- fileTree,
188
- packageManifest,
189
- readmeSnippet,
190
- entryPoints,
191
- configFiles,
192
- };
193
- }
194
-
195
- /**
196
- * Generate a markdown template for context.md from scan results
197
- */
198
- export function generateContextTemplate(scan: ProjectScan): string {
199
- const lines: string[] = [];
200
-
201
- lines.push(`# ${scan.projectName}\n`);
202
-
203
- if (scan.packageManifest?.description) {
204
- lines.push(`${scan.packageManifest.description}\n`);
205
- } else {
206
- lines.push("<!-- TODO: Add project description -->\n");
207
- }
208
-
209
- if (scan.entryPoints.length > 0) {
210
- lines.push("## Entry Points\n");
211
- for (const ep of scan.entryPoints) {
212
- lines.push(`- ${ep}`);
213
- }
214
- lines.push("");
215
- } else {
216
- lines.push("## Entry Points\n");
217
- lines.push("<!-- TODO: Document entry points -->\n");
218
- }
219
-
220
- if (scan.fileTree.length > 0) {
221
- lines.push("## Project Structure\n");
222
- lines.push("```");
223
- for (const file of scan.fileTree.slice(0, 20)) {
224
- lines.push(file);
225
- }
226
- if (scan.fileTree.length > 20) {
227
- lines.push(`... and ${scan.fileTree.length - 20} more files`);
228
- }
229
- lines.push("```\n");
230
- } else {
231
- lines.push("## Project Structure\n");
232
- lines.push("<!-- TODO: Document project structure -->\n");
233
- }
234
-
235
- if (scan.configFiles.length > 0) {
236
- lines.push("## Configuration Files\n");
237
- for (const cf of scan.configFiles) {
238
- lines.push(`- ${cf}`);
239
- }
240
- lines.push("");
241
- } else {
242
- lines.push("## Configuration Files\n");
243
- lines.push("<!-- TODO: Document configuration files -->\n");
244
- }
245
-
246
- if (scan.packageManifest?.scripts) {
247
- const hasScripts = Object.keys(scan.packageManifest.scripts).length > 0;
248
- if (hasScripts) {
249
- lines.push("## Scripts\n");
250
- for (const [name, command] of Object.entries(scan.packageManifest.scripts)) {
251
- lines.push(`- **${name}**: \`${command}\``);
252
- }
253
- lines.push("");
254
- }
255
- }
256
-
257
- if (scan.packageManifest?.dependencies) {
258
- const deps = Object.keys(scan.packageManifest.dependencies);
259
- if (deps.length > 0) {
260
- lines.push("## Dependencies\n");
261
- lines.push("<!-- TODO: Document key dependencies and their purpose -->\n");
262
- }
263
- }
264
-
265
- lines.push("## Development Guidelines\n");
266
- lines.push("<!-- TODO: Document development guidelines and conventions -->\n");
267
-
268
- return `${lines.join("\n").trim()}\n`;
269
- }
270
-
271
- /**
272
- * Generate context.md with LLM enhancement
273
- */
274
- async function generateContextWithLLM(scan: ProjectScan): Promise<string> {
275
- const logger = getLogger();
276
-
277
- // Build LLM prompt from scan results
278
- const scanSummary = `
279
- Project: ${scan.projectName}
280
- Entry Points: ${scan.entryPoints.join(", ") || "None detected"}
281
- Config Files: ${scan.configFiles.join(", ") || "None detected"}
282
- Total Files: ${scan.fileTree.length}
283
- Description: ${scan.packageManifest?.description || "Not provided"}
284
- `;
285
-
286
- const prompt = `
287
- You are a technical documentation expert. Generate a concise, well-structured context.md file for a software project based on this scan:
288
-
289
- ${scanSummary}
290
-
291
- The context.md should include:
292
- 1. Project overview (name, purpose, key technologies)
293
- 2. Entry points and main modules
294
- 3. Key dependencies and why they're used
295
- 4. Development setup and common commands
296
- 5. Architecture overview (brief)
297
- 6. Development guidelines
298
-
299
- Keep it under 2000 tokens. Use markdown formatting. Be specific to the detected stack and structure.
300
- `;
301
-
302
- try {
303
- const result = await _deps.callLLM(prompt);
304
- logger.info("init", "Generated context.md with LLM");
305
- return result;
306
- } catch (err) {
307
- logger.warn(
308
- "init",
309
- `LLM context generation failed, falling back to template: ${err instanceof Error ? err.message : String(err)}`,
310
- );
311
- return generateContextTemplate(scan);
312
- }
313
- }
314
-
315
- /**
316
- * Generate a minimal package context.md template.
317
- *
318
- * @param packagePath - Relative path of the package (e.g. "packages/api")
319
- */
320
- export function generatePackageContextTemplate(packagePath: string): string {
321
- const packageName = packagePath.split("/").pop() ?? packagePath;
322
- return `# ${packageName} — Context
323
-
324
- <!-- Package-specific conventions. Root context.md provides shared rules. -->
325
-
326
- ## Tech Stack
327
-
328
- <!-- TODO: Document this package's tech stack -->
329
-
330
- ## Commands
331
-
332
- | Command | Purpose |
333
- |:--------|:--------|
334
- | \`bun test\` | Unit tests |
335
-
336
- ## Development Guidelines
337
-
338
- <!-- TODO: Document package-specific guidelines -->
339
- `;
340
- }
341
-
342
- /**
343
- * Initialize per-package nax/context.md scaffold.
344
- *
345
- * Creates \`<packageDir>/nax/context.md\` with a minimal template.
346
- * Does not overwrite an existing file unless force is set.
347
- *
348
- * @param repoRoot - Absolute path to repo root
349
- * @param packagePath - Relative path of the package (e.g. "packages/api")
350
- * @param force - Overwrite existing file
351
- */
352
- export async function initPackage(repoRoot: string, packagePath: string, force = false): Promise<void> {
353
- const logger = getLogger();
354
- const packageDir = join(repoRoot, packagePath);
355
- const naxDir = join(packageDir, "nax");
356
- const contextPath = join(naxDir, "context.md");
357
-
358
- if (existsSync(contextPath) && !force) {
359
- logger.info("init", "Package context.md already exists (use --force to overwrite)", { path: contextPath });
360
- return;
361
- }
362
-
363
- if (!existsSync(naxDir)) {
364
- await mkdir(naxDir, { recursive: true });
365
- }
366
-
367
- const content = generatePackageContextTemplate(packagePath);
368
- await Bun.write(contextPath, content);
369
- logger.info("init", "Created package context.md", { path: contextPath });
370
- }
371
-
372
- /**
373
- * Initialize context.md for a project
374
- */
375
- export async function initContext(projectRoot: string, options: InitContextOptions = {}): Promise<void> {
376
- const logger = getLogger();
377
- const naxDir = join(projectRoot, "nax");
378
- const contextPath = join(naxDir, "context.md");
379
-
380
- // Check if context.md already exists
381
- if (existsSync(contextPath) && !options.force) {
382
- logger.info("init", "context.md already exists, skipping (use --force to overwrite)", { path: contextPath });
383
- return;
384
- }
385
-
386
- // Create nax directory if needed
387
- if (!existsSync(naxDir)) {
388
- await mkdir(naxDir, { recursive: true });
389
- }
390
-
391
- // Scan the project
392
- const scan = await scanProject(projectRoot);
393
-
394
- // Generate content (template or LLM-enhanced)
395
- let content: string;
396
- if (options.ai) {
397
- content = await generateContextWithLLM(scan);
398
- } else {
399
- content = generateContextTemplate(scan);
400
- }
401
-
402
- // Write context.md
403
- await Bun.write(contextPath, content);
404
- logger.info("init", "Generated nax/context.md template from project scan", { path: contextPath });
405
- }
@@ -1,303 +0,0 @@
1
- /**
2
- * Project Stack Detection for nax init
3
- *
4
- * Scans the project root for stack indicators and builds quality.commands
5
- * for nax/config.json.
6
- */
7
-
8
- import { existsSync, readFileSync } from "node:fs";
9
- import { join } from "node:path";
10
-
11
- /** Detected project runtime */
12
- export type Runtime = "bun" | "node" | "unknown";
13
-
14
- /** Detected UI framework */
15
- export type UIFramework = "ink" | "react" | "vue" | "svelte";
16
-
17
- /** Full stack info including UI framework and bin detection */
18
- export interface StackInfo extends ProjectStack {
19
- uiFramework?: UIFramework;
20
- hasBin?: boolean;
21
- }
22
-
23
- /** Shape of a parsed package.json for detection purposes */
24
- interface PackageJson {
25
- dependencies?: Record<string, string>;
26
- devDependencies?: Record<string, string>;
27
- peerDependencies?: Record<string, string>;
28
- bin?: Record<string, string> | string;
29
- workspaces?: string[] | { packages: string[] };
30
- }
31
-
32
- function readPackageJson(projectRoot: string): PackageJson | undefined {
33
- const pkgPath = join(projectRoot, "package.json");
34
- if (!existsSync(pkgPath)) return undefined;
35
- try {
36
- return JSON.parse(readFileSync(pkgPath, "utf-8")) as PackageJson;
37
- } catch {
38
- return undefined;
39
- }
40
- }
41
-
42
- function allDeps(pkg: PackageJson): Record<string, string> {
43
- return {
44
- ...pkg.dependencies,
45
- ...pkg.devDependencies,
46
- ...pkg.peerDependencies,
47
- };
48
- }
49
-
50
- function detectUIFramework(pkg: PackageJson): UIFramework | undefined {
51
- const deps = allDeps(pkg);
52
- if ("ink" in deps) return "ink";
53
- if ("react" in deps || "next" in deps) return "react";
54
- if ("vue" in deps || "nuxt" in deps) return "vue";
55
- if ("svelte" in deps || "@sveltejs/kit" in deps) return "svelte";
56
- return undefined;
57
- }
58
-
59
- function detectHasBin(pkg: PackageJson): boolean {
60
- return pkg.bin !== undefined;
61
- }
62
-
63
- /**
64
- * Detect the project stack including UI framework from package.json.
65
- */
66
- export function detectStack(projectRoot: string): StackInfo {
67
- const base = detectProjectStack(projectRoot);
68
- const pkg = readPackageJson(projectRoot);
69
- if (!pkg) return base;
70
- return {
71
- ...base,
72
- uiFramework: detectUIFramework(pkg),
73
- hasBin: detectHasBin(pkg) || undefined,
74
- };
75
- }
76
-
77
- /** Detected project language */
78
- export type Language = "typescript" | "python" | "rust" | "go" | "unknown";
79
-
80
- /** Detected linter */
81
- export type Linter = "biome" | "eslint" | "ruff" | "clippy" | "golangci-lint" | "unknown";
82
-
83
- /** Detected monorepo tooling */
84
- export type Monorepo = "turborepo" | "nx" | "pnpm-workspaces" | "bun-workspaces" | "none";
85
-
86
- /** Full detected project stack */
87
- export interface ProjectStack {
88
- runtime: Runtime;
89
- language: Language;
90
- linter: Linter;
91
- monorepo: Monorepo;
92
- }
93
-
94
- /** Quality commands derived from stack detection */
95
- export interface QualityCommands {
96
- typecheck?: string;
97
- lint?: string;
98
- test?: string;
99
- }
100
-
101
- function detectRuntime(projectRoot: string): Runtime {
102
- if (existsSync(join(projectRoot, "bun.lockb")) || existsSync(join(projectRoot, "bunfig.toml"))) {
103
- return "bun";
104
- }
105
- if (
106
- existsSync(join(projectRoot, "package-lock.json")) ||
107
- existsSync(join(projectRoot, "yarn.lock")) ||
108
- existsSync(join(projectRoot, "pnpm-lock.yaml"))
109
- ) {
110
- return "node";
111
- }
112
- return "unknown";
113
- }
114
-
115
- function detectLanguage(projectRoot: string): Language {
116
- if (existsSync(join(projectRoot, "tsconfig.json"))) return "typescript";
117
- if (existsSync(join(projectRoot, "pyproject.toml")) || existsSync(join(projectRoot, "setup.py"))) {
118
- return "python";
119
- }
120
- if (existsSync(join(projectRoot, "Cargo.toml"))) return "rust";
121
- if (existsSync(join(projectRoot, "go.mod"))) return "go";
122
- return "unknown";
123
- }
124
-
125
- function detectLinter(projectRoot: string): Linter {
126
- if (existsSync(join(projectRoot, "biome.json")) || existsSync(join(projectRoot, "biome.jsonc"))) {
127
- return "biome";
128
- }
129
- if (
130
- existsSync(join(projectRoot, ".eslintrc.json")) ||
131
- existsSync(join(projectRoot, ".eslintrc.js")) ||
132
- existsSync(join(projectRoot, "eslint.config.js"))
133
- ) {
134
- return "eslint";
135
- }
136
- return "unknown";
137
- }
138
-
139
- function detectMonorepo(projectRoot: string): Monorepo {
140
- if (existsSync(join(projectRoot, "turbo.json"))) return "turborepo";
141
- if (existsSync(join(projectRoot, "nx.json"))) return "nx";
142
- if (existsSync(join(projectRoot, "pnpm-workspace.yaml"))) return "pnpm-workspaces";
143
- // Bun/npm/yarn workspaces: package.json with "workspaces" field
144
- const pkg = readPackageJson(projectRoot);
145
- if (pkg?.workspaces) return "bun-workspaces";
146
- return "none";
147
- }
148
-
149
- /**
150
- * Detect the project stack by scanning for indicator files.
151
- */
152
- export function detectProjectStack(projectRoot: string): ProjectStack {
153
- return {
154
- runtime: detectRuntime(projectRoot),
155
- language: detectLanguage(projectRoot),
156
- linter: detectLinter(projectRoot),
157
- monorepo: detectMonorepo(projectRoot),
158
- };
159
- }
160
-
161
- function resolveLintCommand(stack: ProjectStack, fallback: string): string {
162
- if (stack.linter === "biome") return "biome check .";
163
- if (stack.linter === "eslint") return "eslint .";
164
- return fallback;
165
- }
166
-
167
- /**
168
- * Build quality.commands for monorepo orchestrators.
169
- *
170
- * Turborepo and Nx support change-aware filtering natively — delegate
171
- * scoping to the tool rather than nax's smart test runner.
172
- * pnpm/bun workspaces have no built-in affected detection, so nax's
173
- * smart runner still applies; commands run across all packages.
174
- */
175
- function buildMonorepoQualityCommands(stack: ProjectStack): QualityCommands | null {
176
- if (stack.monorepo === "turborepo") {
177
- return {
178
- typecheck: "turbo run typecheck --filter=...[HEAD~1]",
179
- lint: "turbo run lint --filter=...[HEAD~1]",
180
- test: "turbo run test --filter=...[HEAD~1]",
181
- };
182
- }
183
- if (stack.monorepo === "nx") {
184
- return {
185
- typecheck: "nx affected --target=typecheck",
186
- lint: "nx affected --target=lint",
187
- test: "nx affected --target=test",
188
- };
189
- }
190
- if (stack.monorepo === "pnpm-workspaces") {
191
- return {
192
- lint: resolveLintCommand(stack, "pnpm run --recursive lint"),
193
- test: "pnpm run --recursive test",
194
- };
195
- }
196
- if (stack.monorepo === "bun-workspaces") {
197
- return {
198
- lint: resolveLintCommand(stack, "bun run lint"),
199
- test: "bun run --filter '*' test",
200
- };
201
- }
202
- return null;
203
- }
204
-
205
- /**
206
- * Build quality.commands from a detected project stack.
207
- */
208
- export function buildQualityCommands(stack: ProjectStack): QualityCommands {
209
- // Monorepo orchestrators: delegate to the tool's own scoping
210
- const monorepoCommands = buildMonorepoQualityCommands(stack);
211
- if (monorepoCommands) return monorepoCommands;
212
-
213
- if (stack.runtime === "bun" && stack.language === "typescript") {
214
- return {
215
- typecheck: "bun run tsc --noEmit",
216
- lint: resolveLintCommand(stack, "bun run lint"),
217
- test: "bun test",
218
- };
219
- }
220
-
221
- if (stack.runtime === "node" && stack.language === "typescript") {
222
- return {
223
- typecheck: "npx tsc --noEmit",
224
- lint: resolveLintCommand(stack, "npm run lint"),
225
- test: "npm test",
226
- };
227
- }
228
-
229
- if (stack.language === "python") {
230
- return {
231
- lint: "ruff check .",
232
- test: "pytest",
233
- };
234
- }
235
-
236
- if (stack.language === "rust") {
237
- return {
238
- typecheck: "cargo check",
239
- lint: "cargo clippy",
240
- test: "cargo test",
241
- };
242
- }
243
-
244
- if (stack.language === "go") {
245
- return {
246
- typecheck: "go vet ./...",
247
- lint: "golangci-lint run",
248
- test: "go test ./...",
249
- };
250
- }
251
-
252
- return {};
253
- }
254
-
255
- function isStackDetected(stack: ProjectStack): boolean {
256
- return stack.runtime !== "unknown" || stack.language !== "unknown";
257
- }
258
-
259
- /** Build the acceptance config section from StackInfo, or undefined if not applicable. */
260
- function buildAcceptanceConfig(stack: StackInfo): { testStrategy: string; testFramework?: string } | undefined {
261
- if (stack.uiFramework === "ink") {
262
- return { testStrategy: "component", testFramework: "ink-testing-library" };
263
- }
264
- if (stack.uiFramework === "react") {
265
- return { testStrategy: "component", testFramework: "@testing-library/react" };
266
- }
267
- if (stack.uiFramework === "vue") {
268
- return { testStrategy: "component", testFramework: "@testing-library/vue" };
269
- }
270
- if (stack.uiFramework === "svelte") {
271
- return { testStrategy: "component", testFramework: "@testing-library/svelte" };
272
- }
273
- if (stack.hasBin) {
274
- const testFramework = stack.runtime === "bun" ? "bun:test" : "jest";
275
- return { testStrategy: "cli", testFramework };
276
- }
277
- return undefined;
278
- }
279
-
280
- /**
281
- * Build the full init config object from a detected project stack.
282
- * Falls back to minimal config when stack is undetected.
283
- */
284
- export function buildInitConfig(stack: ProjectStack | StackInfo): object {
285
- const stackInfo = stack as StackInfo;
286
- const acceptance = buildAcceptanceConfig(stackInfo);
287
-
288
- if (!isStackDetected(stack)) {
289
- return acceptance ? { version: 1, acceptance } : { version: 1 };
290
- }
291
-
292
- const commands = buildQualityCommands(stack);
293
- const hasCommands = Object.keys(commands).length > 0;
294
-
295
- if (!hasCommands && !acceptance) {
296
- return { version: 1 };
297
- }
298
-
299
- const config: Record<string, unknown> = { version: 1 };
300
- if (hasCommands) config.quality = { commands };
301
- if (acceptance) config.acceptance = acceptance;
302
- return config;
303
- }