@deftai/directive 0.87.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 +15 -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 +42 -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 +103 -6
- 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 +5 -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 = {
|
|
@@ -70,10 +71,22 @@ export const SUBCOMMAND_ROUTES = {
|
|
|
70
71
|
"cache:prune": ["cache", "prune"],
|
|
71
72
|
"cache:clear": ["cache", "clear"],
|
|
72
73
|
"policy:show": ["policy", "show"],
|
|
74
|
+
"policy:disable-directive": ["policy", "disable-directive"],
|
|
75
|
+
"policy:enable-directive": ["policy", "enable-directive"],
|
|
73
76
|
"policy:enforce-branches": ["policy", "enforce-branches"],
|
|
74
77
|
"policy:allow-direct-commits": ["policy", "allow-direct-commits"],
|
|
78
|
+
"policy:allow-bot-merge": ["policy", "allow-bot-merge"],
|
|
75
79
|
"policy:enable-value-feedback": ["policy", "enable-value-feedback"],
|
|
76
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"],
|
|
77
90
|
"product-signal:status": ["product-signal", "status"],
|
|
78
91
|
"product-signal:enable": ["product-signal", "enable"],
|
|
79
92
|
"product-signal:consent": ["product-signal", "consent"],
|
|
@@ -87,9 +100,11 @@ export const SUBCOMMAND_ROUTES = {
|
|
|
87
100
|
"github-body:issue-create": ["github-body", "issue-create"],
|
|
88
101
|
"github-body:issue-edit": ["github-body", "issue-edit"],
|
|
89
102
|
"github-body:issue-fetch": ["github-body", "issue-fetch"],
|
|
103
|
+
"github-body:issue-lint": ["github-body", "issue-lint"],
|
|
90
104
|
"github-body:comment-create": ["github-body", "comment-create"],
|
|
91
105
|
"github-body:comment-edit": ["github-body", "comment-edit"],
|
|
92
106
|
"github-body:pr-edit": ["github-body", "pr-edit"],
|
|
107
|
+
"github-body:pr-lint": ["github-body", "pr-lint"],
|
|
93
108
|
"plan-sequence:set": ["plan-sequence", "set"],
|
|
94
109
|
"plan-sequence:current": ["plan-sequence", "current"],
|
|
95
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,10 +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",
|
|
204
|
+
"policy:disable-directive": "disable-directive",
|
|
205
|
+
"policy:enable-directive": "enable-directive",
|
|
198
206
|
};
|
|
199
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"]));
|
|
200
225
|
/** Colon aliases for plan-sequence subcommands (#2402). */
|
|
201
226
|
export const PLAN_SEQUENCE_ALIAS_SUBCOMMANDS = {
|
|
202
227
|
"plan-sequence:set": "set",
|
|
@@ -249,6 +274,7 @@ export const VERB_ALIASES = {
|
|
|
249
274
|
"verify:rule-ownership": "rule-ownership-lint",
|
|
250
275
|
"rule:ownership-lint": "rule-ownership-lint",
|
|
251
276
|
"verify:biome-config": "verify-biome-config",
|
|
277
|
+
"verify:contained-writes": "verify-contained-writes",
|
|
252
278
|
"verify:content-manifest": "verify-content-manifest",
|
|
253
279
|
"verify:skill-external-fetch-gate": "verify-skill-external-fetch-gate",
|
|
254
280
|
"verify:contract-drift": "verify-contract-drift",
|
|
@@ -273,6 +299,8 @@ export const VERB_ALIASES = {
|
|
|
273
299
|
"triage:scope": "triage-scope",
|
|
274
300
|
...TRIAGE_ACTION_COLON_ALIASES,
|
|
275
301
|
...POLICY_ACTION_COLON_ALIASES,
|
|
302
|
+
...AUTHZ_ACTION_COLON_ALIASES,
|
|
303
|
+
...ESCALATION_ACTION_COLON_ALIASES,
|
|
276
304
|
...PRODUCT_SIGNAL_COLON_ALIASES,
|
|
277
305
|
"agents:refresh": "agents-refresh",
|
|
278
306
|
"migrate:preflight": "migrate-preflight",
|
|
@@ -293,6 +321,8 @@ export const VERB_ALIASES = {
|
|
|
293
321
|
"docs:rule-map": "rule-map",
|
|
294
322
|
"project:export-spec": "export-spec",
|
|
295
323
|
"pr:watch": "pr-watch",
|
|
324
|
+
"pr:finish-loop": "pr-finish-loop",
|
|
325
|
+
"directive:finish-loop": "directive-finish-loop",
|
|
296
326
|
doctor: "doctor",
|
|
297
327
|
"eval:health": "eval-health",
|
|
298
328
|
"feedback:file": "feedback-file",
|
|
@@ -309,6 +339,7 @@ const SUBDIR_CLI_STEMS = {
|
|
|
309
339
|
"verify-stubs": "verify-source-cli/verify-stubs",
|
|
310
340
|
"rule-ownership-lint": "verify-source-cli/rule-ownership-lint",
|
|
311
341
|
"verify-biome-config": "verify-source-cli/verify-biome-config",
|
|
342
|
+
"verify-contained-writes": "verify-source-cli/verify-contained-writes",
|
|
312
343
|
"verify-content-manifest": "verify-source-cli/verify-content-manifest",
|
|
313
344
|
"verify-skill-external-fetch-gate": "verify-source-cli/verify-skill-external-fetch-gate",
|
|
314
345
|
"verify-contract-drift": "verify-source-cli/verify-contract-drift",
|
|
@@ -2596,6 +2627,8 @@ export async function dispatch(argv, io = defaultIo()) {
|
|
|
2596
2627
|
const handler = await loadHandler(canonical, io);
|
|
2597
2628
|
const triageSubcommand = verb !== undefined ? TRIAGE_ACTION_ALIAS_SUBCOMMANDS[verb] : undefined;
|
|
2598
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;
|
|
2599
2632
|
const planSequenceSubcommand = verb !== undefined ? PLAN_SEQUENCE_ALIAS_SUBCOMMANDS[verb] : undefined;
|
|
2600
2633
|
const productSignalSubcommand = verb !== undefined ? PRODUCT_SIGNAL_ALIAS_SUBCOMMANDS[verb] : undefined;
|
|
2601
2634
|
const handlerArgv = canonical === "framework-commands" && verb !== undefined && verb !== canonical
|
|
@@ -2604,11 +2637,15 @@ export async function dispatch(argv, io = defaultIo()) {
|
|
|
2604
2637
|
? [triageSubcommand, ...rest]
|
|
2605
2638
|
: policySubcommand !== undefined && canonical === "policy"
|
|
2606
2639
|
? [policySubcommand, ...rest]
|
|
2607
|
-
:
|
|
2608
|
-
? [
|
|
2609
|
-
:
|
|
2610
|
-
? [
|
|
2611
|
-
:
|
|
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;
|
|
2612
2649
|
return await invokeHandler(handler, handlerArgv);
|
|
2613
2650
|
}
|
|
2614
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" | "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, describeShadowedPlanExtension, detectShadowedPlanExtensions, disclosureLine, enableValueFeedback, FIELD_VALUE_FEEDBACK, FIELD_VALUE_FEEDBACK_CLI_ALIAS, formatValueFeedbackStatusLine, inspectAllPolicies, inspectOnePolicy, loadProjectDefinition, policyColonInvocation, projectDefinitionPath, pythonListRepr, pythonStringRepr, registeredPolicyNames, 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|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,16 +148,25 @@ 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
|
-
cmd === "clear-value-feedback"
|
|
153
|
+
cmd === "clear-value-feedback" ||
|
|
154
|
+
cmd === "disable-directive" ||
|
|
155
|
+
cmd === "enable-directive") {
|
|
153
156
|
let confirm = false;
|
|
154
157
|
let actor = cmd === "enforce-branches"
|
|
155
158
|
? policyColonInvocation("enforce-branches")
|
|
156
159
|
: cmd === "allow-direct-commits"
|
|
157
160
|
? policyColonInvocation("allow-direct-commits")
|
|
158
|
-
: cmd === "
|
|
159
|
-
? policyColonInvocation("
|
|
160
|
-
:
|
|
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");
|
|
161
170
|
let note = "";
|
|
162
171
|
let projectRoot = ".";
|
|
163
172
|
for (let i = 1; i < argv.length; i += 1) {
|
|
@@ -338,6 +347,85 @@ function runClearValueFeedback(args) {
|
|
|
338
347
|
process.stdout.write(result.stdout);
|
|
339
348
|
return result.exitCode;
|
|
340
349
|
}
|
|
350
|
+
/** Create root `.no-deft-directive` opt-out flag (#2926). Does not delete deposits. */
|
|
351
|
+
function runDisableDirective(args) {
|
|
352
|
+
const projectRoot = pathResolve(args.projectRoot);
|
|
353
|
+
const before = detectNoDeftDirective(projectRoot);
|
|
354
|
+
if (before.present) {
|
|
355
|
+
process.stdout.write(`${NO_DEFT_DIRECTIVE_DISABLED_MESSAGE} (already present)\n`);
|
|
356
|
+
if (before.inconsistent) {
|
|
357
|
+
process.stderr.write(`${NO_DEFT_DIRECTIVE_INCONSISTENT_MESSAGE}\n`);
|
|
358
|
+
return 1;
|
|
359
|
+
}
|
|
360
|
+
return 0;
|
|
361
|
+
}
|
|
362
|
+
const path = createNoDeftDirectiveFlag(projectRoot, {
|
|
363
|
+
rationale: args.note.trim().length > 0 ? args.note.trim() : undefined,
|
|
364
|
+
});
|
|
365
|
+
process.stdout.write(`Created ${NO_DEFT_DIRECTIVE_FLAG_NAME} at ${path}\n`);
|
|
366
|
+
process.stdout.write(`${NO_DEFT_DIRECTIVE_DISABLED_MESSAGE}\n`);
|
|
367
|
+
const after = detectNoDeftDirective(projectRoot);
|
|
368
|
+
if (after.inconsistent) {
|
|
369
|
+
process.stderr.write(`${NO_DEFT_DIRECTIVE_INCONSISTENT_MESSAGE}\n`);
|
|
370
|
+
return 1;
|
|
371
|
+
}
|
|
372
|
+
return 0;
|
|
373
|
+
}
|
|
374
|
+
/** Remove root `.no-deft-directive` so Directive may be installed/used again (#2926). */
|
|
375
|
+
function runEnableDirective(args) {
|
|
376
|
+
const projectRoot = pathResolve(args.projectRoot);
|
|
377
|
+
const removed = removeNoDeftDirectiveFlag(projectRoot);
|
|
378
|
+
if (removed) {
|
|
379
|
+
process.stdout.write(`Removed ${NO_DEFT_DIRECTIVE_FLAG_NAME}\n`);
|
|
380
|
+
}
|
|
381
|
+
else {
|
|
382
|
+
process.stdout.write(`${NO_DEFT_DIRECTIVE_FLAG_NAME} was not present\n`);
|
|
383
|
+
}
|
|
384
|
+
process.stdout.write("Directive opt-out cleared. Run `directive init` or `directive update` to ensure install.\n");
|
|
385
|
+
return 0;
|
|
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
|
+
}
|
|
341
429
|
/** Run the policy CLI; returns process exit code. */
|
|
342
430
|
export function run(argv) {
|
|
343
431
|
const args = parseArgs(argv);
|
|
@@ -359,12 +447,21 @@ export function run(argv) {
|
|
|
359
447
|
if (args.cmd === "enforce-branches" || args.cmd === "allow-direct-commits") {
|
|
360
448
|
return runSet(args);
|
|
361
449
|
}
|
|
450
|
+
if (args.cmd === "allow-bot-merge") {
|
|
451
|
+
return runAllowBotMerge(args);
|
|
452
|
+
}
|
|
362
453
|
if (args.cmd === "enable-value-feedback") {
|
|
363
454
|
return runEnableValueFeedback(args);
|
|
364
455
|
}
|
|
365
456
|
if (args.cmd === "clear-value-feedback") {
|
|
366
457
|
return runClearValueFeedback(args);
|
|
367
458
|
}
|
|
459
|
+
if (args.cmd === "disable-directive") {
|
|
460
|
+
return runDisableDirective(args);
|
|
461
|
+
}
|
|
462
|
+
if (args.cmd === "enable-directive") {
|
|
463
|
+
return runEnableDirective(args);
|
|
464
|
+
}
|
|
368
465
|
return 2;
|
|
369
466
|
}
|
|
370
467
|
if (process.argv[1] !== undefined && fileURLToPath(import.meta.url) === process.argv[1]) {
|
|
@@ -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,7 +1,9 @@
|
|
|
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
|
+
"license": "MIT",
|
|
6
|
+
"homepage": "https://deftai.github.io/directive/",
|
|
5
7
|
"type": "module",
|
|
6
8
|
"main": "./dist/index.js",
|
|
7
9
|
"types": "./dist/index.d.ts",
|
|
@@ -32,8 +34,8 @@
|
|
|
32
34
|
"provenance": true
|
|
33
35
|
},
|
|
34
36
|
"dependencies": {
|
|
35
|
-
"@deftai/directive-core": "^0.
|
|
36
|
-
"@deftai/directive-content": "^0.
|
|
37
|
+
"@deftai/directive-core": "^0.89.0",
|
|
38
|
+
"@deftai/directive-content": "^0.89.0"
|
|
37
39
|
},
|
|
38
40
|
"scripts": {
|
|
39
41
|
"build": "tsc -b"
|