@deftai/directive 0.85.0 → 0.87.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.d.ts +1 -1
- package/dist/dispatch.js +9 -2
- package/dist/entrypoint.d.ts +6 -0
- package/dist/entrypoint.js +19 -0
- package/dist/hook-bin.js +2 -2
- package/dist/hook-dispatch.d.ts +51 -0
- package/dist/hook-dispatch.js +74 -3
- package/dist/review-monitor-register.js +2 -2
- package/dist/triage-queue.d.ts +18 -7
- package/dist/triage-queue.js +201 -56
- package/dist/vbrief-preflight.d.ts +1 -0
- package/dist/vbrief-preflight.js +30 -2
- package/dist/verify-encoding.js +2 -5
- package/dist/verify-source-cli/verify-openclaw-tier1.d.ts +12 -0
- package/dist/verify-source-cli/verify-openclaw-tier1.js +51 -0
- package/package.json +3 -3
package/dist/dispatch.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ 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", "hook-dispatch", "umbrella-current-shape", "changelog-check", "change-init", "commit-lint", "coverage-hotspots", "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", "lifecycle-event", "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-skill-external-fetch-gate", "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-review-monitor", "verify-subagent-alive", "review-monitor-register", "review-monitor-release", "verify-tools", "verify-wip-cap", "verify-orphan-active", "verify-agents-md-budget", "verify-agents-md-advisory", "verify-eval-health-relocation", "verify-eval-triggers-relocation", "eval-health", "eval-run", "eval-report", "eval-triggers"];
|
|
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", "coverage-hotspots", "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", "lifecycle-event", "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-skill-external-fetch-gate", "verify-contract-drift", "verify-cursor-tier1", "verify-openclaw-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-review-monitor", "verify-subagent-alive", "review-monitor-register", "review-monitor-release", "verify-tools", "verify-wip-cap", "verify-orphan-active", "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", "issue-sync-from-xbrief", "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", "rule-map", "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", "product-signal"];
|
|
16
16
|
/** Colon aliases for triage-actions (mirrors cli-router SUBCOMMAND_ROUTES). */
|
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",
|
|
@@ -105,6 +107,7 @@ export const CLI_MODULE_VERBS = [
|
|
|
105
107
|
"verify-skill-external-fetch-gate",
|
|
106
108
|
"verify-contract-drift",
|
|
107
109
|
"verify-cursor-tier1",
|
|
110
|
+
"verify-openclaw-tier1",
|
|
108
111
|
"verify-go-freeze",
|
|
109
112
|
"verify-scm-boundary",
|
|
110
113
|
"verify-session-ritual",
|
|
@@ -250,6 +253,7 @@ export const VERB_ALIASES = {
|
|
|
250
253
|
"verify:skill-external-fetch-gate": "verify-skill-external-fetch-gate",
|
|
251
254
|
"verify:contract-drift": "verify-contract-drift",
|
|
252
255
|
"verify:cursor-tier1": "verify-cursor-tier1",
|
|
256
|
+
"verify:openclaw-tier1": "verify-openclaw-tier1",
|
|
253
257
|
"verify:go-freeze": "verify-go-freeze",
|
|
254
258
|
"verify:bridge-drift": "verify-bridge-drift",
|
|
255
259
|
"verify:scm-boundary": "verify-scm-boundary",
|
|
@@ -309,6 +313,7 @@ const SUBDIR_CLI_STEMS = {
|
|
|
309
313
|
"verify-skill-external-fetch-gate": "verify-source-cli/verify-skill-external-fetch-gate",
|
|
310
314
|
"verify-contract-drift": "verify-source-cli/verify-contract-drift",
|
|
311
315
|
"verify-cursor-tier1": "verify-source-cli/verify-cursor-tier1",
|
|
316
|
+
"verify-openclaw-tier1": "verify-source-cli/verify-openclaw-tier1",
|
|
312
317
|
"verify-scm-boundary": "verify-source-cli/verify-scm-boundary",
|
|
313
318
|
"verify-xbrief-drift": "verify-source-cli/verify-xbrief-drift",
|
|
314
319
|
"verify-go-freeze": "gates-cli/verify-go-freeze",
|
|
@@ -2035,7 +2040,8 @@ function writeWipCap(projectRoot, cap, actor, note) {
|
|
|
2035
2040
|
const { path, data, policyBlock } = loadProjectDefinitionForWrite(projectRoot);
|
|
2036
2041
|
const previous = policyBlock.wipCap;
|
|
2037
2042
|
policyBlock.wipCap = cap;
|
|
2038
|
-
|
|
2043
|
+
assertWriteTargetSafe(projectRoot, path);
|
|
2044
|
+
atomicWriteProjectDefinition(path, data);
|
|
2039
2045
|
const changed = previous !== cap;
|
|
2040
2046
|
const parts = [`actor=${actor}`, `wipCap=${cap}`, `previous=${pyRepr(previous)}`];
|
|
2041
2047
|
if (note)
|
|
@@ -2049,7 +2055,8 @@ function writeSubagentBackend(projectRoot, backendId, actor, note) {
|
|
|
2049
2055
|
const { path, data, policyBlock } = loadProjectDefinitionForWrite(projectRoot);
|
|
2050
2056
|
const previous = policyBlock.swarmSubagentBackend;
|
|
2051
2057
|
policyBlock.swarmSubagentBackend = backendId;
|
|
2052
|
-
|
|
2058
|
+
assertWriteTargetSafe(projectRoot, path);
|
|
2059
|
+
atomicWriteProjectDefinition(path, data);
|
|
2053
2060
|
const changed = previous !== backendId;
|
|
2054
2061
|
const parts = [
|
|
2055
2062
|
`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 {
|
|
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 (
|
|
8
|
+
if (isDirectEntrypoint(import.meta.url)) {
|
|
9
9
|
process.exit(main());
|
|
10
10
|
}
|
|
11
11
|
//# sourceMappingURL=hook-bin.js.map
|
package/dist/hook-dispatch.d.ts
CHANGED
|
@@ -11,13 +11,64 @@ export interface HookDispatchCliSeams {
|
|
|
11
11
|
readonly writeOut?: (text: string) => void;
|
|
12
12
|
readonly writeErr?: (text: string) => void;
|
|
13
13
|
readonly cwd?: () => string;
|
|
14
|
+
/**
|
|
15
|
+
* Sleep between empty-stdin retry polls (#2864). Injected in tests so delayed
|
|
16
|
+
* payloads can be simulated without real wall-clock waits.
|
|
17
|
+
*/
|
|
18
|
+
readonly sleepMs?: (ms: number) => void;
|
|
19
|
+
/**
|
|
20
|
+
* Empty-stdin retry budget in ms (default {@link DEFAULT_STDIN_EMPTY_RETRY_MS}).
|
|
21
|
+
* Set `0` to disable retries (single read). Tests for true-empty paths often
|
|
22
|
+
* set this to 0 for speed; delayed-payload tests set a positive budget.
|
|
23
|
+
*/
|
|
24
|
+
readonly stdinEmptyRetryMs?: number;
|
|
25
|
+
/** Clock for empty-stdin deadline; defaults to `Date.now` (tests inject synthetic clocks). */
|
|
26
|
+
readonly nowMs?: () => number;
|
|
14
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* How long to re-poll stdin after an empty first read before concluding the host
|
|
30
|
+
* sent nothing (#2864). Keeps well under Cursor's deposited `timeout: 5` while
|
|
31
|
+
* covering short non-blocking delivery races (reporter matrix rows K/L).
|
|
32
|
+
*/
|
|
33
|
+
export declare const DEFAULT_STDIN_EMPTY_RETRY_MS = 250;
|
|
34
|
+
/** Interval between empty-stdin re-polls while the retry budget remains. */
|
|
35
|
+
export declare const STDIN_EMPTY_RETRY_INTERVAL_MS = 25;
|
|
36
|
+
/**
|
|
37
|
+
* Read hook stdin with a short empty-read retry budget (#2864).
|
|
38
|
+
*
|
|
39
|
+
* A single `readFileSync(0)` on a non-blocking descriptor can return zero bytes
|
|
40
|
+
* before the host has attached a writer. That is indistinguishable from a true
|
|
41
|
+
* empty payload if we never re-poll. We re-read for up to `emptyRetryMs`; true
|
|
42
|
+
* empty (every poll empty through the budget) still yields empty for the deny
|
|
43
|
+
* path. Delayed payloads that arrive within the budget are accepted.
|
|
44
|
+
*/
|
|
45
|
+
export declare function readStdinHardened(readOnce: () => string, options?: {
|
|
46
|
+
emptyRetryMs?: number;
|
|
47
|
+
sleepMs?: (ms: number) => void;
|
|
48
|
+
nowMs?: () => number;
|
|
49
|
+
}): string;
|
|
15
50
|
export declare function parseArgs(argv: readonly string[]): ParsedArgs;
|
|
16
51
|
export interface ParsedPayload {
|
|
17
52
|
readonly payload: unknown;
|
|
18
53
|
readonly context: HookPayloadContext;
|
|
19
54
|
}
|
|
20
55
|
export declare function parsePayload(raw: string): ParsedPayload;
|
|
56
|
+
/**
|
|
57
|
+
* Provider-neutral hook dispatch CLI entry (`deft hook:dispatch` / `deft-hook`).
|
|
58
|
+
*
|
|
59
|
+
* ## Exit-code contract (#2864)
|
|
60
|
+
*
|
|
61
|
+
* | Exit | Meaning |
|
|
62
|
+
* |------|---------|
|
|
63
|
+
* | `0` | A host decision was rendered to stdout (allow **or** deny). Hosts must parse stdout; exit status does **not** encode the verdict. |
|
|
64
|
+
* | `2` | Argv / configuration error (unsupported host/event, missing flags, unrecognized args). |
|
|
65
|
+
*
|
|
66
|
+
* This process never returns `1` for a rendered verdict. Cursor `failClosed`
|
|
67
|
+
* deposits that report "exit code 1" are therefore process-level failures
|
|
68
|
+
* (crash, signal kill under host timeout, spawn environment), not `run()` deny
|
|
69
|
+
* paths. Empty-stdin denies still exit `0` with a Cursor JSON body carrying
|
|
70
|
+
* `code: "stdin-empty"`.
|
|
71
|
+
*/
|
|
21
72
|
export declare function run(argv: string[], seams?: HookDispatchCliSeams): number;
|
|
22
73
|
export {};
|
|
23
74
|
//# sourceMappingURL=hook-dispatch.d.ts.map
|
package/dist/hook-dispatch.js
CHANGED
|
@@ -3,6 +3,52 @@ import { readFileSync } from "node:fs";
|
|
|
3
3
|
import { resolve } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { decideHook, hookPayloadTopLevelKeys, isHookEvent, isHookHost, normalizeHookProjectRoot, projectRootFromHookPayload, renderHostDecision, } from "@deftai/directive-core/hooks";
|
|
6
|
+
/**
|
|
7
|
+
* How long to re-poll stdin after an empty first read before concluding the host
|
|
8
|
+
* sent nothing (#2864). Keeps well under Cursor's deposited `timeout: 5` while
|
|
9
|
+
* covering short non-blocking delivery races (reporter matrix rows K/L).
|
|
10
|
+
*/
|
|
11
|
+
export const DEFAULT_STDIN_EMPTY_RETRY_MS = 250;
|
|
12
|
+
/** Interval between empty-stdin re-polls while the retry budget remains. */
|
|
13
|
+
export const STDIN_EMPTY_RETRY_INTERVAL_MS = 25;
|
|
14
|
+
function defaultSleepMs(ms) {
|
|
15
|
+
if (ms <= 0)
|
|
16
|
+
return;
|
|
17
|
+
try {
|
|
18
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
const end = Date.now() + ms;
|
|
22
|
+
while (Date.now() < end) {
|
|
23
|
+
/* spin — last-resort fallback when Atomics.wait is unavailable */
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Read hook stdin with a short empty-read retry budget (#2864).
|
|
29
|
+
*
|
|
30
|
+
* A single `readFileSync(0)` on a non-blocking descriptor can return zero bytes
|
|
31
|
+
* before the host has attached a writer. That is indistinguishable from a true
|
|
32
|
+
* empty payload if we never re-poll. We re-read for up to `emptyRetryMs`; true
|
|
33
|
+
* empty (every poll empty through the budget) still yields empty for the deny
|
|
34
|
+
* path. Delayed payloads that arrive within the budget are accepted.
|
|
35
|
+
*/
|
|
36
|
+
export function readStdinHardened(readOnce, options = {}) {
|
|
37
|
+
const emptyRetryMs = options.emptyRetryMs ?? DEFAULT_STDIN_EMPTY_RETRY_MS;
|
|
38
|
+
const sleepMs = options.sleepMs ?? defaultSleepMs;
|
|
39
|
+
const nowMs = options.nowMs ?? (() => Date.now());
|
|
40
|
+
let raw = readOnce();
|
|
41
|
+
if (raw.trim().length > 0 || emptyRetryMs <= 0)
|
|
42
|
+
return raw;
|
|
43
|
+
const deadline = nowMs() + emptyRetryMs;
|
|
44
|
+
while (nowMs() < deadline) {
|
|
45
|
+
sleepMs(STDIN_EMPTY_RETRY_INTERVAL_MS);
|
|
46
|
+
raw = readOnce();
|
|
47
|
+
if (raw.trim().length > 0)
|
|
48
|
+
return raw;
|
|
49
|
+
}
|
|
50
|
+
return raw;
|
|
51
|
+
}
|
|
6
52
|
function takeValue(argv, index, flag) {
|
|
7
53
|
const token = argv[index];
|
|
8
54
|
const prefix = `${flag}=`;
|
|
@@ -111,6 +157,22 @@ export function parsePayload(raw) {
|
|
|
111
157
|
return { payload: {}, context: { parseFailed: true } };
|
|
112
158
|
}
|
|
113
159
|
}
|
|
160
|
+
/**
|
|
161
|
+
* Provider-neutral hook dispatch CLI entry (`deft hook:dispatch` / `deft-hook`).
|
|
162
|
+
*
|
|
163
|
+
* ## Exit-code contract (#2864)
|
|
164
|
+
*
|
|
165
|
+
* | Exit | Meaning |
|
|
166
|
+
* |------|---------|
|
|
167
|
+
* | `0` | A host decision was rendered to stdout (allow **or** deny). Hosts must parse stdout; exit status does **not** encode the verdict. |
|
|
168
|
+
* | `2` | Argv / configuration error (unsupported host/event, missing flags, unrecognized args). |
|
|
169
|
+
*
|
|
170
|
+
* This process never returns `1` for a rendered verdict. Cursor `failClosed`
|
|
171
|
+
* deposits that report "exit code 1" are therefore process-level failures
|
|
172
|
+
* (crash, signal kill under host timeout, spawn environment), not `run()` deny
|
|
173
|
+
* paths. Empty-stdin denies still exit `0` with a Cursor JSON body carrying
|
|
174
|
+
* `code: "stdin-empty"`.
|
|
175
|
+
*/
|
|
114
176
|
export function run(argv, seams = {}) {
|
|
115
177
|
const args = parseArgs(argv);
|
|
116
178
|
const writeOut = seams.writeOut ?? ((text) => process.stdout.write(text));
|
|
@@ -119,9 +181,14 @@ export function run(argv, seams = {}) {
|
|
|
119
181
|
writeErr(`${args.error ?? "invalid hook-dispatch arguments"}\n`);
|
|
120
182
|
return 2;
|
|
121
183
|
}
|
|
122
|
-
const
|
|
184
|
+
const readOnce = seams.readStdin ?? (() => readFileSync(0, "utf8"));
|
|
185
|
+
const rawStdin = readStdinHardened(readOnce, {
|
|
186
|
+
emptyRetryMs: seams.stdinEmptyRetryMs,
|
|
187
|
+
sleepMs: seams.sleepMs,
|
|
188
|
+
nowMs: seams.nowMs,
|
|
189
|
+
});
|
|
123
190
|
const cwd = (seams.cwd ?? process.cwd)();
|
|
124
|
-
const { payload, context: payloadContext } = parsePayload(
|
|
191
|
+
const { payload, context: payloadContext } = parsePayload(rawStdin);
|
|
125
192
|
const projectRoot = normalizeHookProjectRoot(args.projectRoot ? resolve(args.projectRoot) : projectRootFromHookPayload(payload, cwd));
|
|
126
193
|
const decision = decideHook({
|
|
127
194
|
host: args.host,
|
|
@@ -133,12 +200,16 @@ export function run(argv, seams = {}) {
|
|
|
133
200
|
const rendered = renderHostDecision(args.host, decision);
|
|
134
201
|
if (rendered.length > 0)
|
|
135
202
|
writeOut(`${rendered}\n`);
|
|
136
|
-
if (decision.code === "invalid-input"
|
|
203
|
+
if ((decision.code === "invalid-input" || decision.code === "stdin-empty") &&
|
|
204
|
+
args.host === "cursor") {
|
|
137
205
|
// Keys are already embedded in decision.message; stderr helps operators tailing logs.
|
|
138
206
|
const keys = hookPayloadTopLevelKeys(payload);
|
|
139
207
|
if (keys.length > 0) {
|
|
140
208
|
writeErr(`Directive hook diagnostic: payload top-level keys: ${keys.join(", ")}\n`);
|
|
141
209
|
}
|
|
210
|
+
else if (decision.code === "stdin-empty") {
|
|
211
|
+
writeErr("Directive hook diagnostic: stdin was empty after empty-read retry budget\n");
|
|
212
|
+
}
|
|
142
213
|
}
|
|
143
214
|
if (decision.code === "session-start-degraded")
|
|
144
215
|
writeErr(`${decision.message}\n`);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
|
-
import { REGISTER_HELP, registerReviewMonitor, } from "@deftai/directive-core/review-monitor";
|
|
5
|
-
const PRIMITIVES =
|
|
4
|
+
import { PLATFORM_PRIMITIVE_SET, REGISTER_HELP, registerReviewMonitor, } from "@deftai/directive-core/review-monitor";
|
|
5
|
+
const PRIMITIVES = PLATFORM_PRIMITIVE_SET;
|
|
6
6
|
export function parseRegisterArgs(argv) {
|
|
7
7
|
const acc = {
|
|
8
8
|
pr: null,
|
package/dist/triage-queue.d.ts
CHANGED
|
@@ -1,23 +1,34 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { type LiveOpenIssuesReader } from "@deftai/directive-core/dist/triage/queue/index.js";
|
|
3
|
-
|
|
3
|
+
export type ShowFormat = "default" | "operator";
|
|
4
|
+
interface CommonArgs {
|
|
4
5
|
projectRoot: string;
|
|
5
6
|
repo: string | null;
|
|
7
|
+
cacheRoot: string | null;
|
|
8
|
+
auditLog: string | null;
|
|
9
|
+
error?: string;
|
|
10
|
+
}
|
|
11
|
+
interface QueueArgs extends CommonArgs {
|
|
12
|
+
cmd: "queue";
|
|
6
13
|
limit: number;
|
|
7
14
|
includeBlocked: boolean;
|
|
8
15
|
reconcile: boolean;
|
|
9
|
-
cacheRoot: string | null;
|
|
10
|
-
auditLog: string | null;
|
|
11
16
|
slicesLog: string | null;
|
|
12
|
-
error?: string;
|
|
13
17
|
}
|
|
14
|
-
|
|
15
|
-
|
|
18
|
+
interface ShowArgs extends CommonArgs {
|
|
19
|
+
cmd: "show";
|
|
20
|
+
number: number | null;
|
|
21
|
+
format: ShowFormat;
|
|
22
|
+
}
|
|
23
|
+
/** Parse triage-queue CLI args (queue subcommand — default). */
|
|
24
|
+
export declare function parseArgs(argv: string[]): QueueArgs;
|
|
25
|
+
/** Parse show subcommand args. */
|
|
26
|
+
export declare function parseShowArgs(argv: string[]): ShowArgs;
|
|
16
27
|
/** Optional injection seam for `run` (tests supply a stub live-open reader). */
|
|
17
28
|
export interface RunOptions {
|
|
18
29
|
readonly liveOpenReader?: LiveOpenIssuesReader;
|
|
19
30
|
}
|
|
20
|
-
/** Run triage:queue and return process exit code. */
|
|
31
|
+
/** Run triage:queue or triage:show and return process exit code. */
|
|
21
32
|
export declare function run(argv: string[], options?: RunOptions): number;
|
|
22
33
|
export {};
|
|
23
34
|
//# sourceMappingURL=triage-queue.d.ts.map
|
package/dist/triage-queue.js
CHANGED
|
@@ -1,20 +1,88 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* triage:queue / triage:show CLI (#1128 / #2890).
|
|
4
|
+
*
|
|
5
|
+
* Subcommands:
|
|
6
|
+
* queue — ranked queue (default when first token is not show/audit)
|
|
7
|
+
* show — per-issue detail; --format=operator for Phase 3 brief backbone
|
|
8
|
+
*
|
|
9
|
+
* audit remains routed here by Taskfile; not reimplemented in this story.
|
|
10
|
+
*/
|
|
2
11
|
import { resolve } from "node:path";
|
|
3
12
|
import { fileURLToPath } from "node:url";
|
|
4
13
|
import { ensureTriageCacheHydrated } from "@deftai/directive-core/dist/cache/empty-populate.js";
|
|
14
|
+
import { findByIssue } from "@deftai/directive-core/dist/triage/actions/candidates-log.js";
|
|
5
15
|
import { resolveTriageCachePath } from "@deftai/directive-core/dist/triage/cache-path.js";
|
|
6
|
-
import { activeReferencedIssueNumbers, buildQueue, collectOrphanIssueNumbers, DEFAULT_QUEUE_LIMIT, loadCachedIssues, loadSliceRecords, readAuditEntries, reconcileLiveOpenState, renderQueue, resolveRankingLabels, resolveRepo, } from "@deftai/directive-core/dist/triage/queue/index.js";
|
|
16
|
+
import { activeReferencedIssueNumbers, buildQueue, collectOrphanIssueNumbers, DEFAULT_QUEUE_LIMIT, loadCachedIssueDetail, loadCachedIssues, loadSliceRecords, readAuditEntries, reconcileLiveOpenState, renderOperatorBrief, renderQueue, renderShow, resolveRankingLabels, resolveRepo, } from "@deftai/directive-core/dist/triage/queue/index.js";
|
|
7
17
|
import { resolveScopeIgnores } from "@deftai/directive-core/dist/triage/scope-drift/index.js";
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const parsed = {
|
|
18
|
+
function baseArgs() {
|
|
19
|
+
return {
|
|
11
20
|
projectRoot: process.env.DEFT_PROJECT_ROOT ?? ".",
|
|
12
21
|
repo: process.env.DEFT_TRIAGE_REPO ?? null,
|
|
22
|
+
cacheRoot: null,
|
|
23
|
+
auditLog: null,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function parseCommonFlag(arg, argv, i, parsed) {
|
|
27
|
+
if (arg === "--project-root") {
|
|
28
|
+
const value = argv[i + 1];
|
|
29
|
+
if (value === undefined) {
|
|
30
|
+
return { consumed: 0, error: "argument --project-root: expected one argument" };
|
|
31
|
+
}
|
|
32
|
+
parsed.projectRoot = value;
|
|
33
|
+
return { consumed: 1 };
|
|
34
|
+
}
|
|
35
|
+
if (arg?.startsWith("--project-root=")) {
|
|
36
|
+
parsed.projectRoot = arg.slice("--project-root=".length);
|
|
37
|
+
return { consumed: 0 };
|
|
38
|
+
}
|
|
39
|
+
if (arg === "--repo") {
|
|
40
|
+
const value = argv[i + 1];
|
|
41
|
+
if (value === undefined) {
|
|
42
|
+
return { consumed: 0, error: "argument --repo: expected one argument" };
|
|
43
|
+
}
|
|
44
|
+
parsed.repo = value;
|
|
45
|
+
return { consumed: 1 };
|
|
46
|
+
}
|
|
47
|
+
if (arg?.startsWith("--repo=")) {
|
|
48
|
+
parsed.repo = arg.slice("--repo=".length);
|
|
49
|
+
return { consumed: 0 };
|
|
50
|
+
}
|
|
51
|
+
if (arg === "--cache-root") {
|
|
52
|
+
const value = argv[i + 1];
|
|
53
|
+
if (value === undefined) {
|
|
54
|
+
return { consumed: 0, error: "argument --cache-root: expected one argument" };
|
|
55
|
+
}
|
|
56
|
+
parsed.cacheRoot = value;
|
|
57
|
+
return { consumed: 1 };
|
|
58
|
+
}
|
|
59
|
+
if (arg?.startsWith("--cache-root=")) {
|
|
60
|
+
parsed.cacheRoot = arg.slice("--cache-root=".length);
|
|
61
|
+
return { consumed: 0 };
|
|
62
|
+
}
|
|
63
|
+
if (arg === "--audit-log") {
|
|
64
|
+
const value = argv[i + 1];
|
|
65
|
+
if (value === undefined) {
|
|
66
|
+
return { consumed: 0, error: "argument --audit-log: expected one argument" };
|
|
67
|
+
}
|
|
68
|
+
parsed.auditLog = value;
|
|
69
|
+
return { consumed: 1 };
|
|
70
|
+
}
|
|
71
|
+
if (arg?.startsWith("--audit-log=")) {
|
|
72
|
+
parsed.auditLog = arg.slice("--audit-log=".length);
|
|
73
|
+
return { consumed: 0 };
|
|
74
|
+
}
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
/** Parse triage-queue CLI args (queue subcommand — default). */
|
|
78
|
+
export function parseArgs(argv) {
|
|
79
|
+
const common = baseArgs();
|
|
80
|
+
const parsed = {
|
|
81
|
+
...common,
|
|
82
|
+
cmd: "queue",
|
|
13
83
|
limit: DEFAULT_QUEUE_LIMIT,
|
|
14
84
|
includeBlocked: false,
|
|
15
85
|
reconcile: true,
|
|
16
|
-
cacheRoot: null,
|
|
17
|
-
auditLog: null,
|
|
18
86
|
slicesLog: null,
|
|
19
87
|
};
|
|
20
88
|
for (let i = 0; i < argv.length; i += 1) {
|
|
@@ -30,30 +98,12 @@ export function parseArgs(argv) {
|
|
|
30
98
|
parsed.reconcile = false;
|
|
31
99
|
continue;
|
|
32
100
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
if (
|
|
36
|
-
return { ...parsed, error:
|
|
101
|
+
const commonHit = parseCommonFlag(arg, argv, i, parsed);
|
|
102
|
+
if (commonHit !== null) {
|
|
103
|
+
if (commonHit.error !== undefined) {
|
|
104
|
+
return { ...parsed, error: commonHit.error };
|
|
37
105
|
}
|
|
38
|
-
|
|
39
|
-
i += 1;
|
|
40
|
-
continue;
|
|
41
|
-
}
|
|
42
|
-
if (arg?.startsWith("--project-root=")) {
|
|
43
|
-
parsed.projectRoot = arg.slice("--project-root=".length);
|
|
44
|
-
continue;
|
|
45
|
-
}
|
|
46
|
-
if (arg === "--repo") {
|
|
47
|
-
const value = argv[i + 1];
|
|
48
|
-
if (value === undefined) {
|
|
49
|
-
return { ...parsed, error: "argument --repo: expected one argument" };
|
|
50
|
-
}
|
|
51
|
-
parsed.repo = value;
|
|
52
|
-
i += 1;
|
|
53
|
-
continue;
|
|
54
|
-
}
|
|
55
|
-
if (arg?.startsWith("--repo=")) {
|
|
56
|
-
parsed.repo = arg.slice("--repo=".length);
|
|
106
|
+
i += commonHit.consumed;
|
|
57
107
|
continue;
|
|
58
108
|
}
|
|
59
109
|
if (arg === "--limit") {
|
|
@@ -78,54 +128,90 @@ export function parseArgs(argv) {
|
|
|
78
128
|
parsed.limit = parsedLimit;
|
|
79
129
|
continue;
|
|
80
130
|
}
|
|
81
|
-
if (arg === "--
|
|
131
|
+
if (arg === "--slices-log") {
|
|
82
132
|
const value = argv[i + 1];
|
|
83
133
|
if (value === undefined) {
|
|
84
|
-
return { ...parsed, error: "argument --
|
|
134
|
+
return { ...parsed, error: "argument --slices-log: expected one argument" };
|
|
85
135
|
}
|
|
86
|
-
parsed.
|
|
136
|
+
parsed.slicesLog = value;
|
|
87
137
|
i += 1;
|
|
88
138
|
continue;
|
|
89
139
|
}
|
|
90
|
-
if (arg?.startsWith("--
|
|
91
|
-
parsed.
|
|
140
|
+
if (arg?.startsWith("--slices-log=")) {
|
|
141
|
+
parsed.slicesLog = arg.slice("--slices-log=".length);
|
|
92
142
|
continue;
|
|
93
143
|
}
|
|
94
|
-
if (arg
|
|
95
|
-
|
|
96
|
-
if (value === undefined) {
|
|
97
|
-
return { ...parsed, error: "argument --audit-log: expected one argument" };
|
|
98
|
-
}
|
|
99
|
-
parsed.auditLog = value;
|
|
100
|
-
i += 1;
|
|
101
|
-
continue;
|
|
144
|
+
if (arg?.startsWith("-")) {
|
|
145
|
+
return { ...parsed, error: `unrecognized argument: ${arg}` };
|
|
102
146
|
}
|
|
103
|
-
|
|
104
|
-
|
|
147
|
+
}
|
|
148
|
+
return parsed;
|
|
149
|
+
}
|
|
150
|
+
/** Parse show subcommand args. */
|
|
151
|
+
export function parseShowArgs(argv) {
|
|
152
|
+
const common = baseArgs();
|
|
153
|
+
const parsed = {
|
|
154
|
+
...common,
|
|
155
|
+
cmd: "show",
|
|
156
|
+
number: null,
|
|
157
|
+
format: "default",
|
|
158
|
+
};
|
|
159
|
+
// Skip leading "show" token if present
|
|
160
|
+
let start = 0;
|
|
161
|
+
if (argv[0] === "show") {
|
|
162
|
+
start = 1;
|
|
163
|
+
}
|
|
164
|
+
for (let i = start; i < argv.length; i += 1) {
|
|
165
|
+
const arg = argv[i];
|
|
166
|
+
const commonHit = parseCommonFlag(arg, argv, i, parsed);
|
|
167
|
+
if (commonHit !== null) {
|
|
168
|
+
if (commonHit.error !== undefined) {
|
|
169
|
+
return { ...parsed, error: commonHit.error };
|
|
170
|
+
}
|
|
171
|
+
i += commonHit.consumed;
|
|
105
172
|
continue;
|
|
106
173
|
}
|
|
107
|
-
if (arg === "--
|
|
174
|
+
if (arg === "--format") {
|
|
108
175
|
const value = argv[i + 1];
|
|
109
176
|
if (value === undefined) {
|
|
110
|
-
return { ...parsed, error: "argument --
|
|
177
|
+
return { ...parsed, error: "argument --format: expected one argument" };
|
|
111
178
|
}
|
|
112
|
-
|
|
179
|
+
if (value !== "default" && value !== "operator" && value !== "plain" && value !== "text") {
|
|
180
|
+
return {
|
|
181
|
+
...parsed,
|
|
182
|
+
error: `argument --format: invalid choice: '${value}' (choose from default, operator)`,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
parsed.format = value === "operator" ? "operator" : "default";
|
|
113
186
|
i += 1;
|
|
114
187
|
continue;
|
|
115
188
|
}
|
|
116
|
-
if (arg?.startsWith("--
|
|
117
|
-
|
|
189
|
+
if (arg?.startsWith("--format=")) {
|
|
190
|
+
const value = arg.slice("--format=".length);
|
|
191
|
+
if (value !== "default" && value !== "operator" && value !== "plain" && value !== "text") {
|
|
192
|
+
return {
|
|
193
|
+
...parsed,
|
|
194
|
+
error: `argument --format: invalid choice: '${value}' (choose from default, operator)`,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
parsed.format = value === "operator" ? "operator" : "default";
|
|
118
198
|
continue;
|
|
119
199
|
}
|
|
120
200
|
if (arg?.startsWith("-")) {
|
|
121
201
|
return { ...parsed, error: `unrecognized argument: ${arg}` };
|
|
122
202
|
}
|
|
203
|
+
// positional issue number — strict positive int (reject "42abc" / "42.5")
|
|
204
|
+
if (!/^[1-9]\d*$/u.test(arg ?? "")) {
|
|
205
|
+
return { ...parsed, error: `argument number: invalid int value: '${arg}'` };
|
|
206
|
+
}
|
|
207
|
+
parsed.number = Number(arg);
|
|
208
|
+
}
|
|
209
|
+
if (parsed.number === null && parsed.error === undefined) {
|
|
210
|
+
return { ...parsed, error: "triage:show: issue number is required (e.g. triage:show -- 42)" };
|
|
123
211
|
}
|
|
124
212
|
return parsed;
|
|
125
213
|
}
|
|
126
|
-
|
|
127
|
-
export function run(argv, options = {}) {
|
|
128
|
-
const args = parseArgs(argv);
|
|
214
|
+
function runQueue(args, options = {}) {
|
|
129
215
|
if (args.error !== undefined) {
|
|
130
216
|
process.stderr.write(`triage_queue: ${args.error}\n`);
|
|
131
217
|
return 2;
|
|
@@ -138,10 +224,6 @@ export function run(argv, options = {}) {
|
|
|
138
224
|
}
|
|
139
225
|
ensureTriageCacheHydrated(projectRoot, { repo });
|
|
140
226
|
const cachedForQueue = loadCachedIssues(repo, { projectRoot });
|
|
141
|
-
// Reconcile the cached candidate set against live open/closed state so a
|
|
142
|
-
// just-closed/merged issue never lingers in the queue as [untriaged] until
|
|
143
|
-
// the cache refreshes (#2238). Fails open: a read error passes candidates
|
|
144
|
-
// through unchanged rather than emptying the queue.
|
|
145
227
|
const issuesForQueue = args.reconcile
|
|
146
228
|
? reconcileLiveOpenState(cachedForQueue, repo, options.liveOpenReader)
|
|
147
229
|
: cachedForQueue;
|
|
@@ -177,6 +259,69 @@ export function run(argv, options = {}) {
|
|
|
177
259
|
})}\n`);
|
|
178
260
|
return 0;
|
|
179
261
|
}
|
|
262
|
+
function runShow(args) {
|
|
263
|
+
if (args.error !== undefined) {
|
|
264
|
+
process.stderr.write(`triage:show: ${args.error}\n`);
|
|
265
|
+
return 2;
|
|
266
|
+
}
|
|
267
|
+
const projectRoot = resolve(args.projectRoot);
|
|
268
|
+
const repo = resolveRepo(args.repo, projectRoot);
|
|
269
|
+
if (repo === null) {
|
|
270
|
+
process.stderr.write("triage:show: --repo OWNER/NAME (or $DEFT_TRIAGE_REPO) is required.\n");
|
|
271
|
+
return 2;
|
|
272
|
+
}
|
|
273
|
+
const number = args.number;
|
|
274
|
+
if (number === null) {
|
|
275
|
+
process.stderr.write("triage:show: issue number is required\n");
|
|
276
|
+
return 2;
|
|
277
|
+
}
|
|
278
|
+
const cacheRoot = args.cacheRoot !== null && args.cacheRoot.length > 0 ? resolve(args.cacheRoot) : null;
|
|
279
|
+
ensureTriageCacheHydrated(projectRoot, {
|
|
280
|
+
repo,
|
|
281
|
+
...(cacheRoot !== null ? { cacheRoot } : {}),
|
|
282
|
+
});
|
|
283
|
+
const issue = loadCachedIssueDetail(repo, number, {
|
|
284
|
+
projectRoot,
|
|
285
|
+
cacheRoot,
|
|
286
|
+
});
|
|
287
|
+
const logPath = args.auditLog ?? resolveTriageCachePath(projectRoot, "candidates.jsonl");
|
|
288
|
+
const history = findByIssue(number, repo, logPath)
|
|
289
|
+
.slice()
|
|
290
|
+
.sort((a, b) => String(a.timestamp ?? "").localeCompare(String(b.timestamp ?? "")));
|
|
291
|
+
const latest = history.length > 0 ? (history[history.length - 1] ?? null) : null;
|
|
292
|
+
const activeRefs = activeReferencedIssueNumbers(projectRoot);
|
|
293
|
+
const inActive = activeRefs.has(number);
|
|
294
|
+
if (args.format === "operator") {
|
|
295
|
+
process.stdout.write(`${renderOperatorBrief({
|
|
296
|
+
issue,
|
|
297
|
+
repo,
|
|
298
|
+
number,
|
|
299
|
+
latestDecision: latest,
|
|
300
|
+
inActiveXbrief: inActive,
|
|
301
|
+
})}\n`);
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
process.stdout.write(`${renderShow({
|
|
305
|
+
issue,
|
|
306
|
+
repo,
|
|
307
|
+
number,
|
|
308
|
+
latestDecision: latest,
|
|
309
|
+
history,
|
|
310
|
+
inActiveXbrief: inActive,
|
|
311
|
+
})}\n`);
|
|
312
|
+
}
|
|
313
|
+
return issue !== null ? 0 : 1;
|
|
314
|
+
}
|
|
315
|
+
/** Run triage:queue or triage:show and return process exit code. */
|
|
316
|
+
export function run(argv, options = {}) {
|
|
317
|
+
const first = argv[0];
|
|
318
|
+
if (first === "show") {
|
|
319
|
+
return runShow(parseShowArgs(argv));
|
|
320
|
+
}
|
|
321
|
+
// Other subcommands (e.g. audit) keep prior queue-module routing until
|
|
322
|
+
// separately ported; only `show` is specialized here (#2890).
|
|
323
|
+
return runQueue(parseArgs(argv), options);
|
|
324
|
+
}
|
|
180
325
|
if (process.argv[1] !== undefined && fileURLToPath(import.meta.url) === process.argv[1]) {
|
|
181
326
|
process.exit(run(process.argv.slice(2)));
|
|
182
327
|
}
|
package/dist/vbrief-preflight.js
CHANGED
|
@@ -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
|
-
|
|
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;
|
package/dist/verify-encoding.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
interface ParsedArgs {
|
|
3
|
+
projectRoot: string;
|
|
4
|
+
quiet: boolean;
|
|
5
|
+
error?: string;
|
|
6
|
+
}
|
|
7
|
+
/** Parse verify-openclaw-tier1 CLI args (#2875). */
|
|
8
|
+
export declare function parseArgs(argv: string[]): ParsedArgs;
|
|
9
|
+
/** Run the gate and return the process exit code. */
|
|
10
|
+
export declare function run(argv: string[]): number;
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=verify-openclaw-tier1.d.ts.map
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { evaluateOpenclawTier1 } from "@deftai/directive-core/verify-source";
|
|
5
|
+
/** Parse verify-openclaw-tier1 CLI args (#2875). */
|
|
6
|
+
export function parseArgs(argv) {
|
|
7
|
+
const parsed = { projectRoot: ".", quiet: false };
|
|
8
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
9
|
+
const arg = argv[i];
|
|
10
|
+
if (arg === "--quiet") {
|
|
11
|
+
parsed.quiet = true;
|
|
12
|
+
}
|
|
13
|
+
else if (arg === "--project-root") {
|
|
14
|
+
const value = argv[i + 1];
|
|
15
|
+
if (value === undefined) {
|
|
16
|
+
return { ...parsed, error: "argument --project-root: expected one argument" };
|
|
17
|
+
}
|
|
18
|
+
parsed.projectRoot = value;
|
|
19
|
+
i += 1;
|
|
20
|
+
}
|
|
21
|
+
else if (arg?.startsWith("--project-root=")) {
|
|
22
|
+
parsed.projectRoot = arg.slice("--project-root=".length);
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
return { ...parsed, error: `unrecognized argument: ${arg}` };
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return parsed;
|
|
29
|
+
}
|
|
30
|
+
/** Run the gate and return the process exit code. */
|
|
31
|
+
export function run(argv) {
|
|
32
|
+
const args = parseArgs(argv);
|
|
33
|
+
if (args.error !== undefined) {
|
|
34
|
+
process.stderr.write(`verify_openclaw_tier1: ${args.error}\n`);
|
|
35
|
+
return 2;
|
|
36
|
+
}
|
|
37
|
+
const result = evaluateOpenclawTier1(resolve(args.projectRoot), { quiet: args.quiet });
|
|
38
|
+
if (result.code === 0) {
|
|
39
|
+
if (!args.quiet && result.message.length > 0) {
|
|
40
|
+
process.stdout.write(`${result.message}\n`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
process.stderr.write(`${result.message}\n`);
|
|
45
|
+
}
|
|
46
|
+
return result.code;
|
|
47
|
+
}
|
|
48
|
+
if (process.argv[1] !== undefined && fileURLToPath(import.meta.url) === process.argv[1]) {
|
|
49
|
+
process.exit(run(process.argv.slice(2)));
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=verify-openclaw-tier1.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deftai/directive",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.87.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.
|
|
36
|
-
"@deftai/directive-content": "^0.
|
|
35
|
+
"@deftai/directive-core": "^0.87.0",
|
|
36
|
+
"@deftai/directive-content": "^0.87.0"
|
|
37
37
|
},
|
|
38
38
|
"scripts": {
|
|
39
39
|
"build": "tsc -b"
|