@tekyzinc/gsd-t 2.20.5 → 2.20.7
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 +19 -0
- package/bin/gsd-t.js +1309 -1163
- package/commands/Claude-md.md +2 -0
- package/commands/gsd-t-execute.md +15 -2
- package/commands/gsd-t-scan.md +16 -0
- package/commands/gsd-t-test-sync.md +11 -0
- package/commands/gsd-t-verify.md +11 -0
- package/package.json +39 -35
- package/scripts/gsd-t-heartbeat.js +198 -156
package/commands/Claude-md.md
CHANGED
|
@@ -44,8 +44,8 @@ For each task:
|
|
|
44
44
|
8. **Run ALL tests** — unit, integration, and full Playwright suite. Fix any failures before proceeding (up to 2 attempts)
|
|
45
45
|
9. Run the Pre-Commit Gate checklist from CLAUDE.md — update ALL affected docs BEFORE committing
|
|
46
46
|
10. Commit with a descriptive message: `[{domain}] Task {N}: {description}`
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
11. Update `.gsd-t/progress.md` — mark task complete
|
|
48
|
+
12. If you've reached a CHECKPOINT in integration-points.md, pause and verify the contract before continuing
|
|
49
49
|
|
|
50
50
|
### Team Mode (when agent teams are enabled)
|
|
51
51
|
Spawn teammates for independent domains:
|
|
@@ -143,4 +143,17 @@ When all tasks in all domains are complete:
|
|
|
143
143
|
|
|
144
144
|
**Level 1–2**: Report completion summary and recommend proceeding to integrate phase. Wait for confirmation.
|
|
145
145
|
|
|
146
|
+
## Document Ripple
|
|
147
|
+
|
|
148
|
+
Execute modifies source code, so the Pre-Commit Gate (referenced in Step 9) covers document updates. For clarity, the key documents affected by execution:
|
|
149
|
+
|
|
150
|
+
### Always update:
|
|
151
|
+
1. **`.gsd-t/progress.md`** — Mark tasks complete, update domain status, log execution summary
|
|
152
|
+
|
|
153
|
+
### Check if affected (per task):
|
|
154
|
+
2. **`.gsd-t/contracts/`** — Did a task change an API endpoint, schema, or component interface? Update the contract
|
|
155
|
+
3. **`docs/requirements.md`** — Did a task implement or change a requirement? Mark it complete or revise
|
|
156
|
+
4. **`docs/architecture.md`** — Did a task add/change a component or data flow? Update it
|
|
157
|
+
5. **`.gsd-t/techdebt.md`** — Did a task resolve a debt item? Mark it done. Did it reveal new debt? Add it
|
|
158
|
+
|
|
146
159
|
$ARGUMENTS
|
package/commands/gsd-t-scan.md
CHANGED
|
@@ -401,4 +401,20 @@ All detailed findings are in `.gsd-t/scan/` for review.
|
|
|
401
401
|
|
|
402
402
|
Ask: "Want to promote any tech debt items to milestones? Or address the critical items first?"
|
|
403
403
|
|
|
404
|
+
## Document Ripple
|
|
405
|
+
|
|
406
|
+
Scan produces analysis files and updates living documents (Step 5 already covers most updates). Verify:
|
|
407
|
+
|
|
408
|
+
### Always update:
|
|
409
|
+
1. **`.gsd-t/progress.md`** — Log scan completion with summary stats in Decision Log
|
|
410
|
+
2. **`docs/architecture.md`** — Merge scan findings (Step 5)
|
|
411
|
+
3. **`docs/workflows.md`** — Merge business rules findings (Step 5)
|
|
412
|
+
4. **`docs/infrastructure.md`** — Merge operational findings (Step 5)
|
|
413
|
+
5. **`docs/requirements.md`** — Merge discovered requirements (Step 5)
|
|
414
|
+
6. **`README.md`** — Update tech stack and setup if needed (Step 5)
|
|
415
|
+
|
|
416
|
+
### Check if affected:
|
|
417
|
+
7. **`.gsd-t/techdebt.md`** — Created/updated with all findings (Step 3)
|
|
418
|
+
8. **`CLAUDE.md`** — If new conventions or patterns were discovered, suggest additions
|
|
419
|
+
|
|
404
420
|
$ARGUMENTS
|
|
@@ -317,4 +317,15 @@ Generated 5 test tasks → added to current domain
|
|
|
317
317
|
|
|
318
318
|
**Level 1–2**: Present the full report and wait for user input before proceeding.
|
|
319
319
|
|
|
320
|
+
## Document Ripple
|
|
321
|
+
|
|
322
|
+
### Always update:
|
|
323
|
+
1. **`.gsd-t/progress.md`** — Log test sync results in Decision Log (standalone mode)
|
|
324
|
+
2. **`.gsd-t/test-coverage.md`** — Created/updated with coverage report (Step 5)
|
|
325
|
+
|
|
326
|
+
### Check if affected:
|
|
327
|
+
3. **`docs/requirements.md`** — If test tasks map to requirements, update the Test Coverage table
|
|
328
|
+
4. **`.gsd-t/domains/{current}/tasks.md`** — If test tasks were generated, append them (Step 6)
|
|
329
|
+
5. **`.gsd-t/techdebt.md`** — If persistent test gaps were found, add as debt items
|
|
330
|
+
|
|
320
331
|
$ARGUMENTS
|
package/commands/gsd-t-verify.md
CHANGED
|
@@ -157,4 +157,15 @@ Update `.gsd-t/progress.md`:
|
|
|
157
157
|
- CONDITIONAL PASS → User decides if warnings are acceptable
|
|
158
158
|
- FAIL → Return to execute phase for remediation tasks
|
|
159
159
|
|
|
160
|
+
## Document Ripple
|
|
161
|
+
|
|
162
|
+
### Always update:
|
|
163
|
+
1. **`.gsd-t/progress.md`** — Set status to VERIFIED/VERIFY-FAILED, log verification summary
|
|
164
|
+
2. **`.gsd-t/verify-report.md`** — Created with full verification results (Step 4)
|
|
165
|
+
|
|
166
|
+
### Check if affected:
|
|
167
|
+
3. **`.gsd-t/domains/{domain}/tasks.md`** — If remediation tasks were created (Step 5)
|
|
168
|
+
4. **`.gsd-t/techdebt.md`** — If verification found new quality or security issues, add as debt
|
|
169
|
+
5. **`docs/requirements.md`** — If verification revealed unmet requirements, update status
|
|
170
|
+
|
|
160
171
|
$ARGUMENTS
|
package/package.json
CHANGED
|
@@ -1,35 +1,39 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@tekyzinc/gsd-t",
|
|
3
|
-
"version": "2.20.
|
|
4
|
-
"description": "GSD-T: Contract-Driven Development for Claude Code — 41 slash commands with backlog management, impact analysis, test sync, and milestone archival",
|
|
5
|
-
"author": "Tekyz, Inc.",
|
|
6
|
-
"license": "MIT",
|
|
7
|
-
"repository": {
|
|
8
|
-
"type": "git",
|
|
9
|
-
"url": "git+https://github.com/Tekyz-Inc/get-stuff-done-teams.git"
|
|
10
|
-
},
|
|
11
|
-
"homepage": "https://github.com/Tekyz-Inc/get-stuff-done-teams#readme",
|
|
12
|
-
"keywords": [
|
|
13
|
-
"claude-code",
|
|
14
|
-
"gsd",
|
|
15
|
-
"ai-development",
|
|
16
|
-
"agent-teams",
|
|
17
|
-
"contract-driven-development",
|
|
18
|
-
"slash-commands"
|
|
19
|
-
],
|
|
20
|
-
"
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
"
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
"
|
|
34
|
-
|
|
35
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "@tekyzinc/gsd-t",
|
|
3
|
+
"version": "2.20.7",
|
|
4
|
+
"description": "GSD-T: Contract-Driven Development for Claude Code — 41 slash commands with backlog management, impact analysis, test sync, and milestone archival",
|
|
5
|
+
"author": "Tekyz, Inc.",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/Tekyz-Inc/get-stuff-done-teams.git"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/Tekyz-Inc/get-stuff-done-teams#readme",
|
|
12
|
+
"keywords": [
|
|
13
|
+
"claude-code",
|
|
14
|
+
"gsd",
|
|
15
|
+
"ai-development",
|
|
16
|
+
"agent-teams",
|
|
17
|
+
"contract-driven-development",
|
|
18
|
+
"slash-commands"
|
|
19
|
+
],
|
|
20
|
+
"main": "bin/gsd-t.js",
|
|
21
|
+
"bin": {
|
|
22
|
+
"gsd-t": "bin/gsd-t.js"
|
|
23
|
+
},
|
|
24
|
+
"scripts": {
|
|
25
|
+
"test": "node --test"
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"bin/",
|
|
29
|
+
"commands/",
|
|
30
|
+
"scripts/",
|
|
31
|
+
"templates/",
|
|
32
|
+
"examples/",
|
|
33
|
+
"docs/",
|
|
34
|
+
"CHANGELOG.md"
|
|
35
|
+
],
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=16.0.0"
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -1,156 +1,198 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* GSD-T Heartbeat — Claude Code Hook Event Writer
|
|
5
|
-
*
|
|
6
|
-
* Writes structured events to .gsd-t/heartbeat-{session_id}.jsonl
|
|
7
|
-
* Installed as an async hook for multiple Claude Code events.
|
|
8
|
-
*
|
|
9
|
-
* Events captured:
|
|
10
|
-
* SessionStart, PostToolUse, SubagentStart, SubagentStop,
|
|
11
|
-
* TaskCompleted, TeammateIdle, Notification, Stop, SessionEnd
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
const fs = require("fs");
|
|
15
|
-
const path = require("path");
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
sid
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
case "
|
|
133
|
-
return {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* GSD-T Heartbeat — Claude Code Hook Event Writer
|
|
5
|
+
*
|
|
6
|
+
* Writes structured events to .gsd-t/heartbeat-{session_id}.jsonl
|
|
7
|
+
* Installed as an async hook for multiple Claude Code events.
|
|
8
|
+
*
|
|
9
|
+
* Events captured:
|
|
10
|
+
* SessionStart, PostToolUse, SubagentStart, SubagentStop,
|
|
11
|
+
* TaskCompleted, TeammateIdle, Notification, Stop, SessionEnd
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const fs = require("fs");
|
|
15
|
+
const path = require("path");
|
|
16
|
+
|
|
17
|
+
const MAX_STDIN = 1024 * 1024; // 1MB — prevent OOM from unbounded input
|
|
18
|
+
const SAFE_SID = /^[a-zA-Z0-9_-]+$/; // Allowlist for session_id — blocks path traversal
|
|
19
|
+
const MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000; // 7 days — auto-cleanup threshold
|
|
20
|
+
|
|
21
|
+
let input = "";
|
|
22
|
+
let aborted = false;
|
|
23
|
+
process.stdin.setEncoding("utf8");
|
|
24
|
+
process.stdin.on("data", (d) => {
|
|
25
|
+
input += d;
|
|
26
|
+
if (input.length > MAX_STDIN) {
|
|
27
|
+
aborted = true;
|
|
28
|
+
process.stdin.destroy();
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
process.stdin.on("end", () => {
|
|
32
|
+
if (aborted) return; // Silently discard oversized input
|
|
33
|
+
try {
|
|
34
|
+
const hook = JSON.parse(input);
|
|
35
|
+
const dir = hook.cwd || process.cwd();
|
|
36
|
+
|
|
37
|
+
// Validate cwd is absolute path
|
|
38
|
+
if (!path.isAbsolute(dir)) return;
|
|
39
|
+
|
|
40
|
+
const gsdtDir = path.join(dir, ".gsd-t");
|
|
41
|
+
if (!fs.existsSync(gsdtDir)) return;
|
|
42
|
+
|
|
43
|
+
const sid = hook.session_id || "unknown";
|
|
44
|
+
|
|
45
|
+
// Validate session_id — block path traversal (e.g., "../../etc/evil")
|
|
46
|
+
if (!SAFE_SID.test(sid)) return;
|
|
47
|
+
|
|
48
|
+
const file = path.join(gsdtDir, `heartbeat-${sid}.jsonl`);
|
|
49
|
+
|
|
50
|
+
// Verify resolved path is still within .gsd-t/ directory
|
|
51
|
+
const resolvedFile = path.resolve(file);
|
|
52
|
+
const resolvedDir = path.resolve(gsdtDir);
|
|
53
|
+
if (!resolvedFile.startsWith(resolvedDir + path.sep)) return;
|
|
54
|
+
|
|
55
|
+
const event = buildEvent(hook);
|
|
56
|
+
if (event) {
|
|
57
|
+
cleanupOldHeartbeats(gsdtDir);
|
|
58
|
+
fs.appendFileSync(file, JSON.stringify(event) + "\n");
|
|
59
|
+
}
|
|
60
|
+
} catch (e) {
|
|
61
|
+
// Silent failure — never interfere with Claude Code
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
function cleanupOldHeartbeats(gsdtDir) {
|
|
66
|
+
try {
|
|
67
|
+
const files = fs.readdirSync(gsdtDir);
|
|
68
|
+
const now = Date.now();
|
|
69
|
+
for (const f of files) {
|
|
70
|
+
if (!f.startsWith("heartbeat-") || !f.endsWith(".jsonl")) continue;
|
|
71
|
+
const fp = path.join(gsdtDir, f);
|
|
72
|
+
const stat = fs.statSync(fp);
|
|
73
|
+
if (now - stat.mtimeMs > MAX_AGE_MS) {
|
|
74
|
+
fs.unlinkSync(fp);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
} catch {
|
|
78
|
+
// Silent failure — never interfere with Claude Code
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function buildEvent(hook) {
|
|
83
|
+
const base = {
|
|
84
|
+
ts: new Date().toISOString(),
|
|
85
|
+
sid: hook.session_id,
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
switch (hook.hook_event_name) {
|
|
89
|
+
case "SessionStart":
|
|
90
|
+
return {
|
|
91
|
+
...base,
|
|
92
|
+
evt: "session_start",
|
|
93
|
+
data: { source: hook.source, model: hook.model },
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
case "PostToolUse":
|
|
97
|
+
return {
|
|
98
|
+
...base,
|
|
99
|
+
evt: "tool",
|
|
100
|
+
tool: hook.tool_name,
|
|
101
|
+
data: summarize(hook.tool_name, hook.tool_input),
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
case "SubagentStart":
|
|
105
|
+
return {
|
|
106
|
+
...base,
|
|
107
|
+
evt: "agent_spawn",
|
|
108
|
+
data: { agent_id: hook.agent_id, agent_type: hook.agent_type },
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
case "SubagentStop":
|
|
112
|
+
return {
|
|
113
|
+
...base,
|
|
114
|
+
evt: "agent_stop",
|
|
115
|
+
data: { agent_id: hook.agent_id, agent_type: hook.agent_type },
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
case "TaskCompleted":
|
|
119
|
+
return {
|
|
120
|
+
...base,
|
|
121
|
+
evt: "task_done",
|
|
122
|
+
data: { task: hook.task_subject, agent: hook.teammate_name },
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
case "TeammateIdle":
|
|
126
|
+
return {
|
|
127
|
+
...base,
|
|
128
|
+
evt: "agent_idle",
|
|
129
|
+
data: { agent: hook.teammate_name, team: hook.team_name },
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
case "Notification":
|
|
133
|
+
return {
|
|
134
|
+
...base,
|
|
135
|
+
evt: "notification",
|
|
136
|
+
data: { message: hook.message, title: hook.title },
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
case "Stop":
|
|
140
|
+
return { ...base, evt: "session_stop" };
|
|
141
|
+
|
|
142
|
+
case "SessionEnd":
|
|
143
|
+
return {
|
|
144
|
+
...base,
|
|
145
|
+
evt: "session_end",
|
|
146
|
+
data: { reason: hook.reason },
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
default:
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function summarize(tool, input) {
|
|
155
|
+
if (!tool || !input) return {};
|
|
156
|
+
switch (tool) {
|
|
157
|
+
case "Read":
|
|
158
|
+
return { file: shortPath(input.file_path) };
|
|
159
|
+
case "Edit":
|
|
160
|
+
return { file: shortPath(input.file_path) };
|
|
161
|
+
case "Write":
|
|
162
|
+
return { file: shortPath(input.file_path) };
|
|
163
|
+
case "Bash":
|
|
164
|
+
return {
|
|
165
|
+
cmd: (input.command || "").slice(0, 150),
|
|
166
|
+
desc: input.description,
|
|
167
|
+
};
|
|
168
|
+
case "Grep":
|
|
169
|
+
return { pattern: input.pattern, path: shortPath(input.path) };
|
|
170
|
+
case "Glob":
|
|
171
|
+
return { pattern: input.pattern };
|
|
172
|
+
case "Task":
|
|
173
|
+
return { desc: input.description, type: input.subagent_type };
|
|
174
|
+
case "WebSearch":
|
|
175
|
+
return { query: input.query };
|
|
176
|
+
case "WebFetch":
|
|
177
|
+
return { url: input.url };
|
|
178
|
+
case "NotebookEdit":
|
|
179
|
+
return { file: shortPath(input.notebook_path) };
|
|
180
|
+
default:
|
|
181
|
+
return {};
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function shortPath(p) {
|
|
186
|
+
if (!p) return null;
|
|
187
|
+
// Convert absolute paths to relative for readability
|
|
188
|
+
const cwd = process.cwd();
|
|
189
|
+
if (p.startsWith(cwd)) {
|
|
190
|
+
return p.slice(cwd.length + 1).replace(/\\/g, "/");
|
|
191
|
+
}
|
|
192
|
+
// For home-dir paths, abbreviate
|
|
193
|
+
const home = require("os").homedir();
|
|
194
|
+
if (p.startsWith(home)) {
|
|
195
|
+
return "~" + p.slice(home.length).replace(/\\/g, "/");
|
|
196
|
+
}
|
|
197
|
+
return p.replace(/\\/g, "/");
|
|
198
|
+
}
|