@cleocode/core 2026.3.74 → 2026.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (428) hide show
  1. package/README.md +1 -1
  2. package/dist/agents/agent-schema.d.ts.map +1 -1
  3. package/dist/agents/retry.js +26 -21
  4. package/dist/agents/retry.js.map +1 -1
  5. package/dist/cant/approval.d.ts +110 -0
  6. package/dist/cant/approval.d.ts.map +1 -0
  7. package/dist/cant/approval.js +185 -0
  8. package/dist/cant/approval.js.map +1 -0
  9. package/dist/cant/context-builder.d.ts +79 -0
  10. package/dist/cant/context-builder.d.ts.map +1 -0
  11. package/dist/cant/context-builder.js +117 -0
  12. package/dist/cant/context-builder.js.map +1 -0
  13. package/dist/cant/discretion.d.ts +95 -0
  14. package/dist/cant/discretion.d.ts.map +1 -0
  15. package/dist/cant/discretion.js +116 -0
  16. package/dist/cant/discretion.js.map +1 -0
  17. package/dist/cant/index.d.ts +25 -0
  18. package/dist/cant/index.d.ts.map +1 -0
  19. package/dist/cant/index.js +23 -0
  20. package/dist/cant/index.js.map +1 -0
  21. package/dist/cant/parallel-runner.d.ts +38 -0
  22. package/dist/cant/parallel-runner.d.ts.map +1 -0
  23. package/dist/cant/parallel-runner.js +173 -0
  24. package/dist/cant/parallel-runner.js.map +1 -0
  25. package/dist/cant/types.d.ts +127 -0
  26. package/dist/cant/types.d.ts.map +1 -0
  27. package/dist/cant/types.js +11 -0
  28. package/dist/cant/types.js.map +1 -0
  29. package/dist/cant/workflow-executor.d.ts +105 -0
  30. package/dist/cant/workflow-executor.d.ts.map +1 -0
  31. package/dist/cant/workflow-executor.js +440 -0
  32. package/dist/cant/workflow-executor.js.map +1 -0
  33. package/dist/cleo.js +21 -1
  34. package/dist/cleo.js.map +1 -1
  35. package/dist/code/index.d.ts +10 -0
  36. package/dist/code/index.d.ts.map +1 -0
  37. package/dist/code/outline.d.ts +51 -0
  38. package/dist/code/outline.d.ts.map +1 -0
  39. package/dist/code/parser.d.ts +30 -0
  40. package/dist/code/parser.d.ts.map +1 -0
  41. package/dist/code/search.d.ts +42 -0
  42. package/dist/code/search.d.ts.map +1 -0
  43. package/dist/code/unfold.d.ts +44 -0
  44. package/dist/code/unfold.d.ts.map +1 -0
  45. package/dist/conduit/conduit-client.d.ts +35 -0
  46. package/dist/conduit/conduit-client.d.ts.map +1 -0
  47. package/dist/conduit/conduit-client.js +94 -0
  48. package/dist/conduit/conduit-client.js.map +1 -0
  49. package/dist/conduit/factory.d.ts +15 -0
  50. package/dist/conduit/factory.d.ts.map +1 -0
  51. package/dist/conduit/factory.js +35 -0
  52. package/dist/conduit/factory.js.map +1 -0
  53. package/dist/conduit/http-transport.d.ts +44 -0
  54. package/dist/conduit/http-transport.d.ts.map +1 -0
  55. package/dist/conduit/http-transport.js +165 -0
  56. package/dist/conduit/http-transport.js.map +1 -0
  57. package/dist/conduit/index.d.ts +15 -0
  58. package/dist/conduit/index.d.ts.map +1 -0
  59. package/dist/conduit/index.js +12 -0
  60. package/dist/conduit/index.js.map +1 -0
  61. package/dist/conduit/local-transport.d.ts +91 -0
  62. package/dist/conduit/local-transport.d.ts.map +1 -0
  63. package/dist/conduit/sse-transport.d.ts +68 -0
  64. package/dist/conduit/sse-transport.d.ts.map +1 -0
  65. package/dist/config.js +4 -3
  66. package/dist/config.js.map +1 -1
  67. package/dist/crypto/credentials.d.ts +40 -0
  68. package/dist/crypto/credentials.d.ts.map +1 -0
  69. package/dist/crypto/credentials.js +144 -0
  70. package/dist/crypto/credentials.js.map +1 -0
  71. package/dist/engine-result.d.ts +1 -1
  72. package/dist/engine-result.d.ts.map +1 -1
  73. package/dist/error-catalog.d.ts +1 -1
  74. package/dist/error-catalog.d.ts.map +1 -1
  75. package/dist/error-registry.d.ts +1 -1
  76. package/dist/error-registry.d.ts.map +1 -1
  77. package/dist/errors.d.ts +1 -1
  78. package/dist/errors.d.ts.map +1 -1
  79. package/dist/hooks/handlers/agent-hooks.d.ts.map +1 -1
  80. package/dist/hooks/handlers/agent-hooks.js +106 -0
  81. package/dist/hooks/handlers/agent-hooks.js.map +1 -0
  82. package/dist/hooks/handlers/context-hooks.d.ts.map +1 -1
  83. package/dist/hooks/handlers/context-hooks.js +111 -0
  84. package/dist/hooks/handlers/context-hooks.js.map +1 -0
  85. package/dist/hooks/handlers/error-hooks.d.ts +14 -5
  86. package/dist/hooks/handlers/error-hooks.d.ts.map +1 -1
  87. package/dist/hooks/handlers/error-hooks.js +15 -6
  88. package/dist/hooks/handlers/error-hooks.js.map +1 -1
  89. package/dist/hooks/handlers/file-hooks.d.ts.map +1 -1
  90. package/dist/hooks/handlers/file-hooks.js +35 -11
  91. package/dist/hooks/handlers/file-hooks.js.map +1 -1
  92. package/dist/hooks/handlers/handler-helpers.d.ts +41 -0
  93. package/dist/hooks/handlers/handler-helpers.d.ts.map +1 -0
  94. package/dist/hooks/handlers/handler-helpers.js +61 -0
  95. package/dist/hooks/handlers/handler-helpers.js.map +1 -0
  96. package/dist/hooks/handlers/index.js +10 -1
  97. package/dist/hooks/handlers/index.js.map +1 -1
  98. package/dist/hooks/handlers/mcp-hooks.d.ts.map +1 -1
  99. package/dist/hooks/handlers/mcp-hooks.js +88 -21
  100. package/dist/hooks/handlers/mcp-hooks.js.map +1 -1
  101. package/dist/hooks/handlers/session-hooks.d.ts.map +1 -1
  102. package/dist/hooks/handlers/session-hooks.js +5 -10
  103. package/dist/hooks/handlers/session-hooks.js.map +1 -1
  104. package/dist/hooks/handlers/task-hooks.d.ts.map +1 -1
  105. package/dist/hooks/handlers/task-hooks.js +5 -10
  106. package/dist/hooks/handlers/task-hooks.js.map +1 -1
  107. package/dist/hooks/handlers/work-capture-hooks.d.ts.map +1 -1
  108. package/dist/hooks/handlers/work-capture-hooks.js +165 -0
  109. package/dist/hooks/handlers/work-capture-hooks.js.map +1 -0
  110. package/dist/hooks/payload-schemas.js +83 -26
  111. package/dist/hooks/payload-schemas.js.map +1 -1
  112. package/dist/hooks/provider-hooks.js +37 -5
  113. package/dist/hooks/provider-hooks.js.map +1 -1
  114. package/dist/hooks/registry.js +76 -23
  115. package/dist/hooks/registry.js.map +1 -1
  116. package/dist/hooks/types.js +17 -13
  117. package/dist/hooks/types.js.map +1 -1
  118. package/dist/index.d.ts +4 -1
  119. package/dist/index.d.ts.map +1 -1
  120. package/dist/index.js +6452 -3371
  121. package/dist/index.js.map +4 -4
  122. package/dist/init.d.ts.map +1 -1
  123. package/dist/init.js +12 -0
  124. package/dist/init.js.map +1 -1
  125. package/dist/internal.d.ts +11 -1
  126. package/dist/internal.d.ts.map +1 -1
  127. package/dist/internal.js +10 -0
  128. package/dist/internal.js.map +1 -1
  129. package/dist/lib/index.d.ts +1 -0
  130. package/dist/lib/index.d.ts.map +1 -1
  131. package/dist/lib/tree-sitter-languages.d.ts +29 -0
  132. package/dist/lib/tree-sitter-languages.d.ts.map +1 -0
  133. package/dist/memory/brain-links.d.ts.map +1 -1
  134. package/dist/memory/brain-maintenance.d.ts +13 -0
  135. package/dist/memory/brain-maintenance.d.ts.map +1 -1
  136. package/dist/memory/brain-retrieval.d.ts +3 -0
  137. package/dist/memory/brain-retrieval.d.ts.map +1 -1
  138. package/dist/memory/brain-retrieval.js +5 -0
  139. package/dist/memory/brain-retrieval.js.map +1 -1
  140. package/dist/memory/decisions.d.ts.map +1 -1
  141. package/dist/mvi-helpers.d.ts +52 -0
  142. package/dist/mvi-helpers.d.ts.map +1 -0
  143. package/dist/mvi-helpers.js +74 -0
  144. package/dist/mvi-helpers.js.map +1 -0
  145. package/dist/nexus/index.js +2 -0
  146. package/dist/nexus/index.js.map +1 -1
  147. package/dist/nexus/workspace.d.ts.map +1 -1
  148. package/dist/nexus/workspace.js +355 -0
  149. package/dist/nexus/workspace.js.map +1 -0
  150. package/dist/orchestration/hierarchy.d.ts +32 -0
  151. package/dist/orchestration/hierarchy.d.ts.map +1 -0
  152. package/dist/orchestration/index.d.ts +1 -0
  153. package/dist/orchestration/index.d.ts.map +1 -1
  154. package/dist/output.d.ts +2 -2
  155. package/dist/output.d.ts.map +1 -1
  156. package/dist/output.js +40 -8
  157. package/dist/output.js.map +1 -1
  158. package/dist/pagination.d.ts +1 -1
  159. package/dist/pagination.d.ts.map +1 -1
  160. package/dist/sessions/find.d.ts +3 -0
  161. package/dist/sessions/find.d.ts.map +1 -1
  162. package/dist/sessions/find.js +3 -1
  163. package/dist/sessions/find.js.map +1 -1
  164. package/dist/sessions/index.d.ts.map +1 -1
  165. package/dist/sessions/index.js +11 -4
  166. package/dist/sessions/index.js.map +1 -1
  167. package/dist/sessions/snapshot.js +213 -0
  168. package/dist/sessions/snapshot.js.map +1 -0
  169. package/dist/store/agent-registry-accessor.d.ts +31 -0
  170. package/dist/store/agent-registry-accessor.d.ts.map +1 -0
  171. package/dist/store/agent-registry-accessor.js +169 -0
  172. package/dist/store/agent-registry-accessor.js.map +1 -0
  173. package/dist/store/converters.d.ts.map +1 -1
  174. package/dist/store/converters.js +2 -0
  175. package/dist/store/converters.js.map +1 -1
  176. package/dist/store/cross-db-cleanup.d.ts +34 -0
  177. package/dist/store/cross-db-cleanup.d.ts.map +1 -1
  178. package/dist/store/db-helpers.d.ts.map +1 -1
  179. package/dist/store/db-helpers.js +1 -0
  180. package/dist/store/db-helpers.js.map +1 -1
  181. package/dist/store/json.js +2 -2
  182. package/dist/store/safety-data-accessor.d.ts +7 -0
  183. package/dist/store/safety-data-accessor.d.ts.map +1 -1
  184. package/dist/store/safety-data-accessor.js +14 -0
  185. package/dist/store/safety-data-accessor.js.map +1 -1
  186. package/dist/store/signaldock-sqlite.d.ts +48 -0
  187. package/dist/store/signaldock-sqlite.d.ts.map +1 -0
  188. package/dist/store/signaldock-sqlite.js +178 -0
  189. package/dist/store/signaldock-sqlite.js.map +1 -0
  190. package/dist/store/sqlite-data-accessor.d.ts.map +1 -1
  191. package/dist/store/sqlite-data-accessor.js +50 -0
  192. package/dist/store/sqlite-data-accessor.js.map +1 -1
  193. package/dist/store/sqlite.d.ts.map +1 -1
  194. package/dist/store/sqlite.js +30 -1
  195. package/dist/store/sqlite.js.map +1 -1
  196. package/dist/store/task-store.d.ts.map +1 -1
  197. package/dist/store/task-store.js +2 -0
  198. package/dist/store/task-store.js.map +1 -1
  199. package/dist/store/tasks-schema.d.ts +16 -0
  200. package/dist/store/tasks-schema.d.ts.map +1 -1
  201. package/dist/store/tasks-schema.js +33 -0
  202. package/dist/store/tasks-schema.js.map +1 -1
  203. package/dist/store/validation-schemas.d.ts +32 -0
  204. package/dist/store/validation-schemas.d.ts.map +1 -1
  205. package/dist/system/health.d.ts +1 -1
  206. package/dist/system/health.d.ts.map +1 -1
  207. package/dist/system/health.js +35 -0
  208. package/dist/system/health.js.map +1 -1
  209. package/dist/task-work/index.d.ts.map +1 -1
  210. package/dist/task-work/index.js +8 -4
  211. package/dist/task-work/index.js.map +1 -1
  212. package/dist/tasks/complete.js +5 -2
  213. package/dist/tasks/complete.js.map +1 -1
  214. package/dist/tasks/find.d.ts +3 -0
  215. package/dist/tasks/find.d.ts.map +1 -1
  216. package/dist/tasks/find.js +7 -1
  217. package/dist/tasks/find.js.map +1 -1
  218. package/dist/tasks/list.d.ts +5 -2
  219. package/dist/tasks/list.d.ts.map +1 -1
  220. package/dist/tasks/list.js +9 -2
  221. package/dist/tasks/list.js.map +1 -1
  222. package/dist/tasks/show.d.ts +3 -0
  223. package/dist/tasks/show.d.ts.map +1 -1
  224. package/dist/tasks/show.js +2 -0
  225. package/dist/tasks/show.js.map +1 -1
  226. package/dist/upgrade.d.ts.map +1 -1
  227. package/dist/upgrade.js +15 -0
  228. package/dist/upgrade.js.map +1 -1
  229. package/migrations/drizzle-tasks/20260324000000_assignee-column/migration.sql +6 -0
  230. package/migrations/drizzle-tasks/20260324000000_assignee-column/snapshot.json +9 -0
  231. package/migrations/drizzle-tasks/20260327000000_agent-credentials/migration.sql +23 -0
  232. package/package.json +17 -7
  233. package/src/__tests__/cli-parity.test.js +11 -1
  234. package/src/__tests__/cli-parity.test.js.map +1 -1
  235. package/src/__tests__/cli-parity.test.ts +17 -1
  236. package/src/__tests__/human-output.test.js +11 -1
  237. package/src/__tests__/human-output.test.js.map +1 -1
  238. package/src/__tests__/human-output.test.ts +18 -1
  239. package/src/__tests__/injection-chain.test.js +3 -2
  240. package/src/__tests__/injection-chain.test.js.map +1 -1
  241. package/src/__tests__/injection-mvi-tiers.test.d.ts +2 -2
  242. package/src/__tests__/injection-mvi-tiers.test.js +15 -15
  243. package/src/__tests__/injection-mvi-tiers.test.js.map +1 -1
  244. package/src/__tests__/lafs-conformance.test.d.ts +1 -1
  245. package/src/__tests__/lafs-conformance.test.js +2 -2
  246. package/src/__tests__/sharing.test.js +19 -0
  247. package/src/__tests__/sharing.test.js.map +1 -1
  248. package/src/agents/__tests__/agent-registry.test.d.ts +12 -0
  249. package/src/agents/__tests__/agent-registry.test.d.ts.map +1 -0
  250. package/src/agents/__tests__/agent-registry.test.js +262 -0
  251. package/src/agents/__tests__/agent-registry.test.js.map +1 -0
  252. package/src/agents/__tests__/execution-learning.test.d.ts +14 -0
  253. package/src/agents/__tests__/execution-learning.test.d.ts.map +1 -0
  254. package/src/agents/__tests__/execution-learning.test.js +533 -0
  255. package/src/agents/__tests__/execution-learning.test.js.map +1 -0
  256. package/src/agents/__tests__/health-monitor.test.d.ts +10 -0
  257. package/src/agents/__tests__/health-monitor.test.d.ts.map +1 -0
  258. package/src/agents/__tests__/health-monitor.test.js +259 -0
  259. package/src/agents/__tests__/health-monitor.test.js.map +1 -0
  260. package/src/agents/__tests__/registry.test.js +27 -2
  261. package/src/agents/__tests__/registry.test.js.map +1 -1
  262. package/src/agents/agent-schema.ts +2 -5
  263. package/src/cant/__tests__/cant-agent-parse.test.ts +94 -0
  264. package/src/cant/approval.ts +218 -0
  265. package/src/cant/context-builder.ts +135 -0
  266. package/src/cant/discretion.ts +149 -0
  267. package/src/cant/index.ts +58 -0
  268. package/src/cant/parallel-runner.ts +205 -0
  269. package/src/cant/types.ts +158 -0
  270. package/src/cant/workflow-executor.ts +618 -0
  271. package/src/code/index.ts +10 -0
  272. package/src/code/outline.ts +214 -0
  273. package/src/code/parser.ts +299 -0
  274. package/src/code/search.ts +173 -0
  275. package/src/code/unfold.ts +204 -0
  276. package/src/conduit/__tests__/dual-api-e2e.test.ts +212 -0
  277. package/src/conduit/__tests__/local-credential-flow.test.ts +230 -0
  278. package/src/conduit/__tests__/local-transport.test.ts +320 -0
  279. package/src/conduit/__tests__/sse-transport.test.ts +344 -0
  280. package/src/conduit/conduit-client.ts +123 -0
  281. package/src/conduit/factory.ts +49 -0
  282. package/src/conduit/http-transport.ts +201 -0
  283. package/src/conduit/index.ts +15 -0
  284. package/src/conduit/local-transport.ts +309 -0
  285. package/src/conduit/sse-transport.ts +382 -0
  286. package/src/crypto/credentials.ts +166 -0
  287. package/src/engine-result.ts +1 -1
  288. package/src/error-catalog.ts +1 -1
  289. package/src/error-registry.ts +1 -1
  290. package/src/errors.ts +1 -1
  291. package/src/hooks/handlers/__tests__/hook-automation-e2e.test.d.ts +13 -0
  292. package/src/hooks/handlers/__tests__/hook-automation-e2e.test.d.ts.map +1 -0
  293. package/src/hooks/handlers/__tests__/hook-automation-e2e.test.js +501 -0
  294. package/src/hooks/handlers/__tests__/hook-automation-e2e.test.js.map +1 -0
  295. package/src/hooks/handlers/agent-hooks.ts +1 -30
  296. package/src/hooks/handlers/context-hooks.ts +1 -30
  297. package/src/hooks/handlers/error-hooks.ts +14 -5
  298. package/src/hooks/handlers/file-hooks.ts +1 -6
  299. package/src/hooks/handlers/handler-helpers.ts +62 -0
  300. package/src/hooks/handlers/mcp-hooks.ts +2 -14
  301. package/src/hooks/handlers/session-hooks.ts +1 -6
  302. package/src/hooks/handlers/task-hooks.ts +1 -6
  303. package/src/hooks/handlers/work-capture-hooks.ts +1 -10
  304. package/src/index.ts +12 -1
  305. package/src/init.ts +12 -0
  306. package/src/intelligence/__tests__/adaptive-validation.test.d.ts +11 -0
  307. package/src/intelligence/__tests__/adaptive-validation.test.d.ts.map +1 -0
  308. package/src/intelligence/__tests__/adaptive-validation.test.js +517 -0
  309. package/src/intelligence/__tests__/adaptive-validation.test.js.map +1 -0
  310. package/src/intelligence/__tests__/impact.test.d.ts +1 -0
  311. package/src/intelligence/__tests__/impact.test.d.ts.map +1 -1
  312. package/src/intelligence/__tests__/impact.test.js +132 -1
  313. package/src/intelligence/__tests__/impact.test.js.map +1 -1
  314. package/src/internal.ts +22 -0
  315. package/src/lib/__tests__/retry.test.d.ts +7 -0
  316. package/src/lib/__tests__/retry.test.d.ts.map +1 -0
  317. package/src/lib/__tests__/retry.test.js +225 -0
  318. package/src/lib/__tests__/retry.test.js.map +1 -0
  319. package/src/lib/index.ts +8 -0
  320. package/src/lib/tree-sitter-languages.ts +88 -0
  321. package/src/lifecycle/__tests__/chain-store.test.js +6 -0
  322. package/src/lifecycle/__tests__/chain-store.test.js.map +1 -1
  323. package/src/lifecycle/__tests__/tessera-engine.test.js +52 -0
  324. package/src/lifecycle/__tests__/tessera-engine.test.js.map +1 -1
  325. package/src/memory/__tests__/brain-automation.test.d.ts +11 -0
  326. package/src/memory/__tests__/brain-automation.test.d.ts.map +1 -0
  327. package/src/memory/__tests__/brain-automation.test.js +730 -0
  328. package/src/memory/__tests__/brain-automation.test.js.map +1 -0
  329. package/src/memory/__tests__/brain-links.test.ts +14 -0
  330. package/src/memory/__tests__/brain-retrieval.test.ts +10 -0
  331. package/src/memory/__tests__/session-memory.test.ts +17 -0
  332. package/src/memory/brain-links.ts +17 -0
  333. package/src/memory/brain-maintenance.ts +33 -1
  334. package/src/memory/brain-retrieval.ts +27 -2
  335. package/src/memory/decisions.ts +18 -2
  336. package/src/mvi-helpers.ts +81 -0
  337. package/src/nexus/workspace.ts +19 -7
  338. package/src/orchestration/hierarchy.ts +202 -0
  339. package/src/orchestration/index.ts +1 -0
  340. package/src/output.ts +43 -10
  341. package/src/pagination.ts +1 -1
  342. package/src/sessions/__tests__/session-edge-cases.test.js +20 -1
  343. package/src/sessions/__tests__/session-edge-cases.test.js.map +1 -1
  344. package/src/sessions/__tests__/session-find.test.js +1 -1
  345. package/src/sessions/__tests__/session-find.test.js.map +1 -1
  346. package/src/sessions/__tests__/session-find.test.ts +1 -1
  347. package/src/sessions/find.ts +6 -1
  348. package/src/sessions/index.ts +9 -0
  349. package/src/store/__tests__/migration-safety.test.js +3 -0
  350. package/src/store/__tests__/migration-safety.test.js.map +1 -1
  351. package/src/store/__tests__/session-store.test.js +128 -1
  352. package/src/store/__tests__/session-store.test.js.map +1 -1
  353. package/src/store/__tests__/task-store.test.js +18 -1
  354. package/src/store/__tests__/task-store.test.js.map +1 -1
  355. package/src/store/__tests__/test-db-helper.d.ts.map +1 -1
  356. package/src/store/__tests__/test-db-helper.js +12 -0
  357. package/src/store/__tests__/test-db-helper.js.map +1 -1
  358. package/src/store/agent-registry-accessor.ts +375 -0
  359. package/src/store/converters.ts +2 -0
  360. package/src/store/cross-db-cleanup.ts +175 -1
  361. package/src/store/db-helpers.ts +1 -0
  362. package/src/store/safety-data-accessor.ts +23 -0
  363. package/src/store/signaldock-sqlite.ts +429 -0
  364. package/src/store/sqlite-data-accessor.ts +72 -0
  365. package/src/store/sqlite.ts +4 -1
  366. package/src/store/task-store.ts +9 -1
  367. package/src/store/tasks-schema.ts +7 -0
  368. package/src/system/__tests__/health.test.ts +2 -2
  369. package/src/system/health.ts +54 -2
  370. package/src/task-work/index.ts +5 -0
  371. package/src/tasks/__tests__/add.test.js +19 -1
  372. package/src/tasks/__tests__/add.test.js.map +1 -1
  373. package/src/tasks/__tests__/assignee.test.d.ts +14 -0
  374. package/src/tasks/__tests__/assignee.test.d.ts.map +1 -0
  375. package/src/tasks/__tests__/assignee.test.js +125 -0
  376. package/src/tasks/__tests__/assignee.test.js.map +1 -0
  377. package/src/tasks/__tests__/assignee.test.ts +162 -0
  378. package/src/tasks/__tests__/complete-unblocks.test.js +13 -1
  379. package/src/tasks/__tests__/complete-unblocks.test.js.map +1 -1
  380. package/src/tasks/__tests__/complete.test.js +28 -7
  381. package/src/tasks/__tests__/complete.test.js.map +1 -1
  382. package/src/tasks/__tests__/epic-enforcement.test.d.ts +15 -0
  383. package/src/tasks/__tests__/epic-enforcement.test.d.ts.map +1 -0
  384. package/src/tasks/__tests__/epic-enforcement.test.js +669 -0
  385. package/src/tasks/__tests__/epic-enforcement.test.js.map +1 -0
  386. package/src/tasks/__tests__/hierarchy-policy.test.js +5 -0
  387. package/src/tasks/__tests__/hierarchy-policy.test.js.map +1 -1
  388. package/src/tasks/__tests__/minimal-test.test.d.ts +2 -0
  389. package/src/tasks/__tests__/minimal-test.test.d.ts.map +1 -0
  390. package/src/tasks/__tests__/minimal-test.test.js +25 -0
  391. package/src/tasks/__tests__/minimal-test.test.js.map +1 -0
  392. package/src/tasks/__tests__/pipeline-stage.test.d.ts +14 -0
  393. package/src/tasks/__tests__/pipeline-stage.test.d.ts.map +1 -0
  394. package/src/tasks/__tests__/pipeline-stage.test.js +277 -0
  395. package/src/tasks/__tests__/pipeline-stage.test.js.map +1 -0
  396. package/src/tasks/__tests__/update.test.js +43 -6
  397. package/src/tasks/__tests__/update.test.js.map +1 -1
  398. package/src/tasks/find.ts +11 -1
  399. package/src/tasks/list.ts +14 -3
  400. package/src/tasks/show.ts +6 -0
  401. package/src/upgrade.ts +16 -0
  402. package/dist/tasks/reparent.d.ts +0 -38
  403. package/dist/tasks/reparent.d.ts.map +0 -1
  404. package/dist/ui/injection-legacy.d.ts +0 -26
  405. package/dist/ui/injection-legacy.d.ts.map +0 -1
  406. package/dist/ui/injection-legacy.js +0 -42
  407. package/dist/ui/injection-legacy.js.map +0 -1
  408. package/src/signaldock/__tests__/claude-code-transport.test.d.ts +0 -7
  409. package/src/signaldock/__tests__/claude-code-transport.test.d.ts.map +0 -1
  410. package/src/signaldock/__tests__/claude-code-transport.test.js +0 -147
  411. package/src/signaldock/__tests__/claude-code-transport.test.js.map +0 -1
  412. package/src/signaldock/__tests__/claude-code-transport.test.ts +0 -180
  413. package/src/signaldock/__tests__/factory.test.d.ts +0 -7
  414. package/src/signaldock/__tests__/factory.test.d.ts.map +0 -1
  415. package/src/signaldock/__tests__/factory.test.js +0 -55
  416. package/src/signaldock/__tests__/factory.test.js.map +0 -1
  417. package/src/signaldock/__tests__/factory.test.ts +0 -61
  418. package/src/signaldock/__tests__/signaldock-transport.test.d.ts +0 -9
  419. package/src/signaldock/__tests__/signaldock-transport.test.d.ts.map +0 -1
  420. package/src/signaldock/__tests__/signaldock-transport.test.js +0 -321
  421. package/src/signaldock/__tests__/signaldock-transport.test.js.map +0 -1
  422. package/src/signaldock/__tests__/signaldock-transport.test.ts +0 -421
  423. package/src/signaldock/claude-code-transport.ts +0 -137
  424. package/src/signaldock/factory.ts +0 -39
  425. package/src/signaldock/index.ts +0 -28
  426. package/src/signaldock/signaldock-transport.ts +0 -194
  427. package/src/signaldock/transport.ts +0 -78
  428. package/src/signaldock/types.ts +0 -100
@@ -0,0 +1,214 @@
1
+ /**
2
+ * smart_outline — file structural skeleton via tree-sitter.
3
+ *
4
+ * Parses a source file and returns all top-level and nested symbols
5
+ * with signatures only (bodies collapsed). This gives agents a ~1-2K
6
+ * token overview of a file vs ~12K for a full Read.
7
+ *
8
+ * @task T151
9
+ */
10
+
11
+ import { readFileSync } from 'node:fs';
12
+ import { relative } from 'node:path';
13
+ import type { CodeSymbol } from '@cleocode/contracts';
14
+ import { detectLanguage } from '../lib/tree-sitter-languages.js';
15
+ import { parseFile } from './parser.js';
16
+
17
+ /** A symbol node in the outline tree, with optional children. */
18
+ export interface OutlineNode {
19
+ /** Symbol name. */
20
+ name: string;
21
+ /** Symbol kind (function, class, method, etc.). */
22
+ kind: string;
23
+ /** Start line (1-based). */
24
+ startLine: number;
25
+ /** End line (1-based). */
26
+ endLine: number;
27
+ /** Signature line(s) — the declaration without the body. */
28
+ signature: string;
29
+ /** Whether this symbol is exported. */
30
+ exported: boolean;
31
+ /** Nested symbols (methods inside classes, etc.). */
32
+ children: OutlineNode[];
33
+ }
34
+
35
+ /** Result of generating a smart outline for a file. */
36
+ export interface SmartOutlineResult {
37
+ /** Source file path (relative). */
38
+ filePath: string;
39
+ /** Detected language. */
40
+ language: string;
41
+ /** Top-level symbol tree. */
42
+ symbols: OutlineNode[];
43
+ /** Estimated token count for this outline. */
44
+ estimatedTokens: number;
45
+ /** Parse errors (non-fatal). */
46
+ errors: string[];
47
+ }
48
+
49
+ /**
50
+ * Extract the signature line(s) from source for a symbol.
51
+ *
52
+ * Reads the opening lines of the symbol declaration up to the first
53
+ * opening brace or colon (for Python). This gives context without
54
+ * the full implementation body.
55
+ */
56
+ function extractSignature(
57
+ lines: string[],
58
+ startLine: number,
59
+ endLine: number,
60
+ language: string,
61
+ ): string {
62
+ const start = Math.max(0, startLine - 1); // 1-based to 0-based
63
+ const end = Math.min(lines.length, endLine);
64
+
65
+ // For short symbols (< 3 lines), return the whole thing
66
+ if (end - start <= 3) {
67
+ return lines.slice(start, end).join('\n');
68
+ }
69
+
70
+ // Find the signature boundary (opening brace, colon for Python)
71
+ const sigLines: string[] = [];
72
+ for (let i = start; i < end && sigLines.length < 5; i++) {
73
+ const line = lines[i]!;
74
+ sigLines.push(line);
75
+
76
+ if (language === 'python') {
77
+ if (line.trimEnd().endsWith(':')) break;
78
+ } else {
79
+ if (line.includes('{')) break;
80
+ }
81
+ }
82
+
83
+ const bodyLines = end - start - sigLines.length;
84
+ if (bodyLines > 0) {
85
+ sigLines.push(` // ... ${bodyLines} lines`);
86
+ }
87
+
88
+ return sigLines.join('\n');
89
+ }
90
+
91
+ /**
92
+ * Build a tree from flat symbols by nesting methods inside their parent classes.
93
+ */
94
+ function buildTree(symbols: CodeSymbol[], lines: string[], language: string): OutlineNode[] {
95
+ const nodes: OutlineNode[] = [];
96
+ const classNodes = new Map<string, OutlineNode>();
97
+
98
+ // First pass: create all nodes
99
+ for (const sym of symbols) {
100
+ const node: OutlineNode = {
101
+ name: sym.name,
102
+ kind: sym.kind,
103
+ startLine: sym.startLine,
104
+ endLine: sym.endLine,
105
+ signature: extractSignature(lines, sym.startLine, sym.endLine, language),
106
+ exported: sym.exported ?? false,
107
+ children: [],
108
+ };
109
+
110
+ if (
111
+ sym.kind === 'class' ||
112
+ sym.kind === 'struct' ||
113
+ sym.kind === 'impl' ||
114
+ sym.kind === 'trait'
115
+ ) {
116
+ classNodes.set(sym.name, node);
117
+ nodes.push(node);
118
+ } else if (sym.kind === 'method' && sym.parent) {
119
+ const parent = classNodes.get(sym.parent);
120
+ if (parent) {
121
+ parent.children.push(node);
122
+ } else {
123
+ nodes.push(node);
124
+ }
125
+ } else {
126
+ // Check if this symbol is nested inside a class by line range
127
+ let nested = false;
128
+ for (const [, classNode] of classNodes) {
129
+ if (sym.startLine >= classNode.startLine && sym.endLine <= classNode.endLine) {
130
+ classNode.children.push(node);
131
+ nested = true;
132
+ break;
133
+ }
134
+ }
135
+ if (!nested) nodes.push(node);
136
+ }
137
+ }
138
+
139
+ return nodes;
140
+ }
141
+
142
+ /** Estimate token count for an outline (rough: ~4 chars per token). */
143
+ function estimateTokens(nodes: OutlineNode[]): number {
144
+ let chars = 0;
145
+ for (const node of nodes) {
146
+ chars += node.signature.length + node.name.length + node.kind.length + 20; // overhead
147
+ chars += estimateTokens(node.children);
148
+ }
149
+ return Math.ceil(chars / 4);
150
+ }
151
+
152
+ /**
153
+ * Generate a smart outline for a source file.
154
+ *
155
+ * Returns a tree of symbols with signatures only (bodies collapsed),
156
+ * suitable for giving agents a quick structural overview.
157
+ *
158
+ * @param filePath - Absolute path to source file
159
+ * @param projectRoot - Project root for relative path computation
160
+ * @returns Smart outline result with symbol tree and token estimate
161
+ */
162
+ export function smartOutline(filePath: string, projectRoot?: string): SmartOutlineResult {
163
+ const root = projectRoot ?? process.cwd();
164
+ const relPath = relative(root, filePath);
165
+ const language = detectLanguage(filePath);
166
+
167
+ if (!language) {
168
+ return {
169
+ filePath: relPath,
170
+ language: 'unknown',
171
+ symbols: [],
172
+ estimatedTokens: 0,
173
+ errors: ['Unsupported language'],
174
+ };
175
+ }
176
+
177
+ // Parse the file for symbols
178
+ const parseResult = parseFile(filePath, root);
179
+ if (parseResult.errors.length > 0 && parseResult.symbols.length === 0) {
180
+ return {
181
+ filePath: relPath,
182
+ language,
183
+ symbols: [],
184
+ estimatedTokens: 0,
185
+ errors: parseResult.errors,
186
+ };
187
+ }
188
+
189
+ // Read source lines for signature extraction
190
+ let lines: string[];
191
+ try {
192
+ lines = readFileSync(filePath, 'utf-8').split('\n');
193
+ } catch (err) {
194
+ return {
195
+ filePath: relPath,
196
+ language,
197
+ symbols: [],
198
+ estimatedTokens: 0,
199
+ errors: [`Failed to read file: ${err instanceof Error ? err.message : String(err)}`],
200
+ };
201
+ }
202
+
203
+ // Build symbol tree with signatures
204
+ const tree = buildTree(parseResult.symbols, lines, language);
205
+ const estimatedTokens = estimateTokens(tree);
206
+
207
+ return {
208
+ filePath: relPath,
209
+ language,
210
+ symbols: tree,
211
+ estimatedTokens,
212
+ errors: parseResult.errors,
213
+ };
214
+ }
@@ -0,0 +1,299 @@
1
+ /**
2
+ * Tree-sitter AST parser — query execution engine for code analysis.
3
+ *
4
+ * Resolves grammar paths from node_modules, writes S-expression query
5
+ * patterns to temp files, executes tree-sitter CLI, and parses output
6
+ * into structured CodeSymbol objects.
7
+ *
8
+ * @task T149
9
+ */
10
+
11
+ import { execFileSync } from 'node:child_process';
12
+ import { existsSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
13
+ import { tmpdir } from 'node:os';
14
+ import { join, relative } from 'node:path';
15
+ import type {
16
+ BatchParseResult,
17
+ CodeSymbol,
18
+ CodeSymbolKind,
19
+ ParseResult,
20
+ } from '@cleocode/contracts';
21
+ import { detectLanguage, type TreeSitterLanguage } from '../lib/tree-sitter-languages.js';
22
+
23
+ // ---------------------------------------------------------------------------
24
+ // Tree-sitter CLI resolution
25
+ // ---------------------------------------------------------------------------
26
+
27
+ /** Resolve the tree-sitter CLI binary from node_modules. */
28
+ function resolveTreeSitterBin(): string {
29
+ const candidates = [
30
+ join(process.cwd(), 'packages', 'core', 'node_modules', '.bin', 'tree-sitter'),
31
+ join(process.cwd(), 'node_modules', '.bin', 'tree-sitter'),
32
+ ];
33
+ for (const p of candidates) {
34
+ if (existsSync(p)) return p;
35
+ }
36
+ throw new Error('tree-sitter CLI not found. Run: pnpm add -F @cleocode/core tree-sitter-cli');
37
+ }
38
+
39
+ // ---------------------------------------------------------------------------
40
+ // S-expression query patterns per language family
41
+ // ---------------------------------------------------------------------------
42
+
43
+ /** Query patterns that match function/method/class/type declarations. */
44
+ const QUERY_PATTERNS: Record<string, string> = {
45
+ // TypeScript / JavaScript
46
+ typescript: `
47
+ (function_declaration name: (identifier) @name) @definition.function
48
+ (method_definition name: (property_identifier) @name) @definition.method
49
+ (class_declaration name: (type_identifier) @name) @definition.class
50
+ (interface_declaration name: (type_identifier) @name) @definition.interface
51
+ (type_alias_declaration name: (type_identifier) @name) @definition.type
52
+ (enum_declaration name: (identifier) @name) @definition.enum
53
+ (lexical_declaration (variable_declarator name: (identifier) @name)) @definition.variable
54
+ (export_statement (function_declaration name: (identifier) @name)) @definition.function
55
+ (export_statement (class_declaration name: (type_identifier) @name)) @definition.class
56
+ (arrow_function) @definition.function
57
+ `,
58
+ javascript: `
59
+ (function_declaration name: (identifier) @name) @definition.function
60
+ (method_definition name: (property_identifier) @name) @definition.method
61
+ (class_declaration name: (identifier) @name) @definition.class
62
+ (lexical_declaration (variable_declarator name: (identifier) @name)) @definition.variable
63
+ (export_statement (function_declaration name: (identifier) @name)) @definition.function
64
+ (export_statement (class_declaration name: (identifier) @name)) @definition.class
65
+ (arrow_function) @definition.function
66
+ `,
67
+ // Python
68
+ python: `
69
+ (function_definition name: (identifier) @name) @definition.function
70
+ (class_definition name: (identifier) @name) @definition.class
71
+ (decorated_definition (function_definition name: (identifier) @name)) @definition.function
72
+ (decorated_definition (class_definition name: (identifier) @name)) @definition.class
73
+ `,
74
+ // Go
75
+ go: `
76
+ (function_declaration name: (identifier) @name) @definition.function
77
+ (method_declaration name: (field_identifier) @name) @definition.method
78
+ (type_declaration (type_spec name: (type_identifier) @name)) @definition.type
79
+ `,
80
+ // Rust
81
+ rust: `
82
+ (function_item name: (identifier) @name) @definition.function
83
+ (impl_item type: (type_identifier) @name) @definition.impl
84
+ (struct_item name: (type_identifier) @name) @definition.struct
85
+ (enum_item name: (type_identifier) @name) @definition.enum
86
+ (trait_item name: (type_identifier) @name) @definition.trait
87
+ (type_item name: (type_identifier) @name) @definition.type
88
+ (mod_item name: (identifier) @name) @definition.module
89
+ `,
90
+ // Ruby
91
+ ruby: `
92
+ (method name: (identifier) @name) @definition.method
93
+ (class name: (constant) @name) @definition.class
94
+ (module name: (constant) @name) @definition.module
95
+ (singleton_method name: (identifier) @name) @definition.method
96
+ `,
97
+ // Java / C / C++
98
+ java: `
99
+ (method_declaration name: (identifier) @name) @definition.method
100
+ (class_declaration name: (identifier) @name) @definition.class
101
+ (interface_declaration name: (identifier) @name) @definition.interface
102
+ (enum_declaration name: (identifier) @name) @definition.enum
103
+ `,
104
+ c: `
105
+ (function_definition declarator: (function_declarator declarator: (identifier) @name)) @definition.function
106
+ (struct_specifier name: (type_identifier) @name) @definition.struct
107
+ (enum_specifier name: (type_identifier) @name) @definition.enum
108
+ (type_definition declarator: (type_identifier) @name) @definition.type
109
+ `,
110
+ cpp: `
111
+ (function_definition declarator: (function_declarator declarator: (qualified_identifier) @name)) @definition.function
112
+ (function_definition declarator: (function_declarator declarator: (identifier) @name)) @definition.function
113
+ (class_specifier name: (type_identifier) @name) @definition.class
114
+ (struct_specifier name: (type_identifier) @name) @definition.struct
115
+ (enum_specifier name: (type_identifier) @name) @definition.enum
116
+ (namespace_definition name: (identifier) @name) @definition.module
117
+ `,
118
+ };
119
+
120
+ /** Map language to query pattern key. */
121
+ function queryKeyForLanguage(lang: TreeSitterLanguage): string {
122
+ if (lang === 'tsx') return 'typescript';
123
+ return lang;
124
+ }
125
+
126
+ /** Map capture name suffix to CodeSymbolKind. */
127
+ function captureToKind(capture: string): CodeSymbolKind {
128
+ const map: Record<string, CodeSymbolKind> = {
129
+ function: 'function',
130
+ method: 'method',
131
+ class: 'class',
132
+ interface: 'interface',
133
+ type: 'type',
134
+ enum: 'enum',
135
+ variable: 'variable',
136
+ constant: 'constant',
137
+ module: 'module',
138
+ import: 'import',
139
+ export: 'export',
140
+ struct: 'struct',
141
+ trait: 'trait',
142
+ impl: 'impl',
143
+ };
144
+ return map[capture] ?? 'function';
145
+ }
146
+
147
+ // ---------------------------------------------------------------------------
148
+ // Output parsing
149
+ // ---------------------------------------------------------------------------
150
+
151
+ /**
152
+ * Parse tree-sitter query output into CodeSymbol objects.
153
+ *
154
+ * tree-sitter query output format (one match per block):
155
+ * ```
156
+ * path/to/file.ts
157
+ * pattern: 0
158
+ * capture: 0 - name, start: (5, 9), end: (5, 20), text: `parseFile`
159
+ * capture: 1 - definition.function, start: (5, 0), end: (15, 1)
160
+ * ```
161
+ */
162
+ function parseQueryOutput(output: string, filePath: string, language: string): CodeSymbol[] {
163
+ const symbols: CodeSymbol[] = [];
164
+ const lines = output.split('\n');
165
+
166
+ let currentName = '';
167
+ let currentKind: CodeSymbolKind = 'function';
168
+ let endLine = 0;
169
+
170
+ for (const line of lines) {
171
+ const nameMatch = line.match(
172
+ /capture: \d+ - name, start: \(\d+, \d+\), end: \(\d+, \d+\)(?:, text: `([^`]+)`)?/,
173
+ );
174
+ if (nameMatch) {
175
+ currentName = nameMatch[1] ?? '';
176
+ continue;
177
+ }
178
+
179
+ const defMatch = line.match(
180
+ /capture: \d+ - definition\.(\w+), start: \((\d+), \d+\), end: \((\d+), \d+\)/,
181
+ );
182
+ if (defMatch && currentName) {
183
+ currentKind = captureToKind(defMatch[1]!);
184
+ const defStart = Number.parseInt(defMatch[2]!, 10) + 1;
185
+ endLine = Number.parseInt(defMatch[3]!, 10) + 1;
186
+
187
+ symbols.push({
188
+ name: currentName,
189
+ kind: currentKind,
190
+ startLine: defStart,
191
+ endLine,
192
+ filePath,
193
+ language,
194
+ });
195
+ currentName = '';
196
+ }
197
+ }
198
+
199
+ return symbols;
200
+ }
201
+
202
+ // ---------------------------------------------------------------------------
203
+ // Public API
204
+ // ---------------------------------------------------------------------------
205
+
206
+ /**
207
+ * Parse a single file and extract code symbols.
208
+ *
209
+ * @param filePath - Absolute or relative path to source file
210
+ * @param projectRoot - Project root for relative path computation
211
+ * @returns Parse result with symbols and any errors
212
+ */
213
+ export function parseFile(filePath: string, projectRoot?: string): ParseResult {
214
+ const root = projectRoot ?? process.cwd();
215
+ const relPath = relative(root, filePath);
216
+ const language = detectLanguage(filePath);
217
+
218
+ if (!language) {
219
+ return {
220
+ filePath: relPath,
221
+ language: 'unknown',
222
+ symbols: [],
223
+ errors: ['Unsupported language'],
224
+ };
225
+ }
226
+
227
+ const queryKey = queryKeyForLanguage(language);
228
+ const pattern = QUERY_PATTERNS[queryKey];
229
+ if (!pattern) {
230
+ return {
231
+ filePath: relPath,
232
+ language,
233
+ symbols: [],
234
+ errors: [`No query pattern for ${language}`],
235
+ };
236
+ }
237
+
238
+ const tmpDir = mkdtempSync(join(tmpdir(), 'cleo-ts-'));
239
+ const queryFile = join(tmpDir, 'query.scm');
240
+
241
+ try {
242
+ writeFileSync(queryFile, pattern);
243
+ const bin = resolveTreeSitterBin();
244
+
245
+ const output = execFileSync(bin, ['query', queryFile, filePath], {
246
+ encoding: 'utf-8',
247
+ timeout: 30000,
248
+ maxBuffer: 10 * 1024 * 1024, // 10MB
249
+ });
250
+
251
+ const symbols = parseQueryOutput(output, relPath, language);
252
+ return { filePath: relPath, language, symbols, errors: [] };
253
+ } catch (err) {
254
+ const msg = err instanceof Error ? err.message : String(err);
255
+ return { filePath: relPath, language, symbols: [], errors: [msg] };
256
+ } finally {
257
+ rmSync(tmpDir, { recursive: true, force: true });
258
+ }
259
+ }
260
+
261
+ /**
262
+ * Batch-parse multiple files, grouping by language for efficiency.
263
+ *
264
+ * Files with the same language share a single query pattern file
265
+ * but each file is parsed individually (tree-sitter CLI limitation).
266
+ *
267
+ * @param filePaths - Array of file paths to parse
268
+ * @param projectRoot - Project root for relative path computation
269
+ * @returns Aggregated results with per-file breakdowns
270
+ */
271
+ export function batchParse(filePaths: string[], projectRoot?: string): BatchParseResult {
272
+ const root = projectRoot ?? process.cwd();
273
+ const results: ParseResult[] = [];
274
+ const skipped: string[] = [];
275
+
276
+ // Group files by language
277
+ const byLanguage = new Map<string, string[]>();
278
+ for (const fp of filePaths) {
279
+ const lang = detectLanguage(fp);
280
+ if (!lang) {
281
+ skipped.push(relative(root, fp));
282
+ continue;
283
+ }
284
+ const key = queryKeyForLanguage(lang);
285
+ const group = byLanguage.get(key) ?? [];
286
+ group.push(fp);
287
+ byLanguage.set(key, group);
288
+ }
289
+
290
+ // Parse each group
291
+ for (const [_queryKey, files] of byLanguage) {
292
+ for (const fp of files) {
293
+ results.push(parseFile(fp, root));
294
+ }
295
+ }
296
+
297
+ const totalSymbols = results.reduce((sum, r) => sum + r.symbols.length, 0);
298
+ return { results, skipped, totalSymbols };
299
+ }
@@ -0,0 +1,173 @@
1
+ /**
2
+ * smart_search — cross-codebase symbol search via tree-sitter.
3
+ *
4
+ * Walks a directory tree, batch-parses code files by language, and
5
+ * matches symbols against a query string with relevance scoring.
6
+ *
7
+ * @task T152
8
+ */
9
+
10
+ import { readdirSync } from 'node:fs';
11
+ import { join, relative } from 'node:path';
12
+ import type { CodeSymbol } from '@cleocode/contracts';
13
+ import { detectLanguage, type TreeSitterLanguage } from '../lib/tree-sitter-languages.js';
14
+ import { batchParse } from './parser.js';
15
+
16
+ /** A search result with relevance score. */
17
+ export interface SmartSearchResult {
18
+ /** The matched symbol. */
19
+ symbol: CodeSymbol;
20
+ /** Relevance score (higher = better match). */
21
+ score: number;
22
+ /** How the query matched (exact, substring, fuzzy, path). */
23
+ matchType: 'exact' | 'substring' | 'fuzzy' | 'path';
24
+ }
25
+
26
+ /** Options for smart_search. */
27
+ export interface SmartSearchOptions {
28
+ /** Maximum results to return (default: 20). */
29
+ maxResults?: number;
30
+ /** Glob-like file pattern filter (e.g. "*.ts", "src/**"). */
31
+ filePattern?: string;
32
+ /** Restrict to specific language. */
33
+ language?: TreeSitterLanguage;
34
+ /** Root directory to search (default: cwd). */
35
+ rootDir?: string;
36
+ }
37
+
38
+ /**
39
+ * Walk a directory tree and collect source files.
40
+ *
41
+ * Respects common ignore patterns (node_modules, dist, .git, etc.)
42
+ * and optionally filters by file pattern and language.
43
+ */
44
+ function collectSourceFiles(dir: string, options: SmartSearchOptions): string[] {
45
+ const IGNORE_DIRS = new Set([
46
+ 'node_modules',
47
+ 'dist',
48
+ '.git',
49
+ '.cleo',
50
+ 'target',
51
+ '__pycache__',
52
+ '.next',
53
+ '.nuxt',
54
+ 'build',
55
+ 'coverage',
56
+ '.turbo',
57
+ '.cache',
58
+ ]);
59
+
60
+ const files: string[] = [];
61
+
62
+ function walk(currentDir: string): void {
63
+ let entries: { isDirectory(): boolean; isFile(): boolean; name: string }[];
64
+ try {
65
+ entries = readdirSync(currentDir, { withFileTypes: true, encoding: 'utf-8' }) as {
66
+ isDirectory(): boolean;
67
+ isFile(): boolean;
68
+ name: string;
69
+ }[];
70
+ } catch {
71
+ return;
72
+ }
73
+
74
+ for (const entry of entries) {
75
+ if (entry.isDirectory()) {
76
+ if (!IGNORE_DIRS.has(entry.name) && !entry.name.startsWith('.')) {
77
+ walk(join(currentDir, entry.name));
78
+ }
79
+ } else if (entry.isFile()) {
80
+ const fullPath = join(currentDir, entry.name);
81
+ const lang = detectLanguage(entry.name);
82
+ if (!lang) continue;
83
+
84
+ // Language filter
85
+ if (options.language && lang !== options.language) continue;
86
+
87
+ // File pattern filter (simple glob: *.ext or dir/**)
88
+ if (options.filePattern) {
89
+ const relPath = relative(dir, fullPath);
90
+ if (options.filePattern.startsWith('*.')) {
91
+ const ext = options.filePattern.slice(1);
92
+ if (!entry.name.endsWith(ext)) continue;
93
+ } else if (!relPath.includes(options.filePattern.replace('/**', ''))) {
94
+ continue;
95
+ }
96
+ }
97
+
98
+ files.push(fullPath);
99
+ }
100
+ }
101
+ }
102
+
103
+ walk(dir);
104
+ return files;
105
+ }
106
+
107
+ /**
108
+ * Score a symbol against a query string.
109
+ *
110
+ * Scoring: exact name match (10) > substring in name (5) >
111
+ * path match (3) > fuzzy (1).
112
+ */
113
+ function scoreSymbol(
114
+ symbol: CodeSymbol,
115
+ query: string,
116
+ ): { score: number; matchType: SmartSearchResult['matchType'] } {
117
+ const q = query.toLowerCase();
118
+ const name = symbol.name.toLowerCase();
119
+ const path = symbol.filePath.toLowerCase();
120
+
121
+ // Exact name match
122
+ if (name === q) return { score: 10, matchType: 'exact' };
123
+
124
+ // Substring in name
125
+ if (name.includes(q)) return { score: 5, matchType: 'substring' };
126
+
127
+ // Path match
128
+ if (path.includes(q)) return { score: 3, matchType: 'path' };
129
+
130
+ // Fuzzy: all query chars appear in order in the name
131
+ let qi = 0;
132
+ for (let ni = 0; ni < name.length && qi < q.length; ni++) {
133
+ if (name[ni] === q[qi]) qi++;
134
+ }
135
+ if (qi === q.length) return { score: 1, matchType: 'fuzzy' };
136
+
137
+ return { score: 0, matchType: 'fuzzy' };
138
+ }
139
+
140
+ /**
141
+ * Search for symbols across a codebase.
142
+ *
143
+ * Walks the directory tree, batch-parses source files, and returns
144
+ * symbols matching the query ranked by relevance score.
145
+ *
146
+ * @param query - Search string to match against symbol names and paths
147
+ * @param options - Search options (maxResults, filePattern, language, rootDir)
148
+ * @returns Ranked array of search results
149
+ */
150
+ export function smartSearch(query: string, options: SmartSearchOptions = {}): SmartSearchResult[] {
151
+ const rootDir = options.rootDir ?? process.cwd();
152
+ const maxResults = options.maxResults ?? 20;
153
+
154
+ // Collect and parse source files
155
+ const files = collectSourceFiles(rootDir, options);
156
+ const parsed = batchParse(files, rootDir);
157
+
158
+ // Score all symbols
159
+ const results: SmartSearchResult[] = [];
160
+ for (const fileResult of parsed.results) {
161
+ for (const symbol of fileResult.symbols) {
162
+ const { score, matchType } = scoreSymbol(symbol, query);
163
+ if (score > 0) {
164
+ results.push({ symbol, score, matchType });
165
+ }
166
+ }
167
+ }
168
+
169
+ // Sort by score descending, then by name
170
+ results.sort((a, b) => b.score - a.score || a.symbol.name.localeCompare(b.symbol.name));
171
+
172
+ return results.slice(0, maxResults);
173
+ }