@phyzess/pappra 0.1.0-beta.13 → 0.1.0-beta.18
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/README.md +134 -15
- package/assets/integrations/skills/pappra/SKILL.md +28 -7
- package/assets/integrations/skills/pappra/references/collaboration.md +633 -0
- package/assets/integrations/skills/pappra/references/environment.md +360 -0
- package/assets/integrations/skills/pappra/references/hosts.md +20 -0
- package/assets/integrations/skills/pappra/references/operations.md +36 -5
- package/dist/adapters/pi/recovery-extension.d.ts +65 -0
- package/dist/adapters/pi/recovery-extension.js +238 -0
- package/dist/adapters/pi/session-transition-extension.d.ts +30 -0
- package/dist/adapters/pi/session-transition-extension.js +265 -0
- package/dist/app/automatic-handoff-events.d.ts +26 -0
- package/dist/app/automatic-handoff-events.js +134 -0
- package/dist/app/bounded-file.d.ts +2 -0
- package/dist/app/bounded-file.js +2 -0
- package/dist/app/capability-evidence/claude.d.ts +37 -0
- package/dist/app/capability-evidence/claude.js +206 -0
- package/dist/app/capability-evidence/codex.d.ts +35 -0
- package/dist/app/capability-evidence/codex.js +193 -0
- package/dist/app/capability-evidence/index.d.ts +33 -0
- package/dist/app/capability-evidence/index.js +117 -0
- package/dist/app/capability-evidence/pi.d.ts +73 -0
- package/dist/app/capability-evidence/pi.js +669 -0
- package/dist/app/capability-evidence/shared.d.ts +69 -0
- package/dist/app/capability-evidence/shared.js +195 -0
- package/dist/app/collab-auto-handoff.d.ts +55 -0
- package/dist/app/collab-auto-handoff.js +384 -0
- package/dist/app/collab-bindings.d.ts +57 -0
- package/dist/app/collab-bindings.js +1006 -0
- package/dist/app/collab-bootstrap-transport.d.ts +163 -0
- package/dist/app/collab-bootstrap-transport.js +769 -0
- package/dist/app/collab-bootstrap.d.ts +137 -0
- package/dist/app/collab-bootstrap.js +3455 -0
- package/dist/app/collab-handoff.d.ts +12 -0
- package/dist/app/collab-handoff.js +511 -0
- package/dist/app/collab-messages.d.ts +2 -0
- package/dist/app/collab-messages.js +2 -0
- package/dist/app/collab-notify.d.ts +156 -0
- package/dist/app/collab-notify.js +1208 -0
- package/dist/app/collab-preflight.d.ts +34 -0
- package/dist/app/collab-preflight.js +96 -0
- package/dist/app/collab-recovery-executor.d.ts +161 -0
- package/dist/app/collab-recovery-executor.js +1153 -0
- package/dist/app/collab-recovery-projection.d.ts +103 -0
- package/dist/app/collab-recovery-projection.js +659 -0
- package/dist/app/collab-recovery.d.ts +25 -0
- package/dist/app/collab-recovery.js +429 -0
- package/dist/app/collab-resources.d.ts +16 -0
- package/dist/app/collab-resources.js +278 -0
- package/dist/app/collab-role-events.d.ts +22 -0
- package/dist/app/collab-role-events.js +329 -0
- package/dist/app/collab-role-orchestration.d.ts +53 -0
- package/dist/app/collab-role-orchestration.js +601 -0
- package/dist/app/collab-role-plan.d.ts +81 -0
- package/dist/app/collab-role-plan.js +504 -0
- package/dist/app/collab-role-repair.d.ts +109 -0
- package/dist/app/collab-role-repair.js +821 -0
- package/dist/app/collab-rollover.d.ts +263 -0
- package/dist/app/collab-rollover.js +1112 -0
- package/dist/app/collab-route.d.ts +64 -0
- package/dist/app/collab-route.js +240 -0
- package/dist/app/collab-session-transition.d.ts +221 -0
- package/dist/app/collab-session-transition.js +3334 -0
- package/dist/app/collab-state.d.ts +7 -0
- package/dist/app/collab-state.js +162 -0
- package/dist/app/collab-task-store.d.ts +22 -0
- package/dist/app/collab-task-store.js +204 -0
- package/dist/app/collab-work-execution.d.ts +127 -0
- package/dist/app/collab-work-execution.js +1027 -0
- package/dist/app/collab-work-plan.d.ts +87 -0
- package/dist/app/collab-work-plan.js +437 -0
- package/dist/app/collaboration.d.ts +8 -2
- package/dist/app/collaboration.js +101 -12
- package/dist/app/doctor.js +108 -5
- package/dist/app/host-mcp.js +34 -8
- package/dist/app/host-probe.js +4 -4
- package/dist/app/init.d.ts +58 -4
- package/dist/app/init.js +768 -155
- package/dist/app/managed-pi-bootstrap-admission.d.ts +14 -0
- package/dist/app/managed-pi-bootstrap-admission.js +104 -0
- package/dist/app/managed-pi-bootstrap-refresh.d.ts +10 -0
- package/dist/app/managed-pi-bootstrap-refresh.js +78 -0
- package/dist/app/managed-pi-delivery.d.ts +8 -0
- package/dist/app/managed-pi-delivery.js +110 -0
- package/dist/app/managed-pi-handoff-receiver.d.ts +15 -0
- package/dist/app/managed-pi-handoff-receiver.js +263 -0
- package/dist/app/managed-pi-handoff-reference.d.ts +3 -0
- package/dist/app/managed-pi-handoff-reference.js +60 -0
- package/dist/app/managed-pi-herdr.d.ts +21 -0
- package/dist/app/managed-pi-herdr.js +185 -0
- package/dist/app/managed-pi-initial-admission.d.ts +28 -0
- package/dist/app/managed-pi-initial-admission.js +416 -0
- package/dist/app/managed-pi-initial-launch.d.ts +5 -0
- package/dist/app/managed-pi-initial-launch.js +61 -0
- package/dist/app/managed-pi-native-observation.d.ts +36 -0
- package/dist/app/managed-pi-native-observation.js +191 -0
- package/dist/app/managed-pi-readiness.d.ts +29 -0
- package/dist/app/managed-pi-readiness.js +288 -0
- package/dist/app/managed-pi-repair-stop.d.ts +21 -0
- package/dist/app/managed-pi-repair-stop.js +371 -0
- package/dist/app/managed-pi-resume-admission.d.ts +32 -0
- package/dist/app/managed-pi-resume-admission.js +618 -0
- package/dist/app/managed-pi-retirement.d.ts +28 -0
- package/dist/app/managed-pi-retirement.js +112 -0
- package/dist/app/managed-pi-sdk-control.d.ts +7 -0
- package/dist/app/managed-pi-sdk-control.js +169 -0
- package/dist/app/managed-pi-sdk-runtime.d.ts +3 -0
- package/dist/app/managed-pi-sdk-runtime.js +483 -0
- package/dist/app/managed-pi-source-admission.d.ts +14 -0
- package/dist/app/managed-pi-source-admission.js +323 -0
- package/dist/app/managed-pi-transition-host.d.ts +36 -0
- package/dist/app/managed-pi-transition-host.js +292 -0
- package/dist/app/managed-pi.d.ts +54 -0
- package/dist/app/managed-pi.js +248 -0
- package/dist/app/shell-readiness.d.ts +66 -0
- package/dist/app/shell-readiness.js +368 -0
- package/dist/app/task-pin.d.ts +2 -23
- package/dist/app/task-pin.js +2 -113
- package/dist/cli/commands/collaboration-binding.d.ts +13 -0
- package/dist/cli/commands/collaboration-binding.js +118 -0
- package/dist/cli/commands/collaboration-bootstrap.d.ts +10 -0
- package/dist/cli/commands/collaboration-bootstrap.js +84 -0
- package/dist/cli/commands/collaboration-capabilities.d.ts +13 -0
- package/dist/cli/commands/collaboration-capabilities.js +127 -0
- package/dist/cli/commands/collaboration-handoff.d.ts +13 -0
- package/dist/cli/commands/collaboration-handoff.js +102 -0
- package/dist/cli/commands/collaboration-message.d.ts +17 -0
- package/dist/cli/commands/collaboration-message.js +177 -0
- package/dist/cli/commands/collaboration-plan.d.ts +8 -0
- package/dist/cli/commands/collaboration-plan.js +108 -0
- package/dist/cli/commands/collaboration-preflight.d.ts +10 -0
- package/dist/cli/commands/collaboration-preflight.js +167 -0
- package/dist/cli/commands/collaboration-recovery-run.d.ts +46 -0
- package/dist/cli/commands/collaboration-recovery-run.js +224 -0
- package/dist/cli/commands/collaboration-recovery.d.ts +11 -0
- package/dist/cli/commands/collaboration-recovery.js +104 -0
- package/dist/cli/commands/collaboration-resources.d.ts +11 -0
- package/dist/cli/commands/collaboration-resources.js +96 -0
- package/dist/cli/commands/collaboration-role-plan.d.ts +9 -0
- package/dist/cli/commands/collaboration-role-plan.js +69 -0
- package/dist/cli/commands/collaboration-role-run.d.ts +11 -0
- package/dist/cli/commands/collaboration-role-run.js +128 -0
- package/dist/cli/commands/collaboration-rollover.d.ts +10 -0
- package/dist/cli/commands/collaboration-rollover.js +103 -0
- package/dist/cli/commands/collaboration-state.d.ts +10 -0
- package/dist/cli/commands/collaboration-state.js +96 -0
- package/dist/cli/commands/collaboration-transition.d.ts +10 -0
- package/dist/cli/commands/collaboration-transition.js +75 -0
- package/dist/cli/commands/collaboration-work-run.d.ts +34 -0
- package/dist/cli/commands/collaboration-work-run.js +178 -0
- package/dist/cli/commands/collaboration.d.ts +4 -0
- package/dist/cli/commands/collaboration.js +39 -4
- package/dist/cli/commands/continuity.js +2 -6
- package/dist/cli/commands/docs.d.ts +2 -0
- package/dist/cli/commands/docs.js +89 -15
- package/dist/cli/commands/host.d.ts +7 -1
- package/dist/cli/commands/host.js +122 -7
- package/dist/cli/commands/init.d.ts +20 -3
- package/dist/cli/commands/init.js +897 -232
- package/dist/cli/commands/mcp.d.ts +1 -0
- package/dist/cli/commands/mcp.js +11 -5
- package/dist/cli/commands/repair.d.ts +12 -0
- package/dist/cli/commands/repair.js +41 -0
- package/dist/cli/commands/runtime.d.ts +1 -1
- package/dist/cli/commands/runtime.js +139 -38
- package/dist/cli/commands/session.d.ts +7 -0
- package/dist/cli/commands/session.js +82 -0
- package/dist/cli/commands/task.js +19 -5
- package/dist/cli/run.js +866 -24
- package/dist/code/identity.d.ts +7 -0
- package/dist/code/identity.js +7 -3
- package/dist/code/registration.js +7 -0
- package/dist/code/service.d.ts +9 -0
- package/dist/code/service.js +16 -3
- package/dist/code/text.js +19 -7
- package/dist/collab/admission.d.ts +91 -66
- package/dist/collab/admission.js +21 -6
- package/dist/collab/canonical.d.ts +1 -1
- package/dist/collab/canonical.js +44 -3
- package/dist/collab/envelope.d.ts +2 -2
- package/dist/collab/envelope.js +12 -12
- package/dist/collab/first-work.d.ts +3 -0
- package/dist/collab/first-work.js +15 -0
- package/dist/collab/handoff.d.ts +16 -0
- package/dist/collab/handoff.js +39 -5
- package/dist/collab/index.d.ts +97 -1
- package/dist/collab/index.js +9 -1
- package/dist/collab/messages.js +126 -3
- package/dist/collab/persisted-messages.d.ts +123 -0
- package/dist/collab/persisted-messages.js +496 -0
- package/dist/collab/preflight.d.ts +1 -1
- package/dist/collab/resources.d.ts +13 -3
- package/dist/collab/resources.js +37 -6
- package/dist/collab/rollover.d.ts +311 -0
- package/dist/collab/rollover.js +1024 -0
- package/dist/collab/store.d.ts +1 -0
- package/dist/collab/store.js +1 -4
- package/dist/collab/types.d.ts +2 -354
- package/dist/collab/validation.d.ts +2 -10
- package/dist/collab/validation.js +2 -303
- package/dist/collab/work-plan.d.ts +105 -0
- package/dist/collab/work-plan.js +843 -0
- package/dist/continuity/context.d.ts +11 -2
- package/dist/continuity/context.js +100 -13
- package/dist/continuity/handoff-bundle.d.ts +296 -0
- package/dist/continuity/handoff-bundle.js +477 -0
- package/dist/continuity/index.d.ts +2 -0
- package/dist/continuity/index.js +1 -0
- package/dist/continuity/paths.js +10 -2
- package/dist/continuity/service.js +91 -15
- package/dist/continuity/task-pin.d.ts +24 -0
- package/dist/continuity/task-pin.js +590 -0
- package/dist/doctor.d.ts +1 -0
- package/dist/doctor.js +6 -0
- package/dist/experimental/index.d.ts +13 -0
- package/dist/experimental/index.js +25 -0
- package/dist/experimental/pi.d.ts +4 -0
- package/dist/experimental/pi.js +12 -0
- package/dist/hosts/claude/native.js +1 -1
- package/dist/hosts/codex/native.js +1 -1
- package/dist/hosts/codex/protocol.js +1 -1
- package/dist/hosts/integration/bootstrap-config.d.ts +58 -0
- package/dist/hosts/integration/bootstrap-config.js +254 -0
- package/dist/hosts/integration/bundle-export.d.ts +44 -0
- package/dist/hosts/integration/bundle-export.js +366 -3
- package/dist/hosts/integration/bundle.js +3 -0
- package/dist/hosts/integration/hook.js +36 -9
- package/dist/hosts/integration/pappra-bootstrap-extension.mjs +80 -0
- package/dist/hosts/integration/pappra-extension-runtime.mjs +59 -24631
- package/dist/hosts/integration/pappra-hook.mjs +61 -24749
- package/dist/hosts/integration/pappra-recovery-extension.mjs +259 -0
- package/dist/hosts/integration/pappra-session-transition-extension.mjs +276 -0
- package/dist/hosts/integration/pi-bootstrap-extension.d.ts +36 -0
- package/dist/hosts/integration/pi-bootstrap-extension.js +101 -0
- package/dist/hosts/integration/pi-bootstrap-guard.d.ts +125 -0
- package/dist/hosts/integration/pi-bootstrap-guard.js +438 -0
- package/dist/hosts/integration/pi-bootstrap-runtime.d.ts +63 -0
- package/dist/hosts/integration/pi-bootstrap-runtime.js +223 -0
- package/dist/hosts/integration/pi-native-events.d.ts +112 -0
- package/dist/hosts/integration/pi-native-events.js +348 -0
- package/dist/hosts/integration/pi-rollover-extension.d.ts +166 -0
- package/dist/hosts/integration/pi-rollover-extension.js +431 -0
- package/dist/hosts/integration/recovery-config.d.ts +48 -0
- package/dist/hosts/integration/recovery-config.js +117 -0
- package/dist/hosts/integration/service.d.ts +6 -1
- package/dist/hosts/integration/service.js +485 -35
- package/dist/hosts/integration/support.d.ts +7 -0
- package/dist/hosts/integration/support.js +9 -1
- package/dist/hosts/integration/templates.d.ts +3 -0
- package/dist/hosts/integration/templates.js +26 -3
- package/dist/hosts/pi/managed-host-identity.d.ts +2 -0
- package/dist/hosts/pi/managed-host-identity.js +40 -0
- package/dist/hosts/pi/managed-provider-diagnostics.d.ts +6 -0
- package/dist/hosts/pi/managed-provider-diagnostics.js +184 -0
- package/dist/hosts/pi/managed-sdk-session.d.ts +335 -0
- package/dist/hosts/pi/managed-sdk-session.js +1639 -0
- package/dist/hosts/pi/managed-session-extension.d.ts +20 -0
- package/dist/hosts/pi/managed-session-extension.js +84 -0
- package/dist/hosts/pi/managed-session.d.ts +177 -0
- package/dist/hosts/pi/managed-session.js +1557 -0
- package/dist/hosts/pi/protocol.js +1 -1
- package/dist/hosts/pi/query-result.js +1 -1
- package/dist/hosts/pi/resume-capability.d.ts +41 -0
- package/dist/hosts/pi/resume-capability.js +33 -0
- package/dist/hosts/pi/rpc.d.ts +182 -134
- package/dist/hosts/pi/rpc.js +230 -37
- package/dist/hosts/pilot.d.ts +97 -74
- package/dist/hosts/pilot.js +5 -1
- package/dist/hosts/version.d.ts +3 -0
- package/dist/hosts/version.js +18 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/infra/bounded-file.d.ts +19 -0
- package/dist/infra/bounded-file.js +66 -0
- package/dist/infra/fs.js +10 -2
- package/dist/infra/git.d.ts +29 -0
- package/dist/infra/git.js +45 -9
- package/dist/infra/json-property.d.ts +2 -0
- package/dist/infra/json-property.js +69 -0
- package/dist/infra/json.d.ts +4 -0
- package/dist/infra/json.js +24 -0
- package/dist/infra/owned-process-group.js +1 -1
- package/dist/infra/private-lock.d.ts +16 -0
- package/dist/infra/private-lock.js +148 -0
- package/dist/infra/private-state.d.ts +7 -0
- package/dist/infra/private-state.js +146 -0
- package/dist/infra/process-identity.d.ts +12 -0
- package/dist/infra/process-identity.js +142 -0
- package/dist/infra/validation.d.ts +5 -0
- package/dist/infra/validation.js +15 -0
- package/dist/init.d.ts +1 -0
- package/dist/init.js +6 -0
- package/dist/install/apply.d.ts +9 -1
- package/dist/install/apply.js +176 -12
- package/dist/install/dependencies.d.ts +38 -0
- package/dist/install/dependencies.js +282 -0
- package/dist/install/ownership.js +30 -1
- package/dist/install/references.d.ts +31 -0
- package/dist/install/references.js +208 -0
- package/dist/install/repair.d.ts +37 -0
- package/dist/install/repair.js +798 -0
- package/dist/install/reset-local.d.ts +16 -0
- package/dist/install/reset-local.js +304 -0
- package/dist/install/rollback.d.ts +3 -0
- package/dist/install/rollback.js +9 -3
- package/dist/install/runtime.d.ts +10 -6
- package/dist/install/runtime.js +299 -59
- package/dist/install/source-fingerprint.d.ts +2 -0
- package/dist/install/source-fingerprint.js +103 -0
- package/dist/knowledge/catalog.d.ts +1 -0
- package/dist/knowledge/catalog.js +143 -10
- package/dist/knowledge/journal.js +17 -7
- package/dist/knowledge/paths.js +2 -1
- package/dist/knowledge/refs.js +4 -0
- package/dist/knowledge/register-batch.d.ts +4 -0
- package/dist/knowledge/register-batch.js +225 -0
- package/dist/knowledge/relocate.d.ts +5 -0
- package/dist/knowledge/relocate.js +92 -0
- package/dist/knowledge/scan.js +103 -47
- package/dist/knowledge/service.d.ts +3 -0
- package/dist/knowledge/service.js +33 -1
- package/dist/knowledge/write.d.ts +4 -1
- package/dist/knowledge/write.js +1 -1
- package/dist/mcp/server.d.ts +4 -2
- package/dist/mcp/server.js +87 -136
- package/dist/schemas/collaboration-state-validation.d.ts +6 -0
- package/dist/schemas/collaboration-state-validation.js +342 -0
- package/dist/schemas/collaboration-state.d.ts +436 -0
- package/dist/schemas/collaboration-state.js +1 -0
- package/dist/schemas/collaboration.d.ts +335 -0
- package/dist/schemas/collaboration.js +272 -0
- package/dist/schemas/continuity.d.ts +8 -0
- package/dist/schemas/continuity.js +13 -3
- package/dist/schemas/doctor.d.ts +1 -0
- package/dist/schemas/doctor.js +1 -0
- package/dist/schemas/document.d.ts +61 -0
- package/dist/schemas/document.js +9 -0
- package/dist/schemas/errors.d.ts +1 -0
- package/dist/schemas/errors.js +1 -0
- package/dist/schemas/host.d.ts +24 -0
- package/dist/schemas/host.js +19 -0
- package/dist/schemas/install.d.ts +8 -2
- package/dist/schemas/install.js +11 -1
- package/dist/schemas/managed-pi-execution.d.ts +16 -0
- package/dist/schemas/managed-pi-execution.js +2 -0
- package/dist/schemas/outcome.d.ts +9 -5
- package/dist/workspace/config-file.js +1 -3
- package/dist/workspace/documents.js +12 -2
- package/dist/workspace/repositories.d.ts +7 -0
- package/dist/workspace/repositories.js +14 -5
- package/package.json +35 -35
- package/skills/pappra/SKILL.md +28 -7
- package/skills/pappra/references/collaboration.md +633 -0
- package/skills/pappra/references/environment.md +360 -0
- package/skills/pappra/references/hosts.md +20 -0
- package/skills/pappra/references/operations.md +36 -5
- package/dist/app/collab-from.d.ts +0 -35
- package/dist/app/collab-from.js +0 -84
|
@@ -1,18 +1,54 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { join, resolve } from 'node:path';
|
|
2
2
|
import * as prompts from '@clack/prompts';
|
|
3
3
|
import { diagnosticCodes } from '../../schemas/errors.js';
|
|
4
4
|
import { INIT_CHOICES_SCHEMA, parseInitChoices, } from '../../schemas/init.js';
|
|
5
5
|
import { readTextFileBounded } from '../../infra/fs.js';
|
|
6
|
-
import { getPackageInfo } from '../../package-info.js';
|
|
7
|
-
import { installRuntime } from '../../install/runtime.js';
|
|
8
|
-
import { applyInitPlan, bindAndRecordRuntime, ensureDefaultRuntime, inspectInitContext, listKnowledgeCandidates, deriveInitChoices, listRepositoryCandidates, planWorkspaceInit, } from '../../app/init.js';
|
|
6
|
+
import { getPackageInfo, getPackageRoot } from '../../package-info.js';
|
|
7
|
+
import { installRuntime, runtimePaths } from '../../install/runtime.js';
|
|
8
|
+
import { applyInitPlan, bindAndRecordRuntime, ensureDefaultRuntime, inspectInitContext, listKnowledgeCandidates, deriveCommonInitDefaults, deriveInitChoices, existingInitChoices, listRepositoryCandidates, validateInitField, inspectInitPathAddition, planWorkspaceInit, } from '../../app/init.js';
|
|
9
|
+
const addExistingRepositoryAction = '../__pappra_add_existing_repository_action__';
|
|
10
|
+
const addExistingKnowledgeAction = '../__pappra_add_existing_knowledge_action__';
|
|
11
|
+
const collectedPathObservations = new WeakMap();
|
|
12
|
+
function initPathObservationKey(kind, path) {
|
|
13
|
+
return `${kind}:${path}`;
|
|
14
|
+
}
|
|
15
|
+
function repositoryCandidateFromPathObservation(observation) {
|
|
16
|
+
if (observation.kind !== 'repository' || observation.git === null) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
key: observation.path,
|
|
21
|
+
id: null,
|
|
22
|
+
external: false,
|
|
23
|
+
access: 'read',
|
|
24
|
+
root: observation.git.root,
|
|
25
|
+
kind: observation.git.kind,
|
|
26
|
+
dirty: observation.git.dirty,
|
|
27
|
+
configured: false,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function knowledgeCandidateFromPathObservation(observation) {
|
|
31
|
+
if (observation.kind !== 'knowledge')
|
|
32
|
+
return null;
|
|
33
|
+
return {
|
|
34
|
+
path: observation.path,
|
|
35
|
+
kind: observation.targetKind,
|
|
36
|
+
origin: 'explicit-path',
|
|
37
|
+
repositoryId: null,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
9
40
|
export const clackWizardUi = {
|
|
10
41
|
intro: (message) => prompts.intro(message),
|
|
11
42
|
outro: (message) => prompts.outro(message),
|
|
12
43
|
note: (message, title) => prompts.note(message, title),
|
|
13
|
-
text: async (message, initialValue) => prompts.text({
|
|
44
|
+
text: async (message, initialValue, validate) => prompts.text({
|
|
14
45
|
message,
|
|
15
|
-
...(initialValue === undefined ? {} : {
|
|
46
|
+
...(initialValue === undefined ? {} : { initialValue }),
|
|
47
|
+
...(validate === undefined
|
|
48
|
+
? {}
|
|
49
|
+
: {
|
|
50
|
+
validate: (value) => value === undefined ? undefined : validate(value),
|
|
51
|
+
}),
|
|
16
52
|
}),
|
|
17
53
|
select: async (message, options, initialValue) => prompts.select({
|
|
18
54
|
message,
|
|
@@ -41,37 +77,22 @@ async function promptValue(promise, ui) {
|
|
|
41
77
|
}
|
|
42
78
|
return value;
|
|
43
79
|
}
|
|
44
|
-
function existingChoices(config, hosts) {
|
|
45
|
-
if (config === null)
|
|
46
|
-
return null;
|
|
47
|
-
return {
|
|
48
|
-
schema: INIT_CHOICES_SCHEMA,
|
|
49
|
-
name: config.name,
|
|
50
|
-
workspaceId: config.workspaceId,
|
|
51
|
-
repositories: config.repositories.map((repository) => ({
|
|
52
|
-
path: repository.path,
|
|
53
|
-
id: repository.id,
|
|
54
|
-
access: repository.access,
|
|
55
|
-
})),
|
|
56
|
-
knowledge: config.knowledge,
|
|
57
|
-
code: config.code,
|
|
58
|
-
validation: [],
|
|
59
|
-
excludes: config.excludes,
|
|
60
|
-
hosts: [...new Set(hosts)],
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
80
|
async function askHosts(ui, initial) {
|
|
64
|
-
return promptValue(ui.multiselect('Select hosts to record (
|
|
65
|
-
{
|
|
81
|
+
return promptValue(ui.multiselect('Select hosts to record (declaration only; no installation, detection or wiring)', [
|
|
82
|
+
{
|
|
83
|
+
value: 'pi',
|
|
84
|
+
label: 'Pi',
|
|
85
|
+
hint: 'Declaration only; not detected or verified',
|
|
86
|
+
},
|
|
66
87
|
{
|
|
67
88
|
value: 'codex',
|
|
68
89
|
label: 'Codex',
|
|
69
|
-
hint: '
|
|
90
|
+
hint: 'Declaration only; not detected or verified',
|
|
70
91
|
},
|
|
71
92
|
{
|
|
72
93
|
value: 'claude',
|
|
73
94
|
label: 'Claude',
|
|
74
|
-
hint: '
|
|
95
|
+
hint: 'Declaration only; not detected or verified',
|
|
75
96
|
},
|
|
76
97
|
], initial), ui);
|
|
77
98
|
}
|
|
@@ -79,163 +100,618 @@ async function askHosts(ui, initial) {
|
|
|
79
100
|
* UI-only selection collection. All validation and plan generation remains in
|
|
80
101
|
* the app service; cancelling this function never writes to the workspace.
|
|
81
102
|
*/
|
|
82
|
-
export async function collectInitChoices(context, ui) {
|
|
103
|
+
export async function collectInitChoices(context, ui, options = {}) {
|
|
83
104
|
ui.intro('Pappra workspace init');
|
|
84
105
|
const existingConfig = context.workspaceInspection.value;
|
|
85
106
|
const existingMachine = context.machineInspection.value;
|
|
86
|
-
if (existingConfig !== null
|
|
107
|
+
if (existingConfig !== null &&
|
|
108
|
+
options.reconfigure !== true &&
|
|
109
|
+
options.draft === undefined) {
|
|
87
110
|
const hosts = existingMachine?.selectedHosts ?? (await askHosts(ui, []));
|
|
88
111
|
if (hosts === null)
|
|
89
112
|
return null;
|
|
90
|
-
const choices =
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
113
|
+
const choices = {
|
|
114
|
+
...existingInitChoices(existingConfig, existingMachine),
|
|
115
|
+
hosts: [...new Set(hosts)],
|
|
116
|
+
};
|
|
117
|
+
ui.note('Existing valid workspace configuration is reused. Only missing local state is planned; no existing file is overwritten.', 'Existing configuration');
|
|
118
|
+
collectedPathObservations.set(choices, options.pathObservations ?? []);
|
|
119
|
+
ui.outro('Selections complete. Review the plan before applying.');
|
|
95
120
|
return choices;
|
|
96
121
|
}
|
|
122
|
+
const seed = options.draft ??
|
|
123
|
+
(existingConfig === null
|
|
124
|
+
? undefined
|
|
125
|
+
: existingInitChoices(existingConfig, existingMachine));
|
|
126
|
+
const common = deriveCommonInitDefaults(context.discovery);
|
|
97
127
|
const repositoryCandidates = listRepositoryCandidates(context.discovery);
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
128
|
+
const knowledgeCandidates = listKnowledgeCandidates(context.discovery);
|
|
129
|
+
const pathObservations = new Map();
|
|
130
|
+
for (const observation of options.pathObservations ?? []) {
|
|
131
|
+
pathObservations.set(initPathObservationKey(observation.kind, observation.path), observation);
|
|
132
|
+
}
|
|
133
|
+
const explicitRepositoryCandidates = new Map();
|
|
134
|
+
const explicitKnowledgeCandidates = new Map();
|
|
135
|
+
for (const observation of pathObservations.values()) {
|
|
136
|
+
const repository = repositoryCandidateFromPathObservation(observation);
|
|
137
|
+
if (repository !== null) {
|
|
138
|
+
explicitRepositoryCandidates.set(repository.key, repository);
|
|
139
|
+
}
|
|
140
|
+
const knowledge = knowledgeCandidateFromPathObservation(observation);
|
|
141
|
+
if (knowledge !== null) {
|
|
142
|
+
explicitKnowledgeCandidates.set(knowledge.path, knowledge);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
const knownKnowledge = new Set([
|
|
146
|
+
...knowledgeCandidates.map((candidate) => candidate.path),
|
|
147
|
+
...explicitKnowledgeCandidates.keys(),
|
|
148
|
+
]);
|
|
149
|
+
const initialRepositories = seed !== undefined
|
|
150
|
+
? seed.repositories.map((repository) => ({ ...repository }))
|
|
151
|
+
: repositoryCandidates.length === 1
|
|
152
|
+
? [
|
|
153
|
+
{
|
|
154
|
+
path: repositoryCandidates[0].key,
|
|
155
|
+
access: repositoryCandidates[0].access,
|
|
156
|
+
...(repositoryCandidates[0].id === null
|
|
157
|
+
? {}
|
|
158
|
+
: { id: repositoryCandidates[0].id }),
|
|
159
|
+
},
|
|
160
|
+
]
|
|
161
|
+
: [];
|
|
162
|
+
const draft = {
|
|
163
|
+
schema: INIT_CHOICES_SCHEMA,
|
|
164
|
+
...(seed?.workspaceId === undefined
|
|
165
|
+
? {}
|
|
166
|
+
: { workspaceId: seed.workspaceId }),
|
|
167
|
+
name: seed?.name ?? common.name,
|
|
168
|
+
repositories: initialRepositories,
|
|
169
|
+
knowledge: {
|
|
170
|
+
...(seed?.knowledge ?? common.knowledge),
|
|
171
|
+
sources: [...(seed?.knowledge.sources ?? common.knowledge.sources)],
|
|
172
|
+
},
|
|
173
|
+
code: seed?.code ?? {
|
|
174
|
+
graph: 'none',
|
|
175
|
+
symbols: common.code.symbols,
|
|
176
|
+
prepare: common.code.prepare,
|
|
177
|
+
},
|
|
178
|
+
validation: [...(seed?.validation ?? [])],
|
|
179
|
+
excludes: [...(seed?.excludes ?? common.excludes)],
|
|
180
|
+
hosts: [...(seed?.hosts ?? [])],
|
|
181
|
+
};
|
|
182
|
+
if (!options.reviewOnly)
|
|
183
|
+
ui.note(`Workspace root: ${context.root}\nMode: ${existingConfig === null
|
|
184
|
+
? 'new workspace'
|
|
185
|
+
: 'editing existing workspace settings'}\nSelections are a read-only draft until the later plan confirmation. Existing knowledge source files/folders are discovered and registered in place later; the new document destination is only for future authored documents. No files are created or changed while collecting choices.`, 'Workspace context');
|
|
186
|
+
const promptText = async (message, initialValue, field) => {
|
|
187
|
+
const value = await promptValue(ui.text(message, initialValue, field === undefined
|
|
188
|
+
? undefined
|
|
189
|
+
: (candidate) => validateInitField(field, candidate) ?? undefined), ui);
|
|
190
|
+
return typeof value === 'symbol' ? null : value;
|
|
191
|
+
};
|
|
192
|
+
const promptExplicitPath = async (kind) => {
|
|
193
|
+
const field = kind === 'repository' ? 'repositoryPath' : 'knowledgePath';
|
|
194
|
+
let initialValue = '';
|
|
195
|
+
for (;;) {
|
|
196
|
+
const value = await ui.text(kind === 'repository'
|
|
197
|
+
? `Existing repository path under workspace root ${context.root}`
|
|
198
|
+
: `Existing knowledge file or folder path under workspace root ${context.root}`, initialValue, (candidate) => validateInitField(field, candidate) ?? undefined);
|
|
199
|
+
if (typeof value !== 'string') {
|
|
200
|
+
ui.note('Path addition cancelled; the current draft selections are unchanged.', 'Path addition');
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
const inspected = await inspectInitPathAddition(context.root, kind, value, draft.excludes);
|
|
204
|
+
if (inspected.ok)
|
|
205
|
+
return inspected.observation;
|
|
206
|
+
ui.note(diagnosticLines(inspected.diagnostics), 'Path not added');
|
|
207
|
+
initialValue = value;
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
const editWorkspace = async () => {
|
|
211
|
+
const name = await promptText('Workspace name', draft.name ?? '', 'name');
|
|
212
|
+
if (name === null)
|
|
213
|
+
return false;
|
|
214
|
+
draft.name = name;
|
|
215
|
+
return true;
|
|
216
|
+
};
|
|
217
|
+
const editRepositories = async () => {
|
|
218
|
+
const buildCandidateMap = () => {
|
|
219
|
+
const candidates = new Map();
|
|
220
|
+
for (const candidate of repositoryCandidates) {
|
|
221
|
+
candidates.set(candidate.key, candidate);
|
|
222
|
+
}
|
|
223
|
+
for (const candidate of explicitRepositoryCandidates.values()) {
|
|
224
|
+
if (!candidates.has(candidate.key))
|
|
225
|
+
candidates.set(candidate.key, candidate);
|
|
226
|
+
}
|
|
227
|
+
return candidates;
|
|
228
|
+
};
|
|
229
|
+
const buildOptions = (candidates) => {
|
|
230
|
+
const result = [...candidates.values()].map((candidate) => ({
|
|
231
|
+
value: candidate.key,
|
|
232
|
+
label: `${candidate.key}${candidate.external ? ' (external)' : ''}`,
|
|
233
|
+
hint: `${candidate.kind}; ${candidate.external ? 'explicit external mapping' : 'discovered or explicitly inspected checkout'}${candidate.dirty === true ? '; dirty' : ''}`,
|
|
234
|
+
}));
|
|
235
|
+
for (const repository of draft.repositories) {
|
|
236
|
+
if (result.some((candidate) => candidate.value === repository.path)) {
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
result.push({
|
|
240
|
+
value: repository.path,
|
|
241
|
+
label: `${repository.path} (configured; not discovered)`,
|
|
242
|
+
hint: 'Retained existing selection; planning fails if it cannot be resolved',
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
result.push({
|
|
246
|
+
value: addExistingRepositoryAction,
|
|
247
|
+
label: 'Add existing repository…',
|
|
248
|
+
hint: `Inspect one workspace-relative checkout beneath ${context.root}; no global or ancestor discovery`,
|
|
249
|
+
});
|
|
250
|
+
return result;
|
|
251
|
+
};
|
|
252
|
+
let selectedKeys = [
|
|
253
|
+
...new Set(draft.repositories.map((repository) => repository.path)),
|
|
254
|
+
];
|
|
255
|
+
for (;;) {
|
|
256
|
+
const candidates = buildCandidateMap();
|
|
257
|
+
const selected = await promptValue(ui.multiselect(`Select repositories in this workspace (resolved root: ${context.root})`, buildOptions(candidates), selectedKeys), ui);
|
|
258
|
+
if (selected === null)
|
|
259
|
+
return false;
|
|
260
|
+
selectedKeys = [
|
|
261
|
+
...new Set(selected.filter((key) => key !== addExistingRepositoryAction)),
|
|
262
|
+
];
|
|
263
|
+
if (!selected.includes(addExistingRepositoryAction))
|
|
264
|
+
break;
|
|
265
|
+
const observation = await promptExplicitPath('repository');
|
|
266
|
+
if (observation === null)
|
|
267
|
+
continue;
|
|
268
|
+
const candidate = repositoryCandidateFromPathObservation(observation);
|
|
269
|
+
if (candidate === null) {
|
|
270
|
+
ui.note('The entered location did not produce a repository observation.', 'Path not added');
|
|
271
|
+
continue;
|
|
272
|
+
}
|
|
273
|
+
const observationKey = initPathObservationKey('repository', observation.path);
|
|
274
|
+
pathObservations.set(observationKey, observation);
|
|
275
|
+
const existing = [...buildCandidateMap().values()].find((entry) => entry.key === candidate.key || entry.root === candidate.root);
|
|
276
|
+
if (existing !== undefined) {
|
|
277
|
+
if (!selectedKeys.includes(existing.key))
|
|
278
|
+
selectedKeys.push(existing.key);
|
|
279
|
+
ui.note(`That checkout is already available as ${existing.key}; it was selected without adding a duplicate.`, 'Repository already available');
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
explicitRepositoryCandidates.set(candidate.key, candidate);
|
|
283
|
+
if (!selectedKeys.includes(candidate.key))
|
|
284
|
+
selectedKeys.push(candidate.key);
|
|
285
|
+
ui.note(`Added ${candidate.key} as an existing workspace-local checkout. The selected access policy remains a separate workspace choice; no files were written.`, 'Repository added');
|
|
286
|
+
}
|
|
287
|
+
const candidates = buildCandidateMap();
|
|
288
|
+
const repositories = [];
|
|
289
|
+
for (const key of selectedKeys) {
|
|
290
|
+
const candidate = candidates.get(key);
|
|
291
|
+
const seeded = draft.repositories.find((repository) => repository.path === key);
|
|
292
|
+
const access = await promptValue(ui.select(`Repository access policy for ${key} (workspace policy only; not a host permission or command execution)`, [
|
|
293
|
+
{ value: 'read', label: 'read', hint: 'Least-privilege policy' },
|
|
294
|
+
{
|
|
295
|
+
value: 'write',
|
|
296
|
+
label: 'write',
|
|
297
|
+
hint: 'Workspace policy cap only; host permissions still apply',
|
|
298
|
+
},
|
|
299
|
+
], seeded?.access ?? candidate?.access ?? 'read'), ui);
|
|
300
|
+
if (access === null)
|
|
301
|
+
return false;
|
|
302
|
+
const id = seeded?.id ?? candidate?.id ?? null;
|
|
303
|
+
repositories.push({
|
|
304
|
+
path: key,
|
|
305
|
+
access: access,
|
|
306
|
+
...(id === null ? {} : { id }),
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
draft.repositories = repositories;
|
|
310
|
+
return true;
|
|
311
|
+
};
|
|
312
|
+
const editKnowledge = async () => {
|
|
313
|
+
const rank = (path, isDirectory) => isDirectory ? 1 : path.includes('/') ? 2 : 0;
|
|
314
|
+
const buildCandidateMap = () => {
|
|
315
|
+
const candidates = new Map();
|
|
316
|
+
for (const candidate of knowledgeCandidates) {
|
|
317
|
+
candidates.set(candidate.path, candidate);
|
|
318
|
+
}
|
|
319
|
+
for (const candidate of explicitKnowledgeCandidates.values()) {
|
|
320
|
+
if (!candidates.has(candidate.path))
|
|
321
|
+
candidates.set(candidate.path, candidate);
|
|
322
|
+
}
|
|
323
|
+
return candidates;
|
|
324
|
+
};
|
|
325
|
+
const buildOptions = (candidates) => {
|
|
326
|
+
const result = [...candidates.values()]
|
|
327
|
+
.map((candidate) => ({
|
|
328
|
+
candidate,
|
|
329
|
+
rank: rank(candidate.path, candidate.kind === 'directory'),
|
|
330
|
+
}))
|
|
331
|
+
.sort((left, right) => left.rank - right.rank ||
|
|
332
|
+
left.candidate.path.localeCompare(right.candidate.path))
|
|
333
|
+
.map(({ candidate }) => {
|
|
334
|
+
const explicit = candidate.origin === 'explicit-path';
|
|
335
|
+
return {
|
|
336
|
+
value: candidate.path,
|
|
337
|
+
label: candidate.kind === 'directory'
|
|
338
|
+
? `Folder: ${candidate.path}${explicit ? ' (explicitly inspected)' : ' (covers supported files beneath)'}`
|
|
339
|
+
: candidate.path.includes('/')
|
|
340
|
+
? `File: ${candidate.path}${explicit ? ' (explicitly inspected)' : ''}`
|
|
341
|
+
: `Root file: ${candidate.path}${explicit ? ' (explicitly inspected)' : ''}`,
|
|
342
|
+
hint: explicit
|
|
343
|
+
? candidate.kind === 'directory'
|
|
344
|
+
? 'Existing source path inspected only; no documents were enumerated or read. Empty folders are valid.'
|
|
345
|
+
: 'Existing source path inspected only; init did not read its document body or copy/move it.'
|
|
346
|
+
: candidate.kind === 'directory'
|
|
347
|
+
? 'Includes supported discovered documents beneath it, subject to exclusions, limits and symlink boundaries; does not promise the entire filesystem tree.'
|
|
348
|
+
: candidate.path.includes('/')
|
|
349
|
+
? 'Select this file directly; its folder is not selected implicitly.'
|
|
350
|
+
: 'Existing root file; selecting it does not copy or register it now.',
|
|
351
|
+
};
|
|
352
|
+
});
|
|
353
|
+
for (const source of draft.knowledge.sources) {
|
|
354
|
+
if (result.some((candidate) => candidate.value === source))
|
|
355
|
+
continue;
|
|
356
|
+
result.push({
|
|
357
|
+
value: source,
|
|
358
|
+
label: `Retained configured: ${source} (unavailable/unverified)`,
|
|
359
|
+
hint: 'Existing configured selection retained; not freshly discovered or verified. You may remove it explicitly.',
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
result.push({
|
|
363
|
+
value: addExistingKnowledgeAction,
|
|
364
|
+
label: 'Add existing knowledge file or folder…',
|
|
365
|
+
hint: `Inspect one workspace-relative source beneath ${context.root}; no document bodies are read`,
|
|
366
|
+
});
|
|
367
|
+
return result;
|
|
368
|
+
};
|
|
369
|
+
let sources = [...draft.knowledge.sources];
|
|
370
|
+
for (;;) {
|
|
371
|
+
const candidates = buildCandidateMap();
|
|
372
|
+
const selected = await promptValue(ui.multiselect(`Select knowledge sources under workspace root ${context.root}`, buildOptions(candidates), sources), ui);
|
|
373
|
+
if (selected === null)
|
|
374
|
+
return false;
|
|
375
|
+
sources = [
|
|
376
|
+
...new Set(selected.filter((source) => source !== addExistingKnowledgeAction)),
|
|
377
|
+
];
|
|
378
|
+
if (!selected.includes(addExistingKnowledgeAction))
|
|
379
|
+
break;
|
|
380
|
+
const observation = await promptExplicitPath('knowledge');
|
|
381
|
+
if (observation === null)
|
|
382
|
+
continue;
|
|
383
|
+
const candidate = knowledgeCandidateFromPathObservation(observation);
|
|
384
|
+
if (candidate === null) {
|
|
385
|
+
ui.note('The entered location did not produce a knowledge-source observation.', 'Path not added');
|
|
386
|
+
continue;
|
|
387
|
+
}
|
|
388
|
+
const observationKey = initPathObservationKey('knowledge', observation.path);
|
|
389
|
+
pathObservations.set(observationKey, observation);
|
|
390
|
+
const existing = buildCandidateMap().get(candidate.path);
|
|
391
|
+
if (existing !== undefined) {
|
|
392
|
+
if (!sources.includes(existing.path))
|
|
393
|
+
sources.push(existing.path);
|
|
394
|
+
ui.note(`${existing.path} is already an available knowledge source; it was selected without adding a duplicate.`, 'Knowledge source already available');
|
|
395
|
+
continue;
|
|
396
|
+
}
|
|
397
|
+
explicitKnowledgeCandidates.set(candidate.path, candidate);
|
|
398
|
+
knownKnowledge.add(candidate.path);
|
|
399
|
+
sources.push(candidate.path);
|
|
400
|
+
ui.note(`Added ${candidate.kind} source ${candidate.path}. It remains in place; init did not read document bodies or create, copy, move, or register documents. Empty folders are valid knowledge sources.`, 'Knowledge source added');
|
|
401
|
+
}
|
|
402
|
+
sources = [...new Set(sources)];
|
|
403
|
+
ui.note(`Existing source folders cover supported discovered documents beneath them, subject to exclusions, limits and symlink boundaries. The new document destination is a workspace-relative path under ${context.root}; it is not scanned/imported and is not created here.`, 'Knowledge sources and destination');
|
|
404
|
+
const newDocuments = await promptText('New authored-document destination (workspace-relative; not created here)', draft.knowledge.newDocuments, 'newDocuments');
|
|
405
|
+
if (newDocuments === null)
|
|
406
|
+
return false;
|
|
407
|
+
const visibility = await promptValue(ui.select('Task document visibility policy (declaration only; no existing record is shared or migrated)', [
|
|
115
408
|
{
|
|
116
|
-
value: '
|
|
117
|
-
label: '
|
|
118
|
-
hint: 'Default
|
|
409
|
+
value: 'private',
|
|
410
|
+
label: 'private',
|
|
411
|
+
hint: 'Default; raw evidence and checkpoints remain private',
|
|
119
412
|
},
|
|
120
413
|
{
|
|
121
|
-
value: '
|
|
122
|
-
label: '
|
|
123
|
-
hint: '
|
|
414
|
+
value: 'shared',
|
|
415
|
+
label: 'shared',
|
|
416
|
+
hint: 'Task documents may be committed according to workspace policy',
|
|
124
417
|
},
|
|
125
|
-
],
|
|
126
|
-
if (
|
|
127
|
-
return
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
label: '
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
return null;
|
|
173
|
-
const symbols = await promptValue(ui.select('Symbol provider preference', [
|
|
174
|
-
{ value: 'auto', label: 'auto', hint: 'Use available existing tools' },
|
|
175
|
-
{
|
|
176
|
-
value: 'serena',
|
|
177
|
-
label: 'serena',
|
|
178
|
-
hint: 'Preference only; no installation is performed',
|
|
179
|
-
},
|
|
180
|
-
{ value: 'none', label: 'none' },
|
|
181
|
-
], 'auto'), ui);
|
|
182
|
-
if (symbols === null)
|
|
183
|
-
return null;
|
|
184
|
-
const prepare = await promptValue(ui.select('Index preparation preference', [
|
|
185
|
-
{
|
|
186
|
-
value: 'on-demand',
|
|
187
|
-
label: 'on-demand',
|
|
188
|
-
hint: 'Prepare only when a later authorized operation asks',
|
|
189
|
-
},
|
|
190
|
-
{
|
|
191
|
-
value: 'manual',
|
|
192
|
-
label: 'manual',
|
|
193
|
-
hint: 'Never prepare automatically',
|
|
194
|
-
},
|
|
195
|
-
], 'on-demand'), ui);
|
|
196
|
-
if (prepare === null)
|
|
197
|
-
return null;
|
|
198
|
-
const validationCandidates = context.discovery.validationCandidates;
|
|
199
|
-
const selectedValidation = validationCandidates.length === 0
|
|
200
|
-
? []
|
|
201
|
-
: await promptValue(ui.multiselect('Select validation command candidates (none are executed or verified)', validationCandidates.map((candidate) => ({
|
|
418
|
+
], draft.knowledge.taskVisibility), ui);
|
|
419
|
+
if (visibility === null)
|
|
420
|
+
return false;
|
|
421
|
+
draft.knowledge = {
|
|
422
|
+
sources: [...sources],
|
|
423
|
+
newDocuments,
|
|
424
|
+
taskVisibility: visibility,
|
|
425
|
+
};
|
|
426
|
+
return true;
|
|
427
|
+
};
|
|
428
|
+
const editAdvanced = async () => {
|
|
429
|
+
const graph = await promptValue(ui.select('Code graph preference (optional; no installation or indexing here)', [
|
|
430
|
+
{ value: 'none', label: 'none', hint: 'No graph provider' },
|
|
431
|
+
{
|
|
432
|
+
value: 'codegraph',
|
|
433
|
+
label: 'codegraph',
|
|
434
|
+
hint: 'Preference only; nothing is installed or indexed',
|
|
435
|
+
},
|
|
436
|
+
], draft.code.graph), ui);
|
|
437
|
+
if (graph === null)
|
|
438
|
+
return false;
|
|
439
|
+
const symbols = await promptValue(ui.select('Symbol provider preference (optional; no installation here)', [
|
|
440
|
+
{
|
|
441
|
+
value: 'auto',
|
|
442
|
+
label: 'auto',
|
|
443
|
+
hint: 'Use available existing tools',
|
|
444
|
+
},
|
|
445
|
+
{ value: 'serena', label: 'serena', hint: 'Preference only' },
|
|
446
|
+
{ value: 'none', label: 'none' },
|
|
447
|
+
], draft.code.symbols), ui);
|
|
448
|
+
if (symbols === null)
|
|
449
|
+
return false;
|
|
450
|
+
const prepare = await promptValue(ui.select('Index preparation preference (optional; nothing is prepared here)', [
|
|
451
|
+
{
|
|
452
|
+
value: 'on-demand',
|
|
453
|
+
label: 'on-demand',
|
|
454
|
+
hint: 'Prepare only when a later authorized operation asks',
|
|
455
|
+
},
|
|
456
|
+
{
|
|
457
|
+
value: 'manual',
|
|
458
|
+
label: 'manual',
|
|
459
|
+
hint: 'Never prepare automatically',
|
|
460
|
+
},
|
|
461
|
+
], draft.code.prepare), ui);
|
|
462
|
+
if (prepare === null)
|
|
463
|
+
return false;
|
|
464
|
+
const validationOptions = context.discovery.validationCandidates.map((candidate) => ({
|
|
202
465
|
value: candidate.id,
|
|
203
466
|
label: `${candidate.id}`,
|
|
204
|
-
hint: `${candidate.origin}; ${candidate.executable} ${candidate.args.join(' ')}; not verified`,
|
|
205
|
-
}))
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
code: {
|
|
467
|
+
hint: `${candidate.origin}; ${candidate.executable} ${candidate.args.join(' ')}; not executed or verified`,
|
|
468
|
+
}));
|
|
469
|
+
for (const candidateId of draft.validation) {
|
|
470
|
+
if (validationOptions.some((candidate) => candidate.value === candidateId)) {
|
|
471
|
+
continue;
|
|
472
|
+
}
|
|
473
|
+
validationOptions.push({
|
|
474
|
+
value: candidateId,
|
|
475
|
+
label: `${candidateId} (configured; not discovered)`,
|
|
476
|
+
hint: 'Retained existing selection; planning fails if it cannot be bound',
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
const selectedValidation = validationOptions.length === 0
|
|
480
|
+
? []
|
|
481
|
+
: await promptValue(ui.multiselect('Select validation command candidates (optional; none are executed or verified)', validationOptions, draft.validation), ui);
|
|
482
|
+
if (selectedValidation === null)
|
|
483
|
+
return false;
|
|
484
|
+
const hosts = await askHosts(ui, draft.hosts);
|
|
485
|
+
if (hosts === null)
|
|
486
|
+
return false;
|
|
487
|
+
const excludesText = await promptText('Workspace-relative exclude prefixes (complete seeded list; comma separated; literal prefixes only)', draft.excludes.join(', '), 'excludes');
|
|
488
|
+
if (excludesText === null)
|
|
489
|
+
return false;
|
|
490
|
+
draft.code = {
|
|
229
491
|
graph: graph,
|
|
230
492
|
symbols: symbols,
|
|
231
493
|
prepare: prepare,
|
|
232
|
-
}
|
|
233
|
-
validation
|
|
234
|
-
|
|
494
|
+
};
|
|
495
|
+
draft.validation = [...selectedValidation];
|
|
496
|
+
draft.hosts = [...new Set(hosts)];
|
|
497
|
+
draft.excludes = excludesText
|
|
235
498
|
.split(/[,\n]/)
|
|
236
499
|
.map((value) => value.trim())
|
|
237
|
-
.filter((value) => value !== '')
|
|
238
|
-
|
|
500
|
+
.filter((value) => value !== '');
|
|
501
|
+
ui.note('Advanced choices only record preferences. No validation command, host probe, installation, index preparation or global wiring runs here.', 'Advanced choices');
|
|
502
|
+
return true;
|
|
503
|
+
};
|
|
504
|
+
const sections = [
|
|
505
|
+
'workspace',
|
|
506
|
+
'repositories',
|
|
507
|
+
'knowledge',
|
|
508
|
+
'advanced',
|
|
509
|
+
];
|
|
510
|
+
const editSection = async (section) => {
|
|
511
|
+
if (section === 'workspace')
|
|
512
|
+
return editWorkspace();
|
|
513
|
+
if (section === 'repositories')
|
|
514
|
+
return editRepositories();
|
|
515
|
+
if (section === 'knowledge')
|
|
516
|
+
return editKnowledge();
|
|
517
|
+
return editAdvanced();
|
|
518
|
+
};
|
|
519
|
+
let index = 0;
|
|
520
|
+
let reviewRequested = false;
|
|
521
|
+
while (!reviewRequested &&
|
|
522
|
+
index < sections.length &&
|
|
523
|
+
options.reviewOnly !== true) {
|
|
524
|
+
const section = sections[index];
|
|
525
|
+
if (section !== 'advanced' && !(await editSection(section)))
|
|
526
|
+
return null;
|
|
527
|
+
const last = index === sections.length - 1;
|
|
528
|
+
const navigation = section === 'advanced'
|
|
529
|
+
? [
|
|
530
|
+
{
|
|
531
|
+
value: 'skip',
|
|
532
|
+
label: 'Keep current advanced settings',
|
|
533
|
+
hint: 'Retain graph/symbols/preparation/validation/hosts/excludes and continue to review',
|
|
534
|
+
},
|
|
535
|
+
{
|
|
536
|
+
value: 'edit',
|
|
537
|
+
label: 'Edit advanced preferences',
|
|
538
|
+
hint: 'Explicitly edit graph, symbols, preparation, validation, hosts and excludes',
|
|
539
|
+
},
|
|
540
|
+
{
|
|
541
|
+
value: 'back',
|
|
542
|
+
label: 'Back',
|
|
543
|
+
hint: 'Return to the knowledge section',
|
|
544
|
+
},
|
|
545
|
+
{
|
|
546
|
+
value: 'cancel',
|
|
547
|
+
label: 'Cancel',
|
|
548
|
+
hint: 'Discard the draft; no files are written',
|
|
549
|
+
},
|
|
550
|
+
]
|
|
551
|
+
: [
|
|
552
|
+
...(index > 0
|
|
553
|
+
? [
|
|
554
|
+
{
|
|
555
|
+
value: 'back',
|
|
556
|
+
label: 'Back',
|
|
557
|
+
hint: 'Return to the previous setup section',
|
|
558
|
+
},
|
|
559
|
+
]
|
|
560
|
+
: []),
|
|
561
|
+
{
|
|
562
|
+
value: 'next',
|
|
563
|
+
label: last ? 'Review selections' : 'Continue',
|
|
564
|
+
hint: last
|
|
565
|
+
? 'Review the draft; a detailed plan follows'
|
|
566
|
+
: 'Move to the next setup section',
|
|
567
|
+
},
|
|
568
|
+
{
|
|
569
|
+
value: 'review',
|
|
570
|
+
label: 'Review selections',
|
|
571
|
+
hint: 'Skip remaining setup and review retained values',
|
|
572
|
+
},
|
|
573
|
+
{
|
|
574
|
+
value: 'cancel',
|
|
575
|
+
label: 'Cancel',
|
|
576
|
+
hint: 'Discard the draft; no files are written',
|
|
577
|
+
},
|
|
578
|
+
];
|
|
579
|
+
const action = await promptValue(ui.select('Section navigation', navigation, section === 'advanced' ? 'skip' : 'next'), ui);
|
|
580
|
+
if (action === null)
|
|
581
|
+
return null;
|
|
582
|
+
if (action === 'cancel') {
|
|
583
|
+
ui.cancel('Init cancelled; no files were written.');
|
|
584
|
+
return null;
|
|
585
|
+
}
|
|
586
|
+
if (action === 'skip') {
|
|
587
|
+
reviewRequested = true;
|
|
588
|
+
}
|
|
589
|
+
else if (action === 'edit') {
|
|
590
|
+
if (!(await editSection(section)))
|
|
591
|
+
return null;
|
|
592
|
+
reviewRequested = true;
|
|
593
|
+
}
|
|
594
|
+
else if (action === 'back') {
|
|
595
|
+
index = Math.max(0, index - 1);
|
|
596
|
+
}
|
|
597
|
+
else if (action === 'review') {
|
|
598
|
+
reviewRequested = true;
|
|
599
|
+
}
|
|
600
|
+
else {
|
|
601
|
+
index += 1;
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
const reviewSummary = () => {
|
|
605
|
+
const unavailable = draft.knowledge.sources.map((source) => knownKnowledge.has(source) ? source : `${source} (retained/unverified)`);
|
|
606
|
+
return `Workspace root: ${context.root}\nMode: ${existingConfig === null ? 'new workspace' : 'editing existing settings'}\nName: ${draft.name}\nRepositories (workspace policy): ${draft.repositories.length === 0
|
|
607
|
+
? '(none)'
|
|
608
|
+
: draft.repositories
|
|
609
|
+
.map((repository) => `${repository.path}=${repository.access}`)
|
|
610
|
+
.join(', ')}\nKnowledge sources (discover/register in place): ${unavailable.length === 0 ? '(none)' : unavailable.join(', ')}\nNew document destination (future authored documents only): ${draft.knowledge.newDocuments}\nDocument policy: ${draft.knowledge.taskVisibility} (raw evidence/checkpoints remain private)\nAdvanced: graph=${draft.code.graph}, symbols=${draft.code.symbols}, prepare=${draft.code.prepare}, validation=${draft.validation.length === 0 ? '(none)' : draft.validation.join(', ')}, hosts=${draft.hosts.length === 0 ? '(none)' : draft.hosts.join(', ')}, excludes=${draft.excludes.length === 0 ? '(none)' : draft.excludes.join(', ')}\nThis is a read-only draft. A detailed file/runtime plan and final apply confirmation follow; review does not authorize writes.`;
|
|
611
|
+
};
|
|
612
|
+
while (true) {
|
|
613
|
+
ui.note(reviewSummary(), 'Review selections');
|
|
614
|
+
const action = await promptValue(ui.select('Edit a section or continue to the detailed plan', [
|
|
615
|
+
{ value: 'continue', label: 'Continue to plan' },
|
|
616
|
+
{ value: 'workspace', label: 'Edit workspace name' },
|
|
617
|
+
{ value: 'repositories', label: 'Edit repositories' },
|
|
618
|
+
{ value: 'knowledge', label: 'Edit knowledge sources/destination' },
|
|
619
|
+
{ value: 'advanced', label: 'Edit advanced settings' },
|
|
620
|
+
{ value: 'cancel', label: 'Cancel' },
|
|
621
|
+
], 'continue'), ui);
|
|
622
|
+
if (action === null)
|
|
623
|
+
return null;
|
|
624
|
+
if (action === 'cancel') {
|
|
625
|
+
ui.cancel('Init cancelled; no files were written.');
|
|
626
|
+
return null;
|
|
627
|
+
}
|
|
628
|
+
if (action === 'continue') {
|
|
629
|
+
collectedPathObservations.set(draft, [...pathObservations.values()]);
|
|
630
|
+
ui.outro('Selections complete. Review the plan before applying.');
|
|
631
|
+
return draft;
|
|
632
|
+
}
|
|
633
|
+
if (!(await editSection(action)))
|
|
634
|
+
return null;
|
|
635
|
+
}
|
|
636
|
+
collectedPathObservations.set(draft, [...pathObservations.values()]);
|
|
637
|
+
ui.outro('Selections complete. Review the plan before applying.');
|
|
638
|
+
return draft;
|
|
639
|
+
}
|
|
640
|
+
/** Describes the runtime work this invocation would perform without touching it. */
|
|
641
|
+
export function describeInitRuntimeOperation(options, bundle, dataDir) {
|
|
642
|
+
const targetVersion = bundle.scope.runtimeVersion;
|
|
643
|
+
const runtimeRoot = runtimePaths(targetVersion, dataDir).runtimeRoot;
|
|
644
|
+
const machineBindingPath = join(bundle.scope.workspaceRoot, '.pappra', 'local', 'machine.json');
|
|
645
|
+
const packageInfo = getPackageInfo();
|
|
646
|
+
if (options.installRuntime === true) {
|
|
647
|
+
const tarball = options.runtimeTarball ?? null;
|
|
648
|
+
const sourceVersion = options.runtimeVersion ?? targetVersion;
|
|
649
|
+
const versionMismatch = sourceVersion !== targetVersion;
|
|
650
|
+
const source = tarball === null
|
|
651
|
+
? `${packageInfo.name}@${sourceVersion}`
|
|
652
|
+
: `tarball ${resolve(options.cwd, tarball)} for ${packageInfo.name}@${sourceVersion}`;
|
|
653
|
+
const sourceKind = tarball === null ? 'version' : 'tarball';
|
|
654
|
+
const previewLines = [
|
|
655
|
+
`Explicit ${sourceKind} source: ${source}.`,
|
|
656
|
+
`Workspace target: ${targetVersion} at ${runtimeRoot}.`,
|
|
657
|
+
`Machine runtime binding: ${machineBindingPath}.`,
|
|
658
|
+
versionMismatch
|
|
659
|
+
? `The requested version ${sourceVersion} does not match the workspace target; after config apply the runtime request will be rejected before installation.`
|
|
660
|
+
: 'On apply, run the package manager and existing runtime verification before recording the machine binding; dependency resolution may require network access.',
|
|
661
|
+
'This preview does not install, pack, probe or write runtime state.',
|
|
662
|
+
];
|
|
663
|
+
const confirmationMessage = versionMismatch
|
|
664
|
+
? `Apply the planned workspace files? The runtime request ${source} does not match workspace target ${targetVersion} and will fail before installation.`
|
|
665
|
+
: `Apply the planned workspace files, then install/verify ${source} at ${runtimeRoot} and bind it in ${machineBindingPath}? Package-manager work may require network access; optional preferences remain deferred.`;
|
|
666
|
+
return {
|
|
667
|
+
mode: sourceKind,
|
|
668
|
+
runtimeRequested: true,
|
|
669
|
+
targetVersion,
|
|
670
|
+
source,
|
|
671
|
+
runtimeRoot,
|
|
672
|
+
machineBindingPath,
|
|
673
|
+
previewLines,
|
|
674
|
+
confirmationMessage,
|
|
675
|
+
};
|
|
676
|
+
}
|
|
677
|
+
if (options.noRuntime === true) {
|
|
678
|
+
return {
|
|
679
|
+
mode: 'disabled',
|
|
680
|
+
runtimeRequested: false,
|
|
681
|
+
targetVersion,
|
|
682
|
+
source: null,
|
|
683
|
+
runtimeRoot: null,
|
|
684
|
+
machineBindingPath,
|
|
685
|
+
previewLines: [
|
|
686
|
+
'No runtime installation, verification or binding operation will run (--no-runtime).',
|
|
687
|
+
'The planned workspace/machine configuration files may still be applied; existing runtime files are not written or bound by this invocation.',
|
|
688
|
+
],
|
|
689
|
+
confirmationMessage: 'Apply the planned workspace/machine configuration files only? No runtime operation will run (--no-runtime); knowledge registration, code indexing/preparation and host integration remain deferred.',
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
const source = `current package ${packageInfo.name}@${packageInfo.version} at ${getPackageRoot()}`;
|
|
693
|
+
const versionMismatch = packageInfo.version !== targetVersion;
|
|
694
|
+
const previewLines = [
|
|
695
|
+
`Default self-package source: ${source}.`,
|
|
696
|
+
`Workspace target: ${targetVersion} at ${runtimeRoot}.`,
|
|
697
|
+
`Machine runtime binding: ${machineBindingPath}.`,
|
|
698
|
+
versionMismatch
|
|
699
|
+
? `The current package version ${packageInfo.version} differs from workspace target ${targetVersion}. A verified runtime for this workspace target may still be reused. If no verified runtime can be reused, the current package source cannot satisfy this target and runtime setup will fail after config apply.`
|
|
700
|
+
: 'On apply, first verify/reuse an existing runtime for the workspace target. Reuse occurs only if the manifest, package contents, executable and smoke check pass; otherwise the current package may be packed and installed.',
|
|
701
|
+
'This preview does not pack, install, run smoke/version probes or write user-data state; it cannot promise reuse.',
|
|
702
|
+
];
|
|
703
|
+
const confirmationMessage = versionMismatch
|
|
704
|
+
? `Apply the planned workspace files, then first verify/reuse a runtime for the workspace target ${targetVersion} at ${runtimeRoot} and bind it in ${machineBindingPath}? A previously installed verified target runtime may still be reused. If no verified target runtime is reusable, the current package cannot satisfy this target; runtime setup will fail after config apply. Knowledge registration, code indexing/preparation and host integration remain deferred.`
|
|
705
|
+
: `Apply the planned workspace files, then verify/reuse a runtime for the workspace target ${targetVersion} at ${runtimeRoot}, or install ${source} if needed, and bind it in ${machineBindingPath}? Reuse requires the manifest, package contents, executable and smoke check to pass; package-manager and smoke-verification work may run. Knowledge registration, code indexing/preparation and host integration remain deferred.`;
|
|
706
|
+
return {
|
|
707
|
+
mode: 'default-self',
|
|
708
|
+
runtimeRequested: true,
|
|
709
|
+
targetVersion,
|
|
710
|
+
source,
|
|
711
|
+
runtimeRoot,
|
|
712
|
+
machineBindingPath,
|
|
713
|
+
previewLines,
|
|
714
|
+
confirmationMessage,
|
|
239
715
|
};
|
|
240
716
|
}
|
|
241
717
|
function runtimeOptionsFailure(message, nextAction) {
|
|
@@ -411,7 +887,26 @@ function diagnosticLines(diagnostics) {
|
|
|
411
887
|
.map((entry) => ` ${entry.severity} ${entry.code}: ${entry.message}${entry.hint === null ? '' : ` (${entry.hint})`}`)
|
|
412
888
|
.join('\n');
|
|
413
889
|
}
|
|
414
|
-
function
|
|
890
|
+
function deferredInitWork(bundle) {
|
|
891
|
+
const knowledge = bundle.scope.knowledge.sources.length === 0
|
|
892
|
+
? 'No knowledge documents are registered by init.'
|
|
893
|
+
: 'Knowledge sources are recorded in workspace settings only; init does not scan or register catalog entries.';
|
|
894
|
+
const code = `Code preferences (graph=${bundle.scope.code.graph}, symbols=${bundle.scope.code.symbols}, prepare=${bundle.scope.code.prepare}) are declarations only; init does not install, index or prepare tools.`;
|
|
895
|
+
const validation = bundle.scope.validation.length === 0
|
|
896
|
+
? 'No validation command is executed by init.'
|
|
897
|
+
: 'Selected validation commands are recorded only; init does not execute them.';
|
|
898
|
+
const hosts = bundle.scope.hosts.length === 0
|
|
899
|
+
? 'No host is declared; init does not detect, install or wire hosts.'
|
|
900
|
+
: `Host declarations (${bundle.scope.hosts.join(', ')}) are not probed, installed or wired by init.`;
|
|
901
|
+
return [
|
|
902
|
+
knowledge,
|
|
903
|
+
'The new-document destination is for future authored documents and is not created by init.',
|
|
904
|
+
code,
|
|
905
|
+
validation,
|
|
906
|
+
hosts,
|
|
907
|
+
];
|
|
908
|
+
}
|
|
909
|
+
function printPlan(output, bundle, runtimeOperation) {
|
|
415
910
|
output.stdout(`Pappra init plan ${bundle.plan.planId}\n`);
|
|
416
911
|
output.stdout(`Workspace: ${bundle.plan.workspaceRoot}\n`);
|
|
417
912
|
output.stdout(`Config source: ${bundle.scope.configSource}\n`);
|
|
@@ -423,15 +918,30 @@ function printPlan(output, bundle) {
|
|
|
423
918
|
for (const file of bundle.plan.files) {
|
|
424
919
|
output.stdout(` ${file.operation} ${file.path} before=${file.expectedBeforeDigest ?? 'absent'} after=${file.nextDigest}\n`);
|
|
425
920
|
}
|
|
426
|
-
|
|
427
|
-
|
|
921
|
+
const externalActions = runtimeOperation.mode === 'disabled'
|
|
922
|
+
? bundle.plan.externalActions.filter((action) => action.kind !== 'install-runtime')
|
|
923
|
+
: bundle.plan.externalActions;
|
|
924
|
+
output.stdout('Plan external actions (not executed during planning):\n');
|
|
925
|
+
if (externalActions.length === 0) {
|
|
428
926
|
output.stdout(' (none)\n');
|
|
429
927
|
}
|
|
430
928
|
else {
|
|
431
|
-
for (const action of
|
|
929
|
+
for (const action of externalActions) {
|
|
432
930
|
output.stdout(` ${action.kind} ${action.target}${action.version === null ? '' : `@${action.version}`}\n`);
|
|
433
931
|
}
|
|
434
932
|
}
|
|
933
|
+
if (runtimeOperation.mode === 'disabled' &&
|
|
934
|
+
bundle.plan.externalActions.some((action) => action.kind === 'install-runtime')) {
|
|
935
|
+
output.stdout(' install-runtime is suppressed by --no-runtime for this invocation.\n');
|
|
936
|
+
}
|
|
937
|
+
output.stdout('\nRuntime operation:\n');
|
|
938
|
+
for (const line of runtimeOperation.previewLines) {
|
|
939
|
+
output.stdout(` ${line}\n`);
|
|
940
|
+
}
|
|
941
|
+
output.stdout('Deferred init work:\n');
|
|
942
|
+
for (const line of deferredInitWork(bundle)) {
|
|
943
|
+
output.stdout(` ${line}\n`);
|
|
944
|
+
}
|
|
435
945
|
if (bundle.diagnostics.length > 0) {
|
|
436
946
|
output.stderr(`${diagnosticLines(bundle.diagnostics)}\n`);
|
|
437
947
|
}
|
|
@@ -444,7 +954,7 @@ function printPlan(output, bundle) {
|
|
|
444
954
|
output.stdout('No file changes are planned.\n');
|
|
445
955
|
}
|
|
446
956
|
}
|
|
447
|
-
function printApply(output, result,
|
|
957
|
+
function printApply(output, result, runtimeOperation) {
|
|
448
958
|
if (!result.ok) {
|
|
449
959
|
output.stderr(`pappra init apply: ${result.status}\n`);
|
|
450
960
|
output.stderr(`${diagnosticLines(result.diagnostics)}\n`);
|
|
@@ -456,14 +966,22 @@ function printApply(output, result, runtimeRequested) {
|
|
|
456
966
|
output.stderr(`Residual planned files require manual inspection: ${result.residualPaths.join(', ')}\n`);
|
|
457
967
|
}
|
|
458
968
|
if (result.pendingExternalActions.length > 0) {
|
|
459
|
-
output.stderr('Pending
|
|
969
|
+
output.stderr('Pending plan actions after the file-apply phase:\n');
|
|
460
970
|
for (const action of result.pendingExternalActions) {
|
|
461
971
|
output.stderr(` ${action.kind} ${action.target}${action.version === null ? '' : `@${action.version}`}\n`);
|
|
462
972
|
}
|
|
463
|
-
output.stderr(runtimeRequested
|
|
464
|
-
? 'The explicit runtime action is still pending; it will be attempted next.\n'
|
|
465
|
-
: 'Only planned config files were applied. Initialization is not complete until runtime/provider actions are authorized and executed.\n');
|
|
466
973
|
}
|
|
974
|
+
output.stdout(runtimeOperation.mode === 'disabled'
|
|
975
|
+
? 'Runtime operation: intentionally deferred by --no-runtime; none was run.\n'
|
|
976
|
+
: 'Runtime operation: scheduled after file application for verification/reuse or installation and machine binding.\n');
|
|
977
|
+
}
|
|
978
|
+
function workspaceApplySummary(bundle, result) {
|
|
979
|
+
if (result.appliedFiles.includes('.pappra/workspace.json')) {
|
|
980
|
+
return 'Workspace configuration applied.';
|
|
981
|
+
}
|
|
982
|
+
return bundle.scope.configSource === 'existing'
|
|
983
|
+
? 'Existing workspace configuration reused.'
|
|
984
|
+
: 'Workspace configuration unchanged.';
|
|
467
985
|
}
|
|
468
986
|
function advisoryNextActions(bundle) {
|
|
469
987
|
const actions = [];
|
|
@@ -478,13 +996,20 @@ function advisoryNextActions(bundle) {
|
|
|
478
996
|
}
|
|
479
997
|
return actions;
|
|
480
998
|
}
|
|
481
|
-
function
|
|
999
|
+
function initNextActions(bundle, runtimeOperation) {
|
|
1000
|
+
const actions = [...advisoryNextActions(bundle)];
|
|
1001
|
+
if (runtimeOperation.mode === 'disabled') {
|
|
1002
|
+
actions.unshift('Runtime operation was intentionally deferred by --no-runtime; rerun init --apply without --no-runtime to verify/reuse or install and bind the runtime.');
|
|
1003
|
+
}
|
|
1004
|
+
return actions;
|
|
1005
|
+
}
|
|
1006
|
+
function planJson(bundle, runtimeOperation) {
|
|
482
1007
|
return {
|
|
483
1008
|
command: 'init',
|
|
484
1009
|
mode: 'plan',
|
|
485
1010
|
plan: bundle.plan,
|
|
486
1011
|
scope: bundle.scope,
|
|
487
|
-
nextActions:
|
|
1012
|
+
nextActions: initNextActions(bundle, runtimeOperation),
|
|
488
1013
|
diagnostics: bundle.diagnostics,
|
|
489
1014
|
};
|
|
490
1015
|
}
|
|
@@ -548,8 +1073,59 @@ export async function runInitCommand(options, output, deps = {}) {
|
|
|
548
1073
|
return writeFailure(output, options, contextResult);
|
|
549
1074
|
}
|
|
550
1075
|
const context = contextResult.context;
|
|
1076
|
+
const recoverablePlanCodes = new Set([
|
|
1077
|
+
diagnosticCodes.initValidationUnbound,
|
|
1078
|
+
diagnosticCodes.initValidationUnknown,
|
|
1079
|
+
diagnosticCodes.initKnowledgeUnknown,
|
|
1080
|
+
diagnosticCodes.initRepositoryUnknown,
|
|
1081
|
+
diagnosticCodes.initRepositoryDuplicate,
|
|
1082
|
+
]);
|
|
551
1083
|
let choices = null;
|
|
552
1084
|
if (printChoices) {
|
|
1085
|
+
const existingConfig = context.workspaceInspection.value;
|
|
1086
|
+
if (options.reconfigure === true && existingConfig === null) {
|
|
1087
|
+
return writeFailure(output, options, {
|
|
1088
|
+
ok: false,
|
|
1089
|
+
status: 'needs_input',
|
|
1090
|
+
code: diagnosticCodes.initInputRequired,
|
|
1091
|
+
message: 'Reconfiguration requires an existing valid workspace configuration.',
|
|
1092
|
+
diagnostics: context.diagnostics,
|
|
1093
|
+
nextAction: 'Run pappra init without --reconfigure to create a workspace, or repair the existing workspace configuration first.',
|
|
1094
|
+
});
|
|
1095
|
+
}
|
|
1096
|
+
if (options.reconfigure === true && existingConfig !== null) {
|
|
1097
|
+
const existing = existingInitChoices(existingConfig, context.machineInspection.value);
|
|
1098
|
+
const rules = [
|
|
1099
|
+
{
|
|
1100
|
+
field: 'source',
|
|
1101
|
+
value: 'existing workspace.json/machine.json',
|
|
1102
|
+
reason: 'reconfiguration edits current represented selections; extensions and continuity are preserved by planning',
|
|
1103
|
+
},
|
|
1104
|
+
];
|
|
1105
|
+
const candidates = {
|
|
1106
|
+
repositories: listRepositoryCandidates(context.discovery).map((candidate) => candidate.key),
|
|
1107
|
+
knowledge: listKnowledgeCandidates(context.discovery).map((candidate) => candidate.path),
|
|
1108
|
+
validation: context.discovery.validationCandidates.map((candidate) => candidate.id),
|
|
1109
|
+
hosts: existing.hosts,
|
|
1110
|
+
};
|
|
1111
|
+
if (options.json) {
|
|
1112
|
+
output.stdout(`${JSON.stringify({
|
|
1113
|
+
ok: true,
|
|
1114
|
+
command: 'init',
|
|
1115
|
+
mode: 'choices',
|
|
1116
|
+
choices: existing,
|
|
1117
|
+
rules,
|
|
1118
|
+
candidates,
|
|
1119
|
+
})}\n`);
|
|
1120
|
+
}
|
|
1121
|
+
else {
|
|
1122
|
+
output.stdout(`${JSON.stringify(existing, null, 2)}\n`);
|
|
1123
|
+
for (const rule of rules) {
|
|
1124
|
+
output.stderr(` ${rule.field} = ${rule.value} (${rule.reason})\n`);
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
return 0;
|
|
1128
|
+
}
|
|
553
1129
|
const derived = await deriveInitChoices(context.discovery);
|
|
554
1130
|
if (options.json) {
|
|
555
1131
|
output.stdout(`${JSON.stringify({
|
|
@@ -576,54 +1152,124 @@ export async function runInitCommand(options, output, deps = {}) {
|
|
|
576
1152
|
choices = loaded.choices;
|
|
577
1153
|
}
|
|
578
1154
|
else if (yes) {
|
|
579
|
-
|
|
1155
|
+
if (options.reconfigure === true) {
|
|
1156
|
+
const existingConfig = context.workspaceInspection.value;
|
|
1157
|
+
if (existingConfig === null) {
|
|
1158
|
+
return writeFailure(output, options, {
|
|
1159
|
+
ok: false,
|
|
1160
|
+
status: 'needs_input',
|
|
1161
|
+
code: diagnosticCodes.initInputRequired,
|
|
1162
|
+
message: 'Reconfiguration requires an existing valid workspace configuration.',
|
|
1163
|
+
diagnostics: context.diagnostics,
|
|
1164
|
+
nextAction: 'Run pappra init without --reconfigure to create a workspace, or repair the existing workspace configuration first.',
|
|
1165
|
+
});
|
|
1166
|
+
}
|
|
1167
|
+
choices = existingInitChoices(existingConfig, context.machineInspection.value);
|
|
1168
|
+
}
|
|
1169
|
+
else {
|
|
1170
|
+
choices = (await deriveInitChoices(context.discovery)).choices;
|
|
1171
|
+
}
|
|
580
1172
|
}
|
|
581
1173
|
else {
|
|
582
|
-
const needsChoices =
|
|
1174
|
+
const needsChoices = options.reconfigure === true ||
|
|
1175
|
+
context.discovery.configState !== 'valid' ||
|
|
583
1176
|
context.discovery.machineState !== 'valid';
|
|
584
1177
|
if (needsChoices && !interactive) {
|
|
585
|
-
const
|
|
1178
|
+
const existingConfig = context.workspaceInspection.value;
|
|
1179
|
+
const suggested = options.reconfigure === true && existingConfig !== null
|
|
1180
|
+
? existingInitChoices(existingConfig, context.machineInspection.value)
|
|
1181
|
+
: (await deriveInitChoices(context.discovery)).choices;
|
|
1182
|
+
const candidates = {
|
|
1183
|
+
repositories: listRepositoryCandidates(context.discovery).map((candidate) => candidate.key),
|
|
1184
|
+
knowledge: listKnowledgeCandidates(context.discovery).map((candidate) => candidate.path),
|
|
1185
|
+
validation: context.discovery.validationCandidates.map((candidate) => candidate.id),
|
|
1186
|
+
hosts: suggested.hosts,
|
|
1187
|
+
};
|
|
586
1188
|
return writeFailure(output, options, {
|
|
587
1189
|
ok: false,
|
|
588
1190
|
status: 'needs_input',
|
|
589
1191
|
code: diagnosticCodes.initNoTty,
|
|
590
|
-
message:
|
|
1192
|
+
message: options.reconfigure === true
|
|
1193
|
+
? 'Reconfiguration requires editing existing selections in a non-interactive session.'
|
|
1194
|
+
: 'Missing init selections in a non-interactive session.',
|
|
591
1195
|
diagnostics: context.diagnostics,
|
|
592
|
-
nextAction:
|
|
1196
|
+
nextAction: options.reconfigure === true
|
|
1197
|
+
? 'Rerun with --print-choices --reconfigure to inspect current selections, --yes --reconfigure --apply to keep them, --config with pappra/init-choices/1 and --reconfigure for explicit edits, or use a TTY for the wizard.'
|
|
1198
|
+
: 'Rerun with --print-choices to inspect derived selections, --yes --apply to write them, --config with pappra/init-choices/1, or use a TTY for the wizard.',
|
|
593
1199
|
details: {
|
|
594
|
-
candidates
|
|
595
|
-
suggestedChoices:
|
|
596
|
-
rules:
|
|
1200
|
+
candidates,
|
|
1201
|
+
suggestedChoices: suggested,
|
|
1202
|
+
rules: [],
|
|
597
1203
|
},
|
|
598
1204
|
});
|
|
599
1205
|
}
|
|
600
1206
|
if (needsChoices && interactive) {
|
|
601
|
-
choices = await collectInitChoices(context, deps.ui ?? clackWizardUi
|
|
1207
|
+
choices = await collectInitChoices(context, deps.ui ?? clackWizardUi, {
|
|
1208
|
+
...(options.reconfigure === true ? { reconfigure: true } : {}),
|
|
1209
|
+
});
|
|
602
1210
|
if (choices === null) {
|
|
603
1211
|
output.stderr('Init cancelled; no files were written.\n');
|
|
604
1212
|
return 130;
|
|
605
1213
|
}
|
|
606
1214
|
}
|
|
607
1215
|
}
|
|
608
|
-
|
|
1216
|
+
let prepared = await planWorkspaceInit({
|
|
609
1217
|
root: context.root,
|
|
610
1218
|
choices,
|
|
611
1219
|
context,
|
|
1220
|
+
pathObservations: choices === null ? [] : (collectedPathObservations.get(choices) ?? []),
|
|
612
1221
|
...(options.reconfigure === true ? { reconfigure: true } : {}),
|
|
613
1222
|
});
|
|
1223
|
+
const recoverableChoiceError = !prepared.ok &&
|
|
1224
|
+
interactive &&
|
|
1225
|
+
choices !== null &&
|
|
1226
|
+
prepared.diagnostics.some((entry) => entry.severity === 'error' && recoverablePlanCodes.has(entry.code));
|
|
1227
|
+
if (recoverableChoiceError && prepared.ok === false) {
|
|
1228
|
+
const recoverable = prepared;
|
|
1229
|
+
const retained = choices;
|
|
1230
|
+
output.stderr(`pappra init: ${recoverable.message}\n${diagnosticLines(recoverable.diagnostics)}\n`);
|
|
1231
|
+
output.stderr(' next: Return to the editor to correct the reported selections, or cancel; no files were written.\n');
|
|
1232
|
+
const correction = await collectInitChoices(context, deps.ui ?? clackWizardUi, {
|
|
1233
|
+
...(options.reconfigure === true ? { reconfigure: true } : {}),
|
|
1234
|
+
...(retained === null ? {} : { draft: retained }),
|
|
1235
|
+
reviewOnly: true,
|
|
1236
|
+
pathObservations: retained === null
|
|
1237
|
+
? []
|
|
1238
|
+
: (collectedPathObservations.get(retained) ?? []),
|
|
1239
|
+
});
|
|
1240
|
+
if (correction === null) {
|
|
1241
|
+
output.stderr('Init cancelled; no files were written.\n');
|
|
1242
|
+
return 130;
|
|
1243
|
+
}
|
|
1244
|
+
if (retained !== null &&
|
|
1245
|
+
JSON.stringify(correction) === JSON.stringify(retained)) {
|
|
1246
|
+
return formatFailure(recoverable);
|
|
1247
|
+
}
|
|
1248
|
+
choices = correction;
|
|
1249
|
+
prepared = await planWorkspaceInit({
|
|
1250
|
+
root: context.root,
|
|
1251
|
+
choices,
|
|
1252
|
+
context,
|
|
1253
|
+
pathObservations: collectedPathObservations.get(correction) ?? [],
|
|
1254
|
+
...(options.reconfigure === true ? { reconfigure: true } : {}),
|
|
1255
|
+
});
|
|
1256
|
+
if (!prepared.ok)
|
|
1257
|
+
return writeFailure(output, options, prepared);
|
|
1258
|
+
}
|
|
614
1259
|
if (!prepared.ok)
|
|
615
1260
|
return writeFailure(output, options, prepared);
|
|
616
1261
|
const bundle = prepared.bundle;
|
|
1262
|
+
const runtimeOperation = describeInitRuntimeOperation(options, bundle);
|
|
617
1263
|
if (options.json) {
|
|
618
1264
|
if (!options.apply) {
|
|
619
1265
|
output.stdout(`${JSON.stringify({
|
|
620
1266
|
ok: true,
|
|
621
|
-
...planJson(bundle),
|
|
1267
|
+
...planJson(bundle, runtimeOperation),
|
|
622
1268
|
})}\n`);
|
|
623
1269
|
return 0;
|
|
624
1270
|
}
|
|
625
1271
|
const applied = await applyInitPlan(bundle);
|
|
626
|
-
const runtimeRequested =
|
|
1272
|
+
const runtimeRequested = runtimeOperation.runtimeRequested;
|
|
627
1273
|
const runtimeFlow = runtimeRequested && applied.ok && applied.residualPaths.length === 0
|
|
628
1274
|
? wantsRuntime
|
|
629
1275
|
? await installAndRecordRuntime(bundle.scope.workspaceRoot, bundle.scope.runtimeVersion, options)
|
|
@@ -642,32 +1288,36 @@ export async function runInitCommand(options, output, deps = {}) {
|
|
|
642
1288
|
apply: applied,
|
|
643
1289
|
runtime: runtimeFlow?.runtime ?? null,
|
|
644
1290
|
initComplete,
|
|
645
|
-
nextActions:
|
|
1291
|
+
nextActions: initNextActions(bundle, runtimeOperation),
|
|
646
1292
|
diagnostics: [
|
|
647
1293
|
...bundle.diagnostics,
|
|
648
1294
|
...(runtimeFlow?.diagnostics ?? []),
|
|
649
1295
|
],
|
|
650
1296
|
})}\n`);
|
|
651
|
-
if (!applied.ok
|
|
1297
|
+
if (!applied.ok)
|
|
1298
|
+
return applied.status === 'failed' ? 3 : 1;
|
|
1299
|
+
if (applied.residualPaths.length > 0)
|
|
652
1300
|
return 1;
|
|
653
1301
|
if (runtimeRequested)
|
|
654
1302
|
return ok ? 0 : 1;
|
|
655
|
-
return
|
|
1303
|
+
return 0;
|
|
656
1304
|
}
|
|
657
|
-
printPlan(output, bundle);
|
|
1305
|
+
printPlan(output, bundle, runtimeOperation);
|
|
658
1306
|
if (!options.apply) {
|
|
659
1307
|
if (!interactive || bundle.plan.files.length === 0) {
|
|
660
|
-
output.stdout(
|
|
1308
|
+
output.stdout(runtimeOperation.runtimeRequested
|
|
1309
|
+
? 'Plan only; no files or runtime operations were performed. Re-run with --apply to apply files and attempt the described runtime operation, even if no files change.\n'
|
|
1310
|
+
: 'Plan only; no files or runtime operations were performed. Re-run with --apply to apply files; --no-runtime will continue to suppress runtime work.\n');
|
|
661
1311
|
return 0;
|
|
662
1312
|
}
|
|
663
|
-
const confirmed = await promptValue((deps.ui ?? clackWizardUi).confirm(
|
|
1313
|
+
const confirmed = await promptValue((deps.ui ?? clackWizardUi).confirm(runtimeOperation.confirmationMessage, false), deps.ui ?? clackWizardUi);
|
|
664
1314
|
if (confirmed !== true) {
|
|
665
1315
|
output.stderr('Init cancelled; no files were written.\n');
|
|
666
1316
|
return 130;
|
|
667
1317
|
}
|
|
668
1318
|
}
|
|
669
1319
|
const applied = await applyInitPlan(bundle);
|
|
670
|
-
printApply(output, applied,
|
|
1320
|
+
printApply(output, applied, runtimeOperation);
|
|
671
1321
|
if (!applied.ok) {
|
|
672
1322
|
return applied.status === 'failed' ? 3 : 1;
|
|
673
1323
|
}
|
|
@@ -675,50 +1325,65 @@ export async function runInitCommand(options, output, deps = {}) {
|
|
|
675
1325
|
output.stderr('Managed apply state is not final; runtime actions will not be attempted.\n');
|
|
676
1326
|
return 1;
|
|
677
1327
|
}
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
output.stdout(`
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
if (runtimeFlow.runtime.residualPaths.length > 0) {
|
|
685
|
-
output.stderr(`Runtime residual paths: ${runtimeFlow.runtime.residualPaths.join(', ')}\n`);
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
if (!runtimeFlow.ok || !runtimeFlow.completion) {
|
|
689
|
-
output.stderr(`${diagnosticLines(runtimeFlow.diagnostics)}\n`);
|
|
690
|
-
output.stderr('Runtime installation or machine binding did not complete. Initialization is not complete.\n');
|
|
691
|
-
return 1;
|
|
1328
|
+
output.stdout(`${workspaceApplySummary(bundle, applied)}\n`);
|
|
1329
|
+
if (!runtimeOperation.runtimeRequested) {
|
|
1330
|
+
for (const line of deferredInitWork(bundle))
|
|
1331
|
+
output.stdout(` ${line}\n`);
|
|
1332
|
+
for (const action of initNextActions(bundle, runtimeOperation)) {
|
|
1333
|
+
output.stdout(` next: ${action}\n`);
|
|
692
1334
|
}
|
|
693
|
-
output.stdout('Runtime installed and verified. Initialization complete for P04 scope; provider/index preferences were not executed.\n');
|
|
694
1335
|
return 0;
|
|
695
1336
|
}
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
}
|
|
1337
|
+
const runtimeFlow = wantsRuntime
|
|
1338
|
+
? await installAndRecordRuntime(bundle.scope.workspaceRoot, bundle.scope.runtimeVersion, options)
|
|
1339
|
+
: await ensureDefaultRuntime(bundle.scope.workspaceRoot, bundle.scope.runtimeVersion);
|
|
1340
|
+
const runtime = runtimeFlow.runtime;
|
|
1341
|
+
if (runtime !== null) {
|
|
1342
|
+
output.stdout(`Runtime operation result: ${runtime.status} ${runtime.version}${runtime.runtimeRoot === null ? '' : ` at ${runtime.runtimeRoot}`}\n`);
|
|
1343
|
+
if (runtime.residualPaths.length > 0) {
|
|
1344
|
+
output.stderr(`Runtime residual paths: ${runtime.residualPaths.join(', ')}\n`);
|
|
705
1345
|
}
|
|
706
|
-
|
|
1346
|
+
}
|
|
1347
|
+
if (!runtimeFlow.ok || !runtimeFlow.completion) {
|
|
1348
|
+
if (runtimeFlow.diagnostics.length > 0) {
|
|
707
1349
|
output.stderr(`${diagnosticLines(runtimeFlow.diagnostics)}\n`);
|
|
708
|
-
output.stderr('The durable runtime copy could not be installed or bound. Initialization is not complete; --no-runtime leaves this action pending intentionally.\n');
|
|
709
|
-
return 1;
|
|
710
|
-
}
|
|
711
|
-
output.stdout('Durable runtime copy installed and bound. Initialization complete.\n');
|
|
712
|
-
for (const action of advisoryNextActions(bundle)) {
|
|
713
|
-
output.stdout(` next: ${action}\n`);
|
|
714
1350
|
}
|
|
715
|
-
|
|
1351
|
+
output.stderr('Runtime verification/installation or machine binding did not complete. The workspace configuration state reported above remains in place; initialization is incomplete and no rollback was attempted.\n');
|
|
1352
|
+
for (const line of deferredInitWork(bundle))
|
|
1353
|
+
output.stdout(` ${line}\n`);
|
|
1354
|
+
return 1;
|
|
1355
|
+
}
|
|
1356
|
+
output.stdout(runtime?.status === 'reused'
|
|
1357
|
+
? 'Existing runtime was verified, reused and bound. Initialization complete.\n'
|
|
1358
|
+
: 'Runtime was installed, verified and bound. Initialization complete.\n');
|
|
1359
|
+
for (const line of deferredInitWork(bundle))
|
|
1360
|
+
output.stdout(` ${line}\n`);
|
|
1361
|
+
for (const action of initNextActions(bundle, runtimeOperation)) {
|
|
1362
|
+
output.stdout(` next: ${action}\n`);
|
|
716
1363
|
}
|
|
717
|
-
return
|
|
1364
|
+
return 0;
|
|
718
1365
|
}
|
|
719
|
-
export const initUsage = 'pappra init [--config <file>
|
|
1366
|
+
export const initUsage = 'pappra init [--config <file> | --yes | --no-interactive] [--print-choices]\n' +
|
|
1367
|
+
' [--apply] [--json] [--reconfigure] [--no-runtime]\n' +
|
|
720
1368
|
' [--install-runtime (--runtime-tarball <file> | --runtime-version <version>)]\n\n' +
|
|
721
|
-
'Without --config, a TTY opens the
|
|
1369
|
+
'Without --config or --yes, a TTY opens the wizard when selections are still needed: a valid\n' +
|
|
1370
|
+
'existing configuration is reused (only missing local state is requested) and --reconfigure\n' +
|
|
1371
|
+
'opens the editor. --config reads a pappra/init-choices/1 document and stays preview-only\n' +
|
|
1372
|
+
'without --apply, even on a TTY. --yes and --no-interactive derive selections from discovery.\n' +
|
|
722
1373
|
'Planning and preview never write. --apply creates planned files through a managed journal.\n' +
|
|
1374
|
+
'--print-choices prints the selections and writes nothing; with --reconfigure it prints the\n' +
|
|
1375
|
+
'current represented choices instead of newly derived ones, and --yes --reconfigure keeps\n' +
|
|
1376
|
+
'those current choices rather than deriving them again.\n' +
|
|
1377
|
+
'--reconfigure is the explicit opt-in that rewrites an existing valid workspace.json from the\n' +
|
|
1378
|
+
'current selections (identity, extensions, continuity and custom validation bindings are kept).\n' +
|
|
1379
|
+
'Without --reconfigure an existing valid configuration is reused and never overwritten.\n' +
|
|
723
1380
|
'--install-runtime installs one explicit exact version or local tarball; provider/index/global\n' +
|
|
724
|
-
'wiring remain separate pending actions.\n'
|
|
1381
|
+
'wiring remain separate pending actions.\n' +
|
|
1382
|
+
'--no-runtime suppresses runtime verification, installation and binding for this invocation.\n' +
|
|
1383
|
+
'When a runtime action is still required the result is ok:true with status\n' +
|
|
1384
|
+
'external-actions-pending and exit 0 for the requested configuration-only work; when the workspace already has a bound matching\n' +
|
|
1385
|
+
'runtime and no file changes are needed the result is ok:true, apply status no-changes and\n' +
|
|
1386
|
+
'exit 0, while initComplete stays false because this invocation verified no runtime.\n' +
|
|
1387
|
+
'A TTY run that collected selections without --apply prints the plan and asks one confirmation\n' +
|
|
1388
|
+
'when config writes are planned; declining writes nothing. --config previews and empty plans\n' +
|
|
1389
|
+
'do not prompt, and --apply never prompts.\n';
|