@tekyzinc/gsd-t 4.11.11 → 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 +12 -0
- package/README.md +1 -1
- package/bin/gsd-t-graph-scip-upgrade.cjs +74 -24
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
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
|
+
|
|
5
17
|
## [4.11.11] - 2026-06-27
|
|
6
18
|
|
|
7
19
|
### Fixed — SCIP resolution missed nested/monorepo tsconfig (call-graph empty on subdir-tsconfig projects)
|
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.
|
|
@@ -146,16 +146,53 @@ function runScipTypescript(projectRoot, outPath) {
|
|
|
146
146
|
return { ok: false, error: scip.stderr || `exit code ${scip.status}` };
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
-
function runScipPython(projectRoot) {
|
|
150
|
-
|
|
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, '.'], {
|
|
151
158
|
cwd: projectRoot,
|
|
152
159
|
encoding: 'utf8',
|
|
153
|
-
timeout:
|
|
160
|
+
timeout: 180_000,
|
|
154
161
|
});
|
|
155
|
-
if (scip.status === 0) return { ok: true };
|
|
162
|
+
if (scip.status === 0) return { ok: true, scipPath: out };
|
|
156
163
|
return { ok: false, error: scip.stderr || `exit code ${scip.status}` };
|
|
157
164
|
}
|
|
158
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
|
+
|
|
159
196
|
function runScipRust(projectRoot) {
|
|
160
197
|
// rust-analyzer + scip: run `scip` (the rust-analyzer scip bridge) if available
|
|
161
198
|
// As with TypeScript, Phase-1 is tier-labelling only
|
|
@@ -218,32 +255,45 @@ function buildScipResolver(repoRoot, opts = {}) {
|
|
|
218
255
|
const { readScipIndex } = require('./gsd-t-scip-reader.cjs');
|
|
219
256
|
const avail = detectScip();
|
|
220
257
|
|
|
221
|
-
//
|
|
222
|
-
//
|
|
223
|
-
|
|
224
|
-
|
|
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
|
+
}
|
|
225
274
|
}
|
|
226
275
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
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 */ }
|
|
233
281
|
}
|
|
234
|
-
if (
|
|
235
|
-
|
|
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 */ }
|
|
236
287
|
}
|
|
237
288
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
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 };
|
|
241
295
|
}
|
|
242
296
|
|
|
243
|
-
// Build a fast per-file callee lookup: for a given test/impl file, the set of
|
|
244
|
-
// funcIds it references (resolved). Used to rewrite UNRESOLVED# call edges.
|
|
245
|
-
const fileRefs = read.fileRefs; // relPath → [{symbol, funcId, line}]
|
|
246
|
-
|
|
247
297
|
/**
|
|
248
298
|
* Resolve a single file's call edges. For each CALL edge whose dst is
|
|
249
299
|
* UNRESOLVED#<name>, if SCIP found a reference to <name> in this file that
|
|
@@ -276,7 +326,7 @@ function buildScipResolver(repoRoot, opts = {}) {
|
|
|
276
326
|
return { edges: out, resolved };
|
|
277
327
|
}
|
|
278
328
|
|
|
279
|
-
return { ok: true, scipPath:
|
|
329
|
+
return { ok: true, indexers: ranIndexers, scipPath: path.join(repoRoot, '.gsd-t', 'index.scip'), resolveFileEdges };
|
|
280
330
|
}
|
|
281
331
|
|
|
282
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",
|