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,433 @@
|
|
|
1
|
+
// CV22.DS7.US6 plateau 6 — the `soul` front-door route.
|
|
2
|
+
//
|
|
3
|
+
// Dispatch only: every behavior it reaches was ported and graded in plateaus
|
|
4
|
+
// 1-5. What this module owns is the argv shape, the exit codes, and the
|
|
5
|
+
// refusals — Python's `cmd_*` wrappers print `Error: <message>` to stderr and
|
|
6
|
+
// exit 1 for a ValueError, and argparse exits 2 for an unknown subcommand or a
|
|
7
|
+
// missing required argument.
|
|
8
|
+
//
|
|
9
|
+
// The subcommands are allowlisted BY NAME by `routing.ts`, so a subcommand
|
|
10
|
+
// Python grows later reaches Python instead of inheriting this route. That is
|
|
11
|
+
// the `conversations append` lesson (RS009/CR055): a family claimed wholesale
|
|
12
|
+
// answered an argv shape it had never implemented, exited 0, and discarded the
|
|
13
|
+
// caller's data.
|
|
14
|
+
|
|
15
|
+
import type { Database, WritableDatabase } from "#db/database.ts";
|
|
16
|
+
import { getIdentityContent } from "#identity/identityRead.ts";
|
|
17
|
+
import { persistStickyDefaults } from "#mirror/orchestration.ts";
|
|
18
|
+
import { resolveRuntimeSessionId } from "#mirror/runtimeSession.ts";
|
|
19
|
+
import { activateOperatingMode } from "#mode/operatingMode.ts";
|
|
20
|
+
import { embeddingLedgerHook } from "#observability/ledgerHooks.ts";
|
|
21
|
+
import { generateEmbeddingSafely } from "#providers/embedding.ts";
|
|
22
|
+
import { resolveFamilyProviders } from "#providers/familyProviders.ts";
|
|
23
|
+
import { SOUL_HARVEST_TRANSPORT } from "#providers/transport.ts";
|
|
24
|
+
import { applyIdentityIntegration, SoulApplyError } from "#soul/apply.ts";
|
|
25
|
+
import {
|
|
26
|
+
persistHarvestSave,
|
|
27
|
+
planHarvestSave,
|
|
28
|
+
SoulHarvestError,
|
|
29
|
+
type TranscriptMessage,
|
|
30
|
+
} from "#soul/harvest.ts";
|
|
31
|
+
import {
|
|
32
|
+
composeSoulBeautyVoicePrompt,
|
|
33
|
+
composeSoulSelfVoicePrompt,
|
|
34
|
+
composeSoulWisdomVoicePrompt,
|
|
35
|
+
} from "#soul/prompts.ts";
|
|
36
|
+
import {
|
|
37
|
+
renderActiveRite,
|
|
38
|
+
renderClosingRite,
|
|
39
|
+
renderEnrichmentProposal,
|
|
40
|
+
renderFruitInMaturation,
|
|
41
|
+
renderHarvestedFruit,
|
|
42
|
+
renderIdentityChangeApplied,
|
|
43
|
+
renderIntegrationReview,
|
|
44
|
+
renderPossibleListenings,
|
|
45
|
+
type SoulListeningOption,
|
|
46
|
+
SoulSurfaceError,
|
|
47
|
+
} from "#soul/render.ts";
|
|
48
|
+
import {
|
|
49
|
+
clearFruitInMaturation,
|
|
50
|
+
clearHarvestedFruit,
|
|
51
|
+
getFruitInMaturation,
|
|
52
|
+
getHarvestedFruit,
|
|
53
|
+
harvestFruit,
|
|
54
|
+
resolveCliSoulSessionId,
|
|
55
|
+
SoulStateError,
|
|
56
|
+
setFruitInMaturation,
|
|
57
|
+
} from "#soul/state.ts";
|
|
58
|
+
import { renderSoulModeTransition } from "#soul/transition.ts";
|
|
59
|
+
import { newId, nowIso } from "#util/pyGenerators.ts";
|
|
60
|
+
import { pyStrip } from "#util/pythonText.ts";
|
|
61
|
+
import { optionValue, stripOptionWithValue } from "./args.ts";
|
|
62
|
+
|
|
63
|
+
/** Python's argparse subcommands, by name. Order is irrelevant; membership is not. */
|
|
64
|
+
export const SOUL_SUBCOMMANDS = [
|
|
65
|
+
"load",
|
|
66
|
+
"listen",
|
|
67
|
+
"rite",
|
|
68
|
+
"close",
|
|
69
|
+
"review",
|
|
70
|
+
"propose",
|
|
71
|
+
"apply",
|
|
72
|
+
"fruit",
|
|
73
|
+
"harvest",
|
|
74
|
+
"prompt",
|
|
75
|
+
] as const;
|
|
76
|
+
|
|
77
|
+
const OPTIONS_WITH_VALUES = [
|
|
78
|
+
"--mirror-home",
|
|
79
|
+
"--db-path",
|
|
80
|
+
"--session-id",
|
|
81
|
+
"--matter",
|
|
82
|
+
"--self",
|
|
83
|
+
"--shadow",
|
|
84
|
+
"--wisdom",
|
|
85
|
+
"--beauty",
|
|
86
|
+
"--says",
|
|
87
|
+
"--listening-for",
|
|
88
|
+
"--question",
|
|
89
|
+
"--harvested",
|
|
90
|
+
"--echoes",
|
|
91
|
+
"--open",
|
|
92
|
+
"--integration",
|
|
93
|
+
"--origin",
|
|
94
|
+
"--journal",
|
|
95
|
+
"--ego",
|
|
96
|
+
"--persona",
|
|
97
|
+
"--key",
|
|
98
|
+
"--current",
|
|
99
|
+
"--proposed",
|
|
100
|
+
"--why",
|
|
101
|
+
"--confirm",
|
|
102
|
+
"--conversation-id",
|
|
103
|
+
"--journal-id",
|
|
104
|
+
"--journey",
|
|
105
|
+
];
|
|
106
|
+
|
|
107
|
+
function positionals(args: readonly string[]): string[] {
|
|
108
|
+
return OPTIONS_WITH_VALUES.reduce<string[]>(
|
|
109
|
+
(remaining, option) => stripOptionWithValue(remaining, option),
|
|
110
|
+
[...args],
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function fail(message: string): number {
|
|
115
|
+
process.stderr.write(`Error: ${message}\n`);
|
|
116
|
+
return 1;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** argparse's own exit for an unknown subcommand or a missing required argument. */
|
|
120
|
+
function usageError(message: string): number {
|
|
121
|
+
process.stderr.write(`${message}\n`);
|
|
122
|
+
return 2;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export interface SoulRouteDeps {
|
|
126
|
+
readMessages: (db: Database, conversationId: string) => TranscriptMessage[];
|
|
127
|
+
/**
|
|
128
|
+
* The harvest journal's embedding (CV22.DS8.US3).
|
|
129
|
+
*
|
|
130
|
+
* Async because it is a real, billable provider round trip: `routing.ts`
|
|
131
|
+
* used to keep `harvest save` on Python unless a replay fixture was set, and
|
|
132
|
+
* nothing ever supplied one through the front door -- so the only leaf of
|
|
133
|
+
* the Soul command that crosses the provider seam could not complete on
|
|
134
|
+
* TypeScript at all. The route now resolves the family's provider and
|
|
135
|
+
* embeds through `generateEmbeddingSafely`, with the ledger hook attached.
|
|
136
|
+
*/
|
|
137
|
+
embed: (db: WritableDatabase, text: string) => Promise<Uint8Array>;
|
|
138
|
+
newId: () => string;
|
|
139
|
+
nowIso: () => string;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export const defaultSoulRouteDeps: SoulRouteDeps = {
|
|
143
|
+
readMessages: (db, conversationId) =>
|
|
144
|
+
db
|
|
145
|
+
.prepare("SELECT role, content FROM messages WHERE conversation_id = ? ORDER BY rowid")
|
|
146
|
+
.all(conversationId) as unknown as TranscriptMessage[],
|
|
147
|
+
embed: async (db, text) => {
|
|
148
|
+
const family = await resolveFamilyProviders(process.env, SOUL_HARVEST_TRANSPORT);
|
|
149
|
+
if (!family?.embedding) {
|
|
150
|
+
// `routing.ts` reverts the family before the route is reached, so this
|
|
151
|
+
// is defense in depth rather than the operator-facing message.
|
|
152
|
+
throw new SoulHarvestError("soul harvest save requires an embedding provider");
|
|
153
|
+
}
|
|
154
|
+
// Through the wrapper, like every other embedding path: bounded retry of a
|
|
155
|
+
// transient empty payload, the AI-07 dimension guard, and one ledger row
|
|
156
|
+
// per round trip, priced (CR075's adjacent case).
|
|
157
|
+
const vector = await generateEmbeddingSafely(family.embedding, text, {
|
|
158
|
+
onAttempt: embeddingLedgerHook(db),
|
|
159
|
+
});
|
|
160
|
+
return new Uint8Array(new Float32Array(vector).buffer);
|
|
161
|
+
},
|
|
162
|
+
newId,
|
|
163
|
+
nowIso,
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
export async function runSoulRoute(
|
|
167
|
+
db: WritableDatabase,
|
|
168
|
+
argv: readonly string[],
|
|
169
|
+
deps: SoulRouteDeps = defaultSoulRouteDeps,
|
|
170
|
+
): Promise<number> {
|
|
171
|
+
const rawArgs = argv.slice(1);
|
|
172
|
+
const args = positionals(rawArgs);
|
|
173
|
+
const sub = args[0];
|
|
174
|
+
const option = (name: string) => optionValue(rawArgs, name);
|
|
175
|
+
|
|
176
|
+
try {
|
|
177
|
+
switch (sub) {
|
|
178
|
+
case "load":
|
|
179
|
+
return runLoad(db, args[1] ?? null, option("--session-id"), deps);
|
|
180
|
+
case "listen":
|
|
181
|
+
return write(renderPossibleListenings(listeningOptions(option)));
|
|
182
|
+
case "rite": {
|
|
183
|
+
const voice = args[1];
|
|
184
|
+
if (!voice) return usageError("soul rite requires a voice");
|
|
185
|
+
return write(
|
|
186
|
+
renderActiveRite(voice, {
|
|
187
|
+
utterance: option("--says"),
|
|
188
|
+
listeningFor: option("--listening-for"),
|
|
189
|
+
question: option("--question"),
|
|
190
|
+
}),
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
case "close":
|
|
194
|
+
return write(
|
|
195
|
+
renderClosingRite({
|
|
196
|
+
harvested: option("--harvested"),
|
|
197
|
+
echoes: option("--echoes"),
|
|
198
|
+
remainsOpen: option("--open"),
|
|
199
|
+
integration: option("--integration"),
|
|
200
|
+
}),
|
|
201
|
+
);
|
|
202
|
+
case "review":
|
|
203
|
+
return write(
|
|
204
|
+
renderIntegrationReview({
|
|
205
|
+
// `--origin` and its legacy alias `--journal` share one argparse
|
|
206
|
+
// destination, so the LAST one on the command line wins -- not a
|
|
207
|
+
// fixed precedence between them.
|
|
208
|
+
journal: lastOptionValue(rawArgs, ["--origin", "--journal"]),
|
|
209
|
+
selfMaterial: option("--self"),
|
|
210
|
+
shadow: option("--shadow"),
|
|
211
|
+
ego: option("--ego"),
|
|
212
|
+
persona: option("--persona"),
|
|
213
|
+
leaveOpen: option("--open"),
|
|
214
|
+
}),
|
|
215
|
+
);
|
|
216
|
+
case "propose": {
|
|
217
|
+
const layer = args[1];
|
|
218
|
+
if (!layer) return usageError("soul propose requires a layer");
|
|
219
|
+
const key = resolveIdentityKey(layer, option("--key"));
|
|
220
|
+
return write(
|
|
221
|
+
renderEnrichmentProposal(layer, {
|
|
222
|
+
key,
|
|
223
|
+
origin: option("--origin") ?? "",
|
|
224
|
+
current: option("--current"),
|
|
225
|
+
proposed: option("--proposed") ?? "",
|
|
226
|
+
why: option("--why") ?? "",
|
|
227
|
+
}),
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
case "apply":
|
|
231
|
+
return runApply(db, args[1], option, deps);
|
|
232
|
+
case "fruit":
|
|
233
|
+
return runFruit(db, args[1], args[2] ?? null, option("--session-id"), deps);
|
|
234
|
+
case "harvest":
|
|
235
|
+
return await runHarvest(db, args[1], args[2] ?? null, option, deps);
|
|
236
|
+
case "prompt":
|
|
237
|
+
return runPrompt(db, args[1]);
|
|
238
|
+
default:
|
|
239
|
+
return usageError(`soul: unknown subcommand ${sub ?? ""}`);
|
|
240
|
+
}
|
|
241
|
+
} catch (error) {
|
|
242
|
+
if (
|
|
243
|
+
error instanceof SoulSurfaceError ||
|
|
244
|
+
error instanceof SoulStateError ||
|
|
245
|
+
error instanceof SoulApplyError ||
|
|
246
|
+
error instanceof SoulHarvestError
|
|
247
|
+
) {
|
|
248
|
+
return fail(error.message);
|
|
249
|
+
}
|
|
250
|
+
throw error;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/** The value of whichever of `names` appears LAST in argv, as argparse resolves a shared dest. */
|
|
255
|
+
function lastOptionValue(args: readonly string[], names: readonly string[]): string | null {
|
|
256
|
+
let value: string | null = null;
|
|
257
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
258
|
+
if (names.includes(args[index] as string)) value = args[index + 1] ?? null;
|
|
259
|
+
}
|
|
260
|
+
return value;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function write(rendered: string): number {
|
|
264
|
+
process.stdout.write(`${rendered}\n`);
|
|
265
|
+
return 0;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function listeningOptions(option: (name: string) => string | null): SoulListeningOption[] {
|
|
269
|
+
const raw: [string, string | null][] = [
|
|
270
|
+
["self", option("--self")],
|
|
271
|
+
["shadow", option("--shadow")],
|
|
272
|
+
["wisdom", option("--wisdom")],
|
|
273
|
+
["beauty", option("--beauty")],
|
|
274
|
+
];
|
|
275
|
+
return raw
|
|
276
|
+
.filter(([, description]) => description !== null && description.trim() !== "")
|
|
277
|
+
.map(([voice, description]) => ({ voice, description: (description as string).trim() }));
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/** Port of `_resolve_identity_key`. */
|
|
281
|
+
function resolveIdentityKey(layer: string, key: string | null): string {
|
|
282
|
+
if (layer === "self") return key || "soul";
|
|
283
|
+
if (layer === "shadow") return key || "profile";
|
|
284
|
+
if (layer === "ego") return key || "behavior";
|
|
285
|
+
if (layer === "persona") {
|
|
286
|
+
if (!key) throw new SoulApplyError("persona proposals require --key");
|
|
287
|
+
return key;
|
|
288
|
+
}
|
|
289
|
+
throw new SoulApplyError(`unsupported psyche layer: ${layer}`);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function runLoad(
|
|
293
|
+
db: WritableDatabase,
|
|
294
|
+
slug: string | null,
|
|
295
|
+
sessionId: string | null,
|
|
296
|
+
deps: SoulRouteDeps,
|
|
297
|
+
): number {
|
|
298
|
+
const now = deps.nowIso();
|
|
299
|
+
if (slug) {
|
|
300
|
+
if (!getIdentityContent(db, "journey", slug)) {
|
|
301
|
+
process.stderr.write(`Error: journey '${slug}' not found.\n`);
|
|
302
|
+
return 1;
|
|
303
|
+
}
|
|
304
|
+
persistStickyDefaults(db, null, slug, now);
|
|
305
|
+
}
|
|
306
|
+
// `cmd_load` uses the OPERATING-MODE resolver, not the Soul one, and that
|
|
307
|
+
// resolver may return null -- in which case `activate_mode` writes the GLOBAL
|
|
308
|
+
// mode row instead of a session row. Falling back to `__global_soul_mode__`
|
|
309
|
+
// here would silently write to a different row than Python does.
|
|
310
|
+
const resolvedSessionId = resolveRuntimeSessionId(
|
|
311
|
+
db,
|
|
312
|
+
sessionId,
|
|
313
|
+
process.env.MIRROR_SESSION_ID ?? null,
|
|
314
|
+
);
|
|
315
|
+
activateOperatingMode(
|
|
316
|
+
db,
|
|
317
|
+
{ mode: "Soul Mode", journey: slug, sessionId: resolvedSessionId },
|
|
318
|
+
now,
|
|
319
|
+
);
|
|
320
|
+
return write(renderSoulModeTransition(slug));
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function runApply(
|
|
324
|
+
db: WritableDatabase,
|
|
325
|
+
layer: string | undefined,
|
|
326
|
+
option: (name: string) => string | null,
|
|
327
|
+
deps: SoulRouteDeps,
|
|
328
|
+
): number {
|
|
329
|
+
if (!layer) return usageError("soul apply requires a layer");
|
|
330
|
+
const key = resolveIdentityKey(layer, option("--key"));
|
|
331
|
+
if (option("--confirm") !== "APPLY") {
|
|
332
|
+
return fail("identity update requires --confirm APPLY");
|
|
333
|
+
}
|
|
334
|
+
const content = pyStrip(option("--proposed") ?? "");
|
|
335
|
+
if (!content) return fail("identity content must not be empty");
|
|
336
|
+
applyIdentityIntegration(
|
|
337
|
+
db,
|
|
338
|
+
{
|
|
339
|
+
layer,
|
|
340
|
+
key,
|
|
341
|
+
content,
|
|
342
|
+
origin: option("--origin"),
|
|
343
|
+
conversationId: option("--conversation-id"),
|
|
344
|
+
journalId: option("--journal-id"),
|
|
345
|
+
},
|
|
346
|
+
{ newId: deps.newId, nowIso: deps.nowIso() },
|
|
347
|
+
);
|
|
348
|
+
return write(renderIdentityChangeApplied(layer, { key, content }));
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
function runFruit(
|
|
352
|
+
db: WritableDatabase,
|
|
353
|
+
action: string | undefined,
|
|
354
|
+
fruit: string | null,
|
|
355
|
+
sessionId: string | null,
|
|
356
|
+
deps: SoulRouteDeps,
|
|
357
|
+
): number {
|
|
358
|
+
const resolved = resolveCliSoulSessionId(db, sessionId, process.env.MIRROR_SESSION_ID ?? null);
|
|
359
|
+
if (action === "set") {
|
|
360
|
+
if (fruit === null) return usageError("soul fruit set requires a fruit");
|
|
361
|
+
const state = setFruitInMaturation(db, fruit, resolved, deps.nowIso());
|
|
362
|
+
return write(renderFruitInMaturation(state.fruit ?? ""));
|
|
363
|
+
}
|
|
364
|
+
if (action === "show") {
|
|
365
|
+
const state = getFruitInMaturation(db, resolved);
|
|
366
|
+
if (!state.fruit) return fail("No fruit in maturation.");
|
|
367
|
+
return write(renderFruitInMaturation(state.fruit));
|
|
368
|
+
}
|
|
369
|
+
if (action === "clear") {
|
|
370
|
+
clearFruitInMaturation(db, resolved, deps.nowIso());
|
|
371
|
+
process.stdout.write("Fruit in maturation cleared.\n");
|
|
372
|
+
return 0;
|
|
373
|
+
}
|
|
374
|
+
return usageError(`soul fruit: unknown action ${action ?? ""}`);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
async function runHarvest(
|
|
378
|
+
db: WritableDatabase,
|
|
379
|
+
action: string | undefined,
|
|
380
|
+
fruit: string | null,
|
|
381
|
+
option: (name: string) => string | null,
|
|
382
|
+
deps: SoulRouteDeps,
|
|
383
|
+
): Promise<number> {
|
|
384
|
+
const resolved = resolveCliSoulSessionId(
|
|
385
|
+
db,
|
|
386
|
+
option("--session-id"),
|
|
387
|
+
process.env.MIRROR_SESSION_ID ?? null,
|
|
388
|
+
);
|
|
389
|
+
if (action === "set") {
|
|
390
|
+
const state = harvestFruit(db, { fruit, sessionId: resolved }, deps.nowIso());
|
|
391
|
+
return write(renderHarvestedFruit(state.fruit ?? ""));
|
|
392
|
+
}
|
|
393
|
+
if (action === "show") {
|
|
394
|
+
const state = getHarvestedFruit(db, resolved);
|
|
395
|
+
if (!state.fruit) return fail("No harvested fruit.");
|
|
396
|
+
return write(renderHarvestedFruit(state.fruit));
|
|
397
|
+
}
|
|
398
|
+
if (action === "save") {
|
|
399
|
+
const state = getHarvestedFruit(db, resolved);
|
|
400
|
+
if (!state.fruit) return fail("No harvested fruit.");
|
|
401
|
+
// Compose, then embed, then persist. The fruit is cleared inside the
|
|
402
|
+
// persist step, so a failing embedding leaves it harvested for a retry
|
|
403
|
+
// rather than losing it to a provider outage -- Python's ordering.
|
|
404
|
+
const plan = planHarvestSave(db, resolved, (conversationId) =>
|
|
405
|
+
deps.readMessages(db, conversationId),
|
|
406
|
+
);
|
|
407
|
+
const embedding = await deps.embed(db, plan.embedText);
|
|
408
|
+
const result = persistHarvestSave(
|
|
409
|
+
db,
|
|
410
|
+
plan,
|
|
411
|
+
{ sessionId: resolved, journey: option("--journey") },
|
|
412
|
+
{ newId: deps.newId, nowIso: deps.nowIso(), embedding },
|
|
413
|
+
);
|
|
414
|
+
process.stdout.write(`Harvest saved to journal. ${result.memoryId}\n`);
|
|
415
|
+
return 0;
|
|
416
|
+
}
|
|
417
|
+
if (action === "decline") {
|
|
418
|
+
clearHarvestedFruit(db, resolved, deps.nowIso());
|
|
419
|
+
process.stdout.write("Harvest discarded without journal save.\n");
|
|
420
|
+
return 0;
|
|
421
|
+
}
|
|
422
|
+
return usageError(`soul harvest: unknown action ${action ?? ""}`);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
function runPrompt(db: Database, voice: string | undefined): number {
|
|
426
|
+
// `print(prompt)` appends a newline to a template that already ends with one,
|
|
427
|
+
// so the shipped output ends with two. Stripping either is a divergence.
|
|
428
|
+
if (voice === "self") return write(composeSoulSelfVoicePrompt(db));
|
|
429
|
+
if (voice === "wisdom") return write(composeSoulWisdomVoicePrompt());
|
|
430
|
+
if (voice === "beauty") return write(composeSoulBeautyVoicePrompt());
|
|
431
|
+
process.stderr.write(`Error: unsupported Soul Mode prompt voice: ${voice ?? ""}\n`);
|
|
432
|
+
return 1;
|
|
433
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Where a stdin-reading route gets its payload (CV21.DS2.US8).
|
|
2
|
+
//
|
|
3
|
+
// Two of the conversation logger's subcommands read a hook payload from
|
|
4
|
+
// stdin: `user-prompt` and `stop`. Until US8 the Claude hook entries did not
|
|
5
|
+
// touch fd 0 and let the route read it. Now an entry reads the payload first,
|
|
6
|
+
// to detect the runtime it is in (`runtimeDetection.ts`), and fd 0 cannot be
|
|
7
|
+
// read twice. So the entry hands the payload over here, and the route takes
|
|
8
|
+
// it before falling back to fd 0.
|
|
9
|
+
//
|
|
10
|
+
// A one-shot handoff rather than an argument because the hooks run the front
|
|
11
|
+
// door in-process (`runFrontDoor`), through the same `main(argv)` every other
|
|
12
|
+
// caller uses, and a payload does not belong in argv: argv is what `ps`
|
|
13
|
+
// shows, and what the Gemini hooks already put a prompt into is the exception
|
|
14
|
+
// this seam avoids repeating.
|
|
15
|
+
|
|
16
|
+
import { readFileSync } from "node:fs";
|
|
17
|
+
|
|
18
|
+
let provided: string | null = null;
|
|
19
|
+
|
|
20
|
+
/** Hand a payload to the next stdin-reading route in this process. */
|
|
21
|
+
export function provideStdin(text: string): void {
|
|
22
|
+
provided = text;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** The provided payload, once; else fd 0 to its end; "" when there is none. */
|
|
26
|
+
export function takeStdin(): string {
|
|
27
|
+
if (provided !== null) {
|
|
28
|
+
const text = provided;
|
|
29
|
+
provided = null;
|
|
30
|
+
return text;
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
return readFileSync(0, "utf-8");
|
|
34
|
+
} catch {
|
|
35
|
+
return "";
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
// Front-door `tasks import|sync|sync-config` write handlers (CV22.DS7.US2
|
|
2
|
+
// slice 3c). The testable core of routing these subcommands to the TS core,
|
|
3
|
+
// mirroring `memory.cli.tasks_cmd`'s `cmd_import`/`cmd_sync`/`cmd_sync_config`.
|
|
4
|
+
|
|
5
|
+
import { existsSync } from "node:fs";
|
|
6
|
+
import type { Database, WritableDatabase } from "#db/database.ts";
|
|
7
|
+
import { listIdentityByLayer } from "#identity/identityRead.ts";
|
|
8
|
+
import { getSyncFile, JOURNEY_LAYER, setSyncFile } from "#journey/journeySyncFile.ts";
|
|
9
|
+
import {
|
|
10
|
+
importTasksFromJourneyPath,
|
|
11
|
+
type SyncResult,
|
|
12
|
+
syncTasksFromFile,
|
|
13
|
+
} from "#tasks/taskImportSync.ts";
|
|
14
|
+
import type { Task } from "#tasks/taskStore.ts";
|
|
15
|
+
import { normalizeProjectPath } from "#util/paths.ts";
|
|
16
|
+
|
|
17
|
+
/** Journeys to operate on for `tasks import [journey]`: the explicit one, or every known journey (key order). */
|
|
18
|
+
export function resolveImportJourneys(db: Database, explicitJourney: string | null): string[] {
|
|
19
|
+
if (explicitJourney) return [explicitJourney];
|
|
20
|
+
return listIdentityByLayer(db, JOURNEY_LAYER).map((row) => row.key);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** One journey's import result, included in rendering only when it created at least one task. */
|
|
24
|
+
export interface ImportJourneyResult {
|
|
25
|
+
journey: string;
|
|
26
|
+
created: Task[];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* `tasks import [journey]`: import pending tasks for each resolved journey.
|
|
31
|
+
* Matches Python's `if created:` filter -- a journey with zero imports never
|
|
32
|
+
* appears in the render, but still contributes to nothing (not an error).
|
|
33
|
+
*/
|
|
34
|
+
export function applyTasksImport(
|
|
35
|
+
db: WritableDatabase,
|
|
36
|
+
explicitJourney: string | null,
|
|
37
|
+
): ImportJourneyResult[] {
|
|
38
|
+
const journeys = resolveImportJourneys(db, explicitJourney);
|
|
39
|
+
const results: ImportJourneyResult[] = [];
|
|
40
|
+
for (const journey of journeys) {
|
|
41
|
+
const created = importTasksFromJourneyPath(db, journey);
|
|
42
|
+
if (created.length > 0) {
|
|
43
|
+
results.push({ journey, created });
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return results;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Journeys to operate on for `tasks sync [journey]`: the explicit one (regardless of sync-file config), or every journey WITH a sync file configured. */
|
|
50
|
+
export function resolveSyncJourneys(db: Database, explicitJourney: string | null): string[] {
|
|
51
|
+
if (explicitJourney) return [explicitJourney];
|
|
52
|
+
return listIdentityByLayer(db, JOURNEY_LAYER)
|
|
53
|
+
.map((row) => row.key)
|
|
54
|
+
.filter((key) => getSyncFile(db, key) !== null);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export type SyncJourneyOutcome =
|
|
58
|
+
| { kind: "no_sync_file"; journey: string }
|
|
59
|
+
| { kind: "synced"; journey: string; syncFile: string; result: SyncResult }
|
|
60
|
+
| { kind: "error"; journey: string; message: string };
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* `tasks sync`'s per-journey step: re-checks the sync file (an explicit
|
|
64
|
+
* journey may have none configured -- Python's own per-iteration check, not a
|
|
65
|
+
* whole-command precondition), then syncs, catching ANY error the same way
|
|
66
|
+
* Python's two `except` clauses collapse to the identical print.
|
|
67
|
+
*/
|
|
68
|
+
export function applyTasksSyncForJourney(
|
|
69
|
+
db: WritableDatabase,
|
|
70
|
+
journey: string,
|
|
71
|
+
): SyncJourneyOutcome {
|
|
72
|
+
const syncFile = getSyncFile(db, journey);
|
|
73
|
+
if (!syncFile) return { kind: "no_sync_file", journey };
|
|
74
|
+
try {
|
|
75
|
+
const result = syncTasksFromFile(db, journey);
|
|
76
|
+
return { kind: "synced", journey, syncFile, result };
|
|
77
|
+
} catch (error) {
|
|
78
|
+
return {
|
|
79
|
+
kind: "error",
|
|
80
|
+
journey,
|
|
81
|
+
message: error instanceof Error ? error.message : String(error),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface SyncConfigOutcome {
|
|
87
|
+
journey: string;
|
|
88
|
+
resolvedPath: string;
|
|
89
|
+
fileExisted: boolean;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* `tasks sync-config <journey> <file>`: normalize the path (the same
|
|
94
|
+
* expanduser+resolve `normalizeProjectPath` already ports for `journey
|
|
95
|
+
* set-path`), record whether it currently exists (a warning, not a failure),
|
|
96
|
+
* and configure it. Throws `JourneyNotFoundError` for an unknown journey --
|
|
97
|
+
* Python's `cmd_sync_config` does not catch this either (see
|
|
98
|
+
* `journeySyncFile.ts`'s module doc).
|
|
99
|
+
*/
|
|
100
|
+
export function applyTasksSyncConfig(
|
|
101
|
+
db: WritableDatabase,
|
|
102
|
+
journey: string,
|
|
103
|
+
rawFilePath: string,
|
|
104
|
+
nowIso: string,
|
|
105
|
+
): SyncConfigOutcome {
|
|
106
|
+
const resolvedPath = normalizeProjectPath(rawFilePath);
|
|
107
|
+
const fileExisted = existsSync(resolvedPath);
|
|
108
|
+
setSyncFile(db, journey, resolvedPath, nowIso);
|
|
109
|
+
return { journey, resolvedPath, fileExisted };
|
|
110
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
// Front-door `tasks add|done|doing|block|delete` write handlers (CV22.DS7.US2
|
|
2
|
+
// slice 3a). The testable core of routing these subcommands to the TS core:
|
|
3
|
+
// each function takes an already-open `WritableDatabase` plus injected
|
|
4
|
+
// id/now (the DS4 determinism-injection idiom) and returns a result the
|
|
5
|
+
// `render/tasks.ts` module turns into Python-identical stdout.
|
|
6
|
+
//
|
|
7
|
+
// `tasks import`/`sync`/`sync-config` (which also depend on the journey
|
|
8
|
+
// sync-file/project-path metadata subsystem) and `week plan`/`save` (LLM-gated,
|
|
9
|
+
// reassigned to US5) are NOT here -- see the plan's slice breakdown.
|
|
10
|
+
|
|
11
|
+
import type { WritableDatabase } from "#db/database.ts";
|
|
12
|
+
import {
|
|
13
|
+
completeTask,
|
|
14
|
+
createTask,
|
|
15
|
+
deleteTaskById,
|
|
16
|
+
resolveTaskByIdOrPrefix,
|
|
17
|
+
type Task,
|
|
18
|
+
updateTaskStatus,
|
|
19
|
+
} from "#tasks/taskStore.ts";
|
|
20
|
+
|
|
21
|
+
/** Fields accepted by `tasks add`, mirroring the Python CLI's flags. */
|
|
22
|
+
export interface AddTaskParams {
|
|
23
|
+
title: string;
|
|
24
|
+
journey?: string | null;
|
|
25
|
+
due?: string | null;
|
|
26
|
+
stage?: string | null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** `tasks add`: create the task and return it for rendering. */
|
|
30
|
+
export function applyTasksAdd(
|
|
31
|
+
db: WritableDatabase,
|
|
32
|
+
params: AddTaskParams,
|
|
33
|
+
id: string,
|
|
34
|
+
nowIso: string,
|
|
35
|
+
): Task {
|
|
36
|
+
return createTask(
|
|
37
|
+
db,
|
|
38
|
+
{ title: params.title, journey: params.journey, dueDate: params.due, stage: params.stage },
|
|
39
|
+
id,
|
|
40
|
+
nowIso,
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** The Python-visible status a `done|doing|block` command applies. */
|
|
45
|
+
export type TaskStatusTarget = "doing" | "blocked" | "done";
|
|
46
|
+
|
|
47
|
+
export type StatusChangeOutcome =
|
|
48
|
+
| { kind: "changed"; task: Task; newStatus: TaskStatusTarget }
|
|
49
|
+
| { kind: "ambiguous"; idOrPrefix: string; matches: Task[] }
|
|
50
|
+
| { kind: "not_found"; idOrPrefix: string };
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* `tasks done|doing|block`: resolve the id/prefix via the ONE shared resolver,
|
|
54
|
+
* apply the status change, and report the outcome. `done` completes the task
|
|
55
|
+
* (status + completed_at); `doing`/`blocked` only change status -- matching
|
|
56
|
+
* Python's `if new_status == "done": complete_task(...) else: update_task(...)`.
|
|
57
|
+
*/
|
|
58
|
+
export function applyTasksStatusChange(
|
|
59
|
+
db: WritableDatabase,
|
|
60
|
+
idOrPrefix: string,
|
|
61
|
+
newStatus: TaskStatusTarget,
|
|
62
|
+
nowIso: string,
|
|
63
|
+
): StatusChangeOutcome {
|
|
64
|
+
const resolved = resolveTaskByIdOrPrefix(db, idOrPrefix);
|
|
65
|
+
if (resolved.kind === "not_found") return { kind: "not_found", idOrPrefix };
|
|
66
|
+
if (resolved.kind === "ambiguous") {
|
|
67
|
+
return { kind: "ambiguous", idOrPrefix, matches: resolved.matches };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (newStatus === "done") {
|
|
71
|
+
completeTask(db, resolved.task.id, nowIso);
|
|
72
|
+
} else {
|
|
73
|
+
updateTaskStatus(db, resolved.task.id, newStatus, nowIso);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const task: Task = {
|
|
77
|
+
...resolved.task,
|
|
78
|
+
status: newStatus,
|
|
79
|
+
updated_at: nowIso,
|
|
80
|
+
completed_at: newStatus === "done" ? nowIso : resolved.task.completed_at,
|
|
81
|
+
};
|
|
82
|
+
return { kind: "changed", task, newStatus };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export type DeleteOutcome =
|
|
86
|
+
| { kind: "deleted"; task: Task }
|
|
87
|
+
| { kind: "not_found"; idOrPrefix: string };
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* `tasks delete`: resolve via the SAME shared resolver as status-change, but
|
|
91
|
+
* fold "ambiguous" into "not_found" -- Python's `cmd_delete` only branches on
|
|
92
|
+
* `len(matches) == 1`, treating both zero AND multiple matches as "not found".
|
|
93
|
+
* This is the deliberate asymmetry the plan calls out; it is expressed here,
|
|
94
|
+
* in presentation, not inside the shared resolver.
|
|
95
|
+
*/
|
|
96
|
+
export function applyTasksDelete(db: WritableDatabase, idOrPrefix: string): DeleteOutcome {
|
|
97
|
+
const resolved = resolveTaskByIdOrPrefix(db, idOrPrefix);
|
|
98
|
+
if (resolved.kind !== "found") return { kind: "not_found", idOrPrefix };
|
|
99
|
+
deleteTaskById(db, resolved.task.id);
|
|
100
|
+
return { kind: "deleted", task: resolved.task };
|
|
101
|
+
}
|