@deftai/directive 0.86.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 +3 -0
- 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/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
|
@@ -107,6 +107,7 @@ export const CLI_MODULE_VERBS = [
|
|
|
107
107
|
"verify-skill-external-fetch-gate",
|
|
108
108
|
"verify-contract-drift",
|
|
109
109
|
"verify-cursor-tier1",
|
|
110
|
+
"verify-openclaw-tier1",
|
|
110
111
|
"verify-go-freeze",
|
|
111
112
|
"verify-scm-boundary",
|
|
112
113
|
"verify-session-ritual",
|
|
@@ -252,6 +253,7 @@ export const VERB_ALIASES = {
|
|
|
252
253
|
"verify:skill-external-fetch-gate": "verify-skill-external-fetch-gate",
|
|
253
254
|
"verify:contract-drift": "verify-contract-drift",
|
|
254
255
|
"verify:cursor-tier1": "verify-cursor-tier1",
|
|
256
|
+
"verify:openclaw-tier1": "verify-openclaw-tier1",
|
|
255
257
|
"verify:go-freeze": "verify-go-freeze",
|
|
256
258
|
"verify:bridge-drift": "verify-bridge-drift",
|
|
257
259
|
"verify:scm-boundary": "verify-scm-boundary",
|
|
@@ -311,6 +313,7 @@ const SUBDIR_CLI_STEMS = {
|
|
|
311
313
|
"verify-skill-external-fetch-gate": "verify-source-cli/verify-skill-external-fetch-gate",
|
|
312
314
|
"verify-contract-drift": "verify-source-cli/verify-contract-drift",
|
|
313
315
|
"verify-cursor-tier1": "verify-source-cli/verify-cursor-tier1",
|
|
316
|
+
"verify-openclaw-tier1": "verify-source-cli/verify-openclaw-tier1",
|
|
314
317
|
"verify-scm-boundary": "verify-source-cli/verify-scm-boundary",
|
|
315
318
|
"verify-xbrief-drift": "verify-source-cli/verify-xbrief-drift",
|
|
316
319
|
"verify-go-freeze": "gates-cli/verify-go-freeze",
|
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
|
}
|
|
@@ -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"
|