cdalkit 0.0.0-stage → 0.1.4
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/LICENSE +176 -0
- package/README.md +35 -2
- package/bin/cdal.mjs +4 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +1391 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/package.json +69 -3
package/dist/cli.js
ADDED
|
@@ -0,0 +1,1391 @@
|
|
|
1
|
+
// src/cli.ts
|
|
2
|
+
import { createRequire } from "module";
|
|
3
|
+
import { runMain as cittyRunMain, defineCommand as defineCommand14 } from "citty";
|
|
4
|
+
|
|
5
|
+
// src/commands/audit.ts
|
|
6
|
+
import { writeFileSync } from "fs";
|
|
7
|
+
import { resolve } from "path";
|
|
8
|
+
import { renderHtmlReport, renderTerminalReport, runAudit } from "@cdal/audit";
|
|
9
|
+
import { defineCommand } from "citty";
|
|
10
|
+
import pc from "picocolors";
|
|
11
|
+
var auditCommand = defineCommand({
|
|
12
|
+
meta: {
|
|
13
|
+
name: "audit",
|
|
14
|
+
description: "Audit repository for AI agent hygiene, type silencing, test isolation and gates (100% offline)"
|
|
15
|
+
},
|
|
16
|
+
args: {
|
|
17
|
+
path: {
|
|
18
|
+
type: "positional",
|
|
19
|
+
description: "Path to repository root to audit",
|
|
20
|
+
required: false,
|
|
21
|
+
default: "."
|
|
22
|
+
},
|
|
23
|
+
html: {
|
|
24
|
+
type: "string",
|
|
25
|
+
description: "Path to save standalone single-file HTML report (e.g. audit-report.html)",
|
|
26
|
+
required: false
|
|
27
|
+
},
|
|
28
|
+
json: {
|
|
29
|
+
type: "boolean",
|
|
30
|
+
description: "Output machine-readable JSON report (schema: audit.v1)",
|
|
31
|
+
default: false
|
|
32
|
+
},
|
|
33
|
+
lang: {
|
|
34
|
+
type: "string",
|
|
35
|
+
description: "Report language (en or pl)",
|
|
36
|
+
default: "en"
|
|
37
|
+
},
|
|
38
|
+
failUnder: {
|
|
39
|
+
type: "string",
|
|
40
|
+
description: "Exit with code 1 if audit score is below this threshold (0-100)",
|
|
41
|
+
required: false
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
async run({ args }) {
|
|
45
|
+
const targetDir = resolve(args.path || ".");
|
|
46
|
+
const lang = args.lang === "pl" ? "pl" : "en";
|
|
47
|
+
if (!args.json) {
|
|
48
|
+
console.log(pc.bold(pc.cyan("\n\u{1F50D} Running CDAL Repository AI Hygiene Audit...\n")));
|
|
49
|
+
}
|
|
50
|
+
const report = await runAudit({
|
|
51
|
+
cwd: targetDir,
|
|
52
|
+
lang
|
|
53
|
+
});
|
|
54
|
+
if (args.html) {
|
|
55
|
+
const htmlContent = renderHtmlReport(report, lang);
|
|
56
|
+
const outPath = resolve(args.html);
|
|
57
|
+
writeFileSync(outPath, htmlContent, "utf8");
|
|
58
|
+
if (!args.json) {
|
|
59
|
+
console.log(pc.green(` \u2714 Wygenerowano raport HTML: ${outPath}`));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (args.json) {
|
|
63
|
+
console.log(JSON.stringify(report, null, 2));
|
|
64
|
+
} else {
|
|
65
|
+
console.log(renderTerminalReport(report, lang));
|
|
66
|
+
}
|
|
67
|
+
if (args.failUnder) {
|
|
68
|
+
const threshold = Number.parseInt(args.failUnder, 10);
|
|
69
|
+
if (!Number.isNaN(threshold) && report.score < threshold) {
|
|
70
|
+
console.error(
|
|
71
|
+
pc.red(
|
|
72
|
+
`
|
|
73
|
+
\u274C Audit score ${report.score} is below threshold ${threshold}. Failing quality gate.`
|
|
74
|
+
)
|
|
75
|
+
);
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// src/commands/codegen.ts
|
|
83
|
+
import { runCodegen } from "@cdal/core";
|
|
84
|
+
import { policies, requirements, stateMachine } from "@cdal/kinds";
|
|
85
|
+
import { defineCommand as defineCommand2 } from "citty";
|
|
86
|
+
import pc2 from "picocolors";
|
|
87
|
+
var codegenCommand = defineCommand2({
|
|
88
|
+
meta: {
|
|
89
|
+
name: "codegen",
|
|
90
|
+
description: "Emit TypeScript types and documentation from contracts (with --check drift detection)"
|
|
91
|
+
},
|
|
92
|
+
args: {
|
|
93
|
+
check: {
|
|
94
|
+
type: "boolean",
|
|
95
|
+
description: "Check for drift between contracts and generated files without modifying disk",
|
|
96
|
+
default: false
|
|
97
|
+
},
|
|
98
|
+
dir: {
|
|
99
|
+
type: "string",
|
|
100
|
+
description: "Custom path to contracts directory"
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
async run({ args }) {
|
|
104
|
+
try {
|
|
105
|
+
const defaultKinds = [requirements(), stateMachine(), policies()];
|
|
106
|
+
const res = await runCodegen({
|
|
107
|
+
contractsDir: args.dir,
|
|
108
|
+
check: Boolean(args.check),
|
|
109
|
+
kinds: defaultKinds
|
|
110
|
+
});
|
|
111
|
+
if (args.check) {
|
|
112
|
+
if (!res.drift) {
|
|
113
|
+
console.log(pc2.green("\u2705 Codegen check: all generated files are in sync byte-for-byte."));
|
|
114
|
+
} else {
|
|
115
|
+
console.error(pc2.red("\u274C Codegen check failed: drift detected in generated files:"));
|
|
116
|
+
for (const f of res.files) {
|
|
117
|
+
if (f.status === "drift") {
|
|
118
|
+
console.error(pc2.red(` - [DRIFT] ${f.path} (${f.fullPath})`));
|
|
119
|
+
} else if (f.status === "missing") {
|
|
120
|
+
console.error(pc2.red(` - [MISSING] ${f.path} (${f.fullPath})`));
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
console.error(
|
|
124
|
+
pc2.yellow("\nRun `cdal codegen` to synchronize generated files with contracts.")
|
|
125
|
+
);
|
|
126
|
+
process.exit(1);
|
|
127
|
+
}
|
|
128
|
+
} else {
|
|
129
|
+
console.log(pc2.green(`\u2705 Emitted ${res.files.length} contract artifact(s):`));
|
|
130
|
+
for (const f of res.files) {
|
|
131
|
+
console.log(pc2.cyan(` - ${f.path} -> ${f.fullPath}`));
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
} catch (err) {
|
|
135
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
136
|
+
console.error(pc2.red(`\u274C Fatal error during codegen: ${msg}`));
|
|
137
|
+
process.exit(1);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
// src/commands/doctor.ts
|
|
143
|
+
import { spawnSync } from "child_process";
|
|
144
|
+
import { existsSync, readFileSync } from "fs";
|
|
145
|
+
import { join } from "path";
|
|
146
|
+
import { syncAdapters } from "@cdal/adapters";
|
|
147
|
+
import { loadConfig, runCodegen as runCodegen2 } from "@cdal/core";
|
|
148
|
+
import { defineCommand as defineCommand3 } from "citty";
|
|
149
|
+
import pc3 from "picocolors";
|
|
150
|
+
function inspectClaudeHooks(settings) {
|
|
151
|
+
const groups = settings.hooks?.PreToolUse;
|
|
152
|
+
if (!Array.isArray(groups)) return "missing";
|
|
153
|
+
let legacy = false;
|
|
154
|
+
for (const group of groups) {
|
|
155
|
+
if (typeof group.command === "string" && /cdal(kit)? hook/.test(group.command)) legacy = true;
|
|
156
|
+
const handlers = Array.isArray(group.hooks) ? group.hooks : [];
|
|
157
|
+
for (const handler of handlers) {
|
|
158
|
+
if (handler.type === "command" && typeof handler.command === "string" && /cdal(kit)? hook guard-paths/.test(handler.command)) {
|
|
159
|
+
return "ok";
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return legacy ? "legacy-format" : "missing";
|
|
164
|
+
}
|
|
165
|
+
function runDoctorChecks(cwd, customConfig) {
|
|
166
|
+
const checks = [];
|
|
167
|
+
const nodeVer = process.version;
|
|
168
|
+
const match = /^v(\d+)\.(\d+)\./.exec(nodeVer);
|
|
169
|
+
const major = match?.[1] ? Number.parseInt(match[1], 10) : 0;
|
|
170
|
+
const minor = match?.[2] ? Number.parseInt(match[2], 10) : 0;
|
|
171
|
+
if (major > 20 || major === 20 && minor >= 9) {
|
|
172
|
+
checks.push({
|
|
173
|
+
name: "Node.js Runtime",
|
|
174
|
+
status: "ok",
|
|
175
|
+
message: `Node ${nodeVer} satisfies engines.node >= 20.9.0 LTS`
|
|
176
|
+
});
|
|
177
|
+
} else {
|
|
178
|
+
checks.push({
|
|
179
|
+
name: "Node.js Runtime",
|
|
180
|
+
status: "error",
|
|
181
|
+
message: `Current Node ${nodeVer} is below required >= 20.9.0`,
|
|
182
|
+
recommendation: "Upgrade Node.js to Node 22 LTS or Node 20.9+."
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
const gitVersionRes = spawnSync("git", ["--version"], { encoding: "utf8" });
|
|
186
|
+
if (gitVersionRes.status !== 0) {
|
|
187
|
+
checks.push({
|
|
188
|
+
name: "Git Binary",
|
|
189
|
+
status: "error",
|
|
190
|
+
message: "git binary is not available in PATH",
|
|
191
|
+
recommendation: "Install Git or add it to PATH."
|
|
192
|
+
});
|
|
193
|
+
} else {
|
|
194
|
+
checks.push({
|
|
195
|
+
name: "Git Binary",
|
|
196
|
+
status: "ok",
|
|
197
|
+
message: `Git is available (${gitVersionRes.stdout.trim()})`
|
|
198
|
+
});
|
|
199
|
+
const gitRepoRes = spawnSync("git", ["rev-parse", "--is-inside-work-tree"], {
|
|
200
|
+
cwd,
|
|
201
|
+
encoding: "utf8"
|
|
202
|
+
});
|
|
203
|
+
if (gitRepoRes.status !== 0) {
|
|
204
|
+
checks.push({
|
|
205
|
+
name: "Git Repository",
|
|
206
|
+
status: "error",
|
|
207
|
+
message: "Current directory is not inside a git working tree",
|
|
208
|
+
recommendation: "Initialize git with `git init`."
|
|
209
|
+
});
|
|
210
|
+
} else {
|
|
211
|
+
checks.push({
|
|
212
|
+
name: "Git Repository",
|
|
213
|
+
status: "ok",
|
|
214
|
+
message: "Inside active git working tree"
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
let configObj = null;
|
|
219
|
+
try {
|
|
220
|
+
const loaded = loadConfigSync(cwd, customConfig);
|
|
221
|
+
configObj = loaded.config;
|
|
222
|
+
checks.push({
|
|
223
|
+
name: "Configuration",
|
|
224
|
+
status: "ok",
|
|
225
|
+
message: `Configuration loaded cleanly (${loaded.filepath ? loaded.filepath : "default config"})`
|
|
226
|
+
});
|
|
227
|
+
} catch (err) {
|
|
228
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
229
|
+
checks.push({
|
|
230
|
+
name: "Configuration",
|
|
231
|
+
status: "error",
|
|
232
|
+
message: `Failed to load cdal.config.mjs: ${msg}`,
|
|
233
|
+
recommendation: "Run `cdal init` or fix configuration errors."
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
const settingsPath = join(cwd, ".claude/settings.json");
|
|
237
|
+
if (existsSync(settingsPath)) {
|
|
238
|
+
try {
|
|
239
|
+
const settings = JSON.parse(readFileSync(settingsPath, "utf8"));
|
|
240
|
+
const wiring = inspectClaudeHooks(settings);
|
|
241
|
+
if (wiring === "ok") {
|
|
242
|
+
checks.push({
|
|
243
|
+
name: "Claude Code Hooks",
|
|
244
|
+
status: "ok",
|
|
245
|
+
message: ".claude/settings.json correctly wires CDAL pre-tool hooks"
|
|
246
|
+
});
|
|
247
|
+
} else {
|
|
248
|
+
checks.push({
|
|
249
|
+
name: "Claude Code Hooks",
|
|
250
|
+
status: "warn",
|
|
251
|
+
message: wiring === "legacy-format" ? ".claude/settings.json uses a hook format Claude Code ignores, so the CDAL gates never run" : ".claude/settings.json exists but does not wire the CDAL guard-paths hook",
|
|
252
|
+
recommendation: "Run `npx cdalkit sync` to regenerate the hook wiring."
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
} catch {
|
|
256
|
+
checks.push({
|
|
257
|
+
name: "Claude Code Hooks",
|
|
258
|
+
status: "warn",
|
|
259
|
+
message: "Could not read .claude/settings.json"
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
} else {
|
|
263
|
+
checks.push({
|
|
264
|
+
name: "Claude Code Hooks",
|
|
265
|
+
status: "warn",
|
|
266
|
+
message: "No .claude/settings.json found in workspace",
|
|
267
|
+
recommendation: "Run `cdal init` or `cdal sync` to generate hook wiring."
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
if (configObj) {
|
|
271
|
+
try {
|
|
272
|
+
const syncRes = syncAdapters({ cwd, config: configObj, check: true });
|
|
273
|
+
if (syncRes.inSync) {
|
|
274
|
+
checks.push({
|
|
275
|
+
name: "Runtime Adapters Sync",
|
|
276
|
+
status: "ok",
|
|
277
|
+
message: `All ${syncRes.totalFiles} adapter file(s) are in sync without drift`
|
|
278
|
+
});
|
|
279
|
+
} else {
|
|
280
|
+
checks.push({
|
|
281
|
+
name: "Runtime Adapters Sync",
|
|
282
|
+
status: "error",
|
|
283
|
+
message: `${syncRes.driftedFiles.length} adapter file(s) drifted from configuration: ${syncRes.driftedFiles.join(", ")}`,
|
|
284
|
+
recommendation: "Run `cdal sync` to synchronize runtime files."
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
} catch (err) {
|
|
288
|
+
checks.push({
|
|
289
|
+
name: "Runtime Adapters Sync",
|
|
290
|
+
status: "warn",
|
|
291
|
+
message: `Could not check adapter sync: ${String(err)}`
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
const healthy = checks.every((c) => c.status !== "error");
|
|
296
|
+
return { checks, healthy };
|
|
297
|
+
}
|
|
298
|
+
function loadConfigSync(cwd, customConfig) {
|
|
299
|
+
const configPath = customConfig || join(cwd, "cdal.config.mjs");
|
|
300
|
+
if (existsSync(configPath)) {
|
|
301
|
+
return { config: null, filepath: configPath };
|
|
302
|
+
}
|
|
303
|
+
return { config: null, filepath: null };
|
|
304
|
+
}
|
|
305
|
+
var doctorCommand = defineCommand3({
|
|
306
|
+
meta: {
|
|
307
|
+
name: "doctor",
|
|
308
|
+
description: "Diagnose CDAL installation, environment health, hook wiring, and configuration drift"
|
|
309
|
+
},
|
|
310
|
+
args: {
|
|
311
|
+
config: {
|
|
312
|
+
type: "string",
|
|
313
|
+
description: "Path to cdal.config.mjs"
|
|
314
|
+
},
|
|
315
|
+
cwd: {
|
|
316
|
+
type: "string",
|
|
317
|
+
description: "Working directory path"
|
|
318
|
+
}
|
|
319
|
+
},
|
|
320
|
+
async run({ args }) {
|
|
321
|
+
const cwd = args.cwd || process.cwd();
|
|
322
|
+
console.log(pc3.bold(pc3.cyan("\n\u{1FA7A} Running CDAL Diagnostic Doctor...\n")));
|
|
323
|
+
let resolvedConfig = null;
|
|
324
|
+
try {
|
|
325
|
+
const loaded = await loadConfig(args.config || cwd);
|
|
326
|
+
resolvedConfig = loaded.config;
|
|
327
|
+
} catch (err) {
|
|
328
|
+
console.error(pc3.red(`\u26A0\uFE0F Configuration error: ${String(err)}`));
|
|
329
|
+
}
|
|
330
|
+
const { checks, healthy } = runDoctorChecks(cwd, args.config);
|
|
331
|
+
if (resolvedConfig) {
|
|
332
|
+
try {
|
|
333
|
+
const codegenRes = await runCodegen2({
|
|
334
|
+
contractsDir: resolvedConfig.contractsDir,
|
|
335
|
+
kinds: resolvedConfig.kinds,
|
|
336
|
+
check: true
|
|
337
|
+
});
|
|
338
|
+
if (!codegenRes.drift) {
|
|
339
|
+
checks.push({
|
|
340
|
+
name: "Contract Codegen Sync",
|
|
341
|
+
status: "ok",
|
|
342
|
+
message: "Generated contract artifacts match contracts byte-for-byte"
|
|
343
|
+
});
|
|
344
|
+
} else {
|
|
345
|
+
checks.push({
|
|
346
|
+
name: "Contract Codegen Sync",
|
|
347
|
+
status: "error",
|
|
348
|
+
message: "Generated contract code drifted from contract sources",
|
|
349
|
+
recommendation: "Run `cdal codegen` to regenerate artifacts."
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
} catch {
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
for (const check of checks) {
|
|
356
|
+
if (check.status === "ok") {
|
|
357
|
+
console.log(pc3.green(` \u2713 [OK] ${pc3.bold(check.name)}: ${check.message}`));
|
|
358
|
+
} else if (check.status === "warn") {
|
|
359
|
+
console.log(pc3.yellow(` \u26A0\uFE0F [WARN] ${pc3.bold(check.name)}: ${check.message}`));
|
|
360
|
+
if (check.recommendation) {
|
|
361
|
+
console.log(pc3.dim(` Recommendation: ${check.recommendation}`));
|
|
362
|
+
}
|
|
363
|
+
} else {
|
|
364
|
+
console.log(pc3.red(` \u274C [FAIL] ${pc3.bold(check.name)}: ${check.message}`));
|
|
365
|
+
if (check.recommendation) {
|
|
366
|
+
console.log(pc3.yellow(` Recommendation: ${check.recommendation}`));
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
if (healthy) {
|
|
371
|
+
console.log(
|
|
372
|
+
pc3.bold(pc3.green("\n\u2705 Doctor report: Environment and CDAL quality gates are healthy!\n"))
|
|
373
|
+
);
|
|
374
|
+
} else {
|
|
375
|
+
console.log(pc3.bold(pc3.red("\n\u274C Doctor report: Some critical diagnostic checks failed.\n")));
|
|
376
|
+
process.exit(1);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
// src/commands/hook.ts
|
|
382
|
+
import fs from "fs";
|
|
383
|
+
import { executeHook } from "@cdal/adapters";
|
|
384
|
+
import { loadConfig as loadConfig2 } from "@cdal/core";
|
|
385
|
+
import { defineCommand as defineCommand4 } from "citty";
|
|
386
|
+
var VALID_HOOKS = /* @__PURE__ */ new Set([
|
|
387
|
+
"guard-paths",
|
|
388
|
+
"guard-forbidden",
|
|
389
|
+
"guard-bash",
|
|
390
|
+
"session-context",
|
|
391
|
+
"log-subagent"
|
|
392
|
+
]);
|
|
393
|
+
var hookCommand = defineCommand4({
|
|
394
|
+
meta: {
|
|
395
|
+
name: "hook",
|
|
396
|
+
description: "Entrypoint for agent runtime hooks (Claude Code, Antigravity, etc.)"
|
|
397
|
+
},
|
|
398
|
+
args: {
|
|
399
|
+
name: {
|
|
400
|
+
type: "positional",
|
|
401
|
+
description: "Hook name to execute: guard-paths, guard-forbidden, guard-bash, session-context, log-subagent",
|
|
402
|
+
required: true
|
|
403
|
+
},
|
|
404
|
+
role: {
|
|
405
|
+
type: "positional",
|
|
406
|
+
description: "Fallback agent role name if not specified in hook payload",
|
|
407
|
+
required: false
|
|
408
|
+
},
|
|
409
|
+
runtime: {
|
|
410
|
+
type: "string",
|
|
411
|
+
description: "Target runtime adapter (claude-code, antigravity)",
|
|
412
|
+
default: "claude-code"
|
|
413
|
+
},
|
|
414
|
+
config: {
|
|
415
|
+
type: "string",
|
|
416
|
+
description: "Path to cdal.config.mjs"
|
|
417
|
+
}
|
|
418
|
+
},
|
|
419
|
+
async run({ args }) {
|
|
420
|
+
const hookName = args.name;
|
|
421
|
+
if (!VALID_HOOKS.has(hookName)) {
|
|
422
|
+
process.stderr.write(
|
|
423
|
+
`Unknown hook name "${hookName}". Valid hooks: ${Array.from(VALID_HOOKS).join(", ")}
|
|
424
|
+
`
|
|
425
|
+
);
|
|
426
|
+
process.exit(1);
|
|
427
|
+
}
|
|
428
|
+
let rawPayload = "";
|
|
429
|
+
try {
|
|
430
|
+
rawPayload = fs.readFileSync(0, "utf8");
|
|
431
|
+
} catch {
|
|
432
|
+
}
|
|
433
|
+
const cwd = process.cwd();
|
|
434
|
+
const { config } = await loadConfig2(args.config);
|
|
435
|
+
const result = await executeHook(hookName, rawPayload, {
|
|
436
|
+
cwd,
|
|
437
|
+
config,
|
|
438
|
+
fallbackRole: args.role,
|
|
439
|
+
runtime: args.runtime
|
|
440
|
+
});
|
|
441
|
+
if (result.stdout) {
|
|
442
|
+
process.stdout.write(`${result.stdout}
|
|
443
|
+
`);
|
|
444
|
+
}
|
|
445
|
+
if (result.stderr) {
|
|
446
|
+
process.stderr.write(result.stderr);
|
|
447
|
+
}
|
|
448
|
+
process.exit(result.exitCode);
|
|
449
|
+
}
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
// src/commands/init.ts
|
|
453
|
+
import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
454
|
+
import { join as join2 } from "path";
|
|
455
|
+
import { syncAdapters as syncAdapters2 } from "@cdal/adapters";
|
|
456
|
+
import { resolveConfig } from "@cdal/core";
|
|
457
|
+
import { defineCommand as defineCommand5 } from "citty";
|
|
458
|
+
import pc4 from "picocolors";
|
|
459
|
+
var initCommand = defineCommand5({
|
|
460
|
+
meta: {
|
|
461
|
+
name: "init",
|
|
462
|
+
description: "Initialize CDAL quality gates, starter contracts, and runtime adapters in the repository"
|
|
463
|
+
},
|
|
464
|
+
args: {
|
|
465
|
+
yes: {
|
|
466
|
+
type: "boolean",
|
|
467
|
+
description: "Accept default recommendations non-interactively",
|
|
468
|
+
alias: "y",
|
|
469
|
+
default: false
|
|
470
|
+
},
|
|
471
|
+
preset: {
|
|
472
|
+
type: "string",
|
|
473
|
+
description: "Preset configuration to apply (e.g. typescript-strict, nextjs, node-service)",
|
|
474
|
+
default: "typescript-strict"
|
|
475
|
+
},
|
|
476
|
+
runtime: {
|
|
477
|
+
type: "string",
|
|
478
|
+
description: "Comma-separated runtimes to enable (claude-code, agents-md, antigravity)",
|
|
479
|
+
default: "claude-code,agents-md"
|
|
480
|
+
},
|
|
481
|
+
cwd: {
|
|
482
|
+
type: "string",
|
|
483
|
+
description: "Target directory to initialize"
|
|
484
|
+
}
|
|
485
|
+
},
|
|
486
|
+
async run({ args }) {
|
|
487
|
+
const cwd = args.cwd || process.cwd();
|
|
488
|
+
console.log(pc4.bold(pc4.cyan("\n\u{1F680} Initializing Contract-Driven Agentic Loop (CDAL)...\n")));
|
|
489
|
+
const preset = args.preset || "typescript-strict";
|
|
490
|
+
const rawRuntimes = (args.runtime || "claude-code,agents-md").split(",").map((r) => r.trim()).filter(Boolean);
|
|
491
|
+
const contractsDir = join2(cwd, "contracts");
|
|
492
|
+
if (!existsSync2(contractsDir)) {
|
|
493
|
+
mkdirSync(contractsDir, { recursive: true });
|
|
494
|
+
}
|
|
495
|
+
const sampleContractPath = join2(contractsDir, "requirements.contract.mjs");
|
|
496
|
+
if (!existsSync2(sampleContractPath)) {
|
|
497
|
+
const sampleContractContent = `// @origin: cdal init
|
|
498
|
+
export const REQUIREMENTS = [
|
|
499
|
+
{
|
|
500
|
+
id: 'REQ-CORE-INIT-1',
|
|
501
|
+
statement: 'System must initialize workspace with contract-driven quality gates.',
|
|
502
|
+
acceptance: [
|
|
503
|
+
'cdal validate succeeds without errors',
|
|
504
|
+
'Test cases are marked with @REQ tags',
|
|
505
|
+
],
|
|
506
|
+
status: 'ACTIVE',
|
|
507
|
+
priority: 'CRITICAL',
|
|
508
|
+
},
|
|
509
|
+
];
|
|
510
|
+
`;
|
|
511
|
+
writeFileSync2(sampleContractPath, sampleContractContent, "utf8");
|
|
512
|
+
console.log(
|
|
513
|
+
pc4.green(` \u2713 Created starter contract: ${pc4.bold("contracts/requirements.contract.mjs")}`)
|
|
514
|
+
);
|
|
515
|
+
}
|
|
516
|
+
const configPath = join2(cwd, "cdal.config.mjs");
|
|
517
|
+
if (!existsSync2(configPath)) {
|
|
518
|
+
const runtimesFormatted = rawRuntimes.map((r) => `'${r}'`).join(", ");
|
|
519
|
+
const configContent = `import { defineConfig } from '@cdal/core';
|
|
520
|
+
import { policies, requirements, stateMachine } from '@cdal/kinds';
|
|
521
|
+
|
|
522
|
+
export default defineConfig({
|
|
523
|
+
contractsDir: 'contracts',
|
|
524
|
+
generated: {
|
|
525
|
+
ts: 'src/generated/contracts',
|
|
526
|
+
docs: 'docs/generated',
|
|
527
|
+
},
|
|
528
|
+
kinds: [requirements(), stateMachine(), policies()],
|
|
529
|
+
presets: ['${preset}'],
|
|
530
|
+
runtimes: [${runtimesFormatted}],
|
|
531
|
+
verify: [
|
|
532
|
+
'cdal validate --strict',
|
|
533
|
+
'cdal codegen --check',
|
|
534
|
+
'cdal trace --enforce',
|
|
535
|
+
'cdal selftest',
|
|
536
|
+
],
|
|
537
|
+
});
|
|
538
|
+
`;
|
|
539
|
+
writeFileSync2(configPath, configContent, "utf8");
|
|
540
|
+
console.log(pc4.green(` \u2713 Created configuration: ${pc4.bold("cdal.config.mjs")}`));
|
|
541
|
+
} else {
|
|
542
|
+
console.log(
|
|
543
|
+
pc4.dim(" \u2139 cdal.config.mjs already exists (preserving existing configuration).")
|
|
544
|
+
);
|
|
545
|
+
}
|
|
546
|
+
const pkgPath = join2(cwd, "package.json");
|
|
547
|
+
if (existsSync2(pkgPath)) {
|
|
548
|
+
try {
|
|
549
|
+
const pkgRaw = readFileSync2(pkgPath, "utf8");
|
|
550
|
+
const pkg = JSON.parse(pkgRaw);
|
|
551
|
+
pkg.scripts = pkg.scripts || {};
|
|
552
|
+
let updated = false;
|
|
553
|
+
if (!pkg.scripts.verify) {
|
|
554
|
+
pkg.scripts.verify = "cdal verify";
|
|
555
|
+
updated = true;
|
|
556
|
+
}
|
|
557
|
+
if (!pkg.scripts.selftest) {
|
|
558
|
+
pkg.scripts.selftest = "cdal selftest";
|
|
559
|
+
updated = true;
|
|
560
|
+
}
|
|
561
|
+
if (!pkg.scripts.trace) {
|
|
562
|
+
pkg.scripts.trace = "cdal trace";
|
|
563
|
+
updated = true;
|
|
564
|
+
}
|
|
565
|
+
if (updated) {
|
|
566
|
+
writeFileSync2(pkgPath, `${JSON.stringify(pkg, null, 2)}
|
|
567
|
+
`, "utf8");
|
|
568
|
+
console.log(
|
|
569
|
+
pc4.green(` \u2713 Added verify/selftest/trace scripts to ${pc4.bold("package.json")}`)
|
|
570
|
+
);
|
|
571
|
+
}
|
|
572
|
+
} catch {
|
|
573
|
+
console.log(pc4.yellow(" \u26A0\uFE0F Could not update package.json scripts automatically."));
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
const resolvedConfig = resolveConfig({
|
|
577
|
+
presets: [preset],
|
|
578
|
+
runtimes: rawRuntimes
|
|
579
|
+
});
|
|
580
|
+
const syncRes = syncAdapters2({
|
|
581
|
+
cwd,
|
|
582
|
+
config: resolvedConfig,
|
|
583
|
+
check: false
|
|
584
|
+
});
|
|
585
|
+
console.log(
|
|
586
|
+
pc4.green(
|
|
587
|
+
` \u2713 Generated ${syncRes.updatedFiles.length} runtime adapter file(s) across [${rawRuntimes.join(", ")}].`
|
|
588
|
+
)
|
|
589
|
+
);
|
|
590
|
+
console.log(pc4.bold(pc4.green("\n\u{1F389} CDAL initialized successfully!")));
|
|
591
|
+
console.log(pc4.cyan("Next steps:"));
|
|
592
|
+
console.log(pc4.dim(" 1. Review contracts in contracts/*.contract.mjs"));
|
|
593
|
+
console.log(pc4.dim(" 2. Run `npx cdalkit doctor` to verify environment health"));
|
|
594
|
+
console.log(pc4.dim(" 3. Run `npx cdalkit verify` to test the loop gate\n"));
|
|
595
|
+
}
|
|
596
|
+
});
|
|
597
|
+
|
|
598
|
+
// src/commands/phase.ts
|
|
599
|
+
import { checkPhase, loadConfig as loadConfig3 } from "@cdal/core";
|
|
600
|
+
import { defineCommand as defineCommand6 } from "citty";
|
|
601
|
+
import pc5 from "picocolors";
|
|
602
|
+
var phaseCommand = defineCommand6({
|
|
603
|
+
meta: {
|
|
604
|
+
name: "phase",
|
|
605
|
+
description: "Verify git diff against active loop phase (contract, red, green)"
|
|
606
|
+
},
|
|
607
|
+
args: {
|
|
608
|
+
phase: {
|
|
609
|
+
type: "positional",
|
|
610
|
+
description: "Phase to verify: contract, red, green",
|
|
611
|
+
required: true
|
|
612
|
+
},
|
|
613
|
+
base: {
|
|
614
|
+
type: "string",
|
|
615
|
+
description: "Base git reference to compare against (e.g. main or origin/main)"
|
|
616
|
+
},
|
|
617
|
+
json: {
|
|
618
|
+
type: "boolean",
|
|
619
|
+
description: "Output as JSON payload",
|
|
620
|
+
default: false
|
|
621
|
+
},
|
|
622
|
+
config: {
|
|
623
|
+
type: "string",
|
|
624
|
+
description: "Path to cdal.config.mjs"
|
|
625
|
+
}
|
|
626
|
+
},
|
|
627
|
+
async run({ args }) {
|
|
628
|
+
const rawPhase = (args.phase || "").toLowerCase();
|
|
629
|
+
if (!["contract", "red", "green"].includes(rawPhase)) {
|
|
630
|
+
console.error(pc5.red(`Invalid phase "${rawPhase}". Valid phases are: contract, red, green.`));
|
|
631
|
+
process.exit(2);
|
|
632
|
+
}
|
|
633
|
+
const phase = rawPhase;
|
|
634
|
+
const cwd = process.cwd();
|
|
635
|
+
const { config: resolvedConfig } = await loadConfig3(args.config);
|
|
636
|
+
const result = checkPhase(phase, {
|
|
637
|
+
cwd,
|
|
638
|
+
base: args.base,
|
|
639
|
+
protectedPaths: resolvedConfig?.protectedPaths
|
|
640
|
+
});
|
|
641
|
+
if (args.json) {
|
|
642
|
+
console.log(JSON.stringify(result, null, 2));
|
|
643
|
+
process.exit(result.ok ? 0 : 1);
|
|
644
|
+
}
|
|
645
|
+
console.log(pc5.bold(`
|
|
646
|
+
Phase: ${result.label}`));
|
|
647
|
+
console.log(` Modified files in diff: ${result.files.length}
|
|
648
|
+
`);
|
|
649
|
+
if (result.violations.length === 0) {
|
|
650
|
+
if (result.files.length === 0) {
|
|
651
|
+
if (phase === "red") {
|
|
652
|
+
console.log(
|
|
653
|
+
pc5.yellow(
|
|
654
|
+
" \u26A0 No changes found in working tree. Phase RED requires at least one test."
|
|
655
|
+
)
|
|
656
|
+
);
|
|
657
|
+
process.exit(1);
|
|
658
|
+
}
|
|
659
|
+
console.log(pc5.dim(" No changes found in working tree."));
|
|
660
|
+
process.exit(0);
|
|
661
|
+
}
|
|
662
|
+
console.log(pc5.green(" \u2714 All changes are within active phase scopes."));
|
|
663
|
+
process.exit(0);
|
|
664
|
+
}
|
|
665
|
+
console.log(
|
|
666
|
+
pc5.red(` \u2717 ${result.violations.length} file(s) outside of permitted phase scope:
|
|
667
|
+
`)
|
|
668
|
+
);
|
|
669
|
+
for (const v of result.violations) {
|
|
670
|
+
console.log(` - ${v}`);
|
|
671
|
+
}
|
|
672
|
+
console.log(pc5.dim(`
|
|
673
|
+
${result.why}
|
|
674
|
+
`));
|
|
675
|
+
process.exit(1);
|
|
676
|
+
}
|
|
677
|
+
});
|
|
678
|
+
|
|
679
|
+
// src/commands/pro.ts
|
|
680
|
+
import { execFileSync } from "child_process";
|
|
681
|
+
import { existsSync as existsSync4, mkdtempSync, rmSync } from "fs";
|
|
682
|
+
import { tmpdir } from "os";
|
|
683
|
+
import { join as join4, resolve as resolve3 } from "path";
|
|
684
|
+
import { defineCommand as defineCommand7 } from "citty";
|
|
685
|
+
import pc6 from "picocolors";
|
|
686
|
+
|
|
687
|
+
// src/pro/install.ts
|
|
688
|
+
import {
|
|
689
|
+
copyFileSync,
|
|
690
|
+
existsSync as existsSync3,
|
|
691
|
+
mkdirSync as mkdirSync2,
|
|
692
|
+
readFileSync as readFileSync3,
|
|
693
|
+
readdirSync,
|
|
694
|
+
statSync,
|
|
695
|
+
writeFileSync as writeFileSync3
|
|
696
|
+
} from "fs";
|
|
697
|
+
import { dirname, isAbsolute, join as join3, relative, resolve as resolve2, sep } from "path";
|
|
698
|
+
var PRO_MANIFEST_FILE = "pro.manifest.json";
|
|
699
|
+
var PRO_RECORD_FILE = ".cdal/pro.json";
|
|
700
|
+
var GENERATED_MARKERS = ["CDAL GENERATED", "GENERATED by cdal sync"];
|
|
701
|
+
function isGeneratedFile(path2) {
|
|
702
|
+
const content = readFileSync3(path2, "utf8");
|
|
703
|
+
return GENERATED_MARKERS.some((marker) => content.includes(marker));
|
|
704
|
+
}
|
|
705
|
+
var ProInstallError = class extends Error {
|
|
706
|
+
constructor(message) {
|
|
707
|
+
super(message);
|
|
708
|
+
this.name = "ProInstallError";
|
|
709
|
+
}
|
|
710
|
+
};
|
|
711
|
+
function toPosix(path2) {
|
|
712
|
+
return path2.split(sep).join("/");
|
|
713
|
+
}
|
|
714
|
+
function resolveInside(root, rel, label) {
|
|
715
|
+
if (typeof rel !== "string" || rel.length === 0 || isAbsolute(rel)) {
|
|
716
|
+
throw new ProInstallError(`Invalid ${label} path in ${PRO_MANIFEST_FILE}: "${rel}"`);
|
|
717
|
+
}
|
|
718
|
+
const full = resolve2(root, rel);
|
|
719
|
+
const fromRoot = relative(root, full);
|
|
720
|
+
if (fromRoot.startsWith("..") || isAbsolute(fromRoot)) {
|
|
721
|
+
throw new ProInstallError(`${label} path "${rel}" escapes its directory`);
|
|
722
|
+
}
|
|
723
|
+
return full;
|
|
724
|
+
}
|
|
725
|
+
function readProManifest(sourceDir) {
|
|
726
|
+
const manifestPath = join3(sourceDir, PRO_MANIFEST_FILE);
|
|
727
|
+
if (!existsSync3(manifestPath)) {
|
|
728
|
+
throw new ProInstallError(`No ${PRO_MANIFEST_FILE} found in ${sourceDir}`);
|
|
729
|
+
}
|
|
730
|
+
let manifest;
|
|
731
|
+
try {
|
|
732
|
+
manifest = JSON.parse(readFileSync3(manifestPath, "utf8"));
|
|
733
|
+
} catch (err) {
|
|
734
|
+
throw new ProInstallError(
|
|
735
|
+
`${PRO_MANIFEST_FILE} is not valid JSON: ${err instanceof Error ? err.message : String(err)}`
|
|
736
|
+
);
|
|
737
|
+
}
|
|
738
|
+
if (!manifest.name || !manifest.version || !Array.isArray(manifest.install)) {
|
|
739
|
+
throw new ProInstallError(`${PRO_MANIFEST_FILE} must define name, version and install[]`);
|
|
740
|
+
}
|
|
741
|
+
return manifest;
|
|
742
|
+
}
|
|
743
|
+
function listFiles(dir) {
|
|
744
|
+
const files = [];
|
|
745
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
746
|
+
const full = join3(dir, entry.name);
|
|
747
|
+
if (entry.isDirectory()) files.push(...listFiles(full));
|
|
748
|
+
else if (entry.isFile()) files.push(full);
|
|
749
|
+
}
|
|
750
|
+
return files;
|
|
751
|
+
}
|
|
752
|
+
function installPro(options) {
|
|
753
|
+
const sourceDir = resolve2(options.sourceDir);
|
|
754
|
+
const targetDir = resolve2(options.targetDir);
|
|
755
|
+
const manifest = readProManifest(sourceDir);
|
|
756
|
+
const plan = [];
|
|
757
|
+
for (const mapping of manifest.install) {
|
|
758
|
+
const from = resolveInside(sourceDir, mapping.from, "from");
|
|
759
|
+
const to = resolveInside(targetDir, mapping.to, "to");
|
|
760
|
+
if (!existsSync3(from)) {
|
|
761
|
+
throw new ProInstallError(`Manifest source "${mapping.from}" does not exist`);
|
|
762
|
+
}
|
|
763
|
+
const sources = statSync(from).isDirectory() ? listFiles(from) : [from];
|
|
764
|
+
for (const src of sources) {
|
|
765
|
+
const dest = statSync(from).isDirectory() ? join3(to, relative(from, src)) : to;
|
|
766
|
+
plan.push({ src, dest, rel: toPosix(relative(targetDir, dest)) });
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
const written = [];
|
|
770
|
+
const skipped = [];
|
|
771
|
+
for (const { src, dest, rel } of plan) {
|
|
772
|
+
if (existsSync3(dest) && !options.force && !isGeneratedFile(dest)) {
|
|
773
|
+
skipped.push(rel);
|
|
774
|
+
continue;
|
|
775
|
+
}
|
|
776
|
+
written.push(rel);
|
|
777
|
+
if (!options.dryRun) {
|
|
778
|
+
mkdirSync2(dirname(dest), { recursive: true });
|
|
779
|
+
copyFileSync(src, dest);
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
if (!options.dryRun) {
|
|
783
|
+
const recordPath = join3(targetDir, PRO_RECORD_FILE);
|
|
784
|
+
mkdirSync2(dirname(recordPath), { recursive: true });
|
|
785
|
+
writeFileSync3(
|
|
786
|
+
recordPath,
|
|
787
|
+
`${JSON.stringify(
|
|
788
|
+
{
|
|
789
|
+
name: manifest.name,
|
|
790
|
+
version: manifest.version,
|
|
791
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
792
|
+
files: plan.map((p) => p.rel).sort()
|
|
793
|
+
},
|
|
794
|
+
null,
|
|
795
|
+
2
|
|
796
|
+
)}
|
|
797
|
+
`,
|
|
798
|
+
"utf8"
|
|
799
|
+
);
|
|
800
|
+
}
|
|
801
|
+
return { name: manifest.name, version: manifest.version, written, skipped };
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
// src/commands/pro.ts
|
|
805
|
+
var PRO_REPOSITORY_URL = "https://github.com/aiprojectmanagersznurowski-afk/cdal-pro.git";
|
|
806
|
+
function resolveSource(from) {
|
|
807
|
+
const local = resolve3(from);
|
|
808
|
+
if (existsSync4(local)) return { dir: local, cleanup: () => {
|
|
809
|
+
} };
|
|
810
|
+
const dir = mkdtempSync(join4(tmpdir(), "cdal-pro-"));
|
|
811
|
+
const cleanup = () => rmSync(dir, { recursive: true, force: true });
|
|
812
|
+
try {
|
|
813
|
+
execFileSync("git", ["clone", "--depth", "1", "--quiet", from, dir], {
|
|
814
|
+
stdio: ["ignore", "ignore", "pipe"]
|
|
815
|
+
});
|
|
816
|
+
} catch {
|
|
817
|
+
cleanup();
|
|
818
|
+
throw new ProInstallError(
|
|
819
|
+
`Could not clone ${from}.
|
|
820
|
+
CDAL Pro is delivered as a private GitHub repository: accept the invitation sent after your purchase, make sure git can access GitHub as that account, then run this command again.`
|
|
821
|
+
);
|
|
822
|
+
}
|
|
823
|
+
return { dir, cleanup };
|
|
824
|
+
}
|
|
825
|
+
var installCommand = defineCommand7({
|
|
826
|
+
meta: {
|
|
827
|
+
name: "install",
|
|
828
|
+
description: "Install CDAL Pro content (agent roles, templates, presets) into this project"
|
|
829
|
+
},
|
|
830
|
+
args: {
|
|
831
|
+
from: {
|
|
832
|
+
type: "string",
|
|
833
|
+
description: "Local copy or git URL of the cdal-pro repository",
|
|
834
|
+
default: PRO_REPOSITORY_URL
|
|
835
|
+
},
|
|
836
|
+
cwd: {
|
|
837
|
+
type: "string",
|
|
838
|
+
description: "Project directory to install into",
|
|
839
|
+
default: "."
|
|
840
|
+
},
|
|
841
|
+
force: {
|
|
842
|
+
type: "boolean",
|
|
843
|
+
description: "Overwrite files that already exist in the project",
|
|
844
|
+
default: false
|
|
845
|
+
},
|
|
846
|
+
dryRun: {
|
|
847
|
+
type: "boolean",
|
|
848
|
+
description: "Show what would be installed without writing anything",
|
|
849
|
+
default: false
|
|
850
|
+
}
|
|
851
|
+
},
|
|
852
|
+
run({ args }) {
|
|
853
|
+
let source;
|
|
854
|
+
try {
|
|
855
|
+
source = resolveSource(args.from);
|
|
856
|
+
const result = installPro({
|
|
857
|
+
sourceDir: source.dir,
|
|
858
|
+
targetDir: resolve3(args.cwd),
|
|
859
|
+
force: args.force,
|
|
860
|
+
dryRun: args.dryRun
|
|
861
|
+
});
|
|
862
|
+
const verb = args.dryRun ? "Would install" : "Installed";
|
|
863
|
+
console.log(pc6.green(`\u2714 ${verb} ${result.name} ${result.version}`));
|
|
864
|
+
console.log(` ${result.written.length} file(s) ${args.dryRun ? "to write" : "written"}`);
|
|
865
|
+
if (result.skipped.length > 0) {
|
|
866
|
+
console.log(
|
|
867
|
+
pc6.yellow(
|
|
868
|
+
` ${result.skipped.length} existing file(s) kept \u2014 rerun with --force to overwrite:`
|
|
869
|
+
)
|
|
870
|
+
);
|
|
871
|
+
for (const file of result.skipped) console.log(pc6.dim(` ${file}`));
|
|
872
|
+
}
|
|
873
|
+
if (!args.dryRun) {
|
|
874
|
+
console.log(pc6.dim(" Run `npx cdalkit sync` to wire the new roles into your runtimes."));
|
|
875
|
+
}
|
|
876
|
+
} catch (err) {
|
|
877
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
878
|
+
console.error(pc6.red(`\u2716 ${message}`));
|
|
879
|
+
process.exit(1);
|
|
880
|
+
} finally {
|
|
881
|
+
source?.cleanup();
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
});
|
|
885
|
+
var proCommand = defineCommand7({
|
|
886
|
+
meta: {
|
|
887
|
+
name: "pro",
|
|
888
|
+
description: "CDAL Pro: install licensed agent roles, templates and presets"
|
|
889
|
+
},
|
|
890
|
+
subCommands: {
|
|
891
|
+
install: installCommand
|
|
892
|
+
}
|
|
893
|
+
});
|
|
894
|
+
|
|
895
|
+
// src/commands/selftest.ts
|
|
896
|
+
import { loadConfig as loadConfig4, runSelftest } from "@cdal/core";
|
|
897
|
+
import { defineCommand as defineCommand8 } from "citty";
|
|
898
|
+
import pc7 from "picocolors";
|
|
899
|
+
var selftestCommand = defineCommand8({
|
|
900
|
+
meta: {
|
|
901
|
+
name: "selftest",
|
|
902
|
+
description: "Run mutation testing on contract invariant rules and content guardrails"
|
|
903
|
+
},
|
|
904
|
+
args: {
|
|
905
|
+
json: {
|
|
906
|
+
type: "boolean",
|
|
907
|
+
description: "Output as JSON payload (selftest.v1)",
|
|
908
|
+
default: false
|
|
909
|
+
},
|
|
910
|
+
config: {
|
|
911
|
+
type: "string",
|
|
912
|
+
description: "Path to cdal.config.mjs"
|
|
913
|
+
}
|
|
914
|
+
},
|
|
915
|
+
async run({ args }) {
|
|
916
|
+
const cwd = process.cwd();
|
|
917
|
+
const { config: resolvedConfig } = await loadConfig4(args.config);
|
|
918
|
+
if (!args.json) {
|
|
919
|
+
console.log(pc7.bold(pc7.cyan("\n cdal selftest \u2014 mutation testing of quality gates")));
|
|
920
|
+
console.log(
|
|
921
|
+
pc7.dim(" Testing whether validator and guardrails actually fire red when violated\n")
|
|
922
|
+
);
|
|
923
|
+
}
|
|
924
|
+
const report = await runSelftest({
|
|
925
|
+
cwd,
|
|
926
|
+
config: resolvedConfig
|
|
927
|
+
});
|
|
928
|
+
if (args.json) {
|
|
929
|
+
console.log(JSON.stringify(report, null, 2));
|
|
930
|
+
process.exit(report.ok ? 0 : 1);
|
|
931
|
+
}
|
|
932
|
+
if (report.mutationResults.length > 0) {
|
|
933
|
+
console.log(
|
|
934
|
+
pc7.bold(
|
|
935
|
+
` Contract Invariant Mutations (${report.mutationsPassed}/${report.mutationsTotal}):`
|
|
936
|
+
)
|
|
937
|
+
);
|
|
938
|
+
for (const m of report.mutationResults) {
|
|
939
|
+
if (m.passed) {
|
|
940
|
+
console.log(
|
|
941
|
+
` ${pc7.green("\u2714")} ${pc7.cyan(m.rule.padEnd(26))} detected in ${pc7.dim(m.file)}`
|
|
942
|
+
);
|
|
943
|
+
} else {
|
|
944
|
+
console.log(
|
|
945
|
+
` ${pc7.red("\u2717")} ${pc7.red(m.rule.padEnd(26))} ${pc7.bold("MISSED")}: ${m.error ?? "rule dead"}`
|
|
946
|
+
);
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
console.log("");
|
|
950
|
+
}
|
|
951
|
+
if (report.probeResults.length > 0) {
|
|
952
|
+
console.log(
|
|
953
|
+
pc7.bold(` Forbidden Content Probes (${report.probesPassed}/${report.probesTotal}):`)
|
|
954
|
+
);
|
|
955
|
+
for (const p of report.probeResults) {
|
|
956
|
+
const action = p.fires ? "detected" : "allowed";
|
|
957
|
+
if (p.passed) {
|
|
958
|
+
console.log(
|
|
959
|
+
` ${pc7.green("\u2714")} ${pc7.cyan(p.rule.padEnd(30))} ${pc7.green(action)}: ${p.desc}`
|
|
960
|
+
);
|
|
961
|
+
} else {
|
|
962
|
+
console.log(
|
|
963
|
+
` ${pc7.red("\u2717")} ${pc7.red(p.rule.padEnd(30))} ${pc7.bold("FAILED")}: ${p.error ?? "unexpected behavior"}`
|
|
964
|
+
);
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
console.log("");
|
|
968
|
+
}
|
|
969
|
+
console.log(
|
|
970
|
+
pc7.bold(
|
|
971
|
+
` Summary: ${report.mutationsPassed}/${report.mutationsTotal} contract rules verified via mutation.`
|
|
972
|
+
)
|
|
973
|
+
);
|
|
974
|
+
console.log(
|
|
975
|
+
pc7.bold(
|
|
976
|
+
` ${report.probesPassed}/${report.probesTotal} content guardrail probes verified.
|
|
977
|
+
`
|
|
978
|
+
)
|
|
979
|
+
);
|
|
980
|
+
if (report.failures.length > 0) {
|
|
981
|
+
console.log(pc7.bold(pc7.red(" CRITICAL GATE FAILURES:")));
|
|
982
|
+
for (const f of report.failures) {
|
|
983
|
+
console.log(` - ${pc7.red(f)}`);
|
|
984
|
+
}
|
|
985
|
+
console.log("");
|
|
986
|
+
process.exit(1);
|
|
987
|
+
}
|
|
988
|
+
console.log(pc7.green(pc7.bold(" \u2714 Every quality gate rule is verified active and firing.\n")));
|
|
989
|
+
process.exit(0);
|
|
990
|
+
}
|
|
991
|
+
});
|
|
992
|
+
|
|
993
|
+
// src/commands/sync.ts
|
|
994
|
+
import { syncAdapters as syncAdapters3 } from "@cdal/adapters";
|
|
995
|
+
import { loadConfig as loadConfig5 } from "@cdal/core";
|
|
996
|
+
import { defineCommand as defineCommand9 } from "citty";
|
|
997
|
+
import pc8 from "picocolors";
|
|
998
|
+
var syncCommand = defineCommand9({
|
|
999
|
+
meta: {
|
|
1000
|
+
name: "sync",
|
|
1001
|
+
description: "Synchronize agent definitions, commands, hooks and rules across configured runtimes (with --check drift detection)"
|
|
1002
|
+
},
|
|
1003
|
+
args: {
|
|
1004
|
+
check: {
|
|
1005
|
+
type: "boolean",
|
|
1006
|
+
description: "Check for drift between cdal.config.mjs and runtime files without modifying disk",
|
|
1007
|
+
default: false
|
|
1008
|
+
},
|
|
1009
|
+
config: {
|
|
1010
|
+
type: "string",
|
|
1011
|
+
description: "Custom path to cdal.config.mjs"
|
|
1012
|
+
},
|
|
1013
|
+
cwd: {
|
|
1014
|
+
type: "string",
|
|
1015
|
+
description: "Working directory path"
|
|
1016
|
+
}
|
|
1017
|
+
},
|
|
1018
|
+
async run({ args }) {
|
|
1019
|
+
try {
|
|
1020
|
+
const cwd = args.cwd || process.cwd();
|
|
1021
|
+
const { config } = await loadConfig5(args.config || cwd);
|
|
1022
|
+
const res = syncAdapters3({
|
|
1023
|
+
cwd,
|
|
1024
|
+
config,
|
|
1025
|
+
check: Boolean(args.check)
|
|
1026
|
+
});
|
|
1027
|
+
if (args.check) {
|
|
1028
|
+
if (res.inSync) {
|
|
1029
|
+
console.log(
|
|
1030
|
+
pc8.green(
|
|
1031
|
+
`\u2705 Sync check passed: all ${res.totalFiles} adapter file(s) are in sync across [${config.runtimes.join(", ")}].`
|
|
1032
|
+
)
|
|
1033
|
+
);
|
|
1034
|
+
} else {
|
|
1035
|
+
console.error(
|
|
1036
|
+
pc8.red(
|
|
1037
|
+
`\u274C Sync check failed: ${res.driftedFiles.length} adapter file(s) drifted from configuration:`
|
|
1038
|
+
)
|
|
1039
|
+
);
|
|
1040
|
+
for (const file of res.driftedFiles) {
|
|
1041
|
+
console.error(pc8.red(` - [DRIFT] ${file}`));
|
|
1042
|
+
}
|
|
1043
|
+
console.error(
|
|
1044
|
+
pc8.yellow(
|
|
1045
|
+
"\nRun `npx cdalkit sync` to synchronize runtime files with cdal.config.mjs."
|
|
1046
|
+
)
|
|
1047
|
+
);
|
|
1048
|
+
process.exit(1);
|
|
1049
|
+
}
|
|
1050
|
+
} else {
|
|
1051
|
+
console.log(
|
|
1052
|
+
pc8.green(
|
|
1053
|
+
`\u2705 Synchronized ${res.updatedFiles.length} adapter file(s) across [${config.runtimes.join(", ")}]:`
|
|
1054
|
+
)
|
|
1055
|
+
);
|
|
1056
|
+
for (const file of res.updatedFiles) {
|
|
1057
|
+
console.log(pc8.cyan(` - ${file}`));
|
|
1058
|
+
}
|
|
1059
|
+
if (res.updatedFiles.length === 0) {
|
|
1060
|
+
console.log(pc8.dim(" (All files were already up-to-date)"));
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
if (res.preservedFiles.length > 0) {
|
|
1064
|
+
console.log(
|
|
1065
|
+
pc8.dim(
|
|
1066
|
+
` ${res.preservedFiles.length} file(s) marked cdal:preserve were left untouched.`
|
|
1067
|
+
)
|
|
1068
|
+
);
|
|
1069
|
+
}
|
|
1070
|
+
} catch (err) {
|
|
1071
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1072
|
+
console.error(pc8.red(`\u274C Fatal error during sync: ${msg}`));
|
|
1073
|
+
process.exit(1);
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
});
|
|
1077
|
+
|
|
1078
|
+
// src/commands/trace.ts
|
|
1079
|
+
import path from "path";
|
|
1080
|
+
import { pathToFileURL } from "url";
|
|
1081
|
+
import {
|
|
1082
|
+
formatTraceMarkdown,
|
|
1083
|
+
formatTraceText,
|
|
1084
|
+
loadConfig as loadConfig6,
|
|
1085
|
+
scanTrace
|
|
1086
|
+
} from "@cdal/core";
|
|
1087
|
+
import { defineCommand as defineCommand10 } from "citty";
|
|
1088
|
+
import pc9 from "picocolors";
|
|
1089
|
+
var traceCommand = defineCommand10({
|
|
1090
|
+
meta: {
|
|
1091
|
+
name: "trace",
|
|
1092
|
+
description: "Inspect requirement-to-test traceability matrix (@REQ tags)"
|
|
1093
|
+
},
|
|
1094
|
+
args: {
|
|
1095
|
+
enforce: {
|
|
1096
|
+
type: "boolean",
|
|
1097
|
+
description: "Exit with code 1 if any implementing/done requirement lacks tests, tests fail, or unknown @REQ tags exist",
|
|
1098
|
+
default: false
|
|
1099
|
+
},
|
|
1100
|
+
markdown: {
|
|
1101
|
+
type: "boolean",
|
|
1102
|
+
description: "Output as Markdown table ready for pull request description",
|
|
1103
|
+
default: false
|
|
1104
|
+
},
|
|
1105
|
+
json: {
|
|
1106
|
+
type: "boolean",
|
|
1107
|
+
description: "Output as structured JSON (trace.v1)",
|
|
1108
|
+
default: false
|
|
1109
|
+
},
|
|
1110
|
+
results: {
|
|
1111
|
+
type: "string",
|
|
1112
|
+
description: "Path to test execution JSON results file (vitest or playwright json reporter)"
|
|
1113
|
+
},
|
|
1114
|
+
config: {
|
|
1115
|
+
type: "string",
|
|
1116
|
+
description: "Path to cdal.config.mjs"
|
|
1117
|
+
}
|
|
1118
|
+
},
|
|
1119
|
+
async run({ args }) {
|
|
1120
|
+
const cwd = process.cwd();
|
|
1121
|
+
const { config: resolvedConfig } = await loadConfig6(args.config);
|
|
1122
|
+
const contractsDir = resolvedConfig?.contractsDir ?? "contracts";
|
|
1123
|
+
const reqContractPath = path.resolve(cwd, contractsDir, "requirements.contract.mjs");
|
|
1124
|
+
let requirements3 = [];
|
|
1125
|
+
try {
|
|
1126
|
+
const imported = await import(pathToFileURL(reqContractPath).href);
|
|
1127
|
+
requirements3 = imported.REQUIREMENTS || imported.default?.REQUIREMENTS || imported.default || [];
|
|
1128
|
+
} catch (err) {
|
|
1129
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
1130
|
+
console.error(pc9.red(`Failed to load requirements from "${reqContractPath}": ${errorMsg}`));
|
|
1131
|
+
process.exit(1);
|
|
1132
|
+
}
|
|
1133
|
+
if (!Array.isArray(requirements3) || requirements3.length === 0) {
|
|
1134
|
+
console.warn(pc9.yellow(`No requirements found in "${reqContractPath}".`));
|
|
1135
|
+
if (args.enforce) process.exit(1);
|
|
1136
|
+
return;
|
|
1137
|
+
}
|
|
1138
|
+
const report = scanTrace(requirements3, {
|
|
1139
|
+
cwd,
|
|
1140
|
+
resultsFile: args.results
|
|
1141
|
+
});
|
|
1142
|
+
if (args.json) {
|
|
1143
|
+
console.log(JSON.stringify(report, null, 2));
|
|
1144
|
+
} else if (args.markdown) {
|
|
1145
|
+
console.log(formatTraceMarkdown(report));
|
|
1146
|
+
} else {
|
|
1147
|
+
console.log(formatTraceText(report));
|
|
1148
|
+
}
|
|
1149
|
+
if (args.enforce && !report.ok) {
|
|
1150
|
+
process.exit(1);
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
});
|
|
1154
|
+
|
|
1155
|
+
// src/commands/validate.ts
|
|
1156
|
+
import { formatFindingsText, runValidation } from "@cdal/core";
|
|
1157
|
+
import { policies as policies2, requirements as requirements2, stateMachine as stateMachine2 } from "@cdal/kinds";
|
|
1158
|
+
import { defineCommand as defineCommand11 } from "citty";
|
|
1159
|
+
import pc10 from "picocolors";
|
|
1160
|
+
var validateCommand = defineCommand11({
|
|
1161
|
+
meta: {
|
|
1162
|
+
name: "validate",
|
|
1163
|
+
description: "Validate contract invariants across all registered contract kinds"
|
|
1164
|
+
},
|
|
1165
|
+
args: {
|
|
1166
|
+
strict: {
|
|
1167
|
+
type: "boolean",
|
|
1168
|
+
description: "Treat warnings as failures (exit 1)",
|
|
1169
|
+
default: false
|
|
1170
|
+
},
|
|
1171
|
+
json: {
|
|
1172
|
+
type: "boolean",
|
|
1173
|
+
description: "Output findings using machine-readable findings.v1 JSON schema",
|
|
1174
|
+
default: false
|
|
1175
|
+
},
|
|
1176
|
+
dir: {
|
|
1177
|
+
type: "string",
|
|
1178
|
+
description: "Custom path to contracts directory"
|
|
1179
|
+
}
|
|
1180
|
+
},
|
|
1181
|
+
async run({ args }) {
|
|
1182
|
+
try {
|
|
1183
|
+
const defaultKinds = [requirements2(), stateMachine2(), policies2()];
|
|
1184
|
+
const report = await runValidation({
|
|
1185
|
+
contractsDir: args.dir,
|
|
1186
|
+
strict: Boolean(args.strict),
|
|
1187
|
+
kinds: defaultKinds
|
|
1188
|
+
});
|
|
1189
|
+
if (args.json) {
|
|
1190
|
+
console.log(JSON.stringify(report, null, 2));
|
|
1191
|
+
} else {
|
|
1192
|
+
const text = formatFindingsText(report, Boolean(args.strict));
|
|
1193
|
+
if (report.valid) {
|
|
1194
|
+
console.log(pc10.green(text));
|
|
1195
|
+
} else {
|
|
1196
|
+
console.error(pc10.red(text));
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
if (!report.valid) {
|
|
1200
|
+
process.exit(1);
|
|
1201
|
+
}
|
|
1202
|
+
} catch (err) {
|
|
1203
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1204
|
+
console.error(pc10.red(`\u274C Fatal error during contract validation: ${msg}`));
|
|
1205
|
+
process.exit(1);
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
});
|
|
1209
|
+
|
|
1210
|
+
// src/commands/verify.ts
|
|
1211
|
+
import { spawnSync as spawnSync2 } from "child_process";
|
|
1212
|
+
import { loadConfig as loadConfig7 } from "@cdal/core";
|
|
1213
|
+
import { defineCommand as defineCommand12 } from "citty";
|
|
1214
|
+
import pc11 from "picocolors";
|
|
1215
|
+
var verifyCommand = defineCommand12({
|
|
1216
|
+
meta: {
|
|
1217
|
+
name: "verify",
|
|
1218
|
+
description: "Run deterministic CDAL verification pipeline defined in configuration"
|
|
1219
|
+
},
|
|
1220
|
+
args: {
|
|
1221
|
+
fast: {
|
|
1222
|
+
type: "boolean",
|
|
1223
|
+
description: "Run fast subset of verification steps (contract checks & selftest)",
|
|
1224
|
+
default: false
|
|
1225
|
+
},
|
|
1226
|
+
full: {
|
|
1227
|
+
type: "boolean",
|
|
1228
|
+
description: "Force execution of all verification steps",
|
|
1229
|
+
default: false
|
|
1230
|
+
},
|
|
1231
|
+
clean: {
|
|
1232
|
+
type: "boolean",
|
|
1233
|
+
description: "Clear temporary caches before running verification",
|
|
1234
|
+
default: false
|
|
1235
|
+
},
|
|
1236
|
+
config: {
|
|
1237
|
+
type: "string",
|
|
1238
|
+
description: "Path to cdal.config.mjs"
|
|
1239
|
+
},
|
|
1240
|
+
cwd: {
|
|
1241
|
+
type: "string",
|
|
1242
|
+
description: "Working directory path"
|
|
1243
|
+
}
|
|
1244
|
+
},
|
|
1245
|
+
async run({ args }) {
|
|
1246
|
+
const cwd = args.cwd || process.cwd();
|
|
1247
|
+
const { config } = await loadConfig7(args.config || cwd);
|
|
1248
|
+
let steps = [...config.verify];
|
|
1249
|
+
if (args.fast) {
|
|
1250
|
+
steps = steps.filter(
|
|
1251
|
+
(s) => s.includes("validate") || s.includes("codegen") || s.includes("selftest") || s.includes("typecheck")
|
|
1252
|
+
);
|
|
1253
|
+
if (steps.length === 0) {
|
|
1254
|
+
steps = [config.verify[0] ?? "cdal validate --strict"];
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
console.log(
|
|
1258
|
+
pc11.bold(pc11.cyan(`
|
|
1259
|
+
\u26A1 Running CDAL Verification Pipeline (${steps.length} step(s))...
|
|
1260
|
+
`))
|
|
1261
|
+
);
|
|
1262
|
+
for (let i = 0; i < steps.length; i++) {
|
|
1263
|
+
const step = steps[i];
|
|
1264
|
+
if (!step) continue;
|
|
1265
|
+
console.log(pc11.bold(pc11.blue(`[${i + 1}/${steps.length}] \u25B6 ${step}`)));
|
|
1266
|
+
const start = Date.now();
|
|
1267
|
+
const res = spawnSync2(step, {
|
|
1268
|
+
cwd,
|
|
1269
|
+
shell: true,
|
|
1270
|
+
stdio: "inherit"
|
|
1271
|
+
});
|
|
1272
|
+
const duration = Date.now() - start;
|
|
1273
|
+
if (res.status !== 0) {
|
|
1274
|
+
console.error(
|
|
1275
|
+
pc11.bold(
|
|
1276
|
+
pc11.red(
|
|
1277
|
+
`
|
|
1278
|
+
\u274C Verification failed at step: "${step}" (exit code ${res.status ?? 1}, took ${duration}ms)`
|
|
1279
|
+
)
|
|
1280
|
+
)
|
|
1281
|
+
);
|
|
1282
|
+
process.exit(res.status ?? 1);
|
|
1283
|
+
}
|
|
1284
|
+
console.log(pc11.green(` \u2713 Step passed in ${duration}ms
|
|
1285
|
+
`));
|
|
1286
|
+
}
|
|
1287
|
+
console.log(
|
|
1288
|
+
pc11.bold(
|
|
1289
|
+
pc11.green(`\u2705 Full verification pipeline succeeded (${steps.length} step(s) passed).`)
|
|
1290
|
+
)
|
|
1291
|
+
);
|
|
1292
|
+
}
|
|
1293
|
+
});
|
|
1294
|
+
|
|
1295
|
+
// src/commands/window.ts
|
|
1296
|
+
import { closeContractWindow, getContractWindowStatus, openContractWindow } from "@cdal/core";
|
|
1297
|
+
import { defineCommand as defineCommand13 } from "citty";
|
|
1298
|
+
import pc12 from "picocolors";
|
|
1299
|
+
var windowCommand = defineCommand13({
|
|
1300
|
+
meta: {
|
|
1301
|
+
name: "window",
|
|
1302
|
+
description: "Manage contract window for protected path modifications"
|
|
1303
|
+
},
|
|
1304
|
+
args: {
|
|
1305
|
+
action: {
|
|
1306
|
+
type: "positional",
|
|
1307
|
+
description: "Action to perform: open, close, status",
|
|
1308
|
+
required: true
|
|
1309
|
+
},
|
|
1310
|
+
ticket: {
|
|
1311
|
+
type: "positional",
|
|
1312
|
+
description: "Ticket or reason ID (required for open)",
|
|
1313
|
+
required: false
|
|
1314
|
+
},
|
|
1315
|
+
duration: {
|
|
1316
|
+
type: "string",
|
|
1317
|
+
description: "Window duration in minutes (default: 60)",
|
|
1318
|
+
default: "60"
|
|
1319
|
+
}
|
|
1320
|
+
},
|
|
1321
|
+
run({ args }) {
|
|
1322
|
+
const action = args.action.toLowerCase();
|
|
1323
|
+
if (action === "open") {
|
|
1324
|
+
const ticket = args.ticket;
|
|
1325
|
+
if (!ticket) {
|
|
1326
|
+
console.error(
|
|
1327
|
+
pc12.red(
|
|
1328
|
+
'Error: "cdal window open" requires a ticket or work order argument. Example: cdal window open WO-123'
|
|
1329
|
+
)
|
|
1330
|
+
);
|
|
1331
|
+
process.exit(1);
|
|
1332
|
+
}
|
|
1333
|
+
const durationMinutes = Number.parseInt(args.duration, 10) || 60;
|
|
1334
|
+
const status = openContractWindow(ticket, { durationMinutes });
|
|
1335
|
+
console.log(pc12.green(`\u2714 Contract window opened for ticket: ${pc12.bold(status.ticket)}`));
|
|
1336
|
+
console.log(pc12.dim(` Expires at: ${status.expiresAt}`));
|
|
1337
|
+
return;
|
|
1338
|
+
}
|
|
1339
|
+
if (action === "close") {
|
|
1340
|
+
closeContractWindow();
|
|
1341
|
+
console.log(pc12.green("\u2714 Contract window closed. Protected paths are locked."));
|
|
1342
|
+
return;
|
|
1343
|
+
}
|
|
1344
|
+
if (action === "status") {
|
|
1345
|
+
const status = getContractWindowStatus();
|
|
1346
|
+
if (status.open) {
|
|
1347
|
+
console.log(pc12.green(`Contract window is ${pc12.bold("OPEN")}`));
|
|
1348
|
+
console.log(` Ticket: ${status.ticket ?? "none"}`);
|
|
1349
|
+
console.log(` Opened at: ${status.openedAt ?? "unknown"}`);
|
|
1350
|
+
console.log(` Expires at: ${status.expiresAt ?? "unknown"}`);
|
|
1351
|
+
} else {
|
|
1352
|
+
console.log(pc12.yellow(`Contract window is ${pc12.bold("CLOSED")}`));
|
|
1353
|
+
}
|
|
1354
|
+
return;
|
|
1355
|
+
}
|
|
1356
|
+
console.error(pc12.red(`Unknown action "${action}". Available actions: open, close, status`));
|
|
1357
|
+
process.exit(1);
|
|
1358
|
+
}
|
|
1359
|
+
});
|
|
1360
|
+
|
|
1361
|
+
// src/cli.ts
|
|
1362
|
+
var { version } = createRequire(import.meta.url)("../package.json");
|
|
1363
|
+
var main = defineCommand14({
|
|
1364
|
+
meta: {
|
|
1365
|
+
name: "cdal",
|
|
1366
|
+
version,
|
|
1367
|
+
description: "Contract-Driven Agentic Loop (CDAL) Quality Gates & Audit CLI"
|
|
1368
|
+
},
|
|
1369
|
+
subCommands: {
|
|
1370
|
+
init: initCommand,
|
|
1371
|
+
validate: validateCommand,
|
|
1372
|
+
codegen: codegenCommand,
|
|
1373
|
+
window: windowCommand,
|
|
1374
|
+
trace: traceCommand,
|
|
1375
|
+
phase: phaseCommand,
|
|
1376
|
+
selftest: selftestCommand,
|
|
1377
|
+
sync: syncCommand,
|
|
1378
|
+
verify: verifyCommand,
|
|
1379
|
+
doctor: doctorCommand,
|
|
1380
|
+
hook: hookCommand,
|
|
1381
|
+
audit: auditCommand,
|
|
1382
|
+
pro: proCommand
|
|
1383
|
+
}
|
|
1384
|
+
});
|
|
1385
|
+
function runMain() {
|
|
1386
|
+
cittyRunMain(main);
|
|
1387
|
+
}
|
|
1388
|
+
export {
|
|
1389
|
+
runMain
|
|
1390
|
+
};
|
|
1391
|
+
//# sourceMappingURL=cli.js.map
|