dsh-plugin-codegraph-tree-sitter 0.5.0 → 0.5.1
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 +9 -0
- package/lib/index.js +47 -16
- package/package.json +2 -2
package/lib/index.d.ts
CHANGED
|
@@ -79,6 +79,8 @@ export declare const DEFAULT_CONCURRENCY = 4;
|
|
|
79
79
|
export declare const DEFAULT_WATCH_DEBOUNCE_MS = 2000;
|
|
80
80
|
/** Default cap on directories watched individually, one watch each, by the fs seam. */
|
|
81
81
|
export declare const DEFAULT_MAX_WATCHED_DIRECTORIES = 4000;
|
|
82
|
+
/** Default age past which a session-open refreshes an existing index (ten minutes). */
|
|
83
|
+
export declare const DEFAULT_AUTO_REFRESH_AFTER_MS: number;
|
|
82
84
|
/** Plugin configuration: the indexer's identity and every deployment-varying bound on a run. */
|
|
83
85
|
export interface Config {
|
|
84
86
|
/**
|
|
@@ -174,6 +176,13 @@ export interface Config {
|
|
|
174
176
|
* so a composition without background jobs simply schedules nothing.
|
|
175
177
|
*/
|
|
176
178
|
autoIndexOnSession?: boolean;
|
|
179
|
+
/**
|
|
180
|
+
* How stale an existing index may be before a new session refreshes it in the background (default
|
|
181
|
+
* 600000, ten minutes). An index older than this is rebuilt on session start using the parse cache,
|
|
182
|
+
* which is milliseconds — and rebuilding is also what restarts the file watcher, since watching
|
|
183
|
+
* only ever begins after a successful index.
|
|
184
|
+
*/
|
|
185
|
+
autoRefreshAfterMs?: number;
|
|
177
186
|
}
|
|
178
187
|
export declare const Config: z<Config>;
|
|
179
188
|
/**
|
package/lib/index.js
CHANGED
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
* this package ships with.
|
|
28
28
|
* @module dsh-plugin-codegraph-tree-sitter
|
|
29
29
|
*/
|
|
30
|
-
import {
|
|
30
|
+
import { readFileSync } from 'node:fs';
|
|
31
31
|
import { stat } from 'node:fs/promises';
|
|
32
32
|
import { join } from 'node:path';
|
|
33
33
|
import z from '@deepseek-ai/schemastery';
|
|
@@ -83,6 +83,8 @@ export const DEFAULT_CONCURRENCY = 4;
|
|
|
83
83
|
export const DEFAULT_WATCH_DEBOUNCE_MS = 2_000;
|
|
84
84
|
/** Default cap on directories watched individually, one watch each, by the fs seam. */
|
|
85
85
|
export const DEFAULT_MAX_WATCHED_DIRECTORIES = 4_000;
|
|
86
|
+
/** Default age past which a session-open refreshes an existing index (ten minutes). */
|
|
87
|
+
export const DEFAULT_AUTO_REFRESH_AFTER_MS = 10 * 60 * 1000;
|
|
86
88
|
export const Config = z.object({
|
|
87
89
|
indexerId: z.string().default(DEFAULT_INDEXER_ID),
|
|
88
90
|
languages: z.array(z.string()).default([...new Set(LANGUAGE_TABLE.map(spec => spec.language))]),
|
|
@@ -99,6 +101,7 @@ export const Config = z.object({
|
|
|
99
101
|
watchDebounceMs: z.number().default(DEFAULT_WATCH_DEBOUNCE_MS),
|
|
100
102
|
maxWatchedDirectories: z.number().default(DEFAULT_MAX_WATCHED_DIRECTORIES),
|
|
101
103
|
autoIndexOnSession: z.boolean().default(true),
|
|
104
|
+
autoRefreshAfterMs: z.number().default(DEFAULT_AUTO_REFRESH_AFTER_MS),
|
|
102
105
|
});
|
|
103
106
|
/**
|
|
104
107
|
* Register the tree-sitter indexer.
|
|
@@ -124,10 +127,11 @@ export function apply(ctx, config) {
|
|
|
124
127
|
// cross-plugin relation here for an invariant to describe — just process-local bookkeeping this
|
|
125
128
|
// effect's cleanup below tears down on unload.
|
|
126
129
|
const watchers = new Map();
|
|
127
|
-
//
|
|
128
|
-
//
|
|
129
|
-
//
|
|
130
|
-
|
|
130
|
+
// When this instance last started an automatic index, keyed by root. This is not a "already done"
|
|
131
|
+
// set: an index goes stale while the process runs, so a later session in the same workspace may
|
|
132
|
+
// legitimately trigger another refresh. The map only keeps a burst of sessions opened at once from
|
|
133
|
+
// queueing the same rebuild repeatedly; the freshness decision itself reads the status sidecar.
|
|
134
|
+
const lastAutoRun = new Map();
|
|
131
135
|
// `agent/created` is typed by `@deepseek-ai/dsh-agent`'s module augmentation. Reading the event
|
|
132
136
|
// through a local structural face keeps this package's host dependencies unchanged: it needs one
|
|
133
137
|
// event name and one payload shape, not the whole agent package.
|
|
@@ -192,7 +196,7 @@ export function apply(ctx, config) {
|
|
|
192
196
|
if (resolved.autoIndexOnSession) {
|
|
193
197
|
// Registered inside this effect so the listener follows the plugin's lifetime: unloading the
|
|
194
198
|
// plugin must stop new sessions from scheduling work this instance can no longer run.
|
|
195
|
-
yield agentEvents.on('agent/created', payload => autoIndexOnSession(ctx,
|
|
199
|
+
yield agentEvents.on('agent/created', payload => autoIndexOnSession(ctx, resolved, lastAutoRun, payload));
|
|
196
200
|
}
|
|
197
201
|
yield ctx.codegraph.registerIndexer(indexer);
|
|
198
202
|
}, 'codegraph-tree-sitter');
|
|
@@ -200,30 +204,39 @@ export function apply(ctx, config) {
|
|
|
200
204
|
/** Stable job kind of an automatic session-start index, used for retention and diagnostics. */
|
|
201
205
|
const AUTO_INDEX_JOB_KIND = 'codegraph-index';
|
|
202
206
|
/**
|
|
203
|
-
* Index a new session's workspace in the background
|
|
207
|
+
* Index a new session's workspace in the background — when nothing has indexed it yet, or when what
|
|
208
|
+
* is there has gone stale.
|
|
204
209
|
*
|
|
205
210
|
* Runs synchronously inside `agent/created` dispatch, so it must never block or throw: it only decides
|
|
206
211
|
* whether a build is warranted and hands one to the host's job registry. A synchronous build here
|
|
207
212
|
* would stall agent creation for the length of a parse, which is why a composition without background
|
|
208
213
|
* jobs does nothing instead.
|
|
214
|
+
*
|
|
215
|
+
* Refreshing a stale index is what restarts the file watcher after a host restart: watching only
|
|
216
|
+
* begins once a successful index has established a baseline, so a workspace whose index is minutes
|
|
217
|
+
* old would otherwise stay frozen at whatever the last process left behind.
|
|
209
218
|
* @param ctx - the plugin context, consulted for the optional job registry.
|
|
210
|
-
* @param
|
|
219
|
+
* @param config - the resolved plugin configuration, for the staleness threshold.
|
|
220
|
+
* @param lastAutoRun - when this instance last started an automatic index for each root.
|
|
211
221
|
* @param payload - the `agent/created` dispatch payload.
|
|
212
222
|
*/
|
|
213
|
-
function autoIndexOnSession(ctx,
|
|
223
|
+
function autoIndexOnSession(ctx, config, lastAutoRun, payload) {
|
|
214
224
|
try {
|
|
215
225
|
const agent = payload.agent;
|
|
216
226
|
const root = agent?.session?.header?.cwd;
|
|
217
227
|
// No workspace means no root to claim: an agent created without a `cwd` has nothing to index.
|
|
218
228
|
if (agent === undefined || !root)
|
|
219
229
|
return;
|
|
220
|
-
|
|
221
|
-
//
|
|
222
|
-
|
|
230
|
+
const now = Date.now();
|
|
231
|
+
// A burst of sessions opened on one workspace must not queue the same rebuild repeatedly.
|
|
232
|
+
const previous = lastAutoRun.get(root);
|
|
233
|
+
if (previous !== undefined && now - previous < config.autoRefreshAfterMs)
|
|
223
234
|
return;
|
|
224
|
-
//
|
|
225
|
-
// the
|
|
226
|
-
|
|
235
|
+
// A fresh sidecar means a recent run is already describing this workspace; an absent, unreadable
|
|
236
|
+
// or old one means the graph is missing or behind. Checked before the job lookup so a composition
|
|
237
|
+
// without background jobs never pays for the probe.
|
|
238
|
+
const indexedAt = readStatusIndexedAt(root);
|
|
239
|
+
if (indexedAt !== undefined && now - indexedAt < config.autoRefreshAfterMs)
|
|
227
240
|
return;
|
|
228
241
|
// `ctx.get` rather than an `inject` entry: `jobs` is a host composition choice, and requiring it in
|
|
229
242
|
// `inject` would hide this whole indexer from a profile that runs no background jobs.
|
|
@@ -239,7 +252,7 @@ function autoIndexOnSession(ctx, autoIndexed, payload) {
|
|
|
239
252
|
});
|
|
240
253
|
// Marked only after the registry accepted the job, so a transient refusal leaves the workspace
|
|
241
254
|
// eligible for the next session that opens in it.
|
|
242
|
-
|
|
255
|
+
lastAutoRun.set(root, now);
|
|
243
256
|
}
|
|
244
257
|
catch (error) {
|
|
245
258
|
// The host treats a throwing listener as vetoing publication. Losing an index is a degradation,
|
|
@@ -247,6 +260,24 @@ function autoIndexOnSession(ctx, autoIndexed, payload) {
|
|
|
247
260
|
console.error(`codegraph-tree-sitter: automatic indexing for a new session failed: ${String(error)}`);
|
|
248
261
|
}
|
|
249
262
|
}
|
|
263
|
+
/**
|
|
264
|
+
* When the workspace's sidecar says its index was built.
|
|
265
|
+
* @param root - the workspace root.
|
|
266
|
+
* @returns the epoch-millisecond timestamp, or `undefined` when the sidecar is absent or unreadable —
|
|
267
|
+
* either way the graph cannot be trusted to be current.
|
|
268
|
+
*/
|
|
269
|
+
function readStatusIndexedAt(root) {
|
|
270
|
+
try {
|
|
271
|
+
const parsed = JSON.parse(readFileSync(join(root, INDEX_STATUS_RELATIVE_PATH), 'utf8'));
|
|
272
|
+
if (typeof parsed !== 'object' || parsed === null)
|
|
273
|
+
return undefined;
|
|
274
|
+
const indexedAt = parsed.indexedAt;
|
|
275
|
+
return typeof indexedAt === 'number' && Number.isFinite(indexedAt) ? indexedAt : undefined;
|
|
276
|
+
}
|
|
277
|
+
catch {
|
|
278
|
+
return undefined;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
250
281
|
/**
|
|
251
282
|
* The producer body of one automatic indexing job.
|
|
252
283
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-plugin-codegraph-tree-sitter",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.1",
|
|
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.5.
|
|
54
|
+
"dsh-plugin-codegraph-service": "^0.5.1"
|
|
55
55
|
},
|
|
56
56
|
"peerDependencies": {
|
|
57
57
|
"@deepseek-ai/cordis": ">=4.0.1",
|