@pareto4/pp-core 1.8.0 → 1.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pp-core",
3
3
  "displayName": "PP Core",
4
- "version": "1.8.0",
4
+ "version": "1.8.2",
5
5
  "description": "PP Core is a meta-prompting, context engineering, and spec-driven development system for AI coding agents.",
6
6
  "author": {
7
7
  "name": "pareto4",
package/NOTICE ADDED
@@ -0,0 +1,23 @@
1
+ pp-core
2
+ =======
3
+
4
+ pp-core is based on gsd-core by Open GSD.
5
+
6
+ ## Upstream Attribution
7
+
8
+ Copyright (c) 2026 Open GSD
9
+
10
+ gsd-core is licensed under the MIT License.
11
+ Source: https://github.com/open-gsd/gsd-core
12
+ npm package: @opengsd/gsd-core @ version 1.5.0
13
+
14
+ The full text of the MIT License is reproduced in the LICENSE file at the root of
15
+ this repository.
16
+
17
+ ## Fork Information
18
+
19
+ pp-core is a hard fork of gsd-core v1.5.0, maintained by The Pareto Project.
20
+ It is not affiliated with or endorsed by OpenGSD.
21
+
22
+ pp-core diverges from gsd-core as of 2026-06-24. The Pareto Project develops and
23
+ maintains pp-core independently; no upstream merges from gsd-core are planned.
package/README.md CHANGED
@@ -87,10 +87,16 @@ Troubleshooting? See [docs/how-to/recover-and-troubleshoot.md](docs/how-to/recov
87
87
 
88
88
  ---
89
89
 
90
- ## License
90
+ ## License & Attribution
91
91
 
92
92
  MIT License. See [LICENSE](LICENSE) for details.
93
93
 
94
+ pp-core is a hard fork of [gsd-core](https://github.com/open-gsd/gsd-core) by
95
+ Open GSD (MIT), rebranded and extended by The Pareto Project. The original
96
+ copyright and permission notice are preserved in [LICENSE](LICENSE), and
97
+ upstream attribution is recorded in [NOTICE](NOTICE) — both files ship in the
98
+ npm package and in every installed footprint.
99
+
94
100
  ---
95
101
 
96
102
  <div align="center">
package/bin/install.js CHANGED
@@ -216,7 +216,7 @@ const GSD_CURSOR_POST_TOOL_HOOK_SCRIPT = 'pp-cursor-post-tool.js';
216
216
  // Marker comment embedded in managed hook entries so GSD can find+remove them.
217
217
  const GSD_CURSOR_HOOK_MARKER = 'gsd-managed';
218
218
 
219
- // GSD-managed files under hooks/lib/ (helpers required by gsd-*.sh hooks).
219
+ // pp-core-managed files under hooks/lib/ (helpers required by gsd-*.sh hooks).
220
220
  // git-cmd.js does not start with "gsd-" (shared classifier for #3129), pp-graphify-rebuild.sh does.
221
221
  const GSD_HOOK_LIB_FILES = ['git-cmd.js', 'pp-graphify-rebuild.sh'];
222
222
 
@@ -297,7 +297,7 @@ function _getGsdEffortCatalog() {
297
297
  } catch (_err) {
298
298
  // Fail loudly — a missing manifest is a broken install, not a soft degradation.
299
299
  throw new Error(
300
- `gsd install: cannot load config-defaults.manifest.json at ${manifestPath}: ${_err.message}`
300
+ `pp-core install: cannot load config-defaults.manifest.json at ${manifestPath}: ${_err.message}`
301
301
  );
302
302
  }
303
303
 
@@ -601,7 +601,7 @@ if (hasUninstall) {
601
601
 
602
602
  // Show help if requested
603
603
  if (hasHelp) {
604
- console.log(` ${yellow}Usage:${reset} npx ${pkg.name} [options]\n\n ${yellow}Options:${reset}\n ${cyan}-g, --global${reset} Install globally (to config directory)\n ${cyan}-l, --local${reset} Install locally (to current directory)\n ${cyan}--claude${reset} Install for Claude Code only\n ${cyan}--opencode${reset} Install for OpenCode only\n ${cyan}--gemini${reset} Install for Gemini only\n ${cyan}--kilo${reset} Install for Kilo only\n ${cyan}--codex${reset} Install for Codex only\n ${cyan}--kimi${reset} Install for Kimi CLI only\n ${cyan}--copilot${reset} Install for Copilot only\n ${cyan}--antigravity${reset} Install for Antigravity only\n ${cyan}--cursor${reset} Install for Cursor only\n ${cyan}--windsurf${reset} Install for Windsurf only\n ${cyan}--augment${reset} Install for Augment only\n ${cyan}--trae${reset} Install for Trae only\n ${cyan}--qwen${reset} Install for Qwen Code only\n ${cyan}--hermes${reset} Install for Hermes Agent only\n ${cyan}--cline${reset} Install for Cline only\n ${cyan}--codebuddy${reset} Install for CodeBuddy only\n ${cyan}--all${reset} Install for all runtimes\n ${cyan}-u, --uninstall${reset} Uninstall pp-core (remove all pp-core files)\n ${cyan}-c, --config-dir <path>${reset} Specify custom config directory\n ${cyan}-h, --help${reset} Show this help message\n ${cyan}--force-statusline${reset} Replace existing statusline config\n ${cyan}--portable-hooks${reset} Emit \$HOME-relative hook paths in settings.json\n (for WSL/Docker bind-mount setups; also GSD_PORTABLE_HOOKS=1)\n ${cyan}--profile=<name>${reset} Install a named skill profile. Profiles:\n core — ${PROFILES.core.length} main-loop skills incl. phase (~130 desc tokens)\n standard — ${PROFILES.standard.length} skills incl. phase, review, config (~700)\n full — all skills (default)\n Composable: --profile=core,audit installs union of closures.\n Profile is persisted and respected by \`gsd update\`.\n ${cyan}--minimal${reset} Alias for --profile=core (back-compat).\n Cuts cold-start overhead from ~12k tokens to ~700.\n Alias: --core-only.\n\n ${yellow}Examples:${reset}\n ${dim}# Interactive install (prompts for runtime and location)${reset}\n npx ${pkg.name}\n\n ${dim}# Install for Claude Code globally${reset}\n npx ${pkg.name} --claude --global\n\n ${dim}# Install for Gemini globally${reset}\n npx ${pkg.name} --gemini --global\n\n ${dim}# Install for Kilo globally${reset}\n npx ${pkg.name} --kilo --global\n\n ${dim}# Install for Codex globally${reset}\n npx ${pkg.name} --codex --global\n\n ${dim}# Install for Kimi CLI globally${reset}\n npx ${pkg.name} --kimi --global\n\n ${dim}# Install for Kimi CLI under ~/.kimi-code${reset}\n npx ${pkg.name} --kimi --global --config-dir ~/.kimi-code\n\n ${dim}# Install for Copilot globally${reset}\n npx ${pkg.name} --copilot --global\n\n ${dim}# Install for Copilot locally${reset}\n npx ${pkg.name} --copilot --local\n\n ${dim}# Install for Antigravity globally${reset}\n npx ${pkg.name} --antigravity --global\n\n ${dim}# Install for Antigravity locally${reset}\n npx ${pkg.name} --antigravity --local\n\n ${dim}# Install for Cursor globally${reset}\n npx ${pkg.name} --cursor --global\n\n ${dim}# Install for Cursor locally${reset}\n npx ${pkg.name} --cursor --local\n\n ${dim}# Install for Windsurf globally${reset}\n npx ${pkg.name} --windsurf --global\n\n ${dim}# Install for Windsurf locally${reset}\n npx ${pkg.name} --windsurf --local\n\n ${dim}# Install for Augment globally${reset}\n npx ${pkg.name} --augment --global\n\n ${dim}# Install for Augment locally${reset}\n npx ${pkg.name} --augment --local\n\n ${dim}# Install for Trae globally${reset}\n npx ${pkg.name} --trae --global\n\n ${dim}# Install for Trae locally${reset}\n npx ${pkg.name} --trae --local\n\n ${dim}# Install for Hermes Agent globally${reset}\n npx ${pkg.name} --hermes --global\n\n ${dim}# Install for Hermes Agent locally${reset}\n npx ${pkg.name} --hermes --local\n\n ${dim}# Install for Cline globally${reset}\n npx ${pkg.name} --cline --global\n\n ${dim}# Install for Cline locally${reset}\n npx ${pkg.name} --cline --local\n\n ${dim}# Install for CodeBuddy globally${reset}\n npx ${pkg.name} --codebuddy --global\n\n ${dim}# Install for CodeBuddy locally${reset}\n npx ${pkg.name} --codebuddy --local\n\n ${dim}# Install for all runtimes globally${reset}\n npx ${pkg.name} --all --global\n\n ${dim}# Install to custom config directory${reset}\n npx ${pkg.name} --kilo --global --config-dir ~/.kilo-work\n\n ${dim}# Install to current project only${reset}\n npx ${pkg.name} --claude --local\n\n ${dim}# Uninstall pp-core from Cursor globally${reset}\n npx ${pkg.name} --cursor --global --uninstall\n\n ${yellow}Notes:${reset}\n The --config-dir option is useful when you have multiple configurations.\n It takes priority over CLAUDE_CONFIG_DIR / OPENCODE_CONFIG_DIR / GEMINI_CONFIG_DIR / KILO_CONFIG_DIR / CODEX_HOME / KIMI_CONFIG_DIR / COPILOT_CONFIG_DIR / COPILOT_HOME / ANTIGRAVITY_CONFIG_DIR / CURSOR_CONFIG_DIR / WINDSURF_CONFIG_DIR / AUGMENT_CONFIG_DIR / TRAE_CONFIG_DIR / QWEN_CONFIG_DIR / HERMES_HOME / CLINE_CONFIG_DIR / CODEBUDDY_CONFIG_DIR environment variables.\n Kimi CLI defaults to the first existing generic skills root: ${cyan}~/.config/agents/skills${reset}, then ${cyan}~/.agents/skills${reset}; if neither exists, pp-core creates ${cyan}~/.config/agents${reset}.\n Use ${cyan}--config-dir ~/.kimi-code${reset} or ${cyan}KIMI_CONFIG_DIR=~/.kimi-code${reset} for brand-specific Kimi installs.\n`);
604
+ console.log(` ${yellow}Usage:${reset} npx ${pkg.name} [options]\n\n ${yellow}Options:${reset}\n ${cyan}-g, --global${reset} Install globally (to config directory)\n ${cyan}-l, --local${reset} Install locally (to current directory)\n ${cyan}--claude${reset} Install for Claude Code only\n ${cyan}--opencode${reset} Install for OpenCode only\n ${cyan}--gemini${reset} Install for Gemini only\n ${cyan}--kilo${reset} Install for Kilo only\n ${cyan}--codex${reset} Install for Codex only\n ${cyan}--kimi${reset} Install for Kimi CLI only\n ${cyan}--copilot${reset} Install for Copilot only\n ${cyan}--antigravity${reset} Install for Antigravity only\n ${cyan}--cursor${reset} Install for Cursor only\n ${cyan}--windsurf${reset} Install for Windsurf only\n ${cyan}--augment${reset} Install for Augment only\n ${cyan}--trae${reset} Install for Trae only\n ${cyan}--qwen${reset} Install for Qwen Code only\n ${cyan}--hermes${reset} Install for Hermes Agent only\n ${cyan}--cline${reset} Install for Cline only\n ${cyan}--codebuddy${reset} Install for CodeBuddy only\n ${cyan}--all${reset} Install for all runtimes\n ${cyan}-u, --uninstall${reset} Uninstall pp-core (remove all pp-core files)\n ${cyan}-c, --config-dir <path>${reset} Specify custom config directory\n ${cyan}-h, --help${reset} Show this help message\n ${cyan}--force-statusline${reset} Replace existing statusline config\n ${cyan}--portable-hooks${reset} Emit \$HOME-relative hook paths in settings.json\n (for WSL/Docker bind-mount setups; also GSD_PORTABLE_HOOKS=1)\n ${cyan}--profile=<name>${reset} Install a named skill profile. Profiles:\n core — ${PROFILES.core.length} main-loop skills incl. phase (~130 desc tokens)\n standard — ${PROFILES.standard.length} skills incl. phase, review, config (~700)\n full — all skills (default)\n Composable: --profile=core,audit installs union of closures.\n Profile is persisted and respected by \`pp update\`.\n ${cyan}--minimal${reset} Alias for --profile=core (back-compat).\n Cuts cold-start overhead from ~12k tokens to ~700.\n Alias: --core-only.\n\n ${yellow}Examples:${reset}\n ${dim}# Interactive install (prompts for runtime and location)${reset}\n npx ${pkg.name}\n\n ${dim}# Install for Claude Code globally${reset}\n npx ${pkg.name} --claude --global\n\n ${dim}# Install for Gemini globally${reset}\n npx ${pkg.name} --gemini --global\n\n ${dim}# Install for Kilo globally${reset}\n npx ${pkg.name} --kilo --global\n\n ${dim}# Install for Codex globally${reset}\n npx ${pkg.name} --codex --global\n\n ${dim}# Install for Kimi CLI globally${reset}\n npx ${pkg.name} --kimi --global\n\n ${dim}# Install for Kimi CLI under ~/.kimi-code${reset}\n npx ${pkg.name} --kimi --global --config-dir ~/.kimi-code\n\n ${dim}# Install for Copilot globally${reset}\n npx ${pkg.name} --copilot --global\n\n ${dim}# Install for Copilot locally${reset}\n npx ${pkg.name} --copilot --local\n\n ${dim}# Install for Antigravity globally${reset}\n npx ${pkg.name} --antigravity --global\n\n ${dim}# Install for Antigravity locally${reset}\n npx ${pkg.name} --antigravity --local\n\n ${dim}# Install for Cursor globally${reset}\n npx ${pkg.name} --cursor --global\n\n ${dim}# Install for Cursor locally${reset}\n npx ${pkg.name} --cursor --local\n\n ${dim}# Install for Windsurf globally${reset}\n npx ${pkg.name} --windsurf --global\n\n ${dim}# Install for Windsurf locally${reset}\n npx ${pkg.name} --windsurf --local\n\n ${dim}# Install for Augment globally${reset}\n npx ${pkg.name} --augment --global\n\n ${dim}# Install for Augment locally${reset}\n npx ${pkg.name} --augment --local\n\n ${dim}# Install for Trae globally${reset}\n npx ${pkg.name} --trae --global\n\n ${dim}# Install for Trae locally${reset}\n npx ${pkg.name} --trae --local\n\n ${dim}# Install for Hermes Agent globally${reset}\n npx ${pkg.name} --hermes --global\n\n ${dim}# Install for Hermes Agent locally${reset}\n npx ${pkg.name} --hermes --local\n\n ${dim}# Install for Cline globally${reset}\n npx ${pkg.name} --cline --global\n\n ${dim}# Install for Cline locally${reset}\n npx ${pkg.name} --cline --local\n\n ${dim}# Install for CodeBuddy globally${reset}\n npx ${pkg.name} --codebuddy --global\n\n ${dim}# Install for CodeBuddy locally${reset}\n npx ${pkg.name} --codebuddy --local\n\n ${dim}# Install for all runtimes globally${reset}\n npx ${pkg.name} --all --global\n\n ${dim}# Install to custom config directory${reset}\n npx ${pkg.name} --kilo --global --config-dir ~/.kilo-work\n\n ${dim}# Install to current project only${reset}\n npx ${pkg.name} --claude --local\n\n ${dim}# Uninstall pp-core from Cursor globally${reset}\n npx ${pkg.name} --cursor --global --uninstall\n\n ${yellow}Notes:${reset}\n The --config-dir option is useful when you have multiple configurations.\n It takes priority over CLAUDE_CONFIG_DIR / OPENCODE_CONFIG_DIR / GEMINI_CONFIG_DIR / KILO_CONFIG_DIR / CODEX_HOME / KIMI_CONFIG_DIR / COPILOT_CONFIG_DIR / COPILOT_HOME / ANTIGRAVITY_CONFIG_DIR / CURSOR_CONFIG_DIR / WINDSURF_CONFIG_DIR / AUGMENT_CONFIG_DIR / TRAE_CONFIG_DIR / QWEN_CONFIG_DIR / HERMES_HOME / CLINE_CONFIG_DIR / CODEBUDDY_CONFIG_DIR environment variables.\n Kimi CLI defaults to the first existing generic skills root: ${cyan}~/.config/agents/skills${reset}, then ${cyan}~/.agents/skills${reset}; if neither exists, pp-core creates ${cyan}~/.config/agents${reset}.\n Use ${cyan}--config-dir ~/.kimi-code${reset} or ${cyan}KIMI_CONFIG_DIR=~/.kimi-code${reset} for brand-specific Kimi installs.\n`);
605
605
  process.exit(0);
606
606
  }
607
607
 
@@ -640,7 +640,7 @@ const resolveBashRunner = hooksSurface.resolveBashRunner;
640
640
  // referencesHook: pure predicate over hook entry objects, shared between
641
641
  // install() and finishInstall() (ADR-857 phase 5f-1b).
642
642
  const referencesHook = hooksSurface.referencesHook;
643
- // applySettingsJsonHooks: mutates settings.hooks.* in place with all GSD-managed
643
+ // applySettingsJsonHooks: mutates settings.hooks.* in place with all pp-core-managed
644
644
  // hook registrations for settings.json-surface runtimes (ADR-857 phase 5f-1b).
645
645
  const applySettingsJsonHooks = hooksSurface.applySettingsJsonHooks;
646
646
 
@@ -649,7 +649,7 @@ function rewriteLegacyManagedNodeHookCommands(settings, absoluteRunner, opts) {
649
649
  }
650
650
 
651
651
  /**
652
- * Build the GSD-managed Codex SessionStart hook block for config.toml.
652
+ * Build the pp-core-managed Codex SessionStart hook block for config.toml.
653
653
  *
654
654
  * Issue #3017: the previous shape inlined `command = "node ${path}"` which
655
655
  * fails under GUI/minimal-PATH runtimes where bare `node` doesn't resolve
@@ -715,7 +715,7 @@ function reconcileCodexHooksJsonEvent(targetDir, eventName, opts = {}) {
715
715
  }
716
716
 
717
717
  /**
718
- * Reconcile the GSD-managed SessionStart hook entry in hooks.json.
718
+ * Reconcile the pp-core-managed SessionStart hook entry in hooks.json.
719
719
  * Delegates to the generic reconcileCodexHooksJsonEvent helper.
720
720
  *
721
721
  * @param {string} targetDir
@@ -818,7 +818,7 @@ function ensureCodexHooksJsonEvent(targetDir, eventName, opts = {}) {
818
818
  }
819
819
 
820
820
  /**
821
- * Remove a GSD-managed event entry from hooks.json. Called during uninstall.
821
+ * Remove a pp-core-managed event entry from hooks.json. Called during uninstall.
822
822
  *
823
823
  * @param {string} targetDir
824
824
  * @param {string} eventName
@@ -1483,7 +1483,7 @@ const claudeToGeminiTools = {
1483
1483
  TodoWrite: 'write_todos',
1484
1484
  };
1485
1485
 
1486
- // Tool name mapping from Claude/GSD agents to Kimi CLI module paths.
1486
+ // Tool name mapping from Claude/pp-core agents to Kimi CLI module paths.
1487
1487
  // Kimi custom agent YAML requires fully-qualified module paths.
1488
1488
  const claudeToKimiTools = {
1489
1489
  Read: 'kimi_cli.tools.file:ReadFile',
@@ -3025,7 +3025,7 @@ Typed mapping (agent_type-capable schema only):
3025
3025
  to \`spawn_agent\` when the runtime/tool supports it. Omit missing, empty,
3026
3026
  inherited, or unsupported values; do not invent one-off effort literals in
3027
3027
  workflow prose.
3028
- - \`fork_context: false\` by default — GSD agents load their own context via \`<files_to_read>\` blocks
3028
+ - \`fork_context: false\` by default — pp-core agents load their own context via \`<files_to_read>\` blocks
3029
3029
  - \`Task(isolation="worktree")\` / \`Agent(isolation="worktree")\` → no direct Codex mapping.
3030
3030
  Codex \`spawn_agent\` does not create or bind a git worktree automatically.
3031
3031
  Workflows that require this isolation must fail closed or use an explicit
@@ -3035,7 +3035,7 @@ Generic-agent workaround (multi_agent_v1 schema — NO agent_type field):
3035
3035
  When only the generic \`multi_agent_v1\` schema is available, typed GSD agent dispatch
3036
3036
  (\`pp-planner\`, \`pp-executor\`, etc.) is NOT possible. This is a known Codex limitation
3037
3037
  (openai/codex#15250). **This workaround is NOT equivalent to typed pp-planner/pp-executor
3038
- execution** — GSD agents carry project-aware prompts, audit logging, and workflow context
3038
+ execution** — pp-core agents carry project-aware prompts, audit logging, and workflow context
3039
3039
  that a generic subagent lacks. Use the following fallback:
3040
3040
  1. Resolve your active Codex config root — the directory that contains your \`config.toml\`.
3041
3041
  This directory is determined in priority order: \`$CODEX_HOME\` (if set), the path given
@@ -3198,11 +3198,11 @@ function generateCodexAgentToml(agentName, agentContent, modelOverrides = null,
3198
3198
  }
3199
3199
 
3200
3200
  /**
3201
- * Remove stale agents/openai.yaml sidecar files from GSD-managed Codex skill dirs.
3201
+ * Remove stale agents/openai.yaml sidecar files from pp-core-managed Codex skill dirs.
3202
3202
  *
3203
3203
  * Prior to #1326, GSD's Codex install path wrote an agents/openai.yaml file
3204
3204
  * alongside each gsd-* SKILL.md. Recent Codex builds index BOTH SKILL.md and
3205
- * the sidecar, causing each GSD skill to appear twice in autocomplete. This
3205
+ * the sidecar, causing each pp-core skill to appear twice in autocomplete. This
3206
3206
  * function removes those stale sidecars and — if the agents/ subdirectory is
3207
3207
  * now empty — prunes it too.
3208
3208
  *
@@ -4066,7 +4066,7 @@ function stripLeakedGsdCodexSections(content) {
4066
4066
  }
4067
4067
 
4068
4068
  /**
4069
- * Strip GSD-managed legacy Codex hook blocks from a config.toml string
4069
+ * Strip pp-core-managed legacy Codex hook blocks from a config.toml string
4070
4070
  * using the TOML AST already used elsewhere in this file
4071
4071
  * (`getTomlTableSections` + `removeContentRanges`). The earlier regex-based
4072
4072
  * implementation required a precise key order, exact single-space padding
@@ -4084,7 +4084,7 @@ function stripLeakedGsdCodexSections(content) {
4084
4084
  * removal range backward through any single preceding comment line.
4085
4085
  *
4086
4086
  * Pure function, exported for test coverage. Returns the input unchanged
4087
- * if no GSD-managed hook section is present.
4087
+ * if no pp-core-managed hook section is present.
4088
4088
  */
4089
4089
  function stripStaleGsdHookBlocks(configContent) {
4090
4090
  const sections = getTomlTableSections(configContent);
@@ -4096,7 +4096,7 @@ function stripStaleGsdHookBlocks(configContent) {
4096
4096
  return configContent;
4097
4097
  }
4098
4098
 
4099
- // A section is GSD-managed if any structural `command` key inside its
4099
+ // A section is pp-core-managed if any structural `command` key inside its
4100
4100
  // body parses to a string whose basename matches `gsd-(check-update|
4101
4101
  // update-check).js`. The TOML line parser already classified each line's
4102
4102
  // `keySegments`, so we never inspect raw text — this handles arbitrary
@@ -4431,7 +4431,7 @@ function migrateCodexHooksMapFormat(content) {
4431
4431
  /**
4432
4432
  * Detect whether the user already uses the namespaced AoT hooks form
4433
4433
  * (`[[hooks.<EVENT>]]`) for the given event in the config. When true,
4434
- * the GSD-managed hook block must be emitted in the same shape so it
4434
+ * the pp-core-managed hook block must be emitted in the same shape so it
4435
4435
  * coexists cleanly — mixing `[[hooks]]` (flat) with `[[hooks.SessionStart]]`
4436
4436
  * (namespaced) in the same file confuses round-trip writers and can
4437
4437
  * produce a config that Codex rejects (#2760, defect 3).
@@ -5145,7 +5145,7 @@ function mergeCodexConfig(configPath, gsdBlock) {
5145
5145
  if (markerIndex !== -1) {
5146
5146
  let before = existing.substring(0, markerIndex).trimEnd();
5147
5147
  if (before) {
5148
- // Strip any GSD-managed sections that leaked above the marker from previous installs
5148
+ // Strip any pp-core-managed sections that leaked above the marker from previous installs
5149
5149
  before = stripLeakedGsdCodexSections(before).trimEnd();
5150
5150
 
5151
5151
  atomicWriteFileSync(configPath, before + eol + eol + normalizedGsdBlock + eol);
@@ -5420,7 +5420,7 @@ function mergeCopilotInstructions(filePath, gsdContent) {
5420
5420
 
5421
5421
  /**
5422
5422
  * Strip GSD section from copilot-instructions.md content.
5423
- * Returns cleaned content, or null if file should be deleted (was GSD-only).
5423
+ * Returns cleaned content, or null if file should be deleted (was pp-core-only).
5424
5424
  * @param {string} content - File content
5425
5425
  * @returns {string|null} - Cleaned content or null if empty
5426
5426
  */
@@ -5492,7 +5492,7 @@ function mergeGsdAgentsMd(filePath, gsdContent) {
5492
5492
 
5493
5493
  /**
5494
5494
  * Strip the GSD block from AGENTS.md content. Returns null if the file became
5495
- * empty (was GSD-only), the unchanged content if no markers were found, or the
5495
+ * empty (was pp-core-only), the unchanged content if no markers were found, or the
5496
5496
  * cleaned content otherwise.
5497
5497
  */
5498
5498
  function stripGsdFromAgentsMd(content) {
@@ -5551,7 +5551,7 @@ function buildCursorHookEntry(scriptPath) {
5551
5551
  }
5552
5552
 
5553
5553
  /**
5554
- * Return true if a Cursor hook entry is GSD-managed.
5554
+ * Return true if a Cursor hook entry is pp-core-managed.
5555
5555
  * Detection: presence of the GSD_CURSOR_HOOK_MARKER sentinel field.
5556
5556
  *
5557
5557
  * @param {object} entry - A hooks array element from hooks.json
@@ -5562,7 +5562,7 @@ function isManagedCursorHookEntry(entry) {
5562
5562
  }
5563
5563
 
5564
5564
  /**
5565
- * Reconcile the GSD-managed entries in a Cursor hooks.json file.
5565
+ * Reconcile the pp-core-managed entries in a Cursor hooks.json file.
5566
5566
  *
5567
5567
  * Supports both known hooks.json shapes:
5568
5568
  * 1) { "version": 1, "hooks": { "sessionStart": [...], "postToolUse": [...] } }
@@ -5583,10 +5583,10 @@ function reconcileCursorHooksJson(hooksJsonPath, managedEntries) {
5583
5583
  }
5584
5584
 
5585
5585
  /**
5586
- * #777 — Write GSD-managed Cursor lifecycle hooks into <targetDir>/hooks.json.
5586
+ * #777 — Write pp-core-managed Cursor lifecycle hooks into <targetDir>/hooks.json.
5587
5587
  *
5588
5588
  * Both managed hook scripts (pp-cursor-session-start.js, pp-cursor-post-tool.js)
5589
- * are copied from the GSD hooks/ source to <targetDir>/hooks/ first, so the
5589
+ * are copied from the pp-core hooks/ source to <targetDir>/hooks/ first, so the
5590
5590
  * hooks.json entries never reference a script that wasn't installed.
5591
5591
  *
5592
5592
  * @param {string} targetDir - The Cursor config dir (global: ~/.cursor; local: .cursor)
@@ -5599,7 +5599,7 @@ function writeCursorHooksJson(targetDir, src, opts) {
5599
5599
  }
5600
5600
 
5601
5601
  /**
5602
- * Remove all GSD-managed Cursor lifecycle hook entries from hooks.json.
5602
+ * Remove all pp-core-managed Cursor lifecycle hook entries from hooks.json.
5603
5603
  * User-owned entries are preserved. If the file becomes empty, it is removed.
5604
5604
  *
5605
5605
  * @param {string} targetDir - The Cursor config dir
@@ -5610,7 +5610,7 @@ function removeCursorHooksJson(targetDir) {
5610
5610
  }
5611
5611
 
5612
5612
  /**
5613
- * #786 — Build the GSD-managed GitHub Copilot lifecycle hook config object.
5613
+ * #786 — Build the pp-core-managed GitHub Copilot lifecycle hook config object.
5614
5614
  *
5615
5615
  * Returns the verbatim JSON shape Copilot CLI expects:
5616
5616
  * { version: 1, hooks: { sessionStart: [ <hook entry> ] } }
@@ -5628,7 +5628,7 @@ function buildCopilotHookConfig() {
5628
5628
  }
5629
5629
 
5630
5630
  /**
5631
- * #786 — Write the GSD-managed Copilot lifecycle hook config under the runtime
5631
+ * #786 — Write the pp-core-managed Copilot lifecycle hook config under the runtime
5632
5632
  * config dir (`<targetDir>/hooks/gsd-session.json`). For local installs
5633
5633
  * targetDir is `.github` (→ `.github/hooks/`); for global installs it is
5634
5634
  * `~/.copilot` (→ `~/.copilot/hooks/`) — both are valid Copilot hook locations.
@@ -5922,7 +5922,7 @@ function convertClaudeToGeminiAgent(content) {
5922
5922
  // Escape ${VAR} patterns in agent body for Gemini CLI compatibility.
5923
5923
  // Gemini's templateString() treats all ${word} patterns as template variables
5924
5924
  // and throws "Template validation failed: Missing required input parameters"
5925
- // when they can't be resolved. GSD agents use ${PHASE}, ${PLAN}, etc. as
5925
+ // when they can't be resolved. pp-core agents use ${PHASE}, ${PLAN}, etc. as
5926
5926
  // shell variables in bash code blocks — convert to $VAR (no braces) which
5927
5927
  // is equivalent bash and invisible to Gemini's /\$\{(\w+)\}/g regex.
5928
5928
  const escapedBody = body.replace(/\$\{(\w+)\}/g, '$$$1');
@@ -6585,7 +6585,7 @@ function writeHermesCategoryDescription(categoryDir) {
6585
6585
  * /gsd-profile-user) and must be preserved across reinstalls. Critically, they
6586
6586
  * MUST be excluded from gsd-file-manifest.json — otherwise saveLocalPatches()
6587
6587
  * will compare a refreshed file against a stale manifest hash and emit a
6588
- * spurious "locally modified GSD file" warning (bug #2771).
6588
+ * spurious "locally modified pp-core file" warning (bug #2771).
6589
6589
  *
6590
6590
  * Invariant: a file is either distribution (manifest-tracked, diff'd against
6591
6591
  * manifest) or user artifact (preserved across installs, never diff'd). Never
@@ -7251,7 +7251,7 @@ function _restoreDir(dir, snapshot) {
7251
7251
  *
7252
7252
  * The removal set is derived from the ACTUAL installed skill dirs (every
7253
7253
  * entry starting with 'gsd-' that is a directory), so it covers ALL shipped
7254
- * GSD skills — including 'dev-preferences' and future additions — without
7254
+ * pp-core skills — including 'dev-preferences' and future additions — without
7255
7255
  * relying on readGsdCommandNames() which only enumerates the commands source
7256
7256
  * tree and can miss skills that ship outside that directory.
7257
7257
  *
@@ -7610,7 +7610,7 @@ function uninstallRuntimeArtifacts(runtime, configDir, scope) {
7610
7610
  }
7611
7611
 
7612
7612
  // Hermes: after removing gsd-* skill dirs from skills/gsd/, also remove
7613
- // the GSD-managed DESCRIPTION.md and then the category dir itself if it
7613
+ // the pp-core-managed DESCRIPTION.md and then the category dir itself if it
7614
7614
  // contains no user content (#947). _removeGsdEntries removed gsd-* dirs
7615
7615
  // but left the category container and DESCRIPTION.md intact.
7616
7616
  if (runtime === 'hermes') {
@@ -8050,7 +8050,7 @@ function uninstall(isGlobal, runtime = 'claude') {
8050
8050
  if (runtime === 'kimi') runtimeLabel = 'Kimi CLI';
8051
8051
  if (runtime === 'codebuddy') runtimeLabel = 'CodeBuddy';
8052
8052
 
8053
- console.log(` Uninstalling GSD from ${cyan}${runtimeLabel}${reset} at ${cyan}${locationLabel}${reset}\n`);
8053
+ console.log(` Uninstalling pp-core from ${cyan}${runtimeLabel}${reset} at ${cyan}${locationLabel}${reset}\n`);
8054
8054
 
8055
8055
  // #786: AGENTS.md lives at the repo root (outside targetDir) for local Copilot
8056
8056
  // installs, so its cleanup must run even when .github (targetDir) was already
@@ -8062,10 +8062,10 @@ function uninstall(isGlobal, runtime = 'claude') {
8062
8062
  const cleaned = stripGsdFromCopilotInstructions(content);
8063
8063
  if (cleaned === null) {
8064
8064
  fs.unlinkSync(agentsMdPath);
8065
- console.log(` ${green}✓${reset} Removed AGENTS.md (was GSD-only)`);
8065
+ console.log(` ${green}✓${reset} Removed AGENTS.md (was pp-core-only)`);
8066
8066
  } else if (cleaned !== content) {
8067
8067
  fs.writeFileSync(agentsMdPath, cleaned);
8068
- console.log(` ${green}✓${reset} Cleaned GSD section from AGENTS.md`);
8068
+ console.log(` ${green}✓${reset} Cleaned pp-core section from AGENTS.md`);
8069
8069
  }
8070
8070
  }
8071
8071
  }
@@ -8116,11 +8116,11 @@ function uninstall(isGlobal, runtime = 'claude') {
8116
8116
  if (cleaned === null) {
8117
8117
  fs.unlinkSync(codexConfigPath);
8118
8118
  removedCount++;
8119
- console.log(` ${green}✓${reset} Removed config.toml (was GSD-only)`);
8119
+ console.log(` ${green}✓${reset} Removed config.toml (was pp-core-only)`);
8120
8120
  } else if (cleaned !== content) {
8121
8121
  fs.writeFileSync(codexConfigPath, cleaned);
8122
8122
  removedCount++;
8123
- console.log(` ${green}✓${reset} Cleaned GSD sections from config.toml`);
8123
+ console.log(` ${green}✓${reset} Cleaned pp-core sections from config.toml`);
8124
8124
  }
8125
8125
  }
8126
8126
 
@@ -8149,15 +8149,15 @@ function uninstall(isGlobal, runtime = 'claude') {
8149
8149
  if (cleaned === null) {
8150
8150
  fs.unlinkSync(instructionsPath);
8151
8151
  removedCount++;
8152
- console.log(` ${green}✓${reset} Removed copilot-instructions.md (was GSD-only)`);
8152
+ console.log(` ${green}✓${reset} Removed copilot-instructions.md (was pp-core-only)`);
8153
8153
  } else if (cleaned !== content) {
8154
8154
  fs.writeFileSync(instructionsPath, cleaned);
8155
8155
  removedCount++;
8156
- console.log(` ${green}✓${reset} Cleaned GSD section from copilot-instructions.md`);
8156
+ console.log(` ${green}✓${reset} Cleaned pp-core section from copilot-instructions.md`);
8157
8157
  }
8158
8158
  }
8159
8159
 
8160
- // #786: remove the GSD-managed Copilot lifecycle hook config and prune the
8160
+ // #786: remove the pp-core-managed Copilot lifecycle hook config and prune the
8161
8161
  // hooks dir if we left it empty.
8162
8162
  const hookPath = path.join(targetDir, 'hooks', GSD_COPILOT_HOOK_FILE);
8163
8163
  if (fs.existsSync(hookPath)) {
@@ -8213,24 +8213,24 @@ function uninstall(isGlobal, runtime = 'claude') {
8213
8213
  if (cleaned === null) {
8214
8214
  fs.unlinkSync(agentsPath);
8215
8215
  removedCount++;
8216
- console.log(` ${green}✓${reset} Removed ~/.agents/AGENTS.md (was GSD-only)`);
8216
+ console.log(` ${green}✓${reset} Removed ~/.agents/AGENTS.md (was pp-core-only)`);
8217
8217
  } else if (cleaned !== content) {
8218
8218
  fs.writeFileSync(agentsPath, cleaned);
8219
8219
  removedCount++;
8220
- console.log(` ${green}✓${reset} Cleaned GSD section from ~/.agents/AGENTS.md`);
8220
+ console.log(` ${green}✓${reset} Cleaned pp-core section from ~/.agents/AGENTS.md`);
8221
8221
  }
8222
8222
  }
8223
8223
  } catch { /* best-effort */ }
8224
8224
  }
8225
8225
  }
8226
8226
 
8227
- // 1b-cursor. Non-layout Cursor side-effects (issue #777): remove GSD-managed
8227
+ // 1b-cursor. Non-layout Cursor side-effects (issue #777): remove pp-core-managed
8228
8228
  // hook entries from hooks.json and clean up the managed hook scripts.
8229
8229
  if (isCursor) {
8230
8230
  const hooksJsonCleanup = removeCursorHooksJson(targetDir);
8231
8231
  if (hooksJsonCleanup.changed) {
8232
8232
  removedCount++;
8233
- console.log(` ${green}✓${reset} Removed GSD-managed Cursor hooks from hooks.json`);
8233
+ console.log(` ${green}✓${reset} Removed pp-core-managed Cursor hooks from hooks.json`);
8234
8234
  }
8235
8235
  // Remove the managed hook scripts (session-start + post-tool).
8236
8236
  const hooksDir = path.join(targetDir, 'hooks');
@@ -8379,7 +8379,7 @@ function uninstall(isGlobal, runtime = 'claude') {
8379
8379
  }
8380
8380
  }
8381
8381
 
8382
- // 3. Remove GSD agents (gsd-*.md files only)
8382
+ // 3. Remove pp-core agents (gsd-*.md files only)
8383
8383
  const agentsDir = path.join(targetDir, 'agents');
8384
8384
  if (fs.existsSync(agentsDir)) {
8385
8385
  const files = fs.readdirSync(agentsDir);
@@ -8392,11 +8392,11 @@ function uninstall(isGlobal, runtime = 'claude') {
8392
8392
  }
8393
8393
  if (agentCount > 0) {
8394
8394
  removedCount++;
8395
- console.log(` ${green}✓${reset} Removed ${agentCount} GSD agents`);
8395
+ console.log(` ${green}✓${reset} Removed ${agentCount} pp-core agents`);
8396
8396
  }
8397
8397
  }
8398
8398
 
8399
- // 4. Remove GSD hooks
8399
+ // 4. Remove pp-core hooks
8400
8400
  const hooksDir = path.join(targetDir, 'hooks');
8401
8401
  if (fs.existsSync(hooksDir)) {
8402
8402
  let hookCount = 0;
@@ -8409,10 +8409,10 @@ function uninstall(isGlobal, runtime = 'claude') {
8409
8409
  }
8410
8410
  if (hookCount > 0) {
8411
8411
  removedCount++;
8412
- console.log(` ${green}✓${reset} Removed ${hookCount} GSD hooks`);
8412
+ console.log(` ${green}✓${reset} Removed ${hookCount} pp-core hooks`);
8413
8413
  }
8414
8414
 
8415
- // Remove only the GSD-managed files from hooks/lib/ (git-cmd.js + pp-graphify-rebuild.sh).
8415
+ // Remove only the pp-core-managed files from hooks/lib/ (git-cmd.js + pp-graphify-rebuild.sh).
8416
8416
  // hooks/lib/ lives inside the user's runtime hooks directory (shared space) and
8417
8417
  // may contain user-owned custom helpers. We must not recursively delete the dir.
8418
8418
  const hooksLibDir = path.join(hooksDir, 'lib');
@@ -8441,9 +8441,9 @@ function uninstall(isGlobal, runtime = 'claude') {
8441
8441
  }
8442
8442
 
8443
8443
  // 4a. Remove scripts/changeset/ and scripts/lib/ (#935)
8444
- // GSD-managed files only: enumerate the exact set the installer writes.
8444
+ // pp-core-managed files only: enumerate the exact set the installer writes.
8445
8445
  // Any file NOT in this set is user-owned and must survive uninstall.
8446
- // After removing GSD files, attempt to rmdir — if the directory is still
8446
+ // After removing pp-core files, attempt to rmdir — if the directory is still
8447
8447
  // non-empty (user has custom helpers) it stays; otherwise it goes cleanly.
8448
8448
  const GSD_CHANGESET_FILES = [
8449
8449
  'cli.cjs', 'parse.cjs', 'render.cjs', 'serialize.cjs',
@@ -8463,7 +8463,7 @@ function uninstall(isGlobal, runtime = 'claude') {
8463
8463
  try { fs.rmdirSync(changesetUninstallDir); } catch (_) { /* Not empty — user content present */ }
8464
8464
  if (removedChangeset > 0) {
8465
8465
  removedCount++;
8466
- console.log(` ${green}✓${reset} Removed scripts/changeset/ GSD files`);
8466
+ console.log(` ${green}✓${reset} Removed scripts/changeset/ pp-core files`);
8467
8467
  }
8468
8468
  }
8469
8469
  const scriptsLibUninstallDir = path.join(targetDir, 'scripts', 'lib');
@@ -8477,7 +8477,7 @@ function uninstall(isGlobal, runtime = 'claude') {
8477
8477
  try { fs.rmdirSync(scriptsLibUninstallDir); } catch (_) { /* Not empty — user content present */ }
8478
8478
  if (removedScriptsLib > 0) {
8479
8479
  removedCount++;
8480
- console.log(` ${green}✓${reset} Removed scripts/lib/ GSD files`);
8480
+ console.log(` ${green}✓${reset} Removed scripts/lib/ pp-core files`);
8481
8481
  }
8482
8482
  }
8483
8483
  // Remove scripts/fix-slash-commands.cjs (#1223) — must come before the scripts/ rmdir
@@ -8490,7 +8490,7 @@ function uninstall(isGlobal, runtime = 'claude') {
8490
8490
  try { fs.rmdirSync(scriptsUninstallDir); } catch (_) { /* Not empty — leave it */ }
8491
8491
  }
8492
8492
 
8493
- // 5. Remove GSD package.json (CommonJS mode marker)
8493
+ // 5. Remove pp-core package.json (CommonJS mode marker)
8494
8494
  const pkgJsonPath = path.join(targetDir, 'package.json');
8495
8495
  if (fs.existsSync(pkgJsonPath)) {
8496
8496
  try {
@@ -8499,14 +8499,14 @@ function uninstall(isGlobal, runtime = 'claude') {
8499
8499
  if (content === '{"type":"commonjs"}') {
8500
8500
  fs.unlinkSync(pkgJsonPath);
8501
8501
  removedCount++;
8502
- console.log(` ${green}✓${reset} Removed GSD package.json`);
8502
+ console.log(` ${green}✓${reset} Removed pp-core package.json`);
8503
8503
  }
8504
8504
  } catch (e) {
8505
8505
  // Ignore read errors
8506
8506
  }
8507
8507
  }
8508
8508
 
8509
- // 6. Clean up settings.json (remove GSD hooks and statusline)
8509
+ // 6. Clean up settings.json (remove pp-core hooks and statusline)
8510
8510
  const settingsPath = path.join(targetDir, 'settings.json');
8511
8511
  if (fs.existsSync(settingsPath)) {
8512
8512
  let settings = readSettings(settingsPath);
@@ -8516,15 +8516,15 @@ function uninstall(isGlobal, runtime = 'claude') {
8516
8516
  }
8517
8517
  let settingsModified = false;
8518
8518
 
8519
- // Remove GSD statusline if it references our hook
8519
+ // Remove pp-core statusline if it references our hook
8520
8520
  if (settings.statusLine && settings.statusLine.command &&
8521
8521
  settings.statusLine.command.includes('pp-statusline')) {
8522
8522
  delete settings.statusLine;
8523
8523
  settingsModified = true;
8524
- console.log(` ${green}✓${reset} Removed GSD statusline from settings`);
8524
+ console.log(` ${green}✓${reset} Removed pp-core statusline from settings`);
8525
8525
  }
8526
8526
 
8527
- // Remove GSD hooks from settings — per-hook granularity to preserve
8527
+ // Remove pp-core hooks from settings — per-hook granularity to preserve
8528
8528
  // user hooks that share an entry with a GSD hook (#1755 followup).
8529
8529
  // Includes the 3 Qwen-only events added in #788 (SubagentStop, Stop,
8530
8530
  // PreCompact, also registered for Claude in #770), the 3 Gemini-only
@@ -8538,7 +8538,7 @@ function uninstall(isGlobal, runtime = 'claude') {
8538
8538
  settings.hooks[eventName] = settings.hooks[eventName]
8539
8539
  .map(entry => {
8540
8540
  if (!entry || typeof entry !== 'object' || !Array.isArray(entry.hooks)) return entry;
8541
- // Filter out individual GSD hooks, keep user hooks
8541
+ // Filter out individual pp-core hooks, keep user hooks
8542
8542
  entry.hooks = entry.hooks.filter((h) => {
8543
8543
  if (!h || typeof h.command !== 'string') return true;
8544
8544
  return !isManagedHookCommand(h.command, {
@@ -8557,7 +8557,7 @@ function uninstall(isGlobal, runtime = 'claude') {
8557
8557
  }
8558
8558
  }
8559
8559
  if (settingsModified) {
8560
- console.log(` ${green}✓${reset} Removed GSD hooks from settings`);
8560
+ console.log(` ${green}✓${reset} Removed pp-core hooks from settings`);
8561
8561
  }
8562
8562
 
8563
8563
  // Clean up empty hooks object
@@ -8569,7 +8569,7 @@ function uninstall(isGlobal, runtime = 'claude') {
8569
8569
  // Applies only to Claude uninstalls. Filter only the exact GSD-owned entries
8570
8570
  // to preserve any user-added allow/deny entries.
8571
8571
  // Uses a local flag to avoid the shared `settingsModified` producing a false
8572
- // "Removed GSD permissions" message when only hooks/statusline changed.
8572
+ // "Removed pp-core permissions" message when only hooks/statusline changed.
8573
8573
  if (runtime === 'claude' && settings.permissions) {
8574
8574
  let permissionsModified = false;
8575
8575
  if (Array.isArray(settings.permissions.allow)) {
@@ -8592,7 +8592,7 @@ function uninstall(isGlobal, runtime = 'claude') {
8592
8592
  }
8593
8593
  if (permissionsModified) {
8594
8594
  settingsModified = true;
8595
- console.log(` ${green}✓${reset} Removed GSD permissions from settings.json`);
8595
+ console.log(` ${green}✓${reset} Removed pp-core permissions from settings.json`);
8596
8596
  }
8597
8597
  }
8598
8598
 
@@ -8635,7 +8635,7 @@ function uninstall(isGlobal, runtime = 'claude') {
8635
8635
  if (modified) {
8636
8636
  fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
8637
8637
  removedCount++;
8638
- console.log(` ${green}✓${reset} Removed GSD permissions from ${path.basename(configPath)}`);
8638
+ console.log(` ${green}✓${reset} Removed pp-core permissions from ${path.basename(configPath)}`);
8639
8639
  }
8640
8640
  } catch (e) {
8641
8641
  // Ignore JSON parse errors
@@ -8676,7 +8676,7 @@ function uninstall(isGlobal, runtime = 'claude') {
8676
8676
  if (modified) {
8677
8677
  fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
8678
8678
  removedCount++;
8679
- console.log(` ${green}✓${reset} Removed GSD permissions from ${path.basename(configPath)}`);
8679
+ console.log(` ${green}✓${reset} Removed pp-core permissions from ${path.basename(configPath)}`);
8680
8680
  }
8681
8681
  } catch (e) {
8682
8682
  // Ignore JSON parse errors
@@ -8694,7 +8694,7 @@ function uninstall(isGlobal, runtime = 'claude') {
8694
8694
  }
8695
8695
 
8696
8696
  if (removedCount === 0) {
8697
- console.log(` ${yellow}⚠${reset} No GSD files found to remove.`);
8697
+ console.log(` ${yellow}⚠${reset} No pp-core files found to remove.`);
8698
8698
  }
8699
8699
 
8700
8700
  console.log(`
@@ -8841,7 +8841,7 @@ function configureOpencodePermissions(isGlobal = true, configDir = null) {
8841
8841
 
8842
8842
  // Write config back
8843
8843
  fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
8844
- console.log(` ${green}✓${reset} Configured read permission for GSD docs`);
8844
+ console.log(` ${green}✓${reset} Configured read permission for pp-core docs`);
8845
8845
  }
8846
8846
 
8847
8847
  /**
@@ -8915,7 +8915,7 @@ function configureKiloPermissions(isGlobal = true, configDir = null) {
8915
8915
 
8916
8916
  // Write config back
8917
8917
  fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
8918
- console.log(` ${green}✓${reset} Configured read permission for GSD docs`);
8918
+ console.log(` ${green}✓${reset} Configured read permission for pp-core docs`);
8919
8919
  }
8920
8920
 
8921
8921
  /**
@@ -9063,7 +9063,7 @@ function writeManifest(configDir, runtime = 'claude', options = {}) {
9063
9063
  // Flat-hyphen layout: pp-*.md files live directly in commands/ (no gsd/ subdir).
9064
9064
  const commandsDir = path.join(configDir, 'commands');
9065
9065
  const opencodeCommandDir = path.join(configDir, 'command');
9066
- // Hermes nests GSD skills under skills/gsd/ as a single category (#2841).
9066
+ // Hermes nests pp-core skills under skills/gsd/ as a single category (#2841).
9067
9067
  // All other runtimes that use the Codex-style skills layout use a flat skills/ root.
9068
9068
  const codexSkillsDir = isHermes
9069
9069
  ? path.join(configDir, 'skills', 'gsd')
@@ -9298,7 +9298,7 @@ function populatePristineDir({ packageSrc, pristineDir, modified, runtime, pathP
9298
9298
  }
9299
9299
 
9300
9300
  /**
9301
- * Detect user-modified GSD files by comparing against install manifest.
9301
+ * Detect user-modified pp-core files by comparing against install manifest.
9302
9302
  * Backs up modified files to gsd-local-patches/ for reapply after update.
9303
9303
  * Also saves pristine copies (from manifest) to gsd-pristine/ to enable
9304
9304
  * three-way merge during reapply-patches (pristine vs user vs new).
@@ -9366,7 +9366,7 @@ function saveLocalPatches(configDir, pristineCtx) {
9366
9366
  meta.pristine_hashes[relPath] = pristineHashes[relPath];
9367
9367
  }
9368
9368
  fs.writeFileSync(path.join(patchesDir, 'backup-meta.json'), JSON.stringify(meta, null, 2));
9369
- console.log(' ' + yellow + 'i' + reset + ' Found ' + modified.length + ' locally modified GSD file(s) — backed up to ' + PATCHES_DIR_NAME + '/');
9369
+ console.log(' ' + yellow + 'i' + reset + ' Found ' + modified.length + ' locally modified pp-core file(s) — backed up to ' + PATCHES_DIR_NAME + '/');
9370
9370
  for (const f of modified) {
9371
9371
  console.log(' ' + dim + f + reset);
9372
9372
  }
@@ -9725,7 +9725,7 @@ function install(isGlobal, runtime = 'claude', options = {}) {
9725
9725
  rollback();
9726
9726
  };
9727
9727
 
9728
- // Save any locally modified GSD files before they get wiped.
9728
+ // Save any locally modified pp-core files before they get wiped.
9729
9729
  // The pristine context lets saveLocalPatches populate gsd-pristine/ via
9730
9730
  // the install transform pipeline, giving the reapply-patches Step 5
9731
9731
  // verifier a real diff baseline (#2998).
@@ -9931,7 +9931,7 @@ function install(isGlobal, runtime = 'claude', options = {}) {
9931
9931
  // #3541: non-interactive runs (typical /gsd-update via Claude Code) have
9932
9932
  // no stdin TTY and therefore no way to answer prompt-user migration
9933
9933
  // actions. Resolve safe categories by classification (stale SDK build
9934
- // artifacts → remove; user-facing skills → keep; bundled GSD hooks →
9934
+ // artifacts → remove; user-facing skills → keep; bundled pp-core hooks →
9935
9935
  // remove [#3610]) and log every resolution; anything that cannot be
9936
9936
  // safely defaulted falls through to assertInstallerMigrationsUnblocked,
9937
9937
  // which now emits a grouped error with the documented resolution path.
@@ -9939,7 +9939,7 @@ function install(isGlobal, runtime = 'claude', options = {}) {
9939
9939
  // #3610: the classifier-based resolution must run regardless of TTY.
9940
9940
  // For unambiguous categories (e.g. `hooks/gsd-*` bundled hooks left
9941
9941
  // behind by a previous version), there is no actual "user choice" to
9942
- // make — the file is a known GSD-managed artifact and the installer is
9942
+ // make — the file is a known pp-core-managed artifact and the installer is
9943
9943
  // about to write the fresh bundled version. Gating the resolver on
9944
9944
  // `!isTTY` made `npx @opengsd/gsd-core@latest --codex` hard-abort with
9945
9945
  // 12 blocked bundled hooks. The env-override branch (operator-supplied
@@ -10011,7 +10011,7 @@ function install(isGlobal, runtime = 'claude', options = {}) {
10011
10011
  // #1326 — Codex only: remove stale agents/openai.yaml sidecars from managed
10012
10012
  // gsd-* skill dirs. Prior installs wrote these files so Codex would show a
10013
10013
  // display name and description in the /skills TUI popup. Recent Codex builds
10014
- // index BOTH SKILL.md and the sidecar, causing each GSD skill to appear twice
10014
+ // index BOTH SKILL.md and the sidecar, causing each pp-core skill to appear twice
10015
10015
  // in autocomplete. Cleaning them up fixes the duplication; SKILL.md alone is
10016
10016
  // sufficient for Codex discovery. User-owned dirs are never touched.
10017
10017
  if (isCodex) {
@@ -10150,7 +10150,7 @@ function install(isGlobal, runtime = 'claude', options = {}) {
10150
10150
  // Global installs are handled above by _isSkillsRuntime (#782).
10151
10151
  console.log(` ${green}✓${reset} Cline: commands will be available via .clinerules`);
10152
10152
  } else if (isGemini) {
10153
- // #3037: when running --local --gemini and a GSD-managed user-scope
10153
+ // #3037: when running --local --gemini and a pp-core-managed user-scope
10154
10154
  // command directory already exists at ~/.gemini/commands/gsd/, skip
10155
10155
  // the local copy. Gemini conflict-detects by command name across
10156
10156
  // scopes and renames every overlapping /gsd:* command to
@@ -10226,7 +10226,7 @@ function install(isGlobal, runtime = 'claude', options = {}) {
10226
10226
  fs.rmSync(path.join(staleSkillsDir, e.name), { recursive: true });
10227
10227
  }
10228
10228
  if (staleGsd.length > 0) {
10229
- console.log(` ${green}✓${reset} Removed ${staleGsd.length} stale GSD skill(s) from skills/`);
10229
+ console.log(` ${green}✓${reset} Removed ${staleGsd.length} stale pp-core skill(s) from skills/`);
10230
10230
  }
10231
10231
  }
10232
10232
  }
@@ -10434,6 +10434,31 @@ function install(isGlobal, runtime = 'claude', options = {}) {
10434
10434
  }
10435
10435
  }
10436
10436
 
10437
+ // Copy NOTICE (MIT attribution to upstream gsd-core) — the installed
10438
+ // footprint is a distributed copy, so the attribution must travel with it.
10439
+ const noticeSrc = path.join(src, 'NOTICE');
10440
+ const noticeDest = path.join(targetDir, 'pp-core', 'NOTICE');
10441
+ if (fs.existsSync(noticeSrc)) {
10442
+ fs.copyFileSync(noticeSrc, noticeDest);
10443
+ if (verifyFileInstalled(noticeDest, 'NOTICE')) {
10444
+ console.log(` ${green}✓${reset} Installed NOTICE`);
10445
+ } else {
10446
+ failures.push('NOTICE');
10447
+ }
10448
+ }
10449
+
10450
+ // Copy LICENSE alongside it for the same reason.
10451
+ const licenseSrc = path.join(src, 'LICENSE');
10452
+ const licenseDest = path.join(targetDir, 'pp-core', 'LICENSE');
10453
+ if (fs.existsSync(licenseSrc)) {
10454
+ fs.copyFileSync(licenseSrc, licenseDest);
10455
+ if (verifyFileInstalled(licenseDest, 'LICENSE')) {
10456
+ console.log(` ${green}✓${reset} Installed LICENSE`);
10457
+ } else {
10458
+ failures.push('LICENSE');
10459
+ }
10460
+ }
10461
+
10437
10462
  // Write VERSION file
10438
10463
  const versionDest = path.join(targetDir, 'pp-core', 'VERSION');
10439
10464
  fs.writeFileSync(versionDest, pkg.version);
@@ -10949,7 +10974,7 @@ function install(isGlobal, runtime = 'claude', options = {}) {
10949
10974
  let configContent = fs.existsSync(configPath) ? fs.readFileSync(configPath, 'utf-8') : '';
10950
10975
  const eol = detectLineEnding(configContent);
10951
10976
 
10952
- // Strip ALL prior GSD-managed hook blocks BEFORE migration so the migration
10977
+ // Strip ALL prior pp-core-managed hook blocks BEFORE migration so the migration
10953
10978
  // only touches user-authored hooks, not GSD-owned stale entries. Running
10954
10979
  // strip after migration causes Shape 1 (legacy gsd-update-check filename)
10955
10980
  // to be converted by migration before the strip regex can match it (#2698).
@@ -10962,7 +10987,7 @@ function install(isGlobal, runtime = 'claude', options = {}) {
10962
10987
  configContent = stripStaleGsdHookBlocks(configContent);
10963
10988
 
10964
10989
  // Migrate legacy [hooks] map format and flat [[hooks]] AoT entries to the
10965
- // namespaced [[hooks.<EVENT>]] form after stripping GSD-managed stale blocks.
10990
+ // namespaced [[hooks.<EVENT>]] form after stripping pp-core-managed stale blocks.
10966
10991
  // Running migration after strip ensures only user-authored hooks are migrated
10967
10992
  // (#2698 regression: migration before strip converts stale GSD blocks before
10968
10993
  // the strip regexes can match their original shape).
@@ -10975,7 +11000,7 @@ function install(isGlobal, runtime = 'claude', options = {}) {
10975
11000
  const codexHooksFeature = ensureCodexHooksFeature(configContent);
10976
11001
  configContent = setManagedCodexHooksOwnership(codexHooksFeature.content, codexHooksFeature.ownership);
10977
11002
 
10978
- // GSD-managed Codex hook payloads now live in hooks.json to avoid mixed
11003
+ // pp-core-managed Codex hook payloads now live in hooks.json to avoid mixed
10979
11004
  // representation warnings when a single layer contains both hooks.json
10980
11005
  // and inline [hooks] entries. Keep config.toml focused on feature flags
10981
11006
  // and agent metadata.
@@ -11243,7 +11268,7 @@ function install(isGlobal, runtime = 'claude', options = {}) {
11243
11268
  }
11244
11269
  }
11245
11270
  writeSettings(sharedSettingsPath, sharedRaw);
11246
- console.log(` ${green}✓${reset} Migrated GSD hook entries from settings.json to settings.local.json (#338)`);
11271
+ console.log(` ${green}✓${reset} Migrated pp-core hook entries from settings.json to settings.local.json (#338)`);
11247
11272
  }
11248
11273
  }
11249
11274
  }
@@ -11341,7 +11366,7 @@ function install(isGlobal, runtime = 'claude', options = {}) {
11341
11366
  }
11342
11367
  }
11343
11368
 
11344
- // Register all GSD-managed hook entries into settings.hooks.* for runtimes
11369
+ // Register all pp-core-managed hook entries into settings.hooks.* for runtimes
11345
11370
  // that use the settings.json hook surface (ADR-857 phase 5f-1b).
11346
11371
  // settings is mutated in place by applySettingsJsonHooks.
11347
11372
  applySettingsJsonHooks(settings, {
@@ -11676,7 +11701,7 @@ function handleStatusline(settings, isInteractive, callback) {
11676
11701
  • Context window usage (color-coded)
11677
11702
 
11678
11703
  ${cyan}1${reset}) Keep existing
11679
- ${cyan}2${reset}) Replace with GSD statusline
11704
+ ${cyan}2${reset}) Replace with pp-core statusline
11680
11705
  `);
11681
11706
 
11682
11707
  rl.question(` Choice ${dim}[1]${reset}: `, (answer) => {
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: pp:build-handoff
3
- description: "[DEPRECATED] Alias for /pp-discovery-to-build — transition a completed Pareto Discovery Sprint into a pp-core-ready project. Use /pp-discovery-to-build; this alias will be removed in a future major."
3
+ description: "[DEPRECATED] Alias for /pp-discovery-to-build; removed in a future major."
4
4
  argument-hint: "[--skip-validation] [--scaffold]"
5
5
  allowed-tools:
6
6
  - Read
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: pp:discovery-to-build
3
- description: Transition a completed Pareto Discovery Sprint into a pp-core-ready project — synthesize sessions into .planning/ and wire cross-references.
3
+ description: Transition a completed Pareto Discovery Sprint into a pp-core-ready project.
4
4
  argument-hint: "[--skip-validation] [--scaffold]"
5
5
  allowed-tools:
6
6
  - Read
@@ -5,7 +5,7 @@ argument-hint: ""
5
5
  allowed-tools:
6
6
  - Read
7
7
  - Skill
8
- requires: [init, new-project, new-milestone, complete-milestone, audit-milestone, milestone-summary, import, ingest-docs, profile-user, review-backlog]
8
+ requires: [init, new-project, discovery-to-build, build-handoff, new-milestone, complete-milestone, audit-milestone, milestone-summary, import, ingest-docs, profile-user, review-backlog]
9
9
  ---
10
10
 
11
11
  Route to the appropriate project / milestone skill based on the user's intent.
@@ -16,6 +16,8 @@ inline as part of `pp-audit-milestone`'s output.
16
16
  |---|---|
17
17
  | Initialize a project (canonical front door) | pp-init |
18
18
  | Start a new project | pp-new-project |
19
+ | Hand off a completed Discovery Sprint into the build flow | pp-discovery-to-build |
20
+ | (deprecated) Discovery→build handoff alias | pp-build-handoff |
19
21
  | Create a new milestone | pp-new-milestone |
20
22
  | Complete the current milestone | pp-complete-milestone |
21
23
  | Audit a milestone for issues | pp-audit-milestone |
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pp-core",
3
- "version": "1.8.0",
3
+ "version": "1.8.2",
4
4
  "description": "PP Core — a meta-prompting, context engineering, and spec-driven development system for AI coding agents. Loads pp-core's operating context into every Gemini CLI session.",
5
5
  "contextFileName": "GEMINI.md"
6
6
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pareto4/pp-core",
3
- "version": "1.8.0",
3
+ "version": "1.8.2",
4
4
  "description": "Pareto pp-core — The Pareto Project's AI-assisted engineering framework: meta-prompting, context engineering, and spec-driven development for AI coding agents.",
5
5
  "bin": {
6
6
  "pp-core": "bin/install.js",
@@ -8,6 +8,8 @@
8
8
  "pp_run": "pp-core/bin/pp_run"
9
9
  },
10
10
  "files": [
11
+ "LICENSE",
12
+ "NOTICE",
11
13
  "bin",
12
14
  "commands",
13
15
  "pp-core",
@@ -23,6 +23,10 @@ exports.CLUSTERS = Object.freeze({
23
23
  'init',
24
24
  'new-project',
25
25
  'discovery',
26
+ // Discovery→build handoff: canonical + retained alias, grouped with the
27
+ // other project-lifecycle entrypoints (routed via pp-ns-project).
28
+ 'discovery-to-build',
29
+ 'build-handoff',
26
30
  'discuss-phase',
27
31
  'plan-phase',
28
32
  'execute-phase',
@@ -1825,9 +1825,9 @@ function buildSkillManifest(cwd, skillsDir = null) {
1825
1825
  // Nested layout: <entry>/skills/<stem>/SKILL.md
1826
1826
  // Used by cline, qwen, hermes, augment, trae, antigravity (#69 nested=true).
1827
1827
  // Descend exactly one level into <entry>/skills/ — no deeper recursion.
1828
- // Scope to gsd-ns-* routers only: never vacuum up an unrelated user skill
1829
- // that happens to have its own `skills/` subdirectory.
1830
- if (!entry.name.startsWith('gsd-ns-'))
1828
+ // Scope to pp-ns-* routers (plus legacy gsd-ns-*) only: never vacuum up an
1829
+ // unrelated user skill that happens to have its own `skills/` subdirectory.
1830
+ if (!entry.name.startsWith('pp-ns-') && !entry.name.startsWith('gsd-ns-'))
1831
1831
  continue;
1832
1832
  const nestedSkillsDir = node_path_1.default.join(rootPath, entry.name, 'skills');
1833
1833
  let nestedEntries = [];
@@ -1859,7 +1859,7 @@ function buildSkillManifest(cwd, skillsDir = null) {
1859
1859
  const rootCmp = a.root.localeCompare(b.root);
1860
1860
  return rootCmp !== 0 ? rootCmp : a.name.localeCompare(b.name);
1861
1861
  });
1862
- const gsdSkillsInstalled = skills.some((skill) => skill.name.startsWith('gsd-'));
1862
+ const gsdSkillsInstalled = skills.some((skill) => skill.name.startsWith('pp-') || skill.name.startsWith('gsd-'));
1863
1863
  return {
1864
1864
  skills,
1865
1865
  roots,
@@ -151,7 +151,7 @@ function classifyPromptUserAction(action) {
151
151
  if (/^pp-core\/sdk\/(dist|src)\//.test(relPath)) {
152
152
  return { category: 'stale-sdk-build-artifact', choice: 'remove' };
153
153
  }
154
- if (/^skills\/gsd-[^/]+\/SKILL\.md$/.test(relPath)) {
154
+ if (/^skills\/(?:pp|gsd)-[^/]+\/SKILL\.md$/.test(relPath)) {
155
155
  return { category: 'user-facing-skill', choice: 'keep' };
156
156
  }
157
157
  // #3610 / #3628: bundled GSD hooks shipped under `hooks/`. The whitelist
@@ -43,7 +43,6 @@ function normalizeRuntimeToken(value) {
43
43
  function loadAliasManifest() {
44
44
  const manifestCandidates = [
45
45
  node_path_1.default.resolve(__dirname, '..', 'shared', 'runtime-aliases.manifest.json'),
46
- node_path_1.default.resolve(__dirname, '../../../sdk/shared/runtime-aliases.manifest.json'),
47
46
  ];
48
47
  for (const manifestPath of manifestCandidates) {
49
48
  try {
@@ -439,11 +439,11 @@ function _syncGsdDir(stagedDir, destDir, kind, manifest) {
439
439
  }
440
440
  // Prune stale GSD-owned files not in the staged set, preserving user-owned files
441
441
  // (mirrors install's prefix-scoped _removeGsdEntries):
442
- // - agents: only gsd-* are GSD-owned
442
+ // - agents: only `${kindPrefix}`-prefixed (plus legacy gsd-*) are GSD-owned
443
443
  // - flat command dirs: only `${kindPrefix}`-prefixed are GSD-owned
444
444
  // - namespaced command dirs: the whole dir is GSD-owned
445
445
  for (const file of node_fs_1.default.readdirSync(destDir).filter(f => f.endsWith('.md'))) {
446
- if (kindName === 'agents' && !file.startsWith('gsd-'))
446
+ if (kindName === 'agents' && !(kindPrefix && file.startsWith(kindPrefix)) && !file.startsWith('gsd-'))
447
447
  continue;
448
448
  if (kindName === 'commands' && !namespacedByDir && kindPrefix && !file.startsWith(kindPrefix))
449
449
  continue;
@@ -68,6 +68,19 @@ All workflow routing commands include `${GSD_WS}` which:
68
68
  This ensures workstream scope chains automatically through the workflow:
69
69
  `new-milestone → discuss-phase → plan-phase → execute-phase → transition`
70
70
 
71
+ ### Substitution rule (user-facing output and Skill/Task args)
72
+
73
+ Outside executed bash blocks, `${GSD_WS}` / `{GSD_WS}` is a placeholder, not a
74
+ shell variable — nothing expands it automatically. When a workflow emits a
75
+ routing or "Next:" command, or passes args to `Skill()`/`Task()`, Claude must
76
+ substitute the placeholder itself:
77
+
78
+ - Workstream active → replace with `--ws <name>`
79
+ - Flat mode → remove the placeholder entirely (and any doubled whitespace)
80
+
81
+ Never print the literal token `${GSD_WS}` in a user-facing command — a pasted
82
+ command containing it breaks in the shell.
83
+
71
84
  ## Directory Structure
72
85
 
73
86
  ```
@@ -22,6 +22,9 @@ Valid pp-core subagent types (use exact names — do not fall back to 'general-p
22
22
  **Flags (parse from `$ARGUMENTS`):**
23
23
  - `--skip-validation` — Skip the Discovery completion check (use when sessions are intentionally incomplete)
24
24
  - `--scaffold` — After initialization, also scaffold lightweight application structure from the Session 5 tech stack
25
+ - `--text` — Text mode: render every interactive prompt as a plain-text numbered list (for non-Claude runtimes)
26
+
27
+ **Text mode (`workflow.text_mode: true` in config or `--text` flag):** Set `TEXT_MODE=true` if `--text` is present in `$ARGUMENTS` OR `text_mode` from init JSON is `true`. When TEXT_MODE is active, replace every `AskUserQuestion` call in this workflow with a plain-text numbered list and ask the user to type their choice number. This is required for non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is not available.
25
28
 
26
29
  **Prerequisites:**
27
30
  - A Pareto project with a `Discovery/` folder (created by `pareto-project-setup`)
@@ -36,7 +39,6 @@ Valid pp-core subagent types (use exact names — do not fall back to 'general-p
36
39
  Resolve the `pp_run` helper before anything else. Every `pp_run` call below uses this shim.
37
40
 
38
41
  ```bash
39
- _PP_SHIM_NAME="pp-tools.cjs"; _PP_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; PP_TOOLS="${_PP_RUNTIME_ROOT}/pp-core/bin/${_PP_SHIM_NAME}"; if [ -f "$PP_TOOLS" ]; then pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${_PP_RUNTIME_ROOT}/.claude/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${_PP_RUNTIME_ROOT}/.claude/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif command -v pp-tools >/dev/null 2>&1; then PP_TOOLS="$(command -v pp-tools)"; pp_run() { "$PP_TOOLS" "$@"; }; elif [ -f "$HOME/.claude/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="$HOME/.claude/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; else echo "ERROR: pp-tools.cjs not found. Run: npx -y @pareto4/pp-core@latest --claude --local" >&2; exit 1; fi
40
42
  ```
41
43
 
42
44
  ---
@@ -343,6 +345,7 @@ workflow uses.
343
345
  ### 5a. Run init
344
346
 
345
347
  ```bash
348
+ _PP_SHIM_NAME="pp-tools.cjs"; _PP_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; PP_TOOLS="${_PP_RUNTIME_ROOT}/pp-core/bin/${_PP_SHIM_NAME}"; if [ -f "$PP_TOOLS" ]; then pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${_PP_RUNTIME_ROOT}/.claude/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${_PP_RUNTIME_ROOT}/.claude/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${_PP_RUNTIME_ROOT}/.codex/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${_PP_RUNTIME_ROOT}/.codex/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif command -v pp-tools >/dev/null 2>&1; then PP_TOOLS="$(command -v pp-tools)"; pp_run() { "$PP_TOOLS" "$@"; }; elif [ -f "$HOME/.claude/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="$HOME/.claude/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${HERMES_HOME:-$HOME/.hermes}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${HERMES_HOME:-$HOME/.hermes}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${CURSOR_CONFIG_DIR:-$HOME/.cursor}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${CURSOR_CONFIG_DIR:-$HOME/.cursor}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${CODEX_HOME:-$HOME/.codex}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${CODEX_HOME:-$HOME/.codex}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${GEMINI_CONFIG_DIR:-$HOME/.gemini}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${GEMINI_CONFIG_DIR:-$HOME/.gemini}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${COPILOT_CONFIG_DIR:-$HOME/.copilot}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${COPILOT_CONFIG_DIR:-$HOME/.copilot}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${AUGMENT_CONFIG_DIR:-$HOME/.augment}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${AUGMENT_CONFIG_DIR:-$HOME/.augment}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${TRAE_CONFIG_DIR:-$HOME/.trae}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${TRAE_CONFIG_DIR:-$HOME/.trae}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${QWEN_CONFIG_DIR:-$HOME/.qwen}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${QWEN_CONFIG_DIR:-$HOME/.qwen}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${CLINE_CONFIG_DIR:-$HOME/.cline}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${CLINE_CONFIG_DIR:-$HOME/.cline}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${GROK_AGENTS_HOME:-$HOME/.agents}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${GROK_AGENTS_HOME:-$HOME/.agents}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; elif [ -f "${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/pp-core/bin/${_PP_SHIM_NAME}" ]; then PP_TOOLS="${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/pp-core/bin/${_PP_SHIM_NAME}"; pp_run() { node "$PP_TOOLS" "$@"; }; else echo "ERROR: pp-tools.cjs not found at $PP_TOOLS and pp-tools is not on PATH. Run: npx -y @pareto4/pp-core@latest --claude --local" >&2; exit 1; fi; if [ -n "${CLAUDE_ENV_FILE:-}" ] && [ -n "${PP_TOOLS:-}" ]; then printf "export PATH='%s':\"\$PATH\"\n" "${PP_TOOLS%/*}" >> "$CLAUDE_ENV_FILE" 2>/dev/null || true; fi
346
349
  mkdir -p .planning
347
350
  INIT=$(pp_run query init.new-project 2>/dev/null)
348
351
  if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
@@ -1,5 +1,10 @@
1
1
  # --chain mode — interactive discuss, then auto-advance
2
2
 
3
+ <workstream_flag_substitution>
4
+ `${GSD_WS}` / `{GSD_WS}` in emitted "Next:" commands and `Skill()` args and agent dispatch prompts is a placeholder, not a shell variable — nothing expands it automatically. Substitute the active `--ws <name>`, or remove it entirely in flat mode. Never print the literal token in user-facing output. See `pp-core/references/workstream-flag.md` § Substitution rule.
5
+ </workstream_flag_substitution>
6
+
7
+
3
8
  > **Lazy-loaded.** Read this file from `workflows/discuss-phase.md` when
4
9
  > `--chain` is present in `$ARGUMENTS`, or when the parent's `auto_advance`
5
10
  > step needs to dispatch to plan-phase under `--auto`.
@@ -11,6 +11,10 @@ Extract implementation decisions that downstream agents need. Analyze the phase
11
11
  You are a thinking partner, not an interviewer. The user is the visionary — you are the builder. Your job is to capture decisions that will guide research and planning, not to figure out implementation yourself.
12
12
  </purpose>
13
13
 
14
+ <workstream_flag_substitution>
15
+ `${GSD_WS}` / `{GSD_WS}` in emitted "Next:" commands and `Skill()` args and agent dispatch prompts is a placeholder, not a shell variable — nothing expands it automatically. Substitute the active `--ws <name>`, or remove it entirely in flat mode. Never print the literal token in user-facing output. See `pp-core/references/workstream-flag.md` § Substitution rule.
16
+ </workstream_flag_substitution>
17
+
14
18
  <required_reading>
15
19
  @~/.claude/pp-core/references/domain-probes.md
16
20
  @~/.claude/pp-core/references/gate-prompts.md
@@ -9,6 +9,10 @@ consumes: PLAN.md
9
9
  Execute all plans in a phase using wave-based parallel execution. Orchestrator stays lean — delegates plan execution to subagents.
10
10
  </purpose>
11
11
 
12
+ <workstream_flag_substitution>
13
+ `${GSD_WS}` / `{GSD_WS}` in emitted "Next:" commands and `Skill()` args and agent dispatch prompts is a placeholder, not a shell variable — nothing expands it automatically. Substitute the active `--ws <name>`, or remove it entirely in flat mode. Never print the literal token in user-facing output. See `pp-core/references/workstream-flag.md` § Substitution rule.
14
+ </workstream_flag_substitution>
15
+
12
16
  <core_principle>
13
17
  Orchestrator coordinates, not executes. Each subagent loads the full execute-plan context. Orchestrator: discover plans → analyze deps → group waves → spawn agents → handle checkpoints → collect results.
14
18
  </core_principle>
@@ -4,6 +4,10 @@ Start a new milestone cycle for an existing project. Loads project context, gath
4
4
 
5
5
  </purpose>
6
6
 
7
+ <workstream_flag_substitution>
8
+ `${GSD_WS}` / `{GSD_WS}` in emitted "Next:" commands and `Skill()` args and agent dispatch prompts is a placeholder, not a shell variable — nothing expands it automatically. Substitute the active `--ws <name>`, or remove it entirely in flat mode. Never print the literal token in user-facing output. See `pp-core/references/workstream-flag.md` § Substitution rule.
9
+ </workstream_flag_substitution>
10
+
7
11
  <required_reading>
8
12
 
9
13
  Read all files referenced by the invoking prompt's execution_context before starting.
@@ -9,6 +9,10 @@ consumes: CONTEXT.md
9
9
  Create executable phase prompts (PLAN.md files) for a roadmap phase with integrated research and verification. Default flow: Research (if needed) -> Plan -> Verify -> Done. Orchestrates pp-phase-researcher, pp-planner, and pp-plan-checker agents with a revision loop (max 3 iterations).
10
10
  </purpose>
11
11
 
12
+ <workstream_flag_substitution>
13
+ `${GSD_WS}` / `{GSD_WS}` in emitted "Next:" commands and `Skill()` args and agent dispatch prompts is a placeholder, not a shell variable — nothing expands it automatically. Substitute the active `--ws <name>`, or remove it entirely in flat mode. Never print the literal token in user-facing output. See `pp-core/references/workstream-flag.md` § Substitution rule.
14
+ </workstream_flag_substitution>
15
+
12
16
  <required_reading>
13
17
  Read all files referenced by the invoking prompt's execution_context before starting.
14
18
 
@@ -2,6 +2,10 @@
2
2
  Check project progress, summarize recent work and what's ahead, then intelligently route to the next action — either executing an existing plan or creating the next one. Provides situational awareness before continuing work.
3
3
  </purpose>
4
4
 
5
+ <workstream_flag_substitution>
6
+ `${GSD_WS}` / `{GSD_WS}` in emitted "Next:" commands and `Skill()` args and agent dispatch prompts is a placeholder, not a shell variable — nothing expands it automatically. Substitute the active `--ws <name>`, or remove it entirely in flat mode. Never print the literal token in user-facing output. See `pp-core/references/workstream-flag.md` § Substitution rule.
7
+ </workstream_flag_substitution>
8
+
5
9
  <required_reading>
6
10
  Read all files referenced by the invoking prompt's execution_context before starting.
7
11
  </required_reading>
@@ -12,6 +12,10 @@ With `--research` flag: spawns a focused research agent before planning. Investi
12
12
  Granular flags are composable: `--discuss --research --validate` gives the same result as `--full`.
13
13
  </purpose>
14
14
 
15
+ <workstream_flag_substitution>
16
+ `${GSD_WS}` / `{GSD_WS}` in emitted "Next:" commands and `Skill()` args and agent dispatch prompts is a placeholder, not a shell variable — nothing expands it automatically. Substitute the active `--ws <name>`, or remove it entirely in flat mode. Never print the literal token in user-facing output. See `pp-core/references/workstream-flag.md` § Substitution rule.
17
+ </workstream_flag_substitution>
18
+
15
19
  <required_reading>
16
20
  Read all files referenced by the invoking prompt's execution_context before starting.
17
21
  </required_reading>
@@ -10,6 +10,10 @@ Use this workflow when:
10
10
  Instantly restore full project context so "Where were we?" has an immediate, complete answer.
11
11
  </purpose>
12
12
 
13
+ <workstream_flag_substitution>
14
+ `${GSD_WS}` / `{GSD_WS}` in emitted "Next:" commands and `Skill()` args and agent dispatch prompts is a placeholder, not a shell variable — nothing expands it automatically. Substitute the active `--ws <name>`, or remove it entirely in flat mode. Never print the literal token in user-facing output. See `pp-core/references/workstream-flag.md` § Substitution rule.
15
+ </workstream_flag_substitution>
16
+
13
17
  <required_reading>
14
18
  @~/.claude/pp-core/references/continuation-format.md
15
19
  </required_reading>
@@ -9,6 +9,10 @@ consumes: UAT.md
9
9
  Create a pull request from completed phase/milestone work, generate a rich PR body from planning artifacts, optionally run code review, and prepare for merge. Closes the plan → execute → verify → ship loop.
10
10
  </purpose>
11
11
 
12
+ <workstream_flag_substitution>
13
+ `${GSD_WS}` / `{GSD_WS}` in emitted "Next:" commands and `Skill()` args and agent dispatch prompts is a placeholder, not a shell variable — nothing expands it automatically. Substitute the active `--ws <name>`, or remove it entirely in flat mode. Never print the literal token in user-facing output. See `pp-core/references/workstream-flag.md` § Substitution rule.
14
+ </workstream_flag_substitution>
15
+
12
16
  <required_reading>
13
17
  Read all files referenced by the invoking prompt's execution_context before starting.
14
18
  </required_reading>
@@ -34,6 +34,10 @@ Mark current phase complete and advance to next. This is the natural point where
34
34
 
35
35
  </purpose>
36
36
 
37
+ <workstream_flag_substitution>
38
+ `${GSD_WS}` / `{GSD_WS}` in emitted "Next:" commands and `Skill()` args and agent dispatch prompts is a placeholder, not a shell variable — nothing expands it automatically. Substitute the active `--ws <name>`, or remove it entirely in flat mode. Never print the literal token in user-facing output. See `pp-core/references/workstream-flag.md` § Substitution rule.
39
+ </workstream_flag_substitution>
40
+
37
41
  <process>
38
42
 
39
43
  <step name="load_project_state" priority="first">
@@ -2,6 +2,10 @@
2
2
  Audit Nyquist validation gaps for a completed phase. Generate missing tests. Update VALIDATION.md.
3
3
  </purpose>
4
4
 
5
+ <workstream_flag_substitution>
6
+ `${GSD_WS}` / `{GSD_WS}` in emitted "Next:" commands and `Skill()` args and agent dispatch prompts is a placeholder, not a shell variable — nothing expands it automatically. Substitute the active `--ws <name>`, or remove it entirely in flat mode. Never print the literal token in user-facing output. See `pp-core/references/workstream-flag.md` § Substitution rule.
7
+ </workstream_flag_substitution>
8
+
5
9
  <required_reading>
6
10
  @~/.claude/pp-core/references/ui-brand.md
7
11
  </required_reading>
@@ -260,6 +260,7 @@
260
260
  "tests/orphaned-hooks.test.cjs :: structural-regression-guard",
261
261
  "tests/package-legitimacy-gate.test.cjs :: source-text-is-the-product",
262
262
  "tests/parallel-dependent-plans.test.cjs :: source-text-is-the-product",
263
+ "tests/pareto-skill-bundle.install.test.cjs :: source-text-is-the-product",
263
264
  "tests/path-replacement.test.cjs :: source-text-is-the-product",
264
265
  "tests/phase-dependency-levels.test.cjs :: source-text-is-the-product",
265
266
  "tests/phase.test.cjs :: source-text-is-the-product",