breakaway 1.2.1-main.14 → 1.2.1-main.15
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/package.json +1 -1
- package/scripts/tasks/cli.js +7 -10
- package/scripts/tasks.mjs +1 -5
- package/src/cli-version.js +2 -2
- package/src/decision.js +0 -72
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.15",
|
|
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/scripts/tasks/cli.js
CHANGED
|
@@ -143,17 +143,15 @@ export function forceFields(force, by) {
|
|
|
143
143
|
/**
|
|
144
144
|
* `agents new`: the request that makes a task from a prompt and starts an agent on it. It's the checkout's repository
|
|
145
145
|
* unless `--repo` names another. It always says who asks, so the board refuses an agent's name: only the owner starts one.
|
|
146
|
-
* With `decision` (`agents new --decision <ID> ["<note>"]`, BRK-110) the board writes the prompt from that answered
|
|
147
|
-
* decision, in the decision's repository, and the text is the owner's note under it.
|
|
148
146
|
* @param {string} prompt
|
|
149
|
-
* @param {{ repo?: string | null, force?: boolean, by?: string
|
|
147
|
+
* @param {{ repo?: string | null, force?: boolean, by?: string }} [options]
|
|
150
148
|
*/
|
|
151
|
-
export function generalAgentRequest(prompt, { repo = null, force = false, by
|
|
149
|
+
export function generalAgentRequest(prompt, { repo = null, force = false, by } = {}) {
|
|
152
150
|
const text = String(prompt ?? '').trim();
|
|
153
|
-
if (!text
|
|
151
|
+
if (!text)
|
|
154
152
|
return { error: 'say what the agent should do: npx breakaway agents new "Tidy the docs" [--image <file>]' };
|
|
155
153
|
const body = {
|
|
156
|
-
|
|
154
|
+
prompt: text,
|
|
157
155
|
...(repo ? { repo } : {}),
|
|
158
156
|
...(force ? { force: true } : {}),
|
|
159
157
|
...(by ? { by } : {}),
|
|
@@ -163,12 +161,11 @@ export function generalAgentRequest(prompt, { repo = null, force = false, by, de
|
|
|
163
161
|
|
|
164
162
|
/**
|
|
165
163
|
* What the CLI says about a general agent's answer: the task and that it started, or why it waits (and whether Force
|
|
166
|
-
* start could skip that)
|
|
167
|
-
* @param {{ task: { wid?: string, short?: string }, run?: { url?: string, agent?: string } | null, waiting?: string | null, forceable?: boolean
|
|
164
|
+
* start could skip that).
|
|
165
|
+
* @param {{ task: { wid?: string, short?: string }, run?: { url?: string, agent?: string } | null, waiting?: string | null, forceable?: boolean }} answer
|
|
168
166
|
*/
|
|
169
|
-
export function generalAgentSummary({ task, run, waiting, forceable
|
|
167
|
+
export function generalAgentSummary({ task, run, waiting, forceable }) {
|
|
170
168
|
const id = task.wid ?? task.short;
|
|
171
|
-
if (!run && already) return `${id} already refines from these answers: ${already}.`;
|
|
172
169
|
if (run) return `Started ${run.agent ? `${run.agent} ` : 'an agent '}on ${id}${run.url ? `: ${run.url}` : ''}`;
|
|
173
170
|
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` : ''}`;
|
|
174
171
|
}
|
package/scripts/tasks.mjs
CHANGED
|
@@ -138,7 +138,6 @@ Reading (list, next, claim, and add work in this checkout's repos
|
|
|
138
138
|
activity recent changes, newest first [--limit <n>]
|
|
139
139
|
agents cloud agents: what's running, what's waiting to start
|
|
140
140
|
agents new "<prompt>" start an agent from a prompt: it makes its own task [--image <file>]… [--repo <slug>] [--force] (owner)
|
|
141
|
-
agents new --decision <ref> ["<note>"] start an agent that brings the work waiting for an answered decision in line with its answers; the board writes its prompt [--force] (owner)
|
|
142
141
|
agents start <ref> start a Claude cloud agent on a task [--note <text>] [--force]
|
|
143
142
|
agents refine <ref> start an agent that improves a task, not builds it --note <what to look at or change> [--force]
|
|
144
143
|
agents plan [<plan>] your Claude plan and what it allows; pro, max5, or max20 picks one (owner) and sets the limits to its defaults
|
|
@@ -763,11 +762,8 @@ const commands = {
|
|
|
763
762
|
return;
|
|
764
763
|
}
|
|
765
764
|
if (sub === 'new') {
|
|
766
|
-
const decision = typeof opts.decision === 'string' ? opts.decision : null;
|
|
767
765
|
const built = generalAgentRequest(args.slice(1).join(' '), {
|
|
768
|
-
|
|
769
|
-
repo: opts.repo ?? (decision ? null : (await checkoutRepo()).slug),
|
|
770
|
-
decision,
|
|
766
|
+
repo: opts.repo ?? (await checkoutRepo()).slug,
|
|
771
767
|
force: Boolean(opts.force),
|
|
772
768
|
by: opts.as ?? setting('AGENT'),
|
|
773
769
|
});
|
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 = 53;
|
|
8
|
+
export const CLI_FINGERPRINT = '2254d8fb27cd3d52';
|
package/src/decision.js
CHANGED
|
@@ -239,75 +239,3 @@ export function summarize(questions, answers) {
|
|
|
239
239
|
});
|
|
240
240
|
return `Decided by the owner: ${parts.join('; ')}`;
|
|
241
241
|
}
|
|
242
|
-
|
|
243
|
-
/** Tags that aren't a feature's (IDEA-28 section 1): the board's own, horizons, and release tags. */
|
|
244
|
-
const NOT_FEATURES = new Set(['agent', 'owner', 'decide', 'idea', 'general']);
|
|
245
|
-
export const featureTags = (tags) =>
|
|
246
|
-
(tags ?? []).filter((t) => !NOT_FEATURES.has(t) && !t.startsWith('horizon-') && !/^v\d/u.test(t));
|
|
247
|
-
|
|
248
|
-
/** One answer as words, in full but for a long open answer. */
|
|
249
|
-
function spell(q, answer) {
|
|
250
|
-
if (!answer) return 'no answer';
|
|
251
|
-
if (q.type === 'open') return clip(answer.value.trim(), 1500);
|
|
252
|
-
const label = (id) =>
|
|
253
|
-
id === 'other' ? `something else: ${answer.other}` : (q.options.find((o) => o.id === id)?.label ?? id);
|
|
254
|
-
if (q.type === 'choice') return label(answer.value);
|
|
255
|
-
if (q.type === 'multi') return answer.value.length ? answer.value.map(label).join(', ') : 'none';
|
|
256
|
-
if (q.type === 'rank') return answer.value.map((id, i) => `${i + 1}. ${label(id)}`).join(', ');
|
|
257
|
-
if (q.type === 'scale')
|
|
258
|
-
return `${answer.value} (${q.min}${q.minLabel ? ` ${q.minLabel}` : ''} to ${q.max}${q.maxLabel ? ` ${q.maxLabel}` : ''})`;
|
|
259
|
-
return String(answer.value);
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
/** The longest the prompt may be: a task's description. */
|
|
263
|
-
const MAX_BRIEF = 10000;
|
|
264
|
-
|
|
265
|
-
/**
|
|
266
|
-
* Refine from the answers (docs/specs/IDEA-30-new-agent.md, section 8): the prompt the board writes for a general
|
|
267
|
-
* agent that brings the work waiting for an answered decision in line with its answers. `decision` is the decision's
|
|
268
|
-
* task (its `ref` is its work ID or short ID), `waiting` the open tasks that depend on it, and `note` the owner's,
|
|
269
|
-
* which goes under the board's prompt. Returns the task's title and description.
|
|
270
|
-
* @param {{ ref: string, description: string, spec?: string | null, questions: any[], answers: Record<string, any> }} decision
|
|
271
|
-
* @param {{ ref: string, description: string, tags?: string[], spec?: string | null }[]} waiting
|
|
272
|
-
* @param {string | null} [note]
|
|
273
|
-
*/
|
|
274
|
-
export function refinePrompt(decision, waiting, note = null) {
|
|
275
|
-
const title = clip(`Refine from the answers to ${decision.ref}: ${decision.description}`, 200);
|
|
276
|
-
const questions = decision.questions.flatMap((q, i) => {
|
|
277
|
-
const answer = decision.answers[q.id];
|
|
278
|
-
return [
|
|
279
|
-
`${i + 1}. ${q.prompt.trim()}`,
|
|
280
|
-
` Answer: ${spell(q, answer)}`,
|
|
281
|
-
...(answer?.comment ? [` The owner's note: ${answer.comment.trim()}`] : []),
|
|
282
|
-
];
|
|
283
|
-
});
|
|
284
|
-
const specs = [...new Set([decision.spec, ...waiting.map((t) => t.spec)].filter(Boolean))];
|
|
285
|
-
const held = waiting.map((t) => {
|
|
286
|
-
const features = featureTags(t.tags);
|
|
287
|
-
return `- ${t.ref}: ${t.description}${features.length ? ` (feature: ${features.join(', ')})` : ''}`;
|
|
288
|
-
});
|
|
289
|
-
const after = [
|
|
290
|
-
'',
|
|
291
|
-
'What to do',
|
|
292
|
-
`- Change the tasks waiting for ${decision.ref}, and their dependencies, so they match the answers (the cross-task edits a general agent may make, each change noted).`,
|
|
293
|
-
`- Update ${specs.length ? 'the spec' : 'any spec the tasks link'} to match, in one pull request that closes your own task.`,
|
|
294
|
-
'- Add the tasks the answers need, filled in and depending on what they wait for.',
|
|
295
|
-
'- Ask a new decision for anything the answers leave open. Never change these answers: only the owner does.',
|
|
296
|
-
'- If nothing in the repository needs to change, comment what you changed on the board, task by task, and release your task.',
|
|
297
|
-
...(note && String(note).trim() ? ['', 'Note from the owner:', String(note).trim().slice(0, 4000)] : []),
|
|
298
|
-
];
|
|
299
|
-
const intro = `The owner answered the decision on ${decision.ref} (${decision.description}). Bring the work waiting for it in line with the answers.`;
|
|
300
|
-
const rest = [
|
|
301
|
-
'',
|
|
302
|
-
`Waiting for ${decision.ref}`,
|
|
303
|
-
...(held.length ? held : ['- Nothing open waits for it: look for tasks and specs the answers change.']),
|
|
304
|
-
...(specs.length ? ['', specs.length === 1 ? 'Spec' : 'Specs', ...specs.map((s) => `- ${s}`)] : []),
|
|
305
|
-
...after,
|
|
306
|
-
].join('\n');
|
|
307
|
-
// Too long for a description: the answers give way first, since they stay on the decision for the agent to read.
|
|
308
|
-
const more = `\n… (the rest is on ${decision.ref}: npx breakaway show ${decision.ref})`;
|
|
309
|
-
const answers = ['', 'Questions and answers', ...questions].join('\n');
|
|
310
|
-
const room = MAX_BRIEF - intro.length - rest.length - 1;
|
|
311
|
-
const middle = answers.length <= room ? answers : `${answers.slice(0, Math.max(0, room - more.length))}${more}`;
|
|
312
|
-
return { title, brief: `${intro}${middle}\n${rest}`.slice(0, MAX_BRIEF) };
|
|
313
|
-
}
|