@praneeth_54/agentdoctor 1.1.1 → 2.0.1
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/CHANGELOG.md +164 -7
- package/README.md +263 -513
- package/dist/agents/aider/adapter.d.ts +1 -0
- package/dist/agents/aider/adapter.js +1 -0
- package/dist/agents/aider/detector.d.ts +14 -0
- package/dist/agents/aider/detector.js +120 -0
- package/dist/agents/copilot/adapter.d.ts +1 -0
- package/dist/agents/copilot/adapter.js +1 -0
- package/dist/agents/copilot/detector.d.ts +13 -0
- package/dist/agents/copilot/detector.js +113 -0
- package/dist/agents/gemini/adapter.d.ts +1 -0
- package/dist/agents/gemini/adapter.js +1 -0
- package/dist/agents/gemini/detector.d.ts +13 -0
- package/dist/agents/gemini/detector.js +151 -0
- package/dist/agents/registry.js +13 -1
- package/dist/agents/types.d.ts +1 -1
- package/dist/agents/windsurf/adapter.d.ts +1 -0
- package/dist/agents/windsurf/adapter.js +1 -0
- package/dist/agents/windsurf/detector.d.ts +14 -0
- package/dist/agents/windsurf/detector.js +119 -0
- package/dist/architecture/c4.d.ts +22 -0
- package/dist/architecture/c4.js +62 -0
- package/dist/architecture/contract.d.ts +64 -0
- package/dist/architecture/contract.js +373 -0
- package/dist/assurance/change.d.ts +211 -0
- package/dist/assurance/change.js +624 -0
- package/dist/assurance/proof.d.ts +136 -0
- package/dist/assurance/proof.js +338 -0
- package/dist/auth/index.d.ts +81 -0
- package/dist/auth/index.js +130 -0
- package/dist/auth/rbac.d.ts +9 -0
- package/dist/auth/rbac.js +26 -0
- package/dist/cli/commands/architecture.d.ts +6 -0
- package/dist/cli/commands/architecture.js +109 -0
- package/dist/cli/commands/assurance.d.ts +67 -0
- package/dist/cli/commands/assurance.js +210 -0
- package/dist/cli/commands/brain.d.ts +12 -0
- package/dist/cli/commands/brain.js +162 -0
- package/dist/cli/commands/complete.d.ts +23 -0
- package/dist/cli/commands/complete.js +232 -0
- package/dist/cli/commands/explain.js +1 -1
- package/dist/cli/commands/fix.js +22 -2
- package/dist/cli/commands/mcp.d.ts +8 -0
- package/dist/cli/commands/mcp.js +14 -0
- package/dist/cli/commands/platform.d.ts +17 -0
- package/dist/cli/commands/platform.js +195 -0
- package/dist/cli/commands/policy-graph-run.d.ts +40 -0
- package/dist/cli/commands/policy-graph-run.js +200 -0
- package/dist/cli/commands/v2.d.ts +55 -0
- package/dist/cli/commands/v2.js +278 -0
- package/dist/cli/commands/workspace.d.ts +9 -0
- package/dist/cli/commands/workspace.js +82 -0
- package/dist/cli/program.js +1328 -3
- package/dist/constants.d.ts +3 -2
- package/dist/constants.js +5 -1
- package/dist/contracts/adapters.d.ts +7 -0
- package/dist/contracts/adapters.js +60 -0
- package/dist/contracts/index.d.ts +162 -0
- package/dist/contracts/index.js +18 -0
- package/dist/core/baseline/store.d.ts +48 -0
- package/dist/core/baseline/store.js +163 -0
- package/dist/core/brain-cli/service.d.ts +26 -0
- package/dist/core/brain-cli/service.js +175 -0
- package/dist/core/brain-product/init.d.ts +45 -0
- package/dist/core/brain-product/init.js +235 -0
- package/dist/core/changes/analyze.d.ts +41 -0
- package/dist/core/changes/analyze.js +223 -0
- package/dist/core/context-health/analyze.d.ts +15 -0
- package/dist/core/context-health/analyze.js +234 -0
- package/dist/core/fix/apply.d.ts +21 -4
- package/dist/core/fix/apply.js +118 -18
- package/dist/core/fix/backup.d.ts +30 -0
- package/dist/core/fix/backup.js +193 -0
- package/dist/core/fix/plan.d.ts +6 -3
- package/dist/core/fix/plan.js +113 -9
- package/dist/core/fix/render.d.ts +2 -0
- package/dist/core/fix/render.js +36 -21
- package/dist/core/fix/run.js +5 -1
- package/dist/core/fix/safe-target.d.ts +14 -0
- package/dist/core/fix/safe-target.js +81 -0
- package/dist/core/fix/types.d.ts +12 -1
- package/dist/core/fix/types.js +2 -0
- package/dist/core/fix/writers/claude-settings.js +3 -1
- package/dist/core/fix/writers/codex-config.d.ts +3 -0
- package/dist/core/fix/writers/codex-config.js +24 -3
- package/dist/core/fix/writers/cursorignore.js +3 -1
- package/dist/core/fix/writers/simple-ignore.d.ts +16 -0
- package/dist/core/fix/writers/simple-ignore.js +68 -0
- package/dist/core/monorepo/detect.d.ts +13 -0
- package/dist/core/monorepo/detect.js +121 -0
- package/dist/core/rules/build-context.js +22 -3
- package/dist/core/rules/context/generated-directory.js +11 -1
- package/dist/core/rules/context/large-instruction-file.js +6 -0
- package/dist/core/rules/context/large-log-file.js +16 -1
- package/dist/core/rules/ignore.d.ts +11 -1
- package/dist/core/rules/ignore.js +22 -1
- package/dist/core/rules/instructions/empty-instructions.js +7 -0
- package/dist/core/rules/security/env-file-exposure.js +23 -2
- package/dist/core/rules/security/private-key-file.js +1 -1
- package/dist/core/scanner/scan.js +1 -1
- package/dist/core/schemas/validate.d.ts +6 -0
- package/dist/core/schemas/validate.js +62 -0
- package/dist/core/scoring/compute-scores.d.ts +1 -1
- package/dist/core/scoring/compute-scores.js +5 -1
- package/dist/core/scoring/placeholder.d.ts +1 -1
- package/dist/core/scoring/placeholder.js +5 -1
- package/dist/core/secrets/scan.d.ts +29 -0
- package/dist/core/secrets/scan.js +172 -0
- package/dist/coverage/cobertura.d.ts +6 -0
- package/dist/coverage/cobertura.js +52 -0
- package/dist/coverage/istanbul.d.ts +7 -0
- package/dist/coverage/istanbul.js +134 -0
- package/dist/coverage/lcov.d.ts +7 -0
- package/dist/coverage/lcov.js +59 -0
- package/dist/coverage/load.d.ts +9 -0
- package/dist/coverage/load.js +39 -0
- package/dist/coverage/types.d.ts +23 -0
- package/dist/coverage/types.js +5 -0
- package/dist/dashboard/server.d.ts +24 -0
- package/dist/dashboard/server.js +402 -0
- package/dist/enforcement/runner.d.ts +67 -0
- package/dist/enforcement/runner.js +460 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.js +25 -0
- package/dist/integrations/github/pr-analyze.d.ts +28 -0
- package/dist/integrations/github/pr-analyze.js +98 -0
- package/dist/integrations/local-ai/provider.d.ts +45 -0
- package/dist/integrations/local-ai/provider.js +104 -0
- package/dist/intelligence/git/analyze.d.ts +34 -0
- package/dist/intelligence/git/analyze.js +136 -0
- package/dist/intelligence/graph/build.d.ts +15 -0
- package/dist/intelligence/graph/build.js +259 -0
- package/dist/intelligence/graph/incremental.d.ts +68 -0
- package/dist/intelligence/graph/incremental.js +234 -0
- package/dist/intelligence/resolve/imports.d.ts +36 -0
- package/dist/intelligence/resolve/imports.js +245 -0
- package/dist/knowledge/store.d.ts +25 -0
- package/dist/knowledge/store.js +91 -0
- package/dist/languages/go.d.ts +9 -0
- package/dist/languages/go.js +45 -0
- package/dist/languages/index.d.ts +11 -0
- package/dist/languages/index.js +76 -0
- package/dist/languages/php.d.ts +7 -0
- package/dist/languages/php.js +193 -0
- package/dist/languages/python.d.ts +7 -0
- package/dist/languages/python.js +150 -0
- package/dist/languages/types.d.ts +42 -0
- package/dist/languages/types.js +26 -0
- package/dist/languages/typescript.d.ts +3 -0
- package/dist/languages/typescript.js +95 -0
- package/dist/mcp/agentdoctor/server.d.ts +16 -0
- package/dist/mcp/agentdoctor/server.js +67 -0
- package/dist/mcp/brain/session.js +7 -7
- package/dist/mcp/intelligence/handlers.d.ts +16 -0
- package/dist/mcp/intelligence/handlers.js +364 -0
- package/dist/mcp/intelligence/path-safety.d.ts +9 -0
- package/dist/mcp/intelligence/path-safety.js +63 -0
- package/dist/mcp/intelligence/registry.d.ts +8 -0
- package/dist/mcp/intelligence/registry.js +228 -0
- package/dist/ops/health.d.ts +18 -0
- package/dist/ops/health.js +51 -0
- package/dist/platform/ai-quality/analyze.d.ts +9 -0
- package/dist/platform/ai-quality/analyze.js +65 -0
- package/dist/platform/architecture/drift.d.ts +17 -0
- package/dist/platform/architecture/drift.js +74 -0
- package/dist/platform/auth/local.d.ts +15 -0
- package/dist/platform/auth/local.js +58 -0
- package/dist/platform/context-security/analyze.d.ts +6 -0
- package/dist/platform/context-security/analyze.js +97 -0
- package/dist/platform/firewall/evaluate.d.ts +57 -0
- package/dist/platform/firewall/evaluate.js +359 -0
- package/dist/platform/graph/build.d.ts +6 -0
- package/dist/platform/graph/build.js +189 -0
- package/dist/platform/health/analyze.d.ts +7 -0
- package/dist/platform/health/analyze.js +192 -0
- package/dist/platform/index.d.ts +27 -0
- package/dist/platform/index.js +89 -0
- package/dist/platform/knowledge/analyze.d.ts +20 -0
- package/dist/platform/knowledge/analyze.js +96 -0
- package/dist/platform/provenance/build.d.ts +44 -0
- package/dist/platform/provenance/build.js +65 -0
- package/dist/platform/readiness/scorecard.d.ts +5 -0
- package/dist/platform/readiness/scorecard.js +98 -0
- package/dist/platform/refactor/impact.d.ts +20 -0
- package/dist/platform/refactor/impact.js +60 -0
- package/dist/platform/reports/export.d.ts +12 -0
- package/dist/platform/reports/export.js +92 -0
- package/dist/platform/security/redact.d.ts +15 -0
- package/dist/platform/security/redact.js +87 -0
- package/dist/platform/sessions/store.d.ts +32 -0
- package/dist/platform/sessions/store.js +97 -0
- package/dist/platform/store.d.ts +6 -0
- package/dist/platform/store.js +57 -0
- package/dist/platform/test-impact/analyze.d.ts +50 -0
- package/dist/platform/test-impact/analyze.js +350 -0
- package/dist/platform/time-machine/compare.d.ts +17 -0
- package/dist/platform/time-machine/compare.js +62 -0
- package/dist/platform/tokens/plan.d.ts +28 -0
- package/dist/platform/tokens/plan.js +108 -0
- package/dist/platform/types.d.ts +120 -0
- package/dist/platform/types.js +5 -0
- package/dist/plugins/runtime.d.ts +36 -0
- package/dist/plugins/runtime.js +101 -0
- package/dist/plugins/sdk.d.ts +29 -0
- package/dist/plugins/sdk.js +114 -0
- package/dist/policy/compose.d.ts +34 -0
- package/dist/policy/compose.js +118 -0
- package/dist/policy/packs.d.ts +9 -0
- package/dist/policy/packs.js +91 -0
- package/dist/reporters/terminal/report.js +38 -5
- package/dist/security/paths.d.ts +21 -0
- package/dist/security/paths.js +105 -0
- package/dist/storage/postgres.d.ts +26 -0
- package/dist/storage/postgres.js +90 -0
- package/dist/storage/provider.d.ts +30 -0
- package/dist/storage/provider.js +109 -0
- package/dist/storage/sqlite.d.ts +33 -0
- package/dist/storage/sqlite.js +77 -0
- package/dist/team/auth.d.ts +35 -0
- package/dist/team/auth.js +70 -0
- package/dist/types/index.d.ts +5 -1
- package/dist/workspace/index.d.ts +69 -0
- package/dist/workspace/index.js +220 -0
- package/package.json +15 -5
package/dist/cli/program.js
CHANGED
|
@@ -3,11 +3,26 @@ import { PACKAGE_VERSION } from "../constants.js";
|
|
|
3
3
|
import { parseFailOnRules, parseSeverityGate } from "../core/policy/evaluate.js";
|
|
4
4
|
import { EXIT_CODES } from "../types/index.js";
|
|
5
5
|
import { runBrainMcpCommand } from "./commands/brain-mcp.js";
|
|
6
|
+
import { runBrainCommand } from "./commands/brain.js";
|
|
6
7
|
import { runDoctorCommand } from "./commands/doctor.js";
|
|
7
8
|
import { runExplainCommand } from "./commands/explain.js";
|
|
8
9
|
import { runFixCommand } from "./commands/fix.js";
|
|
9
10
|
import { resolveTargetArgument, runScanCommand } from "./commands/scan.js";
|
|
10
11
|
import { runVerifyCommand } from "./commands/verify.js";
|
|
12
|
+
import { runBaselineCommand, runChangesCommand, runContextHealthCommand, runDashboardCommand, runFixHistoryCommand, runFixUndoCommand, runLocalAiCommand, runPackagesCommand, runPluginsCommand, runPrReviewCommand, runSecretsCommand, } from "./commands/v2.js";
|
|
13
|
+
import { runPlatformCommand } from "./commands/platform.js";
|
|
14
|
+
import { runInitCommand, runV2SurfaceCommand } from "./commands/complete.js";
|
|
15
|
+
import { runMcpCommand } from "./commands/mcp.js";
|
|
16
|
+
import { runChangeAnalyzeCommand, runChangeDiffCommand, runChangeExplainCommand, runChangeStatusCommand, runChangeVerifyCommand, runEvidenceInspectCommand, runEvidenceVerifyCommand, runProofBuildCommand, runProofExplainCommand, runProofExportCommand, runProofInspectCommand, runProofVerifyCommand, } from "./commands/assurance.js";
|
|
17
|
+
import { runGraphSurfaceCommand, runPolicyCheckCommand, runPolicyEnforceCommand, runPolicyExplainCommand, runRunCommand, runRunExplainCommand, } from "./commands/policy-graph-run.js";
|
|
18
|
+
import { runArchitectureCommand } from "./commands/architecture.js";
|
|
19
|
+
import { runWorkspaceCommand } from "./commands/workspace.js";
|
|
20
|
+
import { collectOpsHealth } from "../ops/health.js";
|
|
21
|
+
import { listSessions, loadSession } from "../platform/sessions/store.js";
|
|
22
|
+
import { exportReports } from "../platform/reports/export.js";
|
|
23
|
+
import { runPlatformScan } from "../platform/index.js";
|
|
24
|
+
import { analyzeTestImpact, formatTestImpactHuman, persistTestImpactReport, } from "../platform/test-impact/analyze.js";
|
|
25
|
+
import { resolveRepoRoot } from "../utils/path.js";
|
|
11
26
|
function parseMinScore(value) {
|
|
12
27
|
const parsed = Number(value);
|
|
13
28
|
if (!Number.isFinite(parsed) || parsed < 0 || parsed > 100) {
|
|
@@ -80,7 +95,7 @@ export function createProgram() {
|
|
|
80
95
|
const program = new Command();
|
|
81
96
|
addScanOptions(program
|
|
82
97
|
.name("agentdoctor")
|
|
83
|
-
.description("
|
|
98
|
+
.description("Engineering intelligence and safety for AI coding agents — scan, knowledge, MCP, and verification (local, deterministic; no API key required for core flows).")
|
|
84
99
|
.version(PACKAGE_VERSION, "-V, --version", "Print AgentDoctor version")
|
|
85
100
|
.argument("[path]", "Repository path to scan (default: current directory)")).action(async (pathArg, _options, command) => {
|
|
86
101
|
await runScanFromCli(pathArg, command);
|
|
@@ -93,7 +108,7 @@ export function createProgram() {
|
|
|
93
108
|
});
|
|
94
109
|
program
|
|
95
110
|
.command("fix")
|
|
96
|
-
.description("Apply safe automatic fixes (
|
|
111
|
+
.description("Apply safe automatic fixes (.cursorignore, Claude Code Read deny, Codex filesystem deny, .geminiignore, .aiderignore for safe context findings)")
|
|
97
112
|
.argument("[path]", "Repository path (default: current directory)")
|
|
98
113
|
.option("--dry-run", "Show proposed fixes without writing files", false)
|
|
99
114
|
.option("-y, --yes", "Skip confirmation prompts", false)
|
|
@@ -105,6 +120,1295 @@ export function createProgram() {
|
|
|
105
120
|
});
|
|
106
121
|
process.exitCode = code;
|
|
107
122
|
});
|
|
123
|
+
program
|
|
124
|
+
.command("fix-undo")
|
|
125
|
+
.description("Restore files from a Safe Fix audit backup")
|
|
126
|
+
.argument("<auditId>", "Audit id from fix-history")
|
|
127
|
+
.argument("[path]", "Repository path (default: current directory)")
|
|
128
|
+
.action(async (auditId, pathArg) => {
|
|
129
|
+
process.exitCode = await runFixUndoCommand({
|
|
130
|
+
auditId,
|
|
131
|
+
root: resolveTargetArgument(pathArg),
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
program
|
|
135
|
+
.command("fix-history")
|
|
136
|
+
.description("List Safe Fix audit / backup records")
|
|
137
|
+
.argument("[path]", "Repository path (default: current directory)")
|
|
138
|
+
.option("--json", "Emit JSON", false)
|
|
139
|
+
.action(async (pathArg, options) => {
|
|
140
|
+
process.exitCode = await runFixHistoryCommand({
|
|
141
|
+
root: resolveTargetArgument(pathArg),
|
|
142
|
+
json: Boolean(options.json),
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
const brain = program.command("brain").description("Project Brain CLI (local, deterministic)");
|
|
146
|
+
brain
|
|
147
|
+
.command("init")
|
|
148
|
+
.description("Ensure Project Brain store directories exist")
|
|
149
|
+
.argument("[path]", "Repository path")
|
|
150
|
+
.option("--json", "Emit JSON", false)
|
|
151
|
+
.action(async (pathArg, _options, command) => {
|
|
152
|
+
const options = command.optsWithGlobals();
|
|
153
|
+
process.exitCode = await runBrainCommand({
|
|
154
|
+
action: "init",
|
|
155
|
+
root: resolveTargetArgument(pathArg),
|
|
156
|
+
json: Boolean(options.json),
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
brain
|
|
160
|
+
.command("status")
|
|
161
|
+
.description("Show Project Brain store status")
|
|
162
|
+
.argument("[path]", "Repository path")
|
|
163
|
+
.option("--json", "Emit JSON", false)
|
|
164
|
+
.action(async (pathArg, _options, command) => {
|
|
165
|
+
const options = command.optsWithGlobals();
|
|
166
|
+
process.exitCode = await runBrainCommand({
|
|
167
|
+
action: "status",
|
|
168
|
+
root: resolveTargetArgument(pathArg),
|
|
169
|
+
json: Boolean(options.json),
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
brain
|
|
173
|
+
.command("inspect")
|
|
174
|
+
.description("Inspect latest Project Brain summary")
|
|
175
|
+
.argument("[path]", "Repository path")
|
|
176
|
+
.option("--json", "Emit JSON", false)
|
|
177
|
+
.action(async (pathArg, _options, command) => {
|
|
178
|
+
const options = command.optsWithGlobals();
|
|
179
|
+
process.exitCode = await runBrainCommand({
|
|
180
|
+
action: "inspect",
|
|
181
|
+
root: resolveTargetArgument(pathArg),
|
|
182
|
+
json: Boolean(options.json),
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
brain
|
|
186
|
+
.command("rebuild")
|
|
187
|
+
.description("Compile and save a new Project Brain snapshot")
|
|
188
|
+
.argument("[path]", "Repository path")
|
|
189
|
+
.option("--json", "Emit JSON", false)
|
|
190
|
+
.action(async (pathArg, _options, command) => {
|
|
191
|
+
const options = command.optsWithGlobals();
|
|
192
|
+
process.exitCode = await runBrainCommand({
|
|
193
|
+
action: "rebuild",
|
|
194
|
+
root: resolveTargetArgument(pathArg),
|
|
195
|
+
json: Boolean(options.json),
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
brain
|
|
199
|
+
.command("history")
|
|
200
|
+
.description("List Project Brain snapshots")
|
|
201
|
+
.argument("[path]", "Repository path")
|
|
202
|
+
.option("--json", "Emit JSON", false)
|
|
203
|
+
.action(async (pathArg, _options, command) => {
|
|
204
|
+
const options = command.optsWithGlobals();
|
|
205
|
+
process.exitCode = await runBrainCommand({
|
|
206
|
+
action: "history",
|
|
207
|
+
root: resolveTargetArgument(pathArg),
|
|
208
|
+
json: Boolean(options.json),
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
brain
|
|
212
|
+
.command("search")
|
|
213
|
+
.description("Search claims/components in the latest brain")
|
|
214
|
+
.argument("<query>", "Search text")
|
|
215
|
+
.argument("[path]", "Repository path")
|
|
216
|
+
.option("--json", "Emit JSON", false)
|
|
217
|
+
.action(async (query, pathArg, _options, command) => {
|
|
218
|
+
const options = command.optsWithGlobals();
|
|
219
|
+
process.exitCode = await runBrainCommand({
|
|
220
|
+
action: "search",
|
|
221
|
+
query,
|
|
222
|
+
root: resolveTargetArgument(pathArg),
|
|
223
|
+
json: Boolean(options.json),
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
brain
|
|
227
|
+
.command("export")
|
|
228
|
+
.description("Export a redacted brain snapshot to JSON")
|
|
229
|
+
.requiredOption("--file <path>", "Output file")
|
|
230
|
+
.option("--snapshot <id>", "Snapshot id (default: latest)")
|
|
231
|
+
.argument("[path]", "Repository path")
|
|
232
|
+
.action(async (pathArg, options) => {
|
|
233
|
+
process.exitCode = await runBrainCommand({
|
|
234
|
+
action: "export",
|
|
235
|
+
file: options.file,
|
|
236
|
+
...(options.snapshot ? { snapshotId: options.snapshot } : {}),
|
|
237
|
+
root: resolveTargetArgument(pathArg),
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
brain
|
|
241
|
+
.command("import")
|
|
242
|
+
.description("Import a brain JSON snapshot into the local store")
|
|
243
|
+
.requiredOption("--file <path>", "Input file")
|
|
244
|
+
.argument("[path]", "Repository path")
|
|
245
|
+
.action(async (pathArg, options) => {
|
|
246
|
+
process.exitCode = await runBrainCommand({
|
|
247
|
+
action: "import",
|
|
248
|
+
file: options.file,
|
|
249
|
+
root: resolveTargetArgument(pathArg),
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
brain
|
|
253
|
+
.command("snapshot")
|
|
254
|
+
.description("Create Brain + Repository Brain product snapshots")
|
|
255
|
+
.argument("[path]", "Repository path")
|
|
256
|
+
.option("--json", "Emit JSON", false)
|
|
257
|
+
.action(async (pathArg, _options, command) => {
|
|
258
|
+
const options = command.optsWithGlobals();
|
|
259
|
+
process.exitCode = await runBrainCommand({
|
|
260
|
+
action: "snapshot",
|
|
261
|
+
root: resolveTargetArgument(pathArg),
|
|
262
|
+
json: Boolean(options.json),
|
|
263
|
+
});
|
|
264
|
+
});
|
|
265
|
+
brain
|
|
266
|
+
.command("update")
|
|
267
|
+
.description("Rebuild Project Brain and write product snapshot")
|
|
268
|
+
.argument("[path]", "Repository path")
|
|
269
|
+
.option("--json", "Emit JSON", false)
|
|
270
|
+
.action(async (pathArg, _options, command) => {
|
|
271
|
+
const options = command.optsWithGlobals();
|
|
272
|
+
process.exitCode = await runBrainCommand({
|
|
273
|
+
action: "update",
|
|
274
|
+
root: resolveTargetArgument(pathArg),
|
|
275
|
+
json: Boolean(options.json),
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
brain
|
|
279
|
+
.command("review")
|
|
280
|
+
.description("Human review of Repository Brain proposals (never auto-approves)")
|
|
281
|
+
.requiredOption("--artifact <id>", "Proposal artifact id")
|
|
282
|
+
.requiredOption("--decision <status>", "approved|rejected|pending-review|deprecated")
|
|
283
|
+
.option("--note <text>", "Review note")
|
|
284
|
+
.argument("[path]", "Repository path")
|
|
285
|
+
.option("--json", "Emit JSON", false)
|
|
286
|
+
.action(async (pathArg, _options, command) => {
|
|
287
|
+
const options = command.optsWithGlobals();
|
|
288
|
+
process.exitCode = await runBrainCommand({
|
|
289
|
+
action: "review",
|
|
290
|
+
artifactId: options.artifact,
|
|
291
|
+
decision: options.decision,
|
|
292
|
+
...(options.note ? { note: options.note } : {}),
|
|
293
|
+
root: resolveTargetArgument(pathArg),
|
|
294
|
+
json: Boolean(options.json),
|
|
295
|
+
});
|
|
296
|
+
});
|
|
297
|
+
brain
|
|
298
|
+
.command("proposals")
|
|
299
|
+
.description("List Repository Brain proposal artifacts")
|
|
300
|
+
.argument("[path]", "Repository path")
|
|
301
|
+
.option("--json", "Emit JSON", false)
|
|
302
|
+
.action(async (pathArg, _options, command) => {
|
|
303
|
+
const options = command.optsWithGlobals();
|
|
304
|
+
process.exitCode = await runBrainCommand({
|
|
305
|
+
action: "proposals",
|
|
306
|
+
root: resolveTargetArgument(pathArg),
|
|
307
|
+
json: Boolean(options.json),
|
|
308
|
+
});
|
|
309
|
+
});
|
|
310
|
+
program
|
|
311
|
+
.command("init")
|
|
312
|
+
.description("Repository Brain initializer — PROPOSED artifacts only (not approved facts)")
|
|
313
|
+
.argument("[path]", "Repository path")
|
|
314
|
+
.option("--name <name>", "Project name")
|
|
315
|
+
.option("--domain <domain>", "Business domain")
|
|
316
|
+
.option("--json", "Emit JSON", false)
|
|
317
|
+
.action(async (pathArg, _options, command) => {
|
|
318
|
+
const options = command.optsWithGlobals();
|
|
319
|
+
process.exitCode = await runInitCommand({
|
|
320
|
+
root: resolveTargetArgument(pathArg),
|
|
321
|
+
json: Boolean(options.json),
|
|
322
|
+
...(options.name ? { name: options.name } : {}),
|
|
323
|
+
...(options.domain ? { domain: options.domain } : {}),
|
|
324
|
+
});
|
|
325
|
+
});
|
|
326
|
+
const graph = program
|
|
327
|
+
.command("graph")
|
|
328
|
+
.description("Build / update intelligence graph (TypeScript AST when available; regex fallback). Bare `graph` aliases build.")
|
|
329
|
+
.argument("[path]", "Repository path")
|
|
330
|
+
.option("--mode <mode>", "auto|regex|typescript-ast", "auto")
|
|
331
|
+
.option("--json", "Emit JSON", false)
|
|
332
|
+
.action(async (pathArg, _options, command) => {
|
|
333
|
+
const options = command.optsWithGlobals();
|
|
334
|
+
// Backward compat: `agentdoctor graph --json` → build + persist
|
|
335
|
+
process.exitCode = await runGraphSurfaceCommand({
|
|
336
|
+
action: "build",
|
|
337
|
+
root: resolveTargetArgument(pathArg),
|
|
338
|
+
mode: options.mode ?? "auto",
|
|
339
|
+
json: Boolean(options.json),
|
|
340
|
+
});
|
|
341
|
+
});
|
|
342
|
+
graph
|
|
343
|
+
.command("build")
|
|
344
|
+
.description("Build graph and persist .agentdoctor/graph/index.json")
|
|
345
|
+
.argument("[path]", "Repository path")
|
|
346
|
+
.option("--mode <mode>", "auto|regex|typescript-ast", "auto")
|
|
347
|
+
.option("--json", "Emit JSON", false)
|
|
348
|
+
.action(async (pathArg, options) => {
|
|
349
|
+
process.exitCode = await runGraphSurfaceCommand({
|
|
350
|
+
action: "build",
|
|
351
|
+
root: resolveTargetArgument(pathArg),
|
|
352
|
+
mode: options.mode ?? "auto",
|
|
353
|
+
json: Boolean(options.json),
|
|
354
|
+
});
|
|
355
|
+
});
|
|
356
|
+
graph
|
|
357
|
+
.command("update")
|
|
358
|
+
.description("Incrementally update graph from git/hash changes (rebuilds on corruption)")
|
|
359
|
+
.argument("[path]", "Repository path")
|
|
360
|
+
.option("--mode <mode>", "auto|regex|typescript-ast", "auto")
|
|
361
|
+
.option("--json", "Emit JSON", false)
|
|
362
|
+
.action(async (pathArg, options) => {
|
|
363
|
+
process.exitCode = await runGraphSurfaceCommand({
|
|
364
|
+
action: "update",
|
|
365
|
+
root: resolveTargetArgument(pathArg),
|
|
366
|
+
mode: options.mode ?? "auto",
|
|
367
|
+
json: Boolean(options.json),
|
|
368
|
+
});
|
|
369
|
+
});
|
|
370
|
+
graph
|
|
371
|
+
.command("rebuild")
|
|
372
|
+
.description("Force full graph rebuild")
|
|
373
|
+
.argument("[path]", "Repository path")
|
|
374
|
+
.option("--mode <mode>", "auto|regex|typescript-ast", "auto")
|
|
375
|
+
.option("--json", "Emit JSON", false)
|
|
376
|
+
.action(async (pathArg, options) => {
|
|
377
|
+
process.exitCode = await runGraphSurfaceCommand({
|
|
378
|
+
action: "rebuild",
|
|
379
|
+
root: resolveTargetArgument(pathArg),
|
|
380
|
+
mode: options.mode ?? "auto",
|
|
381
|
+
json: Boolean(options.json),
|
|
382
|
+
});
|
|
383
|
+
});
|
|
384
|
+
graph
|
|
385
|
+
.command("status")
|
|
386
|
+
.description("Show persisted graph index status")
|
|
387
|
+
.argument("[path]", "Repository path")
|
|
388
|
+
.option("--json", "Emit JSON", false)
|
|
389
|
+
.action(async (pathArg, options) => {
|
|
390
|
+
process.exitCode = await runGraphSurfaceCommand({
|
|
391
|
+
action: "status",
|
|
392
|
+
root: resolveTargetArgument(pathArg),
|
|
393
|
+
json: Boolean(options.json),
|
|
394
|
+
});
|
|
395
|
+
});
|
|
396
|
+
program
|
|
397
|
+
.command("health")
|
|
398
|
+
.description("Git hotspot / engineering intelligence report")
|
|
399
|
+
.argument("[path]", "Repository path")
|
|
400
|
+
.option("--json", "Emit JSON", false)
|
|
401
|
+
.action(async (pathArg, _options, command) => {
|
|
402
|
+
const options = command.optsWithGlobals();
|
|
403
|
+
process.exitCode = await runV2SurfaceCommand({
|
|
404
|
+
action: "health",
|
|
405
|
+
root: resolveTargetArgument(pathArg),
|
|
406
|
+
json: Boolean(options.json),
|
|
407
|
+
});
|
|
408
|
+
});
|
|
409
|
+
program
|
|
410
|
+
.command("impact")
|
|
411
|
+
.description("Change/test impact analysis (heuristic by default; coverage-backed with --coverage)")
|
|
412
|
+
.argument("[path]", "Repository path")
|
|
413
|
+
.option("--coverage <path>", "LCOV / Istanbul JSON / Cobertura XML coverage file")
|
|
414
|
+
.option("--since <ref>", "Diff since git ref")
|
|
415
|
+
.option("--json", "Emit JSON", false)
|
|
416
|
+
.action(async (pathArg, _options, command) => {
|
|
417
|
+
const options = command.optsWithGlobals();
|
|
418
|
+
const root = resolveTargetArgument(pathArg);
|
|
419
|
+
try {
|
|
420
|
+
const impact = await analyzeTestImpact({
|
|
421
|
+
root,
|
|
422
|
+
...(options.coverage ? { coveragePath: options.coverage } : {}),
|
|
423
|
+
...(options.since ? { since: options.since } : {}),
|
|
424
|
+
});
|
|
425
|
+
if (options.json) {
|
|
426
|
+
process.stdout.write(`${JSON.stringify(impact, null, 2)}\n`);
|
|
427
|
+
}
|
|
428
|
+
else {
|
|
429
|
+
process.stdout.write(formatTestImpactHuman(impact));
|
|
430
|
+
}
|
|
431
|
+
process.exitCode = EXIT_CODES.SUCCESS;
|
|
432
|
+
}
|
|
433
|
+
catch (error) {
|
|
434
|
+
console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
435
|
+
process.exitCode = EXIT_CODES.INTERNAL_ERROR;
|
|
436
|
+
}
|
|
437
|
+
});
|
|
438
|
+
program
|
|
439
|
+
.command("test-impact")
|
|
440
|
+
.description("Test-impact analysis (alias of impact)")
|
|
441
|
+
.argument("[path]", "Repository path")
|
|
442
|
+
.option("--coverage <path>", "LCOV / Istanbul JSON / Cobertura XML coverage file")
|
|
443
|
+
.option("--since <ref>", "Diff since git ref")
|
|
444
|
+
.option("--json", "Emit JSON", false)
|
|
445
|
+
.action(async (pathArg, _options, command) => {
|
|
446
|
+
const options = command.optsWithGlobals();
|
|
447
|
+
const root = resolveTargetArgument(pathArg);
|
|
448
|
+
try {
|
|
449
|
+
const impact = await analyzeTestImpact({
|
|
450
|
+
root,
|
|
451
|
+
...(options.coverage ? { coveragePath: options.coverage } : {}),
|
|
452
|
+
...(options.since ? { since: options.since } : {}),
|
|
453
|
+
});
|
|
454
|
+
const out = await persistTestImpactReport(resolveRepoRoot(root), impact);
|
|
455
|
+
if (options.json) {
|
|
456
|
+
process.stdout.write(`${JSON.stringify({ ...impact, reportPath: out }, null, 2)}\n`);
|
|
457
|
+
}
|
|
458
|
+
else {
|
|
459
|
+
process.stdout.write(formatTestImpactHuman(impact));
|
|
460
|
+
process.stdout.write(` wrote: ${out}\n`);
|
|
461
|
+
}
|
|
462
|
+
process.exitCode = EXIT_CODES.SUCCESS;
|
|
463
|
+
}
|
|
464
|
+
catch (error) {
|
|
465
|
+
console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
466
|
+
process.exitCode = EXIT_CODES.INTERNAL_ERROR;
|
|
467
|
+
}
|
|
468
|
+
});
|
|
469
|
+
program
|
|
470
|
+
.command("refactor-impact")
|
|
471
|
+
.description("Refactor/rename blast-radius analysis")
|
|
472
|
+
.requiredOption("--symbol <name>", "Symbol to analyze")
|
|
473
|
+
.argument("[path]", "Repository path")
|
|
474
|
+
.option("--json", "Emit JSON", false)
|
|
475
|
+
.action(async (pathArg, _options, command) => {
|
|
476
|
+
const options = command.optsWithGlobals();
|
|
477
|
+
process.exitCode = await runV2SurfaceCommand({
|
|
478
|
+
action: "refactor-impact",
|
|
479
|
+
root: resolveTargetArgument(pathArg),
|
|
480
|
+
symbol: options.symbol,
|
|
481
|
+
json: Boolean(options.json),
|
|
482
|
+
});
|
|
483
|
+
});
|
|
484
|
+
program
|
|
485
|
+
.command("session")
|
|
486
|
+
.description("List or show agent sessions")
|
|
487
|
+
.argument("[path]", "Repository path")
|
|
488
|
+
.option("--id <id>", "Session id to show")
|
|
489
|
+
.option("--json", "Emit JSON", false)
|
|
490
|
+
.action(async (pathArg, _options, command) => {
|
|
491
|
+
const options = command.optsWithGlobals();
|
|
492
|
+
const root = resolveTargetArgument(pathArg);
|
|
493
|
+
try {
|
|
494
|
+
if (options.id) {
|
|
495
|
+
const session = await loadSession(root, options.id);
|
|
496
|
+
if (options.json)
|
|
497
|
+
process.stdout.write(`${JSON.stringify(session, null, 2)}\n`);
|
|
498
|
+
else
|
|
499
|
+
process.stdout.write(`${session?.id ?? "not-found"}\n`);
|
|
500
|
+
}
|
|
501
|
+
else {
|
|
502
|
+
const sessions = await listSessions(root);
|
|
503
|
+
if (options.json)
|
|
504
|
+
process.stdout.write(`${JSON.stringify(sessions, null, 2)}\n`);
|
|
505
|
+
else
|
|
506
|
+
for (const id of sessions)
|
|
507
|
+
process.stdout.write(`${id}\n`);
|
|
508
|
+
}
|
|
509
|
+
process.exitCode = EXIT_CODES.SUCCESS;
|
|
510
|
+
}
|
|
511
|
+
catch (error) {
|
|
512
|
+
console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
513
|
+
process.exitCode = EXIT_CODES.INTERNAL_ERROR;
|
|
514
|
+
}
|
|
515
|
+
});
|
|
516
|
+
program
|
|
517
|
+
.command("report")
|
|
518
|
+
.description("Run platform scan and export local reports")
|
|
519
|
+
.argument("[path]", "Repository path")
|
|
520
|
+
.option("--json", "Emit JSON", false)
|
|
521
|
+
.action(async (pathArg, _options, command) => {
|
|
522
|
+
const options = command.optsWithGlobals();
|
|
523
|
+
const root = resolveTargetArgument(pathArg);
|
|
524
|
+
try {
|
|
525
|
+
const result = await runPlatformScan(root);
|
|
526
|
+
const paths = await exportReports({
|
|
527
|
+
root,
|
|
528
|
+
findings: result.snapshot.findings,
|
|
529
|
+
readiness: result.snapshot.readiness,
|
|
530
|
+
snapshot: result.snapshot,
|
|
531
|
+
});
|
|
532
|
+
if (options.json)
|
|
533
|
+
process.stdout.write(`${JSON.stringify({ ...result.reportPaths, ...paths }, null, 2)}\n`);
|
|
534
|
+
else
|
|
535
|
+
process.stdout.write(`Reports written under .agentdoctor/platform/\n`);
|
|
536
|
+
process.exitCode = EXIT_CODES.SUCCESS;
|
|
537
|
+
}
|
|
538
|
+
catch (error) {
|
|
539
|
+
console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
540
|
+
process.exitCode = EXIT_CODES.INTERNAL_ERROR;
|
|
541
|
+
}
|
|
542
|
+
});
|
|
543
|
+
program
|
|
544
|
+
.command("c4")
|
|
545
|
+
.description("C4-style architecture views from graph evidence")
|
|
546
|
+
.argument("[path]", "Repository path")
|
|
547
|
+
.option("--json", "Emit JSON", false)
|
|
548
|
+
.action(async (pathArg, _options, command) => {
|
|
549
|
+
const options = command.optsWithGlobals();
|
|
550
|
+
process.exitCode = await runV2SurfaceCommand({
|
|
551
|
+
action: "c4",
|
|
552
|
+
root: resolveTargetArgument(pathArg),
|
|
553
|
+
json: Boolean(options.json),
|
|
554
|
+
});
|
|
555
|
+
});
|
|
556
|
+
const architecture = program
|
|
557
|
+
.command("architecture")
|
|
558
|
+
.description("Architecture contract + C4 views (rules are advisory until you treat check as a gate)");
|
|
559
|
+
architecture
|
|
560
|
+
.command("init")
|
|
561
|
+
.description("Write default .agentdoctor/architecture.json if missing")
|
|
562
|
+
.argument("[path]", "Repository path")
|
|
563
|
+
.option("--json", "Emit JSON", false)
|
|
564
|
+
.action(async (pathArg, options) => {
|
|
565
|
+
process.exitCode = await runArchitectureCommand({
|
|
566
|
+
action: "init",
|
|
567
|
+
root: resolveTargetArgument(pathArg),
|
|
568
|
+
json: Boolean(options.json),
|
|
569
|
+
});
|
|
570
|
+
});
|
|
571
|
+
architecture
|
|
572
|
+
.command("analyze")
|
|
573
|
+
.description("C4 views + architecture contract summary")
|
|
574
|
+
.argument("[path]", "Repository path")
|
|
575
|
+
.option("--json", "Emit JSON", false)
|
|
576
|
+
.action(async (pathArg, options) => {
|
|
577
|
+
process.exitCode = await runArchitectureCommand({
|
|
578
|
+
action: "analyze",
|
|
579
|
+
root: resolveTargetArgument(pathArg),
|
|
580
|
+
json: Boolean(options.json),
|
|
581
|
+
});
|
|
582
|
+
});
|
|
583
|
+
architecture
|
|
584
|
+
.command("check")
|
|
585
|
+
.description("Check import edges against .agentdoctor/architecture.json|.yml")
|
|
586
|
+
.argument("[path]", "Repository path")
|
|
587
|
+
.option("--json", "Emit JSON", false)
|
|
588
|
+
.action(async (pathArg, options) => {
|
|
589
|
+
process.exitCode = await runArchitectureCommand({
|
|
590
|
+
action: "check",
|
|
591
|
+
root: resolveTargetArgument(pathArg),
|
|
592
|
+
json: Boolean(options.json),
|
|
593
|
+
});
|
|
594
|
+
});
|
|
595
|
+
architecture
|
|
596
|
+
.command("explain")
|
|
597
|
+
.description("Explain architecture layers, forbidden/allowed rules, and recent violations")
|
|
598
|
+
.argument("[path]", "Repository path")
|
|
599
|
+
.option("--json", "Emit JSON", false)
|
|
600
|
+
.action(async (pathArg, options) => {
|
|
601
|
+
process.exitCode = await runArchitectureCommand({
|
|
602
|
+
action: "explain",
|
|
603
|
+
root: resolveTargetArgument(pathArg),
|
|
604
|
+
json: Boolean(options.json),
|
|
605
|
+
});
|
|
606
|
+
});
|
|
607
|
+
program
|
|
608
|
+
.command("knowledge")
|
|
609
|
+
.description("List governed knowledge records")
|
|
610
|
+
.argument("[path]", "Repository path")
|
|
611
|
+
.option("--json", "Emit JSON", false)
|
|
612
|
+
.action(async (pathArg, _options, command) => {
|
|
613
|
+
const options = command.optsWithGlobals();
|
|
614
|
+
process.exitCode = await runV2SurfaceCommand({
|
|
615
|
+
action: "knowledge-list",
|
|
616
|
+
root: resolveTargetArgument(pathArg),
|
|
617
|
+
json: Boolean(options.json),
|
|
618
|
+
});
|
|
619
|
+
});
|
|
620
|
+
program
|
|
621
|
+
.command("knowledge-create")
|
|
622
|
+
.description("Create a draft knowledge record")
|
|
623
|
+
.requiredOption("--title <title>", "Title")
|
|
624
|
+
.requiredOption("--content <text>", "Content")
|
|
625
|
+
.argument("[path]", "Repository path")
|
|
626
|
+
.option("--json", "Emit JSON", false)
|
|
627
|
+
.action(async (pathArg, _options, command) => {
|
|
628
|
+
const options = command.optsWithGlobals();
|
|
629
|
+
process.exitCode = await runV2SurfaceCommand({
|
|
630
|
+
action: "knowledge-create",
|
|
631
|
+
root: resolveTargetArgument(pathArg),
|
|
632
|
+
title: options.title,
|
|
633
|
+
content: options.content,
|
|
634
|
+
json: Boolean(options.json),
|
|
635
|
+
});
|
|
636
|
+
});
|
|
637
|
+
program
|
|
638
|
+
.command("knowledge-approve")
|
|
639
|
+
.description("Transition knowledge record (explicit human approval)")
|
|
640
|
+
.requiredOption("--id <id>", "Record id")
|
|
641
|
+
.requiredOption("--decision <status>", "approved|rejected|pending-review|deprecated|draft")
|
|
642
|
+
.argument("[path]", "Repository path")
|
|
643
|
+
.option("--json", "Emit JSON", false)
|
|
644
|
+
.action(async (pathArg, _options, command) => {
|
|
645
|
+
const options = command.optsWithGlobals();
|
|
646
|
+
process.exitCode = await runV2SurfaceCommand({
|
|
647
|
+
action: "knowledge-transition",
|
|
648
|
+
root: resolveTargetArgument(pathArg),
|
|
649
|
+
id: options.id,
|
|
650
|
+
decision: options.decision,
|
|
651
|
+
json: Boolean(options.json),
|
|
652
|
+
});
|
|
653
|
+
});
|
|
654
|
+
program
|
|
655
|
+
.command("enforce")
|
|
656
|
+
.description("AgentDoctor-controlled runner policy check (does not execute by default)")
|
|
657
|
+
.requiredOption("--command <cmd>", "Command string")
|
|
658
|
+
.argument("[path]", "Repository path")
|
|
659
|
+
.option("--json", "Emit JSON", false)
|
|
660
|
+
.action(async (pathArg, _options, command) => {
|
|
661
|
+
const options = command.optsWithGlobals();
|
|
662
|
+
process.exitCode = await runV2SurfaceCommand({
|
|
663
|
+
action: "enforce-check",
|
|
664
|
+
root: resolveTargetArgument(pathArg),
|
|
665
|
+
command: options.command,
|
|
666
|
+
json: Boolean(options.json),
|
|
667
|
+
});
|
|
668
|
+
});
|
|
669
|
+
program
|
|
670
|
+
.command("team-register")
|
|
671
|
+
.description("Register local-dev team user (NOT enterprise SSO)")
|
|
672
|
+
.requiredOption("--username <name>", "Username")
|
|
673
|
+
.requiredOption("--password <password>", "Password (min 8)")
|
|
674
|
+
.argument("[path]", "Repository path")
|
|
675
|
+
.option("--json", "Emit JSON", false)
|
|
676
|
+
.action(async (pathArg, _options, command) => {
|
|
677
|
+
const options = command.optsWithGlobals();
|
|
678
|
+
process.exitCode = await runV2SurfaceCommand({
|
|
679
|
+
action: "team-register",
|
|
680
|
+
root: resolveTargetArgument(pathArg),
|
|
681
|
+
username: options.username,
|
|
682
|
+
password: options.password,
|
|
683
|
+
json: Boolean(options.json),
|
|
684
|
+
});
|
|
685
|
+
});
|
|
686
|
+
program
|
|
687
|
+
.command("team-login")
|
|
688
|
+
.description("Authenticate local-dev team user (NOT enterprise SSO)")
|
|
689
|
+
.requiredOption("--username <name>", "Username")
|
|
690
|
+
.requiredOption("--password <password>", "Password")
|
|
691
|
+
.argument("[path]", "Repository path")
|
|
692
|
+
.option("--json", "Emit JSON", false)
|
|
693
|
+
.action(async (pathArg, _options, command) => {
|
|
694
|
+
const options = command.optsWithGlobals();
|
|
695
|
+
process.exitCode = await runV2SurfaceCommand({
|
|
696
|
+
action: "team-login",
|
|
697
|
+
root: resolveTargetArgument(pathArg),
|
|
698
|
+
username: options.username,
|
|
699
|
+
password: options.password,
|
|
700
|
+
json: Boolean(options.json),
|
|
701
|
+
});
|
|
702
|
+
});
|
|
703
|
+
const change = program
|
|
704
|
+
.command("change")
|
|
705
|
+
.description("Change assurance — structured assessment and evidence (never claims verified without evidence)");
|
|
706
|
+
change
|
|
707
|
+
.command("analyze")
|
|
708
|
+
.description("Assemble a ChangeAssessment from git, graph, policy, knowledge, and test signals")
|
|
709
|
+
.argument("[path]", "Repository path")
|
|
710
|
+
.option("--since <ref>", "Diff since git ref (e.g. main)")
|
|
711
|
+
.option("--coverage <path>", "LCOV / Istanbul JSON / Cobertura XML coverage file")
|
|
712
|
+
.option("--json", "Emit JSON", false)
|
|
713
|
+
.action(async (pathArg, options) => {
|
|
714
|
+
process.exitCode = await runChangeAnalyzeCommand({
|
|
715
|
+
root: resolveTargetArgument(pathArg),
|
|
716
|
+
...(options.since ? { since: options.since } : {}),
|
|
717
|
+
...(options.coverage ? { coverage: options.coverage } : {}),
|
|
718
|
+
json: Boolean(options.json),
|
|
719
|
+
});
|
|
720
|
+
});
|
|
721
|
+
change
|
|
722
|
+
.command("verify")
|
|
723
|
+
.description("Produce a durable evidence bundle under .agentdoctor/evidence/<id>/ (status: evidence-produced)")
|
|
724
|
+
.argument("[path]", "Repository path")
|
|
725
|
+
.option("--since <ref>", "Diff since git ref (e.g. main)")
|
|
726
|
+
.option("--change-id <id>", "Reuse a change id")
|
|
727
|
+
.option("--coverage <path>", "LCOV / Istanbul JSON / Cobertura XML coverage file")
|
|
728
|
+
.option("--json", "Emit JSON", false)
|
|
729
|
+
.action(async (pathArg, options) => {
|
|
730
|
+
process.exitCode = await runChangeVerifyCommand({
|
|
731
|
+
root: resolveTargetArgument(pathArg),
|
|
732
|
+
...(options.since ? { since: options.since } : {}),
|
|
733
|
+
...(options.changeId ? { changeId: options.changeId } : {}),
|
|
734
|
+
...(options.coverage ? { coverage: options.coverage } : {}),
|
|
735
|
+
json: Boolean(options.json),
|
|
736
|
+
});
|
|
737
|
+
});
|
|
738
|
+
change
|
|
739
|
+
.command("explain")
|
|
740
|
+
.description("Human explanation of a change assessment (does not claim correctness)")
|
|
741
|
+
.argument("[path]", "Repository path")
|
|
742
|
+
.option("--since <ref>", "Diff since git ref")
|
|
743
|
+
.option("--change-id <id>", "Change id")
|
|
744
|
+
.option("--coverage <path>", "LCOV / Istanbul JSON / Cobertura XML coverage file")
|
|
745
|
+
.option("--json", "Emit JSON", false)
|
|
746
|
+
.action(async (pathArg, options) => {
|
|
747
|
+
process.exitCode = await runChangeExplainCommand({
|
|
748
|
+
root: resolveTargetArgument(pathArg),
|
|
749
|
+
...(options.since ? { since: options.since } : {}),
|
|
750
|
+
...(options.changeId ? { changeId: options.changeId } : {}),
|
|
751
|
+
...(options.coverage ? { coverage: options.coverage } : {}),
|
|
752
|
+
json: Boolean(options.json),
|
|
753
|
+
});
|
|
754
|
+
});
|
|
755
|
+
change
|
|
756
|
+
.command("diff")
|
|
757
|
+
.description("Summarize base/target file diffs from git")
|
|
758
|
+
.argument("[path]", "Repository path")
|
|
759
|
+
.option("--since <ref>", "Diff since git ref")
|
|
760
|
+
.option("--change-id <id>", "Change id (label only)")
|
|
761
|
+
.option("--json", "Emit JSON", false)
|
|
762
|
+
.action(async (pathArg, options) => {
|
|
763
|
+
process.exitCode = await runChangeDiffCommand({
|
|
764
|
+
root: resolveTargetArgument(pathArg),
|
|
765
|
+
...(options.since ? { since: options.since } : {}),
|
|
766
|
+
...(options.changeId ? { changeId: options.changeId } : {}),
|
|
767
|
+
json: Boolean(options.json),
|
|
768
|
+
});
|
|
769
|
+
});
|
|
770
|
+
change
|
|
771
|
+
.command("status")
|
|
772
|
+
.description("Verification / evidence status for latest or --change-id")
|
|
773
|
+
.argument("[path]", "Repository path")
|
|
774
|
+
.option("--change-id <id>", "Change id")
|
|
775
|
+
.option("--json", "Emit JSON", false)
|
|
776
|
+
.action(async (pathArg, options) => {
|
|
777
|
+
process.exitCode = await runChangeStatusCommand({
|
|
778
|
+
root: resolveTargetArgument(pathArg),
|
|
779
|
+
...(options.changeId ? { changeId: options.changeId } : {}),
|
|
780
|
+
json: Boolean(options.json),
|
|
781
|
+
});
|
|
782
|
+
});
|
|
783
|
+
const evidence = program
|
|
784
|
+
.command("evidence")
|
|
785
|
+
.description("Inspect and hash-verify change evidence bundles");
|
|
786
|
+
evidence
|
|
787
|
+
.command("inspect")
|
|
788
|
+
.description("List evidence artifacts for a change id")
|
|
789
|
+
.argument("<changeId>", "Change id (chg_…)")
|
|
790
|
+
.argument("[path]", "Repository path")
|
|
791
|
+
.option("--json", "Emit JSON", false)
|
|
792
|
+
.action(async (changeId, pathArg, options) => {
|
|
793
|
+
process.exitCode = await runEvidenceInspectCommand({
|
|
794
|
+
root: resolveTargetArgument(pathArg),
|
|
795
|
+
changeId,
|
|
796
|
+
json: Boolean(options.json),
|
|
797
|
+
});
|
|
798
|
+
});
|
|
799
|
+
evidence
|
|
800
|
+
.command("verify")
|
|
801
|
+
.description("Hash-check evidence bundle; status verified only when all hashes match")
|
|
802
|
+
.argument("<changeId>", "Change id (chg_…)")
|
|
803
|
+
.argument("[path]", "Repository path")
|
|
804
|
+
.option("--json", "Emit JSON", false)
|
|
805
|
+
.action(async (changeId, pathArg, options) => {
|
|
806
|
+
process.exitCode = await runEvidenceVerifyCommand({
|
|
807
|
+
root: resolveTargetArgument(pathArg),
|
|
808
|
+
changeId,
|
|
809
|
+
json: Boolean(options.json),
|
|
810
|
+
});
|
|
811
|
+
});
|
|
812
|
+
const proof = program
|
|
813
|
+
.command("proof")
|
|
814
|
+
.description("ChangeProof — hash integrity over evidence (never claims engineering correctness)");
|
|
815
|
+
proof
|
|
816
|
+
.command("build")
|
|
817
|
+
.description("Build a ChangeProof from an evidence bundle")
|
|
818
|
+
.argument("<changeId>", "Change id (chg_…)")
|
|
819
|
+
.argument("[path]", "Repository path")
|
|
820
|
+
.option("--json", "Emit JSON", false)
|
|
821
|
+
.action(async (changeId, pathArg, options) => {
|
|
822
|
+
process.exitCode = await runProofBuildCommand({
|
|
823
|
+
root: resolveTargetArgument(pathArg),
|
|
824
|
+
changeId,
|
|
825
|
+
json: Boolean(options.json),
|
|
826
|
+
});
|
|
827
|
+
});
|
|
828
|
+
proof
|
|
829
|
+
.command("inspect")
|
|
830
|
+
.description("Inspect a proof by proofId or changeId")
|
|
831
|
+
.argument("<id>", "proofId or changeId")
|
|
832
|
+
.argument("[path]", "Repository path")
|
|
833
|
+
.option("--json", "Emit JSON", false)
|
|
834
|
+
.action(async (id, pathArg, options) => {
|
|
835
|
+
process.exitCode = await runProofInspectCommand({
|
|
836
|
+
root: resolveTargetArgument(pathArg),
|
|
837
|
+
id,
|
|
838
|
+
json: Boolean(options.json),
|
|
839
|
+
});
|
|
840
|
+
});
|
|
841
|
+
proof
|
|
842
|
+
.command("explain")
|
|
843
|
+
.description("Human explanation of a ChangeProof (does not claim correctness)")
|
|
844
|
+
.argument("<id>", "proofId or changeId")
|
|
845
|
+
.argument("[path]", "Repository path")
|
|
846
|
+
.option("--json", "Emit JSON", false)
|
|
847
|
+
.action(async (id, pathArg, options) => {
|
|
848
|
+
process.exitCode = await runProofExplainCommand({
|
|
849
|
+
root: resolveTargetArgument(pathArg),
|
|
850
|
+
id,
|
|
851
|
+
json: Boolean(options.json),
|
|
852
|
+
});
|
|
853
|
+
});
|
|
854
|
+
proof
|
|
855
|
+
.command("verify")
|
|
856
|
+
.description("Re-hash evidence and compare to proof (integrity only)")
|
|
857
|
+
.argument("<id>", "proofId or changeId")
|
|
858
|
+
.argument("[path]", "Repository path")
|
|
859
|
+
.option("--json", "Emit JSON", false)
|
|
860
|
+
.action(async (id, pathArg, options) => {
|
|
861
|
+
process.exitCode = await runProofVerifyCommand({
|
|
862
|
+
root: resolveTargetArgument(pathArg),
|
|
863
|
+
id,
|
|
864
|
+
json: Boolean(options.json),
|
|
865
|
+
});
|
|
866
|
+
});
|
|
867
|
+
proof
|
|
868
|
+
.command("export")
|
|
869
|
+
.description("Export a proof JSON file")
|
|
870
|
+
.argument("<id>", "proofId or changeId")
|
|
871
|
+
.requiredOption("--out <file>", "Output file path")
|
|
872
|
+
.argument("[path]", "Repository path")
|
|
873
|
+
.option("--json", "Emit JSON", false)
|
|
874
|
+
.action(async (id, pathArg, options) => {
|
|
875
|
+
process.exitCode = await runProofExportCommand({
|
|
876
|
+
root: resolveTargetArgument(pathArg),
|
|
877
|
+
id,
|
|
878
|
+
out: options.out,
|
|
879
|
+
json: Boolean(options.json),
|
|
880
|
+
});
|
|
881
|
+
});
|
|
882
|
+
const policy = program
|
|
883
|
+
.command("policy")
|
|
884
|
+
.description("Action policy check / explain / enforce (controlled runner)");
|
|
885
|
+
policy
|
|
886
|
+
.command("check")
|
|
887
|
+
.description("Evaluate a command against local policy (evaluate-only)")
|
|
888
|
+
.requiredOption("--command <cmd>", "Command string")
|
|
889
|
+
.argument("[path]", "Repository path")
|
|
890
|
+
.option("--fail-closed", "Deny on invalid local policy", false)
|
|
891
|
+
.option("--json", "Emit JSON", false)
|
|
892
|
+
.action(async (pathArg, options) => {
|
|
893
|
+
process.exitCode = await runPolicyCheckCommand({
|
|
894
|
+
root: resolveTargetArgument(pathArg),
|
|
895
|
+
command: options.command,
|
|
896
|
+
failClosed: Boolean(options.failClosed),
|
|
897
|
+
json: Boolean(options.json),
|
|
898
|
+
});
|
|
899
|
+
});
|
|
900
|
+
policy
|
|
901
|
+
.command("explain")
|
|
902
|
+
.description("Explain why a command receives its policy decision")
|
|
903
|
+
.requiredOption("--command <cmd>", "Command string")
|
|
904
|
+
.argument("[path]", "Repository path")
|
|
905
|
+
.option("--fail-closed", "Deny on invalid local policy", false)
|
|
906
|
+
.option("--json", "Emit JSON", false)
|
|
907
|
+
.action(async (pathArg, options) => {
|
|
908
|
+
process.exitCode = await runPolicyExplainCommand({
|
|
909
|
+
root: resolveTargetArgument(pathArg),
|
|
910
|
+
command: options.command,
|
|
911
|
+
failClosed: Boolean(options.failClosed),
|
|
912
|
+
json: Boolean(options.json),
|
|
913
|
+
});
|
|
914
|
+
});
|
|
915
|
+
policy
|
|
916
|
+
.command("enforce")
|
|
917
|
+
.description("Evaluate policy; optionally execute when --execute and decision=allow")
|
|
918
|
+
.requiredOption("--command <cmd>", "Command string")
|
|
919
|
+
.option("--execute", "Execute via controlled runner when allowed", false)
|
|
920
|
+
.argument("[path]", "Repository path")
|
|
921
|
+
.option("--fail-closed", "Deny on invalid local policy", false)
|
|
922
|
+
.option("--json", "Emit JSON", false)
|
|
923
|
+
.action(async (pathArg, options) => {
|
|
924
|
+
process.exitCode = await runPolicyEnforceCommand({
|
|
925
|
+
root: resolveTargetArgument(pathArg),
|
|
926
|
+
command: options.command,
|
|
927
|
+
execute: Boolean(options.execute),
|
|
928
|
+
failClosed: Boolean(options.failClosed),
|
|
929
|
+
json: Boolean(options.json),
|
|
930
|
+
});
|
|
931
|
+
});
|
|
932
|
+
const runCmd = program
|
|
933
|
+
.command("run")
|
|
934
|
+
.description("Controlled execution (shell=false by default). Prefer: agentdoctor run -- <cmd> <args...>");
|
|
935
|
+
runCmd
|
|
936
|
+
.command("explain")
|
|
937
|
+
.description("Explain controlled-runner decision for a command (evaluate-only, never executes)")
|
|
938
|
+
.requiredOption("--command <cmd>", "Command string")
|
|
939
|
+
.argument("[path]", "Repository path")
|
|
940
|
+
.option("--shell", "Explain as if shell=true were requested", false)
|
|
941
|
+
.option("--fail-closed", "Deny on invalid local policy", false)
|
|
942
|
+
.option("--json", "Emit JSON", false)
|
|
943
|
+
.action(async (pathArg, options) => {
|
|
944
|
+
process.exitCode = await runRunExplainCommand({
|
|
945
|
+
root: resolveTargetArgument(pathArg),
|
|
946
|
+
command: options.command,
|
|
947
|
+
shell: Boolean(options.shell),
|
|
948
|
+
failClosed: Boolean(options.failClosed),
|
|
949
|
+
json: Boolean(options.json),
|
|
950
|
+
});
|
|
951
|
+
});
|
|
952
|
+
runCmd
|
|
953
|
+
.option("--shell", "HIGH RISK: allow shell interpretation (still requires policy allow)", false)
|
|
954
|
+
.option("--timeout <ms>", "Kill after timeout milliseconds", "30000")
|
|
955
|
+
.option("--fail-closed", "Deny on invalid local policy", false)
|
|
956
|
+
.option("--json", "Emit JSON", false)
|
|
957
|
+
.argument("[path]", "Repository path (ignored when -- separates args; use cwd)")
|
|
958
|
+
.argument("[command...]", "Command argv after --")
|
|
959
|
+
.allowUnknownOption(false)
|
|
960
|
+
.action(async (pathArg, commandParts, cmd) => {
|
|
961
|
+
const options = cmd.opts();
|
|
962
|
+
// Commander may put path as first arg when no `--`; prefer argv after `--` via raw args.
|
|
963
|
+
const raw = cmd.args;
|
|
964
|
+
let root = process.cwd();
|
|
965
|
+
let argv = [];
|
|
966
|
+
// If first token looks like a path-only invocation without command, treat as root.
|
|
967
|
+
if (commandParts && commandParts.length > 0) {
|
|
968
|
+
argv = commandParts;
|
|
969
|
+
if (pathArg)
|
|
970
|
+
root = resolveTargetArgument(pathArg);
|
|
971
|
+
}
|
|
972
|
+
else if (pathArg && !pathArg.startsWith("-")) {
|
|
973
|
+
// `run npm --version` style: pathArg is actually first command token
|
|
974
|
+
argv = [pathArg, ...(raw.slice(1) ?? [])];
|
|
975
|
+
}
|
|
976
|
+
// Prefer explicit `--` remainder from process.argv
|
|
977
|
+
const dd = process.argv.indexOf("--");
|
|
978
|
+
if (dd >= 0) {
|
|
979
|
+
argv = process.argv.slice(dd + 1);
|
|
980
|
+
root = process.cwd();
|
|
981
|
+
}
|
|
982
|
+
const timeoutMs = Number(options.timeout ?? 30000);
|
|
983
|
+
process.exitCode = await runRunCommand({
|
|
984
|
+
root: resolveTargetArgument(root),
|
|
985
|
+
argv,
|
|
986
|
+
shell: Boolean(options.shell),
|
|
987
|
+
timeoutMs: Number.isFinite(timeoutMs) ? timeoutMs : 30000,
|
|
988
|
+
failClosed: Boolean(options.failClosed),
|
|
989
|
+
json: Boolean(options.json),
|
|
990
|
+
});
|
|
991
|
+
});
|
|
992
|
+
const workspace = program
|
|
993
|
+
.command("workspace")
|
|
994
|
+
.description("Multi-repo workspace model under .agentdoctor/workspaces/ (local isolation)");
|
|
995
|
+
workspace
|
|
996
|
+
.command("init")
|
|
997
|
+
.description("Create a workspace JSON record")
|
|
998
|
+
.requiredOption("--name <name>", "Workspace display name")
|
|
999
|
+
.option("--id <id>", "Optional workspace id")
|
|
1000
|
+
.option("--repo <path>", "Initial repository root to add")
|
|
1001
|
+
.option("--allow-cross-read", "Allow reading other member repos (default false)", false)
|
|
1002
|
+
.argument("[path]", "Control root (stores .agentdoctor/workspaces)")
|
|
1003
|
+
.option("--json", "Emit JSON", false)
|
|
1004
|
+
.action(async (pathArg, options) => {
|
|
1005
|
+
process.exitCode = await runWorkspaceCommand({
|
|
1006
|
+
action: "init",
|
|
1007
|
+
root: resolveTargetArgument(pathArg),
|
|
1008
|
+
name: options.name,
|
|
1009
|
+
...(options.id ? { id: options.id } : {}),
|
|
1010
|
+
...(options.repo ? { repositoryRoot: resolveTargetArgument(options.repo) } : {}),
|
|
1011
|
+
allowCrossRead: Boolean(options.allowCrossRead),
|
|
1012
|
+
json: Boolean(options.json),
|
|
1013
|
+
});
|
|
1014
|
+
});
|
|
1015
|
+
workspace
|
|
1016
|
+
.command("add")
|
|
1017
|
+
.description("Add a repository root to a workspace")
|
|
1018
|
+
.requiredOption("--id <id>", "Workspace id")
|
|
1019
|
+
.requiredOption("--repo <path>", "Repository root to add")
|
|
1020
|
+
.argument("[path]", "Control root")
|
|
1021
|
+
.option("--json", "Emit JSON", false)
|
|
1022
|
+
.action(async (pathArg, options) => {
|
|
1023
|
+
process.exitCode = await runWorkspaceCommand({
|
|
1024
|
+
action: "add",
|
|
1025
|
+
root: resolveTargetArgument(pathArg),
|
|
1026
|
+
id: options.id,
|
|
1027
|
+
repositoryRoot: resolveTargetArgument(options.repo),
|
|
1028
|
+
json: Boolean(options.json),
|
|
1029
|
+
});
|
|
1030
|
+
});
|
|
1031
|
+
workspace
|
|
1032
|
+
.command("list")
|
|
1033
|
+
.description("List workspaces")
|
|
1034
|
+
.argument("[path]", "Control root")
|
|
1035
|
+
.option("--json", "Emit JSON", false)
|
|
1036
|
+
.action(async (pathArg, options) => {
|
|
1037
|
+
process.exitCode = await runWorkspaceCommand({
|
|
1038
|
+
action: "list",
|
|
1039
|
+
root: resolveTargetArgument(pathArg),
|
|
1040
|
+
json: Boolean(options.json),
|
|
1041
|
+
});
|
|
1042
|
+
});
|
|
1043
|
+
workspace
|
|
1044
|
+
.command("status")
|
|
1045
|
+
.description("Show workspace membership and missing roots")
|
|
1046
|
+
.requiredOption("--id <id>", "Workspace id")
|
|
1047
|
+
.argument("[path]", "Control root")
|
|
1048
|
+
.option("--json", "Emit JSON", false)
|
|
1049
|
+
.action(async (pathArg, options) => {
|
|
1050
|
+
process.exitCode = await runWorkspaceCommand({
|
|
1051
|
+
action: "status",
|
|
1052
|
+
root: resolveTargetArgument(pathArg),
|
|
1053
|
+
id: options.id,
|
|
1054
|
+
json: Boolean(options.json),
|
|
1055
|
+
});
|
|
1056
|
+
});
|
|
1057
|
+
workspace
|
|
1058
|
+
.command("remove")
|
|
1059
|
+
.description("Delete a workspace record")
|
|
1060
|
+
.requiredOption("--id <id>", "Workspace id")
|
|
1061
|
+
.argument("[path]", "Control root")
|
|
1062
|
+
.option("--json", "Emit JSON", false)
|
|
1063
|
+
.action(async (pathArg, options) => {
|
|
1064
|
+
process.exitCode = await runWorkspaceCommand({
|
|
1065
|
+
action: "remove",
|
|
1066
|
+
root: resolveTargetArgument(pathArg),
|
|
1067
|
+
id: options.id,
|
|
1068
|
+
json: Boolean(options.json),
|
|
1069
|
+
});
|
|
1070
|
+
});
|
|
1071
|
+
program
|
|
1072
|
+
.command("changes")
|
|
1073
|
+
.description("Analyze git working-tree / range changes")
|
|
1074
|
+
.argument("[path]", "Repository path")
|
|
1075
|
+
.option("--since <ref>", "Diff since git ref (e.g. main)")
|
|
1076
|
+
.option("--impact", "Compute dependency impact from Project Brain (UNKNOWN when edges are missing)", false)
|
|
1077
|
+
.option("--json", "Emit JSON", false)
|
|
1078
|
+
.action(async (pathArg, options) => {
|
|
1079
|
+
process.exitCode = await runChangesCommand({
|
|
1080
|
+
root: resolveTargetArgument(pathArg),
|
|
1081
|
+
...(options.since ? { since: options.since } : {}),
|
|
1082
|
+
impact: Boolean(options.impact),
|
|
1083
|
+
json: Boolean(options.json),
|
|
1084
|
+
});
|
|
1085
|
+
});
|
|
1086
|
+
program
|
|
1087
|
+
.command("context-health")
|
|
1088
|
+
.description("Detect instruction / ignore surface conflicts")
|
|
1089
|
+
.argument("[path]", "Repository path")
|
|
1090
|
+
.option("--json", "Emit JSON", false)
|
|
1091
|
+
.action(async (pathArg, options) => {
|
|
1092
|
+
process.exitCode = await runContextHealthCommand({
|
|
1093
|
+
root: resolveTargetArgument(pathArg),
|
|
1094
|
+
json: Boolean(options.json),
|
|
1095
|
+
});
|
|
1096
|
+
});
|
|
1097
|
+
program
|
|
1098
|
+
.command("secrets")
|
|
1099
|
+
.description("Opt-in content secret scan (findings are always redacted)")
|
|
1100
|
+
.argument("[path]", "Repository path")
|
|
1101
|
+
.option("--json", "Emit JSON", false)
|
|
1102
|
+
.action(async (pathArg, options) => {
|
|
1103
|
+
process.exitCode = await runSecretsCommand({
|
|
1104
|
+
root: resolveTargetArgument(pathArg),
|
|
1105
|
+
json: Boolean(options.json),
|
|
1106
|
+
});
|
|
1107
|
+
});
|
|
1108
|
+
const baseline = program.command("baseline").description("Named scan baselines");
|
|
1109
|
+
baseline
|
|
1110
|
+
.command("save")
|
|
1111
|
+
.description("Save current scan as a named baseline")
|
|
1112
|
+
.requiredOption("--name <name>", "Baseline name")
|
|
1113
|
+
.argument("[path]", "Repository path")
|
|
1114
|
+
.action(async (pathArg, options) => {
|
|
1115
|
+
process.exitCode = await runBaselineCommand({
|
|
1116
|
+
action: "save",
|
|
1117
|
+
name: options.name,
|
|
1118
|
+
root: resolveTargetArgument(pathArg),
|
|
1119
|
+
});
|
|
1120
|
+
});
|
|
1121
|
+
baseline
|
|
1122
|
+
.command("list")
|
|
1123
|
+
.description("List named baselines")
|
|
1124
|
+
.argument("[path]", "Repository path")
|
|
1125
|
+
.option("--json", "Emit JSON", false)
|
|
1126
|
+
.action(async (pathArg, options) => {
|
|
1127
|
+
process.exitCode = await runBaselineCommand({
|
|
1128
|
+
action: "list",
|
|
1129
|
+
root: resolveTargetArgument(pathArg),
|
|
1130
|
+
json: Boolean(options.json),
|
|
1131
|
+
});
|
|
1132
|
+
});
|
|
1133
|
+
baseline
|
|
1134
|
+
.command("diff")
|
|
1135
|
+
.description("Compare current scan to a named baseline")
|
|
1136
|
+
.requiredOption("--name <name>", "Baseline name")
|
|
1137
|
+
.argument("[path]", "Repository path")
|
|
1138
|
+
.option("--json", "Emit JSON", false)
|
|
1139
|
+
.action(async (pathArg, options) => {
|
|
1140
|
+
process.exitCode = await runBaselineCommand({
|
|
1141
|
+
action: "diff",
|
|
1142
|
+
name: options.name,
|
|
1143
|
+
root: resolveTargetArgument(pathArg),
|
|
1144
|
+
json: Boolean(options.json),
|
|
1145
|
+
});
|
|
1146
|
+
});
|
|
1147
|
+
baseline
|
|
1148
|
+
.command("delete")
|
|
1149
|
+
.description("Delete a named baseline (fails if missing)")
|
|
1150
|
+
.requiredOption("--name <name>", "Baseline name")
|
|
1151
|
+
.argument("[path]", "Repository path")
|
|
1152
|
+
.action(async (pathArg, options) => {
|
|
1153
|
+
process.exitCode = await runBaselineCommand({
|
|
1154
|
+
action: "delete",
|
|
1155
|
+
name: options.name,
|
|
1156
|
+
root: resolveTargetArgument(pathArg),
|
|
1157
|
+
});
|
|
1158
|
+
});
|
|
1159
|
+
baseline
|
|
1160
|
+
.command("trends")
|
|
1161
|
+
.description("Show baseline count trends over time (no causality claimed)")
|
|
1162
|
+
.argument("[path]", "Repository path")
|
|
1163
|
+
.option("--json", "Emit JSON", false)
|
|
1164
|
+
.action(async (pathArg, options) => {
|
|
1165
|
+
process.exitCode = await runBaselineCommand({
|
|
1166
|
+
action: "trends",
|
|
1167
|
+
root: resolveTargetArgument(pathArg),
|
|
1168
|
+
json: Boolean(options.json),
|
|
1169
|
+
});
|
|
1170
|
+
});
|
|
1171
|
+
program
|
|
1172
|
+
.command("packages")
|
|
1173
|
+
.description("Detect workspace packages / optional per-package scan")
|
|
1174
|
+
.argument("[path]", "Repository path")
|
|
1175
|
+
.option("--scan", "Run a scan per package", false)
|
|
1176
|
+
.option("--json", "Emit JSON", false)
|
|
1177
|
+
.action(async (pathArg, options) => {
|
|
1178
|
+
process.exitCode = await runPackagesCommand({
|
|
1179
|
+
root: resolveTargetArgument(pathArg),
|
|
1180
|
+
scan: Boolean(options.scan),
|
|
1181
|
+
json: Boolean(options.json),
|
|
1182
|
+
});
|
|
1183
|
+
});
|
|
1184
|
+
program
|
|
1185
|
+
.command("pr-review")
|
|
1186
|
+
.description("Local PR review dry-run (never posts to GitHub)")
|
|
1187
|
+
.argument("[path]", "Repository path")
|
|
1188
|
+
.option("--base <ref>", "Base git ref")
|
|
1189
|
+
.option("--json", "Emit JSON", false)
|
|
1190
|
+
.action(async (pathArg, options) => {
|
|
1191
|
+
process.exitCode = await runPrReviewCommand({
|
|
1192
|
+
root: resolveTargetArgument(pathArg),
|
|
1193
|
+
...(options.base ? { base: options.base } : {}),
|
|
1194
|
+
json: Boolean(options.json),
|
|
1195
|
+
});
|
|
1196
|
+
});
|
|
1197
|
+
program
|
|
1198
|
+
.command("dashboard")
|
|
1199
|
+
.description("Start local read-only dashboard (loopback only by default)")
|
|
1200
|
+
.argument("[path]", "Repository path")
|
|
1201
|
+
.option("--host <host>", "Bind host (default 127.0.0.1)", "127.0.0.1")
|
|
1202
|
+
.option("--port <port>", "Bind port", "8787")
|
|
1203
|
+
.option("--allow-non-loopback", "Unsafe: allow binding outside 127.0.0.1/::1/localhost (not an enterprise security boundary)", false)
|
|
1204
|
+
.action(async (pathArg, options) => {
|
|
1205
|
+
process.exitCode = await runDashboardCommand({
|
|
1206
|
+
root: resolveTargetArgument(pathArg),
|
|
1207
|
+
host: options.host ?? "127.0.0.1",
|
|
1208
|
+
port: Number(options.port ?? 8787),
|
|
1209
|
+
allowNonLoopback: Boolean(options.allowNonLoopback),
|
|
1210
|
+
});
|
|
1211
|
+
});
|
|
1212
|
+
program
|
|
1213
|
+
.command("plugins")
|
|
1214
|
+
.description("List / validate local plugins under .agentdoctor/plugins")
|
|
1215
|
+
.argument("[path]", "Repository path")
|
|
1216
|
+
.option("--run", "Execute analyzer plugin hooks (isolated; timeouts apply)", false)
|
|
1217
|
+
.option("--json", "Emit JSON", false)
|
|
1218
|
+
.action(async (pathArg, options) => {
|
|
1219
|
+
process.exitCode = await runPluginsCommand({
|
|
1220
|
+
root: resolveTargetArgument(pathArg),
|
|
1221
|
+
json: Boolean(options.json),
|
|
1222
|
+
run: Boolean(options.run),
|
|
1223
|
+
});
|
|
1224
|
+
});
|
|
1225
|
+
program
|
|
1226
|
+
.command("local-ai")
|
|
1227
|
+
.description("Optional local AI provider probe (default: none; core stays deterministic)")
|
|
1228
|
+
.option("--provider <id>", "none | mock | ollama", "none")
|
|
1229
|
+
.option("--prompt <text>", "Prompt text")
|
|
1230
|
+
.option("--json", "Emit JSON", false)
|
|
1231
|
+
.action(async (options) => {
|
|
1232
|
+
process.exitCode = await runLocalAiCommand({
|
|
1233
|
+
...(options.provider ? { provider: options.provider } : {}),
|
|
1234
|
+
...(options.prompt ? { prompt: options.prompt } : {}),
|
|
1235
|
+
json: Boolean(options.json),
|
|
1236
|
+
});
|
|
1237
|
+
});
|
|
1238
|
+
const platform = program
|
|
1239
|
+
.command("platform")
|
|
1240
|
+
.description("AgentDoctor 2.0 local platform (intelligence, Action Policy Evaluator, sessions, reports)");
|
|
1241
|
+
platform
|
|
1242
|
+
.command("scan")
|
|
1243
|
+
.description("Run integrated platform scan and write local reports")
|
|
1244
|
+
.argument("[path]", "Repository path")
|
|
1245
|
+
.option("--json", "Emit JSON", false)
|
|
1246
|
+
.action(async (pathArg, _options, command) => {
|
|
1247
|
+
const options = command.optsWithGlobals();
|
|
1248
|
+
process.exitCode = await runPlatformCommand({
|
|
1249
|
+
action: "scan",
|
|
1250
|
+
root: resolveTargetArgument(pathArg),
|
|
1251
|
+
json: Boolean(options.json),
|
|
1252
|
+
});
|
|
1253
|
+
});
|
|
1254
|
+
platform
|
|
1255
|
+
.command("graph")
|
|
1256
|
+
.description("Build repository intelligence graph")
|
|
1257
|
+
.argument("[path]", "Repository path")
|
|
1258
|
+
.option("--json", "Emit JSON", false)
|
|
1259
|
+
.action(async (pathArg, _options, command) => {
|
|
1260
|
+
const options = command.optsWithGlobals();
|
|
1261
|
+
process.exitCode = await runPlatformCommand({
|
|
1262
|
+
action: "graph",
|
|
1263
|
+
root: resolveTargetArgument(pathArg),
|
|
1264
|
+
json: Boolean(options.json),
|
|
1265
|
+
});
|
|
1266
|
+
});
|
|
1267
|
+
platform
|
|
1268
|
+
.command("test-impact")
|
|
1269
|
+
.description("Analyze which tests relate to git changes (heuristic; coverage-backed with --coverage; does not run tests)")
|
|
1270
|
+
.argument("[path]", "Repository path")
|
|
1271
|
+
.option("--coverage <path>", "LCOV / Istanbul JSON / Cobertura XML coverage file")
|
|
1272
|
+
.option("--since <ref>", "Diff since git ref")
|
|
1273
|
+
.option("--json", "Emit JSON", false)
|
|
1274
|
+
.action(async (pathArg, _options, command) => {
|
|
1275
|
+
const options = command.optsWithGlobals();
|
|
1276
|
+
process.exitCode = await runPlatformCommand({
|
|
1277
|
+
action: "test-impact",
|
|
1278
|
+
root: resolveTargetArgument(pathArg),
|
|
1279
|
+
json: Boolean(options.json),
|
|
1280
|
+
...(options.coverage ? { coverage: options.coverage } : {}),
|
|
1281
|
+
...(options.since ? { since: options.since } : {}),
|
|
1282
|
+
});
|
|
1283
|
+
});
|
|
1284
|
+
const registerPolicyCheck = (name, aliasNote) => {
|
|
1285
|
+
platform
|
|
1286
|
+
.command(name)
|
|
1287
|
+
.description(`Action Policy Evaluator${aliasNote}: evaluate an agent action (never executes; no agent interception)`)
|
|
1288
|
+
.argument("[path]", "Repository path")
|
|
1289
|
+
.option("--type <type>", "Action type", "shell")
|
|
1290
|
+
.option("--command <cmd>", "Shell command string")
|
|
1291
|
+
.option("--path <file>", "Target path")
|
|
1292
|
+
.option("--fail-closed", "If local policy JSON is invalid, deny the action instead of falling back to defaults", false)
|
|
1293
|
+
.option("--json", "Emit JSON", false)
|
|
1294
|
+
.action(async (pathArg, _options, cmd) => {
|
|
1295
|
+
const options = cmd.optsWithGlobals();
|
|
1296
|
+
process.exitCode = await runPlatformCommand({
|
|
1297
|
+
action: "policy-check",
|
|
1298
|
+
root: resolveTargetArgument(pathArg),
|
|
1299
|
+
...(options.type ? { actionType: options.type } : {}),
|
|
1300
|
+
...(options.command ? { command: options.command } : {}),
|
|
1301
|
+
...(options.path ? { path: options.path } : {}),
|
|
1302
|
+
json: Boolean(options.json),
|
|
1303
|
+
failClosed: Boolean(options.failClosed),
|
|
1304
|
+
});
|
|
1305
|
+
});
|
|
1306
|
+
};
|
|
1307
|
+
registerPolicyCheck("policy-check", "");
|
|
1308
|
+
registerPolicyCheck("firewall-check", " (alias)");
|
|
1309
|
+
platform
|
|
1310
|
+
.command("session-list")
|
|
1311
|
+
.description("List recorded agent sessions")
|
|
1312
|
+
.argument("[path]", "Repository path")
|
|
1313
|
+
.option("--json", "Emit JSON", false)
|
|
1314
|
+
.action(async (pathArg, _options, command) => {
|
|
1315
|
+
const options = command.optsWithGlobals();
|
|
1316
|
+
process.exitCode = await runPlatformCommand({
|
|
1317
|
+
action: "session-list",
|
|
1318
|
+
root: resolveTargetArgument(pathArg),
|
|
1319
|
+
json: Boolean(options.json),
|
|
1320
|
+
});
|
|
1321
|
+
});
|
|
1322
|
+
platform
|
|
1323
|
+
.command("session-show")
|
|
1324
|
+
.description("Show / export an agent session")
|
|
1325
|
+
.requiredOption("--session <id>", "Session id")
|
|
1326
|
+
.argument("[path]", "Repository path")
|
|
1327
|
+
.option("--json", "Emit JSON", false)
|
|
1328
|
+
.action(async (pathArg, _options, command) => {
|
|
1329
|
+
const options = command.optsWithGlobals();
|
|
1330
|
+
process.exitCode = await runPlatformCommand({
|
|
1331
|
+
action: "session-show",
|
|
1332
|
+
sessionId: options.session,
|
|
1333
|
+
root: resolveTargetArgument(pathArg),
|
|
1334
|
+
json: Boolean(options.json),
|
|
1335
|
+
});
|
|
1336
|
+
});
|
|
1337
|
+
platform
|
|
1338
|
+
.command("demo-session")
|
|
1339
|
+
.description("Record a demo session with Action Policy Evaluator (evaluate-only; no command execution)")
|
|
1340
|
+
.argument("[path]", "Repository path")
|
|
1341
|
+
.option("--json", "Emit JSON", false)
|
|
1342
|
+
.action(async (pathArg, _options, command) => {
|
|
1343
|
+
const options = command.optsWithGlobals();
|
|
1344
|
+
process.exitCode = await runPlatformCommand({
|
|
1345
|
+
action: "demo-session",
|
|
1346
|
+
root: resolveTargetArgument(pathArg),
|
|
1347
|
+
json: Boolean(options.json),
|
|
1348
|
+
});
|
|
1349
|
+
});
|
|
1350
|
+
platform
|
|
1351
|
+
.command("provenance")
|
|
1352
|
+
.description("Build a provenance record for a file (unknown fields marked)")
|
|
1353
|
+
.requiredOption("--path <file>", "File path")
|
|
1354
|
+
.argument("[repo]", "Repository path")
|
|
1355
|
+
.option("--json", "Emit JSON", false)
|
|
1356
|
+
.action(async (repo, _options, command) => {
|
|
1357
|
+
const options = command.optsWithGlobals();
|
|
1358
|
+
process.exitCode = await runPlatformCommand({
|
|
1359
|
+
action: "provenance",
|
|
1360
|
+
path: options.path,
|
|
1361
|
+
root: resolveTargetArgument(repo),
|
|
1362
|
+
json: Boolean(options.json),
|
|
1363
|
+
});
|
|
1364
|
+
});
|
|
1365
|
+
platform
|
|
1366
|
+
.command("context")
|
|
1367
|
+
.description("Plan token-optimized context for a query")
|
|
1368
|
+
.option("--query <text>", "Query", "src")
|
|
1369
|
+
.argument("[path]", "Repository path")
|
|
1370
|
+
.option("--json", "Emit JSON", false)
|
|
1371
|
+
.action(async (pathArg, _options, command) => {
|
|
1372
|
+
const options = command.optsWithGlobals();
|
|
1373
|
+
process.exitCode = await runPlatformCommand({
|
|
1374
|
+
action: "context",
|
|
1375
|
+
query: options.query ?? "src",
|
|
1376
|
+
root: resolveTargetArgument(pathArg),
|
|
1377
|
+
json: Boolean(options.json),
|
|
1378
|
+
});
|
|
1379
|
+
});
|
|
1380
|
+
platform
|
|
1381
|
+
.command("refactor")
|
|
1382
|
+
.description("Analyze rename impact for a symbol (does not apply edits)")
|
|
1383
|
+
.requiredOption("--symbol <name>", "Symbol name")
|
|
1384
|
+
.argument("[path]", "Repository path")
|
|
1385
|
+
.option("--json", "Emit JSON", false)
|
|
1386
|
+
.action(async (pathArg, _options, command) => {
|
|
1387
|
+
const options = command.optsWithGlobals();
|
|
1388
|
+
process.exitCode = await runPlatformCommand({
|
|
1389
|
+
action: "refactor",
|
|
1390
|
+
symbol: options.symbol,
|
|
1391
|
+
root: resolveTargetArgument(pathArg),
|
|
1392
|
+
json: Boolean(options.json),
|
|
1393
|
+
});
|
|
1394
|
+
});
|
|
1395
|
+
platform
|
|
1396
|
+
.command("time-machine")
|
|
1397
|
+
.description("Compare two git refs")
|
|
1398
|
+
.requiredOption("--left <ref>", "Left ref")
|
|
1399
|
+
.requiredOption("--right <ref>", "Right ref")
|
|
1400
|
+
.argument("[path]", "Repository path")
|
|
1401
|
+
.option("--json", "Emit JSON", false)
|
|
1402
|
+
.action(async (pathArg, _options, command) => {
|
|
1403
|
+
const options = command.optsWithGlobals();
|
|
1404
|
+
process.exitCode = await runPlatformCommand({
|
|
1405
|
+
action: "time-machine",
|
|
1406
|
+
left: options.left,
|
|
1407
|
+
right: options.right,
|
|
1408
|
+
root: resolveTargetArgument(pathArg),
|
|
1409
|
+
json: Boolean(options.json),
|
|
1410
|
+
});
|
|
1411
|
+
});
|
|
108
1412
|
program
|
|
109
1413
|
.command("verify")
|
|
110
1414
|
.description("Re-scan and compare against a prior scan JSON baseline (Scan → Fix → Verify)")
|
|
@@ -150,7 +1454,16 @@ export function createProgram() {
|
|
|
150
1454
|
program
|
|
151
1455
|
.command("doctor")
|
|
152
1456
|
.description("Check AgentDoctor installation health")
|
|
153
|
-
.
|
|
1457
|
+
.option("--json", "Emit JSON ops health", false)
|
|
1458
|
+
.argument("[path]", "Repository path")
|
|
1459
|
+
.action(async (pathArg, _options, command) => {
|
|
1460
|
+
const options = command.optsWithGlobals();
|
|
1461
|
+
if (options.json) {
|
|
1462
|
+
const health = await collectOpsHealth(resolveTargetArgument(pathArg));
|
|
1463
|
+
process.stdout.write(`${JSON.stringify(health, null, 2)}\n`);
|
|
1464
|
+
process.exitCode = health.ok ? EXIT_CODES.SUCCESS : EXIT_CODES.INTERNAL_ERROR;
|
|
1465
|
+
return;
|
|
1466
|
+
}
|
|
154
1467
|
const code = await runDoctorCommand();
|
|
155
1468
|
process.exitCode = code;
|
|
156
1469
|
});
|
|
@@ -166,6 +1479,18 @@ export function createProgram() {
|
|
|
166
1479
|
});
|
|
167
1480
|
process.exitCode = code;
|
|
168
1481
|
});
|
|
1482
|
+
program
|
|
1483
|
+
.command("mcp")
|
|
1484
|
+
.description("Start combined AgentDoctor MCP (Brain tools + intelligence tools) over STDIO")
|
|
1485
|
+
.requiredOption("--root <path>", "Absolute or relative project root (required; never uses process.cwd() implicitly)")
|
|
1486
|
+
.option("--no-build-if-missing", "Fail when no Brain snapshot exists instead of compiling")
|
|
1487
|
+
.action(async (options) => {
|
|
1488
|
+
const code = await runMcpCommand({
|
|
1489
|
+
root: options.root,
|
|
1490
|
+
buildIfMissing: options.buildIfMissing !== false,
|
|
1491
|
+
});
|
|
1492
|
+
process.exitCode = code;
|
|
1493
|
+
});
|
|
169
1494
|
program.configureOutput({
|
|
170
1495
|
outputError: (str, write) => write(str),
|
|
171
1496
|
});
|