@deftai/directive 0.88.0 → 0.89.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 -0
- package/dist/authz.js +363 -0
- package/dist/cli-router/route-argv.js +13 -0
- package/dist/directive-finish-loop.d.ts +3 -0
- package/dist/directive-finish-loop.js +10 -0
- package/dist/dispatch.d.ts +5 -1
- package/dist/dispatch.js +40 -5
- package/dist/escalation-cli.d.ts +4 -0
- package/dist/escalation-cli.js +301 -0
- package/dist/hook-dispatch.d.ts +6 -0
- package/dist/hook-dispatch.js +7 -53
- package/dist/policy.d.ts +1 -1
- package/dist/policy.js +57 -9
- package/dist/pr-finish-loop.d.ts +3 -0
- package/dist/pr-finish-loop.js +10 -0
- package/dist/verify-source-cli/verify-contained-writes.d.ts +13 -0
- package/dist/verify-source-cli/verify-contained-writes.js +67 -0
- package/package.json +3 -3
package/dist/authz.d.ts
ADDED
package/dist/authz.js
ADDED
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Authz CLI (#2944 Wave 1 + #1095 Wave 4): human-origin grants + UAT lease +
|
|
4
|
+
* AFK closed-verb templates (mint via mintHumanOriginGrant only).
|
|
5
|
+
*
|
|
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>
|
|
13
|
+
*/
|
|
14
|
+
import { AFK_TEMPLATE_NAMES, AUTHZ_OPERATIONS, CLOSED_VERB_TEMPLATE_NAMES, FINISH_LOOP_TEMPLATE_NAME, isAfkTemplateName, isClosedVerbTemplateName, isFinishLoopTemplateName, mintAfkTemplateGrant, mintHumanOriginGrant, revokeGrant, showAuthzSnapshot, startUatLease, suspendUatLease, } from "@deftai/directive-core/authz";
|
|
15
|
+
function parseOps(raw) {
|
|
16
|
+
const allowed = new Set(AUTHZ_OPERATIONS);
|
|
17
|
+
const out = [];
|
|
18
|
+
for (const part of raw.split(/[,\s]+/)) {
|
|
19
|
+
const op = part.trim().toLowerCase();
|
|
20
|
+
if (op.length === 0)
|
|
21
|
+
continue;
|
|
22
|
+
if (!allowed.has(op)) {
|
|
23
|
+
throw new Error(`unknown operation '${op}'; expected one of ${AUTHZ_OPERATIONS.join(", ")}`);
|
|
24
|
+
}
|
|
25
|
+
out.push(op);
|
|
26
|
+
}
|
|
27
|
+
return out;
|
|
28
|
+
}
|
|
29
|
+
function parseArgv(argv) {
|
|
30
|
+
const base = {
|
|
31
|
+
cmd: "show",
|
|
32
|
+
projectRoot: process.cwd(),
|
|
33
|
+
campaign: null,
|
|
34
|
+
actor: "operator",
|
|
35
|
+
note: null,
|
|
36
|
+
operations: [],
|
|
37
|
+
surfaces: [],
|
|
38
|
+
cohort: null,
|
|
39
|
+
planRef: null,
|
|
40
|
+
repo: null,
|
|
41
|
+
branch: null,
|
|
42
|
+
storyIds: [],
|
|
43
|
+
issueIds: [],
|
|
44
|
+
expiresAt: null,
|
|
45
|
+
singleUse: false,
|
|
46
|
+
grantId: null,
|
|
47
|
+
template: null,
|
|
48
|
+
target: null,
|
|
49
|
+
format: "text",
|
|
50
|
+
};
|
|
51
|
+
const args = [...argv];
|
|
52
|
+
// Drop leading `--` separators from task-style invocation.
|
|
53
|
+
while (args[0] === "--")
|
|
54
|
+
args.shift();
|
|
55
|
+
if (args.length > 0 && !args[0]?.startsWith("-")) {
|
|
56
|
+
const cmd = args.shift();
|
|
57
|
+
if (cmd === "show" ||
|
|
58
|
+
cmd === "uat-start" ||
|
|
59
|
+
cmd === "uat-suspend" ||
|
|
60
|
+
cmd === "grant" ||
|
|
61
|
+
cmd === "revoke") {
|
|
62
|
+
base.cmd = cmd;
|
|
63
|
+
}
|
|
64
|
+
else if (cmd.startsWith("grant-")) {
|
|
65
|
+
base.cmd = "revoke";
|
|
66
|
+
base.grantId = cmd;
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
return { ...base, error: `unknown authz subcommand: ${cmd}` };
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
while (args[0] === "--")
|
|
73
|
+
args.shift();
|
|
74
|
+
for (let i = 0; i < args.length; i++) {
|
|
75
|
+
const a = args[i];
|
|
76
|
+
if (a === undefined)
|
|
77
|
+
break;
|
|
78
|
+
if (a === "--project-root" || a === "--projectRoot") {
|
|
79
|
+
base.projectRoot = args[++i] ?? base.projectRoot;
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
if (a === "--campaign") {
|
|
83
|
+
base.campaign = args[++i] ?? null;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (a === "--actor") {
|
|
87
|
+
base.actor = args[++i] ?? base.actor;
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
if (a === "--note") {
|
|
91
|
+
base.note = args[++i] ?? null;
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
if (a === "--operations" || a === "--ops") {
|
|
95
|
+
try {
|
|
96
|
+
base.operations = parseOps(args[++i] ?? "");
|
|
97
|
+
}
|
|
98
|
+
catch (err) {
|
|
99
|
+
return { ...base, error: String(err) };
|
|
100
|
+
}
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
if (a === "--surfaces") {
|
|
104
|
+
const raw = args[++i] ?? "";
|
|
105
|
+
base.surfaces = raw
|
|
106
|
+
.split(/[,\s]+/)
|
|
107
|
+
.map((s) => s.trim())
|
|
108
|
+
.filter((s) => s.length > 0);
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
if (a === "--cohort") {
|
|
112
|
+
base.cohort = args[++i] ?? null;
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
if (a === "--plan-ref" || a === "--planRef") {
|
|
116
|
+
base.planRef = args[++i] ?? null;
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
if (a === "--repo") {
|
|
120
|
+
base.repo = args[++i] ?? null;
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
if (a === "--branch") {
|
|
124
|
+
base.branch = args[++i] ?? null;
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
if (a === "--stories" || a === "--story-ids") {
|
|
128
|
+
const raw = args[++i] ?? "";
|
|
129
|
+
base.storyIds = raw
|
|
130
|
+
.split(/[,\s]+/)
|
|
131
|
+
.map((s) => s.trim())
|
|
132
|
+
.filter((s) => s.length > 0);
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
if (a === "--issues" || a === "--issue-ids") {
|
|
136
|
+
const raw = args[++i] ?? "";
|
|
137
|
+
base.issueIds = raw
|
|
138
|
+
.split(/[,\s]+/)
|
|
139
|
+
.map((s) => Number(s.trim()))
|
|
140
|
+
.filter((n) => Number.isFinite(n));
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
if (a === "--expires" || a === "--expires-at") {
|
|
144
|
+
base.expiresAt = args[++i] ?? null;
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
if (a === "--single-use") {
|
|
148
|
+
base.singleUse = true;
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
if (a === "--format") {
|
|
152
|
+
const fmt = (args[++i] ?? "text").toLowerCase();
|
|
153
|
+
base.format = fmt === "json" ? "json" : "text";
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
if (a === "--grant-id") {
|
|
157
|
+
base.grantId = args[++i] ?? null;
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
if (a === "--template") {
|
|
161
|
+
base.template = args[++i] ?? null;
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
if (a === "--target") {
|
|
165
|
+
base.target = args[++i] ?? null;
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
if (!a.startsWith("-") && base.cmd === "revoke" && base.grantId === null) {
|
|
169
|
+
base.grantId = a;
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
if (a === "--help" || a === "-h") {
|
|
173
|
+
return { ...base, error: "help" };
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return base;
|
|
177
|
+
}
|
|
178
|
+
function helpText() {
|
|
179
|
+
return [
|
|
180
|
+
"Usage:",
|
|
181
|
+
" deft authz:show [--format json]",
|
|
182
|
+
" deft authz:uat-start -- --campaign <id> [--actor <name>] [--note <text>]",
|
|
183
|
+
" deft authz:uat-suspend",
|
|
184
|
+
" 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>",
|
|
189
|
+
"",
|
|
190
|
+
"Human-origin grants are minted only via this CLI (origin.kind=operator-cli).",
|
|
191
|
+
"Self-authored xBRIEF/lifecycle/dispatch tokens never satisfy implement gates (#2944).",
|
|
192
|
+
"",
|
|
193
|
+
`AFK templates (#1095 / #871): ${AFK_TEMPLATE_NAMES.join(", ")}`,
|
|
194
|
+
` Closed-verb (#1095): ${CLOSED_VERB_TEMPLATE_NAMES.join(", ")} — require --target`,
|
|
195
|
+
` Finish-loop (#871): ${FINISH_LOOP_TEMPLATE_NAME} — edit/push/pr/merge (no release ops)`,
|
|
196
|
+
" Templates call mintHumanOriginGrant only — no second session-auth mint engine.",
|
|
197
|
+
" Env bypass for a single shell: DEFT_ALLOW_RELEASE_PUBLISH=1 / DEFT_ALLOW_FINISH_LOOP=1.",
|
|
198
|
+
].join("\n");
|
|
199
|
+
}
|
|
200
|
+
export function main(argv = process.argv.slice(2)) {
|
|
201
|
+
const args = parseArgv(argv);
|
|
202
|
+
if (args.error === "help") {
|
|
203
|
+
process.stdout.write(`${helpText()}\n`);
|
|
204
|
+
return 0;
|
|
205
|
+
}
|
|
206
|
+
if (args.error !== undefined) {
|
|
207
|
+
process.stderr.write(`authz: ${args.error}\n`);
|
|
208
|
+
process.stderr.write(`${helpText()}\n`);
|
|
209
|
+
return 2;
|
|
210
|
+
}
|
|
211
|
+
try {
|
|
212
|
+
switch (args.cmd) {
|
|
213
|
+
case "show": {
|
|
214
|
+
const snap = showAuthzSnapshot(args.projectRoot);
|
|
215
|
+
if (args.format === "json") {
|
|
216
|
+
process.stdout.write(`${JSON.stringify(snap, null, 2)}\n`);
|
|
217
|
+
return 0;
|
|
218
|
+
}
|
|
219
|
+
const uat = snap.state.uat;
|
|
220
|
+
if (uat === null) {
|
|
221
|
+
process.stdout.write("UAT lease: inactive\n");
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
process.stdout.write(`UAT lease: ${uat.active ? "ACTIVE" : "suspended"} campaign=${uat.campaignId}\n`);
|
|
225
|
+
process.stdout.write(` started=${uat.startedAt} by=${uat.startedBy.actor} (${uat.startedBy.kind})\n`);
|
|
226
|
+
if (uat.suspendedAt)
|
|
227
|
+
process.stdout.write(` suspended=${uat.suspendedAt}\n`);
|
|
228
|
+
}
|
|
229
|
+
process.stdout.write(`Active human-origin grants: ${snap.activeGrants.length}\n`);
|
|
230
|
+
for (const g of snap.activeGrants) {
|
|
231
|
+
process.stdout.write(` - ${g.id} ops=[${g.scope.operations.join(",")}] ` +
|
|
232
|
+
`cohort=${g.scope.cohortId ?? "-"} surfaces=${g.scope.surfaces.join("|") || "*"}\n`);
|
|
233
|
+
}
|
|
234
|
+
const rejected = snap.allGrants.length - snap.activeGrants.length;
|
|
235
|
+
if (rejected > 0) {
|
|
236
|
+
process.stdout.write(`(${rejected} grant file(s) present but not active/human-origin)\n`);
|
|
237
|
+
}
|
|
238
|
+
return 0;
|
|
239
|
+
}
|
|
240
|
+
case "uat-start": {
|
|
241
|
+
if (args.campaign === null || args.campaign.trim().length === 0) {
|
|
242
|
+
process.stderr.write("authz:uat-start requires --campaign <id>\n");
|
|
243
|
+
return 2;
|
|
244
|
+
}
|
|
245
|
+
const { lease } = startUatLease({
|
|
246
|
+
projectRoot: args.projectRoot,
|
|
247
|
+
campaignId: args.campaign,
|
|
248
|
+
actor: args.actor,
|
|
249
|
+
note: args.note,
|
|
250
|
+
});
|
|
251
|
+
process.stdout.write(`✓ UAT lease ACTIVE campaign=${lease.campaignId} (human-origin operator-cli)\n`);
|
|
252
|
+
process.stdout.write(" Product edit/push/PR/merge denied until a named fix cohort grant is minted.\n");
|
|
253
|
+
process.stdout.write(" Tests, evidence capture, and issue filing remain allowed.\n");
|
|
254
|
+
return 0;
|
|
255
|
+
}
|
|
256
|
+
case "uat-suspend": {
|
|
257
|
+
const state = suspendUatLease({
|
|
258
|
+
projectRoot: args.projectRoot,
|
|
259
|
+
actor: args.actor,
|
|
260
|
+
});
|
|
261
|
+
if (state.uat === null) {
|
|
262
|
+
process.stdout.write("UAT lease was already inactive.\n");
|
|
263
|
+
}
|
|
264
|
+
else {
|
|
265
|
+
process.stdout.write(`✓ UAT lease suspended campaign=${state.uat.campaignId} at ${state.uat.suspendedAt}\n`);
|
|
266
|
+
}
|
|
267
|
+
return 0;
|
|
268
|
+
}
|
|
269
|
+
case "grant": {
|
|
270
|
+
// AFK template path (#1095 / #871): presets only — still mintHumanOriginGrant.
|
|
271
|
+
if (args.template !== null && args.template.trim().length > 0) {
|
|
272
|
+
if (!isAfkTemplateName(args.template)) {
|
|
273
|
+
process.stderr.write(`authz:grant unknown --template '${args.template}'; expected one of: ${AFK_TEMPLATE_NAMES.join(", ")}\n`);
|
|
274
|
+
return 2;
|
|
275
|
+
}
|
|
276
|
+
if (isClosedVerbTemplateName(args.template) &&
|
|
277
|
+
(args.target === null || args.target.trim().length === 0)) {
|
|
278
|
+
process.stderr.write(`authz:grant --template ${args.template} requires --target <version>\n`);
|
|
279
|
+
return 2;
|
|
280
|
+
}
|
|
281
|
+
const grant = mintAfkTemplateGrant({
|
|
282
|
+
projectRoot: args.projectRoot,
|
|
283
|
+
template: args.template,
|
|
284
|
+
target: args.target,
|
|
285
|
+
actor: args.actor,
|
|
286
|
+
expiresAt: args.expiresAt,
|
|
287
|
+
singleUse: args.singleUse,
|
|
288
|
+
planRef: args.planRef,
|
|
289
|
+
repo: args.repo,
|
|
290
|
+
branch: args.branch,
|
|
291
|
+
surfaces: args.surfaces,
|
|
292
|
+
storyIds: args.storyIds,
|
|
293
|
+
issueIds: args.issueIds,
|
|
294
|
+
cohortId: args.cohort,
|
|
295
|
+
});
|
|
296
|
+
process.stdout.write(`✓ human-origin grant minted id=${grant.id} origin=${grant.origin.kind} ` +
|
|
297
|
+
`template=${args.template}\n`);
|
|
298
|
+
if (isFinishLoopTemplateName(args.template)) {
|
|
299
|
+
process.stdout.write(` ops=[${grant.scope.operations.join(",")}] ` +
|
|
300
|
+
`(finish-loop walk-away; release-* NOT authorized)\n`);
|
|
301
|
+
}
|
|
302
|
+
else {
|
|
303
|
+
process.stdout.write(` ops=[${grant.scope.operations.join(",")}] target surfaces=${grant.scope.surfaces.join(", ")}\n`);
|
|
304
|
+
}
|
|
305
|
+
process.stdout.write(" Authorization SoT: Wave 1 grant store (.deft/authz/grants) — not session-auth.\n");
|
|
306
|
+
return 0;
|
|
307
|
+
}
|
|
308
|
+
if (args.operations.length === 0) {
|
|
309
|
+
process.stderr.write("authz:grant requires --operations <edit,push,...> or --template <finish-loop|release-*> \n");
|
|
310
|
+
return 2;
|
|
311
|
+
}
|
|
312
|
+
const grant = mintHumanOriginGrant({
|
|
313
|
+
projectRoot: args.projectRoot,
|
|
314
|
+
actor: args.actor,
|
|
315
|
+
operations: args.operations,
|
|
316
|
+
surfaces: args.surfaces,
|
|
317
|
+
cohortId: args.cohort,
|
|
318
|
+
planRef: args.planRef,
|
|
319
|
+
repo: args.repo,
|
|
320
|
+
branch: args.branch,
|
|
321
|
+
storyIds: args.storyIds,
|
|
322
|
+
issueIds: args.issueIds,
|
|
323
|
+
expiresAt: args.expiresAt,
|
|
324
|
+
singleUse: args.singleUse,
|
|
325
|
+
});
|
|
326
|
+
process.stdout.write(`✓ human-origin grant minted id=${grant.id} origin=${grant.origin.kind}\n`);
|
|
327
|
+
process.stdout.write(` ops=[${grant.scope.operations.join(",")}] cohort=${grant.scope.cohortId ?? "-"}\n`);
|
|
328
|
+
if (grant.scope.surfaces.length > 0) {
|
|
329
|
+
process.stdout.write(` surfaces=${grant.scope.surfaces.join(", ")}\n`);
|
|
330
|
+
}
|
|
331
|
+
return 0;
|
|
332
|
+
}
|
|
333
|
+
case "revoke": {
|
|
334
|
+
if (args.grantId === null) {
|
|
335
|
+
process.stderr.write("authz:revoke requires <grant-id>\n");
|
|
336
|
+
return 2;
|
|
337
|
+
}
|
|
338
|
+
const revoked = revokeGrant({
|
|
339
|
+
projectRoot: args.projectRoot,
|
|
340
|
+
grantId: args.grantId,
|
|
341
|
+
});
|
|
342
|
+
if (revoked === null) {
|
|
343
|
+
process.stderr.write(`authz: grant not found: ${args.grantId}\n`);
|
|
344
|
+
return 1;
|
|
345
|
+
}
|
|
346
|
+
process.stdout.write(`✓ grant revoked id=${revoked.id} at ${revoked.semantics.revokedAt}\n`);
|
|
347
|
+
return 0;
|
|
348
|
+
}
|
|
349
|
+
default:
|
|
350
|
+
process.stderr.write(`${helpText()}\n`);
|
|
351
|
+
return 2;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
catch (err) {
|
|
355
|
+
process.stderr.write(`authz: ${String(err)}\n`);
|
|
356
|
+
return 1;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
export default main;
|
|
360
|
+
if (import.meta.url === `file://${process.argv[1]?.replace(/\\/g, "/")}`) {
|
|
361
|
+
process.exitCode = main();
|
|
362
|
+
}
|
|
363
|
+
//# sourceMappingURL=authz.js.map
|
|
@@ -36,6 +36,7 @@ export const PR_VERB_MAP = {
|
|
|
36
36
|
"check-closing-keywords": "pr-closing-keywords",
|
|
37
37
|
"wait-mergeable-and-merge": "pr-wait-mergeable",
|
|
38
38
|
watch: "pr-watch",
|
|
39
|
+
"finish-loop": "pr-finish-loop",
|
|
39
40
|
};
|
|
40
41
|
/** verify:* aliases that map to non-verify-* handler stems. */
|
|
41
42
|
export const VERIFY_VERB_MAP = {
|
|
@@ -74,8 +75,18 @@ export const SUBCOMMAND_ROUTES = {
|
|
|
74
75
|
"policy:enable-directive": ["policy", "enable-directive"],
|
|
75
76
|
"policy:enforce-branches": ["policy", "enforce-branches"],
|
|
76
77
|
"policy:allow-direct-commits": ["policy", "allow-direct-commits"],
|
|
78
|
+
"policy:allow-bot-merge": ["policy", "allow-bot-merge"],
|
|
77
79
|
"policy:enable-value-feedback": ["policy", "enable-value-feedback"],
|
|
78
80
|
"policy:clear-value-feedback": ["policy", "clear-value-feedback"],
|
|
81
|
+
"authz:show": ["authz", "show"],
|
|
82
|
+
"authz:uat-start": ["authz", "uat-start"],
|
|
83
|
+
"authz:uat-suspend": ["authz", "uat-suspend"],
|
|
84
|
+
"authz:grant": ["authz", "grant"],
|
|
85
|
+
"authz:revoke": ["authz", "revoke"],
|
|
86
|
+
"escalation:file": ["escalation-cli", "file"],
|
|
87
|
+
"escalation:list": ["escalation-cli", "list"],
|
|
88
|
+
"escalation:resolve": ["escalation-cli", "resolve"],
|
|
89
|
+
"escalation:batch-approve": ["escalation-cli", "batch-approve"],
|
|
79
90
|
"product-signal:status": ["product-signal", "status"],
|
|
80
91
|
"product-signal:enable": ["product-signal", "enable"],
|
|
81
92
|
"product-signal:consent": ["product-signal", "consent"],
|
|
@@ -89,9 +100,11 @@ export const SUBCOMMAND_ROUTES = {
|
|
|
89
100
|
"github-body:issue-create": ["github-body", "issue-create"],
|
|
90
101
|
"github-body:issue-edit": ["github-body", "issue-edit"],
|
|
91
102
|
"github-body:issue-fetch": ["github-body", "issue-fetch"],
|
|
103
|
+
"github-body:issue-lint": ["github-body", "issue-lint"],
|
|
92
104
|
"github-body:comment-create": ["github-body", "comment-create"],
|
|
93
105
|
"github-body:comment-edit": ["github-body", "comment-edit"],
|
|
94
106
|
"github-body:pr-edit": ["github-body", "pr-edit"],
|
|
107
|
+
"github-body:pr-lint": ["github-body", "pr-lint"],
|
|
95
108
|
"plan-sequence:set": ["plan-sequence", "set"],
|
|
96
109
|
"plan-sequence:current": ["plan-sequence", "current"],
|
|
97
110
|
"plan-sequence:clear": ["plan-sequence", "clear"],
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { cmdDirectiveFinishLoop } from "@deftai/directive-core/dist/finish-loop/main.js";
|
|
4
|
+
export function run(argv) {
|
|
5
|
+
return cmdDirectiveFinishLoop(argv);
|
|
6
|
+
}
|
|
7
|
+
if (process.argv[1] !== undefined && fileURLToPath(import.meta.url) === process.argv[1]) {
|
|
8
|
+
process.exit(run(process.argv.slice(2)));
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=directive-finish-loop.js.map
|
package/dist/dispatch.d.ts
CHANGED
|
@@ -10,13 +10,17 @@ 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", "pr-closing-keywords", "pr-merge-readiness", "pr-monitor", "pr-protected-issues", "pr-wait-mergeable", "pr-watch", "preflight-cache", "preflight-gh", "probe-session", "release", "release-e2e", "release-publish", "release-rollback", "scope-lifecycle", "lifecycle-event", "session-start", "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", "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-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-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"];
|
|
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", "session-start", "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", "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-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
15
|
export declare const CORE_MODULE_VERBS: readonly ["scm", "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"];
|
|
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). */
|
|
19
19
|
export declare const POLICY_ACTION_ALIAS_SUBCOMMANDS: Readonly<Record<string, string>>;
|
|
20
|
+
/** Colon aliases for authz subcommands (#2944). */
|
|
21
|
+
export declare const AUTHZ_ACTION_ALIAS_SUBCOMMANDS: Readonly<Record<string, string>>;
|
|
22
|
+
/** Colon aliases for escalation subcommands (#518). */
|
|
23
|
+
export declare const ESCALATION_ACTION_ALIAS_SUBCOMMANDS: Readonly<Record<string, string>>;
|
|
20
24
|
/** Colon aliases for plan-sequence subcommands (#2402). */
|
|
21
25
|
export declare const PLAN_SEQUENCE_ALIAS_SUBCOMMANDS: Readonly<Record<string, string>>;
|
|
22
26
|
/** Colon aliases for product-signal subcommands (#2693). */
|
package/dist/dispatch.js
CHANGED
|
@@ -50,12 +50,16 @@ export const CLI_MODULE_VERBS = [
|
|
|
50
50
|
"commit-lint",
|
|
51
51
|
"coverage-hotspots",
|
|
52
52
|
"policy",
|
|
53
|
+
"authz",
|
|
54
|
+
"escalation-cli",
|
|
53
55
|
"pr-closing-keywords",
|
|
54
56
|
"pr-merge-readiness",
|
|
55
57
|
"pr-monitor",
|
|
56
58
|
"pr-protected-issues",
|
|
57
59
|
"pr-wait-mergeable",
|
|
58
60
|
"pr-watch",
|
|
61
|
+
"pr-finish-loop",
|
|
62
|
+
"directive-finish-loop",
|
|
59
63
|
"preflight-cache",
|
|
60
64
|
"preflight-gh",
|
|
61
65
|
"probe-session",
|
|
@@ -103,6 +107,7 @@ export const CLI_MODULE_VERBS = [
|
|
|
103
107
|
"verify-biome-config",
|
|
104
108
|
"verify-bridge-drift",
|
|
105
109
|
"verify-capacity",
|
|
110
|
+
"verify-contained-writes",
|
|
106
111
|
"verify-content-manifest",
|
|
107
112
|
"verify-skill-external-fetch-gate",
|
|
108
113
|
"verify-contract-drift",
|
|
@@ -193,12 +198,30 @@ export const POLICY_ACTION_ALIAS_SUBCOMMANDS = {
|
|
|
193
198
|
"policy:show": "show",
|
|
194
199
|
"policy:enforce-branches": "enforce-branches",
|
|
195
200
|
"policy:allow-direct-commits": "allow-direct-commits",
|
|
201
|
+
"policy:allow-bot-merge": "allow-bot-merge",
|
|
196
202
|
"policy:enable-value-feedback": "enable-value-feedback",
|
|
197
203
|
"policy:clear-value-feedback": "clear-value-feedback",
|
|
198
204
|
"policy:disable-directive": "disable-directive",
|
|
199
205
|
"policy:enable-directive": "enable-directive",
|
|
200
206
|
};
|
|
201
207
|
const POLICY_ACTION_COLON_ALIASES = Object.fromEntries(Object.keys(POLICY_ACTION_ALIAS_SUBCOMMANDS).map((alias) => [alias, "policy"]));
|
|
208
|
+
/** Colon aliases for authz subcommands (#2944). */
|
|
209
|
+
export const AUTHZ_ACTION_ALIAS_SUBCOMMANDS = {
|
|
210
|
+
"authz:show": "show",
|
|
211
|
+
"authz:uat-start": "uat-start",
|
|
212
|
+
"authz:uat-suspend": "uat-suspend",
|
|
213
|
+
"authz:grant": "grant",
|
|
214
|
+
"authz:revoke": "revoke",
|
|
215
|
+
};
|
|
216
|
+
const AUTHZ_ACTION_COLON_ALIASES = Object.fromEntries(Object.keys(AUTHZ_ACTION_ALIAS_SUBCOMMANDS).map((alias) => [alias, "authz"]));
|
|
217
|
+
/** Colon aliases for escalation subcommands (#518). */
|
|
218
|
+
export const ESCALATION_ACTION_ALIAS_SUBCOMMANDS = {
|
|
219
|
+
"escalation:file": "file",
|
|
220
|
+
"escalation:list": "list",
|
|
221
|
+
"escalation:resolve": "resolve",
|
|
222
|
+
"escalation:batch-approve": "batch-approve",
|
|
223
|
+
};
|
|
224
|
+
const ESCALATION_ACTION_COLON_ALIASES = Object.fromEntries(Object.keys(ESCALATION_ACTION_ALIAS_SUBCOMMANDS).map((alias) => [alias, "escalation-cli"]));
|
|
202
225
|
/** Colon aliases for plan-sequence subcommands (#2402). */
|
|
203
226
|
export const PLAN_SEQUENCE_ALIAS_SUBCOMMANDS = {
|
|
204
227
|
"plan-sequence:set": "set",
|
|
@@ -251,6 +274,7 @@ export const VERB_ALIASES = {
|
|
|
251
274
|
"verify:rule-ownership": "rule-ownership-lint",
|
|
252
275
|
"rule:ownership-lint": "rule-ownership-lint",
|
|
253
276
|
"verify:biome-config": "verify-biome-config",
|
|
277
|
+
"verify:contained-writes": "verify-contained-writes",
|
|
254
278
|
"verify:content-manifest": "verify-content-manifest",
|
|
255
279
|
"verify:skill-external-fetch-gate": "verify-skill-external-fetch-gate",
|
|
256
280
|
"verify:contract-drift": "verify-contract-drift",
|
|
@@ -275,6 +299,8 @@ export const VERB_ALIASES = {
|
|
|
275
299
|
"triage:scope": "triage-scope",
|
|
276
300
|
...TRIAGE_ACTION_COLON_ALIASES,
|
|
277
301
|
...POLICY_ACTION_COLON_ALIASES,
|
|
302
|
+
...AUTHZ_ACTION_COLON_ALIASES,
|
|
303
|
+
...ESCALATION_ACTION_COLON_ALIASES,
|
|
278
304
|
...PRODUCT_SIGNAL_COLON_ALIASES,
|
|
279
305
|
"agents:refresh": "agents-refresh",
|
|
280
306
|
"migrate:preflight": "migrate-preflight",
|
|
@@ -295,6 +321,8 @@ export const VERB_ALIASES = {
|
|
|
295
321
|
"docs:rule-map": "rule-map",
|
|
296
322
|
"project:export-spec": "export-spec",
|
|
297
323
|
"pr:watch": "pr-watch",
|
|
324
|
+
"pr:finish-loop": "pr-finish-loop",
|
|
325
|
+
"directive:finish-loop": "directive-finish-loop",
|
|
298
326
|
doctor: "doctor",
|
|
299
327
|
"eval:health": "eval-health",
|
|
300
328
|
"feedback:file": "feedback-file",
|
|
@@ -311,6 +339,7 @@ const SUBDIR_CLI_STEMS = {
|
|
|
311
339
|
"verify-stubs": "verify-source-cli/verify-stubs",
|
|
312
340
|
"rule-ownership-lint": "verify-source-cli/rule-ownership-lint",
|
|
313
341
|
"verify-biome-config": "verify-source-cli/verify-biome-config",
|
|
342
|
+
"verify-contained-writes": "verify-source-cli/verify-contained-writes",
|
|
314
343
|
"verify-content-manifest": "verify-source-cli/verify-content-manifest",
|
|
315
344
|
"verify-skill-external-fetch-gate": "verify-source-cli/verify-skill-external-fetch-gate",
|
|
316
345
|
"verify-contract-drift": "verify-source-cli/verify-contract-drift",
|
|
@@ -2598,6 +2627,8 @@ export async function dispatch(argv, io = defaultIo()) {
|
|
|
2598
2627
|
const handler = await loadHandler(canonical, io);
|
|
2599
2628
|
const triageSubcommand = verb !== undefined ? TRIAGE_ACTION_ALIAS_SUBCOMMANDS[verb] : undefined;
|
|
2600
2629
|
const policySubcommand = verb !== undefined ? POLICY_ACTION_ALIAS_SUBCOMMANDS[verb] : undefined;
|
|
2630
|
+
const authzSubcommand = verb !== undefined ? AUTHZ_ACTION_ALIAS_SUBCOMMANDS[verb] : undefined;
|
|
2631
|
+
const escalationSubcommand = verb !== undefined ? ESCALATION_ACTION_ALIAS_SUBCOMMANDS[verb] : undefined;
|
|
2601
2632
|
const planSequenceSubcommand = verb !== undefined ? PLAN_SEQUENCE_ALIAS_SUBCOMMANDS[verb] : undefined;
|
|
2602
2633
|
const productSignalSubcommand = verb !== undefined ? PRODUCT_SIGNAL_ALIAS_SUBCOMMANDS[verb] : undefined;
|
|
2603
2634
|
const handlerArgv = canonical === "framework-commands" && verb !== undefined && verb !== canonical
|
|
@@ -2606,11 +2637,15 @@ export async function dispatch(argv, io = defaultIo()) {
|
|
|
2606
2637
|
? [triageSubcommand, ...rest]
|
|
2607
2638
|
: policySubcommand !== undefined && canonical === "policy"
|
|
2608
2639
|
? [policySubcommand, ...rest]
|
|
2609
|
-
:
|
|
2610
|
-
? [
|
|
2611
|
-
:
|
|
2612
|
-
? [
|
|
2613
|
-
:
|
|
2640
|
+
: authzSubcommand !== undefined && canonical === "authz"
|
|
2641
|
+
? [authzSubcommand, ...rest]
|
|
2642
|
+
: escalationSubcommand !== undefined && canonical === "escalation-cli"
|
|
2643
|
+
? [escalationSubcommand, ...rest]
|
|
2644
|
+
: planSequenceSubcommand !== undefined && canonical === "plan-sequence"
|
|
2645
|
+
? [planSequenceSubcommand, ...rest]
|
|
2646
|
+
: productSignalSubcommand !== undefined && canonical === "product-signal"
|
|
2647
|
+
? [productSignalSubcommand, ...rest]
|
|
2648
|
+
: rest;
|
|
2614
2649
|
return await invokeHandler(handler, handlerArgv);
|
|
2615
2650
|
}
|
|
2616
2651
|
catch (err) {
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Escalation CLI (#518 slim / #2948 Wave 5): typed queue under .deft/escalations/.
|
|
4
|
+
*
|
|
5
|
+
* deft escalation:file -- --type cmd_approval --title "…"
|
|
6
|
+
* deft escalation:list [--open] [--type <type>] [--format json]
|
|
7
|
+
* deft escalation:resolve -- <id> --decision approved|denied|answered|dismissed
|
|
8
|
+
* deft escalation:batch-approve [--ids a,b] [--include-dangerous]
|
|
9
|
+
*/
|
|
10
|
+
import { batchApproveEscalations, ESCALATION_TYPES, fileEscalation, isEscalationType, listEscalationsFiltered, resolveEscalation, } from "@deftai/directive-core/escalation";
|
|
11
|
+
function parseArgv(argv) {
|
|
12
|
+
const base = {
|
|
13
|
+
cmd: "list",
|
|
14
|
+
projectRoot: process.cwd(),
|
|
15
|
+
type: null,
|
|
16
|
+
title: null,
|
|
17
|
+
body: null,
|
|
18
|
+
agentId: "agent",
|
|
19
|
+
contextRefs: [],
|
|
20
|
+
slaHours: null,
|
|
21
|
+
dangerous: false,
|
|
22
|
+
id: null,
|
|
23
|
+
ids: [],
|
|
24
|
+
decision: null,
|
|
25
|
+
actor: "operator",
|
|
26
|
+
note: null,
|
|
27
|
+
answer: null,
|
|
28
|
+
openOnly: false,
|
|
29
|
+
includeDangerous: false,
|
|
30
|
+
format: "text",
|
|
31
|
+
};
|
|
32
|
+
const args = [...argv];
|
|
33
|
+
while (args[0] === "--")
|
|
34
|
+
args.shift();
|
|
35
|
+
if (args.length > 0 && !args[0]?.startsWith("-")) {
|
|
36
|
+
const cmd = args.shift();
|
|
37
|
+
if (cmd === "file" || cmd === "list" || cmd === "resolve" || cmd === "batch-approve") {
|
|
38
|
+
base.cmd = cmd;
|
|
39
|
+
}
|
|
40
|
+
else if (cmd.startsWith("esc-")) {
|
|
41
|
+
base.cmd = "resolve";
|
|
42
|
+
base.id = cmd;
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
return { ...base, error: `unknown escalation subcommand: ${cmd}` };
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
while (args[0] === "--")
|
|
49
|
+
args.shift();
|
|
50
|
+
for (let i = 0; i < args.length; i++) {
|
|
51
|
+
const a = args[i];
|
|
52
|
+
if (a === undefined)
|
|
53
|
+
break;
|
|
54
|
+
if (a === "--project-root" || a === "--projectRoot") {
|
|
55
|
+
base.projectRoot = args[++i] ?? base.projectRoot;
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (a === "--type") {
|
|
59
|
+
base.type = args[++i] ?? null;
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (a === "--title") {
|
|
63
|
+
base.title = args[++i] ?? null;
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (a === "--body") {
|
|
67
|
+
base.body = args[++i] ?? null;
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
if (a === "--agent" || a === "--agent-id" || a === "--agentId") {
|
|
71
|
+
base.agentId = args[++i] ?? base.agentId;
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
if (a === "--context" || a === "--context-refs" || a === "--contextRefs") {
|
|
75
|
+
const raw = args[++i] ?? "";
|
|
76
|
+
base.contextRefs = raw
|
|
77
|
+
.split(/[,\s]+/)
|
|
78
|
+
.map((s) => s.trim())
|
|
79
|
+
.filter((s) => s.length > 0);
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
if (a === "--sla-hours" || a === "--slaHours") {
|
|
83
|
+
const n = Number(args[++i] ?? "");
|
|
84
|
+
base.slaHours = Number.isFinite(n) ? n : null;
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
if (a === "--dangerous") {
|
|
88
|
+
base.dangerous = true;
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
if (a === "--id") {
|
|
92
|
+
base.id = args[++i] ?? null;
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (a === "--ids") {
|
|
96
|
+
const raw = args[++i] ?? "";
|
|
97
|
+
base.ids = raw
|
|
98
|
+
.split(/[,\s]+/)
|
|
99
|
+
.map((s) => s.trim())
|
|
100
|
+
.filter((s) => s.length > 0);
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
if (a === "--decision") {
|
|
104
|
+
base.decision = args[++i] ?? null;
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
if (a === "--actor") {
|
|
108
|
+
base.actor = args[++i] ?? base.actor;
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
if (a === "--note") {
|
|
112
|
+
base.note = args[++i] ?? null;
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
if (a === "--answer") {
|
|
116
|
+
base.answer = args[++i] ?? null;
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
if (a === "--open") {
|
|
120
|
+
base.openOnly = true;
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
if (a === "--include-dangerous") {
|
|
124
|
+
base.includeDangerous = true;
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
if (a === "--format") {
|
|
128
|
+
const fmt = (args[++i] ?? "text").toLowerCase();
|
|
129
|
+
base.format = fmt === "json" ? "json" : "text";
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
if (!a.startsWith("-") && base.cmd === "resolve" && base.id === null) {
|
|
133
|
+
base.id = a;
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
if (a === "--help" || a === "-h") {
|
|
137
|
+
return { ...base, error: "help" };
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return base;
|
|
141
|
+
}
|
|
142
|
+
function helpText() {
|
|
143
|
+
return [
|
|
144
|
+
"Usage:",
|
|
145
|
+
" deft escalation:file -- --type <type> --title <text> [--body <text>] [--agent <id>]",
|
|
146
|
+
" [--context refs…] [--sla-hours N] [--dangerous] [--format json]",
|
|
147
|
+
" deft escalation:list [--open] [--type <type>] [--format json]",
|
|
148
|
+
" deft escalation:resolve -- <id> --decision approved|denied|answered|dismissed",
|
|
149
|
+
" [--note <text>] [--answer <text>] [--actor <name>]",
|
|
150
|
+
" deft escalation:batch-approve [--ids a,b] [--include-dangerous] [--note <text>]",
|
|
151
|
+
"",
|
|
152
|
+
`Types: ${ESCALATION_TYPES.join(", ")}`,
|
|
153
|
+
"Bulk batch-approve is limited to cmd_approval + question (non-dangerous by default).",
|
|
154
|
+
"design_decision / approval / resource / external require individual resolve.",
|
|
155
|
+
"Store: .deft/escalations/<id>.json Contract: content/contracts/escalation.md",
|
|
156
|
+
"Compose gated actions with deft authz:grant after approval (Wave 1 grants).",
|
|
157
|
+
].join("\n");
|
|
158
|
+
}
|
|
159
|
+
export function main(argv = process.argv.slice(2)) {
|
|
160
|
+
const args = parseArgv(argv);
|
|
161
|
+
if (args.error === "help") {
|
|
162
|
+
process.stdout.write(`${helpText()}\n`);
|
|
163
|
+
return 0;
|
|
164
|
+
}
|
|
165
|
+
if (args.error !== undefined) {
|
|
166
|
+
process.stderr.write(`escalation: ${args.error}\n`);
|
|
167
|
+
process.stderr.write(`${helpText()}\n`);
|
|
168
|
+
return 2;
|
|
169
|
+
}
|
|
170
|
+
try {
|
|
171
|
+
switch (args.cmd) {
|
|
172
|
+
case "file": {
|
|
173
|
+
if (args.type === null || args.type.trim().length === 0) {
|
|
174
|
+
process.stderr.write("escalation:file requires --type <type>\n");
|
|
175
|
+
return 2;
|
|
176
|
+
}
|
|
177
|
+
if (args.title === null || args.title.trim().length === 0) {
|
|
178
|
+
process.stderr.write("escalation:file requires --title <text>\n");
|
|
179
|
+
return 2;
|
|
180
|
+
}
|
|
181
|
+
const event = fileEscalation({
|
|
182
|
+
projectRoot: args.projectRoot,
|
|
183
|
+
type: args.type,
|
|
184
|
+
title: args.title,
|
|
185
|
+
body: args.body ?? undefined,
|
|
186
|
+
agentId: args.agentId,
|
|
187
|
+
contextRefs: args.contextRefs,
|
|
188
|
+
slaHours: args.slaHours ?? undefined,
|
|
189
|
+
dangerous: args.dangerous,
|
|
190
|
+
id: args.id ?? undefined,
|
|
191
|
+
});
|
|
192
|
+
if (args.format === "json") {
|
|
193
|
+
process.stdout.write(`${JSON.stringify(event, null, 2)}\n`);
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
process.stdout.write(`✓ escalation filed id=${event.id} type=${event.type} status=${event.status}` +
|
|
197
|
+
`${event.dangerous ? " dangerous=true" : ""}\n`);
|
|
198
|
+
process.stdout.write(` title=${event.title}\n`);
|
|
199
|
+
process.stdout.write(` store=.deft/escalations/${event.id}.json\n`);
|
|
200
|
+
}
|
|
201
|
+
return 0;
|
|
202
|
+
}
|
|
203
|
+
case "list": {
|
|
204
|
+
let typeFilter;
|
|
205
|
+
if (args.type !== null && args.type.trim().length > 0) {
|
|
206
|
+
if (!isEscalationType(args.type.trim().toLowerCase())) {
|
|
207
|
+
process.stderr.write(`escalation:list unknown --type '${args.type}'; expected: ${ESCALATION_TYPES.join(", ")}\n`);
|
|
208
|
+
return 2;
|
|
209
|
+
}
|
|
210
|
+
typeFilter = args.type.trim().toLowerCase();
|
|
211
|
+
}
|
|
212
|
+
const items = listEscalationsFiltered(args.projectRoot, {
|
|
213
|
+
openOnly: args.openOnly,
|
|
214
|
+
type: typeFilter,
|
|
215
|
+
});
|
|
216
|
+
if (args.format === "json") {
|
|
217
|
+
process.stdout.write(`${JSON.stringify(items, null, 2)}\n`);
|
|
218
|
+
return 0;
|
|
219
|
+
}
|
|
220
|
+
if (items.length === 0) {
|
|
221
|
+
process.stdout.write(args.openOnly ? "No open escalations.\n" : "No escalations on disk.\n");
|
|
222
|
+
return 0;
|
|
223
|
+
}
|
|
224
|
+
process.stdout.write(`Escalations (${items.length}):\n`);
|
|
225
|
+
for (const e of items) {
|
|
226
|
+
const dang = e.dangerous ? " !dangerous" : "";
|
|
227
|
+
process.stdout.write(` - ${e.id} [${e.status}] type=${e.type}${dang} agent=${e.agentId} sla=${e.slaHours}h\n`);
|
|
228
|
+
process.stdout.write(` ${e.title}\n`);
|
|
229
|
+
if (e.resolution) {
|
|
230
|
+
process.stdout.write(` → ${e.resolution.decision} by ${e.resolution.resolvedBy} @ ${e.resolution.resolvedAt}\n`);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return 0;
|
|
234
|
+
}
|
|
235
|
+
case "resolve": {
|
|
236
|
+
if (args.id === null || args.id.trim().length === 0) {
|
|
237
|
+
process.stderr.write("escalation:resolve requires <id>\n");
|
|
238
|
+
return 2;
|
|
239
|
+
}
|
|
240
|
+
if (args.decision === null || args.decision.trim().length === 0) {
|
|
241
|
+
process.stderr.write("escalation:resolve requires --decision approved|denied|answered|dismissed\n");
|
|
242
|
+
return 2;
|
|
243
|
+
}
|
|
244
|
+
const result = resolveEscalation({
|
|
245
|
+
projectRoot: args.projectRoot,
|
|
246
|
+
id: args.id,
|
|
247
|
+
decision: args.decision,
|
|
248
|
+
actor: args.actor,
|
|
249
|
+
note: args.note,
|
|
250
|
+
answer: args.answer,
|
|
251
|
+
});
|
|
252
|
+
if (!result.ok) {
|
|
253
|
+
process.stderr.write(`escalation: ${result.message}\n`);
|
|
254
|
+
return result.code === "not-found" ? 1 : 2;
|
|
255
|
+
}
|
|
256
|
+
if (args.format === "json") {
|
|
257
|
+
process.stdout.write(`${JSON.stringify(result.event, null, 2)}\n`);
|
|
258
|
+
}
|
|
259
|
+
else {
|
|
260
|
+
process.stdout.write(`✓ resolved id=${result.event.id} decision=${result.event.resolution?.decision}\n`);
|
|
261
|
+
process.stdout.write(" For gated product actions, mint a Wave 1 grant: deft authz:grant …\n");
|
|
262
|
+
}
|
|
263
|
+
return 0;
|
|
264
|
+
}
|
|
265
|
+
case "batch-approve": {
|
|
266
|
+
const batch = batchApproveEscalations({
|
|
267
|
+
projectRoot: args.projectRoot,
|
|
268
|
+
ids: args.ids.length > 0 ? args.ids : undefined,
|
|
269
|
+
actor: args.actor,
|
|
270
|
+
note: args.note,
|
|
271
|
+
includeDangerous: args.includeDangerous,
|
|
272
|
+
});
|
|
273
|
+
if (args.format === "json") {
|
|
274
|
+
process.stdout.write(`${JSON.stringify(batch, null, 2)}\n`);
|
|
275
|
+
}
|
|
276
|
+
else {
|
|
277
|
+
process.stdout.write(`✓ batch-approve approved=${batch.approved.length} skipped=${batch.skipped.length}\n`);
|
|
278
|
+
for (const e of batch.approved) {
|
|
279
|
+
process.stdout.write(` + ${e.id} type=${e.type}\n`);
|
|
280
|
+
}
|
|
281
|
+
for (const s of batch.skipped) {
|
|
282
|
+
process.stdout.write(` - skip ${s.id}: ${s.reason}\n`);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
return 0;
|
|
286
|
+
}
|
|
287
|
+
default:
|
|
288
|
+
process.stderr.write(`${helpText()}\n`);
|
|
289
|
+
return 2;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
catch (err) {
|
|
293
|
+
process.stderr.write(`escalation: ${String(err)}\n`);
|
|
294
|
+
return 1;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
export default main;
|
|
298
|
+
if (import.meta.url === `file://${process.argv[1]?.replace(/\\/g, "/")}`) {
|
|
299
|
+
process.exitCode = main();
|
|
300
|
+
}
|
|
301
|
+
//# sourceMappingURL=escalation-cli.js.map
|
package/dist/hook-dispatch.d.ts
CHANGED
|
@@ -48,10 +48,16 @@ export declare function readStdinHardened(readOnce: () => string, options?: {
|
|
|
48
48
|
nowMs?: () => number;
|
|
49
49
|
}): string;
|
|
50
50
|
export declare function parseArgs(argv: readonly string[]): ParsedArgs;
|
|
51
|
+
/** @deprecated Prefer ParsedHookPayload from @deftai/directive-core/hooks (#2950). */
|
|
51
52
|
export interface ParsedPayload {
|
|
52
53
|
readonly payload: unknown;
|
|
53
54
|
readonly context: HookPayloadContext;
|
|
54
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Parse host hook stdin into payload + context.
|
|
58
|
+
* Pure implementation lives in core classify (`parseHookStdin`); CLI re-exports
|
|
59
|
+
* for backward-compatible imports in tests (#2734 / #2738 / #2950).
|
|
60
|
+
*/
|
|
55
61
|
export declare function parsePayload(raw: string): ParsedPayload;
|
|
56
62
|
/**
|
|
57
63
|
* Provider-neutral hook dispatch CLI entry (`deft hook:dispatch` / `deft-hook`).
|
package/dist/hook-dispatch.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { readFileSync } from "node:fs";
|
|
3
3
|
import { resolve } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
-
import { decideHook, hookPayloadTopLevelKeys, isHookEvent, isHookHost, normalizeHookProjectRoot, projectRootFromHookPayload, renderHostDecision, } from "@deftai/directive-core/hooks";
|
|
5
|
+
import { decideHook, hookPayloadTopLevelKeys, isHookEvent, isHookHost, normalizeHookProjectRoot, parseHookStdin, projectRootFromHookPayload, renderHostDecision, } from "@deftai/directive-core/hooks";
|
|
6
6
|
/**
|
|
7
7
|
* How long to re-poll stdin after an empty first read before concluding the host
|
|
8
8
|
* sent nothing (#2864). Keeps well under Cursor's deposited `timeout: 5` while
|
|
@@ -103,59 +103,13 @@ export function parseArgs(argv) {
|
|
|
103
103
|
return { error: "--event is required" };
|
|
104
104
|
return parsed;
|
|
105
105
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
return raw.startsWith(UTF8_BOM) ? raw.slice(UTF8_BOM.length) : raw;
|
|
112
|
-
}
|
|
113
|
-
function trySynthesizeFreeFormApplyPatch(normalized) {
|
|
114
|
-
if (!normalized.includes(APPLY_PATCH_BEGIN_MARKER))
|
|
115
|
-
return null;
|
|
116
|
-
const mutations = [];
|
|
117
|
-
for (const match of normalized.matchAll(APPLY_PATCH_MUTATION_LINE_RE)) {
|
|
118
|
-
const op = match[1];
|
|
119
|
-
const path = match[2]?.trim();
|
|
120
|
-
if (op === undefined || !path)
|
|
121
|
-
continue;
|
|
122
|
-
mutations.push({ op, path });
|
|
123
|
-
}
|
|
124
|
-
if (mutations.length !== 1)
|
|
125
|
-
return null;
|
|
126
|
-
const sole = mutations[0];
|
|
127
|
-
if (sole === undefined || (sole.op !== "Add File" && sole.op !== "Update File"))
|
|
128
|
-
return null;
|
|
129
|
-
return {
|
|
130
|
-
payload: {
|
|
131
|
-
tool_name: "ApplyPatch",
|
|
132
|
-
tool_input: {
|
|
133
|
-
path: sole.path,
|
|
134
|
-
patch: normalized,
|
|
135
|
-
},
|
|
136
|
-
},
|
|
137
|
-
context: {},
|
|
138
|
-
};
|
|
139
|
-
}
|
|
106
|
+
/**
|
|
107
|
+
* Parse host hook stdin into payload + context.
|
|
108
|
+
* Pure implementation lives in core classify (`parseHookStdin`); CLI re-exports
|
|
109
|
+
* for backward-compatible imports in tests (#2734 / #2738 / #2950).
|
|
110
|
+
*/
|
|
140
111
|
export function parsePayload(raw) {
|
|
141
|
-
|
|
142
|
-
return { payload: {}, context: { stdinEmpty: true } };
|
|
143
|
-
}
|
|
144
|
-
const normalized = stripUtf8Bom(raw);
|
|
145
|
-
if (normalized.trim().length === 0) {
|
|
146
|
-
return { payload: {}, context: { stdinEmpty: true } };
|
|
147
|
-
}
|
|
148
|
-
try {
|
|
149
|
-
return { payload: JSON.parse(normalized), context: {} };
|
|
150
|
-
}
|
|
151
|
-
catch {
|
|
152
|
-
const synthesized = trySynthesizeFreeFormApplyPatch(normalized);
|
|
153
|
-
if (synthesized !== null)
|
|
154
|
-
return synthesized;
|
|
155
|
-
// tool.before is installed only on direct-write matchers, so an unreadable
|
|
156
|
-
// payload becomes a missing-tool denial rather than a fail-open crash.
|
|
157
|
-
return { payload: {}, context: { parseFailed: true } };
|
|
158
|
-
}
|
|
112
|
+
return parseHookStdin(raw);
|
|
159
113
|
}
|
|
160
114
|
/**
|
|
161
115
|
* Provider-neutral hook dispatch CLI entry (`deft hook:dispatch` / `deft-hook`).
|
package/dist/policy.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ interface ShowArgs {
|
|
|
7
7
|
error?: string;
|
|
8
8
|
}
|
|
9
9
|
interface SetArgs {
|
|
10
|
-
cmd: "show" | "enforce-branches" | "allow-direct-commits" | "enable-value-feedback" | "clear-value-feedback" | "disable-directive" | "enable-directive" | "resolve";
|
|
10
|
+
cmd: "show" | "enforce-branches" | "allow-direct-commits" | "allow-bot-merge" | "enable-value-feedback" | "clear-value-feedback" | "disable-directive" | "enable-directive" | "resolve";
|
|
11
11
|
confirm: boolean;
|
|
12
12
|
actor: string;
|
|
13
13
|
note: string;
|
package/dist/policy.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import { existsSync } from "node:fs";
|
|
7
7
|
import { resolve as pathResolve, relative } from "node:path";
|
|
8
8
|
import { fileURLToPath } from "node:url";
|
|
9
|
-
import { clearValueFeedback, createNoDeftDirectiveFlag, describeShadowedPlanExtension, detectNoDeftDirective, detectShadowedPlanExtensions, disclosureLine, enableValueFeedback, FIELD_VALUE_FEEDBACK, FIELD_VALUE_FEEDBACK_CLI_ALIAS, formatValueFeedbackStatusLine, inspectAllPolicies, inspectOnePolicy, loadProjectDefinition, NO_DEFT_DIRECTIVE_DISABLED_MESSAGE, NO_DEFT_DIRECTIVE_FLAG_NAME, NO_DEFT_DIRECTIVE_INCONSISTENT_MESSAGE, policyColonInvocation, projectDefinitionPath, pythonListRepr, pythonStringRepr, registeredPolicyNames, removeNoDeftDirectiveFlag, renderJson, renderText, resolvePolicy, resolveValueFeedback, setPolicy, } from "@deftai/directive-core/policy";
|
|
9
|
+
import { ALLOW_BOT_MERGE_CAPABILITY_COST, clearValueFeedback, createNoDeftDirectiveFlag, describeShadowedPlanExtension, detectNoDeftDirective, detectShadowedPlanExtensions, disclosureLine, enableValueFeedback, FIELD_VALUE_FEEDBACK, FIELD_VALUE_FEEDBACK_CLI_ALIAS, formatValueFeedbackStatusLine, humanMergeDisclosureLine, inspectAllPolicies, inspectOnePolicy, loadProjectDefinition, NO_DEFT_DIRECTIVE_DISABLED_MESSAGE, NO_DEFT_DIRECTIVE_FLAG_NAME, NO_DEFT_DIRECTIVE_INCONSISTENT_MESSAGE, policyColonInvocation, projectDefinitionPath, pythonListRepr, pythonStringRepr, registeredPolicyNames, removeNoDeftDirectiveFlag, renderJson, renderText, resolveHumanMergePolicy, resolvePolicy, resolveValueFeedback, setPolicy, setRequireHumanMerge, } from "@deftai/directive-core/policy";
|
|
10
10
|
const CAPABILITY_COST_DISCLOSURE = "\u26a0 Capability-cost disclosure -- enabling direct commits to the default " +
|
|
11
11
|
"branch turns OFF the deft branch-protection policy.\n" +
|
|
12
12
|
" \u2022 Pre-commit + pre-push hooks will no longer block default-branch " +
|
|
@@ -114,7 +114,7 @@ export function parseShowArgs(argv) {
|
|
|
114
114
|
/** Parse argv for the policy CLI (show + set subcommands). */
|
|
115
115
|
export function parseArgs(argv) {
|
|
116
116
|
if (argv.length === 0) {
|
|
117
|
-
const usage = "usage: policy [show|enforce-branches|allow-direct-commits|enable-value-feedback|clear-value-feedback|disable-directive|enable-directive|resolve] ...";
|
|
117
|
+
const usage = "usage: policy [show|enforce-branches|allow-direct-commits|allow-bot-merge|enable-value-feedback|clear-value-feedback|disable-directive|enable-directive|resolve] ...";
|
|
118
118
|
return makeSetError(usage);
|
|
119
119
|
}
|
|
120
120
|
const cmd = argv[0];
|
|
@@ -148,6 +148,7 @@ export function parseArgs(argv) {
|
|
|
148
148
|
}
|
|
149
149
|
if (cmd === "enforce-branches" ||
|
|
150
150
|
cmd === "allow-direct-commits" ||
|
|
151
|
+
cmd === "allow-bot-merge" ||
|
|
151
152
|
cmd === "enable-value-feedback" ||
|
|
152
153
|
cmd === "clear-value-feedback" ||
|
|
153
154
|
cmd === "disable-directive" ||
|
|
@@ -157,13 +158,15 @@ export function parseArgs(argv) {
|
|
|
157
158
|
? policyColonInvocation("enforce-branches")
|
|
158
159
|
: cmd === "allow-direct-commits"
|
|
159
160
|
? policyColonInvocation("allow-direct-commits")
|
|
160
|
-
: cmd === "
|
|
161
|
-
? policyColonInvocation("
|
|
162
|
-
: cmd === "
|
|
163
|
-
? policyColonInvocation("
|
|
164
|
-
: cmd === "
|
|
165
|
-
? policyColonInvocation("
|
|
166
|
-
:
|
|
161
|
+
: cmd === "allow-bot-merge"
|
|
162
|
+
? policyColonInvocation("allow-bot-merge")
|
|
163
|
+
: cmd === "enable-value-feedback"
|
|
164
|
+
? policyColonInvocation("enable-value-feedback")
|
|
165
|
+
: cmd === "clear-value-feedback"
|
|
166
|
+
? policyColonInvocation("clear-value-feedback")
|
|
167
|
+
: cmd === "disable-directive"
|
|
168
|
+
? policyColonInvocation("disable-directive")
|
|
169
|
+
: policyColonInvocation("enable-directive");
|
|
167
170
|
let note = "";
|
|
168
171
|
let projectRoot = ".";
|
|
169
172
|
for (let i = 1; i < argv.length; i += 1) {
|
|
@@ -381,6 +384,48 @@ function runEnableDirective(args) {
|
|
|
381
384
|
process.stdout.write("Directive opt-out cleared. Run `directive init` or `directive update` to ensure install.\n");
|
|
382
385
|
return 0;
|
|
383
386
|
}
|
|
387
|
+
/** Allow agent/bot merge by writing requireHumanMerge=false (#1193). */
|
|
388
|
+
function runAllowBotMerge(args) {
|
|
389
|
+
const projectRoot = pathResolve(args.projectRoot);
|
|
390
|
+
if (!args.confirm) {
|
|
391
|
+
process.stdout.write(`${ALLOW_BOT_MERGE_CAPABILITY_COST}\n\n`);
|
|
392
|
+
process.stdout.write(`Re-run with --confirm to apply: ${policyColonInvocation("allow-bot-merge", " -- --confirm")}\n`);
|
|
393
|
+
return 1;
|
|
394
|
+
}
|
|
395
|
+
try {
|
|
396
|
+
const { changed, auditEntry } = setRequireHumanMerge(projectRoot, {
|
|
397
|
+
requireHumanMerge: false,
|
|
398
|
+
actor: args.actor,
|
|
399
|
+
note: args.note,
|
|
400
|
+
});
|
|
401
|
+
process.stdout.write(`\u2713 plan.policy.requireHumanMerge=false (human merge gate OFF; agent may merge).\n`);
|
|
402
|
+
if (changed) {
|
|
403
|
+
process.stdout.write(` audit: meta/policy-changes.log :: ${auditEntry}\n`);
|
|
404
|
+
}
|
|
405
|
+
else {
|
|
406
|
+
process.stdout.write(" no-op: value already matched (audit entry still appended for trail).\n");
|
|
407
|
+
}
|
|
408
|
+
const line = humanMergeDisclosureLine(resolveHumanMergePolicy(projectRoot));
|
|
409
|
+
if (line !== null) {
|
|
410
|
+
process.stdout.write(`${line}\n`);
|
|
411
|
+
}
|
|
412
|
+
else {
|
|
413
|
+
process.stdout.write("[deft policy] Human merge gate is OFF; agent may merge when other gates allow.\n");
|
|
414
|
+
}
|
|
415
|
+
return 0;
|
|
416
|
+
}
|
|
417
|
+
catch (err) {
|
|
418
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
419
|
+
if (message.includes("PROJECT-DEFINITION not found")) {
|
|
420
|
+
process.stderr.write(`\u274c ${message}\n`);
|
|
421
|
+
const pdRel = relative(projectRoot, projectDefinitionPath(projectRoot));
|
|
422
|
+
process.stderr.write(` Recovery: run \`task setup\` to generate ${pdRel}.\n`);
|
|
423
|
+
return 2;
|
|
424
|
+
}
|
|
425
|
+
process.stderr.write(`\u274c Config error: ${message}\n`);
|
|
426
|
+
return 2;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
384
429
|
/** Run the policy CLI; returns process exit code. */
|
|
385
430
|
export function run(argv) {
|
|
386
431
|
const args = parseArgs(argv);
|
|
@@ -402,6 +447,9 @@ export function run(argv) {
|
|
|
402
447
|
if (args.cmd === "enforce-branches" || args.cmd === "allow-direct-commits") {
|
|
403
448
|
return runSet(args);
|
|
404
449
|
}
|
|
450
|
+
if (args.cmd === "allow-bot-merge") {
|
|
451
|
+
return runAllowBotMerge(args);
|
|
452
|
+
}
|
|
405
453
|
if (args.cmd === "enable-value-feedback") {
|
|
406
454
|
return runEnableValueFeedback(args);
|
|
407
455
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { cmdPrFinishLoop } from "@deftai/directive-core/dist/finish-loop/main.js";
|
|
4
|
+
export function run(argv) {
|
|
5
|
+
return cmdPrFinishLoop(argv);
|
|
6
|
+
}
|
|
7
|
+
if (process.argv[1] !== undefined && fileURLToPath(import.meta.url) === process.argv[1]) {
|
|
8
|
+
process.exit(run(process.argv.slice(2)));
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=pr-finish-loop.js.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
interface ParsedArgs {
|
|
3
|
+
projectRoot: string;
|
|
4
|
+
enforce: boolean;
|
|
5
|
+
help?: boolean;
|
|
6
|
+
error?: string;
|
|
7
|
+
}
|
|
8
|
+
/** Parse verify-contained-writes CLI args (#2951). */
|
|
9
|
+
export declare function parseArgs(argv: string[]): ParsedArgs;
|
|
10
|
+
/** Run the gate and return the process exit code. */
|
|
11
|
+
export declare function run(argv: string[]): number;
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=verify-contained-writes.d.ts.map
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* CLI for task verify:contained-writes (#2951 Phase 1).
|
|
4
|
+
* Fail-open by default; pass --enforce for fail-closed (later phases).
|
|
5
|
+
*/
|
|
6
|
+
import { resolve } from "node:path";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
import { evaluateContainedWrites } from "@deftai/directive-core/verify-source";
|
|
9
|
+
/** Parse verify-contained-writes CLI args (#2951). */
|
|
10
|
+
export function parseArgs(argv) {
|
|
11
|
+
const parsed = { projectRoot: ".", enforce: false };
|
|
12
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
13
|
+
const arg = argv[i];
|
|
14
|
+
if (arg === "--project-root") {
|
|
15
|
+
const value = argv[i + 1];
|
|
16
|
+
if (value === undefined) {
|
|
17
|
+
return { ...parsed, error: "argument --project-root: expected one argument" };
|
|
18
|
+
}
|
|
19
|
+
parsed.projectRoot = value;
|
|
20
|
+
i += 1;
|
|
21
|
+
}
|
|
22
|
+
else if (arg?.startsWith("--project-root=")) {
|
|
23
|
+
parsed.projectRoot = arg.slice("--project-root=".length);
|
|
24
|
+
}
|
|
25
|
+
else if (arg === "--enforce") {
|
|
26
|
+
parsed.enforce = true;
|
|
27
|
+
}
|
|
28
|
+
else if (arg === "--help" || arg === "-h") {
|
|
29
|
+
return { ...parsed, help: true };
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
return { ...parsed, error: `unrecognized argument: ${arg}` };
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return parsed;
|
|
36
|
+
}
|
|
37
|
+
const HELP_TEXT = "Usage: verify-contained-writes [--project-root <path>] [--enforce]\n" +
|
|
38
|
+
" Inventory raw write sinks outside the allowlist (#2951).\n" +
|
|
39
|
+
" Default: fail-open (exit 0 with advisory report).\n" +
|
|
40
|
+
" --enforce: fail closed (exit 1) when non-allowlisted sinks remain.\n";
|
|
41
|
+
/** Run the gate and return the process exit code. */
|
|
42
|
+
export function run(argv) {
|
|
43
|
+
const args = parseArgs(argv);
|
|
44
|
+
if (args.help === true) {
|
|
45
|
+
process.stdout.write(HELP_TEXT);
|
|
46
|
+
return 0;
|
|
47
|
+
}
|
|
48
|
+
if (args.error !== undefined) {
|
|
49
|
+
process.stderr.write(`verify_contained_writes: ${args.error}\n`);
|
|
50
|
+
return 2;
|
|
51
|
+
}
|
|
52
|
+
const result = evaluateContainedWrites({
|
|
53
|
+
projectRoot: resolve(args.projectRoot),
|
|
54
|
+
enforce: args.enforce,
|
|
55
|
+
});
|
|
56
|
+
if (result.stream === "stdout") {
|
|
57
|
+
process.stdout.write(`${result.message}\n`);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
process.stderr.write(`${result.message}\n`);
|
|
61
|
+
}
|
|
62
|
+
return result.code;
|
|
63
|
+
}
|
|
64
|
+
if (process.argv[1] !== undefined && fileURLToPath(import.meta.url) === process.argv[1]) {
|
|
65
|
+
process.exit(run(process.argv.slice(2)));
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=verify-contained-writes.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deftai/directive",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.89.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.89.0",
|
|
38
|
+
"@deftai/directive-content": "^0.89.0"
|
|
39
39
|
},
|
|
40
40
|
"scripts": {
|
|
41
41
|
"build": "tsc -b"
|