@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
@@ -0,0 +1,76 @@
1
+ import { detectLanguage } from "./types.js";
2
+ import { javascriptAdapter, typescriptAdapter } from "./typescript.js";
3
+ import { pythonAdapter } from "./python.js";
4
+ import { phpAdapter } from "./php.js";
5
+ import { goAdapter } from "./go.js";
6
+ function unsupportedAdapter(id, extensions, reason) {
7
+ return {
8
+ id,
9
+ extensions,
10
+ capabilities: () => ({
11
+ parse: "unsupported",
12
+ symbols: "unsupported",
13
+ definitions: "unsupported",
14
+ references: "unsupported",
15
+ imports: "unsupported",
16
+ calls: "unsupported",
17
+ diagnostics: "unsupported",
18
+ }),
19
+ async parse(filePath, _source) {
20
+ return {
21
+ language: id,
22
+ file: filePath,
23
+ ok: false,
24
+ capabilities: this.capabilities(),
25
+ symbols: [],
26
+ imports: [],
27
+ calls: [],
28
+ diagnostics: [reason],
29
+ limitations: [
30
+ reason,
31
+ "No regex fallback is offered under an AST claim — capability remains unsupported until a mature parser is integrated",
32
+ ],
33
+ };
34
+ },
35
+ };
36
+ }
37
+ /** Languages with real AST backends today (or honest unsupported stubs). */
38
+ export const languageAdapters = [
39
+ typescriptAdapter,
40
+ javascriptAdapter,
41
+ pythonAdapter,
42
+ phpAdapter,
43
+ goAdapter,
44
+ unsupportedAdapter("java", [".java"], "Java AST requires an external parser (e.g. tree-sitter-java or javaparser); not bundled"),
45
+ unsupportedAdapter("kotlin", [".kt", ".kts"], "Kotlin AST requires an external parser; not bundled"),
46
+ unsupportedAdapter("rust", [".rs"], "Rust AST requires syn/rustc or tree-sitter-rust; not bundled"),
47
+ unsupportedAdapter("dart", [".dart"], "Dart AST requires Dart analyzer APIs; not bundled"),
48
+ ];
49
+ export function getAdapterForFile(filePath) {
50
+ const lang = detectLanguage(filePath);
51
+ if (lang === "unknown")
52
+ return null;
53
+ return languageAdapters.find((a) => a.id === lang) ?? null;
54
+ }
55
+ export async function parseSourceFile(filePath, source) {
56
+ const adapter = getAdapterForFile(filePath);
57
+ if (!adapter) {
58
+ return {
59
+ language: "unknown",
60
+ file: filePath,
61
+ ok: false,
62
+ capabilities: {},
63
+ symbols: [],
64
+ imports: [],
65
+ calls: [],
66
+ diagnostics: ["unknown language"],
67
+ limitations: ["No language adapter for this file extension"],
68
+ };
69
+ }
70
+ return adapter.parse(filePath, source);
71
+ }
72
+ export { detectLanguage } from "./types.js";
73
+ export { typescriptAdapter, javascriptAdapter } from "./typescript.js";
74
+ export { pythonAdapter, pythonAvailable } from "./python.js";
75
+ export { phpAdapter, phpAvailable } from "./php.js";
76
+ export { goAdapter, goAvailable } from "./go.js";
@@ -0,0 +1,7 @@
1
+ import type { LanguageAdapter } from "./types.js";
2
+ /**
3
+ * Real PHP token extractor via `php -r` + token_get_all (requires `php` on PATH).
4
+ * Not regex. If php is missing, capabilities are unsupported.
5
+ */
6
+ export declare function phpAvailable(): boolean;
7
+ export declare const phpAdapter: LanguageAdapter;
@@ -0,0 +1,193 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import { createHash } from "node:crypto";
3
+ function sid(kind, key) {
4
+ return `${kind}:${createHash("sha256").update(key).digest("hex").slice(0, 12)}`;
5
+ }
6
+ /**
7
+ * Real PHP token extractor via `php -r` + token_get_all (requires `php` on PATH).
8
+ * Not regex. If php is missing, capabilities are unsupported.
9
+ */
10
+ export function phpAvailable() {
11
+ const r = spawnSync("php", ["-r", "echo PHP_VERSION;"], { encoding: "utf8" });
12
+ return r.status === 0 && Boolean(r.stdout?.trim());
13
+ }
14
+ const PHP_EXTRACTOR = `
15
+ $source = stream_get_contents(STDIN);
16
+ $path = $argv[1] ?? "stdin.php";
17
+ if ($source === false || $source === "") {
18
+ echo json_encode(["ok" => false, "error" => "empty source", "symbols" => [], "imports" => [], "calls" => []]);
19
+ exit(0);
20
+ }
21
+ if (strpos($source, "<?") === false) {
22
+ $source = "<?php\\n" . $source;
23
+ }
24
+ $tokens = @token_get_all($source);
25
+ if (!is_array($tokens)) {
26
+ echo json_encode(["ok" => false, "error" => "token_get_all failed", "symbols" => [], "imports" => [], "calls" => []]);
27
+ exit(0);
28
+ }
29
+ $symbols = [];
30
+ $imports = [];
31
+ $calls = [];
32
+ $count = count($tokens);
33
+ for ($i = 0; $i < $count; $i++) {
34
+ $t = $tokens[$i];
35
+ if (!is_array($t)) continue;
36
+ [$id, $text, $line] = $t;
37
+ if ($id === T_FUNCTION) {
38
+ for ($j = $i + 1; $j < $count; $j++) {
39
+ $n = $tokens[$j];
40
+ if (!is_array($n)) continue;
41
+ if ($n[0] === T_STRING) {
42
+ $symbols[] = ["name" => $n[1], "kind" => "function", "line" => $n[2]];
43
+ break;
44
+ }
45
+ if ($n[0] === T_WHITESPACE || $n[0] === T_COMMENT || $n[0] === T_DOC_COMMENT) continue;
46
+ break;
47
+ }
48
+ } elseif ($id === T_CLASS || $id === T_INTERFACE || $id === T_TRAIT) {
49
+ $kind = $id === T_CLASS ? "class" : ($id === T_INTERFACE ? "interface" : "trait");
50
+ for ($j = $i + 1; $j < $count; $j++) {
51
+ $n = $tokens[$j];
52
+ if (!is_array($n)) continue;
53
+ if ($n[0] === T_STRING) {
54
+ $symbols[] = ["name" => $n[1], "kind" => $kind, "line" => $n[2]];
55
+ break;
56
+ }
57
+ if ($n[0] === T_WHITESPACE || $n[0] === T_COMMENT || $n[0] === T_DOC_COMMENT) continue;
58
+ break;
59
+ }
60
+ } elseif ($id === T_USE) {
61
+ $spec = "";
62
+ for ($j = $i + 1; $j < $count; $j++) {
63
+ $n = $tokens[$j];
64
+ if (is_array($n)) {
65
+ if ($n[0] === T_STRING || $n[0] === T_NS_SEPARATOR || $n[0] === T_NAME_QUALIFIED || $n[0] === T_NAME_FULLY_QUALIFIED) {
66
+ $spec .= $n[1];
67
+ continue;
68
+ }
69
+ if ($n[0] === T_WHITESPACE) continue;
70
+ break;
71
+ }
72
+ if ($n === ";" || $n === "{") break;
73
+ }
74
+ if ($spec !== "") $imports[] = ["specifier" => $spec];
75
+ } elseif ($id === T_STRING) {
76
+ $next = $tokens[$i + 1] ?? null;
77
+ $prev = $tokens[$i - 1] ?? null;
78
+ $prevIsObject = is_array($prev) && ($prev[0] === T_OBJECT_OPERATOR || $prev[0] === T_NULLSAFE_OBJECT_OPERATOR || $prev[0] === T_DOUBLE_COLON);
79
+ if ($next === "(" && !$prevIsObject) {
80
+ $calls[] = ["callee" => $text, "line" => $line];
81
+ }
82
+ }
83
+ }
84
+ echo json_encode(["ok" => true, "symbols" => $symbols, "imports" => $imports, "calls" => $calls], JSON_UNESCAPED_SLASHES);
85
+ `;
86
+ export const phpAdapter = {
87
+ id: "php",
88
+ extensions: [".php"],
89
+ capabilities: () => {
90
+ const ok = phpAvailable();
91
+ const v = ok ? "supported" : "unsupported";
92
+ return {
93
+ parse: v,
94
+ symbols: v,
95
+ definitions: v,
96
+ imports: v,
97
+ calls: v,
98
+ references: "unsupported",
99
+ diagnostics: ok ? "supported" : "unsupported",
100
+ };
101
+ },
102
+ async parse(filePath, source) {
103
+ if (!phpAvailable()) {
104
+ return {
105
+ language: "php",
106
+ file: filePath,
107
+ ok: false,
108
+ capabilities: phpAdapter.capabilities(),
109
+ symbols: [],
110
+ imports: [],
111
+ calls: [],
112
+ diagnostics: ["php not available on PATH"],
113
+ limitations: ["PHP adapter requires php CLI with token_get_all"],
114
+ };
115
+ }
116
+ const r = spawnSync("php", ["-r", PHP_EXTRACTOR, "--", filePath], {
117
+ input: source,
118
+ encoding: "utf8",
119
+ maxBuffer: 8 * 1024 * 1024,
120
+ });
121
+ if (r.status !== 0 || !r.stdout) {
122
+ return {
123
+ language: "php",
124
+ file: filePath,
125
+ ok: false,
126
+ capabilities: phpAdapter.capabilities(),
127
+ symbols: [],
128
+ imports: [],
129
+ calls: [],
130
+ diagnostics: [r.stderr || "php extractor failed"],
131
+ limitations: [],
132
+ };
133
+ }
134
+ let parsed;
135
+ try {
136
+ parsed = JSON.parse(r.stdout);
137
+ }
138
+ catch {
139
+ return {
140
+ language: "php",
141
+ file: filePath,
142
+ ok: false,
143
+ capabilities: phpAdapter.capabilities(),
144
+ symbols: [],
145
+ imports: [],
146
+ calls: [],
147
+ diagnostics: ["invalid JSON from php extractor"],
148
+ limitations: [],
149
+ };
150
+ }
151
+ if (!parsed.ok) {
152
+ return {
153
+ language: "php",
154
+ file: filePath,
155
+ ok: false,
156
+ capabilities: phpAdapter.capabilities(),
157
+ symbols: [],
158
+ imports: [],
159
+ calls: [],
160
+ diagnostics: [parsed.error ?? "parse error"],
161
+ limitations: [],
162
+ };
163
+ }
164
+ return {
165
+ language: "php",
166
+ file: filePath,
167
+ ok: true,
168
+ capabilities: phpAdapter.capabilities(),
169
+ symbols: parsed.symbols.map((s) => ({
170
+ id: sid(s.kind, `${filePath}:${s.name}:${s.line}`),
171
+ name: s.name,
172
+ kind: s.kind,
173
+ file: filePath,
174
+ line: s.line,
175
+ evidence: "ast",
176
+ })),
177
+ imports: parsed.imports.map((i) => ({
178
+ fromFile: filePath,
179
+ specifier: i.specifier,
180
+ evidence: "ast",
181
+ })),
182
+ calls: parsed.calls.map((c) => ({
183
+ fromFile: filePath,
184
+ callee: c.callee,
185
+ evidence: "ast",
186
+ })),
187
+ diagnostics: [],
188
+ limitations: [
189
+ "PHP token_get_all single-file extract — not a full php-parser AST; cross-file refs unsupported",
190
+ ],
191
+ };
192
+ },
193
+ };
@@ -0,0 +1,7 @@
1
+ import type { LanguageAdapter } from "./types.js";
2
+ /**
3
+ * Real Python AST via the CPython `ast` module (requires `python3` on PATH).
4
+ * Not regex. If python3 is missing, capabilities are unsupported.
5
+ */
6
+ export declare function pythonAvailable(): boolean;
7
+ export declare const pythonAdapter: LanguageAdapter;
@@ -0,0 +1,150 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import { createHash } from "node:crypto";
3
+ function sid(kind, key) {
4
+ return `${kind}:${createHash("sha256").update(key).digest("hex").slice(0, 12)}`;
5
+ }
6
+ /**
7
+ * Real Python AST via the CPython `ast` module (requires `python3` on PATH).
8
+ * Not regex. If python3 is missing, capabilities are unsupported.
9
+ */
10
+ export function pythonAvailable() {
11
+ const r = spawnSync("python3", ["-c", "import ast, json"], { encoding: "utf8" });
12
+ return r.status === 0;
13
+ }
14
+ const PY_EXTRACTOR = `
15
+ import ast, json, sys
16
+ source = sys.stdin.read()
17
+ path = sys.argv[1]
18
+ try:
19
+ tree = ast.parse(source, filename=path)
20
+ except SyntaxError as e:
21
+ print(json.dumps({"ok": False, "error": str(e), "symbols": [], "imports": [], "calls": []}))
22
+ sys.exit(0)
23
+ symbols = []
24
+ imports = []
25
+ calls = []
26
+ for node in ast.walk(tree):
27
+ if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
28
+ symbols.append({"name": node.name, "kind": "function", "line": node.lineno})
29
+ elif isinstance(node, ast.ClassDef):
30
+ symbols.append({"name": node.name, "kind": "class", "line": node.lineno})
31
+ elif isinstance(node, ast.Import):
32
+ for a in node.names:
33
+ imports.append({"specifier": a.name})
34
+ elif isinstance(node, ast.ImportFrom):
35
+ mod = node.module or ""
36
+ imports.append({"specifier": mod if mod else "."})
37
+ elif isinstance(node, ast.Call):
38
+ try:
39
+ callee = ast.unparse(node.func)
40
+ except Exception:
41
+ callee = type(node.func).__name__
42
+ calls.append({"callee": callee})
43
+ print(json.dumps({"ok": True, "symbols": symbols, "imports": imports, "calls": calls}))
44
+ `;
45
+ export const pythonAdapter = {
46
+ id: "python",
47
+ extensions: [".py"],
48
+ capabilities: () => {
49
+ const ok = pythonAvailable();
50
+ const v = ok ? "supported" : "unsupported";
51
+ return {
52
+ parse: v,
53
+ symbols: v,
54
+ definitions: v,
55
+ imports: v,
56
+ calls: v,
57
+ references: "unsupported",
58
+ diagnostics: ok ? "supported" : "unsupported",
59
+ };
60
+ },
61
+ async parse(filePath, source) {
62
+ if (!pythonAvailable()) {
63
+ return {
64
+ language: "python",
65
+ file: filePath,
66
+ ok: false,
67
+ capabilities: pythonAdapter.capabilities(),
68
+ symbols: [],
69
+ imports: [],
70
+ calls: [],
71
+ diagnostics: ["python3 not available on PATH"],
72
+ limitations: ["Python AST adapter requires python3 with stdlib ast module"],
73
+ };
74
+ }
75
+ const r = spawnSync("python3", ["-c", PY_EXTRACTOR, filePath], {
76
+ input: source,
77
+ encoding: "utf8",
78
+ maxBuffer: 8 * 1024 * 1024,
79
+ });
80
+ if (r.status !== 0 || !r.stdout) {
81
+ return {
82
+ language: "python",
83
+ file: filePath,
84
+ ok: false,
85
+ capabilities: pythonAdapter.capabilities(),
86
+ symbols: [],
87
+ imports: [],
88
+ calls: [],
89
+ diagnostics: [r.stderr || "python3 extractor failed"],
90
+ limitations: [],
91
+ };
92
+ }
93
+ let parsed;
94
+ try {
95
+ parsed = JSON.parse(r.stdout);
96
+ }
97
+ catch {
98
+ return {
99
+ language: "python",
100
+ file: filePath,
101
+ ok: false,
102
+ capabilities: pythonAdapter.capabilities(),
103
+ symbols: [],
104
+ imports: [],
105
+ calls: [],
106
+ diagnostics: ["invalid JSON from python extractor"],
107
+ limitations: [],
108
+ };
109
+ }
110
+ if (!parsed.ok) {
111
+ return {
112
+ language: "python",
113
+ file: filePath,
114
+ ok: false,
115
+ capabilities: pythonAdapter.capabilities(),
116
+ symbols: [],
117
+ imports: [],
118
+ calls: [],
119
+ diagnostics: [parsed.error ?? "parse error"],
120
+ limitations: [],
121
+ };
122
+ }
123
+ return {
124
+ language: "python",
125
+ file: filePath,
126
+ ok: true,
127
+ capabilities: pythonAdapter.capabilities(),
128
+ symbols: parsed.symbols.map((s) => ({
129
+ id: sid(s.kind, `${filePath}:${s.name}:${s.line}`),
130
+ name: s.name,
131
+ kind: s.kind,
132
+ file: filePath,
133
+ line: s.line,
134
+ evidence: "ast",
135
+ })),
136
+ imports: parsed.imports.map((i) => ({
137
+ fromFile: filePath,
138
+ specifier: i.specifier,
139
+ evidence: "ast",
140
+ })),
141
+ calls: parsed.calls.map((c) => ({
142
+ fromFile: filePath,
143
+ callee: c.callee,
144
+ evidence: "ast",
145
+ })),
146
+ diagnostics: [],
147
+ limitations: ["Cross-file references not resolved; CPython ast single-file only"],
148
+ };
149
+ },
150
+ };
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Language adapter contract — real parsers only.
3
+ * Unsupported capabilities must be reported as unsupported, never as inferred AST.
4
+ */
5
+ export type LanguageId = "typescript" | "javascript" | "python" | "java" | "kotlin" | "go" | "rust" | "php" | "dart" | "unknown";
6
+ export type AdapterCapability = "parse" | "symbols" | "definitions" | "references" | "imports" | "calls" | "diagnostics";
7
+ export interface LanguageSymbol {
8
+ id: string;
9
+ name: string;
10
+ kind: string;
11
+ file: string;
12
+ line?: number;
13
+ evidence: "ast" | "unsupported";
14
+ }
15
+ export interface LanguageImport {
16
+ fromFile: string;
17
+ specifier: string;
18
+ evidence: "ast" | "unsupported";
19
+ }
20
+ export interface LanguageCall {
21
+ fromFile: string;
22
+ callee: string;
23
+ evidence: "ast" | "unsupported";
24
+ }
25
+ export interface ParseResult {
26
+ language: LanguageId;
27
+ file: string;
28
+ ok: boolean;
29
+ capabilities: Partial<Record<AdapterCapability, "supported" | "unsupported">>;
30
+ symbols: LanguageSymbol[];
31
+ imports: LanguageImport[];
32
+ calls: LanguageCall[];
33
+ diagnostics: string[];
34
+ limitations: string[];
35
+ }
36
+ export interface LanguageAdapter {
37
+ id: LanguageId;
38
+ extensions: string[];
39
+ capabilities(): Partial<Record<AdapterCapability, "supported" | "unsupported">>;
40
+ parse(filePath: string, source: string): Promise<ParseResult>;
41
+ }
42
+ export declare function detectLanguage(filePath: string): LanguageId;
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Language adapter contract — real parsers only.
3
+ * Unsupported capabilities must be reported as unsupported, never as inferred AST.
4
+ */
5
+ export function detectLanguage(filePath) {
6
+ const lower = filePath.toLowerCase();
7
+ if (/\.tsx?$/.test(lower) || /\.mts$/.test(lower) || /\.cts$/.test(lower))
8
+ return "typescript";
9
+ if (/\.jsx?$/.test(lower) || /\.mjs$/.test(lower) || /\.cjs$/.test(lower))
10
+ return "javascript";
11
+ if (/\.py$/.test(lower))
12
+ return "python";
13
+ if (/\.java$/.test(lower))
14
+ return "java";
15
+ if (/\.kt$/.test(lower) || /\.kts$/.test(lower))
16
+ return "kotlin";
17
+ if (/\.go$/.test(lower))
18
+ return "go";
19
+ if (/\.rs$/.test(lower))
20
+ return "rust";
21
+ if (/\.php$/.test(lower))
22
+ return "php";
23
+ if (/\.dart$/.test(lower))
24
+ return "dart";
25
+ return "unknown";
26
+ }
@@ -0,0 +1,3 @@
1
+ import type { LanguageAdapter } from "./types.js";
2
+ export declare const typescriptAdapter: LanguageAdapter;
3
+ export declare const javascriptAdapter: LanguageAdapter;
@@ -0,0 +1,95 @@
1
+ import { createHash } from "node:crypto";
2
+ import ts from "typescript";
3
+ function sid(kind, key) {
4
+ return `${kind}:${createHash("sha256").update(key).digest("hex").slice(0, 12)}`;
5
+ }
6
+ function parseTsJs(filePath, source, language) {
7
+ const kind = language === "typescript" ? ts.ScriptKind.TS : ts.ScriptKind.JS;
8
+ const sf = ts.createSourceFile(filePath, source, ts.ScriptTarget.ES2022, true, kind);
9
+ const symbols = [];
10
+ const imports = [];
11
+ const calls = [];
12
+ const diagnostics = [];
13
+ const visit = (node) => {
14
+ if (ts.isFunctionDeclaration(node) && node.name) {
15
+ const name = node.name.text;
16
+ symbols.push({
17
+ id: sid("fn", `${filePath}:${name}`),
18
+ name,
19
+ kind: "function",
20
+ file: filePath,
21
+ line: sf.getLineAndCharacterOfPosition(node.getStart(sf)).line + 1,
22
+ evidence: "ast",
23
+ });
24
+ }
25
+ if (ts.isClassDeclaration(node) && node.name) {
26
+ const name = node.name.text;
27
+ symbols.push({
28
+ id: sid("class", `${filePath}:${name}`),
29
+ name,
30
+ kind: "class",
31
+ file: filePath,
32
+ line: sf.getLineAndCharacterOfPosition(node.getStart(sf)).line + 1,
33
+ evidence: "ast",
34
+ });
35
+ }
36
+ if (ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier)) {
37
+ imports.push({
38
+ fromFile: filePath,
39
+ specifier: node.moduleSpecifier.text,
40
+ evidence: "ast",
41
+ });
42
+ }
43
+ if (ts.isCallExpression(node)) {
44
+ const callee = node.expression.getText(sf);
45
+ calls.push({ fromFile: filePath, callee, evidence: "ast" });
46
+ }
47
+ ts.forEachChild(node, visit);
48
+ };
49
+ visit(sf);
50
+ return {
51
+ language,
52
+ file: filePath,
53
+ ok: true,
54
+ capabilities: {
55
+ parse: "supported",
56
+ symbols: "supported",
57
+ definitions: "supported",
58
+ imports: "supported",
59
+ calls: "supported",
60
+ references: "unsupported",
61
+ diagnostics: "unsupported",
62
+ },
63
+ symbols,
64
+ imports,
65
+ calls,
66
+ diagnostics,
67
+ limitations: [
68
+ "TypeScript compiler API parse (single-file); cross-file references unsupported in adapter",
69
+ ],
70
+ };
71
+ }
72
+ export const typescriptAdapter = {
73
+ id: "typescript",
74
+ extensions: [".ts", ".tsx", ".mts", ".cts"],
75
+ capabilities: () => ({
76
+ parse: "supported",
77
+ symbols: "supported",
78
+ definitions: "supported",
79
+ imports: "supported",
80
+ calls: "supported",
81
+ references: "unsupported",
82
+ diagnostics: "unsupported",
83
+ }),
84
+ async parse(filePath, source) {
85
+ return parseTsJs(filePath, source, "typescript");
86
+ },
87
+ };
88
+ export const javascriptAdapter = {
89
+ id: "javascript",
90
+ extensions: [".js", ".jsx", ".mjs", ".cjs"],
91
+ capabilities: () => typescriptAdapter.capabilities(),
92
+ async parse(filePath, source) {
93
+ return parseTsJs(filePath, source, "javascript");
94
+ },
95
+ };
@@ -9,3 +9,8 @@ export declare function handleCodeHealthTool(rootInput: string): Promise<unknown
9
9
  export declare function handleArchitectureTool(rootInput: string): Promise<unknown>;
10
10
  export declare function handleKnowledgeTool(rootInput: string, args: Record<string, unknown>): Promise<unknown>;
11
11
  export declare function handlePolicyEvalTool(rootInput: string, args: Record<string, unknown>): Promise<unknown>;
12
+ export declare function handleChangeAnalyzeTool(rootInput: string, args: Record<string, unknown>): Promise<unknown>;
13
+ export declare function handleArchitectureCheckTool(rootInput: string): Promise<unknown>;
14
+ export declare function handleProofInspectTool(rootInput: string, args: Record<string, unknown>): Promise<unknown>;
15
+ export declare function handleEvidenceInspectTool(rootInput: string, args: Record<string, unknown>): Promise<unknown>;
16
+ export declare function handleGraphQueryTool(rootInput: string, args: Record<string, unknown>): Promise<unknown>;