@deftai/directive 0.66.2 → 0.68.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.
@@ -2,10 +2,9 @@
2
2
  /**
3
3
  * agents:refresh — rewrite AGENTS.md managed section from the canonical template (#768 / #1996).
4
4
  */
5
- import { writeFileSync } from "node:fs";
6
5
  import { resolve } from "node:path";
7
6
  import { fileURLToPath } from "node:url";
8
- import { agentsRefreshPlan } from "@deftai/directive-core/platform";
7
+ import { applyAgentsRefresh } from "@deftai/directive-core/platform";
9
8
  export function parseAgentsRefreshArgs(argv) {
10
9
  let projectRoot = process.cwd();
11
10
  let check = false;
@@ -41,8 +40,13 @@ export function runAgentsRefresh(argv) {
41
40
  process.stderr.write(`agents:refresh: ${args.error}\n`);
42
41
  return 2;
43
42
  }
44
- const plan = agentsRefreshPlan(args.projectRoot);
45
- const state = String(plan.state ?? "unknown");
43
+ // The read->compute->write is serialized behind an advisory lock and written
44
+ // atomically inside applyAgentsRefresh, so concurrent refreshers cannot clobber
45
+ // one another's session= write or observe a partial write (#1329).
46
+ const { state, path, writable } = applyAgentsRefresh(args.projectRoot, {
47
+ check: args.check,
48
+ dryRun: args.dryRun,
49
+ });
46
50
  if (args.check) {
47
51
  if (state === "current")
48
52
  return 0;
@@ -57,17 +61,14 @@ export function runAgentsRefresh(argv) {
57
61
  process.stderr.write(`agents:refresh failed: ${state}\n`);
58
62
  return 2;
59
63
  }
60
- const newContent = plan.new_content;
61
- if (typeof newContent !== "string") {
64
+ if (!writable) {
62
65
  process.stderr.write("agents:refresh failed: plan produced no new_content\n");
63
66
  return 2;
64
67
  }
65
- const path = String(plan.path ?? resolve(args.projectRoot, "AGENTS.md"));
66
68
  if (args.dryRun) {
67
69
  process.stdout.write(`[dry-run] would write ${path} (state=${state})\n`);
68
70
  return 0;
69
71
  }
70
- writeFileSync(path, newContent, "utf8");
71
72
  process.stdout.write(`AGENTS.md updated (state=${state}).\n`);
72
73
  return 0;
73
74
  }
@@ -35,6 +35,7 @@ export const PR_VERB_MAP = {
35
35
  "check-protected-issues": "pr-protected-issues",
36
36
  "check-closing-keywords": "pr-closing-keywords",
37
37
  "wait-mergeable-and-merge": "pr-wait-mergeable",
38
+ watch: "pr-watch",
38
39
  };
39
40
  /** verify:* aliases that map to non-verify-* handler stems. */
40
41
  export const VERIFY_VERB_MAP = {
@@ -10,30 +10,97 @@ 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", "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-hooks-installed", "verify-investigation", "verify-judgment-gates", "verify-no-task-runtime", "validate-links", "validate-strategy-output", "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"];
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"];
14
14
  /** Core-only CLI entrypoints without a packages/cli wrapper. */
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-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"];
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"];
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
  /** Task-style aliases (framework_commands / Taskfile names). */
19
19
  export declare const VERB_ALIASES: Readonly<Record<string, string>>;
20
- /** Pinned ghx version — keep in lockstep with .github/workflows/ci.yml env.GHX_VERSION. */
20
+ /** Pinned ghx version (display only) — keep in lockstep with .github/workflows/ci.yml env.GHX_VERSION. */
21
21
  export declare const GHX_VERSION = "v1.5.1";
22
- export declare const INSTALL_PS1_URL = "https://raw.githubusercontent.com/brunoborges/ghx/v1.5.1/install.ps1";
23
- export declare const INSTALL_SH_URL = "https://raw.githubusercontent.com/brunoborges/ghx/v1.5.1/install.sh";
22
+ /**
23
+ * Immutable commit SHA the GHX_VERSION tag resolved to at vendor time
24
+ * (2026-07-02, via `gh api repos/brunoborges/ghx/git/refs/tags/v1.5.1`).
25
+ * Fetch URLs pin to this SHA rather than the mutable tag name so a future
26
+ * tag force-move on brunoborges/ghx cannot silently swap the fetched bytes
27
+ * out from under the vendored SHA-256 hashes below (#2178).
28
+ */
29
+ export declare const GHX_COMMIT_SHA = "aa4a2786660e27392b0d3e8886f140e0a0261a0c";
30
+ export declare const INSTALL_PS1_URL = "https://raw.githubusercontent.com/brunoborges/ghx/aa4a2786660e27392b0d3e8886f140e0a0261a0c/install.ps1";
31
+ export declare const INSTALL_SH_URL = "https://raw.githubusercontent.com/brunoborges/ghx/aa4a2786660e27392b0d3e8886f140e0a0261a0c/install.sh";
32
+ /**
33
+ * SHA-256 of the installer scripts at GHX_COMMIT_SHA, vendored so the
34
+ * download-verify-execute pipeline below can refuse to run tampered bytes.
35
+ * Matches `.github/workflows/ci.yml` env.GHX_INSTALL_SH_SHA256 /
36
+ * GHX_INSTALL_PS1_SHA256 (#1070 / #1328) — keep both in lockstep (#2178).
37
+ */
38
+ export declare const GHX_INSTALL_SH_SHA256 = "08c768feb6d2bc485079898f7e76c2b07576cbb1188a356acf99dac0fc55d1cb";
39
+ export declare const GHX_INSTALL_PS1_SHA256 = "5f67eab68970ecc55bb0fc1b8399ba6f3ce4b2aadeee39255d628e96d187a5ed";
24
40
  export type SetupGhxHost = "windows" | "darwin" | "linux" | string;
41
+ /** Downloads a URL and resolves to its raw bytes. Injectable so tests never hit the network. */
42
+ export type GhxDownloadFn = (url: string) => Promise<Buffer>;
43
+ /** True when `buf`'s SHA-256 (hex) matches `expectedHex`, case- and whitespace-insensitive. */
44
+ export declare function verifyGhxSha256(buf: Buffer, expectedHex: string): boolean;
45
+ export interface GhxInstallerAsset {
46
+ url: string;
47
+ sha256: string;
48
+ fileExt: "sh" | "ps1";
49
+ }
50
+ export declare function resolveGhxInstallerAsset(host: SetupGhxHost): GhxInstallerAsset;
25
51
  export interface SetupGhxDeps {
26
52
  whichFn?: WhichFn;
27
53
  readConsentLine?: () => string;
28
- runInstall?: (host: SetupGhxHost) => number;
54
+ runInstall?: (host: SetupGhxHost) => number | Promise<number>;
55
+ downloadFn?: GhxDownloadFn;
56
+ runner?: typeof spawnSync;
29
57
  }
30
58
  export declare function ghxPresent(whichFn?: WhichFn): boolean;
31
59
  export declare function detectSetupGhxHost(): SetupGhxHost;
32
60
  export declare function promptSetupGhxConsent(io: DispatchIo, readLine?: () => string): boolean;
33
- export declare function buildSetupGhxInstallCommand(host: SetupGhxHost, whichFn?: WhichFn): string[];
34
- export declare function installSetupGhx(host: SetupGhxHost, whichFn?: WhichFn, runner?: typeof spawnSync): number;
61
+ /**
62
+ * Downloads `asset`, verifies it against its vendored SHA-256, and writes it
63
+ * to a private local temp file. Returns the local path, ready for direct
64
+ * local-file execution (never piped into a shell). Throws -- without
65
+ * writing or executing anything -- on a hash mismatch (#2178). Split out
66
+ * from `fetchAndVerifyGhxInstaller` so tests can exercise the download ->
67
+ * verify -> write pipeline against a synthetic asset/hash without depending
68
+ * on the real vendored constants or the network.
69
+ */
70
+ export declare function fetchAndVerifyGhxInstallerAsset(asset: GhxInstallerAsset, downloadFn?: GhxDownloadFn): Promise<string>;
71
+ /**
72
+ * Downloads the pinned installer for `host`, verifies it against the
73
+ * vendored SHA-256, and writes it to a private local temp file. Returns the
74
+ * local path, ready for direct local-file execution (never piped into a
75
+ * shell). Throws -- without writing or executing anything -- on a hash
76
+ * mismatch (#2178).
77
+ */
78
+ export declare function fetchAndVerifyGhxInstaller(host: SetupGhxHost, downloadFn?: GhxDownloadFn): Promise<string>;
79
+ /**
80
+ * Executes an already-downloaded, hash-verified installer from its local
81
+ * temp path. No live pipe (`curl | bash` / `irm | iex`) and no
82
+ * `-ExecutionPolicy Bypass` -- the file is written by Node, so it never
83
+ * carries a Windows Mark-of-the-Web zone identifier the way a browser or
84
+ * `Invoke-WebRequest` download would; `RemoteSigned` treats it as a local,
85
+ * unsigned-but-trusted script (#2178).
86
+ */
87
+ export declare function executeVerifiedGhxInstaller(host: SetupGhxHost, installerPath: string, whichFn?: WhichFn, runner?: typeof spawnSync): number;
88
+ /**
89
+ * Downloads, hash-verifies, and executes `asset` for `host`. Cleans up the
90
+ * temp file (and its containing directory) regardless of outcome. Split out
91
+ * from `installSetupGhx` so tests can exercise the full download -> verify
92
+ * -> execute -> cleanup pipeline against a synthetic asset without depending
93
+ * on the real vendored constants or the network (#2178).
94
+ */
95
+ export declare function installVerifiedGhxAsset(asset: GhxInstallerAsset, host: SetupGhxHost, whichFn?: WhichFn, runner?: typeof spawnSync, downloadFn?: GhxDownloadFn): Promise<number>;
96
+ /**
97
+ * Downloads, hash-verifies, and executes the ghx installer for `host`.
98
+ * Cleans up the temp file (and its containing directory) regardless of
99
+ * outcome (#2178).
100
+ */
101
+ export declare function installSetupGhx(host: SetupGhxHost, whichFn?: WhichFn, runner?: typeof spawnSync, downloadFn?: GhxDownloadFn): Promise<number>;
35
102
  /** Native `setup:ghx` handler (replaces scripts/setup_ghx.py shell-out, #2022 Phase 1). */
36
- export declare function runSetupGhx(argv: string[], io: DispatchIo, deps?: SetupGhxDeps): number;
103
+ export declare function runSetupGhx(argv: string[], io: DispatchIo, deps?: SetupGhxDeps): Promise<number>;
37
104
  export declare const SETUP_SKILL_REL_PATH = ".deft/core/skills/deft-directive-setup/SKILL.md";
38
105
  export type BootstrapPhaseLabel = "user" | "project" | "spec";
39
106
  export type BootstrapReEntry = "none" | "prompt" | "reconfigure" | "force";