@mindrian_os/install 1.13.0-beta.11
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-plugin/plugin.json +21 -0
- package/.mcp.json +9 -0
- package/CHANGELOG.md +3333 -0
- package/LICENSE +123 -0
- package/README.md +673 -0
- package/agents/brain-query.md +80 -0
- package/agents/framework-runner.md +237 -0
- package/agents/grading.md +188 -0
- package/agents/investor.md +128 -0
- package/agents/larry-extended.md +135 -0
- package/agents/opportunity-scanner.md +91 -0
- package/agents/persona-analyst.md +132 -0
- package/agents/research.md +89 -0
- package/agents/reverse-salient-agent.md +27 -0
- package/bin/cli.js +142 -0
- package/bin/mindrian-mcp-server.cjs +182 -0
- package/bin/mindrian-tools.cjs +765 -0
- package/commands/act.md +439 -0
- package/commands/admin.md +404 -0
- package/commands/analyze-needs.md +42 -0
- package/commands/analyze-systems.md +39 -0
- package/commands/analyze-timing.md +42 -0
- package/commands/auto-explore.md +64 -0
- package/commands/beautiful-question.md +40 -0
- package/commands/brain-derive.md +78 -0
- package/commands/build-knowledge.md +42 -0
- package/commands/build-thesis.md +46 -0
- package/commands/causal.md +234 -0
- package/commands/challenge-assumptions.md +33 -0
- package/commands/compare-ventures.md +83 -0
- package/commands/dashboard.md +110 -0
- package/commands/deep-grade.md +82 -0
- package/commands/diagnose.md +58 -0
- package/commands/diagnostics.md +151 -0
- package/commands/doctor.md +151 -0
- package/commands/dominant-designs.md +40 -0
- package/commands/explain-decision.md +87 -0
- package/commands/explore-domains.md +42 -0
- package/commands/explore-futures.md +40 -0
- package/commands/explore-trends.md +42 -0
- package/commands/export.md +103 -0
- package/commands/file-meeting.md +724 -0
- package/commands/find-analogies.md +188 -0
- package/commands/find-bottlenecks.md +62 -0
- package/commands/find-connections.md +76 -0
- package/commands/funding.md +81 -0
- package/commands/grade.md +203 -0
- package/commands/graph.md +128 -0
- package/commands/hat-briefing.md +125 -0
- package/commands/heal.md +196 -0
- package/commands/help.md +399 -0
- package/commands/hmi-status.md +172 -0
- package/commands/jtbd.md +241 -0
- package/commands/leadership.md +73 -0
- package/commands/lean-canvas.md +40 -0
- package/commands/macro-trends.md +40 -0
- package/commands/map-unknowns.md +40 -0
- package/commands/memory.md +173 -0
- package/commands/models.md +175 -0
- package/commands/mos-reason.md +285 -0
- package/commands/mullins.md +120 -0
- package/commands/new-project.md +481 -0
- package/commands/onboard.md +434 -0
- package/commands/operator.md +149 -0
- package/commands/opportunities.md +144 -0
- package/commands/organize.md +497 -0
- package/commands/persona.md +198 -0
- package/commands/pipeline.md +112 -0
- package/commands/present.md +91 -0
- package/commands/publish.md +201 -0
- package/commands/query.md +124 -0
- package/commands/radar.md +72 -0
- package/commands/reanalyze.md +91 -0
- package/commands/research.md +196 -0
- package/commands/room.md +352 -0
- package/commands/rooms.md +598 -0
- package/commands/root-cause.md +40 -0
- package/commands/rs-experts.md +85 -0
- package/commands/rs-explain.md +100 -0
- package/commands/rs-fetch.md +94 -0
- package/commands/rs-thesis.md +85 -0
- package/commands/scenario-plan.md +40 -0
- package/commands/scheduled-tasks.md +285 -0
- package/commands/score-innovation.md +43 -0
- package/commands/scout.md +239 -0
- package/commands/setup.md +618 -0
- package/commands/snapshot.md +147 -0
- package/commands/speakers.md +84 -0
- package/commands/splash.md +28 -0
- package/commands/status.md +75 -0
- package/commands/structure-argument.md +42 -0
- package/commands/suggest-next.md +80 -0
- package/commands/systems-thinking.md +40 -0
- package/commands/think-hats.md +42 -0
- package/commands/update.md +181 -0
- package/commands/user-needs.md +40 -0
- package/commands/validate.md +40 -0
- package/commands/value-proposition.md +61 -0
- package/commands/vault.md +180 -0
- package/commands/visualize.md +52 -0
- package/commands/whitespace.md +507 -0
- package/commands/wiki.md +69 -0
- package/hooks/hooks.json +381 -0
- package/hooks/run-hook.cmd +64 -0
- package/lib/__init__.py +0 -0
- package/lib/__pycache__/__init__.cpython-312.pyc +0 -0
- package/lib/agents/auto-explore-agent.cjs +1043 -0
- package/lib/agents/reverse-salient-agent.cjs +679 -0
- package/lib/agents/tension-hook-agent.cjs +544 -0
- package/lib/brain/ROOM.md +44 -0
- package/lib/brain/chain-recommender.cjs +301 -0
- package/lib/chat/chat-context.js +185 -0
- package/lib/chat/chat-panel.js +721 -0
- package/lib/chat/fabric-chat.cjs +288 -0
- package/lib/chat/generative-tools.js +219 -0
- package/lib/conversation/ROOM.md +39 -0
- package/lib/conversation/classifier-rules.json +38 -0
- package/lib/conversation/classifier.cjs +264 -0
- package/lib/conversation/operator.cjs +287 -0
- package/lib/copy/115-spec-strings.cjs +55 -0
- package/lib/core/__init__.py +0 -0
- package/lib/core/__nav-stub.cjs +14 -0
- package/lib/core/__pycache__/__init__.cpython-312.pyc +0 -0
- package/lib/core/__pycache__/rs-math.cpython-312.pyc +0 -0
- package/lib/core/__pycache__/rs_cache.cpython-312.pyc +0 -0
- package/lib/core/__pycache__/rs_corpus.cpython-312.pyc +0 -0
- package/lib/core/__pycache__/rs_hybrid.cpython-312.pyc +0 -0
- package/lib/core/__pycache__/rs_math.cpython-312.pyc +0 -0
- package/lib/core/__pycache__/rs_rooms.cpython-312.pyc +0 -0
- package/lib/core/artifact-id.cjs +148 -0
- package/lib/core/asset-ops.cjs +151 -0
- package/lib/core/auto-commit-throttle.cjs +129 -0
- package/lib/core/bearer-token.cjs +199 -0
- package/lib/core/brain-client.cjs +865 -0
- package/lib/core/brain-derivation-prompts.cjs +326 -0
- package/lib/core/brain-derivation-queue.cjs +431 -0
- package/lib/core/brain-derivation.cjs +580 -0
- package/lib/core/brain-md-schema.cjs +528 -0
- package/lib/core/brain-md-staleness.cjs +357 -0
- package/lib/core/brain-response-sanitize.cjs +188 -0
- package/lib/core/bridge-writer.cjs +477 -0
- package/lib/core/chat-context-builder.cjs +253 -0
- package/lib/core/cross-room-aggregator.cjs +762 -0
- package/lib/core/daily-briefing.cjs +438 -0
- package/lib/core/decision-capture.cjs +618 -0
- package/lib/core/deep-links.cjs +82 -0
- package/lib/core/dispatch-optimizer.cjs +354 -0
- package/lib/core/dual-path-detector.cjs +84 -0
- package/lib/core/dual-path-detector.test.cjs +334 -0
- package/lib/core/exports-log.cjs +79 -0
- package/lib/core/feynman-minto-invariants.cjs +605 -0
- package/lib/core/folder-memory-async.cjs +338 -0
- package/lib/core/folder-memory-shared.cjs +890 -0
- package/lib/core/folder-memory.cjs +416 -0
- package/lib/core/framework-chain-composer.cjs +411 -0
- package/lib/core/frontmatter-schemas.cjs +330 -0
- package/lib/core/git-ops.cjs +141 -0
- package/lib/core/graph-ops.cjs +258 -0
- package/lib/core/hat-persistence.cjs +362 -0
- package/lib/core/index.cjs +60 -0
- package/lib/core/integration-registry.cjs +232 -0
- package/lib/core/intelligence-cascade.cjs +661 -0
- package/lib/core/lazygraph-ops.cjs +1057 -0
- package/lib/core/lru-cache.cjs +139 -0
- package/lib/core/mcp-profiles.cjs +182 -0
- package/lib/core/meeting-ops.cjs +54 -0
- package/lib/core/memory-ops.cjs +600 -0
- package/lib/core/migrations/ROOM.md +33 -0
- package/lib/core/migrations/phase-109-nodes-provenance.cjs +339 -0
- package/lib/core/migrations/phase-109-session-focus.cjs +99 -0
- package/lib/core/model-profiles.cjs +246 -0
- package/lib/core/mullins-scaffold.cjs +160 -0
- package/lib/core/nav-dial.cjs +316 -0
- package/lib/core/navigation/ROOM.md +15 -0
- package/lib/core/navigation/explanation.cjs +43 -0
- package/lib/core/navigation/focus.cjs +135 -0
- package/lib/core/navigation/ingestion.cjs +82 -0
- package/lib/core/navigation/insights.cjs +350 -0
- package/lib/core/navigation/memory-events.cjs +118 -0
- package/lib/core/navigation/neighborhood.cjs +78 -0
- package/lib/core/navigation/packet.cjs +182 -0
- package/lib/core/navigation/room-home.cjs +127 -0
- package/lib/core/navigation/transitions.cjs +82 -0
- package/lib/core/navigation-engine-shared.cjs +242 -0
- package/lib/core/navigation-engine.cjs +664 -0
- package/lib/core/navigation.cjs +60 -0
- package/lib/core/nl-graph-queries.cjs +164 -0
- package/lib/core/offer-presenter.cjs +406 -0
- package/lib/core/opportunity-extractor.cjs +183 -0
- package/lib/core/opportunity-ops.cjs +1371 -0
- package/lib/core/persona-ops.cjs +537 -0
- package/lib/core/persona-taxonomy.cjs +190 -0
- package/lib/core/platform-gates.cjs +120 -0
- package/lib/core/platform.cjs +257 -0
- package/lib/core/proactive-intelligence.cjs +528 -0
- package/lib/core/problem-type-router.cjs +315 -0
- package/lib/core/reasoning-ops.cjs +639 -0
- package/lib/core/reverse-salient-persona-suffix.cjs +115 -0
- package/lib/core/room-classifier-strict-mode.cjs +229 -0
- package/lib/core/room-db.cjs +127 -0
- package/lib/core/room-ops-async.cjs +92 -0
- package/lib/core/room-ops-shared.cjs +64 -0
- package/lib/core/room-ops-sync.cjs +70 -0
- package/lib/core/room-ops.cjs +32 -0
- package/lib/core/room-type-detector.cjs +386 -0
- package/lib/core/rs-brain-substrate-prompts.cjs +129 -0
- package/lib/core/rs-brain-substrate.cjs +570 -0
- package/lib/core/rs-breakthrough-scorer.cjs +255 -0
- package/lib/core/rs-canon-violations.cjs +82 -0
- package/lib/core/rs-chain-feeder.cjs +343 -0
- package/lib/core/rs-commercial-assessor.cjs +280 -0
- package/lib/core/rs-differential-scorer.cjs +376 -0
- package/lib/core/rs-domain-analyzer.cjs +385 -0
- package/lib/core/rs-egress-prompts.cjs +113 -0
- package/lib/core/rs-egress-telemetry.cjs +225 -0
- package/lib/core/rs-egress-violations.cjs +53 -0
- package/lib/core/rs-expert-mapper.cjs +467 -0
- package/lib/core/rs-fetcher-academic.cjs +697 -0
- package/lib/core/rs-fetcher-experts.cjs +314 -0
- package/lib/core/rs-fetcher-industry.cjs +731 -0
- package/lib/core/rs-fetcher-patents.cjs +564 -0
- package/lib/core/rs-innovation-classifier.cjs +194 -0
- package/lib/core/rs-mind-map.cjs +656 -0
- package/lib/core/rs-neo4j-writer.cjs +388 -0
- package/lib/core/rs-nl-to-query.cjs +425 -0
- package/lib/core/rs-pinecone-bridge.cjs +303 -0
- package/lib/core/rs-preprocessor.cjs +350 -0
- package/lib/core/rs-query-matrix.cjs +316 -0
- package/lib/core/rs-query-to-text.cjs +438 -0
- package/lib/core/rs-sqlite-mirror.cjs +443 -0
- package/lib/core/rs-thesis-generator.cjs +188 -0
- package/lib/core/rs_cache.py +479 -0
- package/lib/core/rs_corpus.py +468 -0
- package/lib/core/rs_hybrid.py +586 -0
- package/lib/core/rs_math.py +287 -0
- package/lib/core/rs_rooms.py +193 -0
- package/lib/core/scheduled-scanner.cjs +463 -0
- package/lib/core/scratchpad-ops.cjs +201 -0
- package/lib/core/section-8-trace-schema.cjs +138 -0
- package/lib/core/section-registry.cjs +111 -0
- package/lib/core/session-state.cjs +144 -0
- package/lib/core/shallow-doc-parser.cjs +174 -0
- package/lib/core/shallow-doc-parser.test.cjs +226 -0
- package/lib/core/skill-activation-router.cjs +284 -0
- package/lib/core/state-ops.cjs +46 -0
- package/lib/core/statusline-cache.cjs +266 -0
- package/lib/core/token-estimator.cjs +348 -0
- package/lib/core/user-archetype.cjs +239 -0
- package/lib/core/user-md-ops.cjs +524 -0
- package/lib/core/visual-ops.cjs +624 -0
- package/lib/core/write-lock.cjs +149 -0
- package/lib/graph/canvas-graph.js +467 -0
- package/lib/graph/constellation-config.cjs +299 -0
- package/lib/graph/graph-detail-panel.js +165 -0
- package/lib/hmi/ROOM.md +47 -0
- package/lib/hmi/across-session-memory.cjs +604 -0
- package/lib/hmi/cross-room-memory.cjs +575 -0
- package/lib/hmi/decoy-tier.cjs +395 -0
- package/lib/hmi/jtbd-classifier.cjs +219 -0
- package/lib/hmi/jtbd-state.cjs +199 -0
- package/lib/hmi/jtbd-taxonomy.json +392 -0
- package/lib/hmi/selector-dispatcher.cjs +546 -0
- package/lib/hmi/selector-telemetry.cjs +263 -0
- package/lib/hmi/shape-f0-renderer.cjs +139 -0
- package/lib/hmi/shape-f1-fallback.cjs +80 -0
- package/lib/hmi/shape-f1-renderer.cjs +138 -0
- package/lib/hmi/shape-f2-renderer.cjs +132 -0
- package/lib/hmi/shape-f3-renderer.cjs +66 -0
- package/lib/hmi/shape-f4-renderer.cjs +72 -0
- package/lib/hmi/shape-f5-renderer.cjs +155 -0
- package/lib/hmi/shape-f6-plan-review-renderer.cjs +312 -0
- package/lib/hmi/shape-f6-renderer.cjs +144 -0
- package/lib/hmi/shape-g-renderer.cjs +219 -0
- package/lib/hmi/shape-h-renderer.cjs +222 -0
- package/lib/hmi/tier-check.cjs +63 -0
- package/lib/import/PRECONDITIONS.md +41 -0
- package/lib/import/branding.cjs +210 -0
- package/lib/import/branding.test.cjs +235 -0
- package/lib/import/classifications-sync.cjs +104 -0
- package/lib/import/classifications-sync.test.cjs +129 -0
- package/lib/import/enricher.cjs +296 -0
- package/lib/import/enricher.test.cjs +273 -0
- package/lib/import/integration.test.cjs +376 -0
- package/lib/import/manifest.cjs +129 -0
- package/lib/import/manifest.schema.json +185 -0
- package/lib/import/manifest.test.cjs +123 -0
- package/lib/import/meeting-detector.cjs +92 -0
- package/lib/import/meeting-detector.test.cjs +100 -0
- package/lib/import/person-detector.cjs +229 -0
- package/lib/import/person-detector.test.cjs +149 -0
- package/lib/import/report.cjs +186 -0
- package/lib/import/report.test.cjs +186 -0
- package/lib/import/room-md-scaffolder.cjs +49 -0
- package/lib/import/router.cjs +224 -0
- package/lib/import/router.test.cjs +356 -0
- package/lib/import/run-all-tests.cjs +36 -0
- package/lib/import/smoke-test.cjs +213 -0
- package/lib/import/smoke-test.test.cjs +148 -0
- package/lib/import/test-fixtures/collision-vault/preexisting-room/STATE.md +8 -0
- package/lib/import/test-fixtures/collision-vault/preexisting-room/problem-definition/onboarding/onboarding.md +7 -0
- package/lib/import/test-fixtures/collision-vault/source/onboarding.md +5 -0
- package/lib/import/test-fixtures/obsidian-vault/.obsidian/workspace.json +1 -0
- package/lib/import/test-fixtures/obsidian-vault/notes/with-wikilinks.md +4 -0
- package/lib/import/test-fixtures/tiny-vault/notes/2026-01-15-team-sync.md +9 -0
- package/lib/import/test-fixtures/tiny-vault/notes/empty.md +3 -0
- package/lib/import/test-fixtures/tiny-vault/notes/onboarding.md +5 -0
- package/lib/import/test-fixtures/tiny-vault/notes/pricing.md +5 -0
- package/lib/import/test-fixtures/tiny-vault/notes/random.md +4 -0
- package/lib/import/undo.test.cjs +199 -0
- package/lib/import/vault-scanner.cjs +105 -0
- package/lib/import/vault-scanner.test.cjs +67 -0
- package/lib/mcp/app-html/dashboard.html +316 -0
- package/lib/mcp/app-html/graph.html +428 -0
- package/lib/mcp/app-html/mindrian-platform.html +1841 -0
- package/lib/mcp/app-html/wiki.html +383 -0
- package/lib/mcp/app-views.cjs +322 -0
- package/lib/mcp/brain-router.cjs +418 -0
- package/lib/mcp/capability-registry.cjs +62 -0
- package/lib/mcp/larry-context.cjs +46 -0
- package/lib/mcp/larry-server-instructions.md +114 -0
- package/lib/mcp/pipeline-state.cjs +275 -0
- package/lib/mcp/prompts.cjs +302 -0
- package/lib/mcp/resources.cjs +227 -0
- package/lib/mcp/session-catchup.cjs +327 -0
- package/lib/mcp/surface-detect.cjs +75 -0
- package/lib/mcp/tool-router.cjs +1034 -0
- package/lib/memory/aaak-compress.cjs +403 -0
- package/lib/memory/aaak-compress.test.cjs +288 -0
- package/lib/memory/async-artifact-auto-commit.test.cjs +223 -0
- package/lib/memory/bearer-token.test.cjs +315 -0
- package/lib/memory/brain-cache-lru.test.cjs +259 -0
- package/lib/memory/brain-client-query-shape.test.cjs +160 -0
- package/lib/memory/brain-derivation-graceful-degradation.test.cjs +1019 -0
- package/lib/memory/brain-derivation-queue.test.cjs +539 -0
- package/lib/memory/brain-derivation.test.cjs +634 -0
- package/lib/memory/brain-derive-command.test.cjs +534 -0
- package/lib/memory/brain-md-invariants-validator.test.cjs +704 -0
- package/lib/memory/brain-md-schema.test.cjs +467 -0
- package/lib/memory/brain-md-staleness.test.cjs +525 -0
- package/lib/memory/brain-server-resolution.test.cjs +314 -0
- package/lib/memory/chain-recommender.test.cjs +233 -0
- package/lib/memory/chat-context.test.cjs +128 -0
- package/lib/memory/command-registry.test.cjs +220 -0
- package/lib/memory/cross-room-aggregator.test.cjs +909 -0
- package/lib/memory/dashboard-server.test.cjs +256 -0
- package/lib/memory/debouncer-drain-at-prompt.test.cjs +389 -0
- package/lib/memory/decision-capture.test.cjs +632 -0
- package/lib/memory/decision-capture.worker.cjs +70 -0
- package/lib/memory/explain-decision-command.test.cjs +521 -0
- package/lib/memory/explain-decision-footer.test.cjs +316 -0
- package/lib/memory/explored-materials-store.cjs +392 -0
- package/lib/memory/feynman-minto-guardian.test.cjs +736 -0
- package/lib/memory/feynman-minto-invariants.test.cjs +511 -0
- package/lib/memory/feynman-prompts-drift.test.cjs +144 -0
- package/lib/memory/feynman-prompts.cjs +151 -0
- package/lib/memory/feynman-prompts.test.cjs +96 -0
- package/lib/memory/folder-memory-quadruple.test.cjs +548 -0
- package/lib/memory/folder-memory.test.cjs +503 -0
- package/lib/memory/framework-chain-composer.test.cjs +515 -0
- package/lib/memory/frontmatter-schema-validator.test.cjs +290 -0
- package/lib/memory/heal-command.test.cjs +604 -0
- package/lib/memory/index-artifact-transaction.test.cjs +333 -0
- package/lib/memory/lazygraph-rs-discoveries-view.test.cjs +122 -0
- package/lib/memory/mcp-input-validation.test.cjs +240 -0
- package/lib/memory/mcp-server-brain-deps.test.cjs +270 -0
- package/lib/memory/mcp-stack-fallback.test.cjs +433 -0
- package/lib/memory/minto-debouncer.test.cjs +407 -0
- package/lib/memory/minto-debouncer.worker.cjs +46 -0
- package/lib/memory/minto-migration-v88.test.cjs +265 -0
- package/lib/memory/minto-schema-v88.test.cjs +390 -0
- package/lib/memory/mos-status-renderer.test.cjs +631 -0
- package/lib/memory/narrative-schema.cjs +376 -0
- package/lib/memory/narrative-schema.test.cjs +209 -0
- package/lib/memory/nav-dial.test.cjs +414 -0
- package/lib/memory/navigation-engine-core.test.cjs +722 -0
- package/lib/memory/navigation-invariants.test.cjs +483 -0
- package/lib/memory/offer-presenter.test.cjs +554 -0
- package/lib/memory/on-stop-snapshot.test.cjs +404 -0
- package/lib/memory/pending-tension-store.cjs +373 -0
- package/lib/memory/post-compact-reinjection.test.cjs +854 -0
- package/lib/memory/post-write-triple.test.cjs +317 -0
- package/lib/memory/pre-compact-snapshot.test.cjs +495 -0
- package/lib/memory/problem-type-router.test.cjs +656 -0
- package/lib/memory/query-efficiency-telemetry.test.cjs +370 -0
- package/lib/memory/recompile-room-references.test.cjs +392 -0
- package/lib/memory/recompile-room-references.worker.cjs +42 -0
- package/lib/memory/record-decision-dual-write.test.cjs +454 -0
- package/lib/memory/room-classifier-strict-mode.test.cjs +417 -0
- package/lib/memory/room-minto-hook.test.cjs +398 -0
- package/lib/memory/rs-discovery-engine.test.cjs +323 -0
- package/lib/memory/run-feynman-tests.cjs +1247 -0
- package/lib/memory/security-trifecta.test.cjs +312 -0
- package/lib/memory/session-start-brain-staleness.test.cjs +363 -0
- package/lib/memory/session-start-triple-injection.test.cjs +514 -0
- package/lib/memory/sessionstart-banner-formatter.cjs +318 -0
- package/lib/memory/sessionstart-minto-banner.test.cjs +373 -0
- package/lib/memory/skill-activation-router.test.cjs +419 -0
- package/lib/memory/stamp-artifact-write.test.cjs +304 -0
- package/lib/memory/statusline-active-room.test.cjs +315 -0
- package/lib/memory/statusline-minto-segment.test.cjs +292 -0
- package/lib/memory/sync-async-entry-points.test.cjs +204 -0
- package/lib/memory/test-bridge-writer-enhanced.cjs +452 -0
- package/lib/memory/test-rs-brain-substrate-shape.cjs +529 -0
- package/lib/memory/test-rs-brain-substrate.cjs +636 -0
- package/lib/memory/test-rs-breakthrough-scorer.cjs +375 -0
- package/lib/memory/test-rs-canon-violations.cjs +218 -0
- package/lib/memory/test-rs-chain-feeder-core.cjs +344 -0
- package/lib/memory/test-rs-chain-feeder-skill-spawn.cjs +297 -0
- package/lib/memory/test-rs-commercial-assessor.cjs +385 -0
- package/lib/memory/test-rs-differential-scorer.cjs +480 -0
- package/lib/memory/test-rs-discovery-engine.cjs +603 -0
- package/lib/memory/test-rs-domain-analyzer.cjs +492 -0
- package/lib/memory/test-rs-egress-primitives.cjs +420 -0
- package/lib/memory/test-rs-expert-mapper.cjs +547 -0
- package/lib/memory/test-rs-explain-command.cjs +443 -0
- package/lib/memory/test-rs-fetcher-academic.cjs +848 -0
- package/lib/memory/test-rs-fetcher-experts.cjs +496 -0
- package/lib/memory/test-rs-fetcher-industry.cjs +702 -0
- package/lib/memory/test-rs-fetcher-patents.cjs +674 -0
- package/lib/memory/test-rs-innovation-classifier.cjs +301 -0
- package/lib/memory/test-rs-mind-map.cjs +646 -0
- package/lib/memory/test-rs-neo4j-writer.cjs +518 -0
- package/lib/memory/test-rs-nl-to-query.cjs +449 -0
- package/lib/memory/test-rs-pinecone-bridge.cjs +277 -0
- package/lib/memory/test-rs-preprocessor.cjs +433 -0
- package/lib/memory/test-rs-query-matrix.cjs +391 -0
- package/lib/memory/test-rs-query-to-text.cjs +551 -0
- package/lib/memory/test-rs-sqlite-mirror.cjs +649 -0
- package/lib/memory/test-rs-thesis-generator.cjs +360 -0
- package/lib/memory/triple-context-formatter.cjs +473 -0
- package/lib/memory/triple-context-formatter.test.cjs +442 -0
- package/lib/memory/user-md-persona.test.cjs +565 -0
- package/lib/memory/userpromptsubmit-integration.test.cjs +690 -0
- package/lib/memory/validators/README.md +157 -0
- package/lib/memory/validators/brain-md-invariants.cjs +475 -0
- package/lib/memory/validators/brain-substrate-invariants.cjs +285 -0
- package/lib/memory/validators/external-academic-invariants.cjs +249 -0
- package/lib/memory/validators/external-industry-invariants.cjs +271 -0
- package/lib/memory/validators/external-patents-invariants.cjs +266 -0
- package/lib/memory/validators/minto-invariants.cjs +62 -0
- package/lib/memory/validators/navigation-invariants.cjs +340 -0
- package/lib/memory/validators/queue-health.cjs +95 -0
- package/lib/memory/validators/snapshot-integrity.cjs +129 -0
- package/lib/memory/validators/stale-lifecycle.cjs +116 -0
- package/lib/memory/vault-section-minto-generator-atomic.test.cjs +556 -0
- package/lib/memory/vault-section-minto-generator-atomic.worker.cjs +73 -0
- package/lib/memory/write-lock-atomic.test.cjs +137 -0
- package/lib/memory/write-lock-atomic.worker.cjs +55 -0
- package/lib/parity/check-parity.cjs +83 -0
- package/lib/presentation/presentation-server.cjs +101 -0
- package/lib/presentation/presentation-watcher.cjs +123 -0
- package/lib/quickview/hub-server.cjs +719 -0
- package/lib/quickview/server.cjs +533 -0
- package/lib/render/JTBD-PALETTES.md +145 -0
- package/lib/render/ROOM.md +59 -0
- package/lib/render/render-v2.cjs +486 -0
- package/lib/render/render-v2.test.cjs +267 -0
- package/lib/render/render.cjs +65 -0
- package/lib/state/ROOM.md +46 -0
- package/lib/state/state-md-parser.cjs +215 -0
- package/lib/statusline/ROOM.md +38 -0
- package/lib/statusline/banner-suppression.cjs +50 -0
- package/lib/statusline/surface-detect.cjs +85 -0
- package/lib/update-bootstrap.sh.template +145 -0
- package/lib/vault/frontmatter-schema.cjs +297 -0
- package/lib/vault/room-scanner.cjs +352 -0
- package/lib/vault/wikilink-builder.cjs +231 -0
- package/lib/vault/wikilink-builder.test.cjs +182 -0
- package/lib/wiki/graph-links.cjs +281 -0
- package/lib/wiki/page-renderer.cjs +229 -0
- package/lib/wiki/wiki-chat.cjs +81 -0
- package/lib/wiki/wiki-layout.cjs +1459 -0
- package/lib/wiki/wiki-search.cjs +142 -0
- package/lib/wiki/wiki-server.cjs +678 -0
- package/lib/wiki/wiki-watcher.cjs +105 -0
- package/lib/workflow/ROOM.md +47 -0
- package/lib/workflow/command-resolver.cjs +155 -0
- package/lib/workflow/command-resolver.test.cjs +235 -0
- package/package.json +44 -0
- package/pipelines/analogy/01-decompose.md +80 -0
- package/pipelines/analogy/02-abstract.md +87 -0
- package/pipelines/analogy/03-search.md +135 -0
- package/pipelines/analogy/04-transfer.md +101 -0
- package/pipelines/analogy/05-validate.md +106 -0
- package/pipelines/analogy/CHAIN.md +56 -0
- package/pipelines/discovery/01-explore-domains.md +44 -0
- package/pipelines/discovery/02-think-hats.md +50 -0
- package/pipelines/discovery/03-analyze-needs.md +54 -0
- package/pipelines/discovery/CHAIN.md +37 -0
- package/pipelines/thesis/01-structure-argument.md +45 -0
- package/pipelines/thesis/02-challenge-assumptions.md +48 -0
- package/pipelines/thesis/03-build-thesis.md +54 -0
- package/pipelines/thesis/CHAIN.md +37 -0
- package/references/brain/causal-directives.md +91 -0
- package/references/brain/causal-enrichment.cypher +165 -0
- package/references/brain/command-triggers-schema.md +226 -0
- package/references/brain/graph-architecture.md +317 -0
- package/references/brain/query-patterns.md +460 -0
- package/references/brain/room-hierarchy-schema.md +218 -0
- package/references/brain/schema.md +76 -0
- package/references/capability-radar/capabilities-index.md +241 -0
- package/references/capability-radar/changelog-cache.md +81 -0
- package/references/causal/causal-schema.md +103 -0
- package/references/design/email-template-standard.md +155 -0
- package/references/design/graph-visualization-standard.md +178 -0
- package/references/document-generation.md +179 -0
- package/references/hsi/HSI-TOOLS-REFERENCE.md +222 -0
- package/references/import-config.md +141 -0
- package/references/integrations/detection-patterns.md +101 -0
- package/references/meeting/artifact-template.md +377 -0
- package/references/meeting/cross-meeting-intelligence.md +216 -0
- package/references/meeting/cross-relationship-patterns.md +202 -0
- package/references/meeting/live-join-interface.md +244 -0
- package/references/meeting/section-mapping.md +192 -0
- package/references/meeting/segment-classification.md +258 -0
- package/references/meeting/speaker-profile-template.md +219 -0
- package/references/meeting/summary-template.md +348 -0
- package/references/meeting/transcript-patterns.md +226 -0
- package/references/methodology/analyze-needs.md +135 -0
- package/references/methodology/analyze-systems.md +121 -0
- package/references/methodology/analyze-timing.md +149 -0
- package/references/methodology/beautiful-question.md +109 -0
- package/references/methodology/build-knowledge.md +161 -0
- package/references/methodology/build-thesis.md +237 -0
- package/references/methodology/challenge-assumptions.md +127 -0
- package/references/methodology/diagnose.md +169 -0
- package/references/methodology/dominant-designs.md +212 -0
- package/references/methodology/explore-domains.md +147 -0
- package/references/methodology/explore-futures.md +163 -0
- package/references/methodology/explore-trends.md +129 -0
- package/references/methodology/find-bottlenecks.md +131 -0
- package/references/methodology/grade.md +211 -0
- package/references/methodology/index.md +97 -0
- package/references/methodology/leadership.md +200 -0
- package/references/methodology/lean-canvas.md +116 -0
- package/references/methodology/macro-trends.md +192 -0
- package/references/methodology/map-unknowns.md +137 -0
- package/references/methodology/mullins-7-domains.md +104 -0
- package/references/methodology/problem-types.md +65 -0
- package/references/methodology/root-cause.md +178 -0
- package/references/methodology/sapphire-encoding.md +355 -0
- package/references/methodology/scenario-plan.md +178 -0
- package/references/methodology/score-innovation.md +154 -0
- package/references/methodology/structure-argument.md +158 -0
- package/references/methodology/systems-thinking.md +159 -0
- package/references/methodology/think-hats.md +147 -0
- package/references/methodology/triz-matrix.json +751 -0
- package/references/methodology/triz-principles.md +501 -0
- package/references/methodology/user-needs.md +199 -0
- package/references/methodology/validate.md +163 -0
- package/references/methodology/value-proposition.md +244 -0
- package/references/opportunities/funding-lifecycle.md +103 -0
- package/references/opportunities/grant-api-patterns.md +99 -0
- package/references/opportunities/opportunity-template.md +84 -0
- package/references/personality/assessment-philosophy.md +72 -0
- package/references/personality/lexicon.md +100 -0
- package/references/personality/persona-chains.md +56 -0
- package/references/personality/pws-lexicon-full.md +499 -0
- package/references/personality/voice-dna.md +156 -0
- package/references/personas/hat-perspectives.md +76 -0
- package/references/personas/persona-template.md +63 -0
- package/references/pipeline/act-output-contract.md +88 -0
- package/references/pipeline/chains-index.md +39 -0
- package/references/pws-profile-generation.md +79 -0
- package/references/reasoning/reasoning-schema.md +143 -0
- package/references/reasoning/reasoning-template.md +68 -0
- package/references/reasoning/run-template.md +38 -0
- package/references/research/RESEARCH_14_CLAUDE_CODE_SOURCE_ARCHITECTURE.md +209 -0
- package/references/research/RESEARCH_15_V1.8_OPTIMIZATION_JTBD.md +375 -0
- package/references/research/RESEARCH_16_NATIVE_FIRST_PLUGIN_ARCHITECTURE.md +575 -0
- package/references/research/RESEARCH_17_MCP_UI_FRAMEWORKS.md +272 -0
- package/references/taxonomy/TAXONOMY.md +192 -0
- package/references/templates/MINTO.md +36 -0
- package/references/user-research/2026-04-05-leah-lawrence-session.md +202 -0
- package/references/vault-kit/README.md +35 -0
- package/references/vault-kit/app.json +12 -0
- package/references/vault-kit/appearance.json +12 -0
- package/references/vault-kit/graph.json +35 -0
- package/references/vault-kit/snippets/mindrian-destijl.css +297 -0
- package/references/vault-kit/templates/new-artifact.md +37 -0
- package/references/vault-kit/templates/new-meeting-note.md +35 -0
- package/references/vault-kit/templates/new-team-profile.md +29 -0
- package/references/vault-kit/templates/new-xref.md +35 -0
- package/references/visual/symbol-system.md +151 -0
- package/skills/MOSDeckEngine/SKILL.md +325 -0
- package/skills/brain-connector/SKILL.md +114 -0
- package/skills/context-engine/SKILL.md +147 -0
- package/skills/conversation-mode/SKILL.md +102 -0
- package/skills/larry-personality/SKILL.md +219 -0
- package/skills/larry-personality/framework-chains.md +92 -0
- package/skills/larry-personality/mode-engine.md +185 -0
- package/skills/mullins-scaffold/SKILL.md +61 -0
- package/skills/mullins-scaffold/scaffold.json +146 -0
- package/skills/pws-methodology/SKILL.md +49 -0
- package/skills/room-passive/SKILL.md +165 -0
- package/skills/room-proactive/SKILL.md +250 -0
- package/skills/ui-system/SKILL.md +277 -0
|
@@ -0,0 +1,524 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
* Copyright (c) 2026 Mindrian. BSL 1.1.
|
|
6
|
+
*
|
|
7
|
+
* Phase 91-01 -- USER.md persistence + persona update-detect ops
|
|
8
|
+
* ==============================================================
|
|
9
|
+
* USER.md is a per-user, per-room artifact that holds the user's
|
|
10
|
+
* canonical role, current Larry persona, journey stage, role-blend
|
|
11
|
+
* weights, and supporting metadata. This module owns the read / write /
|
|
12
|
+
* update-decide primitives.
|
|
13
|
+
*
|
|
14
|
+
* Three responsibilities, one entry point each:
|
|
15
|
+
*
|
|
16
|
+
* readUserMd(path)
|
|
17
|
+
* Graceful-degradation read. null when absent, emptyUser() shell
|
|
18
|
+
* with parse_failed:true on malformed content, full struct when
|
|
19
|
+
* the file is well-formed. NEVER throws.
|
|
20
|
+
*
|
|
21
|
+
* writeUserMdAtomic(path, data)
|
|
22
|
+
* Phase 87-02 atomic write pattern: openSync('wx') + writeSync +
|
|
23
|
+
* fsyncSync (best-effort) + closeSync + renameSync. Cleans up tmp
|
|
24
|
+
* on any error path. Preserves the user-authored body below the
|
|
25
|
+
* frontmatter delimiter.
|
|
26
|
+
*
|
|
27
|
+
* detectPersonaUpdate({ current, signal })
|
|
28
|
+
* Pure function returning { shouldUpdate, reason } where reason is
|
|
29
|
+
* one of:
|
|
30
|
+
* 'first_detection' -- current === null
|
|
31
|
+
* 'user_override' -- signal.source === 'user_override'
|
|
32
|
+
* 'no_change' -- signal.persona === current.persona
|
|
33
|
+
* 'confidence_below_threshold' -- signal.confidence < threshold
|
|
34
|
+
* 'awaiting_consecutive_signal' -- threshold met but < 3 consecutive
|
|
35
|
+
* 'threshold_met' -- threshold met + >= 3 consecutive
|
|
36
|
+
*
|
|
37
|
+
* Canon Part 8 posture:
|
|
38
|
+
* USER.md is a LOCAL artifact. This module never touches the network.
|
|
39
|
+
* Test 21 of lib/memory/user-md-persona.test.cjs grep-guards this
|
|
40
|
+
* module against Brain MCP query helpers, network primitives, http
|
|
41
|
+
* URL literals, and shell-out HTTP clients. Brain queries that carry
|
|
42
|
+
* persona MUST go through brain-derivation-prompts.cjs and carry only
|
|
43
|
+
* the Larry-or-Brain persona scalar (a generic framework handle),
|
|
44
|
+
* never the role_blend weights or user_id.
|
|
45
|
+
*
|
|
46
|
+
* Phase 87-02 atomic-write pattern source-of-truth:
|
|
47
|
+
* lib/core/decision-capture.cjs lines 320-380 (atomicRewriteMintoWith*)
|
|
48
|
+
* lib/core/write-lock.cjs (the openSync('wx') primitive proven under
|
|
49
|
+
* the 20-fork concurrency fence).
|
|
50
|
+
*
|
|
51
|
+
* Three-surface compatible (CLI hook, Desktop MCP handler, Cowork
|
|
52
|
+
* shared runner). Pure CJS, node built-ins only, zero npm dependencies.
|
|
53
|
+
*
|
|
54
|
+
* License: BSL 1.1.
|
|
55
|
+
*/
|
|
56
|
+
|
|
57
|
+
const fs = require('node:fs');
|
|
58
|
+
const path = require('node:path');
|
|
59
|
+
|
|
60
|
+
const taxonomy = require('./persona-taxonomy.cjs');
|
|
61
|
+
|
|
62
|
+
const SCHEMA_VERSION = 1;
|
|
63
|
+
const DEFAULT_UPDATE_THRESHOLD = 0.75;
|
|
64
|
+
const CONSECUTIVE_SIGNAL_REQUIRED = 3;
|
|
65
|
+
|
|
66
|
+
// ---------- emptyUser shell ----------
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Build an empty USER.md struct. Used as the fallback shape when
|
|
70
|
+
* USER.md is absent (caller wants a non-null default), as the parse
|
|
71
|
+
* failure shape, and as the shell that writeUserMdAtomic merges into
|
|
72
|
+
* before serializing.
|
|
73
|
+
*
|
|
74
|
+
* @returns {object} Empty user struct.
|
|
75
|
+
*/
|
|
76
|
+
function emptyUser() {
|
|
77
|
+
return {
|
|
78
|
+
schema_version: SCHEMA_VERSION,
|
|
79
|
+
user_id: null,
|
|
80
|
+
canonical_role: null,
|
|
81
|
+
larry_persona: null,
|
|
82
|
+
brain_persona: null,
|
|
83
|
+
journey_stage: null,
|
|
84
|
+
role_blend: {
|
|
85
|
+
founder: 0,
|
|
86
|
+
researcher: 0,
|
|
87
|
+
operator: 0,
|
|
88
|
+
investor: 0,
|
|
89
|
+
mentor: 0,
|
|
90
|
+
domain_expert: 0,
|
|
91
|
+
student: 0,
|
|
92
|
+
},
|
|
93
|
+
problem_type: 'unknown',
|
|
94
|
+
venture_stage: 'unknown',
|
|
95
|
+
last_detected_at: null,
|
|
96
|
+
last_updated_at: null,
|
|
97
|
+
detection_confidence: 0.0,
|
|
98
|
+
update_threshold: DEFAULT_UPDATE_THRESHOLD,
|
|
99
|
+
consecutive_signal_count: 0,
|
|
100
|
+
parse_failed: false,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// ---------- Narrow-dialect YAML scalar parser ----------
|
|
105
|
+
//
|
|
106
|
+
// Mirrors the dialect used in lib/core/folder-memory-shared.cjs
|
|
107
|
+
// parseBrainFrontmatter. Flat scalars only: strings, ints, floats,
|
|
108
|
+
// booleans, null. Single one-level-deep object support for role_blend.
|
|
109
|
+
// Duplicated here intentionally so user-md-ops keeps zero cross-import
|
|
110
|
+
// into folder-memory layers (flat dep graph).
|
|
111
|
+
|
|
112
|
+
function unquoteScalar(raw) {
|
|
113
|
+
const s = raw.trim();
|
|
114
|
+
if (s.length === 0) return '';
|
|
115
|
+
if (s[0] === '"') {
|
|
116
|
+
if (s.length < 2 || s[s.length - 1] !== '"') return undefined;
|
|
117
|
+
return s.slice(1, -1);
|
|
118
|
+
}
|
|
119
|
+
if (s[0] === "'") {
|
|
120
|
+
if (s.length < 2 || s[s.length - 1] !== "'") return undefined;
|
|
121
|
+
return s.slice(1, -1);
|
|
122
|
+
}
|
|
123
|
+
return s;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function castScalar(s) {
|
|
127
|
+
if (s === 'null' || s === '~') return null;
|
|
128
|
+
if (s === 'true') return true;
|
|
129
|
+
if (s === 'false') return false;
|
|
130
|
+
if (/^-?\d+$/.test(s)) {
|
|
131
|
+
const n = parseInt(s, 10);
|
|
132
|
+
if (Number.isSafeInteger(n)) return n;
|
|
133
|
+
}
|
|
134
|
+
if (/^-?\d+\.\d+$/.test(s)) {
|
|
135
|
+
const f = parseFloat(s);
|
|
136
|
+
if (Number.isFinite(f)) return f;
|
|
137
|
+
}
|
|
138
|
+
return s;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function parseFrontmatter(src) {
|
|
142
|
+
if (typeof src !== 'string' || src.length === 0) {
|
|
143
|
+
return { ok: false };
|
|
144
|
+
}
|
|
145
|
+
const lines = src.split(/\r?\n/);
|
|
146
|
+
const root = {};
|
|
147
|
+
// role_blend is the only nested scalar map we know about. Track its
|
|
148
|
+
// open block so we can absorb indented children.
|
|
149
|
+
let inRoleBlend = false;
|
|
150
|
+
for (let i = 0; i < lines.length; i++) {
|
|
151
|
+
const line = lines[i];
|
|
152
|
+
if (line.length === 0 || /^\s*$/.test(line) || /^\s*#/.test(line)) {
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
// Indented child of role_blend?
|
|
156
|
+
if (inRoleBlend && /^\s+/.test(line)) {
|
|
157
|
+
const childMatch = line.match(/^\s+([A-Za-z_][A-Za-z0-9_-]*):\s*(.*)$/);
|
|
158
|
+
if (!childMatch) {
|
|
159
|
+
return { ok: false };
|
|
160
|
+
}
|
|
161
|
+
const ck = childMatch[1];
|
|
162
|
+
const cv = childMatch[2];
|
|
163
|
+
if (cv.trim().length === 0) {
|
|
164
|
+
root.role_blend[ck] = '';
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
const rawC = unquoteScalar(cv);
|
|
168
|
+
if (rawC === undefined) return { ok: false };
|
|
169
|
+
root.role_blend[ck] = castScalar(rawC);
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
// Flat top-level key: value. Closes any open block.
|
|
173
|
+
inRoleBlend = false;
|
|
174
|
+
const m = line.match(/^([A-Za-z_][A-Za-z0-9_-]*):(.*)$/);
|
|
175
|
+
if (!m) return { ok: false };
|
|
176
|
+
const key = m[1];
|
|
177
|
+
const rest = m[2];
|
|
178
|
+
if (rest.trim().length === 0) {
|
|
179
|
+
// Empty-RHS top-level key. role_blend is the known container; for
|
|
180
|
+
// any other key treat as empty string scalar.
|
|
181
|
+
if (key === 'role_blend') {
|
|
182
|
+
root.role_blend = {};
|
|
183
|
+
inRoleBlend = true;
|
|
184
|
+
} else {
|
|
185
|
+
root[key] = '';
|
|
186
|
+
}
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
const raw = unquoteScalar(rest);
|
|
190
|
+
if (raw === undefined) return { ok: false };
|
|
191
|
+
root[key] = castScalar(raw);
|
|
192
|
+
}
|
|
193
|
+
return { ok: true, data: root };
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// ---------- readUserMd ----------
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Read and parse a USER.md file with graceful degradation.
|
|
200
|
+
*
|
|
201
|
+
* Precedence:
|
|
202
|
+
* 1. USER.md absent / non-string / empty path -> null
|
|
203
|
+
* 2. Frontmatter delimiter missing -> emptyUser + parse_failed:true
|
|
204
|
+
* 3. Narrow-dialect YAML unparseable -> emptyUser + parse_failed:true
|
|
205
|
+
* 4. Read error (EACCES, etc.) -> emptyUser + parse_failed:true
|
|
206
|
+
* 5. Valid -> full struct, parse_failed:false
|
|
207
|
+
*
|
|
208
|
+
* Schema-tolerant validation: an unknown enum value (e.g. larry_persona
|
|
209
|
+
* not in LARRY_PERSONAS) coerces that single field to null and leaves
|
|
210
|
+
* parse_failed:false. The caller can distinguish "no persona detected
|
|
211
|
+
* yet" from "the file is corrupted" without try/catch.
|
|
212
|
+
*
|
|
213
|
+
* NEVER throws.
|
|
214
|
+
*
|
|
215
|
+
* @param {string} userMdPath Absolute path to USER.md.
|
|
216
|
+
* @returns {object|null} User struct or null when path is absent.
|
|
217
|
+
*/
|
|
218
|
+
function readUserMd(userMdPath) {
|
|
219
|
+
if (typeof userMdPath !== 'string' || userMdPath.length === 0) {
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
let stat;
|
|
223
|
+
try {
|
|
224
|
+
stat = fs.statSync(userMdPath);
|
|
225
|
+
} catch (_e) {
|
|
226
|
+
return null; // absent
|
|
227
|
+
}
|
|
228
|
+
if (!stat.isFile()) return null;
|
|
229
|
+
|
|
230
|
+
if (stat.size === 0) {
|
|
231
|
+
const shell = emptyUser();
|
|
232
|
+
shell.parse_failed = true;
|
|
233
|
+
return shell;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
let raw;
|
|
237
|
+
try {
|
|
238
|
+
raw = fs.readFileSync(userMdPath, 'utf8');
|
|
239
|
+
} catch (_e) {
|
|
240
|
+
const shell = emptyUser();
|
|
241
|
+
shell.parse_failed = true;
|
|
242
|
+
return shell;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const fmRe = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
|
|
246
|
+
const m = raw.match(fmRe);
|
|
247
|
+
if (!m) {
|
|
248
|
+
const shell = emptyUser();
|
|
249
|
+
shell.parse_failed = true;
|
|
250
|
+
return shell;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const parsed = parseFrontmatter(m[1]);
|
|
254
|
+
if (!parsed.ok) {
|
|
255
|
+
const shell = emptyUser();
|
|
256
|
+
shell.parse_failed = true;
|
|
257
|
+
return shell;
|
|
258
|
+
}
|
|
259
|
+
const fm = parsed.data;
|
|
260
|
+
|
|
261
|
+
const out = emptyUser();
|
|
262
|
+
out.parse_failed = false;
|
|
263
|
+
out.schema_version = typeof fm.schema_version === 'number'
|
|
264
|
+
? fm.schema_version : SCHEMA_VERSION;
|
|
265
|
+
out.user_id = typeof fm.user_id === 'string' && fm.user_id.length > 0
|
|
266
|
+
? fm.user_id : null;
|
|
267
|
+
out.canonical_role = typeof fm.canonical_role === 'string' && fm.canonical_role.length > 0
|
|
268
|
+
? fm.canonical_role : null;
|
|
269
|
+
// Schema-tolerant enum coercion: invalid -> null, NOT parse_failed.
|
|
270
|
+
if (typeof fm.larry_persona === 'string'
|
|
271
|
+
&& taxonomy.LARRY_PERSONAS.indexOf(fm.larry_persona) >= 0) {
|
|
272
|
+
out.larry_persona = fm.larry_persona;
|
|
273
|
+
} else {
|
|
274
|
+
out.larry_persona = null;
|
|
275
|
+
}
|
|
276
|
+
if (typeof fm.brain_persona === 'string'
|
|
277
|
+
&& taxonomy.BRAIN_PERSONAS.indexOf(fm.brain_persona) >= 0) {
|
|
278
|
+
out.brain_persona = fm.brain_persona;
|
|
279
|
+
} else {
|
|
280
|
+
out.brain_persona = null;
|
|
281
|
+
}
|
|
282
|
+
if (typeof fm.journey_stage === 'string'
|
|
283
|
+
&& taxonomy.JOURNEY_STAGES.indexOf(fm.journey_stage) >= 0) {
|
|
284
|
+
out.journey_stage = fm.journey_stage;
|
|
285
|
+
} else {
|
|
286
|
+
out.journey_stage = null;
|
|
287
|
+
}
|
|
288
|
+
if (typeof fm.problem_type === 'string'
|
|
289
|
+
&& taxonomy.PROBLEM_TYPES.indexOf(fm.problem_type) >= 0) {
|
|
290
|
+
out.problem_type = fm.problem_type;
|
|
291
|
+
} else {
|
|
292
|
+
out.problem_type = 'unknown';
|
|
293
|
+
}
|
|
294
|
+
if (typeof fm.venture_stage === 'string'
|
|
295
|
+
&& taxonomy.VENTURE_STAGES.indexOf(fm.venture_stage) >= 0) {
|
|
296
|
+
out.venture_stage = fm.venture_stage;
|
|
297
|
+
} else {
|
|
298
|
+
out.venture_stage = 'unknown';
|
|
299
|
+
}
|
|
300
|
+
out.last_detected_at = typeof fm.last_detected_at === 'string'
|
|
301
|
+
&& fm.last_detected_at.length > 0
|
|
302
|
+
? fm.last_detected_at : null;
|
|
303
|
+
out.last_updated_at = typeof fm.last_updated_at === 'string'
|
|
304
|
+
&& fm.last_updated_at.length > 0
|
|
305
|
+
? fm.last_updated_at : null;
|
|
306
|
+
out.detection_confidence = typeof fm.detection_confidence === 'number'
|
|
307
|
+
? fm.detection_confidence : 0.0;
|
|
308
|
+
out.update_threshold = typeof fm.update_threshold === 'number'
|
|
309
|
+
? fm.update_threshold : DEFAULT_UPDATE_THRESHOLD;
|
|
310
|
+
out.consecutive_signal_count = typeof fm.consecutive_signal_count === 'number'
|
|
311
|
+
? fm.consecutive_signal_count : 0;
|
|
312
|
+
// role_blend: inherit defaults, then overwrite per-axis from fm.
|
|
313
|
+
if (fm.role_blend && typeof fm.role_blend === 'object') {
|
|
314
|
+
const allowed = Object.keys(out.role_blend);
|
|
315
|
+
for (const k of allowed) {
|
|
316
|
+
if (typeof fm.role_blend[k] === 'number') {
|
|
317
|
+
out.role_blend[k] = fm.role_blend[k];
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
return out;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// ---------- Frontmatter emitter ----------
|
|
325
|
+
|
|
326
|
+
function emitYamlScalar(v) {
|
|
327
|
+
if (v === null || v === undefined) return 'null';
|
|
328
|
+
if (typeof v === 'boolean') return v ? 'true' : 'false';
|
|
329
|
+
if (typeof v === 'number') return String(v);
|
|
330
|
+
// String. Quote if it could be misparsed as another type.
|
|
331
|
+
const s = String(v);
|
|
332
|
+
// Plain-scalar safe set: ASCII alnum, dash, underscore, slash, dot,
|
|
333
|
+
// colon (for ISO timestamps), plus the +Z/T separators. Otherwise
|
|
334
|
+
// round-trip through quotes.
|
|
335
|
+
if (/^[A-Za-z0-9._/:+\- ]+$/.test(s) && s.length > 0
|
|
336
|
+
&& s !== 'true' && s !== 'false' && s !== 'null' && s !== '~') {
|
|
337
|
+
return s;
|
|
338
|
+
}
|
|
339
|
+
return '"' + s.replace(/\\/g, '\\\\').replace(/"/g, '\\"') + '"';
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function buildFrontmatter(data) {
|
|
343
|
+
const merged = Object.assign(emptyUser(), data || {});
|
|
344
|
+
// Re-merge role_blend separately so partial caller input does not
|
|
345
|
+
// erase the empty-shell defaults.
|
|
346
|
+
if (data && typeof data.role_blend === 'object' && data.role_blend !== null) {
|
|
347
|
+
merged.role_blend = Object.assign(emptyUser().role_blend, data.role_blend);
|
|
348
|
+
}
|
|
349
|
+
const lines = ['---'];
|
|
350
|
+
lines.push('schema_version: ' + emitYamlScalar(merged.schema_version));
|
|
351
|
+
lines.push('user_id: ' + emitYamlScalar(merged.user_id));
|
|
352
|
+
lines.push('canonical_role: ' + emitYamlScalar(merged.canonical_role));
|
|
353
|
+
lines.push('larry_persona: ' + emitYamlScalar(merged.larry_persona));
|
|
354
|
+
lines.push('brain_persona: ' + emitYamlScalar(merged.brain_persona));
|
|
355
|
+
lines.push('journey_stage: ' + emitYamlScalar(merged.journey_stage));
|
|
356
|
+
lines.push('role_blend:');
|
|
357
|
+
const axes = ['founder', 'researcher', 'operator', 'investor',
|
|
358
|
+
'mentor', 'domain_expert', 'student'];
|
|
359
|
+
for (const k of axes) {
|
|
360
|
+
const v = merged.role_blend[k];
|
|
361
|
+
lines.push(' ' + k + ': ' + emitYamlScalar(typeof v === 'number' ? v : 0));
|
|
362
|
+
}
|
|
363
|
+
lines.push('problem_type: ' + emitYamlScalar(merged.problem_type));
|
|
364
|
+
lines.push('venture_stage: ' + emitYamlScalar(merged.venture_stage));
|
|
365
|
+
lines.push('last_detected_at: ' + emitYamlScalar(merged.last_detected_at));
|
|
366
|
+
lines.push('last_updated_at: ' + emitYamlScalar(merged.last_updated_at));
|
|
367
|
+
lines.push('detection_confidence: ' + emitYamlScalar(merged.detection_confidence));
|
|
368
|
+
lines.push('update_threshold: ' + emitYamlScalar(merged.update_threshold));
|
|
369
|
+
lines.push('consecutive_signal_count: ' + emitYamlScalar(merged.consecutive_signal_count));
|
|
370
|
+
lines.push('---');
|
|
371
|
+
return lines.join('\n');
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// ---------- writeUserMdAtomic ----------
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Write USER.md atomically using the Phase 87-02 pattern.
|
|
378
|
+
*
|
|
379
|
+
* Sequence:
|
|
380
|
+
* 1. Read existing USER.md body (if any) to preserve user-authored
|
|
381
|
+
* content below the frontmatter delimiter.
|
|
382
|
+
* 2. Build new frontmatter from data merged into emptyUser() shell.
|
|
383
|
+
* 3. Compose newContent = newFrontmatter + tail + preservedBody.
|
|
384
|
+
* 4. Open tmp = path + '.tmp.<pid>.<rnd>.user' with openSync('wx')
|
|
385
|
+
* to fail-fast on stale tmp.
|
|
386
|
+
* 5. writeSync newContent. fsyncSync best-effort (try/catch for
|
|
387
|
+
* ENOTSUP on tmpfs).
|
|
388
|
+
* 6. closeSync.
|
|
389
|
+
* 7. renameSync(tmp, path) -- atomic on POSIX + Windows-NTFS.
|
|
390
|
+
* 8. On any error: unlink tmp + rethrow with descriptive message.
|
|
391
|
+
*
|
|
392
|
+
* The 'wx' flag plus per-pid + random tmp name makes mid-write crashes
|
|
393
|
+
* recoverable (next call re-creates a fresh tmp).
|
|
394
|
+
*
|
|
395
|
+
* @param {string} userMdPath Absolute path to USER.md.
|
|
396
|
+
* @param {object} data Partial USER.md struct; merged into shell.
|
|
397
|
+
* @returns {void}
|
|
398
|
+
*/
|
|
399
|
+
function writeUserMdAtomic(userMdPath, data) {
|
|
400
|
+
if (typeof userMdPath !== 'string' || userMdPath.length === 0) {
|
|
401
|
+
throw new Error('writeUserMdAtomic: userMdPath must be a non-empty string');
|
|
402
|
+
}
|
|
403
|
+
// Preserve any user-authored body below the existing frontmatter.
|
|
404
|
+
let preservedBody = '\n# USER.md\n';
|
|
405
|
+
try {
|
|
406
|
+
const existing = fs.readFileSync(userMdPath, 'utf8');
|
|
407
|
+
const fmRe = /^---\r?\n[\s\S]*?\r?\n---\r?\n?([\s\S]*)$/;
|
|
408
|
+
const m = existing.match(fmRe);
|
|
409
|
+
if (m && typeof m[1] === 'string') {
|
|
410
|
+
preservedBody = m[1];
|
|
411
|
+
// Ensure exactly one leading newline so the body starts on its
|
|
412
|
+
// own line below the frontmatter close delimiter.
|
|
413
|
+
if (preservedBody.length === 0 || preservedBody[0] !== '\n') {
|
|
414
|
+
preservedBody = '\n' + preservedBody;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
} catch (_e) {
|
|
418
|
+
// No existing file or unreadable. Use the default body shell.
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
const newFrontmatter = buildFrontmatter(data);
|
|
422
|
+
const newContent = newFrontmatter + '\n' + preservedBody.replace(/^\n/, '\n');
|
|
423
|
+
// ^ ensure exactly one '\n' between '---' and the body.
|
|
424
|
+
|
|
425
|
+
// Per-pid + random tmp name. The 'wx' flag on openSync would otherwise
|
|
426
|
+
// race with sibling writers in the same process if they shared a name.
|
|
427
|
+
const rnd = Math.random().toString(36).slice(2, 10);
|
|
428
|
+
const tmpPath = userMdPath + '.tmp.' + process.pid + '.' + rnd + '.user';
|
|
429
|
+
|
|
430
|
+
let fd;
|
|
431
|
+
try {
|
|
432
|
+
fd = fs.openSync(tmpPath, 'wx');
|
|
433
|
+
} catch (e) {
|
|
434
|
+
if (e && e.code === 'EEXIST') {
|
|
435
|
+
// Stale tmp from a prior crash on this exact pid+random slot.
|
|
436
|
+
// Astronomically unlikely with random suffix, but be defensive.
|
|
437
|
+
try { fs.unlinkSync(tmpPath); } catch (_) {}
|
|
438
|
+
fd = fs.openSync(tmpPath, 'wx');
|
|
439
|
+
} else {
|
|
440
|
+
throw e;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
try {
|
|
444
|
+
fs.writeSync(fd, newContent);
|
|
445
|
+
try { fs.fsyncSync(fd); } catch (_e) {
|
|
446
|
+
// ENOTSUP on tmpfs / overlayfs / Windows ImDisk. Best-effort
|
|
447
|
+
// durability is acceptable for USER.md (not load-bearing across
|
|
448
|
+
// power loss; persona will re-detect on next session).
|
|
449
|
+
}
|
|
450
|
+
} finally {
|
|
451
|
+
try { fs.closeSync(fd); } catch (_) {}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
try {
|
|
455
|
+
fs.renameSync(tmpPath, userMdPath);
|
|
456
|
+
} catch (e) {
|
|
457
|
+
try { fs.unlinkSync(tmpPath); } catch (_) {}
|
|
458
|
+
throw e;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// ---------- detectPersonaUpdate ----------
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* Decide whether a new persona signal should overwrite the current
|
|
466
|
+
* USER.md persona.
|
|
467
|
+
*
|
|
468
|
+
* The decision tree is deliberately ordered for early-exit + low-noise
|
|
469
|
+
* behavior:
|
|
470
|
+
*
|
|
471
|
+
* 1. current === null -> 'first_detection' (always update)
|
|
472
|
+
* 2. signal.source === 'user_*' -> 'user_override' (bypass threshold)
|
|
473
|
+
* 3. signal.persona === current -> 'no_change' (no rewrite)
|
|
474
|
+
* 4. confidence < update_threshold -> 'confidence_below_threshold'
|
|
475
|
+
* 5. consecutive < 3 -> 'awaiting_consecutive_signal'
|
|
476
|
+
* 6. consecutive >= 3 -> 'threshold_met' (update)
|
|
477
|
+
*
|
|
478
|
+
* Caller is responsible for incrementing consecutive_signal_count; this
|
|
479
|
+
* function reads the count off `signal` so the caller controls the
|
|
480
|
+
* window definition (per-turn vs per-session vs per-window).
|
|
481
|
+
*
|
|
482
|
+
* @param {object} args
|
|
483
|
+
* @param {object|null} args.current Current USER.md persona struct or null.
|
|
484
|
+
* @param {object} args.signal New persona detection signal.
|
|
485
|
+
* @returns {{shouldUpdate: boolean, reason: string}}
|
|
486
|
+
*/
|
|
487
|
+
function detectPersonaUpdate(args) {
|
|
488
|
+
const current = (args && args.current) || null;
|
|
489
|
+
const signal = (args && args.signal) || {};
|
|
490
|
+
|
|
491
|
+
if (current === null) {
|
|
492
|
+
return { shouldUpdate: true, reason: 'first_detection' };
|
|
493
|
+
}
|
|
494
|
+
if (signal.source === 'user_override') {
|
|
495
|
+
return { shouldUpdate: true, reason: 'user_override' };
|
|
496
|
+
}
|
|
497
|
+
if (signal.persona === current.persona) {
|
|
498
|
+
return { shouldUpdate: false, reason: 'no_change' };
|
|
499
|
+
}
|
|
500
|
+
const threshold = typeof current.update_threshold === 'number'
|
|
501
|
+
? current.update_threshold
|
|
502
|
+
: DEFAULT_UPDATE_THRESHOLD;
|
|
503
|
+
const conf = typeof signal.confidence === 'number' ? signal.confidence : 0.0;
|
|
504
|
+
if (conf < threshold) {
|
|
505
|
+
return { shouldUpdate: false, reason: 'confidence_below_threshold' };
|
|
506
|
+
}
|
|
507
|
+
const consec = typeof signal.consecutive_signal_count === 'number'
|
|
508
|
+
? signal.consecutive_signal_count : 0;
|
|
509
|
+
if (consec < CONSECUTIVE_SIGNAL_REQUIRED) {
|
|
510
|
+
return { shouldUpdate: false, reason: 'awaiting_consecutive_signal' };
|
|
511
|
+
}
|
|
512
|
+
return { shouldUpdate: true, reason: 'threshold_met' };
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
module.exports = {
|
|
516
|
+
emptyUser,
|
|
517
|
+
readUserMd,
|
|
518
|
+
writeUserMdAtomic,
|
|
519
|
+
detectPersonaUpdate,
|
|
520
|
+
// Constants exposed for callers that want to honor the same defaults.
|
|
521
|
+
DEFAULT_UPDATE_THRESHOLD,
|
|
522
|
+
CONSECUTIVE_SIGNAL_REQUIRED,
|
|
523
|
+
SCHEMA_VERSION,
|
|
524
|
+
};
|