@awebai/oats 0.30.0 → 0.30.2

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 (128) hide show
  1. package/bin/oats.mjs +1 -1
  2. package/docs/capabilities.md +3 -3
  3. package/docs/design/2026-09-23-workspace-module-contracts.md +2 -1
  4. package/docs/desktop-cli-api.md +23 -11
  5. package/docs/first-team.md +1 -1
  6. package/docs/implementation.md +2 -1
  7. package/docs/integrations.md +1 -1
  8. package/docs/knowledge-capability-authoring.md +1 -1
  9. package/docs/knowledge.md +4 -4
  10. package/docs/official-catalog.md +4 -4
  11. package/docs/packages.md +13 -13
  12. package/docs/plans/0.30-close-out.md +24 -2
  13. package/docs/release-lane.md +7 -2
  14. package/docs/release-notes/v0.30.1.md +123 -0
  15. package/docs/release-notes/v0.30.2.md +85 -0
  16. package/docs/souls-and-instances.md +6 -5
  17. package/docs/workspaces.md +7 -2
  18. package/lib/core.mjs +42 -28
  19. package/lib/instance-inspect.mjs +1 -1
  20. package/lib/instance-resolution.mjs +15 -8
  21. package/lib/materialize.mjs +33 -19
  22. package/lib/packages.mjs +1 -1
  23. package/lib/resolve.mjs +1 -1
  24. package/package-catalog.json +3 -3
  25. package/package.json +1 -3
  26. package/skills/oats-getting-started/SKILL.md +2 -2
  27. package/capabilities/oats-authoring/LICENSE +0 -21
  28. package/capabilities/oats-authoring/oats-package.json +0 -11
  29. package/capabilities/oats-authoring/oats.json +0 -12
  30. package/capabilities/oats-authoring/skills/integration-authoring/SKILL.md +0 -84
  31. package/capabilities/oats-authoring/skills/skill-craft/SKILL.md +0 -109
  32. package/capabilities/oats-authoring/skills/soul-craft/SKILL.md +0 -116
  33. package/capabilities/oats-aweb/bin/oats-aweb-binding.mjs +0 -11
  34. package/capabilities/oats-aweb/bin/oats-aweb.mjs +0 -1672
  35. package/capabilities/oats-aweb/injects/aweb.md +0 -47
  36. package/capabilities/oats-aweb/lib/binding-wire.mjs +0 -365
  37. package/capabilities/oats-aweb/lib/captured-execution.mjs +0 -91
  38. package/capabilities/oats-aweb/lib/captured-native.mjs +0 -91
  39. package/capabilities/oats-aweb/lib/grant-custody.mjs +0 -38
  40. package/capabilities/oats-aweb/lib/invocation-shape.mjs +0 -135
  41. package/capabilities/oats-aweb/lib/portable-binding.mjs +0 -146
  42. package/capabilities/oats-aweb/lib/session-readiness.mjs +0 -56
  43. package/capabilities/oats-aweb/lib/wake-receive.mjs +0 -56
  44. package/capabilities/oats-aweb/oats.json +0 -201
  45. package/capabilities/oats-aweb/skills/LICENSE +0 -21
  46. package/capabilities/oats-aweb/skills/VENDORED.md +0 -31
  47. package/capabilities/oats-aweb/skills/aweb-identity/SKILL.md +0 -201
  48. package/capabilities/oats-aweb/skills/aweb-messaging/SKILL.md +0 -161
  49. package/capabilities/oats-aweb/skills/aweb-messaging/references/messaging-scenarios.md +0 -61
  50. package/capabilities/oats-aweb/skills/aweb-team-membership/SKILL.md +0 -116
  51. package/capabilities/oats-aweb/skills/aweb-team-membership/references/team-membership-reference.md +0 -74
  52. package/capabilities/oats-aweb/skills/oats-aweb/SKILL.md +0 -286
  53. package/capabilities/oats-code-review/injects/reviewer.md +0 -26
  54. package/capabilities/oats-code-review/oats.json +0 -16
  55. package/capabilities/oats-code-review/skills/adversarial-review/SKILL.md +0 -66
  56. package/capabilities/oats-code-review/skills/review-dev-docs/SKILL.md +0 -30
  57. package/capabilities/oats-code-review/skills/security-review/SKILL.md +0 -56
  58. package/capabilities/oats-code-review/skills/simplification-review/SKILL.md +0 -34
  59. package/capabilities/oats-developer/injects/developer.md +0 -38
  60. package/capabilities/oats-developer/oats.json +0 -17
  61. package/capabilities/oats-developer/skills/execution-strategy/SKILL.md +0 -43
  62. package/capabilities/oats-developer/skills/maintain-dev-docs/SKILL.md +0 -47
  63. package/capabilities/oats-developer/skills/run-the-review-loop/SKILL.md +0 -65
  64. package/capabilities/oats-developer/skills/understand-the-spec/SKILL.md +0 -37
  65. package/capabilities/oats-developer/skills/worktrees/SKILL.md +0 -36
  66. package/capabilities/oats-engineering-expert/injects/expert.md +0 -37
  67. package/capabilities/oats-engineering-expert/oats.json +0 -17
  68. package/capabilities/oats-engineering-expert/skills/coordinate-developers/SKILL.md +0 -37
  69. package/capabilities/oats-engineering-expert/skills/coordinate-experts/SKILL.md +0 -52
  70. package/capabilities/oats-engineering-expert/skills/land-your-prs/SKILL.md +0 -50
  71. package/capabilities/oats-engineering-expert/skills/plan-and-spec/SKILL.md +0 -53
  72. package/capabilities/oats-engineering-expert/skills/verify-developer-work/SKILL.md +0 -49
  73. package/capabilities/oats-jira/bin/oats-jira.mjs +0 -40
  74. package/capabilities/oats-jira/injects/jira.md +0 -10
  75. package/capabilities/oats-jira/oats.json +0 -22
  76. package/capabilities/oats-jira/skills/jira-tasks/SKILL.md +0 -179
  77. package/capabilities/oats-linear/bin/oats-linear-hook.mjs +0 -34
  78. package/capabilities/oats-linear/bin/oats-linear.mjs +0 -344
  79. package/capabilities/oats-linear/injects/linear.md +0 -8
  80. package/capabilities/oats-linear/oats.json +0 -24
  81. package/capabilities/oats-linear/skills/linear-tasks/SKILL.md +0 -223
  82. package/capabilities/oats-okf/bin/oats-okf-binding.mjs +0 -14
  83. package/capabilities/oats-okf/bin/oats-okf.mjs +0 -213
  84. package/capabilities/oats-okf/injects/okf.md +0 -42
  85. package/capabilities/oats-okf/lib/binding-wire.mjs +0 -380
  86. package/capabilities/oats-okf/lib/captured-worker.mjs +0 -109
  87. package/capabilities/oats-okf/lib/config.mjs +0 -124
  88. package/capabilities/oats-okf/lib/consult.mjs +0 -518
  89. package/capabilities/oats-okf/lib/harvest-status.mjs +0 -88
  90. package/capabilities/oats-okf/lib/harvest-switch.mjs +0 -94
  91. package/capabilities/oats-okf/lib/inspection.mjs +0 -138
  92. package/capabilities/oats-okf/lib/invocation-context.mjs +0 -111
  93. package/capabilities/oats-okf/lib/invocation-shape.mjs +0 -135
  94. package/capabilities/oats-okf/lib/io.mjs +0 -118
  95. package/capabilities/oats-okf/lib/migration.mjs +0 -137
  96. package/capabilities/oats-okf/lib/okf-validate.mjs +0 -123
  97. package/capabilities/oats-okf/lib/portable-binding.mjs +0 -199
  98. package/capabilities/oats-okf/lib/source-contract.mjs +0 -46
  99. package/capabilities/oats-okf/lib/sources.mjs +0 -438
  100. package/capabilities/oats-okf/lib/stores.mjs +0 -473
  101. package/capabilities/oats-okf/lib/worker.mjs +0 -486
  102. package/capabilities/oats-okf/oats.json +0 -151
  103. package/capabilities/oats-okf/schemas/okf-base.schema.json +0 -46
  104. package/capabilities/oats-okf/schemas/okf-bindings.schema.json +0 -112
  105. package/capabilities/oats-okf/schemas/okf-portable-declaration.schema.json +0 -87
  106. package/capabilities/oats-okf/schemas/okf-portable-payload.schema.json +0 -113
  107. package/capabilities/oats-okf/schemas/okf-soul.schema.json +0 -37
  108. package/capabilities/oats-okf/skills/okf-consultation/SKILL.md +0 -144
  109. package/capabilities/oats-okf/skills/okf-consultation/references/consult.md +0 -86
  110. package/capabilities/oats-okf/skills/okf-instance-knowledge/SKILL.md +0 -104
  111. package/capabilities/oats-okf-harvest/bin/okf-harvest.mjs +0 -140
  112. package/capabilities/oats-okf-harvest/injects/harvester.md +0 -12
  113. package/capabilities/oats-okf-harvest/oats.json +0 -26
  114. package/capabilities/oats-okf-harvest/skills/knowledge-harvest/SKILL.md +0 -168
  115. package/capabilities/oats-okf-harvest/skills/knowledge-theory/SKILL.md +0 -192
  116. package/capabilities/oats-okf-harvest/skills/okf-authoring/SKILL.md +0 -151
  117. package/capabilities/oats-okf-harvest/skills/okf-authoring/scripts/okf-validate.mjs +0 -123
  118. package/capabilities/oats-okf-maintenance/bin/okf-maintenance.mjs +0 -170
  119. package/capabilities/oats-okf-maintenance/injects/maintainer.md +0 -12
  120. package/capabilities/oats-okf-maintenance/lib/provenance.mjs +0 -50
  121. package/capabilities/oats-okf-maintenance/oats.json +0 -21
  122. package/capabilities/oats-okf-maintenance/skills/knowledge-review/SKILL.md +0 -159
  123. package/capabilities/oats-okf-maintenance/skills/knowledge-theory/SKILL.md +0 -192
  124. package/capabilities/oats-okf-maintenance/skills/okf-authoring/SKILL.md +0 -151
  125. package/capabilities/oats-okf-maintenance/skills/okf-authoring/scripts/okf-validate.mjs +0 -123
  126. package/capabilities/oats-okf-maintenance/skills/okf-trigger-setup/SKILL.md +0 -134
  127. package/capabilities/oats-workspace-experts/injects/oats-experts.md +0 -26
  128. package/capabilities/oats-workspace-experts/oats.json +0 -9
@@ -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
- }
@@ -1,223 +0,0 @@
1
- ---
2
- name: linear-tasks
3
- description: >-
4
- Linear task tracking for OATS agent instances. Use when reading an agent's
5
- Linear work queue, opening or inspecting an issue, creating issues or
6
- sub-issues, claiming work with an agent label, posting progress/blocker/
7
- handoff comments, or moving work through Linear workflow states. Also use
8
- when asked about "my issue", "the project", "the board", a Linear issue key
9
- such as ENG-123, or shared task status. Uses JSON-first `oats linear` commands.
10
- ---
11
-
12
- # Agent task tracking in Linear
13
-
14
- Linear is your deployment's **tasks layer**: task status and outcomes live
15
- here. Conversation lives in the messaging layer; a message may nudge someone,
16
- but it never replaces the Linear update.
17
-
18
- ## Deployment target and authentication
19
-
20
- Get the target from the `Tasks: Linear` line in your `TASK.md` briefing:
21
-
22
- - **team** is required and uses Linear's issue-prefix key (for example `ENG`).
23
- - **project** is an optional deployment default. Do not invent one when unset.
24
- - **alias** is your exact OATS instance name; your label is `agent-<alias>`.
25
-
26
- team and project come from the tasks payload OATS merged for this instance —
27
- the soul's `soul.yaml` `tasks: { team, project }`, the deployment's
28
- `oats-local.yaml` `settings.oats.linear.*`, or a spawn's `--provider` — and
29
- `./instance.json` in your instance home records it as `providers["oats.linear"]`.
30
- If team is unset, stop and ask your human to set it there.
31
-
32
- Before the first operation, run:
33
-
34
- ```bash
35
- oats linear auth
36
- oats linear teams
37
- ```
38
-
39
- If `LINEAR_API_KEY` is missing or rejected, **stop and ask the human** to create
40
- or export a personal API key (Linear Settings → Security & access → API keys).
41
- Never ask for the key's value, print it, put it in a command argument, or store
42
- it in OATS config/files. Never attempt an interactive login.
43
-
44
- Commands emit JSON. An error is JSON on stderr with a non-zero exit code; act
45
- on that error rather than retrying variants blindly.
46
-
47
- ## Hierarchy
48
-
49
- - **Project** — an optional, human-owned outcome or initiative container. Do
50
- not create, rename, change status, or close projects.
51
- - **Issue** — the normal bounded work item assigned to an agent.
52
- - **Sub-issue** — an issue with a parent, used only when the parent genuinely
53
- decomposes into multiple independently verifiable pieces.
54
-
55
- Do not create placeholder parent issues for one child. Every issue belongs to
56
- a team; project membership is optional unless your briefing names a project.
57
-
58
- ## Project context and documentation
59
-
60
- Use project-level and issue-level records deliberately:
61
-
62
- - **Project overview**: intent, scope/non-goals, ownership, constraints, human
63
- gates, architecture, and success criteria.
64
- - **Project documents**: detailed designs, decisions, runbooks, and research.
65
- - **Issues/sub-issues**: bounded execution and acceptance criteria.
66
- - **Issue comments**: milestones, blockers, handoffs, verification, and links.
67
-
68
- The overview/documents explain the work; issues execute it. Link a governing
69
- project document from each affected issue rather than copying inconsistent
70
- versions. Keep task status in issues, not project prose or messaging.
71
-
72
- The current wrapper can discover project metadata but **cannot read or mutate
73
- project overview Markdown or Linear documents**:
74
-
75
- ```bash
76
- oats linear projects --team <TEAM>
77
- ```
78
-
79
- That output includes project IDs, names, slugs, status, and teams. Project
80
- creation, lifecycle/status, overview content, documents, and project updates
81
- remain human-owned in the Linear UI. If your task depends on unavailable
82
- project documentation, ask the human for its URL/content; never infer policy
83
- from an issue title.
84
-
85
- ## Identity and ownership
86
-
87
- - Keep the **human assignee unchanged**. A personal API key acts as its human;
88
- OATS agents are not Linear users.
89
- - Claim work with label `agent-<exact-instance-name>`. `--agent <alias>` creates
90
- this team-scoped label on first use and applies it.
91
- - New issue descriptions also receive `Agent: <alias>`. On existing issues,
92
- use the label and comments; do not rewrite a human's description merely to
93
- add the line.
94
- - Never delete issues, labels, or comments. Do not change cycle, priority,
95
- project, parent, or assignee unless explicitly directed.
96
-
97
- ## Read before writing
98
-
99
- ```bash
100
- # Your open queue (terminal states excluded by default)
101
- oats linear issue list --team <TEAM> --agent <alias>
102
-
103
- # Narrow to the deployment project when one is configured
104
- oats linear issue list --team <TEAM> --agent <alias> --project "<PROJECT>"
105
-
106
- # Read full task context before acting
107
- oats linear issue get <TEAM>-123
108
-
109
- # Discover this team's real workflow names; never guess them
110
- oats linear states --team <TEAM>
111
- ```
112
-
113
- `issue get` includes team, status/type, project, parent, assignee, labels,
114
- description, and URL. Read the parent too when working a sub-issue. Record the
115
- issue key in instance memory (`STATE.md`) if your knowledge layer provides it.
116
-
117
- ## Work an issue
118
-
119
- 1. Read the issue and parent/project context.
120
- 2. If not already claimed, apply your identity label:
121
-
122
- ```bash
123
- oats linear issue update <TEAM>-123 --agent <alias>
124
- ```
125
-
126
- 3. Move to the deployment's `started` workflow state (often `In Progress`),
127
- using the exact name returned by `oats linear states`:
128
-
129
- ```bash
130
- oats linear issue update <TEAM>-123 --state "In Progress"
131
- ```
132
-
133
- 4. Post only useful durable events, prefixed with your alias:
134
-
135
- ```bash
136
- oats linear issue comment <TEAM>-123 \
137
- --body "[<alias>] milestone: implemented parser; tests pass with node --test"
138
- oats linear issue comment <TEAM>-123 \
139
- --body "[<alias>] blocked: need API scope decision from @owner"
140
- oats linear issue comment <TEAM>-123 \
141
- --body "[<alias>] handoff → <next-alias>: branch agents/x, verify with npm test"
142
- ```
143
-
144
- 5. When implementation is review-ready, comment the outcome (branch/PR and
145
- verification), then move to the team's review state. Do **not** mark it
146
- completed:
147
-
148
- ```bash
149
- oats linear issue comment <TEAM>-123 \
150
- --body "[<alias>] review-ready: PR <url>; verified npm test"
151
- oats linear issue update <TEAM>-123 --state "In Review"
152
- ```
153
-
154
- Workflow names vary. Agents may use backlog/unstarted/started states. The
155
- wrapper refuses `completed`, `canceled`, and `duplicate` state types unless
156
- `--allow-terminal` is supplied; use that override only after explicit human
157
- authorization and mention that authorization in a comment.
158
-
159
- ## Create bounded work
160
-
161
- Use ≤12 words in the title. Describe requirements and acceptance checks, not a
162
- speculative implementation. For multiline Markdown, prefer a file so shell
163
- quoting cannot corrupt it.
164
-
165
- ```bash
166
- cat > /tmp/linear-description.md <<'EOF'
167
- Why this is needed.
168
-
169
- Acceptance:
170
- - [ ] Observable outcome one
171
- - [ ] Verification command or evidence
172
- EOF
173
-
174
- oats linear issue create --team <TEAM> --project "<PROJECT>" \
175
- --title "Bounded outcome" --description-file /tmp/linear-description.md \
176
- --agent <alias>
177
- ```
178
-
179
- Omit `--project` when the briefing has none. Create a sub-issue only for a real
180
- independent slice:
181
-
182
- ```bash
183
- oats linear issue create --team <TEAM> --parent <TEAM>-123 \
184
- --title "Independent child outcome" \
185
- --description-file /tmp/linear-description.md --agent <alias>
186
- ```
187
-
188
- `--project` sets project membership; `--parent` sets issue hierarchy. They are
189
- independent, so supply both when a sub-issue must explicitly carry the project:
190
-
191
- ```bash
192
- oats linear issue create --team <TEAM> --project "<PROJECT>" \
193
- --parent <TEAM>-123 --title "Independent child outcome" \
194
- --description-file /tmp/linear-description.md --agent <alias>
195
- ```
196
-
197
- Use an existing non-agent label only after discovery:
198
-
199
- ```bash
200
- oats linear labels --team <TEAM>
201
- oats linear issue create --team <TEAM> --title "Fix token refresh" \
202
- --label bug --agent <alias>
203
- ```
204
-
205
- ## Current command boundary
206
-
207
- Supported: discover teams/states/projects/labels; list/get/create/update/comment
208
- on issues; create sub-issues; claim work with agent labels.
209
-
210
- Not supported: create/update/close projects; read/edit project overviews;
211
- list/read/create/edit project documents; publish project updates; move an
212
- existing issue into/out of a project; reparent an existing issue; or create
213
- issue relations such as blocks/related. Those operations stay in the Linear
214
- UI with the human. **Do not invent GraphQL calls or command flags to bypass
215
- this boundary.**
216
-
217
- ## Validate every mutation
218
-
219
- Mutation output is the resulting issue/comment. Check its identifier, status,
220
- project/parent, and labels immediately. Then run `issue get` for
221
- correctness-critical changes. If a GraphQL permission or validation error
222
- persists, post no partial workaround: preserve the task state and escalate to
223
- the human with the exact error (never the key).
@@ -1,14 +0,0 @@
1
- #!/usr/bin/env node
2
- import { runBindingWire } from '../lib/binding-wire.mjs';
3
-
4
- const args=process.argv.slice(2);
5
- if(args.includes('--help') || args.includes('-h')) {
6
- process.stdout.write('oats okf provider binding phase (manifest-owned JSON stdin/stdout)\n');
7
- } else {
8
- const phase=args[0];
9
- if(args.length!==1) {
10
- await runBindingWire(phase,[],process.stdout);
11
- } else {
12
- await runBindingWire(phase);
13
- }
14
- }