breakaway 1.4.0-main.2 → 1.4.0-main.21
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 +4 -1
- package/scripts/board-files.mjs +18 -0
- package/scripts/deploy-plan.mjs +135 -0
- package/scripts/lib/deploy-plan.js +102 -0
- package/scripts/lib/package-release.js +71 -0
- package/scripts/package-release.mjs +52 -0
- package/scripts/tasks/cli.js +132 -9
- package/scripts/tasks/init.js +3 -562
- package/scripts/tasks/pipeline.js +747 -0
- package/scripts/tasks.mjs +73 -8
- package/src/cli-version.js +2 -2
- package/src/init.js +590 -0
- package/src/packages.js +56 -0
- package/src/prompt.js +1 -1
- package/src/repos.js +28 -9
- package/src/specs.js +96 -0
- package/template/pipeline/deploy.yml +178 -0
- package/template/pipeline/promote.yml +203 -0
- package/template/pipeline/release.yml +212 -0
- package/template/pipeline/rollback.yml +98 -0
package/scripts/tasks.mjs
CHANGED
|
@@ -42,7 +42,14 @@ import { promptPathOf } from '../src/repos.js';
|
|
|
42
42
|
import { hookFailure, sessionProxy, routeThroughSessionProxy } from './tasks/proxy.js';
|
|
43
43
|
import { githubFromRemote, inRepo, pickRepo } from './tasks/repo.js';
|
|
44
44
|
import { NO_TERMINAL, ask as askIn } from './tasks/ask.js';
|
|
45
|
-
import {
|
|
45
|
+
import {
|
|
46
|
+
CLI_PACKAGE,
|
|
47
|
+
PROMPT_SECTIONS,
|
|
48
|
+
initCommitMessage,
|
|
49
|
+
initPlan,
|
|
50
|
+
machineTaskrc,
|
|
51
|
+
promptSections,
|
|
52
|
+
} from './tasks/init.js';
|
|
46
53
|
import {
|
|
47
54
|
chaseRequest,
|
|
48
55
|
chaseSummary,
|
|
@@ -54,7 +61,12 @@ import {
|
|
|
54
61
|
generalAgentRequest,
|
|
55
62
|
generalAgentSummary,
|
|
56
63
|
githubRequest,
|
|
64
|
+
specLines,
|
|
65
|
+
specListLines,
|
|
66
|
+
specRequest,
|
|
67
|
+
specsRequest,
|
|
57
68
|
ideaTask,
|
|
69
|
+
packageReleaseRequest,
|
|
58
70
|
pullAgentRequest,
|
|
59
71
|
pullAgentSummary,
|
|
60
72
|
reviewRequest,
|
|
@@ -147,6 +159,8 @@ Reading (list, next, claim, and add work in this checkout's repos
|
|
|
147
159
|
agents new "<prompt>" start an agent from a prompt: it makes its own task [--image <file>]… [--repo <slug>] [--force] (owner)
|
|
148
160
|
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)
|
|
149
161
|
agents new --next minor|major ["<note>"] start an agent that sets package.json to the next minor or major release; the board writes its prompt [--repo <slug>] [--force] (owner)
|
|
162
|
+
agents new --spec <path> "<what should change>" start an agent that changes a spec as you ask and brings the tasks that
|
|
163
|
+
link it in line; the board writes its prompt [--repo <slug>] [--force] (owner)
|
|
150
164
|
agents start <ref> start a Claude cloud agent on a task [--note <text>] [--force]
|
|
151
165
|
agents refine <ref> start an agent that improves a task, not builds it --note <what to look at or change> [--force]
|
|
152
166
|
agents plan [<plan>] your Claude plan and what it allows; pro, max5, or max20 picks one (owner) and sets the limits to its defaults
|
|
@@ -165,6 +179,10 @@ Reading (list, next, claim, and add work in this checkout's repos
|
|
|
165
179
|
github review <n> start an agent that reviews a pull request that can merge as it stands, on the task it closes, as
|
|
166
180
|
Review with an agent does; on a Dependabot one it tests the update, as Safe to merge? does
|
|
167
181
|
(owner) [--note <text>] [--repo <slug>] [--force]
|
|
182
|
+
github release <pre-release> release a package's pre-release (1.4.0-main.5) as its stable on latest, as Release on the
|
|
183
|
+
GitHub page does: the board starts release.yml's stable job, and npm waits for your 2FA (owner) [--repo <slug>]
|
|
184
|
+
specs the repository's specs, newest first: each one's status and its tasks [--repo <slug>]
|
|
185
|
+
specs show <path> one spec: its status, last change, Markdown, and the tasks that link it [--repo <slug>]
|
|
168
186
|
github the checkout's repository on GitHub: open pull requests, checks, reviews, CI, deploys, alerts [--sync] [--repo <slug>]
|
|
169
187
|
hook session|wait the Claude Code session hooks a repository's .claude/settings.json runs (npx breakaway hook session)
|
|
170
188
|
health the server's state
|
|
@@ -236,7 +254,9 @@ Working
|
|
|
236
254
|
repos modify <slug> change one (owner): --area <project:PREFIX> adds an area, --remove-area <project> drops one with no tasks, --name, --branch, --github,
|
|
237
255
|
--agents-max <n|none> and --agents-hourly <n|none> cap its agents under the board's shared limits,
|
|
238
256
|
--prompt <path|none> says where its agent prompt is in its checkout (default tools/tasks/routine-prompt.md)
|
|
239
|
-
--
|
|
257
|
+
--specs <dir|none> says where its specs are (default docs/specs)
|
|
258
|
+
--pipeline <file.json|none> sets its deploy pipeline ({"workers": {"staging", "production"}, "package": "<npm name>", "workflows": {...}, "deployPaths"},
|
|
259
|
+
with workers, package, or both) or clears it
|
|
240
260
|
features add <slug> new feature: its tasks join by carrying <slug> as a tag [--title <text>]
|
|
241
261
|
[--brief <text> | --brief-file <path>] [--release <x.y.z>] (agents add one without a release)
|
|
242
262
|
--from <ref> (owner): made from the group <ref> is in on the Dependencies view: its open tasks
|
|
@@ -268,6 +288,12 @@ Install repository (no board needed: the files and steps that deploy a board
|
|
|
268
288
|
[--channel stable|main] [--version <release>]
|
|
269
289
|
install resolve|check|config|previous|healthy|update the steps those workflows run (docs in the install's README)
|
|
270
290
|
|
|
291
|
+
Deploy and release flows (no board needed: run in the checkout of the repository that deploys or publishes)
|
|
292
|
+
pipeline init render .github/breakaway-pipeline.json into Deploy, Promote, and Roll back (for its workers),
|
|
293
|
+
.github/deploy-paths.json, and Release (for its npm package); without the config it prints an
|
|
294
|
+
example. Never overwrites a file [--update] replaces what it rendered before [--dry-run]
|
|
295
|
+
pipeline check say whether the config is sound and the workflows are what it renders now (exits 1 if not)
|
|
296
|
+
|
|
271
297
|
Repositories
|
|
272
298
|
The checkout's repository is the one its origin remote names (git remote get-url origin), matched
|
|
273
299
|
against repos. --repo <slug> or BREAKAWAY_REPO=<slug> picks another, --all shows every repository
|
|
@@ -792,11 +818,13 @@ const commands = {
|
|
|
792
818
|
if (sub === 'new') {
|
|
793
819
|
const decision = typeof opts.decision === 'string' ? opts.decision : null;
|
|
794
820
|
const next = typeof opts.next === 'string' ? opts.next : null;
|
|
821
|
+
const spec = typeof opts.spec === 'string' ? opts.spec : null;
|
|
795
822
|
const built = generalAgentRequest(args.slice(1).join(' '), {
|
|
796
823
|
// From a decision, the board runs it in the decision's repository unless --repo says otherwise.
|
|
797
824
|
repo: opts.repo ?? (decision ? null : (await checkoutRepo()).slug),
|
|
798
825
|
decision,
|
|
799
826
|
next,
|
|
827
|
+
spec,
|
|
800
828
|
force: Boolean(opts.force),
|
|
801
829
|
by: opts.as ?? setting('AGENT'),
|
|
802
830
|
});
|
|
@@ -809,7 +837,7 @@ const commands = {
|
|
|
809
837
|
const image = await upload(ref(answer.task), file);
|
|
810
838
|
if (!opts.json) console.log(`Attached ${image.name} (${Math.ceil(image.size / 1024)} KB).`);
|
|
811
839
|
}
|
|
812
|
-
print(answer, (d) => generalAgentSummary(d, { next }));
|
|
840
|
+
print(answer, (d) => generalAgentSummary(d, { next, spec }));
|
|
813
841
|
return;
|
|
814
842
|
}
|
|
815
843
|
if (sub === 'start') {
|
|
@@ -935,9 +963,14 @@ const commands = {
|
|
|
935
963
|
if (sub === 'modify') {
|
|
936
964
|
const slug = need(args[1], 'repository');
|
|
937
965
|
const change = fields();
|
|
966
|
+
const routineFlags =
|
|
967
|
+
opts['agents-max'] !== undefined || opts['agents-hourly'] !== undefined || opts.prompt !== undefined;
|
|
968
|
+
const current =
|
|
969
|
+
routineFlags || opts.specs !== undefined
|
|
970
|
+
? (await call('GET', 'repos')).repos.find((r) => r.slug === slug.toLowerCase())
|
|
971
|
+
: null;
|
|
938
972
|
// Its caps on agents, under the board's shared limits, and where its agent prompt is (CLD-127): kept with the rest of its routine settings.
|
|
939
|
-
if (
|
|
940
|
-
const current = (await call('GET', 'repos')).repos.find((r) => r.slug === slug.toLowerCase());
|
|
973
|
+
if (routineFlags) {
|
|
941
974
|
const routine = { ...current?.routine };
|
|
942
975
|
for (const [flag, key] of [
|
|
943
976
|
['agents-max', 'max'],
|
|
@@ -948,6 +981,9 @@ const commands = {
|
|
|
948
981
|
if (opts.prompt !== undefined) routine.prompt = opts.prompt === 'none' ? null : String(opts.prompt);
|
|
949
982
|
change.routine = routine;
|
|
950
983
|
}
|
|
984
|
+
// Where its specs are (IDEA-31), kept with the rest of its settings; none goes back to docs/specs.
|
|
985
|
+
if (opts.specs !== undefined)
|
|
986
|
+
change.settings = { ...current?.settings, specs: opts.specs === 'none' ? null : String(opts.specs) };
|
|
951
987
|
// Its deploy pipeline (BRK-44): a JSON file, or none to clear it; the board refuses one it couldn't use, with the reason.
|
|
952
988
|
if (opts.pipeline !== undefined) {
|
|
953
989
|
if (opts.pipeline === 'none') change.pipeline = null;
|
|
@@ -1080,6 +1116,17 @@ const commands = {
|
|
|
1080
1116
|
].join('\n'),
|
|
1081
1117
|
);
|
|
1082
1118
|
},
|
|
1119
|
+
async specs() {
|
|
1120
|
+
// A repository's specs (IDEA-31), read from its default branch on GitHub: the checkout's unless --repo names another.
|
|
1121
|
+
const { slug } = await checkoutRepo();
|
|
1122
|
+
if (args[0] === 'show') {
|
|
1123
|
+
const built = specRequest(args.slice(1).join(' ') || undefined, slug);
|
|
1124
|
+
if (built.error || !built.request) fail(built.error ?? 'bad request');
|
|
1125
|
+
print(await call(...built.request), (d) => specLines(d).join('\n'));
|
|
1126
|
+
return;
|
|
1127
|
+
}
|
|
1128
|
+
print(await call(...specsRequest(slug)), (d) => specListLines(d).join('\n'));
|
|
1129
|
+
},
|
|
1083
1130
|
async features() {
|
|
1084
1131
|
const sub = args[0];
|
|
1085
1132
|
const body = () =>
|
|
@@ -1381,6 +1428,20 @@ const commands = {
|
|
|
1381
1428
|
print(answer, (a) => pullAgentSummary(action, args[1].replace(/^#/u, ''), a));
|
|
1382
1429
|
return;
|
|
1383
1430
|
}
|
|
1431
|
+
if (action === 'release') {
|
|
1432
|
+
const built = packageReleaseRequest(args[1], {
|
|
1433
|
+
repo: (await checkoutRepo()).slug,
|
|
1434
|
+
by: opts.as ?? setting('AGENT'),
|
|
1435
|
+
});
|
|
1436
|
+
if (built.error || !built.request) fail(built.error ?? 'bad request');
|
|
1437
|
+
const answer = await call(...built.request);
|
|
1438
|
+
print(
|
|
1439
|
+
answer,
|
|
1440
|
+
() =>
|
|
1441
|
+
`Started ${answer.workflow}'s stable job for ${args[1]}. It stages the stable on npm's latest, where it waits for your approval with 2FA.`,
|
|
1442
|
+
);
|
|
1443
|
+
return;
|
|
1444
|
+
}
|
|
1384
1445
|
const g = await call(...githubRequest((await checkoutRepo()).slug, { sync: Boolean(opts.sync) }));
|
|
1385
1446
|
print(g, (d) => {
|
|
1386
1447
|
if (!d.connected) return "GitHub isn't connected yet: open the GitHub view on the board (docs/tasks.md#github).";
|
|
@@ -1742,7 +1803,7 @@ async function refreshMachineTaskrc() {
|
|
|
1742
1803
|
|
|
1743
1804
|
/**
|
|
1744
1805
|
* repos init (CLD-191): clone the registered repository `slug` (or use the checkout at --dir), add the files
|
|
1745
|
-
* the board's agents need (
|
|
1806
|
+
* the board's agents need (src/init.js says which; nothing that's there is overwritten), and push
|
|
1746
1807
|
* them: the first commit on the default branch of an empty repository, or a pull request otherwise.
|
|
1747
1808
|
*/
|
|
1748
1809
|
async function initRepo(slug) {
|
|
@@ -1894,7 +1955,8 @@ async function initRepo(slug) {
|
|
|
1894
1955
|
`git add failed in ${dir}: ${String(e.stderr || e.message).trim()}. Nothing was committed. Fix that, then run repos init again (it leaves what's there alone), or use git checkout ${repo.defaultBranch || 'main'} && git branch -D ${work} to start over.`,
|
|
1895
1956
|
);
|
|
1896
1957
|
}
|
|
1897
|
-
const
|
|
1958
|
+
const first = initCommitMessage(repo.slug);
|
|
1959
|
+
const title = update ? "Update the task board's agent files" : first.title;
|
|
1898
1960
|
git(
|
|
1899
1961
|
'commit',
|
|
1900
1962
|
'-m',
|
|
@@ -1902,7 +1964,7 @@ async function initRepo(slug) {
|
|
|
1902
1964
|
'-m',
|
|
1903
1965
|
update
|
|
1904
1966
|
? `The board's core, skill, release helpers, and Taskwarrior files as they are in ${board ?? 'breakaway'} now, and the session hooks run through npx, so an old copy of the CLI is removed: run it as npx ${CLI_PACKAGE} (CLI version ${CLI_VERSION}). This repository's own files are unchanged. Updated by npx ${CLI_PACKAGE} repos init ${repo.slug} --update.`
|
|
1905
|
-
:
|
|
1967
|
+
: first.body,
|
|
1906
1968
|
);
|
|
1907
1969
|
const pushed = spawnSync('git', ['-C', dir, 'push', '-u', 'origin', empty ? `HEAD:refs/heads/${branch}` : work], {
|
|
1908
1970
|
stdio: 'inherit',
|
|
@@ -2122,6 +2184,9 @@ if (opts.help || command === 'help') {
|
|
|
2122
2184
|
} else if (command === 'install') {
|
|
2123
2185
|
// An install repository's own steps (BRK-9): they need no board, so they run before the board's address is checked.
|
|
2124
2186
|
await (await import('./install/cli.js')).run(args, opts);
|
|
2187
|
+
} else if (command === 'pipeline') {
|
|
2188
|
+
// A repository's deploy and release workflows (BRK-90): rendered from its own config, so they need no board either.
|
|
2189
|
+
process.exitCode = (await import('./tasks/pipeline.js')).run(args, opts);
|
|
2125
2190
|
} else if (!commands[command]) {
|
|
2126
2191
|
fail(`no command "${command}". npx breakaway help lists them.`);
|
|
2127
2192
|
} else if (!BASE && command !== 'init-secrets') {
|
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 = 69;
|
|
8
|
+
export const CLI_FINGERPRINT = 'd7528500bc5e5a4c';
|