@deftai/directive 0.94.0 → 0.96.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 +29 -1
- package/dist/authz.js +248 -15
- package/dist/cli-router/route-argv.js +6 -0
- package/dist/dispatch.d.ts +3 -1
- package/dist/dispatch.js +26 -1
- package/dist/triage-actions.d.ts +4 -0
- package/dist/triage-actions.js +14 -2
- package/dist/triage-classify.d.ts +26 -4
- package/dist/triage-classify.js +196 -3
- package/dist/triage-queue-fixtures.d.ts +2 -0
- package/dist/triage-queue-fixtures.js +1 -0
- package/dist/triage-queue.d.ts +6 -2
- package/dist/triage-queue.js +56 -2
- package/dist/verify-hooks-installed.d.ts +11 -1
- package/dist/verify-hooks-installed.js +23 -8
- package/package.json +3 -3
package/dist/authz.d.ts
CHANGED
|
@@ -1,4 +1,32 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Env markers that indicate an agent/host/CI shell even when stdin reports a TTY
|
|
4
|
+
* (pseudo-terminal residual; #3110 Greptile). Presence refuses mutating authz.
|
|
5
|
+
* Expanded for dogfood conf 5/5 — markers are fail-closed (any non-empty value).
|
|
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;
|
|
3
31
|
export default main;
|
|
4
32
|
//# sourceMappingURL=authz.d.ts.map
|
package/dist/authz.js
CHANGED
|
@@ -1,17 +1,75 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* Authz CLI (#2944 Wave 1 + #1095 Wave 4): human-origin grants + UAT lease +
|
|
3
|
+
* Authz CLI (#2944 Wave 1 + #1095 Wave 4 + #3110): human-origin grants + UAT lease +
|
|
4
4
|
* AFK closed-verb templates (mint via mintHumanOriginGrant only).
|
|
5
5
|
*
|
|
6
6
|
* deft authz:show
|
|
7
|
-
* deft authz:uat-start -- --campaign <id> [--actor <name>] [--note <text>]
|
|
8
|
-
* deft authz:uat-suspend
|
|
9
|
-
* deft authz:grant -- --operations edit,push --surfaces 'src/**' --cohort <id> ...
|
|
10
|
-
* deft authz:grant -- --template release-publish --target 0.30.0
|
|
11
|
-
* deft authz:grant -- --template finish-loop
|
|
12
|
-
* deft authz:revoke -- <grant-id>
|
|
7
|
+
* deft authz:uat-start -- --campaign <id> [--actor <name>] [--note <text>] [--confirm]
|
|
8
|
+
* deft authz:uat-suspend [--confirm]
|
|
9
|
+
* deft authz:grant -- --operations edit,push --surfaces 'src/**' --cohort <id> ... [--confirm]
|
|
10
|
+
* deft authz:grant -- --template release-publish --target 0.30.0 [--confirm]
|
|
11
|
+
* deft authz:grant -- --template finish-loop [--confirm]
|
|
12
|
+
* deft authz:revoke -- <grant-id> [--confirm]
|
|
13
|
+
*
|
|
14
|
+
* **UAT-active hard refuse (#3110):** while any UAT lease is active, ALL mutating
|
|
15
|
+
* verbs (`uat-start`, `uat-suspend`, `grant`, `revoke`) refuse unconditionally —
|
|
16
|
+
* no TTY, no `--confirm`, no typed phrase path. Self-approval under UAT is
|
|
17
|
+
* impossible by construction (agent PTY cannot mint operator-cli authority).
|
|
18
|
+
*
|
|
19
|
+
* **Outside UAT:** mutating verbs require multi-factor human presence: interactive
|
|
20
|
+
* TTY + controlling terminal + `--confirm` + typed phrase `mint`; agent/CI env
|
|
21
|
+
* markers refuse fail-closed. Argv `--confirm` alone is never enough.
|
|
22
|
+
*/
|
|
23
|
+
import { closeSync, openSync, readSync } from "node:fs";
|
|
24
|
+
import { AFK_TEMPLATE_NAMES, AUTHZ_OPERATIONS, CLOSED_VERB_TEMPLATE_NAMES, FINISH_LOOP_TEMPLATE_NAME, isAfkTemplateName, isClosedVerbTemplateName, isFinishLoopTemplateName, loadAuthzState, mintAfkTemplateGrant, mintHumanOriginGrant, revokeGrant, showAuthzSnapshot, startUatLease, suspendUatLease, } from "@deftai/directive-core/authz";
|
|
25
|
+
/**
|
|
26
|
+
* Env markers that indicate an agent/host/CI shell even when stdin reports a TTY
|
|
27
|
+
* (pseudo-terminal residual; #3110 Greptile). Presence refuses mutating authz.
|
|
28
|
+
* Expanded for dogfood conf 5/5 — markers are fail-closed (any non-empty value).
|
|
13
29
|
*/
|
|
14
|
-
|
|
30
|
+
export const AUTHZ_AGENT_SHELL_ENV_MARKERS = [
|
|
31
|
+
// Coding agents / IDEs
|
|
32
|
+
"CLAUDECODE",
|
|
33
|
+
"CLAUDE_CODE",
|
|
34
|
+
"CLAUDE_CODE_ENTRYPOINT",
|
|
35
|
+
"CURSOR_AGENT",
|
|
36
|
+
"CURSOR_TRACE_ID",
|
|
37
|
+
"CURSOR_SESSION_ID",
|
|
38
|
+
"AIDER",
|
|
39
|
+
"CONTINUE_CLI",
|
|
40
|
+
"CODEX_SANDBOX",
|
|
41
|
+
"CODEX_CI",
|
|
42
|
+
"OPENAI_CODEX",
|
|
43
|
+
"OPENCLAW",
|
|
44
|
+
"OPENCLAW_STATE_DIR",
|
|
45
|
+
"DEFT_PROBE_OPENCLAW",
|
|
46
|
+
"DEFT_HOOK_HOST",
|
|
47
|
+
"DEFT_AGENT_SHELL",
|
|
48
|
+
"DEFT_AGENT_RUNTIME",
|
|
49
|
+
"WARP_SESSION_ID",
|
|
50
|
+
"WARP_HARNESS",
|
|
51
|
+
"WARP_RUN_ID",
|
|
52
|
+
"GEMINI_CLI",
|
|
53
|
+
"AMP_CLI",
|
|
54
|
+
"SWARM_AGENT",
|
|
55
|
+
"AI_AGENT",
|
|
56
|
+
// CI / automation (never a human interactive operator mint)
|
|
57
|
+
"CI",
|
|
58
|
+
"CONTINUOUS_INTEGRATION",
|
|
59
|
+
"GITHUB_ACTIONS",
|
|
60
|
+
"GITLAB_CI",
|
|
61
|
+
"CIRCLECI",
|
|
62
|
+
"BUILDKITE",
|
|
63
|
+
"TRAVIS",
|
|
64
|
+
"JENKINS_URL",
|
|
65
|
+
"TEAMCITY_VERSION",
|
|
66
|
+
"TF_BUILD",
|
|
67
|
+
"APPVEYOR",
|
|
68
|
+
"BITBUCKET_BUILD_NUMBER",
|
|
69
|
+
"CODEBUILD_BUILD_ID",
|
|
70
|
+
];
|
|
71
|
+
/** Phrase an operator must type on the controlling TTY after --confirm (#3110). */
|
|
72
|
+
export const AUTHZ_INTERACTIVE_CONFIRM_PHRASE = "mint";
|
|
15
73
|
function parseOps(raw) {
|
|
16
74
|
const allowed = new Set(AUTHZ_OPERATIONS);
|
|
17
75
|
const out = [];
|
|
@@ -47,6 +105,7 @@ function parseArgv(argv) {
|
|
|
47
105
|
template: null,
|
|
48
106
|
target: null,
|
|
49
107
|
format: "text",
|
|
108
|
+
confirm: false,
|
|
50
109
|
};
|
|
51
110
|
const args = [...argv];
|
|
52
111
|
// Drop leading `--` separators from task-style invocation.
|
|
@@ -165,6 +224,10 @@ function parseArgv(argv) {
|
|
|
165
224
|
base.target = args[++i] ?? null;
|
|
166
225
|
continue;
|
|
167
226
|
}
|
|
227
|
+
if (a === "--confirm") {
|
|
228
|
+
base.confirm = true;
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
168
231
|
if (!a.startsWith("-") && base.cmd === "revoke" && base.grantId === null) {
|
|
169
232
|
base.grantId = a;
|
|
170
233
|
continue;
|
|
@@ -179,16 +242,26 @@ function helpText() {
|
|
|
179
242
|
return [
|
|
180
243
|
"Usage:",
|
|
181
244
|
" deft authz:show [--format json]",
|
|
182
|
-
" deft authz:uat-start -- --campaign <id> [--actor <name>] [--note <text>]",
|
|
183
|
-
" deft authz:uat-suspend",
|
|
245
|
+
" deft authz:uat-start -- --campaign <id> [--actor <name>] [--note <text>] [--confirm]",
|
|
246
|
+
" deft authz:uat-suspend [--confirm]",
|
|
184
247
|
" deft authz:grant -- --operations edit,push --surfaces 'src/**' --cohort <id> \\",
|
|
185
|
-
" [--stories 2944] [--plan-ref <id>] [--repo owner/name] [--branch <b>] [--expires ISO]",
|
|
186
|
-
" deft authz:grant -- --template release-publish --target 0.30.0 [--actor <name>] [--expires ISO]",
|
|
187
|
-
" deft authz:grant -- --template finish-loop [--actor <name>] [--expires ISO]",
|
|
188
|
-
" deft authz:revoke -- <grant-id>",
|
|
248
|
+
" [--stories 2944] [--plan-ref <id>] [--repo owner/name] [--branch <b>] [--expires ISO] [--confirm]",
|
|
249
|
+
" deft authz:grant -- --template release-publish --target 0.30.0 [--actor <name>] [--expires ISO] [--confirm]",
|
|
250
|
+
" deft authz:grant -- --template finish-loop [--actor <name>] [--expires ISO] [--confirm]",
|
|
251
|
+
" deft authz:revoke -- <grant-id> [--confirm]",
|
|
189
252
|
"",
|
|
190
253
|
"Human-origin grants are minted only via this CLI (origin.kind=operator-cli).",
|
|
191
254
|
"Self-authored xBRIEF/lifecycle/dispatch tokens never satisfy implement gates (#2944).",
|
|
255
|
+
"While any UAT lease is ACTIVE (#3110): ALL mutating verbs refuse unconditionally",
|
|
256
|
+
" (grant / uat-start / uat-suspend / revoke) — no TTY, --confirm, or phrase path.",
|
|
257
|
+
" Self-approval under UAT is impossible by construction. Mint grants BEFORE uat-start.",
|
|
258
|
+
"Outside UAT, mutating verbs require multi-factor human presence (#3110):",
|
|
259
|
+
" - Interactive TTY (stdin+stdout) + controlling terminal (/dev/tty|CONIN$)",
|
|
260
|
+
" - Explicit --confirm (argv flag alone never enough)",
|
|
261
|
+
" - Typed phrase 'mint' on the controlling TTY (PTY+--confirm alone never enough)",
|
|
262
|
+
" - Known agent/CI env markers always refuse (fail-closed).",
|
|
263
|
+
" End UAT only after the lease is cleared out-of-band (state edit / suspend path",
|
|
264
|
+
" that does not run while the lease is active) — or suspend before re-minting.",
|
|
192
265
|
"",
|
|
193
266
|
`AFK templates (#1095 / #871): ${AFK_TEMPLATE_NAMES.join(", ")}`,
|
|
194
267
|
` Closed-verb (#1095): ${CLOSED_VERB_TEMPLATE_NAMES.join(", ")} — require --target`,
|
|
@@ -197,7 +270,131 @@ function helpText() {
|
|
|
197
270
|
" Env bypass for a single shell: DEFT_ALLOW_RELEASE_PUBLISH=1 / DEFT_ALLOW_FINISH_LOOP=1.",
|
|
198
271
|
].join("\n");
|
|
199
272
|
}
|
|
200
|
-
|
|
273
|
+
function looksLikeAgentShell(environ) {
|
|
274
|
+
for (const key of AUTHZ_AGENT_SHELL_ENV_MARKERS) {
|
|
275
|
+
const v = environ[key];
|
|
276
|
+
if (v !== undefined && String(v).trim().length > 0)
|
|
277
|
+
return true;
|
|
278
|
+
}
|
|
279
|
+
return false;
|
|
280
|
+
}
|
|
281
|
+
function defaultHasControllingTerminal() {
|
|
282
|
+
try {
|
|
283
|
+
const path = process.platform === "win32" ? "CONIN$" : "/dev/tty";
|
|
284
|
+
const fd = openSync(path, "r");
|
|
285
|
+
closeSync(fd);
|
|
286
|
+
return true;
|
|
287
|
+
}
|
|
288
|
+
catch {
|
|
289
|
+
return false;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
function defaultReadInteractiveConfirm() {
|
|
293
|
+
// Read from the controlling terminal device — not redirected/piped stdin —
|
|
294
|
+
// so agent-controlled stdin alone cannot supply the confirm phrase (#3110).
|
|
295
|
+
let fd = null;
|
|
296
|
+
try {
|
|
297
|
+
const path = process.platform === "win32" ? "CONIN$" : "/dev/tty";
|
|
298
|
+
fd = openSync(path, "r");
|
|
299
|
+
const buf = Buffer.alloc(256);
|
|
300
|
+
const n = readSync(fd, buf, 0, buf.length, null);
|
|
301
|
+
if (n <= 0)
|
|
302
|
+
return null;
|
|
303
|
+
return buf.subarray(0, n).toString("utf8").trim();
|
|
304
|
+
}
|
|
305
|
+
catch {
|
|
306
|
+
return null;
|
|
307
|
+
}
|
|
308
|
+
finally {
|
|
309
|
+
if (fd !== null) {
|
|
310
|
+
try {
|
|
311
|
+
closeSync(fd);
|
|
312
|
+
}
|
|
313
|
+
catch {
|
|
314
|
+
/* ignore */
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* While any UAT lease is active, refuse ALL mutating authz CLI verbs (#3110).
|
|
321
|
+
*
|
|
322
|
+
* No multi-factor escape: TTY, `--confirm`, and typed phrase never authorize.
|
|
323
|
+
* Self-approval under UAT is impossible by construction — agents with a PTY
|
|
324
|
+
* cannot mint/suspend/revoke operator-cli authority during an active lease.
|
|
325
|
+
* Operators mint fix-cohort grants *before* `uat-start`.
|
|
326
|
+
*
|
|
327
|
+
* Returns exit code when blocked, or null when the command may continue.
|
|
328
|
+
*/
|
|
329
|
+
function refuseMutatingAuthzWhileUatActive(projectRoot, cmd) {
|
|
330
|
+
if (cmd === "show")
|
|
331
|
+
return null;
|
|
332
|
+
const state = loadAuthzState(projectRoot);
|
|
333
|
+
if (state.uat === null || !state.uat.active)
|
|
334
|
+
return null;
|
|
335
|
+
process.stderr.write(`authz:${cmd}: refusing mutating authz while UAT lease is ACTIVE ` +
|
|
336
|
+
`(campaign=${state.uat.campaignId}). Under active UAT, grant / uat-start / ` +
|
|
337
|
+
"uat-suspend / revoke are hard-refused — no TTY, --confirm, or phrase path " +
|
|
338
|
+
"authorizes self-approval (#3110). Mint grants before uat-start; clear the " +
|
|
339
|
+
"lease out-of-band (edit .deft/authz/state.json as a human outside agent hooks) " +
|
|
340
|
+
"to end UAT.\n");
|
|
341
|
+
return 2;
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Refuse non-interactive / agent-shell operator-cli stamps outside UAT (#3110).
|
|
345
|
+
*
|
|
346
|
+
* Multi-factor human-presence gate (applies only when UAT lease is inactive):
|
|
347
|
+
* 1. No known agent/CI env markers
|
|
348
|
+
* 2. Interactive TTY (stdin + stdout isTTY)
|
|
349
|
+
* 3. Controlling terminal device present (`/dev/tty` / `CONIN$`)
|
|
350
|
+
* 4. Explicit argv `--confirm` (flag alone never enough)
|
|
351
|
+
* 5. Interactive typed phrase `mint` (argv --confirm alone never enough even on PTY)
|
|
352
|
+
*
|
|
353
|
+
* Fail-closed: if a real human interactive path cannot be proven, refuse mint.
|
|
354
|
+
* Returns an exit code when blocked, or null when the mutation may proceed.
|
|
355
|
+
*/
|
|
356
|
+
function refuseNonInteractiveMint(cmd, isTty, environ, confirm, hasControllingTerminal, readInteractiveConfirm) {
|
|
357
|
+
if (cmd === "show")
|
|
358
|
+
return null;
|
|
359
|
+
if (looksLikeAgentShell(environ)) {
|
|
360
|
+
process.stderr.write(`authz:${cmd}: refusing operator-cli stamp from an agent/host/CI shell ` +
|
|
361
|
+
`(detected agent or CI env marker). Mutating authz requires a human interactive ` +
|
|
362
|
+
"TTY without agent-shell markers, plus --confirm and typed phrase (#3110).\n");
|
|
363
|
+
return 2;
|
|
364
|
+
}
|
|
365
|
+
const tty = isTty();
|
|
366
|
+
if (!tty && !confirm) {
|
|
367
|
+
process.stderr.write(`authz:${cmd}: refusing non-interactive operator-cli stamp. ` +
|
|
368
|
+
"Mutating authz verbs require interactive TTY, --confirm, and typed phrase " +
|
|
369
|
+
`'${AUTHZ_INTERACTIVE_CONFIRM_PHRASE}' (#3110).\n`);
|
|
370
|
+
return 2;
|
|
371
|
+
}
|
|
372
|
+
if (!tty) {
|
|
373
|
+
process.stderr.write(`authz:${cmd}: refusing non-TTY operator-cli stamp. ` +
|
|
374
|
+
"--confirm alone never authorizes mint — interactive TTY is required (#3110).\n");
|
|
375
|
+
return 2;
|
|
376
|
+
}
|
|
377
|
+
if (!confirm) {
|
|
378
|
+
process.stderr.write(`authz:${cmd}: refusing operator-cli stamp without --confirm. ` +
|
|
379
|
+
"Interactive TTY alone never authorizes mint — pass --confirm explicitly (#3110).\n");
|
|
380
|
+
return 2;
|
|
381
|
+
}
|
|
382
|
+
if (!hasControllingTerminal()) {
|
|
383
|
+
process.stderr.write(`authz:${cmd}: refusing operator-cli stamp without a controlling terminal. ` +
|
|
384
|
+
"Open a real interactive console (not a headless/agent pipe) to mint (#3110).\n");
|
|
385
|
+
return 2;
|
|
386
|
+
}
|
|
387
|
+
process.stderr.write(`authz:${cmd}: type '${AUTHZ_INTERACTIVE_CONFIRM_PHRASE}' and press Enter to confirm operator mint: `);
|
|
388
|
+
const line = readInteractiveConfirm();
|
|
389
|
+
const phrase = (line ?? "").trim().toLowerCase();
|
|
390
|
+
if (phrase !== AUTHZ_INTERACTIVE_CONFIRM_PHRASE) {
|
|
391
|
+
process.stderr.write(`\nauthz:${cmd}: interactive confirm phrase mismatch (got ${JSON.stringify(line ?? "")}). ` +
|
|
392
|
+
`Type exactly '${AUTHZ_INTERACTIVE_CONFIRM_PHRASE}' on the controlling TTY (#3110).\n`);
|
|
393
|
+
return 2;
|
|
394
|
+
}
|
|
395
|
+
return null;
|
|
396
|
+
}
|
|
397
|
+
export function main(argv = process.argv.slice(2), seams = {}) {
|
|
201
398
|
const args = parseArgv(argv);
|
|
202
399
|
if (args.error === "help") {
|
|
203
400
|
process.stdout.write(`${helpText()}\n`);
|
|
@@ -208,6 +405,23 @@ export function main(argv = process.argv.slice(2)) {
|
|
|
208
405
|
process.stderr.write(`${helpText()}\n`);
|
|
209
406
|
return 2;
|
|
210
407
|
}
|
|
408
|
+
// Both stdin and stdout TTY — agent-allocated single-side PTY is not enough.
|
|
409
|
+
const isTty = seams.isTty ?? (() => process.stdin.isTTY === true && process.stdout.isTTY === true);
|
|
410
|
+
const environ = seams.environ ?? process.env;
|
|
411
|
+
const hasControllingTerminal = seams.hasControllingTerminal ?? defaultHasControllingTerminal;
|
|
412
|
+
const readInteractiveConfirm = seams.readInteractiveConfirm ?? defaultReadInteractiveConfirm;
|
|
413
|
+
/**
|
|
414
|
+
* Mutating-verb gate stack (#3110):
|
|
415
|
+
* 1. Active UAT → hard refuse (no multi-factor escape; self-approval impossible)
|
|
416
|
+
* 2. Else multi-factor: TTY + controlling tty + --confirm + typed phrase; agent/CI markers refuse
|
|
417
|
+
* Required-arg validation runs before this so missing --campaign / --ops still report clearly.
|
|
418
|
+
*/
|
|
419
|
+
const gateConfirm = () => {
|
|
420
|
+
const uatBlocked = refuseMutatingAuthzWhileUatActive(args.projectRoot, args.cmd);
|
|
421
|
+
if (uatBlocked !== null)
|
|
422
|
+
return uatBlocked;
|
|
423
|
+
return refuseNonInteractiveMint(args.cmd, isTty, environ, args.confirm, hasControllingTerminal, readInteractiveConfirm);
|
|
424
|
+
};
|
|
211
425
|
try {
|
|
212
426
|
switch (args.cmd) {
|
|
213
427
|
case "show": {
|
|
@@ -242,6 +456,9 @@ export function main(argv = process.argv.slice(2)) {
|
|
|
242
456
|
process.stderr.write("authz:uat-start requires --campaign <id>\n");
|
|
243
457
|
return 2;
|
|
244
458
|
}
|
|
459
|
+
const blocked = gateConfirm();
|
|
460
|
+
if (blocked !== null)
|
|
461
|
+
return blocked;
|
|
245
462
|
const { lease } = startUatLease({
|
|
246
463
|
projectRoot: args.projectRoot,
|
|
247
464
|
campaignId: args.campaign,
|
|
@@ -254,6 +471,9 @@ export function main(argv = process.argv.slice(2)) {
|
|
|
254
471
|
return 0;
|
|
255
472
|
}
|
|
256
473
|
case "uat-suspend": {
|
|
474
|
+
const blocked = gateConfirm();
|
|
475
|
+
if (blocked !== null)
|
|
476
|
+
return blocked;
|
|
257
477
|
const state = suspendUatLease({
|
|
258
478
|
projectRoot: args.projectRoot,
|
|
259
479
|
actor: args.actor,
|
|
@@ -278,6 +498,9 @@ export function main(argv = process.argv.slice(2)) {
|
|
|
278
498
|
process.stderr.write(`authz:grant --template ${args.template} requires --target <version>\n`);
|
|
279
499
|
return 2;
|
|
280
500
|
}
|
|
501
|
+
const blocked = gateConfirm();
|
|
502
|
+
if (blocked !== null)
|
|
503
|
+
return blocked;
|
|
281
504
|
const grant = mintAfkTemplateGrant({
|
|
282
505
|
projectRoot: args.projectRoot,
|
|
283
506
|
template: args.template,
|
|
@@ -309,6 +532,11 @@ export function main(argv = process.argv.slice(2)) {
|
|
|
309
532
|
process.stderr.write("authz:grant requires --operations <edit,push,...> or --template <finish-loop|release-*> \n");
|
|
310
533
|
return 2;
|
|
311
534
|
}
|
|
535
|
+
{
|
|
536
|
+
const blocked = gateConfirm();
|
|
537
|
+
if (blocked !== null)
|
|
538
|
+
return blocked;
|
|
539
|
+
}
|
|
312
540
|
const grant = mintHumanOriginGrant({
|
|
313
541
|
projectRoot: args.projectRoot,
|
|
314
542
|
actor: args.actor,
|
|
@@ -335,6 +563,11 @@ export function main(argv = process.argv.slice(2)) {
|
|
|
335
563
|
process.stderr.write("authz:revoke requires <grant-id>\n");
|
|
336
564
|
return 2;
|
|
337
565
|
}
|
|
566
|
+
{
|
|
567
|
+
const blocked = gateConfirm();
|
|
568
|
+
if (blocked !== null)
|
|
569
|
+
return blocked;
|
|
570
|
+
}
|
|
338
571
|
const revoked = revokeGrant({
|
|
339
572
|
projectRoot: args.projectRoot,
|
|
340
573
|
grantId: args.grantId,
|
|
@@ -70,6 +70,12 @@ export const SUBCOMMAND_ROUTES = {
|
|
|
70
70
|
"cache:fetch-all": ["cache", "fetch-all"],
|
|
71
71
|
"cache:prune": ["cache", "prune"],
|
|
72
72
|
"cache:clear": ["cache", "clear"],
|
|
73
|
+
"cache:archive-closed": ["cache", "archive-closed"],
|
|
74
|
+
"cache:archive-list": ["cache", "archive-list"],
|
|
75
|
+
"cache:restore-from-archive": ["cache", "restore-from-archive"],
|
|
76
|
+
"triage:cache-archive": ["cache", "archive-closed"],
|
|
77
|
+
"triage:archive-list": ["cache", "archive-list"],
|
|
78
|
+
"triage:restore-from-archive": ["cache", "restore-from-archive"],
|
|
73
79
|
"policy:show": ["policy", "show"],
|
|
74
80
|
"policy:disable-directive": ["policy", "disable-directive"],
|
|
75
81
|
"policy:enable-directive": ["policy", "enable-directive"],
|
package/dist/dispatch.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export interface DispatchIo {
|
|
|
12
12
|
/** CLI modules in packages/cli/src (excluding parity harnesses and bin/index). */
|
|
13
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", "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-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-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-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
|
-
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-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"];
|
|
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-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"];
|
|
16
16
|
/** Colon aliases for triage-actions (mirrors cli-router SUBCOMMAND_ROUTES). */
|
|
17
17
|
export declare const TRIAGE_ACTION_ALIAS_SUBCOMMANDS: Readonly<Record<string, string>>;
|
|
18
18
|
/** Colon aliases for policy subcommands (mirrors cli-router SUBCOMMAND_ROUTES). */
|
|
@@ -25,6 +25,8 @@ export declare const ESCALATION_ACTION_ALIAS_SUBCOMMANDS: Readonly<Record<string
|
|
|
25
25
|
export declare const PLAN_SEQUENCE_ALIAS_SUBCOMMANDS: Readonly<Record<string, string>>;
|
|
26
26
|
/** Colon aliases for product-signal subcommands (#2693). */
|
|
27
27
|
export declare const PRODUCT_SIGNAL_ALIAS_SUBCOMMANDS: Readonly<Record<string, string>>;
|
|
28
|
+
/** Colon aliases for freshness subcommands (#3117). */
|
|
29
|
+
export declare const FRESHNESS_ALIAS_SUBCOMMANDS: Readonly<Record<string, string>>;
|
|
28
30
|
/** Task-style aliases (framework_commands / Taskfile names). */
|
|
29
31
|
export declare const VERB_ALIASES: Readonly<Record<string, string>>;
|
|
30
32
|
/** Pinned ghx version (display only) — keep in lockstep with .github/workflows/ci.yml env.GHX_VERSION. */
|
package/dist/dispatch.js
CHANGED
|
@@ -186,6 +186,7 @@ export const CORE_MODULE_VERBS = [
|
|
|
186
186
|
"feedback-file",
|
|
187
187
|
"value-readback",
|
|
188
188
|
"product-signal",
|
|
189
|
+
"freshness-report",
|
|
189
190
|
];
|
|
190
191
|
/** Colon aliases for triage-actions (mirrors cli-router SUBCOMMAND_ROUTES). */
|
|
191
192
|
export const TRIAGE_ACTION_ALIAS_SUBCOMMANDS = {
|
|
@@ -245,6 +246,14 @@ export const PRODUCT_SIGNAL_ALIAS_SUBCOMMANDS = {
|
|
|
245
246
|
"product-signal:bootstrap-sink": "bootstrap-sink",
|
|
246
247
|
};
|
|
247
248
|
const PRODUCT_SIGNAL_COLON_ALIASES = Object.fromEntries(Object.keys(PRODUCT_SIGNAL_ALIAS_SUBCOMMANDS).map((alias) => [alias, "product-signal"]));
|
|
249
|
+
/** Colon aliases for freshness subcommands (#3117). */
|
|
250
|
+
export const FRESHNESS_ALIAS_SUBCOMMANDS = {
|
|
251
|
+
"freshness:report": "report",
|
|
252
|
+
"freshness:bind": "bind",
|
|
253
|
+
"session:freshness": "report",
|
|
254
|
+
freshness: "report",
|
|
255
|
+
};
|
|
256
|
+
const FRESHNESS_COLON_ALIASES = Object.fromEntries(Object.keys(FRESHNESS_ALIAS_SUBCOMMANDS).map((alias) => [alias, "freshness-report"]));
|
|
248
257
|
/** Task-style aliases (framework_commands / Taskfile names). */
|
|
249
258
|
export const VERB_ALIASES = {
|
|
250
259
|
"hook:dispatch": "hook-dispatch",
|
|
@@ -321,6 +330,7 @@ export const VERB_ALIASES = {
|
|
|
321
330
|
upgrade: "install-upgrade",
|
|
322
331
|
"session:start": "session-start",
|
|
323
332
|
"session:ready": "session-ready",
|
|
333
|
+
...FRESHNESS_COLON_ALIASES,
|
|
324
334
|
"lifecycle:event": "lifecycle-event",
|
|
325
335
|
"lifecycle:stats": "lifecycle-stats",
|
|
326
336
|
"toolchain:check": "toolchain-check",
|
|
@@ -2445,6 +2455,10 @@ async function loadCoreModuleHandler(verb, io) {
|
|
|
2445
2455
|
const { mainEntry } = await import("@deftai/directive-core/dist/product-signal/submit.js");
|
|
2446
2456
|
return mainEntry;
|
|
2447
2457
|
}
|
|
2458
|
+
case "freshness-report": {
|
|
2459
|
+
const { mainEntry } = await import("@deftai/directive-core/dist/freshness/cli.js");
|
|
2460
|
+
return mainEntry;
|
|
2461
|
+
}
|
|
2448
2462
|
default:
|
|
2449
2463
|
throw new Error(`unknown core verb: ${verb}`);
|
|
2450
2464
|
}
|
|
@@ -2547,6 +2561,14 @@ const CURATED_HELP_GROUPS = [
|
|
|
2547
2561
|
name: "session:ready",
|
|
2548
2562
|
summary: "One-shot recovery to gated write-ready (session + ritual + cache)",
|
|
2549
2563
|
},
|
|
2564
|
+
{
|
|
2565
|
+
name: "freshness:report",
|
|
2566
|
+
summary: "Bound vs live deposit generation (current|stale_soft|stale_hard)",
|
|
2567
|
+
},
|
|
2568
|
+
{
|
|
2569
|
+
name: "freshness:bind",
|
|
2570
|
+
summary: "Bind live deposit generation into this session (no host restart)",
|
|
2571
|
+
},
|
|
2550
2572
|
{
|
|
2551
2573
|
name: "scm:status",
|
|
2552
2574
|
summary: "Probe gh/ghx + auth readiness in this execution env (#2275)",
|
|
@@ -2683,6 +2705,7 @@ export async function dispatch(argv, io = defaultIo()) {
|
|
|
2683
2705
|
const escalationSubcommand = verb !== undefined ? ESCALATION_ACTION_ALIAS_SUBCOMMANDS[verb] : undefined;
|
|
2684
2706
|
const planSequenceSubcommand = verb !== undefined ? PLAN_SEQUENCE_ALIAS_SUBCOMMANDS[verb] : undefined;
|
|
2685
2707
|
const productSignalSubcommand = verb !== undefined ? PRODUCT_SIGNAL_ALIAS_SUBCOMMANDS[verb] : undefined;
|
|
2708
|
+
const freshnessSubcommand = verb !== undefined ? FRESHNESS_ALIAS_SUBCOMMANDS[verb] : undefined;
|
|
2686
2709
|
const handlerArgv = canonical === "framework-commands" && verb !== undefined && verb !== canonical
|
|
2687
2710
|
? [verb, ...rest]
|
|
2688
2711
|
: triageSubcommand !== undefined && canonical === "triage-actions"
|
|
@@ -2697,7 +2720,9 @@ export async function dispatch(argv, io = defaultIo()) {
|
|
|
2697
2720
|
? [planSequenceSubcommand, ...rest]
|
|
2698
2721
|
: productSignalSubcommand !== undefined && canonical === "product-signal"
|
|
2699
2722
|
? [productSignalSubcommand, ...rest]
|
|
2700
|
-
:
|
|
2723
|
+
: freshnessSubcommand !== undefined && canonical === "freshness-report"
|
|
2724
|
+
? [freshnessSubcommand, ...rest]
|
|
2725
|
+
: rest;
|
|
2701
2726
|
return await invokeHandler(handler, handlerArgv);
|
|
2702
2727
|
}
|
|
2703
2728
|
catch (err) {
|
package/dist/triage-actions.d.ts
CHANGED
|
@@ -9,6 +9,10 @@ interface ParsedArgs {
|
|
|
9
9
|
comment?: string;
|
|
10
10
|
ofN?: number;
|
|
11
11
|
projectRoot: string;
|
|
12
|
+
/** After accept+ingest, promote to pending (#1136). */
|
|
13
|
+
autoPromote?: boolean;
|
|
14
|
+
/** WIP-cap override for auto-promote leg (#1136). */
|
|
15
|
+
force?: boolean;
|
|
12
16
|
error?: string;
|
|
13
17
|
}
|
|
14
18
|
/** Parse triage-actions CLI argv mirroring ``triage_actions.py`` argparse. */
|
package/dist/triage-actions.js
CHANGED
|
@@ -101,6 +101,12 @@ export function parseArgs(argv) {
|
|
|
101
101
|
else if (arg?.startsWith("--project-root=")) {
|
|
102
102
|
parsed.projectRoot = arg.slice("--project-root=".length);
|
|
103
103
|
}
|
|
104
|
+
else if (arg === "--auto-promote") {
|
|
105
|
+
parsed.autoPromote = true;
|
|
106
|
+
}
|
|
107
|
+
else if (arg === "--force") {
|
|
108
|
+
parsed.force = true;
|
|
109
|
+
}
|
|
104
110
|
else {
|
|
105
111
|
return { ...parsed, error: `unrecognized argument: ${arg}` };
|
|
106
112
|
}
|
|
@@ -144,8 +150,14 @@ export function run(argv) {
|
|
|
144
150
|
const repo = args.repo;
|
|
145
151
|
try {
|
|
146
152
|
if (args.cmd === "accept") {
|
|
147
|
-
const decisionId = accept(n, repo, deps, {
|
|
148
|
-
|
|
153
|
+
const decisionId = accept(n, repo, deps, {
|
|
154
|
+
actor: args.actor,
|
|
155
|
+
projectRoot,
|
|
156
|
+
autoPromote: args.autoPromote === true,
|
|
157
|
+
force: args.force === true,
|
|
158
|
+
});
|
|
159
|
+
const promoteNote = args.autoPromote === true ? " + auto-promote" : "";
|
|
160
|
+
process.stdout.write(`accept #${n} (${repo}) -> ${decisionId}${promoteNote}\n`);
|
|
149
161
|
}
|
|
150
162
|
else if (args.cmd === "reject") {
|
|
151
163
|
const decisionId = reject(n, repo, args.reason ?? "", deps, {
|
|
@@ -1,13 +1,35 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
2
|
+
import { type ResolveAuthenticatedLogin } from "@deftai/directive-core/dist/triage/author-filter.js";
|
|
3
|
+
import type { LabelClient } from "@deftai/directive-core/dist/vbrief-reconcile/types.js";
|
|
4
|
+
export interface ParsedArgs {
|
|
3
5
|
projectRoot: string;
|
|
4
6
|
doList: boolean;
|
|
5
7
|
doValidate: boolean;
|
|
8
|
+
doMirror: boolean;
|
|
9
|
+
apply: boolean;
|
|
10
|
+
json: boolean;
|
|
11
|
+
repo: string | null;
|
|
12
|
+
allowCrossRepo: boolean;
|
|
13
|
+
/** Opt-in: include closed issues (default open-only, #3125). */
|
|
14
|
+
includeClosed: boolean;
|
|
15
|
+
/** Raw --author value (LOGIN, @me, comma allow-list); null = no filter (#3129). */
|
|
16
|
+
author: string | null;
|
|
17
|
+
/** Apply batch size (rate-limit awareness). */
|
|
18
|
+
batchSize: number | null;
|
|
19
|
+
/** Delay ms between apply batches. */
|
|
20
|
+
delayMs: number | null;
|
|
21
|
+
/** Max samples in human digest. */
|
|
22
|
+
sampleLimit: number | null;
|
|
6
23
|
error?: string;
|
|
7
24
|
}
|
|
8
|
-
/** Parse triage-classify CLI args
|
|
25
|
+
/** Parse triage-classify CLI args (#1129 + #1423 Wave 1/2 mirror flags). */
|
|
9
26
|
export declare function parseArgs(argv: string[]): ParsedArgs;
|
|
27
|
+
export interface RunOptions {
|
|
28
|
+
/** Injected LabelClient for tests (apply path). */
|
|
29
|
+
readonly labelClient?: LabelClient;
|
|
30
|
+
/** Override `@me` resolution for hermetic tests (#3129). */
|
|
31
|
+
readonly resolveAuthenticatedLogin?: ResolveAuthenticatedLogin;
|
|
32
|
+
}
|
|
10
33
|
/** Run the CLI and return the process exit code. */
|
|
11
|
-
export declare function run(argv: string[]): number;
|
|
12
|
-
export {};
|
|
34
|
+
export declare function run(argv: string[], options?: RunOptions): number;
|
|
13
35
|
//# sourceMappingURL=triage-classify.d.ts.map
|
package/dist/triage-classify.js
CHANGED
|
@@ -2,13 +2,44 @@
|
|
|
2
2
|
import { statSync } from "node:fs";
|
|
3
3
|
import { resolve } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
-
import {
|
|
6
|
-
|
|
5
|
+
import { resolveAuthorFilter, } from "@deftai/directive-core/dist/triage/author-filter.js";
|
|
6
|
+
import { labelMirrorOutcomeToJson, listProject, mirrorLabels, renderLabelMirrorReport, validateProject, } from "@deftai/directive-core/dist/triage/classify/index.js";
|
|
7
|
+
function parseNonNegInt(raw, flag) {
|
|
8
|
+
const n = Number.parseInt(raw, 10);
|
|
9
|
+
if (!Number.isFinite(n) || String(n) !== raw.trim() || n < 0) {
|
|
10
|
+
return { error: `argument ${flag}: expected a non-negative integer` };
|
|
11
|
+
}
|
|
12
|
+
return { value: n };
|
|
13
|
+
}
|
|
14
|
+
/** Batch size must be >= 1 (0 would silently fall back in core). */
|
|
15
|
+
function parseBatchSize(raw) {
|
|
16
|
+
const parsed = parseNonNegInt(raw, "--batch-size");
|
|
17
|
+
if (parsed.error !== undefined) {
|
|
18
|
+
return parsed;
|
|
19
|
+
}
|
|
20
|
+
if ((parsed.value ?? 0) < 1) {
|
|
21
|
+
return {
|
|
22
|
+
error: "argument --batch-size: expected an integer >= 1 (omit flag for default 10)",
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
return parsed;
|
|
26
|
+
}
|
|
27
|
+
/** Parse triage-classify CLI args (#1129 + #1423 Wave 1/2 mirror flags). */
|
|
7
28
|
export function parseArgs(argv) {
|
|
8
29
|
const parsed = {
|
|
9
30
|
projectRoot: ".",
|
|
10
31
|
doList: false,
|
|
11
32
|
doValidate: false,
|
|
33
|
+
doMirror: false,
|
|
34
|
+
apply: false,
|
|
35
|
+
json: false,
|
|
36
|
+
repo: null,
|
|
37
|
+
allowCrossRepo: false,
|
|
38
|
+
includeClosed: false,
|
|
39
|
+
author: null,
|
|
40
|
+
batchSize: null,
|
|
41
|
+
delayMs: null,
|
|
42
|
+
sampleLimit: null,
|
|
12
43
|
};
|
|
13
44
|
for (let i = 0; i < argv.length; i += 1) {
|
|
14
45
|
const arg = argv[i];
|
|
@@ -18,6 +49,118 @@ export function parseArgs(argv) {
|
|
|
18
49
|
else if (arg === "--validate") {
|
|
19
50
|
parsed.doValidate = true;
|
|
20
51
|
}
|
|
52
|
+
else if (arg === "--mirror") {
|
|
53
|
+
parsed.doMirror = true;
|
|
54
|
+
}
|
|
55
|
+
else if (arg === "--apply") {
|
|
56
|
+
parsed.apply = true;
|
|
57
|
+
}
|
|
58
|
+
else if (arg === "--json") {
|
|
59
|
+
parsed.json = true;
|
|
60
|
+
}
|
|
61
|
+
else if (arg === "--allow-cross-repo") {
|
|
62
|
+
parsed.allowCrossRepo = true;
|
|
63
|
+
}
|
|
64
|
+
else if (arg === "--include-closed") {
|
|
65
|
+
parsed.includeClosed = true;
|
|
66
|
+
}
|
|
67
|
+
else if (arg === "--author-mine") {
|
|
68
|
+
parsed.author = "@me";
|
|
69
|
+
}
|
|
70
|
+
else if (arg === "--author") {
|
|
71
|
+
const value = argv[i + 1];
|
|
72
|
+
if (value === undefined) {
|
|
73
|
+
return { ...parsed, error: "argument --author: expected one argument" };
|
|
74
|
+
}
|
|
75
|
+
// Reject adjacent flags (e.g. `--author --apply`) so they are not
|
|
76
|
+
// swallowed as logins (#3129 Greptile P1 adjacent-option).
|
|
77
|
+
if (value.startsWith("-")) {
|
|
78
|
+
return {
|
|
79
|
+
...parsed,
|
|
80
|
+
error: `argument --author: expected a login (or @me), got flag token '${value}'`,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
parsed.author = value;
|
|
84
|
+
i += 1;
|
|
85
|
+
}
|
|
86
|
+
else if (arg?.startsWith("--author=")) {
|
|
87
|
+
const value = arg.slice("--author=".length);
|
|
88
|
+
if (value.startsWith("-") && value.length > 1) {
|
|
89
|
+
return {
|
|
90
|
+
...parsed,
|
|
91
|
+
error: `argument --author: expected a login (or @me), got flag token '${value}'`,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
parsed.author = value;
|
|
95
|
+
}
|
|
96
|
+
else if (arg === "--repo") {
|
|
97
|
+
const value = argv[i + 1];
|
|
98
|
+
if (value === undefined) {
|
|
99
|
+
return { ...parsed, error: "argument --repo: expected one argument" };
|
|
100
|
+
}
|
|
101
|
+
parsed.repo = value;
|
|
102
|
+
i += 1;
|
|
103
|
+
}
|
|
104
|
+
else if (arg?.startsWith("--repo=")) {
|
|
105
|
+
parsed.repo = arg.slice("--repo=".length);
|
|
106
|
+
}
|
|
107
|
+
else if (arg === "--batch-size") {
|
|
108
|
+
const value = argv[i + 1];
|
|
109
|
+
if (value === undefined) {
|
|
110
|
+
return { ...parsed, error: "argument --batch-size: expected one argument" };
|
|
111
|
+
}
|
|
112
|
+
const parsedInt = parseBatchSize(value);
|
|
113
|
+
if (parsedInt.error !== undefined) {
|
|
114
|
+
return { ...parsed, error: parsedInt.error };
|
|
115
|
+
}
|
|
116
|
+
parsed.batchSize = parsedInt.value ?? null;
|
|
117
|
+
i += 1;
|
|
118
|
+
}
|
|
119
|
+
else if (arg?.startsWith("--batch-size=")) {
|
|
120
|
+
const parsedInt = parseBatchSize(arg.slice("--batch-size=".length));
|
|
121
|
+
if (parsedInt.error !== undefined) {
|
|
122
|
+
return { ...parsed, error: parsedInt.error };
|
|
123
|
+
}
|
|
124
|
+
parsed.batchSize = parsedInt.value ?? null;
|
|
125
|
+
}
|
|
126
|
+
else if (arg === "--delay-ms") {
|
|
127
|
+
const value = argv[i + 1];
|
|
128
|
+
if (value === undefined) {
|
|
129
|
+
return { ...parsed, error: "argument --delay-ms: expected one argument" };
|
|
130
|
+
}
|
|
131
|
+
const parsedInt = parseNonNegInt(value, "--delay-ms");
|
|
132
|
+
if (parsedInt.error !== undefined) {
|
|
133
|
+
return { ...parsed, error: parsedInt.error };
|
|
134
|
+
}
|
|
135
|
+
parsed.delayMs = parsedInt.value ?? null;
|
|
136
|
+
i += 1;
|
|
137
|
+
}
|
|
138
|
+
else if (arg?.startsWith("--delay-ms=")) {
|
|
139
|
+
const parsedInt = parseNonNegInt(arg.slice("--delay-ms=".length), "--delay-ms");
|
|
140
|
+
if (parsedInt.error !== undefined) {
|
|
141
|
+
return { ...parsed, error: parsedInt.error };
|
|
142
|
+
}
|
|
143
|
+
parsed.delayMs = parsedInt.value ?? null;
|
|
144
|
+
}
|
|
145
|
+
else if (arg === "--sample-limit") {
|
|
146
|
+
const value = argv[i + 1];
|
|
147
|
+
if (value === undefined) {
|
|
148
|
+
return { ...parsed, error: "argument --sample-limit: expected one argument" };
|
|
149
|
+
}
|
|
150
|
+
const parsedInt = parseNonNegInt(value, "--sample-limit");
|
|
151
|
+
if (parsedInt.error !== undefined) {
|
|
152
|
+
return { ...parsed, error: parsedInt.error };
|
|
153
|
+
}
|
|
154
|
+
parsed.sampleLimit = parsedInt.value ?? null;
|
|
155
|
+
i += 1;
|
|
156
|
+
}
|
|
157
|
+
else if (arg?.startsWith("--sample-limit=")) {
|
|
158
|
+
const parsedInt = parseNonNegInt(arg.slice("--sample-limit=".length), "--sample-limit");
|
|
159
|
+
if (parsedInt.error !== undefined) {
|
|
160
|
+
return { ...parsed, error: parsedInt.error };
|
|
161
|
+
}
|
|
162
|
+
parsed.sampleLimit = parsedInt.value ?? null;
|
|
163
|
+
}
|
|
21
164
|
else if (arg === "--project-root") {
|
|
22
165
|
const value = argv[i + 1];
|
|
23
166
|
if (value === undefined) {
|
|
@@ -36,10 +179,27 @@ export function parseArgs(argv) {
|
|
|
36
179
|
return { ...parsed, error: `unrecognized arguments: ${arg}` };
|
|
37
180
|
}
|
|
38
181
|
}
|
|
182
|
+
if (parsed.apply && !parsed.doMirror) {
|
|
183
|
+
return {
|
|
184
|
+
...parsed,
|
|
185
|
+
error: "--apply requires --mirror (Tier-1 label mirror / bootstrap mass-triage, #1423)",
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
if ((parsed.includeClosed ||
|
|
189
|
+
parsed.author !== null ||
|
|
190
|
+
parsed.batchSize !== null ||
|
|
191
|
+
parsed.delayMs !== null ||
|
|
192
|
+
parsed.sampleLimit !== null) &&
|
|
193
|
+
!parsed.doMirror) {
|
|
194
|
+
return {
|
|
195
|
+
...parsed,
|
|
196
|
+
error: "--include-closed / --author / --batch-size / --delay-ms / --sample-limit require --mirror (#3125 / #3129)",
|
|
197
|
+
};
|
|
198
|
+
}
|
|
39
199
|
return parsed;
|
|
40
200
|
}
|
|
41
201
|
/** Run the CLI and return the process exit code. */
|
|
42
|
-
export function run(argv) {
|
|
202
|
+
export function run(argv, options = {}) {
|
|
43
203
|
const args = parseArgs(argv);
|
|
44
204
|
if (args.error !== undefined) {
|
|
45
205
|
process.stderr.write(`ERR: ${args.error}\n`);
|
|
@@ -67,6 +227,39 @@ export function run(argv) {
|
|
|
67
227
|
}
|
|
68
228
|
return result.code;
|
|
69
229
|
}
|
|
230
|
+
if (args.doMirror) {
|
|
231
|
+
let authorFilter = null;
|
|
232
|
+
// Flag present (including empty `--author=`) must resolve or fail closed —
|
|
233
|
+
// never silent no-op that would plan/apply the full open cache (#3129 Greptile P1).
|
|
234
|
+
if (args.author !== null) {
|
|
235
|
+
const resolved = resolveAuthorFilter(args.author, options.resolveAuthenticatedLogin);
|
|
236
|
+
if (resolved.error !== undefined || resolved.filter === undefined) {
|
|
237
|
+
process.stderr.write(`ERR: ${resolved.error ?? "argument --author: expected a non-empty login (or @me)"}\n`);
|
|
238
|
+
return 2;
|
|
239
|
+
}
|
|
240
|
+
authorFilter = resolved.filter;
|
|
241
|
+
}
|
|
242
|
+
const mirrorOpts = {
|
|
243
|
+
dryRun: !args.apply,
|
|
244
|
+
repo: args.repo,
|
|
245
|
+
allowCrossRepo: args.allowCrossRepo,
|
|
246
|
+
includeClosed: args.includeClosed,
|
|
247
|
+
...(authorFilter !== null && authorFilter !== undefined ? { authorFilter } : {}),
|
|
248
|
+
...(args.batchSize !== null ? { batchSize: args.batchSize } : {}),
|
|
249
|
+
...(args.delayMs !== null ? { delayMs: args.delayMs } : {}),
|
|
250
|
+
...(args.sampleLimit !== null ? { sampleLimit: args.sampleLimit } : {}),
|
|
251
|
+
...(options.labelClient !== undefined ? { client: options.labelClient } : {}),
|
|
252
|
+
};
|
|
253
|
+
const [code, outcome] = mirrorLabels(projectRoot, mirrorOpts);
|
|
254
|
+
if (args.json) {
|
|
255
|
+
process.stdout.write(`${JSON.stringify(labelMirrorOutcomeToJson(outcome), null, 2)}\n`);
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
process.stdout.write(renderLabelMirrorReport(outcome));
|
|
259
|
+
}
|
|
260
|
+
return code;
|
|
261
|
+
}
|
|
262
|
+
// Default / --list: print effective rules
|
|
70
263
|
process.stdout.write(listProject(projectRoot));
|
|
71
264
|
return 0;
|
|
72
265
|
}
|
|
@@ -10,6 +10,8 @@ export interface QueueFixtureIssue {
|
|
|
10
10
|
readonly labels?: readonly string[];
|
|
11
11
|
readonly updatedAt?: string;
|
|
12
12
|
readonly createdAt?: string;
|
|
13
|
+
/** Cache author.login for --author filter tests (#3129). */
|
|
14
|
+
readonly author?: string;
|
|
13
15
|
}
|
|
14
16
|
export interface QueueAuditEntry {
|
|
15
17
|
readonly issueNumber: number;
|
|
@@ -25,6 +25,7 @@ function writeCachedIssue(root, repo, issue) {
|
|
|
25
25
|
labels: (issue.labels ?? []).map((label) => ({ name: label })),
|
|
26
26
|
updated_at: issue.updatedAt ?? "2026-05-17T20:00:00Z",
|
|
27
27
|
...(issue.createdAt !== undefined ? { created_at: issue.createdAt } : {}),
|
|
28
|
+
...(issue.author !== undefined ? { author: { login: issue.author } } : {}),
|
|
28
29
|
};
|
|
29
30
|
writeFileSync(join(dir, "raw.json"), `${JSON.stringify(raw)}\n`, { encoding: "utf8" });
|
|
30
31
|
}
|
package/dist/triage-queue.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { type LiveOpenIssuesReader } from "@deftai/directive-core/dist/triage/queue/index.js";
|
|
2
|
+
import { type LiveOpenIssuesReader, type ResolveAuthenticatedLogin } from "@deftai/directive-core/dist/triage/queue/index.js";
|
|
3
3
|
export type ShowFormat = "default" | "operator";
|
|
4
4
|
interface CommonArgs {
|
|
5
5
|
projectRoot: string;
|
|
@@ -14,6 +14,8 @@ interface QueueArgs extends CommonArgs {
|
|
|
14
14
|
includeBlocked: boolean;
|
|
15
15
|
reconcile: boolean;
|
|
16
16
|
slicesLog: string | null;
|
|
17
|
+
/** Raw --author value (LOGIN, @me, or comma allow-list); null = no filter (#3129). */
|
|
18
|
+
author: string | null;
|
|
17
19
|
}
|
|
18
20
|
interface ShowArgs extends CommonArgs {
|
|
19
21
|
cmd: "show";
|
|
@@ -24,9 +26,11 @@ interface ShowArgs extends CommonArgs {
|
|
|
24
26
|
export declare function parseArgs(argv: string[]): QueueArgs;
|
|
25
27
|
/** Parse show subcommand args. */
|
|
26
28
|
export declare function parseShowArgs(argv: string[]): ShowArgs;
|
|
27
|
-
/** Optional injection
|
|
29
|
+
/** Optional injection seams for `run` (tests supply stubs). */
|
|
28
30
|
export interface RunOptions {
|
|
29
31
|
readonly liveOpenReader?: LiveOpenIssuesReader;
|
|
32
|
+
/** Override `@me` resolution for hermetic tests (#3129). */
|
|
33
|
+
readonly resolveAuthenticatedLogin?: ResolveAuthenticatedLogin;
|
|
30
34
|
}
|
|
31
35
|
/** Run triage:queue or triage:show and return process exit code. */
|
|
32
36
|
export declare function run(argv: string[], options?: RunOptions): number;
|
package/dist/triage-queue.js
CHANGED
|
@@ -13,7 +13,7 @@ import { fileURLToPath } from "node:url";
|
|
|
13
13
|
import { ensureTriageCacheHydrated } from "@deftai/directive-core/dist/cache/empty-populate.js";
|
|
14
14
|
import { findByIssue } from "@deftai/directive-core/dist/triage/actions/candidates-log.js";
|
|
15
15
|
import { resolveTriageCachePath } from "@deftai/directive-core/dist/triage/cache-path.js";
|
|
16
|
-
import { activeReferencedIssueNumbers, buildQueue, collectOrphanIssueNumbers, DEFAULT_QUEUE_LIMIT, loadCachedIssueDetail, loadCachedIssues, loadSliceRecords, readAuditEntries, reconcileLiveOpenState, renderOperatorBrief, renderQueue, renderShow, resolveRankingLabels, resolveRepo, } from "@deftai/directive-core/dist/triage/queue/index.js";
|
|
16
|
+
import { activeReferencedIssueNumbers, buildQueue, collectOrphanIssueNumbers, DEFAULT_QUEUE_LIMIT, formatAuthorFilterLine, loadCachedIssueDetail, loadCachedIssues, loadSliceRecords, partitionByAuthorFilter, readAuditEntries, reconcileLiveOpenState, renderOperatorBrief, renderQueue, renderShow, resolveAuthorFilter, resolveRankingLabels, resolveRepo, } from "@deftai/directive-core/dist/triage/queue/index.js";
|
|
17
17
|
import { resolveScopeIgnores } from "@deftai/directive-core/dist/triage/scope-drift/index.js";
|
|
18
18
|
function baseArgs() {
|
|
19
19
|
return {
|
|
@@ -84,6 +84,7 @@ export function parseArgs(argv) {
|
|
|
84
84
|
includeBlocked: false,
|
|
85
85
|
reconcile: true,
|
|
86
86
|
slicesLog: null,
|
|
87
|
+
author: null,
|
|
87
88
|
};
|
|
88
89
|
for (let i = 0; i < argv.length; i += 1) {
|
|
89
90
|
const arg = argv[i];
|
|
@@ -98,6 +99,39 @@ export function parseArgs(argv) {
|
|
|
98
99
|
parsed.reconcile = false;
|
|
99
100
|
continue;
|
|
100
101
|
}
|
|
102
|
+
if (arg === "--author-mine") {
|
|
103
|
+
// #1318 Layer 1 optional alias for --author @me
|
|
104
|
+
parsed.author = "@me";
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
if (arg === "--author") {
|
|
108
|
+
const value = argv[i + 1];
|
|
109
|
+
if (value === undefined) {
|
|
110
|
+
return { ...parsed, error: "argument --author: expected one argument" };
|
|
111
|
+
}
|
|
112
|
+
// Reject adjacent flags (e.g. `--author --limit 10`) so they are not
|
|
113
|
+
// swallowed as logins (#3129 Greptile P1 adjacent-option).
|
|
114
|
+
if (value.startsWith("-")) {
|
|
115
|
+
return {
|
|
116
|
+
...parsed,
|
|
117
|
+
error: `argument --author: expected a login (or @me), got flag token '${value}'`,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
parsed.author = value;
|
|
121
|
+
i += 1;
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
if (arg?.startsWith("--author=")) {
|
|
125
|
+
const value = arg.slice("--author=".length);
|
|
126
|
+
if (value.startsWith("-") && value.length > 1) {
|
|
127
|
+
return {
|
|
128
|
+
...parsed,
|
|
129
|
+
error: `argument --author: expected a login (or @me), got flag token '${value}'`,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
parsed.author = value;
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
101
135
|
const commonHit = parseCommonFlag(arg, argv, i, parsed);
|
|
102
136
|
if (commonHit !== null) {
|
|
103
137
|
if (commonHit.error !== undefined) {
|
|
@@ -222,11 +256,30 @@ function runQueue(args, options = {}) {
|
|
|
222
256
|
process.stderr.write("triage:queue: --repo OWNER/NAME (or $DEFT_TRIAGE_REPO) is required.\n");
|
|
223
257
|
return 2;
|
|
224
258
|
}
|
|
259
|
+
let authorFilterLine = null;
|
|
260
|
+
let authorAllow;
|
|
261
|
+
// Flag present (including empty `--author=`) must resolve or fail closed — never
|
|
262
|
+
// silent no-op that would show the full queue (#3129 Greptile P1).
|
|
263
|
+
if (args.author !== null) {
|
|
264
|
+
const resolved = resolveAuthorFilter(args.author, options.resolveAuthenticatedLogin ?? undefined);
|
|
265
|
+
if (resolved.error !== undefined || resolved.filter === undefined) {
|
|
266
|
+
process.stderr.write(`triage:queue: ${resolved.error ?? "argument --author: expected a non-empty login (or @me)"}\n`);
|
|
267
|
+
return 2;
|
|
268
|
+
}
|
|
269
|
+
authorAllow = resolved.filter;
|
|
270
|
+
}
|
|
225
271
|
ensureTriageCacheHydrated(projectRoot, { repo });
|
|
226
272
|
const cachedForQueue = loadCachedIssues(repo, { projectRoot });
|
|
227
|
-
|
|
273
|
+
let issuesForQueue = args.reconcile
|
|
228
274
|
? reconcileLiveOpenState(cachedForQueue, repo, options.liveOpenReader)
|
|
229
275
|
: cachedForQueue;
|
|
276
|
+
if (authorAllow !== undefined) {
|
|
277
|
+
const partition = partitionByAuthorFilter(issuesForQueue, (row) => row.author ?? null, authorAllow);
|
|
278
|
+
issuesForQueue = [...partition.matched];
|
|
279
|
+
authorFilterLine = formatAuthorFilterLine(authorAllow, {
|
|
280
|
+
unknownCount: partition.unknownCount,
|
|
281
|
+
});
|
|
282
|
+
}
|
|
230
283
|
const issuesWithClosed = loadCachedIssues(repo, { projectRoot, includeClosed: true });
|
|
231
284
|
const issuesByNumber = new Map(issuesWithClosed.map((row) => [row.number, row]));
|
|
232
285
|
const auditEntries = readAuditEntries(repo, {
|
|
@@ -256,6 +309,7 @@ function runQueue(args, options = {}) {
|
|
|
256
309
|
repo,
|
|
257
310
|
limit,
|
|
258
311
|
rankingLabels,
|
|
312
|
+
authorFilterLine,
|
|
259
313
|
})}\n`);
|
|
260
314
|
return 0;
|
|
261
315
|
}
|
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { type AgentHookHealthResult, type AgentHookReadinessResult, type EvaluateResult } from "@deftai/directive-core/verify-env";
|
|
2
3
|
type HookScope = "git" | "agent" | "all";
|
|
3
4
|
interface ParsedArgs {
|
|
4
5
|
projectRoot: string;
|
|
5
6
|
quiet: boolean;
|
|
6
7
|
scope: HookScope;
|
|
8
|
+
live: boolean;
|
|
7
9
|
error?: string;
|
|
8
10
|
}
|
|
11
|
+
export interface VerifyHooksInstalledCliSeams {
|
|
12
|
+
readonly evaluateGit?: (projectRoot: string) => EvaluateResult;
|
|
13
|
+
readonly evaluateAgent?: (projectRoot: string) => AgentHookHealthResult;
|
|
14
|
+
readonly evaluateReadiness?: (projectRoot: string) => AgentHookReadinessResult;
|
|
15
|
+
readonly writeOut?: (text: string) => void;
|
|
16
|
+
readonly writeErr?: (text: string) => void;
|
|
17
|
+
}
|
|
9
18
|
export declare function parseArgs(argv: string[]): ParsedArgs;
|
|
10
|
-
|
|
19
|
+
/** Verify git hooks, structural agent hooks, or functional agent-hook readiness. */
|
|
20
|
+
export declare function run(argv: string[], seams?: VerifyHooksInstalledCliSeams): number;
|
|
11
21
|
export {};
|
|
12
22
|
//# sourceMappingURL=verify-hooks-installed.d.ts.map
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
|
-
import { evaluate, evaluateAgentHooks } from "@deftai/directive-core/verify-env";
|
|
4
|
+
import { evaluate, evaluateAgentHookReadinessSafely, evaluateAgentHooks, } from "@deftai/directive-core/verify-env";
|
|
5
5
|
export function parseArgs(argv) {
|
|
6
|
-
const parsed = { projectRoot: ".", quiet: false, scope: "git" };
|
|
6
|
+
const parsed = { projectRoot: ".", quiet: false, scope: "git", live: false };
|
|
7
7
|
for (let i = 0; i < argv.length; i += 1) {
|
|
8
8
|
const arg = argv[i];
|
|
9
9
|
if (arg === "--quiet")
|
|
10
10
|
parsed.quiet = true;
|
|
11
|
+
else if (arg === "--live")
|
|
12
|
+
parsed.live = true;
|
|
11
13
|
else if (arg === "--project-root") {
|
|
12
14
|
const value = argv[i + 1];
|
|
13
15
|
if (value === undefined) {
|
|
@@ -41,26 +43,39 @@ export function parseArgs(argv) {
|
|
|
41
43
|
return { ...parsed, error: `unrecognized arguments: ${arg}` };
|
|
42
44
|
}
|
|
43
45
|
}
|
|
46
|
+
if (parsed.live && parsed.scope === "git") {
|
|
47
|
+
return { ...parsed, error: "argument --live requires --scope=agent or --scope=all" };
|
|
48
|
+
}
|
|
44
49
|
return parsed;
|
|
45
50
|
}
|
|
46
|
-
|
|
51
|
+
/** Verify git hooks, structural agent hooks, or functional agent-hook readiness. */
|
|
52
|
+
export function run(argv, seams = {}) {
|
|
47
53
|
const args = parseArgs(argv);
|
|
54
|
+
const writeOut = seams.writeOut ?? ((text) => process.stdout.write(text));
|
|
55
|
+
const writeErr = seams.writeErr ?? ((text) => process.stderr.write(text));
|
|
48
56
|
if (args.error !== undefined) {
|
|
49
|
-
|
|
57
|
+
writeErr(`${args.error}\n`);
|
|
50
58
|
return 2;
|
|
51
59
|
}
|
|
52
60
|
const projectRoot = resolve(args.projectRoot);
|
|
61
|
+
const evaluateGit = seams.evaluateGit ?? evaluate;
|
|
62
|
+
const evaluateAgent = seams.evaluateAgent ?? evaluateAgentHooks;
|
|
63
|
+
const evaluateReadiness = seams.evaluateReadiness
|
|
64
|
+
? (root) => evaluateAgentHookReadinessSafely(root, seams.evaluateReadiness)
|
|
65
|
+
: evaluateAgentHookReadinessSafely;
|
|
53
66
|
const results = [
|
|
54
|
-
...(args.scope === "git" || args.scope === "all" ? [
|
|
55
|
-
...(args.scope === "agent" || args.scope === "all"
|
|
67
|
+
...(args.scope === "git" || args.scope === "all" ? [evaluateGit(projectRoot)] : []),
|
|
68
|
+
...(args.scope === "agent" || args.scope === "all"
|
|
69
|
+
? [args.live ? evaluateReadiness(projectRoot) : evaluateAgent(projectRoot)]
|
|
70
|
+
: []),
|
|
56
71
|
];
|
|
57
72
|
if (!args.quiet) {
|
|
58
73
|
for (const result of results) {
|
|
59
74
|
if (result.stream === "stdout") {
|
|
60
|
-
|
|
75
|
+
writeOut(`${result.message}\n`);
|
|
61
76
|
}
|
|
62
77
|
else if (result.stream === "stderr") {
|
|
63
|
-
|
|
78
|
+
writeErr(`${result.message}\n`);
|
|
64
79
|
}
|
|
65
80
|
}
|
|
66
81
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deftai/directive",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.96.0",
|
|
4
4
|
"description": "Directive CLI — npm install path for the Deft Directive framework.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://deftai.github.io/directive/",
|
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
"provenance": true
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@deftai/directive-core": "^0.
|
|
38
|
-
"@deftai/directive-content": "^0.
|
|
37
|
+
"@deftai/directive-core": "^0.96.0",
|
|
38
|
+
"@deftai/directive-content": "^0.96.0"
|
|
39
39
|
},
|
|
40
40
|
"scripts": {
|
|
41
41
|
"build": "tsc -b"
|