kynjal-cli 3.1.4 → 4.0.1

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 (344) hide show
  1. package/.claude/agents/core/coder.md +1 -1
  2. package/.claude/agents/core/planner.md +2 -2
  3. package/.claude/agents/core/researcher.md +1 -1
  4. package/.claude/agents/core/reviewer.md +1 -1
  5. package/.claude/agents/core/tester.md +1 -1
  6. package/.claude/agents/data/data-ml-model.md +4 -4
  7. package/.claude/agents/development/dev-backend-api.md +4 -4
  8. package/.claude/agents/documentation/docs-api-openapi.md +4 -4
  9. package/.claude/agents/github/code-review-swarm.md +2 -2
  10. package/.claude/agents/github/issue-tracker.md +2 -2
  11. package/.claude/agents/github/pr-manager.md +2 -2
  12. package/.claude/agents/github/release-manager.md +2 -2
  13. package/.claude/agents/github/workflow-automation.md +2 -2
  14. package/.claude/agents/sparc/architecture.md +3 -3
  15. package/.claude/agents/sparc/pseudocode.md +2 -2
  16. package/.claude/agents/sparc/refinement.md +3 -3
  17. package/.claude/agents/sparc/specification.md +2 -2
  18. package/.claude/agents/swarm/adaptive-coordinator.md +1 -1
  19. package/.claude/agents/swarm/hierarchical-coordinator.md +1 -1
  20. package/.claude/agents/swarm/mesh-coordinator.md +1 -1
  21. package/.claude/agents/templates/base-template-generator.md +25 -4
  22. package/.claude/agents/templates/sparc-coordinator.md +3 -3
  23. package/.claude/helpers/auto-commit.sh +1 -1
  24. package/.claude/helpers/auto-memory-hook.mjs +27 -9
  25. package/.claude/helpers/hook-handler.cjs +58 -18
  26. package/.claude/helpers/statusline.cjs +420 -613
  27. package/.claude/helpers/statusline.js +3 -3
  28. package/.claude/settings.json +9 -9
  29. package/.claude/skills/reasoningbank-intelligence/SKILL.md +2 -2
  30. package/.claude/skills/swarm-orchestration/SKILL.md +1 -1
  31. package/README.md +383 -170
  32. package/bin/cli.js +6 -6
  33. package/bin/mcp-server.js +1 -1
  34. package/bin/preinstall.cjs +2 -0
  35. package/dist/src/appliance/gguf-engine.d.ts +91 -0
  36. package/dist/src/appliance/gguf-engine.d.ts.map +1 -0
  37. package/dist/src/appliance/gguf-engine.js +425 -0
  38. package/dist/src/appliance/gguf-engine.js.map +1 -0
  39. package/dist/src/appliance/ruvllm-bridge.d.ts +102 -0
  40. package/dist/src/appliance/ruvllm-bridge.d.ts.map +1 -0
  41. package/dist/src/appliance/ruvllm-bridge.js +292 -0
  42. package/dist/src/appliance/ruvllm-bridge.js.map +1 -0
  43. package/dist/src/appliance/rvfa-builder.d.ts +44 -0
  44. package/dist/src/appliance/rvfa-builder.d.ts.map +1 -0
  45. package/dist/src/appliance/rvfa-builder.js +329 -0
  46. package/dist/src/appliance/rvfa-builder.js.map +1 -0
  47. package/dist/src/appliance/rvfa-distribution.d.ts +97 -0
  48. package/dist/src/appliance/rvfa-distribution.d.ts.map +1 -0
  49. package/dist/src/appliance/rvfa-distribution.js +370 -0
  50. package/dist/src/appliance/rvfa-distribution.js.map +1 -0
  51. package/dist/src/appliance/rvfa-format.d.ts +111 -0
  52. package/dist/src/appliance/rvfa-format.d.ts.map +1 -0
  53. package/dist/src/appliance/rvfa-format.js +393 -0
  54. package/dist/src/appliance/rvfa-format.js.map +1 -0
  55. package/dist/src/appliance/rvfa-runner.d.ts +69 -0
  56. package/dist/src/appliance/rvfa-runner.d.ts.map +1 -0
  57. package/dist/src/appliance/rvfa-runner.js +237 -0
  58. package/dist/src/appliance/rvfa-runner.js.map +1 -0
  59. package/dist/src/appliance/rvfa-signing.d.ts +123 -0
  60. package/dist/src/appliance/rvfa-signing.d.ts.map +1 -0
  61. package/dist/src/appliance/rvfa-signing.js +347 -0
  62. package/dist/src/appliance/rvfa-signing.js.map +1 -0
  63. package/dist/src/commands/agent.d.ts.map +1 -1
  64. package/dist/src/commands/agent.js +121 -21
  65. package/dist/src/commands/agent.js.map +1 -1
  66. package/dist/src/commands/appliance-advanced.d.ts +9 -0
  67. package/dist/src/commands/appliance-advanced.d.ts.map +1 -0
  68. package/dist/src/commands/appliance-advanced.js +215 -0
  69. package/dist/src/commands/appliance-advanced.js.map +1 -0
  70. package/dist/src/commands/appliance.d.ts +8 -0
  71. package/dist/src/commands/appliance.d.ts.map +1 -0
  72. package/dist/src/commands/appliance.js +406 -0
  73. package/dist/src/commands/appliance.js.map +1 -0
  74. package/dist/src/commands/benchmark.js +2 -2
  75. package/dist/src/commands/benchmark.js.map +1 -1
  76. package/dist/src/commands/claims.js +1 -1
  77. package/dist/src/commands/claims.js.map +1 -1
  78. package/dist/src/commands/cleanup.d.ts +13 -0
  79. package/dist/src/commands/cleanup.d.ts.map +1 -0
  80. package/dist/src/commands/cleanup.js +218 -0
  81. package/dist/src/commands/cleanup.js.map +1 -0
  82. package/dist/src/commands/config.js +1 -1
  83. package/dist/src/commands/config.js.map +1 -1
  84. package/dist/src/commands/daemon.d.ts.map +1 -1
  85. package/dist/src/commands/daemon.js +81 -18
  86. package/dist/src/commands/daemon.js.map +1 -1
  87. package/dist/src/commands/deployment.js +1 -1
  88. package/dist/src/commands/deployment.js.map +1 -1
  89. package/dist/src/commands/doctor.d.ts.map +1 -1
  90. package/dist/src/commands/doctor.js +99 -51
  91. package/dist/src/commands/doctor.js.map +1 -1
  92. package/dist/src/commands/embeddings.js +1 -1
  93. package/dist/src/commands/embeddings.js.map +1 -1
  94. package/dist/src/commands/hive-mind.js +26 -26
  95. package/dist/src/commands/hive-mind.js.map +1 -1
  96. package/dist/src/commands/hooks.d.ts.map +1 -1
  97. package/dist/src/commands/hooks.js +800 -100
  98. package/dist/src/commands/hooks.js.map +1 -1
  99. package/dist/src/commands/index.d.ts +9 -2
  100. package/dist/src/commands/index.d.ts.map +1 -1
  101. package/dist/src/commands/index.js +20 -2
  102. package/dist/src/commands/index.js.map +1 -1
  103. package/dist/src/commands/init.d.ts +1 -1
  104. package/dist/src/commands/init.d.ts.map +1 -1
  105. package/dist/src/commands/init.js +22 -18
  106. package/dist/src/commands/init.js.map +1 -1
  107. package/dist/src/commands/mcp.d.ts.map +1 -1
  108. package/dist/src/commands/mcp.js +23 -5
  109. package/dist/src/commands/mcp.js.map +1 -1
  110. package/dist/src/commands/memory.d.ts.map +1 -1
  111. package/dist/src/commands/memory.js +24 -0
  112. package/dist/src/commands/memory.js.map +1 -1
  113. package/dist/src/commands/neural.d.ts.map +1 -1
  114. package/dist/src/commands/neural.js +13 -7
  115. package/dist/src/commands/neural.js.map +1 -1
  116. package/dist/src/commands/performance.js +1 -1
  117. package/dist/src/commands/performance.js.map +1 -1
  118. package/dist/src/commands/plugins.js +1 -1
  119. package/dist/src/commands/plugins.js.map +1 -1
  120. package/dist/src/commands/providers.js +1 -1
  121. package/dist/src/commands/providers.js.map +1 -1
  122. package/dist/src/commands/ruvector/import.js +2 -2
  123. package/dist/src/commands/ruvector/import.js.map +1 -1
  124. package/dist/src/commands/ruvector/index.js +1 -1
  125. package/dist/src/commands/ruvector/index.js.map +1 -1
  126. package/dist/src/commands/ruvector/setup.js +6 -6
  127. package/dist/src/commands/security.d.ts.map +1 -1
  128. package/dist/src/commands/security.js +47 -16
  129. package/dist/src/commands/security.js.map +1 -1
  130. package/dist/src/commands/session.d.ts +1 -1
  131. package/dist/src/commands/session.js +1 -1
  132. package/dist/src/commands/start.d.ts +1 -1
  133. package/dist/src/commands/start.js +12 -12
  134. package/dist/src/commands/start.js.map +1 -1
  135. package/dist/src/commands/status.d.ts +1 -1
  136. package/dist/src/commands/status.d.ts.map +1 -1
  137. package/dist/src/commands/status.js +13 -6
  138. package/dist/src/commands/status.js.map +1 -1
  139. package/dist/src/commands/swarm.js +2 -2
  140. package/dist/src/commands/swarm.js.map +1 -1
  141. package/dist/src/commands/task.d.ts +1 -1
  142. package/dist/src/commands/task.js +1 -1
  143. package/dist/src/commands/transfer-store.js +1 -1
  144. package/dist/src/commands/transfer-store.js.map +1 -1
  145. package/dist/src/config-adapter.js +1 -1
  146. package/dist/src/config-adapter.js.map +1 -1
  147. package/dist/src/index.d.ts +4 -3
  148. package/dist/src/index.d.ts.map +1 -1
  149. package/dist/src/index.js +5 -3
  150. package/dist/src/index.js.map +1 -1
  151. package/dist/src/init/claudemd-generator.js +30 -30
  152. package/dist/src/init/claudemd-generator.js.map +1 -1
  153. package/dist/src/init/executor.d.ts.map +1 -1
  154. package/dist/src/init/executor.js +86 -101
  155. package/dist/src/init/executor.js.map +1 -1
  156. package/dist/src/init/helpers-generator.d.ts.map +1 -1
  157. package/dist/src/init/helpers-generator.js +96 -26
  158. package/dist/src/init/helpers-generator.js.map +1 -1
  159. package/dist/src/init/mcp-generator.d.ts +0 -1
  160. package/dist/src/init/mcp-generator.d.ts.map +1 -1
  161. package/dist/src/init/mcp-generator.js +34 -18
  162. package/dist/src/init/mcp-generator.js.map +1 -1
  163. package/dist/src/init/settings-generator.d.ts.map +1 -1
  164. package/dist/src/init/settings-generator.js +107 -41
  165. package/dist/src/init/settings-generator.js.map +1 -1
  166. package/dist/src/init/statusline-generator.d.ts +16 -8
  167. package/dist/src/init/statusline-generator.d.ts.map +1 -1
  168. package/dist/src/init/statusline-generator.js +537 -1019
  169. package/dist/src/init/statusline-generator.js.map +1 -1
  170. package/dist/src/init/types.d.ts +14 -4
  171. package/dist/src/init/types.d.ts.map +1 -1
  172. package/dist/src/init/types.js +9 -2
  173. package/dist/src/init/types.js.map +1 -1
  174. package/dist/src/mcp-client.d.ts.map +1 -1
  175. package/dist/src/mcp-client.js +10 -0
  176. package/dist/src/mcp-client.js.map +1 -1
  177. package/dist/src/mcp-server.d.ts.map +1 -1
  178. package/dist/src/mcp-server.js +30 -4
  179. package/dist/src/mcp-server.js.map +1 -1
  180. package/dist/src/mcp-tools/agentdb-tools.d.ts +30 -0
  181. package/dist/src/mcp-tools/agentdb-tools.d.ts.map +1 -0
  182. package/dist/src/mcp-tools/agentdb-tools.js +557 -0
  183. package/dist/src/mcp-tools/agentdb-tools.js.map +1 -0
  184. package/dist/src/mcp-tools/browser-tools.js +2 -2
  185. package/dist/src/mcp-tools/browser-tools.js.map +1 -1
  186. package/dist/src/mcp-tools/config-tools.d.ts.map +1 -1
  187. package/dist/src/mcp-tools/config-tools.js +21 -2
  188. package/dist/src/mcp-tools/config-tools.js.map +1 -1
  189. package/dist/src/mcp-tools/coordination-tools.d.ts.map +1 -1
  190. package/dist/src/mcp-tools/coordination-tools.js +192 -13
  191. package/dist/src/mcp-tools/coordination-tools.js.map +1 -1
  192. package/dist/src/mcp-tools/daa-tools.js +5 -5
  193. package/dist/src/mcp-tools/daa-tools.js.map +1 -1
  194. package/dist/src/mcp-tools/github-tools.js +2 -2
  195. package/dist/src/mcp-tools/github-tools.js.map +1 -1
  196. package/dist/src/mcp-tools/hive-mind-tools.d.ts.map +1 -1
  197. package/dist/src/mcp-tools/hive-mind-tools.js +263 -35
  198. package/dist/src/mcp-tools/hive-mind-tools.js.map +1 -1
  199. package/dist/src/mcp-tools/hooks-tools.d.ts.map +1 -1
  200. package/dist/src/mcp-tools/hooks-tools.js +355 -40
  201. package/dist/src/mcp-tools/hooks-tools.js.map +1 -1
  202. package/dist/src/mcp-tools/index.d.ts +2 -0
  203. package/dist/src/mcp-tools/index.d.ts.map +1 -1
  204. package/dist/src/mcp-tools/index.js +2 -0
  205. package/dist/src/mcp-tools/index.js.map +1 -1
  206. package/dist/src/mcp-tools/memory-tools.d.ts.map +1 -1
  207. package/dist/src/mcp-tools/memory-tools.js +31 -1
  208. package/dist/src/mcp-tools/memory-tools.js.map +1 -1
  209. package/dist/src/mcp-tools/neural-tools.d.ts.map +1 -1
  210. package/dist/src/mcp-tools/neural-tools.js +32 -27
  211. package/dist/src/mcp-tools/neural-tools.js.map +1 -1
  212. package/dist/src/mcp-tools/performance-tools.js +1 -1
  213. package/dist/src/mcp-tools/performance-tools.js.map +1 -1
  214. package/dist/src/mcp-tools/ruvllm-tools.d.ts +9 -0
  215. package/dist/src/mcp-tools/ruvllm-tools.d.ts.map +1 -0
  216. package/dist/src/mcp-tools/ruvllm-tools.js +283 -0
  217. package/dist/src/mcp-tools/ruvllm-tools.js.map +1 -0
  218. package/dist/src/mcp-tools/swarm-tools.d.ts +2 -1
  219. package/dist/src/mcp-tools/swarm-tools.d.ts.map +1 -1
  220. package/dist/src/mcp-tools/swarm-tools.js +216 -30
  221. package/dist/src/mcp-tools/swarm-tools.js.map +1 -1
  222. package/dist/src/mcp-tools/system-tools.d.ts.map +1 -1
  223. package/dist/src/mcp-tools/system-tools.js +109 -6
  224. package/dist/src/mcp-tools/system-tools.js.map +1 -1
  225. package/dist/src/mcp-tools/task-tools.d.ts.map +1 -1
  226. package/dist/src/mcp-tools/task-tools.js +102 -0
  227. package/dist/src/mcp-tools/task-tools.js.map +1 -1
  228. package/dist/src/mcp-tools/wasm-agent-tools.d.ts +9 -0
  229. package/dist/src/mcp-tools/wasm-agent-tools.d.ts.map +1 -0
  230. package/dist/src/mcp-tools/wasm-agent-tools.js +230 -0
  231. package/dist/src/mcp-tools/wasm-agent-tools.js.map +1 -0
  232. package/dist/src/mcp-tools/workflow-tools.d.ts.map +1 -1
  233. package/dist/src/mcp-tools/workflow-tools.js +91 -0
  234. package/dist/src/mcp-tools/workflow-tools.js.map +1 -1
  235. package/dist/src/memory/ewc-consolidation.d.ts +24 -0
  236. package/dist/src/memory/ewc-consolidation.d.ts.map +1 -1
  237. package/dist/src/memory/ewc-consolidation.js +59 -0
  238. package/dist/src/memory/ewc-consolidation.js.map +1 -1
  239. package/dist/src/memory/intelligence.d.ts +53 -0
  240. package/dist/src/memory/intelligence.d.ts.map +1 -1
  241. package/dist/src/memory/intelligence.js +264 -7
  242. package/dist/src/memory/intelligence.js.map +1 -1
  243. package/dist/src/memory/memory-bridge.d.ts +407 -0
  244. package/dist/src/memory/memory-bridge.d.ts.map +1 -0
  245. package/dist/src/memory/memory-bridge.js +1494 -0
  246. package/dist/src/memory/memory-bridge.js.map +1 -0
  247. package/dist/src/memory/memory-initializer.d.ts +17 -1
  248. package/dist/src/memory/memory-initializer.d.ts.map +1 -1
  249. package/dist/src/memory/memory-initializer.js +320 -42
  250. package/dist/src/memory/memory-initializer.js.map +1 -1
  251. package/dist/src/output.d.ts.map +1 -1
  252. package/dist/src/output.js +1 -0
  253. package/dist/src/output.js.map +1 -1
  254. package/dist/src/parser.d.ts +10 -0
  255. package/dist/src/parser.d.ts.map +1 -1
  256. package/dist/src/parser.js +49 -3
  257. package/dist/src/parser.js.map +1 -1
  258. package/dist/src/plugins/manager.d.ts.map +1 -1
  259. package/dist/src/plugins/manager.js +31 -14
  260. package/dist/src/plugins/manager.js.map +1 -1
  261. package/dist/src/plugins/store/discovery.js +5 -5
  262. package/dist/src/plugins/store/discovery.js.map +1 -1
  263. package/dist/src/plugins/tests/standalone-test.js +4 -4
  264. package/dist/src/plugins/tests/standalone-test.js.map +1 -1
  265. package/dist/src/production/error-handler.js +1 -1
  266. package/dist/src/production/error-handler.js.map +1 -1
  267. package/dist/src/runtime/headless.d.ts +3 -3
  268. package/dist/src/runtime/headless.js +6 -6
  269. package/dist/src/runtime/headless.js.map +1 -1
  270. package/dist/src/ruvector/agent-wasm.d.ts +182 -0
  271. package/dist/src/ruvector/agent-wasm.d.ts.map +1 -0
  272. package/dist/src/ruvector/agent-wasm.js +316 -0
  273. package/dist/src/ruvector/agent-wasm.js.map +1 -0
  274. package/dist/src/ruvector/enhanced-model-router.d.ts.map +1 -1
  275. package/dist/src/ruvector/enhanced-model-router.js +25 -15
  276. package/dist/src/ruvector/enhanced-model-router.js.map +1 -1
  277. package/dist/src/ruvector/index.d.ts +7 -1
  278. package/dist/src/ruvector/index.d.ts.map +1 -1
  279. package/dist/src/ruvector/index.js +17 -1
  280. package/dist/src/ruvector/index.js.map +1 -1
  281. package/dist/src/ruvector/ruvllm-wasm.d.ts +179 -0
  282. package/dist/src/ruvector/ruvllm-wasm.d.ts.map +1 -0
  283. package/dist/src/ruvector/ruvllm-wasm.js +363 -0
  284. package/dist/src/ruvector/ruvllm-wasm.js.map +1 -0
  285. package/dist/src/services/agentic-flow-bridge.d.ts +50 -0
  286. package/dist/src/services/agentic-flow-bridge.d.ts.map +1 -0
  287. package/dist/src/services/agentic-flow-bridge.js +95 -0
  288. package/dist/src/services/agentic-flow-bridge.js.map +1 -0
  289. package/dist/src/services/claim-service.js +1 -1
  290. package/dist/src/services/claim-service.js.map +1 -1
  291. package/dist/src/services/container-worker-pool.d.ts.map +1 -1
  292. package/dist/src/services/container-worker-pool.js +3 -1
  293. package/dist/src/services/container-worker-pool.js.map +1 -1
  294. package/dist/src/services/index.d.ts +1 -1
  295. package/dist/src/services/index.d.ts.map +1 -1
  296. package/dist/src/services/registry-api.d.ts +1 -1
  297. package/dist/src/services/registry-api.js +1 -1
  298. package/dist/src/services/ruvector-training.d.ts +11 -2
  299. package/dist/src/services/ruvector-training.d.ts.map +1 -1
  300. package/dist/src/services/ruvector-training.js +233 -43
  301. package/dist/src/services/ruvector-training.js.map +1 -1
  302. package/dist/src/services/worker-daemon.d.ts +28 -3
  303. package/dist/src/services/worker-daemon.d.ts.map +1 -1
  304. package/dist/src/services/worker-daemon.js +156 -17
  305. package/dist/src/services/worker-daemon.js.map +1 -1
  306. package/dist/src/services/worker-queue.d.ts.map +1 -1
  307. package/dist/src/services/worker-queue.js +2 -0
  308. package/dist/src/services/worker-queue.js.map +1 -1
  309. package/dist/src/transfer/deploy-seraphine.d.ts +1 -1
  310. package/dist/src/transfer/deploy-seraphine.js +4 -4
  311. package/dist/src/transfer/deploy-seraphine.js.map +1 -1
  312. package/dist/src/transfer/ipfs/client.d.ts.map +1 -1
  313. package/dist/src/transfer/ipfs/client.js +8 -0
  314. package/dist/src/transfer/ipfs/client.js.map +1 -1
  315. package/dist/src/transfer/ipfs/upload.d.ts.map +1 -1
  316. package/dist/src/transfer/ipfs/upload.js +0 -2
  317. package/dist/src/transfer/ipfs/upload.js.map +1 -1
  318. package/dist/src/transfer/models/seraphine.d.ts +1 -1
  319. package/dist/src/transfer/models/seraphine.js +5 -5
  320. package/dist/src/transfer/models/seraphine.js.map +1 -1
  321. package/dist/src/transfer/serialization/cfp.d.ts +1 -1
  322. package/dist/src/transfer/serialization/cfp.d.ts.map +1 -1
  323. package/dist/src/transfer/serialization/cfp.js +9 -6
  324. package/dist/src/transfer/serialization/cfp.js.map +1 -1
  325. package/dist/src/transfer/storage/gcs.d.ts.map +1 -1
  326. package/dist/src/transfer/storage/gcs.js +71 -29
  327. package/dist/src/transfer/storage/gcs.js.map +1 -1
  328. package/dist/src/transfer/store/discovery.js +4 -4
  329. package/dist/src/transfer/store/discovery.js.map +1 -1
  330. package/dist/src/transfer/store/registry.js +1 -1
  331. package/dist/src/transfer/store/registry.js.map +1 -1
  332. package/dist/src/transfer/store/tests/standalone-test.js +4 -4
  333. package/dist/src/transfer/store/tests/standalone-test.js.map +1 -1
  334. package/dist/src/transfer/types.d.ts +1 -1
  335. package/dist/src/types.d.ts +1 -1
  336. package/dist/src/types.js +1 -1
  337. package/dist/src/update/validator.js +1 -1
  338. package/dist/src/update/validator.js.map +1 -1
  339. package/dist/tsconfig.tsbuildinfo +1 -1
  340. package/package.json +13 -10
  341. package/.claude/agents/custom/accessibility-auditor.yaml +0 -56
  342. package/.claude/agents/custom/design-architect.yaml +0 -48
  343. package/.claude/agents/custom/ui-developer.yaml +0 -46
  344. package/.claude/agents/custom/ux-researcher.yaml +0 -60
@@ -1,48 +1,54 @@
1
1
  /**
2
- * Statusline Configuration Generator
3
- * Creates statusline configuration for V3 progress display
2
+ * Statusline Configuration Generator (Optimized)
3
+ * Creates fast, reliable statusline for V3 progress display
4
+ *
5
+ * Performance:
6
+ * - Single combined git execSync call (not 8+ separate ones)
7
+ * - process.memoryUsage() instead of ps aux
8
+ * - No recursive test file content reading
9
+ * - Shared settings cache
10
+ * - Strict 2s timeouts on all shell calls
4
11
  */
5
12
  /**
6
- * Generate statusline configuration script
7
- * Matches the advanced format:
8
- * ▊ KynjalFlow● user │ ⎇ v3 │ Opus 4.5
13
+ * Generate optimized statusline script
14
+ * Output format:
15
+ * ▊ RuFlo V3.5 ● user │ ⎇ branch │ Opus 4.6 (1M context)
9
16
  * ─────────────────────────────────────────────────────
10
- * 🏗️ DDD Domains [●●●●●] 5/5 ⚡ HNSW 12500x (or 📚 22.9k patterns)
11
- * 🤖 Swarm ◉ [12/15] 👥 0 🟢 CVE 3/3 💾 5177MB 📂 56% 🧠 30%
12
- * 🔧 Architecture DDD100% │ SecurityCLEAN │ Memory ●AgentDBIntegration
17
+ * 🏗️ DDD Domains [●●○○○] 2/5 ⚡ HNSW 150x
18
+ * 🤖 Swarm ◉ [ 5/15] 👥 2 🪝 10/17 🟢 CVE 3/3 💾 4MB 🧠 63%
19
+ * 🔧 Architecture ADRs71% │ DDD13%SecurityCLEAN
20
+ * 📊 AgentDB Vectors ●3104⚡ │ Size 216KB │ Tests ●6 (~24 cases) │ MCP ●1/1
13
21
  */
14
22
  export function generateStatuslineScript(options) {
15
- const config = options.statusline;
16
- // Generate CommonJS script - use .cjs extension for ES module project compatibility
23
+ const maxAgents = options.runtime.maxAgents;
17
24
  return `#!/usr/bin/env node
18
25
  /**
19
- * KynjalFlow V3 Statusline Generator
26
+ * RuFlo V3 Statusline Generator (Optimized)
20
27
  * Displays real-time V3 implementation progress and system status
21
28
  *
22
29
  * Usage: node statusline.cjs [--json] [--compact]
23
30
  *
24
- * IMPORTANT: This file uses .cjs extension to work in ES module projects.
25
- * The require() syntax is intentional for CommonJS compatibility.
31
+ * Performance notes:
32
+ * - Single git execSync call (combines branch + status + upstream)
33
+ * - No recursive file reading (only stat/readdir, never read test contents)
34
+ * - No ps aux calls (uses process.memoryUsage() + file-based metrics)
35
+ * - Strict 2s timeout on all execSync calls
36
+ * - Shared settings cache across functions
26
37
  */
27
38
 
28
39
  /* eslint-disable @typescript-eslint/no-var-requires */
29
40
  const fs = require('fs');
30
41
  const path = require('path');
31
42
  const { execSync } = require('child_process');
43
+ const os = require('os');
32
44
 
33
45
  // Configuration
34
46
  const CONFIG = {
35
- enabled: ${config.enabled},
36
- showProgress: ${config.showProgress},
37
- showSecurity: ${config.showSecurity},
38
- showSwarm: ${config.showSwarm},
39
- showHooks: ${config.showHooks},
40
- showPerformance: ${config.showPerformance},
41
- refreshInterval: ${config.refreshInterval},
42
- maxAgents: ${options.runtime.maxAgents},
43
- topology: '${options.runtime.topology}',
47
+ maxAgents: ${maxAgents},
44
48
  };
45
49
 
50
+ const CWD = process.cwd();
51
+
46
52
  // ANSI colors
47
53
  const c = {
48
54
  reset: '\\x1b[0m',
@@ -63,225 +69,178 @@ const c = {
63
69
  brightWhite: '\\x1b[1;37m',
64
70
  };
65
71
 
66
- // Get user info
67
- function getUserInfo() {
68
- let name = 'user';
69
- let gitBranch = '';
70
- let modelName = '🤖 Claude Code';
71
-
72
+ // Safe execSync with strict timeout (returns empty string on failure)
73
+ function safeExec(cmd, timeoutMs = 2000) {
72
74
  try {
73
- name = execSync('git config user.name 2>/dev/null || echo "user"', { encoding: 'utf-8' }).trim();
74
- gitBranch = execSync('git branch --show-current 2>/dev/null || echo ""', { encoding: 'utf-8' }).trim();
75
- } catch (e) {
76
- // Ignore errors
75
+ return execSync(cmd, {
76
+ encoding: 'utf-8',
77
+ timeout: timeoutMs,
78
+ stdio: ['pipe', 'pipe', 'pipe'],
79
+ }).trim();
80
+ } catch {
81
+ return '';
77
82
  }
83
+ }
78
84
 
79
- // Auto-detect model from Claude Code's config
85
+ // Safe JSON file reader (returns null on failure)
86
+ function readJSON(filePath) {
80
87
  try {
81
- const homedir = require('os').homedir();
82
- const claudeConfigPath = path.join(homedir, '.claude.json');
83
- if (fs.existsSync(claudeConfigPath)) {
84
- const claudeConfig = JSON.parse(fs.readFileSync(claudeConfigPath, 'utf-8'));
85
- // Try to find lastModelUsage - check current dir and parent dirs
86
- let lastModelUsage = null;
87
- const cwd = process.cwd();
88
- if (claudeConfig.projects) {
89
- // Try exact match first, then check if cwd starts with any project path
90
- for (const [projectPath, projectConfig] of Object.entries(claudeConfig.projects)) {
91
- if (cwd === projectPath || cwd.startsWith(projectPath + '/')) {
92
- lastModelUsage = projectConfig.lastModelUsage;
93
- break;
94
- }
95
- }
96
- }
97
- if (lastModelUsage) {
98
- const modelIds = Object.keys(lastModelUsage);
99
- if (modelIds.length > 0) {
100
- // Find the most recently used model by checking lastUsedAt timestamps
101
- // or fall back to the last key in the object (preserves insertion order in modern JS)
102
- let modelId = modelIds[modelIds.length - 1];
103
- let latestTimestamp = 0;
104
-
105
- for (const id of modelIds) {
106
- const usage = lastModelUsage[id];
107
- // Check for lastUsedAt timestamp (if available)
108
- if (usage.lastUsedAt) {
109
- const ts = new Date(usage.lastUsedAt).getTime();
110
- if (ts > latestTimestamp) {
111
- latestTimestamp = ts;
112
- modelId = id;
113
- }
114
- }
115
- }
116
-
117
- // Parse model ID to human-readable name
118
- if (modelId.includes('opus-4-6') || modelId.includes('opus-4.6')) modelName = 'Opus 4.6';
119
- else if (modelId.includes('opus')) modelName = 'Opus 4.5';
120
- else if (modelId.includes('sonnet-4-6') || modelId.includes('sonnet-4.6')) modelName = 'Sonnet 4.6';
121
- else if (modelId.includes('sonnet')) modelName = 'Sonnet 4';
122
- else if (modelId.includes('haiku')) modelName = 'Haiku 4.5';
123
- else modelName = modelId.split('-').slice(1, 3).join(' ');
124
- }
125
- }
88
+ if (fs.existsSync(filePath)) {
89
+ return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
126
90
  }
127
- } catch (e) {
128
- // Fallback to Unknown if can't read config
129
- }
91
+ } catch { /* ignore */ }
92
+ return null;
93
+ }
130
94
 
131
- // Fallback: check project's .claude/settings.json for model
132
- if (modelName === 'Unknown') {
133
- try {
134
- const settingsPath = path.join(process.cwd(), '.claude', 'settings.json');
135
- if (fs.existsSync(settingsPath)) {
136
- const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
137
- if (settings.model) {
138
- if (settings.model.includes('opus-4-6') || settings.model.includes('opus-4.6')) modelName = 'Opus 4.6';
139
- else if (settings.model.includes('opus')) modelName = 'Opus 4.5';
140
- else if (settings.model.includes('sonnet-4-6') || settings.model.includes('sonnet-4.6')) modelName = 'Sonnet 4.6';
141
- else if (settings.model.includes('sonnet')) modelName = 'Sonnet 4';
142
- else if (settings.model.includes('haiku')) modelName = 'Haiku 4.5';
143
- else modelName = settings.model.split('-').slice(1, 3).join(' ');
144
- }
145
- }
146
- } catch (e) {
147
- // Keep Unknown
148
- }
149
- }
95
+ // Safe file stat (returns null on failure)
96
+ function safeStat(filePath) {
97
+ try {
98
+ return fs.statSync(filePath);
99
+ } catch { /* ignore */ }
100
+ return null;
101
+ }
150
102
 
151
- return { name, gitBranch, modelName };
103
+ // Shared settings cache — read once, used by multiple functions
104
+ let _settingsCache = undefined;
105
+ function getSettings() {
106
+ if (_settingsCache !== undefined) return _settingsCache;
107
+ _settingsCache = readJSON(path.join(CWD, '.claude', 'settings.json'))
108
+ || readJSON(path.join(CWD, '.claude', 'settings.local.json'))
109
+ || null;
110
+ return _settingsCache;
152
111
  }
153
112
 
154
- // Get learning stats from intelligence loop data (ADR-050)
155
- function getLearningStats() {
156
- let patterns = 0;
157
- let sessions = 0;
158
- let trajectories = 0;
159
- let edges = 0;
160
- let confidenceMean = 0;
161
- let accessedCount = 0;
162
- let trend = 'STABLE';
163
-
164
- // PRIMARY: Read from intelligence loop data files
165
- const dataDir = path.join(process.cwd(), '.claude-flow', 'data');
166
-
167
- // 1. graph-state.json — authoritative node/edge counts
168
- const graphPath = path.join(dataDir, 'graph-state.json');
169
- if (fs.existsSync(graphPath)) {
170
- try {
171
- const graph = JSON.parse(fs.readFileSync(graphPath, 'utf-8'));
172
- patterns = graph.nodes ? Object.keys(graph.nodes).length : 0;
173
- edges = Array.isArray(graph.edges) ? graph.edges.length : 0;
174
- } catch (e) { /* ignore */ }
175
- }
113
+ // ─── Data Collection (all pure-Node.js or single-exec) ──────────
176
114
 
177
- // 2. ranked-context.json confidence and access data
178
- const rankedPath = path.join(dataDir, 'ranked-context.json');
179
- if (fs.existsSync(rankedPath)) {
180
- try {
181
- const ranked = JSON.parse(fs.readFileSync(rankedPath, 'utf-8'));
182
- if (ranked.entries && ranked.entries.length > 0) {
183
- patterns = Math.max(patterns, ranked.entries.length);
184
- let confSum = 0;
185
- let accCount = 0;
186
- for (let i = 0; i < ranked.entries.length; i++) {
187
- confSum += (ranked.entries[i].confidence || 0);
188
- if ((ranked.entries[i].accessCount || 0) > 0) accCount++;
189
- }
190
- confidenceMean = confSum / ranked.entries.length;
191
- accessedCount = accCount;
192
- }
193
- } catch (e) { /* ignore */ }
194
- }
115
+ // Get all git info in ONE shell call
116
+ function getGitInfo() {
117
+ const result = {
118
+ name: 'user', gitBranch: '', modified: 0, untracked: 0,
119
+ staged: 0, ahead: 0, behind: 0,
120
+ };
195
121
 
196
- // 3. intelligence-snapshot.json trend history
197
- const snapshotPath = path.join(dataDir, 'intelligence-snapshot.json');
198
- if (fs.existsSync(snapshotPath)) {
199
- try {
200
- const snapshot = JSON.parse(fs.readFileSync(snapshotPath, 'utf-8'));
201
- if (snapshot.history && snapshot.history.length >= 2) {
202
- const first = snapshot.history[0];
203
- const last = snapshot.history[snapshot.history.length - 1];
204
- const confDrift = (last.confidenceMean || 0) - (first.confidenceMean || 0);
205
- trend = confDrift > 0.01 ? 'IMPROVING' : confDrift < -0.01 ? 'DECLINING' : 'STABLE';
206
- sessions = Math.max(sessions, snapshot.history.length);
122
+ // Single shell: get user.name, branch, porcelain status, and upstream diff
123
+ const script = [
124
+ 'git config user.name 2>/dev/null || echo user',
125
+ 'echo "---SEP---"',
126
+ 'git branch --show-current 2>/dev/null',
127
+ 'echo "---SEP---"',
128
+ 'git status --porcelain 2>/dev/null',
129
+ 'echo "---SEP---"',
130
+ 'git rev-list --left-right --count HEAD...@{upstream} 2>/dev/null || echo "0 0"',
131
+ ].join('; ');
132
+
133
+ const raw = safeExec("sh -c '" + script + "'", 3000);
134
+ if (!raw) return result;
135
+
136
+ const parts = raw.split('---SEP---').map(s => s.trim());
137
+ if (parts.length >= 4) {
138
+ result.name = parts[0] || 'user';
139
+ result.gitBranch = parts[1] || '';
140
+
141
+ // Parse porcelain status
142
+ if (parts[2]) {
143
+ for (const line of parts[2].split('\\n')) {
144
+ if (!line || line.length < 2) continue;
145
+ const x = line[0], y = line[1];
146
+ if (x === '?' && y === '?') { result.untracked++; continue; }
147
+ if (x !== ' ' && x !== '?') result.staged++;
148
+ if (y !== ' ' && y !== '?') result.modified++;
207
149
  }
208
- } catch (e) { /* ignore */ }
209
- }
210
-
211
- // 4. auto-memory-store.json — fallback entry count
212
- if (patterns === 0) {
213
- const autoMemPath = path.join(dataDir, 'auto-memory-store.json');
214
- if (fs.existsSync(autoMemPath)) {
215
- try {
216
- const data = JSON.parse(fs.readFileSync(autoMemPath, 'utf-8'));
217
- patterns = Array.isArray(data) ? data.length : (data.entries ? data.entries.length : 0);
218
- } catch (e) { /* ignore */ }
219
150
  }
151
+
152
+ // Parse ahead/behind
153
+ const ab = (parts[3] || '0 0').split(/\\s+/);
154
+ result.ahead = parseInt(ab[0]) || 0;
155
+ result.behind = parseInt(ab[1]) || 0;
220
156
  }
221
157
 
222
- // FALLBACK: Legacy memory.db file-size estimation
223
- if (patterns === 0) {
224
- const memoryPaths = [
225
- path.join(process.cwd(), '.swarm', 'memory.db'),
226
- path.join(process.cwd(), '.claude-flow', 'memory.db'),
227
- path.join(process.cwd(), '.claude', 'memory.db'),
228
- path.join(process.cwd(), 'data', 'memory.db'),
229
- path.join(process.cwd(), 'memory.db'),
230
- path.join(process.cwd(), '.agentdb', 'memory.db'),
231
- ];
232
- for (let j = 0; j < memoryPaths.length; j++) {
233
- if (fs.existsSync(memoryPaths[j])) {
234
- try {
235
- const dbStats = fs.statSync(memoryPaths[j]);
236
- patterns = Math.floor(dbStats.size / 1024 / 2);
158
+ return result;
159
+ }
160
+
161
+ // Detect model name from Claude config (pure file reads, no exec)
162
+ function getModelName() {
163
+ try {
164
+ const claudeConfig = readJSON(path.join(os.homedir(), '.claude.json'));
165
+ if (claudeConfig && claudeConfig.projects) {
166
+ for (const [projectPath, projectConfig] of Object.entries(claudeConfig.projects)) {
167
+ if (CWD === projectPath || CWD.startsWith(projectPath + '/')) {
168
+ const usage = projectConfig.lastModelUsage;
169
+ if (usage) {
170
+ const ids = Object.keys(usage);
171
+ if (ids.length > 0) {
172
+ let modelId = ids[ids.length - 1];
173
+ let latest = 0;
174
+ for (const id of ids) {
175
+ const ts = usage[id] && usage[id].lastUsedAt ? new Date(usage[id].lastUsedAt).getTime() : 0;
176
+ if (ts > latest) { latest = ts; modelId = id; }
177
+ }
178
+ if (modelId.includes('opus')) return 'Opus 4.6 (1M context)';
179
+ if (modelId.includes('sonnet')) return 'Sonnet 4.6';
180
+ if (modelId.includes('haiku')) return 'Haiku 4.5';
181
+ return modelId.split('-').slice(1, 3).join(' ');
182
+ }
183
+ }
237
184
  break;
238
- } catch (e) { /* ignore */ }
185
+ }
239
186
  }
240
187
  }
188
+ } catch { /* ignore */ }
189
+
190
+ // Fallback: settings.json model field
191
+ const settings = getSettings();
192
+ if (settings && settings.model) {
193
+ const m = settings.model;
194
+ if (m.includes('opus')) return 'Opus 4.6 (1M context)';
195
+ if (m.includes('sonnet')) return 'Sonnet 4.6';
196
+ if (m.includes('haiku')) return 'Haiku 4.5';
241
197
  }
198
+ return 'Claude Code';
199
+ }
242
200
 
243
- // Session count from session files
244
- const sessionsPath = path.join(process.cwd(), '.claude', 'sessions');
245
- if (fs.existsSync(sessionsPath)) {
246
- try {
247
- const sessionFiles = fs.readdirSync(sessionsPath).filter(f => f.endsWith('.json'));
248
- sessions = Math.max(sessions, sessionFiles.length);
249
- } catch (e) { /* ignore */ }
201
+ // Get learning stats from memory database (pure stat calls)
202
+ function getLearningStats() {
203
+ const memoryPaths = [
204
+ path.join(CWD, '.swarm', 'memory.db'),
205
+ path.join(CWD, '.claude-flow', 'memory.db'),
206
+ path.join(CWD, '.claude', 'memory.db'),
207
+ path.join(CWD, 'data', 'memory.db'),
208
+ path.join(CWD, '.agentdb', 'memory.db'),
209
+ ];
210
+
211
+ for (const dbPath of memoryPaths) {
212
+ const stat = safeStat(dbPath);
213
+ if (stat) {
214
+ const sizeKB = stat.size / 1024;
215
+ const patterns = Math.floor(sizeKB / 2);
216
+ return {
217
+ patterns,
218
+ sessions: Math.max(1, Math.floor(patterns / 10)),
219
+ };
220
+ }
250
221
  }
251
222
 
252
- trajectories = Math.floor(patterns / 5);
223
+ // Check session files count
224
+ let sessions = 0;
225
+ try {
226
+ const sessDir = path.join(CWD, '.claude', 'sessions');
227
+ if (fs.existsSync(sessDir)) {
228
+ sessions = fs.readdirSync(sessDir).filter(f => f.endsWith('.json')).length;
229
+ }
230
+ } catch { /* ignore */ }
253
231
 
254
- return { patterns, sessions, trajectories, edges, confidenceMean, accessedCount, trend };
232
+ return { patterns: 0, sessions };
255
233
  }
256
234
 
257
- // Get V3 progress from REAL metrics files
235
+ // V3 progress from metrics files (pure file reads)
258
236
  function getV3Progress() {
259
237
  const learning = getLearningStats();
260
238
  const totalDomains = 5;
261
239
 
262
- let dddProgress = 0;
263
- let dddScore = 0;
264
- let dddMaxScore = 100;
265
- let moduleCount = 0;
266
-
267
- // Check ddd-progress.json for REAL DDD analysis
268
- const dddPath = path.join(process.cwd(), '.claude-flow', 'metrics', 'ddd-progress.json');
269
- if (fs.existsSync(dddPath)) {
270
- try {
271
- const data = JSON.parse(fs.readFileSync(dddPath, 'utf-8'));
272
- dddProgress = data.progress || 0;
273
- dddScore = data.score || 0;
274
- dddMaxScore = data.maxScore || 100;
275
- moduleCount = data.modules ? Object.keys(data.modules).length : 0;
276
- } catch (e) {
277
- // Ignore - use fallback
278
- }
279
- }
280
-
281
- // Calculate domains completed from DDD progress (each 20% = 1 domain)
240
+ const dddData = readJSON(path.join(CWD, '.claude-flow', 'metrics', 'ddd-progress.json'));
241
+ let dddProgress = dddData ? (dddData.progress || 0) : 0;
282
242
  let domainsCompleted = Math.min(5, Math.floor(dddProgress / 20));
283
243
 
284
- // Fallback: if no DDD data, use pattern-based calculation
285
244
  if (dddProgress === 0 && learning.patterns > 0) {
286
245
  if (learning.patterns >= 500) domainsCompleted = 5;
287
246
  else if (learning.patterns >= 200) domainsCompleted = 4;
@@ -292,820 +251,336 @@ function getV3Progress() {
292
251
  }
293
252
 
294
253
  return {
295
- domainsCompleted,
296
- totalDomains,
297
- dddProgress,
298
- dddScore,
299
- dddMaxScore,
300
- moduleCount,
254
+ domainsCompleted, totalDomains, dddProgress,
301
255
  patternsLearned: learning.patterns,
302
- sessionsCompleted: learning.sessions
256
+ sessionsCompleted: learning.sessions,
303
257
  };
304
258
  }
305
259
 
306
- // Get security status based on actual scans
260
+ // Security status (pure file reads)
307
261
  function getSecurityStatus() {
308
- const totalCves = 3;
309
- let cvesFixed = 0;
310
-
311
- // Check audit-status.json first (created by init)
312
- const auditStatusPath = path.join(process.cwd(), '.claude-flow', 'security', 'audit-status.json');
313
- if (fs.existsSync(auditStatusPath)) {
314
- try {
315
- const data = JSON.parse(fs.readFileSync(auditStatusPath, 'utf-8'));
316
- return {
317
- status: data.status || 'PENDING',
318
- cvesFixed: data.cvesFixed || 0,
319
- totalCves: data.totalCves || 3,
320
- };
321
- } catch (e) {
322
- // Fall through to scan directory check
323
- }
324
- }
325
-
326
- // Check for security scan results in memory
327
- const scanResultsPath = path.join(process.cwd(), '.claude', 'security-scans');
328
- if (fs.existsSync(scanResultsPath)) {
329
- try {
330
- const scans = fs.readdirSync(scanResultsPath).filter(f => f.endsWith('.json'));
331
- // Each successful scan file = 1 CVE addressed
332
- cvesFixed = Math.min(totalCves, scans.length);
333
- } catch (e) {
334
- // Ignore
335
- }
336
- }
337
-
338
- // Also check .swarm/security for audit results
339
- const swarmAuditPath = path.join(process.cwd(), '.swarm', 'security');
340
- if (fs.existsSync(swarmAuditPath)) {
341
- try {
342
- const audits = fs.readdirSync(swarmAuditPath).filter(f => f.includes('audit'));
343
- cvesFixed = Math.min(totalCves, Math.max(cvesFixed, audits.length));
344
- } catch (e) {
345
- // Ignore
262
+ const auditData = readJSON(path.join(CWD, '.claude-flow', 'security', 'audit-status.json'));
263
+ if (auditData) {
264
+ const auditDate = auditData.lastAudit || auditData.lastScan;
265
+ if (!auditDate) {
266
+ return { status: 'PENDING', cvesFixed: 0, totalCves: 0 };
267
+ }
268
+ const auditAge = Date.now() - new Date(auditDate).getTime();
269
+ const isStale = auditAge > 7 * 24 * 60 * 60 * 1000;
270
+ return {
271
+ status: isStale ? 'STALE' : (auditData.status || 'PENDING'),
272
+ cvesFixed: auditData.cvesFixed || 0,
273
+ totalCves: auditData.totalCves || 0,
274
+ };
275
+ }
276
+
277
+ let scanCount = 0;
278
+ try {
279
+ const scanDir = path.join(CWD, '.claude', 'security-scans');
280
+ if (fs.existsSync(scanDir)) {
281
+ scanCount = fs.readdirSync(scanDir).filter(f => f.endsWith('.json')).length;
346
282
  }
347
- }
348
-
349
- const status = cvesFixed >= totalCves ? 'CLEAN' : cvesFixed > 0 ? 'IN_PROGRESS' : 'PENDING';
283
+ } catch { /* ignore */ }
350
284
 
351
285
  return {
352
- status,
353
- cvesFixed,
354
- totalCves,
286
+ status: scanCount > 0 ? 'SCANNED' : 'NONE',
287
+ cvesFixed: 0,
288
+ totalCves: 0,
355
289
  };
356
290
  }
357
291
 
358
- // Get swarm status (cross-platform)
292
+ // Swarm status (pure file reads, NO ps aux)
359
293
  function getSwarmStatus() {
360
- let activeAgents = 0;
361
- let coordinationActive = false;
362
-
363
- // Check swarm-activity.json first (works on all platforms)
364
- const activityPath = path.join(process.cwd(), '.claude-flow', 'metrics', 'swarm-activity.json');
365
- if (fs.existsSync(activityPath)) {
366
- try {
367
- const data = JSON.parse(fs.readFileSync(activityPath, 'utf-8'));
368
- if (data.swarm) {
369
- return {
370
- activeAgents: data.swarm.agent_count || 0,
371
- maxAgents: CONFIG.maxAgents,
372
- coordinationActive: data.swarm.coordination_active || data.swarm.active || false,
373
- };
374
- }
375
- } catch (e) {
376
- // Fall through to v3-progress.json check
377
- }
378
- }
379
-
380
- // Also check v3-progress.json for swarm data (secondary source)
381
- const progressPath = path.join(process.cwd(), '.claude-flow', 'metrics', 'v3-progress.json');
382
- if (fs.existsSync(progressPath)) {
383
- try {
384
- const data = JSON.parse(fs.readFileSync(progressPath, 'utf-8'));
385
- if (data.swarm) {
386
- return {
387
- activeAgents: data.swarm.activeAgents || data.swarm.agent_count || 0,
388
- maxAgents: data.swarm.totalAgents || CONFIG.maxAgents,
389
- coordinationActive: data.swarm.active || (data.swarm.activeAgents > 0),
390
- };
391
- }
392
- } catch (e) {
393
- // Fall through to process detection
294
+ const staleThresholdMs = 5 * 60 * 1000;
295
+ const now = Date.now();
296
+
297
+ const swarmStatePath = path.join(CWD, '.claude-flow', 'swarm', 'swarm-state.json');
298
+ const swarmState = readJSON(swarmStatePath);
299
+ if (swarmState) {
300
+ const updatedAt = swarmState.updatedAt || swarmState.startedAt;
301
+ const age = updatedAt ? now - new Date(updatedAt).getTime() : Infinity;
302
+ if (age < staleThresholdMs) {
303
+ return {
304
+ activeAgents: (swarmState.agents && swarmState.agents.length) || swarmState.agentCount || 0,
305
+ maxAgents: swarmState.maxAgents || CONFIG.maxAgents,
306
+ coordinationActive: true,
307
+ };
394
308
  }
395
309
  }
396
310
 
397
- // Platform-specific process detection (fallback)
398
- const isWindows = process.platform === 'win32';
399
- try {
400
- if (isWindows) {
401
- // Windows: use tasklist
402
- const ps = execSync('tasklist /FI "IMAGENAME eq node.exe" /NH 2>nul || echo ""', { encoding: 'utf-8' });
403
- const nodeProcesses = (ps.match(/node\\.exe/gi) || []).length;
404
- activeAgents = Math.max(0, Math.floor(nodeProcesses / 3)); // Heuristic
405
- coordinationActive = nodeProcesses > 0;
406
- } else {
407
- // Unix: use ps - check for various agent process patterns
408
- try {
409
- const ps = execSync('ps aux 2>/dev/null | grep -E "(agentic-flow|claude-flow|mcp.*server)" | grep -v grep | wc -l', { encoding: 'utf-8' });
410
- activeAgents = Math.max(0, parseInt(ps.trim()));
411
- coordinationActive = activeAgents > 0;
412
- } catch (e) {
413
- // Fallback to simple agentic-flow check
414
- const ps = execSync('ps aux 2>/dev/null | grep -c agentic-flow || echo "0"', { encoding: 'utf-8' });
415
- activeAgents = Math.max(0, parseInt(ps.trim()) - 1);
416
- coordinationActive = activeAgents > 0;
417
- }
311
+ const activityData = readJSON(path.join(CWD, '.claude-flow', 'metrics', 'swarm-activity.json'));
312
+ if (activityData && activityData.swarm) {
313
+ const updatedAt = activityData.timestamp || (activityData.swarm && activityData.swarm.timestamp);
314
+ const age = updatedAt ? now - new Date(updatedAt).getTime() : Infinity;
315
+ if (age < staleThresholdMs) {
316
+ return {
317
+ activeAgents: activityData.swarm.agent_count || 0,
318
+ maxAgents: CONFIG.maxAgents,
319
+ coordinationActive: activityData.swarm.coordination_active || activityData.swarm.active || false,
320
+ };
418
321
  }
419
- } catch (e) {
420
- // Ignore errors - return defaults
421
322
  }
422
323
 
423
- return {
424
- activeAgents,
425
- maxAgents: CONFIG.maxAgents,
426
- coordinationActive,
427
- };
324
+ return { activeAgents: 0, maxAgents: CONFIG.maxAgents, coordinationActive: false };
428
325
  }
429
326
 
430
- // Get system metrics (cross-platform)
327
+ // System metrics (uses process.memoryUsage() — no shell spawn)
431
328
  function getSystemMetrics() {
432
- let memoryMB = 0;
433
- let subAgents = 0;
434
-
435
- // Check learning.json first for REAL intelligence metrics
436
- const learningMetricsPath = path.join(process.cwd(), '.claude-flow', 'metrics', 'learning.json');
437
- let intelligenceFromFile = null;
438
- let contextFromFile = null;
439
- if (fs.existsSync(learningMetricsPath)) {
440
- try {
441
- const data = JSON.parse(fs.readFileSync(learningMetricsPath, 'utf-8'));
442
- // Use intelligence.score (the REAL metric) instead of routing.accuracy
443
- if (data.intelligence?.score !== undefined) {
444
- intelligenceFromFile = Math.min(100, Math.floor(data.intelligence.score));
445
- }
446
- if (data.sessions?.total !== undefined) {
447
- contextFromFile = Math.min(100, data.sessions.total * 5);
448
- }
449
- } catch (e) {
450
- // Fall through
451
- }
452
- }
453
-
454
- // Platform-specific memory detection
455
- const isWindows = process.platform === 'win32';
456
- try {
457
- if (isWindows) {
458
- // Windows: use process.memoryUsage() (most reliable cross-platform)
459
- memoryMB = Math.floor(process.memoryUsage().heapUsed / 1024 / 1024);
460
- } else {
461
- // Unix: try ps command, fallback to process.memoryUsage()
462
- try {
463
- const mem = execSync('ps aux | grep -E "(node|agentic|claude)" | grep -v grep | awk \\'{sum += \\$6} END {print int(sum/1024)}\\'', { encoding: 'utf-8' });
464
- memoryMB = parseInt(mem.trim()) || 0;
465
- } catch (e) {
466
- memoryMB = Math.floor(process.memoryUsage().heapUsed / 1024 / 1024);
467
- }
468
- }
469
- } catch (e) {
470
- // Fallback to Node.js memory API
471
- memoryMB = Math.floor(process.memoryUsage().heapUsed / 1024 / 1024);
472
- }
473
-
474
- // Get learning stats for intelligence %
329
+ const memoryMB = Math.floor(process.memoryUsage().heapUsed / 1024 / 1024);
475
330
  const learning = getLearningStats();
331
+ const agentdb = getAgentDBStats();
476
332
 
477
- // Also get AgentDB stats for fallback intelligence calculation
478
- const agentdbStats = getAgentDBStats();
479
-
480
- // Intelligence % — priority chain (ADR-050):
481
- // 1. Intelligence loop data (confidenceMean + accessRatio + density)
482
- // 2. learning.json file metric
483
- // 3. Pattern count / vector count fallback
484
- // 4. Project maturity fallback (below)
333
+ // Intelligence from learning.json
334
+ const learningData = readJSON(path.join(CWD, '.claude-flow', 'metrics', 'learning.json'));
485
335
  let intelligencePct = 0;
336
+ let contextPct = 0;
486
337
 
487
- // Priority 1: Intelligence loop real data
488
- if (learning.confidenceMean > 0 || (learning.patterns > 0 && learning.accessedCount > 0)) {
489
- const confScore = Math.min(100, Math.floor(learning.confidenceMean * 100));
490
- const accessRatio = learning.patterns > 0 ? (learning.accessedCount / learning.patterns) : 0;
491
- const accessScore = Math.min(100, Math.floor(accessRatio * 100));
492
- const densityScore = Math.min(100, Math.floor(learning.patterns / 5));
493
- intelligencePct = Math.floor(confScore * 0.4 + accessScore * 0.3 + densityScore * 0.3);
494
- }
495
-
496
- // Priority 2: learning.json file metric
497
- if (intelligencePct === 0 && intelligenceFromFile !== null) {
498
- intelligencePct = intelligenceFromFile;
499
- }
500
-
501
- // Priority 3: Pattern/vector count fallback
502
- if (intelligencePct === 0) {
503
- const fromPatterns = learning.patterns > 0 ? Math.min(100, Math.floor(learning.patterns / 10)) : 0;
504
- const fromVectors = agentdbStats.vectorCount > 0 ? Math.min(100, Math.floor(agentdbStats.vectorCount / 100)) : 0;
338
+ if (learningData && learningData.intelligence && learningData.intelligence.score !== undefined) {
339
+ intelligencePct = Math.min(100, Math.floor(learningData.intelligence.score));
340
+ } else {
341
+ const fromPatterns = learning.patterns > 0 ? Math.min(100, Math.floor(learning.patterns / 20)) : 0;
342
+ const fromVectors = agentdb.vectorCount > 0 ? Math.min(100, Math.floor(agentdb.vectorCount / 20)) : 0;
505
343
  intelligencePct = Math.max(fromPatterns, fromVectors);
506
344
  }
507
345
 
508
- // If still 0, use project maturity fallback
346
+ // Maturity fallback (pure fs checks, no git exec)
509
347
  if (intelligencePct === 0) {
510
- // Final fallback: estimate from project maturity indicators
511
- let maturityScore = 0;
512
-
513
- // Check git commit count (proxy for project development)
514
- try {
515
- const commitCount = parseInt(execSync('git rev-list --count HEAD 2>/dev/null || echo "0"', { encoding: 'utf-8' }).trim());
516
- maturityScore += Math.min(30, Math.floor(commitCount / 10)); // Max 30% from commits
517
- } catch (e) { /* ignore */ }
518
-
519
- // Check for Claude session history
520
- const sessionPaths = [
521
- path.join(process.cwd(), '.claude', 'sessions'),
522
- path.join(process.cwd(), '.claude-flow', 'sessions'),
523
- ];
524
- for (const sessPath of sessionPaths) {
525
- if (fs.existsSync(sessPath)) {
526
- try {
527
- const sessions = fs.readdirSync(sessPath).filter(f => f.endsWith('.json')).length;
528
- maturityScore += Math.min(20, sessions * 2); // Max 20% from sessions
529
- break;
530
- } catch (e) { /* ignore */ }
531
- }
532
- }
533
-
534
- // Check for source files (indicates codebase size)
535
- try {
536
- const srcDirs = ['src', 'lib', 'app', 'packages'];
537
- for (const dir of srcDirs) {
538
- const dirPath = path.join(process.cwd(), dir);
539
- if (fs.existsSync(dirPath)) {
540
- maturityScore += 15; // Base score for having source dir
541
- break;
542
- }
543
- }
544
- } catch (e) { /* ignore */ }
545
-
546
- // Check for test files
547
- try {
548
- const testDirs = ['tests', 'test', '__tests__', 'spec'];
549
- for (const dir of testDirs) {
550
- const dirPath = path.join(process.cwd(), dir);
551
- if (fs.existsSync(dirPath)) {
552
- maturityScore += 10; // Bonus for having tests
553
- break;
554
- }
555
- }
556
- } catch (e) { /* ignore */ }
557
-
558
- // Check for .claude directory (Claude Code usage)
559
- if (fs.existsSync(path.join(process.cwd(), '.claude'))) {
560
- maturityScore += 15; // Bonus for Claude Code integration
561
- }
562
-
563
- // Check for config files (project maturity)
564
- const configFiles = ['package.json', 'tsconfig.json', 'pyproject.toml', 'Cargo.toml', 'go.mod'];
565
- for (const cfg of configFiles) {
566
- if (fs.existsSync(path.join(process.cwd(), cfg))) {
567
- maturityScore += 5;
568
- break;
569
- }
570
- }
571
-
572
- intelligencePct = Math.min(100, maturityScore);
573
- }
574
-
575
- // Context % based on session history (0 sessions = 0%, grows with usage)
576
- const contextPct = contextFromFile !== null
577
- ? contextFromFile
578
- : Math.min(100, Math.floor(learning.sessions * 5));
579
-
580
- // Count active sub-agents (cross-platform via metrics file)
581
- const activityPath = path.join(process.cwd(), '.claude-flow', 'metrics', 'swarm-activity.json');
582
- if (fs.existsSync(activityPath)) {
583
- try {
584
- const data = JSON.parse(fs.readFileSync(activityPath, 'utf-8'));
585
- subAgents = data.processes?.estimated_agents || 0;
586
- } catch (e) {
587
- // Ignore
588
- }
348
+ let score = 0;
349
+ if (fs.existsSync(path.join(CWD, '.claude'))) score += 15;
350
+ const srcDirs = ['src', 'lib', 'app', 'packages', 'v3'];
351
+ for (const d of srcDirs) { if (fs.existsSync(path.join(CWD, d))) { score += 15; break; } }
352
+ const testDirs = ['tests', 'test', '__tests__', 'spec'];
353
+ for (const d of testDirs) { if (fs.existsSync(path.join(CWD, d))) { score += 10; break; } }
354
+ const cfgFiles = ['package.json', 'tsconfig.json', 'pyproject.toml', 'Cargo.toml', 'go.mod'];
355
+ for (const f of cfgFiles) { if (fs.existsSync(path.join(CWD, f))) { score += 5; break; } }
356
+ intelligencePct = Math.min(100, score);
357
+ }
358
+
359
+ if (learningData && learningData.sessions && learningData.sessions.total !== undefined) {
360
+ contextPct = Math.min(100, learningData.sessions.total * 5);
361
+ } else {
362
+ contextPct = Math.min(100, Math.floor(learning.sessions * 5));
589
363
  }
590
364
 
591
- // Fallback to process detection on Unix only
592
- if (subAgents === 0 && !isWindows) {
593
- try {
594
- const agents = execSync('ps aux 2>/dev/null | grep -c "claude-flow.*agent" || echo "0"', { encoding: 'utf-8' });
595
- subAgents = Math.max(0, parseInt(agents.trim()) - 1);
596
- } catch (e) {
597
- // Ignore
598
- }
365
+ // Sub-agents from file metrics (no ps aux)
366
+ let subAgents = 0;
367
+ const activityData = readJSON(path.join(CWD, '.claude-flow', 'metrics', 'swarm-activity.json'));
368
+ if (activityData && activityData.processes && activityData.processes.estimated_agents) {
369
+ subAgents = activityData.processes.estimated_agents;
599
370
  }
600
371
 
601
- return {
602
- memoryMB,
603
- contextPct,
604
- intelligencePct,
605
- subAgents,
606
- };
372
+ return { memoryMB, contextPct, intelligencePct, subAgents };
607
373
  }
608
374
 
609
- // Get ADR (Architecture Decision Records) status from REAL compliance data
375
+ // ADR status (count files only don't read contents)
610
376
  function getADRStatus() {
611
- let compliance = 0;
612
- let totalChecks = 0;
613
- let compliantChecks = 0;
614
- let checks = {};
615
-
616
- // Check adr-compliance.json for REAL compliance data
617
- const compliancePath = path.join(process.cwd(), '.claude-flow', 'metrics', 'adr-compliance.json');
618
- if (fs.existsSync(compliancePath)) {
619
- try {
620
- const data = JSON.parse(fs.readFileSync(compliancePath, 'utf-8'));
621
- compliance = data.compliance || 0;
622
- checks = data.checks || {};
623
- totalChecks = Object.keys(checks).length;
624
- compliantChecks = Object.values(checks).filter(c => c.compliant).length;
625
- return { count: totalChecks, implemented: compliantChecks, compliance };
626
- } catch (e) {
627
- // Fall through to file-based detection
628
- }
629
- }
630
-
631
- // Fallback: count ADR files directly
377
+ // Count actual ADR files first — compliance JSON may be stale
632
378
  const adrPaths = [
633
- path.join(process.cwd(), 'docs', 'adrs'),
634
- path.join(process.cwd(), 'docs', 'adr'),
635
- path.join(process.cwd(), 'adr'),
636
- path.join(process.cwd(), 'ADR'),
637
- path.join(process.cwd(), '.claude-flow', 'adrs'),
638
- path.join(process.cwd(), 'v3', 'implementation', 'adrs'),
639
- path.join(process.cwd(), 'implementation', 'adrs'),
379
+ path.join(CWD, 'v3', 'implementation', 'adrs'),
380
+ path.join(CWD, 'docs', 'adrs'),
381
+ path.join(CWD, '.claude-flow', 'adrs'),
640
382
  ];
641
383
 
642
- let count = 0;
643
- let implemented = 0;
644
-
645
384
  for (const adrPath of adrPaths) {
646
- if (fs.existsSync(adrPath)) {
647
- try {
385
+ try {
386
+ if (fs.existsSync(adrPath)) {
648
387
  const files = fs.readdirSync(adrPath).filter(f =>
649
388
  f.endsWith('.md') && (f.startsWith('ADR-') || f.startsWith('adr-') || /^\\d{4}-/.test(f))
650
389
  );
651
- count = files.length;
652
-
653
- for (const file of files) {
654
- try {
655
- const content = fs.readFileSync(path.join(adrPath, file), 'utf-8');
656
- if (content.includes('Status: Implemented') || content.includes('status: implemented') ||
657
- content.includes('Status: Accepted') || content.includes('status: accepted')) {
658
- implemented++;
659
- }
660
- } catch (e) {
661
- // Skip unreadable files
662
- }
663
- }
664
- break;
665
- } catch (e) {
666
- // Ignore
390
+ return { count: files.length, implemented: files.length, compliance: 0 };
667
391
  }
668
- }
392
+ } catch { /* ignore */ }
669
393
  }
670
394
 
671
- compliance = count > 0 ? Math.floor((implemented / count) * 100) : 0;
672
- return { count, implemented, compliance };
395
+ return { count: 0, implemented: 0, compliance: 0 };
673
396
  }
674
397
 
675
- // Get hooks status (enabled/registered hooks)
398
+ // Hooks status (shared settings cache)
676
399
  function getHooksStatus() {
677
400
  let enabled = 0;
678
- let total = 17; // V3 has 17 hook types
679
-
680
- // Check .claude/settings.json for hooks config
681
- const settingsPaths = [
682
- path.join(process.cwd(), '.claude', 'settings.json'),
683
- path.join(process.cwd(), '.claude', 'settings.local.json'),
684
- ];
685
-
686
- for (const settingsPath of settingsPaths) {
687
- if (fs.existsSync(settingsPath)) {
688
- try {
689
- const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
690
- if (settings.hooks) {
691
- // Claude Code native hooks format: PreToolUse, PostToolUse, SessionStart, etc.
692
- const hookCategories = Object.keys(settings.hooks);
693
- for (const category of hookCategories) {
694
- const categoryHooks = settings.hooks[category];
695
- if (Array.isArray(categoryHooks) && categoryHooks.length > 0) {
696
- // Count categories with at least one hook defined
697
- enabled++;
698
- }
699
- }
401
+ let total = 0;
402
+ const settings = getSettings();
403
+
404
+ if (settings && settings.hooks) {
405
+ for (const category of Object.keys(settings.hooks)) {
406
+ const matchers = settings.hooks[category];
407
+ if (!Array.isArray(matchers)) continue;
408
+ for (const matcher of matchers) {
409
+ const hooks = matcher && matcher.hooks;
410
+ if (Array.isArray(hooks)) {
411
+ total += hooks.length;
412
+ enabled += hooks.length;
700
413
  }
701
- break;
702
- } catch (e) {
703
- // Ignore parse errors
704
414
  }
705
415
  }
706
416
  }
707
417
 
708
- // Also check for hook files in .claude/hooks
709
- const hooksDir = path.join(process.cwd(), '.claude', 'hooks');
710
- if (fs.existsSync(hooksDir)) {
711
- try {
712
- const hookFiles = fs.readdirSync(hooksDir).filter(f => f.endsWith('.js') || f.endsWith('.sh'));
713
- enabled = Math.max(enabled, hookFiles.length);
714
- } catch (e) {
715
- // Ignore
418
+ try {
419
+ const hooksDir = path.join(CWD, '.claude', 'hooks');
420
+ if (fs.existsSync(hooksDir)) {
421
+ const hookFiles = fs.readdirSync(hooksDir).filter(f => f.endsWith('.js') || f.endsWith('.sh')).length;
422
+ total = Math.max(total, hookFiles);
423
+ enabled = Math.max(enabled, hookFiles);
716
424
  }
717
- }
425
+ } catch { /* ignore */ }
718
426
 
719
427
  return { enabled, total };
720
428
  }
721
429
 
722
- // Get AgentDB memory stats
430
+ // AgentDB stats — count real entries, not file-size heuristics
723
431
  function getAgentDBStats() {
724
432
  let vectorCount = 0;
725
433
  let dbSizeKB = 0;
726
434
  let namespaces = 0;
727
435
  let hasHnsw = false;
728
436
 
729
- // Check for database directories
730
- const dbDirPaths = [
731
- path.join(process.cwd(), '.claude-flow', 'agentdb'),
732
- path.join(process.cwd(), '.swarm', 'agentdb'),
733
- path.join(process.cwd(), 'data', 'agentdb'),
734
- path.join(process.cwd(), '.claude', 'memory'),
735
- path.join(process.cwd(), '.agentdb'),
736
- ];
737
-
738
- // Check for direct database files (memory.db, etc.)
739
- const dbFilePaths = [
740
- path.join(process.cwd(), '.swarm', 'memory.db'),
741
- path.join(process.cwd(), '.claude-flow', 'memory.db'),
742
- path.join(process.cwd(), '.claude', 'memory.db'),
743
- path.join(process.cwd(), 'data', 'memory.db'),
744
- path.join(process.cwd(), 'memory.db'),
745
- ];
437
+ // 1. Count real entries from auto-memory-store.json
438
+ const storePath = path.join(CWD, '.claude-flow', 'data', 'auto-memory-store.json');
439
+ const storeStat = safeStat(storePath);
440
+ if (storeStat) {
441
+ dbSizeKB += storeStat.size / 1024;
442
+ try {
443
+ const store = JSON.parse(fs.readFileSync(storePath, 'utf-8'));
444
+ if (Array.isArray(store)) vectorCount += store.length;
445
+ else if (store && store.entries) vectorCount += store.entries.length;
446
+ } catch { /* fall back */ }
447
+ }
746
448
 
747
- // Check for HNSW index files
748
- const hnswPaths = [
749
- path.join(process.cwd(), '.swarm', 'hnsw.index'),
750
- path.join(process.cwd(), '.claude-flow', 'hnsw.index'),
751
- path.join(process.cwd(), 'data', 'hnsw.index'),
449
+ // 2. Count entries from ranked-context.json
450
+ try {
451
+ const ranked = readJSON(path.join(CWD, '.claude-flow', 'data', 'ranked-context.json'));
452
+ if (ranked && ranked.entries && ranked.entries.length > vectorCount) vectorCount = ranked.entries.length;
453
+ } catch { /* ignore */ }
454
+
455
+ // 3. Add DB file sizes
456
+ const dbFiles = [
457
+ path.join(CWD, 'data', 'memory.db'),
458
+ path.join(CWD, '.claude-flow', 'memory.db'),
459
+ path.join(CWD, '.swarm', 'memory.db'),
752
460
  ];
753
-
754
- // Check direct database files first
755
- for (const dbFile of dbFilePaths) {
756
- if (fs.existsSync(dbFile)) {
757
- try {
758
- const stats = fs.statSync(dbFile);
759
- dbSizeKB = stats.size / 1024;
760
- // Estimate vectors: ~2KB per vector for SQLite with embeddings
761
- vectorCount = Math.floor(dbSizeKB / 2);
762
- namespaces = 1;
763
- break;
764
- } catch (e) {
765
- // Ignore
766
- }
461
+ for (const f of dbFiles) {
462
+ const stat = safeStat(f);
463
+ if (stat) {
464
+ dbSizeKB += stat.size / 1024;
465
+ namespaces++;
767
466
  }
768
467
  }
769
468
 
770
- // Check database directories if no direct file found
771
- if (vectorCount === 0) {
772
- for (const dbPath of dbDirPaths) {
773
- if (fs.existsSync(dbPath)) {
774
- try {
775
- const stats = fs.statSync(dbPath);
776
- if (stats.isDirectory()) {
777
- const files = fs.readdirSync(dbPath);
778
- namespaces = files.filter(f => f.endsWith('.db') || f.endsWith('.sqlite')).length;
779
-
780
- for (const file of files) {
781
- const filePath = path.join(dbPath, file);
782
- const fileStat = fs.statSync(filePath);
783
- if (fileStat.isFile()) {
784
- dbSizeKB += fileStat.size / 1024;
785
- }
786
- }
469
+ // 4. Graph data size
470
+ const graphStat = safeStat(path.join(CWD, 'data', 'memory.graph'));
471
+ if (graphStat) dbSizeKB += graphStat.size / 1024;
787
472
 
788
- vectorCount = Math.floor(dbSizeKB / 2);
789
- }
790
- break;
791
- } catch (e) {
792
- // Ignore
793
- }
794
- }
795
- }
796
- }
797
-
798
- // Check for HNSW index (indicates vector search capability)
799
- for (const hnswPath of hnswPaths) {
800
- if (fs.existsSync(hnswPath)) {
801
- hasHnsw = true;
802
- try {
803
- const stats = fs.statSync(hnswPath);
804
- // HNSW index: ~0.5KB per vector
805
- const hnswVectors = Math.floor(stats.size / 1024 / 0.5);
806
- vectorCount = Math.max(vectorCount, hnswVectors);
807
- } catch (e) {
808
- // Ignore
809
- }
810
- break;
811
- }
473
+ // 5. HNSW index or memory package
474
+ const hnswPaths = [
475
+ path.join(CWD, '.swarm', 'hnsw.index'),
476
+ path.join(CWD, '.claude-flow', 'hnsw.index'),
477
+ ];
478
+ for (const p of hnswPaths) {
479
+ if (safeStat(p)) { hasHnsw = true; break; }
812
480
  }
813
-
814
- // Also check for vectors.json (simple vector store)
815
- const vectorsPath = path.join(process.cwd(), '.claude-flow', 'vectors.json');
816
- if (fs.existsSync(vectorsPath) && vectorCount === 0) {
817
- try {
818
- const data = JSON.parse(fs.readFileSync(vectorsPath, 'utf-8'));
819
- if (Array.isArray(data)) {
820
- vectorCount = data.length;
821
- } else if (data.vectors) {
822
- vectorCount = Object.keys(data.vectors).length;
823
- }
824
- } catch (e) {
825
- // Ignore
481
+ if (!hasHnsw) {
482
+ const memPkgPaths = [
483
+ path.join(CWD, 'v3', '@claude-flow', 'memory', 'dist'),
484
+ path.join(CWD, 'node_modules', '@claude-flow', 'memory'),
485
+ ];
486
+ for (const p of memPkgPaths) {
487
+ if (fs.existsSync(p)) { hasHnsw = true; break; }
826
488
  }
827
489
  }
828
490
 
829
491
  return { vectorCount, dbSizeKB: Math.floor(dbSizeKB), namespaces, hasHnsw };
830
492
  }
831
493
 
832
- // Get test statistics
494
+ // Test stats (count files only — NO reading file contents)
833
495
  function getTestStats() {
834
496
  let testFiles = 0;
835
- let testCases = 0;
836
-
837
- const testDirs = [
838
- path.join(process.cwd(), 'tests'),
839
- path.join(process.cwd(), 'test'),
840
- path.join(process.cwd(), '__tests__'),
841
- path.join(process.cwd(), 'src', '__tests__'),
842
- path.join(process.cwd(), 'v3', '__tests__'),
843
- ];
844
-
845
- // Recursively count test files
846
- function countTestFiles(dir, depth = 0) {
847
- if (depth > 3) return; // Limit recursion
848
- if (!fs.existsSync(dir)) return;
849
497
 
498
+ function countTestFiles(dir, depth) {
499
+ if (depth === undefined) depth = 0;
500
+ if (depth > 6) return;
850
501
  try {
502
+ if (!fs.existsSync(dir)) return;
851
503
  const entries = fs.readdirSync(dir, { withFileTypes: true });
852
504
  for (const entry of entries) {
853
505
  if (entry.isDirectory() && !entry.name.startsWith('.') && entry.name !== 'node_modules') {
854
506
  countTestFiles(path.join(dir, entry.name), depth + 1);
855
507
  } else if (entry.isFile()) {
856
- const name = entry.name;
857
- if (name.includes('.test.') || name.includes('.spec.') ||
858
- name.includes('_test.') || name.includes('_spec.') ||
859
- name.startsWith('test_') || name.startsWith('spec_')) {
508
+ const n = entry.name;
509
+ if (n.includes('.test.') || n.includes('.spec.') || n.includes('_test.') || n.includes('_spec.')) {
860
510
  testFiles++;
861
-
862
- // Try to estimate test cases from file
863
- try {
864
- const content = fs.readFileSync(path.join(dir, name), 'utf-8');
865
- // Count it(), test(), describe() patterns
866
- const itMatches = (content.match(/\\bit\\s*\\(/g) || []).length;
867
- const testMatches = (content.match(/\\btest\\s*\\(/g) || []).length;
868
- testCases += itMatches + testMatches;
869
- } catch (e) {
870
- // Estimate 3 tests per file if can't read
871
- testCases += 3;
872
- }
873
511
  }
874
512
  }
875
513
  }
876
- } catch (e) {
877
- // Ignore
878
- }
879
- }
880
-
881
- for (const dir of testDirs) {
882
- countTestFiles(dir);
514
+ } catch { /* ignore */ }
883
515
  }
884
516
 
885
- // Also check src directory for colocated tests
886
- const srcDir = path.join(process.cwd(), 'src');
887
- if (fs.existsSync(srcDir)) {
888
- countTestFiles(srcDir);
517
+ var testDirNames = ['tests', 'test', '__tests__', 'src', 'v3'];
518
+ for (var i = 0; i < testDirNames.length; i++) {
519
+ countTestFiles(path.join(CWD, testDirNames[i]));
889
520
  }
890
521
 
891
- return { testFiles, testCases };
522
+ return { testFiles, testCases: testFiles * 4 };
892
523
  }
893
524
 
894
- // Get integration status (MCP servers, external connections)
525
+ // Integration status (shared settings + file checks)
895
526
  function getIntegrationStatus() {
896
- let mcpServers = { total: 0, enabled: 0, names: [] };
897
- let hasDatabase = false;
898
- let hasCache = false;
899
- let hasApi = false;
900
-
901
- // Check for MCP servers in settings
902
- const settingsPaths = [
903
- path.join(process.cwd(), '.claude', 'settings.json'),
904
- path.join(process.cwd(), '.claude', 'settings.local.json'),
905
- ];
527
+ const mcpServers = { total: 0, enabled: 0 };
528
+ const settings = getSettings();
906
529
 
907
- for (const settingsPath of settingsPaths) {
908
- if (fs.existsSync(settingsPath)) {
909
- try {
910
- const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
911
-
912
- // Check mcpServers object
913
- if (settings.mcpServers && typeof settings.mcpServers === 'object') {
914
- const servers = Object.keys(settings.mcpServers);
915
- mcpServers.total = servers.length;
916
- mcpServers.names = servers;
917
-
918
- // Check enabledMcpjsonServers for enabled count
919
- if (settings.enabledMcpjsonServers && Array.isArray(settings.enabledMcpjsonServers)) {
920
- mcpServers.enabled = settings.enabledMcpjsonServers.filter(s => servers.includes(s)).length;
921
- } else {
922
- mcpServers.enabled = mcpServers.total; // Assume all enabled if not specified
923
- }
924
- }
925
- break;
926
- } catch (e) { /* ignore */ }
927
- }
530
+ if (settings && settings.mcpServers && typeof settings.mcpServers === 'object') {
531
+ const servers = Object.keys(settings.mcpServers);
532
+ mcpServers.total = servers.length;
533
+ mcpServers.enabled = settings.enabledMcpjsonServers
534
+ ? settings.enabledMcpjsonServers.filter(s => servers.includes(s)).length
535
+ : servers.length;
928
536
  }
929
537
 
930
- // Also check .mcp.json or mcp.json
931
- const mcpConfigPaths = [
932
- path.join(process.cwd(), '.mcp.json'),
933
- path.join(process.cwd(), 'mcp.json'),
934
- path.join(require('os').homedir(), '.claude', 'mcp.json'),
935
- ];
936
-
937
- for (const mcpPath of mcpConfigPaths) {
938
- if (fs.existsSync(mcpPath) && mcpServers.total === 0) {
939
- try {
940
- const config = JSON.parse(fs.readFileSync(mcpPath, 'utf-8'));
941
- if (config.mcpServers) {
942
- const servers = Object.keys(config.mcpServers);
943
- mcpServers.total = servers.length;
944
- mcpServers.names = servers;
945
- mcpServers.enabled = servers.length;
946
- }
947
- } catch (e) { /* ignore */ }
538
+ if (mcpServers.total === 0) {
539
+ const mcpConfig = readJSON(path.join(CWD, '.mcp.json'))
540
+ || readJSON(path.join(os.homedir(), '.claude', 'mcp.json'));
541
+ if (mcpConfig && mcpConfig.mcpServers) {
542
+ const s = Object.keys(mcpConfig.mcpServers);
543
+ mcpServers.total = s.length;
544
+ mcpServers.enabled = s.length;
948
545
  }
949
546
  }
950
547
 
951
- // Check for database (AgentDB, SQLite, etc.)
952
- const dbPaths = [
953
- path.join(process.cwd(), '.swarm', 'memory.db'),
954
- path.join(process.cwd(), '.claude-flow', 'memory.db'),
955
- path.join(process.cwd(), 'data', 'memory.db'),
956
- ];
957
- hasDatabase = dbPaths.some(p => fs.existsSync(p));
548
+ const hasDatabase = ['.swarm/memory.db', '.claude-flow/memory.db', 'data/memory.db']
549
+ .some(p => fs.existsSync(path.join(CWD, p)));
550
+ const hasApi = !!(process.env.ANTHROPIC_API_KEY || process.env.OPENAI_API_KEY);
958
551
 
959
- // Check for cache
960
- const cachePaths = [
961
- path.join(process.cwd(), '.claude-flow', 'cache'),
962
- path.join(process.cwd(), '.cache'),
963
- path.join(process.cwd(), 'node_modules', '.cache'),
964
- ];
965
- hasCache = cachePaths.some(p => fs.existsSync(p));
966
-
967
- // Check for API configuration (env vars or config)
968
- try {
969
- hasApi = !!(process.env.ANTHROPIC_API_KEY || process.env.OPENAI_API_KEY);
970
- } catch (e) { /* ignore */ }
971
-
972
- return { mcpServers, hasDatabase, hasCache, hasApi };
552
+ return { mcpServers, hasDatabase, hasApi };
973
553
  }
974
554
 
975
- // Get git status (uncommitted changes, untracked files) - cross-platform
976
- function getGitStatus() {
977
- let modified = 0;
978
- let untracked = 0;
979
- let staged = 0;
980
- let ahead = 0;
981
- let behind = 0;
982
- const isWindows = process.platform === 'win32';
983
-
984
- try {
985
- // Get modified and staged counts - works on all platforms
986
- const status = execSync('git status --porcelain', {
987
- encoding: 'utf-8',
988
- stdio: ['pipe', 'pipe', 'pipe'], // Suppress stderr
989
- timeout: 5000,
990
- });
991
- const lines = status.trim().split('\\n').filter(l => l);
992
- for (const line of lines) {
993
- const code = line.substring(0, 2);
994
- if (code.includes('M') || code.includes('D') || code.includes('R')) {
995
- if (code[0] !== ' ') staged++;
996
- if (code[1] !== ' ') modified++;
997
- }
998
- if (code.includes('?')) untracked++;
999
- if (code.includes('A')) staged++;
1000
- }
1001
-
1002
- // Get ahead/behind - may fail if no upstream
1003
- try {
1004
- const abStatus = execSync('git rev-list --left-right --count HEAD...@{upstream}', {
1005
- encoding: 'utf-8',
1006
- stdio: ['pipe', 'pipe', 'pipe'],
1007
- timeout: 5000,
1008
- });
1009
- const parts = abStatus.trim().split(/\\s+/);
1010
- ahead = parseInt(parts[0]) || 0;
1011
- behind = parseInt(parts[1]) || 0;
1012
- } catch (e) { /* no upstream or error - that's ok */ }
1013
-
1014
- } catch (e) {
1015
- // Not a git repo or git not installed - return zeros
1016
- }
1017
-
1018
- return { modified, untracked, staged, ahead, behind };
1019
- }
1020
-
1021
- // Get session statistics
555
+ // Session stats (pure file reads)
1022
556
  function getSessionStats() {
1023
- let sessionStart = null;
1024
- let duration = '';
1025
- let lastActivity = '';
1026
- let operationsCount = 0;
1027
-
1028
- // Check for session file
1029
- const sessionPaths = [
1030
- path.join(process.cwd(), '.claude-flow', 'session.json'),
1031
- path.join(process.cwd(), '.claude', 'session.json'),
1032
- ];
1033
-
1034
- for (const sessPath of sessionPaths) {
1035
- if (fs.existsSync(sessPath)) {
1036
- try {
1037
- const data = JSON.parse(fs.readFileSync(sessPath, 'utf-8'));
1038
- if (data.startTime) {
1039
- sessionStart = new Date(data.startTime);
1040
- const now = new Date();
1041
- const diffMs = now.getTime() - sessionStart.getTime();
1042
- const diffMins = Math.floor(diffMs / 60000);
1043
- if (diffMins < 60) {
1044
- duration = \`\${diffMins}m\`;
1045
- } else {
1046
- const hours = Math.floor(diffMins / 60);
1047
- const mins = diffMins % 60;
1048
- duration = \`\${hours}h\${mins}m\`;
1049
- }
1050
- }
1051
- if (data.lastActivity) {
1052
- const last = new Date(data.lastActivity);
1053
- const now = new Date();
1054
- const diffMs = now.getTime() - last.getTime();
1055
- const diffMins = Math.floor(diffMs / 60000);
1056
- if (diffMins < 1) lastActivity = 'now';
1057
- else if (diffMins < 60) lastActivity = \`\${diffMins}m ago\`;
1058
- else lastActivity = \`\${Math.floor(diffMins / 60)}h ago\`;
1059
- }
1060
- operationsCount = data.operationsCount || data.commandCount || 0;
1061
- break;
1062
- } catch (e) { /* ignore */ }
557
+ var sessionPaths = ['.claude-flow/session.json', '.claude/session.json'];
558
+ for (var i = 0; i < sessionPaths.length; i++) {
559
+ const data = readJSON(path.join(CWD, sessionPaths[i]));
560
+ if (data && data.startTime) {
561
+ const diffMs = Date.now() - new Date(data.startTime).getTime();
562
+ const mins = Math.floor(diffMs / 60000);
563
+ const duration = mins < 60 ? mins + 'm' : Math.floor(mins / 60) + 'h' + (mins % 60) + 'm';
564
+ return { duration: duration };
1063
565
  }
1064
566
  }
1065
-
1066
- // Fallback: check metrics for activity
1067
- if (!duration) {
1068
- const metricsPath = path.join(process.cwd(), '.claude-flow', 'metrics', 'activity.json');
1069
- if (fs.existsSync(metricsPath)) {
1070
- try {
1071
- const data = JSON.parse(fs.readFileSync(metricsPath, 'utf-8'));
1072
- operationsCount = data.totalOperations || 0;
1073
- } catch (e) { /* ignore */ }
1074
- }
1075
- }
1076
-
1077
- return { duration, lastActivity, operationsCount };
567
+ return { duration: '' };
1078
568
  }
1079
569
 
1080
- // Get trend indicator based on change
1081
- function getTrend(current, previous) {
1082
- if (previous === null || previous === undefined) return '';
1083
- if (current > previous) return \`\${c.brightGreen}↑\${c.reset}\`;
1084
- if (current < previous) return \`\${c.brightRed}↓\${c.reset}\`;
1085
- return \`\${c.dim}→\${c.reset}\`;
1086
- }
1087
-
1088
- // Store previous values for trends (persisted between calls)
1089
- let prevIntelligence = null;
1090
- try {
1091
- const trendPath = path.join(process.cwd(), '.claude-flow', '.trend-cache.json');
1092
- if (fs.existsSync(trendPath)) {
1093
- const data = JSON.parse(fs.readFileSync(trendPath, 'utf-8'));
1094
- prevIntelligence = data.intelligence;
1095
- }
1096
- } catch (e) { /* ignore */ }
570
+ // ─── Rendering ──────────────────────────────────────────────────
1097
571
 
1098
- // Generate progress bar
1099
572
  function progressBar(current, total) {
1100
573
  const width = 5;
1101
574
  const filled = Math.round((current / total) * width);
1102
- const empty = width - filled;
1103
- return '[' + '\\u25CF'.repeat(filled) + '\\u25CB'.repeat(empty) + ']';
575
+ return '[' + '\\u25CF'.repeat(filled) + '\\u25CB'.repeat(width - filled) + ']';
1104
576
  }
1105
577
 
1106
- // Generate full statusline
1107
578
  function generateStatusline() {
1108
- const user = getUserInfo();
579
+ const git = getGitInfo();
580
+ // Prefer model name from Claude Code stdin data, fallback to file-based detection
581
+ const modelName = getModelFromStdin() || getModelName();
582
+ const ctxInfo = getContextFromStdin();
583
+ const costInfo = getCostFromStdin();
1109
584
  const progress = getV3Progress();
1110
585
  const security = getSecurityStatus();
1111
586
  const swarm = getSwarmStatus();
@@ -1114,147 +589,122 @@ function generateStatusline() {
1114
589
  const hooks = getHooksStatus();
1115
590
  const agentdb = getAgentDBStats();
1116
591
  const tests = getTestStats();
1117
- const git = getGitStatus();
1118
592
  const session = getSessionStats();
1119
593
  const integration = getIntegrationStatus();
1120
594
  const lines = [];
1121
595
 
1122
- // Calculate intelligence trend
1123
- const intellTrend = getTrend(system.intelligencePct, prevIntelligence);
1124
-
1125
- // Save current values for next trend calculation
1126
- try {
1127
- const trendPath = path.join(process.cwd(), '.claude-flow', '.trend-cache.json');
1128
- const trendDir = path.dirname(trendPath);
1129
- if (!fs.existsSync(trendDir)) fs.mkdirSync(trendDir, { recursive: true });
1130
- fs.writeFileSync(trendPath, JSON.stringify({ intelligence: system.intelligencePct, timestamp: Date.now() }));
1131
- } catch (e) { /* ignore */ }
1132
-
1133
- // Header Line with git changes indicator
1134
- let header = \`\${c.bold}\${c.brightPurple}▊ KynjalFlow\${c.reset}\`;
1135
- header += \`\${swarm.coordinationActive ? c.brightCyan : c.dim}● \${c.brightCyan}\${user.name}\${c.reset}\`;
1136
- if (user.gitBranch) {
1137
- header += \` \${c.dim}│\${c.reset} \${c.brightBlue}⎇ \${user.gitBranch}\${c.reset}\`;
1138
- // Add git changes indicator
1139
- const gitChanges = git.modified + git.staged + git.untracked;
1140
- if (gitChanges > 0) {
1141
- let gitIndicator = '';
1142
- if (git.staged > 0) gitIndicator += \`\${c.brightGreen}+\${git.staged}\${c.reset}\`;
1143
- if (git.modified > 0) gitIndicator += \`\${c.brightYellow}~\${git.modified}\${c.reset}\`;
1144
- if (git.untracked > 0) gitIndicator += \`\${c.dim}?\${git.untracked}\${c.reset}\`;
1145
- header += \` \${gitIndicator}\`;
1146
- }
1147
- // Add ahead/behind indicator
1148
- if (git.ahead > 0 || git.behind > 0) {
1149
- if (git.ahead > 0) header += \` \${c.brightGreen}↑\${git.ahead}\${c.reset}\`;
1150
- if (git.behind > 0) header += \` \${c.brightRed}↓\${git.behind}\${c.reset}\`;
1151
- }
1152
- }
1153
- header += \` \${c.dim}│\${c.reset} \${c.purple}\${user.modelName}\${c.reset}\`;
1154
- // Add session duration if available
1155
- if (session.duration) {
1156
- header += \` \${c.dim}│\${c.reset} \${c.cyan}⏱ \${session.duration}\${c.reset}\`;
596
+ // Header
597
+ let header = c.bold + c.brightPurple + '\\u258A RuFlo V3.5 ' + c.reset;
598
+ header += (swarm.coordinationActive ? c.brightCyan : c.dim) + '\\u25CF ' + c.brightCyan + git.name + c.reset;
599
+ if (git.gitBranch) {
600
+ header += ' ' + c.dim + '\\u2502' + c.reset + ' ' + c.brightBlue + '\\u23C7 ' + git.gitBranch + c.reset;
601
+ const changes = git.modified + git.staged + git.untracked;
602
+ if (changes > 0) {
603
+ let ind = '';
604
+ if (git.staged > 0) ind += c.brightGreen + '+' + git.staged + c.reset;
605
+ if (git.modified > 0) ind += c.brightYellow + '~' + git.modified + c.reset;
606
+ if (git.untracked > 0) ind += c.dim + '?' + git.untracked + c.reset;
607
+ header += ' ' + ind;
608
+ }
609
+ if (git.ahead > 0) header += ' ' + c.brightGreen + '\\u2191' + git.ahead + c.reset;
610
+ if (git.behind > 0) header += ' ' + c.brightRed + '\\u2193' + git.behind + c.reset;
611
+ }
612
+ header += ' ' + c.dim + '\\u2502' + c.reset + ' ' + c.purple + modelName + c.reset;
613
+ // Show session duration from Claude Code stdin if available, else from local files
614
+ const duration = costInfo ? costInfo.duration : session.duration;
615
+ if (duration) header += ' ' + c.dim + '\\u2502' + c.reset + ' ' + c.cyan + '\\u23F1 ' + duration + c.reset;
616
+ // Show context usage from Claude Code stdin if available
617
+ if (ctxInfo && ctxInfo.usedPct > 0) {
618
+ const ctxColor = ctxInfo.usedPct >= 90 ? c.brightRed : ctxInfo.usedPct >= 70 ? c.brightYellow : c.brightGreen;
619
+ header += ' ' + c.dim + '\\u2502' + c.reset + ' ' + ctxColor + '\\u25CF ' + ctxInfo.usedPct + '% ctx' + c.reset;
620
+ }
621
+ // Show cost from Claude Code stdin if available
622
+ if (costInfo && costInfo.costUsd > 0) {
623
+ header += ' ' + c.dim + '\\u2502' + c.reset + ' ' + c.brightYellow + '$' + costInfo.costUsd.toFixed(2) + c.reset;
1157
624
  }
1158
625
  lines.push(header);
1159
626
 
1160
627
  // Separator
1161
- lines.push(\`\${c.dim}─────────────────────────────────────────────────────\${c.reset}\`);
628
+ lines.push(c.dim + '\\u2500'.repeat(53) + c.reset);
1162
629
 
1163
- // Line 1: DDD Domain Progress with dynamic performance indicator
630
+ // Line 1: DDD Domains
1164
631
  const domainsColor = progress.domainsCompleted >= 3 ? c.brightGreen : progress.domainsCompleted > 0 ? c.yellow : c.red;
1165
- // Show HNSW speedup if enabled, otherwise show patterns learned
1166
- let perfIndicator = '';
632
+ let perfIndicator;
1167
633
  if (agentdb.hasHnsw && agentdb.vectorCount > 0) {
1168
- // HNSW enabled: show estimated speedup (150x-12500x based on vector count)
1169
634
  const speedup = agentdb.vectorCount > 10000 ? '12500x' : agentdb.vectorCount > 1000 ? '150x' : '10x';
1170
- perfIndicator = \`\${c.brightGreen}⚡ HNSW \${speedup}\${c.reset}\`;
635
+ perfIndicator = c.brightGreen + '\\u26A1 HNSW ' + speedup + c.reset;
1171
636
  } else if (progress.patternsLearned > 0) {
1172
- // Show patterns learned
1173
- const patternsK = progress.patternsLearned >= 1000
1174
- ? \`\${(progress.patternsLearned / 1000).toFixed(1)}k\`
1175
- : String(progress.patternsLearned);
1176
- perfIndicator = \`\${c.brightYellow}📚 \${patternsK} patterns\${c.reset}\`;
637
+ const pk = progress.patternsLearned >= 1000 ? (progress.patternsLearned / 1000).toFixed(1) + 'k' : String(progress.patternsLearned);
638
+ perfIndicator = c.brightYellow + '\\uD83D\\uDCDA ' + pk + ' patterns' + c.reset;
1177
639
  } else {
1178
- // New project: show target
1179
- perfIndicator = \`\${c.dim}⚡ target: 150x-12500x\${c.reset}\`;
640
+ perfIndicator = c.dim + '\\u26A1 target: 150x-12500x' + c.reset;
1180
641
  }
1181
642
  lines.push(
1182
- \`\${c.brightCyan}🏗️ DDD Domains\${c.reset} \${progressBar(progress.domainsCompleted, progress.totalDomains)} \` +
1183
- \`\${domainsColor}\${progress.domainsCompleted}\${c.reset}/\${c.brightWhite}\${progress.totalDomains}\${c.reset} \` +
1184
- perfIndicator
643
+ c.brightCyan + '\\uD83C\\uDFD7\\uFE0F DDD Domains' + c.reset + ' ' + progressBar(progress.domainsCompleted, progress.totalDomains) + ' ' +
644
+ domainsColor + progress.domainsCompleted + c.reset + '/' + c.brightWhite + progress.totalDomains + c.reset + ' ' + perfIndicator
1185
645
  );
1186
646
 
1187
- // Line 2: Swarm + Hooks + CVE + Memory + Context + Intelligence
1188
- const swarmIndicator = swarm.coordinationActive ? \`\${c.brightGreen}◉\${c.reset}\` : \`\${c.dim}○\${c.reset}\`;
647
+ // Line 2: Swarm + Hooks + CVE + Memory + Intelligence
648
+ const swarmInd = swarm.coordinationActive ? c.brightGreen + '\\u25C9' + c.reset : c.dim + '\\u25CB' + c.reset;
1189
649
  const agentsColor = swarm.activeAgents > 0 ? c.brightGreen : c.red;
1190
- let securityIcon = security.status === 'CLEAN' ? '🟢' : security.status === 'IN_PROGRESS' ? '🟡' : '🔴';
1191
- let securityColor = security.status === 'CLEAN' ? c.brightGreen : security.status === 'IN_PROGRESS' ? c.brightYellow : c.brightRed;
650
+ const secIcon = security.status === 'CLEAN' ? '\\uD83D\\uDFE2' : (security.status === 'IN_PROGRESS' || security.status === 'STALE') ? '\\uD83D\\uDFE1' : (security.status === 'NONE' ? '\\u26AA' : '\\uD83D\\uDD34');
651
+ const secColor = security.status === 'CLEAN' ? c.brightGreen : (security.status === 'IN_PROGRESS' || security.status === 'STALE') ? c.brightYellow : (security.status === 'NONE' ? c.dim : c.brightRed);
1192
652
  const hooksColor = hooks.enabled > 0 ? c.brightGreen : c.dim;
653
+ const intellColor = system.intelligencePct >= 80 ? c.brightGreen : system.intelligencePct >= 40 ? c.brightYellow : c.dim;
1193
654
 
1194
655
  lines.push(
1195
- \`\${c.brightYellow}🤖 Swarm\${c.reset} \${swarmIndicator} [\${agentsColor}\${String(swarm.activeAgents).padStart(2)}\${c.reset}/\${c.brightWhite}\${swarm.maxAgents}\${c.reset}] \` +
1196
- \`\${c.brightPurple}👥 \${system.subAgents}\${c.reset} \` +
1197
- \`\${c.brightBlue}🪝 \${hooksColor}\${hooks.enabled}\${c.reset}/\${c.brightWhite}\${hooks.total}\${c.reset} \` +
1198
- \`\${securityIcon} \${securityColor}CVE \${security.cvesFixed}\${c.reset}/\${c.brightWhite}\${security.totalCves}\${c.reset} \` +
1199
- \`\${c.brightCyan}💾 \${system.memoryMB}MB\${c.reset} \` +
1200
- \`\${system.intelligencePct >= 80 ? c.brightGreen : system.intelligencePct >= 40 ? c.brightYellow : c.dim}🧠 \${String(system.intelligencePct).padStart(3)}%\${intellTrend}\${c.reset}\`
656
+ c.brightYellow + '\\uD83E\\uDD16 Swarm' + c.reset + ' ' + swarmInd + ' [' + agentsColor + String(swarm.activeAgents).padStart(2) + c.reset + '/' + c.brightWhite + swarm.maxAgents + c.reset + '] ' +
657
+ c.brightPurple + '\\uD83D\\uDC65 ' + system.subAgents + c.reset + ' ' +
658
+ c.brightBlue + '\\uD83E\\uDE9D ' + hooksColor + hooks.enabled + c.reset + '/' + c.brightWhite + hooks.total + c.reset + ' ' +
659
+ secIcon + ' ' + secColor + 'CVE ' + security.cvesFixed + c.reset + '/' + c.brightWhite + security.totalCves + c.reset + ' ' +
660
+ c.brightCyan + '\\uD83D\\uDCBE ' + system.memoryMB + 'MB' + c.reset + ' ' +
661
+ intellColor + '\\uD83E\\uDDE0 ' + String(system.intelligencePct).padStart(3) + '%' + c.reset
1201
662
  );
1202
663
 
1203
- // Line 3: Architecture status with ADRs, AgentDB, Tests
664
+ // Line 3: Architecture
1204
665
  const dddColor = progress.dddProgress >= 50 ? c.brightGreen : progress.dddProgress > 0 ? c.yellow : c.red;
1205
666
  const adrColor = adrs.count > 0 ? (adrs.implemented === adrs.count ? c.brightGreen : c.yellow) : c.dim;
1206
- const vectorColor = agentdb.vectorCount > 0 ? c.brightGreen : c.dim;
1207
- const testColor = tests.testFiles > 0 ? c.brightGreen : c.dim;
1208
-
1209
- // Show ADR compliance % if from real data, otherwise show count
1210
- const adrDisplay = adrs.compliance > 0
1211
- ? \`\${adrColor}●\${adrs.compliance}%\${c.reset}\`
1212
- : \`\${adrColor}●\${adrs.implemented}/\${adrs.count}\${c.reset}\`;
667
+ const adrDisplay = adrs.compliance > 0 ? adrColor + '\\u25CF' + adrs.compliance + '%' + c.reset : adrColor + '\\u25CF' + adrs.implemented + '/' + adrs.count + c.reset;
1213
668
 
1214
669
  lines.push(
1215
- \`\${c.brightPurple}🔧 Architecture\${c.reset} \` +
1216
- \`\${c.cyan}ADRs\${c.reset} \${adrDisplay} \${c.dim}│\${c.reset} \` +
1217
- \`\${c.cyan}DDD\${c.reset} \${dddColor}●\${String(progress.dddProgress).padStart(3)}%\${c.reset} \${c.dim}│\${c.reset} \` +
1218
- \`\${c.cyan}Security\${c.reset} \${securityColor}●\${security.status}\${c.reset}\`
670
+ c.brightPurple + '\\uD83D\\uDD27 Architecture' + c.reset + ' ' +
671
+ c.cyan + 'ADRs' + c.reset + ' ' + adrDisplay + ' ' + c.dim + '\\u2502' + c.reset + ' ' +
672
+ c.cyan + 'DDD' + c.reset + ' ' + dddColor + '\\u25CF' + String(progress.dddProgress).padStart(3) + '%' + c.reset + ' ' + c.dim + '\\u2502' + c.reset + ' ' +
673
+ c.cyan + 'Security' + c.reset + ' ' + secColor + '\\u25CF' + security.status + c.reset
1219
674
  );
1220
675
 
1221
- // Line 4: Memory, Vectors, Tests
1222
- const hnswIndicator = agentdb.hasHnsw ? \`\${c.brightGreen}⚡\${c.reset}\` : '';
1223
- const sizeDisplay = agentdb.dbSizeKB >= 1024
1224
- ? \`\${(agentdb.dbSizeKB / 1024).toFixed(1)}MB\`
1225
- : \`\${agentdb.dbSizeKB}KB\`;
1226
- // Build integration status string
1227
- let integrationStr = '';
676
+ // Line 4: AgentDB, Tests, Integration
677
+ const hnswInd = agentdb.hasHnsw ? c.brightGreen + '\\u26A1' + c.reset : '';
678
+ const sizeDisp = agentdb.dbSizeKB >= 1024 ? (agentdb.dbSizeKB / 1024).toFixed(1) + 'MB' : agentdb.dbSizeKB + 'KB';
679
+ const vectorColor = agentdb.vectorCount > 0 ? c.brightGreen : c.dim;
680
+ const testColor = tests.testFiles > 0 ? c.brightGreen : c.dim;
681
+
682
+ let integStr = '';
1228
683
  if (integration.mcpServers.total > 0) {
1229
- const mcpColor = integration.mcpServers.enabled === integration.mcpServers.total ? c.brightGreen :
1230
- integration.mcpServers.enabled > 0 ? c.brightYellow : c.red;
1231
- integrationStr += \`\${c.cyan}MCP\${c.reset} \${mcpColor}●\${integration.mcpServers.enabled}/\${integration.mcpServers.total}\${c.reset}\`;
1232
- }
1233
- if (integration.hasDatabase) {
1234
- integrationStr += (integrationStr ? ' ' : '') + \`\${c.brightGreen}◆\${c.reset}DB\`;
1235
- }
1236
- if (integration.hasApi) {
1237
- integrationStr += (integrationStr ? ' ' : '') + \`\${c.brightGreen}◆\${c.reset}API\`;
1238
- }
1239
- if (!integrationStr) {
1240
- integrationStr = \`\${c.dim}●none\${c.reset}\`;
684
+ const mcpCol = integration.mcpServers.enabled === integration.mcpServers.total ? c.brightGreen :
685
+ integration.mcpServers.enabled > 0 ? c.brightYellow : c.red;
686
+ integStr += c.cyan + 'MCP' + c.reset + ' ' + mcpCol + '\\u25CF' + integration.mcpServers.enabled + '/' + integration.mcpServers.total + c.reset;
1241
687
  }
688
+ if (integration.hasDatabase) integStr += (integStr ? ' ' : '') + c.brightGreen + '\\u25C6' + c.reset + 'DB';
689
+ if (integration.hasApi) integStr += (integStr ? ' ' : '') + c.brightGreen + '\\u25C6' + c.reset + 'API';
690
+ if (!integStr) integStr = c.dim + '\\u25CF none' + c.reset;
1242
691
 
1243
692
  lines.push(
1244
- \`\${c.brightCyan}📊 AgentDB\${c.reset} \` +
1245
- \`\${c.cyan}Vectors\${c.reset} \${vectorColor}●\${agentdb.vectorCount}\${hnswIndicator}\${c.reset} \${c.dim}│\${c.reset} \` +
1246
- \`\${c.cyan}Size\${c.reset} \${c.brightWhite}\${sizeDisplay}\${c.reset} \${c.dim}│\${c.reset} \` +
1247
- \`\${c.cyan}Tests\${c.reset} \${testColor}●\${tests.testFiles}\${c.reset} \${c.dim}(\${tests.testCases} cases)\${c.reset} \${c.dim}│\${c.reset} \` +
1248
- integrationStr
693
+ c.brightCyan + '\\uD83D\\uDCCA AgentDB' + c.reset + ' ' +
694
+ c.cyan + 'Vectors' + c.reset + ' ' + vectorColor + '\\u25CF' + agentdb.vectorCount + hnswInd + c.reset + ' ' + c.dim + '\\u2502' + c.reset + ' ' +
695
+ c.cyan + 'Size' + c.reset + ' ' + c.brightWhite + sizeDisp + c.reset + ' ' + c.dim + '\\u2502' + c.reset + ' ' +
696
+ c.cyan + 'Tests' + c.reset + ' ' + testColor + '\\u25CF' + tests.testFiles + c.reset + ' ' + c.dim + '(~' + tests.testCases + ' cases)' + c.reset + ' ' + c.dim + '\\u2502' + c.reset + ' ' +
697
+ integStr
1249
698
  );
1250
699
 
1251
700
  return lines.join('\\n');
1252
701
  }
1253
702
 
1254
- // Generate JSON data
703
+ // JSON output
1255
704
  function generateJSON() {
705
+ const git = getGitInfo();
1256
706
  return {
1257
- user: getUserInfo(),
707
+ user: { name: git.name, gitBranch: git.gitBranch, modelName: getModelName() },
1258
708
  v3Progress: getV3Progress(),
1259
709
  security: getSecurityStatus(),
1260
710
  swarm: getSwarmStatus(),
@@ -1263,16 +713,81 @@ function generateJSON() {
1263
713
  hooks: getHooksStatus(),
1264
714
  agentdb: getAgentDBStats(),
1265
715
  tests: getTestStats(),
1266
- performance: {
1267
- flashAttentionTarget: '2.49x-7.47x',
1268
- searchImprovement: '150x-12,500x',
1269
- memoryReduction: '50-75%',
1270
- },
716
+ git: { modified: git.modified, untracked: git.untracked, staged: git.staged, ahead: git.ahead, behind: git.behind },
1271
717
  lastUpdated: new Date().toISOString(),
1272
718
  };
1273
719
  }
1274
720
 
1275
- // Main
721
+ // ─── Stdin reader (Claude Code pipes session JSON) ──────────────
722
+
723
+ // Claude Code sends session JSON via stdin (model, context, cost, etc.)
724
+ // Read it synchronously so the script works both:
725
+ // 1. When invoked by Claude Code (stdin has JSON)
726
+ // 2. When invoked manually from terminal (stdin is empty/tty)
727
+ let _stdinData = null;
728
+ function getStdinData() {
729
+ if (_stdinData !== undefined && _stdinData !== null) return _stdinData;
730
+ try {
731
+ // Check if stdin is a TTY (manual run) — skip reading
732
+ if (process.stdin.isTTY) { _stdinData = null; return null; }
733
+ // Read stdin synchronously via fd 0
734
+ const chunks = [];
735
+ const buf = Buffer.alloc(4096);
736
+ let bytesRead;
737
+ try {
738
+ while ((bytesRead = fs.readSync(0, buf, 0, buf.length, null)) > 0) {
739
+ chunks.push(buf.slice(0, bytesRead));
740
+ }
741
+ } catch { /* EOF or read error */ }
742
+ const raw = Buffer.concat(chunks).toString('utf-8').trim();
743
+ if (raw && raw.startsWith('{')) {
744
+ _stdinData = JSON.parse(raw);
745
+ } else {
746
+ _stdinData = null;
747
+ }
748
+ } catch {
749
+ _stdinData = null;
750
+ }
751
+ return _stdinData;
752
+ }
753
+
754
+ // Override model detection to prefer stdin data from Claude Code
755
+ function getModelFromStdin() {
756
+ const data = getStdinData();
757
+ if (data && data.model && data.model.display_name) return data.model.display_name;
758
+ return null;
759
+ }
760
+
761
+ // Get context window info from Claude Code session
762
+ function getContextFromStdin() {
763
+ const data = getStdinData();
764
+ if (data && data.context_window) {
765
+ return {
766
+ usedPct: Math.floor(data.context_window.used_percentage || 0),
767
+ remainingPct: Math.floor(data.context_window.remaining_percentage || 100),
768
+ };
769
+ }
770
+ return null;
771
+ }
772
+
773
+ // Get cost info from Claude Code session
774
+ function getCostFromStdin() {
775
+ const data = getStdinData();
776
+ if (data && data.cost) {
777
+ const durationMs = data.cost.total_duration_ms || 0;
778
+ const mins = Math.floor(durationMs / 60000);
779
+ const secs = Math.floor((durationMs % 60000) / 1000);
780
+ return {
781
+ costUsd: data.cost.total_cost_usd || 0,
782
+ duration: mins > 0 ? mins + 'm' + secs + 's' : secs + 's',
783
+ linesAdded: data.cost.total_lines_added || 0,
784
+ linesRemoved: data.cost.total_lines_removed || 0,
785
+ };
786
+ }
787
+ return null;
788
+ }
789
+
790
+ // ─── Main ───────────────────────────────────────────────────────
1276
791
  if (process.argv.includes('--json')) {
1277
792
  console.log(JSON.stringify(generateJSON(), null, 2));
1278
793
  } else if (process.argv.includes('--compact')) {
@@ -1287,10 +802,11 @@ if (process.argv.includes('--json')) {
1287
802
  */
1288
803
  export function generateStatuslineHook(options) {
1289
804
  if (!options.statusline.enabled) {
1290
- return '# Statusline disabled';
805
+ return '#!/bin/bash\n# Statusline disabled\n';
1291
806
  }
1292
- return `# KynjalFlow V3 Statusline Hook
1293
- # Add to your shell RC file (.bashrc, .zshrc, etc.)
807
+ return `#!/bin/bash
808
+ # RuFlo V3 Statusline Hook
809
+ # Source this in your .bashrc/.zshrc for terminal statusline
1294
810
 
1295
811
  # Function to get statusline
1296
812
  claude_flow_statusline() {
@@ -1300,16 +816,18 @@ claude_flow_statusline() {
1300
816
  fi
1301
817
  }
1302
818
 
1303
- # For bash PS1
819
+ # Bash: Add to PS1
1304
820
  # export PS1='$(claude_flow_statusline) \\n\\$ '
1305
821
 
1306
- # For zsh RPROMPT
822
+ # Zsh: Add to RPROMPT
1307
823
  # export RPROMPT='$(claude_flow_statusline)'
1308
824
 
1309
- # For starship (add to starship.toml)
1310
- # [custom.claude_flow]
1311
- # command = "node .claude/helpers/statusline.cjs 2>/dev/null"
1312
- # when = "test -f .claude/helpers/statusline.cjs"
825
+ # Claude Code: Add to .claude/settings.json
826
+ # "statusLine": {
827
+ # "type": "command",
828
+ # "command": "node .claude/helpers/statusline.cjs 2>/dev/null"
829
+ # "when": "test -f .claude/helpers/statusline.cjs"
830
+ # }
1313
831
  `;
1314
832
  }
1315
833
  //# sourceMappingURL=statusline-generator.js.map