@pareto4/pp-core 1.7.1 → 1.8.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/bin/install.js +197 -81
- package/commands/pp-build-handoff.md +8 -2
- package/commands/pp-discovery-to-build.md +40 -0
- package/commands/pp-ns-project.md +3 -1
- package/gemini-extension.json +1 -1
- package/package.json +2 -1
- package/pareto-skills/_shared/create-project-board.sh +195 -0
- package/pareto-skills/_shared/pp-claude-md-protocol.md +306 -0
- package/pareto-skills/_shared/sync-phase-status.sh +139 -0
- package/pareto-skills/pp-brand/SKILL.md +644 -0
- package/pareto-skills/pp-brand/assets/crew/ParetoAgent.inline.html +120 -0
- package/pareto-skills/pp-brand/assets/crew/ParetoAgent.tsx +239 -0
- package/pareto-skills/pp-brand/assets/crew/build.svg +11 -0
- package/pareto-skills/pp-brand/assets/crew/deploy.svg +11 -0
- package/pareto-skills/pp-brand/assets/crew/discovery.svg +11 -0
- package/pareto-skills/pp-brand/assets/crew/plan.svg +11 -0
- package/pareto-skills/pp-brand/assets/crew/run.svg +11 -0
- package/pareto-skills/pp-brand/assets/crew/validate.svg +11 -0
- package/pareto-skills/pp-brand/assets/pareto-brand-preview.html +547 -0
- package/pareto-skills/pp-obsidian-notes/SKILL.md +127 -0
- package/pareto-skills/pp-obsidian-notes/templates/CLAUDE.md +29 -0
- package/pareto-skills/pp-obsidian-notes/templates/client-context.md +116 -0
- package/pareto-skills/pp-obsidian-notes/templates/meeting-note.md +66 -0
- package/pareto-skills/pp-obsidian-notes/templates/pareto-context.md +102 -0
- package/pareto-skills/pp-obsidian-notes/templates/proposal.md +117 -0
- package/pareto-skills/pp-project-setup/SKILL.md +1364 -0
- package/pp-core/bin/lib/clusters.cjs +4 -0
- package/pp-core/bin/lib/intel.cjs +1 -1
- package/pp-core/bin/lib/io.cjs +120 -0
- package/pp-core/bin/lib/profile-pipeline-command-router.cjs +1 -1
- package/pp-core/bin/lib/roadmap-parser.cjs +1 -1
- package/pp-core/bin/lib/runtime-artifact-conversion.cjs +2 -2
- package/pp-core/bin/lib/surface.cjs +1 -1
- package/pp-core/bin/lib/worktree-safety.cjs +2 -2
- package/pp-core/bin/pp-tools.cjs +23 -0
- package/pp-core/references/ui-brand.md +73 -11
- package/pp-core/templates/codebase/structure.md +5 -5
- package/pp-core/templates/spec.md +7 -7
- package/pp-core/workflows/add-backlog.md +1 -1
- package/pp-core/workflows/autonomous.md +4 -4
- package/pp-core/workflows/debug.md +1 -1
- package/pp-core/workflows/{build-handoff.md → discovery-to-build.md} +6 -3
- package/pp-core/workflows/execute-phase.md +9 -1
- package/pp-core/workflows/plan-phase.md +14 -12
- package/pp-core/workflows/pr-branch.md +5 -4
- package/pp-core/workflows/reapply-patches.md +1 -1
- package/pp-core/workflows/ship.md +1 -1
- package/pp-core/workflows/thread.md +1 -1
- package/pp-core/workflows/verify-work.md +6 -5
- package/scripts/lint-allow-test-rule-refs.allowlist.json +1 -0
|
@@ -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',
|
|
@@ -240,7 +240,7 @@ function intelUpdate(planningDir) {
|
|
|
240
240
|
return disabledResponse();
|
|
241
241
|
return {
|
|
242
242
|
action: 'spawn_agent',
|
|
243
|
-
message: 'Run pp-tools intel update or spawn
|
|
243
|
+
message: 'Run pp-tools intel update or spawn pp-intel-updater agent for full refresh',
|
|
244
244
|
};
|
|
245
245
|
}
|
|
246
246
|
/**
|
package/pp-core/bin/lib/io.cjs
CHANGED
|
@@ -210,6 +210,119 @@ function error(message, reason = ERROR_REASON.UNKNOWN) {
|
|
|
210
210
|
}
|
|
211
211
|
process.exit(1);
|
|
212
212
|
}
|
|
213
|
+
// ─── Pareto visual brand: banner engine (Phase 17 / UIBR-01, UIBR-02) ─────────
|
|
214
|
+
//
|
|
215
|
+
// A hand-rolled ANSI banner emitter (no color library — CLAUDE.md constraint).
|
|
216
|
+
// The pure/impure split (formatBanner vs banner) lets the whole degrade ladder
|
|
217
|
+
// be asserted by injecting `level`, with no PTY. Branding is routed to fd 2
|
|
218
|
+
// (stderr) ONLY; output() (fd 1) is left byte-for-byte unchanged, so machine-
|
|
219
|
+
// readable payloads physically cannot carry branding bytes (UIBR-02, D-03).
|
|
220
|
+
/**
|
|
221
|
+
* Pareto Blue — the single signal color for the banner rule (D-01). Every
|
|
222
|
+
* banner rule renders in this hue at every color tier; only the per-phase Crew
|
|
223
|
+
* glyph carries the phase accent. #2251FF.
|
|
224
|
+
*/
|
|
225
|
+
const PARETO_BLUE = [34, 81, 255];
|
|
226
|
+
/**
|
|
227
|
+
* Frozen phase→Crew-identity map (mirrors the ERROR_REASON frozen-object idiom
|
|
228
|
+
* at ~163). Keyed by the six Crew phases; each entry carries the locked glyph
|
|
229
|
+
* (D-01) and its accent at three color tiers. Accent hexes are verbatim from
|
|
230
|
+
* pareto-skills/pp-brand/SKILL.md Crew Accent Palette (D-02).
|
|
231
|
+
*/
|
|
232
|
+
const CREW = Object.freeze({
|
|
233
|
+
discovery: { glyph: '◎', ascii: '(o)', rgb: [246, 180, 55], c256: 221, c16: '33' }, // Amber #F6B437
|
|
234
|
+
plan: { glyph: '◆', ascii: '<>', rgb: [20, 111, 244], c256: 33, c16: '34' }, // Steel #146FF4
|
|
235
|
+
build: { glyph: '⬢', ascii: '#', rgb: [34, 81, 255], c256: 27, c16: '34' }, // Pareto Blue #2251FF
|
|
236
|
+
validate: { glyph: '◈', ascii: '<*>', rgb: [62, 207, 142], c256: 42, c16: '32' }, // Mint #3ECF8E
|
|
237
|
+
deploy: { glyph: '▲', ascii: '^', rgb: [255, 107, 74], c256: 209, c16: '31' }, // Coral #FF6B4A
|
|
238
|
+
run: { glyph: '▶', ascii: '>', rgb: [168, 132, 255], c256: 141, c16: '35' }, // Violet #A884FF
|
|
239
|
+
});
|
|
240
|
+
/**
|
|
241
|
+
* Wrap `text` in the SGR foreground escape for the given color `level`, always
|
|
242
|
+
* reset-terminated. level>=3 → 24-bit truecolor; 2 → 256-color; 1 → 16-color;
|
|
243
|
+
* 0 → plain text, no escapes at all (D-04 ladder bottom).
|
|
244
|
+
*/
|
|
245
|
+
function paint(text, rgb, level, c256, c16) {
|
|
246
|
+
const [r, g, b] = rgb;
|
|
247
|
+
if (level >= 3)
|
|
248
|
+
return `\x1b[38;2;${r};${g};${b}m${text}\x1b[0m`;
|
|
249
|
+
if (level === 2)
|
|
250
|
+
return `\x1b[38;5;${c256}m${text}\x1b[0m`;
|
|
251
|
+
if (level === 1)
|
|
252
|
+
return `\x1b[${c16}m${text}\x1b[0m`;
|
|
253
|
+
return text;
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Detect the terminal color capability of `stream` (default: process.stderr,
|
|
257
|
+
* since banners target fd 2). Returns 0 = none, 1 = 16-color, 2 = 256-color,
|
|
258
|
+
* 3 = truecolor. Gates on COLOR CAPABILITY only — never on the runtime — so the
|
|
259
|
+
* brand renders on any color-capable TTY and degrades cleanly on no-color /
|
|
260
|
+
* non-TTY environments (D-04). Honors NO_COLOR (no-color.org) and FORCE_COLOR.
|
|
261
|
+
*/
|
|
262
|
+
function colorLevel(stream = process.stderr) {
|
|
263
|
+
const env = process.env;
|
|
264
|
+
// 1. NO_COLOR present-and-nonempty disables color (no-color.org).
|
|
265
|
+
if (env.NO_COLOR != null && env.NO_COLOR !== '')
|
|
266
|
+
return 0;
|
|
267
|
+
// 2. FORCE_COLOR override (force a specific tier, e.g. on a pipe in tests).
|
|
268
|
+
const fc = env.FORCE_COLOR;
|
|
269
|
+
if (fc === '0' || fc === 'false')
|
|
270
|
+
return 0;
|
|
271
|
+
if (fc === '1' || fc === 'true')
|
|
272
|
+
return 1;
|
|
273
|
+
if (fc === '2')
|
|
274
|
+
return 2;
|
|
275
|
+
if (fc === '3')
|
|
276
|
+
return 3;
|
|
277
|
+
// 3. Not a TTY → plain (piped, redirected, tool-harness capture).
|
|
278
|
+
if (!stream || !stream.isTTY)
|
|
279
|
+
return 0;
|
|
280
|
+
// 4. Truecolor advertised.
|
|
281
|
+
if (env.COLORTERM === 'truecolor' || env.COLORTERM === '24bit')
|
|
282
|
+
return 3;
|
|
283
|
+
// 5. 256-color terminals.
|
|
284
|
+
const term = env.TERM || '';
|
|
285
|
+
if (/-256(color)?$/.test(term))
|
|
286
|
+
return 2;
|
|
287
|
+
if (term === 'dumb')
|
|
288
|
+
return 0;
|
|
289
|
+
// 6. Known ANSI terminal families → 16-color floor; unknown TTY → safe 16.
|
|
290
|
+
if (/xterm|screen|vt100|vt220|rxvt|color|ansi|cygwin|linux|tmux/i.test(term))
|
|
291
|
+
return 1;
|
|
292
|
+
return 1;
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* PURE: build the three-line brand banner for `phaseKey` at color `level`. No
|
|
296
|
+
* I/O and no env reads — `level` is injected so every ladder tier is unit-
|
|
297
|
+
* testable without a PTY. The rule always renders in Pareto Blue (the D-01
|
|
298
|
+
* signal color); only the glyph carries the phase accent. At level 0 the rule
|
|
299
|
+
* is plain ASCII and the glyph is kept as a plain char — zero escapes (D-04).
|
|
300
|
+
*
|
|
301
|
+
* `stageText` is treated as an opaque literal: it is interpolated verbatim and
|
|
302
|
+
* never parsed, so embedded \x1b sequences are never re-interpreted into new
|
|
303
|
+
* SGR state (V5 / T-17-01 ANSI-injection mitigation).
|
|
304
|
+
*/
|
|
305
|
+
function formatBanner(phaseKey, stageText, level) {
|
|
306
|
+
const RULE = '━'.repeat(51);
|
|
307
|
+
const crew = CREW[phaseKey] ?? CREW.build;
|
|
308
|
+
if (level === 0) {
|
|
309
|
+
return `${RULE}\n ${crew.glyph} pp-core ► ${stageText}\n${RULE}\n`;
|
|
310
|
+
}
|
|
311
|
+
const rule = paint(RULE, PARETO_BLUE, level, 27, '34');
|
|
312
|
+
const glyph = paint(crew.glyph, crew.rgb, level, crew.c256, crew.c16);
|
|
313
|
+
return `${rule}\n ${glyph} pp-core ► ${stageText}\n${rule}\n`;
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* IMPURE: render the banner for `phaseKey`/`stageText` and write it to fd 2
|
|
317
|
+
* (stderr) via the back-pressure-safe writeAllSync. The color level is detected
|
|
318
|
+
* from opts.stream (default process.stderr) unless opts.forceLevel is given.
|
|
319
|
+
* NEVER touches output()/stdout — fd separation is the structural UIBR-02
|
|
320
|
+
* guarantee (D-03, T-17-02).
|
|
321
|
+
*/
|
|
322
|
+
function banner(phaseKey, stageText, opts = {}) {
|
|
323
|
+
const level = opts.forceLevel ?? colorLevel(opts.stream ?? process.stderr);
|
|
324
|
+
writeAllSync(2, formatBanner(phaseKey, stageText, level));
|
|
325
|
+
}
|
|
213
326
|
module.exports = {
|
|
214
327
|
GSD_TEMP_DIR,
|
|
215
328
|
ensureGsdTempDir,
|
|
@@ -219,4 +332,11 @@ module.exports = {
|
|
|
219
332
|
setJsonErrorMode,
|
|
220
333
|
getJsonErrorMode,
|
|
221
334
|
error,
|
|
335
|
+
// Pareto visual brand banner engine (Phase 17 / UIBR-01, UIBR-02)
|
|
336
|
+
PARETO_BLUE,
|
|
337
|
+
CREW,
|
|
338
|
+
paint,
|
|
339
|
+
colorLevel,
|
|
340
|
+
formatBanner,
|
|
341
|
+
banner,
|
|
222
342
|
};
|
|
@@ -44,7 +44,7 @@ function routeExtractMessages({ args, cwd, raw, error, _pipeline }) {
|
|
|
44
44
|
// args[0] = 'extract-messages' (family name), args[1] = project positional
|
|
45
45
|
const projectArg = args[1];
|
|
46
46
|
if (!projectArg || projectArg.startsWith('--')) {
|
|
47
|
-
error('Usage:
|
|
47
|
+
error('Usage: pp-tools extract-messages <project> [--session <id>] [--limit N] [--path <dir>]\nRun scan-sessions first to see available projects.', ERROR_REASON.USAGE);
|
|
48
48
|
return;
|
|
49
49
|
}
|
|
50
50
|
// cmdExtractMessages is async — do NOT return the Promise.
|
|
@@ -344,7 +344,7 @@ function getMilestonePhaseFilter(cwd, versionOverride, phaseIdConvention) {
|
|
|
344
344
|
const hasVersionedMilestonesGlobal = /^#{1,3}\s+.*v\d+\.\d+/mi.test(roadmapContent);
|
|
345
345
|
const hasPhaseHeadings = /#{2,4}\s*(?:\[[^\]]+\]\s*)?Phase\s+[\w]/i.test(roadmapContent);
|
|
346
346
|
if (!hasVersionedMilestonesGlobal && hasPhaseHeadings && phaseIdConvention === 'milestone-prefixed') {
|
|
347
|
-
console.warn('[
|
|
347
|
+
console.warn('[pp-core] Deprecated: free-form ROADMAP.md detected (no versioned milestone headings). ' +
|
|
348
348
|
'The project has phase_id_convention set to "milestone-prefixed" in config.json but the ' +
|
|
349
349
|
'ROADMAP does not use versioned milestone headings. Run `pp-tools roadmap upgrade --convention milestone-prefixed` to migrate (dry-run by default).');
|
|
350
350
|
}
|
|
@@ -427,8 +427,8 @@ function convertClaudeCommandToKimiSkill(content, skillName, _runtime = null, cm
|
|
|
427
427
|
const kimiSkillName = normalizeKimiSkillName(skillName);
|
|
428
428
|
const names = cmdNames || readGsdCommandNames();
|
|
429
429
|
const description = frontmatter
|
|
430
|
-
? extractFrontmatterField(frontmatter, 'description') || `Run
|
|
431
|
-
: `Run
|
|
430
|
+
? extractFrontmatterField(frontmatter, 'description') || `Run pp-core workflow ${kimiSkillName}.`
|
|
431
|
+
: `Run pp-core workflow ${kimiSkillName}.`;
|
|
432
432
|
const normalizedBody = convertGsdCommandReferencesToKimiSkillInvocations(frontmatter ? body : content, names);
|
|
433
433
|
return `---\nname: ${kimiSkillName}\ndescription: ${yamlQuote(toSingleLine(description))}\n---\nInvoke this pp-core skill with \`/skill:${kimiSkillName}\`.\n\n${normalizedBody}`;
|
|
434
434
|
}
|
|
@@ -348,7 +348,7 @@ function pruneSkillDirs(skillsDir, retainedNames, prefix, manifest) {
|
|
|
348
348
|
// The dir must also be in the manifest to be considered GSD-owned.
|
|
349
349
|
// A user-created gsd-* dir that isn't in the manifest is preserved with a warning.
|
|
350
350
|
if (!canonicalStems.has(entry.slice(prefix.length))) {
|
|
351
|
-
process.stderr.write(`[
|
|
351
|
+
process.stderr.write(`[pp-core] Warning: ${entry} matches GSD prefix '${prefix}' but is not in the manifest — preserving (user-owned or unknown)\n`);
|
|
352
352
|
continue;
|
|
353
353
|
}
|
|
354
354
|
isGsdOwned = true;
|
|
@@ -920,13 +920,13 @@ function cmdWorktreeReapOrphans(cwd) {
|
|
|
920
920
|
}
|
|
921
921
|
catch (err) {
|
|
922
922
|
// Surface failure as a one-line warning; keep exit-zero so workflows don't break.
|
|
923
|
-
process.stderr.write(`[
|
|
923
|
+
process.stderr.write(`[pp-core] worktree.reap-orphans failed: ${err && err.message ? err.message : String(err)}\n`);
|
|
924
924
|
result = [];
|
|
925
925
|
}
|
|
926
926
|
const skippedCount = result.filter((r) => r.status === 'skipped').length;
|
|
927
927
|
if (skippedCount > 0) {
|
|
928
928
|
// Surface skipped entries so operators are aware of unresolved orphans.
|
|
929
|
-
process.stderr.write(`[
|
|
929
|
+
process.stderr.write(`[pp-core] worktree.reap-orphans: ${skippedCount} orphan(s) skipped (run with DEBUG=1 for details)\n`);
|
|
930
930
|
}
|
|
931
931
|
process.stdout.write(`${JSON.stringify({ ok: true, reaped: result.filter((r) => r.status === 'reaped').length, entries: result }, null, 2)}\n`);
|
|
932
932
|
}
|
package/pp-core/bin/pp-tools.cjs
CHANGED
|
@@ -709,6 +709,29 @@ async function runCommand(command, args, cwd, raw, defaultValue, originalCommand
|
|
|
709
709
|
break;
|
|
710
710
|
}
|
|
711
711
|
|
|
712
|
+
case 'banner': {
|
|
713
|
+
// Pareto brand banner (Phase 17 / UIBR-02). Workflows shell out via
|
|
714
|
+
// `pp_run banner <phaseKey> "<STAGE TEXT>"`. The banner reaches the
|
|
715
|
+
// terminal ONLY via io.banner → fd 2 (stderr); output()/stdout is never
|
|
716
|
+
// touched, so machine-readable payloads physically cannot carry branding
|
|
717
|
+
// bytes (D-03, T-17-02). The `--raw` flag is already stripped upstream and
|
|
718
|
+
// is irrelevant here — stdout stays clean regardless.
|
|
719
|
+
const phaseKey = args[1];
|
|
720
|
+
const stageText = args[2] ?? '';
|
|
721
|
+
if (!phaseKey || !Object.prototype.hasOwnProperty.call(io.CREW, phaseKey)) {
|
|
722
|
+
// Unknown key → fail loud, never render a partial/garbage banner
|
|
723
|
+
// (T-17-03, ASVS V5). io.error writes to fd 2 and exits non-zero.
|
|
724
|
+
error(
|
|
725
|
+
`banner: unknown phase key "${phaseKey ?? ''}" — expected one of: ${Object.keys(io.CREW).join(', ')}`,
|
|
726
|
+
ERROR_REASON.SDK_MISSING_ARG,
|
|
727
|
+
);
|
|
728
|
+
}
|
|
729
|
+
// stageText is passed as an opaque literal — no re-interpretation of
|
|
730
|
+
// embedded escapes (V5 / T-17-01 ANSI-injection mitigation).
|
|
731
|
+
io.banner(phaseKey, stageText);
|
|
732
|
+
break;
|
|
733
|
+
}
|
|
734
|
+
|
|
712
735
|
case 'generate-client-context': {
|
|
713
736
|
// generate-client-context --from-json <path> [--output <path>] [--force]
|
|
714
737
|
// generate-client-context --read [--output <path>]
|
|
@@ -4,24 +4,86 @@ Visual patterns for user-facing pp-core output. Orchestrators @-reference this f
|
|
|
4
4
|
|
|
5
5
|
## Stage Banners
|
|
6
6
|
|
|
7
|
-
Use for major workflow transitions.
|
|
7
|
+
Use for major workflow transitions. The banner is a three-line block: a rule, a
|
|
8
|
+
title line ` {glyph} pp-core ► {STAGE NAME}`, and a closing rule.
|
|
8
9
|
|
|
9
10
|
```
|
|
10
11
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
11
|
-
pp-core ► {STAGE NAME}
|
|
12
|
+
{glyph} pp-core ► {STAGE NAME}
|
|
12
13
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
13
14
|
```
|
|
14
15
|
|
|
16
|
+
**Color rule (D-01):** the two rules are **ALWAYS Pareto Blue `#2251FF`** — the
|
|
17
|
+
single signal color. Only the **glyph** carries the phase accent (the per-phase
|
|
18
|
+
Crew color below). The rule hue never changes with the phase; this keeps the
|
|
19
|
+
brand's one signal color constant while the glyph communicates which Crew phase
|
|
20
|
+
is active.
|
|
21
|
+
|
|
22
|
+
### Phase → glyph → accent-hex mapping (D-02)
|
|
23
|
+
|
|
24
|
+
Each major transition belongs to one of the six Pareto Crew phases. The glyph and
|
|
25
|
+
its accent color are fixed per phase (all glyphs are BMP for wide font coverage):
|
|
26
|
+
|
|
27
|
+
| Crew phase | phaseKey | Glyph | Codepoint | Accent | Hex | RGB | ASCII fallback | Stage banners |
|
|
28
|
+
|-----------|----------|:-----:|-----------|--------|-----|-----|:--------------:|---------------|
|
|
29
|
+
| Discovery | `discovery` | ◎ | U+25CE BULLSEYE | Amber | `#F6B437` | 246,180,55 | `(o)` | QUESTIONING · RESEARCHING · DEFINING REQUIREMENTS · CREATING ROADMAP |
|
|
30
|
+
| Plan | `plan` | ◆ | U+25C6 BLACK DIAMOND | Steel | `#146FF4` | 20,111,244 | `<>` | PLANNING PHASE {N} · PATTERN MAPPING |
|
|
31
|
+
| Build | `build` | ⬢ | U+2B22 BLACK HEXAGON | Pareto Blue | `#2251FF` | 34,81,255 | `#` | EXECUTING WAVE {N} |
|
|
32
|
+
| Validate | `validate` | ◈ | U+25C8 WHITE DIAMOND CONTAINING BLACK SMALL DIAMOND | Mint | `#3ECF8E` | 62,207,142 | `<*>` | VERIFYING · PHASE {N} COMPLETE ✓ |
|
|
33
|
+
| Deploy | `deploy` | ▲ | U+25B2 BLACK UP-POINTING TRIANGLE | Coral | `#FF6B4A` | 255,107,74 | `^` | ship · PR BRANCH · MILESTONE COMPLETE 🎉 |
|
|
34
|
+
| Run | `run` | ▶ | U+25B6 BLACK RIGHT-POINTING TRIANGLE | Violet | `#A884FF` | 168,132,255 | `>` | autonomous · progress · resume |
|
|
35
|
+
|
|
36
|
+
Build's accent equals Pareto Blue by design, so at the Build transition the glyph
|
|
37
|
+
and rule share a hue.
|
|
38
|
+
|
|
15
39
|
**Stage names (uppercase):**
|
|
16
|
-
- `QUESTIONING`
|
|
17
|
-
- `RESEARCHING`
|
|
18
|
-
- `DEFINING REQUIREMENTS`
|
|
19
|
-
- `CREATING ROADMAP`
|
|
20
|
-
- `PLANNING PHASE {N}`
|
|
21
|
-
- `
|
|
22
|
-
- `
|
|
23
|
-
- `
|
|
24
|
-
- `
|
|
40
|
+
- `QUESTIONING` (Discovery ◎)
|
|
41
|
+
- `RESEARCHING` (Discovery ◎)
|
|
42
|
+
- `DEFINING REQUIREMENTS` (Discovery ◎)
|
|
43
|
+
- `CREATING ROADMAP` (Discovery ◎)
|
|
44
|
+
- `PLANNING PHASE {N}` (Plan ◆)
|
|
45
|
+
- `PATTERN MAPPING PHASE {N}` (Plan ◆)
|
|
46
|
+
- `EXECUTING WAVE {N}` (Build ⬢)
|
|
47
|
+
- `VERIFYING` (Validate ◈)
|
|
48
|
+
- `PHASE {N} COMPLETE ✓` (Validate ◈)
|
|
49
|
+
- `PR BRANCH` (Deploy ▲)
|
|
50
|
+
- `MILESTONE COMPLETE 🎉` (Deploy ▲)
|
|
51
|
+
|
|
52
|
+
### How banners are emitted (the shell-out call)
|
|
53
|
+
|
|
54
|
+
Banners are **not** printed as LLM prose. Workflows emit them via the CLI, which
|
|
55
|
+
writes the rendered banner to **stderr (fd 2)** and auto-degrades on non-TTY:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
pp_run banner <phaseKey> "<STAGE TEXT>"
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
For example, at the research transition a workflow runs
|
|
62
|
+
`pp_run banner discovery "RESEARCHING PHASE {X}"`; at wave start it runs
|
|
63
|
+
`pp_run banner build "EXECUTING WAVE {N}"`. The `<phaseKey>` is the Crew key from
|
|
64
|
+
the table above; the stage text is passed as an opaque literal.
|
|
65
|
+
|
|
66
|
+
**Degrade ladder (D-04):** the emitter gates on **color capability, not runtime**.
|
|
67
|
+
It detects the terminal level from fd 2 and steps down cleanly:
|
|
68
|
+
|
|
69
|
+
- **truecolor (level 3):** 24-bit `\x1b[38;2;R;G;Bm` — full accent hex.
|
|
70
|
+
- **256-color (level 2):** nearest xterm-256 index for rule and glyph.
|
|
71
|
+
- **16-color (level 1):** basic ANSI SGR for rule and glyph.
|
|
72
|
+
- **none (level 0):** plain ASCII — no escapes at all; the glyph is kept as a
|
|
73
|
+
plain character (or its ASCII fallback where a terminal can't render it).
|
|
74
|
+
|
|
75
|
+
Because level 0 is exactly the non-TTY / piped / no-color case, branding
|
|
76
|
+
auto-degrades in precisely the environments where ANSI wouldn't render anyway,
|
|
77
|
+
and `NO_COLOR` / `FORCE_COLOR` are honored.
|
|
78
|
+
|
|
79
|
+
**Double-print rule (Pitfall 4):** the inline ASCII block shown above is the
|
|
80
|
+
**documented visual spec only**. A workflow that adopts the `pp_run banner` call
|
|
81
|
+
MUST drop its own inline `━━━ / pp-core ► {STAGE} / ━━━` block at that transition —
|
|
82
|
+
otherwise the banner prints twice. Keep the inline block here in `ui-brand.md`;
|
|
83
|
+
remove it from the adopting workflow.
|
|
84
|
+
|
|
85
|
+
The `✓` / `🎉` status symbols below remain additive to the stage text (e.g.
|
|
86
|
+
`PHASE {N} COMPLETE ✓`).
|
|
25
87
|
|
|
26
88
|
---
|
|
27
89
|
|
|
@@ -145,10 +145,10 @@ pp-core/
|
|
|
145
145
|
- Key files: install.js - handles npx installation
|
|
146
146
|
- Subdirectories: None
|
|
147
147
|
|
|
148
|
-
**commands
|
|
149
|
-
- Purpose: Slash command definitions for Claude Code
|
|
150
|
-
- Contains:
|
|
151
|
-
- Key files: new-project.md, plan-phase.md, execute-
|
|
148
|
+
**commands/**
|
|
149
|
+
- Purpose: Slash command definitions for Claude Code (pp-* flat-hyphen layout)
|
|
150
|
+
- Contains: pp-*.md files (one per command)
|
|
151
|
+
- Key files: pp-new-project.md, pp-plan-phase.md, pp-execute-phase.md
|
|
152
152
|
- Subdirectories: None (flat structure)
|
|
153
153
|
|
|
154
154
|
**pp-core/references/**
|
|
@@ -206,7 +206,7 @@ pp-core/
|
|
|
206
206
|
## Where to Add New Code
|
|
207
207
|
|
|
208
208
|
**New Slash Command:**
|
|
209
|
-
- Primary code: `commands/
|
|
209
|
+
- Primary code: `commands/pp-{command-name}.md`
|
|
210
210
|
- Tests: `tests/commands/{command-name}.test.js` (if testing implemented)
|
|
211
211
|
- Documentation: Update `README.md` with new command
|
|
212
212
|
|
|
@@ -232,7 +232,7 @@ The database has a `posts` table and `follows` table. No feed query or feed UI e
|
|
|
232
232
|
|
|
233
233
|
## Goal
|
|
234
234
|
|
|
235
|
-
A `
|
|
235
|
+
A `myapp backup` CLI command creates a reproducible database snapshot that can be restored by `myapp restore` (a separate phase).
|
|
236
236
|
|
|
237
237
|
## Background
|
|
238
238
|
|
|
@@ -242,8 +242,8 @@ No backup tooling exists. The project uses PostgreSQL. Developers currently use
|
|
|
242
242
|
|
|
243
243
|
1. **Backup creation**: CLI command executes a full database backup.
|
|
244
244
|
- Current: No `backup` subcommand exists in the CLI
|
|
245
|
-
- Target: `
|
|
246
|
-
- Acceptance: Running `
|
|
245
|
+
- Target: `myapp backup` connects to the database (via `DATABASE_URL` env or `--db` flag), runs pg_dump, writes output to `./backups/YYYY-MM-DD_HH-MM-SS.dump`
|
|
246
|
+
- Acceptance: Running `myapp backup` on a test database creates a `.dump` file; running `pg_restore` on that file recreates the database without error
|
|
247
247
|
|
|
248
248
|
2. **Network retry**: Transient network failures are retried automatically.
|
|
249
249
|
- Current: pg_dump fails immediately on network error
|
|
@@ -258,13 +258,13 @@ No backup tooling exists. The project uses PostgreSQL. Developers currently use
|
|
|
258
258
|
## Boundaries
|
|
259
259
|
|
|
260
260
|
**In scope:**
|
|
261
|
-
- `
|
|
261
|
+
- `myapp backup` subcommand (full dump only)
|
|
262
262
|
- Output to `./backups/` directory (created if missing)
|
|
263
263
|
- Network retry (3 attempts)
|
|
264
264
|
- Partial file cleanup on failure
|
|
265
265
|
|
|
266
266
|
**Out of scope:**
|
|
267
|
-
- `
|
|
267
|
+
- `myapp restore` — that is Phase 3
|
|
268
268
|
- Incremental backups — separate backlog item (full dump only for now)
|
|
269
269
|
- S3 or remote storage — separate backlog item
|
|
270
270
|
- Encryption — separate backlog item
|
|
@@ -277,8 +277,8 @@ No backup tooling exists. The project uses PostgreSQL. Developers currently use
|
|
|
277
277
|
|
|
278
278
|
## Acceptance Criteria
|
|
279
279
|
|
|
280
|
-
- [ ] `
|
|
281
|
-
- [ ] `
|
|
280
|
+
- [ ] `myapp backup` creates a `.dump` file in `./backups/YYYY-MM-DD_HH-MM-SS.dump` format
|
|
281
|
+
- [ ] `myapp backup` uses `DATABASE_URL` env var or `--db` flag for connection
|
|
282
282
|
- [ ] 3 retries on network failure, then exit code 1 with stderr message
|
|
283
283
|
- [ ] `--no-retry` flag skips retries and fails immediately on first error
|
|
284
284
|
- [ ] No partial `.dump` file left after a failed backup
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Add Backlog Item Workflow
|
|
2
2
|
|
|
3
|
-
Invoked by `/pp:capture --backlog` (`commands/
|
|
3
|
+
Invoked by `/pp:capture --backlog` (`commands/pp-capture.md`).
|
|
4
4
|
|
|
5
5
|
Adds an idea to the ROADMAP.md backlog parking lot using 999.x numbering. Backlog items
|
|
6
6
|
are unsequenced ideas that aren't ready for active planning — they live outside the normal
|
|
@@ -342,10 +342,10 @@ Read `frontend` and `hasUiSpec` from `GATE` (in-context).
|
|
|
342
342
|
**Otherwise (frontend phase + no UI-SPEC):** For each active step hook (the `kind == "step"` set from above, in array order):
|
|
343
343
|
|
|
344
344
|
```
|
|
345
|
-
Skill(skill="
|
|
345
|
+
Skill(skill="pp-${ref.skill}", args="${PHASE_NUM}")
|
|
346
346
|
```
|
|
347
347
|
|
|
348
|
-
(Prepend `
|
|
348
|
+
(Prepend `pp-` to `ref.skill` — so `ui-phase` → `pp-ui-phase`. Bare `${PHASE_NUM}` args — autonomous style, same pattern as the verify:post dispatch.) Entries where `kind == "gate"` are silently ignored — autonomous is always pipeline mode, there is no blocking gate here.
|
|
349
349
|
|
|
350
350
|
After all step hooks return, re-read:
|
|
351
351
|
|
|
@@ -465,7 +465,7 @@ If no active code-review step hook exists: display "Code review skipped (code-re
|
|
|
465
465
|
For each active code-review step hook, dispatch the skill using the registry-provided stem:
|
|
466
466
|
|
|
467
467
|
```
|
|
468
|
-
Skill(skill="
|
|
468
|
+
Skill(skill="pp-${ref.skill}", args="${PHASE_NUM}")
|
|
469
469
|
```
|
|
470
470
|
|
|
471
471
|
Parse status from REVIEW.md frontmatter. If "clean" or "skipped": proceed to 3d. If findings found after the capability-dispatched review, auto-invoke the consolidated fix entry point:
|
|
@@ -590,7 +590,7 @@ For each entry in `activeHooks` in array order where `kind == "step"` and `ref.s
|
|
|
590
590
|
- Invoke:
|
|
591
591
|
|
|
592
592
|
```
|
|
593
|
-
Skill(skill="
|
|
593
|
+
Skill(skill="pp-${ref.skill}", args="${PHASE_NUM}")
|
|
594
594
|
```
|
|
595
595
|
|
|
596
596
|
(i.e. prepend `pp-` to `ref.skill` — so `ui-review` → `pp-ui-review`.)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Debug Workflow
|
|
2
2
|
|
|
3
|
-
Invoked by `/pp:debug` (`commands/
|
|
3
|
+
Invoked by `/pp:debug` (`commands/pp-debug.md`).
|
|
4
4
|
|
|
5
5
|
Systematic debugging using the scientific method with subagent isolation.
|
|
6
6
|
Orchestrates symptom gathering, session creation, and delegation to `pp-debug-session-manager`.
|
|
@@ -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
|
---
|
|
@@ -95,7 +97,7 @@ AskUserQuestion([
|
|
|
95
97
|
**If "Complete discovery first":** Exit with guidance:
|
|
96
98
|
```
|
|
97
99
|
Run /pp-discovery to see progress and continue with the next session.
|
|
98
|
-
When all sessions are complete, run /pp-build
|
|
100
|
+
When all sessions are complete, run /pp-discovery-to-build again.
|
|
99
101
|
```
|
|
100
102
|
|
|
101
103
|
**If "Proceed anyway":** Continue to Step 3, noting which sessions are incomplete.
|
|
@@ -326,7 +328,7 @@ AskUserQuestion([
|
|
|
326
328
|
```
|
|
327
329
|
|
|
328
330
|
**If "Review first":** Wait for the user to review, then remind them to say "continue" or
|
|
329
|
-
re-run `/pp-build
|
|
331
|
+
re-run `/pp-discovery-to-build` when ready.
|
|
330
332
|
|
|
331
333
|
**If "Regenerate":** Ask what's wrong, adjust, and regenerate Step 3.
|
|
332
334
|
|
|
@@ -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
|
|
@@ -539,6 +539,14 @@ increases monotonically across waves. `{status}` is `complete` (success),
|
|
|
539
539
|
[checkpoint] phase {PHASE_NUMBER} wave {N}/{M} starting, {wave_plan_count} plan(s), {P}/{Q} plans done
|
|
540
540
|
```
|
|
541
541
|
|
|
542
|
+
**Emit the branded Build stage banner (the marquee wave-start transition).**
|
|
543
|
+
Run this Bash command — it renders on stderr and auto-degrades on non-TTY; do
|
|
544
|
+
NOT print an inline ASCII banner alongside it:
|
|
545
|
+
|
|
546
|
+
```bash
|
|
547
|
+
pp_run banner build "EXECUTING WAVE {N}"
|
|
548
|
+
```
|
|
549
|
+
|
|
542
550
|
Then read each plan's `<objective>`. Extract what's being built and why.
|
|
543
551
|
|
|
544
552
|
```
|
|
@@ -1184,7 +1192,7 @@ If no active code-review step hook exists: display "Code review skipped (code-re
|
|
|
1184
1192
|
|
|
1185
1193
|
**Invoke review:**
|
|
1186
1194
|
```
|
|
1187
|
-
Skill(skill="
|
|
1195
|
+
Skill(skill="pp-${ref.skill}", args="${PHASE_NUMBER}")
|
|
1188
1196
|
```
|
|
1189
1197
|
|
|
1190
1198
|
**Check results using deterministic path (not glob):**
|
|
@@ -488,12 +488,13 @@ If user selects "Skip research": skip to step 6.
|
|
|
488
488
|
|
|
489
489
|
**If `--auto` and `research_enabled` is false:** Skip research silently (preserves automated behavior).
|
|
490
490
|
|
|
491
|
-
|
|
491
|
+
Run this Bash command to emit the branded Discovery stage banner (renders on stderr, auto-degrades on non-TTY — do NOT print an inline ASCII banner, that would double-print):
|
|
492
|
+
```bash
|
|
493
|
+
pp_run banner discovery "RESEARCHING PHASE {X}"
|
|
492
494
|
```
|
|
493
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
494
|
-
pp-core ► RESEARCHING PHASE {X}
|
|
495
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
496
495
|
|
|
496
|
+
Then announce the spawn:
|
|
497
|
+
```
|
|
497
498
|
◆ Spawning researcher... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)
|
|
498
499
|
```
|
|
499
500
|
|
|
@@ -501,7 +502,7 @@ Display banner:
|
|
|
501
502
|
|
|
502
503
|
```bash
|
|
503
504
|
if pp_run query teams-status --active >/dev/null 2>&1; then
|
|
504
|
-
echo "⚠️ CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS detected. pp-core's multi-agent orchestration is not validated under claude-code agent-teams and may stall (a subagent's completion can fail to route to the orchestrator). Recommend disabling agent-teams for
|
|
505
|
+
echo "⚠️ CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS detected. pp-core's multi-agent orchestration is not validated under claude-code agent-teams and may stall (a subagent's completion can fail to route to the orchestrator). Recommend disabling agent-teams for pp-core workflows. See https://github.com/open-gsd/pp-core/issues/1355" >&2
|
|
505
506
|
fi
|
|
506
507
|
```
|
|
507
508
|
|
|
@@ -634,12 +635,12 @@ Read the `activeHooks` array directly from `PLAN_PRE_HOOKS_JSON` / `HOOKS_JSON`
|
|
|
634
635
|
**Branch 1 — all plan:pre hooks inactive (`activeHooks` is empty or absent):** Skip to step 6.
|
|
635
636
|
|
|
636
637
|
**Generic step hook dispatch contract:** For each active entry where `kind == "step"`:
|
|
637
|
-
- If `ref.skill` is set, dispatch with `Skill(skill="
|
|
638
|
+
- If `ref.skill` is set, dispatch with `Skill(skill="pp-${ref.skill}", args="${PHASE} --auto ${GSD_WS}")` when pipeline mode allows auto-chaining. Prepend `pp-` to `ref.skill` — `ui-phase` → `pp-ui-phase`.
|
|
638
639
|
- If `ref.agent` is set, dispatch with `Agent(prompt=filled_hook_fragment, subagent_type=ref.agent, model="{researcher_model}")`. Use the hook's `fragment.inline` as the prompt body and fill phase fields before spawning.
|
|
639
640
|
- The `research` hook is handled by §5.1's research decision. The `pattern-mapper` hook is handled by §7.8 after `RESEARCH_PATH` is known. Future plan:pre agent hooks use the same `ref.agent` fragment contract.
|
|
640
641
|
|
|
641
642
|
**AI integration capability:** If the active `ai-integration` step hook is present, `AI_SPEC_PATH` is empty, and the phase goal contains AI keywords (`agent`, `llm`, `rag`, `chatbot`, `embedding`, `langchain`, `llamaindex`, `crewai`, `langgraph`, `openai`, `anthropic`, `vector`, `eval`, `ai system`), then:
|
|
642
|
-
- In pipeline / `--auto` mode, invoke the hook's `ref.skill` via `Skill(skill="
|
|
643
|
+
- In pipeline / `--auto` mode, invoke the hook's `ref.skill` via `Skill(skill="pp-${ref.skill}", args="${PHASE} --auto ${GSD_WS}")`.
|
|
643
644
|
- In manual mode, display the existing non-blocking `/pp:ai-integration-phase {N}` recommendation and let the user continue planning without AI-SPEC or stop to run the capability workflow first.
|
|
644
645
|
|
|
645
646
|
Run the UI deterministic gate whenever **any** `plan:pre` UI hook is active — including the step-only case (`workflow.ui_safety_gate` off). (`check.query` = `"ui.plan-gate"`; router normalizes dots→hyphens.)
|
|
@@ -674,7 +675,7 @@ AUTO_CHAIN=$(pp_run query check auto-mode --pick auto_chain_active 2>/dev/null |
|
|
|
674
675
|
**Branch 5 — `AUTO_CHAIN` is `true` (pipeline / `--auto`):** Fire each active UI **step** hook — runs independently of whether a gate is active (covers `{ui_phase:true,ui_safety_gate:false}`). For each entry in `activeHooks` (in array order) where `kind == "step"` and `ref.skill` is set:
|
|
675
676
|
|
|
676
677
|
```
|
|
677
|
-
Skill(skill="
|
|
678
|
+
Skill(skill="pp-${ref.skill}", args="${PHASE} --auto ${GSD_WS}")
|
|
678
679
|
```
|
|
679
680
|
|
|
680
681
|
After all UI step hooks return, re-read:
|
|
@@ -827,12 +828,13 @@ Continue to step 8.
|
|
|
827
828
|
|
|
828
829
|
## 8. Spawn pp-planner Agent
|
|
829
830
|
|
|
830
|
-
|
|
831
|
+
Run this Bash command to emit the branded Plan stage banner (renders on stderr, auto-degrades on non-TTY — do NOT print an inline ASCII banner, that would double-print):
|
|
832
|
+
```bash
|
|
833
|
+
pp_run banner plan "PLANNING PHASE {X}"
|
|
831
834
|
```
|
|
832
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
833
|
-
pp-core ► PLANNING PHASE {X}
|
|
834
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
835
835
|
|
|
836
|
+
Then announce the spawn:
|
|
837
|
+
```
|
|
836
838
|
◆ Spawning planner... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)
|
|
837
839
|
```
|
|
838
840
|
|
|
@@ -31,12 +31,13 @@ if [ "$AHEAD" = "0" ]; then
|
|
|
31
31
|
fi
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
Run this Bash command to emit the branded Deploy stage banner (renders on stderr, auto-degrades on non-TTY — do NOT print an inline ASCII banner, that would double-print):
|
|
35
|
+
```bash
|
|
36
|
+
pp_run banner deploy "PR BRANCH"
|
|
35
37
|
```
|
|
36
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
37
|
-
pp-core ► PR BRANCH
|
|
38
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
39
38
|
|
|
39
|
+
Then display the branch summary:
|
|
40
|
+
```
|
|
40
41
|
Branch: {CURRENT_BRANCH}
|
|
41
42
|
Target: {TARGET}
|
|
42
43
|
Commits: {AHEAD} ahead
|