@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.
Files changed (119) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/bin/install.js +205 -28
  3. package/commands/pp-help.md +2 -1
  4. package/gemini-extension.json +1 -1
  5. package/hooks/dist/pp-check-update-worker.js +1 -1
  6. package/hooks/dist/pp-context-monitor.js +3 -3
  7. package/hooks/dist/pp-statusline.js +88 -14
  8. package/hooks/dist/pp-update-banner.js +44 -4
  9. package/hooks/dist/pp-workflow-guard.js +3 -3
  10. package/hooks/pp-check-update-worker.js +1 -1
  11. package/hooks/pp-context-monitor.js +3 -3
  12. package/hooks/pp-statusline.js +88 -14
  13. package/hooks/pp-update-banner.js +44 -4
  14. package/hooks/pp-workflow-guard.js +3 -3
  15. package/package.json +1 -1
  16. package/pp-core/bin/lib/audience-command.cjs +271 -0
  17. package/pp-core/bin/lib/audience.cjs +293 -0
  18. package/pp-core/bin/lib/commands.cjs +26 -17
  19. package/pp-core/bin/lib/discovery-charter.cjs +13 -1
  20. package/pp-core/bin/lib/discovery-input-check.cjs +11 -11
  21. package/pp-core/bin/lib/discovery-session-check.cjs +7 -7
  22. package/pp-core/bin/lib/discovery-status.cjs +419 -0
  23. package/pp-core/bin/lib/explain-command.cjs +48 -0
  24. package/pp-core/bin/lib/gate-card.cjs +97 -0
  25. package/pp-core/bin/lib/hook-messages.cjs +132 -0
  26. package/pp-core/bin/lib/init-contract.cjs +116 -76
  27. package/pp-core/bin/lib/init-interactive.cjs +110 -49
  28. package/pp-core/bin/lib/init.cjs +44 -3
  29. package/pp-core/bin/lib/io.cjs +33 -0
  30. package/pp-core/bin/lib/message-labels.cjs +75 -0
  31. package/pp-core/bin/lib/messages/core.cjs +20 -0
  32. package/pp-core/bin/lib/messages/discovery.cjs +267 -0
  33. package/pp-core/bin/lib/messages/gate.cjs +257 -0
  34. package/pp-core/bin/lib/messages/hook.cjs +114 -0
  35. package/pp-core/bin/lib/messages/init.cjs +513 -0
  36. package/pp-core/bin/lib/messages/progress.cjs +110 -0
  37. package/pp-core/bin/lib/messages.cjs +208 -0
  38. package/pp-core/bin/lib/vault-command-router.cjs +16 -3
  39. package/pp-core/bin/lib/violation-render.cjs +78 -0
  40. package/pp-core/bin/pp-tools.cjs +68 -23
  41. package/pp-core/bin/shared/config-schema.manifest.json +1 -0
  42. package/pp-core/references/audience-moments.md +119 -0
  43. package/pp-core/references/audience.md +114 -0
  44. package/pp-core/references/discovery-charter.md +2 -2
  45. package/pp-core/templates/project-charter.md +3 -4
  46. package/pp-core/workflows/add-phase.md +2 -0
  47. package/pp-core/workflows/add-tests.md +2 -0
  48. package/pp-core/workflows/add-todo.md +2 -0
  49. package/pp-core/workflows/ai-integration-phase.md +2 -0
  50. package/pp-core/workflows/audit-milestone.md +2 -0
  51. package/pp-core/workflows/autonomous.md +2 -0
  52. package/pp-core/workflows/check-todos.md +2 -0
  53. package/pp-core/workflows/code-review-fix.md +2 -0
  54. package/pp-core/workflows/code-review.md +2 -0
  55. package/pp-core/workflows/complete-milestone.md +10 -1
  56. package/pp-core/workflows/discovery-sprint/charter.md +3 -2
  57. package/pp-core/workflows/discovery-sprint/prefill.md +3 -0
  58. package/pp-core/workflows/discovery-sprint/prototype.md +14 -0
  59. package/pp-core/workflows/discovery-sprint/sessions/01-kick-off.md +4 -1
  60. package/pp-core/workflows/discovery-sprint/sessions/13-wrap-up.md +1 -1
  61. package/pp-core/workflows/discovery-sprint/vault-context.md +15 -0
  62. package/pp-core/workflows/discovery-sprint.md +14 -0
  63. package/pp-core/workflows/discovery-to-build.md +8 -2
  64. package/pp-core/workflows/discuss-phase-assumptions.md +2 -0
  65. package/pp-core/workflows/discuss-phase.md +1 -1
  66. package/pp-core/workflows/edit-phase.md +2 -0
  67. package/pp-core/workflows/eval-review.md +2 -0
  68. package/pp-core/workflows/execute-phase/steps/check-query-gate.md +25 -1
  69. package/pp-core/workflows/execute-phase/steps/vault-fetch.md +1 -0
  70. package/pp-core/workflows/execute-phase/steps/vault-load-gate.md +2 -0
  71. package/pp-core/workflows/execute-phase.md +1 -1
  72. package/pp-core/workflows/execute-plan.md +2 -0
  73. package/pp-core/workflows/extract-learnings.md +2 -0
  74. package/pp-core/workflows/help/modes/business.md +56 -0
  75. package/pp-core/workflows/help.md +12 -2
  76. package/pp-core/workflows/import.md +10 -1
  77. package/pp-core/workflows/ingest-docs.md +2 -0
  78. package/pp-core/workflows/init.md +17 -1
  79. package/pp-core/workflows/insert-phase.md +2 -0
  80. package/pp-core/workflows/list-workspaces.md +2 -0
  81. package/pp-core/workflows/manager.md +2 -0
  82. package/pp-core/workflows/map-codebase.md +2 -0
  83. package/pp-core/workflows/milestone-summary.md +2 -0
  84. package/pp-core/workflows/new-milestone.md +10 -1
  85. package/pp-core/workflows/new-project.md +12 -2
  86. package/pp-core/workflows/new-workspace.md +2 -0
  87. package/pp-core/workflows/next.md +8 -0
  88. package/pp-core/workflows/plan-milestone-gaps.md +10 -1
  89. package/pp-core/workflows/plan-phase.md +2 -1
  90. package/pp-core/workflows/plan-review-convergence.md +1 -1
  91. package/pp-core/workflows/progress.md +10 -0
  92. package/pp-core/workflows/quick.md +11 -1
  93. package/pp-core/workflows/remove-phase.md +2 -0
  94. package/pp-core/workflows/remove-workspace.md +2 -0
  95. package/pp-core/workflows/resume-project.md +25 -0
  96. package/pp-core/workflows/review.md +2 -0
  97. package/pp-core/workflows/scan.md +2 -0
  98. package/pp-core/workflows/secure-phase.md +2 -0
  99. package/pp-core/workflows/settings.md +41 -0
  100. package/pp-core/workflows/ship.md +2 -0
  101. package/pp-core/workflows/spec-phase.md +1 -1
  102. package/pp-core/workflows/ui-phase.md +2 -0
  103. package/pp-core/workflows/ui-review.md +2 -0
  104. package/pp-core/workflows/ultraplan-phase.md +2 -0
  105. package/pp-core/workflows/validate-phase.md +2 -0
  106. package/pp-core/workflows/vault-review.md +2 -0
  107. package/pp-core/workflows/verify-phase.md +2 -0
  108. package/pp-core/workflows/verify-work.md +2 -0
  109. package/scripts/affected-tests-lib.cjs +26 -12
  110. package/scripts/ci-release-only.cjs +158 -0
  111. package/scripts/live-runs/driver.cjs +326 -0
  112. package/scripts/live-runs/run.cjs +906 -0
  113. package/scripts/live-runs/scenarios.cjs +326 -0
  114. package/scripts/live-runs/scratch-project.cjs +132 -0
  115. package/scripts/live-runs/scrub.cjs +291 -0
  116. package/scripts/live-runs/turn-guard.cjs +107 -0
  117. package/scripts/live-runs/walk-setup.cjs +198 -0
  118. package/scripts/run-tests.cjs +151 -135
  119. 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
+ }