@soleri/core 9.14.4 → 9.16.7

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 (355) hide show
  1. package/data/flows/deliver.flow.yaml +11 -0
  2. package/data/flows/design.flow.yaml +4 -14
  3. package/data/flows/enhance.flow.yaml +10 -0
  4. package/data/flows/explore.flow.yaml +16 -0
  5. package/data/flows/fix.flow.yaml +1 -1
  6. package/data/flows/review.flow.yaml +13 -4
  7. package/dist/brain/brain.d.ts +9 -0
  8. package/dist/brain/brain.d.ts.map +1 -1
  9. package/dist/brain/brain.js +11 -1
  10. package/dist/brain/brain.js.map +1 -1
  11. package/dist/brain/intelligence.d.ts.map +1 -1
  12. package/dist/brain/intelligence.js +24 -0
  13. package/dist/brain/intelligence.js.map +1 -1
  14. package/dist/brain/types.d.ts +1 -0
  15. package/dist/brain/types.d.ts.map +1 -1
  16. package/dist/capabilities/chain-mapping.d.ts.map +1 -1
  17. package/dist/capabilities/chain-mapping.js +5 -4
  18. package/dist/capabilities/chain-mapping.js.map +1 -1
  19. package/dist/capabilities/registry.d.ts +6 -0
  20. package/dist/capabilities/registry.d.ts.map +1 -1
  21. package/dist/capabilities/registry.js +3 -2
  22. package/dist/capabilities/registry.js.map +1 -1
  23. package/dist/chat/chat-session.d.ts +6 -0
  24. package/dist/chat/chat-session.d.ts.map +1 -1
  25. package/dist/chat/chat-session.js +68 -17
  26. package/dist/chat/chat-session.js.map +1 -1
  27. package/dist/context/context-engine.js +1 -1
  28. package/dist/context/context-engine.js.map +1 -1
  29. package/dist/curator/curator.d.ts +6 -0
  30. package/dist/curator/curator.d.ts.map +1 -1
  31. package/dist/curator/curator.js +138 -0
  32. package/dist/curator/curator.js.map +1 -1
  33. package/dist/curator/types.d.ts +10 -0
  34. package/dist/curator/types.d.ts.map +1 -1
  35. package/dist/engine/bin/soleri-engine.js +0 -0
  36. package/dist/engine/core-ops.d.ts.map +1 -1
  37. package/dist/engine/core-ops.js +38 -1
  38. package/dist/engine/core-ops.js.map +1 -1
  39. package/dist/flows/epilogue.d.ts +5 -1
  40. package/dist/flows/epilogue.d.ts.map +1 -1
  41. package/dist/flows/epilogue.js +11 -3
  42. package/dist/flows/epilogue.js.map +1 -1
  43. package/dist/flows/executor.d.ts.map +1 -1
  44. package/dist/flows/executor.js +13 -5
  45. package/dist/flows/executor.js.map +1 -1
  46. package/dist/flows/index.d.ts +1 -2
  47. package/dist/flows/index.d.ts.map +1 -1
  48. package/dist/flows/index.js +1 -0
  49. package/dist/flows/index.js.map +1 -1
  50. package/dist/flows/plan-builder.d.ts +17 -1
  51. package/dist/flows/plan-builder.d.ts.map +1 -1
  52. package/dist/flows/plan-builder.js +67 -6
  53. package/dist/flows/plan-builder.js.map +1 -1
  54. package/dist/flows/probes.d.ts +1 -1
  55. package/dist/flows/probes.d.ts.map +1 -1
  56. package/dist/flows/probes.js +15 -3
  57. package/dist/flows/probes.js.map +1 -1
  58. package/dist/flows/types.d.ts +47 -20
  59. package/dist/flows/types.d.ts.map +1 -1
  60. package/dist/flows/types.js +6 -1
  61. package/dist/flows/types.js.map +1 -1
  62. package/dist/index.d.ts +10 -0
  63. package/dist/index.d.ts.map +1 -1
  64. package/dist/index.js +9 -0
  65. package/dist/index.js.map +1 -1
  66. package/dist/intake/content-classifier.d.ts +10 -4
  67. package/dist/intake/content-classifier.d.ts.map +1 -1
  68. package/dist/intake/content-classifier.js +19 -5
  69. package/dist/intake/content-classifier.js.map +1 -1
  70. package/dist/intake/text-ingester.d.ts +18 -0
  71. package/dist/intake/text-ingester.d.ts.map +1 -1
  72. package/dist/intake/text-ingester.js +37 -13
  73. package/dist/intake/text-ingester.js.map +1 -1
  74. package/dist/packs/pack-installer.d.ts.map +1 -1
  75. package/dist/packs/pack-installer.js +28 -2
  76. package/dist/packs/pack-installer.js.map +1 -1
  77. package/dist/planning/planner-types.d.ts +2 -0
  78. package/dist/planning/planner-types.d.ts.map +1 -1
  79. package/dist/planning/planner.d.ts +4 -0
  80. package/dist/planning/planner.d.ts.map +1 -1
  81. package/dist/planning/planner.js +50 -4
  82. package/dist/planning/planner.js.map +1 -1
  83. package/dist/playbooks/playbook-executor.d.ts +10 -1
  84. package/dist/playbooks/playbook-executor.d.ts.map +1 -1
  85. package/dist/playbooks/playbook-executor.js +8 -2
  86. package/dist/playbooks/playbook-executor.js.map +1 -1
  87. package/dist/playbooks/playbook-types.d.ts +8 -0
  88. package/dist/playbooks/playbook-types.d.ts.map +1 -1
  89. package/dist/plugins/types.d.ts +2 -2
  90. package/dist/runtime/admin-extra-ops.d.ts.map +1 -1
  91. package/dist/runtime/admin-extra-ops.js +30 -0
  92. package/dist/runtime/admin-extra-ops.js.map +1 -1
  93. package/dist/runtime/admin-ops.d.ts.map +1 -1
  94. package/dist/runtime/admin-ops.js +60 -21
  95. package/dist/runtime/admin-ops.js.map +1 -1
  96. package/dist/runtime/admin-setup-ops.d.ts +11 -0
  97. package/dist/runtime/admin-setup-ops.d.ts.map +1 -1
  98. package/dist/runtime/admin-setup-ops.js +146 -37
  99. package/dist/runtime/admin-setup-ops.js.map +1 -1
  100. package/dist/runtime/capture-ops.d.ts.map +1 -1
  101. package/dist/runtime/capture-ops.js +38 -12
  102. package/dist/runtime/capture-ops.js.map +1 -1
  103. package/dist/runtime/facades/brain-facade.d.ts.map +1 -1
  104. package/dist/runtime/facades/brain-facade.js +16 -4
  105. package/dist/runtime/facades/brain-facade.js.map +1 -1
  106. package/dist/runtime/facades/context-facade.d.ts.map +1 -1
  107. package/dist/runtime/facades/context-facade.js +9 -3
  108. package/dist/runtime/facades/context-facade.js.map +1 -1
  109. package/dist/runtime/facades/memory-facade.d.ts.map +1 -1
  110. package/dist/runtime/facades/memory-facade.js +20 -7
  111. package/dist/runtime/facades/memory-facade.js.map +1 -1
  112. package/dist/runtime/facades/orchestrate-facade.d.ts.map +1 -1
  113. package/dist/runtime/facades/orchestrate-facade.js +40 -1
  114. package/dist/runtime/facades/orchestrate-facade.js.map +1 -1
  115. package/dist/runtime/facades/plan-facade.d.ts.map +1 -1
  116. package/dist/runtime/facades/plan-facade.js +113 -4
  117. package/dist/runtime/facades/plan-facade.js.map +1 -1
  118. package/dist/runtime/facades/vault-facade.d.ts.map +1 -1
  119. package/dist/runtime/facades/vault-facade.js +24 -3
  120. package/dist/runtime/facades/vault-facade.js.map +1 -1
  121. package/dist/runtime/orchestrate-ops.d.ts +21 -0
  122. package/dist/runtime/orchestrate-ops.d.ts.map +1 -1
  123. package/dist/runtime/orchestrate-ops.js +132 -38
  124. package/dist/runtime/orchestrate-ops.js.map +1 -1
  125. package/dist/runtime/runtime.d.ts.map +1 -1
  126. package/dist/runtime/runtime.js +16 -0
  127. package/dist/runtime/runtime.js.map +1 -1
  128. package/dist/runtime/schema-helpers.d.ts.map +1 -1
  129. package/dist/runtime/schema-helpers.js +4 -0
  130. package/dist/runtime/schema-helpers.js.map +1 -1
  131. package/dist/runtime/types.d.ts +19 -0
  132. package/dist/runtime/types.d.ts.map +1 -1
  133. package/dist/runtime/vault-linking-ops.d.ts.map +1 -1
  134. package/dist/runtime/vault-linking-ops.js +16 -3
  135. package/dist/runtime/vault-linking-ops.js.map +1 -1
  136. package/dist/scheduler/cron-validator.d.ts +15 -0
  137. package/dist/scheduler/cron-validator.d.ts.map +1 -0
  138. package/dist/scheduler/cron-validator.js +93 -0
  139. package/dist/scheduler/cron-validator.js.map +1 -0
  140. package/dist/scheduler/platform-linux.d.ts +14 -0
  141. package/dist/scheduler/platform-linux.d.ts.map +1 -0
  142. package/dist/scheduler/platform-linux.js +107 -0
  143. package/dist/scheduler/platform-linux.js.map +1 -0
  144. package/dist/scheduler/platform-macos.d.ts +15 -0
  145. package/dist/scheduler/platform-macos.d.ts.map +1 -0
  146. package/dist/scheduler/platform-macos.js +131 -0
  147. package/dist/scheduler/platform-macos.js.map +1 -0
  148. package/dist/scheduler/scheduler-ops.d.ts +14 -0
  149. package/dist/scheduler/scheduler-ops.d.ts.map +1 -0
  150. package/dist/scheduler/scheduler-ops.js +77 -0
  151. package/dist/scheduler/scheduler-ops.js.map +1 -0
  152. package/dist/scheduler/scheduler.d.ts +55 -0
  153. package/dist/scheduler/scheduler.d.ts.map +1 -0
  154. package/dist/scheduler/scheduler.js +144 -0
  155. package/dist/scheduler/scheduler.js.map +1 -0
  156. package/dist/scheduler/types.d.ts +48 -0
  157. package/dist/scheduler/types.d.ts.map +1 -0
  158. package/dist/scheduler/types.js +6 -0
  159. package/dist/scheduler/types.js.map +1 -0
  160. package/dist/skills/sync-skills.d.ts +11 -0
  161. package/dist/skills/sync-skills.d.ts.map +1 -1
  162. package/dist/skills/sync-skills.js +132 -38
  163. package/dist/skills/sync-skills.js.map +1 -1
  164. package/dist/skills/validate-skills.d.ts +32 -0
  165. package/dist/skills/validate-skills.d.ts.map +1 -0
  166. package/dist/skills/validate-skills.js +396 -0
  167. package/dist/skills/validate-skills.js.map +1 -0
  168. package/dist/utils/worktree-reaper.d.ts +38 -0
  169. package/dist/utils/worktree-reaper.d.ts.map +1 -0
  170. package/dist/utils/worktree-reaper.js +85 -0
  171. package/dist/utils/worktree-reaper.js.map +1 -0
  172. package/dist/vault/default-canonical-tags.d.ts +15 -0
  173. package/dist/vault/default-canonical-tags.d.ts.map +1 -0
  174. package/dist/vault/default-canonical-tags.js +65 -0
  175. package/dist/vault/default-canonical-tags.js.map +1 -0
  176. package/dist/vault/scope-detector.d.ts.map +1 -1
  177. package/dist/vault/scope-detector.js +37 -4
  178. package/dist/vault/scope-detector.js.map +1 -1
  179. package/dist/vault/tag-normalizer.d.ts +42 -0
  180. package/dist/vault/tag-normalizer.d.ts.map +1 -0
  181. package/dist/vault/tag-normalizer.js +157 -0
  182. package/dist/vault/tag-normalizer.js.map +1 -0
  183. package/dist/vault/vault-entries.d.ts.map +1 -1
  184. package/dist/vault/vault-entries.js +3 -1
  185. package/dist/vault/vault-entries.js.map +1 -1
  186. package/package.json +5 -1
  187. package/src/__tests__/embeddings.test.ts +3 -3
  188. package/src/agency/agency-manager.test.ts +4 -4
  189. package/src/agency/default-rules.test.ts +0 -13
  190. package/src/brain/brain-intelligence.test.ts +0 -5
  191. package/src/brain/brain.ts +25 -1
  192. package/src/brain/intelligence.ts +25 -0
  193. package/src/brain/second-brain-features.test.ts +2 -14
  194. package/src/brain/types.ts +1 -0
  195. package/src/capabilities/chain-mapping.test.ts +1 -6
  196. package/src/capabilities/chain-mapping.ts +6 -4
  197. package/src/capabilities/registry.test.ts +1 -1
  198. package/src/capabilities/registry.ts +9 -2
  199. package/src/chat/agent-loop.test.ts +1 -1
  200. package/src/chat/chat-enhanced.test.ts +0 -8
  201. package/src/chat/chat-session.ts +75 -17
  202. package/src/chat/chat-transport.test.ts +31 -1
  203. package/src/claudemd/compose.test.ts +0 -5
  204. package/src/context/context-engine.test.ts +0 -1
  205. package/src/context/context-engine.ts +1 -1
  206. package/src/control/intent-router.test.ts +2 -2
  207. package/src/curator/curator.ts +180 -0
  208. package/src/curator/tag-manager.test.ts +0 -4
  209. package/src/curator/types.ts +10 -0
  210. package/src/domain-packs/types.test.ts +0 -5
  211. package/src/dream/dream.test.ts +0 -7
  212. package/src/enforcement/registry.test.ts +2 -2
  213. package/src/engine/core-ops.test.ts +4 -22
  214. package/src/engine/core-ops.ts +36 -1
  215. package/src/engine/module-manifest.test.ts +1 -31
  216. package/src/engine/register-engine.test.ts +3 -33
  217. package/src/errors/retry.test.ts +3 -1
  218. package/src/flows/chain-runner.test.ts +0 -6
  219. package/src/flows/context-router.test.ts +3 -3
  220. package/src/flows/epilogue.test.ts +40 -2
  221. package/src/flows/epilogue.ts +11 -2
  222. package/src/flows/executor.test.ts +48 -2
  223. package/src/flows/executor.ts +15 -5
  224. package/src/flows/index.ts +1 -3
  225. package/src/flows/plan-builder.test.ts +201 -0
  226. package/src/flows/plan-builder.ts +81 -5
  227. package/src/flows/probes.ts +17 -3
  228. package/src/flows/types.ts +31 -2
  229. package/src/health/health-registry.test.ts +3 -1
  230. package/src/index.ts +24 -0
  231. package/src/intake/content-classifier.ts +22 -4
  232. package/src/intake/dedup-gate.test.ts +2 -6
  233. package/src/intake/text-ingester.test.ts +3 -4
  234. package/src/intake/text-ingester.ts +61 -12
  235. package/src/llm/llm-client.test.ts +1 -1
  236. package/src/llm/utils.test.ts +1 -1
  237. package/src/migrations/migration-runner.test.ts +0 -1
  238. package/src/operator/operator-context-store.test.ts +0 -13
  239. package/src/operator/operator-profile.test.ts +2 -20
  240. package/src/packs/pack-installer.ts +28 -2
  241. package/src/packs/pack-system.test.ts +2 -2
  242. package/src/persona/defaults.test.ts +19 -19
  243. package/src/planning/gap-passes.test.ts +0 -46
  244. package/src/planning/gap-patterns.test.ts +0 -42
  245. package/src/planning/goal-ancestry.test.ts +3 -1
  246. package/src/planning/plan-lifecycle.test.ts +15 -7
  247. package/src/planning/planner-types.ts +2 -0
  248. package/src/planning/planner.test.ts +86 -90
  249. package/src/planning/planner.ts +56 -4
  250. package/src/planning/reconciliation-engine.test.ts +3 -10
  251. package/src/planning/task-complexity-assessor.test.ts +0 -5
  252. package/src/planning/task-verifier.test.ts +3 -1
  253. package/src/playbooks/generic/generic-playbooks.test.ts +0 -28
  254. package/src/playbooks/index.test.ts +0 -55
  255. package/src/playbooks/playbook-executor.test.ts +76 -0
  256. package/src/playbooks/playbook-executor.ts +24 -3
  257. package/src/playbooks/playbook-types.ts +8 -0
  258. package/src/plugins/plugin-registry.test.ts +6 -2
  259. package/src/project/project-registry.test.ts +2 -0
  260. package/src/queue/async-infrastructure.test.ts +6 -4
  261. package/src/queue/job-queue.test.ts +13 -7
  262. package/src/runtime/admin-extra-ops.test.ts +35 -30
  263. package/src/runtime/admin-extra-ops.ts +30 -0
  264. package/src/runtime/admin-ops.test.ts +0 -4
  265. package/src/runtime/admin-ops.ts +63 -21
  266. package/src/runtime/admin-setup-ops.test.ts +229 -13
  267. package/src/runtime/admin-setup-ops.ts +145 -36
  268. package/src/runtime/archive-ops.test.ts +0 -28
  269. package/src/runtime/branching-ops.test.ts +0 -17
  270. package/src/runtime/capture-ops.test.ts +41 -16
  271. package/src/runtime/capture-ops.ts +78 -46
  272. package/src/runtime/chain-ops.test.ts +0 -21
  273. package/src/runtime/facades/admin-facade.test.ts +0 -34
  274. package/src/runtime/facades/agency-facade.test.ts +0 -39
  275. package/src/runtime/facades/archive-facade.test.ts +0 -43
  276. package/src/runtime/facades/brain-facade.test.ts +8 -99
  277. package/src/runtime/facades/brain-facade.ts +29 -12
  278. package/src/runtime/facades/branching-facade.test.ts +30 -17
  279. package/src/runtime/facades/chat-facade.test.ts +0 -91
  280. package/src/runtime/facades/chat-service-ops.test.ts +0 -24
  281. package/src/runtime/facades/chat-session-ops.test.ts +0 -12
  282. package/src/runtime/facades/chat-transport-ops.test.ts +0 -23
  283. package/src/runtime/facades/context-facade.test.ts +0 -17
  284. package/src/runtime/facades/context-facade.ts +11 -4
  285. package/src/runtime/facades/control-facade.test.ts +0 -30
  286. package/src/runtime/facades/curator-facade.test.ts +0 -33
  287. package/src/runtime/facades/intake-facade.test.ts +0 -33
  288. package/src/runtime/facades/links-facade.test.ts +0 -37
  289. package/src/runtime/facades/loop-facade.test.ts +0 -26
  290. package/src/runtime/facades/memory-facade.test.ts +0 -18
  291. package/src/runtime/facades/memory-facade.ts +27 -11
  292. package/src/runtime/facades/operator-facade.test.ts +0 -31
  293. package/src/runtime/facades/orchestrate-facade.test.ts +0 -21
  294. package/src/runtime/facades/orchestrate-facade.ts +39 -1
  295. package/src/runtime/facades/plan-facade.test.ts +7 -32
  296. package/src/runtime/facades/plan-facade.ts +137 -4
  297. package/src/runtime/facades/review-facade.test.ts +1 -49
  298. package/src/runtime/facades/sync-facade.test.ts +24 -41
  299. package/src/runtime/facades/tier-facade.test.ts +30 -22
  300. package/src/runtime/facades/vault-facade.test.ts +0 -41
  301. package/src/runtime/facades/vault-facade.ts +26 -3
  302. package/src/runtime/grading-ops.test.ts +0 -27
  303. package/src/runtime/intake-ops.test.ts +0 -19
  304. package/src/runtime/loop-ops.test.ts +0 -48
  305. package/src/runtime/memory-cross-project-ops.test.ts +0 -14
  306. package/src/runtime/memory-extra-ops.test.ts +4 -8
  307. package/src/runtime/orchestrate-ops.test.ts +238 -19
  308. package/src/runtime/orchestrate-ops.ts +166 -41
  309. package/src/runtime/pack-ops.test.ts +0 -26
  310. package/src/runtime/planning-extra-ops.test.ts +2 -14
  311. package/src/runtime/playbook-ops-execution.test.ts +9 -20
  312. package/src/runtime/playbook-ops.test.ts +4 -67
  313. package/src/runtime/review-ops.test.ts +0 -15
  314. package/src/runtime/runtime.ts +18 -0
  315. package/src/runtime/schema-helpers.ts +4 -0
  316. package/src/runtime/sync-ops.test.ts +0 -18
  317. package/src/runtime/tier-ops.test.ts +0 -21
  318. package/src/runtime/types.ts +19 -0
  319. package/src/runtime/vault-extra-ops.test.ts +0 -12
  320. package/src/runtime/vault-linking-ops.test.ts +0 -4
  321. package/src/runtime/vault-linking-ops.ts +26 -8
  322. package/src/runtime/vault-sharing-ops.test.ts +0 -9
  323. package/src/scheduler/cron-validator.ts +101 -0
  324. package/src/scheduler/platform-linux.ts +122 -0
  325. package/src/scheduler/platform-macos.ts +150 -0
  326. package/src/scheduler/scheduler-ops.ts +77 -0
  327. package/src/scheduler/scheduler.test.ts +247 -0
  328. package/src/scheduler/scheduler.ts +174 -0
  329. package/src/scheduler/types.ts +52 -0
  330. package/src/skills/__tests__/sync-skills.test.ts +6 -17
  331. package/src/skills/global-claude-md.test.ts +113 -0
  332. package/src/skills/sync-skills.ts +143 -35
  333. package/src/skills/validate-skills.test.ts +206 -0
  334. package/src/skills/validate-skills.ts +470 -0
  335. package/src/telemetry/telemetry.test.ts +1 -0
  336. package/src/transport/http-server.test.ts +3 -0
  337. package/src/transport/session-manager.test.ts +3 -1
  338. package/src/transport/token-auth.test.ts +6 -9
  339. package/src/transport/ws-server.test.ts +10 -2
  340. package/src/utils/worktree-reaper.ts +113 -0
  341. package/src/vault/__tests__/vault-characterization.test.ts +0 -108
  342. package/src/vault/default-canonical-tags.ts +64 -0
  343. package/src/vault/linking.test.ts +0 -2
  344. package/src/vault/playbook.test.ts +4 -1
  345. package/src/vault/scope-detector.test.ts +3 -1
  346. package/src/vault/scope-detector.ts +42 -4
  347. package/src/vault/tag-normalizer.test.ts +214 -0
  348. package/src/vault/tag-normalizer.ts +188 -0
  349. package/src/vault/vault-connect.test.ts +1 -1
  350. package/src/vault/vault-entries.ts +3 -1
  351. package/src/vault/vault.test.ts +23 -8
  352. package/dist/embeddings/index.d.ts +0 -5
  353. package/dist/embeddings/index.d.ts.map +0 -1
  354. package/dist/embeddings/index.js +0 -3
  355. package/dist/embeddings/index.js.map +0 -1
@@ -0,0 +1,470 @@
1
+ /**
2
+ * Validator for user-installed SKILL.md op-call examples.
3
+ *
4
+ * Reads all SKILL.md files from a given skills directory (e.g. ~/.claude/skills/),
5
+ * extracts inline op-call examples, and validates their params against the actual
6
+ * Zod schemas from the facade layer.
7
+ *
8
+ * Returns structured results rather than printing or exiting — the CLI layer owns I/O.
9
+ */
10
+
11
+ import { readFileSync, readdirSync, existsSync, statSync } from 'node:fs';
12
+ import { join } from 'node:path';
13
+ import type { ZodType, ZodError } from 'zod';
14
+ import type { OpDefinition } from '../facades/types.js';
15
+ import type { AgentRuntime } from '../runtime/types.js';
16
+
17
+ // ── Facade factory imports ──────────────────────────────────────────────
18
+ import { createVaultFacadeOps } from '../runtime/facades/vault-facade.js';
19
+ import { createPlanFacadeOps } from '../runtime/facades/plan-facade.js';
20
+ import { createBrainFacadeOps } from '../runtime/facades/brain-facade.js';
21
+ import { createMemoryFacadeOps } from '../runtime/facades/memory-facade.js';
22
+ import { createAdminFacadeOps } from '../runtime/facades/admin-facade.js';
23
+ import { createCuratorFacadeOps } from '../runtime/facades/curator-facade.js';
24
+ import { createLoopFacadeOps } from '../runtime/facades/loop-facade.js';
25
+ import { createOrchestrateFacadeOps } from '../runtime/facades/orchestrate-facade.js';
26
+ import { createControlFacadeOps } from '../runtime/facades/control-facade.js';
27
+ import { createContextFacadeOps } from '../runtime/facades/context-facade.js';
28
+ import { createAgencyFacadeOps } from '../runtime/facades/agency-facade.js';
29
+ import { createChatFacadeOps } from '../runtime/facades/chat-facade.js';
30
+ import { createOperatorFacadeOps } from '../runtime/facades/operator-facade.js';
31
+ import { createArchiveFacadeOps } from '../runtime/facades/archive-facade.js';
32
+ import { createSyncFacadeOps } from '../runtime/facades/sync-facade.js';
33
+ import { createReviewFacadeOps } from '../runtime/facades/review-facade.js';
34
+ import { createIntakeFacadeOps } from '../runtime/facades/intake-facade.js';
35
+ import { createLinksFacadeOps } from '../runtime/facades/links-facade.js';
36
+ import { createBranchingFacadeOps } from '../runtime/facades/branching-facade.js';
37
+ import { createTierFacadeOps } from '../runtime/facades/tier-facade.js';
38
+ import { createEmbeddingFacadeOps } from '../runtime/facades/embedding-facade.js';
39
+
40
+ // ── Public types ────────────────────────────────────────────────────────
41
+
42
+ export interface SkillValidationError {
43
+ file: string;
44
+ op: string;
45
+ message: string;
46
+ line?: number;
47
+ }
48
+
49
+ export interface SkillValidationResult {
50
+ valid: boolean;
51
+ errors: SkillValidationError[];
52
+ totalFiles: number;
53
+ totalExamples: number;
54
+ registrySize: number;
55
+ }
56
+
57
+ // ── Internal types ──────────────────────────────────────────────────────
58
+
59
+ interface OpExample {
60
+ file: string;
61
+ line: number;
62
+ opName: string;
63
+ rawParams: string;
64
+ parsedParams: Record<string, unknown> | null;
65
+ parseError?: string;
66
+ }
67
+
68
+ // ── Mock runtime ────────────────────────────────────────────────────────
69
+ // Schemas are constructed during factory calls but handlers are never invoked.
70
+
71
+ function createNoopProxy(): AgentRuntime {
72
+ const handler: ProxyHandler<object> = {
73
+ get(_target, prop) {
74
+ if (prop === Symbol.toPrimitive) return () => '';
75
+ if (prop === Symbol.iterator) return undefined;
76
+ if (prop === 'then') return undefined;
77
+ if (prop === 'toString') return () => '[mock]';
78
+ if (prop === 'valueOf') return () => 0;
79
+ if (prop === 'length') return 0;
80
+ return new Proxy(function () {}, handler);
81
+ },
82
+ apply() {
83
+ return new Proxy({}, handler);
84
+ },
85
+ };
86
+ return new Proxy({}, handler) as unknown as AgentRuntime;
87
+ }
88
+
89
+ // ── Schema registry ─────────────────────────────────────────────────────
90
+
91
+ function buildSchemaRegistry(): Map<string, ZodType> {
92
+ const runtime = createNoopProxy();
93
+ const registry = new Map<string, ZodType>();
94
+
95
+ const facadeFactories: Array<(rt: AgentRuntime) => OpDefinition[]> = [
96
+ createVaultFacadeOps,
97
+ createPlanFacadeOps,
98
+ createBrainFacadeOps,
99
+ createMemoryFacadeOps,
100
+ createAdminFacadeOps,
101
+ createCuratorFacadeOps,
102
+ createLoopFacadeOps,
103
+ createOrchestrateFacadeOps,
104
+ createControlFacadeOps,
105
+ createContextFacadeOps,
106
+ createAgencyFacadeOps,
107
+ createChatFacadeOps,
108
+ createOperatorFacadeOps,
109
+ createArchiveFacadeOps,
110
+ createSyncFacadeOps,
111
+ createReviewFacadeOps,
112
+ createIntakeFacadeOps,
113
+ createLinksFacadeOps,
114
+ createBranchingFacadeOps,
115
+ createTierFacadeOps,
116
+ createEmbeddingFacadeOps,
117
+ ];
118
+
119
+ for (const factory of facadeFactories) {
120
+ try {
121
+ const ops = factory(runtime);
122
+ for (const op of ops) {
123
+ if (op.schema) {
124
+ registry.set(op.name, op.schema);
125
+ }
126
+ }
127
+ } catch {
128
+ // Some facades may fail with the mock runtime — skip them.
129
+ }
130
+ }
131
+
132
+ return registry;
133
+ }
134
+
135
+ // ── SKILL.md discovery ──────────────────────────────────────────────────
136
+ // Discovers all SKILL.md files directly inside skillsDir.
137
+ // Supports both layouts:
138
+ // - skillsDir/{name}/SKILL.md (directory layout)
139
+ // - skillsDir/{name}.md (flat file layout)
140
+
141
+ function discoverSkillFiles(skillsDir: string): string[] {
142
+ const paths: string[] = [];
143
+
144
+ if (!existsSync(skillsDir)) return paths;
145
+
146
+ let entries: string[];
147
+ try {
148
+ entries = readdirSync(skillsDir);
149
+ } catch {
150
+ return paths;
151
+ }
152
+
153
+ for (const entry of entries) {
154
+ const entryPath = join(skillsDir, entry);
155
+ try {
156
+ const stat = statSync(entryPath);
157
+ if (stat.isDirectory()) {
158
+ const skillMd = join(entryPath, 'SKILL.md');
159
+ if (existsSync(skillMd)) {
160
+ paths.push(skillMd);
161
+ }
162
+ } else if (entry.endsWith('.md')) {
163
+ paths.push(entryPath);
164
+ }
165
+ } catch {
166
+ // Skip unreadable entries
167
+ }
168
+ }
169
+
170
+ return paths;
171
+ }
172
+
173
+ // ── SKILL.md parser ─────────────────────────────────────────────────────
174
+
175
+ function extractOpExamples(filePath: string): OpExample[] {
176
+ let content: string;
177
+ try {
178
+ content = readFileSync(filePath, 'utf-8');
179
+ } catch {
180
+ return [];
181
+ }
182
+
183
+ const lines = content.split('\n');
184
+ const examples: OpExample[] = [];
185
+
186
+ let inCodeBlock = false;
187
+ let codeBlockStart = -1;
188
+ let codeBlockLines: string[] = [];
189
+
190
+ for (let i = 0; i < lines.length; i++) {
191
+ const line = lines[i];
192
+ if (line.trimStart().startsWith('```')) {
193
+ if (!inCodeBlock) {
194
+ inCodeBlock = true;
195
+ codeBlockStart = i + 1;
196
+ codeBlockLines = [];
197
+ } else {
198
+ extractFromCodeBlock(filePath, codeBlockStart, codeBlockLines, examples);
199
+ inCodeBlock = false;
200
+ codeBlockLines = [];
201
+ }
202
+ continue;
203
+ }
204
+ if (inCodeBlock) {
205
+ codeBlockLines.push(line);
206
+ }
207
+ }
208
+
209
+ return examples;
210
+ }
211
+
212
+ function extractFromCodeBlock(
213
+ filePath: string,
214
+ startLine: number,
215
+ blockLines: string[],
216
+ results: OpExample[],
217
+ ): void {
218
+ const opPattern = /(?:YOUR_AGENT_\w+\s+)?op:(\w+)(?:\s+params:\s*(.*))?/;
219
+
220
+ for (let i = 0; i < blockLines.length; i++) {
221
+ const line = blockLines[i];
222
+ const match = line.match(opPattern);
223
+ if (!match) continue;
224
+
225
+ const opName = match[1];
226
+ const lineNum = startLine + i + 1;
227
+
228
+ let rawParams = '';
229
+
230
+ if (match[2]) {
231
+ rawParams = match[2].trim();
232
+ } else if (i + 1 < blockLines.length && blockLines[i + 1].trim().startsWith('params:')) {
233
+ const paramsLine = blockLines[i + 1].trim();
234
+ const paramsMatch = paramsLine.match(/^params:\s*(.*)/);
235
+ if (paramsMatch) {
236
+ rawParams = paramsMatch[1].trim();
237
+ }
238
+ }
239
+
240
+ if (rawParams) {
241
+ const fullParams = collectMultiLineParams(blockLines, i, rawParams);
242
+ const { parsed, error } = parseLooseJson(fullParams);
243
+
244
+ results.push({
245
+ file: filePath,
246
+ line: lineNum,
247
+ opName,
248
+ rawParams: fullParams,
249
+ parsedParams: parsed,
250
+ parseError: error,
251
+ });
252
+ }
253
+ }
254
+ }
255
+
256
+ function collectMultiLineParams(blockLines: string[], opLineIdx: number, initial: string): string {
257
+ if (isBalanced(initial)) return initial;
258
+
259
+ let startIdx = opLineIdx + 1;
260
+ if (!blockLines[opLineIdx].includes('params:') && startIdx < blockLines.length) {
261
+ if (blockLines[startIdx].trim().startsWith('params:')) {
262
+ startIdx++;
263
+ }
264
+ }
265
+
266
+ let result = initial;
267
+ for (let j = startIdx; j < blockLines.length; j++) {
268
+ const nextLine = blockLines[j].trim();
269
+ if (!nextLine) continue;
270
+ if (nextLine.match(/(?:YOUR_AGENT_\w+\s+)?op:\w+/)) break;
271
+ result += '\n' + nextLine;
272
+ if (isBalanced(result)) break;
273
+ }
274
+
275
+ return result;
276
+ }
277
+
278
+ function isBalanced(s: string): boolean {
279
+ let depth = 0;
280
+ for (const ch of s) {
281
+ if (ch === '{' || ch === '[') depth++;
282
+ if (ch === '}' || ch === ']') depth--;
283
+ if (depth < 0) return false;
284
+ }
285
+ return depth === 0;
286
+ }
287
+
288
+ function parseLooseJson(raw: string): {
289
+ parsed: Record<string, unknown> | null;
290
+ error?: string;
291
+ } {
292
+ if (!raw.trim()) return { parsed: null };
293
+
294
+ try {
295
+ const normalized = raw
296
+ .replace(/"<([^">]+)>"/g, '"$1"')
297
+ .replace(/([{,]\s*)(\w+)\s*:/g, '$1"$2":')
298
+ .replace(/'/g, '"')
299
+ .replace(/,\s*([}\]])/g, '$1')
300
+ .replace(/\.\.\./g, '')
301
+ .replace(/\["?<[^>]+>"?\]/g, '["placeholder"]');
302
+
303
+ const result = JSON.parse(normalized);
304
+ return { parsed: result };
305
+ } catch (e) {
306
+ try {
307
+ const result = extractFlatParams(raw);
308
+ if (result && Object.keys(result).length > 0) {
309
+ return { parsed: result };
310
+ }
311
+ } catch {
312
+ // fall through
313
+ }
314
+ return {
315
+ parsed: null,
316
+ error: `Cannot parse params: ${(e as Error).message}`,
317
+ };
318
+ }
319
+ }
320
+
321
+ function extractFlatParams(raw: string): Record<string, unknown> | null {
322
+ const result: Record<string, unknown> = {};
323
+ const kvPattern = /(\w+)\s*:\s*(?:"([^"]*)"|\[([^\]]*)\]|(\{[^}]*\})|(\w+))/g;
324
+ let match;
325
+ let found = false;
326
+
327
+ while ((match = kvPattern.exec(raw)) !== null) {
328
+ found = true;
329
+ const key = match[1];
330
+ if (match[2] !== undefined) {
331
+ result[key] = match[2].replace(/<[^>]+>/g, 'placeholder');
332
+ } else if (match[3] !== undefined) {
333
+ result[key] = ['placeholder'];
334
+ } else if (match[4] !== undefined) {
335
+ result[key] = {};
336
+ } else if (match[5] !== undefined) {
337
+ const val = match[5];
338
+ if (val === 'true') result[key] = true;
339
+ else if (val === 'false') result[key] = false;
340
+ else if (/^\d+$/.test(val)) result[key] = parseInt(val, 10);
341
+ else result[key] = val;
342
+ }
343
+ }
344
+
345
+ return found ? result : null;
346
+ }
347
+
348
+ // ── Validation ──────────────────────────────────────────────────────────
349
+
350
+ function isPlaceholder(value: unknown): boolean {
351
+ if (typeof value !== 'string') return false;
352
+ if (value.includes('|')) return true;
353
+ if (/^<.*>$/.test(value)) return true;
354
+ if (/^(placeholder|example|value|name|id|title|description|type|domain)$/i.test(value))
355
+ return true;
356
+ if (
357
+ /^[\w]+-[\w]+$/.test(value) &&
358
+ /\b(correct|your|my|the|this|some|new|old|entry|item|current)\b/i.test(value)
359
+ )
360
+ return true;
361
+ return false;
362
+ }
363
+
364
+ function isPlaceholderIssue(
365
+ issue: { code: string; path: (string | number)[]; received?: unknown; message: string },
366
+ params: Record<string, unknown>,
367
+ ): boolean {
368
+ if (issue.code === 'invalid_enum_value') {
369
+ const received = issue.received ?? getNestedValue(params, issue.path);
370
+ if (isPlaceholder(received)) return true;
371
+ }
372
+ return false;
373
+ }
374
+
375
+ function getNestedValue(obj: Record<string, unknown>, path: (string | number)[]): unknown {
376
+ let current: unknown = obj;
377
+ for (const key of path) {
378
+ if (current === null || current === undefined || typeof current !== 'object') return undefined;
379
+ current = (current as Record<string | number, unknown>)[key];
380
+ }
381
+ return current;
382
+ }
383
+
384
+ function validateExamples(
385
+ examples: OpExample[],
386
+ registry: Map<string, ZodType>,
387
+ ): SkillValidationError[] {
388
+ const errors: SkillValidationError[] = [];
389
+
390
+ for (const ex of examples) {
391
+ if (ex.parseError) continue;
392
+ if (!ex.parsedParams) continue;
393
+
394
+ const schema = registry.get(ex.opName);
395
+ if (!schema) {
396
+ errors.push({
397
+ file: ex.file,
398
+ op: ex.opName,
399
+ line: ex.line,
400
+ message: `unknown op — not found in any facade schema registry`,
401
+ });
402
+ continue;
403
+ }
404
+
405
+ const result = (
406
+ schema as {
407
+ safeParse: (p: unknown) => { success: boolean; error?: ZodError };
408
+ }
409
+ ).safeParse(ex.parsedParams);
410
+
411
+ if (!result.success && result.error) {
412
+ for (const issue of result.error.issues) {
413
+ if (
414
+ isPlaceholderIssue(
415
+ issue as {
416
+ code: string;
417
+ path: (string | number)[];
418
+ received?: unknown;
419
+ message: string;
420
+ },
421
+ ex.parsedParams,
422
+ )
423
+ ) {
424
+ continue;
425
+ }
426
+ const path = issue.path.join('.');
427
+ errors.push({
428
+ file: ex.file,
429
+ op: ex.opName,
430
+ line: ex.line,
431
+ message: `${path ? path + ': ' : ''}${issue.message}`,
432
+ });
433
+ }
434
+ }
435
+ }
436
+
437
+ return errors;
438
+ }
439
+
440
+ // ── Public API ──────────────────────────────────────────────────────────
441
+
442
+ /**
443
+ * Validate all SKILL.md files found in `skillsDir`.
444
+ *
445
+ * `skillsDir` is the directory that contains skill subdirectories, e.g. ~/.claude/skills/.
446
+ * Each subdirectory is expected to have a SKILL.md file.
447
+ *
448
+ * @returns Structured result with errors, counts, and whether all examples are valid.
449
+ */
450
+ export function validateSkillDocs(skillsDir: string): SkillValidationResult {
451
+ const registry = buildSchemaRegistry();
452
+ const skillFiles = discoverSkillFiles(skillsDir);
453
+ let totalExamples = 0;
454
+ const allErrors: SkillValidationError[] = [];
455
+
456
+ for (const file of skillFiles) {
457
+ const examples = extractOpExamples(file);
458
+ totalExamples += examples.length;
459
+ const errors = validateExamples(examples, registry);
460
+ allErrors.push(...errors);
461
+ }
462
+
463
+ return {
464
+ valid: allErrors.length === 0,
465
+ errors: allErrors,
466
+ totalFiles: skillFiles.length,
467
+ totalExamples,
468
+ registrySize: registry.size,
469
+ };
470
+ }
@@ -36,6 +36,7 @@ describe('Telemetry', () => {
36
36
  expect(stats.callsByOp).toEqual({});
37
37
  expect(stats.errorsByOp).toEqual({});
38
38
  expect(stats.slowestOps).toEqual([]);
39
+ expect(typeof stats.since).toBe('number');
39
40
  expect(stats.since).toBeLessThanOrEqual(Date.now());
40
41
  });
41
42
 
@@ -128,7 +128,10 @@ describe('HttpMcpServer', () => {
128
128
  it('starts and stops without error', async () => {
129
129
  await server.start();
130
130
  const stats = server.getStats();
131
+ expect(typeof stats.uptime).toBe('number');
132
+ // uptime is elapsed ms since start — should be a small non-negative number
131
133
  expect(stats.uptime).toBeGreaterThanOrEqual(0);
134
+ expect(stats.uptime).toBeLessThan(5000); // must have completed in under 5s
132
135
  await server.stop();
133
136
  });
134
137
 
@@ -30,11 +30,13 @@ describe('SessionManager', () => {
30
30
 
31
31
  describe('add / get / remove', () => {
32
32
  it('adds and retrieves a session', () => {
33
+ const before = Date.now();
33
34
  const session = manager.add('s1', 'transport', 'server');
34
35
  expect(session.id).toBe('s1');
35
36
  expect(session.transport).toBe('transport');
36
37
  expect(session.server).toBe('server');
37
- expect(session.createdAt).toBeGreaterThan(0);
38
+ expect(session.createdAt).toBeGreaterThanOrEqual(before);
39
+ expect(session.createdAt).toBeLessThanOrEqual(Date.now());
38
40
  expect(manager.get('s1')).toBe(session);
39
41
  });
40
42
 
@@ -121,16 +121,13 @@ describe('loadToken / saveToken / getOrGenerateToken', () => {
121
121
  expect(result).toBe('trimmed-token');
122
122
  });
123
123
 
124
- it('loadToken returns undefined for empty env var', () => {
124
+ it('skips whitespace-only env var and does not return the raw whitespace value', () => {
125
125
  vi.stubEnv('MY_AGENT_HTTP_TOKEN', ' ');
126
+ // env var is whitespace-only — function must skip it and NOT return whitespace
126
127
  const result = loadToken('my-agent');
127
- // Falls through to file-based lookup
128
- expect(result === undefined || typeof result === 'string').toBe(true);
129
- });
130
-
131
- it('generateToken produces different tokens each call', () => {
132
- const t1 = generateToken();
133
- const t2 = generateToken();
134
- expect(t1).not.toBe(t2);
128
+ expect(result).not.toBe(' ');
129
+ if (result !== undefined) {
130
+ expect(result.trim().length).toBeGreaterThan(0);
131
+ }
135
132
  });
136
133
  });
@@ -124,7 +124,11 @@ describe('WsMcpServer', () => {
124
124
  describe('standalone start / stop', () => {
125
125
  it('starts and stops without error', async () => {
126
126
  await server.start(0);
127
- expect(server.getStats().uptime).toBeGreaterThanOrEqual(0);
127
+ const uptime = server.getStats().uptime;
128
+ expect(typeof uptime).toBe('number');
129
+ // uptime is elapsed ms since start — should be a small non-negative number
130
+ expect(uptime).toBeGreaterThanOrEqual(0);
131
+ expect(uptime).toBeLessThan(5000); // must have started in under 5s
128
132
  await server.stop();
129
133
  });
130
134
 
@@ -145,7 +149,11 @@ describe('WsMcpServer', () => {
145
149
  callbacks,
146
150
  );
147
151
  wsServer.attachTo(httpServer);
148
- expect(wsServer.getStats().uptime).toBeGreaterThanOrEqual(0);
152
+ const attachUptime = wsServer.getStats().uptime;
153
+ expect(typeof attachUptime).toBe('number');
154
+ // uptime is elapsed ms since attach — valid non-negative number
155
+ expect(attachUptime).toBeGreaterThanOrEqual(0);
156
+ expect(attachUptime).toBeLessThan(5000);
149
157
 
150
158
  await wsServer.stop();
151
159
  await new Promise<void>((resolve, reject) => {
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Worktree reaper — cleans up stale .claude/worktrees/ entries left by subagent execution.
3
+ *
4
+ * Claude Code creates worktrees via `isolation: "worktree"` for parallel subagent runs.
5
+ * If the agent commits changes, the worktree persists — nobody reaps it automatically.
6
+ *
7
+ * Usage: call worktreeReap() at session start and after plan completion (best-effort).
8
+ */
9
+
10
+ import { spawnSync } from 'node:child_process';
11
+ import { existsSync } from 'node:fs';
12
+ import { join } from 'node:path';
13
+
14
+ export interface ReapReport {
15
+ /** Number of worktrees successfully reaped */
16
+ reaped: number;
17
+ /** Paths of stale worktrees found */
18
+ found: string[];
19
+ /** Any errors encountered (non-fatal) */
20
+ errors: string[];
21
+ /** Whether git worktree prune ran successfully */
22
+ pruned: boolean;
23
+ }
24
+
25
+ export interface WorktreeStatus {
26
+ /** All .claude/worktrees/ entries found */
27
+ stale: Array<{ path: string; branch: string; commit: string }>;
28
+ /** Total count */
29
+ total: number;
30
+ }
31
+
32
+ /**
33
+ * Parse `git worktree list --porcelain` output into structured entries.
34
+ */
35
+ function parseWorktreeList(
36
+ output: string,
37
+ ): Array<{ path: string; branch: string; commit: string }> {
38
+ const entries: Array<{ path: string; branch: string; commit: string }> = [];
39
+ const blocks = output.trim().split(/\n\n+/);
40
+
41
+ for (const block of blocks) {
42
+ const lines = block.trim().split('\n');
43
+ let path = '';
44
+ let branch = '';
45
+ let commit = '';
46
+
47
+ for (const line of lines) {
48
+ if (line.startsWith('worktree ')) path = line.slice(9).trim();
49
+ else if (line.startsWith('HEAD ')) commit = line.slice(5).trim();
50
+ else if (line.startsWith('branch ')) branch = line.slice(7).trim();
51
+ }
52
+
53
+ if (path) entries.push({ path, branch, commit });
54
+ }
55
+
56
+ return entries;
57
+ }
58
+
59
+ /**
60
+ * Get status of stale worktrees under .claude/worktrees/ without removing them.
61
+ */
62
+ export function worktreeStatus(projectPath: string): WorktreeStatus {
63
+ const result = spawnSync('git', ['worktree', 'list', '--porcelain'], {
64
+ cwd: projectPath,
65
+ encoding: 'utf-8',
66
+ });
67
+
68
+ if (result.status !== 0 || !result.stdout) {
69
+ return { stale: [], total: 0 };
70
+ }
71
+
72
+ const all = parseWorktreeList(result.stdout);
73
+ const worktreeBase = join(projectPath, '.claude', 'worktrees');
74
+ const stale = all.filter((e) => e.path.startsWith(worktreeBase) && existsSync(e.path));
75
+
76
+ return { stale, total: stale.length };
77
+ }
78
+
79
+ /**
80
+ * Reap stale worktrees under .claude/worktrees/.
81
+ * Best-effort — errors are collected but never thrown.
82
+ */
83
+ export function worktreeReap(projectPath: string): ReapReport {
84
+ const report: ReapReport = { reaped: 0, found: [], errors: [], pruned: false };
85
+
86
+ try {
87
+ const { stale } = worktreeStatus(projectPath);
88
+
89
+ for (const { path } of stale) {
90
+ report.found.push(path);
91
+ const rm = spawnSync('git', ['worktree', 'remove', '--force', path], {
92
+ cwd: projectPath,
93
+ encoding: 'utf-8',
94
+ });
95
+ if (rm.status === 0) {
96
+ report.reaped++;
97
+ } else {
98
+ report.errors.push(`Failed to remove ${path}: ${rm.stderr?.trim() ?? 'unknown error'}`);
99
+ }
100
+ }
101
+
102
+ // Prune dangling refs
103
+ const prune = spawnSync('git', ['worktree', 'prune'], {
104
+ cwd: projectPath,
105
+ encoding: 'utf-8',
106
+ });
107
+ report.pruned = prune.status === 0;
108
+ } catch (err) {
109
+ report.errors.push(err instanceof Error ? err.message : String(err));
110
+ }
111
+
112
+ return report;
113
+ }