@deftai/directive 0.103.0 → 0.105.0

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.
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env node
2
+ import { resolve } from "node:path";
3
+ import { stealOccupancy } from "@deftai/directive-core/session";
4
+ export function parseArgs(argv) {
5
+ const parsed = { projectRoot: ".", confirm: false, occupant: null };
6
+ for (let i = 0; i < argv.length; i += 1) {
7
+ const arg = argv[i];
8
+ if (arg === "--confirm") {
9
+ parsed.confirm = true;
10
+ }
11
+ else if (arg === "--occupant") {
12
+ const value = argv[i + 1];
13
+ if (value === undefined) {
14
+ return { ...parsed, error: "argument --occupant: expected one argument" };
15
+ }
16
+ parsed.occupant = value;
17
+ i += 1;
18
+ }
19
+ else if (arg?.startsWith("--occupant=")) {
20
+ parsed.occupant = arg.slice("--occupant=".length);
21
+ }
22
+ else if (arg === "--project-root") {
23
+ const value = argv[i + 1];
24
+ if (value === undefined) {
25
+ return { ...parsed, error: "argument --project-root: expected one argument" };
26
+ }
27
+ parsed.projectRoot = value;
28
+ i += 1;
29
+ }
30
+ else if (arg?.startsWith("--project-root=")) {
31
+ parsed.projectRoot = arg.slice("--project-root=".length);
32
+ }
33
+ else {
34
+ return { ...parsed, error: `unrecognized argument: ${arg}` };
35
+ }
36
+ }
37
+ return parsed;
38
+ }
39
+ export function run(argv) {
40
+ const args = parseArgs(argv);
41
+ if (args.error !== undefined) {
42
+ process.stderr.write(`occupancy:steal: ${args.error}\n`);
43
+ return 2;
44
+ }
45
+ const result = stealOccupancy(resolve(args.projectRoot), {
46
+ confirm: args.confirm,
47
+ occupant: args.occupant ?? undefined,
48
+ env: process.env,
49
+ });
50
+ const sink = result.code === 0 ? process.stdout : process.stderr;
51
+ sink.write(`${result.message}\n`);
52
+ return result.code;
53
+ }
54
+ //# sourceMappingURL=occupancy-steal.js.map
@@ -0,0 +1,34 @@
1
+ /**
2
+ * CLI for `scm:sync-default` (#3391).
3
+ *
4
+ * Opens dest-targeted default-branch sync PRs using the shared detector and
5
+ * syncMaxFiles. Over-limit legs are new branches and new PRs.
6
+ */
7
+ import { planSyncDefault, type SyncDefaultForge, type SyncDefaultOpenPull } from "@deftai/directive-core/policy";
8
+ export declare const USAGE: string;
9
+ export interface SyncDefaultCliArgs {
10
+ readonly dryRun?: boolean;
11
+ readonly json?: boolean;
12
+ readonly help?: boolean;
13
+ readonly maxFiles?: number;
14
+ readonly projectRoot?: string;
15
+ readonly repo?: string;
16
+ }
17
+ export declare function parseSyncDefaultArgs(argv: readonly string[]): {
18
+ args: SyncDefaultCliArgs;
19
+ error: string | null;
20
+ };
21
+ export declare function pullsFromRestJson(payload: unknown): readonly SyncDefaultOpenPull[];
22
+ export declare function createGhSyncDefaultForge(): SyncDefaultForge;
23
+ export declare function resolveRepoFromGit(projectRoot: string): string | null;
24
+ export declare function runSyncDefaultCli(args: SyncDefaultCliArgs, options?: {
25
+ writeOut?: (s: string) => void;
26
+ writeErr?: (s: string) => void;
27
+ forge?: SyncDefaultForge;
28
+ cwd?: string;
29
+ runGit?: Parameters<typeof planSyncDefault>[0]["runGit"];
30
+ resolveRepo?: (projectRoot: string) => string | null;
31
+ }): number;
32
+ export declare function mainEntry(argv?: string[]): number;
33
+ export declare function main(argv?: string[]): number;
34
+ //# sourceMappingURL=scm-sync-default.d.ts.map
@@ -0,0 +1,210 @@
1
+ /**
2
+ * CLI for `scm:sync-default` (#3391).
3
+ *
4
+ * Opens dest-targeted default-branch sync PRs using the shared detector and
5
+ * syncMaxFiles. Over-limit legs are new branches and new PRs.
6
+ */
7
+ import { writeFileSync } from "node:fs";
8
+ import { tmpdir } from "node:os";
9
+ import { join } from "node:path";
10
+ import { fileURLToPath } from "node:url";
11
+ import { applySyncDefault, formatSyncDefaultHuman, parseGithubOwnerRepo, planSyncDefault, SYNC_DEFAULT_VERB, } from "@deftai/directive-core/policy";
12
+ import { runGhApi, splitRepo } from "@deftai/directive-core/scm";
13
+ import { defaultGitRunner } from "@deftai/directive-core/session";
14
+ export const USAGE = `usage: deft ${SYNC_DEFAULT_VERB} [--dry-run] [--json] [--max-files N] [--repo OWNER/REPO]\n` +
15
+ " Open dest-targeted sync PRs under syncMaxFiles. Under the limit: one new PR\n" +
16
+ " from source tip to dest. Over: merge-commit cuts; each dest-based leg is a\n" +
17
+ " new branch and a new PR. After a leg merges, run again. Never retarget or\n" +
18
+ " reuse an oversized PR. Each leg must be new when the reviewer first sees it.\n" +
19
+ " Required checks stay on except the Wave 1 core-guard sync exemption.\n";
20
+ export function parseSyncDefaultArgs(argv) {
21
+ const out = {};
22
+ for (let i = 0; i < argv.length; i += 1) {
23
+ const arg = argv[i];
24
+ if (arg === "--dry-run") {
25
+ out.dryRun = true;
26
+ }
27
+ else if (arg === "--json") {
28
+ out.json = true;
29
+ }
30
+ else if (arg === "--help" || arg === "-h") {
31
+ out.help = true;
32
+ }
33
+ else if (arg === "--max-files") {
34
+ const raw = argv[++i];
35
+ const parsed = raw === undefined ? Number.NaN : Number(raw);
36
+ if (!Number.isInteger(parsed) || parsed < 0) {
37
+ return { args: out, error: `--max-files expects a non-negative integer, got ${raw ?? ""}` };
38
+ }
39
+ out.maxFiles = parsed;
40
+ }
41
+ else if (arg === "--project-root") {
42
+ const value = argv[++i];
43
+ if (value === undefined) {
44
+ return { args: out, error: "--project-root expects a path" };
45
+ }
46
+ out.projectRoot = value;
47
+ }
48
+ else if (arg === "--repo") {
49
+ const value = argv[++i];
50
+ if (value === undefined) {
51
+ return { args: out, error: "--repo expects OWNER/REPO" };
52
+ }
53
+ out.repo = value;
54
+ }
55
+ else if (arg.startsWith("-")) {
56
+ return { args: out, error: `unknown flag ${JSON.stringify(arg)}` };
57
+ }
58
+ else {
59
+ return { args: out, error: `unexpected argument ${JSON.stringify(arg)}` };
60
+ }
61
+ }
62
+ return { args: out, error: null };
63
+ }
64
+ export function pullsFromRestJson(payload) {
65
+ if (!Array.isArray(payload))
66
+ return [];
67
+ const pulls = [];
68
+ for (const row of payload) {
69
+ if (typeof row !== "object" || row === null)
70
+ continue;
71
+ const rec = row;
72
+ const head = rec.head;
73
+ const baseRec = rec.base;
74
+ if (typeof head !== "object" || head === null)
75
+ continue;
76
+ if (typeof baseRec !== "object" || baseRec === null)
77
+ continue;
78
+ const headRec = head;
79
+ const baseObj = baseRec;
80
+ const number = rec.number;
81
+ const htmlUrl = rec.html_url;
82
+ const headRef = headRec.ref;
83
+ const headSha = headRec.sha;
84
+ const baseRef = baseObj.ref;
85
+ if (typeof number !== "number" ||
86
+ typeof htmlUrl !== "string" ||
87
+ typeof headRef !== "string" ||
88
+ typeof headSha !== "string" ||
89
+ typeof baseRef !== "string") {
90
+ continue;
91
+ }
92
+ pulls.push({ number, htmlUrl, headRef, headSha, baseRef });
93
+ }
94
+ return pulls;
95
+ }
96
+ export function createGhSyncDefaultForge() {
97
+ return {
98
+ listOpenPulls(repo, base) {
99
+ const [owner, name] = splitRepo(repo);
100
+ const endpoint = `repos/${owner}/${name}/pulls`;
101
+ const result = runGhApi([
102
+ endpoint,
103
+ "--method",
104
+ "GET",
105
+ "--raw-field",
106
+ `base=${base}`,
107
+ "--raw-field",
108
+ "state=open",
109
+ "--raw-field",
110
+ "per_page=100",
111
+ ]);
112
+ if (result.returncode !== 0) {
113
+ throw new Error(result.stderr || `failed to list open pulls for ${repo}`);
114
+ }
115
+ return pullsFromRestJson(JSON.parse(result.stdout || "[]"));
116
+ },
117
+ createPull(repo, input) {
118
+ const [owner, name] = splitRepo(repo);
119
+ const endpoint = `repos/${owner}/${name}/pulls`;
120
+ const bodyPath = join(tmpdir(), `deft-sync-default-${Date.now()}.json`);
121
+ writeFileSync(bodyPath, JSON.stringify({
122
+ title: input.title,
123
+ head: input.head,
124
+ base: input.base,
125
+ body: input.body,
126
+ }), { encoding: "utf8" });
127
+ const result = runGhApi(["-X", "POST", endpoint, "--input", bodyPath]);
128
+ if (result.returncode !== 0) {
129
+ throw new Error(result.stderr || `failed to create pull for ${repo}`);
130
+ }
131
+ const created = JSON.parse(result.stdout);
132
+ if (typeof created.number !== "number" || typeof created.html_url !== "string") {
133
+ throw new Error("create pull returned no number/html_url");
134
+ }
135
+ return { number: created.number, htmlUrl: created.html_url };
136
+ },
137
+ };
138
+ }
139
+ export function resolveRepoFromGit(projectRoot) {
140
+ const remote = defaultGitRunner(projectRoot, ["remote", "get-url", "origin"]);
141
+ if (remote.code !== 0)
142
+ return null;
143
+ return parseGithubOwnerRepo(remote.stdout);
144
+ }
145
+ export function runSyncDefaultCli(args, options = {}) {
146
+ const writeOut = options.writeOut ?? ((s) => process.stdout.write(s));
147
+ const writeErr = options.writeErr ?? ((s) => process.stderr.write(s));
148
+ if (args.help === true) {
149
+ writeOut(USAGE);
150
+ return 0;
151
+ }
152
+ const projectRoot = args.projectRoot ?? options.cwd ?? process.cwd();
153
+ const plan = planSyncDefault({
154
+ projectRoot,
155
+ maxFiles: args.maxFiles,
156
+ runGit: options.runGit,
157
+ });
158
+ if (plan.action === "noop") {
159
+ if (args.json === true) {
160
+ writeOut(`${JSON.stringify({ ...plan, opened: [], retargeted: false }, null, 2)}\n`);
161
+ }
162
+ else {
163
+ writeOut(`${plan.message}\n`);
164
+ }
165
+ return plan.noopReason === "fetch-failed" || plan.noopReason === "diff-failed" ? 2 : 0;
166
+ }
167
+ const resolveRepo = options.resolveRepo ?? resolveRepoFromGit;
168
+ const repo = args.repo ?? resolveRepo(projectRoot);
169
+ if (repo === null && args.dryRun !== true) {
170
+ writeErr("scm:sync-default: could not resolve OWNER/REPO from origin; pass --repo\n");
171
+ return 2;
172
+ }
173
+ try {
174
+ const result = applySyncDefault({
175
+ projectRoot,
176
+ repo: repo ?? "owner/repo",
177
+ plan,
178
+ dryRun: args.dryRun === true,
179
+ runGit: options.runGit,
180
+ forge: args.dryRun === true ? undefined : (options.forge ?? createGhSyncDefaultForge()),
181
+ });
182
+ if (args.json === true) {
183
+ writeOut(`${JSON.stringify(result, null, 2)}\n`);
184
+ }
185
+ else {
186
+ writeOut(formatSyncDefaultHuman(result));
187
+ }
188
+ return 0;
189
+ }
190
+ catch (err) {
191
+ const message = err instanceof Error ? err.message : String(err);
192
+ writeErr(`scm:sync-default: ${message}\n`);
193
+ return 1;
194
+ }
195
+ }
196
+ export function mainEntry(argv = process.argv.slice(2)) {
197
+ const { args, error } = parseSyncDefaultArgs(argv);
198
+ if (error !== null) {
199
+ process.stderr.write(`error: ${error}\n${USAGE}`);
200
+ return 2;
201
+ }
202
+ return runSyncDefaultCli(args);
203
+ }
204
+ export function main(argv = process.argv.slice(2)) {
205
+ return mainEntry(argv);
206
+ }
207
+ if (process.argv[1] !== undefined && fileURLToPath(import.meta.url) === process.argv[1]) {
208
+ process.exit(mainEntry());
209
+ }
210
+ //# sourceMappingURL=scm-sync-default.js.map
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+ import { type HumanPresenceMintSeams } from "./human-presence-mint.js";
2
3
  /** True when `candidate` is the same as or a descendant of `root` after realpath. */
3
4
  export declare function isPathInsideRoot(root: string, candidate: string): boolean;
4
5
  export interface ParsedArgs {
@@ -9,6 +10,10 @@ export interface ParsedArgs {
9
10
  /** Optional override for recorded xbriefRelPath (defaults: pending→active map). */
10
11
  xbriefRelPath: string;
11
12
  quiet: boolean;
13
+ /** Explicit operator confirm for the #3110 human-presence mint (#3384). */
14
+ confirm: boolean;
15
+ /** Optional owner/name seed for preimage approvedRepos (#3385 R5). */
16
+ repo: string;
12
17
  error?: string;
13
18
  }
14
19
  /**
@@ -18,5 +23,5 @@ export interface ParsedArgs {
18
23
  */
19
24
  export declare function resolveApprovalXbriefRelPath(sourceRelOrAbs: string, projectRoot: string, override?: string): string | null;
20
25
  export declare function parseArgs(argv: string[]): ParsedArgs;
21
- export declare function run(argv: string[]): number;
26
+ export declare function run(argv: string[], seams?: HumanPresenceMintSeams): number;
22
27
  //# sourceMappingURL=scope-record-approved-scope.d.ts.map
@@ -9,8 +9,9 @@
9
9
  */
10
10
  import { existsSync, lstatSync, readFileSync, realpathSync } from "node:fs";
11
11
  import { basename, join, relative, resolve, sep } from "node:path";
12
- import { scopeProvenance } from "@deftai/directive-core";
12
+ import { scopeProvenance, slice } from "@deftai/directive-core";
13
13
  import { isDirectEntrypoint } from "./entrypoint.js";
14
+ import { refuseMintWhileUatActive, refuseNonInteractiveMint, resolveHumanPresenceMintSeams, } from "./human-presence-mint.js";
14
15
  /** True when `candidate` is the same as or a descendant of `root` after realpath. */
15
16
  export function isPathInsideRoot(root, candidate) {
16
17
  let rootReal;
@@ -37,13 +38,13 @@ export function isPathInsideRoot(root, candidate) {
37
38
  return false;
38
39
  return !rel.startsWith("..");
39
40
  }
40
- const { buildApprovedScopeRecord, isHumanApprovalStamp, writeApprovedScopeRecord } = scopeProvenance;
41
+ const { isHumanApprovalStamp, mintApprovedScopeArtifacts } = scopeProvenance;
41
42
  function usage() {
42
- return ("usage: scope:record-approved-scope -- <xbrief-path> --actor <name> " +
43
- "[--kind operator] [--project-root <dir>] [--xbrief-rel-path <posix>] [--quiet]\n" +
44
- " Writes .deft/approved-scope/<plan-id>.json with a humanApproval stamp (#3205).\n" +
45
- " Agent-shaped stamps are refused. Path-binds to xbrief/active/ by default when " +
46
- "the source is under pending/ or active/.");
43
+ return ("usage: scope:record-approved-scope -- <xbrief-path> --actor <name> --confirm " +
44
+ "[--kind operator] [--project-root <dir>] [--xbrief-rel-path <posix>] [--repo owner/name] [--quiet]\n" +
45
+ " Writes .deft/approved-scope/<plan-id>.json and <plan-id>.intent.json (#3205 / #3384 / #3385).\n" +
46
+ " --actor is display only and never authorizes mint. Mint requires a real TTY, " +
47
+ "controlling terminal, --confirm, and typed phrase mint (#3110). Agent/CI shells refuse.");
47
48
  }
48
49
  /**
49
50
  * Map pending/ → active/ for path-bound approval records.
@@ -96,6 +97,8 @@ export function parseArgs(argv) {
96
97
  kind: "operator",
97
98
  xbriefRelPath: "",
98
99
  quiet: false,
100
+ confirm: false,
101
+ repo: "",
99
102
  };
100
103
  const positionals = [];
101
104
  for (let i = 0; i < argv.length; i += 1) {
@@ -106,6 +109,9 @@ export function parseArgs(argv) {
106
109
  if (arg === "--quiet") {
107
110
  parsed.quiet = true;
108
111
  }
112
+ else if (arg === "--confirm") {
113
+ parsed.confirm = true;
114
+ }
109
115
  else if (arg === "--project-root") {
110
116
  const value = argv[i + 1];
111
117
  if (value === undefined) {
@@ -150,6 +156,17 @@ export function parseArgs(argv) {
150
156
  else if (arg?.startsWith("--xbrief-rel-path=")) {
151
157
  parsed.xbriefRelPath = arg.slice("--xbrief-rel-path=".length);
152
158
  }
159
+ else if (arg === "--repo") {
160
+ const value = argv[i + 1];
161
+ if (value === undefined) {
162
+ return { ...parsed, error: "argument --repo: expected one argument" };
163
+ }
164
+ parsed.repo = value;
165
+ i += 1;
166
+ }
167
+ else if (arg?.startsWith("--repo=")) {
168
+ parsed.repo = arg.slice("--repo=".length);
169
+ }
153
170
  else if (arg?.startsWith("-")) {
154
171
  return { ...parsed, error: `unrecognized argument: ${arg}` };
155
172
  }
@@ -172,7 +189,7 @@ export function parseArgs(argv) {
172
189
  }
173
190
  return parsed;
174
191
  }
175
- export function run(argv) {
192
+ export function run(argv, seams = {}) {
176
193
  const args = parseArgs(argv);
177
194
  if (args.error !== undefined) {
178
195
  process.stderr.write(`scope_record_approved_scope: ${args.error}\n`);
@@ -202,14 +219,27 @@ export function run(argv) {
202
219
  process.stderr.write(`scope_record_approved_scope: failed to read xBRIEF: ${String(err)}\n`);
203
220
  return 2;
204
221
  }
205
- let payload;
206
- try {
207
- payload = JSON.parse(raw);
208
- }
209
- catch {
210
- process.stderr.write("scope_record_approved_scope: xBRIEF is not valid JSON\n");
222
+ const parsed = scopeProvenance.parseJsonRejectingDuplicateKeys(raw);
223
+ if (!parsed.ok) {
224
+ process.stderr.write(`scope_record_approved_scope: ${parsed.error}\n`);
211
225
  return 2;
212
226
  }
227
+ const payload = parsed.value;
228
+ const verb = "scope:record-approved-scope";
229
+ const uatBlocked = refuseMintWhileUatActive(verb, projectRoot);
230
+ if (uatBlocked !== null)
231
+ return uatBlocked;
232
+ const resolved = resolveHumanPresenceMintSeams(seams);
233
+ const mintBlocked = refuseNonInteractiveMint({
234
+ verb,
235
+ confirm: args.confirm,
236
+ isTty: resolved.isTty,
237
+ environ: resolved.environ,
238
+ hasControllingTerminal: resolved.hasControllingTerminal,
239
+ readInteractiveConfirm: resolved.readInteractiveConfirm,
240
+ });
241
+ if (mintBlocked !== null)
242
+ return mintBlocked;
213
243
  const stamp = {
214
244
  kind: args.kind.trim(),
215
245
  actor: args.actor.trim(),
@@ -228,22 +258,40 @@ export function run(argv) {
228
258
  "(repo-relative path required for approval binding) (#3205).\n");
229
259
  return 2;
230
260
  }
231
- const record = buildApprovedScopeRecord({
232
- xbriefRelPath,
233
- payload,
234
- humanApproval: stamp,
235
- xbriefRawText: raw,
236
- });
237
- const outPath = writeApprovedScopeRecord(projectRoot, record);
261
+ const repoSeed = slice.resolveProjectRepo(args.repo.trim().length > 0 ? args.repo.trim() : undefined, projectRoot);
262
+ // Mint writes record + preimage as one fail-closed pair. A dest-write
263
+ // failure restores the prior pair or leaves neither dest (#3385 residual).
264
+ let minted;
265
+ try {
266
+ minted = mintApprovedScopeArtifacts({
267
+ xbriefRelPath,
268
+ payload,
269
+ rawText: raw,
270
+ projectRoot,
271
+ humanApproval: stamp,
272
+ extract: {
273
+ projectRoot,
274
+ approvedReposSeed: repoSeed !== null ? [repoSeed] : [],
275
+ },
276
+ });
277
+ }
278
+ catch (err) {
279
+ process.stderr.write(`scope_record_approved_scope: ${String(err)}\n`);
280
+ return 1;
281
+ }
282
+ const { record, recordPath, intentPath } = minted;
238
283
  if (!args.quiet) {
239
- process.stdout.write(`scope_record_approved_scope: wrote ${outPath}\n` +
284
+ process.stdout.write(`scope_record_approved_scope: wrote ${recordPath}\n` +
285
+ ` preimage: ${intentPath}\n` +
240
286
  ` planId: ${record.planId}\n` +
241
287
  ` xbriefRelPath: ${record.xbriefRelPath}\n` +
242
288
  ` fileScopeDigest: ${record.fileScopeDigest}\n` +
289
+ ` intentDigest: ${record.intentDigest ?? ""}\n` +
243
290
  ` paths: ${record.fileScope.length}\n` +
244
291
  ` humanApproval: ${stamp.kind}/${stamp.actor}\n` +
245
- " Next: commit this file on the merge base (or a prior PR) before " +
246
- "activation/expansion in the implementation change set (#3205).\n");
292
+ " Read the preimage before you commit. That file is the approved intent (#3385).\n" +
293
+ " Next: commit record + preimage on the merge base (or a prior PR) before " +
294
+ "activation/expansion in the implementation change set (#3205 / #3385).\n");
247
295
  }
248
296
  return 0;
249
297
  }
@@ -32,6 +32,10 @@ export interface ParsedSessionStartArgs {
32
32
  * the default; also settable via DEFT_SESSION_COMPACT=1.
33
33
  */
34
34
  compact: boolean;
35
+ /** #3433: steal the worktree occupancy lease. */
36
+ steal: boolean;
37
+ confirm: boolean;
38
+ occupant: string | null;
35
39
  error?: string;
36
40
  }
37
41
  /** Parse session:start CLI args, mirroring scripts/session_start.py. */
@@ -31,6 +31,9 @@ export function parseArgs(argv) {
31
31
  ceremonyDepthOverride: null,
32
32
  effortBudgetHost: {},
33
33
  compact: false,
34
+ steal: false,
35
+ confirm: false,
36
+ occupant: null,
34
37
  };
35
38
  const dialInputs = {};
36
39
  for (let i = 0; i < argv.length; i += 1) {
@@ -273,6 +276,23 @@ export function parseArgs(argv) {
273
276
  // #3286: terse machine orientation output (opt-in; verbose remains default)
274
277
  parsed.compact = true;
275
278
  }
279
+ else if (arg === "--steal") {
280
+ parsed.steal = true;
281
+ }
282
+ else if (arg === "--confirm") {
283
+ parsed.confirm = true;
284
+ }
285
+ else if (arg === "--occupant") {
286
+ const value = argv[i + 1];
287
+ if (value === undefined) {
288
+ return { ...parsed, error: "argument --occupant: expected one argument" };
289
+ }
290
+ parsed.occupant = value;
291
+ i += 1;
292
+ }
293
+ else if (arg?.startsWith("--occupant=")) {
294
+ parsed.occupant = arg.slice("--occupant=".length);
295
+ }
276
296
  else {
277
297
  return { ...parsed, error: `unrecognized argument: ${arg}` };
278
298
  }
@@ -335,6 +355,9 @@ export function run(argv) {
335
355
  effortBudgetSeams: { hostDescriptor, environ: process.env },
336
356
  // #3286: --compact flag; DEFT_SESSION_COMPACT still resolved inside core.
337
357
  compact: args.compact ? true : undefined,
358
+ steal: args.steal ? true : undefined,
359
+ confirm: args.confirm ? true : undefined,
360
+ occupant: args.occupant ?? undefined,
338
361
  ...(args.ceremonyDepthOverride !== null
339
362
  ? {
340
363
  ceremonyDial: selectCeremonyDepth({
@@ -20,6 +20,8 @@ export type FindActiveXbriefResult = {
20
20
  readonly paths: readonly string[];
21
21
  readonly dir: string;
22
22
  };
23
+ /** Three-state exit: pass=0, fail=1, config=2. A printed pass must not leak 201. */
24
+ export declare function clampVerifyAcExit(ok: boolean, code: number): number;
23
25
  /** Run the gate and return the process exit code. */
24
26
  export declare function run(argv: string[]): number;
25
27
  export {};