@detergent-software/conductor 1.3.0-dev.5 → 1.3.0-dev.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.
- package/CHANGELOG.md +21 -0
- package/README.md +28 -2
- package/build/checkout.js +139 -0
- package/build/checkout.js.map +1 -0
- package/build/cli.js +103 -27
- package/build/cli.js.map +1 -1
- package/build/code-host-cli.js +417 -0
- package/build/code-host-cli.js.map +1 -0
- package/build/code-host-service.js +74 -0
- package/build/code-host-service.js.map +1 -0
- package/build/code-host.js +143 -0
- package/build/code-host.js.map +1 -0
- package/build/conditions.js +9 -6
- package/build/conditions.js.map +1 -1
- package/build/credentials.js +29 -12
- package/build/credentials.js.map +1 -1
- package/build/daemon.js +3 -0
- package/build/daemon.js.map +1 -1
- package/build/help.js +76 -2
- package/build/help.js.map +1 -1
- package/build/landing.js +142 -0
- package/build/landing.js.map +1 -0
- package/build/lib/providers/ado/writes.js +8 -10
- package/build/lib/providers/ado/writes.js.map +1 -1
- package/build/ownership.js +57 -0
- package/build/ownership.js.map +1 -0
- package/build/server.js +74 -6
- package/build/server.js.map +1 -1
- package/build/store.js +8 -0
- package/build/store.js.map +1 -1
- package/build/watchers/ado.js +43 -6
- package/build/watchers/ado.js.map +1 -1
- package/build/watchers/github.js +9 -3
- package/build/watchers/github.js.map +1 -1
- package/build/watchers/index.js +26 -3
- package/build/watchers/index.js.map +1 -1
- package/build/watchers/source.js +17 -0
- package/build/watchers/source.js.map +1 -1
- package/build/worktrees.js +26 -36
- package/build/worktrees.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,27 @@
|
|
|
2
2
|
|
|
3
3
|
Releases are tagged `conductor-v<version>` and staged on npm by `atk-conductor-release` (`pipelines/release-conductor.yml`), which writes each section below. `scripts/check-changelog.mjs` fails when a tag has no section here.
|
|
4
4
|
|
|
5
|
+
## [1.3.0-dev.7](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/branchCompare?baseVersion=GTconductor-v1.3.0-dev.6&targetVersion=GTconductor-v1.3.0-dev.7) (2026-10-14)
|
|
6
|
+
|
|
7
|
+
### Features
|
|
8
|
+
|
|
9
|
+
* **conductor:** conduct land, wait pr verdict, pr threads and reply (ATK-454) ([8c2275b](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/8c2275bef44fd7ce07b887ff470ffd20829f1af4))
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* **conductor:** review fixes for land and keep-armed (ATK-454) ([7200260](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/7200260ee78c334f85abd3cdf6bb509ae12ecd51))
|
|
14
|
+
|
|
15
|
+
## [1.3.0-dev.6](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/branchCompare?baseVersion=GTconductor-v1.3.0-dev.5&targetVersion=GTconductor-v1.3.0-dev.6) (2026-10-14)
|
|
16
|
+
|
|
17
|
+
### Features
|
|
18
|
+
|
|
19
|
+
* **conductor:** conduct state, branch, checkout and pr create (ATK-454) ([626f9eb](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/626f9eb0fb74f454190888c9e4fb420a642a8766))
|
|
20
|
+
|
|
21
|
+
### Bug Fixes
|
|
22
|
+
|
|
23
|
+
* **conductor:** pr help names every check before the push (ATK-454) ([b866334](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/b8663346be93057a499634c35a429b809a2de523))
|
|
24
|
+
* **conductor:** review fixes for the worker verbs (ATK-454) ([6806665](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/68066658c029fab55bdbef56721beb1cb66af810))
|
|
25
|
+
|
|
5
26
|
## [1.3.0-dev.5](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/branchCompare?baseVersion=GTconductor-v1.3.0-dev.4&targetVersion=GTconductor-v1.3.0-dev.5) (2026-10-14)
|
|
6
27
|
|
|
7
28
|
### Features
|
package/README.md
CHANGED
|
@@ -51,6 +51,19 @@ conduct note <item> <file> post a phase note on the item and record its p
|
|
|
51
51
|
conduct brief <item> [--json] [--fresh]
|
|
52
52
|
the item's status, links, phase notes and approval, from the tracker (see Briefs)
|
|
53
53
|
conduct wait <condition> wait for an outside event (see Waits)
|
|
54
|
+
conduct state [--repo <repo>] the worktree's branch, changes and PR, and its other repos' (see Worker verbs)
|
|
55
|
+
conduct branch <type> <slug> [--item <item>]
|
|
56
|
+
rename worktree-ATK-n to <type>/atk-n-<slug>
|
|
57
|
+
conduct checkout <owner>/<repo> <type> <slug> [--item <item>]
|
|
58
|
+
clone another GitHub repo into .scratch/repos/<repo> for the item
|
|
59
|
+
conduct pr create --title <title> --body-file <file> [--repo <repo>] [--item <item>] [--dry-run]
|
|
60
|
+
check, push and open the worker's PR (see Worker verbs)
|
|
61
|
+
conduct pr threads <pr> [--repo <repo>]
|
|
62
|
+
the PR's threads, with the ones that wait on the worker
|
|
63
|
+
conduct land <pr> --reason <why> [--mode auto|review] [--rearm] [--tier S|M|L] [--repo <repo>]
|
|
64
|
+
arm the worker's PR to land and give its first verdict (see Worker verbs)
|
|
65
|
+
conduct reply <pr> [<thread>] --body-file <file> [--repo <repo>]
|
|
66
|
+
answer on the worker's PR, headed as a note
|
|
54
67
|
conduct train [<project>] [--dry-run]
|
|
55
68
|
merge the waiting skill publishes into one registry PR (see Skill trains)
|
|
56
69
|
conduct ports [<path>] [--prefer <slot>] [--json]
|
|
@@ -124,6 +137,16 @@ A brief is kept for 30 seconds. Anything the daemon sees happen to the item drop
|
|
|
124
137
|
| 3 | no daemon, one without the brief API, or one that couldn't read the tracker (no key, the tracker down) | reads the item from the tracker itself (the Linear MCP, or the work skill's helper) |
|
|
125
138
|
| 1 | refused: no project tracks the item, or the tracker has no such item | reads the message |
|
|
126
139
|
|
|
140
|
+
## Worker verbs
|
|
141
|
+
|
|
142
|
+
Protocol 2's verbs on git and the code host (providers design note, section 4), run in the item's worktree. They come beside the work-item helper's `state`, `rename`, `checkout` and `pr-create`, which stay until the cutover (ATK-457). Each prints one JSON object with `via`: `daemon` when the daemon wrote for the worker that owns the checkout, `local` when it ran in the `conduct` process (no daemon, or one without the route). Exit codes: 0 done, 1 refused or failed (the message says why), 5 a lost login (run `conduct wait login <provider>` and end the turn).
|
|
143
|
+
|
|
144
|
+
- `conduct state`, `conduct branch` and `conduct checkout` are git in the worktree, so they always run here. `state` reads the branch's PR as the project's account (see Accounts). `checkout` clones another GitHub repo into `.scratch/repos/<repo>`, keeps it out of the worktree's git status, and retire checks it for unpushed or uncommitted work (ATK-429).
|
|
145
|
+
- `conduct pr create` checks everything before it pushes, `--dry-run` included: the title carries the item key, the branch carries it too and isn't the base, the description fits the host (ADO 4,000 characters, GitHub 65,536), no PR is open from the branch yet, nothing is uncommitted, the branch is ahead of its base, and commitlint passes (skipped in a clone without it installed, whose own CI lints the PR). Then it pushes from the checkout and has the daemon open the PR (`POST /workers/:id/prs`). The daemon checks ownership again from its own records and the worktree, never from the request: the worker is live, the branch the worktree is on now (or the `--repo` checkout's) carries the worker's item, and the title carries it. It records the PR on the worker (`pr`; another repo's only while the worker has none) and logs `code-host.pr-created`. `conduct branch` renames only the item's own branch, never the base, `main` or another item's.
|
|
146
|
+
- `conduct land <pr>` sets the worker's PR up to land, as the helper's `land` did, and is safe to repeat. The PR must be the worker's (the same checks, and from the branch the checkout is on now, into its base). A brake already on it (a comment, a failed build, conflicts) is reported, and nothing is armed. A Landing note whose landing armed something, with that gone now, means someone cancelled it: `auto-merge-cancelled`, until `--rearm`. `--mode` is held to the profile's landing rules (`work.landing`) for the PR's files as pushed (`origin/<base>...origin/<head>`, fetched first) and the plan's tier. The daemon reads the tier from the item's Plan note; in-process it takes `--tier`, and auto is refused without one. Without `--mode` the rules pick it. Then it arms the host's own landing behind its capability check (`landingCheck`). On ADO that is a +10 vote in auto mode, with auto-complete and noFastForward. On GitHub it is auto-merge with a merge commit in auto mode, where the repo allows it; in review mode nothing is armed. It posts the Landing note last, and a new one whenever the mode changes, then prints the verdict. A failed build comes with its log, and a merge with whether it has two parents. The daemon logs `code-host.landing-armed` with the mode.
|
|
147
|
+
- `conduct wait pr <pr>` (no event word) is the verdict wait, `pr:<host>:<repo>#<n>:verdict`: it holds on the first verdict that isn't `waiting`, with a line such as `pr 2030 build-failed: <pipeline>`. While it waits on a PR whose landing the daemon recorded, the ADO watcher keeps it armed. It queues an expired build again (once per wait) and, in auto mode, restores a cleared vote (never over a negative one), logged as `code-host.kept-armed`. A local wait (no daemon) gives the same verdicts but keeps nothing armed.
|
|
148
|
+
- `conduct pr threads <pr>` lists the PR's threads (on GitHub each comment and review) with `needsAnswer` (ATK-384). `conduct reply <pr> [<thread>]` answers on the worker's own PR: in the ADO thread named, or as a note on the PR. The text is headed `## <work skill> · Reply` unless it starts with a note heading. The daemon logs `code-host.replied`. `--repo` takes `github:<owner>/<repo>` (from its `.scratch/repos` checkout, into its default branch) or `ado:<repository>`; the profile's `codeHost` is the default.
|
|
149
|
+
|
|
127
150
|
## Inbox
|
|
128
151
|
|
|
129
152
|
The `inbox` table holds everything that waits on Jason, across projects. Each item records:
|
|
@@ -303,6 +326,7 @@ A worker waits by posting its note, running `conduct wait …` and ending the tu
|
|
|
303
326
|
|
|
304
327
|
| Verb | Full form | Holds when |
|
|
305
328
|
|---|---|---|
|
|
329
|
+
| `pr 1850` | `pr:ado:Emergent.AgenticToolkit#1850:verdict` | the PR's verdict isn't `waiting` (see Worker verbs); the daemon keeps a recorded landing armed meanwhile |
|
|
306
330
|
| `pr 1850 merged` | `pr:ado:Emergent.AgenticToolkit#1850:merged` | the PR is completed (or abandoned: that resolves it too, as a fact) |
|
|
307
331
|
| `pr 1850 checks` | `…#1850:checks` | no blocking build of the PR's current commit is queued or running (passed or failed; a path-filtered build it doesn't touch counts as passed) |
|
|
308
332
|
| `pr 1850 comment` | `…#1850:comment` | a thread is open whose last word isn't a work-skill note |
|
|
@@ -349,7 +373,7 @@ Each token is kept per account: an az token 20 minutes, renewed when ADO answers
|
|
|
349
373
|
2. While the spell lasts, the account's calls fail at once, asking nothing. Every `watchers.login.intervalSeconds` (60) the daemon probes each lost account again: a fresh token for ADO, the token and `gh api user` for GitHub, and the key and `{ viewer { id } }` for Linear. That is the spell's back-off.
|
|
350
374
|
3. The first probe the provider answers (an answer other than a refusal; a timeout, a network error or a 5xx says nothing) logs `provider.login-restored`, which resolves the item, and calls go through again.
|
|
351
375
|
|
|
352
|
-
`conduct wait login <provider>` waits on the same probe for the worktree's project (`login:<provider>:<project>`), within the `login` watcher's budget. A worker that can't go on without a login posts its note, waits on it and ends its turn; once Jason signs in, the daemon wakes it. **Exit code 5** ("login needed") is
|
|
376
|
+
`conduct wait login <provider>` waits on the same probe for the worktree's project (`login:<provider>:<project>`), within the `login` watcher's budget. A worker that can't go on without a login posts its note, waits on it and ends its turn; once Jason signs in, the daemon wakes it. **Exit code 5** ("login needed") is what the worker verbs (`state`, `pr`, `land`, `reply`) exit with when they hit a lost login, here or in the daemon.
|
|
353
377
|
|
|
354
378
|
## ADO Boards
|
|
355
379
|
|
|
@@ -456,8 +480,10 @@ Everything lives in `~/.conductor` (`CONDUCTOR_HOME` overrides it):
|
|
|
456
480
|
- `POST /console/cache` with `{ "action": "warm" | "cold" | "failed", "contextTokens", "usage": { "cacheRead", "cacheWrite", "input", "output" } | null, "warms" }` or `{ "action": "clear", "contextTokens", "reason" }`: the mod's report on the idle console's prompt cache (see Mod); 201 with the `console.cache` event's id.
|
|
457
481
|
- `GET /fleet`: the workers in flight, with session activity, phase, PR, waits and spend (today, total).
|
|
458
482
|
- `POST /workers` with `{ "item": "ATK-309", "project"?, "probe"?, "prompt"?, "conductor"? }`: launch; answers 202 with the `launching` worker. `GET /workers/:id` shows it with its latest events.
|
|
459
|
-
- `DELETE /workers/:id` (`?force=true`): retire; 409 with the unfinished work when it refuses.
|
|
483
|
+
- `DELETE /workers/:id` (`?force=true`): retire; 409 with the unfinished work (the worktree's, and each `.scratch/repos` checkout's under `checkouts`) when it refuses.
|
|
460
484
|
- `POST /workers/:id/attach`: wake if parked, mark attached, open the tab.
|
|
485
|
+
- `POST /workers/:id/prs` with `{ "title", "body", "repo"? }`: open the worker's PR after the ownership checks (see Worker verbs); 201 with the PR, 409 refused, 503 a lost login (with `login.provider` and `login.command`), 502 the host failed.
|
|
486
|
+
- `POST /workers/:id/landings` with `{ "pr", "reason", "mode"?, "rearm"?, "repo"? }`: `conduct land` for the worker; 200 with the landing and its verdict. `POST /workers/:id/replies` with `{ "pr", "body", "thread"?, "repo"? }`: `conduct reply`; 201. Both answer 409, 503 and 502 as `/prs` does.
|
|
461
487
|
- `POST /items/:id/notes` with `{ "body": "## work-issue · …", "worker"?: 1 }`: post a phase note (see Notes); 201 with the note and the worker. 409 when it won't post it; 502 when the tracker didn't take it, with `posted: "no"` or `"unknown"`.
|
|
462
488
|
- `GET /items/:id/brief` (`?fresh=true`): the item's brief (see Briefs), as `{ "brief": … }`. 404 when the tracker has no such item, 409 when no project tracks it, 502 when the tracker couldn't be read.
|
|
463
489
|
- `GET /inbox`: the open inbox items, oldest first.
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { stripVTControlCharacters } from 'node:util';
|
|
4
|
+
import { ConductorError } from "./errors.js";
|
|
5
|
+
import { githubRepoRef } from "./lib/providers/index.js";
|
|
6
|
+
import { outputOf, run } from "./run.js";
|
|
7
|
+
// Git in a worker's checkouts, for the worker verbs and retire: the item's worktree and the other
|
|
8
|
+
// repos it cloned under .scratch/repos/<repo> (`conduct checkout`).
|
|
9
|
+
// Other repos a worker clones into its worktree (`conduct checkout`), each at
|
|
10
|
+
// <worktree>/.scratch/repos/<repo>.
|
|
11
|
+
export const REPOS_DIR = join('.scratch', 'repos');
|
|
12
|
+
export const runGit = (args, cwd) => run('git', args, { cwd, timeoutMs: 120_000 });
|
|
13
|
+
// The commits on HEAD that aren't on origin/<base>, and the ones on origin/<base> that aren't on
|
|
14
|
+
// HEAD, after fetching the base.
|
|
15
|
+
export async function aheadBehind(git, dir, base) {
|
|
16
|
+
await git(['fetch', '--quiet', 'origin', base], dir);
|
|
17
|
+
const count = async (range) => Number((await out(git, ['rev-list', '--count', range], dir)) ?? 0);
|
|
18
|
+
return { ahead: await count(`origin/${base}..HEAD`), behind: await count(`HEAD..origin/${base}`) };
|
|
19
|
+
}
|
|
20
|
+
// The branch a checkout is on now, or null (detached, or not a checkout).
|
|
21
|
+
export async function branchOf(git, dir) {
|
|
22
|
+
return (await out(git, ['branch', '--show-current'], dir)) || null;
|
|
23
|
+
}
|
|
24
|
+
// A checkout's base: its origin's default branch (refs/remotes/origin/HEAD, which a clone sets).
|
|
25
|
+
export async function checkoutBase(git, dir) {
|
|
26
|
+
const head = await out(git, ['symbolic-ref', '--short', 'refs/remotes/origin/HEAD'], dir);
|
|
27
|
+
if (!head)
|
|
28
|
+
throw new ConductorError(`${dir} has no origin/HEAD`, ['run git remote set-head origin --auto there']);
|
|
29
|
+
return head.replace(/^origin\//, '');
|
|
30
|
+
}
|
|
31
|
+
// commitlint over every commit the PR will carry, from the checkout's root where its config lives:
|
|
32
|
+
// 'passed', or 'skipped' without a commitlint config. Throws with its output when it fails.
|
|
33
|
+
export async function commitlint(dir, base) {
|
|
34
|
+
if (!hasCommitlint(dir))
|
|
35
|
+
return 'skipped (no commitlint config)';
|
|
36
|
+
// npx is a .cmd on Windows; its arguments are constants and the base branch's name.
|
|
37
|
+
const result = await run('npx', ['--no-install', 'commitlint', '--from', `origin/${base}`, '--to', 'HEAD'], {
|
|
38
|
+
cwd: dir,
|
|
39
|
+
shell: process.platform === 'win32',
|
|
40
|
+
timeoutMs: 120_000,
|
|
41
|
+
});
|
|
42
|
+
if (result.code === 0)
|
|
43
|
+
return 'passed';
|
|
44
|
+
const text = stripVTControlCharacters(`${result.stdout}\n${result.stderr}`);
|
|
45
|
+
// A clone that was never installed (`conduct checkout` doesn't) has the config but not the tool:
|
|
46
|
+
// its own CI lints the PR.
|
|
47
|
+
if (/could not determine executable|commitlint: (?:command )?not found|is not recognized/i.test(text)) {
|
|
48
|
+
return 'skipped (commitlint is not installed here)';
|
|
49
|
+
}
|
|
50
|
+
const said = text
|
|
51
|
+
.split(/\r?\n/)
|
|
52
|
+
.filter((line) => line.trim() && !line.startsWith('npm notice'))
|
|
53
|
+
.slice(-30);
|
|
54
|
+
throw new ConductorError(`commitlint failed over origin/${base}..HEAD`, said);
|
|
55
|
+
}
|
|
56
|
+
// Whether HEAD has commits its upstream hasn't (or it has no upstream yet).
|
|
57
|
+
export async function needsPush(git, dir) {
|
|
58
|
+
const upstream = await out(git, ['rev-parse', '--abbrev-ref', '@{upstream}'], dir);
|
|
59
|
+
if (!upstream)
|
|
60
|
+
return true;
|
|
61
|
+
return (await out(git, ['rev-list', '--count', `${upstream}..HEAD`], dir)) !== '0';
|
|
62
|
+
}
|
|
63
|
+
// The other repos checked out under the worktree's .scratch/repos/, with the repo each origin names.
|
|
64
|
+
export async function otherCheckouts(git, worktree) {
|
|
65
|
+
return Promise.all(scratchClones(worktree).map(async (dir) => ({
|
|
66
|
+
dir,
|
|
67
|
+
repo: githubRepoRef(await out(git, ['remote', 'get-url', 'origin'], dir)),
|
|
68
|
+
})));
|
|
69
|
+
}
|
|
70
|
+
// `git status --porcelain=v1 -z` as { code, path }. A rename or copy carries its source path as the
|
|
71
|
+
// next NUL-separated field.
|
|
72
|
+
export function parsePorcelain(text) {
|
|
73
|
+
const fields = text.split('\0');
|
|
74
|
+
const entries = [];
|
|
75
|
+
for (let i = 0; i < fields.length; i++) {
|
|
76
|
+
const field = fields[i] ?? '';
|
|
77
|
+
if (field.length < 4)
|
|
78
|
+
continue;
|
|
79
|
+
const code = field.slice(0, 2);
|
|
80
|
+
entries.push({ code, path: field.slice(3) });
|
|
81
|
+
if (code[0] === 'R' || code[0] === 'C')
|
|
82
|
+
i++;
|
|
83
|
+
}
|
|
84
|
+
return entries;
|
|
85
|
+
}
|
|
86
|
+
// Pushes the branch with an upstream, once more after a failure: a push has failed on a connection
|
|
87
|
+
// reset before.
|
|
88
|
+
export async function push(git, dir, branch, sleep = (ms) => new Promise((done) => setTimeout(done, ms))) {
|
|
89
|
+
const args = ['push', '--quiet', '-u', 'origin', branch];
|
|
90
|
+
const first = await git(args, dir);
|
|
91
|
+
if (first.code === 0)
|
|
92
|
+
return { attempts: 1 };
|
|
93
|
+
await sleep(5_000);
|
|
94
|
+
const second = await git(args, dir);
|
|
95
|
+
if (second.code === 0)
|
|
96
|
+
return { attempts: 2, firstError: outputOf(first, 300) };
|
|
97
|
+
throw new ConductorError(`git push of ${branch} failed twice`, [outputOf(second, 500)]);
|
|
98
|
+
}
|
|
99
|
+
// Uncommitted changes, without files whose only difference from HEAD is their line endings (a
|
|
100
|
+
// checkout with core.autocrlf can show those as modified).
|
|
101
|
+
export async function realDirty(git, dir) {
|
|
102
|
+
const status = await git(['status', '--porcelain=v1', '-z'], dir);
|
|
103
|
+
if (status.code !== 0)
|
|
104
|
+
throw new ConductorError(`git status failed in ${dir}`, [outputOf(status, 300)]);
|
|
105
|
+
const dirty = [];
|
|
106
|
+
const lineEndingOnly = [];
|
|
107
|
+
for (const entry of parsePorcelain(status.stdout)) {
|
|
108
|
+
const same = /^[ M][ M]$/.test(entry.code) &&
|
|
109
|
+
(await git(['diff', '--quiet', '--ignore-cr-at-eol', 'HEAD', '--', entry.path], dir)).code === 0;
|
|
110
|
+
(same ? lineEndingOnly : dirty).push(entry.path);
|
|
111
|
+
}
|
|
112
|
+
return { dirty, lineEndingOnly };
|
|
113
|
+
}
|
|
114
|
+
// The clones under the worktree's .scratch/repos/, by directory.
|
|
115
|
+
export function scratchClones(worktree) {
|
|
116
|
+
const root = join(worktree, REPOS_DIR);
|
|
117
|
+
if (!existsSync(root))
|
|
118
|
+
return [];
|
|
119
|
+
return readdirSync(root, { withFileTypes: true })
|
|
120
|
+
.filter((entry) => entry.isDirectory() && existsSync(join(root, entry.name, '.git')))
|
|
121
|
+
.map((entry) => join(root, entry.name));
|
|
122
|
+
}
|
|
123
|
+
function hasCommitlint(dir) {
|
|
124
|
+
const configured = readdirSync(dir).some((name) => /^(?:commitlint\.config\.[cm]?[jt]s|\.commitlintrc(?:\.(?:json|ya?ml|[cm]?[jt]s))?)$/.test(name));
|
|
125
|
+
if (configured)
|
|
126
|
+
return true;
|
|
127
|
+
try {
|
|
128
|
+
return 'commitlint' in JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8'));
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// A git command's output, trimmed, or null when it failed.
|
|
135
|
+
async function out(git, args, dir) {
|
|
136
|
+
const result = await git(args, dir);
|
|
137
|
+
return result.code === 0 ? result.stdout.trim() : null;
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=checkout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checkout.js","sourceRoot":"","sources":["../src/checkout.ts"],"names":[],"mappings":"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"}
|
package/build/cli.js
CHANGED
|
@@ -8,6 +8,7 @@ import { z } from 'zod';
|
|
|
8
8
|
import { briefCommand } from "./brief-cli.js";
|
|
9
9
|
import { heldLocks, lockCommand, lockLine, portsCommand } from "./broker-cli.js";
|
|
10
10
|
import { callApi, NotRunningError, ping, readEndpoint } from "./client.js";
|
|
11
|
+
import { branchCommand, checkoutCommand, landCommand, prCreateCommand, prThreadsCommand, replyCommand, stateCommand, } from "./code-host-cli.js";
|
|
11
12
|
import { configSchema, loadConfig } from "./config.js";
|
|
12
13
|
import { claimConsole, CONSOLE_ARGS, CONSOLE_WINDOW, consoleProject, consoleTabArgs, escalationLines, escalationSchema, handledSchema, } from "./console.js";
|
|
13
14
|
import { AlreadyRunningError, logCrashes, startDaemon } from "./daemon.js";
|
|
@@ -63,15 +64,18 @@ const workerDetailBody = z.object({
|
|
|
63
64
|
events: z.array(z.object({ data: z.record(z.string(), z.unknown()), kind: z.string() })),
|
|
64
65
|
worker: workerSchema,
|
|
65
66
|
});
|
|
67
|
+
const workSchema = z.object({
|
|
68
|
+
dirty: z.array(z.string()).optional(),
|
|
69
|
+
unknown: z.string().optional(),
|
|
70
|
+
unpushed: z.number().optional(),
|
|
71
|
+
});
|
|
66
72
|
const retireBody = z.object({
|
|
67
73
|
outcome: z.enum(['partial', 'refused', 'retired']),
|
|
68
74
|
reason: z.string().optional(),
|
|
69
75
|
steps: z
|
|
70
76
|
.array(z.object({ error: z.string().optional(), note: z.string().optional(), ok: z.boolean(), step: z.string() }))
|
|
71
77
|
.optional(),
|
|
72
|
-
work: z
|
|
73
|
-
.object({ dirty: z.array(z.string()).optional(), unknown: z.string().optional(), unpushed: z.number().optional() })
|
|
74
|
-
.optional(),
|
|
78
|
+
work: workSchema.extend({ checkouts: z.record(z.string(), workSchema).optional() }).optional(),
|
|
75
79
|
worker: workerSchema,
|
|
76
80
|
});
|
|
77
81
|
const attachBody = z.object({ resumed: z.boolean(), tab: z.string().nullable(), worker: workerSchema });
|
|
@@ -421,6 +425,7 @@ async function main(argv) {
|
|
|
421
425
|
args: argv,
|
|
422
426
|
options: {
|
|
423
427
|
'allow-downgrade': { type: 'boolean' },
|
|
428
|
+
'body-file': { type: 'string' },
|
|
424
429
|
channel: { type: 'string' },
|
|
425
430
|
conductor: { type: 'string' },
|
|
426
431
|
console: { type: 'boolean' },
|
|
@@ -431,19 +436,26 @@ async function main(argv) {
|
|
|
431
436
|
fresh: { type: 'boolean' },
|
|
432
437
|
help: { short: 'h', type: 'boolean' },
|
|
433
438
|
here: { type: 'boolean' },
|
|
439
|
+
item: { type: 'string' },
|
|
434
440
|
json: { type: 'boolean' },
|
|
435
441
|
kind: { type: 'string' },
|
|
442
|
+
mode: { type: 'string' },
|
|
436
443
|
owner: { type: 'string' },
|
|
437
444
|
prefer: { type: 'string' },
|
|
438
445
|
probe: { type: 'boolean' },
|
|
439
446
|
project: { type: 'string' },
|
|
440
447
|
prompt: { type: 'string' },
|
|
441
448
|
raise: { type: 'boolean' },
|
|
449
|
+
rearm: { type: 'boolean' },
|
|
450
|
+
reason: { type: 'string' },
|
|
442
451
|
remove: { type: 'boolean' },
|
|
452
|
+
repo: { type: 'string' },
|
|
443
453
|
since: { type: 'string' },
|
|
444
454
|
slots: { type: 'string' },
|
|
445
455
|
tag: { type: 'string' },
|
|
446
456
|
text: { type: 'string' },
|
|
457
|
+
tier: { type: 'string' },
|
|
458
|
+
title: { type: 'string' },
|
|
447
459
|
until: { type: 'string' },
|
|
448
460
|
version: { short: 'v', type: 'boolean' },
|
|
449
461
|
wait: { type: 'string' },
|
|
@@ -452,7 +464,9 @@ async function main(argv) {
|
|
|
452
464
|
},
|
|
453
465
|
}));
|
|
454
466
|
const paths = conductorPaths();
|
|
455
|
-
const [command, sub, arg] = positionals;
|
|
467
|
+
const [command, sub, arg, fourth] = positionals;
|
|
468
|
+
const item = values.item === undefined ? {} : { item: values.item };
|
|
469
|
+
const repo = values.repo === undefined ? {} : { repo: values.repo };
|
|
456
470
|
if (values.version)
|
|
457
471
|
return out(version());
|
|
458
472
|
if (command === undefined || command === 'help')
|
|
@@ -470,6 +484,11 @@ async function main(argv) {
|
|
|
470
484
|
if (sub === undefined)
|
|
471
485
|
throw usageError('attach');
|
|
472
486
|
return attach(paths, sub);
|
|
487
|
+
case 'branch':
|
|
488
|
+
if (sub === undefined || arg === undefined)
|
|
489
|
+
throw usageError('branch');
|
|
490
|
+
process.exitCode = await branchCommand(sub, arg, item, verbIo());
|
|
491
|
+
return;
|
|
473
492
|
case 'brief':
|
|
474
493
|
if (sub === undefined)
|
|
475
494
|
throw usageError('brief');
|
|
@@ -478,6 +497,11 @@ async function main(argv) {
|
|
|
478
497
|
out,
|
|
479
498
|
});
|
|
480
499
|
return;
|
|
500
|
+
case 'checkout':
|
|
501
|
+
if (sub === undefined || arg === undefined || fourth === undefined)
|
|
502
|
+
throw usageError('checkout');
|
|
503
|
+
process.exitCode = await checkoutCommand(sub, arg, fourth, item, verbIo());
|
|
504
|
+
return;
|
|
481
505
|
case 'console':
|
|
482
506
|
return consoleCommand(paths, {
|
|
483
507
|
force: values.force === true,
|
|
@@ -496,6 +520,16 @@ async function main(argv) {
|
|
|
496
520
|
// CONDUCTOR_CONSOLE as well: a console still running a mod from before --console (until
|
|
497
521
|
// /reload-plugins after an upgrade) starts this package's new build, in the console's env.
|
|
498
522
|
return events(paths, values.follow === true, values.since, values.console === true || process.env.CONDUCTOR_CONSOLE === '1');
|
|
523
|
+
case 'focus':
|
|
524
|
+
if (sub === undefined || values.until === undefined)
|
|
525
|
+
throw usageError('focus');
|
|
526
|
+
return override(paths, {
|
|
527
|
+
action: 'focus',
|
|
528
|
+
project: sub,
|
|
529
|
+
...(values.slots === undefined ? {} : { slots: wholeNumber('--slots', values.slots) }),
|
|
530
|
+
until: untilOf(values.until),
|
|
531
|
+
...(values.weight === undefined ? {} : { weight: wholeNumber('--weight', values.weight) }),
|
|
532
|
+
});
|
|
499
533
|
case 'history':
|
|
500
534
|
case 'retro':
|
|
501
535
|
return history(paths, command, sub, {
|
|
@@ -512,6 +546,26 @@ async function main(argv) {
|
|
|
512
546
|
: installMod(fileURLToPath(new URL('../mod', import.meta.url)), modDeps());
|
|
513
547
|
case 'install-startup':
|
|
514
548
|
return values.remove ? uninstall() : install();
|
|
549
|
+
case 'land': {
|
|
550
|
+
if (sub === undefined || values.reason === undefined)
|
|
551
|
+
throw usageError('land');
|
|
552
|
+
const mode = values.mode;
|
|
553
|
+
if (mode !== undefined && mode !== 'auto' && mode !== 'review')
|
|
554
|
+
throw new ConductorError('--mode takes auto or review');
|
|
555
|
+
const tier = values.tier;
|
|
556
|
+
if (tier !== undefined && tier !== 'S' && tier !== 'M' && tier !== 'L')
|
|
557
|
+
throw new ConductorError('--tier takes S, M or L');
|
|
558
|
+
process.exitCode = await landCommand(paths, {
|
|
559
|
+
pr: wholeNumber('<pr>', sub),
|
|
560
|
+
rearm: values.rearm === true,
|
|
561
|
+
reason: values.reason,
|
|
562
|
+
...item,
|
|
563
|
+
...repo,
|
|
564
|
+
...(mode === undefined ? {} : { mode }),
|
|
565
|
+
...(tier === undefined ? {} : { tier }),
|
|
566
|
+
}, verbIo());
|
|
567
|
+
return;
|
|
568
|
+
}
|
|
515
569
|
case 'launch':
|
|
516
570
|
if (sub === undefined)
|
|
517
571
|
throw usageError('launch');
|
|
@@ -547,22 +601,15 @@ async function main(argv) {
|
|
|
547
601
|
if (sub === 'test')
|
|
548
602
|
return notifyTest(paths, { channel: values.channel, kind: values.kind, raise: values.raise === true });
|
|
549
603
|
throw usageError('notify');
|
|
550
|
-
case '
|
|
551
|
-
if (sub === '
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
return override(paths, {
|
|
560
|
-
action: 'focus',
|
|
561
|
-
project: sub,
|
|
562
|
-
...(values.slots === undefined ? {} : { slots: wholeNumber('--slots', values.slots) }),
|
|
563
|
-
until: untilOf(values.until),
|
|
564
|
-
...(values.weight === undefined ? {} : { weight: wholeNumber('--weight', values.weight) }),
|
|
565
|
-
});
|
|
604
|
+
case 'pr':
|
|
605
|
+
if (sub === 'threads' && arg !== undefined) {
|
|
606
|
+
process.exitCode = await prThreadsCommand(paths, { pr: wholeNumber('<pr>', arg), ...repo }, verbIo());
|
|
607
|
+
return;
|
|
608
|
+
}
|
|
609
|
+
if (sub !== 'create' || values.title === undefined || values['body-file'] === undefined)
|
|
610
|
+
throw usageError('pr');
|
|
611
|
+
process.exitCode = await prCreateCommand(paths, { bodyFile: values['body-file'], dryRun: values['dry-run'] === true, title: values.title, ...item, ...repo }, verbIo());
|
|
612
|
+
return;
|
|
566
613
|
case 'pause':
|
|
567
614
|
return override(paths, {
|
|
568
615
|
action: 'pause',
|
|
@@ -575,22 +622,44 @@ async function main(argv) {
|
|
|
575
622
|
...(values.prefer === undefined ? {} : { prefer: wholeNumber('--prefer', values.prefer) }),
|
|
576
623
|
}, brokerIo());
|
|
577
624
|
return;
|
|
625
|
+
case 'project':
|
|
626
|
+
if (sub === 'add' && arg !== undefined)
|
|
627
|
+
return addProject(paths, arg);
|
|
628
|
+
if (sub === 'list')
|
|
629
|
+
return listProjects(paths, values.json === true);
|
|
630
|
+
throw usageError('project');
|
|
578
631
|
case 'queue':
|
|
579
632
|
return queue(paths, values.json === true, values.fresh === true);
|
|
633
|
+
case 'retire':
|
|
634
|
+
if (sub === undefined)
|
|
635
|
+
throw usageError('retire');
|
|
636
|
+
return retire(paths, sub, values.force === true);
|
|
580
637
|
case 'rules':
|
|
581
638
|
if (sub === undefined)
|
|
582
639
|
return rules(paths, values.json === true);
|
|
583
640
|
if (sub === 'test' && arg !== undefined)
|
|
584
641
|
return rulesTest(paths, arg, values.json === true);
|
|
585
642
|
throw usageError('rules');
|
|
586
|
-
case '
|
|
587
|
-
if (sub === undefined)
|
|
588
|
-
throw usageError('
|
|
589
|
-
|
|
643
|
+
case 'reply':
|
|
644
|
+
if (sub === undefined || values['body-file'] === undefined)
|
|
645
|
+
throw usageError('reply');
|
|
646
|
+
process.exitCode = await replyCommand(paths, {
|
|
647
|
+
bodyFile: values['body-file'],
|
|
648
|
+
pr: wholeNumber('<pr>', sub),
|
|
649
|
+
thread: arg === undefined ? null : wholeNumber('<thread>', arg),
|
|
650
|
+
...item,
|
|
651
|
+
...repo,
|
|
652
|
+
}, verbIo());
|
|
653
|
+
return;
|
|
590
654
|
case 'serve':
|
|
591
655
|
return serve(paths);
|
|
592
656
|
case 'start':
|
|
593
657
|
return start(paths);
|
|
658
|
+
case 'state':
|
|
659
|
+
if (sub !== undefined)
|
|
660
|
+
throw usageError('state');
|
|
661
|
+
process.exitCode = await stateCommand(paths, repo, verbIo());
|
|
662
|
+
return;
|
|
594
663
|
case 'status':
|
|
595
664
|
return status(paths, values.json === true);
|
|
596
665
|
case 'stop':
|
|
@@ -814,10 +883,14 @@ async function retire(paths, ref, force) {
|
|
|
814
883
|
const result = parsed.data;
|
|
815
884
|
const steps = (result.steps ?? []).map((s) => `${s.step}: ${s.ok ? `ok${s.note ? ` (${s.note})` : ''}` : `failed (${s.error ?? '?'})`}`);
|
|
816
885
|
if (result.outcome === 'refused') {
|
|
886
|
+
const lines = (w, where = '') => [
|
|
887
|
+
...(w?.unpushed ? [`${where}${w.unpushed} unpushed commit(s)`] : []),
|
|
888
|
+
...(w?.dirty ? [`${where}uncommitted: ${w.dirty.join(', ')}`] : []),
|
|
889
|
+
...(w?.unknown ? [`${where}not checked: ${w.unknown}`] : []),
|
|
890
|
+
];
|
|
817
891
|
const work = [
|
|
818
|
-
...(result.work
|
|
819
|
-
...(result.work?.
|
|
820
|
-
...(result.work?.unknown ? [`not checked: ${result.work.unknown}`] : []),
|
|
892
|
+
...lines(result.work),
|
|
893
|
+
...Object.entries(result.work?.checkouts ?? {}).flatMap(([name, w]) => lines(w, `.scratch/repos/${name}: `)),
|
|
821
894
|
];
|
|
822
895
|
throw new ConductorError(`refused to retire ${worker.name}: ${result.reason ?? ''}`, work);
|
|
823
896
|
}
|
|
@@ -1146,6 +1219,9 @@ async function upgradeCommand(paths, tag, allowDowngrade) {
|
|
|
1146
1219
|
stopDaemon: () => stopDaemon(paths),
|
|
1147
1220
|
});
|
|
1148
1221
|
}
|
|
1222
|
+
function verbIo() {
|
|
1223
|
+
return { cwd: process.cwd(), err: (line) => process.stderr.write(`${line}\n`), out };
|
|
1224
|
+
}
|
|
1149
1225
|
// Read from disk each time: upgrade asks again after npm has replaced the package.
|
|
1150
1226
|
function version() {
|
|
1151
1227
|
const file = fileURLToPath(new URL('../package.json', import.meta.url));
|