@deftai/directive 0.119.7 → 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.
@@ -0,0 +1,3 @@
1
+ /** Reads `@deftai/directive` version from the installed CLI package.json adjacent to dist/ or src/. */
2
+ export declare function readCliPackageVersion(): string;
3
+ //# sourceMappingURL=cli-package-version.d.ts.map
@@ -0,0 +1,23 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ const FALLBACK_VERSION = "0.0.0";
5
+ function parsePackageVersion(raw) {
6
+ const parsed = JSON.parse(raw);
7
+ if (parsed === null || typeof parsed !== "object") {
8
+ return FALLBACK_VERSION;
9
+ }
10
+ const version = parsed.version;
11
+ return typeof version === "string" && version.length > 0 ? version : FALLBACK_VERSION;
12
+ }
13
+ /** Reads `@deftai/directive` version from the installed CLI package.json adjacent to dist/ or src/. */
14
+ export function readCliPackageVersion() {
15
+ try {
16
+ const pkgPath = join(dirname(fileURLToPath(import.meta.url)), "..", "package.json");
17
+ return parsePackageVersion(readFileSync(pkgPath, "utf8"));
18
+ }
19
+ catch {
20
+ return FALLBACK_VERSION;
21
+ }
22
+ }
23
+ //# sourceMappingURL=cli-package-version.js.map
@@ -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
  }
@@ -10,9 +10,9 @@ 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
- 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", "docs-impact"];
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). */
17
17
  export declare const TRIAGE_ACTION_ALIAS_SUBCOMMANDS: Readonly<Record<string, string>>;
18
18
  /** Colon aliases for policy subcommands (mirrors cli-router SUBCOMMAND_ROUTES). */
package/dist/dispatch.js CHANGED
@@ -14,6 +14,7 @@ import { appendAuditLog, disclosureLine, migrateLegacyPolicyKey, PLAN_POLICY_KEY
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 { withProjectDefinitionMutation, } from "@deftai/directive-core/vbrief-build";
17
+ import { identityBanner } from "./identity-banner.js";
17
18
  const HANDLER_KEYS = [
18
19
  "run",
19
20
  "main",
@@ -110,10 +111,12 @@ export const CLI_MODULE_VERBS = [
110
111
  "vbrief-validation",
111
112
  "xbrief-create",
112
113
  "xbrief-verify",
114
+ "xbrief-adopt-stored-plan-id",
113
115
  "verify-branch",
114
116
  "verify-encoding",
115
117
  "verify-forward-coverage",
116
118
  "verify-test-boundary",
119
+ "verify-class-checks",
117
120
  "verify-scope-provenance",
118
121
  "verify-consumer-check-contract",
119
122
  "verify-hooks-installed",
@@ -198,6 +201,7 @@ export const CORE_MODULE_VERBS = [
198
201
  "prd-render",
199
202
  "export-spec",
200
203
  "project-render",
204
+ "project-write-narratives",
201
205
  "roadmap-render",
202
206
  "rule-map",
203
207
  "spec-render",
@@ -240,6 +244,8 @@ export const POLICY_ACTION_ALIAS_SUBCOMMANDS = {
240
244
  "policy:show": "show",
241
245
  "policy:enforce-branches": "enforce-branches",
242
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",
243
249
  "policy:allow-bot-merge": "allow-bot-merge",
244
250
  "policy:enable-value-feedback": "enable-value-feedback",
245
251
  "policy:disable-host-hooks": "disable-host-hooks",
@@ -297,6 +303,7 @@ export const VERB_ALIASES = {
297
303
  "verify:encoding": "verify-encoding",
298
304
  "verify:forward-coverage": "verify-forward-coverage",
299
305
  "verify:test-boundary": "verify-test-boundary",
306
+ "verify:class-checks": "verify-class-checks",
300
307
  "verify:scope-provenance": "verify-scope-provenance",
301
308
  "scope:record-approved-scope": "scope-record-approved-scope",
302
309
  "scope:record-observable-scope": "scope-record-observable-scope",
@@ -335,6 +342,7 @@ export const VERB_ALIASES = {
335
342
  "xbrief:preflight": "vbrief-preflight",
336
343
  "xbrief:create": "xbrief-create",
337
344
  "xbrief:verify": "xbrief-verify",
345
+ "xbrief:adopt-stored-plan-id": "xbrief-adopt-stored-plan-id",
338
346
  "vbrief:activate": "vbrief-activate",
339
347
  "verify:story-ready": "verify-story-ready",
340
348
  "verify:review-monitor": "verify-review-monitor",
@@ -413,6 +421,7 @@ export const VERB_ALIASES = {
413
421
  "spec:render": "spec-render",
414
422
  "prd:render": "prd-render",
415
423
  "project:render": "project-render",
424
+ "project:write-narratives": "project-write-narratives",
416
425
  "docs:rule-map": "rule-map",
417
426
  "project:export-spec": "export-spec",
418
427
  "pr:watch": "pr-watch",
@@ -475,6 +484,7 @@ const WRAPPER_CLI_STEMS = new Set([
475
484
  "vbrief-validation",
476
485
  "xbrief-create",
477
486
  "xbrief-verify",
487
+ "xbrief-adopt-stored-plan-id",
478
488
  ]);
479
489
  function emitCliResult(result, io) {
480
490
  if (result.stdout)
@@ -550,6 +560,10 @@ async function loadWrapperCliHandler(stem, io) {
550
560
  const { runXbriefVerifyCli } = await import("@deftai/directive-core/xbrief");
551
561
  return (argv) => emitCliResult(runXbriefVerifyCli(argv), io);
552
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
+ }
553
567
  default:
554
568
  throw new Error(`no wrapper handler for ${stem}`);
555
569
  }
@@ -2497,6 +2511,10 @@ async function loadCoreModuleHandler(verb, io) {
2497
2511
  const { runProjectRenderCli } = await import("./render-cli/project-render-cli.js");
2498
2512
  return (argv) => runProjectRenderCli(argv);
2499
2513
  }
2514
+ case "project-write-narratives": {
2515
+ const { writePhase2NarrativesMain } = await import("@deftai/directive-core/dist/render/phase2-narratives-cli.js");
2516
+ return (argv) => writePhase2NarrativesMain(argv);
2517
+ }
2500
2518
  case "rule-map": {
2501
2519
  const { runRuleMapCli } = await import("./render-cli/rule-map-cli.js");
2502
2520
  return (argv) => runRuleMapCli(argv);
@@ -2764,12 +2782,20 @@ const CURATED_HELP_GROUPS = [
2764
2782
  name: "xbrief:verify",
2765
2783
  summary: "Verify a dense xBRIEF artifact at --out (requires --format); not scope:*",
2766
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
+ },
2767
2789
  ],
2768
2790
  },
2769
2791
  {
2770
2792
  title: "Project artifacts",
2771
2793
  commands: [
2772
2794
  { name: "project:render", summary: "Render PROJECT-DEFINITION projection" },
2795
+ {
2796
+ name: "project:write-narratives",
2797
+ summary: "Store Phase 2 narratives in PROJECT-DEFINITION without policy keys",
2798
+ },
2773
2799
  { name: "spec:render", summary: "Render specification projection" },
2774
2800
  ],
2775
2801
  },
@@ -2824,7 +2850,6 @@ async function invokeHandler(handler, argv) {
2824
2850
  const code = await handler(argv);
2825
2851
  return typeof code === "number" ? code : 0;
2826
2852
  }
2827
- const CLI_PACKAGE = "@deftai/directive";
2828
2853
  const PLAN_SEQUENCE_VERBS = "set|current|clear|advance";
2829
2854
  /**
2830
2855
  * Unknown-colon-verb remediation (#3439 / #2652).
@@ -2843,8 +2868,7 @@ export function unknownColonVerbHint(verb) {
2843
2868
  `Hyphen stem: ${hyphen}.\n`);
2844
2869
  }
2845
2870
  function versionBanner() {
2846
- const info = engineInfo();
2847
- return `${CLI_PACKAGE} (engine: ${info.name}@${info.version})\n`;
2871
+ return `${identityBanner()}\n`;
2848
2872
  }
2849
2873
  /** Dispatch argv to a registered verb; returns the handler exit code. */
2850
2874
  export async function dispatch(argv, io = defaultIo()) {
@@ -0,0 +1,13 @@
1
+ export declare const CLI_PACKAGE: "@deftai/directive";
2
+ export interface IdentityBannerParts {
3
+ readonly cliVersion: string;
4
+ readonly engineName: string;
5
+ readonly engineVersion: string;
6
+ }
7
+ /**
8
+ * Display-both identity line. Engine semver is first so parseVersionFromOutput
9
+ * in active-cli.ts and classify.ts still treat --version as the engine.
10
+ */
11
+ export declare function formatIdentityBanner(parts: IdentityBannerParts): string;
12
+ export declare function identityBanner(): string;
13
+ //# sourceMappingURL=identity-banner.d.ts.map
@@ -0,0 +1,19 @@
1
+ import { engineInfo } from "@deftai/directive-core";
2
+ import { readCliPackageVersion } from "./cli-package-version.js";
3
+ export const CLI_PACKAGE = "@deftai/directive";
4
+ /**
5
+ * Display-both identity line. Engine semver is first so parseVersionFromOutput
6
+ * in active-cli.ts and classify.ts still treat --version as the engine.
7
+ */
8
+ export function formatIdentityBanner(parts) {
9
+ return `${CLI_PACKAGE} (engine: ${parts.engineName}@${parts.engineVersion}; package: ${CLI_PACKAGE}@${parts.cliVersion})`;
10
+ }
11
+ export function identityBanner() {
12
+ const info = engineInfo();
13
+ return formatIdentityBanner({
14
+ cliVersion: readCliPackageVersion(),
15
+ engineName: info.name,
16
+ engineVersion: info.version,
17
+ });
18
+ }
19
+ //# sourceMappingURL=identity-banner.js.map
package/dist/index.d.ts CHANGED
@@ -5,7 +5,7 @@
5
5
  * (cli → core → types), proving the project-reference graph builds and
6
6
  * resolves end-to-end. Real commands land in later migration waves.
7
7
  */
8
- export declare const CLI_PACKAGE: "@deftai/directive";
9
- /** Renders the engine banner string, sourcing core engine metadata. */
8
+ export { CLI_PACKAGE } from "./identity-banner.js";
9
+ /** Renders CLI package and engine identity. First semver is the engine (#4766). */
10
10
  export declare function banner(): string;
11
11
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { engineInfo } from "@deftai/directive-core";
1
+ import { identityBanner } from "./identity-banner.js";
2
2
  /**
3
3
  * `@deftai/directive` — entrypoint for the deft directive TypeScript engine.
4
4
  *
@@ -6,10 +6,9 @@ import { engineInfo } from "@deftai/directive-core";
6
6
  * (cli → core → types), proving the project-reference graph builds and
7
7
  * resolves end-to-end. Real commands land in later migration waves.
8
8
  */
9
- export const CLI_PACKAGE = "@deftai/directive";
10
- /** Renders the engine banner string, sourcing core engine metadata. */
9
+ export { CLI_PACKAGE } from "./identity-banner.js";
10
+ /** Renders CLI package and engine identity. First semver is the engine (#4766). */
11
11
  export function banner() {
12
- const info = engineInfo();
13
- return `${CLI_PACKAGE} (engine: ${info.name}@${info.version})`;
12
+ return identityBanner();
14
13
  }
15
14
  //# sourceMappingURL=index.js.map
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-bot-merge"
164
- ? policyColonInvocation("allow-bot-merge")
165
- : cmd === "enable-value-feedback"
166
- ? policyColonInvocation("enable-value-feedback")
167
- : cmd === "clear-value-feedback"
168
- ? policyColonInvocation("clear-value-feedback")
169
- : cmd === "set-ceremony-dial"
170
- ? policyColonInvocation("set-ceremony-dial")
171
- : cmd === "disable-host-hooks"
172
- ? policyColonInvocation("disable-host-hooks")
173
- : cmd === "disable-directive"
174
- ? policyColonInvocation("disable-directive")
175
- : policyColonInvocation("enable-directive");
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
  }
@@ -3,6 +3,7 @@ interface ParsedArgs {
3
3
  mode?: "self-test" | "command" | "pre-push-stdin";
4
4
  command?: string;
5
5
  defaultBranches?: Set<string>;
6
+ projectRoot?: string;
6
7
  quiet?: boolean;
7
8
  error?: string;
8
9
  }
@@ -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 to @deftai/directive-core/preflight-gh.
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, ENV_BYPASS, evaluateCommand, runSelfTest, } from "@deftai/directive-core/preflight-gh";
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
- // accepted for parity with preflight_branch.py; ignored here
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 parsePrePushLines(text) {
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 = parsePrePushLines(lines.join("\n"));
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`);
@@ -5,6 +5,7 @@ export interface ParsedArgs {
5
5
  xbriefPath: string;
6
6
  actor: string;
7
7
  kind: string;
8
+ mintedVia: string;
8
9
  quiet: boolean;
9
10
  confirm: boolean;
10
11
  help?: boolean;
@@ -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] [--project-root <dir>] [--quiet]\n" +
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
- " --actor is display only and never authorizes mint. Mint requires a real TTY, " +
19
- "controlling terminal, --confirm, and typed phrase mint (#3110). Agent/CI shells refuse.");
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: "scope:record-intent-constraint",
186
+ mintedVia: args.mintedVia,
163
187
  },
164
188
  });
165
189
  if ("error" in rec) {