brainclaw 1.16.0 → 1.18.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 +22 -8
- package/dist/brainclaw-vscode.vsix +0 -0
- package/dist/cli/register-review.js +2 -2
- package/dist/commands/code-map.js +4 -1
- package/dist/commands/codev.js +61 -30
- package/dist/commands/doctor.js +14 -1
- package/dist/commands/harvest.js +241 -25
- package/dist/commands/inbox.js +10 -4
- package/dist/commands/loop.js +2 -2
- package/dist/commands/loops-handlers.js +82 -1
- package/dist/commands/mcp-catalog.js +12 -4
- package/dist/commands/mcp-read-handlers.js +90 -7
- package/dist/commands/mcp-schemas.generated.js +3 -0
- package/dist/commands/mcp-write-coordination.js +159 -40
- package/dist/commands/mcp.js +11 -2
- package/dist/core/agent-capability.js +7 -2
- package/dist/core/agent-files.js +53 -2
- package/dist/core/agent-integrations.js +1 -0
- package/dist/core/agentrun-reconciler.js +171 -7
- package/dist/core/agentruns.js +6 -1
- package/dist/core/code-map/aggregate.js +473 -0
- package/dist/core/code-map/backend.js +36 -10
- package/dist/core/code-map/freshness.js +36 -1
- package/dist/core/code-map/lang/c/imports.scm +12 -0
- package/dist/core/code-map/lang/c/index.js +150 -0
- package/dist/core/code-map/lang/c/tags.scm +68 -0
- package/dist/core/code-map/lang/cpp/imports.scm +14 -0
- package/dist/core/code-map/lang/cpp/index.js +149 -0
- package/dist/core/code-map/lang/cpp/tags.scm +87 -0
- package/dist/core/code-map/lang/csharp/imports.scm +20 -0
- package/dist/core/code-map/lang/csharp/index.js +224 -0
- package/dist/core/code-map/lang/csharp/tags.scm +63 -0
- package/dist/core/code-map/lang/go/imports.scm +13 -0
- package/dist/core/code-map/lang/go/index.js +139 -0
- package/dist/core/code-map/lang/go/tags.scm +36 -0
- package/dist/core/code-map/lang/providers.js +12 -1
- package/dist/core/code-map/lang/ruby/imports.scm +24 -0
- package/dist/core/code-map/lang/ruby/index.js +198 -0
- package/dist/core/code-map/lang/ruby/tags.scm +49 -0
- package/dist/core/code-map/lang/rust/imports.scm +44 -0
- package/dist/core/code-map/lang/rust/index.js +136 -0
- package/dist/core/code-map/lang/rust/tags.scm +47 -0
- package/dist/core/code-map/query.js +229 -80
- package/dist/core/code-map/types.js +18 -0
- package/dist/core/code-map/work-section.js +8 -7
- package/dist/core/codev-responses.js +16 -0
- package/dist/core/dispatcher.js +209 -29
- package/dist/core/execution-adapters.js +29 -3
- package/dist/core/ideation-loop-close.js +124 -0
- package/dist/core/loops/artifact-resolver.js +197 -0
- package/dist/core/loops/attempt-reservation.js +576 -0
- package/dist/core/loops/commit-intent.js +494 -0
- package/dist/core/loops/facade-schema.js +48 -0
- package/dist/core/loops/impl-bind.js +144 -0
- package/dist/core/loops/index.js +1 -1
- package/dist/core/loops/iteration-engine.js +29 -0
- package/dist/core/loops/lock.js +14 -0
- package/dist/core/loops/project-resolution.js +157 -0
- package/dist/core/loops/reconcile-turn.js +369 -0
- package/dist/core/loops/result-reducers.js +88 -0
- package/dist/core/loops/store.js +46 -7
- package/dist/core/loops/types.js +139 -11
- package/dist/core/loops/verbs.js +9 -3
- package/dist/core/loops/verify-command.js +209 -0
- package/dist/core/messaging.js +58 -5
- package/dist/core/review-loop-close.js +106 -34
- package/dist/core/review-loop-turn-dispatch.js +445 -0
- package/dist/core/runtime-signals.js +68 -0
- package/dist/core/schema.js +34 -0
- package/dist/core/worktree.js +240 -22
- package/dist/facts.js +10 -10
- package/dist/facts.json +9 -9
- package/dist/wasm/tree-sitter-c.wasm +0 -0
- package/dist/wasm/tree-sitter-c_sharp.wasm +0 -0
- package/dist/wasm/tree-sitter-cpp.wasm +0 -0
- package/dist/wasm/tree-sitter-go.wasm +0 -0
- package/dist/wasm/tree-sitter-ruby.wasm +0 -0
- package/dist/wasm/tree-sitter-rust.wasm +0 -0
- package/docs/cli.md +1 -1
- package/docs/code-map.md +22 -6
- package/docs/concepts/loop-engine.md +28 -2
- package/docs/concepts/observer-protocol.md +22 -0
- package/docs/integrations/codex.md +19 -3
- package/docs/mcp-schema-changelog.md +43 -1
- package/package.json +1 -1
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code Map langs batch 2 — RubyProvider (provider #6).
|
|
3
|
+
*
|
|
4
|
+
* Owns `.rb` (runtime lang `ruby`). `extractDraft` delegates to the generic
|
|
5
|
+
* query-runtime; the curated `tags.scm`/`imports.scm` (this dir) drive extraction.
|
|
6
|
+
*
|
|
7
|
+
* Definitions (verified via dogfood against tree-sitter-ruby):
|
|
8
|
+
* - `class` -> class (name: a `constant`)
|
|
9
|
+
* - `module` -> namespace (name: a `constant`; Ruby modules are namespaces/mixins)
|
|
10
|
+
* - `method` (def) -> method (uniform capture)
|
|
11
|
+
* - `singleton_method` (def self.x) -> method (always a member method)
|
|
12
|
+
* - constant assignment (`FOO = ...`, left is a bare `constant`) -> constant
|
|
13
|
+
*
|
|
14
|
+
* refine() carries what the structural query CANNOT express (mirrors Python's
|
|
15
|
+
* dynamic-language refine):
|
|
16
|
+
* - a `method` whose enclosing scope is NOT a class/module (a TOP-LEVEL `def`, or a
|
|
17
|
+
* def nested inside another def/block) -> reclassified `function` (Ruby's
|
|
18
|
+
* module-level-function analog). A `method` directly in a class/module body stays
|
|
19
|
+
* `method`. `singleton_method` stays `method` regardless of location.
|
|
20
|
+
* - import sources: strip the surrounding quotes from the required string
|
|
21
|
+
* (`'sinatra'` -> `sinatra`). Defensive/idempotent — the query-runtime already
|
|
22
|
+
* strips quotes; kept to match the Go provider's documented pattern.
|
|
23
|
+
*
|
|
24
|
+
* Imports: Ruby has NO static import statement — `require`/`require_relative` are
|
|
25
|
+
* method CALLS. imports.scm captures those calls (a `#any-of?` predicate limits the
|
|
26
|
+
* match to the require family) and the module node `name` is the required string.
|
|
27
|
+
* Best-effort T2 only: no path resolution (T3 = none). NO exports edges — Ruby has
|
|
28
|
+
* no export statement (capabilities: T1.definitions + T2.imports).
|
|
29
|
+
*
|
|
30
|
+
* Identity is owned by the CORE finalizer — this provider mints NO ids. The grammar
|
|
31
|
+
* loads through the SHARED engine glue (`loadGrammarWasm`), never a fresh
|
|
32
|
+
* `web-tree-sitter` import (trp_8df65ab7).
|
|
33
|
+
*/
|
|
34
|
+
import crypto from 'node:crypto';
|
|
35
|
+
import fs from 'node:fs';
|
|
36
|
+
import path from 'node:path';
|
|
37
|
+
import { fileURLToPath } from 'node:url';
|
|
38
|
+
import { loadGrammarWasm, grammarHashForWasm } from '../../wasm-loader.js';
|
|
39
|
+
import { extractWithQueries } from '../query-runtime.js';
|
|
40
|
+
const HERE = path.dirname(fileURLToPath(import.meta.url));
|
|
41
|
+
const RUBY_WASM_BASENAME = 'tree-sitter-ruby.wasm';
|
|
42
|
+
const RUBY_WASM_NODE_MODULES_SPEC = 'tree-sitter-wasms/out/tree-sitter-ruby.wasm';
|
|
43
|
+
const RUBY_GRAMMAR_NAME = 'tree-sitter-ruby';
|
|
44
|
+
/** Resolve a vendored `.scm` next to this module (dist) or from the source tree. */
|
|
45
|
+
function readScm(basename) {
|
|
46
|
+
const local = path.join(HERE, basename);
|
|
47
|
+
if (fs.existsSync(local))
|
|
48
|
+
return fs.readFileSync(local, 'utf-8');
|
|
49
|
+
let dir = HERE;
|
|
50
|
+
for (let i = 0; i < 12; i++) {
|
|
51
|
+
if (fs.existsSync(path.join(dir, 'package.json'))) {
|
|
52
|
+
const fromSrc = path.join(dir, 'src', 'core', 'code-map', 'lang', 'ruby', basename);
|
|
53
|
+
if (fs.existsSync(fromSrc))
|
|
54
|
+
return fs.readFileSync(fromSrc, 'utf-8');
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
const parent = path.dirname(dir);
|
|
58
|
+
if (parent === dir)
|
|
59
|
+
break;
|
|
60
|
+
dir = parent;
|
|
61
|
+
}
|
|
62
|
+
throw new Error(`code-map: could not locate query asset ${basename} (from ${HERE})`);
|
|
63
|
+
}
|
|
64
|
+
function sha256(s) {
|
|
65
|
+
return `sha256:${crypto.createHash('sha256').update(s, 'utf-8').digest('hex')}`;
|
|
66
|
+
}
|
|
67
|
+
const TAGS = readScm('tags.scm');
|
|
68
|
+
const IMPORTS = readScm('imports.scm');
|
|
69
|
+
const TAGS_HASH = sha256(TAGS);
|
|
70
|
+
const IMPORTS_HASH = sha256(IMPORTS);
|
|
71
|
+
const parser = {
|
|
72
|
+
grammarForLang: () => loadGrammarWasm(RUBY_WASM_BASENAME, RUBY_WASM_NODE_MODULES_SPEC),
|
|
73
|
+
grammarNameForLang: () => RUBY_GRAMMAR_NAME,
|
|
74
|
+
grammarHashForLang: () => grammarHashForWasm(RUBY_WASM_BASENAME, RUBY_WASM_NODE_MODULES_SPEC),
|
|
75
|
+
};
|
|
76
|
+
const queries = {
|
|
77
|
+
tags: { name: 'tags', sourceForLang: () => TAGS, hashForLang: () => TAGS_HASH },
|
|
78
|
+
imports: { name: 'imports', sourceForLang: () => IMPORTS, hashForLang: () => IMPORTS_HASH },
|
|
79
|
+
// Ruby has no import statement — the require CALL is the span/ordinal anchor.
|
|
80
|
+
enclosingStatementNodeTypes: ['call'],
|
|
81
|
+
captureMap: [
|
|
82
|
+
{ capture: 'definition.class.node', field: 'node', subtype: 'class' },
|
|
83
|
+
{ capture: 'definition.class.name', field: 'name' },
|
|
84
|
+
{ capture: 'definition.namespace.node', field: 'node', subtype: 'namespace' },
|
|
85
|
+
{ capture: 'definition.namespace.name', field: 'name' },
|
|
86
|
+
{ capture: 'definition.method.node', field: 'node', subtype: 'method' },
|
|
87
|
+
{ capture: 'definition.method.name', field: 'name' },
|
|
88
|
+
{ capture: 'definition.constant.node', field: 'node', subtype: 'constant' },
|
|
89
|
+
{ capture: 'definition.constant.name', field: 'name' },
|
|
90
|
+
{ capture: 'import.source', field: 'source' },
|
|
91
|
+
],
|
|
92
|
+
};
|
|
93
|
+
const vocabulary = {
|
|
94
|
+
nodeSubtypes: ['class', 'namespace', 'method', 'function', 'constant'],
|
|
95
|
+
edgeKinds: ['contains', 'defines', 'imports'],
|
|
96
|
+
captureMap: queries.captureMap,
|
|
97
|
+
};
|
|
98
|
+
const capabilities = {
|
|
99
|
+
tiers: ['T1.definitions', 'T2.imports'],
|
|
100
|
+
proven: {
|
|
101
|
+
'T1.definitions': true,
|
|
102
|
+
'T2.imports': true,
|
|
103
|
+
'T3.import_resolution': false,
|
|
104
|
+
'T4.tests_for': false,
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
/** Strip the surrounding quotes from a required-string import path. */
|
|
108
|
+
function stripQuotes(s) {
|
|
109
|
+
return s.replace(/^['"`]/, '').replace(/['"`]$/, '');
|
|
110
|
+
}
|
|
111
|
+
function isDefSourceNode(v) {
|
|
112
|
+
return (typeof v === 'object' &&
|
|
113
|
+
v !== null &&
|
|
114
|
+
'node' in v &&
|
|
115
|
+
typeof v.node === 'object');
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* True iff `defNode` (a `method`) is a member of a class/module — i.e. directly owned
|
|
119
|
+
* by a class/module body. tree-sitter-ruby nests a def's statements under a
|
|
120
|
+
* `body_statement` whose parent is the enclosing `class`/`module`/`singleton_class`;
|
|
121
|
+
* a TOP-LEVEL def's parent is `program` (no body_statement wrapper), and a def nested
|
|
122
|
+
* in another def/block has a `body_statement` whose parent is that def/block (not a
|
|
123
|
+
* class/module). So a member method is exactly: parent `body_statement` whose parent
|
|
124
|
+
* is `class` | `module` | `singleton_class`.
|
|
125
|
+
*/
|
|
126
|
+
function isClassOrModuleMember(defNode) {
|
|
127
|
+
const owner = defNode.parent;
|
|
128
|
+
if (!owner || owner.type !== 'body_statement')
|
|
129
|
+
return false;
|
|
130
|
+
const container = owner.parent;
|
|
131
|
+
if (!container)
|
|
132
|
+
return false;
|
|
133
|
+
return (container.type === 'class' ||
|
|
134
|
+
container.type === 'module' ||
|
|
135
|
+
container.type === 'singleton_class');
|
|
136
|
+
}
|
|
137
|
+
export class RubyProvider {
|
|
138
|
+
id = 'ruby';
|
|
139
|
+
displayName = 'Ruby';
|
|
140
|
+
languages = ['ruby'];
|
|
141
|
+
extensions = ['.rb'];
|
|
142
|
+
priority = 0;
|
|
143
|
+
version = '0.1.0';
|
|
144
|
+
parser = parser;
|
|
145
|
+
queries = queries;
|
|
146
|
+
vocabulary = vocabulary;
|
|
147
|
+
capabilities = capabilities;
|
|
148
|
+
langForPath(_p) {
|
|
149
|
+
return 'ruby';
|
|
150
|
+
}
|
|
151
|
+
async extractDraft(input, _services) {
|
|
152
|
+
return extractWithQueries({
|
|
153
|
+
providerId: this.id,
|
|
154
|
+
lang: input.lang,
|
|
155
|
+
source: input.source,
|
|
156
|
+
sizeBytes: input.sizeBytes,
|
|
157
|
+
maxParseFileBytes: input.maxParseFileBytes,
|
|
158
|
+
maxQueryWaitMs: input.maxQueryWaitMs,
|
|
159
|
+
path: input.path,
|
|
160
|
+
grammarForLang: this.parser.grammarForLang,
|
|
161
|
+
tagsSource: TAGS,
|
|
162
|
+
tagsHash: TAGS_HASH,
|
|
163
|
+
importsSource: IMPORTS,
|
|
164
|
+
importsHash: IMPORTS_HASH,
|
|
165
|
+
enclosingStatementNodeTypes: queries.enclosingStatementNodeTypes,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Drafts-only refinement:
|
|
170
|
+
* - a `method` def whose enclosing scope is NOT a class/module body -> `function`
|
|
171
|
+
* (top-level defs + defs nested in another def/block); `singleton_method` and
|
|
172
|
+
* class/module-body `method`s stay `method`.
|
|
173
|
+
* - strip the surrounding quotes from each import source (defensive/idempotent).
|
|
174
|
+
*/
|
|
175
|
+
refine(draft, _ctx) {
|
|
176
|
+
const definitions = draft.definitions.map((d) => {
|
|
177
|
+
if (d.subtype !== 'method')
|
|
178
|
+
return d;
|
|
179
|
+
const src = isDefSourceNode(d.sourceNode) ? d.sourceNode : null;
|
|
180
|
+
if (!src)
|
|
181
|
+
return d;
|
|
182
|
+
// singleton_method (def self.x) is always a member method.
|
|
183
|
+
if (src.node.type === 'singleton_method')
|
|
184
|
+
return d;
|
|
185
|
+
// A plain `method` (def) is a `function` unless it is a class/module member.
|
|
186
|
+
return isClassOrModuleMember(src.node) ? d : setSubtype(d, 'function');
|
|
187
|
+
});
|
|
188
|
+
const imports = draft.imports.length === 0
|
|
189
|
+
? draft.imports
|
|
190
|
+
: draft.imports.map((im) => ({ ...im, source: stripQuotes(im.source) }));
|
|
191
|
+
return { ...draft, definitions, imports };
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
function setSubtype(d, subtype) {
|
|
195
|
+
return d.subtype === subtype ? d : { ...d, subtype };
|
|
196
|
+
}
|
|
197
|
+
export const rubyProvider = new RubyProvider();
|
|
198
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
; Code Map — Ruby definitions (tags.scm). Provider #6 (langs batch 2).
|
|
2
|
+
;
|
|
3
|
+
; Capture-name convention (query-runtime.ts is the contract):
|
|
4
|
+
; @definition.<subtype>.node = identity-span anchor (the declaration)
|
|
5
|
+
; @definition.<subtype>.name = the symbol name node
|
|
6
|
+
;
|
|
7
|
+
; Subtype mapping (verified against tree-sitter-ruby via dogfood):
|
|
8
|
+
; class -> class (name: a (constant))
|
|
9
|
+
; module -> namespace (name: a (constant); Ruby modules are namespaces/mixins)
|
|
10
|
+
; method (def ...) -> method (name: any node — identifier/operator/setter)
|
|
11
|
+
; singleton_method (def self.x) -> method (always a member method)
|
|
12
|
+
; constant assignment (FOO = ...) -> constant (left is a bare (constant))
|
|
13
|
+
;
|
|
14
|
+
; TOP-LEVEL vs CLASS/MODULE def: `method` is captured UNIFORMLY as
|
|
15
|
+
; @definition.method (like Python captures every function_definition as function).
|
|
16
|
+
; The provider's refine() reclassifies a `method` whose enclosing scope is NOT a
|
|
17
|
+
; class/module body (i.e. a top-level `def`, or a def nested in another def/block)
|
|
18
|
+
; to `function` — mirroring Python's module-level-function analog. `singleton_method`
|
|
19
|
+
; stays `method` regardless of location (def self.x is a member method by intent).
|
|
20
|
+
; refine() must do this because the structural query cannot inspect the enclosing
|
|
21
|
+
; scope chain (query-runtime nests every def under a `body_statement`).
|
|
22
|
+
;
|
|
23
|
+
; The method `name:` field is captured with `(_)` (any node) so operator/setter
|
|
24
|
+
; methods (`def <=>`, `def []`, `def foo=`) are captured too, not just identifiers.
|
|
25
|
+
;
|
|
26
|
+
; NAMESPACED constant assignments (`Foo::BAR = 1`, left is a `scope_resolution`,
|
|
27
|
+
; NOT a bare `constant`) are intentionally NOT captured in v1 — low value and the
|
|
28
|
+
; bare-constant pattern below deliberately does not match them.
|
|
29
|
+
|
|
30
|
+
(class
|
|
31
|
+
name: (constant) @definition.class.name) @definition.class.node
|
|
32
|
+
|
|
33
|
+
(module
|
|
34
|
+
name: (constant) @definition.namespace.name) @definition.namespace.node
|
|
35
|
+
|
|
36
|
+
; def foo / def <=> / def foo= — captured uniformly; refine() may narrow a
|
|
37
|
+
; non-class/module-body method to `function`.
|
|
38
|
+
(method
|
|
39
|
+
name: (_) @definition.method.name) @definition.method.node
|
|
40
|
+
|
|
41
|
+
; def self.foo — a singleton (class-level) method; always `method`.
|
|
42
|
+
(singleton_method
|
|
43
|
+
name: (_) @definition.method.name) @definition.method.node
|
|
44
|
+
|
|
45
|
+
; FOO = ... — a constant assignment (left is a bare constant). Captured at ANY depth
|
|
46
|
+
; (top-level, class body, module body) — a Ruby constant is a meaningful symbol
|
|
47
|
+
; wherever it is declared (parity with Go's const capture).
|
|
48
|
+
(assignment
|
|
49
|
+
left: (constant) @definition.constant.name) @definition.constant.node
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
; Code Map — Rust imports (imports.scm). Provider #6 (langs batch 2).
|
|
2
|
+
;
|
|
3
|
+
; enclosingStatementNodeTypes = [use_declaration] (the import span/ordinal anchor).
|
|
4
|
+
; Rust `use` paths are bare `a::b::c` (NO quotes) so — unlike Go — nothing to strip,
|
|
5
|
+
; and the common use-tree shapes expand STRUCTURALLY in the query (no refine()):
|
|
6
|
+
; use a::b::c; -> module a::b::c
|
|
7
|
+
; use foo; -> module foo
|
|
8
|
+
; use a::b::{c, d}; -> module a::b + imported names c, d (group-use)
|
|
9
|
+
; use a::b as c; -> module a::b + imported name c (alias)
|
|
10
|
+
; use a::b::*; -> module a::b + imported name '*' (wildcard)
|
|
11
|
+
; Grouping is keyed by the captured @import.source NODE (query-runtime), so the two
|
|
12
|
+
; matches a `{c, d}` list produces share one source node -> one module with names
|
|
13
|
+
; [c, d].
|
|
14
|
+
;
|
|
15
|
+
; v1 limitations (documented, not bugs): NESTED use-trees `use a::{b, c::{d}}` capture
|
|
16
|
+
; only the top path `a` + the direct-identifier members (the nested `c::{…}` subtree
|
|
17
|
+
; is dropped); `use a::{self, b}` drops the `self`; `pub use` re-exports are treated
|
|
18
|
+
; as plain imports (NO exports edge — Rust re-export modelling deferred, capabilities
|
|
19
|
+
; declare T2 imports only).
|
|
20
|
+
|
|
21
|
+
; use a::b::c; / use foo; (direct scoped/plain path is the module source)
|
|
22
|
+
(use_declaration
|
|
23
|
+
argument: [
|
|
24
|
+
(scoped_identifier)
|
|
25
|
+
(identifier)
|
|
26
|
+
] @import.source)
|
|
27
|
+
|
|
28
|
+
; use a::b::{c, d}; -> source a::b, imported names c, d
|
|
29
|
+
(use_declaration
|
|
30
|
+
argument: (scoped_use_list
|
|
31
|
+
path: [(scoped_identifier) (identifier)] @import.source
|
|
32
|
+
list: (use_list (identifier) @import.named.name)))
|
|
33
|
+
|
|
34
|
+
; use a::b as c; -> source a::b, imported name c
|
|
35
|
+
(use_declaration
|
|
36
|
+
argument: (use_as_clause
|
|
37
|
+
path: [(scoped_identifier) (identifier)] @import.source
|
|
38
|
+
alias: (identifier) @import.named.name))
|
|
39
|
+
|
|
40
|
+
; use a::b::*; -> source a::b, imported name '*'
|
|
41
|
+
(use_declaration
|
|
42
|
+
argument: (use_wildcard
|
|
43
|
+
[(scoped_identifier) (identifier)] @import.source
|
|
44
|
+
"*" @import.namespace.name))
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code Map langs batch 2 — RustProvider (provider #6).
|
|
3
|
+
*
|
|
4
|
+
* Owns `.rs` (runtime lang `rust`). `extractDraft` delegates to the generic
|
|
5
|
+
* query-runtime; the curated `tags.scm`/`imports.scm` (this dir) drive extraction.
|
|
6
|
+
* Definition subtypes are fixed by the query (struct→class, enum/function/constant/
|
|
7
|
+
* type/macro/namespace universal, `trait`→namespaced `rust.trait`). Rust methods are
|
|
8
|
+
* `function_item` nodes nested in an impl/trait body — the SAME node type as a free
|
|
9
|
+
* `fn` — so v1 maps ALL `function_item`→function (no `method` subtype; tree-sitter
|
|
10
|
+
* can't express "not inside an impl").
|
|
11
|
+
*
|
|
12
|
+
* NO `refine()` — Rust `use` paths are bare `a::b::c` (no quotes to strip), and the
|
|
13
|
+
* common use-tree shapes (group `{a,b}`, `as` alias, `*` wildcard) expand
|
|
14
|
+
* STRUCTURALLY in imports.scm via the runtime's per-`@import.source`-node grouping.
|
|
15
|
+
*
|
|
16
|
+
* NO exports edges — `pub use` re-export modelling is deferred (capabilities: T2 =
|
|
17
|
+
* imports). Identity is owned by the CORE finalizer — this provider mints NO ids.
|
|
18
|
+
* The grammar loads through the SHARED engine glue (`loadGrammarWasm`), never a
|
|
19
|
+
* fresh `web-tree-sitter` import (trp_8df65ab7).
|
|
20
|
+
*/
|
|
21
|
+
import crypto from 'node:crypto';
|
|
22
|
+
import fs from 'node:fs';
|
|
23
|
+
import path from 'node:path';
|
|
24
|
+
import { fileURLToPath } from 'node:url';
|
|
25
|
+
import { loadGrammarWasm, grammarHashForWasm } from '../../wasm-loader.js';
|
|
26
|
+
import { extractWithQueries } from '../query-runtime.js';
|
|
27
|
+
const HERE = path.dirname(fileURLToPath(import.meta.url));
|
|
28
|
+
const RUST_WASM_BASENAME = 'tree-sitter-rust.wasm';
|
|
29
|
+
const RUST_WASM_NODE_MODULES_SPEC = 'tree-sitter-wasms/out/tree-sitter-rust.wasm';
|
|
30
|
+
const RUST_GRAMMAR_NAME = 'tree-sitter-rust';
|
|
31
|
+
/** Resolve a vendored `.scm` next to this module (dist) or from the source tree. */
|
|
32
|
+
function readScm(basename) {
|
|
33
|
+
const local = path.join(HERE, basename);
|
|
34
|
+
if (fs.existsSync(local))
|
|
35
|
+
return fs.readFileSync(local, 'utf-8');
|
|
36
|
+
let dir = HERE;
|
|
37
|
+
for (let i = 0; i < 12; i++) {
|
|
38
|
+
if (fs.existsSync(path.join(dir, 'package.json'))) {
|
|
39
|
+
const fromSrc = path.join(dir, 'src', 'core', 'code-map', 'lang', 'rust', basename);
|
|
40
|
+
if (fs.existsSync(fromSrc))
|
|
41
|
+
return fs.readFileSync(fromSrc, 'utf-8');
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
const parent = path.dirname(dir);
|
|
45
|
+
if (parent === dir)
|
|
46
|
+
break;
|
|
47
|
+
dir = parent;
|
|
48
|
+
}
|
|
49
|
+
throw new Error(`code-map: could not locate query asset ${basename} (from ${HERE})`);
|
|
50
|
+
}
|
|
51
|
+
function sha256(s) {
|
|
52
|
+
return `sha256:${crypto.createHash('sha256').update(s, 'utf-8').digest('hex')}`;
|
|
53
|
+
}
|
|
54
|
+
const TAGS = readScm('tags.scm');
|
|
55
|
+
const IMPORTS = readScm('imports.scm');
|
|
56
|
+
const TAGS_HASH = sha256(TAGS);
|
|
57
|
+
const IMPORTS_HASH = sha256(IMPORTS);
|
|
58
|
+
const parser = {
|
|
59
|
+
grammarForLang: () => loadGrammarWasm(RUST_WASM_BASENAME, RUST_WASM_NODE_MODULES_SPEC),
|
|
60
|
+
grammarNameForLang: () => RUST_GRAMMAR_NAME,
|
|
61
|
+
grammarHashForLang: () => grammarHashForWasm(RUST_WASM_BASENAME, RUST_WASM_NODE_MODULES_SPEC),
|
|
62
|
+
};
|
|
63
|
+
const queries = {
|
|
64
|
+
tags: { name: 'tags', sourceForLang: () => TAGS, hashForLang: () => TAGS_HASH },
|
|
65
|
+
imports: { name: 'imports', sourceForLang: () => IMPORTS, hashForLang: () => IMPORTS_HASH },
|
|
66
|
+
enclosingStatementNodeTypes: ['use_declaration'],
|
|
67
|
+
captureMap: [
|
|
68
|
+
{ capture: 'definition.function.node', field: 'node', subtype: 'function' },
|
|
69
|
+
{ capture: 'definition.function.name', field: 'name' },
|
|
70
|
+
{ capture: 'definition.class.node', field: 'node', subtype: 'class' },
|
|
71
|
+
{ capture: 'definition.class.name', field: 'name' },
|
|
72
|
+
{ capture: 'definition.enum.node', field: 'node', subtype: 'enum' },
|
|
73
|
+
{ capture: 'definition.enum.name', field: 'name' },
|
|
74
|
+
{ capture: 'definition.rust.trait.node', field: 'node', subtype: 'rust.trait' },
|
|
75
|
+
{ capture: 'definition.rust.trait.name', field: 'name' },
|
|
76
|
+
{ capture: 'definition.namespace.node', field: 'node', subtype: 'namespace' },
|
|
77
|
+
{ capture: 'definition.namespace.name', field: 'name' },
|
|
78
|
+
{ capture: 'definition.constant.node', field: 'node', subtype: 'constant' },
|
|
79
|
+
{ capture: 'definition.constant.name', field: 'name' },
|
|
80
|
+
{ capture: 'definition.type.node', field: 'node', subtype: 'type' },
|
|
81
|
+
{ capture: 'definition.type.name', field: 'name' },
|
|
82
|
+
{ capture: 'definition.macro.node', field: 'node', subtype: 'macro' },
|
|
83
|
+
{ capture: 'definition.macro.name', field: 'name' },
|
|
84
|
+
{ capture: 'import.source', field: 'source' },
|
|
85
|
+
{ capture: 'import.named.name', field: 'name' },
|
|
86
|
+
{ capture: 'import.namespace.name', field: 'name' },
|
|
87
|
+
],
|
|
88
|
+
};
|
|
89
|
+
const vocabulary = {
|
|
90
|
+
nodeSubtypes: ['function', 'class', 'enum', 'rust.trait', 'namespace', 'constant', 'type', 'macro'],
|
|
91
|
+
edgeKinds: ['contains', 'defines', 'imports'],
|
|
92
|
+
captureMap: queries.captureMap,
|
|
93
|
+
};
|
|
94
|
+
const capabilities = {
|
|
95
|
+
tiers: ['T1.definitions', 'T2.imports'],
|
|
96
|
+
proven: {
|
|
97
|
+
'T1.definitions': true,
|
|
98
|
+
'T2.imports': true,
|
|
99
|
+
'T3.import_resolution': false,
|
|
100
|
+
'T4.tests_for': false,
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
export class RustProvider {
|
|
104
|
+
id = 'rust';
|
|
105
|
+
displayName = 'Rust';
|
|
106
|
+
languages = ['rust'];
|
|
107
|
+
extensions = ['.rs'];
|
|
108
|
+
priority = 0;
|
|
109
|
+
version = '0.1.0';
|
|
110
|
+
parser = parser;
|
|
111
|
+
queries = queries;
|
|
112
|
+
vocabulary = vocabulary;
|
|
113
|
+
capabilities = capabilities;
|
|
114
|
+
langForPath(_p) {
|
|
115
|
+
return 'rust';
|
|
116
|
+
}
|
|
117
|
+
async extractDraft(input, _services) {
|
|
118
|
+
return extractWithQueries({
|
|
119
|
+
providerId: this.id,
|
|
120
|
+
lang: input.lang,
|
|
121
|
+
source: input.source,
|
|
122
|
+
sizeBytes: input.sizeBytes,
|
|
123
|
+
maxParseFileBytes: input.maxParseFileBytes,
|
|
124
|
+
maxQueryWaitMs: input.maxQueryWaitMs,
|
|
125
|
+
path: input.path,
|
|
126
|
+
grammarForLang: this.parser.grammarForLang,
|
|
127
|
+
tagsSource: TAGS,
|
|
128
|
+
tagsHash: TAGS_HASH,
|
|
129
|
+
importsSource: IMPORTS,
|
|
130
|
+
importsHash: IMPORTS_HASH,
|
|
131
|
+
enclosingStatementNodeTypes: queries.enclosingStatementNodeTypes,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
export const rustProvider = new RustProvider();
|
|
136
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
; Code Map — Rust definitions (tags.scm). Provider #6 (langs batch 2).
|
|
2
|
+
;
|
|
3
|
+
; Capture-name convention (query-runtime.ts is the contract):
|
|
4
|
+
; @definition.<subtype>.node = identity-span anchor (the whole declaration)
|
|
5
|
+
; @definition.<subtype>.name = the symbol name node
|
|
6
|
+
; Subtypes: function/enum/namespace/constant/type/macro are universal; a Rust
|
|
7
|
+
; struct maps to `class` (the named aggregate devs search for like a class), and a
|
|
8
|
+
; `trait` has no clean universal equal (it carries default methods + associated
|
|
9
|
+
; types/consts, unlike a plain interface) so it is NAMESPACED as `rust.trait`
|
|
10
|
+
; (guide §"when to namespace" cites this exact case).
|
|
11
|
+
;
|
|
12
|
+
; METHOD caveat (v1): Rust methods are `function_item` nodes nested in an
|
|
13
|
+
; `impl_item` (or `trait_item`) body — the SAME node type as a free `fn`. Tree-sitter
|
|
14
|
+
; queries can't express "function_item NOT inside an impl", so a single generic
|
|
15
|
+
; pattern maps ALL `function_item` -> `function` (no distinct `method` subtype in
|
|
16
|
+
; v1). Trait method *signatures* (`function_signature_item`, no body) are a distinct
|
|
17
|
+
; node type and are intentionally NOT captured in v1.
|
|
18
|
+
;
|
|
19
|
+
; const + static both map to `constant`; associated const/type inside impl/trait
|
|
20
|
+
; reuse the const_item/type_item patterns (captured as constant/type — acceptable).
|
|
21
|
+
|
|
22
|
+
(function_item
|
|
23
|
+
name: (identifier) @definition.function.name) @definition.function.node
|
|
24
|
+
|
|
25
|
+
(struct_item
|
|
26
|
+
name: (type_identifier) @definition.class.name) @definition.class.node
|
|
27
|
+
|
|
28
|
+
(enum_item
|
|
29
|
+
name: (type_identifier) @definition.enum.name) @definition.enum.node
|
|
30
|
+
|
|
31
|
+
(trait_item
|
|
32
|
+
name: (type_identifier) @definition.rust.trait.name) @definition.rust.trait.node
|
|
33
|
+
|
|
34
|
+
(mod_item
|
|
35
|
+
name: (identifier) @definition.namespace.name) @definition.namespace.node
|
|
36
|
+
|
|
37
|
+
(const_item
|
|
38
|
+
name: (identifier) @definition.constant.name) @definition.constant.node
|
|
39
|
+
|
|
40
|
+
(static_item
|
|
41
|
+
name: (identifier) @definition.constant.name) @definition.constant.node
|
|
42
|
+
|
|
43
|
+
(type_item
|
|
44
|
+
name: (type_identifier) @definition.type.name) @definition.type.node
|
|
45
|
+
|
|
46
|
+
(macro_definition
|
|
47
|
+
name: (identifier) @definition.macro.name) @definition.macro.node
|