@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,258 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MindrianOS Plugin -- Graph Operations
|
|
3
|
+
* Wraps existing Bash scripts via execSync AND lazygraph-ops.cjs for SQLite.
|
|
4
|
+
* build-graph: Bash script wrapper (JSON export for dashboard).
|
|
5
|
+
* index/rebuild/query/stats: SQLite via lazygraph-ops.cjs (per-project graph).
|
|
6
|
+
*
|
|
7
|
+
* All lazygraph functions use open-use-close pattern to avoid DB locking.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
'use strict';
|
|
11
|
+
|
|
12
|
+
const { execSync } = require('child_process');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
const lazygraph = require('./lazygraph-ops.cjs');
|
|
15
|
+
const { acquireLock, releaseLock } = require('./write-lock.cjs');
|
|
16
|
+
|
|
17
|
+
// Promise-chain write queue: serializes all SQLite writes.
|
|
18
|
+
// Read operations bypass this queue (SQLite WAL allows concurrent reads).
|
|
19
|
+
let writeQueue = Promise.resolve();
|
|
20
|
+
|
|
21
|
+
function enqueueWrite(roomDir, fn) {
|
|
22
|
+
const op = writeQueue.then(async () => {
|
|
23
|
+
acquireLock(roomDir);
|
|
24
|
+
try {
|
|
25
|
+
return await fn();
|
|
26
|
+
} finally {
|
|
27
|
+
releaseLock(roomDir);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
writeQueue = op.catch(() => {}); // prevent unhandled rejection from blocking queue
|
|
31
|
+
return op;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const SCRIPTS_DIR = path.resolve(__dirname, '../../scripts');
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Run build-graph script against a room directory.
|
|
38
|
+
* @param {string} roomDir - Path to room directory
|
|
39
|
+
* @param {string} [outputPath] - Output path for graph JSON (defaults to ./dashboard/graph.json)
|
|
40
|
+
* @returns {{ success: boolean, outputPath: string }} Result object
|
|
41
|
+
*/
|
|
42
|
+
function buildGraph(roomDir, outputPath) {
|
|
43
|
+
const resolved = path.resolve(roomDir);
|
|
44
|
+
const resolvedOutput = outputPath || './dashboard/graph.json';
|
|
45
|
+
const scriptPath = path.join(SCRIPTS_DIR, 'build-graph');
|
|
46
|
+
try {
|
|
47
|
+
execSync(`bash "${scriptPath}" "${resolved}" "${resolvedOutput}"`, {
|
|
48
|
+
timeout: 30000,
|
|
49
|
+
encoding: 'utf-8',
|
|
50
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
51
|
+
});
|
|
52
|
+
return { success: true, outputPath: resolvedOutput };
|
|
53
|
+
} catch (e) {
|
|
54
|
+
throw new Error(`build-graph failed: ${e.message}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Index a single artifact into the per-project SQLite graph.
|
|
60
|
+
* Opens graph, indexes artifact, closes graph (open-use-close pattern).
|
|
61
|
+
* @param {string} roomDir - Path to room directory
|
|
62
|
+
* @param {string} filePath - Path to .md file to index
|
|
63
|
+
* @returns {Promise<{ success: boolean, id: string, section: string, title: string }>}
|
|
64
|
+
*/
|
|
65
|
+
async function indexArtifact(roomDir, filePath) {
|
|
66
|
+
return enqueueWrite(roomDir, async () => {
|
|
67
|
+
const { db, conn } = await lazygraph.openGraph(roomDir);
|
|
68
|
+
try {
|
|
69
|
+
const result = await lazygraph.indexArtifact(conn, roomDir, filePath);
|
|
70
|
+
return { success: true, id: result.id, section: result.section, title: result.title };
|
|
71
|
+
} finally {
|
|
72
|
+
await lazygraph.closeGraph(db);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Rebuild the entire per-project SQLite graph from all room artifacts.
|
|
79
|
+
* Opens graph, clears and re-indexes everything, closes graph.
|
|
80
|
+
* @param {string} roomDir - Path to room directory
|
|
81
|
+
* @returns {Promise<{ success: boolean, artifacts: number, sections: number }>}
|
|
82
|
+
*/
|
|
83
|
+
async function rebuildGraph(roomDir) {
|
|
84
|
+
return enqueueWrite(roomDir, async () => {
|
|
85
|
+
const { db, conn } = await lazygraph.openGraph(roomDir);
|
|
86
|
+
try {
|
|
87
|
+
const result = await lazygraph.rebuildGraph(conn, roomDir);
|
|
88
|
+
return { success: true, artifacts: result.artifacts, sections: result.sections };
|
|
89
|
+
} finally {
|
|
90
|
+
await lazygraph.closeGraph(db);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Execute a SQL query against the per-project SQLite graph.
|
|
97
|
+
* Opens graph, runs query, closes graph.
|
|
98
|
+
* @param {string} roomDir - Path to room directory
|
|
99
|
+
* @param {string} sql - SQL query string
|
|
100
|
+
* @returns {Promise<{ success: boolean, rows: Array<object>, count: number }>}
|
|
101
|
+
*/
|
|
102
|
+
async function queryGraph(roomDir, sql) {
|
|
103
|
+
const { db, conn } = await lazygraph.openGraph(roomDir);
|
|
104
|
+
try {
|
|
105
|
+
const rows = await lazygraph.queryGraph(conn, sql);
|
|
106
|
+
return { success: true, rows, count: rows.length };
|
|
107
|
+
} finally {
|
|
108
|
+
await lazygraph.closeGraph(db);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Get graph statistics from the per-project SQLite graph.
|
|
114
|
+
* Opens graph, gets stats, closes graph.
|
|
115
|
+
* @param {string} roomDir - Path to room directory
|
|
116
|
+
* @returns {Promise<{ nodes: object, edges: object, total: { nodes: number, edges: number } }>}
|
|
117
|
+
*/
|
|
118
|
+
async function graphStats(roomDir) {
|
|
119
|
+
const { db, conn } = await lazygraph.openGraph(roomDir);
|
|
120
|
+
try {
|
|
121
|
+
return await lazygraph.graphStats(conn);
|
|
122
|
+
} finally {
|
|
123
|
+
await lazygraph.closeGraph(db);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Build graph.json from SQLite as primary source.
|
|
129
|
+
* Falls back to file-scanning buildGraph if SQLite fails.
|
|
130
|
+
* NOTE: This function calls the build-graph-from-sqlite.cjs script.
|
|
131
|
+
* @param {string} roomDir - Path to room directory
|
|
132
|
+
* @param {string} [outputPath] - Output path for graph JSON
|
|
133
|
+
* @returns {{ success: boolean, outputPath: string, source: string }}
|
|
134
|
+
*/
|
|
135
|
+
function buildGraphFromSQLite(roomDir, outputPath) {
|
|
136
|
+
const resolved = path.resolve(roomDir);
|
|
137
|
+
const resolvedOutput = outputPath || path.join(resolved, '.presentation', 'graph.json');
|
|
138
|
+
const scriptPath = path.join(SCRIPTS_DIR, 'build-graph-from-sqlite.cjs');
|
|
139
|
+
|
|
140
|
+
try {
|
|
141
|
+
execSync(`node "${scriptPath}" "${resolved}" "${resolvedOutput}"`, {
|
|
142
|
+
timeout: 10000,
|
|
143
|
+
encoding: 'utf-8',
|
|
144
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
// Check if file was actually created (script exits 0 even on graceful degradation)
|
|
148
|
+
const fs = require('fs');
|
|
149
|
+
if (fs.existsSync(resolvedOutput)) {
|
|
150
|
+
return { success: true, outputPath: resolvedOutput, source: 'sqlite' };
|
|
151
|
+
}
|
|
152
|
+
} catch (_e) {
|
|
153
|
+
// Fall through to file-scanning fallback
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Fallback to existing file-scanning build-graph
|
|
157
|
+
try {
|
|
158
|
+
const result = buildGraph(roomDir, resolvedOutput);
|
|
159
|
+
return { success: true, outputPath: resolvedOutput, source: 'fallback-filescan' };
|
|
160
|
+
} catch (_e2) {
|
|
161
|
+
return { success: false, outputPath: resolvedOutput, source: 'none' };
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Keep legacy name as alias for backward compatibility
|
|
166
|
+
const buildGraphFromKuzu = buildGraphFromSQLite;
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Persist a decision edge (INVALIDATES/CONFIRMS/DEFERRED) to SQLite.
|
|
170
|
+
* Uses enqueueWrite for serialized access + open-use-close pattern.
|
|
171
|
+
*
|
|
172
|
+
* @param {string} roomDir - Path to room directory
|
|
173
|
+
* @param {string} sourceArtifactId - Source artifact ID
|
|
174
|
+
* @param {string} targetArtifactId - Target artifact ID
|
|
175
|
+
* @param {'INVALIDATES'|'CONFIRMS'|'DEFERRED'} edgeType - Edge type from recordDecision
|
|
176
|
+
* @param {Object} [properties={}] - Edge properties (reason, timestamp)
|
|
177
|
+
* @returns {Promise<{ success: boolean, edgeType: string }>}
|
|
178
|
+
*/
|
|
179
|
+
async function persistDecisionEdge(roomDir, sourceArtifactId, targetArtifactId, edgeType, properties = {}) {
|
|
180
|
+
return enqueueWrite(roomDir, async () => {
|
|
181
|
+
const { db, conn } = await lazygraph.openGraph(roomDir);
|
|
182
|
+
try {
|
|
183
|
+
const edgeProps = JSON.stringify({
|
|
184
|
+
reason: properties.reason || '',
|
|
185
|
+
timestamp: properties.timestamp || new Date().toISOString(),
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
conn.prepare(
|
|
189
|
+
'INSERT INTO edges (source, target, type, properties) VALUES (?, ?, ?, ?) ON CONFLICT(source, target, type) DO UPDATE SET properties = excluded.properties'
|
|
190
|
+
).run(sourceArtifactId, targetArtifactId, edgeType, edgeProps);
|
|
191
|
+
|
|
192
|
+
return { success: true, edgeType };
|
|
193
|
+
} finally {
|
|
194
|
+
await lazygraph.closeGraph(db);
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Index an opportunity into the per-project SQLite graph.
|
|
201
|
+
* Creates Opportunity node, ADDRESSES edges to domain artifacts, IN_DOMAIN edge to section.
|
|
202
|
+
* Uses enqueueWrite for serialized SQLite access (open-use-close pattern).
|
|
203
|
+
*
|
|
204
|
+
* @param {string} roomDir - Path to room directory
|
|
205
|
+
* @param {object} opportunity - Opportunity object with problem_hash, domain, etc.
|
|
206
|
+
* @returns {Promise<{success: boolean, problem_hash: string}>}
|
|
207
|
+
*/
|
|
208
|
+
async function indexOpportunity(roomDir, opportunity) {
|
|
209
|
+
return enqueueWrite(roomDir, async () => {
|
|
210
|
+
const { db, conn } = await lazygraph.openGraph(roomDir);
|
|
211
|
+
try {
|
|
212
|
+
// Create opportunity node (upsert = idempotent)
|
|
213
|
+
if (typeof lazygraph.createOpportunityNode === 'function') {
|
|
214
|
+
await lazygraph.createOpportunityNode(conn, opportunity);
|
|
215
|
+
} else {
|
|
216
|
+
// Fallback: create as generic node
|
|
217
|
+
const props = JSON.stringify(opportunity);
|
|
218
|
+
conn.prepare(
|
|
219
|
+
'INSERT INTO nodes (id, type, properties) VALUES (?, ?, ?) ON CONFLICT(id) DO UPDATE SET properties = excluded.properties'
|
|
220
|
+
).run(opportunity.problem_hash, 'Opportunity', props);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// ADDRESSES edge: link to artifacts in the opportunity's domain section
|
|
224
|
+
if (opportunity.domain) {
|
|
225
|
+
const rows = conn.prepare(
|
|
226
|
+
"SELECT id FROM nodes WHERE type = 'Artifact' AND json_extract(properties, '$.section') = ? LIMIT 5"
|
|
227
|
+
).all(opportunity.domain);
|
|
228
|
+
|
|
229
|
+
for (const row of rows) {
|
|
230
|
+
if (typeof lazygraph.createAddressesEdge === 'function') {
|
|
231
|
+
await lazygraph.createAddressesEdge(conn, opportunity.problem_hash, row.id);
|
|
232
|
+
} else {
|
|
233
|
+
conn.prepare(
|
|
234
|
+
'INSERT INTO edges (source, target, type) VALUES (?, ?, ?) ON CONFLICT DO NOTHING'
|
|
235
|
+
).run(opportunity.problem_hash, row.id, 'ADDRESSES');
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// IN_DOMAIN edge: link to section node
|
|
241
|
+
if (opportunity.domain) {
|
|
242
|
+
if (typeof lazygraph.createInDomainEdge === 'function') {
|
|
243
|
+
await lazygraph.createInDomainEdge(conn, opportunity.problem_hash, opportunity.domain);
|
|
244
|
+
} else {
|
|
245
|
+
conn.prepare(
|
|
246
|
+
'INSERT INTO edges (source, target, type) VALUES (?, ?, ?) ON CONFLICT DO NOTHING'
|
|
247
|
+
).run(opportunity.problem_hash, opportunity.domain, 'IN_DOMAIN');
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return { success: true, problem_hash: opportunity.problem_hash };
|
|
252
|
+
} finally {
|
|
253
|
+
await lazygraph.closeGraph(db);
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
module.exports = { buildGraph, buildGraphFromKuzu, buildGraphFromSQLite, indexArtifact, rebuildGraph, queryGraph, graphStats, enqueueWrite, persistDecisionEdge, indexOpportunity };
|
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MindrianOS Plugin -- De Bono Hat Persistence
|
|
3
|
+
* Manages persistent hat state across sessions for each room.
|
|
4
|
+
*
|
|
5
|
+
* Storage layout:
|
|
6
|
+
* room/.mindrian/hats/{color}/STATE.md - Current hat state (focus, concerns, opportunities)
|
|
7
|
+
* room/.mindrian/hats/{color}/session-log/YYYY-MM-DD.md - Daily session findings
|
|
8
|
+
*
|
|
9
|
+
* Exports: loadHatState, saveHatState, logSession, loadAllHatStates, getRecentLogs
|
|
10
|
+
*
|
|
11
|
+
* Pure Node.js built-ins only (zero npm deps per Phase 10 decision).
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
'use strict';
|
|
15
|
+
|
|
16
|
+
const fs = require('fs');
|
|
17
|
+
const path = require('path');
|
|
18
|
+
|
|
19
|
+
const HAT_COLORS = ['white', 'red', 'black', 'yellow', 'green', 'blue'];
|
|
20
|
+
|
|
21
|
+
const HAT_LABELS = {
|
|
22
|
+
white: 'Facts & Data',
|
|
23
|
+
red: 'Emotions & Intuition',
|
|
24
|
+
black: 'Risks & Dangers',
|
|
25
|
+
yellow: 'Benefits & Opportunities',
|
|
26
|
+
green: 'Creativity & Alternatives',
|
|
27
|
+
blue: 'Process & Meta',
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
// Directory helpers
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Get the hat directory for a given color within a room.
|
|
36
|
+
* @param {string} roomDir - Absolute path to room directory
|
|
37
|
+
* @param {string} color - Hat color (white|red|black|yellow|green|blue)
|
|
38
|
+
* @returns {string}
|
|
39
|
+
*/
|
|
40
|
+
function hatDir(roomDir, color) {
|
|
41
|
+
return path.join(path.resolve(roomDir), '.mindrian', 'hats', color);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Ensure hat directories exist for a given color.
|
|
46
|
+
* @param {string} roomDir
|
|
47
|
+
* @param {string} color
|
|
48
|
+
*/
|
|
49
|
+
function ensureHatDirs(roomDir, color) {
|
|
50
|
+
const dir = hatDir(roomDir, color);
|
|
51
|
+
fs.mkdirSync(path.join(dir, 'session-log'), { recursive: true });
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
// STATE.md parsing / serialization
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Parse a hat STATE.md into structured data.
|
|
60
|
+
* @param {string} content - Raw markdown content
|
|
61
|
+
* @returns {{ current_focus: string, last_analysis: string, top_concerns: string[], top_opportunities: string[], methodology_notes: string[], session_count: number }}
|
|
62
|
+
*/
|
|
63
|
+
function parseHatState(content) {
|
|
64
|
+
const state = {
|
|
65
|
+
current_focus: '',
|
|
66
|
+
last_analysis: '',
|
|
67
|
+
top_concerns: [],
|
|
68
|
+
top_opportunities: [],
|
|
69
|
+
methodology_notes: [],
|
|
70
|
+
session_count: 0,
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
if (!content || typeof content !== 'string') return state;
|
|
74
|
+
|
|
75
|
+
// Parse frontmatter
|
|
76
|
+
const fmMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
77
|
+
if (fmMatch) {
|
|
78
|
+
const lines = fmMatch[1].split('\n');
|
|
79
|
+
for (const line of lines) {
|
|
80
|
+
const kvMatch = line.match(/^([a-z_]+):\s*(.*)$/);
|
|
81
|
+
if (kvMatch) {
|
|
82
|
+
const key = kvMatch[1];
|
|
83
|
+
const val = kvMatch[2].trim();
|
|
84
|
+
if (key === 'current_focus') state.current_focus = val;
|
|
85
|
+
if (key === 'last_analysis') state.last_analysis = val;
|
|
86
|
+
if (key === 'session_count') state.session_count = parseInt(val, 10) || 0;
|
|
87
|
+
}
|
|
88
|
+
const listMatch = line.match(/^\s+-\s+(.+)$/);
|
|
89
|
+
if (listMatch) {
|
|
90
|
+
// Determine which list this belongs to based on preceding key
|
|
91
|
+
// Simple heuristic: check what the last key was
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Parse sections from body
|
|
97
|
+
const bodyStart = content.indexOf('---', content.indexOf('---') + 3);
|
|
98
|
+
const body = bodyStart >= 0 ? content.slice(bodyStart + 3) : content;
|
|
99
|
+
|
|
100
|
+
const concernsMatch = body.match(/## Top Concerns\n([\s\S]*?)(?=\n## |$)/);
|
|
101
|
+
if (concernsMatch) {
|
|
102
|
+
state.top_concerns = concernsMatch[1]
|
|
103
|
+
.split('\n')
|
|
104
|
+
.filter(l => l.trim().startsWith('-'))
|
|
105
|
+
.map(l => l.replace(/^\s*-\s*/, '').trim())
|
|
106
|
+
.filter(Boolean);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const oppsMatch = body.match(/## Top Opportunities\n([\s\S]*?)(?=\n## |$)/);
|
|
110
|
+
if (oppsMatch) {
|
|
111
|
+
state.top_opportunities = oppsMatch[1]
|
|
112
|
+
.split('\n')
|
|
113
|
+
.filter(l => l.trim().startsWith('-'))
|
|
114
|
+
.map(l => l.replace(/^\s*-\s*/, '').trim())
|
|
115
|
+
.filter(Boolean);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const methMatch = body.match(/## Methodology Notes\n([\s\S]*?)(?=\n## |$)/);
|
|
119
|
+
if (methMatch) {
|
|
120
|
+
state.methodology_notes = methMatch[1]
|
|
121
|
+
.split('\n')
|
|
122
|
+
.filter(l => l.trim().startsWith('-'))
|
|
123
|
+
.map(l => l.replace(/^\s*-\s*/, '').trim())
|
|
124
|
+
.filter(Boolean);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return state;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Serialize hat state to STATE.md markdown content.
|
|
132
|
+
* @param {string} color
|
|
133
|
+
* @param {object} state
|
|
134
|
+
* @returns {string}
|
|
135
|
+
*/
|
|
136
|
+
function serializeHatState(color, state) {
|
|
137
|
+
const label = HAT_LABELS[color] || color;
|
|
138
|
+
const concerns = (state.top_concerns || []).map(c => `- ${c}`).join('\n') || '- None yet';
|
|
139
|
+
const opps = (state.top_opportunities || []).map(o => `- ${o}`).join('\n') || '- None yet';
|
|
140
|
+
const meth = (state.methodology_notes || []).map(m => `- ${m}`).join('\n') || '- None yet';
|
|
141
|
+
|
|
142
|
+
return `---
|
|
143
|
+
hat: ${color}
|
|
144
|
+
hat_label: ${label}
|
|
145
|
+
current_focus: ${state.current_focus || 'General analysis'}
|
|
146
|
+
last_analysis: ${state.last_analysis || 'never'}
|
|
147
|
+
session_count: ${state.session_count || 0}
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
# ${color.charAt(0).toUpperCase() + color.slice(1)} Hat -- ${label}
|
|
151
|
+
|
|
152
|
+
## Top Concerns
|
|
153
|
+
${concerns}
|
|
154
|
+
|
|
155
|
+
## Top Opportunities
|
|
156
|
+
${opps}
|
|
157
|
+
|
|
158
|
+
## Methodology Notes
|
|
159
|
+
${meth}
|
|
160
|
+
`;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// ---------------------------------------------------------------------------
|
|
164
|
+
// Core API
|
|
165
|
+
// ---------------------------------------------------------------------------
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Load the persistent state for a given hat color.
|
|
169
|
+
* Returns default state if no STATE.md exists yet.
|
|
170
|
+
* @param {string} roomDir - Absolute path to room directory
|
|
171
|
+
* @param {string} color - Hat color
|
|
172
|
+
* @returns {{ current_focus: string, last_analysis: string, top_concerns: string[], top_opportunities: string[], methodology_notes: string[], session_count: number }}
|
|
173
|
+
*/
|
|
174
|
+
function loadHatState(roomDir, color) {
|
|
175
|
+
if (!HAT_COLORS.includes(color)) {
|
|
176
|
+
return { error: `Invalid hat color: ${color}. Must be one of: ${HAT_COLORS.join(', ')}` };
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const statePath = path.join(hatDir(roomDir, color), 'STATE.md');
|
|
180
|
+
try {
|
|
181
|
+
const content = fs.readFileSync(statePath, 'utf-8');
|
|
182
|
+
return parseHatState(content);
|
|
183
|
+
} catch (e) {
|
|
184
|
+
// No state yet -- return defaults
|
|
185
|
+
return {
|
|
186
|
+
current_focus: 'General analysis',
|
|
187
|
+
last_analysis: 'never',
|
|
188
|
+
top_concerns: [],
|
|
189
|
+
top_opportunities: [],
|
|
190
|
+
methodology_notes: [],
|
|
191
|
+
session_count: 0,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Save hat state to disk. Creates directories if needed.
|
|
198
|
+
* @param {string} roomDir - Absolute path to room directory
|
|
199
|
+
* @param {string} color - Hat color
|
|
200
|
+
* @param {object} state - State to persist
|
|
201
|
+
* @returns {{ saved: boolean, path: string }}
|
|
202
|
+
*/
|
|
203
|
+
function saveHatState(roomDir, color, state) {
|
|
204
|
+
if (!HAT_COLORS.includes(color)) {
|
|
205
|
+
return { error: `Invalid hat color: ${color}` };
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
ensureHatDirs(roomDir, color);
|
|
209
|
+
const statePath = path.join(hatDir(roomDir, color), 'STATE.md');
|
|
210
|
+
|
|
211
|
+
// Merge with existing state to preserve fields not in this update
|
|
212
|
+
const existing = loadHatState(roomDir, color);
|
|
213
|
+
const merged = {
|
|
214
|
+
current_focus: state.current_focus || existing.current_focus,
|
|
215
|
+
last_analysis: state.last_analysis || new Date().toISOString().split('T')[0],
|
|
216
|
+
top_concerns: state.top_concerns || existing.top_concerns,
|
|
217
|
+
top_opportunities: state.top_opportunities || existing.top_opportunities,
|
|
218
|
+
methodology_notes: state.methodology_notes || existing.methodology_notes,
|
|
219
|
+
session_count: (state.session_count !== undefined) ? state.session_count : existing.session_count,
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
const content = serializeHatState(color, merged);
|
|
223
|
+
fs.writeFileSync(statePath, content, 'utf-8');
|
|
224
|
+
|
|
225
|
+
return { saved: true, path: statePath };
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Log a session's findings for a hat color. Appends to the daily log file.
|
|
230
|
+
* @param {string} roomDir - Absolute path to room directory
|
|
231
|
+
* @param {string} color - Hat color
|
|
232
|
+
* @param {{ focus: string, findings: string[], concerns: string[], opportunities: string[], artifact?: string }} findings
|
|
233
|
+
* @returns {{ logged: boolean, path: string }}
|
|
234
|
+
*/
|
|
235
|
+
function logSession(roomDir, color, findings) {
|
|
236
|
+
if (!HAT_COLORS.includes(color)) {
|
|
237
|
+
return { error: `Invalid hat color: ${color}` };
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
ensureHatDirs(roomDir, color);
|
|
241
|
+
|
|
242
|
+
const today = new Date().toISOString().split('T')[0];
|
|
243
|
+
const time = new Date().toISOString().split('T')[1].replace(/\.\d+Z/, 'Z');
|
|
244
|
+
const logPath = path.join(hatDir(roomDir, color), 'session-log', `${today}.md`);
|
|
245
|
+
|
|
246
|
+
const label = HAT_LABELS[color] || color;
|
|
247
|
+
const entry = [
|
|
248
|
+
'',
|
|
249
|
+
`## Session at ${time}`,
|
|
250
|
+
'',
|
|
251
|
+
`**Focus:** ${findings.focus || 'General analysis'}`,
|
|
252
|
+
findings.artifact ? `**Artifact:** ${findings.artifact}` : '',
|
|
253
|
+
'',
|
|
254
|
+
'### Findings',
|
|
255
|
+
...(findings.findings || []).map(f => `- ${f}`),
|
|
256
|
+
'',
|
|
257
|
+
'### Concerns',
|
|
258
|
+
...(findings.concerns || []).map(c => `- ${c}`),
|
|
259
|
+
'',
|
|
260
|
+
'### Opportunities',
|
|
261
|
+
...(findings.opportunities || []).map(o => `- ${o}`),
|
|
262
|
+
'',
|
|
263
|
+
'---',
|
|
264
|
+
].filter(l => l !== undefined).join('\n');
|
|
265
|
+
|
|
266
|
+
// If file exists, append. Otherwise create with header.
|
|
267
|
+
let content;
|
|
268
|
+
try {
|
|
269
|
+
content = fs.readFileSync(logPath, 'utf-8');
|
|
270
|
+
content += '\n' + entry;
|
|
271
|
+
} catch (e) {
|
|
272
|
+
content = `# ${color.charAt(0).toUpperCase() + color.slice(1)} Hat -- ${label} Session Log (${today})\n` + entry;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
fs.writeFileSync(logPath, content, 'utf-8');
|
|
276
|
+
|
|
277
|
+
// Update session count in STATE.md
|
|
278
|
+
const state = loadHatState(roomDir, color);
|
|
279
|
+
state.session_count = (state.session_count || 0) + 1;
|
|
280
|
+
state.last_analysis = today;
|
|
281
|
+
|
|
282
|
+
// Merge new concerns/opportunities into state (keep last 5 each)
|
|
283
|
+
if (findings.concerns && findings.concerns.length > 0) {
|
|
284
|
+
const merged = [...findings.concerns, ...(state.top_concerns || [])];
|
|
285
|
+
state.top_concerns = [...new Set(merged)].slice(0, 5);
|
|
286
|
+
}
|
|
287
|
+
if (findings.opportunities && findings.opportunities.length > 0) {
|
|
288
|
+
const merged = [...findings.opportunities, ...(state.top_opportunities || [])];
|
|
289
|
+
state.top_opportunities = [...new Set(merged)].slice(0, 5);
|
|
290
|
+
}
|
|
291
|
+
if (findings.focus) {
|
|
292
|
+
state.current_focus = findings.focus;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
saveHatState(roomDir, color, state);
|
|
296
|
+
|
|
297
|
+
return { logged: true, path: logPath };
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Load all 6 hat states for a room. Returns map of color -> state.
|
|
302
|
+
* @param {string} roomDir
|
|
303
|
+
* @returns {Object<string, object>}
|
|
304
|
+
*/
|
|
305
|
+
function loadAllHatStates(roomDir) {
|
|
306
|
+
const states = {};
|
|
307
|
+
for (const color of HAT_COLORS) {
|
|
308
|
+
states[color] = loadHatState(roomDir, color);
|
|
309
|
+
}
|
|
310
|
+
return states;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Get recent session logs for a hat (last N days).
|
|
315
|
+
* @param {string} roomDir
|
|
316
|
+
* @param {string} color
|
|
317
|
+
* @param {number} [days=7] - How many days back to look
|
|
318
|
+
* @returns {Array<{ date: string, content: string }>}
|
|
319
|
+
*/
|
|
320
|
+
function getRecentLogs(roomDir, color, days = 7) {
|
|
321
|
+
if (!HAT_COLORS.includes(color)) return [];
|
|
322
|
+
|
|
323
|
+
const logDir = path.join(hatDir(roomDir, color), 'session-log');
|
|
324
|
+
let files;
|
|
325
|
+
try {
|
|
326
|
+
files = fs.readdirSync(logDir).filter(f => f.endsWith('.md')).sort().reverse();
|
|
327
|
+
} catch (e) {
|
|
328
|
+
return [];
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const cutoff = new Date();
|
|
332
|
+
cutoff.setDate(cutoff.getDate() - days);
|
|
333
|
+
const cutoffStr = cutoff.toISOString().split('T')[0];
|
|
334
|
+
|
|
335
|
+
const logs = [];
|
|
336
|
+
for (const file of files) {
|
|
337
|
+
const date = file.replace('.md', '');
|
|
338
|
+
if (date < cutoffStr) break;
|
|
339
|
+
try {
|
|
340
|
+
const content = fs.readFileSync(path.join(logDir, file), 'utf-8');
|
|
341
|
+
logs.push({ date, content });
|
|
342
|
+
} catch (e) {
|
|
343
|
+
// skip unreadable
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
return logs;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// ---------------------------------------------------------------------------
|
|
351
|
+
// Exports
|
|
352
|
+
// ---------------------------------------------------------------------------
|
|
353
|
+
|
|
354
|
+
module.exports = {
|
|
355
|
+
HAT_COLORS,
|
|
356
|
+
HAT_LABELS,
|
|
357
|
+
loadHatState,
|
|
358
|
+
saveHatState,
|
|
359
|
+
logSession,
|
|
360
|
+
loadAllHatStates,
|
|
361
|
+
getRecentLogs,
|
|
362
|
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MindrianOS Plugin — Shared Core Helpers
|
|
3
|
+
* Pure Node.js built-ins only. No npm dependencies.
|
|
4
|
+
*
|
|
5
|
+
* Replicates the GSD gsd-tools.cjs output pattern:
|
|
6
|
+
* - JSON to stdout for structured data
|
|
7
|
+
* - Large payloads (>50KB) written to tmpfile with @file: prefix
|
|
8
|
+
* - Errors to stderr with exit 1
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
'use strict';
|
|
12
|
+
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
const os = require('os');
|
|
16
|
+
|
|
17
|
+
/** Root of the plugin repository */
|
|
18
|
+
const PLUGIN_ROOT = path.resolve(__dirname, '../..');
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Output structured result to stdout.
|
|
22
|
+
* If raw mode, write rawValue directly.
|
|
23
|
+
* Otherwise JSON-stringify; if >50KB, write to tmpfile with @file: prefix.
|
|
24
|
+
*/
|
|
25
|
+
function output(result, raw, rawValue) {
|
|
26
|
+
if (raw && rawValue !== undefined) {
|
|
27
|
+
process.stdout.write(String(rawValue));
|
|
28
|
+
} else {
|
|
29
|
+
const json = JSON.stringify(result, null, 2);
|
|
30
|
+
if (json.length > 50000) {
|
|
31
|
+
const tmpPath = path.join(os.tmpdir(), `mindrian-${Date.now()}.json`);
|
|
32
|
+
fs.writeFileSync(tmpPath, json, 'utf-8');
|
|
33
|
+
process.stdout.write('@file:' + tmpPath);
|
|
34
|
+
} else {
|
|
35
|
+
process.stdout.write(json);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
process.exit(0);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Write error message to stderr and exit 1.
|
|
43
|
+
*/
|
|
44
|
+
function error(msg) {
|
|
45
|
+
process.stderr.write('ERROR: ' + msg + '\n');
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Read file contents safely. Returns string or null if not found.
|
|
51
|
+
*/
|
|
52
|
+
function safeReadFile(filePath) {
|
|
53
|
+
try {
|
|
54
|
+
return fs.readFileSync(filePath, 'utf-8');
|
|
55
|
+
} catch (e) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
module.exports = { output, error, safeReadFile, PLUGIN_ROOT };
|