@deftai/directive 0.105.0 → 0.107.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.js +1 -1
- package/dist/cli-router/route-argv.js +4 -0
- package/dist/dispatch.d.ts +7 -1
- package/dist/dispatch.js +58 -20
- package/dist/human-presence-mint.d.ts +11 -2
- package/dist/human-presence-mint.js +17 -5
- package/dist/occupancy-release.d.ts +7 -0
- package/dist/occupancy-release.js +38 -0
- package/dist/plan-sequence.d.ts +10 -0
- package/dist/plan-sequence.js +3 -1
- package/dist/policy.d.ts +3 -2
- package/dist/policy.js +52 -7
- package/dist/preflight-cache.d.ts +2 -0
- package/dist/preflight-cache.js +7 -0
- package/dist/triage-actions.js +4 -3
- package/dist/triage-evaluate.d.ts +14 -0
- package/dist/triage-evaluate.js +153 -0
- package/dist/triage-queue.js +4 -1
- package/dist/verify-completed-tracked.js +34 -1
- package/dist/verify-completed-write-guard.d.ts +13 -0
- package/dist/verify-completed-write-guard.js +72 -0
- package/dist/verify-forward-coverage.d.ts +4 -0
- package/dist/verify-forward-coverage.js +77 -11
- package/dist/verify-lifecycle-visible.d.ts +13 -0
- package/dist/verify-lifecycle-visible.js +68 -0
- package/package.json +3 -3
package/dist/authz.js
CHANGED
|
@@ -225,7 +225,7 @@ function helpText() {
|
|
|
225
225
|
" (grant / uat-start / uat-suspend / revoke) — no TTY, --confirm, or phrase path.",
|
|
226
226
|
" Self-approval under UAT is impossible by construction. Mint grants BEFORE uat-start.",
|
|
227
227
|
"Outside UAT, mutating verbs require multi-factor human presence (#3110):",
|
|
228
|
-
" - Interactive TTY (stdin+stdout) + controlling terminal (/dev/tty
|
|
228
|
+
" - Interactive TTY (stdin+stdout) + controlling terminal (/dev/tty|\\\\.\\CONIN$)",
|
|
229
229
|
" - Explicit --confirm (argv flag alone never enough)",
|
|
230
230
|
" - Typed phrase 'mint' on the controlling TTY (PTY+--confirm alone never enough)",
|
|
231
231
|
" - Known agent/CI env markers always refuse (fail-closed).",
|
|
@@ -83,6 +83,7 @@ export const SUBCOMMAND_ROUTES = {
|
|
|
83
83
|
"policy:allow-direct-commits": ["policy", "allow-direct-commits"],
|
|
84
84
|
"policy:allow-bot-merge": ["policy", "allow-bot-merge"],
|
|
85
85
|
"policy:enable-value-feedback": ["policy", "enable-value-feedback"],
|
|
86
|
+
"policy:disable-host-hooks": ["policy", "disable-host-hooks"],
|
|
86
87
|
"policy:set-ceremony-dial": ["policy", "set-ceremony-dial"],
|
|
87
88
|
"policy:clear-value-feedback": ["policy", "clear-value-feedback"],
|
|
88
89
|
"authz:show": ["authz", "show"],
|
|
@@ -196,6 +197,9 @@ function routeNamespaceVerb(ns, verb, rest) {
|
|
|
196
197
|
return { kind: "dispatch", argv: [verifyStem, ...rest] };
|
|
197
198
|
}
|
|
198
199
|
}
|
|
200
|
+
if (ns === "scm" && verb === "issue:design-critique-chip") {
|
|
201
|
+
return { kind: "dispatch", argv: ["scm", "issue", "design-critique-chip", ...rest] };
|
|
202
|
+
}
|
|
199
203
|
if (ns === "issue" && verb === "ingest") {
|
|
200
204
|
return { kind: "dispatch", argv: ["issue-ingest", ...rest] };
|
|
201
205
|
}
|
package/dist/dispatch.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ export interface DispatchIo {
|
|
|
10
10
|
writeErr: (text: string) => void;
|
|
11
11
|
}
|
|
12
12
|
/** CLI modules in packages/cli/src (excluding parity harnesses and bin/index). */
|
|
13
|
-
export declare const CLI_MODULE_VERBS: readonly ["agents-refresh", "cache", "check", "capacity-backfill", "capacity-show", "codebase-default-extractor", "codebase-map", "codebase-map-fresh", "codebase-projection-registry", "codebase-provider", "doctor", "install-upgrade", "install-uninstall", "migrate-preflight", "migrate-xbrief", "migrate-category-b", "framework-check-updates", "hook-dispatch", "umbrella-current-shape", "changelog-check", "change-init", "commit-lint", "coverage-hotspots", "policy", "authz", "escalation-cli", "pr-closing-keywords", "pr-merge-readiness", "pr-monitor", "pr-protected-issues", "pr-wait-mergeable", "pr-watch", "pr-finish-loop", "directive-finish-loop", "preflight-cache", "preflight-gh", "probe-session", "release", "release-e2e", "release-publish", "release-rollback", "scope-lifecycle", "scope-record-approved-scope", "scm-sync-default", "lifecycle-event", "lifecycle-stats", "session-start", "session-ready", "occupancy-steal", "plan-sequence", "slice", "subagent-monitor", "toolchain-check", "triage-actions", "triage-bootstrap", "triage-bulk", "triage-classify", "triage-help", "triage-queue", "triage-reconcile", "triage-refresh", "triage-scope", "triage-scope-drift", "triage-smoketest", "triage-subscribe", "triage-summary", "triage-welcome", "ts-check-lane", "vbrief-activate", "vbrief-build", "vbrief-preflight", "vbrief-reconcile", "vbrief-validate", "vbrief-validation", "xbrief-create", "xbrief-verify", "verify-branch", "verify-encoding", "verify-forward-coverage", "verify-test-boundary", "verify-scope-provenance", "verify-consumer-check-contract", "verify-hooks-installed", "verify-investigation", "verify-judgment-gates", "verify-no-task-runtime", "validate-links", "validate-strategy-output", "verify-biome-config", "verify-bridge-drift", "verify-capacity", "verify-contained-writes", "verify-telemetry-coverage", "verify-content-manifest", "verify-skill-external-fetch-gate", "verify-contract-drift", "verify-cursor-tier1", "verify-openclaw-tier1", "verify-go-freeze", "verify-scm-boundary", "verify-session-ritual", "verify-plan-sequence", "verify-stubs", "verify-xbrief-drift", "rule-ownership-lint", "verify-story-ready", "verify-review-monitor", "verify-l4-owner", "verify-subagent-alive", "review-monitor-register", "review-monitor-release", "verify-tools", "verify-wip-cap", "verify-orphan-active", "verify-completed-tracked", "verify-literal-ac", "verify-ac", "verify-agents-md-budget", "verify-agents-md-advisory", "verify-eval-health-relocation", "verify-eval-triggers-relocation", "eval-health", "eval-run", "eval-report", "eval-triggers"];
|
|
13
|
+
export declare const CLI_MODULE_VERBS: readonly ["agents-refresh", "cache", "check", "capacity-backfill", "capacity-show", "codebase-default-extractor", "codebase-map", "codebase-map-fresh", "codebase-projection-registry", "codebase-provider", "doctor", "install-upgrade", "install-uninstall", "migrate-preflight", "migrate-xbrief", "migrate-category-b", "framework-check-updates", "hook-dispatch", "umbrella-current-shape", "changelog-check", "change-init", "commit-lint", "coverage-hotspots", "policy", "authz", "escalation-cli", "pr-closing-keywords", "pr-merge-readiness", "pr-monitor", "pr-protected-issues", "pr-wait-mergeable", "pr-watch", "pr-finish-loop", "directive-finish-loop", "preflight-cache", "preflight-gh", "probe-session", "release", "release-e2e", "release-publish", "release-rollback", "scope-lifecycle", "scope-record-approved-scope", "scm-sync-default", "lifecycle-event", "lifecycle-stats", "session-start", "session-ready", "occupancy-steal", "occupancy-release", "plan-sequence", "slice", "subagent-monitor", "toolchain-check", "triage-actions", "triage-bootstrap", "triage-bulk", "triage-classify", "triage-evaluate", "triage-help", "triage-queue", "triage-reconcile", "triage-refresh", "triage-scope", "triage-scope-drift", "triage-smoketest", "triage-subscribe", "triage-summary", "triage-welcome", "ts-check-lane", "vbrief-activate", "vbrief-build", "vbrief-preflight", "vbrief-reconcile", "vbrief-validate", "vbrief-validation", "xbrief-create", "xbrief-verify", "verify-branch", "verify-encoding", "verify-forward-coverage", "verify-test-boundary", "verify-scope-provenance", "verify-consumer-check-contract", "verify-hooks-installed", "verify-investigation", "verify-judgment-gates", "verify-no-task-runtime", "validate-links", "validate-strategy-output", "verify-biome-config", "verify-bridge-drift", "verify-capacity", "verify-contained-writes", "verify-telemetry-coverage", "verify-content-manifest", "verify-skill-external-fetch-gate", "verify-contract-drift", "verify-cursor-tier1", "verify-openclaw-tier1", "verify-go-freeze", "verify-scm-boundary", "verify-session-ritual", "verify-plan-sequence", "verify-stubs", "verify-xbrief-drift", "rule-ownership-lint", "verify-story-ready", "verify-review-monitor", "verify-l4-owner", "verify-subagent-alive", "review-monitor-register", "review-monitor-release", "verify-tools", "verify-wip-cap", "verify-orphan-active", "verify-lifecycle-visible", "verify-completed-tracked", "verify-completed-write-guard", "verify-literal-ac", "verify-ac", "verify-agents-md-budget", "verify-agents-md-advisory", "verify-eval-health-relocation", "verify-eval-triggers-relocation", "eval-health", "eval-run", "eval-report", "eval-triggers"];
|
|
14
14
|
/** Core-only CLI entrypoints without a packages/cli wrapper. */
|
|
15
15
|
export declare const CORE_MODULE_VERBS: readonly ["scm", "scm-readiness", "github-auth-modes", "github-body", "issue-emit", "issue-ingest", "issue-sync-from-xbrief", "reconcile-issues", "swarm-launch", "swarm-complete-cohort", "swarm-finalize-cohort", "swarm-pre-dispatch", "swarm-readiness", "swarm-routing-verify", "swarm-routing-set", "swarm-verify-review-clean", "swarm-worktrees", "framework-commands", "pack-render", "packs-slice", "prd-render", "export-spec", "project-render", "roadmap-render", "rule-map", "spec-render", "spec-validate", "code-structure-validate", "pack-migrate-skills", "pack-migrate-rules", "pack-migrate-strategies", "pack-migrate-patterns", "pack-migrate-swarm-spec", "policy-set", "setup-ghx", "scope-undo", "scope-demote", "scope-decompose", "changelog-resolve-unreleased", "architecture-preflight-sor", "feedback-file", "value-readback", "product-signal", "freshness-report", "decision-write", "decision-list"];
|
|
16
16
|
/** Colon aliases for triage-actions (mirrors cli-router SUBCOMMAND_ROUTES). */
|
|
@@ -177,6 +177,12 @@ export declare function printCommandsList(io?: DispatchIo): void;
|
|
|
177
177
|
* (#2172). Preserves the init/update/doctor first-run guidance from #2273.
|
|
178
178
|
*/
|
|
179
179
|
export declare function printHelp(io?: DispatchIo): void;
|
|
180
|
+
/**
|
|
181
|
+
* Unknown-colon-verb remediation (#3439 / #2652).
|
|
182
|
+
* Agent-facing spelling is `deft <verb>` (works on consumer CLI and source).
|
|
183
|
+
* Bare `task <verb>` is not consumer-runnable on include-only Taskfiles.
|
|
184
|
+
*/
|
|
185
|
+
export declare function unknownColonVerbHint(verb: string): string;
|
|
180
186
|
/** Dispatch argv to a registered verb; returns the handler exit code. */
|
|
181
187
|
export declare function dispatch(argv: string[], io?: DispatchIo): Promise<number>;
|
|
182
188
|
/** Test seam: reset lazy handler cache between cases. */
|
package/dist/dispatch.js
CHANGED
|
@@ -10,7 +10,7 @@ import { basename, dirname, isAbsolute, join, relative, resolve } from "node:pat
|
|
|
10
10
|
import { engineInfo, userConfig } from "@deftai/directive-core";
|
|
11
11
|
import { assertWriteTargetSafe } from "@deftai/directive-core/dist/fs/projection-containment.js";
|
|
12
12
|
import { parseInitArgv, runInitDepositCli } from "@deftai/directive-core/init-deposit";
|
|
13
|
-
import { appendAuditLog, disclosureLine, migrateLegacyPolicyKey, PLAN_POLICY_KEY, policyColonInvocation, policySetInvocation, projectDefinitionPath, resolvePolicy, resolveWipCap, setPolicy, } from "@deftai/directive-core/policy";
|
|
13
|
+
import { appendAuditLog, disclosureLine, migrateLegacyPolicyKey, PLAN_POLICY_KEY, POLICY_AUDIT_NOOP_STDOUT, policyColonInvocation, policySetInvocation, projectDefinitionPath, resolvePolicy, resolveWipCap, setPolicy, stampChangedToken, } from "@deftai/directive-core/policy";
|
|
14
14
|
import { defaultWhich } from "@deftai/directive-core/scm";
|
|
15
15
|
import { KNOWN_SUBAGENT_BACKEND_IDS, probeSubagentBackends, resolveSwarmSubagentBackend, } from "@deftai/directive-core/swarm";
|
|
16
16
|
import { atomicWriteProjectDefinition } from "@deftai/directive-core/vbrief-build";
|
|
@@ -75,6 +75,7 @@ export const CLI_MODULE_VERBS = [
|
|
|
75
75
|
"session-start",
|
|
76
76
|
"session-ready",
|
|
77
77
|
"occupancy-steal",
|
|
78
|
+
"occupancy-release",
|
|
78
79
|
"plan-sequence",
|
|
79
80
|
"slice",
|
|
80
81
|
"subagent-monitor",
|
|
@@ -83,6 +84,7 @@ export const CLI_MODULE_VERBS = [
|
|
|
83
84
|
"triage-bootstrap",
|
|
84
85
|
"triage-bulk",
|
|
85
86
|
"triage-classify",
|
|
87
|
+
"triage-evaluate",
|
|
86
88
|
"triage-help",
|
|
87
89
|
"triage-queue",
|
|
88
90
|
"triage-reconcile",
|
|
@@ -140,7 +142,9 @@ export const CLI_MODULE_VERBS = [
|
|
|
140
142
|
"verify-tools",
|
|
141
143
|
"verify-wip-cap",
|
|
142
144
|
"verify-orphan-active",
|
|
145
|
+
"verify-lifecycle-visible",
|
|
143
146
|
"verify-completed-tracked",
|
|
147
|
+
"verify-completed-write-guard",
|
|
144
148
|
"verify-literal-ac",
|
|
145
149
|
"verify-ac",
|
|
146
150
|
"verify-agents-md-budget",
|
|
@@ -220,6 +224,7 @@ export const POLICY_ACTION_ALIAS_SUBCOMMANDS = {
|
|
|
220
224
|
"policy:allow-direct-commits": "allow-direct-commits",
|
|
221
225
|
"policy:allow-bot-merge": "allow-bot-merge",
|
|
222
226
|
"policy:enable-value-feedback": "enable-value-feedback",
|
|
227
|
+
"policy:disable-host-hooks": "disable-host-hooks",
|
|
223
228
|
"policy:set-ceremony-dial": "set-ceremony-dial",
|
|
224
229
|
"policy:clear-value-feedback": "clear-value-feedback",
|
|
225
230
|
"policy:disable-directive": "disable-directive",
|
|
@@ -282,7 +287,9 @@ export const VERB_ALIASES = {
|
|
|
282
287
|
"verify:vbrief-conformance": "vbrief-validate",
|
|
283
288
|
"verify:wip-cap": "verify-wip-cap",
|
|
284
289
|
"verify:orphan-active": "verify-orphan-active",
|
|
290
|
+
"verify:lifecycle-visible": "verify-lifecycle-visible",
|
|
285
291
|
"verify:completed-tracked": "verify-completed-tracked",
|
|
292
|
+
"verify:completed-write-guard": "verify-completed-write-guard",
|
|
286
293
|
"verify:literal-ac": "verify-literal-ac",
|
|
287
294
|
"verify:literal-acceptance": "verify-literal-ac",
|
|
288
295
|
"verify:ac": "verify-ac",
|
|
@@ -343,6 +350,7 @@ export const VERB_ALIASES = {
|
|
|
343
350
|
"triage:bootstrap": "triage-bootstrap",
|
|
344
351
|
"triage:summary": "triage-summary",
|
|
345
352
|
"triage:queue": "triage-queue",
|
|
353
|
+
"triage:evaluate": "triage-evaluate",
|
|
346
354
|
"triage:scope": "triage-scope",
|
|
347
355
|
...TRIAGE_ACTION_COLON_ALIASES,
|
|
348
356
|
...POLICY_ACTION_COLON_ALIASES,
|
|
@@ -360,6 +368,8 @@ export const VERB_ALIASES = {
|
|
|
360
368
|
"session:start": "session-start",
|
|
361
369
|
"session:ready": "session-ready",
|
|
362
370
|
"occupancy:steal": "occupancy-steal",
|
|
371
|
+
"occupancy:release": "occupancy-release",
|
|
372
|
+
"session:end": "occupancy-release",
|
|
363
373
|
...FRESHNESS_COLON_ALIASES,
|
|
364
374
|
"lifecycle:event": "lifecycle-event",
|
|
365
375
|
"lifecycle:stats": "lifecycle-stats",
|
|
@@ -2122,7 +2132,7 @@ function loadProjectDefinitionForWrite(projectRoot) {
|
|
|
2122
2132
|
throw new PolicySetError("PROJECT-DEFINITION 'plan' is not an object", "config");
|
|
2123
2133
|
}
|
|
2124
2134
|
const planObj = plan;
|
|
2125
|
-
migrateLegacyPolicyKey(planObj);
|
|
2135
|
+
const legacyMigrated = migrateLegacyPolicyKey(planObj);
|
|
2126
2136
|
let policy = planObj[PLAN_POLICY_KEY];
|
|
2127
2137
|
if (policy === undefined) {
|
|
2128
2138
|
policy = {};
|
|
@@ -2131,31 +2141,31 @@ function loadProjectDefinitionForWrite(projectRoot) {
|
|
|
2131
2141
|
if (typeof policy !== "object" || policy === null || Array.isArray(policy)) {
|
|
2132
2142
|
throw new PolicySetError("plan.policy is not an object", "config");
|
|
2133
2143
|
}
|
|
2134
|
-
return { path, data, policyBlock: policy };
|
|
2144
|
+
return { path, data, policyBlock: policy, legacyMigrated };
|
|
2135
2145
|
}
|
|
2136
2146
|
/** Write plan.policy.wipCap in place + append the audit row (mirrors set_wip_cap). */
|
|
2137
2147
|
function writeWipCap(projectRoot, cap, actor, note) {
|
|
2138
|
-
const { path, data, policyBlock } = loadProjectDefinitionForWrite(projectRoot);
|
|
2148
|
+
const { path, data, policyBlock, legacyMigrated } = loadProjectDefinitionForWrite(projectRoot);
|
|
2139
2149
|
const previous = policyBlock.wipCap;
|
|
2140
2150
|
policyBlock.wipCap = cap;
|
|
2141
|
-
|
|
2142
|
-
atomicWriteProjectDefinition(path, data);
|
|
2143
|
-
const changed = previous !== cap;
|
|
2151
|
+
const changed = previous !== cap || legacyMigrated;
|
|
2144
2152
|
const parts = [`actor=${actor}`, `wipCap=${cap}`, `previous=${pyRepr(previous)}`];
|
|
2145
2153
|
if (note)
|
|
2146
2154
|
parts.push(`note=${sanitizeNote(note)}`);
|
|
2147
|
-
const auditEntry = parts.join(" ");
|
|
2148
|
-
|
|
2155
|
+
const auditEntry = stampChangedToken(parts.join(" "), changed);
|
|
2156
|
+
if (changed) {
|
|
2157
|
+
assertWriteTargetSafe(projectRoot, path);
|
|
2158
|
+
atomicWriteProjectDefinition(path, data);
|
|
2159
|
+
}
|
|
2160
|
+
appendAuditLog(projectRoot, auditEntry, changed);
|
|
2149
2161
|
return { changed, auditEntry };
|
|
2150
2162
|
}
|
|
2151
2163
|
/** Write plan.policy.swarmSubagentBackend in place + append the audit row. */
|
|
2152
2164
|
function writeSubagentBackend(projectRoot, backendId, actor, note) {
|
|
2153
|
-
const { path, data, policyBlock } = loadProjectDefinitionForWrite(projectRoot);
|
|
2165
|
+
const { path, data, policyBlock, legacyMigrated } = loadProjectDefinitionForWrite(projectRoot);
|
|
2154
2166
|
const previous = policyBlock.swarmSubagentBackend;
|
|
2155
2167
|
policyBlock.swarmSubagentBackend = backendId;
|
|
2156
|
-
|
|
2157
|
-
atomicWriteProjectDefinition(path, data);
|
|
2158
|
-
const changed = previous !== backendId;
|
|
2168
|
+
const changed = previous !== backendId || legacyMigrated;
|
|
2159
2169
|
const parts = [
|
|
2160
2170
|
`actor=${actor}`,
|
|
2161
2171
|
`swarmSubagentBackend=${backendId}`,
|
|
@@ -2163,8 +2173,12 @@ function writeSubagentBackend(projectRoot, backendId, actor, note) {
|
|
|
2163
2173
|
];
|
|
2164
2174
|
if (note)
|
|
2165
2175
|
parts.push(`note=${sanitizeNote(note)}`);
|
|
2166
|
-
const auditEntry = parts.join(" ");
|
|
2167
|
-
|
|
2176
|
+
const auditEntry = stampChangedToken(parts.join(" "), changed);
|
|
2177
|
+
if (changed) {
|
|
2178
|
+
assertWriteTargetSafe(projectRoot, path);
|
|
2179
|
+
atomicWriteProjectDefinition(path, data);
|
|
2180
|
+
}
|
|
2181
|
+
appendAuditLog(projectRoot, auditEntry, changed);
|
|
2168
2182
|
return { changed, auditEntry };
|
|
2169
2183
|
}
|
|
2170
2184
|
/** Serialise the probe output for `subagent-backends --format json`. */
|
|
@@ -2229,7 +2243,7 @@ function applyBranchPolicy(args, io) {
|
|
|
2229
2243
|
io.writeOut(` audit: meta/policy-changes.log :: ${result.auditEntry}\n`);
|
|
2230
2244
|
}
|
|
2231
2245
|
else {
|
|
2232
|
-
io.writeOut(
|
|
2246
|
+
io.writeOut(`${POLICY_AUDIT_NOOP_STDOUT}\n`);
|
|
2233
2247
|
}
|
|
2234
2248
|
io.writeOut(`${disclosureLine(resolvePolicy(args.projectRoot))}\n`);
|
|
2235
2249
|
return 0;
|
|
@@ -2258,7 +2272,7 @@ function applyWipCap(args, io) {
|
|
|
2258
2272
|
io.writeOut(` audit: meta/policy-changes.log :: ${res.auditEntry}\n`);
|
|
2259
2273
|
}
|
|
2260
2274
|
else {
|
|
2261
|
-
io.writeOut(
|
|
2275
|
+
io.writeOut(`${POLICY_AUDIT_NOOP_STDOUT}\n`);
|
|
2262
2276
|
}
|
|
2263
2277
|
const result = resolveWipCap(args.projectRoot);
|
|
2264
2278
|
io.writeOut(`[deft policy] plan.policy.wipCap=${result.cap} (source: ${result.source}).\n`);
|
|
@@ -2278,7 +2292,7 @@ function applySubagentBackend(args, io) {
|
|
|
2278
2292
|
io.writeOut(` audit: meta/policy-changes.log :: ${res.auditEntry}\n`);
|
|
2279
2293
|
}
|
|
2280
2294
|
else {
|
|
2281
|
-
io.writeOut(
|
|
2295
|
+
io.writeOut(`${POLICY_AUDIT_NOOP_STDOUT}\n`);
|
|
2282
2296
|
}
|
|
2283
2297
|
const result = resolveSwarmSubagentBackend(args.projectRoot);
|
|
2284
2298
|
io.writeOut(`[deft policy] plan.policy.swarmSubagentBackend=${pyRepr(result.backend_id)} ` +
|
|
@@ -2612,6 +2626,14 @@ const CURATED_HELP_GROUPS = [
|
|
|
2612
2626
|
name: "occupancy:steal",
|
|
2613
2627
|
summary: "Supersede a live worktree occupancy lease (--confirm --occupant)",
|
|
2614
2628
|
},
|
|
2629
|
+
{
|
|
2630
|
+
name: "occupancy:release",
|
|
2631
|
+
summary: "Release this worktree occupancy lease (owner live, or expired residue)",
|
|
2632
|
+
},
|
|
2633
|
+
{
|
|
2634
|
+
name: "session:end",
|
|
2635
|
+
summary: "End this mutation session by releasing occupancy (same as occupancy:release)",
|
|
2636
|
+
},
|
|
2615
2637
|
{
|
|
2616
2638
|
name: "freshness:report",
|
|
2617
2639
|
summary: "Bound vs live deposit generation (current|stale_soft|stale_hard)",
|
|
@@ -2720,6 +2742,23 @@ async function invokeHandler(handler, argv) {
|
|
|
2720
2742
|
return typeof code === "number" ? code : 0;
|
|
2721
2743
|
}
|
|
2722
2744
|
const CLI_PACKAGE = "@deftai/directive";
|
|
2745
|
+
const PLAN_SEQUENCE_VERBS = "set|current|clear|advance";
|
|
2746
|
+
/**
|
|
2747
|
+
* Unknown-colon-verb remediation (#3439 / #2652).
|
|
2748
|
+
* Agent-facing spelling is `deft <verb>` (works on consumer CLI and source).
|
|
2749
|
+
* Bare `task <verb>` is not consumer-runnable on include-only Taskfiles.
|
|
2750
|
+
*/
|
|
2751
|
+
export function unknownColonVerbHint(verb) {
|
|
2752
|
+
if (verb.startsWith("plan-sequence:") && !(verb in PLAN_SEQUENCE_ALIAS_SUBCOMMANDS)) {
|
|
2753
|
+
return (`hint: plan-sequence verbs are ${PLAN_SEQUENCE_VERBS} ` +
|
|
2754
|
+
`(e.g. \`deft plan-sequence:current\`). '${verb}' is not a verb. ` +
|
|
2755
|
+
`Do not run bare \`task ${verb}\` -- consumer include-only Taskfiles cannot resolve it.\n`);
|
|
2756
|
+
}
|
|
2757
|
+
const hyphen = verb.replaceAll(":", "-");
|
|
2758
|
+
return (`hint: try \`deft ${verb}\` (CLI) or \`task deft:${verb}\` (consumer Taskfile). ` +
|
|
2759
|
+
`Bare \`task ${verb}\` does not exist on include-only deposits. ` +
|
|
2760
|
+
`Hyphen stem: ${hyphen}.\n`);
|
|
2761
|
+
}
|
|
2723
2762
|
function versionBanner() {
|
|
2724
2763
|
const info = engineInfo();
|
|
2725
2764
|
return `${CLI_PACKAGE} (engine: ${info.name}@${info.version})\n`;
|
|
@@ -2743,8 +2782,7 @@ export async function dispatch(argv, io = defaultIo()) {
|
|
|
2743
2782
|
if (canonical === null) {
|
|
2744
2783
|
io.writeErr(`directive: unknown verb '${verb}'\n`);
|
|
2745
2784
|
if (verb?.includes(":")) {
|
|
2746
|
-
io.writeErr(
|
|
2747
|
-
`(e.g. pr:watch → pr-watch / \`task pr:watch\`)\n`);
|
|
2785
|
+
io.writeErr(unknownColonVerbHint(verb));
|
|
2748
2786
|
}
|
|
2749
2787
|
return 1;
|
|
2750
2788
|
}
|
|
@@ -16,7 +16,7 @@ export interface HumanPresenceMintSeams {
|
|
|
16
16
|
/** Environ for agent-shell marker detection (default: process.env). */
|
|
17
17
|
readonly environ?: NodeJS.ProcessEnv;
|
|
18
18
|
/**
|
|
19
|
-
* True when a controlling terminal device is available (`/dev/tty` or
|
|
19
|
+
* True when a controlling terminal device is available (`/dev/tty` or `\\.\CONIN$`).
|
|
20
20
|
* Default: open/close the platform controlling terminal (fail-closed on error).
|
|
21
21
|
*/
|
|
22
22
|
readonly hasControllingTerminal?: () => boolean;
|
|
@@ -27,6 +27,15 @@ export interface HumanPresenceMintSeams {
|
|
|
27
27
|
readonly readInteractiveConfirm?: () => string | null;
|
|
28
28
|
}
|
|
29
29
|
export declare function looksLikeAgentShell(environ: NodeJS.ProcessEnv): boolean;
|
|
30
|
+
/** Platform controlling-terminal path (`\\.\CONIN$` on win32, `/dev/tty` elsewhere). */
|
|
31
|
+
export declare function controllingTerminalPath(platform?: NodeJS.Platform): string;
|
|
32
|
+
/**
|
|
33
|
+
* Open flag for the platform controlling-terminal device (#3596 leftover).
|
|
34
|
+
*
|
|
35
|
+
* Win32 stays `r+` so the probe and phrase-read share one flag. The leftover
|
|
36
|
+
* path is the Windows device-namespace CONIN$ string, not bare CONIN$.
|
|
37
|
+
*/
|
|
38
|
+
export declare function controllingTerminalOpenFlag(platform?: NodeJS.Platform): "r" | "r+";
|
|
30
39
|
export declare function resolveHumanPresenceMintSeams(seams?: HumanPresenceMintSeams): Required<HumanPresenceMintSeams>;
|
|
31
40
|
/** Active UAT campaign id, or null when no lease is active. */
|
|
32
41
|
export declare function activeUatCampaignId(projectRoot: string): string | null;
|
|
@@ -43,7 +52,7 @@ export declare function refuseMintWhileUatActive(verb: string, projectRoot: stri
|
|
|
43
52
|
* Multi-factor human-presence gate (applies only when UAT lease is inactive):
|
|
44
53
|
* 1. No known agent/CI env markers
|
|
45
54
|
* 2. Interactive TTY (stdin + stdout isTTY)
|
|
46
|
-
* 3. Controlling terminal device present (`/dev/tty` /
|
|
55
|
+
* 3. Controlling terminal device present (`/dev/tty` / `\\.\CONIN$`)
|
|
47
56
|
* 4. Explicit argv `--confirm` (flag alone never enough)
|
|
48
57
|
* 5. Interactive typed phrase `mint` (argv --confirm alone never enough even on PTY)
|
|
49
58
|
*
|
|
@@ -67,10 +67,23 @@ export function looksLikeAgentShell(environ) {
|
|
|
67
67
|
function defaultIsTty() {
|
|
68
68
|
return process.stdin.isTTY === true && process.stdout.isTTY === true;
|
|
69
69
|
}
|
|
70
|
+
/** Platform controlling-terminal path (`\\.\CONIN$` on win32, `/dev/tty` elsewhere). */
|
|
71
|
+
export function controllingTerminalPath(platform = process.platform) {
|
|
72
|
+
// Real-console diagnostic on #3596: bare CONIN$ and CON ENOENT; device-namespace path opens.
|
|
73
|
+
return platform === "win32" ? "\\\\.\\CONIN$" : "/dev/tty";
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Open flag for the platform controlling-terminal device (#3596 leftover).
|
|
77
|
+
*
|
|
78
|
+
* Win32 stays `r+` so the probe and phrase-read share one flag. The leftover
|
|
79
|
+
* path is the Windows device-namespace CONIN$ string, not bare CONIN$.
|
|
80
|
+
*/
|
|
81
|
+
export function controllingTerminalOpenFlag(platform = process.platform) {
|
|
82
|
+
return platform === "win32" ? "r+" : "r";
|
|
83
|
+
}
|
|
70
84
|
function defaultHasControllingTerminal() {
|
|
71
85
|
try {
|
|
72
|
-
const
|
|
73
|
-
const fd = openSync(path, "r");
|
|
86
|
+
const fd = openSync(controllingTerminalPath(), controllingTerminalOpenFlag());
|
|
74
87
|
closeSync(fd);
|
|
75
88
|
return true;
|
|
76
89
|
}
|
|
@@ -83,8 +96,7 @@ function defaultReadInteractiveConfirm() {
|
|
|
83
96
|
// so agent-controlled stdin alone cannot supply the confirm phrase (#3110).
|
|
84
97
|
let fd = null;
|
|
85
98
|
try {
|
|
86
|
-
|
|
87
|
-
fd = openSync(path, "r");
|
|
99
|
+
fd = openSync(controllingTerminalPath(), controllingTerminalOpenFlag());
|
|
88
100
|
const buf = Buffer.alloc(256);
|
|
89
101
|
const n = readSync(fd, buf, 0, buf.length, null);
|
|
90
102
|
if (n <= 0)
|
|
@@ -142,7 +154,7 @@ export function refuseMintWhileUatActive(verb, projectRoot) {
|
|
|
142
154
|
* Multi-factor human-presence gate (applies only when UAT lease is inactive):
|
|
143
155
|
* 1. No known agent/CI env markers
|
|
144
156
|
* 2. Interactive TTY (stdin + stdout isTTY)
|
|
145
|
-
* 3. Controlling terminal device present (`/dev/tty` /
|
|
157
|
+
* 3. Controlling terminal device present (`/dev/tty` / `\\.\CONIN$`)
|
|
146
158
|
* 4. Explicit argv `--confirm` (flag alone never enough)
|
|
147
159
|
* 5. Interactive typed phrase `mint` (argv --confirm alone never enough even on PTY)
|
|
148
160
|
*
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { releaseOccupancy } from "@deftai/directive-core/session";
|
|
4
|
+
export function parseArgs(argv) {
|
|
5
|
+
const parsed = { projectRoot: "." };
|
|
6
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
7
|
+
const arg = argv[i];
|
|
8
|
+
if (arg === "--project-root") {
|
|
9
|
+
const value = argv[i + 1];
|
|
10
|
+
if (value === undefined) {
|
|
11
|
+
return { ...parsed, error: "argument --project-root: expected one argument" };
|
|
12
|
+
}
|
|
13
|
+
parsed.projectRoot = value;
|
|
14
|
+
i += 1;
|
|
15
|
+
}
|
|
16
|
+
else if (arg?.startsWith("--project-root=")) {
|
|
17
|
+
parsed.projectRoot = arg.slice("--project-root=".length);
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
return { ...parsed, error: `unrecognized argument: ${arg}` };
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return parsed;
|
|
24
|
+
}
|
|
25
|
+
export function run(argv) {
|
|
26
|
+
const args = parseArgs(argv);
|
|
27
|
+
if (args.error !== undefined) {
|
|
28
|
+
process.stderr.write(`occupancy:release: ${args.error}\n`);
|
|
29
|
+
return 2;
|
|
30
|
+
}
|
|
31
|
+
const result = releaseOccupancy(resolve(args.projectRoot), {
|
|
32
|
+
env: process.env,
|
|
33
|
+
});
|
|
34
|
+
const sink = result.code === 0 ? process.stdout : process.stderr;
|
|
35
|
+
sink.write(`${result.message}\n`);
|
|
36
|
+
return result.code;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=occupancy-release.js.map
|
package/dist/plan-sequence.d.ts
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
interface Parsed {
|
|
3
|
+
projectRoot: string;
|
|
4
|
+
action: "set" | "current" | "clear" | "advance" | null;
|
|
5
|
+
file: string | null;
|
|
6
|
+
jsonInline: string | null;
|
|
7
|
+
emitJson: boolean;
|
|
8
|
+
error?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function parseArgs(argv: string[]): Parsed;
|
|
2
11
|
export declare function main(argv?: string[]): number;
|
|
12
|
+
export {};
|
|
3
13
|
//# sourceMappingURL=plan-sequence.d.ts.map
|
package/dist/plan-sequence.js
CHANGED
|
@@ -6,7 +6,7 @@ import { readFileSync } from "node:fs";
|
|
|
6
6
|
import { resolve } from "node:path";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
8
|
import { advancePlanSequence, clearPlanSequence, createPlanSequence, parsePlanSequence, readPlanSequence, writePlanSequence, } from "@deftai/directive-core/plan-sequence";
|
|
9
|
-
function parseArgs(argv) {
|
|
9
|
+
export function parseArgs(argv) {
|
|
10
10
|
const parsed = {
|
|
11
11
|
projectRoot: ".",
|
|
12
12
|
action: null,
|
|
@@ -19,6 +19,8 @@ function parseArgs(argv) {
|
|
|
19
19
|
const arg = argv[i];
|
|
20
20
|
if (arg === undefined)
|
|
21
21
|
continue;
|
|
22
|
+
if (arg === "--")
|
|
23
|
+
continue;
|
|
22
24
|
if (arg === "--json") {
|
|
23
25
|
parsed.emitJson = true;
|
|
24
26
|
}
|
package/dist/policy.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { type CeremonyDepth } from "@deftai/directive-core/policy";
|
|
2
|
+
import { type CeremonyDepth, type HookHost } from "@deftai/directive-core/policy";
|
|
3
3
|
interface ShowArgs {
|
|
4
4
|
format: "text" | "json";
|
|
5
5
|
changedOnly: boolean;
|
|
@@ -8,10 +8,11 @@ interface ShowArgs {
|
|
|
8
8
|
error?: string;
|
|
9
9
|
}
|
|
10
10
|
interface SetArgs {
|
|
11
|
-
cmd: "show" | "enforce-branches" | "allow-direct-commits" | "allow-bot-merge" | "enable-value-feedback" | "clear-value-feedback" | "set-ceremony-dial" | "disable-directive" | "enable-directive" | "resolve";
|
|
11
|
+
cmd: "show" | "enforce-branches" | "allow-direct-commits" | "allow-bot-merge" | "enable-value-feedback" | "clear-value-feedback" | "set-ceremony-dial" | "disable-host-hooks" | "disable-directive" | "enable-directive" | "resolve";
|
|
12
12
|
confirm: boolean;
|
|
13
13
|
actor: string;
|
|
14
14
|
note: string;
|
|
15
|
+
host?: HookHost;
|
|
15
16
|
projectRoot: string;
|
|
16
17
|
format: "text" | "json";
|
|
17
18
|
changedOnly: boolean;
|
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 { ALLOW_BOT_MERGE_CAPABILITY_COST, CEREMONY_DEPTHS, 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, setCeremonyDial, setPolicy, setRequireHumanMerge, } from "@deftai/directive-core/policy";
|
|
9
|
+
import { ALLOW_BOT_MERGE_CAPABILITY_COST, CEREMONY_DEPTHS, clearValueFeedback, createNoDeftDirectiveFlag, describeShadowedPlanExtension, detectNoDeftDirective, detectShadowedPlanExtensions, disableHostHooks, 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, POLICY_AUDIT_NOOP_STDOUT, parseHookHost, policyColonInvocation, projectDefinitionPath, pythonListRepr, pythonStringRepr, registeredPolicyNames, removeNoDeftDirectiveFlag, renderJson, renderText, resolveHumanMergePolicy, resolvePolicy, resolveValueFeedback, setCeremonyDial, 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|allow-bot-merge|enable-value-feedback|clear-value-feedback|set-ceremony-dial|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|set-ceremony-dial|disable-host-hooks|disable-directive|enable-directive|resolve] ...";
|
|
118
118
|
return makeSetError(usage);
|
|
119
119
|
}
|
|
120
120
|
const cmd = argv[0];
|
|
@@ -152,6 +152,7 @@ export function parseArgs(argv) {
|
|
|
152
152
|
cmd === "enable-value-feedback" ||
|
|
153
153
|
cmd === "clear-value-feedback" ||
|
|
154
154
|
cmd === "set-ceremony-dial" ||
|
|
155
|
+
cmd === "disable-host-hooks" ||
|
|
155
156
|
cmd === "disable-directive" ||
|
|
156
157
|
cmd === "enable-directive") {
|
|
157
158
|
let confirm = false;
|
|
@@ -167,11 +168,14 @@ export function parseArgs(argv) {
|
|
|
167
168
|
? policyColonInvocation("clear-value-feedback")
|
|
168
169
|
: cmd === "set-ceremony-dial"
|
|
169
170
|
? policyColonInvocation("set-ceremony-dial")
|
|
170
|
-
: cmd === "disable-
|
|
171
|
-
? policyColonInvocation("disable-
|
|
172
|
-
:
|
|
171
|
+
: cmd === "disable-host-hooks"
|
|
172
|
+
? policyColonInvocation("disable-host-hooks")
|
|
173
|
+
: cmd === "disable-directive"
|
|
174
|
+
? policyColonInvocation("disable-directive")
|
|
175
|
+
: policyColonInvocation("enable-directive");
|
|
173
176
|
let note = "";
|
|
174
177
|
let projectRoot = ".";
|
|
178
|
+
let host;
|
|
175
179
|
let ceremonyOverride;
|
|
176
180
|
let ceremonyEnabled;
|
|
177
181
|
for (let i = 1; i < argv.length; i += 1) {
|
|
@@ -253,10 +257,32 @@ export function parseArgs(argv) {
|
|
|
253
257
|
}
|
|
254
258
|
ceremonyEnabled = v === "true";
|
|
255
259
|
}
|
|
260
|
+
else if (cmd === "disable-host-hooks" && arg === "--host") {
|
|
261
|
+
const v = argv[i + 1];
|
|
262
|
+
const parsedHost = parseHookHost(v);
|
|
263
|
+
if (parsedHost === null) {
|
|
264
|
+
return makeSetError("argument --host: expected claude|cursor|grok|codex");
|
|
265
|
+
}
|
|
266
|
+
host = parsedHost;
|
|
267
|
+
i += 1;
|
|
268
|
+
}
|
|
269
|
+
else if (cmd === "disable-host-hooks" && arg?.startsWith("--host=")) {
|
|
270
|
+
const parsedHost = parseHookHost(arg.slice("--host=".length));
|
|
271
|
+
if (parsedHost === null) {
|
|
272
|
+
return makeSetError("argument --host: expected claude|cursor|grok|codex");
|
|
273
|
+
}
|
|
274
|
+
host = parsedHost;
|
|
275
|
+
}
|
|
276
|
+
else if (arg === "--") {
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
256
279
|
else {
|
|
257
280
|
return makeSetError(`unrecognized argument: ${arg}`);
|
|
258
281
|
}
|
|
259
282
|
}
|
|
283
|
+
if (cmd === "disable-host-hooks" && host === undefined) {
|
|
284
|
+
return makeSetError("disable-host-hooks requires --host claude|cursor|grok|codex");
|
|
285
|
+
}
|
|
260
286
|
return {
|
|
261
287
|
cmd,
|
|
262
288
|
confirm,
|
|
@@ -266,6 +292,7 @@ export function parseArgs(argv) {
|
|
|
266
292
|
format: "text",
|
|
267
293
|
changedOnly: false,
|
|
268
294
|
field: null,
|
|
295
|
+
host,
|
|
269
296
|
ceremonyOverride,
|
|
270
297
|
ceremonyEnabled,
|
|
271
298
|
};
|
|
@@ -352,6 +379,21 @@ function runEnableValueFeedback(args) {
|
|
|
352
379
|
process.stdout.write(result.stdout);
|
|
353
380
|
return result.exitCode;
|
|
354
381
|
}
|
|
382
|
+
function runDisableHostHooks(args) {
|
|
383
|
+
const host = args.host;
|
|
384
|
+
if (host === undefined) {
|
|
385
|
+
process.stderr.write("policy: disable-host-hooks requires --host claude|cursor|grok|codex\n");
|
|
386
|
+
return 2;
|
|
387
|
+
}
|
|
388
|
+
const result = disableHostHooks(pathResolve(args.projectRoot), {
|
|
389
|
+
host,
|
|
390
|
+
confirm: args.confirm,
|
|
391
|
+
actor: args.actor,
|
|
392
|
+
note: args.note,
|
|
393
|
+
});
|
|
394
|
+
process.stdout.write(result.stdout);
|
|
395
|
+
return result.exitCode;
|
|
396
|
+
}
|
|
355
397
|
function runSet(args) {
|
|
356
398
|
const projectRoot = pathResolve(args.projectRoot);
|
|
357
399
|
if (args.cmd === "allow-direct-commits" && !args.confirm) {
|
|
@@ -373,7 +415,7 @@ function runSet(args) {
|
|
|
373
415
|
process.stdout.write(` audit: meta/policy-changes.log :: ${auditEntry}\n`);
|
|
374
416
|
}
|
|
375
417
|
else {
|
|
376
|
-
process.stdout.write(
|
|
418
|
+
process.stdout.write(`${POLICY_AUDIT_NOOP_STDOUT}\n`);
|
|
377
419
|
}
|
|
378
420
|
process.stdout.write(`${disclosureLine(resolvePolicy(projectRoot))}\n`);
|
|
379
421
|
return 0;
|
|
@@ -454,7 +496,7 @@ function runAllowBotMerge(args) {
|
|
|
454
496
|
process.stdout.write(` audit: meta/policy-changes.log :: ${auditEntry}\n`);
|
|
455
497
|
}
|
|
456
498
|
else {
|
|
457
|
-
process.stdout.write(
|
|
499
|
+
process.stdout.write(`${POLICY_AUDIT_NOOP_STDOUT}\n`);
|
|
458
500
|
}
|
|
459
501
|
const line = humanMergeDisclosureLine(resolveHumanMergePolicy(projectRoot));
|
|
460
502
|
if (line !== null) {
|
|
@@ -504,6 +546,9 @@ export function run(argv) {
|
|
|
504
546
|
if (args.cmd === "enable-value-feedback") {
|
|
505
547
|
return runEnableValueFeedback(args);
|
|
506
548
|
}
|
|
549
|
+
if (args.cmd === "disable-host-hooks") {
|
|
550
|
+
return runDisableHostHooks(args);
|
|
551
|
+
}
|
|
507
552
|
if (args.cmd === "clear-value-feedback") {
|
|
508
553
|
return runClearValueFeedback(args);
|
|
509
554
|
}
|
package/dist/preflight-cache.js
CHANGED
|
@@ -58,6 +58,12 @@ export function parseArgs(argv) {
|
|
|
58
58
|
else if (arg === "--allow-missing-bootstrap") {
|
|
59
59
|
parsed.allowMissingBootstrap = true;
|
|
60
60
|
}
|
|
61
|
+
else if (arg === "--skip-drift-probe") {
|
|
62
|
+
parsed.skipDriftProbe = true;
|
|
63
|
+
}
|
|
64
|
+
else if (arg === "--work-selection") {
|
|
65
|
+
parsed.workSelection = true;
|
|
66
|
+
}
|
|
61
67
|
else if (arg === "--quiet") {
|
|
62
68
|
parsed.quiet = true;
|
|
63
69
|
}
|
|
@@ -81,6 +87,7 @@ export function run(argv) {
|
|
|
81
87
|
forIssue: args.forIssue ?? null,
|
|
82
88
|
allowStale: args.allowStale ?? false,
|
|
83
89
|
allowMissingBootstrap: args.allowMissingBootstrap ?? false,
|
|
90
|
+
skipDriftProbe: (args.skipDriftProbe ?? false) && !args.workSelection,
|
|
84
91
|
});
|
|
85
92
|
const quiet = args.quiet ?? false;
|
|
86
93
|
if (result.code === 0) {
|
package/dist/triage-actions.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
|
-
import { accept, createDefaultDeps, deferAction, formatDecision, history, markDuplicate, needsAc, reject, reset, status, TriageError, UpstreamCloseError, } from "@deftai/directive-core/dist/triage/actions/index.js";
|
|
4
|
+
import { accept, createDefaultDeps, deferAction, formatAcceptOutput, formatDecision, history, markDuplicate, needsAc, reject, reset, status, TriageError, UpstreamCloseError, } from "@deftai/directive-core/dist/triage/actions/index.js";
|
|
5
5
|
const VALID_CMDS = new Set([
|
|
6
6
|
"accept",
|
|
7
7
|
"reject",
|
|
@@ -21,6 +21,8 @@ export function parseArgs(argv) {
|
|
|
21
21
|
parsed.cmd = argv[0];
|
|
22
22
|
for (let i = 1; i < argv.length; i += 1) {
|
|
23
23
|
const arg = argv[i];
|
|
24
|
+
if (arg === "--")
|
|
25
|
+
continue;
|
|
24
26
|
if (arg === "--issue") {
|
|
25
27
|
const value = argv[i + 1];
|
|
26
28
|
if (value === undefined)
|
|
@@ -156,8 +158,7 @@ export function run(argv) {
|
|
|
156
158
|
autoPromote: args.autoPromote === true,
|
|
157
159
|
force: args.force === true,
|
|
158
160
|
});
|
|
159
|
-
|
|
160
|
-
process.stdout.write(`accept #${n} (${repo}) -> ${decisionId}${promoteNote}\n`);
|
|
161
|
+
process.stdout.write(`${formatAcceptOutput(n, repo, decisionId, { autoPromote: args.autoPromote === true })}\n`);
|
|
161
162
|
}
|
|
162
163
|
else if (args.cmd === "reject") {
|
|
163
164
|
const decisionId = reject(n, repo, args.reason ?? "", deps, {
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
interface Parsed {
|
|
3
|
+
projectRoot: string;
|
|
4
|
+
repo: string | null;
|
|
5
|
+
issues: number[];
|
|
6
|
+
concurrency: number;
|
|
7
|
+
json: boolean;
|
|
8
|
+
error?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function parseArgs(argv: string[]): Parsed;
|
|
11
|
+
export declare function run(argv: string[]): Promise<number>;
|
|
12
|
+
export declare function main(argv: string[]): Promise<number>;
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=triage-evaluate.d.ts.map
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { DEFAULT_CONCURRENCY, EvaluateError, evaluateIssues, renderEvaluateText, } from "@deftai/directive-core/dist/triage/evaluate/index.js";
|
|
5
|
+
import { interceptHelp } from "@deftai/directive-core/dist/triage/help/index.js";
|
|
6
|
+
import { resolveRepo } from "@deftai/directive-core/dist/triage/queue/repo.js";
|
|
7
|
+
export function parseArgs(argv) {
|
|
8
|
+
const parsed = {
|
|
9
|
+
projectRoot: process.env.DEFT_PROJECT_ROOT ?? process.cwd(),
|
|
10
|
+
repo: process.env.DEFT_TRIAGE_REPO ?? null,
|
|
11
|
+
issues: [],
|
|
12
|
+
concurrency: DEFAULT_CONCURRENCY,
|
|
13
|
+
json: false,
|
|
14
|
+
};
|
|
15
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
16
|
+
const arg = argv[i];
|
|
17
|
+
if (arg === undefined || arg === "--") {
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
if (arg === "--project-root") {
|
|
21
|
+
if (i + 1 >= argv.length) {
|
|
22
|
+
return { ...parsed, error: "--project-root requires a path" };
|
|
23
|
+
}
|
|
24
|
+
i += 1;
|
|
25
|
+
const value = argv[i];
|
|
26
|
+
if (value === undefined) {
|
|
27
|
+
return { ...parsed, error: "--project-root requires a path" };
|
|
28
|
+
}
|
|
29
|
+
parsed.projectRoot = value;
|
|
30
|
+
}
|
|
31
|
+
else if (arg.startsWith("--project-root=")) {
|
|
32
|
+
parsed.projectRoot = arg.slice("--project-root=".length);
|
|
33
|
+
}
|
|
34
|
+
else if (arg === "--repo") {
|
|
35
|
+
if (i + 1 >= argv.length) {
|
|
36
|
+
return { ...parsed, error: "--repo requires owner/name" };
|
|
37
|
+
}
|
|
38
|
+
i += 1;
|
|
39
|
+
const value = argv[i];
|
|
40
|
+
if (value === undefined) {
|
|
41
|
+
return { ...parsed, error: "--repo requires owner/name" };
|
|
42
|
+
}
|
|
43
|
+
parsed.repo = value;
|
|
44
|
+
}
|
|
45
|
+
else if (arg.startsWith("--repo=")) {
|
|
46
|
+
parsed.repo = arg.slice("--repo=".length);
|
|
47
|
+
}
|
|
48
|
+
else if (arg === "--issue") {
|
|
49
|
+
if (i + 1 >= argv.length) {
|
|
50
|
+
return { ...parsed, error: "--issue requires a number" };
|
|
51
|
+
}
|
|
52
|
+
i += 1;
|
|
53
|
+
const value = argv[i];
|
|
54
|
+
if (value === undefined) {
|
|
55
|
+
return { ...parsed, error: "--issue requires a number" };
|
|
56
|
+
}
|
|
57
|
+
const n = Number.parseInt(value, 10);
|
|
58
|
+
if (!Number.isInteger(n) || n < 1) {
|
|
59
|
+
return { ...parsed, error: `--issue invalid: ${value}` };
|
|
60
|
+
}
|
|
61
|
+
parsed.issues.push(n);
|
|
62
|
+
}
|
|
63
|
+
else if (arg.startsWith("--issue=")) {
|
|
64
|
+
const n = Number.parseInt(arg.slice("--issue=".length), 10);
|
|
65
|
+
if (!Number.isInteger(n) || n < 1) {
|
|
66
|
+
return { ...parsed, error: `--issue invalid: ${arg}` };
|
|
67
|
+
}
|
|
68
|
+
parsed.issues.push(n);
|
|
69
|
+
}
|
|
70
|
+
else if (arg === "--concurrency") {
|
|
71
|
+
if (i + 1 >= argv.length) {
|
|
72
|
+
return { ...parsed, error: "--concurrency requires a positive integer" };
|
|
73
|
+
}
|
|
74
|
+
i += 1;
|
|
75
|
+
const value = argv[i];
|
|
76
|
+
if (value === undefined) {
|
|
77
|
+
return { ...parsed, error: "--concurrency requires a positive integer" };
|
|
78
|
+
}
|
|
79
|
+
const n = Number.parseInt(value, 10);
|
|
80
|
+
if (!Number.isInteger(n) || n < 1) {
|
|
81
|
+
return { ...parsed, error: `--concurrency invalid: ${value}` };
|
|
82
|
+
}
|
|
83
|
+
parsed.concurrency = n;
|
|
84
|
+
}
|
|
85
|
+
else if (arg.startsWith("--concurrency=")) {
|
|
86
|
+
const n = Number.parseInt(arg.slice("--concurrency=".length), 10);
|
|
87
|
+
if (!Number.isInteger(n) || n < 1) {
|
|
88
|
+
return { ...parsed, error: `--concurrency invalid: ${arg}` };
|
|
89
|
+
}
|
|
90
|
+
parsed.concurrency = n;
|
|
91
|
+
}
|
|
92
|
+
else if (arg === "--json") {
|
|
93
|
+
parsed.json = true;
|
|
94
|
+
}
|
|
95
|
+
else if (arg.startsWith("-")) {
|
|
96
|
+
return { ...parsed, error: `unrecognized argument: ${arg}` };
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
const n = Number.parseInt(arg.replace(/^#/u, ""), 10);
|
|
100
|
+
if (!Number.isInteger(n) || n < 1) {
|
|
101
|
+
return { ...parsed, error: `invalid issue number: ${arg}` };
|
|
102
|
+
}
|
|
103
|
+
parsed.issues.push(n);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return parsed;
|
|
107
|
+
}
|
|
108
|
+
export async function run(argv) {
|
|
109
|
+
const help = interceptHelp("triage_evaluate", argv);
|
|
110
|
+
if (help !== null) {
|
|
111
|
+
return help;
|
|
112
|
+
}
|
|
113
|
+
const parsed = parseArgs(argv);
|
|
114
|
+
if (parsed.error !== undefined) {
|
|
115
|
+
process.stderr.write(`triage:evaluate: ${parsed.error}\n`);
|
|
116
|
+
return 2;
|
|
117
|
+
}
|
|
118
|
+
const projectRoot = resolve(parsed.projectRoot);
|
|
119
|
+
const repo = resolveRepo(parsed.repo, projectRoot);
|
|
120
|
+
if (repo === null) {
|
|
121
|
+
process.stderr.write("triage:evaluate: --repo owner/name is required\n");
|
|
122
|
+
return 2;
|
|
123
|
+
}
|
|
124
|
+
try {
|
|
125
|
+
const result = await evaluateIssues({
|
|
126
|
+
projectRoot,
|
|
127
|
+
repo,
|
|
128
|
+
issues: parsed.issues,
|
|
129
|
+
concurrency: parsed.concurrency,
|
|
130
|
+
});
|
|
131
|
+
if (parsed.json) {
|
|
132
|
+
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
process.stdout.write(renderEvaluateText(result));
|
|
136
|
+
}
|
|
137
|
+
return result.verdicts.some((v) => v.error !== null) ? 1 : 0;
|
|
138
|
+
}
|
|
139
|
+
catch (err) {
|
|
140
|
+
const message = err instanceof EvaluateError ? err.message : String(err);
|
|
141
|
+
process.stderr.write(`triage:evaluate: ${message}\n`);
|
|
142
|
+
return err instanceof EvaluateError ? 2 : 1;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
export async function main(argv) {
|
|
146
|
+
return run(argv);
|
|
147
|
+
}
|
|
148
|
+
if (process.argv[1] !== undefined && fileURLToPath(import.meta.url) === process.argv[1]) {
|
|
149
|
+
void run(process.argv.slice(2)).then((code) => {
|
|
150
|
+
process.exit(code);
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=triage-evaluate.js.map
|
package/dist/triage-queue.js
CHANGED
|
@@ -88,7 +88,7 @@ export function parseArgs(argv) {
|
|
|
88
88
|
};
|
|
89
89
|
for (let i = 0; i < argv.length; i += 1) {
|
|
90
90
|
const arg = argv[i];
|
|
91
|
-
if (arg === "queue") {
|
|
91
|
+
if (arg === "queue" || arg === "--") {
|
|
92
92
|
continue;
|
|
93
93
|
}
|
|
94
94
|
if (arg === "--include-blocked") {
|
|
@@ -197,6 +197,9 @@ export function parseShowArgs(argv) {
|
|
|
197
197
|
}
|
|
198
198
|
for (let i = start; i < argv.length; i += 1) {
|
|
199
199
|
const arg = argv[i];
|
|
200
|
+
if (arg === "--") {
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
200
203
|
const commonHit = parseCommonFlag(arg, argv, i, parsed);
|
|
201
204
|
if (commonHit !== null) {
|
|
202
205
|
if (commonHit.error !== undefined) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
|
-
import { evaluateCompletedTracked } from "@deftai/directive-core/lifecycle";
|
|
4
|
+
import { COMPLETED_TRACKED_PROGRESS_MIN_BLOBS, COMPLETED_TRACKED_PROGRESS_THRESHOLD_MS, evaluateCompletedTracked, shouldAnnounceProgress, shouldAnnounceUpFrontCount, } from "@deftai/directive-core/lifecycle";
|
|
5
5
|
function parseIssueNumber(raw) {
|
|
6
6
|
const trimmed = raw.startsWith("#") ? raw.slice(1) : raw;
|
|
7
7
|
if (!/^\d+$/.test(trimmed)) {
|
|
@@ -95,12 +95,27 @@ export function run(argv) {
|
|
|
95
95
|
return 2;
|
|
96
96
|
}
|
|
97
97
|
const projectRoot = resolve(args.projectRoot);
|
|
98
|
+
const thresholdMs = resolveProgressThresholdMs();
|
|
99
|
+
let announced = false;
|
|
98
100
|
const result = evaluateCompletedTracked(projectRoot, {
|
|
99
101
|
quiet: args.quiet,
|
|
100
102
|
repo: args.repo,
|
|
101
103
|
tip: args.tip,
|
|
102
104
|
issue: args.issue,
|
|
103
105
|
skipGh: args.skipGh,
|
|
106
|
+
onProgress: (event) => {
|
|
107
|
+
if (args.quiet || announced) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const minBlobs = resolveProgressMinBlobs();
|
|
111
|
+
const upFront = event.phase === "listed" &&
|
|
112
|
+
shouldAnnounceUpFrontCount(event.terminalCount, event.nonterminalCount, minBlobs);
|
|
113
|
+
if (!upFront && !shouldAnnounceProgress(event.elapsedMs, thresholdMs)) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
announced = true;
|
|
117
|
+
writeProgress(event);
|
|
118
|
+
},
|
|
104
119
|
});
|
|
105
120
|
if (result.message.length > 0) {
|
|
106
121
|
if (result.stream === "stdout") {
|
|
@@ -112,6 +127,24 @@ export function run(argv) {
|
|
|
112
127
|
}
|
|
113
128
|
return result.code;
|
|
114
129
|
}
|
|
130
|
+
function resolveProgressThresholdMs() {
|
|
131
|
+
const raw = process.env.DEFT_COMPLETED_TRACKED_PROGRESS_MS;
|
|
132
|
+
if (raw !== undefined && /^\d+$/.test(raw)) {
|
|
133
|
+
return Number(raw);
|
|
134
|
+
}
|
|
135
|
+
return COMPLETED_TRACKED_PROGRESS_THRESHOLD_MS;
|
|
136
|
+
}
|
|
137
|
+
function resolveProgressMinBlobs() {
|
|
138
|
+
const raw = process.env.DEFT_COMPLETED_TRACKED_PROGRESS_MIN_BLOBS;
|
|
139
|
+
if (raw !== undefined && /^\d+$/.test(raw)) {
|
|
140
|
+
return Number(raw);
|
|
141
|
+
}
|
|
142
|
+
return COMPLETED_TRACKED_PROGRESS_MIN_BLOBS;
|
|
143
|
+
}
|
|
144
|
+
function writeProgress(event) {
|
|
145
|
+
process.stderr.write(`verify:completed-tracked: still running (${event.elapsedMs}ms); ` +
|
|
146
|
+
`reading ${event.terminalCount} terminal and ${event.nonterminalCount} nonterminal blobs.\n`);
|
|
147
|
+
}
|
|
115
148
|
if (process.argv[1] !== undefined && fileURLToPath(import.meta.url) === process.argv[1]) {
|
|
116
149
|
process.exit(run(process.argv.slice(2)));
|
|
117
150
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
interface ParsedArgs {
|
|
3
|
+
projectRoot: string;
|
|
4
|
+
baseRef: string;
|
|
5
|
+
quiet: boolean;
|
|
6
|
+
error?: string;
|
|
7
|
+
}
|
|
8
|
+
/** Parse verify-completed-write-guard CLI args (#3679). */
|
|
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-completed-write-guard.d.ts.map
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* CLI for verify:completed-write-guard (#3679).
|
|
4
|
+
*/
|
|
5
|
+
import { resolve } from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { evaluateCompletedWriteGuard } from "@deftai/directive-core/lifecycle";
|
|
8
|
+
/** Parse verify-completed-write-guard CLI args (#3679). */
|
|
9
|
+
export function parseArgs(argv) {
|
|
10
|
+
const parsed = {
|
|
11
|
+
projectRoot: ".",
|
|
12
|
+
baseRef: "",
|
|
13
|
+
quiet: false,
|
|
14
|
+
};
|
|
15
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
16
|
+
const arg = argv[i];
|
|
17
|
+
if (arg === "--quiet") {
|
|
18
|
+
parsed.quiet = true;
|
|
19
|
+
}
|
|
20
|
+
else if (arg === "--project-root") {
|
|
21
|
+
const value = argv[i + 1];
|
|
22
|
+
if (value === undefined) {
|
|
23
|
+
return { ...parsed, error: "argument --project-root: expected one argument" };
|
|
24
|
+
}
|
|
25
|
+
parsed.projectRoot = value;
|
|
26
|
+
i += 1;
|
|
27
|
+
}
|
|
28
|
+
else if (arg?.startsWith("--project-root=")) {
|
|
29
|
+
parsed.projectRoot = arg.slice("--project-root=".length);
|
|
30
|
+
}
|
|
31
|
+
else if (arg === "--base-ref") {
|
|
32
|
+
const value = argv[i + 1];
|
|
33
|
+
if (value === undefined) {
|
|
34
|
+
return { ...parsed, error: "argument --base-ref: expected one argument" };
|
|
35
|
+
}
|
|
36
|
+
parsed.baseRef = value;
|
|
37
|
+
i += 1;
|
|
38
|
+
}
|
|
39
|
+
else if (arg?.startsWith("--base-ref=")) {
|
|
40
|
+
parsed.baseRef = arg.slice("--base-ref=".length);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
return { ...parsed, error: `unrecognized argument: ${arg}` };
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return parsed;
|
|
47
|
+
}
|
|
48
|
+
/** Run the gate and return the process exit code. */
|
|
49
|
+
export function run(argv) {
|
|
50
|
+
const args = parseArgs(argv);
|
|
51
|
+
if (args.error !== undefined) {
|
|
52
|
+
process.stderr.write(`verify_completed_write_guard: ${args.error}\n`);
|
|
53
|
+
return 2;
|
|
54
|
+
}
|
|
55
|
+
const projectRoot = resolve(args.projectRoot);
|
|
56
|
+
const result = evaluateCompletedWriteGuard(projectRoot, {
|
|
57
|
+
...(args.baseRef.length > 0 ? { baseRef: args.baseRef } : {}),
|
|
58
|
+
});
|
|
59
|
+
if (result.message.length > 0 && !args.quiet) {
|
|
60
|
+
if (result.code === 0) {
|
|
61
|
+
process.stdout.write(`${result.message}\n`);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
process.stderr.write(`${result.message}\n`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return result.code;
|
|
68
|
+
}
|
|
69
|
+
if (process.argv[1] !== undefined && fileURLToPath(import.meta.url) === process.argv[1]) {
|
|
70
|
+
process.exit(run(process.argv.slice(2)));
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=verify-completed-write-guard.js.map
|
|
@@ -5,6 +5,10 @@ interface ParsedArgs {
|
|
|
5
5
|
projectRoot: string;
|
|
6
6
|
allowList: string | null;
|
|
7
7
|
quiet: boolean;
|
|
8
|
+
enforce: boolean;
|
|
9
|
+
coverageReport: string | null;
|
|
10
|
+
coverageDir: string | null;
|
|
11
|
+
help?: boolean;
|
|
8
12
|
error?: string;
|
|
9
13
|
}
|
|
10
14
|
/** Parse the verify-forward-coverage CLI args, mirroring the verify-encoding surface. */
|
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
import { evaluateForwardCoverage } from "@deftai/directive-core";
|
|
5
|
+
const HELP_TEXT = "Usage: verify-forward-coverage [--project-root <path>] [--staged|--head] [--allow-list <path>]\n" +
|
|
6
|
+
" [--coverage-dir <path>] [--coverage-report <path>]\n" +
|
|
7
|
+
" [--enforce] [--quiet]\n" +
|
|
8
|
+
" Fail-closed new-source-file existence (#1310) plus warn-first diff coverage of\n" +
|
|
9
|
+
" added/modified branches (#3514). The 90% per-diff threshold is not the 75\n" +
|
|
10
|
+
" global floor. Missing coverage reports skip the diff half.\n" +
|
|
11
|
+
" --enforce: fail closed when uncovered changed branches are below 90%.\n";
|
|
12
|
+
function takeValue(argv, i, flag) {
|
|
13
|
+
const value = argv[i + 1];
|
|
14
|
+
if (value === undefined) {
|
|
15
|
+
return { error: `argument ${flag}: expected one argument` };
|
|
16
|
+
}
|
|
17
|
+
return { value, next: i + 1 };
|
|
18
|
+
}
|
|
5
19
|
/** Parse the verify-forward-coverage CLI args, mirroring the verify-encoding surface. */
|
|
6
20
|
export function parseArgs(argv) {
|
|
7
21
|
const parsed = {
|
|
@@ -9,6 +23,9 @@ export function parseArgs(argv) {
|
|
|
9
23
|
projectRoot: ".",
|
|
10
24
|
allowList: null,
|
|
11
25
|
quiet: false,
|
|
26
|
+
enforce: false,
|
|
27
|
+
coverageReport: null,
|
|
28
|
+
coverageDir: null,
|
|
12
29
|
};
|
|
13
30
|
for (let i = 0; i < argv.length; i += 1) {
|
|
14
31
|
const arg = argv[i];
|
|
@@ -21,48 +38,97 @@ export function parseArgs(argv) {
|
|
|
21
38
|
else if (arg === "--quiet") {
|
|
22
39
|
parsed.quiet = true;
|
|
23
40
|
}
|
|
41
|
+
else if (arg === "--enforce") {
|
|
42
|
+
parsed.enforce = true;
|
|
43
|
+
}
|
|
44
|
+
else if (arg === "--help" || arg === "-h") {
|
|
45
|
+
return { ...parsed, help: true };
|
|
46
|
+
}
|
|
24
47
|
else if (arg === "--project-root") {
|
|
25
|
-
const
|
|
26
|
-
if (
|
|
27
|
-
return { ...parsed, error:
|
|
48
|
+
const taken = takeValue(argv, i, "--project-root");
|
|
49
|
+
if ("error" in taken) {
|
|
50
|
+
return { ...parsed, error: taken.error };
|
|
28
51
|
}
|
|
29
|
-
parsed.projectRoot = value;
|
|
30
|
-
i
|
|
52
|
+
parsed.projectRoot = taken.value;
|
|
53
|
+
i = taken.next;
|
|
31
54
|
}
|
|
32
55
|
else if (arg?.startsWith("--project-root=")) {
|
|
33
56
|
parsed.projectRoot = arg.slice("--project-root=".length);
|
|
34
57
|
}
|
|
35
58
|
else if (arg === "--allow-list") {
|
|
36
|
-
const
|
|
37
|
-
if (
|
|
38
|
-
return { ...parsed, error:
|
|
59
|
+
const taken = takeValue(argv, i, "--allow-list");
|
|
60
|
+
if ("error" in taken) {
|
|
61
|
+
return { ...parsed, error: taken.error };
|
|
39
62
|
}
|
|
40
|
-
parsed.allowList = value;
|
|
41
|
-
i
|
|
63
|
+
parsed.allowList = taken.value;
|
|
64
|
+
i = taken.next;
|
|
42
65
|
}
|
|
43
66
|
else if (arg?.startsWith("--allow-list=")) {
|
|
44
67
|
parsed.allowList = arg.slice("--allow-list=".length);
|
|
45
68
|
}
|
|
69
|
+
else if (arg === "--coverage-report") {
|
|
70
|
+
const taken = takeValue(argv, i, "--coverage-report");
|
|
71
|
+
if ("error" in taken) {
|
|
72
|
+
return { ...parsed, error: taken.error };
|
|
73
|
+
}
|
|
74
|
+
parsed.coverageReport = taken.value;
|
|
75
|
+
i = taken.next;
|
|
76
|
+
}
|
|
77
|
+
else if (arg?.startsWith("--coverage-report=")) {
|
|
78
|
+
parsed.coverageReport = arg.slice("--coverage-report=".length);
|
|
79
|
+
}
|
|
80
|
+
else if (arg === "--coverage-dir") {
|
|
81
|
+
const taken = takeValue(argv, i, "--coverage-dir");
|
|
82
|
+
if ("error" in taken) {
|
|
83
|
+
return { ...parsed, error: taken.error };
|
|
84
|
+
}
|
|
85
|
+
parsed.coverageDir = taken.value;
|
|
86
|
+
i = taken.next;
|
|
87
|
+
}
|
|
88
|
+
else if (arg?.startsWith("--coverage-dir=")) {
|
|
89
|
+
parsed.coverageDir = arg.slice("--coverage-dir=".length);
|
|
90
|
+
}
|
|
46
91
|
else {
|
|
47
92
|
return { ...parsed, error: `unrecognized argument: ${arg}` };
|
|
48
93
|
}
|
|
49
94
|
}
|
|
50
95
|
return parsed;
|
|
51
96
|
}
|
|
97
|
+
function resolveCoverageReport(args) {
|
|
98
|
+
if (args.coverageReport !== null) {
|
|
99
|
+
return resolve(args.coverageReport);
|
|
100
|
+
}
|
|
101
|
+
if (args.coverageDir !== null) {
|
|
102
|
+
return resolve(args.coverageDir, "coverage-final.json");
|
|
103
|
+
}
|
|
104
|
+
return undefined;
|
|
105
|
+
}
|
|
52
106
|
/** Run the gate and return the process exit code (argv parse error -> 2). */
|
|
53
107
|
export function run(argv) {
|
|
54
108
|
const args = parseArgs(argv);
|
|
109
|
+
if (args.help === true) {
|
|
110
|
+
process.stdout.write(HELP_TEXT);
|
|
111
|
+
return 0;
|
|
112
|
+
}
|
|
55
113
|
if (args.error !== undefined) {
|
|
56
114
|
process.stderr.write(`verify_forward_coverage: ${args.error}\n`);
|
|
57
115
|
return 2;
|
|
58
116
|
}
|
|
59
117
|
const projectRoot = resolve(args.projectRoot);
|
|
60
118
|
const allowListPath = args.allowList !== null ? resolve(args.allowList) : null;
|
|
61
|
-
const result = evaluateForwardCoverage(projectRoot, {
|
|
119
|
+
const result = evaluateForwardCoverage(projectRoot, {
|
|
120
|
+
mode: args.mode,
|
|
121
|
+
allowListPath,
|
|
122
|
+
enforceDiffCoverage: args.enforce,
|
|
123
|
+
coverageReportPath: resolveCoverageReport(args),
|
|
124
|
+
});
|
|
62
125
|
if (result.exitCode === 0) {
|
|
63
126
|
if (!args.quiet) {
|
|
64
127
|
process.stdout.write(`${result.message}\n`);
|
|
65
128
|
}
|
|
129
|
+
else if (result.message.includes("ADVISORY")) {
|
|
130
|
+
process.stdout.write(`${result.message}\n`);
|
|
131
|
+
}
|
|
66
132
|
}
|
|
67
133
|
else {
|
|
68
134
|
process.stderr.write(`${result.message}\n`);
|
|
@@ -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-lifecycle-visible CLI args (#3505). */
|
|
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-lifecycle-visible.d.ts.map
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* CLI for task verify:lifecycle-visible (#3505).
|
|
4
|
+
* Warn-only by default; pass --enforce to fail closed.
|
|
5
|
+
*/
|
|
6
|
+
import { resolve } from "node:path";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
import { evaluateLifecycleVisible } from "@deftai/directive-core/lifecycle-visible";
|
|
9
|
+
/** Parse verify-lifecycle-visible CLI args (#3505). */
|
|
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-lifecycle-visible [--project-root <path>] [--enforce]\n" +
|
|
38
|
+
" Detect ignore/exclude/skip-worktree flags that hide xbrief/vbrief lifecycle roots (#3505).\n" +
|
|
39
|
+
" Default: warn-only (exit 0 with advisory report).\n" +
|
|
40
|
+
" --enforce: fail closed (exit 1) when a lifecycle root is hidden.\n" +
|
|
41
|
+
" Not part of task check — per-clone property, wired from session:start.\n";
|
|
42
|
+
/** Run the gate and return the process exit code. */
|
|
43
|
+
export function run(argv) {
|
|
44
|
+
const args = parseArgs(argv);
|
|
45
|
+
if (args.help === true) {
|
|
46
|
+
process.stdout.write(HELP_TEXT);
|
|
47
|
+
return 0;
|
|
48
|
+
}
|
|
49
|
+
if (args.error !== undefined) {
|
|
50
|
+
process.stderr.write(`verify_lifecycle_visible: ${args.error}\n`);
|
|
51
|
+
return 2;
|
|
52
|
+
}
|
|
53
|
+
const result = evaluateLifecycleVisible({
|
|
54
|
+
projectRoot: resolve(args.projectRoot),
|
|
55
|
+
enforce: args.enforce,
|
|
56
|
+
});
|
|
57
|
+
if (result.stream === "stdout") {
|
|
58
|
+
process.stdout.write(`${result.message}\n`);
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
process.stderr.write(`${result.message}\n`);
|
|
62
|
+
}
|
|
63
|
+
return result.code;
|
|
64
|
+
}
|
|
65
|
+
if (process.argv[1] !== undefined && fileURLToPath(import.meta.url) === process.argv[1]) {
|
|
66
|
+
process.exit(run(process.argv.slice(2)));
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=verify-lifecycle-visible.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deftai/directive",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.107.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.107.0",
|
|
38
|
+
"@deftai/directive-content": "^0.107.0"
|
|
39
39
|
},
|
|
40
40
|
"scripts": {
|
|
41
41
|
"build": "tsc -b"
|