dorfl 0.1.2 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (364) hide show
  1. package/dist/advance-drivers.d.ts +1 -1
  2. package/dist/advance-drivers.d.ts.map +1 -1
  3. package/dist/advance-drivers.js +15 -3
  4. package/dist/advance-drivers.js.map +1 -1
  5. package/dist/advance-lifecycle-template.d.ts.map +1 -1
  6. package/dist/advance-lifecycle-template.js +76 -4
  7. package/dist/advance-lifecycle-template.js.map +1 -1
  8. package/dist/advance-treeless-publish.d.ts +23 -0
  9. package/dist/advance-treeless-publish.d.ts.map +1 -1
  10. package/dist/advance-treeless-publish.js +41 -0
  11. package/dist/advance-treeless-publish.js.map +1 -1
  12. package/dist/advance.d.ts +59 -7
  13. package/dist/advance.d.ts.map +1 -1
  14. package/dist/advance.js +389 -82
  15. package/dist/advance.js.map +1 -1
  16. package/dist/advancing-lock.d.ts +30 -2
  17. package/dist/advancing-lock.d.ts.map +1 -1
  18. package/dist/advancing-lock.js +50 -0
  19. package/dist/advancing-lock.js.map +1 -1
  20. package/dist/agent-launch.d.ts +12 -0
  21. package/dist/agent-launch.d.ts.map +1 -1
  22. package/dist/agent-launch.js +22 -12
  23. package/dist/agent-launch.js.map +1 -1
  24. package/dist/agent-stop.d.ts +40 -2
  25. package/dist/agent-stop.d.ts.map +1 -1
  26. package/dist/agent-stop.js +30 -2
  27. package/dist/agent-stop.js.map +1 -1
  28. package/dist/apply-decide.d.ts +17 -3
  29. package/dist/apply-decide.d.ts.map +1 -1
  30. package/dist/apply-decide.js +36 -7
  31. package/dist/apply-decide.js.map +1 -1
  32. package/dist/apply-merge-action.d.ts +17 -5
  33. package/dist/apply-merge-action.d.ts.map +1 -1
  34. package/dist/apply-merge-action.js +43 -8
  35. package/dist/apply-merge-action.js.map +1 -1
  36. package/dist/apply-persist.d.ts +57 -26
  37. package/dist/apply-persist.d.ts.map +1 -1
  38. package/dist/apply-persist.js +166 -37
  39. package/dist/apply-persist.js.map +1 -1
  40. package/dist/apply-stuck-action.d.ts +151 -0
  41. package/dist/apply-stuck-action.d.ts.map +1 -0
  42. package/dist/apply-stuck-action.js +125 -0
  43. package/dist/apply-stuck-action.js.map +1 -0
  44. package/dist/brand.d.ts +12 -1
  45. package/dist/brand.d.ts.map +1 -1
  46. package/dist/brand.js +2 -1
  47. package/dist/brand.js.map +1 -1
  48. package/dist/cli-spinner.d.ts +1 -1
  49. package/dist/cli-spinner.js +1 -1
  50. package/dist/cli.d.ts +10 -0
  51. package/dist/cli.d.ts.map +1 -1
  52. package/dist/cli.js +320 -63
  53. package/dist/cli.js.map +1 -1
  54. package/dist/close-job-template.js +1 -1
  55. package/dist/complete.d.ts +6 -3
  56. package/dist/complete.d.ts.map +1 -1
  57. package/dist/complete.js +91 -37
  58. package/dist/complete.js.map +1 -1
  59. package/dist/config.d.ts +55 -9
  60. package/dist/config.d.ts.map +1 -1
  61. package/dist/config.js +56 -4
  62. package/dist/config.js.map +1 -1
  63. package/dist/continue-branch.d.ts.map +1 -1
  64. package/dist/continue-branch.js +22 -0
  65. package/dist/continue-branch.js.map +1 -1
  66. package/dist/decision-engine.d.ts +38 -10
  67. package/dist/decision-engine.d.ts.map +1 -1
  68. package/dist/decision-engine.js +10 -6
  69. package/dist/decision-engine.js.map +1 -1
  70. package/dist/do-autopick.d.ts.map +1 -1
  71. package/dist/do-autopick.js +11 -1
  72. package/dist/do-autopick.js.map +1 -1
  73. package/dist/do-config.d.ts +1 -1
  74. package/dist/do.d.ts +49 -56
  75. package/dist/do.d.ts.map +1 -1
  76. package/dist/do.js +419 -30
  77. package/dist/do.js.map +1 -1
  78. package/dist/env-config.d.ts.map +1 -1
  79. package/dist/env-config.js +12 -4
  80. package/dist/env-config.js.map +1 -1
  81. package/dist/failure-cause.d.ts +3 -2
  82. package/dist/failure-cause.d.ts.map +1 -1
  83. package/dist/failure-cause.js +28 -1
  84. package/dist/failure-cause.js.map +1 -1
  85. package/dist/format.d.ts +6 -6
  86. package/dist/format.d.ts.map +1 -1
  87. package/dist/format.js +10 -27
  88. package/dist/format.js.map +1 -1
  89. package/dist/frontmatter.d.ts +16 -4
  90. package/dist/frontmatter.d.ts.map +1 -1
  91. package/dist/frontmatter.js +27 -1
  92. package/dist/frontmatter.js.map +1 -1
  93. package/dist/gc.d.ts +65 -6
  94. package/dist/gc.d.ts.map +1 -1
  95. package/dist/gc.js +126 -9
  96. package/dist/gc.js.map +1 -1
  97. package/dist/github.d.ts +14 -0
  98. package/dist/github.d.ts.map +1 -1
  99. package/dist/github.js +73 -0
  100. package/dist/github.js.map +1 -1
  101. package/dist/harness.d.ts +25 -0
  102. package/dist/harness.d.ts.map +1 -1
  103. package/dist/harness.js.map +1 -1
  104. package/dist/index.d.ts +4 -1
  105. package/dist/index.d.ts.map +1 -1
  106. package/dist/index.js +2 -1
  107. package/dist/index.js.map +1 -1
  108. package/dist/install-ci-branch-protection.d.ts +132 -38
  109. package/dist/install-ci-branch-protection.d.ts.map +1 -1
  110. package/dist/install-ci-branch-protection.js +190 -48
  111. package/dist/install-ci-branch-protection.js.map +1 -1
  112. package/dist/install-ci-core.d.ts +21 -1
  113. package/dist/install-ci-core.d.ts.map +1 -1
  114. package/dist/install-ci-core.js +10 -3
  115. package/dist/install-ci-core.js.map +1 -1
  116. package/dist/install-ci-github.d.ts +32 -0
  117. package/dist/install-ci-github.d.ts.map +1 -1
  118. package/dist/install-ci-github.js +73 -0
  119. package/dist/install-ci-github.js.map +1 -1
  120. package/dist/install-skills.d.ts +123 -0
  121. package/dist/install-skills.d.ts.map +1 -0
  122. package/dist/install-skills.js +100 -0
  123. package/dist/install-skills.js.map +1 -0
  124. package/dist/intake-trigger-template.js +1 -1
  125. package/dist/intake.d.ts +17 -4
  126. package/dist/intake.d.ts.map +1 -1
  127. package/dist/intake.js +23 -7
  128. package/dist/intake.js.map +1 -1
  129. package/dist/integration-core.d.ts +10 -7
  130. package/dist/integration-core.d.ts.map +1 -1
  131. package/dist/integration-core.js +174 -60
  132. package/dist/integration-core.js.map +1 -1
  133. package/dist/integrator.d.ts +1 -1
  134. package/dist/integrator.d.ts.map +1 -1
  135. package/dist/integrator.js +19 -3
  136. package/dist/integrator.js.map +1 -1
  137. package/dist/isolation.d.ts +2 -2
  138. package/dist/isolation.d.ts.map +1 -1
  139. package/dist/isolation.js +9 -0
  140. package/dist/isolation.js.map +1 -1
  141. package/dist/item-lock.d.ts +196 -129
  142. package/dist/item-lock.d.ts.map +1 -1
  143. package/dist/item-lock.js +329 -254
  144. package/dist/item-lock.js.map +1 -1
  145. package/dist/ledger-write.d.ts +31 -26
  146. package/dist/ledger-write.d.ts.map +1 -1
  147. package/dist/ledger-write.js +100 -122
  148. package/dist/ledger-write.js.map +1 -1
  149. package/dist/lifecycle-gather.d.ts +17 -0
  150. package/dist/lifecycle-gather.d.ts.map +1 -1
  151. package/dist/lifecycle-gather.js +7 -1
  152. package/dist/lifecycle-gather.js.map +1 -1
  153. package/dist/lifecycle-pools.d.ts +40 -3
  154. package/dist/lifecycle-pools.d.ts.map +1 -1
  155. package/dist/lifecycle-pools.js +26 -6
  156. package/dist/lifecycle-pools.js.map +1 -1
  157. package/dist/merge-question-surfacer.d.ts +15 -1
  158. package/dist/merge-question-surfacer.d.ts.map +1 -1
  159. package/dist/merge-question-surfacer.js +18 -3
  160. package/dist/merge-question-surfacer.js.map +1 -1
  161. package/dist/migrate-stuck-locks.d.ts +129 -0
  162. package/dist/migrate-stuck-locks.d.ts.map +1 -0
  163. package/dist/migrate-stuck-locks.js +355 -0
  164. package/dist/migrate-stuck-locks.js.map +1 -0
  165. package/dist/needs-attention.d.ts +293 -16
  166. package/dist/needs-attention.d.ts.map +1 -1
  167. package/dist/needs-attention.js +532 -57
  168. package/dist/needs-attention.js.map +1 -1
  169. package/dist/orphan-sidecar.d.ts +8 -4
  170. package/dist/orphan-sidecar.d.ts.map +1 -1
  171. package/dist/orphan-sidecar.js +35 -2
  172. package/dist/orphan-sidecar.js.map +1 -1
  173. package/dist/pi-harness.d.ts +16 -0
  174. package/dist/pi-harness.d.ts.map +1 -1
  175. package/dist/pi-harness.js +82 -2
  176. package/dist/pi-harness.js.map +1 -1
  177. package/dist/placement.d.ts +3 -3
  178. package/dist/placement.js +1 -1
  179. package/dist/prd-to-spec.d.ts.map +1 -1
  180. package/dist/prd-to-spec.js +9 -5
  181. package/dist/prd-to-spec.js.map +1 -1
  182. package/dist/protocol/CLAIM-PROTOCOL.md +17 -10
  183. package/dist/protocol/REVIEW-PROTOCOL.md +4 -1
  184. package/dist/protocol/SURFACE-PROTOCOL.md +16 -2
  185. package/dist/protocol/TASKING-PROTOCOL.md +3 -1
  186. package/dist/protocol/WORK-CONTRACT.md +22 -18
  187. package/dist/protocol/task-template.md +1 -1
  188. package/dist/reap-branches.d.ts +12 -9
  189. package/dist/reap-branches.d.ts.map +1 -1
  190. package/dist/reap-branches.js +25 -7
  191. package/dist/reap-branches.js.map +1 -1
  192. package/dist/recover-isolated.d.ts +12 -0
  193. package/dist/recover-isolated.d.ts.map +1 -1
  194. package/dist/recover-isolated.js +5 -0
  195. package/dist/recover-isolated.js.map +1 -1
  196. package/dist/repo-config.d.ts +23 -2
  197. package/dist/repo-config.d.ts.map +1 -1
  198. package/dist/repo-config.js +53 -7
  199. package/dist/repo-config.js.map +1 -1
  200. package/dist/repo-mirror.d.ts.map +1 -1
  201. package/dist/repo-mirror.js +18 -2
  202. package/dist/repo-mirror.js.map +1 -1
  203. package/dist/review-verdict.js +1 -1
  204. package/dist/review-verdict.js.map +1 -1
  205. package/dist/run.d.ts +1 -1
  206. package/dist/run.d.ts.map +1 -1
  207. package/dist/run.js +54 -19
  208. package/dist/run.js.map +1 -1
  209. package/dist/scan.d.ts +17 -8
  210. package/dist/scan.d.ts.map +1 -1
  211. package/dist/scan.js +22 -8
  212. package/dist/scan.js.map +1 -1
  213. package/dist/select-priority.d.ts +2 -2
  214. package/dist/select-priority.js +1 -1
  215. package/dist/select-priority.js.map +1 -1
  216. package/dist/sidecar-apply.js +1 -1
  217. package/dist/sidecar-apply.js.map +1 -1
  218. package/dist/sidecar.d.ts +45 -10
  219. package/dist/sidecar.d.ts.map +1 -1
  220. package/dist/sidecar.js +82 -2
  221. package/dist/sidecar.js.map +1 -1
  222. package/dist/skills/answer-questions/SKILL.md +89 -0
  223. package/dist/skills/capture-signal/SKILL.md +52 -0
  224. package/dist/skills/convert-from-prd-to-spec/SKILL.md +90 -0
  225. package/dist/skills/drive-tasks/SKILL.md +218 -0
  226. package/dist/skills/from-idea/SKILL.md +83 -0
  227. package/dist/skills/merge-prs/SKILL.md +70 -0
  228. package/dist/skills/orchestrate/SKILL.md +101 -0
  229. package/dist/skills/promote/SKILL.md +35 -0
  230. package/dist/skills/review/SKILL.md +16 -0
  231. package/dist/skills/setup/SKILL.md +258 -0
  232. package/dist/skills/setup/protocol/ADR-FORMAT.md +47 -0
  233. package/dist/skills/setup/protocol/CLAIM-PROTOCOL.md +224 -0
  234. package/dist/skills/setup/protocol/REVIEW-PROTOCOL.md +122 -0
  235. package/dist/skills/setup/protocol/SURFACE-PROTOCOL.md +135 -0
  236. package/dist/skills/setup/protocol/TASKING-PROTOCOL.md +124 -0
  237. package/dist/skills/setup/protocol/WORK-CONTRACT.md +280 -0
  238. package/dist/skills/setup/protocol/spec-template.md +71 -0
  239. package/dist/skills/setup/protocol/task-template.md +65 -0
  240. package/dist/skills/surface-questions/SKILL.md +16 -0
  241. package/dist/skills/to-spec/SKILL.md +34 -0
  242. package/dist/skills/to-task/SKILL.md +19 -0
  243. package/dist/skills/triage-observations/SKILL.md +78 -0
  244. package/dist/skills/work/SKILL.md +51 -0
  245. package/dist/slug-namespace.d.ts +5 -5
  246. package/dist/slug-namespace.js +5 -5
  247. package/dist/slug-namespace.js.map +1 -1
  248. package/dist/spec-complete.d.ts.map +1 -1
  249. package/dist/spec-complete.js +2 -3
  250. package/dist/spec-complete.js.map +1 -1
  251. package/dist/start.d.ts.map +1 -1
  252. package/dist/start.js +51 -57
  253. package/dist/start.js.map +1 -1
  254. package/dist/surface-gate.d.ts.map +1 -1
  255. package/dist/surface-gate.js +10 -3
  256. package/dist/surface-gate.js.map +1 -1
  257. package/dist/surface-persist.js +1 -1
  258. package/dist/surface-persist.js.map +1 -1
  259. package/dist/tasker-review-loop.d.ts +4 -3
  260. package/dist/tasker-review-loop.d.ts.map +1 -1
  261. package/dist/tasker-review-loop.js.map +1 -1
  262. package/dist/tasking-lock.d.ts +2 -2
  263. package/dist/tasking-lock.d.ts.map +1 -1
  264. package/dist/tasking-lock.js +53 -32
  265. package/dist/tasking-lock.js.map +1 -1
  266. package/dist/tasking.d.ts +40 -6
  267. package/dist/tasking.d.ts.map +1 -1
  268. package/dist/tasking.js +224 -12
  269. package/dist/tasking.js.map +1 -1
  270. package/dist/triage-persist.d.ts +10 -6
  271. package/dist/triage-persist.d.ts.map +1 -1
  272. package/dist/triage-persist.js +34 -4
  273. package/dist/triage-persist.js.map +1 -1
  274. package/dist/vendor/incur/agents.d.ts +58 -0
  275. package/dist/vendor/incur/agents.d.ts.map +1 -0
  276. package/dist/vendor/incur/agents.js +343 -0
  277. package/dist/vendor/incur/agents.js.map +1 -0
  278. package/dist/verify-workflow-template.js +1 -1
  279. package/dist/watch-session.d.ts +11 -3
  280. package/dist/watch-session.d.ts.map +1 -1
  281. package/dist/watch-session.js +94 -7
  282. package/dist/watch-session.js.map +1 -1
  283. package/dist/work-layout.d.ts +7 -3
  284. package/dist/work-layout.d.ts.map +1 -1
  285. package/dist/work-layout.js +6 -3
  286. package/dist/work-layout.js.map +1 -1
  287. package/dist/workspace.d.ts +1 -1
  288. package/package.json +2 -2
  289. package/src/advance-drivers.ts +16 -3
  290. package/src/advance-lifecycle-template.ts +89 -4
  291. package/src/advance-treeless-publish.ts +45 -0
  292. package/src/advance.ts +467 -87
  293. package/src/advancing-lock.ts +96 -1
  294. package/src/agent-launch.ts +37 -12
  295. package/src/agent-stop.ts +60 -2
  296. package/src/apply-decide.ts +36 -7
  297. package/src/apply-merge-action.ts +45 -7
  298. package/src/apply-persist.ts +228 -54
  299. package/src/apply-stuck-action.ts +260 -0
  300. package/src/brand.ts +14 -2
  301. package/src/cli-spinner.ts +1 -1
  302. package/src/cli.ts +401 -70
  303. package/src/close-job-template.ts +1 -1
  304. package/src/complete.ts +110 -52
  305. package/src/config.ts +107 -13
  306. package/src/continue-branch.ts +22 -0
  307. package/src/decision-engine.ts +55 -17
  308. package/src/do-autopick.ts +12 -1
  309. package/src/do-config.ts +1 -1
  310. package/src/do.ts +511 -38
  311. package/src/env-config.ts +12 -4
  312. package/src/failure-cause.ts +30 -1
  313. package/src/format.ts +10 -30
  314. package/src/frontmatter.ts +35 -5
  315. package/src/gc.ts +172 -9
  316. package/src/github.ts +78 -0
  317. package/src/harness.ts +25 -0
  318. package/src/index.ts +10 -0
  319. package/src/install-ci-branch-protection.ts +282 -57
  320. package/src/install-ci-core.ts +30 -3
  321. package/src/install-ci-github.ts +86 -0
  322. package/src/install-skills.ts +166 -0
  323. package/src/intake-trigger-template.ts +1 -1
  324. package/src/intake.ts +46 -11
  325. package/src/integration-core.ts +199 -65
  326. package/src/integrator.ts +22 -9
  327. package/src/isolation.ts +11 -2
  328. package/src/item-lock.ts +485 -341
  329. package/src/ledger-write.ts +135 -150
  330. package/src/lifecycle-gather.ts +29 -6
  331. package/src/lifecycle-pools.ts +67 -9
  332. package/src/merge-question-surfacer.ts +28 -4
  333. package/src/migrate-stuck-locks.ts +451 -0
  334. package/src/needs-attention.ts +857 -84
  335. package/src/orphan-sidecar.ts +47 -6
  336. package/src/pi-harness.ts +82 -2
  337. package/src/placement.ts +3 -3
  338. package/src/prd-to-spec.ts +13 -5
  339. package/src/reap-branches.ts +38 -14
  340. package/src/recover-isolated.ts +17 -0
  341. package/src/repo-config.ts +55 -6
  342. package/src/repo-mirror.ts +23 -1
  343. package/src/review-verdict.ts +1 -1
  344. package/src/run.ts +64 -18
  345. package/src/scan.ts +22 -8
  346. package/src/select-priority.ts +3 -3
  347. package/src/sidecar-apply.ts +1 -1
  348. package/src/sidecar.ts +138 -11
  349. package/src/slug-namespace.ts +7 -7
  350. package/src/spec-complete.ts +2 -3
  351. package/src/start.ts +53 -59
  352. package/src/surface-gate.ts +10 -3
  353. package/src/surface-persist.ts +1 -1
  354. package/src/tasker-review-loop.ts +4 -3
  355. package/src/tasking-lock.ts +52 -36
  356. package/src/tasking.ts +261 -17
  357. package/src/triage-persist.ts +56 -12
  358. package/src/vendor/incur/LICENSE +21 -0
  359. package/src/vendor/incur/README.md +19 -0
  360. package/src/vendor/incur/agents.ts +392 -0
  361. package/src/verify-workflow-template.ts +1 -1
  362. package/src/watch-session.ts +102 -7
  363. package/src/work-layout.ts +6 -3
  364. package/src/workspace.ts +1 -1
@@ -0,0 +1,392 @@
1
+ // ---------------------------------------------------------------------------
2
+ // VENDORED FROM: https://github.com/wevm/incur
3
+ // Source: src/internal/agents.ts
4
+ // License: MIT (see ./LICENSE beside this file)
5
+ // Copyright: (c) 2025-present weth, LLC
6
+ //
7
+ // This file is a VERBATIM copy of the upstream `src/internal/agents.ts` from
8
+ // wevm/incur, preserved byte-close so future upstream updates are a mechanical
9
+ // re-copy. Do NOT edit it in place — put all dorfl wrapper code OUTSIDE this
10
+ // directory (see `../../install-skills.ts`). Only the block ABOVE this line is
11
+ // dorfl-authored (attribution header). See `./README.md` for provenance and
12
+ // the `docs/adr/skill-install-vendors-incur-agents-map.md` ADR for why we
13
+ // vendor rather than depend.
14
+ //
15
+ // MIT permission notice (required by upstream license, kept in-file so the
16
+ // attribution survives isolation from the sibling LICENSE):
17
+ //
18
+ // Permission is hereby granted, free of charge, to any person obtaining a
19
+ // copy of this software and associated documentation files (the "Software"),
20
+ // to deal in the Software without restriction, including without limitation
21
+ // the rights to use, copy, modify, merge, publish, distribute, sublicense,
22
+ // and/or sell copies of the Software, and to permit persons to whom the
23
+ // Software is furnished to do so, subject to the following conditions:
24
+ //
25
+ // The above copyright notice and this permission notice shall be included
26
+ // in all copies or substantial portions of the Software.
27
+ //
28
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
29
+ // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
30
+ // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
31
+ // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
32
+ // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
33
+ // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
34
+ // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35
+ // ---------------------------------------------------------------------------
36
+
37
+ import * as fs from 'node:fs'
38
+ import * as os from 'node:os'
39
+ import * as path from 'node:path'
40
+
41
+ /** Agent configuration for skill installation. */
42
+ export type Agent = {
43
+ /** Display name. */
44
+ name: string
45
+ /** Absolute path to the global skills directory. */
46
+ globalSkillsDir: string
47
+ /** Project-relative skills directory path. */
48
+ projectSkillsDir: string
49
+ /** Whether this agent uses the canonical `.agents/skills` path. */
50
+ universal: boolean
51
+ /** Checks if the agent is installed on the system. */
52
+ detect(): boolean
53
+ }
54
+
55
+ const home = os.homedir()
56
+ const configHome = process.env.XDG_CONFIG_HOME || path.join(home, '.config')
57
+ const claudeHome = process.env.CLAUDE_CONFIG_DIR?.trim() || path.join(home, '.claude')
58
+ const codexHome = process.env.CODEX_HOME?.trim() || path.join(home, '.codex')
59
+
60
+ /** All known agent definitions. */
61
+ export const all: Agent[] = [
62
+ // Universal agents (project skillsDir = .agents/skills)
63
+ {
64
+ name: 'Amp',
65
+ globalSkillsDir: path.join(configHome, 'agents', 'skills'),
66
+ projectSkillsDir: '.agents/skills',
67
+ universal: true,
68
+ detect: () => fs.existsSync(path.join(configHome, 'amp')),
69
+ },
70
+ {
71
+ name: 'Cline',
72
+ globalSkillsDir: path.join(home, '.agents', 'skills'),
73
+ projectSkillsDir: '.agents/skills',
74
+ universal: true,
75
+ detect: () => fs.existsSync(path.join(home, '.cline')),
76
+ },
77
+ {
78
+ name: 'Codex',
79
+ globalSkillsDir: path.join(codexHome, 'skills'),
80
+ projectSkillsDir: '.agents/skills',
81
+ universal: true,
82
+ detect: () => fs.existsSync(codexHome),
83
+ },
84
+ {
85
+ name: 'Cursor',
86
+ globalSkillsDir: path.join(home, '.cursor', 'skills'),
87
+ projectSkillsDir: '.agents/skills',
88
+ universal: true,
89
+ detect: () => fs.existsSync(path.join(home, '.cursor')),
90
+ },
91
+ {
92
+ name: 'Gemini CLI',
93
+ globalSkillsDir: path.join(home, '.gemini', 'skills'),
94
+ projectSkillsDir: '.agents/skills',
95
+ universal: true,
96
+ detect: () => fs.existsSync(path.join(home, '.gemini')),
97
+ },
98
+ {
99
+ name: 'GitHub Copilot',
100
+ globalSkillsDir: path.join(home, '.copilot', 'skills'),
101
+ projectSkillsDir: '.agents/skills',
102
+ universal: true,
103
+ detect: () => fs.existsSync(path.join(home, '.copilot')),
104
+ },
105
+ {
106
+ name: 'Kimi CLI',
107
+ globalSkillsDir: path.join(configHome, 'agents', 'skills'),
108
+ projectSkillsDir: '.agents/skills',
109
+ universal: true,
110
+ detect: () => fs.existsSync(path.join(home, '.kimi')),
111
+ },
112
+ {
113
+ name: 'OpenCode',
114
+ globalSkillsDir: path.join(configHome, 'opencode', 'skills'),
115
+ projectSkillsDir: '.agents/skills',
116
+ universal: true,
117
+ detect: () => fs.existsSync(path.join(configHome, 'opencode')),
118
+ },
119
+
120
+ // Non-universal agents (need symlink from their skills dir to canonical)
121
+ {
122
+ name: 'Claude Code',
123
+ globalSkillsDir: path.join(claudeHome, 'skills'),
124
+ projectSkillsDir: '.claude/skills',
125
+ universal: false,
126
+ detect: () => fs.existsSync(claudeHome),
127
+ },
128
+ {
129
+ name: 'Windsurf',
130
+ globalSkillsDir: path.join(home, '.codeium', 'windsurf', 'skills'),
131
+ projectSkillsDir: '.windsurf/skills',
132
+ universal: false,
133
+ detect: () => fs.existsSync(path.join(home, '.codeium', 'windsurf')),
134
+ },
135
+ {
136
+ name: 'Continue',
137
+ globalSkillsDir: path.join(home, '.continue', 'skills'),
138
+ projectSkillsDir: '.continue/skills',
139
+ universal: false,
140
+ detect: () => fs.existsSync(path.join(home, '.continue')),
141
+ },
142
+ {
143
+ name: 'Roo',
144
+ globalSkillsDir: path.join(home, '.roo', 'skills'),
145
+ projectSkillsDir: '.roo/skills',
146
+ universal: false,
147
+ detect: () => fs.existsSync(path.join(home, '.roo')),
148
+ },
149
+ {
150
+ name: 'Kilo',
151
+ globalSkillsDir: path.join(home, '.kilocode', 'skills'),
152
+ projectSkillsDir: '.kilocode/skills',
153
+ universal: false,
154
+ detect: () => fs.existsSync(path.join(home, '.kilocode')),
155
+ },
156
+ {
157
+ name: 'Goose',
158
+ globalSkillsDir: path.join(configHome, 'goose', 'skills'),
159
+ projectSkillsDir: '.goose/skills',
160
+ universal: false,
161
+ detect: () => fs.existsSync(path.join(configHome, 'goose')),
162
+ },
163
+ {
164
+ name: 'Augment',
165
+ globalSkillsDir: path.join(home, '.augment', 'skills'),
166
+ projectSkillsDir: '.augment/skills',
167
+ universal: false,
168
+ detect: () => fs.existsSync(path.join(home, '.augment')),
169
+ },
170
+ {
171
+ name: 'Trae',
172
+ globalSkillsDir: path.join(home, '.trae', 'skills'),
173
+ projectSkillsDir: '.trae/skills',
174
+ universal: false,
175
+ detect: () => fs.existsSync(path.join(home, '.trae')),
176
+ },
177
+ {
178
+ name: 'Junie',
179
+ globalSkillsDir: path.join(home, '.junie', 'skills'),
180
+ projectSkillsDir: '.junie/skills',
181
+ universal: false,
182
+ detect: () => fs.existsSync(path.join(home, '.junie')),
183
+ },
184
+ {
185
+ name: 'Crush',
186
+ globalSkillsDir: path.join(configHome, 'crush', 'skills'),
187
+ projectSkillsDir: '.crush/skills',
188
+ universal: false,
189
+ detect: () => fs.existsSync(path.join(configHome, 'crush')),
190
+ },
191
+ {
192
+ name: 'Kiro CLI',
193
+ globalSkillsDir: path.join(home, '.kiro', 'skills'),
194
+ projectSkillsDir: '.kiro/skills',
195
+ universal: false,
196
+ detect: () => fs.existsSync(path.join(home, '.kiro')),
197
+ },
198
+ {
199
+ name: 'Qwen Code',
200
+ globalSkillsDir: path.join(home, '.qwen', 'skills'),
201
+ projectSkillsDir: '.qwen/skills',
202
+ universal: false,
203
+ detect: () => fs.existsSync(path.join(home, '.qwen')),
204
+ },
205
+ {
206
+ name: 'OpenHands',
207
+ globalSkillsDir: path.join(home, '.openhands', 'skills'),
208
+ projectSkillsDir: '.openhands/skills',
209
+ universal: false,
210
+ detect: () => fs.existsSync(path.join(home, '.openhands')),
211
+ },
212
+ ]
213
+
214
+ /** Detects which agents are installed on the system. */
215
+ export function detect(): Agent[] {
216
+ return all.filter((a) => a.detect())
217
+ }
218
+
219
+ /**
220
+ * Installs skill directories to the canonical location and creates symlinks for
221
+ * detected non-universal agents.
222
+ *
223
+ * @param sourceDir - Directory containing skill subdirectories (each with a `SKILL.md`).
224
+ * @param options - Installation options.
225
+ * @returns Installed canonical paths.
226
+ */
227
+ export function install(sourceDir: string, options: install.Options = {}): install.Result {
228
+ const isGlobal = options.global !== false
229
+ const cwd = options.cwd || process.cwd()
230
+ const base = isGlobal ? home : cwd
231
+ const canonicalBase = path.join(base, '.agents', 'skills')
232
+ const detected = options.agents ?? detect()
233
+
234
+ const paths: string[] = []
235
+ const agents: install.AgentInstall[] = []
236
+
237
+ for (const skill of discoverSkills(sourceDir)) {
238
+ const canonicalDir = path.join(canonicalBase, skill.name)
239
+
240
+ // Copy to canonical location
241
+ rmForce(canonicalDir)
242
+ fs.mkdirSync(canonicalDir, { recursive: true })
243
+ if (skill.root)
244
+ fs.copyFileSync(path.join(skill.dir, 'SKILL.md'), path.join(canonicalDir, 'SKILL.md'))
245
+ else fs.cpSync(skill.dir, canonicalDir, { recursive: true })
246
+ paths.push(canonicalDir)
247
+
248
+ // Create symlinks for non-universal agents
249
+ for (const agent of detected) {
250
+ if (agent.universal) continue
251
+ const agentSkillsDir = isGlobal
252
+ ? agent.globalSkillsDir
253
+ : path.join(cwd, agent.projectSkillsDir)
254
+ const agentDir = path.join(agentSkillsDir, skill.name)
255
+
256
+ // Skip if agent dir resolves to canonical (no symlink needed)
257
+ if (agentDir === canonicalDir) continue
258
+
259
+ try {
260
+ rmForce(agentDir)
261
+ fs.mkdirSync(path.dirname(agentDir), { recursive: true })
262
+ // Resolve through any existing symlinks in parent directories
263
+ const realLinkDir = resolveParent(path.dirname(agentDir))
264
+ const realTarget = resolveParent(canonicalDir)
265
+ const rel = path.relative(realLinkDir, realTarget)
266
+ fs.symlinkSync(rel, agentDir)
267
+ agents.push({ agent: agent.name, path: agentDir, mode: 'symlink' })
268
+ } catch {
269
+ // Fallback to copy if symlink fails (e.g. Windows without permissions)
270
+ try {
271
+ fs.cpSync(canonicalDir, agentDir, { recursive: true })
272
+ agents.push({ agent: agent.name, path: agentDir, mode: 'copy' })
273
+ } catch {}
274
+ }
275
+ }
276
+ }
277
+
278
+ return { paths, agents }
279
+ }
280
+
281
+ export declare namespace install {
282
+ type Options = {
283
+ /** Override detected agents. */
284
+ agents?: Agent[] | undefined
285
+ /** Working directory for project-local installs. */
286
+ cwd?: string | undefined
287
+ /** Install globally. Defaults to `true`. */
288
+ global?: boolean | undefined
289
+ }
290
+ type Result = {
291
+ /** Canonical install paths. */
292
+ paths: string[]
293
+ /** Per-agent install details (non-universal agents only). */
294
+ agents: AgentInstall[]
295
+ }
296
+ type AgentInstall = {
297
+ /** Agent display name. */
298
+ agent: string
299
+ /** Installed path. */
300
+ path: string
301
+ /** Whether it was symlinked or copied. */
302
+ mode: 'symlink' | 'copy'
303
+ }
304
+ }
305
+
306
+ /**
307
+ * Removes a skill by name from the canonical location and all detected agent directories.
308
+ */
309
+ export function remove(
310
+ skillName: string,
311
+ options: { global?: boolean | undefined; cwd?: string | undefined } = {},
312
+ ) {
313
+ const isGlobal = options.global !== false
314
+ const cwd = options.cwd || process.cwd()
315
+ const base = isGlobal ? home : cwd
316
+ const canonicalDir = path.join(base, '.agents', 'skills', skillName)
317
+ rmForce(canonicalDir)
318
+
319
+ for (const agent of detect()) {
320
+ if (agent.universal) continue
321
+ const agentSkillsDir = isGlobal ? agent.globalSkillsDir : path.join(cwd, agent.projectSkillsDir)
322
+ const agentDir = path.join(agentSkillsDir, skillName)
323
+ rmForce(agentDir)
324
+ }
325
+ }
326
+
327
+ /** Recursively discovers skill directories (those containing a `SKILL.md`). */
328
+ function discoverSkills(rootDir: string): { name: string; dir: string; root?: boolean }[] {
329
+ const results: { name: string; dir: string; root?: boolean }[] = []
330
+
331
+ function visit(dir: string) {
332
+ let entries: fs.Dirent[]
333
+ try {
334
+ entries = fs.readdirSync(dir, { withFileTypes: true })
335
+ } catch {
336
+ return
337
+ }
338
+ for (const entry of entries) {
339
+ if (!entry.isDirectory()) continue
340
+ const subDir = path.join(dir, entry.name)
341
+ const skillPath = path.join(subDir, 'SKILL.md')
342
+ if (fs.existsSync(skillPath)) {
343
+ const content = fs.readFileSync(skillPath, 'utf8')
344
+ const nameMatch = content.match(/^name:\s*(.+)$/m)
345
+ results.push({ name: sanitizeName(nameMatch?.[1] ?? entry.name), dir: subDir })
346
+ }
347
+ visit(subDir)
348
+ }
349
+ }
350
+
351
+ visit(rootDir)
352
+
353
+ // Root-level SKILL.md (e.g. depth=0)
354
+ const rootSkill = path.join(rootDir, 'SKILL.md')
355
+ if (fs.existsSync(rootSkill)) {
356
+ const content = fs.readFileSync(rootSkill, 'utf8')
357
+ const nameMatch = content.match(/^name:\s*(.+)$/m)
358
+ const name = sanitizeName(nameMatch?.[1] ?? 'skill')
359
+ if (!results.some((r) => r.name === name)) results.push({ name, dir: rootDir, root: true })
360
+ }
361
+
362
+ return results
363
+ }
364
+
365
+ /** Sanitizes a skill name for use as a directory name. */
366
+ function sanitizeName(name: string): string {
367
+ return name.trim().replace(/[/\\]/g, '-').replace(/\.\./g, '').slice(0, 255)
368
+ }
369
+
370
+ /** Removes a file, directory, or symlink (including broken symlinks). */
371
+ function rmForce(target: string) {
372
+ try {
373
+ const stat = fs.lstatSync(target)
374
+ if (stat.isSymbolicLink()) fs.unlinkSync(target)
375
+ else fs.rmSync(target, { recursive: true, force: true })
376
+ } catch {}
377
+ }
378
+
379
+ /** Resolves parent directories through symlinks. */
380
+ function resolveParent(dir: string): string {
381
+ try {
382
+ return fs.realpathSync(dir)
383
+ } catch {
384
+ const parent = path.dirname(dir)
385
+ if (parent === dir) return dir
386
+ try {
387
+ return path.join(fs.realpathSync(parent), path.relative(parent, dir))
388
+ } catch {
389
+ return dir
390
+ }
391
+ }
392
+ }
@@ -100,7 +100,7 @@ jobs:
100
100
  ${VERIFY_CHECK_CONTEXT}:
101
101
  runs-on: ubuntu-latest
102
102
  steps:
103
- - uses: actions/checkout@v4
103
+ - uses: actions/checkout@v5
104
104
  with:
105
105
  fetch-depth: 0
106
106
  - uses: ./.github/actions/dorfl-setup
@@ -13,6 +13,16 @@ import {open, type FileHandle} from 'node:fs/promises';
13
13
  * `--mode json` STREAM `ar-run.sh` piped. The tailer is pointed at that EXACT
14
14
  * path (no newest-by-mtime dir guessing), so the stale-sibling race is gone.
15
15
  *
16
+ * Studied (task `pi-harness-polish`, finding
17
+ * `work/notes/findings/pi-harness-channels.md`, pinned against pi 0.73.1 +
18
+ * session format v3): tailing the session file is deliberately the CHOSEN
19
+ * channel for `--watch`, not just what was easiest to reuse. `--mode json`'s
20
+ * STDOUT event vocabulary would be lower-latency but it is unversioned, would
21
+ * fork a second parser away from pi-remote's `session-pool.ts` reference walk
22
+ * this classifier mirrors, and the vocabulary-drift risk that already bit
23
+ * `do --watch` once applies MORE to it than to the versioned+migrated session
24
+ * file. Revisit only if pi's session format changes.
25
+ *
16
26
  * A session-log record is `{"type":"message", "message":{role, content[]}}` (or a
17
27
  * `session`/`model_change`/`thinking_level_change` preamble record). The earlier
18
28
  * classifier matched the STREAM vocabulary (`tool_start`/`message_end`/`agent_end`),
@@ -21,8 +31,10 @@ import {open, type FileHandle} from 'node:fs/promises';
21
31
  * surfaces the same high-signal level `ar-run.sh --watch` gave, read off the
22
32
  * SESSION-LOG shape:
23
33
  *
24
- * - an `assistant` `message` → its `content[]` `text` parts, then `▶ <name>`
25
- * (cyan) per `content[]` `toolCall` part;
34
+ * - an `assistant` `message` → its `content[]` `text` parts, then
35
+ * `▶ <name> <detail>` (cyan) per `content[]` `toolCall` part, where
36
+ * `<detail>` is the high-signal argument for that tool (a `read`'s path, a
37
+ * `bash`'s command, …), truncated to 64 chars with a trailing `…`;
26
38
  * - `✓ agent finished` (green) on PROCESS EXIT — the session log has no
27
39
  * `agent_end` event, so the tailer emits it once on `stop()`;
28
40
  * - everything else (`session`/`model_change`/user messages/tool results/…) →
@@ -50,6 +62,77 @@ function paint(text: string, color: boolean, code: string): string {
50
62
  return color ? `${code}${text}${RESET}` : text;
51
63
  }
52
64
 
65
+ /**
66
+ * The max width of a tool-call DETAIL string (the `read <path>` / `bash <cmd>`
67
+ * argument summary appended after the tool name). A longer detail is truncated
68
+ * to this many characters with a trailing `…`, so a giant `bash` heredoc or a
69
+ * long path never floods the live view with one enormous line.
70
+ */
71
+ const MAX_DETAIL_LENGTH = 64;
72
+
73
+ /** Truncate `text` to {@link MAX_DETAIL_LENGTH} chars, appending `…` when cut. */
74
+ function truncateDetail(text: string): string {
75
+ // Collapse any embedded newlines/tabs to single spaces first: a multi-line
76
+ // `bash` command must stay ONE line in the tail view.
77
+ const oneLine = text.replace(/\s+/g, ' ').trim();
78
+ if (oneLine.length <= MAX_DETAIL_LENGTH) {
79
+ return oneLine;
80
+ }
81
+ return `${oneLine.slice(0, MAX_DETAIL_LENGTH)}…`;
82
+ }
83
+
84
+ /**
85
+ * The FIRST non-empty string among `keys` in a tool-call's arguments object, or
86
+ * `undefined` when the args are absent / none of the keys carry a string. The
87
+ * args live under `arguments` (pi's canonical key) OR `args` (the variant seen
88
+ * in some session records), mirroring the `name || toolName` defensiveness used
89
+ * for the tool name itself.
90
+ */
91
+ function firstArgString(
92
+ part: Record<string, unknown>,
93
+ keys: readonly string[],
94
+ ): string | undefined {
95
+ const rawArgs =
96
+ (typeof part.arguments === 'object' && part.arguments) ||
97
+ (typeof part.args === 'object' && part.args) ||
98
+ undefined;
99
+ if (!rawArgs || rawArgs === null) {
100
+ return undefined;
101
+ }
102
+ const args = rawArgs as Record<string, unknown>;
103
+ for (const key of keys) {
104
+ const value = args[key];
105
+ if (typeof value === 'string' && value !== '') {
106
+ return value;
107
+ }
108
+ }
109
+ return undefined;
110
+ }
111
+
112
+ /**
113
+ * The high-signal ARGUMENT to show after a tool name, chosen per tool so the
114
+ * `▶ <name>` marker reads like the agent's actual action (`▶ read work/x.md`,
115
+ * `▶ bash pnpm -r build`). The mapping mirrors this harness's own tool set
116
+ * (read/edit/write/grep/find/ls take a `path`; bash a `command`; grep a
117
+ * `pattern`; find/ls a `pattern`/`path`); any unmapped tool falls back to a
118
+ * generic `path`/`command`/`pattern`/`query` probe so a new tool still shows
119
+ * *something* useful. Returns `''` when no informative arg is present.
120
+ */
121
+ function toolCallDetail(name: string, part: Record<string, unknown>): string {
122
+ const byTool: Record<string, readonly string[]> = {
123
+ read: ['path'],
124
+ edit: ['path'],
125
+ write: ['path'],
126
+ ls: ['path'],
127
+ find: ['pattern', 'path'],
128
+ grep: ['pattern', 'path'],
129
+ bash: ['command'],
130
+ };
131
+ const keys = byTool[name] ?? ['path', 'command', 'pattern', 'query', 'file'];
132
+ const detail = firstArgString(part, keys);
133
+ return detail === undefined ? '' : truncateDetail(detail);
134
+ }
135
+
53
136
  /**
54
137
  * A structural view of the pi SESSION-LOG records we classify. It mirrors the
55
138
  * `@earendil-works/pi-coding-agent` `SessionEntry` / `SessionMessageEntry`
@@ -94,9 +177,10 @@ export function boundaryLine(label: string, color: boolean): string {
94
177
  * - a `{type:"message", message:{role:"assistant", content}}` record → for each
95
178
  * `content[]` part: a `text` part yields its text (parts concatenated into
96
179
  * ONE line, like the old `jq` `.content[]` select); a `toolCall` part yields
97
- * `▶ <name>` (cyan; `name` OR `toolName`, defaulting to `tool`); a `thinking`
98
- * part (and anything else) is skipped. A plain-string `content` yields one
99
- * text line.
180
+ * `▶ <name> <detail>` (cyan; `name` OR `toolName`, defaulting to `tool`;
181
+ * `<detail>` is the per-tool argument summary, truncated to 64 chars with a
182
+ * `…`); a `thinking` part (and anything else) is skipped. A plain-string
183
+ * `content` yields one text line.
100
184
  * - everything else (`session`/`model_change`/`thinking_level_change`, user
101
185
  * messages, tool results, blank/malformed lines) → `[]` (skipped).
102
186
  *
@@ -138,7 +222,9 @@ export function formatWatchEvent(line: string, color: boolean): string[] {
138
222
  * pi-remote's `session-pool.ts` reference parser. The `text` parts are
139
223
  * concatenated into a SINGLE leading line (so a multi-part assistant turn reads
140
224
  * as one sentence, as `ar-run.sh --watch` showed it); each `toolCall` part adds a
141
- * `▶ <name>` line after it. A plain-string content yields the single text line.
225
+ * `▶ <name> <detail>` line after it (`<detail>` = the tool's high-signal
226
+ * argument, truncated to 64 chars). A plain-string content yields the single
227
+ * text line.
142
228
  * `thinking` parts and any other block type are skipped.
143
229
  */
144
230
  function assistantLines(content: unknown, color: boolean): string[] {
@@ -160,7 +246,9 @@ function assistantLines(content: unknown, color: boolean): string[] {
160
246
  (typeof p.name === 'string' && p.name !== '' && p.name) ||
161
247
  (typeof p.toolName === 'string' && p.toolName !== '' && p.toolName) ||
162
248
  'tool';
163
- lines.push(paint(`▶ ${name}`, color, CYAN));
249
+ const detail = toolCallDetail(name, p);
250
+ const label = detail === '' ? name : `${name} ${detail}`;
251
+ lines.push(paint(`▶ ${label}`, color, CYAN));
164
252
  }
165
253
  // `text` is collected by assistantContentText; `thinking` and any other
166
254
  // block type are skipped.
@@ -203,6 +291,13 @@ function assistantContentText(content: unknown): string {
203
291
  * message:{role:"assistant", content[]}}` records, takes the LAST one carrying
204
292
  * non-empty `text`, and returns its concatenated text.
205
293
  *
294
+ * Kept as a PURE `string → string | undefined` function on purpose: the pi
295
+ * adapter reads the session file and passes the JSONL text in, but a future
296
+ * stream/HTTP-shaped harness (opencode-style) can accumulate its own event
297
+ * stream into the same JSONL-shaped text and reuse this reader unchanged — the
298
+ * file shape does NOT leak through the `LaunchResult.output` seam (Option C).
299
+ * See `work/notes/findings/pi-harness-channels.md` (task `pi-harness-polish`).
300
+ *
206
301
  * Returns `undefined` when the log has no assistant text at all — an absent log,
207
302
  * a malformed/short log, or a run whose only assistant turn was tool-calls (no
208
303
  * `text` part). A tool-only assistant turn is NOT a final answer, so a LATER
@@ -74,7 +74,7 @@ export const WORK_ROOT = 'work' as const;
74
74
  * key-only relabel, this batch moves the folders too, so the KEY and VALUE change
75
75
  * together and the self-renaming-folder guard stays green. The KEY rename is a
76
76
  * mechanical relabel of the key literal at each call site (behaviour-preserving);
77
- * the legacy frontmatter `prd:` field survives ONLY as un-migrated on-disk DATA the
77
+ * the legacy frontmatter ''prd:'' field survives ONLY as un-migrated on-disk DATA the
78
78
  * `dorfl prd-to-spec` command textually converts to `spec:` (HARD CUTOVER: it is no
79
79
  * longer READ by `parseFrontmatter` — `spec:` is the only read key); the
80
80
  * `--specs-land-in` flag/`specsLandIn` config + the
@@ -219,12 +219,15 @@ export type TaskResolutionFolder =
219
219
  /**
220
220
  * The task LIFECYCLE folders a `task:<slug>` / lone-task `issue:` can reside
221
221
  * in (spec-complete.ts + close-job.ts `TASK_FOLDERS`): `tasks-ready`, `in-progress`,
222
- * `needs-attention`, `done`.
222
+ * `done`. `needs-attention` was dropped post-cutover
223
+ * (`finish-needs-attention-folder-cutover-remove-legacy-recovery-readers`, ratified
224
+ * in `docs/adr/needs-attention-folder-cutover-followup-nits.md`): the folder is
225
+ * retired — a stuck item is the per-item lock `state: stuck` with the body resting
226
+ * in `tasks/ready/`, so no task ever resides in `work/needs-attention/`.
223
227
  */
224
228
  export const TASK_LIFECYCLE_FOLDERS = [
225
229
  'tasks-ready',
226
230
  'in-progress',
227
- 'needs-attention',
228
231
  'done',
229
232
  ] as const satisfies readonly WorkFolderKey[];
230
233
 
package/src/workspace.ts CHANGED
@@ -155,7 +155,7 @@ export interface CreateJobOptions {
155
155
  /** The work slug being processed (→ branch `work/<type>-<slug>` + work-id). */
156
156
  slug: string;
157
157
  /**
158
- * The item TYPE — `'task'` (build) or `'prd'` (tasking) — namespacing the
158
+ * The item TYPE — `'task'` (build) or `'spec'` (tasking) — namespacing the
159
159
  * work branch via {@link workBranchRef} so a same-slug task and spec never
160
160
  * collide. Defaults to `'task'`.
161
161
  */