@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.
- package/.claude-plugin/plugin.json +1 -1
- package/NOTICE +23 -0
- package/README.md +7 -1
- package/bin/install.js +106 -81
- package/commands/pp-build-handoff.md +1 -1
- package/commands/pp-discovery-to-build.md +1 -1
- package/commands/pp-ns-project.md +3 -1
- package/gemini-extension.json +1 -1
- package/package.json +3 -1
- package/pp-core/bin/lib/clusters.cjs +4 -0
- package/pp-core/bin/lib/init.cjs +4 -4
- package/pp-core/bin/lib/installer-migration-report.cjs +1 -1
- package/pp-core/bin/lib/runtime-name-policy.cjs +0 -1
- package/pp-core/bin/lib/surface.cjs +2 -2
- package/pp-core/references/workstream-flag.md +13 -0
- package/pp-core/workflows/discovery-to-build.md +4 -1
- package/pp-core/workflows/discuss-phase/modes/chain.md +5 -0
- package/pp-core/workflows/discuss-phase.md +4 -0
- package/pp-core/workflows/execute-phase.md +4 -0
- package/pp-core/workflows/new-milestone.md +4 -0
- package/pp-core/workflows/plan-phase.md +4 -0
- package/pp-core/workflows/progress.md +4 -0
- package/pp-core/workflows/quick.md +4 -0
- package/pp-core/workflows/resume-project.md +4 -0
- package/pp-core/workflows/ship.md +4 -0
- package/pp-core/workflows/transition.md +4 -0
- package/pp-core/workflows/validate-phase.md +4 -0
- package/scripts/lint-allow-test-rule-refs.allowlist.json +1 -0
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
|
-
//
|
|
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
|
-
`
|
|
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 \`
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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/
|
|
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 —
|
|
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** —
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
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
|
|
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
|
-
*
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
8156
|
+
console.log(` ${green}✓${reset} Cleaned pp-core section from copilot-instructions.md`);
|
|
8157
8157
|
}
|
|
8158
8158
|
}
|
|
8159
8159
|
|
|
8160
|
-
// #786: remove 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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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}
|
|
8395
|
+
console.log(` ${green}✓${reset} Removed ${agentCount} pp-core agents`);
|
|
8396
8396
|
}
|
|
8397
8397
|
}
|
|
8398
8398
|
|
|
8399
|
-
// 4. Remove
|
|
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}
|
|
8412
|
+
console.log(` ${green}✓${reset} Removed ${hookCount} pp-core hooks`);
|
|
8413
8413
|
}
|
|
8414
8414
|
|
|
8415
|
-
// Remove only the
|
|
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
|
-
//
|
|
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
|
|
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/
|
|
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/
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
8524
|
+
console.log(` ${green}✓${reset} Removed pp-core statusline from settings`);
|
|
8525
8525
|
}
|
|
8526
8526
|
|
|
8527
|
-
// Remove
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
//
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 |
|
package/gemini-extension.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pp-core",
|
|
3
|
-
"version": "1.8.
|
|
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.
|
|
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',
|
package/pp-core/bin/lib/init.cjs
CHANGED
|
@@ -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
|
|
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",
|