@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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pp-core",
3
3
  "displayName": "PP Core",
4
- "version": "1.21.0",
4
+ "version": "1.21.1",
5
5
  "description": "PP Core is a meta-prompting, context engineering, and spec-driven development system for AI coding agents.",
6
6
  "author": {
7
7
  "name": "pareto4",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pp-core",
3
- "version": "1.21.0",
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.0",
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
- let stateContent = originalStateContent;
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
- stateContent = stateContent.replace(/(percent:\s*)\d+/, `$1${newPercent}`);
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)(originalStateContent, 'Last Activity', 'Last activity');
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
  }
@@ -3011,6 +3011,7 @@ module.exports = {
3011
3011
  writeStateMd,
3012
3012
  readModifyWriteStateMd,
3013
3013
  syncStateFrontmatter,
3014
+ stripFrontmatter,
3014
3015
  matchSessionSection,
3015
3016
  withStateLock,
3016
3017
  updatePerformanceMetricsSection,
@@ -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('./pp-core/bin/lib/code-review-flags.cjs');
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('./pp-core/bin/lib/fallow-runner.cjs');
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('./pp-core/bin/lib/fallow-runner.cjs');
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.json")
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.json)
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.json)
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.json)
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.json")
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.md")
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.json")
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