@deftai/directive 0.104.0 → 0.106.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/authz.d.ts +4 -29
  2. package/dist/authz.js +16 -181
  3. package/dist/cli-router/route-argv.js +1 -0
  4. package/dist/dispatch.d.ts +7 -1
  5. package/dist/dispatch.js +56 -20
  6. package/dist/human-presence-mint.d.ts +70 -0
  7. package/dist/human-presence-mint.js +203 -0
  8. package/dist/occupancy-steal.d.ts +9 -0
  9. package/dist/occupancy-steal.js +54 -0
  10. package/dist/plan-sequence.d.ts +10 -0
  11. package/dist/plan-sequence.js +3 -1
  12. package/dist/policy.d.ts +3 -2
  13. package/dist/policy.js +52 -7
  14. package/dist/preflight-cache.d.ts +2 -0
  15. package/dist/preflight-cache.js +7 -0
  16. package/dist/scm-sync-default.d.ts +34 -0
  17. package/dist/scm-sync-default.js +210 -0
  18. package/dist/scope-record-approved-scope.d.ts +6 -1
  19. package/dist/scope-record-approved-scope.js +72 -24
  20. package/dist/session-start.d.ts +4 -0
  21. package/dist/session-start.js +23 -0
  22. package/dist/triage-actions.js +2 -0
  23. package/dist/triage-queue.js +4 -1
  24. package/dist/verify-ac.d.ts +2 -0
  25. package/dist/verify-ac.js +30 -6
  26. package/dist/verify-completed-tracked.d.ts +2 -1
  27. package/dist/verify-completed-tracked.js +31 -1
  28. package/dist/verify-forward-coverage.d.ts +4 -0
  29. package/dist/verify-forward-coverage.js +77 -11
  30. package/dist/verify-lifecycle-visible.d.ts +13 -0
  31. package/dist/verify-lifecycle-visible.js +68 -0
  32. package/dist/verify-literal-ac.js +7 -0
  33. package/dist/verify-orphan-active.d.ts +1 -0
  34. package/dist/verify-orphan-active.js +30 -0
  35. package/dist/verify-session-ritual.d.ts +10 -2
  36. package/dist/verify-session-ritual.js +16 -3
  37. package/package.json +3 -3
@@ -2,6 +2,20 @@
2
2
  import { resolve } from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
4
  import { evaluateForwardCoverage } from "@deftai/directive-core";
5
+ const HELP_TEXT = "Usage: verify-forward-coverage [--project-root <path>] [--staged|--head] [--allow-list <path>]\n" +
6
+ " [--coverage-dir <path>] [--coverage-report <path>]\n" +
7
+ " [--enforce] [--quiet]\n" +
8
+ " Fail-closed new-source-file existence (#1310) plus warn-first diff coverage of\n" +
9
+ " added/modified branches (#3514). The 90% per-diff threshold is not the 75\n" +
10
+ " global floor. Missing coverage reports skip the diff half.\n" +
11
+ " --enforce: fail closed when uncovered changed branches are below 90%.\n";
12
+ function takeValue(argv, i, flag) {
13
+ const value = argv[i + 1];
14
+ if (value === undefined) {
15
+ return { error: `argument ${flag}: expected one argument` };
16
+ }
17
+ return { value, next: i + 1 };
18
+ }
5
19
  /** Parse the verify-forward-coverage CLI args, mirroring the verify-encoding surface. */
6
20
  export function parseArgs(argv) {
7
21
  const parsed = {
@@ -9,6 +23,9 @@ export function parseArgs(argv) {
9
23
  projectRoot: ".",
10
24
  allowList: null,
11
25
  quiet: false,
26
+ enforce: false,
27
+ coverageReport: null,
28
+ coverageDir: null,
12
29
  };
13
30
  for (let i = 0; i < argv.length; i += 1) {
14
31
  const arg = argv[i];
@@ -21,48 +38,97 @@ export function parseArgs(argv) {
21
38
  else if (arg === "--quiet") {
22
39
  parsed.quiet = true;
23
40
  }
41
+ else if (arg === "--enforce") {
42
+ parsed.enforce = true;
43
+ }
44
+ else if (arg === "--help" || arg === "-h") {
45
+ return { ...parsed, help: true };
46
+ }
24
47
  else if (arg === "--project-root") {
25
- const value = argv[i + 1];
26
- if (value === undefined) {
27
- return { ...parsed, error: "argument --project-root: expected one argument" };
48
+ const taken = takeValue(argv, i, "--project-root");
49
+ if ("error" in taken) {
50
+ return { ...parsed, error: taken.error };
28
51
  }
29
- parsed.projectRoot = value;
30
- i += 1;
52
+ parsed.projectRoot = taken.value;
53
+ i = taken.next;
31
54
  }
32
55
  else if (arg?.startsWith("--project-root=")) {
33
56
  parsed.projectRoot = arg.slice("--project-root=".length);
34
57
  }
35
58
  else if (arg === "--allow-list") {
36
- const value = argv[i + 1];
37
- if (value === undefined) {
38
- return { ...parsed, error: "argument --allow-list: expected one argument" };
59
+ const taken = takeValue(argv, i, "--allow-list");
60
+ if ("error" in taken) {
61
+ return { ...parsed, error: taken.error };
39
62
  }
40
- parsed.allowList = value;
41
- i += 1;
63
+ parsed.allowList = taken.value;
64
+ i = taken.next;
42
65
  }
43
66
  else if (arg?.startsWith("--allow-list=")) {
44
67
  parsed.allowList = arg.slice("--allow-list=".length);
45
68
  }
69
+ else if (arg === "--coverage-report") {
70
+ const taken = takeValue(argv, i, "--coverage-report");
71
+ if ("error" in taken) {
72
+ return { ...parsed, error: taken.error };
73
+ }
74
+ parsed.coverageReport = taken.value;
75
+ i = taken.next;
76
+ }
77
+ else if (arg?.startsWith("--coverage-report=")) {
78
+ parsed.coverageReport = arg.slice("--coverage-report=".length);
79
+ }
80
+ else if (arg === "--coverage-dir") {
81
+ const taken = takeValue(argv, i, "--coverage-dir");
82
+ if ("error" in taken) {
83
+ return { ...parsed, error: taken.error };
84
+ }
85
+ parsed.coverageDir = taken.value;
86
+ i = taken.next;
87
+ }
88
+ else if (arg?.startsWith("--coverage-dir=")) {
89
+ parsed.coverageDir = arg.slice("--coverage-dir=".length);
90
+ }
46
91
  else {
47
92
  return { ...parsed, error: `unrecognized argument: ${arg}` };
48
93
  }
49
94
  }
50
95
  return parsed;
51
96
  }
97
+ function resolveCoverageReport(args) {
98
+ if (args.coverageReport !== null) {
99
+ return resolve(args.coverageReport);
100
+ }
101
+ if (args.coverageDir !== null) {
102
+ return resolve(args.coverageDir, "coverage-final.json");
103
+ }
104
+ return undefined;
105
+ }
52
106
  /** Run the gate and return the process exit code (argv parse error -> 2). */
53
107
  export function run(argv) {
54
108
  const args = parseArgs(argv);
109
+ if (args.help === true) {
110
+ process.stdout.write(HELP_TEXT);
111
+ return 0;
112
+ }
55
113
  if (args.error !== undefined) {
56
114
  process.stderr.write(`verify_forward_coverage: ${args.error}\n`);
57
115
  return 2;
58
116
  }
59
117
  const projectRoot = resolve(args.projectRoot);
60
118
  const allowListPath = args.allowList !== null ? resolve(args.allowList) : null;
61
- const result = evaluateForwardCoverage(projectRoot, { mode: args.mode, allowListPath });
119
+ const result = evaluateForwardCoverage(projectRoot, {
120
+ mode: args.mode,
121
+ allowListPath,
122
+ enforceDiffCoverage: args.enforce,
123
+ coverageReportPath: resolveCoverageReport(args),
124
+ });
62
125
  if (result.exitCode === 0) {
63
126
  if (!args.quiet) {
64
127
  process.stdout.write(`${result.message}\n`);
65
128
  }
129
+ else if (result.message.includes("ADVISORY")) {
130
+ process.stdout.write(`${result.message}\n`);
131
+ }
66
132
  }
67
133
  else {
68
134
  process.stderr.write(`${result.message}\n`);
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+ interface ParsedArgs {
3
+ projectRoot: string;
4
+ enforce: boolean;
5
+ help?: boolean;
6
+ error?: string;
7
+ }
8
+ /** Parse verify-lifecycle-visible CLI args (#3505). */
9
+ export declare function parseArgs(argv: string[]): ParsedArgs;
10
+ /** Run the gate and return the process exit code. */
11
+ export declare function run(argv: string[]): number;
12
+ export {};
13
+ //# sourceMappingURL=verify-lifecycle-visible.d.ts.map
@@ -0,0 +1,68 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * CLI for task verify:lifecycle-visible (#3505).
4
+ * Warn-only by default; pass --enforce to fail closed.
5
+ */
6
+ import { resolve } from "node:path";
7
+ import { fileURLToPath } from "node:url";
8
+ import { evaluateLifecycleVisible } from "@deftai/directive-core/lifecycle-visible";
9
+ /** Parse verify-lifecycle-visible CLI args (#3505). */
10
+ export function parseArgs(argv) {
11
+ const parsed = { projectRoot: ".", enforce: false };
12
+ for (let i = 0; i < argv.length; i += 1) {
13
+ const arg = argv[i];
14
+ if (arg === "--project-root") {
15
+ const value = argv[i + 1];
16
+ if (value === undefined) {
17
+ return { ...parsed, error: "argument --project-root: expected one argument" };
18
+ }
19
+ parsed.projectRoot = value;
20
+ i += 1;
21
+ }
22
+ else if (arg?.startsWith("--project-root=")) {
23
+ parsed.projectRoot = arg.slice("--project-root=".length);
24
+ }
25
+ else if (arg === "--enforce") {
26
+ parsed.enforce = true;
27
+ }
28
+ else if (arg === "--help" || arg === "-h") {
29
+ return { ...parsed, help: true };
30
+ }
31
+ else {
32
+ return { ...parsed, error: `unrecognized argument: ${arg}` };
33
+ }
34
+ }
35
+ return parsed;
36
+ }
37
+ const HELP_TEXT = "Usage: verify-lifecycle-visible [--project-root <path>] [--enforce]\n" +
38
+ " Detect ignore/exclude/skip-worktree flags that hide xbrief/vbrief lifecycle roots (#3505).\n" +
39
+ " Default: warn-only (exit 0 with advisory report).\n" +
40
+ " --enforce: fail closed (exit 1) when a lifecycle root is hidden.\n" +
41
+ " Not part of task check — per-clone property, wired from session:start.\n";
42
+ /** Run the gate and return the process exit code. */
43
+ export function run(argv) {
44
+ const args = parseArgs(argv);
45
+ if (args.help === true) {
46
+ process.stdout.write(HELP_TEXT);
47
+ return 0;
48
+ }
49
+ if (args.error !== undefined) {
50
+ process.stderr.write(`verify_lifecycle_visible: ${args.error}\n`);
51
+ return 2;
52
+ }
53
+ const result = evaluateLifecycleVisible({
54
+ projectRoot: resolve(args.projectRoot),
55
+ enforce: args.enforce,
56
+ });
57
+ if (result.stream === "stdout") {
58
+ process.stdout.write(`${result.message}\n`);
59
+ }
60
+ else {
61
+ process.stderr.write(`${result.message}\n`);
62
+ }
63
+ return result.code;
64
+ }
65
+ if (process.argv[1] !== undefined && fileURLToPath(import.meta.url) === process.argv[1]) {
66
+ process.exit(run(process.argv.slice(2)));
67
+ }
68
+ //# sourceMappingURL=verify-lifecycle-visible.js.map
@@ -128,6 +128,13 @@ export function run(argv) {
128
128
  reason: r.reason,
129
129
  sourceSpan: r.sourceSpan ?? null,
130
130
  })),
131
+ // Prose-derived captures demoted by structured acceptance commands (#3484).
132
+ advisory_rejected_count: resolved.advisoryRejected.length,
133
+ advisory_rejected: resolved.advisoryRejected.map((r) => ({
134
+ command: r.command,
135
+ reason: r.reason,
136
+ sourceSpan: r.sourceSpan ?? null,
137
+ })),
131
138
  };
132
139
  process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
133
140
  if (resolved.rejected.length > 0) {
@@ -2,6 +2,7 @@
2
2
  interface ParsedArgs {
3
3
  projectRoot: string;
4
4
  repo: string | null;
5
+ issue: number | null;
5
6
  quiet: boolean;
6
7
  skipGh: boolean;
7
8
  error?: string;
@@ -2,11 +2,20 @@
2
2
  import { resolve } from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
4
  import { evaluate } from "@deftai/directive-core/orphan-active";
5
+ function parseIssueNumber(raw) {
6
+ const trimmed = raw.startsWith("#") ? raw.slice(1) : raw;
7
+ if (!/^\d+$/.test(trimmed)) {
8
+ return null;
9
+ }
10
+ const value = Number(trimmed);
11
+ return Number.isInteger(value) && value > 0 ? value : null;
12
+ }
5
13
  /** Parse verify-orphan-active CLI args. */
6
14
  export function parseArgs(argv) {
7
15
  const parsed = {
8
16
  projectRoot: ".",
9
17
  repo: null,
18
+ issue: null,
10
19
  quiet: false,
11
20
  skipGh: false,
12
21
  };
@@ -40,6 +49,26 @@ export function parseArgs(argv) {
40
49
  else if (arg?.startsWith("--repo=")) {
41
50
  parsed.repo = arg.slice("--repo=".length);
42
51
  }
52
+ else if (arg === "--issue") {
53
+ const value = argv[i + 1];
54
+ if (value === undefined) {
55
+ return { ...parsed, error: "argument --issue: expected one argument" };
56
+ }
57
+ const issue = parseIssueNumber(value);
58
+ if (issue === null) {
59
+ return { ...parsed, error: `argument --issue: expected a positive integer, got ${value}` };
60
+ }
61
+ parsed.issue = issue;
62
+ i += 1;
63
+ }
64
+ else if (arg?.startsWith("--issue=")) {
65
+ const value = arg.slice("--issue=".length);
66
+ const issue = parseIssueNumber(value);
67
+ if (issue === null) {
68
+ return { ...parsed, error: `argument --issue: expected a positive integer, got ${value}` };
69
+ }
70
+ parsed.issue = issue;
71
+ }
43
72
  else {
44
73
  return { ...parsed, error: `unrecognized argument: ${arg}` };
45
74
  }
@@ -58,6 +87,7 @@ export function run(argv) {
58
87
  quiet: args.quiet,
59
88
  repo: args.repo,
60
89
  skipGh: args.skipGh,
90
+ issue: args.issue,
61
91
  });
62
92
  if (result.message.length > 0) {
63
93
  if (result.stream === "stdout") {
@@ -1,5 +1,11 @@
1
1
  #!/usr/bin/env node
2
- import { type DirectivePosture } from "@deftai/directive-core/session";
2
+ import { type DirectivePosture, type VerifyResult } from "@deftai/directive-core/session";
3
+ export interface VerifySessionRitualRunDeps {
4
+ readonly verifySessionRitual?: (projectRoot: string, options: {
5
+ tier: "quick" | "gated";
6
+ posture?: DirectivePosture;
7
+ }) => VerifyResult;
8
+ }
3
9
  interface ParsedArgs {
4
10
  projectRoot: string;
5
11
  tier: "quick" | "gated";
@@ -9,7 +15,9 @@ interface ParsedArgs {
9
15
  }
10
16
  /** Parse verify-session-ritual CLI args, mirroring scripts/verify_session_ritual.py. */
11
17
  export declare function parseArgs(argv: string[]): ParsedArgs;
18
+ /** True when the ritual failure is the gated cache_fresh step (#3506 / #3507). */
19
+ export declare function isCacheFreshFailureMessage(message: string): boolean;
12
20
  /** Run the gate and return the process exit code. */
13
- export declare function run(argv: string[]): number;
21
+ export declare function run(argv: string[], deps?: VerifySessionRitualRunDeps): number;
14
22
  export {};
15
23
  //# sourceMappingURL=verify-session-ritual.d.ts.map
@@ -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, verifySessionRitual, } from "@deftai/directive-core/session";
4
+ import { emitBypassWarning, emitVerifyJson, formatCacheFreshDeferSoftPath, formatRitualRecoveryInstruction, verifySessionRitual, } from "@deftai/directive-core/session";
5
5
  function parsePosture(value) {
6
6
  if (value === "read-only")
7
7
  return "read-only";
@@ -86,15 +86,20 @@ export function parseArgs(argv) {
86
86
  }
87
87
  return parsed;
88
88
  }
89
+ /** True when the ritual failure is the gated cache_fresh step (#3506 / #3507). */
90
+ export function isCacheFreshFailureMessage(message) {
91
+ return message.includes("cache_fresh") || message.includes("cache-fresh");
92
+ }
89
93
  /** Run the gate and return the process exit code. */
90
- export function run(argv) {
94
+ export function run(argv, deps = {}) {
91
95
  const args = parseArgs(argv);
92
96
  if (args.error !== undefined) {
93
97
  process.stderr.write(`verify_session_ritual: ${args.error}\n`);
94
98
  return 2;
95
99
  }
96
100
  const projectRoot = resolve(args.projectRoot);
97
- const result = verifySessionRitual(projectRoot, {
101
+ const verify = deps.verifySessionRitual ?? verifySessionRitual;
102
+ const result = verify(projectRoot, {
98
103
  tier: args.tier,
99
104
  posture: args.posture ?? undefined,
100
105
  });
@@ -108,6 +113,14 @@ export function run(argv) {
108
113
  process.stdout.write(`${result.message}\n`);
109
114
  }
110
115
  }
116
+ else if (result.code === 1) {
117
+ const recovery = formatRitualRecoveryInstruction(result.recoveryTier ?? "cold");
118
+ const lines = [result.message, recovery];
119
+ if (isCacheFreshFailureMessage(result.message)) {
120
+ lines.push(formatCacheFreshDeferSoftPath());
121
+ }
122
+ process.stderr.write(`${lines.join("\n")}\n`);
123
+ }
111
124
  else {
112
125
  process.stderr.write(`${result.message}\n`);
113
126
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deftai/directive",
3
- "version": "0.104.0",
3
+ "version": "0.106.0",
4
4
  "description": "Directive CLI — npm install path for the Deft Directive framework.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://deftai.github.io/directive/",
@@ -34,8 +34,8 @@
34
34
  "provenance": true
35
35
  },
36
36
  "dependencies": {
37
- "@deftai/directive-core": "^0.104.0",
38
- "@deftai/directive-content": "^0.104.0"
37
+ "@deftai/directive-core": "^0.106.0",
38
+ "@deftai/directive-content": "^0.106.0"
39
39
  },
40
40
  "scripts": {
41
41
  "build": "tsc -b"