maestro-flow 0.5.34 → 0.5.36

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 (325) hide show
  1. package/.agents/agents/cli-explore-agent.md +71 -68
  2. package/.agents/agents/workflow-analyzer.md +1 -0
  3. package/.agents/agents/workflow-collab-planner.md +1 -0
  4. package/.agents/agents/workflow-debugger.md +1 -0
  5. package/.agents/agents/workflow-executor.md +1 -0
  6. package/.agents/agents/workflow-planner.md +2 -0
  7. package/.agents/skills/domain-add/SKILL.md +71 -0
  8. package/.agents/skills/maestro/SKILL.md +7 -6
  9. package/.agents/skills/maestro-amend/SKILL.md +0 -2
  10. package/.agents/skills/maestro-analyze/SKILL.md +9 -0
  11. package/.agents/skills/maestro-brainstorm/SKILL.md +10 -1
  12. package/.agents/skills/maestro-collab/SKILL.md +14 -1
  13. package/.agents/skills/maestro-help/SKILL.md +1 -1
  14. package/.agents/skills/maestro-help/index/catalog.json +0 -1
  15. package/.agents/skills/maestro-help/phases/01-parse-intent.md +1 -1
  16. package/.agents/skills/maestro-next/SKILL.md +15 -4
  17. package/.agents/skills/maestro-overlay/SKILL.md +4 -3
  18. package/.agents/skills/maestro-plan/SKILL.md +15 -4
  19. package/.agents/skills/maestro-ralph/SKILL.md +135 -31
  20. package/.agents/skills/maestro-ralph-execute/SKILL.md +51 -10
  21. package/.agents/skills/maestro-tools-register/SKILL.md +0 -6
  22. package/.agents/skills/maestro-ui-codify/SKILL.md +0 -2
  23. package/.agents/skills/manage-kg-extractors/SKILL.md +0 -2
  24. package/.agents/skills/quality-test/SKILL.md +25 -1
  25. package/.agents/skills/spec-load/SKILL.md +0 -1
  26. package/.agents/skills/spec-setup/SKILL.md +0 -1
  27. package/.agents/skills/team-lifecycle-v4/roles/analyst/role.md +13 -7
  28. package/.agents/skills/team-quality-assurance/roles/scout/role.md +15 -11
  29. package/.agents/skills/team-tech-debt/roles/scanner/role.md +1 -1
  30. package/.agents/skills/team-ultra-analyze/roles/explorer/role.md +23 -16
  31. package/.agy/agents/cli-explore-agent.md +71 -68
  32. package/.agy/agents/workflow-analyzer.md +1 -0
  33. package/.agy/agents/workflow-collab-planner.md +1 -0
  34. package/.agy/agents/workflow-debugger.md +1 -0
  35. package/.agy/agents/workflow-executor.md +1 -0
  36. package/.agy/agents/workflow-planner.md +2 -0
  37. package/.agy/skills/domain-add/SKILL.md +68 -0
  38. package/.agy/skills/maestro/SKILL.md +7 -6
  39. package/.agy/skills/maestro-amend/SKILL.md +0 -2
  40. package/.agy/skills/maestro-analyze/SKILL.md +9 -0
  41. package/.agy/skills/maestro-brainstorm/SKILL.md +10 -1
  42. package/.agy/skills/maestro-collab/SKILL.md +14 -1
  43. package/.agy/skills/maestro-help/SKILL.md +1 -1
  44. package/.agy/skills/maestro-help/index/catalog.json +0 -1
  45. package/.agy/skills/maestro-help/phases/01-parse-intent.md +1 -1
  46. package/.agy/skills/maestro-next/SKILL.md +15 -4
  47. package/.agy/skills/maestro-overlay/SKILL.md +4 -3
  48. package/.agy/skills/maestro-plan/SKILL.md +15 -4
  49. package/.agy/skills/maestro-ralph/SKILL.md +135 -31
  50. package/.agy/skills/maestro-ralph-execute/SKILL.md +51 -10
  51. package/.agy/skills/maestro-tools-register/SKILL.md +0 -6
  52. package/.agy/skills/maestro-ui-codify/SKILL.md +0 -2
  53. package/.agy/skills/manage-kg-extractors/SKILL.md +0 -2
  54. package/.agy/skills/quality-test/SKILL.md +25 -1
  55. package/.agy/skills/spec-load/SKILL.md +0 -1
  56. package/.agy/skills/spec-setup/SKILL.md +0 -1
  57. package/.agy/skills/team-lifecycle-v4/roles/analyst/role.md +13 -7
  58. package/.agy/skills/team-quality-assurance/roles/scout/role.md +15 -11
  59. package/.agy/skills/team-tech-debt/roles/scanner/role.md +1 -1
  60. package/.agy/skills/team-ultra-analyze/roles/explorer/role.md +23 -16
  61. package/.claude/agents/cli-explore-agent.md +71 -68
  62. package/.claude/agents/workflow-analyzer.md +1 -0
  63. package/.claude/agents/workflow-collab-planner.md +1 -0
  64. package/.claude/agents/workflow-debugger.md +1 -0
  65. package/.claude/agents/workflow-executor.md +1 -0
  66. package/.claude/agents/workflow-planner.md +2 -0
  67. package/.claude/commands/domain-add.md +69 -0
  68. package/.claude/commands/maestro-amend.md +0 -2
  69. package/.claude/commands/maestro-analyze.md +9 -0
  70. package/.claude/commands/maestro-brainstorm.md +10 -1
  71. package/.claude/commands/maestro-collab.md +14 -1
  72. package/.claude/commands/maestro-next.md +15 -4
  73. package/.claude/commands/maestro-overlay.md +4 -3
  74. package/.claude/commands/maestro-plan.md +15 -4
  75. package/.claude/commands/maestro-ralph-execute.md +51 -10
  76. package/.claude/commands/maestro-ralph.md +135 -31
  77. package/.claude/commands/maestro-tools-register.md +0 -6
  78. package/.claude/commands/maestro-ui-codify.md +0 -2
  79. package/.claude/commands/maestro.md +7 -6
  80. package/.claude/commands/manage-kg-extractors.md +0 -2
  81. package/.claude/commands/quality-test.md +25 -1
  82. package/.claude/commands/spec-load.md +0 -1
  83. package/.claude/commands/spec-setup.md +0 -1
  84. package/.claude/skills/maestro-help/SKILL.md +1 -1
  85. package/.claude/skills/maestro-help/index/catalog.json +0 -1
  86. package/.claude/skills/maestro-help/phases/01-parse-intent.md +1 -1
  87. package/.claude/skills/team-lifecycle-v4/roles/analyst/role.md +13 -7
  88. package/.claude/skills/team-quality-assurance/roles/scout/role.md +15 -11
  89. package/.claude/skills/team-tech-debt/roles/scanner/role.md +1 -1
  90. package/.claude/skills/team-ultra-analyze/roles/explorer/role.md +23 -16
  91. package/.codex/skills/domain-add/SKILL.md +79 -0
  92. package/.codex/skills/domain-discover/SKILL.md +43 -0
  93. package/.codex/skills/domain-list/SKILL.md +38 -0
  94. package/.codex/skills/learn-follow/SKILL.md +0 -3
  95. package/.codex/skills/learn-investigate/SKILL.md +0 -3
  96. package/.codex/skills/learn-retro/SKILL.md +0 -2
  97. package/.codex/skills/maestro/SKILL.md +34 -13
  98. package/.codex/skills/maestro-amend/SKILL.md +0 -2
  99. package/.codex/skills/maestro-analyze/SKILL.md +36 -6
  100. package/.codex/skills/maestro-blueprint/SKILL.md +0 -4
  101. package/.codex/skills/maestro-brainstorm/SKILL.md +46 -6
  102. package/.codex/skills/maestro-collab/SKILL.md +14 -1
  103. package/.codex/skills/maestro-companion/SKILL.md +0 -2
  104. package/.codex/skills/maestro-execute/SKILL.md +25 -3
  105. package/.codex/skills/maestro-fork/SKILL.md +0 -2
  106. package/.codex/skills/maestro-grill/SKILL.md +9 -1
  107. package/.codex/skills/maestro-next/SKILL.md +12 -5
  108. package/.codex/skills/maestro-plan/SKILL.md +43 -5
  109. package/.codex/skills/maestro-ralph/SKILL.md +10 -3
  110. package/.codex/skills/maestro-ralph-execute/SKILL.md +51 -10
  111. package/.codex/skills/maestro-roadmap/SKILL.md +30 -3
  112. package/.codex/skills/manage-wiki/SKILL.md +0 -2
  113. package/.codex/skills/odyssey-debug/SKILL.md +0 -2
  114. package/.codex/skills/odyssey-improve/SKILL.md +0 -6
  115. package/.codex/skills/odyssey-review-test-fix/SKILL.md +0 -3
  116. package/.codex/skills/odyssey-ui/SKILL.md +0 -3
  117. package/.codex/skills/quality-review/SKILL.md +9 -0
  118. package/.codex/skills/spec-setup/SKILL.md +0 -2
  119. package/.codex/skills/team-coordinate/SKILL.md +0 -3
  120. package/.codex/skills/team-lifecycle-v4/instructions/agent-instruction.md +0 -2
  121. package/README.md +28 -5
  122. package/README.zh-CN.md +28 -5
  123. package/chains/_intent-map.json +36 -0
  124. package/chains/blueprint-driven.json +49 -0
  125. package/chains/grill-driven.json +51 -0
  126. package/dashboard/dist-server/dashboard/src/server/agents/adapter-factory.js +4 -0
  127. package/dashboard/dist-server/dashboard/src/server/agents/adapter-factory.js.map +1 -1
  128. package/dashboard/dist-server/dashboard/src/server/agents/api-explore-adapter.d.ts +6 -0
  129. package/dashboard/dist-server/dashboard/src/server/agents/api-explore-adapter.js +36 -0
  130. package/dashboard/dist-server/dashboard/src/server/agents/api-explore-adapter.js.map +1 -0
  131. package/dashboard/dist-server/dashboard/src/server/agents/stream-json-adapter.d.ts +1 -1
  132. package/dashboard/dist-server/dashboard/src/server/agents/stream-json-adapter.js.map +1 -1
  133. package/dashboard/dist-server/dashboard/src/server/wiki/embedding.d.ts +82 -0
  134. package/dashboard/dist-server/dashboard/src/server/wiki/embedding.js +444 -0
  135. package/dashboard/dist-server/dashboard/src/server/wiki/embedding.js.map +1 -0
  136. package/dashboard/dist-server/dashboard/src/server/wiki/search.d.ts +47 -1
  137. package/dashboard/dist-server/dashboard/src/server/wiki/search.js +426 -19
  138. package/dashboard/dist-server/dashboard/src/server/wiki/search.js.map +1 -1
  139. package/dashboard/dist-server/dashboard/src/server/wiki/stress.test.js +16 -0
  140. package/dashboard/dist-server/dashboard/src/server/wiki/stress.test.js.map +1 -1
  141. package/dashboard/dist-server/dashboard/src/server/wiki/virtual-wiki-adapters.d.ts +11 -0
  142. package/dashboard/dist-server/dashboard/src/server/wiki/virtual-wiki-adapters.js +492 -1
  143. package/dashboard/dist-server/dashboard/src/server/wiki/virtual-wiki-adapters.js.map +1 -1
  144. package/dashboard/dist-server/dashboard/src/server/wiki/wiki-indexer.d.ts +18 -0
  145. package/dashboard/dist-server/dashboard/src/server/wiki/wiki-indexer.js +192 -9
  146. package/dashboard/dist-server/dashboard/src/server/wiki/wiki-indexer.js.map +1 -1
  147. package/dashboard/dist-server/dashboard/src/server/wiki/wiki-indexer.test.js +42 -0
  148. package/dashboard/dist-server/dashboard/src/server/wiki/wiki-indexer.test.js.map +1 -1
  149. package/dashboard/dist-server/dashboard/src/shared/constants.js +2 -0
  150. package/dashboard/dist-server/dashboard/src/shared/constants.js.map +1 -1
  151. package/dashboard/dist-server/shared/agent-types.d.ts +1 -1
  152. package/dashboard/dist-server/src/commands/hooks.d.ts +3 -0
  153. package/dashboard/dist-server/src/commands/hooks.js +60 -9
  154. package/dashboard/dist-server/src/commands/hooks.js.map +1 -1
  155. package/dashboard/dist-server/src/graph/kg/extraction/code/code-extractor.js +1 -1
  156. package/dashboard/dist-server/src/graph/kg/extraction/code/code-extractor.js.map +1 -1
  157. package/dashboard/dist-server/src/graph/kg/extraction/knowledge/domain-extractor.js +4 -2
  158. package/dashboard/dist-server/src/graph/kg/extraction/knowledge/domain-extractor.js.map +1 -1
  159. package/dashboard/dist-server/src/graph/kg/extraction/knowledge-extractor-registry.js +6 -1
  160. package/dashboard/dist-server/src/graph/kg/extraction/knowledge-extractor-registry.js.map +1 -1
  161. package/dashboard/dist-server/src/graph/kg/extraction/orchestrator.js +2 -6
  162. package/dashboard/dist-server/src/graph/kg/extraction/orchestrator.js.map +1 -1
  163. package/dashboard/dist-server/src/hooks/session-context.js +37 -1
  164. package/dashboard/dist-server/src/hooks/session-context.js.map +1 -1
  165. package/dashboard/dist-server/src/search/daemon.d.ts +42 -0
  166. package/dashboard/dist-server/src/search/daemon.js +180 -0
  167. package/dashboard/dist-server/src/search/daemon.js.map +1 -0
  168. package/dashboard/dist-server/src/tools/domain-loader.d.ts +4 -1
  169. package/dashboard/dist-server/src/tools/domain-loader.js +56 -25
  170. package/dashboard/dist-server/src/tools/domain-loader.js.map +1 -1
  171. package/dashboard/dist-server/src/tools/domain-schema.d.ts +1 -1
  172. package/dashboard/dist-server/src/tools/domain-schema.js +1 -1
  173. package/dist/shared/agent-types.d.ts +1 -1
  174. package/dist/shared/agent-types.d.ts.map +1 -1
  175. package/dist/src/agents/api-explore/agent-loop.d.ts +14 -0
  176. package/dist/src/agents/api-explore/agent-loop.d.ts.map +1 -0
  177. package/dist/src/agents/api-explore/agent-loop.js +86 -0
  178. package/dist/src/agents/api-explore/agent-loop.js.map +1 -0
  179. package/dist/src/agents/api-explore/config.d.ts +42 -0
  180. package/dist/src/agents/api-explore/config.d.ts.map +1 -0
  181. package/dist/src/agents/api-explore/config.js +99 -0
  182. package/dist/src/agents/api-explore/config.js.map +1 -0
  183. package/dist/src/agents/api-explore/index.d.ts +2 -0
  184. package/dist/src/agents/api-explore/index.d.ts.map +1 -0
  185. package/dist/src/agents/api-explore/index.js +102 -0
  186. package/dist/src/agents/api-explore/index.js.map +1 -0
  187. package/dist/src/agents/api-explore/llm.d.ts +28 -0
  188. package/dist/src/agents/api-explore/llm.d.ts.map +1 -0
  189. package/dist/src/agents/api-explore/llm.js +48 -0
  190. package/dist/src/agents/api-explore/llm.js.map +1 -0
  191. package/dist/src/agents/api-explore/prompt-parser.d.ts +23 -0
  192. package/dist/src/agents/api-explore/prompt-parser.d.ts.map +1 -0
  193. package/dist/src/agents/api-explore/prompt-parser.js +73 -0
  194. package/dist/src/agents/api-explore/prompt-parser.js.map +1 -0
  195. package/dist/src/agents/api-explore/runner.d.ts +65 -0
  196. package/dist/src/agents/api-explore/runner.d.ts.map +1 -0
  197. package/dist/src/agents/api-explore/runner.js +196 -0
  198. package/dist/src/agents/api-explore/runner.js.map +1 -0
  199. package/dist/src/agents/api-explore/session.d.ts +23 -0
  200. package/dist/src/agents/api-explore/session.d.ts.map +1 -0
  201. package/dist/src/agents/api-explore/session.js +57 -0
  202. package/dist/src/agents/api-explore/session.js.map +1 -0
  203. package/dist/src/agents/api-explore/stream-json-emitter.d.ts +10 -0
  204. package/dist/src/agents/api-explore/stream-json-emitter.d.ts.map +1 -0
  205. package/dist/src/agents/api-explore/stream-json-emitter.js +19 -0
  206. package/dist/src/agents/api-explore/stream-json-emitter.js.map +1 -0
  207. package/dist/src/agents/api-explore/system-prompt.d.ts +2 -0
  208. package/dist/src/agents/api-explore/system-prompt.d.ts.map +1 -0
  209. package/dist/src/agents/api-explore/system-prompt.js +31 -0
  210. package/dist/src/agents/api-explore/system-prompt.js.map +1 -0
  211. package/dist/src/agents/api-explore/tools.d.ts +11 -0
  212. package/dist/src/agents/api-explore/tools.d.ts.map +1 -0
  213. package/dist/src/agents/api-explore/tools.js +168 -0
  214. package/dist/src/agents/api-explore/tools.js.map +1 -0
  215. package/dist/src/agents/cli-agent-runner.d.ts.map +1 -1
  216. package/dist/src/agents/cli-agent-runner.js +3 -0
  217. package/dist/src/agents/cli-agent-runner.js.map +1 -1
  218. package/dist/src/cli.js +4 -0
  219. package/dist/src/cli.js.map +1 -1
  220. package/dist/src/commands/domain.d.ts.map +1 -1
  221. package/dist/src/commands/domain.js +5 -4
  222. package/dist/src/commands/domain.js.map +1 -1
  223. package/dist/src/commands/explore.d.ts +7 -0
  224. package/dist/src/commands/explore.d.ts.map +1 -0
  225. package/dist/src/commands/explore.js +201 -0
  226. package/dist/src/commands/explore.js.map +1 -0
  227. package/dist/src/commands/hooks.d.ts +3 -0
  228. package/dist/src/commands/hooks.d.ts.map +1 -1
  229. package/dist/src/commands/hooks.js +60 -9
  230. package/dist/src/commands/hooks.js.map +1 -1
  231. package/dist/src/commands/install.d.ts.map +1 -1
  232. package/dist/src/commands/install.js +42 -9
  233. package/dist/src/commands/install.js.map +1 -1
  234. package/dist/src/commands/search.d.ts +26 -4
  235. package/dist/src/commands/search.d.ts.map +1 -1
  236. package/dist/src/commands/search.js +359 -93
  237. package/dist/src/commands/search.js.map +1 -1
  238. package/dist/src/commands/update.d.ts.map +1 -1
  239. package/dist/src/commands/update.js +37 -21
  240. package/dist/src/commands/update.js.map +1 -1
  241. package/dist/src/commands/workspace.d.ts.map +1 -1
  242. package/dist/src/commands/workspace.js +9 -3
  243. package/dist/src/commands/workspace.js.map +1 -1
  244. package/dist/src/config/cli-tools-defaults.json +2 -1
  245. package/dist/src/core/install-executor.d.ts.map +1 -1
  246. package/dist/src/core/install-executor.js +6 -3
  247. package/dist/src/core/install-executor.js.map +1 -1
  248. package/dist/src/graph/kg/extraction/code/code-extractor.js +1 -1
  249. package/dist/src/graph/kg/extraction/code/code-extractor.js.map +1 -1
  250. package/dist/src/graph/kg/extraction/knowledge/domain-extractor.d.ts.map +1 -1
  251. package/dist/src/graph/kg/extraction/knowledge/domain-extractor.js +4 -2
  252. package/dist/src/graph/kg/extraction/knowledge/domain-extractor.js.map +1 -1
  253. package/dist/src/graph/kg/extraction/knowledge-extractor-registry.d.ts.map +1 -1
  254. package/dist/src/graph/kg/extraction/knowledge-extractor-registry.js +6 -1
  255. package/dist/src/graph/kg/extraction/knowledge-extractor-registry.js.map +1 -1
  256. package/dist/src/graph/kg/extraction/orchestrator.d.ts.map +1 -1
  257. package/dist/src/graph/kg/extraction/orchestrator.js +2 -6
  258. package/dist/src/graph/kg/extraction/orchestrator.js.map +1 -1
  259. package/dist/src/graph/kg/surface/cli.d.ts.map +1 -1
  260. package/dist/src/graph/kg/surface/cli.js +5 -2
  261. package/dist/src/graph/kg/surface/cli.js.map +1 -1
  262. package/dist/src/hooks/session-context.d.ts.map +1 -1
  263. package/dist/src/hooks/session-context.js +37 -1
  264. package/dist/src/hooks/session-context.js.map +1 -1
  265. package/dist/src/ralph/cmd-next.d.ts.map +1 -1
  266. package/dist/src/ralph/cmd-next.js +66 -4
  267. package/dist/src/ralph/cmd-next.js.map +1 -1
  268. package/dist/src/ralph/status-schema.d.ts +1 -0
  269. package/dist/src/ralph/status-schema.d.ts.map +1 -1
  270. package/dist/src/search/daemon.d.ts +43 -0
  271. package/dist/src/search/daemon.d.ts.map +1 -0
  272. package/dist/src/search/daemon.js +180 -0
  273. package/dist/src/search/daemon.js.map +1 -0
  274. package/dist/src/tools/__tests__/domain-search-debug.js +3 -1
  275. package/dist/src/tools/__tests__/domain-search-debug.js.map +1 -1
  276. package/dist/src/tools/domain-loader.d.ts +4 -1
  277. package/dist/src/tools/domain-loader.d.ts.map +1 -1
  278. package/dist/src/tools/domain-loader.js +56 -25
  279. package/dist/src/tools/domain-loader.js.map +1 -1
  280. package/dist/src/tools/domain-schema.d.ts +1 -1
  281. package/dist/src/tools/domain-schema.js +1 -1
  282. package/dist/src/utils/update-notices.js +12 -0
  283. package/dist/src/utils/update-notices.js.map +1 -1
  284. package/package.json +5 -1
  285. package/shared/agent-types.ts +1 -1
  286. package/templates/search-tools.md +22 -6
  287. package/workflows/agy-instructions.md +0 -4
  288. package/workflows/analyze.md +2 -2
  289. package/workflows/boundary-grill.md +50 -0
  290. package/workflows/brainstorm.md +6 -5
  291. package/workflows/claude-instructions.md +29 -2
  292. package/workflows/codebase-rebuild.md +0 -1
  293. package/workflows/codex-instructions.md +97 -14
  294. package/workflows/command-authoring.md +1 -1
  295. package/workflows/debug.md +0 -4
  296. package/workflows/domain-add.md +115 -0
  297. package/workflows/execute.md +7 -0
  298. package/workflows/explore-usage.md +128 -0
  299. package/workflows/finish-work.md +2 -6
  300. package/workflows/fork.md +0 -4
  301. package/workflows/grill.md +14 -1
  302. package/workflows/harvest.md +0 -3
  303. package/workflows/impeccable/audit.md +0 -2
  304. package/workflows/impeccable/codex.md +0 -4
  305. package/workflows/init.md +9 -0
  306. package/workflows/interview-mechanics.md +3 -2
  307. package/workflows/issue-analyze.md +0 -5
  308. package/workflows/issue-gaps-analyze.codex.md +0 -6
  309. package/workflows/knowledge-audit.md +0 -4
  310. package/workflows/learn.md +0 -6
  311. package/workflows/maestro.codex.md +0 -4
  312. package/workflows/maestro.md +31 -18
  313. package/workflows/merge.md +0 -4
  314. package/workflows/milestone-complete.md +0 -4
  315. package/workflows/overlays.md +0 -10
  316. package/workflows/plan.md +2 -3
  317. package/workflows/roadmap-common.md +0 -4
  318. package/workflows/tdd.md +0 -2
  319. package/workflows/tools-spec.md +1 -7
  320. package/workflows/ui-codify-extract.md +0 -2
  321. package/workflows/ui-codify-package.md +0 -2
  322. package/.agents/skills/maestro-ralph-beta/SKILL.md +0 -882
  323. package/.agy/skills/maestro-ralph-beta/SKILL.md +0 -878
  324. package/.claude/commands/maestro-ralph-beta.md +0 -880
  325. package/.codex/skills/maestro-ralph-beta/SKILL.md +0 -883
@@ -1,8 +1,14 @@
1
1
  /**
2
- * Search Command — Unified knowledge search across specs, knowhow, issues, and more.
2
+ * Search Command — Unified knowledge search across wiki + code.
3
3
  *
4
- * Uses WikiIndexer BM25F search with deduplication and type filtering.
5
- * Optional --code flag adds CodeGraph AST results in a separate section.
4
+ * Default: mixed results (wiki + code interleaved by normalized score).
5
+ * --code: separate section display (backward compat).
6
+ * --wiki-only: wiki results only (no code search).
7
+ *
8
+ * Scoring: multi-signal normalization inspired by codebase-memory-mcp.
9
+ * Wiki: BM25F score + type boost (spec > knowhow > note)
10
+ * Code: BM25 score + kind boost + name-match bonus
11
+ * Merge: percentile-aware normalization + source weight
6
12
  */
7
13
  import type { Command } from 'commander';
8
14
  import type { WikiEntry, WikiNodeType } from '#maestro-dashboard/wiki/wiki-types.js';
@@ -38,6 +44,22 @@ export interface UnifiedSearchOptions {
38
44
  * Unified knowledge search — BM25F ranking via WikiIndexer, with type/category
39
45
  * filtering and per-source deduplication.
40
46
  */
41
- export declare function runUnifiedSearch(q: string, opts: UnifiedSearchOptions): Promise<SearchResult[]>;
47
+ export interface SearchMeta {
48
+ embeddingUsed: boolean;
49
+ embeddingDocs: number;
50
+ }
51
+ export declare function getLastSearchMeta(): SearchMeta;
52
+ export declare function runUnifiedSearch(q: string, opts: UnifiedSearchOptions & {
53
+ skipEmbedding?: boolean;
54
+ }): Promise<SearchResult[]>;
42
55
  export declare function registerSearchCommand(program: Command): void;
56
+ export interface MergedResult {
57
+ source: 'wiki' | 'code';
58
+ kind: string;
59
+ name: string;
60
+ detail: string;
61
+ normalizedScore: number;
62
+ snippet?: string;
63
+ signature?: string;
64
+ }
43
65
  //# sourceMappingURL=search.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../src/commands/search.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKzC,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,uCAAuC,CAAC;AAMrF,kEAAkE;AAClE,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,YAAY,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,EAAE,SAAS,CAAC,QAAQ,CAAC,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,2CAA2C;AAC3C,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,2EAA2E;AAC3E,MAAM,WAAW,oBAAoB;IACnC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;CACf;AAoBD;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CA6CrG;AAgDD,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA0G5D"}
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../src/commands/search.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKzC,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,uCAAuC,CAAC;AAOrF,kEAAkE;AAClE,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,YAAY,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,EAAE,SAAS,CAAC,QAAQ,CAAC,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,2CAA2C;AAC3C,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,2EAA2E;AAC3E,MAAM,WAAW,oBAAoB;IACnC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;CACf;AAoBD;;;GAGG;AACH,MAAM,WAAW,UAAU;IACzB,aAAa,EAAE,OAAO,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;CACvB;AAGD,wBAAgB,iBAAiB,IAAI,UAAU,CAA4B;AAE3E,wBAAsB,gBAAgB,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,oBAAoB,GAAG;IAAE,aAAa,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CA+DnI;AAgDD,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAsO5D;AAkCD,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB"}
@@ -1,13 +1,20 @@
1
1
  /**
2
- * Search Command — Unified knowledge search across specs, knowhow, issues, and more.
2
+ * Search Command — Unified knowledge search across wiki + code.
3
3
  *
4
- * Uses WikiIndexer BM25F search with deduplication and type filtering.
5
- * Optional --code flag adds CodeGraph AST results in a separate section.
4
+ * Default: mixed results (wiki + code interleaved by normalized score).
5
+ * --code: separate section display (backward compat).
6
+ * --wiki-only: wiki results only (no code search).
7
+ *
8
+ * Scoring: multi-signal normalization inspired by codebase-memory-mcp.
9
+ * Wiki: BM25F score + type boost (spec > knowhow > note)
10
+ * Code: BM25 score + kind boost + name-match bonus
11
+ * Merge: percentile-aware normalization + source weight
6
12
  */
7
13
  import { resolve } from 'node:path';
8
14
  import { truncate, extractSnippet, highlightTerms } from '../utils/cli-format.js';
9
15
  import { WikiIndexer } from '#maestro-dashboard/wiki/wiki-indexer.js';
10
16
  import { loadWorkspaceConfig, resolveWorkspaceLinks } from '../config/index.js';
17
+ import { tryDaemonSearch, startDaemon, stopDaemon, readDaemonInfo, isDaemonAlive, getDaemonPath } from '../search/daemon.js';
11
18
  // Valid type filter values — matches WikiNodeType.
12
19
  const VALID_TYPES = ['project', 'roadmap', 'spec', 'issue', 'knowhow', 'note', 'domain'];
13
20
  // ── Lazy offline client ────────────────────────────────────────────────
@@ -25,15 +32,30 @@ function getIndexer() {
25
32
  }
26
33
  return _indexer;
27
34
  }
28
- /**
29
- * Unified knowledge search BM25F ranking via WikiIndexer, with type/category
30
- * filtering and per-source deduplication.
31
- */
35
+ let _lastSearchMeta = { embeddingUsed: false, embeddingDocs: 0 };
36
+ export function getLastSearchMeta() { return _lastSearchMeta; }
32
37
  export async function runUnifiedSearch(q, opts) {
33
38
  const limit = opts.limit > 0 ? opts.limit : 20;
34
- const indexer = getIndexer();
35
39
  const candidateLimit = Math.max(limit * 3, 60);
36
- const scored = await indexer.searchWithScores(q, candidateLimit);
40
+ // Try daemon first (warm ONNX model, no cold-start penalty)
41
+ const workflowRoot = resolve('.workflow');
42
+ const daemonResult = await tryDaemonSearch(workflowRoot, q, candidateLimit, opts.skipEmbedding);
43
+ let scored;
44
+ let embeddingUsed;
45
+ let embeddingDocs;
46
+ if (daemonResult?.ok && daemonResult.results) {
47
+ scored = daemonResult.results;
48
+ embeddingUsed = daemonResult.embeddingUsed ?? false;
49
+ embeddingDocs = daemonResult.embeddingDocs ?? 0;
50
+ }
51
+ else {
52
+ const indexer = getIndexer();
53
+ const result = await indexer.searchWithMeta(q, candidateLimit, { skipEmbedding: opts.skipEmbedding });
54
+ scored = result.results;
55
+ embeddingUsed = result.embeddingUsed;
56
+ embeddingDocs = result.embeddingDocs;
57
+ }
58
+ _lastSearchMeta = { embeddingUsed, embeddingDocs };
37
59
  let filtered = scored;
38
60
  if (opts.type) {
39
61
  filtered = filtered.filter(r => r.entry.type === opts.type);
@@ -54,13 +76,14 @@ export async function runUnifiedSearch(q, opts) {
54
76
  if (deduped.length >= limit)
55
77
  break;
56
78
  }
79
+ const maxScore = deduped.length > 0 ? deduped[0].score : 1;
57
80
  const results = deduped.map(({ entry, score }) => ({
58
81
  id: entry.id,
59
82
  type: entry.type,
60
83
  title: entry.title,
61
84
  category: entry.category,
62
85
  summary: entry.summary,
63
- score,
86
+ score: maxScore > 0 ? score / maxScore : score,
64
87
  snippet: extractSnippet(entry.body, q),
65
88
  source: entry.source,
66
89
  workspace: entry.source.workspace,
@@ -124,142 +147,385 @@ async function runCodeSearch(q, limit) {
124
147
  export function registerSearchCommand(program) {
125
148
  program
126
149
  .command('search <query...>')
127
- .description('Unified knowledge search across specs, knowhow, issues, and more')
150
+ .description('Unified knowledge search across wiki + code (mixed by default)')
128
151
  .option('--type <type>', `Filter by type: ${VALID_TYPES.join(', ')}`)
129
152
  .option('--category <cat>', 'Filter by category (e.g. coding, arch, debug, test, review, learning)')
130
- .option('--code', 'Include CodeGraph code results')
131
- .option('--all', 'Search all sources (wiki + code) with normalized ranking')
153
+ .option('--code', 'Show wiki and code in separate sections (legacy display)')
154
+ .option('--all', 'Alias for default mixed mode (backward compat)')
155
+ .option('--wiki-only', 'Search wiki only, skip code results')
132
156
  .option('--workspace <name>', 'Filter results to a specific linked workspace')
157
+ .option('--no-emb', 'Skip embedding, use BM25 only')
133
158
  .option('--limit <n>', 'Max results', '20')
134
159
  .option('--json', 'Output as JSON')
135
160
  .action(async (queryParts, opts) => {
136
161
  const q = queryParts.join(' ');
137
162
  const limit = parseInt(opts.limit, 10) || 20;
138
- const includeCode = opts.code || opts.all;
163
+ const wikiOnly = opts.wikiOnly === true;
164
+ const separateSections = opts.code === true && !opts.all;
139
165
  if (opts.type && !VALID_TYPES.includes(opts.type)) {
140
166
  console.error(`Error: --type must be one of ${VALID_TYPES.join(', ')} (got "${opts.type}")`);
141
167
  process.exit(1);
142
168
  }
143
- let wikiResults = await runUnifiedSearch(q, { type: opts.type, category: opts.category, workspace: opts.workspace, limit });
144
- const codeResults = includeCode ? await runCodeSearch(q, limit) : [];
145
- // --all: normalize and merge scores for unified ranking
146
- if (opts.all) {
147
- const merged = mergeAndNormalize(wikiResults, codeResults, limit);
169
+ const skipEmbedding = opts.emb === false;
170
+ const wikiResults = await runUnifiedSearch(q, { type: opts.type, category: opts.category, workspace: opts.workspace, limit, skipEmbedding });
171
+ const codeResults = wikiOnly ? [] : await runCodeSearch(q, limit);
172
+ const meta = getLastSearchMeta();
173
+ const embTag = meta.embeddingUsed ? `+emb(${meta.embeddingDocs})` : 'bm25';
174
+ const isTTY = process.stdout.isTTY === true;
175
+ const qTerms = q.toLowerCase().split(/\s+/).filter(Boolean);
176
+ // --code (without --all): legacy separate-section display
177
+ if (separateSections) {
148
178
  if (opts.json) {
149
- console.log(JSON.stringify({ query: q, count: merged.length, results: merged }, null, 2));
179
+ console.log(JSON.stringify({ query: q, wikiCount: wikiResults.length, codeCount: codeResults.length, wikiResults, codeResults }, null, 2));
150
180
  return;
151
181
  }
152
- console.log(`Search: "${q}" (${merged.length} results, all sources)`);
153
- if (merged.length === 0) {
182
+ console.log(`Search: "${q}" (${wikiResults.length} wiki + ${codeResults.length} code, ${embTag})`);
183
+ if (wikiResults.length === 0 && codeResults.length === 0) {
154
184
  console.log(' No matches found.');
155
185
  return;
156
186
  }
157
- const isTTY = process.stdout.isTTY === true;
158
- const qTerms = q.toLowerCase().split(/\s+/).filter(Boolean);
159
- for (const r of merged) {
160
- const name = isTTY ? highlightTerms(r.name, qTerms) : r.name;
161
- const scoreTag = ` (${r.normalizedScore.toFixed(2)})`;
162
- console.log(` [${r.source}] [${r.kind}] ${name} ${r.detail}${scoreTag}`);
187
+ if (wikiResults.length > 0) {
188
+ console.log(' [Wiki Results]');
189
+ for (const r of wikiResults) {
190
+ printWikiResult(r, ' ', isTTY, qTerms);
191
+ }
192
+ }
193
+ if (codeResults.length > 0) {
194
+ console.log(' [Code Results]');
195
+ for (const r of codeResults) {
196
+ printCodeResult(r, ' ', isTTY, qTerms);
197
+ }
163
198
  }
164
199
  return;
165
200
  }
201
+ // Default / --all / --wiki-only: mixed interleaved results
202
+ const merged = mergeAndNormalize(wikiResults, codeResults, limit, q);
203
+ const wikiCount = merged.filter(r => r.source === 'wiki').length;
204
+ const codeCount = merged.filter(r => r.source === 'code').length;
166
205
  if (opts.json) {
167
- const output = { query: q };
168
- if (includeCode) {
169
- output.wikiResults = wikiResults;
170
- output.codeResults = codeResults;
171
- output.wikiCount = wikiResults.length;
172
- output.codeCount = codeResults.length;
173
- }
174
- else {
175
- output.count = wikiResults.length;
176
- output.results = wikiResults;
177
- }
178
- console.log(JSON.stringify(output, null, 2));
206
+ console.log(JSON.stringify({ query: q, wikiCount, codeCount, count: merged.length, results: merged }, null, 2));
179
207
  return;
180
208
  }
181
- const isTTY = process.stdout.isTTY === true;
182
- const qTerms = q.toLowerCase().split(/\s+/).filter(Boolean);
183
- if (includeCode && codeResults.length > 0) {
184
- console.log(`Search: "${q}" (${wikiResults.length} wiki + ${codeResults.length} code results)`);
185
- }
186
- else {
187
- console.log(`Search: "${q}" (${wikiResults.length} results)`);
209
+ const countParts = [];
210
+ if (wikiCount > 0)
211
+ countParts.push(`wiki ${wikiCount}个`);
212
+ if (codeCount > 0)
213
+ countParts.push(`代码 ${codeCount}个`);
214
+ const countSummary = countParts.length > 0
215
+ ? `${countParts.join(' + ')} = ${merged.length} results`
216
+ : '0 results';
217
+ console.log(`Search: "${q}" (${countSummary}, ${embTag})`);
218
+ if (qTerms.length > 4) {
219
+ console.log(` Hint: ${qTerms.length} terms — split into 1-3 keyword queries for better precision`);
188
220
  }
189
- if (wikiResults.length === 0 && codeResults.length === 0) {
221
+ if (merged.length === 0) {
190
222
  console.log(' No matches found.');
191
223
  return;
192
224
  }
193
- if (wikiResults.length > 0) {
194
- if (includeCode)
195
- console.log(' [Wiki Results]');
196
- for (const r of wikiResults) {
197
- const indent = includeCode ? ' ' : ' ';
198
- const typeTag = `[${r.type}]`;
199
- const catTag = r.category ? ` ${r.category}` : '';
200
- const wsTag = r.workspace ? ` [ws:${r.workspace}]` : '';
201
- const scoreTag = r.score !== null ? ` (${r.score.toFixed(2)})` : '';
202
- const title = isTTY ? highlightTerms(r.title, qTerms) : r.title;
203
- console.log(`${indent}${typeTag}${catTag}${wsTag} ${r.id} ${title}${scoreTag}`);
225
+ for (const r of merged) {
226
+ const name = isTTY ? highlightTerms(r.name, qTerms) : r.name;
227
+ const scoreTag = ` (${r.normalizedScore.toFixed(4)})`;
228
+ if (r.source === 'wiki') {
229
+ console.log(` [wiki:${r.kind}] ${name} ${r.detail}${scoreTag}`);
204
230
  if (r.snippet) {
205
231
  const snippet = isTTY ? highlightTerms(r.snippet, qTerms) : r.snippet;
206
- console.log(`${indent} ${snippet}`);
232
+ console.log(` ${snippet}`);
207
233
  }
208
- else if (r.summary) {
209
- const summary = isTTY ? highlightTerms(truncate(r.summary, 80), qTerms) : truncate(r.summary, 80);
210
- console.log(`${indent} ${summary}`);
234
+ }
235
+ else {
236
+ const sigTag = r.signature ? ` ${truncate(r.signature, 60)}` : '';
237
+ console.log(` [code:${r.kind}] ${name} ${r.detail}${sigTag}${scoreTag}`);
238
+ }
239
+ }
240
+ });
241
+ // ── Search daemon management ───────────────────────────────────────────
242
+ program
243
+ .command('search-daemon')
244
+ .description('Manage the resident search daemon (warm ONNX model)')
245
+ .argument('<action>', 'start | stop | status')
246
+ .action(async (action) => {
247
+ const workflowRoot = resolve('.workflow');
248
+ if (action === 'start' || action === 'start-daemon') {
249
+ const info = readDaemonInfo(workflowRoot);
250
+ if (info && isDaemonAlive(info)) {
251
+ console.log(`Search daemon already running (pid=${info.pid}, port=${info.port})`);
252
+ return;
253
+ }
254
+ console.log('Starting search daemon...');
255
+ const projectPath = process.cwd();
256
+ const wsConfig = loadWorkspaceConfig(projectPath);
257
+ const resolved = resolveWorkspaceLinks(projectPath, wsConfig);
258
+ const linkedWorkspaces = resolved
259
+ .filter(lw => lw.valid)
260
+ .map(lw => ({ name: lw.name, workflowRoot: lw.workflowRoot, shareTypes: lw.share }));
261
+ const { port } = await startDaemon(workflowRoot, { workflowRoot, linkedWorkspaces });
262
+ console.log(`Search daemon started (pid=${process.pid}, port=${port})`);
263
+ // Keep process alive
264
+ return;
265
+ }
266
+ if (action === 'stop') {
267
+ const stopped = stopDaemon(workflowRoot);
268
+ console.log(stopped ? 'Search daemon stopped.' : 'No daemon running.');
269
+ return;
270
+ }
271
+ if (action === 'status') {
272
+ const info = readDaemonInfo(workflowRoot);
273
+ if (!info) {
274
+ console.log('Search daemon: not running');
275
+ return;
276
+ }
277
+ const alive = isDaemonAlive(info);
278
+ console.log(`Search daemon: ${alive ? 'running' : 'stale (pid dead)'} pid=${info.pid} port=${info.port} started=${info.startedAt}`);
279
+ if (!alive)
280
+ try {
281
+ const { unlinkSync } = await import('node:fs');
282
+ unlinkSync(getDaemonPath(workflowRoot));
211
283
  }
284
+ catch { }
285
+ return;
286
+ }
287
+ console.error(`Unknown action: ${action}. Use: start, stop, status`);
288
+ });
289
+ // Hidden flag for hook-spawned daemon startup
290
+ program
291
+ .command('search-start-daemon', { hidden: true })
292
+ .action(async () => {
293
+ const workflowRoot = resolve('.workflow');
294
+ const projectPath = process.cwd();
295
+ const wsConfig = loadWorkspaceConfig(projectPath);
296
+ const resolved = resolveWorkspaceLinks(projectPath, wsConfig);
297
+ const linkedWorkspaces = resolved
298
+ .filter(lw => lw.valid)
299
+ .map(lw => ({ name: lw.name, workflowRoot: lw.workflowRoot, shareTypes: lw.share }));
300
+ try {
301
+ await startDaemon(workflowRoot, { workflowRoot, linkedWorkspaces });
302
+ }
303
+ catch {
304
+ process.exit(0);
305
+ }
306
+ });
307
+ program
308
+ .command('embedding')
309
+ .description('Embedding model status, warmup, and rebuild')
310
+ .argument('[action]', 'status (default), warmup, rebuild', 'status')
311
+ .action(async (action) => {
312
+ const workflowRoot = resolve('.workflow');
313
+ const { isAvailable, getUnavailableReason, loadEmbeddingIndex, embedTexts, getDeviceSummary, detectDevice } = await import('#maestro-dashboard/wiki/embedding.js');
314
+ if (action === 'status') {
315
+ const avail = await isAvailable();
316
+ console.log(`Transformers: ${avail ? 'available' : 'NOT available (' + (getUnavailableReason?.() ?? 'unknown') + ')'}`);
317
+ if (avail) {
318
+ await detectDevice();
319
+ console.log(`Device: ${getDeviceSummary()}`);
320
+ }
321
+ const idx = loadEmbeddingIndex(workflowRoot);
322
+ if (idx) {
323
+ console.log(`Index: ${idx.docIds.length} docs, dim=${idx.dimension}, model=${idx.modelId}`);
324
+ console.log(`Built: ${new Date(idx.builtAt).toISOString()}, device=${idx.deviceUsed}`);
325
+ if (idx.buildTimeMs)
326
+ console.log(`Build time: ${idx.buildTimeMs}ms`);
327
+ }
328
+ else {
329
+ console.log('Index: not built (will build on first search)');
212
330
  }
331
+ return;
213
332
  }
214
- if (codeResults.length > 0) {
215
- console.log(' [Code Results]');
216
- for (const r of codeResults) {
217
- const scoreTag = r.score !== null ? ` (${r.score.toFixed(2)})` : '';
218
- const name = isTTY ? highlightTerms(r.name, qTerms) : r.name;
219
- console.log(` [${r.kind}] ${name} ${r.filePath}${scoreTag}`);
333
+ if (action === 'warmup') {
334
+ const avail = await isAvailable();
335
+ if (!avail) {
336
+ console.error(`Embedding unavailable: ${getUnavailableReason?.() ?? 'unknown'}`);
337
+ process.exit(1);
220
338
  }
339
+ console.log('Warming up model...');
340
+ const t0 = Date.now();
341
+ await embedTexts(['warmup']);
342
+ console.log(`Model ready (${getDeviceSummary()}, ${Date.now() - t0}ms)`);
343
+ return;
221
344
  }
345
+ if (action === 'rebuild') {
346
+ const avail = await isAvailable();
347
+ if (!avail) {
348
+ console.error(`Embedding unavailable: ${getUnavailableReason?.() ?? 'unknown'}`);
349
+ process.exit(1);
350
+ }
351
+ console.log('Rebuilding embedding index...');
352
+ const { WikiIndexer } = await import('#maestro-dashboard/wiki/wiki-indexer.js');
353
+ const { loadWorkspaceConfig, resolveWorkspaceLinks } = await import('../config/index.js');
354
+ const projectPath = process.cwd();
355
+ const wsConfig = loadWorkspaceConfig(projectPath);
356
+ const resolved = resolveWorkspaceLinks(projectPath, wsConfig);
357
+ const linkedWorkspaces = resolved.filter(lw => lw.valid).map(lw => ({ name: lw.name, workflowRoot: lw.workflowRoot, shareTypes: lw.share }));
358
+ const indexer = new WikiIndexer({ workflowRoot, linkedWorkspaces });
359
+ const t0 = Date.now();
360
+ const { results, embeddingUsed, embeddingDocs } = await indexer.searchWithMeta('warmup', 1);
361
+ if (embeddingUsed) {
362
+ console.log(`Index rebuilt: ${embeddingDocs} docs (${Date.now() - t0}ms)`);
363
+ }
364
+ else {
365
+ console.log(`Rebuild failed — check with: maestro embedding status`);
366
+ }
367
+ return;
368
+ }
369
+ console.error(`Unknown action: ${action}. Use: status, warmup, rebuild`);
370
+ process.exit(1);
222
371
  });
223
372
  }
224
- function getMinMax(scores) {
225
- let min = Infinity;
226
- let max = -Infinity;
227
- for (const s of scores) {
228
- if (s < min)
229
- min = s;
230
- if (s > max)
231
- max = s;
373
+ // ── Display helpers ──────────────────────────────────────────────────
374
+ function printWikiResult(r, indent, isTTY, qTerms) {
375
+ const typeTag = `[${r.type}]`;
376
+ const catTag = r.category ? ` ${r.category}` : '';
377
+ const wsTag = r.workspace ? ` [ws:${r.workspace}]` : '';
378
+ const scoreTag = r.score !== null ? ` (${r.score.toFixed(4)})` : '';
379
+ const title = isTTY ? highlightTerms(r.title, qTerms) : r.title;
380
+ console.log(`${indent}${typeTag}${catTag}${wsTag} ${r.id} ${title}${scoreTag}`);
381
+ if (r.snippet) {
382
+ const snippet = isTTY ? highlightTerms(r.snippet, qTerms) : r.snippet;
383
+ console.log(`${indent} ${snippet}`);
384
+ }
385
+ else if (r.summary) {
386
+ const summary = isTTY ? highlightTerms(truncate(r.summary, 80), qTerms) : truncate(r.summary, 80);
387
+ console.log(`${indent} ${summary}`);
232
388
  }
233
- return { min, max };
234
389
  }
235
- function mergeAndNormalize(wiki, code, limit) {
236
- const WIKI_WEIGHT = 0.6;
237
- const CODE_WEIGHT = 0.4;
238
- const wikiScores = wiki.map(r => r.score ?? 0);
239
- const codeScores = code.map(r => r.score ?? 0);
240
- const wikiMM = wikiScores.length > 0 ? getMinMax(wikiScores) : { min: 0, max: 0 };
241
- const codeMM = codeScores.length > 0 ? getMinMax(codeScores) : { min: 0, max: 0 };
242
- const wikiRange = wikiMM.max - wikiMM.min || 1;
243
- const codeRange = codeMM.max - codeMM.min || 1;
244
- const merged = [];
245
- for (const r of wiki) {
390
+ function printCodeResult(r, indent, isTTY, qTerms) {
391
+ const scoreTag = r.score !== null ? ` (${r.score.toFixed(4)})` : '';
392
+ const name = isTTY ? highlightTerms(r.name, qTerms) : r.name;
393
+ const sigTag = r.signature ? ` ${truncate(r.signature, 60)}` : '';
394
+ console.log(`${indent}[${r.kind}] ${name} ${r.filePath}${sigTag}${scoreTag}`);
395
+ }
396
+ const WIKI_TYPE_BOOST = {
397
+ spec: 1.15,
398
+ domain: 1.10,
399
+ knowhow: 1.05,
400
+ project: 0.95,
401
+ roadmap: 0.95,
402
+ issue: 0.85,
403
+ note: 0.80,
404
+ };
405
+ const CODE_KIND_BOOST = {
406
+ class: 1.20,
407
+ interface: 1.15,
408
+ function: 1.10,
409
+ method: 1.10,
410
+ component: 1.08,
411
+ route: 1.12,
412
+ type_alias: 1.05,
413
+ enum: 1.05,
414
+ constant: 1.00,
415
+ variable: 0.90,
416
+ field: 0.85,
417
+ property: 0.80,
418
+ };
419
+ function isCodeIdentifier(query) {
420
+ const trimmed = query.trim();
421
+ if (/^[a-z]+[A-Z]/.test(trimmed))
422
+ return true;
423
+ if (/^[A-Z][a-z]+[A-Z]/.test(trimmed))
424
+ return true;
425
+ if (/^[A-Z]{2,}[a-z]/.test(trimmed))
426
+ return true;
427
+ if (/^[a-z]+_[a-z]+/.test(trimmed))
428
+ return true;
429
+ if (/^[A-Z][a-zA-Z]+$/.test(trimmed) && !trimmed.includes(' '))
430
+ return true;
431
+ return false;
432
+ }
433
+ function splitCamelSnake(s) {
434
+ return s
435
+ .replace(/([a-z])([A-Z])/g, '$1 $2')
436
+ .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')
437
+ .split(/[\s_\-.]+/)
438
+ .map(t => t.toLowerCase())
439
+ .filter(t => t.length > 0);
440
+ }
441
+ function codeNameMatchBonus(codeName, query) {
442
+ const nameLower = codeName.toLowerCase();
443
+ const queryLower = query.toLowerCase().trim();
444
+ if (!queryLower)
445
+ return 0;
446
+ if (nameLower === queryLower)
447
+ return 50;
448
+ if (nameLower.startsWith(queryLower))
449
+ return 30;
450
+ if (queryLower.startsWith(nameLower))
451
+ return 20;
452
+ if (nameLower.includes(queryLower) || queryLower.includes(nameLower))
453
+ return 10;
454
+ const queryTokens = splitCamelSnake(query);
455
+ const nameTokens = splitCamelSnake(codeName);
456
+ if (queryTokens.length === 0)
457
+ return 0;
458
+ const matched = queryTokens.filter(qt => nameTokens.some(nt => nt.includes(qt) || qt.includes(nt)));
459
+ if (matched.length === queryTokens.length)
460
+ return 15 + 5 * matched.length;
461
+ if (matched.length > 0)
462
+ return 5 * matched.length;
463
+ return 0;
464
+ }
465
+ function rankNormalize(items) {
466
+ if (items.length === 0)
467
+ return [];
468
+ const n = items.length;
469
+ const sorted = [...items].sort((a, b) => b.score - a.score);
470
+ const result = new Array(n);
471
+ let i = 0;
472
+ while (i < n) {
473
+ let j = i;
474
+ while (j < n - 1 && sorted[j + 1].score === sorted[j].score)
475
+ j++;
476
+ const avgRank = (i + j) / 2;
477
+ const normalizedRank = 1 - avgRank / n;
478
+ for (let k = i; k <= j; k++) {
479
+ result[sorted[k].index] = normalizedRank;
480
+ }
481
+ i = j + 1;
482
+ }
483
+ return result;
484
+ }
485
+ function mergeAndNormalize(wiki, code, limit, query) {
486
+ const q = query ?? '';
487
+ const isIdQuery = isCodeIdentifier(q);
488
+ const hasStrongCodeMatch = code.length > 0 && code.some(r => codeNameMatchBonus(r.name, q) >= 15);
489
+ const WIKI_WEIGHT = isIdQuery ? 0.4 : hasStrongCodeMatch ? 0.5 : 0.6;
490
+ const CODE_WEIGHT = isIdQuery ? 0.6 : hasStrongCodeMatch ? 0.5 : 0.4;
491
+ const codeNames = new Set(code.map(r => r.name.toLowerCase()));
492
+ const wikiScored = wiki.map((r, i) => {
246
493
  const raw = r.score ?? 0;
494
+ let typeBoost = WIKI_TYPE_BOOST[r.type] ?? 1.0;
495
+ if (r.id.startsWith('kg-') && codeNames.has(r.title.toLowerCase())) {
496
+ typeBoost *= 0.7;
497
+ }
498
+ return { ...r, finalScore: raw * typeBoost, index: i };
499
+ });
500
+ const codeScored = code.map((r, i) => {
501
+ const raw = r.score ?? 0;
502
+ const kindBoost = CODE_KIND_BOOST[r.kind] ?? 1.0;
503
+ const nameBonus = codeNameMatchBonus(r.name, q);
504
+ return { ...r, finalScore: raw * kindBoost + nameBonus, index: i };
505
+ });
506
+ const wikiRanks = rankNormalize(wikiScored.map(r => ({ index: r.index, score: r.finalScore })));
507
+ const codeRanks = rankNormalize(codeScored.map(r => ({ index: r.index, score: r.finalScore })));
508
+ const merged = [];
509
+ for (let i = 0; i < wikiScored.length; i++) {
510
+ const r = wikiScored[i];
247
511
  merged.push({
248
512
  source: 'wiki',
249
513
  kind: r.type,
250
514
  name: r.title,
251
515
  detail: r.category ? `${r.category} ${r.id}` : r.id,
252
- normalizedScore: ((raw - wikiMM.min) / wikiRange) * WIKI_WEIGHT,
516
+ normalizedScore: wikiRanks[i] * WIKI_WEIGHT,
517
+ snippet: r.snippet ?? undefined,
253
518
  });
254
519
  }
255
- for (const r of code) {
256
- const raw = r.score ?? 0;
520
+ for (let i = 0; i < codeScored.length; i++) {
521
+ const r = codeScored[i];
257
522
  merged.push({
258
523
  source: 'code',
259
524
  kind: r.kind,
260
525
  name: r.name,
261
526
  detail: r.filePath,
262
- normalizedScore: ((raw - codeMM.min) / codeRange) * CODE_WEIGHT,
527
+ normalizedScore: codeRanks[i] * CODE_WEIGHT,
528
+ signature: r.signature,
263
529
  });
264
530
  }
265
531
  merged.sort((a, b) => b.normalizedScore - a.normalizedScore);