pi-gsd 2.0.23 → 2.1.0
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/dist/pi-gsd-hooks.js +5 -5
- package/dist/pi-gsd-tools.js +38 -38
- package/gsd/hooks/gsd-context-monitor.js +164 -0
- package/gsd/hooks/gsd-prompt-guard.js +99 -0
- package/gsd/hooks/gsd-workflow-guard.js +98 -0
- package/gsd/prompts/gsd-discuss-milestone.md +7 -0
- package/gsd/templates/milestone-context.md +231 -0
- package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/complete-milestone.md +7 -1
- package/gsd/workflows/discuss-milestone.md +467 -0
- package/package.json +4 -5
- package/scripts/postinstall.js +129 -332
- package/.gsd/harnesses/pi/get-shit-done/workflows/discuss-phase.md.bak +0 -1049
- package/.gsd/harnesses/pi/get-shit-done/workflows/execute-phase.md.bak +0 -846
- package/.gsd/harnesses/pi/get-shit-done/workflows/new-milestone.md.bak +0 -486
- package/.gsd/harnesses/pi/get-shit-done/workflows/new-project.md.bak +0 -1250
- package/.gsd/harnesses/pi/get-shit-done/workflows/plan-phase.md.bak +0 -859
- package/.gsd/harnesses/pi/gsd-file-manifest.json +0 -219
- package/.gsd/harnesses/pi/hooks/gsd-context-monitor.js +0 -159
- package/.gsd/harnesses/pi/hooks/gsd-prompt-guard.js +0 -99
- package/.gsd/harnesses/pi/hooks/gsd-workflow-guard.js +0 -97
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/VERSION +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/agents/gsd-advisor-researcher.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/agents/gsd-assumptions-analyzer.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/agents/gsd-codebase-mapper.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/agents/gsd-debugger.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/agents/gsd-executor.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/agents/gsd-integration-checker.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/agents/gsd-nyquist-auditor.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/agents/gsd-phase-researcher.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/agents/gsd-plan-checker.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/agents/gsd-planner.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/agents/gsd-project-researcher.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/agents/gsd-research-synthesizer.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/agents/gsd-roadmapper.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/agents/gsd-ui-auditor.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/agents/gsd-ui-checker.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/agents/gsd-ui-researcher.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/agents/gsd-user-profiler.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/agents/gsd-verifier.md +0 -0
- /package/{.gsd/harnesses/pi → gsd}/hooks/gsd-check-update.js +0 -0
- /package/{.gsd/harnesses/pi → gsd}/hooks/gsd-statusline.js +0 -0
- /package/{prompts → gsd/prompts}/gsd-add-backlog.md +0 -0
- /package/{prompts → gsd/prompts}/gsd-add-phase.md +0 -0
- /package/{prompts → gsd/prompts}/gsd-add-tests.md +0 -0
- /package/{prompts → gsd/prompts}/gsd-add-todo.md +0 -0
- /package/{prompts → gsd/prompts}/gsd-audit-milestone.md +0 -0
- /package/{prompts → gsd/prompts}/gsd-audit-uat.md +0 -0
- /package/{prompts → gsd/prompts}/gsd-autonomous.md +0 -0
- /package/{prompts → gsd/prompts}/gsd-check-todos.md +0 -0
- /package/{prompts → gsd/prompts}/gsd-cleanup.md +0 -0
- /package/{prompts → gsd/prompts}/gsd-complete-milestone.md +0 -0
- /package/{prompts → gsd/prompts}/gsd-debug.md +0 -0
- /package/{prompts → gsd/prompts}/gsd-discuss-phase.md +0 -0
- /package/{prompts → gsd/prompts}/gsd-do.md +0 -0
- /package/{prompts → gsd/prompts}/gsd-execute-milestone.md +0 -0
- /package/{prompts → gsd/prompts}/gsd-execute-phase.md +0 -0
- /package/{prompts → gsd/prompts}/gsd-fast.md +0 -0
- /package/{prompts → gsd/prompts}/gsd-forensics.md +0 -0
- /package/{prompts → gsd/prompts}/gsd-insert-phase.md +0 -0
- /package/{prompts → gsd/prompts}/gsd-join-discord.md +0 -0
- /package/{prompts → gsd/prompts}/gsd-list-phase-assumptions.md +0 -0
- /package/{prompts → gsd/prompts}/gsd-list-workspaces.md +0 -0
- /package/{prompts → gsd/prompts}/gsd-manager.md +0 -0
- /package/{prompts → gsd/prompts}/gsd-map-codebase.md +0 -0
- /package/{prompts → gsd/prompts}/gsd-milestone-summary.md +0 -0
- /package/{prompts → gsd/prompts}/gsd-new-milestone.md +0 -0
- /package/{prompts → gsd/prompts}/gsd-new-project.md +0 -0
- /package/{prompts → gsd/prompts}/gsd-new-workspace.md +0 -0
- /package/{prompts → gsd/prompts}/gsd-note.md +0 -0
- /package/{prompts → gsd/prompts}/gsd-pause-work.md +0 -0
- /package/{prompts → gsd/prompts}/gsd-plan-milestone-gaps.md +0 -0
- /package/{prompts → gsd/prompts}/gsd-plan-milestone.md +0 -0
- /package/{prompts → gsd/prompts}/gsd-plan-phase.md +0 -0
- /package/{prompts → gsd/prompts}/gsd-plant-seed.md +0 -0
- /package/{prompts → gsd/prompts}/gsd-pr-branch.md +0 -0
- /package/{prompts → gsd/prompts}/gsd-profile-user.md +0 -0
- /package/{prompts → gsd/prompts}/gsd-quick.md +0 -0
- /package/{prompts → gsd/prompts}/gsd-reapply-patches.md +0 -0
- /package/{prompts → gsd/prompts}/gsd-remove-phase.md +0 -0
- /package/{prompts → gsd/prompts}/gsd-remove-workspace.md +0 -0
- /package/{prompts → gsd/prompts}/gsd-research-phase.md +0 -0
- /package/{prompts → gsd/prompts}/gsd-resume-work.md +0 -0
- /package/{prompts → gsd/prompts}/gsd-review-backlog.md +0 -0
- /package/{prompts → gsd/prompts}/gsd-review.md +0 -0
- /package/{prompts → gsd/prompts}/gsd-session-report.md +0 -0
- /package/{prompts → gsd/prompts}/gsd-set-profile.md +0 -0
- /package/{prompts → gsd/prompts}/gsd-settings.md +0 -0
- /package/{prompts → gsd/prompts}/gsd-ship.md +0 -0
- /package/{prompts → gsd/prompts}/gsd-thread.md +0 -0
- /package/{prompts → gsd/prompts}/gsd-ui-phase.md +0 -0
- /package/{prompts → gsd/prompts}/gsd-ui-review.md +0 -0
- /package/{prompts → gsd/prompts}/gsd-validate-phase.md +0 -0
- /package/{prompts → gsd/prompts}/gsd-verify-work.md +0 -0
- /package/{prompts → gsd/prompts}/gsd-workstreams.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/references/checkpoints.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/references/continuation-format.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/references/decimal-phase-calculation.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/references/git-integration.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/references/git-planning-commit.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/references/model-profile-resolution.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/references/model-profiles.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/references/phase-argument-parsing.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/references/planning-config.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/references/questioning.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/references/tdd.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/references/ui-brand.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/references/user-profiling.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/references/verification-patterns.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/references/workstream-flag.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/DEBUG.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/UAT.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/UI-SPEC.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/VALIDATION.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/claude-md.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/codebase/architecture.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/codebase/concerns.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/codebase/conventions.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/codebase/integrations.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/codebase/stack.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/codebase/structure.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/codebase/testing.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/config.json +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/context.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/continue-here.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/copilot-instructions.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/debug-subagent-prompt.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/dev-preferences.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/discovery.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/discussion-log.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/milestone-archive.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/milestone.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/phase-prompt.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/planner-subagent-prompt.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/project.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/requirements.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/research-project/ARCHITECTURE.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/research-project/FEATURES.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/research-project/PITFALLS.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/research-project/STACK.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/research-project/SUMMARY.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/research.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/retrospective.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/roadmap.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/state.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/summary-complex.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/summary-minimal.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/summary-standard.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/summary.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/user-profile.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/user-setup.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/templates/verification-report.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/add-backlog.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/add-phase.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/add-tests.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/add-todo.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/audit-milestone.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/audit-uat.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/autonomous.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/check-todos.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/cleanup.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/debug.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/diagnose-issues.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/discovery-phase.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/discuss-phase-assumptions.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/discuss-phase.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/do.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/execute-milestone.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/execute-phase.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/execute-plan.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/fast.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/forensics.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/health.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/help.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/insert-phase.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/list-phase-assumptions.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/list-workspaces.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/manager.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/map-codebase.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/milestone-summary.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/new-milestone.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/new-project.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/new-workspace.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/next.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/node-repair.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/note.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/pause-work.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/plan-milestone-gaps.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/plan-milestone.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/plan-phase.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/plant-seed.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/pr-branch.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/profile-user.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/progress.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/quick.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/remove-phase.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/remove-workspace.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/research-phase.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/resume-project.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/review-backlog.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/review.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/session-report.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/set-profile.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/settings.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/ship.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/stats.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/thread.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/transition.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/ui-phase.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/ui-review.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/update.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/validate-phase.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/verify-phase.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/verify-work.md +0 -0
- /package/{.gsd/harnesses/pi/get-shit-done → gsd}/workflows/workstreams.md +0 -0
package/scripts/postinstall.js
CHANGED
|
@@ -2,15 +2,24 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* postinstall.js - GSD harness installer
|
|
4
4
|
*
|
|
5
|
-
* Runs automatically after `npm install pi-gsd
|
|
6
|
-
* Copies
|
|
7
|
-
*
|
|
8
|
-
* and installs the \`pi-gsd-hooks.ts\` extension into \`.pi/extensions/\`.
|
|
5
|
+
* Runs automatically after `npm install pi-gsd` (or `pi install npm:pi-gsd`).
|
|
6
|
+
* Copies runtime harness content from this package's `gsd/` into the consumer
|
|
7
|
+
* project's `.pi/gsd/`, and hook scripts into `.pi/gsd/hooks/`.
|
|
9
8
|
*
|
|
10
|
-
*
|
|
9
|
+
* Version detection: each installed .md file carries a `<gsd-version v="X.Y.Z" />`
|
|
10
|
+
* tag stamped with the npm package version that wrote it. On reinstall we read
|
|
11
|
+
* that tag from a sample file; if it doesn't match the current package version we
|
|
12
|
+
* overwrite everything so workflow/template fixes always reach installed projects.
|
|
13
|
+
* Files tagged `do-not-update` are never overwritten (user customisations).
|
|
14
|
+
*
|
|
15
|
+
* Excluded from the `.pi/gsd/` copy:
|
|
16
|
+
* gsd/prompts/ → served from npm package via pi.prompts (not installed locally)
|
|
17
|
+
* gsd/hooks/ → copied to .pi/gsd/hooks/ instead
|
|
11
18
|
*/
|
|
12
19
|
|
|
13
|
-
|
|
20
|
+
"use strict";
|
|
21
|
+
|
|
22
|
+
const fs = require("fs");
|
|
14
23
|
const path = require("path");
|
|
15
24
|
|
|
16
25
|
// ─── Constants ────────────────────────────────────────────────────────────────
|
|
@@ -19,274 +28,190 @@ const FORCE =
|
|
|
19
28
|
process.env.GSD_FORCE_REINSTALL === "1" ||
|
|
20
29
|
process.argv.includes("--force-reinstall");
|
|
21
30
|
|
|
22
|
-
/**
|
|
23
|
-
* Directory that contains this package's files.
|
|
24
|
-
* When executed via npm postinstall, __dirname is the package root.
|
|
25
|
-
*/
|
|
31
|
+
/** Directory that contains this package's files. */
|
|
26
32
|
const PKG_DIR = path.resolve(__dirname, "..");
|
|
27
33
|
|
|
28
34
|
/**
|
|
29
35
|
* The consuming project's root.
|
|
30
36
|
* npm sets INIT_CWD to the directory where `npm install` was run.
|
|
31
|
-
* Fall back to process.cwd() for programmatic / npx usage.
|
|
32
37
|
*/
|
|
33
38
|
const PROJECT_ROOT = process.env.INIT_CWD || process.cwd();
|
|
34
39
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
*/
|
|
43
|
-
const HARNESSES = [{ src: "pi", dest: ".pi", hooks: true, subdir: "gsd" }];
|
|
40
|
+
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
41
|
+
|
|
42
|
+
function getPackageVersion() {
|
|
43
|
+
try {
|
|
44
|
+
return JSON.parse(fs.readFileSync(path.join(PKG_DIR, "package.json"), "utf8")).version || "0.0.0";
|
|
45
|
+
} catch { return "0.0.0"; }
|
|
46
|
+
}
|
|
44
47
|
|
|
45
48
|
/**
|
|
46
|
-
*
|
|
47
|
-
*
|
|
49
|
+
* Read the `<gsd-version v="..." />` tag from a file.
|
|
50
|
+
* Returns { version, doNotUpdate } or null.
|
|
48
51
|
*/
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
+
function readGsdVersion(filePath) {
|
|
53
|
+
try {
|
|
54
|
+
const head = fs.readFileSync(filePath, "utf8").slice(0, 256);
|
|
55
|
+
const m = /<gsd-version\s+v="([^"]+)"(\s+do-not-update)?\s*\/>/.exec(head);
|
|
56
|
+
return m ? { version: m[1], doNotUpdate: Boolean(m[2]) } : null;
|
|
57
|
+
} catch { return null; }
|
|
58
|
+
}
|
|
52
59
|
|
|
53
60
|
/**
|
|
54
|
-
* Recursively copy
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
* @param {string} dest Absolute destination path
|
|
59
|
-
* @param {boolean} overwrite Replace existing files when true
|
|
60
|
-
* @returns {{ copied: number, skipped: number }}
|
|
61
|
+
* Recursively copy src → dest, stamping each .md file with `pkgVersion`
|
|
62
|
+
* in its `<gsd-version>` tag (unless `do-not-update` is set).
|
|
63
|
+
* Skips top-level subdirs in `exclude`. Skips existing files when !overwrite
|
|
64
|
+
* (except: always updates if installed version tag differs from pkgVersion).
|
|
61
65
|
*/
|
|
62
|
-
function copyDir(src, dest, overwrite) {
|
|
63
|
-
let copied = 0;
|
|
64
|
-
let skipped = 0;
|
|
65
|
-
|
|
66
|
+
function copyDir(src, dest, overwrite, exclude, pkgVersion) {
|
|
67
|
+
let copied = 0, skipped = 0;
|
|
66
68
|
if (!fs.existsSync(src)) return { copied, skipped };
|
|
67
|
-
|
|
68
69
|
fs.mkdirSync(dest, { recursive: true });
|
|
69
70
|
|
|
70
71
|
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
71
|
-
|
|
72
|
+
if (exclude && exclude.includes(entry.name)) continue;
|
|
73
|
+
const srcEntry = path.join(src, entry.name);
|
|
72
74
|
const destEntry = path.join(dest, entry.name);
|
|
73
75
|
|
|
74
76
|
if (entry.isDirectory()) {
|
|
75
|
-
const sub = copyDir(srcEntry, destEntry, overwrite);
|
|
76
|
-
copied
|
|
77
|
+
const sub = copyDir(srcEntry, destEntry, overwrite, null, pkgVersion);
|
|
78
|
+
copied += sub.copied;
|
|
77
79
|
skipped += sub.skipped;
|
|
78
80
|
} else if (entry.isFile()) {
|
|
81
|
+
const isMd = entry.name.endsWith(".md");
|
|
82
|
+
|
|
79
83
|
if (!overwrite && fs.existsSync(destEntry)) {
|
|
80
|
-
|
|
84
|
+
// Even in no-overwrite mode, update .md files whose version tag
|
|
85
|
+
// doesn't match the current package (the whole point of this system).
|
|
86
|
+
if (isMd) {
|
|
87
|
+
const installed = readGsdVersion(destEntry);
|
|
88
|
+
if (!installed || installed.doNotUpdate || installed.version === pkgVersion) {
|
|
89
|
+
skipped++;
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
// Fall through — version mismatch, needs update
|
|
93
|
+
} else {
|
|
94
|
+
skipped++;
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (isMd) {
|
|
100
|
+
// Stamp with current package version
|
|
101
|
+
let content = fs.readFileSync(srcEntry, "utf8");
|
|
102
|
+
content = content.replace(
|
|
103
|
+
/<gsd-version\s+v="[^"]*"(\s+do-not-update)?\s*\/>/,
|
|
104
|
+
(_, dnu) => dnu ? `<gsd-version v="${pkgVersion}" do-not-update />` : `<gsd-version v="${pkgVersion}" />`,
|
|
105
|
+
);
|
|
106
|
+
fs.writeFileSync(destEntry, content, "utf8");
|
|
81
107
|
} else {
|
|
82
108
|
fs.copyFileSync(srcEntry, destEntry);
|
|
83
|
-
copied++;
|
|
84
109
|
}
|
|
110
|
+
copied++;
|
|
85
111
|
}
|
|
86
112
|
}
|
|
87
|
-
|
|
88
113
|
return { copied, skipped };
|
|
89
114
|
}
|
|
90
115
|
|
|
91
|
-
/**
|
|
92
|
-
* Emit a coloured status line to stdout.
|
|
93
|
-
* Colours are stripped when stdout is not a TTY (CI / pipe).
|
|
94
|
-
*
|
|
95
|
-
* @param {'ok'|'skip'|'warn'|'err'} level
|
|
96
|
-
* @param {string} msg
|
|
97
|
-
*/
|
|
98
116
|
function log(level, msg) {
|
|
99
117
|
const isTTY = process.stdout.isTTY;
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
skip: isTTY ? "\x1b[33m–\x1b[0m" : "–",
|
|
103
|
-
warn: isTTY ? "\x1b[33m⚠\x1b[0m" : "⚠",
|
|
104
|
-
err: isTTY ? "\x1b[31m✗\x1b[0m" : "✗",
|
|
105
|
-
};
|
|
106
|
-
console.log(` ${colours[level] || " "} ${msg}`);
|
|
118
|
+
const sym = { ok: isTTY ? "\x1b[32m✓\x1b[0m" : "✓", skip: isTTY ? "\x1b[33m–\x1b[0m" : "–", warn: isTTY ? "\x1b[33m⚠\x1b[0m" : "⚠" };
|
|
119
|
+
console.log(` ${sym[level] || " "} ${msg}`);
|
|
107
120
|
}
|
|
108
121
|
|
|
109
122
|
// ─── Main ─────────────────────────────────────────────────────────────────────
|
|
110
123
|
|
|
111
124
|
function main() {
|
|
112
|
-
// Skip when running inside the package's own development tree
|
|
113
|
-
// (i.e. when INIT_CWD === the package directory itself).
|
|
114
|
-
if (path.resolve(PROJECT_ROOT) === path.resolve(PKG_DIR)) {
|
|
115
|
-
log(
|
|
116
|
-
"skip",
|
|
117
|
-
"Running inside package source tree - skipping harness install.",
|
|
118
|
-
);
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// Skip when explicitly opted out
|
|
123
125
|
if (process.env.GSD_SKIP_INSTALL === "1") {
|
|
124
126
|
log("skip", "GSD_SKIP_INSTALL=1 - skipping harness install.");
|
|
125
127
|
return;
|
|
126
128
|
}
|
|
127
129
|
|
|
128
|
-
const
|
|
129
|
-
const
|
|
130
|
+
const pkgVersion = getPackageVersion();
|
|
131
|
+
const gsdSrc = path.join(PKG_DIR, "gsd");
|
|
132
|
+
const gsdDest = path.join(PROJECT_ROOT, ".pi", "gsd");
|
|
133
|
+
const hooksSrc = path.join(gsdSrc, "hooks");
|
|
134
|
+
const hooksDest = path.join(PROJECT_ROOT, ".pi", "gsd", "hooks");
|
|
130
135
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
let totalCopied = 0;
|
|
138
|
-
let totalSkipped = 0;
|
|
139
|
-
let installed = 0;
|
|
140
|
-
|
|
141
|
-
for (const harness of HARNESSES) {
|
|
142
|
-
const srcHarness = path.join(harnessesRoot, harness.src);
|
|
143
|
-
const destHarness = path.join(PROJECT_ROOT, harness.dest);
|
|
136
|
+
// Detect installed version from a sample file's <gsd-version> tag.
|
|
137
|
+
const sample = path.join(gsdDest, "workflows", "plan-phase.md");
|
|
138
|
+
const installedTag = readGsdVersion(sample);
|
|
139
|
+
const installedVersion = installedTag?.version ?? null;
|
|
140
|
+
const versionChanged = installedVersion !== pkgVersion;
|
|
144
141
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
142
|
+
// In no-force mode, copyDir still updates individual files whose version
|
|
143
|
+
// tag doesn't match — so we only need to force-overwrite non-.md files
|
|
144
|
+
// (config.json, VERSION, hooks) when the version changes.
|
|
145
|
+
const overwriteAll = FORCE || versionChanged;
|
|
148
146
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
const { copied, skipped } = copyDir(srcGsd, destGsd, FORCE);
|
|
158
|
-
totalCopied += copied;
|
|
159
|
-
totalSkipped += skipped;
|
|
147
|
+
console.log("");
|
|
148
|
+
console.log(` GSD v${pkgVersion} - installing into project…`);
|
|
149
|
+
if (versionChanged && installedVersion) {
|
|
150
|
+
console.log(` (updating from v${installedVersion} → v${pkgVersion})`);
|
|
151
|
+
} else if (FORCE) {
|
|
152
|
+
console.log(" (force-reinstall: existing files will be overwritten)");
|
|
153
|
+
}
|
|
154
|
+
console.log("");
|
|
160
155
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
);
|
|
166
|
-
} else {
|
|
167
|
-
log(
|
|
168
|
-
"skip",
|
|
169
|
-
`${harness.dest}/${harness.subdir} (already up-to-date, ${skipped} file${skipped === 1 ? "" : "s"} skipped)`,
|
|
170
|
-
);
|
|
171
|
-
}
|
|
156
|
+
// ── Runtime harness → .pi/gsd/ ────────────────────────────────────────────
|
|
157
|
+
const { copied: gsdCopied, skipped: gsdSkipped } = copyDir(
|
|
158
|
+
gsdSrc, gsdDest, overwriteAll, ["prompts", "hooks"], pkgVersion,
|
|
159
|
+
);
|
|
172
160
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
161
|
+
if (gsdCopied > 0) {
|
|
162
|
+
log("ok", `.pi/gsd (${gsdCopied} file${gsdCopied === 1 ? "" : "s"} updated to v${pkgVersion})`);
|
|
163
|
+
} else {
|
|
164
|
+
log("skip", `.pi/gsd (v${pkgVersion} already installed, ${gsdSkipped} files skipped)`);
|
|
165
|
+
}
|
|
176
166
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
fs.copyFileSync(manifestSrc, manifestDest);
|
|
183
|
-
totalCopied++;
|
|
184
|
-
}
|
|
167
|
+
// ── Hook scripts → .pi/gsd/hooks/ ──────────────────────────────────────────
|
|
168
|
+
if (fs.existsSync(hooksSrc)) {
|
|
169
|
+
const { copied: hCopied } = copyDir(hooksSrc, hooksDest, overwriteAll, null, pkgVersion);
|
|
170
|
+
if (hCopied > 0) {
|
|
171
|
+
log("ok", `.pi/gsd/hooks (${hCopied} hook${hCopied === 1 ? "" : "s"} installed)`);
|
|
185
172
|
}
|
|
173
|
+
}
|
|
186
174
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
"ok",
|
|
197
|
-
`${harness.dest}/hooks (${h.copied} hook${h.copied === 1 ? "" : "s"} installed)`,
|
|
198
|
-
);
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
// ── skills/ (opencode only - present in .gsd/harnesses/opencode/skills) ─
|
|
203
|
-
const srcSkills = path.join(srcHarness, "skills");
|
|
204
|
-
const destSkills = path.join(destHarness, "skills");
|
|
205
|
-
|
|
206
|
-
if (fs.existsSync(srcSkills)) {
|
|
207
|
-
const s = copyDir(srcSkills, destSkills, FORCE);
|
|
208
|
-
totalCopied += s.copied;
|
|
209
|
-
totalSkipped += s.skipped;
|
|
210
|
-
|
|
211
|
-
if (s.copied > 0) {
|
|
212
|
-
log(
|
|
213
|
-
"ok",
|
|
214
|
-
`${harness.dest}/skills (${s.copied} skill file${s.copied === 1 ? "" : "s"} installed)`,
|
|
215
|
-
);
|
|
216
|
-
}
|
|
175
|
+
// ── Cleanup: stale .pi/hooks/ from old layout (pi ≥0.35 renames this dir) ──
|
|
176
|
+
const oldHooksDir = path.join(PROJECT_ROOT, ".pi", "hooks");
|
|
177
|
+
if (fs.existsSync(oldHooksDir)) {
|
|
178
|
+
const oldEntries = fs.readdirSync(oldHooksDir);
|
|
179
|
+
const allGsd = oldEntries.every((f) => f.startsWith("gsd-") && f.endsWith(".js"));
|
|
180
|
+
if (allGsd) {
|
|
181
|
+
for (const f of oldEntries) fs.rmSync(path.join(oldHooksDir, f));
|
|
182
|
+
fs.rmdirSync(oldHooksDir);
|
|
183
|
+
log("ok", ".pi/hooks (removed - hooks moved to .pi/gsd/hooks/)");
|
|
217
184
|
}
|
|
218
|
-
|
|
219
|
-
installed++;
|
|
220
185
|
}
|
|
221
186
|
|
|
222
|
-
// ──
|
|
223
|
-
// No local copying needed. Cleanup stale files from old install approach.
|
|
187
|
+
// ── Cleanup: stale files from old install layouts ──────────────────────────
|
|
224
188
|
const extDir = path.join(PROJECT_ROOT, ".pi", "extensions");
|
|
225
|
-
const
|
|
226
|
-
for (const name of staleExts) {
|
|
189
|
+
for (const name of ["gsd-hooks.ts", "pi-gsd-hooks.ts"]) {
|
|
227
190
|
const stale = path.join(extDir, name);
|
|
228
191
|
if (fs.existsSync(stale)) {
|
|
229
192
|
fs.rmSync(stale);
|
|
230
193
|
log("ok", `.pi/extensions/${name} (removed - extension now served from package)`);
|
|
231
194
|
}
|
|
232
195
|
}
|
|
233
|
-
const
|
|
234
|
-
if (fs.existsSync(
|
|
196
|
+
const settingsFile = path.join(PROJECT_ROOT, ".pi", "settings.json");
|
|
197
|
+
if (fs.existsSync(settingsFile)) {
|
|
235
198
|
try {
|
|
236
|
-
const
|
|
237
|
-
if (Array.isArray(
|
|
238
|
-
const
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
199
|
+
const settings = JSON.parse(fs.readFileSync(settingsFile, "utf8"));
|
|
200
|
+
if (Array.isArray(settings.extensions)) {
|
|
201
|
+
const staleNames = ["gsd-hooks.ts", "pi-gsd-hooks.ts"];
|
|
202
|
+
const cleaned = settings.extensions.filter((e) => !staleNames.some((n) => e.endsWith(n)));
|
|
203
|
+
if (cleaned.length !== settings.extensions.length) {
|
|
204
|
+
settings.extensions = cleaned;
|
|
205
|
+
fs.writeFileSync(settingsFile, JSON.stringify(settings, null, "\t"), "utf8");
|
|
242
206
|
log("ok", ".pi/settings.json (removed stale extension entries)");
|
|
243
207
|
}
|
|
244
208
|
}
|
|
245
209
|
} catch { /* ignore */ }
|
|
246
210
|
}
|
|
247
|
-
// ── Pi prompt templates - cleanup stale local copies ───────────────────────
|
|
248
|
-
// Prompts are served directly from the npm package (user scope).
|
|
249
|
-
// Local copies in .pi/prompts/ cause collision warnings on every pi update.
|
|
250
|
-
// Remove any gsd-*.md files previously installed there.
|
|
251
|
-
// Also remove the old gsd-hooks.ts if present (renamed to pi-gsd-hooks.ts).
|
|
252
|
-
const promptsDest = path.join(PROJECT_ROOT, ".pi", "prompts");
|
|
253
|
-
if (fs.existsSync(promptsDest)) {
|
|
254
|
-
const stale = fs
|
|
255
|
-
.readdirSync(promptsDest)
|
|
256
|
-
.filter((f) => f.startsWith("gsd-") && f.endsWith(".md"));
|
|
257
|
-
if (stale.length > 0) {
|
|
258
|
-
for (const f of stale) fs.rmSync(path.join(promptsDest, f));
|
|
259
|
-
log(
|
|
260
|
-
"ok",
|
|
261
|
-
`.pi/prompts (removed ${stale.length} stale local gsd-*.md - served from package instead)`,
|
|
262
|
-
);
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
const oldExt = path.join(PROJECT_ROOT, ".pi", "extensions", "gsd-hooks.ts");
|
|
266
|
-
if (fs.existsSync(oldExt)) {
|
|
267
|
-
fs.rmSync(oldExt);
|
|
268
|
-
log(
|
|
269
|
-
"ok",
|
|
270
|
-
".pi/extensions/gsd-hooks.ts (removed - renamed to pi-gsd-hooks.ts)",
|
|
271
|
-
);
|
|
272
|
-
}
|
|
273
211
|
|
|
274
212
|
console.log("");
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
log("warn", "No harness source directories found inside the package.");
|
|
278
|
-
log(
|
|
279
|
-
"warn",
|
|
280
|
-
"The package may be incomplete. Try: npm install --force get-shit-done-cc",
|
|
281
|
-
);
|
|
282
|
-
console.log("");
|
|
283
|
-
return;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
console.log(` GSD v${getPackageVersion()} installed successfully.`);
|
|
287
|
-
console.log(
|
|
288
|
-
` ${totalCopied} file${totalCopied === 1 ? "" : "s"} copied, ${totalSkipped} skipped.`,
|
|
289
|
-
);
|
|
213
|
+
console.log(` GSD v${pkgVersion} installed successfully.`);
|
|
214
|
+
console.log(` ${gsdCopied} file${gsdCopied === 1 ? "" : "s"} written.`);
|
|
290
215
|
console.log("");
|
|
291
216
|
console.log(" Next steps:");
|
|
292
217
|
console.log(" Run /gsd-new-project to initialise a project.");
|
|
@@ -295,132 +220,4 @@ function main() {
|
|
|
295
220
|
console.log("");
|
|
296
221
|
}
|
|
297
222
|
|
|
298
|
-
/**
|
|
299
|
-
* Read the version from this package's own package.json.
|
|
300
|
-
* Gracefully returns 'unknown' if the file is unreadable.
|
|
301
|
-
*
|
|
302
|
-
* @returns {string}
|
|
303
|
-
*/
|
|
304
|
-
function getPackageVersion() {
|
|
305
|
-
try {
|
|
306
|
-
const pkg = JSON.parse(
|
|
307
|
-
fs.readFileSync(path.join(PKG_DIR, "package.json"), "utf8"),
|
|
308
|
-
);
|
|
309
|
-
return pkg.version || "unknown";
|
|
310
|
-
} catch {
|
|
311
|
-
return "unknown";
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
/**
|
|
316
|
-
* Install the GSD pi extension into the consumer project's .pi/extensions/ directory.
|
|
317
|
-
* Also updates .pi/settings.json to include the extension in the extensions array.
|
|
318
|
-
*
|
|
319
|
-
* The extension registers three non-blocking pi lifecycle hooks:
|
|
320
|
-
* session_start → background GSD update check
|
|
321
|
-
* tool_call → workflow guard advisory (write/edit outside GSD context)
|
|
322
|
-
* tool_result → context usage monitor
|
|
323
|
-
*
|
|
324
|
-
* @param {string} projectRoot Consumer project root
|
|
325
|
-
* @param {string} pkgDir This package's root directory
|
|
326
|
-
* @param {boolean} force Overwrite existing files
|
|
327
|
-
* @param {function} callback Called with (copied: boolean)
|
|
328
|
-
*/
|
|
329
|
-
function installPiExtension(projectRoot, pkgDir, force, callback) {
|
|
330
|
-
const piDir = path.join(projectRoot, ".pi");
|
|
331
|
-
const extDir = path.join(piDir, "extensions");
|
|
332
|
-
const extDest = path.join(extDir, "pi-gsd-hooks.ts");
|
|
333
|
-
const extSrc = path.join(pkgDir, ".gsd", "extensions", "pi-gsd-hooks.ts");
|
|
334
|
-
|
|
335
|
-
if (!fs.existsSync(extSrc)) {
|
|
336
|
-
log("warn", ".pi/extensions/pi-gsd-hooks.ts (source absent - skipped)");
|
|
337
|
-
callback(false);
|
|
338
|
-
return;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
// Always update the extension - it is owned by pi-gsd, not the user.
|
|
342
|
-
// Compare pi-gsd-extension-version comments to detect staleness.
|
|
343
|
-
const extractVersion = (file) => {
|
|
344
|
-
try {
|
|
345
|
-
const match = fs
|
|
346
|
-
.readFileSync(file, "utf8")
|
|
347
|
-
.match(/pi-gsd-extension-version:\s*([\d.]+)/);
|
|
348
|
-
return match ? match[1] : null;
|
|
349
|
-
} catch {
|
|
350
|
-
return null;
|
|
351
|
-
}
|
|
352
|
-
};
|
|
353
|
-
const srcVersion = extractVersion(extSrc);
|
|
354
|
-
const destVersion = fs.existsSync(extDest) ? extractVersion(extDest) : null;
|
|
355
|
-
const needsUpdate = force || !destVersion || destVersion !== srcVersion;
|
|
356
|
-
|
|
357
|
-
if (!needsUpdate) {
|
|
358
|
-
log("skip", `.pi/extensions/pi-gsd-hooks.ts (up-to-date v${destVersion})`);
|
|
359
|
-
callback(false);
|
|
360
|
-
} else {
|
|
361
|
-
try {
|
|
362
|
-
fs.mkdirSync(extDir, { recursive: true });
|
|
363
|
-
fs.copyFileSync(extSrc, extDest);
|
|
364
|
-
log(
|
|
365
|
-
"ok",
|
|
366
|
-
".pi/extensions/pi-gsd-hooks.ts (GSD lifecycle extension installed)",
|
|
367
|
-
);
|
|
368
|
-
callback(true);
|
|
369
|
-
} catch (e) {
|
|
370
|
-
log(
|
|
371
|
-
"warn",
|
|
372
|
-
".pi/extensions/pi-gsd-hooks.ts (install failed: " + e.message + ")",
|
|
373
|
-
);
|
|
374
|
-
callback(false);
|
|
375
|
-
return;
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
// Update .pi/settings.json to include the extension path in the extensions array.
|
|
380
|
-
// The file is already auto-discovered from .pi/extensions/, but explicit registration
|
|
381
|
-
// is added as a belt-and-suspenders measure.
|
|
382
|
-
const settingsFile = path.join(piDir, "settings.json");
|
|
383
|
-
try {
|
|
384
|
-
let settings = {};
|
|
385
|
-
if (fs.existsSync(settingsFile)) {
|
|
386
|
-
try {
|
|
387
|
-
settings = JSON.parse(fs.readFileSync(settingsFile, "utf8"));
|
|
388
|
-
} catch {
|
|
389
|
-
// Unreadable settings - start fresh object
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
const extensions = Array.isArray(settings.extensions)
|
|
394
|
-
? settings.extensions
|
|
395
|
-
: [];
|
|
396
|
-
|
|
397
|
-
// Remove stale gsd-hooks.ts entry if present (renamed to pi-gsd-hooks.ts)
|
|
398
|
-
const oldExtPath = path.join(extDir, "gsd-hooks.ts");
|
|
399
|
-
const cleaned = extensions.filter((e) => e !== oldExtPath);
|
|
400
|
-
|
|
401
|
-
// Avoid duplicate entries
|
|
402
|
-
if (!cleaned.includes(extDest)) {
|
|
403
|
-
settings.extensions = [...cleaned, extDest];
|
|
404
|
-
fs.mkdirSync(piDir, { recursive: true });
|
|
405
|
-
fs.writeFileSync(
|
|
406
|
-
settingsFile,
|
|
407
|
-
JSON.stringify(settings, null, "\t"),
|
|
408
|
-
"utf8",
|
|
409
|
-
);
|
|
410
|
-
log("ok", ".pi/settings.json (extensions array updated)");
|
|
411
|
-
} else if (cleaned.length !== extensions.length) {
|
|
412
|
-
// Removed stale entry but extDest already present
|
|
413
|
-
settings.extensions = cleaned;
|
|
414
|
-
fs.writeFileSync(
|
|
415
|
-
settingsFile,
|
|
416
|
-
JSON.stringify(settings, null, "\t"),
|
|
417
|
-
"utf8",
|
|
418
|
-
);
|
|
419
|
-
log("ok", ".pi/settings.json (removed stale gsd-hooks.ts entry)");
|
|
420
|
-
}
|
|
421
|
-
} catch (e) {
|
|
422
|
-
log("warn", ".pi/settings.json (could not update: " + e.message + ")");
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
|
|
426
223
|
main();
|