pi-gsd 1.0.7 → 1.2.2
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/.gsd/extensions/gsd-hooks.ts +237 -0
- package/README.md +108 -310
- package/dist/pi-gsd-tools.js +383 -0
- package/package.json +11 -10
- package/scripts/postinstall.js +270 -220
- package/skills/gsd-add-backlog/SKILL.md +3 -3
- package/skills/gsd-add-phase/SKILL.md +2 -2
- package/skills/gsd-add-tests/SKILL.md +2 -2
- package/skills/gsd-add-todo/SKILL.md +2 -2
- package/skills/gsd-audit-milestone/SKILL.md +2 -2
- package/skills/gsd-audit-uat/SKILL.md +1 -1
- package/skills/gsd-autonomous/SKILL.md +4 -4
- package/skills/gsd-check-todos/SKILL.md +2 -2
- package/skills/gsd-cleanup/SKILL.md +2 -2
- package/skills/gsd-complete-milestone/SKILL.md +2 -2
- package/skills/gsd-debug/SKILL.md +2 -2
- package/skills/gsd-discuss-phase/SKILL.md +6 -6
- package/skills/gsd-do/SKILL.md +3 -3
- package/skills/gsd-execute-phase/SKILL.md +4 -4
- package/skills/gsd-fast/SKILL.md +2 -2
- package/skills/gsd-forensics/SKILL.md +2 -2
- package/skills/gsd-health/SKILL.md +7 -3
- package/skills/gsd-help/SKILL.md +2 -2
- package/skills/gsd-insert-phase/SKILL.md +2 -2
- package/skills/gsd-list-phase-assumptions/SKILL.md +1 -1
- package/skills/gsd-list-workspaces/SKILL.md +3 -3
- package/skills/gsd-manager/SKILL.md +4 -4
- package/skills/gsd-map-codebase/SKILL.md +1 -1
- package/skills/gsd-milestone-summary/SKILL.md +2 -2
- package/skills/gsd-new-milestone/SKILL.md +6 -6
- package/skills/gsd-new-project/SKILL.md +6 -6
- package/skills/gsd-new-workspace/SKILL.md +3 -3
- package/skills/gsd-next/SKILL.md +2 -2
- package/skills/gsd-note/SKILL.md +3 -3
- package/skills/gsd-pause-work/SKILL.md +2 -2
- package/skills/gsd-plan-milestone-gaps/SKILL.md +2 -2
- package/skills/gsd-plan-phase/SKILL.md +3 -3
- package/skills/gsd-plant-seed/SKILL.md +2 -2
- package/skills/gsd-pr-branch/SKILL.md +2 -2
- package/skills/gsd-profile-user/SKILL.md +2 -2
- package/skills/gsd-progress/SKILL.md +7 -3
- package/skills/gsd-quick/SKILL.md +2 -2
- package/skills/gsd-remove-phase/SKILL.md +2 -2
- package/skills/gsd-remove-workspace/SKILL.md +3 -3
- package/skills/gsd-research-phase/SKILL.md +3 -3
- package/skills/gsd-resume-work/SKILL.md +2 -2
- package/skills/gsd-review/SKILL.md +2 -2
- package/skills/gsd-review-backlog/SKILL.md +2 -2
- package/skills/gsd-session-report/SKILL.md +2 -2
- package/skills/gsd-set-profile/SKILL.md +1 -1
- package/skills/gsd-settings/SKILL.md +2 -2
- package/skills/gsd-setup-pi/SKILL.md +105 -0
- package/skills/gsd-ship/SKILL.md +2 -2
- package/skills/gsd-stats/SKILL.md +6 -2
- package/skills/gsd-thread/SKILL.md +2 -2
- package/skills/gsd-ui-phase/SKILL.md +3 -3
- package/skills/gsd-ui-review/SKILL.md +3 -3
- package/skills/gsd-update/SKILL.md +2 -2
- package/skills/gsd-validate-phase/SKILL.md +2 -2
- package/skills/gsd-verify-work/SKILL.md +3 -3
- package/skills/gsd-workstreams/SKILL.md +1 -1
- package/dist/gsd-tools.js +0 -380
package/package.json
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-gsd",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.2",
|
|
4
4
|
"description": "Get Shit Done - Unofficial port of the renowned AI-native project-planning spec-driven toolkit",
|
|
5
|
-
"main": "dist/gsd-tools.js",
|
|
5
|
+
"main": "dist/pi-gsd-tools.js",
|
|
6
6
|
"bin": {
|
|
7
|
-
"gsd-tools": "./dist/gsd-tools.js",
|
|
8
|
-
"pi-gsd": "./dist/gsd-tools.js"
|
|
7
|
+
"pi-gsd-tools": "./dist/pi-gsd-tools.js",
|
|
8
|
+
"pi-gsd": "./dist/pi-gsd-tools.js"
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
|
-
"build": "tsup src/cli.ts --format cjs --out-dir dist --minify --no-splitting && mv dist/cli.js dist/gsd-tools.js && chmod +x dist/gsd-tools.js",
|
|
11
|
+
"build": "tsup src/cli.ts --format cjs --out-dir dist --minify --no-splitting && mv dist/cli.js dist/pi-gsd-tools.js && chmod +x dist/pi-gsd-tools.js",
|
|
12
12
|
"dev": "tsup src/cli.ts --format cjs --out-dir dist --watch",
|
|
13
13
|
"typecheck": "tsc --noEmit",
|
|
14
|
-
"build:harnesses": "node scripts/build-harnesses.js --clean",
|
|
15
14
|
"postinstall": "node scripts/postinstall.js",
|
|
16
|
-
"prepublishOnly": "npm run build
|
|
15
|
+
"prepublishOnly": "npm run build"
|
|
17
16
|
},
|
|
18
17
|
"files": [
|
|
19
18
|
"skills",
|
|
20
19
|
"dist",
|
|
21
20
|
"scripts/postinstall.js",
|
|
21
|
+
".gsd/extensions",
|
|
22
22
|
"README.md",
|
|
23
23
|
"LICENSE"
|
|
24
24
|
],
|
|
@@ -62,11 +62,12 @@
|
|
|
62
62
|
"dependencies": {
|
|
63
63
|
"@toon-format/toon": "^2.1.0",
|
|
64
64
|
"commander": "^13.0.0",
|
|
65
|
-
"jsonpath-plus": "^10.4.0"
|
|
65
|
+
"jsonpath-plus": "^10.4.0",
|
|
66
|
+
"zod": "^3.25.76"
|
|
66
67
|
},
|
|
67
68
|
"devDependencies": {
|
|
69
|
+
"@types/node": "^22.0.0",
|
|
68
70
|
"tsup": "^8.0.0",
|
|
69
|
-
"typescript": "^5.0.0"
|
|
70
|
-
"@types/node": "^22.0.0"
|
|
71
|
+
"typescript": "^5.0.0"
|
|
71
72
|
}
|
|
72
73
|
}
|
package/scripts/postinstall.js
CHANGED
|
@@ -2,32 +2,12 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* postinstall.js - GSD harness installer
|
|
4
4
|
*
|
|
5
|
-
* Runs automatically after `npm install
|
|
6
|
-
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* and CLI binary at the expected platform-native paths.
|
|
5
|
+
* Runs automatically after `npm install pi-gsd`.
|
|
6
|
+
* Copies the pi harness from this package's
|
|
7
|
+
* \`.gsd/harnesses/pi/\` into the consumer project's \`.pi/gsd/\`
|
|
8
|
+
* and installs the \`gsd-hooks.ts\` extension into \`.pi/extensions/\`.
|
|
10
9
|
*
|
|
11
|
-
*
|
|
12
|
-
* .gsd/harnesses/
|
|
13
|
-
* agent/ → consumer project root: .agent/get-shit-done/
|
|
14
|
-
* claude/ → consumer project root: .claude/get-shit-done/
|
|
15
|
-
* codex/ → consumer project root: .codex/get-shit-done/
|
|
16
|
-
* cursor/ → consumer project root: .cursor/get-shit-done/
|
|
17
|
-
* gemini/ → consumer project root: .gemini/get-shit-done/
|
|
18
|
-
* github/ → consumer project root: .github/get-shit-done/
|
|
19
|
-
* opencode/ → consumer project root: .opencode/get-shit-done/
|
|
20
|
-
* windsurf/ → consumer project root: .windsurf/get-shit-done/
|
|
21
|
-
*
|
|
22
|
-
* Hook files are also copied from `.gsd/hooks/` into each harness
|
|
23
|
-
* that supports the GSD hook system.
|
|
24
|
-
*
|
|
25
|
-
* The script is intentionally defensive: it never overwrites files
|
|
26
|
-
* that already exist (use --force-reinstall env flag to override),
|
|
27
|
-
* and it skips silently if a harness source directory is absent
|
|
28
|
-
* from the package (forward-compatibility).
|
|
29
|
-
*
|
|
30
|
-
* @see README.md §3 (Installation) for usage details.
|
|
10
|
+
* Safe to re-run — files are skipped if already present (unless GSD_FORCE=1).
|
|
31
11
|
*/
|
|
32
12
|
|
|
33
13
|
const fs = require("fs");
|
|
@@ -36,8 +16,8 @@ const path = require("path");
|
|
|
36
16
|
// ─── Constants ────────────────────────────────────────────────────────────────
|
|
37
17
|
|
|
38
18
|
const FORCE =
|
|
39
|
-
|
|
40
|
-
|
|
19
|
+
process.env.GSD_FORCE_REINSTALL === "1" ||
|
|
20
|
+
process.argv.includes("--force-reinstall");
|
|
41
21
|
|
|
42
22
|
/**
|
|
43
23
|
* Directory that contains this package's files.
|
|
@@ -61,22 +41,14 @@ const PROJECT_ROOT = process.env.INIT_CWD || process.cwd();
|
|
|
61
41
|
* hooks - whether this platform supports GSD hooks (copied from .gsd/hooks/)
|
|
62
42
|
*/
|
|
63
43
|
const HARNESSES = [
|
|
64
|
-
{ src: "
|
|
65
|
-
{ src: "agent", dest: ".pi", hooks: true },
|
|
66
|
-
{ src: "claude", dest: ".claude", hooks: true },
|
|
67
|
-
{ src: "codex", dest: ".codex", hooks: false },
|
|
68
|
-
{ src: "cursor", dest: ".cursor", hooks: false },
|
|
69
|
-
{ src: "gemini", dest: ".gemini", hooks: true },
|
|
70
|
-
{ src: "github", dest: ".github", hooks: false },
|
|
71
|
-
{ src: "opencode", dest: ".opencode", hooks: true },
|
|
72
|
-
{ src: "windsurf", dest: ".windsurf", hooks: false },
|
|
44
|
+
{ src: "pi", dest: ".pi", hooks: true, subdir: "gsd" },
|
|
73
45
|
];
|
|
74
46
|
|
|
75
47
|
/**
|
|
76
48
|
* Subdirectory name used inside each harness's dest folder for
|
|
77
49
|
* GSD-specific content (workflows, bin, references, templates …).
|
|
78
50
|
*/
|
|
79
|
-
|
|
51
|
+
// subdir is now per-harness (see harness config above)
|
|
80
52
|
|
|
81
53
|
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
82
54
|
|
|
@@ -90,32 +62,32 @@ const GSD_SUBDIR = "get-shit-done";
|
|
|
90
62
|
* @returns {{ copied: number, skipped: number }}
|
|
91
63
|
*/
|
|
92
64
|
function copyDir(src, dest, overwrite) {
|
|
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
|
-
|
|
65
|
+
let copied = 0;
|
|
66
|
+
let skipped = 0;
|
|
67
|
+
|
|
68
|
+
if (!fs.existsSync(src)) return { copied, skipped };
|
|
69
|
+
|
|
70
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
71
|
+
|
|
72
|
+
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
73
|
+
const srcEntry = path.join(src, entry.name);
|
|
74
|
+
const destEntry = path.join(dest, entry.name);
|
|
75
|
+
|
|
76
|
+
if (entry.isDirectory()) {
|
|
77
|
+
const sub = copyDir(srcEntry, destEntry, overwrite);
|
|
78
|
+
copied += sub.copied;
|
|
79
|
+
skipped += sub.skipped;
|
|
80
|
+
} else if (entry.isFile()) {
|
|
81
|
+
if (!overwrite && fs.existsSync(destEntry)) {
|
|
82
|
+
skipped++;
|
|
83
|
+
} else {
|
|
84
|
+
fs.copyFileSync(srcEntry, destEntry);
|
|
85
|
+
copied++;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return { copied, skipped };
|
|
119
91
|
}
|
|
120
92
|
|
|
121
93
|
/**
|
|
@@ -126,160 +98,156 @@ function copyDir(src, dest, overwrite) {
|
|
|
126
98
|
* @param {string} msg
|
|
127
99
|
*/
|
|
128
100
|
function log(level, msg) {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
101
|
+
const isTTY = process.stdout.isTTY;
|
|
102
|
+
const colours = {
|
|
103
|
+
ok: isTTY ? "\x1b[32m✓\x1b[0m" : "✓",
|
|
104
|
+
skip: isTTY ? "\x1b[33m–\x1b[0m" : "–",
|
|
105
|
+
warn: isTTY ? "\x1b[33m⚠\x1b[0m" : "⚠",
|
|
106
|
+
err: isTTY ? "\x1b[31m✗\x1b[0m" : "✗",
|
|
107
|
+
};
|
|
108
|
+
console.log(` ${colours[level] || " "} ${msg}`);
|
|
137
109
|
}
|
|
138
110
|
|
|
139
111
|
// ─── Main ─────────────────────────────────────────────────────────────────────
|
|
140
112
|
|
|
141
113
|
function main() {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
);
|
|
280
|
-
console.log("");
|
|
281
|
-
console.log(" Docs: https://github.com/fulgidus/pi-gsd#readme");
|
|
282
|
-
console.log("");
|
|
114
|
+
// Skip when running inside the package's own development tree
|
|
115
|
+
// (i.e. when INIT_CWD === the package directory itself).
|
|
116
|
+
if (path.resolve(PROJECT_ROOT) === path.resolve(PKG_DIR)) {
|
|
117
|
+
log(
|
|
118
|
+
"skip",
|
|
119
|
+
"Running inside package source tree - skipping harness install.",
|
|
120
|
+
);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Skip when explicitly opted out
|
|
125
|
+
if (process.env.GSD_SKIP_INSTALL === "1") {
|
|
126
|
+
log("skip", "GSD_SKIP_INSTALL=1 - skipping harness install.");
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const harnessesRoot = path.join(PKG_DIR, ".gsd", "harnesses");
|
|
131
|
+
const hooksRoot = path.join(PKG_DIR, ".gsd", "hooks");
|
|
132
|
+
|
|
133
|
+
console.log("");
|
|
134
|
+
console.log(" GSD - installing harness files into your project…");
|
|
135
|
+
if (FORCE)
|
|
136
|
+
console.log(" (force-reinstall mode: existing files will be overwritten)");
|
|
137
|
+
console.log("");
|
|
138
|
+
|
|
139
|
+
let totalCopied = 0;
|
|
140
|
+
let totalSkipped = 0;
|
|
141
|
+
let installed = 0;
|
|
142
|
+
|
|
143
|
+
for (const harness of HARNESSES) {
|
|
144
|
+
const srcHarness = path.join(harnessesRoot, harness.src);
|
|
145
|
+
const destHarness = path.join(PROJECT_ROOT, harness.dest);
|
|
146
|
+
|
|
147
|
+
// ── get-shit-done/ content ──────────────────────────────────────────────
|
|
148
|
+
const srcGsd = path.join(srcHarness, harness.subdir);
|
|
149
|
+
const destGsd = path.join(destHarness, harness.subdir);
|
|
150
|
+
|
|
151
|
+
if (!fs.existsSync(srcHarness)) {
|
|
152
|
+
log("skip", `${harness.dest}/${harness.subdir} (source absent - skipped)`);
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const { copied, skipped } = copyDir(srcGsd, destGsd, FORCE);
|
|
157
|
+
totalCopied += copied;
|
|
158
|
+
totalSkipped += skipped;
|
|
159
|
+
|
|
160
|
+
if (copied > 0 || skipped === 0) {
|
|
161
|
+
log(
|
|
162
|
+
"ok",
|
|
163
|
+
`${harness.dest}/${harness.subdir} (${copied} file${copied === 1 ? "" : "s"} installed)`,
|
|
164
|
+
);
|
|
165
|
+
} else {
|
|
166
|
+
log(
|
|
167
|
+
"skip",
|
|
168
|
+
`${harness.dest}/${harness.subdir} (already up-to-date, ${skipped} file${skipped === 1 ? "" : "s"} skipped)`,
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// ── gsd-file-manifest.json ──────────────────────────────────────────────
|
|
173
|
+
const manifestSrc = path.join(srcHarness, "gsd-file-manifest.json");
|
|
174
|
+
const manifestDest = path.join(destHarness, "gsd-file-manifest.json");
|
|
175
|
+
|
|
176
|
+
if (fs.existsSync(manifestSrc)) {
|
|
177
|
+
if (!FORCE && fs.existsSync(manifestDest)) {
|
|
178
|
+
totalSkipped++;
|
|
179
|
+
} else {
|
|
180
|
+
fs.mkdirSync(destHarness, { recursive: true });
|
|
181
|
+
fs.copyFileSync(manifestSrc, manifestDest);
|
|
182
|
+
totalCopied++;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// ── hooks/ (platform-selective) ─────────────────────────────────────────
|
|
187
|
+
if (harness.hooks && fs.existsSync(hooksRoot)) {
|
|
188
|
+
const destHooks = path.join(destHarness, "hooks");
|
|
189
|
+
const h = copyDir(hooksRoot, destHooks, FORCE);
|
|
190
|
+
totalCopied += h.copied;
|
|
191
|
+
totalSkipped += h.skipped;
|
|
192
|
+
|
|
193
|
+
if (h.copied > 0) {
|
|
194
|
+
log(
|
|
195
|
+
"ok",
|
|
196
|
+
`${harness.dest}/hooks (${h.copied} hook${h.copied === 1 ? "" : "s"} installed)`,
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// ── skills/ (opencode only - present in .gsd/harnesses/opencode/skills) ─
|
|
202
|
+
const srcSkills = path.join(srcHarness, "skills");
|
|
203
|
+
const destSkills = path.join(destHarness, "skills");
|
|
204
|
+
|
|
205
|
+
if (fs.existsSync(srcSkills)) {
|
|
206
|
+
const s = copyDir(srcSkills, destSkills, FORCE);
|
|
207
|
+
totalCopied += s.copied;
|
|
208
|
+
totalSkipped += s.skipped;
|
|
209
|
+
|
|
210
|
+
if (s.copied > 0) {
|
|
211
|
+
log(
|
|
212
|
+
"ok",
|
|
213
|
+
`${harness.dest}/skills (${s.copied} skill file${s.copied === 1 ? "" : "s"} installed)`,
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
installed++;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// ── Pi extension (.pi/extensions/gsd-hooks.ts) ─────────────────────────────
|
|
222
|
+
// Install the GSD pi lifecycle extension (session_start, tool_call, tool_result hooks).
|
|
223
|
+
// The extension is auto-discovered by pi from .pi/extensions/ - no manual wiring needed.
|
|
224
|
+
installPiExtension(PROJECT_ROOT, PKG_DIR, FORCE, (copied) => {
|
|
225
|
+
if (copied) totalCopied++;
|
|
226
|
+
else totalSkipped++;
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
console.log("");
|
|
230
|
+
|
|
231
|
+
if (installed === 0) {
|
|
232
|
+
log("warn", "No harness source directories found inside the package.");
|
|
233
|
+
log(
|
|
234
|
+
"warn",
|
|
235
|
+
"The package may be incomplete. Try: npm install --force get-shit-done-cc",
|
|
236
|
+
);
|
|
237
|
+
console.log("");
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
console.log(` GSD v${getPackageVersion()} installed successfully.`);
|
|
242
|
+
console.log(
|
|
243
|
+
` ${totalCopied} file${totalCopied === 1 ? "" : "s"} copied, ${totalSkipped} skipped.`,
|
|
244
|
+
);
|
|
245
|
+
console.log("");
|
|
246
|
+
console.log(" Next steps:");
|
|
247
|
+
console.log(" Run /gsd-new-project to initialise a project.");
|
|
248
|
+
console.log("");
|
|
249
|
+
console.log(" Docs: https://github.com/fulgidus/pi-gsd#readme");
|
|
250
|
+
console.log("");
|
|
283
251
|
}
|
|
284
252
|
|
|
285
253
|
/**
|
|
@@ -289,14 +257,96 @@ function main() {
|
|
|
289
257
|
* @returns {string}
|
|
290
258
|
*/
|
|
291
259
|
function getPackageVersion() {
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
260
|
+
try {
|
|
261
|
+
const pkg = JSON.parse(
|
|
262
|
+
fs.readFileSync(path.join(PKG_DIR, "package.json"), "utf8"),
|
|
263
|
+
);
|
|
264
|
+
return pkg.version || "unknown";
|
|
265
|
+
} catch {
|
|
266
|
+
return "unknown";
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Install the GSD pi extension into the consumer project's .pi/extensions/ directory.
|
|
272
|
+
* Also updates .pi/settings.json to include the extension in the extensions array.
|
|
273
|
+
*
|
|
274
|
+
* The extension registers three non-blocking pi lifecycle hooks:
|
|
275
|
+
* session_start → background GSD update check
|
|
276
|
+
* tool_call → workflow guard advisory (write/edit outside GSD context)
|
|
277
|
+
* tool_result → context usage monitor
|
|
278
|
+
*
|
|
279
|
+
* @param {string} projectRoot Consumer project root
|
|
280
|
+
* @param {string} pkgDir This package's root directory
|
|
281
|
+
* @param {boolean} force Overwrite existing files
|
|
282
|
+
* @param {function} callback Called with (copied: boolean)
|
|
283
|
+
*/
|
|
284
|
+
function installPiExtension(projectRoot, pkgDir, force, callback) {
|
|
285
|
+
const piDir = path.join(projectRoot, ".pi");
|
|
286
|
+
const extDir = path.join(piDir, "extensions");
|
|
287
|
+
const extDest = path.join(extDir, "gsd-hooks.ts");
|
|
288
|
+
const extSrc = path.join(pkgDir, ".gsd", "extensions", "gsd-hooks.ts");
|
|
289
|
+
|
|
290
|
+
if (!fs.existsSync(extSrc)) {
|
|
291
|
+
log("warn", ".pi/extensions/gsd-hooks.ts (source absent - skipped)");
|
|
292
|
+
callback(false);
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (!force && fs.existsSync(extDest)) {
|
|
297
|
+
log("skip", ".pi/extensions/gsd-hooks.ts (already exists)");
|
|
298
|
+
callback(false);
|
|
299
|
+
} else {
|
|
300
|
+
try {
|
|
301
|
+
fs.mkdirSync(extDir, { recursive: true });
|
|
302
|
+
fs.copyFileSync(extSrc, extDest);
|
|
303
|
+
log(
|
|
304
|
+
"ok",
|
|
305
|
+
".pi/extensions/gsd-hooks.ts (GSD lifecycle extension installed)",
|
|
306
|
+
);
|
|
307
|
+
callback(true);
|
|
308
|
+
} catch (e) {
|
|
309
|
+
log(
|
|
310
|
+
"warn",
|
|
311
|
+
".pi/extensions/gsd-hooks.ts (install failed: " + e.message + ")",
|
|
312
|
+
);
|
|
313
|
+
callback(false);
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Update .pi/settings.json to include the extension path in the extensions array.
|
|
319
|
+
// The file is already auto-discovered from .pi/extensions/, but explicit registration
|
|
320
|
+
// is added as a belt-and-suspenders measure.
|
|
321
|
+
const settingsFile = path.join(piDir, "settings.json");
|
|
322
|
+
try {
|
|
323
|
+
let settings = {};
|
|
324
|
+
if (fs.existsSync(settingsFile)) {
|
|
325
|
+
try {
|
|
326
|
+
settings = JSON.parse(fs.readFileSync(settingsFile, "utf8"));
|
|
327
|
+
} catch {
|
|
328
|
+
// Unreadable settings - start fresh object
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
const extensions = Array.isArray(settings.extensions)
|
|
333
|
+
? settings.extensions
|
|
334
|
+
: [];
|
|
335
|
+
|
|
336
|
+
// Avoid duplicate entries
|
|
337
|
+
if (!extensions.includes(extDest)) {
|
|
338
|
+
settings.extensions = [...extensions, extDest];
|
|
339
|
+
fs.mkdirSync(piDir, { recursive: true });
|
|
340
|
+
fs.writeFileSync(
|
|
341
|
+
settingsFile,
|
|
342
|
+
JSON.stringify(settings, null, "\t"),
|
|
343
|
+
"utf8",
|
|
344
|
+
);
|
|
345
|
+
log("ok", ".pi/settings.json (extensions array updated)");
|
|
346
|
+
}
|
|
347
|
+
} catch (e) {
|
|
348
|
+
log("warn", ".pi/settings.json (could not update: " + e.message + ")");
|
|
349
|
+
}
|
|
300
350
|
}
|
|
301
351
|
|
|
302
352
|
main();
|
|
@@ -20,7 +20,7 @@ the normal phase sequence and accumulate context over time.
|
|
|
20
20
|
2. **Find next backlog number:**
|
|
21
21
|
|
|
22
22
|
```bash
|
|
23
|
-
NEXT=$(node ".
|
|
23
|
+
NEXT=$(node ".pi/gsd/bin/pi-gsd-tools.cjs" phase next-decimal 999 --raw)
|
|
24
24
|
```
|
|
25
25
|
|
|
26
26
|
If no 999.x phases exist, start at 999.1.
|
|
@@ -28,7 +28,7 @@ the normal phase sequence and accumulate context over time.
|
|
|
28
28
|
3. **Create the phase directory:**
|
|
29
29
|
|
|
30
30
|
```bash
|
|
31
|
-
SLUG=$(node ".
|
|
31
|
+
SLUG=$(node ".pi/gsd/bin/pi-gsd-tools.cjs" generate-slug "$ARGUMENTS")
|
|
32
32
|
mkdir -p ".planning/phases/${NEXT}-${SLUG}"
|
|
33
33
|
touch ".planning/phases/${NEXT}-${SLUG}/.gitkeep"
|
|
34
34
|
```
|
|
@@ -52,7 +52,7 @@ the normal phase sequence and accumulate context over time.
|
|
|
52
52
|
5. **Commit:**
|
|
53
53
|
|
|
54
54
|
```bash
|
|
55
|
-
node ".
|
|
55
|
+
node ".pi/gsd/bin/pi-gsd-tools.cjs" commit "docs: add backlog item ${NEXT} - ${ARGUMENTS}" --files .planning/ROADMAP.md ".planning/phases/${NEXT}-${SLUG}/.gitkeep"
|
|
56
56
|
```
|
|
57
57
|
|
|
58
58
|
6. **Report:**
|
|
@@ -15,7 +15,7 @@ Routes to the add-phase workflow which handles:
|
|
|
15
15
|
</objective>
|
|
16
16
|
|
|
17
17
|
<execution_context>
|
|
18
|
-
@.
|
|
18
|
+
@.pi/gsd/workflows/add-phase.md
|
|
19
19
|
</execution_context>
|
|
20
20
|
|
|
21
21
|
<context>
|
|
@@ -25,7 +25,7 @@ Roadmap and state are resolved in-workflow via `init phase-op` and targeted tool
|
|
|
25
25
|
</context>
|
|
26
26
|
|
|
27
27
|
<process>
|
|
28
|
-
**Follow the add-phase workflow** from `@.
|
|
28
|
+
**Follow the add-phase workflow** from `@.pi/gsd/workflows/add-phase.md`.
|
|
29
29
|
|
|
30
30
|
The workflow handles all logic including:
|
|
31
31
|
1. Argument parsing and validation
|
|
@@ -12,7 +12,7 @@ Output: Test files committed with message `test(phase-{N}): add unit and E2E tes
|
|
|
12
12
|
</objective>
|
|
13
13
|
|
|
14
14
|
<execution_context>
|
|
15
|
-
@.
|
|
15
|
+
@.pi/gsd/workflows/add-tests.md
|
|
16
16
|
</execution_context>
|
|
17
17
|
|
|
18
18
|
<context>
|
|
@@ -23,6 +23,6 @@ Phase: $ARGUMENTS
|
|
|
23
23
|
</context>
|
|
24
24
|
|
|
25
25
|
<process>
|
|
26
|
-
Execute the add-tests workflow from @.
|
|
26
|
+
Execute the add-tests workflow from @.pi/gsd/workflows/add-tests.md end-to-end.
|
|
27
27
|
Preserve all workflow gates (classification approval, test plan approval, RED-GREEN verification, gap reporting).
|
|
28
28
|
</process>
|