@pareto4/pp-core 1.21.1 → 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 +2 -2
- 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-flow-page.cjs +191 -0
- package/pp-core/bin/lib/discovery-for-phase.cjs +33 -5
- package/pp-core/bin/lib/discovery-input-check.cjs +11 -11
- package/pp-core/bin/lib/discovery-processes.cjs +97 -4
- 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/discovery-stories.cjs +36 -5
- package/pp-core/bin/lib/discovery-tests.cjs +398 -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 +38 -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 +92 -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-build-consumption.md +5 -2
- package/pp-core/references/discovery-charter.md +2 -2
- package/pp-core/references/discovery-id-conventions.md +1 -1
- package/pp-core/references/discovery-processes.md +31 -7
- package/pp-core/references/discovery-session-output.md +1 -1
- package/pp-core/references/discovery-tests.md +186 -0
- 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/session-13-gates.md +2 -0
- package/pp-core/workflows/discovery-sprint/sessions/01-kick-off.md +4 -1
- package/pp-core/workflows/discovery-sprint/sessions/03-to-be.md +135 -76
- package/pp-core/workflows/discovery-sprint/sessions/09-qa.md +94 -64
- package/pp-core/workflows/discovery-sprint/sessions/13-wrap-up.md +3 -2
- package/pp-core/workflows/discovery-sprint/vault-context.md +15 -0
- package/pp-core/workflows/discovery-sprint.md +16 -2
- package/pp-core/workflows/discovery-to-build/steps/handoff-gates.md +16 -9
- package/pp-core/workflows/discovery-to-build/templates/discovery-references.md +1 -1
- package/pp-core/workflows/discovery-to-build/templates/pp-handoff.md +9 -1
- package/pp-core/workflows/discovery-to-build.md +10 -4
- package/pp-core/workflows/discuss-phase/discovery.md +2 -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 -132
- package/scripts/test-env.cjs +64 -0
|
@@ -40,10 +40,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
40
40
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
41
41
|
};
|
|
42
42
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
43
|
+
exports.printsResultJson = printsResultJson;
|
|
43
44
|
exports.runInitInteractive = runInitInteractive;
|
|
44
45
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
45
46
|
const node_path_1 = __importDefault(require("node:path"));
|
|
46
47
|
const init_contract_cjs_1 = require("./init-contract.cjs");
|
|
48
|
+
const messages_cjs_1 = require("./messages.cjs");
|
|
47
49
|
// ─── Constants ───────────────────────────────────────────────────────────────
|
|
48
50
|
/** Defensive circuit breaker against a runaway loop (a misbehaving
|
|
49
51
|
* prompter or an unexpected session state that never reaches `done` or
|
|
@@ -71,29 +73,63 @@ function readBindingsContent(cwd) {
|
|
|
71
73
|
return null;
|
|
72
74
|
}
|
|
73
75
|
}
|
|
76
|
+
/** A value shown in the compose summary or an echo, worded for business and engineer through the catalog
|
|
77
|
+
* (`init.word.<kind>.<value>`). A developer, and any value the catalog does not know, keeps the raw value. */
|
|
78
|
+
function word(kind, value, audience) {
|
|
79
|
+
if (audience === 'developer')
|
|
80
|
+
return value;
|
|
81
|
+
const code = `init.word.${kind}.${value.replace(/-/g, '_')}`;
|
|
82
|
+
return (0, messages_cjs_1.has)(code) ? (0, messages_cjs_1.text)(code, {}, { audience }) : value;
|
|
83
|
+
}
|
|
84
|
+
/** A parameterless message: the developer's catalog text, or the business/engineer wording with its Ref line. */
|
|
85
|
+
function plainOrMsg(code, audience) {
|
|
86
|
+
return audience === 'developer' ? (0, messages_cjs_1.msgDeveloper)(code) : (0, messages_cjs_1.msg)(code, {}, { audience });
|
|
87
|
+
}
|
|
88
|
+
/** One unfinished-session problem as a block: its message with its own `Ref:` line, the first line prefixed `- ` and
|
|
89
|
+
* every following line indented two spaces (the shape `renderViolation` gives discovery problems). */
|
|
90
|
+
function violationBlock(ref, audience) {
|
|
91
|
+
const lines = (0, messages_cjs_1.msg)(ref.code, ref.params, { audience }).split('\n');
|
|
92
|
+
return lines.map((line, i) => (i === 0 ? `- ${line}` : ` ${line}`)).join('\n');
|
|
93
|
+
}
|
|
94
|
+
/** Whether the CLI prints the trailing `{ "ok": … }` JSON after the human result. Autonomous Decision 7: it is a
|
|
95
|
+
* TTY-only dump that no workflow parses (the command refuses a non-TTY), so business users and engineers see only
|
|
96
|
+
* the human result; developers and `--raw` keep it. */
|
|
97
|
+
function printsResultJson(audience, raw) {
|
|
98
|
+
return raw || audience === 'developer';
|
|
99
|
+
}
|
|
74
100
|
/** Render `compose_confirm`'s provenance table, drift list and action to
|
|
75
101
|
* the prompter, BEFORE the caller asks for approval — plan Task 1's own
|
|
76
|
-
* ordering requirement.
|
|
77
|
-
|
|
102
|
+
* ordering requirement. The text is the catalog's (`init.compose.*`): a
|
|
103
|
+
* developer sees what the code always printed. */
|
|
104
|
+
function renderComposeConfirm(prompter, info, audience) {
|
|
78
105
|
if (!isRecord(info))
|
|
79
106
|
return;
|
|
80
107
|
const typed = info;
|
|
81
|
-
|
|
108
|
+
const say = (code, params) => audience === 'developer' ? (0, messages_cjs_1.msgDeveloper)(code, params) : (0, messages_cjs_1.text)(code, params, { audience });
|
|
109
|
+
prompter.print(say('init.compose.header', {}));
|
|
82
110
|
for (const entry of typed.provenance ?? []) {
|
|
83
|
-
prompter.print(
|
|
111
|
+
prompter.print(say('init.compose.provenance', {
|
|
112
|
+
field: entry.field,
|
|
113
|
+
value: JSON.stringify(entry.value),
|
|
114
|
+
source: word('source', entry.source, audience),
|
|
115
|
+
}));
|
|
84
116
|
}
|
|
85
117
|
const drift = typed.drift ?? [];
|
|
86
118
|
if (drift.length > 0) {
|
|
87
|
-
prompter.print('
|
|
119
|
+
prompter.print(say('init.compose.drift_header', {}));
|
|
88
120
|
for (const d of drift) {
|
|
89
|
-
prompter.print(
|
|
90
|
-
|
|
121
|
+
prompter.print(say('init.compose.drift', {
|
|
122
|
+
key: d.key,
|
|
123
|
+
profile_value: JSON.stringify(d.profileValue),
|
|
124
|
+
file_value: JSON.stringify(d.effectiveValue),
|
|
125
|
+
}));
|
|
91
126
|
}
|
|
92
127
|
}
|
|
93
128
|
for (const warning of typed.warnings ?? []) {
|
|
94
|
-
prompter.print(
|
|
129
|
+
prompter.print(say('init.compose.warning', { warning }));
|
|
95
130
|
}
|
|
96
|
-
|
|
131
|
+
const action = typed.action ?? 'unknown';
|
|
132
|
+
prompter.print(say('init.compose.action', { action: word('action', action, audience) }));
|
|
97
133
|
}
|
|
98
134
|
// ─── runInitInteractive ─────────────────────────────────────────────────────
|
|
99
135
|
/**
|
|
@@ -117,6 +153,9 @@ function renderComposeConfirm(prompter, info) {
|
|
|
117
153
|
*/
|
|
118
154
|
async function runInitInteractive(cwd, prompter, opts = {}) {
|
|
119
155
|
const sessionOpts = { offline: opts.offline === true, _globalRoot: opts._globalRoot };
|
|
156
|
+
const audience = opts.audience ?? (0, messages_cjs_1.audienceOf)(process.env);
|
|
157
|
+
/** The step's wording for this audience: the catalog text for business and engineer, the session's own text for a developer. */
|
|
158
|
+
const say = (ref, developerText) => audience === 'developer' || ref === undefined ? developerText : (0, messages_cjs_1.text)(ref.code, ref.params, { audience });
|
|
120
159
|
let wroteBinding = false;
|
|
121
160
|
// PBI 74299: answers the session filled in itself are shown once each, as
|
|
122
161
|
// soon as they are decided, so the operator sees every value before the
|
|
@@ -125,15 +164,21 @@ async function runInitInteractive(cwd, prompter, opts = {}) {
|
|
|
125
164
|
for (let steps = 1; steps <= MAX_SESSION_STEPS; steps++) {
|
|
126
165
|
const state = (0, init_contract_cjs_1.initSessionNext)(cwd, sessionOpts);
|
|
127
166
|
if (state.error !== undefined) {
|
|
128
|
-
|
|
167
|
+
const errorRef = state.display?.message;
|
|
168
|
+
prompter.print(audience !== 'developer' && errorRef !== undefined
|
|
169
|
+
? (0, messages_cjs_1.msg)(errorRef.code, errorRef.params, { audience })
|
|
170
|
+
: (0, messages_cjs_1.msgDeveloper)('init.error_line', { message: state.message ?? state.error }));
|
|
129
171
|
return { ok: false, wroteBinding, steps, violations: [] };
|
|
130
172
|
}
|
|
131
173
|
for (const auto of state.autoAnswered ?? []) {
|
|
132
174
|
if (shownAutoAnswers.has(auto.id))
|
|
133
175
|
continue;
|
|
134
176
|
shownAutoAnswers.add(auto.id);
|
|
135
|
-
const
|
|
136
|
-
|
|
177
|
+
const code = auto.source === 'derived' ? 'init.auto_answer.derived' : 'init.auto_answer.org_profile';
|
|
178
|
+
const id = word('step', auto.id, audience);
|
|
179
|
+
prompter.print(audience === 'developer'
|
|
180
|
+
? (0, messages_cjs_1.msgDeveloper)(code, { id, value: auto.value })
|
|
181
|
+
: (0, messages_cjs_1.text)(code, { id, value: auto.value }, { audience }));
|
|
137
182
|
}
|
|
138
183
|
if (state.done) {
|
|
139
184
|
if (wroteBinding) {
|
|
@@ -148,35 +193,20 @@ async function runInitInteractive(cwd, prompter, opts = {}) {
|
|
|
148
193
|
// line `pp-tools init` prints contradict the config file it just
|
|
149
194
|
// finished writing, which is precisely the
|
|
150
195
|
// tool-reports-a-state-that-isn't-real failure this phase exists to
|
|
151
|
-
// end.
|
|
196
|
+
// end. Each reading has its own whole-sentence catalog entry.
|
|
152
197
|
const vaultState = (0, init_contract_cjs_1.readVaultEnabledState)(cwd).state;
|
|
198
|
+
let code;
|
|
153
199
|
if (vaultState === 'off') {
|
|
154
|
-
|
|
155
|
-
'as this project set it, so the vault capability stays off and /pp-init Step 2.6 stays ' +
|
|
156
|
-
'inert (arm it with pp-tools config-set vault.enabled true).');
|
|
200
|
+
code = 'init.done.vault_off';
|
|
157
201
|
}
|
|
158
202
|
else if (vaultState === 'malformed') {
|
|
159
|
-
|
|
160
|
-
'value and was left untouched — pp-tools query init-contract --verify reports it as ' +
|
|
161
|
-
'INIT_VAULT_ENABLED_MALFORMED; store the boolean with pp-tools config-set vault.enabled true.');
|
|
203
|
+
code = 'init.done.vault_malformed';
|
|
162
204
|
}
|
|
163
205
|
else if (vaultState === 'armed') {
|
|
164
206
|
// Quick 260916-s5n: vault.required is named only when the READING
|
|
165
207
|
// says armed, by the same rule — an explicit false is the project's
|
|
166
208
|
// choice and the last line printed must not contradict it.
|
|
167
|
-
|
|
168
|
-
// Built as a head plus clauses carrying no terminator of their own,
|
|
169
|
-
// with exactly one full stop appended at the end.
|
|
170
|
-
const head = 'Done — .platform-bindings.json was composed and written; vault.enabled is now true ' +
|
|
171
|
-
'(the vault capability is armed — /pp-init Step 2.6 probes reachability next)';
|
|
172
|
-
// 15-REVIEW WR-09: the clause names what is recorded and checked.
|
|
173
|
-
const standardsClause = '; each plan records which client standards documents governed it ' +
|
|
174
|
-
'(<vault_standards>), checked by the vault.trace gate';
|
|
175
|
-
const requiredClause = requiredArmed
|
|
176
|
-
? '; vault.required is true, so a client vault miss, a failed fetch or an unwritable vault summary ' +
|
|
177
|
-
'stops the build at plan:pre and execute:pre'
|
|
178
|
-
: '';
|
|
179
|
-
prompter.print(head + standardsClause + requiredClause + '.');
|
|
209
|
+
code = (0, init_contract_cjs_1.readVaultRequiredState)(cwd).state === 'armed' ? 'init.done.vault_armed_required' : 'init.done.vault_armed';
|
|
180
210
|
}
|
|
181
211
|
else {
|
|
182
212
|
// 'absent' — unreachable after an approved compose (the arming
|
|
@@ -184,34 +214,49 @@ async function runInitInteractive(cwd, prompter, opts = {}) {
|
|
|
184
214
|
// where that write itself failed. It says only what is certain:
|
|
185
215
|
// the binding was written, and it does NOT claim the capability is
|
|
186
216
|
// on, because the key is not there to say so.
|
|
187
|
-
|
|
217
|
+
code = 'init.done.written';
|
|
188
218
|
}
|
|
219
|
+
prompter.print(plainOrMsg(code, audience));
|
|
189
220
|
}
|
|
190
221
|
else if (typeof state.info === 'string') {
|
|
191
|
-
|
|
222
|
+
const infoRef = state.display?.info;
|
|
223
|
+
prompter.print(audience === 'developer' || infoRef === undefined ? state.info : (0, messages_cjs_1.msg)(infoRef.code, infoRef.params, { audience }));
|
|
192
224
|
}
|
|
193
225
|
else {
|
|
194
|
-
prompter.print('
|
|
226
|
+
prompter.print(plainOrMsg('init.done.nothing_composed', audience));
|
|
195
227
|
}
|
|
196
228
|
return { ok: true, wroteBinding, steps, violations: [] };
|
|
197
229
|
}
|
|
198
230
|
if (state.step === 'verify') {
|
|
199
231
|
const violations = state.violations ?? [];
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
232
|
+
if (audience === 'developer') {
|
|
233
|
+
prompter.print((0, messages_cjs_1.msgDeveloper)('init.verify'));
|
|
234
|
+
for (const v of violations) {
|
|
235
|
+
prompter.print((0, messages_cjs_1.msgDeveloper)('init.verify.line', { code: v.code, message: v.message }));
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
else {
|
|
239
|
+
prompter.print((0, messages_cjs_1.text)('init.verify', {}, { audience }));
|
|
240
|
+
violations.forEach((v, i) => {
|
|
241
|
+
const ref = state.display?.violations?.[i];
|
|
242
|
+
// A code the catalog has no wording for keeps the developer line.
|
|
243
|
+
prompter.print(ref !== undefined && (0, messages_cjs_1.has)(ref.code)
|
|
244
|
+
? violationBlock(ref, audience)
|
|
245
|
+
: (0, messages_cjs_1.msgDeveloper)('init.verify.line', { code: v.code, message: v.message }));
|
|
246
|
+
});
|
|
247
|
+
prompter.print((0, messages_cjs_1.refLine)('init.verify'));
|
|
203
248
|
}
|
|
204
249
|
return { ok: false, wroteBinding, steps, violations };
|
|
205
250
|
}
|
|
206
251
|
if (state.step === null) {
|
|
207
252
|
// Defensive: the session contract never returns a null step outside
|
|
208
253
|
// `done`/`verify`/`error` above. Fail closed rather than crash.
|
|
209
|
-
prompter.print('
|
|
254
|
+
prompter.print(plainOrMsg('init.unexpected_state', audience));
|
|
210
255
|
return { ok: false, wroteBinding, steps, violations: [] };
|
|
211
256
|
}
|
|
212
257
|
const stepId = state.step;
|
|
213
258
|
if (stepId === 'compose_confirm') {
|
|
214
|
-
renderComposeConfirm(prompter, state.info);
|
|
259
|
+
renderComposeConfirm(prompter, state.info, audience);
|
|
215
260
|
}
|
|
216
261
|
// Ask, re-asking on rejection — never swallowed (T-34-73).
|
|
217
262
|
for (;;) {
|
|
@@ -220,13 +265,23 @@ async function runInitInteractive(cwd, prompter, opts = {}) {
|
|
|
220
265
|
// pre-existing file that predates this run (a bare post-hoc
|
|
221
266
|
// `existsSync` cannot make that distinction).
|
|
222
267
|
const preBindings = stepId === 'compose_confirm' ? readBindingsContent(cwd) : null;
|
|
223
|
-
const answer = await prompter.ask(
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
268
|
+
const answer = await prompter.ask(audience === 'developer'
|
|
269
|
+
? {
|
|
270
|
+
prompt: state.prompt ?? '',
|
|
271
|
+
options: state.options,
|
|
272
|
+
preselected: state.preselected,
|
|
273
|
+
preselectedLabel: state.preselectedLabel,
|
|
274
|
+
optionalAnswer: state.optionalAnswer,
|
|
275
|
+
}
|
|
276
|
+
: {
|
|
277
|
+
prompt: say(state.display?.prompt, state.prompt ?? ''),
|
|
278
|
+
options: state.options,
|
|
279
|
+
preselected: state.preselected,
|
|
280
|
+
preselectedLabel: state.preselectedLabel === undefined ? undefined : say(state.display?.preselectedLabel, state.preselectedLabel),
|
|
281
|
+
optionalAnswer: state.optionalAnswer,
|
|
282
|
+
code: state.display?.prompt?.code,
|
|
283
|
+
audience,
|
|
284
|
+
});
|
|
230
285
|
// UAT-13-F2: the driver has no default-acceptance path, on purpose — the
|
|
231
286
|
// answer goes to initSessionAnswer verbatim, which owns the none/blank decline.
|
|
232
287
|
const result = (0, init_contract_cjs_1.initSessionAnswer)(cwd, stepId, answer, sessionOpts);
|
|
@@ -236,9 +291,15 @@ async function runInitInteractive(cwd, prompter, opts = {}) {
|
|
|
236
291
|
}
|
|
237
292
|
break;
|
|
238
293
|
}
|
|
239
|
-
|
|
294
|
+
const rejectedRef = result.display?.message;
|
|
295
|
+
const fallback = { code: 'init.rejected_fallback', params: { reason: result.reason ?? 'unknown reason' } };
|
|
296
|
+
prompter.print(audience === 'developer'
|
|
297
|
+
? (result.message ?? (0, messages_cjs_1.msgDeveloper)(fallback.code, fallback.params))
|
|
298
|
+
: rejectedRef !== undefined
|
|
299
|
+
? (0, messages_cjs_1.msg)(rejectedRef.code, rejectedRef.params, { audience })
|
|
300
|
+
: (result.message ?? (0, messages_cjs_1.msg)(fallback.code, fallback.params, { audience })));
|
|
240
301
|
}
|
|
241
302
|
}
|
|
242
|
-
prompter.print('init
|
|
303
|
+
prompter.print(plainOrMsg('init.step_overflow', audience));
|
|
243
304
|
return { ok: false, wroteBinding, steps: MAX_SESSION_STEPS, violations: [] };
|
|
244
305
|
}
|
package/pp-core/bin/lib/init.cjs
CHANGED
|
@@ -36,6 +36,7 @@ const plan_sizing_cjs_1 = require("./plan-sizing.cjs");
|
|
|
36
36
|
// eslint-disable-next-line @typescript-eslint/no-require-imports -- plan-scan.cjs is an export= CommonJS module
|
|
37
37
|
const scanPhasePlans = require("./plan-scan.cjs");
|
|
38
38
|
const state_document_cjs_1 = require("./state-document.cjs");
|
|
39
|
+
const audience_cjs_1 = require("./audience.cjs");
|
|
39
40
|
const runtime_slash_cjs_1 = require("./runtime-slash.cjs");
|
|
40
41
|
// eslint-disable-next-line @typescript-eslint/no-require-imports -- commands.cjs is an export= CommonJS module
|
|
41
42
|
const commandsMod = require("./commands.cjs");
|
|
@@ -113,7 +114,45 @@ function withProjectRoot(cwd, result) {
|
|
|
113
114
|
result['project_title'] = h1Match[1].trim();
|
|
114
115
|
}
|
|
115
116
|
}
|
|
116
|
-
return result;
|
|
117
|
+
return withAudience(result, typeof config.response_language === 'string' ? config.response_language : null);
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Phase 40 (D-01..D-04, AUD-03): carry the person's audience in the init payload.
|
|
121
|
+
*
|
|
122
|
+
* Resolved once per init call through the Phase 39 leaf. For `developer` (set or
|
|
123
|
+
* defaulted) the result is returned untouched, so every developer payload stays
|
|
124
|
+
* byte-identical (D-01, D-03). For `business` and `engineer` the payload gains `audience`
|
|
125
|
+
* and `audience_ref`, and `response_language` becomes the resolved language name, so the
|
|
126
|
+
* person's language wins over a project's (D-02). Person-file warnings go to stderr only,
|
|
127
|
+
* never into the JSON (D-04).
|
|
128
|
+
*/
|
|
129
|
+
function withAudience(result, projectLanguage) {
|
|
130
|
+
// Assignment keeps an existing response_language key in place, as before.
|
|
131
|
+
return Object.assign(result, audienceFields(projectLanguage));
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* The fields `withAudience` adds, on their own: `{}` for a developer (set or defaulted),
|
|
135
|
+
* otherwise `audience`, `audience_ref` and the resolved `response_language`. Phase 40 CR-01:
|
|
136
|
+
* `pp-tools audience fields` prints this object, so a workflow that asks the person something
|
|
137
|
+
* before its first init call learns the audience first, resolved by this same code.
|
|
138
|
+
*/
|
|
139
|
+
function audienceFields(projectLanguage) {
|
|
140
|
+
const person = (0, audience_cjs_1.readPerson)(process.env);
|
|
141
|
+
const aud = (0, audience_cjs_1.resolveAudience)(process.env, person);
|
|
142
|
+
for (const w of aud.warnings)
|
|
143
|
+
process.stderr.write(`pp-tools: warning: ${w}\n`);
|
|
144
|
+
if (aud.audience === 'developer')
|
|
145
|
+
return {};
|
|
146
|
+
const lang = (0, audience_cjs_1.resolveLanguage)({ env: process.env, person, projectResponseLanguage: projectLanguage });
|
|
147
|
+
const fields = { audience: aud.audience };
|
|
148
|
+
// The compiled file is pp-core/bin/lib/init.cjs, so ../../references is pp-core/references.
|
|
149
|
+
// bin/install.js copies pp-core/ wholesale for every runtime, so the path resolves in every
|
|
150
|
+
// install. Absolute with forward slashes; never derived from env, the person file or config.
|
|
151
|
+
fields['audience_ref'] = toPosixPath(node_path_1.default.join(__dirname, '..', '..', 'references', 'audience.md'));
|
|
152
|
+
if (typeof lang.name === 'string' && lang.name !== '') {
|
|
153
|
+
fields['response_language'] = lang.name;
|
|
154
|
+
}
|
|
155
|
+
return fields;
|
|
117
156
|
}
|
|
118
157
|
function getInitGitState(cwd) {
|
|
119
158
|
const info = gitWorktreeInfoInternal(cwd);
|
|
@@ -1487,7 +1526,7 @@ function cmdInitListWorkspaces(cwd, raw) {
|
|
|
1487
1526
|
workspaces,
|
|
1488
1527
|
workspace_count: workspaces.length,
|
|
1489
1528
|
};
|
|
1490
|
-
output(result, raw);
|
|
1529
|
+
output(withAudience(result, null), raw);
|
|
1491
1530
|
}
|
|
1492
1531
|
function cmdInitRemoveWorkspace(cwd, name, raw) {
|
|
1493
1532
|
const homedir = process.env['HOME'] || node_os_1.default.homedir();
|
|
@@ -1550,7 +1589,7 @@ function cmdInitRemoveWorkspace(cwd, name, raw) {
|
|
|
1550
1589
|
dirty_repos: dirtyRepos,
|
|
1551
1590
|
has_dirty_repos: dirtyRepos.length > 0,
|
|
1552
1591
|
};
|
|
1553
|
-
output(result, raw);
|
|
1592
|
+
output(withAudience(result, null), raw);
|
|
1554
1593
|
}
|
|
1555
1594
|
function buildAgentSkillsBlock(config, agentType, projectRoot) {
|
|
1556
1595
|
const runtime = (config && config['runtime']) || 'claude';
|
|
@@ -1916,6 +1955,8 @@ module.exports = {
|
|
|
1916
1955
|
cmdInitNewWorkspace,
|
|
1917
1956
|
cmdInitListWorkspaces,
|
|
1918
1957
|
cmdInitRemoveWorkspace,
|
|
1958
|
+
// Phase 40 CR-01: the audience fields alone, printed by `pp-tools audience fields`.
|
|
1959
|
+
audienceFields,
|
|
1919
1960
|
detectChildRepos,
|
|
1920
1961
|
buildAgentSkillsBlock,
|
|
1921
1962
|
cmdAgentSkills,
|
package/pp-core/bin/lib/io.cjs
CHANGED
|
@@ -15,6 +15,7 @@ const node_fs_1 = __importDefault(require("node:fs"));
|
|
|
15
15
|
const node_os_1 = __importDefault(require("node:os"));
|
|
16
16
|
const node_path_1 = __importDefault(require("node:path"));
|
|
17
17
|
const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
|
|
18
|
+
const messages_cjs_1 = require("./messages.cjs");
|
|
18
19
|
// ─── Temp-file helpers (needed by output()) ──────────────────────────────────
|
|
19
20
|
/**
|
|
20
21
|
* Dedicated GSD temp directory: path.join(os.tmpdir(), 'gsd').
|
|
@@ -225,8 +226,17 @@ const ERROR_REASON = Object.freeze({
|
|
|
225
226
|
DISCOVERY_SESSION_CHECK_FAILED: 'discovery_session_check_failed',
|
|
226
227
|
DISCOVERY_DEVIATIONS_CHECK_FAILED: 'discovery_deviations_check_failed',
|
|
227
228
|
DISCOVERY_DEVIATIONS_UNAPPROVED: 'discovery_deviations_unapproved',
|
|
229
|
+
// discovery tests register (Session 9 — testing and UAT)
|
|
230
|
+
DISCOVERY_TESTS_CHECK_FAILED: 'discovery_tests_check_failed',
|
|
231
|
+
DISCOVERY_TESTS_INCOMPLETE: 'discovery_tests_incomplete',
|
|
232
|
+
// discovery flow page (Session 3 Step 5C — iterate a swimlane)
|
|
233
|
+
DISCOVERY_FLOW_PAGE_FAILED: 'discovery_flow_page_failed',
|
|
228
234
|
// timeline (Phase 35: end with no matching open run)
|
|
229
235
|
TIMELINE_NO_OPEN_RUN: 'timeline_no_open_run',
|
|
236
|
+
// audience (Phase 39: person file could not be saved)
|
|
237
|
+
AUDIENCE_WRITE_FAILED: 'audience_write_failed',
|
|
238
|
+
// explain (Phase 41: pp-tools explain <code>)
|
|
239
|
+
EXPLAIN_UNKNOWN_CODE: 'explain_unknown_code',
|
|
230
240
|
// generic
|
|
231
241
|
USAGE: 'usage',
|
|
232
242
|
UNKNOWN: 'unknown',
|
|
@@ -260,6 +270,33 @@ function error(message, reason = ERROR_REASON.UNKNOWN) {
|
|
|
260
270
|
}
|
|
261
271
|
process.exit(1);
|
|
262
272
|
}
|
|
273
|
+
/**
|
|
274
|
+
* Emit a catalog message as an error and exit (Phase 41 D-05, D-08).
|
|
275
|
+
*
|
|
276
|
+
* The `--json-errors` `message` is the DEVELOPER text for every audience,
|
|
277
|
+
* because workflows and tests parse it. In plain mode a developer gets
|
|
278
|
+
* `Error: ` plus the developer text, exactly as error() writes today; a
|
|
279
|
+
* business user or engineer gets the resolved wording with its `Ref: <code>`
|
|
280
|
+
* line and no `Error: ` prefix, since the first sentence already says what
|
|
281
|
+
* happened. `params` may be a function of the audience so a caller can supply
|
|
282
|
+
* different human-facing values (a session number and a section title) than the
|
|
283
|
+
* developer text uses.
|
|
284
|
+
*/
|
|
285
|
+
function errorCode(code, params, reason = ERROR_REASON.UNKNOWN) {
|
|
286
|
+
const p = (a) => (typeof params === 'function' ? params(a) : params);
|
|
287
|
+
if (_jsonErrorMode) {
|
|
288
|
+
const message = (0, messages_cjs_1.msgDeveloper)(code, p('developer'));
|
|
289
|
+
writeAllSync(2, JSON.stringify({ ok: false, reason, message }) + '\n');
|
|
290
|
+
}
|
|
291
|
+
else {
|
|
292
|
+
const audience = (0, messages_cjs_1.audienceOf)();
|
|
293
|
+
if (audience === 'developer')
|
|
294
|
+
writeAllSync(2, 'Error: ' + (0, messages_cjs_1.msgDeveloper)(code, p('developer')) + '\n');
|
|
295
|
+
else
|
|
296
|
+
writeAllSync(2, (0, messages_cjs_1.msg)(code, p(audience), { audience }) + '\n');
|
|
297
|
+
}
|
|
298
|
+
process.exit(1);
|
|
299
|
+
}
|
|
263
300
|
// ─── Pareto visual brand: banner engine (Phase 17 / UIBR-01, UIBR-02) ─────────
|
|
264
301
|
//
|
|
265
302
|
// A hand-rolled ANSI banner emitter (no color library — CLAUDE.md constraint).
|
|
@@ -382,6 +419,7 @@ module.exports = {
|
|
|
382
419
|
setJsonErrorMode,
|
|
383
420
|
getJsonErrorMode,
|
|
384
421
|
error,
|
|
422
|
+
errorCode,
|
|
385
423
|
// Pareto visual brand banner engine (Phase 17 / UIBR-01, UIBR-02)
|
|
386
424
|
PARETO_BLUE,
|
|
387
425
|
CREW,
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Display-time labels for values the code reads back (Phase 41, D-13).
|
|
4
|
+
*
|
|
5
|
+
* ADR-457 build-at-publish: compiles to the gitignored
|
|
6
|
+
* `pp-core/bin/lib/message-labels.cjs`.
|
|
7
|
+
*
|
|
8
|
+
* Display time only: writers write and readers read the canonical word
|
|
9
|
+
* ("Executed", "Pre-Filled"); only the text a person reads is relabelled, at
|
|
10
|
+
* the last step before output. A developer, and any unknown value or domain,
|
|
11
|
+
* gets the canonical word back. D-14 allowlists the modules that may import
|
|
12
|
+
* this file (plan 41-11 enforces it), so a label can never flow into a parser.
|
|
13
|
+
*
|
|
14
|
+
* `state.status` and `state.next_action` relabel the STATE.md lifecycle words
|
|
15
|
+
* and recommended next actions the statusline shows (Phase 43, MSG-06). They are
|
|
16
|
+
* their own domains because those words (executing, verifying, ...) are not
|
|
17
|
+
* `phase.status` keys, which are ROADMAP words pinned to the contract snapshot.
|
|
18
|
+
*
|
|
19
|
+
* Wording follows pp-core/references/audience.md: the business column never
|
|
20
|
+
* shows an internal word; the engineer column keeps real terms.
|
|
21
|
+
*/
|
|
22
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
|
+
exports.LABELS = void 0;
|
|
24
|
+
exports.label = label;
|
|
25
|
+
exports.LABELS = Object.freeze({
|
|
26
|
+
'phase.status': Object.freeze({
|
|
27
|
+
Pending: { engineer: 'Pending', business: 'Waiting' },
|
|
28
|
+
'Not Started': { engineer: 'Not started', business: 'Not started yet' },
|
|
29
|
+
Planned: { engineer: 'Planned', business: 'Ready to build' },
|
|
30
|
+
'In Progress': { engineer: 'In progress', business: 'Being worked on' },
|
|
31
|
+
Executed: { engineer: 'Executed (not yet verified)', business: 'Built, not yet checked' },
|
|
32
|
+
'Needs Review': { engineer: 'Needs review', business: 'Waiting for your review' },
|
|
33
|
+
Complete: { engineer: 'Complete', business: 'Done' },
|
|
34
|
+
}),
|
|
35
|
+
'discovery.status': Object.freeze({
|
|
36
|
+
'Not Started': { engineer: 'Not started', business: 'Not started yet' },
|
|
37
|
+
'Pre-Filled': { engineer: 'Pre-filled from documents', business: 'Prepared from your documents' },
|
|
38
|
+
'In Progress': { engineer: 'In progress', business: 'Being worked on' },
|
|
39
|
+
Completed: { engineer: 'Completed', business: 'Done' },
|
|
40
|
+
}),
|
|
41
|
+
// STATE.md frontmatter `status`: normalizeStateStatus output plus the pass-through values
|
|
42
|
+
// (`Defining requirements`, `Active`) and the statusline body fallback `complete`.
|
|
43
|
+
'state.status': Object.freeze({
|
|
44
|
+
discussing: { engineer: 'discussing', business: 'being discussed' },
|
|
45
|
+
planning: { engineer: 'planning', business: 'being planned' },
|
|
46
|
+
executing: { engineer: 'executing', business: 'being built' },
|
|
47
|
+
verifying: { engineer: 'verifying', business: 'being checked' },
|
|
48
|
+
completed: { engineer: 'completed', business: 'done' },
|
|
49
|
+
complete: { engineer: 'complete', business: 'done' },
|
|
50
|
+
paused: { engineer: 'paused', business: 'on hold' },
|
|
51
|
+
unknown: { engineer: 'unknown', business: 'not recorded yet' },
|
|
52
|
+
'Defining requirements': { engineer: 'defining requirements', business: 'agreeing what to build' },
|
|
53
|
+
Active: { engineer: 'active', business: 'under way' },
|
|
54
|
+
}),
|
|
55
|
+
// STATE.md frontmatter `next_action`: the recommended next command, when idle.
|
|
56
|
+
'state.next_action': Object.freeze({
|
|
57
|
+
'discuss-phase': { engineer: 'discuss phase', business: 'talk through stage' },
|
|
58
|
+
'plan-phase': { engineer: 'plan phase', business: 'plan stage' },
|
|
59
|
+
'execute-phase': { engineer: 'execute phase', business: 'build stage' },
|
|
60
|
+
'verify-phase': { engineer: 'verify phase', business: 'check stage' },
|
|
61
|
+
'verify-work': { engineer: 'verify work (UAT)', business: 'test stage' },
|
|
62
|
+
}),
|
|
63
|
+
});
|
|
64
|
+
/** The word to show for a canonical value. Developer and anything unknown get the canonical word back. */
|
|
65
|
+
function label(domain, canonical, opts) {
|
|
66
|
+
if (opts.audience === 'developer')
|
|
67
|
+
return canonical;
|
|
68
|
+
if (!Object.hasOwn(exports.LABELS, domain))
|
|
69
|
+
return canonical;
|
|
70
|
+
const table = exports.LABELS[domain];
|
|
71
|
+
if (!Object.hasOwn(table, canonical))
|
|
72
|
+
return canonical;
|
|
73
|
+
const pair = table[canonical];
|
|
74
|
+
return opts.audience === 'business' ? pair.business : pair.engineer;
|
|
75
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Catalog slice: core messages (Phase 41, plan 41-05). Data only.
|
|
4
|
+
*
|
|
5
|
+
* ADR-457 build-at-publish: compiles to the gitignored
|
|
6
|
+
* `pp-core/bin/lib/messages/core.cjs`. Imports nothing but the entry type.
|
|
7
|
+
* Wording follows pp-core/references/audience.md. Every code is a stable key;
|
|
8
|
+
* `Ref:` lines are added by the engine at render time, never written here.
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.core = void 0;
|
|
12
|
+
exports.core = Object.freeze({
|
|
13
|
+
explain_unknown_code: {
|
|
14
|
+
reach: 'business',
|
|
15
|
+
developer: 'No explanation for {code}.',
|
|
16
|
+
engineer: 'No explanation for {code}. No message has that reference code. Check that you copied the whole code from the line after "Ref:".',
|
|
17
|
+
business: 'No explanation for {code}. Copy the whole code from the line after "Ref:" and try again, or send it to Pareto as it is.',
|
|
18
|
+
explain: 'Every message from pp-core ends with a reference code on its own line. This command explains that code. An unknown code usually means it was cut short when it was copied.',
|
|
19
|
+
},
|
|
20
|
+
});
|