dsh-plugin-codegraph-tree-sitter 0.2.0 → 0.3.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/lib/index.d.ts +17 -0
- package/lib/index.js +101 -1
- package/package.json +2 -2
package/lib/index.d.ts
CHANGED
|
@@ -15,6 +15,13 @@
|
|
|
15
15
|
* backend serves from another execution world is watched where it actually lives, and a backend that
|
|
16
16
|
* cannot watch at all degrades to explicit indexing instead of failing the plugin.
|
|
17
17
|
*
|
|
18
|
+
* A workspace with no index yet is also indexed automatically the first time a session is created in
|
|
19
|
+
* it (`agent/created`), through the host's background-job registry when the composition has one. The
|
|
20
|
+
* graph is most useful on the first `codegraph` query, and a session that starts in an unindexed
|
|
21
|
+
* workspace is exactly the moment a caller would otherwise have to ask for a build by hand. This
|
|
22
|
+
* never runs synchronously inside the event dispatch: creating an agent must not wait on a
|
|
23
|
+
* multi-minute parse, and an index is an optimization, never a precondition for starting work.
|
|
24
|
+
*
|
|
18
25
|
* Namespace plugin (named exports, no default export). Grammars load lazily, one per language, on
|
|
19
26
|
* first sight of a matching file, and stay cached for the process — never eagerly for every grammar
|
|
20
27
|
* this package ships with.
|
|
@@ -143,6 +150,16 @@ export interface Config {
|
|
|
143
150
|
* covering only part of the tree.
|
|
144
151
|
*/
|
|
145
152
|
maxWatchedDirectories?: number;
|
|
153
|
+
/**
|
|
154
|
+
* Build a first index automatically, in the background, when a session is created in a workspace
|
|
155
|
+
* that has none (default true). A workspace is only ever auto-indexed once per process and never
|
|
156
|
+
* when its status sidecar already exists, so this cannot rebuild a graph that is already there.
|
|
157
|
+
* Set false to keep every build explicitly requested.
|
|
158
|
+
*
|
|
159
|
+
* Requires the host's `jobs` service: the build is never run synchronously inside session creation,
|
|
160
|
+
* so a composition without background jobs simply schedules nothing.
|
|
161
|
+
*/
|
|
162
|
+
autoIndexOnSession?: boolean;
|
|
146
163
|
}
|
|
147
164
|
export declare const Config: z<Config>;
|
|
148
165
|
/**
|
package/lib/index.js
CHANGED
|
@@ -15,15 +15,23 @@
|
|
|
15
15
|
* backend serves from another execution world is watched where it actually lives, and a backend that
|
|
16
16
|
* cannot watch at all degrades to explicit indexing instead of failing the plugin.
|
|
17
17
|
*
|
|
18
|
+
* A workspace with no index yet is also indexed automatically the first time a session is created in
|
|
19
|
+
* it (`agent/created`), through the host's background-job registry when the composition has one. The
|
|
20
|
+
* graph is most useful on the first `codegraph` query, and a session that starts in an unindexed
|
|
21
|
+
* workspace is exactly the moment a caller would otherwise have to ask for a build by hand. This
|
|
22
|
+
* never runs synchronously inside the event dispatch: creating an agent must not wait on a
|
|
23
|
+
* multi-minute parse, and an index is an optimization, never a precondition for starting work.
|
|
24
|
+
*
|
|
18
25
|
* Namespace plugin (named exports, no default export). Grammars load lazily, one per language, on
|
|
19
26
|
* first sight of a matching file, and stay cached for the process — never eagerly for every grammar
|
|
20
27
|
* this package ships with.
|
|
21
28
|
* @module dsh-plugin-codegraph-tree-sitter
|
|
22
29
|
*/
|
|
30
|
+
import { existsSync } from 'node:fs';
|
|
23
31
|
import { stat } from 'node:fs/promises';
|
|
24
32
|
import { join } from 'node:path';
|
|
25
33
|
import z from '@deepseek-ai/schemastery';
|
|
26
|
-
import { CodegraphIndexerId } from 'dsh-plugin-codegraph-service';
|
|
34
|
+
import { CodegraphIndexerId, INDEX_STATUS_RELATIVE_PATH } from 'dsh-plugin-codegraph-service';
|
|
27
35
|
import { LANGUAGE_TABLE } from "./languages.js";
|
|
28
36
|
import { walkAndExtract } from "./walk.js";
|
|
29
37
|
import { writeParseCache } from "./cache.js";
|
|
@@ -81,6 +89,7 @@ export const Config = z.object({
|
|
|
81
89
|
watch: z.boolean().default(true),
|
|
82
90
|
watchDebounceMs: z.number().default(DEFAULT_WATCH_DEBOUNCE_MS),
|
|
83
91
|
maxWatchedDirectories: z.number().default(DEFAULT_MAX_WATCHED_DIRECTORIES),
|
|
92
|
+
autoIndexOnSession: z.boolean().default(true),
|
|
84
93
|
});
|
|
85
94
|
/**
|
|
86
95
|
* Register the tree-sitter indexer.
|
|
@@ -105,6 +114,14 @@ export function apply(ctx, config) {
|
|
|
105
114
|
// cross-plugin relation here for an invariant to describe — just process-local bookkeeping this
|
|
106
115
|
// effect's cleanup below tears down on unload.
|
|
107
116
|
const watchers = new Map();
|
|
117
|
+
// Roots this plugin instance has already started an automatic index for. The set is only a guard
|
|
118
|
+
// against a burst of agents opened on one workspace each scheduling the same rebuild; what a
|
|
119
|
+
// previous *process* indexed is recorded by the status sidecar `autoIndexOnSession` reads instead.
|
|
120
|
+
const autoIndexed = new Set();
|
|
121
|
+
// `agent/created` is typed by `@deepseek-ai/dsh-agent`'s module augmentation. Reading the event
|
|
122
|
+
// through a local structural face keeps this package's host dependencies unchanged: it needs one
|
|
123
|
+
// event name and one payload shape, not the whole agent package.
|
|
124
|
+
const agentEvents = ctx;
|
|
108
125
|
/** Start (or leave running) file watching for `projectRoot`, once it has a baseline to refresh. */
|
|
109
126
|
function ensureWatching(projectRoot) {
|
|
110
127
|
if (watchers.has(projectRoot))
|
|
@@ -162,9 +179,92 @@ export function apply(ctx, config) {
|
|
|
162
179
|
watcher.stop();
|
|
163
180
|
watchers.clear();
|
|
164
181
|
};
|
|
182
|
+
if (resolved.autoIndexOnSession) {
|
|
183
|
+
// Registered inside this effect so the listener follows the plugin's lifetime: unloading the
|
|
184
|
+
// plugin must stop new sessions from scheduling work this instance can no longer run.
|
|
185
|
+
yield agentEvents.on('agent/created', payload => autoIndexOnSession(ctx, autoIndexed, payload));
|
|
186
|
+
}
|
|
165
187
|
yield ctx.codegraph.registerIndexer(indexer);
|
|
166
188
|
}, 'codegraph-tree-sitter');
|
|
167
189
|
}
|
|
190
|
+
/** Stable job kind of an automatic session-start index, used for retention and diagnostics. */
|
|
191
|
+
const AUTO_INDEX_JOB_KIND = 'codegraph-index';
|
|
192
|
+
/**
|
|
193
|
+
* Index a new session's workspace in the background, when nothing has indexed it yet.
|
|
194
|
+
*
|
|
195
|
+
* Runs synchronously inside `agent/created` dispatch, so it must never block or throw: it only decides
|
|
196
|
+
* whether a build is warranted and hands one to the host's job registry. A synchronous build here
|
|
197
|
+
* would stall agent creation for the length of a parse, which is why a composition without background
|
|
198
|
+
* jobs does nothing instead.
|
|
199
|
+
* @param ctx - the plugin context, consulted for the optional job registry.
|
|
200
|
+
* @param autoIndexed - roots this process has already started an automatic index for.
|
|
201
|
+
* @param payload - the `agent/created` dispatch payload.
|
|
202
|
+
*/
|
|
203
|
+
function autoIndexOnSession(ctx, autoIndexed, payload) {
|
|
204
|
+
try {
|
|
205
|
+
const agent = payload.agent;
|
|
206
|
+
const root = agent?.session?.header?.cwd;
|
|
207
|
+
// No workspace means no root to claim: an agent created without a `cwd` has nothing to index.
|
|
208
|
+
if (agent === undefined || !root)
|
|
209
|
+
return;
|
|
210
|
+
// Once per root per process. A second agent opened on the same workspace must not queue a second
|
|
211
|
+
// full rebuild of a graph the first one is already building.
|
|
212
|
+
if (autoIndexed.has(root))
|
|
213
|
+
return;
|
|
214
|
+
// An existing status sidecar means a previous run already indexed this workspace. Checked before
|
|
215
|
+
// the job lookup so a composition without background jobs never pays for a filesystem probe.
|
|
216
|
+
if (existsSync(join(root, INDEX_STATUS_RELATIVE_PATH)))
|
|
217
|
+
return;
|
|
218
|
+
// `ctx.get` rather than an `inject` entry: `jobs` is a host composition choice, and requiring it in
|
|
219
|
+
// `inject` would hide this whole indexer from a profile that runs no background jobs.
|
|
220
|
+
const jobs = ctx.get('jobs');
|
|
221
|
+
if (jobs === undefined)
|
|
222
|
+
return;
|
|
223
|
+
jobs.start({
|
|
224
|
+
// The registry keys a job by its owning session id, not the agent object.
|
|
225
|
+
owner: agent.id,
|
|
226
|
+
kind: AUTO_INDEX_JOB_KIND,
|
|
227
|
+
label: `codegraph auto index ${root}`,
|
|
228
|
+
run: handle => runAutoIndexJob(ctx, root, handle),
|
|
229
|
+
});
|
|
230
|
+
// Marked only after the registry accepted the job, so a transient refusal leaves the workspace
|
|
231
|
+
// eligible for the next session that opens in it.
|
|
232
|
+
autoIndexed.add(root);
|
|
233
|
+
}
|
|
234
|
+
catch (error) {
|
|
235
|
+
// The host treats a throwing listener as vetoing publication. Losing an index is a degradation,
|
|
236
|
+
// never a reason to stop a session from starting.
|
|
237
|
+
console.error(`codegraph-tree-sitter: automatic indexing for a new session failed: ${String(error)}`);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* The producer body of one automatic indexing job.
|
|
242
|
+
*
|
|
243
|
+
* Both halves of the host's job contract are mandatory: `cancel` aborts the run, and `done` settles
|
|
244
|
+
* even on failure — a rejected `done` breaks the registry's bookkeeping and leaves the job looking
|
|
245
|
+
* stuck forever.
|
|
246
|
+
* @param ctx - the plugin context.
|
|
247
|
+
* @param root - the workspace to index.
|
|
248
|
+
* @param handle - the job's producer face, for the job's live log.
|
|
249
|
+
* @returns the job's cancellation hook and settlement promise.
|
|
250
|
+
*/
|
|
251
|
+
function runAutoIndexJob(ctx, root, handle) {
|
|
252
|
+
const controller = new AbortController();
|
|
253
|
+
const done = (async () => {
|
|
254
|
+
try {
|
|
255
|
+
const report = await ctx.codegraph.index(root, controller.signal);
|
|
256
|
+
handle.append(`Indexed ${root}: ${report.filesIndexed} files, ${report.nodeCount} symbols, ` +
|
|
257
|
+
`${report.edgeCount} relationships.\n`);
|
|
258
|
+
return { status: 'completed' };
|
|
259
|
+
}
|
|
260
|
+
catch (error) {
|
|
261
|
+
const message = String(error);
|
|
262
|
+
handle.append(`codegraph auto index failed for ${root}: ${message}\n`);
|
|
263
|
+
return { status: 'failed', detail: message };
|
|
264
|
+
}
|
|
265
|
+
})();
|
|
266
|
+
return { cancel: () => controller.abort(), done };
|
|
267
|
+
}
|
|
168
268
|
/**
|
|
169
269
|
* Run one watch-triggered reindex. Always a full rebuild in this version — `runIndex` has no notion of
|
|
170
270
|
* a file subset — so the watcher's own collected paths are not consulted here.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-plugin-codegraph-tree-sitter",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
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.
|
|
54
|
+
"dsh-plugin-codegraph-service": "^0.3.0"
|
|
55
55
|
},
|
|
56
56
|
"peerDependencies": {
|
|
57
57
|
"@deepseek-ai/cordis": ">=4.0.1",
|