@tekyzinc/gsd-t 3.13.12 → 3.13.14
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 +40 -0
- package/bin/gsd-t-unattended.cjs +1 -1
- package/bin/gsd-t.js +44 -29
- package/bin/headless-exit-codes.cjs +49 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,46 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to GSD-T are documented here. Updated with each release.
|
|
4
4
|
|
|
5
|
+
## [3.13.14] - 2026-04-17
|
|
6
|
+
|
|
7
|
+
### Fixed — Supervisor no longer requires project-local `bin/gsd-t.js` + sweep self-protection
|
|
8
|
+
|
|
9
|
+
v3.13.13 successfully swept bee-poc's stray `bin/gsd-t.js`, but exposed a second bug: `bin/gsd-t-unattended.cjs:31` still did `require("./gsd-t.js")` to pull in the `mapHeadlessExitCode` helper. With the stray removed, projects could no longer load the supervisor at all — the require chain now failed on `gsd-t.js` itself instead of `debug-ledger.js`. Three options were on the table (restore both files, resolve from global, or remove all stale copies); we picked the middle path via file extraction.
|
|
10
|
+
|
|
11
|
+
**Fix 1 — extract `mapHeadlessExitCode` to its own file** (`bin/headless-exit-codes.cjs`, new):
|
|
12
|
+
The exit-code contract helper (0=success, 1=verify fail, 2=context budget, 3=non-zero exit, 4=blocked, 5=unknown command) is now a standalone zero-dependency module. `bin/gsd-t-unattended.cjs:31` now does `require("./headless-exit-codes.cjs")` — no transitive dependency on the full CLI installer. `bin/gsd-t.js` still re-exports `mapHeadlessExitCode` for backward compatibility (top-of-file require + `module.exports`).
|
|
13
|
+
|
|
14
|
+
**Fix 2 — `headless-exit-codes.cjs` joins `PROJECT_BIN_TOOLS`**: `copyBinToolsToProject` now copies the new helper into every registered project's `bin/` on the next `update-all`, so the supervisor can load it locally without reaching into the global package.
|
|
15
|
+
|
|
16
|
+
**Fix 3 — sweep self-protection** (`copyBinToolsToProject`):
|
|
17
|
+
While dogfooding v3.13.13, the sweep ran against GSD-T's own source repo (which is registered as a project for eating-our-own-dogfood purposes), recognized the source `bin/gsd-t.js` as matching the installer signature (it IS the installer), and deleted it. The source was restored via `git restore`, but the sweep now carries a guard: if `realpathSync(projectBinDir) === realpathSync(PKG_ROOT/bin)`, skip the sweep entirely. Dogfooding the installer on itself no longer cannibalizes the source.
|
|
18
|
+
|
|
19
|
+
**Files**:
|
|
20
|
+
- `bin/headless-exit-codes.cjs` — new file, 50 lines, extracted helper with explanatory header.
|
|
21
|
+
- `bin/gsd-t-unattended.cjs` — line 31 now requires the new helper instead of `./gsd-t.js`.
|
|
22
|
+
- `bin/gsd-t.js` — top-of-file re-export of `mapHeadlessExitCode` (backward compat); original declaration replaced with a comment pointing to the extracted module; `PROJECT_BIN_TOOLS` gains `headless-exit-codes.cjs`; sweep logic gains the realpath equality guard.
|
|
23
|
+
- `test/bin-gsd-t-resilience.test.js` — new test: `copyBinToolsToProject refuses to sweep the source package's own bin/` (run sweep with `projectDir = PKG_ROOT`, assert `bin/gsd-t.js` still exists after).
|
|
24
|
+
|
|
25
|
+
**Tests**: 1240/1240 pass (+1 new self-protection test). E2E: N/A.
|
|
26
|
+
|
|
27
|
+
**Impact**: bee-poc's supervisor can now load after `gsd-t update-all` copies the new `headless-exit-codes.cjs` helper into `bin/`. No project needs a local `bin/gsd-t.js` for the supervisor to function. Running the installer against its own source repo no longer destroys the source.
|
|
28
|
+
|
|
29
|
+
## [3.13.13] - 2026-04-17
|
|
30
|
+
|
|
31
|
+
### Fixed — Stray sweep now matches older-version installer artifacts
|
|
32
|
+
|
|
33
|
+
v3.13.12 shipped the defensive require + DEPRECATED_BIN_STRAYS sweep, but the sweep's safety check was too narrow: it only deleted strays whose bytes matched the **current** source. Projects left behind with a v3.13.11 (or earlier) `bin/gsd-t.js` would not match the current source (different body), so the sweep refused to delete them and those projects stayed crashed.
|
|
34
|
+
|
|
35
|
+
**Fix**: sweep now uses a **signature** check rather than a byte-identity check. A stray is deleted when it starts with `#!/usr/bin/env node` AND contains the verbatim JSDoc header `GSD-T CLI Installer` in the first 400 characters. That combination is unique enough to rule out user-owned files (a user's own script would not contain our header) while matching every historical version of this installer — so older-version artifacts are swept correctly.
|
|
36
|
+
|
|
37
|
+
**Files**:
|
|
38
|
+
- `bin/gsd-t.js` — sweep loop now uses signature match instead of byte-match.
|
|
39
|
+
- `test/bin-gsd-t-resilience.test.js` — new test case covering older-version stray (shebang + header + different body) → must be deleted. Existing byte-match test kept. Existing user-owned test kept.
|
|
40
|
+
|
|
41
|
+
**Tests**: 1239/1239 pass (+1 new assertion vs v3.13.12). E2E: N/A.
|
|
42
|
+
|
|
43
|
+
**Impact**: bee-poc and any other project carrying a pre-v3.13.12 `bin/gsd-t.js` now self-heals on the next `gsd-t update-all` pass after installing v3.13.13.
|
|
44
|
+
|
|
5
45
|
## [3.13.12] - 2026-04-17
|
|
6
46
|
|
|
7
47
|
### Fixed — Project-local `bin/gsd-t.js` crash on missing `debug-ledger.js` + self-heal sweep
|
package/bin/gsd-t-unattended.cjs
CHANGED
|
@@ -28,7 +28,7 @@ const fs = require("fs");
|
|
|
28
28
|
const path = require("path");
|
|
29
29
|
const os = require("os");
|
|
30
30
|
const { execSync, spawnSync } = require("child_process");
|
|
31
|
-
const { mapHeadlessExitCode } = require("./
|
|
31
|
+
const { mapHeadlessExitCode } = require("./headless-exit-codes.cjs");
|
|
32
32
|
|
|
33
33
|
// Safety rails (m36-safety-rails) — pure-function checks for pre-launch,
|
|
34
34
|
// supervisor-init, pre-worker, and post-worker hook points per contract §12.
|
package/bin/gsd-t.js
CHANGED
|
@@ -34,6 +34,12 @@ try {
|
|
|
34
34
|
};
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
// Shared headless exit-code helper — lives in its own file so non-entry
|
|
38
|
+
// modules (e.g. bin/gsd-t-unattended.cjs) can require it without pulling
|
|
39
|
+
// in the full CLI. See commentary near the original declaration site and
|
|
40
|
+
// in headless-exit-codes.cjs itself.
|
|
41
|
+
const { mapHeadlessExitCode } = require(path.join(__dirname, "headless-exit-codes.cjs"));
|
|
42
|
+
|
|
37
43
|
// ─── Configuration ───────────────────────────────────────────────────────────
|
|
38
44
|
|
|
39
45
|
const CLAUDE_DIR = path.join(os.homedir(), ".claude");
|
|
@@ -2110,6 +2116,7 @@ const PROJECT_BIN_TOOLS = [
|
|
|
2110
2116
|
"context-meter-config.cjs", "token-budget.cjs",
|
|
2111
2117
|
"gsd-t-unattended.cjs", "gsd-t-unattended-platform.cjs", "gsd-t-unattended-safety.cjs",
|
|
2112
2118
|
"handoff-lock.cjs", "headless-auto-spawn.cjs",
|
|
2119
|
+
"headless-exit-codes.cjs",
|
|
2113
2120
|
];
|
|
2114
2121
|
|
|
2115
2122
|
// Files that older versions of this installer copied into project bin/ but
|
|
@@ -2154,21 +2161,41 @@ function copyBinToolsToProject(projectDir, projectName) {
|
|
|
2154
2161
|
}
|
|
2155
2162
|
}
|
|
2156
2163
|
}
|
|
2164
|
+
// Self-protection: NEVER sweep the package's own bin/. Without this guard,
|
|
2165
|
+
// running `gsd-t update-all` with the GSD-T source repo itself registered
|
|
2166
|
+
// as a project (legitimate during development) would signature-match
|
|
2167
|
+
// bin/gsd-t.js — which IS the installer — and delete the source file.
|
|
2168
|
+
// Resolve both paths to handle symlinks / relative path quirks.
|
|
2169
|
+
const resolvedProjectBin = fs.realpathSync.native
|
|
2170
|
+
? (() => { try { return fs.realpathSync(projectBinDir); } catch { return projectBinDir; } })()
|
|
2171
|
+
: projectBinDir;
|
|
2172
|
+
const resolvedPkgBin = (() => {
|
|
2173
|
+
try { return fs.realpathSync(path.join(PKG_ROOT, "bin")); } catch { return path.join(PKG_ROOT, "bin"); }
|
|
2174
|
+
})();
|
|
2175
|
+
const isSourcePackage = resolvedProjectBin === resolvedPkgBin;
|
|
2157
2176
|
let cleaned = 0;
|
|
2158
|
-
|
|
2159
|
-
const
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2177
|
+
if (!isSourcePackage) {
|
|
2178
|
+
for (const stray of DEPRECATED_BIN_STRAYS) {
|
|
2179
|
+
const strayPath = path.join(projectBinDir, stray);
|
|
2180
|
+
if (!fs.existsSync(strayPath)) continue;
|
|
2181
|
+
try {
|
|
2182
|
+
const strayContent = fs.readFileSync(strayPath, "utf8");
|
|
2183
|
+
const head = strayContent.slice(0, 400);
|
|
2184
|
+
// Signature-match any version this installer ever shipped. The marker
|
|
2185
|
+
// is unique enough to rule out user-owned files: node shebang + the
|
|
2186
|
+
// verbatim JSDoc header "GSD-T CLI Installer". Matches every historical
|
|
2187
|
+
// version of bin/gsd-t.js, not just the current one, so older strays
|
|
2188
|
+
// (e.g. v3.13.11 left behind on v3.13.12+) are still swept.
|
|
2189
|
+
const isOurs =
|
|
2190
|
+
head.startsWith("#!/usr/bin/env node") &&
|
|
2191
|
+
head.includes("GSD-T CLI Installer");
|
|
2192
|
+
if (isOurs) {
|
|
2193
|
+
fs.unlinkSync(strayPath);
|
|
2194
|
+
cleaned++;
|
|
2195
|
+
}
|
|
2196
|
+
} catch {
|
|
2197
|
+
// leave the file alone on any read error
|
|
2169
2198
|
}
|
|
2170
|
-
} catch {
|
|
2171
|
-
// leave the file alone on any read error
|
|
2172
2199
|
}
|
|
2173
2200
|
}
|
|
2174
2201
|
if (cleaned > 0) {
|
|
@@ -2846,22 +2873,10 @@ function buildHeadlessCmd(command, cmdArgs) {
|
|
|
2846
2873
|
* Exit codes: 0=success, 1=verify-fail, 2=context-budget-exceeded, 3=error,
|
|
2847
2874
|
* 4=blocked-needs-human, 5=command-dispatch-failed
|
|
2848
2875
|
*/
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
// Command dispatch failure — `claude -p` prints "Unknown command: /X"
|
|
2854
|
-
// to stdout and still exits 0. Without this sentinel, a mistyped or
|
|
2855
|
-
// namespace-prefixed slash command silently reports success. (M36 Phase 0.)
|
|
2856
|
-
if (/^unknown command:/im.test(raw)) return 5;
|
|
2857
|
-
if (lower.includes("context budget exceeded") || lower.includes("context window exceeded") ||
|
|
2858
|
-
lower.includes("budget exceeded") || lower.includes("token limit")) return 2;
|
|
2859
|
-
if (lower.includes("blocked") && (lower.includes("needs human") || lower.includes("need human") ||
|
|
2860
|
-
lower.includes("human input") || lower.includes("human approval"))) return 4;
|
|
2861
|
-
if (lower.includes("verification failed") || lower.includes("verify failed") ||
|
|
2862
|
-
lower.includes("quality gate failed") || lower.includes("tests failed")) return 1;
|
|
2863
|
-
return 0;
|
|
2864
|
-
}
|
|
2876
|
+
// mapHeadlessExitCode now lives in ./headless-exit-codes.cjs — see re-export
|
|
2877
|
+
// near the top of this file. Kept out-of-line here so non-entry modules
|
|
2878
|
+
// (e.g. bin/gsd-t-unattended.cjs) can require the shared helper without
|
|
2879
|
+
// pulling in the full CLI at bin/gsd-t.js.
|
|
2865
2880
|
|
|
2866
2881
|
/**
|
|
2867
2882
|
* Generate a headless log file path.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared helper: map a `claude -p` process exit code + output to the
|
|
3
|
+
* GSD-T headless exit-code contract.
|
|
4
|
+
*
|
|
5
|
+
* Lives in its own file so non-entry modules (e.g. gsd-t-unattended.cjs)
|
|
6
|
+
* can require it without pulling in the full CLI at bin/gsd-t.js. That
|
|
7
|
+
* decoupling lets PROJECT_BIN_TOOLS ship the supervisor without also
|
|
8
|
+
* vendoring the CLI itself — projects resolve `gsd-t` from the global
|
|
9
|
+
* install, not from a project-local copy.
|
|
10
|
+
*
|
|
11
|
+
* Exit codes (contract):
|
|
12
|
+
* 0 — success
|
|
13
|
+
* 1 — verification/test failure
|
|
14
|
+
* 2 — context budget exceeded
|
|
15
|
+
* 3 — non-zero process exit (other)
|
|
16
|
+
* 4 — blocked / needs human
|
|
17
|
+
* 5 — unknown slash command (claude -p exited 0 but rejected the prompt)
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
"use strict";
|
|
21
|
+
|
|
22
|
+
function mapHeadlessExitCode(processExitCode, output) {
|
|
23
|
+
if (processExitCode !== 0 && processExitCode !== null) return 3;
|
|
24
|
+
const raw = output || "";
|
|
25
|
+
const lower = raw.toLowerCase();
|
|
26
|
+
if (/^unknown command:/im.test(raw)) return 5;
|
|
27
|
+
if (
|
|
28
|
+
lower.includes("context budget exceeded") ||
|
|
29
|
+
lower.includes("context window exceeded") ||
|
|
30
|
+
lower.includes("budget exceeded") ||
|
|
31
|
+
lower.includes("token limit")
|
|
32
|
+
) return 2;
|
|
33
|
+
if (
|
|
34
|
+
lower.includes("blocked") &&
|
|
35
|
+
(lower.includes("needs human") ||
|
|
36
|
+
lower.includes("need human") ||
|
|
37
|
+
lower.includes("human input") ||
|
|
38
|
+
lower.includes("human approval"))
|
|
39
|
+
) return 4;
|
|
40
|
+
if (
|
|
41
|
+
lower.includes("verification failed") ||
|
|
42
|
+
lower.includes("verify failed") ||
|
|
43
|
+
lower.includes("quality gate failed") ||
|
|
44
|
+
lower.includes("tests failed")
|
|
45
|
+
) return 1;
|
|
46
|
+
return 0;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
module.exports = { mapHeadlessExitCode };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tekyzinc/gsd-t",
|
|
3
|
-
"version": "3.13.
|
|
3
|
+
"version": "3.13.14",
|
|
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",
|