breakaway 1.2.1-main.5 → 1.2.1-main.7
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/.agents/skills/tasks/SKILL.md +1 -1
- package/package.json +1 -1
- package/prompts/breakaway.md +1 -1
- package/scripts/tasks/cli.js +45 -4
- package/scripts/tasks.mjs +39 -7
- package/src/cli-version.js +2 -2
|
@@ -19,7 +19,7 @@ breakaway's work is on the board that tracks this repository. The CLI is `npx br
|
|
|
19
19
|
|
|
20
20
|
A `409` means someone has it or it's blocked. Pick another; never `--force` someone else's claim.
|
|
21
21
|
4. **Read it:** `tasks show <ID>` (description, done when, comments, spec, what it waits for and holds up), then [`AGENTS.md`](../../../AGENTS.md) if you haven't this session.
|
|
22
|
-
5. **Work** on a branch. Record what you learn as you go: `tasks comment <ID> "<finding>"`. Comments are append-only; the description is the current brief, and you edit it only on a task you made or are refining. New work you find becomes `tasks add "<title>" --project <area> --tag agent|owner --horizon <h> --brief "<what and why>" --done-when "<done when>"`, with `--depends <ID>` when it waits for something. breakaway's areas: `board`, `web`, `docs`, `launch`, `brand`.
|
|
22
|
+
5. **Work** on a branch. Record what you learn as you go: `tasks comment <ID> "<finding>"`. Comments are append-only; the description is the current brief, and you edit it only on a task you made or are refining. New work you find becomes `tasks add "<title>" --project <area> --tag agent|owner --horizon <h> --brief "<what and why>" --done-when "<done when>"`, with `--depends <ID>` when it waits for something. breakaway's areas: `board`, `web`, `docs`, `launch`, `brand`, `cli`.
|
|
23
23
|
6. **Hand over:** open the pull request with `Closes <ID>.` in its description, then `tasks modify <ID> --pr <number>` and `tasks comment <ID> "<one-line result>"`. The board moves the task to In review and marks it done when the pull request merges; don't mark it done yourself. If you stop before a pull request: `comment` where you got to, then `release <ID>`.
|
|
24
24
|
|
|
25
25
|
## Rules
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "breakaway",
|
|
3
|
-
"version": "1.2.1-main.
|
|
3
|
+
"version": "1.2.1-main.7",
|
|
4
4
|
"description": "The task board for you and your coding agents: a Cloudflare Worker, its web app, Taskwarrior sync, and the CLI (npx breakaway).",
|
|
5
5
|
"license": "FSL-1.1-Apache-2.0",
|
|
6
6
|
"type": "module",
|
package/prompts/breakaway.md
CHANGED
|
@@ -9,7 +9,7 @@ The routine on claude.ai holds only the stub, [`prompts/stub.md`](stub.md), whic
|
|
|
9
9
|
|
|
10
10
|
## Repository
|
|
11
11
|
|
|
12
|
-
breakaway: the repository whose `origin` ends with `/breakaway`. Its areas on the board, with their work-ID prefixes: board (`BRK`), web (`WEB`), docs (`DOC`), launch (`LCH`),
|
|
12
|
+
breakaway: the repository whose `origin` ends with `/breakaway`. Its areas on the board, with their work-ID prefixes: board (`BRK`), web (`WEB`), docs (`DOC`), launch (`LCH`), brand (`ID`), and cli (`CLI`). Its rules are in `AGENTS.md`; read it first, then the `tasks` skill (`.agents/skills/tasks/SKILL.md`).
|
|
13
13
|
|
|
14
14
|
## Building
|
|
15
15
|
|
package/scripts/tasks/cli.js
CHANGED
|
@@ -6,7 +6,7 @@ import { CLI_PACKAGE } from './init.js';
|
|
|
6
6
|
|
|
7
7
|
/** The subcommands each command knows. Without one, each lists or shows (horizon needs close). */
|
|
8
8
|
export const SUBCOMMANDS = {
|
|
9
|
-
agents: ['next', 'start', 'refine'],
|
|
9
|
+
agents: ['next', 'start', 'refine', 'new'],
|
|
10
10
|
github: ['fix', 'review'],
|
|
11
11
|
repos: ['add', 'init', 'modify', 'remove', 'setup'],
|
|
12
12
|
routines: ['add', 'modify', 'run', 'trigger', 'revoke', 'pause', 'resume'],
|
|
@@ -83,10 +83,10 @@ export const FIX_PROBLEMS = ['conflicts', 'failing', 'review'];
|
|
|
83
83
|
* like `github` does. Returns an error message instead when the number or `problem` can't be right.
|
|
84
84
|
* @param {'fix' | 'review'} action
|
|
85
85
|
* @param {string | number | undefined} number
|
|
86
|
-
* @param {{ repo?: string | null, problem?: string, note?: string }} [options]
|
|
87
|
-
* @returns {{ error?: string, request?: [string, string, Record<string, string>] }}
|
|
86
|
+
* @param {{ repo?: string | null, problem?: string, note?: string, force?: boolean, by?: string }} [options]
|
|
87
|
+
* @returns {{ error?: string, request?: [string, string, Record<string, string | boolean>] }}
|
|
88
88
|
*/
|
|
89
|
-
export function pullAgentRequest(action, number, { repo = null, problem, note } = {}) {
|
|
89
|
+
export function pullAgentRequest(action, number, { repo = null, problem, note, force = false, by } = {}) {
|
|
90
90
|
const n = String(number ?? '').replace(/^#/u, '');
|
|
91
91
|
if (!/^[1-9]\d{0,8}$/u.test(n)) return { error: `say which pull request: npx breakaway github ${action} <number>` };
|
|
92
92
|
if (problem !== undefined && action !== 'fix') return { error: '--problem is for github fix' };
|
|
@@ -96,10 +96,51 @@ export function pullAgentRequest(action, number, { repo = null, problem, note }
|
|
|
96
96
|
...(repo ? { repo } : {}),
|
|
97
97
|
...(problem ? { problem } : {}),
|
|
98
98
|
...(typeof note === 'string' && note.trim() ? { note } : {}),
|
|
99
|
+
...forceFields(force, by),
|
|
99
100
|
};
|
|
100
101
|
return { request: ['POST', `github/pulls/${n}/${action}`, body] };
|
|
101
102
|
}
|
|
102
103
|
|
|
104
|
+
/**
|
|
105
|
+
* Force start on a request that starts an agent (BRK-107): `force`, and who is asking, so the board can refuse an
|
|
106
|
+
* agent's name (only the owner forces a start). Nothing when it isn't forced.
|
|
107
|
+
* @param {unknown} force
|
|
108
|
+
* @param {string | undefined} by
|
|
109
|
+
*/
|
|
110
|
+
export function forceFields(force, by) {
|
|
111
|
+
return force ? { force: true, ...(by ? { by } : {}) } : {};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* `agents new`: the request that makes a task from a prompt and starts an agent on it. It's the checkout's repository
|
|
116
|
+
* unless `--repo` names another. It always says who asks, so the board refuses an agent's name: only the owner starts one.
|
|
117
|
+
* @param {string} prompt
|
|
118
|
+
* @param {{ repo?: string | null, force?: boolean, by?: string }} [options]
|
|
119
|
+
*/
|
|
120
|
+
export function generalAgentRequest(prompt, { repo = null, force = false, by } = {}) {
|
|
121
|
+
const text = String(prompt ?? '').trim();
|
|
122
|
+
if (!text)
|
|
123
|
+
return { error: 'say what the agent should do: npx breakaway agents new "Tidy the docs" [--image <file>]' };
|
|
124
|
+
const body = {
|
|
125
|
+
prompt: text,
|
|
126
|
+
...(repo ? { repo } : {}),
|
|
127
|
+
...(force ? { force: true } : {}),
|
|
128
|
+
...(by ? { by } : {}),
|
|
129
|
+
};
|
|
130
|
+
return { request: ['POST', 'agents/general', body] };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* What the CLI says about a general agent's answer: the task and that it started, or why it waits (and whether Force
|
|
135
|
+
* start could skip that).
|
|
136
|
+
* @param {{ task: { wid?: string, short?: string }, run?: { url?: string, agent?: string } | null, waiting?: string | null, forceable?: boolean }} answer
|
|
137
|
+
*/
|
|
138
|
+
export function generalAgentSummary({ task, run, waiting, forceable }) {
|
|
139
|
+
const id = task.wid ?? task.short;
|
|
140
|
+
if (run) return `Started ${run.agent ? `${run.agent} ` : 'an agent '}on ${id}${run.url ? `: ${run.url}` : ''}`;
|
|
141
|
+
return `Saved ${id}, waiting to start: ${waiting ?? 'no room yet'}.${forceable ? ` Start it now past the board's limits: npx breakaway agents start ${id} --force` : ''}`;
|
|
142
|
+
}
|
|
143
|
+
|
|
103
144
|
/**
|
|
104
145
|
* What the CLI says about an answer to those requests: which task and agent took the pull request, or who already has it.
|
|
105
146
|
* @param {'fix' | 'review'} action
|
package/scripts/tasks.mjs
CHANGED
|
@@ -44,6 +44,9 @@ import { githubFromRemote, inRepo, pickRepo } from './tasks/repo.js';
|
|
|
44
44
|
import { NO_TERMINAL, ask as askIn } from './tasks/ask.js';
|
|
45
45
|
import { CLI_PACKAGE, PROMPT_SECTIONS, initPlan, machineTaskrc, promptSections } from './tasks/init.js';
|
|
46
46
|
import {
|
|
47
|
+
forceFields,
|
|
48
|
+
generalAgentRequest,
|
|
49
|
+
generalAgentSummary,
|
|
47
50
|
githubRequest,
|
|
48
51
|
ideaTask,
|
|
49
52
|
pullAgentRequest,
|
|
@@ -133,15 +136,16 @@ Reading (list, next, claim, and add work in this checkout's repos
|
|
|
133
136
|
--project <p> --horizon <h> --tag <t>
|
|
134
137
|
activity recent changes, newest first [--limit <n>]
|
|
135
138
|
agents cloud agents: what's running, what's waiting to start
|
|
136
|
-
agents
|
|
137
|
-
agents
|
|
139
|
+
agents new "<prompt>" start an agent from a prompt: it makes its own task [--image <file>]… [--repo <slug>] [--force] (owner)
|
|
140
|
+
agents start <ref> start a Claude cloud agent on a task [--note <text>] [--force]
|
|
141
|
+
agents refine <ref> start an agent that improves a task, not builds it --note <what to look at or change> [--force]
|
|
138
142
|
agents plan [<plan>] your Claude plan and what it allows; pro, max5, or max20 picks one (owner) and sets the limits to its defaults
|
|
139
143
|
agents next start the next few ready tasks, one per area [--count <n>] [--dry-run] [--repo <slug>]
|
|
140
144
|
routines saved prompts the owner runs with a button, and their caps
|
|
141
|
-
routines run <slug> run one now: makes a RUN task and starts an agent on it [--note <text>]
|
|
145
|
+
routines run <slug> run one now: makes a RUN task and starts an agent on it [--note <text>] [--force]
|
|
142
146
|
horizon close close now: finished tasks go to the archive, next becomes now, later becomes next [--dry-run]
|
|
143
|
-
github fix <n> start an agent on a pull request's conflicts, failing checks, or review comments (owner) [--problem conflicts|failing|review] [--note <text>] [--repo <slug>]
|
|
144
|
-
github review <n> start an agent that tests a Dependabot pull request, as Safe to merge? does (owner) [--note <text>] [--repo <slug>]
|
|
147
|
+
github fix <n> start an agent on a pull request's conflicts, failing checks, or review comments (owner) [--problem conflicts|failing|review] [--note <text>] [--repo <slug>] [--force]
|
|
148
|
+
github review <n> start an agent that tests a Dependabot pull request, as Safe to merge? does (owner) [--note <text>] [--repo <slug>] [--force]
|
|
145
149
|
github the checkout's repository on GitHub: open pull requests, checks, reviews, CI, deploys, alerts [--sync] [--repo <slug>]
|
|
146
150
|
hook session|wait the Claude Code session hooks a repository's .claude/settings.json runs (npx breakaway hook session)
|
|
147
151
|
health the server's state
|
|
@@ -752,8 +756,30 @@ const commands = {
|
|
|
752
756
|
);
|
|
753
757
|
return;
|
|
754
758
|
}
|
|
759
|
+
if (sub === 'new') {
|
|
760
|
+
const built = generalAgentRequest(args.slice(1).join(' '), {
|
|
761
|
+
repo: opts.repo ?? (await checkoutRepo()).slug,
|
|
762
|
+
force: Boolean(opts.force),
|
|
763
|
+
by: opts.as ?? setting('AGENT'),
|
|
764
|
+
});
|
|
765
|
+
if (built.error || !built.request) fail(built.error ?? 'bad request');
|
|
766
|
+
if ((opts.image ?? []).length > 4) fail('an agent takes up to 4 images');
|
|
767
|
+
for (const file of opts.image ?? []) if (!existsSync(file)) fail(`can't read ${file}`);
|
|
768
|
+
const answer = await call(...built.request);
|
|
769
|
+
// The task exists now: attach the images to it, as an idea's are.
|
|
770
|
+
for (const file of opts.image ?? []) {
|
|
771
|
+
const image = await upload(ref(answer.task), file);
|
|
772
|
+
if (!opts.json) console.log(`Attached ${image.name} (${Math.ceil(image.size / 1024)} KB).`);
|
|
773
|
+
}
|
|
774
|
+
print(answer, generalAgentSummary);
|
|
775
|
+
return;
|
|
776
|
+
}
|
|
755
777
|
if (sub === 'start') {
|
|
756
|
-
const { task, run } = await call('POST', 'agents/start', {
|
|
778
|
+
const { task, run } = await call('POST', 'agents/start', {
|
|
779
|
+
ref: need(args[1], 'task'),
|
|
780
|
+
note: opts.note,
|
|
781
|
+
...forceFields(opts.force, opts.as ?? setting('AGENT')),
|
|
782
|
+
});
|
|
757
783
|
print({ task, run }, () => `Started an agent on ${task.wid ?? task.short}: ${run.url}`);
|
|
758
784
|
return;
|
|
759
785
|
}
|
|
@@ -764,6 +790,7 @@ const commands = {
|
|
|
764
790
|
ref: need(args[1], 'task'),
|
|
765
791
|
note: opts.note,
|
|
766
792
|
mode: 'refine',
|
|
793
|
+
...forceFields(opts.force, opts.as ?? setting('AGENT')),
|
|
767
794
|
});
|
|
768
795
|
print({ task, run }, () => `Started an agent refining ${task.wid ?? task.short}: ${run.url}`);
|
|
769
796
|
return;
|
|
@@ -938,7 +965,10 @@ const commands = {
|
|
|
938
965
|
};
|
|
939
966
|
const clean = (o) => Object.fromEntries(Object.entries(o).filter(([, v]) => v !== undefined));
|
|
940
967
|
if (sub === 'run') {
|
|
941
|
-
const { task, run } = await call('POST', `routines/${enc(need(args[1], 'routine'))}/run`, {
|
|
968
|
+
const { task, run } = await call('POST', `routines/${enc(need(args[1], 'routine'))}/run`, {
|
|
969
|
+
note: opts.note,
|
|
970
|
+
...forceFields(opts.force, opts.as ?? setting('AGENT')),
|
|
971
|
+
});
|
|
942
972
|
print({ task, run }, () => `Started ${task.wid}: ${run.url}`);
|
|
943
973
|
return;
|
|
944
974
|
}
|
|
@@ -1206,6 +1236,8 @@ const commands = {
|
|
|
1206
1236
|
repo: (await checkoutRepo()).slug,
|
|
1207
1237
|
problem: opts.problem,
|
|
1208
1238
|
note: opts.note,
|
|
1239
|
+
force: Boolean(opts.force),
|
|
1240
|
+
by: opts.as ?? setting('AGENT'),
|
|
1209
1241
|
});
|
|
1210
1242
|
if (built.error || !built.request) fail(built.error ?? 'bad request');
|
|
1211
1243
|
const answer = await call(...built.request);
|
package/src/cli-version.js
CHANGED
|
@@ -4,5 +4,5 @@
|
|
|
4
4
|
* and how to update it. scripts/tasks/version.test.js fails when the copied files change and this doesn't:
|
|
5
5
|
* so it lives in the board's package (CLD-135) and the CLI imports it from here.
|
|
6
6
|
*/
|
|
7
|
-
export const CLI_VERSION =
|
|
8
|
-
export const CLI_FINGERPRINT = '
|
|
7
|
+
export const CLI_VERSION = 52;
|
|
8
|
+
export const CLI_FINGERPRINT = 'b1542857a107623b';
|