@pareto4/pp-core 1.22.0 → 1.23.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 +205 -28
- package/commands/pp-help.md +2 -1
- package/gemini-extension.json +1 -1
- package/hooks/dist/pp-check-update-worker.js +1 -1
- package/hooks/dist/pp-context-monitor.js +3 -3
- package/hooks/dist/pp-statusline.js +88 -14
- package/hooks/dist/pp-update-banner.js +44 -4
- package/hooks/dist/pp-workflow-guard.js +3 -3
- package/hooks/pp-check-update-worker.js +1 -1
- package/hooks/pp-context-monitor.js +3 -3
- package/hooks/pp-statusline.js +88 -14
- package/hooks/pp-update-banner.js +44 -4
- package/hooks/pp-workflow-guard.js +3 -3
- package/package.json +1 -1
- package/pp-core/bin/lib/audience-command.cjs +271 -0
- package/pp-core/bin/lib/audience.cjs +293 -0
- package/pp-core/bin/lib/commands.cjs +26 -17
- package/pp-core/bin/lib/discovery-charter.cjs +13 -1
- package/pp-core/bin/lib/discovery-input-check.cjs +11 -11
- package/pp-core/bin/lib/discovery-session-check.cjs +7 -7
- package/pp-core/bin/lib/discovery-status.cjs +419 -0
- package/pp-core/bin/lib/explain-command.cjs +48 -0
- package/pp-core/bin/lib/gate-card.cjs +97 -0
- package/pp-core/bin/lib/hook-messages.cjs +132 -0
- package/pp-core/bin/lib/init-contract.cjs +116 -76
- package/pp-core/bin/lib/init-interactive.cjs +110 -49
- package/pp-core/bin/lib/init.cjs +44 -3
- package/pp-core/bin/lib/io.cjs +33 -0
- package/pp-core/bin/lib/message-labels.cjs +75 -0
- package/pp-core/bin/lib/messages/core.cjs +20 -0
- package/pp-core/bin/lib/messages/discovery.cjs +267 -0
- package/pp-core/bin/lib/messages/gate.cjs +257 -0
- package/pp-core/bin/lib/messages/hook.cjs +114 -0
- package/pp-core/bin/lib/messages/init.cjs +513 -0
- package/pp-core/bin/lib/messages/progress.cjs +110 -0
- package/pp-core/bin/lib/messages.cjs +208 -0
- package/pp-core/bin/lib/vault-command-router.cjs +16 -3
- package/pp-core/bin/lib/violation-render.cjs +78 -0
- package/pp-core/bin/pp-tools.cjs +68 -23
- package/pp-core/bin/shared/config-schema.manifest.json +1 -0
- package/pp-core/references/audience-moments.md +119 -0
- package/pp-core/references/audience.md +114 -0
- package/pp-core/references/discovery-charter.md +2 -2
- package/pp-core/templates/project-charter.md +3 -4
- package/pp-core/workflows/add-phase.md +2 -0
- package/pp-core/workflows/add-tests.md +2 -0
- package/pp-core/workflows/add-todo.md +2 -0
- package/pp-core/workflows/ai-integration-phase.md +2 -0
- package/pp-core/workflows/audit-milestone.md +2 -0
- package/pp-core/workflows/autonomous.md +2 -0
- package/pp-core/workflows/check-todos.md +2 -0
- package/pp-core/workflows/code-review-fix.md +2 -0
- package/pp-core/workflows/code-review.md +2 -0
- package/pp-core/workflows/complete-milestone.md +10 -1
- package/pp-core/workflows/discovery-sprint/charter.md +3 -2
- package/pp-core/workflows/discovery-sprint/prefill.md +3 -0
- package/pp-core/workflows/discovery-sprint/prototype.md +14 -0
- package/pp-core/workflows/discovery-sprint/sessions/01-kick-off.md +4 -1
- package/pp-core/workflows/discovery-sprint/sessions/13-wrap-up.md +1 -1
- package/pp-core/workflows/discovery-sprint/vault-context.md +15 -0
- package/pp-core/workflows/discovery-sprint.md +14 -0
- package/pp-core/workflows/discovery-to-build.md +8 -2
- package/pp-core/workflows/discuss-phase-assumptions.md +2 -0
- package/pp-core/workflows/discuss-phase.md +1 -1
- package/pp-core/workflows/edit-phase.md +2 -0
- package/pp-core/workflows/eval-review.md +2 -0
- package/pp-core/workflows/execute-phase/steps/check-query-gate.md +25 -1
- package/pp-core/workflows/execute-phase/steps/vault-fetch.md +1 -0
- package/pp-core/workflows/execute-phase/steps/vault-load-gate.md +2 -0
- package/pp-core/workflows/execute-phase.md +1 -1
- package/pp-core/workflows/execute-plan.md +2 -0
- package/pp-core/workflows/extract-learnings.md +2 -0
- package/pp-core/workflows/help/modes/business.md +56 -0
- package/pp-core/workflows/help.md +12 -2
- package/pp-core/workflows/import.md +10 -1
- package/pp-core/workflows/ingest-docs.md +2 -0
- package/pp-core/workflows/init.md +17 -1
- package/pp-core/workflows/insert-phase.md +2 -0
- package/pp-core/workflows/list-workspaces.md +2 -0
- package/pp-core/workflows/manager.md +2 -0
- package/pp-core/workflows/map-codebase.md +2 -0
- package/pp-core/workflows/milestone-summary.md +2 -0
- package/pp-core/workflows/new-milestone.md +10 -1
- package/pp-core/workflows/new-project.md +12 -2
- package/pp-core/workflows/new-workspace.md +2 -0
- package/pp-core/workflows/next.md +8 -0
- package/pp-core/workflows/plan-milestone-gaps.md +10 -1
- package/pp-core/workflows/plan-phase.md +2 -1
- package/pp-core/workflows/plan-review-convergence.md +1 -1
- package/pp-core/workflows/progress.md +10 -0
- package/pp-core/workflows/quick.md +11 -1
- package/pp-core/workflows/remove-phase.md +2 -0
- package/pp-core/workflows/remove-workspace.md +2 -0
- package/pp-core/workflows/resume-project.md +25 -0
- package/pp-core/workflows/review.md +2 -0
- package/pp-core/workflows/scan.md +2 -0
- package/pp-core/workflows/secure-phase.md +2 -0
- package/pp-core/workflows/settings.md +41 -0
- package/pp-core/workflows/ship.md +2 -0
- package/pp-core/workflows/spec-phase.md +1 -1
- package/pp-core/workflows/ui-phase.md +2 -0
- package/pp-core/workflows/ui-review.md +2 -0
- package/pp-core/workflows/ultraplan-phase.md +2 -0
- package/pp-core/workflows/validate-phase.md +2 -0
- package/pp-core/workflows/vault-review.md +2 -0
- package/pp-core/workflows/verify-phase.md +2 -0
- package/pp-core/workflows/verify-work.md +2 -0
- package/scripts/affected-tests-lib.cjs +26 -12
- package/scripts/ci-release-only.cjs +158 -0
- package/scripts/live-runs/driver.cjs +326 -0
- package/scripts/live-runs/run.cjs +906 -0
- package/scripts/live-runs/scenarios.cjs +326 -0
- package/scripts/live-runs/scratch-project.cjs +132 -0
- package/scripts/live-runs/scrub.cjs +291 -0
- package/scripts/live-runs/turn-guard.cjs +107 -0
- package/scripts/live-runs/walk-setup.cjs +198 -0
- package/scripts/run-tests.cjs +151 -135
- package/scripts/test-env.cjs +64 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* `pp-tools gate-card <query>`: the stopped-check card in the person's wording
|
|
4
|
+
* (Phase 43: D-11, TPL-02).
|
|
5
|
+
*
|
|
6
|
+
* ADR-457 build-at-publish: this TypeScript file is the SOURCE OF TRUTH. It
|
|
7
|
+
* compiles to the gitignored `pp-core/bin/lib/gate-card.cjs`; do NOT hand-edit
|
|
8
|
+
* the compiled output.
|
|
9
|
+
*
|
|
10
|
+
* Like `audience fields`, it prints nothing for a developer, so a workflow fence
|
|
11
|
+
* that calls it prints no extra line for a developer (D-03). Every word comes
|
|
12
|
+
* from the `gate` catalog slice through `msg()` / `text()` (D-10); this verb never
|
|
13
|
+
* composes wording. Codes are looked up only through the catalog Map (`has`), and
|
|
14
|
+
* the query only through `GATE_QUERIES.includes`, so `__proto__`, `constructor`
|
|
15
|
+
* and an unknown name print nothing (T-43-10). The query argument is never echoed
|
|
16
|
+
* on any path, including usage errors (T-43-11). The no-state output is one
|
|
17
|
+
* `JSON.stringify` line, so card text cannot forge a line of its own (T-43-12).
|
|
18
|
+
*/
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.USAGE = void 0;
|
|
21
|
+
exports.cardsFor = cardsFor;
|
|
22
|
+
exports.cmdGateCard = cmdGateCard;
|
|
23
|
+
const messages_cjs_1 = require("./messages.cjs");
|
|
24
|
+
const gate_cjs_1 = require("./messages/gate.cjs");
|
|
25
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
26
|
+
const io = require("./io.cjs");
|
|
27
|
+
const { output, ERROR_REASON } = io;
|
|
28
|
+
/** Explicitly typed so TypeScript narrows after a failing guard (the destructured `io.error` does not). */
|
|
29
|
+
function error(message, reason) {
|
|
30
|
+
return io.error(message, reason);
|
|
31
|
+
}
|
|
32
|
+
exports.USAGE = "Usage: pp-tools gate-card <query> [--state blocked|advisory|error] [--details] [--raw] — the stopped-check card in the person's wording; prints nothing for a developer";
|
|
33
|
+
/**
|
|
34
|
+
* The cards of one query, keyed by state; null for a developer or an unknown query.
|
|
35
|
+
* Only the states the query defines appear.
|
|
36
|
+
*/
|
|
37
|
+
function cardsFor(query, audience) {
|
|
38
|
+
if (audience === 'developer')
|
|
39
|
+
return null;
|
|
40
|
+
if (!gate_cjs_1.GATE_QUERIES.includes(query))
|
|
41
|
+
return null;
|
|
42
|
+
const cards = {};
|
|
43
|
+
for (const state of gate_cjs_1.GATE_STATES) {
|
|
44
|
+
const code = `gate.${query}.${state}`;
|
|
45
|
+
if ((0, messages_cjs_1.has)(code))
|
|
46
|
+
cards[state] = { code, text: (0, messages_cjs_1.text)(code, {}, { audience }) };
|
|
47
|
+
}
|
|
48
|
+
return cards;
|
|
49
|
+
}
|
|
50
|
+
function cmdGateCard(_cwd, args, raw) {
|
|
51
|
+
let query = null;
|
|
52
|
+
let state = null;
|
|
53
|
+
let details = false;
|
|
54
|
+
for (let i = 0; i < args.length; i++) {
|
|
55
|
+
const a = args[i];
|
|
56
|
+
if (a === '--raw')
|
|
57
|
+
continue;
|
|
58
|
+
if (a === '--details') {
|
|
59
|
+
details = true;
|
|
60
|
+
}
|
|
61
|
+
else if (a === '--state') {
|
|
62
|
+
const next = args[i + 1];
|
|
63
|
+
if (next === undefined || !gate_cjs_1.GATE_STATES.includes(next))
|
|
64
|
+
error(exports.USAGE, ERROR_REASON.USAGE);
|
|
65
|
+
state = next;
|
|
66
|
+
i++;
|
|
67
|
+
}
|
|
68
|
+
else if (a.startsWith('-')) {
|
|
69
|
+
error(exports.USAGE, ERROR_REASON.USAGE);
|
|
70
|
+
}
|
|
71
|
+
else if (query === null) {
|
|
72
|
+
query = a;
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
error(exports.USAGE, ERROR_REASON.USAGE);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (query === null)
|
|
79
|
+
error(exports.USAGE, ERROR_REASON.USAGE);
|
|
80
|
+
const audience = (0, messages_cjs_1.audienceOf)(process.env);
|
|
81
|
+
const cards = cardsFor(query, audience);
|
|
82
|
+
if (cards === null)
|
|
83
|
+
return;
|
|
84
|
+
if (state === null) {
|
|
85
|
+
output(cards, true, JSON.stringify(cards) + '\n');
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const card = Object.prototype.hasOwnProperty.call(cards, state) ? cards[state] : undefined;
|
|
89
|
+
if (card === undefined)
|
|
90
|
+
return;
|
|
91
|
+
const body = (0, messages_cjs_1.msg)(card.code, {}, { audience });
|
|
92
|
+
if (raw) {
|
|
93
|
+
output({ code: card.code, text: body }, false);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
output({ code: card.code, text: body }, true, body + (details ? '\nDetails:' : '') + '\n');
|
|
97
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* The hooks' light catalog entry (Phase 43, MSG-06; D-18, D-20).
|
|
4
|
+
*
|
|
5
|
+
* ADR-457 build-at-publish: this TypeScript file is the SOURCE OF TRUTH. It
|
|
6
|
+
* compiles to the gitignored `pp-core/bin/lib/hook-messages.cjs`; do NOT
|
|
7
|
+
* hand-edit the compiled output.
|
|
8
|
+
*
|
|
9
|
+
* The statusline runs on every prompt, so it cannot load the whole catalog
|
|
10
|
+
* (`messages.cjs` pulls in every slice, ~73 KB). This module imports only
|
|
11
|
+
* `./audience.cjs`, `./message-labels.cjs` and `./messages/hook.cjs`, plus
|
|
12
|
+
* `import type { CatalogEntry } from './messages.cjs'`, which compiles away.
|
|
13
|
+
* It carries its own copy of the engine's control-character regex, slot regex,
|
|
14
|
+
* ECHO_CAP, sanitise, fill and pick (about 25 lines), kept equal to
|
|
15
|
+
* `src/messages.cts` by the parity test in tests/hook-messages.test.cjs
|
|
16
|
+
* (PITFALLS #19).
|
|
17
|
+
*
|
|
18
|
+
* Its functions never throw: an unknown code, a bad param or an unreadable
|
|
19
|
+
* person file returns the fallback text. The hooks wrap their calls in
|
|
20
|
+
* try/catch as well and fall back to today's text when this module cannot load
|
|
21
|
+
* (D-20).
|
|
22
|
+
*/
|
|
23
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
+
exports.audienceOf = audienceOf;
|
|
25
|
+
exports.developerText = developerText;
|
|
26
|
+
exports.text = text;
|
|
27
|
+
exports.msg = msg;
|
|
28
|
+
exports.label = label;
|
|
29
|
+
exports.statusWord = statusWord;
|
|
30
|
+
const audience_cjs_1 = require("./audience.cjs");
|
|
31
|
+
const message_labels_cjs_1 = require("./message-labels.cjs");
|
|
32
|
+
const hook_cjs_1 = require("./messages/hook.cjs");
|
|
33
|
+
// Copies of the engine's constants; see the header.
|
|
34
|
+
const CONTROL_CHARS_RE = /[\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\x9f\p{Cf}]/gu;
|
|
35
|
+
const SLOT_RE = /\{([a-z][a-z0-9_]*)\}/g;
|
|
36
|
+
const ECHO_CAP = 120;
|
|
37
|
+
/** The shape ship.md writes into the STATE.md status: `Phase N shipped — PR #N`. Anchored, linear. */
|
|
38
|
+
const SHIPPED_STATUS_RE = /^Phase (\S+) shipped — PR #(\d+)$/;
|
|
39
|
+
// Lookup only: a Map, so `__proto__` and `constructor` are unknown codes.
|
|
40
|
+
const ENTRIES = new Map(Object.entries(hook_cjs_1.hook));
|
|
41
|
+
function sanitize(value) {
|
|
42
|
+
let raw;
|
|
43
|
+
if (typeof value === 'number')
|
|
44
|
+
raw = String(value);
|
|
45
|
+
else if (typeof value === 'string')
|
|
46
|
+
raw = value;
|
|
47
|
+
else
|
|
48
|
+
return '';
|
|
49
|
+
const collapsed = raw.replace(CONTROL_CHARS_RE, '').replace(/\s+/g, ' ').trim();
|
|
50
|
+
return collapsed.length <= ECHO_CAP ? collapsed : `${collapsed.slice(0, ECHO_CAP)}…`;
|
|
51
|
+
}
|
|
52
|
+
function fill(template, params, clean) {
|
|
53
|
+
return template.replace(SLOT_RE, (match, name) => {
|
|
54
|
+
if (!Object.hasOwn(params, name))
|
|
55
|
+
return clean ? match : '';
|
|
56
|
+
const value = params[name];
|
|
57
|
+
if (!clean)
|
|
58
|
+
return typeof value === 'string' || typeof value === 'number' ? String(value) : '';
|
|
59
|
+
return sanitize(value);
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
function pick(entry, audience) {
|
|
63
|
+
if (audience === 'business')
|
|
64
|
+
return entry.business ?? entry.engineer ?? entry.developer;
|
|
65
|
+
if (audience === 'engineer')
|
|
66
|
+
return entry.engineer ?? entry.developer;
|
|
67
|
+
return entry.developer;
|
|
68
|
+
}
|
|
69
|
+
/** The audience for this process; developer on any failure. */
|
|
70
|
+
function audienceOf(env = process.env) {
|
|
71
|
+
try {
|
|
72
|
+
return (0, audience_cjs_1.resolveAudience)(env, (0, audience_cjs_1.readPerson)(env)).audience;
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return 'developer';
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/** The developer text, params untouched. An unknown code returns the code. */
|
|
79
|
+
function developerText(code, params = {}) {
|
|
80
|
+
const entry = ENTRIES.get(code);
|
|
81
|
+
if (entry === undefined)
|
|
82
|
+
return code;
|
|
83
|
+
return fill(entry.developer, params ?? {}, false);
|
|
84
|
+
}
|
|
85
|
+
/** The text for an audience with no Ref line. An unknown code returns the code. */
|
|
86
|
+
function text(code, params = {}, audience = 'developer') {
|
|
87
|
+
const entry = ENTRIES.get(code);
|
|
88
|
+
if (entry === undefined)
|
|
89
|
+
return code;
|
|
90
|
+
if (audience === 'developer')
|
|
91
|
+
return developerText(code, params);
|
|
92
|
+
return fill(pick(entry, audience), params ?? {}, true);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* The message for an audience: business and engineer messages that are not
|
|
96
|
+
* parts end with the Ref line. An unknown code returns `fallback` when it is a
|
|
97
|
+
* string, else the code.
|
|
98
|
+
*/
|
|
99
|
+
function msg(code, params = {}, audience = 'developer', fallback) {
|
|
100
|
+
const entry = ENTRIES.get(code);
|
|
101
|
+
if (entry === undefined)
|
|
102
|
+
return typeof fallback === 'string' ? fallback : code;
|
|
103
|
+
if (audience === 'developer')
|
|
104
|
+
return developerText(code, params);
|
|
105
|
+
const body = text(code, params, audience);
|
|
106
|
+
return entry.part === true ? body : `${body}\nRef: ${code}`;
|
|
107
|
+
}
|
|
108
|
+
/** The display word for a canonical value; the value itself for a developer, an unknown domain or an unknown value. */
|
|
109
|
+
function label(domain, canonical, audience = 'developer') {
|
|
110
|
+
try {
|
|
111
|
+
return (0, message_labels_cjs_1.label)(domain, canonical, { audience });
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
return canonical;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* The display text for a STATE.md `status`. A lifecycle word is relabelled
|
|
119
|
+
* through `state.status`; the ship shape renders through
|
|
120
|
+
* `hook.statusline.status_shipped`; anything else shows as written. A non-string
|
|
121
|
+
* returns ''.
|
|
122
|
+
*/
|
|
123
|
+
function statusWord(status, audience = 'developer') {
|
|
124
|
+
if (typeof status !== 'string')
|
|
125
|
+
return '';
|
|
126
|
+
if (audience === 'developer')
|
|
127
|
+
return status;
|
|
128
|
+
const shipped = SHIPPED_STATUS_RE.exec(status);
|
|
129
|
+
if (shipped !== null)
|
|
130
|
+
return text('hook.statusline.status_shipped', { number: shipped[1], pr: shipped[2] }, audience);
|
|
131
|
+
return label('state.status', status, audience);
|
|
132
|
+
}
|