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,148 @@
|
|
|
1
|
+
// CV22.DS7.US8 plateau 2 — method template preparation.
|
|
2
|
+
//
|
|
3
|
+
// Port of `src/memory/builder/template_generation.py`. This is the one command in
|
|
4
|
+
// the plateau that writes into the USER's repository: nine files under
|
|
5
|
+
// `docs/project/roadmap/`, whose bytes are the template bodies transcribed in
|
|
6
|
+
// `ariadMethod.ts` and graded by deep equality there.
|
|
7
|
+
//
|
|
8
|
+
// The rule that makes it safe to re-run is `if target.exists()` — an existing file
|
|
9
|
+
// is PRESERVED, never overwritten, and reported as such. A port that writes
|
|
10
|
+
// unconditionally would silently replace a Navigator's authored `plan.md` with the
|
|
11
|
+
// blank template, which is the worst defect available in this story: destructive,
|
|
12
|
+
// silent, and in someone else's git history.
|
|
13
|
+
//
|
|
14
|
+
// Confinement is checked after resolution (`is_relative_to(root)`), so a template
|
|
15
|
+
// path that escapes the project root raises instead of writing. Today no declared
|
|
16
|
+
// template can trigger it — `validateMethodDefinition` already rejects an absolute
|
|
17
|
+
// path or a `..` component — but the guard is the second of the two, and it is the
|
|
18
|
+
// one that survives a DSL edited by hand after DS10.
|
|
19
|
+
|
|
20
|
+
import { relative, resolve } from "node:path";
|
|
21
|
+
import { writeBuilderArtifact } from "./artifacts/artifactWriter.ts";
|
|
22
|
+
import type { MethodDefinition, TemplateDefinition } from "./methodDefinition.ts";
|
|
23
|
+
import { projectFolder } from "./projectPaths.ts";
|
|
24
|
+
|
|
25
|
+
/** Python `PENDING_TEMPLATE_PREPARATION_ITEMS`. */
|
|
26
|
+
export const PENDING_TEMPLATE_PREPARATION_ITEMS = [
|
|
27
|
+
"runtime delivery cursor sync",
|
|
28
|
+
"active roadmap item resolution",
|
|
29
|
+
"story lifecycle execution",
|
|
30
|
+
"project/journey override merge",
|
|
31
|
+
"release and push policy enforcement",
|
|
32
|
+
] as const;
|
|
33
|
+
|
|
34
|
+
/** Python `TemplateWriteResult`. */
|
|
35
|
+
export interface TemplateWriteResult {
|
|
36
|
+
readonly id: string;
|
|
37
|
+
readonly path: string;
|
|
38
|
+
readonly action: "created" | "preserved";
|
|
39
|
+
readonly description: string | null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Python `BuilderTemplatePreparation`. */
|
|
43
|
+
export interface BuilderTemplatePreparation {
|
|
44
|
+
readonly journey: string;
|
|
45
|
+
/** The project's folder, named beside the journey (CR009). */
|
|
46
|
+
readonly project: string;
|
|
47
|
+
readonly method: string;
|
|
48
|
+
readonly checked: readonly string[];
|
|
49
|
+
readonly created: readonly TemplateWriteResult[];
|
|
50
|
+
readonly preserved: readonly TemplateWriteResult[];
|
|
51
|
+
readonly pending: readonly string[];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Python `_target_path`, including the escape guard. */
|
|
55
|
+
function targetPath(root: string, template: TemplateDefinition): string {
|
|
56
|
+
const target = resolve(root, template.path);
|
|
57
|
+
const relation = relative(root, target);
|
|
58
|
+
if (relation.startsWith("..") || resolve(relation) === relation) {
|
|
59
|
+
throw new Error(`template path escapes project root: ${template.path}`);
|
|
60
|
+
}
|
|
61
|
+
return target;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Python `prepare_method_templates`.
|
|
66
|
+
*
|
|
67
|
+
* `checked` accumulates EVERY template path, in declaration order, whether or not
|
|
68
|
+
* it was written — so the report distinguishes "looked at nine, created two,
|
|
69
|
+
* preserved seven" from "only saw two".
|
|
70
|
+
*/
|
|
71
|
+
export function prepareMethodTemplates(
|
|
72
|
+
projectPath: string,
|
|
73
|
+
options: { journey: string; method: MethodDefinition },
|
|
74
|
+
): BuilderTemplatePreparation {
|
|
75
|
+
const root = resolve(projectPath);
|
|
76
|
+
const created: TemplateWriteResult[] = [];
|
|
77
|
+
const preserved: TemplateWriteResult[] = [];
|
|
78
|
+
const checked: string[] = [];
|
|
79
|
+
|
|
80
|
+
for (const template of options.method.templates) {
|
|
81
|
+
const target = targetPath(root, template);
|
|
82
|
+
checked.push(template.path);
|
|
83
|
+
// A template is a scaffold: written where absent, and the Driver's from then on.
|
|
84
|
+
const outcome = writeBuilderArtifact({
|
|
85
|
+
path: target,
|
|
86
|
+
content: template.content,
|
|
87
|
+
policy: "create-only",
|
|
88
|
+
projectRoot: root,
|
|
89
|
+
});
|
|
90
|
+
const result: TemplateWriteResult = {
|
|
91
|
+
id: template.id,
|
|
92
|
+
path: template.path,
|
|
93
|
+
action: outcome === "existing" ? "preserved" : "created",
|
|
94
|
+
description: template.description ?? null,
|
|
95
|
+
};
|
|
96
|
+
(outcome === "existing" ? preserved : created).push(result);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
journey: options.journey,
|
|
101
|
+
project: projectFolder(projectPath),
|
|
102
|
+
method: options.method.id,
|
|
103
|
+
checked,
|
|
104
|
+
created,
|
|
105
|
+
preserved,
|
|
106
|
+
pending: [...PENDING_TEMPLATE_PREPARATION_ITEMS],
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** Python `_format_paths` / `_format_results`: the PATH, not the id. */
|
|
111
|
+
function formatPaths(paths: readonly string[]): string[] {
|
|
112
|
+
return paths.length > 0 ? [...paths] : ["none"];
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function formatResults(results: readonly TemplateWriteResult[]): string[] {
|
|
116
|
+
return results.length > 0 ? results.map((result) => result.path) : ["none"];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Python `render_template_preparation_report`: plain text, no card frame. */
|
|
120
|
+
export function renderTemplatePreparationReport(report: BuilderTemplatePreparation): string {
|
|
121
|
+
return `${[
|
|
122
|
+
"■ Ariad Template Preparation",
|
|
123
|
+
"",
|
|
124
|
+
"journey",
|
|
125
|
+
report.journey,
|
|
126
|
+
"",
|
|
127
|
+
"project",
|
|
128
|
+
report.project,
|
|
129
|
+
"",
|
|
130
|
+
"method",
|
|
131
|
+
report.method,
|
|
132
|
+
"",
|
|
133
|
+
"checked",
|
|
134
|
+
...formatPaths(report.checked),
|
|
135
|
+
"",
|
|
136
|
+
"created",
|
|
137
|
+
...formatResults(report.created),
|
|
138
|
+
"",
|
|
139
|
+
"preserved",
|
|
140
|
+
...formatResults(report.preserved),
|
|
141
|
+
"",
|
|
142
|
+
"pending",
|
|
143
|
+
...report.pending,
|
|
144
|
+
"",
|
|
145
|
+
"boundary",
|
|
146
|
+
"No story lifecycle work was executed.",
|
|
147
|
+
].join("\n")}\n`;
|
|
148
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
// CV22.DS7.US8 plateau 7 — the ■ BUILDER MODE ACTIVE card and `load`'s query.
|
|
2
|
+
//
|
|
3
|
+
// Ports `render_builder_mode_transition` and its private helpers from
|
|
4
|
+
// `src/memory/surfaces/mode_transition.py`, plus `_extract_query` from
|
|
5
|
+
// `cli/build.py`.
|
|
6
|
+
//
|
|
7
|
+
// `_box` / `_line` / `_wrap` are character-identical to the Explorer card's
|
|
8
|
+
// (`explorer/transition.ts`), and that is not an accident worth deduplicating
|
|
9
|
+
// away: they are the SAME Python module, so sharing them here is correct where
|
|
10
|
+
// sharing them with the story cards in `render.ts` would not be — those chunk
|
|
11
|
+
// over-long words, these truncate. The duplication stays inside the module that
|
|
12
|
+
// owns each oracle.
|
|
13
|
+
//
|
|
14
|
+
// Three behaviors this file reproduces that a reasonable port would fix:
|
|
15
|
+
//
|
|
16
|
+
// 1. **`_extract_query` tests every LINE, not only headings**, and a match
|
|
17
|
+
// `continue`s — so a body line containing `context` is silently DROPPED from
|
|
18
|
+
// the query while the lines around it survive. Harmless in a long
|
|
19
|
+
// description, fatal in a short one: a body whose only line mentions the word
|
|
20
|
+
// captures nothing and the query becomes the slug.
|
|
21
|
+
// 2. **No Unicode normalization.** The Portuguese section names are compared as
|
|
22
|
+
// literals, so an NFD `Descrição` does not match the NFC list and the query
|
|
23
|
+
// falls back to the slug. Adding `.normalize("NFC")` here would fix the
|
|
24
|
+
// defect and break parity; it is a CR instead.
|
|
25
|
+
// 3. **`[:500]` is code points**, not UTF-16 units. A briefing of astral
|
|
26
|
+
// characters cut with `slice(0, 500)` loses half the text, changing the
|
|
27
|
+
// embedding and therefore the ranking, with no error.
|
|
28
|
+
|
|
29
|
+
import { codePointLength, pySplitWhitespace, sliceCodePoints } from "#util/pythonText.ts";
|
|
30
|
+
|
|
31
|
+
const WIDTH = 56;
|
|
32
|
+
|
|
33
|
+
/** `mode_transition._line`: slice to WIDTH, then pad — both by code point. */
|
|
34
|
+
function line(text: string): string {
|
|
35
|
+
const content = sliceCodePoints(text, WIDTH);
|
|
36
|
+
return `│${content.padEnd(WIDTH + (content.length - codePointLength(content)))}│`;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** `mode_transition._wrap`: greedy fill at WIDTH-2, long words NOT chunked. */
|
|
40
|
+
function wrap(text: string): string[] {
|
|
41
|
+
const maxWidth = WIDTH - 2;
|
|
42
|
+
const words = pySplitWhitespace(text);
|
|
43
|
+
if (words.length === 0) return [""];
|
|
44
|
+
const lines: string[] = [];
|
|
45
|
+
let current = words[0] as string;
|
|
46
|
+
for (const word of words.slice(1)) {
|
|
47
|
+
if (codePointLength(current) + 1 + codePointLength(word) <= maxWidth) {
|
|
48
|
+
current = `${current} ${word}`;
|
|
49
|
+
} else {
|
|
50
|
+
lines.push(current);
|
|
51
|
+
current = word;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
lines.push(current);
|
|
55
|
+
return lines;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
type Row = readonly [label: string, value: string];
|
|
59
|
+
|
|
60
|
+
/** `mode_transition._box`. */
|
|
61
|
+
function box(title: string, rows: readonly Row[]): string {
|
|
62
|
+
const lines = ["Mirror", `╭${"─".repeat(WIDTH)}╮`, line(` ${title}`)];
|
|
63
|
+
for (const [label, value] of rows) {
|
|
64
|
+
lines.push(line(""));
|
|
65
|
+
lines.push(line(` ${label}`));
|
|
66
|
+
for (const wrapped of wrap(value)) lines.push(line(` ${wrapped}`));
|
|
67
|
+
}
|
|
68
|
+
lines.push(`╰${"─".repeat(WIDTH)}╯`);
|
|
69
|
+
return lines.join("\n");
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* `mode_transition._extract_stage`.
|
|
74
|
+
*
|
|
75
|
+
* `^\*\*Stage:\*\*\s*(.+)$` under `re.MULTILINE`. Written with explicit anchors
|
|
76
|
+
* because JavaScript's `m` flag also anchors at U+2028/U+2029 where Python's does
|
|
77
|
+
* not, and `.` must not cross a newline in either.
|
|
78
|
+
*/
|
|
79
|
+
const STAGE_RE = /(?<=^|\n)\*\*Stage:\*\*[^\S\n]*(?<stage>[^\n]+)(?=\n|$)/u;
|
|
80
|
+
|
|
81
|
+
export function extractStage(content: string): string | null {
|
|
82
|
+
const match = STAGE_RE.exec(content);
|
|
83
|
+
const stage = match?.groups?.stage;
|
|
84
|
+
return stage === undefined ? null : stage.trim();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* `mode_transition._extract_section`.
|
|
89
|
+
*
|
|
90
|
+
* Capture starts at the first heading whose text contains one of `names`, and ends
|
|
91
|
+
* at the NEXT heading. When nothing was captured it falls back to every non-heading
|
|
92
|
+
* line joined and cut at 240 — a port that returns `null` there renders a card with
|
|
93
|
+
* no briefing row, which is the row a Navigator reads first.
|
|
94
|
+
*/
|
|
95
|
+
export function extractSection(content: string, names: readonly string[]): string | null {
|
|
96
|
+
const lines = content.split("\n");
|
|
97
|
+
let capturing = false;
|
|
98
|
+
const result: string[] = [];
|
|
99
|
+
for (const raw of lines) {
|
|
100
|
+
const stripped = raw.trim();
|
|
101
|
+
const header = stripped.replace(/^#+/u, "").trim().toLowerCase();
|
|
102
|
+
if (stripped.startsWith("#")) {
|
|
103
|
+
if (capturing) break;
|
|
104
|
+
if (names.some((name) => header.includes(name))) {
|
|
105
|
+
capturing = true;
|
|
106
|
+
}
|
|
107
|
+
} else if (capturing) {
|
|
108
|
+
result.push(stripped);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
const text = result
|
|
112
|
+
.filter((entry) => entry !== "")
|
|
113
|
+
.join(" ")
|
|
114
|
+
.trim();
|
|
115
|
+
if (text) return text;
|
|
116
|
+
const fallback = lines
|
|
117
|
+
.filter((entry) => entry.trim() !== "" && !entry.startsWith("#"))
|
|
118
|
+
.map((entry) => entry.trim())
|
|
119
|
+
.join(" ");
|
|
120
|
+
return sliceCodePoints(fallback, 240).trim() || null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** `mode_transition._truncate_words`. */
|
|
124
|
+
export function truncateWords(text: string, limit: number): string {
|
|
125
|
+
const words = pySplitWhitespace(text);
|
|
126
|
+
if (words.length <= limit) return text;
|
|
127
|
+
// Python `rstrip(".,;")` strips a SET of characters, not a suffix.
|
|
128
|
+
return `${words
|
|
129
|
+
.slice(0, limit)
|
|
130
|
+
.join(" ")
|
|
131
|
+
.replace(/[.,;]+$/u, "")}…`;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** Python `render_builder_mode_transition`. */
|
|
135
|
+
export function renderBuilderModeTransition(options: {
|
|
136
|
+
journey: string;
|
|
137
|
+
journeyContent: string;
|
|
138
|
+
projectPath?: string | null;
|
|
139
|
+
}): string {
|
|
140
|
+
const rows: Row[] = [["active journey", options.journey]];
|
|
141
|
+
const stage = extractStage(options.journeyContent);
|
|
142
|
+
if (stage) rows.push(["journey path", stage]);
|
|
143
|
+
if (options.projectPath) rows.push(["project path", options.projectPath]);
|
|
144
|
+
const briefing = extractSection(options.journeyContent, ["briefing", "description", "context"]);
|
|
145
|
+
if (briefing) rows.push(["briefing", truncateWords(briefing, 32)]);
|
|
146
|
+
rows.push(["boundary", "Builder executes commitment."]);
|
|
147
|
+
return box("■ BUILDER MODE ACTIVE", rows);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** The five section names `_extract_query` accepts, NFC as Python writes them. */
|
|
151
|
+
const QUERY_SECTIONS = ["description", "briefing", "context", "descrição", "contexto"] as const;
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Python `cli/build._extract_query`.
|
|
155
|
+
*
|
|
156
|
+
* Note what this is NOT: it is not `extractSection` with different names. The
|
|
157
|
+
* match runs against every line, a matching line is SKIPPED rather than captured,
|
|
158
|
+
* and the fallback is the slug rather than a 240-character digest. Two extractors,
|
|
159
|
+
* two behaviors, over the same document — reproduced separately because merging
|
|
160
|
+
* them changes both.
|
|
161
|
+
*/
|
|
162
|
+
export function extractQuery(journeyContent: string, slug: string): string {
|
|
163
|
+
const result: string[] = [];
|
|
164
|
+
let capturing = false;
|
|
165
|
+
for (const raw of journeyContent.split("\n")) {
|
|
166
|
+
const header = raw.replace(/^#+/u, "").trim().toLowerCase();
|
|
167
|
+
if (QUERY_SECTIONS.some((section) => header.includes(section))) {
|
|
168
|
+
capturing = true;
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
if (capturing) {
|
|
172
|
+
if (raw.startsWith("#")) break;
|
|
173
|
+
result.push(raw);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
const text = result.join(" ").trim();
|
|
177
|
+
return text ? sliceCodePoints(text, 500) : slug;
|
|
178
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { isConsultTier, resolveConsultModel } from "./modelCatalog.ts";
|
|
2
|
+
|
|
3
|
+
export type ConsultParseResult = ConsultCreditsCommand | ConsultAskCommand;
|
|
4
|
+
|
|
5
|
+
export interface ConsultCreditsCommand {
|
|
6
|
+
command: "credits";
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface ConsultAskCommand {
|
|
10
|
+
command: "ask";
|
|
11
|
+
modelId: string;
|
|
12
|
+
prompt: string;
|
|
13
|
+
persona: string | null;
|
|
14
|
+
journey: string | null;
|
|
15
|
+
org: boolean;
|
|
16
|
+
query: string | null;
|
|
17
|
+
mirrorHome: string | null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export class ConsultArgError extends Error {}
|
|
21
|
+
|
|
22
|
+
export const CONSULT_USAGE =
|
|
23
|
+
"Usage: consult <family> [tier] [question] [--persona P] [--journey J] [--org]\n" +
|
|
24
|
+
" consult credits";
|
|
25
|
+
|
|
26
|
+
export function parseConsultArgs(argv: readonly string[]): ConsultParseResult {
|
|
27
|
+
if (argv.length === 0) {
|
|
28
|
+
throw new ConsultArgError(CONSULT_USAGE);
|
|
29
|
+
}
|
|
30
|
+
if (argv[0] === "credits") {
|
|
31
|
+
return { command: "credits" };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
let persona: string | null = null;
|
|
35
|
+
let journey: string | null = null;
|
|
36
|
+
let org = false;
|
|
37
|
+
let query: string | null = null;
|
|
38
|
+
let mirrorHome: string | null = null;
|
|
39
|
+
const positional: string[] = [];
|
|
40
|
+
|
|
41
|
+
let i = 0;
|
|
42
|
+
while (i < argv.length) {
|
|
43
|
+
const arg = argv[i];
|
|
44
|
+
const next = argv[i + 1];
|
|
45
|
+
if (arg === "--persona" && next !== undefined) {
|
|
46
|
+
persona = next;
|
|
47
|
+
i += 2;
|
|
48
|
+
} else if (arg === "--journey" && next !== undefined) {
|
|
49
|
+
journey = next;
|
|
50
|
+
i += 2;
|
|
51
|
+
} else if (arg === "--query" && next !== undefined) {
|
|
52
|
+
query = next;
|
|
53
|
+
i += 2;
|
|
54
|
+
} else if (arg === "--mirror-home" && next !== undefined) {
|
|
55
|
+
mirrorHome = next;
|
|
56
|
+
i += 2;
|
|
57
|
+
} else if (arg === "--org") {
|
|
58
|
+
org = true;
|
|
59
|
+
i += 1;
|
|
60
|
+
} else {
|
|
61
|
+
positional.push(arg);
|
|
62
|
+
i += 1;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (positional.length === 0) {
|
|
67
|
+
throw new ConsultArgError("Error: model family is required.");
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const family = positional[0] ?? "";
|
|
71
|
+
let tier = "lite";
|
|
72
|
+
let prompt: string | null = null;
|
|
73
|
+
|
|
74
|
+
if (positional.length === 1) {
|
|
75
|
+
throw new ConsultArgError("Error: question is required.");
|
|
76
|
+
}
|
|
77
|
+
if (positional.length === 2) {
|
|
78
|
+
if (isConsultTier(positional[1] ?? "")) {
|
|
79
|
+
throw new ConsultArgError("Error: question is required.");
|
|
80
|
+
}
|
|
81
|
+
prompt = positional[1] ?? "";
|
|
82
|
+
} else if (isConsultTier(positional[1] ?? "")) {
|
|
83
|
+
tier = positional[1] ?? "lite";
|
|
84
|
+
prompt = positional.slice(2).join(" ");
|
|
85
|
+
} else {
|
|
86
|
+
prompt = positional.slice(1).join(" ");
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
command: "ask",
|
|
91
|
+
modelId: resolveConsultModel(family, tier),
|
|
92
|
+
prompt,
|
|
93
|
+
persona,
|
|
94
|
+
journey,
|
|
95
|
+
org,
|
|
96
|
+
query,
|
|
97
|
+
mirrorHome,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import type { WritableDatabase } from "#db/database.ts";
|
|
2
|
+
import { logLlmCall } from "#observability/llmCalls.ts";
|
|
3
|
+
import { computeCost } from "#providers/cost.ts";
|
|
4
|
+
import type { CreditProvider } from "#providers/credits.ts";
|
|
5
|
+
import type { LlmMessage, LlmProvider, LlmRequest } from "#providers/llm.ts";
|
|
6
|
+
import { pythonJsonDumps } from "#util/pyGenerators.ts";
|
|
7
|
+
import type { ConsultAskCommand, ConsultCreditsCommand, ConsultParseResult } from "./args.ts";
|
|
8
|
+
import { renderConsultAsk, renderCredits } from "./render.ts";
|
|
9
|
+
|
|
10
|
+
export const SYSTEM_PREAMBLE = `You are the user's Mirror, as described in the context below. Answer in first person, as the user.
|
|
11
|
+
Respect the vocabulary, tone, and philosophy described in the identity context.
|
|
12
|
+
|
|
13
|
+
`;
|
|
14
|
+
|
|
15
|
+
export interface ConsultContextRequest {
|
|
16
|
+
persona: string | null;
|
|
17
|
+
journey: string | null;
|
|
18
|
+
org: boolean;
|
|
19
|
+
query: string | null;
|
|
20
|
+
mirrorHome: string | null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type ConsultContextLoader = (request: ConsultContextRequest) => Promise<string> | string;
|
|
24
|
+
|
|
25
|
+
export interface RunConsultOptions {
|
|
26
|
+
llm: LlmProvider;
|
|
27
|
+
credits: CreditProvider;
|
|
28
|
+
loadContext: ConsultContextLoader;
|
|
29
|
+
/** Optional: when provided, `ask` logs to the llm_calls ledger with the
|
|
30
|
+
* real fetched cost (AI-09). Omitted by existing/credits-only callers with
|
|
31
|
+
* no behavior change -- logging is simply skipped. */
|
|
32
|
+
db?: WritableDatabase;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export async function runConsult(
|
|
36
|
+
command: ConsultParseResult,
|
|
37
|
+
options: RunConsultOptions,
|
|
38
|
+
): Promise<string> {
|
|
39
|
+
if (command.command === "credits") {
|
|
40
|
+
return runConsultCredits(command, options);
|
|
41
|
+
}
|
|
42
|
+
return runConsultAsk(command, options);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export async function runConsultCredits(
|
|
46
|
+
_command: ConsultCreditsCommand,
|
|
47
|
+
options: RunConsultOptions,
|
|
48
|
+
): Promise<string> {
|
|
49
|
+
return renderCredits(await options.credits.getCredits());
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export async function runConsultAsk(
|
|
53
|
+
command: ConsultAskCommand,
|
|
54
|
+
options: RunConsultOptions,
|
|
55
|
+
): Promise<string> {
|
|
56
|
+
const context = await options.loadContext({
|
|
57
|
+
persona: command.persona,
|
|
58
|
+
journey: command.journey,
|
|
59
|
+
org: command.org,
|
|
60
|
+
query: command.query,
|
|
61
|
+
mirrorHome: command.mirrorHome,
|
|
62
|
+
});
|
|
63
|
+
const request = buildConsultLlmRequest(command.modelId, command.prompt, context);
|
|
64
|
+
const response = await options.llm.complete(request);
|
|
65
|
+
const cost = response.generationId
|
|
66
|
+
? await options.credits.fetchGenerationCost(response.generationId)
|
|
67
|
+
: null;
|
|
68
|
+
const credits = await options.credits.getCredits();
|
|
69
|
+
if (options.db) {
|
|
70
|
+
// Consult joins the ledger with its real fetched cost (AI-09). When the
|
|
71
|
+
// poll comes back empty, Python does NOT store null -- `build_llm_logger`
|
|
72
|
+
// falls through to `compute_cost`, so the row says "estimated" where this
|
|
73
|
+
// used to say "unknown". Most of consult's models are outside the price
|
|
74
|
+
// table and estimate to null anyway, but the two engines must mean the
|
|
75
|
+
// same thing by a null (CV22.DS8.US3 plan review, database-architect).
|
|
76
|
+
const model = response.model ?? command.modelId;
|
|
77
|
+
logLlmCall(options.db, {
|
|
78
|
+
role: "consult",
|
|
79
|
+
model,
|
|
80
|
+
prompt: request.prompt,
|
|
81
|
+
response: response.content,
|
|
82
|
+
promptTokens: response.promptTokens,
|
|
83
|
+
completionTokens: response.completionTokens,
|
|
84
|
+
latencyMs: response.latencyMs,
|
|
85
|
+
costUsd:
|
|
86
|
+
cost ??
|
|
87
|
+
computeCost(model, response.promptTokens ?? null, response.completionTokens ?? null),
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
return renderConsultAsk(command.modelId, response, credits, cost);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Python's `cmd_ask` envelope: a system message carrying the preamble and the
|
|
95
|
+
* Mirror context, then the user's question.
|
|
96
|
+
*
|
|
97
|
+
* `messages` is what leaves for the provider; `prompt` is the same envelope
|
|
98
|
+
* serialized exactly as Python serializes it (`json.dumps(messages,
|
|
99
|
+
* ensure_ascii=False)` inside `send_to_model`), because that string is what
|
|
100
|
+
* the replay transport resolves against and what the ledger's `prompt` column
|
|
101
|
+
* holds under `MEMORY_LOG_LLM_CALLS=full`. `JSON.stringify` was close enough
|
|
102
|
+
* to read but not to compare: it omits the space after `,` and `:` that
|
|
103
|
+
* Python writes, so the two engines recorded different bytes for the same
|
|
104
|
+
* call in a column both of them read (CV22.DS8.US3 plan review,
|
|
105
|
+
* database-architect).
|
|
106
|
+
*/
|
|
107
|
+
export function buildConsultLlmRequest(
|
|
108
|
+
modelId: string,
|
|
109
|
+
prompt: string,
|
|
110
|
+
context: string,
|
|
111
|
+
): LlmRequest {
|
|
112
|
+
const messages: LlmMessage[] = [
|
|
113
|
+
{ role: "system", content: SYSTEM_PREAMBLE + context },
|
|
114
|
+
{ role: "user", content: prompt },
|
|
115
|
+
];
|
|
116
|
+
return {
|
|
117
|
+
role: "consult",
|
|
118
|
+
model: modelId,
|
|
119
|
+
messages,
|
|
120
|
+
prompt: pythonJsonDumps(messages),
|
|
121
|
+
};
|
|
122
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export type ConsultTier = "lite" | "mid" | "flagship";
|
|
2
|
+
|
|
3
|
+
export const CONSULT_TIERS = ["lite", "mid", "flagship"] as const;
|
|
4
|
+
|
|
5
|
+
export const LLM_FAMILIES: Record<string, Record<ConsultTier, string>> = {
|
|
6
|
+
claude: {
|
|
7
|
+
lite: "anthropic/claude-haiku-4.5",
|
|
8
|
+
mid: "anthropic/claude-sonnet-4.6",
|
|
9
|
+
flagship: "anthropic/claude-opus-4.6",
|
|
10
|
+
},
|
|
11
|
+
deepseek: {
|
|
12
|
+
lite: "deepseek/deepseek-chat",
|
|
13
|
+
mid: "deepseek/deepseek-v3.2",
|
|
14
|
+
flagship: "deepseek/deepseek-r1",
|
|
15
|
+
},
|
|
16
|
+
gemini: {
|
|
17
|
+
lite: "google/gemini-2.5-flash-lite",
|
|
18
|
+
mid: "google/gemini-2.5-flash",
|
|
19
|
+
flagship: "google/gemini-2.5-pro",
|
|
20
|
+
},
|
|
21
|
+
grok: {
|
|
22
|
+
lite: "x-ai/grok-3-mini",
|
|
23
|
+
mid: "x-ai/grok-3",
|
|
24
|
+
flagship: "x-ai/grok-4.1-fast",
|
|
25
|
+
},
|
|
26
|
+
llama: {
|
|
27
|
+
lite: "meta-llama/llama-3.3-70b-instruct",
|
|
28
|
+
mid: "meta-llama/llama-4-scout",
|
|
29
|
+
flagship: "meta-llama/llama-4-maverick",
|
|
30
|
+
},
|
|
31
|
+
openai: {
|
|
32
|
+
lite: "openai/gpt-5.4-nano",
|
|
33
|
+
mid: "openai/gpt-5.4-mini",
|
|
34
|
+
flagship: "openai/gpt-5.4",
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export function isConsultTier(value: string): value is ConsultTier {
|
|
39
|
+
return value === "lite" || value === "mid" || value === "flagship";
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function resolveConsultModel(family: string, tier = "mid"): string {
|
|
43
|
+
if (family.includes("/")) return family;
|
|
44
|
+
const familyLower = family.toLowerCase();
|
|
45
|
+
const tiers = LLM_FAMILIES[familyLower];
|
|
46
|
+
if (!tiers) {
|
|
47
|
+
const available = Object.keys(LLM_FAMILIES).sort().join(", ");
|
|
48
|
+
throw new Error(`Family '${family}' not found. Available: ${available}`);
|
|
49
|
+
}
|
|
50
|
+
const tierLower = tier.toLowerCase();
|
|
51
|
+
if (!isConsultTier(tierLower)) {
|
|
52
|
+
throw new Error(`Tier '${tier}' does not exist for '${family}'. Use: lite, mid, flagship`);
|
|
53
|
+
}
|
|
54
|
+
return tiers[tierLower];
|
|
55
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { LlmResponse } from "#providers/llm.ts";
|
|
2
|
+
|
|
3
|
+
export const USD_TO_BRL = 5.7;
|
|
4
|
+
export const BALANCE_BAR_WIDTH = 20;
|
|
5
|
+
|
|
6
|
+
export interface CreditInfo {
|
|
7
|
+
totalCredits: number;
|
|
8
|
+
totalUsage: number;
|
|
9
|
+
balance: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function renderCredits(info: CreditInfo): string {
|
|
13
|
+
const balanceBrl = info.balance * USD_TO_BRL;
|
|
14
|
+
const fill =
|
|
15
|
+
info.totalCredits > 0 ? Math.trunc((BALANCE_BAR_WIDTH * info.balance) / info.totalCredits) : 0;
|
|
16
|
+
const bar = "▓".repeat(fill) + "░".repeat(BALANCE_BAR_WIDTH - fill);
|
|
17
|
+
return `Balance: openrouter: ${bar} R$ ${balanceBrl.toFixed(2)}`;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function renderConsultAsk(
|
|
21
|
+
requestedModel: string,
|
|
22
|
+
response: LlmResponse,
|
|
23
|
+
credits: CreditInfo,
|
|
24
|
+
cost: number | null = null,
|
|
25
|
+
): string {
|
|
26
|
+
const lines = [
|
|
27
|
+
`Consulting ${requestedModel}...`,
|
|
28
|
+
`--- response via ${response.model ?? requestedModel} ---`,
|
|
29
|
+
response.content,
|
|
30
|
+
"--- end ---",
|
|
31
|
+
];
|
|
32
|
+
const costParts: string[] = [];
|
|
33
|
+
if (response.promptTokens) costParts.push(`prompt: ${response.promptTokens}`);
|
|
34
|
+
if (response.completionTokens) costParts.push(`completion: ${response.completionTokens}`);
|
|
35
|
+
if (costParts.length > 0) lines.push(`[${costParts.join(", ")}]`);
|
|
36
|
+
if (cost !== null) lines.push(renderCost(cost));
|
|
37
|
+
lines.push(renderCredits(credits));
|
|
38
|
+
return lines.join("\n");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function renderCost(totalCost: number): string {
|
|
42
|
+
const costBrl = totalCost * USD_TO_BRL;
|
|
43
|
+
if (totalCost < 0.01) {
|
|
44
|
+
return `Call cost: $${totalCost.toFixed(6)} (R$ ${costBrl.toFixed(4)})`;
|
|
45
|
+
}
|
|
46
|
+
return `Call cost: $${totalCost.toFixed(4)} (R$ ${costBrl.toFixed(2)})`;
|
|
47
|
+
}
|