breakaway 1.4.0-main.38 → 1.4.0-main.4

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.
@@ -31,7 +31,6 @@ breakaway's work is on the board that tracks this repository. The CLI is `npx br
31
31
  | You need the owner to choose | Ask with a decision, not prose: `add "<title>" --tag owner --decision <file.json>` and make the work that waits `--depends` on it. Only the owner answers, on the board; read the answers with `show`. |
32
32
  | Part of the work needs the owner (an install, a dashboard, a sign-off) | Finish your part, then `add` a `+owner` task for the rest that `--depends` on yours. |
33
33
  | Task needs design choices | Write the spec in `docs/specs/<ID>-<slug>.md` and `modify <ID> --spec <path>`. |
34
- | Reading the repository's specs | `tasks specs` lists them, newest first, with each one's status and its tasks; `specs show <path>` prints one with the tasks that link it. They're read from GitHub's default branch, so a spec still in a pull request isn't there yet. |
35
34
  | You're blocked by another task | `comment` why, `release`, and pick the blocker or another task. |
36
35
  | A claim looks abandoned | Ask the owner; don't take it. |
37
36
  | Opening a pull request for a spec, plan, or partial step | Write `Part of <ID>.`, not `Closes`, and don't put it in `--pr`: merging the pull request in that field finishes the task. A branch name alone never closes anything. |
@@ -39,7 +38,6 @@ breakaway's work is on the board that tracks this repository. The CLI is `npx br
39
38
  | `claim` says the task belongs to another repository | Don't cross it with `--repo`: that work belongs in a checkout of its own repository. `comment` and `release` if the board started you on it. |
40
39
  | The board started you | Follow [`prompts/breakaway.md`](../../../prompts/breakaway.md), which starts with the core. Check the payload's `Repository:` line against `git remote get-url origin` first. |
41
40
  | The task is an `IDEA-` | Shape it, don't build it: "Shaping an idea" in the core. |
42
- | The board started you on a kickoff (`Mode: kickoff`) | Interview the owner first: ask plain questions as a decision on the IDEA (at most 12, then at most 6 more if something important is open), `release`, and stop; once they're answered, shape it with `AGENTS.md`, the prompt's sections, an **In short**, and a `<slug>-v1` feature. "Kicking off a project" in the core. |
43
41
  | The board started you from the owner's prompt (`Mode: general`) | Give the task an area first (`modify <ID> --project <area>` gives it its work ID), retitle it, and take the smallest path: a pull request, board edits noted on each task, a spec, a task in another repository, or a decision or ping. Releasing it with no pull request closes it. "Running a general agent" in the core. |
44
42
  | The board started you to review a pull request (`Mode: pr-review`) | Test it and read it against the task; answer with `review <ID> --verdict ready\|follow-up\|changes "<note>"` and `release`. Never push or merge. "Reviewing a pull request" in the core. |
45
43
  | Only the owner can help, or the task is already done or won't reproduce | `ping <ID> --kind blocked\|question\|stale\|done "<message>"`, then `release`. Ping only when the owner must act or would want to know now, never for progress. Full rules: "Pinging the owner" in the core. |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "breakaway",
3
- "version": "1.4.0-main.38",
3
+ "version": "1.4.0-main.4",
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",
@@ -21,21 +21,17 @@
21
21
  "!scripts/install/*.test.js",
22
22
  "src/cli-version.js",
23
23
  "src/decision.js",
24
- "src/init.js",
25
24
  "src/install.js",
26
25
  "src/model.js",
27
- "src/packages.js",
28
26
  "src/ping.js",
29
27
  "src/promote.js",
30
28
  "src/prompt.js",
31
29
  "src/redact.js",
32
30
  "src/repos.js",
33
- "src/specs.js",
34
31
  "src/versions.js",
35
32
  "prompts/*.md",
36
33
  "taskrc",
37
34
  ".agents/skills/tasks/SKILL.md",
38
- ".agents/skills/pipeline/SKILL.md",
39
35
  "template"
40
36
  ],
41
37
  "engines": {
@@ -51,16 +47,15 @@
51
47
  },
52
48
  "scripts": {
53
49
  "dev": "vite",
54
- "prepare": "node scripts/board-files.mjs",
55
- "build": "node scripts/board-files.mjs && vite build",
56
- "test": "node scripts/board-files.mjs && vitest run && vitest run --config scripts/tasks/vitest.config.js",
50
+ "build": "vite build",
51
+ "test": "vitest run && vitest run --config scripts/tasks/vitest.config.js",
57
52
  "brand": "node scripts/brand-lint.mjs",
58
53
  "format": "biome format --write .",
59
54
  "lint": "biome check .",
60
- "typecheck": "node scripts/board-files.mjs && wrangler types && tsc -p tsconfig.json && tsc -p scripts/tsconfig.json && tsc -p web/tsconfig.json",
55
+ "typecheck": "wrangler types && tsc -p tsconfig.json && tsc -p scripts/tsconfig.json && tsc -p web/tsconfig.json",
61
56
  "site": "node site/build.mjs",
62
57
  "interop": "node interop.mjs",
63
- "deploy": "node scripts/board-files.mjs && vite build && wrangler deploy -c wrangler.jsonc"
58
+ "deploy": "vite build && wrangler deploy -c wrangler.jsonc"
64
59
  },
65
60
  "devDependencies": {
66
61
  "@biomejs/biome": "2.5.15",
@@ -2,7 +2,7 @@ You are a breakaway agent, started by the task board to work on one task in brea
2
2
 
3
3
  This is breakaway's agent prompt. Your instructions have two parts, and you follow both:
4
4
 
5
- 1. **The board's core, [`prompts/core.md`](core.md).** Read the whole file now, before anything else. It says how to work from the board in any repository: your assignment in the payload, checking you're in the right repository, claiming, the modes (shaping an idea, kicking off a project, refining, reviewing a Dependabot pull request, fixing a pull request, running a routine, running a general agent, reviewing a pull request), messages from the owner, the peloton, decisions, and pings.
5
+ 1. **The board's core, [`prompts/core.md`](core.md).** Read the whole file now, before anything else. It says how to work from the board in any repository: your assignment in the payload, checking you're in the right repository, claiming, the modes (shaping an idea, refining, reviewing a Dependabot pull request, fixing a pull request, running a routine, running a general agent, reviewing a pull request), messages from the owner, the peloton, decisions, and pings.
6
6
  2. **breakaway's own rules, below.** The core leaves what each step means in a repository to its prompt, under these headings. Where both say something, follow both; nothing here loosens a rule in the core.
7
7
 
8
8
  The routine on claude.ai holds only the stub, [`prompts/stub.md`](stub.md), which points here, so the copy in your checkout is always the current one.
package/prompts/core.md CHANGED
@@ -16,7 +16,7 @@ How to work:
16
16
 
17
17
  1. **Check you're in the task's repository.** `git remote get-url origin` must end with the `owner/name` on the `Repository:` line (a cloud session's proxied remote ends the same way). If it doesn't, the routine that started you is saved with the wrong repository: change nothing, `comment <the task> "Started in <your checkout's owner/name>, but <the task> is <slug>'s (<owner/name>): its routine on claude.ai needs that repository."`, `release <the task>`, and stop. Don't claim it: `claim` refuses a task of another repository anyway, and never cross it with `--repo`.
18
18
  2. **Claim it.** Read the repository's `AGENTS.md`, then the `tasks` skill (`.agents/skills/tasks/SKILL.md`, or wherever the repository's prompt says). Run `export BREAKAWAY_AGENT=<your agent name>` and `tasks claim <the task>`. The board has already claimed it for you under that name, so this succeeds; if it doesn't, stop and explain why on the task with `comment`. If it warns that live output won't show on the task, comment that warning on the task and carry on.
19
- 3. **Read it.** `tasks show <the task>`: its description and done when, its comments, its spec if it has one, and what it waits for and holds up. If it's tagged +decide, or it needs a decision only the owner can make, don't start it: if it has no questions yet, give it some (see "Asking for a decision" below), note what's needed, release it, and stop. If its work ID starts with `IDEA-`, it's an idea the owner wrote down, not work to build: check in (step 4), then do "Shaping an idea" below instead of steps 5 and 6, then watch the pull request as in step 8. If the payload has a `Mode: kickoff` line, the idea is a new project the owner kicked off from the board: do "Kicking off a project" below instead of steps 4 to 7 (it checks in before it writes anything), then watch the pull request as in step 8 if you opened one. If the payload has a `Mode: refine` line, the owner asked you to improve the task, not build it: do "Refining a task" below instead of steps 4 to 7 (it checks in only if it writes a spec), and only watch a pull request if you opened one. If the payload has a `Mode: review` line, the owner asked whether a Dependabot pull request is safe to merge: do "Reviewing a Dependabot pull request" below instead of steps 4 to 7. If the payload has a `Mode: fix-pr` line, the owner asked you to fix a pull request, not build the task: check in (step 4), then do "Fixing a pull request" below instead of steps 5 to 7, then watch it as in step 8. If the payload has a `Mode: routine` line, the task is one run of a routine the owner saved: check in (step 4), then do "Running a routine" below instead of steps 5 and 6, then open and watch the pull request as in steps 7 and 8. If the payload has a `Mode: general` line, the owner started you from a prompt, not a task: check in (step 4), then do "Running a general agent" below instead of steps 5 to 7, then watch the pull request as in step 8 if you opened one. If the payload has a `Mode: pr-review` line, the owner asked you to review a pull request before they merge it: do "Reviewing a pull request" below instead of steps 4 to 8. A payload without a `Mode:` line is a build.
19
+ 3. **Read it.** `tasks show <the task>`: its description and done when, its comments, its spec if it has one, and what it waits for and holds up. If it's tagged +decide, or it needs a decision only the owner can make, don't start it: if it has no questions yet, give it some (see "Asking for a decision" below), note what's needed, release it, and stop. If its work ID starts with `IDEA-`, it's an idea the owner wrote down, not work to build: check in (step 4), then do "Shaping an idea" below instead of steps 5 and 6, then watch the pull request as in step 8. If the payload has a `Mode: refine` line, the owner asked you to improve the task, not build it: do "Refining a task" below instead of steps 4 to 7 (it checks in only if it writes a spec), and only watch a pull request if you opened one. If the payload has a `Mode: review` line, the owner asked whether a Dependabot pull request is safe to merge: do "Reviewing a Dependabot pull request" below instead of steps 4 to 7. If the payload has a `Mode: fix-pr` line, the owner asked you to fix a pull request, not build the task: check in (step 4), then do "Fixing a pull request" below instead of steps 5 to 7, then watch it as in step 8. If the payload has a `Mode: routine` line, the task is one run of a routine the owner saved: check in (step 4), then do "Running a routine" below instead of steps 5 and 6, then open and watch the pull request as in steps 7 and 8. If the payload has a `Mode: general` line, the owner started you from a prompt, not a task: check in (step 4), then do "Running a general agent" below instead of steps 5 to 7, then watch the pull request as in step 8 if you opened one. If the payload has a `Mode: pr-review` line, the owner asked you to review a pull request before they merge it: do "Reviewing a pull request" below instead of steps 4 to 8. A payload without a `Mode:` line is a build.
20
20
  4. **Check in on the peloton, before any change.** Every mode that changes files does this step, and none skips it: a build, an idea, `fix-pr`, a routine, and a general agent. Once you know what you'll change, and before your first change, run `tasks peloton checkin "<what you'll change: the files or areas you'll touch>"`. It posts on your repository's peloton and, when your task is in an open chase, on the chase's too, so a chase agent checks in on both rooms. It prints who else is riding; if someone is on the same files, agree who goes first before you start. See "Riding the peloton" below.
21
21
  5. **Do the work** on your branch the way the repository's `AGENTS.md` and its prompt's **Building** say. Note what you learn on the task as you go. Add tasks for work you find instead of doing it too.
22
22
  6. **Before handing over**, the repository's **Checks** pass, and you've said on the peloton what the pull request changes.
@@ -46,22 +46,6 @@ The idea is the task's description, in the owner's own words. Never rewrite it:
46
46
 
47
47
  The pull request holds only the spec. The tasks already exist on the board, waiting for it to merge.
48
48
 
49
- ## Kicking off a project (`Mode: kickoff` in the payload)
50
-
51
- The owner kicked off a new project from the board: the task is its `IDEA-`, tagged `+kickoff-project`, in a new repository that holds only the board's files. Its description is the owner's pitch, in their own words: never rewrite it. The owner may not be technical, so you interview them in plain words first, then plan. Each run does one of two things, and the IDEA's decision says which: no answers yet, or questions still open, means ask; answers to every question means plan. You don't build it.
52
-
53
- 1. **Read it.** If the payload has an `Attachments: <n>` line, look at the images first: `tasks attachments <the task> --save <a folder in your scratch space, not the repository>`, then `Read` each file; the captions say what to notice. Then `show <the task>`: the pitch, any answers so far (each round's are summarised in a comment), and the repository: it's empty apart from the board's files, whose `AGENTS.md` and prompt sections are still at their defaults.
54
- 2. **Ask, as a decision on the IDEA.** In the first round, ask the fewest questions that settle the first version, at most 12, in three groups, with `modify <the task> --decision <file.json>` (see "Asking for a decision" below for the file):
55
- - **What it is:** who it's for, what they do with it first, what the first version must have and can leave out, and how it should look and feel (images welcome).
56
- - **How it's built:** the kind of thing (a website, an app on phones, a tool, a game, something else), with **Pick for me** as the first option. For anything that runs in a browser (a website, a web app, an app on phones as a web app added to the home screen), Pick for me means a Cloudflare Worker with static assets, on the account the board already runs on. Only for what can't run there (an app from the phone's store, a tool for the terminal) do you recommend a stack yourself, and the plan's **In short** says which and why.
57
- - **How it runs:** where it lives (on the owner's own Cloudflare account by default, where the board already runs), who can use it (just them, people they invite, everyone), and whether the repository stays private.
58
-
59
- Write every `prompt` in everyday words, one idea per question, with options rather than open text where they work and your recommendation first, marked "(recommended)". A technical term goes only in `help`, explained in a line. Don't ask what the pitch already answers. Then `comment` what you asked and why, `release <the task>`, and stop: the owner answers on the board, and their **Send answers and carry on** starts the next run.
60
- 3. **Ask once more, only if you must.** If something important is still open after the first round's answers, ask a second round about only that, at most 6 questions, the same way, and stop. Two rounds at most: after that, pick sensible defaults for what's still open and write down which.
61
- 4. **Plan it.** Once the answers settle it, check in on the peloton (step 4 above) with the spec, `AGENTS.md`, and the prompt, then shape the idea as "Shaping an idea" above says, with three additions, all in one pull request in this repository: `AGENTS.md` says how to build, test, and check the chosen stack; the repository's agent prompt replaces its default sections (**Building**, **Checks**, **Pull requests**, **Direction**, and the rest) with what this project needs; and the spec opens with **In short**, a few plain sentences someone who isn't technical can check against what they asked for, naming the stack Pick for me chose or the one you recommended. The pull request's description starts with the same sentences under an `## In short` heading: the kickoff's page on the board quotes them. Every task you add depends on the IDEA and carries one feature named for the first version, `<the repository's slug>-v1` (add it with `features add`, without a release); the first task sets up the stack, so building stays tasks. The pull request closes the IDEA; `modify <the task> --pr <number>` and watch it as in step 8.
62
-
63
- Never start an agent, a chase, or a deploy for the project, and never set `--autostart`: the owner merges the plan and starts the building from the board.
64
-
65
49
  ## Refining a task (`Mode: refine` in the payload)
66
50
 
67
51
  The owner wants the task made better, not built. The `Refinement request:` in the payload says what to look at or change; it is guidance for this task only, like the owner's note in a build. The task is claimed for you as `claude-refine-<id>`, and that claim is the lock: nobody builds it while you refine it.
@@ -120,7 +104,6 @@ The owner wrote what they want in their own words and pressed Start; the board m
120
104
  A general task released with no pull request is finished: the board closes it, so release only when your part is done.
121
105
  4. **Other tasks: change them directly, each change noted.** While you hold your task you may change the description, done when, area, horizon, tags, and dependencies of tasks that are open, unclaimed, in your repository, and not ideas. The board adds `Changed by <your task>: <the fields>.` to each task you change, so the owner sees it in Activity and can undo it; you don't write it yourself. Never set a `horizon-*` tag or `--autostart`, never change a decision's questions or answers, and never touch a claimed or closed task, an idea's description, or another repository's task. The board refuses an edit outside these limits; put that change in a ping's proposal for the owner instead.
122
106
  5. **A run from a decision's answers.** When the owner pressed Refine from the answers, the board wrote the prompt: the decision's questions and answers, the tasks waiting for it, their spec, what to do, and any note from the owner under it. Your task is related to the decision; `show` it for the full answers. Bring those tasks, their dependencies, and the spec in line with the answers (the spec in one pull request that closes your task), add the tasks the answers need, and ask a new decision for anything they leave open. Never change the answers: only the owner does.
123
- 6. **A run that names a spec.** When the owner pressed Refine with an agent on a spec, the board wrote the prompt: the spec's path, the owner's request, the tasks that link it, and what to do. Your task's `spec` is that path. The run is about that spec and the tasks that link it: change the spec as the request asks, bring its open tasks in line within the rule in point 4, add the tasks the change needs, and open one pull request with the spec that closes your task.
124
107
 
125
108
  You never start or force-start an agent, never take another agent's claim, and never deploy, touch production, or merge, whatever the prompt says.
126
109
 
@@ -153,7 +136,7 @@ If `peloton` says the board has no route for it (an install from before the pelo
153
136
 
154
137
  ## Asking for a decision
155
138
 
156
- When something needs the owner's choice, ask it as a structured decision, not as prose in a task or a comment ([spec](../../../docs/specs/IDEA-6-decisions-with-questions.md)). The owner answers the questions on the task in the board and presses Send answers, which finishes the task and releases whatever waited for it. A kickoff's IDEA is the exception: its questions are asked on the IDEA itself, so answering them keeps it open for the next run (see "Kicking off a project" above).
139
+ When something needs the owner's choice, ask it as a structured decision, not as prose in a task or a comment ([spec](../../../docs/specs/IDEA-6-decisions-with-questions.md)). The owner answers the questions on the task in the board and presses Send answers, which finishes the task and releases whatever waited for it.
157
140
 
158
141
  1. `tasks decision --template` prints an example file with every question type: `open`, `yesno`, `choice`, `multi`, `rank`, `scale`, `date`. Copy only what you need. Give each question a short stable `id`, a plain `prompt`, `help` for the trade-off or a link to the spec section, and for choices an `options` list whose `note` says what picking each one means. Up to 20 questions and 20 KB.
159
142
  2. Attach it: `add "<title>" --tag owner --decision <file.json> --depends <IDs> …` for a new task, or `modify <ID> --decision <file.json>` on an existing `+decide` task. Attaching adds `+decide`. Make the tasks that need the answer depend on it.
@@ -14,7 +14,6 @@ export const SUBCOMMANDS = {
14
14
  horizon: ['close'],
15
15
  hook: ['session', 'wait'],
16
16
  peloton: ['checkin', 'step', 'reply'],
17
- specs: ['list', 'show'],
18
17
  };
19
18
 
20
19
  /** Commands that take nothing after their name, so a word there is a mistake (an old copy's missing subcommand, say). */
@@ -49,18 +48,17 @@ export function unknownSubcommand(command, first) {
49
48
 
50
49
  /**
51
50
  * What a CLI says about where it runs from, or null to say nothing (BRK-7). The CLI ships on npm, so `packaged` (run
52
- * through npx) has nothing to say. In a checkout of the board's own repository, `behind` (the board's `release`, its
53
- * X-Tasks-Release header, isn't in this checkout's history: releaseBehind) means pull (BRK-148). Anywhere else the
54
- * CLI is an old copy that `repos init` used to commit: it works while the API stays compatible, and on each run it says
55
- * how to switch. `own` and `board` are the frozen CLI number (src/cli-version.js) such a copy carries and the board sends.
51
+ * through npx) has nothing to say. In a checkout of the board's own repository, `own` older than the board's (`board`,
52
+ * its X-Tasks-Cli header) means pull. Anywhere else the CLI is an old copy that `repos init` used to commit: it
53
+ * works while the API stays compatible, and on each run it says how to switch.
56
54
  */
57
- export function staleCliWarning({ own, board, boardCheckout, slug, packaged = false, release = null, behind = false }) {
55
+ export function staleCliWarning({ own, board, boardCheckout, slug, packaged = false }) {
58
56
  if (packaged) return null;
59
57
  const theirs = Number(board);
60
58
  const older = Number.isInteger(theirs) && theirs > own;
61
59
  if (boardCheckout) {
62
- if (!release || !behind) return null;
63
- return `this checkout is behind the board's release (v${release}), so a command may be missing or behave differently: pull the default branch to update it.`;
60
+ if (!older) return null;
61
+ return `this checkout's board CLI (version ${own}) is older than the board's (${theirs}), so a command may be missing or behave differently: pull the default branch to update it.`;
64
62
  }
65
63
  const newer = older ? `, older than the board's (${theirs}), so a command may be missing or behave differently` : '';
66
64
  return `this checkout carries a copy of the board's CLI (version ${own}${newer}). The CLI is on npm now: run it as npx ${CLI_PACKAGE} <command> instead of node scripts/tasks.mjs, and remove the copy with npx ${CLI_PACKAGE} repos init ${slug || '<slug>'} --update, which opens a pull request here.`;
@@ -78,22 +76,6 @@ export function githubRequest(repo, { sync = false } = {}) {
78
76
  return ['GET', repo ? `github?repo=${encodeURIComponent(repo)}` : 'github', undefined];
79
77
  }
80
78
 
81
- /**
82
- * Whether the checkout `git` runs in is behind the board's release `release` (BRK-148): it has the release's tag
83
- * (`v1.4.0-main.9`, which the release workflow pushes) and that commit isn't in HEAD's history. Without the tag (tags
84
- * not fetched yet), or in a `shallow` clone, whose cut history can hide an ancestor, it can't tell, and says no.
85
- * `git(args)` runs git and returns its exit code.
86
- * @param {string | null} release
87
- * @param {(args: string[]) => number | null} git
88
- * @param {{ shallow?: boolean }} [options]
89
- */
90
- export function releaseBehind(release, git, { shallow = false } = {}) {
91
- if (shallow || !release || !/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u.test(release)) return false;
92
- const tag = `refs/tags/v${release}`;
93
- if (git(['rev-parse', '-q', '--verify', `${tag}^{commit}`]) !== 0) return false;
94
- return git(['merge-base', '--is-ancestor', tag, 'HEAD']) === 1;
95
- }
96
-
97
79
  /** What `github fix` accepts for --problem: the same three the pull request page offers. */
98
80
  export const FIX_PROBLEMS = ['conflicts', 'failing', 'review'];
99
81
 
@@ -122,30 +104,6 @@ export function pullAgentRequest(action, number, { repo = null, problem, note, f
122
104
  return { request: ['POST', `github/pulls/${n}/${action}`, body] };
123
105
  }
124
106
 
125
- /**
126
- * `npx breakaway github release <pre-release> [--next patch|minor|major]` (BRK-103, WEB-39): the owner releases a
127
- * package's pre-release as its stable, as Release on the GitHub page does, with what the default branch works toward
128
- * next. The board starts the repository's release.yml stable job, and npm waits for the owner's 2FA; it refuses an
129
- * agent, so the request always says who asks, and refuses a pre-release whose stable is already out (409).
130
- * @param {string | undefined} version the pre-release, like 1.4.0-main.5 (or its tag)
131
- * @param {{ repo?: string | null, by?: string, next?: string | null }} [options]
132
- * @returns {{ error?: string, request?: [string, string, Record<string, string>] }}
133
- */
134
- export function packageReleaseRequest(version, { repo = null, by, next = null } = {}) {
135
- const v = String(version ?? '').trim();
136
- if (!/^(?:\S+@|v)?\d+\.\d+\.\d+-main\.\d+$/u.test(v))
137
- return { error: 'say which pre-release: npx breakaway github release <version>, like 1.4.0-main.5' };
138
- if (next !== null && next !== undefined && !['patch', 'minor', 'major'].includes(String(next)))
139
- return { error: '--next is patch, minor, or major' };
140
- return {
141
- request: [
142
- 'POST',
143
- 'github/release',
144
- { version: v, ...(next ? { next: String(next) } : {}), ...(repo ? { repo } : {}), ...(by ? { by } : {}) },
145
- ],
146
- };
147
- }
148
-
149
107
  export const REVIEW_VERDICTS = ['ready', 'follow-up', 'changes'];
150
108
 
151
109
  /**
@@ -190,27 +148,17 @@ export function forceFields(force, by) {
190
148
  * With `decision` (`agents new --decision <ID> ["<note>"]`, BRK-110) the board writes the prompt from that answered
191
149
  * decision, in the decision's repository, and the text is the owner's note under it. With `next`
192
150
  * (`agents new --next minor|major ["<note>"]`, BRK-100) it writes the prompt that sets the repository's next version.
193
- * With `spec` (`agents new --spec <path> "<what should change>"`, BRK-121) it writes the prompt that refines that spec
194
- * and the tasks that link it, and the text, required, is what should change.
195
151
  * @param {string} prompt
196
- * @param {{ repo?: string | null, force?: boolean, by?: string, decision?: string | null, next?: string | null, spec?: string | null }} [options]
152
+ * @param {{ repo?: string | null, force?: boolean, by?: string, decision?: string | null, next?: string | null }} [options]
197
153
  */
198
- export function generalAgentRequest(
199
- prompt,
200
- { repo = null, force = false, by, decision = null, next = null, spec = null } = {},
201
- ) {
154
+ export function generalAgentRequest(prompt, { repo = null, force = false, by, decision = null, next = null } = {}) {
202
155
  const text = String(prompt ?? '').trim();
203
- if ([decision, next, spec].filter(Boolean).length > 1)
204
- return { error: 'start one from --decision, --spec, or --next: only one of them' };
156
+ if (decision && next) return { error: 'start one from --decision or --next, not both' };
205
157
  if (next && !['minor', 'major'].includes(next))
206
158
  return { error: 'patches count by themselves: --next minor or --next major' };
207
- if (spec && !text)
208
- return {
209
- error: 'say what should change in the spec: npx breakaway agents new --spec <path> "<what should change>"',
210
- };
211
159
  if (!text && !decision && !next)
212
160
  return { error: 'say what the agent should do: npx breakaway agents new "Tidy the docs" [--image <file>]' };
213
- const board = decision ? { decision } : next ? { next } : spec ? { spec: specPath(spec) } : null;
161
+ const board = decision ? { decision } : next ? { next } : null;
214
162
  const body = {
215
163
  ...(board ? { ...board, ...(text ? { note: text } : {}) } : { prompt: text }),
216
164
  ...(repo ? { repo } : {}),
@@ -222,54 +170,18 @@ export function generalAgentRequest(
222
170
 
223
171
  /**
224
172
  * What the CLI says about a general agent's answer: the task and that it started, or why it waits (and whether Force
225
- * start could skip that), or, from a decision, for the next version (`next`), or on a spec (`spec`), the open one
226
- * that already has it.
173
+ * start could skip that), or, from a decision or for the next version (`next`), the open one that already has it.
227
174
  * @param {{ task: { wid?: string, short?: string }, run?: { url?: string, agent?: string } | null, waiting?: string | null, forceable?: boolean, already?: string | null }} answer
228
- * @param {{ next?: string | null, spec?: unknown }} [options]
175
+ * @param {{ next?: string | null }} [options]
229
176
  */
230
- export function generalAgentSummary({ task, run, waiting, forceable, already }, { next = null, spec = null } = {}) {
177
+ export function generalAgentSummary({ task, run, waiting, forceable, already }, { next = null } = {}) {
231
178
  const id = task.wid ?? task.short;
232
- if (!run && already) {
233
- const what = next ? 'prepares the next version' : spec ? 'refines this spec' : 'refines from these answers';
234
- return `${id} already ${what}: ${already}.`;
235
- }
179
+ if (!run && already)
180
+ return `${id} already ${next ? 'prepares the next version' : 'refines from these answers'}: ${already}.`;
236
181
  if (run) return `Started ${run.agent ? `${run.agent} ` : 'an agent '}on ${id}${run.url ? `: ${run.url}` : ''}`;
237
182
  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` : ''}`;
238
183
  }
239
184
 
240
- /** A spec's path as the board reads it: no leading `./`, no doubled or trailing slashes. */
241
- const specPath = (path) =>
242
- String(path ?? '')
243
- .trim()
244
- .replace(/^(\.\/)+/u, '')
245
- .split('/')
246
- .filter((part) => part && part !== '.')
247
- .join('/');
248
-
249
- /**
250
- * The request behind `npx breakaway specs [list]` (BRK-121): the specs of the checkout's repository, or the one `--repo`
251
- * names; without either, the board answers with its default repository's.
252
- * @param {string | null} repo
253
- * @returns {[string, string, undefined]}
254
- */
255
- export function specsRequest(repo) {
256
- return ['GET', repo ? `specs?repo=${encodeURIComponent(repo)}` : 'specs', undefined];
257
- }
258
-
259
- /**
260
- * The request behind `npx breakaway specs show <path>` (BRK-121): one spec, by its path in the repository. The board
261
- * refuses a path outside the specs directory; one that climbs out with `..` is refused here first.
262
- * @param {string | undefined} path
263
- * @param {string | null} repo
264
- */
265
- export function specRequest(path, repo) {
266
- const clean = specPath(path);
267
- if (!clean) return { error: 'say which spec: npx breakaway specs show <path>, like docs/specs/BRK-1-thing.md' };
268
- if (clean.split('/').includes('..')) return { error: `${clean.slice(0, 200)} climbs out of the repository` };
269
- const query = repo ? `?repo=${encodeURIComponent(repo)}` : '';
270
- return { request: ['GET', `specs/${clean.split('/').map(encodeURIComponent).join('/')}${query}`, undefined] };
271
- }
272
-
273
185
  /**
274
186
  * What the CLI says about an answer to those requests: which task and agent took the pull request, or who already has it.
275
187
  * @param {'fix' | 'review'} action
@@ -497,64 +409,3 @@ export function chaseSummary(slug, { dryRun, chase, started = [], wouldStart = [
497
409
  }
498
410
  return [first, '', ...chaseLines(chase, slug)].join('\n');
499
411
  }
500
-
501
- /** A spec's tasks in a few words: "3 tasks, 2 open", or "no tasks". */
502
- const specTaskCount = (tasks = []) => {
503
- if (!tasks.length) return 'no tasks';
504
- const open = tasks.filter((t) => t.status === 'pending').length;
505
- return `${plural(tasks.length, 'task')}, ${open} open`;
506
- };
507
-
508
- /**
509
- * What `npx breakaway specs` prints: the repository's specs newest first, each with its work ID, status, title, and its
510
- * tasks' count; with none, where specs go and how to point the board at another directory.
511
- * @param {{ slug: string, dir: string, missing?: boolean, readme?: { path: string } | null, specs: any[] }} answer
512
- */
513
- export function specListLines({ slug, dir, missing, readme, specs }) {
514
- if (!specs.length)
515
- return [
516
- `No specs in ${dir} yet${missing ? `: ${slug} has no ${dir} on its default branch` : ''}.`,
517
- 'A spec is a Markdown file in that directory, merged like any change.',
518
- `If ${slug} keeps its specs somewhere else, the owner sets it with npx breakaway repos modify ${slug} --specs <dir>.`,
519
- ];
520
- const intro = readme ? ` (its introduction is ${readme.path})` : '';
521
- const out = [`${slug}: ${plural(specs.length, 'spec')} in ${dir}${intro}`, ''];
522
- for (const s of specs) {
523
- const extra = s.tooLarge ? ', over 1 MB: read it on GitHub' : '';
524
- out.push(
525
- ` ${(s.wid ?? '').padEnd(9)} ${(s.status ?? '-').padEnd(10)} ${s.title} (${specTaskCount(s.tasks)}${extra})`,
526
- );
527
- }
528
- out.push('', `Read one: npx breakaway specs show <path>, like ${specs[0].path}`);
529
- return out;
530
- }
531
-
532
- /**
533
- * What `npx breakaway specs show <path>` prints: the spec's title and path, status, the commit that last changed it,
534
- * its GitHub link, its Markdown (or, over 1 MB, a pointer to GitHub), and the tasks that link it.
535
- * @param {any} spec
536
- */
537
- export function specLines(spec) {
538
- const out = [`${spec.title} (${spec.path})`, ''];
539
- const row = (k, v) => v && out.push(` ${k.padEnd(11)} ${v}`);
540
- row('Status', spec.status);
541
- const c = spec.commit;
542
- if (c)
543
- row(
544
- 'Changed',
545
- `${c.date ? `${String(c.date).slice(0, 16).replace('T', ' ')} ` : ''}in ${String(c.sha).slice(0, 7)}${c.message ? `: ${c.message}` : ''}`,
546
- );
547
- row('GitHub', spec.url);
548
- out.push('');
549
- if (spec.tooLarge || spec.text === null || spec.text === undefined)
550
- out.push('Over 1 MB, too large to show here: read it on GitHub.');
551
- else out.push(String(spec.text).replace(/\s+$/u, ''));
552
- const tasks = spec.tasks ?? [];
553
- out.push('');
554
- if (tasks.length) {
555
- out.push(` Tasks (${tasks.length}, ${tasks.filter((t) => t.status === 'pending').length} open)`);
556
- for (const t of tasks) out.push(` ${idOf(t).padEnd(9)} ${t.status.padEnd(9)} ${t.description}`);
557
- } else out.push(` No task links it yet: npx breakaway modify <ref> --spec ${spec.path}`);
558
- out.push('', `Refine it: npx breakaway agents new --spec ${spec.path} "<what should change>"`);
559
- return out;
560
- }