@deftai/directive 0.119.8 → 0.119.9
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/cli-router/route-argv.js +6 -0
- package/dist/dispatch.d.ts +1 -1
- package/dist/dispatch.js +15 -0
- package/dist/policy.d.ts +1 -1
- package/dist/policy.js +69 -15
- package/dist/preflight-gh.d.ts +1 -0
- package/dist/preflight-gh.js +16 -74
- package/dist/scope-record-intent-constraint.d.ts +1 -0
- package/dist/scope-record-intent-constraint.js +28 -4
- package/dist/scope-record-observable-scope.d.ts +1 -0
- package/dist/scope-record-observable-scope.js +28 -4
- package/dist/verify-class-checks.d.ts +11 -0
- package/dist/verify-class-checks.js +71 -0
- package/dist/verify-review-monitor.d.ts +5 -0
- package/dist/verify-review-monitor.js +82 -2
- package/dist/verify-session-ritual.js +5 -3
- package/dist/windows-bin-ps1.d.ts +39 -0
- package/dist/windows-bin-ps1.js +224 -0
- package/package.json +7 -5
- package/scripts/remove-windows-ps1-shims.mjs +26 -0
|
@@ -29,6 +29,7 @@ export const SCOPE_LIFECYCLE_VERBS = new Set([
|
|
|
29
29
|
"block",
|
|
30
30
|
"unblock",
|
|
31
31
|
"stamp-evidence",
|
|
32
|
+
"bind-clause",
|
|
32
33
|
]);
|
|
33
34
|
/** pr:* task names that do not hyphenate 1:1 to handler stems. */
|
|
34
35
|
export const PR_VERB_MAP = {
|
|
@@ -82,6 +83,8 @@ export const SUBCOMMAND_ROUTES = {
|
|
|
82
83
|
"policy:enable-directive": ["policy", "enable-directive"],
|
|
83
84
|
"policy:enforce-branches": ["policy", "enforce-branches"],
|
|
84
85
|
"policy:allow-direct-commits": ["policy", "allow-direct-commits"],
|
|
86
|
+
"policy:allow-destructive-gh-verbs": ["policy", "allow-destructive-gh-verbs"],
|
|
87
|
+
"policy:enforce-destructive-gh-verbs": ["policy", "enforce-destructive-gh-verbs"],
|
|
85
88
|
"policy:allow-bot-merge": ["policy", "allow-bot-merge"],
|
|
86
89
|
"policy:enable-value-feedback": ["policy", "enable-value-feedback"],
|
|
87
90
|
"policy:disable-host-hooks": ["policy", "disable-host-hooks"],
|
|
@@ -207,6 +210,9 @@ function routeNamespaceVerb(ns, verb, rest) {
|
|
|
207
210
|
if (ns === "scm" && verb === "issue:design-critique-chip") {
|
|
208
211
|
return { kind: "dispatch", argv: ["scm", "issue", "design-critique-chip", ...rest] };
|
|
209
212
|
}
|
|
213
|
+
if (ns === "scm" && verb === "issue:design-critique-stale-ready") {
|
|
214
|
+
return { kind: "dispatch", argv: ["scm", "issue", "design-critique-stale-ready", ...rest] };
|
|
215
|
+
}
|
|
210
216
|
if (ns === "scm" && verb === "issue:work-claim") {
|
|
211
217
|
return { kind: "dispatch", argv: ["scm", "issue", "work-claim", ...rest] };
|
|
212
218
|
}
|
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", "release-wait-npm", "scope-lifecycle", "scope-record-approved-scope", "scope-record-observable-scope", "scope-record-intent-constraint", "scm-sync-default", "lifecycle-event", "lifecycle-stats", "session-start", "session-ready", "occupancy-steal", "occupancy-release", "occupancy-heartbeat", "occupancy-grant", "plan-sequence", "slice", "subagent-monitor", "toolchain-check", "triage-actions", "triage-bootstrap", "triage-bulk", "triage-classify", "triage-strip-withdrawn", "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-deposit-closure", "verify-consumer-hard-stops", "verify-closing-keywords", "verify-skill-external-fetch-gate", "verify-semantic-single-source", "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", "verify-spec-prd-fresh", "rule-ownership-lint", "verify-story-ready", "verify-review-monitor", "verify-l4-owner", "verify-subagent-alive", "verify-subagent-steer", "review-monitor-register", "review-monitor-release", "verify-tools", "verify-wip-cap", "verify-orphan-active", "verify-pr-closeout-attestable", "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", "verify-evaluator-surface", "verify-observable-scope", "verify-intent-constraint", "verify-consumer-test-lane", "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", "release-wait-npm", "scope-lifecycle", "scope-record-approved-scope", "scope-record-observable-scope", "scope-record-intent-constraint", "scm-sync-default", "lifecycle-event", "lifecycle-stats", "session-start", "session-ready", "occupancy-steal", "occupancy-release", "occupancy-heartbeat", "occupancy-grant", "plan-sequence", "slice", "subagent-monitor", "toolchain-check", "triage-actions", "triage-bootstrap", "triage-bulk", "triage-classify", "triage-strip-withdrawn", "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", "xbrief-adopt-stored-plan-id", "verify-branch", "verify-encoding", "verify-forward-coverage", "verify-test-boundary", "verify-class-checks", "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-deposit-closure", "verify-consumer-hard-stops", "verify-closing-keywords", "verify-skill-external-fetch-gate", "verify-semantic-single-source", "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", "verify-spec-prd-fresh", "rule-ownership-lint", "verify-story-ready", "verify-review-monitor", "verify-l4-owner", "verify-subagent-alive", "verify-subagent-steer", "review-monitor-register", "review-monitor-release", "verify-tools", "verify-wip-cap", "verify-orphan-active", "verify-pr-closeout-attestable", "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", "verify-evaluator-surface", "verify-observable-scope", "verify-intent-constraint", "verify-consumer-test-lane", "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", "project-write-narratives", "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", "docs-impact"];
|
|
16
16
|
/** Colon aliases for triage-actions (mirrors cli-router SUBCOMMAND_ROUTES). */
|
package/dist/dispatch.js
CHANGED
|
@@ -111,10 +111,12 @@ export const CLI_MODULE_VERBS = [
|
|
|
111
111
|
"vbrief-validation",
|
|
112
112
|
"xbrief-create",
|
|
113
113
|
"xbrief-verify",
|
|
114
|
+
"xbrief-adopt-stored-plan-id",
|
|
114
115
|
"verify-branch",
|
|
115
116
|
"verify-encoding",
|
|
116
117
|
"verify-forward-coverage",
|
|
117
118
|
"verify-test-boundary",
|
|
119
|
+
"verify-class-checks",
|
|
118
120
|
"verify-scope-provenance",
|
|
119
121
|
"verify-consumer-check-contract",
|
|
120
122
|
"verify-hooks-installed",
|
|
@@ -242,6 +244,8 @@ export const POLICY_ACTION_ALIAS_SUBCOMMANDS = {
|
|
|
242
244
|
"policy:show": "show",
|
|
243
245
|
"policy:enforce-branches": "enforce-branches",
|
|
244
246
|
"policy:allow-direct-commits": "allow-direct-commits",
|
|
247
|
+
"policy:allow-destructive-gh-verbs": "allow-destructive-gh-verbs",
|
|
248
|
+
"policy:enforce-destructive-gh-verbs": "enforce-destructive-gh-verbs",
|
|
245
249
|
"policy:allow-bot-merge": "allow-bot-merge",
|
|
246
250
|
"policy:enable-value-feedback": "enable-value-feedback",
|
|
247
251
|
"policy:disable-host-hooks": "disable-host-hooks",
|
|
@@ -299,6 +303,7 @@ export const VERB_ALIASES = {
|
|
|
299
303
|
"verify:encoding": "verify-encoding",
|
|
300
304
|
"verify:forward-coverage": "verify-forward-coverage",
|
|
301
305
|
"verify:test-boundary": "verify-test-boundary",
|
|
306
|
+
"verify:class-checks": "verify-class-checks",
|
|
302
307
|
"verify:scope-provenance": "verify-scope-provenance",
|
|
303
308
|
"scope:record-approved-scope": "scope-record-approved-scope",
|
|
304
309
|
"scope:record-observable-scope": "scope-record-observable-scope",
|
|
@@ -337,6 +342,7 @@ export const VERB_ALIASES = {
|
|
|
337
342
|
"xbrief:preflight": "vbrief-preflight",
|
|
338
343
|
"xbrief:create": "xbrief-create",
|
|
339
344
|
"xbrief:verify": "xbrief-verify",
|
|
345
|
+
"xbrief:adopt-stored-plan-id": "xbrief-adopt-stored-plan-id",
|
|
340
346
|
"vbrief:activate": "vbrief-activate",
|
|
341
347
|
"verify:story-ready": "verify-story-ready",
|
|
342
348
|
"verify:review-monitor": "verify-review-monitor",
|
|
@@ -478,6 +484,7 @@ const WRAPPER_CLI_STEMS = new Set([
|
|
|
478
484
|
"vbrief-validation",
|
|
479
485
|
"xbrief-create",
|
|
480
486
|
"xbrief-verify",
|
|
487
|
+
"xbrief-adopt-stored-plan-id",
|
|
481
488
|
]);
|
|
482
489
|
function emitCliResult(result, io) {
|
|
483
490
|
if (result.stdout)
|
|
@@ -553,6 +560,10 @@ async function loadWrapperCliHandler(stem, io) {
|
|
|
553
560
|
const { runXbriefVerifyCli } = await import("@deftai/directive-core/xbrief");
|
|
554
561
|
return (argv) => emitCliResult(runXbriefVerifyCli(argv), io);
|
|
555
562
|
}
|
|
563
|
+
case "xbrief-adopt-stored-plan-id": {
|
|
564
|
+
const { runAdoptStoredPlanIdCli } = await import("@deftai/directive-core/xbrief");
|
|
565
|
+
return (argv) => emitCliResult(runAdoptStoredPlanIdCli(argv), io);
|
|
566
|
+
}
|
|
556
567
|
default:
|
|
557
568
|
throw new Error(`no wrapper handler for ${stem}`);
|
|
558
569
|
}
|
|
@@ -2771,6 +2782,10 @@ const CURATED_HELP_GROUPS = [
|
|
|
2771
2782
|
name: "xbrief:verify",
|
|
2772
2783
|
summary: "Verify a dense xBRIEF artifact at --out (requires --format); not scope:*",
|
|
2773
2784
|
},
|
|
2785
|
+
{
|
|
2786
|
+
name: "xbrief:adopt-stored-plan-id",
|
|
2787
|
+
summary: "Set plan.id from the stored plan-id binding; refuse if another artifact has it",
|
|
2788
|
+
},
|
|
2774
2789
|
],
|
|
2775
2790
|
},
|
|
2776
2791
|
{
|
package/dist/policy.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ 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-host-hooks" | "disable-directive" | "enable-directive" | "resolve";
|
|
11
|
+
cmd: "show" | "enforce-branches" | "allow-direct-commits" | "allow-destructive-gh-verbs" | "enforce-destructive-gh-verbs" | "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;
|
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, 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";
|
|
9
|
+
import { ALLOW_BOT_MERGE_CAPABILITY_COST, ALLOW_DESTRUCTIVE_GH_VERBS_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, setAllowDestructiveGhVerbs, 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-host-hooks|disable-directive|enable-directive|resolve] ...";
|
|
117
|
+
const usage = "usage: policy [show|enforce-branches|allow-direct-commits|allow-destructive-gh-verbs|enforce-destructive-gh-verbs|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];
|
|
@@ -148,6 +148,8 @@ export function parseArgs(argv) {
|
|
|
148
148
|
}
|
|
149
149
|
if (cmd === "enforce-branches" ||
|
|
150
150
|
cmd === "allow-direct-commits" ||
|
|
151
|
+
cmd === "allow-destructive-gh-verbs" ||
|
|
152
|
+
cmd === "enforce-destructive-gh-verbs" ||
|
|
151
153
|
cmd === "allow-bot-merge" ||
|
|
152
154
|
cmd === "enable-value-feedback" ||
|
|
153
155
|
cmd === "clear-value-feedback" ||
|
|
@@ -160,19 +162,23 @@ export function parseArgs(argv) {
|
|
|
160
162
|
? policyColonInvocation("enforce-branches")
|
|
161
163
|
: cmd === "allow-direct-commits"
|
|
162
164
|
? policyColonInvocation("allow-direct-commits")
|
|
163
|
-
: cmd === "allow-
|
|
164
|
-
? policyColonInvocation("allow-
|
|
165
|
-
: cmd === "
|
|
166
|
-
? policyColonInvocation("
|
|
167
|
-
: cmd === "
|
|
168
|
-
? policyColonInvocation("
|
|
169
|
-
: cmd === "
|
|
170
|
-
? policyColonInvocation("
|
|
171
|
-
: cmd === "
|
|
172
|
-
? policyColonInvocation("
|
|
173
|
-
: cmd === "
|
|
174
|
-
? policyColonInvocation("
|
|
175
|
-
:
|
|
165
|
+
: cmd === "allow-destructive-gh-verbs"
|
|
166
|
+
? policyColonInvocation("allow-destructive-gh-verbs")
|
|
167
|
+
: cmd === "enforce-destructive-gh-verbs"
|
|
168
|
+
? policyColonInvocation("enforce-destructive-gh-verbs")
|
|
169
|
+
: cmd === "allow-bot-merge"
|
|
170
|
+
? policyColonInvocation("allow-bot-merge")
|
|
171
|
+
: cmd === "enable-value-feedback"
|
|
172
|
+
? policyColonInvocation("enable-value-feedback")
|
|
173
|
+
: cmd === "clear-value-feedback"
|
|
174
|
+
? policyColonInvocation("clear-value-feedback")
|
|
175
|
+
: cmd === "set-ceremony-dial"
|
|
176
|
+
? policyColonInvocation("set-ceremony-dial")
|
|
177
|
+
: cmd === "disable-host-hooks"
|
|
178
|
+
? policyColonInvocation("disable-host-hooks")
|
|
179
|
+
: cmd === "disable-directive"
|
|
180
|
+
? policyColonInvocation("disable-directive")
|
|
181
|
+
: policyColonInvocation("enable-directive");
|
|
176
182
|
let note = "";
|
|
177
183
|
let projectRoot = ".";
|
|
178
184
|
let host;
|
|
@@ -476,6 +482,51 @@ function runEnableDirective(args) {
|
|
|
476
482
|
process.stdout.write("Directive opt-out cleared. Run `directive init` or `directive update` to ensure install.\n");
|
|
477
483
|
return 0;
|
|
478
484
|
}
|
|
485
|
+
/** Allow or re-enforce the #1019 destructive-gh-verb gate. */
|
|
486
|
+
function runDestructiveGhVerbs(args) {
|
|
487
|
+
const projectRoot = pathResolve(args.projectRoot);
|
|
488
|
+
const allow = args.cmd === "allow-destructive-gh-verbs";
|
|
489
|
+
if (allow && !args.confirm) {
|
|
490
|
+
process.stdout.write(`${ALLOW_DESTRUCTIVE_GH_VERBS_CAPABILITY_COST}\n\n`);
|
|
491
|
+
process.stdout.write(`Re-run with --confirm to apply: ${policyColonInvocation("allow-destructive-gh-verbs", " -- --confirm")}\n`);
|
|
492
|
+
return 1;
|
|
493
|
+
}
|
|
494
|
+
try {
|
|
495
|
+
const result = setAllowDestructiveGhVerbs(projectRoot, {
|
|
496
|
+
allowDestructiveGhVerbs: allow,
|
|
497
|
+
actor: args.actor,
|
|
498
|
+
note: args.note,
|
|
499
|
+
});
|
|
500
|
+
if (!result.ok) {
|
|
501
|
+
process.stderr.write(`\u274c ${result.message}\n`);
|
|
502
|
+
if (result.message.includes("PROJECT-DEFINITION not found")) {
|
|
503
|
+
const pdRel = relative(projectRoot, projectDefinitionPath(projectRoot));
|
|
504
|
+
process.stderr.write(` Recovery: run \`task setup\` to generate ${pdRel}.\n`);
|
|
505
|
+
}
|
|
506
|
+
return 2;
|
|
507
|
+
}
|
|
508
|
+
process.stdout.write(`\u2713 plan.policy.allowDestructiveGhVerbs=${allow ? "true" : "false"} ` +
|
|
509
|
+
`(#1019 gate ${allow ? "OFF" : "ON"}).\n`);
|
|
510
|
+
if (result.changed) {
|
|
511
|
+
process.stdout.write(` audit: meta/policy-changes.log :: ${result.auditEntry}\n`);
|
|
512
|
+
}
|
|
513
|
+
else {
|
|
514
|
+
process.stdout.write(`${POLICY_AUDIT_NOOP_STDOUT}\n`);
|
|
515
|
+
}
|
|
516
|
+
return 0;
|
|
517
|
+
}
|
|
518
|
+
catch (err) {
|
|
519
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
520
|
+
if (message.includes("PROJECT-DEFINITION not found")) {
|
|
521
|
+
process.stderr.write(`\u274c ${message}\n`);
|
|
522
|
+
const pdRel = relative(projectRoot, projectDefinitionPath(projectRoot));
|
|
523
|
+
process.stderr.write(` Recovery: run \`task setup\` to generate ${pdRel}.\n`);
|
|
524
|
+
return 2;
|
|
525
|
+
}
|
|
526
|
+
process.stderr.write(`\u274c Config error: ${message}\n`);
|
|
527
|
+
return 2;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
479
530
|
/** Allow agent/bot merge by writing requireHumanMerge=false (#1193). */
|
|
480
531
|
function runAllowBotMerge(args) {
|
|
481
532
|
const projectRoot = pathResolve(args.projectRoot);
|
|
@@ -539,6 +590,9 @@ export function run(argv) {
|
|
|
539
590
|
if (args.cmd === "enforce-branches" || args.cmd === "allow-direct-commits") {
|
|
540
591
|
return runSet(args);
|
|
541
592
|
}
|
|
593
|
+
if (args.cmd === "allow-destructive-gh-verbs" || args.cmd === "enforce-destructive-gh-verbs") {
|
|
594
|
+
return runDestructiveGhVerbs(args);
|
|
595
|
+
}
|
|
542
596
|
if (args.cmd === "allow-bot-merge") {
|
|
543
597
|
return runAllowBotMerge(args);
|
|
544
598
|
}
|
package/dist/preflight-gh.d.ts
CHANGED
package/dist/preflight-gh.js
CHANGED
|
@@ -7,11 +7,13 @@
|
|
|
7
7
|
* deft-ts preflight-gh --command "<gh ...>"
|
|
8
8
|
* deft-ts preflight-gh --pre-push-stdin (reads from stdin)
|
|
9
9
|
*
|
|
10
|
-
* Thin shim -- delegates
|
|
10
|
+
* Thin shim -- delegates through @deftai/directive-core/preflight
|
|
11
|
+
* (re-exports the preflight-gh API; vitest already aliases that subpath).
|
|
12
|
+
* --project-root is consulted by evaluatePrePush / evaluateCommand (#4384).
|
|
11
13
|
*/
|
|
12
14
|
import { createInterface } from "node:readline";
|
|
13
15
|
import { fileURLToPath } from "node:url";
|
|
14
|
-
import { DEFAULT_BRANCHES,
|
|
16
|
+
import { DEFAULT_BRANCHES, evaluateCommand, evaluatePrePush, parsePrePushStdin, runSelfTest, } from "@deftai/directive-core/preflight";
|
|
15
17
|
export function parseArgs(argv) {
|
|
16
18
|
const parsed = { defaultBranches: new Set(DEFAULT_BRANCHES) };
|
|
17
19
|
for (let i = 0; i < argv.length; i++) {
|
|
@@ -44,7 +46,11 @@ export function parseArgs(argv) {
|
|
|
44
46
|
i++;
|
|
45
47
|
}
|
|
46
48
|
else if (arg === "--project-root") {
|
|
47
|
-
|
|
49
|
+
const next = argv[i + 1];
|
|
50
|
+
if (next === undefined) {
|
|
51
|
+
return { ...parsed, error: "argument --project-root: expected one argument" };
|
|
52
|
+
}
|
|
53
|
+
parsed.projectRoot = next;
|
|
48
54
|
i++;
|
|
49
55
|
}
|
|
50
56
|
else {
|
|
@@ -73,7 +79,9 @@ export function run(argv) {
|
|
|
73
79
|
return code;
|
|
74
80
|
}
|
|
75
81
|
if (args.mode === "command" && args.command !== undefined) {
|
|
76
|
-
const [code, msg] = evaluateCommand(args.command, branches
|
|
82
|
+
const [code, msg] = evaluateCommand(args.command, branches, {
|
|
83
|
+
projectRoot: args.projectRoot,
|
|
84
|
+
});
|
|
77
85
|
if (code === 0) {
|
|
78
86
|
if (!quiet)
|
|
79
87
|
process.stdout.write(`${msg}\n`);
|
|
@@ -84,85 +92,19 @@ export function run(argv) {
|
|
|
84
92
|
return code;
|
|
85
93
|
}
|
|
86
94
|
if (args.mode === "pre-push-stdin") {
|
|
87
|
-
return runPrePushStdin(branches, quiet);
|
|
95
|
+
return runPrePushStdin(branches, quiet, args.projectRoot);
|
|
88
96
|
}
|
|
89
97
|
process.stderr.write("preflight-gh: one of --self-test / --command / --pre-push-stdin required\n");
|
|
90
98
|
return 2;
|
|
91
99
|
}
|
|
92
|
-
function
|
|
93
|
-
return text
|
|
94
|
-
.split("\n")
|
|
95
|
-
.map((l) => l.trim())
|
|
96
|
-
.filter((l) => l.length > 0)
|
|
97
|
-
.flatMap((l) => {
|
|
98
|
-
const parts = l.split(/\s+/);
|
|
99
|
-
if (parts.length !== 4)
|
|
100
|
-
return [];
|
|
101
|
-
return [
|
|
102
|
-
{
|
|
103
|
-
localRef: parts[0] ?? "",
|
|
104
|
-
localOid: parts[1] ?? "",
|
|
105
|
-
remoteRef: parts[2] ?? "",
|
|
106
|
-
remoteOid: parts[3] ?? "",
|
|
107
|
-
},
|
|
108
|
-
];
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
const ZERO_OID_RE = /^0+$/;
|
|
112
|
-
function evaluatePrePush(refs, branches) {
|
|
113
|
-
if (refs.length === 0) {
|
|
114
|
-
return [0, "✓ deft destructive-gh-verb gate (pre-push): no refs in stdin -- nothing to gate."];
|
|
115
|
-
}
|
|
116
|
-
const branchesLower = new Set([...branches].map((b) => b.toLowerCase()));
|
|
117
|
-
const blocked = [];
|
|
118
|
-
for (const { localRef, localOid, remoteRef, remoteOid } of refs) {
|
|
119
|
-
const branch = remoteRef.replace(/^refs\/heads\//, "");
|
|
120
|
-
if (!branchesLower.has(branch.toLowerCase()))
|
|
121
|
-
continue;
|
|
122
|
-
if (ZERO_OID_RE.test(remoteOid)) {
|
|
123
|
-
blocked.push(`create ${branch} (local=${localRef})`);
|
|
124
|
-
}
|
|
125
|
-
else if (ZERO_OID_RE.test(localOid)) {
|
|
126
|
-
blocked.push(`delete ${branch}`);
|
|
127
|
-
}
|
|
128
|
-
else {
|
|
129
|
-
blocked.push(`update ${branch} (local=${localRef})`);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
if (blocked.length === 0) {
|
|
133
|
-
return [
|
|
134
|
-
0,
|
|
135
|
-
"✓ deft destructive-gh-verb gate (pre-push): no pushes to default branches detected -- proceeding.",
|
|
136
|
-
];
|
|
137
|
-
}
|
|
138
|
-
const bypass = (process.env[ENV_BYPASS] ?? "").trim().toLowerCase();
|
|
139
|
-
if (["1", "true", "yes", "on"].includes(bypass)) {
|
|
140
|
-
return [
|
|
141
|
-
0,
|
|
142
|
-
`⚠ deft destructive-gh-verb gate (pre-push): default-branch push detected (${blocked.join("; ")}) but ${ENV_BYPASS}=1 is set -- policy bypassed for this invocation.`,
|
|
143
|
-
];
|
|
144
|
-
}
|
|
145
|
-
return [
|
|
146
|
-
1,
|
|
147
|
-
[
|
|
148
|
-
"❌ deft destructive-gh-verb gate (pre-push): refusing to push directly to the default branch.",
|
|
149
|
-
` Detail: ${blocked.join("; ")}`,
|
|
150
|
-
"",
|
|
151
|
-
" How to proceed:",
|
|
152
|
-
" • push to a feature branch and open a PR",
|
|
153
|
-
` • or set the env-var bypass for this shell: ${ENV_BYPASS}=1`,
|
|
154
|
-
" See scm/github.md (## Destructive gh verbs (#1019)).",
|
|
155
|
-
].join("\n"),
|
|
156
|
-
];
|
|
157
|
-
}
|
|
158
|
-
function runPrePushStdin(branches, quiet) {
|
|
100
|
+
function runPrePushStdin(branches, quiet, projectRoot) {
|
|
159
101
|
return new Promise((resolve) => {
|
|
160
102
|
const lines = [];
|
|
161
103
|
const rl = createInterface({ input: process.stdin });
|
|
162
104
|
rl.on("line", (l) => lines.push(l));
|
|
163
105
|
rl.on("close", () => {
|
|
164
|
-
const refs =
|
|
165
|
-
const [code, msg] = evaluatePrePush(refs, branches);
|
|
106
|
+
const refs = parsePrePushStdin(lines.join("\n"));
|
|
107
|
+
const [code, msg] = evaluatePrePush(refs, { branches, projectRoot });
|
|
166
108
|
if (code === 0) {
|
|
167
109
|
if (!quiet)
|
|
168
110
|
process.stdout.write(`${msg}\n`);
|
|
@@ -11,12 +11,18 @@ import { buildIntentConstraintRecord, extractIntentConstraintFromPlan, parseInte
|
|
|
11
11
|
import { isDirectEntrypoint } from "./entrypoint.js";
|
|
12
12
|
import { refuseMintWhileUatActive, refuseNonInteractiveMint, resolveHumanPresenceMintSeams, } from "./human-presence-mint.js";
|
|
13
13
|
import { isPathInsideRoot } from "./scope-record-approved-scope.js";
|
|
14
|
+
const DEFAULT_MINTED_VIA = "scope:record-intent-constraint";
|
|
15
|
+
const ALLOWED_MINTED_VIA = new Set([DEFAULT_MINTED_VIA, "in-harness-ask"]);
|
|
14
16
|
function usage() {
|
|
15
17
|
return ("usage: scope:record-intent-constraint -- <xbrief-path> --actor <name> --confirm " +
|
|
16
|
-
"[--kind operator] [--
|
|
18
|
+
"[--kind operator] [--minted-via in-harness-ask|scope:record-intent-constraint] " +
|
|
19
|
+
"[--project-root <dir>] [--quiet]\n" +
|
|
17
20
|
' Writes .deft/intent-constraint/<plan-id>.json from plan["x-directive/intentConstraint"].\n' +
|
|
18
|
-
"
|
|
19
|
-
"
|
|
21
|
+
" Attended mid-build (#5010): ask in parent chat; on yes the human runs this verb on a real TTY " +
|
|
22
|
+
"with --minted-via=in-harness-ask (attestation is not an agent-shell bypass). Commit the record on " +
|
|
23
|
+
"the delivery-branch merge base, then rebase the implementation branch — same-PR working-tree-only " +
|
|
24
|
+
"records are not authoritative. Unattended/C1: rewrite or park. --actor is display only. Mint still " +
|
|
25
|
+
"requires a real TTY, controlling terminal, --confirm, and typed phrase mint (#3110). Agent/CI shells refuse.");
|
|
20
26
|
}
|
|
21
27
|
function extractPlanId(payload) {
|
|
22
28
|
if (payload === null || typeof payload !== "object" || Array.isArray(payload))
|
|
@@ -33,6 +39,7 @@ export function parseArgs(argv) {
|
|
|
33
39
|
xbriefPath: "",
|
|
34
40
|
actor: "",
|
|
35
41
|
kind: "operator",
|
|
42
|
+
mintedVia: DEFAULT_MINTED_VIA,
|
|
36
43
|
quiet: false,
|
|
37
44
|
confirm: false,
|
|
38
45
|
};
|
|
@@ -77,6 +84,16 @@ export function parseArgs(argv) {
|
|
|
77
84
|
else if (arg?.startsWith("--kind=")) {
|
|
78
85
|
parsed.kind = arg.slice("--kind=".length);
|
|
79
86
|
}
|
|
87
|
+
else if (arg === "--minted-via") {
|
|
88
|
+
const value = argv[i + 1];
|
|
89
|
+
if (value === undefined)
|
|
90
|
+
return { ...parsed, error: "argument --minted-via: expected one argument" };
|
|
91
|
+
parsed.mintedVia = value;
|
|
92
|
+
i += 1;
|
|
93
|
+
}
|
|
94
|
+
else if (arg?.startsWith("--minted-via=")) {
|
|
95
|
+
parsed.mintedVia = arg.slice("--minted-via=".length);
|
|
96
|
+
}
|
|
80
97
|
else if (arg?.startsWith("-")) {
|
|
81
98
|
return { ...parsed, error: `unrecognized argument: ${arg}` };
|
|
82
99
|
}
|
|
@@ -98,6 +115,13 @@ export function parseArgs(argv) {
|
|
|
98
115
|
error: `argument --actor is required (human operator identity)\n${usage()}`,
|
|
99
116
|
};
|
|
100
117
|
}
|
|
118
|
+
if (!ALLOWED_MINTED_VIA.has(parsed.mintedVia.trim())) {
|
|
119
|
+
return {
|
|
120
|
+
...parsed,
|
|
121
|
+
error: `argument --minted-via must be one of: ${[...ALLOWED_MINTED_VIA].join(", ")}\n${usage()}`,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
parsed.mintedVia = parsed.mintedVia.trim();
|
|
101
125
|
return parsed;
|
|
102
126
|
}
|
|
103
127
|
export function run(argv, seams = {}) {
|
|
@@ -159,7 +183,7 @@ export function run(argv, seams = {}) {
|
|
|
159
183
|
kind: args.kind,
|
|
160
184
|
actor: args.actor,
|
|
161
185
|
mintedAt: new Date().toISOString().replace(/\.\d{3}Z$/, "Z"),
|
|
162
|
-
mintedVia:
|
|
186
|
+
mintedVia: args.mintedVia,
|
|
163
187
|
},
|
|
164
188
|
});
|
|
165
189
|
if ("error" in rec) {
|
|
@@ -11,12 +11,18 @@ import { buildObservableScopeRecord, extractObservableChangeFromPlan, parseObser
|
|
|
11
11
|
import { isDirectEntrypoint } from "./entrypoint.js";
|
|
12
12
|
import { refuseMintWhileUatActive, refuseNonInteractiveMint, resolveHumanPresenceMintSeams, } from "./human-presence-mint.js";
|
|
13
13
|
import { isPathInsideRoot } from "./scope-record-approved-scope.js";
|
|
14
|
+
const DEFAULT_MINTED_VIA = "scope:record-observable-scope";
|
|
15
|
+
const ALLOWED_MINTED_VIA = new Set([DEFAULT_MINTED_VIA, "in-harness-ask"]);
|
|
14
16
|
function usage() {
|
|
15
17
|
return ("usage: scope:record-observable-scope -- <xbrief-path> --actor <name> --confirm " +
|
|
16
|
-
"[--kind operator] [--
|
|
18
|
+
"[--kind operator] [--minted-via in-harness-ask|scope:record-observable-scope] " +
|
|
19
|
+
"[--project-root <dir>] [--quiet]\n" +
|
|
17
20
|
' Writes .deft/observable-scope/<plan-id>.json from plan["x-directive/observableChange"].\n' +
|
|
18
|
-
"
|
|
19
|
-
"
|
|
21
|
+
" Attended mid-build (#5010): ask in parent chat; on yes the human runs this verb on a real TTY " +
|
|
22
|
+
"with --minted-via=in-harness-ask (attestation is not an agent-shell bypass). Commit the record on " +
|
|
23
|
+
"the delivery-branch merge base, then rebase the implementation branch — same-PR working-tree-only " +
|
|
24
|
+
"records are not authoritative. Unattended/C1: rewrite or park. --actor is display only. Mint still " +
|
|
25
|
+
"requires a real TTY, controlling terminal, --confirm, and typed phrase mint (#3110). Agent/CI shells refuse.");
|
|
20
26
|
}
|
|
21
27
|
function extractPlanId(payload) {
|
|
22
28
|
if (payload === null || typeof payload !== "object" || Array.isArray(payload))
|
|
@@ -33,6 +39,7 @@ export function parseArgs(argv) {
|
|
|
33
39
|
xbriefPath: "",
|
|
34
40
|
actor: "",
|
|
35
41
|
kind: "operator",
|
|
42
|
+
mintedVia: DEFAULT_MINTED_VIA,
|
|
36
43
|
quiet: false,
|
|
37
44
|
confirm: false,
|
|
38
45
|
};
|
|
@@ -77,6 +84,16 @@ export function parseArgs(argv) {
|
|
|
77
84
|
else if (arg?.startsWith("--kind=")) {
|
|
78
85
|
parsed.kind = arg.slice("--kind=".length);
|
|
79
86
|
}
|
|
87
|
+
else if (arg === "--minted-via") {
|
|
88
|
+
const value = argv[i + 1];
|
|
89
|
+
if (value === undefined)
|
|
90
|
+
return { ...parsed, error: "argument --minted-via: expected one argument" };
|
|
91
|
+
parsed.mintedVia = value;
|
|
92
|
+
i += 1;
|
|
93
|
+
}
|
|
94
|
+
else if (arg?.startsWith("--minted-via=")) {
|
|
95
|
+
parsed.mintedVia = arg.slice("--minted-via=".length);
|
|
96
|
+
}
|
|
80
97
|
else if (arg?.startsWith("-")) {
|
|
81
98
|
return { ...parsed, error: `unrecognized argument: ${arg}` };
|
|
82
99
|
}
|
|
@@ -98,6 +115,13 @@ export function parseArgs(argv) {
|
|
|
98
115
|
error: `argument --actor is required (human operator identity)\n${usage()}`,
|
|
99
116
|
};
|
|
100
117
|
}
|
|
118
|
+
if (!ALLOWED_MINTED_VIA.has(parsed.mintedVia.trim())) {
|
|
119
|
+
return {
|
|
120
|
+
...parsed,
|
|
121
|
+
error: `argument --minted-via must be one of: ${[...ALLOWED_MINTED_VIA].join(", ")}\n${usage()}`,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
parsed.mintedVia = parsed.mintedVia.trim();
|
|
101
125
|
return parsed;
|
|
102
126
|
}
|
|
103
127
|
export function run(argv, seams = {}) {
|
|
@@ -161,7 +185,7 @@ export function run(argv, seams = {}) {
|
|
|
161
185
|
kind: args.kind,
|
|
162
186
|
actor: args.actor,
|
|
163
187
|
mintedAt: new Date().toISOString().replace(/\.\d{3}Z$/, "Z"),
|
|
164
|
-
mintedVia:
|
|
188
|
+
mintedVia: args.mintedVia,
|
|
165
189
|
},
|
|
166
190
|
});
|
|
167
191
|
if ("error" in rec) {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
interface ParsedArgs {
|
|
3
|
+
projectRoot: string;
|
|
4
|
+
baseRef: string | null;
|
|
5
|
+
quiet: boolean;
|
|
6
|
+
error?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function parseArgs(argv: string[]): ParsedArgs;
|
|
9
|
+
export declare function run(argv: string[]): number;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=verify-class-checks.d.ts.map
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* CLI for verify:class-checks (#4980 / #3145 class).
|
|
4
|
+
*/
|
|
5
|
+
import { resolve } from "node:path";
|
|
6
|
+
import { classChecks } from "@deftai/directive-core";
|
|
7
|
+
import { isDirectEntrypoint } from "./entrypoint.js";
|
|
8
|
+
export function parseArgs(argv) {
|
|
9
|
+
const parsed = {
|
|
10
|
+
projectRoot: ".",
|
|
11
|
+
baseRef: null,
|
|
12
|
+
quiet: false,
|
|
13
|
+
};
|
|
14
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
15
|
+
const arg = argv[i];
|
|
16
|
+
if (arg === "--quiet") {
|
|
17
|
+
parsed.quiet = true;
|
|
18
|
+
}
|
|
19
|
+
else if (arg === "--project-root") {
|
|
20
|
+
const value = argv[i + 1];
|
|
21
|
+
if (value === undefined) {
|
|
22
|
+
return { ...parsed, error: "argument --project-root: expected one argument" };
|
|
23
|
+
}
|
|
24
|
+
parsed.projectRoot = value;
|
|
25
|
+
i += 1;
|
|
26
|
+
}
|
|
27
|
+
else if (arg?.startsWith("--project-root=")) {
|
|
28
|
+
parsed.projectRoot = arg.slice("--project-root=".length);
|
|
29
|
+
}
|
|
30
|
+
else if (arg === "--base-ref") {
|
|
31
|
+
const value = argv[i + 1];
|
|
32
|
+
if (value === undefined) {
|
|
33
|
+
return { ...parsed, error: "argument --base-ref: expected one argument" };
|
|
34
|
+
}
|
|
35
|
+
parsed.baseRef = value;
|
|
36
|
+
i += 1;
|
|
37
|
+
}
|
|
38
|
+
else if (arg?.startsWith("--base-ref=")) {
|
|
39
|
+
parsed.baseRef = arg.slice("--base-ref=".length);
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
return { ...parsed, error: `unrecognized argument: ${arg}` };
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return parsed;
|
|
46
|
+
}
|
|
47
|
+
export function run(argv) {
|
|
48
|
+
const args = parseArgs(argv);
|
|
49
|
+
if (args.error !== undefined) {
|
|
50
|
+
process.stderr.write(`verify_class_checks: ${args.error}\n`);
|
|
51
|
+
return 2;
|
|
52
|
+
}
|
|
53
|
+
const projectRoot = resolve(args.projectRoot);
|
|
54
|
+
const result = classChecks.evaluateClassChecks(projectRoot, {
|
|
55
|
+
baseRef: args.baseRef,
|
|
56
|
+
quiet: args.quiet,
|
|
57
|
+
});
|
|
58
|
+
if (result.exitCode === 0) {
|
|
59
|
+
if (!args.quiet) {
|
|
60
|
+
process.stdout.write(`${result.message}\n`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
process.stderr.write(`${result.message}\n`);
|
|
65
|
+
}
|
|
66
|
+
return result.exitCode;
|
|
67
|
+
}
|
|
68
|
+
if (isDirectEntrypoint(import.meta.url)) {
|
|
69
|
+
process.exit(run(process.argv.slice(2)));
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=verify-class-checks.js.map
|
|
@@ -9,6 +9,11 @@ interface ParsedArgs {
|
|
|
9
9
|
approach3: boolean;
|
|
10
10
|
approach3Warned: boolean;
|
|
11
11
|
emitJson: boolean;
|
|
12
|
+
/** When true, also run the #4882 merge-path arm observer (attested flags). */
|
|
13
|
+
mergePathArm: boolean;
|
|
14
|
+
liveWait: boolean;
|
|
15
|
+
explicitFinish: boolean;
|
|
16
|
+
stickyLease: boolean;
|
|
12
17
|
help: boolean;
|
|
13
18
|
error?: string;
|
|
14
19
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
|
-
import {
|
|
4
|
+
import { evaluateMergePathArm, } from "@deftai/directive-core/dist/pr-watch/main.js";
|
|
5
|
+
import { EXIT_CONFIG_ERROR, EXIT_NOT_READY, evaluateReviewMonitorGate, isTier1, REVIEW_MONITOR_HELP, verifyResultToJson, } from "@deftai/directive-core/review-monitor";
|
|
5
6
|
const CALL_SITES = new Set([
|
|
6
7
|
"solo",
|
|
7
8
|
"swarm-phase5-6",
|
|
@@ -18,6 +19,10 @@ export function parseVerifyReviewMonitorArgs(argv) {
|
|
|
18
19
|
approach3: false,
|
|
19
20
|
approach3Warned: false,
|
|
20
21
|
emitJson: false,
|
|
22
|
+
mergePathArm: false,
|
|
23
|
+
liveWait: false,
|
|
24
|
+
explicitFinish: false,
|
|
25
|
+
stickyLease: false,
|
|
21
26
|
help: false,
|
|
22
27
|
};
|
|
23
28
|
for (let i = 0; i < argv.length; i += 1) {
|
|
@@ -28,6 +33,18 @@ export function parseVerifyReviewMonitorArgs(argv) {
|
|
|
28
33
|
if (arg === "--json") {
|
|
29
34
|
acc.emitJson = true;
|
|
30
35
|
}
|
|
36
|
+
else if (arg === "--merge-path-arm") {
|
|
37
|
+
acc.mergePathArm = true;
|
|
38
|
+
}
|
|
39
|
+
else if (arg === "--live-wait") {
|
|
40
|
+
acc.liveWait = true;
|
|
41
|
+
}
|
|
42
|
+
else if (arg === "--explicit-finish") {
|
|
43
|
+
acc.explicitFinish = true;
|
|
44
|
+
}
|
|
45
|
+
else if (arg === "--sticky-lease") {
|
|
46
|
+
acc.stickyLease = true;
|
|
47
|
+
}
|
|
31
48
|
else if (arg === "--approach3") {
|
|
32
49
|
acc.approach3 = true;
|
|
33
50
|
}
|
|
@@ -117,6 +134,13 @@ export function run(argv) {
|
|
|
117
134
|
const args = parseVerifyReviewMonitorArgs(argv);
|
|
118
135
|
if (args.help) {
|
|
119
136
|
process.stdout.write(REVIEW_MONITOR_HELP);
|
|
137
|
+
process.stdout.write("\n#4882 merge-path arm observer (optional):\n" +
|
|
138
|
+
" --merge-path-arm Fail closed when neither live wait nor explicit finish\n" +
|
|
139
|
+
" --live-wait Attest a still-running phase-correct wait for this PR\n" +
|
|
140
|
+
" (Tier 1: bound to gate lease evidence for --pr)\n" +
|
|
141
|
+
" --explicit-finish Attest option-C BLOCKED/FAILED finish for this PR\n" +
|
|
142
|
+
" --sticky-lease Attest a fresh sticky lease (not sufficient alone)\n" +
|
|
143
|
+
" Prefer Approach 1 / native pr:watch; homemade line-parsed --json is not an arm.\n");
|
|
120
144
|
return 0;
|
|
121
145
|
}
|
|
122
146
|
if (args.error !== undefined) {
|
|
@@ -129,6 +153,8 @@ export function run(argv) {
|
|
|
129
153
|
process.stderr.write("Try: task verify:review-monitor -- --help\n");
|
|
130
154
|
return 2;
|
|
131
155
|
}
|
|
156
|
+
// Always evaluate the gate first so --project-root / repo config errors stay
|
|
157
|
+
// exit 2 even when --merge-path-arm would otherwise fail closed as unarmed.
|
|
132
158
|
const result = evaluateReviewMonitorGate({
|
|
133
159
|
pr: args.pr,
|
|
134
160
|
projectRoot: resolve(args.projectRoot),
|
|
@@ -139,15 +165,69 @@ export function run(argv) {
|
|
|
139
165
|
approach3Warned: args.approach3Warned,
|
|
140
166
|
environ: process.env,
|
|
141
167
|
});
|
|
168
|
+
let arm = null;
|
|
169
|
+
if (args.mergePathArm) {
|
|
170
|
+
// Bind --live-wait to gate-observed lease evidence on Tier 1 for this PR.
|
|
171
|
+
// Bare flags must not arm when Tier 1 requires a lease and none is present.
|
|
172
|
+
const leaseEvidence = result.monitorRecord !== null;
|
|
173
|
+
const liveBound = args.liveWait && (!isTier1(result.tier) || leaseEvidence);
|
|
174
|
+
arm = evaluateMergePathArm({
|
|
175
|
+
livePhaseCorrectWait: liveBound,
|
|
176
|
+
explicitFinish: args.explicitFinish,
|
|
177
|
+
stickyLeaseActive: args.stickyLease || leaseEvidence,
|
|
178
|
+
});
|
|
179
|
+
if (args.liveWait && !liveBound && !args.explicitFinish && !arm.armed) {
|
|
180
|
+
arm = {
|
|
181
|
+
armed: false,
|
|
182
|
+
reason: "unarmed_stand_down",
|
|
183
|
+
message: `unarmed stand-down: --live-wait attestation unbound to lease evidence ` +
|
|
184
|
+
`for PR #${args.pr} (Tier 1); sticky lease alone is not a live arm (#4882)`,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
}
|
|
142
188
|
if (args.emitJson) {
|
|
143
|
-
|
|
189
|
+
const payload = verifyResultToJson(result);
|
|
190
|
+
if (arm !== null) {
|
|
191
|
+
payload.merge_path_arm = {
|
|
192
|
+
armed: arm.armed,
|
|
193
|
+
reason: arm.reason,
|
|
194
|
+
message: arm.message,
|
|
195
|
+
live_wait: args.liveWait,
|
|
196
|
+
explicit_finish: args.explicitFinish,
|
|
197
|
+
sticky_lease: args.stickyLease,
|
|
198
|
+
lease_evidence: result.monitorRecord !== null,
|
|
199
|
+
};
|
|
200
|
+
// Combined gate+arm: unarmed fails closed even when the monitor gate is ready.
|
|
201
|
+
if (!arm.armed && result.exitCode !== EXIT_CONFIG_ERROR) {
|
|
202
|
+
payload.ready = false;
|
|
203
|
+
payload.exit_code = EXIT_NOT_READY;
|
|
204
|
+
// Gate-ready → arm diagnosis; both failing → keep both messages.
|
|
205
|
+
payload.message = result.exitCode === 0 ? arm.message : `${result.message}\n${arm.message}`;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
|
|
209
|
+
}
|
|
210
|
+
else if (result.exitCode === EXIT_CONFIG_ERROR) {
|
|
211
|
+
process.stderr.write(`${result.message}\n`);
|
|
212
|
+
}
|
|
213
|
+
else if (arm !== null && !arm.armed) {
|
|
214
|
+
process.stderr.write(`${arm.message}\n`);
|
|
144
215
|
}
|
|
145
216
|
else if (result.exitCode === 0) {
|
|
146
217
|
process.stdout.write(`${result.message}\n`);
|
|
218
|
+
if (arm !== null) {
|
|
219
|
+
process.stdout.write(`${arm.message}\n`);
|
|
220
|
+
}
|
|
147
221
|
}
|
|
148
222
|
else {
|
|
149
223
|
process.stderr.write(`${result.message}\n`);
|
|
150
224
|
}
|
|
225
|
+
if (result.exitCode === EXIT_CONFIG_ERROR) {
|
|
226
|
+
return EXIT_CONFIG_ERROR;
|
|
227
|
+
}
|
|
228
|
+
if (arm !== null && !arm.armed) {
|
|
229
|
+
return EXIT_NOT_READY;
|
|
230
|
+
}
|
|
151
231
|
return result.exitCode;
|
|
152
232
|
}
|
|
153
233
|
if (process.argv[1] !== undefined && fileURLToPath(import.meta.url) === process.argv[1]) {
|
|
@@ -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 { emitBypassWarning, emitVerifyJson, formatCacheFreshDeferSoftPath,
|
|
4
|
+
import { emitBypassWarning, emitVerifyJson, formatCacheFreshDeferSoftPath, occupancyAwareDenialMessage, verifySessionRitual, } from "@deftai/directive-core/session";
|
|
5
5
|
function parsePosture(value) {
|
|
6
6
|
if (value === "read-only")
|
|
7
7
|
return "read-only";
|
|
@@ -121,8 +121,10 @@ export function run(argv, deps = {}) {
|
|
|
121
121
|
}
|
|
122
122
|
}
|
|
123
123
|
else if (result.code === 1) {
|
|
124
|
-
const
|
|
125
|
-
const lines = [
|
|
124
|
+
const recoveryTier = result.recoveryTier ?? "cold";
|
|
125
|
+
const lines = [
|
|
126
|
+
occupancyAwareDenialMessage(projectRoot, result.message, { env: process.env }, recoveryTier),
|
|
127
|
+
];
|
|
126
128
|
if (isCacheFreshFailureMessage(result.message)) {
|
|
127
129
|
lines.push(formatCacheFreshDeferSoftPath());
|
|
128
130
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/** Fallback when package.json cannot be read. Keep aligned with the bin map. */
|
|
2
|
+
export declare const PACKAGE_BIN_NAMES: readonly ["directive", "deft", "deft-ts", "deft-hook", "deft-verify-encoding"];
|
|
3
|
+
/** Node owns these shims. This package must not delete them. */
|
|
4
|
+
export declare const NODE_OWNED_SHIM_NAMES: readonly ["npx", "npm"];
|
|
5
|
+
export interface Ps1ShimRemoval {
|
|
6
|
+
readonly removed: readonly string[];
|
|
7
|
+
readonly failed: readonly string[];
|
|
8
|
+
}
|
|
9
|
+
export declare function packageIsUnderNodeModules(pkgDir: string): boolean;
|
|
10
|
+
/** bin-links layout. Valid only when {@link packageIsUnderNodeModules} is true. */
|
|
11
|
+
export declare function nodeModulesDir(pkgDir: string): string;
|
|
12
|
+
/** Same directory bin-links uses for this package root. */
|
|
13
|
+
export declare function linkerBinDir(opts: {
|
|
14
|
+
readonly pkgDir: string;
|
|
15
|
+
readonly global: boolean;
|
|
16
|
+
readonly platform: string;
|
|
17
|
+
}): string;
|
|
18
|
+
export declare function isGlobalInstall(env: Readonly<Record<string, string | undefined>>): boolean;
|
|
19
|
+
export declare function candidateBinDirs(opts: {
|
|
20
|
+
readonly pkgDir: string;
|
|
21
|
+
readonly env: Readonly<Record<string, string | undefined>>;
|
|
22
|
+
readonly platform: string;
|
|
23
|
+
}): string[];
|
|
24
|
+
export declare function removeGeneratedPs1Shims(opts: {
|
|
25
|
+
readonly binDir: string;
|
|
26
|
+
readonly binNames: readonly string[];
|
|
27
|
+
readonly platform: string;
|
|
28
|
+
readonly protectedNames?: readonly string[];
|
|
29
|
+
}): Ps1ShimRemoval;
|
|
30
|
+
/** True when cmd-shim text records a target path inside this install. */
|
|
31
|
+
export declare function cmdShimTargetsPackage(cmdText: string, binDir: string, pkgDir: string): boolean;
|
|
32
|
+
export declare function readPackageBinNames(packageJsonText: string): string[];
|
|
33
|
+
export declare function removeInstalledWindowsPs1Shims(opts: {
|
|
34
|
+
readonly pkgDir: string;
|
|
35
|
+
readonly env: Readonly<Record<string, string | undefined>>;
|
|
36
|
+
readonly platform: string;
|
|
37
|
+
readonly readText?: (path: string) => string;
|
|
38
|
+
}): Ps1ShimRemoval;
|
|
39
|
+
//# sourceMappingURL=windows-bin-ps1.d.ts.map
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* npm's Windows bin linker (cmd-shim) always writes a .ps1 beside the .cmd.
|
|
3
|
+
* PowerShell selects that .ps1 and stops when script execution is refused, so
|
|
4
|
+
* the bare name never reaches the .cmd. This runs from postinstall, after
|
|
5
|
+
* that link, and removes only this package's generated .ps1 files.
|
|
6
|
+
* A .ps1 is removed only when its sibling .cmd records a target inside this
|
|
7
|
+
* install, so an ambient PNPM_HOME from another install is left alone.
|
|
8
|
+
* It does not change npx.ps1, npm.ps1, or the operator's execution policy.
|
|
9
|
+
*/
|
|
10
|
+
import { existsSync, readFileSync, realpathSync, unlinkSync } from "node:fs";
|
|
11
|
+
import { basename, dirname, join, win32 } from "node:path";
|
|
12
|
+
/** Fallback when package.json cannot be read. Keep aligned with the bin map. */
|
|
13
|
+
export const PACKAGE_BIN_NAMES = [
|
|
14
|
+
"directive",
|
|
15
|
+
"deft",
|
|
16
|
+
"deft-ts",
|
|
17
|
+
"deft-hook",
|
|
18
|
+
"deft-verify-encoding",
|
|
19
|
+
];
|
|
20
|
+
/** Node owns these shims. This package must not delete them. */
|
|
21
|
+
export const NODE_OWNED_SHIM_NAMES = ["npx", "npm"];
|
|
22
|
+
export function packageIsUnderNodeModules(pkgDir) {
|
|
23
|
+
const parent = dirname(pkgDir);
|
|
24
|
+
if (basename(parent) === "node_modules")
|
|
25
|
+
return true;
|
|
26
|
+
return basename(dirname(parent)) === "node_modules";
|
|
27
|
+
}
|
|
28
|
+
/** bin-links layout. Valid only when {@link packageIsUnderNodeModules} is true. */
|
|
29
|
+
export function nodeModulesDir(pkgDir) {
|
|
30
|
+
const scopeOrNm = dirname(pkgDir);
|
|
31
|
+
if (basename(scopeOrNm) === "node_modules")
|
|
32
|
+
return scopeOrNm;
|
|
33
|
+
return dirname(scopeOrNm);
|
|
34
|
+
}
|
|
35
|
+
/** Same directory bin-links uses for this package root. */
|
|
36
|
+
export function linkerBinDir(opts) {
|
|
37
|
+
const nm = nodeModulesDir(opts.pkgDir);
|
|
38
|
+
if (!opts.global)
|
|
39
|
+
return join(nm, ".bin");
|
|
40
|
+
const prefix = dirname(nm);
|
|
41
|
+
if (opts.platform === "win32")
|
|
42
|
+
return prefix;
|
|
43
|
+
return join(prefix, "bin");
|
|
44
|
+
}
|
|
45
|
+
export function isGlobalInstall(env) {
|
|
46
|
+
return env.npm_config_global === "true";
|
|
47
|
+
}
|
|
48
|
+
function dirKey(dir, platform) {
|
|
49
|
+
const norm = dir.replace(/[\\/]+/g, "/");
|
|
50
|
+
if (platform === "win32")
|
|
51
|
+
return norm.toLowerCase();
|
|
52
|
+
return norm;
|
|
53
|
+
}
|
|
54
|
+
function ancestorBinDirs(pkgDir, stopDir, platform) {
|
|
55
|
+
const stop = dirKey(stopDir, platform);
|
|
56
|
+
const start = dirKey(pkgDir, platform);
|
|
57
|
+
const stopPrefix = stop.endsWith("/") ? stop : `${stop}/`;
|
|
58
|
+
if (start !== stop && !start.startsWith(stopPrefix)) {
|
|
59
|
+
return [join(stopDir, "node_modules", ".bin")];
|
|
60
|
+
}
|
|
61
|
+
const out = [];
|
|
62
|
+
let dir = pkgDir;
|
|
63
|
+
for (;;) {
|
|
64
|
+
out.push(join(dir, "node_modules", ".bin"));
|
|
65
|
+
if (dirKey(dir, platform) === stop)
|
|
66
|
+
break;
|
|
67
|
+
const parent = dirname(dir);
|
|
68
|
+
if (parent === dir)
|
|
69
|
+
break;
|
|
70
|
+
dir = parent;
|
|
71
|
+
}
|
|
72
|
+
return out;
|
|
73
|
+
}
|
|
74
|
+
export function candidateBinDirs(opts) {
|
|
75
|
+
const global = isGlobalInstall(opts.env);
|
|
76
|
+
const dirs = [];
|
|
77
|
+
if (packageIsUnderNodeModules(opts.pkgDir)) {
|
|
78
|
+
dirs.push(linkerBinDir({ pkgDir: opts.pkgDir, global, platform: opts.platform }));
|
|
79
|
+
}
|
|
80
|
+
if (global) {
|
|
81
|
+
const prefix = opts.env.npm_config_prefix;
|
|
82
|
+
if (prefix !== undefined && prefix.length > 0) {
|
|
83
|
+
dirs.push(opts.platform === "win32" ? prefix : join(prefix, "bin"));
|
|
84
|
+
}
|
|
85
|
+
// Candidate only. Removal still requires the .cmd to target this install.
|
|
86
|
+
const pnpmHome = opts.env.PNPM_HOME;
|
|
87
|
+
if (pnpmHome !== undefined && pnpmHome.length > 0)
|
|
88
|
+
dirs.push(pnpmHome);
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
const localPrefix = opts.env.npm_config_local_prefix;
|
|
92
|
+
if (localPrefix !== undefined && localPrefix.length > 0) {
|
|
93
|
+
dirs.push(...ancestorBinDirs(opts.pkgDir, localPrefix, opts.platform));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
const seen = new Set();
|
|
97
|
+
const out = [];
|
|
98
|
+
for (const dir of dirs) {
|
|
99
|
+
const key = dirKey(dir, opts.platform);
|
|
100
|
+
if (seen.has(key))
|
|
101
|
+
continue;
|
|
102
|
+
seen.add(key);
|
|
103
|
+
out.push(dir);
|
|
104
|
+
}
|
|
105
|
+
return out;
|
|
106
|
+
}
|
|
107
|
+
function isSafeBinName(name) {
|
|
108
|
+
if (name.length === 0)
|
|
109
|
+
return false;
|
|
110
|
+
if (name.includes("/") || name.includes("\\") || name.includes(".."))
|
|
111
|
+
return false;
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
export function removeGeneratedPs1Shims(opts) {
|
|
115
|
+
if (opts.platform !== "win32")
|
|
116
|
+
return { removed: [], failed: [] };
|
|
117
|
+
const protect = new Set((opts.protectedNames ?? NODE_OWNED_SHIM_NAMES).map((name) => name.toLowerCase()));
|
|
118
|
+
const removed = [];
|
|
119
|
+
const failed = [];
|
|
120
|
+
for (const name of opts.binNames) {
|
|
121
|
+
if (!isSafeBinName(name) || protect.has(name.toLowerCase()))
|
|
122
|
+
continue;
|
|
123
|
+
const ps1 = join(opts.binDir, `${name}.ps1`);
|
|
124
|
+
const cmd = join(opts.binDir, `${name}.cmd`);
|
|
125
|
+
if (!existsSync(cmd) || !existsSync(ps1))
|
|
126
|
+
continue;
|
|
127
|
+
try {
|
|
128
|
+
unlinkSync(ps1);
|
|
129
|
+
removed.push(ps1);
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
failed.push(ps1);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return { removed, failed };
|
|
136
|
+
}
|
|
137
|
+
function normalizedWinPath(p) {
|
|
138
|
+
const norm = win32.normalize(p).replace(/[\\/]+/g, "\\");
|
|
139
|
+
const trimmed = norm.length > 3 && norm.endsWith("\\") ? norm.slice(0, -1) : norm;
|
|
140
|
+
return trimmed.toLowerCase();
|
|
141
|
+
}
|
|
142
|
+
function pathIsInside(root, target) {
|
|
143
|
+
const rootKey = normalizedWinPath(root);
|
|
144
|
+
const targetKey = normalizedWinPath(target);
|
|
145
|
+
return targetKey === rootKey || targetKey.startsWith(`${rootKey}\\`);
|
|
146
|
+
}
|
|
147
|
+
/** cmd-shim text is Windows-shaped. realpath needs host separators or a junction never matches. */
|
|
148
|
+
function hostPath(p) {
|
|
149
|
+
return p.replace(/\\/g, "/");
|
|
150
|
+
}
|
|
151
|
+
function realpathIsInside(root, target) {
|
|
152
|
+
try {
|
|
153
|
+
return pathIsInside(realpathSync(hostPath(root)), realpathSync(hostPath(target)));
|
|
154
|
+
}
|
|
155
|
+
catch {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/** True when cmd-shim text records a target path inside this install. */
|
|
160
|
+
export function cmdShimTargetsPackage(cmdText, binDir, pkgDir) {
|
|
161
|
+
const re = /%(?:dp0%|~dp0)(?:\\+|\/+)([^"\r\n\s]+)/gi;
|
|
162
|
+
for (const match of cmdText.matchAll(re)) {
|
|
163
|
+
const rel = match[1];
|
|
164
|
+
if (rel === undefined || rel.length === 0)
|
|
165
|
+
continue;
|
|
166
|
+
const resolved = win32.normalize(win32.join(binDir, rel.replace(/\//g, "\\")));
|
|
167
|
+
if (pathIsInside(pkgDir, resolved) || realpathIsInside(pkgDir, resolved))
|
|
168
|
+
return true;
|
|
169
|
+
}
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
function cmdLinksCurrentInstall(binDir, name, pkgDir) {
|
|
173
|
+
if (!isSafeBinName(name))
|
|
174
|
+
return false;
|
|
175
|
+
let text;
|
|
176
|
+
try {
|
|
177
|
+
text = readFileSync(join(binDir, `${name}.cmd`), "utf8");
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
return cmdShimTargetsPackage(text, binDir, pkgDir);
|
|
183
|
+
}
|
|
184
|
+
export function readPackageBinNames(packageJsonText) {
|
|
185
|
+
let parsed;
|
|
186
|
+
try {
|
|
187
|
+
parsed = JSON.parse(packageJsonText);
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
return [];
|
|
191
|
+
}
|
|
192
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed))
|
|
193
|
+
return [];
|
|
194
|
+
const bin = parsed.bin;
|
|
195
|
+
if (bin === null || typeof bin !== "object" || Array.isArray(bin))
|
|
196
|
+
return [];
|
|
197
|
+
return Object.keys(bin);
|
|
198
|
+
}
|
|
199
|
+
export function removeInstalledWindowsPs1Shims(opts) {
|
|
200
|
+
if (opts.platform !== "win32")
|
|
201
|
+
return { removed: [], failed: [] };
|
|
202
|
+
const readText = opts.readText ?? ((path) => readFileSync(path, "utf8"));
|
|
203
|
+
let names;
|
|
204
|
+
try {
|
|
205
|
+
names = readPackageBinNames(readText(join(opts.pkgDir, "package.json")));
|
|
206
|
+
}
|
|
207
|
+
catch {
|
|
208
|
+
names = PACKAGE_BIN_NAMES;
|
|
209
|
+
}
|
|
210
|
+
const removed = [];
|
|
211
|
+
const failed = [];
|
|
212
|
+
for (const binDir of candidateBinDirs(opts)) {
|
|
213
|
+
const linked = names.filter((name) => cmdLinksCurrentInstall(binDir, name, opts.pkgDir));
|
|
214
|
+
const result = removeGeneratedPs1Shims({
|
|
215
|
+
binDir,
|
|
216
|
+
binNames: linked,
|
|
217
|
+
platform: opts.platform,
|
|
218
|
+
});
|
|
219
|
+
removed.push(...result.removed);
|
|
220
|
+
failed.push(...result.failed);
|
|
221
|
+
}
|
|
222
|
+
return { removed, failed };
|
|
223
|
+
}
|
|
224
|
+
//# sourceMappingURL=windows-bin-ps1.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deftai/directive",
|
|
3
|
-
"version": "0.119.
|
|
3
|
+
"version": "0.119.9",
|
|
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/",
|
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
},
|
|
23
23
|
"files": [
|
|
24
24
|
"dist/**/*.js",
|
|
25
|
-
"dist/**/*.d.ts"
|
|
25
|
+
"dist/**/*.d.ts",
|
|
26
|
+
"scripts/remove-windows-ps1-shims.mjs"
|
|
26
27
|
],
|
|
27
28
|
"repository": {
|
|
28
29
|
"type": "git",
|
|
@@ -34,10 +35,11 @@
|
|
|
34
35
|
"provenance": true
|
|
35
36
|
},
|
|
36
37
|
"dependencies": {
|
|
37
|
-
"@deftai/directive-core": "^0.119.
|
|
38
|
-
"@deftai/directive-content": "^0.119.
|
|
38
|
+
"@deftai/directive-core": "^0.119.9",
|
|
39
|
+
"@deftai/directive-content": "^0.119.9"
|
|
39
40
|
},
|
|
40
41
|
"scripts": {
|
|
41
|
-
"build": "tsc -b"
|
|
42
|
+
"build": "tsc -b",
|
|
43
|
+
"postinstall": "node ./scripts/remove-windows-ps1-shims.mjs"
|
|
42
44
|
}
|
|
43
45
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* postinstall entry (#4654). The published tarball includes dist/.
|
|
3
|
+
* A workspace install before tsc has no linked shims yet, so a missing
|
|
4
|
+
* dist file is a no-op rather than a failed install.
|
|
5
|
+
*/
|
|
6
|
+
import { existsSync } from "node:fs";
|
|
7
|
+
import { dirname, join } from "node:path";
|
|
8
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
9
|
+
|
|
10
|
+
const pkgDir = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
11
|
+
const entry = join(pkgDir, "dist", "windows-bin-ps1.js");
|
|
12
|
+
|
|
13
|
+
if (existsSync(entry)) {
|
|
14
|
+
const mod = await import(pathToFileURL(entry).href);
|
|
15
|
+
const result = mod.removeInstalledWindowsPs1Shims({
|
|
16
|
+
pkgDir,
|
|
17
|
+
env: process.env,
|
|
18
|
+
platform: process.platform,
|
|
19
|
+
});
|
|
20
|
+
if (result.failed.length > 0) {
|
|
21
|
+
process.stderr.write(
|
|
22
|
+
`remove-windows-ps1-shims: could not remove ${result.failed.join(", ")}\n`,
|
|
23
|
+
);
|
|
24
|
+
process.exitCode = 1;
|
|
25
|
+
}
|
|
26
|
+
}
|