@pareto4/pp-core 1.13.0 → 1.17.1
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 +3 -3
- package/README.md +26 -9
- package/agents/pp-code-reviewer.md +2 -1
- package/agents/pp-debug-session-manager.md +9 -6
- package/agents/pp-executor.md +10 -13
- package/agents/pp-plan-checker.md +4 -4
- package/agents/pp-planner.md +23 -9
- package/agents/pp-vault-reviewer.md +244 -21
- package/agents/pp-verifier.md +7 -3
- package/bin/install.js +10 -3
- package/commands/pp-execute-phase.md +1 -3
- package/commands/pp-ns-review.md +1 -1
- package/commands/pp-plan-phase.md +1 -2
- package/commands/pp-vault-review.md +5 -3
- package/commands/pp-verify-work.md +1 -2
- package/gemini-extension.json +1 -1
- package/hooks/dist/pp-check-update.js +0 -0
- package/hooks/dist/pp-cursor-session-start.js +1 -1
- package/hooks/dist/pp-statusline.js +1 -1
- package/hooks/dist/pp-update-banner.js +0 -0
- package/hooks/pp-check-update.js +0 -0
- package/hooks/pp-cursor-session-start.js +1 -1
- package/hooks/pp-statusline.js +1 -1
- package/hooks/pp-update-banner.js +0 -0
- package/package.json +14 -19
- package/pareto-skills/_shared/create-project-board.sh +0 -0
- package/pareto-skills/pp-project-setup/SKILL.md +1 -1
- package/pp-core/bin/check-latest-version.cjs +0 -0
- package/pp-core/bin/detect-build-commands.sh +2 -2
- package/pp-core/bin/lib/capability-activation.cjs +83 -9
- package/pp-core/bin/lib/capability-registry.cjs +396 -82
- package/pp-core/bin/lib/capability-state.cjs +160 -4
- package/pp-core/bin/lib/capability-writer.cjs +19 -5
- package/pp-core/bin/lib/check-command-router.cjs +3334 -946
- package/pp-core/bin/lib/clusters.cjs +3 -0
- package/pp-core/bin/lib/command-aliases.cjs +0 -35
- package/pp-core/bin/lib/commands.cjs +9 -4
- package/pp-core/bin/lib/compose-bindings.cjs +251 -88
- package/pp-core/bin/lib/config-loader.cjs +7 -103
- package/pp-core/bin/lib/config-schema.cjs +1 -1
- package/pp-core/bin/lib/config.cjs +154 -62
- package/pp-core/bin/lib/decisions.cjs +4 -4
- package/pp-core/bin/lib/discovery-vault-check.cjs +246 -0
- package/pp-core/bin/lib/frontmatter.cjs +1 -38
- package/pp-core/bin/lib/git-base-branch.cjs +13 -11
- package/pp-core/bin/lib/git-provider.cjs +4 -4
- package/pp-core/bin/lib/init-command-router.cjs +5 -8
- package/pp-core/bin/lib/init-contract.cjs +648 -738
- package/pp-core/bin/lib/init-interactive.cjs +76 -4
- package/pp-core/bin/lib/init.cjs +24 -51
- package/pp-core/bin/lib/install-profiles.cjs +1 -1
- package/pp-core/bin/lib/io.cjs +8 -0
- package/pp-core/bin/lib/loop-resolver.cjs +189 -129
- package/pp-core/bin/lib/org-profile.cjs +469 -152
- package/pp-core/bin/lib/package-identity.cjs +3 -3
- package/pp-core/bin/lib/phase-command-router.cjs +1 -29
- package/pp-core/bin/lib/phase-scope.cjs +1 -1
- package/pp-core/bin/lib/phase.cjs +11 -253
- package/pp-core/bin/lib/platform-bindings.cjs +40 -19
- package/pp-core/bin/lib/practices-review-check.cjs +15 -9
- package/pp-core/bin/lib/profile-output.cjs +1 -1
- package/pp-core/bin/lib/shell-command-projection.cjs +104 -1
- package/pp-core/bin/lib/state.cjs +11 -85
- package/pp-core/bin/lib/template.cjs +1 -1
- package/pp-core/bin/lib/vault-audit.cjs +650 -126
- package/pp-core/bin/lib/vault-command-router.cjs +2604 -367
- package/pp-core/bin/lib/vault-coordinates.cjs +13 -4
- package/pp-core/bin/lib/vault-index.cjs +699 -0
- package/pp-core/bin/lib/vault-mode.cjs +235 -0
- package/pp-core/bin/lib/vault-practices.cjs +139 -72
- package/pp-core/bin/lib/vault-review.cjs +194 -451
- package/pp-core/bin/lib/vault-seal.cjs +1936 -0
- package/pp-core/bin/lib/vault-standards.cjs +1505 -0
- package/pp-core/bin/lib/vault-trace.cjs +607 -254
- package/pp-core/bin/lib/vault-update.cjs +9 -33
- package/pp-core/bin/lib/vault-write-back.cjs +70 -42
- package/pp-core/bin/lib/vault.cjs +2734 -920
- package/pp-core/bin/lib/verify.cjs +0 -11
- package/pp-core/bin/portable-timeout.sh +1 -1
- package/pp-core/bin/pp-tools.cjs +184 -54
- package/pp-core/bin/shared/config-defaults.manifest.json +0 -7
- package/pp-core/bin/shared/config-schema.manifest.json +0 -3
- package/pp-core/bin/shared/model-catalog.json +2 -2
- package/pp-core/bin/verify-reapply-patches.cjs +0 -0
- package/pp-core/org-profile.json +26 -0
- package/pp-core/references/autonomous-best-effort.md +8 -7
- package/pp-core/references/loop-hook-dispatch.md +0 -25
- package/pp-core/references/planner-gap-closure.md +0 -1
- package/pp-core/references/planner-tdd.md +0 -1
- package/pp-core/references/planner-vault-practices.md +17 -34
- package/pp-core/references/planner-vault-standards.md +320 -0
- package/pp-core/references/planning-config.md +0 -8
- package/pp-core/references/practice-arithmetic-contract.md +121 -0
- package/pp-core/references/tdd.md +0 -1
- package/pp-core/references/verifier-practice-verdicts.md +263 -0
- package/pp-core/templates/README.md +1 -0
- package/pp-core/templates/VAULT-REVIEW.md +202 -0
- package/pp-core/templates/phase-prompt.md +2 -6
- package/pp-core/templates/practices/README.md +20 -23
- package/pp-core/workflows/_runtime-launcher.snippet.sh +0 -0
- package/pp-core/workflows/add-tests.md +55 -4
- package/pp-core/workflows/audit-fix.md +63 -2
- package/pp-core/workflows/audit-milestone.md +1 -1
- package/pp-core/workflows/autonomous.md +85 -11
- package/pp-core/workflows/check-todos.md +13 -2
- package/pp-core/workflows/code-review-fix.md +59 -3
- package/pp-core/workflows/code-review.md +10 -1
- package/pp-core/workflows/complete-milestone.md +1 -1
- package/pp-core/workflows/debug.md +54 -4
- package/pp-core/workflows/discovery-sprint/prefill.md +19 -7
- package/pp-core/workflows/discovery-sprint/prototype.md +46 -49
- package/pp-core/workflows/discovery-sprint/session-13-gates.md +2 -0
- package/pp-core/workflows/discovery-sprint/sessions/01-kick-off.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/02-as-is.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/03-to-be.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/04-ux-design.md +20 -24
- package/pp-core/workflows/discovery-sprint/sessions/05-architecture.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/06-security.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/07-identity.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/08-integration.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/09-qa.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/10-infrastructure.md +1 -6
- package/pp-core/workflows/discovery-sprint/sessions/11-roadmap.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/12-delivery-model.md +1 -3
- package/pp-core/workflows/discovery-sprint/sessions/13-wrap-up.md +1 -3
- package/pp-core/workflows/discovery-sprint/templates/context-template.md +1 -1
- package/pp-core/workflows/discovery-sprint/vault-context.md +213 -0
- package/pp-core/workflows/discovery-sprint.md +7 -3
- package/pp-core/workflows/discovery-to-build/steps/architecture-deliverables.md +4 -4
- package/pp-core/workflows/discovery-to-build/templates/pp-handoff.md +43 -1
- package/pp-core/workflows/discovery-to-build.md +10 -2
- package/pp-core/workflows/discuss-phase.md +3 -10
- package/pp-core/workflows/execute-phase/steps/check-query-gate.md +329 -0
- package/pp-core/workflows/execute-phase/steps/client-context-gate.md +3 -2
- package/pp-core/workflows/execute-phase/steps/cross-ai-delegation.md +1 -1
- package/pp-core/workflows/execute-phase/steps/execute-pre-dispatch.md +98 -0
- package/pp-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +94 -0
- package/pp-core/workflows/execute-phase/steps/post-merge-gate.md +6 -8
- package/pp-core/workflows/execute-phase/steps/vault-fetch.md +115 -14
- package/pp-core/workflows/execute-phase/steps/vault-load-gate.md +322 -20
- package/pp-core/workflows/execute-phase/steps/verifier-practice-payload.md +165 -0
- package/pp-core/workflows/execute-phase/steps/verify-post-gate.md +163 -0
- package/pp-core/workflows/execute-phase.md +155 -128
- package/pp-core/workflows/execute-plan.md +56 -6
- package/pp-core/workflows/fast.md +53 -4
- package/pp-core/workflows/help/modes/full.md +3 -3
- package/pp-core/workflows/init/steps/client-context-capture.md +101 -0
- package/pp-core/workflows/init/steps/gitlab-provisioning.md +130 -0
- package/pp-core/workflows/init/steps/project-md-deferral-narration.md +66 -0
- package/pp-core/workflows/init/steps/reachability-outcome.md +70 -0
- package/pp-core/workflows/init.md +232 -272
- package/pp-core/workflows/new-project.md +31 -23
- package/pp-core/workflows/plan-phase.md +98 -45
- package/pp-core/workflows/quick.md +117 -3
- package/pp-core/workflows/secure-phase.md +1 -1
- package/pp-core/workflows/ship.md +57 -28
- package/pp-core/workflows/undo.md +1 -1
- package/pp-core/workflows/update.md +3 -3
- package/pp-core/workflows/validate-phase.md +58 -3
- package/pp-core/workflows/vault-review.md +282 -22
- package/pp-core/workflows/verify-work.md +12 -14
- package/scripts/changeset/cli.cjs +6 -5
- package/scripts/changeset/lint.cjs +0 -0
- package/scripts/changeset/new.cjs +33 -50
- package/scripts/dev-env.cjs +25 -85
- package/scripts/gen-capability-registry.cjs +199 -11
- package/scripts/gen-practice-arithmetic-contract.cjs +412 -0
- package/scripts/lib/allowlist-ratchet.cjs +1 -103
- package/scripts/lint-allow-test-rule-refs.allowlist.json +0 -2
- package/scripts/lint-docs-required.cjs +0 -0
- package/scripts/lint-pr-check-project-dir.cjs +1 -3
- package/scripts/lint-test-file-count.allowlist.json +12 -10
- package/scripts/practice-arithmetic-contract.allow.json +35 -0
- package/scripts/prompt-injection-scan.sh +22 -17
- package/scripts/record-vault-mode-golden.cjs +89 -0
- package/scripts/release-channel.cjs +0 -0
- package/scripts/sync-manifest-versions.cjs +5 -4
- package/scripts/sync-rulesets.sh +1 -1
- package/scripts/update-size-baseline.cjs +30 -274
- package/scripts/vault-calibrate.cjs +996 -0
- package/scripts/verify-opengsd-sweep.cjs +3 -5
- package/scripts/workflow-size.cjs +9 -61
- package/pp-core/bin/lib/dispatch-plan.cjs +0 -483
- package/pp-core/bin/lib/install-run.cjs +0 -536
- package/pp-core/bin/lib/loop-gates.cjs +0 -213
- package/pp-core/bin/lib/phase-manifest.cjs +0 -527
- package/pp-core/bin/lib/phase-tier.cjs +0 -480
- package/pp-core/bin/lib/plan-graph.cjs +0 -368
- package/pp-core/references/executor-vault-trace.md +0 -47
- package/pp-core/references/planner-plan-graph.md +0 -66
- package/pp-core/workflows/discovery-sprint/vault-fetch.md +0 -37
- package/pp-core/workflows/execute-phase/steps/checkpoint-handling.md +0 -45
- package/pp-core/workflows/execute-phase/steps/install-run.md +0 -29
- package/pp-core/workflows/execute-phase/steps/phase-close-rebaseline.md +0 -23
- package/pp-core/workflows/execute-phase/steps/serial-group-dispatch.md +0 -73
- package/pp-core/workflows/execute-phase/steps/vault-door-gate.md +0 -19
- package/pp-core/workflows/plan-phase/steps/phase-tier.md +0 -43
- package/scripts/lib/size-budget.cjs +0 -311
- package/scripts/lint-neutrality-names.cjs +0 -694
- package/scripts/lint-org-coordinates.allowlist.json +0 -35
- package/scripts/lint-org-coordinates.cjs +0 -426
|
@@ -41,16 +41,14 @@ _PP_SHIM_NAME="pp-tools.cjs"; _pp_root() { ( for _v in $(env | sed -n 's/^\(GIT_
|
|
|
41
41
|
GSD_WS=""
|
|
42
42
|
echo "$ARGUMENTS" | grep -qE -- '--ws[[:space:]]+[^[:space:]]+' && GSD_WS=$(echo "$ARGUMENTS" | grep -oE -- '--ws[[:space:]]+[^[:space:]]+')
|
|
43
43
|
PHASE_ARG=$(echo "$ARGUMENTS" | sed -E 's/--ws[[:space:]]+[^[:space:]]+//g' | xargs)
|
|
44
|
-
TIER_FLAG=$(printf '%s\n' "$ARGUMENTS" | grep -oE -- '--tier[[:space:]=]+(light|standard|heavy)' | head -1 | sed -E 's/^--tier[[:space:]=]+/--tier=/')
|
|
45
|
-
PHASE_ARG=$(echo "$PHASE_ARG" | sed -E 's/--tier[[:space:]]+[a-z]+//g' | xargs)
|
|
46
44
|
|
|
47
|
-
INIT=$(pp_run query init.verify-work "${PHASE_ARG}" ${GSD_WS}
|
|
45
|
+
INIT=$(pp_run query init.verify-work "${PHASE_ARG}" ${GSD_WS})
|
|
48
46
|
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
|
49
47
|
AGENT_SKILLS_PLANNER=$(pp_run query agent-skills pp-planner)
|
|
50
48
|
AGENT_SKILLS_CHECKER=$(pp_run query agent-skills pp-plan-checker)
|
|
51
49
|
```
|
|
52
50
|
|
|
53
|
-
Parse JSON for: `planner_model`, `checker_model`, `commit_docs`, `
|
|
51
|
+
Parse JSON for: `planner_model`, `checker_model`, `commit_docs`, `phase_found`, `phase_dir`, `phase_number`, `phase_name`, `has_verification`, `uat_path`.
|
|
54
52
|
|
|
55
53
|
```bash
|
|
56
54
|
# MVP mode detection via the centralized phase.mvp-mode resolver.
|
|
@@ -113,7 +111,7 @@ Continue to `create_uat_file`.
|
|
|
113
111
|
Before UAT, check UI capability activation and whether Playwright/Puppeteer MCP tools are available.
|
|
114
112
|
|
|
115
113
|
```bash
|
|
116
|
-
PLAN_HOOKS_JSON=$(pp_run loop render-hooks plan:pre --raw
|
|
114
|
+
PLAN_HOOKS_JSON=$(pp_run loop render-hooks plan:pre --raw)
|
|
117
115
|
UI_SPEC_FILE=$(ls "${PHASE_DIR}"/*-UI-SPEC.md 2>/dev/null | head -1)
|
|
118
116
|
```
|
|
119
117
|
|
|
@@ -460,12 +458,14 @@ Present summary:
|
|
|
460
458
|
**If issues == 0:**
|
|
461
459
|
|
|
462
460
|
```bash
|
|
463
|
-
VERIFY_POST_HOOKS_JSON=$(pp_run loop render-hooks verify:post --raw
|
|
464
|
-
|
|
461
|
+
VERIFY_POST_HOOKS_JSON=$(pp_run loop render-hooks verify:post --raw)
|
|
462
|
+
printf '%s' "$VERIFY_POST_HOOKS_JSON" | node -e "let h=[];try{h=JSON.parse(require('fs').readFileSync(0,'utf-8')).activeHooks||[]}catch{}console.log('VERIFY_POST_HOOKS_JSON='+JSON.stringify({activeHooks:h}))"
|
|
465
463
|
SECURITY_FILE=$(ls "${PHASE_DIR}"/*-SECURITY.md 2>/dev/null | head -1)
|
|
466
464
|
```
|
|
467
465
|
|
|
468
|
-
|
|
466
|
+
**Vault practice gates.** Read `pp-core/workflows/execute-phase/steps/verify-post-gate.md` — it
|
|
467
|
+
dispatches this point's blocking gates to the generic enforcer, which owns the halt. Rendering the
|
|
468
|
+
payload above is not enforcing it, and the branch below marks the phase complete.
|
|
469
469
|
|
|
470
470
|
Resolve active step hooks from `VERIFY_POST_HOOKS_JSON` where `kind == "step"` and `ref.skill == "secure-phase"`.
|
|
471
471
|
|
|
@@ -624,8 +624,6 @@ On return:
|
|
|
624
624
|
<step name="verify_gap_plans">
|
|
625
625
|
**Verify fix plans with checker:**
|
|
626
626
|
|
|
627
|
-
**Skip if** `plan_checker_enabled` is false (the tier, or the project's explicit `false`) — go to `present_ready`.
|
|
628
|
-
|
|
629
627
|
Run this Bash command to emit the branded Validate stage banner (renders on stderr, auto-degrades on non-TTY — do NOT print an inline ASCII banner, that would double-print):
|
|
630
628
|
```bash
|
|
631
629
|
pp_run banner validate "VERIFYING"
|
|
@@ -677,11 +675,11 @@ On return:
|
|
|
677
675
|
</step>
|
|
678
676
|
|
|
679
677
|
<step name="revision_loop">
|
|
680
|
-
**Iterate planner ↔ checker until plans pass (max
|
|
678
|
+
**Iterate planner ↔ checker until plans pass (max 3):**
|
|
681
679
|
|
|
682
|
-
**If iteration_count <
|
|
680
|
+
**If iteration_count < 3:**
|
|
683
681
|
|
|
684
|
-
Display: `Sending back to planner for revision... (iteration {N}/
|
|
682
|
+
Display: `Sending back to planner for revision... (iteration {N}/3)`
|
|
685
683
|
|
|
686
684
|
Spawn pp-planner with revision context:
|
|
687
685
|
|
|
@@ -720,7 +718,7 @@ Do NOT replan from scratch unless issues are fundamental.
|
|
|
720
718
|
After planner returns → spawn checker again (verify_gap_plans logic)
|
|
721
719
|
Increment iteration_count
|
|
722
720
|
|
|
723
|
-
**If iteration_count >=
|
|
721
|
+
**If iteration_count >= 3:**
|
|
724
722
|
|
|
725
723
|
Display: `Max iterations reached. {N} issues remain.`
|
|
726
724
|
|
|
@@ -90,11 +90,12 @@ function parseArgs(argv) {
|
|
|
90
90
|
return { ok: true, opts };
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
//
|
|
94
|
-
//
|
|
95
|
-
//
|
|
96
|
-
//
|
|
97
|
-
//
|
|
93
|
+
// Release tag gate (#71924 gap plan 01-06). CI's
|
|
94
|
+
// `tag_version_check` job runs `verify` on a bare checkout: no `npm ci`, no
|
|
95
|
+
// `build:lib`, and the compiled semver module under pp-core/bin/lib is
|
|
96
|
+
// gitignored build output. The first shipped version of that step required the
|
|
97
|
+
// compiled module at the top of this file, so it died at require time on every
|
|
98
|
+
// hosted checkout (never observed live: the step was skipped on every probe).
|
|
98
99
|
// `verify` and `render` therefore require node builtins and tracked files only;
|
|
99
100
|
// the compiled dependency is loaded lazily by `extract`, which the update
|
|
100
101
|
// workflow runs on built trees.
|
|
File without changes
|
|
@@ -4,56 +4,42 @@
|
|
|
4
4
|
/**
|
|
5
5
|
* Scaffolds a new changeset fragment (#2975).
|
|
6
6
|
*
|
|
7
|
-
* npm run changeset -- --type Fixed --pr 1234 --body "fix the thing"
|
|
7
|
+
* npm run changeset -- --type Fixed --pr 1234 --body "fix the thing" [--name <slug>]
|
|
8
8
|
*
|
|
9
|
-
* Writes `.changeset/<
|
|
10
|
-
*
|
|
11
|
-
*
|
|
9
|
+
* Writes `.changeset/<pr>-<slug>.md` with frontmatter + body. The slug is
|
|
10
|
+
* --name, else derived from the bold lead sentence; a taken name gets -2, -3, ...
|
|
11
|
+
* FORK-ONLY (PBI 74336): upstream draws a random three-word name.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
const fs = require('node:fs');
|
|
15
15
|
const path = require('node:path');
|
|
16
16
|
const { ExitError, runMain } = require('../lib/cli-exit.cjs');
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
// the random draw repeats.
|
|
22
|
-
const ADJECTIVES = [
|
|
23
|
-
'silly', 'brave', 'calm', 'eager', 'gentle', 'happy', 'jolly', 'kind',
|
|
24
|
-
'lively', 'merry', 'nimble', 'plucky', 'quick', 'sturdy', 'witty', 'zesty',
|
|
25
|
-
'bold', 'clever', 'daring', 'fierce', 'graceful', 'humble', 'lucky', 'noble',
|
|
26
|
-
'proud', 'rapid', 'sharp', 'tidy', 'vivid', 'wise', 'agile', 'curious',
|
|
27
|
-
'eager', 'gallant', 'mellow', 'patient', 'serene', 'steady', 'sturdy', 'sunny',
|
|
28
|
-
];
|
|
29
|
-
const NOUNS_A = [
|
|
30
|
-
'bears', 'birds', 'cats', 'dogs', 'elks', 'foxes', 'goats', 'hawks',
|
|
31
|
-
'ibex', 'jays', 'koalas', 'lynx', 'moles', 'newts', 'otters', 'pumas',
|
|
32
|
-
'quails', 'rams', 'seals', 'tigers', 'voles', 'wolves', 'yaks', 'zebras',
|
|
33
|
-
'badgers', 'cranes', 'deer', 'eagles', 'finches', 'geese', 'herons', 'jaguars',
|
|
34
|
-
'lemurs', 'mice', 'orcas', 'pandas', 'ravens', 'sloths', 'tunas', 'wasps',
|
|
35
|
-
];
|
|
36
|
-
const NOUNS_B = [
|
|
37
|
-
'dance', 'sing', 'leap', 'run', 'jump', 'climb', 'fly', 'swim',
|
|
38
|
-
'rest', 'wake', 'roam', 'greet', 'wander', 'gather', 'forage', 'travel',
|
|
39
|
-
'glide', 'sprint', 'tumble', 'wave', 'cheer', 'rally', 'parade', 'march',
|
|
40
|
-
'hop', 'frolic', 'caper', 'romp', 'zip', 'dart', 'snooze', 'munch',
|
|
41
|
-
'chatter', 'squeak', 'howl', 'bark', 'purr', 'roar', 'hum', 'click',
|
|
42
|
-
];
|
|
18
|
+
const SLUG_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
19
|
+
const SLUG_STOPWORDS = new Set(['a', 'an', 'the', 'and', 'or', 'of', 'to', 'for', 'in', 'on', 'is', 'are', 'now', 'its']);
|
|
20
|
+
const SLUG_MAX_WORDS = 7;
|
|
43
21
|
|
|
44
|
-
|
|
45
|
-
|
|
22
|
+
// Kebab-case slug from the bold lead sentence (or the first sentence), minus filler words.
|
|
23
|
+
function deriveFragmentSlug(body) {
|
|
24
|
+
const bold = /\*\*([^*]+)\*\*/.exec(body || '');
|
|
25
|
+
const lead = bold ? bold[1] : String(body || '').trim().split(/[.!?\n]/)[0];
|
|
26
|
+
const words = lead
|
|
27
|
+
.toLowerCase()
|
|
28
|
+
.replace(/'s\b/g, '')
|
|
29
|
+
.split(/[^a-z0-9]+/)
|
|
30
|
+
.filter((w) => w && !SLUG_STOPWORDS.has(w));
|
|
31
|
+
return words.slice(0, SLUG_MAX_WORDS).join('-') || 'change';
|
|
46
32
|
}
|
|
47
33
|
|
|
48
|
-
function
|
|
49
|
-
return `${
|
|
34
|
+
function fragmentBaseName({ pr, body, name }) {
|
|
35
|
+
return `${pr}-${name || deriveFragmentSlug(body)}`;
|
|
50
36
|
}
|
|
51
37
|
|
|
52
38
|
// Allowed Keep-a-Changelog section types. Used by both scaffoldFragment
|
|
53
39
|
// (sanitization at write time) and parse.cjs (validation at consume time).
|
|
54
40
|
const ALLOWED_TYPES = new Set(['Added', 'Changed', 'Deprecated', 'Removed', 'Fixed', 'Security']);
|
|
55
41
|
|
|
56
|
-
function scaffoldFragment({ repo, type, pr, body }) {
|
|
42
|
+
function scaffoldFragment({ repo, type, pr, body, name = null }) {
|
|
57
43
|
// Sanitize: reject any type value not on the allowlist BEFORE embedding it
|
|
58
44
|
// in frontmatter. A newline in `type` would corrupt the fragment; an
|
|
59
45
|
// unrecognized value would be rejected later by parse.cjs but with a
|
|
@@ -63,28 +49,24 @@ function scaffoldFragment({ repo, type, pr, body }) {
|
|
|
63
49
|
`scaffoldFragment: type=${JSON.stringify(type)} is not one of [${[...ALLOWED_TYPES].join(', ')}]`,
|
|
64
50
|
);
|
|
65
51
|
}
|
|
52
|
+
if (name !== null && !SLUG_RE.test(name)) {
|
|
53
|
+
throw new Error(`scaffoldFragment: name=${JSON.stringify(name)} is not kebab-case (a-z, 0-9, hyphens)`);
|
|
54
|
+
}
|
|
66
55
|
const dir = path.join(repo, '.changeset');
|
|
67
56
|
fs.mkdirSync(dir, { recursive: true });
|
|
68
57
|
const content = `---\ntype: ${type}\npr: ${pr}\n---\n${body}\n`;
|
|
69
|
-
//
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
for (let i = 0; i < 16; i++) {
|
|
74
|
-
const name = generateFragmentName();
|
|
75
|
-
const target = path.join(dir, `${name}.md`);
|
|
58
|
+
// flag 'wx' fails with EEXIST instead of overwriting, so concurrent runs cannot clobber a fragment.
|
|
59
|
+
const base = fragmentBaseName({ pr, body, name });
|
|
60
|
+
for (let i = 1; i <= 50; i++) {
|
|
61
|
+
const target = path.join(dir, `${i === 1 ? base : `${base}-${i}`}.md`);
|
|
76
62
|
try {
|
|
77
63
|
fs.writeFileSync(target, content, { flag: 'wx' });
|
|
78
64
|
return target;
|
|
79
65
|
} catch (e) {
|
|
80
66
|
if (e.code !== 'EEXIST') throw e;
|
|
81
|
-
// collision — try another random draw
|
|
82
67
|
}
|
|
83
68
|
}
|
|
84
|
-
throw new Error(
|
|
85
|
-
'scaffoldFragment: 16 random filename draws all collided; ' +
|
|
86
|
-
'expand the word lists or investigate corrupted .changeset/ state',
|
|
87
|
-
);
|
|
69
|
+
throw new Error(`scaffoldFragment: ${base}.md and 49 numbered variants already exist; pass --name`);
|
|
88
70
|
}
|
|
89
71
|
|
|
90
72
|
function parseArgs(argv) {
|
|
@@ -102,7 +84,7 @@ function parseArgs(argv) {
|
|
|
102
84
|
|
|
103
85
|
for (let i = 0; i < argv.length; i++) {
|
|
104
86
|
const a = argv[i];
|
|
105
|
-
if (a === '--type' || a === '--pr' || a === '--body' || a === '--repo') {
|
|
87
|
+
if (a === '--type' || a === '--pr' || a === '--body' || a === '--name' || a === '--repo') {
|
|
106
88
|
const r = requireValue(a, i);
|
|
107
89
|
if (!r.ok) return { ok: false, error: r.error };
|
|
108
90
|
if (a === '--type') opts.type = r.value;
|
|
@@ -114,6 +96,7 @@ function parseArgs(argv) {
|
|
|
114
96
|
const trimmed = r.value.trim();
|
|
115
97
|
opts.pr = /^\d+$/.test(trimmed) ? Number(trimmed) : NaN;
|
|
116
98
|
} else if (a === '--body') opts.body = r.value;
|
|
99
|
+
else if (a === '--name') opts.name = r.value;
|
|
117
100
|
else if (a === '--repo') opts.repo = r.value;
|
|
118
101
|
i++;
|
|
119
102
|
continue;
|
|
@@ -127,7 +110,7 @@ function main() {
|
|
|
127
110
|
const parsed = parseArgs(process.argv.slice(2));
|
|
128
111
|
if (!parsed.ok) {
|
|
129
112
|
process.stderr.write(`${parsed.error}\n`);
|
|
130
|
-
process.stderr.write('usage: changeset/new.cjs --type <Fixed|Added|...> --pr NNNN --body "..."\n');
|
|
113
|
+
process.stderr.write('usage: changeset/new.cjs --type <Fixed|Added|...> --pr NNNN --body "..." [--name <slug>]\n');
|
|
131
114
|
throw new ExitError(2);
|
|
132
115
|
}
|
|
133
116
|
const { opts } = parsed;
|
|
@@ -140,7 +123,7 @@ function main() {
|
|
|
140
123
|
const prMissing = opts.pr === null;
|
|
141
124
|
const prNaN = typeof opts.pr === 'number' && Number.isNaN(opts.pr);
|
|
142
125
|
if (!opts.type || prMissing || prNaN || !opts.body) {
|
|
143
|
-
throw new ExitError(2, 'usage: changeset/new.cjs --type <Fixed|Added|...> --pr NNNN --body "..."');
|
|
126
|
+
throw new ExitError(2, 'usage: changeset/new.cjs --type <Fixed|Added|...> --pr NNNN --body "..." [--name <slug>]');
|
|
144
127
|
}
|
|
145
128
|
const file = scaffoldFragment(opts);
|
|
146
129
|
process.stdout.write(`${path.relative(process.cwd(), file)}\n`);
|
|
@@ -148,4 +131,4 @@ function main() {
|
|
|
148
131
|
|
|
149
132
|
if (require.main === module) runMain(main);
|
|
150
133
|
|
|
151
|
-
module.exports = {
|
|
134
|
+
module.exports = { deriveFragmentSlug, fragmentBaseName, scaffoldFragment, parseArgs, ALLOWED_TYPES };
|
package/scripts/dev-env.cjs
CHANGED
|
@@ -2,16 +2,16 @@
|
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* dev-env.cjs — keep
|
|
5
|
+
* dev-env.cjs — keep the global pp-core install and a development build apart.
|
|
6
|
+
* FORK-ONLY (PBI 74336)
|
|
6
7
|
*
|
|
7
|
-
*
|
|
8
|
+
* setup [--runtime <id>] build this checkout on its current branch, installed --global
|
|
8
9
|
* dev-setup [--config-dir <dir>] [--no-shell] npm ci, install the dev profile, put claude-dev on PATH
|
|
9
10
|
* install [--config-dir <dir>] [--no-build] build, install into a separate Claude profile
|
|
10
11
|
* install --local [<dir>] [--no-build] build, install into a sandbox project
|
|
11
12
|
* claude [--config-dir <dir>] [-- <args>] start the Claude CLI on the dev profile
|
|
12
13
|
*
|
|
13
|
-
* Defaults: the
|
|
14
|
-
* PP_DEV_CONFIG_DIR, else ~/.claude-dev; the sandbox is ../pp-sandbox.
|
|
14
|
+
* Defaults: the profile is PP_DEV_CONFIG_DIR, else ~/.claude-dev; the sandbox is ../pp-sandbox.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
const fs = require('node:fs');
|
|
@@ -25,27 +25,21 @@ const INSTALLER = path.join(ROOT, 'bin', 'install.js');
|
|
|
25
25
|
|
|
26
26
|
const USAGE = [
|
|
27
27
|
'Usage:',
|
|
28
|
-
' npm run setup [--
|
|
28
|
+
' npm run setup [-- --runtime <id>]',
|
|
29
29
|
' npm run dev:setup [-- [--config-dir <dir>] [--no-shell]]',
|
|
30
30
|
' npm run dev:install [-- [--config-dir <dir>] [--no-build]]',
|
|
31
31
|
' npm run dev:install -- --local [<dir>] [--no-build]',
|
|
32
32
|
' npm run dev:claude [-- <claude args>]',
|
|
33
33
|
].join('\n');
|
|
34
34
|
|
|
35
|
-
function
|
|
36
|
-
const opts = { command: '
|
|
35
|
+
function parseSetupArgs(rest) {
|
|
36
|
+
const opts = { command: 'setup', runtime: 'claude' };
|
|
37
37
|
for (let i = 0; i < rest.length; i++) {
|
|
38
38
|
const arg = rest[i];
|
|
39
|
-
if (arg === '--
|
|
40
|
-
const value = rest[++i];
|
|
41
|
-
if (!value) return { error: '--dir requires a path' };
|
|
42
|
-
opts.dir = value;
|
|
43
|
-
} else if (arg === '--runtime') {
|
|
39
|
+
if (arg === '--runtime') {
|
|
44
40
|
const value = rest[++i];
|
|
45
41
|
if (!value) return { error: '--runtime requires a runtime id' };
|
|
46
42
|
opts.runtime = value;
|
|
47
|
-
} else if (!arg.startsWith('-') && opts.tag === null) {
|
|
48
|
-
opts.tag = arg;
|
|
49
43
|
} else {
|
|
50
44
|
return { error: `unknown option: ${arg}` };
|
|
51
45
|
}
|
|
@@ -72,7 +66,7 @@ function parseDevSetupArgs(rest) {
|
|
|
72
66
|
|
|
73
67
|
function parseArgs(argv) {
|
|
74
68
|
const [command, ...rest] = argv;
|
|
75
|
-
if (command === '
|
|
69
|
+
if (command === 'setup') return parseSetupArgs(rest);
|
|
76
70
|
if (command === 'dev-setup') return parseDevSetupArgs(rest);
|
|
77
71
|
if (command !== 'install' && command !== 'claude') {
|
|
78
72
|
return { error: `unknown command: ${command === undefined ? '(none)' : command}` };
|
|
@@ -133,7 +127,7 @@ function resolveLocalDir(opts, root = ROOT) {
|
|
|
133
127
|
return path.resolve(opts.localDir || path.join(root, '..', 'pp-sandbox'));
|
|
134
128
|
}
|
|
135
129
|
|
|
136
|
-
//
|
|
130
|
+
// ~/.claude holds the global install from npm run setup; the dev routes never write there.
|
|
137
131
|
function assertNotStableProfile(dir, home = os.homedir()) {
|
|
138
132
|
if (samePath(dir, path.join(home, '.claude'))) {
|
|
139
133
|
throw new ExitError(1, `Refusing to install a dev build into the stable profile ${dir}.`);
|
|
@@ -167,23 +161,6 @@ function git(args, cwd) {
|
|
|
167
161
|
return execFileSync('git', args, { cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }).trim();
|
|
168
162
|
}
|
|
169
163
|
|
|
170
|
-
// Highest vX.Y.Z (or X.Y.Z) tag; pre-release tags are skipped.
|
|
171
|
-
function pickLatestTag(tags) {
|
|
172
|
-
const parsed = tags
|
|
173
|
-
.map((tag) => ({ tag, m: /^v?(\d+)\.(\d+)\.(\d+)$/.exec(tag) }))
|
|
174
|
-
.filter((t) => t.m)
|
|
175
|
-
.map((t) => ({ tag: t.tag, v: t.m.slice(1).map(Number) }));
|
|
176
|
-
parsed.sort((a, b) => b.v[0] - a.v[0] || b.v[1] - a.v[1] || b.v[2] - a.v[2]);
|
|
177
|
-
return parsed.length ? parsed[0].tag : null;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
// Next to the main checkout, so running this from any worktree finds the same directory.
|
|
181
|
-
function resolveStableDir(opts, repo = ROOT) {
|
|
182
|
-
if (opts.dir) return path.resolve(opts.dir);
|
|
183
|
-
const commonDir = path.resolve(repo, git(['rev-parse', '--git-common-dir'], repo));
|
|
184
|
-
return path.join(path.dirname(commonDir), '..', 'pp-core-stable');
|
|
185
|
-
}
|
|
186
|
-
|
|
187
164
|
// The runtimes install.js takes as --<id>, read from the capability manifests so no build is needed.
|
|
188
165
|
function listRuntimes(root = ROOT) {
|
|
189
166
|
const capDir = path.join(root, 'capabilities');
|
|
@@ -206,57 +183,22 @@ function resolveRuntime(id, root = ROOT) {
|
|
|
206
183
|
return found;
|
|
207
184
|
}
|
|
208
185
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
.split(/\r?\n/)
|
|
212
|
-
.filter((line) => line.startsWith('worktree '))
|
|
213
|
-
.map((line) => path.resolve(line.slice('worktree '.length)));
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// Adds the worktree at the tag, or moves an existing clean one to it; never touches your own checkout.
|
|
217
|
-
function prepareStableWorktree({ repo, dir, tag }) {
|
|
218
|
-
try {
|
|
219
|
-
git(['rev-parse', '--verify', '--quiet', `refs/tags/${tag}`], repo);
|
|
220
|
-
} catch {
|
|
221
|
-
throw new ExitError(1, `Tag ${tag} does not exist in ${repo}.`);
|
|
222
|
-
}
|
|
223
|
-
let known = listWorktrees(repo).some((w) => samePath(w, dir));
|
|
224
|
-
// A worktree folder deleted by hand stays registered; prune it so it is added again.
|
|
225
|
-
if (known && !fs.existsSync(dir)) {
|
|
226
|
-
git(['worktree', 'prune'], repo);
|
|
227
|
-
known = false;
|
|
228
|
-
}
|
|
229
|
-
if (!known) {
|
|
230
|
-
if (fs.existsSync(dir)) {
|
|
231
|
-
throw new ExitError(1, `${dir} exists but is not a worktree of ${repo}; pass --dir <other dir>.`);
|
|
232
|
-
}
|
|
233
|
-
git(['worktree', 'add', '--detach', dir, tag], repo);
|
|
234
|
-
return;
|
|
235
|
-
}
|
|
236
|
-
if (git(['status', '--porcelain'], dir) !== '') {
|
|
237
|
-
throw new ExitError(1, `The stable worktree ${dir} has local changes; commit or discard them first.`);
|
|
238
|
-
}
|
|
239
|
-
git(['checkout', '--quiet', '--detach', tag], dir);
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
function installStable(opts) {
|
|
186
|
+
// Installs the checkout this runs from, so the global install follows its current branch.
|
|
187
|
+
function installSetup(opts) {
|
|
243
188
|
const runtime = resolveRuntime(opts.runtime);
|
|
244
189
|
npmCliPath();
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
} catch {
|
|
249
|
-
process.stderr.write('Could not fetch tags from origin; using the tags already in this clone.\n');
|
|
250
|
-
}
|
|
251
|
-
const tag = opts.tag || pickLatestTag(git(['tag', '--list'], ROOT).split(/\r?\n/));
|
|
252
|
-
if (!tag) throw new ExitError(1, 'No release tag found; pass one: npm run setup -- <tag>.');
|
|
253
|
-
prepareStableWorktree({ repo: ROOT, dir, tag });
|
|
254
|
-
process.stdout.write(`Stable worktree ${dir} is on ${tag}.\n`);
|
|
255
|
-
runNpm(['ci'], dir);
|
|
256
|
-
runNpm(['run', 'build'], dir);
|
|
257
|
-
execFileSync(process.execPath, [path.join(dir, 'bin', 'install.js'), `--${runtime.id}`, '--global'], { cwd: dir, stdio: 'inherit' });
|
|
190
|
+
runNpm(['ci'], ROOT);
|
|
191
|
+
runBuild();
|
|
192
|
+
execFileSync(process.execPath, [INSTALLER, `--${runtime.id}`, '--global'], { cwd: ROOT, stdio: 'inherit' });
|
|
258
193
|
const again = runtime.id === 'claude' ? 'npm run setup' : `npm run setup -- --runtime ${runtime.id}`;
|
|
259
|
-
process.stdout.write(
|
|
194
|
+
process.stdout.write(`
|
|
195
|
+
pp-core from ${describeCheckout(devBuildInfo())} is installed as the global pp-core for ${runtime.title}. Update later with: ${again}
|
|
196
|
+
`);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function describeCheckout(info) {
|
|
200
|
+
const ref = info.commit ? `${info.branch || 'detached HEAD'}@${info.commit}` : info.checkout;
|
|
201
|
+
return info.dirty ? `${ref} (with uncommitted changes)` : ref;
|
|
260
202
|
}
|
|
261
203
|
|
|
262
204
|
// The statusline shows a DEV badge with this build's branch and commit when this file is present.
|
|
@@ -429,7 +371,7 @@ function main(argv = process.argv.slice(2)) {
|
|
|
429
371
|
if (opts.error) throw new ExitError(2, `${opts.error}\n${USAGE}`);
|
|
430
372
|
if (opts.command === 'claude') return launchClaude(opts);
|
|
431
373
|
if (opts.command === 'dev-setup') devSetup(opts);
|
|
432
|
-
else if (opts.command === '
|
|
374
|
+
else if (opts.command === 'setup') installSetup(opts);
|
|
433
375
|
else if (opts.local) installSandbox(opts);
|
|
434
376
|
else installProfile(opts);
|
|
435
377
|
return 0;
|
|
@@ -441,9 +383,9 @@ module.exports = {
|
|
|
441
383
|
parseArgs,
|
|
442
384
|
resolveConfigDir,
|
|
443
385
|
resolveLocalDir,
|
|
444
|
-
resolveStableDir,
|
|
445
386
|
resolveLaunchDir,
|
|
446
387
|
devBuildInfo,
|
|
388
|
+
describeCheckout,
|
|
447
389
|
renderRcLauncher,
|
|
448
390
|
upsertLauncher,
|
|
449
391
|
windowsLauncherDir,
|
|
@@ -452,10 +394,8 @@ module.exports = {
|
|
|
452
394
|
writeWindowsLaunchers,
|
|
453
395
|
pathHasEntry,
|
|
454
396
|
appendPathEntry,
|
|
455
|
-
pickLatestTag,
|
|
456
397
|
listRuntimes,
|
|
457
398
|
resolveRuntime,
|
|
458
|
-
prepareStableWorktree,
|
|
459
399
|
assertNotStableProfile,
|
|
460
400
|
assertOutsideRepo,
|
|
461
401
|
samePath,
|