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,160 @@
|
|
|
1
|
+
// CV22.DS7.US8 plateau 3 — the post-Pull orientation for a Delivery Story.
|
|
2
|
+
//
|
|
3
|
+
// Port of `render_delivery_story_ready_report` and its helpers from
|
|
4
|
+
// `src/memory/builder/lifecycle.py`.
|
|
5
|
+
//
|
|
6
|
+
// This is the only surface composed from THREE reports — Pull, Prepare, and Expand —
|
|
7
|
+
// which is why it lives in its own module rather than inside any one event. The CLI
|
|
8
|
+
// renders it instead of the Pull and Prepare surfaces when the pulled item is a
|
|
9
|
+
// Delivery Story, so a port that emits all three has a duplicate-surface defect no
|
|
10
|
+
// renderer-level test can see.
|
|
11
|
+
//
|
|
12
|
+
// Its ribbon is also its own: `DS Plan` rather than `Plan`, from a stage list that
|
|
13
|
+
// exists nowhere else.
|
|
14
|
+
|
|
15
|
+
import { basename, dirname } from "node:path";
|
|
16
|
+
import { kebabSlug } from "#util/slug.ts";
|
|
17
|
+
import { cardPrefixed, cardText, cardWrapped } from "./card.ts";
|
|
18
|
+
import type { BuilderExpandReport } from "./expand.ts";
|
|
19
|
+
import type { BuilderPrepareReport } from "./prepare.ts";
|
|
20
|
+
import { type BuilderPullReport, placementRows } from "./pull.ts";
|
|
21
|
+
import { wrapAriadSurface } from "./surfaceProtocol.ts";
|
|
22
|
+
|
|
23
|
+
/** Python `_delivery_story_flow_ribbon`: the Delivery-Story-level stage labels. */
|
|
24
|
+
export function deliveryStoryFlowRibbon(current: string): string {
|
|
25
|
+
const stages: [string, string][] = [
|
|
26
|
+
["pull", "Pull"],
|
|
27
|
+
["prepare", "Prepare"],
|
|
28
|
+
["expand", "Expand"],
|
|
29
|
+
["plan", "DS Plan"],
|
|
30
|
+
["implement", "Implement"],
|
|
31
|
+
["validate", "Validate"],
|
|
32
|
+
["debt_review", "Debt Review"],
|
|
33
|
+
["done", "Done"],
|
|
34
|
+
];
|
|
35
|
+
const index = stages.findIndex(([stage]) => stage === current);
|
|
36
|
+
if (index === -1) throw new Error(`unknown Delivery Story lifecycle stage: ${current}`);
|
|
37
|
+
const parts = stages.map(([, label], position) => {
|
|
38
|
+
const marker = position < index ? "✓" : position === index ? "◉" : "○";
|
|
39
|
+
return `${marker} ${label}`;
|
|
40
|
+
});
|
|
41
|
+
return `Delivery Flow: ${parts.join(" → ")}`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Python `_prepare_finding`. */
|
|
45
|
+
function prepareFinding(report: BuilderPrepareReport): string {
|
|
46
|
+
return report.granularityDecisionRequired
|
|
47
|
+
? "Delivery Story requires a granularity decision before implementation."
|
|
48
|
+
: "Pulled story is implementable after Plan approval.";
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Python `_materialized_summary`.
|
|
53
|
+
*
|
|
54
|
+
* Three chained guesses at what a materialized path IS, in Python's order, then a
|
|
55
|
+
* de-duplication that preserves first appearance. The last branch falls back to the
|
|
56
|
+
* folder NAME, which is why an unexpected layout degrades into something readable
|
|
57
|
+
* rather than failing.
|
|
58
|
+
*/
|
|
59
|
+
function materializedSummary(report: BuilderExpandReport): string[] {
|
|
60
|
+
if (report.materializedPaths.length === 0) return ["none"];
|
|
61
|
+
const deliveryStorySlug = kebabSlug(report.deliveryStory.toLowerCase());
|
|
62
|
+
const recommendedLeaf = report.recommendedStory.split(".").at(-1) ?? report.recommendedStory;
|
|
63
|
+
const recommendedSlug = kebabSlug(recommendedLeaf.toLowerCase());
|
|
64
|
+
const summaries: string[] = [];
|
|
65
|
+
for (const path of report.materializedPaths) {
|
|
66
|
+
const parentName = basename(dirname(path));
|
|
67
|
+
if (parentName === deliveryStorySlug) {
|
|
68
|
+
summaries.push("DS package");
|
|
69
|
+
} else if (parentName.endsWith(recommendedSlug)) {
|
|
70
|
+
summaries.push(`${recommendedLeaf} package`);
|
|
71
|
+
} else if (path.includes(report.deliveryStory.toLowerCase().replaceAll(".", "-"))) {
|
|
72
|
+
summaries.push("DS package");
|
|
73
|
+
} else {
|
|
74
|
+
summaries.push(parentName);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
const deduped: string[] = [];
|
|
78
|
+
for (const summary of summaries) {
|
|
79
|
+
if (!deduped.includes(summary)) deduped.push(summary);
|
|
80
|
+
}
|
|
81
|
+
return deduped;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Python `_flow_unit_recommendation`.
|
|
86
|
+
*
|
|
87
|
+
* `max(1, len(materialized_artifacts) - 1)` — the artifact list carries the DS
|
|
88
|
+
* package plus one entry per child, so subtracting one counts children, and the
|
|
89
|
+
* floor keeps a single-child Delivery Story from reading as zero.
|
|
90
|
+
*/
|
|
91
|
+
function flowUnitRecommendation(report: BuilderExpandReport): string {
|
|
92
|
+
const childCount = Math.max(1, report.materializedArtifacts.length - 1);
|
|
93
|
+
return childCount <= 1
|
|
94
|
+
? "delivery_story — recommended"
|
|
95
|
+
: "delivery_story — recommended unless you want separate Navigator validation for each child story";
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Python `_flow_unit_recommendation_rationale`. */
|
|
99
|
+
function flowUnitRecommendationRationale(report: BuilderExpandReport): string {
|
|
100
|
+
const childCount = Math.max(1, report.materializedArtifacts.length - 1);
|
|
101
|
+
return childCount <= 1
|
|
102
|
+
? "The Delivery Story appears coherent enough to plan, implement, and validate as one Navigator-facing unit while keeping the child story as a traceable work package."
|
|
103
|
+
: "The child work packages appear to support one Delivery Story outcome. Use story_by_story only if each child needs separate Navigator checkpoints.";
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Python `render_delivery_story_ready_report`, with the CV named by `placementCvTitle`. */
|
|
107
|
+
export function renderDeliveryStoryReadyReport(reports: {
|
|
108
|
+
pull: BuilderPullReport;
|
|
109
|
+
prepare: BuilderPrepareReport;
|
|
110
|
+
expand: BuilderExpandReport;
|
|
111
|
+
cvTitle: string;
|
|
112
|
+
}): string {
|
|
113
|
+
const { pull, prepare, expand, cvTitle } = reports;
|
|
114
|
+
const title = pull.item.title;
|
|
115
|
+
const recommendedLeaf = expand.recommendedStory.split(".").at(-1) ?? expand.recommendedStory;
|
|
116
|
+
const body = `${[
|
|
117
|
+
"Delivery",
|
|
118
|
+
deliveryStoryFlowRibbon("expand"),
|
|
119
|
+
"",
|
|
120
|
+
"╭────────────────────────────────────────────────────────╮",
|
|
121
|
+
cardText(`🧭 ${pull.item.code} READY`),
|
|
122
|
+
"│ │",
|
|
123
|
+
cardText("What was pulled?"),
|
|
124
|
+
...cardWrapped(title),
|
|
125
|
+
"│ │",
|
|
126
|
+
cardText("Where are we in the roadmap?"),
|
|
127
|
+
// The Pull card's lineage renderer, one level shorter: a Delivery Story (CR113).
|
|
128
|
+
...placementRows(pull.item, { cvTitle, deliveryStory: null }),
|
|
129
|
+
"│ │",
|
|
130
|
+
cardText("What did Prepare find?"),
|
|
131
|
+
...cardWrapped(prepareFinding(prepare)),
|
|
132
|
+
"│ │",
|
|
133
|
+
cardText("What was materialized?"),
|
|
134
|
+
...cardPrefixed(materializedSummary(expand), "✓"),
|
|
135
|
+
"│ │",
|
|
136
|
+
cardText("What is recommended next?"),
|
|
137
|
+
// Whole: the skill tells the agent to pull the confirmed child by this title (CR018).
|
|
138
|
+
...cardWrapped(`🟩[${recommendedLeaf}] ${expand.recommendedStoryTitle}`),
|
|
139
|
+
"│ │",
|
|
140
|
+
cardText("Recommended flow unit"),
|
|
141
|
+
...cardWrapped(flowUnitRecommendation(expand)),
|
|
142
|
+
"│ │",
|
|
143
|
+
cardText("Why this recommendation?"),
|
|
144
|
+
...cardWrapped(flowUnitRecommendationRationale(expand)),
|
|
145
|
+
"│ │",
|
|
146
|
+
cardText("What can we do now?"),
|
|
147
|
+
...cardPrefixed(
|
|
148
|
+
[
|
|
149
|
+
`confirm ${recommendedLeaf} and continue story_by_story`,
|
|
150
|
+
"choose delivery_story as the flow unit",
|
|
151
|
+
"inspect generated story packages",
|
|
152
|
+
],
|
|
153
|
+
"-",
|
|
154
|
+
),
|
|
155
|
+
"│ │",
|
|
156
|
+
...cardWrapped("Choose the next flow unit when ready."),
|
|
157
|
+
"╰────────────────────────────────────────────────────────╯",
|
|
158
|
+
].join("\n")}\n`;
|
|
159
|
+
return wrapAriadSurface("delivery_story_ready", body);
|
|
160
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
// CV22.DS7.US8 plateau 5 — the authored-roadmap preflight for Delivery Story Done.
|
|
2
|
+
//
|
|
3
|
+
// Port of `src/memory/builder/delivery_story_roadmap_closure.py`.
|
|
4
|
+
//
|
|
5
|
+
// Every other Builder guard reads the delivery cursor. This one reads the
|
|
6
|
+
// NAVIGATOR'S REPOSITORY: before a Delivery Story may close, its own package, every
|
|
7
|
+
// known child package, and every canonical table row naming those codes must
|
|
8
|
+
// already say Done in authored Markdown. The agent owns the semantic edit; this
|
|
9
|
+
// verifies explicit evidence and never invents project meaning.
|
|
10
|
+
//
|
|
11
|
+
// Three details decide whether a port refuses the same closures Python refuses:
|
|
12
|
+
//
|
|
13
|
+
// 1. **The walk includes `legacy/`.** Python calls `rglob("index.md")` with no
|
|
14
|
+
// exclusion here, unlike the three grammar call sites. An archived row for a
|
|
15
|
+
// known code blocks Done — reproduced, and pinned by
|
|
16
|
+
// `authored_closure_reads_legacy_rows`.
|
|
17
|
+
// 2. **The status pattern is LINE-ANCHORED.** `_STATUS_RE` is
|
|
18
|
+
// `^\*\*Status:\*\*...$` under `re.MULTILINE`, so unlike `roadmapGrammar`'s
|
|
19
|
+
// unanchored `STATUS_RE` the marker must open its line. Written with explicit
|
|
20
|
+
// `(?<=^|\n)` / `(?=\n|$)` anchors because JavaScript's `m` flag also anchors
|
|
21
|
+
// at U+2028/U+2029, where Python's does not.
|
|
22
|
+
// 3. **`_is_done` is a SUFFIX test:** `strip().casefold().endswith("done")`. So
|
|
23
|
+
// `Done` and `✅ DONE` pass while `✅ Done (2026-09-14)` does not — the dated
|
|
24
|
+
// form this repository's own roadmap uses. Parity-bound, recorded as a CR
|
|
25
|
+
// candidate rather than smoothed here.
|
|
26
|
+
//
|
|
27
|
+
// Read-only by construction: nothing in this module writes.
|
|
28
|
+
|
|
29
|
+
import { readFileSync } from "node:fs";
|
|
30
|
+
import { join, resolve, sep } from "node:path";
|
|
31
|
+
import { PYTHON_WHITESPACE_CLASS, pyStrip } from "#util/pythonText.ts";
|
|
32
|
+
import { projectRelative } from "./projectPaths.ts";
|
|
33
|
+
import { stripMarkdownLink, tableRowCells } from "./roadmapGrammar.ts";
|
|
34
|
+
import { scanRoadmapIndexFiles } from "./roadmapScan.ts";
|
|
35
|
+
import { resolveStoryDirectory } from "./storyPaths.ts";
|
|
36
|
+
|
|
37
|
+
const WS = `[${PYTHON_WHITESPACE_CLASS}]`;
|
|
38
|
+
|
|
39
|
+
/** Python `_STATUS_RE`: line-anchored, first match wins. */
|
|
40
|
+
const STATUS_LINE_RE = new RegExp(
|
|
41
|
+
`(?<=^|\\n)\\*\\*Status:\\*\\*${WS}*(?<status>[^\\n]+?)${WS}*(?=\\n|$)`,
|
|
42
|
+
"u",
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
export interface AuthoredClosureReport {
|
|
46
|
+
/** True when nothing blocks Done. */
|
|
47
|
+
readonly ready: boolean;
|
|
48
|
+
/** Project-relative evidence, in the order Python discovers it. */
|
|
49
|
+
readonly issues: readonly string[];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Python `_is_done`. */
|
|
53
|
+
function isDone(status: string): boolean {
|
|
54
|
+
return pyStrip(status).toLowerCase().endsWith("done");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Python `_relative`: project-relative POSIX, raising when the path escapes. The
|
|
59
|
+
* relation comes from the core every surface names paths with (CR082); this caller
|
|
60
|
+
* refuses the project root and a path outside, where a surface prints `.` or the path
|
|
61
|
+
* as given.
|
|
62
|
+
*/
|
|
63
|
+
export function relativeOrRefuse(path: string, projectRoot: string): string {
|
|
64
|
+
const relation = projectRelative(path, projectRoot);
|
|
65
|
+
if (relation === null || relation === "") {
|
|
66
|
+
// Python raises `ValueError` from `relative_to` here and the CLI prints
|
|
67
|
+
// CPython's own prose. Recorded divergence (plan, Parity Contract): the
|
|
68
|
+
// message is ours, the refusal is the same.
|
|
69
|
+
throw new Error(
|
|
70
|
+
`roadmap file ${path} is not inside the project at ${projectRoot}; ` +
|
|
71
|
+
"refusing to name a path outside the project in a closure refusal",
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
return relation.split(sep).join("/");
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Python `_status_table_rows`: header-driven `Code`/`Status` extraction. */
|
|
78
|
+
function statusTableRows(indexPath: string): [string, string][] {
|
|
79
|
+
const rows: [string, string][] = [];
|
|
80
|
+
let columns: { code: number; status: number } | null = null;
|
|
81
|
+
let content: string;
|
|
82
|
+
try {
|
|
83
|
+
content = readFileSync(indexPath, "utf8");
|
|
84
|
+
} catch {
|
|
85
|
+
return rows;
|
|
86
|
+
}
|
|
87
|
+
for (const rawLine of content.split("\n")) {
|
|
88
|
+
const line = pyStrip(rawLine);
|
|
89
|
+
if (!line.startsWith("|")) {
|
|
90
|
+
// A non-table line CLOSES the table: a second table later in the file needs
|
|
91
|
+
// its own header, exactly as Python's loop resets `columns`.
|
|
92
|
+
columns = null;
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
const cells = tableRowCells(line);
|
|
96
|
+
if (columns === null) {
|
|
97
|
+
const lowered = cells.map((cell) => cell.toLowerCase());
|
|
98
|
+
const code = lowered.indexOf("code");
|
|
99
|
+
const status = lowered.indexOf("status");
|
|
100
|
+
if (code !== -1 && status !== -1) columns = { code, status };
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
if (cells.every((cell) => cell === "" || /^[-:]+$/u.test(cell))) continue;
|
|
104
|
+
if (cells.length <= Math.max(columns.code, columns.status)) continue;
|
|
105
|
+
rows.push([stripMarkdownLink(cells[columns.code] ?? ""), cells[columns.status] ?? ""]);
|
|
106
|
+
}
|
|
107
|
+
return rows;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Python `inspect_authored_closure`.
|
|
112
|
+
*
|
|
113
|
+
* Package evidence first, in cursor order (the Delivery Story, then each child),
|
|
114
|
+
* then every table row under the roadmap in path order. The issue ORDER is part of
|
|
115
|
+
* the refusal message the CLI joins with `; `, so it is behavior.
|
|
116
|
+
*/
|
|
117
|
+
export function inspectAuthoredClosure(
|
|
118
|
+
projectPath: string,
|
|
119
|
+
options: { deliveryStory: string; childWorkItems: readonly string[] },
|
|
120
|
+
): AuthoredClosureReport {
|
|
121
|
+
const projectRoot = resolve(projectPath);
|
|
122
|
+
const issues: string[] = [];
|
|
123
|
+
const codes = [options.deliveryStory, ...options.childWorkItems];
|
|
124
|
+
for (const code of codes) {
|
|
125
|
+
const packagePath = resolveStoryDirectory(projectRoot, code);
|
|
126
|
+
if (packagePath === null) {
|
|
127
|
+
issues.push(`docs/project/roadmap: package ${code} was not found`);
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
const indexPath = join(packagePath, "index.md");
|
|
131
|
+
const content = readFileSync(indexPath, "utf8");
|
|
132
|
+
const status = STATUS_LINE_RE.exec(content)?.groups?.status;
|
|
133
|
+
if (status === undefined || !isDone(status)) {
|
|
134
|
+
issues.push(`${relativeOrRefuse(indexPath, projectRoot)}: package status is not Done`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const roadmapRoot = join(projectRoot, "docs", "project", "roadmap");
|
|
139
|
+
const known = new Set(codes);
|
|
140
|
+
for (const file of scanRoadmapIndexFiles(roadmapRoot, { includeLegacy: true })) {
|
|
141
|
+
for (const [code, status] of statusTableRows(file.absolutePath)) {
|
|
142
|
+
if (known.has(code) && !isDone(status)) {
|
|
143
|
+
issues.push(
|
|
144
|
+
`${relativeOrRefuse(file.absolutePath, projectRoot)}: table row ${code} is not Done`,
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return { ready: issues.length === 0, issues };
|
|
151
|
+
}
|
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
// CV22.DS7.US8 plateau 3 — Expand: a Delivery Story becomes implementable stories.
|
|
2
|
+
//
|
|
3
|
+
// Port of `expand_delivery_story` and its renderers from
|
|
4
|
+
// `src/memory/builder/lifecycle.py`.
|
|
5
|
+
//
|
|
6
|
+
// Expand is the highest-consequence command in this plateau, because it is the one
|
|
7
|
+
// that WRITES INTO AUTHORED CONTENT. Three properties are load-bearing and each
|
|
8
|
+
// has already cost this project a defect:
|
|
9
|
+
//
|
|
10
|
+
// * **It resolves the package by heading code, never by arithmetic on code and
|
|
11
|
+
// title** (`storyPaths`). An authored package lives wherever a human put it.
|
|
12
|
+
// CR048 was this, twice.
|
|
13
|
+
// * **It refuses rather than fabricates.** An authored package whose candidate
|
|
14
|
+
// table does not parse blocks Expand. The alternative — inventing a generic
|
|
15
|
+
// `US1` on top of real authored content — is the CV22.DS7 secondary defect,
|
|
16
|
+
// and it is worse than failing because the fabrication looks like work.
|
|
17
|
+
// * **It never overwrites.** Every write is guarded by "does this exist", so
|
|
18
|
+
// re-expanding an authored package reports `existing` and changes nothing.
|
|
19
|
+
// "Exists" means a package whose heading claims the child's code, wherever it
|
|
20
|
+
// lives, not a file at the folder Expand would have named. Until CR018 it meant
|
|
21
|
+
// the folder, so a child a human had filed elsewhere was written a second time.
|
|
22
|
+
//
|
|
23
|
+
// The candidate-table parser is header-driven on purpose: it accepts the 4-column
|
|
24
|
+
// authored shape and the 5-column generated one, in any column order, with extra
|
|
25
|
+
// columns ignored. What it does not accept is a table that fails to declare Code,
|
|
26
|
+
// Story, Type, and Status — which is precisely the refusal above.
|
|
27
|
+
|
|
28
|
+
import { existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
29
|
+
import { join } from "node:path";
|
|
30
|
+
import type { WritableDatabase } from "#db/database.ts";
|
|
31
|
+
import { existingArtifact, type MaterializedArtifact } from "./artifacts/artifactSurfaces.ts";
|
|
32
|
+
import { writeBuilderArtifact } from "./artifacts/artifactWriter.ts";
|
|
33
|
+
import { artifactState, describeArtifactState } from "./artifacts/scaffoldState.ts";
|
|
34
|
+
import {
|
|
35
|
+
renderDeliveryStoryIndex,
|
|
36
|
+
renderStoryIndex,
|
|
37
|
+
renderUserStoryIndex,
|
|
38
|
+
} from "./artifacts/storyIndex.ts";
|
|
39
|
+
import { cardText, cardWrapped } from "./card.ts";
|
|
40
|
+
import { normalizeRequired } from "./cursorTransitions.ts";
|
|
41
|
+
import {
|
|
42
|
+
type BuilderDeliveryCursor,
|
|
43
|
+
type CursorWriteDeps,
|
|
44
|
+
getDeliveryCursor,
|
|
45
|
+
setDeliveryCursor,
|
|
46
|
+
} from "./deliveryCursor.ts";
|
|
47
|
+
import { FLOW_STOPS } from "./flowUnit.ts";
|
|
48
|
+
import { renderLifecycleRibbon } from "./lifecycleRibbon.ts";
|
|
49
|
+
import { displayPath } from "./projectPaths.ts";
|
|
50
|
+
import { type CandidateChild, parseCandidateStories } from "./roadmapGrammar.ts";
|
|
51
|
+
import { createStoryDirectory, storyDirectoryResolver, storyFolderName } from "./storyPaths.ts";
|
|
52
|
+
import { wrapAriadSurface } from "./surfaceProtocol.ts";
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Python `ExpandBlockedError`.
|
|
56
|
+
*
|
|
57
|
+
* A distinct type, not a bare `Error`: the CLI catches it alongside
|
|
58
|
+
* `StoryPackageAmbiguityError` to render `EXPAND_BLOCKED`, while an ordinary
|
|
59
|
+
* `ValueError` from a guard stays a plain stderr refusal.
|
|
60
|
+
*/
|
|
61
|
+
export class ExpandBlockedError extends Error {
|
|
62
|
+
constructor(message: string) {
|
|
63
|
+
super(message);
|
|
64
|
+
this.name = "ExpandBlockedError";
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Python `BuilderExpandReport`. */
|
|
69
|
+
export interface BuilderExpandReport {
|
|
70
|
+
readonly journey: string;
|
|
71
|
+
readonly method: string;
|
|
72
|
+
readonly deliveryStory: string;
|
|
73
|
+
readonly deliveryStoryTitle: string;
|
|
74
|
+
readonly materializedPaths: readonly string[];
|
|
75
|
+
readonly materializedArtifacts: readonly MaterializedArtifact[];
|
|
76
|
+
readonly recommendedStory: string;
|
|
77
|
+
readonly recommendedStoryTitle: string;
|
|
78
|
+
readonly cursor: BuilderDeliveryCursor;
|
|
79
|
+
readonly nextEvent: string;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Python `_first_pending_child`.
|
|
84
|
+
*
|
|
85
|
+
* "Done" is matched as a SUBSTRING of the status cell in both cases, so `✅ Done`
|
|
86
|
+
* and `done` both count and `Doneness` would too. Falls back to the first child
|
|
87
|
+
* when every child is done, so Expand always recommends something.
|
|
88
|
+
*/
|
|
89
|
+
function firstPendingChild(children: readonly CandidateChild[]): CandidateChild {
|
|
90
|
+
for (const child of children) {
|
|
91
|
+
if (!child.status.includes("Done") && !child.status.includes("done")) return child;
|
|
92
|
+
}
|
|
93
|
+
return children[0] as CandidateChild;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Python `_materialize_child_package`: the child's FULL title slugs its folder.
|
|
98
|
+
*
|
|
99
|
+
* `authored` is the package that already claims the child's code, found by heading
|
|
100
|
+
* before Expand wrote anything. It is reported where it lives and never written.
|
|
101
|
+
*/
|
|
102
|
+
/** An existing child index, named with what it is: a scaffold or an authored file (CR112). */
|
|
103
|
+
function existingIndex(label: string, path: string): MaterializedArtifact {
|
|
104
|
+
return existingArtifact(
|
|
105
|
+
label,
|
|
106
|
+
path,
|
|
107
|
+
describeArtifactState(artifactState("index.md", path).state),
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function materializeChildPackage(
|
|
112
|
+
dsDirectory: string,
|
|
113
|
+
child: CandidateChild,
|
|
114
|
+
authored: string | null,
|
|
115
|
+
projectRoot: string,
|
|
116
|
+
): { path: string; artifact: MaterializedArtifact } {
|
|
117
|
+
const label = `${child.code.split(".").at(-1) ?? child.code} package`;
|
|
118
|
+
if (authored !== null) {
|
|
119
|
+
const authoredIndex = join(authored, "index.md");
|
|
120
|
+
return { path: authoredIndex, artifact: existingIndex(label, authoredIndex) };
|
|
121
|
+
}
|
|
122
|
+
// The whole title slugs the folder, as it does for any new package (CR018).
|
|
123
|
+
const childDirectory = join(dsDirectory, storyFolderName(child.code, child.title));
|
|
124
|
+
const childIndex = join(childDirectory, "index.md");
|
|
125
|
+
const outcome = writeBuilderArtifact({
|
|
126
|
+
path: childIndex,
|
|
127
|
+
content: renderStoryIndex(child.code, child.title, child.level),
|
|
128
|
+
policy: "create-only",
|
|
129
|
+
projectRoot,
|
|
130
|
+
});
|
|
131
|
+
return {
|
|
132
|
+
path: childIndex,
|
|
133
|
+
artifact:
|
|
134
|
+
outcome === "existing"
|
|
135
|
+
? existingIndex(label, childIndex)
|
|
136
|
+
: { kind: label, path: childIndex, status: "created" },
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export interface ExpandOptions {
|
|
141
|
+
readonly journey: string;
|
|
142
|
+
readonly method: string;
|
|
143
|
+
readonly projectPath: string;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** Python `expand_delivery_story`. */
|
|
147
|
+
export function expandDeliveryStory(
|
|
148
|
+
db: WritableDatabase,
|
|
149
|
+
options: ExpandOptions,
|
|
150
|
+
deps: CursorWriteDeps,
|
|
151
|
+
): BuilderExpandReport {
|
|
152
|
+
const journey = normalizeRequired(options.journey, "journey");
|
|
153
|
+
const method = normalizeRequired(options.method, "method");
|
|
154
|
+
|
|
155
|
+
const existing = getDeliveryCursor(db, journey);
|
|
156
|
+
if (existing === null) throw new Error("delivery cursor is required before expand");
|
|
157
|
+
// Level before item: a cursor with no active item but the wrong level reports
|
|
158
|
+
// the LEVEL problem. Guard order is observable, so it is preserved.
|
|
159
|
+
if (existing.activeItemLevel !== "delivery_story") {
|
|
160
|
+
throw new Error("expand requires an active Delivery Story");
|
|
161
|
+
}
|
|
162
|
+
if (!existing.activeItem) throw new Error("active item is required before expand");
|
|
163
|
+
|
|
164
|
+
const activeItem = existing.activeItem;
|
|
165
|
+
const title = existing.activeItemTitle || activeItem;
|
|
166
|
+
// One reading of the roadmap answers for the Delivery Story and for every child, so
|
|
167
|
+
// each is settled before the first write (CR018).
|
|
168
|
+
const packageOf = storyDirectoryResolver(options.projectPath);
|
|
169
|
+
const dsDirectory =
|
|
170
|
+
packageOf(activeItem) ?? createStoryDirectory(options.projectPath, activeItem, title);
|
|
171
|
+
const dsIndex = join(dsDirectory, "index.md");
|
|
172
|
+
const dsExists = existsSync(dsIndex);
|
|
173
|
+
const children = dsExists ? parseCandidateStories(readFileSync(dsIndex, "utf8")) : [];
|
|
174
|
+
|
|
175
|
+
// CR082: a refusal names the package relative to the project, where it is raised, so
|
|
176
|
+
// `EXPAND_BLOCKED` prints the reason as given and copies no home directory anywhere.
|
|
177
|
+
if (dsExists && children.length === 0) {
|
|
178
|
+
throw new ExpandBlockedError(
|
|
179
|
+
`authored package at ${displayPath(dsDirectory, options.projectPath)} has no ` +
|
|
180
|
+
"canonical candidate-stories table " +
|
|
181
|
+
"(a Markdown table header must include Code, Story, Type, and Status columns); " +
|
|
182
|
+
"refusing to fabricate a generic story",
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const materializedPaths: string[] = [dsIndex];
|
|
187
|
+
const materializedArtifacts: MaterializedArtifact[] = [];
|
|
188
|
+
let recommendedCode: string;
|
|
189
|
+
let recommendedTitle: string;
|
|
190
|
+
let childWorkItems: string[];
|
|
191
|
+
|
|
192
|
+
if (children.length > 0) {
|
|
193
|
+
const recommended = firstPendingChild(children);
|
|
194
|
+
recommendedCode = recommended.code;
|
|
195
|
+
recommendedTitle = recommended.title;
|
|
196
|
+
// Before any write: a child two packages claim throws here, while EXPAND_BLOCKED's
|
|
197
|
+
// "No files were materialized" is still true.
|
|
198
|
+
const placements = children.map((child) => ({ child, authored: packageOf(child.code) }));
|
|
199
|
+
mkdirSync(dsDirectory, { recursive: true });
|
|
200
|
+
const dsOutcome = writeBuilderArtifact({
|
|
201
|
+
path: dsIndex,
|
|
202
|
+
content: renderDeliveryStoryIndex(activeItem, title, recommendedCode, recommendedTitle),
|
|
203
|
+
policy: "create-only",
|
|
204
|
+
projectRoot: options.projectPath,
|
|
205
|
+
});
|
|
206
|
+
materializedArtifacts.push(
|
|
207
|
+
dsOutcome === "existing"
|
|
208
|
+
? existingArtifact("DS package", dsIndex)
|
|
209
|
+
: { kind: "DS package", path: dsIndex, status: "created" },
|
|
210
|
+
);
|
|
211
|
+
for (const { child, authored } of placements) {
|
|
212
|
+
const materialized = materializeChildPackage(
|
|
213
|
+
dsDirectory,
|
|
214
|
+
child,
|
|
215
|
+
authored,
|
|
216
|
+
options.projectPath,
|
|
217
|
+
);
|
|
218
|
+
materializedPaths.push(materialized.path);
|
|
219
|
+
materializedArtifacts.push(materialized.artifact);
|
|
220
|
+
}
|
|
221
|
+
childWorkItems = children.map((child) => child.code);
|
|
222
|
+
} else {
|
|
223
|
+
// No authored package at all: synthesize one child so the Delivery Story has
|
|
224
|
+
// something implementable. This is not fabrication on top of authored content
|
|
225
|
+
// — the refusal above already covers that case — it is the empty-field case.
|
|
226
|
+
recommendedCode = `${activeItem}.US1`;
|
|
227
|
+
recommendedTitle = title;
|
|
228
|
+
const claimed = packageOf(recommendedCode);
|
|
229
|
+
if (claimed !== null) {
|
|
230
|
+
throw new ExpandBlockedError(
|
|
231
|
+
`${activeItem} has no package, and an authored package already claims ` +
|
|
232
|
+
`${recommendedCode} at ${displayPath(claimed, options.projectPath)}; ` +
|
|
233
|
+
"refusing to invent a story over it",
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
const usDirectory = join(dsDirectory, storyFolderName(recommendedCode, recommendedTitle));
|
|
237
|
+
const usIndex = join(usDirectory, "index.md");
|
|
238
|
+
mkdirSync(dsDirectory, { recursive: true });
|
|
239
|
+
mkdirSync(usDirectory, { recursive: true });
|
|
240
|
+
const dsOutcome = writeBuilderArtifact({
|
|
241
|
+
path: dsIndex,
|
|
242
|
+
content: renderDeliveryStoryIndex(activeItem, title, recommendedCode, recommendedTitle),
|
|
243
|
+
policy: "create-only",
|
|
244
|
+
projectRoot: options.projectPath,
|
|
245
|
+
});
|
|
246
|
+
const usOutcome = writeBuilderArtifact({
|
|
247
|
+
path: usIndex,
|
|
248
|
+
content: renderUserStoryIndex(recommendedCode, recommendedTitle),
|
|
249
|
+
policy: "create-only",
|
|
250
|
+
projectRoot: options.projectPath,
|
|
251
|
+
});
|
|
252
|
+
materializedArtifacts.push(
|
|
253
|
+
dsOutcome === "existing"
|
|
254
|
+
? existingArtifact("DS package", dsIndex)
|
|
255
|
+
: { kind: "DS package", path: dsIndex, status: "created" },
|
|
256
|
+
);
|
|
257
|
+
const label = `${recommendedCode.split(".").at(-1) ?? recommendedCode} package`;
|
|
258
|
+
materializedArtifacts.push(
|
|
259
|
+
usOutcome === "existing"
|
|
260
|
+
? existingArtifact(label, usIndex)
|
|
261
|
+
: { kind: label, path: usIndex, status: "created" },
|
|
262
|
+
);
|
|
263
|
+
materializedPaths.push(usIndex);
|
|
264
|
+
childWorkItems = [recommendedCode];
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const cursor = setDeliveryCursor(
|
|
268
|
+
db,
|
|
269
|
+
{
|
|
270
|
+
journey,
|
|
271
|
+
method,
|
|
272
|
+
activeItem: existing.activeItem,
|
|
273
|
+
activeItemTitle: existing.activeItemTitle,
|
|
274
|
+
activeItemLevel: existing.activeItemLevel,
|
|
275
|
+
// The default flow's question, the one DELIVERY_STORY_READY recommends answering
|
|
276
|
+
// or replacing with a choice of Delivery Story flow (CR105).
|
|
277
|
+
activeCheckpoint: FLOW_STOPS.story_by_story.checkpoint,
|
|
278
|
+
pendingConfirmation: FLOW_STOPS.story_by_story.confirmation,
|
|
279
|
+
lastDeliveryEvent: "expand",
|
|
280
|
+
cadenceProfile: existing.cadenceProfile,
|
|
281
|
+
cadenceLimits: existing.cadenceLimits,
|
|
282
|
+
granularityDecision: "expanded_to_implementable_stories",
|
|
283
|
+
navigatorFlowUnit: existing.navigatorFlowUnit,
|
|
284
|
+
// Replaced wholesale, so a previous Delivery Story's children cannot survive.
|
|
285
|
+
childWorkItems,
|
|
286
|
+
aggregateCheckpointStatus: existing.aggregateCheckpointStatus,
|
|
287
|
+
},
|
|
288
|
+
deps,
|
|
289
|
+
);
|
|
290
|
+
|
|
291
|
+
return {
|
|
292
|
+
journey,
|
|
293
|
+
method,
|
|
294
|
+
deliveryStory: activeItem,
|
|
295
|
+
deliveryStoryTitle: title,
|
|
296
|
+
materializedPaths,
|
|
297
|
+
materializedArtifacts,
|
|
298
|
+
recommendedStory: recommendedCode,
|
|
299
|
+
recommendedStoryTitle: recommendedTitle,
|
|
300
|
+
cursor,
|
|
301
|
+
nextEvent: "plan",
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/** Python `render_expand_blocked`. */
|
|
306
|
+
export function renderExpandBlocked(activeItem: string, reason: string): string {
|
|
307
|
+
const body = `${[
|
|
308
|
+
"Delivery",
|
|
309
|
+
// Expand is a Delivery Story's stage; only a Delivery Story reaches it (CR113).
|
|
310
|
+
renderLifecycleRibbon("expand", "delivery_story"),
|
|
311
|
+
"",
|
|
312
|
+
"╭────────────────────────────────────────────────────────╮",
|
|
313
|
+
"│ 🧭◆ EXPAND BLOCKED │",
|
|
314
|
+
"│ │",
|
|
315
|
+
cardText("delivery story"),
|
|
316
|
+
cardText(`🟦[${activeItem}]`),
|
|
317
|
+
"│ │",
|
|
318
|
+
cardText("why blocked"),
|
|
319
|
+
...cardWrapped(reason),
|
|
320
|
+
"│ │",
|
|
321
|
+
cardText("required Navigator action"),
|
|
322
|
+
// Names the Delivery Story rather than "the resolved package": the refusal CR018
|
|
323
|
+
// added is for a Delivery Story that has no package to resolve.
|
|
324
|
+
...cardWrapped(
|
|
325
|
+
"Add a canonical candidate-stories table (Markdown table header including " +
|
|
326
|
+
`Code, Story, Type, and Status columns) to ${activeItem}'s index.md, ` +
|
|
327
|
+
"creating it if the Delivery Story has none, or resolve the duplicate heading, " +
|
|
328
|
+
"then Expand again.",
|
|
329
|
+
),
|
|
330
|
+
"│ │",
|
|
331
|
+
cardText("boundary"),
|
|
332
|
+
...cardWrapped("No files were materialized. Expand refuses to fabricate a generic story."),
|
|
333
|
+
"╰────────────────────────────────────────────────────────╯",
|
|
334
|
+
].join("\n")}\n`;
|
|
335
|
+
return wrapAriadSurface("expand_blocked", body);
|
|
336
|
+
}
|