mirror-mind 0.0.1 → 0.33.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/hooks/log-assistant-reply.sh +85 -0
- package/.claude/hooks/log-session-end.sh +85 -0
- package/.claude/hooks/log-user-prompt.sh +85 -0
- package/.claude/hooks/mirror-inject.sh +85 -0
- package/.claude/hooks/session-start.sh +85 -0
- package/.gemini/hooks/log-assistant.sh +85 -0
- package/.gemini/hooks/log-user.sh +85 -0
- package/.gemini/hooks/session-end.sh +85 -0
- package/.gemini/hooks/session-start.sh +85 -0
- package/.pi/extensions/mirror-logger.ts +646 -0
- package/.pi/skills/mm-backup/SKILL.md +21 -0
- package/.pi/skills/mm-build/SKILL.md +1206 -0
- package/.pi/skills/mm-consolidate/SKILL.md +112 -0
- package/.pi/skills/mm-consult/SKILL.md +70 -0
- package/.pi/skills/mm-conversations/SKILL.md +16 -0
- package/.pi/skills/mm-discard/SKILL.md +39 -0
- package/.pi/skills/mm-explore/SKILL.md +255 -0
- package/.pi/skills/mm-help/SKILL.md +74 -0
- package/.pi/skills/mm-identity/SKILL.md +96 -0
- package/.pi/skills/mm-journal/SKILL.md +16 -0
- package/.pi/skills/mm-journey/SKILL.md +23 -0
- package/.pi/skills/mm-journeys/SKILL.md +15 -0
- package/.pi/skills/mm-memories/SKILL.md +17 -0
- package/.pi/skills/mm-mirror/SKILL.md +98 -0
- package/.pi/skills/mm-mute/SKILL.md +18 -0
- package/.pi/skills/mm-new/SKILL.md +18 -0
- package/.pi/skills/mm-recall/SKILL.md +17 -0
- package/.pi/skills/mm-release-notes/SKILL.md +52 -0
- package/.pi/skills/mm-seed/SKILL.md +19 -0
- package/.pi/skills/mm-shadow/SKILL.md +117 -0
- package/.pi/skills/mm-soul/SKILL.md +502 -0
- package/.pi/skills/mm-tasks/SKILL.md +24 -0
- package/.pi/skills/mm-update/SKILL.md +32 -0
- package/.pi/skills/mm-week/SKILL.md +28 -0
- package/.pi/skills/mm-welcome/SKILL.md +24 -0
- package/AGENTS.md +188 -0
- package/README.md +126 -9
- package/bin/loader.js +67 -0
- package/bin/mirror-hook.js +7 -0
- package/bin/mirror.js +7 -0
- package/docs/releases/index.md +100 -0
- package/docs/releases/pending-cutoffs.md +510 -0
- package/docs/releases/v0.10.0.md +123 -0
- package/docs/releases/v0.10.1.md +66 -0
- package/docs/releases/v0.10.10.md +51 -0
- package/docs/releases/v0.10.11.md +39 -0
- package/docs/releases/v0.10.12.md +57 -0
- package/docs/releases/v0.10.13.md +35 -0
- package/docs/releases/v0.10.14.md +42 -0
- package/docs/releases/v0.10.15.md +53 -0
- package/docs/releases/v0.10.16.md +35 -0
- package/docs/releases/v0.10.17.md +43 -0
- package/docs/releases/v0.10.2.md +64 -0
- package/docs/releases/v0.10.3.md +78 -0
- package/docs/releases/v0.10.4.md +42 -0
- package/docs/releases/v0.10.5.md +57 -0
- package/docs/releases/v0.10.6.md +47 -0
- package/docs/releases/v0.10.7.md +53 -0
- package/docs/releases/v0.10.8.md +49 -0
- package/docs/releases/v0.10.9.md +37 -0
- package/docs/releases/v0.11.0.md +86 -0
- package/docs/releases/v0.12.0.md +67 -0
- package/docs/releases/v0.13.0.md +64 -0
- package/docs/releases/v0.14.0.md +68 -0
- package/docs/releases/v0.15.0.md +48 -0
- package/docs/releases/v0.16.0.md +50 -0
- package/docs/releases/v0.16.1.md +55 -0
- package/docs/releases/v0.16.2.md +36 -0
- package/docs/releases/v0.17.0.md +50 -0
- package/docs/releases/v0.17.1.md +58 -0
- package/docs/releases/v0.17.2.md +42 -0
- package/docs/releases/v0.17.3.md +58 -0
- package/docs/releases/v0.18.0.md +84 -0
- package/docs/releases/v0.18.1.md +51 -0
- package/docs/releases/v0.18.2.md +69 -0
- package/docs/releases/v0.19.0.md +85 -0
- package/docs/releases/v0.19.1.md +46 -0
- package/docs/releases/v0.20.0.md +85 -0
- package/docs/releases/v0.21.0.md +61 -0
- package/docs/releases/v0.21.1.md +47 -0
- package/docs/releases/v0.22.0.md +77 -0
- package/docs/releases/v0.22.1.md +42 -0
- package/docs/releases/v0.23.0.md +105 -0
- package/docs/releases/v0.24.0.md +125 -0
- package/docs/releases/v0.25.0.md +107 -0
- package/docs/releases/v0.26.0.md +139 -0
- package/docs/releases/v0.26.1.md +65 -0
- package/docs/releases/v0.26.2.md +43 -0
- package/docs/releases/v0.27.0.md +63 -0
- package/docs/releases/v0.28.0.md +59 -0
- package/docs/releases/v0.29.0.md +61 -0
- package/docs/releases/v0.29.1.md +59 -0
- package/docs/releases/v0.29.2.md +59 -0
- package/docs/releases/v0.29.3.md +93 -0
- package/docs/releases/v0.30.0.md +75 -0
- package/docs/releases/v0.30.1.md +93 -0
- package/docs/releases/v0.30.2.md +66 -0
- package/docs/releases/v0.31.0.md +122 -0
- package/docs/releases/v0.31.1.md +142 -0
- package/docs/releases/v0.31.10.md +118 -0
- package/docs/releases/v0.31.11.md +88 -0
- package/docs/releases/v0.31.12.md +105 -0
- package/docs/releases/v0.31.13.md +99 -0
- package/docs/releases/v0.31.14.md +97 -0
- package/docs/releases/v0.31.2.md +115 -0
- package/docs/releases/v0.31.3.md +142 -0
- package/docs/releases/v0.31.4.md +117 -0
- package/docs/releases/v0.31.5.md +155 -0
- package/docs/releases/v0.31.6.md +132 -0
- package/docs/releases/v0.31.7.md +132 -0
- package/docs/releases/v0.31.8.md +120 -0
- package/docs/releases/v0.31.9.md +120 -0
- package/docs/releases/v0.32.0.md +261 -0
- package/docs/releases/v0.32.1.md +59 -0
- package/docs/releases/v0.33.0.md +164 -0
- package/docs/releases/v0.8.0.md +118 -0
- package/docs/releases/v0.9.0.md +98 -0
- package/docs/releases/v0.9.1.md +83 -0
- package/package.json +102 -6
- package/plugins/mirror-mind/.claude-plugin/plugin.json +17 -0
- package/plugins/mirror-mind/hooks/hooks.json +62 -0
- package/plugins/mirror-mind/hooks/log-assistant-reply.sh +98 -0
- package/plugins/mirror-mind/hooks/log-session-end.sh +98 -0
- package/plugins/mirror-mind/hooks/log-user-prompt.sh +98 -0
- package/plugins/mirror-mind/hooks/mirror-inject.sh +98 -0
- package/plugins/mirror-mind/hooks/session-start.sh +98 -0
- package/plugins/mirror-mind/skills/mm-backup/SKILL.md +27 -0
- package/plugins/mirror-mind/skills/mm-build/SKILL.md +1206 -0
- package/plugins/mirror-mind/skills/mm-consolidate/SKILL.md +112 -0
- package/plugins/mirror-mind/skills/mm-consult/SKILL.md +83 -0
- package/plugins/mirror-mind/skills/mm-conversations/SKILL.md +17 -0
- package/plugins/mirror-mind/skills/mm-discard/SKILL.md +40 -0
- package/plugins/mirror-mind/skills/mm-explore/SKILL.md +241 -0
- package/plugins/mirror-mind/skills/mm-help/SKILL.md +104 -0
- package/plugins/mirror-mind/skills/mm-identity/SKILL.md +96 -0
- package/plugins/mirror-mind/skills/mm-journal/SKILL.md +21 -0
- package/plugins/mirror-mind/skills/mm-journey/SKILL.md +37 -0
- package/plugins/mirror-mind/skills/mm-journeys/SKILL.md +15 -0
- package/plugins/mirror-mind/skills/mm-memories/SKILL.md +20 -0
- package/plugins/mirror-mind/skills/mm-mirror/SKILL.md +67 -0
- package/plugins/mirror-mind/skills/mm-mute/SKILL.md +18 -0
- package/plugins/mirror-mind/skills/mm-new/SKILL.md +18 -0
- package/plugins/mirror-mind/skills/mm-recall/SKILL.md +17 -0
- package/plugins/mirror-mind/skills/mm-release-notes/SKILL.md +31 -0
- package/plugins/mirror-mind/skills/mm-seed/SKILL.md +15 -0
- package/plugins/mirror-mind/skills/mm-shadow/SKILL.md +117 -0
- package/plugins/mirror-mind/skills/mm-soul/SKILL.md +488 -0
- package/plugins/mirror-mind/skills/mm-tasks/SKILL.md +36 -0
- package/plugins/mirror-mind/skills/mm-update/SKILL.md +30 -0
- package/plugins/mirror-mind/skills/mm-week/SKILL.md +46 -0
- package/plugins/mirror-mind/skills/mm-welcome/SKILL.md +22 -0
- package/scripts/codex-hooks/session-end.sh +85 -0
- package/scripts/codex-hooks/session-start.sh +85 -0
- package/scripts/codex-mirror.sh +48 -0
- package/templates/identity/README.md +119 -0
- package/templates/identity/ego/behavior.yaml +52 -0
- package/templates/identity/ego/constraints.yaml +15 -0
- package/templates/identity/ego/identity.yaml +43 -0
- package/templates/identity/journeys/personal-growth.yaml +39 -0
- package/templates/identity/organization/identity.yaml +24 -0
- package/templates/identity/organization/principles.yaml +17 -0
- package/templates/identity/personas/coach.yaml +94 -0
- package/templates/identity/personas/designer.yaml +93 -0
- package/templates/identity/personas/doctor.yaml +96 -0
- package/templates/identity/personas/engineer.yaml +77 -0
- package/templates/identity/personas/financial.yaml +97 -0
- package/templates/identity/personas/prompt-engineer.yaml +122 -0
- package/templates/identity/personas/researcher.yaml +88 -0
- package/templates/identity/personas/strategist.yaml +91 -0
- package/templates/identity/personas/teacher.yaml +95 -0
- package/templates/identity/personas/therapist.yaml +91 -0
- package/templates/identity/personas/thinker.yaml +74 -0
- package/templates/identity/personas/writer.yaml +76 -0
- package/templates/identity/self/config.yaml +16 -0
- package/templates/identity/self/soul.yaml +105 -0
- package/templates/identity/user/identity.yaml +30 -0
- package/ts/src/backup/staging.ts +62 -0
- package/ts/src/backup/zipBackup.ts +267 -0
- package/ts/src/backup/zipReader.ts +109 -0
- package/ts/src/backup/zipWriter.ts +148 -0
- package/ts/src/builder/activeCheckpoint.ts +134 -0
- package/ts/src/builder/aggregateStatus.ts +34 -0
- package/ts/src/builder/approve.ts +149 -0
- package/ts/src/builder/argv.ts +292 -0
- package/ts/src/builder/ariadMethod.ts +536 -0
- package/ts/src/builder/artifacts/artifactSurfaces.ts +106 -0
- package/ts/src/builder/artifacts/artifactWriter.ts +113 -0
- package/ts/src/builder/artifacts/closureArtifacts.ts +199 -0
- package/ts/src/builder/artifacts/deliveryStoryArtifacts.ts +316 -0
- package/ts/src/builder/artifacts/planArtifacts.ts +242 -0
- package/ts/src/builder/artifacts/scaffoldSections.ts +273 -0
- package/ts/src/builder/artifacts/scaffoldState.ts +227 -0
- package/ts/src/builder/artifacts/storyFiles.ts +61 -0
- package/ts/src/builder/artifacts/storyIndex.ts +166 -0
- package/ts/src/builder/cadence.ts +59 -0
- package/ts/src/builder/card.ts +192 -0
- package/ts/src/builder/checkpointRefused.ts +66 -0
- package/ts/src/builder/closure.ts +964 -0
- package/ts/src/builder/commands.ts +2053 -0
- package/ts/src/builder/cursorPosition.ts +176 -0
- package/ts/src/builder/cursorTransitions.ts +77 -0
- package/ts/src/builder/deliveryCursor.ts +577 -0
- package/ts/src/builder/deliveryStoryClosure.ts +521 -0
- package/ts/src/builder/deliveryStoryPlan.ts +536 -0
- package/ts/src/builder/deliveryStoryReady.ts +160 -0
- package/ts/src/builder/deliveryStoryRoadmapClosure.ts +151 -0
- package/ts/src/builder/expand.ts +336 -0
- package/ts/src/builder/flowUnit.ts +339 -0
- package/ts/src/builder/homeSurface.ts +122 -0
- package/ts/src/builder/implementationGuard.ts +125 -0
- package/ts/src/builder/index.ts +11 -0
- package/ts/src/builder/lifecycleRefusal.ts +92 -0
- package/ts/src/builder/lifecycleRibbon.ts +159 -0
- package/ts/src/builder/load.ts +313 -0
- package/ts/src/builder/methodAdoption.ts +113 -0
- package/ts/src/builder/methodDefinition.ts +429 -0
- package/ts/src/builder/methodInspection.ts +371 -0
- package/ts/src/builder/plan.ts +408 -0
- package/ts/src/builder/planPreauthorization.ts +265 -0
- package/ts/src/builder/prepare.ts +225 -0
- package/ts/src/builder/projectPaths.ts +64 -0
- package/ts/src/builder/pull.ts +356 -0
- package/ts/src/builder/pullCandidates.ts +463 -0
- package/ts/src/builder/pullCandidatesRender.ts +238 -0
- package/ts/src/builder/refinementField.ts +93 -0
- package/ts/src/builder/releaseIntent.ts +173 -0
- package/ts/src/builder/resumeState.ts +84 -0
- package/ts/src/builder/resumeSurface.ts +149 -0
- package/ts/src/builder/roadmapGrammar.ts +300 -0
- package/ts/src/builder/roadmapScan.ts +97 -0
- package/ts/src/builder/roadmapScope.ts +210 -0
- package/ts/src/builder/scopePhrases.ts +146 -0
- package/ts/src/builder/storyPaths.ts +196 -0
- package/ts/src/builder/storyPlanPreauthorization.ts +287 -0
- package/ts/src/builder/surfaceProtocol.ts +42 -0
- package/ts/src/builder/templateGeneration.ts +148 -0
- package/ts/src/builder/transition.ts +178 -0
- package/ts/src/consult/args.ts +99 -0
- package/ts/src/consult/core.ts +122 -0
- package/ts/src/consult/modelCatalog.ts +55 -0
- package/ts/src/consult/render.ts +47 -0
- package/ts/src/conversation/append.ts +414 -0
- package/ts/src/conversation/backfill.ts +387 -0
- package/ts/src/conversation/closeTail.ts +467 -0
- package/ts/src/conversation/extraction.ts +333 -0
- package/ts/src/conversation/extractionDriver.ts +130 -0
- package/ts/src/conversation/extractionRun.ts +70 -0
- package/ts/src/conversation/hookPayload.ts +51 -0
- package/ts/src/conversation/journeyInference.ts +165 -0
- package/ts/src/conversation/journeyRepair.ts +114 -0
- package/ts/src/conversation/lifecycleFaces.ts +197 -0
- package/ts/src/conversation/lifecycleWrites.ts +313 -0
- package/ts/src/conversation/listing.ts +77 -0
- package/ts/src/conversation/logger.ts +851 -0
- package/ts/src/conversation/loggerCli.ts +321 -0
- package/ts/src/conversation/loggerRuntime.ts +259 -0
- package/ts/src/conversation/metadataLifecycle.ts +443 -0
- package/ts/src/conversation/recall.ts +66 -0
- package/ts/src/conversation/sessionComposites.ts +258 -0
- package/ts/src/conversation/sessionImport.ts +90 -0
- package/ts/src/conversation/transcriptBackfill.ts +141 -0
- package/ts/src/cultivation/applyActions.ts +241 -0
- package/ts/src/cultivation/cluster.ts +86 -0
- package/ts/src/cultivation/consolidationStore.ts +281 -0
- package/ts/src/cultivation/promptContext.ts +62 -0
- package/ts/src/cultivation/propose.ts +305 -0
- package/ts/src/cultivation/scan.ts +155 -0
- package/ts/src/db/backupGate.ts +47 -0
- package/ts/src/db/bootstrap.ts +108 -0
- package/ts/src/db/bootstrapLock.ts +223 -0
- package/ts/src/db/copyGuard.ts +60 -0
- package/ts/src/db/database.ts +397 -0
- package/ts/src/db/decode.ts +66 -0
- package/ts/src/db/ftsIntegrity.ts +41 -0
- package/ts/src/db/migrateOnOpen.ts +214 -0
- package/ts/src/db/migrations.ts +576 -0
- package/ts/src/db/readyOnOpen.ts +35 -0
- package/ts/src/db/rowDecode.ts +42 -0
- package/ts/src/db/schema.ts +432 -0
- package/ts/src/db/schemaInventory.ts +225 -0
- package/ts/src/db/schemaInventorySnapshot.ts +2107 -0
- package/ts/src/db/schemaState.ts +91 -0
- package/ts/src/db/schemaTsDivergence.ts +114 -0
- package/ts/src/descriptor/descriptorRead.ts +49 -0
- package/ts/src/docs/docsLint.ts +265 -0
- package/ts/src/explorer/handoff.ts +522 -0
- package/ts/src/explorer/render.ts +335 -0
- package/ts/src/explorer/story.ts +769 -0
- package/ts/src/explorer/transition.ts +72 -0
- package/ts/src/extensions/bindings.ts +170 -0
- package/ts/src/extensions/catalog.ts +249 -0
- package/ts/src/extensions/catalogCommands.ts +513 -0
- package/ts/src/extensions/catalogWrites.ts +652 -0
- package/ts/src/extensions/contextRuntime.ts +325 -0
- package/ts/src/extensions/dispatch.ts +374 -0
- package/ts/src/extensions/errors.ts +24 -0
- package/ts/src/extensions/manifest.ts +356 -0
- package/ts/src/extensions/migrations.ts +350 -0
- package/ts/src/extraction/conversation.ts +304 -0
- package/ts/src/extraction/conversationMetadata.ts +206 -0
- package/ts/src/extraction/fencing.ts +106 -0
- package/ts/src/extraction/json.ts +17 -0
- package/ts/src/extraction/prompts.ts +470 -0
- package/ts/src/frontDoor/args.ts +30 -0
- package/ts/src/frontDoor/argvShape.ts +106 -0
- package/ts/src/frontDoor/buildLoadRuntime.ts +139 -0
- package/ts/src/frontDoor/buildRoute.ts +398 -0
- package/ts/src/frontDoor/cli.ts +1872 -0
- package/ts/src/frontDoor/cloneRoleGuard.ts +98 -0
- package/ts/src/frontDoor/consultRoute.ts +50 -0
- package/ts/src/frontDoor/contentTailRoute.ts +239 -0
- package/ts/src/frontDoor/conversationLoggerRoute.ts +83 -0
- package/ts/src/frontDoor/cultivationRoute.ts +210 -0
- package/ts/src/frontDoor/dbPath.ts +131 -0
- package/ts/src/frontDoor/dbSafetyToolsRoute.ts +240 -0
- package/ts/src/frontDoor/exploreRoute.ts +572 -0
- package/ts/src/frontDoor/extensionCatalogRoute.ts +252 -0
- package/ts/src/frontDoor/frontDoorLog.ts +57 -0
- package/ts/src/frontDoor/identityWrite.ts +36 -0
- package/ts/src/frontDoor/journeyWriteRoute.ts +26 -0
- package/ts/src/frontDoor/lifecycleWriteRoute.ts +110 -0
- package/ts/src/frontDoor/liveBackup.ts +100 -0
- package/ts/src/frontDoor/mirrorModeRoute.ts +163 -0
- package/ts/src/frontDoor/namedRefusal.ts +35 -0
- package/ts/src/frontDoor/nodeSupport.ts +24 -0
- package/ts/src/frontDoor/render/consolidate.ts +199 -0
- package/ts/src/frontDoor/render/conversations.ts +33 -0
- package/ts/src/frontDoor/render/cultivationShared.ts +21 -0
- package/ts/src/frontDoor/render/descriptor.ts +20 -0
- package/ts/src/frontDoor/render/detectPersona.ts +50 -0
- package/ts/src/frontDoor/render/icons.ts +13 -0
- package/ts/src/frontDoor/render/identity.ts +59 -0
- package/ts/src/frontDoor/render/identityRows.ts +17 -0
- package/ts/src/frontDoor/render/inspectPersona.ts +53 -0
- package/ts/src/frontDoor/render/journeyStatus.ts +78 -0
- package/ts/src/frontDoor/render/journeys.ts +79 -0
- package/ts/src/frontDoor/render/list.ts +41 -0
- package/ts/src/frontDoor/render/memories.ts +67 -0
- package/ts/src/frontDoor/render/recall.ts +49 -0
- package/ts/src/frontDoor/render/shadow.ts +155 -0
- package/ts/src/frontDoor/render/tasks.ts +99 -0
- package/ts/src/frontDoor/render/tasksImportSync.ts +63 -0
- package/ts/src/frontDoor/render/week.ts +113 -0
- package/ts/src/frontDoor/routing.ts +1001 -0
- package/ts/src/frontDoor/runtimeRoute.ts +623 -0
- package/ts/src/frontDoor/searchRoute.ts +166 -0
- package/ts/src/frontDoor/seedPaths.ts +98 -0
- package/ts/src/frontDoor/sessionStartWelcomeRoute.ts +166 -0
- package/ts/src/frontDoor/soulRoute.ts +433 -0
- package/ts/src/frontDoor/stdinSource.ts +37 -0
- package/ts/src/frontDoor/tasksImportSyncRoute.ts +110 -0
- package/ts/src/frontDoor/tasksWriteRoute.ts +101 -0
- package/ts/src/frontDoor/usage.ts +176 -0
- package/ts/src/guards/claudePlugin.ts +298 -0
- package/ts/src/guards/packContents.ts +306 -0
- package/ts/src/guards/piSourcedSkills.ts +70 -0
- package/ts/src/guards/retiredRouteShadows.ts +57 -0
- package/ts/src/guards/retiredSurfaces.ts +564 -0
- package/ts/src/hooks/claude.ts +214 -0
- package/ts/src/hooks/codex.ts +45 -0
- package/ts/src/hooks/gemini.ts +126 -0
- package/ts/src/hooks/main.ts +109 -0
- package/ts/src/hooks/mirrorState.ts +71 -0
- package/ts/src/hooks/payload.ts +73 -0
- package/ts/src/hooks/runtime.ts +179 -0
- package/ts/src/hooks/runtimeDetection.ts +34 -0
- package/ts/src/identity/applyConsolidationIdentityUpdate.ts +67 -0
- package/ts/src/identity/descriptorGenerate.ts +162 -0
- package/ts/src/identity/identityEdit.ts +143 -0
- package/ts/src/identity/identityKey.ts +77 -0
- package/ts/src/identity/identityRead.ts +69 -0
- package/ts/src/identity/identityStore.ts +80 -0
- package/ts/src/identity/journeyListing.ts +53 -0
- package/ts/src/identity/personaInspect.ts +44 -0
- package/ts/src/identity/personaListing.ts +37 -0
- package/ts/src/identity/setIdentity.ts +64 -0
- package/ts/src/index.ts +231 -0
- package/ts/src/init/init.ts +111 -0
- package/ts/src/journey/journeyOptions.ts +131 -0
- package/ts/src/journey/journeyRemoval.ts +158 -0
- package/ts/src/journey/journeyStatus.ts +149 -0
- package/ts/src/journey/journeySyncFile.ts +87 -0
- package/ts/src/journey/journeyWrite.ts +203 -0
- package/ts/src/journey/parentJourney.ts +41 -0
- package/ts/src/journey/validateParentJourney.ts +55 -0
- package/ts/src/mcp/boundary.ts +151 -0
- package/ts/src/mcp/guards.ts +244 -0
- package/ts/src/mcp/main.ts +121 -0
- package/ts/src/mcp/payload.ts +141 -0
- package/ts/src/mcp/protocol.ts +141 -0
- package/ts/src/mcp/readModels.ts +241 -0
- package/ts/src/mcp/registry.ts +201 -0
- package/ts/src/mcp/serve.ts +120 -0
- package/ts/src/mcp/tools/deterministic.ts +137 -0
- package/ts/src/mcp/tools/providerCrossing.ts +187 -0
- package/ts/src/mcp/wire.ts +8 -0
- package/ts/src/memory/journal.ts +180 -0
- package/ts/src/memory/listing.ts +107 -0
- package/ts/src/memory/memoryWrite.ts +88 -0
- package/ts/src/memory/reinforcement.ts +35 -0
- package/ts/src/mirror/context.ts +161 -0
- package/ts/src/mirror/defaultResolution.ts +199 -0
- package/ts/src/mirror/mirrorForm.ts +43 -0
- package/ts/src/mirror/orchestration.ts +364 -0
- package/ts/src/mirror/reception.ts +194 -0
- package/ts/src/mirror/render.ts +75 -0
- package/ts/src/mirror/runtimeSession.ts +205 -0
- package/ts/src/mode/operatingMode.ts +148 -0
- package/ts/src/observability/callOutcome.ts +115 -0
- package/ts/src/observability/inspectLedger.ts +329 -0
- package/ts/src/observability/ledgerHooks.ts +126 -0
- package/ts/src/observability/llmCalls.ts +154 -0
- package/ts/src/persona/detectPersona.ts +81 -0
- package/ts/src/planning/promptAssembly.ts +111 -0
- package/ts/src/planning/weekPending.ts +81 -0
- package/ts/src/planning/weekPlan.ts +183 -0
- package/ts/src/planning/weekSave.ts +117 -0
- package/ts/src/providers/config.ts +189 -0
- package/ts/src/providers/cost.ts +64 -0
- package/ts/src/providers/credits.ts +204 -0
- package/ts/src/providers/embedding.ts +321 -0
- package/ts/src/providers/familyProviders.ts +147 -0
- package/ts/src/providers/llm.ts +334 -0
- package/ts/src/providers/openrouter.ts +244 -0
- package/ts/src/providers/redaction.ts +110 -0
- package/ts/src/providers/replay.ts +15 -0
- package/ts/src/providers/transport.ts +410 -0
- package/ts/src/repair/encodingRepair.ts +253 -0
- package/ts/src/runtime/backup.ts +151 -0
- package/ts/src/runtime/channel.ts +146 -0
- package/ts/src/runtime/config.ts +168 -0
- package/ts/src/runtime/diagnose.ts +686 -0
- package/ts/src/runtime/git.ts +510 -0
- package/ts/src/runtime/installKind.ts +220 -0
- package/ts/src/runtime/migrate.ts +154 -0
- package/ts/src/runtime/packageIdentity.ts +102 -0
- package/ts/src/runtime/releaseNotes.ts +244 -0
- package/ts/src/runtime/status.ts +557 -0
- package/ts/src/runtime/strategies/clone.ts +200 -0
- package/ts/src/runtime/strategies/package.ts +328 -0
- package/ts/src/runtime/treeRoot.ts +30 -0
- package/ts/src/runtime/update.ts +445 -0
- package/ts/src/runtime/updateGate.ts +40 -0
- package/ts/src/runtime/updatePipeline.ts +159 -0
- package/ts/src/runtime/version.ts +36 -0
- package/ts/src/runtime/wiring.ts +130 -0
- package/ts/src/search/memorySearch.ts +338 -0
- package/ts/src/search/ranker.ts +154 -0
- package/ts/src/seed/seed.ts +256 -0
- package/ts/src/seed/seedYaml.ts +133 -0
- package/ts/src/soul/apply.ts +218 -0
- package/ts/src/soul/harvest.ts +267 -0
- package/ts/src/soul/prompts/soul_beauty_voice.md +103 -0
- package/ts/src/soul/prompts/soul_self_voice.md +219 -0
- package/ts/src/soul/prompts/soul_wisdom_voice.md +121 -0
- package/ts/src/soul/prompts.ts +85 -0
- package/ts/src/soul/render.ts +358 -0
- package/ts/src/soul/state.ts +212 -0
- package/ts/src/soul/transition.ts +37 -0
- package/ts/src/tasks/journeyPathParse.ts +113 -0
- package/ts/src/tasks/taskImportSync.ts +142 -0
- package/ts/src/tasks/taskStore.ts +312 -0
- package/ts/src/tasks/weekView.ts +120 -0
- package/ts/src/util/clipCodePoints.ts +26 -0
- package/ts/src/util/paths.ts +63 -0
- package/ts/src/util/program.ts +9 -0
- package/ts/src/util/pyGenerators.ts +144 -0
- package/ts/src/util/pythonText.ts +317 -0
- package/ts/src/util/semver.ts +45 -0
- package/ts/src/util/shellWord.ts +25 -0
- package/ts/src/util/slug.ts +64 -0
- package/ts/src/welcome/card.ts +339 -0
- package/ts/src/welcome/statusLine.ts +101 -0
- package/ts/src/welcome/updateCache.ts +129 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
// CR002 — the surface vocabulary for a journey's roadmap scope.
|
|
2
|
+
//
|
|
3
|
+
// Every Builder surface that answers "where are we" or "what next" states the
|
|
4
|
+
// scope in the same words. They are behavioral contracts rather than prose: the
|
|
5
|
+
// agent reads them to decide its next move, and the frozen goldens pin their
|
|
6
|
+
// bytes. So they live here, once, and every renderer composes them instead of
|
|
7
|
+
// spelling its own near-synonym. That includes the focus a surface shows
|
|
8
|
+
// (`scopeFocus`), because a focus the roadmap cannot supply is stated in these
|
|
9
|
+
// same words.
|
|
10
|
+
//
|
|
11
|
+
// `no item pulled yet` names a beginning, not a loss: the unscoped state means
|
|
12
|
+
// exactly that no item has been pulled since the cursor was last synchronized
|
|
13
|
+
// (see `roadmapScope.ts`). `none` stays only in fields that are genuinely empty.
|
|
14
|
+
|
|
15
|
+
import { PROGRAM } from "#util/program.ts";
|
|
16
|
+
import { shellWord } from "#util/shellWord.ts";
|
|
17
|
+
import type { RoadmapSnapshotItem } from "./pullCandidates.ts";
|
|
18
|
+
import type {
|
|
19
|
+
AuthoredPackage,
|
|
20
|
+
RoadmapScope,
|
|
21
|
+
ScopedPullCandidates,
|
|
22
|
+
ScopePosition,
|
|
23
|
+
} from "./roadmapScope.ts";
|
|
24
|
+
|
|
25
|
+
/** The value of every position row when the journey is unscoped. */
|
|
26
|
+
export const NO_ITEM_PULLED_YET = "no item pulled yet";
|
|
27
|
+
|
|
28
|
+
/** The header of a candidate list that no journey scope narrows. */
|
|
29
|
+
export const PROJECT_WIDE_CANDIDATES = "project-wide candidates";
|
|
30
|
+
|
|
31
|
+
export const NO_AUTHORED_PACKAGE = "no authored package";
|
|
32
|
+
export const NO_PROJECT_PATH = "no project path configured";
|
|
33
|
+
export const claimedBy = (count: number) => `claimed by ${count} packages`;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* What stands where a recommendation would when there is none: the literal Pull
|
|
37
|
+
* command, carrying the journey's real slug and placeholders the Navigator fills
|
|
38
|
+
* from the item they name. The command itself, not a description of one, because
|
|
39
|
+
* the agent copies it, so the slug goes through `shellWord` (CR104).
|
|
40
|
+
*/
|
|
41
|
+
export function pullExplicitly(journey: string): string {
|
|
42
|
+
return (
|
|
43
|
+
`pull explicitly: ${PROGRAM} build pull-item --journey ${shellWord(journey)} --method ariad ` +
|
|
44
|
+
`--item-code <code> --item-title "<title>" --item-level <level> --why-now "<why now>"`
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* The read-only command that shows a journey's active checkpoint (CR067 with CR020).
|
|
50
|
+
* A refusal prints it to be copied, so it is quoted exactly as the Pull command is.
|
|
51
|
+
*/
|
|
52
|
+
export function showCheckpoint(journey: string): string {
|
|
53
|
+
return `${PROGRAM} build show --journey ${shellWord(journey)} --method ariad`;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** The header of every candidate list: whose candidates these are. */
|
|
57
|
+
export function candidateListHeader(scope: RoadmapScope): string {
|
|
58
|
+
return scope.kind === "unscoped" ? PROJECT_WIDE_CANDIDATES : `candidates in ${scope.cvCode}`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** The one line a scoped list adds for the candidates it leaves out, when it leaves any. */
|
|
62
|
+
export function outsideCountLines(view: ScopedPullCandidates): string[] {
|
|
63
|
+
if (view.scope.kind === "unscoped" || view.outsideCount === 0) return [];
|
|
64
|
+
return [`${view.outsideCount} more outside ${view.scope.cvCode}`];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* The paragraphs that stand where a recommendation would when there is none:
|
|
69
|
+
* why, for a scoped CV with nothing left, then the literal command. Unscoped, the
|
|
70
|
+
* command alone, because the position rows already say no item was pulled.
|
|
71
|
+
*/
|
|
72
|
+
export function noRecommendationLines(view: ScopedPullCandidates): string[] {
|
|
73
|
+
const pull = pullExplicitly(view.journey);
|
|
74
|
+
if (view.scope.kind === "unscoped") return [pull];
|
|
75
|
+
return [`no remaining candidates in ${view.scope.cvCode}`, pull];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* `CV22 — <title> (<status>) [<path>]`, the pre-CR002 format; no `()` when there is no
|
|
80
|
+
* status. The status is the package's clause (CR103): this row once printed CV22's
|
|
81
|
+
* whole changelog, twenty card lines, on every load.
|
|
82
|
+
*/
|
|
83
|
+
function formatPackage(authored: AuthoredPackage): string {
|
|
84
|
+
const status = authored.status ? ` (${authored.status})` : "";
|
|
85
|
+
return `${authored.code} — ${authored.title}${status} [${authored.path}]`;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* The `roadmap position` row of `■ BUILDER RESUME`, one entry per paragraph (each
|
|
90
|
+
* is wrapped onto its own card lines). Every fallback states what is missing or
|
|
91
|
+
* wrong; none of them throws and none of them guesses.
|
|
92
|
+
*/
|
|
93
|
+
export function roadmapPositionLines(scope: RoadmapScope): string[] {
|
|
94
|
+
if (scope.kind === "unscoped") return [NO_ITEM_PULLED_YET];
|
|
95
|
+
const { position, cvCode, activeItem } = scope;
|
|
96
|
+
switch (position.kind) {
|
|
97
|
+
case "cv_package":
|
|
98
|
+
return [formatPackage(position.package)];
|
|
99
|
+
case "item_package":
|
|
100
|
+
return [formatPackage(position.package), `${NO_AUTHORED_PACKAGE} for ${cvCode}`];
|
|
101
|
+
case "no_package":
|
|
102
|
+
return [
|
|
103
|
+
activeItem === cvCode
|
|
104
|
+
? `${NO_AUTHORED_PACKAGE} for ${cvCode}`
|
|
105
|
+
: `${NO_AUTHORED_PACKAGE} for ${cvCode} or ${activeItem}`,
|
|
106
|
+
];
|
|
107
|
+
case "ambiguous":
|
|
108
|
+
return [
|
|
109
|
+
`${position.code} is ${claimedBy(position.paths.length)}: ${position.paths.join(", ")}`,
|
|
110
|
+
];
|
|
111
|
+
case "no_project":
|
|
112
|
+
return [NO_PROJECT_PATH];
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* The roadmap focus a surface shows as "where are we": the roadmap index row for
|
|
118
|
+
* the journey's CV, else the CV package's heading, else the position stated in
|
|
119
|
+
* this vocabulary. `null` when unscoped.
|
|
120
|
+
*
|
|
121
|
+
* The index row wins because it is what the focus showed before CR002, for the
|
|
122
|
+
* same CV; the change is WHICH CV, not where its title is read from.
|
|
123
|
+
*/
|
|
124
|
+
export function scopeFocus(
|
|
125
|
+
items: readonly RoadmapSnapshotItem[],
|
|
126
|
+
scope: RoadmapScope,
|
|
127
|
+
): RoadmapSnapshotItem | null {
|
|
128
|
+
if (scope.kind === "unscoped") return null;
|
|
129
|
+
const row = items.find((item) => item.code === scope.cvCode);
|
|
130
|
+
if (row !== undefined) return row;
|
|
131
|
+
const position = scope.position;
|
|
132
|
+
if (position.kind === "cv_package") {
|
|
133
|
+
const { code, title, status } = position.package;
|
|
134
|
+
return { code, title, status };
|
|
135
|
+
}
|
|
136
|
+
return { code: scope.cvCode, title: placeholderTitle(scope.cvCode, position), status: "" };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** The focus title for a CV the roadmap cannot describe; the focus row already names the CV. */
|
|
140
|
+
function placeholderTitle(cvCode: string, position: ScopePosition): string {
|
|
141
|
+
if (position.kind === "no_project") return NO_PROJECT_PATH;
|
|
142
|
+
if (position.kind === "ambiguous" && position.code === cvCode) {
|
|
143
|
+
return claimedBy(position.paths.length);
|
|
144
|
+
}
|
|
145
|
+
return NO_AUTHORED_PACKAGE;
|
|
146
|
+
}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
// CV22.DS7.US8 plateau 1 — resolve-then-create for roadmap story packages.
|
|
2
|
+
//
|
|
3
|
+
// Port of `src/memory/builder/story_paths.py`.
|
|
4
|
+
//
|
|
5
|
+
// The rule that matters: an EXISTING package is located by its own heading
|
|
6
|
+
// (`# <code> — <title>`), never by a folder-naming convention or by arithmetic
|
|
7
|
+
// on code+title. An authored package can live under any folder a human chose,
|
|
8
|
+
// and what makes it `CV2.DS1` is its heading. Python learned this twice —
|
|
9
|
+
// CR048 in DS6.TS5, recurring in CV22.DS7 — so the resolver is shared by every
|
|
10
|
+
// call site rather than re-derived per caller.
|
|
11
|
+
//
|
|
12
|
+
// Creating a NEW package is the lower-stakes half: nothing is authored for that
|
|
13
|
+
// code yet, so a new path cannot duplicate or contradict anything, and naming a
|
|
14
|
+
// not-yet-materialized parent degrades gracefully instead of failing.
|
|
15
|
+
//
|
|
16
|
+
// Confinement is not incidental. `createStoryDirectory` builds a path from a
|
|
17
|
+
// caller-supplied code and title — candidate-table cells, which may be
|
|
18
|
+
// Navigator- or model-authored — so the final path is checked to be inside the
|
|
19
|
+
// roadmap root and the code segment is sanitized before it can create nested
|
|
20
|
+
// directories (Python's D-012).
|
|
21
|
+
|
|
22
|
+
import { relative, resolve, sep } from "node:path";
|
|
23
|
+
import { kebabSlug } from "#util/slug.ts";
|
|
24
|
+
import { displayPath } from "./projectPaths.ts";
|
|
25
|
+
import { inspectRoadmapSnapshot, roadmapPaths } from "./pullCandidates.ts";
|
|
26
|
+
import { matchHeading } from "./roadmapGrammar.ts";
|
|
27
|
+
import { readRoadmapFile, scanRoadmapIndexFiles } from "./roadmapScan.ts";
|
|
28
|
+
|
|
29
|
+
/** Python `StoryPackageAmbiguityError`: two packages claim one code. */
|
|
30
|
+
export class StoryPackageAmbiguityError extends Error {
|
|
31
|
+
constructor(message: string) {
|
|
32
|
+
super(message);
|
|
33
|
+
this.name = "StoryPackageAmbiguityError";
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Python `_sanitize_code_segment`: dots are the code's own level separator, so
|
|
39
|
+
* they become hyphens FIRST (preserving `cv2-ds1` folder naming for well-formed
|
|
40
|
+
* codes), and everything else goes through the same slug sanitizer titles use.
|
|
41
|
+
* That is what stops a `/` or `..` in a code cell from creating directories.
|
|
42
|
+
*/
|
|
43
|
+
function sanitizeCodeSegment(code: string): string {
|
|
44
|
+
return kebabSlug(code.replaceAll(".", "-"));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Python `story_folder_name`: `<code>-<title-slug>`, or the code alone. */
|
|
48
|
+
export function storyFolderName(code: string, title: string): string {
|
|
49
|
+
const codePart = sanitizeCodeSegment(code);
|
|
50
|
+
const slug = kebabSlug(title);
|
|
51
|
+
return slug ? `${codePart}-${slug}` : codePart;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Every code claimed by a non-legacy `index.md` heading, mapped to its directories. */
|
|
55
|
+
function groupRoadmapHeadings(roadmapRoot: string): Map<string, string[]> {
|
|
56
|
+
const byCode = new Map<string, string[]>();
|
|
57
|
+
for (const file of scanRoadmapIndexFiles(roadmapRoot)) {
|
|
58
|
+
const content = readRoadmapFile(file.absolutePath);
|
|
59
|
+
if (content === null) continue;
|
|
60
|
+
const heading = matchHeading(content);
|
|
61
|
+
if (!heading) continue;
|
|
62
|
+
const code = heading.code.trim();
|
|
63
|
+
const directory = file.components.slice(0, -1).join("/");
|
|
64
|
+
const existing = byCode.get(code);
|
|
65
|
+
if (existing) existing.push(directory);
|
|
66
|
+
else byCode.set(code, [directory]);
|
|
67
|
+
}
|
|
68
|
+
return byCode;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* The package resolver over one reading of the roadmap: for a code, the absolute
|
|
73
|
+
* directory of the authored package whose heading claims it, `null` when none does,
|
|
74
|
+
* and a throw when more than one does.
|
|
75
|
+
*
|
|
76
|
+
* `resolveStoryDirectory` asks it about one code. Expand asks it about every child of
|
|
77
|
+
* a Delivery Story, and must settle them all before it writes anything, so it reads
|
|
78
|
+
* the roadmap once and keeps this function (CR018). Both go through the one rule.
|
|
79
|
+
*/
|
|
80
|
+
export function storyDirectoryResolver(projectRoot: string): (code: string) => string | null {
|
|
81
|
+
// ABSOLUTE, like Python's `(project_path / … ).resolve()`. This was relative until
|
|
82
|
+
// CV22.DS7.US8 plateau 3, and the divergence was invisible because every test and
|
|
83
|
+
// every real caller passed an absolute project path: the front door reads it from
|
|
84
|
+
// the journey row, where `journey set-path` stores an absolute one. A
|
|
85
|
+
// project-relative path made Python print absolute package paths and TypeScript
|
|
86
|
+
// print relative ones in the same surface, and — worse — would have compared a
|
|
87
|
+
// relative candidate against a resolved root inside `createStoryDirectory`'s
|
|
88
|
+
// confinement guard.
|
|
89
|
+
const roadmapRoot = resolve(roadmapPaths(projectRoot).roadmapRoot);
|
|
90
|
+
const claims = groupRoadmapHeadings(roadmapRoot);
|
|
91
|
+
const absolute = (directory: string): string =>
|
|
92
|
+
directory ? `${roadmapRoot}${sep}${directory.split("/").join(sep)}` : roadmapRoot;
|
|
93
|
+
return (code) => {
|
|
94
|
+
const matches = claims.get(code) ?? [];
|
|
95
|
+
if (matches.length === 0) return null;
|
|
96
|
+
if (matches.length > 1) {
|
|
97
|
+
// Named relative to the project (CR082): Pull prints this message on
|
|
98
|
+
// `EXPAND_BLOCKED`, and every other command on an `Error:` line.
|
|
99
|
+
const claimants = matches.map((directory) => displayPath(absolute(directory), projectRoot));
|
|
100
|
+
throw new StoryPackageAmbiguityError(
|
|
101
|
+
`${matches.length} roadmap packages claim code '${code}': ${claimants.join(", ")}`,
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
return absolute(matches[0] ?? "");
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Python `resolve_story_directory`: the directory of the authored package whose
|
|
110
|
+
* heading code equals `code`, `null` when none does, and a throw when more than
|
|
111
|
+
* one does.
|
|
112
|
+
*/
|
|
113
|
+
export function resolveStoryDirectory(projectRoot: string, code: string): string | null {
|
|
114
|
+
return storyDirectoryResolver(projectRoot)(code);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Every heading code in the roadmap, mapped to the directories that claim it —
|
|
119
|
+
* relative to the roadmap root, POSIX, in scan order.
|
|
120
|
+
*
|
|
121
|
+
* One code, one directory is the healthy case. `resolveStoryDirectory` refuses to
|
|
122
|
+
* choose among several; CR002's roadmap scope reads the map directly because a
|
|
123
|
+
* surface must be able to SAY that a package is claimed twice, and to name the
|
|
124
|
+
* claimants project-relatively, rather than throw from inside `build load`.
|
|
125
|
+
*/
|
|
126
|
+
export function roadmapHeadingDirectories(projectRoot: string): Map<string, string[]> {
|
|
127
|
+
return groupRoadmapHeadings(resolve(roadmapPaths(projectRoot).roadmapRoot));
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** Python `find_duplicate_roadmap_headings`: the CI-facing whole-tree counterpart. */
|
|
131
|
+
export function findDuplicateRoadmapHeadings(projectRoot: string): Map<string, string[]> {
|
|
132
|
+
const { roadmapRoot } = roadmapPaths(projectRoot);
|
|
133
|
+
const duplicates = new Map<string, string[]>();
|
|
134
|
+
for (const [code, directories] of groupRoadmapHeadings(roadmapRoot)) {
|
|
135
|
+
if (directories.length > 1) duplicates.set(code, directories);
|
|
136
|
+
}
|
|
137
|
+
return duplicates;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** Python `_parent_code`: `CV2.DS1` -> `CV2`; `DS-35` -> `null`. */
|
|
141
|
+
function parentCode(code: string): string | null {
|
|
142
|
+
if (!code.includes(".")) return null;
|
|
143
|
+
return code.slice(0, code.lastIndexOf("."));
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** Python `_snapshot_title`: name a not-yet-materialized coordinate from the root snapshot. */
|
|
147
|
+
function snapshotTitle(projectRoot: string, code: string): string | null {
|
|
148
|
+
const snapshot = inspectRoadmapSnapshot(projectRoot, { journey: "", method: "ariad" });
|
|
149
|
+
for (const item of snapshot.items) {
|
|
150
|
+
if (item.code === code) return item.title;
|
|
151
|
+
}
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Python `_parent_directory`: resolve an existing package by heading, else name
|
|
157
|
+
* it from the root snapshot, else recurse to the grandparent and fall back to a
|
|
158
|
+
* bare code segment. Never fails loud — nothing is authored for the code yet.
|
|
159
|
+
*/
|
|
160
|
+
function parentDirectory(projectRoot: string, code: string): string {
|
|
161
|
+
const resolved = resolveStoryDirectory(projectRoot, code);
|
|
162
|
+
if (resolved !== null) return resolved;
|
|
163
|
+
// Resolved for the same reason as above: Python's `_parent_directory` bases every
|
|
164
|
+
// derived path on the resolved roadmap root.
|
|
165
|
+
const roadmapRoot = resolve(roadmapPaths(projectRoot).roadmapRoot);
|
|
166
|
+
const parent = parentCode(code);
|
|
167
|
+
const base = parent === null ? roadmapRoot : parentDirectory(projectRoot, parent);
|
|
168
|
+
const title = snapshotTitle(projectRoot, code);
|
|
169
|
+
const folder = title ? storyFolderName(code, title) : sanitizeCodeSegment(code);
|
|
170
|
+
return `${base}${sep}${folder}`;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Python `create_story_directory`: the canonical directory for a NEW package,
|
|
175
|
+
* nested under its best-available parent coordinate, with the story's whole title
|
|
176
|
+
* slugging its own folder: the rule Expand applies to a child (CR018).
|
|
177
|
+
*
|
|
178
|
+
* Throws when the resolved target escapes the roadmap root. That guard is the
|
|
179
|
+
* reason a `../` code or an absolute-looking title cannot write outside
|
|
180
|
+
* `docs/project/roadmap/`, and it is checked AFTER resolution so a symlink or a
|
|
181
|
+
* `..` surviving sanitization is still caught.
|
|
182
|
+
*/
|
|
183
|
+
export function createStoryDirectory(projectRoot: string, code: string, title: string): string {
|
|
184
|
+
const { roadmapRoot } = roadmapPaths(projectRoot);
|
|
185
|
+
const resolvedRoadmapRoot = resolve(roadmapRoot);
|
|
186
|
+
const parent = parentCode(code);
|
|
187
|
+
const base = parent === null ? resolvedRoadmapRoot : parentDirectory(projectRoot, parent);
|
|
188
|
+
const target = resolve(base, storyFolderName(code, title));
|
|
189
|
+
const relation = relative(resolvedRoadmapRoot, target);
|
|
190
|
+
const escapes =
|
|
191
|
+
relation.startsWith("..") || relation.startsWith(`${sep}`) || resolve(relation) === relation;
|
|
192
|
+
if (escapes) {
|
|
193
|
+
throw new Error(`story directory escapes roadmap root: ${target}`);
|
|
194
|
+
}
|
|
195
|
+
return target;
|
|
196
|
+
}
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
// CV22.DS7.US8 plateau 3 — consuming story authority, once.
|
|
2
|
+
//
|
|
3
|
+
// Port of `src/memory/builder/story_plan_preauthorization.py`.
|
|
4
|
+
//
|
|
5
|
+
// The consume is a compare-and-swap against the observed cursor, and every branch
|
|
6
|
+
// around it is a safety property:
|
|
7
|
+
//
|
|
8
|
+
// * an already-consumed receipt returns `already_approved` and starts NO second
|
|
9
|
+
// implementation, so a retried command is idempotent rather than additive;
|
|
10
|
+
// * a lost CAS re-reads the cursor and returns `already_approved` if another
|
|
11
|
+
// writer got there first, and otherwise refuses with `cursor_changed`;
|
|
12
|
+
// * a mismatch invalidates the receipt BEFORE raising, so a refused authority
|
|
13
|
+
// cannot be retried into acceptance;
|
|
14
|
+
// * the refusal path still leaves the Plan checkpoint pending, which is what makes
|
|
15
|
+
// "fall back to ordinary Navigator approval" true rather than aspirational.
|
|
16
|
+
|
|
17
|
+
import type { WritableDatabase } from "#db/database.ts";
|
|
18
|
+
import { planSectionsToAuthor } from "./artifacts/scaffoldState.ts";
|
|
19
|
+
import { cardPrefixed, cardText } from "./card.ts";
|
|
20
|
+
import {
|
|
21
|
+
type BuilderDeliveryCursor,
|
|
22
|
+
type CursorWriteDeps,
|
|
23
|
+
DeliveryCursorConflict,
|
|
24
|
+
getDeliveryCursor,
|
|
25
|
+
setDeliveryCursor,
|
|
26
|
+
setTo,
|
|
27
|
+
} from "./deliveryCursor.ts";
|
|
28
|
+
import { FLOW_UNIT_STORY_BY_STORY } from "./flowUnit.ts";
|
|
29
|
+
import {
|
|
30
|
+
invalidatePlanPreauthorization,
|
|
31
|
+
PlanPreauthorizationMismatch,
|
|
32
|
+
planPreauthorizationMismatchReason,
|
|
33
|
+
STORY_PLAN_CONTRACT,
|
|
34
|
+
} from "./planPreauthorization.ts";
|
|
35
|
+
import { wrapAriadSurface } from "./surfaceProtocol.ts";
|
|
36
|
+
|
|
37
|
+
const STORY_LEVELS = new Set(["user_story", "technical_story"]);
|
|
38
|
+
|
|
39
|
+
/** Python `StoryPlanPreauthorizationReport`. */
|
|
40
|
+
export interface StoryPlanPreauthorizationReport {
|
|
41
|
+
readonly cursor: BuilderDeliveryCursor;
|
|
42
|
+
readonly status: string;
|
|
43
|
+
readonly unfilledSections: readonly string[];
|
|
44
|
+
readonly implementationStarted: boolean;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Python `_is_consumed_story_approval`: all six conditions, or it is not consumed. */
|
|
48
|
+
function isConsumedStoryApproval(cursor: BuilderDeliveryCursor): boolean {
|
|
49
|
+
const receipt = cursor.planPreauthorization;
|
|
50
|
+
return (
|
|
51
|
+
receipt !== null &&
|
|
52
|
+
receipt.status === "consumed" &&
|
|
53
|
+
receipt.planContractVersion === STORY_PLAN_CONTRACT &&
|
|
54
|
+
cursor.lastDeliveryEvent === "plan_approved" &&
|
|
55
|
+
cursor.activeCheckpoint === null &&
|
|
56
|
+
cursor.pendingConfirmation === null
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface ApproveWithAuthorityOptions {
|
|
61
|
+
readonly journey: string;
|
|
62
|
+
readonly method: string;
|
|
63
|
+
readonly planArtifactPath: string | null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Python `approve_story_plan_with_preauthorization`. */
|
|
67
|
+
export function approveStoryPlanWithPreauthorization(
|
|
68
|
+
db: WritableDatabase,
|
|
69
|
+
options: ApproveWithAuthorityOptions,
|
|
70
|
+
deps: CursorWriteDeps,
|
|
71
|
+
): StoryPlanPreauthorizationReport {
|
|
72
|
+
const { journey, method } = options;
|
|
73
|
+
const cursor = getDeliveryCursor(db, journey);
|
|
74
|
+
if (cursor === null) throw new Error("delivery cursor is required before story Plan approval");
|
|
75
|
+
if (isConsumedStoryApproval(cursor)) {
|
|
76
|
+
return {
|
|
77
|
+
cursor,
|
|
78
|
+
status: "already_approved",
|
|
79
|
+
unfilledSections: [],
|
|
80
|
+
implementationStarted: false,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
if (cursor.activeItemLevel === null || !STORY_LEVELS.has(cursor.activeItemLevel)) {
|
|
84
|
+
throw new Error("story Plan preauthorization requires a User or Technical Story");
|
|
85
|
+
}
|
|
86
|
+
if (
|
|
87
|
+
cursor.activeCheckpoint !== "after_plan" ||
|
|
88
|
+
cursor.pendingConfirmation !== "navigator_approval"
|
|
89
|
+
) {
|
|
90
|
+
throw new Error("story Plan approval requires a pending after_plan checkpoint");
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// The same rule the ordinary route applies (CR112), and the list `build show` prints
|
|
94
|
+
// (CR111 D4): a section still holding the scaffold's sentences is not authored,
|
|
95
|
+
// whatever its structure.
|
|
96
|
+
const unfilled = planSectionsToAuthor(options.planArtifactPath);
|
|
97
|
+
const mismatch = planPreauthorizationMismatchReason(cursor, {
|
|
98
|
+
journey,
|
|
99
|
+
method,
|
|
100
|
+
flowUnit: FLOW_UNIT_STORY_BY_STORY,
|
|
101
|
+
childWorkItems: [],
|
|
102
|
+
planContractVersion: STORY_PLAN_CONTRACT,
|
|
103
|
+
unfilledSections: unfilled,
|
|
104
|
+
});
|
|
105
|
+
if (mismatch !== null) {
|
|
106
|
+
invalidatePlanPreauthorization(db, cursor, mismatch, deps);
|
|
107
|
+
throw new PlanPreauthorizationMismatch(
|
|
108
|
+
mismatch,
|
|
109
|
+
mismatch === "plan_incomplete" ? unfilled : [],
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const receipt = cursor.planPreauthorization;
|
|
114
|
+
// Unreachable: the mismatch guard establishes a pending receipt. Kept because
|
|
115
|
+
// Python keeps it, and because the type system cannot see that.
|
|
116
|
+
if (receipt === null) throw new PlanPreauthorizationMismatch("authorization_missing");
|
|
117
|
+
|
|
118
|
+
let updated: BuilderDeliveryCursor;
|
|
119
|
+
try {
|
|
120
|
+
updated = setDeliveryCursor(
|
|
121
|
+
db,
|
|
122
|
+
{
|
|
123
|
+
journey,
|
|
124
|
+
method,
|
|
125
|
+
activeItem: cursor.activeItem,
|
|
126
|
+
activeItemTitle: cursor.activeItemTitle,
|
|
127
|
+
activeItemLevel: cursor.activeItemLevel,
|
|
128
|
+
activeCheckpoint: null,
|
|
129
|
+
pendingConfirmation: null,
|
|
130
|
+
lastDeliveryEvent: "plan_approved",
|
|
131
|
+
cadenceProfile: cursor.cadenceProfile,
|
|
132
|
+
cadenceLimits: cursor.cadenceLimits,
|
|
133
|
+
granularityDecision: cursor.granularityDecision,
|
|
134
|
+
navigatorFlowUnit: cursor.navigatorFlowUnit,
|
|
135
|
+
childWorkItems: cursor.childWorkItems,
|
|
136
|
+
aggregateCheckpointStatus: cursor.aggregateCheckpointStatus,
|
|
137
|
+
cursorGeneration: cursor.cursorGeneration,
|
|
138
|
+
planPreauthorization: setTo({ ...receipt, status: "consumed", reason: null }),
|
|
139
|
+
expectedCursor: cursor,
|
|
140
|
+
},
|
|
141
|
+
deps,
|
|
142
|
+
);
|
|
143
|
+
} catch (error) {
|
|
144
|
+
if (!(error instanceof DeliveryCursorConflict)) throw error;
|
|
145
|
+
const current = getDeliveryCursor(db, journey);
|
|
146
|
+
if (current !== null && isConsumedStoryApproval(current)) {
|
|
147
|
+
return {
|
|
148
|
+
cursor: current,
|
|
149
|
+
status: "already_approved",
|
|
150
|
+
unfilledSections: [],
|
|
151
|
+
implementationStarted: false,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
throw new PlanPreauthorizationMismatch("cursor_changed");
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return {
|
|
158
|
+
cursor: updated,
|
|
159
|
+
status: "approved",
|
|
160
|
+
unfilledSections: unfilled,
|
|
161
|
+
implementationStarted: true,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/** Python `cancel_story_plan_preauthorization`. */
|
|
166
|
+
export function cancelStoryPlanPreauthorization(
|
|
167
|
+
db: WritableDatabase,
|
|
168
|
+
options: { journey: string; method: string },
|
|
169
|
+
deps: CursorWriteDeps,
|
|
170
|
+
): BuilderDeliveryCursor {
|
|
171
|
+
const cursor = getDeliveryCursor(db, options.journey);
|
|
172
|
+
if (cursor === null) {
|
|
173
|
+
throw new Error("delivery cursor is required before Plan preauthorization cancellation");
|
|
174
|
+
}
|
|
175
|
+
if (cursor.method !== options.method) {
|
|
176
|
+
throw new Error("active Builder method does not match cancellation method");
|
|
177
|
+
}
|
|
178
|
+
if (cursor.activeItemLevel === null || !STORY_LEVELS.has(cursor.activeItemLevel)) {
|
|
179
|
+
throw new Error("story Plan cancellation requires a User or Technical Story");
|
|
180
|
+
}
|
|
181
|
+
const receipt = cursor.planPreauthorization;
|
|
182
|
+
if (receipt === null || receipt.status !== "pending") {
|
|
183
|
+
throw new Error("pending Plan preauthorization is required before cancellation");
|
|
184
|
+
}
|
|
185
|
+
return invalidatePlanPreauthorization(db, cursor, "navigator_cancelled", deps);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/** Python `render_story_plan_preauthorization_recorded`. */
|
|
189
|
+
export function renderStoryPlanPreauthorizationRecorded(cursor: BuilderDeliveryCursor): string {
|
|
190
|
+
const receipt = cursor.planPreauthorization;
|
|
191
|
+
if (receipt === null) throw new Error("Plan preauthorization receipt is required");
|
|
192
|
+
const body = `${[
|
|
193
|
+
"Delivery",
|
|
194
|
+
"",
|
|
195
|
+
"╭────────────────────────────────────────────────────────╮",
|
|
196
|
+
"│ 🧭 PLAN PREAUTHORIZATION RECORDED │",
|
|
197
|
+
"│ │",
|
|
198
|
+
cardText("Authorized story"),
|
|
199
|
+
cardText(receipt.activeItem),
|
|
200
|
+
"│ │",
|
|
201
|
+
cardText("Story level"),
|
|
202
|
+
cardText(receipt.activeItemLevel),
|
|
203
|
+
"│ │",
|
|
204
|
+
cardText("Policy"),
|
|
205
|
+
cardText(receipt.policy),
|
|
206
|
+
"│ │",
|
|
207
|
+
cardText("Fixed stop"),
|
|
208
|
+
cardText(receipt.stopBoundary),
|
|
209
|
+
"│ │",
|
|
210
|
+
cardText("Boundary"),
|
|
211
|
+
cardText("Single-use authority remains pending until the"),
|
|
212
|
+
cardText("Driver completes the exact story Plan."),
|
|
213
|
+
"╰────────────────────────────────────────────────────────╯",
|
|
214
|
+
].join("\n")}\n`;
|
|
215
|
+
return wrapAriadSurface("plan_preauthorization_recorded", body);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/** Python `render_story_implementation_started`. */
|
|
219
|
+
export function renderStoryImplementationStarted(cursor: BuilderDeliveryCursor): string {
|
|
220
|
+
const body = `${[
|
|
221
|
+
"Delivery",
|
|
222
|
+
"",
|
|
223
|
+
"╭────────────────────────────────────────────────────────╮",
|
|
224
|
+
"│ 🟧 IMPLEMENTATION STARTED │",
|
|
225
|
+
"│ │",
|
|
226
|
+
cardText("Active story"),
|
|
227
|
+
cardText(cursor.activeItem ?? "none"),
|
|
228
|
+
"│ │",
|
|
229
|
+
cardText("Boundary"),
|
|
230
|
+
cardText("Local implementation may proceed under the exact"),
|
|
231
|
+
cardText("approved Plan and stops at Navigator Validation."),
|
|
232
|
+
"╰────────────────────────────────────────────────────────╯",
|
|
233
|
+
].join("\n")}\n`;
|
|
234
|
+
return wrapAriadSurface("implementation_started", body);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/** Python `render_story_preauthorization_already_consumed`. */
|
|
238
|
+
export function renderStoryPreauthorizationAlreadyConsumed(cursor: BuilderDeliveryCursor): string {
|
|
239
|
+
const body = `${[
|
|
240
|
+
"Delivery",
|
|
241
|
+
"",
|
|
242
|
+
"╭────────────────────────────────────────────────────────╮",
|
|
243
|
+
"│ ✓ PLAN PREAUTHORIZATION ALREADY CONSUMED │",
|
|
244
|
+
"│ │",
|
|
245
|
+
cardText("Active story"),
|
|
246
|
+
cardText(cursor.activeItem ?? "none"),
|
|
247
|
+
"│ │",
|
|
248
|
+
cardText("Outcome"),
|
|
249
|
+
cardText("No approval or implementation start was repeated."),
|
|
250
|
+
"╰────────────────────────────────────────────────────────╯",
|
|
251
|
+
].join("\n")}\n`;
|
|
252
|
+
return wrapAriadSurface("plan_preauthorization_already_consumed", body);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/** Python `render_story_plan_preauthorization_mismatch`: payload-free by construction. */
|
|
256
|
+
export function renderStoryPlanPreauthorizationMismatch(options: {
|
|
257
|
+
activeItem: string | null;
|
|
258
|
+
reason: string;
|
|
259
|
+
/** The `plan.md` sections still to author, when the reason is `plan_incomplete`. */
|
|
260
|
+
toAuthor?: readonly string[];
|
|
261
|
+
}): string {
|
|
262
|
+
const toAuthor = options.toAuthor ?? [];
|
|
263
|
+
const body = `${[
|
|
264
|
+
"Delivery",
|
|
265
|
+
"",
|
|
266
|
+
"╭────────────────────────────────────────────────────────╮",
|
|
267
|
+
"│ ✕ PLAN PREAUTHORIZATION MISMATCH │",
|
|
268
|
+
"│ │",
|
|
269
|
+
cardText("Active story"),
|
|
270
|
+
cardText(options.activeItem ?? "none"),
|
|
271
|
+
"│ │",
|
|
272
|
+
cardText("Reason"),
|
|
273
|
+
cardText(options.reason),
|
|
274
|
+
"│ │",
|
|
275
|
+
...(toAuthor.length > 0
|
|
276
|
+
? [
|
|
277
|
+
cardText("To author in plan.md"),
|
|
278
|
+
...cardPrefixed(toAuthor, "-"),
|
|
279
|
+
"│ │",
|
|
280
|
+
]
|
|
281
|
+
: []),
|
|
282
|
+
cardText("Fallback"),
|
|
283
|
+
cardText("Ordinary Navigator Plan approval remains required."),
|
|
284
|
+
"╰────────────────────────────────────────────────────────╯",
|
|
285
|
+
].join("\n")}\n`;
|
|
286
|
+
return wrapAriadSurface("plan_preauthorization_mismatch", body);
|
|
287
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// CV22.DS7.US8 plateau 1 — the Ariad surface transport boundary.
|
|
2
|
+
//
|
|
3
|
+
// Port of `src/memory/builder/surface_protocol.py`, all ten lines of it, and it
|
|
4
|
+
// earns its own module for the same reason Python gives it one: every Ariad
|
|
5
|
+
// surface passes through here, and the marked block is a transport contract the
|
|
6
|
+
// runtime harness parses. A re-indent, a doubled newline, or a lost marker is a
|
|
7
|
+
// protocol break, not a formatting difference.
|
|
8
|
+
//
|
|
9
|
+
// Two details a port drops:
|
|
10
|
+
//
|
|
11
|
+
// * the id is normalized — `strip()`, `upper()`, and spaces to underscores —
|
|
12
|
+
// so callers pass `"plan_checkpoint"` or `"Plan Checkpoint"` and both emit
|
|
13
|
+
// `PLAN_CHECKPOINT`;
|
|
14
|
+
// * the body is `rstrip()`ed, and call sites are INCONSISTENT about whether
|
|
15
|
+
// they append a trailing newline (`body + "\n"` in most of `builder/`, bare
|
|
16
|
+
// `body` in three places in `lifecycle.py`). Because the body is stripped
|
|
17
|
+
// either way the output is identical, which is exactly why a port that
|
|
18
|
+
// forgets the strip appears to work until it meets the other call style.
|
|
19
|
+
|
|
20
|
+
import { PYTHON_WHITESPACE_CLASS, pyStrip } from "#util/pythonText.ts";
|
|
21
|
+
|
|
22
|
+
const TRAILING_WS_RE = new RegExp(`[${PYTHON_WHITESPACE_CLASS}]+$`, "u");
|
|
23
|
+
|
|
24
|
+
/** Python `str.rstrip()` with no argument — Python's whitespace set, not `.trimEnd()`. */
|
|
25
|
+
function pyRStripAll(text: string): string {
|
|
26
|
+
return text.replace(TRAILING_WS_RE, "");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Python `wrap_ariad_surface`: wrap a rendered surface in explicit transport
|
|
31
|
+
* boundaries.
|
|
32
|
+
*
|
|
33
|
+
* Normalization is `surface_id.strip().upper().replace(" ", "_")`. Note it
|
|
34
|
+
* replaces spaces only — a tab or a newline in an id would survive into the
|
|
35
|
+
* marker, which no caller does but which the port must not silently "fix" by
|
|
36
|
+
* normalizing all whitespace.
|
|
37
|
+
*/
|
|
38
|
+
export function wrapAriadSurface(surfaceId: string, body: string): string {
|
|
39
|
+
const normalized = pyStrip(surfaceId).toUpperCase().replaceAll(" ", "_");
|
|
40
|
+
const content = pyRStripAll(body);
|
|
41
|
+
return `<<<ARIAD:${normalized}>>>\n${content}\n<<<END:${normalized}>>>\n`;
|
|
42
|
+
}
|