@deftai/directive 0.76.0 → 0.78.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.
@@ -82,6 +82,10 @@ export const SUBCOMMAND_ROUTES = {
82
82
  "github-body:comment-create": ["github-body", "comment-create"],
83
83
  "github-body:comment-edit": ["github-body", "comment-edit"],
84
84
  "github-body:pr-edit": ["github-body", "pr-edit"],
85
+ "plan-sequence:set": ["plan-sequence", "set"],
86
+ "plan-sequence:current": ["plan-sequence", "current"],
87
+ "plan-sequence:clear": ["plan-sequence", "clear"],
88
+ "plan-sequence:advance": ["plan-sequence", "advance"],
85
89
  };
86
90
  function isMetaVerb(token) {
87
91
  return token === "--help" || token === "-h" || token === "--version" || token === "-V";
@@ -10,13 +10,15 @@ 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", "umbrella-current-shape", "changelog-check", "change-init", "commit-lint", "policy", "pr-closing-keywords", "pr-merge-readiness", "pr-monitor", "pr-protected-issues", "pr-wait-mergeable", "pr-watch", "preflight-cache", "preflight-gh", "probe-session", "release", "release-e2e", "release-publish", "release-rollback", "scope-lifecycle", "session-start", "slice", "subagent-monitor", "toolchain-check", "triage-actions", "triage-bootstrap", "triage-bulk", "triage-classify", "triage-help", "triage-queue", "triage-reconcile", "triage-refresh", "triage-scope", "triage-scope-drift", "triage-smoketest", "triage-subscribe", "triage-summary", "triage-welcome", "ts-check-lane", "vbrief-activate", "vbrief-build", "vbrief-preflight", "vbrief-reconcile", "vbrief-validate", "vbrief-validation", "verify-branch", "verify-encoding", "verify-forward-coverage", "verify-hooks-installed", "verify-investigation", "verify-judgment-gates", "verify-no-task-runtime", "validate-links", "validate-strategy-output", "verify-biome-config", "verify-bridge-drift", "verify-capacity", "verify-content-manifest", "verify-contract-drift", "verify-cursor-tier1", "verify-go-freeze", "verify-scm-boundary", "verify-session-ritual", "verify-stubs", "verify-xbrief-drift", "rule-ownership-lint", "verify-story-ready", "verify-tools", "verify-wip-cap", "verify-agents-md-budget", "verify-agents-md-advisory", "verify-eval-health-relocation", "eval-health", "eval-run", "eval-report"];
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", "policy", "pr-closing-keywords", "pr-merge-readiness", "pr-monitor", "pr-protected-issues", "pr-wait-mergeable", "pr-watch", "preflight-cache", "preflight-gh", "probe-session", "release", "release-e2e", "release-publish", "release-rollback", "scope-lifecycle", "session-start", "plan-sequence", "slice", "subagent-monitor", "toolchain-check", "triage-actions", "triage-bootstrap", "triage-bulk", "triage-classify", "triage-help", "triage-queue", "triage-reconcile", "triage-refresh", "triage-scope", "triage-scope-drift", "triage-smoketest", "triage-subscribe", "triage-summary", "triage-welcome", "ts-check-lane", "vbrief-activate", "vbrief-build", "vbrief-preflight", "vbrief-reconcile", "vbrief-validate", "vbrief-validation", "verify-branch", "verify-encoding", "verify-forward-coverage", "verify-hooks-installed", "verify-investigation", "verify-judgment-gates", "verify-no-task-runtime", "validate-links", "validate-strategy-output", "verify-biome-config", "verify-bridge-drift", "verify-capacity", "verify-content-manifest", "verify-contract-drift", "verify-cursor-tier1", "verify-go-freeze", "verify-scm-boundary", "verify-session-ritual", "verify-plan-sequence", "verify-stubs", "verify-xbrief-drift", "rule-ownership-lint", "verify-story-ready", "verify-tools", "verify-wip-cap", "verify-agents-md-budget", "verify-agents-md-advisory", "verify-eval-health-relocation", "verify-eval-triggers-relocation", "eval-health", "eval-run", "eval-report", "eval-triggers"];
14
14
  /** Core-only CLI entrypoints without a packages/cli wrapper. */
15
15
  export declare const CORE_MODULE_VERBS: readonly ["scm", "github-auth-modes", "github-body", "issue-emit", "issue-ingest", "reconcile-issues", "swarm-launch", "swarm-complete-cohort", "swarm-finalize-cohort", "swarm-readiness", "swarm-routing-verify", "swarm-routing-set", "swarm-verify-review-clean", "swarm-worktrees", "framework-commands", "pack-render", "packs-slice", "prd-render", "export-spec", "project-render", "roadmap-render", "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"];
16
16
  /** Colon aliases for triage-actions (mirrors cli-router SUBCOMMAND_ROUTES). */
17
17
  export declare const TRIAGE_ACTION_ALIAS_SUBCOMMANDS: Readonly<Record<string, string>>;
18
18
  /** Colon aliases for policy subcommands (mirrors cli-router SUBCOMMAND_ROUTES). */
19
19
  export declare const POLICY_ACTION_ALIAS_SUBCOMMANDS: Readonly<Record<string, string>>;
20
+ /** Colon aliases for plan-sequence subcommands (#2402). */
21
+ export declare const PLAN_SEQUENCE_ALIAS_SUBCOMMANDS: Readonly<Record<string, string>>;
20
22
  /** Task-style aliases (framework_commands / Taskfile names). */
21
23
  export declare const VERB_ALIASES: Readonly<Record<string, string>>;
22
24
  /** Pinned ghx version (display only) — keep in lockstep with .github/workflows/ci.yml env.GHX_VERSION. */
package/dist/dispatch.js CHANGED
@@ -41,6 +41,7 @@ export const CLI_MODULE_VERBS = [
41
41
  "migrate-xbrief",
42
42
  "migrate-category-b",
43
43
  "framework-check-updates",
44
+ "hook-dispatch",
44
45
  "umbrella-current-shape",
45
46
  "changelog-check",
46
47
  "change-init",
@@ -61,6 +62,7 @@ export const CLI_MODULE_VERBS = [
61
62
  "release-rollback",
62
63
  "scope-lifecycle",
63
64
  "session-start",
65
+ "plan-sequence",
64
66
  "slice",
65
67
  "subagent-monitor",
66
68
  "toolchain-check",
@@ -103,6 +105,7 @@ export const CLI_MODULE_VERBS = [
103
105
  "verify-go-freeze",
104
106
  "verify-scm-boundary",
105
107
  "verify-session-ritual",
108
+ "verify-plan-sequence",
106
109
  "verify-stubs",
107
110
  "verify-xbrief-drift",
108
111
  "rule-ownership-lint",
@@ -112,9 +115,11 @@ export const CLI_MODULE_VERBS = [
112
115
  "verify-agents-md-budget",
113
116
  "verify-agents-md-advisory",
114
117
  "verify-eval-health-relocation",
118
+ "verify-eval-triggers-relocation",
115
119
  "eval-health",
116
120
  "eval-run",
117
121
  "eval-report",
122
+ "eval-triggers",
118
123
  ];
119
124
  /** Core-only CLI entrypoints without a packages/cli wrapper. */
120
125
  export const CORE_MODULE_VERBS = [
@@ -177,8 +182,17 @@ export const POLICY_ACTION_ALIAS_SUBCOMMANDS = {
177
182
  "policy:enable-value-feedback": "enable-value-feedback",
178
183
  };
179
184
  const POLICY_ACTION_COLON_ALIASES = Object.fromEntries(Object.keys(POLICY_ACTION_ALIAS_SUBCOMMANDS).map((alias) => [alias, "policy"]));
185
+ /** Colon aliases for plan-sequence subcommands (#2402). */
186
+ export const PLAN_SEQUENCE_ALIAS_SUBCOMMANDS = {
187
+ "plan-sequence:set": "set",
188
+ "plan-sequence:current": "current",
189
+ "plan-sequence:clear": "clear",
190
+ "plan-sequence:advance": "advance",
191
+ };
192
+ const PLAN_SEQUENCE_COLON_ALIASES = Object.fromEntries(Object.keys(PLAN_SEQUENCE_ALIAS_SUBCOMMANDS).map((alias) => [alias, "plan-sequence"]));
180
193
  /** Task-style aliases (framework_commands / Taskfile names). */
181
194
  export const VERB_ALIASES = {
195
+ "hook:dispatch": "hook-dispatch",
182
196
  "verify:encoding": "verify-encoding",
183
197
  "verify:forward-coverage": "verify-forward-coverage",
184
198
  "verify:branch": "verify-branch",
@@ -187,6 +201,7 @@ export const VERB_ALIASES = {
187
201
  "verify:agents-md-budget": "verify-agents-md-budget",
188
202
  "verify:agents-md-advisory": "verify-agents-md-advisory",
189
203
  "verify:eval-health-relocation": "verify-eval-health-relocation",
204
+ "verify:eval-triggers-relocation": "verify-eval-triggers-relocation",
190
205
  "verify:hooks-installed": "verify-hooks-installed",
191
206
  "verify:no-task-runtime": "verify-no-task-runtime",
192
207
  "vbrief:validate": "vbrief-validate",
@@ -212,6 +227,8 @@ export const VERB_ALIASES = {
212
227
  "verify:xbrief-drift": "verify-xbrief-drift",
213
228
  "verify:capacity": "verify-capacity",
214
229
  "verify:session-ritual": "verify-session-ritual",
230
+ "verify:plan-sequence": "verify-plan-sequence",
231
+ ...PLAN_SEQUENCE_COLON_ALIASES,
215
232
  "verify-strategy-output": "validate-strategy-output",
216
233
  "validate:strategy-output": "validate-strategy-output",
217
234
  "verify:codebase-map-fresh": "codebase-map-fresh",
@@ -244,6 +261,7 @@ export const VERB_ALIASES = {
244
261
  "value:show": "value-readback",
245
262
  "triage:metrics": "value-readback",
246
263
  "eval:run": "eval-run",
264
+ "eval:triggers": "eval-triggers",
247
265
  "eval:report": "eval-report",
248
266
  build: "framework-commands",
249
267
  "setup:ghx": "setup-ghx",
@@ -2514,13 +2532,16 @@ export async function dispatch(argv, io = defaultIo()) {
2514
2532
  const handler = await loadHandler(canonical, io);
2515
2533
  const triageSubcommand = verb !== undefined ? TRIAGE_ACTION_ALIAS_SUBCOMMANDS[verb] : undefined;
2516
2534
  const policySubcommand = verb !== undefined ? POLICY_ACTION_ALIAS_SUBCOMMANDS[verb] : undefined;
2535
+ const planSequenceSubcommand = verb !== undefined ? PLAN_SEQUENCE_ALIAS_SUBCOMMANDS[verb] : undefined;
2517
2536
  const handlerArgv = canonical === "framework-commands" && verb !== undefined && verb !== canonical
2518
2537
  ? [verb, ...rest]
2519
2538
  : triageSubcommand !== undefined && canonical === "triage-actions"
2520
2539
  ? [triageSubcommand, ...rest]
2521
2540
  : policySubcommand !== undefined && canonical === "policy"
2522
2541
  ? [policySubcommand, ...rest]
2523
- : rest;
2542
+ : planSequenceSubcommand !== undefined && canonical === "plan-sequence"
2543
+ ? [planSequenceSubcommand, ...rest]
2544
+ : rest;
2524
2545
  return await invokeHandler(handler, handlerArgv);
2525
2546
  }
2526
2547
  catch (err) {
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env node
2
+ interface ParsedArgs {
3
+ projectRoot: string;
4
+ json: boolean;
5
+ error?: string;
6
+ }
7
+ /** Parse eval-triggers CLI args. */
8
+ export declare function parseArgs(argv: string[]): ParsedArgs;
9
+ /** Run eval:triggers (offline skill-pi-trigger-eval routing) and return exit code. */
10
+ export declare function run(argv: string[]): number;
11
+ export {};
12
+ //# sourceMappingURL=eval-triggers.d.ts.map
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env node
2
+ import { resolve } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { runTriggerEval } from "@deftai/directive-core/eval/triggers";
5
+ /** Parse eval-triggers CLI args. */
6
+ export function parseArgs(argv) {
7
+ const parsed = {
8
+ projectRoot: ".",
9
+ json: false,
10
+ };
11
+ for (let i = 0; i < argv.length; i += 1) {
12
+ const arg = argv[i];
13
+ if (arg === "--json") {
14
+ parsed.json = true;
15
+ }
16
+ else if (arg === "--project-root") {
17
+ const value = argv[i + 1];
18
+ if (value === undefined) {
19
+ return { ...parsed, error: "argument --project-root: expected one argument" };
20
+ }
21
+ parsed.projectRoot = value;
22
+ i += 1;
23
+ }
24
+ else if (arg?.startsWith("--project-root=")) {
25
+ parsed.projectRoot = arg.slice("--project-root=".length);
26
+ }
27
+ else {
28
+ return { ...parsed, error: `unrecognized argument: ${arg}` };
29
+ }
30
+ }
31
+ return parsed;
32
+ }
33
+ /** Run eval:triggers (offline skill-pi-trigger-eval routing) and return exit code. */
34
+ export function run(argv) {
35
+ const args = parseArgs(argv);
36
+ if (args.error !== undefined) {
37
+ process.stderr.write(`eval:triggers: ${args.error}\n`);
38
+ return 2;
39
+ }
40
+ const result = runTriggerEval({ projectRoot: resolve(args.projectRoot) });
41
+ if (result.report === null) {
42
+ process.stderr.write(`${result.message}\n`);
43
+ return result.code;
44
+ }
45
+ if (args.json) {
46
+ process.stdout.write(`${JSON.stringify(result.report, null, 2)}\n`);
47
+ }
48
+ else {
49
+ process.stdout.write(`${result.message}\n`);
50
+ }
51
+ return result.code;
52
+ }
53
+ if (process.argv[1] !== undefined && fileURLToPath(import.meta.url) === process.argv[1]) {
54
+ process.exit(run(process.argv.slice(2)));
55
+ }
56
+ //# sourceMappingURL=eval-triggers.js.map
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env node
2
+ import { type HookEvent, type HookHost } from "@deftai/directive-core/hooks";
3
+ interface ParsedArgs {
4
+ host?: HookHost;
5
+ event?: HookEvent;
6
+ projectRoot?: string;
7
+ error?: string;
8
+ }
9
+ export interface HookDispatchCliSeams {
10
+ readonly readStdin?: () => string;
11
+ readonly writeOut?: (text: string) => void;
12
+ readonly writeErr?: (text: string) => void;
13
+ readonly cwd?: () => string;
14
+ }
15
+ export declare function parseArgs(argv: readonly string[]): ParsedArgs;
16
+ export declare function run(argv: string[], seams?: HookDispatchCliSeams): number;
17
+ export {};
18
+ //# sourceMappingURL=hook-dispatch.d.ts.map
@@ -0,0 +1,102 @@
1
+ #!/usr/bin/env node
2
+ import { readFileSync } from "node:fs";
3
+ import { resolve } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import { decideHook, isHookEvent, isHookHost, projectRootFromHookPayload, renderHostDecision, } from "@deftai/directive-core/hooks";
6
+ function takeValue(argv, index, flag) {
7
+ const token = argv[index];
8
+ const prefix = `${flag}=`;
9
+ if (token?.startsWith(prefix))
10
+ return { value: token.slice(prefix.length), next: index };
11
+ const value = argv[index + 1];
12
+ if (value === undefined)
13
+ return { next: index, error: `argument ${flag}: expected one argument` };
14
+ return { value, next: index + 1 };
15
+ }
16
+ export function parseArgs(argv) {
17
+ const parsed = {};
18
+ for (let i = 0; i < argv.length; i += 1) {
19
+ const token = argv[i];
20
+ if (token === "--host" || token?.startsWith("--host=")) {
21
+ const taken = takeValue(argv, i, "--host");
22
+ if (taken.error)
23
+ return { error: taken.error };
24
+ const value = taken.value ?? "";
25
+ if (!isHookHost(value)) {
26
+ return { error: `unsupported host: ${JSON.stringify(taken.value)}` };
27
+ }
28
+ parsed.host = value;
29
+ i = taken.next;
30
+ continue;
31
+ }
32
+ if (token === "--event" || token?.startsWith("--event=")) {
33
+ const taken = takeValue(argv, i, "--event");
34
+ if (taken.error)
35
+ return { error: taken.error };
36
+ const value = taken.value ?? "";
37
+ if (!isHookEvent(value)) {
38
+ return { error: `unsupported event: ${JSON.stringify(taken.value)}` };
39
+ }
40
+ parsed.event = value;
41
+ i = taken.next;
42
+ continue;
43
+ }
44
+ if (token === "--project-root" || token?.startsWith("--project-root=")) {
45
+ const taken = takeValue(argv, i, "--project-root");
46
+ if (taken.error)
47
+ return { error: taken.error };
48
+ parsed.projectRoot = taken.value;
49
+ i = taken.next;
50
+ continue;
51
+ }
52
+ return { error: `unrecognized argument: ${token}` };
53
+ }
54
+ if (parsed.host === undefined)
55
+ return { error: "--host is required" };
56
+ if (parsed.event === undefined)
57
+ return { error: "--event is required" };
58
+ return parsed;
59
+ }
60
+ function parsePayload(raw) {
61
+ if (raw.trim().length === 0)
62
+ return {};
63
+ try {
64
+ return JSON.parse(raw);
65
+ }
66
+ catch {
67
+ // tool.before is installed only on direct-write matchers, so an unreadable
68
+ // payload becomes a missing-tool denial rather than a fail-open crash.
69
+ return {};
70
+ }
71
+ }
72
+ export function run(argv, seams = {}) {
73
+ const args = parseArgs(argv);
74
+ const writeOut = seams.writeOut ?? ((text) => process.stdout.write(text));
75
+ const writeErr = seams.writeErr ?? ((text) => process.stderr.write(text));
76
+ if (args.error !== undefined || args.host === undefined || args.event === undefined) {
77
+ writeErr(`${args.error ?? "invalid hook-dispatch arguments"}\n`);
78
+ return 2;
79
+ }
80
+ const readStdin = seams.readStdin ?? (() => readFileSync(0, "utf8"));
81
+ const cwd = (seams.cwd ?? process.cwd)();
82
+ const payload = parsePayload(readStdin());
83
+ const projectRoot = args.projectRoot
84
+ ? resolve(args.projectRoot)
85
+ : projectRootFromHookPayload(payload, cwd);
86
+ const decision = decideHook({
87
+ host: args.host,
88
+ event: args.event,
89
+ projectRoot,
90
+ payload,
91
+ });
92
+ const rendered = renderHostDecision(args.host, decision);
93
+ if (rendered.length > 0)
94
+ writeOut(`${rendered}\n`);
95
+ if (decision.code === "session-start-degraded")
96
+ writeErr(`${decision.message}\n`);
97
+ return 0;
98
+ }
99
+ if (process.argv[1] !== undefined && fileURLToPath(import.meta.url) === process.argv[1]) {
100
+ process.exit(run(process.argv.slice(2)));
101
+ }
102
+ //# sourceMappingURL=hook-dispatch.js.map
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export declare function main(argv?: string[]): number;
3
+ //# sourceMappingURL=plan-sequence.d.ts.map
@@ -0,0 +1,167 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * plan-sequence CLI (#2402): set | current | clear | advance
4
+ */
5
+ import { readFileSync } from "node:fs";
6
+ import { resolve } from "node:path";
7
+ import { fileURLToPath } from "node:url";
8
+ import { advancePlanSequence, clearPlanSequence, createPlanSequence, parsePlanSequence, readPlanSequence, writePlanSequence, } from "@deftai/directive-core/plan-sequence";
9
+ function parseArgs(argv) {
10
+ const parsed = {
11
+ projectRoot: ".",
12
+ action: null,
13
+ file: null,
14
+ jsonInline: null,
15
+ emitJson: false,
16
+ };
17
+ const positionals = [];
18
+ for (let i = 0; i < argv.length; i += 1) {
19
+ const arg = argv[i];
20
+ if (arg === undefined)
21
+ continue;
22
+ if (arg === "--json") {
23
+ parsed.emitJson = true;
24
+ }
25
+ else if (arg === "--project-root") {
26
+ const value = argv[++i];
27
+ if (value === undefined) {
28
+ return { ...parsed, error: "--project-root requires a value" };
29
+ }
30
+ parsed.projectRoot = value;
31
+ }
32
+ else if (arg.startsWith("--project-root=")) {
33
+ parsed.projectRoot = arg.slice("--project-root=".length);
34
+ }
35
+ else if (arg === "--file") {
36
+ const value = argv[++i];
37
+ if (value === undefined) {
38
+ return { ...parsed, error: "--file requires a path" };
39
+ }
40
+ parsed.file = value;
41
+ }
42
+ else if (arg.startsWith("--file=")) {
43
+ parsed.file = arg.slice("--file=".length);
44
+ }
45
+ else if (arg === "--from-json") {
46
+ const value = argv[++i];
47
+ if (value === undefined) {
48
+ return { ...parsed, error: "--from-json requires a JSON string" };
49
+ }
50
+ parsed.jsonInline = value;
51
+ }
52
+ else if (arg.startsWith("-")) {
53
+ return { ...parsed, error: `unknown flag: ${arg}` };
54
+ }
55
+ else {
56
+ positionals.push(arg);
57
+ }
58
+ }
59
+ const action = positionals[0];
60
+ if (action === "set" || action === "current" || action === "clear" || action === "advance") {
61
+ parsed.action = action;
62
+ }
63
+ else if (action !== undefined) {
64
+ return { ...parsed, error: `unknown action: ${action} (set|current|clear|advance)` };
65
+ }
66
+ else {
67
+ parsed.error =
68
+ "usage: plan-sequence <set|current|clear|advance> [--file path] [--project-root path]";
69
+ }
70
+ return parsed;
71
+ }
72
+ function loadSetPayload(parsed, root) {
73
+ const text = parsed.file ? readFileSync(resolve(root, parsed.file), "utf8") : parsed.jsonInline;
74
+ if (text === null || text === undefined) {
75
+ throw new Error("plan-sequence set requires --file <path> or --from-json <json>");
76
+ }
77
+ const payload = JSON.parse(text);
78
+ if (typeof payload !== "object" || payload === null || Array.isArray(payload)) {
79
+ throw new Error("plan-sequence set: --file/--from-json must contain a JSON object");
80
+ }
81
+ const raw = payload;
82
+ if (typeof raw.current_index === "number" || raw.exhausted === true) {
83
+ return parsePlanSequence(raw);
84
+ }
85
+ return createPlanSequence({
86
+ sequence_id: String(raw.sequence_id),
87
+ sequence_kind: raw.sequence_kind,
88
+ entries: raw.entries,
89
+ authorized_by: String(raw.authorized_by ?? ""),
90
+ batching_allowed: raw.batching_allowed === true,
91
+ continuation_past_final: raw.continuation_past_final === true,
92
+ });
93
+ }
94
+ export function main(argv = process.argv.slice(2)) {
95
+ const parsed = parseArgs(argv);
96
+ if (parsed.error || parsed.action === null) {
97
+ process.stderr.write(`${parsed.error ?? "usage error"}\n`);
98
+ return 2;
99
+ }
100
+ const root = resolve(parsed.projectRoot);
101
+ try {
102
+ if (parsed.action === "set") {
103
+ const seq = loadSetPayload(parsed, root);
104
+ const path = writePlanSequence(root, seq);
105
+ if (parsed.emitJson) {
106
+ process.stdout.write(`${JSON.stringify({ ok: true, path, sequence: seq }, null, 2)}\n`);
107
+ }
108
+ else {
109
+ process.stdout.write(`OK plan-sequence set ${seq.sequence_id} (${seq.entries.length} entries, index=${seq.current_index})\n`);
110
+ }
111
+ return 0;
112
+ }
113
+ if (parsed.action === "current") {
114
+ const seq = readPlanSequence(root);
115
+ if (seq === null) {
116
+ process.stderr.write("No active ordered-plan sequence (.deft/plan-sequence.json missing).\n");
117
+ return 1;
118
+ }
119
+ if (parsed.emitJson) {
120
+ process.stdout.write(`${JSON.stringify(seq, null, 2)}\n`);
121
+ }
122
+ else {
123
+ const cur = seq.exhausted ? null : seq.entries[seq.current_index];
124
+ process.stdout.write(`sequence_id=${seq.sequence_id} kind=${seq.sequence_kind} index=${seq.current_index} exhausted=${String(seq.exhausted)}\n`);
125
+ if (cur) {
126
+ const issue = cur.issue !== undefined ? ` (#${cur.issue})` : "";
127
+ process.stdout.write(`current: ${cur.kind}:${cur.id}${issue}\n`);
128
+ }
129
+ else {
130
+ process.stdout.write("current: (exhausted — fresh approval required)\n");
131
+ }
132
+ }
133
+ return 0;
134
+ }
135
+ if (parsed.action === "clear") {
136
+ const cleared = clearPlanSequence(root);
137
+ process.stdout.write(cleared ? "OK plan-sequence cleared\n" : "OK plan-sequence already absent\n");
138
+ return 0;
139
+ }
140
+ const seq = readPlanSequence(root);
141
+ if (seq === null) {
142
+ process.stderr.write("No active ordered-plan sequence to advance.\n");
143
+ return 1;
144
+ }
145
+ const next = advancePlanSequence(seq);
146
+ writePlanSequence(root, next);
147
+ if (parsed.emitJson) {
148
+ process.stdout.write(`${JSON.stringify(next, null, 2)}\n`);
149
+ }
150
+ else if (next.exhausted) {
151
+ process.stdout.write("OK advanced; sequence exhausted. Fresh operator approval required before another unit.\n");
152
+ }
153
+ else {
154
+ const cur = next.entries[next.current_index];
155
+ process.stdout.write(`OK advanced to index=${next.current_index} (${cur?.kind}:${cur?.id})\n`);
156
+ }
157
+ return 0;
158
+ }
159
+ catch (err) {
160
+ process.stderr.write(`${err instanceof Error ? err.message : String(err)}\n`);
161
+ return 1;
162
+ }
163
+ }
164
+ if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
165
+ process.exit(main());
166
+ }
167
+ //# sourceMappingURL=plan-sequence.js.map
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env node
2
+ interface ParsedArgs {
3
+ projectRoot: string;
4
+ baseRef?: string;
5
+ staged: boolean;
6
+ quiet: boolean;
7
+ error?: string;
8
+ }
9
+ /** Parse verify-eval-triggers-relocation CLI args. */
10
+ export declare function parseArgs(argv: string[]): ParsedArgs;
11
+ /** Run verify:eval-triggers-relocation and return exit code. */
12
+ export declare function run(argv: string[]): number;
13
+ export {};
14
+ //# sourceMappingURL=verify-eval-triggers-relocation.d.ts.map
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env node
2
+ import { resolve } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { evaluate } from "@deftai/directive-core/eval-triggers-relocation";
5
+ /** Parse verify-eval-triggers-relocation CLI args. */
6
+ export function parseArgs(argv) {
7
+ const parsed = {
8
+ projectRoot: ".",
9
+ staged: false,
10
+ quiet: false,
11
+ };
12
+ for (let i = 0; i < argv.length; i += 1) {
13
+ const arg = argv[i];
14
+ if (arg === "--staged") {
15
+ parsed.staged = true;
16
+ }
17
+ else if (arg === "--quiet") {
18
+ parsed.quiet = true;
19
+ }
20
+ else if (arg === "--base-ref") {
21
+ const value = argv[i + 1];
22
+ if (value === undefined) {
23
+ return { ...parsed, error: "argument --base-ref: expected one argument" };
24
+ }
25
+ parsed.baseRef = value;
26
+ i += 1;
27
+ }
28
+ else if (arg?.startsWith("--base-ref=")) {
29
+ parsed.baseRef = arg.slice("--base-ref=".length);
30
+ }
31
+ else if (arg === "--project-root") {
32
+ const value = argv[i + 1];
33
+ if (value === undefined) {
34
+ return { ...parsed, error: "argument --project-root: expected one argument" };
35
+ }
36
+ parsed.projectRoot = value;
37
+ i += 1;
38
+ }
39
+ else if (arg?.startsWith("--project-root=")) {
40
+ parsed.projectRoot = arg.slice("--project-root=".length);
41
+ }
42
+ else {
43
+ return { ...parsed, error: `unrecognized argument: ${arg}` };
44
+ }
45
+ }
46
+ return parsed;
47
+ }
48
+ /** Run verify:eval-triggers-relocation and return exit code. */
49
+ export function run(argv) {
50
+ const args = parseArgs(argv);
51
+ if (args.error !== undefined) {
52
+ process.stderr.write(`verify:eval-triggers-relocation: ${args.error}\n`);
53
+ return 2;
54
+ }
55
+ const result = evaluate({
56
+ projectRoot: resolve(args.projectRoot),
57
+ baseRef: args.baseRef,
58
+ staged: args.staged,
59
+ quiet: args.quiet,
60
+ });
61
+ if (result.message.length > 0) {
62
+ if (result.stream === "stderr") {
63
+ process.stderr.write(`${result.message}\n`);
64
+ }
65
+ else if (result.stream === "stdout") {
66
+ process.stdout.write(`${result.message}\n`);
67
+ }
68
+ }
69
+ return result.code;
70
+ }
71
+ if (process.argv[1] !== undefined && fileURLToPath(import.meta.url) === process.argv[1]) {
72
+ process.exit(run(process.argv.slice(2)));
73
+ }
74
+ //# sourceMappingURL=verify-eval-triggers-relocation.js.map
@@ -1,7 +1,9 @@
1
1
  #!/usr/bin/env node
2
+ type HookScope = "git" | "agent" | "all";
2
3
  interface ParsedArgs {
3
4
  projectRoot: string;
4
5
  quiet: boolean;
6
+ scope: HookScope;
5
7
  error?: string;
6
8
  }
7
9
  export declare function parseArgs(argv: string[]): ParsedArgs;
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import { resolve } from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
- import { evaluate } from "@deftai/directive-core/verify-env";
4
+ import { evaluate, evaluateAgentHooks } from "@deftai/directive-core/verify-env";
5
5
  export function parseArgs(argv) {
6
- const parsed = { projectRoot: ".", quiet: false };
6
+ const parsed = { projectRoot: ".", quiet: false, scope: "git" };
7
7
  for (let i = 0; i < argv.length; i += 1) {
8
8
  const arg = argv[i];
9
9
  if (arg === "--quiet")
@@ -19,6 +19,24 @@ export function parseArgs(argv) {
19
19
  else if (arg?.startsWith("--project-root=")) {
20
20
  parsed.projectRoot = arg.slice("--project-root=".length);
21
21
  }
22
+ else if (arg === "--scope") {
23
+ const value = argv[i + 1];
24
+ if (value === undefined) {
25
+ return { ...parsed, error: "argument --scope: expected one argument" };
26
+ }
27
+ if (value !== "git" && value !== "agent" && value !== "all") {
28
+ return { ...parsed, error: `argument --scope: invalid choice: '${value}'` };
29
+ }
30
+ parsed.scope = value;
31
+ i += 1;
32
+ }
33
+ else if (arg?.startsWith("--scope=")) {
34
+ const value = arg.slice("--scope=".length);
35
+ if (value !== "git" && value !== "agent" && value !== "all") {
36
+ return { ...parsed, error: `argument --scope: invalid choice: '${value}'` };
37
+ }
38
+ parsed.scope = value;
39
+ }
22
40
  else {
23
41
  return { ...parsed, error: `unrecognized arguments: ${arg}` };
24
42
  }
@@ -31,16 +49,22 @@ export function run(argv) {
31
49
  process.stderr.write(`${args.error}\n`);
32
50
  return 2;
33
51
  }
34
- const result = evaluate(resolve(args.projectRoot));
52
+ const projectRoot = resolve(args.projectRoot);
53
+ const results = [
54
+ ...(args.scope === "git" || args.scope === "all" ? [evaluate(projectRoot)] : []),
55
+ ...(args.scope === "agent" || args.scope === "all" ? [evaluateAgentHooks(projectRoot)] : []),
56
+ ];
35
57
  if (!args.quiet) {
36
- if (result.stream === "stdout") {
37
- process.stdout.write(`${result.message}\n`);
38
- }
39
- else if (result.stream === "stderr") {
40
- process.stderr.write(`${result.message}\n`);
58
+ for (const result of results) {
59
+ if (result.stream === "stdout") {
60
+ process.stdout.write(`${result.message}\n`);
61
+ }
62
+ else if (result.stream === "stderr") {
63
+ process.stderr.write(`${result.message}\n`);
64
+ }
41
65
  }
42
66
  }
43
- return result.code;
67
+ return Math.max(...results.map((result) => result.code));
44
68
  }
45
69
  if (process.argv[1] !== undefined && fileURLToPath(import.meta.url) === process.argv[1]) {
46
70
  process.exit(run(process.argv.slice(2)));
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export declare function main(argv?: string[]): number;
3
+ //# sourceMappingURL=verify-plan-sequence.d.ts.map
@@ -0,0 +1,110 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * verify:plan-sequence (#2402) — fail closed unless target matches current entry.
4
+ */
5
+ import { resolve } from "node:path";
6
+ import { fileURLToPath } from "node:url";
7
+ import { readPlanSequence, verifyPlanTarget, } from "@deftai/directive-core/plan-sequence";
8
+ const KINDS = [
9
+ "pr",
10
+ "issue",
11
+ "story",
12
+ "task",
13
+ "phase",
14
+ "checklist",
15
+ "review",
16
+ ];
17
+ function parseArgs(argv) {
18
+ const parsed = {
19
+ projectRoot: ".",
20
+ targetKind: null,
21
+ target: null,
22
+ emitJson: false,
23
+ };
24
+ for (let i = 0; i < argv.length; i += 1) {
25
+ const arg = argv[i];
26
+ if (arg === undefined)
27
+ continue;
28
+ if (arg === "--json") {
29
+ parsed.emitJson = true;
30
+ }
31
+ else if (arg === "--project-root") {
32
+ const value = argv[++i];
33
+ if (value === undefined)
34
+ return { ...parsed, error: "--project-root requires a value" };
35
+ parsed.projectRoot = value;
36
+ }
37
+ else if (arg.startsWith("--project-root=")) {
38
+ parsed.projectRoot = arg.slice("--project-root=".length);
39
+ }
40
+ else if (arg === "--target-kind") {
41
+ const value = argv[++i];
42
+ if (value === undefined || !KINDS.includes(value)) {
43
+ return { ...parsed, error: `--target-kind must be one of ${KINDS.join("|")}` };
44
+ }
45
+ parsed.targetKind = value;
46
+ }
47
+ else if (arg.startsWith("--target-kind=")) {
48
+ const value = arg.slice("--target-kind=".length);
49
+ if (!KINDS.includes(value)) {
50
+ return { ...parsed, error: `--target-kind must be one of ${KINDS.join("|")}` };
51
+ }
52
+ parsed.targetKind = value;
53
+ }
54
+ else if (arg === "--target") {
55
+ const value = argv[++i];
56
+ if (value === undefined)
57
+ return { ...parsed, error: "--target requires a value" };
58
+ parsed.target = value;
59
+ }
60
+ else if (arg.startsWith("--target=")) {
61
+ parsed.target = arg.slice("--target=".length);
62
+ }
63
+ else if (arg.startsWith("-")) {
64
+ return { ...parsed, error: `unknown flag: ${arg}` };
65
+ }
66
+ }
67
+ if (parsed.targetKind === null || parsed.target === null) {
68
+ return {
69
+ ...parsed,
70
+ error: "usage: verify:plan-sequence -- --target-kind <kind> --target <id-or-title>",
71
+ };
72
+ }
73
+ return parsed;
74
+ }
75
+ export function main(argv = process.argv.slice(2)) {
76
+ const parsed = parseArgs(argv);
77
+ if (parsed.error || parsed.targetKind === null || parsed.target === null) {
78
+ process.stderr.write(`${parsed.error ?? "usage error"}\n`);
79
+ return 2;
80
+ }
81
+ const seq = readPlanSequence(resolve(parsed.projectRoot));
82
+ // No sequence: allow (caller may have explicit operator approval); warn on stderr.
83
+ if (seq === null) {
84
+ if (parsed.emitJson) {
85
+ process.stdout.write(`${JSON.stringify({ ok: true, skipped: true, reason: "no-active-sequence" }, null, 2)}\n`);
86
+ }
87
+ else {
88
+ process.stdout.write("OK verify:plan-sequence skipped (no active ordered-plan sequence)\n");
89
+ }
90
+ return 0;
91
+ }
92
+ const result = verifyPlanTarget(seq, {
93
+ targetKind: parsed.targetKind,
94
+ target: parsed.target,
95
+ });
96
+ if (parsed.emitJson) {
97
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
98
+ }
99
+ else if (result.ok) {
100
+ process.stdout.write(`OK verify:plan-sequence matched ${result.entry.kind}:${result.entry.id} (index=${result.index})\n`);
101
+ }
102
+ else {
103
+ process.stderr.write(`${result.message}\n`);
104
+ }
105
+ return result.ok ? 0 : 1;
106
+ }
107
+ if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
108
+ process.exit(main());
109
+ }
110
+ //# sourceMappingURL=verify-plan-sequence.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deftai/directive",
3
- "version": "0.76.0",
3
+ "version": "0.78.0",
4
4
  "description": "Directive CLI — npm install path for the Deft Directive framework.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -31,8 +31,8 @@
31
31
  "provenance": true
32
32
  },
33
33
  "dependencies": {
34
- "@deftai/directive-core": "^0.76.0",
35
- "@deftai/directive-content": "^0.76.0"
34
+ "@deftai/directive-core": "^0.78.0",
35
+ "@deftai/directive-content": "^0.78.0"
36
36
  },
37
37
  "scripts": {
38
38
  "build": "tsc -b"