@deftai/directive 0.67.0 → 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,9 +10,9 @@ 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-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"];
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). */
package/dist/dispatch.js CHANGED
@@ -51,6 +51,7 @@ export const CLI_MODULE_VERBS = [
51
51
  "pr-monitor",
52
52
  "pr-protected-issues",
53
53
  "pr-wait-mergeable",
54
+ "pr-watch",
54
55
  "preflight-cache",
55
56
  "preflight-gh",
56
57
  "probe-session",
@@ -86,6 +87,7 @@ export const CLI_MODULE_VERBS = [
86
87
  "vbrief-validation",
87
88
  "verify-branch",
88
89
  "verify-encoding",
90
+ "verify-forward-coverage",
89
91
  "verify-hooks-installed",
90
92
  "verify-investigation",
91
93
  "verify-judgment-gates",
@@ -120,6 +122,7 @@ export const CORE_MODULE_VERBS = [
120
122
  "reconcile-issues",
121
123
  "swarm-launch",
122
124
  "swarm-complete-cohort",
125
+ "swarm-finalize-cohort",
123
126
  "swarm-readiness",
124
127
  "swarm-routing-verify",
125
128
  "swarm-routing-set",
@@ -163,6 +166,7 @@ const TRIAGE_ACTION_COLON_ALIASES = Object.fromEntries(Object.keys(TRIAGE_ACTION
163
166
  /** Task-style aliases (framework_commands / Taskfile names). */
164
167
  export const VERB_ALIASES = {
165
168
  "verify:encoding": "verify-encoding",
169
+ "verify:forward-coverage": "verify-forward-coverage",
166
170
  "verify:branch": "verify-branch",
167
171
  "verify:vbrief-conformance": "vbrief-validate",
168
172
  "verify:wip-cap": "verify-wip-cap",
@@ -2160,6 +2164,10 @@ async function loadCoreModuleHandler(verb, io) {
2160
2164
  const { completeCohortMain } = await import("@deftai/directive-core/dist/swarm/complete-cohort-cli.js");
2161
2165
  return completeCohortMain;
2162
2166
  }
2167
+ case "swarm-finalize-cohort": {
2168
+ const { finalizeCohortMain } = await import("@deftai/directive-core/dist/swarm/finalize-cohort-cli.js");
2169
+ return finalizeCohortMain;
2170
+ }
2163
2171
  case "swarm-readiness": {
2164
2172
  const { readinessMain } = await import("@deftai/directive-core/dist/swarm/readiness-cli.js");
2165
2173
  return readinessMain;
@@ -1,11 +1,41 @@
1
1
  #!/usr/bin/env node
2
- export interface ParsedInstallUpgradeArgs {
3
- projectRoot: string;
4
- frameworkRoot: string;
5
- migrate: boolean;
6
- force: boolean;
7
- error?: string;
2
+ import type { DispatchIo } from "./dispatch.js";
3
+ /**
4
+ * #2064: `deft install-upgrade` is now a thin redirect onto the SAME code path
5
+ * as `directive update` (`runRefreshDeposit`). The two verbs previously had
6
+ * overlapping-but-divergent semantics: `directive update` file-swaps the
7
+ * vendored `.deft/core` payload, rewrites the install manifest (#2056), and
8
+ * regenerates the `.deft-version` marker (#2055), whereas the old
9
+ * `install-upgrade` only wrote the marker/manifest and refreshed AGENTS.md
10
+ * WITHOUT swapping the payload -- so on a stale deposit it reported a confident
11
+ * false no-op ("Project already at X. Nothing to do.") that steered operators
12
+ * away from the command that actually works. Consolidating to one path removes
13
+ * that hazard and gives consumers a single upgrade mental model:
14
+ * npm i -g @deftai/directive@latest -> deft update -> deft migrate -> deft doctor
15
+ *
16
+ * The legacy `.deft/VERSION` cleanup that only `install-upgrade` used to perform
17
+ * is folded into the shared `runRefreshDeposit` path (see
18
+ * `migrateLegacyInstallManifest` in init-deposit/refresh.ts) so no manifest
19
+ * behavior is dropped. Layout migration (the old `--migrate` flag) is the
20
+ * separate `deft migrate` step in the canonical flow above.
21
+ */
22
+ /** One-line notice emitted on the redirect so operators learn the canonical verb. */
23
+ export declare const REDIRECT_NOTICE: string;
24
+ export interface InstallUpgradeDeps {
25
+ /** Injectable seam so tests can drive the shared update path with fixtures. */
26
+ readonly runUpdate?: (argv: readonly string[], io: DispatchIo) => Promise<number>;
8
27
  }
9
- export declare function parseArgs(argv: readonly string[]): ParsedInstallUpgradeArgs;
10
- export declare function run(argv: readonly string[]): number;
28
+ /**
29
+ * Translate the historical `install-upgrade` flag surface onto the
30
+ * `directive update` argv. `--project-root <p>` maps to `--repo-root <p>`;
31
+ * `--framework-root` is dropped (update resolves its own content root) and the
32
+ * legacy `--migrate` / `--force` flags are dropped (layout migration is now the
33
+ * separate `deft migrate` step). Any other argv passes through unchanged.
34
+ */
35
+ export declare function translateArgs(argv: readonly string[]): string[];
36
+ /**
37
+ * Redirect handler: emit the one-line notice, then delegate to the identical
38
+ * code path `directive update` uses so deposit state + stdout are identical.
39
+ */
40
+ export declare function run(argv: readonly string[], io?: DispatchIo, deps?: InstallUpgradeDeps): Promise<number>;
11
41
  //# sourceMappingURL=install-upgrade.d.ts.map
@@ -1,84 +1,86 @@
1
1
  #!/usr/bin/env node
2
- import { resolve } from "node:path";
3
2
  import { fileURLToPath } from "node:url";
4
- import { runInstallUpgrade } from "@deftai/directive-core/install-upgrade";
5
- export function parseArgs(argv) {
6
- let projectRoot = ".";
7
- let frameworkRoot = resolve(import.meta.dirname, "..", "..", "..");
8
- let migrate = false;
9
- let force = false;
3
+ import { runUpdate } from "./init-cli/update.js";
4
+ /**
5
+ * #2064: `deft install-upgrade` is now a thin redirect onto the SAME code path
6
+ * as `directive update` (`runRefreshDeposit`). The two verbs previously had
7
+ * overlapping-but-divergent semantics: `directive update` file-swaps the
8
+ * vendored `.deft/core` payload, rewrites the install manifest (#2056), and
9
+ * regenerates the `.deft-version` marker (#2055), whereas the old
10
+ * `install-upgrade` only wrote the marker/manifest and refreshed AGENTS.md
11
+ * WITHOUT swapping the payload -- so on a stale deposit it reported a confident
12
+ * false no-op ("Project already at X. Nothing to do.") that steered operators
13
+ * away from the command that actually works. Consolidating to one path removes
14
+ * that hazard and gives consumers a single upgrade mental model:
15
+ * npm i -g @deftai/directive@latest -> deft update -> deft migrate -> deft doctor
16
+ *
17
+ * The legacy `.deft/VERSION` cleanup that only `install-upgrade` used to perform
18
+ * is folded into the shared `runRefreshDeposit` path (see
19
+ * `migrateLegacyInstallManifest` in init-deposit/refresh.ts) so no manifest
20
+ * behavior is dropped. Layout migration (the old `--migrate` flag) is the
21
+ * separate `deft migrate` step in the canonical flow above.
22
+ */
23
+ /** One-line notice emitted on the redirect so operators learn the canonical verb. */
24
+ export const REDIRECT_NOTICE = "install-upgrade: delegating to `directive update` -- the single canonical " +
25
+ "upgrade verb (run `deft update` directly; use `deft migrate` for layout " +
26
+ "migration). Refs #2064.\n";
27
+ /**
28
+ * Translate the historical `install-upgrade` flag surface onto the
29
+ * `directive update` argv. `--project-root <p>` maps to `--repo-root <p>`;
30
+ * `--framework-root` is dropped (update resolves its own content root) and the
31
+ * legacy `--migrate` / `--force` flags are dropped (layout migration is now the
32
+ * separate `deft migrate` step). Any other argv passes through unchanged.
33
+ */
34
+ export function translateArgs(argv) {
35
+ const out = [];
10
36
  for (let i = 0; i < argv.length; i += 1) {
11
37
  const arg = argv[i] ?? "";
12
- if (arg === "--migrate") {
13
- migrate = true;
14
- }
15
- else if (arg === "--force") {
16
- force = true;
17
- }
18
- else if (arg === "--project-root") {
38
+ if (arg === "--project-root") {
39
+ out.push("--repo-root");
19
40
  const value = argv[i + 1];
20
- if (value === undefined) {
21
- return {
22
- projectRoot,
23
- frameworkRoot,
24
- migrate,
25
- force,
26
- error: "argument --project-root: expected one argument",
27
- };
41
+ if (value !== undefined) {
42
+ out.push(value);
43
+ i += 1;
28
44
  }
29
- projectRoot = value;
30
- i += 1;
31
45
  }
32
46
  else if (arg.startsWith("--project-root=")) {
33
- projectRoot = arg.slice("--project-root=".length);
47
+ out.push(`--repo-root=${arg.slice("--project-root=".length)}`);
34
48
  }
35
49
  else if (arg === "--framework-root") {
36
- const value = argv[i + 1];
37
- if (value === undefined) {
38
- return {
39
- projectRoot,
40
- frameworkRoot,
41
- migrate,
42
- force,
43
- error: "argument --framework-root: expected one argument",
44
- };
45
- }
46
- frameworkRoot = value;
47
- i += 1;
50
+ i += 1; // drop the flag and its value
48
51
  }
49
- else if (arg.startsWith("--framework-root=")) {
50
- frameworkRoot = arg.slice("--framework-root=".length);
52
+ else if (arg.startsWith("--framework-root=") || arg === "--migrate" || arg === "--force") {
53
+ // Intentionally dropped: update resolves its own content root, and layout
54
+ // migration is the separate `deft migrate` step in the canonical flow.
51
55
  }
52
56
  else {
53
- return { projectRoot, frameworkRoot, migrate, force, error: `unrecognized argument: ${arg}` };
57
+ out.push(arg);
54
58
  }
55
59
  }
56
- if (process.env.DEFT_ROOT && process.env.DEFT_ROOT.length > 0) {
57
- frameworkRoot = process.env.DEFT_ROOT;
58
- }
59
- return { projectRoot, frameworkRoot, migrate, force };
60
+ return out;
60
61
  }
61
- export function run(argv) {
62
- const args = parseArgs(argv);
63
- if (args.error !== undefined) {
64
- process.stderr.write(`install-upgrade: ${args.error}\n`);
65
- return 2;
66
- }
67
- return runInstallUpgrade({
68
- projectRoot: args.projectRoot,
69
- frameworkRoot: args.frameworkRoot,
70
- migrate: args.migrate,
71
- force: args.force,
72
- }, {
62
+ function defaultIo() {
63
+ return {
73
64
  writeOut: (text) => {
74
65
  process.stdout.write(text);
75
66
  },
76
67
  writeErr: (text) => {
77
68
  process.stderr.write(text);
78
69
  },
79
- });
70
+ };
71
+ }
72
+ /**
73
+ * Redirect handler: emit the one-line notice, then delegate to the identical
74
+ * code path `directive update` uses so deposit state + stdout are identical.
75
+ */
76
+ export async function run(argv, io = defaultIo(), deps = {}) {
77
+ const update = deps.runUpdate ?? runUpdate;
78
+ io.writeErr(REDIRECT_NOTICE);
79
+ return update(translateArgs(argv), io);
80
80
  }
81
81
  if (process.argv[1] !== undefined && fileURLToPath(import.meta.url) === process.argv[1]) {
82
- process.exit(run(process.argv.slice(2)));
82
+ run(process.argv.slice(2)).then((code) => {
83
+ process.exit(code);
84
+ });
83
85
  }
84
86
  //# sourceMappingURL=install-upgrade.js.map
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export declare function run(argv: string[]): number;
3
+ //# sourceMappingURL=pr-watch.d.ts.map
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ import { fileURLToPath } from "node:url";
3
+ import { cmdPrWatch } from "@deftai/directive-core/dist/pr-watch/main.js";
4
+ export function run(argv) {
5
+ return cmdPrWatch(argv);
6
+ }
7
+ if (process.argv[1] !== undefined && fileURLToPath(import.meta.url) === process.argv[1]) {
8
+ process.exit(run(process.argv.slice(2)));
9
+ }
10
+ //# sourceMappingURL=pr-watch.js.map
@@ -1,3 +1,7 @@
1
1
  #!/usr/bin/env node
2
- export declare function run(argv?: readonly string[]): number;
2
+ import { type CommandRunner } from "@deftai/directive-core/verify-env";
3
+ export interface ToolchainCheckRunOptions {
4
+ readonly runner?: CommandRunner;
5
+ }
6
+ export declare function run(argv?: readonly string[], options?: ToolchainCheckRunOptions): number;
3
7
  //# sourceMappingURL=toolchain-check.d.ts.map
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import { fileURLToPath } from "node:url";
3
3
  import { runToolchainCheck } from "@deftai/directive-core/verify-env";
4
- export function run(argv = process.argv.slice(2)) {
4
+ export function run(argv = process.argv.slice(2), options = {}) {
5
5
  const consumer = argv.includes("--consumer");
6
- const result = runToolchainCheck(undefined, { consumer });
6
+ const result = runToolchainCheck(options.runner, { consumer });
7
7
  for (const line of result.lines) {
8
8
  process.stdout.write(`${line}\n`);
9
9
  }
@@ -1,9 +1,11 @@
1
1
  #!/usr/bin/env node
2
+ import { type LiveOpenIssuesReader } from "@deftai/directive-core/dist/triage/queue/index.js";
2
3
  interface ParsedArgs {
3
4
  projectRoot: string;
4
5
  repo: string | null;
5
6
  limit: number;
6
7
  includeBlocked: boolean;
8
+ reconcile: boolean;
7
9
  cacheRoot: string | null;
8
10
  auditLog: string | null;
9
11
  slicesLog: string | null;
@@ -11,7 +13,11 @@ interface ParsedArgs {
11
13
  }
12
14
  /** Parse triage-queue CLI args for the queue subcommand. */
13
15
  export declare function parseArgs(argv: string[]): ParsedArgs;
16
+ /** Optional injection seam for `run` (tests supply a stub live-open reader). */
17
+ export interface RunOptions {
18
+ readonly liveOpenReader?: LiveOpenIssuesReader;
19
+ }
14
20
  /** Run triage:queue and return process exit code. */
15
- export declare function run(argv: string[]): number;
21
+ export declare function run(argv: string[], options?: RunOptions): number;
16
22
  export {};
17
23
  //# sourceMappingURL=triage-queue.d.ts.map
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env node
2
- import { dirname, resolve } from "node:path";
2
+ import { resolve } from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
- import { activeReferencedIssueNumbers, buildQueue, collectOrphanIssueNumbers, DEFAULT_QUEUE_LIMIT, loadCachedIssues, loadSliceRecords, readAuditEntries, renderQueue, resolveRankingLabels, resolveRepo, } from "@deftai/directive-core/dist/triage/queue/index.js";
4
+ import { resolveEvalPath } from "@deftai/directive-core/dist/layout/resolve.js";
5
+ import { activeReferencedIssueNumbers, buildQueue, collectOrphanIssueNumbers, DEFAULT_QUEUE_LIMIT, loadCachedIssues, loadSliceRecords, readAuditEntries, reconcileLiveOpenState, renderQueue, resolveRankingLabels, resolveRepo, } from "@deftai/directive-core/dist/triage/queue/index.js";
5
6
  import { resolveScopeIgnores } from "@deftai/directive-core/dist/triage/scope-drift/index.js";
6
7
  /** Parse triage-queue CLI args for the queue subcommand. */
7
8
  export function parseArgs(argv) {
@@ -10,6 +11,7 @@ export function parseArgs(argv) {
10
11
  repo: process.env.DEFT_TRIAGE_REPO ?? null,
11
12
  limit: DEFAULT_QUEUE_LIMIT,
12
13
  includeBlocked: false,
14
+ reconcile: true,
13
15
  cacheRoot: null,
14
16
  auditLog: null,
15
17
  slicesLog: null,
@@ -23,6 +25,10 @@ export function parseArgs(argv) {
23
25
  parsed.includeBlocked = true;
24
26
  continue;
25
27
  }
28
+ if (arg === "--no-reconcile") {
29
+ parsed.reconcile = false;
30
+ continue;
31
+ }
26
32
  if (arg === "--project-root") {
27
33
  const value = argv[i + 1];
28
34
  if (value === undefined) {
@@ -116,15 +122,8 @@ export function parseArgs(argv) {
116
122
  }
117
123
  return parsed;
118
124
  }
119
- function resolveFrameworkRoot() {
120
- const fromEnv = process.env.DEFT_ROOT?.trim() ?? "";
121
- if (fromEnv.length > 0) {
122
- return resolve(fromEnv);
123
- }
124
- return resolve(dirname(fileURLToPath(import.meta.url)), "..", "..");
125
- }
126
125
  /** Run triage:queue and return process exit code. */
127
- export function run(argv) {
126
+ export function run(argv, options = {}) {
128
127
  const args = parseArgs(argv);
129
128
  if (args.error !== undefined) {
130
129
  process.stderr.write(`triage_queue: ${args.error}\n`);
@@ -136,18 +135,23 @@ export function run(argv) {
136
135
  process.stderr.write("triage:queue: --repo OWNER/NAME (or $DEFT_TRIAGE_REPO) is required.\n");
137
136
  return 2;
138
137
  }
139
- const issuesForQueue = loadCachedIssues(repo, { projectRoot });
138
+ const cachedForQueue = loadCachedIssues(repo, { projectRoot });
139
+ // Reconcile the cached candidate set against live open/closed state so a
140
+ // just-closed/merged issue never lingers in the queue as [untriaged] until
141
+ // the cache refreshes (#2238). Fails open: a read error passes candidates
142
+ // through unchanged rather than emptying the queue.
143
+ const issuesForQueue = args.reconcile
144
+ ? reconcileLiveOpenState(cachedForQueue, repo, options.liveOpenReader)
145
+ : cachedForQueue;
140
146
  const issuesWithClosed = loadCachedIssues(repo, { projectRoot, includeClosed: true });
141
147
  const issuesByNumber = new Map(issuesWithClosed.map((row) => [row.number, row]));
142
148
  const auditEntries = readAuditEntries(repo, {
143
- frameworkRoot: resolveFrameworkRoot(),
144
- auditLogPath: args.auditLog,
149
+ auditLogPath: args.auditLog ?? resolveEvalPath(projectRoot, "candidates.jsonl"),
145
150
  });
146
151
  const rankingLabels = resolveRankingLabels(projectRoot);
147
152
  const activeRefs = activeReferencedIssueNumbers(projectRoot);
148
153
  const sliceRecords = loadSliceRecords({
149
- frameworkRoot: resolveFrameworkRoot(),
150
- slicesLogPath: args.slicesLog,
154
+ slicesLogPath: args.slicesLog ?? resolveEvalPath(projectRoot, "slices.jsonl"),
151
155
  });
152
156
  const orphanNumbers = collectOrphanIssueNumbers(sliceRecords, issuesByNumber);
153
157
  const limit = args.limit === 0 ? null : Math.max(0, args.limit);
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env node
2
+ import { type ForwardCoverageMode } from "@deftai/directive-core";
3
+ interface ParsedArgs {
4
+ mode: ForwardCoverageMode;
5
+ projectRoot: string;
6
+ allowList: string | null;
7
+ quiet: boolean;
8
+ error?: string;
9
+ }
10
+ /** Parse the verify-forward-coverage CLI args, mirroring the verify-encoding surface. */
11
+ export declare function parseArgs(argv: string[]): ParsedArgs;
12
+ /** Run the gate and return the process exit code (argv parse error -> 2). */
13
+ export declare function run(argv: string[]): number;
14
+ export {};
15
+ //# sourceMappingURL=verify-forward-coverage.d.ts.map
@@ -0,0 +1,77 @@
1
+ #!/usr/bin/env node
2
+ import { resolve } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { evaluateForwardCoverage } from "@deftai/directive-core";
5
+ /** Parse the verify-forward-coverage CLI args, mirroring the verify-encoding surface. */
6
+ export function parseArgs(argv) {
7
+ const parsed = {
8
+ mode: "head",
9
+ projectRoot: ".",
10
+ allowList: null,
11
+ quiet: false,
12
+ };
13
+ for (let i = 0; i < argv.length; i += 1) {
14
+ const arg = argv[i];
15
+ if (arg === "--staged") {
16
+ parsed.mode = "staged";
17
+ }
18
+ else if (arg === "--head") {
19
+ parsed.mode = "head";
20
+ }
21
+ else if (arg === "--quiet") {
22
+ parsed.quiet = true;
23
+ }
24
+ 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" };
28
+ }
29
+ parsed.projectRoot = value;
30
+ i += 1;
31
+ }
32
+ else if (arg?.startsWith("--project-root=")) {
33
+ parsed.projectRoot = arg.slice("--project-root=".length);
34
+ }
35
+ 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" };
39
+ }
40
+ parsed.allowList = value;
41
+ i += 1;
42
+ }
43
+ else if (arg?.startsWith("--allow-list=")) {
44
+ parsed.allowList = arg.slice("--allow-list=".length);
45
+ }
46
+ else {
47
+ return { ...parsed, error: `unrecognized argument: ${arg}` };
48
+ }
49
+ }
50
+ return parsed;
51
+ }
52
+ /** Run the gate and return the process exit code (argv parse error -> 2). */
53
+ export function run(argv) {
54
+ const args = parseArgs(argv);
55
+ if (args.error !== undefined) {
56
+ process.stderr.write(`verify_forward_coverage: ${args.error}\n`);
57
+ return 2;
58
+ }
59
+ const projectRoot = resolve(args.projectRoot);
60
+ const allowListPath = args.allowList !== null ? resolve(args.allowList) : null;
61
+ const result = evaluateForwardCoverage(projectRoot, { mode: args.mode, allowListPath });
62
+ if (result.exitCode === 0) {
63
+ if (!args.quiet) {
64
+ process.stdout.write(`${result.message}\n`);
65
+ }
66
+ }
67
+ else {
68
+ process.stderr.write(`${result.message}\n`);
69
+ }
70
+ return result.exitCode;
71
+ }
72
+ // Only execute when invoked directly as a binary (not when imported in tests).
73
+ // Normalize both sides via fileURLToPath so the guard fires on Windows too.
74
+ if (process.argv[1] !== undefined && fileURLToPath(import.meta.url) === process.argv[1]) {
75
+ process.exit(run(process.argv.slice(2)));
76
+ }
77
+ //# sourceMappingURL=verify-forward-coverage.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deftai/directive",
3
- "version": "0.67.0",
3
+ "version": "0.68.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.67.0",
35
- "@deftai/directive-content": "^0.67.0"
34
+ "@deftai/directive-core": "^0.68.0",
35
+ "@deftai/directive-content": "^0.68.0"
36
36
  },
37
37
  "scripts": {
38
38
  "build": "tsc -b"