@tekyzinc/gsd-t 4.11.10 → 4.12.10
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/CHANGELOG.md +22 -0
- package/README.md +1 -1
- package/bin/gsd-t-graph-scip-upgrade.cjs +79 -26
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to GSD-T are documented here. Updated with each release.
|
|
4
4
|
|
|
5
|
+
## [4.12.10] - 2026-06-27
|
|
6
|
+
|
|
7
|
+
### Added — Python call-graph resolution (scip-python)
|
|
8
|
+
|
|
9
|
+
The precise call-graph now covers Python, not just TS/JS. `buildScipResolver` runs scip-python alongside scip-typescript and merges both resolution maps — a repo with both languages gets one unified call-graph.
|
|
10
|
+
|
|
11
|
+
- `bin/gsd-t-graph-scip-upgrade.cjs`: run scip-python when Python source is present; merge its `fileRefs` into the resolver. New `detectRepoLanguages()` skips an indexer when its language is absent (no wasted run). `runScipPython` now emits to a real `.gsd-t/index-python.scip` and passes `--project-name` + `--project-version 0.0.0` (scip-python crashes on an undefined version).
|
|
12
|
+
- The SCIP reader was already language-agnostic (Python symbols use the same `name().` descriptor form), so cross-file Python calls resolve with no reader change.
|
|
13
|
+
- `gsd-t install` / `gsd-t doctor` already covered scip-python (added in M95/M96).
|
|
14
|
+
|
|
15
|
+
Proven on BDS (35 .py): 125 → 1,331 resolved calls (1,206 Python); `who-calls` returns real Python callers. Fixture test resolves a cross-file `pkg.util.compute_total` call. Suite: 2521/2521 pass.
|
|
16
|
+
|
|
17
|
+
## [4.11.11] - 2026-06-27
|
|
18
|
+
|
|
19
|
+
### Fixed — SCIP resolution missed nested/monorepo tsconfig (call-graph empty on subdir-tsconfig projects)
|
|
20
|
+
|
|
21
|
+
`runScipTypescript` ran scip-typescript without `--infer-tsconfig`, so projects whose `tsconfig.json` lives in a subdirectory (`web/tsconfig.json`, monorepo packages) produced 0 resolved call edges — scip-typescript found no root tsconfig and skipped resolution.
|
|
22
|
+
|
|
23
|
+
- `bin/gsd-t-graph-scip-upgrade.cjs`: add `--infer-tsconfig` so scip-typescript discovers a nested tsconfig; bumped the timeout to 180s for larger inferred projects.
|
|
24
|
+
|
|
25
|
+
Proven on ClaudeWebCLI (tsconfig at `web/tsconfig.json`): 0 → 1,149 resolved call edges. Suite: 2519/2519 pass.
|
|
26
|
+
|
|
5
27
|
## [4.11.10] - 2026-06-27
|
|
6
28
|
|
|
7
29
|
### Added — M97: the code graph is the default for ambient code-reading (grep-intercept), + 3 call-resolution fixes
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# GSD-T: Contract-Driven Development for Claude Code
|
|
2
2
|
|
|
3
|
-
**v4.
|
|
3
|
+
**v4.12.10** - A methodology for reliable, parallelizable development using Claude Code with optional Agent Teams support.
|
|
4
4
|
|
|
5
5
|
**Eliminates context rot** — task-level fresh dispatch (one subagent per task, ~10-20% context each) means compaction never triggers.
|
|
6
6
|
**Compaction-proof debug loops** — `gsd-t headless --debug-loop` runs test-fix-retest cycles as separate `claude -p` sessions. A JSONL debug ledger persists all hypothesis/fix/learning history across fresh sessions. Anti-repetition preamble injection prevents retrying failed hypotheses. Escalation tiers (sonnet → opus → human) and a hard iteration ceiling enforced externally.
|
|
@@ -134,25 +134,65 @@ function runScipTypescript(projectRoot, outPath) {
|
|
|
134
134
|
// M95: emit to a REAL file (outPath) so the index can be READ and call edges
|
|
135
135
|
// resolved — not /dev/null. The old /dev/null path only proved invocability.
|
|
136
136
|
const out = outPath || path.join(projectRoot, '.gsd-t', 'index.scip');
|
|
137
|
-
|
|
137
|
+
// --infer-tsconfig: resolve a tsconfig even when one isn't at the repo root
|
|
138
|
+
// (monorepos / nested layouts — e.g. web/tsconfig.json). Without it, projects
|
|
139
|
+
// whose tsconfig lives in a subdir got 0 resolved call edges. [RULE] scip-infer-nested-tsconfig
|
|
140
|
+
const scip = spawnSync('scip-typescript', ['index', '--infer-tsconfig', '--output', out, '.'], {
|
|
138
141
|
cwd: projectRoot,
|
|
139
142
|
encoding: 'utf8',
|
|
140
|
-
timeout:
|
|
143
|
+
timeout: 180_000,
|
|
141
144
|
});
|
|
142
145
|
if (scip.status === 0) return { ok: true, scipPath: out };
|
|
143
146
|
return { ok: false, error: scip.stderr || `exit code ${scip.status}` };
|
|
144
147
|
}
|
|
145
148
|
|
|
146
|
-
function runScipPython(projectRoot) {
|
|
147
|
-
|
|
149
|
+
function runScipPython(projectRoot, outPath) {
|
|
150
|
+
// Emit to a REAL file (separate from the TS index) so it can be READ + merged.
|
|
151
|
+
// --project-name is required-ish for stable symbols; derive from the dir name.
|
|
152
|
+
const out = outPath || path.join(projectRoot, '.gsd-t', 'index-python.scip');
|
|
153
|
+
const projName = path.basename(projectRoot).replace(/[^A-Za-z0-9_-]/g, '-') || 'project';
|
|
154
|
+
// scip-python REQUIRES a project version — it crashes (makeModuleInit) when the
|
|
155
|
+
// version is undefined (no pyproject.toml). Pass an explicit fallback version.
|
|
156
|
+
const scip = spawnSync('scip-python',
|
|
157
|
+
['index', '--project-name', projName, '--project-version', '0.0.0', '--output', out, '.'], {
|
|
148
158
|
cwd: projectRoot,
|
|
149
159
|
encoding: 'utf8',
|
|
150
|
-
timeout:
|
|
160
|
+
timeout: 180_000,
|
|
151
161
|
});
|
|
152
|
-
if (scip.status === 0) return { ok: true };
|
|
162
|
+
if (scip.status === 0) return { ok: true, scipPath: out };
|
|
153
163
|
return { ok: false, error: scip.stderr || `exit code ${scip.status}` };
|
|
154
164
|
}
|
|
155
165
|
|
|
166
|
+
// Cheap shallow language detection — does the repo contain TS/JS or Python source
|
|
167
|
+
// (excluding vendored/build dirs)? Used to skip an indexer when its language is
|
|
168
|
+
// absent. Walks at most a bounded depth to stay fast.
|
|
169
|
+
function detectRepoLanguages(repoRoot) {
|
|
170
|
+
const SKIP = new Set(['node_modules', '.git', 'dist', 'build', 'out', '.venv', 'venv',
|
|
171
|
+
'site-packages', '__pycache__', '.next', 'coverage', 'Pods', '.dart_tool', 'vendor']);
|
|
172
|
+
let typescript = false, python = false;
|
|
173
|
+
function isSkip(name) {
|
|
174
|
+
if (SKIP.has(name)) return true;
|
|
175
|
+
return ['dist', 'build', 'out'].some(p => name.length > p.length && name.startsWith(p) &&
|
|
176
|
+
(name[p.length] === '-' || name[p.length] === '.' || name[p.length] === '_'));
|
|
177
|
+
}
|
|
178
|
+
function walk(dir, depth) {
|
|
179
|
+
if ((typescript && python) || depth > 6) return;
|
|
180
|
+
let entries;
|
|
181
|
+
try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch { return; }
|
|
182
|
+
for (const e of entries) {
|
|
183
|
+
if (typescript && python) return;
|
|
184
|
+
if (e.isDirectory()) { if (!isSkip(e.name)) walk(path.join(dir, e.name), depth + 1); }
|
|
185
|
+
else if (e.isFile()) {
|
|
186
|
+
const ext = path.extname(e.name).toLowerCase();
|
|
187
|
+
if (ext === '.ts' || ext === '.tsx' || ext === '.js' || ext === '.jsx' || ext === '.mjs' || ext === '.cjs') typescript = true;
|
|
188
|
+
else if (ext === '.py') python = true;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
walk(repoRoot, 0);
|
|
193
|
+
return { typescript, python };
|
|
194
|
+
}
|
|
195
|
+
|
|
156
196
|
function runScipRust(projectRoot) {
|
|
157
197
|
// rust-analyzer + scip: run `scip` (the rust-analyzer scip bridge) if available
|
|
158
198
|
// As with TypeScript, Phase-1 is tier-labelling only
|
|
@@ -215,32 +255,45 @@ function buildScipResolver(repoRoot, opts = {}) {
|
|
|
215
255
|
const { readScipIndex } = require('./gsd-t-scip-reader.cjs');
|
|
216
256
|
const avail = detectScip();
|
|
217
257
|
|
|
218
|
-
//
|
|
219
|
-
//
|
|
220
|
-
|
|
221
|
-
|
|
258
|
+
// Detect which languages have source present (so we only run the relevant
|
|
259
|
+
// indexer). Cheap shallow check: does the repo contain .ts/.tsx/.js or .py?
|
|
260
|
+
const langs = detectRepoLanguages(repoRoot);
|
|
261
|
+
|
|
262
|
+
// Run each available+relevant indexer, merge their resolution maps. The reader
|
|
263
|
+
// is language-agnostic (Python symbols use the same `name().` descriptor form),
|
|
264
|
+
// so TS and Python refs merge into one fileRefs map keyed by repo-relative path.
|
|
265
|
+
const fileRefs = new Map(); // relPath → [{symbol, funcId, line}]
|
|
266
|
+
const ranIndexers = [];
|
|
267
|
+
|
|
268
|
+
function mergeRead(read) {
|
|
269
|
+
if (!read || !read.ok) return;
|
|
270
|
+
for (const [file, refs] of read.fileRefs) {
|
|
271
|
+
if (fileRefs.has(file)) fileRefs.get(file).push(...refs);
|
|
272
|
+
else fileRefs.set(file, refs.slice());
|
|
273
|
+
}
|
|
222
274
|
}
|
|
223
275
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
return { ok: false, reason: `scip-run-threw: ${e.message}`, resolveFileEdges: passthroughResolver };
|
|
276
|
+
if (avail.typescript && langs.typescript) {
|
|
277
|
+
try {
|
|
278
|
+
const run = runScipTypescript(repoRoot, path.join(repoRoot, '.gsd-t', 'index.scip'));
|
|
279
|
+
if (run && run.ok) { mergeRead(readScipIndex(run.scipPath)); ranIndexers.push('typescript'); }
|
|
280
|
+
} catch { /* degrade to floor for TS */ }
|
|
230
281
|
}
|
|
231
|
-
if (
|
|
232
|
-
|
|
282
|
+
if (avail.python && langs.python) {
|
|
283
|
+
try {
|
|
284
|
+
const run = runScipPython(repoRoot, path.join(repoRoot, '.gsd-t', 'index-python.scip'));
|
|
285
|
+
if (run && run.ok) { mergeRead(readScipIndex(run.scipPath)); ranIndexers.push('python'); }
|
|
286
|
+
} catch { /* degrade to floor for Python */ }
|
|
233
287
|
}
|
|
234
288
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
289
|
+
if (!ranIndexers.length) {
|
|
290
|
+
// No applicable indexer present/ran → floor mode.
|
|
291
|
+
const reason = (!avail.typescript && !avail.python) ? 'scip-indexers-absent'
|
|
292
|
+
: (!langs.typescript && !langs.python) ? 'no-indexable-source'
|
|
293
|
+
: 'scip-run-failed';
|
|
294
|
+
return { ok: false, reason, resolveFileEdges: passthroughResolver };
|
|
238
295
|
}
|
|
239
296
|
|
|
240
|
-
// Build a fast per-file callee lookup: for a given test/impl file, the set of
|
|
241
|
-
// funcIds it references (resolved). Used to rewrite UNRESOLVED# call edges.
|
|
242
|
-
const fileRefs = read.fileRefs; // relPath → [{symbol, funcId, line}]
|
|
243
|
-
|
|
244
297
|
/**
|
|
245
298
|
* Resolve a single file's call edges. For each CALL edge whose dst is
|
|
246
299
|
* UNRESOLVED#<name>, if SCIP found a reference to <name> in this file that
|
|
@@ -273,7 +326,7 @@ function buildScipResolver(repoRoot, opts = {}) {
|
|
|
273
326
|
return { edges: out, resolved };
|
|
274
327
|
}
|
|
275
328
|
|
|
276
|
-
return { ok: true, scipPath:
|
|
329
|
+
return { ok: true, indexers: ranIndexers, scipPath: path.join(repoRoot, '.gsd-t', 'index.scip'), resolveFileEdges };
|
|
277
330
|
}
|
|
278
331
|
|
|
279
332
|
/** No-op resolver used when SCIP is unavailable (floor mode). */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tekyzinc/gsd-t",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.12.10",
|
|
4
4
|
"description": "GSD-T: Contract-Driven Development for Claude Code — 54 slash commands with headless-by-default workflow spawning, unattended supervisor relay with event stream, graph-powered code analysis, real-time agent dashboard, task telemetry, doc-ripple enforcement, backlog management, impact analysis, test sync, milestone archival, and PRD generation",
|
|
5
5
|
"author": "Tekyz, Inc.",
|
|
6
6
|
"license": "MIT",
|