@phamvuhoang/otto-core 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/branch.d.ts +20 -0
  2. package/dist/branch.d.ts.map +1 -1
  3. package/dist/branch.js +34 -1
  4. package/dist/branch.js.map +1 -1
  5. package/dist/cli-help.d.ts +42 -0
  6. package/dist/cli-help.d.ts.map +1 -1
  7. package/dist/cli-help.js +56 -2
  8. package/dist/cli-help.js.map +1 -1
  9. package/dist/gh-main.d.ts.map +1 -1
  10. package/dist/gh-main.js +1 -0
  11. package/dist/gh-main.js.map +1 -1
  12. package/dist/index.d.ts +1 -0
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +1 -0
  15. package/dist/index.js.map +1 -1
  16. package/dist/linear-api.d.ts +1 -0
  17. package/dist/linear-api.d.ts.map +1 -1
  18. package/dist/linear-api.js +6 -1
  19. package/dist/linear-api.js.map +1 -1
  20. package/dist/linear-cli.d.ts.map +1 -1
  21. package/dist/linear-cli.js +11 -3
  22. package/dist/linear-cli.js.map +1 -1
  23. package/dist/linear-main.d.ts.map +1 -1
  24. package/dist/linear-main.js +4 -1
  25. package/dist/linear-main.js.map +1 -1
  26. package/dist/run-bin.d.ts +10 -0
  27. package/dist/run-bin.d.ts.map +1 -1
  28. package/dist/run-bin.js +116 -0
  29. package/dist/run-bin.js.map +1 -1
  30. package/dist/task-key.d.ts +65 -0
  31. package/dist/task-key.d.ts.map +1 -0
  32. package/dist/task-key.js +112 -0
  33. package/dist/task-key.js.map +1 -0
  34. package/dist/watch.d.ts +26 -4
  35. package/dist/watch.d.ts.map +1 -1
  36. package/dist/watch.js +96 -41
  37. package/dist/watch.js.map +1 -1
  38. package/package.json +1 -1
  39. package/templates/apply-review.md +16 -3
  40. package/templates/ghafk-issue.md +4 -2
  41. package/templates/ghafk.md +2 -2
  42. package/templates/ghprompt.md +2 -0
  43. package/templates/linearprompt.md +3 -1
  44. package/templates/superpowers.md +23 -10
package/dist/watch.js CHANGED
@@ -4,13 +4,18 @@ import { createLinearClient, resolveLinearAuth, LinearApiError, } from "./linear
4
4
  import { runLoop } from "./loop.js";
5
5
  import { notifyComplete, notifyError } from "./notify.js";
6
6
  import { sleep } from "./pacing.js";
7
+ import { describeScope } from "./task-key.js";
7
8
  import { bold, dim, greenOut, boldOut, dimOut, SYM_OUT, USE_COLOR, } from "./stream-render.js";
8
- /** Poll open issues carrying `label`, via gh. Never throws. */
9
- export function pollOpenIssues(label, cwd) {
9
+ /**
10
+ * Poll open issues carrying `label`, via gh. Never throws. When `repo`
11
+ * (`owner/name`) is given the poll is confined to that repository (watch
12
+ * scoping) instead of the workspace's default repo.
13
+ */
14
+ export function pollOpenIssues(label, cwd, repo) {
10
15
  try {
11
- // execFileSync (no shell) so `label` is passed as a literal argv entry — a
12
- // value like `$(rm -rf ~)` can never be shell-evaluated. See SECURITY.md.
13
- // stderr is piped (not ignored) so a failure's message can be classified.
16
+ // execFileSync (no shell) so `label`/`repo` are passed as literal argv
17
+ // entries — a value like `$(rm -rf ~)` can never be shell-evaluated. See
18
+ // SECURITY.md. stderr is piped (not ignored) so failures can be classified.
14
19
  const out = execFileSync("gh", [
15
20
  "issue",
16
21
  "list",
@@ -18,6 +23,7 @@ export function pollOpenIssues(label, cwd) {
18
23
  "open",
19
24
  "--label",
20
25
  label,
26
+ ...(repo ? ["--repo", repo] : []),
21
27
  "--json",
22
28
  "number",
23
29
  ], { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
@@ -58,6 +64,7 @@ export async function pollLinearIssues(deps) {
58
64
  const issues = await client.listIssues({
59
65
  label: deps.label,
60
66
  team: deps.team,
67
+ project: deps.project,
61
68
  limit: deps.limit ?? 50,
62
69
  });
63
70
  return { ok: true, count: issues.length };
@@ -71,7 +78,28 @@ export async function pollLinearIssues(deps) {
71
78
  }
72
79
  const GH_PROVIDER = { name: "gh", authCmd: "gh auth login" };
73
80
  export async function runWatch(opts) {
74
- const { stages, iterations, workspaceDir, packageDir, watchIntervalSec, watchLabel, budgetUsd, cooldownMs, maxRetries, reviewLenses, notify = false, bin = "otto-ghafk", pollIssues = pollOpenIssues, provider = GH_PROVIDER, } = opts;
81
+ const { stages, iterations, workspaceDir, packageDir, watchIntervalSec, watchLabel, budgetUsd, cooldownMs, maxRetries, reviewLenses, notify = false, bin = "otto-ghafk", pollIssues = pollOpenIssues, provider = GH_PROVIDER, scope, scopes, } = opts;
82
+ // The scopes this daemon rotates through each cycle. Multi-target watch
83
+ // passes `scopes`; otherwise a single `scope` (or the workspace default,
84
+ // represented by a lone `undefined`).
85
+ const scopeList = scopes && scopes.length > 0 ? scopes : [scope];
86
+ // Derive the gh `--repo` filter from a github scope; other providers (Linear)
87
+ // carry their scope in the label/team the poller already honors.
88
+ const ghRepoOf = (s) => s?.provider === "github" && s.owner && s.repo
89
+ ? `${s.owner}/${s.repo}`
90
+ : undefined;
91
+ // The Linear project for a scope. Unlike github (which gets a poll `--repo`
92
+ // arg), the Linear poller reads OTTO_LINEAR_PROJECT from the env, so the
93
+ // daemon pins it before each poll/run to confine that scope.
94
+ const linearProjectOf = (s) => s?.provider === "linear" && s.project ? s.project : undefined;
95
+ // Human-readable scope prefix for a poll line (e.g. "github acme/web ").
96
+ const labelOf = (s) => s ? `${describeScope(s)} ` : "";
97
+ // The banner names every scope so a maintainer sees the exact watch surface.
98
+ const bannerScope = scopeList
99
+ .filter((s) => !!s)
100
+ .map(describeScope)
101
+ .join(", ");
102
+ const scopeLabel = bannerScope ? `${bannerScope} ` : "";
75
103
  const releaser = acquire({ reason: `${bin} watch` });
76
104
  let released = false;
77
105
  const releaseOnce = () => {
@@ -92,7 +120,7 @@ export async function runWatch(opts) {
92
120
  const onSigterm = onSig(143);
93
121
  process.on("SIGINT", onSigint);
94
122
  process.on("SIGTERM", onSigterm);
95
- process.stderr.write(`${USE_COLOR ? dim("watching") + " " + bold(`label:${watchLabel} every ${watchIntervalSec}s`) : `watching label:${watchLabel} every ${watchIntervalSec}s`}\n`);
123
+ process.stderr.write(`${USE_COLOR ? dim("watching") + " " + bold(`${scopeLabel}label:${watchLabel} every ${watchIntervalSec}s`) : `watching ${scopeLabel}label:${watchLabel} every ${watchIntervalSec}s`}\n`);
96
124
  let cumulativeCost = 0;
97
125
  // Track idle state so the "no open issues" line prints only on the
98
126
  // idle→busy→idle transition, not on every empty poll — otherwise an
@@ -107,42 +135,69 @@ export async function runWatch(opts) {
107
135
  notifyComplete(0, false);
108
136
  return;
109
137
  }
110
- const poll = await pollIssues(watchLabel, workspaceDir);
111
- if (!poll.ok) {
112
- // Broken poll say *why*, distinctly from an idle queue, and keep
113
- // polling (auth may get fixed / a transient failure may clear).
114
- wasIdle = false;
115
- const suffix = poll.detail ? ` — ${poll.detail}` : "";
116
- const why = poll.auth
117
- ? `${provider.name} not authenticated — run '${provider.authCmd}' (label ${watchLabel})${suffix}`
118
- : `${provider.name} issue poll failed (label ${watchLabel})${suffix}`;
119
- process.stderr.write(`${dim(why)}\n`);
120
- }
121
- else if (poll.count > 0) {
122
- wasIdle = false;
123
- process.stderr.write(`${dim(`${poll.count} open issue(s) labelled ${watchLabel} running loop`)}\n`);
124
- const remaining = budgetUsd != null ? budgetUsd - cumulativeCost : undefined;
125
- const outcome = await runLoop({
126
- stages,
127
- inputs: "",
128
- iterations,
129
- workspaceDir,
130
- packageDir,
131
- budgetUsd: remaining,
132
- cooldownMs,
133
- maxRetries,
134
- reviewLenses,
135
- noKeepAlive: true,
136
- signal: daemonAbort.signal,
137
- bin,
138
- cliVersion: opts.cliVersion,
139
- });
140
- cumulativeCost += outcome.costUsd;
141
- process.stderr.write(`${dim(`watch run done — cumulative $${cumulativeCost.toFixed(2)}`)}\n`);
138
+ // Poll every scope this cycle. Run one loop for the FIRST scope with
139
+ // work, then break back to the sleep+repoll — one loop at a time, no
140
+ // parallel mutation of the workspace. A failed poll for one scope is
141
+ // logged and skipped so it never blocks polling the others.
142
+ let ran = false;
143
+ let allIdle = true;
144
+ for (const s of scopeList) {
145
+ const sRepo = ghRepoOf(s);
146
+ const sProject = linearProjectOf(s);
147
+ const sLabel = labelOf(s);
148
+ // Pin the Linear project before polling so the poller (which reads it
149
+ // from the inherited env, not a poll arg) is confined to this scope;
150
+ // it also stays pinned for the loop run below. GitHub uses the sRepo
151
+ // poll arg instead and pins OTTO_GITHUB_REPO only on the run.
152
+ if (sProject)
153
+ process.env.OTTO_LINEAR_PROJECT = sProject;
154
+ const poll = await pollIssues(watchLabel, workspaceDir, sRepo);
155
+ if (!poll.ok) {
156
+ // Broken poll — say *why*, distinctly from an idle queue, and keep
157
+ // polling (auth may get fixed / a transient failure may clear).
158
+ allIdle = false;
159
+ wasIdle = false;
160
+ const suffix = poll.detail ? ` — ${poll.detail}` : "";
161
+ const why = poll.auth
162
+ ? `${sLabel}${provider.name} not authenticated — run '${provider.authCmd}' (label ${watchLabel})${suffix}`
163
+ : `${sLabel}${provider.name} issue poll failed (label ${watchLabel})${suffix}`;
164
+ process.stderr.write(`${dim(why)}\n`);
165
+ continue;
166
+ }
167
+ if (poll.count > 0) {
168
+ allIdle = false;
169
+ wasIdle = false;
170
+ process.stderr.write(`${dim(`${sLabel}${poll.count} open issue(s) labelled ${watchLabel} — running loop`)}\n`);
171
+ // Confine this loop's `gh` commands (render-time tags + the spawned
172
+ // agent) to the selected scope by pinning OTTO_GITHUB_REPO before the
173
+ // run; single-target left it set in run-bin, this covers multi-target.
174
+ if (sRepo)
175
+ process.env.OTTO_GITHUB_REPO = sRepo;
176
+ const remaining = budgetUsd != null ? budgetUsd - cumulativeCost : undefined;
177
+ const outcome = await runLoop({
178
+ stages,
179
+ inputs: "",
180
+ iterations,
181
+ workspaceDir,
182
+ packageDir,
183
+ budgetUsd: remaining,
184
+ cooldownMs,
185
+ maxRetries,
186
+ reviewLenses,
187
+ noKeepAlive: true,
188
+ signal: daemonAbort.signal,
189
+ bin,
190
+ cliVersion: opts.cliVersion,
191
+ });
192
+ cumulativeCost += outcome.costUsd;
193
+ process.stderr.write(`${dim(`${sLabel}watch run done — cumulative $${cumulativeCost.toFixed(2)}`)}\n`);
194
+ ran = true;
195
+ break;
196
+ }
142
197
  }
143
- else if (!wasIdle) {
198
+ if (!ran && allIdle && !wasIdle) {
144
199
  // First empty poll after activity — announce idle once, then stay quiet
145
- // until the queue becomes non-empty (or a poll fails) and idles again.
200
+ // until a queue becomes non-empty (or a poll fails) and idles again.
146
201
  wasIdle = true;
147
202
  process.stderr.write(`${dim(`no open issues labelled ${watchLabel} — idle, next poll in ${watchIntervalSec}s`)}\n`);
148
203
  }
package/dist/watch.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"watch.js","sourceRoot":"","sources":["../src/watch.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"watch.js","sourceRoot":"","sources":["../src/watch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,OAAO,EAAiB,MAAM,gBAAgB,CAAC;AACxD,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,cAAc,GAGf,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACpC,OAAO,EAAE,aAAa,EAAkB,MAAM,eAAe,CAAC;AAC9D,OAAO,EACL,IAAI,EACJ,GAAG,EACH,QAAQ,EACR,OAAO,EACP,MAAM,EACN,OAAO,EACP,SAAS,GACV,MAAM,oBAAoB,CAAC;AAa5B;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAC5B,KAAa,EACb,GAAW,EACX,IAAa;IAEb,IAAI,CAAC;QACH,uEAAuE;QACvE,yEAAyE;QACzE,4EAA4E;QAC5E,MAAM,GAAG,GAAG,YAAY,CACtB,IAAI,EACJ;YACE,OAAO;YACP,MAAM;YACN,SAAS;YACT,MAAM;YACN,SAAS;YACT,KAAK;YACL,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YACjC,QAAQ;YACR,QAAQ;SACT,EACD,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,CAC7D,CAAC;QACF,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAc,CAAC;QACzC,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAClE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,MAAM,GAAG,MAAM,CAClB,GAA4B,EAAE,MAAM,IAAK,GAAa,EAAE,OAAO,IAAI,EAAE,CACvE,CAAC;QACF,2EAA2E;QAC3E,0EAA0E;QAC1E,qEAAqE;QACrE,MAAM,IAAI,GAAG,2DAA2D,CAAC,IAAI,CAC3E,MAAM,CACP,CAAC;QACF,MAAM,MAAM,GAAG,MAAM;aAClB,KAAK,CAAC,IAAI,CAAC;aACX,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;aACpB,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;QACvB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IACrC,CAAC;AACH,CAAC;AAkBD;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,IAAoB;IACzD,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,WAAW,IAAI,CAAC,GAAG,EAAE,CAAC,iBAAiB,EAAE,CAAC,CAAC,EAAE,CAAC;IACjE,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO;YACL,EAAE,EAAE,KAAK;YACT,IAAI,EAAE,IAAI;YACV,MAAM,EAAE,kDAAkD;SAC3D,CAAC;IACJ,CAAC;IACD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,CAAC,IAAI,CAAC,UAAU;YAC7B,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,kBAAkB,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACjE,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC;YACrC,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,EAAE;SACxB,CAAC,CAAC;QACH,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;IAC5C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,cAAc,EAAE,CAAC;YAClC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,KAAK,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC;QACvE,CAAC;QACD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAG,GAAa,CAAC,OAAO,EAAE,CAAC;IACpE,CAAC;AACH,CAAC;AAQD,MAAM,WAAW,GAAkB,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,eAAe,EAAE,CAAC;AA6C5E,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,IAAqB;IAClD,MAAM,EACJ,MAAM,EACN,UAAU,EACV,YAAY,EACZ,UAAU,EACV,gBAAgB,EAChB,UAAU,EACV,SAAS,EACT,UAAU,EACV,UAAU,EACV,YAAY,EACZ,MAAM,GAAG,KAAK,EACd,GAAG,GAAG,YAAY,EAClB,UAAU,GAAG,cAAc,EAC3B,QAAQ,GAAG,WAAW,EACtB,KAAK,EACL,MAAM,GACP,GAAG,IAAI,CAAC;IAET,wEAAwE;IACxE,yEAAyE;IACzE,sCAAsC;IACtC,MAAM,SAAS,GACb,MAAM,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IACjD,8EAA8E;IAC9E,iEAAiE;IACjE,MAAM,QAAQ,GAAG,CAAC,CAAa,EAAsB,EAAE,CACrD,CAAC,EAAE,QAAQ,KAAK,QAAQ,IAAI,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,IAAI;QAC3C,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,IAAI,EAAE;QACxB,CAAC,CAAC,SAAS,CAAC;IAChB,4EAA4E;IAC5E,yEAAyE;IACzE,6DAA6D;IAC7D,MAAM,eAAe,GAAG,CAAC,CAAa,EAAsB,EAAE,CAC5D,CAAC,EAAE,QAAQ,KAAK,QAAQ,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;IAChE,yEAAyE;IACzE,MAAM,OAAO,GAAG,CAAC,CAAa,EAAU,EAAE,CACxC,CAAC,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAClC,6EAA6E;IAC7E,MAAM,WAAW,GAAG,SAAS;SAC1B,MAAM,CAAC,CAAC,CAAC,EAAkB,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;SAClC,GAAG,CAAC,aAAa,CAAC;SAClB,IAAI,CAAC,IAAI,CAAC,CAAC;IACd,MAAM,UAAU,GAAG,WAAW,CAAC,CAAC,CAAC,GAAG,WAAW,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAExD,MAAM,QAAQ,GAAa,OAAO,CAAC,EAAE,MAAM,EAAE,GAAG,GAAG,QAAQ,EAAE,CAAC,CAAC;IAC/D,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,MAAM,WAAW,GAAG,GAAS,EAAE;QAC7B,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,QAAQ,GAAG,IAAI,CAAC;YAChB,QAAQ,CAAC,OAAO,EAAE,CAAC;QACrB,CAAC;IACH,CAAC,CAAC;IACF,MAAM,WAAW,GAAG,IAAI,eAAe,EAAE,CAAC;IAE1C,MAAM,KAAK,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,GAAS,EAAE;QACzC,WAAW,CAAC,KAAK,EAAE,CAAC;QACpB,IAAI,MAAM;YAAE,WAAW,CAAC,wBAAwB,CAAC,CAAC;QAClD,WAAW,EAAE,CAAC;QACd,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACrB,CAAC,CAAC;IACF,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC;IAC5B,MAAM,SAAS,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC;IAC7B,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAC/B,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;IAEjC,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,GAAG,GAAG,IAAI,CAAC,GAAG,UAAU,SAAS,UAAU,UAAU,gBAAgB,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,UAAU,SAAS,UAAU,UAAU,gBAAgB,GAAG,IAAI,CACxL,CAAC;IAEF,IAAI,cAAc,GAAG,CAAC,CAAC;IACvB,mEAAmE;IACnE,oEAAoE;IACpE,0EAA0E;IAC1E,oEAAoE;IACpE,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,IAAI,CAAC;QACH,SAAS,CAAC;YACR,IAAI,SAAS,IAAI,IAAI,IAAI,cAAc,IAAI,SAAS,EAAE,CAAC;gBACrD,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,GAAG,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,OAAO,CAAC,sBAAsB,CAAC,GAAG,MAAM,CAAC,KAAK,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,aAAa,CAAC,IAAI,CACpJ,CAAC;gBACF,IAAI,MAAM;oBAAE,cAAc,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;gBACrC,OAAO;YACT,CAAC;YACD,qEAAqE;YACrE,qEAAqE;YACrE,qEAAqE;YACrE,4DAA4D;YAC5D,IAAI,GAAG,GAAG,KAAK,CAAC;YAChB,IAAI,OAAO,GAAG,IAAI,CAAC;YACnB,KAAK,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;gBAC1B,MAAM,KAAK,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;gBAC1B,MAAM,QAAQ,GAAG,eAAe,CAAC,CAAC,CAAC,CAAC;gBACpC,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;gBAC1B,sEAAsE;gBACtE,qEAAqE;gBACrE,qEAAqE;gBACrE,8DAA8D;gBAC9D,IAAI,QAAQ;oBAAE,OAAO,CAAC,GAAG,CAAC,mBAAmB,GAAG,QAAQ,CAAC;gBACzD,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,UAAU,EAAE,YAAY,EAAE,KAAK,CAAC,CAAC;gBAC/D,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;oBACb,mEAAmE;oBACnE,gEAAgE;oBAChE,OAAO,GAAG,KAAK,CAAC;oBAChB,OAAO,GAAG,KAAK,CAAC;oBAChB,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;oBACtD,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI;wBACnB,CAAC,CAAC,GAAG,MAAM,GAAG,QAAQ,CAAC,IAAI,6BAA6B,QAAQ,CAAC,OAAO,YAAY,UAAU,IAAI,MAAM,EAAE;wBAC1G,CAAC,CAAC,GAAG,MAAM,GAAG,QAAQ,CAAC,IAAI,6BAA6B,UAAU,IAAI,MAAM,EAAE,CAAC;oBACjF,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;oBACtC,SAAS;gBACX,CAAC;gBACD,IAAI,IAAI,CAAC,KAAK,GAAG,CAAC,EAAE,CAAC;oBACnB,OAAO,GAAG,KAAK,CAAC;oBAChB,OAAO,GAAG,KAAK,CAAC;oBAChB,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,GAAG,GAAG,CAAC,GAAG,MAAM,GAAG,IAAI,CAAC,KAAK,2BAA2B,UAAU,iBAAiB,CAAC,IAAI,CACzF,CAAC;oBACF,oEAAoE;oBACpE,sEAAsE;oBACtE,uEAAuE;oBACvE,IAAI,KAAK;wBAAE,OAAO,CAAC,GAAG,CAAC,gBAAgB,GAAG,KAAK,CAAC;oBAChD,MAAM,SAAS,GACb,SAAS,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,GAAG,cAAc,CAAC,CAAC,CAAC,SAAS,CAAC;oBAC7D,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC;wBAC5B,MAAM;wBACN,MAAM,EAAE,EAAE;wBACV,UAAU;wBACV,YAAY;wBACZ,UAAU;wBACV,SAAS,EAAE,SAAS;wBACpB,UAAU;wBACV,UAAU;wBACV,YAAY;wBACZ,WAAW,EAAE,IAAI;wBACjB,MAAM,EAAE,WAAW,CAAC,MAAM;wBAC1B,GAAG;wBACH,UAAU,EAAE,IAAI,CAAC,UAAU;qBAC5B,CAAC,CAAC;oBACH,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC;oBAClC,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,GAAG,GAAG,CAAC,GAAG,MAAM,gCAAgC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CACjF,CAAC;oBACF,GAAG,GAAG,IAAI,CAAC;oBACX,MAAM;gBACR,CAAC;YACH,CAAC;YACD,IAAI,CAAC,GAAG,IAAI,OAAO,IAAI,CAAC,OAAO,EAAE,CAAC;gBAChC,wEAAwE;gBACxE,qEAAqE;gBACrE,OAAO,GAAG,IAAI,CAAC;gBACf,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,GAAG,GAAG,CAAC,2BAA2B,UAAU,yBAAyB,gBAAgB,GAAG,CAAC,IAAI,CAC9F,CAAC;YACJ,CAAC;YACD,MAAM,KAAK,CAAC,gBAAgB,GAAG,IAAI,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC;YAAS,CAAC;QACT,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QAChC,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;QAClC,WAAW,EAAE,CAAC;IAChB,CAAC;AACH,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phamvuhoang/otto-core",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Claude Code AFK orchestration: iteration loop, native-sandbox runner, template renderer.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -30,6 +30,16 @@
30
30
 
31
31
  When every actionable finding has been addressed (fixed, or already fixed in git, or recorded as a follow-up), produce the completion report (see COMPLETION REPORT below), then output `<promise>NO MORE TASKS</promise>`.
32
32
 
33
+ # TASK KEY
34
+
35
+ Per-task artifacts (spec, plan, follow-ups) live together under
36
+ `.otto/tasks/<task-key>/`, so everything Otto knows about a task is in one place.
37
+ Resolve the task key for THIS run from the current git branch: run
38
+ `git branch --show-current` and take the **final path segment** (the part after the
39
+ last `/`) — e.g. `otto/issue-21` → `issue-21`, `feat/gh-acme-web-14` →
40
+ `gh-acme-web-14`. If the branch name has no `/`, use it whole. Use this key in the
41
+ follow-ups path below.
42
+
33
43
  # TRIAGE
34
44
 
35
45
  Classify each finding (judge from the review's own language — severity labels, "follow-up", "operational", "cosmetic", "low risk"):
@@ -56,7 +66,9 @@ Pick the highest-value actionable finding not yet addressed. Implement the fix.
56
66
 
57
67
  # RECORD FOLLOW-UPS
58
68
 
59
- For each Deferred / follow-up finding, append a terse entry to `./.otto/review-followups.md` (create it lazily). Use a dated `##` heading for this review, then one bullet per finding with its severity and why it is deferred. This file is git-tracked commit it WITH the related fix (do not make a separate commit just for it).
69
+ For each Deferred / follow-up finding, append a terse entry to the **task-local** follow-ups file `./.otto/tasks/<task-key>/followups.md` (create it and its parent dir lazily), using the task key resolved above. Use a dated `##` heading for this review, then one bullet per finding with its severity and why it is deferred. Keeping follow-ups beside the task's spec/plan means everything Otto knows about a task is in one place, while staying globally summarizable by globbing `.otto/tasks/*/followups.md`.
70
+
71
+ Read the task-local file first (it may already hold this task's prior deferrals). The legacy global `./.otto/review-followups.md` is still READ as a fallback for older runs (see `<existing-followups>`) for one release, but do NOT append new entries there. This file is git-tracked — commit it WITH the related fix (do not make a separate commit just for it).
60
72
 
61
73
  # COMMIT
62
74
 
@@ -76,8 +88,9 @@ contract below onto this round:
76
88
  - **What Changed / Evidence:** the findings you CONFIRMED and fixed, each with
77
89
  its `fix(review):` commit SHA and the review section it came from; the
78
90
  feedback loops you ran (tests / typecheck) and their result.
79
- - **Gaps And Follow-Ups:** findings you DEFERRED to `./.otto/review-followups.md`
80
- (with why), and any REJECTED / won't-fix findings with their reason. Verdict
91
+ - **Gaps And Follow-Ups:** findings you DEFERRED to
92
+ `./.otto/tasks/<task-key>/followups.md` (with why), and any REJECTED / won't-fix
93
+ findings with their reason. Verdict
81
94
  defaults to **Needs human review** when any actionable finding was left
82
95
  unfixed.
83
96
 
@@ -12,12 +12,14 @@
12
12
 
13
13
  <issue>
14
14
 
15
- !?`gh issue view "$OTTO_ISSUE" --json number,title,state|||Issue not found`
15
+ !?`gh issue view "$OTTO_ISSUE" ${OTTO_GITHUB_REPO:+--repo "$OTTO_GITHUB_REPO"} --json number,title,state|||Issue not found`
16
16
 
17
- Full issue body + comments spilled to: @spill?:issue.json=`gh issue view "$OTTO_ISSUE" --json number,title,body,comments,state|||[]`
17
+ Full issue body + comments spilled to: @spill?:issue.json=`gh issue view "$OTTO_ISSUE" ${OTTO_GITHUB_REPO:+--repo "$OTTO_GITHUB_REPO"} --json number,title,body,comments,state|||[]`
18
18
 
19
19
  `Read` that file to get the full body and comments before acting on the issue.
20
20
 
21
+ If `$OTTO_GITHUB_REPO` is set (run scoped with `--repo owner/name`), pass `--repo "$OTTO_GITHUB_REPO"` to every `gh` command you run yourself (issue comment, pr create) so completion targets that repo. If unset, `gh` uses the workspace's own repo.
22
+
21
23
  </issue>
22
24
 
23
25
  # THE TASK
@@ -14,13 +14,13 @@
14
14
 
15
15
  <issues-summary>
16
16
 
17
- `gh issue list --state open --limit 50 --json number,title,labels`
17
+ `gh issue list ${OTTO_GITHUB_REPO:+--repo "$OTTO_GITHUB_REPO"} --state open --limit 50 --json number,title,labels`
18
18
 
19
19
  </issues-summary>
20
20
 
21
21
  <issues-full-file>
22
22
 
23
- Full issue bodies + comments spilled to: @spill?:issues.json=`gh issue list --state open --limit 50 --json number,title,body,labels,comments|||[]`
23
+ Full issue bodies + comments spilled to: @spill?:issues.json=`gh issue list ${OTTO_GITHUB_REPO:+--repo "$OTTO_GITHUB_REPO"} --state open --limit 50 --json number,title,body,labels,comments|||[]`
24
24
 
25
25
  Read that file with `Read` (use `offset`/`limit` if it is large) to get bodies and comments before picking a task. The `<issues-summary>` block above is the lean index for triage.
26
26
 
@@ -7,6 +7,8 @@ Two views of open GitHub issues are provided at the start of context:
7
7
 
8
8
  You will work on the AFK issues only, not the HITL ones. Label filtering uses the `labels` field in the summary.
9
9
 
10
+ **Repo scope.** If the `$OTTO_GITHUB_REPO` environment variable is set (the run was scoped with `--repo owner/name`), the issue list above is already confined to that repo — work only on those issues, and pass `--repo "$OTTO_GITHUB_REPO"` to every `gh` command you run yourself (e.g. `gh issue comment`, `gh pr create`) so completion targets the same repo. If it is unset, `gh` uses the workspace's own repo as before.
11
+
10
12
  You've also been passed a file containing the last few commits. Review these to understand what work has been done.
11
13
 
12
14
  If all AFK tasks are complete, output <promise>NO MORE TASKS</promise>.
@@ -9,7 +9,9 @@ Two views of open Linear issues are provided at the start of context:
9
9
  large) once you have picked an issue you want to act on.
10
10
 
11
11
  Issue selection is already filtered to open Linear issues carrying the `otto`
12
- label (override via `OTTO_LINEAR_LABEL`, narrow to a team via `OTTO_LINEAR_TEAM`).
12
+ label (override via `OTTO_LINEAR_LABEL`, narrow to a team via `OTTO_LINEAR_TEAM`,
13
+ and narrow to a project via `OTTO_LINEAR_PROJECT`). Work only on the issues the
14
+ list shows — they are already confined to the configured team/project scope.
13
15
 
14
16
  You've also been passed a file containing the last few commits. Review these to
15
17
  understand what work has been done.
@@ -10,29 +10,41 @@ If the `superpowers:brainstorming`, `superpowers:writing-plans`, and
10
10
  fuller guidance. If they are not installed, follow the inline protocol below —
11
11
  it is self-contained.
12
12
 
13
- ## 0. Resolve the task key
13
+ ## 0. Resolve the task key and artifact paths
14
14
 
15
15
  - GitHub issue run → task-key = `issue-<issue number>`.
16
16
  - Plan/PRD run → task-key = a stable slug from the primary plan-file basename
17
17
  (e.g. `docs/plans/foo.md` → `foo`). If inputs are inline text, use a short
18
18
  kebab-case of the task title.
19
19
 
20
- Spec path: `.otto/specs/<task-key>-design.md`
21
- Plan path: `.otto/plans/<task-key>.md`
20
+ Per-task artifacts live together under one task directory, so everything Otto
21
+ knows about a task is in one place:
22
+
23
+ - Task dir: `.otto/tasks/<task-key>/`
24
+ - Spec path: `.otto/tasks/<task-key>/spec.md`
25
+ - Plan path: `.otto/tasks/<task-key>/plan.md`
26
+
27
+ Always **WRITE** the new task-grouped layout. The legacy flat layout
28
+ (`.otto/specs/<task-key>-design.md` + `.otto/plans/<task-key>.md`) is still
29
+ **READ** as a fallback (see the CLARITY GATE), so a task created before this
30
+ layout continues without re-brainstorming.
22
31
 
23
32
  ## 1. CLARITY GATE
24
33
 
25
- Check whether `.otto/specs/<task-key>-design.md` already exists.
34
+ Check whether the spec already exists — look under the new task dir
35
+ `.otto/tasks/<task-key>/spec.md` first, then fall back to the legacy flat path
36
+ `.otto/specs/<task-key>-design.md`.
26
37
 
27
- - **Spec exists** → skip brainstorming. Read the spec and
28
- `.otto/plans/<task-key>.md`, pick the next unchecked task, and go to
38
+ - **Spec exists** → skip brainstorming. Read the spec and its matching plan
39
+ (`.otto/tasks/<task-key>/plan.md`, or legacy `.otto/plans/<task-key>.md`),
40
+ pick the next unchecked task, and go to
29
41
  TDD IMPLEMENT (section 3). If every plan task is checked AND the feedback
30
42
  loops pass, output `<promise>NO MORE TASKS</promise>`.
31
43
  - **No spec** → judge the input's clarity. It is UNCLEAR if any of: no
32
44
  plan/PRD provided; a vague directive ("make it better"); missing acceptance
33
45
  criteria; multiple plausible interpretations; internal contradictions.
34
46
  - Clear enough → go straight to TDD IMPLEMENT (section 3). Optionally jot a
35
- short plan to `.otto/plans/<task-key>.md` first.
47
+ short plan to `.otto/tasks/<task-key>/plan.md` first.
36
48
  - Unclear → AUTONOMOUS BRAINSTORM (section 2).
37
49
 
38
50
  ## 2. AUTONOMOUS BRAINSTORM (no human in the loop)
@@ -43,10 +55,10 @@ Play both sides of a brainstorming session:
43
55
  scope, constraints, success criteria, edge cases).
44
56
  2. Answer each one yourself with the most reasonable default given the repo's
45
57
  existing patterns. Prefer the simplest viable option (YAGNI).
46
- 3. Write `.otto/specs/<task-key>-design.md` containing: Problem, Approach, an
58
+ 3. Write `.otto/tasks/<task-key>/spec.md` containing: Problem, Approach, an
47
59
  **Assumptions** section listing each `question → chosen answer → rationale`,
48
60
  and Testing notes.
49
- 4. Write `.otto/plans/<task-key>.md` as an ordered checklist of bite-sized,
61
+ 4. Write `.otto/tasks/<task-key>/plan.md` as an ordered checklist of bite-sized,
50
62
  testable tasks (one `- [ ]` per task).
51
63
  5. Do NOT wait for approval — the written assumptions are the record.
52
64
 
@@ -63,7 +75,8 @@ Implement exactly one task, test-first:
63
75
  2. Run it; confirm it fails for the right reason.
64
76
  3. Write the minimal code to make it pass.
65
77
  4. Run the feedback loops described below until green.
66
- 5. Update `.otto/plans/<task-key>.md`: check off the task. If a new durable,
78
+ 5. Update the plan you read (`.otto/tasks/<task-key>/plan.md`, or its legacy
79
+ fallback `.otto/plans/<task-key>.md`): check off the task. If a new durable,
67
80
  reusable learning emerged, append it to `.otto/LEARNINGS.md`.
68
81
 
69
82
  Commit the code, the updated spec/plan, and LEARNINGS together in the single