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,212 @@
|
|
|
1
|
+
// CV22.DS7.US6 plateau 2 — Soul Mode provisional session state, ported from
|
|
2
|
+
// `src/memory/services/soul.py`.
|
|
3
|
+
//
|
|
4
|
+
// The ritual's fruit lives in `runtime_sessions.metadata`, a JSON TEXT column
|
|
5
|
+
// both cores read and write while the strangler runs, so the contract graded by
|
|
6
|
+
// `soul-state.golden.json` is the column's exact BYTES:
|
|
7
|
+
//
|
|
8
|
+
// * Python serializes with `json.dumps(..., ensure_ascii=False)`, whose
|
|
9
|
+
// separators are `", "` and `": "`. `JSON.stringify` writes neither, so
|
|
10
|
+
// every write here goes through `pythonJsonDumps`.
|
|
11
|
+
// * Clearing the last soul key writes SQL NULL, not `"{}"`.
|
|
12
|
+
// * Clearing against a MISSING session row writes nothing at all -- it must
|
|
13
|
+
// not create the row on the way past.
|
|
14
|
+
// * `harvestFruit` with no argument promotes the maturation fruit and deletes
|
|
15
|
+
// it in the same write; both keys are never present afterwards.
|
|
16
|
+
//
|
|
17
|
+
// Known limit, inherited and not introduced here: JavaScript objects reorder
|
|
18
|
+
// integer-like keys ahead of string keys, while Python dicts preserve insertion
|
|
19
|
+
// order for all of them. Mirror's own metadata keys (`soul`, `operating_mode`)
|
|
20
|
+
// are unaffected; a column carrying a numeric top-level key -- only reachable
|
|
21
|
+
// through corruption or a foreign writer -- would round-trip in a different
|
|
22
|
+
// order than Python's. Reproducing that would require an order-preserving JSON
|
|
23
|
+
// model across the whole port, which is not this story's scope.
|
|
24
|
+
|
|
25
|
+
import type { Database, WritableDatabase } from "#db/database.ts";
|
|
26
|
+
import {
|
|
27
|
+
decodeMetadata,
|
|
28
|
+
getRuntimeSession,
|
|
29
|
+
resolveRuntimeSessionId,
|
|
30
|
+
upsertRuntimeSession,
|
|
31
|
+
} from "#mirror/runtimeSession.ts";
|
|
32
|
+
import { pythonJsonDumps } from "#util/pyGenerators.ts";
|
|
33
|
+
import { pyStrip } from "#util/pythonText.ts";
|
|
34
|
+
|
|
35
|
+
export const SOUL_STATE_SESSION_ID = "__global_soul_mode__";
|
|
36
|
+
export const SOUL_METADATA_KEY = "soul";
|
|
37
|
+
export const FRUIT_METADATA_KEY = "fruit_in_maturation";
|
|
38
|
+
export const HARVEST_METADATA_KEY = "harvested_fruit";
|
|
39
|
+
|
|
40
|
+
export interface SoulFruitState {
|
|
41
|
+
sessionId: string;
|
|
42
|
+
fruit: string | null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Port of `resolve_soul_session_id`: explicit (stripped) → `MIRROR_SESSION_ID`
|
|
47
|
+
* (stripped) → the global Soul constant. Always returns an id.
|
|
48
|
+
*/
|
|
49
|
+
export function resolveSoulSessionId(
|
|
50
|
+
explicitSessionId: string | null = null,
|
|
51
|
+
environmentSessionId: string | null = null,
|
|
52
|
+
): string {
|
|
53
|
+
const explicit = pyStrip(explicitSessionId ?? "");
|
|
54
|
+
if (explicit) return explicit;
|
|
55
|
+
return pyStrip(environmentSessionId ?? "") || SOUL_STATE_SESSION_ID;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Port of `_resolve_cli_soul_session_id`: the operating-mode resolver first,
|
|
60
|
+
* the Soul resolver as the fallback.
|
|
61
|
+
*
|
|
62
|
+
* The two disagree on purpose and the golden pins it. The operating-mode
|
|
63
|
+
* resolver returns an explicit id VERBATIM, so `--session-id " "` resolves to
|
|
64
|
+
* `" "`, while the Soul resolver would have stripped it to the global
|
|
65
|
+
* constant. Composing them the other way round would silently retarget a
|
|
66
|
+
* session.
|
|
67
|
+
*/
|
|
68
|
+
export function resolveCliSoulSessionId(
|
|
69
|
+
db: Database,
|
|
70
|
+
explicitSessionId: string | null = null,
|
|
71
|
+
environmentSessionId: string | null = null,
|
|
72
|
+
): string {
|
|
73
|
+
return (
|
|
74
|
+
resolveRuntimeSessionId(db, explicitSessionId, environmentSessionId) ??
|
|
75
|
+
resolveSoulSessionId(explicitSessionId, environmentSessionId)
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function getFruitInMaturation(
|
|
80
|
+
db: Database,
|
|
81
|
+
sessionId: string | null = null,
|
|
82
|
+
): SoulFruitState {
|
|
83
|
+
return readSoulKey(db, FRUIT_METADATA_KEY, sessionId);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function getHarvestedFruit(db: Database, sessionId: string | null = null): SoulFruitState {
|
|
87
|
+
return readSoulKey(db, HARVEST_METADATA_KEY, sessionId);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function readSoulKey(db: Database, key: string, sessionId: string | null): SoulFruitState {
|
|
91
|
+
const resolvedSessionId = resolveSoulSessionId(sessionId);
|
|
92
|
+
const session = getRuntimeSession(db, resolvedSessionId);
|
|
93
|
+
const soul = decodeMetadata(session?.metadata ?? null)[SOUL_METADATA_KEY];
|
|
94
|
+
const value = isRecord(soul) ? soul[key] : undefined;
|
|
95
|
+
const fruit = typeof value === "string" && pyStrip(value) ? pyStrip(value) : null;
|
|
96
|
+
return { sessionId: resolvedSessionId, fruit };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function setFruitInMaturation(
|
|
100
|
+
db: WritableDatabase,
|
|
101
|
+
fruit: string,
|
|
102
|
+
sessionId: string | null,
|
|
103
|
+
nowIso: string,
|
|
104
|
+
): SoulFruitState {
|
|
105
|
+
const normalizedFruit = pyStrip(fruit);
|
|
106
|
+
if (!normalizedFruit) throw new SoulStateError("fruit must not be empty");
|
|
107
|
+
|
|
108
|
+
const resolvedSessionId = resolveSoulSessionId(sessionId);
|
|
109
|
+
const metadata = readMetadata(db, resolvedSessionId);
|
|
110
|
+
const soul = soulObject(metadata);
|
|
111
|
+
soul[FRUIT_METADATA_KEY] = normalizedFruit;
|
|
112
|
+
metadata[SOUL_METADATA_KEY] = soul;
|
|
113
|
+
upsertRuntimeSession(
|
|
114
|
+
db,
|
|
115
|
+
resolvedSessionId,
|
|
116
|
+
{ metadata: pythonJsonDumps(metadata), active: true },
|
|
117
|
+
nowIso,
|
|
118
|
+
);
|
|
119
|
+
return { sessionId: resolvedSessionId, fruit: normalizedFruit };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function harvestFruit(
|
|
123
|
+
db: WritableDatabase,
|
|
124
|
+
input: { fruit?: string | null; sessionId?: string | null },
|
|
125
|
+
nowIso: string,
|
|
126
|
+
): SoulFruitState {
|
|
127
|
+
const resolvedSessionId = resolveSoulSessionId(input.sessionId ?? null);
|
|
128
|
+
let finalFruit =
|
|
129
|
+
typeof input.fruit === "string" && pyStrip(input.fruit) ? pyStrip(input.fruit) : null;
|
|
130
|
+
if (finalFruit === null) {
|
|
131
|
+
finalFruit = getFruitInMaturation(db, resolvedSessionId).fruit;
|
|
132
|
+
}
|
|
133
|
+
if (!finalFruit) throw new SoulStateError("harvested fruit must not be empty");
|
|
134
|
+
|
|
135
|
+
const metadata = readMetadata(db, resolvedSessionId);
|
|
136
|
+
const soul = soulObject(metadata);
|
|
137
|
+
soul[HARVEST_METADATA_KEY] = finalFruit;
|
|
138
|
+
delete soul[FRUIT_METADATA_KEY];
|
|
139
|
+
metadata[SOUL_METADATA_KEY] = soul;
|
|
140
|
+
upsertRuntimeSession(
|
|
141
|
+
db,
|
|
142
|
+
resolvedSessionId,
|
|
143
|
+
{ metadata: pythonJsonDumps(metadata), active: true },
|
|
144
|
+
nowIso,
|
|
145
|
+
);
|
|
146
|
+
return { sessionId: resolvedSessionId, fruit: finalFruit };
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export function clearFruitInMaturation(
|
|
150
|
+
db: WritableDatabase,
|
|
151
|
+
sessionId: string | null,
|
|
152
|
+
nowIso: string,
|
|
153
|
+
): void {
|
|
154
|
+
clearSoulKey(db, FRUIT_METADATA_KEY, sessionId, nowIso);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export function clearHarvestedFruit(
|
|
158
|
+
db: WritableDatabase,
|
|
159
|
+
sessionId: string | null,
|
|
160
|
+
nowIso: string,
|
|
161
|
+
): void {
|
|
162
|
+
clearSoulKey(db, HARVEST_METADATA_KEY, sessionId, nowIso);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/** Port of `_clear_soul_key`, including its two silences. */
|
|
166
|
+
function clearSoulKey(
|
|
167
|
+
db: WritableDatabase,
|
|
168
|
+
key: string,
|
|
169
|
+
sessionId: string | null,
|
|
170
|
+
nowIso: string,
|
|
171
|
+
): void {
|
|
172
|
+
const resolvedSessionId = resolveSoulSessionId(sessionId);
|
|
173
|
+
const session = getRuntimeSession(db, resolvedSessionId);
|
|
174
|
+
// No row: Python returns before touching the database, so clearing a fruit
|
|
175
|
+
// that was never set must not conjure a session row.
|
|
176
|
+
if (!session) return;
|
|
177
|
+
|
|
178
|
+
const metadata = decodeMetadata(session.metadata);
|
|
179
|
+
const soul = metadata[SOUL_METADATA_KEY];
|
|
180
|
+
if (isRecord(soul)) {
|
|
181
|
+
delete soul[key];
|
|
182
|
+
if (Object.keys(soul).length > 0) {
|
|
183
|
+
metadata[SOUL_METADATA_KEY] = soul;
|
|
184
|
+
} else {
|
|
185
|
+
delete metadata[SOUL_METADATA_KEY];
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
upsertRuntimeSession(
|
|
189
|
+
db,
|
|
190
|
+
resolvedSessionId,
|
|
191
|
+
{ metadata: Object.keys(metadata).length > 0 ? pythonJsonDumps(metadata) : null },
|
|
192
|
+
nowIso,
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function readMetadata(db: Database, sessionId: string): Record<string, unknown> {
|
|
197
|
+
const session = getRuntimeSession(db, sessionId);
|
|
198
|
+
return decodeMetadata(session?.metadata ?? null);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/** Python replaces a non-dict `soul` value with a fresh dict rather than raising. */
|
|
202
|
+
function soulObject(metadata: Record<string, unknown>): Record<string, unknown> {
|
|
203
|
+
const existing = metadata[SOUL_METADATA_KEY];
|
|
204
|
+
return isRecord(existing) ? existing : {};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
208
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/** Python raises `ValueError`; the CLI renders it as `Error: <message>`. */
|
|
212
|
+
export class SoulStateError extends Error {}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// CV22.DS7.US6 plateau 6 — the Soul Mode entry card, ported from
|
|
2
|
+
// `render_soul_mode_transition` (`src/memory/surfaces/mode_transition.py`).
|
|
3
|
+
//
|
|
4
|
+
// A different module and a different WIDTH (56, against the 40 of the ritual
|
|
5
|
+
// cards in `render.ts`), but the same transport=verbatim contract.
|
|
6
|
+
//
|
|
7
|
+
// The card is CONSTANT: the oracle accepts `journey` and immediately discards
|
|
8
|
+
// it — "Soul Mode entry keeps journey context in state, not in the ritual card"
|
|
9
|
+
// — so the journey reaches the operating-mode row and the sticky defaults, and
|
|
10
|
+
// never the rendered surface. The golden records both call shapes so that
|
|
11
|
+
// property is pinned rather than assumed.
|
|
12
|
+
|
|
13
|
+
const WIDTH = 56;
|
|
14
|
+
|
|
15
|
+
function line(text: string): string {
|
|
16
|
+
return `│${text.padEnd(WIDTH)}│`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const CARD = [
|
|
20
|
+
"Mirror",
|
|
21
|
+
`╭${"─".repeat(WIDTH)}╮`,
|
|
22
|
+
line(" ☾ SOUL MODE ACTIVE"),
|
|
23
|
+
line(""),
|
|
24
|
+
line(" Soul Mode turns the day toward the inner life."),
|
|
25
|
+
line(""),
|
|
26
|
+
line(" ✦ IN ORDER TO"),
|
|
27
|
+
line(" remember who you are"),
|
|
28
|
+
line(""),
|
|
29
|
+
line(" ▹ START BY ANSWERING"),
|
|
30
|
+
line(" how is your day going today?"),
|
|
31
|
+
`╰${"─".repeat(WIDTH)}╯`,
|
|
32
|
+
].join("\n");
|
|
33
|
+
|
|
34
|
+
/** Port of `render_soul_mode_transition`. The journey never reaches the card. */
|
|
35
|
+
export function renderSoulModeTransition(_journey?: string | null): string {
|
|
36
|
+
return CARD;
|
|
37
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// Journey-path task parser parity port (CV22.DS7.US2).
|
|
2
|
+
//
|
|
3
|
+
// A faithful TypeScript port of Python `parse_journey_path_tasks` /
|
|
4
|
+
// `parse_done_tasks` (`src/memory/cli/tasks.py`). Both functions are pure over
|
|
5
|
+
// a markdown string — no database involved — and are used by both `tasks
|
|
6
|
+
// import` and `tasks sync`, so this is the **one shared** parser both callers
|
|
7
|
+
// use (the `kebab_slug` writer/locator lesson: do not let two callers grow two
|
|
8
|
+
// slightly-different copies of the same regex logic).
|
|
9
|
+
//
|
|
10
|
+
// Kept as one module with two exported functions, mirroring the Python file,
|
|
11
|
+
// because the two parsers share the same stage-tracking state machine and are
|
|
12
|
+
// always read together.
|
|
13
|
+
|
|
14
|
+
/** One parsed task payload, matching the Python dict shape exactly. */
|
|
15
|
+
export interface ParsedTask {
|
|
16
|
+
title: string;
|
|
17
|
+
stage: string | null;
|
|
18
|
+
status: "todo" | "done";
|
|
19
|
+
journey: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const STAGE_HEADER_RE = /^###\s+(?:Etapa\s+\d+:\s*)?(.+?)(?:\s*[✅🚧⏸])?$/u;
|
|
23
|
+
const CYCLE_HEADER_RE = /^\*\*(.+?)(?:\s*[✅🚧⏸])?\s*:?\*\*/u;
|
|
24
|
+
const UNCHECKED_BOX_RE = /^\s*-\s*\[\s*\]\s+(.+)/;
|
|
25
|
+
const CHECKED_BOX_RE = /^\s*-\s*\[x\]\s+(.+)/i;
|
|
26
|
+
const BOLD_RE = /\*\*(.+?)\*\*/g;
|
|
27
|
+
|
|
28
|
+
/** Strip Markdown bold markers and a single trailing period from a title. */
|
|
29
|
+
function cleanTitle(raw: string): string {
|
|
30
|
+
const unbolded = raw.trim().replace(BOLD_RE, "$1");
|
|
31
|
+
return unbolded.replace(/\.+$/, "");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Extract pending tasks from unchecked journey-path checkboxes.
|
|
36
|
+
*
|
|
37
|
+
* Reproduces Python `parse_journey_path_tasks`, including the legacy
|
|
38
|
+
* bold-cycle-header branch: a `**...✅**` line resets the current stage to
|
|
39
|
+
* `null` (skipping tasks under it) while a `**...**` line without `✅` is a
|
|
40
|
+
* no-op when a stage is already active. A `###` header carrying `✅` also
|
|
41
|
+
* resets the stage to `null` so a completed stage's checkboxes are skipped.
|
|
42
|
+
*/
|
|
43
|
+
export function parseJourneyPathTasks(journeyPath: string, journey: string): ParsedTask[] {
|
|
44
|
+
const tasks: ParsedTask[] = [];
|
|
45
|
+
let currentStage: string | null = null;
|
|
46
|
+
|
|
47
|
+
for (const rawLine of journeyPath.split("\n")) {
|
|
48
|
+
const line = rawLine.trim();
|
|
49
|
+
|
|
50
|
+
const stageMatch = STAGE_HEADER_RE.exec(line);
|
|
51
|
+
if (stageMatch) {
|
|
52
|
+
if (rawLine.includes("✅")) {
|
|
53
|
+
currentStage = null;
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
currentStage = stageMatch[1].trim();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const cycleMatch = CYCLE_HEADER_RE.exec(line);
|
|
60
|
+
if (cycleMatch && rawLine.includes("✅")) {
|
|
61
|
+
currentStage = null;
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
// else if cycleMatch && currentStage === null: no-op in Python (`pass`) —
|
|
65
|
+
// nothing to do here either.
|
|
66
|
+
|
|
67
|
+
const checkboxMatch = UNCHECKED_BOX_RE.exec(rawLine);
|
|
68
|
+
if (checkboxMatch && currentStage !== null) {
|
|
69
|
+
tasks.push({
|
|
70
|
+
title: cleanTitle(checkboxMatch[1]),
|
|
71
|
+
stage: currentStage,
|
|
72
|
+
status: "todo",
|
|
73
|
+
journey,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return tasks;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Extract completed tasks from checked journey-path checkboxes.
|
|
83
|
+
*
|
|
84
|
+
* Reproduces Python `parse_done_tasks`: unlike the pending parser, a stage
|
|
85
|
+
* header never resets `currentStage` to `null` on `✅` (there is no
|
|
86
|
+
* "completed stage" skip for already-done tasks), and every checked box is
|
|
87
|
+
* appended regardless of whether a stage has been seen yet.
|
|
88
|
+
*/
|
|
89
|
+
export function parseDoneTasks(journeyPath: string, journey: string): ParsedTask[] {
|
|
90
|
+
const tasks: ParsedTask[] = [];
|
|
91
|
+
let currentStage: string | null = null;
|
|
92
|
+
|
|
93
|
+
for (const rawLine of journeyPath.split("\n")) {
|
|
94
|
+
const line = rawLine.trim();
|
|
95
|
+
|
|
96
|
+
const stageMatch = STAGE_HEADER_RE.exec(line);
|
|
97
|
+
if (stageMatch) {
|
|
98
|
+
currentStage = stageMatch[1].trim();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const checkboxMatch = CHECKED_BOX_RE.exec(rawLine);
|
|
102
|
+
if (checkboxMatch) {
|
|
103
|
+
tasks.push({
|
|
104
|
+
title: cleanTitle(checkboxMatch[1]),
|
|
105
|
+
stage: currentStage,
|
|
106
|
+
status: "done",
|
|
107
|
+
journey,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return tasks;
|
|
113
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
// `tasks import` / `tasks sync` orchestration parity port (CV22.DS7.US2 slice
|
|
2
|
+
// 3c). A faithful port of `TaskService.import_tasks_from_journey_path` /
|
|
3
|
+
// `sync_tasks_from_file` (`src/memory/services/tasks.py`), which read a
|
|
4
|
+
// journey's path/reference file, parse it with the shared
|
|
5
|
+
// `journeyPathParse.ts` parser, and create/complete tasks accordingly.
|
|
6
|
+
//
|
|
7
|
+
// Both functions generate a fresh id/now PER created row (not one injected
|
|
8
|
+
// value for the whole call) -- matching `runSeed`'s established pattern for
|
|
9
|
+
// multi-row writes, since either function can create zero, one, or many
|
|
10
|
+
// tasks in a single invocation.
|
|
11
|
+
|
|
12
|
+
import { readFileSync } from "node:fs";
|
|
13
|
+
import type { WritableDatabase } from "#db/database.ts";
|
|
14
|
+
import { getJourneyPath, getSyncFile } from "#journey/journeySyncFile.ts";
|
|
15
|
+
import { expandHome } from "#util/paths.ts";
|
|
16
|
+
import { newId, nowIso } from "#util/pyGenerators.ts";
|
|
17
|
+
import { parseDoneTasks, parseJourneyPathTasks } from "./journeyPathParse.ts";
|
|
18
|
+
import {
|
|
19
|
+
completeTask,
|
|
20
|
+
createTask,
|
|
21
|
+
findTasksByTitle,
|
|
22
|
+
getTasksByJourney,
|
|
23
|
+
type Task,
|
|
24
|
+
} from "./taskStore.ts";
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Port of `import_tasks_from_journey_path`: read the journey's path (external
|
|
28
|
+
* sync file if configured, else the DB `journey_path` layer), parse pending
|
|
29
|
+
* checkboxes, and create a task for each title not already present for this
|
|
30
|
+
* journey. Dedup uses the SAME `LIKE %title%` fragment match `findTasksByTitle`
|
|
31
|
+
* already ports -- a deliberate, pre-existing Python characteristic (a
|
|
32
|
+
* substring match, not an exact one), not something to tighten here.
|
|
33
|
+
*/
|
|
34
|
+
export function importTasksFromJourneyPath(db: WritableDatabase, journey: string): Task[] {
|
|
35
|
+
const journeyPath = getJourneyPath(db, journey);
|
|
36
|
+
if (!journeyPath) return [];
|
|
37
|
+
|
|
38
|
+
const parsed = parseJourneyPathTasks(journeyPath, journey);
|
|
39
|
+
const created: Task[] = [];
|
|
40
|
+
for (const item of parsed) {
|
|
41
|
+
const existing = findTasksByTitle(db, item.title, journey);
|
|
42
|
+
if (existing.length > 0) continue;
|
|
43
|
+
const task = createTask(
|
|
44
|
+
db,
|
|
45
|
+
{ title: item.title, journey, stage: item.stage, source: "journey_path" },
|
|
46
|
+
newId(),
|
|
47
|
+
nowIso(),
|
|
48
|
+
);
|
|
49
|
+
created.push(task);
|
|
50
|
+
}
|
|
51
|
+
return created;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Raised when a journey has no sync file configured (`sync_tasks_from_file`'s `ValueError`). */
|
|
55
|
+
export class NoSyncFileConfiguredError extends Error {
|
|
56
|
+
readonly journey: string;
|
|
57
|
+
constructor(journey: string) {
|
|
58
|
+
super(
|
|
59
|
+
`No sync file configured for '${journey}'. Use: mm:tasks sync-config ${journey} /path/to/file`,
|
|
60
|
+
);
|
|
61
|
+
this.journey = journey;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Raised when the configured sync file does not exist on disk. */
|
|
66
|
+
export class SyncFileNotFoundError extends Error {
|
|
67
|
+
readonly syncFile: string;
|
|
68
|
+
constructor(syncFile: string) {
|
|
69
|
+
super(`File not found: ${syncFile}`);
|
|
70
|
+
this.syncFile = syncFile;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface SyncResult {
|
|
75
|
+
created: number;
|
|
76
|
+
completed: number;
|
|
77
|
+
unchanged: number;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Port of `sync_tasks_from_file`: read the configured external file, parse
|
|
82
|
+
* BOTH pending and done checkboxes, and reconcile against a SNAPSHOT of the
|
|
83
|
+
* journey's existing tasks (by title) taken ONCE before either loop runs --
|
|
84
|
+
* matching Python's single dict comprehension exactly. A task created by the
|
|
85
|
+
* pending loop is deliberately invisible to the done loop in the SAME call
|
|
86
|
+
* (this is Python's real behavior, not a bug to fix): if a title later marked
|
|
87
|
+
* done wasn't already a known task before this sync ran, it is silently
|
|
88
|
+
* skipped, never created from the done list alone.
|
|
89
|
+
*/
|
|
90
|
+
export function syncTasksFromFile(db: WritableDatabase, journey: string): SyncResult {
|
|
91
|
+
const syncFile = getSyncFile(db, journey);
|
|
92
|
+
if (!syncFile) {
|
|
93
|
+
throw new NoSyncFileConfiguredError(journey);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const expandedPath = expandHome(syncFile);
|
|
97
|
+
let content: string;
|
|
98
|
+
try {
|
|
99
|
+
content = readFileSync(expandedPath, "utf8");
|
|
100
|
+
} catch {
|
|
101
|
+
throw new SyncFileNotFoundError(syncFile);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const filePending = parseJourneyPathTasks(content, journey);
|
|
105
|
+
const fileDone = parseDoneTasks(content, journey);
|
|
106
|
+
|
|
107
|
+
// Last-title-wins, in get_tasks_by_journey's own order -- matches Python's
|
|
108
|
+
// `{t.title: t for t in existing_tasks}` dict-comprehension semantics.
|
|
109
|
+
const existingByTitle = new Map<string, Task>();
|
|
110
|
+
for (const t of getTasksByJourney(db, journey)) {
|
|
111
|
+
existingByTitle.set(t.title, t);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const result: SyncResult = { created: 0, completed: 0, unchanged: 0 };
|
|
115
|
+
|
|
116
|
+
for (const item of filePending) {
|
|
117
|
+
if (!existingByTitle.has(item.title)) {
|
|
118
|
+
createTask(
|
|
119
|
+
db,
|
|
120
|
+
{ title: item.title, journey, stage: item.stage, source: "sync" },
|
|
121
|
+
newId(),
|
|
122
|
+
nowIso(),
|
|
123
|
+
);
|
|
124
|
+
result.created += 1;
|
|
125
|
+
} else {
|
|
126
|
+
result.unchanged += 1;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
for (const item of fileDone) {
|
|
131
|
+
const existing = existingByTitle.get(item.title);
|
|
132
|
+
if (!existing) continue; // done items never create a task on their own.
|
|
133
|
+
if (existing.status !== "done") {
|
|
134
|
+
completeTask(db, existing.id, nowIso());
|
|
135
|
+
result.completed += 1;
|
|
136
|
+
} else {
|
|
137
|
+
result.unchanged += 1;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return result;
|
|
142
|
+
}
|