@deftai/directive 0.104.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.
- package/dist/authz.d.ts +4 -29
- package/dist/authz.js +16 -181
- package/dist/dispatch.d.ts +1 -1
- package/dist/dispatch.js +13 -0
- package/dist/human-presence-mint.d.ts +61 -0
- package/dist/human-presence-mint.js +192 -0
- package/dist/occupancy-steal.d.ts +9 -0
- package/dist/occupancy-steal.js +54 -0
- package/dist/scm-sync-default.d.ts +34 -0
- package/dist/scm-sync-default.js +210 -0
- package/dist/scope-record-approved-scope.d.ts +6 -1
- package/dist/scope-record-approved-scope.js +72 -24
- package/dist/session-start.d.ts +4 -0
- package/dist/session-start.js +23 -0
- package/dist/verify-ac.d.ts +2 -0
- package/dist/verify-ac.js +30 -6
- package/dist/verify-completed-tracked.d.ts +2 -1
- package/dist/verify-completed-tracked.js +31 -1
- package/dist/verify-literal-ac.js +7 -0
- package/dist/verify-orphan-active.d.ts +1 -0
- package/dist/verify-orphan-active.js +30 -0
- package/dist/verify-session-ritual.d.ts +10 -2
- package/dist/verify-session-ritual.js +16 -3
- package/package.json +3 -3
package/dist/authz.d.ts
CHANGED
|
@@ -1,32 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
*/
|
|
7
|
-
export declare const AUTHZ_AGENT_SHELL_ENV_MARKERS: readonly ["CLAUDECODE", "CLAUDE_CODE", "CLAUDE_CODE_ENTRYPOINT", "CURSOR_AGENT", "CURSOR_TRACE_ID", "CURSOR_SESSION_ID", "AIDER", "CONTINUE_CLI", "CODEX_SANDBOX", "CODEX_CI", "OPENAI_CODEX", "OPENCLAW", "OPENCLAW_STATE_DIR", "DEFT_PROBE_OPENCLAW", "DEFT_HOOK_HOST", "DEFT_AGENT_SHELL", "DEFT_AGENT_RUNTIME", "WARP_SESSION_ID", "WARP_HARNESS", "WARP_RUN_ID", "GEMINI_CLI", "AMP_CLI", "SWARM_AGENT", "AI_AGENT", "CI", "CONTINUOUS_INTEGRATION", "GITHUB_ACTIONS", "GITLAB_CI", "CIRCLECI", "BUILDKITE", "TRAVIS", "JENKINS_URL", "TEAMCITY_VERSION", "TF_BUILD", "APPVEYOR", "BITBUCKET_BUILD_NUMBER", "CODEBUILD_BUILD_ID"];
|
|
8
|
-
/** Phrase an operator must type on the controlling TTY after --confirm (#3110). */
|
|
9
|
-
export declare const AUTHZ_INTERACTIVE_CONFIRM_PHRASE = "mint";
|
|
10
|
-
/** Testable seams for TTY / agent-shell detection (#3110). */
|
|
11
|
-
export interface AuthzMainSeams {
|
|
12
|
-
/**
|
|
13
|
-
* When true, interactive human TTY is present.
|
|
14
|
-
* Default: both stdin and stdout report isTTY (pseudo-TTY residual; #3110).
|
|
15
|
-
*/
|
|
16
|
-
readonly isTty?: () => boolean;
|
|
17
|
-
/** Environ for agent-shell marker detection (default: process.env). */
|
|
18
|
-
readonly environ?: NodeJS.ProcessEnv;
|
|
19
|
-
/**
|
|
20
|
-
* True when a controlling terminal device is available (`/dev/tty` or `CONIN$`).
|
|
21
|
-
* Default: open/close the platform controlling terminal (fail-closed on error).
|
|
22
|
-
*/
|
|
23
|
-
readonly hasControllingTerminal?: () => boolean;
|
|
24
|
-
/**
|
|
25
|
-
* Read one interactive confirmation line from the operator (trimmed).
|
|
26
|
-
* Default: one line from stdin. Tests inject a fixed phrase.
|
|
27
|
-
*/
|
|
28
|
-
readonly readInteractiveConfirm?: () => string | null;
|
|
29
|
-
}
|
|
30
|
-
export declare function main(argv?: string[], seams?: AuthzMainSeams): number;
|
|
2
|
+
import { type HumanPresenceMintSeams } from "./human-presence-mint.js";
|
|
3
|
+
export type { HumanPresenceMintSeams as AuthzMainSeams } from "./human-presence-mint.js";
|
|
4
|
+
export { AUTHZ_AGENT_SHELL_ENV_MARKERS, AUTHZ_INTERACTIVE_CONFIRM_PHRASE, } from "./human-presence-mint.js";
|
|
5
|
+
export declare function main(argv?: string[], seams?: HumanPresenceMintSeams): number;
|
|
31
6
|
export default main;
|
|
32
7
|
//# sourceMappingURL=authz.d.ts.map
|
package/dist/authz.js
CHANGED
|
@@ -22,57 +22,11 @@
|
|
|
22
22
|
* TTY + controlling terminal + `--confirm` + typed phrase `mint`; agent/CI env
|
|
23
23
|
* markers refuse fail-closed. Argv `--confirm` alone is never enough.
|
|
24
24
|
*/
|
|
25
|
-
import {
|
|
25
|
+
import { existsSync } from "node:fs";
|
|
26
26
|
import { isAbsolute, resolve } from "node:path";
|
|
27
|
-
import { AFK_TEMPLATE_NAMES, AUTHZ_OPERATIONS, CLOSED_VERB_TEMPLATE_NAMES, FINISH_LOOP_TEMPLATE_NAME, formatDecomposeStructuralMintCommand, isAfkTemplateName, isClosedVerbTemplateName, isFinishLoopTemplateName,
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
* (pseudo-terminal residual; #3110 Greptile). Presence refuses mutating authz.
|
|
31
|
-
* Expanded for dogfood conf 5/5 — markers are fail-closed (any non-empty value).
|
|
32
|
-
*/
|
|
33
|
-
export const AUTHZ_AGENT_SHELL_ENV_MARKERS = [
|
|
34
|
-
// Coding agents / IDEs
|
|
35
|
-
"CLAUDECODE",
|
|
36
|
-
"CLAUDE_CODE",
|
|
37
|
-
"CLAUDE_CODE_ENTRYPOINT",
|
|
38
|
-
"CURSOR_AGENT",
|
|
39
|
-
"CURSOR_TRACE_ID",
|
|
40
|
-
"CURSOR_SESSION_ID",
|
|
41
|
-
"AIDER",
|
|
42
|
-
"CONTINUE_CLI",
|
|
43
|
-
"CODEX_SANDBOX",
|
|
44
|
-
"CODEX_CI",
|
|
45
|
-
"OPENAI_CODEX",
|
|
46
|
-
"OPENCLAW",
|
|
47
|
-
"OPENCLAW_STATE_DIR",
|
|
48
|
-
"DEFT_PROBE_OPENCLAW",
|
|
49
|
-
"DEFT_HOOK_HOST",
|
|
50
|
-
"DEFT_AGENT_SHELL",
|
|
51
|
-
"DEFT_AGENT_RUNTIME",
|
|
52
|
-
"WARP_SESSION_ID",
|
|
53
|
-
"WARP_HARNESS",
|
|
54
|
-
"WARP_RUN_ID",
|
|
55
|
-
"GEMINI_CLI",
|
|
56
|
-
"AMP_CLI",
|
|
57
|
-
"SWARM_AGENT",
|
|
58
|
-
"AI_AGENT",
|
|
59
|
-
// CI / automation (never a human interactive operator mint)
|
|
60
|
-
"CI",
|
|
61
|
-
"CONTINUOUS_INTEGRATION",
|
|
62
|
-
"GITHUB_ACTIONS",
|
|
63
|
-
"GITLAB_CI",
|
|
64
|
-
"CIRCLECI",
|
|
65
|
-
"BUILDKITE",
|
|
66
|
-
"TRAVIS",
|
|
67
|
-
"JENKINS_URL",
|
|
68
|
-
"TEAMCITY_VERSION",
|
|
69
|
-
"TF_BUILD",
|
|
70
|
-
"APPVEYOR",
|
|
71
|
-
"BITBUCKET_BUILD_NUMBER",
|
|
72
|
-
"CODEBUILD_BUILD_ID",
|
|
73
|
-
];
|
|
74
|
-
/** Phrase an operator must type on the controlling TTY after --confirm (#3110). */
|
|
75
|
-
export const AUTHZ_INTERACTIVE_CONFIRM_PHRASE = "mint";
|
|
27
|
+
import { AFK_TEMPLATE_NAMES, AUTHZ_OPERATIONS, CLOSED_VERB_TEMPLATE_NAMES, FINISH_LOOP_TEMPLATE_NAME, formatDecomposeStructuralMintCommand, isAfkTemplateName, isClosedVerbTemplateName, isFinishLoopTemplateName, mintAfkTemplateGrant, mintDecomposeStructuralApplyGrant, mintHumanOriginGrant, revokeGrant, showAuthzSnapshot, startUatLease, suspendUatLease, toProjectRelativePosix, } from "@deftai/directive-core/authz";
|
|
28
|
+
import { refuseMintWhileUatActive, refuseNonInteractiveMint, resolveHumanPresenceMintSeams, } from "./human-presence-mint.js";
|
|
29
|
+
export { AUTHZ_AGENT_SHELL_ENV_MARKERS, AUTHZ_INTERACTIVE_CONFIRM_PHRASE, } from "./human-presence-mint.js";
|
|
76
30
|
function parseOps(raw) {
|
|
77
31
|
const allowed = new Set(AUTHZ_OPERATIONS);
|
|
78
32
|
const out = [];
|
|
@@ -295,130 +249,6 @@ function helpText() {
|
|
|
295
249
|
" Env bypass for a single shell: DEFT_ALLOW_RELEASE_PUBLISH=1 / DEFT_ALLOW_FINISH_LOOP=1.",
|
|
296
250
|
].join("\n");
|
|
297
251
|
}
|
|
298
|
-
function looksLikeAgentShell(environ) {
|
|
299
|
-
for (const key of AUTHZ_AGENT_SHELL_ENV_MARKERS) {
|
|
300
|
-
const v = environ[key];
|
|
301
|
-
if (v !== undefined && String(v).trim().length > 0)
|
|
302
|
-
return true;
|
|
303
|
-
}
|
|
304
|
-
return false;
|
|
305
|
-
}
|
|
306
|
-
function defaultHasControllingTerminal() {
|
|
307
|
-
try {
|
|
308
|
-
const path = process.platform === "win32" ? "CONIN$" : "/dev/tty";
|
|
309
|
-
const fd = openSync(path, "r");
|
|
310
|
-
closeSync(fd);
|
|
311
|
-
return true;
|
|
312
|
-
}
|
|
313
|
-
catch {
|
|
314
|
-
return false;
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
function defaultReadInteractiveConfirm() {
|
|
318
|
-
// Read from the controlling terminal device — not redirected/piped stdin —
|
|
319
|
-
// so agent-controlled stdin alone cannot supply the confirm phrase (#3110).
|
|
320
|
-
let fd = null;
|
|
321
|
-
try {
|
|
322
|
-
const path = process.platform === "win32" ? "CONIN$" : "/dev/tty";
|
|
323
|
-
fd = openSync(path, "r");
|
|
324
|
-
const buf = Buffer.alloc(256);
|
|
325
|
-
const n = readSync(fd, buf, 0, buf.length, null);
|
|
326
|
-
if (n <= 0)
|
|
327
|
-
return null;
|
|
328
|
-
return buf.subarray(0, n).toString("utf8").trim();
|
|
329
|
-
}
|
|
330
|
-
catch {
|
|
331
|
-
return null;
|
|
332
|
-
}
|
|
333
|
-
finally {
|
|
334
|
-
if (fd !== null) {
|
|
335
|
-
try {
|
|
336
|
-
closeSync(fd);
|
|
337
|
-
}
|
|
338
|
-
catch {
|
|
339
|
-
/* ignore */
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
/**
|
|
345
|
-
* While any UAT lease is active, refuse ALL mutating authz CLI verbs (#3110).
|
|
346
|
-
*
|
|
347
|
-
* No multi-factor escape: TTY, `--confirm`, and typed phrase never authorize.
|
|
348
|
-
* Self-approval under UAT is impossible by construction — agents with a PTY
|
|
349
|
-
* cannot mint/suspend/revoke operator-cli authority during an active lease.
|
|
350
|
-
* Operators mint fix-cohort grants *before* `uat-start`.
|
|
351
|
-
*
|
|
352
|
-
* Returns exit code when blocked, or null when the command may continue.
|
|
353
|
-
*/
|
|
354
|
-
function refuseMutatingAuthzWhileUatActive(projectRoot, cmd) {
|
|
355
|
-
if (cmd === "show")
|
|
356
|
-
return null;
|
|
357
|
-
const state = loadAuthzState(projectRoot);
|
|
358
|
-
if (state.uat === null || !state.uat.active)
|
|
359
|
-
return null;
|
|
360
|
-
process.stderr.write(`authz:${cmd}: refusing mutating authz while UAT lease is ACTIVE ` +
|
|
361
|
-
`(campaign=${state.uat.campaignId}). Under active UAT, grant / uat-start / ` +
|
|
362
|
-
"uat-suspend / revoke are hard-refused — no TTY, --confirm, or phrase path " +
|
|
363
|
-
"authorizes self-approval (#3110). Mint grants before uat-start; clear the " +
|
|
364
|
-
"lease out-of-band (edit .deft/authz/state.json as a human outside agent hooks) " +
|
|
365
|
-
"to end UAT.\n");
|
|
366
|
-
return 2;
|
|
367
|
-
}
|
|
368
|
-
/**
|
|
369
|
-
* Refuse non-interactive / agent-shell operator-cli stamps outside UAT (#3110).
|
|
370
|
-
*
|
|
371
|
-
* Multi-factor human-presence gate (applies only when UAT lease is inactive):
|
|
372
|
-
* 1. No known agent/CI env markers
|
|
373
|
-
* 2. Interactive TTY (stdin + stdout isTTY)
|
|
374
|
-
* 3. Controlling terminal device present (`/dev/tty` / `CONIN$`)
|
|
375
|
-
* 4. Explicit argv `--confirm` (flag alone never enough)
|
|
376
|
-
* 5. Interactive typed phrase `mint` (argv --confirm alone never enough even on PTY)
|
|
377
|
-
*
|
|
378
|
-
* Fail-closed: if a real human interactive path cannot be proven, refuse mint.
|
|
379
|
-
* Returns an exit code when blocked, or null when the mutation may proceed.
|
|
380
|
-
*/
|
|
381
|
-
function refuseNonInteractiveMint(cmd, isTty, environ, confirm, hasControllingTerminal, readInteractiveConfirm) {
|
|
382
|
-
if (cmd === "show")
|
|
383
|
-
return null;
|
|
384
|
-
if (looksLikeAgentShell(environ)) {
|
|
385
|
-
process.stderr.write(`authz:${cmd}: refusing operator-cli stamp from an agent/host/CI shell ` +
|
|
386
|
-
`(detected agent or CI env marker). Mutating authz requires a human interactive ` +
|
|
387
|
-
"TTY without agent-shell markers, plus --confirm and typed phrase (#3110).\n");
|
|
388
|
-
return 2;
|
|
389
|
-
}
|
|
390
|
-
const tty = isTty();
|
|
391
|
-
if (!tty && !confirm) {
|
|
392
|
-
process.stderr.write(`authz:${cmd}: refusing non-interactive operator-cli stamp. ` +
|
|
393
|
-
"Mutating authz verbs require interactive TTY, --confirm, and typed phrase " +
|
|
394
|
-
`'${AUTHZ_INTERACTIVE_CONFIRM_PHRASE}' (#3110).\n`);
|
|
395
|
-
return 2;
|
|
396
|
-
}
|
|
397
|
-
if (!tty) {
|
|
398
|
-
process.stderr.write(`authz:${cmd}: refusing non-TTY operator-cli stamp. ` +
|
|
399
|
-
"--confirm alone never authorizes mint — interactive TTY is required (#3110).\n");
|
|
400
|
-
return 2;
|
|
401
|
-
}
|
|
402
|
-
if (!confirm) {
|
|
403
|
-
process.stderr.write(`authz:${cmd}: refusing operator-cli stamp without --confirm. ` +
|
|
404
|
-
"Interactive TTY alone never authorizes mint — pass --confirm explicitly (#3110).\n");
|
|
405
|
-
return 2;
|
|
406
|
-
}
|
|
407
|
-
if (!hasControllingTerminal()) {
|
|
408
|
-
process.stderr.write(`authz:${cmd}: refusing operator-cli stamp without a controlling terminal. ` +
|
|
409
|
-
"Open a real interactive console (not a headless/agent pipe) to mint (#3110).\n");
|
|
410
|
-
return 2;
|
|
411
|
-
}
|
|
412
|
-
process.stderr.write(`authz:${cmd}: type '${AUTHZ_INTERACTIVE_CONFIRM_PHRASE}' and press Enter to confirm operator mint: `);
|
|
413
|
-
const line = readInteractiveConfirm();
|
|
414
|
-
const phrase = (line ?? "").trim().toLowerCase();
|
|
415
|
-
if (phrase !== AUTHZ_INTERACTIVE_CONFIRM_PHRASE) {
|
|
416
|
-
process.stderr.write(`\nauthz:${cmd}: interactive confirm phrase mismatch (got ${JSON.stringify(line ?? "")}). ` +
|
|
417
|
-
`Type exactly '${AUTHZ_INTERACTIVE_CONFIRM_PHRASE}' on the controlling TTY (#3110).\n`);
|
|
418
|
-
return 2;
|
|
419
|
-
}
|
|
420
|
-
return null;
|
|
421
|
-
}
|
|
422
252
|
export function main(argv = process.argv.slice(2), seams = {}) {
|
|
423
253
|
const args = parseArgv(argv);
|
|
424
254
|
if (args.error === "help") {
|
|
@@ -430,11 +260,7 @@ export function main(argv = process.argv.slice(2), seams = {}) {
|
|
|
430
260
|
process.stderr.write(`${helpText()}\n`);
|
|
431
261
|
return 2;
|
|
432
262
|
}
|
|
433
|
-
|
|
434
|
-
const isTty = seams.isTty ?? (() => process.stdin.isTTY === true && process.stdout.isTTY === true);
|
|
435
|
-
const environ = seams.environ ?? process.env;
|
|
436
|
-
const hasControllingTerminal = seams.hasControllingTerminal ?? defaultHasControllingTerminal;
|
|
437
|
-
const readInteractiveConfirm = seams.readInteractiveConfirm ?? defaultReadInteractiveConfirm;
|
|
263
|
+
const resolved = resolveHumanPresenceMintSeams(seams);
|
|
438
264
|
/**
|
|
439
265
|
* Mutating-verb gate stack (#3110):
|
|
440
266
|
* 1. Active UAT → hard refuse (no multi-factor escape; self-approval impossible)
|
|
@@ -442,10 +268,19 @@ export function main(argv = process.argv.slice(2), seams = {}) {
|
|
|
442
268
|
* Required-arg validation runs before this so missing --campaign / --ops still report clearly.
|
|
443
269
|
*/
|
|
444
270
|
const gateConfirm = () => {
|
|
445
|
-
|
|
271
|
+
if (args.cmd === "show")
|
|
272
|
+
return null;
|
|
273
|
+
const uatBlocked = refuseMintWhileUatActive(`authz:${args.cmd}`, args.projectRoot);
|
|
446
274
|
if (uatBlocked !== null)
|
|
447
275
|
return uatBlocked;
|
|
448
|
-
return refuseNonInteractiveMint(
|
|
276
|
+
return refuseNonInteractiveMint({
|
|
277
|
+
verb: `authz:${args.cmd}`,
|
|
278
|
+
confirm: args.confirm,
|
|
279
|
+
isTty: resolved.isTty,
|
|
280
|
+
environ: resolved.environ,
|
|
281
|
+
hasControllingTerminal: resolved.hasControllingTerminal,
|
|
282
|
+
readInteractiveConfirm: resolved.readInteractiveConfirm,
|
|
283
|
+
});
|
|
449
284
|
};
|
|
450
285
|
try {
|
|
451
286
|
switch (args.cmd) {
|
package/dist/dispatch.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ export interface DispatchIo {
|
|
|
10
10
|
writeErr: (text: string) => void;
|
|
11
11
|
}
|
|
12
12
|
/** CLI modules in packages/cli/src (excluding parity harnesses and bin/index). */
|
|
13
|
-
export declare const CLI_MODULE_VERBS: readonly ["agents-refresh", "cache", "check", "capacity-backfill", "capacity-show", "codebase-default-extractor", "codebase-map", "codebase-map-fresh", "codebase-projection-registry", "codebase-provider", "doctor", "install-upgrade", "install-uninstall", "migrate-preflight", "migrate-xbrief", "migrate-category-b", "framework-check-updates", "hook-dispatch", "umbrella-current-shape", "changelog-check", "change-init", "commit-lint", "coverage-hotspots", "policy", "authz", "escalation-cli", "pr-closing-keywords", "pr-merge-readiness", "pr-monitor", "pr-protected-issues", "pr-wait-mergeable", "pr-watch", "pr-finish-loop", "directive-finish-loop", "preflight-cache", "preflight-gh", "probe-session", "release", "release-e2e", "release-publish", "release-rollback", "scope-lifecycle", "scope-record-approved-scope", "lifecycle-event", "lifecycle-stats", "session-start", "session-ready", "plan-sequence", "slice", "subagent-monitor", "toolchain-check", "triage-actions", "triage-bootstrap", "triage-bulk", "triage-classify", "triage-help", "triage-queue", "triage-reconcile", "triage-refresh", "triage-scope", "triage-scope-drift", "triage-smoketest", "triage-subscribe", "triage-summary", "triage-welcome", "ts-check-lane", "vbrief-activate", "vbrief-build", "vbrief-preflight", "vbrief-reconcile", "vbrief-validate", "vbrief-validation", "xbrief-create", "xbrief-verify", "verify-branch", "verify-encoding", "verify-forward-coverage", "verify-test-boundary", "verify-scope-provenance", "verify-consumer-check-contract", "verify-hooks-installed", "verify-investigation", "verify-judgment-gates", "verify-no-task-runtime", "validate-links", "validate-strategy-output", "verify-biome-config", "verify-bridge-drift", "verify-capacity", "verify-contained-writes", "verify-telemetry-coverage", "verify-content-manifest", "verify-skill-external-fetch-gate", "verify-contract-drift", "verify-cursor-tier1", "verify-openclaw-tier1", "verify-go-freeze", "verify-scm-boundary", "verify-session-ritual", "verify-plan-sequence", "verify-stubs", "verify-xbrief-drift", "rule-ownership-lint", "verify-story-ready", "verify-review-monitor", "verify-l4-owner", "verify-subagent-alive", "review-monitor-register", "review-monitor-release", "verify-tools", "verify-wip-cap", "verify-orphan-active", "verify-completed-tracked", "verify-literal-ac", "verify-ac", "verify-agents-md-budget", "verify-agents-md-advisory", "verify-eval-health-relocation", "verify-eval-triggers-relocation", "eval-health", "eval-run", "eval-report", "eval-triggers"];
|
|
13
|
+
export declare const CLI_MODULE_VERBS: readonly ["agents-refresh", "cache", "check", "capacity-backfill", "capacity-show", "codebase-default-extractor", "codebase-map", "codebase-map-fresh", "codebase-projection-registry", "codebase-provider", "doctor", "install-upgrade", "install-uninstall", "migrate-preflight", "migrate-xbrief", "migrate-category-b", "framework-check-updates", "hook-dispatch", "umbrella-current-shape", "changelog-check", "change-init", "commit-lint", "coverage-hotspots", "policy", "authz", "escalation-cli", "pr-closing-keywords", "pr-merge-readiness", "pr-monitor", "pr-protected-issues", "pr-wait-mergeable", "pr-watch", "pr-finish-loop", "directive-finish-loop", "preflight-cache", "preflight-gh", "probe-session", "release", "release-e2e", "release-publish", "release-rollback", "scope-lifecycle", "scope-record-approved-scope", "scm-sync-default", "lifecycle-event", "lifecycle-stats", "session-start", "session-ready", "occupancy-steal", "plan-sequence", "slice", "subagent-monitor", "toolchain-check", "triage-actions", "triage-bootstrap", "triage-bulk", "triage-classify", "triage-help", "triage-queue", "triage-reconcile", "triage-refresh", "triage-scope", "triage-scope-drift", "triage-smoketest", "triage-subscribe", "triage-summary", "triage-welcome", "ts-check-lane", "vbrief-activate", "vbrief-build", "vbrief-preflight", "vbrief-reconcile", "vbrief-validate", "vbrief-validation", "xbrief-create", "xbrief-verify", "verify-branch", "verify-encoding", "verify-forward-coverage", "verify-test-boundary", "verify-scope-provenance", "verify-consumer-check-contract", "verify-hooks-installed", "verify-investigation", "verify-judgment-gates", "verify-no-task-runtime", "validate-links", "validate-strategy-output", "verify-biome-config", "verify-bridge-drift", "verify-capacity", "verify-contained-writes", "verify-telemetry-coverage", "verify-content-manifest", "verify-skill-external-fetch-gate", "verify-contract-drift", "verify-cursor-tier1", "verify-openclaw-tier1", "verify-go-freeze", "verify-scm-boundary", "verify-session-ritual", "verify-plan-sequence", "verify-stubs", "verify-xbrief-drift", "rule-ownership-lint", "verify-story-ready", "verify-review-monitor", "verify-l4-owner", "verify-subagent-alive", "review-monitor-register", "review-monitor-release", "verify-tools", "verify-wip-cap", "verify-orphan-active", "verify-completed-tracked", "verify-literal-ac", "verify-ac", "verify-agents-md-budget", "verify-agents-md-advisory", "verify-eval-health-relocation", "verify-eval-triggers-relocation", "eval-health", "eval-run", "eval-report", "eval-triggers"];
|
|
14
14
|
/** Core-only CLI entrypoints without a packages/cli wrapper. */
|
|
15
15
|
export declare const CORE_MODULE_VERBS: readonly ["scm", "scm-readiness", "github-auth-modes", "github-body", "issue-emit", "issue-ingest", "issue-sync-from-xbrief", "reconcile-issues", "swarm-launch", "swarm-complete-cohort", "swarm-finalize-cohort", "swarm-pre-dispatch", "swarm-readiness", "swarm-routing-verify", "swarm-routing-set", "swarm-verify-review-clean", "swarm-worktrees", "framework-commands", "pack-render", "packs-slice", "prd-render", "export-spec", "project-render", "roadmap-render", "rule-map", "spec-render", "spec-validate", "code-structure-validate", "pack-migrate-skills", "pack-migrate-rules", "pack-migrate-strategies", "pack-migrate-patterns", "pack-migrate-swarm-spec", "policy-set", "setup-ghx", "scope-undo", "scope-demote", "scope-decompose", "changelog-resolve-unreleased", "architecture-preflight-sor", "feedback-file", "value-readback", "product-signal", "freshness-report", "decision-write", "decision-list"];
|
|
16
16
|
/** Colon aliases for triage-actions (mirrors cli-router SUBCOMMAND_ROUTES). */
|
package/dist/dispatch.js
CHANGED
|
@@ -69,10 +69,12 @@ export const CLI_MODULE_VERBS = [
|
|
|
69
69
|
"release-rollback",
|
|
70
70
|
"scope-lifecycle",
|
|
71
71
|
"scope-record-approved-scope",
|
|
72
|
+
"scm-sync-default",
|
|
72
73
|
"lifecycle-event",
|
|
73
74
|
"lifecycle-stats",
|
|
74
75
|
"session-start",
|
|
75
76
|
"session-ready",
|
|
77
|
+
"occupancy-steal",
|
|
76
78
|
"plan-sequence",
|
|
77
79
|
"slice",
|
|
78
80
|
"subagent-monitor",
|
|
@@ -293,6 +295,11 @@ export const VERB_ALIASES = {
|
|
|
293
295
|
"verify:no-task-runtime": "verify-no-task-runtime",
|
|
294
296
|
"vbrief:validate": "vbrief-validate",
|
|
295
297
|
"vbrief:preflight": "vbrief-preflight",
|
|
298
|
+
// xbrief:-prefixed aliases for the canonical post-#2109 lifecycle spelling.
|
|
299
|
+
// xbrief:validate closes the #3483 gap: the swarm skill named it at MUST
|
|
300
|
+
// level but only vbrief:validate resolved.
|
|
301
|
+
"xbrief:validate": "vbrief-validate",
|
|
302
|
+
"xbrief:activate": "vbrief-activate",
|
|
296
303
|
"xbrief:preflight": "vbrief-preflight",
|
|
297
304
|
"xbrief:create": "xbrief-create",
|
|
298
305
|
"xbrief:verify": "xbrief-verify",
|
|
@@ -352,6 +359,7 @@ export const VERB_ALIASES = {
|
|
|
352
359
|
upgrade: "install-upgrade",
|
|
353
360
|
"session:start": "session-start",
|
|
354
361
|
"session:ready": "session-ready",
|
|
362
|
+
"occupancy:steal": "occupancy-steal",
|
|
355
363
|
...FRESHNESS_COLON_ALIASES,
|
|
356
364
|
"lifecycle:event": "lifecycle-event",
|
|
357
365
|
"lifecycle:stats": "lifecycle-stats",
|
|
@@ -380,6 +388,7 @@ export const VERB_ALIASES = {
|
|
|
380
388
|
"setup:ghx": "setup-ghx",
|
|
381
389
|
"scm:status": "scm-readiness",
|
|
382
390
|
"scm:readiness": "scm-readiness",
|
|
391
|
+
"scm:sync-default": "scm-sync-default",
|
|
383
392
|
};
|
|
384
393
|
/** CLI modules living under verify-source-cli/ or content-validate-cli/ subdirs. */
|
|
385
394
|
const SUBDIR_CLI_STEMS = {
|
|
@@ -2599,6 +2608,10 @@ const CURATED_HELP_GROUPS = [
|
|
|
2599
2608
|
name: "session:ready",
|
|
2600
2609
|
summary: "One-shot recovery to gated write-ready (session + ritual + cache)",
|
|
2601
2610
|
},
|
|
2611
|
+
{
|
|
2612
|
+
name: "occupancy:steal",
|
|
2613
|
+
summary: "Supersede a live worktree occupancy lease (--confirm --occupant)",
|
|
2614
|
+
},
|
|
2602
2615
|
{
|
|
2603
2616
|
name: "freshness:report",
|
|
2604
2617
|
summary: "Bound vs live deposit generation (current|stale_soft|stale_hard)",
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Env markers that indicate an agent/host/CI shell even when stdin reports a TTY
|
|
3
|
+
* (pseudo-terminal residual; #3110 Greptile). Presence refuses mint.
|
|
4
|
+
* Expanded for dogfood conf 5/5 — markers are fail-closed (any non-empty value).
|
|
5
|
+
*/
|
|
6
|
+
export declare const AUTHZ_AGENT_SHELL_ENV_MARKERS: readonly ["CLAUDECODE", "CLAUDE_CODE", "CLAUDE_CODE_ENTRYPOINT", "CURSOR_AGENT", "CURSOR_TRACE_ID", "CURSOR_SESSION_ID", "AIDER", "CONTINUE_CLI", "CODEX_SANDBOX", "CODEX_CI", "OPENAI_CODEX", "OPENCLAW", "OPENCLAW_STATE_DIR", "DEFT_PROBE_OPENCLAW", "DEFT_HOOK_HOST", "DEFT_AGENT_SHELL", "DEFT_AGENT_RUNTIME", "WARP_SESSION_ID", "WARP_HARNESS", "WARP_RUN_ID", "GEMINI_CLI", "AMP_CLI", "SWARM_AGENT", "AI_AGENT", "CI", "CONTINUOUS_INTEGRATION", "GITHUB_ACTIONS", "GITLAB_CI", "CIRCLECI", "BUILDKITE", "TRAVIS", "JENKINS_URL", "TEAMCITY_VERSION", "TF_BUILD", "APPVEYOR", "BITBUCKET_BUILD_NUMBER", "CODEBUILD_BUILD_ID"];
|
|
7
|
+
/** Phrase an operator must type on the controlling TTY after --confirm (#3110). */
|
|
8
|
+
export declare const AUTHZ_INTERACTIVE_CONFIRM_PHRASE = "mint";
|
|
9
|
+
/** Testable seams for TTY / agent-shell detection (#3110 / #3384). */
|
|
10
|
+
export interface HumanPresenceMintSeams {
|
|
11
|
+
/**
|
|
12
|
+
* When true, interactive human TTY is present.
|
|
13
|
+
* Default: both stdin and stdout report isTTY (pseudo-TTY residual; #3110).
|
|
14
|
+
*/
|
|
15
|
+
readonly isTty?: () => boolean;
|
|
16
|
+
/** Environ for agent-shell marker detection (default: process.env). */
|
|
17
|
+
readonly environ?: NodeJS.ProcessEnv;
|
|
18
|
+
/**
|
|
19
|
+
* True when a controlling terminal device is available (`/dev/tty` or `CONIN$`).
|
|
20
|
+
* Default: open/close the platform controlling terminal (fail-closed on error).
|
|
21
|
+
*/
|
|
22
|
+
readonly hasControllingTerminal?: () => boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Read one interactive confirmation line from the operator (trimmed).
|
|
25
|
+
* Default: one line from the controlling terminal. Tests inject a fixed phrase.
|
|
26
|
+
*/
|
|
27
|
+
readonly readInteractiveConfirm?: () => string | null;
|
|
28
|
+
}
|
|
29
|
+
export declare function looksLikeAgentShell(environ: NodeJS.ProcessEnv): boolean;
|
|
30
|
+
export declare function resolveHumanPresenceMintSeams(seams?: HumanPresenceMintSeams): Required<HumanPresenceMintSeams>;
|
|
31
|
+
/** Active UAT campaign id, or null when no lease is active. */
|
|
32
|
+
export declare function activeUatCampaignId(projectRoot: string): string | null;
|
|
33
|
+
/**
|
|
34
|
+
* While any UAT lease is active, refuse mint (#3110 / #3384).
|
|
35
|
+
*
|
|
36
|
+
* No multi-factor escape: TTY, `--confirm`, and typed phrase never authorize.
|
|
37
|
+
* Returns an exit code when blocked, or null when mint may continue.
|
|
38
|
+
*/
|
|
39
|
+
export declare function refuseMintWhileUatActive(verb: string, projectRoot: string): number | null;
|
|
40
|
+
/**
|
|
41
|
+
* Refuse non-interactive / agent-shell mint outside UAT (#3110 / #3384).
|
|
42
|
+
*
|
|
43
|
+
* Multi-factor human-presence gate (applies only when UAT lease is inactive):
|
|
44
|
+
* 1. No known agent/CI env markers
|
|
45
|
+
* 2. Interactive TTY (stdin + stdout isTTY)
|
|
46
|
+
* 3. Controlling terminal device present (`/dev/tty` / `CONIN$`)
|
|
47
|
+
* 4. Explicit argv `--confirm` (flag alone never enough)
|
|
48
|
+
* 5. Interactive typed phrase `mint` (argv --confirm alone never enough even on PTY)
|
|
49
|
+
*
|
|
50
|
+
* Fail-closed: if a real human interactive path cannot be proven, refuse mint.
|
|
51
|
+
* Returns an exit code when blocked, or null when the mutation may proceed.
|
|
52
|
+
*/
|
|
53
|
+
export declare function refuseNonInteractiveMint(input: {
|
|
54
|
+
readonly verb: string;
|
|
55
|
+
readonly confirm: boolean;
|
|
56
|
+
readonly isTty: () => boolean;
|
|
57
|
+
readonly environ: NodeJS.ProcessEnv;
|
|
58
|
+
readonly hasControllingTerminal: () => boolean;
|
|
59
|
+
readonly readInteractiveConfirm: () => string | null;
|
|
60
|
+
}): number | null;
|
|
61
|
+
//# sourceMappingURL=human-presence-mint.d.ts.map
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared #3110 human-presence mint gate (#3384).
|
|
3
|
+
*
|
|
4
|
+
* Used by `authz` mutating verbs and `scope:record-approved-scope`. Multi-factor
|
|
5
|
+
* human presence: interactive TTY + controlling terminal + `--confirm` + typed
|
|
6
|
+
* phrase `mint`. Agent/CI env markers refuse fail-closed. `--actor` is never
|
|
7
|
+
* an input here — display only at the caller.
|
|
8
|
+
*/
|
|
9
|
+
import { closeSync, openSync, readSync } from "node:fs";
|
|
10
|
+
import { loadAuthzState } from "@deftai/directive-core/authz";
|
|
11
|
+
/**
|
|
12
|
+
* Env markers that indicate an agent/host/CI shell even when stdin reports a TTY
|
|
13
|
+
* (pseudo-terminal residual; #3110 Greptile). Presence refuses mint.
|
|
14
|
+
* Expanded for dogfood conf 5/5 — markers are fail-closed (any non-empty value).
|
|
15
|
+
*/
|
|
16
|
+
export const AUTHZ_AGENT_SHELL_ENV_MARKERS = [
|
|
17
|
+
// Coding agents / IDEs
|
|
18
|
+
"CLAUDECODE",
|
|
19
|
+
"CLAUDE_CODE",
|
|
20
|
+
"CLAUDE_CODE_ENTRYPOINT",
|
|
21
|
+
"CURSOR_AGENT",
|
|
22
|
+
"CURSOR_TRACE_ID",
|
|
23
|
+
"CURSOR_SESSION_ID",
|
|
24
|
+
"AIDER",
|
|
25
|
+
"CONTINUE_CLI",
|
|
26
|
+
"CODEX_SANDBOX",
|
|
27
|
+
"CODEX_CI",
|
|
28
|
+
"OPENAI_CODEX",
|
|
29
|
+
"OPENCLAW",
|
|
30
|
+
"OPENCLAW_STATE_DIR",
|
|
31
|
+
"DEFT_PROBE_OPENCLAW",
|
|
32
|
+
"DEFT_HOOK_HOST",
|
|
33
|
+
"DEFT_AGENT_SHELL",
|
|
34
|
+
"DEFT_AGENT_RUNTIME",
|
|
35
|
+
"WARP_SESSION_ID",
|
|
36
|
+
"WARP_HARNESS",
|
|
37
|
+
"WARP_RUN_ID",
|
|
38
|
+
"GEMINI_CLI",
|
|
39
|
+
"AMP_CLI",
|
|
40
|
+
"SWARM_AGENT",
|
|
41
|
+
"AI_AGENT",
|
|
42
|
+
// CI / automation (never a human interactive operator mint)
|
|
43
|
+
"CI",
|
|
44
|
+
"CONTINUOUS_INTEGRATION",
|
|
45
|
+
"GITHUB_ACTIONS",
|
|
46
|
+
"GITLAB_CI",
|
|
47
|
+
"CIRCLECI",
|
|
48
|
+
"BUILDKITE",
|
|
49
|
+
"TRAVIS",
|
|
50
|
+
"JENKINS_URL",
|
|
51
|
+
"TEAMCITY_VERSION",
|
|
52
|
+
"TF_BUILD",
|
|
53
|
+
"APPVEYOR",
|
|
54
|
+
"BITBUCKET_BUILD_NUMBER",
|
|
55
|
+
"CODEBUILD_BUILD_ID",
|
|
56
|
+
];
|
|
57
|
+
/** Phrase an operator must type on the controlling TTY after --confirm (#3110). */
|
|
58
|
+
export const AUTHZ_INTERACTIVE_CONFIRM_PHRASE = "mint";
|
|
59
|
+
export function looksLikeAgentShell(environ) {
|
|
60
|
+
for (const key of AUTHZ_AGENT_SHELL_ENV_MARKERS) {
|
|
61
|
+
const v = environ[key];
|
|
62
|
+
if (v !== undefined && String(v).trim().length > 0)
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
function defaultIsTty() {
|
|
68
|
+
return process.stdin.isTTY === true && process.stdout.isTTY === true;
|
|
69
|
+
}
|
|
70
|
+
function defaultHasControllingTerminal() {
|
|
71
|
+
try {
|
|
72
|
+
const path = process.platform === "win32" ? "CONIN$" : "/dev/tty";
|
|
73
|
+
const fd = openSync(path, "r");
|
|
74
|
+
closeSync(fd);
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function defaultReadInteractiveConfirm() {
|
|
82
|
+
// Read from the controlling terminal device — not redirected/piped stdin —
|
|
83
|
+
// so agent-controlled stdin alone cannot supply the confirm phrase (#3110).
|
|
84
|
+
let fd = null;
|
|
85
|
+
try {
|
|
86
|
+
const path = process.platform === "win32" ? "CONIN$" : "/dev/tty";
|
|
87
|
+
fd = openSync(path, "r");
|
|
88
|
+
const buf = Buffer.alloc(256);
|
|
89
|
+
const n = readSync(fd, buf, 0, buf.length, null);
|
|
90
|
+
if (n <= 0)
|
|
91
|
+
return null;
|
|
92
|
+
return buf.subarray(0, n).toString("utf8").trim();
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
finally {
|
|
98
|
+
if (fd !== null) {
|
|
99
|
+
try {
|
|
100
|
+
closeSync(fd);
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
/* ignore */
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
export function resolveHumanPresenceMintSeams(seams = {}) {
|
|
109
|
+
return {
|
|
110
|
+
isTty: seams.isTty ?? defaultIsTty,
|
|
111
|
+
environ: seams.environ ?? process.env,
|
|
112
|
+
hasControllingTerminal: seams.hasControllingTerminal ?? defaultHasControllingTerminal,
|
|
113
|
+
readInteractiveConfirm: seams.readInteractiveConfirm ?? defaultReadInteractiveConfirm,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
/** Active UAT campaign id, or null when no lease is active. */
|
|
117
|
+
export function activeUatCampaignId(projectRoot) {
|
|
118
|
+
const state = loadAuthzState(projectRoot);
|
|
119
|
+
if (state.uat === null || !state.uat.active)
|
|
120
|
+
return null;
|
|
121
|
+
return state.uat.campaignId;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* While any UAT lease is active, refuse mint (#3110 / #3384).
|
|
125
|
+
*
|
|
126
|
+
* No multi-factor escape: TTY, `--confirm`, and typed phrase never authorize.
|
|
127
|
+
* Returns an exit code when blocked, or null when mint may continue.
|
|
128
|
+
*/
|
|
129
|
+
export function refuseMintWhileUatActive(verb, projectRoot) {
|
|
130
|
+
const campaignId = activeUatCampaignId(projectRoot);
|
|
131
|
+
if (campaignId === null)
|
|
132
|
+
return null;
|
|
133
|
+
process.stderr.write(`${verb}: refusing mint while UAT lease is ACTIVE (campaign=${campaignId}). ` +
|
|
134
|
+
"Under active UAT, mint is hard-refused — no TTY, --confirm, or phrase path " +
|
|
135
|
+
"authorizes remint (#3110 / #3384). Mint before uat-start; clear the lease " +
|
|
136
|
+
"out-of-band to end UAT.\n");
|
|
137
|
+
return 2;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Refuse non-interactive / agent-shell mint outside UAT (#3110 / #3384).
|
|
141
|
+
*
|
|
142
|
+
* Multi-factor human-presence gate (applies only when UAT lease is inactive):
|
|
143
|
+
* 1. No known agent/CI env markers
|
|
144
|
+
* 2. Interactive TTY (stdin + stdout isTTY)
|
|
145
|
+
* 3. Controlling terminal device present (`/dev/tty` / `CONIN$`)
|
|
146
|
+
* 4. Explicit argv `--confirm` (flag alone never enough)
|
|
147
|
+
* 5. Interactive typed phrase `mint` (argv --confirm alone never enough even on PTY)
|
|
148
|
+
*
|
|
149
|
+
* Fail-closed: if a real human interactive path cannot be proven, refuse mint.
|
|
150
|
+
* Returns an exit code when blocked, or null when the mutation may proceed.
|
|
151
|
+
*/
|
|
152
|
+
export function refuseNonInteractiveMint(input) {
|
|
153
|
+
const { verb, confirm } = input;
|
|
154
|
+
if (looksLikeAgentShell(input.environ)) {
|
|
155
|
+
process.stderr.write(`${verb}: refusing operator-cli stamp from an agent/host/CI shell ` +
|
|
156
|
+
`(detected agent or CI env marker). Mint requires a human interactive ` +
|
|
157
|
+
"TTY without agent-shell markers, plus --confirm and typed phrase (#3110).\n");
|
|
158
|
+
return 2;
|
|
159
|
+
}
|
|
160
|
+
const tty = input.isTty();
|
|
161
|
+
if (!tty && !confirm) {
|
|
162
|
+
process.stderr.write(`${verb}: refusing non-interactive operator-cli stamp. ` +
|
|
163
|
+
"Mint requires interactive TTY, --confirm, and typed phrase " +
|
|
164
|
+
`'${AUTHZ_INTERACTIVE_CONFIRM_PHRASE}' (#3110).\n`);
|
|
165
|
+
return 2;
|
|
166
|
+
}
|
|
167
|
+
if (!tty) {
|
|
168
|
+
process.stderr.write(`${verb}: refusing non-TTY operator-cli stamp. ` +
|
|
169
|
+
"--confirm alone never authorizes mint — interactive TTY is required (#3110).\n");
|
|
170
|
+
return 2;
|
|
171
|
+
}
|
|
172
|
+
if (!confirm) {
|
|
173
|
+
process.stderr.write(`${verb}: refusing operator-cli stamp without --confirm. ` +
|
|
174
|
+
"Interactive TTY alone never authorizes mint — pass --confirm explicitly (#3110).\n");
|
|
175
|
+
return 2;
|
|
176
|
+
}
|
|
177
|
+
if (!input.hasControllingTerminal()) {
|
|
178
|
+
process.stderr.write(`${verb}: refusing operator-cli stamp without a controlling terminal. ` +
|
|
179
|
+
"Open a real interactive console (not a headless/agent pipe) to mint (#3110).\n");
|
|
180
|
+
return 2;
|
|
181
|
+
}
|
|
182
|
+
process.stderr.write(`${verb}: type '${AUTHZ_INTERACTIVE_CONFIRM_PHRASE}' and press Enter to confirm operator mint: `);
|
|
183
|
+
const line = input.readInteractiveConfirm();
|
|
184
|
+
const phrase = (line ?? "").trim().toLowerCase();
|
|
185
|
+
if (phrase !== AUTHZ_INTERACTIVE_CONFIRM_PHRASE) {
|
|
186
|
+
process.stderr.write(`\n${verb}: interactive confirm phrase mismatch (got ${JSON.stringify(line ?? "")}). ` +
|
|
187
|
+
`Type exactly '${AUTHZ_INTERACTIVE_CONFIRM_PHRASE}' on the controlling TTY (#3110).\n`);
|
|
188
|
+
return 2;
|
|
189
|
+
}
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
//# sourceMappingURL=human-presence-mint.js.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
export declare function parseArgs(argv: readonly string[]): {
|
|
3
|
+
projectRoot: string;
|
|
4
|
+
confirm: boolean;
|
|
5
|
+
occupant: string | null;
|
|
6
|
+
error?: string;
|
|
7
|
+
};
|
|
8
|
+
export declare function run(argv: readonly string[]): number;
|
|
9
|
+
//# sourceMappingURL=occupancy-steal.d.ts.map
|
|
@@ -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
|