@praneeth_54/agentdoctor 1.1.1 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (224) hide show
  1. package/CHANGELOG.md +164 -7
  2. package/README.md +263 -513
  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/architecture/contract.d.ts +64 -0
  24. package/dist/architecture/contract.js +373 -0
  25. package/dist/assurance/change.d.ts +211 -0
  26. package/dist/assurance/change.js +624 -0
  27. package/dist/assurance/proof.d.ts +136 -0
  28. package/dist/assurance/proof.js +338 -0
  29. package/dist/auth/index.d.ts +81 -0
  30. package/dist/auth/index.js +130 -0
  31. package/dist/auth/rbac.d.ts +9 -0
  32. package/dist/auth/rbac.js +26 -0
  33. package/dist/cli/commands/architecture.d.ts +6 -0
  34. package/dist/cli/commands/architecture.js +109 -0
  35. package/dist/cli/commands/assurance.d.ts +67 -0
  36. package/dist/cli/commands/assurance.js +210 -0
  37. package/dist/cli/commands/brain.d.ts +12 -0
  38. package/dist/cli/commands/brain.js +162 -0
  39. package/dist/cli/commands/complete.d.ts +23 -0
  40. package/dist/cli/commands/complete.js +232 -0
  41. package/dist/cli/commands/explain.js +1 -1
  42. package/dist/cli/commands/fix.js +22 -2
  43. package/dist/cli/commands/mcp.d.ts +8 -0
  44. package/dist/cli/commands/mcp.js +14 -0
  45. package/dist/cli/commands/platform.d.ts +17 -0
  46. package/dist/cli/commands/platform.js +195 -0
  47. package/dist/cli/commands/policy-graph-run.d.ts +40 -0
  48. package/dist/cli/commands/policy-graph-run.js +200 -0
  49. package/dist/cli/commands/v2.d.ts +55 -0
  50. package/dist/cli/commands/v2.js +278 -0
  51. package/dist/cli/commands/workspace.d.ts +9 -0
  52. package/dist/cli/commands/workspace.js +82 -0
  53. package/dist/cli/program.js +1328 -3
  54. package/dist/constants.d.ts +3 -2
  55. package/dist/constants.js +5 -1
  56. package/dist/contracts/adapters.d.ts +7 -0
  57. package/dist/contracts/adapters.js +60 -0
  58. package/dist/contracts/index.d.ts +162 -0
  59. package/dist/contracts/index.js +18 -0
  60. package/dist/core/baseline/store.d.ts +48 -0
  61. package/dist/core/baseline/store.js +163 -0
  62. package/dist/core/brain-cli/service.d.ts +26 -0
  63. package/dist/core/brain-cli/service.js +175 -0
  64. package/dist/core/brain-product/init.d.ts +45 -0
  65. package/dist/core/brain-product/init.js +235 -0
  66. package/dist/core/changes/analyze.d.ts +41 -0
  67. package/dist/core/changes/analyze.js +223 -0
  68. package/dist/core/context-health/analyze.d.ts +15 -0
  69. package/dist/core/context-health/analyze.js +234 -0
  70. package/dist/core/fix/apply.d.ts +21 -4
  71. package/dist/core/fix/apply.js +118 -18
  72. package/dist/core/fix/backup.d.ts +30 -0
  73. package/dist/core/fix/backup.js +193 -0
  74. package/dist/core/fix/plan.d.ts +6 -3
  75. package/dist/core/fix/plan.js +113 -9
  76. package/dist/core/fix/render.d.ts +2 -0
  77. package/dist/core/fix/render.js +36 -21
  78. package/dist/core/fix/run.js +5 -1
  79. package/dist/core/fix/safe-target.d.ts +14 -0
  80. package/dist/core/fix/safe-target.js +81 -0
  81. package/dist/core/fix/types.d.ts +12 -1
  82. package/dist/core/fix/types.js +2 -0
  83. package/dist/core/fix/writers/claude-settings.js +3 -1
  84. package/dist/core/fix/writers/codex-config.d.ts +3 -0
  85. package/dist/core/fix/writers/codex-config.js +24 -3
  86. package/dist/core/fix/writers/cursorignore.js +3 -1
  87. package/dist/core/fix/writers/simple-ignore.d.ts +16 -0
  88. package/dist/core/fix/writers/simple-ignore.js +68 -0
  89. package/dist/core/monorepo/detect.d.ts +13 -0
  90. package/dist/core/monorepo/detect.js +121 -0
  91. package/dist/core/rules/build-context.js +22 -3
  92. package/dist/core/rules/context/generated-directory.js +11 -1
  93. package/dist/core/rules/context/large-instruction-file.js +6 -0
  94. package/dist/core/rules/context/large-log-file.js +16 -1
  95. package/dist/core/rules/ignore.d.ts +11 -1
  96. package/dist/core/rules/ignore.js +22 -1
  97. package/dist/core/rules/instructions/empty-instructions.js +7 -0
  98. package/dist/core/rules/security/env-file-exposure.js +23 -2
  99. package/dist/core/rules/security/private-key-file.js +1 -1
  100. package/dist/core/scanner/scan.js +1 -1
  101. package/dist/core/schemas/validate.d.ts +6 -0
  102. package/dist/core/schemas/validate.js +62 -0
  103. package/dist/core/scoring/compute-scores.d.ts +1 -1
  104. package/dist/core/scoring/compute-scores.js +5 -1
  105. package/dist/core/scoring/placeholder.d.ts +1 -1
  106. package/dist/core/scoring/placeholder.js +5 -1
  107. package/dist/core/secrets/scan.d.ts +29 -0
  108. package/dist/core/secrets/scan.js +172 -0
  109. package/dist/coverage/cobertura.d.ts +6 -0
  110. package/dist/coverage/cobertura.js +52 -0
  111. package/dist/coverage/istanbul.d.ts +7 -0
  112. package/dist/coverage/istanbul.js +134 -0
  113. package/dist/coverage/lcov.d.ts +7 -0
  114. package/dist/coverage/lcov.js +59 -0
  115. package/dist/coverage/load.d.ts +9 -0
  116. package/dist/coverage/load.js +39 -0
  117. package/dist/coverage/types.d.ts +23 -0
  118. package/dist/coverage/types.js +5 -0
  119. package/dist/dashboard/server.d.ts +24 -0
  120. package/dist/dashboard/server.js +402 -0
  121. package/dist/enforcement/runner.d.ts +67 -0
  122. package/dist/enforcement/runner.js +460 -0
  123. package/dist/index.d.ts +34 -0
  124. package/dist/index.js +25 -0
  125. package/dist/integrations/github/pr-analyze.d.ts +28 -0
  126. package/dist/integrations/github/pr-analyze.js +98 -0
  127. package/dist/integrations/local-ai/provider.d.ts +45 -0
  128. package/dist/integrations/local-ai/provider.js +104 -0
  129. package/dist/intelligence/git/analyze.d.ts +34 -0
  130. package/dist/intelligence/git/analyze.js +136 -0
  131. package/dist/intelligence/graph/build.d.ts +15 -0
  132. package/dist/intelligence/graph/build.js +259 -0
  133. package/dist/intelligence/graph/incremental.d.ts +68 -0
  134. package/dist/intelligence/graph/incremental.js +234 -0
  135. package/dist/intelligence/resolve/imports.d.ts +36 -0
  136. package/dist/intelligence/resolve/imports.js +245 -0
  137. package/dist/knowledge/store.d.ts +25 -0
  138. package/dist/knowledge/store.js +91 -0
  139. package/dist/languages/go.d.ts +9 -0
  140. package/dist/languages/go.js +45 -0
  141. package/dist/languages/index.d.ts +11 -0
  142. package/dist/languages/index.js +76 -0
  143. package/dist/languages/php.d.ts +7 -0
  144. package/dist/languages/php.js +193 -0
  145. package/dist/languages/python.d.ts +7 -0
  146. package/dist/languages/python.js +150 -0
  147. package/dist/languages/types.d.ts +42 -0
  148. package/dist/languages/types.js +26 -0
  149. package/dist/languages/typescript.d.ts +3 -0
  150. package/dist/languages/typescript.js +95 -0
  151. package/dist/mcp/agentdoctor/server.d.ts +16 -0
  152. package/dist/mcp/agentdoctor/server.js +67 -0
  153. package/dist/mcp/brain/session.js +7 -7
  154. package/dist/mcp/intelligence/handlers.d.ts +16 -0
  155. package/dist/mcp/intelligence/handlers.js +364 -0
  156. package/dist/mcp/intelligence/path-safety.d.ts +9 -0
  157. package/dist/mcp/intelligence/path-safety.js +63 -0
  158. package/dist/mcp/intelligence/registry.d.ts +8 -0
  159. package/dist/mcp/intelligence/registry.js +228 -0
  160. package/dist/ops/health.d.ts +18 -0
  161. package/dist/ops/health.js +51 -0
  162. package/dist/platform/ai-quality/analyze.d.ts +9 -0
  163. package/dist/platform/ai-quality/analyze.js +65 -0
  164. package/dist/platform/architecture/drift.d.ts +17 -0
  165. package/dist/platform/architecture/drift.js +74 -0
  166. package/dist/platform/auth/local.d.ts +15 -0
  167. package/dist/platform/auth/local.js +58 -0
  168. package/dist/platform/context-security/analyze.d.ts +6 -0
  169. package/dist/platform/context-security/analyze.js +97 -0
  170. package/dist/platform/firewall/evaluate.d.ts +57 -0
  171. package/dist/platform/firewall/evaluate.js +359 -0
  172. package/dist/platform/graph/build.d.ts +6 -0
  173. package/dist/platform/graph/build.js +189 -0
  174. package/dist/platform/health/analyze.d.ts +7 -0
  175. package/dist/platform/health/analyze.js +192 -0
  176. package/dist/platform/index.d.ts +27 -0
  177. package/dist/platform/index.js +89 -0
  178. package/dist/platform/knowledge/analyze.d.ts +20 -0
  179. package/dist/platform/knowledge/analyze.js +96 -0
  180. package/dist/platform/provenance/build.d.ts +44 -0
  181. package/dist/platform/provenance/build.js +65 -0
  182. package/dist/platform/readiness/scorecard.d.ts +5 -0
  183. package/dist/platform/readiness/scorecard.js +98 -0
  184. package/dist/platform/refactor/impact.d.ts +20 -0
  185. package/dist/platform/refactor/impact.js +60 -0
  186. package/dist/platform/reports/export.d.ts +12 -0
  187. package/dist/platform/reports/export.js +92 -0
  188. package/dist/platform/security/redact.d.ts +15 -0
  189. package/dist/platform/security/redact.js +87 -0
  190. package/dist/platform/sessions/store.d.ts +32 -0
  191. package/dist/platform/sessions/store.js +97 -0
  192. package/dist/platform/store.d.ts +6 -0
  193. package/dist/platform/store.js +57 -0
  194. package/dist/platform/test-impact/analyze.d.ts +50 -0
  195. package/dist/platform/test-impact/analyze.js +350 -0
  196. package/dist/platform/time-machine/compare.d.ts +17 -0
  197. package/dist/platform/time-machine/compare.js +62 -0
  198. package/dist/platform/tokens/plan.d.ts +28 -0
  199. package/dist/platform/tokens/plan.js +108 -0
  200. package/dist/platform/types.d.ts +120 -0
  201. package/dist/platform/types.js +5 -0
  202. package/dist/plugins/runtime.d.ts +36 -0
  203. package/dist/plugins/runtime.js +101 -0
  204. package/dist/plugins/sdk.d.ts +29 -0
  205. package/dist/plugins/sdk.js +114 -0
  206. package/dist/policy/compose.d.ts +34 -0
  207. package/dist/policy/compose.js +118 -0
  208. package/dist/policy/packs.d.ts +9 -0
  209. package/dist/policy/packs.js +91 -0
  210. package/dist/reporters/terminal/report.js +38 -5
  211. package/dist/security/paths.d.ts +21 -0
  212. package/dist/security/paths.js +105 -0
  213. package/dist/storage/postgres.d.ts +26 -0
  214. package/dist/storage/postgres.js +90 -0
  215. package/dist/storage/provider.d.ts +30 -0
  216. package/dist/storage/provider.js +109 -0
  217. package/dist/storage/sqlite.d.ts +33 -0
  218. package/dist/storage/sqlite.js +77 -0
  219. package/dist/team/auth.d.ts +35 -0
  220. package/dist/team/auth.js +70 -0
  221. package/dist/types/index.d.ts +5 -1
  222. package/dist/workspace/index.d.ts +69 -0
  223. package/dist/workspace/index.js +220 -0
  224. package/package.json +15 -5
@@ -0,0 +1,228 @@
1
+ import { handleArchitectureCheckTool, handleArchitectureTool, handleCallGraphLookup, handleChangeAnalyzeTool, handleCodeHealthTool, handleCodebaseSearch, handleDependencyLookup, handleEvidenceInspectTool, handleGraphQueryTool, handleKnowledgeTool, handlePolicyEvalTool, handleProofInspectTool, handleRefactorImpactTool, handleRepoOverview, handleSymbolLookup, handleTestImpactTool, } from "./handlers.js";
2
+ export const INTELLIGENCE_MCP_TOOL_NAMES = [
3
+ "repo_overview",
4
+ "codebase_search",
5
+ "symbol_lookup",
6
+ "dependency_lookup",
7
+ "call_graph_lookup",
8
+ "test_impact",
9
+ "refactor_impact",
10
+ "code_health",
11
+ "architecture_info",
12
+ "architecture_check",
13
+ "knowledge_retrieve",
14
+ "policy_evaluate",
15
+ "change_analyze",
16
+ "proof_inspect",
17
+ "evidence_inspect",
18
+ "graph_query",
19
+ ];
20
+ const emptyObjectSchema = {
21
+ type: "object",
22
+ properties: {},
23
+ additionalProperties: false,
24
+ };
25
+ export function listIntelligenceMcpTools() {
26
+ return [
27
+ {
28
+ name: "repo_overview",
29
+ description: "READ: Repository intelligence overview (graph builder, node/edge counts, limitations). No shell execution.",
30
+ inputSchema: emptyObjectSchema,
31
+ },
32
+ {
33
+ name: "codebase_search",
34
+ description: "READ: Search graph nodes by label/path substring. Bounded to 50 hits.",
35
+ inputSchema: {
36
+ type: "object",
37
+ properties: { query: { type: "string" } },
38
+ required: ["query"],
39
+ additionalProperties: false,
40
+ },
41
+ },
42
+ {
43
+ name: "symbol_lookup",
44
+ description: "READ: Lookup symbols/functions/classes/interfaces by name.",
45
+ inputSchema: {
46
+ type: "object",
47
+ properties: { name: { type: "string" } },
48
+ required: ["name"],
49
+ additionalProperties: false,
50
+ },
51
+ },
52
+ {
53
+ name: "dependency_lookup",
54
+ description: "READ: Edges connected to a graph node (id/path/label).",
55
+ inputSchema: {
56
+ type: "object",
57
+ properties: { target: { type: "string" } },
58
+ required: ["target"],
59
+ additionalProperties: false,
60
+ },
61
+ },
62
+ {
63
+ name: "call_graph_lookup",
64
+ description: "READ: Call edges involving a symbol (best-effort; AST when available).",
65
+ inputSchema: {
66
+ type: "object",
67
+ properties: { symbol: { type: "string" } },
68
+ required: ["symbol"],
69
+ additionalProperties: false,
70
+ },
71
+ },
72
+ {
73
+ name: "test_impact",
74
+ description: "READ: Test-impact analysis for current git changes. Coverage-backed mapping not bundled.",
75
+ inputSchema: emptyObjectSchema,
76
+ },
77
+ {
78
+ name: "refactor_impact",
79
+ description: "READ: Rename/refactor blast radius for a symbol.",
80
+ inputSchema: {
81
+ type: "object",
82
+ properties: { symbol: { type: "string" } },
83
+ required: ["symbol"],
84
+ additionalProperties: false,
85
+ },
86
+ },
87
+ {
88
+ name: "code_health",
89
+ description: "READ: Git hotspot / engineering intelligence with method disclosure.",
90
+ inputSchema: emptyObjectSchema,
91
+ },
92
+ {
93
+ name: "architecture_info",
94
+ description: "READ: C4-style views inferred from graph evidence (labeled proposed/inferred).",
95
+ inputSchema: emptyObjectSchema,
96
+ },
97
+ {
98
+ name: "architecture_check",
99
+ description: "READ: Check import edges against .agentdoctor/architecture.json|.yml contract (repo-local).",
100
+ inputSchema: emptyObjectSchema,
101
+ },
102
+ {
103
+ name: "knowledge_retrieve",
104
+ description: "READ: Governed knowledge retrieval. Abstains when no approved authoritative record matches.",
105
+ inputSchema: {
106
+ type: "object",
107
+ properties: { query: { type: "string" } },
108
+ additionalProperties: false,
109
+ },
110
+ },
111
+ {
112
+ name: "policy_evaluate",
113
+ description: "READ: Evaluate a shell command against local policy. Never executes. Always executionResult=not-executed.",
114
+ inputSchema: {
115
+ type: "object",
116
+ properties: { command: { type: "string" } },
117
+ required: ["command"],
118
+ additionalProperties: false,
119
+ },
120
+ },
121
+ {
122
+ name: "change_analyze",
123
+ description: "READ: Change assurance assessment (evidence/heuristics). Never claims verified engineering correctness.",
124
+ inputSchema: {
125
+ type: "object",
126
+ properties: {
127
+ since: { type: "string" },
128
+ changeId: { type: "string" },
129
+ coveragePath: { type: "string", description: "Repo-relative coverage file path" },
130
+ },
131
+ additionalProperties: false,
132
+ },
133
+ },
134
+ {
135
+ name: "proof_inspect",
136
+ description: "READ: Inspect ChangeProof by proofId or changeId (hash integrity metadata only).",
137
+ inputSchema: {
138
+ type: "object",
139
+ properties: { id: { type: "string" } },
140
+ required: ["id"],
141
+ additionalProperties: false,
142
+ },
143
+ },
144
+ {
145
+ name: "evidence_inspect",
146
+ description: "READ: Inspect change evidence bundle artifacts for a changeId.",
147
+ inputSchema: {
148
+ type: "object",
149
+ properties: { changeId: { type: "string" } },
150
+ required: ["changeId"],
151
+ additionalProperties: false,
152
+ },
153
+ },
154
+ {
155
+ name: "graph_query",
156
+ description: "READ: Substring query over intelligence graph nodes/edges. Optional kind/path filters; path validated.",
157
+ inputSchema: {
158
+ type: "object",
159
+ properties: {
160
+ query: { type: "string" },
161
+ kind: { type: "string" },
162
+ path: { type: "string" },
163
+ limit: { type: "number" },
164
+ },
165
+ required: ["query"],
166
+ additionalProperties: false,
167
+ },
168
+ },
169
+ ];
170
+ }
171
+ export async function invokeIntelligenceMcpTool(root, name, args) {
172
+ try {
173
+ switch (name) {
174
+ case "repo_overview":
175
+ return { structured: await handleRepoOverview(root), isError: false };
176
+ case "codebase_search":
177
+ return { structured: await handleCodebaseSearch(root, args), isError: false };
178
+ case "symbol_lookup":
179
+ return { structured: await handleSymbolLookup(root, args), isError: false };
180
+ case "dependency_lookup":
181
+ return { structured: await handleDependencyLookup(root, args), isError: false };
182
+ case "call_graph_lookup":
183
+ return { structured: await handleCallGraphLookup(root, args), isError: false };
184
+ case "test_impact":
185
+ return { structured: await handleTestImpactTool(root), isError: false };
186
+ case "refactor_impact":
187
+ return { structured: await handleRefactorImpactTool(root, args), isError: false };
188
+ case "code_health":
189
+ return { structured: await handleCodeHealthTool(root), isError: false };
190
+ case "architecture_info":
191
+ return { structured: await handleArchitectureTool(root), isError: false };
192
+ case "architecture_check":
193
+ return { structured: await handleArchitectureCheckTool(root), isError: false };
194
+ case "knowledge_retrieve":
195
+ return { structured: await handleKnowledgeTool(root, args), isError: false };
196
+ case "policy_evaluate":
197
+ return { structured: await handlePolicyEvalTool(root, args), isError: false };
198
+ case "change_analyze":
199
+ return { structured: await handleChangeAnalyzeTool(root, args), isError: false };
200
+ case "proof_inspect":
201
+ return { structured: await handleProofInspectTool(root, args), isError: false };
202
+ case "evidence_inspect":
203
+ return { structured: await handleEvidenceInspectTool(root, args), isError: false };
204
+ case "graph_query":
205
+ return { structured: await handleGraphQueryTool(root, args), isError: false };
206
+ default:
207
+ return {
208
+ structured: {
209
+ ok: false,
210
+ error: { code: "invalid_argument", message: `unknown intelligence tool: ${name}` },
211
+ },
212
+ isError: true,
213
+ };
214
+ }
215
+ }
216
+ catch (error) {
217
+ return {
218
+ structured: {
219
+ ok: false,
220
+ error: {
221
+ code: "internal",
222
+ message: error instanceof Error ? error.message : String(error),
223
+ },
224
+ },
225
+ isError: true,
226
+ };
227
+ }
228
+ }
@@ -0,0 +1,18 @@
1
+ import { DEFAULT_FEATURE_FLAGS } from "../contracts/index.js";
2
+ export interface OpsHealthReport {
3
+ ok: boolean;
4
+ version: string;
5
+ contractsVersion: string;
6
+ root: string;
7
+ featureFlags: typeof DEFAULT_FEATURE_FLAGS;
8
+ checks: Array<{
9
+ id: string;
10
+ ok: boolean;
11
+ detail: string;
12
+ }>;
13
+ timestamp: string;
14
+ }
15
+ /**
16
+ * Local ops health probe — no network calls, no agent interception.
17
+ */
18
+ export declare function collectOpsHealth(rootInput?: string): Promise<OpsHealthReport>;
@@ -0,0 +1,51 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { PACKAGE_VERSION } from "../constants.js";
4
+ import { CONTRACTS_VERSION, DEFAULT_FEATURE_FLAGS } from "../contracts/index.js";
5
+ import { resolveRepoRoot } from "../utils/path.js";
6
+ /**
7
+ * Local ops health probe — no network calls, no agent interception.
8
+ */
9
+ export async function collectOpsHealth(rootInput) {
10
+ const root = resolveRepoRoot(rootInput ?? process.cwd());
11
+ const checks = [];
12
+ try {
13
+ await fs.access(root);
14
+ checks.push({ id: "root-readable", ok: true, detail: root });
15
+ }
16
+ catch {
17
+ checks.push({ id: "root-readable", ok: false, detail: "cannot access root" });
18
+ }
19
+ const agentdoctorDir = path.join(root, ".agentdoctor");
20
+ try {
21
+ await fs.access(agentdoctorDir);
22
+ checks.push({ id: "agentdoctor-dir", ok: true, detail: ".agentdoctor present" });
23
+ }
24
+ catch {
25
+ checks.push({
26
+ id: "agentdoctor-dir",
27
+ ok: true,
28
+ detail: ".agentdoctor absent (created on first scan/init)",
29
+ });
30
+ }
31
+ checks.push({
32
+ id: "evaluate-only-firewall",
33
+ ok: true,
34
+ detail: "Action Policy Evaluator remains evaluate-only by default",
35
+ });
36
+ checks.push({
37
+ id: "local-dev-auth",
38
+ ok: true,
39
+ detail: "Team auth is local-dev only unless external IdP is configured (not bundled)",
40
+ });
41
+ const ok = checks.every((c) => c.ok);
42
+ return {
43
+ ok,
44
+ version: PACKAGE_VERSION,
45
+ contractsVersion: CONTRACTS_VERSION,
46
+ root,
47
+ featureFlags: DEFAULT_FEATURE_FLAGS,
48
+ checks,
49
+ timestamp: new Date().toISOString(),
50
+ };
51
+ }
@@ -0,0 +1,9 @@
1
+ import type { PlatformFinding } from "../types.js";
2
+ /**
3
+ * Module M — AI code quality analysis over git diff (when available).
4
+ * AI attribution is unknown unless provided.
5
+ */
6
+ export declare function analyzeAiChangeQuality(options: {
7
+ root: string;
8
+ aiAssisted?: boolean | "unknown";
9
+ }): Promise<PlatformFinding[]>;
@@ -0,0 +1,65 @@
1
+ import { createHash } from "node:crypto";
2
+ import { spawnSync } from "node:child_process";
3
+ import { resolveRepoRoot } from "../../utils/path.js";
4
+ /**
5
+ * Module M — AI code quality analysis over git diff (when available).
6
+ * AI attribution is unknown unless provided.
7
+ */
8
+ export async function analyzeAiChangeQuality(options) {
9
+ const root = resolveRepoRoot(options.root);
10
+ const attribution = options.aiAssisted === true ? "ai-assisted" : "unknown";
11
+ const findings = [];
12
+ const diff = spawnSync("git", ["diff", "--unified=0"], {
13
+ cwd: root,
14
+ encoding: "utf8",
15
+ maxBuffer: 4 * 1024 * 1024,
16
+ });
17
+ if (diff.status !== 0) {
18
+ return [
19
+ {
20
+ id: "aiq_no_diff",
21
+ module: "ai-quality",
22
+ severity: "info",
23
+ title: "No git diff available",
24
+ message: "Cannot analyze working-tree changes",
25
+ recommendation: "Run inside a git repo with local changes",
26
+ confidence: 1,
27
+ evidence: [{ kind: "unknown", detail: "git diff unavailable" }],
28
+ },
29
+ ];
30
+ }
31
+ const text = diff.stdout ?? "";
32
+ const added = text
33
+ .split(/\r?\n/)
34
+ .filter((l) => l.startsWith("+") && !l.startsWith("+++"))
35
+ .map((l) => l.slice(1));
36
+ const checks = [
37
+ { re: /eval\s*\(/, title: "Added eval()", severity: "critical" },
38
+ { re: /innerHTML\s*=/, title: "Added innerHTML assignment", severity: "high" },
39
+ {
40
+ re: /password\s*=\s*['"][^'"]+['"]/i,
41
+ title: "Hardcoded password-like literal",
42
+ severity: "critical",
43
+ },
44
+ { re: /TODO|FIXME/, title: "Added TODO/FIXME in change", severity: "low" },
45
+ { re: /catch\s*\([^)]*\)\s*\{\s*\}/, title: "Added empty catch", severity: "medium" },
46
+ ];
47
+ for (const [i, line] of added.entries()) {
48
+ for (const check of checks) {
49
+ if (!check.re.test(line))
50
+ continue;
51
+ findings.push({
52
+ id: `aiq_${createHash("sha1").update(`${check.title}:${i}:${line}`).digest("hex").slice(0, 12)}`,
53
+ module: "ai-quality",
54
+ severity: check.severity,
55
+ title: check.title,
56
+ message: `In working-tree addition (attribution=${attribution}): ${line.slice(0, 120)}`,
57
+ recommendation: "Review before commit; add tests for behavioral changes",
58
+ confidence: 0.6,
59
+ evidence: [{ kind: "inferred", detail: line.slice(0, 160) }],
60
+ falsePositiveWarning: "Diff-line heuristic only",
61
+ });
62
+ }
63
+ }
64
+ return findings.sort((a, b) => a.id.localeCompare(b.id));
65
+ }
@@ -0,0 +1,17 @@
1
+ import type { PlatformFinding, RepositoryGraph } from "../types.js";
2
+ export interface ArchitectureRule {
3
+ id: string;
4
+ description: string;
5
+ forbidImportFrom?: string;
6
+ forbidImportTo?: string;
7
+ severity: PlatformFinding["severity"];
8
+ }
9
+ export interface ArchitecturePolicy {
10
+ version: "2.0";
11
+ rules: ArchitectureRule[];
12
+ }
13
+ export declare function loadArchitecturePolicy(root: string): Promise<ArchitecturePolicy>;
14
+ /**
15
+ * Module I — Architecture drift detection.
16
+ */
17
+ export declare function analyzeArchitectureDrift(rootInput: string, graph: RepositoryGraph): Promise<PlatformFinding[]>;
@@ -0,0 +1,74 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { createHash } from "node:crypto";
4
+ import { resolveRepoRoot } from "../../utils/path.js";
5
+ import { writeJsonArtifact, platformDir } from "../store.js";
6
+ const DEFAULT_POLICY = {
7
+ version: "2.0",
8
+ rules: [
9
+ {
10
+ id: "no-src-to-dist",
11
+ description: "Source should not import built dist artifacts",
12
+ forbidImportFrom: "src/",
13
+ forbidImportTo: "dist/",
14
+ severity: "high",
15
+ },
16
+ {
17
+ id: "tests-not-imported-by-src",
18
+ description: "Production source should not import test files",
19
+ forbidImportFrom: "src/",
20
+ forbidImportTo: ".test.",
21
+ severity: "medium",
22
+ },
23
+ ],
24
+ };
25
+ export async function loadArchitecturePolicy(root) {
26
+ const file = path.join(platformDir(root), "architecture-policy.json");
27
+ try {
28
+ const raw = await fs.readFile(file, "utf8");
29
+ const parsed = JSON.parse(raw);
30
+ if (parsed.version === "2.0")
31
+ return parsed;
32
+ }
33
+ catch {
34
+ // default
35
+ }
36
+ await writeJsonArtifact(resolveRepoRoot(root), "architecture-policy.json", DEFAULT_POLICY);
37
+ return DEFAULT_POLICY;
38
+ }
39
+ /**
40
+ * Module I — Architecture drift detection.
41
+ */
42
+ export async function analyzeArchitectureDrift(rootInput, graph) {
43
+ const root = resolveRepoRoot(rootInput);
44
+ const policy = await loadArchitecturePolicy(root);
45
+ const findings = [];
46
+ for (const edge of graph.edges.filter((e) => e.kind === "imports")) {
47
+ const fromNode = graph.nodes.find((n) => n.id === edge.from);
48
+ const toLabel = graph.nodes.find((n) => n.id === edge.to)?.label ?? edge.to;
49
+ const fromPath = fromNode?.path ?? "";
50
+ for (const rule of policy.rules) {
51
+ const fromHit = rule.forbidImportFrom ? fromPath.includes(rule.forbidImportFrom) : true;
52
+ const toHit = rule.forbidImportTo ? toLabel.includes(rule.forbidImportTo) : true;
53
+ if (fromHit && toHit && rule.forbidImportFrom && rule.forbidImportTo) {
54
+ findings.push({
55
+ id: `arch_${createHash("sha1").update(`${rule.id}:${edge.id}`).digest("hex").slice(0, 12)}`,
56
+ module: "architecture-drift",
57
+ severity: rule.severity,
58
+ title: `Architecture rule violated: ${rule.id}`,
59
+ message: `${fromPath} imports ${toLabel}`,
60
+ recommendation: rule.description,
61
+ confidence: edge.evidence === "verified" ? 0.7 : 0.4,
62
+ evidence: [
63
+ {
64
+ kind: edge.evidence,
65
+ path: fromPath,
66
+ detail: `edge=${edge.kind} rule=${rule.id}`,
67
+ },
68
+ ],
69
+ });
70
+ }
71
+ }
72
+ }
73
+ return findings.sort((a, b) => a.id.localeCompare(b.id));
74
+ }
@@ -0,0 +1,15 @@
1
+ import type { LocalRole } from "../types.js";
2
+ export interface LocalAuthConfig {
3
+ /** Local-only roles map. Not a cloud IdP. */
4
+ users: Array<{
5
+ id: string;
6
+ role: LocalRole;
7
+ }>;
8
+ defaultRole: LocalRole;
9
+ }
10
+ export declare function loadLocalAuthConfig(root: string): Promise<LocalAuthConfig>;
11
+ export declare function resolveRole(config: LocalAuthConfig, userId?: string): LocalRole;
12
+ export declare function roleAtLeast(role: LocalRole, required: LocalRole): boolean;
13
+ /** Permission matrix for local dashboard/API (no network auth; spoofable via local config). */
14
+ export declare function canAccess(role: LocalRole, resource: "read-findings" | "manage-policies" | "approve-actions" | "read-audit" | "export-reports" | "admin-settings"): boolean;
15
+ export declare function ensureAuthConfig(root: string): Promise<string>;
@@ -0,0 +1,58 @@
1
+ import path from "node:path";
2
+ import { platformDir, readJsonIfExists, writeJsonArtifact } from "../store.js";
3
+ import { resolveRepoRoot } from "../../utils/path.js";
4
+ const DEFAULT_CONFIG = {
5
+ defaultRole: "readonly",
6
+ users: [
7
+ { id: "local-admin", role: "admin" },
8
+ { id: "local-developer", role: "developer" },
9
+ { id: "local-reviewer", role: "reviewer" },
10
+ { id: "local-auditor", role: "auditor" },
11
+ ],
12
+ };
13
+ const ROLE_RANK = {
14
+ readonly: 0,
15
+ auditor: 1,
16
+ developer: 2,
17
+ reviewer: 3,
18
+ admin: 4,
19
+ };
20
+ export async function loadLocalAuthConfig(root) {
21
+ const file = path.join(platformDir(root), "auth.json");
22
+ const existing = await readJsonIfExists(file);
23
+ if (existing?.users && existing.defaultRole)
24
+ return existing;
25
+ await writeJsonArtifact(resolveRepoRoot(root), "auth.json", DEFAULT_CONFIG);
26
+ return DEFAULT_CONFIG;
27
+ }
28
+ export function resolveRole(config, userId) {
29
+ if (!userId)
30
+ return config.defaultRole;
31
+ return config.users.find((u) => u.id === userId)?.role ?? config.defaultRole;
32
+ }
33
+ export function roleAtLeast(role, required) {
34
+ return (ROLE_RANK[role] ?? 0) >= (ROLE_RANK[required] ?? 0);
35
+ }
36
+ /** Permission matrix for local dashboard/API (no network auth; spoofable via local config). */
37
+ export function canAccess(role, resource) {
38
+ switch (resource) {
39
+ case "read-findings":
40
+ return roleAtLeast(role, "readonly");
41
+ case "read-audit":
42
+ return roleAtLeast(role, "auditor");
43
+ case "export-reports":
44
+ return roleAtLeast(role, "developer");
45
+ case "approve-actions":
46
+ return roleAtLeast(role, "reviewer");
47
+ case "manage-policies":
48
+ return roleAtLeast(role, "admin");
49
+ case "admin-settings":
50
+ return role === "admin";
51
+ default:
52
+ return false;
53
+ }
54
+ }
55
+ export async function ensureAuthConfig(root) {
56
+ await loadLocalAuthConfig(root);
57
+ return path.join(platformDir(root), "auth.json");
58
+ }
@@ -0,0 +1,6 @@
1
+ import type { PlatformFinding } from "../types.js";
2
+ /**
3
+ * Module F — Context security / poisoning detection (pluggable pattern set).
4
+ * Does not claim perfect detection. Evidence snippets are secret-redacted.
5
+ */
6
+ export declare function analyzeContextSecurity(rootInput: string): Promise<PlatformFinding[]>;
@@ -0,0 +1,97 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { createHash } from "node:crypto";
4
+ import { resolveRepoRoot, toPosixRelative } from "../../utils/path.js";
5
+ import { redactEvidenceDetail } from "../security/redact.js";
6
+ const INJECTION_PATTERNS = [
7
+ {
8
+ id: "ignore-previous",
9
+ re: /ignore (all )?(previous|prior|above) (instructions|rules|policies)/i,
10
+ severity: "critical",
11
+ title: "Prompt-injection: ignore previous instructions",
12
+ },
13
+ {
14
+ id: "exfiltrate",
15
+ re: /exfiltrat|send (all )?(secrets|credentials|api keys) to/i,
16
+ severity: "critical",
17
+ title: "Suspected exfiltration instruction",
18
+ },
19
+ {
20
+ id: "bypass-policy",
21
+ re: /bypass (the )?(firewall|policy|approval|security|action policy)/i,
22
+ severity: "high",
23
+ title: "Policy-bypass instruction",
24
+ },
25
+ {
26
+ id: "hidden-html",
27
+ re: /<!--\s*(system|assistant|secret instruction)/i,
28
+ severity: "high",
29
+ title: "Hidden HTML instruction comment",
30
+ },
31
+ {
32
+ id: "social-eng",
33
+ re: /do not tell (the )?user|hide this from (the )?developer/i,
34
+ severity: "high",
35
+ title: "Social-engineering pattern",
36
+ },
37
+ {
38
+ id: "obfuscated-b64",
39
+ re: /eval\s*\(\s*atob\s*\(|Buffer\.from\([^,]+,\s*['"]base64['"]/i,
40
+ severity: "medium",
41
+ title: "Obfuscated execution pattern",
42
+ },
43
+ ];
44
+ /**
45
+ * Module F — Context security / poisoning detection (pluggable pattern set).
46
+ * Does not claim perfect detection. Evidence snippets are secret-redacted.
47
+ */
48
+ export async function analyzeContextSecurity(rootInput) {
49
+ const root = resolveRepoRoot(rootInput);
50
+ const findings = [];
51
+ const candidates = [
52
+ "AGENTS.md",
53
+ "CLAUDE.md",
54
+ "GEMINI.md",
55
+ "CONVENTIONS.md",
56
+ "README.md",
57
+ ".cursorrules",
58
+ ".github/copilot-instructions.md",
59
+ ];
60
+ for (const rel of candidates) {
61
+ const absolute = path.join(root, rel);
62
+ let text;
63
+ try {
64
+ text = await fs.readFile(absolute, "utf8");
65
+ }
66
+ catch {
67
+ continue;
68
+ }
69
+ const lines = text.split(/\r?\n/);
70
+ for (let i = 0; i < lines.length; i += 1) {
71
+ const line = lines[i] ?? "";
72
+ for (const pattern of INJECTION_PATTERNS) {
73
+ if (!pattern.re.test(line))
74
+ continue;
75
+ findings.push({
76
+ id: `ctxsec_${createHash("sha1").update(`${rel}:${i}:${pattern.id}`).digest("hex").slice(0, 12)}`,
77
+ module: "context-security",
78
+ severity: pattern.severity,
79
+ title: pattern.title,
80
+ message: `Pattern ${pattern.id} in ${rel}:${i + 1}`,
81
+ recommendation: "Review and remove untrusted instructions; tighten agent policies",
82
+ confidence: 0.65,
83
+ evidence: [
84
+ {
85
+ kind: "inferred",
86
+ path: toPosixRelative(root, absolute),
87
+ line: i + 1,
88
+ detail: redactEvidenceDetail(line.slice(0, 200)),
89
+ },
90
+ ],
91
+ falsePositiveWarning: "Pattern match only — confirm intent before treating as an attack",
92
+ });
93
+ }
94
+ }
95
+ }
96
+ return findings.sort((a, b) => a.id.localeCompare(b.id));
97
+ }