@danielblomma/cortex-mcp 1.3.1 → 1.4.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.
- package/README.md +62 -14
- package/package.json +2 -2
- package/scaffold/mcp/package-lock.json +3 -7
- package/scaffold/mcp/package.json +1 -1
- package/scaffold/scripts/dashboard.mjs +15 -1
- package/scaffold/scripts/doctor.sh +64 -10
- package/scaffold/scripts/ingest.mjs +323 -50
- package/scaffold/scripts/parsers/bash-treesitter.mjs +229 -0
- package/scaffold/scripts/parsers/cpp-dispatch.mjs +56 -0
- package/scaffold/scripts/parsers/cpp-treesitter.mjs +333 -0
- package/scaffold/scripts/parsers/csharp.mjs +197 -10
- package/scaffold/scripts/parsers/dotnet/CSharpParser/CSharpParser.csproj +1 -0
- package/scaffold/scripts/parsers/dotnet/CSharpParser/Program.cs +126 -21
- package/scaffold/scripts/parsers/go-treesitter.mjs +283 -0
- package/scaffold/scripts/parsers/java-treesitter.mjs +250 -0
- package/scaffold/scripts/parsers/javascript/ast.mjs +118 -12
- package/scaffold/scripts/parsers/javascript/chunks.mjs +4 -0
- package/scaffold/scripts/parsers/javascript/patterns.mjs +6 -0
- package/scaffold/scripts/parsers/javascript.mjs +4 -19
- package/scaffold/scripts/parsers/node_modules/.package-lock.json +57 -0
- package/scaffold/scripts/parsers/node_modules/acorn/CHANGELOG.md +972 -0
- package/scaffold/scripts/parsers/node_modules/acorn/LICENSE +21 -0
- package/scaffold/scripts/parsers/node_modules/acorn/README.md +301 -0
- package/scaffold/scripts/parsers/node_modules/acorn/bin/acorn +4 -0
- package/scaffold/scripts/parsers/node_modules/acorn/dist/acorn.d.mts +883 -0
- package/scaffold/scripts/parsers/node_modules/acorn/dist/acorn.d.ts +883 -0
- package/scaffold/scripts/parsers/node_modules/acorn/dist/acorn.js +6295 -0
- package/scaffold/scripts/parsers/node_modules/acorn/dist/acorn.mjs +6266 -0
- package/scaffold/scripts/parsers/node_modules/acorn/dist/bin.js +90 -0
- package/scaffold/scripts/parsers/node_modules/acorn/package.json +50 -0
- package/scaffold/scripts/parsers/node_modules/acorn-typescript/CHANGELOG.md +421 -0
- package/scaffold/scripts/parsers/node_modules/acorn-typescript/LICENSE +21 -0
- package/scaffold/scripts/parsers/node_modules/acorn-typescript/README.md +81 -0
- package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/error.d.ts +103 -0
- package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/error.js +78 -0
- package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/error.js.map +1 -0
- package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/extentions/decorators.d.ts +167 -0
- package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/extentions/decorators.js +75 -0
- package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/extentions/decorators.js.map +1 -0
- package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/extentions/import-assertions.d.ts +177 -0
- package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/extentions/import-assertions.js +56 -0
- package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/extentions/import-assertions.js.map +1 -0
- package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/extentions/jsx/index.d.ts +198 -0
- package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/extentions/jsx/index.js +327 -0
- package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/extentions/jsx/index.js.map +1 -0
- package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/extentions/jsx/xhtml.d.ts +256 -0
- package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/extentions/jsx/xhtml.js +256 -0
- package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/extentions/jsx/xhtml.js.map +1 -0
- package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/index.d.ts +472 -0
- package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/index.js +1 -0
- package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/index.js.map +1 -0
- package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/index.mjs +1 -0
- package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/middleware.d.ts +159 -0
- package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/middleware.js +2 -0
- package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/middleware.js.map +1 -0
- package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/parseutil.d.ts +10 -0
- package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/parseutil.js +38 -0
- package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/parseutil.js.map +1 -0
- package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/scopeflags.d.ts +12 -0
- package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/scopeflags.js +29 -0
- package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/scopeflags.js.map +1 -0
- package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/tokenType.d.ts +2 -0
- package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/tokenType.js +118 -0
- package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/tokenType.js.map +1 -0
- package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/types.d.ts +60 -0
- package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/types.js +2 -0
- package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/types.js.map +1 -0
- package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/whitespace.d.ts +2 -0
- package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/whitespace.js +19 -0
- package/scaffold/scripts/parsers/node_modules/acorn-typescript/lib/whitespace.js.map +1 -0
- package/scaffold/scripts/parsers/node_modules/acorn-typescript/package.json +53 -0
- package/scaffold/scripts/parsers/node_modules/acorn-typescript/tsconfig.json +19 -0
- package/scaffold/scripts/parsers/node_modules/acorn-walk/CHANGELOG.md +209 -0
- package/scaffold/scripts/parsers/node_modules/acorn-walk/LICENSE +21 -0
- package/scaffold/scripts/parsers/node_modules/acorn-walk/README.md +124 -0
- package/scaffold/scripts/parsers/node_modules/acorn-walk/dist/walk.d.mts +152 -0
- package/scaffold/scripts/parsers/node_modules/acorn-walk/dist/walk.d.ts +152 -0
- package/scaffold/scripts/parsers/node_modules/acorn-walk/dist/walk.js +485 -0
- package/scaffold/scripts/parsers/node_modules/acorn-walk/dist/walk.mjs +467 -0
- package/scaffold/scripts/parsers/node_modules/acorn-walk/package.json +50 -0
- package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/LICENSE +24 -0
- package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/README.md +23 -0
- package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-bash.wasm +0 -0
- package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-c.wasm +0 -0
- package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-c_sharp.wasm +0 -0
- package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-cpp.wasm +0 -0
- package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-css.wasm +0 -0
- package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-dart.wasm +0 -0
- package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-elisp.wasm +0 -0
- package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-elixir.wasm +0 -0
- package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-elm.wasm +0 -0
- package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-embedded_template.wasm +0 -0
- package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-go.wasm +0 -0
- package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-html.wasm +0 -0
- package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-java.wasm +0 -0
- package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-javascript.wasm +0 -0
- package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-json.wasm +0 -0
- package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-kotlin.wasm +0 -0
- package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-lua.wasm +0 -0
- package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-objc.wasm +0 -0
- package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-ocaml.wasm +0 -0
- package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-php.wasm +0 -0
- package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-python.wasm +0 -0
- package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-ql.wasm +0 -0
- package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-rescript.wasm +0 -0
- package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-ruby.wasm +0 -0
- package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-rust.wasm +0 -0
- package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-scala.wasm +0 -0
- package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-solidity.wasm +0 -0
- package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-swift.wasm +0 -0
- package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-systemrdl.wasm +0 -0
- package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-tlaplus.wasm +0 -0
- package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-toml.wasm +0 -0
- package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-tsx.wasm +0 -0
- package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-typescript.wasm +0 -0
- package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-vue.wasm +0 -0
- package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-yaml.wasm +0 -0
- package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/out/tree-sitter-zig.wasm +0 -0
- package/scaffold/scripts/parsers/node_modules/tree-sitter-wasms/package.json +64 -0
- package/scaffold/scripts/parsers/node_modules/web-tree-sitter/LICENSE +21 -0
- package/scaffold/scripts/parsers/node_modules/web-tree-sitter/README.md +198 -0
- package/scaffold/scripts/parsers/node_modules/web-tree-sitter/package.json +37 -0
- package/scaffold/scripts/parsers/node_modules/web-tree-sitter/tree-sitter-web.d.ts +242 -0
- package/scaffold/scripts/parsers/node_modules/web-tree-sitter/tree-sitter.js +1 -0
- package/scaffold/scripts/parsers/node_modules/web-tree-sitter/tree-sitter.wasm +0 -0
- package/scaffold/scripts/parsers/package-lock.json +19 -1
- package/scaffold/scripts/parsers/package.json +3 -1
- package/scaffold/scripts/parsers/python-treesitter.mjs +271 -0
- package/scaffold/scripts/parsers/ruby-treesitter.mjs +271 -0
- package/scaffold/scripts/parsers/rust-dispatch.mjs +43 -0
- package/scaffold/scripts/parsers/rust-treesitter.mjs +291 -0
- package/scaffold/scripts/parsers/tree-sitter/base.mjs +163 -0
- package/scaffold/scripts/parsers/tree-sitter/queries/bash.calls.scm +7 -0
- package/scaffold/scripts/parsers/tree-sitter/queries/bash.chunks.scm +6 -0
- package/scaffold/scripts/parsers/tree-sitter/queries/bash.imports.scm +5 -0
- package/scaffold/scripts/parsers/tree-sitter/queries/cpp.calls.scm +17 -0
- package/scaffold/scripts/parsers/tree-sitter/queries/cpp.chunks.scm +30 -0
- package/scaffold/scripts/parsers/tree-sitter/queries/cpp.imports.scm +6 -0
- package/scaffold/scripts/parsers/tree-sitter/queries/go.calls.scm +11 -0
- package/scaffold/scripts/parsers/tree-sitter/queries/go.chunks.scm +19 -0
- package/scaffold/scripts/parsers/tree-sitter/queries/go.imports.scm +6 -0
- package/scaffold/scripts/parsers/tree-sitter/queries/java.calls.scm +6 -0
- package/scaffold/scripts/parsers/tree-sitter/queries/java.chunks.scm +23 -0
- package/scaffold/scripts/parsers/tree-sitter/queries/java.imports.scm +6 -0
- package/scaffold/scripts/parsers/tree-sitter/queries/python.calls.scm +11 -0
- package/scaffold/scripts/parsers/tree-sitter/queries/python.chunks.scm +11 -0
- package/scaffold/scripts/parsers/tree-sitter/queries/python.imports.scm +13 -0
- package/scaffold/scripts/parsers/tree-sitter/queries/ruby.calls.scm +6 -0
- package/scaffold/scripts/parsers/tree-sitter/queries/ruby.chunks.scm +16 -0
- package/scaffold/scripts/parsers/tree-sitter/queries/ruby.imports.scm +8 -0
- package/scaffold/scripts/parsers/tree-sitter/queries/rust.calls.scm +31 -0
- package/scaffold/scripts/parsers/tree-sitter/queries/rust.chunks.scm +29 -0
- package/scaffold/scripts/parsers/tree-sitter/queries/rust.imports.scm +5 -0
- package/scaffold/scripts/parsers/vb6.mjs +395 -0
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tree-sitter Bash parser for Cortex.
|
|
3
|
+
*
|
|
4
|
+
* Extracts `function_definition` nodes as chunks — both
|
|
5
|
+
* `function foo { ... }` and `foo() { ... }` styles are handled by
|
|
6
|
+
* tree-sitter-bash as the same node type.
|
|
7
|
+
*
|
|
8
|
+
* Imports cover `source path.sh` and `. path.sh` commands at program
|
|
9
|
+
* scope. Dynamic path expressions like `. "$(dirname "$0")/lib.sh"`
|
|
10
|
+
* are skipped — only static `word` arguments are extracted, since
|
|
11
|
+
* anything else can't be resolved at parse time.
|
|
12
|
+
*
|
|
13
|
+
* Call extraction captures the `command_name` of every `command`
|
|
14
|
+
* node, filtered against a large list of shell builtins and
|
|
15
|
+
* ubiquitous system commands so the call graph reflects user-defined
|
|
16
|
+
* function calls rather than shell plumbing.
|
|
17
|
+
*
|
|
18
|
+
* Naming:
|
|
19
|
+
* top-level function: name = "deploy"
|
|
20
|
+
* nested function: name = "inner" (shell has no real nesting scope)
|
|
21
|
+
*
|
|
22
|
+
* exported: true iff the function name does NOT start with an
|
|
23
|
+
* underscore. Shell has no export/import model per se; this mirrors
|
|
24
|
+
* the convention used by Python and Ruby parsers.
|
|
25
|
+
*
|
|
26
|
+
* parseCode is async; the WASM grammar is lazily loaded on first call
|
|
27
|
+
* and cached for subsequent calls.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
import path from "node:path";
|
|
31
|
+
import fs from "node:fs";
|
|
32
|
+
import { fileURLToPath } from "node:url";
|
|
33
|
+
import {
|
|
34
|
+
dedupe,
|
|
35
|
+
initTreeSitter,
|
|
36
|
+
lineRangeOf,
|
|
37
|
+
loadGrammar,
|
|
38
|
+
parseSource,
|
|
39
|
+
runQuery
|
|
40
|
+
} from "./tree-sitter/base.mjs";
|
|
41
|
+
|
|
42
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
43
|
+
const __dirname = path.dirname(__filename);
|
|
44
|
+
const QUERY_DIR = path.join(__dirname, "tree-sitter", "queries");
|
|
45
|
+
|
|
46
|
+
let BASH_LANG = null;
|
|
47
|
+
let langPromise = null;
|
|
48
|
+
|
|
49
|
+
async function ensureLanguage() {
|
|
50
|
+
if (BASH_LANG) return BASH_LANG;
|
|
51
|
+
if (!langPromise) {
|
|
52
|
+
langPromise = (async () => {
|
|
53
|
+
await initTreeSitter();
|
|
54
|
+
BASH_LANG = await loadGrammar("bash");
|
|
55
|
+
return BASH_LANG;
|
|
56
|
+
})();
|
|
57
|
+
}
|
|
58
|
+
await langPromise;
|
|
59
|
+
return BASH_LANG;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export async function isAvailable() {
|
|
63
|
+
try {
|
|
64
|
+
await ensureLanguage();
|
|
65
|
+
return true;
|
|
66
|
+
} catch {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const CHUNK_QUERY = fs.readFileSync(path.join(QUERY_DIR, "bash.chunks.scm"), "utf8");
|
|
72
|
+
const CALL_QUERY = fs.readFileSync(path.join(QUERY_DIR, "bash.calls.scm"), "utf8");
|
|
73
|
+
const IMPORT_QUERY = fs.readFileSync(path.join(QUERY_DIR, "bash.imports.scm"), "utf8");
|
|
74
|
+
|
|
75
|
+
const LOADER_COMMANDS = new Set(["source", "."]);
|
|
76
|
+
|
|
77
|
+
// Shell builtins and ubiquitous system commands. Filtered out of the
|
|
78
|
+
// call graph so it reflects user-defined function calls, not shell
|
|
79
|
+
// plumbing. Keeping this list deliberately broad — the goal is graph
|
|
80
|
+
// signal-to-noise, not exhaustive coverage.
|
|
81
|
+
const CALL_FILTER = new Set([
|
|
82
|
+
// Builtins
|
|
83
|
+
"echo", "printf", "read", "test", "[", "[[", "true", "false",
|
|
84
|
+
"exit", "return", "break", "continue", "shift", "trap",
|
|
85
|
+
"export", "unset", "set", "local", "declare", "readonly", "typeset",
|
|
86
|
+
"let", "eval", "exec", "source", ".", "alias", "unalias", "type",
|
|
87
|
+
"command", "builtin", "hash", "help", "jobs", "fg", "bg", "kill",
|
|
88
|
+
"pwd", "cd", "pushd", "popd", "dirs", "umask", "ulimit",
|
|
89
|
+
"getopts", "shopt", "enable", "history", "fc", "logout", "suspend",
|
|
90
|
+
"wait", "times", "login", "complete", "compgen",
|
|
91
|
+
// Common system commands
|
|
92
|
+
"ls", "cat", "grep", "sed", "awk", "cut", "sort", "uniq", "wc",
|
|
93
|
+
"head", "tail", "tee", "find", "xargs", "tr", "rev",
|
|
94
|
+
"cp", "mv", "rm", "mkdir", "rmdir", "ln", "touch", "chmod", "chown",
|
|
95
|
+
"tar", "gzip", "gunzip", "zip", "unzip", "curl", "wget",
|
|
96
|
+
"git", "docker", "kubectl", "make", "npm", "yarn",
|
|
97
|
+
"python", "python3", "node", "ruby", "go", "bash", "sh", "zsh",
|
|
98
|
+
"env", "which", "whereis", "whoami", "id", "uname", "hostname",
|
|
99
|
+
"date", "sleep", "ps", "top", "df", "du", "mount", "umount",
|
|
100
|
+
"ssh", "scp", "rsync", "ping", "netstat", "ifconfig", "ip",
|
|
101
|
+
"basename", "dirname", "realpath", "readlink", "file", "stat",
|
|
102
|
+
"awk", "perl", "dd"
|
|
103
|
+
]);
|
|
104
|
+
|
|
105
|
+
function normalizeWhitespace(value) {
|
|
106
|
+
return String(value).replace(/\s+/g, " ").trim();
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function signatureOfDecl(node) {
|
|
110
|
+
const braceIndex = node.text.indexOf("{");
|
|
111
|
+
if (braceIndex === -1) return normalizeWhitespace(node.text);
|
|
112
|
+
return normalizeWhitespace(node.text.slice(0, braceIndex));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function collectCallsInNode(node) {
|
|
116
|
+
const captures = runQuery(BASH_LANG, CALL_QUERY, node);
|
|
117
|
+
const names = captures
|
|
118
|
+
.filter((c) => c.name === "call.name")
|
|
119
|
+
.map((c) => c.node.text)
|
|
120
|
+
.filter((name) => {
|
|
121
|
+
if (!name) return false;
|
|
122
|
+
if (CALL_FILTER.has(name)) return false;
|
|
123
|
+
// Strip absolute paths to compare: /usr/bin/foo -> foo
|
|
124
|
+
const trimmed = name.includes("/") ? name.slice(name.lastIndexOf("/") + 1) : name;
|
|
125
|
+
if (CALL_FILTER.has(trimmed)) return false;
|
|
126
|
+
return true;
|
|
127
|
+
});
|
|
128
|
+
return dedupe(names);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function collectImports(rootNode) {
|
|
132
|
+
const captures = runQuery(BASH_LANG, IMPORT_QUERY, rootNode);
|
|
133
|
+
const imports = [];
|
|
134
|
+
for (const cap of captures) {
|
|
135
|
+
if (cap.name !== "import.cmd") continue;
|
|
136
|
+
const cmd = cap.node;
|
|
137
|
+
|
|
138
|
+
// Only top-level sourcing counts as an "import" — nested
|
|
139
|
+
// source-calls inside function bodies are conditional runtime
|
|
140
|
+
// behavior rather than declared dependencies.
|
|
141
|
+
let ancestor = cmd.parent;
|
|
142
|
+
let isTopLevel = true;
|
|
143
|
+
while (ancestor) {
|
|
144
|
+
if (
|
|
145
|
+
ancestor.type === "function_definition" ||
|
|
146
|
+
ancestor.type === "compound_statement" ||
|
|
147
|
+
ancestor.type === "subshell"
|
|
148
|
+
) {
|
|
149
|
+
isTopLevel = false;
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
ancestor = ancestor.parent;
|
|
153
|
+
}
|
|
154
|
+
if (!isTopLevel) continue;
|
|
155
|
+
|
|
156
|
+
const nameNode = cmd.childForFieldName("name") ?? cmd.namedChild(0);
|
|
157
|
+
if (!nameNode) continue;
|
|
158
|
+
const commandText = nameNode.text;
|
|
159
|
+
if (!LOADER_COMMANDS.has(commandText)) continue;
|
|
160
|
+
|
|
161
|
+
// First static `word` argument is the sourced path. Dynamic
|
|
162
|
+
// expressions (strings, substitutions) are skipped.
|
|
163
|
+
for (let i = 0; i < cmd.namedChildCount; i += 1) {
|
|
164
|
+
const child = cmd.namedChild(i);
|
|
165
|
+
if (child === nameNode) continue;
|
|
166
|
+
if (child.type === "word") {
|
|
167
|
+
imports.push(child.text);
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return dedupe(imports);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function buildFunctionChunk(node, imports, language) {
|
|
176
|
+
const nameNode = node.childForFieldName("name");
|
|
177
|
+
if (!nameNode) return null;
|
|
178
|
+
const name = nameNode.text;
|
|
179
|
+
const { startLine, endLine } = lineRangeOf(node);
|
|
180
|
+
return {
|
|
181
|
+
name,
|
|
182
|
+
kind: "function",
|
|
183
|
+
signature: signatureOfDecl(node),
|
|
184
|
+
body: node.text,
|
|
185
|
+
startLine,
|
|
186
|
+
endLine,
|
|
187
|
+
language,
|
|
188
|
+
exported: !name.startsWith("_"),
|
|
189
|
+
calls: collectCallsInNode(node),
|
|
190
|
+
imports
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export async function parseCode(code, filePath, language = "bash") {
|
|
195
|
+
await ensureLanguage();
|
|
196
|
+
const { tree } = parseSource(BASH_LANG, code);
|
|
197
|
+
const root = tree.rootNode;
|
|
198
|
+
const imports = collectImports(root);
|
|
199
|
+
|
|
200
|
+
const captures = runQuery(BASH_LANG, CHUNK_QUERY, root);
|
|
201
|
+
const declCaptures = captures.filter((c) => c.name.endsWith(".decl"));
|
|
202
|
+
|
|
203
|
+
const chunks = [];
|
|
204
|
+
for (const cap of declCaptures) {
|
|
205
|
+
const chunk = buildFunctionChunk(cap.node, imports, language);
|
|
206
|
+
if (chunk) chunks.push(chunk);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const seen = new Set();
|
|
210
|
+
const deduped = chunks.filter((chunk) => {
|
|
211
|
+
const key = `${chunk.kind}|${chunk.name}|${chunk.startLine}|${chunk.endLine}`;
|
|
212
|
+
if (seen.has(key)) return false;
|
|
213
|
+
seen.add(key);
|
|
214
|
+
return true;
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
return { chunks: deduped, errors: [] };
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
221
|
+
const target = process.argv[2];
|
|
222
|
+
if (!target) {
|
|
223
|
+
console.error("Usage: bash-treesitter.mjs <file.sh>");
|
|
224
|
+
process.exit(1);
|
|
225
|
+
}
|
|
226
|
+
const code = fs.readFileSync(target, "utf8");
|
|
227
|
+
const result = await parseCode(code, target, "bash");
|
|
228
|
+
console.log(JSON.stringify(result, null, 2));
|
|
229
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* C/C++ parser dispatcher.
|
|
3
|
+
*
|
|
4
|
+
* Selects between the tree-sitter parser (default, no runtime deps)
|
|
5
|
+
* and the legacy clang-bridge parser based on the CORTEX_CPP_PARSER
|
|
6
|
+
* environment variable. Selection is deferred until the first parseCode
|
|
7
|
+
* call so no WASM is loaded if the project contains no C/C++ files.
|
|
8
|
+
*
|
|
9
|
+
* CORTEX_CPP_PARSER=clang → always use clang-bridge
|
|
10
|
+
* CORTEX_CPP_PARSER=tree-sitter → force tree-sitter (error if unavailable)
|
|
11
|
+
* unset / other → tree-sitter with clang auto-fallback
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const choice = process.env.CORTEX_CPP_PARSER;
|
|
15
|
+
|
|
16
|
+
let activeParser = null;
|
|
17
|
+
let resolvePromise = null;
|
|
18
|
+
|
|
19
|
+
function availabilityOf(parser) {
|
|
20
|
+
if (typeof parser.isAvailable === "function") return parser.isAvailable;
|
|
21
|
+
if (typeof parser.isCppParserAvailable === "function") return parser.isCppParserAvailable;
|
|
22
|
+
return () => typeof parser.parseCode === "function";
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async function resolveParser() {
|
|
26
|
+
if (activeParser) return activeParser;
|
|
27
|
+
if (resolvePromise) return resolvePromise;
|
|
28
|
+
resolvePromise = (async () => {
|
|
29
|
+
if (choice === "clang") {
|
|
30
|
+
activeParser = await import("./cpp.mjs");
|
|
31
|
+
} else if (choice === "tree-sitter") {
|
|
32
|
+
activeParser = await import("./cpp-treesitter.mjs");
|
|
33
|
+
} else {
|
|
34
|
+
const ts = await import("./cpp-treesitter.mjs");
|
|
35
|
+
if (await ts.isAvailable()) {
|
|
36
|
+
activeParser = ts;
|
|
37
|
+
} else {
|
|
38
|
+
activeParser = await import("./cpp.mjs");
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return activeParser;
|
|
42
|
+
})();
|
|
43
|
+
return resolvePromise;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export async function parseCode(code, filePath, language = "cpp") {
|
|
47
|
+
const parser = await resolveParser();
|
|
48
|
+
return parser.parseCode(code, filePath, language);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export async function isCppParserAvailable() {
|
|
52
|
+
const parser = await resolveParser();
|
|
53
|
+
const check = availabilityOf(parser);
|
|
54
|
+
const result = check();
|
|
55
|
+
return result && typeof result.then === "function" ? await result : result;
|
|
56
|
+
}
|
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tree-sitter C/C++ parser for Cortex.
|
|
3
|
+
*
|
|
4
|
+
* Uses tree-sitter-cpp (a superset that parses C correctly) as a
|
|
5
|
+
* single grammar for .c, .h, .cpp, .cc, .hpp, .hh files. This removes
|
|
6
|
+
* the clang runtime dependency that the legacy cpp.mjs parser required.
|
|
7
|
+
*
|
|
8
|
+
* Chunk shape matches the Cortex convention. Methods and nested types
|
|
9
|
+
* are qualified by their enclosing class/struct/union/namespace path
|
|
10
|
+
* using `::` as the separator, matching C++ source syntax.
|
|
11
|
+
*
|
|
12
|
+
* Naming:
|
|
13
|
+
* free function: name = "add"
|
|
14
|
+
* method in class body: name = "Foo::bar"
|
|
15
|
+
* out-of-class method: name = "Foo::bar" (def like `Foo::bar()`)
|
|
16
|
+
* nested class: name = "Outer::Inner"
|
|
17
|
+
* namespace function: name = "app::handler"
|
|
18
|
+
* namespace class: name = "app::Service"
|
|
19
|
+
*
|
|
20
|
+
* parseCode is async; the WASM grammar is lazily loaded on first call
|
|
21
|
+
* and cached for subsequent calls.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import path from "node:path";
|
|
25
|
+
import fs from "node:fs";
|
|
26
|
+
import { fileURLToPath } from "node:url";
|
|
27
|
+
import {
|
|
28
|
+
dedupe,
|
|
29
|
+
initTreeSitter,
|
|
30
|
+
lineRangeOf,
|
|
31
|
+
loadGrammar,
|
|
32
|
+
parseSource,
|
|
33
|
+
runQuery
|
|
34
|
+
} from "./tree-sitter/base.mjs";
|
|
35
|
+
|
|
36
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
37
|
+
const __dirname = path.dirname(__filename);
|
|
38
|
+
const QUERY_DIR = path.join(__dirname, "tree-sitter", "queries");
|
|
39
|
+
|
|
40
|
+
let CPP_LANG = null;
|
|
41
|
+
let langPromise = null;
|
|
42
|
+
|
|
43
|
+
async function ensureLanguage() {
|
|
44
|
+
if (CPP_LANG) return CPP_LANG;
|
|
45
|
+
if (!langPromise) {
|
|
46
|
+
langPromise = (async () => {
|
|
47
|
+
await initTreeSitter();
|
|
48
|
+
CPP_LANG = await loadGrammar("cpp");
|
|
49
|
+
return CPP_LANG;
|
|
50
|
+
})();
|
|
51
|
+
}
|
|
52
|
+
await langPromise;
|
|
53
|
+
return CPP_LANG;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const CHUNK_QUERY = fs.readFileSync(path.join(QUERY_DIR, "cpp.chunks.scm"), "utf8");
|
|
57
|
+
const CALL_QUERY = fs.readFileSync(path.join(QUERY_DIR, "cpp.calls.scm"), "utf8");
|
|
58
|
+
const IMPORT_QUERY = fs.readFileSync(path.join(QUERY_DIR, "cpp.imports.scm"), "utf8");
|
|
59
|
+
|
|
60
|
+
// Calls that are control-flow, builtins, or stdlib logging noise —
|
|
61
|
+
// kept out of the graph so real function-to-function edges stand out.
|
|
62
|
+
const CALL_FILTER = new Set([
|
|
63
|
+
"sizeof", "alignof", "typeid", "decltype", "typeof",
|
|
64
|
+
"static_cast", "dynamic_cast", "reinterpret_cast", "const_cast",
|
|
65
|
+
"printf", "fprintf", "sprintf", "snprintf", "puts",
|
|
66
|
+
"malloc", "free", "calloc", "realloc", "memcpy", "memset", "memmove"
|
|
67
|
+
]);
|
|
68
|
+
|
|
69
|
+
function normalizeWhitespace(value) {
|
|
70
|
+
return String(value).replace(/\s+/g, " ").trim();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function signatureOfDecl(node) {
|
|
74
|
+
const braceIndex = node.text.indexOf("{");
|
|
75
|
+
const semiIndex = node.text.indexOf(";");
|
|
76
|
+
const end = braceIndex === -1 ? semiIndex : (semiIndex === -1 ? braceIndex : Math.min(braceIndex, semiIndex));
|
|
77
|
+
if (end === -1) return normalizeWhitespace(node.text);
|
|
78
|
+
return normalizeWhitespace(node.text.slice(0, end));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function collectCallsInNode(node) {
|
|
82
|
+
const captures = runQuery(CPP_LANG, CALL_QUERY, node);
|
|
83
|
+
const names = captures
|
|
84
|
+
.filter((c) => c.name === "call.name")
|
|
85
|
+
.map((c) => c.node.text)
|
|
86
|
+
.filter((name) => name && !CALL_FILTER.has(name));
|
|
87
|
+
return dedupe(names);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function collectImports(rootNode) {
|
|
91
|
+
const captures = runQuery(CPP_LANG, IMPORT_QUERY, rootNode);
|
|
92
|
+
const imports = [];
|
|
93
|
+
for (const cap of captures) {
|
|
94
|
+
if (cap.name !== "include.decl") continue;
|
|
95
|
+
const decl = cap.node;
|
|
96
|
+
for (let i = 0; i < decl.namedChildCount; i += 1) {
|
|
97
|
+
const child = decl.namedChild(i);
|
|
98
|
+
if (child.type === "system_lib_string") {
|
|
99
|
+
// <vector> — strip angle brackets
|
|
100
|
+
const text = child.text;
|
|
101
|
+
imports.push(text.startsWith("<") && text.endsWith(">") ? text.slice(1, -1) : text);
|
|
102
|
+
} else if (child.type === "string_literal") {
|
|
103
|
+
// "local.h" — walk for string_content
|
|
104
|
+
for (let j = 0; j < child.namedChildCount; j += 1) {
|
|
105
|
+
const inner = child.namedChild(j);
|
|
106
|
+
if (inner.type === "string_content") {
|
|
107
|
+
imports.push(inner.text);
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return dedupe(imports);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Walk up the tree and collect names of enclosing classes, structs,
|
|
119
|
+
* unions, and namespaces. Nested namespaces like `namespace a::b`
|
|
120
|
+
* contribute both `a` and `b` to the path.
|
|
121
|
+
*/
|
|
122
|
+
function enclosingScopePath(node) {
|
|
123
|
+
const parts = [];
|
|
124
|
+
let cur = node.parent;
|
|
125
|
+
while (cur && cur.type !== "translation_unit") {
|
|
126
|
+
if (
|
|
127
|
+
cur.type === "class_specifier" ||
|
|
128
|
+
cur.type === "struct_specifier" ||
|
|
129
|
+
cur.type === "union_specifier"
|
|
130
|
+
) {
|
|
131
|
+
const nameNode = cur.childForFieldName("name");
|
|
132
|
+
if (nameNode) parts.unshift(nameNode.text);
|
|
133
|
+
} else if (cur.type === "namespace_definition") {
|
|
134
|
+
parts.unshift(...namespaceDefinitionNames(cur));
|
|
135
|
+
}
|
|
136
|
+
cur = cur.parent;
|
|
137
|
+
}
|
|
138
|
+
return parts;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function namespaceDefinitionNames(nsNode) {
|
|
142
|
+
const names = [];
|
|
143
|
+
// Could have a single namespace_identifier OR a nested_namespace_specifier
|
|
144
|
+
for (let i = 0; i < nsNode.namedChildCount; i += 1) {
|
|
145
|
+
const child = nsNode.namedChild(i);
|
|
146
|
+
if (child.type === "namespace_identifier") {
|
|
147
|
+
names.push(child.text);
|
|
148
|
+
} else if (child.type === "nested_namespace_specifier") {
|
|
149
|
+
for (let j = 0; j < child.namedChildCount; j += 1) {
|
|
150
|
+
const inner = child.namedChild(j);
|
|
151
|
+
if (inner.type === "namespace_identifier") names.push(inner.text);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return names;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Extract the function name and any qualifying path from a
|
|
160
|
+
* function_definition's function_declarator. Returns { name, isMethod }.
|
|
161
|
+
*
|
|
162
|
+
* - identifier inside declarator → free function, name = identifier
|
|
163
|
+
* - field_identifier inside declarator (in class body) → method,
|
|
164
|
+
* name = field identifier, qualifies with enclosing class
|
|
165
|
+
* - qualified_identifier (like `Foo::bar`) → out-of-class method,
|
|
166
|
+
* name encoded as `Foo::bar` directly
|
|
167
|
+
*/
|
|
168
|
+
function functionNameFrom(fnDefNode) {
|
|
169
|
+
let declarator = null;
|
|
170
|
+
for (let i = 0; i < fnDefNode.namedChildCount; i += 1) {
|
|
171
|
+
const child = fnDefNode.namedChild(i);
|
|
172
|
+
if (child.type === "function_declarator") {
|
|
173
|
+
declarator = child;
|
|
174
|
+
break;
|
|
175
|
+
}
|
|
176
|
+
if (child.type === "pointer_declarator" || child.type === "reference_declarator") {
|
|
177
|
+
for (let j = 0; j < child.namedChildCount; j += 1) {
|
|
178
|
+
const inner = child.namedChild(j);
|
|
179
|
+
if (inner.type === "function_declarator") {
|
|
180
|
+
declarator = inner;
|
|
181
|
+
break;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
if (declarator) break;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
if (!declarator) return null;
|
|
188
|
+
|
|
189
|
+
for (let i = 0; i < declarator.namedChildCount; i += 1) {
|
|
190
|
+
const child = declarator.namedChild(i);
|
|
191
|
+
if (child.type === "identifier") {
|
|
192
|
+
return { name: child.text, qualifiedForm: null };
|
|
193
|
+
}
|
|
194
|
+
if (child.type === "field_identifier") {
|
|
195
|
+
return { name: child.text, qualifiedForm: null };
|
|
196
|
+
}
|
|
197
|
+
if (child.type === "qualified_identifier") {
|
|
198
|
+
return { name: null, qualifiedForm: child.text };
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function buildFunctionChunk(node, imports, language) {
|
|
205
|
+
const nameInfo = functionNameFrom(node);
|
|
206
|
+
if (!nameInfo) return null;
|
|
207
|
+
|
|
208
|
+
let qualifiedName;
|
|
209
|
+
let kind;
|
|
210
|
+
|
|
211
|
+
if (nameInfo.qualifiedForm) {
|
|
212
|
+
qualifiedName = nameInfo.qualifiedForm;
|
|
213
|
+
kind = "method";
|
|
214
|
+
} else {
|
|
215
|
+
const scope = enclosingScopePath(node);
|
|
216
|
+
const baseName = nameInfo.name;
|
|
217
|
+
if (scope.length > 0) {
|
|
218
|
+
qualifiedName = `${scope.join("::")}::${baseName}`;
|
|
219
|
+
kind = "method";
|
|
220
|
+
} else {
|
|
221
|
+
qualifiedName = baseName;
|
|
222
|
+
kind = "function";
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const { startLine, endLine } = lineRangeOf(node);
|
|
227
|
+
return {
|
|
228
|
+
name: qualifiedName,
|
|
229
|
+
kind,
|
|
230
|
+
signature: signatureOfDecl(node),
|
|
231
|
+
body: node.text,
|
|
232
|
+
startLine,
|
|
233
|
+
endLine,
|
|
234
|
+
language,
|
|
235
|
+
exported: true,
|
|
236
|
+
calls: collectCallsInNode(node),
|
|
237
|
+
imports
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function buildTypeChunk(node, kind, language) {
|
|
242
|
+
const nameNode = node.childForFieldName("name");
|
|
243
|
+
if (!nameNode) return null;
|
|
244
|
+
const baseName = nameNode.text;
|
|
245
|
+
const scope = enclosingScopePath(node);
|
|
246
|
+
const qualifiedName = scope.length > 0 ? `${scope.join("::")}::${baseName}` : baseName;
|
|
247
|
+
const { startLine, endLine } = lineRangeOf(node);
|
|
248
|
+
return {
|
|
249
|
+
name: qualifiedName,
|
|
250
|
+
kind,
|
|
251
|
+
signature: signatureOfDecl(node),
|
|
252
|
+
body: node.text,
|
|
253
|
+
startLine,
|
|
254
|
+
endLine,
|
|
255
|
+
language,
|
|
256
|
+
exported: true,
|
|
257
|
+
calls: [],
|
|
258
|
+
imports: []
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function buildNamespaceChunk(node, language) {
|
|
263
|
+
const names = namespaceDefinitionNames(node);
|
|
264
|
+
if (names.length === 0) return null;
|
|
265
|
+
const scope = enclosingScopePath(node);
|
|
266
|
+
const fullPath = [...scope, ...names].join("::");
|
|
267
|
+
const { startLine, endLine } = lineRangeOf(node);
|
|
268
|
+
return {
|
|
269
|
+
name: fullPath,
|
|
270
|
+
kind: "namespace",
|
|
271
|
+
signature: signatureOfDecl(node),
|
|
272
|
+
body: node.text,
|
|
273
|
+
startLine,
|
|
274
|
+
endLine,
|
|
275
|
+
language,
|
|
276
|
+
exported: true,
|
|
277
|
+
calls: [],
|
|
278
|
+
imports: []
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
export async function parseCode(code, filePath, language = "cpp") {
|
|
283
|
+
await ensureLanguage();
|
|
284
|
+
const { tree } = parseSource(CPP_LANG, code);
|
|
285
|
+
const root = tree.rootNode;
|
|
286
|
+
const imports = collectImports(root);
|
|
287
|
+
|
|
288
|
+
const captures = runQuery(CPP_LANG, CHUNK_QUERY, root);
|
|
289
|
+
const declCaptures = captures.filter((c) => c.name.endsWith(".decl"));
|
|
290
|
+
|
|
291
|
+
const chunks = [];
|
|
292
|
+
for (const cap of declCaptures) {
|
|
293
|
+
const kindTag = cap.name.split(".")[0];
|
|
294
|
+
let chunk = null;
|
|
295
|
+
if (kindTag === "fn") chunk = buildFunctionChunk(cap.node, imports, language);
|
|
296
|
+
else if (kindTag === "class") chunk = buildTypeChunk(cap.node, "class", language);
|
|
297
|
+
else if (kindTag === "struct") chunk = buildTypeChunk(cap.node, "struct", language);
|
|
298
|
+
else if (kindTag === "union") chunk = buildTypeChunk(cap.node, "union", language);
|
|
299
|
+
else if (kindTag === "enum") chunk = buildTypeChunk(cap.node, "enum", language);
|
|
300
|
+
else if (kindTag === "namespace") chunk = buildNamespaceChunk(cap.node, language);
|
|
301
|
+
if (chunk) chunks.push(chunk);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
const seen = new Set();
|
|
305
|
+
const deduped = chunks.filter((chunk) => {
|
|
306
|
+
const key = `${chunk.kind}|${chunk.name}|${chunk.startLine}|${chunk.endLine}`;
|
|
307
|
+
if (seen.has(key)) return false;
|
|
308
|
+
seen.add(key);
|
|
309
|
+
return true;
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
return { chunks: deduped, errors: [] };
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
export async function isAvailable() {
|
|
316
|
+
try {
|
|
317
|
+
await ensureLanguage();
|
|
318
|
+
return true;
|
|
319
|
+
} catch {
|
|
320
|
+
return false;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
325
|
+
const target = process.argv[2];
|
|
326
|
+
if (!target) {
|
|
327
|
+
console.error("Usage: cpp-treesitter.mjs <file.{c,cpp,h,hpp}>");
|
|
328
|
+
process.exit(1);
|
|
329
|
+
}
|
|
330
|
+
const code = fs.readFileSync(target, "utf8");
|
|
331
|
+
const result = await parseCode(code, target, target.endsWith(".c") || target.endsWith(".h") ? "c" : "cpp");
|
|
332
|
+
console.log(JSON.stringify(result, null, 2));
|
|
333
|
+
}
|