@pareto4/pp-core 1.11.0 → 1.12.0
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/README.md +31 -0
- package/agents/pp-code-fixer.md +1 -1
- package/agents/pp-debug-session-manager.md +1 -1
- package/agents/pp-debugger.md +1 -1
- package/agents/pp-executor.md +20 -10
- package/agents/pp-intel-updater.md +1 -1
- package/agents/pp-phase-researcher.md +1 -1
- package/agents/pp-plan-checker.md +5 -5
- package/agents/pp-planner.md +10 -24
- package/agents/pp-project-researcher.md +1 -1
- package/agents/pp-research-synthesizer.md +1 -1
- package/agents/pp-ui-researcher.md +1 -1
- package/agents/pp-verifier.md +8 -4
- package/bin/install.js +429 -82
- package/commands/pp-autonomous.md +8 -6
- package/commands/pp-discovery-to-build.md +1 -1
- package/commands/pp-discovery.md +3 -2
- package/commands/pp-discuss-phase.md +2 -2
- package/commands/pp-execute-phase.md +3 -1
- package/commands/pp-graphify.md +5 -5
- package/commands/pp-import.md +1 -1
- package/commands/pp-map-codebase.md +1 -1
- package/commands/pp-ns-project.md +2 -3
- package/commands/pp-ns-workflow.md +1 -1
- package/commands/pp-plan-phase.md +2 -1
- package/commands/pp-verify-work.md +2 -1
- package/gemini-extension.json +1 -1
- package/hooks/dist/pp-cursor-session-start.js +2 -2
- package/hooks/dist/pp-session-state.sh +1 -1
- package/hooks/dist/pp-statusline.js +21 -2
- package/hooks/pp-cursor-session-start.js +2 -2
- package/hooks/pp-session-state.sh +1 -1
- package/hooks/pp-statusline.js +21 -2
- package/package.json +18 -5
- package/pareto-skills/pp-project-setup/SKILL.md +5 -63
- package/pp-core/bin/detect-build-commands.sh +182 -0
- package/pp-core/bin/lib/capability-registry.cjs +65 -7
- package/pp-core/bin/lib/check-command-router.cjs +79 -38
- package/pp-core/bin/lib/clusters.cjs +2 -3
- package/pp-core/bin/lib/command-aliases.cjs +36 -0
- package/pp-core/bin/lib/command-arg-projection.cjs +99 -1
- package/pp-core/bin/lib/commands.cjs +21 -6
- package/pp-core/bin/lib/compose-bindings.cjs +128 -15
- package/pp-core/bin/lib/config-schema.cjs +16 -0
- package/pp-core/bin/lib/config.cjs +142 -27
- package/pp-core/bin/lib/core-utils.cjs +43 -7
- package/pp-core/bin/lib/discovery-input-check.cjs +218 -0
- package/pp-core/bin/lib/discovery-rebaseline.cjs +1372 -0
- package/pp-core/bin/lib/discovery-research-track.cjs +979 -0
- package/pp-core/bin/lib/discovery-rollup.cjs +476 -0
- package/pp-core/bin/lib/discovery-screens.cjs +1995 -0
- package/pp-core/bin/lib/discovery-sweep.cjs +390 -0
- package/pp-core/bin/lib/discovery-table.cjs +98 -0
- package/pp-core/bin/lib/dispatch-plan.cjs +472 -0
- package/pp-core/bin/lib/frontmatter.cjs +17 -2
- package/pp-core/bin/lib/init-command-router.cjs +8 -5
- package/pp-core/bin/lib/init-contract.cjs +213 -33
- package/pp-core/bin/lib/init-interactive.cjs +20 -1
- package/pp-core/bin/lib/init.cjs +124 -134
- package/pp-core/bin/lib/install-profiles.cjs +5 -0
- package/pp-core/bin/lib/install-run.cjs +536 -0
- package/pp-core/bin/lib/io.cjs +15 -0
- package/pp-core/bin/lib/loop-resolver.cjs +24 -0
- package/pp-core/bin/lib/milestone.cjs +345 -48
- package/pp-core/bin/lib/phase-command-router.cjs +23 -1
- package/pp-core/bin/lib/phase-locator.cjs +15 -6
- package/pp-core/bin/lib/phase-scope.cjs +91 -0
- package/pp-core/bin/lib/phase-tier.cjs +467 -0
- package/pp-core/bin/lib/phase.cjs +247 -21
- package/pp-core/bin/lib/phases-command-router.cjs +2 -0
- package/pp-core/bin/lib/plan-graph.cjs +368 -0
- package/pp-core/bin/lib/planning-workspace.cjs +53 -0
- package/pp-core/bin/lib/platform-bindings.cjs +32 -2
- package/pp-core/bin/lib/profile-output.cjs +11 -3
- package/pp-core/bin/lib/prohibition-enforcement.cjs +112 -0
- package/pp-core/bin/lib/roadmap-parser.cjs +366 -99
- package/pp-core/bin/lib/roadmap.cjs +141 -39
- package/pp-core/bin/lib/runtime-hooks-surface.cjs +1 -1
- package/pp-core/bin/lib/shell-command-projection.cjs +24 -0
- package/pp-core/bin/lib/state-command-router.cjs +82 -13
- package/pp-core/bin/lib/state-document.cjs +124 -7
- package/pp-core/bin/lib/state.cjs +784 -257
- package/pp-core/bin/lib/template.cjs +22 -6
- package/pp-core/bin/lib/vault-command-router.cjs +57 -6
- package/pp-core/bin/lib/vault-practices.cjs +6 -36
- package/pp-core/bin/lib/vault-write-back.cjs +20 -4
- package/pp-core/bin/lib/vault.cjs +555 -23
- package/pp-core/bin/lib/verify.cjs +53 -31
- package/pp-core/bin/lib/workstream.cjs +1 -1
- package/pp-core/bin/lib/worktree-safety.cjs +26 -15
- package/pp-core/bin/portable-timeout.sh +89 -0
- package/pp-core/bin/pp-tools.cjs +158 -8
- package/pp-core/bin/shared/config-defaults.manifest.json +7 -0
- package/pp-core/bin/shared/config-schema.manifest.json +3 -0
- package/pp-core/references/autonomous-best-effort.md +461 -0
- package/pp-core/references/autonomous-lifecycle.md +146 -0
- package/pp-core/references/autonomous-smart-discuss.md +16 -12
- package/pp-core/references/checkpoints.md +1 -1
- package/pp-core/references/discovery-id-conventions.md +202 -0
- package/pp-core/references/discovery-rebaseline.md +254 -0
- package/pp-core/references/discovery-research-track.md +283 -0
- package/pp-core/references/discovery-traceability.md +400 -0
- package/pp-core/references/loop-hook-dispatch.md +7 -0
- package/pp-core/references/mandatory-initial-read.md +1 -1
- package/pp-core/references/planner-gap-closure.md +1 -0
- package/pp-core/references/planner-human-verify-mode.md +1 -1
- package/pp-core/references/planner-load-graph-context.md +1 -1
- package/pp-core/references/planner-plan-graph.md +66 -0
- package/pp-core/references/planner-tdd.md +1 -0
- package/pp-core/references/planning-config.md +8 -0
- package/pp-core/references/sketch-tooling.md +4 -0
- package/pp-core/references/tdd.md +1 -0
- package/pp-core/templates/claude-md.md +1 -1
- package/pp-core/templates/phase-prompt.md +6 -2
- package/pp-core/templates/prototype-review/review.js +303 -0
- package/pp-core/templates/state.md +6 -0
- package/pp-core/workflows/_runtime-launcher.snippet.sh +1 -1
- package/pp-core/workflows/add-backlog.md +1 -1
- package/pp-core/workflows/add-phase.md +1 -1
- package/pp-core/workflows/add-tests.md +1 -1
- package/pp-core/workflows/add-todo.md +1 -1
- package/pp-core/workflows/ai-integration-phase.md +2 -2
- package/pp-core/workflows/audit-fix.md +12 -16
- package/pp-core/workflows/audit-milestone.md +1 -1
- package/pp-core/workflows/audit-uat.md +1 -1
- package/pp-core/workflows/autonomous.md +68 -306
- package/pp-core/workflows/check-todos.md +1 -1
- package/pp-core/workflows/cleanup.md +1 -1
- package/pp-core/workflows/code-review-fix.md +1 -1
- package/pp-core/workflows/code-review.md +19 -7
- package/pp-core/workflows/complete-milestone.md +41 -60
- package/pp-core/workflows/debug.md +1 -1
- package/pp-core/workflows/diagnose-issues.md +1 -1
- package/pp-core/workflows/discovery-sprint/gap-analysis.md +140 -0
- package/pp-core/workflows/discovery-sprint/input-package.md +140 -0
- package/pp-core/workflows/discovery-sprint/prefill.md +92 -0
- package/pp-core/workflows/discovery-sprint/prototype.md +257 -0
- package/pp-core/workflows/discovery-sprint/rebaseline.md +276 -0
- package/pp-core/workflows/discovery-sprint/session-13-gates.md +24 -0
- package/pp-core/workflows/discovery-sprint/sessions/01-kick-off.md +92 -0
- package/pp-core/workflows/discovery-sprint/sessions/02-as-is.md +98 -0
- package/pp-core/workflows/discovery-sprint/sessions/03-to-be.md +215 -0
- package/pp-core/workflows/discovery-sprint/sessions/04-ux-design.md +146 -0
- package/pp-core/workflows/discovery-sprint/sessions/05-architecture.md +98 -0
- package/pp-core/workflows/discovery-sprint/sessions/06-security.md +107 -0
- package/pp-core/workflows/discovery-sprint/sessions/07-identity.md +98 -0
- package/pp-core/workflows/discovery-sprint/sessions/08-integration.md +85 -0
- package/pp-core/workflows/discovery-sprint/sessions/09-qa.md +109 -0
- package/pp-core/workflows/discovery-sprint/sessions/10-infrastructure.md +119 -0
- package/pp-core/workflows/discovery-sprint/sessions/11-roadmap.md +92 -0
- package/pp-core/workflows/discovery-sprint/sessions/12-delivery-model.md +98 -0
- package/pp-core/workflows/discovery-sprint/sessions/13-wrap-up.md +205 -0
- package/pp-core/workflows/discovery-sprint/templates/context-template.md +266 -0
- package/pp-core/workflows/discovery-sprint.md +79 -1714
- package/pp-core/workflows/discovery-to-build/steps/architecture-deliverables.md +5 -5
- package/pp-core/workflows/discovery-to-build/steps/screen-inventory-gate.md +70 -0
- package/pp-core/workflows/discovery-to-build/templates/discovery-references.md +54 -0
- package/pp-core/workflows/discovery-to-build/templates/pp-handoff.md +184 -0
- package/pp-core/workflows/discovery-to-build.md +21 -238
- package/pp-core/workflows/discuss-phase/modes/advisor.md +1 -1
- package/pp-core/workflows/discuss-phase/modes/auto.md +1 -1
- package/pp-core/workflows/discuss-phase/modes/chain.md +1 -1
- package/pp-core/workflows/discuss-phase-assumptions.md +1 -1
- package/pp-core/workflows/discuss-phase.md +9 -2
- package/pp-core/workflows/do.md +4 -4
- package/pp-core/workflows/docs-update.md +1 -1
- package/pp-core/workflows/edit-phase.md +1 -1
- package/pp-core/workflows/eval-review.md +1 -1
- package/pp-core/workflows/execute-phase/steps/checkpoint-handling.md +45 -0
- package/pp-core/workflows/execute-phase/steps/client-context-gate.md +1 -1
- package/pp-core/workflows/execute-phase/steps/codebase-drift-gate.md +1 -1
- package/pp-core/workflows/execute-phase/steps/cross-ai-delegation.md +90 -0
- package/pp-core/workflows/execute-phase/steps/install-run.md +29 -0
- package/pp-core/workflows/execute-phase/steps/phase-close-rebaseline.md +23 -0
- package/pp-core/workflows/execute-phase/steps/post-merge-gate.md +76 -77
- package/pp-core/workflows/execute-phase/steps/serial-group-dispatch.md +72 -0
- package/pp-core/workflows/execute-phase/steps/vault-fetch.md +5 -5
- package/pp-core/workflows/execute-phase/steps/vault-load-gate.md +19 -112
- package/pp-core/workflows/execute-phase.md +102 -216
- package/pp-core/workflows/execute-plan.md +6 -2
- package/pp-core/workflows/explore.md +1 -1
- package/pp-core/workflows/extract-learnings.md +1 -1
- package/pp-core/workflows/forensics.md +4 -4
- package/pp-core/workflows/graduation.md +1 -1
- package/pp-core/workflows/health.md +1 -1
- package/pp-core/workflows/help/modes/brief.md +1 -0
- package/pp-core/workflows/help/modes/default.md +2 -1
- package/pp-core/workflows/help/modes/full.md +12 -5
- package/pp-core/workflows/import.md +1 -1
- package/pp-core/workflows/ingest-docs.md +1 -1
- package/pp-core/workflows/init.md +18 -2
- package/pp-core/workflows/insert-phase.md +1 -1
- package/pp-core/workflows/list-workspaces.md +1 -1
- package/pp-core/workflows/manager.md +3 -3
- package/pp-core/workflows/map-codebase.md +2 -2
- package/pp-core/workflows/milestone-summary.md +4 -4
- package/pp-core/workflows/mvp-phase.md +1 -1
- package/pp-core/workflows/new-milestone.md +7 -8
- package/pp-core/workflows/new-project.md +6 -1
- package/pp-core/workflows/new-workspace.md +1 -1
- package/pp-core/workflows/next.md +2 -2
- package/pp-core/workflows/note.md +2 -2
- package/pp-core/workflows/pause-work.md +1 -1
- package/pp-core/workflows/plan-milestone-gaps.md +1 -1
- package/pp-core/workflows/plan-phase/steps/chunked-planning.md +111 -0
- package/pp-core/workflows/plan-phase/steps/phase-tier.md +34 -0
- package/pp-core/workflows/plan-phase.md +43 -167
- package/pp-core/workflows/plan-review-convergence.md +2 -2
- package/pp-core/workflows/plant-seed.md +1 -1
- package/pp-core/workflows/pr-branch.md +1 -1
- package/pp-core/workflows/profile-user.md +1 -1
- package/pp-core/workflows/progress.md +3 -3
- package/pp-core/workflows/quick.md +3 -1
- package/pp-core/workflows/remove-phase.md +1 -1
- package/pp-core/workflows/remove-workspace.md +1 -1
- package/pp-core/workflows/resume-project.md +6 -12
- package/pp-core/workflows/review.md +1 -1
- package/pp-core/workflows/scan.md +1 -1
- package/pp-core/workflows/secure-phase.md +1 -1
- package/pp-core/workflows/settings-advanced.md +1 -1
- package/pp-core/workflows/settings-integrations.md +1 -1
- package/pp-core/workflows/settings.md +1 -1
- package/pp-core/workflows/ship.md +25 -8
- package/pp-core/workflows/sketch-wrap-up.md +1 -1
- package/pp-core/workflows/sketch.md +2 -1
- package/pp-core/workflows/spec-phase.md +1 -1
- package/pp-core/workflows/spike-wrap-up.md +1 -1
- package/pp-core/workflows/spike.md +1 -1
- package/pp-core/workflows/stats.md +2 -2
- package/pp-core/workflows/thread.md +1 -1
- package/pp-core/workflows/transition.md +5 -9
- package/pp-core/workflows/ui-phase.md +23 -2
- package/pp-core/workflows/ui-review.md +1 -1
- package/pp-core/workflows/ultraplan-phase.md +2 -2
- package/pp-core/workflows/validate-phase.md +1 -1
- package/pp-core/workflows/vault-write-back.md +1 -1
- package/pp-core/workflows/verify-phase.md +29 -26
- package/pp-core/workflows/verify-work.md +14 -10
- package/scripts/base64-scan.sh +7 -41
- package/scripts/changeset/cli.cjs +25 -5
- package/scripts/dev-env.cjs +462 -0
- package/scripts/lib/allowlist-ratchet.cjs +103 -1
- package/scripts/lib/size-budget.cjs +311 -0
- package/scripts/lint-neutrality-names.cjs +694 -0
- package/scripts/lint-org-coordinates.allowlist.json +35 -0
- package/scripts/lint-org-coordinates.cjs +426 -0
- package/scripts/lint-skill-deps.cjs +48 -9
- package/scripts/lint-test-file-count.cjs +17 -2
- package/scripts/prompt-injection-scan.sh +41 -20
- package/scripts/sync-manifest-versions.cjs +41 -7
- package/scripts/sync-runtime-launcher.cjs +106 -1
- package/scripts/update-size-baseline.cjs +274 -30
- package/scripts/workflow-size.cjs +61 -9
- package/commands/pp-build-handoff.md +0 -46
- package/pp-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +0 -94
|
@@ -50,6 +50,7 @@ exports.verifyInitInvariants = verifyInitInvariants;
|
|
|
50
50
|
exports.initSessionNext = initSessionNext;
|
|
51
51
|
exports.initSessionAnswer = initSessionAnswer;
|
|
52
52
|
exports.initSessionReset = initSessionReset;
|
|
53
|
+
exports.armVault = armVault;
|
|
53
54
|
exports.cmdInitContract = cmdInitContract;
|
|
54
55
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
55
56
|
const node_path_1 = __importDefault(require("node:path"));
|
|
@@ -102,6 +103,7 @@ exports.INIT_CONTRACT_FLAGS = Object.freeze([
|
|
|
102
103
|
'--session-answer',
|
|
103
104
|
'--session-reset',
|
|
104
105
|
'--offline',
|
|
106
|
+
'--arm-vault',
|
|
105
107
|
]);
|
|
106
108
|
/** `.planning/.init-session.json` — the CLI-owned session state file
|
|
107
109
|
* (Artifacts table, 34-08). Never hand-edited; malformed content is
|
|
@@ -165,6 +167,14 @@ function getDotted(obj, dottedPath) {
|
|
|
165
167
|
* `optionalAnswer: true` — that flag describes the ANSWER (pressing Enter
|
|
166
168
|
* is a complete, valid response), never whether the question is asked.
|
|
167
169
|
*
|
|
170
|
+
* Question 6 (`org_profile`)'s `preselected` is resolved via
|
|
171
|
+
* `resolveDetectedOrgProfile` — set only when exactly one org-profile-shaped
|
|
172
|
+
* package is installed (INIT-PATCH-C); the prompt text is built from that
|
|
173
|
+
* detected value rather than kept as two hard-coded strings that can drift.
|
|
174
|
+
* `opts._globalRoot` is forwarded to it unchanged, the same test-seam
|
|
175
|
+
* convention `verifyInitInvariants`'s own `_globalRoot` already follows —
|
|
176
|
+
* production callers omit it.
|
|
177
|
+
*
|
|
168
178
|
* Question 7 (`provider`) carries the fixed, complete option list: every
|
|
169
179
|
* value in `PROVIDERS` (imported, never retyped — this is what keeps
|
|
170
180
|
* `azure` from being silently dropped again, T-34-50) plus the three
|
|
@@ -173,7 +183,7 @@ function getDotted(obj, dottedPath) {
|
|
|
173
183
|
* probe reads (pure config + org-profile consultation, no network) — so
|
|
174
184
|
* calling it directly here is equivalent, not a second implementation.
|
|
175
185
|
*/
|
|
176
|
-
function buildInitQuestions(cwd) {
|
|
186
|
+
function buildInitQuestions(cwd, opts = {}) {
|
|
177
187
|
let preselected = git_provider_cjs_1.DEFAULT_PROVIDER;
|
|
178
188
|
try {
|
|
179
189
|
preselected = (0, git_provider_cjs_1.resolveProvider)(cwd).provider;
|
|
@@ -182,6 +192,29 @@ function buildInitQuestions(cwd) {
|
|
|
182
192
|
preselected = git_provider_cjs_1.DEFAULT_PROVIDER;
|
|
183
193
|
}
|
|
184
194
|
const providerOptions = Object.freeze([...git_provider_cjs_1.PROVIDERS, ...IDENTITY_ONLY_PROVIDER_OPTIONS]);
|
|
195
|
+
const detectedOrgProfile = resolveDetectedOrgProfile(cwd, opts);
|
|
196
|
+
// The template example is written as an angle-bracket placeholder, not a
|
|
197
|
+
// literal package name. `tests/org-profile.test.cjs`'s client-confidentiality
|
|
198
|
+
// guard scans this file for any concrete scope-slash-org-prefix package
|
|
199
|
+
// identifier, so even a generic literal placeholder trips it — the template
|
|
200
|
+
// keeps the naming convention visible while staying clean, and the guard
|
|
201
|
+
// stays maximally strict, no allowlist entry was added for it. The detected
|
|
202
|
+
// value itself is never a literal in this file — it is a runtime string
|
|
203
|
+
// read from an installed package, interpolated into the prompt below.
|
|
204
|
+
const orgProfileQuestion = isUsableString(detectedOrgProfile)
|
|
205
|
+
? {
|
|
206
|
+
id: 'org_profile',
|
|
207
|
+
prompt: `Org profile package (optional — a single installed org profile was detected: "${detectedOrgProfile}". ` +
|
|
208
|
+
'Press Enter to use it, type "none" to skip it, or type a different package name.)',
|
|
209
|
+
optionalAnswer: true,
|
|
210
|
+
preselected: detectedOrgProfile,
|
|
211
|
+
preselectedLabel: `${detectedOrgProfile} (detected installed profile)`,
|
|
212
|
+
}
|
|
213
|
+
: {
|
|
214
|
+
id: 'org_profile',
|
|
215
|
+
prompt: 'Org profile package (optional — press Enter to skip, e.g. "@<your-scope>/org-<name>")',
|
|
216
|
+
optionalAnswer: true,
|
|
217
|
+
};
|
|
185
218
|
return [
|
|
186
219
|
{ id: 'project_name', prompt: 'Project name (human-readable, e.g. "Acme Dashboard")', optionalAnswer: false },
|
|
187
220
|
{ id: 'project_slug', prompt: 'Project slug (kebab-case, e.g. "acme-dashboard")', optionalAnswer: false },
|
|
@@ -196,17 +229,7 @@ function buildInitQuestions(cwd) {
|
|
|
196
229
|
optionalAnswer: false,
|
|
197
230
|
},
|
|
198
231
|
{ id: 'description', prompt: 'One-line description (what this project does)', optionalAnswer: false },
|
|
199
|
-
|
|
200
|
-
id: 'org_profile',
|
|
201
|
-
// The example is written as an angle-bracket TEMPLATE, not a literal package
|
|
202
|
-
// name. `tests/org-profile.test.cjs`'s client-confidentiality guard scans this
|
|
203
|
-
// file for any concrete scope-slash-org-prefix package identifier, so even a
|
|
204
|
-
// generic literal placeholder trips it. The template keeps the naming
|
|
205
|
-
// convention visible while staying clean, and the guard stays maximally
|
|
206
|
-
// strict — no allowlist entry was added for it.
|
|
207
|
-
prompt: 'Org profile package (optional — press Enter to skip, e.g. "@<your-scope>/org-<name>")',
|
|
208
|
-
optionalAnswer: true,
|
|
209
|
-
},
|
|
232
|
+
orgProfileQuestion,
|
|
210
233
|
{
|
|
211
234
|
id: 'provider',
|
|
212
235
|
prompt: 'Provider (gitlab / azure / github / other / none)',
|
|
@@ -249,30 +272,47 @@ function resolveGlobalNodeModulesRoot() {
|
|
|
249
272
|
return null;
|
|
250
273
|
}
|
|
251
274
|
}
|
|
275
|
+
/** Conservative npm-package-name shape check (bare `name` or scoped
|
|
276
|
+
* `@scope/name`; lowercase, digits, `.`/`_`/`-`, never leading `.`/`_` in
|
|
277
|
+
* either segment). A directory name that does not match is never offered as
|
|
278
|
+
* a detected default (T-QP-02): it cannot be a resolvable package pointer,
|
|
279
|
+
* so rejecting it here keeps a hostile directory name (control characters,
|
|
280
|
+
* terminal escapes) from ever reaching a rendered prompt. */
|
|
281
|
+
const NPM_PACKAGE_NAME_SHAPE_RE = /^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/;
|
|
252
282
|
/**
|
|
253
|
-
*
|
|
254
|
-
*
|
|
255
|
-
* `ORG_PROFILE_DATA_FILE` at its root
|
|
256
|
-
* `
|
|
257
|
-
*
|
|
258
|
-
* check
|
|
283
|
+
* Walk `nodeModulesDir` exactly like the boolean check below (one level, or
|
|
284
|
+
* one level inside an `@scope/` directory), collecting the name of every
|
|
285
|
+
* package directory carrying `ORG_PROFILE_DATA_FILE` at its root instead of
|
|
286
|
+
* stopping at the first hit. `opts.limit`, when set, stops the walk once
|
|
287
|
+
* that many names have been collected — passing `1` reproduces the boolean
|
|
288
|
+
* check's early-exit behaviour exactly. Bounded at `NODE_MODULES_SCAN_CAP`
|
|
289
|
+
* total directory entries visited (T-34-54: this check must never hang on a
|
|
290
|
+
* pathological `node_modules` tree).
|
|
259
291
|
*
|
|
260
292
|
* This is a heuristic, not a resolution: it answers "a package shaped like
|
|
261
293
|
* an org profile is installed here", not "this exact profile would
|
|
262
|
-
* resolve" —
|
|
294
|
+
* resolve" — callers never claim more than that.
|
|
263
295
|
*/
|
|
264
|
-
function
|
|
296
|
+
function collectOrgProfileShapedPackages(nodeModulesDir, opts = {}) {
|
|
297
|
+
const names = [];
|
|
265
298
|
let visited = 0;
|
|
299
|
+
const limitReached = () => opts.limit !== undefined && names.length >= opts.limit;
|
|
300
|
+
const addIfShaped = (name) => {
|
|
301
|
+
if (NPM_PACKAGE_NAME_SHAPE_RE.test(name))
|
|
302
|
+
names.push(name);
|
|
303
|
+
};
|
|
266
304
|
let entries;
|
|
267
305
|
try {
|
|
268
306
|
entries = node_fs_1.default.readdirSync(nodeModulesDir, { withFileTypes: true });
|
|
269
307
|
}
|
|
270
308
|
catch {
|
|
271
|
-
return false;
|
|
309
|
+
return { names, capped: false };
|
|
272
310
|
}
|
|
273
311
|
for (const entry of entries) {
|
|
312
|
+
if (limitReached())
|
|
313
|
+
return { names, capped: false };
|
|
274
314
|
if (visited >= NODE_MODULES_SCAN_CAP)
|
|
275
|
-
return
|
|
315
|
+
return { names, capped: true };
|
|
276
316
|
visited += 1;
|
|
277
317
|
if (!entry.isDirectory())
|
|
278
318
|
continue;
|
|
@@ -286,14 +326,17 @@ function containsOrgProfileShapedPackage(nodeModulesDir) {
|
|
|
286
326
|
continue;
|
|
287
327
|
}
|
|
288
328
|
for (const scopedEntry of scoped) {
|
|
329
|
+
if (limitReached())
|
|
330
|
+
return { names, capped: false };
|
|
289
331
|
if (visited >= NODE_MODULES_SCAN_CAP)
|
|
290
|
-
return
|
|
332
|
+
return { names, capped: true };
|
|
291
333
|
visited += 1;
|
|
292
334
|
if (!scopedEntry.isDirectory())
|
|
293
335
|
continue;
|
|
294
336
|
try {
|
|
295
|
-
if (node_fs_1.default.existsSync(node_path_1.default.join(scopeDir, scopedEntry.name, org_profile_cjs_1.ORG_PROFILE_DATA_FILE)))
|
|
296
|
-
|
|
337
|
+
if (node_fs_1.default.existsSync(node_path_1.default.join(scopeDir, scopedEntry.name, org_profile_cjs_1.ORG_PROFILE_DATA_FILE))) {
|
|
338
|
+
addIfShaped(`${entry.name}/${scopedEntry.name}`);
|
|
339
|
+
}
|
|
297
340
|
}
|
|
298
341
|
catch {
|
|
299
342
|
continue;
|
|
@@ -302,15 +345,27 @@ function containsOrgProfileShapedPackage(nodeModulesDir) {
|
|
|
302
345
|
}
|
|
303
346
|
else {
|
|
304
347
|
try {
|
|
305
|
-
if (node_fs_1.default.existsSync(node_path_1.default.join(nodeModulesDir, entry.name, org_profile_cjs_1.ORG_PROFILE_DATA_FILE)))
|
|
306
|
-
|
|
348
|
+
if (node_fs_1.default.existsSync(node_path_1.default.join(nodeModulesDir, entry.name, org_profile_cjs_1.ORG_PROFILE_DATA_FILE))) {
|
|
349
|
+
addIfShaped(entry.name);
|
|
350
|
+
}
|
|
307
351
|
}
|
|
308
352
|
catch {
|
|
309
353
|
continue;
|
|
310
354
|
}
|
|
311
355
|
}
|
|
312
356
|
}
|
|
313
|
-
return false;
|
|
357
|
+
return { names, capped: false };
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* True when `nodeModulesDir` contains at least one package directory (one
|
|
361
|
+
* level, or one level inside an `@scope/` directory) carrying a file named
|
|
362
|
+
* `ORG_PROFILE_DATA_FILE` at its root — the same fixed filename
|
|
363
|
+
* `resolveOrgProfileDocument` reads, imported rather than retyped. Reduced to
|
|
364
|
+
* the collecting walk above with `limit: 1`, preserving its early-exit
|
|
365
|
+
* behaviour exactly.
|
|
366
|
+
*/
|
|
367
|
+
function containsOrgProfileShapedPackage(nodeModulesDir) {
|
|
368
|
+
return collectOrgProfileShapedPackages(nodeModulesDir, { limit: 1 }).names.length > 0;
|
|
314
369
|
}
|
|
315
370
|
function orgProfilePackageResolvable(cwd, opts) {
|
|
316
371
|
try {
|
|
@@ -328,6 +383,44 @@ function orgProfilePackageResolvable(cwd, opts) {
|
|
|
328
383
|
return false;
|
|
329
384
|
}
|
|
330
385
|
}
|
|
386
|
+
/**
|
|
387
|
+
* Detect a single, unambiguous installed org-profile-shaped package
|
|
388
|
+
* (INIT-PATCH-C): collects from the project's `node_modules` and — using the
|
|
389
|
+
* same `opts._globalRoot` override convention `orgProfilePackageResolvable`
|
|
390
|
+
* already follows — the global root, de-duplicates by package name across
|
|
391
|
+
* both roots, and returns that single name only when exactly one distinct
|
|
392
|
+
* name was found and NEITHER scan reported `capped`. A capped scan is
|
|
393
|
+
* inconclusive and must never yield a default (T-QP-03). Two or more
|
|
394
|
+
* distinct names, or none, returns null. Never throws.
|
|
395
|
+
*/
|
|
396
|
+
function resolveDetectedOrgProfile(cwd, opts) {
|
|
397
|
+
try {
|
|
398
|
+
const names = new Set();
|
|
399
|
+
let anyCapped = false;
|
|
400
|
+
const projectNodeModules = node_path_1.default.join(cwd, 'node_modules');
|
|
401
|
+
if (node_fs_1.default.existsSync(projectNodeModules)) {
|
|
402
|
+
const projectResult = collectOrgProfileShapedPackages(projectNodeModules);
|
|
403
|
+
if (projectResult.capped)
|
|
404
|
+
anyCapped = true;
|
|
405
|
+
for (const name of projectResult.names)
|
|
406
|
+
names.add(name);
|
|
407
|
+
}
|
|
408
|
+
const globalRoot = opts._globalRoot !== undefined ? opts._globalRoot : resolveGlobalNodeModulesRoot();
|
|
409
|
+
if (globalRoot) {
|
|
410
|
+
const globalResult = collectOrgProfileShapedPackages(globalRoot);
|
|
411
|
+
if (globalResult.capped)
|
|
412
|
+
anyCapped = true;
|
|
413
|
+
for (const name of globalResult.names)
|
|
414
|
+
names.add(name);
|
|
415
|
+
}
|
|
416
|
+
if (anyCapped)
|
|
417
|
+
return null;
|
|
418
|
+
return names.size === 1 ? [...names][0] : null;
|
|
419
|
+
}
|
|
420
|
+
catch {
|
|
421
|
+
return null;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
331
424
|
/** Read `.planning/config.json` at `cwd`, distinguishing "file absent" from
|
|
332
425
|
* "file present but empty/malformed" — `INIT_IDENTITY_MISSING` only fires
|
|
333
426
|
* on the latter (see `verifyInitInvariants`'s own doc comment for why an
|
|
@@ -654,7 +747,7 @@ function computeCurrentStep(cwd, session, opts) {
|
|
|
654
747
|
// the convention: any failure anywhere in this body degrades to a
|
|
655
748
|
// `write_failed` verdict instead of propagating.
|
|
656
749
|
try {
|
|
657
|
-
const questions = buildInitQuestions(cwd);
|
|
750
|
+
const questions = buildInitQuestions(cwd, { _globalRoot: opts._globalRoot });
|
|
658
751
|
for (const q of questions) {
|
|
659
752
|
if (!Object.prototype.hasOwnProperty.call(session.answers, q.id)) {
|
|
660
753
|
return { kind: 'question', question: q };
|
|
@@ -678,14 +771,18 @@ function computeCurrentStep(cwd, session, opts) {
|
|
|
678
771
|
// end states and must record (and later report) distinct decisions,
|
|
679
772
|
// never sharing the inert-path (INIT-02) message below.
|
|
680
773
|
if (dryRun.composable === true && dryRun.action !== 'unchanged') {
|
|
774
|
+
// Pack 6 D-07: `info` is display-only (the CLI and the TTY renderer
|
|
775
|
+
// print it); approval re-composes with `write: true`, so the written
|
|
776
|
+
// file keeps the RAW coordinates.
|
|
777
|
+
const shown = (0, compose_bindings_cjs_1.redactComposeResultForEmission)(dryRun);
|
|
681
778
|
return {
|
|
682
779
|
kind: 'compose_confirm',
|
|
683
780
|
info: {
|
|
684
|
-
provenance:
|
|
685
|
-
drift:
|
|
781
|
+
provenance: shown.provenance,
|
|
782
|
+
drift: shown.drift,
|
|
686
783
|
action: dryRun.action,
|
|
687
|
-
composed:
|
|
688
|
-
warnings:
|
|
784
|
+
composed: shown.composed,
|
|
785
|
+
warnings: shown.warnings,
|
|
689
786
|
},
|
|
690
787
|
};
|
|
691
788
|
}
|
|
@@ -927,6 +1024,81 @@ function initSessionReset(cwd) {
|
|
|
927
1024
|
}
|
|
928
1025
|
return { ok: true };
|
|
929
1026
|
}
|
|
1027
|
+
/**
|
|
1028
|
+
* `init-contract --arm-vault`: turn the client vault on for a project whose
|
|
1029
|
+
* client vault is bound. Writes `vault.enabled: true` ONLY when the key is
|
|
1030
|
+
* absent — an explicit `false` (or any other explicit value) is the
|
|
1031
|
+
* operator's decision and is kept. With no client vault bound (no or
|
|
1032
|
+
* malformed `.platform-bindings.json`, or `client.vaultRepo` /
|
|
1033
|
+
* `client.vaultRef` missing — the same "bound" the gate's verdict uses) it
|
|
1034
|
+
* writes nothing and returns `ok: false` naming /pp-init. A present but
|
|
1035
|
+
* unparseable `.planning/config.json` is never overwritten. Never throws.
|
|
1036
|
+
* The reported coordinate is display-safe (D-07).
|
|
1037
|
+
*/
|
|
1038
|
+
function armVault(cwd) {
|
|
1039
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
1040
|
+
const vaultMod = require('./vault.cjs');
|
|
1041
|
+
let binding = { bound: false, repo: null, ref: null };
|
|
1042
|
+
try {
|
|
1043
|
+
binding = vaultMod.readClientBinding(cwd);
|
|
1044
|
+
}
|
|
1045
|
+
catch {
|
|
1046
|
+
/* unbound */
|
|
1047
|
+
}
|
|
1048
|
+
const client = { repo: vaultMod.redactCloneTarget(binding.repo), ref: binding.ref };
|
|
1049
|
+
const configPath = node_path_1.default.join(cwd, '.planning', 'config.json');
|
|
1050
|
+
let cfg = {};
|
|
1051
|
+
let cfgExists = false;
|
|
1052
|
+
try {
|
|
1053
|
+
cfgExists = node_fs_1.default.existsSync(configPath);
|
|
1054
|
+
if (cfgExists) {
|
|
1055
|
+
const parsed = JSON.parse(node_fs_1.default.readFileSync(configPath, 'utf-8'));
|
|
1056
|
+
if (!isObject(parsed))
|
|
1057
|
+
throw new Error('not an object');
|
|
1058
|
+
cfg = parsed;
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
catch {
|
|
1062
|
+
return {
|
|
1063
|
+
ok: false,
|
|
1064
|
+
armed: false,
|
|
1065
|
+
enabled: undefined,
|
|
1066
|
+
client,
|
|
1067
|
+
message: '.planning/config.json is not valid JSON — fix it (or re-run /pp-init), then arm the vault again. Nothing was written.',
|
|
1068
|
+
};
|
|
1069
|
+
}
|
|
1070
|
+
const vaultSection = isObject(cfg.vault) ? cfg.vault : null;
|
|
1071
|
+
const nested = vaultSection && Object.prototype.hasOwnProperty.call(vaultSection, 'enabled');
|
|
1072
|
+
const flat = Object.prototype.hasOwnProperty.call(cfg, 'vault.enabled');
|
|
1073
|
+
const current = nested && vaultSection ? vaultSection.enabled : flat ? cfg['vault.enabled'] : undefined;
|
|
1074
|
+
if (!binding.bound) {
|
|
1075
|
+
return {
|
|
1076
|
+
ok: false,
|
|
1077
|
+
armed: false,
|
|
1078
|
+
enabled: current,
|
|
1079
|
+
client,
|
|
1080
|
+
message: 'No client vault is bound — .platform-bindings.json needs client.vaultRepo and client.vaultRef. Run /pp-init to bind it; vault.enabled was not changed.',
|
|
1081
|
+
};
|
|
1082
|
+
}
|
|
1083
|
+
if (nested || flat) {
|
|
1084
|
+
return {
|
|
1085
|
+
ok: true,
|
|
1086
|
+
armed: false,
|
|
1087
|
+
enabled: current,
|
|
1088
|
+
client,
|
|
1089
|
+
message: current === true
|
|
1090
|
+
? 'vault.enabled is already true — nothing to do.'
|
|
1091
|
+
: `vault.enabled is explicitly set to ${JSON.stringify(current)} — kept as the operator's choice.`,
|
|
1092
|
+
};
|
|
1093
|
+
}
|
|
1094
|
+
try {
|
|
1095
|
+
writeConfigMerge(cwd, { vault: { enabled: true } });
|
|
1096
|
+
}
|
|
1097
|
+
catch (err) {
|
|
1098
|
+
return { ok: false, armed: false, enabled: undefined, client, message: writeFailedMessage(err) };
|
|
1099
|
+
}
|
|
1100
|
+
return { ok: true, armed: true, enabled: true, client, message: 'vault.enabled set to true (it was absent).' };
|
|
1101
|
+
}
|
|
930
1102
|
// ─── cmdInitContract ────────────────────────────────────────────────────────
|
|
931
1103
|
/**
|
|
932
1104
|
* CLI adapter for `pp-tools query init-contract`.
|
|
@@ -954,6 +1126,14 @@ function initSessionReset(cwd) {
|
|
|
954
1126
|
* convention as `--verify`.
|
|
955
1127
|
*/
|
|
956
1128
|
function cmdInitContract(cwd, options, raw) {
|
|
1129
|
+
if (options.armVault === true) {
|
|
1130
|
+
const result = armVault(cwd);
|
|
1131
|
+
output(result, raw);
|
|
1132
|
+
if (!result.ok) {
|
|
1133
|
+
process.exitCode = 1;
|
|
1134
|
+
}
|
|
1135
|
+
return;
|
|
1136
|
+
}
|
|
957
1137
|
if (options.sessionReset === true) {
|
|
958
1138
|
const result = initSessionReset(cwd);
|
|
959
1139
|
output(result, raw);
|
|
@@ -168,7 +168,26 @@ async function runInitInteractive(cwd, prompter, opts = {}) {
|
|
|
168
168
|
preselectedLabel: state.preselectedLabel,
|
|
169
169
|
optionalAnswer: state.optionalAnswer,
|
|
170
170
|
});
|
|
171
|
-
|
|
171
|
+
// INIT-PATCH-C: Enter accepts a carried default, "none" clears it.
|
|
172
|
+
// Scoped to an OPTIONAL step carrying a usable `preselected` — today
|
|
173
|
+
// that is only `org_profile` when exactly one org-profile-shaped
|
|
174
|
+
// package was detected. `provider` also carries a `preselected` but is
|
|
175
|
+
// NOT `optionalAnswer`, so the `state.optionalAnswer === true` guard
|
|
176
|
+
// below leaves it untouched: a blank answer there still falls through
|
|
177
|
+
// to `initSessionAnswer` verbatim, which still rejects it and re-asks
|
|
178
|
+
// exactly as today.
|
|
179
|
+
const hasUsableDefault = state.optionalAnswer === true && typeof state.preselected === 'string' && state.preselected.trim() !== '';
|
|
180
|
+
let submittedAnswer = answer;
|
|
181
|
+
if (hasUsableDefault) {
|
|
182
|
+
const trimmedLower = answer.trim().toLowerCase();
|
|
183
|
+
if (answer.trim() === '') {
|
|
184
|
+
submittedAnswer = state.preselected;
|
|
185
|
+
}
|
|
186
|
+
else if (trimmedLower === 'none') {
|
|
187
|
+
submittedAnswer = '';
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
const result = (0, init_contract_cjs_1.initSessionAnswer)(cwd, stepId, submittedAnswer, sessionOpts);
|
|
172
191
|
if (result.ok) {
|
|
173
192
|
if (stepId === 'compose_confirm') {
|
|
174
193
|
wroteBinding = readBindingsContent(cwd) !== preBindings;
|