dorfl 0.10.1 → 0.11.1
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/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +99 -21
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +73 -12
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +28 -0
- package/dist/config.js.map +1 -1
- package/dist/do-config.d.ts +21 -1
- package/dist/do-config.d.ts.map +1 -1
- package/dist/do-config.js +19 -0
- package/dist/do-config.js.map +1 -1
- package/dist/do.d.ts +3 -1
- package/dist/do.d.ts.map +1 -1
- package/dist/do.js.map +1 -1
- package/dist/env-config.d.ts.map +1 -1
- package/dist/env-config.js +3 -0
- package/dist/env-config.js.map +1 -1
- package/dist/gc.d.ts +42 -0
- package/dist/gc.d.ts.map +1 -1
- package/dist/gc.js +71 -1
- package/dist/gc.js.map +1 -1
- package/dist/protocol/WORK-CONTRACT.md +11 -2
- package/dist/protocol/spec-template.md +1 -1
- package/dist/protocol/task-template.md +1 -1
- package/dist/repo-config.d.ts +1 -1
- package/dist/repo-config.d.ts.map +1 -1
- package/dist/repo-config.js +19 -2
- package/dist/repo-config.js.map +1 -1
- package/dist/run.d.ts.map +1 -1
- package/dist/run.js +4 -3
- package/dist/run.js.map +1 -1
- package/dist/skills/drive-tasks/SKILL.md +21 -3
- package/dist/skills/setup/protocol/WORK-CONTRACT.md +11 -2
- package/dist/skills/setup/protocol/spec-template.md +1 -1
- package/dist/skills/setup/protocol/task-template.md +1 -1
- package/package.json +1 -1
- package/src/cli.ts +143 -21
- package/src/config.ts +90 -12
- package/src/do-config.ts +38 -0
- package/src/do.ts +3 -1
- package/src/env-config.ts +3 -0
- package/src/gc.ts +108 -1
- package/src/repo-config.ts +19 -1
- package/src/run.ts +4 -3
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAC,OAAO,EAAS,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAC,OAAO,EAAS,MAAM,WAAW,CAAC;AAyH1C,OAAO,EAAiB,KAAK,YAAY,EAAC,MAAM,sBAAsB,CAAC;AAwCvE,OAAO,EAAgB,KAAK,mBAAmB,EAAC,MAAM,qBAAqB,CAAC;AA8yB5E;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC/B,MAAM,EAAE,YAAY,EACpB,MAAM,EAAE,OAAO,GACb,MAAM,CAoCR;AA6RD,wBAAgB,YAAY,IAAI,OAAO,CAiiHtC;AAaD;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACpC,MAAM,EAAE,mBAAmB,EAC3B,KAAK,EAAE,OAAO,GACZ,MAAM,CAyBR;AA+FD;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,MAAM,CAAC,IAAI,GAAE,MAAM,EAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAkBzE"}
|
package/dist/cli.js
CHANGED
|
@@ -35,7 +35,7 @@ import { shouldUseColor } from './output.js';
|
|
|
35
35
|
import { resolveRepoConfig, resolveRepoConfigFromLoaded, loadRepoConfigFromContent, REPO_CONFIG_FILENAME, } from './repo-config.js';
|
|
36
36
|
import { ensureMirrorMain, readRepoConfigFromMirrorMain, encodeRepoKey, } from './repo-mirror.js';
|
|
37
37
|
import { identityEnv } from './identity.js';
|
|
38
|
-
import { harnessFlagOverrides, doFlagOverrides, doNeedsAgentCmd, NO_AGENT_CMD_MESSAGE, reviewFlagOverrides, freshWorktreeGateFlagOverrides, mergeRetriesFlagOverrides, noPRFlagOverrides, } from './do-config.js';
|
|
38
|
+
import { harnessFlagOverrides, doFlagOverrides, doNeedsAgentCmd, NO_AGENT_CMD_MESSAGE, reviewFlagOverrides, freshWorktreeGateFlagOverrides, mergeRetriesFlagOverrides, noPRFlagOverrides, triageModelFlagOverrides, } from './do-config.js';
|
|
39
39
|
import { harnessReviewGate, harnessTaskAcceptanceGate } from './review-gate.js';
|
|
40
40
|
import { harnessSurfaceGate } from './surface-gate.js';
|
|
41
41
|
import { harnessTriageGate } from './triage-gate.js';
|
|
@@ -43,8 +43,8 @@ import { harnessApplyDecider } from './apply-decide.js';
|
|
|
43
43
|
import { harnessTaskReviewGate } from './tasker-review-loop.js';
|
|
44
44
|
import { runVerify } from './verify.js';
|
|
45
45
|
import { renderPrompt } from './prompt.js';
|
|
46
|
-
import { resolvePromptGuidance } from './config.js';
|
|
47
|
-
import { gc, RETAIN_REASON_TEXT } from './gc.js';
|
|
46
|
+
import { resolvePromptGuidance, applyModelFallbacks } from './config.js';
|
|
47
|
+
import { gc, resolveArbiterKeyFromCwd, enumerateWorkspaceArbiters, RETAIN_REASON_TEXT, } from './gc.js';
|
|
48
48
|
import { runPrdToSpec, } from './prd-to-spec.js';
|
|
49
49
|
import { resyncProtocol } from './resync-protocol.js';
|
|
50
50
|
import { sweepRemoteMergedBranches } from './reap-branches.js';
|
|
@@ -107,7 +107,9 @@ function flagOverrides(flags, command) {
|
|
|
107
107
|
* `resolveRepoConfig` instead.
|
|
108
108
|
*/
|
|
109
109
|
function resolveGlobalConfig(fileConfig, flags) {
|
|
110
|
-
|
|
110
|
+
const config = mergeConfig({ ...fileConfig, ...envOverrides(), ...flags });
|
|
111
|
+
applyModelFallbacks(config);
|
|
112
|
+
return config;
|
|
111
113
|
}
|
|
112
114
|
/**
|
|
113
115
|
* Load BOTH the global config file AND the per-machine `config.override.json`
|
|
@@ -274,7 +276,7 @@ function buildRegistrySetAdvanceTick(options) {
|
|
|
274
276
|
noPR: config.noPR,
|
|
275
277
|
harness,
|
|
276
278
|
agentCmd: config.agentCmd,
|
|
277
|
-
model: config.
|
|
279
|
+
model: config.buildModel,
|
|
278
280
|
sessionsDir: config.sessionsDir,
|
|
279
281
|
review: config.review,
|
|
280
282
|
reviewModel: config.reviewModel,
|
|
@@ -299,12 +301,12 @@ function buildRegistrySetAdvanceTick(options) {
|
|
|
299
301
|
arbiter: arbiter ?? config.defaultArbiter,
|
|
300
302
|
doOptions,
|
|
301
303
|
surfaceGate: harnessSurfaceGate({ harness, agentCmd: config.agentCmd }),
|
|
302
|
-
surfaceModel: config.
|
|
304
|
+
surfaceModel: config.triageModel,
|
|
303
305
|
applyDecide: harnessApplyDecider({ harness, agentCmd: config.agentCmd }),
|
|
304
|
-
applyModel: config.
|
|
306
|
+
applyModel: config.triageModel,
|
|
305
307
|
observationTriage: config.observationTriage,
|
|
306
308
|
triageGate: harnessTriageGate({ harness, agentCmd: config.agentCmd }),
|
|
307
|
-
triageModel: config.
|
|
309
|
+
triageModel: config.triageModel,
|
|
308
310
|
// The ANSWERED-MERGE LAND DISPATCH context (task
|
|
309
311
|
// `apply-rung-merge-disposition`, spec `land-time-reverify-and-parallel-
|
|
310
312
|
// merge-ceiling`): the dispatcher cuts a per-job worktree via
|
|
@@ -396,6 +398,8 @@ function runFlagOverrides(flags, command) {
|
|
|
396
398
|
// flag > env > per-repo > global > default — mirroring the `do` command (the
|
|
397
399
|
// fleet inherits the review gate via the converged `performIntegration` core).
|
|
398
400
|
Object.assign(overrides, reviewFlagOverrides(flags));
|
|
401
|
+
// `--triage-model` (the advance lifecycle gates' model) rides the SAME chain.
|
|
402
|
+
Object.assign(overrides, triageModelFlagOverrides(flags));
|
|
399
403
|
// `--fresh-worktree-gate`/`--no-fresh-worktree-gate` rides the SAME chain: gate
|
|
400
404
|
// the REBASED tip in a clean throwaway worktree (ON by default). The `run` fleet
|
|
401
405
|
// caller additionally downgrades it to today's gate at `perRepoMax > 1` (the
|
|
@@ -872,6 +876,7 @@ export function buildProgram() {
|
|
|
872
876
|
.option('--no-pr', 'propose without opening a PR: push the branch but deliberately skip the review request, even on an authed GitHub arbiter (the explicit suppress-PR intent). Resolved flag > env > per-repo > global > default off.')
|
|
873
877
|
.option('--agent-cmd <cmd>', 'command to run one agent on a task prompt')
|
|
874
878
|
.option('--model <id>', 'model the agent runs on (routing intent; auth/keys stay the harness\u2019s job). pi: passed as --model; null/shell: substitutes a {model} placeholder in agentCmd. Resolved flag > env > per-repo > global > default (unset).')
|
|
879
|
+
.option('--build-model <id>', 'model the BUILD agent runs on (the builder-specific override of --model). Unset at every level inherits --model. Resolved flag > env > per-repo > global > fallback to model > default (unset).')
|
|
875
880
|
.option('--harness <adapter>', 'harness adapter that launches the agent + reports liveness: null (default, shells out to agentCmd) or pi (the pi CLI)')
|
|
876
881
|
.option('--pi-bin <path>', 'pi CLI binary the pi harness invokes (default: pi on PATH)')
|
|
877
882
|
.option('--sessions-dir <dir>', 'HOST-ONLY root folder under which pi session files are generated (--session <dir>/<id>.jsonl). Default: pi per-cwd folder under ~/.pi/agent/sessions. Resolved flag > env > global > default (no per-repo).')
|
|
@@ -879,6 +884,7 @@ export function buildProgram() {
|
|
|
879
884
|
.option('--review', 'run Gate 2 (PR/code review) after verify, before the done-move, on every item (overrides config). Resolved flag > env > per-repo > global > default off.')
|
|
880
885
|
.option('--no-review', 'do NOT run Gate 2 this tick (overrides config)')
|
|
881
886
|
.option('--review-model <id>', 'model the Gate-2 review agent runs on (de-correlated from the builder; routing intent). Resolved flag > env > per-repo > global > default.')
|
|
887
|
+
.option('--triage-model <id>', 'model the advance lifecycle gates (surface, apply, triage) run on. Unset at every level inherits --model. Resolved flag > env > per-repo > global > fallback to model > default (unset).')
|
|
882
888
|
.option('--review-max-rounds <n>', 'bound the revise/review loop; on exhaustion force needs-attention (default 2)')
|
|
883
889
|
.option('--fresh-worktree-gate', 'run the acceptance gate (prepare then verify) against the REBASED tip in a CLEAN throwaway worktree (the tree that integrates). ON by default; the `run` fleet uses it only when same-repo concurrency is off (perRepoMax=1), else today\u2019s in-build-worktree gate.')
|
|
884
890
|
.option('--no-fresh-worktree-gate', 'run the acceptance gate in the build worktree (the pre-rebase tree) — the opt-out for when the per-gate install cost is too high')
|
|
@@ -1420,6 +1426,7 @@ export function buildProgram() {
|
|
|
1420
1426
|
.option('--allow-backlog', 'do task:<slug> ONLY: also FIND, CLAIM, and COMPLETE a task that lives in tasks/backlog/ (staging), driving it in place WITHOUT promoting it to the pool (so no advance leg / run daemon can claim it out from under you). The done-move goes tasks/backlog/ -> tasks/done/ directly (your explicit drive IS the promotion). EXPLICIT-INVOCATION-ONLY: default off, never set by run/auto-pick/advance or config/env.')
|
|
1421
1427
|
.option('--agent-cmd <cmd>', 'command to run the agent on the task prompt')
|
|
1422
1428
|
.option('--model <id>', 'model the agent runs on (routing intent; resolved flag > env > per-repo > global > default)')
|
|
1429
|
+
.option('--build-model <id>', 'model the BUILD agent runs on (the builder-specific override of --model). Unset at every level inherits --model. Resolved flag > env > per-repo > global > fallback to model > default (unset).')
|
|
1423
1430
|
.option('--harness <adapter>', 'harness adapter that launches the agent: null (default, shells out to agentCmd) or pi (the pi CLI)')
|
|
1424
1431
|
.option('--pi-bin <path>', 'pi CLI binary the pi harness invokes (default: pi on PATH)')
|
|
1425
1432
|
.option('--sessions-dir <dir>', 'HOST-ONLY root folder under which the pi session file is generated (--session <dir>/<id>.jsonl). Default: pi per-cwd folder under ~/.pi/agent/sessions. Resolved flag > env > global > default (no per-repo).')
|
|
@@ -1427,6 +1434,7 @@ export function buildProgram() {
|
|
|
1427
1434
|
.option('--review', 'run Gate 2 (PR/code review) after verify, before the done-move (overrides config). Resolved flag > env > per-repo > global > default off.')
|
|
1428
1435
|
.option('--no-review', 'do NOT run Gate 2 this invocation (overrides config)')
|
|
1429
1436
|
.option('--review-model <id>', 'model the Gate-2 review agent runs on (de-correlated from the builder; routing intent). Resolved flag > env > per-repo > global > default.')
|
|
1437
|
+
.option('--triage-model <id>', 'model the advance lifecycle gates (surface, apply, triage) run on. Unset at every level inherits --model. Resolved flag > env > per-repo > global > fallback to model > default (unset).')
|
|
1430
1438
|
.option('--review-max-rounds <n>', 'bound the revise/review loop; on exhaustion force needs-attention (default 2)')
|
|
1431
1439
|
.option('--tasker-loop', 'run the tasker IMPROVER loop on `do spec:<slug>` (review→edit→converge over the produced task set). ON by default; --no-tasker-loop skips it. DISTINCT from the acceptance gate (--review).')
|
|
1432
1440
|
.option('--no-tasker-loop', 'skip the tasker improver loop on `do spec:<slug>`')
|
|
@@ -1649,7 +1657,7 @@ export function buildProgram() {
|
|
|
1649
1657
|
noPR: remoteConfig.noPR,
|
|
1650
1658
|
harness: remoteHarness,
|
|
1651
1659
|
agentCmd: remoteConfig.agentCmd,
|
|
1652
|
-
model: remoteConfig.
|
|
1660
|
+
model: remoteConfig.buildModel,
|
|
1653
1661
|
sessionsDir: remoteConfig.sessionsDir,
|
|
1654
1662
|
review: remoteConfig.review,
|
|
1655
1663
|
reviewModel: remoteConfig.reviewModel,
|
|
@@ -1820,7 +1828,7 @@ export function buildProgram() {
|
|
|
1820
1828
|
noPR: config.noPR,
|
|
1821
1829
|
harness,
|
|
1822
1830
|
agentCmd: config.agentCmd,
|
|
1823
|
-
model: config.
|
|
1831
|
+
model: config.buildModel,
|
|
1824
1832
|
// The HOST-ONLY sessions root (resolved Config → DoOptions bridge, like
|
|
1825
1833
|
// model/agentCmd): the path generator turns it into
|
|
1826
1834
|
// `<sessionsDir>/<id>.jsonl` for `--session`. Without this map the key
|
|
@@ -2082,7 +2090,7 @@ export function buildProgram() {
|
|
|
2082
2090
|
noPR: remoteConfig.noPR,
|
|
2083
2091
|
harness: isoHarness,
|
|
2084
2092
|
agentCmd: remoteConfig.agentCmd,
|
|
2085
|
-
model: remoteConfig.
|
|
2093
|
+
model: remoteConfig.buildModel,
|
|
2086
2094
|
sessionsDir: remoteConfig.sessionsDir,
|
|
2087
2095
|
review: remoteConfig.review,
|
|
2088
2096
|
reviewModel: remoteConfig.reviewModel,
|
|
@@ -2122,18 +2130,18 @@ export function buildProgram() {
|
|
|
2122
2130
|
harness: isoHarness,
|
|
2123
2131
|
agentCmd: remoteConfig.agentCmd,
|
|
2124
2132
|
}),
|
|
2125
|
-
surfaceModel: remoteConfig.
|
|
2133
|
+
surfaceModel: remoteConfig.triageModel,
|
|
2126
2134
|
applyDecide: harnessApplyDecider({
|
|
2127
2135
|
harness: isoHarness,
|
|
2128
2136
|
agentCmd: remoteConfig.agentCmd,
|
|
2129
2137
|
}),
|
|
2130
|
-
applyModel: remoteConfig.
|
|
2138
|
+
applyModel: remoteConfig.triageModel,
|
|
2131
2139
|
observationTriage: remoteConfig.observationTriage,
|
|
2132
2140
|
triageGate: harnessTriageGate({
|
|
2133
2141
|
harness: isoHarness,
|
|
2134
2142
|
agentCmd: remoteConfig.agentCmd,
|
|
2135
2143
|
}),
|
|
2136
|
-
triageModel: remoteConfig.
|
|
2144
|
+
triageModel: remoteConfig.triageModel,
|
|
2137
2145
|
note: (message) => console.error(`>> ${message}`),
|
|
2138
2146
|
env: process.env,
|
|
2139
2147
|
};
|
|
@@ -2222,7 +2230,7 @@ export function buildProgram() {
|
|
|
2222
2230
|
noPR: config.noPR,
|
|
2223
2231
|
harness,
|
|
2224
2232
|
agentCmd: config.agentCmd,
|
|
2225
|
-
model: config.
|
|
2233
|
+
model: config.buildModel,
|
|
2226
2234
|
sessionsDir: config.sessionsDir,
|
|
2227
2235
|
review: config.review,
|
|
2228
2236
|
reviewModel: config.reviewModel,
|
|
@@ -2258,12 +2266,12 @@ export function buildProgram() {
|
|
|
2258
2266
|
arbiter: flags.arbiter ?? config.defaultArbiter,
|
|
2259
2267
|
doOptions,
|
|
2260
2268
|
surfaceGate: harnessSurfaceGate({ harness, agentCmd: config.agentCmd }),
|
|
2261
|
-
surfaceModel: config.
|
|
2269
|
+
surfaceModel: config.triageModel,
|
|
2262
2270
|
applyDecide: harnessApplyDecider({ harness, agentCmd: config.agentCmd }),
|
|
2263
|
-
applyModel: config.
|
|
2271
|
+
applyModel: config.triageModel,
|
|
2264
2272
|
observationTriage: config.observationTriage,
|
|
2265
2273
|
triageGate: harnessTriageGate({ harness, agentCmd: config.agentCmd }),
|
|
2266
|
-
triageModel: config.
|
|
2274
|
+
triageModel: config.triageModel,
|
|
2267
2275
|
note: (message) => console.error(`>> ${message}`),
|
|
2268
2276
|
};
|
|
2269
2277
|
// `--watch` tails ONE pi session, so it only fits the single-named-item form
|
|
@@ -2332,7 +2340,7 @@ export function buildProgram() {
|
|
|
2332
2340
|
program
|
|
2333
2341
|
.command('gc')
|
|
2334
2342
|
.helpGroup(ADVANCED_GROUP)
|
|
2335
|
-
.description('Re-apply the provably-safe deletion predicate (ADR \u00a74)
|
|
2343
|
+
.description('Re-apply the provably-safe deletion predicate (ADR \u00a74) to job worktrees under workspacesDir/work/*: reap the provably-safe ones (clean tree AND branch tip reachable on the arbiter \u2014 merged or pushed) via git worktree remove (+ prune, never rm -rf), and report each RETAINED one with a reason. The catch-up for when end-of-job auto-reap did not run (runner crash/kill). SCOPE: by DEFAULT this is ARBITER-SCOPED \u2014 it acts ONLY on worktrees belonging to the arbiter resolved from the cwd (the same arbiter do --isolated/the mirror uses), so a gc in repo A can never reap repo B\u2019s worktrees. Pass --all-arbiters to sweep across EVERY arbiter (loud + global), or --arbiter <remote-or-url> to target a specific one. --force overrides the predicate (discards un-saved work) and requires --yes \u2014 loud, never default; --all-arbiters --force must be opted into explicitly, never reached by default.')
|
|
2336
2344
|
.option('-c, --config <path>', 'config file path', defaultConfigPath())
|
|
2337
2345
|
.option('--workspace <dir>', 'execution working area to sweep (default: workspacesDir / ~/.dorfl)')
|
|
2338
2346
|
.option('--force', 'OVERRIDE the predicate: remove worktrees even with un-saved work (requires --yes; never the default)')
|
|
@@ -2340,7 +2348,8 @@ export function buildProgram() {
|
|
|
2340
2348
|
.option('--ledger [repoPath]', 'SWEEP the work/ lifecycle LEDGER instead of job worktrees: REPORT (never delete) every slug present in more than one work/ status folder (the one-slug-one-folder belt-and-suspenders), with its folders + candidate canonical folder, for a HUMAN to resolve. Defaults to the cwd repo.')
|
|
2341
2349
|
.option('--reap-stale-locks', '(with --ledger) OPT-IN: also CLEAR every ORPHAN lock the report finds via the SAME leased delete `release-lock` uses, so one command sweeps all orphaned locks instead of N hand-run release-locks. Reaps the `cleared-stale` class: a held `active` lock that is EITHER terminal on `<arbiter>/main` (stranded between the durable move and the release) OR non-terminal + SURFACED (`needsAnswers:true` + sidecar on `<arbiter>/main`) = the crash-window orphan the ordered bounce transition leaves when step 1 lands but step 2 never runs. A `kept-in-flight` (active + non-terminal + NOT surfaced — a healthy build) is NEVER reaped, even with this flag. A concurrent change to a lock ref makes its leased delete REJECT (reported), never --force. WITHOUT this flag `gc --ledger` stays report-only (fail-loud, deletes nothing).')
|
|
2342
2350
|
.option('--remote-branches', 'SWEEP the arbiter’s remote work/* BRANCHES instead of job worktrees: delete (via git push --delete, NEVER --force) exactly those PROVABLY MERGED into <arbiter>/main (git merge-base --is-ancestor, the SAME predicate the worktree reaper uses), and RETAIN the rest with a reason. An in-flight/un-merged branch (the recovery point) is NEVER touched. Provider-agnostic plain git — works on a --bare arbiter. The merged-only complement of `requeue --reset`.')
|
|
2343
|
-
.option('--arbiter <remote>', '(
|
|
2351
|
+
.option('--arbiter <remote-or-url>', 'SCOPE the worktree sweep to a SPECIFIC arbiter (a remote NAME resolved from --cwd, or a direct arbiter URL) instead of the cwd\u2019s. (With --remote-branches / --ledger it names the arbiter git remote whose work/* branches / locks to sweep; default there: origin.)')
|
|
2352
|
+
.option('--all-arbiters', 'GLOBAL, LOUD override: sweep worktrees across EVERY arbiter under workspacesDir/work/* (today\u2019s pre-scoping behaviour), not just the cwd\u2019s. Prints a banner naming every arbiter it will act on before doing anything; combining it with --force still requires --yes. This is the destructive cross-repo path \u2014 opt into it explicitly.')
|
|
2344
2353
|
.option('--cwd <dir>', '(with --remote-branches) the local repo/clone whose --arbiter remote points at the arbiter to sweep (default: cwd); only remote refs are read + deleted, never the working tree')
|
|
2345
2354
|
.option('--dry-run', '(with --remote-branches) REPORT which merged branches WOULD be reaped without deleting anything (a read-only preview)')
|
|
2346
2355
|
.option('--json', 'output the raw result as JSON')
|
|
@@ -2511,6 +2520,68 @@ export function buildProgram() {
|
|
|
2511
2520
|
console.log(`Summary: ${reapedCount} ${verb}, ${sweep.retained.length} retained.`);
|
|
2512
2521
|
return;
|
|
2513
2522
|
}
|
|
2523
|
+
// SCOPE RESOLUTION (this task): the worktree reaper is ARBITER-SCOPED by
|
|
2524
|
+
// default. `--all-arbiters` restores the GLOBAL (cross-arbiter) sweep behind
|
|
2525
|
+
// a LOUD banner; `--arbiter <remote-or-url>` targets a specific arbiter; the
|
|
2526
|
+
// bare default resolves the arbiter from the cwd (the SAME resolution
|
|
2527
|
+
// `do --isolated` / the mirror uses). With NO resolvable cwd arbiter and
|
|
2528
|
+
// neither flag, we REFUSE (never silently fall back to global) — gc used to
|
|
2529
|
+
// default to global, and a global `gc --force --yes` from repo A could
|
|
2530
|
+
// irreversibly discard repo B's un-pushed work.
|
|
2531
|
+
let arbiterKey;
|
|
2532
|
+
if (flags.allArbiters !== true) {
|
|
2533
|
+
if (flags.arbiter !== undefined && flags.arbiter.trim() !== '') {
|
|
2534
|
+
// An explicit arbiter: a direct URL (contains `://`, a scp-like `:`,
|
|
2535
|
+
// or an absolute path) is keyed directly; otherwise it is a remote NAME
|
|
2536
|
+
// resolved from the cwd (default `origin`).
|
|
2537
|
+
const spec = flags.arbiter.trim();
|
|
2538
|
+
const looksLikeUrl = spec.includes('://') ||
|
|
2539
|
+
spec.startsWith('/') ||
|
|
2540
|
+
/^[^/]+@/.test(spec);
|
|
2541
|
+
// A remote NAME resolves from --cwd if given (consistent with the
|
|
2542
|
+
// other gc sub-modes), else the process cwd.
|
|
2543
|
+
const resolveFrom = flags.cwd ?? process.cwd();
|
|
2544
|
+
arbiterKey = looksLikeUrl
|
|
2545
|
+
? encodeRepoKey(spec)
|
|
2546
|
+
: resolveArbiterKeyFromCwd(resolveFrom, spec, process.env);
|
|
2547
|
+
if (arbiterKey === undefined) {
|
|
2548
|
+
console.error(`refusing: could not resolve arbiter '${spec}' \u2014 not a URL and ` +
|
|
2549
|
+
'no git remote with that name in the cwd. Pass a remote name that ' +
|
|
2550
|
+
'exists here, an arbiter URL, or --all-arbiters.');
|
|
2551
|
+
process.exit(1);
|
|
2552
|
+
}
|
|
2553
|
+
}
|
|
2554
|
+
else {
|
|
2555
|
+
arbiterKey = resolveArbiterKeyFromCwd(flags.cwd ?? process.cwd(), config.defaultArbiter, process.env);
|
|
2556
|
+
if (arbiterKey === undefined) {
|
|
2557
|
+
console.error('refusing: `gc` is now ARBITER-SCOPED and could not resolve an ' +
|
|
2558
|
+
`arbiter from the current directory (no '${config.defaultArbiter}' ` +
|
|
2559
|
+
'git remote, or not inside a repo). It will NOT fall back to a ' +
|
|
2560
|
+
'global cross-arbiter sweep. Run it from a repo with an arbiter ' +
|
|
2561
|
+
'remote, pass --arbiter <remote-or-url>, or --all-arbiters to sweep ' +
|
|
2562
|
+
'every arbiter.');
|
|
2563
|
+
process.exit(1);
|
|
2564
|
+
}
|
|
2565
|
+
}
|
|
2566
|
+
}
|
|
2567
|
+
else {
|
|
2568
|
+
// GLOBAL sweep: LOUD banner naming EVERY arbiter it will act on before
|
|
2569
|
+
// doing anything (this task's "loud" requirement). Global + --force must be
|
|
2570
|
+
// opted into explicitly (the --yes gate below still applies), never reached
|
|
2571
|
+
// by default.
|
|
2572
|
+
const arbiters = enumerateWorkspaceArbiters(workspacesDir, process.env);
|
|
2573
|
+
console.error('>> --all-arbiters: operating GLOBALLY across ALL arbiters under ' +
|
|
2574
|
+
`${workspacesDir}/work/* (NOT scoped to the current repo).`);
|
|
2575
|
+
if (arbiters.length > 0) {
|
|
2576
|
+
console.error('>> arbiters in scope:');
|
|
2577
|
+
for (const a of arbiters) {
|
|
2578
|
+
console.error(`>> - ${a.url ?? a.key ?? '(malformed mirror)'}`);
|
|
2579
|
+
}
|
|
2580
|
+
}
|
|
2581
|
+
else {
|
|
2582
|
+
console.error('>> (no worktrees found under any arbiter)');
|
|
2583
|
+
}
|
|
2584
|
+
}
|
|
2514
2585
|
// `--force` discards un-saved work, so it is gated behind an explicit
|
|
2515
2586
|
// confirmation (`--yes`) — loud + intentional, NEVER the default (ADR §4).
|
|
2516
2587
|
if (flags.force && !flags.yes) {
|
|
@@ -2525,6 +2596,7 @@ export function buildProgram() {
|
|
|
2525
2596
|
}
|
|
2526
2597
|
const result = gc({
|
|
2527
2598
|
workspacesDir,
|
|
2599
|
+
arbiterKey,
|
|
2528
2600
|
force: flags.force === true,
|
|
2529
2601
|
note: (message) => console.error(`>> ${message}`),
|
|
2530
2602
|
});
|
|
@@ -3012,6 +3084,7 @@ export function buildProgram() {
|
|
|
3012
3084
|
.option('--tasks-land-in <where>', 'where an intake-authored TASK (emitted DIRECTLY from an issue) lands: `backlog` (staged, not agent-eligible) or `ready` (the agent POOL). The task twin of --specs-land-in: the EXPLICIT operator override at the top of the placement precedence (explicit flag > configured default > built-in staging; the caller selects the trusted-vs-untrusted default from the origin-trust stamp). Resolved flag > env (DORFL_TASKS_LAND_IN) > per-repo > global > built-in.')
|
|
3013
3085
|
.option('--agent-cmd <cmd>', 'command to run the decision agent')
|
|
3014
3086
|
.option('--model <id>', 'model the decision agent runs on (routing intent; resolved flag > env > per-repo > global > default)')
|
|
3087
|
+
.option('--intake-model <id>', 'model the intake decision agent runs on (the builder-specific override of --model). Unset at every level inherits --model. Resolved flag > env > per-repo > global > fallback to model > default (unset).')
|
|
3015
3088
|
.option('--harness <adapter>', 'harness adapter that launches the decision agent: null (default) or pi')
|
|
3016
3089
|
.option('--pi-bin <path>', 'pi CLI binary the pi harness invokes (default: pi on PATH)')
|
|
3017
3090
|
.option('--sessions-dir <dir>', 'HOST-ONLY root folder under which the pi session file is generated')
|
|
@@ -3033,6 +3106,11 @@ export function buildProgram() {
|
|
|
3033
3106
|
...harnessFlagOverrides(flags),
|
|
3034
3107
|
// `--no-pr` (the PR-INTENT axis) rides the SAME chain.
|
|
3035
3108
|
...noPRFlagOverrides(flags),
|
|
3109
|
+
// `--intake-model` rides the SAME chain (falls back to `model`
|
|
3110
|
+
// when unset at all levels via `applyModelFallbacks`).
|
|
3111
|
+
...(flags.intakeModel !== undefined
|
|
3112
|
+
? { intakeModel: flags.intakeModel }
|
|
3113
|
+
: {}),
|
|
3036
3114
|
},
|
|
3037
3115
|
});
|
|
3038
3116
|
if (resolved.message) {
|
|
@@ -3137,7 +3215,7 @@ export function buildProgram() {
|
|
|
3137
3215
|
explicitTasksLandIn,
|
|
3138
3216
|
harness,
|
|
3139
3217
|
agentCmd: config.agentCmd,
|
|
3140
|
-
model: config.
|
|
3218
|
+
model: config.intakeModel,
|
|
3141
3219
|
sessionsDir: config.sessionsDir,
|
|
3142
3220
|
// Host-only runner IDENTITY — scopes intake's `gh`/git ops (not the
|
|
3143
3221
|
// decision/review AGENT launches); absent ⇒ ambient.
|