brainclaw 1.9.1 → 1.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/README.md +47 -1
  2. package/dist/brainclaw-vscode.vsix +0 -0
  3. package/dist/cli.js +18 -1
  4. package/dist/commands/code-map.js +129 -0
  5. package/dist/commands/codev.js +7 -0
  6. package/dist/commands/mcp.js +121 -0
  7. package/dist/commands/run-profile.js +3 -2
  8. package/dist/commands/switch.js +100 -89
  9. package/dist/core/agent-files.js +12 -0
  10. package/dist/core/code-map/backend.js +123 -0
  11. package/dist/core/code-map/core.js +81 -0
  12. package/dist/core/code-map/drafts.js +2 -0
  13. package/dist/core/code-map/extractor.js +29 -0
  14. package/dist/core/code-map/finalizer.js +191 -0
  15. package/dist/core/code-map/freshness.js +108 -0
  16. package/dist/core/code-map/ids.js +0 -0
  17. package/dist/core/code-map/importable.js +35 -0
  18. package/dist/core/code-map/indexes.js +197 -0
  19. package/dist/core/code-map/lang/java/imports.scm +17 -0
  20. package/dist/core/code-map/lang/java/index.js +254 -0
  21. package/dist/core/code-map/lang/java/tags.scm +48 -0
  22. package/dist/core/code-map/lang/php/imports.scm +21 -0
  23. package/dist/core/code-map/lang/php/index.js +251 -0
  24. package/dist/core/code-map/lang/php/tags.scm +44 -0
  25. package/dist/core/code-map/lang/provider.js +9 -0
  26. package/dist/core/code-map/lang/providers.js +24 -0
  27. package/dist/core/code-map/lang/python/imports.scm +90 -0
  28. package/dist/core/code-map/lang/python/index.js +364 -0
  29. package/dist/core/code-map/lang/python/tags.scm +81 -0
  30. package/dist/core/code-map/lang/query-runtime.js +374 -0
  31. package/dist/core/code-map/lang/registry.js +125 -0
  32. package/dist/core/code-map/lang/typescript/imports.scm +90 -0
  33. package/dist/core/code-map/lang/typescript/index.js +306 -0
  34. package/dist/core/code-map/lang/typescript/tags.js.scm +106 -0
  35. package/dist/core/code-map/lang/typescript/tags.scm +151 -0
  36. package/dist/core/code-map/lock.js +210 -0
  37. package/dist/core/code-map/materialized.js +51 -0
  38. package/dist/core/code-map/memory-reader.js +59 -0
  39. package/dist/core/code-map/paths.js +53 -0
  40. package/dist/core/code-map/query.js +568 -0
  41. package/dist/core/code-map/refresh.js +0 -0
  42. package/dist/core/code-map/resolve.js +177 -0
  43. package/dist/core/code-map/store.js +206 -0
  44. package/dist/core/code-map/types.js +288 -0
  45. package/dist/core/code-map/vocabulary.js +57 -0
  46. package/dist/core/code-map/wasm-loader.js +294 -0
  47. package/dist/core/code-map/work-section.js +206 -0
  48. package/dist/core/codev-rounds.js +4 -0
  49. package/dist/core/execution-adapters.js +11 -10
  50. package/dist/core/execution-profile.js +58 -0
  51. package/dist/core/facade-schema.js +9 -0
  52. package/dist/core/instruction-templates.js +2 -0
  53. package/dist/core/mcp-command-resolution.js +3 -1
  54. package/dist/core/store-resolution.js +41 -4
  55. package/dist/facts.js +9 -5
  56. package/dist/facts.json +8 -4
  57. package/dist/vendor/web-tree-sitter/tree-sitter.js +3980 -0
  58. package/dist/vendor/web-tree-sitter/tree-sitter.wasm +0 -0
  59. package/dist/wasm/tree-sitter-java.wasm +0 -0
  60. package/dist/wasm/tree-sitter-javascript.wasm +0 -0
  61. package/dist/wasm/tree-sitter-php.wasm +0 -0
  62. package/dist/wasm/tree-sitter-python.wasm +0 -0
  63. package/dist/wasm/tree-sitter-tsx.wasm +0 -0
  64. package/dist/wasm/tree-sitter-typescript.wasm +0 -0
  65. package/dist/wasm/tree-sitter.wasm +0 -0
  66. package/docs/cli.md +46 -8
  67. package/docs/code-map.md +198 -0
  68. package/docs/integrations/mcp.md +13 -6
  69. package/docs/mcp-schema-changelog.md +7 -3
  70. package/docs/quickstart.md +1 -1
  71. package/package.json +11 -6
@@ -0,0 +1,254 @@
1
+ /**
2
+ * Code Map langs#3-4 — JavaProvider (provider #4; cadrage v2 §6, dec#113).
3
+ *
4
+ * Owns `.java` (runtime lang `java`). `extractDraft` delegates to the generic
5
+ * query-runtime; the curated `tags.scm`/`imports.scm` (this dir) drive structural
6
+ * extraction. All definition subtypes are fixed by the query (no def
7
+ * reclassification needed). `refine()` carries ONLY the import shaping the
8
+ * structural query cannot express without overlapping matches (Codex R1):
9
+ * - wildcard `import a.b.*;` → imported name `*` (the package source `a.b` is
10
+ * already captured; the grammar puts `*` in a sibling `asterisk` node).
11
+ * - static `import static a.b.C.m;` → split the declaring type `a.b.C` (module
12
+ * source) from the member `m` (imported name); `import static a.b.C.*;` →
13
+ * source `a.b.C` + name `*`.
14
+ * - plain `import a.b.C;` → source `a.b.C`, no imported name.
15
+ *
16
+ * NO exports edges — Java has no export statement (capabilities: T2 = imports).
17
+ * Nested/inner classes + their members are emitted by the same class/method
18
+ * patterns; the finalizer emits only file-level contains/defines (no nesting edges).
19
+ *
20
+ * Identity is owned by the CORE finalizer — this provider mints NO ids. The grammar
21
+ * is loaded through the SHARED engine glue (`loadGrammarWasm`), NEVER a fresh
22
+ * `web-tree-sitter` import (trp_8df65ab7).
23
+ */
24
+ import crypto from 'node:crypto';
25
+ import fs from 'node:fs';
26
+ import path from 'node:path';
27
+ import { fileURLToPath } from 'node:url';
28
+ import { loadGrammarWasm, grammarHashForWasm } from '../../wasm-loader.js';
29
+ import { extractWithQueries } from '../query-runtime.js';
30
+ const HERE = path.dirname(fileURLToPath(import.meta.url));
31
+ /** The java grammar .wasm: dist basename + node_modules devDep fallback spec. */
32
+ const JAVA_WASM_BASENAME = 'tree-sitter-java.wasm';
33
+ const JAVA_WASM_NODE_MODULES_SPEC = 'tree-sitter-wasms/out/tree-sitter-java.wasm';
34
+ const JAVA_GRAMMAR_NAME = 'tree-sitter-java';
35
+ /** Resolve a vendored `.scm` next to this module (dist) or from the source tree. */
36
+ function readScm(basename) {
37
+ // Published / dist runtime: this module is dist/core/code-map/lang/java/index.js
38
+ // and the build copies the .scm assets alongside it (copy-code-map-wasm.mjs).
39
+ const local = path.join(HERE, basename);
40
+ if (fs.existsSync(local))
41
+ return fs.readFileSync(local, 'utf-8');
42
+ // From-source / dist-test fallback: tsc emits to dist[-test]/... but does NOT copy
43
+ // .scm, so walk up to the repo root (the dir holding package.json) and read the
44
+ // curated asset from src/core/code-map/lang/java/.
45
+ let dir = HERE;
46
+ for (let i = 0; i < 12; i++) {
47
+ if (fs.existsSync(path.join(dir, 'package.json'))) {
48
+ const fromSrc = path.join(dir, 'src', 'core', 'code-map', 'lang', 'java', basename);
49
+ if (fs.existsSync(fromSrc))
50
+ return fs.readFileSync(fromSrc, 'utf-8');
51
+ break;
52
+ }
53
+ const parent = path.dirname(dir);
54
+ if (parent === dir)
55
+ break;
56
+ dir = parent;
57
+ }
58
+ throw new Error(`code-map: could not locate query asset ${basename} (from ${HERE})`);
59
+ }
60
+ function sha256(s) {
61
+ return `sha256:${crypto.createHash('sha256').update(s, 'utf-8').digest('hex')}`;
62
+ }
63
+ // Load the curated query assets once at module init.
64
+ const TAGS = readScm('tags.scm');
65
+ const IMPORTS = readScm('imports.scm');
66
+ const TAGS_HASH = sha256(TAGS);
67
+ const IMPORTS_HASH = sha256(IMPORTS);
68
+ const parser = {
69
+ grammarForLang: () => loadGrammarWasm(JAVA_WASM_BASENAME, JAVA_WASM_NODE_MODULES_SPEC),
70
+ grammarNameForLang: () => JAVA_GRAMMAR_NAME,
71
+ grammarHashForLang: () => grammarHashForWasm(JAVA_WASM_BASENAME, JAVA_WASM_NODE_MODULES_SPEC),
72
+ };
73
+ const queries = {
74
+ tags: {
75
+ name: 'tags',
76
+ sourceForLang: () => TAGS,
77
+ hashForLang: () => TAGS_HASH,
78
+ },
79
+ imports: {
80
+ name: 'imports',
81
+ sourceForLang: () => IMPORTS,
82
+ hashForLang: () => IMPORTS_HASH,
83
+ },
84
+ // Java import declarations are the import statement; Java has no export statement.
85
+ enclosingStatementNodeTypes: ['import_declaration'],
86
+ // P1b §3.4 / cadrage: every capture mirrors the hard-coded runtime convention and
87
+ // is validated by `assertCaptureMapConforms`. The namespaced java.annotation /
88
+ // java.record are still definition.<subtype>.* captures.
89
+ captureMap: [
90
+ { capture: 'definition.package.node', field: 'node', subtype: 'package' },
91
+ { capture: 'definition.package.name', field: 'name' },
92
+ { capture: 'definition.class.node', field: 'node', subtype: 'class' },
93
+ { capture: 'definition.class.name', field: 'name' },
94
+ { capture: 'definition.interface.node', field: 'node', subtype: 'interface' },
95
+ { capture: 'definition.interface.name', field: 'name' },
96
+ { capture: 'definition.enum.node', field: 'node', subtype: 'enum' },
97
+ { capture: 'definition.enum.name', field: 'name' },
98
+ { capture: 'definition.java.annotation.node', field: 'node', subtype: 'java.annotation' },
99
+ { capture: 'definition.java.annotation.name', field: 'name' },
100
+ { capture: 'definition.java.record.node', field: 'node', subtype: 'java.record' },
101
+ { capture: 'definition.java.record.name', field: 'name' },
102
+ { capture: 'definition.method.node', field: 'node', subtype: 'method' },
103
+ { capture: 'definition.method.name', field: 'name' },
104
+ { capture: 'definition.constructor.node', field: 'node', subtype: 'constructor' },
105
+ { capture: 'definition.constructor.name', field: 'name' },
106
+ { capture: 'definition.field.node', field: 'node', subtype: 'field' },
107
+ { capture: 'definition.field.name', field: 'name' },
108
+ { capture: 'definition.constant.node', field: 'node', subtype: 'constant' },
109
+ { capture: 'definition.constant.name', field: 'name' },
110
+ { capture: 'import.source', field: 'source' },
111
+ ],
112
+ };
113
+ const vocabulary = {
114
+ nodeSubtypes: [
115
+ 'package',
116
+ 'class',
117
+ 'interface',
118
+ 'enum',
119
+ 'java.annotation',
120
+ 'java.record',
121
+ 'method',
122
+ 'constructor',
123
+ 'field',
124
+ 'constant',
125
+ ],
126
+ edgeKinds: ['contains', 'defines', 'imports'],
127
+ captureMap: queries.captureMap,
128
+ };
129
+ const capabilities = {
130
+ tiers: ['T1.definitions', 'T2.imports'],
131
+ proven: {
132
+ 'T1.definitions': true,
133
+ 'T2.imports': true,
134
+ 'T3.import_resolution': false,
135
+ 'T4.tests_for': false,
136
+ },
137
+ };
138
+ /** A span key matching the runtime's `spanOf` (1-based start/end line+col). */
139
+ function spanKey(span) {
140
+ return `${span.start_line}:${span.start_col}:${span.end_line}:${span.end_col}`;
141
+ }
142
+ function spanKeyOfNode(node) {
143
+ return `${node.startPosition.row + 1}:${node.startPosition.column + 1}:${node.endPosition.row + 1}:${node.endPosition.column + 1}`;
144
+ }
145
+ /** Iterative named-node DFS collecting nodes whose type is in `types`. */
146
+ function collectByType(root, types) {
147
+ const found = [];
148
+ const stack = [root];
149
+ while (stack.length > 0) {
150
+ const n = stack.pop();
151
+ if (types.has(n.type))
152
+ found.push(n);
153
+ for (let i = 0; i < n.namedChildCount; i++) {
154
+ const c = n.namedChild(i);
155
+ if (c)
156
+ stack.push(c);
157
+ }
158
+ }
159
+ return found;
160
+ }
161
+ const IMPORT_DECL = new Set(['import_declaration']);
162
+ /** Map each import_declaration's statement span → its static/wildcard shape. */
163
+ function importShapesBySpan(tree) {
164
+ const out = new Map();
165
+ for (const decl of collectByType(tree.rootNode, IMPORT_DECL)) {
166
+ let isStatic = false;
167
+ let isWildcard = false;
168
+ // `static` is an anonymous keyword token; `asterisk` is a named node. Iterate
169
+ // ALL children (named + anonymous) to detect both.
170
+ for (let i = 0; i < decl.childCount; i++) {
171
+ const c = decl.child(i);
172
+ if (!c)
173
+ continue;
174
+ if (c.type === 'static')
175
+ isStatic = true;
176
+ else if (c.type === 'asterisk')
177
+ isWildcard = true;
178
+ }
179
+ out.set(spanKeyOfNode(decl), { isStatic, isWildcard });
180
+ }
181
+ return out;
182
+ }
183
+ export class JavaProvider {
184
+ id = 'java';
185
+ displayName = 'Java';
186
+ languages = ['java'];
187
+ extensions = ['.java'];
188
+ priority = 0;
189
+ version = '0.1.0';
190
+ parser = parser;
191
+ queries = queries;
192
+ vocabulary = vocabulary;
193
+ capabilities = capabilities;
194
+ /** `.java` → `java`. */
195
+ langForPath(_p) {
196
+ return 'java';
197
+ }
198
+ async extractDraft(input, _services) {
199
+ return extractWithQueries({
200
+ providerId: this.id,
201
+ lang: input.lang,
202
+ source: input.source,
203
+ sizeBytes: input.sizeBytes,
204
+ maxParseFileBytes: input.maxParseFileBytes,
205
+ maxQueryWaitMs: input.maxQueryWaitMs,
206
+ path: input.path,
207
+ grammarForLang: this.parser.grammarForLang,
208
+ tagsSource: TAGS,
209
+ tagsHash: TAGS_HASH,
210
+ importsSource: IMPORTS,
211
+ importsHash: IMPORTS_HASH,
212
+ enclosingStatementNodeTypes: queries.enclosingStatementNodeTypes,
213
+ });
214
+ }
215
+ /**
216
+ * Drafts-only refinement (cadrage v2 §6): shape import source / imported-names
217
+ * per the static-split and wildcard rules. Definitions need no reclassification.
218
+ */
219
+ refine(draft, _ctx) {
220
+ const tree = draft.attributes?.__tree;
221
+ if (!tree || draft.imports.length === 0)
222
+ return draft;
223
+ const shapes = importShapesBySpan(tree);
224
+ if (shapes.size === 0)
225
+ return draft;
226
+ const imports = draft.imports.map((im) => {
227
+ const shape = shapes.get(spanKey(im.span));
228
+ if (!shape)
229
+ return im;
230
+ if (shape.isWildcard) {
231
+ // `import a.b.*` / `import static a.b.C.*` → source is the captured path; add '*'.
232
+ return { ...im, importedNames: ['*'] };
233
+ }
234
+ if (shape.isStatic) {
235
+ // `import static a.b.C.m` → split declaring type (module) vs member (name).
236
+ const dot = im.source.lastIndexOf('.');
237
+ if (dot > 0) {
238
+ return {
239
+ ...im,
240
+ source: im.source.slice(0, dot),
241
+ importedNames: [im.source.slice(dot + 1)],
242
+ };
243
+ }
244
+ return im;
245
+ }
246
+ // Plain `import a.b.C` → module a.b.C, no imported name.
247
+ return im;
248
+ });
249
+ return { ...draft, imports };
250
+ }
251
+ }
252
+ /** Singleton instance for registry wiring. */
253
+ export const javaProvider = new JavaProvider();
254
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,48 @@
1
+ ; Code Map — Java definitions (tags.scm). Provider #4 (langs#3-4).
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: package/class/interface/enum/method/constructor/field/constant are
7
+ ; universal; annotation types → java.annotation and records → java.record are
8
+ ; namespaced (they are class-like at the JVM level but users reason about them as
9
+ ; distinct kinds — Codex R1). Nested/inner classes are captured by the same
10
+ ; class_declaration pattern (the finalizer emits file-level contains/defines only;
11
+ ; no fabricated nesting edges). enum constants → constant; constructors are a
12
+ ; distinct grammar node (constructor_declaration → constructor, not method).
13
+
14
+ (package_declaration
15
+ [(scoped_identifier) (identifier)] @definition.package.name) @definition.package.node
16
+
17
+ (class_declaration
18
+ name: (identifier) @definition.class.name) @definition.class.node
19
+
20
+ (interface_declaration
21
+ name: (identifier) @definition.interface.name) @definition.interface.node
22
+
23
+ (enum_declaration
24
+ name: (identifier) @definition.enum.name) @definition.enum.node
25
+
26
+ (enum_constant
27
+ name: (identifier) @definition.constant.name) @definition.constant.node
28
+
29
+ (annotation_type_declaration
30
+ name: (identifier) @definition.java.annotation.name) @definition.java.annotation.node
31
+
32
+ (record_declaration
33
+ name: (identifier) @definition.java.record.name) @definition.java.record.node
34
+
35
+ (method_declaration
36
+ name: (identifier) @definition.method.name) @definition.method.node
37
+
38
+ (constructor_declaration
39
+ name: (identifier) @definition.constructor.name) @definition.constructor.node
40
+
41
+ ; `int a, b;` yields one match per variable_declarator (all sharing the
42
+ ; field_declaration identity span) — mirrors the const/property multi-declarator rule.
43
+ (field_declaration
44
+ declarator: (variable_declarator name: (identifier) @definition.field.name)) @definition.field.node
45
+
46
+ ; Annotation member declarations are method-shaped (`String value() default ""`).
47
+ (annotation_type_element_declaration
48
+ name: (identifier) @definition.method.name) @definition.method.node
@@ -0,0 +1,21 @@
1
+ ; Code Map — PHP imports (imports.scm). Provider #3 (langs#3-4).
2
+ ;
3
+ ; enclosingStatementNodeTypes = [namespace_use_declaration] (provider declaration);
4
+ ; that statement is the import span/ordinal anchor. PHP `use` brings a namespaced
5
+ ; symbol/module into scope; the source-side path is the module specifier.
6
+ ;
7
+ ; - simple / comma / `use function` / `use const`: each clause's qualified_name
8
+ ; (or bare name) is the full source path → captured directly as @import.source.
9
+ ; Aliases (`use A\B as C`) are dropped (we capture the path, not the alias) —
10
+ ; Codex R1.
11
+ ; - GROUP use (`use A\{B, C as Bee}`): no single tree node carries the full
12
+ ; `A\B` (the prefix `A` and the leaf `B` are separate nodes — Codex R1). We
13
+ ; capture each group clause's leaf name here; the provider's refine() prepends
14
+ ; the group prefix to synthesize the full source path.
15
+ ; - include/require are OUT of scope (dynamic/runtime, not static specifiers).
16
+
17
+ (namespace_use_clause [(qualified_name) (name)] @import.source)
18
+
19
+ ; Group clauses only ever carry a `namespace_name` (which itself spans
20
+ ; backslash-separated paths, e.g. `B\C`); `qualified_name` is not a valid child here.
21
+ (namespace_use_group_clause (namespace_name) @import.source)
@@ -0,0 +1,251 @@
1
+ /**
2
+ * Code Map langs#3-4 — PhpProvider (provider #3; cadrage v2 §5, dec#113).
3
+ *
4
+ * Owns `.php` (runtime lang `php`). `extractDraft` delegates to the generic
5
+ * query-runtime; the curated `tags.scm`/`imports.scm` (this dir) drive structural
6
+ * extraction. `refine()` carries what the tree-sitter queries CANNOT express
7
+ * (all provider-local, drafts-only):
8
+ * - `__construct` (grammar node `method_declaration`) → subtype `constructor`
9
+ * (Codex R1: PHP has a language-level constructor; collapse into `method` would
10
+ * lose that distinction). Other methods/magic methods stay `method`.
11
+ * - property names: the grammar's `variable_name` node text includes the leading
12
+ * `$` (`$id`); the symbol name is `id` (how the property is referenced) → strip.
13
+ * - GROUP use (`use A\{B, C as Bee}`): the query captures each clause's leaf name
14
+ * (`B`, `C`) because NO single tree node carries the full `A\B` (Codex R1). We
15
+ * walk the retained tree, find each group-use statement's prefix (`A`), and
16
+ * prepend it to the leaf-name import drafts at that statement's span → full
17
+ * source paths `A\B`, `A\C` (aliases already dropped by the query).
18
+ *
19
+ * NO exports edges — PHP `use` is modeled as module imports; there is no export
20
+ * statement (capabilities: T2 = imports).
21
+ *
22
+ * Identity is owned by the CORE finalizer — this provider mints NO ids. The grammar
23
+ * is loaded through the SHARED engine glue (`loadGrammarWasm`), NEVER a fresh
24
+ * `web-tree-sitter` import (trp_8df65ab7).
25
+ */
26
+ import crypto from 'node:crypto';
27
+ import fs from 'node:fs';
28
+ import path from 'node:path';
29
+ import { fileURLToPath } from 'node:url';
30
+ import { loadGrammarWasm, grammarHashForWasm } from '../../wasm-loader.js';
31
+ import { extractWithQueries } from '../query-runtime.js';
32
+ const HERE = path.dirname(fileURLToPath(import.meta.url));
33
+ /** The php grammar .wasm: dist basename + node_modules devDep fallback spec. */
34
+ const PHP_WASM_BASENAME = 'tree-sitter-php.wasm';
35
+ const PHP_WASM_NODE_MODULES_SPEC = 'tree-sitter-wasms/out/tree-sitter-php.wasm';
36
+ const PHP_GRAMMAR_NAME = 'tree-sitter-php';
37
+ /** Resolve a vendored `.scm` next to this module (dist) or from the source tree. */
38
+ function readScm(basename) {
39
+ // Published / dist runtime: this module is dist/core/code-map/lang/php/index.js
40
+ // and the build copies the .scm assets alongside it (copy-code-map-wasm.mjs).
41
+ const local = path.join(HERE, basename);
42
+ if (fs.existsSync(local))
43
+ return fs.readFileSync(local, 'utf-8');
44
+ // From-source / dist-test fallback: tsc emits to dist[-test]/... but does NOT copy
45
+ // .scm, so walk up to the repo root (the dir holding package.json) and read the
46
+ // curated asset from src/core/code-map/lang/php/.
47
+ let dir = HERE;
48
+ for (let i = 0; i < 12; i++) {
49
+ if (fs.existsSync(path.join(dir, 'package.json'))) {
50
+ const fromSrc = path.join(dir, 'src', 'core', 'code-map', 'lang', 'php', basename);
51
+ if (fs.existsSync(fromSrc))
52
+ return fs.readFileSync(fromSrc, 'utf-8');
53
+ break;
54
+ }
55
+ const parent = path.dirname(dir);
56
+ if (parent === dir)
57
+ break;
58
+ dir = parent;
59
+ }
60
+ throw new Error(`code-map: could not locate query asset ${basename} (from ${HERE})`);
61
+ }
62
+ function sha256(s) {
63
+ return `sha256:${crypto.createHash('sha256').update(s, 'utf-8').digest('hex')}`;
64
+ }
65
+ // Load the curated query assets once at module init.
66
+ const TAGS = readScm('tags.scm');
67
+ const IMPORTS = readScm('imports.scm');
68
+ const TAGS_HASH = sha256(TAGS);
69
+ const IMPORTS_HASH = sha256(IMPORTS);
70
+ const parser = {
71
+ grammarForLang: () => loadGrammarWasm(PHP_WASM_BASENAME, PHP_WASM_NODE_MODULES_SPEC),
72
+ grammarNameForLang: () => PHP_GRAMMAR_NAME,
73
+ grammarHashForLang: () => grammarHashForWasm(PHP_WASM_BASENAME, PHP_WASM_NODE_MODULES_SPEC),
74
+ };
75
+ const queries = {
76
+ tags: {
77
+ name: 'tags',
78
+ sourceForLang: () => TAGS,
79
+ hashForLang: () => TAGS_HASH,
80
+ },
81
+ imports: {
82
+ name: 'imports',
83
+ sourceForLang: () => IMPORTS,
84
+ hashForLang: () => IMPORTS_HASH,
85
+ },
86
+ // PHP `use` declarations are the import statement; PHP has no export statement.
87
+ enclosingStatementNodeTypes: ['namespace_use_declaration'],
88
+ // P1b §3.4 / cadrage: every capture mirrors the hard-coded runtime convention and
89
+ // is validated by `assertCaptureMapConforms`. PHP invents none beyond the
90
+ // namespaced `php.trait` subtype (still a definition.<subtype>.* capture).
91
+ captureMap: [
92
+ { capture: 'definition.namespace.node', field: 'node', subtype: 'namespace' },
93
+ { capture: 'definition.namespace.name', field: 'name' },
94
+ { capture: 'definition.class.node', field: 'node', subtype: 'class' },
95
+ { capture: 'definition.class.name', field: 'name' },
96
+ { capture: 'definition.interface.node', field: 'node', subtype: 'interface' },
97
+ { capture: 'definition.interface.name', field: 'name' },
98
+ { capture: 'definition.php.trait.node', field: 'node', subtype: 'php.trait' },
99
+ { capture: 'definition.php.trait.name', field: 'name' },
100
+ { capture: 'definition.enum.node', field: 'node', subtype: 'enum' },
101
+ { capture: 'definition.enum.name', field: 'name' },
102
+ { capture: 'definition.function.node', field: 'node', subtype: 'function' },
103
+ { capture: 'definition.function.name', field: 'name' },
104
+ { capture: 'definition.method.node', field: 'node', subtype: 'method' },
105
+ { capture: 'definition.method.name', field: 'name' },
106
+ { capture: 'definition.constant.node', field: 'node', subtype: 'constant' },
107
+ { capture: 'definition.constant.name', field: 'name' },
108
+ { capture: 'definition.property.node', field: 'node', subtype: 'property' },
109
+ { capture: 'definition.property.name', field: 'name' },
110
+ { capture: 'import.source', field: 'source' },
111
+ ],
112
+ };
113
+ const vocabulary = {
114
+ nodeSubtypes: [
115
+ 'namespace',
116
+ 'class',
117
+ 'interface',
118
+ 'php.trait',
119
+ 'enum',
120
+ 'function',
121
+ 'method',
122
+ 'constructor',
123
+ 'constant',
124
+ 'property',
125
+ ],
126
+ edgeKinds: ['contains', 'defines', 'imports'],
127
+ captureMap: queries.captureMap,
128
+ };
129
+ const capabilities = {
130
+ tiers: ['T1.definitions', 'T2.imports'],
131
+ proven: {
132
+ 'T1.definitions': true,
133
+ 'T2.imports': true,
134
+ 'T3.import_resolution': false,
135
+ 'T4.tests_for': false,
136
+ },
137
+ };
138
+ /** Build a span key matching the runtime's `spanOf` (1-based start/end line+col). */
139
+ function spanKeyOfNode(node) {
140
+ return `${node.startPosition.row + 1}:${node.startPosition.column + 1}:${node.endPosition.row + 1}:${node.endPosition.column + 1}`;
141
+ }
142
+ /** Iterative named-node DFS collecting nodes whose type is in `types`. */
143
+ function collectByType(root, types) {
144
+ const found = [];
145
+ const stack = [root];
146
+ while (stack.length > 0) {
147
+ const n = stack.pop();
148
+ if (types.has(n.type))
149
+ found.push(n);
150
+ for (let i = 0; i < n.namedChildCount; i++) {
151
+ const c = n.namedChild(i);
152
+ if (c)
153
+ stack.push(c);
154
+ }
155
+ }
156
+ return found;
157
+ }
158
+ const USE_DECL = new Set(['namespace_use_declaration']);
159
+ /**
160
+ * For each GROUP use statement (a `namespace_use_declaration` that contains a
161
+ * `namespace_use_group`), map its statement span → the group PREFIX (the direct
162
+ * `namespace_name` child, e.g. `App\Util`). The runtime emitted one import draft
163
+ * per group leaf (sources are the bare leaf names) at that statement's span;
164
+ * refine prepends `prefix\` to each.
165
+ */
166
+ function groupPrefixBySpan(tree) {
167
+ const out = new Map();
168
+ for (const decl of collectByType(tree.rootNode, USE_DECL)) {
169
+ let group = null;
170
+ let prefix = null;
171
+ for (let i = 0; i < decl.namedChildCount; i++) {
172
+ const c = decl.namedChild(i);
173
+ if (!c)
174
+ continue;
175
+ if (c.type === 'namespace_use_group')
176
+ group = c;
177
+ else if (c.type === 'namespace_name' && !prefix)
178
+ prefix = c;
179
+ }
180
+ if (group && prefix)
181
+ out.set(spanKeyOfNode(decl), prefix.text);
182
+ }
183
+ return out;
184
+ }
185
+ export class PhpProvider {
186
+ id = 'php';
187
+ displayName = 'PHP';
188
+ languages = ['php'];
189
+ extensions = ['.php'];
190
+ priority = 0;
191
+ version = '0.1.0';
192
+ parser = parser;
193
+ queries = queries;
194
+ vocabulary = vocabulary;
195
+ capabilities = capabilities;
196
+ /** `.php` → `php`. */
197
+ langForPath(_p) {
198
+ return 'php';
199
+ }
200
+ async extractDraft(input, _services) {
201
+ return extractWithQueries({
202
+ providerId: this.id,
203
+ lang: input.lang,
204
+ source: input.source,
205
+ sizeBytes: input.sizeBytes,
206
+ maxParseFileBytes: input.maxParseFileBytes,
207
+ maxQueryWaitMs: input.maxQueryWaitMs,
208
+ path: input.path,
209
+ grammarForLang: this.parser.grammarForLang,
210
+ tagsSource: TAGS,
211
+ tagsHash: TAGS_HASH,
212
+ importsSource: IMPORTS,
213
+ importsHash: IMPORTS_HASH,
214
+ enclosingStatementNodeTypes: queries.enclosingStatementNodeTypes,
215
+ });
216
+ }
217
+ /**
218
+ * Drafts-only refinement (cadrage v2 §5):
219
+ * - definitions: `__construct` method → constructor; property name `$x` → `x`.
220
+ * - imports: synthesize full group-use source paths by prepending the prefix.
221
+ */
222
+ refine(draft, _ctx) {
223
+ const definitions = draft.definitions.map((d) => {
224
+ if (d.subtype === 'method' && d.name === '__construct') {
225
+ return { ...d, subtype: 'constructor' };
226
+ }
227
+ if (d.subtype === 'property' && d.name.startsWith('$')) {
228
+ return { ...d, name: d.name.slice(1) };
229
+ }
230
+ return d;
231
+ });
232
+ let imports = draft.imports;
233
+ const tree = draft.attributes?.__tree;
234
+ if (tree) {
235
+ const prefixBySpan = groupPrefixBySpan(tree);
236
+ if (prefixBySpan.size > 0) {
237
+ imports = draft.imports.map((im) => {
238
+ const key = `${im.span.start_line}:${im.span.start_col}:${im.span.end_line}:${im.span.end_col}`;
239
+ const prefix = prefixBySpan.get(key);
240
+ // A group statement only ever produced group-leaf imports at its span;
241
+ // a comma/simple `use` is a different statement (different span).
242
+ return prefix ? { ...im, source: `${prefix}\\${im.source}` } : im;
243
+ });
244
+ }
245
+ }
246
+ return { ...draft, definitions, imports };
247
+ }
248
+ }
249
+ /** Singleton instance for registry wiring. */
250
+ export const phpProvider = new PhpProvider();
251
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,44 @@
1
+ ; Code Map — PHP definitions (tags.scm). Provider #3 (langs#3-4).
2
+ ;
3
+ ; Capture-name convention (query-runtime.ts is the contract):
4
+ ; @definition.<subtype>.node = the identity-span anchor (the whole declaration)
5
+ ; @definition.<subtype>.name = the symbol name node
6
+ ; Subtypes: namespace/class/interface/enum/function/method/constant/property are
7
+ ; universal; php.trait is namespaced (a trait is NOT substitutable for an
8
+ ; interface). __construct is captured as `method` here and reclassified to
9
+ ; `constructor` in the provider's refine() (the grammar node is method_declaration).
10
+ ; enum cases and class consts both map to `constant`.
11
+
12
+ (namespace_definition
13
+ name: (namespace_name) @definition.namespace.name) @definition.namespace.node
14
+
15
+ (class_declaration
16
+ name: (name) @definition.class.name) @definition.class.node
17
+
18
+ (interface_declaration
19
+ name: (name) @definition.interface.name) @definition.interface.node
20
+
21
+ (trait_declaration
22
+ name: (name) @definition.php.trait.name) @definition.php.trait.node
23
+
24
+ (enum_declaration
25
+ name: (name) @definition.enum.name) @definition.enum.node
26
+
27
+ (enum_case
28
+ name: (name) @definition.constant.name) @definition.constant.node
29
+
30
+ (function_definition
31
+ name: (name) @definition.function.name) @definition.function.node
32
+
33
+ (method_declaration
34
+ name: (name) @definition.method.name) @definition.method.node
35
+
36
+ ; `const A = 1, B = 2;` yields one match per const_element (all sharing the
37
+ ; const_declaration identity span) — mirrors the JS/TS `const a,b` shared-span rule.
38
+ (const_declaration
39
+ (const_element (name) @definition.constant.name)) @definition.constant.node
40
+
41
+ ; `public int $a, $b;` yields one match per property_element. The name node is the
42
+ ; `variable_name` (text "$a"); refine() strips the leading `$` for the symbol name.
43
+ (property_declaration
44
+ (property_element (variable_name) @definition.property.name)) @definition.property.node
@@ -0,0 +1,9 @@
1
+ /**
2
+ * The default {@link CodeLanguageProvider.isImportableSymbol}: a real, externally
3
+ * importable definition. Excludes synthetic `subtype:'export'` placeholders (TS
4
+ * export-clause nodes) so B never binds to a non-definition (Codex cadrage review).
5
+ */
6
+ export function defaultImportableSymbol(node) {
7
+ return node.kind === 'symbol' && node.exported === true && node.subtype !== 'export';
8
+ }
9
+ //# sourceMappingURL=provider.js.map
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Code Map — default provider REGISTRATION list (P1b §3.2).
3
+ *
4
+ * This is the single declared EXTENSION POINT for "which language providers ship
5
+ * by default". Adding a language = importing its provider here and adding it to
6
+ * the `createRegistry(...)` call below — NOT editing `core.ts` (the orchestration)
7
+ * or the registry mechanics. `core.ts` imports the constructed `defaultRegistry`
8
+ * from this module.
9
+ *
10
+ * Registration order is the secondary collision tiebreak (after `priority`); keep
11
+ * it intentional. Pure construction — no behavior change vs the P1a inline
12
+ * `createRegistry(typeScriptProvider)` that previously lived in `core.ts`.
13
+ */
14
+ import { createRegistry } from './registry.js';
15
+ import { typeScriptProvider } from './typescript/index.js';
16
+ import { pythonProvider } from './python/index.js';
17
+ import { phpProvider } from './php/index.js';
18
+ import { javaProvider } from './java/index.js';
19
+ /**
20
+ * The default registry, pre-loaded with the bundled providers. Add a new
21
+ * provider's singleton to this `createRegistry(...)` call to register it.
22
+ */
23
+ export const defaultRegistry = createRegistry(typeScriptProvider, pythonProvider, phpProvider, javaProvider);
24
+ //# sourceMappingURL=providers.js.map