@mirnoorata/codexa 0.2.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 (364) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +634 -0
  3. package/dist/artifacts.d.ts +2 -0
  4. package/dist/artifacts.js +375 -0
  5. package/dist/artifacts.js.map +1 -0
  6. package/dist/autonomy.d.ts +17 -0
  7. package/dist/autonomy.js +124 -0
  8. package/dist/autonomy.js.map +1 -0
  9. package/dist/autoverify/policy.d.ts +5 -0
  10. package/dist/autoverify/policy.js +18 -0
  11. package/dist/autoverify/policy.js.map +1 -0
  12. package/dist/autoverify.d.ts +45 -0
  13. package/dist/autoverify.js +1041 -0
  14. package/dist/autoverify.js.map +1 -0
  15. package/dist/cache-lock.d.ts +16 -0
  16. package/dist/cache-lock.js +181 -0
  17. package/dist/cache-lock.js.map +1 -0
  18. package/dist/cli/hooks.d.ts +5 -0
  19. package/dist/cli/hooks.js +264 -0
  20. package/dist/cli/hooks.js.map +1 -0
  21. package/dist/cli.d.ts +2 -0
  22. package/dist/cli.js +1034 -0
  23. package/dist/cli.js.map +1 -0
  24. package/dist/codex-contract.d.ts +2 -0
  25. package/dist/codex-contract.js +78 -0
  26. package/dist/codex-contract.js.map +1 -0
  27. package/dist/command.d.ts +34 -0
  28. package/dist/command.js +162 -0
  29. package/dist/command.js.map +1 -0
  30. package/dist/doctor.d.ts +112 -0
  31. package/dist/doctor.js +518 -0
  32. package/dist/doctor.js.map +1 -0
  33. package/dist/eval/baseline.d.ts +7 -0
  34. package/dist/eval/baseline.js +146 -0
  35. package/dist/eval/baseline.js.map +1 -0
  36. package/dist/eval/historical.d.ts +4 -0
  37. package/dist/eval/historical.js +663 -0
  38. package/dist/eval/historical.js.map +1 -0
  39. package/dist/eval/render.d.ts +2 -0
  40. package/dist/eval/render.js +53 -0
  41. package/dist/eval/render.js.map +1 -0
  42. package/dist/eval/scoring.d.ts +21 -0
  43. package/dist/eval/scoring.js +618 -0
  44. package/dist/eval/scoring.js.map +1 -0
  45. package/dist/eval/synthetic.d.ts +36 -0
  46. package/dist/eval/synthetic.js +107 -0
  47. package/dist/eval/synthetic.js.map +1 -0
  48. package/dist/eval/types.d.ts +36 -0
  49. package/dist/eval/types.js +2 -0
  50. package/dist/eval/types.js.map +1 -0
  51. package/dist/eval.d.ts +140 -0
  52. package/dist/eval.js +551 -0
  53. package/dist/eval.js.map +1 -0
  54. package/dist/git.d.ts +17 -0
  55. package/dist/git.js +189 -0
  56. package/dist/git.js.map +1 -0
  57. package/dist/github-release.d.ts +47 -0
  58. package/dist/github-release.js +610 -0
  59. package/dist/github-release.js.map +1 -0
  60. package/dist/github-sync.d.ts +68 -0
  61. package/dist/github-sync.js +345 -0
  62. package/dist/github-sync.js.map +1 -0
  63. package/dist/graph.d.ts +10 -0
  64. package/dist/graph.js +665 -0
  65. package/dist/graph.js.map +1 -0
  66. package/dist/indexer/aliases.d.ts +2 -0
  67. package/dist/indexer/aliases.js +190 -0
  68. package/dist/indexer/aliases.js.map +1 -0
  69. package/dist/indexer/artifact-writing.d.ts +3 -0
  70. package/dist/indexer/artifact-writing.js +79 -0
  71. package/dist/indexer/artifact-writing.js.map +1 -0
  72. package/dist/indexer/discovery.d.ts +2 -0
  73. package/dist/indexer/discovery.js +5 -0
  74. package/dist/indexer/discovery.js.map +1 -0
  75. package/dist/indexer/external-facts.d.ts +6 -0
  76. package/dist/indexer/external-facts.js +45 -0
  77. package/dist/indexer/external-facts.js.map +1 -0
  78. package/dist/indexer/freshness.d.ts +8 -0
  79. package/dist/indexer/freshness.js +56 -0
  80. package/dist/indexer/freshness.js.map +1 -0
  81. package/dist/indexer/graph-stage.d.ts +2 -0
  82. package/dist/indexer/graph-stage.js +21 -0
  83. package/dist/indexer/graph-stage.js.map +1 -0
  84. package/dist/indexer/parsing.d.ts +30 -0
  85. package/dist/indexer/parsing.js +177 -0
  86. package/dist/indexer/parsing.js.map +1 -0
  87. package/dist/indexer/pipeline.d.ts +5 -0
  88. package/dist/indexer/pipeline.js +8 -0
  89. package/dist/indexer/pipeline.js.map +1 -0
  90. package/dist/indexer/ranking.d.ts +4 -0
  91. package/dist/indexer/ranking.js +134 -0
  92. package/dist/indexer/ranking.js.map +1 -0
  93. package/dist/indexer.d.ts +13 -0
  94. package/dist/indexer.js +395 -0
  95. package/dist/indexer.js.map +1 -0
  96. package/dist/init.d.ts +24 -0
  97. package/dist/init.js +566 -0
  98. package/dist/init.js.map +1 -0
  99. package/dist/language.d.ts +8 -0
  100. package/dist/language.js +123 -0
  101. package/dist/language.js.map +1 -0
  102. package/dist/live-index.d.ts +68 -0
  103. package/dist/live-index.js +215 -0
  104. package/dist/live-index.js.map +1 -0
  105. package/dist/lsp/assist.d.ts +44 -0
  106. package/dist/lsp/assist.js +331 -0
  107. package/dist/lsp/assist.js.map +1 -0
  108. package/dist/lsp/client.d.ts +59 -0
  109. package/dist/lsp/client.js +208 -0
  110. package/dist/lsp/client.js.map +1 -0
  111. package/dist/mcp/compaction.d.ts +15 -0
  112. package/dist/mcp/compaction.js +1249 -0
  113. package/dist/mcp/compaction.js.map +1 -0
  114. package/dist/mcp/envelope.d.ts +44 -0
  115. package/dist/mcp/envelope.js +425 -0
  116. package/dist/mcp/envelope.js.map +1 -0
  117. package/dist/mcp/prompts.d.ts +2 -0
  118. package/dist/mcp/prompts.js +109 -0
  119. package/dist/mcp/prompts.js.map +1 -0
  120. package/dist/mcp/resources.d.ts +2 -0
  121. package/dist/mcp/resources.js +132 -0
  122. package/dist/mcp/resources.js.map +1 -0
  123. package/dist/mcp/runtime.d.ts +15 -0
  124. package/dist/mcp/runtime.js +122 -0
  125. package/dist/mcp/runtime.js.map +1 -0
  126. package/dist/mcp/session-memory.d.ts +3 -0
  127. package/dist/mcp/session-memory.js +61 -0
  128. package/dist/mcp/session-memory.js.map +1 -0
  129. package/dist/mcp/tool-registry.d.ts +269 -0
  130. package/dist/mcp/tool-registry.js +284 -0
  131. package/dist/mcp/tool-registry.js.map +1 -0
  132. package/dist/mcp/tools.d.ts +53 -0
  133. package/dist/mcp/tools.js +372 -0
  134. package/dist/mcp/tools.js.map +1 -0
  135. package/dist/mcp-repo-root.d.ts +16 -0
  136. package/dist/mcp-repo-root.js +322 -0
  137. package/dist/mcp-repo-root.js.map +1 -0
  138. package/dist/mcp-tool-catalog.d.ts +2 -0
  139. package/dist/mcp-tool-catalog.js +2 -0
  140. package/dist/mcp-tool-catalog.js.map +1 -0
  141. package/dist/mcp.d.ts +11 -0
  142. package/dist/mcp.js +332 -0
  143. package/dist/mcp.js.map +1 -0
  144. package/dist/outcome-ranking.d.ts +5 -0
  145. package/dist/outcome-ranking.js +115 -0
  146. package/dist/outcome-ranking.js.map +1 -0
  147. package/dist/parser/context.d.ts +28 -0
  148. package/dist/parser/context.js +2 -0
  149. package/dist/parser/context.js.map +1 -0
  150. package/dist/parser/ecma.d.ts +5 -0
  151. package/dist/parser/ecma.js +388 -0
  152. package/dist/parser/ecma.js.map +1 -0
  153. package/dist/parser/facts.d.ts +12 -0
  154. package/dist/parser/facts.js +137 -0
  155. package/dist/parser/facts.js.map +1 -0
  156. package/dist/parser/json.d.ts +3 -0
  157. package/dist/parser/json.js +318 -0
  158. package/dist/parser/json.js.map +1 -0
  159. package/dist/parser/markdown.d.ts +3 -0
  160. package/dist/parser/markdown.js +180 -0
  161. package/dist/parser/markdown.js.map +1 -0
  162. package/dist/parser/nodes.d.ts +5 -0
  163. package/dist/parser/nodes.js +75 -0
  164. package/dist/parser/nodes.js.map +1 -0
  165. package/dist/parser/python.d.ts +2 -0
  166. package/dist/parser/python.js +307 -0
  167. package/dist/parser/python.js.map +1 -0
  168. package/dist/parser/references.d.ts +3 -0
  169. package/dist/parser/references.js +204 -0
  170. package/dist/parser/references.js.map +1 -0
  171. package/dist/parser/risks.d.ts +4 -0
  172. package/dist/parser/risks.js +62 -0
  173. package/dist/parser/risks.js.map +1 -0
  174. package/dist/parser/routes.d.ts +5 -0
  175. package/dist/parser/routes.js +97 -0
  176. package/dist/parser/routes.js.map +1 -0
  177. package/dist/parser/shallow.d.ts +3 -0
  178. package/dist/parser/shallow.js +545 -0
  179. package/dist/parser/shallow.js.map +1 -0
  180. package/dist/parser/source.d.ts +4 -0
  181. package/dist/parser/source.js +127 -0
  182. package/dist/parser/source.js.map +1 -0
  183. package/dist/parser.d.ts +2 -0
  184. package/dist/parser.js +2 -0
  185. package/dist/parser.js.map +1 -0
  186. package/dist/placeholder-signals.d.ts +15 -0
  187. package/dist/placeholder-signals.js +511 -0
  188. package/dist/placeholder-signals.js.map +1 -0
  189. package/dist/post-edit-outcomes.d.ts +167 -0
  190. package/dist/post-edit-outcomes.js +484 -0
  191. package/dist/post-edit-outcomes.js.map +1 -0
  192. package/dist/queries.d.ts +12 -0
  193. package/dist/queries.js +13 -0
  194. package/dist/queries.js.map +1 -0
  195. package/dist/query/change-plan.d.ts +48 -0
  196. package/dist/query/change-plan.js +858 -0
  197. package/dist/query/change-plan.js.map +1 -0
  198. package/dist/query/compact-data.d.ts +25 -0
  199. package/dist/query/compact-data.js +74 -0
  200. package/dist/query/compact-data.js.map +1 -0
  201. package/dist/query/context.d.ts +5 -0
  202. package/dist/query/context.js +1162 -0
  203. package/dist/query/context.js.map +1 -0
  204. package/dist/query/diff.d.ts +5 -0
  205. package/dist/query/diff.js +111 -0
  206. package/dist/query/diff.js.map +1 -0
  207. package/dist/query/edge-evidence.d.ts +3 -0
  208. package/dist/query/edge-evidence.js +36 -0
  209. package/dist/query/edge-evidence.js.map +1 -0
  210. package/dist/query/formatting.d.ts +14 -0
  211. package/dist/query/formatting.js +67 -0
  212. package/dist/query/formatting.js.map +1 -0
  213. package/dist/query/graph-traversal.d.ts +22 -0
  214. package/dist/query/graph-traversal.js +218 -0
  215. package/dist/query/graph-traversal.js.map +1 -0
  216. package/dist/query/graph.d.ts +14 -0
  217. package/dist/query/graph.js +102 -0
  218. package/dist/query/graph.js.map +1 -0
  219. package/dist/query/impact.d.ts +28 -0
  220. package/dist/query/impact.js +568 -0
  221. package/dist/query/impact.js.map +1 -0
  222. package/dist/query/inspection.d.ts +9 -0
  223. package/dist/query/inspection.js +290 -0
  224. package/dist/query/inspection.js.map +1 -0
  225. package/dist/query/next-tools.d.ts +3 -0
  226. package/dist/query/next-tools.js +25 -0
  227. package/dist/query/next-tools.js.map +1 -0
  228. package/dist/query/placeholders.d.ts +24 -0
  229. package/dist/query/placeholders.js +121 -0
  230. package/dist/query/placeholders.js.map +1 -0
  231. package/dist/query/post-edit/decision.d.ts +49 -0
  232. package/dist/query/post-edit/decision.js +130 -0
  233. package/dist/query/post-edit/decision.js.map +1 -0
  234. package/dist/query/post-edit/dirty-scope.d.ts +16 -0
  235. package/dist/query/post-edit/dirty-scope.js +21 -0
  236. package/dist/query/post-edit/dirty-scope.js.map +1 -0
  237. package/dist/query/post-edit/next-actions.d.ts +22 -0
  238. package/dist/query/post-edit/next-actions.js +44 -0
  239. package/dist/query/post-edit/next-actions.js.map +1 -0
  240. package/dist/query/post-edit/snapshot-contract.d.ts +8 -0
  241. package/dist/query/post-edit/snapshot-contract.js +111 -0
  242. package/dist/query/post-edit/snapshot-contract.js.map +1 -0
  243. package/dist/query/post-edit.d.ts +5 -0
  244. package/dist/query/post-edit.js +1108 -0
  245. package/dist/query/post-edit.js.map +1 -0
  246. package/dist/query/quality.d.ts +43 -0
  247. package/dist/query/quality.js +134 -0
  248. package/dist/query/quality.js.map +1 -0
  249. package/dist/query/raw-search.d.ts +23 -0
  250. package/dist/query/raw-search.js +147 -0
  251. package/dist/query/raw-search.js.map +1 -0
  252. package/dist/query/runtime.d.ts +11 -0
  253. package/dist/query/runtime.js +79 -0
  254. package/dist/query/runtime.js.map +1 -0
  255. package/dist/query/search.d.ts +25 -0
  256. package/dist/query/search.js +429 -0
  257. package/dist/query/search.js.map +1 -0
  258. package/dist/query/session-memory.d.ts +3 -0
  259. package/dist/query/session-memory.js +108 -0
  260. package/dist/query/session-memory.js.map +1 -0
  261. package/dist/query/session.d.ts +41 -0
  262. package/dist/query/session.js +90 -0
  263. package/dist/query/session.js.map +1 -0
  264. package/dist/query/targets.d.ts +25 -0
  265. package/dist/query/targets.js +97 -0
  266. package/dist/query/targets.js.map +1 -0
  267. package/dist/query/test-commands.d.ts +10 -0
  268. package/dist/query/test-commands.js +110 -0
  269. package/dist/query/test-commands.js.map +1 -0
  270. package/dist/query/test-plan.d.ts +6 -0
  271. package/dist/query/test-plan.js +104 -0
  272. package/dist/query/test-plan.js.map +1 -0
  273. package/dist/query/tests.d.ts +48 -0
  274. package/dist/query/tests.js +444 -0
  275. package/dist/query/tests.js.map +1 -0
  276. package/dist/query/verification/shell.d.ts +20 -0
  277. package/dist/query/verification/shell.js +164 -0
  278. package/dist/query/verification/shell.js.map +1 -0
  279. package/dist/query/verification.d.ts +47 -0
  280. package/dist/query/verification.js +1123 -0
  281. package/dist/query/verification.js.map +1 -0
  282. package/dist/query/workflow.d.ts +17 -0
  283. package/dist/query/workflow.js +252 -0
  284. package/dist/query/workflow.js.map +1 -0
  285. package/dist/query/workspace-guidance.d.ts +26 -0
  286. package/dist/query/workspace-guidance.js +214 -0
  287. package/dist/query/workspace-guidance.js.map +1 -0
  288. package/dist/query/worktree-state.d.ts +22 -0
  289. package/dist/query/worktree-state.js +32 -0
  290. package/dist/query/worktree-state.js.map +1 -0
  291. package/dist/query/worktree.d.ts +16 -0
  292. package/dist/query/worktree.js +194 -0
  293. package/dist/query/worktree.js.map +1 -0
  294. package/dist/query-data.d.ts +4 -0
  295. package/dist/query-data.js +112 -0
  296. package/dist/query-data.js.map +1 -0
  297. package/dist/repo-files.d.ts +24 -0
  298. package/dist/repo-files.js +105 -0
  299. package/dist/repo-files.js.map +1 -0
  300. package/dist/resolver.d.ts +9 -0
  301. package/dist/resolver.js +555 -0
  302. package/dist/resolver.js.map +1 -0
  303. package/dist/retrieval.d.ts +46 -0
  304. package/dist/retrieval.js +783 -0
  305. package/dist/retrieval.js.map +1 -0
  306. package/dist/risk-ingest.d.ts +16 -0
  307. package/dist/risk-ingest.js +458 -0
  308. package/dist/risk-ingest.js.map +1 -0
  309. package/dist/rules.d.ts +10 -0
  310. package/dist/rules.js +107 -0
  311. package/dist/rules.js.map +1 -0
  312. package/dist/semantic/python.d.ts +9 -0
  313. package/dist/semantic/python.js +817 -0
  314. package/dist/semantic/python.js.map +1 -0
  315. package/dist/semantic/typescript.d.ts +10 -0
  316. package/dist/semantic/typescript.js +714 -0
  317. package/dist/semantic/typescript.js.map +1 -0
  318. package/dist/semantic-retrieval.d.ts +53 -0
  319. package/dist/semantic-retrieval.js +673 -0
  320. package/dist/semantic-retrieval.js.map +1 -0
  321. package/dist/session-memory/derivation.d.ts +6 -0
  322. package/dist/session-memory/derivation.js +400 -0
  323. package/dist/session-memory/derivation.js.map +1 -0
  324. package/dist/session-memory/event-log.d.ts +23 -0
  325. package/dist/session-memory/event-log.js +126 -0
  326. package/dist/session-memory/event-log.js.map +1 -0
  327. package/dist/session-memory/formatting.d.ts +7 -0
  328. package/dist/session-memory/formatting.js +86 -0
  329. package/dist/session-memory/formatting.js.map +1 -0
  330. package/dist/session-memory/model.d.ts +94 -0
  331. package/dist/session-memory/model.js +17 -0
  332. package/dist/session-memory/model.js.map +1 -0
  333. package/dist/session-memory/runtime.d.ts +24 -0
  334. package/dist/session-memory/runtime.js +289 -0
  335. package/dist/session-memory/runtime.js.map +1 -0
  336. package/dist/session-memory/store.d.ts +27 -0
  337. package/dist/session-memory/store.js +447 -0
  338. package/dist/session-memory/store.js.map +1 -0
  339. package/dist/session-memory.d.ts +1 -0
  340. package/dist/session-memory.js +2 -0
  341. package/dist/session-memory.js.map +1 -0
  342. package/dist/static-analysis.d.ts +36 -0
  343. package/dist/static-analysis.js +505 -0
  344. package/dist/static-analysis.js.map +1 -0
  345. package/dist/symbol-report-ingest.d.ts +8 -0
  346. package/dist/symbol-report-ingest.js +504 -0
  347. package/dist/symbol-report-ingest.js.map +1 -0
  348. package/dist/task-snapshots.d.ts +41 -0
  349. package/dist/task-snapshots.js +430 -0
  350. package/dist/task-snapshots.js.map +1 -0
  351. package/dist/types.d.ts +848 -0
  352. package/dist/types.js +12 -0
  353. package/dist/types.js.map +1 -0
  354. package/dist/util.d.ts +11 -0
  355. package/dist/util.js +63 -0
  356. package/dist/util.js.map +1 -0
  357. package/dist/version.d.ts +1 -0
  358. package/dist/version.js +5 -0
  359. package/dist/version.js.map +1 -0
  360. package/package.json +81 -0
  361. package/plugins/codexa/.codex-plugin/plugin.json +38 -0
  362. package/plugins/codexa/.mcp.json +20 -0
  363. package/plugins/codexa/scripts/codexa-mcp.js +100 -0
  364. package/plugins/codexa/skills/codexa/SKILL.md +48 -0
package/dist/cli.js ADDED
@@ -0,0 +1,1034 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from "commander";
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { buildIndexLocked } from "./indexer.js";
6
+ import { defaultAutonomyMode, effectiveAutonomyMode, parseAutonomyMode, setAutonomyMode } from "./autonomy.js";
7
+ import { checkGithubSync } from "./github-sync.js";
8
+ import { publishProjectGithubRelease } from "./github-release.js";
9
+ import { runDoctor } from "./doctor.js";
10
+ import { initializeProject, sessionStartSummary } from "./init.js";
11
+ import { runLiveIndexer } from "./live-index.js";
12
+ import { serveMcp, serveMcpHttp } from "./mcp.js";
13
+ import { resolveMcpRepoRoot } from "./mcp-repo-root.js";
14
+ import { buildSemanticIndex, semanticProviderFromValue } from "./semantic-retrieval.js";
15
+ import { updateStaticAnalysisReports } from "./static-analysis.js";
16
+ import { recordAdvisoryHookEvent, runPostEditHook, runPreEditHook } from "./cli/hooks.js";
17
+ import { contextPackQuery, callersQuery, calleesQuery, changePlanQuery, dependencyPathQuery, diffImpactQuery, fileContextQuery, findContextQuery, focusBriefQuery, impactQuery, placeholderReportQuery, postEditReviewQuery, repoMapQuery, searchQuery, sessionMemoryQuery, statusQuery, symbolContextQuery, taskBriefQuery, testPlanQuery, workflowPathQuery } from "./queries.js";
18
+ import { RAW_SEARCH_EXPLICIT_PATTERN_LIMIT } from "./query/raw-search.js";
19
+ import { runEval } from "./eval.js";
20
+ import { CODEXA_VERSION } from "./version.js";
21
+ const program = new Command();
22
+ const cliModulePath = fileURLToPath(import.meta.url);
23
+ const defaultCliPath = cliModulePath.endsWith(`${path.sep}src${path.sep}cli.ts`)
24
+ ? path.resolve(path.dirname(cliModulePath), "../dist/cli.js")
25
+ : cliModulePath;
26
+ program
27
+ .name(invokedCliName())
28
+ .description("Codex-native codebase intelligence context compiler and MCP context server.")
29
+ .version(CODEXA_VERSION);
30
+ program
31
+ .command("init")
32
+ .argument("[repo]", "repository root; defaults to the current git root")
33
+ .option("--server-name <name>", "MCP server name to write in .codex/config.toml")
34
+ .option("--cli-path <path>", "path Codex should use to start Codexa", defaultCliPath)
35
+ .option("--auto-refresh", "allow Codexa MCP context tools to refresh stale generated artifacts", true)
36
+ .option("--no-auto-refresh", "disable Codexa MCP auto-refresh in generated config")
37
+ .option("--hooks", "write the repo-local Codex SessionStart hook", true)
38
+ .option("--no-hooks", "do not write hooks.json")
39
+ .option("--index", "index the repository immediately", true)
40
+ .option("--no-index", "only write Codex config and hooks")
41
+ .option("--tools <profile>", "MCP tool exposure profile: core (primary loop only, cheaper per turn) or full", parseToolProfile, "full")
42
+ .option("--agents-md", "write a managed Codexa workflow block into the repo's AGENTS.md", false)
43
+ .description("Initialize Codexa for a project so future Codex sessions discover it automatically.")
44
+ .action(async (repo, opts) => {
45
+ const result = await initializeProject(repo, {
46
+ autoRefresh: opts.autoRefresh,
47
+ cliPath: opts.cliPath,
48
+ hooks: opts.hooks,
49
+ index: opts.index,
50
+ serverName: opts.serverName,
51
+ toolProfile: opts.tools,
52
+ agentsMd: opts.agentsMd
53
+ });
54
+ console.log(`Codexa initialized for ${result.repoRoot}`);
55
+ console.log(`Config: ${result.configPath}`);
56
+ if (result.hooksPath) {
57
+ console.log(`Hook: ${result.hooksPath}`);
58
+ }
59
+ if (result.agentsMdPath) {
60
+ console.log(`AGENTS.md: ${result.agentsMdPath}`);
61
+ }
62
+ console.log(`MCP server: ${result.serverName}`);
63
+ if (result.indexed) {
64
+ console.log(`Indexed ${result.indexed.files} files, ${result.indexed.symbols} symbols, ${result.indexed.usageSites} usage sites.`);
65
+ }
66
+ else {
67
+ console.log("Index: skipped");
68
+ }
69
+ console.log(`Next Codex sessions only need: focus on ${result.repoRoot}`);
70
+ });
71
+ program
72
+ .command("autonomy")
73
+ .argument("[repo]", "repository root for a repo-specific user policy; omitted with --global sets the user default")
74
+ .option("--mode <mode>", "set user-owned autonomy mode: read-only or full-access", parseAutonomyOption)
75
+ .option("--global", "set the user default instead of a repo-specific policy", false)
76
+ .option("--json", "print JSON")
77
+ .description("Inspect or set Codexa's user-owned autonomy policy. Repo config cannot enable execution.")
78
+ .action(async (repo, opts) => {
79
+ const repoRoot = repo ? path.resolve(repo) : process.cwd();
80
+ const status = opts.mode
81
+ ? await setAutonomyMode({ repoRoot: opts.global ? undefined : repoRoot, global: opts.global || !repo, mode: opts.mode })
82
+ : opts.global
83
+ ? await defaultAutonomyMode()
84
+ : await effectiveAutonomyMode(repoRoot);
85
+ if (opts.json) {
86
+ console.log(JSON.stringify(status, null, 2));
87
+ return;
88
+ }
89
+ console.log(`Codexa autonomy: ${status.mode}`);
90
+ console.log(`Source: ${status.source}`);
91
+ console.log(`Config: ${status.configPath}`);
92
+ if (status.repoRoot) {
93
+ console.log(`Repo: ${status.repoRoot}`);
94
+ }
95
+ });
96
+ program
97
+ .command("hook-pre-edit")
98
+ .argument("<repo>", "repository root")
99
+ .description("Cheap hook helper that reminds Codex when no change-plan snapshot exists before an edit.")
100
+ .action(async (repo) => {
101
+ await runPreEditHook(repo);
102
+ });
103
+ program
104
+ .command("hook-post-edit")
105
+ .argument("<repo>", "repository root")
106
+ .description("Bounded hook helper that runs the post-edit review packet after edit tools.")
107
+ .action(async (repo) => {
108
+ await runPostEditHook(repo);
109
+ });
110
+ program
111
+ .command("index")
112
+ .argument("<repo>", "repository root to index")
113
+ .description("Index a repository and write .codex/codebase artifacts.")
114
+ .action(async (repo) => {
115
+ const index = await buildIndexLocked({ repoRoot: path.resolve(repo), writeArtifacts: true });
116
+ console.log(`Indexed ${index.files.length} files, ${index.symbols.length} symbols, ${index.usageSites.length} usage sites.`);
117
+ console.log(`Artifacts: ${path.join(path.resolve(repo), ".codex/codebase")}`);
118
+ });
119
+ program
120
+ .command("semantic-index")
121
+ .argument("<repo>", "repository root to embed for first-class hybrid semantic retrieval")
122
+ .requiredOption("--provider <provider>", "embedding provider: openai or local-command", parseSemanticProvider)
123
+ .option("--model <model>", "embedding model name; defaults to provider-specific default")
124
+ .option("--dimensions <n>", "embedding dimensions when the provider supports it", parseIntOption)
125
+ .option("--command <command>", "local embedding command for --provider local-command")
126
+ .option("--arg <arg...>", "argument for the local embedding command; repeat or pass multiple values")
127
+ .option("--timeout-ms <n>", "embedding provider timeout in milliseconds", parseIntOption, 60_000)
128
+ .option("--batch-size <n>", "number of chunks to send per provider request", parseIntOption, 64)
129
+ .option("--max-files <n>", "maximum indexed files to embed", parseIntOption, 750)
130
+ .description("Build the semantic retrieval cache used by first-class hybrid Codexa search and task context.")
131
+ .action(async (repo, opts) => {
132
+ const repoRoot = path.resolve(repo);
133
+ const index = await buildIndexLocked({ repoRoot, writeArtifacts: true });
134
+ const result = await buildSemanticIndex(repoRoot, index, {
135
+ provider: opts.provider,
136
+ model: opts.model,
137
+ dimensions: opts.dimensions,
138
+ command: opts.command,
139
+ args: opts.arg,
140
+ timeoutMs: opts.timeoutMs,
141
+ batchSize: opts.batchSize,
142
+ maxFiles: opts.maxFiles
143
+ });
144
+ console.log(`Codexa semantic index built for ${result.repoRoot}`);
145
+ console.log(`Provider: ${result.provider}; model: ${result.model}; dimensions: ${result.dimensions}`);
146
+ console.log(`Chunks: ${result.chunkCount}`);
147
+ console.log(`Cache: ${result.cacheDir}`);
148
+ });
149
+ program
150
+ .command("watch")
151
+ .argument("<repo>", "repository root to keep indexed")
152
+ .option("--debounce-ms <n>", "milliseconds to wait after detected changes before rebuilding", parseIntOption, 750)
153
+ .option("--poll-ms <n>", "fallback git freshness polling interval in milliseconds", parseIntOption, 2000)
154
+ .option("--initial", "index immediately before watching", true)
155
+ .option("--no-initial", "wait for the next detected change instead of indexing immediately")
156
+ .option("--max-runs <n>", "stop after N index runs; useful for smoke tests and hooks", parseIntOption)
157
+ .description("Keep .codex/codebase artifacts live with debounced filesystem watching plus git freshness polling.")
158
+ .action(async (repo, opts) => {
159
+ const controller = new AbortController();
160
+ process.once("SIGINT", () => controller.abort());
161
+ process.once("SIGTERM", () => controller.abort());
162
+ const summary = await runLiveIndexer(path.resolve(repo), {
163
+ debounceMs: opts.debounceMs,
164
+ pollMs: opts.pollMs,
165
+ initial: opts.initial,
166
+ maxRuns: opts.maxRuns,
167
+ signal: controller.signal,
168
+ onEvent: logLiveIndexEvent
169
+ });
170
+ if (summary.runs.length === 0) {
171
+ console.error(`Codexa watch stopped for ${summary.repoRoot}; no index runs completed.`);
172
+ }
173
+ });
174
+ program
175
+ .command("static-analysis")
176
+ .argument("<repo>", "repository root")
177
+ .option("--semgrep-report <path...>", "existing Semgrep JSON/SARIF report to copy into .codex/static-analysis")
178
+ .option("--codeql-report <path...>", "existing CodeQL SARIF report to copy into .codex/static-analysis")
179
+ .option("--sarif <path...>", "generic SARIF report to copy into .codex/static-analysis")
180
+ .option("--generic-report <path...>", "generic Codexa risk JSON report to copy into .codex/static-analysis")
181
+ .option("--symbol-report <path...>", "CodexaSymbolReportV1 JSON report to copy into .codex/static-analysis")
182
+ .option("--run-semgrep", "run an installed Semgrep CLI and ingest JSON output", false)
183
+ .option("--semgrep-config <config...>", "Semgrep config value; repeat or pass multiple values", ["p/default"])
184
+ .option("--run-codeql", "run an installed CodeQL CLI for JavaScript/TypeScript and Python and ingest SARIF output", false)
185
+ .option("--codeql-language <language...>", "CodeQL language ids; supported in Codexa helper: javascript-typescript, python", ["javascript-typescript", "python"])
186
+ .option("--codeql-suite <suite>", "CodeQL suite suffix to use with bundled query packs", "code-scanning")
187
+ .option("--run-shellcheck", "run an installed ShellCheck CLI for tracked shell scripts and ingest findings", false)
188
+ .option("--timeout-ms <n>", "scanner command timeout in milliseconds", parseIntOption, 600_000)
189
+ .option("--index", "reindex after reports are copied or generated", true)
190
+ .option("--no-index", "only copy/generate reports")
191
+ .description("Import Semgrep/CodeQL/SARIF risk reports, optionally run user-installed scanners, and fold findings into Codexa context.")
192
+ .action(async (repo, opts) => {
193
+ const result = await updateStaticAnalysisReports(path.resolve(repo), {
194
+ semgrepReports: opts.semgrepReport,
195
+ codeqlReports: opts.codeqlReport,
196
+ sarifReports: opts.sarif,
197
+ genericReports: opts.genericReport,
198
+ symbolReports: opts.symbolReport,
199
+ runSemgrep: opts.runSemgrep,
200
+ semgrepConfigs: opts.semgrepConfig,
201
+ runCodeql: opts.runCodeql,
202
+ codeqlLanguages: opts.codeqlLanguage,
203
+ codeqlSuite: opts.codeqlSuite,
204
+ runShellcheck: opts.runShellcheck,
205
+ timeoutMs: opts.timeoutMs,
206
+ index: opts.index
207
+ });
208
+ console.log(result.text);
209
+ });
210
+ program
211
+ .command("github-sync-check")
212
+ .argument("[repo]", "repository root; defaults to the current directory", process.cwd())
213
+ .option("--remote <name>", "git remote name to inspect", "origin")
214
+ .option("--branch <branch>", "branch to inspect; defaults to the current branch")
215
+ .option("--no-network", "skip remote ls-remote and push dry-run checks")
216
+ .option("--no-push-check", "skip git push --dry-run")
217
+ .option("--no-gh-check", "skip GitHub CLI detection")
218
+ .option("--json", "emit structured JSON")
219
+ .description("Diagnose whether a Codexa repo is ready for normal authenticated GitHub source sync.")
220
+ .action(async (repo, opts) => {
221
+ const result = await checkGithubSync(path.resolve(repo), {
222
+ remote: opts.remote,
223
+ branch: opts.branch,
224
+ skipNetwork: !opts.network,
225
+ checkPush: opts.pushCheck,
226
+ checkGh: opts.ghCheck
227
+ });
228
+ console.log(opts.json ? JSON.stringify(result.data, null, 2) : result.text);
229
+ });
230
+ program
231
+ .command("github-release")
232
+ .argument("[repo]", "repository root; defaults to the current directory", process.cwd())
233
+ .option("--tag <tag>", "release tag; defaults to v<package.json version>")
234
+ .option("--title <title>", "GitHub Release title; defaults to <project name> <tag>")
235
+ .option("--project-name <name>", "project display name for release notes; defaults to package.json name or repo directory")
236
+ .option("--repo <owner/name>", "GitHub repo slug; defaults to origin remote")
237
+ .option("--remote <name>", "git remote name", "origin")
238
+ .option("--branch <branch>", "branch to push; defaults to current branch")
239
+ .option("--latest <mode>", "GitHub latest marker behavior: auto, true, or false", "auto")
240
+ .option("--notes-file <path>", "write generated release notes to a file and stop before git/gh mutation")
241
+ .option("--dry-run", "print intended tag, push, and release actions without mutating git or GitHub", false)
242
+ .option("--push", "push the branch and tag to GitHub", true)
243
+ .option("--no-push", "leave branch and tag local")
244
+ .option("--create-tag", "create the annotated release tag when missing", true)
245
+ .option("--no-create-tag", "require an existing tag and skip tag creation")
246
+ .option("--github-release", "create or update the GitHub Release timeline entry", true)
247
+ .option("--no-github-release", "skip GitHub Release creation/update")
248
+ .option("--allow-dirty", "allow release notes/dry-run while the working tree is dirty", false)
249
+ .option("--allow-non-main", "allow releasing from a branch other than main", false)
250
+ .description("Create a visible GitHub Release with exact continue and forward-only revert commands.")
251
+ .action(async (repo, opts) => {
252
+ const result = await publishProjectGithubRelease(path.resolve(repo), {
253
+ tag: opts.tag,
254
+ title: opts.title,
255
+ projectName: opts.projectName,
256
+ repo: opts.repo,
257
+ remote: opts.remote,
258
+ branch: opts.branch,
259
+ latest: opts.latest,
260
+ notesFile: opts.notesFile,
261
+ dryRun: opts.dryRun,
262
+ push: opts.push,
263
+ createTag: opts.createTag,
264
+ githubRelease: opts.githubRelease,
265
+ allowDirty: opts.allowDirty,
266
+ allowNonMain: opts.allowNonMain
267
+ });
268
+ console.log(result.text);
269
+ });
270
+ program
271
+ .command("doctor")
272
+ .argument("[repo]", "repository root; defaults to the current directory", process.cwd())
273
+ .option("--json", "emit structured JSON")
274
+ .option("--mcp-readiness", "include Codex MCP readiness checks")
275
+ .option("--workspace-focus-file <path>", "workspace focus file to consult when diagnosing a workspace launch root")
276
+ .option("--workspace-session <id>", "active WORKING.md session row to prefer when diagnosing a workspace launch root")
277
+ .description("Diagnose local Codexa wiring, index freshness, hooks, and generated state.")
278
+ .action(async (repo, opts) => {
279
+ const result = await runDoctor(path.resolve(repo), {
280
+ json: opts.json,
281
+ mcpReadiness: opts.mcpReadiness,
282
+ workspaceFocusFile: opts.workspaceFocusFile ? path.resolve(opts.workspaceFocusFile) : undefined,
283
+ workspaceSessionId: opts.workspaceSession
284
+ });
285
+ console.log(result.text);
286
+ if (!result.ok) {
287
+ process.exitCode = 1;
288
+ }
289
+ });
290
+ program
291
+ .command("status")
292
+ .argument("<repo>", "repository root")
293
+ .description("Report Codexa index freshness and parser status.")
294
+ .action(async (repo) => printQuery(await statusQuery(await resolveQueryRepoRoot(repo))));
295
+ program
296
+ .command("repo-map")
297
+ .argument("<repo>", "repository root")
298
+ .option("--limit <n>", "maximum files/modules to return", parseIntOption, 20)
299
+ .option("--budget <tokens>", "approximate token budget", parseIntOption, 1500)
300
+ .option("--auto-refresh", "refresh a stale or missing index before querying", true)
301
+ .option("--no-auto-refresh", "do not refresh a stale or missing index before querying")
302
+ .description("Print the top-ranked repo map, refreshing stale artifacts when needed.")
303
+ .action(async (repo, opts) => printQuery(await repoMapQuery(await resolveQueryRepoRoot(repo), opts.limit, { autoRefresh: opts.autoRefresh }, opts.budget)));
304
+ program
305
+ .command("find-context")
306
+ .argument("<repo>", "repository root")
307
+ .requiredOption("--query <query>", "search query")
308
+ .option("--limit <n>", "maximum matches", parseIntOption, 12)
309
+ .option("--semantic", "force the semantic retrieval lane even when auto-detection would skip it")
310
+ .option("--no-semantic", "disable automatic semantic retrieval for this query")
311
+ .option("--semantic-provider <provider>", "semantic query provider: openai or local-command", parseSemanticProvider)
312
+ .option("--semantic-model <model>", "semantic embedding model name")
313
+ .option("--semantic-dimensions <n>", "semantic embedding dimensions", parseIntOption)
314
+ .option("--semantic-command <command>", "local semantic embedding command for --semantic-provider local-command")
315
+ .option("--semantic-arg <arg...>", "argument for the local semantic embedding command")
316
+ .option("--semantic-timeout-ms <n>", "semantic query timeout in milliseconds", parseIntOption)
317
+ .option("--semantic-batch-size <n>", "semantic query batch size", parseIntOption)
318
+ .option("--auto-refresh", "refresh a stale or missing index before querying", true)
319
+ .option("--no-auto-refresh", "do not refresh a stale or missing index before querying")
320
+ .description("Find matching files, symbols, and usage sites.")
321
+ .action(async (repo, opts) => printQuery(await findContextQuery(await resolveQueryRepoRoot(repo), opts.query, opts.limit, queryOptionsFromCli(opts))));
322
+ program
323
+ .command("search")
324
+ .argument("<repo>", "repository root")
325
+ .requiredOption("--query <query>", "search query")
326
+ .option("--pattern <pattern...>", `additional literal raw-search patterns; pass up to ${RAW_SEARCH_EXPLICIT_PATTERN_LIMIT} variants with the query`)
327
+ .option("--limit <n>", "maximum matches", parseIntOption, 12)
328
+ .option("--raw", "include raw hit lines", true)
329
+ .option("--no-raw", "summarize raw hit files without lines")
330
+ .option("--semantic", "force the semantic retrieval lane even when auto-detection would skip it")
331
+ .option("--no-semantic", "disable automatic semantic retrieval for this query")
332
+ .option("--semantic-provider <provider>", "semantic query provider: openai or local-command", parseSemanticProvider)
333
+ .option("--semantic-model <model>", "semantic embedding model name")
334
+ .option("--semantic-dimensions <n>", "semantic embedding dimensions", parseIntOption)
335
+ .option("--semantic-command <command>", "local semantic embedding command for --semantic-provider local-command")
336
+ .option("--semantic-arg <arg...>", "argument for the local semantic embedding command")
337
+ .option("--semantic-timeout-ms <n>", "semantic query timeout in milliseconds", parseIntOption)
338
+ .option("--semantic-batch-size <n>", "semantic query batch size", parseIntOption)
339
+ .option("--auto-refresh", "refresh a stale or missing index before querying", true)
340
+ .option("--no-auto-refresh", "do not refresh a stale or missing index before querying")
341
+ .description("Run first-class hybrid semantic search over raw hits, Codexa ranking, tests, and known gaps.")
342
+ .action(async (repo, opts) => printQuery(await searchQuery(await resolveQueryRepoRoot(repo), { query: opts.query, patterns: opts.pattern, limit: opts.limit, includeRaw: opts.raw }, queryOptionsFromCli(opts))));
343
+ program
344
+ .command("placeholder-report")
345
+ .argument("<repo>", "repository root")
346
+ .option("--include-tests", "include test files in placeholder findings", false)
347
+ .option("--include-docs", "include documentation files in placeholder findings", false)
348
+ .option("--include-generated", "include generated files in placeholder findings", false)
349
+ .option("--limit <n>", "maximum findings", parseIntOption, 40)
350
+ .option("--budget <tokens>", "approximate token budget", parseIntOption, 2400)
351
+ .option("--auto-refresh", "refresh a stale or missing index before querying", true)
352
+ .option("--no-auto-refresh", "do not refresh a stale or missing index before querying")
353
+ .description("Report indexed placeholder, dummy, TODO, and stub code/data findings.")
354
+ .action(async (repo, opts) => printQuery(await placeholderReportQuery(await resolveQueryRepoRoot(repo), {
355
+ includeTests: opts.includeTests,
356
+ includeDocs: opts.includeDocs,
357
+ includeGenerated: opts.includeGenerated,
358
+ limit: opts.limit,
359
+ tokenBudget: opts.budget
360
+ }, { autoRefresh: opts.autoRefresh })));
361
+ program
362
+ .command("explain")
363
+ .argument("<repo>", "repository root")
364
+ .option("--file <path>", "file to explain")
365
+ .option("--symbol <symbol>", "symbol id or name to explain")
366
+ .option("--depth <n>", "symbol neighborhood depth, 1-3", parseIntOption)
367
+ .option("--language <language>", "optional symbol language filter")
368
+ .option("--no-evidence", "omit compact edge evidence from symbol_context output")
369
+ .option("--lsp", "include optional read-only LSP assist for TypeScript, JavaScript, or Python")
370
+ .option("--lsp-timeout-ms <n>", "LSP request timeout in milliseconds", parseIntOption)
371
+ .option("--lsp-max-files <n>", "maximum files to inspect with LSP assist", parseIntOption)
372
+ .option("--auto-refresh", "refresh a stale or missing index before querying", true)
373
+ .option("--no-auto-refresh", "do not refresh a stale or missing index before querying")
374
+ .description("Return compact evidence for a file or symbol.")
375
+ .action(async (repo, opts) => {
376
+ const queryOptions = queryOptionsFromCli(opts);
377
+ const repoRoot = await resolveQueryRepoRoot(repo);
378
+ if (opts.symbol) {
379
+ printQuery(await symbolContextQuery(repoRoot, opts.symbol, queryOptions, { depth: opts.depth, language: opts.language, includeEvidence: opts.evidence }));
380
+ return;
381
+ }
382
+ if (opts.file) {
383
+ printQuery(await fileContextQuery(repoRoot, opts.file, queryOptions));
384
+ return;
385
+ }
386
+ throw new Error("explain requires --file or --symbol");
387
+ });
388
+ program
389
+ .command("impact")
390
+ .argument("<repo>", "repository root")
391
+ .option("--file <path>", "file to analyze")
392
+ .option("--symbol <symbol>", "symbol id or name to analyze")
393
+ .option("--change-type <type>", "change type: style, api, behavior, rename, delete, unknown", parseChangeType, "unknown")
394
+ .option("--depth <n>", "import/test traversal depth, 1-3; default is adaptive by change type", parseIntOption)
395
+ .option("--auto-refresh", "refresh a stale or missing index before querying", true)
396
+ .option("--no-auto-refresh", "do not refresh a stale or missing index before querying")
397
+ .description("Return blast-radius evidence for a file or symbol.")
398
+ .action(async (repo, opts) => {
399
+ if (!opts.file && !opts.symbol) {
400
+ throw new Error("impact requires --file or --symbol");
401
+ }
402
+ printQuery(await impactQuery(await resolveQueryRepoRoot(repo), opts, { autoRefresh: opts.autoRefresh }));
403
+ });
404
+ program
405
+ .command("diff-impact")
406
+ .argument("<repo>", "repository root")
407
+ .option("--auto-refresh", "refresh a stale or missing index before querying", true)
408
+ .option("--no-auto-refresh", "do not refresh a stale or missing index before querying")
409
+ .description("Return impact context for the current dirty git diff.")
410
+ .action(async (repo, opts) => printQuery(await diffImpactQuery(await resolveQueryRepoRoot(repo), { autoRefresh: opts.autoRefresh })));
411
+ program
412
+ .command("test-plan")
413
+ .argument("<repo>", "repository root")
414
+ .option("--diff", "use current dirty git diff", true)
415
+ .option("--change-type <type>", "change type: style, api, behavior, rename, delete, unknown", parseChangeType, "unknown")
416
+ .option("--auto-refresh", "refresh a stale or missing index before querying", true)
417
+ .option("--no-auto-refresh", "do not refresh a stale or missing index before querying")
418
+ .description("Recommend targeted tests.")
419
+ .action(async (repo, opts) => printQuery(await testPlanQuery(await resolveQueryRepoRoot(repo), opts.diff, {
420
+ autoRefresh: opts.autoRefresh,
421
+ changeType: opts.changeType
422
+ })));
423
+ program
424
+ .command("brief")
425
+ .argument("<repo>", "repository root")
426
+ .option("--task <task>", "task description to shape the brief")
427
+ .option("--file <path...>", "focus file path; repeat or pass multiple paths")
428
+ .option("--symbol <symbol...>", "focus symbol id, qualified name, or unique name")
429
+ .option("--query <query>", "search query to seed context")
430
+ .option("--change-type <type>", "change type: style, api, behavior, rename, delete, unknown", parseChangeType, "unknown")
431
+ .option("--diff", "include current dirty git diff", true)
432
+ .option("--no-diff", "ignore current dirty git diff")
433
+ .option("--budget <tokens>", "approximate token budget", parseIntOption, 3000)
434
+ .option("--limit <n>", "maximum focus items", parseIntOption, 10)
435
+ .option("--snippets", "include source snippets", true)
436
+ .option("--no-snippets", "omit source snippets")
437
+ .option("--semantic", "force the semantic retrieval lane even when auto-detection would skip it")
438
+ .option("--no-semantic", "disable automatic semantic retrieval for this query")
439
+ .option("--semantic-provider <provider>", "semantic query provider: openai or local-command", parseSemanticProvider)
440
+ .option("--semantic-model <model>", "semantic embedding model name")
441
+ .option("--semantic-dimensions <n>", "semantic embedding dimensions", parseIntOption)
442
+ .option("--semantic-command <command>", "local semantic embedding command for --semantic-provider local-command")
443
+ .option("--semantic-arg <arg...>", "argument for the local semantic embedding command")
444
+ .option("--semantic-timeout-ms <n>", "semantic query timeout in milliseconds", parseIntOption)
445
+ .option("--semantic-batch-size <n>", "semantic query batch size", parseIntOption)
446
+ .option("--lsp", "include optional read-only LSP assist for selected focus files")
447
+ .option("--lsp-timeout-ms <n>", "LSP request timeout in milliseconds", parseIntOption)
448
+ .option("--lsp-max-files <n>", "maximum files to inspect with LSP assist", parseIntOption)
449
+ .option("--auto-refresh", "refresh a stale or missing index before querying", true)
450
+ .option("--no-auto-refresh", "do not refresh a stale or missing index before querying")
451
+ .description("Build the default Codex-first task brief with bounded impact, risks, tests, freshness, and snippets.")
452
+ .action(async (repo, opts) => printQuery(await taskBriefQuery(await resolveQueryRepoRoot(repo), {
453
+ task: opts.task,
454
+ files: opts.file,
455
+ symbols: opts.symbol,
456
+ query: opts.query,
457
+ changeType: opts.changeType,
458
+ diff: opts.diff,
459
+ tokenBudget: opts.budget,
460
+ limit: opts.limit,
461
+ includeSnippets: opts.snippets
462
+ }, queryOptionsFromCli(opts))));
463
+ program
464
+ .command("context-pack")
465
+ .argument("<repo>", "repository root")
466
+ .option("--task <task>", "task description to shape the context pack")
467
+ .option("--file <path...>", "focus file path; repeat or pass multiple paths")
468
+ .option("--symbol <symbol...>", "focus symbol id, qualified name, or unique name")
469
+ .option("--query <query>", "search query to seed context")
470
+ .option("--change-type <type>", "change type: style, api, behavior, rename, delete, unknown", parseChangeType, "unknown")
471
+ .option("--diff", "include current dirty git diff", true)
472
+ .option("--no-diff", "ignore current dirty git diff")
473
+ .option("--budget <tokens>", "approximate token budget", parseIntOption, 4000)
474
+ .option("--limit <n>", "maximum focus items", parseIntOption, 12)
475
+ .option("--snippets", "include source snippets", true)
476
+ .option("--no-snippets", "omit source snippets")
477
+ .option("--semantic", "force the semantic retrieval lane even when auto-detection would skip it")
478
+ .option("--no-semantic", "disable automatic semantic retrieval for this query")
479
+ .option("--semantic-provider <provider>", "semantic query provider: openai or local-command", parseSemanticProvider)
480
+ .option("--semantic-model <model>", "semantic embedding model name")
481
+ .option("--semantic-dimensions <n>", "semantic embedding dimensions", parseIntOption)
482
+ .option("--semantic-command <command>", "local semantic embedding command for --semantic-provider local-command")
483
+ .option("--semantic-arg <arg...>", "argument for the local semantic embedding command")
484
+ .option("--semantic-timeout-ms <n>", "semantic query timeout in milliseconds", parseIntOption)
485
+ .option("--semantic-batch-size <n>", "semantic query batch size", parseIntOption)
486
+ .option("--lsp", "include optional read-only LSP assist for selected focus files")
487
+ .option("--lsp-timeout-ms <n>", "LSP request timeout in milliseconds", parseIntOption)
488
+ .option("--lsp-max-files <n>", "maximum files to inspect with LSP assist", parseIntOption)
489
+ .option("--auto-refresh", "refresh a stale or missing index before querying", true)
490
+ .option("--no-auto-refresh", "do not refresh a stale or missing index before querying")
491
+ .description("Build a compact task-shaped Codexa context pack.")
492
+ .action(async (repo, opts) => printQuery(await contextPackQuery(await resolveQueryRepoRoot(repo), {
493
+ task: opts.task,
494
+ files: opts.file,
495
+ symbols: opts.symbol,
496
+ query: opts.query,
497
+ changeType: opts.changeType,
498
+ diff: opts.diff,
499
+ tokenBudget: opts.budget,
500
+ limit: opts.limit,
501
+ includeSnippets: opts.snippets
502
+ }, queryOptionsFromCli(opts))));
503
+ program
504
+ .command("focus-brief")
505
+ .argument("<repo>", "repository root")
506
+ .option("--task <task>", "natural-language task to classify and focus")
507
+ .option("--budget <tokens>", "approximate token budget", parseIntOption, 2400)
508
+ .option("--limit <n>", "maximum focus items", parseIntOption, 10)
509
+ .option("--diff", "include current dirty git diff", true)
510
+ .option("--no-diff", "ignore current dirty git diff")
511
+ .option("--semantic", "force the semantic retrieval lane even when auto-detection would skip it")
512
+ .option("--no-semantic", "disable automatic semantic retrieval for this query")
513
+ .option("--semantic-provider <provider>", "semantic query provider: openai or local-command", parseSemanticProvider)
514
+ .option("--semantic-model <model>", "semantic embedding model name")
515
+ .option("--semantic-dimensions <n>", "semantic embedding dimensions", parseIntOption)
516
+ .option("--semantic-command <command>", "local semantic embedding command for --semantic-provider local-command")
517
+ .option("--semantic-arg <arg...>", "argument for the local semantic embedding command")
518
+ .option("--semantic-timeout-ms <n>", "semantic query timeout in milliseconds", parseIntOption)
519
+ .option("--semantic-batch-size <n>", "semantic query batch size", parseIntOption)
520
+ .option("--auto-refresh", "refresh a stale or missing index before querying", true)
521
+ .option("--no-auto-refresh", "do not refresh a stale or missing index before querying")
522
+ .description("Classify a broad task, choose likely subsystems, and recommend the next Codexa call.")
523
+ .action(async (repo, opts) => printQuery(await focusBriefQuery(await resolveQueryRepoRoot(repo), { task: opts.task, tokenBudget: opts.budget, limit: opts.limit, diff: opts.diff }, queryOptionsFromCli(opts))));
524
+ program
525
+ .command("session-context")
526
+ .argument("<repo>", "repository root")
527
+ .option("--task <task>", "optional task to shape startup context")
528
+ .option("--budget <tokens>", "approximate token budget", parseIntOption, 2400)
529
+ .option("--limit <n>", "maximum focus items", parseIntOption, 10)
530
+ .option("--diff", "include current dirty git diff", true)
531
+ .option("--no-diff", "ignore current dirty git diff")
532
+ .option("--semantic", "force the semantic retrieval lane even when auto-detection would skip it")
533
+ .option("--no-semantic", "disable automatic semantic retrieval for this query")
534
+ .option("--semantic-provider <provider>", "semantic query provider: openai or local-command", parseSemanticProvider)
535
+ .option("--semantic-model <model>", "semantic embedding model name")
536
+ .option("--semantic-dimensions <n>", "semantic embedding dimensions", parseIntOption)
537
+ .option("--semantic-command <command>", "local semantic embedding command for --semantic-provider local-command")
538
+ .option("--semantic-arg <arg...>", "argument for the local semantic embedding command")
539
+ .option("--semantic-timeout-ms <n>", "semantic query timeout in milliseconds", parseIntOption)
540
+ .option("--semantic-batch-size <n>", "semantic query batch size", parseIntOption)
541
+ .option("--auto-refresh", "refresh a stale or missing index before querying", true)
542
+ .option("--no-auto-refresh", "do not refresh a stale or missing index before querying")
543
+ .description("Print the Codexa focus/session packet used when Codex focuses a project.")
544
+ .action(async (repo, opts) => printQuery(await focusBriefQuery(await resolveQueryRepoRoot(repo), { task: opts.task, tokenBudget: opts.budget, limit: opts.limit, diff: opts.diff }, queryOptionsFromCli(opts))));
545
+ program
546
+ .command("session-memory")
547
+ .argument("<repo>", "repository root")
548
+ .option("--action <action>", "summary, read, remember, or compact", parseSessionMemoryAction, "summary")
549
+ .option("--session-id <id>", "session memory id; defaults to the latest local session")
550
+ .option("--task-id <id>", "task snapshot id to filter or attach memory")
551
+ .option("--task <task>", "task text to attach to remembered entries")
552
+ .option("--kind <kind...>", "memory kind filter; repeat or pass multiple values")
553
+ .option("--file <path...>", "file scope filter; repeat or pass multiple values")
554
+ .option("--symbol <symbol...>", "symbol id scope filter; repeat or pass multiple values")
555
+ .option("--topic <topic...>", "topic substring filter; repeat or pass multiple values")
556
+ .option("--entry-json <json...>", "entry JSON for --action remember; repeat for multiple entries")
557
+ .option("--limit <n>", "maximum entries", parseIntOption, 20)
558
+ .option("--budget <tokens>", "approximate token budget", parseIntOption, 1800)
559
+ .option("--include-stale", "include stale entries", true)
560
+ .option("--no-include-stale", "hide stale entries")
561
+ .option("--auto-refresh", "refresh a stale or missing index before querying", true)
562
+ .option("--no-auto-refresh", "do not refresh a stale or missing index before querying")
563
+ .description("Read, summarize, compact, or explicitly remember Codexa session working memory.")
564
+ .action(async (repo, opts) => printQuery(await sessionMemoryQuery(await resolveQueryRepoRoot(repo), {
565
+ action: opts.action,
566
+ sessionId: opts.sessionId,
567
+ taskId: opts.taskId,
568
+ task: opts.task,
569
+ kinds: parseSessionMemoryKinds(opts.kind),
570
+ files: opts.file,
571
+ symbols: opts.symbol,
572
+ topics: opts.topic,
573
+ entries: parseSessionMemoryEntries(opts.entryJson),
574
+ limit: opts.limit,
575
+ tokenBudget: opts.budget,
576
+ includeStale: opts.includeStale
577
+ }, queryOptionsFromCli(opts))));
578
+ program
579
+ .command("callers")
580
+ .argument("<repo>", "repository root")
581
+ .option("--file <path>", "target file")
582
+ .option("--symbol <symbol>", "target symbol id, qualified name, or unique name")
583
+ .option("--limit <n>", "maximum graph edges", parseIntOption, 20)
584
+ .option("--auto-refresh", "refresh a stale or missing index before querying", true)
585
+ .option("--no-auto-refresh", "do not refresh a stale or missing index before querying")
586
+ .description("Show graph callers, importers, references, and tests for a target.")
587
+ .action(async (repo, opts) => printQuery(await callersQuery(await resolveQueryRepoRoot(repo), opts, { autoRefresh: opts.autoRefresh })));
588
+ program
589
+ .command("callees")
590
+ .argument("<repo>", "repository root")
591
+ .option("--file <path>", "target file")
592
+ .option("--symbol <symbol>", "target symbol id, qualified name, or unique name")
593
+ .option("--limit <n>", "maximum graph edges", parseIntOption, 20)
594
+ .option("--auto-refresh", "refresh a stale or missing index before querying", true)
595
+ .option("--no-auto-refresh", "do not refresh a stale or missing index before querying")
596
+ .description("Show graph callees, dependencies, imports, and risk surfaces for a target.")
597
+ .action(async (repo, opts) => printQuery(await calleesQuery(await resolveQueryRepoRoot(repo), opts, { autoRefresh: opts.autoRefresh })));
598
+ program
599
+ .command("dependency-path")
600
+ .argument("<repo>", "repository root")
601
+ .option("--from-file <path>", "source file")
602
+ .option("--from-symbol <symbol>", "source symbol")
603
+ .option("--to-file <path>", "target file")
604
+ .option("--to-symbol <symbol>", "target symbol")
605
+ .option("--max-depth <n>", "maximum graph depth", parseIntOption, 6)
606
+ .option("--auto-refresh", "refresh a stale or missing index before querying", true)
607
+ .option("--no-auto-refresh", "do not refresh a stale or missing index before querying")
608
+ .description("Find a typed dependency path between files or symbols.")
609
+ .action(async (repo, opts) => printQuery(await dependencyPathQuery(await resolveQueryRepoRoot(repo), opts, { autoRefresh: opts.autoRefresh })));
610
+ program
611
+ .command("workflow-path")
612
+ .argument("<repo>", "repository root")
613
+ .option("--query <query>", "natural-language workflow query")
614
+ .option("--file <path>", "target file")
615
+ .option("--symbol <symbol>", "target symbol")
616
+ .option("--limit <n>", "maximum workflow traces", parseIntOption, 8)
617
+ .option("--semantic", "force the semantic retrieval lane even when auto-detection would skip it")
618
+ .option("--no-semantic", "disable automatic semantic retrieval for this query")
619
+ .option("--semantic-provider <provider>", "semantic query provider: openai or local-command", parseSemanticProvider)
620
+ .option("--semantic-model <model>", "semantic embedding model name")
621
+ .option("--semantic-dimensions <n>", "semantic embedding dimensions", parseIntOption)
622
+ .option("--semantic-command <command>", "local semantic embedding command for --semantic-provider local-command")
623
+ .option("--semantic-arg <arg...>", "argument for the local semantic embedding command")
624
+ .option("--semantic-timeout-ms <n>", "semantic query timeout in milliseconds", parseIntOption)
625
+ .option("--semantic-batch-size <n>", "semantic query batch size", parseIntOption)
626
+ .option("--auto-refresh", "refresh a stale or missing index before querying", true)
627
+ .option("--no-auto-refresh", "do not refresh a stale or missing index before querying")
628
+ .description("Show route/job/manifest workflow traces related to a task, file, or symbol.")
629
+ .action(async (repo, opts) => printQuery(await workflowPathQuery(await resolveQueryRepoRoot(repo), opts, queryOptionsFromCli(opts))));
630
+ program
631
+ .command("change-plan")
632
+ .argument("<repo>", "repository root")
633
+ .option("--task <task>", "task description")
634
+ .option("--file <path...>", "focus file path; repeat or pass multiple paths")
635
+ .option("--symbol <symbol...>", "focus symbol id, qualified name, or unique name")
636
+ .option("--query <query>", "search query to seed context")
637
+ .option("--change-type <type>", "change type: style, api, behavior, rename, delete, unknown", parseChangeType, "unknown")
638
+ .option("--diff", "include current dirty git diff", true)
639
+ .option("--no-diff", "ignore current dirty git diff")
640
+ .option("--budget <tokens>", "approximate token budget", parseIntOption, 3200)
641
+ .option("--limit <n>", "maximum focus items", parseIntOption, 10)
642
+ .option("--save-snapshot", "save a plan-time task snapshot for post-edit review", false)
643
+ .option("--task-id <id>", "optional id for the saved task snapshot")
644
+ .option("--follow-candidate <id>", "follow an edit-ready target candidate from a blocked orientation plan")
645
+ .option("--semantic", "force the semantic retrieval lane even when auto-detection would skip it")
646
+ .option("--no-semantic", "disable automatic semantic retrieval for this query")
647
+ .option("--semantic-provider <provider>", "semantic query provider: openai or local-command", parseSemanticProvider)
648
+ .option("--semantic-model <model>", "semantic embedding model name")
649
+ .option("--semantic-dimensions <n>", "semantic embedding dimensions", parseIntOption)
650
+ .option("--semantic-command <command>", "local semantic embedding command for --semantic-provider local-command")
651
+ .option("--semantic-arg <arg...>", "argument for the local semantic embedding command")
652
+ .option("--semantic-timeout-ms <n>", "semantic query timeout in milliseconds", parseIntOption)
653
+ .option("--semantic-batch-size <n>", "semantic query batch size", parseIntOption)
654
+ .option("--lsp", "include optional read-only LSP assist for selected focus files")
655
+ .option("--lsp-timeout-ms <n>", "LSP request timeout in milliseconds", parseIntOption)
656
+ .option("--lsp-max-files <n>", "maximum files to inspect with LSP assist", parseIntOption)
657
+ .option("--auto-refresh", "refresh a stale or missing index before querying", true)
658
+ .option("--no-auto-refresh", "do not refresh a stale or missing index before querying")
659
+ .description("Build a Codex edit plan from focus, graph/workflow context, risks, tests, and gaps.")
660
+ .action(async (repo, opts) => printQuery(await changePlanQuery(await resolveQueryRepoRoot(repo), {
661
+ task: opts.task,
662
+ files: opts.file,
663
+ symbols: opts.symbol,
664
+ query: opts.query,
665
+ changeType: opts.changeType,
666
+ diff: opts.diff,
667
+ tokenBudget: opts.budget,
668
+ limit: opts.limit,
669
+ saveSnapshot: opts.saveSnapshot,
670
+ taskId: opts.taskId,
671
+ followCandidate: opts.followCandidate
672
+ }, queryOptionsFromCli(opts))));
673
+ program
674
+ .command("post-edit-review")
675
+ .alias("post-edit")
676
+ .argument("<repo>", "repository root")
677
+ .option("--task <task>", "task description if no saved snapshot is available")
678
+ .option("--task-id <id>", "task snapshot id; defaults to the latest saved snapshot")
679
+ .option("--file <path...>", "additional edited or focus file path")
680
+ .option("--symbol <symbol...>", "additional focus symbol id, qualified name, or unique name")
681
+ .option("--change-type <type>", "change type: style, api, behavior, rename, delete, unknown", parseChangeType, "unknown")
682
+ .option("--budget <tokens>", "approximate token budget", parseIntOption, 2800)
683
+ .option("--limit <n>", "maximum focus items", parseIntOption, 10)
684
+ .option("--snippets", "include source snippets", false)
685
+ .option("--no-snippets", "omit source snippets")
686
+ .option("--ran-test <test...>", "test file or direct test reference already run; repeat or pass multiple values")
687
+ .option("--ran-command <command...>", "verification command already run; repeat or pass multiple values")
688
+ .option("--ran-command-report <json...>", "structured command report JSON with command, cwd, packageManager, workspace/packageRoot/packageName, scriptName, args, exitCode, durationMs, and output summaries")
689
+ .option("--waive-check <target...>", "legacy test-target waiver shortcut; use --waiver for workflow/dependency checks")
690
+ .option("--waiver <json...>", "structured verification waiver JSON: {\"kind\":\"test\",\"target\":\"tests/foo.test.ts\",\"reason\":\"manual check\"}")
691
+ .option("--semantic", "force the semantic retrieval lane even when auto-detection would skip it")
692
+ .option("--no-semantic", "disable automatic semantic retrieval for this review")
693
+ .option("--semantic-provider <provider>", "semantic query provider: openai or local-command", parseSemanticProvider)
694
+ .option("--semantic-model <model>", "semantic embedding model name")
695
+ .option("--semantic-dimensions <n>", "semantic embedding dimensions", parseIntOption)
696
+ .option("--semantic-command <command>", "local semantic embedding command for --semantic-provider local-command")
697
+ .option("--semantic-arg <arg...>", "argument for the local semantic embedding command")
698
+ .option("--semantic-timeout-ms <n>", "semantic query timeout in milliseconds", parseIntOption)
699
+ .option("--semantic-batch-size <n>", "semantic query batch size", parseIntOption)
700
+ .option("--auto-refresh", "refresh a stale or missing index before querying", true)
701
+ .option("--no-auto-refresh", "do not refresh a stale or missing index before querying")
702
+ .description("Compare the current dirty tree against a saved Codexa change-plan snapshot.")
703
+ .action(async (repo, opts) => printQuery(await postEditReviewQuery(await resolveQueryRepoRoot(repo), {
704
+ task: opts.task,
705
+ taskId: opts.taskId,
706
+ files: opts.file,
707
+ symbols: opts.symbol,
708
+ changeType: opts.changeType,
709
+ tokenBudget: opts.budget,
710
+ limit: opts.limit,
711
+ includeSnippets: opts.snippets,
712
+ ranTests: opts.ranTest,
713
+ ranCommands: opts.ranCommand,
714
+ ranCommandReports: parseCommandReportOptions(opts.ranCommandReport),
715
+ waivedChecks: opts.waiveCheck,
716
+ waivers: parseWaiverOptions(opts.waiver)
717
+ }, queryOptionsFromCli(opts))));
718
+ program
719
+ .command("eval")
720
+ .argument("<repo>", "repository root")
721
+ .option("--suite <suite>", "eval suite: all, project, synthetic, historical-fixture, task-pack", "all")
722
+ .option("--seed <seed>", "seed for randomized synthetic holdouts")
723
+ .option("--task-pack <path>", "external historical task pack JSON")
724
+ .option("--json", "emit machine-readable JSON")
725
+ .option("--auto-refresh", "allow eval queries to refresh stale artifacts", false)
726
+ .option("--no-auto-refresh", "keep eval queries frozen against the existing index")
727
+ .option("--fail-on-refresh", "fail a scenario if a query auto-refreshes during scoring", true)
728
+ .option("--no-fail-on-refresh", "record refreshes without failing the scenario")
729
+ .option("--centrality-experiment", "run eval-only transitive centrality/PageRank experiment without changing default rank", false)
730
+ .description("Run a structured Codexa quality benchmark with randomized anti-cheat holdouts.")
731
+ .action(async (repo, opts) => {
732
+ const result = await runEval(await resolveQueryRepoRoot(repo), { autoRefresh: opts.autoRefresh }, { suite: opts.suite, seed: opts.seed, json: opts.json, failOnRefresh: opts.failOnRefresh, taskPackPath: opts.taskPack ? path.resolve(opts.taskPack) : undefined, centralityExperiment: opts.centralityExperiment });
733
+ console.log(result.text);
734
+ if (!result.passed) {
735
+ process.exitCode = 1;
736
+ }
737
+ });
738
+ program
739
+ .command("session-start")
740
+ .argument("[repo]", "repository root; defaults to current directory")
741
+ .option("--context", "include a small context preview", false)
742
+ .option("--auto-refresh", "refresh a stale or missing index before rendering the context preview", false)
743
+ .option("--no-auto-refresh", "do not refresh a stale or missing index before rendering the context preview")
744
+ .description("Print the lightweight Codexa SessionStart summary used by Codex hooks.")
745
+ .action(async (repo, opts) => {
746
+ const resolved = path.resolve(repo ?? process.cwd());
747
+ const startedAt = Date.now();
748
+ const summary = await sessionStartSummary(repo, opts.context || process.env.CODEXA_SESSIONSTART_CONTEXT === "1", opts.autoRefresh);
749
+ console.log(summary);
750
+ const unavailable = summary.includes("Codexa status unavailable:");
751
+ await recordAdvisoryHookEvent(resolved, {
752
+ hook: "session-start",
753
+ status: unavailable ? "failed" : "ok",
754
+ durationMs: Date.now() - startedAt,
755
+ reason: opts.context || process.env.CODEXA_SESSIONSTART_CONTEXT === "1" ? "context-preview" : "status",
756
+ error: unavailable ? summary.split(/\r?\n/u).find((line) => line.includes("Codexa status unavailable:")) : undefined
757
+ });
758
+ });
759
+ program
760
+ .command("serve")
761
+ .argument("<repo>", "repository root")
762
+ .option("--semantic", "force semantic retrieval for MCP task queries when auto-detection would skip it")
763
+ .option("--no-semantic", "disable automatic semantic retrieval for MCP task queries")
764
+ .option("--semantic-provider <provider>", "semantic query provider: openai or local-command", parseSemanticProvider)
765
+ .option("--semantic-model <model>", "semantic embedding model name")
766
+ .option("--semantic-dimensions <n>", "semantic embedding dimensions", parseIntOption)
767
+ .option("--semantic-command <command>", "local semantic embedding command for --semantic-provider local-command")
768
+ .option("--semantic-arg <arg...>", "argument for the local semantic embedding command")
769
+ .option("--semantic-timeout-ms <n>", "semantic query timeout in milliseconds", parseIntOption)
770
+ .option("--semantic-batch-size <n>", "semantic query batch size", parseIntOption)
771
+ .option("--lsp", "enable optional read-only LSP assist for MCP symbol/file/context calls")
772
+ .option("--lsp-timeout-ms <n>", "LSP request timeout in milliseconds", parseIntOption)
773
+ .option("--lsp-max-files <n>", "maximum files to inspect with LSP assist", parseIntOption)
774
+ .option("--auto-refresh", "refresh a stale or missing index before answering MCP context tools", true)
775
+ .option("--no-auto-refresh", "do not refresh a stale or missing index before answering MCP context tools")
776
+ .option("--session-memory <mode>", "auto-record MCP session memory: auto or off", parseSessionMemoryMode, "auto")
777
+ .option("--workspace-focus-file <path>", "workspace focus file to consult when <repo> is a workspace launch root")
778
+ .option("--workspace-session <id>", "active WORKING.md session row to prefer when <repo> is a workspace launch root")
779
+ .option("--transport <transport>", "MCP transport: stdio or http", parseMcpTransport, "stdio")
780
+ .option("--host <host>", "HTTP host for --transport http; must be loopback", "127.0.0.1")
781
+ .option("--port <n>", "HTTP port for --transport http", parseIntOption, 8729)
782
+ .option("--endpoint <path>", "HTTP MCP endpoint path for --transport http", "/mcp")
783
+ .description("Start the MCP server over stdio by default, or Streamable HTTP with --transport http.")
784
+ .action(async (repo, opts) => {
785
+ const resolved = path.resolve(repo);
786
+ const queryOptions = queryOptionsFromCli(opts);
787
+ if (opts.transport === "http") {
788
+ await serveMcpHttp(resolved, queryOptions, { host: opts.host, port: opts.port, endpoint: opts.endpoint });
789
+ return;
790
+ }
791
+ await serveMcp(resolved, queryOptions);
792
+ });
793
+ program.parseAsync(process.argv).catch((error) => {
794
+ console.error(error instanceof Error ? error.message : String(error));
795
+ process.exitCode = 1;
796
+ });
797
+ function printQuery(result) {
798
+ console.log(result.text);
799
+ }
800
+ async function resolveQueryRepoRoot(repo) {
801
+ return (await resolveMcpRepoRoot(path.resolve(repo))).repoRoot;
802
+ }
803
+ function invokedCliName() {
804
+ const basename = path.basename(process.argv[1] ?? "codexa").replace(/\.[cm]?[jt]sx?$/u, "");
805
+ return basename && basename !== "cli" ? basename : "codexa";
806
+ }
807
+ function queryOptionsFromCli(opts) {
808
+ return {
809
+ autoRefresh: opts.autoRefresh,
810
+ semantic: opts.semantic,
811
+ semanticProvider: opts.semanticProvider,
812
+ semanticModel: opts.semanticModel,
813
+ semanticDimensions: opts.semanticDimensions,
814
+ semanticCommand: opts.semanticCommand,
815
+ semanticArgs: opts.semanticArg,
816
+ semanticTimeoutMs: opts.semanticTimeoutMs,
817
+ semanticBatchSize: opts.semanticBatchSize,
818
+ lsp: opts.lsp,
819
+ lspTimeoutMs: opts.lspTimeoutMs,
820
+ lspMaxFiles: opts.lspMaxFiles,
821
+ sessionMemory: opts.sessionMemory,
822
+ workspaceFocusFile: opts.workspaceFocusFile ? path.resolve(opts.workspaceFocusFile) : undefined,
823
+ workspaceSessionId: opts.workspaceSession
824
+ };
825
+ }
826
+ function parseSessionMemoryMode(value) {
827
+ if (value === "auto" || value === "off") {
828
+ return value;
829
+ }
830
+ throw new Error("session memory mode must be auto or off");
831
+ }
832
+ function parseMcpTransport(value) {
833
+ if (value === "stdio" || value === "http") {
834
+ return value;
835
+ }
836
+ throw new Error("MCP transport must be stdio or http");
837
+ }
838
+ function parseIntOption(value) {
839
+ const trimmed = value.trim();
840
+ if (!/^[+-]?\d+$/u.test(trimmed)) {
841
+ throw new Error(`Invalid integer: ${value}`);
842
+ }
843
+ const parsed = Number.parseInt(trimmed, 10);
844
+ if (!Number.isSafeInteger(parsed)) {
845
+ throw new Error(`Invalid integer: ${value}`);
846
+ }
847
+ return parsed;
848
+ }
849
+ function parseAutonomyOption(value) {
850
+ return parseAutonomyMode(value);
851
+ }
852
+ function parseToolProfile(value) {
853
+ if (value === "core" || value === "full") {
854
+ return value;
855
+ }
856
+ throw new Error(`Invalid tool profile: ${value} (expected core or full)`);
857
+ }
858
+ function parseChangeType(value) {
859
+ const allowed = new Set(["style", "api", "behavior", "rename", "delete", "unknown"]);
860
+ if (allowed.has(value)) {
861
+ return value;
862
+ }
863
+ throw new Error(`Invalid change type: ${value}`);
864
+ }
865
+ function parseSessionMemoryAction(value) {
866
+ const allowed = new Set(["read", "remember", "summary", "compact"]);
867
+ if (allowed.has(value)) {
868
+ return value;
869
+ }
870
+ throw new Error(`Invalid session memory action: ${value}`);
871
+ }
872
+ function parseSessionMemoryKinds(values) {
873
+ return values?.map(parseSessionMemoryKind);
874
+ }
875
+ function parseSessionMemoryKind(value) {
876
+ const allowed = new Set([
877
+ "viewed",
878
+ "claim",
879
+ "ruled_out",
880
+ "open_question",
881
+ "next_read",
882
+ "decision",
883
+ "verification",
884
+ "risk",
885
+ "constraint"
886
+ ]);
887
+ if (allowed.has(value)) {
888
+ return value;
889
+ }
890
+ throw new Error(`Invalid session memory kind: ${value}`);
891
+ }
892
+ function parseSessionMemoryEntries(values) {
893
+ if (!values?.length) {
894
+ return undefined;
895
+ }
896
+ return values.map((value) => {
897
+ let parsed;
898
+ try {
899
+ parsed = JSON.parse(value);
900
+ }
901
+ catch {
902
+ throw new Error(`Invalid session memory entry JSON: ${value}`);
903
+ }
904
+ if (!isCliRecord(parsed)) {
905
+ throw new Error(`Invalid session memory entry JSON: ${value}`);
906
+ }
907
+ const entry = parsed;
908
+ if (typeof entry.summary !== "string" || entry.summary.trim().length === 0) {
909
+ throw new Error(`Invalid session memory entry JSON: summary is required`);
910
+ }
911
+ const kind = parseSessionMemoryKind(String(entry.kind));
912
+ if (entry.confidence !== "authoritative" && entry.confidence !== "derived" && entry.confidence !== "heuristic") {
913
+ throw new Error(`Invalid session memory entry JSON: confidence is required`);
914
+ }
915
+ if (entry.evidenceTier !== "authoritative" && entry.evidenceTier !== "derived" && entry.evidenceTier !== "heuristic" && entry.evidenceTier !== "fallback") {
916
+ throw new Error(`Invalid session memory entry JSON: evidenceTier is required`);
917
+ }
918
+ return {
919
+ ...entry,
920
+ kind,
921
+ summary: entry.summary.trim()
922
+ };
923
+ });
924
+ }
925
+ function parseSemanticProvider(value) {
926
+ const provider = semanticProviderFromValue(value);
927
+ if (!provider) {
928
+ throw new Error(`Invalid semantic provider: ${value}`);
929
+ }
930
+ return provider;
931
+ }
932
+ function parseWaiverOptions(values) {
933
+ if (!values?.length) {
934
+ return undefined;
935
+ }
936
+ return values.map((value) => {
937
+ let parsed;
938
+ try {
939
+ parsed = JSON.parse(value);
940
+ }
941
+ catch {
942
+ throw new Error(`Invalid waiver JSON: ${value}`);
943
+ }
944
+ if ((parsed.kind !== "test" && parsed.kind !== "workflow" && parsed.kind !== "dependency") || typeof parsed.target !== "string" || typeof parsed.reason !== "string") {
945
+ throw new Error(`Invalid waiver JSON: ${value}`);
946
+ }
947
+ return { kind: parsed.kind, target: parsed.target, reason: parsed.reason };
948
+ });
949
+ }
950
+ function isCliRecord(value) {
951
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
952
+ }
953
+ function parseCommandReportOptions(values) {
954
+ if (!values?.length) {
955
+ return undefined;
956
+ }
957
+ return values.map((value) => {
958
+ let parsed;
959
+ try {
960
+ parsed = JSON.parse(value);
961
+ }
962
+ catch {
963
+ throw new Error(`Invalid command report JSON: ${value}`);
964
+ }
965
+ if (typeof parsed.command !== "string" || parsed.command.trim().length === 0) {
966
+ throw new Error(`Invalid command report JSON: ${value}`);
967
+ }
968
+ if (parsed.cwd !== undefined && typeof parsed.cwd !== "string") {
969
+ throw new Error(`Invalid command report JSON: ${value}`);
970
+ }
971
+ for (const field of ["packageManager", "workspace", "packageRoot", "packageName", "scriptName"]) {
972
+ if (parsed[field] !== undefined && typeof parsed[field] !== "string") {
973
+ throw new Error(`Invalid command report JSON: ${value}`);
974
+ }
975
+ }
976
+ if (parsed.args !== undefined && (!Array.isArray(parsed.args) || parsed.args.some((arg) => typeof arg !== "string"))) {
977
+ throw new Error(`Invalid command report JSON: ${value}`);
978
+ }
979
+ if (parsed.args !== undefined && parsed.args.length > 80) {
980
+ throw new Error(`Invalid command report JSON: args exceeds 80 entries`);
981
+ }
982
+ if (parsed.exitCode !== undefined && (!Number.isInteger(parsed.exitCode) || parsed.exitCode < 0)) {
983
+ throw new Error(`Invalid command report JSON: ${value}`);
984
+ }
985
+ if (parsed.durationMs !== undefined && (!Number.isFinite(parsed.durationMs) || parsed.durationMs < 0)) {
986
+ throw new Error(`Invalid command report JSON: ${value}`);
987
+ }
988
+ for (const field of ["stdoutSummary", "stderrSummary", "outputSummary"]) {
989
+ if (parsed[field] !== undefined && typeof parsed[field] !== "string") {
990
+ throw new Error(`Invalid command report JSON: ${value}`);
991
+ }
992
+ if (typeof parsed[field] === "string" && parsed[field].length > 1000) {
993
+ throw new Error(`Invalid command report JSON: ${field} exceeds 1000 characters`);
994
+ }
995
+ }
996
+ return {
997
+ command: parsed.command,
998
+ cwd: parsed.cwd,
999
+ packageManager: parsed.packageManager,
1000
+ workspace: parsed.workspace,
1001
+ packageRoot: parsed.packageRoot,
1002
+ packageName: parsed.packageName,
1003
+ scriptName: parsed.scriptName,
1004
+ args: parsed.args,
1005
+ exitCode: parsed.exitCode,
1006
+ durationMs: parsed.durationMs,
1007
+ stdoutSummary: parsed.stdoutSummary,
1008
+ stderrSummary: parsed.stderrSummary,
1009
+ outputSummary: parsed.outputSummary
1010
+ };
1011
+ });
1012
+ }
1013
+ function logLiveIndexEvent(event) {
1014
+ if (event.type === "watch-ready") {
1015
+ console.error(`Codexa watch ready: ${event.repoRoot} (${event.directories} dirs, debounce ${event.debounceMs}ms, poll ${event.pollMs}ms)`);
1016
+ return;
1017
+ }
1018
+ if (event.type === "index-start") {
1019
+ console.error(`Codexa indexing started (${event.reason}).`);
1020
+ return;
1021
+ }
1022
+ if (event.type === "index-complete") {
1023
+ console.error(`Codexa indexed ${event.files} files, ${event.symbols} symbols, ${event.usageSites} usage sites in ${event.durationMs}ms (${event.reason}).`);
1024
+ return;
1025
+ }
1026
+ if (event.type === "watch-warning") {
1027
+ console.error(`Codexa watch warning: ${event.message}`);
1028
+ return;
1029
+ }
1030
+ if (event.type === "watch-stopped") {
1031
+ console.error(`Codexa watch stopped after ${event.runs} index run(s).`);
1032
+ }
1033
+ }
1034
+ //# sourceMappingURL=cli.js.map