@deftai/directive 0.85.0 → 0.86.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.
package/dist/dispatch.js CHANGED
@@ -8,10 +8,12 @@ import { existsSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, readSync
8
8
  import { homedir, tmpdir } from "node:os";
9
9
  import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
10
10
  import { engineInfo, userConfig } from "@deftai/directive-core";
11
+ import { assertWriteTargetSafe } from "@deftai/directive-core/dist/fs/projection-containment.js";
11
12
  import { parseInitArgv, runInitDepositCli } from "@deftai/directive-core/init-deposit";
12
13
  import { appendAuditLog, disclosureLine, migrateLegacyPolicyKey, PLAN_POLICY_KEY, policyColonInvocation, policySetInvocation, projectDefinitionPath, resolvePolicy, resolveWipCap, setPolicy, } from "@deftai/directive-core/policy";
13
14
  import { defaultWhich } from "@deftai/directive-core/scm";
14
15
  import { KNOWN_SUBAGENT_BACKEND_IDS, probeSubagentBackends, resolveSwarmSubagentBackend, } from "@deftai/directive-core/swarm";
16
+ import { atomicWriteProjectDefinition } from "@deftai/directive-core/vbrief-build";
15
17
  const HANDLER_KEYS = [
16
18
  "run",
17
19
  "main",
@@ -2035,7 +2037,8 @@ function writeWipCap(projectRoot, cap, actor, note) {
2035
2037
  const { path, data, policyBlock } = loadProjectDefinitionForWrite(projectRoot);
2036
2038
  const previous = policyBlock.wipCap;
2037
2039
  policyBlock.wipCap = cap;
2038
- writeFileSync(path, `${JSON.stringify(data, null, 2)}\n`, "utf8");
2040
+ assertWriteTargetSafe(projectRoot, path);
2041
+ atomicWriteProjectDefinition(path, data);
2039
2042
  const changed = previous !== cap;
2040
2043
  const parts = [`actor=${actor}`, `wipCap=${cap}`, `previous=${pyRepr(previous)}`];
2041
2044
  if (note)
@@ -2049,7 +2052,8 @@ function writeSubagentBackend(projectRoot, backendId, actor, note) {
2049
2052
  const { path, data, policyBlock } = loadProjectDefinitionForWrite(projectRoot);
2050
2053
  const previous = policyBlock.swarmSubagentBackend;
2051
2054
  policyBlock.swarmSubagentBackend = backendId;
2052
- writeFileSync(path, `${JSON.stringify(data, null, 2)}\n`, "utf8");
2055
+ assertWriteTargetSafe(projectRoot, path);
2056
+ atomicWriteProjectDefinition(path, data);
2053
2057
  const changed = previous !== backendId;
2054
2058
  const parts = [
2055
2059
  `actor=${actor}`,
@@ -0,0 +1,6 @@
1
+ /**
2
+ * True when this module is the process entrypoint, including npm bin symlinks.
3
+ * Node passes the symlink path in argv[1]; compare via realpath on both sides.
4
+ */
5
+ export declare function isDirectEntrypoint(moduleUrl: string | URL): boolean;
6
+ //# sourceMappingURL=entrypoint.d.ts.map
@@ -0,0 +1,19 @@
1
+ import { realpathSync } from "node:fs";
2
+ import { fileURLToPath } from "node:url";
3
+ /**
4
+ * True when this module is the process entrypoint, including npm bin symlinks.
5
+ * Node passes the symlink path in argv[1]; compare via realpath on both sides.
6
+ */
7
+ export function isDirectEntrypoint(moduleUrl) {
8
+ const argv1 = process.argv[1];
9
+ if (argv1 === undefined)
10
+ return false;
11
+ try {
12
+ const modulePath = fileURLToPath(moduleUrl);
13
+ return realpathSync(argv1) === realpathSync(modulePath);
14
+ }
15
+ catch {
16
+ return false;
17
+ }
18
+ }
19
+ //# sourceMappingURL=entrypoint.js.map
package/dist/hook-bin.js CHANGED
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
- import { fileURLToPath } from "node:url";
2
+ import { isDirectEntrypoint } from "./entrypoint.js";
3
3
  import { run } from "./hook-dispatch.js";
4
4
  // Keep PreToolUse off the general CLI router's cold path.
5
5
  export function main(argv = process.argv.slice(2)) {
6
6
  return run(argv);
7
7
  }
8
- if (process.argv[1] !== undefined && fileURLToPath(import.meta.url) === process.argv[1]) {
8
+ if (isDirectEntrypoint(import.meta.url)) {
9
9
  process.exit(main());
10
10
  }
11
11
  //# sourceMappingURL=hook-bin.js.map
@@ -2,6 +2,7 @@
2
2
  interface ParsedArgs {
3
3
  vbriefPath: string | null;
4
4
  emitJson: boolean;
5
+ help?: boolean;
5
6
  error?: string;
6
7
  }
7
8
  /** Parse vbrief-preflight / xbrief:preflight CLI args (#810 / #2449). */
@@ -1,6 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
  import { fileURLToPath } from "node:url";
3
3
  import { emitJson, evaluate, PREFLIGHT_USAGE_HINT } from "@deftai/directive-core/preflight";
4
+ const HELP_TEXT = `usage: xbrief:preflight [--vbrief-path PATH | PATH] [--json] [--help]
5
+
6
+ Implementation-intent gate (#810): exits 0 only when the xBRIEF is in
7
+ xbrief/active/ (or legacy vbrief/active/) AND plan.status == 'running'.
8
+
9
+ Examples:
10
+ deft xbrief:preflight -- xbrief/active/<story>.xbrief.json
11
+ deft xbrief:preflight --vbrief-path xbrief/active/<story>.xbrief.json
12
+ `;
4
13
  const PATH_FLAG_NAMES = ["--vbrief-path", "--brief-path", "--xbrief-path"];
5
14
  function assignPathFlag(parsed, value) {
6
15
  if (value === undefined || value.length === 0) {
@@ -19,15 +28,30 @@ export function parseArgs(argv) {
19
28
  };
20
29
  for (let i = 0; i < argv.length; i += 1) {
21
30
  const arg = argv[i];
31
+ if (arg === "--") {
32
+ continue;
33
+ }
22
34
  if (arg === "--json") {
23
35
  parsed.emitJson = true;
24
36
  continue;
25
37
  }
26
38
  if (arg === "--help" || arg === "-h") {
27
- return parsed;
39
+ return { ...parsed, help: true };
28
40
  }
29
41
  if (PATH_FLAG_NAMES.includes(arg)) {
30
- const next = assignPathFlag(parsed, argv[i + 1]);
42
+ // Task wrappers bake `--vbrief-path {{.CLI_ARGS}}`, so
43
+ // `task xbrief:preflight -- --help` arrives as `--vbrief-path --help`.
44
+ const nextToken = argv[i + 1];
45
+ if (nextToken === "--help" || nextToken === "-h") {
46
+ return { ...parsed, help: true };
47
+ }
48
+ if (nextToken !== undefined && nextToken.startsWith("-")) {
49
+ return {
50
+ ...parsed,
51
+ error: "argument --vbrief-path: expected one argument",
52
+ };
53
+ }
54
+ const next = assignPathFlag(parsed, nextToken);
31
55
  if (next?.error !== undefined)
32
56
  return next;
33
57
  parsed.vbriefPath = next?.vbriefPath ?? null;
@@ -66,6 +90,10 @@ export function parseArgs(argv) {
66
90
  /** Run the gate and return the process exit code (parse errors -> 2). */
67
91
  export function run(argv) {
68
92
  const args = parseArgs(argv);
93
+ if (args.help) {
94
+ process.stdout.write(HELP_TEXT);
95
+ return 0;
96
+ }
69
97
  if (args.error !== undefined) {
70
98
  process.stderr.write(`preflight_implementation: ${args.error}\n`);
71
99
  return 2;
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { resolve } from "node:path";
3
- import { fileURLToPath } from "node:url";
4
3
  import { evaluate } from "@deftai/directive-core";
4
+ import { isDirectEntrypoint } from "./entrypoint.js";
5
5
  /** Parse the verify-encoding CLI args, mirroring the Python argparse surface. */
6
6
  export function parseArgs(argv) {
7
7
  const parsed = {
@@ -77,10 +77,7 @@ export function run(argv) {
77
77
  return result.exitCode;
78
78
  }
79
79
  // Only execute when invoked directly as a binary (not when imported in tests).
80
- // Normalize both sides via fileURLToPath so the guard fires on Windows too,
81
- // where process.argv[1] is a native backslash path and import.meta.url is a
82
- // forward-slash file:// URL -- a raw `file://${process.argv[1]}` never matches.
83
- if (process.argv[1] !== undefined && fileURLToPath(import.meta.url) === process.argv[1]) {
80
+ if (isDirectEntrypoint(import.meta.url)) {
84
81
  process.exit(run(process.argv.slice(2)));
85
82
  }
86
83
  //# sourceMappingURL=verify-encoding.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deftai/directive",
3
- "version": "0.85.0",
3
+ "version": "0.86.0",
4
4
  "description": "Directive CLI — npm install path for the Deft Directive framework.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -32,8 +32,8 @@
32
32
  "provenance": true
33
33
  },
34
34
  "dependencies": {
35
- "@deftai/directive-core": "^0.85.0",
36
- "@deftai/directive-content": "^0.85.0"
35
+ "@deftai/directive-core": "^0.86.0",
36
+ "@deftai/directive-content": "^0.86.0"
37
37
  },
38
38
  "scripts": {
39
39
  "build": "tsc -b"