@praneeth_54/agentdoctor 1.1.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (170) hide show
  1. package/CHANGELOG.md +119 -5
  2. package/README.md +165 -332
  3. package/dist/agents/aider/adapter.d.ts +1 -0
  4. package/dist/agents/aider/adapter.js +1 -0
  5. package/dist/agents/aider/detector.d.ts +14 -0
  6. package/dist/agents/aider/detector.js +120 -0
  7. package/dist/agents/copilot/adapter.d.ts +1 -0
  8. package/dist/agents/copilot/adapter.js +1 -0
  9. package/dist/agents/copilot/detector.d.ts +13 -0
  10. package/dist/agents/copilot/detector.js +113 -0
  11. package/dist/agents/gemini/adapter.d.ts +1 -0
  12. package/dist/agents/gemini/adapter.js +1 -0
  13. package/dist/agents/gemini/detector.d.ts +13 -0
  14. package/dist/agents/gemini/detector.js +151 -0
  15. package/dist/agents/registry.js +13 -1
  16. package/dist/agents/types.d.ts +1 -1
  17. package/dist/agents/windsurf/adapter.d.ts +1 -0
  18. package/dist/agents/windsurf/adapter.js +1 -0
  19. package/dist/agents/windsurf/detector.d.ts +14 -0
  20. package/dist/agents/windsurf/detector.js +119 -0
  21. package/dist/architecture/c4.d.ts +22 -0
  22. package/dist/architecture/c4.js +62 -0
  23. package/dist/cli/commands/brain.d.ts +12 -0
  24. package/dist/cli/commands/brain.js +162 -0
  25. package/dist/cli/commands/complete.d.ts +23 -0
  26. package/dist/cli/commands/complete.js +232 -0
  27. package/dist/cli/commands/explain.js +1 -1
  28. package/dist/cli/commands/fix.js +22 -2
  29. package/dist/cli/commands/mcp.d.ts +8 -0
  30. package/dist/cli/commands/mcp.js +14 -0
  31. package/dist/cli/commands/platform.d.ts +15 -0
  32. package/dist/cli/commands/platform.js +191 -0
  33. package/dist/cli/commands/v2.d.ts +55 -0
  34. package/dist/cli/commands/v2.js +278 -0
  35. package/dist/cli/program.js +809 -2
  36. package/dist/constants.d.ts +3 -2
  37. package/dist/constants.js +5 -1
  38. package/dist/contracts/adapters.d.ts +7 -0
  39. package/dist/contracts/adapters.js +60 -0
  40. package/dist/contracts/index.d.ts +162 -0
  41. package/dist/contracts/index.js +18 -0
  42. package/dist/core/baseline/store.d.ts +48 -0
  43. package/dist/core/baseline/store.js +163 -0
  44. package/dist/core/brain-cli/service.d.ts +26 -0
  45. package/dist/core/brain-cli/service.js +175 -0
  46. package/dist/core/brain-product/init.d.ts +45 -0
  47. package/dist/core/brain-product/init.js +232 -0
  48. package/dist/core/changes/analyze.d.ts +41 -0
  49. package/dist/core/changes/analyze.js +223 -0
  50. package/dist/core/context-health/analyze.d.ts +15 -0
  51. package/dist/core/context-health/analyze.js +234 -0
  52. package/dist/core/fix/apply.d.ts +21 -4
  53. package/dist/core/fix/apply.js +118 -18
  54. package/dist/core/fix/backup.d.ts +30 -0
  55. package/dist/core/fix/backup.js +193 -0
  56. package/dist/core/fix/plan.d.ts +6 -3
  57. package/dist/core/fix/plan.js +113 -9
  58. package/dist/core/fix/render.d.ts +2 -0
  59. package/dist/core/fix/render.js +36 -21
  60. package/dist/core/fix/run.js +5 -1
  61. package/dist/core/fix/safe-target.d.ts +14 -0
  62. package/dist/core/fix/safe-target.js +81 -0
  63. package/dist/core/fix/types.d.ts +12 -1
  64. package/dist/core/fix/types.js +2 -0
  65. package/dist/core/fix/writers/claude-settings.js +3 -1
  66. package/dist/core/fix/writers/codex-config.d.ts +3 -0
  67. package/dist/core/fix/writers/codex-config.js +24 -3
  68. package/dist/core/fix/writers/cursorignore.js +3 -1
  69. package/dist/core/fix/writers/simple-ignore.d.ts +16 -0
  70. package/dist/core/fix/writers/simple-ignore.js +68 -0
  71. package/dist/core/monorepo/detect.d.ts +13 -0
  72. package/dist/core/monorepo/detect.js +121 -0
  73. package/dist/core/rules/build-context.js +22 -3
  74. package/dist/core/rules/context/generated-directory.js +11 -1
  75. package/dist/core/rules/context/large-instruction-file.js +6 -0
  76. package/dist/core/rules/context/large-log-file.js +16 -1
  77. package/dist/core/rules/ignore.d.ts +11 -1
  78. package/dist/core/rules/ignore.js +22 -1
  79. package/dist/core/rules/instructions/empty-instructions.js +7 -0
  80. package/dist/core/rules/security/env-file-exposure.js +23 -2
  81. package/dist/core/rules/security/private-key-file.js +1 -1
  82. package/dist/core/scanner/scan.js +1 -1
  83. package/dist/core/schemas/validate.d.ts +6 -0
  84. package/dist/core/schemas/validate.js +62 -0
  85. package/dist/core/scoring/compute-scores.d.ts +1 -1
  86. package/dist/core/scoring/compute-scores.js +5 -1
  87. package/dist/core/scoring/placeholder.d.ts +1 -1
  88. package/dist/core/scoring/placeholder.js +5 -1
  89. package/dist/core/secrets/scan.d.ts +23 -0
  90. package/dist/core/secrets/scan.js +172 -0
  91. package/dist/dashboard/server.d.ts +24 -0
  92. package/dist/dashboard/server.js +386 -0
  93. package/dist/enforcement/runner.d.ts +23 -0
  94. package/dist/enforcement/runner.js +56 -0
  95. package/dist/index.d.ts +13 -0
  96. package/dist/index.js +12 -0
  97. package/dist/integrations/github/pr-analyze.d.ts +28 -0
  98. package/dist/integrations/github/pr-analyze.js +98 -0
  99. package/dist/integrations/local-ai/provider.d.ts +45 -0
  100. package/dist/integrations/local-ai/provider.js +104 -0
  101. package/dist/intelligence/git/analyze.d.ts +34 -0
  102. package/dist/intelligence/git/analyze.js +136 -0
  103. package/dist/intelligence/graph/build.d.ts +13 -0
  104. package/dist/intelligence/graph/build.js +215 -0
  105. package/dist/knowledge/store.d.ts +25 -0
  106. package/dist/knowledge/store.js +91 -0
  107. package/dist/mcp/agentdoctor/server.d.ts +16 -0
  108. package/dist/mcp/agentdoctor/server.js +67 -0
  109. package/dist/mcp/brain/session.js +7 -7
  110. package/dist/mcp/intelligence/handlers.d.ts +11 -0
  111. package/dist/mcp/intelligence/handlers.js +175 -0
  112. package/dist/mcp/intelligence/path-safety.d.ts +11 -0
  113. package/dist/mcp/intelligence/path-safety.js +82 -0
  114. package/dist/mcp/intelligence/registry.d.ts +8 -0
  115. package/dist/mcp/intelligence/registry.js +160 -0
  116. package/dist/ops/health.d.ts +18 -0
  117. package/dist/ops/health.js +51 -0
  118. package/dist/platform/ai-quality/analyze.d.ts +9 -0
  119. package/dist/platform/ai-quality/analyze.js +65 -0
  120. package/dist/platform/architecture/drift.d.ts +17 -0
  121. package/dist/platform/architecture/drift.js +74 -0
  122. package/dist/platform/auth/local.d.ts +15 -0
  123. package/dist/platform/auth/local.js +58 -0
  124. package/dist/platform/context-security/analyze.d.ts +6 -0
  125. package/dist/platform/context-security/analyze.js +97 -0
  126. package/dist/platform/firewall/evaluate.d.ts +54 -0
  127. package/dist/platform/firewall/evaluate.js +308 -0
  128. package/dist/platform/graph/build.d.ts +6 -0
  129. package/dist/platform/graph/build.js +193 -0
  130. package/dist/platform/health/analyze.d.ts +7 -0
  131. package/dist/platform/health/analyze.js +186 -0
  132. package/dist/platform/index.d.ts +27 -0
  133. package/dist/platform/index.js +87 -0
  134. package/dist/platform/knowledge/analyze.d.ts +20 -0
  135. package/dist/platform/knowledge/analyze.js +96 -0
  136. package/dist/platform/provenance/build.d.ts +44 -0
  137. package/dist/platform/provenance/build.js +65 -0
  138. package/dist/platform/readiness/scorecard.d.ts +5 -0
  139. package/dist/platform/readiness/scorecard.js +98 -0
  140. package/dist/platform/refactor/impact.d.ts +20 -0
  141. package/dist/platform/refactor/impact.js +60 -0
  142. package/dist/platform/reports/export.d.ts +12 -0
  143. package/dist/platform/reports/export.js +92 -0
  144. package/dist/platform/security/redact.d.ts +15 -0
  145. package/dist/platform/security/redact.js +87 -0
  146. package/dist/platform/sessions/store.d.ts +32 -0
  147. package/dist/platform/sessions/store.js +97 -0
  148. package/dist/platform/store.d.ts +6 -0
  149. package/dist/platform/store.js +57 -0
  150. package/dist/platform/test-impact/analyze.d.ts +32 -0
  151. package/dist/platform/test-impact/analyze.js +164 -0
  152. package/dist/platform/time-machine/compare.d.ts +17 -0
  153. package/dist/platform/time-machine/compare.js +62 -0
  154. package/dist/platform/tokens/plan.d.ts +28 -0
  155. package/dist/platform/tokens/plan.js +89 -0
  156. package/dist/platform/types.d.ts +111 -0
  157. package/dist/platform/types.js +5 -0
  158. package/dist/plugins/runtime.d.ts +36 -0
  159. package/dist/plugins/runtime.js +101 -0
  160. package/dist/plugins/sdk.d.ts +29 -0
  161. package/dist/plugins/sdk.js +114 -0
  162. package/dist/policy/packs.d.ts +9 -0
  163. package/dist/policy/packs.js +59 -0
  164. package/dist/reporters/terminal/report.js +38 -5
  165. package/dist/storage/provider.d.ts +35 -0
  166. package/dist/storage/provider.js +103 -0
  167. package/dist/team/auth.d.ts +35 -0
  168. package/dist/team/auth.js +70 -0
  169. package/dist/types/index.d.ts +5 -1
  170. package/package.json +14 -7
@@ -3,11 +3,20 @@ 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 { collectOpsHealth } from "../ops/health.js";
17
+ import { listSessions, loadSession } from "../platform/sessions/store.js";
18
+ import { exportReports } from "../platform/reports/export.js";
19
+ import { runPlatformScan } from "../platform/index.js";
11
20
  function parseMinScore(value) {
12
21
  const parsed = Number(value);
13
22
  if (!Number.isFinite(parsed) || parsed < 0 || parsed > 100) {
@@ -93,7 +102,7 @@ export function createProgram() {
93
102
  });
94
103
  program
95
104
  .command("fix")
96
- .description("Apply safe automatic fixes (Cursor .cursorignore, Claude Code Read deny, and Codex filesystem deny for safe context findings)")
105
+ .description("Apply safe automatic fixes (.cursorignore, Claude Code Read deny, Codex filesystem deny, .geminiignore, .aiderignore for safe context findings)")
97
106
  .argument("[path]", "Repository path (default: current directory)")
98
107
  .option("--dry-run", "Show proposed fixes without writing files", false)
99
108
  .option("-y, --yes", "Skip confirmation prompts", false)
@@ -105,6 +114,783 @@ export function createProgram() {
105
114
  });
106
115
  process.exitCode = code;
107
116
  });
117
+ program
118
+ .command("fix-undo")
119
+ .description("Restore files from a Safe Fix audit backup")
120
+ .argument("<auditId>", "Audit id from fix-history")
121
+ .argument("[path]", "Repository path (default: current directory)")
122
+ .action(async (auditId, pathArg) => {
123
+ process.exitCode = await runFixUndoCommand({
124
+ auditId,
125
+ root: resolveTargetArgument(pathArg),
126
+ });
127
+ });
128
+ program
129
+ .command("fix-history")
130
+ .description("List Safe Fix audit / backup records")
131
+ .argument("[path]", "Repository path (default: current directory)")
132
+ .option("--json", "Emit JSON", false)
133
+ .action(async (pathArg, options) => {
134
+ process.exitCode = await runFixHistoryCommand({
135
+ root: resolveTargetArgument(pathArg),
136
+ json: Boolean(options.json),
137
+ });
138
+ });
139
+ const brain = program.command("brain").description("Project Brain CLI (local, deterministic)");
140
+ brain
141
+ .command("init")
142
+ .description("Ensure Project Brain store directories exist")
143
+ .argument("[path]", "Repository path")
144
+ .option("--json", "Emit JSON", false)
145
+ .action(async (pathArg, _options, command) => {
146
+ const options = command.optsWithGlobals();
147
+ process.exitCode = await runBrainCommand({
148
+ action: "init",
149
+ root: resolveTargetArgument(pathArg),
150
+ json: Boolean(options.json),
151
+ });
152
+ });
153
+ brain
154
+ .command("status")
155
+ .description("Show Project Brain store status")
156
+ .argument("[path]", "Repository path")
157
+ .option("--json", "Emit JSON", false)
158
+ .action(async (pathArg, _options, command) => {
159
+ const options = command.optsWithGlobals();
160
+ process.exitCode = await runBrainCommand({
161
+ action: "status",
162
+ root: resolveTargetArgument(pathArg),
163
+ json: Boolean(options.json),
164
+ });
165
+ });
166
+ brain
167
+ .command("inspect")
168
+ .description("Inspect latest Project Brain summary")
169
+ .argument("[path]", "Repository path")
170
+ .option("--json", "Emit JSON", false)
171
+ .action(async (pathArg, _options, command) => {
172
+ const options = command.optsWithGlobals();
173
+ process.exitCode = await runBrainCommand({
174
+ action: "inspect",
175
+ root: resolveTargetArgument(pathArg),
176
+ json: Boolean(options.json),
177
+ });
178
+ });
179
+ brain
180
+ .command("rebuild")
181
+ .description("Compile and save a new Project Brain snapshot")
182
+ .argument("[path]", "Repository path")
183
+ .option("--json", "Emit JSON", false)
184
+ .action(async (pathArg, _options, command) => {
185
+ const options = command.optsWithGlobals();
186
+ process.exitCode = await runBrainCommand({
187
+ action: "rebuild",
188
+ root: resolveTargetArgument(pathArg),
189
+ json: Boolean(options.json),
190
+ });
191
+ });
192
+ brain
193
+ .command("history")
194
+ .description("List Project Brain snapshots")
195
+ .argument("[path]", "Repository path")
196
+ .option("--json", "Emit JSON", false)
197
+ .action(async (pathArg, _options, command) => {
198
+ const options = command.optsWithGlobals();
199
+ process.exitCode = await runBrainCommand({
200
+ action: "history",
201
+ root: resolveTargetArgument(pathArg),
202
+ json: Boolean(options.json),
203
+ });
204
+ });
205
+ brain
206
+ .command("search")
207
+ .description("Search claims/components in the latest brain")
208
+ .argument("<query>", "Search text")
209
+ .argument("[path]", "Repository path")
210
+ .option("--json", "Emit JSON", false)
211
+ .action(async (query, pathArg, _options, command) => {
212
+ const options = command.optsWithGlobals();
213
+ process.exitCode = await runBrainCommand({
214
+ action: "search",
215
+ query,
216
+ root: resolveTargetArgument(pathArg),
217
+ json: Boolean(options.json),
218
+ });
219
+ });
220
+ brain
221
+ .command("export")
222
+ .description("Export a redacted brain snapshot to JSON")
223
+ .requiredOption("--file <path>", "Output file")
224
+ .option("--snapshot <id>", "Snapshot id (default: latest)")
225
+ .argument("[path]", "Repository path")
226
+ .action(async (pathArg, options) => {
227
+ process.exitCode = await runBrainCommand({
228
+ action: "export",
229
+ file: options.file,
230
+ ...(options.snapshot ? { snapshotId: options.snapshot } : {}),
231
+ root: resolveTargetArgument(pathArg),
232
+ });
233
+ });
234
+ brain
235
+ .command("import")
236
+ .description("Import a brain JSON snapshot into the local store")
237
+ .requiredOption("--file <path>", "Input file")
238
+ .argument("[path]", "Repository path")
239
+ .action(async (pathArg, options) => {
240
+ process.exitCode = await runBrainCommand({
241
+ action: "import",
242
+ file: options.file,
243
+ root: resolveTargetArgument(pathArg),
244
+ });
245
+ });
246
+ brain
247
+ .command("snapshot")
248
+ .description("Create Brain + Repository Brain product snapshots")
249
+ .argument("[path]", "Repository path")
250
+ .option("--json", "Emit JSON", false)
251
+ .action(async (pathArg, _options, command) => {
252
+ const options = command.optsWithGlobals();
253
+ process.exitCode = await runBrainCommand({
254
+ action: "snapshot",
255
+ root: resolveTargetArgument(pathArg),
256
+ json: Boolean(options.json),
257
+ });
258
+ });
259
+ brain
260
+ .command("update")
261
+ .description("Rebuild Project Brain and write product snapshot")
262
+ .argument("[path]", "Repository path")
263
+ .option("--json", "Emit JSON", false)
264
+ .action(async (pathArg, _options, command) => {
265
+ const options = command.optsWithGlobals();
266
+ process.exitCode = await runBrainCommand({
267
+ action: "update",
268
+ root: resolveTargetArgument(pathArg),
269
+ json: Boolean(options.json),
270
+ });
271
+ });
272
+ brain
273
+ .command("review")
274
+ .description("Human review of Repository Brain proposals (never auto-approves)")
275
+ .requiredOption("--artifact <id>", "Proposal artifact id")
276
+ .requiredOption("--decision <status>", "approved|rejected|pending-review|deprecated")
277
+ .option("--note <text>", "Review note")
278
+ .argument("[path]", "Repository path")
279
+ .option("--json", "Emit JSON", false)
280
+ .action(async (pathArg, _options, command) => {
281
+ const options = command.optsWithGlobals();
282
+ process.exitCode = await runBrainCommand({
283
+ action: "review",
284
+ artifactId: options.artifact,
285
+ decision: options.decision,
286
+ ...(options.note ? { note: options.note } : {}),
287
+ root: resolveTargetArgument(pathArg),
288
+ json: Boolean(options.json),
289
+ });
290
+ });
291
+ brain
292
+ .command("proposals")
293
+ .description("List Repository Brain proposal artifacts")
294
+ .argument("[path]", "Repository path")
295
+ .option("--json", "Emit JSON", false)
296
+ .action(async (pathArg, _options, command) => {
297
+ const options = command.optsWithGlobals();
298
+ process.exitCode = await runBrainCommand({
299
+ action: "proposals",
300
+ root: resolveTargetArgument(pathArg),
301
+ json: Boolean(options.json),
302
+ });
303
+ });
304
+ program
305
+ .command("init")
306
+ .description("Repository Brain initializer — PROPOSED artifacts only (not approved facts)")
307
+ .argument("[path]", "Repository path")
308
+ .option("--name <name>", "Project name")
309
+ .option("--domain <domain>", "Business domain")
310
+ .option("--json", "Emit JSON", false)
311
+ .action(async (pathArg, _options, command) => {
312
+ const options = command.optsWithGlobals();
313
+ process.exitCode = await runInitCommand({
314
+ root: resolveTargetArgument(pathArg),
315
+ json: Boolean(options.json),
316
+ ...(options.name ? { name: options.name } : {}),
317
+ ...(options.domain ? { domain: options.domain } : {}),
318
+ });
319
+ });
320
+ program
321
+ .command("graph")
322
+ .description("Build intelligence graph (TypeScript AST when available; regex fallback)")
323
+ .argument("[path]", "Repository path")
324
+ .option("--mode <mode>", "auto|regex|typescript-ast", "auto")
325
+ .option("--json", "Emit JSON", false)
326
+ .action(async (pathArg, _options, command) => {
327
+ const options = command.optsWithGlobals();
328
+ process.exitCode = await runV2SurfaceCommand({
329
+ action: "graph-ast",
330
+ root: resolveTargetArgument(pathArg),
331
+ mode: options.mode ?? "auto",
332
+ json: Boolean(options.json),
333
+ });
334
+ });
335
+ program
336
+ .command("health")
337
+ .description("Git hotspot / engineering intelligence report")
338
+ .argument("[path]", "Repository path")
339
+ .option("--json", "Emit JSON", false)
340
+ .action(async (pathArg, _options, command) => {
341
+ const options = command.optsWithGlobals();
342
+ process.exitCode = await runV2SurfaceCommand({
343
+ action: "health",
344
+ root: resolveTargetArgument(pathArg),
345
+ json: Boolean(options.json),
346
+ });
347
+ });
348
+ program
349
+ .command("impact")
350
+ .description("Change/test impact analysis")
351
+ .argument("[path]", "Repository path")
352
+ .option("--json", "Emit JSON", false)
353
+ .action(async (pathArg, _options, command) => {
354
+ const options = command.optsWithGlobals();
355
+ process.exitCode = await runV2SurfaceCommand({
356
+ action: "impact",
357
+ root: resolveTargetArgument(pathArg),
358
+ json: Boolean(options.json),
359
+ });
360
+ });
361
+ program
362
+ .command("test-impact")
363
+ .description("Test-impact analysis (alias of impact)")
364
+ .argument("[path]", "Repository path")
365
+ .option("--json", "Emit JSON", false)
366
+ .action(async (pathArg, _options, command) => {
367
+ const options = command.optsWithGlobals();
368
+ process.exitCode = await runV2SurfaceCommand({
369
+ action: "impact",
370
+ root: resolveTargetArgument(pathArg),
371
+ json: Boolean(options.json),
372
+ });
373
+ });
374
+ program
375
+ .command("refactor-impact")
376
+ .description("Refactor/rename blast-radius analysis")
377
+ .requiredOption("--symbol <name>", "Symbol to analyze")
378
+ .argument("[path]", "Repository path")
379
+ .option("--json", "Emit JSON", false)
380
+ .action(async (pathArg, _options, command) => {
381
+ const options = command.optsWithGlobals();
382
+ process.exitCode = await runV2SurfaceCommand({
383
+ action: "refactor-impact",
384
+ root: resolveTargetArgument(pathArg),
385
+ symbol: options.symbol,
386
+ json: Boolean(options.json),
387
+ });
388
+ });
389
+ program
390
+ .command("session")
391
+ .description("List or show agent sessions")
392
+ .argument("[path]", "Repository path")
393
+ .option("--id <id>", "Session id to show")
394
+ .option("--json", "Emit JSON", false)
395
+ .action(async (pathArg, _options, command) => {
396
+ const options = command.optsWithGlobals();
397
+ const root = resolveTargetArgument(pathArg);
398
+ try {
399
+ if (options.id) {
400
+ const session = await loadSession(root, options.id);
401
+ if (options.json)
402
+ process.stdout.write(`${JSON.stringify(session, null, 2)}\n`);
403
+ else
404
+ process.stdout.write(`${session?.id ?? "not-found"}\n`);
405
+ }
406
+ else {
407
+ const sessions = await listSessions(root);
408
+ if (options.json)
409
+ process.stdout.write(`${JSON.stringify(sessions, null, 2)}\n`);
410
+ else
411
+ for (const id of sessions)
412
+ process.stdout.write(`${id}\n`);
413
+ }
414
+ process.exitCode = EXIT_CODES.SUCCESS;
415
+ }
416
+ catch (error) {
417
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
418
+ process.exitCode = EXIT_CODES.INTERNAL_ERROR;
419
+ }
420
+ });
421
+ program
422
+ .command("report")
423
+ .description("Run platform scan and export local reports")
424
+ .argument("[path]", "Repository path")
425
+ .option("--json", "Emit JSON", false)
426
+ .action(async (pathArg, _options, command) => {
427
+ const options = command.optsWithGlobals();
428
+ const root = resolveTargetArgument(pathArg);
429
+ try {
430
+ const result = await runPlatformScan(root);
431
+ const paths = await exportReports({
432
+ root,
433
+ findings: result.snapshot.findings,
434
+ readiness: result.snapshot.readiness,
435
+ snapshot: result.snapshot,
436
+ });
437
+ if (options.json)
438
+ process.stdout.write(`${JSON.stringify({ ...result.reportPaths, ...paths }, null, 2)}\n`);
439
+ else
440
+ process.stdout.write(`Reports written under .agentdoctor/platform/\n`);
441
+ process.exitCode = EXIT_CODES.SUCCESS;
442
+ }
443
+ catch (error) {
444
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
445
+ process.exitCode = EXIT_CODES.INTERNAL_ERROR;
446
+ }
447
+ });
448
+ program
449
+ .command("c4")
450
+ .description("C4-style architecture views from graph evidence")
451
+ .argument("[path]", "Repository path")
452
+ .option("--json", "Emit JSON", false)
453
+ .action(async (pathArg, _options, command) => {
454
+ const options = command.optsWithGlobals();
455
+ process.exitCode = await runV2SurfaceCommand({
456
+ action: "c4",
457
+ root: resolveTargetArgument(pathArg),
458
+ json: Boolean(options.json),
459
+ });
460
+ });
461
+ program
462
+ .command("knowledge")
463
+ .description("List governed knowledge records")
464
+ .argument("[path]", "Repository path")
465
+ .option("--json", "Emit JSON", false)
466
+ .action(async (pathArg, _options, command) => {
467
+ const options = command.optsWithGlobals();
468
+ process.exitCode = await runV2SurfaceCommand({
469
+ action: "knowledge-list",
470
+ root: resolveTargetArgument(pathArg),
471
+ json: Boolean(options.json),
472
+ });
473
+ });
474
+ program
475
+ .command("knowledge-create")
476
+ .description("Create a draft knowledge record")
477
+ .requiredOption("--title <title>", "Title")
478
+ .requiredOption("--content <text>", "Content")
479
+ .argument("[path]", "Repository path")
480
+ .option("--json", "Emit JSON", false)
481
+ .action(async (pathArg, _options, command) => {
482
+ const options = command.optsWithGlobals();
483
+ process.exitCode = await runV2SurfaceCommand({
484
+ action: "knowledge-create",
485
+ root: resolveTargetArgument(pathArg),
486
+ title: options.title,
487
+ content: options.content,
488
+ json: Boolean(options.json),
489
+ });
490
+ });
491
+ program
492
+ .command("knowledge-approve")
493
+ .description("Transition knowledge record (explicit human approval)")
494
+ .requiredOption("--id <id>", "Record id")
495
+ .requiredOption("--decision <status>", "approved|rejected|pending-review|deprecated|draft")
496
+ .argument("[path]", "Repository path")
497
+ .option("--json", "Emit JSON", false)
498
+ .action(async (pathArg, _options, command) => {
499
+ const options = command.optsWithGlobals();
500
+ process.exitCode = await runV2SurfaceCommand({
501
+ action: "knowledge-transition",
502
+ root: resolveTargetArgument(pathArg),
503
+ id: options.id,
504
+ decision: options.decision,
505
+ json: Boolean(options.json),
506
+ });
507
+ });
508
+ program
509
+ .command("enforce")
510
+ .description("AgentDoctor-controlled runner policy check (does not execute by default)")
511
+ .requiredOption("--command <cmd>", "Command string")
512
+ .argument("[path]", "Repository path")
513
+ .option("--json", "Emit JSON", false)
514
+ .action(async (pathArg, _options, command) => {
515
+ const options = command.optsWithGlobals();
516
+ process.exitCode = await runV2SurfaceCommand({
517
+ action: "enforce-check",
518
+ root: resolveTargetArgument(pathArg),
519
+ command: options.command,
520
+ json: Boolean(options.json),
521
+ });
522
+ });
523
+ program
524
+ .command("team-register")
525
+ .description("Register local-dev team user (NOT enterprise SSO)")
526
+ .requiredOption("--username <name>", "Username")
527
+ .requiredOption("--password <password>", "Password (min 8)")
528
+ .argument("[path]", "Repository path")
529
+ .option("--json", "Emit JSON", false)
530
+ .action(async (pathArg, _options, command) => {
531
+ const options = command.optsWithGlobals();
532
+ process.exitCode = await runV2SurfaceCommand({
533
+ action: "team-register",
534
+ root: resolveTargetArgument(pathArg),
535
+ username: options.username,
536
+ password: options.password,
537
+ json: Boolean(options.json),
538
+ });
539
+ });
540
+ program
541
+ .command("team-login")
542
+ .description("Authenticate local-dev team user (NOT enterprise SSO)")
543
+ .requiredOption("--username <name>", "Username")
544
+ .requiredOption("--password <password>", "Password")
545
+ .argument("[path]", "Repository path")
546
+ .option("--json", "Emit JSON", false)
547
+ .action(async (pathArg, _options, command) => {
548
+ const options = command.optsWithGlobals();
549
+ process.exitCode = await runV2SurfaceCommand({
550
+ action: "team-login",
551
+ root: resolveTargetArgument(pathArg),
552
+ username: options.username,
553
+ password: options.password,
554
+ json: Boolean(options.json),
555
+ });
556
+ });
557
+ program
558
+ .command("changes")
559
+ .description("Analyze git working-tree / range changes")
560
+ .argument("[path]", "Repository path")
561
+ .option("--since <ref>", "Diff since git ref (e.g. main)")
562
+ .option("--impact", "Compute dependency impact from Project Brain (UNKNOWN when edges are missing)", false)
563
+ .option("--json", "Emit JSON", false)
564
+ .action(async (pathArg, options) => {
565
+ process.exitCode = await runChangesCommand({
566
+ root: resolveTargetArgument(pathArg),
567
+ ...(options.since ? { since: options.since } : {}),
568
+ impact: Boolean(options.impact),
569
+ json: Boolean(options.json),
570
+ });
571
+ });
572
+ program
573
+ .command("context-health")
574
+ .description("Detect instruction / ignore surface conflicts")
575
+ .argument("[path]", "Repository path")
576
+ .option("--json", "Emit JSON", false)
577
+ .action(async (pathArg, options) => {
578
+ process.exitCode = await runContextHealthCommand({
579
+ root: resolveTargetArgument(pathArg),
580
+ json: Boolean(options.json),
581
+ });
582
+ });
583
+ program
584
+ .command("secrets")
585
+ .description("Opt-in content secret scan (findings are always redacted)")
586
+ .argument("[path]", "Repository path")
587
+ .option("--json", "Emit JSON", false)
588
+ .action(async (pathArg, options) => {
589
+ process.exitCode = await runSecretsCommand({
590
+ root: resolveTargetArgument(pathArg),
591
+ json: Boolean(options.json),
592
+ });
593
+ });
594
+ const baseline = program.command("baseline").description("Named scan baselines");
595
+ baseline
596
+ .command("save")
597
+ .description("Save current scan as a named baseline")
598
+ .requiredOption("--name <name>", "Baseline name")
599
+ .argument("[path]", "Repository path")
600
+ .action(async (pathArg, options) => {
601
+ process.exitCode = await runBaselineCommand({
602
+ action: "save",
603
+ name: options.name,
604
+ root: resolveTargetArgument(pathArg),
605
+ });
606
+ });
607
+ baseline
608
+ .command("list")
609
+ .description("List named baselines")
610
+ .argument("[path]", "Repository path")
611
+ .option("--json", "Emit JSON", false)
612
+ .action(async (pathArg, options) => {
613
+ process.exitCode = await runBaselineCommand({
614
+ action: "list",
615
+ root: resolveTargetArgument(pathArg),
616
+ json: Boolean(options.json),
617
+ });
618
+ });
619
+ baseline
620
+ .command("diff")
621
+ .description("Compare current scan to a named baseline")
622
+ .requiredOption("--name <name>", "Baseline name")
623
+ .argument("[path]", "Repository path")
624
+ .option("--json", "Emit JSON", false)
625
+ .action(async (pathArg, options) => {
626
+ process.exitCode = await runBaselineCommand({
627
+ action: "diff",
628
+ name: options.name,
629
+ root: resolveTargetArgument(pathArg),
630
+ json: Boolean(options.json),
631
+ });
632
+ });
633
+ baseline
634
+ .command("delete")
635
+ .description("Delete a named baseline (fails if missing)")
636
+ .requiredOption("--name <name>", "Baseline name")
637
+ .argument("[path]", "Repository path")
638
+ .action(async (pathArg, options) => {
639
+ process.exitCode = await runBaselineCommand({
640
+ action: "delete",
641
+ name: options.name,
642
+ root: resolveTargetArgument(pathArg),
643
+ });
644
+ });
645
+ baseline
646
+ .command("trends")
647
+ .description("Show baseline count trends over time (no causality claimed)")
648
+ .argument("[path]", "Repository path")
649
+ .option("--json", "Emit JSON", false)
650
+ .action(async (pathArg, options) => {
651
+ process.exitCode = await runBaselineCommand({
652
+ action: "trends",
653
+ root: resolveTargetArgument(pathArg),
654
+ json: Boolean(options.json),
655
+ });
656
+ });
657
+ program
658
+ .command("packages")
659
+ .description("Detect workspace packages / optional per-package scan")
660
+ .argument("[path]", "Repository path")
661
+ .option("--scan", "Run a scan per package", false)
662
+ .option("--json", "Emit JSON", false)
663
+ .action(async (pathArg, options) => {
664
+ process.exitCode = await runPackagesCommand({
665
+ root: resolveTargetArgument(pathArg),
666
+ scan: Boolean(options.scan),
667
+ json: Boolean(options.json),
668
+ });
669
+ });
670
+ program
671
+ .command("pr-review")
672
+ .description("Local PR review dry-run (never posts to GitHub)")
673
+ .argument("[path]", "Repository path")
674
+ .option("--base <ref>", "Base git ref")
675
+ .option("--json", "Emit JSON", false)
676
+ .action(async (pathArg, options) => {
677
+ process.exitCode = await runPrReviewCommand({
678
+ root: resolveTargetArgument(pathArg),
679
+ ...(options.base ? { base: options.base } : {}),
680
+ json: Boolean(options.json),
681
+ });
682
+ });
683
+ program
684
+ .command("dashboard")
685
+ .description("Start local read-only dashboard (loopback only by default)")
686
+ .argument("[path]", "Repository path")
687
+ .option("--host <host>", "Bind host (default 127.0.0.1)", "127.0.0.1")
688
+ .option("--port <port>", "Bind port", "8787")
689
+ .option("--allow-non-loopback", "Unsafe: allow binding outside 127.0.0.1/::1/localhost (not an enterprise security boundary)", false)
690
+ .action(async (pathArg, options) => {
691
+ process.exitCode = await runDashboardCommand({
692
+ root: resolveTargetArgument(pathArg),
693
+ host: options.host ?? "127.0.0.1",
694
+ port: Number(options.port ?? 8787),
695
+ allowNonLoopback: Boolean(options.allowNonLoopback),
696
+ });
697
+ });
698
+ program
699
+ .command("plugins")
700
+ .description("List / validate local plugins under .agentdoctor/plugins")
701
+ .argument("[path]", "Repository path")
702
+ .option("--run", "Execute analyzer plugin hooks (isolated; timeouts apply)", false)
703
+ .option("--json", "Emit JSON", false)
704
+ .action(async (pathArg, options) => {
705
+ process.exitCode = await runPluginsCommand({
706
+ root: resolveTargetArgument(pathArg),
707
+ json: Boolean(options.json),
708
+ run: Boolean(options.run),
709
+ });
710
+ });
711
+ program
712
+ .command("local-ai")
713
+ .description("Optional local AI provider probe (default: none; core stays deterministic)")
714
+ .option("--provider <id>", "none | mock | ollama", "none")
715
+ .option("--prompt <text>", "Prompt text")
716
+ .option("--json", "Emit JSON", false)
717
+ .action(async (options) => {
718
+ process.exitCode = await runLocalAiCommand({
719
+ ...(options.provider ? { provider: options.provider } : {}),
720
+ ...(options.prompt ? { prompt: options.prompt } : {}),
721
+ json: Boolean(options.json),
722
+ });
723
+ });
724
+ const platform = program
725
+ .command("platform")
726
+ .description("AgentDoctor 2.0 local platform (intelligence, Action Policy Evaluator, sessions, reports)");
727
+ platform
728
+ .command("scan")
729
+ .description("Run integrated platform scan and write local reports")
730
+ .argument("[path]", "Repository path")
731
+ .option("--json", "Emit JSON", false)
732
+ .action(async (pathArg, _options, command) => {
733
+ const options = command.optsWithGlobals();
734
+ process.exitCode = await runPlatformCommand({
735
+ action: "scan",
736
+ root: resolveTargetArgument(pathArg),
737
+ json: Boolean(options.json),
738
+ });
739
+ });
740
+ platform
741
+ .command("graph")
742
+ .description("Build repository intelligence graph")
743
+ .argument("[path]", "Repository path")
744
+ .option("--json", "Emit JSON", false)
745
+ .action(async (pathArg, _options, command) => {
746
+ const options = command.optsWithGlobals();
747
+ process.exitCode = await runPlatformCommand({
748
+ action: "graph",
749
+ root: resolveTargetArgument(pathArg),
750
+ json: Boolean(options.json),
751
+ });
752
+ });
753
+ platform
754
+ .command("test-impact")
755
+ .description("Analyze which tests likely relate to git changes (heuristic; does not run tests)")
756
+ .argument("[path]", "Repository path")
757
+ .option("--json", "Emit JSON", false)
758
+ .action(async (pathArg, _options, command) => {
759
+ const options = command.optsWithGlobals();
760
+ process.exitCode = await runPlatformCommand({
761
+ action: "test-impact",
762
+ root: resolveTargetArgument(pathArg),
763
+ json: Boolean(options.json),
764
+ });
765
+ });
766
+ const registerPolicyCheck = (name, aliasNote) => {
767
+ platform
768
+ .command(name)
769
+ .description(`Action Policy Evaluator${aliasNote}: evaluate an agent action (never executes; no agent interception)`)
770
+ .argument("[path]", "Repository path")
771
+ .option("--type <type>", "Action type", "shell")
772
+ .option("--command <cmd>", "Shell command string")
773
+ .option("--path <file>", "Target path")
774
+ .option("--fail-closed", "If local policy JSON is invalid, deny the action instead of falling back to defaults", false)
775
+ .option("--json", "Emit JSON", false)
776
+ .action(async (pathArg, _options, cmd) => {
777
+ const options = cmd.optsWithGlobals();
778
+ process.exitCode = await runPlatformCommand({
779
+ action: "policy-check",
780
+ root: resolveTargetArgument(pathArg),
781
+ ...(options.type ? { actionType: options.type } : {}),
782
+ ...(options.command ? { command: options.command } : {}),
783
+ ...(options.path ? { path: options.path } : {}),
784
+ json: Boolean(options.json),
785
+ failClosed: Boolean(options.failClosed),
786
+ });
787
+ });
788
+ };
789
+ registerPolicyCheck("policy-check", "");
790
+ registerPolicyCheck("firewall-check", " (alias)");
791
+ platform
792
+ .command("session-list")
793
+ .description("List recorded agent sessions")
794
+ .argument("[path]", "Repository path")
795
+ .option("--json", "Emit JSON", false)
796
+ .action(async (pathArg, _options, command) => {
797
+ const options = command.optsWithGlobals();
798
+ process.exitCode = await runPlatformCommand({
799
+ action: "session-list",
800
+ root: resolveTargetArgument(pathArg),
801
+ json: Boolean(options.json),
802
+ });
803
+ });
804
+ platform
805
+ .command("session-show")
806
+ .description("Show / export an agent session")
807
+ .requiredOption("--session <id>", "Session id")
808
+ .argument("[path]", "Repository path")
809
+ .option("--json", "Emit JSON", false)
810
+ .action(async (pathArg, _options, command) => {
811
+ const options = command.optsWithGlobals();
812
+ process.exitCode = await runPlatformCommand({
813
+ action: "session-show",
814
+ sessionId: options.session,
815
+ root: resolveTargetArgument(pathArg),
816
+ json: Boolean(options.json),
817
+ });
818
+ });
819
+ platform
820
+ .command("demo-session")
821
+ .description("Record a demo session with Action Policy Evaluator (evaluate-only; no command execution)")
822
+ .argument("[path]", "Repository path")
823
+ .option("--json", "Emit JSON", false)
824
+ .action(async (pathArg, _options, command) => {
825
+ const options = command.optsWithGlobals();
826
+ process.exitCode = await runPlatformCommand({
827
+ action: "demo-session",
828
+ root: resolveTargetArgument(pathArg),
829
+ json: Boolean(options.json),
830
+ });
831
+ });
832
+ platform
833
+ .command("provenance")
834
+ .description("Build a provenance record for a file (unknown fields marked)")
835
+ .requiredOption("--path <file>", "File path")
836
+ .argument("[repo]", "Repository path")
837
+ .option("--json", "Emit JSON", false)
838
+ .action(async (repo, _options, command) => {
839
+ const options = command.optsWithGlobals();
840
+ process.exitCode = await runPlatformCommand({
841
+ action: "provenance",
842
+ path: options.path,
843
+ root: resolveTargetArgument(repo),
844
+ json: Boolean(options.json),
845
+ });
846
+ });
847
+ platform
848
+ .command("context")
849
+ .description("Plan token-optimized context for a query")
850
+ .option("--query <text>", "Query", "src")
851
+ .argument("[path]", "Repository path")
852
+ .option("--json", "Emit JSON", false)
853
+ .action(async (pathArg, _options, command) => {
854
+ const options = command.optsWithGlobals();
855
+ process.exitCode = await runPlatformCommand({
856
+ action: "context",
857
+ query: options.query ?? "src",
858
+ root: resolveTargetArgument(pathArg),
859
+ json: Boolean(options.json),
860
+ });
861
+ });
862
+ platform
863
+ .command("refactor")
864
+ .description("Analyze rename impact for a symbol (does not apply edits)")
865
+ .requiredOption("--symbol <name>", "Symbol name")
866
+ .argument("[path]", "Repository path")
867
+ .option("--json", "Emit JSON", false)
868
+ .action(async (pathArg, _options, command) => {
869
+ const options = command.optsWithGlobals();
870
+ process.exitCode = await runPlatformCommand({
871
+ action: "refactor",
872
+ symbol: options.symbol,
873
+ root: resolveTargetArgument(pathArg),
874
+ json: Boolean(options.json),
875
+ });
876
+ });
877
+ platform
878
+ .command("time-machine")
879
+ .description("Compare two git refs")
880
+ .requiredOption("--left <ref>", "Left ref")
881
+ .requiredOption("--right <ref>", "Right ref")
882
+ .argument("[path]", "Repository path")
883
+ .option("--json", "Emit JSON", false)
884
+ .action(async (pathArg, _options, command) => {
885
+ const options = command.optsWithGlobals();
886
+ process.exitCode = await runPlatformCommand({
887
+ action: "time-machine",
888
+ left: options.left,
889
+ right: options.right,
890
+ root: resolveTargetArgument(pathArg),
891
+ json: Boolean(options.json),
892
+ });
893
+ });
108
894
  program
109
895
  .command("verify")
110
896
  .description("Re-scan and compare against a prior scan JSON baseline (Scan → Fix → Verify)")
@@ -150,7 +936,16 @@ export function createProgram() {
150
936
  program
151
937
  .command("doctor")
152
938
  .description("Check AgentDoctor installation health")
153
- .action(async () => {
939
+ .option("--json", "Emit JSON ops health", false)
940
+ .argument("[path]", "Repository path")
941
+ .action(async (pathArg, _options, command) => {
942
+ const options = command.optsWithGlobals();
943
+ if (options.json) {
944
+ const health = await collectOpsHealth(resolveTargetArgument(pathArg));
945
+ process.stdout.write(`${JSON.stringify(health, null, 2)}\n`);
946
+ process.exitCode = health.ok ? EXIT_CODES.SUCCESS : EXIT_CODES.INTERNAL_ERROR;
947
+ return;
948
+ }
154
949
  const code = await runDoctorCommand();
155
950
  process.exitCode = code;
156
951
  });
@@ -166,6 +961,18 @@ export function createProgram() {
166
961
  });
167
962
  process.exitCode = code;
168
963
  });
964
+ program
965
+ .command("mcp")
966
+ .description("Start combined AgentDoctor MCP (Brain tools + intelligence tools) over STDIO")
967
+ .requiredOption("--root <path>", "Absolute or relative project root (required; never uses process.cwd() implicitly)")
968
+ .option("--no-build-if-missing", "Fail when no Brain snapshot exists instead of compiling")
969
+ .action(async (options) => {
970
+ const code = await runMcpCommand({
971
+ root: options.root,
972
+ buildIfMissing: options.buildIfMissing !== false,
973
+ });
974
+ process.exitCode = code;
975
+ });
169
976
  program.configureOutput({
170
977
  outputError: (str, write) => write(str),
171
978
  });