@deftai/directive 0.84.0 → 0.86.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli-router/route-argv.js +1 -0
- package/dist/dispatch.d.ts +2 -2
- package/dist/dispatch.js +18 -2
- package/dist/doctor.d.ts +11 -2
- package/dist/doctor.js +42 -11
- package/dist/entrypoint.d.ts +6 -0
- package/dist/entrypoint.js +19 -0
- package/dist/hook-bin.d.ts +3 -0
- package/dist/hook-bin.js +11 -0
- package/dist/init-cli/help.d.ts +9 -0
- package/dist/init-cli/help.js +42 -0
- package/dist/init-cli/init.js +5 -0
- package/dist/init-cli/migrate.js +5 -0
- package/dist/init-cli/update.js +5 -0
- package/dist/policy.d.ts +1 -1
- package/dist/policy.js +18 -4
- package/dist/render-cli/rule-map-cli.d.ts +2 -0
- package/dist/render-cli/rule-map-cli.js +5 -0
- package/dist/review-monitor-register.d.ts +2 -0
- package/dist/review-monitor-register.js +33 -16
- package/dist/review-monitor-release.d.ts +14 -0
- package/dist/review-monitor-release.js +123 -0
- 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-subagent-alive.d.ts +21 -0
- package/dist/verify-subagent-alive.js +199 -0
- package/package.json +4 -3
|
@@ -73,6 +73,7 @@ export const SUBCOMMAND_ROUTES = {
|
|
|
73
73
|
"policy:enforce-branches": ["policy", "enforce-branches"],
|
|
74
74
|
"policy:allow-direct-commits": ["policy", "allow-direct-commits"],
|
|
75
75
|
"policy:enable-value-feedback": ["policy", "enable-value-feedback"],
|
|
76
|
+
"policy:clear-value-feedback": ["policy", "clear-value-feedback"],
|
|
76
77
|
"product-signal:status": ["product-signal", "status"],
|
|
77
78
|
"product-signal:enable": ["product-signal", "enable"],
|
|
78
79
|
"product-signal:consent": ["product-signal", "consent"],
|
package/dist/dispatch.d.ts
CHANGED
|
@@ -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", "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", "review-monitor-register", "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-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
|
-
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", "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"];
|
|
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). */
|
|
17
17
|
export declare const TRIAGE_ACTION_ALIAS_SUBCOMMANDS: Readonly<Record<string, string>>;
|
|
18
18
|
/** Colon aliases for policy subcommands (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",
|
|
@@ -114,7 +116,9 @@ export const CLI_MODULE_VERBS = [
|
|
|
114
116
|
"rule-ownership-lint",
|
|
115
117
|
"verify-story-ready",
|
|
116
118
|
"verify-review-monitor",
|
|
119
|
+
"verify-subagent-alive",
|
|
117
120
|
"review-monitor-register",
|
|
121
|
+
"review-monitor-release",
|
|
118
122
|
"verify-tools",
|
|
119
123
|
"verify-wip-cap",
|
|
120
124
|
"verify-orphan-active",
|
|
@@ -151,6 +155,7 @@ export const CORE_MODULE_VERBS = [
|
|
|
151
155
|
"export-spec",
|
|
152
156
|
"project-render",
|
|
153
157
|
"roadmap-render",
|
|
158
|
+
"rule-map",
|
|
154
159
|
"spec-render",
|
|
155
160
|
"spec-validate",
|
|
156
161
|
"code-structure-validate",
|
|
@@ -188,6 +193,7 @@ export const POLICY_ACTION_ALIAS_SUBCOMMANDS = {
|
|
|
188
193
|
"policy:enforce-branches": "enforce-branches",
|
|
189
194
|
"policy:allow-direct-commits": "allow-direct-commits",
|
|
190
195
|
"policy:enable-value-feedback": "enable-value-feedback",
|
|
196
|
+
"policy:clear-value-feedback": "clear-value-feedback",
|
|
191
197
|
};
|
|
192
198
|
const POLICY_ACTION_COLON_ALIASES = Object.fromEntries(Object.keys(POLICY_ACTION_ALIAS_SUBCOMMANDS).map((alias) => [alias, "policy"]));
|
|
193
199
|
/** Colon aliases for plan-sequence subcommands (#2402). */
|
|
@@ -229,7 +235,10 @@ export const VERB_ALIASES = {
|
|
|
229
235
|
"vbrief:activate": "vbrief-activate",
|
|
230
236
|
"verify:story-ready": "verify-story-ready",
|
|
231
237
|
"verify:review-monitor": "verify-review-monitor",
|
|
238
|
+
"verify:subagent-alive": "verify-subagent-alive",
|
|
239
|
+
"agent:monitor": "subagent-monitor",
|
|
232
240
|
"review-monitor:register": "review-monitor-register",
|
|
241
|
+
"review-monitor:release": "review-monitor-release",
|
|
233
242
|
"verify:tools": "verify-tools",
|
|
234
243
|
"verify:investigation": "verify-investigation",
|
|
235
244
|
"verify:judgment-gates": "verify-judgment-gates",
|
|
@@ -279,6 +288,7 @@ export const VERB_ALIASES = {
|
|
|
279
288
|
"spec:render": "spec-render",
|
|
280
289
|
"prd:render": "prd-render",
|
|
281
290
|
"project:render": "project-render",
|
|
291
|
+
"docs:rule-map": "rule-map",
|
|
282
292
|
"project:export-spec": "export-spec",
|
|
283
293
|
"pr:watch": "pr-watch",
|
|
284
294
|
doctor: "doctor",
|
|
@@ -2027,7 +2037,8 @@ function writeWipCap(projectRoot, cap, actor, note) {
|
|
|
2027
2037
|
const { path, data, policyBlock } = loadProjectDefinitionForWrite(projectRoot);
|
|
2028
2038
|
const previous = policyBlock.wipCap;
|
|
2029
2039
|
policyBlock.wipCap = cap;
|
|
2030
|
-
|
|
2040
|
+
assertWriteTargetSafe(projectRoot, path);
|
|
2041
|
+
atomicWriteProjectDefinition(path, data);
|
|
2031
2042
|
const changed = previous !== cap;
|
|
2032
2043
|
const parts = [`actor=${actor}`, `wipCap=${cap}`, `previous=${pyRepr(previous)}`];
|
|
2033
2044
|
if (note)
|
|
@@ -2041,7 +2052,8 @@ function writeSubagentBackend(projectRoot, backendId, actor, note) {
|
|
|
2041
2052
|
const { path, data, policyBlock } = loadProjectDefinitionForWrite(projectRoot);
|
|
2042
2053
|
const previous = policyBlock.swarmSubagentBackend;
|
|
2043
2054
|
policyBlock.swarmSubagentBackend = backendId;
|
|
2044
|
-
|
|
2055
|
+
assertWriteTargetSafe(projectRoot, path);
|
|
2056
|
+
atomicWriteProjectDefinition(path, data);
|
|
2045
2057
|
const changed = previous !== backendId;
|
|
2046
2058
|
const parts = [
|
|
2047
2059
|
`actor=${actor}`,
|
|
@@ -2298,6 +2310,10 @@ async function loadCoreModuleHandler(verb, io) {
|
|
|
2298
2310
|
const { runProjectRenderCli } = await import("./render-cli/project-render-cli.js");
|
|
2299
2311
|
return (argv) => runProjectRenderCli(argv);
|
|
2300
2312
|
}
|
|
2313
|
+
case "rule-map": {
|
|
2314
|
+
const { runRuleMapCli } = await import("./render-cli/rule-map-cli.js");
|
|
2315
|
+
return (argv) => runRuleMapCli(argv);
|
|
2316
|
+
}
|
|
2301
2317
|
case "export-spec": {
|
|
2302
2318
|
const { runExportSpecCli } = await import("./render-cli/export-spec-cli.js");
|
|
2303
2319
|
return (argv) => runExportSpecCli(argv);
|
package/dist/doctor.d.ts
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
export interface DepositFileSetHygieneResult {
|
|
3
|
+
readonly absent: readonly string[];
|
|
4
|
+
readonly contentRoot: string | null;
|
|
5
|
+
readonly skipped: boolean;
|
|
6
|
+
}
|
|
7
|
+
export interface EvaluateDepositFileSetOptions {
|
|
8
|
+
readonly contentRoot?: string;
|
|
9
|
+
}
|
|
10
|
+
/** Compare `.deft/core/` against `@deftai/directive-content` (#2804). */
|
|
11
|
+
export declare function evaluateDepositFileSetHygiene(projectRoot: string, options?: EvaluateDepositFileSetOptions): DepositFileSetHygieneResult;
|
|
12
|
+
export declare function renderDepositFileSetHygieneLine(projectRoot: string, result?: DepositFileSetHygieneResult): string;
|
|
4
13
|
export declare function run(argv: string[]): number;
|
|
5
14
|
//# sourceMappingURL=doctor.d.ts.map
|
package/dist/doctor.js
CHANGED
|
@@ -2,20 +2,45 @@
|
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { resolveContentPackageRoot } from "@deftai/directive-core/dist/content-root.js";
|
|
5
6
|
import { parseDoctorFlags } from "@deftai/directive-core/dist/doctor/flags.js";
|
|
6
7
|
import { cmdDoctor } from "@deftai/directive-core/dist/doctor/main.js";
|
|
8
|
+
import { findPackageAbsentDepositPathsSync } from "@deftai/directive-core/dist/init-deposit/hygiene.js";
|
|
7
9
|
import { renderPrecutoverLine } from "@deftai/directive-core/dist/vbrief-validate/precutover.js";
|
|
8
10
|
import { renderStaleHeaderLine, renderXbriefMigrationLine, } from "@deftai/directive-core/xbrief-migrate";
|
|
9
|
-
/**
|
|
10
|
-
export function
|
|
11
|
-
const
|
|
12
|
-
if (!existsSync(
|
|
13
|
-
return
|
|
11
|
+
/** Compare `.deft/core/` against `@deftai/directive-content` (#2804). */
|
|
12
|
+
export function evaluateDepositFileSetHygiene(projectRoot, options = {}) {
|
|
13
|
+
const deftDir = join(projectRoot, ".deft", "core");
|
|
14
|
+
if (!existsSync(deftDir)) {
|
|
15
|
+
return { absent: [], contentRoot: null, skipped: true };
|
|
14
16
|
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
const contentRoot = options.contentRoot ?? resolveContentPackageRoot(projectRoot);
|
|
18
|
+
if (contentRoot === null || !existsSync(contentRoot)) {
|
|
19
|
+
return { absent: [], contentRoot: null, skipped: true };
|
|
20
|
+
}
|
|
21
|
+
return {
|
|
22
|
+
absent: findPackageAbsentDepositPathsSync(deftDir, contentRoot),
|
|
23
|
+
contentRoot,
|
|
24
|
+
skipped: false,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export function renderDepositFileSetHygieneLine(projectRoot, result = evaluateDepositFileSetHygiene(projectRoot)) {
|
|
28
|
+
const deftDir = join(projectRoot, ".deft", "core");
|
|
29
|
+
if (!existsSync(deftDir)) {
|
|
30
|
+
return "Deposit hygiene: none -- no .deft/core deposit.";
|
|
31
|
+
}
|
|
32
|
+
if (result.skipped) {
|
|
33
|
+
return ("Deposit hygiene: skip -- @deftai/directive-content is not installed " +
|
|
34
|
+
"(cannot compare deposit file-set).");
|
|
35
|
+
}
|
|
36
|
+
if (result.absent.length === 0) {
|
|
37
|
+
return "Deposit hygiene: none -- .deft/core file-set matches @deftai/directive-content.";
|
|
38
|
+
}
|
|
39
|
+
const sample = result.absent.slice(0, 5).join(", ");
|
|
40
|
+
const suffix = result.absent.length > 5 ? ` (+${result.absent.length - 5} more)` : "";
|
|
41
|
+
return (`Deposit hygiene: fail -- ${result.absent.length} package-absent file(s) in .deft/core ` +
|
|
42
|
+
`(not shipped by @deftai/directive-content). Examples: ${sample}${suffix}. ` +
|
|
43
|
+
"Run `directive update` to auto-prune these stale deposit files (#2804).");
|
|
19
44
|
}
|
|
20
45
|
export function run(argv) {
|
|
21
46
|
// #2022: surface pre-cutover (pre-v0.20 document model) migration state alongside the
|
|
@@ -23,14 +48,20 @@ export function run(argv) {
|
|
|
23
48
|
// --json (so the machine-readable report stays valid), on --help, and when unknown flags
|
|
24
49
|
// are present (so an invalid invocation still mirrors the core error path exactly).
|
|
25
50
|
const flags = parseDoctorFlags(argv);
|
|
51
|
+
let depositHygieneExit = 0;
|
|
26
52
|
if (!flags.json && !flags.help && flags.unknown.length === 0) {
|
|
27
53
|
const projectRoot = flags.projectRoot ?? process.cwd();
|
|
54
|
+
const depositResult = evaluateDepositFileSetHygiene(projectRoot);
|
|
28
55
|
process.stdout.write(`${renderPrecutoverLine(projectRoot)}\n`);
|
|
29
56
|
process.stdout.write(`${renderXbriefMigrationLine(projectRoot)}\n`);
|
|
30
57
|
process.stdout.write(`${renderStaleHeaderLine(projectRoot)}\n`);
|
|
31
|
-
process.stdout.write(`${
|
|
58
|
+
process.stdout.write(`${renderDepositFileSetHygieneLine(projectRoot, depositResult)}\n`);
|
|
59
|
+
if (flags.full && !depositResult.skipped && depositResult.absent.length > 0) {
|
|
60
|
+
depositHygieneExit = 1;
|
|
61
|
+
}
|
|
32
62
|
}
|
|
33
|
-
|
|
63
|
+
const doctorExit = cmdDoctor(argv);
|
|
64
|
+
return Math.max(depositHygieneExit, doctorExit);
|
|
34
65
|
}
|
|
35
66
|
if (process.argv[1] !== undefined && fileURLToPath(import.meta.url) === process.argv[1]) {
|
|
36
67
|
process.exit(run(process.argv.slice(2)));
|
|
@@ -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
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { isDirectEntrypoint } from "./entrypoint.js";
|
|
3
|
+
import { run } from "./hook-dispatch.js";
|
|
4
|
+
// Keep PreToolUse off the general CLI router's cold path.
|
|
5
|
+
export function main(argv = process.argv.slice(2)) {
|
|
6
|
+
return run(argv);
|
|
7
|
+
}
|
|
8
|
+
if (isDirectEntrypoint(import.meta.url)) {
|
|
9
|
+
process.exit(main());
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=hook-bin.js.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { DispatchIo } from "../dispatch.js";
|
|
2
|
+
/** Flags that request usage text without executing a mutating install path (#2828). */
|
|
3
|
+
export declare const INIT_CLI_HELP_FLAGS: readonly ["--help", "-h", "/help", "/h"];
|
|
4
|
+
/** True when argv includes a help flag anywhere in the token list. */
|
|
5
|
+
export declare function argvWantsHelp(argv: readonly string[]): boolean;
|
|
6
|
+
export declare function printUpdateHelp(io: DispatchIo): void;
|
|
7
|
+
export declare function printInitHelp(io: DispatchIo): void;
|
|
8
|
+
export declare function printMigrateHelp(io: DispatchIo): void;
|
|
9
|
+
//# sourceMappingURL=help.d.ts.map
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/** Flags that request usage text without executing a mutating install path (#2828). */
|
|
2
|
+
export const INIT_CLI_HELP_FLAGS = ["--help", "-h", "/help", "/h"];
|
|
3
|
+
/** True when argv includes a help flag anywhere in the token list. */
|
|
4
|
+
export function argvWantsHelp(argv) {
|
|
5
|
+
const flags = INIT_CLI_HELP_FLAGS;
|
|
6
|
+
return argv.some((arg) => flags.includes(arg));
|
|
7
|
+
}
|
|
8
|
+
export function printUpdateHelp(io) {
|
|
9
|
+
io.writeOut("Usage: directive update [options]\n" +
|
|
10
|
+
" deft update [options]\n\n" +
|
|
11
|
+
"Refresh the framework deposit in an existing Directive project and self-heal the engine.\n\n" +
|
|
12
|
+
"Options:\n" +
|
|
13
|
+
" --repo-root <path> Project root (default: current directory)\n" +
|
|
14
|
+
" --json Machine-readable summary on stdout\n" +
|
|
15
|
+
" --yes, --non-interactive Run without prompts\n" +
|
|
16
|
+
" --dry-run, --plan Classify and print the refresh plan without writing\n" +
|
|
17
|
+
" -h, --help Show this help\n");
|
|
18
|
+
}
|
|
19
|
+
export function printInitHelp(io) {
|
|
20
|
+
io.writeOut("Usage: directive init [options]\n" +
|
|
21
|
+
" deft init [options]\n\n" +
|
|
22
|
+
"Set up Directive in the current project (greenfield scaffold or brownfield adoption).\n\n" +
|
|
23
|
+
"Options:\n" +
|
|
24
|
+
" --repo-root <path> Project root (default: current directory)\n" +
|
|
25
|
+
" --json Machine-readable summary on stdout\n" +
|
|
26
|
+
" --yes, --non-interactive Run without prompts\n" +
|
|
27
|
+
" --dry-run, --plan Classify and print the dispatch plan without writing\n" +
|
|
28
|
+
" --headless Emit a { version, files } manifest with no side effects\n" +
|
|
29
|
+
" --output <path> Manifest output path for --headless (default: stdout)\n" +
|
|
30
|
+
" -h, --help Show this help\n");
|
|
31
|
+
}
|
|
32
|
+
export function printMigrateHelp(io) {
|
|
33
|
+
io.writeOut("Usage: directive migrate [options]\n" +
|
|
34
|
+
" deft migrate [options]\n\n" +
|
|
35
|
+
"Stamp a canonical-vendored .deft/core deposit as npm-managed (provenance only).\n\n" +
|
|
36
|
+
"Options:\n" +
|
|
37
|
+
" --repo-root <path> Project root (default: current directory)\n" +
|
|
38
|
+
" --json Machine-readable summary on stdout\n" +
|
|
39
|
+
" --untrack-core Un-commit .deft/core from git (destructive index mutation)\n" +
|
|
40
|
+
" -h, --help Show this help\n");
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=help.js.map
|
package/dist/init-cli/init.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { parseInitArgv, runInitDispatchCli, runInitHeadlessCli, } from "@deftai/directive-core/init-deposit";
|
|
2
2
|
import { CANONICAL_INIT_ARGV, INIT_DRY_RUN_FLAGS, INIT_HEADLESS_FLAGS, INIT_OUTPUT_FLAGS, } from "./constants.js";
|
|
3
|
+
import { argvWantsHelp, printInitHelp } from "./help.js";
|
|
3
4
|
/** True when the user argv asked for a classify-only dispatch plan (`--dry-run`/`--plan`). */
|
|
4
5
|
export function isInitDryRun(argv) {
|
|
5
6
|
const flags = INIT_DRY_RUN_FLAGS;
|
|
@@ -53,6 +54,10 @@ export function parseInitOutputPath(argv) {
|
|
|
53
54
|
* `headlessSeams` injects the content-resolution seam for the headless path.
|
|
54
55
|
*/
|
|
55
56
|
export function runInit(argv, io, seams, headlessSeams) {
|
|
57
|
+
if (argvWantsHelp(argv)) {
|
|
58
|
+
printInitHelp(io);
|
|
59
|
+
return Promise.resolve(0);
|
|
60
|
+
}
|
|
56
61
|
if (isInitHeadless(argv)) {
|
|
57
62
|
return runInitHeadlessCli({
|
|
58
63
|
outputPath: parseInitOutputPath(argv),
|
package/dist/init-cli/migrate.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { parseInitArgv, runMigrateCli, runUntrackCoreCli, } from "@deftai/directive-core/init-deposit";
|
|
2
2
|
import { CANONICAL_MIGRATE_ARGV, MIGRATE_UNTRACK_CORE_FLAG } from "./constants.js";
|
|
3
|
+
import { argvWantsHelp, printMigrateHelp } from "./help.js";
|
|
3
4
|
/**
|
|
4
5
|
* `directive migrate` (alias `deft migrate`) -- stage-2 provenance verb (#1941):
|
|
5
6
|
* stamp a canonical-vendored `.deft/core` deposit as npm-managed. Thin wrapper
|
|
@@ -19,6 +20,10 @@ function hasUntrackCoreFlag(argv) {
|
|
|
19
20
|
return argv.some((arg) => arg === MIGRATE_UNTRACK_CORE_FLAG || arg === `/${MIGRATE_UNTRACK_CORE_FLAG.slice(2)}`);
|
|
20
21
|
}
|
|
21
22
|
export function runMigrate(argv, io) {
|
|
23
|
+
if (argvWantsHelp(argv)) {
|
|
24
|
+
printMigrateHelp(io);
|
|
25
|
+
return 0;
|
|
26
|
+
}
|
|
22
27
|
const args = parseInitArgv(CANONICAL_MIGRATE_ARGV, argv);
|
|
23
28
|
// `migrate --untrack-core` selects the destructive un-track subcommand;
|
|
24
29
|
// bare `migrate` keeps the provenance-stamp behavior below.
|
package/dist/init-cli/update.js
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import { parseUpdateArgv, runRefreshDepositCli } from "@deftai/directive-core/init-deposit";
|
|
2
2
|
import { CANONICAL_UPDATE_ARGV, UPDATE_DRY_RUN_FLAGS } from "./constants.js";
|
|
3
|
+
import { argvWantsHelp, printUpdateHelp } from "./help.js";
|
|
3
4
|
/** True when the user argv asked for a classify-only dry-run (`--dry-run`/`--plan`). */
|
|
4
5
|
export function isUpdateDryRun(argv) {
|
|
5
6
|
const flags = UPDATE_DRY_RUN_FLAGS;
|
|
6
7
|
return argv.some((arg) => flags.includes(arg));
|
|
7
8
|
}
|
|
8
9
|
export function runUpdate(argv, io) {
|
|
10
|
+
if (argvWantsHelp(argv)) {
|
|
11
|
+
printUpdateHelp(io);
|
|
12
|
+
return Promise.resolve(0);
|
|
13
|
+
}
|
|
9
14
|
const args = parseUpdateArgv(CANONICAL_UPDATE_ARGV, argv);
|
|
10
15
|
return runRefreshDepositCli({
|
|
11
16
|
...args,
|
package/dist/policy.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ interface ShowArgs {
|
|
|
7
7
|
error?: string;
|
|
8
8
|
}
|
|
9
9
|
interface SetArgs {
|
|
10
|
-
cmd: "show" | "enforce-branches" | "allow-direct-commits" | "enable-value-feedback" | "resolve";
|
|
10
|
+
cmd: "show" | "enforce-branches" | "allow-direct-commits" | "enable-value-feedback" | "clear-value-feedback" | "resolve";
|
|
11
11
|
confirm: boolean;
|
|
12
12
|
actor: string;
|
|
13
13
|
note: string;
|
package/dist/policy.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import { existsSync } from "node:fs";
|
|
7
7
|
import { resolve as pathResolve, relative } from "node:path";
|
|
8
8
|
import { fileURLToPath } from "node:url";
|
|
9
|
-
import { describeShadowedPlanExtension, detectShadowedPlanExtensions, disclosureLine, enableValueFeedback, FIELD_VALUE_FEEDBACK, FIELD_VALUE_FEEDBACK_CLI_ALIAS, formatValueFeedbackStatusLine, inspectAllPolicies, inspectOnePolicy, loadProjectDefinition, policyColonInvocation, projectDefinitionPath, pythonListRepr, pythonStringRepr, registeredPolicyNames, renderJson, renderText, resolvePolicy, resolveValueFeedback, setPolicy, } from "@deftai/directive-core/policy";
|
|
9
|
+
import { clearValueFeedback, describeShadowedPlanExtension, detectShadowedPlanExtensions, disclosureLine, enableValueFeedback, FIELD_VALUE_FEEDBACK, FIELD_VALUE_FEEDBACK_CLI_ALIAS, formatValueFeedbackStatusLine, inspectAllPolicies, inspectOnePolicy, loadProjectDefinition, policyColonInvocation, projectDefinitionPath, pythonListRepr, pythonStringRepr, registeredPolicyNames, renderJson, renderText, resolvePolicy, resolveValueFeedback, setPolicy, } from "@deftai/directive-core/policy";
|
|
10
10
|
const CAPABILITY_COST_DISCLOSURE = "\u26a0 Capability-cost disclosure -- enabling direct commits to the default " +
|
|
11
11
|
"branch turns OFF the deft branch-protection policy.\n" +
|
|
12
12
|
" \u2022 Pre-commit + pre-push hooks will no longer block default-branch " +
|
|
@@ -114,7 +114,7 @@ export function parseShowArgs(argv) {
|
|
|
114
114
|
/** Parse argv for the policy CLI (show + set subcommands). */
|
|
115
115
|
export function parseArgs(argv) {
|
|
116
116
|
if (argv.length === 0) {
|
|
117
|
-
const usage = "usage: policy [show|enforce-branches|allow-direct-commits|enable-value-feedback|resolve] ...";
|
|
117
|
+
const usage = "usage: policy [show|enforce-branches|allow-direct-commits|enable-value-feedback|clear-value-feedback|resolve] ...";
|
|
118
118
|
return makeSetError(usage);
|
|
119
119
|
}
|
|
120
120
|
const cmd = argv[0];
|
|
@@ -148,13 +148,16 @@ export function parseArgs(argv) {
|
|
|
148
148
|
}
|
|
149
149
|
if (cmd === "enforce-branches" ||
|
|
150
150
|
cmd === "allow-direct-commits" ||
|
|
151
|
-
cmd === "enable-value-feedback"
|
|
151
|
+
cmd === "enable-value-feedback" ||
|
|
152
|
+
cmd === "clear-value-feedback") {
|
|
152
153
|
let confirm = false;
|
|
153
154
|
let actor = cmd === "enforce-branches"
|
|
154
155
|
? policyColonInvocation("enforce-branches")
|
|
155
156
|
: cmd === "allow-direct-commits"
|
|
156
157
|
? policyColonInvocation("allow-direct-commits")
|
|
157
|
-
:
|
|
158
|
+
: cmd === "enable-value-feedback"
|
|
159
|
+
? policyColonInvocation("enable-value-feedback")
|
|
160
|
+
: policyColonInvocation("clear-value-feedback");
|
|
158
161
|
let note = "";
|
|
159
162
|
let projectRoot = ".";
|
|
160
163
|
for (let i = 1; i < argv.length; i += 1) {
|
|
@@ -327,6 +330,14 @@ function runSet(args) {
|
|
|
327
330
|
return 2;
|
|
328
331
|
}
|
|
329
332
|
}
|
|
333
|
+
function runClearValueFeedback(args) {
|
|
334
|
+
const result = clearValueFeedback(pathResolve(args.projectRoot), {
|
|
335
|
+
actor: args.actor,
|
|
336
|
+
note: args.note,
|
|
337
|
+
});
|
|
338
|
+
process.stdout.write(result.stdout);
|
|
339
|
+
return result.exitCode;
|
|
340
|
+
}
|
|
330
341
|
/** Run the policy CLI; returns process exit code. */
|
|
331
342
|
export function run(argv) {
|
|
332
343
|
const args = parseArgs(argv);
|
|
@@ -351,6 +362,9 @@ export function run(argv) {
|
|
|
351
362
|
if (args.cmd === "enable-value-feedback") {
|
|
352
363
|
return runEnableValueFeedback(args);
|
|
353
364
|
}
|
|
365
|
+
if (args.cmd === "clear-value-feedback") {
|
|
366
|
+
return runClearValueFeedback(args);
|
|
367
|
+
}
|
|
354
368
|
return 2;
|
|
355
369
|
}
|
|
356
370
|
if (process.argv[1] !== undefined && fileURLToPath(import.meta.url) === process.argv[1]) {
|
|
@@ -6,8 +6,10 @@ interface ParsedArgs {
|
|
|
6
6
|
platformPrimitive: PlatformPrimitive | null;
|
|
7
7
|
repo: string | null;
|
|
8
8
|
headSha: string | null;
|
|
9
|
+
owner: string | null;
|
|
9
10
|
projectRoot: string;
|
|
10
11
|
parentSessionId: string | null;
|
|
12
|
+
force: boolean;
|
|
11
13
|
help: boolean;
|
|
12
14
|
error?: string;
|
|
13
15
|
}
|
|
@@ -10,8 +10,10 @@ export function parseRegisterArgs(argv) {
|
|
|
10
10
|
platformPrimitive: null,
|
|
11
11
|
repo: null,
|
|
12
12
|
headSha: null,
|
|
13
|
+
owner: null,
|
|
13
14
|
projectRoot: ".",
|
|
14
15
|
parentSessionId: null,
|
|
16
|
+
force: false,
|
|
15
17
|
help: false,
|
|
16
18
|
};
|
|
17
19
|
for (let i = 0; i < argv.length; i += 1) {
|
|
@@ -19,7 +21,10 @@ export function parseRegisterArgs(argv) {
|
|
|
19
21
|
if (arg === "--help" || arg === "-h") {
|
|
20
22
|
return { ...acc, help: true };
|
|
21
23
|
}
|
|
22
|
-
if (arg === "--
|
|
24
|
+
if (arg === "--force") {
|
|
25
|
+
acc.force = true;
|
|
26
|
+
}
|
|
27
|
+
else if (arg === "--pr") {
|
|
23
28
|
const value = argv[i + 1];
|
|
24
29
|
if (value === undefined) {
|
|
25
30
|
return { ...acc, error: "argument --pr: expected one argument" };
|
|
@@ -78,6 +83,17 @@ export function parseRegisterArgs(argv) {
|
|
|
78
83
|
else if (arg?.startsWith("--repo=")) {
|
|
79
84
|
acc.repo = arg.slice("--repo=".length);
|
|
80
85
|
}
|
|
86
|
+
else if (arg === "--owner") {
|
|
87
|
+
const value = argv[i + 1];
|
|
88
|
+
if (value === undefined) {
|
|
89
|
+
return { ...acc, error: "argument --owner: expected one argument" };
|
|
90
|
+
}
|
|
91
|
+
acc.owner = value;
|
|
92
|
+
i += 1;
|
|
93
|
+
}
|
|
94
|
+
else if (arg?.startsWith("--owner=")) {
|
|
95
|
+
acc.owner = arg.slice("--owner=".length);
|
|
96
|
+
}
|
|
81
97
|
else if (arg === "--head-sha") {
|
|
82
98
|
const value = argv[i + 1];
|
|
83
99
|
if (value === undefined) {
|
|
@@ -142,23 +158,24 @@ export function run(argv) {
|
|
|
142
158
|
process.stderr.write("review_monitor_register: --platform-primitive is required\n");
|
|
143
159
|
return 2;
|
|
144
160
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
161
|
+
const result = registerReviewMonitor({
|
|
162
|
+
pr: args.pr,
|
|
163
|
+
repo: args.repo,
|
|
164
|
+
headSha: args.headSha,
|
|
165
|
+
owner: args.owner,
|
|
166
|
+
platformPrimitive: args.platformPrimitive,
|
|
167
|
+
monitorAgentId: args.monitorAgentId,
|
|
168
|
+
projectRoot: resolve(args.projectRoot),
|
|
169
|
+
parentSessionId: args.parentSessionId,
|
|
170
|
+
force: args.force,
|
|
171
|
+
});
|
|
172
|
+
if (result.exitCode === 0) {
|
|
173
|
+
process.stdout.write(`${result.message}\n`);
|
|
157
174
|
}
|
|
158
|
-
|
|
159
|
-
process.stderr.write(
|
|
160
|
-
return 2;
|
|
175
|
+
else {
|
|
176
|
+
process.stderr.write(`${result.message}\n`);
|
|
161
177
|
}
|
|
178
|
+
return result.exitCode;
|
|
162
179
|
}
|
|
163
180
|
if (process.argv[1] !== undefined && fileURLToPath(import.meta.url) === process.argv[1]) {
|
|
164
181
|
process.exit(run(process.argv.slice(2)));
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
interface ParsedArgs {
|
|
3
|
+
pr: number | null;
|
|
4
|
+
repo: string | null;
|
|
5
|
+
monitorAgentId: string | null;
|
|
6
|
+
owner: string | null;
|
|
7
|
+
projectRoot: string;
|
|
8
|
+
help: boolean;
|
|
9
|
+
error?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function parseReleaseArgs(argv: readonly string[]): ParsedArgs;
|
|
12
|
+
export declare function run(argv: readonly string[]): number;
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=review-monitor-release.d.ts.map
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { RELEASE_HELP, releaseReviewMonitor } from "@deftai/directive-core/review-monitor";
|
|
5
|
+
export function parseReleaseArgs(argv) {
|
|
6
|
+
const acc = {
|
|
7
|
+
pr: null,
|
|
8
|
+
repo: null,
|
|
9
|
+
monitorAgentId: null,
|
|
10
|
+
owner: null,
|
|
11
|
+
projectRoot: ".",
|
|
12
|
+
help: false,
|
|
13
|
+
};
|
|
14
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
15
|
+
const arg = argv[i];
|
|
16
|
+
if (arg === "--help" || arg === "-h") {
|
|
17
|
+
return { ...acc, help: true };
|
|
18
|
+
}
|
|
19
|
+
if (arg === "--pr") {
|
|
20
|
+
const value = argv[i + 1];
|
|
21
|
+
if (value === undefined) {
|
|
22
|
+
return { ...acc, error: "argument --pr: expected one argument" };
|
|
23
|
+
}
|
|
24
|
+
const n = Number(value);
|
|
25
|
+
if (!Number.isInteger(n) || n <= 0) {
|
|
26
|
+
return { ...acc, error: `invalid --pr value: ${value}` };
|
|
27
|
+
}
|
|
28
|
+
acc.pr = n;
|
|
29
|
+
i += 1;
|
|
30
|
+
}
|
|
31
|
+
else if (arg?.startsWith("--pr=")) {
|
|
32
|
+
const n = Number(arg.slice("--pr=".length));
|
|
33
|
+
if (!Number.isInteger(n) || n <= 0) {
|
|
34
|
+
return { ...acc, error: `invalid --pr value: ${arg}` };
|
|
35
|
+
}
|
|
36
|
+
acc.pr = n;
|
|
37
|
+
}
|
|
38
|
+
else if (arg === "--repo") {
|
|
39
|
+
const value = argv[i + 1];
|
|
40
|
+
if (value === undefined) {
|
|
41
|
+
return { ...acc, error: "argument --repo: expected one argument" };
|
|
42
|
+
}
|
|
43
|
+
acc.repo = value;
|
|
44
|
+
i += 1;
|
|
45
|
+
}
|
|
46
|
+
else if (arg?.startsWith("--repo=")) {
|
|
47
|
+
acc.repo = arg.slice("--repo=".length);
|
|
48
|
+
}
|
|
49
|
+
else if (arg === "--monitor-agent-id") {
|
|
50
|
+
const value = argv[i + 1];
|
|
51
|
+
if (value === undefined) {
|
|
52
|
+
return { ...acc, error: "argument --monitor-agent-id: expected one argument" };
|
|
53
|
+
}
|
|
54
|
+
acc.monitorAgentId = value;
|
|
55
|
+
i += 1;
|
|
56
|
+
}
|
|
57
|
+
else if (arg?.startsWith("--monitor-agent-id=")) {
|
|
58
|
+
acc.monitorAgentId = arg.slice("--monitor-agent-id=".length);
|
|
59
|
+
}
|
|
60
|
+
else if (arg === "--owner") {
|
|
61
|
+
const value = argv[i + 1];
|
|
62
|
+
if (value === undefined) {
|
|
63
|
+
return { ...acc, error: "argument --owner: expected one argument" };
|
|
64
|
+
}
|
|
65
|
+
acc.owner = value;
|
|
66
|
+
i += 1;
|
|
67
|
+
}
|
|
68
|
+
else if (arg?.startsWith("--owner=")) {
|
|
69
|
+
acc.owner = arg.slice("--owner=".length);
|
|
70
|
+
}
|
|
71
|
+
else if (arg === "--project-root") {
|
|
72
|
+
const value = argv[i + 1];
|
|
73
|
+
if (value === undefined) {
|
|
74
|
+
return { ...acc, error: "argument --project-root: expected one argument" };
|
|
75
|
+
}
|
|
76
|
+
acc.projectRoot = value;
|
|
77
|
+
i += 1;
|
|
78
|
+
}
|
|
79
|
+
else if (arg?.startsWith("--project-root=")) {
|
|
80
|
+
acc.projectRoot = arg.slice("--project-root=".length);
|
|
81
|
+
}
|
|
82
|
+
else if (arg?.startsWith("-")) {
|
|
83
|
+
return { ...acc, error: `unrecognized argument: ${arg}` };
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
return { ...acc, error: `unrecognized argument: ${arg}` };
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return acc;
|
|
90
|
+
}
|
|
91
|
+
export function run(argv) {
|
|
92
|
+
const args = parseReleaseArgs(argv);
|
|
93
|
+
if (args.help) {
|
|
94
|
+
process.stdout.write(RELEASE_HELP);
|
|
95
|
+
return 0;
|
|
96
|
+
}
|
|
97
|
+
if (args.error !== undefined) {
|
|
98
|
+
process.stderr.write(`review_monitor_release: ${args.error}\n`);
|
|
99
|
+
return 2;
|
|
100
|
+
}
|
|
101
|
+
if (args.pr === null) {
|
|
102
|
+
process.stderr.write("review_monitor_release: --pr is required\n");
|
|
103
|
+
return 2;
|
|
104
|
+
}
|
|
105
|
+
const result = releaseReviewMonitor({
|
|
106
|
+
pr: args.pr,
|
|
107
|
+
repo: args.repo,
|
|
108
|
+
monitorAgentId: args.monitorAgentId,
|
|
109
|
+
owner: args.owner,
|
|
110
|
+
projectRoot: resolve(args.projectRoot),
|
|
111
|
+
});
|
|
112
|
+
if (result.exitCode === 0) {
|
|
113
|
+
process.stdout.write(`${result.message}\n`);
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
process.stderr.write(`${result.message}\n`);
|
|
117
|
+
}
|
|
118
|
+
return result.exitCode;
|
|
119
|
+
}
|
|
120
|
+
if (process.argv[1] !== undefined && fileURLToPath(import.meta.url) === process.argv[1]) {
|
|
121
|
+
process.exit(run(process.argv.slice(2)));
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=review-monitor-release.js.map
|
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,21 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
export declare const SUBAGENT_ALIVE_HELP = "verify:subagent-alive \u2014 fail-closed worker liveness gate (#2824)\n\nUsage:\n task verify:subagent-alive -- [options]\n task agent:monitor -- [options] (alias: raw heartbeat sweep)\n\nOptions:\n --scratch-dir PATH Heartbeat directory (repeatable; default: .deft-scratch/subagent-status)\n --require-agent ID In-flight worker that MUST have a fresh heartbeat (repeatable)\n --threshold-minutes N Staleness threshold (default: 30)\n --json Machine-readable output\n\nExit codes:\n 0 All required agents fresh (or no required agents and sweep clean)\n 1 Missing/STALE/malformed heartbeat \u2014 prints REDISPATCH_OK for takeover\n 2 Config error (invalid args, missing scratch dir with no records)\n";
|
|
3
|
+
export interface VerifySubagentAliveArgs {
|
|
4
|
+
scratchDirs: string[];
|
|
5
|
+
requireAgents: string[];
|
|
6
|
+
thresholdMinutes: number;
|
|
7
|
+
emitJson: boolean;
|
|
8
|
+
help: boolean;
|
|
9
|
+
error?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function parseVerifySubagentAliveArgs(argv: readonly string[]): VerifySubagentAliveArgs;
|
|
12
|
+
export interface SubagentAliveVerdict {
|
|
13
|
+
exitCode: number;
|
|
14
|
+
redispatchOk: boolean;
|
|
15
|
+
message: string;
|
|
16
|
+
json?: Record<string, unknown>;
|
|
17
|
+
}
|
|
18
|
+
/** Evaluate liveness for required in-flight workers. */
|
|
19
|
+
export declare function evaluateSubagentAliveGate(args: VerifySubagentAliveArgs, cwd?: string): SubagentAliveVerdict;
|
|
20
|
+
export declare function run(argv: readonly string[]): number;
|
|
21
|
+
//# sourceMappingURL=verify-subagent-alive.d.ts.map
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Fail-closed worker liveness gate (#2824). Wraps subagent-monitor with
|
|
4
|
+
* REDISPATCH_OK on missing/STALE heartbeats for registered in-flight workers.
|
|
5
|
+
*/
|
|
6
|
+
import { resolve } from "node:path";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
import { DEFAULT_THRESHOLD_MINUTES, defaultScratchDir, EXIT_EXTERNAL_ERROR, EXIT_OK, EXIT_STALE, recordOk, sweepAllOk, sweepScratchDirs, } from "@deftai/directive-core/orchestration";
|
|
9
|
+
export const SUBAGENT_ALIVE_HELP = `verify:subagent-alive — fail-closed worker liveness gate (#2824)
|
|
10
|
+
|
|
11
|
+
Usage:
|
|
12
|
+
task verify:subagent-alive -- [options]
|
|
13
|
+
task agent:monitor -- [options] (alias: raw heartbeat sweep)
|
|
14
|
+
|
|
15
|
+
Options:
|
|
16
|
+
--scratch-dir PATH Heartbeat directory (repeatable; default: .deft-scratch/subagent-status)
|
|
17
|
+
--require-agent ID In-flight worker that MUST have a fresh heartbeat (repeatable)
|
|
18
|
+
--threshold-minutes N Staleness threshold (default: 30)
|
|
19
|
+
--json Machine-readable output
|
|
20
|
+
|
|
21
|
+
Exit codes:
|
|
22
|
+
0 All required agents fresh (or no required agents and sweep clean)
|
|
23
|
+
1 Missing/STALE/malformed heartbeat — prints REDISPATCH_OK for takeover
|
|
24
|
+
2 Config error (invalid args, missing scratch dir with no records)
|
|
25
|
+
`;
|
|
26
|
+
export function parseVerifySubagentAliveArgs(argv) {
|
|
27
|
+
const acc = {
|
|
28
|
+
scratchDirs: [],
|
|
29
|
+
requireAgents: [],
|
|
30
|
+
thresholdMinutes: DEFAULT_THRESHOLD_MINUTES,
|
|
31
|
+
emitJson: false,
|
|
32
|
+
help: false,
|
|
33
|
+
};
|
|
34
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
35
|
+
const arg = argv[i];
|
|
36
|
+
if (arg === "--help" || arg === "-h") {
|
|
37
|
+
return { ...acc, help: true };
|
|
38
|
+
}
|
|
39
|
+
if (arg === "--json") {
|
|
40
|
+
acc.emitJson = true;
|
|
41
|
+
}
|
|
42
|
+
else if (arg === "--scratch-dir") {
|
|
43
|
+
const value = argv[i + 1];
|
|
44
|
+
if (value === undefined) {
|
|
45
|
+
return { ...acc, error: "argument --scratch-dir: expected one argument" };
|
|
46
|
+
}
|
|
47
|
+
acc.scratchDirs.push(value);
|
|
48
|
+
i += 1;
|
|
49
|
+
}
|
|
50
|
+
else if (arg?.startsWith("--scratch-dir=")) {
|
|
51
|
+
acc.scratchDirs.push(arg.slice("--scratch-dir=".length));
|
|
52
|
+
}
|
|
53
|
+
else if (arg === "--threshold-minutes") {
|
|
54
|
+
const value = argv[i + 1];
|
|
55
|
+
if (value === undefined) {
|
|
56
|
+
return { ...acc, error: "argument --threshold-minutes: expected one argument" };
|
|
57
|
+
}
|
|
58
|
+
acc.thresholdMinutes = Number(value);
|
|
59
|
+
i += 1;
|
|
60
|
+
}
|
|
61
|
+
else if (arg?.startsWith("--threshold-minutes=")) {
|
|
62
|
+
acc.thresholdMinutes = Number(arg.slice("--threshold-minutes=".length));
|
|
63
|
+
}
|
|
64
|
+
else if (arg === "--require-agent") {
|
|
65
|
+
const value = argv[i + 1];
|
|
66
|
+
if (value === undefined) {
|
|
67
|
+
return { ...acc, error: "argument --require-agent: expected one argument" };
|
|
68
|
+
}
|
|
69
|
+
acc.requireAgents.push(value);
|
|
70
|
+
i += 1;
|
|
71
|
+
}
|
|
72
|
+
else if (arg?.startsWith("--require-agent=")) {
|
|
73
|
+
acc.requireAgents.push(arg.slice("--require-agent=".length));
|
|
74
|
+
}
|
|
75
|
+
else if (arg?.startsWith("-")) {
|
|
76
|
+
return { ...acc, error: `unrecognized argument: ${arg}` };
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
return { ...acc, error: `unrecognized argument: ${arg}` };
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return acc;
|
|
83
|
+
}
|
|
84
|
+
function scratchEntries(scratchDirs, cwd) {
|
|
85
|
+
if (scratchDirs.length > 0) {
|
|
86
|
+
return scratchDirs.map((p) => ({ readPath: resolve(cwd, p), label: p }));
|
|
87
|
+
}
|
|
88
|
+
const defaultDir = defaultScratchDir(cwd);
|
|
89
|
+
return [{ readPath: defaultDir, label: defaultDir }];
|
|
90
|
+
}
|
|
91
|
+
/** Evaluate liveness for required in-flight workers. */
|
|
92
|
+
export function evaluateSubagentAliveGate(args, cwd = process.cwd()) {
|
|
93
|
+
if (args.error !== undefined) {
|
|
94
|
+
return {
|
|
95
|
+
exitCode: EXIT_EXTERNAL_ERROR,
|
|
96
|
+
redispatchOk: false,
|
|
97
|
+
message: `verify_subagent_alive: ${args.error}`,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
if (Number.isNaN(args.thresholdMinutes) || args.thresholdMinutes <= 0) {
|
|
101
|
+
return {
|
|
102
|
+
exitCode: EXIT_EXTERNAL_ERROR,
|
|
103
|
+
redispatchOk: false,
|
|
104
|
+
message: `verify_subagent_alive: --threshold-minutes must be positive, got ${args.thresholdMinutes}`,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
const entries = scratchEntries(args.scratchDirs, cwd);
|
|
108
|
+
const sweep = sweepScratchDirs(entries, { thresholdMinutes: args.thresholdMinutes });
|
|
109
|
+
const configError = sweep.sweep_errors.length > 0 && sweep.records.length === 0;
|
|
110
|
+
const missingAgents = [];
|
|
111
|
+
for (const agentId of args.requireAgents) {
|
|
112
|
+
const rec = sweep.records.find((r) => r.agent_id === agentId);
|
|
113
|
+
if (rec === undefined || !recordOk(rec)) {
|
|
114
|
+
missingAgents.push(agentId);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
const requiredUnhealthy = missingAgents.length > 0;
|
|
118
|
+
// When --require-agent is set, only named workers gate the verdict; unrelated
|
|
119
|
+
// stale records in a shared scratch dir must not trigger REDISPATCH_OK (#2824).
|
|
120
|
+
const sweepUnhealthy = args.requireAgents.length === 0 && !sweepAllOk(sweep);
|
|
121
|
+
const unhealthy = sweepUnhealthy || requiredUnhealthy;
|
|
122
|
+
if (args.emitJson) {
|
|
123
|
+
return {
|
|
124
|
+
exitCode: configError ? EXIT_EXTERNAL_ERROR : unhealthy ? EXIT_STALE : EXIT_OK,
|
|
125
|
+
redispatchOk: unhealthy && !configError,
|
|
126
|
+
message: "",
|
|
127
|
+
json: {
|
|
128
|
+
all_ok: !unhealthy && !configError,
|
|
129
|
+
redispatch_ok: unhealthy && !configError,
|
|
130
|
+
required_agents: [...args.requireAgents],
|
|
131
|
+
missing_or_stale_agents: missingAgents,
|
|
132
|
+
sweep: {
|
|
133
|
+
scratch_dirs: sweep.scratch_dirs,
|
|
134
|
+
threshold_minutes: sweep.threshold_minutes,
|
|
135
|
+
stale_count: sweep.records.filter((r) => r.is_stale).length,
|
|
136
|
+
malformed_count: sweep.records.filter((r) => r.failures.length > 0).length,
|
|
137
|
+
sweep_errors: sweep.sweep_errors,
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
if (configError) {
|
|
143
|
+
const detail = sweep.sweep_errors.join("; ");
|
|
144
|
+
return {
|
|
145
|
+
exitCode: EXIT_EXTERNAL_ERROR,
|
|
146
|
+
redispatchOk: false,
|
|
147
|
+
message: `verify_subagent_alive: config error — ${detail}`,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
if (!unhealthy) {
|
|
151
|
+
const count = args.requireAgents.length > 0 ? args.requireAgents.length : sweep.records.length;
|
|
152
|
+
return {
|
|
153
|
+
exitCode: EXIT_OK,
|
|
154
|
+
redispatchOk: false,
|
|
155
|
+
message: `verify_subagent_alive: all ${count} monitored worker(s) alive`,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
const lines = ["verify_subagent_alive: worker liveness gate FAILED"];
|
|
159
|
+
if (requiredUnhealthy) {
|
|
160
|
+
lines.push(` Missing or STALE required agent(s): ${missingAgents.join(", ")}`);
|
|
161
|
+
}
|
|
162
|
+
if (sweepUnhealthy && args.requireAgents.length === 0) {
|
|
163
|
+
const stale = sweep.records.filter((r) => r.is_stale).length;
|
|
164
|
+
const malformed = sweep.records.filter((r) => r.failures.length > 0).length;
|
|
165
|
+
lines.push(` Sweep: ${stale} stale, ${malformed} malformed record(s)`);
|
|
166
|
+
}
|
|
167
|
+
lines.push("");
|
|
168
|
+
lines.push("REDISPATCH_OK: host-reported running + missing/STALE heartbeat authorizes takeover.");
|
|
169
|
+
lines.push("Re-dispatch a replacement worker; do not wait on Cursor false-alive state.");
|
|
170
|
+
return {
|
|
171
|
+
exitCode: EXIT_STALE,
|
|
172
|
+
redispatchOk: true,
|
|
173
|
+
message: lines.join("\n"),
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
export function run(argv) {
|
|
177
|
+
const args = parseVerifySubagentAliveArgs(argv);
|
|
178
|
+
if (args.help) {
|
|
179
|
+
process.stdout.write(SUBAGENT_ALIVE_HELP);
|
|
180
|
+
return EXIT_OK;
|
|
181
|
+
}
|
|
182
|
+
const verdict = evaluateSubagentAliveGate(args);
|
|
183
|
+
if (args.emitJson && verdict.json !== undefined) {
|
|
184
|
+
process.stdout.write(`${JSON.stringify(verdict.json, null, 2)}\n`);
|
|
185
|
+
}
|
|
186
|
+
else if (verdict.message.length > 0) {
|
|
187
|
+
if (verdict.exitCode === EXIT_OK) {
|
|
188
|
+
process.stdout.write(`${verdict.message}\n`);
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
process.stderr.write(`${verdict.message}\n`);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return verdict.exitCode;
|
|
195
|
+
}
|
|
196
|
+
if (process.argv[1] !== undefined && fileURLToPath(import.meta.url) === process.argv[1]) {
|
|
197
|
+
process.exit(run(process.argv.slice(2)));
|
|
198
|
+
}
|
|
199
|
+
//# sourceMappingURL=verify-subagent-alive.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deftai/directive",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.86.0",
|
|
4
4
|
"description": "Directive CLI — npm install path for the Deft Directive framework.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"directive": "./dist/bin.js",
|
|
10
10
|
"deft": "./dist/bin.js",
|
|
11
11
|
"deft-ts": "./dist/bin.js",
|
|
12
|
+
"deft-hook": "./dist/hook-bin.js",
|
|
12
13
|
"deft-verify-encoding": "./dist/verify-encoding.js"
|
|
13
14
|
},
|
|
14
15
|
"exports": {
|
|
@@ -31,8 +32,8 @@
|
|
|
31
32
|
"provenance": true
|
|
32
33
|
},
|
|
33
34
|
"dependencies": {
|
|
34
|
-
"@deftai/directive-core": "^0.
|
|
35
|
-
"@deftai/directive-content": "^0.
|
|
35
|
+
"@deftai/directive-core": "^0.86.0",
|
|
36
|
+
"@deftai/directive-content": "^0.86.0"
|
|
36
37
|
},
|
|
37
38
|
"scripts": {
|
|
38
39
|
"build": "tsc -b"
|