dsh-plugin-codegraph-tree-sitter 0.1.6 → 0.1.7

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/lib/index.d.ts CHANGED
@@ -25,6 +25,8 @@ export { loadGitignore, matchesGitignore, parseGitignore } from './gitignore.ts'
25
25
  export type { GitignoreRule } from './gitignore.ts';
26
26
  export { resolveWorkspace } from './resolve.ts';
27
27
  export type { ExtractedFile, GraphEdge, GraphNode, ResolvedGraph, UnresolvedRef } from './resolve.ts';
28
+ export { isMinifiedName, looksMinified, measureText, MINIFIED_AVERAGE_LINE_LENGTH } from './minified.ts';
29
+ export type { TextShape } from './minified.ts';
28
30
  export { SCHEMA_VERSION, writeGraph } from './schema.ts';
29
31
  export { isExcluded, walkAndExtract } from './walk.ts';
30
32
  export type { WalkConfig, WalkResult } from './walk.ts';
@@ -80,6 +82,18 @@ export interface Config {
80
82
  * see `./gitignore.ts`. A project with no root `.gitignore` is unaffected either way.
81
83
  */
82
84
  respectGitignore?: boolean;
85
+ /**
86
+ * Leave generated (bundled or minified) artifacts out of the index instead of parsing them
87
+ * (default true). A workspace that commits its build output — `dist/` in version control so a
88
+ * server can serve it — hands the walk megabytes of bundler output declaring thousands of short
89
+ * mangled names; indexing it both dominates the run's time and collides with real source symbols,
90
+ * making "one unique workspace-wide name wins" refuse calls the duplicates made ambiguous.
91
+ *
92
+ * Two signals, one structural: a `.min.` path segment, or an average line length past
93
+ * {@link MINIFIED_AVERAGE_LINE_LENGTH}. Set false to index such files anyway, or name their
94
+ * directory in `exclude`.
95
+ */
96
+ skipMinified?: boolean;
83
97
  /** Files larger than this are skipped and counted in the report, never parsed (default 2000000). */
84
98
  maxFileBytes?: number;
85
99
  /** The walk stops discovering new files once this many have been found (default 50000). */
package/lib/index.js CHANGED
@@ -29,6 +29,7 @@ export { LANGUAGE_TABLE, languageFor } from "./languages.js";
29
29
  export { extractFile } from "./extract.js";
30
30
  export { loadGitignore, matchesGitignore, parseGitignore } from "./gitignore.js";
31
31
  export { resolveWorkspace } from "./resolve.js";
32
+ export { isMinifiedName, looksMinified, measureText, MINIFIED_AVERAGE_LINE_LENGTH } from "./minified.js";
32
33
  export { SCHEMA_VERSION, writeGraph } from "./schema.js";
33
34
  export { isExcluded, walkAndExtract } from "./walk.js";
34
35
  export { createWatcher, nodeWatch } from "./watcher.js";
@@ -64,6 +65,7 @@ export const Config = z.object({
64
65
  languages: z.array(z.string()).default([...new Set(LANGUAGE_TABLE.map(spec => spec.language))]),
65
66
  exclude: z.array(z.string()).default(DEFAULT_EXCLUDE),
66
67
  respectGitignore: z.boolean().default(true),
68
+ skipMinified: z.boolean().default(true),
67
69
  maxFileBytes: z.number().default(DEFAULT_MAX_FILE_BYTES),
68
70
  maxFiles: z.number().default(DEFAULT_MAX_FILES),
69
71
  concurrency: z.number().default(DEFAULT_CONCURRENCY),
@@ -167,9 +169,10 @@ async function runSync(projectRoot, config) {
167
169
  * @returns the run's report.
168
170
  */
169
171
  async function runIndex(projectRoot, config, signal) {
170
- const { files, filesSkipped } = await walkAndExtract(projectRoot, {
172
+ const { files, filesSkipped, filesMinified } = await walkAndExtract(projectRoot, {
171
173
  exclude: config.exclude,
172
174
  respectGitignore: config.respectGitignore,
175
+ skipMinified: config.skipMinified,
173
176
  maxFileBytes: config.maxFileBytes,
174
177
  maxFiles: config.maxFiles,
175
178
  concurrency: config.concurrency,
@@ -191,6 +194,7 @@ async function runIndex(projectRoot, config, signal) {
191
194
  projectRoot,
192
195
  filesIndexed: files.length,
193
196
  filesSkipped,
197
+ filesMinified,
194
198
  nodeCount: graph.nodes.length,
195
199
  edgeCount: graph.edges.length,
196
200
  unresolvedCount: graph.unresolved.length,
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Detection of generated (bundled or minified) code, so an index can leave it out.
3
+ *
4
+ * A workspace's build output is not always gitignored: a deployment that ships committed artifacts
5
+ * (`dist/` committed so a server can serve it as-is) hands the walk megabytes of bundler output that
6
+ * is neither source nor hidden from version control. Indexing it is not merely wasted time — those
7
+ * files declare thousands of short, mangled names (`e`, `t`, `n`) that collide with real source
8
+ * symbols, and "one unique workspace-wide name wins" then refuses to resolve every call that the
9
+ * duplicate makes ambiguous.
10
+ *
11
+ * Two signals, both structural rather than name-based on a build tool:
12
+ * - a `.min.` path segment (`bundle.min.js`) — the conventional marker, checked before any read;
13
+ * - an average source line longer than {@link MINIFIED_AVERAGE_LINE_LENGTH} characters, which
14
+ * hand-written code does not reach. Measured on real workspaces: the longest average found in
15
+ * hand-written source is ~70 characters, while bundled output runs from thousands to tens of
16
+ * thousands.
17
+ * @module dsh-plugin-codegraph-tree-sitter/minified
18
+ */
19
+ /**
20
+ * Average line length at or above which a file is treated as generated output. Chosen with roughly
21
+ * 7× headroom over the widest real source line measured (~70 characters), and far below the narrowest
22
+ * generated sample seen (~5 000).
23
+ */
24
+ export declare const MINIFIED_AVERAGE_LINE_LENGTH = 500;
25
+ /** A file's size expressed in lines, without allocating one array element per line. */
26
+ export interface TextShape {
27
+ /** Number of newline-separated lines, matching `text.split('\n').length`. */
28
+ readonly lineCount: number;
29
+ /** Mean characters per line, counting the trailing partial line. */
30
+ readonly averageLineLength: number;
31
+ }
32
+ /**
33
+ * Measure a file's line structure in one pass over its text.
34
+ * @param text - the file's decoded contents.
35
+ * @returns its line count and mean line length. An empty file is one empty line, matching
36
+ * `''.split('\n').length`.
37
+ */
38
+ export declare function measureText(text: string): TextShape;
39
+ /**
40
+ * Whether a path carries the conventional `.min.` marker, which needs no content read to decide.
41
+ * @param path - project-relative path of the file.
42
+ * @returns whether its own name marks it as minified.
43
+ */
44
+ export declare function isMinifiedName(path: string): boolean;
45
+ /**
46
+ * Whether a file is generated output the index should leave out.
47
+ * @param path - project-relative path of the file.
48
+ * @param shape - the file's measured line structure.
49
+ * @returns whether its name or its line shape marks it as generated.
50
+ */
51
+ export declare function looksMinified(path: string, shape: TextShape): boolean;
52
+ //# sourceMappingURL=minified.d.ts.map
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Detection of generated (bundled or minified) code, so an index can leave it out.
3
+ *
4
+ * A workspace's build output is not always gitignored: a deployment that ships committed artifacts
5
+ * (`dist/` committed so a server can serve it as-is) hands the walk megabytes of bundler output that
6
+ * is neither source nor hidden from version control. Indexing it is not merely wasted time — those
7
+ * files declare thousands of short, mangled names (`e`, `t`, `n`) that collide with real source
8
+ * symbols, and "one unique workspace-wide name wins" then refuses to resolve every call that the
9
+ * duplicate makes ambiguous.
10
+ *
11
+ * Two signals, both structural rather than name-based on a build tool:
12
+ * - a `.min.` path segment (`bundle.min.js`) — the conventional marker, checked before any read;
13
+ * - an average source line longer than {@link MINIFIED_AVERAGE_LINE_LENGTH} characters, which
14
+ * hand-written code does not reach. Measured on real workspaces: the longest average found in
15
+ * hand-written source is ~70 characters, while bundled output runs from thousands to tens of
16
+ * thousands.
17
+ * @module dsh-plugin-codegraph-tree-sitter/minified
18
+ */
19
+ import { posix } from 'node:path';
20
+ /**
21
+ * Average line length at or above which a file is treated as generated output. Chosen with roughly
22
+ * 7× headroom over the widest real source line measured (~70 characters), and far below the narrowest
23
+ * generated sample seen (~5 000).
24
+ */
25
+ export const MINIFIED_AVERAGE_LINE_LENGTH = 500;
26
+ /**
27
+ * Measure a file's line structure in one pass over its text.
28
+ * @param text - the file's decoded contents.
29
+ * @returns its line count and mean line length. An empty file is one empty line, matching
30
+ * `''.split('\n').length`.
31
+ */
32
+ export function measureText(text) {
33
+ let newlines = 0;
34
+ for (let index = 0; index < text.length; index++) {
35
+ if (text.charCodeAt(index) === 10)
36
+ newlines++;
37
+ }
38
+ const lineCount = newlines + 1;
39
+ return { lineCount, averageLineLength: text.length / lineCount };
40
+ }
41
+ /**
42
+ * Whether a path carries the conventional `.min.` marker, which needs no content read to decide.
43
+ * @param path - project-relative path of the file.
44
+ * @returns whether its own name marks it as minified.
45
+ */
46
+ export function isMinifiedName(path) {
47
+ return posix.basename(path).includes('.min.');
48
+ }
49
+ /**
50
+ * Whether a file is generated output the index should leave out.
51
+ * @param path - project-relative path of the file.
52
+ * @param shape - the file's measured line structure.
53
+ * @returns whether its name or its line shape marks it as generated.
54
+ */
55
+ export function looksMinified(path, shape) {
56
+ return isMinifiedName(path) || shape.averageLineLength >= MINIFIED_AVERAGE_LINE_LENGTH;
57
+ }
58
+ //# sourceMappingURL=minified.js.map
package/lib/resolve.d.ts CHANGED
@@ -3,13 +3,20 @@
3
3
  *
4
4
  * Pass 1 collects every definition into a name index. Pass 2 resolves each call site's callee name,
5
5
  * in order: (1) the enclosing file imports that name from a relative specifier resolving to a file
6
- * this run indexed — that file's matching declaration wins; (2) otherwise exactly one definition
7
- * workspace-wide carries the name — it wins; (3) otherwise the call is recorded in `unresolved` and no
8
- * `calls` edge is written. An ambiguous edge is worse than a missing one: the model acts on `callers`
9
- * output, so a confidently wrong caller sends it to edit the wrong file, while a missing caller merely
10
- * sends it back to text search.
6
+ * this run indexed — that file's matching declaration wins; (2) otherwise exactly one definition **in
7
+ * the calling file itself** carries the name — lexical scope settles it before the workspace gets a
8
+ * say, which is what keeps a helper declared in two test files (or a `render` in two components) from
9
+ * being counted as ambiguous; (3) otherwise exactly one definition workspace-wide carries the name —
10
+ * it wins; (4) otherwise the call is recorded in `unresolved` and no `calls` edge is written. An
11
+ * ambiguous edge is worse than a missing one: the model acts on `callers` output, so a confidently
12
+ * wrong caller sends it to edit the wrong file, while a missing caller merely sends it back to text
13
+ * search.
11
14
  *
12
- * Rule 3's `unresolved` entries are not one uniform signal. Each carries {@link UnresolvedRef.likelyExternal},
15
+ * Rule 2 is restricted to bare calls. A member call (`x.map()`) names a property of a receiver whose
16
+ * type this resolver does not track, so a same-named local declaration winning it would be a guess
17
+ * rather than a scope resolution — exactly the kind of confidently wrong edge rule 4 exists to avoid.
18
+ *
19
+ * Rule 4's `unresolved` entries are not one uniform signal. Each carries {@link UnresolvedRef.likelyExternal},
13
20
  * which is true for a member call (no type information behind its receiver) or a name the file itself
14
21
  * imports (a known external origin the specifier just didn't resolve) — both real code that was never a
15
22
  * candidate for a workspace edge, as against a bare, undeclared name, which is the actual gap this
package/lib/resolve.js CHANGED
@@ -3,13 +3,20 @@
3
3
  *
4
4
  * Pass 1 collects every definition into a name index. Pass 2 resolves each call site's callee name,
5
5
  * in order: (1) the enclosing file imports that name from a relative specifier resolving to a file
6
- * this run indexed — that file's matching declaration wins; (2) otherwise exactly one definition
7
- * workspace-wide carries the name — it wins; (3) otherwise the call is recorded in `unresolved` and no
8
- * `calls` edge is written. An ambiguous edge is worse than a missing one: the model acts on `callers`
9
- * output, so a confidently wrong caller sends it to edit the wrong file, while a missing caller merely
10
- * sends it back to text search.
6
+ * this run indexed — that file's matching declaration wins; (2) otherwise exactly one definition **in
7
+ * the calling file itself** carries the name — lexical scope settles it before the workspace gets a
8
+ * say, which is what keeps a helper declared in two test files (or a `render` in two components) from
9
+ * being counted as ambiguous; (3) otherwise exactly one definition workspace-wide carries the name —
10
+ * it wins; (4) otherwise the call is recorded in `unresolved` and no `calls` edge is written. An
11
+ * ambiguous edge is worse than a missing one: the model acts on `callers` output, so a confidently
12
+ * wrong caller sends it to edit the wrong file, while a missing caller merely sends it back to text
13
+ * search.
11
14
  *
12
- * Rule 3's `unresolved` entries are not one uniform signal. Each carries {@link UnresolvedRef.likelyExternal},
15
+ * Rule 2 is restricted to bare calls. A member call (`x.map()`) names a property of a receiver whose
16
+ * type this resolver does not track, so a same-named local declaration winning it would be a guess
17
+ * rather than a scope resolution — exactly the kind of confidently wrong edge rule 4 exists to avoid.
18
+ *
19
+ * Rule 4's `unresolved` entries are not one uniform signal. Each carries {@link UnresolvedRef.likelyExternal},
13
20
  * which is true for a member call (no type information behind its receiver) or a name the file itself
14
21
  * imports (a known external origin the specifier just didn't resolve) — both real code that was never a
15
22
  * candidate for a workspace edge, as against a bare, undeclared name, which is the actual gap this
@@ -99,6 +106,22 @@ function buildNameIndex(nodesByFile) {
99
106
  }
100
107
  return index;
101
108
  }
109
+ /**
110
+ * Group one file's own nodes by declaration name, for the same-file tier of call resolution.
111
+ * @param nodes - the nodes declared in one file.
112
+ * @returns every name that file declares, mapped to the nodes carrying it.
113
+ */
114
+ function indexByName(nodes) {
115
+ const index = new Map();
116
+ for (const node of nodes) {
117
+ const existing = index.get(node.name);
118
+ if (existing === undefined)
119
+ index.set(node.name, [node]);
120
+ else
121
+ existing.push(node);
122
+ }
123
+ return index;
124
+ }
102
125
  /**
103
126
  * A name index restricted to `class`/`interface` nodes, for `extends`/`implements` resolution.
104
127
  * `extractHeritage` only ever names a base class or interface, never a function or variable — indexing
@@ -213,9 +236,10 @@ export function resolveWorkspace(files, now) {
213
236
  }
214
237
  const keyToId = mustGet(defIdByKey, file.path);
215
238
  const importedLocalNames = new Set(file.extraction.imports.map(binding => binding.localName));
239
+ const localByName = indexByName(mustGet(nodesByFile, file.path));
216
240
  for (const call of file.extraction.calls) {
217
241
  const callerId = call.callerKey === null ? mustGet(fileNodeId, file.path) : mustGet(keyToId, call.callerKey);
218
- const resolved = resolveName(call.calleeName, bindings, nodesByFile, nameIndex);
242
+ const resolved = resolveName(call.calleeName, bindings, nodesByFile, call.isMemberCall ? undefined : localByName, nameIndex);
219
243
  if (resolved === undefined) {
220
244
  const likelyExternal = call.isMemberCall || importedLocalNames.has(call.calleeName);
221
245
  unresolved.push({ source: callerId, filePath: file.path, calleeName: call.calleeName, line: call.line, col: call.column, likelyExternal });
@@ -228,7 +252,7 @@ export function resolveWorkspace(files, now) {
228
252
  // on it) is a calibrated signal specifically about call-resolution gaps; folding in a different edge
229
253
  // kind would change what that count measures.
230
254
  for (const ref of file.extraction.heritage) {
231
- const resolved = resolveName(ref.targetName, bindings, nodesByFile, typeNameIndex);
255
+ const resolved = resolveName(ref.targetName, bindings, nodesByFile, undefined, typeNameIndex);
232
256
  if (resolved === undefined)
233
257
  continue;
234
258
  edges.push({ source: mustGet(keyToId, ref.sourceKey), target: resolved, kind: ref.relation, provenance: 'tree-sitter' });
@@ -237,13 +261,21 @@ export function resolveWorkspace(files, now) {
237
261
  return { nodes, edges, unresolved };
238
262
  }
239
263
  /**
240
- * Resolve a bare name to exactly one declaration, or `undefined` per rule 3 — shared by call
264
+ * Resolve a bare name to exactly one declaration, or `undefined` per rule 4 — shared by call
241
265
  * resolution (against the workspace-wide, any-kind `nameIndex`) and `extends`/`implements` resolution
242
- * (against the `class`/`interface`-only {@link buildTypeNameIndex}); the two-tier rule itself (an
243
- * imported binding wins if unambiguous in its target file, else a workspace-wide-unique name wins) does
244
- * not depend on which candidate pool the caller passes in.
266
+ * (against the `class`/`interface`-only {@link buildTypeNameIndex}); the rule itself (an imported
267
+ * binding wins if unambiguous in its target file, else a same-file declaration when the caller passes
268
+ * one, else a workspace-wide-unique name wins) does not depend on which candidate pool the caller
269
+ * passes in.
270
+ * @param calleeName - the name at the call or heritage site.
271
+ * @param bindings - the calling file's resolved import bindings.
272
+ * @param nodesByFile - every file's declarations, for the import target lookup.
273
+ * @param localByName - the calling file's own declarations, or `undefined` to skip the same-file tier
274
+ * (the caller decides: member calls and heritage references do not get it).
275
+ * @param nameIndex - the workspace-wide candidate pool this reference resolves against.
276
+ * @returns the winning declaration's node id, or `undefined` when nothing settles it.
245
277
  */
246
- function resolveName(calleeName, bindings, nodesByFile, nameIndex) {
278
+ function resolveName(calleeName, bindings, nodesByFile, localByName, nameIndex) {
247
279
  const bound = bindings.byLocalName.get(calleeName);
248
280
  if (bound !== undefined) {
249
281
  const targetNodes = mustGet(nodesByFile, bound.targetFile);
@@ -253,6 +285,11 @@ function resolveName(calleeName, bindings, nodesByFile, nameIndex) {
253
285
  if (candidates.length === 1)
254
286
  return candidates[0]?.id;
255
287
  }
288
+ // Lexical scope before workspace scope: a name this very file declares is the call's declaration
289
+ // whenever exactly one declaration carries it, no matter how many other files reuse the name.
290
+ const local = localByName?.get(calleeName);
291
+ if (local !== undefined && local.length === 1)
292
+ return local[0]?.id;
256
293
  const workspaceWide = nameIndex.get(calleeName) ?? [];
257
294
  return workspaceWide.length === 1 ? workspaceWide[0]?.id : undefined;
258
295
  }
package/lib/walk.d.ts CHANGED
@@ -33,6 +33,12 @@ export interface WalkConfig {
33
33
  readonly concurrency: number;
34
34
  /** Restrict extraction to these seam language labels when set; every loaded grammar otherwise. */
35
35
  readonly languages?: readonly string[];
36
+ /**
37
+ * Leave generated (bundled or minified) artifacts out of the index instead of parsing them. See
38
+ * `./minified.ts` for the two signals; a workspace that commits its build output is the case this
39
+ * exists for, since such output is neither gitignored nor declared in `exclude`.
40
+ */
41
+ readonly skipMinified: boolean;
36
42
  /**
37
43
  * Also exclude whatever the project root's `.gitignore` names, unioned with `exclude`. A project
38
44
  * that compiles to a gitignored directory (`lib`, `dist`, ...) would otherwise hand the same symbol
@@ -44,7 +50,11 @@ export interface WalkConfig {
44
50
  /** What the walk produced. */
45
51
  export interface WalkResult {
46
52
  readonly files: ExtractedFile[];
53
+ /** Candidates skipped for a reason other than being generated output: over `maxFileBytes`, or cut
54
+ * off by `maxFiles`. */
47
55
  readonly filesSkipped: number;
56
+ /** Candidates skipped because they are generated output — see {@link WalkConfig.skipMinified}. */
57
+ readonly filesMinified: number;
48
58
  }
49
59
  /**
50
60
  * Walk `projectRoot` and parse every candidate file it finds.
package/lib/walk.js CHANGED
@@ -15,6 +15,7 @@ import { createParser } from "./grammar.js";
15
15
  import { extractFile } from "./extract.js";
16
16
  import { loadGitignore, matchesGitignore } from "./gitignore.js";
17
17
  import { languageFor } from "./languages.js";
18
+ import { isMinifiedName, looksMinified, measureText } from "./minified.js";
18
19
  /**
19
20
  * Whether one directory entry is excluded from both the index and (for `watcher.ts`) the watch tree —
20
21
  * the single definition both consult, so "what the index covers" and "what gets watched" can never
@@ -69,8 +70,18 @@ async function discover(projectRoot, config, signal) {
69
70
  await scan('');
70
71
  return { candidates, overflow };
71
72
  }
72
- /** Parse one candidate, or return `undefined` when it is over {@link WalkConfig.maxFileBytes}. */
73
+ /**
74
+ * Parse one candidate.
75
+ * @param candidate - the file to parse.
76
+ * @param config - the walk's bounds.
77
+ * @param signal - aborts the parse.
78
+ * @returns the extracted file, `'minified'` when it was skipped as generated output, or `undefined`
79
+ * when it is over {@link WalkConfig.maxFileBytes}.
80
+ */
73
81
  async function parseCandidate(candidate, config, signal) {
82
+ // Checked before the stat and read: a `.min.` name settles it with no I/O at all.
83
+ if (config.skipMinified && isMinifiedName(candidate.path))
84
+ return 'minified';
74
85
  const stats = await stat(candidate.absolute);
75
86
  if (stats.size > config.maxFileBytes)
76
87
  return undefined;
@@ -79,6 +90,9 @@ async function parseCandidate(candidate, config, signal) {
79
90
  if (spec === undefined)
80
91
  return undefined;
81
92
  const text = await readFile(candidate.absolute, 'utf8');
93
+ const shape = measureText(text);
94
+ if (config.skipMinified && looksMinified(candidate.path, shape))
95
+ return 'minified';
82
96
  const parser = await createParser(spec);
83
97
  try {
84
98
  signal?.throwIfAborted();
@@ -94,7 +108,7 @@ async function parseCandidate(candidate, config, signal) {
94
108
  size: stats.size,
95
109
  modifiedAt: stats.mtimeMs,
96
110
  contentHash: createHash('sha256').update(text).digest('hex'),
97
- lineCount: text.split('\n').length,
111
+ lineCount: shape.lineCount,
98
112
  extraction,
99
113
  };
100
114
  }
@@ -135,12 +149,15 @@ export async function walkAndExtract(projectRoot, config, signal) {
135
149
  const parsed = await mapWithConcurrency(candidates, config.concurrency, candidate => parseCandidate(candidate, config, signal));
136
150
  const files = [];
137
151
  let filesSkipped = overflow;
138
- for (const file of parsed) {
139
- if (file === undefined)
152
+ let filesMinified = 0;
153
+ for (const result of parsed) {
154
+ if (result === undefined)
140
155
  filesSkipped++;
156
+ else if (result === 'minified')
157
+ filesMinified++;
141
158
  else
142
- files.push(file);
159
+ files.push(result);
143
160
  }
144
- return { files, filesSkipped };
161
+ return { files, filesSkipped, filesMinified };
145
162
  }
146
163
  //# sourceMappingURL=walk.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-plugin-codegraph-tree-sitter",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "description": "Self-built code-graph indexer for DeepSeek Harness — parses a workspace with web-tree-sitter and writes schema version 4 to .codegraph/codegraph.db, the same on-disk format the codegraph CLI writes, so no external indexer is required",
5
5
  "keywords": [
6
6
  "dsh",
@@ -51,7 +51,7 @@
51
51
  "@deepseek-ai/schemastery": "^3.18.1",
52
52
  "tree-sitter-wasms": "^0.1.13",
53
53
  "web-tree-sitter": "^0.25.10",
54
- "dsh-plugin-codegraph-service": "^0.1.6"
54
+ "dsh-plugin-codegraph-service": "^0.1.7"
55
55
  },
56
56
  "peerDependencies": {
57
57
  "@deepseek-ai/cordis": ">=4.0.1",