@deftai/directive 0.75.0 → 0.77.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", "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", "eval-health", "eval-run", "eval-report"];
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
@@ -61,6 +61,7 @@ export const CLI_MODULE_VERBS = [
61
61
  "release-rollback",
62
62
  "scope-lifecycle",
63
63
  "session-start",
64
+ "plan-sequence",
64
65
  "slice",
65
66
  "subagent-monitor",
66
67
  "toolchain-check",
@@ -103,6 +104,7 @@ export const CLI_MODULE_VERBS = [
103
104
  "verify-go-freeze",
104
105
  "verify-scm-boundary",
105
106
  "verify-session-ritual",
107
+ "verify-plan-sequence",
106
108
  "verify-stubs",
107
109
  "verify-xbrief-drift",
108
110
  "rule-ownership-lint",
@@ -177,6 +179,14 @@ export const POLICY_ACTION_ALIAS_SUBCOMMANDS = {
177
179
  "policy:enable-value-feedback": "enable-value-feedback",
178
180
  };
179
181
  const POLICY_ACTION_COLON_ALIASES = Object.fromEntries(Object.keys(POLICY_ACTION_ALIAS_SUBCOMMANDS).map((alias) => [alias, "policy"]));
182
+ /** Colon aliases for plan-sequence subcommands (#2402). */
183
+ export const PLAN_SEQUENCE_ALIAS_SUBCOMMANDS = {
184
+ "plan-sequence:set": "set",
185
+ "plan-sequence:current": "current",
186
+ "plan-sequence:clear": "clear",
187
+ "plan-sequence:advance": "advance",
188
+ };
189
+ const PLAN_SEQUENCE_COLON_ALIASES = Object.fromEntries(Object.keys(PLAN_SEQUENCE_ALIAS_SUBCOMMANDS).map((alias) => [alias, "plan-sequence"]));
180
190
  /** Task-style aliases (framework_commands / Taskfile names). */
181
191
  export const VERB_ALIASES = {
182
192
  "verify:encoding": "verify-encoding",
@@ -191,6 +201,7 @@ export const VERB_ALIASES = {
191
201
  "verify:no-task-runtime": "verify-no-task-runtime",
192
202
  "vbrief:validate": "vbrief-validate",
193
203
  "vbrief:preflight": "vbrief-preflight",
204
+ "xbrief:preflight": "vbrief-preflight",
194
205
  "vbrief:activate": "vbrief-activate",
195
206
  "verify:story-ready": "verify-story-ready",
196
207
  "verify:tools": "verify-tools",
@@ -211,6 +222,8 @@ export const VERB_ALIASES = {
211
222
  "verify:xbrief-drift": "verify-xbrief-drift",
212
223
  "verify:capacity": "verify-capacity",
213
224
  "verify:session-ritual": "verify-session-ritual",
225
+ "verify:plan-sequence": "verify-plan-sequence",
226
+ ...PLAN_SEQUENCE_COLON_ALIASES,
214
227
  "verify-strategy-output": "validate-strategy-output",
215
228
  "validate:strategy-output": "validate-strategy-output",
216
229
  "verify:codebase-map-fresh": "codebase-map-fresh",
@@ -2513,13 +2526,16 @@ export async function dispatch(argv, io = defaultIo()) {
2513
2526
  const handler = await loadHandler(canonical, io);
2514
2527
  const triageSubcommand = verb !== undefined ? TRIAGE_ACTION_ALIAS_SUBCOMMANDS[verb] : undefined;
2515
2528
  const policySubcommand = verb !== undefined ? POLICY_ACTION_ALIAS_SUBCOMMANDS[verb] : undefined;
2529
+ const planSequenceSubcommand = verb !== undefined ? PLAN_SEQUENCE_ALIAS_SUBCOMMANDS[verb] : undefined;
2516
2530
  const handlerArgv = canonical === "framework-commands" && verb !== undefined && verb !== canonical
2517
2531
  ? [verb, ...rest]
2518
2532
  : triageSubcommand !== undefined && canonical === "triage-actions"
2519
2533
  ? [triageSubcommand, ...rest]
2520
2534
  : policySubcommand !== undefined && canonical === "policy"
2521
2535
  ? [policySubcommand, ...rest]
2522
- : rest;
2536
+ : planSequenceSubcommand !== undefined && canonical === "plan-sequence"
2537
+ ? [planSequenceSubcommand, ...rest]
2538
+ : rest;
2523
2539
  return await invokeHandler(handler, handlerArgv);
2524
2540
  }
2525
2541
  catch (err) {
@@ -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
@@ -4,6 +4,7 @@ export interface ParsedSessionStartArgs {
4
4
  deferValues: string[];
5
5
  emitJson: boolean;
6
6
  noHistory: boolean;
7
+ readOnly: boolean;
7
8
  error?: string;
8
9
  }
9
10
  /** Parse session:start CLI args, mirroring scripts/session_start.py. */
@@ -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 { parseDeferrals, ritualStatePath, runSessionStart } from "@deftai/directive-core/session";
4
+ import { parseDeferrals, READ_ONLY_POSTURE, ritualStatePath, runSessionStart, } from "@deftai/directive-core/session";
5
5
  /** Parse session:start CLI args, mirroring scripts/session_start.py. */
6
6
  export function parseArgs(argv) {
7
7
  const parsed = {
@@ -9,6 +9,7 @@ export function parseArgs(argv) {
9
9
  deferValues: [],
10
10
  emitJson: false,
11
11
  noHistory: false,
12
+ readOnly: false,
12
13
  };
13
14
  for (let i = 0; i < argv.length; i += 1) {
14
15
  const arg = argv[i];
@@ -18,6 +19,9 @@ export function parseArgs(argv) {
18
19
  else if (arg === "--no-history") {
19
20
  parsed.noHistory = true;
20
21
  }
22
+ else if (arg === "--read-only") {
23
+ parsed.readOnly = true;
24
+ }
21
25
  else if (arg === "--project-root") {
22
26
  const value = argv[i + 1];
23
27
  if (value === undefined) {
@@ -76,6 +80,7 @@ export function run(argv) {
76
80
  result = runSessionStart(projectRoot, {
77
81
  deferrals,
78
82
  writeHistory: !args.noHistory,
83
+ posture: args.readOnly ? READ_ONLY_POSTURE : undefined,
79
84
  });
80
85
  }
81
86
  finally {
@@ -101,7 +106,13 @@ export function run(argv) {
101
106
  sink.write(`${line}\n`);
102
107
  }
103
108
  if (result.code === 0) {
104
- process.stdout.write(`[deft] session ritual recorded at ${ritualStatePath(projectRoot)}\n`);
109
+ const posture = result.payload.posture;
110
+ if (posture === READ_ONLY_POSTURE) {
111
+ process.stdout.write(`[deft] ${String(result.payload.message)}\n`);
112
+ }
113
+ else {
114
+ process.stdout.write(`[deft] session ritual recorded at ${ritualStatePath(projectRoot)} (diagnostic-only; not posture authority)\n`);
115
+ }
105
116
  }
106
117
  return result.code;
107
118
  }
@@ -4,7 +4,7 @@ interface ParsedArgs {
4
4
  emitJson: boolean;
5
5
  error?: string;
6
6
  }
7
- /** Parse vbrief-preflight CLI args, mirroring the Python argparse surface (#810). */
7
+ /** Parse vbrief-preflight / xbrief:preflight CLI args (#810 / #2449). */
8
8
  export declare function parseArgs(argv: string[]): ParsedArgs;
9
9
  /** Run the gate and return the process exit code (parse errors -> 2). */
10
10
  export declare function run(argv: string[]): number;
@@ -1,7 +1,17 @@
1
1
  #!/usr/bin/env node
2
2
  import { fileURLToPath } from "node:url";
3
- import { emitJson, evaluate } from "@deftai/directive-core/preflight";
4
- /** Parse vbrief-preflight CLI args, mirroring the Python argparse surface (#810). */
3
+ import { emitJson, evaluate, PREFLIGHT_USAGE_HINT } from "@deftai/directive-core/preflight";
4
+ const PATH_FLAG_NAMES = ["--vbrief-path", "--brief-path", "--xbrief-path"];
5
+ function assignPathFlag(parsed, value) {
6
+ if (value === undefined || value.length === 0) {
7
+ return { ...parsed, error: "argument --vbrief-path: expected one argument" };
8
+ }
9
+ if (parsed.vbriefPath !== null) {
10
+ return { ...parsed, error: "multiple path arguments are not allowed" };
11
+ }
12
+ return { ...parsed, vbriefPath: value };
13
+ }
14
+ /** Parse vbrief-preflight / xbrief:preflight CLI args (#810 / #2449). */
5
15
  export function parseArgs(argv) {
6
16
  const parsed = {
7
17
  vbriefPath: null,
@@ -11,24 +21,45 @@ export function parseArgs(argv) {
11
21
  const arg = argv[i];
12
22
  if (arg === "--json") {
13
23
  parsed.emitJson = true;
24
+ continue;
14
25
  }
15
- else if (arg === "--vbrief-path") {
16
- const value = argv[i + 1];
17
- if (value === undefined) {
18
- return { ...parsed, error: "argument --vbrief-path: expected one argument" };
19
- }
20
- parsed.vbriefPath = value;
26
+ if (arg === "--help" || arg === "-h") {
27
+ return parsed;
28
+ }
29
+ if (PATH_FLAG_NAMES.includes(arg)) {
30
+ const next = assignPathFlag(parsed, argv[i + 1]);
31
+ if (next?.error !== undefined)
32
+ return next;
33
+ parsed.vbriefPath = next?.vbriefPath ?? null;
21
34
  i += 1;
35
+ continue;
22
36
  }
23
- else if (arg?.startsWith("--vbrief-path=")) {
24
- parsed.vbriefPath = arg.slice("--vbrief-path=".length);
37
+ let matchedEqualsForm = false;
38
+ for (const flag of PATH_FLAG_NAMES) {
39
+ if (arg?.startsWith(`${flag}=`)) {
40
+ const next = assignPathFlag(parsed, arg.slice(flag.length + 1));
41
+ if (next?.error !== undefined)
42
+ return next;
43
+ parsed.vbriefPath = next?.vbriefPath ?? null;
44
+ matchedEqualsForm = true;
45
+ break;
46
+ }
25
47
  }
26
- else {
48
+ if (matchedEqualsForm)
49
+ continue;
50
+ if (arg?.startsWith("-")) {
27
51
  return { ...parsed, error: `unrecognized argument: ${arg}` };
28
52
  }
53
+ const next = assignPathFlag(parsed, arg);
54
+ if (next?.error !== undefined)
55
+ return next;
56
+ parsed.vbriefPath = next?.vbriefPath ?? null;
29
57
  }
30
58
  if (parsed.vbriefPath === null) {
31
- return { ...parsed, error: "the following arguments are required: --vbrief-path" };
59
+ return {
60
+ ...parsed,
61
+ error: `the following arguments are required: --vbrief-path (or positional path). ${PREFLIGHT_USAGE_HINT}`,
62
+ };
32
63
  }
33
64
  return parsed;
34
65
  }
@@ -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
@@ -1,7 +1,9 @@
1
1
  #!/usr/bin/env node
2
+ import { type DirectivePosture } from "@deftai/directive-core/session";
2
3
  interface ParsedArgs {
3
4
  projectRoot: string;
4
5
  tier: "quick" | "gated";
6
+ posture: DirectivePosture | null;
5
7
  emitJson: boolean;
6
8
  error?: string;
7
9
  }
@@ -2,9 +2,21 @@
2
2
  import { resolve } from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
4
  import { emitBypassWarning, emitVerifyJson, verifySessionRitual, } from "@deftai/directive-core/session";
5
+ function parsePosture(value) {
6
+ if (value === "read-only")
7
+ return "read-only";
8
+ if (value === "mutation" || value === "mutating")
9
+ return "mutation";
10
+ return null;
11
+ }
5
12
  /** Parse verify-session-ritual CLI args, mirroring scripts/verify_session_ritual.py. */
6
13
  export function parseArgs(argv) {
7
- const parsed = { projectRoot: ".", tier: "quick", emitJson: false };
14
+ const parsed = {
15
+ projectRoot: ".",
16
+ tier: "quick",
17
+ posture: null,
18
+ emitJson: false,
19
+ };
8
20
  for (let i = 0; i < argv.length; i += 1) {
9
21
  const arg = argv[i];
10
22
  if (arg === "--json") {
@@ -39,6 +51,32 @@ export function parseArgs(argv) {
39
51
  }
40
52
  parsed.tier = value;
41
53
  }
54
+ else if (arg === "--posture") {
55
+ const value = argv[i + 1];
56
+ if (value === undefined) {
57
+ return { ...parsed, error: "argument --posture: expected one argument" };
58
+ }
59
+ const posture = parsePosture(value);
60
+ if (posture === null) {
61
+ return {
62
+ ...parsed,
63
+ error: `argument --posture: invalid choice: '${value}' (expected read-only|mutation)`,
64
+ };
65
+ }
66
+ parsed.posture = posture;
67
+ i += 1;
68
+ }
69
+ else if (arg?.startsWith("--posture=")) {
70
+ const value = arg.slice("--posture=".length);
71
+ const posture = parsePosture(value);
72
+ if (posture === null) {
73
+ return {
74
+ ...parsed,
75
+ error: `argument --posture: invalid choice: '${value}' (expected read-only|mutation)`,
76
+ };
77
+ }
78
+ parsed.posture = posture;
79
+ }
42
80
  else {
43
81
  return { ...parsed, error: `unrecognized argument: ${arg}` };
44
82
  }
@@ -53,7 +91,10 @@ export function run(argv) {
53
91
  return 2;
54
92
  }
55
93
  const projectRoot = resolve(args.projectRoot);
56
- const result = verifySessionRitual(projectRoot, { tier: args.tier });
94
+ const result = verifySessionRitual(projectRoot, {
95
+ tier: args.tier,
96
+ posture: args.posture ?? undefined,
97
+ });
57
98
  const warning = emitBypassWarning(result);
58
99
  const warningNeeded = result.bypassed && result.wouldFailCode !== null;
59
100
  if (args.emitJson) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deftai/directive",
3
- "version": "0.75.0",
3
+ "version": "0.77.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.75.0",
35
- "@deftai/directive-content": "^0.75.0"
34
+ "@deftai/directive-core": "^0.77.0",
35
+ "@deftai/directive-content": "^0.77.0"
36
36
  },
37
37
  "scripts": {
38
38
  "build": "tsc -b"