@united-workforce/cli 0.1.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 (310) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +221 -0
  3. package/dist/__tests__/adapter-json-roundtrip.test.d.ts +2 -0
  4. package/dist/__tests__/adapter-json-roundtrip.test.d.ts.map +1 -0
  5. package/dist/__tests__/adapter-json-roundtrip.test.js +147 -0
  6. package/dist/__tests__/adapter-json-roundtrip.test.js.map +1 -0
  7. package/dist/__tests__/config.test.d.ts +2 -0
  8. package/dist/__tests__/config.test.d.ts.map +1 -0
  9. package/dist/__tests__/config.test.js +685 -0
  10. package/dist/__tests__/config.test.js.map +1 -0
  11. package/dist/__tests__/current-role.test.d.ts +2 -0
  12. package/dist/__tests__/current-role.test.d.ts.map +1 -0
  13. package/dist/__tests__/current-role.test.js +401 -0
  14. package/dist/__tests__/current-role.test.js.map +1 -0
  15. package/dist/__tests__/e2e-mock-agent.test.d.ts +2 -0
  16. package/dist/__tests__/e2e-mock-agent.test.d.ts.map +1 -0
  17. package/dist/__tests__/e2e-mock-agent.test.js +401 -0
  18. package/dist/__tests__/e2e-mock-agent.test.js.map +1 -0
  19. package/dist/__tests__/include-tag.test.d.ts +2 -0
  20. package/dist/__tests__/include-tag.test.d.ts.map +1 -0
  21. package/dist/__tests__/include-tag.test.js +69 -0
  22. package/dist/__tests__/include-tag.test.js.map +1 -0
  23. package/dist/__tests__/log.test.d.ts +2 -0
  24. package/dist/__tests__/log.test.d.ts.map +1 -0
  25. package/dist/__tests__/log.test.js +161 -0
  26. package/dist/__tests__/log.test.js.map +1 -0
  27. package/dist/__tests__/moderator-evaluate.test.d.ts +2 -0
  28. package/dist/__tests__/moderator-evaluate.test.d.ts.map +1 -0
  29. package/dist/__tests__/moderator-evaluate.test.js +170 -0
  30. package/dist/__tests__/moderator-evaluate.test.js.map +1 -0
  31. package/dist/__tests__/preload.d.ts +3 -0
  32. package/dist/__tests__/preload.d.ts.map +1 -0
  33. package/dist/__tests__/preload.js +6 -0
  34. package/dist/__tests__/preload.js.map +1 -0
  35. package/dist/__tests__/prompt.test.d.ts +2 -0
  36. package/dist/__tests__/prompt.test.d.ts.map +1 -0
  37. package/dist/__tests__/prompt.test.js +111 -0
  38. package/dist/__tests__/prompt.test.js.map +1 -0
  39. package/dist/__tests__/resolve-head-hash.test.d.ts +2 -0
  40. package/dist/__tests__/resolve-head-hash.test.d.ts.map +1 -0
  41. package/dist/__tests__/resolve-head-hash.test.js +66 -0
  42. package/dist/__tests__/resolve-head-hash.test.js.map +1 -0
  43. package/dist/__tests__/setup-agent-discovery.test.d.ts +2 -0
  44. package/dist/__tests__/setup-agent-discovery.test.d.ts.map +1 -0
  45. package/dist/__tests__/setup-agent-discovery.test.js +119 -0
  46. package/dist/__tests__/setup-agent-discovery.test.js.map +1 -0
  47. package/dist/__tests__/setup-complexity.test.d.ts +2 -0
  48. package/dist/__tests__/setup-complexity.test.d.ts.map +1 -0
  49. package/dist/__tests__/setup-complexity.test.js +314 -0
  50. package/dist/__tests__/setup-complexity.test.js.map +1 -0
  51. package/dist/__tests__/setup-validate.test.d.ts +2 -0
  52. package/dist/__tests__/setup-validate.test.d.ts.map +1 -0
  53. package/dist/__tests__/setup-validate.test.js +108 -0
  54. package/dist/__tests__/setup-validate.test.js.map +1 -0
  55. package/dist/__tests__/solve-issue-tea-worktree.test.d.ts +2 -0
  56. package/dist/__tests__/solve-issue-tea-worktree.test.d.ts.map +1 -0
  57. package/dist/__tests__/solve-issue-tea-worktree.test.js +107 -0
  58. package/dist/__tests__/solve-issue-tea-worktree.test.js.map +1 -0
  59. package/dist/__tests__/spawn-agent-json.test.d.ts +2 -0
  60. package/dist/__tests__/spawn-agent-json.test.d.ts.map +1 -0
  61. package/dist/__tests__/spawn-agent-json.test.js +79 -0
  62. package/dist/__tests__/spawn-agent-json.test.js.map +1 -0
  63. package/dist/__tests__/step-read.test.d.ts +2 -0
  64. package/dist/__tests__/step-read.test.d.ts.map +1 -0
  65. package/dist/__tests__/step-read.test.js +561 -0
  66. package/dist/__tests__/step-read.test.js.map +1 -0
  67. package/dist/__tests__/step-show-json.test.d.ts +2 -0
  68. package/dist/__tests__/step-show-json.test.d.ts.map +1 -0
  69. package/dist/__tests__/step-show-json.test.js +311 -0
  70. package/dist/__tests__/step-show-json.test.js.map +1 -0
  71. package/dist/__tests__/step-timing.test.d.ts +2 -0
  72. package/dist/__tests__/step-timing.test.d.ts.map +1 -0
  73. package/dist/__tests__/step-timing.test.js +345 -0
  74. package/dist/__tests__/step-timing.test.js.map +1 -0
  75. package/dist/__tests__/store-global-cas.test.d.ts +2 -0
  76. package/dist/__tests__/store-global-cas.test.d.ts.map +1 -0
  77. package/dist/__tests__/store-global-cas.test.js +235 -0
  78. package/dist/__tests__/store-global-cas.test.js.map +1 -0
  79. package/dist/__tests__/store-storage-root.test.d.ts +2 -0
  80. package/dist/__tests__/store-storage-root.test.d.ts.map +1 -0
  81. package/dist/__tests__/store-storage-root.test.js +43 -0
  82. package/dist/__tests__/store-storage-root.test.js.map +1 -0
  83. package/dist/__tests__/store-unified-threads.test.d.ts +2 -0
  84. package/dist/__tests__/store-unified-threads.test.d.ts.map +1 -0
  85. package/dist/__tests__/store-unified-threads.test.js +189 -0
  86. package/dist/__tests__/store-unified-threads.test.js.map +1 -0
  87. package/dist/__tests__/thread-cancel-status.test.d.ts +2 -0
  88. package/dist/__tests__/thread-cancel-status.test.d.ts.map +1 -0
  89. package/dist/__tests__/thread-cancel-status.test.js +111 -0
  90. package/dist/__tests__/thread-cancel-status.test.js.map +1 -0
  91. package/dist/__tests__/thread-list-filters.test.d.ts +2 -0
  92. package/dist/__tests__/thread-list-filters.test.d.ts.map +1 -0
  93. package/dist/__tests__/thread-list-filters.test.js +442 -0
  94. package/dist/__tests__/thread-list-filters.test.js.map +1 -0
  95. package/dist/__tests__/thread-location.test.d.ts +2 -0
  96. package/dist/__tests__/thread-location.test.d.ts.map +1 -0
  97. package/dist/__tests__/thread-location.test.js +159 -0
  98. package/dist/__tests__/thread-location.test.js.map +1 -0
  99. package/dist/__tests__/thread-read-quota.test.d.ts +2 -0
  100. package/dist/__tests__/thread-read-quota.test.d.ts.map +1 -0
  101. package/dist/__tests__/thread-read-quota.test.js +546 -0
  102. package/dist/__tests__/thread-read-quota.test.js.map +1 -0
  103. package/dist/__tests__/thread-read-xml-tags.test.d.ts +2 -0
  104. package/dist/__tests__/thread-read-xml-tags.test.d.ts.map +1 -0
  105. package/dist/__tests__/thread-read-xml-tags.test.js +610 -0
  106. package/dist/__tests__/thread-read-xml-tags.test.js.map +1 -0
  107. package/dist/__tests__/thread-resume.test.d.ts +2 -0
  108. package/dist/__tests__/thread-resume.test.d.ts.map +1 -0
  109. package/dist/__tests__/thread-resume.test.js +592 -0
  110. package/dist/__tests__/thread-resume.test.js.map +1 -0
  111. package/dist/__tests__/thread-show-status.test.d.ts +2 -0
  112. package/dist/__tests__/thread-show-status.test.d.ts.map +1 -0
  113. package/dist/__tests__/thread-show-status.test.js +267 -0
  114. package/dist/__tests__/thread-show-status.test.js.map +1 -0
  115. package/dist/__tests__/thread-start-cwd-cli.test.d.ts +2 -0
  116. package/dist/__tests__/thread-start-cwd-cli.test.d.ts.map +1 -0
  117. package/dist/__tests__/thread-start-cwd-cli.test.js +130 -0
  118. package/dist/__tests__/thread-start-cwd-cli.test.js.map +1 -0
  119. package/dist/__tests__/thread-step-count.test.d.ts +2 -0
  120. package/dist/__tests__/thread-step-count.test.d.ts.map +1 -0
  121. package/dist/__tests__/thread-step-count.test.js +55 -0
  122. package/dist/__tests__/thread-step-count.test.js.map +1 -0
  123. package/dist/__tests__/thread-suspend-step.test.d.ts +2 -0
  124. package/dist/__tests__/thread-suspend-step.test.d.ts.map +1 -0
  125. package/dist/__tests__/thread-suspend-step.test.js +155 -0
  126. package/dist/__tests__/thread-suspend-step.test.js.map +1 -0
  127. package/dist/__tests__/thread-suspended-display.test.d.ts +2 -0
  128. package/dist/__tests__/thread-suspended-display.test.d.ts.map +1 -0
  129. package/dist/__tests__/thread-suspended-display.test.js +247 -0
  130. package/dist/__tests__/thread-suspended-display.test.js.map +1 -0
  131. package/dist/__tests__/thread-test-helpers.d.ts +4 -0
  132. package/dist/__tests__/thread-test-helpers.d.ts.map +1 -0
  133. package/dist/__tests__/thread-test-helpers.js +23 -0
  134. package/dist/__tests__/thread-test-helpers.js.map +1 -0
  135. package/dist/__tests__/thread.test.d.ts +2 -0
  136. package/dist/__tests__/thread.test.d.ts.map +1 -0
  137. package/dist/__tests__/thread.test.js +883 -0
  138. package/dist/__tests__/thread.test.js.map +1 -0
  139. package/dist/__tests__/validate-semantic.test.d.ts +2 -0
  140. package/dist/__tests__/validate-semantic.test.d.ts.map +1 -0
  141. package/dist/__tests__/validate-semantic.test.js +408 -0
  142. package/dist/__tests__/validate-semantic.test.js.map +1 -0
  143. package/dist/__tests__/workflow-resolution.test.d.ts +2 -0
  144. package/dist/__tests__/workflow-resolution.test.d.ts.map +1 -0
  145. package/dist/__tests__/workflow-resolution.test.js +308 -0
  146. package/dist/__tests__/workflow-resolution.test.js.map +1 -0
  147. package/dist/background/background.d.ts +38 -0
  148. package/dist/background/background.d.ts.map +1 -0
  149. package/dist/background/background.js +123 -0
  150. package/dist/background/background.js.map +1 -0
  151. package/dist/background/index.d.ts +3 -0
  152. package/dist/background/index.d.ts.map +1 -0
  153. package/dist/background/index.js +2 -0
  154. package/dist/background/index.js.map +1 -0
  155. package/dist/background/types.d.ts +9 -0
  156. package/dist/background/types.d.ts.map +1 -0
  157. package/dist/background/types.js +2 -0
  158. package/dist/background/types.js.map +1 -0
  159. package/dist/cli.d.ts +3 -0
  160. package/dist/cli.d.ts.map +1 -0
  161. package/dist/cli.js +535 -0
  162. package/dist/cli.js.map +1 -0
  163. package/dist/commands/config.d.ts +41 -0
  164. package/dist/commands/config.d.ts.map +1 -0
  165. package/dist/commands/config.js +252 -0
  166. package/dist/commands/config.js.map +1 -0
  167. package/dist/commands/log.d.ts +26 -0
  168. package/dist/commands/log.d.ts.map +1 -0
  169. package/dist/commands/log.js +79 -0
  170. package/dist/commands/log.js.map +1 -0
  171. package/dist/commands/prompt.d.ts +6 -0
  172. package/dist/commands/prompt.d.ts.map +1 -0
  173. package/dist/commands/prompt.js +67 -0
  174. package/dist/commands/prompt.js.map +1 -0
  175. package/dist/commands/setup.d.ts +73 -0
  176. package/dist/commands/setup.d.ts.map +1 -0
  177. package/dist/commands/setup.js +522 -0
  178. package/dist/commands/setup.js.map +1 -0
  179. package/dist/commands/shared.d.ts +31 -0
  180. package/dist/commands/shared.d.ts.map +1 -0
  181. package/dist/commands/shared.js +154 -0
  182. package/dist/commands/shared.js.map +1 -0
  183. package/dist/commands/step.d.ts +18 -0
  184. package/dist/commands/step.d.ts.map +1 -0
  185. package/dist/commands/step.js +257 -0
  186. package/dist/commands/step.js.map +1 -0
  187. package/dist/commands/thread-time-parser.d.ts +6 -0
  188. package/dist/commands/thread-time-parser.d.ts.map +1 -0
  189. package/dist/commands/thread-time-parser.js +22 -0
  190. package/dist/commands/thread-time-parser.js.map +1 -0
  191. package/dist/commands/thread.d.ts +38 -0
  192. package/dist/commands/thread.d.ts.map +1 -0
  193. package/dist/commands/thread.js +1087 -0
  194. package/dist/commands/thread.js.map +1 -0
  195. package/dist/commands/workflow.d.ts +24 -0
  196. package/dist/commands/workflow.d.ts.map +1 -0
  197. package/dist/commands/workflow.js +138 -0
  198. package/dist/commands/workflow.js.map +1 -0
  199. package/dist/format.d.ts +3 -0
  200. package/dist/format.d.ts.map +1 -0
  201. package/dist/format.js +10 -0
  202. package/dist/format.js.map +1 -0
  203. package/dist/include.d.ts +12 -0
  204. package/dist/include.d.ts.map +1 -0
  205. package/dist/include.js +35 -0
  206. package/dist/include.js.map +1 -0
  207. package/dist/moderator/__tests__/evaluate.test.d.ts +2 -0
  208. package/dist/moderator/__tests__/evaluate.test.d.ts.map +1 -0
  209. package/dist/moderator/__tests__/evaluate.test.js +167 -0
  210. package/dist/moderator/__tests__/evaluate.test.js.map +1 -0
  211. package/dist/moderator/evaluate.d.ts +6 -0
  212. package/dist/moderator/evaluate.d.ts.map +1 -0
  213. package/dist/moderator/evaluate.js +65 -0
  214. package/dist/moderator/evaluate.js.map +1 -0
  215. package/dist/moderator/index.d.ts +4 -0
  216. package/dist/moderator/index.d.ts.map +1 -0
  217. package/dist/moderator/index.js +3 -0
  218. package/dist/moderator/index.js.map +1 -0
  219. package/dist/moderator/types.d.ts +25 -0
  220. package/dist/moderator/types.d.ts.map +1 -0
  221. package/dist/moderator/types.js +4 -0
  222. package/dist/moderator/types.js.map +1 -0
  223. package/dist/schemas.d.ts +16 -0
  224. package/dist/schemas.d.ts.map +1 -0
  225. package/dist/schemas.js +17 -0
  226. package/dist/schemas.js.map +1 -0
  227. package/dist/store.d.ts +77 -0
  228. package/dist/store.d.ts.map +1 -0
  229. package/dist/store.js +392 -0
  230. package/dist/store.js.map +1 -0
  231. package/dist/validate-semantic.d.ts +7 -0
  232. package/dist/validate-semantic.d.ts.map +1 -0
  233. package/dist/validate-semantic.js +263 -0
  234. package/dist/validate-semantic.js.map +1 -0
  235. package/dist/validate.d.ts +16 -0
  236. package/dist/validate.d.ts.map +1 -0
  237. package/dist/validate.js +115 -0
  238. package/dist/validate.js.map +1 -0
  239. package/package.json +44 -0
  240. package/src/__tests__/adapter-json-roundtrip.test.ts +181 -0
  241. package/src/__tests__/config.test.ts +740 -0
  242. package/src/__tests__/current-role.test.ts +438 -0
  243. package/src/__tests__/e2e-mock-agent.test.ts +498 -0
  244. package/src/__tests__/fixtures/e2e-completed-resume.mock.yaml +15 -0
  245. package/src/__tests__/fixtures/e2e-count.mock.yaml +19 -0
  246. package/src/__tests__/fixtures/e2e-count.workflow.yaml +45 -0
  247. package/src/__tests__/fixtures/e2e-linear.mock.yaml +13 -0
  248. package/src/__tests__/fixtures/e2e-linear.workflow.yaml +32 -0
  249. package/src/__tests__/fixtures/e2e-loop.mock.yaml +25 -0
  250. package/src/__tests__/fixtures/e2e-loop.workflow.yaml +36 -0
  251. package/src/__tests__/fixtures/e2e-mismatch.mock.yaml +16 -0
  252. package/src/__tests__/fixtures/e2e-mustache.mock.yaml +15 -0
  253. package/src/__tests__/fixtures/e2e-mustache.workflow.yaml +34 -0
  254. package/src/__tests__/fixtures/e2e-suspend.mock.yaml +14 -0
  255. package/src/__tests__/fixtures/e2e-suspend.workflow.yaml +24 -0
  256. package/src/__tests__/include-tag.test.ts +84 -0
  257. package/src/__tests__/log.test.ts +181 -0
  258. package/src/__tests__/moderator-evaluate.test.ts +186 -0
  259. package/src/__tests__/preload.ts +7 -0
  260. package/src/__tests__/prompt.test.ts +129 -0
  261. package/src/__tests__/resolve-head-hash.test.ts +86 -0
  262. package/src/__tests__/setup-agent-discovery.test.ts +167 -0
  263. package/src/__tests__/setup-complexity.test.ts +381 -0
  264. package/src/__tests__/setup-validate.test.ts +148 -0
  265. package/src/__tests__/solve-issue-tea-worktree.test.ts +144 -0
  266. package/src/__tests__/spawn-agent-json.test.ts +100 -0
  267. package/src/__tests__/step-read.test.ts +632 -0
  268. package/src/__tests__/step-show-json.test.ts +373 -0
  269. package/src/__tests__/step-timing.test.ts +392 -0
  270. package/src/__tests__/store-global-cas.test.ts +308 -0
  271. package/src/__tests__/store-storage-root.test.ts +49 -0
  272. package/src/__tests__/store-unified-threads.test.ts +235 -0
  273. package/src/__tests__/thread-cancel-status.test.ts +138 -0
  274. package/src/__tests__/thread-list-filters.test.ts +572 -0
  275. package/src/__tests__/thread-location.test.ts +186 -0
  276. package/src/__tests__/thread-read-quota.test.ts +613 -0
  277. package/src/__tests__/thread-read-xml-tags.test.ts +717 -0
  278. package/src/__tests__/thread-resume.test.ts +710 -0
  279. package/src/__tests__/thread-show-status.test.ts +317 -0
  280. package/src/__tests__/thread-start-cwd-cli.test.ts +164 -0
  281. package/src/__tests__/thread-step-count.test.ts +70 -0
  282. package/src/__tests__/thread-suspend-step.test.ts +181 -0
  283. package/src/__tests__/thread-suspended-display.test.ts +287 -0
  284. package/src/__tests__/thread-test-helpers.ts +37 -0
  285. package/src/__tests__/thread.test.ts +1025 -0
  286. package/src/__tests__/validate-semantic.test.ts +474 -0
  287. package/src/__tests__/workflow-resolution.test.ts +421 -0
  288. package/src/background/background.ts +147 -0
  289. package/src/background/index.ts +11 -0
  290. package/src/background/types.ts +9 -0
  291. package/src/cli.ts +692 -0
  292. package/src/commands/config.ts +304 -0
  293. package/src/commands/log.ts +116 -0
  294. package/src/commands/prompt.ts +81 -0
  295. package/src/commands/setup.ts +603 -0
  296. package/src/commands/shared.ts +227 -0
  297. package/src/commands/step.ts +343 -0
  298. package/src/commands/thread-time-parser.ts +23 -0
  299. package/src/commands/thread.ts +1575 -0
  300. package/src/commands/workflow.ts +213 -0
  301. package/src/format.ts +12 -0
  302. package/src/include.ts +37 -0
  303. package/src/moderator/__tests__/evaluate.test.ts +199 -0
  304. package/src/moderator/evaluate.ts +80 -0
  305. package/src/moderator/index.ts +7 -0
  306. package/src/moderator/types.ts +24 -0
  307. package/src/schemas.ts +26 -0
  308. package/src/store.ts +479 -0
  309. package/src/validate-semantic.ts +304 -0
  310. package/src/validate.ts +137 -0
@@ -0,0 +1,16 @@
1
+ # Reuses the test-linear workflow. The moderator routes step 0 -> planner and
2
+ # step 1 -> worker, but step[1].role below is "planner", so the mock agent must
3
+ # detect the role mismatch on the second step and exit with an error.
4
+ steps:
5
+ - role: planner
6
+ output: |
7
+ ---
8
+ $status: ready
9
+ ---
10
+ Planning complete.
11
+ - role: planner
12
+ output: |
13
+ ---
14
+ $status: done
15
+ ---
16
+ This step claims to be planner, but the moderator routes to worker.
@@ -0,0 +1,15 @@
1
+ steps:
2
+ - role: planner
3
+ output: |
4
+ ---
5
+ $status: ready
6
+ branch: fix/42-auth
7
+ repoPath: /tmp/my-repo
8
+ ---
9
+ Planned the work.
10
+ - role: worker
11
+ output: |
12
+ ---
13
+ $status: done
14
+ ---
15
+ Work complete.
@@ -0,0 +1,34 @@
1
+ name: test-mustache
2
+ description: Planner emits template variables consumed by the worker edge prompt
3
+ roles:
4
+ planner:
5
+ description: Plans work and emits branch + repo path
6
+ goal: Plan the task
7
+ capabilities: []
8
+ procedure: Decide the branch and repo path
9
+ output: Set $status to ready and emit branch and repoPath
10
+ frontmatter:
11
+ oneOf:
12
+ - properties:
13
+ $status: { const: ready }
14
+ branch: { type: string }
15
+ repoPath: { type: string }
16
+ required: [$status, branch, repoPath]
17
+ worker:
18
+ description: Works on the planned branch
19
+ goal: Do the work
20
+ capabilities: []
21
+ procedure: Do it
22
+ output: Output the result and set $status to done
23
+ frontmatter:
24
+ oneOf:
25
+ - properties:
26
+ $status: { const: done }
27
+ required: [$status]
28
+ graph:
29
+ $START:
30
+ _: { role: planner, prompt: 'Plan the task' }
31
+ planner:
32
+ ready: { role: worker, prompt: 'Work on branch {{{branch}}} in {{{repoPath}}}' }
33
+ worker:
34
+ done: { role: '$END', prompt: 'Complete' }
@@ -0,0 +1,14 @@
1
+ steps:
2
+ - role: planner
3
+ output: |
4
+ ---
5
+ $status: insufficient_info
6
+ reason: missing requirements
7
+ ---
8
+ I need more information before I can plan this.
9
+ - role: planner
10
+ output: |
11
+ ---
12
+ $status: ready
13
+ ---
14
+ I now have what I need. Ready to proceed.
@@ -0,0 +1,24 @@
1
+ name: test-suspend
2
+ description: Planner can suspend for more info or finish when ready
3
+ roles:
4
+ planner:
5
+ description: Plans work and may request more info
6
+ goal: Analyze the task
7
+ capabilities: []
8
+ procedure: Analyze the task and decide if more info is needed
9
+ output: Set $status to insufficient_info (with reason) or ready
10
+ frontmatter:
11
+ oneOf:
12
+ - properties:
13
+ $status: { const: insufficient_info }
14
+ reason: { type: string }
15
+ required: [$status, reason]
16
+ - properties:
17
+ $status: { const: ready }
18
+ required: [$status]
19
+ graph:
20
+ $START:
21
+ _: { role: planner, prompt: 'Analyze the task' }
22
+ planner:
23
+ insufficient_info: { role: '$SUSPEND', prompt: 'Need more info: {{{reason}}}' }
24
+ ready: { role: '$END', prompt: 'Done' }
@@ -0,0 +1,84 @@
1
+ import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { afterEach, beforeEach, describe, expect, test } from "vitest";
5
+ import { parse } from "yaml";
6
+ import { createIncludeTag } from "../include.js";
7
+
8
+ let tmpDir: string;
9
+
10
+ beforeEach(async () => {
11
+ tmpDir = await mkdtemp(join(tmpdir(), "include-tag-test-"));
12
+ });
13
+
14
+ afterEach(async () => {
15
+ await rm(tmpDir, { recursive: true, force: true });
16
+ });
17
+
18
+ describe("!include tag", () => {
19
+ test("includes .md file as string", async () => {
20
+ await writeFile(join(tmpDir, "prompt.md"), "You are an analyst.");
21
+ const yaml = "system: !include prompt.md";
22
+ const result = parse(yaml, { customTags: [createIncludeTag(tmpDir)] });
23
+ expect(result.system).toBe("You are an analyst.");
24
+ });
25
+
26
+ test("includes .json file as parsed object", async () => {
27
+ await writeFile(join(tmpDir, "schema.json"), '{"type":"object","properties":{}}');
28
+ const yaml = "outputSchema: !include schema.json";
29
+ const result = parse(yaml, { customTags: [createIncludeTag(tmpDir)] });
30
+ expect(result.outputSchema).toEqual({ type: "object", properties: {} });
31
+ });
32
+
33
+ test("includes .yaml file as parsed object", async () => {
34
+ await writeFile(join(tmpDir, "config.yaml"), "key: value\nlist:\n - a\n - b");
35
+ const yaml = "config: !include config.yaml";
36
+ const result = parse(yaml, { customTags: [createIncludeTag(tmpDir)] });
37
+ expect(result.config).toEqual({ key: "value", list: ["a", "b"] });
38
+ });
39
+
40
+ test("resolves relative subdirectory paths", async () => {
41
+ const subdir = join(tmpDir, "roles");
42
+ await mkdir(subdir, { recursive: true });
43
+ await writeFile(join(subdir, "analyst.md"), "Analyze data.");
44
+ const yaml = "system: !include roles/analyst.md";
45
+ const result = parse(yaml, { customTags: [createIncludeTag(tmpDir)] });
46
+ expect(result.system).toBe("Analyze data.");
47
+ });
48
+
49
+ test("throws on missing file", () => {
50
+ const yaml = "system: !include nonexistent.md";
51
+ expect(() => parse(yaml, { customTags: [createIncludeTag(tmpDir)] })).toThrow();
52
+ });
53
+
54
+ test("includes .txt file as string", async () => {
55
+ await writeFile(join(tmpDir, "note.txt"), "Hello world");
56
+ const yaml = "note: !include note.txt";
57
+ const result = parse(yaml, { customTags: [createIncludeTag(tmpDir)] });
58
+ expect(result.note).toBe("Hello world");
59
+ });
60
+
61
+ test("blocks path traversal with ../", async () => {
62
+ const yaml = "secret: !include ../../etc/passwd";
63
+ expect(() => parse(yaml, { customTags: [createIncludeTag(tmpDir)] })).toThrow(
64
+ /path traversal blocked/,
65
+ );
66
+ });
67
+
68
+ test("blocks absolute path traversal", async () => {
69
+ const yaml = "secret: !include /etc/passwd";
70
+ expect(() => parse(yaml, { customTags: [createIncludeTag(tmpDir)] })).toThrow(
71
+ /path traversal blocked/,
72
+ );
73
+ });
74
+
75
+ test("supports nested !include in yaml files", async () => {
76
+ const subdir = join(tmpDir, "parts");
77
+ await mkdir(subdir, { recursive: true });
78
+ await writeFile(join(subdir, "inner.md"), "nested content");
79
+ await writeFile(join(tmpDir, "outer.yaml"), "value: !include parts/inner.md");
80
+ const yaml = "config: !include outer.yaml";
81
+ const result = parse(yaml, { customTags: [createIncludeTag(tmpDir)] });
82
+ expect(result.config).toEqual({ value: "nested content" });
83
+ });
84
+ });
@@ -0,0 +1,181 @@
1
+ import { mkdir, readdir, rm, writeFile } from "node:fs/promises";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { afterEach, beforeEach, describe, expect, test } from "vitest";
5
+ import { cmdLogClean, cmdLogList, cmdLogShow } from "../commands/log.js";
6
+
7
+ let storageRoot: string;
8
+
9
+ beforeEach(async () => {
10
+ storageRoot = join(tmpdir(), `uwf-log-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
11
+ await mkdir(join(storageRoot, "logs"), { recursive: true });
12
+ });
13
+
14
+ afterEach(async () => {
15
+ await rm(storageRoot, { recursive: true, force: true });
16
+ });
17
+
18
+ const entry1 = JSON.stringify({
19
+ ts: "2026-05-20T10:00:00.000Z",
20
+ pid: "1716200000000-1234",
21
+ tag: "W9F3RK2M",
22
+ msg: "process start",
23
+ thread: "01J1234ABCDEF",
24
+ workflow: "solve-issue",
25
+ });
26
+
27
+ const entry2 = JSON.stringify({
28
+ ts: "2026-05-20T10:00:01.000Z",
29
+ pid: "1716200000000-1234",
30
+ tag: "ABC12345",
31
+ msg: "step executed",
32
+ thread: "01J1234ABCDEF",
33
+ workflow: "solve-issue",
34
+ });
35
+
36
+ const entry3 = JSON.stringify({
37
+ ts: "2026-05-20T10:00:02.000Z",
38
+ pid: "1716200000000-5678",
39
+ tag: "XYZ98765",
40
+ msg: "different process",
41
+ thread: "01JOTHER000000",
42
+ workflow: "review-code",
43
+ });
44
+
45
+ const oldEntry = JSON.stringify({
46
+ ts: "2026-05-19T08:00:00.000Z",
47
+ pid: "1716200000000-9999",
48
+ tag: "OLD1TAG1",
49
+ msg: "old entry",
50
+ thread: "01JOLD0000000",
51
+ workflow: "solve-issue",
52
+ });
53
+
54
+ const olderEntry = JSON.stringify({
55
+ ts: "2026-05-18T08:00:00.000Z",
56
+ pid: "1716200000000-0001",
57
+ tag: "OLD2TAG2",
58
+ msg: "older entry",
59
+ thread: "01JOLDER00000",
60
+ workflow: "review-code",
61
+ });
62
+
63
+ async function writeLogFiles(): Promise<void> {
64
+ const logsDir = join(storageRoot, "logs");
65
+ await writeFile(join(logsDir, "2026-05-20.jsonl"), `${[entry1, entry2, entry3].join("\n")}\n`);
66
+ await writeFile(join(logsDir, "2026-05-19.jsonl"), `${oldEntry}\n`);
67
+ await writeFile(join(logsDir, "2026-05-18.jsonl"), `${olderEntry}\n`);
68
+ }
69
+
70
+ describe("cmdLogList", () => {
71
+ test("lists log files with sizes sorted by date descending", async () => {
72
+ await writeLogFiles();
73
+ const result = await cmdLogList(storageRoot);
74
+ expect(result).toHaveLength(3);
75
+ expect(result[0].name).toBe("2026-05-20.jsonl");
76
+ expect(result[0].date).toBe("2026-05-20");
77
+ expect(result[0].size).toBeGreaterThan(0);
78
+ expect(result[1].name).toBe("2026-05-19.jsonl");
79
+ expect(result[2].name).toBe("2026-05-18.jsonl");
80
+ });
81
+
82
+ test("returns empty array when no log files exist", async () => {
83
+ const result = await cmdLogList(storageRoot);
84
+ expect(result).toEqual([]);
85
+ });
86
+
87
+ test("returns empty array when logs directory does not exist", async () => {
88
+ const noLogsRoot = join(storageRoot, "nonexistent");
89
+ await mkdir(noLogsRoot, { recursive: true });
90
+ const result = await cmdLogList(noLogsRoot);
91
+ expect(result).toEqual([]);
92
+ });
93
+ });
94
+
95
+ describe("cmdLogShow", () => {
96
+ test("filters by thread ID", async () => {
97
+ await writeLogFiles();
98
+ const result = await cmdLogShow(storageRoot, {
99
+ thread: "01J1234ABCDEF",
100
+ process: null,
101
+ date: null,
102
+ });
103
+ expect(result).toHaveLength(2);
104
+ expect(result.every((e) => e.thread === "01J1234ABCDEF")).toBe(true);
105
+ });
106
+
107
+ test("filters by process ID", async () => {
108
+ await writeLogFiles();
109
+ const result = await cmdLogShow(storageRoot, {
110
+ thread: null,
111
+ process: "1716200000000-1234",
112
+ date: null,
113
+ });
114
+ expect(result).toHaveLength(2);
115
+ expect(result.every((e) => e.pid === "1716200000000-1234")).toBe(true);
116
+ });
117
+
118
+ test("filters by date", async () => {
119
+ await writeLogFiles();
120
+ const result = await cmdLogShow(storageRoot, {
121
+ thread: null,
122
+ process: null,
123
+ date: "2026-05-19",
124
+ });
125
+ expect(result).toHaveLength(1);
126
+ expect(result[0].msg).toBe("old entry");
127
+ });
128
+
129
+ test("reads all files when no date filter", async () => {
130
+ await writeLogFiles();
131
+ const result = await cmdLogShow(storageRoot, { thread: null, process: null, date: null });
132
+ expect(result).toHaveLength(5);
133
+ // sorted by ts ascending
134
+ expect(result[0].ts).toBe("2026-05-18T08:00:00.000Z");
135
+ expect(result[4].ts).toBe("2026-05-20T10:00:02.000Z");
136
+ });
137
+
138
+ test("returns empty when no matches", async () => {
139
+ await writeLogFiles();
140
+ const result = await cmdLogShow(storageRoot, {
141
+ thread: "NONEXISTENT",
142
+ process: null,
143
+ date: null,
144
+ });
145
+ expect(result).toEqual([]);
146
+ });
147
+
148
+ test("combined thread + date filter", async () => {
149
+ await writeLogFiles();
150
+ const result = await cmdLogShow(storageRoot, {
151
+ thread: "01J1234ABCDEF",
152
+ process: null,
153
+ date: "2026-05-20",
154
+ });
155
+ expect(result).toHaveLength(2);
156
+ expect(result.every((e) => e.thread === "01J1234ABCDEF")).toBe(true);
157
+ });
158
+ });
159
+
160
+ describe("cmdLogClean", () => {
161
+ test("deletes files before given date", async () => {
162
+ await writeLogFiles();
163
+ const result = await cmdLogClean(storageRoot, "2026-05-20");
164
+ expect(result.deleted).toBe(2);
165
+ const remaining = await readdir(join(storageRoot, "logs"));
166
+ expect(remaining).toEqual(["2026-05-20.jsonl"]);
167
+ });
168
+
169
+ test("deletes nothing when all files are newer", async () => {
170
+ await writeLogFiles();
171
+ const result = await cmdLogClean(storageRoot, "2026-05-18");
172
+ expect(result.deleted).toBe(0);
173
+ });
174
+
175
+ test("handles missing logs directory gracefully", async () => {
176
+ const noLogsRoot = join(storageRoot, "nonexistent");
177
+ await mkdir(noLogsRoot, { recursive: true });
178
+ const result = await cmdLogClean(noLogsRoot, "2026-05-20");
179
+ expect(result).toEqual({ deleted: 0 });
180
+ });
181
+ });
@@ -0,0 +1,186 @@
1
+ import type { Target, WorkflowPayload } from "@united-workforce/protocol";
2
+ import { describe, expect, test } from "vitest";
3
+
4
+ import { evaluate } from "../moderator/evaluate.js";
5
+
6
+ const solveIssueGraph: WorkflowPayload["graph"] = {
7
+ $START: {
8
+ _: { role: "planner", prompt: "Start planning from the issue in the task.", location: null },
9
+ },
10
+ planner: {
11
+ planned: { role: "developer", prompt: "Implement the plan: {{plan}}", location: null },
12
+ },
13
+ developer: {
14
+ implemented: { role: "reviewer", prompt: "Review the changes: {{summary}}", location: null },
15
+ },
16
+ reviewer: {
17
+ approved: { role: "$END", prompt: "Done.", location: null },
18
+ rejected: { role: "developer", prompt: "Fix: {{comments}}", location: null },
19
+ },
20
+ };
21
+
22
+ describe("evaluate", () => {
23
+ test("$START → first role (unit status _)", () => {
24
+ const result = evaluate(solveIssueGraph, "$START", { $status: "_" });
25
+ expect(result).toEqual({
26
+ ok: true,
27
+ value: {
28
+ role: "planner",
29
+ prompt: "Start planning from the issue in the task.",
30
+ location: null,
31
+ },
32
+ });
33
+ });
34
+
35
+ test("status-based routing (reviewer rejected → developer)", () => {
36
+ const result = evaluate(solveIssueGraph, "reviewer", {
37
+ $status: "rejected",
38
+ comments: "missing tests",
39
+ });
40
+ expect(result).toEqual({
41
+ ok: true,
42
+ value: { role: "developer", prompt: "Fix: missing tests", location: null },
43
+ });
44
+ });
45
+
46
+ test("status-based routing (reviewer approved → $END)", () => {
47
+ const result = evaluate(solveIssueGraph, "reviewer", { $status: "approved" });
48
+ expect(result).toEqual({
49
+ ok: true,
50
+ value: { role: "$END", prompt: "Done.", location: null },
51
+ });
52
+ });
53
+
54
+ test("status-based routing (needs input → $SUSPEND)", () => {
55
+ const graph: Record<string, Record<string, Target>> = {
56
+ ...solveIssueGraph,
57
+ reviewer: {
58
+ ...solveIssueGraph.reviewer,
59
+ needs_input: { role: "$SUSPEND", prompt: "Waiting for user input.", location: null },
60
+ },
61
+ };
62
+ const result = evaluate(graph, "reviewer", { $status: "needs_input" });
63
+ expect(result).toEqual({
64
+ ok: true,
65
+ value: {
66
+ action: "suspend",
67
+ suspendedRole: "reviewer",
68
+ prompt: "Waiting for user input.",
69
+ },
70
+ });
71
+ });
72
+
73
+ test("$SUSPEND prompt template renders mustache variables", () => {
74
+ const graph: Record<string, Record<string, Target>> = {
75
+ reviewer: {
76
+ needs_input: {
77
+ role: "$SUSPEND",
78
+ prompt: "Please clarify: {{{question}}}",
79
+ location: null,
80
+ },
81
+ },
82
+ };
83
+ const result = evaluate(graph, "reviewer", {
84
+ $status: "needs_input",
85
+ question: "Which API endpoint?",
86
+ });
87
+ expect(result).toEqual({
88
+ ok: true,
89
+ value: {
90
+ action: "suspend",
91
+ suspendedRole: "reviewer",
92
+ prompt: "Please clarify: Which API endpoint?",
93
+ },
94
+ });
95
+ });
96
+
97
+ test("missing role in graph → error", () => {
98
+ const result = evaluate(solveIssueGraph, "unknown-role", { $status: "_" });
99
+ expect(result.ok).toBe(false);
100
+ if (!result.ok) {
101
+ expect(result.error.message).toBe('no transitions defined for role "unknown-role"');
102
+ }
103
+ });
104
+
105
+ test("missing status in graph → error", () => {
106
+ const result = evaluate(solveIssueGraph, "reviewer", { $status: "pending" });
107
+ expect(result.ok).toBe(false);
108
+ if (!result.ok) {
109
+ expect(result.error.message).toBe('no transition for role "reviewer" with status "pending"');
110
+ }
111
+ });
112
+
113
+ test("mustache template rendering with simple fields", () => {
114
+ const result = evaluate(solveIssueGraph, "planner", {
115
+ $status: "planned",
116
+ plan: "Add auth middleware",
117
+ });
118
+ expect(result).toEqual({
119
+ ok: true,
120
+ value: {
121
+ role: "developer",
122
+ prompt: "Implement the plan: Add auth middleware",
123
+ location: null,
124
+ },
125
+ });
126
+ });
127
+
128
+ test("mustache does not HTML-escape prompt content", () => {
129
+ const result = evaluate(solveIssueGraph, "reviewer", {
130
+ $status: "rejected",
131
+ comments: 'use <T> & "Result<T, E>" types',
132
+ });
133
+ expect(result).toEqual({
134
+ ok: true,
135
+ value: { role: "developer", prompt: 'Fix: use <T> & "Result<T, E>" types', location: null },
136
+ });
137
+ });
138
+
139
+ test("triple mustache also works for unescaped output", () => {
140
+ const graph: Record<string, Record<string, Target>> = {
141
+ reviewer: {
142
+ rejected: { role: "developer", prompt: "Fix: {{{comments}}}", location: null },
143
+ },
144
+ };
145
+ const result = evaluate(graph, "reviewer", {
146
+ $status: "rejected",
147
+ comments: "<script>alert(1)</script>",
148
+ });
149
+ expect(result).toEqual({
150
+ ok: true,
151
+ value: { role: "developer", prompt: "Fix: <script>alert(1)</script>", location: null },
152
+ });
153
+ });
154
+
155
+ test("missing $status → error (no unit fallback)", () => {
156
+ const result = evaluate(solveIssueGraph, "planner", {
157
+ plan: "Add auth middleware",
158
+ });
159
+ expect(result.ok).toBe(false);
160
+ if (!result.ok) {
161
+ expect(result.error.message).toBe(
162
+ 'agent output for role "planner" is missing required "$status" string',
163
+ );
164
+ }
165
+ });
166
+
167
+ test("mustache template with nested object paths", () => {
168
+ const graph: Record<string, Record<string, Target>> = {
169
+ reviewer: {
170
+ rejected: {
171
+ role: "developer",
172
+ prompt: "Address: {{review.comments}}",
173
+ location: null,
174
+ },
175
+ },
176
+ };
177
+ const result = evaluate(graph, "reviewer", {
178
+ $status: "rejected",
179
+ review: { comments: "refactor the handler" },
180
+ });
181
+ expect(result).toEqual({
182
+ ok: true,
183
+ value: { role: "developer", prompt: "Address: refactor the handler", location: null },
184
+ });
185
+ });
186
+ });
@@ -0,0 +1,7 @@
1
+ const originalExit = process.exit;
2
+
3
+ process.exit = ((code?: number) => {
4
+ throw new Error(`process.exit(${code ?? 1})`);
5
+ }) as typeof process.exit;
6
+
7
+ export { originalExit };