@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
@@ -0,0 +1,62 @@
1
+ /**
2
+ * C4-style views derived from repository graph evidence.
3
+ * Always labels inferred vs observed.
4
+ */
5
+ export function buildC4Views(graph) {
6
+ const files = graph.nodes.filter((n) => n.kind === "file" || n.kind === "test");
7
+ const deps = graph.edges.filter((e) => e.kind === "imports" || e.kind === "depends_on");
8
+ const containers = [
9
+ { id: "c_app", label: "Application", kind: "container" },
10
+ { id: "c_tests", label: "Tests", kind: "container" },
11
+ { id: "c_docs", label: "Docs", kind: "container" },
12
+ ];
13
+ const system = {
14
+ level: "system-context",
15
+ title: "System context (inferred)",
16
+ nodes: [
17
+ { id: "sys", label: graph.root.split("/").pop() ?? "system", kind: "system" },
18
+ { id: "dev", label: "Developer / Agent", kind: "person" },
19
+ ],
20
+ edges: [{ from: "dev", to: "sys", kind: "uses" }],
21
+ evidenceKind: "inferred",
22
+ limitations: ["System context is scaffolding from repository root name only"],
23
+ };
24
+ const container = {
25
+ level: "container",
26
+ title: "Containers (inferred from paths)",
27
+ nodes: containers,
28
+ edges: [
29
+ { from: "c_app", to: "c_tests", kind: "tested_by" },
30
+ { from: "c_docs", to: "c_app", kind: "documents" },
31
+ ],
32
+ evidenceKind: "inferred",
33
+ limitations: ["Container boundaries inferred from folder conventions, not runtime topology"],
34
+ };
35
+ const component = {
36
+ level: "component",
37
+ title: "Components (from graph files)",
38
+ nodes: files.slice(0, 40).map((n) => ({
39
+ id: n.id,
40
+ label: n.label,
41
+ kind: n.kind,
42
+ })),
43
+ edges: deps.slice(0, 80).map((e) => ({ from: e.from, to: e.to, kind: e.kind })),
44
+ evidenceKind: deps.some((e) => e.evidence === "verified") ? "observed" : "inferred",
45
+ limitations: ["Component view capped; not a full deployment diagram"],
46
+ };
47
+ const code = {
48
+ level: "code",
49
+ title: "Code-level symbols",
50
+ nodes: graph.nodes
51
+ .filter((n) => n.kind === "function" || n.kind === "class")
52
+ .slice(0, 60)
53
+ .map((n) => ({ id: n.id, label: n.label, kind: n.kind })),
54
+ edges: graph.edges
55
+ .filter((e) => e.kind === "calls" || e.kind === "extends" || e.kind === "implements")
56
+ .slice(0, 80)
57
+ .map((e) => ({ from: e.from, to: e.to, kind: e.kind })),
58
+ evidenceKind: "observed",
59
+ limitations: ["Code view depends on active graph builder (AST or regex)"],
60
+ };
61
+ return [system, container, component, code];
62
+ }
@@ -0,0 +1,12 @@
1
+ import { type ExitCode } from "../../types/index.js";
2
+ export declare function runBrainCommand(options: {
3
+ action: string;
4
+ root?: string;
5
+ query?: string;
6
+ snapshotId?: string;
7
+ file?: string;
8
+ json?: boolean;
9
+ artifactId?: string;
10
+ decision?: "approved" | "rejected" | "pending-review" | "deprecated";
11
+ note?: string;
12
+ }): Promise<ExitCode>;
@@ -0,0 +1,162 @@
1
+ import { EXIT_CODES } from "../../types/index.js";
2
+ import { resolveRepoRoot } from "../../utils/path.js";
3
+ import { exportBrain, getBrainStatus, importBrain, initBrainStore, inspectBrainSummary, listBrainSnapshots, loadLatestBrain, rebuildBrain, searchBrain, showBrainSnapshot, } from "../../core/brain-cli/service.js";
4
+ import { listProposals, reviewProposal, writeBrainProductSnapshot, } from "../../core/brain-product/init.js";
5
+ function printJson(value) {
6
+ process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
7
+ }
8
+ export async function runBrainCommand(options) {
9
+ const root = resolveRepoRoot(options.root ?? process.cwd());
10
+ const json = options.json === true;
11
+ try {
12
+ switch (options.action) {
13
+ case "init": {
14
+ const status = await initBrainStore(root);
15
+ if (json)
16
+ printJson(status);
17
+ else
18
+ process.stdout.write(`Project Brain store ready at ${status.storeRoot}\n`);
19
+ return EXIT_CODES.SUCCESS;
20
+ }
21
+ case "status": {
22
+ const status = await getBrainStatus(root);
23
+ if (json)
24
+ printJson(status);
25
+ else
26
+ process.stdout.write(`Brain status\n root: ${status.root}\n snapshots: ${status.snapshotCount}\n latest: ${status.latestSnapshotId ?? "(none)"}\n`);
27
+ return EXIT_CODES.SUCCESS;
28
+ }
29
+ case "inspect": {
30
+ const summary = await inspectBrainSummary(root);
31
+ if (json)
32
+ printJson(summary);
33
+ else
34
+ process.stdout.write(`Brain inspect\n project: ${String(summary.projectName)}\n snapshot: ${String(summary.snapshotId)}\n claims: ${String(summary.claimCount)}\n components: ${String(summary.componentCount)}\n risks: ${String(summary.riskCount)}\n`);
35
+ return EXIT_CODES.SUCCESS;
36
+ }
37
+ case "rebuild":
38
+ case "update": {
39
+ const brain = await rebuildBrain(root);
40
+ const productSnap = await writeBrainProductSnapshot(root);
41
+ if (json)
42
+ printJson({
43
+ snapshotId: brain.snapshot.id,
44
+ projectName: brain.metadata.projectName,
45
+ productSnapshot: productSnap,
46
+ });
47
+ else
48
+ process.stdout.write(`Rebuilt Project Brain snapshot ${brain.snapshot.id}\nRepository Brain product snapshot: ${productSnap}\n`);
49
+ return EXIT_CODES.SUCCESS;
50
+ }
51
+ case "snapshot": {
52
+ const brain = await rebuildBrain(root);
53
+ const productSnap = await writeBrainProductSnapshot(root);
54
+ if (json)
55
+ printJson({ brainSnapshotId: brain.snapshot.id, productSnapshot: productSnap });
56
+ else
57
+ process.stdout.write(`Brain snapshot ${brain.snapshot.id}\nProduct snapshot ${productSnap}\n`);
58
+ return EXIT_CODES.SUCCESS;
59
+ }
60
+ case "review": {
61
+ if (!options.artifactId || !options.decision) {
62
+ console.error("Error: --artifact <id> and --decision <approved|rejected|...> required");
63
+ return EXIT_CODES.USAGE_ERROR;
64
+ }
65
+ const item = await reviewProposal({
66
+ root,
67
+ artifactId: options.artifactId,
68
+ decision: options.decision,
69
+ ...(options.note ? { note: options.note } : {}),
70
+ });
71
+ if (json)
72
+ printJson(item);
73
+ else
74
+ process.stdout.write(`Reviewed ${item.artifactId} -> ${item.status}\n`);
75
+ return EXIT_CODES.SUCCESS;
76
+ }
77
+ case "proposals": {
78
+ const proposals = await listProposals(root);
79
+ if (json)
80
+ printJson({ proposals });
81
+ else {
82
+ for (const p of proposals) {
83
+ process.stdout.write(`${p.id} [${p.status}] ${p.kind} ${p.path}\n`);
84
+ }
85
+ }
86
+ return EXIT_CODES.SUCCESS;
87
+ }
88
+ case "snapshot-list":
89
+ case "history": {
90
+ const snapshots = await listBrainSnapshots(root);
91
+ if (json)
92
+ printJson({ snapshots });
93
+ else if (snapshots.length === 0) {
94
+ process.stdout.write("No snapshots yet. Run: agentdoctor brain rebuild\n");
95
+ }
96
+ else {
97
+ for (const snap of snapshots) {
98
+ process.stdout.write(`${snap.id} ${snap.createdAt} ${snap.projectName}\n`);
99
+ }
100
+ }
101
+ return EXIT_CODES.SUCCESS;
102
+ }
103
+ case "snapshot-show": {
104
+ if (!options.snapshotId) {
105
+ console.error("Error: snapshot id required");
106
+ return EXIT_CODES.USAGE_ERROR;
107
+ }
108
+ const brain = await showBrainSnapshot(root, options.snapshotId);
109
+ printJson({
110
+ snapshotId: brain.snapshot.id,
111
+ projectName: brain.metadata.projectName,
112
+ claimCount: brain.claims.length,
113
+ componentCount: brain.components.length,
114
+ });
115
+ return EXIT_CODES.SUCCESS;
116
+ }
117
+ case "search": {
118
+ if (!options.query) {
119
+ console.error("Error: search query required");
120
+ return EXIT_CODES.USAGE_ERROR;
121
+ }
122
+ let brain = await loadLatestBrain(root);
123
+ if (!brain)
124
+ brain = await rebuildBrain(root);
125
+ const hits = searchBrain(brain, options.query);
126
+ if (json)
127
+ printJson({ query: options.query, hits });
128
+ else if (hits.length === 0)
129
+ process.stdout.write("No matches.\n");
130
+ else
131
+ for (const hit of hits)
132
+ process.stdout.write(`[${hit.kind}] ${hit.id}: ${hit.text}\n`);
133
+ return EXIT_CODES.SUCCESS;
134
+ }
135
+ case "export": {
136
+ if (!options.file) {
137
+ console.error("Error: --file <path> required for export");
138
+ return EXIT_CODES.USAGE_ERROR;
139
+ }
140
+ const out = await exportBrain(root, options.file, options.snapshotId);
141
+ process.stdout.write(`Exported redacted brain to ${out}\n`);
142
+ return EXIT_CODES.SUCCESS;
143
+ }
144
+ case "import": {
145
+ if (!options.file) {
146
+ console.error("Error: --file <path> required for import");
147
+ return EXIT_CODES.USAGE_ERROR;
148
+ }
149
+ const meta = await importBrain(root, options.file);
150
+ process.stdout.write(`Imported snapshot ${meta.id}\n`);
151
+ return EXIT_CODES.SUCCESS;
152
+ }
153
+ default:
154
+ console.error(`Error: unknown brain action: ${options.action}`);
155
+ return EXIT_CODES.USAGE_ERROR;
156
+ }
157
+ }
158
+ catch (error) {
159
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
160
+ return EXIT_CODES.INTERNAL_ERROR;
161
+ }
162
+ }
@@ -0,0 +1,23 @@
1
+ import { type ExitCode } from "../../types/index.js";
2
+ export declare function runInitCommand(options: {
3
+ root?: string;
4
+ json?: boolean;
5
+ name?: string;
6
+ domain?: string;
7
+ }): Promise<ExitCode>;
8
+ export declare function runV2SurfaceCommand(options: {
9
+ action: string;
10
+ root?: string;
11
+ json?: boolean;
12
+ query?: string;
13
+ title?: string;
14
+ content?: string;
15
+ id?: string;
16
+ decision?: string;
17
+ username?: string;
18
+ password?: string;
19
+ token?: string;
20
+ command?: string;
21
+ mode?: string;
22
+ symbol?: string;
23
+ }): Promise<ExitCode>;
@@ -0,0 +1,232 @@
1
+ import { EXIT_CODES } from "../../types/index.js";
2
+ import { resolveRepoRoot } from "../../utils/path.js";
3
+ import { runProjectInit } from "../../core/brain-product/init.js";
4
+ import { buildIntelligenceGraph } from "../../intelligence/graph/build.js";
5
+ import { analyzeGitIntelligence, deadCodeCategory } from "../../intelligence/git/analyze.js";
6
+ import { buildC4Views } from "../../architecture/c4.js";
7
+ import { createKnowledgeRecord, listKnowledge, retrieveAuthoritative, transitionKnowledge, } from "../../knowledge/store.js";
8
+ import { authenticateLocalDev, authorize, registerLocalDevUser } from "../../team/auth.js";
9
+ import { runControlledCommand } from "../../enforcement/runner.js";
10
+ import { analyzeTestImpact, analyzeRenameImpact, buildRepositoryGraph, } from "../../platform/index.js";
11
+ import { CONTRACTS_VERSION, DEFAULT_FEATURE_FLAGS } from "../../contracts/index.js";
12
+ import { platformFindingToContract } from "../../contracts/adapters.js";
13
+ function printJson(value) {
14
+ process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
15
+ }
16
+ export async function runInitCommand(options) {
17
+ const root = resolveRepoRoot(options.root ?? process.cwd());
18
+ const result = await runProjectInit(root, {
19
+ ...(options.name ? { projectName: options.name } : {}),
20
+ ...(options.domain ? { businessDomain: options.domain } : {}),
21
+ });
22
+ if (options.json)
23
+ printJson(result);
24
+ else {
25
+ process.stdout.write(`AgentDoctor init wrote ${result.artifacts.length} PROPOSED artifacts under .agentdoctor/repository-brain/proposals/\nNone are approved facts until: agentdoctor brain review --artifact <id> --decision approved\n`);
26
+ }
27
+ return EXIT_CODES.SUCCESS;
28
+ }
29
+ export async function runV2SurfaceCommand(options) {
30
+ const root = resolveRepoRoot(options.root ?? process.cwd());
31
+ try {
32
+ switch (options.action) {
33
+ case "graph-ast": {
34
+ const graph = await buildIntelligenceGraph({
35
+ root,
36
+ mode: options.mode ?? "auto",
37
+ });
38
+ if (options.json)
39
+ printJson(graph);
40
+ else
41
+ process.stdout.write(`graph builder=${graph.builder} nodes=${graph.nodes.length} edges=${graph.edges.length} astFiles=${graph.astFilesParsed}\n`);
42
+ return EXIT_CODES.SUCCESS;
43
+ }
44
+ case "health":
45
+ case "git-intel": {
46
+ const report = await analyzeGitIntelligence(root);
47
+ if (options.json)
48
+ printJson(report);
49
+ else
50
+ process.stdout.write(`git-intel hotspots=${report.hotspots.length} coChanges=${report.coChanges.length} git=${report.gitAvailable}\n`);
51
+ return EXIT_CODES.SUCCESS;
52
+ }
53
+ case "c4": {
54
+ const graph = await buildIntelligenceGraph({ root, mode: "auto" });
55
+ const views = buildC4Views(graph);
56
+ if (options.json)
57
+ printJson({ views, featureFlag: DEFAULT_FEATURE_FLAGS.architectureC4 });
58
+ else
59
+ process.stdout.write(`C4 views: ${views.map((v) => v.level).join(", ")}\n`);
60
+ return EXIT_CODES.SUCCESS;
61
+ }
62
+ case "dead-code-demo": {
63
+ const sample = deadCodeCategory({
64
+ exported: true,
65
+ referenced: false,
66
+ frameworkEntry: false,
67
+ dynamicHint: false,
68
+ });
69
+ if (options.json)
70
+ printJson(sample);
71
+ else
72
+ process.stdout.write(`${sample.category} confidence=${sample.confidence}\n`);
73
+ return EXIT_CODES.SUCCESS;
74
+ }
75
+ case "knowledge-create": {
76
+ if (!options.title || !options.content) {
77
+ console.error("Error: --title and --content required");
78
+ return EXIT_CODES.USAGE_ERROR;
79
+ }
80
+ const rec = await createKnowledgeRecord({
81
+ root,
82
+ title: options.title,
83
+ content: options.content,
84
+ status: "draft",
85
+ });
86
+ if (options.json)
87
+ printJson(rec);
88
+ else
89
+ process.stdout.write(`Created draft knowledge ${rec.id}\n`);
90
+ return EXIT_CODES.SUCCESS;
91
+ }
92
+ case "knowledge-list": {
93
+ const list = await listKnowledge(root);
94
+ if (options.json)
95
+ printJson({ records: list });
96
+ else
97
+ for (const r of list)
98
+ process.stdout.write(`${r.id} [${r.status}] ${r.title}\n`);
99
+ return EXIT_CODES.SUCCESS;
100
+ }
101
+ case "knowledge-transition": {
102
+ if (!options.id || !options.decision) {
103
+ console.error("Error: --id and --decision required");
104
+ return EXIT_CODES.USAGE_ERROR;
105
+ }
106
+ const rec = await transitionKnowledge({
107
+ root,
108
+ id: options.id,
109
+ to: options.decision,
110
+ });
111
+ if (options.json)
112
+ printJson(rec);
113
+ else
114
+ process.stdout.write(`${rec.id} -> ${rec.status}\n`);
115
+ return EXIT_CODES.SUCCESS;
116
+ }
117
+ case "knowledge-query": {
118
+ const list = await listKnowledge(root);
119
+ const result = retrieveAuthoritative(list, options.query ?? "");
120
+ if (options.json)
121
+ printJson(result);
122
+ else
123
+ process.stdout.write(result.abstain
124
+ ? `ABSTAIN: ${result.reason}\n`
125
+ : `AUTHORITATIVE: ${result.record?.title}\n`);
126
+ return EXIT_CODES.SUCCESS;
127
+ }
128
+ case "team-register": {
129
+ if (!options.username || !options.password) {
130
+ console.error("Error: --username and --password required (local-dev auth only)");
131
+ return EXIT_CODES.USAGE_ERROR;
132
+ }
133
+ const user = await registerLocalDevUser({
134
+ root,
135
+ username: options.username,
136
+ password: options.password,
137
+ role: "admin",
138
+ });
139
+ if (options.json)
140
+ printJson({ ...user, notice: "local-dev auth — not enterprise SSO" });
141
+ else
142
+ process.stdout.write(`Registered local-dev user ${user.username} (${user.id})\n`);
143
+ return EXIT_CODES.SUCCESS;
144
+ }
145
+ case "team-login": {
146
+ if (!options.username || !options.password) {
147
+ console.error("Error: --username and --password required");
148
+ return EXIT_CODES.USAGE_ERROR;
149
+ }
150
+ const session = await authenticateLocalDev({
151
+ root,
152
+ username: options.username,
153
+ password: options.password,
154
+ });
155
+ if (options.json)
156
+ printJson({ ...session, notice: "local-dev session — not enterprise SSO" });
157
+ else
158
+ process.stdout.write(`token=${session.token} expires=${session.expiresAt}\n`);
159
+ return EXIT_CODES.SUCCESS;
160
+ }
161
+ case "team-authorize": {
162
+ if (!options.token) {
163
+ console.error("Error: --token required");
164
+ return EXIT_CODES.USAGE_ERROR;
165
+ }
166
+ const result = await authorize(root, options.token, "member");
167
+ if (options.json)
168
+ printJson(result);
169
+ else
170
+ process.stdout.write(`${result.ok ? "OK" : "DENIED"}: ${result.reason}\n`);
171
+ return EXIT_CODES.SUCCESS;
172
+ }
173
+ case "enforce-check": {
174
+ if (!options.command) {
175
+ console.error("Error: --command required");
176
+ return EXIT_CODES.USAGE_ERROR;
177
+ }
178
+ const result = await runControlledCommand({ root, command: options.command });
179
+ if (options.json)
180
+ printJson(result);
181
+ else
182
+ process.stdout.write(`enforced=${result.enforced} status=${result.decision.executionStatus} decision=${result.decision.decision}\n`);
183
+ return EXIT_CODES.SUCCESS;
184
+ }
185
+ case "impact": {
186
+ const impact = await analyzeTestImpact(root);
187
+ if (options.json)
188
+ printJson({ kind: "test-impact", contractsVersion: CONTRACTS_VERSION, impact });
189
+ else
190
+ process.stdout.write(`impact recommendedTests=${impact.recommendedTests.length}\n`);
191
+ return EXIT_CODES.SUCCESS;
192
+ }
193
+ case "refactor-impact": {
194
+ if (!options.symbol) {
195
+ console.error("Error: --symbol required");
196
+ return EXIT_CODES.USAGE_ERROR;
197
+ }
198
+ const graph = await buildRepositoryGraph(root);
199
+ const impact = await analyzeRenameImpact({ root, symbol: options.symbol, graph });
200
+ if (options.json)
201
+ printJson(impact);
202
+ else
203
+ process.stdout.write(`refactor ${options.symbol} affected=${impact.affectedFiles.length}\n`);
204
+ return EXIT_CODES.SUCCESS;
205
+ }
206
+ case "contracts-demo": {
207
+ const sample = platformFindingToContract({
208
+ id: "demo",
209
+ module: "demo",
210
+ severity: "info",
211
+ title: "demo",
212
+ message: "demo",
213
+ recommendation: "n/a",
214
+ confidence: 1,
215
+ evidence: [{ kind: "verified", detail: "ok" }],
216
+ });
217
+ if (options.json)
218
+ printJson({ contractsVersion: CONTRACTS_VERSION, sample });
219
+ else
220
+ process.stdout.write(`contracts ${CONTRACTS_VERSION}\n`);
221
+ return EXIT_CODES.SUCCESS;
222
+ }
223
+ default:
224
+ console.error(`Error: unknown action ${options.action}`);
225
+ return EXIT_CODES.USAGE_ERROR;
226
+ }
227
+ }
228
+ catch (error) {
229
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
230
+ return EXIT_CODES.INTERNAL_ERROR;
231
+ }
232
+ }
@@ -13,7 +13,7 @@ export async function runExplainCommand(ruleId) {
13
13
  const rule = getRuleById(ruleId.trim());
14
14
  if (!rule) {
15
15
  console.error(`Error: unknown rule id "${ruleId}"`);
16
- console.error("Run agentdoctor explain with a documented rule from docs/rules.md");
16
+ console.error("Run agentdoctor explain with a documented rule from docs/reference/rules.md");
17
17
  return EXIT_CODES.USAGE_ERROR;
18
18
  }
19
19
  const agents = rule.affectedAgents?.map((id) => AGENT_DISPLAY_NAMES[id] ?? id).join(", ") ??
@@ -1,7 +1,7 @@
1
1
  import { EXIT_CODES } from "../../types/index.js";
2
2
  import { isDirectory } from "../../utils/fs.js";
3
3
  import { resolveRepoRoot } from "../../utils/path.js";
4
- import { applyFixPlan, readClaudeSettings, readCodexConfig, readCursorignore, } from "../../core/fix/apply.js";
4
+ import { applyFixPlan, readClaudeSettings, readCodexConfig, readCursorignore, readSimpleIgnore, } from "../../core/fix/apply.js";
5
5
  import { buildFixPlan } from "../../core/fix/plan.js";
6
6
  import { renderFixPlanTerminal } from "../../core/fix/render.js";
7
7
  import { runFix } from "../../core/fix/run.js";
@@ -25,14 +25,22 @@ export async function runFixCommand(options) {
25
25
  const cursorContent = await readCursorignore(plan.root);
26
26
  const claudeSettingsContent = await readClaudeSettings(plan.root);
27
27
  const codexConfigContent = await readCodexConfig(plan.root);
28
+ const geminiignoreContent = await readSimpleIgnore(plan.root, ".geminiignore");
29
+ const aiderignoreContent = await readSimpleIgnore(plan.root, ".aiderignore");
28
30
  const applyResult = await applyFixPlan(plan, { dryRun: true });
29
31
  process.stdout.write(renderFixPlanTerminal(plan, {
30
32
  dryRun: true,
31
33
  cursorContent,
32
34
  claudeSettingsContent,
33
35
  codexConfigContent,
36
+ geminiignoreContent,
37
+ aiderignoreContent,
34
38
  applyResult,
35
39
  }));
40
+ if (applyResult.error) {
41
+ console.error(`Error: ${applyResult.error}`);
42
+ return EXIT_CODES.USAGE_ERROR;
43
+ }
36
44
  return EXIT_CODES.SUCCESS;
37
45
  }
38
46
  const { plan, applyResult, cancelled } = await runFix({
@@ -47,13 +55,23 @@ export async function runFixCommand(options) {
47
55
  const cursorContentAfter = await readCursorignore(plan.root);
48
56
  const claudeSettingsAfter = await readClaudeSettings(plan.root);
49
57
  const codexConfigAfter = await readCodexConfig(plan.root);
58
+ const geminiignoreAfter = await readSimpleIgnore(plan.root, ".geminiignore");
59
+ const aiderignoreAfter = await readSimpleIgnore(plan.root, ".aiderignore");
50
60
  process.stdout.write(renderFixPlanTerminal(plan, {
51
61
  dryRun: false,
52
62
  cursorContent: cursorContentAfter,
53
63
  claudeSettingsContent: claudeSettingsAfter,
54
64
  codexConfigContent: codexConfigAfter,
65
+ geminiignoreContent: geminiignoreAfter,
66
+ aiderignoreContent: aiderignoreAfter,
55
67
  applyResult,
56
68
  }));
69
+ if (applyResult.error) {
70
+ console.error(`Error: ${applyResult.error}`);
71
+ return isFixConfigOrPermissionError(applyResult.error)
72
+ ? EXIT_CODES.USAGE_ERROR
73
+ : EXIT_CODES.INTERNAL_ERROR;
74
+ }
57
75
  if (applyResult.writtenFiles.length > 0) {
58
76
  const after = await scan({ cwd: target });
59
77
  const remainingSafeContext = after.findings.filter((f) => f.fixability === "safe" &&
@@ -73,8 +91,10 @@ export async function runFixCommand(options) {
73
91
  }
74
92
  function isFixConfigOrPermissionError(message) {
75
93
  return (message.includes("refusing") ||
94
+ message.includes("not allowlisted") ||
76
95
  message.includes("not valid JSON") ||
77
96
  message.includes("EACCES") ||
78
97
  message.includes("EPERM") ||
79
- message.includes("permission denied"));
98
+ message.includes("permission denied") ||
99
+ message.includes("symlink"));
80
100
  }
@@ -0,0 +1,8 @@
1
+ import type { ExitCode } from "../../types/index.js";
2
+ /**
3
+ * Start combined AgentDoctor MCP (Brain tools + intelligence tools) over STDIO.
4
+ */
5
+ export declare function runMcpCommand(options: {
6
+ root: string;
7
+ buildIfMissing?: boolean;
8
+ }): Promise<ExitCode>;
@@ -0,0 +1,14 @@
1
+ import { EXIT_CODES } from "../../types/index.js";
2
+ import { resolveRepoRoot } from "../../utils/path.js";
3
+ import { runAgentDoctorMcpStdio } from "../../mcp/agentdoctor/server.js";
4
+ /**
5
+ * Start combined AgentDoctor MCP (Brain tools + intelligence tools) over STDIO.
6
+ */
7
+ export async function runMcpCommand(options) {
8
+ const root = resolveRepoRoot(options.root);
9
+ await runAgentDoctorMcpStdio({
10
+ root,
11
+ buildIfMissing: options.buildIfMissing !== false,
12
+ });
13
+ return EXIT_CODES.SUCCESS;
14
+ }
@@ -0,0 +1,15 @@
1
+ import { type ExitCode } from "../../types/index.js";
2
+ export declare function runPlatformCommand(options: {
3
+ action: string;
4
+ root?: string;
5
+ json?: boolean;
6
+ query?: string;
7
+ symbol?: string;
8
+ left?: string;
9
+ right?: string;
10
+ command?: string;
11
+ path?: string;
12
+ actionType?: string;
13
+ sessionId?: string;
14
+ failClosed?: boolean;
15
+ }): Promise<ExitCode>;