@awebai/oats 0.29.4 → 0.30.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 (263) hide show
  1. package/README.md +12 -6
  2. package/bin/oats.mjs +194 -50
  3. package/docs/capabilities.md +160 -171
  4. package/docs/capability-manifest.schema.json +6 -11
  5. package/docs/configuration.md +213 -64
  6. package/docs/design/2026-09-16-knowledge-capability-contract.md +36 -50
  7. package/docs/design/2026-09-23-workspace-module-contracts.md +377 -544
  8. package/docs/design/2026-09-26-okf-knowledge-operations.md +132 -357
  9. package/docs/design/2026-09-27-team-model-v2.md +97 -117
  10. package/docs/design/2026-09-28-automations-trust.md +38 -0
  11. package/docs/design/2026-09-28-soul-launch-preference.md +63 -0
  12. package/docs/design/HISTORY.md +65 -0
  13. package/docs/design/README.md +23 -54
  14. package/docs/desktop-cli-api.md +1787 -1777
  15. package/docs/desktop.md +30 -91
  16. package/docs/execution-targets.md +146 -292
  17. package/docs/first-team.md +31 -17
  18. package/docs/implementation.md +77 -288
  19. package/docs/integrations.md +118 -320
  20. package/docs/knowledge-capability-authoring.md +25 -52
  21. package/docs/knowledge-reference/acceptance.md +3 -3
  22. package/docs/knowledge-reference/adoption.md +1 -1
  23. package/docs/knowledge-reference/harvester.md +2 -2
  24. package/docs/knowledge-reference/package-craft.md +3 -3
  25. package/docs/knowledge-reference/provider-mapping.md +3 -6
  26. package/docs/knowledge-reference/reader-capture.md +3 -3
  27. package/docs/knowledge-theory.md +62 -166
  28. package/docs/knowledge.md +225 -404
  29. package/docs/layers.md +42 -97
  30. package/docs/oats-local.schema.json +58 -5
  31. package/docs/oats-membership.schema.json +1 -8
  32. package/docs/oats-package.schema.json +5 -5
  33. package/docs/oats-workspace.schema.json +8 -22
  34. package/docs/official-catalog.md +25 -28
  35. package/docs/packages.md +45 -63
  36. package/docs/plans/0.30-close-out.md +83 -0
  37. package/docs/release-lane.md +82 -0
  38. package/docs/release-notes/oats-framework-v1.1.3.md +10 -8
  39. package/docs/release-notes/v0.19.0.md +48 -147
  40. package/docs/release-notes/v0.19.1.md +2 -3
  41. package/docs/release-notes/v0.19.3.md +2 -15
  42. package/docs/release-notes/v0.20.0.md +0 -15
  43. package/docs/release-notes/v0.22.0.md +71 -138
  44. package/docs/release-notes/v0.22.1.md +42 -90
  45. package/docs/release-notes/v0.22.10.md +1 -1
  46. package/docs/release-notes/v0.22.11.md +1 -47
  47. package/docs/release-notes/v0.22.12.md +4 -13
  48. package/docs/release-notes/v0.22.13.md +1 -42
  49. package/docs/release-notes/v0.22.14.md +3 -11
  50. package/docs/release-notes/v0.22.15.md +1 -46
  51. package/docs/release-notes/v0.22.16.md +6 -8
  52. package/docs/release-notes/v0.22.18.md +1 -99
  53. package/docs/release-notes/v0.22.19.md +3 -14
  54. package/docs/release-notes/v0.22.2.md +6 -15
  55. package/docs/release-notes/v0.22.3.md +0 -1
  56. package/docs/release-notes/v0.22.4.md +1 -14
  57. package/docs/release-notes/v0.22.5.md +2 -12
  58. package/docs/release-notes/v0.22.6.md +0 -3
  59. package/docs/release-notes/v0.23.0.md +9 -25
  60. package/docs/release-notes/v0.23.1.md +9 -25
  61. package/docs/release-notes/v0.23.2.md +2 -4
  62. package/docs/release-notes/v0.24.0.md +56 -97
  63. package/docs/release-notes/v0.24.1.md +7 -11
  64. package/docs/release-notes/v0.24.10.md +34 -45
  65. package/docs/release-notes/v0.24.11.md +12 -20
  66. package/docs/release-notes/v0.24.12.md +35 -48
  67. package/docs/release-notes/v0.24.13.md +34 -41
  68. package/docs/release-notes/v0.24.2.md +9 -13
  69. package/docs/release-notes/v0.24.3.md +7 -11
  70. package/docs/release-notes/v0.24.4.md +6 -6
  71. package/docs/release-notes/v0.24.5.md +6 -10
  72. package/docs/release-notes/v0.24.6.md +2 -5
  73. package/docs/release-notes/v0.24.7.md +46 -75
  74. package/docs/release-notes/v0.24.8.md +58 -96
  75. package/docs/release-notes/v0.24.9.md +38 -54
  76. package/docs/release-notes/v0.25.0.md +59 -76
  77. package/docs/release-notes/v0.25.1.md +57 -81
  78. package/docs/release-notes/v0.25.2.md +51 -70
  79. package/docs/release-notes/v0.25.3.md +11 -13
  80. package/docs/release-notes/v0.25.4.md +9 -13
  81. package/docs/release-notes/v0.25.5.md +3 -5
  82. package/docs/release-notes/v0.25.6.md +20 -29
  83. package/docs/release-notes/v0.25.7.md +5 -7
  84. package/docs/release-notes/v0.25.8.md +26 -39
  85. package/docs/release-notes/v0.26.0.md +175 -646
  86. package/docs/release-notes/v0.27.0.md +4 -5
  87. package/docs/release-notes/v0.27.1.md +4 -6
  88. package/docs/release-notes/v0.27.2.md +1 -1
  89. package/docs/release-notes/v0.28.0.md +57 -124
  90. package/docs/release-notes/v0.29.0.md +89 -208
  91. package/docs/release-notes/v0.29.1.md +1 -1
  92. package/docs/release-notes/v0.29.2.md +3 -4
  93. package/docs/release-notes/v0.30.0.md +205 -0
  94. package/docs/release-notes/v0.30.1.md +123 -0
  95. package/docs/schedules.md +280 -363
  96. package/docs/servers.md +99 -117
  97. package/docs/soul.schema.json +2 -9
  98. package/docs/souls-and-instances.md +145 -158
  99. package/docs/workspaces.md +137 -215
  100. package/lib/automations.mjs +21 -6
  101. package/lib/core.mjs +226 -74
  102. package/lib/instance-events.mjs +1 -1
  103. package/lib/instance-inspect.mjs +109 -34
  104. package/lib/instance-lifecycle.mjs +14 -1
  105. package/lib/instance-resolution.mjs +26 -27
  106. package/lib/launch-preference.mjs +87 -0
  107. package/lib/materialize.mjs +3 -3
  108. package/lib/packages.mjs +1 -1
  109. package/lib/resolve.mjs +30 -88
  110. package/lib/schedule.mjs +1 -1
  111. package/lib/teams-verbs.mjs +195 -0
  112. package/lib/teams.mjs +190 -0
  113. package/lib/triggers.mjs +2 -2
  114. package/lib/workspace.mjs +54 -147
  115. package/package-catalog.json +10 -16
  116. package/package.json +1 -3
  117. package/skills/oats-getting-started/SKILL.md +25 -13
  118. package/capabilities/oats-authoring/LICENSE +0 -21
  119. package/capabilities/oats-authoring/oats-package.json +0 -11
  120. package/capabilities/oats-authoring/oats.json +0 -12
  121. package/capabilities/oats-authoring/skills/integration-authoring/SKILL.md +0 -84
  122. package/capabilities/oats-authoring/skills/skill-craft/SKILL.md +0 -109
  123. package/capabilities/oats-authoring/skills/soul-craft/SKILL.md +0 -116
  124. package/capabilities/oats-aweb/bin/oats-aweb-binding.mjs +0 -11
  125. package/capabilities/oats-aweb/bin/oats-aweb.mjs +0 -1338
  126. package/capabilities/oats-aweb/injects/aweb.md +0 -47
  127. package/capabilities/oats-aweb/lib/binding-wire.mjs +0 -356
  128. package/capabilities/oats-aweb/lib/captured-execution.mjs +0 -91
  129. package/capabilities/oats-aweb/lib/captured-native.mjs +0 -91
  130. package/capabilities/oats-aweb/lib/grant-custody.mjs +0 -38
  131. package/capabilities/oats-aweb/lib/invocation-shape.mjs +0 -135
  132. package/capabilities/oats-aweb/lib/portable-binding.mjs +0 -146
  133. package/capabilities/oats-aweb/lib/session-readiness.mjs +0 -56
  134. package/capabilities/oats-aweb/lib/wake-receive.mjs +0 -56
  135. package/capabilities/oats-aweb/oats.json +0 -208
  136. package/capabilities/oats-aweb/skills/LICENSE +0 -21
  137. package/capabilities/oats-aweb/skills/VENDORED.md +0 -31
  138. package/capabilities/oats-aweb/skills/aweb-identity/SKILL.md +0 -201
  139. package/capabilities/oats-aweb/skills/aweb-messaging/SKILL.md +0 -161
  140. package/capabilities/oats-aweb/skills/aweb-messaging/references/messaging-scenarios.md +0 -61
  141. package/capabilities/oats-aweb/skills/aweb-team-membership/SKILL.md +0 -116
  142. package/capabilities/oats-aweb/skills/aweb-team-membership/references/team-membership-reference.md +0 -74
  143. package/capabilities/oats-aweb/skills/oats-aweb/SKILL.md +0 -216
  144. package/capabilities/oats-jira/bin/oats-jira.mjs +0 -40
  145. package/capabilities/oats-jira/injects/jira.md +0 -10
  146. package/capabilities/oats-jira/oats.json +0 -22
  147. package/capabilities/oats-jira/skills/jira-tasks/SKILL.md +0 -179
  148. package/capabilities/oats-linear/bin/oats-linear-hook.mjs +0 -34
  149. package/capabilities/oats-linear/bin/oats-linear.mjs +0 -344
  150. package/capabilities/oats-linear/injects/linear.md +0 -8
  151. package/capabilities/oats-linear/oats.json +0 -24
  152. package/capabilities/oats-linear/skills/linear-tasks/SKILL.md +0 -223
  153. package/capabilities/oats-okf/bin/oats-okf-binding.mjs +0 -14
  154. package/capabilities/oats-okf/bin/oats-okf.mjs +0 -209
  155. package/capabilities/oats-okf/injects/okf.md +0 -42
  156. package/capabilities/oats-okf/lib/binding-wire.mjs +0 -348
  157. package/capabilities/oats-okf/lib/captured-worker.mjs +0 -109
  158. package/capabilities/oats-okf/lib/config.mjs +0 -124
  159. package/capabilities/oats-okf/lib/consult.mjs +0 -518
  160. package/capabilities/oats-okf/lib/harvest-status.mjs +0 -88
  161. package/capabilities/oats-okf/lib/harvest-switch.mjs +0 -94
  162. package/capabilities/oats-okf/lib/inspection.mjs +0 -119
  163. package/capabilities/oats-okf/lib/invocation-context.mjs +0 -111
  164. package/capabilities/oats-okf/lib/invocation-shape.mjs +0 -135
  165. package/capabilities/oats-okf/lib/io.mjs +0 -118
  166. package/capabilities/oats-okf/lib/migration.mjs +0 -137
  167. package/capabilities/oats-okf/lib/okf-validate.mjs +0 -123
  168. package/capabilities/oats-okf/lib/portable-binding.mjs +0 -199
  169. package/capabilities/oats-okf/lib/source-contract.mjs +0 -46
  170. package/capabilities/oats-okf/lib/sources.mjs +0 -424
  171. package/capabilities/oats-okf/lib/stores.mjs +0 -473
  172. package/capabilities/oats-okf/lib/worker.mjs +0 -497
  173. package/capabilities/oats-okf/oats.json +0 -148
  174. package/capabilities/oats-okf/schemas/okf-base.schema.json +0 -46
  175. package/capabilities/oats-okf/schemas/okf-bindings.schema.json +0 -112
  176. package/capabilities/oats-okf/schemas/okf-portable-declaration.schema.json +0 -87
  177. package/capabilities/oats-okf/schemas/okf-portable-payload.schema.json +0 -113
  178. package/capabilities/oats-okf/schemas/okf-soul.schema.json +0 -37
  179. package/capabilities/oats-okf/skills/okf-consultation/SKILL.md +0 -144
  180. package/capabilities/oats-okf/skills/okf-consultation/references/consult.md +0 -86
  181. package/capabilities/oats-okf/skills/okf-instance-knowledge/SKILL.md +0 -104
  182. package/capabilities/oats-okf-harvest/bin/okf-harvest.mjs +0 -140
  183. package/capabilities/oats-okf-harvest/injects/harvester.md +0 -12
  184. package/capabilities/oats-okf-harvest/oats.json +0 -26
  185. package/capabilities/oats-okf-harvest/skills/knowledge-harvest/SKILL.md +0 -168
  186. package/capabilities/oats-okf-harvest/skills/knowledge-theory/SKILL.md +0 -192
  187. package/capabilities/oats-okf-harvest/skills/okf-authoring/SKILL.md +0 -151
  188. package/capabilities/oats-okf-harvest/skills/okf-authoring/scripts/okf-validate.mjs +0 -123
  189. package/capabilities/oats-okf-maintenance/bin/okf-maintenance.mjs +0 -170
  190. package/capabilities/oats-okf-maintenance/injects/maintainer.md +0 -12
  191. package/capabilities/oats-okf-maintenance/lib/provenance.mjs +0 -50
  192. package/capabilities/oats-okf-maintenance/oats.json +0 -21
  193. package/capabilities/oats-okf-maintenance/skills/knowledge-review/SKILL.md +0 -159
  194. package/capabilities/oats-okf-maintenance/skills/knowledge-theory/SKILL.md +0 -192
  195. package/capabilities/oats-okf-maintenance/skills/okf-authoring/SKILL.md +0 -151
  196. package/capabilities/oats-okf-maintenance/skills/okf-authoring/scripts/okf-validate.mjs +0 -123
  197. package/capabilities/oats-okf-maintenance/skills/okf-trigger-setup/SKILL.md +0 -146
  198. package/capabilities/oats-review/injects/review.md +0 -69
  199. package/capabilities/oats-review/oats.json +0 -10
  200. package/capabilities/oats-review/skills/code-review/SKILL.md +0 -44
  201. package/capabilities/oats-review/skills/security-review/SKILL.md +0 -59
  202. package/docs/conventions.md +0 -90
  203. package/docs/design/2026-09-07-architecture-reassessment.md +0 -131
  204. package/docs/design/2026-09-07-desktop-souls-capabilities.md +0 -50
  205. package/docs/design/2026-09-07-mobile-agent-management-proposal.md +0 -228
  206. package/docs/design/2026-09-08-expert-assisted-deployment-proposal.md +0 -558
  207. package/docs/design/2026-09-13-knowledge-and-memory-direction.md +0 -744
  208. package/docs/design/2026-09-13-knowledge-implementation.md +0 -127
  209. package/docs/design/2026-09-13-knowledge-location-contract.md +0 -340
  210. package/docs/design/2026-09-14-artifact-retention-contract.md +0 -190
  211. package/docs/design/2026-09-14-portable-souls-and-git-workspaces.md +0 -708
  212. package/docs/design/2026-09-14-portable-souls-contract-amendments.md +0 -85
  213. package/docs/design/2026-09-14-portable-souls-explainer.md +0 -750
  214. package/docs/design/2026-09-15-captured-dispatch.md +0 -127
  215. package/docs/design/2026-09-15-captured-resolution-records.md +0 -143
  216. package/docs/design/2026-09-15-package-preparation.md +0 -100
  217. package/docs/design/2026-09-15-portable-data-contract.md +0 -121
  218. package/docs/design/2026-09-15-portable-declarations.md +0 -189
  219. package/docs/design/2026-09-15-portable-souls-handoff.md +0 -150
  220. package/docs/design/2026-09-15-portable-souls-implementation.md +0 -417
  221. package/docs/design/2026-09-15-selection-lock-and-approval.md +0 -122
  222. package/docs/design/2026-09-15-source-observation.md +0 -119
  223. package/docs/design/2026-09-16-captured-admission.md +0 -77
  224. package/docs/design/2026-09-16-captured-helper-dispatch.md +0 -105
  225. package/docs/design/2026-09-16-captured-launch-inputs.md +0 -42
  226. package/docs/design/2026-09-16-command-profile-preparation.md +0 -86
  227. package/docs/design/2026-09-16-fresh-install-first-rollout.md +0 -47
  228. package/docs/design/2026-09-16-fresh-operator-walkthrough.md +0 -282
  229. package/docs/design/2026-09-16-messaging-capability-contract.md +0 -59
  230. package/docs/design/2026-09-16-portable-migration-evidence.md +0 -158
  231. package/docs/design/2026-09-16-portable-onboarding.md +0 -179
  232. package/docs/design/2026-09-16-prepare-request-transport.md +0 -26
  233. package/docs/design/2026-09-16-provider-binding-codecs.md +0 -98
  234. package/docs/design/2026-09-16-provider-binding-wire.md +0 -274
  235. package/docs/design/2026-09-17-capability-helper-input-contract.md +0 -95
  236. package/docs/design/2026-09-17-captured-backend-parity.md +0 -53
  237. package/docs/design/2026-09-17-captured-native-start.md +0 -58
  238. package/docs/design/2026-09-17-portable-boundary-hookup.md +0 -19
  239. package/docs/design/2026-09-17-portable-boundary-resources.md +0 -52
  240. package/docs/design/2026-09-17-public-captured-start.md +0 -108
  241. package/docs/design/2026-09-17-public-prepare-request.md +0 -90
  242. package/docs/design/2026-09-18-captured-pi-host.md +0 -205
  243. package/docs/design/2026-09-18-first-cut-release-checklist.md +0 -131
  244. package/docs/design/2026-09-18-herdr-protocol-compatibility.md +0 -60
  245. package/docs/design/2026-09-20-redesign-program-board.md +0 -142
  246. package/docs/design/2026-09-20-workspace-and-portable-adoption-plan.md +0 -289
  247. package/docs/design/2026-09-20-workspace-onboarding-public.md +0 -207
  248. package/docs/design/2026-09-22-desktop-parity-seams.md +0 -58
  249. package/docs/design/2026-09-23-simplified-workspace-model.md +0 -711
  250. package/docs/design/2026-09-23-workspace-v2-implementation-plan.md +0 -65
  251. package/docs/design/2026-09-24-desktop-phase-f-boundary.md +0 -242
  252. package/docs/design/2026-09-24-phase-d-plan.md +0 -305
  253. package/docs/design/2026-09-25-teams-contract.md +0 -258
  254. package/docs/design/2026-09-26-desktop-design-brief-architecture.md +0 -241
  255. package/docs/design/desktop-ux-plan.md +0 -362
  256. package/docs/design/launch-configurations.md +0 -168
  257. package/docs/design/okf-mirror-provenance.md +0 -105
  258. package/docs/design/operations-contract.md +0 -141
  259. package/docs/oats-member.schema.json +0 -38
  260. package/skills/integration-authoring/SKILL.md +0 -84
  261. package/skills/oats-support/SKILL.md +0 -79
  262. package/skills/skill-craft/SKILL.md +0 -109
  263. package/skills/soul-craft/SKILL.md +0 -116
@@ -1,179 +0,0 @@
1
- ---
2
- name: jira-tasks
3
- description: >-
4
- Jira task tracking and agent roster protocol for OATS agents. Use when you
5
- are an agent instance working an epic, story, or task in Jira: reading your
6
- assignment, finding your work queue, joining or leaving an epic's Agent
7
- Roster, posting progress or handoff comments, transitioning ticket status,
8
- or creating stories/tasks under an epic. Also use when asked about "the
9
- board", "the roster", "your ticket", "epic status", or task tracking
10
- between agents. Uses the acli CLI (Atlassian CLI) from bash.
11
- ---
12
-
13
- # Agent task tracking (Jira)
14
-
15
- Jira is your deployment's **tasks layer** — the shared record for task
16
- tracking and the agent roster. Agents do not know about projects — you know
17
- **repos and epics**. The hierarchy:
18
-
19
- - **Epic** — the unit of work that kicks off, runs, and completes. May touch
20
- one repo or several. Its description carries the **Agent Roster** — the
21
- source of truth for who is on the epic and their role. Bugs/support run as
22
- a standing epic.
23
- - **Story** — a **group of related tasks** covering one part of the epic's
24
- work (often one repo's slice, one feature area). Not every task needs a
25
- story.
26
- - **Task** — a small bounded item, the thing an agent actually works. Lives
27
- **either directly under the epic** (standalone item) **or under a story**
28
- (part of a grouped slice). Everything traces up to an epic.
29
-
30
- ## Site and project (from your deployment, never hardcoded)
31
-
32
- Your Jira **site** and **project key** come from the tasks payload OATS merged
33
- for this instance: the soul's `soul.yaml` `tasks: { site, project }`, the
34
- deployment's `oats-local.yaml` `settings.oats.jira.{site,project}`, or a
35
- spawn's `--provider oats.jira key=value`. Find them, in order:
36
-
37
- 1. Your `TASK.md` briefing — the spawn hook writes a
38
- `Tasks: Jira — project <KEY> on <site>` line.
39
- 2. `./instance.json` in your instance home — `providers["oats.jira"]` is the
40
- merged payload this instance received.
41
- 3. Ask your human.
42
-
43
- Below, `<PROJECT>` means that project key. If site or project are unset,
44
- STOP and ask your human to set them — do not guess.
45
-
46
- **First use**: run `acli jira auth status` — if unauthorized, STOP and tell
47
- the human to run `acli jira auth login --web`. Never attempt login yourself.
48
-
49
- ## Identity rules (non-negotiable)
50
-
51
- - **The human assignee is always the owning engineer** (never change
52
- assignee to yourself; agents are not Jira users). Do not touch assignee
53
- unless told.
54
- - **You are identified by label and description**, not the assignee field:
55
- - Label `agent-<your-instance-alias>` on any story/task you work.
56
- - An `Agent:` line in the description (see templates).
57
- - **Never set or modify sprints.** Never delete tickets. Comment, don't
58
- rewrite, other agents' descriptions (exception: coordinators maintain the
59
- roster table).
60
-
61
- ## Your work queue
62
-
63
- ```bash
64
- acli jira workitem search --jql "project = <PROJECT> AND labels = agent-<alias> AND statusCategory != Done ORDER BY rank" --json
65
- acli jira workitem view <PROJECT>-1234 --json # read one ticket (description, labels, status)
66
- acli jira workitem search --jql "project = <PROJECT> AND parent = <PROJECT>-<epic> AND statusCategory != Done" --json # epic's direct children (stories + standalone tasks)
67
- acli jira workitem search --jql "project = <PROJECT> AND parent = <PROJECT>-<story> AND statusCategory != Done" --json # a story's tasks
68
- ```
69
-
70
- An epic's full open work = its direct children **plus** the tasks under each
71
- of its stories — walk one level down from stories when you need the complete
72
- picture.
73
-
74
- Record your epic and ticket keys in your instance memory (e.g. `STATE.md`
75
- `# Context`).
76
-
77
- ## The Agent Roster (epics)
78
-
79
- The epic description contains a `## Agent Roster` markdown table — current
80
- truth for who is on the epic:
81
-
82
- ```
83
- ## Agent Roster
84
-
85
- | Agent (instance) | Soul / class | Repo | Role on epic | Status | Since |
86
- |---|---|---|---|---|---|
87
- | coordinator-digest | coordinator (newsletter) | newsletter-service | runs the epic | active | 2026-07-07 |
88
- | developer-digest-api | developer (newsletter) | newsletter-service | implements API | active | 2026-07-07 |
89
- ```
90
-
91
- Protocol:
92
- - **Joining**: the coordinator (or the spawning agent) adds your row to the
93
- table (`acli jira workitem edit <epic> --description ...` with the full
94
- updated description — read it first, edit only the roster table) AND posts
95
- a comment: `[roster] <alias> joined — role: <role>, repo: <repo>`.
96
- - **Leaving/retiring**: set the row's Status to `retired` (keep the row — it
97
- is history) and comment `[roster] <alias> retired — <one-line outcome>`.
98
- - Only edit the roster table; never rewrite the rest of the epic description.
99
- - Comments are the event log; the table is current state. On conflict, fix
100
- the table and note it in a comment.
101
-
102
- ## Working a ticket
103
-
104
- 1. Read your ticket and its epic (description + roster) before starting.
105
- 2. **Milestones** → comment on your ticket, prefixed `[<alias>]`. Mirror the
106
- entry you record in your instance memory — same events, two audiences.
107
- 3. **Status transitions** — move your ticket as you work:
108
- `acli jira workitem transition <PROJECT>-1234 --status "In Progress"`.
109
- Discover valid statuses with `--help` or by trying; if a transition is
110
- rejected, comment instead and let the coordinator move it.
111
- 4. **Done** = your latest commit is review-clean and the branch is handed
112
- off. Comment the outcome (branch, PR link, verification), then transition.
113
- 5. **Handoff/blocked** → comment
114
- `[<alias>] handoff → <next-alias>: <what+where>` or
115
- `[<alias>] blocked: <what is needed, from whom>`.
116
-
117
- Tasks ≠ messaging: status and outcomes live here in Jira; conversation lives
118
- in your deployment's messaging layer. Mail nudges; Jira records.
119
-
120
- ## Creating tickets (coordinators; developers file follow-ups as Tasks)
121
-
122
- House rules: summary ≤ 12 words, describe the requirement not the solution,
123
- bugs always include reproduction steps, keep descriptions to a few bullets.
124
-
125
- **Choosing the level:**
126
- - Small bounded item, no siblings needed → **Task directly under the epic**.
127
- - A part of the epic's work that breaks into several related tasks → **Story
128
- under the epic, tasks under the story**. The story is the group, not the
129
- work item — agents are assigned to its tasks (a story worked wholly by one
130
- agent may carry that agent's label too).
131
- - Never create a story for a single task, and never nest stories.
132
-
133
- ```bash
134
- acli jira workitem create --project <PROJECT> --type Task --summary "<summary>" \
135
- --parent <PROJECT>-<epic-or-story> --label "agent-<alias>" --description "<see template>"
136
- acli jira workitem create --project <PROJECT> --type Story --summary "<summary>" \
137
- --parent <PROJECT>-<epic> --description "<see template>"
138
- ```
139
-
140
- ### Story/Task description template
141
-
142
- ```
143
- <What & why — 2-4 bullets. Acceptance criteria as a checklist.>
144
-
145
- ---
146
- Agent: <instance-alias> (who works this — matches the agent-<alias> label; stories list it only when one agent works the whole story)
147
- Soul: <soul-name> · Repo: <repo>
148
- Parent: <PROJECT>-<epic-or-story-key> · Epic: <PROJECT>-<epic-key>
149
- ```
150
-
151
- ### Epic description template
152
-
153
- ```
154
- <Intent — what this epic delivers and why. Walls — what is explicitly out.>
155
-
156
- Repos touched: <repo>, <repo>
157
- Human gates: <security/authz/migration/contract items needing sign-off, or "none">
158
-
159
- ## Agent Roster
160
-
161
- | Agent (instance) | Soul / class | Repo | Role on epic | Status | Since |
162
- |---|---|---|---|---|---|
163
- ```
164
-
165
- ### Comment conventions (machine-greppable prefixes)
166
-
167
- - `[roster] <alias> joined|retired — …`
168
- - `[<alias>] milestone: …` · `[<alias>] handoff → <alias>: …` ·
169
- `[<alias>] blocked: …` · `[<alias>] done: branch <name>, <verification>`
170
-
171
- ## Verify-before-trusting
172
-
173
- Jira workflows differ per site. On first real use in a deployment: check the
174
- project's issue types (`Epic/Story/Task/Bug`), whether `--parent` links
175
- stories/tasks to epics, whether a **Task can take a Story as parent** (some
176
- Jira configs only allow that via the Sub-task type — if so, use Sub-tasks
177
- under stories and treat them as tasks), and the exact status names. If
178
- reality differs, note it in a comment on your ticket and tell your
179
- coordinator so your deployment's conventions get recorded.
@@ -1,34 +0,0 @@
1
- #!/usr/bin/env node
2
- /** OATS spawn briefing for the Linear tasks integration. Makes no API calls. */
3
- const output = (value) => {
4
- process.stdout.write(JSON.stringify(value) + "\n");
5
- process.exit(0);
6
- };
7
-
8
- const event = process.env.OATS_EVENT || process.argv[2];
9
- if (event !== "spawn") output({ warning: `oats-linear: unknown event "${event}" (expected spawn)` });
10
-
11
- let settings = {};
12
- try { settings = JSON.parse(process.env.OATS_SETTINGS || "{}"); }
13
- catch { output({ warning: "oats-linear: the tasks settings payload (OATS_SETTINGS) is not valid JSON" }); }
14
-
15
- const instance = process.env.OATS_INSTANCE || "unknown-instance";
16
- const team = settings.team;
17
- const project = settings.project;
18
- const label = `agent-${instance}`;
19
- // Where team lives under the workspace model (0.26): the soul's tasks payload or this machine's settings.
20
- const TEAM_HOMES = "tasks: { team } in the soul's soul.yaml, or settings.oats.linear.team in the deployment's oats-local.yaml";
21
- const target = team
22
- ? `team ${team}${project ? `, default project ${project}` : ""}`
23
- : `team unset — ask your human to set ${TEAM_HOMES}`;
24
- const warnings = [];
25
- if (!team) warnings.push(`settings.team is unset (set ${TEAM_HOMES})`);
26
- if (!process.env.LINEAR_API_KEY) warnings.push("LINEAR_API_KEY is not in the spawn environment");
27
-
28
- output({
29
- meta: { label, ...(team ? { team } : {}), ...(project ? { project } : {}) },
30
- brief: `Tasks: Linear — ${target}. Your agent identity is label "${label}"; keep the human assignee unchanged. Load the linear-tasks skill before touching issues.`,
31
- ...(warnings.length ? {
32
- warning: `oats-linear: ${warnings.join("; ")} — see the linear-tasks skill`,
33
- } : {}),
34
- });
@@ -1,344 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * JSON-first Linear task operations for OATS.
4
- *
5
- * Uses Linear's official GraphQL API directly. No third-party Linear CLI or
6
- * SDK is required; authentication is a personal key in LINEAR_API_KEY.
7
- */
8
- import { readFileSync } from "node:fs";
9
-
10
- const API_URL = process.env.LINEAR_API_URL || "https://api.linear.app/graphql";
11
- const argv = process.argv.slice(2);
12
- const command = argv.shift();
13
-
14
- function die(message, details) {
15
- process.stderr.write(JSON.stringify({ error: String(message), ...(details ? { details } : {}) }, null, 2) + "\n");
16
- process.exit(1);
17
- }
18
- function print(value) { process.stdout.write(JSON.stringify(value, null, 2) + "\n"); }
19
- function parseArgs(values) {
20
- const options = new Map();
21
- const positional = [];
22
- for (let i = 0; i < values.length; i++) {
23
- const value = values[i];
24
- if (!value.startsWith("--")) { positional.push(value); continue; }
25
- const name = value.slice(2);
26
- const next = values[i + 1];
27
- const parsed = next !== undefined && !next.startsWith("--") ? values[++i] : true;
28
- const previous = options.get(name) || [];
29
- previous.push(parsed);
30
- options.set(name, previous);
31
- }
32
- return {
33
- positional,
34
- has: (name) => options.has(name),
35
- one: (name) => options.get(name)?.at(-1),
36
- many: (name) => options.get(name) || [],
37
- };
38
- }
39
- const args = parseArgs(argv);
40
-
41
- async function graphql(query, variables = {}) {
42
- const key = process.env.LINEAR_API_KEY;
43
- if (!key) die("LINEAR_API_KEY is not set", "Create a personal API key in Linear Settings → Security & access → API keys, export it, then run `oats linear auth`.");
44
- let response;
45
- try {
46
- response = await fetch(API_URL, {
47
- method: "POST",
48
- headers: {
49
- "Authorization": key,
50
- "Content-Type": "application/json",
51
- "User-Agent": "oats-linear/0.1",
52
- },
53
- body: JSON.stringify({ query, variables }),
54
- signal: AbortSignal.timeout(30000),
55
- });
56
- } catch (error) {
57
- die(`Linear API request failed: ${error.message || error}`);
58
- }
59
- const text = await response.text();
60
- let payload;
61
- try { payload = JSON.parse(text); }
62
- catch { die(`Linear API returned HTTP ${response.status} with non-JSON content`, text.slice(0, 500)); }
63
- if (!response.ok || payload.errors?.length) {
64
- const errors = (payload.errors || []).map((error) => ({
65
- message: error.extensions?.userPresentableMessage || error.message,
66
- code: error.extensions?.code,
67
- path: error.path,
68
- }));
69
- const hint = response.status === 401 ? "Check LINEAR_API_KEY and run `oats linear auth`." : undefined;
70
- die(`Linear API request failed (HTTP ${response.status})`, { errors, ...(hint ? { hint } : {}) });
71
- }
72
- return payload.data;
73
- }
74
-
75
- const PAGE_INFO = "pageInfo { hasNextPage endCursor }";
76
- const ISSUE_FIELDS = `
77
- id identifier title description url priority priorityLabel
78
- team { id key name }
79
- state { id name type }
80
- project { id name slugId }
81
- parent { id identifier title }
82
- assignee { id name email }
83
- labels(first: 100) { nodes { id name } }
84
- `;
85
-
86
- async function teamByKey(key) {
87
- if (!key || key === true) die("--team <KEY> is required");
88
- const data = await graphql(`
89
- query OatsLinearTeam($key: String!) {
90
- teams(first: 2, filter: { key: { eqIgnoreCase: $key } }) { nodes { id key name } }
91
- }
92
- `, { key });
93
- if (data.teams.nodes.length === 0) die(`Linear team "${key}" was not found`, "Run `oats linear teams` and use its key.");
94
- if (data.teams.nodes.length > 1) die(`Linear team key "${key}" is ambiguous`);
95
- return data.teams.nodes[0];
96
- }
97
-
98
- async function statesForTeam(team) {
99
- const data = await graphql(`
100
- query OatsLinearStates($id: String!) {
101
- team(id: $id) { states(first: 100) { nodes { id name type position } } }
102
- }
103
- `, { id: team.id });
104
- return data.team.states.nodes.sort((a, b) => a.position - b.position);
105
- }
106
- async function stateByName(team, name) {
107
- const states = await statesForTeam(team);
108
- const matches = states.filter((state) => state.id === name || state.name.toLowerCase() === String(name).toLowerCase());
109
- if (matches.length !== 1) die(`Workflow state "${name}" was not found for team ${team.key}`, { available: states.map((state) => `${state.name} (${state.type})`) });
110
- return matches[0];
111
- }
112
-
113
- async function labelsForTeam(team) {
114
- const data = await graphql(`
115
- query OatsLinearLabels($teamId: ID!) {
116
- issueLabels(first: 250, filter: { or: [
117
- { team: { null: true } },
118
- { team: { id: { eq: $teamId } } }
119
- ] }) {
120
- nodes { id name color isGroup team { id key } }
121
- }
122
- }
123
- `, { teamId: team.id });
124
- return data.issueLabels.nodes;
125
- }
126
- async function findLabel(team, name) {
127
- const labels = await labelsForTeam(team);
128
- const matches = labels.filter((label) => !label.isGroup && label.name.toLowerCase() === String(name).toLowerCase());
129
- const scoped = matches.find((label) => label.team?.id === team.id);
130
- return scoped || matches.find((label) => !label.team);
131
- }
132
- async function ensureAgentLabel(team, alias) {
133
- const name = `agent-${alias}`;
134
- const existing = await findLabel(team, name);
135
- if (existing) return existing;
136
- const data = await graphql(`
137
- mutation OatsLinearCreateLabel($input: IssueLabelCreateInput!) {
138
- issueLabelCreate(input: $input) { success issueLabel { id name color team { id key } } }
139
- }
140
- `, { input: { name, teamId: team.id, color: "#5E6AD2", description: "OATS agent instance identity" } });
141
- if (!data.issueLabelCreate.success) die(`Linear did not create label "${name}"`);
142
- return data.issueLabelCreate.issueLabel;
143
- }
144
- async function labelByName(team, name) {
145
- const label = await findLabel(team, name);
146
- if (!label) die(`Label "${name}" was not found for team ${team.key}`, "Create it in Linear first. Agent labels are created automatically by --agent.");
147
- return label;
148
- }
149
-
150
- async function projectsForTeam(team) {
151
- const data = await graphql(`
152
- query OatsLinearProjects($teamId: ID!) {
153
- projects(first: 250, filter: { accessibleTeams: { some: { id: { eq: $teamId } } } }) {
154
- nodes { id name slugId status { id name type } teams(first: 20) { nodes { id key name } } }
155
- }
156
- }
157
- `, { teamId: team.id });
158
- return data.projects.nodes;
159
- }
160
- async function projectByRef(team, ref) {
161
- const projects = await projectsForTeam(team);
162
- const needle = String(ref).toLowerCase();
163
- const matches = projects.filter((project) =>
164
- project.id === ref || project.slugId.toLowerCase() === needle || project.name.toLowerCase() === needle);
165
- if (matches.length !== 1) die(`Project "${ref}" ${matches.length ? "is ambiguous" : "was not found"} for team ${team.key}`, { available: projects.map((project) => ({ name: project.name, slug: project.slugId })) });
166
- return matches[0];
167
- }
168
-
169
- async function issueById(id) {
170
- if (!id || id === true) die("an issue identifier such as ENG-123 is required");
171
- const data = await graphql(`
172
- query OatsLinearIssue($id: String!) { issue(id: $id) { ${ISSUE_FIELDS} } }
173
- `, { id });
174
- return data.issue;
175
- }
176
-
177
- function textOption(name) {
178
- const inline = args.one(name);
179
- const file = args.one(`${name}-file`);
180
- if (inline !== undefined && file !== undefined) die(`use only one of --${name} or --${name}-file`);
181
- if (file !== undefined) {
182
- if (file === true) die(`--${name}-file needs a path`);
183
- try { return readFileSync(file, "utf8").trim(); }
184
- catch (error) { die(`cannot read --${name}-file ${file}: ${error.message}`); }
185
- }
186
- return inline;
187
- }
188
- function assertTerminalAllowed(state) {
189
- if (["completed", "canceled", "duplicate"].includes(state.type) && !args.has("allow-terminal")) {
190
- die(`refusing terminal state "${state.name}" without --allow-terminal`, "Agents should hand work to review, not close or cancel it. Use --allow-terminal only with explicit human authorization.");
191
- }
192
- }
193
-
194
- async function auth() {
195
- const data = await graphql(`
196
- query OatsLinearAuth { viewer { id name email } organization { id name urlKey } }
197
- `);
198
- print({ authenticated: true, endpoint: API_URL, viewer: data.viewer, workspace: data.organization });
199
- }
200
- async function teams() {
201
- const data = await graphql(`
202
- query OatsLinearTeams { teams(first: 100) { nodes { id key name } } }
203
- `);
204
- print(data.teams.nodes);
205
- }
206
- async function states() {
207
- const team = await teamByKey(args.one("team"));
208
- print(await statesForTeam(team));
209
- }
210
- async function projects() {
211
- const team = await teamByKey(args.one("team"));
212
- print(await projectsForTeam(team));
213
- }
214
- async function labels() {
215
- const team = await teamByKey(args.one("team"));
216
- print(await labelsForTeam(team));
217
- }
218
-
219
- async function listIssues() {
220
- const team = await teamByKey(args.one("team"));
221
- const requestedLimit = Number(args.one("limit") || 100);
222
- if (!Number.isInteger(requestedLimit) || requestedLimit < 1 || requestedLimit > 250) die("--limit must be an integer from 1 to 250");
223
- const filter = { team: { id: { eq: team.id } } };
224
- if (!args.has("all")) filter.state = { type: { nin: ["completed", "canceled", "duplicate"] } };
225
- if (args.one("agent")) filter.labels = { some: { name: { eqIgnoreCase: `agent-${args.one("agent")}` } } };
226
- if (args.one("project")) {
227
- const project = await projectByRef(team, args.one("project"));
228
- filter.project = { id: { eq: project.id } };
229
- }
230
- const data = await graphql(`
231
- query OatsLinearIssues($first: Int!, $filter: IssueFilter) {
232
- issues(first: $first, filter: $filter) { nodes { ${ISSUE_FIELDS} } ${PAGE_INFO} }
233
- }
234
- `, { first: requestedLimit, filter });
235
- print({ issues: data.issues.nodes, pageInfo: data.issues.pageInfo });
236
- }
237
- async function createIssue() {
238
- const team = await teamByKey(args.one("team"));
239
- const title = args.one("title");
240
- if (!title || title === true) die("--title <text> is required");
241
- let description = textOption("description");
242
- const input = { teamId: team.id, title };
243
- if (description !== undefined) input.description = description;
244
- if (args.one("project")) input.projectId = (await projectByRef(team, args.one("project"))).id;
245
- if (args.one("parent")) input.parentId = args.one("parent");
246
- if (args.one("state")) {
247
- const state = await stateByName(team, args.one("state"));
248
- assertTerminalAllowed(state);
249
- input.stateId = state.id;
250
- }
251
- const issueLabels = [];
252
- if (args.one("agent")) {
253
- const alias = args.one("agent");
254
- issueLabels.push(await ensureAgentLabel(team, alias));
255
- if (!/^Agent:/mi.test(description || "")) {
256
- description = `${description ? `${description.trim()}\n\n` : ""}---\nAgent: ${alias}`;
257
- input.description = description;
258
- }
259
- }
260
- for (const name of args.many("label")) issueLabels.push(await labelByName(team, name));
261
- if (issueLabels.length) input.labelIds = [...new Set(issueLabels.map((label) => label.id))];
262
- const data = await graphql(`
263
- mutation OatsLinearIssueCreate($input: IssueCreateInput!) {
264
- issueCreate(input: $input) { success issue { ${ISSUE_FIELDS} } }
265
- }
266
- `, { input });
267
- if (!data.issueCreate.success || !data.issueCreate.issue) die("Linear did not create the issue");
268
- print(data.issueCreate.issue);
269
- }
270
- async function updateIssue(id) {
271
- const current = await issueById(id);
272
- const team = current.team;
273
- const input = {};
274
- if (args.has("title")) input.title = args.one("title");
275
- const description = textOption("description");
276
- if (description !== undefined) input.description = description;
277
- if (args.one("state")) {
278
- const state = await stateByName(team, args.one("state"));
279
- assertTerminalAllowed(state);
280
- input.stateId = state.id;
281
- }
282
- const added = [];
283
- if (args.one("agent")) added.push((await ensureAgentLabel(team, args.one("agent"))).id);
284
- for (const name of args.many("add-label")) added.push((await labelByName(team, name)).id);
285
- const removed = [];
286
- for (const name of args.many("remove-label")) removed.push((await labelByName(team, name)).id);
287
- if (added.length) input.addedLabelIds = [...new Set(added)];
288
- if (removed.length) input.removedLabelIds = [...new Set(removed)];
289
- if (Object.keys(input).length === 0) die("no update supplied", "Use --title, --description[-file], --state, --agent, --add-label, or --remove-label.");
290
- const data = await graphql(`
291
- mutation OatsLinearIssueUpdate($id: String!, $input: IssueUpdateInput!) {
292
- issueUpdate(id: $id, input: $input) { success issue { ${ISSUE_FIELDS} } }
293
- }
294
- `, { id, input });
295
- if (!data.issueUpdate.success || !data.issueUpdate.issue) die(`Linear did not update ${id}`);
296
- print(data.issueUpdate.issue);
297
- }
298
- async function commentIssue(id) {
299
- const body = textOption("body");
300
- if (!body || body === true) die("--body <markdown> or --body-file <path> is required");
301
- const data = await graphql(`
302
- mutation OatsLinearComment($input: CommentCreateInput!) {
303
- commentCreate(input: $input) { success comment { id body createdAt url user { id name } } }
304
- }
305
- `, { input: { issueId: id, body } });
306
- if (!data.commentCreate.success) die(`Linear did not comment on ${id}`);
307
- print(data.commentCreate.comment);
308
- }
309
-
310
- function usage() {
311
- process.stderr.write(`oats linear commands (all output JSON):
312
- auth
313
- teams
314
- states --team <KEY>
315
- projects --team <KEY>
316
- labels --team <KEY>
317
- issue list --team <KEY> [--agent <alias>] [--project <name|slug>] [--all] [--limit 100]
318
- issue get <KEY-123>
319
- issue create --team <KEY> --title <text> [--description <md>|--description-file <path>]
320
- [--project <name|slug>] [--parent <KEY-123>] [--state <name>] [--agent <alias>]
321
- [--label <name> ...]
322
- issue update <KEY-123> [--title <text>] [--description <md>|--description-file <path>]
323
- [--state <name>] [--agent <alias>] [--add-label <name> ...] [--remove-label <name> ...]
324
- [--allow-terminal]
325
- issue comment <KEY-123> (--body <md>|--body-file <path>)
326
- `);
327
- process.exit(1);
328
- }
329
-
330
- if (command === "auth") await auth();
331
- else if (command === "teams") await teams();
332
- else if (command === "states") await states();
333
- else if (command === "projects") await projects();
334
- else if (command === "labels") await labels();
335
- else if (command === "issue") {
336
- const subcommand = args.positional[0];
337
- const id = args.positional[1];
338
- if (subcommand === "list") await listIssues();
339
- else if (subcommand === "get") print(await issueById(id));
340
- else if (subcommand === "create") await createIssue();
341
- else if (subcommand === "update") await updateIssue(id);
342
- else if (subcommand === "comment") await commentIssue(id);
343
- else usage();
344
- } else usage();
@@ -1,8 +0,0 @@
1
- ## Tasks: Linear
2
-
3
- Your tasks layer is **Linear**, operated through the JSON-first `oats linear`
4
- commands. You are identified by the label `agent-<your-instance-name>`, not
5
- by changing the human assignee. Load the **linear-tasks** skill before reading
6
- your queue, creating or updating issues/sub-issues, changing status, or posting
7
- handoffs. Tasks only: status and outcomes live in Linear; conversation lives
8
- in your deployment's messaging layer.
@@ -1,24 +0,0 @@
1
- {
2
- "capability": "oats.linear",
3
- "command": "linear",
4
- "version": "1.0.1",
5
- "compatibility": { "oats": ">=0.26.0" },
6
- "layer": "tasks",
7
- "description": "Tasks layer via Linear: JSON-first GraphQL commands, project/issue/sub-issue workflow, label-based agent identity.",
8
- "requires": [],
9
- "skills": [
10
- "skills"
11
- ],
12
- "commands": {
13
- "auth": "bin/oats-linear.mjs auth",
14
- "teams": "bin/oats-linear.mjs teams",
15
- "states": "bin/oats-linear.mjs states",
16
- "projects": "bin/oats-linear.mjs projects",
17
- "labels": "bin/oats-linear.mjs labels",
18
- "issue": "bin/oats-linear.mjs issue"
19
- },
20
- "inject": "injects/linear.md",
21
- "hooks": {
22
- "spawn": "bin/oats-linear-hook.mjs spawn"
23
- }
24
- }