@pareto4/pp-core 1.21.0 → 1.21.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 +1 -1
- package/gemini-extension.json +1 -1
- package/package.json +1 -1
- package/pp-core/bin/lib/phase.cjs +11 -5
- package/pp-core/bin/lib/state.cjs +1 -0
- package/pp-core/workflows/code-review.md +6 -3
- package/pp-core/workflows/execute-phase.md +1 -1
- package/pp-core/workflows/init/steps/client-context-capture.md +1 -1
- package/pp-core/workflows/profile-user.md +2 -2
- package/pp-core/workflows/quick.md +1 -1
- package/pp-core/workflows/ship.md +1 -1
- package/pp-core/workflows/spec-phase.md +2 -2
package/gemini-extension.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pp-core",
|
|
3
|
-
"version": "1.21.
|
|
3
|
+
"version": "1.21.1",
|
|
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.21.
|
|
3
|
+
"version": "1.21.1",
|
|
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",
|
|
@@ -58,7 +58,7 @@ const uatPredicate = require("./uat-predicate.cjs");
|
|
|
58
58
|
const { evaluateUatPassed } = uatPredicate;
|
|
59
59
|
const { planningDir, withPlanningLock, milestonePathsFor } = planningWorkspace;
|
|
60
60
|
const { extractFrontmatter } = frontmatterMod;
|
|
61
|
-
const { readModifyWriteStateMd, stateExtractField, stateReplaceField, stateReplaceFieldWithFallback, syncStateFrontmatter, withStateLock, updatePerformanceMetricsSection, } = stateMod;
|
|
61
|
+
const { readModifyWriteStateMd, stateExtractField, stateReplaceField, stateReplaceFieldWithFallback, syncStateFrontmatter, stripFrontmatter, withStateLock, updatePerformanceMetricsSection, } = stateMod;
|
|
62
62
|
// #2893 — strict canonical filter: `{padded_phase}-{NN}-PLAN.md` or `PLAN.md`.
|
|
63
63
|
const isCanonicalPlanFile = (f) => f.endsWith('-PLAN.md') || f === 'PLAN.md';
|
|
64
64
|
// Any .md file with PLAN anywhere in the basename — diagnostic net
|
|
@@ -1355,7 +1355,13 @@ function cmdPhaseComplete(cwd, phaseNum, raw) {
|
|
|
1355
1355
|
}
|
|
1356
1356
|
if (node_fs_1.default.existsSync(statePath)) {
|
|
1357
1357
|
const originalStateContent = (0, shell_command_projection_cjs_1.platformReadSync)(statePath) || '';
|
|
1358
|
-
|
|
1358
|
+
// #1257 class: the body-field patterns are case-insensitive and line-anchored, so
|
|
1359
|
+
// run over the whole file they hit the YAML keys first (`status:`, `progress:`)
|
|
1360
|
+
// and the body Status line never moved. Edit the body only; syncStateFrontmatter
|
|
1361
|
+
// below re-derives the frontmatter from it.
|
|
1362
|
+
const strippedBody = stripFrontmatter(originalStateContent);
|
|
1363
|
+
let frontmatterPrefix = originalStateContent.slice(0, originalStateContent.length - strippedBody.length);
|
|
1364
|
+
let stateContent = strippedBody;
|
|
1359
1365
|
const phaseValue = nextPhaseNum || phaseNum;
|
|
1360
1366
|
const nextPhaseDisplayName = phaseDisplayNameFromRoadmap(roadmapContent, nextPhaseNum) ??
|
|
1361
1367
|
phaseDisplayNameFromSlug(nextPhaseName);
|
|
@@ -1420,7 +1426,7 @@ function cmdPhaseComplete(cwd, phaseNum, raw) {
|
|
|
1420
1426
|
const newPercent = (0, phase_lifecycle_cjs_1.clampPercent)(newCompleted, totalPhases);
|
|
1421
1427
|
stateContent =
|
|
1422
1428
|
stateReplaceField(stateContent, 'Progress', `${newPercent}%`) || stateContent;
|
|
1423
|
-
|
|
1429
|
+
frontmatterPrefix = frontmatterPrefix.replace(/(percent:\s*)\d+/, `$1${newPercent}`);
|
|
1424
1430
|
}
|
|
1425
1431
|
}
|
|
1426
1432
|
stateContent = updatePerformanceMetricsSection(stateContent, cwd, phaseNum, planCount, summaryCount);
|
|
@@ -1430,14 +1436,14 @@ function cmdPhaseComplete(cwd, phaseNum, raw) {
|
|
|
1430
1436
|
// one still on disk.
|
|
1431
1437
|
// D-10: this transaction sets Last activity itself; that explicit date flows.
|
|
1432
1438
|
const activityFieldSet = (0, state_document_cjs_1.extractPositionalField)(stateContent, 'Last Activity', 'Last activity') !==
|
|
1433
|
-
(0, state_document_cjs_1.extractPositionalField)(
|
|
1439
|
+
(0, state_document_cjs_1.extractPositionalField)(strippedBody, 'Last Activity', 'Last activity');
|
|
1434
1440
|
// 261007-rly D-11: phase complete opts OUT of the stopped_at hold and keeps
|
|
1435
1441
|
// re-deriving the key from the Session line, as it did before the hold moved
|
|
1436
1442
|
// into syncStateFrontmatter: bug #3517's pin (tests/phase.test.cjs) needs a
|
|
1437
1443
|
// newer Session line to replace a stale stored value here, the opposite of
|
|
1438
1444
|
// the operator-note shape the hold protects. Filed, not decided here:
|
|
1439
1445
|
// todos/pending/phase-complete-rederives-stopped-at-from-the-session-line.md.
|
|
1440
|
-
stateContent = syncStateFrontmatter(stateContent, cwd, roadmapContent, activityFieldSet, true);
|
|
1446
|
+
stateContent = syncStateFrontmatter(frontmatterPrefix + stateContent, cwd, roadmapContent, activityFieldSet, true);
|
|
1441
1447
|
stateFrontmatterUpdated = (0, state_document_cjs_1.diffFrontmatterFields)(extractFrontmatter(originalStateContent), extractFrontmatter(stateContent));
|
|
1442
1448
|
writes.push({ filePath: statePath, before: originalStateContent, after: stateContent });
|
|
1443
1449
|
}
|
|
@@ -53,8 +53,9 @@ Parse optional flags from $ARGUMENTS using the typed flag parser:
|
|
|
53
53
|
# This is the canonical flag-parsing surface — do not replicate inline bash parsing
|
|
54
54
|
# for --fix/--all/--auto here; the module handles all flag extraction and implication
|
|
55
55
|
# logic (e.g., --all and --auto imply --fix).
|
|
56
|
+
PP_LIB=$(printf '%s\n' "${PP_TOOLS:+${PP_TOOLS%/*}:}$PATH" | tr : '\n' | while IFS= read -r d; do if [ -r "$d/lib/code-review-flags.cjs" ]; then printf %s "$d/lib"; break; fi; done); export PP_LIB="${PP_LIB:-$PWD/pp-core/bin/lib}"
|
|
56
57
|
FLAGS_JSON=$(node -e "
|
|
57
|
-
const { parseCodeReviewFlags } = require('
|
|
58
|
+
const { parseCodeReviewFlags } = require(require('path').resolve(process.env.PP_LIB, 'code-review-flags.cjs'));
|
|
58
59
|
const flags = parseCodeReviewFlags(process.argv.slice(1));
|
|
59
60
|
process.stdout.write(JSON.stringify(flags));
|
|
60
61
|
" -- "$@" 2>/dev/null)
|
|
@@ -373,8 +374,9 @@ When `FALLOW_ENABLED=true`:
|
|
|
373
374
|
|
|
374
375
|
1) Resolve binary via PATH first, then `node_modules/.bin/fallow`.
|
|
375
376
|
```bash
|
|
377
|
+
PP_LIB=$(printf '%s\n' "${PP_TOOLS:+${PP_TOOLS%/*}:}$PATH" | tr : '\n' | while IFS= read -r d; do if [ -r "$d/lib/fallow-runner.cjs" ]; then printf %s "$d/lib"; break; fi; done); export PP_LIB="${PP_LIB:-$PWD/pp-core/bin/lib}"
|
|
376
378
|
FALLOW_BIN=$(FALLOW_CWD="$(pwd)" node -e "
|
|
377
|
-
const { resolveFallowBinary } = require('
|
|
379
|
+
const { resolveFallowBinary } = require(require('path').resolve(process.env.PP_LIB, 'fallow-runner.cjs'));
|
|
378
380
|
const resolved = resolveFallowBinary({ cwd: process.env.FALLOW_CWD });
|
|
379
381
|
if (resolved) process.stdout.write(resolved);
|
|
380
382
|
")
|
|
@@ -497,9 +499,10 @@ if [ -n "$FALLOW_JSON_PATH" ] && [ -f "$FALLOW_JSON_PATH" ]; then
|
|
|
497
499
|
# Normalize fallow's raw report into the compact {summary, findings[]} contract
|
|
498
500
|
# the reviewer consumes (real fallow schema -> normalized findings).
|
|
499
501
|
FALLOW_NORMALIZED_PATH="${PHASE_DIR}/FALLOW-normalized.json"
|
|
502
|
+
PP_LIB=$(printf '%s\n' "${PP_TOOLS:+${PP_TOOLS%/*}:}$PATH" | tr : '\n' | while IFS= read -r d; do if [ -r "$d/lib/fallow-runner.cjs" ]; then printf %s "$d/lib"; break; fi; done); export PP_LIB="${PP_LIB:-$PWD/pp-core/bin/lib}"
|
|
500
503
|
FALLOW_SRC="$FALLOW_JSON_PATH" FALLOW_OUT="$FALLOW_NORMALIZED_PATH" node -e "
|
|
501
504
|
const fs = require('fs');
|
|
502
|
-
const { normalizeFallowReportFile } = require('
|
|
505
|
+
const { normalizeFallowReportFile } = require(require('path').resolve(process.env.PP_LIB, 'fallow-runner.cjs'));
|
|
503
506
|
const n = normalizeFallowReportFile(process.env.FALLOW_SRC);
|
|
504
507
|
fs.writeFileSync(process.env.FALLOW_OUT, JSON.stringify(n, null, 2));
|
|
505
508
|
" 2>/dev/null && FALLOW_EMBED_PATH="$FALLOW_NORMALIZED_PATH" || FALLOW_EMBED_PATH="$FALLOW_JSON_PATH"
|
|
@@ -549,7 +549,7 @@ increases monotonically across waves. `{status}` is `complete` (success),
|
|
|
549
549
|
DISPATCH_TS=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
550
550
|
EXPECTED_BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
|
551
551
|
if [ "${USE_WORKTREES_FOR_PLAN:-true}" != "false" ] && [ -z "${WAVE_WORKTREE_MANIFEST:-}" ]; then
|
|
552
|
-
WAVE_WORKTREE_MANIFEST=$(mktemp "${TMPDIR:-/tmp}/pp-worktree-wave-XXXXXX
|
|
552
|
+
WAVE_WORKTREE_MANIFEST=$(mktemp "${TMPDIR:-/tmp}/pp-worktree-wave-XXXXXX")
|
|
553
553
|
# Persist the dispatch-time orchestrator worktree root so wave-cleanup can pin back to the
|
|
554
554
|
# orchestrator's OWN worktree — NOT `git worktree list`'s first entry (always the main
|
|
555
555
|
# checkout), which pins a non-primary (per-phase lane) orchestrator off its branch (#630).
|
|
@@ -67,7 +67,7 @@ if [ "$(pp_run query platform-bindings --pick present 2>/dev/null)" = "true" ];
|
|
|
67
67
|
# multi-line values, embedded quotes, backslashes, and $(...) sequences are encoded
|
|
68
68
|
# correctly (CR-02: an unquoted heredoc would produce invalid JSON for any multi-line
|
|
69
69
|
# field, breaking cmdGenerateClientContext with "Failed to parse JSON input").
|
|
70
|
-
TMP_CTX=$(mktemp /tmp/pp-client-ctx-XXXXXX
|
|
70
|
+
TMP_CTX=$(mktemp /tmp/pp-client-ctx-XXXXXX)
|
|
71
71
|
CLIENT_NAME="$CLIENT_NAME" \
|
|
72
72
|
CLIENT_SLUG="$CLIENT_SLUG" \
|
|
73
73
|
CODING_STANDARDS="$CODING_STANDARDS" \
|
|
@@ -218,7 +218,7 @@ Collect all answers into an answers JSON object mapping dimension keys to select
|
|
|
218
218
|
|
|
219
219
|
**Save answers to temp file:**
|
|
220
220
|
```bash
|
|
221
|
-
ANSWERS_PATH=$(mktemp /tmp/pp-profile-answers-XXXXXX
|
|
221
|
+
ANSWERS_PATH=$(mktemp /tmp/pp-profile-answers-XXXXXX)
|
|
222
222
|
```
|
|
223
223
|
|
|
224
224
|
Write the answers JSON to `$ANSWERS_PATH`.
|
|
@@ -232,7 +232,7 @@ Parse the analysis JSON from the result.
|
|
|
232
232
|
|
|
233
233
|
Save analysis JSON to a temp file:
|
|
234
234
|
```bash
|
|
235
|
-
ANALYSIS_PATH=$(mktemp /tmp/pp-profile-analysis-XXXXXX
|
|
235
|
+
ANALYSIS_PATH=$(mktemp /tmp/pp-profile-analysis-XXXXXX)
|
|
236
236
|
```
|
|
237
237
|
|
|
238
238
|
Write the analysis JSON to `$ANALYSIS_PATH`.
|
|
@@ -758,7 +758,7 @@ Capture current HEAD before spawning (used for worktree branch check, and as the
|
|
|
758
758
|
EXPECTED_BASE=$(git rev-parse HEAD)
|
|
759
759
|
pp_run vault anchor start "${QUICK_DIR}" --raw
|
|
760
760
|
if [ "${USE_WORKTREES:-true}" != "false" ]; then
|
|
761
|
-
QUICK_WORKTREE_MANIFEST=$(mktemp "${TMPDIR:-/tmp}/pp-quick-worktree-XXXXXX
|
|
761
|
+
QUICK_WORKTREE_MANIFEST=$(mktemp "${TMPDIR:-/tmp}/pp-quick-worktree-XXXXXX")
|
|
762
762
|
printf '{"worktrees":[]}\n' > "$QUICK_WORKTREE_MANIFEST"
|
|
763
763
|
export QUICK_WORKTREE_MANIFEST
|
|
764
764
|
fi
|
|
@@ -344,7 +344,7 @@ Use the exact key order `skill=`, `fallback=`, `exempt=`, `missing=` so downstre
|
|
|
344
344
|
Create the PR using the generated body. Write the body to a temp file first so large generated PRD sections do not hit shell argument limits:
|
|
345
345
|
|
|
346
346
|
```bash
|
|
347
|
-
PR_BODY_FILE=$(mktemp "${TMPDIR:-/tmp}/pp-pr-body.XXXXXX
|
|
347
|
+
PR_BODY_FILE=$(mktemp "${TMPDIR:-/tmp}/pp-pr-body.XXXXXX")
|
|
348
348
|
trap 'rm -f "${PR_BODY_FILE:-}"' EXIT
|
|
349
349
|
printf '%s\n' "${PR_BODY}" > "${PR_BODY_FILE}"
|
|
350
350
|
|
|
@@ -236,7 +236,7 @@ fi
|
|
|
236
236
|
# canonical coverage compute. Populate the heredoc from the SPEC's Requirements — one object
|
|
237
237
|
# per requirement: {"id","text","shapes"?}. This is the load-bearing step: an empty file makes
|
|
238
238
|
# the probe a no-op, so the guard below fails loud rather than silently skipping (RR-04).
|
|
239
|
-
REQS_JSON=$(mktemp "${TMPDIR:-/tmp}/edge-probe-reqs-XXXXXX
|
|
239
|
+
REQS_JSON=$(mktemp "${TMPDIR:-/tmp}/edge-probe-reqs-XXXXXX")
|
|
240
240
|
cat > "$REQS_JSON" <<'JSON'
|
|
241
241
|
[
|
|
242
242
|
{ "id": "R1", "text": "<replace: requirement text from the SPEC>" }
|
|
@@ -245,7 +245,7 @@ JSON
|
|
|
245
245
|
# Guard — never invoke on an empty/invalid array, OR one still holding the heredoc
|
|
246
246
|
# `<replace: …>` placeholder (a forgotten substitution would otherwise yield a
|
|
247
247
|
# meaningful-looking but bogus coverage report). Fail loud, not silent no-op.
|
|
248
|
-
if ! node -e 'const a=require(process.argv[1]);if(!Array.isArray(a)||a.length===0)process.exit(1);if(a.some(r=>typeof r.text!=="string"||!r.text.trim()||r.text.includes("<replace:")))process.exit(1)' "$REQS_JSON" 2>/dev/null; then
|
|
248
|
+
if ! node -e 'const a=JSON.parse(require("fs").readFileSync(process.argv[1],"utf8"));if(!Array.isArray(a)||a.length===0)process.exit(1);if(a.some(r=>typeof r.text!=="string"||!r.text.trim()||r.text.includes("<replace:")))process.exit(1)' "$REQS_JSON" 2>/dev/null; then
|
|
249
249
|
echo "ERROR: edge-probe requirements JSON is empty/invalid or still holds the <replace: …> placeholder — populate \$REQS_JSON from the SPEC Requirements before Step 5.5 runs." >&2
|
|
250
250
|
exit 1
|
|
251
251
|
fi
|