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,176 @@
|
|
|
1
|
+
// The front door's own answer for a name it does not know (CV22.DS10.TS5, D2).
|
|
2
|
+
//
|
|
3
|
+
// Until TS5 every such name fell through to Python: an unknown command reached
|
|
4
|
+
// `_dispatch`'s `Unknown command` and its usage block, an unknown subcommand
|
|
5
|
+
// reached argparse, which names the program `__main__.py`. With the fallback
|
|
6
|
+
// gone nobody would own the answer, and an unowned answer is how a surface
|
|
7
|
+
// disappears without anyone deciding to remove it. So TypeScript owns it, in
|
|
8
|
+
// the shapes D2 records:
|
|
9
|
+
//
|
|
10
|
+
// * top level -- `Unknown command: <name>`, a blank line, and the usage
|
|
11
|
+
// block, all on stdout, exit 1: `_dispatch`'s shape, the one scripts saw.
|
|
12
|
+
// No command at all prints the usage block alone, exit 1.
|
|
13
|
+
// * a family -- argparse's shape: one `usage:` line and one `error:` line on
|
|
14
|
+
// stderr, exit 2, with the FAMILY as the program name instead of
|
|
15
|
+
// `__main__.py`. US2 gave `runtime` exactly this shape first; this module
|
|
16
|
+
// generalizes it, byte-for-byte.
|
|
17
|
+
// * `-h` / `--help` -- the usage on stdout, exit 0, at either level (the
|
|
18
|
+
// plateau-2 amendment to D2): help must not become an error just because
|
|
19
|
+
// the program that used to print it is gone.
|
|
20
|
+
//
|
|
21
|
+
// Seven families answered an unknown subcommand differently in the oracle
|
|
22
|
+
// (inventory F6): a usage line on stdout with exit 1, `Unknown subcommand` with
|
|
23
|
+
// exit 1, or -- `conversation-logger` -- nothing at all with exit 0. They move
|
|
24
|
+
// to the uniform shape deliberately; the table is in the story's inventory.
|
|
25
|
+
|
|
26
|
+
import { PROGRAM } from "#util/program.ts";
|
|
27
|
+
|
|
28
|
+
/** The program name the top-level usage prints: `#util/program.ts`, the one place US3 renames. */
|
|
29
|
+
export { PROGRAM };
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Every top-level command the front door answers, in the oracle's order, with a
|
|
33
|
+
* one-line summary. The unknown-command answer prints this; a test proves every
|
|
34
|
+
* name here routes somewhere other than the unknown-command answer.
|
|
35
|
+
*/
|
|
36
|
+
export const FRONT_DOOR_COMMANDS: ReadonlyArray<readonly [name: string, summary: string]> = [
|
|
37
|
+
["init", "Initialize a user home from identity templates"],
|
|
38
|
+
["seed", "Seed identity YAML files into the database"],
|
|
39
|
+
["identity", "Read and update identity in the database"],
|
|
40
|
+
["list", "List personas, journeys, extensions, or all"],
|
|
41
|
+
["inspect", "Inspect a persona, an extension, the runtime catalog, or the call ledgers"],
|
|
42
|
+
["detect-persona", "Show persona routing matches for a query"],
|
|
43
|
+
["extensions", "List, validate, sync, install, or uninstall extensions"],
|
|
44
|
+
["ext", "Run a command-skill extension's CLI"],
|
|
45
|
+
["mirror", "Mirror Mode: load context, log, deactivate"],
|
|
46
|
+
["mode", "Operating mode lifecycle"],
|
|
47
|
+
["conversation-logger", "Conversation logging"],
|
|
48
|
+
["backup", "Create a zipped backup of the memory database"],
|
|
49
|
+
["repair-encoding", "Repair reversible UTF-8 mojibake in user text"],
|
|
50
|
+
["journal", "Record a journal entry"],
|
|
51
|
+
["journey", "Inspect or update a journey"],
|
|
52
|
+
["build", "Builder Mode and the Ariad lifecycle"],
|
|
53
|
+
["explore", "Explorer Mode"],
|
|
54
|
+
["soul", "Soul Mode"],
|
|
55
|
+
["memories", "List or search memories"],
|
|
56
|
+
["conversations", "List conversations, or append a message batch"],
|
|
57
|
+
["recall", "Load messages from a previous conversation"],
|
|
58
|
+
["tasks", "Task management"],
|
|
59
|
+
["week", "Weekly planning"],
|
|
60
|
+
["journeys", "List journeys with status and stage"],
|
|
61
|
+
["consult", "Ask other LLMs through OpenRouter"],
|
|
62
|
+
["descriptor", "Generate and list routing descriptors"],
|
|
63
|
+
["consolidate", "Scan memories for patterns and manage consolidation proposals"],
|
|
64
|
+
["shadow", "Surface and promote shadow-layer observations"],
|
|
65
|
+
["mcp", "Run the Mirror MCP server over stdio"],
|
|
66
|
+
["runtime", "Runtime status, drift, backups, version, and updates"],
|
|
67
|
+
["welcome", "Render the welcome card"],
|
|
68
|
+
];
|
|
69
|
+
|
|
70
|
+
const NAME_WIDTH = Math.max(...FRONT_DOOR_COMMANDS.map(([name]) => name.length)) + 2;
|
|
71
|
+
|
|
72
|
+
/** The top-level usage block. */
|
|
73
|
+
export const USAGE = [
|
|
74
|
+
`Usage: ${PROGRAM} <command> [args]`,
|
|
75
|
+
"",
|
|
76
|
+
"Commands:",
|
|
77
|
+
...FRONT_DOOR_COMMANDS.map(([name, summary]) => ` ${name.padEnd(NAME_WIDTH)}${summary}`),
|
|
78
|
+
"",
|
|
79
|
+
].join("\n");
|
|
80
|
+
|
|
81
|
+
/** What routing hands the front door when a name is not one it answers. */
|
|
82
|
+
export type UsageRequest =
|
|
83
|
+
| {
|
|
84
|
+
readonly scope: "top-level";
|
|
85
|
+
/** The unknown name, or null when no command was given at all. */
|
|
86
|
+
readonly given: string | null;
|
|
87
|
+
}
|
|
88
|
+
| {
|
|
89
|
+
readonly scope: "family";
|
|
90
|
+
/** The family as a user types it: `build`, `explore story`. */
|
|
91
|
+
readonly program: string;
|
|
92
|
+
/** What the family does answer, from routing's own allowlist. */
|
|
93
|
+
readonly choices: readonly string[];
|
|
94
|
+
/** The unrecognized subcommand, or "" when none was given. */
|
|
95
|
+
readonly given: string;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
export interface UsageAnswer {
|
|
99
|
+
readonly stream: "stdout" | "stderr";
|
|
100
|
+
readonly exitCode: number;
|
|
101
|
+
readonly text: string;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const HELP = new Set(["-h", "--help"]);
|
|
105
|
+
|
|
106
|
+
/** One family's `usage:` line. */
|
|
107
|
+
export function familyUsageLine(program: string, choices: readonly string[]): string {
|
|
108
|
+
return `usage: ${program} [-h] {${choices.join(",")}} ...\n`;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* argparse's refusal, in the product's vocabulary: the usage line, then one
|
|
113
|
+
* `error:` line naming what was wrong.
|
|
114
|
+
*/
|
|
115
|
+
export function renderUnknownSubcommand(
|
|
116
|
+
program: string,
|
|
117
|
+
choices: readonly string[],
|
|
118
|
+
given: string,
|
|
119
|
+
): string {
|
|
120
|
+
const detail =
|
|
121
|
+
given === ""
|
|
122
|
+
? "the following arguments are required: command"
|
|
123
|
+
: `argument command: invalid choice: '${given}' (choose from ${choices.join(", ")})`;
|
|
124
|
+
return `${familyUsageLine(program, choices)}${program}: error: ${detail}\n`;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* What the front door knows about the machine when it answers bare `mirror`:
|
|
129
|
+
* whether any source configured a user, and where the config file would be.
|
|
130
|
+
*/
|
|
131
|
+
export interface UsageContext {
|
|
132
|
+
readonly userConfigured: boolean;
|
|
133
|
+
readonly configFile: string;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* The first seconds after `npm install -g mirror-mind` (CV22.DS10.US3): bare
|
|
138
|
+
* `mirror` on a machine with no user configured is an orientation, not a
|
|
139
|
+
* usage dump -- three lines that say what is missing, what to run, and where
|
|
140
|
+
* the configuration will live. With a user configured, bare `mirror` is the
|
|
141
|
+
* usage block, as before.
|
|
142
|
+
*/
|
|
143
|
+
export function renderOrientation(configFile: string): string {
|
|
144
|
+
return [
|
|
145
|
+
"No user configured yet.",
|
|
146
|
+
` Run: ${PROGRAM} init <your-name>`,
|
|
147
|
+
` Configuration will live at: ${configFile}`,
|
|
148
|
+
"",
|
|
149
|
+
].join("\n");
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** Render the answer for a request routing could not place. */
|
|
153
|
+
export function answerUsage(request: UsageRequest, context?: UsageContext): UsageAnswer {
|
|
154
|
+
if (request.scope === "top-level") {
|
|
155
|
+
if (request.given !== null && HELP.has(request.given)) {
|
|
156
|
+
return { stream: "stdout", exitCode: 0, text: USAGE };
|
|
157
|
+
}
|
|
158
|
+
if (request.given === null && context !== undefined && !context.userConfigured) {
|
|
159
|
+
return { stream: "stdout", exitCode: 1, text: renderOrientation(context.configFile) };
|
|
160
|
+
}
|
|
161
|
+
const unknown = request.given === null ? "" : `Unknown command: ${request.given}\n\n`;
|
|
162
|
+
return { stream: "stdout", exitCode: 1, text: `${unknown}${USAGE}` };
|
|
163
|
+
}
|
|
164
|
+
if (HELP.has(request.given)) {
|
|
165
|
+
return {
|
|
166
|
+
stream: "stdout",
|
|
167
|
+
exitCode: 0,
|
|
168
|
+
text: familyUsageLine(request.program, request.choices),
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
return {
|
|
172
|
+
stream: "stderr",
|
|
173
|
+
exitCode: 2,
|
|
174
|
+
text: renderUnknownSubcommand(request.program, request.choices, request.given),
|
|
175
|
+
};
|
|
176
|
+
}
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
// Generate the canonical Mirror Mind Claude plugin (manifest + skills), and the
|
|
2
|
+
// Claude Code copies of Pi-sourced skills.
|
|
3
|
+
//
|
|
4
|
+
// The Node port of `src/memory/plugins/claude.py` and its wrapper
|
|
5
|
+
// `scripts/build_claude_plugin.py` (CV22.DS10.TS5, slice B).
|
|
6
|
+
//
|
|
7
|
+
// The Zero Python gate assigns the builder to **US3**, on the grounds that US3
|
|
8
|
+
// decides what it builds. But it is a `.py` file, and TS5's done condition is
|
|
9
|
+
// that `git ls-files '*.py'` is empty -- so the file cannot survive this story
|
|
10
|
+
// whoever owns its content. Decision D6 resolves that: the port lands here,
|
|
11
|
+
// byte-identical in output; US3 still owns the shape of what it emits when the
|
|
12
|
+
// npm entry point becomes real.
|
|
13
|
+
//
|
|
14
|
+
// The plugin skills are generated from `.claude/skills/` (the Claude-tuned
|
|
15
|
+
// source), with one exception since CR102. A Pi-sourced skill
|
|
16
|
+
// (`piSourcedSkills.ts`) has one body in every runtime, so this module also
|
|
17
|
+
// generates its `.claude/skills/` copy from `.pi/skills/`, and takes its plugin
|
|
18
|
+
// copy from that generated content rather than from the file on disk. Hooks
|
|
19
|
+
// under `plugins/mirror-mind/hooks/` are hand-authored plugin source and are
|
|
20
|
+
// NOT managed by this module -- which is precisely why TS5's inventory had to
|
|
21
|
+
// find their interpreter spawns by reading them.
|
|
22
|
+
|
|
23
|
+
import {
|
|
24
|
+
existsSync,
|
|
25
|
+
mkdirSync,
|
|
26
|
+
readdirSync,
|
|
27
|
+
readFileSync,
|
|
28
|
+
rmdirSync,
|
|
29
|
+
unlinkSync,
|
|
30
|
+
writeFileSync,
|
|
31
|
+
} from "node:fs";
|
|
32
|
+
import { dirname, join, relative } from "node:path";
|
|
33
|
+
|
|
34
|
+
import { packageVersion } from "#runtime/version.ts";
|
|
35
|
+
import { claudeVariant, PI_SKILLS_DIR, PI_SOURCED_SKILLS } from "./piSourcedSkills.ts";
|
|
36
|
+
|
|
37
|
+
export const PLUGIN_NAME = "mirror-mind";
|
|
38
|
+
export const PLUGIN_DESCRIPTION =
|
|
39
|
+
"Mirror Mind — local-first memory and identity for agentic runtimes.";
|
|
40
|
+
export const PLUGIN_AUTHOR = "Mirror Mind";
|
|
41
|
+
|
|
42
|
+
export const SKILLS_SOURCE_DIR = ".claude/skills";
|
|
43
|
+
export const PLUGIN_DIR = "plugins/mirror-mind";
|
|
44
|
+
export const SKILL_FILENAME = "SKILL.md";
|
|
45
|
+
|
|
46
|
+
/** A file the generator owns, addressed relative to the repo root. */
|
|
47
|
+
export interface GeneratedFile {
|
|
48
|
+
readonly relativePath: string;
|
|
49
|
+
readonly content: string;
|
|
50
|
+
/** The authored file it derives from, when a person could mistake it for one. */
|
|
51
|
+
readonly source?: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* The project version, read from source.
|
|
56
|
+
*
|
|
57
|
+
* Read from source rather than from installed metadata, so the build is
|
|
58
|
+
* deterministic. `packageVersion` is the single body that answers this for the
|
|
59
|
+
* whole core (US2 decision D2); CV22.DS10.TS5 re-pointed it at
|
|
60
|
+
* `package.json` and this builder followed with no edit, which is the point
|
|
61
|
+
* of there being one body.
|
|
62
|
+
*/
|
|
63
|
+
export function readVersion(repoRoot: string): string {
|
|
64
|
+
const version = packageVersion(repoRoot);
|
|
65
|
+
if (!version) throw new Error(`version not found from ${repoRoot}`);
|
|
66
|
+
return version;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* The directories the MCP entry and the plugin's hook wrappers search for
|
|
71
|
+
* `mirror` and `node` when the PATH does not hold them: a GUI launch leaves
|
|
72
|
+
* the npm global bin off it. One list, so `mcpEntry.test.ts` can hold the
|
|
73
|
+
* entry's search equal to the wrappers' (`generate_hook_wrappers.sh`).
|
|
74
|
+
*/
|
|
75
|
+
export const GLOBAL_BIN_DIRS = ['"$HOME/.nvm/current/bin"', "/opt/homebrew/bin", "/usr/local/bin"];
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* The MCP entry's locator (CV21.DS2.US6).
|
|
79
|
+
*
|
|
80
|
+
* The manifest used to name `${CLAUDE_PLUGIN_ROOT}/mcp/launch.sh`. Claude Code
|
|
81
|
+
* expands that variable; Codex does not expand it in an MCP command (DS12.TS2
|
|
82
|
+
* L6), so the server never started there. The locator is the launcher's
|
|
83
|
+
* search, inline: `MIRROR_BIN`, the PATH, then the global bin directories for
|
|
84
|
+
* `mirror`; `MIRROR_NODE`, the PATH, the directory `mirror` was found in, then
|
|
85
|
+
* the same directories for `node`. A client whose PATH holds neither (a GUI
|
|
86
|
+
* launch) still starts the server, and one with no `mirror` anywhere gets one
|
|
87
|
+
* stderr line naming the install step, and exit 1.
|
|
88
|
+
*
|
|
89
|
+
* Written for what three runtimes do to the string (the US6 spike):
|
|
90
|
+
*
|
|
91
|
+
* * POSIX `sh`: `/bin/sh` is dash on Linux;
|
|
92
|
+
* * no `${...}`: Claude Code expands that form in MCP configuration, and a
|
|
93
|
+
* runtime that did so with an unset name would blank it;
|
|
94
|
+
* * no positional parameter (`$1`, `$@`, ...) and no backtick, for the same
|
|
95
|
+
* reason, and because the text travels through two JSON encoders;
|
|
96
|
+
* * variables named `mmbin`, `mmnode`, `mmdir`: Grok substitutes SET
|
|
97
|
+
* environment variables by name inside MCP args at load time (`$HOME`
|
|
98
|
+
* became the path), so a script variable with a name a person exports
|
|
99
|
+
* would be replaced before the shell ever saw it.
|
|
100
|
+
*/
|
|
101
|
+
export const MCP_LOCATOR = [
|
|
102
|
+
'mmbin=""',
|
|
103
|
+
'[ -n "$MIRROR_BIN" ] && [ -x "$MIRROR_BIN/mirror" ] && mmbin="$MIRROR_BIN/mirror"',
|
|
104
|
+
'[ -n "$mmbin" ] || mmbin="$(command -v mirror 2>/dev/null)"',
|
|
105
|
+
`for mmdir in ${GLOBAL_BIN_DIRS.join(" ")}; do [ -n "$mmbin" ] && break; [ -x "$mmdir/mirror" ] && mmbin="$mmdir/mirror"; done`,
|
|
106
|
+
'[ -n "$mmbin" ] || { echo "mirror-mind: mirror not found on PATH; the MCP server cannot start. Install mirror-mind (npm install -g mirror-mind) or set MIRROR_BIN to its bin directory." >&2; exit 1; }',
|
|
107
|
+
'mmnode="$MIRROR_NODE"',
|
|
108
|
+
'[ -n "$mmnode" ] && [ -x "$mmnode" ] || mmnode="$(command -v node 2>/dev/null)"',
|
|
109
|
+
`for mmdir in "$(dirname "$mmbin")" ${GLOBAL_BIN_DIRS.join(" ")}; do [ -n "$mmnode" ] && break; [ -x "$mmdir/node" ] && mmnode="$mmdir/node"; done`,
|
|
110
|
+
'[ -n "$mmnode" ] || { echo "mirror-mind: node not found on PATH; the MCP server cannot start. Set MIRROR_NODE." >&2; exit 1; }',
|
|
111
|
+
'exec "$mmnode" "$mmbin" mcp',
|
|
112
|
+
].join("; ");
|
|
113
|
+
|
|
114
|
+
/** The `mcpServers` entry: the locator, run by the system shell. */
|
|
115
|
+
export const MCP_ENTRY: { readonly command: string; readonly args: readonly string[] } = {
|
|
116
|
+
command: "/bin/sh",
|
|
117
|
+
args: ["-c", MCP_LOCATOR],
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* The Claude plugin manifest (minimal; no unsupported keys).
|
|
122
|
+
*
|
|
123
|
+
* Declares the Mirror MCP server so the canonical package carries it. The
|
|
124
|
+
* entry names no file of the plugin's and no variable a runtime must expand
|
|
125
|
+
* (`MCP_LOCATOR`): Claude Code, Codex, and Grok render it as written, and
|
|
126
|
+
* each starts the server from it (CV21.DS2.US6).
|
|
127
|
+
*/
|
|
128
|
+
export function buildManifest(version: string): Record<string, unknown> {
|
|
129
|
+
return {
|
|
130
|
+
name: PLUGIN_NAME,
|
|
131
|
+
version,
|
|
132
|
+
description: PLUGIN_DESCRIPTION,
|
|
133
|
+
author: { name: PLUGIN_AUTHOR },
|
|
134
|
+
mcpServers: {
|
|
135
|
+
[PLUGIN_NAME]: { command: MCP_ENTRY.command, args: [...MCP_ENTRY.args] },
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** The manifest as deterministic JSON text, byte-identical to Python's. */
|
|
141
|
+
export function manifestJson(version: string): string {
|
|
142
|
+
return `${JSON.stringify(buildManifest(version), null, 2)}\n`;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* The skill markdown file, matching `skill.md` case-insensitively.
|
|
147
|
+
*
|
|
148
|
+
* Case-insensitive makes the generator robust on both case-sensitive and
|
|
149
|
+
* case-insensitive filesystems, including older checkouts that may still
|
|
150
|
+
* contain lowercase `skill.md`.
|
|
151
|
+
*/
|
|
152
|
+
function findSkillMarkdown(skillDir: string): string | null {
|
|
153
|
+
for (const entry of readdirSync(skillDir, { withFileTypes: true, encoding: "utf8" }).sort(
|
|
154
|
+
(a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0),
|
|
155
|
+
)) {
|
|
156
|
+
if (entry.isFile() && entry.name.toLowerCase() === SKILL_FILENAME.toLowerCase()) {
|
|
157
|
+
return join(skillDir, entry.name);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* `[skillDirName, markdownPath]` for each skill under `.claude/skills/`. Most
|
|
165
|
+
* are authored there. A Pi-sourced skill's file is generated instead
|
|
166
|
+
* (`piSourcedSkills.ts`), and `planGeneratedFiles` takes that one from its Pi
|
|
167
|
+
* copy, never from this path.
|
|
168
|
+
*/
|
|
169
|
+
export function discoverSkillSources(repoRoot: string): [string, string][] {
|
|
170
|
+
const base = join(repoRoot, SKILLS_SOURCE_DIR);
|
|
171
|
+
const sources: [string, string][] = [];
|
|
172
|
+
const entries = readdirSync(base, { withFileTypes: true, encoding: "utf8" }).sort((a, b) =>
|
|
173
|
+
a.name < b.name ? -1 : a.name > b.name ? 1 : 0,
|
|
174
|
+
);
|
|
175
|
+
for (const child of entries) {
|
|
176
|
+
if (!child.isDirectory()) continue;
|
|
177
|
+
const markdown = findSkillMarkdown(join(base, child.name));
|
|
178
|
+
if (markdown === null) continue;
|
|
179
|
+
sources.push([child.name, markdown]);
|
|
180
|
+
}
|
|
181
|
+
return sources;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* The `.claude/skills/` copy of each Pi-sourced skill, generated from its Pi
|
|
186
|
+
* copy. Refuses, before anything is written, when a listed skill has no Pi
|
|
187
|
+
* copy: generation would otherwise stop without a word, and the old Claude copy
|
|
188
|
+
* would pass for an authored one again.
|
|
189
|
+
*/
|
|
190
|
+
function planPiSourcedClaudeCopies(repoRoot: string): Map<string, GeneratedFile> {
|
|
191
|
+
const copies = new Map<string, GeneratedFile>();
|
|
192
|
+
for (const skill of PI_SOURCED_SKILLS) {
|
|
193
|
+
const source = join(PI_SKILLS_DIR, skill, SKILL_FILENAME);
|
|
194
|
+
const path = join(repoRoot, source);
|
|
195
|
+
if (!existsSync(path)) {
|
|
196
|
+
throw new Error(
|
|
197
|
+
`${source}: missing, but ${skill} is listed as Pi-sourced in ts/src/guards/piSourcedSkills.ts`,
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
copies.set(skill, {
|
|
201
|
+
relativePath: join(SKILLS_SOURCE_DIR, skill, SKILL_FILENAME),
|
|
202
|
+
content: claudeVariant(skill, readFileSync(path, "utf8"), source),
|
|
203
|
+
source,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
return copies;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/** Every file the generator owns, without touching the filesystem. */
|
|
210
|
+
export function planGeneratedFiles(repoRoot: string): GeneratedFile[] {
|
|
211
|
+
const version = readVersion(repoRoot);
|
|
212
|
+
const claudeCopies = planPiSourcedClaudeCopies(repoRoot);
|
|
213
|
+
|
|
214
|
+
const pluginSkills = new Map<string, GeneratedFile>();
|
|
215
|
+
for (const [name, markdown] of discoverSkillSources(repoRoot)) {
|
|
216
|
+
if (claudeCopies.has(name)) continue;
|
|
217
|
+
pluginSkills.set(name, {
|
|
218
|
+
relativePath: join(PLUGIN_DIR, "skills", name, SKILL_FILENAME),
|
|
219
|
+
content: readFileSync(markdown, "utf8"),
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
for (const [name, claudeCopy] of claudeCopies) {
|
|
223
|
+
pluginSkills.set(name, {
|
|
224
|
+
relativePath: join(PLUGIN_DIR, "skills", name, SKILL_FILENAME),
|
|
225
|
+
content: claudeCopy.content,
|
|
226
|
+
source: claudeCopy.source,
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return [
|
|
231
|
+
{
|
|
232
|
+
relativePath: join(PLUGIN_DIR, ".claude-plugin", "plugin.json"),
|
|
233
|
+
content: manifestJson(version),
|
|
234
|
+
},
|
|
235
|
+
...claudeCopies.values(),
|
|
236
|
+
...[...pluginSkills].sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0)).map(([, file]) => file),
|
|
237
|
+
];
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/** One drift line; a derived file also names the source a person should edit. */
|
|
241
|
+
function driftLine(kind: "missing" | "out of date", file: GeneratedFile): string {
|
|
242
|
+
const from = file.source
|
|
243
|
+
? ` (generated from ${file.source}: edit the source, then regenerate)`
|
|
244
|
+
: "";
|
|
245
|
+
return `${kind}: ${file.relativePath}${from}`;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function generatedSkillFiles(repoRoot: string): string[] {
|
|
249
|
+
const skillsRoot = join(repoRoot, PLUGIN_DIR, "skills");
|
|
250
|
+
if (!existsSync(skillsRoot)) return [];
|
|
251
|
+
const found: string[] = [];
|
|
252
|
+
for (const entry of readdirSync(skillsRoot, { withFileTypes: true, encoding: "utf8" })) {
|
|
253
|
+
if (!entry.isDirectory()) continue;
|
|
254
|
+
const candidate = join(skillsRoot, entry.name, SKILL_FILENAME);
|
|
255
|
+
if (existsSync(candidate)) found.push(candidate);
|
|
256
|
+
}
|
|
257
|
+
return found.sort();
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Write every generated file, the plugin and the `.claude/skills/` copies of
|
|
262
|
+
* Pi-sourced skills, or report drift when `write` is false.
|
|
263
|
+
*
|
|
264
|
+
* Returns human-readable drift descriptions. In check mode the list is the
|
|
265
|
+
* assertion target (empty == in sync). In write mode it is empty and the files
|
|
266
|
+
* are written; stale generated skills are removed.
|
|
267
|
+
*/
|
|
268
|
+
export function materialize(repoRoot: string, options: { write: boolean }): string[] {
|
|
269
|
+
const desired = planGeneratedFiles(repoRoot);
|
|
270
|
+
const desiredPaths = new Set(desired.map((file) => file.relativePath));
|
|
271
|
+
const problems: string[] = [];
|
|
272
|
+
|
|
273
|
+
for (const generated of desired) {
|
|
274
|
+
const target = join(repoRoot, generated.relativePath);
|
|
275
|
+
if (options.write) {
|
|
276
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
277
|
+
writeFileSync(target, generated.content, "utf8");
|
|
278
|
+
} else if (!existsSync(target)) {
|
|
279
|
+
problems.push(driftLine("missing", generated));
|
|
280
|
+
} else if (readFileSync(target, "utf8") !== generated.content) {
|
|
281
|
+
problems.push(driftLine("out of date", generated));
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
for (const existing of generatedSkillFiles(repoRoot)) {
|
|
286
|
+
const rel = relative(repoRoot, existing);
|
|
287
|
+
if (desiredPaths.has(rel)) continue;
|
|
288
|
+
if (options.write) {
|
|
289
|
+
const parent = dirname(existing);
|
|
290
|
+
for (const leftover of readdirSync(parent).sort()) unlinkSync(join(parent, leftover));
|
|
291
|
+
rmdirSync(parent);
|
|
292
|
+
} else {
|
|
293
|
+
problems.push(`stale: ${rel}`);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
return problems;
|
|
298
|
+
}
|