@pareto4/pp-core 1.11.0 → 1.12.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/.claude-plugin/plugin.json +1 -1
- package/README.md +31 -0
- package/agents/pp-code-fixer.md +1 -1
- package/agents/pp-debug-session-manager.md +1 -1
- package/agents/pp-debugger.md +1 -1
- package/agents/pp-executor.md +20 -10
- package/agents/pp-intel-updater.md +1 -1
- package/agents/pp-phase-researcher.md +1 -1
- package/agents/pp-plan-checker.md +5 -5
- package/agents/pp-planner.md +10 -24
- package/agents/pp-project-researcher.md +1 -1
- package/agents/pp-research-synthesizer.md +1 -1
- package/agents/pp-ui-researcher.md +1 -1
- package/agents/pp-verifier.md +8 -4
- package/bin/install.js +429 -82
- package/commands/pp-autonomous.md +8 -6
- package/commands/pp-discovery-to-build.md +1 -1
- package/commands/pp-discovery.md +3 -2
- package/commands/pp-discuss-phase.md +2 -2
- package/commands/pp-execute-phase.md +3 -1
- package/commands/pp-graphify.md +5 -5
- package/commands/pp-import.md +1 -1
- package/commands/pp-map-codebase.md +1 -1
- package/commands/pp-ns-project.md +2 -3
- package/commands/pp-ns-workflow.md +1 -1
- package/commands/pp-plan-phase.md +2 -1
- package/commands/pp-verify-work.md +2 -1
- package/gemini-extension.json +1 -1
- package/hooks/dist/pp-cursor-session-start.js +2 -2
- package/hooks/dist/pp-session-state.sh +1 -1
- package/hooks/dist/pp-statusline.js +21 -2
- package/hooks/pp-cursor-session-start.js +2 -2
- package/hooks/pp-session-state.sh +1 -1
- package/hooks/pp-statusline.js +21 -2
- package/package.json +18 -5
- package/pareto-skills/pp-project-setup/SKILL.md +5 -63
- package/pp-core/bin/detect-build-commands.sh +182 -0
- package/pp-core/bin/lib/capability-registry.cjs +65 -7
- package/pp-core/bin/lib/check-command-router.cjs +79 -38
- package/pp-core/bin/lib/clusters.cjs +2 -3
- package/pp-core/bin/lib/command-aliases.cjs +36 -0
- package/pp-core/bin/lib/command-arg-projection.cjs +99 -1
- package/pp-core/bin/lib/commands.cjs +21 -6
- package/pp-core/bin/lib/compose-bindings.cjs +128 -15
- package/pp-core/bin/lib/config-schema.cjs +16 -0
- package/pp-core/bin/lib/config.cjs +142 -27
- package/pp-core/bin/lib/core-utils.cjs +43 -7
- package/pp-core/bin/lib/discovery-input-check.cjs +218 -0
- package/pp-core/bin/lib/discovery-rebaseline.cjs +1372 -0
- package/pp-core/bin/lib/discovery-research-track.cjs +979 -0
- package/pp-core/bin/lib/discovery-rollup.cjs +476 -0
- package/pp-core/bin/lib/discovery-screens.cjs +1995 -0
- package/pp-core/bin/lib/discovery-sweep.cjs +390 -0
- package/pp-core/bin/lib/discovery-table.cjs +98 -0
- package/pp-core/bin/lib/dispatch-plan.cjs +472 -0
- package/pp-core/bin/lib/frontmatter.cjs +17 -2
- package/pp-core/bin/lib/init-command-router.cjs +8 -5
- package/pp-core/bin/lib/init-contract.cjs +213 -33
- package/pp-core/bin/lib/init-interactive.cjs +20 -1
- package/pp-core/bin/lib/init.cjs +124 -134
- package/pp-core/bin/lib/install-profiles.cjs +5 -0
- package/pp-core/bin/lib/install-run.cjs +536 -0
- package/pp-core/bin/lib/io.cjs +15 -0
- package/pp-core/bin/lib/loop-resolver.cjs +24 -0
- package/pp-core/bin/lib/milestone.cjs +345 -48
- package/pp-core/bin/lib/phase-command-router.cjs +23 -1
- package/pp-core/bin/lib/phase-locator.cjs +15 -6
- package/pp-core/bin/lib/phase-scope.cjs +91 -0
- package/pp-core/bin/lib/phase-tier.cjs +467 -0
- package/pp-core/bin/lib/phase.cjs +247 -21
- package/pp-core/bin/lib/phases-command-router.cjs +2 -0
- package/pp-core/bin/lib/plan-graph.cjs +368 -0
- package/pp-core/bin/lib/planning-workspace.cjs +53 -0
- package/pp-core/bin/lib/platform-bindings.cjs +32 -2
- package/pp-core/bin/lib/profile-output.cjs +11 -3
- package/pp-core/bin/lib/prohibition-enforcement.cjs +112 -0
- package/pp-core/bin/lib/roadmap-parser.cjs +366 -99
- package/pp-core/bin/lib/roadmap.cjs +141 -39
- package/pp-core/bin/lib/runtime-hooks-surface.cjs +1 -1
- package/pp-core/bin/lib/shell-command-projection.cjs +24 -0
- package/pp-core/bin/lib/state-command-router.cjs +82 -13
- package/pp-core/bin/lib/state-document.cjs +124 -7
- package/pp-core/bin/lib/state.cjs +784 -257
- package/pp-core/bin/lib/template.cjs +22 -6
- package/pp-core/bin/lib/vault-command-router.cjs +57 -6
- package/pp-core/bin/lib/vault-practices.cjs +6 -36
- package/pp-core/bin/lib/vault-write-back.cjs +20 -4
- package/pp-core/bin/lib/vault.cjs +555 -23
- package/pp-core/bin/lib/verify.cjs +53 -31
- package/pp-core/bin/lib/workstream.cjs +1 -1
- package/pp-core/bin/lib/worktree-safety.cjs +26 -15
- package/pp-core/bin/portable-timeout.sh +89 -0
- package/pp-core/bin/pp-tools.cjs +158 -8
- package/pp-core/bin/shared/config-defaults.manifest.json +7 -0
- package/pp-core/bin/shared/config-schema.manifest.json +3 -0
- package/pp-core/references/autonomous-best-effort.md +461 -0
- package/pp-core/references/autonomous-lifecycle.md +146 -0
- package/pp-core/references/autonomous-smart-discuss.md +16 -12
- package/pp-core/references/checkpoints.md +1 -1
- package/pp-core/references/discovery-id-conventions.md +202 -0
- package/pp-core/references/discovery-rebaseline.md +254 -0
- package/pp-core/references/discovery-research-track.md +283 -0
- package/pp-core/references/discovery-traceability.md +400 -0
- package/pp-core/references/loop-hook-dispatch.md +7 -0
- package/pp-core/references/mandatory-initial-read.md +1 -1
- package/pp-core/references/planner-gap-closure.md +1 -0
- package/pp-core/references/planner-human-verify-mode.md +1 -1
- package/pp-core/references/planner-load-graph-context.md +1 -1
- package/pp-core/references/planner-plan-graph.md +66 -0
- package/pp-core/references/planner-tdd.md +1 -0
- package/pp-core/references/planning-config.md +8 -0
- package/pp-core/references/sketch-tooling.md +4 -0
- package/pp-core/references/tdd.md +1 -0
- package/pp-core/templates/claude-md.md +1 -1
- package/pp-core/templates/phase-prompt.md +6 -2
- package/pp-core/templates/prototype-review/review.js +303 -0
- package/pp-core/templates/state.md +6 -0
- package/pp-core/workflows/_runtime-launcher.snippet.sh +1 -1
- package/pp-core/workflows/add-backlog.md +1 -1
- package/pp-core/workflows/add-phase.md +1 -1
- package/pp-core/workflows/add-tests.md +1 -1
- package/pp-core/workflows/add-todo.md +1 -1
- package/pp-core/workflows/ai-integration-phase.md +2 -2
- package/pp-core/workflows/audit-fix.md +12 -16
- package/pp-core/workflows/audit-milestone.md +1 -1
- package/pp-core/workflows/audit-uat.md +1 -1
- package/pp-core/workflows/autonomous.md +68 -306
- package/pp-core/workflows/check-todos.md +1 -1
- package/pp-core/workflows/cleanup.md +1 -1
- package/pp-core/workflows/code-review-fix.md +1 -1
- package/pp-core/workflows/code-review.md +19 -7
- package/pp-core/workflows/complete-milestone.md +41 -60
- package/pp-core/workflows/debug.md +1 -1
- package/pp-core/workflows/diagnose-issues.md +1 -1
- package/pp-core/workflows/discovery-sprint/gap-analysis.md +140 -0
- package/pp-core/workflows/discovery-sprint/input-package.md +140 -0
- package/pp-core/workflows/discovery-sprint/prefill.md +92 -0
- package/pp-core/workflows/discovery-sprint/prototype.md +257 -0
- package/pp-core/workflows/discovery-sprint/rebaseline.md +276 -0
- package/pp-core/workflows/discovery-sprint/session-13-gates.md +24 -0
- package/pp-core/workflows/discovery-sprint/sessions/01-kick-off.md +92 -0
- package/pp-core/workflows/discovery-sprint/sessions/02-as-is.md +98 -0
- package/pp-core/workflows/discovery-sprint/sessions/03-to-be.md +215 -0
- package/pp-core/workflows/discovery-sprint/sessions/04-ux-design.md +146 -0
- package/pp-core/workflows/discovery-sprint/sessions/05-architecture.md +98 -0
- package/pp-core/workflows/discovery-sprint/sessions/06-security.md +107 -0
- package/pp-core/workflows/discovery-sprint/sessions/07-identity.md +98 -0
- package/pp-core/workflows/discovery-sprint/sessions/08-integration.md +85 -0
- package/pp-core/workflows/discovery-sprint/sessions/09-qa.md +109 -0
- package/pp-core/workflows/discovery-sprint/sessions/10-infrastructure.md +119 -0
- package/pp-core/workflows/discovery-sprint/sessions/11-roadmap.md +92 -0
- package/pp-core/workflows/discovery-sprint/sessions/12-delivery-model.md +98 -0
- package/pp-core/workflows/discovery-sprint/sessions/13-wrap-up.md +205 -0
- package/pp-core/workflows/discovery-sprint/templates/context-template.md +266 -0
- package/pp-core/workflows/discovery-sprint.md +79 -1714
- package/pp-core/workflows/discovery-to-build/steps/architecture-deliverables.md +5 -5
- package/pp-core/workflows/discovery-to-build/steps/screen-inventory-gate.md +70 -0
- package/pp-core/workflows/discovery-to-build/templates/discovery-references.md +54 -0
- package/pp-core/workflows/discovery-to-build/templates/pp-handoff.md +184 -0
- package/pp-core/workflows/discovery-to-build.md +21 -238
- package/pp-core/workflows/discuss-phase/modes/advisor.md +1 -1
- package/pp-core/workflows/discuss-phase/modes/auto.md +1 -1
- package/pp-core/workflows/discuss-phase/modes/chain.md +1 -1
- package/pp-core/workflows/discuss-phase-assumptions.md +1 -1
- package/pp-core/workflows/discuss-phase.md +9 -2
- package/pp-core/workflows/do.md +4 -4
- package/pp-core/workflows/docs-update.md +1 -1
- package/pp-core/workflows/edit-phase.md +1 -1
- package/pp-core/workflows/eval-review.md +1 -1
- package/pp-core/workflows/execute-phase/steps/checkpoint-handling.md +45 -0
- package/pp-core/workflows/execute-phase/steps/client-context-gate.md +1 -1
- package/pp-core/workflows/execute-phase/steps/codebase-drift-gate.md +1 -1
- package/pp-core/workflows/execute-phase/steps/cross-ai-delegation.md +90 -0
- package/pp-core/workflows/execute-phase/steps/install-run.md +29 -0
- package/pp-core/workflows/execute-phase/steps/phase-close-rebaseline.md +23 -0
- package/pp-core/workflows/execute-phase/steps/post-merge-gate.md +76 -77
- package/pp-core/workflows/execute-phase/steps/serial-group-dispatch.md +72 -0
- package/pp-core/workflows/execute-phase/steps/vault-fetch.md +5 -5
- package/pp-core/workflows/execute-phase/steps/vault-load-gate.md +19 -112
- package/pp-core/workflows/execute-phase.md +102 -216
- package/pp-core/workflows/execute-plan.md +6 -2
- package/pp-core/workflows/explore.md +1 -1
- package/pp-core/workflows/extract-learnings.md +1 -1
- package/pp-core/workflows/forensics.md +4 -4
- package/pp-core/workflows/graduation.md +1 -1
- package/pp-core/workflows/health.md +1 -1
- package/pp-core/workflows/help/modes/brief.md +1 -0
- package/pp-core/workflows/help/modes/default.md +2 -1
- package/pp-core/workflows/help/modes/full.md +12 -5
- package/pp-core/workflows/import.md +1 -1
- package/pp-core/workflows/ingest-docs.md +1 -1
- package/pp-core/workflows/init.md +18 -2
- package/pp-core/workflows/insert-phase.md +1 -1
- package/pp-core/workflows/list-workspaces.md +1 -1
- package/pp-core/workflows/manager.md +3 -3
- package/pp-core/workflows/map-codebase.md +2 -2
- package/pp-core/workflows/milestone-summary.md +4 -4
- package/pp-core/workflows/mvp-phase.md +1 -1
- package/pp-core/workflows/new-milestone.md +7 -8
- package/pp-core/workflows/new-project.md +6 -1
- package/pp-core/workflows/new-workspace.md +1 -1
- package/pp-core/workflows/next.md +2 -2
- package/pp-core/workflows/note.md +2 -2
- package/pp-core/workflows/pause-work.md +1 -1
- package/pp-core/workflows/plan-milestone-gaps.md +1 -1
- package/pp-core/workflows/plan-phase/steps/chunked-planning.md +111 -0
- package/pp-core/workflows/plan-phase/steps/phase-tier.md +34 -0
- package/pp-core/workflows/plan-phase.md +43 -167
- package/pp-core/workflows/plan-review-convergence.md +2 -2
- package/pp-core/workflows/plant-seed.md +1 -1
- package/pp-core/workflows/pr-branch.md +1 -1
- package/pp-core/workflows/profile-user.md +1 -1
- package/pp-core/workflows/progress.md +3 -3
- package/pp-core/workflows/quick.md +3 -1
- package/pp-core/workflows/remove-phase.md +1 -1
- package/pp-core/workflows/remove-workspace.md +1 -1
- package/pp-core/workflows/resume-project.md +6 -12
- package/pp-core/workflows/review.md +1 -1
- package/pp-core/workflows/scan.md +1 -1
- package/pp-core/workflows/secure-phase.md +1 -1
- package/pp-core/workflows/settings-advanced.md +1 -1
- package/pp-core/workflows/settings-integrations.md +1 -1
- package/pp-core/workflows/settings.md +1 -1
- package/pp-core/workflows/ship.md +25 -8
- package/pp-core/workflows/sketch-wrap-up.md +1 -1
- package/pp-core/workflows/sketch.md +2 -1
- package/pp-core/workflows/spec-phase.md +1 -1
- package/pp-core/workflows/spike-wrap-up.md +1 -1
- package/pp-core/workflows/spike.md +1 -1
- package/pp-core/workflows/stats.md +2 -2
- package/pp-core/workflows/thread.md +1 -1
- package/pp-core/workflows/transition.md +5 -9
- package/pp-core/workflows/ui-phase.md +23 -2
- package/pp-core/workflows/ui-review.md +1 -1
- package/pp-core/workflows/ultraplan-phase.md +2 -2
- package/pp-core/workflows/validate-phase.md +1 -1
- package/pp-core/workflows/vault-write-back.md +1 -1
- package/pp-core/workflows/verify-phase.md +29 -26
- package/pp-core/workflows/verify-work.md +14 -10
- package/scripts/base64-scan.sh +7 -41
- package/scripts/changeset/cli.cjs +25 -5
- package/scripts/dev-env.cjs +462 -0
- package/scripts/lib/allowlist-ratchet.cjs +103 -1
- package/scripts/lib/size-budget.cjs +311 -0
- package/scripts/lint-neutrality-names.cjs +694 -0
- package/scripts/lint-org-coordinates.allowlist.json +35 -0
- package/scripts/lint-org-coordinates.cjs +426 -0
- package/scripts/lint-skill-deps.cjs +48 -9
- package/scripts/lint-test-file-count.cjs +17 -2
- package/scripts/prompt-injection-scan.sh +41 -20
- package/scripts/sync-manifest-versions.cjs +41 -7
- package/scripts/sync-runtime-launcher.cjs +106 -1
- package/scripts/update-size-baseline.cjs +274 -30
- package/scripts/workflow-size.cjs +61 -9
- package/commands/pp-build-handoff.md +0 -46
- package/pp-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +0 -94
package/bin/install.js
CHANGED
|
@@ -186,22 +186,6 @@ const GSD_COPILOT_INSTRUCTIONS_CLOSE_MARKER = '<!-- /GSD Configuration -->';
|
|
|
186
186
|
// nothing that can dangle if a script copy is skipped. See
|
|
187
187
|
// https://docs.github.com/en/copilot/reference/hooks-configuration
|
|
188
188
|
const GSD_COPILOT_HOOK_FILE = 'pp-session.json';
|
|
189
|
-
// Copilot parses a command hook's stdout as the hook-output JSON. For sessionStart
|
|
190
|
-
// the schema is `{ additionalContext?: string }` (the text is prepended to the
|
|
191
|
-
// session as context). So the hook must emit that JSON envelope — not bare text.
|
|
192
|
-
// The two messages contain no JSON-special characters, so they embed verbatim.
|
|
193
|
-
const GSD_COPILOT_SESSION_MSG_PRESENT =
|
|
194
|
-
'GSD: .planning/STATE.md present - review the current phase and any blockers before acting.';
|
|
195
|
-
const GSD_COPILOT_SESSION_MSG_ABSENT =
|
|
196
|
-
'GSD: no .planning/ workflow found - run /pp-new-project to start a tracked workflow.';
|
|
197
|
-
const GSD_COPILOT_SESSION_HOOK_BASH =
|
|
198
|
-
'if [ -f .planning/STATE.md ]; then ' +
|
|
199
|
-
`printf '%s' '{"additionalContext":"${GSD_COPILOT_SESSION_MSG_PRESENT}"}'; else ` +
|
|
200
|
-
`printf '%s' '{"additionalContext":"${GSD_COPILOT_SESSION_MSG_ABSENT}"}'; fi`;
|
|
201
|
-
const GSD_COPILOT_SESSION_HOOK_PWSH =
|
|
202
|
-
'if (Test-Path .planning/STATE.md) ' +
|
|
203
|
-
`{ '{"additionalContext":"${GSD_COPILOT_SESSION_MSG_PRESENT}"}' } ` +
|
|
204
|
-
`else { '{"additionalContext":"${GSD_COPILOT_SESSION_MSG_ABSENT}"}' }`;
|
|
205
189
|
|
|
206
190
|
// #777 — Cursor CLI lifecycle hook constants.
|
|
207
191
|
// Cursor reads hook configs from <project-root>/.cursor/hooks.json (local) or
|
|
@@ -630,6 +614,203 @@ function computePathPrefix({ isGlobal, isOpencode, isWindowsHost: _isWindowsHost
|
|
|
630
614
|
return `${resolvedTarget}/`;
|
|
631
615
|
}
|
|
632
616
|
|
|
617
|
+
/**
|
|
618
|
+
* Does Claude Code substitute `${CLAUDE_PROJECT_DIR}` inside a user-scope
|
|
619
|
+
* SKILL.md body?
|
|
620
|
+
*
|
|
621
|
+
* MEASURED 2026-09-19, `09-PROBE.md` arm B: **yes** — a personal skill whose
|
|
622
|
+
* body contained `${CLAUDE_PROJECT_DIR}` was delivered to the model as
|
|
623
|
+
* `B: [<the project root>]`. (`$HOME`, `~` and
|
|
624
|
+
* `${VAR:-default}` are NOT substituted — arms E, G, D.)
|
|
625
|
+
*
|
|
626
|
+
* true → the dispatcher emits the literal `${CLAUDE_PROJECT_DIR}` and needs no
|
|
627
|
+
* shell step to find the project root.
|
|
628
|
+
* false → the dispatcher first instructs one Bash derivation
|
|
629
|
+
* (`git rev-parse --show-toplevel 2>/dev/null || pwd`) and refers to the
|
|
630
|
+
* result as PROJECT_ROOT.
|
|
631
|
+
*
|
|
632
|
+
* Either way the dispatcher is a Read instruction, never an `@` include:
|
|
633
|
+
* `09-PROBE.md` arms A, A2–A6 and H1 measured that `@` inlines no file at all,
|
|
634
|
+
* in any path form, so a static include could never express "local if present,
|
|
635
|
+
* else global" (D-09-15).
|
|
636
|
+
*/
|
|
637
|
+
const CLAUDE_PROJECT_DIR_SUBSTITUTES_IN_SKILL_BODY = true;
|
|
638
|
+
|
|
639
|
+
/**
|
|
640
|
+
* Replace every `@<prefix>pp-core/<rel>` include line in a GLOBAL Claude
|
|
641
|
+
* SKILL.md with a local-first dispatcher.
|
|
642
|
+
*
|
|
643
|
+
* Why (HINST-04 / D-09-14): Claude Code's documented skill precedence is
|
|
644
|
+
* Personal (`~/.claude/skills/`) > Project (`.claude/skills/`), confirmed on
|
|
645
|
+
* this build by `09-PROBE.md` arm F — a personal skill beat both a project
|
|
646
|
+
* command and a same-named project skill. A local pp-core install writes no
|
|
647
|
+
* `skills/` at all, so the personal skill is ALWAYS the one that answers, and
|
|
648
|
+
* its hard-coded global include made a 1.11.1 global workflow run over a 1.13.0
|
|
649
|
+
* project engine for ~47 days. Shipping the skill project-side would be dead
|
|
650
|
+
* weight; the fix is to make the personal skill resolve the project's install.
|
|
651
|
+
*
|
|
652
|
+
* @param {string} content SKILL.md text, after the path rewrites
|
|
653
|
+
* @param {object} args
|
|
654
|
+
* @param {string} args.pathPrefix trailing-slash prefix the rewrites emitted
|
|
655
|
+
* (`$HOME/.claude/` on a home-dir install)
|
|
656
|
+
* @param {string} args.absolutePrefix trailing-slash absolute config dir
|
|
657
|
+
* @returns {string} content, unchanged when no include line matched
|
|
658
|
+
*/
|
|
659
|
+
function rewriteGlobalClaudeSkillIncludes(content, { pathPrefix, absolutePrefix } = {}) {
|
|
660
|
+
if (!content || !pathPrefix || !absolutePrefix) return content;
|
|
661
|
+
const escapeRe = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
662
|
+
const prefixes = [...new Set([pathPrefix, absolutePrefix])].map(escapeRe);
|
|
663
|
+
const prefixAlt = '(?:' + prefixes.join('|') + ')';
|
|
664
|
+
// Transform A — a line whose only content is an `@`-include.
|
|
665
|
+
//
|
|
666
|
+
// G-4 (2026-09-20): the leading `(\\s*[-*+]\\s+)?` and trailing `(\\s+\\S.*?)?`
|
|
667
|
+
// groups are new. The 09-07 form was anchored `^@…$`, which could not see
|
|
668
|
+
// `- @$HOME/.claude/pp-core/workflows/complete-milestone.md (main workflow)`
|
|
669
|
+
// — a list-marker include with a trailing label. That shape reaches TWO
|
|
670
|
+
// references in pp-complete-milestone (one of them a `templates/` path), and
|
|
671
|
+
// the skill consequently carried no dispatcher at all.
|
|
672
|
+
// `\s*$` absorbs a trailing \r so CRLF-authored bodies match identically.
|
|
673
|
+
const includeRe = new RegExp(
|
|
674
|
+
'^(\\s*(?:[-*+]\\s+)?)@' + prefixAlt + 'pp-core/(\\S+?)(\\s+\\S.*?)?\\s*$'
|
|
675
|
+
);
|
|
676
|
+
// Transform B — an IN-PROSE reference, with or without `@`, on a line that is
|
|
677
|
+
// not a bare include. Three shapes ship today and none were visible to the
|
|
678
|
+
// 09-07 form:
|
|
679
|
+
// `Execute the ship workflow from @$HOME/…/ship.md end-to-end.`
|
|
680
|
+
// ``Read and execute `$HOME/…/discuss-phase.md` end-to-end.``
|
|
681
|
+
// `Follow $HOME/…/help.md with $ARGUMENTS.`
|
|
682
|
+
// Six skills carried a correct dispatcher AND one of these, so the run read
|
|
683
|
+
// the project-local workflow first and the GLOBAL copy next, under a
|
|
684
|
+
// `pp-core answered from:` line that only described the first read. A
|
|
685
|
+
// whole-line replacement would destroy the sentence, so this transform
|
|
686
|
+
// rewrites the PATH TOKEN in place and leaves the prose around it alone.
|
|
687
|
+
//
|
|
688
|
+
// TWO exclusions, and both are load-bearing — a first cut of this transform
|
|
689
|
+
// carried neither and corrupted the launcher in pp-discuss-phase, pp-graphify
|
|
690
|
+
// and pp-import, because the shim's `elif [ -f "$HOME/.claude/pp-core/bin/…" ]`
|
|
691
|
+
// branch is a prefixed reference like any other:
|
|
692
|
+
// (a) `pp-core/bin/…` is the shim's own engine lookup, never a document to
|
|
693
|
+
// read. `(?!bin/)` keeps it out.
|
|
694
|
+
// (b) anything inside a ``` fence is code, not prose. Rewriting there both
|
|
695
|
+
// mangles the shell AND — since the header block is emitted before the
|
|
696
|
+
// first touched line — injects markdown prose into the middle of a bash
|
|
697
|
+
// block, the same fence-breaking shape `sync-runtime-launcher` hit.
|
|
698
|
+
const proseRe = new RegExp('@?' + prefixAlt + 'pp-core/(?!bin/)([^\\s`)\\]]+)', 'g');
|
|
699
|
+
|
|
700
|
+
// Split on '\n' only (never a `\r?\n` rejoin): untouched lines keep their own
|
|
701
|
+
// line ending byte-for-byte, and each emitted line copies the ending of the
|
|
702
|
+
// include line it replaces.
|
|
703
|
+
const lines = content.split('\n');
|
|
704
|
+
const matchedIndexes = [];
|
|
705
|
+
const proseIndexes = [];
|
|
706
|
+
let inFence = false;
|
|
707
|
+
for (let i = 0; i < lines.length; i++) {
|
|
708
|
+
if (/^\s*(?:```|~~~)/.test(lines[i])) {
|
|
709
|
+
inFence = !inFence;
|
|
710
|
+
continue;
|
|
711
|
+
}
|
|
712
|
+
if (inFence) continue;
|
|
713
|
+
if (includeRe.test(lines[i])) {
|
|
714
|
+
matchedIndexes.push(i);
|
|
715
|
+
continue;
|
|
716
|
+
}
|
|
717
|
+
proseRe.lastIndex = 0;
|
|
718
|
+
if (proseRe.test(lines[i])) proseIndexes.push(i);
|
|
719
|
+
}
|
|
720
|
+
if (matchedIndexes.length === 0 && proseIndexes.length === 0) return content;
|
|
721
|
+
|
|
722
|
+
const projectRootExpr = CLAUDE_PROJECT_DIR_SUBSTITUTES_IN_SKILL_BODY
|
|
723
|
+
? '${CLAUDE_PROJECT_DIR}'
|
|
724
|
+
: 'PROJECT_ROOT';
|
|
725
|
+
const header = [
|
|
726
|
+
'<!-- pp-core install resolution (HINST-04, D-09-14): a project-local pp-core install answers before this global one. -->',
|
|
727
|
+
'Before anything else, resolve which pp-core install answers this skill:',
|
|
728
|
+
];
|
|
729
|
+
if (!CLAUDE_PROJECT_DIR_SUBSTITUTES_IN_SKILL_BODY) {
|
|
730
|
+
header.push('Run `git rev-parse --show-toplevel 2>/dev/null || pwd` once and call the result PROJECT_ROOT.');
|
|
731
|
+
}
|
|
732
|
+
const answeredFrom =
|
|
733
|
+
'Then print exactly one line: `pp-core answered from: <dir> (VERSION: <contents of <dir>/VERSION, ' +
|
|
734
|
+
'or "no VERSION file">)` where <dir> is the pp-core directory you read from (D-09-13).';
|
|
735
|
+
|
|
736
|
+
// The header and the `answered from` line are emitted ONCE, bracketing every
|
|
737
|
+
// rewritten reference in the file. When only Transform B matched
|
|
738
|
+
// (pp-discuss-phase, which states all three of its workflow hand-offs in
|
|
739
|
+
// prose) the block hangs off B's first and last line instead — otherwise
|
|
740
|
+
// those skills would resolve locally while printing no provenance line at
|
|
741
|
+
// all, which is the one outcome D-09-13 exists to prevent.
|
|
742
|
+
const allIndexes = [...matchedIndexes, ...proseIndexes].sort((a, b) => a - b);
|
|
743
|
+
const firstTouched = allIndexes[0];
|
|
744
|
+
const lastTouched = allIndexes[allIndexes.length - 1];
|
|
745
|
+
const proseSet = new Set(proseIndexes);
|
|
746
|
+
const out = [];
|
|
747
|
+
for (let i = 0; i < lines.length; i++) {
|
|
748
|
+
const line = lines[i];
|
|
749
|
+
const eol = line.endsWith('\r') ? '\r' : '';
|
|
750
|
+
const m = includeRe.exec(line);
|
|
751
|
+
if (!m && !proseSet.has(i)) {
|
|
752
|
+
out.push(line);
|
|
753
|
+
continue;
|
|
754
|
+
}
|
|
755
|
+
// The full header reads "Before anything else, resolve …", which is true
|
|
756
|
+
// above a bare include but NOT true spliced into a conditional branch —
|
|
757
|
+
// pp-discuss-phase states its three hand-offs under `If --assumptions …` /
|
|
758
|
+
// `Otherwise …`, and that sentence there would invite a resolve-and-read on
|
|
759
|
+
// a branch that was not taken. So a file with no bare include gets the
|
|
760
|
+
// marker comment only: each rewritten sentence already carries its own
|
|
761
|
+
// local-first conditional, and the provenance line still lands below.
|
|
762
|
+
if (i === firstTouched) {
|
|
763
|
+
const preamble = matchedIndexes.length > 0 ? header : header.slice(0, 1);
|
|
764
|
+
for (const h of preamble) out.push(h + eol);
|
|
765
|
+
}
|
|
766
|
+
if (m) {
|
|
767
|
+
const [, marker, rel] = m;
|
|
768
|
+
// Emit at the include's own indentation, always as a `- ` bullet: a bare
|
|
769
|
+
// column-0 include keeps the exact 09-07 output, and an indented list
|
|
770
|
+
// include stays inside its list.
|
|
771
|
+
const indent = /^\s*/.exec(marker)[0];
|
|
772
|
+
out.push(
|
|
773
|
+
indent + '- Read `' + projectRootExpr + '/.claude/pp-core/' + rel + '` if that file exists; ' +
|
|
774
|
+
'otherwise Read `' + absolutePrefix + 'pp-core/' + rel + '`. ' +
|
|
775
|
+
"Follow whichever you read as this skill's execution context." + eol
|
|
776
|
+
);
|
|
777
|
+
} else {
|
|
778
|
+
proseRe.lastIndex = 0;
|
|
779
|
+
out.push(
|
|
780
|
+
line.replace(proseRe, (_match, rel) =>
|
|
781
|
+
projectRootExpr + '/.claude/pp-core/' + rel +
|
|
782
|
+
' (or ' + absolutePrefix + 'pp-core/' + rel +
|
|
783
|
+
' if no project-local install exists)'
|
|
784
|
+
)
|
|
785
|
+
);
|
|
786
|
+
}
|
|
787
|
+
if (i === lastTouched) out.push(answeredFrom + eol);
|
|
788
|
+
}
|
|
789
|
+
return out.join('\n');
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
/**
|
|
793
|
+
* The ONE owner of "which settings file does this runtime+scope use".
|
|
794
|
+
*
|
|
795
|
+
* #338: a local Claude install writes to settings.local.json — Claude Code's
|
|
796
|
+
* per-user, gitignored slot — so engineer-specific absolute paths (Node binary,
|
|
797
|
+
* home dir) never land in the repo-shared settings.json. Global installs and
|
|
798
|
+
* every non-Claude runtime use settings.json.
|
|
799
|
+
*
|
|
800
|
+
* D-09-06: install and uninstall MUST share this one decision. They did not:
|
|
801
|
+
* install resolved the name through an inline ternary while uninstall hardcoded
|
|
802
|
+
* 'settings.json', so a local uninstall never opened the file the matching
|
|
803
|
+
* install had written and left every pp-core hook entry behind, pointing at
|
|
804
|
+
* scripts it had just deleted. Both paths now call this function.
|
|
805
|
+
*
|
|
806
|
+
* @param {string} runtime - Target runtime ('claude', 'gemini', ...)
|
|
807
|
+
* @param {boolean} isGlobal - Whether this is a global install
|
|
808
|
+
* @returns {string} Settings file basename
|
|
809
|
+
*/
|
|
810
|
+
function resolveSettingsFileName(runtime, isGlobal) {
|
|
811
|
+
return runtime === 'claude' && !isGlobal ? 'settings.local.json' : 'settings.json';
|
|
812
|
+
}
|
|
813
|
+
|
|
633
814
|
// normalizeNodePath, resolveNodeRunner, resolveBashRunner, referencesHook are
|
|
634
815
|
// now owned by the runtime-hooks-surface module. Import them here so
|
|
635
816
|
// install.js callers continue to work and so there is a single implementation
|
|
@@ -1726,8 +1907,8 @@ function convertClaudeToCopilotContent(content, isGlobal = false) {
|
|
|
1726
1907
|
} else {
|
|
1727
1908
|
c = c.replace(/\$HOME\/\.claude\//g, '.github/');
|
|
1728
1909
|
c = c.replace(/~\/\.claude\//g, '.github/');
|
|
1729
|
-
c = c.replace(/\$HOME\/\.claude\
|
|
1730
|
-
c = c.replace(/~\/\.claude\
|
|
1910
|
+
c = c.replace(/\$HOME\/\.claude(?![\w-])/g, '.github');
|
|
1911
|
+
c = c.replace(/~\/\.claude(?![\w-])/g, '.github');
|
|
1731
1912
|
}
|
|
1732
1913
|
c = c.replace(/\.\/\.claude\//g, './.github/');
|
|
1733
1914
|
c = c.replace(/\.claude\//g, '.github/');
|
|
@@ -2228,14 +2409,14 @@ function convertClaudeToAntigravityContent(content, isGlobal = false) {
|
|
|
2228
2409
|
c = c.replace(/\$HOME\/\.claude\//g, '$HOME/.gemini/antigravity/');
|
|
2229
2410
|
c = c.replace(/~\/\.claude\//g, '~/.gemini/antigravity/');
|
|
2230
2411
|
// Bare form (no trailing slash) — must come after slash form to avoid double-replace
|
|
2231
|
-
c = c.replace(/\$HOME\/\.claude\
|
|
2232
|
-
c = c.replace(/~\/\.claude\
|
|
2412
|
+
c = c.replace(/\$HOME\/\.claude(?![\w-])/g, '$HOME/.gemini/antigravity');
|
|
2413
|
+
c = c.replace(/~\/\.claude(?![\w-])/g, '~/.gemini/antigravity');
|
|
2233
2414
|
} else {
|
|
2234
2415
|
c = c.replace(/\$HOME\/\.claude\//g, '.agents/');
|
|
2235
2416
|
c = c.replace(/~\/\.claude\//g, '.agents/');
|
|
2236
2417
|
// Bare form (no trailing slash) — must come after slash form to avoid double-replace
|
|
2237
|
-
c = c.replace(/\$HOME\/\.claude\
|
|
2238
|
-
c = c.replace(/~\/\.claude\
|
|
2418
|
+
c = c.replace(/\$HOME\/\.claude(?![\w-])/g, '.agents');
|
|
2419
|
+
c = c.replace(/~\/\.claude(?![\w-])/g, '.agents');
|
|
2239
2420
|
}
|
|
2240
2421
|
c = c.replace(/\.\/\.claude\//g, './.agents/');
|
|
2241
2422
|
c = c.replace(/\.claude\//g, '.agents/');
|
|
@@ -2849,8 +3030,8 @@ function convertClaudeToCliineMarkdown(content) {
|
|
|
2849
3030
|
converted = converted.replace(/\.\/\.claude\//g, './.cline/');
|
|
2850
3031
|
converted = converted.replace(/\.claude\//g, '.cline/');
|
|
2851
3032
|
// Bare forms (no trailing slash) — after slash forms to avoid double-rewrite
|
|
2852
|
-
converted = converted.replace(/~\/\.claude\
|
|
2853
|
-
converted = converted.replace(/\$HOME\/\.claude\
|
|
3033
|
+
converted = converted.replace(/~\/\.claude(?![\w-])/g, '~/.cline');
|
|
3034
|
+
converted = converted.replace(/\$HOME\/\.claude(?![\w-])/g, '$HOME/.cline');
|
|
2854
3035
|
// Environment variable name rewrite
|
|
2855
3036
|
converted = converted.replace(/\bCLAUDE_CONFIG_DIR\b/g, 'CLINE_CONFIG_DIR');
|
|
2856
3037
|
converted = converted.replace(/\*\*Known Claude Code bug \(classifyHandoffIfNeeded\):\*\*[^\n]*\n/g, '');
|
|
@@ -2955,8 +3136,8 @@ function convertClaudeToCodexMarkdown(content) {
|
|
|
2955
3136
|
converted = converted.replace(/~\/\.claude\//g, '~/.codex/');
|
|
2956
3137
|
converted = converted.replace(/\.\/\.claude\//g, './.codex/');
|
|
2957
3138
|
// Bare ~/.claude without trailing slash (e.g. configDir = ~/.claude)
|
|
2958
|
-
converted = converted.replace(/\$HOME\/\.claude\
|
|
2959
|
-
converted = converted.replace(/~\/\.claude\
|
|
3139
|
+
converted = converted.replace(/\$HOME\/\.claude(?![\w-])/g, '$HOME/.codex');
|
|
3140
|
+
converted = converted.replace(/~\/\.claude(?![\w-])/g, '~/.codex');
|
|
2960
3141
|
// Bare/project-relative .claude/... references (#2639). Covers strings like
|
|
2961
3142
|
// "check `.claude/skills/`" where there is no ~/, $HOME/, or ./ anchor.
|
|
2962
3143
|
// Negative lookbehind prevents double-replacing already-anchored forms and
|
|
@@ -5943,8 +6124,8 @@ function convertClaudeToOpencodeFrontmatter(content, { isAgent = false, modelOve
|
|
|
5943
6124
|
// Replace /gsd-command colon variant with /gsd-command for opencode (flat command structure)
|
|
5944
6125
|
convertedContent = convertedContent.replace(/\/gsd:/g, '/gsd-');
|
|
5945
6126
|
// Replace ~/.claude and $HOME/.claude with OpenCode's config location
|
|
5946
|
-
convertedContent = convertedContent.replace(/~\/\.claude\
|
|
5947
|
-
convertedContent = convertedContent.replace(/\$HOME\/\.claude\
|
|
6127
|
+
convertedContent = convertedContent.replace(/~\/\.claude(?![\w-])/g, '~/.config/opencode');
|
|
6128
|
+
convertedContent = convertedContent.replace(/\$HOME\/\.claude(?![\w-])/g, '$HOME/.config/opencode');
|
|
5948
6129
|
// Replace general-purpose subagent type with OpenCode's equivalent "general"
|
|
5949
6130
|
convertedContent = convertedContent.replace(/subagent_type="general-purpose"/g, 'subagent_type="general"');
|
|
5950
6131
|
// Runtime-neutral agent name replacement (#766)
|
|
@@ -6100,8 +6281,8 @@ function convertClaudeToKiloFrontmatter(content, { isAgent = false } = {}) {
|
|
|
6100
6281
|
// Replace /gsd-command colon variant with /gsd-command for Kilo (flat command structure)
|
|
6101
6282
|
convertedContent = convertedContent.replace(/\/gsd:/g, '/gsd-');
|
|
6102
6283
|
// Replace ~/.claude and $HOME/.claude with Kilo's config location
|
|
6103
|
-
convertedContent = convertedContent.replace(/~\/\.claude\
|
|
6104
|
-
convertedContent = convertedContent.replace(/\$HOME\/\.claude\
|
|
6284
|
+
convertedContent = convertedContent.replace(/~\/\.claude(?![\w-])/g, '~/.config/kilo');
|
|
6285
|
+
convertedContent = convertedContent.replace(/\$HOME\/\.claude(?![\w-])/g, '$HOME/.config/kilo');
|
|
6105
6286
|
convertedContent = convertedContent.replace(/\.\/\.claude\//g, './.kilo/');
|
|
6106
6287
|
// Normalize both Claude skill directory variants to Kilo's canonical skills dir.
|
|
6107
6288
|
convertedContent = replaceRelativePathReference(convertedContent, '.claude/skills/', '.kilo/skills/');
|
|
@@ -6554,7 +6735,7 @@ function writeHermesCategoryDescription(categoryDir) {
|
|
|
6554
6735
|
'---',
|
|
6555
6736
|
'name: gsd',
|
|
6556
6737
|
`version: ${pkg.version}`,
|
|
6557
|
-
'description: pp-core — The Pareto delivery framework. Disciplined planning, execution, and shipping workflows. Use any pp-* skill in this category to drive a project through new-project → discuss-phase → plan-phase → execute-phase → ship.',
|
|
6738
|
+
'description: pp-core — The Pareto delivery framework. Disciplined planning, execution, and shipping workflows. Use any pp-* skill in this category to drive a project through init → new-project → discuss-phase → plan-phase → execute-phase → ship.',
|
|
6558
6739
|
'---',
|
|
6559
6740
|
'',
|
|
6560
6741
|
'# pp-core',
|
|
@@ -6696,8 +6877,10 @@ function migrateLegacyDevPreferencesToSkill(targetDir, saved, runtime, scope = '
|
|
|
6696
6877
|
* @param {string} runtime
|
|
6697
6878
|
* @param {string} pathPrefix e.g. "~/.codex/" — trailing-slash string
|
|
6698
6879
|
* @param {boolean} [isGlobal=false] true when the install is a global (home-dir) install
|
|
6880
|
+
* @param {object} [opts={}] extra rewrite inputs; `opts.configDir` supplies the
|
|
6881
|
+
* absolute global install path the HINST-04 dispatcher names in its else-clause
|
|
6699
6882
|
*/
|
|
6700
|
-
function applyRuntimeContentRewritesInPlace(stagedDir, runtime, pathPrefix, isGlobal = false) {
|
|
6883
|
+
function applyRuntimeContentRewritesInPlace(stagedDir, runtime, pathPrefix, isGlobal = false, opts = {}) {
|
|
6701
6884
|
if (!fs.existsSync(stagedDir)) return;
|
|
6702
6885
|
|
|
6703
6886
|
// Walk all SKILL.md files under stagedDir
|
|
@@ -6708,7 +6891,7 @@ function applyRuntimeContentRewritesInPlace(stagedDir, runtime, pathPrefix, isGl
|
|
|
6708
6891
|
walkAndRewrite(fullPath);
|
|
6709
6892
|
} else if (entry.name.endsWith('.md')) {
|
|
6710
6893
|
let content = fs.readFileSync(fullPath, 'utf8');
|
|
6711
|
-
content = _applyRuntimeRewrites(content, runtime, pathPrefix, isGlobal);
|
|
6894
|
+
content = _applyRuntimeRewrites(content, runtime, pathPrefix, isGlobal, opts);
|
|
6712
6895
|
fs.writeFileSync(fullPath, content);
|
|
6713
6896
|
}
|
|
6714
6897
|
}
|
|
@@ -6765,9 +6948,10 @@ function applyRuntimeContentRewritesForCommandsInPlace(stagedDir, runtime, pathP
|
|
|
6765
6948
|
* @param {string} runtime
|
|
6766
6949
|
* @param {string} pathPrefix trailing-slash string
|
|
6767
6950
|
* @param {boolean} [isGlobal=false] true when the install is a global (home-dir) install
|
|
6951
|
+
* @param {object} [opts={}] extra rewrite inputs (see applyRuntimeContentRewritesInPlace)
|
|
6768
6952
|
* @returns {string}
|
|
6769
6953
|
*/
|
|
6770
|
-
function _applyRuntimeRewrites(content, runtime, pathPrefix, isGlobal = false) {
|
|
6954
|
+
function _applyRuntimeRewrites(content, runtime, pathPrefix, isGlobal = false, opts = {}) {
|
|
6771
6955
|
const dirName = getDirName(runtime);
|
|
6772
6956
|
const normalizedPathPrefix = pathPrefix.replace(/\/$/, '');
|
|
6773
6957
|
|
|
@@ -6789,8 +6973,8 @@ function _applyRuntimeRewrites(content, runtime, pathPrefix, isGlobal = false) {
|
|
|
6789
6973
|
content = content.replace(/~\/\.cline\//g, pathPrefix);
|
|
6790
6974
|
content = content.replace(/\$HOME\/\.cline\//g, pathPrefix);
|
|
6791
6975
|
// Bare forms (no trailing slash)
|
|
6792
|
-
content = content.replace(/~\/\.claude\
|
|
6793
|
-
content = content.replace(/\$HOME\/\.claude\
|
|
6976
|
+
content = content.replace(/~\/\.claude(?![\w-])/g, normalizedPathPrefix);
|
|
6977
|
+
content = content.replace(/\$HOME\/\.claude(?![\w-])/g, normalizedPathPrefix);
|
|
6794
6978
|
content = content.replace(/~\/\.cline\b/g, normalizedPathPrefix);
|
|
6795
6979
|
content = content.replace(/\$HOME\/\.cline\b/g, normalizedPathPrefix);
|
|
6796
6980
|
content = processAttribution(content, getCommitAttribution(runtime));
|
|
@@ -6853,9 +7037,9 @@ function _applyRuntimeRewrites(content, runtime, pathPrefix, isGlobal = false) {
|
|
|
6853
7037
|
content = content.replace(/~\/\.claude\//g, pathPrefix);
|
|
6854
7038
|
content = content.replace(/\$HOME\/\.claude\//g, pathPrefix);
|
|
6855
7039
|
content = content.replace(/\.\/\.claude\//g, `./${dirName}/`);
|
|
6856
|
-
content = content.replace(/~\/\.claude\
|
|
6857
|
-
content = content.replace(/\$HOME\/\.claude\
|
|
6858
|
-
content = content.replace(/\.\/\.claude\
|
|
7040
|
+
content = content.replace(/~\/\.claude(?![\w-])/g, normalizedPathPrefix);
|
|
7041
|
+
content = content.replace(/\$HOME\/\.claude(?![\w-])/g, normalizedPathPrefix);
|
|
7042
|
+
content = content.replace(/\.\/\.claude(?![\w-])/g, `./${dirName}`);
|
|
6859
7043
|
content = content.replace(/~\/\.trae\//g, pathPrefix);
|
|
6860
7044
|
content = processAttribution(content, getCommitAttribution(runtime));
|
|
6861
7045
|
break;
|
|
@@ -6864,9 +7048,9 @@ function _applyRuntimeRewrites(content, runtime, pathPrefix, isGlobal = false) {
|
|
|
6864
7048
|
content = content.replace(/~\/\.claude\//g, pathPrefix);
|
|
6865
7049
|
content = content.replace(/\$HOME\/\.claude\//g, pathPrefix);
|
|
6866
7050
|
content = content.replace(/\.\/\.claude\//g, `./${dirName}/`);
|
|
6867
|
-
content = content.replace(/~\/\.claude\
|
|
6868
|
-
content = content.replace(/\$HOME\/\.claude\
|
|
6869
|
-
content = content.replace(/\.\/\.claude\
|
|
7051
|
+
content = content.replace(/~\/\.claude(?![\w-])/g, normalizedPathPrefix);
|
|
7052
|
+
content = content.replace(/\$HOME\/\.claude(?![\w-])/g, normalizedPathPrefix);
|
|
7053
|
+
content = content.replace(/\.\/\.claude(?![\w-])/g, `./${dirName}`);
|
|
6870
7054
|
// The codebuddy converter rewrites `.claude/` → `.codebuddy/` at stage
|
|
6871
7055
|
// time, so `$HOME/.claude/...` arrives here as `$HOME/.codebuddy/...`.
|
|
6872
7056
|
// Normalize BOTH the `~/` and `$HOME/` forms (slash + bare) to the install
|
|
@@ -6892,6 +7076,16 @@ function _applyRuntimeRewrites(content, runtime, pathPrefix, isGlobal = false) {
|
|
|
6892
7076
|
content = content.replace(/~\/\.claude\//g, pathPrefix);
|
|
6893
7077
|
content = content.replace(/\$HOME\/\.claude\//g, pathPrefix);
|
|
6894
7078
|
content = content.replace(/\.\/\.claude\//g, `./${dirName}/`);
|
|
7079
|
+
// HINST-04 / D-09-14: only the GLOBAL Claude skills kind reaches this —
|
|
7080
|
+
// `opts.configDir` is supplied by installRuntimeArtifacts for that kind
|
|
7081
|
+
// alone, and the local commands path never passes opts. A local install
|
|
7082
|
+
// writes no skills/, so its commands keep their absolute include (D-09-12).
|
|
7083
|
+
if (isGlobal && opts && opts.configDir) {
|
|
7084
|
+
content = rewriteGlobalClaudeSkillIncludes(content, {
|
|
7085
|
+
pathPrefix,
|
|
7086
|
+
absolutePrefix: path.resolve(opts.configDir).replace(/\\/g, '/').replace(/\/$/, '') + '/',
|
|
7087
|
+
});
|
|
7088
|
+
}
|
|
6895
7089
|
content = processAttribution(content, getCommitAttribution(runtime));
|
|
6896
7090
|
break;
|
|
6897
7091
|
|
|
@@ -6940,9 +7134,9 @@ function _applyRuntimeRewrites(content, runtime, pathPrefix, isGlobal = false) {
|
|
|
6940
7134
|
content = content.replace(/~\/\.claude\//g, pathPrefix);
|
|
6941
7135
|
content = content.replace(/\$HOME\/\.claude\//g, pathPrefix);
|
|
6942
7136
|
content = content.replace(/\.\/\.claude\//g, `./${dirName}/`);
|
|
6943
|
-
content = content.replace(/~\/\.claude\
|
|
6944
|
-
content = content.replace(/\$HOME\/\.claude\
|
|
6945
|
-
content = content.replace(/\.\/\.claude\
|
|
7137
|
+
content = content.replace(/~\/\.claude(?![\w-])/g, normalizedPathPrefix);
|
|
7138
|
+
content = content.replace(/\$HOME\/\.claude(?![\w-])/g, normalizedPathPrefix);
|
|
7139
|
+
content = content.replace(/\.\/\.claude(?![\w-])/g, `./${dirName}`);
|
|
6946
7140
|
content = processAttribution(content, getCommitAttribution(runtime));
|
|
6947
7141
|
break;
|
|
6948
7142
|
|
|
@@ -7326,7 +7520,7 @@ function installRuntimeArtifacts(runtime, configDir, scope, resolvedProfile) {
|
|
|
7326
7520
|
let stagedForCopy = staged;
|
|
7327
7521
|
const isGlobal = scope === 'global';
|
|
7328
7522
|
if (kind.kind === 'skills' || kind.kind === 'kimi-agents') {
|
|
7329
|
-
applyRuntimeContentRewritesInPlace(staged, runtime, pathPrefix, isGlobal);
|
|
7523
|
+
applyRuntimeContentRewritesInPlace(staged, runtime, pathPrefix, isGlobal, { configDir });
|
|
7330
7524
|
} else if (kind.kind === 'commands') {
|
|
7331
7525
|
// Returns a temp dir with rewritten content so source files are never mutated.
|
|
7332
7526
|
stagedForCopy = applyRuntimeContentRewritesForCommandsInPlace(staged, runtime, pathPrefix, isGlobal);
|
|
@@ -7693,9 +7887,9 @@ function copyWithPathReplacement(srcDir, destDir, pathPrefix, runtime, isCommand
|
|
|
7693
7887
|
content = content.replace(globalClaudeRegex, pathPrefix);
|
|
7694
7888
|
content = content.replace(globalClaudeHomeRegex, pathPrefix);
|
|
7695
7889
|
content = content.replace(localClaudeRegex, `./${dirName}/`);
|
|
7696
|
-
content = content.replace(/~\/\.claude\
|
|
7697
|
-
content = content.replace(/\$HOME\/\.claude\
|
|
7698
|
-
content = content.replace(/\.\/\.claude\
|
|
7890
|
+
content = content.replace(/~\/\.claude(?![\w-])/g, pathPrefix.replace(/\/$/, ''));
|
|
7891
|
+
content = content.replace(/\$HOME\/\.claude(?![\w-])/g, pathPrefix.replace(/\/$/, ''));
|
|
7892
|
+
content = content.replace(/\.\/\.claude(?![\w-])/g, `./${dirName}`);
|
|
7699
7893
|
content = content.replace(/~\/\.qwen\//g, pathPrefix);
|
|
7700
7894
|
content = content.replace(/\$HOME\/\.qwen\//g, pathPrefix);
|
|
7701
7895
|
content = content.replace(/\.\/\.qwen\//g, `./${dirName}/`);
|
|
@@ -8005,8 +8199,42 @@ const GSD_UNINSTALL_HOOKS = [
|
|
|
8005
8199
|
'pp-validate-commit.sh',
|
|
8006
8200
|
'pp-phase-boundary.sh',
|
|
8007
8201
|
'pp-graphify-update.sh',
|
|
8202
|
+
// D-09-08: absent from this list until phase 9 while still being registered
|
|
8203
|
+
// in the settings file, so a project the operator believed was uninstalled
|
|
8204
|
+
// kept a live pp-core PreToolUse hook on disk and wired.
|
|
8205
|
+
'pp-worktree-path-guard.js',
|
|
8008
8206
|
];
|
|
8009
8207
|
|
|
8208
|
+
/**
|
|
8209
|
+
* Files pp-core ships into the runtime hooks/ dir and deliberately does NOT
|
|
8210
|
+
* remove on uninstall, each with the reason. D-09-08 locks the retention and
|
|
8211
|
+
* requires uninstall to NAME what it keeps rather than leave it silently.
|
|
8212
|
+
*
|
|
8213
|
+
* The pin in tests/hooks-opt-in.test.cjs asserts that this table plus
|
|
8214
|
+
* GSD_UNINSTALL_HOOKS together cover every name in
|
|
8215
|
+
* hooks/managed-hooks-registry.cjs (plus managed-hooks-registry.cjs itself,
|
|
8216
|
+
* which ships via HOOKS_TO_COPY and is not a hook), so the two lists cannot
|
|
8217
|
+
* drift apart silently again.
|
|
8218
|
+
*/
|
|
8219
|
+
const GSD_UNINSTALL_RETAINED_HOOKS = Object.freeze([
|
|
8220
|
+
Object.freeze({
|
|
8221
|
+
file: 'managed-hooks-registry.cjs',
|
|
8222
|
+
reason: 'retained per D-09-08: the list pp-check-update-worker.js requires at runtime; '
|
|
8223
|
+
+ 'not a hook, never registered in any settings file, referenced by no settings entry after uninstall',
|
|
8224
|
+
}),
|
|
8225
|
+
Object.freeze({
|
|
8226
|
+
file: 'pp-check-update-worker.js',
|
|
8227
|
+
reason: 'retained per D-09-08: a detached worker that pp-check-update.js spawns by path on '
|
|
8228
|
+
+ 'SessionStart, never registered as a hook; a worker still running from the last session keeps '
|
|
8229
|
+
+ 'resolving its require',
|
|
8230
|
+
}),
|
|
8231
|
+
Object.freeze({
|
|
8232
|
+
file: 'pp-ensure-canonical-path.js',
|
|
8233
|
+
reason: 'retained per D-09-08: the marketplace-plugin SessionStart bootstrap (#997); a classic '
|
|
8234
|
+
+ 'install never registers it, so no settings entry references it after uninstall',
|
|
8235
|
+
}),
|
|
8236
|
+
]);
|
|
8237
|
+
|
|
8010
8238
|
/**
|
|
8011
8239
|
* Uninstall pp-core from the specified directory for a specific runtime
|
|
8012
8240
|
* Removes only GSD-specific files/directories, preserves user content
|
|
@@ -8405,6 +8633,9 @@ function uninstall(isGlobal, runtime = 'claude') {
|
|
|
8405
8633
|
}
|
|
8406
8634
|
|
|
8407
8635
|
// 4. Remove pp-core hooks
|
|
8636
|
+
// Populated by the removal block below and reported in the completion message
|
|
8637
|
+
// (D-09-08): uninstall names the files it deliberately keeps.
|
|
8638
|
+
let retainedHooks = [];
|
|
8408
8639
|
const hooksDir = path.join(targetDir, 'hooks');
|
|
8409
8640
|
if (fs.existsSync(hooksDir)) {
|
|
8410
8641
|
let hookCount = 0;
|
|
@@ -8420,6 +8651,16 @@ function uninstall(isGlobal, runtime = 'claude') {
|
|
|
8420
8651
|
console.log(` ${green}✓${reset} Removed ${hookCount} pp-core hooks`);
|
|
8421
8652
|
}
|
|
8422
8653
|
|
|
8654
|
+
// D-09-08: name what survives, and why. Driven by what is actually still on
|
|
8655
|
+
// disk rather than by the table alone, so a runtime that never received a
|
|
8656
|
+
// given file is never told it was kept.
|
|
8657
|
+
retainedHooks = GSD_UNINSTALL_RETAINED_HOOKS.filter(
|
|
8658
|
+
(entry) => fs.existsSync(path.join(hooksDir, entry.file))
|
|
8659
|
+
);
|
|
8660
|
+
for (const entry of retainedHooks) {
|
|
8661
|
+
console.log(` ${yellow}i${reset} retained hooks/${entry.file} — ${entry.reason}`);
|
|
8662
|
+
}
|
|
8663
|
+
|
|
8423
8664
|
// Remove only the pp-core-managed files from hooks/lib/ (git-cmd.js + pp-graphify-rebuild.sh).
|
|
8424
8665
|
// hooks/lib/ lives inside the user's runtime hooks directory (shared space) and
|
|
8425
8666
|
// may contain user-owned custom helpers. We must not recursively delete the dir.
|
|
@@ -8458,7 +8699,7 @@ function uninstall(isGlobal, runtime = 'claude') {
|
|
|
8458
8699
|
'github-release-notes.cjs', 'lint.cjs', 'new.cjs',
|
|
8459
8700
|
'README.md', // documentation only — not user-authored
|
|
8460
8701
|
];
|
|
8461
|
-
const GSD_SCRIPTS_LIB_FILES = ['cli-exit.cjs', 'allowlist-ratchet.cjs'];
|
|
8702
|
+
const GSD_SCRIPTS_LIB_FILES = ['cli-exit.cjs', 'allowlist-ratchet.cjs', 'size-budget.cjs'];
|
|
8462
8703
|
|
|
8463
8704
|
const changesetUninstallDir = path.join(targetDir, 'scripts', 'changeset');
|
|
8464
8705
|
if (fs.existsSync(changesetUninstallDir)) {
|
|
@@ -8514,13 +8755,31 @@ function uninstall(isGlobal, runtime = 'claude') {
|
|
|
8514
8755
|
}
|
|
8515
8756
|
}
|
|
8516
8757
|
|
|
8517
|
-
// 6. Clean up settings
|
|
8518
|
-
|
|
8519
|
-
|
|
8520
|
-
|
|
8758
|
+
// 6. Clean up the settings file (remove pp-core hooks, statusline,
|
|
8759
|
+
// permissions and the worktree residue the installer wrote).
|
|
8760
|
+
//
|
|
8761
|
+
// D-09-06: the filename comes from resolveSettingsFileName — the SAME owner
|
|
8762
|
+
// the install path calls — so a local Claude uninstall opens the file a local
|
|
8763
|
+
// Claude install actually wrote. Hardcoding 'settings.json' here (as this did
|
|
8764
|
+
// before) left every pp-core hook entry behind in settings.local.json,
|
|
8765
|
+
// pointing at scripts the same uninstall had just deleted.
|
|
8766
|
+
//
|
|
8767
|
+
// @param {string} targetSettingsPath - Absolute path to the settings file
|
|
8768
|
+
// @param {boolean} opts.removeWhenEmpty - Delete the file when nothing but
|
|
8769
|
+
// pp-core-authored content was in it. True ONLY for local Claude's
|
|
8770
|
+
// settings.local.json; the repo-shared settings.json is user territory even
|
|
8771
|
+
// when it parses to {}.
|
|
8772
|
+
// @param {boolean} opts.pruneWorktreeBaseRef - Drop worktree:{baseRef:'head'}.
|
|
8773
|
+
// True ONLY where the installer can have written it (#683 applies it to
|
|
8774
|
+
// local Claude's settings.local.json and nowhere else).
|
|
8775
|
+
const cleanupSettingsFile = (targetSettingsPath, { removeWhenEmpty = false, pruneWorktreeBaseRef = false } = {}) => {
|
|
8776
|
+
if (!fs.existsSync(targetSettingsPath)) return;
|
|
8777
|
+
const settingsName = path.basename(targetSettingsPath);
|
|
8778
|
+
const settings = readSettings(targetSettingsPath);
|
|
8521
8779
|
if (settings === null) {
|
|
8522
|
-
|
|
8523
|
-
|
|
8780
|
+
// Unparseable: never write it back and never delete it.
|
|
8781
|
+
console.log(` ${yellow}i${reset} Skipping ${settingsName} cleanup — file could not be parsed`);
|
|
8782
|
+
return;
|
|
8524
8783
|
}
|
|
8525
8784
|
let settingsModified = false;
|
|
8526
8785
|
|
|
@@ -8540,6 +8799,7 @@ function uninstall(isGlobal, runtime = 'claude') {
|
|
|
8540
8799
|
// Claude-only FileChanged event added in #770 — safe to iterate for all
|
|
8541
8800
|
// runtimes; installs that don't register these events simply find no
|
|
8542
8801
|
// entries and skip.
|
|
8802
|
+
let hooksModified = false;
|
|
8543
8803
|
for (const eventName of ['SessionStart', 'PostToolUse', 'AfterTool', 'PreToolUse', 'BeforeTool', 'SubagentStop', 'Stop', 'PreCompact', 'BeforeAgent', 'AfterAgent', 'BeforeModel', 'FileChanged']) {
|
|
8544
8804
|
if (settings.hooks && settings.hooks[eventName]) {
|
|
8545
8805
|
const before = JSON.stringify(settings.hooks[eventName]);
|
|
@@ -8558,14 +8818,15 @@ function uninstall(isGlobal, runtime = 'claude') {
|
|
|
8558
8818
|
.filter(Boolean);
|
|
8559
8819
|
if (JSON.stringify(settings.hooks[eventName]) !== before) {
|
|
8560
8820
|
settingsModified = true;
|
|
8821
|
+
hooksModified = true;
|
|
8561
8822
|
}
|
|
8562
8823
|
if (settings.hooks[eventName].length === 0) {
|
|
8563
8824
|
delete settings.hooks[eventName];
|
|
8564
8825
|
}
|
|
8565
8826
|
}
|
|
8566
8827
|
}
|
|
8567
|
-
if (
|
|
8568
|
-
console.log(` ${green}✓${reset} Removed pp-core hooks from
|
|
8828
|
+
if (hooksModified) {
|
|
8829
|
+
console.log(` ${green}✓${reset} Removed pp-core hooks from ${settingsName}`);
|
|
8569
8830
|
}
|
|
8570
8831
|
|
|
8571
8832
|
// Clean up empty hooks object
|
|
@@ -8574,6 +8835,9 @@ function uninstall(isGlobal, runtime = 'claude') {
|
|
|
8574
8835
|
}
|
|
8575
8836
|
|
|
8576
8837
|
// #768 — Remove GSD-owned Claude permissions from settings.json.
|
|
8838
|
+
// D-09-06: "settings.json" above is the historical wording; the file is now
|
|
8839
|
+
// whatever resolveSettingsFileName picked, which for a local Claude
|
|
8840
|
+
// install/uninstall is settings.local.json.
|
|
8577
8841
|
// Applies only to Claude uninstalls. Filter only the exact GSD-owned entries
|
|
8578
8842
|
// to preserve any user-added allow/deny entries.
|
|
8579
8843
|
// Uses a local flag to avoid the shared `settingsModified` producing a false
|
|
@@ -8600,14 +8864,83 @@ function uninstall(isGlobal, runtime = 'claude') {
|
|
|
8600
8864
|
}
|
|
8601
8865
|
if (permissionsModified) {
|
|
8602
8866
|
settingsModified = true;
|
|
8603
|
-
console.log(` ${green}✓${reset} Removed pp-core permissions from
|
|
8867
|
+
console.log(` ${green}✓${reset} Removed pp-core permissions from ${settingsName}`);
|
|
8868
|
+
// D-09-06: prune ONLY the arrays this filter just emptied. A user's own
|
|
8869
|
+
// empty allow[] that we never touched is their content and stays — that
|
|
8870
|
+
// is why this is gated on permissionsModified rather than run always.
|
|
8871
|
+
for (const key of ['allow', 'deny']) {
|
|
8872
|
+
if (Array.isArray(settings.permissions[key]) && settings.permissions[key].length === 0) {
|
|
8873
|
+
delete settings.permissions[key];
|
|
8874
|
+
}
|
|
8875
|
+
}
|
|
8876
|
+
if (Object.keys(settings.permissions).length === 0) {
|
|
8877
|
+
delete settings.permissions;
|
|
8878
|
+
}
|
|
8604
8879
|
}
|
|
8605
8880
|
}
|
|
8606
8881
|
|
|
8607
|
-
|
|
8608
|
-
|
|
8882
|
+
// D-09-06: worktree:{baseRef:'head'} is the ONE shape applyWorktreeBaseRef
|
|
8883
|
+
// writes (src/worktree-base-ref.cts) and it never overwrites an explicit
|
|
8884
|
+
// other value — so exactly that object, with no extra keys, is installer
|
|
8885
|
+
// residue. Any other value, or extra keys, is user content and stays.
|
|
8886
|
+
if (pruneWorktreeBaseRef && settings.worktree && typeof settings.worktree === 'object' &&
|
|
8887
|
+
!Array.isArray(settings.worktree) &&
|
|
8888
|
+
Object.keys(settings.worktree).length === 1 && settings.worktree.baseRef === 'head') {
|
|
8889
|
+
delete settings.worktree;
|
|
8890
|
+
settingsModified = true;
|
|
8891
|
+
console.log(` ${green}✓${reset} Removed pp-core worktree.baseRef from ${settingsName}`);
|
|
8892
|
+
}
|
|
8893
|
+
|
|
8894
|
+
if (!settingsModified) return;
|
|
8895
|
+
|
|
8896
|
+
// D-09-06: when the filters emptied the file completely, nothing but
|
|
8897
|
+
// pp-core content had been in it — leave no husk behind. Gated on
|
|
8898
|
+
// settingsModified so a pre-existing empty file we never touched survives.
|
|
8899
|
+
if (removeWhenEmpty && Object.keys(settings).length === 0) {
|
|
8900
|
+
fs.unlinkSync(targetSettingsPath);
|
|
8609
8901
|
removedCount++;
|
|
8902
|
+
console.log(` ${green}✓${reset} Removed ${settingsName} (only pp-core content remained)`);
|
|
8903
|
+
return;
|
|
8610
8904
|
}
|
|
8905
|
+
|
|
8906
|
+
writeSettings(targetSettingsPath, settings);
|
|
8907
|
+
removedCount++;
|
|
8908
|
+
};
|
|
8909
|
+
|
|
8910
|
+
const isLocalClaudeUninstall = runtime === 'claude' && !isGlobal;
|
|
8911
|
+
cleanupSettingsFile(path.join(targetDir, resolveSettingsFileName(runtime, isGlobal)), {
|
|
8912
|
+
removeWhenEmpty: isLocalClaudeUninstall,
|
|
8913
|
+
pruneWorktreeBaseRef: isLocalClaudeUninstall,
|
|
8914
|
+
});
|
|
8915
|
+
if (isLocalClaudeUninstall) {
|
|
8916
|
+
// Legacy sweep, the mirror of install's #338 migration read: a pre-#338
|
|
8917
|
+
// local install wrote its managed entries into the repo-shared
|
|
8918
|
+
// settings.json. Filter them out of there too, but never delete that file
|
|
8919
|
+
// and never prune worktree residue from it — the installer writes neither
|
|
8920
|
+
// there.
|
|
8921
|
+
cleanupSettingsFile(path.join(targetDir, 'settings.json'), {
|
|
8922
|
+
removeWhenEmpty: false,
|
|
8923
|
+
pruneWorktreeBaseRef: false,
|
|
8924
|
+
});
|
|
8925
|
+
}
|
|
8926
|
+
|
|
8927
|
+
// The installer-migrations journal: installer-written, deliberately absent
|
|
8928
|
+
// from pp-file-manifest.json, and left behind by every uninstall before this
|
|
8929
|
+
// one. A stale journal makes a later install skip migrations it should run —
|
|
8930
|
+
// a stored record asserting something untrue about a directory that no longer
|
|
8931
|
+
// holds an install.
|
|
8932
|
+
const installStatePath = path.join(targetDir, 'pp-install-state.json');
|
|
8933
|
+
if (fs.existsSync(installStatePath)) {
|
|
8934
|
+
fs.rmSync(installStatePath, { force: true });
|
|
8935
|
+
removedCount++;
|
|
8936
|
+
console.log(` ${green}✓${reset} Removed pp-install-state.json`);
|
|
8937
|
+
}
|
|
8938
|
+
|
|
8939
|
+
// Installer-created artifact dirs that the layout-driven removal empties but
|
|
8940
|
+
// does not itself remove — mirror of the scripts/ handling above. A non-empty
|
|
8941
|
+
// dir (user content) throws and stays.
|
|
8942
|
+
for (const artifactDir of ['agents', 'commands']) {
|
|
8943
|
+
try { fs.rmdirSync(path.join(targetDir, artifactDir)); } catch (_) { /* Not empty — leave it */ }
|
|
8611
8944
|
}
|
|
8612
8945
|
|
|
8613
8946
|
// 6. For OpenCode, clean up permissions from opencode.json or opencode.jsonc
|
|
@@ -8705,9 +9038,12 @@ function uninstall(isGlobal, runtime = 'claude') {
|
|
|
8705
9038
|
console.log(` ${yellow}⚠${reset} No pp-core files found to remove.`);
|
|
8706
9039
|
}
|
|
8707
9040
|
|
|
9041
|
+
const retainedSentence = retainedHooks.length > 0
|
|
9042
|
+
? `\n Retained on disk (named per HINST-01): ${retainedHooks.map((e) => `hooks/${e.file}`).join(', ')}`
|
|
9043
|
+
: '';
|
|
8708
9044
|
console.log(`
|
|
8709
9045
|
${green}Done!${reset} GSD has been uninstalled from ${runtimeLabel}.
|
|
8710
|
-
Your other files and settings have been preserved
|
|
9046
|
+
Your other files and settings have been preserved.${retainedSentence}
|
|
8711
9047
|
`);
|
|
8712
9048
|
}
|
|
8713
9049
|
|
|
@@ -10335,8 +10671,8 @@ function install(isGlobal, runtime = 'claude', options = {}) {
|
|
|
10335
10671
|
// Replace ~/.claude/ and $HOME/.claude/ as they are the source of truth in the repo
|
|
10336
10672
|
const dirRegex = /~\/\.claude\//g;
|
|
10337
10673
|
const homeDirRegex = /\$HOME\/\.claude\//g;
|
|
10338
|
-
const bareDirRegex = /~\/\.claude\
|
|
10339
|
-
const bareHomeDirRegex = /\$HOME\/\.claude\
|
|
10674
|
+
const bareDirRegex = /~\/\.claude(?![\w-])/g;
|
|
10675
|
+
const bareHomeDirRegex = /\$HOME\/\.claude(?![\w-])/g;
|
|
10340
10676
|
const normalizedPathPrefix = pathPrefix.replace(/\/$/, '');
|
|
10341
10677
|
if (!isCopilot && !isAntigravity) {
|
|
10342
10678
|
content = content.replace(dirRegex, pathPrefix);
|
|
@@ -10716,7 +11052,7 @@ function install(isGlobal, runtime = 'claude', options = {}) {
|
|
|
10716
11052
|
}
|
|
10717
11053
|
throw err;
|
|
10718
11054
|
}
|
|
10719
|
-
const matches = content.match(/(?:~|\$HOME)\/\.claude\
|
|
11055
|
+
const matches = content.match(/(?:~|\$HOME)\/\.claude(?![\w-])/g);
|
|
10720
11056
|
if (matches) {
|
|
10721
11057
|
leakedPaths.push({ file: relPath, count: matches.length });
|
|
10722
11058
|
}
|
|
@@ -11205,7 +11541,7 @@ function install(isGlobal, runtime = 'claude', options = {}) {
|
|
|
11205
11541
|
// so engineer-specific absolute paths (Node binary, home dir) never land in the repo-shared
|
|
11206
11542
|
// settings.json. Global installs and all other runtimes continue to use settings.json.
|
|
11207
11543
|
const isLocalClaude = (runtime === 'claude' && !isGlobal);
|
|
11208
|
-
const settingsFileName =
|
|
11544
|
+
const settingsFileName = resolveSettingsFileName(runtime, isGlobal);
|
|
11209
11545
|
const settingsPath = path.join(targetDir, settingsFileName);
|
|
11210
11546
|
|
|
11211
11547
|
// #338 migration: if a prior local Claude install wrote GSD-shaped entries to settings.json,
|
|
@@ -11497,6 +11833,25 @@ function install(isGlobal, runtime = 'claude', options = {}) {
|
|
|
11497
11833
|
};
|
|
11498
11834
|
}
|
|
11499
11835
|
|
|
11836
|
+
// The ONE owner of "how to start a project" in the installer. Every
|
|
11837
|
+
// runtime's Done! line names the init front door (git init, remote wiring, vault
|
|
11838
|
+
// binding, then handoff to /pp-discovery or /pp-new-project), derived from this
|
|
11839
|
+
// single stem so no runtime can drift back to naming the second step.
|
|
11840
|
+
const START_COMMAND_STEM = 'init';
|
|
11841
|
+
|
|
11842
|
+
/**
|
|
11843
|
+
* Resolve the start command in a runtime's own invocation syntax.
|
|
11844
|
+
* @param {string} runtime - installer runtime id (unknown ids get the default form)
|
|
11845
|
+
* @returns {string}
|
|
11846
|
+
*/
|
|
11847
|
+
function resolveStartCommand(runtime) {
|
|
11848
|
+
if (runtime === 'gemini') return `/pp:${START_COMMAND_STEM}`;
|
|
11849
|
+
if (runtime === 'codex') return `$pp-${START_COMMAND_STEM}`;
|
|
11850
|
+
if (runtime === 'cursor') return `pp-${START_COMMAND_STEM} (mention the skill name)`;
|
|
11851
|
+
if (runtime === 'kimi') return `/skill:pp-${START_COMMAND_STEM}`;
|
|
11852
|
+
return `/pp-${START_COMMAND_STEM}`;
|
|
11853
|
+
}
|
|
11854
|
+
|
|
11500
11855
|
/**
|
|
11501
11856
|
* Apply statusline config, then print completion message
|
|
11502
11857
|
*/
|
|
@@ -11628,21 +11983,7 @@ function finishInstall(settingsPath, settings, statuslineCommand, shouldInstallS
|
|
|
11628
11983
|
if (runtime === 'hermes') program = 'Hermes Agent';
|
|
11629
11984
|
if (runtime === 'kimi') program = 'Kimi CLI';
|
|
11630
11985
|
|
|
11631
|
-
|
|
11632
|
-
if (runtime === 'opencode') command = '/pp-new-project';
|
|
11633
|
-
if (runtime === 'kilo') command = '/pp-new-project';
|
|
11634
|
-
if (runtime === 'gemini') command = '/pp:new-project';
|
|
11635
|
-
if (runtime === 'codex') command = '$pp-new-project';
|
|
11636
|
-
if (runtime === 'copilot') command = '/pp-new-project';
|
|
11637
|
-
if (runtime === 'antigravity') command = '/pp-new-project';
|
|
11638
|
-
if (runtime === 'cursor') command = 'pp-new-project (mention the skill name)';
|
|
11639
|
-
if (runtime === 'windsurf') command = '/pp-new-project';
|
|
11640
|
-
if (runtime === 'augment') command = '/pp-new-project';
|
|
11641
|
-
if (runtime === 'trae') command = '/pp-new-project';
|
|
11642
|
-
if (runtime === 'cline') command = '/pp-new-project';
|
|
11643
|
-
if (runtime === 'qwen') command = '/pp-new-project';
|
|
11644
|
-
if (runtime === 'hermes') command = '/pp-new-project';
|
|
11645
|
-
if (runtime === 'kimi') command = '/skill:pp-new-project';
|
|
11986
|
+
const command = resolveStartCommand(runtime);
|
|
11646
11987
|
|
|
11647
11988
|
// Claude Code global installs use the skills/ format (CC 2.1.88+).
|
|
11648
11989
|
// Restart is required for CC to pick up newly-installed skills, and the
|
|
@@ -11650,7 +11991,7 @@ function finishInstall(settingsPath, settings, statuslineCommand, shouldInstallS
|
|
|
11650
11991
|
// cover both invocation paths to avoid #2957-style "no commands appear".
|
|
11651
11992
|
if (runtime === 'claude' && isGlobal) {
|
|
11652
11993
|
console.log(`
|
|
11653
|
-
${green}Done!${reset} Restart ${program}, then in any directory either type ${cyan}${command}${reset} or ask Claude to run the ${cyan}pp
|
|
11994
|
+
${green}Done!${reset} Restart ${program}, then in any directory either type ${cyan}${command}${reset} or ask Claude to run the ${cyan}pp-${START_COMMAND_STEM}${reset} skill.
|
|
11654
11995
|
`);
|
|
11655
11996
|
return;
|
|
11656
11997
|
}
|
|
@@ -12315,6 +12656,9 @@ module.exports = {
|
|
|
12315
12656
|
normalizeAgentBodyForRuntime,
|
|
12316
12657
|
yamlIdentifier,
|
|
12317
12658
|
computePathPrefix,
|
|
12659
|
+
// HINST-04 / D-09-14 — global Claude SKILL.md local-first dispatcher
|
|
12660
|
+
rewriteGlobalClaudeSkillIncludes,
|
|
12661
|
+
CLAUDE_PROJECT_DIR_SUBSTITUTES_IN_SKILL_BODY,
|
|
12318
12662
|
applyRuntimeContentRewritesInPlace,
|
|
12319
12663
|
getCodexSkillAdapterHeader,
|
|
12320
12664
|
convertClaudeCommandToCursorSkill,
|
|
@@ -12429,12 +12773,15 @@ module.exports = {
|
|
|
12429
12773
|
populatePristineDir,
|
|
12430
12774
|
USER_OWNED_ARTIFACTS,
|
|
12431
12775
|
finishInstall,
|
|
12776
|
+
resolveStartCommand,
|
|
12432
12777
|
homePathCoveredByRc,
|
|
12433
12778
|
maybeSuggestPathExport,
|
|
12434
12779
|
runtimeMap,
|
|
12435
12780
|
allRuntimes,
|
|
12436
12781
|
selectRuntimesFromArgs,
|
|
12437
12782
|
GSD_UNINSTALL_HOOKS,
|
|
12783
|
+
GSD_UNINSTALL_RETAINED_HOOKS,
|
|
12784
|
+
resolveSettingsFileName,
|
|
12438
12785
|
parseRuntimeInput,
|
|
12439
12786
|
buildRuntimePromptText,
|
|
12440
12787
|
buildUpdateBannerPromptText,
|