@phamvuhoang/otto-core 0.5.0 → 0.7.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.
- package/dist/branch.d.ts +20 -0
- package/dist/branch.d.ts.map +1 -1
- package/dist/branch.js +34 -1
- package/dist/branch.js.map +1 -1
- package/dist/cli-help.d.ts +46 -0
- package/dist/cli-help.d.ts.map +1 -1
- package/dist/cli-help.js +76 -2
- package/dist/cli-help.js.map +1 -1
- package/dist/gh-main.d.ts.map +1 -1
- package/dist/gh-main.js +1 -0
- package/dist/gh-main.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/linear-api.d.ts +1 -0
- package/dist/linear-api.d.ts.map +1 -1
- package/dist/linear-api.js +6 -1
- package/dist/linear-api.js.map +1 -1
- package/dist/linear-cli.d.ts.map +1 -1
- package/dist/linear-cli.js +11 -3
- package/dist/linear-cli.js.map +1 -1
- package/dist/linear-main.d.ts.map +1 -1
- package/dist/linear-main.js +4 -1
- package/dist/linear-main.js.map +1 -1
- package/dist/loop.d.ts +4 -0
- package/dist/loop.d.ts.map +1 -1
- package/dist/loop.js +39 -8
- package/dist/loop.js.map +1 -1
- package/dist/panel.d.ts +2 -0
- package/dist/panel.d.ts.map +1 -1
- package/dist/panel.js +4 -1
- package/dist/panel.js.map +1 -1
- package/dist/prompt-reduction.d.ts +17 -0
- package/dist/prompt-reduction.d.ts.map +1 -0
- package/dist/prompt-reduction.js +25 -0
- package/dist/prompt-reduction.js.map +1 -0
- package/dist/run-bin.d.ts +10 -0
- package/dist/run-bin.d.ts.map +1 -1
- package/dist/run-bin.js +140 -6
- package/dist/run-bin.js.map +1 -1
- package/dist/runner.d.ts +2 -0
- package/dist/runner.d.ts.map +1 -1
- package/dist/runner.js +3 -0
- package/dist/runner.js.map +1 -1
- package/dist/stage-exec.d.ts +2 -0
- package/dist/stage-exec.d.ts.map +1 -1
- package/dist/stage-exec.js +9 -2
- package/dist/stage-exec.js.map +1 -1
- package/dist/task-key.d.ts +65 -0
- package/dist/task-key.d.ts.map +1 -0
- package/dist/task-key.js +112 -0
- package/dist/task-key.js.map +1 -0
- package/dist/tokens.d.ts +15 -0
- package/dist/tokens.d.ts.map +1 -0
- package/dist/tokens.js +62 -0
- package/dist/tokens.js.map +1 -0
- package/dist/watch.d.ts +28 -4
- package/dist/watch.d.ts.map +1 -1
- package/dist/watch.js +98 -43
- package/dist/watch.js.map +1 -1
- package/package.json +1 -1
- package/templates/apply-review.md +16 -3
- package/templates/ghafk-issue.md +4 -2
- package/templates/ghafk.md +2 -2
- package/templates/ghprompt.md +2 -0
- package/templates/linearprompt.md +3 -1
- package/templates/superpowers.md +23 -10
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../src/tokens.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,SAAS,GAAG,QAAQ,CAAC;AAErD,MAAM,MAAM,UAAU,GAAG;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,wBAAwB,EAAE,MAAM,CAAC;IACjC,oBAAoB,EAAE,MAAM,CAAC;CAC9B,CAAC;AAEF,wBAAgB,eAAe,IAAI,UAAU,CAO5C;AAcD,kFAAkF;AAClF,wBAAgB,eAAe,CAAC,EAAE,EAAE,OAAO,GAAG,UAAU,CAQvD;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,UAAU,GAAG,UAAU,CAQtE;AAED,wBAAgB,eAAe,CAAC,CAAC,EAAE,UAAU,GAAG,MAAM,CAOrD;AAID,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,UAAU,GAAG,MAAM,CAQtD;AAED,wBAAgB,cAAc,CAC5B,GAAG,EAAE,MAAM,GAAG,SAAS,EACvB,MAAM,SAAiB,GACtB,SAAS,CASX"}
|
package/dist/tokens.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export function emptyTokenUsage() {
|
|
2
|
+
return {
|
|
3
|
+
inputTokens: 0,
|
|
4
|
+
outputTokens: 0,
|
|
5
|
+
cacheCreationInputTokens: 0,
|
|
6
|
+
cacheReadInputTokens: 0,
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
function usageObject(ev) {
|
|
10
|
+
const e = (ev ?? {});
|
|
11
|
+
return (e.usage ?? {});
|
|
12
|
+
}
|
|
13
|
+
function usageNumber(value) {
|
|
14
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
|
|
15
|
+
return 0;
|
|
16
|
+
}
|
|
17
|
+
return Math.floor(value);
|
|
18
|
+
}
|
|
19
|
+
/** Parse Claude stream-json result-event token usage. Missing fields are zero. */
|
|
20
|
+
export function parseTokenUsage(ev) {
|
|
21
|
+
const u = usageObject(ev);
|
|
22
|
+
return {
|
|
23
|
+
inputTokens: usageNumber(u.input_tokens),
|
|
24
|
+
outputTokens: usageNumber(u.output_tokens),
|
|
25
|
+
cacheCreationInputTokens: usageNumber(u.cache_creation_input_tokens),
|
|
26
|
+
cacheReadInputTokens: usageNumber(u.cache_read_input_tokens),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export function addTokenUsage(a, b) {
|
|
30
|
+
return {
|
|
31
|
+
inputTokens: a.inputTokens + b.inputTokens,
|
|
32
|
+
outputTokens: a.outputTokens + b.outputTokens,
|
|
33
|
+
cacheCreationInputTokens: a.cacheCreationInputTokens + b.cacheCreationInputTokens,
|
|
34
|
+
cacheReadInputTokens: a.cacheReadInputTokens + b.cacheReadInputTokens,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
export function tokenUsageTotal(u) {
|
|
38
|
+
return (u.inputTokens +
|
|
39
|
+
u.outputTokens +
|
|
40
|
+
u.cacheCreationInputTokens +
|
|
41
|
+
u.cacheReadInputTokens);
|
|
42
|
+
}
|
|
43
|
+
const fmt = new Intl.NumberFormat("en-US");
|
|
44
|
+
export function formatTokenUsage(u) {
|
|
45
|
+
return [
|
|
46
|
+
`in ${fmt.format(u.inputTokens)}`,
|
|
47
|
+
`out ${fmt.format(u.outputTokens)}`,
|
|
48
|
+
`cache create ${fmt.format(u.cacheCreationInputTokens)}`,
|
|
49
|
+
`cache read ${fmt.format(u.cacheReadInputTokens)}`,
|
|
50
|
+
`total ${fmt.format(tokenUsageTotal(u))}`,
|
|
51
|
+
].join(" | ");
|
|
52
|
+
}
|
|
53
|
+
export function parseTokenMode(raw, source = "--token-mode") {
|
|
54
|
+
const trimmed = raw?.trim();
|
|
55
|
+
if (!trimmed)
|
|
56
|
+
return "off";
|
|
57
|
+
if (trimmed === "off" || trimmed === "measure" || trimmed === "reduce") {
|
|
58
|
+
return trimmed;
|
|
59
|
+
}
|
|
60
|
+
throw new Error(`${source} must be one of off|measure|reduce, got: ${JSON.stringify(raw)}`);
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=tokens.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokens.js","sourceRoot":"","sources":["../src/tokens.ts"],"names":[],"mappings":"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"}
|
package/dist/watch.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { type LinearAuth, type LinearClient } from "./linear-api.js";
|
|
2
|
+
import { type WorkScope } from "./task-key.js";
|
|
2
3
|
import type { Stage } from "./stages.js";
|
|
4
|
+
import type { TokenMode } from "./tokens.js";
|
|
3
5
|
/**
|
|
4
6
|
* Outcome of one issue poll. Distinguishes a real idle queue (`ok` with
|
|
5
7
|
* `count: 0`) from a broken poll (`!ok`) so the daemon can say *why* it is not
|
|
@@ -14,8 +16,12 @@ export type PollResult = {
|
|
|
14
16
|
auth: boolean;
|
|
15
17
|
detail: string;
|
|
16
18
|
};
|
|
17
|
-
/**
|
|
18
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Poll open issues carrying `label`, via gh. Never throws. When `repo`
|
|
21
|
+
* (`owner/name`) is given the poll is confined to that repository (watch
|
|
22
|
+
* scoping) instead of the workspace's default repo.
|
|
23
|
+
*/
|
|
24
|
+
export declare function pollOpenIssues(label: string, cwd: string, repo?: string): PollResult;
|
|
19
25
|
/**
|
|
20
26
|
* Injectable deps for {@link pollLinearIssues}, so poll classification is
|
|
21
27
|
* unit-testable without a real credential file or network. Auth is re-resolved
|
|
@@ -24,6 +30,7 @@ export declare function pollOpenIssues(label: string, cwd: string): PollResult;
|
|
|
24
30
|
export type LinearPollDeps = {
|
|
25
31
|
label: string;
|
|
26
32
|
team?: string;
|
|
33
|
+
project?: string;
|
|
27
34
|
limit?: number;
|
|
28
35
|
/** Resolve the Linear credential; defaults to env/file precedence. */
|
|
29
36
|
resolveAuth?: () => LinearAuth | null;
|
|
@@ -54,6 +61,7 @@ export type RunWatchOptions = {
|
|
|
54
61
|
watchLabel: string;
|
|
55
62
|
budgetUsd?: number;
|
|
56
63
|
cooldownMs?: number;
|
|
64
|
+
tokenMode?: TokenMode;
|
|
57
65
|
maxRetries?: number;
|
|
58
66
|
reviewLenses?: string[];
|
|
59
67
|
notify?: boolean;
|
|
@@ -61,11 +69,27 @@ export type RunWatchOptions = {
|
|
|
61
69
|
cliVersion?: string;
|
|
62
70
|
/**
|
|
63
71
|
* Poller for open labelled issues. Defaults to the gh poller; otto-linear-afk
|
|
64
|
-
* passes a Linear poller. May be async. Injectable for tests too.
|
|
72
|
+
* passes a Linear poller. May be async. Injectable for tests too. `repo` is
|
|
73
|
+
* the resolved GitHub scope (`owner/name`) or undefined; the Linear poller
|
|
74
|
+
* ignores it.
|
|
65
75
|
*/
|
|
66
|
-
pollIssues?: (label: string, cwd: string) => PollResult | Promise<PollResult>;
|
|
76
|
+
pollIssues?: (label: string, cwd: string, repo?: string) => PollResult | Promise<PollResult>;
|
|
67
77
|
/** Provider-specific poll/auth messaging; defaults to gh. */
|
|
68
78
|
provider?: WatchProvider;
|
|
79
|
+
/**
|
|
80
|
+
* Resolved work scope this daemon is confined to (e.g. github owner/name).
|
|
81
|
+
* Named in the poll lines and used to derive the gh `--repo` poll filter so
|
|
82
|
+
* watch never picks up issues from outside the scope.
|
|
83
|
+
*/
|
|
84
|
+
scope?: WorkScope;
|
|
85
|
+
/**
|
|
86
|
+
* Multi-target watch: several GitHub scopes the daemon rotates through. When
|
|
87
|
+
* set, each cycle polls every scope, runs one loop for the first scope with
|
|
88
|
+
* work (confining `OTTO_GITHUB_REPO` to it), then returns to polling all. A
|
|
89
|
+
* failed poll for one scope never blocks the others. Falls back to `scope`
|
|
90
|
+
* (or the workspace default) when omitted.
|
|
91
|
+
*/
|
|
92
|
+
scopes?: WorkScope[];
|
|
69
93
|
};
|
|
70
94
|
export declare function runWatch(opts: RunWatchOptions): Promise<void>;
|
|
71
95
|
//# sourceMappingURL=watch.d.ts.map
|
package/dist/watch.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"watch.d.ts","sourceRoot":"","sources":["../src/watch.ts"],"names":[],"mappings":"AAEA,OAAO,EAIL,KAAK,UAAU,EACf,KAAK,YAAY,EAClB,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"watch.d.ts","sourceRoot":"","sources":["../src/watch.ts"],"names":[],"mappings":"AAEA,OAAO,EAIL,KAAK,UAAU,EACf,KAAK,YAAY,EAClB,MAAM,iBAAiB,CAAC;AAIzB,OAAO,EAAiB,KAAK,SAAS,EAAE,MAAM,eAAe,CAAC;AAU9D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C;;;;;GAKG;AACH,MAAM,MAAM,UAAU,GAClB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC3B;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjD;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,EACX,IAAI,CAAC,EAAE,MAAM,GACZ,UAAU,CAsCZ;AAED;;;;GAIG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sEAAsE;IACtE,WAAW,CAAC,EAAE,MAAM,UAAU,GAAG,IAAI,CAAC;IACtC,wEAAwE;IACxE,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC,YAAY,EAAE,YAAY,CAAC,CAAC;CAClE,CAAC;AAEF;;;;;GAKG;AACH,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,cAAc,GACnB,OAAO,CAAC,UAAU,CAAC,CA0BrB;AAED;;;GAGG;AACH,MAAM,MAAM,aAAa,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAI9D,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,CACX,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,EACX,IAAI,CAAC,EAAE,MAAM,KACV,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IACtC,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB;;;;OAIG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC;CACtB,CAAC;AAEF,wBAAsB,QAAQ,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAqKnE"}
|
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
|
-
/**
|
|
9
|
-
|
|
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`
|
|
12
|
-
// value like `$(rm -rf ~)` can never be shell-evaluated. See
|
|
13
|
-
// stderr is piped (not ignored) so
|
|
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"] });
|
|
@@ -53,11 +59,11 @@ export async function pollLinearIssues(deps) {
|
|
|
53
59
|
};
|
|
54
60
|
}
|
|
55
61
|
try {
|
|
56
|
-
const client = (deps.makeClient ??
|
|
57
|
-
((t) => createLinearClient({ token: t })))(auth.token);
|
|
62
|
+
const client = (deps.makeClient ?? ((t) => createLinearClient({ token: t })))(auth.token);
|
|
58
63
|
const issues = await client.listIssues({
|
|
59
64
|
label: deps.label,
|
|
60
65
|
team: deps.team,
|
|
66
|
+
project: deps.project,
|
|
61
67
|
limit: deps.limit ?? 50,
|
|
62
68
|
});
|
|
63
69
|
return { ok: true, count: issues.length };
|
|
@@ -71,7 +77,28 @@ export async function pollLinearIssues(deps) {
|
|
|
71
77
|
}
|
|
72
78
|
const GH_PROVIDER = { name: "gh", authCmd: "gh auth login" };
|
|
73
79
|
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;
|
|
80
|
+
const { stages, iterations, workspaceDir, packageDir, watchIntervalSec, watchLabel, budgetUsd, cooldownMs, tokenMode = "off", maxRetries, reviewLenses, notify = false, bin = "otto-ghafk", pollIssues = pollOpenIssues, provider = GH_PROVIDER, scope, scopes, } = opts;
|
|
81
|
+
// The scopes this daemon rotates through each cycle. Multi-target watch
|
|
82
|
+
// passes `scopes`; otherwise a single `scope` (or the workspace default,
|
|
83
|
+
// represented by a lone `undefined`).
|
|
84
|
+
const scopeList = scopes && scopes.length > 0 ? scopes : [scope];
|
|
85
|
+
// Derive the gh `--repo` filter from a github scope; other providers (Linear)
|
|
86
|
+
// carry their scope in the label/team the poller already honors.
|
|
87
|
+
const ghRepoOf = (s) => s?.provider === "github" && s.owner && s.repo
|
|
88
|
+
? `${s.owner}/${s.repo}`
|
|
89
|
+
: undefined;
|
|
90
|
+
// The Linear project for a scope. Unlike github (which gets a poll `--repo`
|
|
91
|
+
// arg), the Linear poller reads OTTO_LINEAR_PROJECT from the env, so the
|
|
92
|
+
// daemon pins it before each poll/run to confine that scope.
|
|
93
|
+
const linearProjectOf = (s) => s?.provider === "linear" && s.project ? s.project : undefined;
|
|
94
|
+
// Human-readable scope prefix for a poll line (e.g. "github acme/web ").
|
|
95
|
+
const labelOf = (s) => (s ? `${describeScope(s)} ` : "");
|
|
96
|
+
// The banner names every scope so a maintainer sees the exact watch surface.
|
|
97
|
+
const bannerScope = scopeList
|
|
98
|
+
.filter((s) => !!s)
|
|
99
|
+
.map(describeScope)
|
|
100
|
+
.join(", ");
|
|
101
|
+
const scopeLabel = bannerScope ? `${bannerScope} ` : "";
|
|
75
102
|
const releaser = acquire({ reason: `${bin} watch` });
|
|
76
103
|
let released = false;
|
|
77
104
|
const releaseOnce = () => {
|
|
@@ -92,7 +119,7 @@ export async function runWatch(opts) {
|
|
|
92
119
|
const onSigterm = onSig(143);
|
|
93
120
|
process.on("SIGINT", onSigint);
|
|
94
121
|
process.on("SIGTERM", onSigterm);
|
|
95
|
-
process.stderr.write(`${USE_COLOR ? dim("watching") + " " + bold(
|
|
122
|
+
process.stderr.write(`${USE_COLOR ? dim("watching") + " " + bold(`${scopeLabel}label:${watchLabel} every ${watchIntervalSec}s`) : `watching ${scopeLabel}label:${watchLabel} every ${watchIntervalSec}s`}\n`);
|
|
96
123
|
let cumulativeCost = 0;
|
|
97
124
|
// Track idle state so the "no open issues" line prints only on the
|
|
98
125
|
// idle→busy→idle transition, not on every empty poll — otherwise an
|
|
@@ -107,42 +134,70 @@ export async function runWatch(opts) {
|
|
|
107
134
|
notifyComplete(0, false);
|
|
108
135
|
return;
|
|
109
136
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
137
|
+
// Poll every scope this cycle. Run one loop for the FIRST scope with
|
|
138
|
+
// work, then break back to the sleep+repoll — one loop at a time, no
|
|
139
|
+
// parallel mutation of the workspace. A failed poll for one scope is
|
|
140
|
+
// logged and skipped so it never blocks polling the others.
|
|
141
|
+
let ran = false;
|
|
142
|
+
let allIdle = true;
|
|
143
|
+
for (const s of scopeList) {
|
|
144
|
+
const sRepo = ghRepoOf(s);
|
|
145
|
+
const sProject = linearProjectOf(s);
|
|
146
|
+
const sLabel = labelOf(s);
|
|
147
|
+
// Pin the Linear project before polling so the poller (which reads it
|
|
148
|
+
// from the inherited env, not a poll arg) is confined to this scope;
|
|
149
|
+
// it also stays pinned for the loop run below. GitHub uses the sRepo
|
|
150
|
+
// poll arg instead and pins OTTO_GITHUB_REPO only on the run.
|
|
151
|
+
if (sProject)
|
|
152
|
+
process.env.OTTO_LINEAR_PROJECT = sProject;
|
|
153
|
+
const poll = await pollIssues(watchLabel, workspaceDir, sRepo);
|
|
154
|
+
if (!poll.ok) {
|
|
155
|
+
// Broken poll — say *why*, distinctly from an idle queue, and keep
|
|
156
|
+
// polling (auth may get fixed / a transient failure may clear).
|
|
157
|
+
allIdle = false;
|
|
158
|
+
wasIdle = false;
|
|
159
|
+
const suffix = poll.detail ? ` — ${poll.detail}` : "";
|
|
160
|
+
const why = poll.auth
|
|
161
|
+
? `${sLabel}${provider.name} not authenticated — run '${provider.authCmd}' (label ${watchLabel})${suffix}`
|
|
162
|
+
: `${sLabel}${provider.name} issue poll failed (label ${watchLabel})${suffix}`;
|
|
163
|
+
process.stderr.write(`${dim(why)}\n`);
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
if (poll.count > 0) {
|
|
167
|
+
allIdle = false;
|
|
168
|
+
wasIdle = false;
|
|
169
|
+
process.stderr.write(`${dim(`${sLabel}${poll.count} open issue(s) labelled ${watchLabel} — running loop`)}\n`);
|
|
170
|
+
// Confine this loop's `gh` commands (render-time tags + the spawned
|
|
171
|
+
// agent) to the selected scope by pinning OTTO_GITHUB_REPO before the
|
|
172
|
+
// run; single-target left it set in run-bin, this covers multi-target.
|
|
173
|
+
if (sRepo)
|
|
174
|
+
process.env.OTTO_GITHUB_REPO = sRepo;
|
|
175
|
+
const remaining = budgetUsd != null ? budgetUsd - cumulativeCost : undefined;
|
|
176
|
+
const outcome = await runLoop({
|
|
177
|
+
stages,
|
|
178
|
+
inputs: "",
|
|
179
|
+
iterations,
|
|
180
|
+
workspaceDir,
|
|
181
|
+
packageDir,
|
|
182
|
+
budgetUsd: remaining,
|
|
183
|
+
cooldownMs,
|
|
184
|
+
tokenMode,
|
|
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
|
-
|
|
198
|
+
if (!ran && allIdle && !wasIdle) {
|
|
144
199
|
// First empty poll after activity — announce idle once, then stay quiet
|
|
145
|
-
// until
|
|
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":"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,EACL,IAAI,EACJ,GAAG,EACH,QAAQ,EACR,OAAO,EACP,MAAM,EACN,OAAO,EACP,SAAS,GACV,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"watch.js","sourceRoot":"","sources":["../src/watch.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -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/
|
|
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
|
|
80
|
-
(with why), and any REJECTED / won't-fix
|
|
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
|
|
package/templates/ghafk-issue.md
CHANGED
|
@@ -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
|
package/templates/ghafk.md
CHANGED
|
@@ -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
|
|
package/templates/ghprompt.md
CHANGED
|
@@ -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.
|
package/templates/superpowers.md
CHANGED
|
@@ -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
|
-
|
|
21
|
-
|
|
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
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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
|