mirror-mind 0.0.1 → 0.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/hooks/log-assistant-reply.sh +85 -0
- package/.claude/hooks/log-session-end.sh +85 -0
- package/.claude/hooks/log-user-prompt.sh +85 -0
- package/.claude/hooks/mirror-inject.sh +85 -0
- package/.claude/hooks/session-start.sh +85 -0
- package/.gemini/hooks/log-assistant.sh +85 -0
- package/.gemini/hooks/log-user.sh +85 -0
- package/.gemini/hooks/session-end.sh +85 -0
- package/.gemini/hooks/session-start.sh +85 -0
- package/.pi/extensions/mirror-logger.ts +646 -0
- package/.pi/skills/mm-backup/SKILL.md +21 -0
- package/.pi/skills/mm-build/SKILL.md +1206 -0
- package/.pi/skills/mm-consolidate/SKILL.md +112 -0
- package/.pi/skills/mm-consult/SKILL.md +70 -0
- package/.pi/skills/mm-conversations/SKILL.md +16 -0
- package/.pi/skills/mm-discard/SKILL.md +39 -0
- package/.pi/skills/mm-explore/SKILL.md +255 -0
- package/.pi/skills/mm-help/SKILL.md +74 -0
- package/.pi/skills/mm-identity/SKILL.md +96 -0
- package/.pi/skills/mm-journal/SKILL.md +16 -0
- package/.pi/skills/mm-journey/SKILL.md +23 -0
- package/.pi/skills/mm-journeys/SKILL.md +15 -0
- package/.pi/skills/mm-memories/SKILL.md +17 -0
- package/.pi/skills/mm-mirror/SKILL.md +98 -0
- package/.pi/skills/mm-mute/SKILL.md +18 -0
- package/.pi/skills/mm-new/SKILL.md +18 -0
- package/.pi/skills/mm-recall/SKILL.md +17 -0
- package/.pi/skills/mm-release-notes/SKILL.md +52 -0
- package/.pi/skills/mm-seed/SKILL.md +19 -0
- package/.pi/skills/mm-shadow/SKILL.md +117 -0
- package/.pi/skills/mm-soul/SKILL.md +502 -0
- package/.pi/skills/mm-tasks/SKILL.md +24 -0
- package/.pi/skills/mm-update/SKILL.md +32 -0
- package/.pi/skills/mm-week/SKILL.md +28 -0
- package/.pi/skills/mm-welcome/SKILL.md +24 -0
- package/AGENTS.md +188 -0
- package/README.md +126 -9
- package/bin/loader.js +67 -0
- package/bin/mirror-hook.js +7 -0
- package/bin/mirror.js +7 -0
- package/docs/releases/index.md +100 -0
- package/docs/releases/pending-cutoffs.md +510 -0
- package/docs/releases/v0.10.0.md +123 -0
- package/docs/releases/v0.10.1.md +66 -0
- package/docs/releases/v0.10.10.md +51 -0
- package/docs/releases/v0.10.11.md +39 -0
- package/docs/releases/v0.10.12.md +57 -0
- package/docs/releases/v0.10.13.md +35 -0
- package/docs/releases/v0.10.14.md +42 -0
- package/docs/releases/v0.10.15.md +53 -0
- package/docs/releases/v0.10.16.md +35 -0
- package/docs/releases/v0.10.17.md +43 -0
- package/docs/releases/v0.10.2.md +64 -0
- package/docs/releases/v0.10.3.md +78 -0
- package/docs/releases/v0.10.4.md +42 -0
- package/docs/releases/v0.10.5.md +57 -0
- package/docs/releases/v0.10.6.md +47 -0
- package/docs/releases/v0.10.7.md +53 -0
- package/docs/releases/v0.10.8.md +49 -0
- package/docs/releases/v0.10.9.md +37 -0
- package/docs/releases/v0.11.0.md +86 -0
- package/docs/releases/v0.12.0.md +67 -0
- package/docs/releases/v0.13.0.md +64 -0
- package/docs/releases/v0.14.0.md +68 -0
- package/docs/releases/v0.15.0.md +48 -0
- package/docs/releases/v0.16.0.md +50 -0
- package/docs/releases/v0.16.1.md +55 -0
- package/docs/releases/v0.16.2.md +36 -0
- package/docs/releases/v0.17.0.md +50 -0
- package/docs/releases/v0.17.1.md +58 -0
- package/docs/releases/v0.17.2.md +42 -0
- package/docs/releases/v0.17.3.md +58 -0
- package/docs/releases/v0.18.0.md +84 -0
- package/docs/releases/v0.18.1.md +51 -0
- package/docs/releases/v0.18.2.md +69 -0
- package/docs/releases/v0.19.0.md +85 -0
- package/docs/releases/v0.19.1.md +46 -0
- package/docs/releases/v0.20.0.md +85 -0
- package/docs/releases/v0.21.0.md +61 -0
- package/docs/releases/v0.21.1.md +47 -0
- package/docs/releases/v0.22.0.md +77 -0
- package/docs/releases/v0.22.1.md +42 -0
- package/docs/releases/v0.23.0.md +105 -0
- package/docs/releases/v0.24.0.md +125 -0
- package/docs/releases/v0.25.0.md +107 -0
- package/docs/releases/v0.26.0.md +139 -0
- package/docs/releases/v0.26.1.md +65 -0
- package/docs/releases/v0.26.2.md +43 -0
- package/docs/releases/v0.27.0.md +63 -0
- package/docs/releases/v0.28.0.md +59 -0
- package/docs/releases/v0.29.0.md +61 -0
- package/docs/releases/v0.29.1.md +59 -0
- package/docs/releases/v0.29.2.md +59 -0
- package/docs/releases/v0.29.3.md +93 -0
- package/docs/releases/v0.30.0.md +75 -0
- package/docs/releases/v0.30.1.md +93 -0
- package/docs/releases/v0.30.2.md +66 -0
- package/docs/releases/v0.31.0.md +122 -0
- package/docs/releases/v0.31.1.md +142 -0
- package/docs/releases/v0.31.10.md +118 -0
- package/docs/releases/v0.31.11.md +88 -0
- package/docs/releases/v0.31.12.md +105 -0
- package/docs/releases/v0.31.13.md +99 -0
- package/docs/releases/v0.31.14.md +97 -0
- package/docs/releases/v0.31.2.md +115 -0
- package/docs/releases/v0.31.3.md +142 -0
- package/docs/releases/v0.31.4.md +117 -0
- package/docs/releases/v0.31.5.md +155 -0
- package/docs/releases/v0.31.6.md +132 -0
- package/docs/releases/v0.31.7.md +132 -0
- package/docs/releases/v0.31.8.md +120 -0
- package/docs/releases/v0.31.9.md +120 -0
- package/docs/releases/v0.32.0.md +261 -0
- package/docs/releases/v0.32.1.md +59 -0
- package/docs/releases/v0.33.0.md +164 -0
- package/docs/releases/v0.8.0.md +118 -0
- package/docs/releases/v0.9.0.md +98 -0
- package/docs/releases/v0.9.1.md +83 -0
- package/package.json +102 -6
- package/plugins/mirror-mind/.claude-plugin/plugin.json +17 -0
- package/plugins/mirror-mind/hooks/hooks.json +62 -0
- package/plugins/mirror-mind/hooks/log-assistant-reply.sh +98 -0
- package/plugins/mirror-mind/hooks/log-session-end.sh +98 -0
- package/plugins/mirror-mind/hooks/log-user-prompt.sh +98 -0
- package/plugins/mirror-mind/hooks/mirror-inject.sh +98 -0
- package/plugins/mirror-mind/hooks/session-start.sh +98 -0
- package/plugins/mirror-mind/skills/mm-backup/SKILL.md +27 -0
- package/plugins/mirror-mind/skills/mm-build/SKILL.md +1206 -0
- package/plugins/mirror-mind/skills/mm-consolidate/SKILL.md +112 -0
- package/plugins/mirror-mind/skills/mm-consult/SKILL.md +83 -0
- package/plugins/mirror-mind/skills/mm-conversations/SKILL.md +17 -0
- package/plugins/mirror-mind/skills/mm-discard/SKILL.md +40 -0
- package/plugins/mirror-mind/skills/mm-explore/SKILL.md +241 -0
- package/plugins/mirror-mind/skills/mm-help/SKILL.md +104 -0
- package/plugins/mirror-mind/skills/mm-identity/SKILL.md +96 -0
- package/plugins/mirror-mind/skills/mm-journal/SKILL.md +21 -0
- package/plugins/mirror-mind/skills/mm-journey/SKILL.md +37 -0
- package/plugins/mirror-mind/skills/mm-journeys/SKILL.md +15 -0
- package/plugins/mirror-mind/skills/mm-memories/SKILL.md +20 -0
- package/plugins/mirror-mind/skills/mm-mirror/SKILL.md +67 -0
- package/plugins/mirror-mind/skills/mm-mute/SKILL.md +18 -0
- package/plugins/mirror-mind/skills/mm-new/SKILL.md +18 -0
- package/plugins/mirror-mind/skills/mm-recall/SKILL.md +17 -0
- package/plugins/mirror-mind/skills/mm-release-notes/SKILL.md +31 -0
- package/plugins/mirror-mind/skills/mm-seed/SKILL.md +15 -0
- package/plugins/mirror-mind/skills/mm-shadow/SKILL.md +117 -0
- package/plugins/mirror-mind/skills/mm-soul/SKILL.md +488 -0
- package/plugins/mirror-mind/skills/mm-tasks/SKILL.md +36 -0
- package/plugins/mirror-mind/skills/mm-update/SKILL.md +30 -0
- package/plugins/mirror-mind/skills/mm-week/SKILL.md +46 -0
- package/plugins/mirror-mind/skills/mm-welcome/SKILL.md +22 -0
- package/scripts/codex-hooks/session-end.sh +85 -0
- package/scripts/codex-hooks/session-start.sh +85 -0
- package/scripts/codex-mirror.sh +48 -0
- package/templates/identity/README.md +119 -0
- package/templates/identity/ego/behavior.yaml +52 -0
- package/templates/identity/ego/constraints.yaml +15 -0
- package/templates/identity/ego/identity.yaml +43 -0
- package/templates/identity/journeys/personal-growth.yaml +39 -0
- package/templates/identity/organization/identity.yaml +24 -0
- package/templates/identity/organization/principles.yaml +17 -0
- package/templates/identity/personas/coach.yaml +94 -0
- package/templates/identity/personas/designer.yaml +93 -0
- package/templates/identity/personas/doctor.yaml +96 -0
- package/templates/identity/personas/engineer.yaml +77 -0
- package/templates/identity/personas/financial.yaml +97 -0
- package/templates/identity/personas/prompt-engineer.yaml +122 -0
- package/templates/identity/personas/researcher.yaml +88 -0
- package/templates/identity/personas/strategist.yaml +91 -0
- package/templates/identity/personas/teacher.yaml +95 -0
- package/templates/identity/personas/therapist.yaml +91 -0
- package/templates/identity/personas/thinker.yaml +74 -0
- package/templates/identity/personas/writer.yaml +76 -0
- package/templates/identity/self/config.yaml +16 -0
- package/templates/identity/self/soul.yaml +105 -0
- package/templates/identity/user/identity.yaml +30 -0
- package/ts/src/backup/staging.ts +62 -0
- package/ts/src/backup/zipBackup.ts +267 -0
- package/ts/src/backup/zipReader.ts +109 -0
- package/ts/src/backup/zipWriter.ts +148 -0
- package/ts/src/builder/activeCheckpoint.ts +134 -0
- package/ts/src/builder/aggregateStatus.ts +34 -0
- package/ts/src/builder/approve.ts +149 -0
- package/ts/src/builder/argv.ts +292 -0
- package/ts/src/builder/ariadMethod.ts +536 -0
- package/ts/src/builder/artifacts/artifactSurfaces.ts +106 -0
- package/ts/src/builder/artifacts/artifactWriter.ts +113 -0
- package/ts/src/builder/artifacts/closureArtifacts.ts +199 -0
- package/ts/src/builder/artifacts/deliveryStoryArtifacts.ts +316 -0
- package/ts/src/builder/artifacts/planArtifacts.ts +242 -0
- package/ts/src/builder/artifacts/scaffoldSections.ts +273 -0
- package/ts/src/builder/artifacts/scaffoldState.ts +227 -0
- package/ts/src/builder/artifacts/storyFiles.ts +61 -0
- package/ts/src/builder/artifacts/storyIndex.ts +166 -0
- package/ts/src/builder/cadence.ts +59 -0
- package/ts/src/builder/card.ts +192 -0
- package/ts/src/builder/checkpointRefused.ts +66 -0
- package/ts/src/builder/closure.ts +964 -0
- package/ts/src/builder/commands.ts +2053 -0
- package/ts/src/builder/cursorPosition.ts +176 -0
- package/ts/src/builder/cursorTransitions.ts +77 -0
- package/ts/src/builder/deliveryCursor.ts +577 -0
- package/ts/src/builder/deliveryStoryClosure.ts +521 -0
- package/ts/src/builder/deliveryStoryPlan.ts +536 -0
- package/ts/src/builder/deliveryStoryReady.ts +160 -0
- package/ts/src/builder/deliveryStoryRoadmapClosure.ts +151 -0
- package/ts/src/builder/expand.ts +336 -0
- package/ts/src/builder/flowUnit.ts +339 -0
- package/ts/src/builder/homeSurface.ts +122 -0
- package/ts/src/builder/implementationGuard.ts +125 -0
- package/ts/src/builder/index.ts +11 -0
- package/ts/src/builder/lifecycleRefusal.ts +92 -0
- package/ts/src/builder/lifecycleRibbon.ts +159 -0
- package/ts/src/builder/load.ts +313 -0
- package/ts/src/builder/methodAdoption.ts +113 -0
- package/ts/src/builder/methodDefinition.ts +429 -0
- package/ts/src/builder/methodInspection.ts +371 -0
- package/ts/src/builder/plan.ts +408 -0
- package/ts/src/builder/planPreauthorization.ts +265 -0
- package/ts/src/builder/prepare.ts +225 -0
- package/ts/src/builder/projectPaths.ts +64 -0
- package/ts/src/builder/pull.ts +356 -0
- package/ts/src/builder/pullCandidates.ts +463 -0
- package/ts/src/builder/pullCandidatesRender.ts +238 -0
- package/ts/src/builder/refinementField.ts +93 -0
- package/ts/src/builder/releaseIntent.ts +173 -0
- package/ts/src/builder/resumeState.ts +84 -0
- package/ts/src/builder/resumeSurface.ts +149 -0
- package/ts/src/builder/roadmapGrammar.ts +300 -0
- package/ts/src/builder/roadmapScan.ts +97 -0
- package/ts/src/builder/roadmapScope.ts +210 -0
- package/ts/src/builder/scopePhrases.ts +146 -0
- package/ts/src/builder/storyPaths.ts +196 -0
- package/ts/src/builder/storyPlanPreauthorization.ts +287 -0
- package/ts/src/builder/surfaceProtocol.ts +42 -0
- package/ts/src/builder/templateGeneration.ts +148 -0
- package/ts/src/builder/transition.ts +178 -0
- package/ts/src/consult/args.ts +99 -0
- package/ts/src/consult/core.ts +122 -0
- package/ts/src/consult/modelCatalog.ts +55 -0
- package/ts/src/consult/render.ts +47 -0
- package/ts/src/conversation/append.ts +414 -0
- package/ts/src/conversation/backfill.ts +387 -0
- package/ts/src/conversation/closeTail.ts +467 -0
- package/ts/src/conversation/extraction.ts +333 -0
- package/ts/src/conversation/extractionDriver.ts +130 -0
- package/ts/src/conversation/extractionRun.ts +70 -0
- package/ts/src/conversation/hookPayload.ts +51 -0
- package/ts/src/conversation/journeyInference.ts +165 -0
- package/ts/src/conversation/journeyRepair.ts +114 -0
- package/ts/src/conversation/lifecycleFaces.ts +197 -0
- package/ts/src/conversation/lifecycleWrites.ts +313 -0
- package/ts/src/conversation/listing.ts +77 -0
- package/ts/src/conversation/logger.ts +851 -0
- package/ts/src/conversation/loggerCli.ts +321 -0
- package/ts/src/conversation/loggerRuntime.ts +259 -0
- package/ts/src/conversation/metadataLifecycle.ts +443 -0
- package/ts/src/conversation/recall.ts +66 -0
- package/ts/src/conversation/sessionComposites.ts +258 -0
- package/ts/src/conversation/sessionImport.ts +90 -0
- package/ts/src/conversation/transcriptBackfill.ts +141 -0
- package/ts/src/cultivation/applyActions.ts +241 -0
- package/ts/src/cultivation/cluster.ts +86 -0
- package/ts/src/cultivation/consolidationStore.ts +281 -0
- package/ts/src/cultivation/promptContext.ts +62 -0
- package/ts/src/cultivation/propose.ts +305 -0
- package/ts/src/cultivation/scan.ts +155 -0
- package/ts/src/db/backupGate.ts +47 -0
- package/ts/src/db/bootstrap.ts +108 -0
- package/ts/src/db/bootstrapLock.ts +223 -0
- package/ts/src/db/copyGuard.ts +60 -0
- package/ts/src/db/database.ts +397 -0
- package/ts/src/db/decode.ts +66 -0
- package/ts/src/db/ftsIntegrity.ts +41 -0
- package/ts/src/db/migrateOnOpen.ts +214 -0
- package/ts/src/db/migrations.ts +576 -0
- package/ts/src/db/readyOnOpen.ts +35 -0
- package/ts/src/db/rowDecode.ts +42 -0
- package/ts/src/db/schema.ts +432 -0
- package/ts/src/db/schemaInventory.ts +225 -0
- package/ts/src/db/schemaInventorySnapshot.ts +2107 -0
- package/ts/src/db/schemaState.ts +91 -0
- package/ts/src/db/schemaTsDivergence.ts +114 -0
- package/ts/src/descriptor/descriptorRead.ts +49 -0
- package/ts/src/docs/docsLint.ts +265 -0
- package/ts/src/explorer/handoff.ts +522 -0
- package/ts/src/explorer/render.ts +335 -0
- package/ts/src/explorer/story.ts +769 -0
- package/ts/src/explorer/transition.ts +72 -0
- package/ts/src/extensions/bindings.ts +170 -0
- package/ts/src/extensions/catalog.ts +249 -0
- package/ts/src/extensions/catalogCommands.ts +513 -0
- package/ts/src/extensions/catalogWrites.ts +652 -0
- package/ts/src/extensions/contextRuntime.ts +325 -0
- package/ts/src/extensions/dispatch.ts +374 -0
- package/ts/src/extensions/errors.ts +24 -0
- package/ts/src/extensions/manifest.ts +356 -0
- package/ts/src/extensions/migrations.ts +350 -0
- package/ts/src/extraction/conversation.ts +304 -0
- package/ts/src/extraction/conversationMetadata.ts +206 -0
- package/ts/src/extraction/fencing.ts +106 -0
- package/ts/src/extraction/json.ts +17 -0
- package/ts/src/extraction/prompts.ts +470 -0
- package/ts/src/frontDoor/args.ts +30 -0
- package/ts/src/frontDoor/argvShape.ts +106 -0
- package/ts/src/frontDoor/buildLoadRuntime.ts +139 -0
- package/ts/src/frontDoor/buildRoute.ts +398 -0
- package/ts/src/frontDoor/cli.ts +1872 -0
- package/ts/src/frontDoor/cloneRoleGuard.ts +98 -0
- package/ts/src/frontDoor/consultRoute.ts +50 -0
- package/ts/src/frontDoor/contentTailRoute.ts +239 -0
- package/ts/src/frontDoor/conversationLoggerRoute.ts +83 -0
- package/ts/src/frontDoor/cultivationRoute.ts +210 -0
- package/ts/src/frontDoor/dbPath.ts +131 -0
- package/ts/src/frontDoor/dbSafetyToolsRoute.ts +240 -0
- package/ts/src/frontDoor/exploreRoute.ts +572 -0
- package/ts/src/frontDoor/extensionCatalogRoute.ts +252 -0
- package/ts/src/frontDoor/frontDoorLog.ts +57 -0
- package/ts/src/frontDoor/identityWrite.ts +36 -0
- package/ts/src/frontDoor/journeyWriteRoute.ts +26 -0
- package/ts/src/frontDoor/lifecycleWriteRoute.ts +110 -0
- package/ts/src/frontDoor/liveBackup.ts +100 -0
- package/ts/src/frontDoor/mirrorModeRoute.ts +163 -0
- package/ts/src/frontDoor/namedRefusal.ts +35 -0
- package/ts/src/frontDoor/nodeSupport.ts +24 -0
- package/ts/src/frontDoor/render/consolidate.ts +199 -0
- package/ts/src/frontDoor/render/conversations.ts +33 -0
- package/ts/src/frontDoor/render/cultivationShared.ts +21 -0
- package/ts/src/frontDoor/render/descriptor.ts +20 -0
- package/ts/src/frontDoor/render/detectPersona.ts +50 -0
- package/ts/src/frontDoor/render/icons.ts +13 -0
- package/ts/src/frontDoor/render/identity.ts +59 -0
- package/ts/src/frontDoor/render/identityRows.ts +17 -0
- package/ts/src/frontDoor/render/inspectPersona.ts +53 -0
- package/ts/src/frontDoor/render/journeyStatus.ts +78 -0
- package/ts/src/frontDoor/render/journeys.ts +79 -0
- package/ts/src/frontDoor/render/list.ts +41 -0
- package/ts/src/frontDoor/render/memories.ts +67 -0
- package/ts/src/frontDoor/render/recall.ts +49 -0
- package/ts/src/frontDoor/render/shadow.ts +155 -0
- package/ts/src/frontDoor/render/tasks.ts +99 -0
- package/ts/src/frontDoor/render/tasksImportSync.ts +63 -0
- package/ts/src/frontDoor/render/week.ts +113 -0
- package/ts/src/frontDoor/routing.ts +1001 -0
- package/ts/src/frontDoor/runtimeRoute.ts +623 -0
- package/ts/src/frontDoor/searchRoute.ts +166 -0
- package/ts/src/frontDoor/seedPaths.ts +98 -0
- package/ts/src/frontDoor/sessionStartWelcomeRoute.ts +166 -0
- package/ts/src/frontDoor/soulRoute.ts +433 -0
- package/ts/src/frontDoor/stdinSource.ts +37 -0
- package/ts/src/frontDoor/tasksImportSyncRoute.ts +110 -0
- package/ts/src/frontDoor/tasksWriteRoute.ts +101 -0
- package/ts/src/frontDoor/usage.ts +176 -0
- package/ts/src/guards/claudePlugin.ts +298 -0
- package/ts/src/guards/packContents.ts +306 -0
- package/ts/src/guards/piSourcedSkills.ts +70 -0
- package/ts/src/guards/retiredRouteShadows.ts +57 -0
- package/ts/src/guards/retiredSurfaces.ts +564 -0
- package/ts/src/hooks/claude.ts +214 -0
- package/ts/src/hooks/codex.ts +45 -0
- package/ts/src/hooks/gemini.ts +126 -0
- package/ts/src/hooks/main.ts +109 -0
- package/ts/src/hooks/mirrorState.ts +71 -0
- package/ts/src/hooks/payload.ts +73 -0
- package/ts/src/hooks/runtime.ts +179 -0
- package/ts/src/hooks/runtimeDetection.ts +34 -0
- package/ts/src/identity/applyConsolidationIdentityUpdate.ts +67 -0
- package/ts/src/identity/descriptorGenerate.ts +162 -0
- package/ts/src/identity/identityEdit.ts +143 -0
- package/ts/src/identity/identityKey.ts +77 -0
- package/ts/src/identity/identityRead.ts +69 -0
- package/ts/src/identity/identityStore.ts +80 -0
- package/ts/src/identity/journeyListing.ts +53 -0
- package/ts/src/identity/personaInspect.ts +44 -0
- package/ts/src/identity/personaListing.ts +37 -0
- package/ts/src/identity/setIdentity.ts +64 -0
- package/ts/src/index.ts +231 -0
- package/ts/src/init/init.ts +111 -0
- package/ts/src/journey/journeyOptions.ts +131 -0
- package/ts/src/journey/journeyRemoval.ts +158 -0
- package/ts/src/journey/journeyStatus.ts +149 -0
- package/ts/src/journey/journeySyncFile.ts +87 -0
- package/ts/src/journey/journeyWrite.ts +203 -0
- package/ts/src/journey/parentJourney.ts +41 -0
- package/ts/src/journey/validateParentJourney.ts +55 -0
- package/ts/src/mcp/boundary.ts +151 -0
- package/ts/src/mcp/guards.ts +244 -0
- package/ts/src/mcp/main.ts +121 -0
- package/ts/src/mcp/payload.ts +141 -0
- package/ts/src/mcp/protocol.ts +141 -0
- package/ts/src/mcp/readModels.ts +241 -0
- package/ts/src/mcp/registry.ts +201 -0
- package/ts/src/mcp/serve.ts +120 -0
- package/ts/src/mcp/tools/deterministic.ts +137 -0
- package/ts/src/mcp/tools/providerCrossing.ts +187 -0
- package/ts/src/mcp/wire.ts +8 -0
- package/ts/src/memory/journal.ts +180 -0
- package/ts/src/memory/listing.ts +107 -0
- package/ts/src/memory/memoryWrite.ts +88 -0
- package/ts/src/memory/reinforcement.ts +35 -0
- package/ts/src/mirror/context.ts +161 -0
- package/ts/src/mirror/defaultResolution.ts +199 -0
- package/ts/src/mirror/mirrorForm.ts +43 -0
- package/ts/src/mirror/orchestration.ts +364 -0
- package/ts/src/mirror/reception.ts +194 -0
- package/ts/src/mirror/render.ts +75 -0
- package/ts/src/mirror/runtimeSession.ts +205 -0
- package/ts/src/mode/operatingMode.ts +148 -0
- package/ts/src/observability/callOutcome.ts +115 -0
- package/ts/src/observability/inspectLedger.ts +329 -0
- package/ts/src/observability/ledgerHooks.ts +126 -0
- package/ts/src/observability/llmCalls.ts +154 -0
- package/ts/src/persona/detectPersona.ts +81 -0
- package/ts/src/planning/promptAssembly.ts +111 -0
- package/ts/src/planning/weekPending.ts +81 -0
- package/ts/src/planning/weekPlan.ts +183 -0
- package/ts/src/planning/weekSave.ts +117 -0
- package/ts/src/providers/config.ts +189 -0
- package/ts/src/providers/cost.ts +64 -0
- package/ts/src/providers/credits.ts +204 -0
- package/ts/src/providers/embedding.ts +321 -0
- package/ts/src/providers/familyProviders.ts +147 -0
- package/ts/src/providers/llm.ts +334 -0
- package/ts/src/providers/openrouter.ts +244 -0
- package/ts/src/providers/redaction.ts +110 -0
- package/ts/src/providers/replay.ts +15 -0
- package/ts/src/providers/transport.ts +410 -0
- package/ts/src/repair/encodingRepair.ts +253 -0
- package/ts/src/runtime/backup.ts +151 -0
- package/ts/src/runtime/channel.ts +146 -0
- package/ts/src/runtime/config.ts +168 -0
- package/ts/src/runtime/diagnose.ts +686 -0
- package/ts/src/runtime/git.ts +510 -0
- package/ts/src/runtime/installKind.ts +220 -0
- package/ts/src/runtime/migrate.ts +154 -0
- package/ts/src/runtime/packageIdentity.ts +102 -0
- package/ts/src/runtime/releaseNotes.ts +244 -0
- package/ts/src/runtime/status.ts +557 -0
- package/ts/src/runtime/strategies/clone.ts +200 -0
- package/ts/src/runtime/strategies/package.ts +328 -0
- package/ts/src/runtime/treeRoot.ts +30 -0
- package/ts/src/runtime/update.ts +445 -0
- package/ts/src/runtime/updateGate.ts +40 -0
- package/ts/src/runtime/updatePipeline.ts +159 -0
- package/ts/src/runtime/version.ts +36 -0
- package/ts/src/runtime/wiring.ts +130 -0
- package/ts/src/search/memorySearch.ts +338 -0
- package/ts/src/search/ranker.ts +154 -0
- package/ts/src/seed/seed.ts +256 -0
- package/ts/src/seed/seedYaml.ts +133 -0
- package/ts/src/soul/apply.ts +218 -0
- package/ts/src/soul/harvest.ts +267 -0
- package/ts/src/soul/prompts/soul_beauty_voice.md +103 -0
- package/ts/src/soul/prompts/soul_self_voice.md +219 -0
- package/ts/src/soul/prompts/soul_wisdom_voice.md +121 -0
- package/ts/src/soul/prompts.ts +85 -0
- package/ts/src/soul/render.ts +358 -0
- package/ts/src/soul/state.ts +212 -0
- package/ts/src/soul/transition.ts +37 -0
- package/ts/src/tasks/journeyPathParse.ts +113 -0
- package/ts/src/tasks/taskImportSync.ts +142 -0
- package/ts/src/tasks/taskStore.ts +312 -0
- package/ts/src/tasks/weekView.ts +120 -0
- package/ts/src/util/clipCodePoints.ts +26 -0
- package/ts/src/util/paths.ts +63 -0
- package/ts/src/util/program.ts +9 -0
- package/ts/src/util/pyGenerators.ts +144 -0
- package/ts/src/util/pythonText.ts +317 -0
- package/ts/src/util/semver.ts +45 -0
- package/ts/src/util/shellWord.ts +25 -0
- package/ts/src/util/slug.ts +64 -0
- package/ts/src/welcome/card.ts +339 -0
- package/ts/src/welcome/statusLine.ts +101 -0
- package/ts/src/welcome/updateCache.ts +129 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// `inspect persona <id>` rendering — the port of memory.cli.inspect._inspect_persona.
|
|
2
|
+
|
|
3
|
+
import type { Database } from "#db/database.ts";
|
|
4
|
+
import { getPersonaInspect } from "#identity/personaInspect.ts";
|
|
5
|
+
|
|
6
|
+
/** Raised when no persona identity row exists for the id. Python prints this to
|
|
7
|
+
* STDOUT (not stderr) before exit(1) — a deliberate divergence from `identity get`. */
|
|
8
|
+
export class PersonaNotFoundError extends Error {
|
|
9
|
+
readonly personaId: string;
|
|
10
|
+
constructor(personaId: string) {
|
|
11
|
+
super(`persona/${personaId} not found`);
|
|
12
|
+
this.personaId = personaId;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Fixed key order the Python source iterates, printing only present (non-null) values. */
|
|
17
|
+
const METADATA_KEYS = [
|
|
18
|
+
"persona_id",
|
|
19
|
+
"name",
|
|
20
|
+
"inherits_from",
|
|
21
|
+
"description",
|
|
22
|
+
"default_model",
|
|
23
|
+
] as const;
|
|
24
|
+
|
|
25
|
+
/** Render `inspect persona <id>`, or throw PersonaNotFoundError when absent. */
|
|
26
|
+
export function renderInspectPersona(db: Database, personaId: string): string {
|
|
27
|
+
const row = getPersonaInspect(db, personaId);
|
|
28
|
+
if (!row) throw new PersonaNotFoundError(personaId);
|
|
29
|
+
|
|
30
|
+
const prints: string[] = [
|
|
31
|
+
`=== persona/${personaId} ===`,
|
|
32
|
+
`version: ${row.version}`,
|
|
33
|
+
`updated_at: ${row.updatedAt}`,
|
|
34
|
+
"metadata:",
|
|
35
|
+
];
|
|
36
|
+
const metadataKeys = Object.keys(row.metadata);
|
|
37
|
+
if (metadataKeys.length > 0) {
|
|
38
|
+
for (const key of METADATA_KEYS) {
|
|
39
|
+
const value = row.metadata[key];
|
|
40
|
+
if (value !== undefined && value !== null) prints.push(` ${key}: ${value}`);
|
|
41
|
+
}
|
|
42
|
+
const rawKeywords = row.metadata.routing_keywords;
|
|
43
|
+
const keywords = Array.isArray(rawKeywords)
|
|
44
|
+
? rawKeywords.filter((item): item is string => typeof item === "string")
|
|
45
|
+
: [];
|
|
46
|
+
prints.push(` routing_keywords: ${keywords.length > 0 ? keywords.join(", ") : "(none)"}`);
|
|
47
|
+
} else {
|
|
48
|
+
prints.push(" (none)");
|
|
49
|
+
}
|
|
50
|
+
prints.push("\ncontent:\n");
|
|
51
|
+
prints.push(row.content);
|
|
52
|
+
return prints.map((line) => `${line}\n`).join("");
|
|
53
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// `journey status` rendering — the port of memory.cli.journey.cmd_status and
|
|
2
|
+
// main()'s positional-argument dispatch quirk.
|
|
3
|
+
|
|
4
|
+
import type { Database } from "#db/database.ts";
|
|
5
|
+
import {
|
|
6
|
+
allJourneyKeys,
|
|
7
|
+
getJourneyStatusEntries,
|
|
8
|
+
type JourneyStatusEntry,
|
|
9
|
+
type ReadTextFile,
|
|
10
|
+
} from "#journey/journeyStatus.ts";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Port of the slug-resolution branch in `journey.main()`. This reproduces a
|
|
14
|
+
* real, verified Python quirk rather than the "obvious" reading: only
|
|
15
|
+
* `remaining.length >= 2 && remaining[0] === "status"` treats `remaining[1]`
|
|
16
|
+
* as the slug. A bare `["status"]` (length 1, nothing after it) falls through
|
|
17
|
+
* to the generic `remaining[0] if remaining else None` branch — so the
|
|
18
|
+
* literal token "status" itself becomes the requested journey slug, NOT a
|
|
19
|
+
* request to show every journey. To see every journey, the caller must pass
|
|
20
|
+
* no positional tokens at all. Verified against the live oracle.
|
|
21
|
+
*/
|
|
22
|
+
export function resolveJourneyStatusSlug(remaining: readonly string[]): string | null {
|
|
23
|
+
if (remaining.length >= 2 && remaining[0] === "status") return remaining[1];
|
|
24
|
+
return remaining.length > 0 ? remaining[0] : null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Render one journey's status block (identity / journey path / memories / conversations). */
|
|
28
|
+
function renderJourneyStatusEntry(entry: JourneyStatusEntry): string[] {
|
|
29
|
+
const prints: string[] = [`=== journey: ${entry.journeyId} ===`];
|
|
30
|
+
if (entry.identity) {
|
|
31
|
+
prints.push("\n--- identity ---");
|
|
32
|
+
prints.push(entry.identity);
|
|
33
|
+
}
|
|
34
|
+
if (entry.journeyPath) {
|
|
35
|
+
prints.push("\n--- journey path ---");
|
|
36
|
+
prints.push(entry.journeyPath);
|
|
37
|
+
}
|
|
38
|
+
if (entry.recentMemories.length > 0) {
|
|
39
|
+
prints.push(`\n--- recent memories (${entry.recentMemories.length}) ---`);
|
|
40
|
+
for (const memory of entry.recentMemories) {
|
|
41
|
+
prints.push(` [${memory.created_at.slice(0, 10)}] ${memory.title}`);
|
|
42
|
+
}
|
|
43
|
+
} else {
|
|
44
|
+
prints.push("\n--- recent memories ---");
|
|
45
|
+
prints.push(" No recent memories.");
|
|
46
|
+
}
|
|
47
|
+
if (entry.recentConversations.length > 0) {
|
|
48
|
+
prints.push(`\n--- recent conversations (${entry.recentConversations.length}) ---`);
|
|
49
|
+
for (const conversation of entry.recentConversations) {
|
|
50
|
+
prints.push(
|
|
51
|
+
` [${conversation.started_at.slice(0, 10)}] ${conversation.title || "(untitled)"}`,
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
} else {
|
|
55
|
+
prints.push("\n--- recent conversations ---");
|
|
56
|
+
prints.push(" No recent conversations.");
|
|
57
|
+
}
|
|
58
|
+
prints.push("");
|
|
59
|
+
return prints;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Render `journey status` for a resolved slug (or every journey when `slug`
|
|
64
|
+
* is null). A non-existent slug is NOT an error — it renders an
|
|
65
|
+
* identity-less, path-less, empty-history block, matching the Python oracle
|
|
66
|
+
* (`get_journey_status` never validates the slug exists).
|
|
67
|
+
*/
|
|
68
|
+
export function renderJourneyStatus(
|
|
69
|
+
db: Database,
|
|
70
|
+
slug: string | null,
|
|
71
|
+
readTextFile?: ReadTextFile,
|
|
72
|
+
): string {
|
|
73
|
+
const keys = slug !== null ? [slug] : allJourneyKeys(db);
|
|
74
|
+
const entries = getJourneyStatusEntries(db, keys, readTextFile);
|
|
75
|
+
const prints: string[] = [];
|
|
76
|
+
for (const entry of entries) prints.push(...renderJourneyStatusEntry(entry));
|
|
77
|
+
return prints.map((line) => `${line}\n`).join("");
|
|
78
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// `journeys` rendering: read journey options, enrich with stage/description,
|
|
2
|
+
// format the bounded depth-first hierarchy. Extracted from cli.ts (CR002).
|
|
3
|
+
|
|
4
|
+
import type { Database } from "#db/database.ts";
|
|
5
|
+
import { optionalString, requireString } from "#db/rowDecode.ts";
|
|
6
|
+
import {
|
|
7
|
+
type JourneyIdentityRow,
|
|
8
|
+
type JourneyOption,
|
|
9
|
+
listJourneyOptions,
|
|
10
|
+
} from "#journey/journeyOptions.ts";
|
|
11
|
+
import { identityRows } from "./identityRows.ts";
|
|
12
|
+
|
|
13
|
+
/** A journey option enriched with the display-only stage and description. */
|
|
14
|
+
export interface JourneyDisplayRow extends JourneyOption {
|
|
15
|
+
stage: string;
|
|
16
|
+
description: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const STATUS_ICONS: Record<string, string> = { active: "🚧", completed: "✅", paused: "⏸" };
|
|
20
|
+
|
|
21
|
+
/** Read the `content` column of an identity layer keyed by identity key. */
|
|
22
|
+
function contentByKey(db: Database, layer: string): Map<string, string> {
|
|
23
|
+
return new Map(
|
|
24
|
+
identityRows(db, layer).map((row) => [
|
|
25
|
+
requireString(row, "key"),
|
|
26
|
+
optionalString(row, "content") ?? "",
|
|
27
|
+
]),
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Read journeys and enrich each with its stage (from journey_path) and
|
|
33
|
+
* description. Two queries total (journey + journey_path layers), not the
|
|
34
|
+
* former 1 + 2N: the journey content is loaded once into a map and the stage
|
|
35
|
+
* layer once, instead of a per-journey pair of lookups.
|
|
36
|
+
*/
|
|
37
|
+
export function journeyRows(db: Database): JourneyDisplayRow[] {
|
|
38
|
+
const journeyContent = contentByKey(db, "journey");
|
|
39
|
+
const stageContent = contentByKey(db, "journey_path");
|
|
40
|
+
const identityForOptions: JourneyIdentityRow[] = identityRows(db, "journey").map((row) => ({
|
|
41
|
+
key: requireString(row, "key"),
|
|
42
|
+
content: optionalString(row, "content") ?? "",
|
|
43
|
+
metadata: optionalString(row, "metadata"),
|
|
44
|
+
parent_journey: optionalString(row, "parent_journey"),
|
|
45
|
+
}));
|
|
46
|
+
return listJourneyOptions(identityForOptions).map((option) => {
|
|
47
|
+
const desc = (journeyContent.get(option.id) ?? "")
|
|
48
|
+
.split("\n")
|
|
49
|
+
.map((line) => line.trim())
|
|
50
|
+
.find((line) => line && !line.startsWith("#") && !line.startsWith("**"));
|
|
51
|
+
const stage =
|
|
52
|
+
(stageContent.get(option.id) ?? "")
|
|
53
|
+
.match(/\*\*(?:Current stage|Etapa atual):\*\*\s*(.+)/)?.[1]
|
|
54
|
+
?.trim() ?? "—";
|
|
55
|
+
return { ...option, stage, description: (desc ?? "").slice(0, 80) };
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Format one journey at its projected depth into display lines. */
|
|
60
|
+
export function renderJourney(row: JourneyDisplayRow): string[] {
|
|
61
|
+
const icon = STATUS_ICONS[row.status] ?? "•";
|
|
62
|
+
const connector = "│ ".repeat(row.depth);
|
|
63
|
+
const prefix = row.depth > 0 ? `${connector}└─ ` : "";
|
|
64
|
+
const detailIndent = row.depth > 0 ? `${connector} ` : " ";
|
|
65
|
+
const lines = [
|
|
66
|
+
`${prefix}${icon} **${row.id}** (${row.status})`,
|
|
67
|
+
`${detailIndent}Stage: ${row.stage}`,
|
|
68
|
+
];
|
|
69
|
+
if (row.description) lines.push(`${detailIndent}${row.description}`);
|
|
70
|
+
lines.push("");
|
|
71
|
+
return lines;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Render the full bounded depth-first `journeys` listing. */
|
|
75
|
+
export function renderJourneys(db: Database): string {
|
|
76
|
+
const rows = journeyRows(db);
|
|
77
|
+
if (rows.length === 0) return "No journeys found.\n";
|
|
78
|
+
return rows.flatMap((row) => renderJourney(row)).join("\n");
|
|
79
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// `list personas` / `list journeys` rendering — the port of the personas/journeys
|
|
2
|
+
// branches of memory.cli.inspect.cmd_list. `list extensions`/`list all` touch the
|
|
3
|
+
// extension catalog and stay on Python (bound to CV22.DS7.TS1).
|
|
4
|
+
|
|
5
|
+
import type { JourneyListRow } from "#identity/journeyListing.ts";
|
|
6
|
+
import type { PersonaListRow } from "#identity/personaListing.ts";
|
|
7
|
+
|
|
8
|
+
/** Render `list personas` (and the personas branch of `--verbose`). */
|
|
9
|
+
export function renderListPersonas(rows: PersonaListRow[], verbose: boolean): string {
|
|
10
|
+
const prints: string[] = ["=== PERSONAS ==="];
|
|
11
|
+
if (rows.length === 0) {
|
|
12
|
+
prints.push(" (none)");
|
|
13
|
+
} else {
|
|
14
|
+
for (const row of rows) {
|
|
15
|
+
if (!verbose) {
|
|
16
|
+
prints.push(` ${row.key}`);
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
const keywordsStr =
|
|
20
|
+
row.routingKeywords.length > 0 ? row.routingKeywords.join(", ") : "(none)";
|
|
21
|
+
prints.push(` ${row.key}`);
|
|
22
|
+
prints.push(` version: ${row.version}`);
|
|
23
|
+
prints.push(` routing_keywords: ${keywordsStr}`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return prints.map((line) => `${line}\n`).join("");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Render `list journeys`. */
|
|
30
|
+
export function renderListJourneys(rows: JourneyListRow[]): string {
|
|
31
|
+
const prints: string[] = ["=== JOURNEYS ==="];
|
|
32
|
+
if (rows.length === 0) {
|
|
33
|
+
prints.push(" (none)");
|
|
34
|
+
} else {
|
|
35
|
+
for (const row of rows) {
|
|
36
|
+
const suffix = row.description ? `: ${row.description}` : "";
|
|
37
|
+
prints.push(` [${row.status}] ${row.key}${suffix}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return prints.map((line) => `${line}\n`).join("");
|
|
41
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// `memories` rendering: read recent summaries, format rows and the
|
|
2
|
+
// count-by-type header. Extracted from cli.ts (CR002).
|
|
3
|
+
|
|
4
|
+
import type { Database } from "#db/database.ts";
|
|
5
|
+
import { hasOption, optionValue } from "#frontDoor/args.ts";
|
|
6
|
+
import {
|
|
7
|
+
countMemoriesByType,
|
|
8
|
+
type ListRecentFilters,
|
|
9
|
+
listRecentMemorySummaries,
|
|
10
|
+
type MemorySummary,
|
|
11
|
+
} from "#memory/listing.ts";
|
|
12
|
+
import { ICONS } from "./icons.ts";
|
|
13
|
+
|
|
14
|
+
/** Format a JSON tags string into a comma-joined list, or "" when absent/malformed. */
|
|
15
|
+
export function tagsText(tags: string | null): string {
|
|
16
|
+
if (!tags) return "";
|
|
17
|
+
try {
|
|
18
|
+
const parsed = JSON.parse(tags) as unknown;
|
|
19
|
+
return Array.isArray(parsed)
|
|
20
|
+
? parsed.filter((item) => typeof item === "string").join(", ")
|
|
21
|
+
: "";
|
|
22
|
+
} catch {
|
|
23
|
+
return "";
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Format one memory summary into display lines. */
|
|
28
|
+
export function renderMemoryRow(memory: MemorySummary): string[] {
|
|
29
|
+
const icon = ICONS[memory.memory_type] ?? "•";
|
|
30
|
+
const date = memory.created_at ? memory.created_at.slice(0, 10) : "?";
|
|
31
|
+
const journey = memory.journey ? ` 🧭 ${memory.journey}` : "";
|
|
32
|
+
const persona = memory.persona ? ` ◇ ${memory.persona}` : "";
|
|
33
|
+
const tags = tagsText(memory.tags);
|
|
34
|
+
const lines = [
|
|
35
|
+
`${icon} **${memory.title}**`,
|
|
36
|
+
` ${date} | \`${memory.id.slice(0, 8)}\` | ${memory.memory_type} [${memory.layer}]${journey}${persona}`,
|
|
37
|
+
` ${memory.content.slice(0, 200)}`,
|
|
38
|
+
];
|
|
39
|
+
if (tags) lines.push(` 🏷 ${tags}`);
|
|
40
|
+
lines.push("");
|
|
41
|
+
return lines;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Render the `memories` listing. Throws on `--search` (that route stays on Python). */
|
|
45
|
+
export function renderMemories(db: Database, args: readonly string[]): string {
|
|
46
|
+
if (hasOption(args, "--search")) throw new Error("search route must fallback to Python");
|
|
47
|
+
const filters: ListRecentFilters = {
|
|
48
|
+
limit: Number(optionValue(args, "--limit") ?? 20),
|
|
49
|
+
memoryType: optionValue(args, "--type"),
|
|
50
|
+
layer: optionValue(args, "--layer"),
|
|
51
|
+
journey: optionValue(args, "--journey"),
|
|
52
|
+
};
|
|
53
|
+
const memories = listRecentMemorySummaries(db, filters);
|
|
54
|
+
if (memories.length === 0) return "No memories found.\n";
|
|
55
|
+
const filterParts = [];
|
|
56
|
+
if (filters.memoryType) filterParts.push(`type=${filters.memoryType}`);
|
|
57
|
+
if (filters.layer) filterParts.push(`layer=${filters.layer}`);
|
|
58
|
+
if (filters.journey) filterParts.push(`journey=${filters.journey}`);
|
|
59
|
+
const filterText = filterParts.length ? ` (${filterParts.join(", ")})` : "";
|
|
60
|
+
const totals = countMemoriesByType(db)
|
|
61
|
+
.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
|
|
62
|
+
.map(([type, count]) => `${ICONS[type] ?? "•"} ${type}: ${count}`)
|
|
63
|
+
.join(" | ");
|
|
64
|
+
const lines = [`📦 Memories${filterText} — ${memories.length} shown`, totals, ""];
|
|
65
|
+
for (const memory of memories) lines.push(...renderMemoryRow(memory));
|
|
66
|
+
return lines.join("\n");
|
|
67
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// `recall <conv_id>` rendering — the port of memory.cli.recall.main.
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
findConversationByIdPrefix,
|
|
5
|
+
getMessagesForConversation,
|
|
6
|
+
pythonTailSliceStart,
|
|
7
|
+
} from "#conversation/recall.ts";
|
|
8
|
+
import type { Database } from "#db/database.ts";
|
|
9
|
+
|
|
10
|
+
/** Raised when no conversation matches the id prefix. Python prints this to stderr, exit 1. */
|
|
11
|
+
export class ConversationNotFoundError extends Error {
|
|
12
|
+
readonly convId: string;
|
|
13
|
+
constructor(convId: string) {
|
|
14
|
+
super(`Conversation '${convId}' not found.`);
|
|
15
|
+
this.convId = convId;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Render `recall <conv_id> [--limit N]`, or throw ConversationNotFoundError. */
|
|
20
|
+
export function renderRecall(db: Database, convId: string, limit: number): string {
|
|
21
|
+
const conv = findConversationByIdPrefix(db, convId);
|
|
22
|
+
if (!conv) throw new ConversationNotFoundError(convId);
|
|
23
|
+
|
|
24
|
+
const prints: string[] = [
|
|
25
|
+
`# Conversation: ${conv.title || "(untitled)"}`,
|
|
26
|
+
`**Date:** ${conv.started_at ? conv.started_at.slice(0, 10) : "?"}`,
|
|
27
|
+
];
|
|
28
|
+
if (conv.persona) prints.push(`**Persona:** ${conv.persona}`);
|
|
29
|
+
if (conv.journey) prints.push(`**Journey:** ${conv.journey}`);
|
|
30
|
+
prints.push(`**ID:** \`${conv.id}\``);
|
|
31
|
+
if (conv.summary) {
|
|
32
|
+
prints.push("");
|
|
33
|
+
prints.push(`**Summary:** ${conv.summary}`);
|
|
34
|
+
}
|
|
35
|
+
prints.push("", "---", "");
|
|
36
|
+
|
|
37
|
+
const messages = getMessagesForConversation(db, conv.id);
|
|
38
|
+
if (messages.length === 0) {
|
|
39
|
+
prints.push("(conversation has no messages)");
|
|
40
|
+
return prints.map((line) => `${line}\n`).join("");
|
|
41
|
+
}
|
|
42
|
+
const start = pythonTailSliceStart(messages.length, limit);
|
|
43
|
+
for (const msg of messages.slice(start)) {
|
|
44
|
+
prints.push(msg.role === "user" ? "**User:**" : "**Mirror:**");
|
|
45
|
+
prints.push(msg.content);
|
|
46
|
+
prints.push("");
|
|
47
|
+
}
|
|
48
|
+
return prints.map((line) => `${line}\n`).join("");
|
|
49
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
// `shadow` command rendering (CV22.DS7.US3) -- the port of
|
|
2
|
+
// `cli/shadow_cmd.py`'s print statements. See `consolidate.ts`'s module doc
|
|
3
|
+
// for the shared rendering philosophy and blank-line idiom.
|
|
4
|
+
|
|
5
|
+
import type { ConsolidationRow } from "#cultivation/consolidationStore.ts";
|
|
6
|
+
import type { ShadowScanResult } from "#cultivation/scan.ts";
|
|
7
|
+
import type { RejectOutcome, ShadowApplyRouteOutcome } from "#frontDoor/cultivationRoute.ts";
|
|
8
|
+
import { PROGRAM } from "#util/program.ts";
|
|
9
|
+
import type { RenderedCommand } from "./consolidate.ts";
|
|
10
|
+
import { renderAlreadyReviewed, renderProposalNotFound } from "./cultivationShared.ts";
|
|
11
|
+
|
|
12
|
+
const STATUS_ICON: Record<string, string> = {
|
|
13
|
+
pending: "⏳",
|
|
14
|
+
accepted: "✓",
|
|
15
|
+
rejected: "✗",
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
function sourceIds(sourceMemoryIds: string): string[] {
|
|
19
|
+
const parsed: unknown = sourceMemoryIds ? JSON.parse(sourceMemoryIds) : [];
|
|
20
|
+
return Array.isArray(parsed) ? parsed.map((value) => String(value)) : [];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Port of `shadow list`'s `cmd_list` -- callers pre-filter to `action === 'shadow_observation'`. */
|
|
24
|
+
export function renderShadowList(
|
|
25
|
+
items: readonly ConsolidationRow[],
|
|
26
|
+
status: string | null,
|
|
27
|
+
): string {
|
|
28
|
+
if (items.length === 0) {
|
|
29
|
+
const label = status ? ` (${status})` : "";
|
|
30
|
+
return `No shadow observations found${label}.\n`;
|
|
31
|
+
}
|
|
32
|
+
const lines: string[] = [];
|
|
33
|
+
for (const c of items) {
|
|
34
|
+
const st = STATUS_ICON[c.status] ?? "?";
|
|
35
|
+
const date = c.created_at.slice(0, 10);
|
|
36
|
+
const count = sourceIds(c.source_memory_ids).length;
|
|
37
|
+
lines.push(
|
|
38
|
+
`${st} [${c.id.slice(0, 8)}] ${date} 🌑 ${c.rationale || "shadow observation"} (${count} memories)`,
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
return lines.map((line) => `${line}\n`).join("");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Port of `shadow show`'s `cmd_show`. */
|
|
45
|
+
export function renderShadowShow(entries: readonly { key: string; content: string }[]): string {
|
|
46
|
+
if (entries.length === 0) {
|
|
47
|
+
return (
|
|
48
|
+
"The structural shadow layer is empty.\n" +
|
|
49
|
+
`Run '${PROGRAM} shadow scan' to surface candidate observations.\n`
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
const lines: string[] = [`Shadow layer (${entries.length} entries):`, ""];
|
|
53
|
+
for (const entry of entries) {
|
|
54
|
+
lines.push(`=== shadow/${entry.key} ===`);
|
|
55
|
+
lines.push(entry.content);
|
|
56
|
+
lines.push("");
|
|
57
|
+
}
|
|
58
|
+
return lines.map((line) => `${line}\n`).join("");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Port of `shadow reject`'s success message; not-found/already-reviewed are shared. */
|
|
62
|
+
export function renderShadowRejected(consolidation: ConsolidationRow): string {
|
|
63
|
+
return `Proposal [${consolidation.id.slice(0, 8)}] rejected. Shadow layer unchanged.\n`;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Port of `shadow reject`'s full output (not_found/already_reviewed shared, success family-specific). */
|
|
67
|
+
export function renderShadowReject(outcome: RejectOutcome): RenderedCommand {
|
|
68
|
+
if (outcome.kind === "not_found") {
|
|
69
|
+
return { text: renderProposalNotFound(outcome.proposalId), stderr: true, exitCode: 1 };
|
|
70
|
+
}
|
|
71
|
+
if (outcome.kind === "already_reviewed") {
|
|
72
|
+
return { text: renderAlreadyReviewed(outcome.consolidation), stderr: false, exitCode: 0 };
|
|
73
|
+
}
|
|
74
|
+
return { text: renderShadowRejected(outcome.consolidation), stderr: false, exitCode: 0 };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Port of `shadow apply`'s full output. */
|
|
78
|
+
export function renderShadowApply(outcome: ShadowApplyRouteOutcome): RenderedCommand {
|
|
79
|
+
if (outcome.kind === "not_found") {
|
|
80
|
+
return { text: renderProposalNotFound(outcome.proposalId), stderr: true, exitCode: 1 };
|
|
81
|
+
}
|
|
82
|
+
if (outcome.kind === "already_reviewed") {
|
|
83
|
+
return { text: renderAlreadyReviewed(outcome.consolidation), stderr: false, exitCode: 0 };
|
|
84
|
+
}
|
|
85
|
+
if (outcome.kind === "wrong_action") {
|
|
86
|
+
const { id, action } = outcome.consolidation;
|
|
87
|
+
return {
|
|
88
|
+
text: `Error: [${id.slice(0, 8)}] has action='${action}'. Use mm-consolidate for non-shadow proposals.\n`,
|
|
89
|
+
stderr: true,
|
|
90
|
+
exitCode: 1,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
// outcome.kind === "applied"
|
|
94
|
+
let text = `✓ Shadow layer updated: shadow/${outcome.targetKey}\n`;
|
|
95
|
+
if (outcome.sourceMemoryIds.length > 0) {
|
|
96
|
+
text += ` ${outcome.sourceMemoryIds.length} source memories advanced to 'acknowledged'.\n`;
|
|
97
|
+
}
|
|
98
|
+
text += `\nProposal [${outcome.consolidation.id.slice(0, 8)}] accepted and recorded with provenance.\n`;
|
|
99
|
+
return { text, stderr: false, exitCode: 0 };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const BAR = "─".repeat(60);
|
|
103
|
+
|
|
104
|
+
/** Port of `_print_proposal` in `shadow_cmd.py` (the scan-time observation card) -- DIFFERENT shape from consolidate's. */
|
|
105
|
+
function formatObservationBlock(proposal: ConsolidationRow, index: number, total: number): string {
|
|
106
|
+
const ids = sourceIds(proposal.source_memory_ids);
|
|
107
|
+
let out = `\n${BAR}\n`;
|
|
108
|
+
out += `Observation ${index}/${total} [${proposal.id.slice(0, 8)}] 🌑 SHADOW_OBSERVATION\n`;
|
|
109
|
+
out += `${BAR}\n`;
|
|
110
|
+
if (proposal.rationale) {
|
|
111
|
+
out += `**Pattern:** ${proposal.rationale}\n`;
|
|
112
|
+
}
|
|
113
|
+
if (ids.length > 0) {
|
|
114
|
+
out += `**Source memories:** ${ids.map((id) => id.slice(0, 8)).join(", ")}\n`;
|
|
115
|
+
}
|
|
116
|
+
out += `\n${proposal.proposal}\n`;
|
|
117
|
+
out += "\n";
|
|
118
|
+
return out;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** Port of `shadow scan`'s full output. */
|
|
122
|
+
export function renderShadowScan(result: ShadowScanResult): string {
|
|
123
|
+
if (result.candidatesConsidered === 0) {
|
|
124
|
+
return (
|
|
125
|
+
"No shadow-candidate memories found.\n" +
|
|
126
|
+
"Shadow candidates come from:\n" +
|
|
127
|
+
" \u2022 memories with layer='shadow'\n" +
|
|
128
|
+
" \u2022 memories of type 'tension' or 'pattern'\n" +
|
|
129
|
+
" \u2022 memories advanced to 'candidate' via mm-consolidate\n" +
|
|
130
|
+
"\n"
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
let out = `Found ${result.candidatesConsidered} shadow-candidate memories. Generating observations...\n\n`;
|
|
135
|
+
|
|
136
|
+
if (result.proposalsCreated.length === 0) {
|
|
137
|
+
out +=
|
|
138
|
+
"No new observations were proposed.\n" +
|
|
139
|
+
"The LLM found nothing new beyond what is already in the structural shadow layer,\n" +
|
|
140
|
+
"or the evidence was insufficient to surface an observation.\n";
|
|
141
|
+
return out;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
result.proposalsCreated.forEach((proposal, index) => {
|
|
145
|
+
out += formatObservationBlock(proposal, index + 1, result.proposalsCreated.length);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
out +=
|
|
149
|
+
`\n${result.proposalsCreated.length} observation(s) created with status='pending'.\n` +
|
|
150
|
+
"Review each observation above, then:\n" +
|
|
151
|
+
` Accept: ${PROGRAM} shadow apply <proposal_id>\n` +
|
|
152
|
+
` Edit: ${PROGRAM} shadow apply <proposal_id> --content "revised text"\n` +
|
|
153
|
+
` Reject: ${PROGRAM} shadow reject <proposal_id>\n`;
|
|
154
|
+
return out;
|
|
155
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// `tasks` command rendering — the port of `memory.cli.tasks_cmd`'s print
|
|
2
|
+
// statements. Every function here is a pure string builder over already-decided
|
|
3
|
+
// data (the write route in `tasksWriteRoute.ts` decides WHAT happened; this
|
|
4
|
+
// module decides HOW it is printed), matching Python's exact output including
|
|
5
|
+
// its blank-line quirks (`print(f"...\n")` followed by `print()`'s own newline).
|
|
6
|
+
|
|
7
|
+
import type { DeleteOutcome, StatusChangeOutcome } from "#frontDoor/tasksWriteRoute.ts";
|
|
8
|
+
import type { Task } from "#tasks/taskStore.ts";
|
|
9
|
+
|
|
10
|
+
const STATUS_ICONS: Record<string, string> = {
|
|
11
|
+
todo: "○",
|
|
12
|
+
doing: "◐",
|
|
13
|
+
done: "●",
|
|
14
|
+
blocked: "✖",
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/** Options mirroring the CLI flags that decide `tasks list`'s label/open-count. */
|
|
18
|
+
export interface ListTasksRenderOptions {
|
|
19
|
+
all: boolean;
|
|
20
|
+
status: string | null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Render `tasks list` (and the bare `tasks` default). `by_journey` grouping
|
|
25
|
+
* preserves Python's dict-insertion-order semantics via a `Map`, keyed by
|
|
26
|
+
* `journey || "(no journey)"`.
|
|
27
|
+
*/
|
|
28
|
+
export function renderTasksList(tasks: readonly Task[], options: ListTasksRenderOptions): string {
|
|
29
|
+
if (tasks.length === 0) return "No tasks found.\n";
|
|
30
|
+
|
|
31
|
+
const byJourney = new Map<string, Task[]>();
|
|
32
|
+
for (const t of tasks) {
|
|
33
|
+
const key = t.journey || "(no journey)";
|
|
34
|
+
const bucket = byJourney.get(key);
|
|
35
|
+
if (bucket) bucket.push(t);
|
|
36
|
+
else byJourney.set(key, [t]);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const label = options.all ? "all" : (options.status ?? "open");
|
|
40
|
+
const totalOpen = tasks.filter(
|
|
41
|
+
(t) => t.status === "todo" || t.status === "doing" || t.status === "blocked",
|
|
42
|
+
).length;
|
|
43
|
+
// Suppressed purely by `status`, independent of `all` -- matches Python's
|
|
44
|
+
// own independent `if not args.status` check (not nested under the
|
|
45
|
+
// all/status/else label branch).
|
|
46
|
+
const openNote = options.status ? "" : ` (${totalOpen} open)`;
|
|
47
|
+
|
|
48
|
+
// The header line's trailing "" reproduces `print(f"...\n")` (a literal \n
|
|
49
|
+
// inside the f-string) immediately followed by `print()`'s own newline.
|
|
50
|
+
const lines: string[] = [`📋 Tasks ${label}: ${tasks.length}${openNote}`, ""];
|
|
51
|
+
for (const [journey, journeyTasks] of byJourney) {
|
|
52
|
+
lines.push(`🧭 ${journey}`);
|
|
53
|
+
for (const t of journeyTasks) {
|
|
54
|
+
const icon = STATUS_ICONS[t.status] ?? "?";
|
|
55
|
+
const due = t.due_date ? ` 📅 ${t.due_date}` : "";
|
|
56
|
+
const stage = t.stage ? ` [${t.stage}]` : "";
|
|
57
|
+
lines.push(` ${icon} \`${t.id}\` ${t.title}${due}${stage}`);
|
|
58
|
+
}
|
|
59
|
+
lines.push(""); // the trailing bare `print()` after each journey's tasks
|
|
60
|
+
}
|
|
61
|
+
return lines.map((line) => `${line}\n`).join("");
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Render `tasks add`. */
|
|
65
|
+
export function renderTasksAdd(task: Task): string {
|
|
66
|
+
const lines = [`✅ Task created: \`${task.id}\` - ${task.title}`];
|
|
67
|
+
if (task.journey) lines.push(` Journey: ${task.journey}`);
|
|
68
|
+
if (task.due_date) lines.push(` Due: ${task.due_date}`);
|
|
69
|
+
return lines.map((line) => `${line}\n`).join("");
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Render a `tasks done|doing|block` outcome. This is where the resolver's
|
|
74
|
+
* "ambiguous" fact becomes Python's `cmd_status_change` message -- the
|
|
75
|
+
* shared resolver stays silent on presentation (taskStore.ts's module doc).
|
|
76
|
+
*/
|
|
77
|
+
export function renderTasksStatusChange(outcome: StatusChangeOutcome): string {
|
|
78
|
+
if (outcome.kind === "not_found") {
|
|
79
|
+
return `❌ Task '${outcome.idOrPrefix}' not found.\n`;
|
|
80
|
+
}
|
|
81
|
+
if (outcome.kind === "ambiguous") {
|
|
82
|
+
const ids = outcome.matches.map((t) => t.id).join(", ");
|
|
83
|
+
return `❌ Ambiguous ID '${outcome.idOrPrefix}'. Matches: ${ids}\n`;
|
|
84
|
+
}
|
|
85
|
+
const icon = STATUS_ICONS[outcome.newStatus] ?? "?";
|
|
86
|
+
return `${icon} Task \`${outcome.task.id}\` → ${outcome.newStatus}: ${outcome.task.title}\n`;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Render a `tasks delete` outcome. This is the ONE place the asymmetry with
|
|
91
|
+
* status-change shows: an "ambiguous" resolver result is folded into the same
|
|
92
|
+
* "not found" message Python's `cmd_delete` prints for zero matches.
|
|
93
|
+
*/
|
|
94
|
+
export function renderTasksDelete(outcome: DeleteOutcome): string {
|
|
95
|
+
if (outcome.kind === "not_found") {
|
|
96
|
+
return `❌ Task '${outcome.idOrPrefix}' not found.\n`;
|
|
97
|
+
}
|
|
98
|
+
return `🗑 Task removed: \`${outcome.task.id}\` - ${outcome.task.title}\n`;
|
|
99
|
+
}
|