claude-flow 3.27.4 → 3.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (689) hide show
  1. package/.claude/agents/MIGRATION_SUMMARY.md +221 -221
  2. package/.claude/agents/analysis/analyze-code-quality.md +57 -57
  3. package/.claude/agents/analysis/code-analyzer.md +188 -188
  4. package/.claude/agents/analysis/code-review/analyze-code-quality.md +57 -57
  5. package/.claude/agents/architecture/system-design/arch-system-design.md +35 -35
  6. package/.claude/agents/base-template-generator.md +41 -41
  7. package/.claude/agents/consensus/byzantine-coordinator.md +42 -42
  8. package/.claude/agents/consensus/crdt-synchronizer.md +976 -976
  9. package/.claude/agents/consensus/gossip-coordinator.md +42 -42
  10. package/.claude/agents/consensus/performance-benchmarker.md +830 -830
  11. package/.claude/agents/consensus/quorum-manager.md +802 -802
  12. package/.claude/agents/consensus/raft-manager.md +42 -42
  13. package/.claude/agents/consensus/security-manager.md +601 -601
  14. package/.claude/agents/core/coder.md +254 -254
  15. package/.claude/agents/core/planner.md +151 -151
  16. package/.claude/agents/core/researcher.md +173 -173
  17. package/.claude/agents/core/reviewer.md +308 -308
  18. package/.claude/agents/core/tester.md +299 -299
  19. package/.claude/agents/custom/test-long-runner.md +43 -43
  20. package/.claude/agents/data/ml/data-ml-model.md +75 -75
  21. package/.claude/agents/database-specialist.md +9 -9
  22. package/.claude/agents/development/backend/dev-backend-api.md +28 -28
  23. package/.claude/agents/development/dev-backend-api.md +177 -177
  24. package/.claude/agents/devops/ci-cd/ops-cicd-github.md +51 -51
  25. package/.claude/agents/documentation/api-docs/docs-api-openapi.md +62 -62
  26. package/.claude/agents/dual-mode/codex-coordinator.md +206 -206
  27. package/.claude/agents/dual-mode/codex-worker.md +190 -190
  28. package/.claude/agents/dual-mode/dual-orchestrator.md +253 -253
  29. package/.claude/agents/flow-nexus/app-store.md +87 -87
  30. package/.claude/agents/flow-nexus/authentication.md +68 -68
  31. package/.claude/agents/flow-nexus/challenges.md +80 -80
  32. package/.claude/agents/flow-nexus/neural-network.md +87 -87
  33. package/.claude/agents/flow-nexus/payments.md +82 -82
  34. package/.claude/agents/flow-nexus/sandbox.md +75 -75
  35. package/.claude/agents/flow-nexus/swarm.md +75 -75
  36. package/.claude/agents/flow-nexus/user-tools.md +95 -95
  37. package/.claude/agents/flow-nexus/workflow.md +83 -83
  38. package/.claude/agents/github/code-review-swarm.md +520 -520
  39. package/.claude/agents/github/github-modes.md +153 -153
  40. package/.claude/agents/github/issue-tracker.md +298 -298
  41. package/.claude/agents/github/multi-repo-swarm.md +524 -524
  42. package/.claude/agents/github/pr-manager.md +162 -162
  43. package/.claude/agents/github/project-board-sync.md +477 -477
  44. package/.claude/agents/github/release-manager.md +337 -337
  45. package/.claude/agents/github/release-swarm.md +550 -550
  46. package/.claude/agents/github/repo-architect.md +364 -364
  47. package/.claude/agents/github/swarm-issue.md +550 -550
  48. package/.claude/agents/github/swarm-pr.md +401 -401
  49. package/.claude/agents/github/sync-coordinator.md +424 -424
  50. package/.claude/agents/github/workflow-automation.md +604 -604
  51. package/.claude/agents/goal/agent.md +816 -816
  52. package/.claude/agents/goal/code-goal-planner.md +444 -444
  53. package/.claude/agents/goal/goal-planner.md +167 -167
  54. package/.claude/agents/hive-mind/collective-intelligence-coordinator.md +128 -128
  55. package/.claude/agents/hive-mind/queen-coordinator.md +201 -201
  56. package/.claude/agents/hive-mind/scout-explorer.md +240 -240
  57. package/.claude/agents/hive-mind/swarm-memory-manager.md +191 -191
  58. package/.claude/agents/hive-mind/worker-specialist.md +215 -215
  59. package/.claude/agents/neural/safla-neural.md +73 -73
  60. package/.claude/agents/optimization/benchmark-suite.md +662 -662
  61. package/.claude/agents/optimization/load-balancer.md +428 -428
  62. package/.claude/agents/optimization/performance-monitor.md +669 -669
  63. package/.claude/agents/optimization/resource-allocator.md +671 -671
  64. package/.claude/agents/optimization/topology-optimizer.md +805 -805
  65. package/.claude/agents/payments/agentic-payments.md +126 -126
  66. package/.claude/agents/project-coordinator.md +8 -8
  67. package/.claude/agents/python-specialist.md +9 -9
  68. package/.claude/agents/reasoning/agent.md +816 -816
  69. package/.claude/agents/reasoning/goal-planner.md +72 -72
  70. package/.claude/agents/security-auditor.md +9 -9
  71. package/.claude/agents/sona/sona-learning-optimizer.md +65 -65
  72. package/.claude/agents/sparc/architecture.md +452 -452
  73. package/.claude/agents/sparc/pseudocode.md +298 -298
  74. package/.claude/agents/sparc/refinement.md +503 -503
  75. package/.claude/agents/sparc/specification.md +257 -257
  76. package/.claude/agents/specialized/mobile/spec-mobile-react-native.md +87 -87
  77. package/.claude/agents/sublinear/consensus-coordinator.md +337 -337
  78. package/.claude/agents/sublinear/matrix-optimizer.md +184 -184
  79. package/.claude/agents/sublinear/pagerank-analyzer.md +298 -298
  80. package/.claude/agents/sublinear/performance-optimizer.md +367 -367
  81. package/.claude/agents/sublinear/trading-predictor.md +245 -245
  82. package/.claude/agents/swarm/adaptive-coordinator.md +363 -363
  83. package/.claude/agents/swarm/hierarchical-coordinator.md +299 -299
  84. package/.claude/agents/swarm/mesh-coordinator.md +362 -362
  85. package/.claude/agents/templates/automation-smart-agent.md +184 -184
  86. package/.claude/agents/templates/coordinator-swarm-init.md +82 -82
  87. package/.claude/agents/templates/github-pr-manager.md +154 -154
  88. package/.claude/agents/templates/implementer-sparc-coder.md +242 -242
  89. package/.claude/agents/templates/memory-coordinator.md +162 -162
  90. package/.claude/agents/templates/migration-plan.md +723 -723
  91. package/.claude/agents/templates/orchestrator-task.md +119 -119
  92. package/.claude/agents/templates/performance-analyzer.md +178 -178
  93. package/.claude/agents/templates/sparc-coordinator.md +162 -162
  94. package/.claude/agents/testing/production-validator.md +372 -372
  95. package/.claude/agents/testing/tdd-london-swarm.md +221 -221
  96. package/.claude/agents/testing/unit/tdd-london-swarm.md +221 -221
  97. package/.claude/agents/testing/validation/production-validator.md +372 -372
  98. package/.claude/agents/typescript-specialist.md +9 -9
  99. package/.claude/agents/v3/database-specialist.md +9 -9
  100. package/.claude/agents/v3/project-coordinator.md +8 -8
  101. package/.claude/agents/v3/python-specialist.md +9 -9
  102. package/.claude/agents/v3/test-architect.md +9 -9
  103. package/.claude/agents/v3/typescript-specialist.md +9 -9
  104. package/.claude/agents/v3/v3-integration-architect.md +311 -311
  105. package/.claude/agents/v3/v3-memory-specialist.md +280 -280
  106. package/.claude/agents/v3/v3-performance-engineer.md +362 -362
  107. package/.claude/agents/v3/v3-queen-coordinator.md +62 -62
  108. package/.claude/agents/v3/v3-security-architect.md +139 -139
  109. package/.claude/checkpoints/1767754460.json +8 -8
  110. package/.claude/commands/agents/README.md +10 -10
  111. package/.claude/commands/agents/agent-capabilities.md +21 -21
  112. package/.claude/commands/agents/agent-coordination.md +28 -28
  113. package/.claude/commands/agents/agent-spawning.md +28 -28
  114. package/.claude/commands/agents/agent-types.md +26 -26
  115. package/.claude/commands/analysis/COMMAND_COMPLIANCE_REPORT.md +53 -53
  116. package/.claude/commands/analysis/README.md +9 -9
  117. package/.claude/commands/analysis/bottleneck-detect.md +162 -162
  118. package/.claude/commands/analysis/performance-bottlenecks.md +58 -58
  119. package/.claude/commands/analysis/performance-report.md +25 -25
  120. package/.claude/commands/analysis/token-efficiency.md +44 -44
  121. package/.claude/commands/analysis/token-usage.md +25 -25
  122. package/.claude/commands/automation/README.md +9 -9
  123. package/.claude/commands/automation/auto-agent.md +122 -122
  124. package/.claude/commands/automation/self-healing.md +105 -105
  125. package/.claude/commands/automation/session-memory.md +89 -89
  126. package/.claude/commands/automation/smart-agents.md +72 -72
  127. package/.claude/commands/automation/smart-spawn.md +25 -25
  128. package/.claude/commands/automation/workflow-select.md +25 -25
  129. package/.claude/commands/claude-flow-help.md +103 -103
  130. package/.claude/commands/claude-flow-memory.md +107 -107
  131. package/.claude/commands/claude-flow-swarm.md +205 -205
  132. package/.claude/commands/coordination/README.md +9 -9
  133. package/.claude/commands/coordination/agent-spawn.md +25 -25
  134. package/.claude/commands/coordination/init.md +44 -44
  135. package/.claude/commands/coordination/orchestrate.md +43 -43
  136. package/.claude/commands/coordination/spawn.md +45 -45
  137. package/.claude/commands/coordination/swarm-init.md +85 -85
  138. package/.claude/commands/coordination/task-orchestrate.md +25 -25
  139. package/.claude/commands/flow-nexus/app-store.md +123 -123
  140. package/.claude/commands/flow-nexus/challenges.md +119 -119
  141. package/.claude/commands/flow-nexus/login-registration.md +64 -64
  142. package/.claude/commands/flow-nexus/neural-network.md +133 -133
  143. package/.claude/commands/flow-nexus/payments.md +115 -115
  144. package/.claude/commands/flow-nexus/sandbox.md +82 -82
  145. package/.claude/commands/flow-nexus/swarm.md +86 -86
  146. package/.claude/commands/flow-nexus/user-tools.md +151 -151
  147. package/.claude/commands/flow-nexus/workflow.md +114 -114
  148. package/.claude/commands/github/README.md +11 -11
  149. package/.claude/commands/github/code-review-swarm.md +513 -513
  150. package/.claude/commands/github/code-review.md +25 -25
  151. package/.claude/commands/github/github-modes.md +146 -146
  152. package/.claude/commands/github/github-swarm.md +121 -121
  153. package/.claude/commands/github/issue-tracker.md +291 -291
  154. package/.claude/commands/github/issue-triage.md +25 -25
  155. package/.claude/commands/github/multi-repo-swarm.md +518 -518
  156. package/.claude/commands/github/pr-enhance.md +26 -26
  157. package/.claude/commands/github/pr-manager.md +169 -169
  158. package/.claude/commands/github/project-board-sync.md +470 -470
  159. package/.claude/commands/github/release-manager.md +337 -337
  160. package/.claude/commands/github/release-swarm.md +543 -543
  161. package/.claude/commands/github/repo-analyze.md +25 -25
  162. package/.claude/commands/github/repo-architect.md +366 -366
  163. package/.claude/commands/github/swarm-issue.md +481 -481
  164. package/.claude/commands/github/swarm-pr.md +284 -284
  165. package/.claude/commands/github/sync-coordinator.md +300 -300
  166. package/.claude/commands/github/workflow-automation.md +441 -441
  167. package/.claude/commands/hive-mind/README.md +17 -17
  168. package/.claude/commands/hive-mind/hive-mind-consensus.md +8 -8
  169. package/.claude/commands/hive-mind/hive-mind-init.md +18 -18
  170. package/.claude/commands/hive-mind/hive-mind-memory.md +8 -8
  171. package/.claude/commands/hive-mind/hive-mind-metrics.md +8 -8
  172. package/.claude/commands/hive-mind/hive-mind-resume.md +8 -8
  173. package/.claude/commands/hive-mind/hive-mind-sessions.md +8 -8
  174. package/.claude/commands/hive-mind/hive-mind-spawn.md +21 -21
  175. package/.claude/commands/hive-mind/hive-mind-status.md +8 -8
  176. package/.claude/commands/hive-mind/hive-mind-stop.md +8 -8
  177. package/.claude/commands/hive-mind/hive-mind-wizard.md +8 -8
  178. package/.claude/commands/hive-mind/hive-mind.md +27 -27
  179. package/.claude/commands/hooks/README.md +11 -11
  180. package/.claude/commands/hooks/overview.md +57 -57
  181. package/.claude/commands/hooks/post-edit.md +117 -117
  182. package/.claude/commands/hooks/post-task.md +112 -112
  183. package/.claude/commands/hooks/pre-edit.md +113 -113
  184. package/.claude/commands/hooks/pre-task.md +111 -111
  185. package/.claude/commands/hooks/session-end.md +118 -118
  186. package/.claude/commands/hooks/setup.md +102 -102
  187. package/.claude/commands/memory/README.md +9 -9
  188. package/.claude/commands/memory/memory-persist.md +25 -25
  189. package/.claude/commands/memory/memory-search.md +25 -25
  190. package/.claude/commands/memory/memory-usage.md +25 -25
  191. package/.claude/commands/memory/neural.md +47 -47
  192. package/.claude/commands/monitoring/README.md +9 -9
  193. package/.claude/commands/monitoring/agent-metrics.md +25 -25
  194. package/.claude/commands/monitoring/agents.md +44 -44
  195. package/.claude/commands/monitoring/real-time-view.md +25 -25
  196. package/.claude/commands/monitoring/status.md +46 -46
  197. package/.claude/commands/monitoring/swarm-monitor.md +25 -25
  198. package/.claude/commands/optimization/README.md +9 -9
  199. package/.claude/commands/optimization/auto-topology.md +61 -61
  200. package/.claude/commands/optimization/cache-manage.md +25 -25
  201. package/.claude/commands/optimization/parallel-execute.md +25 -25
  202. package/.claude/commands/optimization/parallel-execution.md +49 -49
  203. package/.claude/commands/optimization/topology-optimize.md +25 -25
  204. package/.claude/commands/pair/README.md +260 -260
  205. package/.claude/commands/pair/commands.md +545 -545
  206. package/.claude/commands/pair/config.md +509 -509
  207. package/.claude/commands/pair/examples.md +511 -511
  208. package/.claude/commands/pair/modes.md +347 -347
  209. package/.claude/commands/pair/session.md +406 -406
  210. package/.claude/commands/pair/start.md +208 -208
  211. package/.claude/commands/sparc/analyzer.md +51 -51
  212. package/.claude/commands/sparc/architect.md +53 -53
  213. package/.claude/commands/sparc/ask.md +97 -97
  214. package/.claude/commands/sparc/batch-executor.md +54 -54
  215. package/.claude/commands/sparc/code.md +89 -89
  216. package/.claude/commands/sparc/coder.md +54 -54
  217. package/.claude/commands/sparc/debug.md +83 -83
  218. package/.claude/commands/sparc/debugger.md +54 -54
  219. package/.claude/commands/sparc/designer.md +53 -53
  220. package/.claude/commands/sparc/devops.md +109 -109
  221. package/.claude/commands/sparc/docs-writer.md +80 -80
  222. package/.claude/commands/sparc/documenter.md +54 -54
  223. package/.claude/commands/sparc/innovator.md +54 -54
  224. package/.claude/commands/sparc/integration.md +83 -83
  225. package/.claude/commands/sparc/mcp.md +117 -117
  226. package/.claude/commands/sparc/memory-manager.md +54 -54
  227. package/.claude/commands/sparc/optimizer.md +54 -54
  228. package/.claude/commands/sparc/orchestrator.md +131 -131
  229. package/.claude/commands/sparc/post-deployment-monitoring-mode.md +83 -83
  230. package/.claude/commands/sparc/refinement-optimization-mode.md +83 -83
  231. package/.claude/commands/sparc/researcher.md +54 -54
  232. package/.claude/commands/sparc/reviewer.md +54 -54
  233. package/.claude/commands/sparc/security-review.md +80 -80
  234. package/.claude/commands/sparc/sparc-modes.md +174 -174
  235. package/.claude/commands/sparc/sparc.md +111 -111
  236. package/.claude/commands/sparc/spec-pseudocode.md +80 -80
  237. package/.claude/commands/sparc/supabase-admin.md +348 -348
  238. package/.claude/commands/sparc/swarm-coordinator.md +54 -54
  239. package/.claude/commands/sparc/tdd.md +54 -54
  240. package/.claude/commands/sparc/tester.md +54 -54
  241. package/.claude/commands/sparc/tutorial.md +79 -79
  242. package/.claude/commands/sparc/workflow-manager.md +54 -54
  243. package/.claude/commands/sparc.md +166 -166
  244. package/.claude/commands/stream-chain/pipeline.md +120 -120
  245. package/.claude/commands/stream-chain/run.md +69 -69
  246. package/.claude/commands/swarm/README.md +15 -15
  247. package/.claude/commands/swarm/analysis.md +95 -95
  248. package/.claude/commands/swarm/development.md +96 -96
  249. package/.claude/commands/swarm/examples.md +168 -168
  250. package/.claude/commands/swarm/maintenance.md +102 -102
  251. package/.claude/commands/swarm/optimization.md +117 -117
  252. package/.claude/commands/swarm/research.md +136 -136
  253. package/.claude/commands/swarm/swarm-analysis.md +8 -8
  254. package/.claude/commands/swarm/swarm-background.md +8 -8
  255. package/.claude/commands/swarm/swarm-init.md +19 -19
  256. package/.claude/commands/swarm/swarm-modes.md +8 -8
  257. package/.claude/commands/swarm/swarm-monitor.md +8 -8
  258. package/.claude/commands/swarm/swarm-spawn.md +19 -19
  259. package/.claude/commands/swarm/swarm-status.md +8 -8
  260. package/.claude/commands/swarm/swarm-strategies.md +8 -8
  261. package/.claude/commands/swarm/swarm.md +27 -27
  262. package/.claude/commands/swarm/testing.md +131 -131
  263. package/.claude/commands/training/README.md +9 -9
  264. package/.claude/commands/training/model-update.md +25 -25
  265. package/.claude/commands/training/neural-patterns.md +73 -73
  266. package/.claude/commands/training/neural-train.md +25 -25
  267. package/.claude/commands/training/pattern-learn.md +25 -25
  268. package/.claude/commands/training/specialization.md +62 -62
  269. package/.claude/commands/truth/start.md +142 -142
  270. package/.claude/commands/verify/check.md +49 -49
  271. package/.claude/commands/verify/start.md +127 -127
  272. package/.claude/commands/workflows/README.md +9 -9
  273. package/.claude/commands/workflows/development.md +77 -77
  274. package/.claude/commands/workflows/research.md +62 -62
  275. package/.claude/commands/workflows/workflow-create.md +25 -25
  276. package/.claude/commands/workflows/workflow-execute.md +25 -25
  277. package/.claude/commands/workflows/workflow-export.md +25 -25
  278. package/.claude/config/v3-dependency-optimization.json +265 -265
  279. package/.claude/config/v3-performance-targets.json +250 -250
  280. package/.claude/helpers/.helpers-version +1 -1
  281. package/.claude/helpers/README.md +96 -96
  282. package/.claude/helpers/adr-compliance.sh +186 -186
  283. package/.claude/helpers/aggressive-microcompact.mjs +36 -36
  284. package/.claude/helpers/auto-commit.sh +178 -178
  285. package/.claude/helpers/auto-memory-hook.mjs +430 -430
  286. package/.claude/helpers/checkpoint-manager.sh +251 -251
  287. package/.claude/helpers/context-persistence-hook.mjs +2001 -2001
  288. package/.claude/helpers/daemon-manager.sh +252 -252
  289. package/.claude/helpers/ddd-tracker.sh +144 -144
  290. package/.claude/helpers/github-safe.js +156 -156
  291. package/.claude/helpers/github-setup.sh +45 -45
  292. package/.claude/helpers/guidance-hook.sh +13 -13
  293. package/.claude/helpers/guidance-hooks.sh +102 -102
  294. package/.claude/helpers/health-monitor.sh +108 -108
  295. package/.claude/helpers/helpers.manifest.json +6 -5
  296. package/.claude/helpers/hook-handler.cjs +460 -311
  297. package/.claude/helpers/intelligence.cjs +1058 -1048
  298. package/.claude/helpers/learning-hooks.sh +329 -329
  299. package/.claude/helpers/learning-optimizer.sh +127 -127
  300. package/.claude/helpers/learning-service.mjs +1144 -1144
  301. package/.claude/helpers/memory.cjs +84 -84
  302. package/.claude/helpers/metrics-db.mjs +503 -503
  303. package/.claude/helpers/patch-aggressive-prune.mjs +184 -184
  304. package/.claude/helpers/pattern-consolidator.sh +86 -86
  305. package/.claude/helpers/perf-worker.sh +160 -160
  306. package/.claude/helpers/quick-start.sh +19 -19
  307. package/.claude/helpers/router.cjs +62 -62
  308. package/.claude/helpers/security-scanner.sh +127 -127
  309. package/.claude/helpers/session.cjs +125 -125
  310. package/.claude/helpers/setup-mcp.sh +18 -18
  311. package/.claude/helpers/standard-checkpoint-hooks.sh +189 -189
  312. package/.claude/helpers/statusline.cjs +925 -925
  313. package/.claude/helpers/swarm-comms.sh +353 -353
  314. package/.claude/helpers/swarm-hooks.sh +761 -761
  315. package/.claude/helpers/swarm-monitor.sh +210 -210
  316. package/.claude/helpers/sync-v3-metrics.sh +245 -245
  317. package/.claude/helpers/update-v3-progress.sh +165 -165
  318. package/.claude/helpers/v3-quick-status.sh +57 -57
  319. package/.claude/helpers/v3.sh +110 -110
  320. package/.claude/helpers/validate-v3-config.sh +215 -215
  321. package/.claude/helpers/worker-manager.sh +170 -170
  322. package/.claude/mcp.json +12 -12
  323. package/.claude/proven-config.json +1 -1
  324. package/.claude/settings.json +285 -285
  325. package/.claude/settings.json.bak +526 -526
  326. package/.claude/skills/agentdb-advanced/SKILL.md +550 -550
  327. package/.claude/skills/agentdb-learning/SKILL.md +545 -545
  328. package/.claude/skills/agentdb-memory-patterns/SKILL.md +339 -339
  329. package/.claude/skills/agentdb-optimization/SKILL.md +509 -509
  330. package/.claude/skills/agentdb-vector-search/SKILL.md +339 -339
  331. package/.claude/skills/agentic-jujutsu/SKILL.md +645 -645
  332. package/.claude/skills/browser/SKILL.md +204 -204
  333. package/.claude/skills/dual-mode/README.md +71 -71
  334. package/.claude/skills/dual-mode/dual-collect.md +103 -103
  335. package/.claude/skills/dual-mode/dual-coordinate.md +85 -85
  336. package/.claude/skills/dual-mode/dual-spawn.md +81 -81
  337. package/.claude/skills/flow-nexus-neural/SKILL.md +727 -727
  338. package/.claude/skills/flow-nexus-platform/SKILL.md +1154 -1154
  339. package/.claude/skills/flow-nexus-swarm/SKILL.md +604 -604
  340. package/.claude/skills/github-code-review/SKILL.md +1125 -1125
  341. package/.claude/skills/github-multi-repo/SKILL.md +862 -862
  342. package/.claude/skills/github-project-management/SKILL.md +1262 -1262
  343. package/.claude/skills/github-release-management/SKILL.md +1064 -1064
  344. package/.claude/skills/github-workflow-automation/SKILL.md +1047 -1047
  345. package/.claude/skills/hive-mind-advanced/SKILL.md +709 -709
  346. package/.claude/skills/hooks-automation/SKILL.md +1201 -1201
  347. package/.claude/skills/pair-programming/SKILL.md +1202 -1202
  348. package/.claude/skills/performance-analysis/SKILL.md +560 -560
  349. package/.claude/skills/reasoningbank-agentdb/SKILL.md +446 -446
  350. package/.claude/skills/reasoningbank-intelligence/SKILL.md +201 -201
  351. package/.claude/skills/skill-builder/SKILL.md +910 -910
  352. package/.claude/skills/sparc-methodology/SKILL.md +1106 -1106
  353. package/.claude/skills/stream-chain/SKILL.md +560 -560
  354. package/.claude/skills/swarm-advanced/SKILL.md +970 -970
  355. package/.claude/skills/swarm-orchestration/SKILL.md +179 -179
  356. package/.claude/skills/v3-cli-modernization/SKILL.md +871 -871
  357. package/.claude/skills/v3-core-implementation/SKILL.md +796 -796
  358. package/.claude/skills/v3-ddd-architecture/SKILL.md +441 -441
  359. package/.claude/skills/v3-integration-deep/SKILL.md +240 -240
  360. package/.claude/skills/v3-mcp-optimization/SKILL.md +776 -776
  361. package/.claude/skills/v3-memory-unification/SKILL.md +173 -173
  362. package/.claude/skills/v3-performance-optimization/SKILL.md +389 -389
  363. package/.claude/skills/v3-security-overhaul/SKILL.md +81 -81
  364. package/.claude/skills/v3-swarm-coordination/SKILL.md +339 -339
  365. package/.claude/skills/verification-quality/SKILL.md +691 -691
  366. package/.claude/skills/worker-benchmarks/SKILL.md +129 -129
  367. package/.claude/skills/worker-integration/SKILL.md +147 -147
  368. package/.claude/statusline-command.sh +176 -176
  369. package/.claude/statusline.mjs +109 -109
  370. package/.claude/statusline.sh +431 -431
  371. package/.claude/workflows/full-system-test.js +65 -65
  372. package/.claude/workflows/intelligence-system-hardening.js +120 -120
  373. package/.claude/workflows/plugin-contract-audit.js +91 -91
  374. package/.claude-plugin/README.md +720 -720
  375. package/.claude-plugin/docs/INSTALLATION.md +261 -261
  376. package/.claude-plugin/docs/PLUGIN_SUMMARY.md +361 -361
  377. package/.claude-plugin/docs/QUICKSTART.md +361 -361
  378. package/.claude-plugin/docs/STRUCTURE.md +128 -128
  379. package/.claude-plugin/hooks/hooks.json +77 -77
  380. package/.claude-plugin/marketplace.json +185 -185
  381. package/.claude-plugin/plugin.json +71 -71
  382. package/.claude-plugin/scripts/install.sh +234 -234
  383. package/.claude-plugin/scripts/ruflo-hook.cjs +166 -166
  384. package/.claude-plugin/scripts/ruflo-hook.sh +37 -37
  385. package/.claude-plugin/scripts/uninstall.sh +36 -36
  386. package/.claude-plugin/scripts/verify.sh +108 -108
  387. package/LICENSE +21 -21
  388. package/README.md +419 -419
  389. package/bin/cli.js +11 -11
  390. package/bin/npx-repair.js +7 -7
  391. package/bin/npx-safe-launch.js +9 -9
  392. package/package.json +185 -185
  393. package/v3/@claude-flow/cli/README.md +419 -419
  394. package/v3/@claude-flow/cli/bin/cli.js +314 -314
  395. package/v3/@claude-flow/cli/bin/mcp-server.js +224 -224
  396. package/v3/@claude-flow/cli/bin/preinstall.cjs +2 -2
  397. package/v3/@claude-flow/cli/catalog-manifest.json +2 -2
  398. package/v3/@claude-flow/cli/dist/src/benchmarks/gaia-critic.js +24 -24
  399. package/v3/@claude-flow/cli/dist/src/business-pods/bbs-budget-tracker.js +53 -53
  400. package/v3/@claude-flow/cli/dist/src/commands/completions.js +409 -409
  401. package/v3/@claude-flow/cli/dist/src/commands/daemon.d.ts +23 -0
  402. package/v3/@claude-flow/cli/dist/src/commands/daemon.js +216 -44
  403. package/v3/@claude-flow/cli/dist/src/commands/embeddings.js +26 -26
  404. package/v3/@claude-flow/cli/dist/src/commands/funnel.d.ts +2 -0
  405. package/v3/@claude-flow/cli/dist/src/commands/funnel.js +110 -2
  406. package/v3/@claude-flow/cli/dist/src/commands/hive-mind.js +97 -97
  407. package/v3/@claude-flow/cli/dist/src/commands/hooks.js +9 -9
  408. package/v3/@claude-flow/cli/dist/src/commands/init.js +94 -38
  409. package/v3/@claude-flow/cli/dist/src/commands/memory.js +85 -1
  410. package/v3/@claude-flow/cli/dist/src/commands/ruvector/backup.js +23 -23
  411. package/v3/@claude-flow/cli/dist/src/commands/ruvector/benchmark.js +31 -31
  412. package/v3/@claude-flow/cli/dist/src/commands/ruvector/import.js +14 -14
  413. package/v3/@claude-flow/cli/dist/src/commands/ruvector/init.js +115 -115
  414. package/v3/@claude-flow/cli/dist/src/commands/ruvector/migrate.js +99 -99
  415. package/v3/@claude-flow/cli/dist/src/commands/ruvector/optimize.js +51 -51
  416. package/v3/@claude-flow/cli/dist/src/commands/ruvector/setup.js +624 -624
  417. package/v3/@claude-flow/cli/dist/src/commands/ruvector/status.js +38 -38
  418. package/v3/@claude-flow/cli/dist/src/config/proven-config.js +2 -2
  419. package/v3/@claude-flow/cli/dist/src/funnel/disclosure.d.ts +1 -0
  420. package/v3/@claude-flow/cli/dist/src/funnel/disclosure.js +12 -0
  421. package/v3/@claude-flow/cli/dist/src/funnel/index.d.ts +1 -1
  422. package/v3/@claude-flow/cli/dist/src/funnel/index.js +1 -1
  423. package/v3/@claude-flow/cli/dist/src/init/claudemd-generator.js +231 -231
  424. package/v3/@claude-flow/cli/dist/src/init/executor.js +453 -453
  425. package/v3/@claude-flow/cli/dist/src/init/helper-signing.d.ts +8 -1
  426. package/v3/@claude-flow/cli/dist/src/init/helper-signing.js +9 -2
  427. package/v3/@claude-flow/cli/dist/src/init/helpers-generator.js +751 -751
  428. package/v3/@claude-flow/cli/dist/src/init/statusline-generator.js +994 -951
  429. package/v3/@claude-flow/cli/dist/src/mcp-tools/agentdb-tools.js +15 -15
  430. package/v3/@claude-flow/cli/dist/src/mcp-tools/browser-intent-tools.js +19 -19
  431. package/v3/@claude-flow/cli/dist/src/memory/graph-edge-writer.js +22 -22
  432. package/v3/@claude-flow/cli/dist/src/memory/memory-bridge.d.ts +10 -0
  433. package/v3/@claude-flow/cli/dist/src/memory/memory-bridge.js +139 -88
  434. package/v3/@claude-flow/cli/dist/src/memory/memory-initializer.d.ts +18 -0
  435. package/v3/@claude-flow/cli/dist/src/memory/memory-initializer.js +539 -407
  436. package/v3/@claude-flow/cli/dist/src/memory/rabitq-index.js +5 -5
  437. package/v3/@claude-flow/cli/dist/src/runtime/headless.js +28 -28
  438. package/v3/@claude-flow/cli/dist/src/ruvector/flash-attention.d.ts +195 -0
  439. package/v3/@claude-flow/cli/dist/src/ruvector/flash-attention.js +643 -0
  440. package/v3/@claude-flow/cli/dist/src/ruvector/moe-router.d.ts +206 -0
  441. package/v3/@claude-flow/cli/dist/src/ruvector/moe-router.js +626 -0
  442. package/v3/@claude-flow/cli/dist/src/services/distill-tuning.js +7 -7
  443. package/v3/@claude-flow/cli/dist/src/services/event-stream.d.ts +25 -0
  444. package/v3/@claude-flow/cli/dist/src/services/event-stream.js +27 -0
  445. package/v3/@claude-flow/cli/dist/src/services/global-ai-budget.d.ts +25 -0
  446. package/v3/@claude-flow/cli/dist/src/services/global-ai-budget.js +56 -0
  447. package/v3/@claude-flow/cli/dist/src/services/headless-worker-executor.d.ts +21 -0
  448. package/v3/@claude-flow/cli/dist/src/services/headless-worker-executor.js +152 -86
  449. package/v3/@claude-flow/cli/dist/src/services/loop-worker-runner.d.ts +16 -0
  450. package/v3/@claude-flow/cli/dist/src/services/loop-worker-runner.js +34 -0
  451. package/v3/@claude-flow/cli/dist/src/services/memory-distillation.js +4 -4
  452. package/v3/@claude-flow/cli/dist/src/services/repo-supervisor.d.ts +70 -0
  453. package/v3/@claude-flow/cli/dist/src/services/repo-supervisor.js +228 -0
  454. package/v3/@claude-flow/cli/dist/src/services/runtime-capabilities.d.ts +22 -0
  455. package/v3/@claude-flow/cli/dist/src/services/runtime-capabilities.js +45 -0
  456. package/v3/@claude-flow/cli/dist/src/services/worker-daemon.d.ts +26 -0
  457. package/v3/@claude-flow/cli/dist/src/services/worker-daemon.js +95 -5
  458. package/v3/@claude-flow/cli/dist/src/services/workspace-lease.d.ts +55 -0
  459. package/v3/@claude-flow/cli/dist/src/services/workspace-lease.js +191 -0
  460. package/v3/@claude-flow/cli/dist/src/transfer/deploy-seraphine.js +23 -23
  461. package/v3/@claude-flow/cli/package.json +134 -133
  462. package/v3/@claude-flow/guidance/README.md +1195 -1195
  463. package/v3/@claude-flow/guidance/dist/adversarial.d.ts +284 -0
  464. package/v3/@claude-flow/guidance/dist/adversarial.js +572 -0
  465. package/v3/@claude-flow/guidance/dist/analyzer.d.ts +530 -0
  466. package/v3/@claude-flow/guidance/dist/analyzer.js +2587 -0
  467. package/v3/@claude-flow/guidance/dist/artifacts.d.ts +283 -0
  468. package/v3/@claude-flow/guidance/dist/artifacts.js +356 -0
  469. package/v3/@claude-flow/guidance/dist/authority.d.ts +290 -0
  470. package/v3/@claude-flow/guidance/dist/authority.js +558 -0
  471. package/v3/@claude-flow/guidance/dist/capabilities.d.ts +209 -0
  472. package/v3/@claude-flow/guidance/dist/capabilities.js +485 -0
  473. package/v3/@claude-flow/guidance/dist/coherence.d.ts +233 -0
  474. package/v3/@claude-flow/guidance/dist/coherence.js +372 -0
  475. package/v3/@claude-flow/guidance/dist/compiler.d.ts +87 -0
  476. package/v3/@claude-flow/guidance/dist/compiler.js +410 -0
  477. package/v3/@claude-flow/guidance/dist/conformance-kit.d.ts +225 -0
  478. package/v3/@claude-flow/guidance/dist/conformance-kit.js +629 -0
  479. package/v3/@claude-flow/guidance/dist/continue-gate.d.ts +214 -0
  480. package/v3/@claude-flow/guidance/dist/continue-gate.js +353 -0
  481. package/v3/@claude-flow/guidance/dist/crypto-utils.d.ts +17 -0
  482. package/v3/@claude-flow/guidance/dist/crypto-utils.js +24 -0
  483. package/v3/@claude-flow/guidance/dist/evolution.d.ts +282 -0
  484. package/v3/@claude-flow/guidance/dist/evolution.js +500 -0
  485. package/v3/@claude-flow/guidance/dist/gates.d.ts +79 -0
  486. package/v3/@claude-flow/guidance/dist/gates.js +302 -0
  487. package/v3/@claude-flow/guidance/dist/gateway.d.ts +206 -0
  488. package/v3/@claude-flow/guidance/dist/gateway.js +452 -0
  489. package/v3/@claude-flow/guidance/dist/generators.d.ts +153 -0
  490. package/v3/@claude-flow/guidance/dist/generators.js +682 -0
  491. package/v3/@claude-flow/guidance/dist/headless.d.ts +177 -0
  492. package/v3/@claude-flow/guidance/dist/headless.js +342 -0
  493. package/v3/@claude-flow/guidance/dist/hooks.d.ts +109 -0
  494. package/v3/@claude-flow/guidance/dist/hooks.js +347 -0
  495. package/v3/@claude-flow/guidance/dist/index.d.ts +205 -0
  496. package/v3/@claude-flow/guidance/dist/index.js +321 -0
  497. package/v3/@claude-flow/guidance/dist/ledger.d.ts +162 -0
  498. package/v3/@claude-flow/guidance/dist/ledger.js +375 -0
  499. package/v3/@claude-flow/guidance/dist/manifest-validator.d.ts +289 -0
  500. package/v3/@claude-flow/guidance/dist/manifest-validator.js +838 -0
  501. package/v3/@claude-flow/guidance/dist/memory-gate.d.ts +222 -0
  502. package/v3/@claude-flow/guidance/dist/memory-gate.js +382 -0
  503. package/v3/@claude-flow/guidance/dist/meta-governance.d.ts +265 -0
  504. package/v3/@claude-flow/guidance/dist/meta-governance.js +348 -0
  505. package/v3/@claude-flow/guidance/dist/optimizer.d.ts +104 -0
  506. package/v3/@claude-flow/guidance/dist/optimizer.js +329 -0
  507. package/v3/@claude-flow/guidance/dist/persistence.d.ts +189 -0
  508. package/v3/@claude-flow/guidance/dist/persistence.js +464 -0
  509. package/v3/@claude-flow/guidance/dist/proof.d.ts +185 -0
  510. package/v3/@claude-flow/guidance/dist/proof.js +238 -0
  511. package/v3/@claude-flow/guidance/dist/retriever.d.ts +172 -0
  512. package/v3/@claude-flow/guidance/dist/retriever.js +596 -0
  513. package/v3/@claude-flow/guidance/dist/ruvbot-integration.d.ts +370 -0
  514. package/v3/@claude-flow/guidance/dist/ruvbot-integration.js +738 -0
  515. package/v3/@claude-flow/guidance/dist/temporal.d.ts +426 -0
  516. package/v3/@claude-flow/guidance/dist/temporal.js +658 -0
  517. package/v3/@claude-flow/guidance/dist/trust.d.ts +283 -0
  518. package/v3/@claude-flow/guidance/dist/trust.js +473 -0
  519. package/v3/@claude-flow/guidance/dist/truth-anchors.d.ts +276 -0
  520. package/v3/@claude-flow/guidance/dist/truth-anchors.js +488 -0
  521. package/v3/@claude-flow/guidance/dist/types.d.ts +378 -0
  522. package/v3/@claude-flow/guidance/dist/types.js +10 -0
  523. package/v3/@claude-flow/guidance/dist/uncertainty.d.ts +372 -0
  524. package/v3/@claude-flow/guidance/dist/uncertainty.js +619 -0
  525. package/v3/@claude-flow/guidance/dist/wasm-kernel.d.ts +48 -0
  526. package/v3/@claude-flow/guidance/dist/wasm-kernel.js +158 -0
  527. package/v3/@claude-flow/guidance/package.json +198 -198
  528. package/v3/@claude-flow/shared/README.md +323 -323
  529. package/v3/@claude-flow/shared/dist/core/config/defaults.d.ts +41 -0
  530. package/v3/@claude-flow/shared/dist/core/config/defaults.js +186 -0
  531. package/v3/@claude-flow/shared/dist/core/config/index.d.ts +8 -0
  532. package/v3/@claude-flow/shared/dist/core/config/index.js +12 -0
  533. package/v3/@claude-flow/shared/dist/core/config/loader.d.ts +45 -0
  534. package/v3/@claude-flow/shared/dist/core/config/loader.js +238 -0
  535. package/v3/@claude-flow/shared/dist/core/config/schema.d.ts +1728 -0
  536. package/v3/@claude-flow/shared/dist/core/config/schema.js +160 -0
  537. package/v3/@claude-flow/shared/dist/core/config/validator.d.ts +92 -0
  538. package/v3/@claude-flow/shared/dist/core/config/validator.js +147 -0
  539. package/v3/@claude-flow/shared/dist/core/event-bus.d.ts +31 -0
  540. package/v3/@claude-flow/shared/dist/core/event-bus.js +197 -0
  541. package/v3/@claude-flow/shared/dist/core/index.d.ts +15 -0
  542. package/v3/@claude-flow/shared/dist/core/index.js +19 -0
  543. package/v3/@claude-flow/shared/dist/core/interfaces/agent.interface.d.ts +200 -0
  544. package/v3/@claude-flow/shared/dist/core/interfaces/agent.interface.js +6 -0
  545. package/v3/@claude-flow/shared/dist/core/interfaces/coordinator.interface.d.ts +310 -0
  546. package/v3/@claude-flow/shared/dist/core/interfaces/coordinator.interface.js +7 -0
  547. package/v3/@claude-flow/shared/dist/core/interfaces/event.interface.d.ts +224 -0
  548. package/v3/@claude-flow/shared/dist/core/interfaces/event.interface.js +46 -0
  549. package/v3/@claude-flow/shared/dist/core/interfaces/index.d.ts +10 -0
  550. package/v3/@claude-flow/shared/dist/core/interfaces/index.js +15 -0
  551. package/v3/@claude-flow/shared/dist/core/interfaces/memory.interface.d.ts +298 -0
  552. package/v3/@claude-flow/shared/dist/core/interfaces/memory.interface.js +7 -0
  553. package/v3/@claude-flow/shared/dist/core/interfaces/task.interface.d.ts +185 -0
  554. package/v3/@claude-flow/shared/dist/core/interfaces/task.interface.js +6 -0
  555. package/v3/@claude-flow/shared/dist/core/orchestrator/event-coordinator.d.ts +35 -0
  556. package/v3/@claude-flow/shared/dist/core/orchestrator/event-coordinator.js +101 -0
  557. package/v3/@claude-flow/shared/dist/core/orchestrator/health-monitor.d.ts +60 -0
  558. package/v3/@claude-flow/shared/dist/core/orchestrator/health-monitor.js +166 -0
  559. package/v3/@claude-flow/shared/dist/core/orchestrator/index.d.ts +46 -0
  560. package/v3/@claude-flow/shared/dist/core/orchestrator/index.js +64 -0
  561. package/v3/@claude-flow/shared/dist/core/orchestrator/lifecycle-manager.d.ts +56 -0
  562. package/v3/@claude-flow/shared/dist/core/orchestrator/lifecycle-manager.js +195 -0
  563. package/v3/@claude-flow/shared/dist/core/orchestrator/session-manager.d.ts +83 -0
  564. package/v3/@claude-flow/shared/dist/core/orchestrator/session-manager.js +193 -0
  565. package/v3/@claude-flow/shared/dist/core/orchestrator/task-manager.d.ts +49 -0
  566. package/v3/@claude-flow/shared/dist/core/orchestrator/task-manager.js +253 -0
  567. package/v3/@claude-flow/shared/dist/events/domain-events.d.ts +282 -0
  568. package/v3/@claude-flow/shared/dist/events/domain-events.js +165 -0
  569. package/v3/@claude-flow/shared/dist/events/event-store.d.ts +126 -0
  570. package/v3/@claude-flow/shared/dist/events/event-store.js +427 -0
  571. package/v3/@claude-flow/shared/dist/events/event-store.test.d.ts +8 -0
  572. package/v3/@claude-flow/shared/dist/events/event-store.test.js +293 -0
  573. package/v3/@claude-flow/shared/dist/events/example-usage.d.ts +10 -0
  574. package/v3/@claude-flow/shared/dist/events/example-usage.js +193 -0
  575. package/v3/@claude-flow/shared/dist/events/index.d.ts +21 -0
  576. package/v3/@claude-flow/shared/dist/events/index.js +22 -0
  577. package/v3/@claude-flow/shared/dist/events/projections.d.ts +177 -0
  578. package/v3/@claude-flow/shared/dist/events/projections.js +421 -0
  579. package/v3/@claude-flow/shared/dist/events/rvf-event-log.d.ts +82 -0
  580. package/v3/@claude-flow/shared/dist/events/rvf-event-log.js +340 -0
  581. package/v3/@claude-flow/shared/dist/events/state-reconstructor.d.ts +101 -0
  582. package/v3/@claude-flow/shared/dist/events/state-reconstructor.js +263 -0
  583. package/v3/@claude-flow/shared/dist/events.d.ts +80 -0
  584. package/v3/@claude-flow/shared/dist/events.js +249 -0
  585. package/v3/@claude-flow/shared/dist/hooks/example-usage.d.ts +42 -0
  586. package/v3/@claude-flow/shared/dist/hooks/example-usage.js +351 -0
  587. package/v3/@claude-flow/shared/dist/hooks/executor.d.ts +100 -0
  588. package/v3/@claude-flow/shared/dist/hooks/executor.js +267 -0
  589. package/v3/@claude-flow/shared/dist/hooks/hooks.test.d.ts +9 -0
  590. package/v3/@claude-flow/shared/dist/hooks/hooks.test.js +322 -0
  591. package/v3/@claude-flow/shared/dist/hooks/index.d.ts +52 -0
  592. package/v3/@claude-flow/shared/dist/hooks/index.js +51 -0
  593. package/v3/@claude-flow/shared/dist/hooks/registry.d.ts +133 -0
  594. package/v3/@claude-flow/shared/dist/hooks/registry.js +277 -0
  595. package/v3/@claude-flow/shared/dist/hooks/safety/bash-safety.d.ts +105 -0
  596. package/v3/@claude-flow/shared/dist/hooks/safety/bash-safety.js +481 -0
  597. package/v3/@claude-flow/shared/dist/hooks/safety/file-organization.d.ts +144 -0
  598. package/v3/@claude-flow/shared/dist/hooks/safety/file-organization.js +328 -0
  599. package/v3/@claude-flow/shared/dist/hooks/safety/git-commit.d.ts +158 -0
  600. package/v3/@claude-flow/shared/dist/hooks/safety/git-commit.js +450 -0
  601. package/v3/@claude-flow/shared/dist/hooks/safety/index.d.ts +17 -0
  602. package/v3/@claude-flow/shared/dist/hooks/safety/index.js +17 -0
  603. package/v3/@claude-flow/shared/dist/hooks/session-hooks.d.ts +234 -0
  604. package/v3/@claude-flow/shared/dist/hooks/session-hooks.js +334 -0
  605. package/v3/@claude-flow/shared/dist/hooks/task-hooks.d.ts +163 -0
  606. package/v3/@claude-flow/shared/dist/hooks/task-hooks.js +326 -0
  607. package/v3/@claude-flow/shared/dist/hooks/types.d.ts +267 -0
  608. package/v3/@claude-flow/shared/dist/hooks/types.js +62 -0
  609. package/v3/@claude-flow/shared/dist/hooks/verify-exports.test.d.ts +9 -0
  610. package/v3/@claude-flow/shared/dist/hooks/verify-exports.test.js +93 -0
  611. package/v3/@claude-flow/shared/dist/index.d.ts +20 -0
  612. package/v3/@claude-flow/shared/dist/index.js +50 -0
  613. package/v3/@claude-flow/shared/dist/mcp/connection-pool.d.ts +98 -0
  614. package/v3/@claude-flow/shared/dist/mcp/connection-pool.js +364 -0
  615. package/v3/@claude-flow/shared/dist/mcp/index.d.ts +69 -0
  616. package/v3/@claude-flow/shared/dist/mcp/index.js +84 -0
  617. package/v3/@claude-flow/shared/dist/mcp/server.d.ts +166 -0
  618. package/v3/@claude-flow/shared/dist/mcp/server.js +590 -0
  619. package/v3/@claude-flow/shared/dist/mcp/session-manager.d.ts +136 -0
  620. package/v3/@claude-flow/shared/dist/mcp/session-manager.js +335 -0
  621. package/v3/@claude-flow/shared/dist/mcp/tool-registry.d.ts +178 -0
  622. package/v3/@claude-flow/shared/dist/mcp/tool-registry.js +439 -0
  623. package/v3/@claude-flow/shared/dist/mcp/transport/http.d.ts +104 -0
  624. package/v3/@claude-flow/shared/dist/mcp/transport/http.js +476 -0
  625. package/v3/@claude-flow/shared/dist/mcp/transport/index.d.ts +102 -0
  626. package/v3/@claude-flow/shared/dist/mcp/transport/index.js +238 -0
  627. package/v3/@claude-flow/shared/dist/mcp/transport/stdio.d.ts +104 -0
  628. package/v3/@claude-flow/shared/dist/mcp/transport/stdio.js +263 -0
  629. package/v3/@claude-flow/shared/dist/mcp/transport/websocket.d.ts +133 -0
  630. package/v3/@claude-flow/shared/dist/mcp/transport/websocket.js +396 -0
  631. package/v3/@claude-flow/shared/dist/mcp/types.d.ts +436 -0
  632. package/v3/@claude-flow/shared/dist/mcp/types.js +54 -0
  633. package/v3/@claude-flow/shared/dist/plugin-interface.d.ts +544 -0
  634. package/v3/@claude-flow/shared/dist/plugin-interface.js +23 -0
  635. package/v3/@claude-flow/shared/dist/plugin-loader.d.ts +139 -0
  636. package/v3/@claude-flow/shared/dist/plugin-loader.js +434 -0
  637. package/v3/@claude-flow/shared/dist/plugin-registry.d.ts +183 -0
  638. package/v3/@claude-flow/shared/dist/plugin-registry.js +457 -0
  639. package/v3/@claude-flow/shared/dist/plugins/index.d.ts +10 -0
  640. package/v3/@claude-flow/shared/dist/plugins/index.js +10 -0
  641. package/v3/@claude-flow/shared/dist/plugins/official/hive-mind-plugin.d.ts +106 -0
  642. package/v3/@claude-flow/shared/dist/plugins/official/hive-mind-plugin.js +241 -0
  643. package/v3/@claude-flow/shared/dist/plugins/official/index.d.ts +10 -0
  644. package/v3/@claude-flow/shared/dist/plugins/official/index.js +10 -0
  645. package/v3/@claude-flow/shared/dist/plugins/official/maestro-plugin.d.ts +121 -0
  646. package/v3/@claude-flow/shared/dist/plugins/official/maestro-plugin.js +355 -0
  647. package/v3/@claude-flow/shared/dist/plugins/types.d.ts +93 -0
  648. package/v3/@claude-flow/shared/dist/plugins/types.js +9 -0
  649. package/v3/@claude-flow/shared/dist/resilience/bulkhead.d.ts +105 -0
  650. package/v3/@claude-flow/shared/dist/resilience/bulkhead.js +206 -0
  651. package/v3/@claude-flow/shared/dist/resilience/circuit-breaker.d.ts +132 -0
  652. package/v3/@claude-flow/shared/dist/resilience/circuit-breaker.js +233 -0
  653. package/v3/@claude-flow/shared/dist/resilience/index.d.ts +19 -0
  654. package/v3/@claude-flow/shared/dist/resilience/index.js +19 -0
  655. package/v3/@claude-flow/shared/dist/resilience/rate-limiter.d.ts +168 -0
  656. package/v3/@claude-flow/shared/dist/resilience/rate-limiter.js +314 -0
  657. package/v3/@claude-flow/shared/dist/resilience/retry.d.ts +91 -0
  658. package/v3/@claude-flow/shared/dist/resilience/retry.js +159 -0
  659. package/v3/@claude-flow/shared/dist/security/index.d.ts +10 -0
  660. package/v3/@claude-flow/shared/dist/security/index.js +12 -0
  661. package/v3/@claude-flow/shared/dist/security/input-validation.d.ts +73 -0
  662. package/v3/@claude-flow/shared/dist/security/input-validation.js +201 -0
  663. package/v3/@claude-flow/shared/dist/security/secure-random.d.ts +92 -0
  664. package/v3/@claude-flow/shared/dist/security/secure-random.js +142 -0
  665. package/v3/@claude-flow/shared/dist/services/index.d.ts +7 -0
  666. package/v3/@claude-flow/shared/dist/services/index.js +7 -0
  667. package/v3/@claude-flow/shared/dist/services/v3-progress.service.d.ts +124 -0
  668. package/v3/@claude-flow/shared/dist/services/v3-progress.service.js +402 -0
  669. package/v3/@claude-flow/shared/dist/types/agent.types.d.ts +137 -0
  670. package/v3/@claude-flow/shared/dist/types/agent.types.js +6 -0
  671. package/v3/@claude-flow/shared/dist/types/index.d.ts +11 -0
  672. package/v3/@claude-flow/shared/dist/types/index.js +17 -0
  673. package/v3/@claude-flow/shared/dist/types/mcp.types.d.ts +266 -0
  674. package/v3/@claude-flow/shared/dist/types/mcp.types.js +7 -0
  675. package/v3/@claude-flow/shared/dist/types/memory.types.d.ts +236 -0
  676. package/v3/@claude-flow/shared/dist/types/memory.types.js +7 -0
  677. package/v3/@claude-flow/shared/dist/types/swarm.types.d.ts +186 -0
  678. package/v3/@claude-flow/shared/dist/types/swarm.types.js +65 -0
  679. package/v3/@claude-flow/shared/dist/types/task.types.d.ts +178 -0
  680. package/v3/@claude-flow/shared/dist/types/task.types.js +32 -0
  681. package/v3/@claude-flow/shared/dist/types.d.ts +197 -0
  682. package/v3/@claude-flow/shared/dist/types.js +21 -0
  683. package/v3/@claude-flow/shared/dist/utils/secure-logger.d.ts +69 -0
  684. package/v3/@claude-flow/shared/dist/utils/secure-logger.js +208 -0
  685. package/v3/@claude-flow/shared/package.json +43 -43
  686. package/v3/README.md +493 -493
  687. package/.claude/security-scans/scan-all-standard.json +0 -321
  688. package/.claude/security-scans/scan-code-deep.json +0 -1209
  689. package/.claude/security-scans/scan-deps-full.json +0 -14
@@ -9,7 +9,50 @@
9
9
  * - Shared settings cache
10
10
  * - Strict 2s timeouts on all shell calls
11
11
  */
12
- import { getInstalledCliVersion } from './helper-refresh.js';
12
+ import * as fs from 'fs';
13
+ import * as path from 'path';
14
+ import { fileURLToPath } from 'url';
15
+ import { createRequire } from 'module';
16
+ const __dirname_sg = path.dirname(fileURLToPath(import.meta.url));
17
+ /**
18
+ * Resolves the running CLI's own version — same createRequire/walk-up
19
+ * approach as helper-refresh.ts's getInstalledCliVersion(), duplicated
20
+ * here rather than imported. helper-refresh.ts pulls in the `semver`
21
+ * package at module scope (for autoRefreshHelpersIfStale()'s version
22
+ * comparison, unrelated to this) — ES module imports load a module's
23
+ * ENTIRE top-level regardless of which export is used, so importing just
24
+ * getInstalledCliVersion from there still requires `semver` to be resolvable.
25
+ * Confirmed live: the CI smoke job that loads this generator via a minimal
26
+ * "smoke deps" install (no full `npm install`) failed with
27
+ * ERR_MODULE_NOT_FOUND('semver') the moment this file gained that import,
28
+ * even though this function itself never touches semver. Keeping this
29
+ * generator's own dependency footprint to bare Node builtins avoids
30
+ * dragging every future helper-refresh.ts dependency into every context
31
+ * that merely wants to render a statusline script.
32
+ */
33
+ function getInstalledCliVersionLocal() {
34
+ try {
35
+ const esmRequire = createRequire(import.meta.url);
36
+ const pkg = JSON.parse(fs.readFileSync(esmRequire.resolve('@claude-flow/cli/package.json'), 'utf-8'));
37
+ return String(pkg.version || '0.0.0');
38
+ }
39
+ catch {
40
+ let dir = __dirname_sg;
41
+ for (let i = 0; i < 6; i++) {
42
+ try {
43
+ const pkg = JSON.parse(fs.readFileSync(path.join(dir, 'package.json'), 'utf-8'));
44
+ if (pkg && pkg.name === '@claude-flow/cli')
45
+ return String(pkg.version || '0.0.0');
46
+ }
47
+ catch { /* no package.json here, or unreadable — keep climbing */ }
48
+ const parent = path.dirname(dir);
49
+ if (parent === dir)
50
+ break;
51
+ dir = parent;
52
+ }
53
+ return '0.0.0';
54
+ }
55
+ }
13
56
  /**
14
57
  * Generate optimized statusline script
15
58
  * Output format:
@@ -33,932 +76,932 @@ export function generateStatuslineScript(options) {
33
76
  // previous hardcoded "3.6" placeholder. getPkgVersion()'s own runtime
34
77
  // candidate scan still wins over this baked-in value when it finds
35
78
  // something newer (e.g. a later `npm update` in the same project).
36
- const bakedVersion = getInstalledCliVersion();
37
- return `#!/usr/bin/env node
38
- /**
39
- * RuFlo V3 Statusline — delegation build (#2195)
40
- *
41
- * Fix for ruvnet/ruflo#2195: the previous version re-implemented all data
42
- * readers locally using fragile file probes that missed AgentDB patterns,
43
- * the v3/docs/adr/ ADR directory, and the real vector count.
44
- *
45
- * This version delegates to 'npx @claude-flow/cli hooks statusline --json'
46
- * as the single source of truth. That command queries AgentDB directly,
47
- * counts ADRs in both directories, and reports the real intelligence pct.
48
- *
49
- * ADR counting falls back to local file reads so the display still works
50
- * without network access (counts both v3/docs/adr/ and v3/implementation/adrs/).
51
- *
52
- * Cache: JSON result is cached in /tmp for 10s so rapid prompt triggers
53
- * (every keystroke in some shells) don't hammer the CLI on every call.
54
- *
55
- * Usage: node statusline.cjs [--json] [--compact] [--dashboard]
56
- */
57
-
58
- /* eslint-disable @typescript-eslint/no-var-requires */
59
- const fs = require('fs');
60
- const path = require('path');
61
- const { execSync } = require('child_process');
62
- const os = require('os');
63
-
64
- // Configuration
65
- const CONFIG = {
66
- maxAgents: ${maxAgents},
67
- // Session-cost display. Claude Code's cost.total_cost_usd is a client-side
68
- // estimate that "may differ from your actual bill" and reads as misleading on
69
- // subscription plans, where token usage is not billed per dollar. These let
70
- // each user pick what the segment means to them without changing the default.
71
- // RUFLO_STATUSLINE_COST_SYMBOL override the leading '$' (e.g. ⚡, €, 🌱);
72
- // set to an empty string for the number alone.
73
- // RUFLO_STATUSLINE_HIDE_COST 1/true/yes/on removes the segment entirely.
74
- costSymbol: process.env.RUFLO_STATUSLINE_COST_SYMBOL ?? '$',
75
- hideCost: /^(1|true|yes|on)$/i.test(process.env.RUFLO_STATUSLINE_HIDE_COST || ''),
76
- };
77
-
78
- const CWD = process.cwd();
79
-
80
- // ─── Delegation cache ───────────────────────────────────────────
81
- // Cache the CLI JSON result for 60s so rapid prompt re-renders
82
- // (Claude Code refreshes the statusline several times a second while
83
- // streaming) don't re-invoke the CLI each time. #2337: bumped 10s→60s
84
- // because 10s was far too short for how often Claude Code re-renders.
85
- const CACHE_FILE = path.join(os.tmpdir(), 'ruflo-statusline-cache-' + require('crypto').createHash('md5').update(CWD).digest('hex').slice(0, 8) + '.json');
86
- const CACHE_TTL_MS = 60000;
87
-
88
- // The promo/insight row is designed to rotate on a 20s cadence (funnel/
89
- // rotation.ts's ROTATION_SLOT_MS / funnel/promo.ts's insight-slot check —
90
- // duplicated here as a bare number since this generated script has no
91
- // runtime import of the funnel module; keep in sync if that constant ever
92
- // changes). The rotation slot is only ever (re)computed SERVER-SIDE inside
93
- // the CLI subprocess this file shells out to — so a general 60s data cache
94
- // (correct and necessary for #2337) silently made that 20s design
95
- // unreachable: cache.fresh stayed true across 2-3 whole rotation slots,
96
- // so the row visibly "didn't rotate" (user report). Fix: track promo
97
- // freshness on its OWN, tighter clock — when it lags behind the current
98
- // slot, fall through to a real CLI call even though the REST of the
99
- // cached data (security/swarm/system) is still within CACHE_TTL_MS. This
100
- // does not touch or regress #2337's fix; it only adds a narrower check.
101
- const PROMO_ROTATION_SLOT_MS = 20000;
102
-
103
- // Persistent last-known-good promo record. Lives outside the /tmp cache so it
104
- // survives a full cache wipe / cache write race / CLI failure combo. Written
105
- // every time we successfully render a promo; read as a last resort so the row
106
- // never blinks out mid-session (was: 'promo shows then hides' bug report).
107
- const PROMO_MEMO_FILE = path.join(os.homedir(), '.ruflo', 'statusline-promo.json');
108
- const PROMO_MEMO_TTL_MS = 6 * 60 * 60 * 1000; // 6h — long enough to bridge any hiccup, short enough that a real disable takes effect fast.
109
-
110
- // #2337: resolve an already-installed @claude-flow/cli (or ruflo) bin so we
111
- // can invoke it directly via \`node\`. The previous version called
112
- // \`npx --yes @claude-flow/cli@latest\` on every uncached render, which forces
113
- // a registry resolution + cold-start of the entire CLI per render. With
114
- // multiple concurrent Claude Code sessions this storms the host (reporter
115
- // saw load average 40-65 on a 12-core box).
116
- //
117
- // Returns EVERY existing bin/cli.js candidate, in preference order (project,
118
- // monorepo, plugin marketplace, global node_modules including custom-prefix
119
- // layouts like ~/.npm-global) — mirrors getPkgVersion()'s own path probing.
120
- //
121
- // Returns a list, not a single winner: \`fs.existsSync\` only proves a file is
122
- // present, not that it actually runs. A marketplace/npx-cached install can
123
- // exist on disk but be broken (observed in practice: a stale marketplace
124
- // checkout whose dist/ imports a workspace package, '@claude-flow/cli-core',
125
- // that isn't bundled there — every invocation throws ERR_MODULE_NOT_FOUND).
126
- // Picking the first EXISTING path and never falling through meant a single
127
- // broken install silently killed the promo row for the entire session (the
128
- // CLI call always failed, so the memo could never refresh and eventually
129
- // expired). getStatuslineData() now walks this whole list and tries the next
130
- // candidate on failure, so one broken install can't permanently wedge it.
131
- function resolveCliBinCandidates() {
132
- const candidates = [];
133
- try {
134
- const home = os.homedir();
135
- candidates.push(
136
- path.join(home, '.claude', 'plugins', 'marketplaces', 'ruflo', 'bin', 'cli.js'),
137
- path.join(CWD, 'node_modules', '@claude-flow', 'cli', 'bin', 'cli.js'),
138
- path.join(CWD, 'node_modules', 'ruflo', 'bin', 'cli.js'),
139
- path.join(CWD, 'v3', '@claude-flow', 'cli', 'bin', 'cli.js'),
140
- );
141
- try {
142
- const binDir = path.dirname(process.execPath);
143
- const globalModuleDirs = [path.join(binDir, '..', 'lib', 'node_modules'), path.join(binDir, 'node_modules')];
144
- for (const prefix of [process.env.npm_config_prefix, process.env.PREFIX, path.join(home, '.npm-global')]) {
145
- if (prefix) globalModuleDirs.push(path.join(prefix, 'lib', 'node_modules'));
146
- }
147
- for (const gm of globalModuleDirs) {
148
- candidates.push(
149
- path.join(gm, 'ruflo', 'bin', 'cli.js'),
150
- path.join(gm, '@claude-flow', 'cli', 'bin', 'cli.js'),
151
- );
152
- }
153
- } catch { /* ignore */ }
154
- } catch { /* ignore */ }
155
- return candidates.filter((p) => {
156
- try {
157
- if (!fs.existsSync(p)) return false;
158
- // A candidate's bin/cli.js can exist on disk while its compiled
159
- // dist/ never got built (Claude Code's own plugin marketplace just
160
- // git-clones the repo — no install/build step — so every marketplace
161
- // install is a source-only checkout by construction). Importing
162
- // dist/src/index.js from bin/cli.js then throws MODULE_NOT_FOUND on
163
- // every real command; only --version happens to survive it. Check
164
- // for the compiled entrypoint too so a doomed candidate is skipped
165
- // up front instead of wasting a spawn-and-fail on every render.
166
- return fs.existsSync(path.join(path.dirname(p), '..', 'dist', 'src', 'index.js'));
167
- } catch { return false; }
168
- });
169
- }
170
-
171
- // Return { fresh, promoFresh, data }. 'fresh' is true only if within the TTL
172
- // — but data is returned regardless (stale-while-revalidate). This lets us
173
- // serve last known state (specifically the promo row) when the CLI is
174
- // slow/unavailable, so users don't see the funnel row flicker in and out on
175
- // cache expiry. 'promoFresh' is a SEPARATE, tighter check on the same clock
176
- // as PROMO_ROTATION_SLOT_MS — see that constant's comment for why the promo
177
- // row needs its own freshness bound distinct from the general 60s TTL.
178
- function readCache() {
179
- try {
180
- if (fs.existsSync(CACHE_FILE)) {
181
- const raw = JSON.parse(fs.readFileSync(CACHE_FILE, 'utf-8'));
182
- if (raw && raw._ts && raw.data) {
183
- const age = Date.now() - raw._ts;
184
- return { fresh: age < CACHE_TTL_MS, promoFresh: age < PROMO_ROTATION_SLOT_MS, data: raw.data };
185
- }
186
- }
187
- } catch { /* ignore */ }
188
- return { fresh: false, promoFresh: false, data: null };
189
- }
190
-
191
- function writeCache(data) {
192
- try { fs.writeFileSync(CACHE_FILE, JSON.stringify({ _ts: Date.now(), data }), 'utf-8'); } catch { /* ignore */ }
193
- // Also memoize any promo we saw so the row can survive future CLI hiccups.
194
- try {
195
- if (data && data.promo && typeof data.promo === 'object') {
196
- fs.mkdirSync(path.dirname(PROMO_MEMO_FILE), { recursive: true, mode: 0o700 });
197
- fs.writeFileSync(PROMO_MEMO_FILE, JSON.stringify({ _ts: Date.now(), promo: data.promo }), { encoding: 'utf-8', mode: 0o600 });
198
- }
199
- } catch { /* ignore */ }
200
- }
201
-
202
- // Last resort: read a memoized promo (up to 6h old). Used when no cache and
203
- // no CLI response is available — the row still renders, so users don't see
204
- // the disclosure blink out. Returns null when the memo is absent, expired,
205
- // or malformed. Never throws.
206
- function readPromoMemo() {
207
- try {
208
- if (!fs.existsSync(PROMO_MEMO_FILE)) return null;
209
- const raw = JSON.parse(fs.readFileSync(PROMO_MEMO_FILE, 'utf-8'));
210
- if (raw && raw._ts && (Date.now() - raw._ts) < PROMO_MEMO_TTL_MS && raw.promo) {
211
- return raw.promo;
212
- }
213
- } catch { /* ignore */ }
214
- return null;
215
- }
216
-
217
- /**
218
- * Single source of truth: delegate to the CLI hooks statusline --json command.
219
- * Falls back to a minimal static object on failure so the statusline still renders.
220
- *
221
- * Fix for ruflo#2195: the previous local readers returned 0 for AgentDB patterns
222
- * (missed the .swarm/memory.db → AgentDB path), computed dddProgress wrong,
223
- * and only counted ADRs in v3/implementation/adrs/ (missed v3/docs/adr/).
224
- */
225
- // Overlay the memoized promo onto any data object that's missing one. This is
226
- // the safety net that keeps the funnel row rendered when an OLDER cached CLI
227
- // version is picked up by npx — that older CLI succeeds but omits promo, so
228
- // the JSON round-trips clean but without our row. We patch it back here.
229
- function overlayMemoPromo(data) {
230
- if (data && !data.promo) {
231
- const memoPromo = readPromoMemo();
232
- if (memoPromo) data.promo = memoPromo;
233
- }
234
- return data;
235
- }
236
-
237
- function getStatuslineData() {
238
- const cache = readCache();
239
- // Both clocks must be satisfied to skip the CLI call entirely: the general
240
- // 60s TTL (#2337 — don't re-spawn the CLI on every rapid re-render) AND the
241
- // tighter promo-rotation clock (this fix — don't let a still-fresh 60s
242
- // cache silently freeze the promo/insight row across multiple 20s slots).
243
- if (cache.fresh && cache.promoFresh) return overlayMemoPromo(cache.data);
244
-
245
- // #2337: prefer an already-installed CLI bin via direct \`node\` invocation —
246
- // no npx, no registry round-trip, no @latest re-resolve per render. Try
247
- // every candidate that actually EXISTS (not just the first) before falling
248
- // back to \`npx --prefer-offline @claude-flow/cli\` (no @latest); an existing
249
- // but broken install (e.g. a stale marketplace checkout missing a bundled
250
- // workspace dep) must not block trying the next one.
251
- //
252
- // No \`2>/dev/null\` here (deliberately) — the execSync call below already
253
- // sets stdio: ['pipe','pipe','pipe'], which captures/discards stderr at the
254
- // Node level regardless of shell. The redirect was redundant on POSIX and
255
- // actively broke every candidate on Windows: cmd.exe (execSync's default
256
- // shell there) doesn't understand /dev/null, so the CLI delegation always
257
- // failed, silently degrading every render to buildLocalFallback() — 0%
258
- // intelligence and an empty promo row (the memo cache that keeps the row
259
- // populated across CLI hiccups is only ever written from a SUCCESSFUL
260
- // delegation, so it could never get seeded on Windows either).
261
- const cmds = resolveCliBinCandidates()
262
- .map((bin) => '"' + process.execPath + '" "' + bin + '" hooks statusline --json')
263
- .concat(['npx --prefer-offline @claude-flow/cli hooks statusline --json']);
264
- for (const cmd of cmds) {
265
- try {
266
- const raw = execSync(
267
- cmd,
268
- { encoding: 'utf-8', timeout: 8000, stdio: ['pipe', 'pipe', 'pipe'], cwd: CWD }
269
- ).trim();
270
- // The CLI may emit preamble lines before the JSON — find the first '{'.
271
- const jsonStart = raw.indexOf('{');
272
- if (jsonStart === -1) throw new Error('no JSON in CLI output');
273
- const data = JSON.parse(raw.slice(jsonStart));
274
- // Overlay every block the CLI JSON omits (adrs/agentdb/tests/hooks/integration)
275
- // with real local reads, so those segments reflect actual state instead of 0.
276
- applyLocalOverlays(data);
277
- overlayMemoPromo(data);
278
- writeCache(data);
279
- return data;
280
- } catch { /* this candidate unavailable, broken, or timed out — try the next */ }
281
- }
282
-
283
- // Stale-while-revalidate: if we have any cached data, keep serving it so the
284
- // funnel row doesn't flicker on CLI hiccups. Overlay fresh local reads for
285
- // the segments the CLI JSON doesn't populate; the promo row survives.
286
- if (cache.data) {
287
- applyLocalOverlays(cache.data);
288
- overlayMemoPromo(cache.data);
289
- return cache.data;
290
- }
291
-
292
- // Last resort: local probes + memo. Users still see the funnel row.
293
- return overlayMemoPromo(buildLocalFallback());
294
- }
295
-
296
- // Count ADRs from BOTH known directories (fix for ruflo#2195: old code missed
297
- // v3/docs/adr/ which holds ADR-088..ADR-137, i.e. 41 of the 128 total ADRs).
298
- function getLocalADRCount() {
299
- const adrDirs = [
300
- path.join(CWD, 'v3', 'implementation', 'adrs'),
301
- path.join(CWD, 'v3', 'docs', 'adr'),
302
- path.join(CWD, 'docs', 'adrs'),
303
- path.join(CWD, '.claude-flow', 'adrs'),
304
- ];
305
- let total = 0;
306
- for (const dir of adrDirs) {
307
- try {
308
- if (fs.existsSync(dir)) {
309
- const files = fs.readdirSync(dir).filter(function(f) {
310
- return f.endsWith('.md') && (f.startsWith('ADR-') || f.startsWith('adr-') || /^\\d{4}-/.test(f));
311
- });
312
- total += files.length;
313
- }
314
- } catch { /* ignore */ }
315
- }
316
- return { count: total, implemented: total, compliance: 0 };
317
- }
318
-
319
- // ─── Local overlays for segments the CLI JSON omits ──────────────
320
- // 'hooks statusline --json' only returns user/v3Progress/security/swarm/system.
321
- // agentdb/tests/hooks/integration are never populated, so without these overlays
322
- // they render as a permanent 0. Each reader is cheap and degrades to zeros.
323
-
324
- // Real AgentDB stats from the local memory DB. Vectors live in .swarm/memory.db
325
- // (sql.js + HNSW); ruvector.db is an opaque redb store counted only toward size.
326
- // One read-only sqlite3 query (mode=ro never takes a write lock the daemon owns).
327
- function getLocalAgentDB() {
328
- const result = { vectorCount: 0, dbSizeKB: 0, hasHnsw: false };
329
- try {
330
- let bytes = 0;
331
- for (const f of ['.swarm/memory.db', 'ruvector.db']) {
332
- try { bytes += fs.statSync(path.join(CWD, f)).size; } catch { /* missing */ }
333
- }
334
- result.dbSizeKB = Math.round(bytes / 1024);
335
-
336
- const memDb = path.join(CWD, '.swarm', 'memory.db');
337
- if (fs.existsSync(memDb)) {
338
- const Q = String.fromCharCode(34);
339
- // Two INDEPENDENT statements -- do NOT combine into one. Coupling the
340
- // vector count with the vector_indexes row count in a single statement
341
- // meant that on a DB missing the vector_indexes table (older/agentdb-
342
- // written DBs), the whole statement failed at PREPARE time (SQLite
343
- // compiles the full SQL before running), so the valid memory_entries
344
- // count was discarded too and the statusline showed Vectors 0 despite
345
- // thousands of real vectors. Split so a missing table can only zero the
346
- // HNSW flag, never the count. The init self-heal provisions the table so
347
- // the flag recovers on the next ruflo init / MCP start.
348
- const countSql = Q + 'SELECT COUNT(*) FROM memory_entries WHERE embedding IS NOT NULL;' + Q;
349
- const vc = safeExec("sqlite3 'file:" + memDb + "?mode=ro' " + countSql, 1500);
350
- if (vc) result.vectorCount = parseInt(vc, 10) || 0;
351
- // HNSW flag: separate statement. If vector_indexes is absent, sqlite3
352
- // exits non-zero and safeExec returns empty -- hasHnsw stays false (exact
353
- // original semantics: at least one index-config row present).
354
- const hnswSql = Q + 'SELECT COUNT(*) FROM vector_indexes;' + Q;
355
- const hn = safeExec("sqlite3 'file:" + memDb + "?mode=ro' " + hnswSql, 1500);
356
- if (hn) result.hasHnsw = (parseInt(hn, 10) || 0) > 0;
357
- }
358
- } catch { /* ignore */ }
359
- return result;
360
- }
361
-
362
- // Count test files via a bounded directory walk (no file reads).
363
- function getLocalTests() {
364
- let testFiles = 0;
365
- function countTests(dir, depth) {
366
- if ((depth || 0) > 4) return;
367
- try {
368
- if (!fs.existsSync(dir)) return;
369
- for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
370
- if (e.isDirectory() && !e.name.startsWith('.') && e.name !== 'node_modules') {
371
- countTests(path.join(dir, e.name), (depth || 0) + 1);
372
- } else if (e.isFile() && (e.name.includes('.test.') || e.name.includes('.spec.') || e.name.startsWith('test_') || e.name.startsWith('spec_'))) {
373
- testFiles++;
374
- }
375
- }
376
- } catch { /* ignore */ }
377
- }
378
- for (const d of ['tests', 'test', '__tests__', 'src', 'v3']) countTests(path.join(CWD, d));
379
- return { testFiles, testCases: testFiles * 4 };
380
- }
381
-
382
- // Count configured hooks from project .claude/settings.json. Claude Code hooks
383
- // have no enabled/disabled flag, so every configured hook counts as enabled.
384
- function getLocalHooks() {
385
- const result = { enabled: 0, total: 0 };
386
- try {
387
- const settings = readJSON(path.join(CWD, '.claude', 'settings.json'));
388
- const hooks = settings && settings.hooks;
389
- if (hooks && typeof hooks === 'object') {
390
- let n = 0;
391
- for (const ev of Object.keys(hooks)) {
392
- const groups = hooks[ev];
393
- if (Array.isArray(groups)) {
394
- for (const g of groups) {
395
- if (g && Array.isArray(g.hooks)) n += g.hooks.length;
396
- }
397
- }
398
- }
399
- result.total = n;
400
- result.enabled = n;
401
- }
402
- } catch { /* ignore */ }
403
- return result;
404
- }
405
-
406
- // Best-effort integration block: DB presence + locally-configured stdio MCP
407
- // servers (project .mcp.json + global ~/.claude.json). Remote connectors are
408
- // account-managed and not present in local config, so they are not counted.
409
- function getLocalIntegration() {
410
- const integration = { mcpServers: { enabled: 0, total: 0 }, hasDatabase: false };
411
- try {
412
- for (const f of ['.swarm/memory.db', 'ruvector.db']) {
413
- if (fs.existsSync(path.join(CWD, f))) { integration.hasDatabase = true; break; }
414
- }
415
- const names = new Set();
416
- const projMcp = readJSON(path.join(CWD, '.mcp.json'));
417
- if (projMcp && projMcp.mcpServers) for (const k of Object.keys(projMcp.mcpServers)) names.add(k);
418
- const claudeJson = readJSON(path.join(os.homedir(), '.claude.json'));
419
- if (claudeJson) {
420
- if (claudeJson.mcpServers) for (const k of Object.keys(claudeJson.mcpServers)) names.add(k);
421
- const proj = claudeJson.projects && claudeJson.projects[CWD];
422
- if (proj && proj.mcpServers && !Array.isArray(proj.mcpServers)) {
423
- for (const k of Object.keys(proj.mcpServers)) names.add(k);
424
- }
425
- }
426
- integration.mcpServers.total = names.size;
427
- integration.mcpServers.enabled = names.size;
428
- } catch { /* ignore */ }
429
- return integration;
430
- }
431
-
432
- // Overlay every locally-derived block onto the CLI data (mutates in place).
433
- function applyLocalOverlays(data) {
434
- data.adrs = getLocalADRCount();
435
- data.agentdb = getLocalAgentDB();
436
- data.tests = getLocalTests();
437
- data.hooks = getLocalHooks();
438
- data.integration = getLocalIntegration();
439
- return data;
440
- }
441
-
442
- // Minimal local fallback when the CLI is not installed or times out.
443
- // Returns a structure that matches the CLI JSON schema so the renderer works.
444
- function buildLocalFallback() {
445
- const memMB = Math.floor(process.memoryUsage().heapUsed / 1024 / 1024);
446
-
447
- return applyLocalOverlays({
448
- user: { name: 'user', gitBranch: '', modelName: 'Claude Code' },
449
- v3Progress: { domainsCompleted: 0, totalDomains: 5, dddProgress: 0, patternsLearned: 0, sessionsCompleted: 0 },
450
- security: { status: 'NONE', cvesFixed: 0, totalCves: 0 },
451
- swarm: { activeAgents: 0, maxAgents: CONFIG.maxAgents, coordinationActive: false },
452
- system: { memoryMB: memMB, contextPct: 0, intelligencePct: 0, subAgents: 0 },
453
- lastUpdated: new Date().toISOString(),
454
- });
455
- }
456
-
457
- // ANSI colors
458
- const c = {
459
- reset: '\\x1b[0m',
460
- bold: '\\x1b[1m',
461
- dim: '\\x1b[2m',
462
- red: '\\x1b[0;31m',
463
- green: '\\x1b[0;32m',
464
- yellow: '\\x1b[0;33m',
465
- blue: '\\x1b[0;34m',
466
- purple: '\\x1b[0;35m',
467
- cyan: '\\x1b[0;36m',
468
- brightRed: '\\x1b[1;31m',
469
- brightGreen: '\\x1b[1;32m',
470
- brightYellow: '\\x1b[1;33m',
471
- brightBlue: '\\x1b[1;34m',
472
- brightPurple: '\\x1b[1;35m',
473
- brightCyan: '\\x1b[1;36m',
474
- brightWhite: '\\x1b[1;37m',
475
- };
476
-
477
- // Safe execSync with strict timeout (returns empty string on failure)
478
- function safeExec(cmd, timeoutMs) {
479
- try {
480
- return execSync(cmd, {
481
- encoding: 'utf-8',
482
- timeout: timeoutMs || 2000,
483
- stdio: ['pipe', 'pipe', 'pipe'],
484
- }).trim();
485
- } catch {
486
- return '';
487
- }
488
- }
489
-
490
- // Safe JSON file reader (returns null on failure)
491
- function readJSON(filePath) {
492
- try {
493
- if (fs.existsSync(filePath)) {
494
- return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
495
- }
496
- } catch { /* ignore */ }
497
- return null;
498
- }
499
-
500
- // ─── Git info (pure-Node / single exec — needed for branch display) ──────────
501
-
502
- function getGitInfo() {
503
- const result = {
504
- name: 'user', gitBranch: '', modified: 0, untracked: 0,
505
- staged: 0, ahead: 0, behind: 0,
506
- };
507
-
508
- const script = [
509
- 'git config user.name 2>/dev/null || echo user',
510
- 'echo "---SEP---"',
511
- 'git branch --show-current 2>/dev/null',
512
- 'echo "---SEP---"',
513
- 'git status --porcelain 2>/dev/null',
514
- 'echo "---SEP---"',
515
- 'git rev-list --left-right --count HEAD...@{upstream} 2>/dev/null || echo "0 0"',
516
- ].join('; ');
517
-
518
- const raw = safeExec("sh -c '" + script + "'", 3000);
519
- if (!raw) return result;
520
-
521
- const parts = raw.split('---SEP---').map(function(s) { return s.trim(); });
522
- if (parts.length >= 4) {
523
- result.name = parts[0] || 'user';
524
- result.gitBranch = parts[1] || '';
525
-
526
- if (parts[2]) {
527
- for (const line of parts[2].split('\\n')) {
528
- if (!line || line.length < 2) continue;
529
- const x = line[0], y = line[1];
530
- if (x === '?' && y === '?') { result.untracked++; continue; }
531
- if (x !== ' ' && x !== '?') result.staged++;
532
- if (y !== ' ' && y !== '?') result.modified++;
533
- }
534
- }
535
-
536
- const ab = (parts[3] || '0 0').split(/\\s+/);
537
- result.ahead = parseInt(ab[0]) || 0;
538
- result.behind = parseInt(ab[1]) || 0;
539
- }
540
-
541
- return result;
542
- }
543
-
544
- // Detect model name from Claude config (pure file reads, no exec)
545
- function getModelName() {
546
- try {
547
- const claudeConfig = readJSON(path.join(os.homedir(), '.claude.json'));
548
- if (claudeConfig && claudeConfig.projects) {
549
- for (const [projectPath, projectConfig] of Object.entries(claudeConfig.projects)) {
550
- if (CWD === projectPath || CWD.startsWith(projectPath + '/')) {
551
- const usage = projectConfig.lastModelUsage;
552
- if (usage) {
553
- const ids = Object.keys(usage);
554
- if (ids.length > 0) {
555
- let modelId = ids[ids.length - 1];
556
- let latest = 0;
557
- for (const id of ids) {
558
- const ts = usage[id] && usage[id].lastUsedAt ? new Date(usage[id].lastUsedAt).getTime() : 0;
559
- if (ts > latest) { latest = ts; modelId = id; }
560
- }
561
- if (modelId.includes('opus')) return 'Opus 4.8';
562
- if (modelId.includes('sonnet')) return 'Sonnet 4.6';
563
- if (modelId.includes('haiku')) return 'Haiku 4.5';
564
- return modelId.split('-').slice(1, 3).join(' ');
565
- }
566
- }
567
- break;
568
- }
569
- }
570
- }
571
- } catch { /* ignore */ }
572
-
573
- // Fallback: settings.json model field
574
- const settings = getSettings();
575
- if (settings && settings.model) {
576
- const m = settings.model;
577
- if (m.includes('opus')) return 'Opus 4.8';
578
- if (m.includes('sonnet')) return 'Sonnet 4.6';
579
- if (m.includes('haiku')) return 'Haiku 4.5';
580
- }
581
- return 'Claude Code';
582
- }
583
-
584
- // ─── Stdin reader (Claude Code pipes session JSON) ──────────────
585
- // Claude Code sends session JSON via stdin. Read synchronously so the
586
- // script works both when invoked by Claude Code (stdin has JSON) and
587
- // when run manually from terminal (stdin is empty/tty).
588
- let _stdinData = null;
589
- function getStdinData() {
590
- if (_stdinData !== undefined && _stdinData !== null) return _stdinData;
591
- try {
592
- if (process.stdin.isTTY) { _stdinData = null; return null; }
593
- const chunks = [];
594
- const buf = Buffer.alloc(4096);
595
- let bytesRead;
596
- try {
597
- while ((bytesRead = fs.readSync(0, buf, 0, buf.length, null)) > 0) {
598
- chunks.push(buf.slice(0, bytesRead));
599
- }
600
- } catch { /* EOF or read error */ }
601
- const raw = Buffer.concat(chunks).toString('utf-8').trim();
602
- _stdinData = (raw && raw.startsWith('{')) ? JSON.parse(raw) : null;
603
- } catch {
604
- _stdinData = null;
605
- }
606
- return _stdinData;
607
- }
608
-
609
- function getModelFromStdin() {
610
- const data = getStdinData();
611
- return (data && data.model && data.model.display_name) ? data.model.display_name : null;
612
- }
613
-
614
- function getContextFromStdin() {
615
- const data = getStdinData();
616
- if (data && data.context_window) {
617
- return { usedPct: Math.floor(data.context_window.used_percentage || 0) };
618
- }
619
- return null;
620
- }
621
-
622
- function getCostFromStdin() {
623
- const data = getStdinData();
624
- if (data && data.cost) {
625
- const durationMs = data.cost.total_duration_ms || 0;
626
- const mins = Math.floor(durationMs / 60000);
627
- const secs = Math.floor((durationMs % 60000) / 1000);
628
- return {
629
- costUsd: data.cost.total_cost_usd || 0,
630
- duration: mins > 0 ? mins + 'm' + secs + 's' : secs + 's',
631
- };
632
- }
633
- return null;
634
- }
635
-
636
- // Compares dotted-numeric version strings (e.g. "3.27.1" vs "3.27.10").
637
- // Returns >0 if a>b, <0 if a<b, 0 if equal-as-far-as-parseable. Deliberately
638
- // simple (no prerelease/build-metadata handling) — this only orders local
639
- // package.json versions against each other, never anything untrusted from
640
- // a payload, so a full semver implementation would be dead weight here.
641
- function compareVersions(a, b) {
642
- const pa = String(a).split('.').map((n) => parseInt(n, 10));
643
- const pb = String(b).split('.').map((n) => parseInt(n, 10));
644
- for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
645
- const na = Number.isFinite(pa[i]) ? pa[i] : 0;
646
- const nb = Number.isFinite(pb[i]) ? pb[i] : 0;
647
- if (na !== nb) return na - nb;
648
- }
649
- return 0;
650
- }
651
-
652
- function getPkgVersion() {
653
- // Baked in at generation time from the real running CLI's own resolved
654
- // version (see generateStatuslineScript()'s doc comment) — correct even
655
- // when this renders via a pure npx invocation with no local install for
656
- // the candidate scan below to find.
657
- let ver = ${JSON.stringify(bakedVersion)};
658
- try {
659
- const home = os.homedir();
660
- const pkgPaths = [
661
- path.join(home, '.claude', 'plugins', 'marketplaces', 'ruflo', 'package.json'),
662
- path.join(CWD, 'node_modules', '@claude-flow', 'cli', 'package.json'),
663
- path.join(CWD, 'node_modules', 'ruflo', 'package.json'),
664
- path.join(CWD, 'v3', '@claude-flow', 'cli', 'package.json'),
665
- ];
666
- // #2221: global installs (npm i -g ruflo) live outside CWD/node_modules, so the
667
- // probes above all miss and the version falls back to the hard-coded default.
668
- // Derive the global node_modules dir from the running node binary (no npm spawn —
669
- // statusline renders often). Covers nvm/mise (bin/../lib/node_modules) and Windows
670
- // (bin/node_modules) layouts.
671
- try {
672
- const binDir = path.dirname(process.execPath);
673
- const globalModuleDirs = [path.join(binDir, '..', 'lib', 'node_modules'), path.join(binDir, 'node_modules')];
674
- // #2221 follow-up: a custom npm prefix (e.g. ~/.npm-global) is decoupled from
675
- // the node binary location, so the binDir-derived probes above all miss. Also
676
- // probe the npm prefix from the environment and the common ~/.npm-global default.
677
- for (const prefix of [process.env.npm_config_prefix, process.env.PREFIX, path.join(home, '.npm-global')]) {
678
- if (prefix) globalModuleDirs.push(path.join(prefix, 'lib', 'node_modules'));
679
- }
680
- for (const gm of globalModuleDirs) {
681
- pkgPaths.push(
682
- path.join(gm, 'ruflo', 'package.json'),
683
- path.join(gm, '@claude-flow', 'cli', 'package.json'),
684
- );
685
- }
686
- } catch { /* ignore */ }
687
- // Pick the HIGHEST version among every candidate that exists, not the
688
- // first one found. The marketplace plugin path is probed first (list
689
- // order above), but Claude Code's own plugin marketplace mechanism
690
- // syncs on its own git-pull cadence, independent of npm publishes — a
691
- // freshly-published npm version can sit alongside a stale marketplace
692
- // checkout for a while (observed live: marketplace one release behind
693
- // right after a publish). Taking the first EXISTING candidate meant the
694
- // header could show a stale version even when a newer install (e.g.
695
- // node_modules/@claude-flow/cli from a plain npm install) was sitting right there.
696
- let found = false;
697
- for (const p of pkgPaths) {
698
- if (!fs.existsSync(p)) continue;
699
- try {
700
- const pkg = JSON.parse(fs.readFileSync(p, 'utf-8'));
701
- if (pkg && typeof pkg.version === 'string' && pkg.version.length > 0) {
702
- if (!found || compareVersions(pkg.version, ver) > 0) ver = pkg.version;
703
- found = true;
704
- }
705
- } catch { /* ignore */ }
706
- }
707
- } catch { /* ignore */ }
708
- return ver;
709
- }
710
-
711
- // ─── Rendering ──────────────────────────────────────────────────
712
-
713
- function progressBar(current, total) {
714
- const width = 5;
715
- const filled = Math.round((current / total) * width);
716
- return '[' + '●'.repeat(filled) + '○'.repeat(width - filled) + ']';
717
- }
718
-
719
- function generateStatusline() {
720
- const d = getStatuslineData();
721
- const git = getGitInfo();
722
- const modelName = getModelFromStdin() || (d.user && d.user.modelName) || 'Claude Code';
723
- const ctxInfo = getContextFromStdin();
724
- const costInfo = getCostFromStdin();
725
- // Named RUFLO_VERSION (not pkgVersion) so the #1951 regression guard
726
- // (scripts/audit-fix-invariants.mjs) can pin its presence in the emitted
727
- // .cjs artifact — without it the header silently reverts to a hard-coded
728
- // "RuFlo V3.5" for anyone whose install doesn't match the first probe path.
729
- const RUFLO_VERSION = getPkgVersion();
730
-
731
- const progress = d.v3Progress || {};
732
- const security = d.security || {};
733
- const swarm = d.swarm || {};
734
- const system = d.system || {};
735
- const adrs = d.adrs || {};
736
- const hooks = d.hooks || {};
737
- const agentdb = d.agentdb || {};
738
- const tests = d.tests || {};
739
-
740
- const domainsCompleted = progress.domainsCompleted || 0;
741
- const totalDomains = progress.totalDomains || 5;
742
- const dddProgress = progress.dddProgress || 0;
743
- const patternsLearned = progress.patternsLearned || 0;
744
- const activeAgents = swarm.activeAgents || 0;
745
- const maxAgents = swarm.maxAgents || CONFIG.maxAgents;
746
- const coordinationActive = swarm.coordinationActive || false;
747
- const intelligencePct = system.intelligencePct || 0;
748
- const memoryMB = system.memoryMB || 0;
749
- const subAgents = system.subAgents || 0;
750
- const cvesFixed = security.cvesFixed || 0;
751
- const totalCves = security.totalCves || 0;
752
- const secStatus = security.status || 'NONE';
753
- const adrCount = adrs.count || 0;
754
- const adrImpl = adrs.implemented || 0;
755
- const hooksEnabled = hooks.enabled || 0;
756
- const hooksTotal = hooks.total || 0;
757
- const vectorCount = agentdb.vectorCount || 0;
758
- const hasHnsw = agentdb.hasHnsw || false;
759
- const dbSizeKB = agentdb.dbSizeKB || 0;
760
- const testFiles = tests.testFiles || 0;
761
- const testCases = tests.testCases || testFiles * 4;
762
-
763
- const lines = [];
764
-
765
- // 3-line design (fits Claude Code's visible statusline area — line 4+ gets
766
- // replaced by the system guidance / input prompt line):
767
- // Line 1 — Header (RuFlo version · git · model · timing · context · cost)
768
- // Line 2 — Compressed ops (Swarm · Hooks · 🧠 · 💾 · Health)
769
- // Line 3 — Promo / disclosure row (funnel surface, ADR-301)
770
-
771
- // ─── Line 1: header ────────────────────────────────────────────
772
- let header = c.bold + c.brightPurple + '▊ RuFlo V' + RUFLO_VERSION + ' ' + c.reset;
773
- header += (coordinationActive ? c.brightCyan : c.dim) + '● ' + c.brightCyan + git.name + c.reset;
774
- if (git.gitBranch) {
775
- header += ' ' + c.dim + '│' + c.reset + ' ' + c.brightBlue + '⏇ ' + git.gitBranch + c.reset;
776
- const changes = git.modified + git.staged + git.untracked;
777
- if (changes > 0) {
778
- let ind = '';
779
- if (git.staged > 0) ind += c.brightGreen + '+' + git.staged + c.reset;
780
- if (git.modified > 0) ind += c.brightYellow + '~' + git.modified + c.reset;
781
- if (git.untracked > 0) ind += c.dim + '?' + git.untracked + c.reset;
782
- header += ' ' + ind;
783
- }
784
- if (git.ahead > 0) header += ' ' + c.brightGreen + '↑' + git.ahead + c.reset;
785
- if (git.behind > 0) header += ' ' + c.brightRed + '↓' + git.behind + c.reset;
786
- }
787
- header += ' ' + c.dim + '│' + c.reset + ' ' + c.purple + modelName + c.reset;
788
- const duration = costInfo ? costInfo.duration : '';
789
- if (duration) header += ' ' + c.dim + '│' + c.reset + ' ' + c.cyan + '⏱ ' + duration + c.reset;
790
- if (ctxInfo && ctxInfo.usedPct > 0) {
791
- const ctxColor = ctxInfo.usedPct >= 90 ? c.brightRed : ctxInfo.usedPct >= 70 ? c.brightYellow : c.brightGreen;
792
- header += ' ' + c.dim + '│' + c.reset + ' ' + ctxColor + '● ' + ctxInfo.usedPct + '% ctx' + c.reset;
793
- }
794
- if (!CONFIG.hideCost && costInfo && costInfo.costUsd > 0) {
795
- header += ' ' + c.dim + '│' + c.reset + ' ' + c.brightYellow + CONFIG.costSymbol + costInfo.costUsd.toFixed(2) + c.reset;
796
- }
797
- lines.push(header);
798
-
799
- // ─── Line 2: compressed ops ────────────────────────────────────
800
- // Everything actionable in one dense row. Show only what changes what you
801
- // do next; diagnostic detail moves to \`ruflo status --verbose\`.
802
- const agentsColor = activeAgents > 0 ? c.brightGreen : c.dim;
803
- const hooksColor = hooksEnabled > 0 ? c.brightGreen : c.dim;
804
- const intellColor = intelligencePct >= 80 ? c.brightGreen : intelligencePct >= 40 ? c.brightYellow : c.dim;
805
- const swarmInd = coordinationActive ? c.brightGreen + '◉' + c.reset + ' ' : c.dim + '○' + c.reset + ' ';
806
- const cvesClean = totalCves === 0 || cvesFixed === totalCves;
807
- const healthAllGreen = (secStatus === 'CLEAN' || secStatus === 'NONE') && cvesClean;
808
- const opsParts = [];
809
- opsParts.push(c.cyan + 'Swarm ' + swarmInd + agentsColor + activeAgents + c.reset + '/' + c.brightWhite + maxAgents + c.reset);
810
- if (subAgents > 0) opsParts.push(c.brightPurple + '👥 ' + subAgents + c.reset);
811
- opsParts.push(c.cyan + 'Hooks ' + hooksColor + hooksEnabled + c.reset + '/' + c.brightWhite + hooksTotal + c.reset);
812
- opsParts.push(intellColor + '🧠 ' + intelligencePct + '%' + c.reset);
813
- opsParts.push(c.brightCyan + '💾 ' + memoryMB + 'MB' + c.reset);
814
- // Health: one glyph when green, terse copy when there's something to act on.
815
- if (healthAllGreen) {
816
- opsParts.push(c.brightGreen + '🛡 ✓' + c.reset);
817
- } else {
818
- if (secStatus === 'PENDING') opsParts.push(c.brightYellow + '🛡 scan pending' + c.reset);
819
- else if (secStatus === 'IN_PROGRESS') opsParts.push(c.brightYellow + '🛡 scanning…' + c.reset);
820
- else if (secStatus === 'STALE') opsParts.push(c.brightYellow + '🛡 scan stale' + c.reset);
821
- else if (secStatus !== 'NONE' && secStatus !== 'CLEAN') opsParts.push(c.brightRed + '🛡 ' + secStatus.toLowerCase() + c.reset);
822
- if (totalCves > 0 && cvesFixed < totalCves) {
823
- const unfixed = totalCves - cvesFixed;
824
- opsParts.push(c.brightRed + '⚠ ' + unfixed + ' CVE' + (unfixed === 1 ? '' : 's') + c.reset);
825
- }
826
- }
827
- lines.push(opsParts.join(' ' + c.dim + '·' + c.reset + ' '));
828
-
829
- // ─── Line 3: promo / disclosure / insight ───────────────────────
830
- // Colored by content kind so it reads as *what it is*, not as noise:
831
- // disclosure → brightCyan (announcement / capability link)
832
- // promotional → brightPurple (Cognitum sponsor spot)
833
- // educational → yellow (a tip)
834
- // insight → brightRed (environment/task-aware, local, actionable —
835
- // distinct from remote content on purpose)
836
- const promoRow = getPromoRow(d);
837
- if (promoRow) {
838
- const kind = (d && d.promo && d.promo.kind) || 'disclosure';
839
- const promoColor = kind === 'promotional' ? c.brightPurple
840
- : kind === 'educational' ? c.yellow
841
- : kind === 'insight' ? c.brightRed
842
- : c.brightCyan;
843
- lines.push(promoColor + promoRow + c.reset);
844
- }
845
-
846
- // Trailing blank line so Claude Code's input prompt gets breathing room
847
- // instead of butting directly against the last statusline row.
848
- return lines.join('\\n') + '\\n';
849
- }
850
-
851
- // ─── Funnel promo row (ADR-301) ─────────────────────────────────
852
- // Allowlist for OSC 8 hyperlink targets. Ships in code (not in payload) so
853
- // no message can smuggle a link to an unapproved host.
854
- //
855
- // The final destination hosts (cognitum.one / agentics.org) AND the
856
- // click-redirect host are both allowlisted here: promo.ts routes every
857
- // clickable message through the server-side click-redirect (ADR-311 §7)
858
- // so promo_open + geo are captured before the 302 to the real target —
859
- // so the OSC 8 link the renderer emits points at the redirect host, not
860
- // the final destination directly.
861
- const PROMO_LINK_HOSTS = new Set([
862
- 'cognitum.one', 'www.cognitum.one', 'docs.cognitum.one',
863
- // agentics.org — OSS foundation, distinct sponsor domain. Kept in sync
864
- // with messages.ts ALLOWED_URL_HOSTS.
865
- 'agentics.org', 'www.agentics.org',
866
- // Click-redirect host (funnel.ruv.io once its TLS cert is live; the raw
867
- // Cloud Run hostname is allowlisted too since event-transport.ts /
868
- // message-transport.ts / attribution.ts currently point at it as a TEMP
869
- // fallback while the domain mapping's cert provisions).
870
- 'funnel.ruv.io',
871
- 'cognitum-analytics-63rzcdswba-uc.a.run.app',
872
- ]);
873
-
874
- // Emit OSC 8 hyperlinks unless the environment is known-broken. tmux mangles
875
- // raw OSC 8 (see anthropics/claude-code#27047) — opt in via env if wrapped.
876
- function terminalSupportsHyperlinks() {
877
- if (process.env.CI || process.env.GITHUB_ACTIONS) return false;
878
- if (process.env.TERM === 'dumb') return false;
879
- if (/^(0|false|off|no)$/i.test(String(process.env.RUFLO_STATUSLINE_HYPERLINKS || ''))) return false;
880
- if (process.env.TMUX && !process.env.RUFLO_STATUSLINE_HYPERLINKS_TMUX) return false;
881
- return true;
882
- }
883
-
884
- // Wrap a label in an OSC 8 hyperlink escape sequence. Falls back to the raw
885
- // label whenever the URL is not an allowlisted https target, when the terminal
886
- // can't render hyperlinks, or when parsing fails — a broken link must never
887
- // leave a raw URL or stray escape in the statusline output.
888
- function safeTerminalLink(label, url) {
889
- if (!terminalSupportsHyperlinks()) return label;
890
- if (typeof url !== 'string' || url.length === 0) return label;
891
- let parsed;
892
- try { parsed = new URL(url); } catch { return label; }
893
- if (parsed.protocol !== 'https:') return label;
894
- if (!PROMO_LINK_HOSTS.has(parsed.hostname)) return label;
895
- const cleanLabel = String(label).replace(/[\\u0000-\\u001f\\u007f-\\u009f\\u202a-\\u202e\\u2066-\\u2069]/g, '');
896
- if (cleanLabel.length === 0) return label;
897
- const ESC = '\\u001b';
898
- return ESC + ']8;;' + parsed.href + ESC + '\\\\' + cleanLabel + ESC + ']8;;' + ESC + '\\\\';
899
- }
900
-
901
- function getPromoRow(d) {
902
- try {
903
- if (process.env.CI || process.env.GITHUB_ACTIONS) return null;
904
- if (/^(0|false|off|no)$/i.test(String(process.env.RUFLO_FUNNEL || ''))) return null;
905
- const promo = d && d.promo;
906
- if (!promo || typeof promo.text !== 'string') return null;
907
- // Strip control chars / ANSI / bidi overrides and hard-cap length —
908
- // promo copy is data and must never emit its own terminal sequences.
909
- const text = promo.text
910
- .replace(/[\\u0000-\\u001f\\u007f-\\u009f\\u202a-\\u202e\\u2066-\\u2069]/g, '')
911
- .slice(0, 100)
912
- .trim();
913
- if (text.length === 0) return null;
914
- // Split the label from the trailing "· manage: ruflo settings" instruction
915
- // so each part gets styling that matches what it actually IS:
916
- // 1. label — OSC 8 hyperlink + underline. A real clickable link.
917
- // 2. "manage:" — dim. Just a connector word, no action implied.
918
- // 3. "ruflo settings" — bold/bright, NOT underlined. This is a shell
919
- // command the user TYPES, not a link they CLICK — a terminal can
920
- // never safely execute a command from a click (that would let any
921
- // server-served message run arbitrary commands), so we deliberately
922
- // avoid the underline/OSC8 cues that imply "clickable". Bold+bright
923
- // instead signals "this is the important bit — copy/type it".
924
- // Educational tips have no manage tail and no URL — plain text through.
925
- const manageIdx = text.indexOf(' · manage: ');
926
- const label = manageIdx > 0 ? text.slice(0, manageIdx) : text;
927
- const manageWord = manageIdx > 0 ? ' · manage: ' : '';
928
- const command = manageIdx > 0 ? text.slice(manageIdx + manageWord.length) : '';
929
- const UL_ON = '\\u001b[4m';
930
- const UL_OFF = '\\u001b[24m';
931
- const DIM_ON = '\\u001b[2m';
932
- const DIM_OFF = '\\u001b[22m';
933
- const BOLD_ON = '\\u001b[1m';
934
- const BOLD_OFF = '\\u001b[22m';
935
- const linked = promo.url ? UL_ON + safeTerminalLink(label, promo.url) + UL_OFF : label;
936
- if (!command) return linked;
937
- return linked + DIM_ON + manageWord + DIM_OFF + BOLD_ON + command + BOLD_OFF;
938
- } catch (e) {
939
- return null; // the promo row must never break the statusline
940
- }
941
- }
942
-
943
- // JSON output — delegates to CLI for accuracy; caller can use --json flag
944
- function generateJSON() {
945
- const d = getStatuslineData();
946
- const git = getGitInfo();
947
- return Object.assign({}, d, {
948
- user: Object.assign({ name: git.name, gitBranch: git.gitBranch }, d.user || {}),
949
- git: { modified: git.modified, untracked: git.untracked, staged: git.staged, ahead: git.ahead, behind: git.behind },
950
- lastUpdated: new Date().toISOString(),
951
- });
952
- }
953
-
954
- // ─── Main ───────────────────────────────────────────────────────
955
- if (process.argv.includes('--json')) {
956
- console.log(JSON.stringify(generateJSON(), null, 2));
957
- } else if (process.argv.includes('--compact')) {
958
- console.log(JSON.stringify(generateJSON()));
959
- } else {
960
- console.log(generateStatusline());
961
- }
79
+ const bakedVersion = getInstalledCliVersionLocal();
80
+ return `#!/usr/bin/env node
81
+ /**
82
+ * RuFlo V3 Statusline — delegation build (#2195)
83
+ *
84
+ * Fix for ruvnet/ruflo#2195: the previous version re-implemented all data
85
+ * readers locally using fragile file probes that missed AgentDB patterns,
86
+ * the v3/docs/adr/ ADR directory, and the real vector count.
87
+ *
88
+ * This version delegates to 'npx @claude-flow/cli hooks statusline --json'
89
+ * as the single source of truth. That command queries AgentDB directly,
90
+ * counts ADRs in both directories, and reports the real intelligence pct.
91
+ *
92
+ * ADR counting falls back to local file reads so the display still works
93
+ * without network access (counts both v3/docs/adr/ and v3/implementation/adrs/).
94
+ *
95
+ * Cache: JSON result is cached in /tmp for 10s so rapid prompt triggers
96
+ * (every keystroke in some shells) don't hammer the CLI on every call.
97
+ *
98
+ * Usage: node statusline.cjs [--json] [--compact] [--dashboard]
99
+ */
100
+
101
+ /* eslint-disable @typescript-eslint/no-var-requires */
102
+ const fs = require('fs');
103
+ const path = require('path');
104
+ const { execSync } = require('child_process');
105
+ const os = require('os');
106
+
107
+ // Configuration
108
+ const CONFIG = {
109
+ maxAgents: ${maxAgents},
110
+ // Session-cost display. Claude Code's cost.total_cost_usd is a client-side
111
+ // estimate that "may differ from your actual bill" and reads as misleading on
112
+ // subscription plans, where token usage is not billed per dollar. These let
113
+ // each user pick what the segment means to them without changing the default.
114
+ // RUFLO_STATUSLINE_COST_SYMBOL override the leading '$' (e.g. ⚡, €, 🌱);
115
+ // set to an empty string for the number alone.
116
+ // RUFLO_STATUSLINE_HIDE_COST 1/true/yes/on removes the segment entirely.
117
+ costSymbol: process.env.RUFLO_STATUSLINE_COST_SYMBOL ?? '$',
118
+ hideCost: /^(1|true|yes|on)$/i.test(process.env.RUFLO_STATUSLINE_HIDE_COST || ''),
119
+ };
120
+
121
+ const CWD = process.cwd();
122
+
123
+ // ─── Delegation cache ───────────────────────────────────────────
124
+ // Cache the CLI JSON result for 60s so rapid prompt re-renders
125
+ // (Claude Code refreshes the statusline several times a second while
126
+ // streaming) don't re-invoke the CLI each time. #2337: bumped 10s→60s
127
+ // because 10s was far too short for how often Claude Code re-renders.
128
+ const CACHE_FILE = path.join(os.tmpdir(), 'ruflo-statusline-cache-' + require('crypto').createHash('md5').update(CWD).digest('hex').slice(0, 8) + '.json');
129
+ const CACHE_TTL_MS = 60000;
130
+
131
+ // The promo/insight row is designed to rotate on a 20s cadence (funnel/
132
+ // rotation.ts's ROTATION_SLOT_MS / funnel/promo.ts's insight-slot check —
133
+ // duplicated here as a bare number since this generated script has no
134
+ // runtime import of the funnel module; keep in sync if that constant ever
135
+ // changes). The rotation slot is only ever (re)computed SERVER-SIDE inside
136
+ // the CLI subprocess this file shells out to — so a general 60s data cache
137
+ // (correct and necessary for #2337) silently made that 20s design
138
+ // unreachable: cache.fresh stayed true across 2-3 whole rotation slots,
139
+ // so the row visibly "didn't rotate" (user report). Fix: track promo
140
+ // freshness on its OWN, tighter clock — when it lags behind the current
141
+ // slot, fall through to a real CLI call even though the REST of the
142
+ // cached data (security/swarm/system) is still within CACHE_TTL_MS. This
143
+ // does not touch or regress #2337's fix; it only adds a narrower check.
144
+ const PROMO_ROTATION_SLOT_MS = 20000;
145
+
146
+ // Persistent last-known-good promo record. Lives outside the /tmp cache so it
147
+ // survives a full cache wipe / cache write race / CLI failure combo. Written
148
+ // every time we successfully render a promo; read as a last resort so the row
149
+ // never blinks out mid-session (was: 'promo shows then hides' bug report).
150
+ const PROMO_MEMO_FILE = path.join(os.homedir(), '.ruflo', 'statusline-promo.json');
151
+ const PROMO_MEMO_TTL_MS = 6 * 60 * 60 * 1000; // 6h — long enough to bridge any hiccup, short enough that a real disable takes effect fast.
152
+
153
+ // #2337: resolve an already-installed @claude-flow/cli (or ruflo) bin so we
154
+ // can invoke it directly via \`node\`. The previous version called
155
+ // \`npx --yes @claude-flow/cli@latest\` on every uncached render, which forces
156
+ // a registry resolution + cold-start of the entire CLI per render. With
157
+ // multiple concurrent Claude Code sessions this storms the host (reporter
158
+ // saw load average 40-65 on a 12-core box).
159
+ //
160
+ // Returns EVERY existing bin/cli.js candidate, in preference order (project,
161
+ // monorepo, plugin marketplace, global node_modules including custom-prefix
162
+ // layouts like ~/.npm-global) — mirrors getPkgVersion()'s own path probing.
163
+ //
164
+ // Returns a list, not a single winner: \`fs.existsSync\` only proves a file is
165
+ // present, not that it actually runs. A marketplace/npx-cached install can
166
+ // exist on disk but be broken (observed in practice: a stale marketplace
167
+ // checkout whose dist/ imports a workspace package, '@claude-flow/cli-core',
168
+ // that isn't bundled there — every invocation throws ERR_MODULE_NOT_FOUND).
169
+ // Picking the first EXISTING path and never falling through meant a single
170
+ // broken install silently killed the promo row for the entire session (the
171
+ // CLI call always failed, so the memo could never refresh and eventually
172
+ // expired). getStatuslineData() now walks this whole list and tries the next
173
+ // candidate on failure, so one broken install can't permanently wedge it.
174
+ function resolveCliBinCandidates() {
175
+ const candidates = [];
176
+ try {
177
+ const home = os.homedir();
178
+ candidates.push(
179
+ path.join(home, '.claude', 'plugins', 'marketplaces', 'ruflo', 'bin', 'cli.js'),
180
+ path.join(CWD, 'node_modules', '@claude-flow', 'cli', 'bin', 'cli.js'),
181
+ path.join(CWD, 'node_modules', 'ruflo', 'bin', 'cli.js'),
182
+ path.join(CWD, 'v3', '@claude-flow', 'cli', 'bin', 'cli.js'),
183
+ );
184
+ try {
185
+ const binDir = path.dirname(process.execPath);
186
+ const globalModuleDirs = [path.join(binDir, '..', 'lib', 'node_modules'), path.join(binDir, 'node_modules')];
187
+ for (const prefix of [process.env.npm_config_prefix, process.env.PREFIX, path.join(home, '.npm-global')]) {
188
+ if (prefix) globalModuleDirs.push(path.join(prefix, 'lib', 'node_modules'));
189
+ }
190
+ for (const gm of globalModuleDirs) {
191
+ candidates.push(
192
+ path.join(gm, 'ruflo', 'bin', 'cli.js'),
193
+ path.join(gm, '@claude-flow', 'cli', 'bin', 'cli.js'),
194
+ );
195
+ }
196
+ } catch { /* ignore */ }
197
+ } catch { /* ignore */ }
198
+ return candidates.filter((p) => {
199
+ try {
200
+ if (!fs.existsSync(p)) return false;
201
+ // A candidate's bin/cli.js can exist on disk while its compiled
202
+ // dist/ never got built (Claude Code's own plugin marketplace just
203
+ // git-clones the repo — no install/build step — so every marketplace
204
+ // install is a source-only checkout by construction). Importing
205
+ // dist/src/index.js from bin/cli.js then throws MODULE_NOT_FOUND on
206
+ // every real command; only --version happens to survive it. Check
207
+ // for the compiled entrypoint too so a doomed candidate is skipped
208
+ // up front instead of wasting a spawn-and-fail on every render.
209
+ return fs.existsSync(path.join(path.dirname(p), '..', 'dist', 'src', 'index.js'));
210
+ } catch { return false; }
211
+ });
212
+ }
213
+
214
+ // Return { fresh, promoFresh, data }. 'fresh' is true only if within the TTL
215
+ // — but data is returned regardless (stale-while-revalidate). This lets us
216
+ // serve last known state (specifically the promo row) when the CLI is
217
+ // slow/unavailable, so users don't see the funnel row flicker in and out on
218
+ // cache expiry. 'promoFresh' is a SEPARATE, tighter check on the same clock
219
+ // as PROMO_ROTATION_SLOT_MS — see that constant's comment for why the promo
220
+ // row needs its own freshness bound distinct from the general 60s TTL.
221
+ function readCache() {
222
+ try {
223
+ if (fs.existsSync(CACHE_FILE)) {
224
+ const raw = JSON.parse(fs.readFileSync(CACHE_FILE, 'utf-8'));
225
+ if (raw && raw._ts && raw.data) {
226
+ const age = Date.now() - raw._ts;
227
+ return { fresh: age < CACHE_TTL_MS, promoFresh: age < PROMO_ROTATION_SLOT_MS, data: raw.data };
228
+ }
229
+ }
230
+ } catch { /* ignore */ }
231
+ return { fresh: false, promoFresh: false, data: null };
232
+ }
233
+
234
+ function writeCache(data) {
235
+ try { fs.writeFileSync(CACHE_FILE, JSON.stringify({ _ts: Date.now(), data }), 'utf-8'); } catch { /* ignore */ }
236
+ // Also memoize any promo we saw so the row can survive future CLI hiccups.
237
+ try {
238
+ if (data && data.promo && typeof data.promo === 'object') {
239
+ fs.mkdirSync(path.dirname(PROMO_MEMO_FILE), { recursive: true, mode: 0o700 });
240
+ fs.writeFileSync(PROMO_MEMO_FILE, JSON.stringify({ _ts: Date.now(), promo: data.promo }), { encoding: 'utf-8', mode: 0o600 });
241
+ }
242
+ } catch { /* ignore */ }
243
+ }
244
+
245
+ // Last resort: read a memoized promo (up to 6h old). Used when no cache and
246
+ // no CLI response is available — the row still renders, so users don't see
247
+ // the disclosure blink out. Returns null when the memo is absent, expired,
248
+ // or malformed. Never throws.
249
+ function readPromoMemo() {
250
+ try {
251
+ if (!fs.existsSync(PROMO_MEMO_FILE)) return null;
252
+ const raw = JSON.parse(fs.readFileSync(PROMO_MEMO_FILE, 'utf-8'));
253
+ if (raw && raw._ts && (Date.now() - raw._ts) < PROMO_MEMO_TTL_MS && raw.promo) {
254
+ return raw.promo;
255
+ }
256
+ } catch { /* ignore */ }
257
+ return null;
258
+ }
259
+
260
+ /**
261
+ * Single source of truth: delegate to the CLI hooks statusline --json command.
262
+ * Falls back to a minimal static object on failure so the statusline still renders.
263
+ *
264
+ * Fix for ruflo#2195: the previous local readers returned 0 for AgentDB patterns
265
+ * (missed the .swarm/memory.db → AgentDB path), computed dddProgress wrong,
266
+ * and only counted ADRs in v3/implementation/adrs/ (missed v3/docs/adr/).
267
+ */
268
+ // Overlay the memoized promo onto any data object that's missing one. This is
269
+ // the safety net that keeps the funnel row rendered when an OLDER cached CLI
270
+ // version is picked up by npx — that older CLI succeeds but omits promo, so
271
+ // the JSON round-trips clean but without our row. We patch it back here.
272
+ function overlayMemoPromo(data) {
273
+ if (data && !data.promo) {
274
+ const memoPromo = readPromoMemo();
275
+ if (memoPromo) data.promo = memoPromo;
276
+ }
277
+ return data;
278
+ }
279
+
280
+ function getStatuslineData() {
281
+ const cache = readCache();
282
+ // Both clocks must be satisfied to skip the CLI call entirely: the general
283
+ // 60s TTL (#2337 — don't re-spawn the CLI on every rapid re-render) AND the
284
+ // tighter promo-rotation clock (this fix — don't let a still-fresh 60s
285
+ // cache silently freeze the promo/insight row across multiple 20s slots).
286
+ if (cache.fresh && cache.promoFresh) return overlayMemoPromo(cache.data);
287
+
288
+ // #2337: prefer an already-installed CLI bin via direct \`node\` invocation —
289
+ // no npx, no registry round-trip, no @latest re-resolve per render. Try
290
+ // every candidate that actually EXISTS (not just the first) before falling
291
+ // back to \`npx --prefer-offline @claude-flow/cli\` (no @latest); an existing
292
+ // but broken install (e.g. a stale marketplace checkout missing a bundled
293
+ // workspace dep) must not block trying the next one.
294
+ //
295
+ // No \`2>/dev/null\` here (deliberately) — the execSync call below already
296
+ // sets stdio: ['pipe','pipe','pipe'], which captures/discards stderr at the
297
+ // Node level regardless of shell. The redirect was redundant on POSIX and
298
+ // actively broke every candidate on Windows: cmd.exe (execSync's default
299
+ // shell there) doesn't understand /dev/null, so the CLI delegation always
300
+ // failed, silently degrading every render to buildLocalFallback() — 0%
301
+ // intelligence and an empty promo row (the memo cache that keeps the row
302
+ // populated across CLI hiccups is only ever written from a SUCCESSFUL
303
+ // delegation, so it could never get seeded on Windows either).
304
+ const cmds = resolveCliBinCandidates()
305
+ .map((bin) => '"' + process.execPath + '" "' + bin + '" hooks statusline --json')
306
+ .concat(['npx --prefer-offline @claude-flow/cli hooks statusline --json']);
307
+ for (const cmd of cmds) {
308
+ try {
309
+ const raw = execSync(
310
+ cmd,
311
+ { encoding: 'utf-8', timeout: 8000, stdio: ['pipe', 'pipe', 'pipe'], cwd: CWD }
312
+ ).trim();
313
+ // The CLI may emit preamble lines before the JSON — find the first '{'.
314
+ const jsonStart = raw.indexOf('{');
315
+ if (jsonStart === -1) throw new Error('no JSON in CLI output');
316
+ const data = JSON.parse(raw.slice(jsonStart));
317
+ // Overlay every block the CLI JSON omits (adrs/agentdb/tests/hooks/integration)
318
+ // with real local reads, so those segments reflect actual state instead of 0.
319
+ applyLocalOverlays(data);
320
+ overlayMemoPromo(data);
321
+ writeCache(data);
322
+ return data;
323
+ } catch { /* this candidate unavailable, broken, or timed out — try the next */ }
324
+ }
325
+
326
+ // Stale-while-revalidate: if we have any cached data, keep serving it so the
327
+ // funnel row doesn't flicker on CLI hiccups. Overlay fresh local reads for
328
+ // the segments the CLI JSON doesn't populate; the promo row survives.
329
+ if (cache.data) {
330
+ applyLocalOverlays(cache.data);
331
+ overlayMemoPromo(cache.data);
332
+ return cache.data;
333
+ }
334
+
335
+ // Last resort: local probes + memo. Users still see the funnel row.
336
+ return overlayMemoPromo(buildLocalFallback());
337
+ }
338
+
339
+ // Count ADRs from BOTH known directories (fix for ruflo#2195: old code missed
340
+ // v3/docs/adr/ which holds ADR-088..ADR-137, i.e. 41 of the 128 total ADRs).
341
+ function getLocalADRCount() {
342
+ const adrDirs = [
343
+ path.join(CWD, 'v3', 'implementation', 'adrs'),
344
+ path.join(CWD, 'v3', 'docs', 'adr'),
345
+ path.join(CWD, 'docs', 'adrs'),
346
+ path.join(CWD, '.claude-flow', 'adrs'),
347
+ ];
348
+ let total = 0;
349
+ for (const dir of adrDirs) {
350
+ try {
351
+ if (fs.existsSync(dir)) {
352
+ const files = fs.readdirSync(dir).filter(function(f) {
353
+ return f.endsWith('.md') && (f.startsWith('ADR-') || f.startsWith('adr-') || /^\\d{4}-/.test(f));
354
+ });
355
+ total += files.length;
356
+ }
357
+ } catch { /* ignore */ }
358
+ }
359
+ return { count: total, implemented: total, compliance: 0 };
360
+ }
361
+
362
+ // ─── Local overlays for segments the CLI JSON omits ──────────────
363
+ // 'hooks statusline --json' only returns user/v3Progress/security/swarm/system.
364
+ // agentdb/tests/hooks/integration are never populated, so without these overlays
365
+ // they render as a permanent 0. Each reader is cheap and degrades to zeros.
366
+
367
+ // Real AgentDB stats from the local memory DB. Vectors live in .swarm/memory.db
368
+ // (sql.js + HNSW); ruvector.db is an opaque redb store counted only toward size.
369
+ // One read-only sqlite3 query (mode=ro never takes a write lock the daemon owns).
370
+ function getLocalAgentDB() {
371
+ const result = { vectorCount: 0, dbSizeKB: 0, hasHnsw: false };
372
+ try {
373
+ let bytes = 0;
374
+ for (const f of ['.swarm/memory.db', 'ruvector.db']) {
375
+ try { bytes += fs.statSync(path.join(CWD, f)).size; } catch { /* missing */ }
376
+ }
377
+ result.dbSizeKB = Math.round(bytes / 1024);
378
+
379
+ const memDb = path.join(CWD, '.swarm', 'memory.db');
380
+ if (fs.existsSync(memDb)) {
381
+ const Q = String.fromCharCode(34);
382
+ // Two INDEPENDENT statements -- do NOT combine into one. Coupling the
383
+ // vector count with the vector_indexes row count in a single statement
384
+ // meant that on a DB missing the vector_indexes table (older/agentdb-
385
+ // written DBs), the whole statement failed at PREPARE time (SQLite
386
+ // compiles the full SQL before running), so the valid memory_entries
387
+ // count was discarded too and the statusline showed Vectors 0 despite
388
+ // thousands of real vectors. Split so a missing table can only zero the
389
+ // HNSW flag, never the count. The init self-heal provisions the table so
390
+ // the flag recovers on the next ruflo init / MCP start.
391
+ const countSql = Q + 'SELECT COUNT(*) FROM memory_entries WHERE embedding IS NOT NULL;' + Q;
392
+ const vc = safeExec("sqlite3 'file:" + memDb + "?mode=ro' " + countSql, 1500);
393
+ if (vc) result.vectorCount = parseInt(vc, 10) || 0;
394
+ // HNSW flag: separate statement. If vector_indexes is absent, sqlite3
395
+ // exits non-zero and safeExec returns empty -- hasHnsw stays false (exact
396
+ // original semantics: at least one index-config row present).
397
+ const hnswSql = Q + 'SELECT COUNT(*) FROM vector_indexes;' + Q;
398
+ const hn = safeExec("sqlite3 'file:" + memDb + "?mode=ro' " + hnswSql, 1500);
399
+ if (hn) result.hasHnsw = (parseInt(hn, 10) || 0) > 0;
400
+ }
401
+ } catch { /* ignore */ }
402
+ return result;
403
+ }
404
+
405
+ // Count test files via a bounded directory walk (no file reads).
406
+ function getLocalTests() {
407
+ let testFiles = 0;
408
+ function countTests(dir, depth) {
409
+ if ((depth || 0) > 4) return;
410
+ try {
411
+ if (!fs.existsSync(dir)) return;
412
+ for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
413
+ if (e.isDirectory() && !e.name.startsWith('.') && e.name !== 'node_modules') {
414
+ countTests(path.join(dir, e.name), (depth || 0) + 1);
415
+ } else if (e.isFile() && (e.name.includes('.test.') || e.name.includes('.spec.') || e.name.startsWith('test_') || e.name.startsWith('spec_'))) {
416
+ testFiles++;
417
+ }
418
+ }
419
+ } catch { /* ignore */ }
420
+ }
421
+ for (const d of ['tests', 'test', '__tests__', 'src', 'v3']) countTests(path.join(CWD, d));
422
+ return { testFiles, testCases: testFiles * 4 };
423
+ }
424
+
425
+ // Count configured hooks from project .claude/settings.json. Claude Code hooks
426
+ // have no enabled/disabled flag, so every configured hook counts as enabled.
427
+ function getLocalHooks() {
428
+ const result = { enabled: 0, total: 0 };
429
+ try {
430
+ const settings = readJSON(path.join(CWD, '.claude', 'settings.json'));
431
+ const hooks = settings && settings.hooks;
432
+ if (hooks && typeof hooks === 'object') {
433
+ let n = 0;
434
+ for (const ev of Object.keys(hooks)) {
435
+ const groups = hooks[ev];
436
+ if (Array.isArray(groups)) {
437
+ for (const g of groups) {
438
+ if (g && Array.isArray(g.hooks)) n += g.hooks.length;
439
+ }
440
+ }
441
+ }
442
+ result.total = n;
443
+ result.enabled = n;
444
+ }
445
+ } catch { /* ignore */ }
446
+ return result;
447
+ }
448
+
449
+ // Best-effort integration block: DB presence + locally-configured stdio MCP
450
+ // servers (project .mcp.json + global ~/.claude.json). Remote connectors are
451
+ // account-managed and not present in local config, so they are not counted.
452
+ function getLocalIntegration() {
453
+ const integration = { mcpServers: { enabled: 0, total: 0 }, hasDatabase: false };
454
+ try {
455
+ for (const f of ['.swarm/memory.db', 'ruvector.db']) {
456
+ if (fs.existsSync(path.join(CWD, f))) { integration.hasDatabase = true; break; }
457
+ }
458
+ const names = new Set();
459
+ const projMcp = readJSON(path.join(CWD, '.mcp.json'));
460
+ if (projMcp && projMcp.mcpServers) for (const k of Object.keys(projMcp.mcpServers)) names.add(k);
461
+ const claudeJson = readJSON(path.join(os.homedir(), '.claude.json'));
462
+ if (claudeJson) {
463
+ if (claudeJson.mcpServers) for (const k of Object.keys(claudeJson.mcpServers)) names.add(k);
464
+ const proj = claudeJson.projects && claudeJson.projects[CWD];
465
+ if (proj && proj.mcpServers && !Array.isArray(proj.mcpServers)) {
466
+ for (const k of Object.keys(proj.mcpServers)) names.add(k);
467
+ }
468
+ }
469
+ integration.mcpServers.total = names.size;
470
+ integration.mcpServers.enabled = names.size;
471
+ } catch { /* ignore */ }
472
+ return integration;
473
+ }
474
+
475
+ // Overlay every locally-derived block onto the CLI data (mutates in place).
476
+ function applyLocalOverlays(data) {
477
+ data.adrs = getLocalADRCount();
478
+ data.agentdb = getLocalAgentDB();
479
+ data.tests = getLocalTests();
480
+ data.hooks = getLocalHooks();
481
+ data.integration = getLocalIntegration();
482
+ return data;
483
+ }
484
+
485
+ // Minimal local fallback when the CLI is not installed or times out.
486
+ // Returns a structure that matches the CLI JSON schema so the renderer works.
487
+ function buildLocalFallback() {
488
+ const memMB = Math.floor(process.memoryUsage().heapUsed / 1024 / 1024);
489
+
490
+ return applyLocalOverlays({
491
+ user: { name: 'user', gitBranch: '', modelName: 'Claude Code' },
492
+ v3Progress: { domainsCompleted: 0, totalDomains: 5, dddProgress: 0, patternsLearned: 0, sessionsCompleted: 0 },
493
+ security: { status: 'NONE', cvesFixed: 0, totalCves: 0 },
494
+ swarm: { activeAgents: 0, maxAgents: CONFIG.maxAgents, coordinationActive: false },
495
+ system: { memoryMB: memMB, contextPct: 0, intelligencePct: 0, subAgents: 0 },
496
+ lastUpdated: new Date().toISOString(),
497
+ });
498
+ }
499
+
500
+ // ANSI colors
501
+ const c = {
502
+ reset: '\\x1b[0m',
503
+ bold: '\\x1b[1m',
504
+ dim: '\\x1b[2m',
505
+ red: '\\x1b[0;31m',
506
+ green: '\\x1b[0;32m',
507
+ yellow: '\\x1b[0;33m',
508
+ blue: '\\x1b[0;34m',
509
+ purple: '\\x1b[0;35m',
510
+ cyan: '\\x1b[0;36m',
511
+ brightRed: '\\x1b[1;31m',
512
+ brightGreen: '\\x1b[1;32m',
513
+ brightYellow: '\\x1b[1;33m',
514
+ brightBlue: '\\x1b[1;34m',
515
+ brightPurple: '\\x1b[1;35m',
516
+ brightCyan: '\\x1b[1;36m',
517
+ brightWhite: '\\x1b[1;37m',
518
+ };
519
+
520
+ // Safe execSync with strict timeout (returns empty string on failure)
521
+ function safeExec(cmd, timeoutMs) {
522
+ try {
523
+ return execSync(cmd, {
524
+ encoding: 'utf-8',
525
+ timeout: timeoutMs || 2000,
526
+ stdio: ['pipe', 'pipe', 'pipe'],
527
+ }).trim();
528
+ } catch {
529
+ return '';
530
+ }
531
+ }
532
+
533
+ // Safe JSON file reader (returns null on failure)
534
+ function readJSON(filePath) {
535
+ try {
536
+ if (fs.existsSync(filePath)) {
537
+ return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
538
+ }
539
+ } catch { /* ignore */ }
540
+ return null;
541
+ }
542
+
543
+ // ─── Git info (pure-Node / single exec — needed for branch display) ──────────
544
+
545
+ function getGitInfo() {
546
+ const result = {
547
+ name: 'user', gitBranch: '', modified: 0, untracked: 0,
548
+ staged: 0, ahead: 0, behind: 0,
549
+ };
550
+
551
+ const script = [
552
+ 'git config user.name 2>/dev/null || echo user',
553
+ 'echo "---SEP---"',
554
+ 'git branch --show-current 2>/dev/null',
555
+ 'echo "---SEP---"',
556
+ 'git status --porcelain 2>/dev/null',
557
+ 'echo "---SEP---"',
558
+ 'git rev-list --left-right --count HEAD...@{upstream} 2>/dev/null || echo "0 0"',
559
+ ].join('; ');
560
+
561
+ const raw = safeExec("sh -c '" + script + "'", 3000);
562
+ if (!raw) return result;
563
+
564
+ const parts = raw.split('---SEP---').map(function(s) { return s.trim(); });
565
+ if (parts.length >= 4) {
566
+ result.name = parts[0] || 'user';
567
+ result.gitBranch = parts[1] || '';
568
+
569
+ if (parts[2]) {
570
+ for (const line of parts[2].split('\\n')) {
571
+ if (!line || line.length < 2) continue;
572
+ const x = line[0], y = line[1];
573
+ if (x === '?' && y === '?') { result.untracked++; continue; }
574
+ if (x !== ' ' && x !== '?') result.staged++;
575
+ if (y !== ' ' && y !== '?') result.modified++;
576
+ }
577
+ }
578
+
579
+ const ab = (parts[3] || '0 0').split(/\\s+/);
580
+ result.ahead = parseInt(ab[0]) || 0;
581
+ result.behind = parseInt(ab[1]) || 0;
582
+ }
583
+
584
+ return result;
585
+ }
586
+
587
+ // Detect model name from Claude config (pure file reads, no exec)
588
+ function getModelName() {
589
+ try {
590
+ const claudeConfig = readJSON(path.join(os.homedir(), '.claude.json'));
591
+ if (claudeConfig && claudeConfig.projects) {
592
+ for (const [projectPath, projectConfig] of Object.entries(claudeConfig.projects)) {
593
+ if (CWD === projectPath || CWD.startsWith(projectPath + '/')) {
594
+ const usage = projectConfig.lastModelUsage;
595
+ if (usage) {
596
+ const ids = Object.keys(usage);
597
+ if (ids.length > 0) {
598
+ let modelId = ids[ids.length - 1];
599
+ let latest = 0;
600
+ for (const id of ids) {
601
+ const ts = usage[id] && usage[id].lastUsedAt ? new Date(usage[id].lastUsedAt).getTime() : 0;
602
+ if (ts > latest) { latest = ts; modelId = id; }
603
+ }
604
+ if (modelId.includes('opus')) return 'Opus 4.8';
605
+ if (modelId.includes('sonnet')) return 'Sonnet 4.6';
606
+ if (modelId.includes('haiku')) return 'Haiku 4.5';
607
+ return modelId.split('-').slice(1, 3).join(' ');
608
+ }
609
+ }
610
+ break;
611
+ }
612
+ }
613
+ }
614
+ } catch { /* ignore */ }
615
+
616
+ // Fallback: settings.json model field
617
+ const settings = getSettings();
618
+ if (settings && settings.model) {
619
+ const m = settings.model;
620
+ if (m.includes('opus')) return 'Opus 4.8';
621
+ if (m.includes('sonnet')) return 'Sonnet 4.6';
622
+ if (m.includes('haiku')) return 'Haiku 4.5';
623
+ }
624
+ return 'Claude Code';
625
+ }
626
+
627
+ // ─── Stdin reader (Claude Code pipes session JSON) ──────────────
628
+ // Claude Code sends session JSON via stdin. Read synchronously so the
629
+ // script works both when invoked by Claude Code (stdin has JSON) and
630
+ // when run manually from terminal (stdin is empty/tty).
631
+ let _stdinData = null;
632
+ function getStdinData() {
633
+ if (_stdinData !== undefined && _stdinData !== null) return _stdinData;
634
+ try {
635
+ if (process.stdin.isTTY) { _stdinData = null; return null; }
636
+ const chunks = [];
637
+ const buf = Buffer.alloc(4096);
638
+ let bytesRead;
639
+ try {
640
+ while ((bytesRead = fs.readSync(0, buf, 0, buf.length, null)) > 0) {
641
+ chunks.push(buf.slice(0, bytesRead));
642
+ }
643
+ } catch { /* EOF or read error */ }
644
+ const raw = Buffer.concat(chunks).toString('utf-8').trim();
645
+ _stdinData = (raw && raw.startsWith('{')) ? JSON.parse(raw) : null;
646
+ } catch {
647
+ _stdinData = null;
648
+ }
649
+ return _stdinData;
650
+ }
651
+
652
+ function getModelFromStdin() {
653
+ const data = getStdinData();
654
+ return (data && data.model && data.model.display_name) ? data.model.display_name : null;
655
+ }
656
+
657
+ function getContextFromStdin() {
658
+ const data = getStdinData();
659
+ if (data && data.context_window) {
660
+ return { usedPct: Math.floor(data.context_window.used_percentage || 0) };
661
+ }
662
+ return null;
663
+ }
664
+
665
+ function getCostFromStdin() {
666
+ const data = getStdinData();
667
+ if (data && data.cost) {
668
+ const durationMs = data.cost.total_duration_ms || 0;
669
+ const mins = Math.floor(durationMs / 60000);
670
+ const secs = Math.floor((durationMs % 60000) / 1000);
671
+ return {
672
+ costUsd: data.cost.total_cost_usd || 0,
673
+ duration: mins > 0 ? mins + 'm' + secs + 's' : secs + 's',
674
+ };
675
+ }
676
+ return null;
677
+ }
678
+
679
+ // Compares dotted-numeric version strings (e.g. "3.27.1" vs "3.27.10").
680
+ // Returns >0 if a>b, <0 if a<b, 0 if equal-as-far-as-parseable. Deliberately
681
+ // simple (no prerelease/build-metadata handling) — this only orders local
682
+ // package.json versions against each other, never anything untrusted from
683
+ // a payload, so a full semver implementation would be dead weight here.
684
+ function compareVersions(a, b) {
685
+ const pa = String(a).split('.').map((n) => parseInt(n, 10));
686
+ const pb = String(b).split('.').map((n) => parseInt(n, 10));
687
+ for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
688
+ const na = Number.isFinite(pa[i]) ? pa[i] : 0;
689
+ const nb = Number.isFinite(pb[i]) ? pb[i] : 0;
690
+ if (na !== nb) return na - nb;
691
+ }
692
+ return 0;
693
+ }
694
+
695
+ function getPkgVersion() {
696
+ // Baked in at generation time from the real running CLI's own resolved
697
+ // version (see generateStatuslineScript()'s doc comment) — correct even
698
+ // when this renders via a pure npx invocation with no local install for
699
+ // the candidate scan below to find.
700
+ let ver = ${JSON.stringify(bakedVersion)};
701
+ try {
702
+ const home = os.homedir();
703
+ const pkgPaths = [
704
+ path.join(home, '.claude', 'plugins', 'marketplaces', 'ruflo', 'package.json'),
705
+ path.join(CWD, 'node_modules', '@claude-flow', 'cli', 'package.json'),
706
+ path.join(CWD, 'node_modules', 'ruflo', 'package.json'),
707
+ path.join(CWD, 'v3', '@claude-flow', 'cli', 'package.json'),
708
+ ];
709
+ // #2221: global installs (npm i -g ruflo) live outside CWD/node_modules, so the
710
+ // probes above all miss and the version falls back to the hard-coded default.
711
+ // Derive the global node_modules dir from the running node binary (no npm spawn —
712
+ // statusline renders often). Covers nvm/mise (bin/../lib/node_modules) and Windows
713
+ // (bin/node_modules) layouts.
714
+ try {
715
+ const binDir = path.dirname(process.execPath);
716
+ const globalModuleDirs = [path.join(binDir, '..', 'lib', 'node_modules'), path.join(binDir, 'node_modules')];
717
+ // #2221 follow-up: a custom npm prefix (e.g. ~/.npm-global) is decoupled from
718
+ // the node binary location, so the binDir-derived probes above all miss. Also
719
+ // probe the npm prefix from the environment and the common ~/.npm-global default.
720
+ for (const prefix of [process.env.npm_config_prefix, process.env.PREFIX, path.join(home, '.npm-global')]) {
721
+ if (prefix) globalModuleDirs.push(path.join(prefix, 'lib', 'node_modules'));
722
+ }
723
+ for (const gm of globalModuleDirs) {
724
+ pkgPaths.push(
725
+ path.join(gm, 'ruflo', 'package.json'),
726
+ path.join(gm, '@claude-flow', 'cli', 'package.json'),
727
+ );
728
+ }
729
+ } catch { /* ignore */ }
730
+ // Pick the HIGHEST version among every candidate that exists, not the
731
+ // first one found. The marketplace plugin path is probed first (list
732
+ // order above), but Claude Code's own plugin marketplace mechanism
733
+ // syncs on its own git-pull cadence, independent of npm publishes — a
734
+ // freshly-published npm version can sit alongside a stale marketplace
735
+ // checkout for a while (observed live: marketplace one release behind
736
+ // right after a publish). Taking the first EXISTING candidate meant the
737
+ // header could show a stale version even when a newer install (e.g.
738
+ // node_modules/@claude-flow/cli from a plain npm install) was sitting right there.
739
+ let found = false;
740
+ for (const p of pkgPaths) {
741
+ if (!fs.existsSync(p)) continue;
742
+ try {
743
+ const pkg = JSON.parse(fs.readFileSync(p, 'utf-8'));
744
+ if (pkg && typeof pkg.version === 'string' && pkg.version.length > 0) {
745
+ if (!found || compareVersions(pkg.version, ver) > 0) ver = pkg.version;
746
+ found = true;
747
+ }
748
+ } catch { /* ignore */ }
749
+ }
750
+ } catch { /* ignore */ }
751
+ return ver;
752
+ }
753
+
754
+ // ─── Rendering ──────────────────────────────────────────────────
755
+
756
+ function progressBar(current, total) {
757
+ const width = 5;
758
+ const filled = Math.round((current / total) * width);
759
+ return '[' + '●'.repeat(filled) + '○'.repeat(width - filled) + ']';
760
+ }
761
+
762
+ function generateStatusline() {
763
+ const d = getStatuslineData();
764
+ const git = getGitInfo();
765
+ const modelName = getModelFromStdin() || (d.user && d.user.modelName) || 'Claude Code';
766
+ const ctxInfo = getContextFromStdin();
767
+ const costInfo = getCostFromStdin();
768
+ // Named RUFLO_VERSION (not pkgVersion) so the #1951 regression guard
769
+ // (scripts/audit-fix-invariants.mjs) can pin its presence in the emitted
770
+ // .cjs artifact — without it the header silently reverts to a hard-coded
771
+ // "RuFlo V3.5" for anyone whose install doesn't match the first probe path.
772
+ const RUFLO_VERSION = getPkgVersion();
773
+
774
+ const progress = d.v3Progress || {};
775
+ const security = d.security || {};
776
+ const swarm = d.swarm || {};
777
+ const system = d.system || {};
778
+ const adrs = d.adrs || {};
779
+ const hooks = d.hooks || {};
780
+ const agentdb = d.agentdb || {};
781
+ const tests = d.tests || {};
782
+
783
+ const domainsCompleted = progress.domainsCompleted || 0;
784
+ const totalDomains = progress.totalDomains || 5;
785
+ const dddProgress = progress.dddProgress || 0;
786
+ const patternsLearned = progress.patternsLearned || 0;
787
+ const activeAgents = swarm.activeAgents || 0;
788
+ const maxAgents = swarm.maxAgents || CONFIG.maxAgents;
789
+ const coordinationActive = swarm.coordinationActive || false;
790
+ const intelligencePct = system.intelligencePct || 0;
791
+ const memoryMB = system.memoryMB || 0;
792
+ const subAgents = system.subAgents || 0;
793
+ const cvesFixed = security.cvesFixed || 0;
794
+ const totalCves = security.totalCves || 0;
795
+ const secStatus = security.status || 'NONE';
796
+ const adrCount = adrs.count || 0;
797
+ const adrImpl = adrs.implemented || 0;
798
+ const hooksEnabled = hooks.enabled || 0;
799
+ const hooksTotal = hooks.total || 0;
800
+ const vectorCount = agentdb.vectorCount || 0;
801
+ const hasHnsw = agentdb.hasHnsw || false;
802
+ const dbSizeKB = agentdb.dbSizeKB || 0;
803
+ const testFiles = tests.testFiles || 0;
804
+ const testCases = tests.testCases || testFiles * 4;
805
+
806
+ const lines = [];
807
+
808
+ // 3-line design (fits Claude Code's visible statusline area — line 4+ gets
809
+ // replaced by the system guidance / input prompt line):
810
+ // Line 1 — Header (RuFlo version · git · model · timing · context · cost)
811
+ // Line 2 — Compressed ops (Swarm · Hooks · 🧠 · 💾 · Health)
812
+ // Line 3 — Promo / disclosure row (funnel surface, ADR-301)
813
+
814
+ // ─── Line 1: header ────────────────────────────────────────────
815
+ let header = c.bold + c.brightPurple + '▊ RuFlo V' + RUFLO_VERSION + ' ' + c.reset;
816
+ header += (coordinationActive ? c.brightCyan : c.dim) + '● ' + c.brightCyan + git.name + c.reset;
817
+ if (git.gitBranch) {
818
+ header += ' ' + c.dim + '│' + c.reset + ' ' + c.brightBlue + '⏇ ' + git.gitBranch + c.reset;
819
+ const changes = git.modified + git.staged + git.untracked;
820
+ if (changes > 0) {
821
+ let ind = '';
822
+ if (git.staged > 0) ind += c.brightGreen + '+' + git.staged + c.reset;
823
+ if (git.modified > 0) ind += c.brightYellow + '~' + git.modified + c.reset;
824
+ if (git.untracked > 0) ind += c.dim + '?' + git.untracked + c.reset;
825
+ header += ' ' + ind;
826
+ }
827
+ if (git.ahead > 0) header += ' ' + c.brightGreen + '↑' + git.ahead + c.reset;
828
+ if (git.behind > 0) header += ' ' + c.brightRed + '↓' + git.behind + c.reset;
829
+ }
830
+ header += ' ' + c.dim + '│' + c.reset + ' ' + c.purple + modelName + c.reset;
831
+ const duration = costInfo ? costInfo.duration : '';
832
+ if (duration) header += ' ' + c.dim + '│' + c.reset + ' ' + c.cyan + '⏱ ' + duration + c.reset;
833
+ if (ctxInfo && ctxInfo.usedPct > 0) {
834
+ const ctxColor = ctxInfo.usedPct >= 90 ? c.brightRed : ctxInfo.usedPct >= 70 ? c.brightYellow : c.brightGreen;
835
+ header += ' ' + c.dim + '│' + c.reset + ' ' + ctxColor + '● ' + ctxInfo.usedPct + '% ctx' + c.reset;
836
+ }
837
+ if (!CONFIG.hideCost && costInfo && costInfo.costUsd > 0) {
838
+ header += ' ' + c.dim + '│' + c.reset + ' ' + c.brightYellow + CONFIG.costSymbol + costInfo.costUsd.toFixed(2) + c.reset;
839
+ }
840
+ lines.push(header);
841
+
842
+ // ─── Line 2: compressed ops ────────────────────────────────────
843
+ // Everything actionable in one dense row. Show only what changes what you
844
+ // do next; diagnostic detail moves to \`ruflo status --verbose\`.
845
+ const agentsColor = activeAgents > 0 ? c.brightGreen : c.dim;
846
+ const hooksColor = hooksEnabled > 0 ? c.brightGreen : c.dim;
847
+ const intellColor = intelligencePct >= 80 ? c.brightGreen : intelligencePct >= 40 ? c.brightYellow : c.dim;
848
+ const swarmInd = coordinationActive ? c.brightGreen + '◉' + c.reset + ' ' : c.dim + '○' + c.reset + ' ';
849
+ const cvesClean = totalCves === 0 || cvesFixed === totalCves;
850
+ const healthAllGreen = (secStatus === 'CLEAN' || secStatus === 'NONE') && cvesClean;
851
+ const opsParts = [];
852
+ opsParts.push(c.cyan + 'Swarm ' + swarmInd + agentsColor + activeAgents + c.reset + '/' + c.brightWhite + maxAgents + c.reset);
853
+ if (subAgents > 0) opsParts.push(c.brightPurple + '👥 ' + subAgents + c.reset);
854
+ opsParts.push(c.cyan + 'Hooks ' + hooksColor + hooksEnabled + c.reset + '/' + c.brightWhite + hooksTotal + c.reset);
855
+ opsParts.push(intellColor + '🧠 ' + intelligencePct + '%' + c.reset);
856
+ opsParts.push(c.brightCyan + '💾 ' + memoryMB + 'MB' + c.reset);
857
+ // Health: one glyph when green, terse copy when there's something to act on.
858
+ if (healthAllGreen) {
859
+ opsParts.push(c.brightGreen + '🛡 ✓' + c.reset);
860
+ } else {
861
+ if (secStatus === 'PENDING') opsParts.push(c.brightYellow + '🛡 scan pending' + c.reset);
862
+ else if (secStatus === 'IN_PROGRESS') opsParts.push(c.brightYellow + '🛡 scanning…' + c.reset);
863
+ else if (secStatus === 'STALE') opsParts.push(c.brightYellow + '🛡 scan stale' + c.reset);
864
+ else if (secStatus !== 'NONE' && secStatus !== 'CLEAN') opsParts.push(c.brightRed + '🛡 ' + secStatus.toLowerCase() + c.reset);
865
+ if (totalCves > 0 && cvesFixed < totalCves) {
866
+ const unfixed = totalCves - cvesFixed;
867
+ opsParts.push(c.brightRed + '⚠ ' + unfixed + ' CVE' + (unfixed === 1 ? '' : 's') + c.reset);
868
+ }
869
+ }
870
+ lines.push(opsParts.join(' ' + c.dim + '·' + c.reset + ' '));
871
+
872
+ // ─── Line 3: promo / disclosure / insight ───────────────────────
873
+ // Colored by content kind so it reads as *what it is*, not as noise:
874
+ // disclosure → brightCyan (announcement / capability link)
875
+ // promotional → brightPurple (Cognitum sponsor spot)
876
+ // educational → yellow (a tip)
877
+ // insight → brightRed (environment/task-aware, local, actionable —
878
+ // distinct from remote content on purpose)
879
+ const promoRow = getPromoRow(d);
880
+ if (promoRow) {
881
+ const kind = (d && d.promo && d.promo.kind) || 'disclosure';
882
+ const promoColor = kind === 'promotional' ? c.brightPurple
883
+ : kind === 'educational' ? c.yellow
884
+ : kind === 'insight' ? c.brightRed
885
+ : c.brightCyan;
886
+ lines.push(promoColor + promoRow + c.reset);
887
+ }
888
+
889
+ // Trailing blank line so Claude Code's input prompt gets breathing room
890
+ // instead of butting directly against the last statusline row.
891
+ return lines.join('\\n') + '\\n';
892
+ }
893
+
894
+ // ─── Funnel promo row (ADR-301) ─────────────────────────────────
895
+ // Allowlist for OSC 8 hyperlink targets. Ships in code (not in payload) so
896
+ // no message can smuggle a link to an unapproved host.
897
+ //
898
+ // The final destination hosts (cognitum.one / agentics.org) AND the
899
+ // click-redirect host are both allowlisted here: promo.ts routes every
900
+ // clickable message through the server-side click-redirect (ADR-311 §7)
901
+ // so promo_open + geo are captured before the 302 to the real target —
902
+ // so the OSC 8 link the renderer emits points at the redirect host, not
903
+ // the final destination directly.
904
+ const PROMO_LINK_HOSTS = new Set([
905
+ 'cognitum.one', 'www.cognitum.one', 'docs.cognitum.one',
906
+ // agentics.org — OSS foundation, distinct sponsor domain. Kept in sync
907
+ // with messages.ts ALLOWED_URL_HOSTS.
908
+ 'agentics.org', 'www.agentics.org',
909
+ // Click-redirect host (funnel.ruv.io once its TLS cert is live; the raw
910
+ // Cloud Run hostname is allowlisted too since event-transport.ts /
911
+ // message-transport.ts / attribution.ts currently point at it as a TEMP
912
+ // fallback while the domain mapping's cert provisions).
913
+ 'funnel.ruv.io',
914
+ 'cognitum-analytics-63rzcdswba-uc.a.run.app',
915
+ ]);
916
+
917
+ // Emit OSC 8 hyperlinks unless the environment is known-broken. tmux mangles
918
+ // raw OSC 8 (see anthropics/claude-code#27047) — opt in via env if wrapped.
919
+ function terminalSupportsHyperlinks() {
920
+ if (process.env.CI || process.env.GITHUB_ACTIONS) return false;
921
+ if (process.env.TERM === 'dumb') return false;
922
+ if (/^(0|false|off|no)$/i.test(String(process.env.RUFLO_STATUSLINE_HYPERLINKS || ''))) return false;
923
+ if (process.env.TMUX && !process.env.RUFLO_STATUSLINE_HYPERLINKS_TMUX) return false;
924
+ return true;
925
+ }
926
+
927
+ // Wrap a label in an OSC 8 hyperlink escape sequence. Falls back to the raw
928
+ // label whenever the URL is not an allowlisted https target, when the terminal
929
+ // can't render hyperlinks, or when parsing fails — a broken link must never
930
+ // leave a raw URL or stray escape in the statusline output.
931
+ function safeTerminalLink(label, url) {
932
+ if (!terminalSupportsHyperlinks()) return label;
933
+ if (typeof url !== 'string' || url.length === 0) return label;
934
+ let parsed;
935
+ try { parsed = new URL(url); } catch { return label; }
936
+ if (parsed.protocol !== 'https:') return label;
937
+ if (!PROMO_LINK_HOSTS.has(parsed.hostname)) return label;
938
+ const cleanLabel = String(label).replace(/[\\u0000-\\u001f\\u007f-\\u009f\\u202a-\\u202e\\u2066-\\u2069]/g, '');
939
+ if (cleanLabel.length === 0) return label;
940
+ const ESC = '\\u001b';
941
+ return ESC + ']8;;' + parsed.href + ESC + '\\\\' + cleanLabel + ESC + ']8;;' + ESC + '\\\\';
942
+ }
943
+
944
+ function getPromoRow(d) {
945
+ try {
946
+ if (process.env.CI || process.env.GITHUB_ACTIONS) return null;
947
+ if (/^(0|false|off|no)$/i.test(String(process.env.RUFLO_FUNNEL || ''))) return null;
948
+ const promo = d && d.promo;
949
+ if (!promo || typeof promo.text !== 'string') return null;
950
+ // Strip control chars / ANSI / bidi overrides and hard-cap length —
951
+ // promo copy is data and must never emit its own terminal sequences.
952
+ const text = promo.text
953
+ .replace(/[\\u0000-\\u001f\\u007f-\\u009f\\u202a-\\u202e\\u2066-\\u2069]/g, '')
954
+ .slice(0, 100)
955
+ .trim();
956
+ if (text.length === 0) return null;
957
+ // Split the label from the trailing "· manage: ruflo settings" instruction
958
+ // so each part gets styling that matches what it actually IS:
959
+ // 1. label — OSC 8 hyperlink + underline. A real clickable link.
960
+ // 2. "manage:" — dim. Just a connector word, no action implied.
961
+ // 3. "ruflo settings" — bold/bright, NOT underlined. This is a shell
962
+ // command the user TYPES, not a link they CLICK — a terminal can
963
+ // never safely execute a command from a click (that would let any
964
+ // server-served message run arbitrary commands), so we deliberately
965
+ // avoid the underline/OSC8 cues that imply "clickable". Bold+bright
966
+ // instead signals "this is the important bit — copy/type it".
967
+ // Educational tips have no manage tail and no URL — plain text through.
968
+ const manageIdx = text.indexOf(' · manage: ');
969
+ const label = manageIdx > 0 ? text.slice(0, manageIdx) : text;
970
+ const manageWord = manageIdx > 0 ? ' · manage: ' : '';
971
+ const command = manageIdx > 0 ? text.slice(manageIdx + manageWord.length) : '';
972
+ const UL_ON = '\\u001b[4m';
973
+ const UL_OFF = '\\u001b[24m';
974
+ const DIM_ON = '\\u001b[2m';
975
+ const DIM_OFF = '\\u001b[22m';
976
+ const BOLD_ON = '\\u001b[1m';
977
+ const BOLD_OFF = '\\u001b[22m';
978
+ const linked = promo.url ? UL_ON + safeTerminalLink(label, promo.url) + UL_OFF : label;
979
+ if (!command) return linked;
980
+ return linked + DIM_ON + manageWord + DIM_OFF + BOLD_ON + command + BOLD_OFF;
981
+ } catch (e) {
982
+ return null; // the promo row must never break the statusline
983
+ }
984
+ }
985
+
986
+ // JSON output — delegates to CLI for accuracy; caller can use --json flag
987
+ function generateJSON() {
988
+ const d = getStatuslineData();
989
+ const git = getGitInfo();
990
+ return Object.assign({}, d, {
991
+ user: Object.assign({ name: git.name, gitBranch: git.gitBranch }, d.user || {}),
992
+ git: { modified: git.modified, untracked: git.untracked, staged: git.staged, ahead: git.ahead, behind: git.behind },
993
+ lastUpdated: new Date().toISOString(),
994
+ });
995
+ }
996
+
997
+ // ─── Main ───────────────────────────────────────────────────────
998
+ if (process.argv.includes('--json')) {
999
+ console.log(JSON.stringify(generateJSON(), null, 2));
1000
+ } else if (process.argv.includes('--compact')) {
1001
+ console.log(JSON.stringify(generateJSON()));
1002
+ } else {
1003
+ console.log(generateStatusline());
1004
+ }
962
1005
  `;
963
1006
  }
964
1007
  /**
@@ -968,30 +1011,30 @@ export function generateStatuslineHook(options) {
968
1011
  if (!options.statusline.enabled) {
969
1012
  return '#!/bin/bash\n# Statusline disabled\n';
970
1013
  }
971
- return `#!/bin/bash
972
- # RuFlo V3 Statusline Hook
973
- # Source this in your .bashrc/.zshrc for terminal statusline
974
-
975
- # Function to get statusline
976
- claude_flow_statusline() {
977
- local statusline_script="\${CLAUDE_FLOW_DIR:-.claude}/helpers/statusline.cjs"
978
- if [ -f "$statusline_script" ]; then
979
- node "$statusline_script" 2>/dev/null || echo ""
980
- fi
981
- }
982
-
983
- # Bash: Add to PS1
984
- # export PS1='$(claude_flow_statusline) \\n\\$ '
985
-
986
- # Zsh: Add to RPROMPT
987
- # export RPROMPT='$(claude_flow_statusline)'
988
-
989
- # Claude Code: Add to .claude/settings.json
990
- # "statusLine": {
991
- # "type": "command",
992
- # "command": "node .claude/helpers/statusline.cjs 2>/dev/null"
993
- # "when": "test -f .claude/helpers/statusline.cjs"
994
- # }
1014
+ return `#!/bin/bash
1015
+ # RuFlo V3 Statusline Hook
1016
+ # Source this in your .bashrc/.zshrc for terminal statusline
1017
+
1018
+ # Function to get statusline
1019
+ claude_flow_statusline() {
1020
+ local statusline_script="\${CLAUDE_FLOW_DIR:-.claude}/helpers/statusline.cjs"
1021
+ if [ -f "$statusline_script" ]; then
1022
+ node "$statusline_script" 2>/dev/null || echo ""
1023
+ fi
1024
+ }
1025
+
1026
+ # Bash: Add to PS1
1027
+ # export PS1='$(claude_flow_statusline) \\n\\$ '
1028
+
1029
+ # Zsh: Add to RPROMPT
1030
+ # export RPROMPT='$(claude_flow_statusline)'
1031
+
1032
+ # Claude Code: Add to .claude/settings.json
1033
+ # "statusLine": {
1034
+ # "type": "command",
1035
+ # "command": "node .claude/helpers/statusline.cjs 2>/dev/null"
1036
+ # "when": "test -f .claude/helpers/statusline.cjs"
1037
+ # }
995
1038
  `;
996
1039
  }
997
1040
  //# sourceMappingURL=statusline-generator.js.map