@monoes/monomindcli 1.10.54 → 1.10.55
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/agents/optimization/benchmark-suite.md +2 -0
- package/.claude/agents/optimization/load-balancer.md +2 -0
- package/.claude/agents/optimization/performance-monitor.md +2 -0
- package/.claude/agents/optimization/resource-allocator.md +3 -1
- package/.claude/agents/optimization/topology-optimizer.md +2 -0
- package/.claude/commands/mastermind/_repeat.md +21 -0
- package/.claude/commands/mastermind/_taskfile.md +235 -0
- package/.claude/commands/mastermind/adr.md +11 -0
- package/.claude/commands/mastermind/approve.md +94 -0
- package/.claude/commands/mastermind/autodev.md +32 -0
- package/.claude/commands/mastermind/budget.md +7 -0
- package/.claude/commands/mastermind/code-review.md +317 -0
- package/.claude/commands/mastermind/createorg.md +40 -1
- package/.claude/commands/mastermind/createtask.md +383 -0
- package/.claude/commands/mastermind/debug.md +22 -0
- package/.claude/commands/mastermind/design.md +20 -0
- package/.claude/commands/mastermind/do.md +526 -0
- package/.claude/commands/mastermind/execute.md +20 -0
- package/.claude/commands/mastermind/finish.md +20 -0
- package/.claude/commands/mastermind/graph-status.md +7 -0
- package/.claude/commands/mastermind/help.md +118 -0
- package/.claude/commands/mastermind/ideate.md +261 -0
- package/.claude/commands/mastermind/improve.md +345 -0
- package/.claude/commands/mastermind/loops.md +7 -0
- package/.claude/commands/mastermind/master.md +186 -6
- package/.claude/commands/mastermind/memory.md +230 -0
- package/.claude/commands/mastermind/plan.md +26 -0
- package/.claude/commands/mastermind/receive-review.md +20 -0
- package/.claude/commands/mastermind/repeat.md +257 -0
- package/.claude/commands/mastermind/runorg.md +3 -0
- package/.claude/commands/mastermind/skill-builder.md +20 -0
- package/.claude/commands/mastermind/specialagents.md +125 -0
- package/.claude/commands/mastermind/swarm.md +161 -0
- package/.claude/commands/mastermind/taskdev.md +26 -0
- package/.claude/commands/mastermind/tdd.md +22 -0
- package/.claude/commands/mastermind/techport.md +4 -0
- package/.claude/commands/mastermind/understand.md +139 -0
- package/.claude/commands/mastermind/verify.md +22 -0
- package/.claude/commands/mastermind/worktree.md +20 -0
- package/.claude/helpers/handlers/graph-status-handler.cjs +2 -1
- package/.claude/helpers/hook-handler.cjs +19 -0
- package/.claude/helpers/skill-registry.json +23 -0
- package/.claude/helpers/statusline.cjs +1 -1
- package/.claude/skills/mastermind/approve.md +15 -7
- package/.claude/skills/mastermind/autodev.md +534 -0
- package/.claude/skills/mastermind/createorg.md +21 -5
- package/.claude/skills/mastermind/debug.md +232 -0
- package/.claude/skills/mastermind/design.md +187 -0
- package/.claude/skills/mastermind/execute.md +104 -0
- package/.claude/skills/mastermind/finish.md +251 -0
- package/.claude/skills/mastermind/plan.md +180 -0
- package/.claude/skills/mastermind/receive-review.md +213 -0
- package/.claude/skills/mastermind/runorg.md +23 -8
- package/.claude/skills/mastermind/skill-builder.md +274 -0
- package/.claude/skills/mastermind/taskdev.md +307 -0
- package/.claude/skills/mastermind/tdd.md +394 -0
- package/.claude/skills/mastermind/verify.md +196 -0
- package/.claude/skills/mastermind/worktree.md +160 -132
- package/README.md +320 -253
- package/dist/src/commands/analyze.d.ts.map +1 -1
- package/dist/src/commands/analyze.js +9 -2
- package/dist/src/commands/analyze.js.map +1 -1
- package/dist/src/commands/benchmark.js.map +1 -1
- package/dist/src/commands/completions.js +1 -1
- package/dist/src/commands/guidance.js +7 -7
- package/dist/src/commands/hooks.d.ts.map +1 -1
- package/dist/src/commands/hooks.js +16 -3
- package/dist/src/commands/hooks.js.map +1 -1
- package/dist/src/commands/index.d.ts +3 -2
- package/dist/src/commands/index.d.ts.map +1 -1
- package/dist/src/commands/index.js +7 -0
- package/dist/src/commands/index.js.map +1 -1
- package/dist/src/commands/init.d.ts.map +1 -1
- package/dist/src/commands/init.js +47 -13
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/commands/neural.d.ts.map +1 -1
- package/dist/src/commands/neural.js +100 -14
- package/dist/src/commands/neural.js.map +1 -1
- package/dist/src/commands/platforms.d.ts +11 -0
- package/dist/src/commands/platforms.d.ts.map +1 -0
- package/dist/src/commands/platforms.js +195 -0
- package/dist/src/commands/platforms.js.map +1 -0
- package/dist/src/commands/ruvector/backup.js.map +1 -1
- package/dist/src/commands/ruvector/benchmark.js.map +1 -1
- package/dist/src/commands/ruvector/init.js.map +1 -1
- package/dist/src/commands/ruvector/migrate.js.map +1 -1
- package/dist/src/commands/ruvector/optimize.js.map +1 -1
- package/dist/src/commands/ruvector/status.js.map +1 -1
- package/dist/src/commands/update.js +6 -6
- package/dist/src/init/executor.d.ts.map +1 -1
- package/dist/src/init/executor.js +28 -0
- package/dist/src/init/executor.js.map +1 -1
- package/dist/src/init/statusline-generator.js +1 -1
- package/dist/src/init/types.d.ts +1 -0
- package/dist/src/init/types.d.ts.map +1 -1
- package/dist/src/mcp-server.d.ts.map +1 -1
- package/dist/src/mcp-server.js +92 -0
- package/dist/src/mcp-server.js.map +1 -1
- package/dist/src/mcp-tools/hive-mind-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/hive-mind-tools.js +52 -0
- package/dist/src/mcp-tools/hive-mind-tools.js.map +1 -1
- package/dist/src/mcp-tools/hooks-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/hooks-tools.js +106 -5
- package/dist/src/mcp-tools/hooks-tools.js.map +1 -1
- package/dist/src/mcp-tools/index.d.ts +0 -5
- package/dist/src/mcp-tools/index.d.ts.map +1 -1
- package/dist/src/mcp-tools/index.js +0 -5
- package/dist/src/mcp-tools/index.js.map +1 -1
- package/dist/src/mcp-tools/monograph-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/monograph-tools.js +507 -5587
- package/dist/src/mcp-tools/monograph-tools.js.map +1 -1
- package/dist/src/mcp-tools/neural-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/neural-tools.js +64 -4
- package/dist/src/mcp-tools/neural-tools.js.map +1 -1
- package/dist/src/mcp-tools/security-tools.js +4 -4
- package/dist/src/memory/intelligence.d.ts +2 -2
- package/dist/src/memory/intelligence.d.ts.map +1 -1
- package/dist/src/memory/intelligence.js +108 -3
- package/dist/src/memory/intelligence.js.map +1 -1
- package/dist/src/memory/memory-bridge.js +1 -1
- package/dist/src/memory/memory-bridge.js.map +1 -1
- package/dist/src/memory/sona-optimizer.d.ts +1 -10
- package/dist/src/memory/sona-optimizer.d.ts.map +1 -1
- package/dist/src/memory/sona-optimizer.js +0 -46
- package/dist/src/memory/sona-optimizer.js.map +1 -1
- package/dist/src/runtime/headless.js +3 -3
- package/dist/src/ruvector/diff-classifier.d.ts +0 -2
- package/dist/src/ruvector/diff-classifier.d.ts.map +1 -1
- package/dist/src/ruvector/diff-classifier.js +2 -14
- package/dist/src/ruvector/diff-classifier.js.map +1 -1
- package/dist/src/ruvector/index.d.ts +26 -9
- package/dist/src/ruvector/index.d.ts.map +1 -1
- package/dist/src/ruvector/index.js +3 -21
- package/dist/src/ruvector/index.js.map +1 -1
- package/dist/src/ruvector/ruvllm-wasm.js +2 -2
- package/dist/src/ruvector/ruvllm-wasm.js.map +1 -1
- package/dist/src/types.d.ts +0 -15
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/types.js +0 -18
- package/dist/src/types.js.map +1 -1
- package/dist/src/ui/dashboard.html +8763 -9766
- package/dist/src/ui/orgs.html +1 -1
- package/dist/src/ui/server.mjs +504 -35
- package/dist/src/update/index.js +1 -1
- package/dist/src/update/validator.js +8 -8
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/.claude/agents/academic/academic-anthropologist.md +0 -126
- package/.claude/agents/academic/academic-geographer.md +0 -128
- package/.claude/agents/academic/academic-historian.md +0 -124
- package/.claude/agents/academic/academic-narratologist.md +0 -119
- package/.claude/agents/academic/academic-psychologist.md +0 -119
- package/.claude/agents/analysis/analyze-code-quality.md +0 -58
- package/.claude/agents/analysis/code-analyzer.md +0 -189
- package/.claude/agents/analysis/code-review/analyze-code-quality.md +0 -58
- package/.claude/agents/consensus/performance-benchmarker.md +0 -831
- package/.claude/agents/data/ml/data-ml-model.md +0 -76
- package/.claude/agents/development/dev-backend-api.md +0 -178
- package/.claude/agents/devops/ci-cd/ops-cicd-github.md +0 -52
- package/.claude/agents/documentation/api-docs/docs-api-openapi.md +0 -63
- package/.claude/agents/game-development/blender/blender-addon-engineer.md +0 -235
- package/.claude/agents/game-development/game-audio-engineer.md +0 -265
- package/.claude/agents/game-development/game-designer.md +0 -168
- package/.claude/agents/game-development/godot/godot-gameplay-scripter.md +0 -335
- package/.claude/agents/game-development/godot/godot-multiplayer-engineer.md +0 -298
- package/.claude/agents/game-development/godot/godot-shader-developer.md +0 -267
- package/.claude/agents/game-development/level-designer.md +0 -209
- package/.claude/agents/game-development/narrative-designer.md +0 -244
- package/.claude/agents/game-development/roblox-studio/roblox-avatar-creator.md +0 -298
- package/.claude/agents/game-development/roblox-studio/roblox-experience-designer.md +0 -306
- package/.claude/agents/game-development/roblox-studio/roblox-systems-scripter.md +0 -326
- package/.claude/agents/game-development/technical-artist.md +0 -230
- package/.claude/agents/game-development/unity/unity-architect.md +0 -272
- package/.claude/agents/game-development/unity/unity-editor-tool-developer.md +0 -311
- package/.claude/agents/game-development/unity/unity-multiplayer-engineer.md +0 -322
- package/.claude/agents/game-development/unity/unity-shader-graph-artist.md +0 -270
- package/.claude/agents/game-development/unreal-engine/unreal-multiplayer-architect.md +0 -314
- package/.claude/agents/game-development/unreal-engine/unreal-systems-engineer.md +0 -311
- package/.claude/agents/game-development/unreal-engine/unreal-technical-artist.md +0 -257
- package/.claude/agents/game-development/unreal-engine/unreal-world-builder.md +0 -274
- package/.claude/agents/github/release-swarm.md +0 -597
- package/.claude/agents/goal/agent.md +0 -804
- package/.claude/agents/goal/code-goal-planner.md +0 -445
- package/.claude/agents/marketing/marketing-ai-citation-strategist.md +0 -171
- package/.claude/agents/marketing/marketing-app-store-optimizer.md +0 -322
- package/.claude/agents/marketing/marketing-baidu-seo-specialist.md +0 -227
- package/.claude/agents/marketing/marketing-bilibili-content-strategist.md +0 -200
- package/.claude/agents/marketing/marketing-book-co-author.md +0 -111
- package/.claude/agents/marketing/marketing-carousel-growth-engine.md +0 -200
- package/.claude/agents/marketing/marketing-china-ecommerce-operator.md +0 -284
- package/.claude/agents/marketing/marketing-content-creator.md +0 -67
- package/.claude/agents/marketing/marketing-cross-border-ecommerce.md +0 -260
- package/.claude/agents/marketing/marketing-douyin-strategist.md +0 -150
- package/.claude/agents/marketing/marketing-growth-hacker.md +0 -54
- package/.claude/agents/marketing/marketing-instagram-curator.md +0 -114
- package/.claude/agents/marketing/marketing-kuaishou-strategist.md +0 -224
- package/.claude/agents/marketing/marketing-linkedin-content-creator.md +0 -215
- package/.claude/agents/marketing/marketing-livestream-commerce-coach.md +0 -306
- package/.claude/agents/marketing/marketing-podcast-strategist.md +0 -278
- package/.claude/agents/marketing/marketing-private-domain-operator.md +0 -309
- package/.claude/agents/marketing/marketing-reddit-community-builder.md +0 -124
- package/.claude/agents/marketing/marketing-seo-specialist.md +0 -279
- package/.claude/agents/marketing/marketing-short-video-editing-coach.md +0 -413
- package/.claude/agents/marketing/marketing-social-media-strategist.md +0 -125
- package/.claude/agents/marketing/marketing-tiktok-strategist.md +0 -126
- package/.claude/agents/marketing/marketing-twitter-engager.md +0 -127
- package/.claude/agents/marketing/marketing-wechat-official-account.md +0 -146
- package/.claude/agents/marketing/marketing-weibo-strategist.md +0 -241
- package/.claude/agents/marketing/marketing-xiaohongshu-specialist.md +0 -139
- package/.claude/agents/marketing/marketing-zhihu-strategist.md +0 -163
- package/.claude/agents/neural/safla-neural.md +0 -74
- package/.claude/agents/paid-media/paid-media-auditor.md +0 -71
- package/.claude/agents/paid-media/paid-media-creative-strategist.md +0 -71
- package/.claude/agents/paid-media/paid-media-paid-social-strategist.md +0 -71
- package/.claude/agents/paid-media/paid-media-ppc-strategist.md +0 -71
- package/.claude/agents/paid-media/paid-media-programmatic-buyer.md +0 -71
- package/.claude/agents/paid-media/paid-media-search-query-analyst.md +0 -71
- package/.claude/agents/paid-media/paid-media-tracking-specialist.md +0 -71
- package/.claude/agents/payments/agentic-payments.md +0 -126
- package/.claude/agents/product/product-behavioral-nudge-engine.md +0 -81
- package/.claude/agents/product/product-feedback-synthesizer.md +0 -119
- package/.claude/agents/product/product-manager.md +0 -469
- package/.claude/agents/product/product-sprint-prioritizer.md +0 -154
- package/.claude/agents/product/product-trend-researcher.md +0 -159
- package/.claude/agents/project-management/project-management-experiment-tracker.md +0 -199
- package/.claude/agents/project-management/project-management-jira-workflow-steward.md +0 -231
- package/.claude/agents/project-management/project-management-project-shepherd.md +0 -195
- package/.claude/agents/project-management/project-management-studio-operations.md +0 -201
- package/.claude/agents/project-management/project-management-studio-producer.md +0 -204
- package/.claude/agents/project-management/project-manager-senior.md +0 -136
- package/.claude/agents/reasoning/agent.md +0 -804
- package/.claude/agents/reasoning/goal-planner.md +0 -73
- package/.claude/agents/sales/sales-account-strategist.md +0 -228
- package/.claude/agents/sales/sales-coach.md +0 -272
- package/.claude/agents/sales/sales-deal-strategist.md +0 -181
- package/.claude/agents/sales/sales-discovery-coach.md +0 -226
- package/.claude/agents/sales/sales-engineer.md +0 -183
- package/.claude/agents/sales/sales-outbound-strategist.md +0 -202
- package/.claude/agents/sales/sales-pipeline-analyst.md +0 -268
- package/.claude/agents/sales/sales-proposal-strategist.md +0 -218
- package/.claude/agents/sona/sona-learning-optimizer.md +0 -65
- package/.claude/agents/spatial-computing/macos-spatial-metal-engineer.md +0 -338
- package/.claude/agents/spatial-computing/terminal-integration-specialist.md +0 -71
- package/.claude/agents/spatial-computing/visionos-spatial-engineer.md +0 -55
- package/.claude/agents/specialists/memory-specialist.md +0 -298
- package/.claude/agents/specialists/performance-engineer.md +0 -387
- package/.claude/agents/specialists/queen-coordinator.md +0 -67
- package/.claude/agents/specialists/security-architect.md +0 -154
- package/.claude/agents/specialized/accounts-payable-agent.md +0 -186
- package/.claude/agents/specialized/corporate-training-designer.md +0 -193
- package/.claude/agents/specialized/data-consolidation-agent.md +0 -61
- package/.claude/agents/specialized/government-digital-presales-consultant.md +0 -364
- package/.claude/agents/specialized/healthcare-marketing-compliance.md +0 -396
- package/.claude/agents/specialized/recruitment-specialist.md +0 -510
- package/.claude/agents/specialized/report-distribution-agent.md +0 -66
- package/.claude/agents/specialized/sales-data-extraction-agent.md +0 -68
- package/.claude/agents/specialized/specialized-french-consulting-market.md +0 -193
- package/.claude/agents/specialized/specialized-korean-business-navigator.md +0 -217
- package/.claude/agents/specialized/specialized-salesforce-architect.md +0 -181
- package/.claude/agents/specialized/study-abroad-advisor.md +0 -283
- package/.claude/agents/specialized/supply-chain-strategist.md +0 -583
- package/.claude/agents/sublinear/consensus-coordinator.md +0 -333
- package/.claude/agents/sublinear/matrix-optimizer.md +0 -180
- package/.claude/agents/sublinear/pagerank-analyzer.md +0 -295
- package/.claude/agents/sublinear/performance-optimizer.md +0 -363
- package/.claude/agents/sublinear/trading-predictor.md +0 -242
- package/.claude/agents/support/support-analytics-reporter.md +0 -366
- package/.claude/agents/support/support-executive-summary-generator.md +0 -213
- package/.claude/agents/support/support-finance-tracker.md +0 -443
- package/.claude/agents/support/support-infrastructure-maintainer.md +0 -619
- package/.claude/agents/support/support-legal-compliance-checker.md +0 -589
- package/.claude/agents/support/support-support-responder.md +0 -586
- package/.claude/agents/swarm/adaptive-coordinator.md +0 -364
- package/.claude/agents/swarm/hierarchical-coordinator.md +0 -318
- package/.claude/agents/templates/github-pr-manager.md +0 -155
- package/.claude/agents/templates/memory-coordinator.md +0 -163
- package/.claude/agents/templates/migration-plan.md +0 -724
- package/.claude/agents/templates/orchestrator-task.md +0 -120
- package/.claude/agents/templates/performance-analyzer.md +0 -179
- package/.claude/agents/templates/sparc-coordinator.md +0 -163
- package/.claude/agents/testing/testing-reality-checker.md +0 -237
- package/.claude/commands/analysis/token-efficiency.md +0 -42
- package/.claude/commands/optimization/README.md +0 -73
- package/.claude/commands/optimization/parallel-execution.md +0 -76
- package/.claude/commands/swarm/swarm-analysis.md +0 -62
- package/.claude/commands/swarm/swarm-background.md +0 -65
- package/.claude/commands/swarm/swarm-modes.md +0 -67
- package/.claude/commands/swarm/swarm-monitor.md +0 -54
- package/.claude/commands/swarm/swarm-status.md +0 -44
- package/.claude/commands/swarm/swarm-strategies.md +0 -76
- package/.claude/commands/training/model-update.md +0 -78
- package/.claude/commands/training/pattern-learn.md +0 -69
- package/.claude/commands/training/specialization.md +0 -92
- package/.claude/commands/verify/check.md +0 -106
- package/.claude/commands/verify/start.md +0 -105
- package/.claude/helpers/README.md +0 -105
- package/.claude/helpers/context-persistence-hook.mjs +0 -1988
- package/.claude/helpers/intelligence.cjs +0 -247
- package/.claude/helpers/learning-service.mjs +0 -1302
- package/.claude/helpers/memory-palace.cjs +0 -461
- package/.claude/helpers/memory.cjs +0 -84
- package/.claude/helpers/metrics-db.mjs +0 -488
- package/.claude/helpers/router.cjs +0 -559
- package/.claude/helpers/session.cjs +0 -126
- package/.claude/helpers/swarm-hooks.sh +0 -761
- package/.claude/helpers/toggle-statusline.cjs +0 -58
- package/.claude/helpers/token-tracker.cjs +0 -934
- package/.claude/skills/agentdb-advanced/SKILL.md +0 -549
- package/.claude/skills/agentdb-learning/SKILL.md +0 -544
- package/.claude/skills/agentdb-memory-patterns/SKILL.md +0 -337
- package/.claude/skills/agentdb-optimization/SKILL.md +0 -508
- package/.claude/skills/agentdb-vector-search/SKILL.md +0 -335
- package/.claude/skills/agentic-integration/SKILL.md +0 -265
- package/.claude/skills/cli-modernization/SKILL.md +0 -950
- package/.claude/skills/core-implementation/SKILL.md +0 -892
- package/.claude/skills/ddd-architecture/SKILL.md +0 -444
- package/.claude/skills/github-code-review/SKILL.md +0 -1147
- package/.claude/skills/github-multi-repo/SKILL.md +0 -912
- package/.claude/skills/github-project-management/SKILL.md +0 -1245
- package/.claude/skills/github-release-management/SKILL.md +0 -1118
- package/.claude/skills/github-workflow-automation/SKILL.md +0 -1107
- package/.claude/skills/mcp-optimization/SKILL.md +0 -837
- package/.claude/skills/memory-unification/SKILL.md +0 -196
- package/.claude/skills/performance-optimization/SKILL.md +0 -416
- package/.claude/skills/reasoningbank-agentdb/SKILL.md +0 -444
- package/.claude/skills/reasoningbank-intelligence/SKILL.md +0 -199
- package/.claude/skills/security-hardening/SKILL.md +0 -101
- package/.claude/skills/stream-chain/SKILL.md +0 -560
- package/.claude/skills/swarm-coordination/SKILL.md +0 -451
- package/bundled-graph/dist/src/analyze.d.ts +0 -32
- package/bundled-graph/dist/src/analyze.d.ts.map +0 -1
- package/bundled-graph/dist/src/analyze.js +0 -297
- package/bundled-graph/dist/src/analyze.js.map +0 -1
- package/bundled-graph/dist/src/build.d.ts +0 -8
- package/bundled-graph/dist/src/build.d.ts.map +0 -1
- package/bundled-graph/dist/src/build.js.map +0 -1
- package/bundled-graph/dist/src/cache.d.ts +0 -12
- package/bundled-graph/dist/src/cache.d.ts.map +0 -1
- package/bundled-graph/dist/src/cache.js +0 -43
- package/bundled-graph/dist/src/cache.js.map +0 -1
- package/bundled-graph/dist/src/cluster.d.ts +0 -5
- package/bundled-graph/dist/src/cluster.d.ts.map +0 -1
- package/bundled-graph/dist/src/cluster.js.map +0 -1
- package/bundled-graph/dist/src/detect.d.ts +0 -21
- package/bundled-graph/dist/src/detect.d.ts.map +0 -1
- package/bundled-graph/dist/src/detect.js +0 -195
- package/bundled-graph/dist/src/detect.js.map +0 -1
- package/bundled-graph/dist/src/export.d.ts +0 -21
- package/bundled-graph/dist/src/export.d.ts.map +0 -1
- package/bundled-graph/dist/src/export.js +0 -68
- package/bundled-graph/dist/src/export.js.map +0 -1
- package/bundled-graph/dist/src/extract/index.d.ts +0 -20
- package/bundled-graph/dist/src/extract/index.d.ts.map +0 -1
- package/bundled-graph/dist/src/extract/index.js +0 -158
- package/bundled-graph/dist/src/extract/index.js.map +0 -1
- package/bundled-graph/dist/src/extract/languages/c.d.ts +0 -3
- package/bundled-graph/dist/src/extract/languages/c.d.ts.map +0 -1
- package/bundled-graph/dist/src/extract/languages/c.js +0 -88
- package/bundled-graph/dist/src/extract/languages/c.js.map +0 -1
- package/bundled-graph/dist/src/extract/languages/cpp.d.ts +0 -3
- package/bundled-graph/dist/src/extract/languages/cpp.d.ts.map +0 -1
- package/bundled-graph/dist/src/extract/languages/cpp.js +0 -121
- package/bundled-graph/dist/src/extract/languages/cpp.js.map +0 -1
- package/bundled-graph/dist/src/extract/languages/csharp.d.ts +0 -3
- package/bundled-graph/dist/src/extract/languages/csharp.d.ts.map +0 -1
- package/bundled-graph/dist/src/extract/languages/csharp.js +0 -121
- package/bundled-graph/dist/src/extract/languages/csharp.js.map +0 -1
- package/bundled-graph/dist/src/extract/languages/go.d.ts +0 -3
- package/bundled-graph/dist/src/extract/languages/go.d.ts.map +0 -1
- package/bundled-graph/dist/src/extract/languages/go.js +0 -181
- package/bundled-graph/dist/src/extract/languages/go.js.map +0 -1
- package/bundled-graph/dist/src/extract/languages/java.d.ts +0 -3
- package/bundled-graph/dist/src/extract/languages/java.d.ts.map +0 -1
- package/bundled-graph/dist/src/extract/languages/java.js +0 -117
- package/bundled-graph/dist/src/extract/languages/java.js.map +0 -1
- package/bundled-graph/dist/src/extract/languages/kotlin.d.ts +0 -3
- package/bundled-graph/dist/src/extract/languages/kotlin.d.ts.map +0 -1
- package/bundled-graph/dist/src/extract/languages/kotlin.js +0 -112
- package/bundled-graph/dist/src/extract/languages/kotlin.js.map +0 -1
- package/bundled-graph/dist/src/extract/languages/php.d.ts +0 -3
- package/bundled-graph/dist/src/extract/languages/php.d.ts.map +0 -1
- package/bundled-graph/dist/src/extract/languages/php.js +0 -130
- package/bundled-graph/dist/src/extract/languages/php.js.map +0 -1
- package/bundled-graph/dist/src/extract/languages/python.d.ts +0 -3
- package/bundled-graph/dist/src/extract/languages/python.d.ts.map +0 -1
- package/bundled-graph/dist/src/extract/languages/python.js +0 -230
- package/bundled-graph/dist/src/extract/languages/python.js.map +0 -1
- package/bundled-graph/dist/src/extract/languages/ruby.d.ts +0 -3
- package/bundled-graph/dist/src/extract/languages/ruby.d.ts.map +0 -1
- package/bundled-graph/dist/src/extract/languages/ruby.js +0 -120
- package/bundled-graph/dist/src/extract/languages/ruby.js.map +0 -1
- package/bundled-graph/dist/src/extract/languages/rust.d.ts +0 -3
- package/bundled-graph/dist/src/extract/languages/rust.d.ts.map +0 -1
- package/bundled-graph/dist/src/extract/languages/rust.js +0 -195
- package/bundled-graph/dist/src/extract/languages/rust.js.map +0 -1
- package/bundled-graph/dist/src/extract/languages/scala.d.ts +0 -3
- package/bundled-graph/dist/src/extract/languages/scala.d.ts.map +0 -1
- package/bundled-graph/dist/src/extract/languages/scala.js +0 -110
- package/bundled-graph/dist/src/extract/languages/scala.js.map +0 -1
- package/bundled-graph/dist/src/extract/languages/swift.d.ts +0 -3
- package/bundled-graph/dist/src/extract/languages/swift.d.ts.map +0 -1
- package/bundled-graph/dist/src/extract/languages/swift.js +0 -122
- package/bundled-graph/dist/src/extract/languages/swift.js.map +0 -1
- package/bundled-graph/dist/src/extract/languages/typescript.d.ts +0 -3
- package/bundled-graph/dist/src/extract/languages/typescript.d.ts.map +0 -1
- package/bundled-graph/dist/src/extract/languages/typescript.js +0 -295
- package/bundled-graph/dist/src/extract/languages/typescript.js.map +0 -1
- package/bundled-graph/dist/src/extract/semantic.d.ts +0 -38
- package/bundled-graph/dist/src/extract/semantic.d.ts.map +0 -1
- package/bundled-graph/dist/src/extract/semantic.js +0 -242
- package/bundled-graph/dist/src/extract/semantic.js.map +0 -1
- package/bundled-graph/dist/src/extract/tree-sitter-runner.d.ts +0 -48
- package/bundled-graph/dist/src/extract/tree-sitter-runner.d.ts.map +0 -1
- package/bundled-graph/dist/src/extract/tree-sitter-runner.js +0 -137
- package/bundled-graph/dist/src/extract/tree-sitter-runner.js.map +0 -1
- package/bundled-graph/dist/src/extract/types.d.ts +0 -7
- package/bundled-graph/dist/src/extract/types.d.ts.map +0 -1
- package/bundled-graph/dist/src/extract/types.js +0 -2
- package/bundled-graph/dist/src/extract/types.js.map +0 -1
- package/bundled-graph/dist/src/index.d.ts +0 -28
- package/bundled-graph/dist/src/index.d.ts.map +0 -1
- package/bundled-graph/dist/src/index.js +0 -26
- package/bundled-graph/dist/src/index.js.map +0 -1
- package/bundled-graph/dist/src/pipeline.d.ts +0 -27
- package/bundled-graph/dist/src/pipeline.d.ts.map +0 -1
- package/bundled-graph/dist/src/pipeline.js +0 -269
- package/bundled-graph/dist/src/pipeline.js.map +0 -1
- package/bundled-graph/dist/src/report.d.ts +0 -26
- package/bundled-graph/dist/src/report.d.ts.map +0 -1
- package/bundled-graph/dist/src/report.js +0 -214
- package/bundled-graph/dist/src/report.js.map +0 -1
- package/bundled-graph/dist/src/types.d.ts +0 -124
- package/bundled-graph/dist/src/types.d.ts.map +0 -1
- package/bundled-graph/dist/src/types.js +0 -2
- package/bundled-graph/dist/src/types.js.map +0 -1
- package/bundled-graph/dist/src/visualize.d.ts +0 -4
- package/bundled-graph/dist/src/visualize.d.ts.map +0 -1
- package/bundled-graph/dist/src/visualize.js +0 -574
- package/bundled-graph/dist/src/visualize.js.map +0 -1
- package/bundled-graph/dist/tsconfig.tsbuildinfo +0 -1
- package/dist/src/ui/dashboard-v2.html +0 -5316
package/dist/src/ui/server.mjs
CHANGED
|
@@ -251,6 +251,48 @@ function bindServer(server, port) {
|
|
|
251
251
|
* @param {boolean} [options.openBrowser=true] - Whether to open the dashboard in the default browser.
|
|
252
252
|
* @returns {Promise<{port: number, url: string, server: http.Server}>}
|
|
253
253
|
*/
|
|
254
|
+
/**
|
|
255
|
+
* Resolve a Claude project slug back to the real filesystem path.
|
|
256
|
+
* Slugs are created by replacing all '/' with '-', so paths containing
|
|
257
|
+
* hyphens (like agent-f/agf-accounting) are ambiguous. This function
|
|
258
|
+
* uses a greedy BFS over the real filesystem to find the correct path.
|
|
259
|
+
* Falls back to cwd in session files, then to direct slug replacement.
|
|
260
|
+
*/
|
|
261
|
+
function resolveSlugToPath(slug, projDir) {
|
|
262
|
+
// 1. Try filesystem BFS (most reliable)
|
|
263
|
+
const parts = slug.replace(/^-/, '').split('-');
|
|
264
|
+
function tryPaths(idx, current) {
|
|
265
|
+
if (idx === parts.length) return fs.existsSync(current) ? current : null;
|
|
266
|
+
// Option A: next part is a new path component
|
|
267
|
+
const asDir = path.join(current, parts[idx]);
|
|
268
|
+
const r1 = tryPaths(idx + 1, asDir);
|
|
269
|
+
if (r1) return r1;
|
|
270
|
+
// Option B: combine with hyphen into current basename
|
|
271
|
+
if (current !== '/') {
|
|
272
|
+
const combined = path.join(path.dirname(current), path.basename(current) + '-' + parts[idx]);
|
|
273
|
+
const r2 = tryPaths(idx + 1, combined);
|
|
274
|
+
if (r2) return r2;
|
|
275
|
+
}
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
278
|
+
const fsResolved = parts.length ? tryPaths(1, '/' + parts[0]) : null;
|
|
279
|
+
if (fsResolved) return fsResolved;
|
|
280
|
+
|
|
281
|
+
// 2. Try reading cwd from a session file
|
|
282
|
+
try {
|
|
283
|
+
const sfiles = fs.readdirSync(projDir).filter(f => f.endsWith('.jsonl'));
|
|
284
|
+
for (const sf of sfiles) {
|
|
285
|
+
try {
|
|
286
|
+
const line = fs.readFileSync(path.join(projDir, sf), 'utf-8').split('\n').find(l => l.includes('"cwd"'));
|
|
287
|
+
if (line) { const m = line.match(/"cwd"\s*:\s*"([^"]+)"/); if (m?.[1]) return m[1]; }
|
|
288
|
+
} catch {}
|
|
289
|
+
}
|
|
290
|
+
} catch {}
|
|
291
|
+
|
|
292
|
+
// 3. Dumb fallback (known-broken for hyphenated dirs, but last resort)
|
|
293
|
+
return slug.replace(/-/g, '/');
|
|
294
|
+
}
|
|
295
|
+
|
|
254
296
|
export async function startServer({ port = 4242, projectDir, openBrowser = true } = {}) {
|
|
255
297
|
const server = http.createServer(async (req, res) => {
|
|
256
298
|
const url = req.url.split('?')[0];
|
|
@@ -269,17 +311,10 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
269
311
|
return;
|
|
270
312
|
}
|
|
271
313
|
|
|
272
|
-
// ----------------------------------------------------------------- GET /v2
|
|
314
|
+
// ----------------------------------------------------------------- GET /v2 (alias → /)
|
|
273
315
|
if (req.method === 'GET' && url === '/v2') {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
const html = fs.readFileSync(htmlPath, 'utf8');
|
|
277
|
-
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
278
|
-
res.end(html);
|
|
279
|
-
} catch (err) {
|
|
280
|
-
res.writeHead(500, { 'Content-Type': 'text/plain' });
|
|
281
|
-
res.end(`Failed to load dashboard-v2.html: ${err.message}`);
|
|
282
|
-
}
|
|
316
|
+
res.writeHead(301, { 'Location': '/' });
|
|
317
|
+
res.end();
|
|
283
318
|
return;
|
|
284
319
|
}
|
|
285
320
|
|
|
@@ -601,7 +636,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
601
636
|
const projectCosts = [];
|
|
602
637
|
for (const slug of slugDirs) {
|
|
603
638
|
const projDir = path.join(projectsBase, slug);
|
|
604
|
-
const projPath =
|
|
639
|
+
const projPath = resolveSlugToPath(slug, projDir);
|
|
605
640
|
let sessionFiles = [];
|
|
606
641
|
try { sessionFiles = fs.readdirSync(projDir).filter(f => f.endsWith('.jsonl')).map(f => path.join(projDir, f)); } catch {}
|
|
607
642
|
if (!sessionFiles.length) continue;
|
|
@@ -635,9 +670,8 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
635
670
|
try { slugDirs = fs.readdirSync(projectsBase, { withFileTypes: true }).filter(e => e.isDirectory()).map(e => e.name); } catch {}
|
|
636
671
|
const projects = slugDirs.map(slug => {
|
|
637
672
|
const projDir = path.join(projectsBase, slug);
|
|
638
|
-
|
|
639
|
-
const
|
|
640
|
-
const name = slug.split('-').filter(Boolean).pop() || slug;
|
|
673
|
+
const projPath = resolveSlugToPath(slug, projDir);
|
|
674
|
+
const name = projPath.split('/').filter(Boolean).pop() || slug.split('-').filter(Boolean).pop() || slug;
|
|
641
675
|
let sessionCount = 0; let lastActivity = 0; let memoryCount = 0;
|
|
642
676
|
try {
|
|
643
677
|
const files = fs.readdirSync(projDir).filter(f => f.endsWith('.jsonl'));
|
|
@@ -850,6 +884,79 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
850
884
|
return;
|
|
851
885
|
}
|
|
852
886
|
|
|
887
|
+
// ------------------------------------------------- GET /api/routing-feedback
|
|
888
|
+
if (req.method === 'GET' && url === '/api/routing-feedback') {
|
|
889
|
+
try {
|
|
890
|
+
const qs = new URL(req.url, 'http://localhost').searchParams;
|
|
891
|
+
const d = path.resolve(qs.get('dir') || projectDir || process.cwd());
|
|
892
|
+
const feedbackPath = path.join(d, '.monomind', 'routing-feedback.jsonl');
|
|
893
|
+
let rows = [];
|
|
894
|
+
if (fs.existsSync(feedbackPath)) {
|
|
895
|
+
const raw = fs.readFileSync(feedbackPath, 'utf-8');
|
|
896
|
+
rows = raw.split('\n').filter(Boolean).map(l => { try { return JSON.parse(l); } catch { return null; } }).filter(Boolean);
|
|
897
|
+
}
|
|
898
|
+
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
|
|
899
|
+
res.end(JSON.stringify(rows));
|
|
900
|
+
} catch (err) {
|
|
901
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
902
|
+
res.end(JSON.stringify({ error: err.message }));
|
|
903
|
+
}
|
|
904
|
+
return;
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
// ---------------------------------------------------- GET /api/memory/stats
|
|
908
|
+
if (req.method === 'GET' && url === '/api/memory/stats') {
|
|
909
|
+
try {
|
|
910
|
+
const qs = new URL(req.url, 'http://localhost').searchParams;
|
|
911
|
+
const d = path.resolve(qs.get('dir') || projectDir || process.cwd());
|
|
912
|
+
const slug = d.replace(/\//g, '-');
|
|
913
|
+
const memDir = path.join(os.homedir(), '.claude', 'projects', slug, 'memory');
|
|
914
|
+
|
|
915
|
+
let total = 0, namespaces = 0, size = 0, lastWrite = null;
|
|
916
|
+
const byType = {};
|
|
917
|
+
if (fs.existsSync(memDir)) {
|
|
918
|
+
const files = fs.readdirSync(memDir).filter(f => f.endsWith('.md'));
|
|
919
|
+
total = files.length;
|
|
920
|
+
namespaces = files.length; // each .md file is a memory namespace
|
|
921
|
+
files.forEach(f => {
|
|
922
|
+
const fp = path.join(memDir, f);
|
|
923
|
+
try {
|
|
924
|
+
const st = fs.statSync(fp);
|
|
925
|
+
size += st.size;
|
|
926
|
+
if (!lastWrite || st.mtimeMs > lastWrite) lastWrite = st.mtimeMs;
|
|
927
|
+
} catch {}
|
|
928
|
+
const type = f.replace('.md', '');
|
|
929
|
+
byType[type] = (byType[type] || 0) + 1;
|
|
930
|
+
});
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
// Check for AgentDB / HNSW / RVF backends
|
|
934
|
+
const dbPath = path.join(d, '.monomind', 'agentdb.db');
|
|
935
|
+
const hnswPath = path.join(d, '.monomind', 'hnsw.index');
|
|
936
|
+
const rvfPath = path.join(d, '.monomind', 'memory.rvf');
|
|
937
|
+
|
|
938
|
+
const stats = {
|
|
939
|
+
total,
|
|
940
|
+
count: total,
|
|
941
|
+
namespaces,
|
|
942
|
+
ns: Object.keys(byType).length,
|
|
943
|
+
size,
|
|
944
|
+
byType,
|
|
945
|
+
hnsw: fs.existsSync(hnswPath),
|
|
946
|
+
agentdb: fs.existsSync(dbPath),
|
|
947
|
+
rvf: fs.existsSync(rvfPath),
|
|
948
|
+
lastWrite,
|
|
949
|
+
memDir,
|
|
950
|
+
};
|
|
951
|
+
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
|
|
952
|
+
res.end(JSON.stringify({ stats }));
|
|
953
|
+
} catch (err) {
|
|
954
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
955
|
+
res.end(JSON.stringify({ error: err.message }));
|
|
956
|
+
}
|
|
957
|
+
return;
|
|
958
|
+
}
|
|
959
|
+
|
|
853
960
|
// ---------------------------------------------------------- GET /api/loops
|
|
854
961
|
if (req.method === 'GET' && url === '/api/loops') {
|
|
855
962
|
try {
|
|
@@ -1165,7 +1272,8 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1165
1272
|
|
|
1166
1273
|
// Generate HTML on-the-fly from SQLite DB using the improved toHtml export
|
|
1167
1274
|
if (fs.existsSync(dbPath)) {
|
|
1168
|
-
const { openDb, closeDb
|
|
1275
|
+
const { openDb, closeDb } = await import('/Users/morteza/Desktop/tools/monomind/packages/@monomind/monograph/dist/src/storage/db.js');
|
|
1276
|
+
const { toHtml } = await import('/Users/morteza/Desktop/tools/monomind/packages/@monomind/monograph/dist/src/export/html.js');
|
|
1169
1277
|
const db = openDb(dbPath);
|
|
1170
1278
|
let html;
|
|
1171
1279
|
try {
|
|
@@ -1225,7 +1333,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1225
1333
|
let report = null, exists = false, stats = null;
|
|
1226
1334
|
if (fs.existsSync(dbPath)) {
|
|
1227
1335
|
exists = true;
|
|
1228
|
-
const { openDb, closeDb } = await import('
|
|
1336
|
+
const { openDb, closeDb } = await import('/Users/morteza/Desktop/tools/monomind/packages/@monomind/monograph/dist/src/storage/db.js');
|
|
1229
1337
|
const db = openDb(dbPath);
|
|
1230
1338
|
try {
|
|
1231
1339
|
const nodeCount = db.prepare('SELECT COUNT(*) AS c FROM nodes').get().c;
|
|
@@ -1268,7 +1376,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1268
1376
|
const dbPath = path.join(d, '.monomind', 'monograph.db');
|
|
1269
1377
|
let nodes = [], edges = [];
|
|
1270
1378
|
if (fs.existsSync(dbPath)) {
|
|
1271
|
-
const { openDb, closeDb } = await import('
|
|
1379
|
+
const { openDb, closeDb } = await import('/Users/morteza/Desktop/tools/monomind/packages/@monomind/monograph/dist/src/storage/db.js');
|
|
1272
1380
|
const db = openDb(dbPath);
|
|
1273
1381
|
try {
|
|
1274
1382
|
const nodeLimit = Math.min(parseInt(qs.get('limit') || '500', 10), 5000);
|
|
@@ -1406,7 +1514,8 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1406
1514
|
|
|
1407
1515
|
// Run doc parsing in background
|
|
1408
1516
|
(async () => {
|
|
1409
|
-
const { openDb, closeDb
|
|
1517
|
+
const { openDb, closeDb } = await import('/Users/morteza/Desktop/tools/monomind/packages/@monomind/monograph/dist/src/storage/db.js');
|
|
1518
|
+
const { isFileCached, updateFileCache, hashFileContent } = await import('/Users/morteza/Desktop/tools/monomind/packages/@monomind/monograph/dist/src/storage/file-cache.js');
|
|
1410
1519
|
const { readFileSync, readdirSync, statSync } = fs;
|
|
1411
1520
|
|
|
1412
1521
|
const docExts = new Set(['.md', '.mdx', '.txt', '.rst']);
|
|
@@ -1569,7 +1678,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1569
1678
|
const dbPath = path.join(d, '.monomind', 'monograph.db');
|
|
1570
1679
|
if (!id) { res.writeHead(400); res.end(JSON.stringify({ error: 'Missing ?id=' })); return; }
|
|
1571
1680
|
if (!fs.existsSync(dbPath)) { res.writeHead(404); res.end(JSON.stringify({ error: 'Graph not built' })); return; }
|
|
1572
|
-
const { openDb, closeDb } = await import('
|
|
1681
|
+
const { openDb, closeDb } = await import('/Users/morteza/Desktop/tools/monomind/packages/@monomind/monograph/dist/src/storage/db.js');
|
|
1573
1682
|
const db = openDb(dbPath);
|
|
1574
1683
|
let content = '', filePath = '', startLine = 0, endLine = 0, language = '', name = '', type = '';
|
|
1575
1684
|
try {
|
|
@@ -1619,7 +1728,8 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1619
1728
|
const dbPath = path.join(d, '.monomind', 'monograph.db');
|
|
1620
1729
|
if (!q) { res.writeHead(400); res.end(JSON.stringify({ error: 'Missing ?q=' })); return; }
|
|
1621
1730
|
if (!fs.existsSync(dbPath)) { res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' }); res.end(JSON.stringify({ nodes: [] })); return; }
|
|
1622
|
-
const { openDb, closeDb
|
|
1731
|
+
const { openDb, closeDb } = await import('/Users/morteza/Desktop/tools/monomind/packages/@monomind/monograph/dist/src/storage/db.js');
|
|
1732
|
+
const { ftsSearch } = await import('/Users/morteza/Desktop/tools/monomind/packages/@monomind/monograph/dist/src/storage/fts-store.js');
|
|
1623
1733
|
const db = openDb(dbPath);
|
|
1624
1734
|
let nodes = [];
|
|
1625
1735
|
try {
|
|
@@ -1650,7 +1760,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1650
1760
|
const d = path.resolve(dir || process.cwd());
|
|
1651
1761
|
const dbPath = path.join(d, '.monomind', 'monograph.db');
|
|
1652
1762
|
if (!id || !fs.existsSync(dbPath)) { res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' }); res.end(JSON.stringify({ related: [] })); return; }
|
|
1653
|
-
const { openDb, closeDb } = await import('
|
|
1763
|
+
const { openDb, closeDb } = await import('/Users/morteza/Desktop/tools/monomind/packages/@monomind/monograph/dist/src/storage/db.js');
|
|
1654
1764
|
const db = openDb(dbPath);
|
|
1655
1765
|
const related = [];
|
|
1656
1766
|
try {
|
|
@@ -1691,7 +1801,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1691
1801
|
const d = path.resolve(dir || process.cwd());
|
|
1692
1802
|
const dbPath = path.join(d, '.monomind', 'monograph.db');
|
|
1693
1803
|
if (!id || !fs.existsSync(dbPath)) { res.writeHead(404); res.end(JSON.stringify({ error: 'Not found' })); return; }
|
|
1694
|
-
const { openDb, closeDb } = await import('
|
|
1804
|
+
const { openDb, closeDb } = await import('/Users/morteza/Desktop/tools/monomind/packages/@monomind/monograph/dist/src/storage/db.js');
|
|
1695
1805
|
const db = openDb(dbPath);
|
|
1696
1806
|
let result = { node: null, content: '', neighbors: [], markdown: '' };
|
|
1697
1807
|
try {
|
|
@@ -1751,7 +1861,8 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1751
1861
|
const dbPath = path.join(d, '.monomind', 'monograph.db');
|
|
1752
1862
|
if (!q) { res.writeHead(400); res.end(JSON.stringify({ error: 'Missing ?q= parameter' })); return; }
|
|
1753
1863
|
if (!fs.existsSync(dbPath)) { res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' }); res.end(JSON.stringify({ success: false, result: 'Graph not built yet. Run: monomind monograph build' })); return; }
|
|
1754
|
-
const { openDb, closeDb
|
|
1864
|
+
const { openDb, closeDb } = await import('/Users/morteza/Desktop/tools/monomind/packages/@monomind/monograph/dist/src/storage/db.js');
|
|
1865
|
+
const { ftsSearch } = await import('/Users/morteza/Desktop/tools/monomind/packages/@monomind/monograph/dist/src/storage/fts-store.js');
|
|
1755
1866
|
const db = openDb(dbPath);
|
|
1756
1867
|
let result = '';
|
|
1757
1868
|
try {
|
|
@@ -1787,7 +1898,8 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1787
1898
|
const dbPath = path.join(d, '.monomind', 'monograph.db');
|
|
1788
1899
|
if (!nodeQ) { res.writeHead(400); res.end(JSON.stringify({ error: 'Missing ?node= parameter' })); return; }
|
|
1789
1900
|
if (!fs.existsSync(dbPath)) { res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' }); res.end(JSON.stringify({ success: false, explanation: 'Graph not built yet. Run: monomind monograph build' })); return; }
|
|
1790
|
-
const { openDb, closeDb
|
|
1901
|
+
const { openDb, closeDb } = await import('/Users/morteza/Desktop/tools/monomind/packages/@monomind/monograph/dist/src/storage/db.js');
|
|
1902
|
+
const { ftsSearch } = await import('/Users/morteza/Desktop/tools/monomind/packages/@monomind/monograph/dist/src/storage/fts-store.js');
|
|
1791
1903
|
const db = openDb(dbPath);
|
|
1792
1904
|
let explanation = '';
|
|
1793
1905
|
try {
|
|
@@ -1829,7 +1941,33 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1829
1941
|
const dbPath = path.join(d, '.monomind', 'monograph.db');
|
|
1830
1942
|
if (!from || !to) { res.writeHead(400); res.end(JSON.stringify({ error: 'Missing ?from= and ?to= parameters' })); return; }
|
|
1831
1943
|
if (!fs.existsSync(dbPath)) { res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' }); res.end(JSON.stringify({ success: false, path: 'Graph not built yet.' })); return; }
|
|
1832
|
-
|
|
1944
|
+
// Import only graphology-free storage modules to avoid broken graphology dep
|
|
1945
|
+
const { openDb, closeDb } = await import('/Users/morteza/Desktop/tools/monomind/packages/@monomind/monograph/dist/src/storage/db.js');
|
|
1946
|
+
const { ftsSearch } = await import('/Users/morteza/Desktop/tools/monomind/packages/@monomind/monograph/dist/src/storage/fts-store.js');
|
|
1947
|
+
// SQL-based BFS for shortest path (avoids graphology)
|
|
1948
|
+
const getShortestPath = (db, fromId, toId, maxDepth = 6) => {
|
|
1949
|
+
if (fromId === toId) return [fromId];
|
|
1950
|
+
const visited = new Set([fromId]);
|
|
1951
|
+
let frontier = [[fromId]];
|
|
1952
|
+
for (let depth = 0; depth < maxDepth; depth++) {
|
|
1953
|
+
const next = [];
|
|
1954
|
+
for (const chain of frontier) {
|
|
1955
|
+
const cur = chain[chain.length - 1];
|
|
1956
|
+
const neighbors = db.prepare('SELECT target_id AS id FROM edges WHERE source_id=? UNION SELECT source_id AS id FROM edges WHERE target_id=?').all(cur, cur);
|
|
1957
|
+
for (const { id } of neighbors) {
|
|
1958
|
+
if (!visited.has(id)) {
|
|
1959
|
+
const newChain = [...chain, id];
|
|
1960
|
+
if (id === toId) return newChain;
|
|
1961
|
+
visited.add(id);
|
|
1962
|
+
next.push(newChain);
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1965
|
+
}
|
|
1966
|
+
if (!next.length) break;
|
|
1967
|
+
frontier = next;
|
|
1968
|
+
}
|
|
1969
|
+
return null;
|
|
1970
|
+
};
|
|
1833
1971
|
const db = openDb(dbPath);
|
|
1834
1972
|
let pathResult = '';
|
|
1835
1973
|
try {
|
|
@@ -1924,13 +2062,41 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
1924
2062
|
const ok = (data) => { json(res); res.end(JSON.stringify({ content: [{ type: 'text', text: typeof data === 'string' ? data : JSON.stringify(data, null, 2) }] })); };
|
|
1925
2063
|
const err = (msg) => { json(res); res.end(JSON.stringify({ error: msg })); };
|
|
1926
2064
|
try {
|
|
1927
|
-
const { tool, input = {} } = JSON.parse(body);
|
|
2065
|
+
const { tool, input = {}, args = {} } = JSON.parse(body);
|
|
1928
2066
|
const qs2 = new URL(req.url, 'http://localhost').searchParams;
|
|
1929
|
-
|
|
2067
|
+
// dir can come from: URL query string, body.args.dir, body.input.dir, or server default
|
|
2068
|
+
const dir2 = qs2.get('dir') || args.dir || input.dir || projectDir;
|
|
1930
2069
|
const d2 = path.resolve(dir2 || process.cwd());
|
|
1931
2070
|
const dbPath2 = path.join(d2, '.monomind', 'monograph.db');
|
|
1932
2071
|
if (!fs.existsSync(dbPath2)) { err('monograph.db not found — run monograph build first'); return; }
|
|
1933
|
-
|
|
2072
|
+
// Import only graphology-free storage modules to avoid broken graphology dep
|
|
2073
|
+
const { openDb, closeDb } = await import('/Users/morteza/Desktop/tools/monomind/packages/@monomind/monograph/dist/src/storage/db.js');
|
|
2074
|
+
const { ftsSearch } = await import('/Users/morteza/Desktop/tools/monomind/packages/@monomind/monograph/dist/src/storage/fts-store.js');
|
|
2075
|
+
const { countNodes } = await import('/Users/morteza/Desktop/tools/monomind/packages/@monomind/monograph/dist/src/storage/node-store.js');
|
|
2076
|
+
const { countEdges } = await import('/Users/morteza/Desktop/tools/monomind/packages/@monomind/monograph/dist/src/storage/edge-store.js');
|
|
2077
|
+
const getShortestPath = (db, fromId, toId, maxDepth = 6) => {
|
|
2078
|
+
if (fromId === toId) return [fromId];
|
|
2079
|
+
const visited = new Set([fromId]);
|
|
2080
|
+
let frontier = [[fromId]];
|
|
2081
|
+
for (let depth = 0; depth < maxDepth; depth++) {
|
|
2082
|
+
const next = [];
|
|
2083
|
+
for (const chain of frontier) {
|
|
2084
|
+
const cur = chain[chain.length - 1];
|
|
2085
|
+
const neighbors = db.prepare('SELECT target_id AS id FROM edges WHERE source_id=? UNION SELECT source_id AS id FROM edges WHERE target_id=?').all(cur, cur);
|
|
2086
|
+
for (const { id } of neighbors) {
|
|
2087
|
+
if (!visited.has(id)) {
|
|
2088
|
+
const newChain = [...chain, id];
|
|
2089
|
+
if (id === toId) return newChain;
|
|
2090
|
+
visited.add(id);
|
|
2091
|
+
next.push(newChain);
|
|
2092
|
+
}
|
|
2093
|
+
}
|
|
2094
|
+
}
|
|
2095
|
+
if (!next.length) break;
|
|
2096
|
+
frontier = next;
|
|
2097
|
+
}
|
|
2098
|
+
return null;
|
|
2099
|
+
};
|
|
1934
2100
|
const db2 = openDb(dbPath2);
|
|
1935
2101
|
try {
|
|
1936
2102
|
if (tool === 'monograph_stats') {
|
|
@@ -2181,6 +2347,145 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
2181
2347
|
else { ok(`Author Analytics (by commit count):\n${'─'.repeat(50)}\n${log.trim().split('\n').map(l => { const m = l.trim().match(/^(\d+)\s+(.+)$/); return m ? ` ${m[2].padEnd(45)} ${m[1]} commits` : l; }).join('\n')}`); }
|
|
2182
2348
|
} catch { ok('Author analytics requires git. Ensure this directory is a git repository.'); }
|
|
2183
2349
|
|
|
2350
|
+
} else if (tool === 'monograph_reachability') {
|
|
2351
|
+
// Files with no inbound edges (nothing imports them)
|
|
2352
|
+
const allNodes = db2.prepare(`SELECT id, name, file_path FROM nodes WHERE label IN ('File','Module') LIMIT 5000`).all();
|
|
2353
|
+
const inboundSet = new Set(db2.prepare(`SELECT DISTINCT target_id FROM edges`).all().map(r => r.target_id));
|
|
2354
|
+
const unreachable = allNodes.filter(n => !inboundSet.has(n.id)).slice(0, 40);
|
|
2355
|
+
const outdeg = db2.prepare(`SELECT source_id, COUNT(*) as c FROM edges GROUP BY source_id`);
|
|
2356
|
+
const degMap = {};
|
|
2357
|
+
for (const r of outdeg.all()) degMap[r.source_id] = r.c;
|
|
2358
|
+
if (!unreachable.length) { ok('All files have at least one inbound reference.'); }
|
|
2359
|
+
else ok(`Unreachable Files (${unreachable.length} of ${allNodes.length} total):\n${'─'.repeat(50)}\n${unreachable.slice(0,30).map(n => ` ${n.name || n.id.split('/').pop()} (imports ${degMap[n.id]||0} others)\n ${n.file_path||''}`).join('\n\n')}`);
|
|
2360
|
+
|
|
2361
|
+
} else if (tool === 'monograph_vital_signs_snapshot') {
|
|
2362
|
+
// Same as health_score — kept for backward compatibility
|
|
2363
|
+
const n = db2.prepare('SELECT COUNT(*) as c FROM nodes').get().c;
|
|
2364
|
+
const e = db2.prepare('SELECT COUNT(*) as c FROM edges').get().c;
|
|
2365
|
+
const dead = db2.prepare(`SELECT COUNT(*) as c FROM nodes n WHERE NOT EXISTS (SELECT 1 FROM edges WHERE source_id=n.id OR target_id=n.id)`).get().c;
|
|
2366
|
+
const hubs = db2.prepare(`SELECT COUNT(*) as c FROM (SELECT source_id FROM edges GROUP BY source_id HAVING COUNT(*)>20)`).get().c;
|
|
2367
|
+
const density = n > 1 ? (e / (n * (n-1))).toFixed(6) : '0';
|
|
2368
|
+
const score = Math.max(0, Math.min(100, Math.round(100 - (dead/Math.max(n,1)*30) - (hubs/Math.max(n,1)*500))));
|
|
2369
|
+
ok(`Vital Signs — ${new Date().toISOString()}\n${'─'.repeat(50)}\n Health Score: ${score}/100 ${score>=80?'✓ OK':score>=60?'⚠ Warning':'✗ Critical'}\n Nodes: ${n}\n Edges: ${e}\n Density: ${density}\n Dead symbols: ${dead} (${(dead/Math.max(n,1)*100).toFixed(1)}%)\n Hub nodes: ${hubs} nodes with >20 edges`);
|
|
2370
|
+
|
|
2371
|
+
} else if (tool === 'monograph_circular_deps') {
|
|
2372
|
+
// Find import cycles using iterative DFS
|
|
2373
|
+
const limit = Math.min(parseInt(input.limit||'10'), 20);
|
|
2374
|
+
const importEdges = db2.prepare(`SELECT source_id, target_id FROM edges WHERE relation IN ('IMPORTS','REQUIRES','USES','DEPENDS_ON') LIMIT 50000`).all();
|
|
2375
|
+
const adj = {};
|
|
2376
|
+
for (const e of importEdges) { (adj[e.source_id] = adj[e.source_id]||[]).push(e.target_id); }
|
|
2377
|
+
const cycles = [];
|
|
2378
|
+
const visited = new Set(), inStack = new Set();
|
|
2379
|
+
function dfs(node, path) {
|
|
2380
|
+
if (cycles.length >= limit) return;
|
|
2381
|
+
if (inStack.has(node)) {
|
|
2382
|
+
const cycleStart = path.indexOf(node);
|
|
2383
|
+
if (cycleStart >= 0) cycles.push(path.slice(cycleStart).concat(node));
|
|
2384
|
+
return;
|
|
2385
|
+
}
|
|
2386
|
+
if (visited.has(node)) return;
|
|
2387
|
+
visited.add(node); inStack.add(node); path.push(node);
|
|
2388
|
+
for (const nb of (adj[node]||[])) dfs(nb, path);
|
|
2389
|
+
path.pop(); inStack.delete(node);
|
|
2390
|
+
}
|
|
2391
|
+
for (const node of Object.keys(adj).slice(0, 2000)) dfs(node, []);
|
|
2392
|
+
const getName = id => id.split('/').slice(-2).join('/');
|
|
2393
|
+
if (!cycles.length) ok(`No circular dependencies found among ${Object.keys(adj).length} nodes with import edges.`);
|
|
2394
|
+
else ok(`Circular Dependencies (${cycles.length} found):\n${'─'.repeat(50)}\n${cycles.slice(0,limit).map((c,i) => ` ${i+1}. ${c.map(getName).join(' → ')}`).join('\n')}`);
|
|
2395
|
+
|
|
2396
|
+
} else if (tool === 'monograph_largest_files') {
|
|
2397
|
+
const limit2 = Math.min(parseInt(input.limit||'25'), 50);
|
|
2398
|
+
const rows = db2.prepare(`SELECT file_path, MAX(end_line) as lines, COUNT(*) as symbols FROM nodes WHERE file_path IS NOT NULL AND end_line IS NOT NULL AND end_line > 0 GROUP BY file_path ORDER BY lines DESC LIMIT ${limit2}`).all();
|
|
2399
|
+
if (!rows.length) ok('No line-count data available. Ensure the index was built with source parsing enabled.');
|
|
2400
|
+
else ok(`Largest Files by Line Count:\n${'─'.repeat(50)}\n${rows.map((r,i) => ` ${String(i+1).padStart(2)}. ${r.lines.toString().padStart(5)} lines ${r.symbols} symbols ${r.file_path.split('/').slice(-2).join('/')}`).join('\n')}`);
|
|
2401
|
+
|
|
2402
|
+
} else if (tool === 'monograph_coupling_balance') {
|
|
2403
|
+
// Fan-out (what this file uses) vs Fan-in (what uses this file)
|
|
2404
|
+
const limit3 = Math.min(parseInt(input.limit||'20'), 40);
|
|
2405
|
+
const fanOut = db2.prepare(`SELECT source_id, COUNT(*) as c FROM edges GROUP BY source_id`).all();
|
|
2406
|
+
const fanIn = db2.prepare(`SELECT target_id, COUNT(*) as c FROM edges GROUP BY target_id`).all();
|
|
2407
|
+
const outMap = {}, inMap = {};
|
|
2408
|
+
for (const r of fanOut) outMap[r.source_id] = r.c;
|
|
2409
|
+
for (const r of fanIn) inMap[r.target_id] = r.c;
|
|
2410
|
+
const allIds = new Set([...Object.keys(outMap), ...Object.keys(inMap)]);
|
|
2411
|
+
const nodes3 = db2.prepare(`SELECT id, name, file_path FROM nodes WHERE label='File' LIMIT 10000`).all();
|
|
2412
|
+
const fileSet = new Set(nodes3.map(n => n.id));
|
|
2413
|
+
const entries = [...allIds].filter(id => fileSet.has(id)).map(id => {
|
|
2414
|
+
const o = outMap[id]||0, i = inMap[id]||0;
|
|
2415
|
+
const n = nodes3.find(x=>x.id===id);
|
|
2416
|
+
return { name: n?.name || id.split('/').pop(), path: n?.file_path||'', out: o, inn: i, ratio: i > 0 ? (o/i).toFixed(1) : '∞' };
|
|
2417
|
+
}).filter(x => x.out > 0 || x.inn > 0).sort((a,b) => (b.out+b.inn) - (a.out+a.inn)).slice(0, limit3);
|
|
2418
|
+
ok(`Coupling Balance (Fan-out vs Fan-in, top ${limit3} by activity):\n${'─'.repeat(60)}\n ${'File'.padEnd(35)} Out In Ratio\n${'─'.repeat(60)}\n${entries.map(e => ` ${e.name.slice(0,35).padEnd(35)} ${String(e.out).padStart(3)} ${String(e.inn).padStart(2)} ${e.ratio}`).join('\n')}`);
|
|
2419
|
+
|
|
2420
|
+
} else if (tool === 'monograph_dead_exports') {
|
|
2421
|
+
// Exported symbols with zero inbound edges
|
|
2422
|
+
const exported = db2.prepare(`SELECT id, name, label, file_path FROM nodes WHERE is_exported=1 LIMIT 10000`).all();
|
|
2423
|
+
const inbound = new Set(db2.prepare(`SELECT DISTINCT target_id FROM edges`).all().map(r => r.target_id));
|
|
2424
|
+
const dead2 = exported.filter(n => !inbound.has(n.id));
|
|
2425
|
+
if (!dead2.length) ok('No dead exports found — all exported symbols have at least one inbound reference.');
|
|
2426
|
+
else ok(`Dead Exports — exported but never imported (${dead2.length} of ${exported.length} exported symbols):\n${'─'.repeat(50)}\n${dead2.slice(0,30).map(n => ` ${n.label.padEnd(12)} ${n.name} → ${(n.file_path||'').split('/').slice(-2).join('/')}`).join('\n')}`);
|
|
2427
|
+
|
|
2428
|
+
} else if (tool === 'monograph_language_breakdown') {
|
|
2429
|
+
const rows2 = db2.prepare(`SELECT language, COUNT(*) as c FROM nodes WHERE language IS NOT NULL AND language != '' GROUP BY language ORDER BY c DESC`).all();
|
|
2430
|
+
if (!rows2.length) ok('No language metadata available in this graph index.');
|
|
2431
|
+
else {
|
|
2432
|
+
const total2 = rows2.reduce((s,r) => s+r.c, 0);
|
|
2433
|
+
const maxC = rows2[0].c;
|
|
2434
|
+
ok(`Language Breakdown:\n${'─'.repeat(50)}\n${rows2.map(r => { const bar = '█'.repeat(Math.round(r.c/maxC*20)); const pct = (r.c/total2*100).toFixed(1); return ` ${r.language.padEnd(15)} ${bar.padEnd(20)} ${String(r.c).padStart(6)} (${pct}%)`; }).join('\n')}\n\n Total nodes: ${total2}`);
|
|
2435
|
+
}
|
|
2436
|
+
|
|
2437
|
+
} else if (tool === 'monograph_instability') {
|
|
2438
|
+
// Robert Martin's Instability = Ce / (Ca + Ce)
|
|
2439
|
+
// Ca = afferent coupling (in-degree), Ce = efferent coupling (out-degree)
|
|
2440
|
+
const limit4 = Math.min(parseInt(input.limit||'25'), 50);
|
|
2441
|
+
const outRows = db2.prepare(`SELECT source_id, COUNT(*) as c FROM edges GROUP BY source_id`).all();
|
|
2442
|
+
const inRows = db2.prepare(`SELECT target_id, COUNT(*) as c FROM edges GROUP BY target_id`).all();
|
|
2443
|
+
const Ce = {}, Ca = {};
|
|
2444
|
+
for (const r of outRows) Ce[r.source_id] = r.c;
|
|
2445
|
+
for (const r of inRows) Ca[r.target_id] = r.c;
|
|
2446
|
+
const fileNodes = db2.prepare(`SELECT id, name, file_path FROM nodes WHERE label='File' LIMIT 10000`).all();
|
|
2447
|
+
const entries4 = fileNodes.map(n => {
|
|
2448
|
+
const ca = Ca[n.id]||0, ce = Ce[n.id]||0;
|
|
2449
|
+
const total = ca + ce;
|
|
2450
|
+
const inst = total > 0 ? ce / total : 0;
|
|
2451
|
+
return { name: n.name||n.id.split('/').pop(), ca, ce, inst };
|
|
2452
|
+
}).filter(x => x.ca+x.ce > 0).sort((a,b) => b.inst - a.inst);
|
|
2453
|
+
const risky = entries4.filter(x => x.inst > 0.7 && x.ca > 3);
|
|
2454
|
+
const stable = entries4.filter(x => x.inst < 0.2 && x.ce > 3);
|
|
2455
|
+
ok(`Instability Index (Ce÷(Ca+Ce), 0=stable 1=unstable):\n${'─'.repeat(60)}\n\n ⚠ High instability + high dependents (blast radius risk):\n${risky.slice(0,10).map(x => ` ${x.name.slice(0,40).padEnd(40)} I=${x.inst.toFixed(2)} Ca=${x.ca} Ce=${x.ce}`).join('\n')||' none'}\n\n ✓ Stable (low instability, many dependents on them):\n${stable.slice(0,8).map(x => ` ${x.name.slice(0,40).padEnd(40)} I=${x.inst.toFixed(2)} Ca=${x.ca} Ce=${x.ce}`).join('\n')||' none'}\n\n Total files analyzed: ${entries4.length}`);
|
|
2456
|
+
|
|
2457
|
+
} else if (tool === 'monograph_churn_hotspots') {
|
|
2458
|
+
// Combines git churn frequency with structural complexity (out-degree)
|
|
2459
|
+
const limit5 = Math.min(parseInt(input.limit||'15'), 30);
|
|
2460
|
+
const { execSync: execS2 } = await import('child_process');
|
|
2461
|
+
let churnMap = {};
|
|
2462
|
+
try {
|
|
2463
|
+
const since = input.since || '6 months ago';
|
|
2464
|
+
const log2 = execS2(`git log --since="${since}" --name-only --format="" -- . 2>/dev/null | grep -v '^$' | sort | uniq -c | sort -rn | head -200`, { cwd: d2, encoding: 'utf-8', timeout: 8000 });
|
|
2465
|
+
for (const line of log2.trim().split('\n')) {
|
|
2466
|
+
const m = line.trim().match(/^(\d+)\s+(.+)$/);
|
|
2467
|
+
if (m) churnMap[m[2]] = parseInt(m[1]);
|
|
2468
|
+
}
|
|
2469
|
+
} catch {}
|
|
2470
|
+
if (!Object.keys(churnMap).length) { ok('No git history found — churn analysis requires a git repository.'); }
|
|
2471
|
+
else {
|
|
2472
|
+
const outDeg = db2.prepare(`SELECT source_id, COUNT(*) as c FROM edges GROUP BY source_id`).all();
|
|
2473
|
+
const degMap2 = {};
|
|
2474
|
+
for (const r of outDeg) degMap2[r.source_id] = r.c;
|
|
2475
|
+
const fileNodes2 = db2.prepare(`SELECT id, name, file_path FROM nodes WHERE label='File' LIMIT 10000`).all();
|
|
2476
|
+
const maxChurn = Math.max(...Object.values(churnMap), 1);
|
|
2477
|
+
const maxDeg2 = Math.max(...Object.values(degMap2), 1);
|
|
2478
|
+
const scored = fileNodes2.map(n => {
|
|
2479
|
+
const fp = n.file_path || '';
|
|
2480
|
+
const churn = churnMap[fp] || Object.entries(churnMap).find(([k]) => fp.endsWith(k))?.[1] || 0;
|
|
2481
|
+
const deg = degMap2[n.id] || 0;
|
|
2482
|
+
const score2 = (churn/maxChurn * 0.6) + (deg/maxDeg2 * 0.4);
|
|
2483
|
+
return { name: n.name||fp.split('/').pop(), fp, churn, deg, score: score2 };
|
|
2484
|
+
}).filter(x => x.churn > 0 || x.deg > 5).sort((a,b) => b.score - a.score).slice(0, limit5);
|
|
2485
|
+
if (!scored.length) ok('No files matched both churn and complexity criteria.');
|
|
2486
|
+
else ok(`Churn × Complexity Hotspots (60% churn weight + 40% coupling weight):\n${'─'.repeat(60)}\n ${'File'.padEnd(38)} Churn Deps Score\n${'─'.repeat(60)}\n${scored.map(x => ` ${x.name.slice(0,38).padEnd(38)} ${String(x.churn).padStart(5)} ${String(x.deg).padStart(4)} ${(x.score*100).toFixed(0)}%`).join('\n')}\n\n Analyzed: ${scored.length} hotspot candidates from last ${input.since||'6 months'}`);
|
|
2487
|
+
}
|
|
2488
|
+
|
|
2184
2489
|
} else {
|
|
2185
2490
|
ok(`Tool "${tool}" not implemented in control panel`);
|
|
2186
2491
|
}
|
|
@@ -2938,7 +3243,7 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
2938
3243
|
Object.values(agents).forEach(a => {
|
|
2939
3244
|
if (a.status === 'running') agentsRunning++;
|
|
2940
3245
|
else agentsIdle++;
|
|
2941
|
-
budgetUsedTokens += (a.tokens_used || 0);
|
|
3246
|
+
budgetUsedTokens += (a.tokens_used || ((a.tokens_in || 0) + (a.tokens_out || 0)));
|
|
2942
3247
|
});
|
|
2943
3248
|
} catch(_) {}
|
|
2944
3249
|
|
|
@@ -2977,13 +3282,16 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
2977
3282
|
res.end(JSON.stringify({
|
|
2978
3283
|
agents_running: agentsRunning,
|
|
2979
3284
|
agents_idle: agentsIdle,
|
|
3285
|
+
agents_active: agentsRunning,
|
|
2980
3286
|
open_issues: openIssues,
|
|
2981
3287
|
in_progress_issues: inProgressIssues,
|
|
3288
|
+
tasks_pending: openIssues + inProgressIssues,
|
|
2982
3289
|
budget_used_tokens: budgetUsedTokens,
|
|
2983
3290
|
budget_max_tokens: budgetMaxTokens,
|
|
2984
3291
|
budget_used_pct: budgetUsedPct,
|
|
2985
3292
|
run_success_rate_7d: successRate,
|
|
2986
3293
|
total_runs_7d: totalRuns,
|
|
3294
|
+
errors: [],
|
|
2987
3295
|
}));
|
|
2988
3296
|
} catch(_) { res.writeHead(500); res.end('{}'); }
|
|
2989
3297
|
return;
|
|
@@ -3174,18 +3482,25 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
3174
3482
|
const readJsonSafe = (f) => { try { return JSON.parse(fs.readFileSync(f, 'utf8')); } catch(_) { return null; } };
|
|
3175
3483
|
const data = readJsonSafe(path.join(base, `${orgName}-approvals.json`)) || { approvals: [] };
|
|
3176
3484
|
const approvals = (data.approvals || [])
|
|
3177
|
-
.sort((a, b) => new Date(b.createdAt || 0) - new Date(a.createdAt || 0))
|
|
3485
|
+
.sort((a, b) => new Date(b.createdAt || b.created_at || b.requested_at || 0) - new Date(a.createdAt || a.created_at || a.requested_at || 0))
|
|
3178
3486
|
.map(a => ({
|
|
3179
3487
|
id: a.id,
|
|
3180
3488
|
title: a.title || a.action || null,
|
|
3489
|
+
action: a.action || a.title || null,
|
|
3490
|
+
description: a.description || a.action || a.title || null,
|
|
3181
3491
|
status: a.status || 'pending',
|
|
3182
3492
|
agentId: a.agentId || a.agent_id || null,
|
|
3183
3493
|
agentTitle: a.agentTitle || null,
|
|
3494
|
+
requester: a.requester || a.agentTitle || a.agent_id || a.agentId || null,
|
|
3495
|
+
agent: a.agent || a.agent_id || a.agentId || null,
|
|
3184
3496
|
payload: a.payload || null,
|
|
3185
|
-
|
|
3497
|
+
risk_level: a.risk_level || 'medium',
|
|
3498
|
+
created_at: a.created_at || a.createdAt || a.requested_at || null,
|
|
3499
|
+
createdAt: a.createdAt || a.created_at || a.requested_at || null,
|
|
3186
3500
|
updatedAt: a.updatedAt || null,
|
|
3187
3501
|
resolvedAt: a.resolvedAt || null,
|
|
3188
3502
|
resolvedBy: a.resolvedBy || null,
|
|
3503
|
+
ts: a.ts || null,
|
|
3189
3504
|
}));
|
|
3190
3505
|
const pending = approvals.filter(a => a.status === 'pending' || a.status === 'revision_requested').length;
|
|
3191
3506
|
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
|
|
@@ -3194,6 +3509,49 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
3194
3509
|
return;
|
|
3195
3510
|
}
|
|
3196
3511
|
|
|
3512
|
+
// POST /api/org/:name/approvals/:id — approve or reject a pending approval request
|
|
3513
|
+
// Body: { action: "approve" | "reject" | "revision_requested" }
|
|
3514
|
+
if (req.method === 'POST' && url.match(/^\/api\/org\/[a-z0-9][a-z0-9_-]{0,63}\/approvals\/[^/]+$/i)) {
|
|
3515
|
+
let body = '';
|
|
3516
|
+
for await (const chunk of req) body += chunk;
|
|
3517
|
+
try {
|
|
3518
|
+
const parts = url.split('/');
|
|
3519
|
+
const orgName = decodeURIComponent(parts[3]);
|
|
3520
|
+
const approvalId = decodeURIComponent(parts[5]);
|
|
3521
|
+
if (orgName.length > 64 || !/^[a-z0-9][a-z0-9_-]*$/i.test(orgName)) { res.writeHead(400); res.end('Invalid org name'); return; }
|
|
3522
|
+
if (!approvalId) { res.writeHead(400); res.end('{"error":"approval id required"}'); return; }
|
|
3523
|
+
const parsed = JSON.parse(body);
|
|
3524
|
+
const action = parsed.action;
|
|
3525
|
+
if (!['approve', 'reject', 'revision_requested'].includes(action)) {
|
|
3526
|
+
res.writeHead(400); res.end('{"error":"action must be approve, reject, or revision_requested"}'); return;
|
|
3527
|
+
}
|
|
3528
|
+
const base = path.join(projectDir || process.cwd(), '.monomind', 'orgs');
|
|
3529
|
+
const approvalsFile = path.join(base, `${orgName}-approvals.json`);
|
|
3530
|
+
let data = { approvals: [] };
|
|
3531
|
+
try { data = JSON.parse(fs.readFileSync(approvalsFile, 'utf8')); } catch(_) {}
|
|
3532
|
+
const idx = (data.approvals || []).findIndex(a => a.id === approvalId);
|
|
3533
|
+
if (idx === -1) { res.writeHead(404); res.end('{"error":"approval not found"}'); return; }
|
|
3534
|
+
const status = action === 'approve' ? 'approved' : action === 'reject' ? 'rejected' : 'revision_requested';
|
|
3535
|
+
data.approvals[idx] = {
|
|
3536
|
+
...data.approvals[idx],
|
|
3537
|
+
status,
|
|
3538
|
+
resolvedAt: new Date().toISOString(),
|
|
3539
|
+
resolvedBy: 'operator',
|
|
3540
|
+
};
|
|
3541
|
+
const tmp = `${approvalsFile}.tmp`;
|
|
3542
|
+
fs.writeFileSync(tmp, JSON.stringify(data, null, 2), 'utf-8');
|
|
3543
|
+
fs.renameSync(tmp, approvalsFile);
|
|
3544
|
+
// Emit org:approval:resolved event so boss agent unblocks
|
|
3545
|
+
const event = { type: 'org:approval:resolved', org: orgName, approval_id: approvalId, status, ts: Date.now() };
|
|
3546
|
+
try { fs.appendFileSync(path.join(projectDir || process.cwd(), 'data', 'mastermind-events.jsonl'), JSON.stringify(event) + '\n'); } catch(_) {}
|
|
3547
|
+
const msg = `data: ${JSON.stringify(event)}\n\n`;
|
|
3548
|
+
for (const c of mmSseClients) { try { c.write(msg); } catch(_) { mmSseClients.delete(c); } }
|
|
3549
|
+
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
|
|
3550
|
+
res.end(JSON.stringify({ ok: true, status }));
|
|
3551
|
+
} catch(_) { res.writeHead(500); res.end('{}'); }
|
|
3552
|
+
return;
|
|
3553
|
+
}
|
|
3554
|
+
|
|
3197
3555
|
// GET /api/org/:name/secrets — masked secrets list (NEVER exposes values)
|
|
3198
3556
|
if (req.method === 'GET' && url.match(/^\/api\/org\/[a-z0-9][a-z0-9_-]{0,63}\/secrets$/i)) {
|
|
3199
3557
|
try {
|
|
@@ -3229,13 +3587,25 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
3229
3587
|
const base = path.join(projectDir || process.cwd(), '.monomind', 'orgs');
|
|
3230
3588
|
let budgetData = { org_budget: {}, agent_budgets: {}, period: 'monthly', currency: 'USD' };
|
|
3231
3589
|
try { budgetData = JSON.parse(fs.readFileSync(path.join(base, `${orgName}-budgets.json`), 'utf8')); } catch(_) {}
|
|
3232
|
-
// Enrich with per-agent spend from state file
|
|
3590
|
+
// Enrich with per-agent spend from state file.
|
|
3591
|
+
// State file format: { agents: { "<role_id>": { tokens_in, tokens_out, ... } } }
|
|
3233
3592
|
let agents = [];
|
|
3234
3593
|
try {
|
|
3235
3594
|
const state = JSON.parse(fs.readFileSync(path.join(base, `${orgName}-state.json`), 'utf8'));
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3595
|
+
const agentMap = state.agents || {};
|
|
3596
|
+
// Also load role titles from org config for enrichment
|
|
3597
|
+
let roleMap = {};
|
|
3598
|
+
try {
|
|
3599
|
+
const cfg = JSON.parse(fs.readFileSync(path.join(base, `${orgName}.json`), 'utf8'));
|
|
3600
|
+
(cfg.roles || []).forEach(r => { roleMap[r.id] = r.title || r.id; });
|
|
3601
|
+
} catch(_) {}
|
|
3602
|
+
agents = Object.entries(agentMap).map(([id, s]) => ({
|
|
3603
|
+
id,
|
|
3604
|
+
title: roleMap[id] || s.title || id,
|
|
3605
|
+
tokens_in: s.tokens_in || 0,
|
|
3606
|
+
tokens_out: s.tokens_out || 0,
|
|
3607
|
+
tokens_used: s.tokens_used || (s.tokens_in || 0) + (s.tokens_out || 0),
|
|
3608
|
+
total_cost_usd: s.total_cost_usd || 0,
|
|
3239
3609
|
}));
|
|
3240
3610
|
} catch(_) {}
|
|
3241
3611
|
// Also include roles from org config if state is empty
|
|
@@ -3298,6 +3668,105 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
|
|
|
3298
3668
|
return;
|
|
3299
3669
|
}
|
|
3300
3670
|
|
|
3671
|
+
// GET /api/org/:name/goals — read org goals
|
|
3672
|
+
if (req.method === 'GET' && url.match(/^\/api\/org\/[a-z0-9][a-z0-9_-]{0,63}\/goals$/i)) {
|
|
3673
|
+
try {
|
|
3674
|
+
const orgName = decodeURIComponent(url.split('/')[3]);
|
|
3675
|
+
if (orgName.length > 64 || !/^[a-z0-9][a-z0-9_-]*$/i.test(orgName)) { res.writeHead(400); res.end('Invalid org name'); return; }
|
|
3676
|
+
const goalsFile = path.join(projectDir || process.cwd(), '.monomind', 'orgs', `${orgName}-goals.json`);
|
|
3677
|
+
let data = { goals: [] };
|
|
3678
|
+
try { data = JSON.parse(fs.readFileSync(goalsFile, 'utf8')); } catch(_) {}
|
|
3679
|
+
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
|
|
3680
|
+
res.end(JSON.stringify({ goals: data.goals || [] }));
|
|
3681
|
+
} catch(_) { res.writeHead(500); res.end('{"goals":[]}'); }
|
|
3682
|
+
return;
|
|
3683
|
+
}
|
|
3684
|
+
|
|
3685
|
+
// GET /api/org/:name/routines — read org routines
|
|
3686
|
+
if (req.method === 'GET' && url.match(/^\/api\/org\/[a-z0-9][a-z0-9_-]{0,63}\/routines$/i)) {
|
|
3687
|
+
try {
|
|
3688
|
+
const orgName = decodeURIComponent(url.split('/')[3]);
|
|
3689
|
+
if (orgName.length > 64 || !/^[a-z0-9][a-z0-9_-]*$/i.test(orgName)) { res.writeHead(400); res.end('Invalid org name'); return; }
|
|
3690
|
+
const routinesFile = path.join(projectDir || process.cwd(), '.monomind', 'orgs', `${orgName}-routines.json`);
|
|
3691
|
+
let data = { routines: [] };
|
|
3692
|
+
try { data = JSON.parse(fs.readFileSync(routinesFile, 'utf8')); } catch(_) {}
|
|
3693
|
+
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
|
|
3694
|
+
res.end(JSON.stringify({ routines: data.routines || [] }));
|
|
3695
|
+
} catch(_) { res.writeHead(500); res.end('{"routines":[]}'); }
|
|
3696
|
+
return;
|
|
3697
|
+
}
|
|
3698
|
+
|
|
3699
|
+
// POST /api/org/:name/goals — upsert the org goals file
|
|
3700
|
+
// Body: { goals: [{id, title, description, status, priority, assignee_id, created_at}] }
|
|
3701
|
+
if (req.method === 'POST' && url.match(/^\/api\/org\/[a-z0-9][a-z0-9_-]{0,63}\/goals$/i)) {
|
|
3702
|
+
let body = '';
|
|
3703
|
+
for await (const chunk of req) body += chunk;
|
|
3704
|
+
try {
|
|
3705
|
+
const orgName = decodeURIComponent(url.split('/')[3]);
|
|
3706
|
+
if (orgName.length > 64 || !/^[a-z0-9][a-z0-9_-]*$/i.test(orgName)) { res.writeHead(400); res.end('Invalid org name'); return; }
|
|
3707
|
+
const parsed = JSON.parse(body);
|
|
3708
|
+
if (!parsed || !Array.isArray(parsed.goals)) { res.writeHead(400); res.end('{"error":"goals array required"}'); return; }
|
|
3709
|
+
const goalsFile = path.join(projectDir || process.cwd(), '.monomind', 'orgs', `${orgName}-goals.json`);
|
|
3710
|
+
const tmp = `${goalsFile}.tmp`;
|
|
3711
|
+
const payload = { org: orgName, updated_at: new Date().toISOString(), goals: parsed.goals };
|
|
3712
|
+
fs.writeFileSync(tmp, JSON.stringify(payload, null, 2), 'utf-8');
|
|
3713
|
+
fs.renameSync(tmp, goalsFile);
|
|
3714
|
+
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
|
|
3715
|
+
res.end(JSON.stringify({ ok: true, count: parsed.goals.length }));
|
|
3716
|
+
} catch(_) { res.writeHead(500); res.end('{"error":"' + String(_).replace(/"/g, '\\"') + '"}'); }
|
|
3717
|
+
return;
|
|
3718
|
+
}
|
|
3719
|
+
|
|
3720
|
+
// POST /api/org/:name/routines — upsert the org routines file
|
|
3721
|
+
// Body: { routines: [{name, description, schedule, enabled, last_run, next_run}] }
|
|
3722
|
+
if (req.method === 'POST' && url.match(/^\/api\/org\/[a-z0-9][a-z0-9_-]{0,63}\/routines$/i)) {
|
|
3723
|
+
let body = '';
|
|
3724
|
+
for await (const chunk of req) body += chunk;
|
|
3725
|
+
try {
|
|
3726
|
+
const orgName = decodeURIComponent(url.split('/')[3]);
|
|
3727
|
+
if (orgName.length > 64 || !/^[a-z0-9][a-z0-9_-]*$/i.test(orgName)) { res.writeHead(400); res.end('Invalid org name'); return; }
|
|
3728
|
+
const parsed = JSON.parse(body);
|
|
3729
|
+
if (!parsed || !Array.isArray(parsed.routines)) { res.writeHead(400); res.end('{"error":"routines array required"}'); return; }
|
|
3730
|
+
const routinesFile = path.join(projectDir || process.cwd(), '.monomind', 'orgs', `${orgName}-routines.json`);
|
|
3731
|
+
const tmp = `${routinesFile}.tmp`;
|
|
3732
|
+
const payload = { org: orgName, updated_at: new Date().toISOString(), routines: parsed.routines };
|
|
3733
|
+
fs.writeFileSync(tmp, JSON.stringify(payload, null, 2), 'utf-8');
|
|
3734
|
+
fs.renameSync(tmp, routinesFile);
|
|
3735
|
+
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
|
|
3736
|
+
res.end(JSON.stringify({ ok: true, count: parsed.routines.length }));
|
|
3737
|
+
} catch(_) { res.writeHead(500); res.end('{"error":"' + String(_).replace(/"/g, '\\"') + '"}'); }
|
|
3738
|
+
return;
|
|
3739
|
+
}
|
|
3740
|
+
|
|
3741
|
+
// DELETE /api/orgs/:name — delete an org config and all associated data files
|
|
3742
|
+
if (req.method === 'DELETE' && url.match(/^\/api\/orgs\/[a-z0-9][a-z0-9_-]{0,63}$/i)) {
|
|
3743
|
+
try {
|
|
3744
|
+
const orgName = decodeURIComponent(url.split('/')[3]);
|
|
3745
|
+
if (orgName.length > 64 || !/^[a-z0-9][a-z0-9_-]*$/i.test(orgName)) { res.writeHead(400); res.end('Invalid org name'); return; }
|
|
3746
|
+
const orgsDir = path.join(projectDir || process.cwd(), '.monomind', 'orgs');
|
|
3747
|
+
const configFile = path.join(orgsDir, `${orgName}.json`);
|
|
3748
|
+
if (!fs.existsSync(configFile)) { res.writeHead(404); res.end('{"error":"org not found"}'); return; }
|
|
3749
|
+
// Remove all org-associated files (config + state + data)
|
|
3750
|
+
const suffixes = ['', '-state', '-goals', '-routines', '-approvals', '-activity', '-issues', '-members', '-projects', '-workspaces', '-worktrees', '-environments', '-plugins', '-adapters'];
|
|
3751
|
+
for (const suf of suffixes) {
|
|
3752
|
+
const f = path.join(orgsDir, `${orgName}${suf}.json`);
|
|
3753
|
+
try { if (fs.existsSync(f)) fs.unlinkSync(f); } catch(_) {}
|
|
3754
|
+
const fjsonl = path.join(orgsDir, `${orgName}${suf}.jsonl`);
|
|
3755
|
+
try { if (fs.existsSync(fjsonl)) fs.unlinkSync(fjsonl); } catch(_) {}
|
|
3756
|
+
}
|
|
3757
|
+
// Remove stop file if present
|
|
3758
|
+
try { fs.unlinkSync(path.join(orgsDir, '.stops', `${orgName}.stop`)); } catch(_) {}
|
|
3759
|
+
// Emit org:delete event
|
|
3760
|
+
const deleteEvent = { type: 'org:delete', org: orgName, ts: Date.now() };
|
|
3761
|
+
try { fs.appendFileSync(path.join(projectDir || process.cwd(), 'data', 'mastermind-events.jsonl'), JSON.stringify(deleteEvent) + '\n'); } catch(_) {}
|
|
3762
|
+
const msg = `data: ${JSON.stringify(deleteEvent)}\n\n`;
|
|
3763
|
+
for (const c of mmSseClients) { try { c.write(msg); } catch(_) { mmSseClients.delete(c); } }
|
|
3764
|
+
res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
|
|
3765
|
+
res.end('{"ok":true}');
|
|
3766
|
+
} catch(_) { res.writeHead(500); res.end('{}'); }
|
|
3767
|
+
return;
|
|
3768
|
+
}
|
|
3769
|
+
|
|
3301
3770
|
// POST /api/orgs/:name/stop — send stop signal to a running org
|
|
3302
3771
|
if (req.method === 'POST' && url.match(/^\/api\/orgs\/[a-z0-9][a-z0-9_-]{0,63}\/stop$/i)) {
|
|
3303
3772
|
try {
|