@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.
- package/LICENSE +21 -0
- package/README.md +634 -0
- package/dist/artifacts.d.ts +2 -0
- package/dist/artifacts.js +375 -0
- package/dist/artifacts.js.map +1 -0
- package/dist/autonomy.d.ts +17 -0
- package/dist/autonomy.js +124 -0
- package/dist/autonomy.js.map +1 -0
- package/dist/autoverify/policy.d.ts +5 -0
- package/dist/autoverify/policy.js +18 -0
- package/dist/autoverify/policy.js.map +1 -0
- package/dist/autoverify.d.ts +45 -0
- package/dist/autoverify.js +1041 -0
- package/dist/autoverify.js.map +1 -0
- package/dist/cache-lock.d.ts +16 -0
- package/dist/cache-lock.js +181 -0
- package/dist/cache-lock.js.map +1 -0
- package/dist/cli/hooks.d.ts +5 -0
- package/dist/cli/hooks.js +264 -0
- package/dist/cli/hooks.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +1034 -0
- package/dist/cli.js.map +1 -0
- package/dist/codex-contract.d.ts +2 -0
- package/dist/codex-contract.js +78 -0
- package/dist/codex-contract.js.map +1 -0
- package/dist/command.d.ts +34 -0
- package/dist/command.js +162 -0
- package/dist/command.js.map +1 -0
- package/dist/doctor.d.ts +112 -0
- package/dist/doctor.js +518 -0
- package/dist/doctor.js.map +1 -0
- package/dist/eval/baseline.d.ts +7 -0
- package/dist/eval/baseline.js +146 -0
- package/dist/eval/baseline.js.map +1 -0
- package/dist/eval/historical.d.ts +4 -0
- package/dist/eval/historical.js +663 -0
- package/dist/eval/historical.js.map +1 -0
- package/dist/eval/render.d.ts +2 -0
- package/dist/eval/render.js +53 -0
- package/dist/eval/render.js.map +1 -0
- package/dist/eval/scoring.d.ts +21 -0
- package/dist/eval/scoring.js +618 -0
- package/dist/eval/scoring.js.map +1 -0
- package/dist/eval/synthetic.d.ts +36 -0
- package/dist/eval/synthetic.js +107 -0
- package/dist/eval/synthetic.js.map +1 -0
- package/dist/eval/types.d.ts +36 -0
- package/dist/eval/types.js +2 -0
- package/dist/eval/types.js.map +1 -0
- package/dist/eval.d.ts +140 -0
- package/dist/eval.js +551 -0
- package/dist/eval.js.map +1 -0
- package/dist/git.d.ts +17 -0
- package/dist/git.js +189 -0
- package/dist/git.js.map +1 -0
- package/dist/github-release.d.ts +47 -0
- package/dist/github-release.js +610 -0
- package/dist/github-release.js.map +1 -0
- package/dist/github-sync.d.ts +68 -0
- package/dist/github-sync.js +345 -0
- package/dist/github-sync.js.map +1 -0
- package/dist/graph.d.ts +10 -0
- package/dist/graph.js +665 -0
- package/dist/graph.js.map +1 -0
- package/dist/indexer/aliases.d.ts +2 -0
- package/dist/indexer/aliases.js +190 -0
- package/dist/indexer/aliases.js.map +1 -0
- package/dist/indexer/artifact-writing.d.ts +3 -0
- package/dist/indexer/artifact-writing.js +79 -0
- package/dist/indexer/artifact-writing.js.map +1 -0
- package/dist/indexer/discovery.d.ts +2 -0
- package/dist/indexer/discovery.js +5 -0
- package/dist/indexer/discovery.js.map +1 -0
- package/dist/indexer/external-facts.d.ts +6 -0
- package/dist/indexer/external-facts.js +45 -0
- package/dist/indexer/external-facts.js.map +1 -0
- package/dist/indexer/freshness.d.ts +8 -0
- package/dist/indexer/freshness.js +56 -0
- package/dist/indexer/freshness.js.map +1 -0
- package/dist/indexer/graph-stage.d.ts +2 -0
- package/dist/indexer/graph-stage.js +21 -0
- package/dist/indexer/graph-stage.js.map +1 -0
- package/dist/indexer/parsing.d.ts +30 -0
- package/dist/indexer/parsing.js +177 -0
- package/dist/indexer/parsing.js.map +1 -0
- package/dist/indexer/pipeline.d.ts +5 -0
- package/dist/indexer/pipeline.js +8 -0
- package/dist/indexer/pipeline.js.map +1 -0
- package/dist/indexer/ranking.d.ts +4 -0
- package/dist/indexer/ranking.js +134 -0
- package/dist/indexer/ranking.js.map +1 -0
- package/dist/indexer.d.ts +13 -0
- package/dist/indexer.js +395 -0
- package/dist/indexer.js.map +1 -0
- package/dist/init.d.ts +24 -0
- package/dist/init.js +566 -0
- package/dist/init.js.map +1 -0
- package/dist/language.d.ts +8 -0
- package/dist/language.js +123 -0
- package/dist/language.js.map +1 -0
- package/dist/live-index.d.ts +68 -0
- package/dist/live-index.js +215 -0
- package/dist/live-index.js.map +1 -0
- package/dist/lsp/assist.d.ts +44 -0
- package/dist/lsp/assist.js +331 -0
- package/dist/lsp/assist.js.map +1 -0
- package/dist/lsp/client.d.ts +59 -0
- package/dist/lsp/client.js +208 -0
- package/dist/lsp/client.js.map +1 -0
- package/dist/mcp/compaction.d.ts +15 -0
- package/dist/mcp/compaction.js +1249 -0
- package/dist/mcp/compaction.js.map +1 -0
- package/dist/mcp/envelope.d.ts +44 -0
- package/dist/mcp/envelope.js +425 -0
- package/dist/mcp/envelope.js.map +1 -0
- package/dist/mcp/prompts.d.ts +2 -0
- package/dist/mcp/prompts.js +109 -0
- package/dist/mcp/prompts.js.map +1 -0
- package/dist/mcp/resources.d.ts +2 -0
- package/dist/mcp/resources.js +132 -0
- package/dist/mcp/resources.js.map +1 -0
- package/dist/mcp/runtime.d.ts +15 -0
- package/dist/mcp/runtime.js +122 -0
- package/dist/mcp/runtime.js.map +1 -0
- package/dist/mcp/session-memory.d.ts +3 -0
- package/dist/mcp/session-memory.js +61 -0
- package/dist/mcp/session-memory.js.map +1 -0
- package/dist/mcp/tool-registry.d.ts +269 -0
- package/dist/mcp/tool-registry.js +284 -0
- package/dist/mcp/tool-registry.js.map +1 -0
- package/dist/mcp/tools.d.ts +53 -0
- package/dist/mcp/tools.js +372 -0
- package/dist/mcp/tools.js.map +1 -0
- package/dist/mcp-repo-root.d.ts +16 -0
- package/dist/mcp-repo-root.js +322 -0
- package/dist/mcp-repo-root.js.map +1 -0
- package/dist/mcp-tool-catalog.d.ts +2 -0
- package/dist/mcp-tool-catalog.js +2 -0
- package/dist/mcp-tool-catalog.js.map +1 -0
- package/dist/mcp.d.ts +11 -0
- package/dist/mcp.js +332 -0
- package/dist/mcp.js.map +1 -0
- package/dist/outcome-ranking.d.ts +5 -0
- package/dist/outcome-ranking.js +115 -0
- package/dist/outcome-ranking.js.map +1 -0
- package/dist/parser/context.d.ts +28 -0
- package/dist/parser/context.js +2 -0
- package/dist/parser/context.js.map +1 -0
- package/dist/parser/ecma.d.ts +5 -0
- package/dist/parser/ecma.js +388 -0
- package/dist/parser/ecma.js.map +1 -0
- package/dist/parser/facts.d.ts +12 -0
- package/dist/parser/facts.js +137 -0
- package/dist/parser/facts.js.map +1 -0
- package/dist/parser/json.d.ts +3 -0
- package/dist/parser/json.js +318 -0
- package/dist/parser/json.js.map +1 -0
- package/dist/parser/markdown.d.ts +3 -0
- package/dist/parser/markdown.js +180 -0
- package/dist/parser/markdown.js.map +1 -0
- package/dist/parser/nodes.d.ts +5 -0
- package/dist/parser/nodes.js +75 -0
- package/dist/parser/nodes.js.map +1 -0
- package/dist/parser/python.d.ts +2 -0
- package/dist/parser/python.js +307 -0
- package/dist/parser/python.js.map +1 -0
- package/dist/parser/references.d.ts +3 -0
- package/dist/parser/references.js +204 -0
- package/dist/parser/references.js.map +1 -0
- package/dist/parser/risks.d.ts +4 -0
- package/dist/parser/risks.js +62 -0
- package/dist/parser/risks.js.map +1 -0
- package/dist/parser/routes.d.ts +5 -0
- package/dist/parser/routes.js +97 -0
- package/dist/parser/routes.js.map +1 -0
- package/dist/parser/shallow.d.ts +3 -0
- package/dist/parser/shallow.js +545 -0
- package/dist/parser/shallow.js.map +1 -0
- package/dist/parser/source.d.ts +4 -0
- package/dist/parser/source.js +127 -0
- package/dist/parser/source.js.map +1 -0
- package/dist/parser.d.ts +2 -0
- package/dist/parser.js +2 -0
- package/dist/parser.js.map +1 -0
- package/dist/placeholder-signals.d.ts +15 -0
- package/dist/placeholder-signals.js +511 -0
- package/dist/placeholder-signals.js.map +1 -0
- package/dist/post-edit-outcomes.d.ts +167 -0
- package/dist/post-edit-outcomes.js +484 -0
- package/dist/post-edit-outcomes.js.map +1 -0
- package/dist/queries.d.ts +12 -0
- package/dist/queries.js +13 -0
- package/dist/queries.js.map +1 -0
- package/dist/query/change-plan.d.ts +48 -0
- package/dist/query/change-plan.js +858 -0
- package/dist/query/change-plan.js.map +1 -0
- package/dist/query/compact-data.d.ts +25 -0
- package/dist/query/compact-data.js +74 -0
- package/dist/query/compact-data.js.map +1 -0
- package/dist/query/context.d.ts +5 -0
- package/dist/query/context.js +1162 -0
- package/dist/query/context.js.map +1 -0
- package/dist/query/diff.d.ts +5 -0
- package/dist/query/diff.js +111 -0
- package/dist/query/diff.js.map +1 -0
- package/dist/query/edge-evidence.d.ts +3 -0
- package/dist/query/edge-evidence.js +36 -0
- package/dist/query/edge-evidence.js.map +1 -0
- package/dist/query/formatting.d.ts +14 -0
- package/dist/query/formatting.js +67 -0
- package/dist/query/formatting.js.map +1 -0
- package/dist/query/graph-traversal.d.ts +22 -0
- package/dist/query/graph-traversal.js +218 -0
- package/dist/query/graph-traversal.js.map +1 -0
- package/dist/query/graph.d.ts +14 -0
- package/dist/query/graph.js +102 -0
- package/dist/query/graph.js.map +1 -0
- package/dist/query/impact.d.ts +28 -0
- package/dist/query/impact.js +568 -0
- package/dist/query/impact.js.map +1 -0
- package/dist/query/inspection.d.ts +9 -0
- package/dist/query/inspection.js +290 -0
- package/dist/query/inspection.js.map +1 -0
- package/dist/query/next-tools.d.ts +3 -0
- package/dist/query/next-tools.js +25 -0
- package/dist/query/next-tools.js.map +1 -0
- package/dist/query/placeholders.d.ts +24 -0
- package/dist/query/placeholders.js +121 -0
- package/dist/query/placeholders.js.map +1 -0
- package/dist/query/post-edit/decision.d.ts +49 -0
- package/dist/query/post-edit/decision.js +130 -0
- package/dist/query/post-edit/decision.js.map +1 -0
- package/dist/query/post-edit/dirty-scope.d.ts +16 -0
- package/dist/query/post-edit/dirty-scope.js +21 -0
- package/dist/query/post-edit/dirty-scope.js.map +1 -0
- package/dist/query/post-edit/next-actions.d.ts +22 -0
- package/dist/query/post-edit/next-actions.js +44 -0
- package/dist/query/post-edit/next-actions.js.map +1 -0
- package/dist/query/post-edit/snapshot-contract.d.ts +8 -0
- package/dist/query/post-edit/snapshot-contract.js +111 -0
- package/dist/query/post-edit/snapshot-contract.js.map +1 -0
- package/dist/query/post-edit.d.ts +5 -0
- package/dist/query/post-edit.js +1108 -0
- package/dist/query/post-edit.js.map +1 -0
- package/dist/query/quality.d.ts +43 -0
- package/dist/query/quality.js +134 -0
- package/dist/query/quality.js.map +1 -0
- package/dist/query/raw-search.d.ts +23 -0
- package/dist/query/raw-search.js +147 -0
- package/dist/query/raw-search.js.map +1 -0
- package/dist/query/runtime.d.ts +11 -0
- package/dist/query/runtime.js +79 -0
- package/dist/query/runtime.js.map +1 -0
- package/dist/query/search.d.ts +25 -0
- package/dist/query/search.js +429 -0
- package/dist/query/search.js.map +1 -0
- package/dist/query/session-memory.d.ts +3 -0
- package/dist/query/session-memory.js +108 -0
- package/dist/query/session-memory.js.map +1 -0
- package/dist/query/session.d.ts +41 -0
- package/dist/query/session.js +90 -0
- package/dist/query/session.js.map +1 -0
- package/dist/query/targets.d.ts +25 -0
- package/dist/query/targets.js +97 -0
- package/dist/query/targets.js.map +1 -0
- package/dist/query/test-commands.d.ts +10 -0
- package/dist/query/test-commands.js +110 -0
- package/dist/query/test-commands.js.map +1 -0
- package/dist/query/test-plan.d.ts +6 -0
- package/dist/query/test-plan.js +104 -0
- package/dist/query/test-plan.js.map +1 -0
- package/dist/query/tests.d.ts +48 -0
- package/dist/query/tests.js +444 -0
- package/dist/query/tests.js.map +1 -0
- package/dist/query/verification/shell.d.ts +20 -0
- package/dist/query/verification/shell.js +164 -0
- package/dist/query/verification/shell.js.map +1 -0
- package/dist/query/verification.d.ts +47 -0
- package/dist/query/verification.js +1123 -0
- package/dist/query/verification.js.map +1 -0
- package/dist/query/workflow.d.ts +17 -0
- package/dist/query/workflow.js +252 -0
- package/dist/query/workflow.js.map +1 -0
- package/dist/query/workspace-guidance.d.ts +26 -0
- package/dist/query/workspace-guidance.js +214 -0
- package/dist/query/workspace-guidance.js.map +1 -0
- package/dist/query/worktree-state.d.ts +22 -0
- package/dist/query/worktree-state.js +32 -0
- package/dist/query/worktree-state.js.map +1 -0
- package/dist/query/worktree.d.ts +16 -0
- package/dist/query/worktree.js +194 -0
- package/dist/query/worktree.js.map +1 -0
- package/dist/query-data.d.ts +4 -0
- package/dist/query-data.js +112 -0
- package/dist/query-data.js.map +1 -0
- package/dist/repo-files.d.ts +24 -0
- package/dist/repo-files.js +105 -0
- package/dist/repo-files.js.map +1 -0
- package/dist/resolver.d.ts +9 -0
- package/dist/resolver.js +555 -0
- package/dist/resolver.js.map +1 -0
- package/dist/retrieval.d.ts +46 -0
- package/dist/retrieval.js +783 -0
- package/dist/retrieval.js.map +1 -0
- package/dist/risk-ingest.d.ts +16 -0
- package/dist/risk-ingest.js +458 -0
- package/dist/risk-ingest.js.map +1 -0
- package/dist/rules.d.ts +10 -0
- package/dist/rules.js +107 -0
- package/dist/rules.js.map +1 -0
- package/dist/semantic/python.d.ts +9 -0
- package/dist/semantic/python.js +817 -0
- package/dist/semantic/python.js.map +1 -0
- package/dist/semantic/typescript.d.ts +10 -0
- package/dist/semantic/typescript.js +714 -0
- package/dist/semantic/typescript.js.map +1 -0
- package/dist/semantic-retrieval.d.ts +53 -0
- package/dist/semantic-retrieval.js +673 -0
- package/dist/semantic-retrieval.js.map +1 -0
- package/dist/session-memory/derivation.d.ts +6 -0
- package/dist/session-memory/derivation.js +400 -0
- package/dist/session-memory/derivation.js.map +1 -0
- package/dist/session-memory/event-log.d.ts +23 -0
- package/dist/session-memory/event-log.js +126 -0
- package/dist/session-memory/event-log.js.map +1 -0
- package/dist/session-memory/formatting.d.ts +7 -0
- package/dist/session-memory/formatting.js +86 -0
- package/dist/session-memory/formatting.js.map +1 -0
- package/dist/session-memory/model.d.ts +94 -0
- package/dist/session-memory/model.js +17 -0
- package/dist/session-memory/model.js.map +1 -0
- package/dist/session-memory/runtime.d.ts +24 -0
- package/dist/session-memory/runtime.js +289 -0
- package/dist/session-memory/runtime.js.map +1 -0
- package/dist/session-memory/store.d.ts +27 -0
- package/dist/session-memory/store.js +447 -0
- package/dist/session-memory/store.js.map +1 -0
- package/dist/session-memory.d.ts +1 -0
- package/dist/session-memory.js +2 -0
- package/dist/session-memory.js.map +1 -0
- package/dist/static-analysis.d.ts +36 -0
- package/dist/static-analysis.js +505 -0
- package/dist/static-analysis.js.map +1 -0
- package/dist/symbol-report-ingest.d.ts +8 -0
- package/dist/symbol-report-ingest.js +504 -0
- package/dist/symbol-report-ingest.js.map +1 -0
- package/dist/task-snapshots.d.ts +41 -0
- package/dist/task-snapshots.js +430 -0
- package/dist/task-snapshots.js.map +1 -0
- package/dist/types.d.ts +848 -0
- package/dist/types.js +12 -0
- package/dist/types.js.map +1 -0
- package/dist/util.d.ts +11 -0
- package/dist/util.js +63 -0
- package/dist/util.js.map +1 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +5 -0
- package/dist/version.js.map +1 -0
- package/package.json +81 -0
- package/plugins/codexa/.codex-plugin/plugin.json +38 -0
- package/plugins/codexa/.mcp.json +20 -0
- package/plugins/codexa/scripts/codexa-mcp.js +100 -0
- 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
|