@deftai/directive-core 0.92.0 → 0.93.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.
Files changed (52) hide show
  1. package/dist/doctor/index.d.ts +1 -0
  2. package/dist/doctor/index.js +1 -0
  3. package/dist/doctor/main.js +12 -0
  4. package/dist/doctor/openclaw-l2-adapter.d.ts +26 -0
  5. package/dist/doctor/openclaw-l2-adapter.js +199 -0
  6. package/dist/index.d.ts +2 -0
  7. package/dist/index.js +2 -0
  8. package/dist/init-deposit/hygiene.js +22 -0
  9. package/dist/init-deposit/index.d.ts +3 -0
  10. package/dist/init-deposit/index.js +3 -0
  11. package/dist/init-deposit/init-deposit.js +10 -0
  12. package/dist/init-deposit/refresh.js +11 -0
  13. package/dist/init-deposit/scaffold.js +3 -133
  14. package/dist/init-deposit/skill-discovery-deposit.d.ts +65 -0
  15. package/dist/init-deposit/skill-discovery-deposit.js +287 -0
  16. package/dist/init-deposit/skill-discovery-hosts.d.ts +94 -0
  17. package/dist/init-deposit/skill-discovery-hosts.js +217 -0
  18. package/dist/init-deposit/slash-deposit.d.ts +46 -0
  19. package/dist/init-deposit/slash-deposit.js +174 -0
  20. package/dist/policy/host-slash-commands.d.ts +28 -0
  21. package/dist/policy/host-slash-commands.js +103 -0
  22. package/dist/policy/index.d.ts +1 -0
  23. package/dist/policy/index.js +47 -7
  24. package/dist/slash/emitters.d.ts +102 -0
  25. package/dist/slash/emitters.js +148 -0
  26. package/dist/slash/generator.d.ts +98 -0
  27. package/dist/slash/generator.js +145 -0
  28. package/dist/slash/index.d.ts +16 -0
  29. package/dist/slash/index.js +16 -0
  30. package/dist/slash/openclaw-adapter.d.ts +64 -0
  31. package/dist/slash/openclaw-adapter.js +198 -0
  32. package/dist/slash/openclaw-deposit.d.ts +73 -0
  33. package/dist/slash/openclaw-deposit.js +279 -0
  34. package/dist/slash/openclaw-slugs.d.ts +52 -0
  35. package/dist/slash/openclaw-slugs.js +126 -0
  36. package/dist/slash/product-set.d.ts +50 -0
  37. package/dist/slash/product-set.js +142 -0
  38. package/dist/vbrief-validate/plan-hooks.d.ts +4 -0
  39. package/dist/vbrief-validate/plan-hooks.js +50 -0
  40. package/dist/xbrief/create.d.ts +36 -0
  41. package/dist/xbrief/create.js +285 -0
  42. package/dist/xbrief/index.d.ts +14 -0
  43. package/dist/xbrief/index.js +42 -0
  44. package/dist/xbrief/paths.d.ts +37 -0
  45. package/dist/xbrief/paths.js +123 -0
  46. package/dist/xbrief/styles.d.ts +36 -0
  47. package/dist/xbrief/styles.js +235 -0
  48. package/dist/xbrief/types.d.ts +50 -0
  49. package/dist/xbrief/types.js +17 -0
  50. package/dist/xbrief/verify.d.ts +30 -0
  51. package/dist/xbrief/verify.js +251 -0
  52. package/package.json +11 -3
@@ -0,0 +1,235 @@
1
+ /**
2
+ * P0 xBRIEF style templates and markdown section maps (#3057).
3
+ *
4
+ * One schema spine (xBRIEF v0.8); markdown is a dense render of the same plan,
5
+ * not a second dialect.
6
+ */
7
+ /** Required H2 section titles in the markdown form per style. */
8
+ export const MD_REQUIRED_SECTIONS = {
9
+ scope: ["Title", "Status", "Overview", "Items"],
10
+ playbook: ["Title", "Status", "Overview", "Steps"],
11
+ mission: ["Title", "Status", "Outcome", "Evidence"],
12
+ project: ["Title", "Status", "Overview", "TechStack"],
13
+ };
14
+ function isoNow(now) {
15
+ return now.toISOString().replace(/\.\d{3}Z$/, "Z");
16
+ }
17
+ function defaultStatus(style) {
18
+ switch (style) {
19
+ case "scope":
20
+ return "draft";
21
+ case "playbook":
22
+ return "approved";
23
+ case "mission":
24
+ return "completed";
25
+ case "project":
26
+ return "approved";
27
+ }
28
+ }
29
+ function defaultNarratives(style, title) {
30
+ switch (style) {
31
+ case "scope":
32
+ return {
33
+ Overview: `Scope brief for ${title}.`,
34
+ Description: "",
35
+ };
36
+ case "playbook":
37
+ return {
38
+ Overview: `Playbook for ${title}.`,
39
+ Steps: "1. Inspect\n2. Act\n3. Verify",
40
+ };
41
+ case "mission":
42
+ return {
43
+ Outcome: `Mission outcome for ${title}.`,
44
+ Evidence: "",
45
+ };
46
+ case "project":
47
+ return {
48
+ Overview: `Project identity for ${title}.`,
49
+ TechStack: "",
50
+ };
51
+ }
52
+ }
53
+ function defaultItems(style) {
54
+ if (style === "playbook") {
55
+ return [
56
+ {
57
+ title: "Inspect",
58
+ status: "proposed",
59
+ narrative: { Acceptance: "Context is loaded and preconditions hold." },
60
+ },
61
+ {
62
+ title: "Act",
63
+ status: "proposed",
64
+ narrative: { Acceptance: "Primary steps complete." },
65
+ },
66
+ {
67
+ title: "Verify",
68
+ status: "proposed",
69
+ narrative: { Acceptance: "Checks pass or failures are recorded." },
70
+ },
71
+ ];
72
+ }
73
+ return [];
74
+ }
75
+ /** Build a minimal valid xBRIEF v0.8 document for the given P0 style. */
76
+ export function buildStyleDocument(input) {
77
+ const now = input.now ?? new Date();
78
+ const stamp = isoNow(now);
79
+ const status = input.status ?? defaultStatus(input.style);
80
+ const baseNarratives = defaultNarratives(input.style, input.title);
81
+ const narratives = { ...baseNarratives, ...(input.narratives ?? {}) };
82
+ const items = input.items ?? defaultItems(input.style);
83
+ const plan = {
84
+ title: input.title,
85
+ status,
86
+ narratives,
87
+ items,
88
+ metadata: {
89
+ kind: input.style,
90
+ xbriefCreate: true,
91
+ },
92
+ };
93
+ if (input.id !== undefined && input.id.length > 0) {
94
+ plan.id = input.id;
95
+ }
96
+ return {
97
+ xBRIEFInfo: {
98
+ version: "0.8",
99
+ description: input.description ?? `xBRIEF ${input.style} artifact (on-demand create)`,
100
+ created: stamp,
101
+ updated: stamp,
102
+ },
103
+ plan,
104
+ };
105
+ }
106
+ function sectionBody(doc, key) {
107
+ const narratives = doc.plan.narratives ?? {};
108
+ // Prefer exact key, then case-insensitive match.
109
+ if (typeof narratives[key] === "string")
110
+ return narratives[key];
111
+ const lower = key.toLowerCase();
112
+ for (const [k, v] of Object.entries(narratives)) {
113
+ if (k.toLowerCase() === lower && typeof v === "string")
114
+ return v;
115
+ }
116
+ return "";
117
+ }
118
+ function renderItemsList(doc, heading) {
119
+ const lines = [`## ${heading}`, ""];
120
+ const items = Array.isArray(doc.plan.items) ? doc.plan.items : [];
121
+ if (items.length === 0) {
122
+ lines.push("_(none)_", "");
123
+ return lines.join("\n");
124
+ }
125
+ for (const raw of items) {
126
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
127
+ lines.push(`- ${String(raw)}`);
128
+ continue;
129
+ }
130
+ const item = raw;
131
+ const title = typeof item.title === "string" ? item.title : "(untitled)";
132
+ const status = typeof item.status === "string" ? item.status : "unknown";
133
+ lines.push(`- **${title}** (${status})`);
134
+ }
135
+ lines.push("");
136
+ return lines.join("\n");
137
+ }
138
+ /** Render dense markdown from the JSON spine (one dialect). */
139
+ export function renderMarkdown(doc, style) {
140
+ const idLine = typeof doc.plan.id === "string" && doc.plan.id.length > 0 ? `id: ${doc.plan.id}\n` : "";
141
+ const header = [
142
+ "---",
143
+ "xbrief: 0.8",
144
+ `style: ${style}`,
145
+ idLine.trimEnd() === "" ? null : idLine.trimEnd(),
146
+ "---",
147
+ "",
148
+ `# ${doc.plan.title}`,
149
+ "",
150
+ ]
151
+ .filter((line) => line !== null)
152
+ .join("\n");
153
+ const sections = MD_REQUIRED_SECTIONS[style];
154
+ const parts = [header];
155
+ for (const section of sections) {
156
+ if (section === "Title") {
157
+ parts.push("## Title", "", doc.plan.title, "");
158
+ continue;
159
+ }
160
+ if (section === "Status") {
161
+ parts.push("## Status", "", String(doc.plan.status), "");
162
+ continue;
163
+ }
164
+ if (section === "Items" || section === "Steps") {
165
+ parts.push(renderItemsList(doc, section));
166
+ // Also include narrative Steps when present for playbook density.
167
+ if (section === "Steps") {
168
+ const stepsNarrative = sectionBody(doc, "Steps");
169
+ if (stepsNarrative.length > 0) {
170
+ parts.push("### Steps narrative", "", stepsNarrative, "");
171
+ }
172
+ }
173
+ continue;
174
+ }
175
+ const body = sectionBody(doc, section);
176
+ parts.push(`## ${section}`, "", body.length > 0 ? body : "_(empty)_", "");
177
+ }
178
+ return `${parts
179
+ .join("\n")
180
+ .replace(/\n{3,}/g, "\n\n")
181
+ .trimEnd()}\n`;
182
+ }
183
+ /**
184
+ * Lightweight parse of md form for verify: extract title, status, id, and H2 sections.
185
+ * Not a full reverse dialect — verify uses it for required-section + consistency checks.
186
+ */
187
+ export function parseMarkdownMeta(md) {
188
+ const sections = new Set();
189
+ let title = null;
190
+ let status = null;
191
+ let id = null;
192
+ let style = null;
193
+ const lines = md.split(/\r?\n/);
194
+ let inFront = false;
195
+ let afterTitleH2 = false;
196
+ let afterStatusH2 = false;
197
+ for (const line of lines) {
198
+ if (line.trim() === "---") {
199
+ inFront = !inFront;
200
+ continue;
201
+ }
202
+ if (inFront) {
203
+ const idMatch = /^id:\s*(.+)\s*$/.exec(line);
204
+ if (idMatch?.[1] !== undefined)
205
+ id = idMatch[1].trim();
206
+ const styleMatch = /^style:\s*(.+)\s*$/.exec(line);
207
+ if (styleMatch?.[1] !== undefined)
208
+ style = styleMatch[1].trim();
209
+ continue;
210
+ }
211
+ const h1 = /^#\s+(.+)$/.exec(line);
212
+ if (h1?.[1] !== undefined && title === null) {
213
+ title = h1[1].trim();
214
+ }
215
+ const h2 = /^##\s+(.+)$/.exec(line);
216
+ if (h2?.[1] !== undefined) {
217
+ const name = h2[1].trim();
218
+ sections.add(name);
219
+ afterTitleH2 = name === "Title";
220
+ afterStatusH2 = name === "Status";
221
+ continue;
222
+ }
223
+ if (afterTitleH2 && line.trim().length > 0 && !line.startsWith("#")) {
224
+ // Prefer ## Title body over H1 when present.
225
+ title = line.trim();
226
+ afterTitleH2 = false;
227
+ }
228
+ if (afterStatusH2 && line.trim().length > 0 && !line.startsWith("#")) {
229
+ status = line.trim();
230
+ afterStatusH2 = false;
231
+ }
232
+ }
233
+ return { title, status, id, style, sections };
234
+ }
235
+ //# sourceMappingURL=styles.js.map
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Shared types for on-demand xBRIEF create/verify (#3057).
3
+ *
4
+ * create/verify write or check shaped SoT artifacts at an explicit path.
5
+ * They are NOT scope lifecycle verbs (scope:promote / activate / complete).
6
+ */
7
+ export declare const XBRIEF_FORMATS: readonly ["json", "md", "both"];
8
+ export type XbriefFormat = (typeof XBRIEF_FORMATS)[number];
9
+ export declare const XBRIEF_STYLES: readonly ["scope", "playbook", "mission", "project"];
10
+ export type XbriefStyle = (typeof XBRIEF_STYLES)[number];
11
+ /** Default size cap for create/verify payloads (bytes). */
12
+ export declare const DEFAULT_XBRIEF_SIZE_CAP_BYTES: number;
13
+ export interface XbriefCliResult {
14
+ readonly exitCode: number;
15
+ readonly stdout: string;
16
+ readonly stderr: string;
17
+ }
18
+ export interface XbriefPaths {
19
+ /** Absolute project root used for containment. */
20
+ readonly projectRoot: string;
21
+ /** Absolute stem path (no .xbrief.json / .xbrief.md suffix). */
22
+ readonly stemAbs: string;
23
+ /** Absolute JSON path when format includes json. */
24
+ readonly jsonAbs: string | null;
25
+ /** Absolute MD path when format includes md. */
26
+ readonly mdAbs: string | null;
27
+ }
28
+ export interface XbriefDocument {
29
+ readonly xBRIEFInfo: {
30
+ readonly version: "0.8";
31
+ readonly description?: string;
32
+ readonly created?: string;
33
+ readonly updated?: string;
34
+ readonly author?: string;
35
+ readonly [key: string]: unknown;
36
+ };
37
+ readonly plan: {
38
+ readonly title: string;
39
+ readonly status: string;
40
+ readonly id?: string;
41
+ readonly narratives?: Record<string, string>;
42
+ readonly items: unknown[];
43
+ readonly metadata?: Record<string, unknown>;
44
+ readonly [key: string]: unknown;
45
+ };
46
+ readonly [key: string]: unknown;
47
+ }
48
+ export declare function isXbriefFormat(value: string): value is XbriefFormat;
49
+ export declare function isXbriefStyle(value: string): value is XbriefStyle;
50
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Shared types for on-demand xBRIEF create/verify (#3057).
3
+ *
4
+ * create/verify write or check shaped SoT artifacts at an explicit path.
5
+ * They are NOT scope lifecycle verbs (scope:promote / activate / complete).
6
+ */
7
+ export const XBRIEF_FORMATS = ["json", "md", "both"];
8
+ export const XBRIEF_STYLES = ["scope", "playbook", "mission", "project"];
9
+ /** Default size cap for create/verify payloads (bytes). */
10
+ export const DEFAULT_XBRIEF_SIZE_CAP_BYTES = 512 * 1024;
11
+ export function isXbriefFormat(value) {
12
+ return XBRIEF_FORMATS.includes(value);
13
+ }
14
+ export function isXbriefStyle(value) {
15
+ return XBRIEF_STYLES.includes(value);
16
+ }
17
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1,30 @@
1
+ /**
2
+ * xbrief:verify — fail-closed check of an xBRIEF artifact at --out (#3057).
3
+ *
4
+ * Required: --format (json|md|both), --out
5
+ * Checks: schema/parse, required fields, size cap, md sections per style,
6
+ * and stem/title/id consistency when format=both.
7
+ *
8
+ * Does NOT move lifecycle folders.
9
+ */
10
+ import { type XbriefCliResult, type XbriefFormat, type XbriefStyle } from "./types.js";
11
+ export declare const VERIFY_USAGE: string;
12
+ export interface VerifyOptions {
13
+ format: XbriefFormat;
14
+ out: string;
15
+ style?: XbriefStyle;
16
+ projectRoot: string;
17
+ sizeCapBytes?: number;
18
+ cwd?: string;
19
+ home?: string;
20
+ env?: NodeJS.ProcessEnv;
21
+ }
22
+ /** Parse verify CLI argv into options (or error string). */
23
+ export declare function parseVerifyArgv(argv: readonly string[]): VerifyOptions | {
24
+ error: string;
25
+ };
26
+ /** Core verify implementation (testable). */
27
+ export declare function verifyXbrief(options: VerifyOptions): XbriefCliResult;
28
+ /** CLI entry for dispatch wrapper. */
29
+ export declare function runXbriefVerifyCli(argv: string[]): XbriefCliResult;
30
+ //# sourceMappingURL=verify.d.ts.map
@@ -0,0 +1,251 @@
1
+ /**
2
+ * xbrief:verify — fail-closed check of an xBRIEF artifact at --out (#3057).
3
+ *
4
+ * Required: --format (json|md|both), --out
5
+ * Checks: schema/parse, required fields, size cap, md sections per style,
6
+ * and stem/title/id consistency when format=both.
7
+ *
8
+ * Does NOT move lifecycle folders.
9
+ */
10
+ import { existsSync, readFileSync, statSync } from "node:fs";
11
+ import { validateVbriefSchema } from "../vbrief-validate/schema.js";
12
+ import { resolveXbriefOutPaths, XbriefPathError } from "./paths.js";
13
+ import { MD_REQUIRED_SECTIONS, parseMarkdownMeta } from "./styles.js";
14
+ import { DEFAULT_XBRIEF_SIZE_CAP_BYTES, isXbriefFormat, isXbriefStyle, } from "./types.js";
15
+ export const VERIFY_USAGE = "Usage: deft xbrief:verify -- --format <json|md|both> --out <path> [--style <scope|playbook|mission|project>] [--project-root <dir>]\n" +
16
+ " Verify a dense xBRIEF artifact at --out. Required: --format and --out.\n" +
17
+ " verify ≠ lifecycle: does not promote/activate/complete (use scope:* for lifecycle).\n";
18
+ function fail(stderr, exitCode = 1) {
19
+ return { exitCode, stdout: "", stderr };
20
+ }
21
+ function parseFlagValue(argv, i, name) {
22
+ const eq = argv[i]?.startsWith(`${name}=`) ? argv[i].slice(name.length + 1) : undefined;
23
+ if (eq !== undefined) {
24
+ if (eq.length === 0)
25
+ return { error: `argument ${name}: expected one argument\n` };
26
+ return { value: eq, next: i };
27
+ }
28
+ const next = argv[i + 1];
29
+ if (next === undefined || next.startsWith("-")) {
30
+ return { error: `argument ${name}: expected one argument\n` };
31
+ }
32
+ return { value: next, next: i + 1 };
33
+ }
34
+ /** Parse verify CLI argv into options (or error string). */
35
+ export function parseVerifyArgv(argv) {
36
+ let format;
37
+ let out;
38
+ let style;
39
+ let projectRoot = process.cwd();
40
+ for (let i = 0; i < argv.length; i += 1) {
41
+ const arg = argv[i];
42
+ if (arg === undefined)
43
+ continue;
44
+ if (arg === "-h" || arg === "--help") {
45
+ return { error: VERIFY_USAGE };
46
+ }
47
+ if (arg === "--format" || arg.startsWith("--format=")) {
48
+ const parsed = parseFlagValue(argv, i, "--format");
49
+ if ("error" in parsed)
50
+ return parsed;
51
+ format = parsed.value;
52
+ i = parsed.next;
53
+ continue;
54
+ }
55
+ if (arg === "--out" || arg.startsWith("--out=")) {
56
+ const parsed = parseFlagValue(argv, i, "--out");
57
+ if ("error" in parsed)
58
+ return parsed;
59
+ out = parsed.value;
60
+ i = parsed.next;
61
+ continue;
62
+ }
63
+ if (arg === "--style" || arg.startsWith("--style=")) {
64
+ const parsed = parseFlagValue(argv, i, "--style");
65
+ if ("error" in parsed)
66
+ return parsed;
67
+ style = parsed.value;
68
+ i = parsed.next;
69
+ continue;
70
+ }
71
+ if (arg === "--project-root" || arg.startsWith("--project-root=")) {
72
+ const parsed = parseFlagValue(argv, i, "--project-root");
73
+ if ("error" in parsed)
74
+ return parsed;
75
+ projectRoot = parsed.value;
76
+ i = parsed.next;
77
+ continue;
78
+ }
79
+ if (arg === "--")
80
+ continue;
81
+ return { error: `unrecognized argument: ${arg}\n${VERIFY_USAGE}` };
82
+ }
83
+ if (format === undefined) {
84
+ return { error: `missing required --format (json|md|both)\n${VERIFY_USAGE}` };
85
+ }
86
+ if (!isXbriefFormat(format)) {
87
+ return { error: `invalid --format ${format} (expected json|md|both)\n${VERIFY_USAGE}` };
88
+ }
89
+ if (out === undefined || out.length === 0) {
90
+ return { error: `missing required --out\n${VERIFY_USAGE}` };
91
+ }
92
+ if (style !== undefined && !isXbriefStyle(style)) {
93
+ return {
94
+ error: `invalid --style ${style} (expected scope|playbook|mission|project)\n${VERIFY_USAGE}`,
95
+ };
96
+ }
97
+ return {
98
+ format,
99
+ out,
100
+ style: style,
101
+ projectRoot,
102
+ };
103
+ }
104
+ function readText(path, sizeCap) {
105
+ if (!existsSync(path)) {
106
+ return { error: `missing file: ${path}\n` };
107
+ }
108
+ try {
109
+ const st = statSync(path);
110
+ if (!st.isFile()) {
111
+ return { error: `not a file: ${path}\n` };
112
+ }
113
+ if (st.size > sizeCap) {
114
+ return { error: `file exceeds size cap (${sizeCap} bytes): ${path}\n` };
115
+ }
116
+ return readFileSync(path, "utf8");
117
+ }
118
+ catch (err) {
119
+ const msg = err instanceof Error ? err.message : String(err);
120
+ return { error: `failed to read ${path}: ${msg}\n` };
121
+ }
122
+ }
123
+ function parseJsonDoc(text, label) {
124
+ try {
125
+ const data = JSON.parse(text);
126
+ if (typeof data !== "object" || data === null || Array.isArray(data)) {
127
+ return { ok: false, error: `${label}: JSON root must be an object\n` };
128
+ }
129
+ return { ok: true, doc: data };
130
+ }
131
+ catch (err) {
132
+ const msg = err instanceof Error ? err.message : String(err);
133
+ return { ok: false, error: `${label}: invalid JSON (${msg})\n` };
134
+ }
135
+ }
136
+ function resolveStyle(explicit, doc, mdStyle) {
137
+ if (explicit !== undefined)
138
+ return explicit;
139
+ if (doc !== null) {
140
+ const meta = doc.plan.metadata;
141
+ if (typeof meta === "object" && meta !== null && !Array.isArray(meta)) {
142
+ const kind = meta.kind;
143
+ if (typeof kind === "string" && isXbriefStyle(kind))
144
+ return kind;
145
+ }
146
+ }
147
+ if (mdStyle !== null && isXbriefStyle(mdStyle))
148
+ return mdStyle;
149
+ return "scope";
150
+ }
151
+ /** Core verify implementation (testable). */
152
+ export function verifyXbrief(options) {
153
+ let paths;
154
+ try {
155
+ paths = resolveXbriefOutPaths({
156
+ projectRoot: options.projectRoot,
157
+ out: options.out,
158
+ format: options.format,
159
+ cwd: options.cwd,
160
+ home: options.home,
161
+ env: options.env,
162
+ });
163
+ }
164
+ catch (err) {
165
+ if (err instanceof XbriefPathError) {
166
+ return fail(`${err.message}\n`, 1);
167
+ }
168
+ throw err;
169
+ }
170
+ const sizeCap = options.sizeCapBytes ?? DEFAULT_XBRIEF_SIZE_CAP_BYTES;
171
+ const errors = [];
172
+ let doc = null;
173
+ let mdMeta = null;
174
+ if (paths.jsonAbs !== null) {
175
+ const text = readText(paths.jsonAbs, sizeCap);
176
+ if (typeof text !== "string") {
177
+ errors.push(text.error.trimEnd());
178
+ }
179
+ else {
180
+ const parsed = parseJsonDoc(text, paths.jsonAbs);
181
+ if (!parsed.ok) {
182
+ errors.push(parsed.error.trimEnd());
183
+ }
184
+ else {
185
+ doc = parsed.doc;
186
+ const schemaErrors = validateVbriefSchema(doc, paths.jsonAbs);
187
+ for (const e of schemaErrors)
188
+ errors.push(e);
189
+ }
190
+ }
191
+ }
192
+ if (paths.mdAbs !== null) {
193
+ const text = readText(paths.mdAbs, sizeCap);
194
+ if (typeof text !== "string") {
195
+ errors.push(text.error.trimEnd());
196
+ }
197
+ else {
198
+ mdMeta = parseMarkdownMeta(text);
199
+ const style = resolveStyle(options.style, doc, mdMeta.style);
200
+ const required = MD_REQUIRED_SECTIONS[style];
201
+ for (const section of required) {
202
+ if (!mdMeta.sections.has(section)) {
203
+ errors.push(`${paths.mdAbs}: missing required markdown section '## ${section}' (style=${style})`);
204
+ }
205
+ }
206
+ if (mdMeta.title === null || mdMeta.title.length === 0) {
207
+ errors.push(`${paths.mdAbs}: missing title`);
208
+ }
209
+ if (mdMeta.status === null || mdMeta.status.length === 0) {
210
+ errors.push(`${paths.mdAbs}: missing status`);
211
+ }
212
+ }
213
+ }
214
+ // both: stem/title/id consistency between json + md
215
+ if (options.format === "both" && doc !== null && mdMeta !== null) {
216
+ if (mdMeta.title !== null && mdMeta.title !== doc.plan.title) {
217
+ errors.push(`title mismatch: json=${JSON.stringify(doc.plan.title)} md=${JSON.stringify(mdMeta.title)}`);
218
+ }
219
+ if (mdMeta.status !== null && mdMeta.status !== String(doc.plan.status)) {
220
+ errors.push(`status mismatch: json=${JSON.stringify(doc.plan.status)} md=${JSON.stringify(mdMeta.status)}`);
221
+ }
222
+ const jsonId = typeof doc.plan.id === "string" ? doc.plan.id : null;
223
+ if (jsonId !== null && mdMeta.id !== null && jsonId !== mdMeta.id) {
224
+ errors.push(`id mismatch: json=${JSON.stringify(jsonId)} md=${JSON.stringify(mdMeta.id)}`);
225
+ }
226
+ }
227
+ if (errors.length > 0) {
228
+ return fail(`xbrief:verify failed:\n${errors.map((e) => ` - ${e}`).join("\n")}\n`, 1);
229
+ }
230
+ const checked = [paths.jsonAbs, paths.mdAbs].filter((p) => p !== null);
231
+ const lines = [
232
+ `OK xbrief:verify format=${options.format}`,
233
+ ...checked.map((p) => ` checked ${p}`),
234
+ " note: verify is not a lifecycle move (scope:* handles promote/activate/complete)",
235
+ ];
236
+ return { exitCode: 0, stdout: `${lines.join("\n")}\n`, stderr: "" };
237
+ }
238
+ /** CLI entry for dispatch wrapper. */
239
+ export function runXbriefVerifyCli(argv) {
240
+ const parsed = parseVerifyArgv(argv);
241
+ if ("error" in parsed) {
242
+ const isHelp = parsed.error === VERIFY_USAGE;
243
+ return {
244
+ exitCode: isHelp ? 0 : 2,
245
+ stdout: isHelp ? VERIFY_USAGE : "",
246
+ stderr: isHelp ? "" : parsed.error,
247
+ };
248
+ }
249
+ return verifyXbrief(parsed);
250
+ }
251
+ //# sourceMappingURL=verify.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deftai/directive-core",
3
- "version": "0.92.0",
3
+ "version": "0.93.0",
4
4
  "description": "TypeScript engine core for the Directive framework.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -23,6 +23,10 @@
23
23
  "types": "./dist/story-ready/index.d.ts",
24
24
  "default": "./dist/story-ready/index.js"
25
25
  },
26
+ "./slash": {
27
+ "types": "./dist/slash/index.d.ts",
28
+ "default": "./dist/slash/index.js"
29
+ },
26
30
  "./branch": {
27
31
  "types": "./dist/branch/index.d.ts",
28
32
  "default": "./dist/branch/index.js"
@@ -55,6 +59,10 @@
55
59
  "types": "./dist/xbrief-migrate/index.d.ts",
56
60
  "default": "./dist/xbrief-migrate/index.js"
57
61
  },
62
+ "./xbrief": {
63
+ "types": "./dist/xbrief/index.d.ts",
64
+ "default": "./dist/xbrief/index.js"
65
+ },
58
66
  "./category-b-namespace": {
59
67
  "types": "./dist/category-b-namespace/index.d.ts",
60
68
  "default": "./dist/category-b-namespace/index.js"
@@ -334,8 +342,8 @@
334
342
  "provenance": true
335
343
  },
336
344
  "dependencies": {
337
- "@deftai/directive-content": "^0.92.0",
338
- "@deftai/directive-types": "^0.92.0",
345
+ "@deftai/directive-content": "^0.93.0",
346
+ "@deftai/directive-types": "^0.93.0",
339
347
  "archiver": "^8.0.0"
340
348
  },
341
349
  "scripts": {