@praneeth_54/agentdoctor 2.0.0 → 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.
Files changed (95) hide show
  1. package/CHANGELOG.md +73 -4
  2. package/README.md +283 -158
  3. package/dist/architecture/contract.d.ts +64 -0
  4. package/dist/architecture/contract.js +373 -0
  5. package/dist/assurance/change.d.ts +211 -0
  6. package/dist/assurance/change.js +624 -0
  7. package/dist/assurance/proof.d.ts +136 -0
  8. package/dist/assurance/proof.js +338 -0
  9. package/dist/auth/index.d.ts +81 -0
  10. package/dist/auth/index.js +130 -0
  11. package/dist/auth/rbac.d.ts +9 -0
  12. package/dist/auth/rbac.js +26 -0
  13. package/dist/cli/commands/architecture.d.ts +6 -0
  14. package/dist/cli/commands/architecture.js +109 -0
  15. package/dist/cli/commands/assurance.d.ts +67 -0
  16. package/dist/cli/commands/assurance.js +210 -0
  17. package/dist/cli/commands/complete.js +2 -2
  18. package/dist/cli/commands/platform.d.ts +2 -0
  19. package/dist/cli/commands/platform.js +5 -1
  20. package/dist/cli/commands/policy-graph-run.d.ts +40 -0
  21. package/dist/cli/commands/policy-graph-run.js +200 -0
  22. package/dist/cli/commands/workspace.d.ts +9 -0
  23. package/dist/cli/commands/workspace.js +82 -0
  24. package/dist/cli/program.js +535 -17
  25. package/dist/constants.d.ts +1 -1
  26. package/dist/constants.js +1 -1
  27. package/dist/contracts/index.d.ts +1 -1
  28. package/dist/contracts/index.js +2 -2
  29. package/dist/core/brain-product/init.js +5 -2
  30. package/dist/core/secrets/scan.d.ts +7 -1
  31. package/dist/core/secrets/scan.js +9 -9
  32. package/dist/coverage/cobertura.d.ts +6 -0
  33. package/dist/coverage/cobertura.js +52 -0
  34. package/dist/coverage/istanbul.d.ts +7 -0
  35. package/dist/coverage/istanbul.js +134 -0
  36. package/dist/coverage/lcov.d.ts +7 -0
  37. package/dist/coverage/lcov.js +59 -0
  38. package/dist/coverage/load.d.ts +9 -0
  39. package/dist/coverage/load.js +39 -0
  40. package/dist/coverage/types.d.ts +23 -0
  41. package/dist/coverage/types.js +5 -0
  42. package/dist/dashboard/server.d.ts +1 -1
  43. package/dist/dashboard/server.js +21 -5
  44. package/dist/enforcement/runner.d.ts +47 -3
  45. package/dist/enforcement/runner.js +420 -16
  46. package/dist/index.d.ts +23 -2
  47. package/dist/index.js +15 -2
  48. package/dist/intelligence/graph/build.d.ts +2 -0
  49. package/dist/intelligence/graph/build.js +49 -5
  50. package/dist/intelligence/graph/incremental.d.ts +68 -0
  51. package/dist/intelligence/graph/incremental.js +234 -0
  52. package/dist/intelligence/resolve/imports.d.ts +36 -0
  53. package/dist/intelligence/resolve/imports.js +245 -0
  54. package/dist/languages/go.d.ts +9 -0
  55. package/dist/languages/go.js +45 -0
  56. package/dist/languages/index.d.ts +11 -0
  57. package/dist/languages/index.js +76 -0
  58. package/dist/languages/php.d.ts +7 -0
  59. package/dist/languages/php.js +193 -0
  60. package/dist/languages/python.d.ts +7 -0
  61. package/dist/languages/python.js +150 -0
  62. package/dist/languages/types.d.ts +42 -0
  63. package/dist/languages/types.js +26 -0
  64. package/dist/languages/typescript.d.ts +3 -0
  65. package/dist/languages/typescript.js +95 -0
  66. package/dist/mcp/intelligence/handlers.d.ts +5 -0
  67. package/dist/mcp/intelligence/handlers.js +229 -40
  68. package/dist/mcp/intelligence/path-safety.d.ts +2 -4
  69. package/dist/mcp/intelligence/path-safety.js +25 -44
  70. package/dist/mcp/intelligence/registry.d.ts +1 -1
  71. package/dist/mcp/intelligence/registry.js +69 -1
  72. package/dist/platform/firewall/evaluate.d.ts +6 -3
  73. package/dist/platform/firewall/evaluate.js +66 -15
  74. package/dist/platform/graph/build.js +19 -23
  75. package/dist/platform/health/analyze.js +23 -17
  76. package/dist/platform/index.js +2 -0
  77. package/dist/platform/test-impact/analyze.d.ts +21 -3
  78. package/dist/platform/test-impact/analyze.js +214 -28
  79. package/dist/platform/tokens/plan.js +39 -20
  80. package/dist/platform/types.d.ts +9 -0
  81. package/dist/policy/compose.d.ts +34 -0
  82. package/dist/policy/compose.js +118 -0
  83. package/dist/policy/packs.js +33 -1
  84. package/dist/security/paths.d.ts +21 -0
  85. package/dist/security/paths.js +105 -0
  86. package/dist/storage/postgres.d.ts +26 -0
  87. package/dist/storage/postgres.js +90 -0
  88. package/dist/storage/provider.d.ts +2 -7
  89. package/dist/storage/provider.js +22 -16
  90. package/dist/storage/sqlite.d.ts +33 -0
  91. package/dist/storage/sqlite.js +77 -0
  92. package/dist/team/auth.js +1 -1
  93. package/dist/workspace/index.d.ts +69 -0
  94. package/dist/workspace/index.js +220 -0
  95. package/package.json +9 -3
@@ -13,10 +13,16 @@ import { runBaselineCommand, runChangesCommand, runContextHealthCommand, runDash
13
13
  import { runPlatformCommand } from "./commands/platform.js";
14
14
  import { runInitCommand, runV2SurfaceCommand } from "./commands/complete.js";
15
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";
16
20
  import { collectOpsHealth } from "../ops/health.js";
17
21
  import { listSessions, loadSession } from "../platform/sessions/store.js";
18
22
  import { exportReports } from "../platform/reports/export.js";
19
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";
20
26
  function parseMinScore(value) {
21
27
  const parsed = Number(value);
22
28
  if (!Number.isFinite(parsed) || parsed < 0 || parsed > 100) {
@@ -89,7 +95,7 @@ export function createProgram() {
89
95
  const program = new Command();
90
96
  addScanOptions(program
91
97
  .name("agentdoctor")
92
- .description("Audit AI coding agent configuration in a repository (local, deterministic, no API key).")
98
+ .description("Engineering intelligence and safety for AI coding agents — scan, knowledge, MCP, and verification (local, deterministic; no API key required for core flows).")
93
99
  .version(PACKAGE_VERSION, "-V, --version", "Print AgentDoctor version")
94
100
  .argument("[path]", "Repository path to scan (default: current directory)")).action(async (pathArg, _options, command) => {
95
101
  await runScanFromCli(pathArg, command);
@@ -317,21 +323,76 @@ export function createProgram() {
317
323
  ...(options.domain ? { domain: options.domain } : {}),
318
324
  });
319
325
  });
320
- program
326
+ const graph = program
321
327
  .command("graph")
322
- .description("Build intelligence graph (TypeScript AST when available; regex fallback)")
328
+ .description("Build / update intelligence graph (TypeScript AST when available; regex fallback). Bare `graph` aliases build.")
323
329
  .argument("[path]", "Repository path")
324
330
  .option("--mode <mode>", "auto|regex|typescript-ast", "auto")
325
331
  .option("--json", "Emit JSON", false)
326
332
  .action(async (pathArg, _options, command) => {
327
333
  const options = command.optsWithGlobals();
328
- process.exitCode = await runV2SurfaceCommand({
329
- action: "graph-ast",
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",
330
379
  root: resolveTargetArgument(pathArg),
331
380
  mode: options.mode ?? "auto",
332
381
  json: Boolean(options.json),
333
382
  });
334
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
+ });
335
396
  program
336
397
  .command("health")
337
398
  .description("Git hotspot / engineering intelligence report")
@@ -347,29 +408,63 @@ export function createProgram() {
347
408
  });
348
409
  program
349
410
  .command("impact")
350
- .description("Change/test impact analysis")
411
+ .description("Change/test impact analysis (heuristic by default; coverage-backed with --coverage)")
351
412
  .argument("[path]", "Repository path")
413
+ .option("--coverage <path>", "LCOV / Istanbul JSON / Cobertura XML coverage file")
414
+ .option("--since <ref>", "Diff since git ref")
352
415
  .option("--json", "Emit JSON", false)
353
416
  .action(async (pathArg, _options, command) => {
354
417
  const options = command.optsWithGlobals();
355
- process.exitCode = await runV2SurfaceCommand({
356
- action: "impact",
357
- root: resolveTargetArgument(pathArg),
358
- json: Boolean(options.json),
359
- });
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
+ }
360
437
  });
361
438
  program
362
439
  .command("test-impact")
363
440
  .description("Test-impact analysis (alias of impact)")
364
441
  .argument("[path]", "Repository path")
442
+ .option("--coverage <path>", "LCOV / Istanbul JSON / Cobertura XML coverage file")
443
+ .option("--since <ref>", "Diff since git ref")
365
444
  .option("--json", "Emit JSON", false)
366
445
  .action(async (pathArg, _options, command) => {
367
446
  const options = command.optsWithGlobals();
368
- process.exitCode = await runV2SurfaceCommand({
369
- action: "impact",
370
- root: resolveTargetArgument(pathArg),
371
- json: Boolean(options.json),
372
- });
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
+ }
373
468
  });
374
469
  program
375
470
  .command("refactor-impact")
@@ -458,6 +553,57 @@ export function createProgram() {
458
553
  json: Boolean(options.json),
459
554
  });
460
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
+ });
461
607
  program
462
608
  .command("knowledge")
463
609
  .description("List governed knowledge records")
@@ -554,6 +700,374 @@ export function createProgram() {
554
700
  json: Boolean(options.json),
555
701
  });
556
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
+ });
557
1071
  program
558
1072
  .command("changes")
559
1073
  .description("Analyze git working-tree / range changes")
@@ -752,8 +1266,10 @@ export function createProgram() {
752
1266
  });
753
1267
  platform
754
1268
  .command("test-impact")
755
- .description("Analyze which tests likely relate to git changes (heuristic; does not run tests)")
1269
+ .description("Analyze which tests relate to git changes (heuristic; coverage-backed with --coverage; does not run tests)")
756
1270
  .argument("[path]", "Repository path")
1271
+ .option("--coverage <path>", "LCOV / Istanbul JSON / Cobertura XML coverage file")
1272
+ .option("--since <ref>", "Diff since git ref")
757
1273
  .option("--json", "Emit JSON", false)
758
1274
  .action(async (pathArg, _options, command) => {
759
1275
  const options = command.optsWithGlobals();
@@ -761,6 +1277,8 @@ export function createProgram() {
761
1277
  action: "test-impact",
762
1278
  root: resolveTargetArgument(pathArg),
763
1279
  json: Boolean(options.json),
1280
+ ...(options.coverage ? { coverage: options.coverage } : {}),
1281
+ ...(options.since ? { since: options.since } : {}),
764
1282
  });
765
1283
  });
766
1284
  const registerPolicyCheck = (name, aliasNote) => {
@@ -1,5 +1,5 @@
1
1
  import type { AgentId } from "./types/index.js";
2
- export declare const PACKAGE_VERSION = "2.0.0";
2
+ export declare const PACKAGE_VERSION = "2.0.1";
3
3
  export declare const DEFAULT_MAX_FILE_SIZE_BYTES: number;
4
4
  /** Directories skipped during normal discovery (unless a rule needs them later). */
5
5
  export declare const DEFAULT_IGNORE_DIRECTORIES: Set<string>;
package/dist/constants.js CHANGED
@@ -1,4 +1,4 @@
1
- export const PACKAGE_VERSION = "2.0.0";
1
+ export const PACKAGE_VERSION = "2.0.1";
2
2
  export const DEFAULT_MAX_FILE_SIZE_BYTES = 2 * 1024 * 1024; // 2 MiB
3
3
  /** Directories skipped during normal discovery (unless a rule needs them later). */
4
4
  export const DEFAULT_IGNORE_DIRECTORIES = new Set([
@@ -105,7 +105,7 @@ export interface PolicyDecisionContract {
105
105
  inputClassification: string;
106
106
  evidence: EvidenceRef[];
107
107
  riskLevel: Severity;
108
- executionStatus: "not-executed" | "executed" | "blocked-by-enforcement";
108
+ executionStatus: "not-executed" | "executed" | "blocked-by-enforcement" | "execution-failed";
109
109
  approvalStatus: "not-required" | "pending" | "approved" | "denied";
110
110
  timestamp: string;
111
111
  }
@@ -5,9 +5,9 @@
5
5
  export const CONTRACTS_VERSION = "2.0.0-contracts";
6
6
  export const DEFAULT_FEATURE_FLAGS = {
7
7
  typescriptAst: true,
8
- sqliteStorage: false,
8
+ sqliteStorage: true,
9
9
  teamMode: false,
10
- enforcementRunner: false,
10
+ enforcementRunner: true,
11
11
  vectorSearch: false,
12
12
  architectureC4: true,
13
13
  };