breakaway 1.5.0-main.45 → 1.5.0-main.47

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "breakaway",
3
- "version": "1.5.0-main.45",
3
+ "version": "1.5.0-main.47",
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 @@ export const SUBCOMMANDS = {
9
9
  agents: ['next', 'start', 'refine', 'new'],
10
10
  github: ['fix', 'review'],
11
11
  repos: ['add', 'init', 'modify', 'remove', 'setup'],
12
- routines: ['add', 'modify', 'run', 'trigger', 'revoke', 'pause', 'resume'],
12
+ routines: ['add', 'modify', 'run', 'trigger', 'revoke', 'pause', 'resume', 'cap', 'new'],
13
13
  features: ['list', 'add', 'show', 'modify'],
14
14
  horizon: ['close'],
15
15
  hook: ['session', 'wait'],
@@ -198,6 +198,39 @@ export function forceFields(force, by) {
198
198
  return force ? { force: true, ...(by ? { by } : {}) } : {};
199
199
  }
200
200
 
201
+ /**
202
+ * Who writes a routine (BRK-220 section 5): every routines write says who asks, like `features` does, so the board
203
+ * can tell an agent from the owner and let only a routine maker's agent through. Nothing when no name is set.
204
+ * @template {Record<string, unknown>} T
205
+ * @param {T} body
206
+ * @param {string | undefined} by
207
+ */
208
+ export function routineWrite(body, by) {
209
+ return by ? { ...body, by } : body;
210
+ }
211
+
212
+ /**
213
+ * `routines new` (BRK-220 section 1, Make with an agent): the request that makes a routine maker's task from the
214
+ * owner's words and starts its agent, in the checkout's repository unless `--repo` names another. It says who asks,
215
+ * so the board refuses an agent's name: only the owner starts one.
216
+ * @param {string} prompt
217
+ * @param {{ repo?: string | null, force?: boolean, by?: string }} [options]
218
+ */
219
+ export function routineMakerRequest(prompt, { repo = null, force = false, by } = {}) {
220
+ const text = String(prompt ?? '').trim();
221
+ if (!text)
222
+ return {
223
+ error:
224
+ 'say what the routine should do, and when: npx breakaway routines new "Every Monday, update the changelog from what merged"',
225
+ };
226
+ const body = {
227
+ prompt: text,
228
+ ...(repo ? { repo } : {}),
229
+ ...(force ? { force: true } : {}),
230
+ };
231
+ return { request: ['POST', 'routines/agent', routineWrite(body, by)] };
232
+ }
233
+
201
234
  /**
202
235
  * `agents new`: the request that makes a task from a prompt and starts an agent on it. It's the checkout's repository
203
236
  * unless `--repo` names another. It always says who asks, so the board refuses an agent's name: only the owner starts one.
package/scripts/tasks.mjs CHANGED
@@ -67,6 +67,8 @@ import {
67
67
  forceFields,
68
68
  generalAgentRequest,
69
69
  generalAgentSummary,
70
+ routineMakerRequest,
71
+ routineWrite,
70
72
  githubRequest,
71
73
  specLines,
72
74
  specListLines,
@@ -237,6 +239,8 @@ Reading (list, next, claim, and add work in this checkout's repos
237
239
  agents next start the next few ready tasks, one per area [--count <n>] [--dry-run] [--repo <slug>]
238
240
  routines saved prompts the owner runs with a button, and their caps
239
241
  routines run <slug> run one now: makes a RUN task and starts an agent on it [--note <text>] [--force]
242
+ routines new "<what you want>" start an agent that asks how the routines should run, then makes them and turns
243
+ them on [--repo <slug>] [--force] (owner)
240
244
  features features by release: each one's progress and chase, and tags that could be features
241
245
  features show <slug> one feature: its release, progress, what waits for you, its chase, and its tasks in order
242
246
  chase <slug> start a chase (owner): the board starts an agent on every ready task in the feature and on
@@ -358,9 +362,9 @@ Working
358
362
  --from <ref> (owner): made from the group <ref> is in on the Dependencies view: its open tasks
359
363
  join, and tasks already in another feature stay there
360
364
  features modify <slug> change one (owner): --title, --brief, --brief-file, --release <x.y.z|none>, --state open|shipped
361
- routines add <slug> new routine (owner) --name <text> --prompt <text> | --prompt-file <path> [--done-when <text>] [--horizon now|next|later] [--gap <minutes>] [--daily <n>]
365
+ routines add <slug> new routine (owner, or the agent of a routine maker's task) --name <text> --prompt <text> | --prompt-file <path> [--done-when <text>] [--horizon now|next|later] [--gap <minutes>] [--daily <n>]
362
366
  [--repo <slug>] the repository it runs in (default: the checkout's)
363
- routines modify <slug> change one (owner): the same options (--repo <slug> moves it), and --enabled yes|no; --schedule "0 9 * * 1" runs it on a cron schedule (UTC), --schedule "" clears it; --trigger-start auto|wait sets whether a webhook or GitHub event starts the agent or waits for your Start; --github-events pr_merged,release_published,workflow_failed (or "") starts it on those GitHub events
367
+ routines modify <slug> change one (owner, or the agent of the routine maker's task that made it): the same options (--repo <slug> moves it), and --enabled yes|no; --schedule "0 9 * * 1" runs it on a cron schedule (UTC), --schedule "" clears it; --trigger-start auto|wait sets whether a webhook or GitHub event starts the agent or waits for your Start; --github-events pr_merged,release_published,workflow_failed (or "") starts it on those GitHub events
364
368
  routines trigger <slug> new webhook/API trigger (owner): prints its secret once [--label <text>]
365
369
  routines revoke <slug> <id> revoke a trigger
366
370
  routines pause|resume stop or allow every routine [--daily-all <n>] sets the cap for all routines a day
@@ -1272,10 +1276,23 @@ const commands = {
1272
1276
  };
1273
1277
  };
1274
1278
  const clean = (o) => Object.fromEntries(Object.entries(o).filter(([, v]) => v !== undefined));
1279
+ // Who writes (BRK-220 section 5): the owner, or a routine maker's agent while it holds its task.
1280
+ const by = opts.as ?? setting('AGENT');
1281
+ if (sub === 'new') {
1282
+ // Make with an agent (BRK-220 section 1): a routine maker's task in the checkout's repository unless --repo.
1283
+ const built = routineMakerRequest(args.slice(1).join(' '), {
1284
+ repo: opts.repo ?? (await checkoutRepo()).slug,
1285
+ force: Boolean(opts.force),
1286
+ by,
1287
+ });
1288
+ if (built.error || !built.request) fail(built.error ?? 'bad request');
1289
+ print(await call(...built.request), (d) => generalAgentSummary(d));
1290
+ return;
1291
+ }
1275
1292
  if (sub === 'run') {
1276
1293
  const { task, run } = await call('POST', `routines/${enc(need(args[1], 'routine'))}/run`, {
1277
1294
  note: opts.note,
1278
- ...forceFields(opts.force, opts.as ?? setting('AGENT')),
1295
+ ...forceFields(opts.force, by),
1279
1296
  });
1280
1297
  print({ task, run }, () => `Started ${task.wid}: ${run.url}`);
1281
1298
  return;
@@ -1283,7 +1300,11 @@ const commands = {
1283
1300
  if (sub === 'add') {
1284
1301
  // A routine runs in one repository (CLD-127): --repo, else the checkout's (none: the board's default).
1285
1302
  const repo = opts.repo ?? (await checkoutRepo()).slug ?? undefined;
1286
- const { routine } = await call('POST', 'routines', clean({ slug: need(args[1], 'slug'), ...fields(), repo }));
1303
+ const { routine } = await call(
1304
+ 'POST',
1305
+ 'routines',
1306
+ routineWrite(clean({ slug: need(args[1], 'slug'), ...fields(), repo }), by),
1307
+ );
1287
1308
  print({ routine }, () => `Saved the routine ${routine.slug}. Run it: npx breakaway routines run ${routine.slug}`);
1288
1309
  return;
1289
1310
  }
@@ -1291,14 +1312,18 @@ const commands = {
1291
1312
  const { routine } = await call(
1292
1313
  'PATCH',
1293
1314
  `routines/${enc(need(args[1], 'routine'))}`,
1294
- clean({ ...fields(), repo: opts.repo }),
1315
+ routineWrite(clean({ ...fields(), repo: opts.repo }), by),
1295
1316
  );
1296
1317
  print({ routine }, () => `Saved ${routine.slug}.`);
1297
1318
  return;
1298
1319
  }
1299
1320
  if (sub === 'trigger') {
1300
1321
  const slug = need(args[1], 'routine');
1301
- const { trigger, secret } = await call('POST', `routines/${enc(slug)}/triggers`, { label: opts.label });
1322
+ const { trigger, secret } = await call(
1323
+ 'POST',
1324
+ `routines/${enc(slug)}/triggers`,
1325
+ routineWrite({ label: opts.label }, by),
1326
+ );
1302
1327
  print({ trigger, secret }, () =>
1303
1328
  [
1304
1329
  `Made trigger ${trigger.id} (${trigger.label}) for ${slug}. The secret is shown once; the board keeps only its hash:`,
@@ -1314,14 +1339,18 @@ const commands = {
1314
1339
  await call(
1315
1340
  'DELETE',
1316
1341
  `routines/${enc(need(args[1], 'routine'))}/triggers/${enc(need(args[2], 'trigger id'))}`,
1317
- {},
1342
+ routineWrite({}, by),
1318
1343
  );
1319
1344
  print({ ok: true }, () => 'Revoked.');
1320
1345
  return;
1321
1346
  }
1322
1347
  if (sub === 'cap') {
1323
1348
  // The cap for all routines a day (CLD-199), without pausing or resuming them.
1324
- const { settings } = await call('PATCH', 'routines/settings', { dailyCap: Number(need(args[1], 'runs a day')) });
1349
+ const { settings } = await call(
1350
+ 'PATCH',
1351
+ 'routines/settings',
1352
+ routineWrite({ dailyCap: Number(need(args[1], 'runs a day')) }, by),
1353
+ );
1325
1354
  print({ settings }, (d) => `All routines can run ${d.settings.dailyCap} times a day.`);
1326
1355
  return;
1327
1356
  }
@@ -1329,10 +1358,13 @@ const commands = {
1329
1358
  const { settings } = await call(
1330
1359
  'PATCH',
1331
1360
  'routines/settings',
1332
- clean({
1333
- paused: sub === 'pause',
1334
- dailyCap: opts['daily-all'] === undefined ? undefined : Number(opts['daily-all']),
1335
- }),
1361
+ routineWrite(
1362
+ clean({
1363
+ paused: sub === 'pause',
1364
+ dailyCap: opts['daily-all'] === undefined ? undefined : Number(opts['daily-all']),
1365
+ }),
1366
+ by,
1367
+ ),
1336
1368
  );
1337
1369
  print({ settings }, () => (settings.paused ? 'All routines are paused.' : 'Routines can run.'));
1338
1370
  return;