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.
@@ -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.5",
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",
@@ -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`), and brand (`ID`). Its rules are in `AGENTS.md`; read it first, then the `tasks` skill (`.agents/skills/tasks/SKILL.md`).
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
 
@@ -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 start <ref> start a Claude cloud agent on a task [--note <text>]
137
- agents refine <ref> start an agent that improves a task, not builds it --note <what to look at or change>
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', { ref: need(args[1], 'task'), note: opts.note });
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`, { note: opts.note });
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);
@@ -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 = 50;
8
- export const CLI_FINGERPRINT = '2eb2f4fbe777547e';
7
+ export const CLI_VERSION = 52;
8
+ export const CLI_FINGERPRINT = 'b1542857a107623b';