mirror-mind 0.0.1 → 0.32.1
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-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 +561 -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 +99 -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.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 +13 -0
- package/plugins/mirror-mind/hooks/hooks.json +50 -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/mcp/launch.sh +52 -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 +65 -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/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 +576 -0
- package/ts/src/conversation/loggerCli.ts +282 -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 +1865 -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 +73 -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 +999 -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/soulRoute.ts +433 -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 +252 -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 +176 -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 +68 -0
- package/ts/src/hooks/runtime.ts +159 -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/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,131 @@
|
|
|
1
|
+
// Journey listing parity port (CV22.DS2.US3).
|
|
2
|
+
//
|
|
3
|
+
// A faithful TypeScript port of Python `JourneyService.list_journey_options` +
|
|
4
|
+
// `_sort_journey_options` (`src/memory/services/journey.py`). The logic is pure
|
|
5
|
+
// over the `journey` identity rows: it derives each journey's display name,
|
|
6
|
+
// status, and parent from row content/metadata, then orders every descendant in
|
|
7
|
+
// bounded depth-first order. Reading the rows out of the SQLite
|
|
8
|
+
// seam belongs to the caller, keeping this the pure decision core.
|
|
9
|
+
|
|
10
|
+
import { resolveParentJourney } from "./parentJourney.ts";
|
|
11
|
+
|
|
12
|
+
/** One `journey`-layer identity row, as read from the DB. */
|
|
13
|
+
export interface JourneyIdentityRow {
|
|
14
|
+
key: string;
|
|
15
|
+
content: string;
|
|
16
|
+
metadata?: string | null;
|
|
17
|
+
/** Migration 017's derived parent projection. Metadata remains semantic
|
|
18
|
+
* authority while Python and TS can both write parentage (CR050). */
|
|
19
|
+
parent_journey?: string | null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** A journey option DTO with hierarchy metadata, mirroring the Python output. */
|
|
23
|
+
export interface JourneyOption {
|
|
24
|
+
id: string;
|
|
25
|
+
name: string;
|
|
26
|
+
status: string;
|
|
27
|
+
parent_journey: string;
|
|
28
|
+
depth: number;
|
|
29
|
+
lineage: string[];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
type UnorderedJourneyOption = Omit<JourneyOption, "depth" | "lineage">;
|
|
33
|
+
|
|
34
|
+
/** Build the unordered option fields for a single journey row. */
|
|
35
|
+
function toOption(row: JourneyIdentityRow): UnorderedJourneyOption {
|
|
36
|
+
const content = row.content || "";
|
|
37
|
+
// Python: content.split("\n")[0].strip().lstrip("# ").strip()
|
|
38
|
+
const firstLine = (content.split("\n")[0] ?? "")
|
|
39
|
+
.trim()
|
|
40
|
+
.replace(/^[# ]+/, "")
|
|
41
|
+
.trim();
|
|
42
|
+
const statusMatch = content.match(/\*\*Status:\*\*\s*([^\n]+)/);
|
|
43
|
+
const status = statusMatch ? statusMatch[1].trim() : "unknown";
|
|
44
|
+
return {
|
|
45
|
+
id: row.key,
|
|
46
|
+
name: firstLine || row.key,
|
|
47
|
+
status,
|
|
48
|
+
parent_journey: resolveParentJourney(row),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** A roots-then-children split of journey-like items keyed by `parent_journey`. */
|
|
53
|
+
export interface JourneyHierarchy<T> {
|
|
54
|
+
roots: T[];
|
|
55
|
+
childrenByParent: Map<string, T[]>;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Split items into roots and children by `parent_journey`, preserving input
|
|
60
|
+
* order within each group. An item whose parent is empty or absent from the set
|
|
61
|
+
* is a root. The depth-first sorter consumes this index; renderers consume the
|
|
62
|
+
* resulting ordered projection and must not reconstruct a shallower hierarchy.
|
|
63
|
+
*/
|
|
64
|
+
export function groupJourneysByParent<T extends { id: string; parent_journey: string }>(
|
|
65
|
+
items: readonly T[],
|
|
66
|
+
): JourneyHierarchy<T> {
|
|
67
|
+
const knownIds = new Set(items.map((item) => item.id));
|
|
68
|
+
const roots: T[] = [];
|
|
69
|
+
const childrenByParent = new Map<string, T[]>();
|
|
70
|
+
for (const item of items) {
|
|
71
|
+
const parent = item.parent_journey || "";
|
|
72
|
+
if (parent && knownIds.has(parent)) {
|
|
73
|
+
const bucket = childrenByParent.get(parent);
|
|
74
|
+
if (bucket) bucket.push(item);
|
|
75
|
+
else childrenByParent.set(parent, [item]);
|
|
76
|
+
} else {
|
|
77
|
+
roots.push(item);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return { roots, childrenByParent };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Order every option in bounded depth-first hierarchy order, mirroring the
|
|
85
|
+
* released Python `_sort_journey_options` oracle.
|
|
86
|
+
*
|
|
87
|
+
* Roots (no parent, or a parent absent from the set) and every sibling set are
|
|
88
|
+
* sorted by `(status !== "active", name.toLowerCase())`. A visited set makes
|
|
89
|
+
* malformed legacy cycles bounded; a final sorted pass starts each rootless
|
|
90
|
+
* component so corrupt rows remain visible exactly once.
|
|
91
|
+
*/
|
|
92
|
+
function sortJourneyOptions(options: UnorderedJourneyOption[]): JourneyOption[] {
|
|
93
|
+
const { roots, childrenByParent } = groupJourneysByParent(options);
|
|
94
|
+
|
|
95
|
+
const compare = (a: UnorderedJourneyOption, b: UnorderedJourneyOption): number => {
|
|
96
|
+
const aInactive = a.status !== "active" ? 1 : 0;
|
|
97
|
+
const bInactive = b.status !== "active" ? 1 : 0;
|
|
98
|
+
if (aInactive !== bInactive) return aInactive - bInactive;
|
|
99
|
+
const aName = a.name.toLowerCase();
|
|
100
|
+
const bName = b.name.toLowerCase();
|
|
101
|
+
return aName < bName ? -1 : aName > bName ? 1 : 0;
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const ordered: JourneyOption[] = [];
|
|
105
|
+
const visited = new Set<string>();
|
|
106
|
+
|
|
107
|
+
const appendBranch = (item: UnorderedJourneyOption, lineage: string[]): void => {
|
|
108
|
+
if (visited.has(item.id)) return;
|
|
109
|
+
visited.add(item.id);
|
|
110
|
+
const currentLineage = [...lineage, item.id];
|
|
111
|
+
ordered.push({ ...item, depth: lineage.length, lineage: currentLineage });
|
|
112
|
+
for (const child of [...(childrenByParent.get(item.id) ?? [])].sort(compare)) {
|
|
113
|
+
appendBranch(child, currentLineage);
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
for (const root of [...roots].sort(compare)) appendBranch(root, []);
|
|
118
|
+
for (const unvisited of [...options].sort(compare)) {
|
|
119
|
+
if (!visited.has(unvisited.id)) appendBranch(unvisited, []);
|
|
120
|
+
}
|
|
121
|
+
return ordered;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Return all journeys as option DTOs with hierarchy metadata, reproducing the
|
|
126
|
+
* Python oracle. `rows` must arrive in the DB's `ORDER BY key` order (as
|
|
127
|
+
* `get_identity_by_layer` returns them) so stable-sort tie-breaks match Python.
|
|
128
|
+
*/
|
|
129
|
+
export function listJourneyOptions(rows: readonly JourneyIdentityRow[]): JourneyOption[] {
|
|
130
|
+
return sortJourneyOptions(rows.map(toOption));
|
|
131
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
// Conservative journey removal ported from the v0.31.9 Python domain operation.
|
|
2
|
+
//
|
|
3
|
+
// A journey is deletable only when it exists, has no metadata-authoritative
|
|
4
|
+
// children, and has no durable association in the closed inventory below. The
|
|
5
|
+
// complete check and the single identity DELETE share BEGIN IMMEDIATE so a
|
|
6
|
+
// competing writer that commits first is visible before the decision.
|
|
7
|
+
|
|
8
|
+
import { type WritableDatabase, withTransaction } from "#db/database.ts";
|
|
9
|
+
|
|
10
|
+
export interface JourneyAssociationCounts {
|
|
11
|
+
child_journeys: number;
|
|
12
|
+
journey_paths: number;
|
|
13
|
+
identity_integrations: number;
|
|
14
|
+
conversations: number;
|
|
15
|
+
memories: number;
|
|
16
|
+
tasks: number;
|
|
17
|
+
attachments: number;
|
|
18
|
+
runtime_sessions: number;
|
|
19
|
+
explorer_stories: number;
|
|
20
|
+
refinement_stories: number;
|
|
21
|
+
change_requests: number;
|
|
22
|
+
refinement_cursors: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export class JourneyRemovalError extends Error {
|
|
26
|
+
readonly slug: string;
|
|
27
|
+
readonly associations?: JourneyAssociationCounts;
|
|
28
|
+
|
|
29
|
+
constructor(slug: string, message: string, associations?: JourneyAssociationCounts) {
|
|
30
|
+
super(message);
|
|
31
|
+
this.slug = slug;
|
|
32
|
+
this.associations = associations;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function count(db: WritableDatabase, sql: string, journey: string): number {
|
|
37
|
+
const value = db.prepare(sql).get(journey)?.count;
|
|
38
|
+
if (typeof value === "number") return value;
|
|
39
|
+
if (typeof value === "bigint") return Number(value);
|
|
40
|
+
throw new Error("journey association count did not return a number");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Count the released closed inventory of records that removal would orphan.
|
|
45
|
+
* Every query is a fixed literal. Child parentage comes only from valid JSON
|
|
46
|
+
* metadata (CR050 semantic authority), never migration 017's projection.
|
|
47
|
+
*/
|
|
48
|
+
export function countJourneyAssociations(
|
|
49
|
+
db: WritableDatabase,
|
|
50
|
+
journey: string,
|
|
51
|
+
): JourneyAssociationCounts {
|
|
52
|
+
return {
|
|
53
|
+
child_journeys: count(
|
|
54
|
+
db,
|
|
55
|
+
`SELECT COUNT(*) AS count FROM identity
|
|
56
|
+
WHERE layer = 'journey'
|
|
57
|
+
AND CASE WHEN json_valid(metadata)
|
|
58
|
+
THEN json_extract(metadata, '$.parent_journey') END = ?`,
|
|
59
|
+
journey,
|
|
60
|
+
),
|
|
61
|
+
journey_paths: count(
|
|
62
|
+
db,
|
|
63
|
+
"SELECT COUNT(*) AS count FROM identity WHERE layer = 'journey_path' AND key = ?",
|
|
64
|
+
journey,
|
|
65
|
+
),
|
|
66
|
+
identity_integrations: count(
|
|
67
|
+
db,
|
|
68
|
+
"SELECT COUNT(*) AS count FROM identity_integrations WHERE layer = 'journey' AND key = ?",
|
|
69
|
+
journey,
|
|
70
|
+
),
|
|
71
|
+
conversations: count(
|
|
72
|
+
db,
|
|
73
|
+
"SELECT COUNT(*) AS count FROM conversations WHERE journey = ?",
|
|
74
|
+
journey,
|
|
75
|
+
),
|
|
76
|
+
memories: count(db, "SELECT COUNT(*) AS count FROM memories WHERE journey = ?", journey),
|
|
77
|
+
tasks: count(db, "SELECT COUNT(*) AS count FROM tasks WHERE journey = ?", journey),
|
|
78
|
+
attachments: count(
|
|
79
|
+
db,
|
|
80
|
+
"SELECT COUNT(*) AS count FROM attachments WHERE journey_id = ?",
|
|
81
|
+
journey,
|
|
82
|
+
),
|
|
83
|
+
runtime_sessions: count(
|
|
84
|
+
db,
|
|
85
|
+
"SELECT COUNT(*) AS count FROM runtime_sessions WHERE journey = ?",
|
|
86
|
+
journey,
|
|
87
|
+
),
|
|
88
|
+
explorer_stories: count(
|
|
89
|
+
db,
|
|
90
|
+
"SELECT COUNT(*) AS count FROM exploratory_stories WHERE journey = ?",
|
|
91
|
+
journey,
|
|
92
|
+
),
|
|
93
|
+
refinement_stories: count(
|
|
94
|
+
db,
|
|
95
|
+
"SELECT COUNT(*) AS count FROM builder_refinement_stories WHERE journey = ?",
|
|
96
|
+
journey,
|
|
97
|
+
),
|
|
98
|
+
change_requests: count(
|
|
99
|
+
db,
|
|
100
|
+
"SELECT COUNT(*) AS count FROM builder_change_requests WHERE journey = ?",
|
|
101
|
+
journey,
|
|
102
|
+
),
|
|
103
|
+
refinement_cursors: count(
|
|
104
|
+
db,
|
|
105
|
+
"SELECT COUNT(*) AS count FROM builder_refinement_cursors WHERE journey = ?",
|
|
106
|
+
journey,
|
|
107
|
+
),
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const ASSOCIATED_RECORD_KEYS = [
|
|
112
|
+
"journey_paths",
|
|
113
|
+
"identity_integrations",
|
|
114
|
+
"conversations",
|
|
115
|
+
"memories",
|
|
116
|
+
"tasks",
|
|
117
|
+
"attachments",
|
|
118
|
+
"runtime_sessions",
|
|
119
|
+
"explorer_stories",
|
|
120
|
+
"refinement_stories",
|
|
121
|
+
"change_requests",
|
|
122
|
+
"refinement_cursors",
|
|
123
|
+
] as const satisfies readonly (keyof JourneyAssociationCounts)[];
|
|
124
|
+
|
|
125
|
+
/** Remove exactly one empty leaf journey, or throw without mutating state. */
|
|
126
|
+
export function removeJourney(db: WritableDatabase, journey: string): true {
|
|
127
|
+
return withTransaction(db, () => {
|
|
128
|
+
const exists = db
|
|
129
|
+
.prepare("SELECT 1 AS present FROM identity WHERE layer = 'journey' AND key = ?")
|
|
130
|
+
.get(journey);
|
|
131
|
+
if (exists === undefined) {
|
|
132
|
+
throw new JourneyRemovalError(journey, `Journey '${journey}' not found`);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const associations = countJourneyAssociations(db, journey);
|
|
136
|
+
if (associations.child_journeys > 0) {
|
|
137
|
+
throw new JourneyRemovalError(
|
|
138
|
+
journey,
|
|
139
|
+
`Journey '${journey}' has child journeys; move or remove them first`,
|
|
140
|
+
associations,
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const details = ASSOCIATED_RECORD_KEYS.filter((key) => associations[key] > 0).map(
|
|
145
|
+
(key) => `${key}=${associations[key]}`,
|
|
146
|
+
);
|
|
147
|
+
if (details.length > 0) {
|
|
148
|
+
throw new JourneyRemovalError(
|
|
149
|
+
journey,
|
|
150
|
+
`Journey '${journey}' has associated records: ${details.join(", ")}`,
|
|
151
|
+
associations,
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
db.prepare("DELETE FROM identity WHERE layer = 'journey' AND key = ?").run(journey);
|
|
156
|
+
return true;
|
|
157
|
+
});
|
|
158
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
// `journey status` read primitives — the port of JourneyService.get_journey_status,
|
|
2
|
+
// get_journey_path, get_sync_file, and the store reads it composes
|
|
3
|
+
// (get_memories_by_journey, get_recent_conversations_by_journey).
|
|
4
|
+
|
|
5
|
+
import { readFileSync } from "node:fs";
|
|
6
|
+
import type { Database } from "#db/database.ts";
|
|
7
|
+
import { optionalString, requireString } from "#db/rowDecode.ts";
|
|
8
|
+
import { getIdentityContent, getIdentityMetadata } from "#identity/identityRead.ts";
|
|
9
|
+
import { expandHome } from "#util/paths.ts";
|
|
10
|
+
|
|
11
|
+
const JOURNEY_LAYER = "journey";
|
|
12
|
+
/** The `journey_path` identity layer, also the write target of `journey update`. */
|
|
13
|
+
export const JOURNEY_PATH_LAYER = "journey_path";
|
|
14
|
+
|
|
15
|
+
export interface JourneyStatusMemoryRow {
|
|
16
|
+
created_at: string;
|
|
17
|
+
title: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface JourneyStatusConversationRow {
|
|
21
|
+
started_at: string;
|
|
22
|
+
title: string | null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface JourneyStatusEntry {
|
|
26
|
+
journeyId: string;
|
|
27
|
+
identity: string | null;
|
|
28
|
+
journeyPath: string | null;
|
|
29
|
+
recentMemories: JourneyStatusMemoryRow[];
|
|
30
|
+
recentConversations: JourneyStatusConversationRow[];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Reads a file as utf8 text; injectable so tests never touch the real filesystem. */
|
|
34
|
+
export type ReadTextFile = (path: string) => string;
|
|
35
|
+
|
|
36
|
+
const defaultReadTextFile: ReadTextFile = (path) => readFileSync(path, "utf8");
|
|
37
|
+
|
|
38
|
+
/** Port of `get_sync_file`: the journey's own metadata `sync_file`, or null. */
|
|
39
|
+
export function getSyncFile(db: Database, journeyKey: string): string | null {
|
|
40
|
+
const metadata = getIdentityMetadata(db, JOURNEY_LAYER, journeyKey);
|
|
41
|
+
if (!metadata) return null;
|
|
42
|
+
try {
|
|
43
|
+
const parsed = JSON.parse(metadata) as unknown;
|
|
44
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return null;
|
|
45
|
+
const value = (parsed as Record<string, unknown>).sync_file;
|
|
46
|
+
return typeof value === "string" ? value : null;
|
|
47
|
+
} catch {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Port of `get_project_path`: the journey's metadata `project_path`, expanded.
|
|
54
|
+
*
|
|
55
|
+
* Python expands `~` defensively on read, because values written before
|
|
56
|
+
* write-side normalization can still be stored in tilde form. A no-op for
|
|
57
|
+
* canonical paths, and load-bearing for the ones it is not.
|
|
58
|
+
*/
|
|
59
|
+
export function getProjectPath(db: Database, journeyKey: string): string | null {
|
|
60
|
+
const metadata = getIdentityMetadata(db, JOURNEY_LAYER, journeyKey);
|
|
61
|
+
if (!metadata) return null;
|
|
62
|
+
try {
|
|
63
|
+
const parsed = JSON.parse(metadata) as unknown;
|
|
64
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return null;
|
|
65
|
+
const value = (parsed as Record<string, unknown>).project_path;
|
|
66
|
+
return typeof value === "string" ? expandHome(value) : null;
|
|
67
|
+
} catch {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Port of `get_journey_path`: prefer the external sync file (expanduser'd) when
|
|
74
|
+
* configured and readable; on ANY read failure (missing, permission, or other
|
|
75
|
+
* I/O error — matching Python's broad `OSError` catch), fall back to the
|
|
76
|
+
* database's `journey_path` identity content.
|
|
77
|
+
*/
|
|
78
|
+
export function getJourneyPathContent(
|
|
79
|
+
db: Database,
|
|
80
|
+
journeyKey: string,
|
|
81
|
+
readTextFile: ReadTextFile = defaultReadTextFile,
|
|
82
|
+
): string | null {
|
|
83
|
+
const syncFile = getSyncFile(db, journeyKey);
|
|
84
|
+
if (syncFile) {
|
|
85
|
+
try {
|
|
86
|
+
return readTextFile(expandHome(syncFile));
|
|
87
|
+
} catch {
|
|
88
|
+
// Fall through to the database.
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return getIdentityContent(db, JOURNEY_PATH_LAYER, journeyKey);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Port of `store.get_memories_by_journey(journey)[:10]`, LIMIT pushed down to SQL. */
|
|
95
|
+
export function getMemoriesByJourney(
|
|
96
|
+
db: Database,
|
|
97
|
+
journeyKey: string,
|
|
98
|
+
limit = 10,
|
|
99
|
+
): JourneyStatusMemoryRow[] {
|
|
100
|
+
return db
|
|
101
|
+
.prepare(
|
|
102
|
+
"SELECT created_at, title FROM memories WHERE journey = ? ORDER BY created_at DESC LIMIT ?",
|
|
103
|
+
)
|
|
104
|
+
.all(journeyKey, limit)
|
|
105
|
+
.map((row) => ({
|
|
106
|
+
created_at: requireString(row, "created_at"),
|
|
107
|
+
title: requireString(row, "title"),
|
|
108
|
+
}));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** Port of `store.get_recent_conversations_by_journey(journey, limit=5)`. */
|
|
112
|
+
export function getRecentConversationsByJourney(
|
|
113
|
+
db: Database,
|
|
114
|
+
journeyKey: string,
|
|
115
|
+
limit = 5,
|
|
116
|
+
): JourneyStatusConversationRow[] {
|
|
117
|
+
return db
|
|
118
|
+
.prepare(
|
|
119
|
+
"SELECT started_at, title FROM conversations WHERE journey = ? ORDER BY started_at DESC LIMIT ?",
|
|
120
|
+
)
|
|
121
|
+
.all(journeyKey, limit)
|
|
122
|
+
.map((row) => ({
|
|
123
|
+
started_at: requireString(row, "started_at"),
|
|
124
|
+
title: optionalString(row, "title"),
|
|
125
|
+
}));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** Port of `_get_journey_identities()` keys only: every journey key, ordered. */
|
|
129
|
+
export function allJourneyKeys(db: Database): string[] {
|
|
130
|
+
return db
|
|
131
|
+
.prepare("SELECT key FROM identity WHERE layer = 'journey' ORDER BY key")
|
|
132
|
+
.all()
|
|
133
|
+
.map((row) => requireString(row, "key"));
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** Port of `get_journey_status` for an explicit, pre-resolved list of journey keys. */
|
|
137
|
+
export function getJourneyStatusEntries(
|
|
138
|
+
db: Database,
|
|
139
|
+
journeyKeys: readonly string[],
|
|
140
|
+
readTextFile: ReadTextFile = defaultReadTextFile,
|
|
141
|
+
): JourneyStatusEntry[] {
|
|
142
|
+
return journeyKeys.map((journeyId) => ({
|
|
143
|
+
journeyId,
|
|
144
|
+
identity: getIdentityContent(db, JOURNEY_LAYER, journeyId),
|
|
145
|
+
journeyPath: getJourneyPathContent(db, journeyId, readTextFile),
|
|
146
|
+
recentMemories: getMemoriesByJourney(db, journeyId, 10),
|
|
147
|
+
recentConversations: getRecentConversationsByJourney(db, journeyId, 5),
|
|
148
|
+
}));
|
|
149
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// Journey sync-file metadata parity port (CV22.DS7.US2 slice 3c).
|
|
2
|
+
//
|
|
3
|
+
// A faithful port of `JourneyService.get_sync_file` / `set_sync_file` /
|
|
4
|
+
// `get_journey_path` (`src/memory/services/journey.py`). `sync_file` is a
|
|
5
|
+
// field inside the "journey" layer identity row's `metadata` JSON — the same
|
|
6
|
+
// metadata object `journeyWrite.ts`'s `journeyMetadata` composes for
|
|
7
|
+
// `project_path`/`icon`/`color`/`parent_journey` — read and written here with
|
|
8
|
+
// its own narrow read-modify-write (Python's `set_sync_file` reads the whole
|
|
9
|
+
// metadata object, sets one key, and writes it back; it does NOT go through
|
|
10
|
+
// the ordered `journeyMetadata` composer).
|
|
11
|
+
|
|
12
|
+
import { readFileSync } from "node:fs";
|
|
13
|
+
import type { Database, WritableDatabase } from "#db/database.ts";
|
|
14
|
+
import { getIdentityContent, getIdentityMetadata } from "#identity/identityRead.ts";
|
|
15
|
+
import { updateIdentityMetadata } from "#identity/identityStore.ts";
|
|
16
|
+
import { expandHome } from "#util/paths.ts";
|
|
17
|
+
import { JOURNEY_PATH_LAYER } from "./journeyStatus.ts";
|
|
18
|
+
import { JourneyNotFoundError } from "./journeyWrite.ts";
|
|
19
|
+
|
|
20
|
+
export const JOURNEY_LAYER = "journey";
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Port of `get_sync_file`: the configured sync file path for a journey, or
|
|
24
|
+
* `null` when the journey has no identity row, no metadata, no `sync_file`
|
|
25
|
+
* key, or the metadata fails to parse (matching Python's
|
|
26
|
+
* `except (JSONDecodeError, TypeError): return None`).
|
|
27
|
+
*/
|
|
28
|
+
export function getSyncFile(db: Database, journey: string): string | null {
|
|
29
|
+
const metadataJson = getIdentityMetadata(db, JOURNEY_LAYER, journey);
|
|
30
|
+
if (!metadataJson) return null;
|
|
31
|
+
try {
|
|
32
|
+
const meta = JSON.parse(metadataJson) as Record<string, unknown>;
|
|
33
|
+
return typeof meta.sync_file === "string" ? meta.sync_file : null;
|
|
34
|
+
} catch {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Port of `set_sync_file`: read-modify-write the journey's metadata, setting
|
|
41
|
+
* `sync_file`, preserving every other existing key. Throws `JourneyNotFoundError`
|
|
42
|
+
* when the journey has no "journey"-layer identity row -- Python's own
|
|
43
|
+
* `cmd_sync_config` does not catch this ValueError either (an uncaught
|
|
44
|
+
* exception, exit 1); the front-door route maps it to a clear stderr message
|
|
45
|
+
* instead of fabricating a fake traceback, the same DS7.US1 `init` precedent.
|
|
46
|
+
*/
|
|
47
|
+
export function setSyncFile(
|
|
48
|
+
db: WritableDatabase,
|
|
49
|
+
journey: string,
|
|
50
|
+
filePath: string,
|
|
51
|
+
nowIso: string,
|
|
52
|
+
): void {
|
|
53
|
+
const existingMetadata = getIdentityMetadata(db, JOURNEY_LAYER, journey);
|
|
54
|
+
const exists =
|
|
55
|
+
db
|
|
56
|
+
.prepare("SELECT 1 FROM identity WHERE layer = ? AND key = ? LIMIT 1")
|
|
57
|
+
.get(JOURNEY_LAYER, journey) !== undefined;
|
|
58
|
+
if (!exists) {
|
|
59
|
+
throw new JourneyNotFoundError(journey);
|
|
60
|
+
}
|
|
61
|
+
let meta: Record<string, unknown>;
|
|
62
|
+
try {
|
|
63
|
+
meta = existingMetadata ? (JSON.parse(existingMetadata) as Record<string, unknown>) : {};
|
|
64
|
+
} catch {
|
|
65
|
+
meta = {};
|
|
66
|
+
}
|
|
67
|
+
meta.sync_file = filePath;
|
|
68
|
+
updateIdentityMetadata(db, JOURNEY_LAYER, journey, JSON.stringify(meta), nowIso);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Port of `get_journey_path`: if a sync file is configured, read its EXTERNAL
|
|
73
|
+
* file content, falling back to the database's `journey_path`-layer content
|
|
74
|
+
* when the file is absent/unreadable (matching Python's
|
|
75
|
+
* `except (FileNotFoundError, PermissionError, OSError): pass`).
|
|
76
|
+
*/
|
|
77
|
+
export function getJourneyPath(db: Database, journey: string): string | null {
|
|
78
|
+
const syncFile = getSyncFile(db, journey);
|
|
79
|
+
if (syncFile) {
|
|
80
|
+
try {
|
|
81
|
+
return readFileSync(expandHome(syncFile), "utf8");
|
|
82
|
+
} catch {
|
|
83
|
+
// Fall back to the database.
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return getIdentityContent(db, JOURNEY_PATH_LAYER, journey);
|
|
87
|
+
}
|