airgen-cli 0.2.1 → 0.3.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/commands/documents.js +1 -1
- package/dist/commands/projects.js +3 -0
- package/dist/commands/verify.d.ts +3 -0
- package/dist/commands/verify.js +325 -0
- package/dist/index.js +2 -0
- package/package.json +1 -1
|
@@ -99,7 +99,7 @@ export function registerDocumentCommands(program, client) {
|
|
|
99
99
|
projectKey,
|
|
100
100
|
documentSlug: document,
|
|
101
101
|
name: opts.title,
|
|
102
|
-
order: opts.order ? parseInt(opts.order, 10) :
|
|
102
|
+
order: opts.order ? parseInt(opts.order, 10) : 0,
|
|
103
103
|
description: opts.description,
|
|
104
104
|
shortCode: opts.code,
|
|
105
105
|
});
|
|
@@ -28,9 +28,12 @@ export function registerProjectCommands(program, client) {
|
|
|
28
28
|
.requiredOption("--name <name>", "Project name")
|
|
29
29
|
.option("--key <key>", "Project key")
|
|
30
30
|
.option("--code <code>", "Project code")
|
|
31
|
+
.option("--slug <slug>", "Project slug (auto-generated from name if omitted)")
|
|
31
32
|
.option("--description <desc>", "Description")
|
|
32
33
|
.action(async (tenant, opts) => {
|
|
34
|
+
const slug = opts.slug ?? opts.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
|
|
33
35
|
const data = await client.post(`/tenants/${tenant}/projects`, {
|
|
36
|
+
slug,
|
|
34
37
|
name: opts.name,
|
|
35
38
|
key: opts.key,
|
|
36
39
|
code: opts.code,
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
import { output, printTable, isJsonMode, truncate } from "../output.js";
|
|
2
|
+
const METHODS = ["Test", "Analysis", "Inspection", "Demonstration"];
|
|
3
|
+
const ACTIVITY_STATUSES = ["planned", "in_progress", "executed", "passed", "failed", "blocked"];
|
|
4
|
+
const EVIDENCE_TYPES = ["test_result", "analysis_report", "inspection_record", "demonstration_record"];
|
|
5
|
+
const VERDICTS = ["pass", "fail", "inconclusive", "not_applicable"];
|
|
6
|
+
const DOC_KINDS = ["test_plan", "test_procedure", "test_report", "analysis_report", "inspection_checklist", "demonstration_protocol"];
|
|
7
|
+
const DOC_STATUSES = ["draft", "review", "approved", "superseded"];
|
|
8
|
+
function severityIcon(s) {
|
|
9
|
+
switch (s) {
|
|
10
|
+
case "error": return "[!]";
|
|
11
|
+
case "warning": return "[~]";
|
|
12
|
+
case "info": return "[i]";
|
|
13
|
+
default: return "[ ]";
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
function statusIcon(s) {
|
|
17
|
+
switch (s) {
|
|
18
|
+
case "passed": return "[v]";
|
|
19
|
+
case "failed": return "[x]";
|
|
20
|
+
case "blocked": return "[!]";
|
|
21
|
+
case "executed": return "[>]";
|
|
22
|
+
case "in_progress": return "[~]";
|
|
23
|
+
case "planned": return "[ ]";
|
|
24
|
+
default: return "[-]";
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
// ── Command registration ─────────────────────────────────────
|
|
28
|
+
export function registerVerifyCommands(program, client) {
|
|
29
|
+
const cmd = program.command("verify").description("Verification management");
|
|
30
|
+
// ── Activities ──────────────────────────────────────────
|
|
31
|
+
const act = cmd.command("activities").alias("act").description("Verification activities");
|
|
32
|
+
act
|
|
33
|
+
.command("list")
|
|
34
|
+
.description("List verification activities")
|
|
35
|
+
.argument("<tenant>", "Tenant slug")
|
|
36
|
+
.argument("<project>", "Project slug")
|
|
37
|
+
.option("--status <s>", "Filter by status")
|
|
38
|
+
.option("--method <m>", "Filter by method")
|
|
39
|
+
.action(async (tenant, project, opts) => {
|
|
40
|
+
const query = {};
|
|
41
|
+
if (opts.status)
|
|
42
|
+
query.status = opts.status;
|
|
43
|
+
if (opts.method)
|
|
44
|
+
query.method = opts.method;
|
|
45
|
+
const data = await client.get(`/verification/activities/${tenant}/${project}`, query);
|
|
46
|
+
const activities = data.activities ?? [];
|
|
47
|
+
if (isJsonMode()) {
|
|
48
|
+
output(activities);
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
printTable(["ID", "Method", "Status", "Title", "Req Ref"], activities.map(a => [
|
|
52
|
+
a.activityId,
|
|
53
|
+
a.method,
|
|
54
|
+
statusIcon(a.status) + " " + a.status,
|
|
55
|
+
truncate(a.title, 40),
|
|
56
|
+
a.requirementRef ?? "",
|
|
57
|
+
]));
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
act
|
|
61
|
+
.command("create")
|
|
62
|
+
.description("Create a verification activity")
|
|
63
|
+
.argument("<tenant>", "Tenant slug")
|
|
64
|
+
.argument("<project>", "Project key")
|
|
65
|
+
.argument("<requirement-id>", "Requirement ID")
|
|
66
|
+
.requiredOption("--method <m>", `Method: ${METHODS.join(", ")}`)
|
|
67
|
+
.requiredOption("--title <t>", "Activity title")
|
|
68
|
+
.option("--description <d>", "Description")
|
|
69
|
+
.action(async (tenant, project, requirementId, opts) => {
|
|
70
|
+
if (!METHODS.includes(opts.method)) {
|
|
71
|
+
console.error(`Invalid method. Must be one of: ${METHODS.join(", ")}`);
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
const data = await client.post("/verification/activities", {
|
|
75
|
+
tenant,
|
|
76
|
+
projectKey: project,
|
|
77
|
+
requirementId,
|
|
78
|
+
method: opts.method,
|
|
79
|
+
title: opts.title,
|
|
80
|
+
description: opts.description,
|
|
81
|
+
});
|
|
82
|
+
if (isJsonMode()) {
|
|
83
|
+
output(data.activity);
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
console.log(`Activity created: ${data.activity.activityId}`);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
act
|
|
90
|
+
.command("update")
|
|
91
|
+
.description("Update a verification activity")
|
|
92
|
+
.argument("<activity-id>", "Activity ID")
|
|
93
|
+
.option("--status <s>", `Status: ${ACTIVITY_STATUSES.join(", ")}`)
|
|
94
|
+
.option("--title <t>", "Title")
|
|
95
|
+
.option("--description <d>", "Description")
|
|
96
|
+
.action(async (activityId, opts) => {
|
|
97
|
+
const data = await client.patch(`/verification/activities/${activityId}`, opts);
|
|
98
|
+
if (isJsonMode()) {
|
|
99
|
+
output(data.activity);
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
console.log(`Activity updated: ${data.activity.activityId} (${data.activity.status})`);
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
// ── Evidence ────────────────────────────────────────────
|
|
106
|
+
const ev = cmd.command("evidence").alias("ev").description("Verification evidence");
|
|
107
|
+
ev
|
|
108
|
+
.command("list")
|
|
109
|
+
.description("List verification evidence")
|
|
110
|
+
.argument("<tenant>", "Tenant slug")
|
|
111
|
+
.argument("<project>", "Project slug")
|
|
112
|
+
.option("--activity <id>", "Filter by activity ID")
|
|
113
|
+
.action(async (tenant, project, opts) => {
|
|
114
|
+
const query = {};
|
|
115
|
+
if (opts.activity)
|
|
116
|
+
query.activityId = opts.activity;
|
|
117
|
+
const data = await client.get(`/verification/evidence/${tenant}/${project}`, query);
|
|
118
|
+
const evidence = data.evidence ?? [];
|
|
119
|
+
if (isJsonMode()) {
|
|
120
|
+
output(evidence);
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
printTable(["ID", "Type", "Verdict", "Title", "Recorded By"], evidence.map(e => [
|
|
124
|
+
e.evidenceId,
|
|
125
|
+
e.type,
|
|
126
|
+
e.verdict,
|
|
127
|
+
truncate(e.title, 40),
|
|
128
|
+
e.recordedBy,
|
|
129
|
+
]));
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
ev
|
|
133
|
+
.command("add")
|
|
134
|
+
.description("Add verification evidence to an activity")
|
|
135
|
+
.argument("<tenant>", "Tenant slug")
|
|
136
|
+
.argument("<project>", "Project key")
|
|
137
|
+
.argument("<activity-id>", "Activity ID")
|
|
138
|
+
.requiredOption("--type <t>", `Type: ${EVIDENCE_TYPES.join(", ")}`)
|
|
139
|
+
.requiredOption("--title <t>", "Evidence title")
|
|
140
|
+
.requiredOption("--verdict <v>", `Verdict: ${VERDICTS.join(", ")}`)
|
|
141
|
+
.requiredOption("--recorded-by <name>", "Who recorded this evidence")
|
|
142
|
+
.option("--summary <s>", "Summary text")
|
|
143
|
+
.action(async (tenant, project, activityId, opts) => {
|
|
144
|
+
const data = await client.post("/verification/evidence", {
|
|
145
|
+
tenant,
|
|
146
|
+
projectKey: project,
|
|
147
|
+
activityId,
|
|
148
|
+
type: opts.type,
|
|
149
|
+
title: opts.title,
|
|
150
|
+
verdict: opts.verdict,
|
|
151
|
+
recordedBy: opts.recordedBy,
|
|
152
|
+
summary: opts.summary,
|
|
153
|
+
});
|
|
154
|
+
if (isJsonMode()) {
|
|
155
|
+
output(data.evidence);
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
console.log(`Evidence added: ${data.evidence.evidenceId} (${data.evidence.verdict})`);
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
// ── Verification Documents ─────────────────────────────
|
|
162
|
+
const docs = cmd.command("documents").alias("docs").description("Verification documents");
|
|
163
|
+
docs
|
|
164
|
+
.command("list")
|
|
165
|
+
.description("List verification documents")
|
|
166
|
+
.argument("<tenant>", "Tenant slug")
|
|
167
|
+
.argument("<project>", "Project slug")
|
|
168
|
+
.action(async (tenant, project) => {
|
|
169
|
+
const data = await client.get(`/verification/documents/${tenant}/${project}`);
|
|
170
|
+
const documents = data.documents ?? [];
|
|
171
|
+
if (isJsonMode()) {
|
|
172
|
+
output(documents);
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
printTable(["ID", "Name", "Kind", "Status", "Rev"], documents.map(d => [
|
|
176
|
+
d.vdocId,
|
|
177
|
+
truncate(d.name, 40),
|
|
178
|
+
d.kind,
|
|
179
|
+
d.status,
|
|
180
|
+
d.currentRevision,
|
|
181
|
+
]));
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
docs
|
|
185
|
+
.command("create")
|
|
186
|
+
.description("Create a verification document")
|
|
187
|
+
.argument("<tenant>", "Tenant slug")
|
|
188
|
+
.argument("<project>", "Project key")
|
|
189
|
+
.requiredOption("--name <n>", "Document name")
|
|
190
|
+
.requiredOption("--kind <k>", `Kind: ${DOC_KINDS.join(", ")}`)
|
|
191
|
+
.action(async (tenant, project, opts) => {
|
|
192
|
+
if (!DOC_KINDS.includes(opts.kind)) {
|
|
193
|
+
console.error(`Invalid kind. Must be one of: ${DOC_KINDS.join(", ")}`);
|
|
194
|
+
process.exit(1);
|
|
195
|
+
}
|
|
196
|
+
const data = await client.post("/verification/documents", {
|
|
197
|
+
tenant,
|
|
198
|
+
projectKey: project,
|
|
199
|
+
name: opts.name,
|
|
200
|
+
kind: opts.kind,
|
|
201
|
+
});
|
|
202
|
+
if (isJsonMode()) {
|
|
203
|
+
output(data.document);
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
console.log(`Document created: ${data.document.vdocId}`);
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
docs
|
|
210
|
+
.command("status")
|
|
211
|
+
.description("Update verification document status")
|
|
212
|
+
.argument("<vdoc-id>", "Document ID")
|
|
213
|
+
.requiredOption("--status <s>", `Status: ${DOC_STATUSES.join(", ")}`)
|
|
214
|
+
.action(async (vdocId, opts) => {
|
|
215
|
+
if (!DOC_STATUSES.includes(opts.status)) {
|
|
216
|
+
console.error(`Invalid status. Must be one of: ${DOC_STATUSES.join(", ")}`);
|
|
217
|
+
process.exit(1);
|
|
218
|
+
}
|
|
219
|
+
const data = await client.patch(`/verification/documents/${vdocId}/status`, { status: opts.status });
|
|
220
|
+
if (isJsonMode()) {
|
|
221
|
+
output(data.document);
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
console.log(`Document status updated to ${data.document.status}.`);
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
// ── Revisions ──────────────────────────────────────────
|
|
228
|
+
docs
|
|
229
|
+
.command("revisions")
|
|
230
|
+
.description("List revisions for a verification document")
|
|
231
|
+
.argument("<vdoc-id>", "Document ID")
|
|
232
|
+
.action(async (vdocId) => {
|
|
233
|
+
const data = await client.get(`/verification/documents/${vdocId}/revisions`);
|
|
234
|
+
const revisions = data.revisions ?? [];
|
|
235
|
+
if (isJsonMode()) {
|
|
236
|
+
output(revisions);
|
|
237
|
+
}
|
|
238
|
+
else {
|
|
239
|
+
printTable(["Rev ID", "Number", "Change", "By", "Date"], revisions.map(r => [
|
|
240
|
+
r.revisionId,
|
|
241
|
+
r.revisionNumber,
|
|
242
|
+
truncate(r.changeDescription, 40),
|
|
243
|
+
r.createdBy,
|
|
244
|
+
r.createdAt.split("T")[0],
|
|
245
|
+
]));
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
docs
|
|
249
|
+
.command("revise")
|
|
250
|
+
.description("Create a new revision of a verification document")
|
|
251
|
+
.argument("<vdoc-id>", "Document ID")
|
|
252
|
+
.requiredOption("--rev <n>", "Revision number (e.g. 0.2, 1.0)")
|
|
253
|
+
.requiredOption("--change <desc>", "Change description")
|
|
254
|
+
.requiredOption("--by <name>", "Created by")
|
|
255
|
+
.action(async (vdocId, opts) => {
|
|
256
|
+
const data = await client.post(`/verification/documents/${vdocId}/revisions`, {
|
|
257
|
+
revisionNumber: opts.rev,
|
|
258
|
+
changeDescription: opts.change,
|
|
259
|
+
createdBy: opts.by,
|
|
260
|
+
});
|
|
261
|
+
if (isJsonMode()) {
|
|
262
|
+
output(data.revision);
|
|
263
|
+
}
|
|
264
|
+
else {
|
|
265
|
+
console.log(`Revision created: ${data.revision.revisionId} (${data.revision.revisionNumber})`);
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
// ── Engine ─────────────────────────────────────────────
|
|
269
|
+
cmd
|
|
270
|
+
.command("run")
|
|
271
|
+
.description("Run the verification engine — check for gaps, conflicts, and drift")
|
|
272
|
+
.argument("<tenant>", "Tenant slug")
|
|
273
|
+
.argument("<project>", "Project slug")
|
|
274
|
+
.action(async (tenant, project) => {
|
|
275
|
+
const data = await client.get(`/verification/engine/${tenant}/${project}`);
|
|
276
|
+
const report = data.report;
|
|
277
|
+
if (isJsonMode()) {
|
|
278
|
+
output(report);
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
const s = report.summary;
|
|
282
|
+
console.log(`Verification Report\n`);
|
|
283
|
+
console.log(`Coverage: ${s.coveragePercent}% (${s.verified}/${s.totalRequirements} verified)`);
|
|
284
|
+
console.log(`Unverified: ${s.unverified} | Incomplete: ${s.incomplete} | Drifted: ${s.driftedEvidence}\n`);
|
|
285
|
+
if (report.findings.length === 0) {
|
|
286
|
+
console.log("No findings. All clear.");
|
|
287
|
+
}
|
|
288
|
+
else {
|
|
289
|
+
printTable(["Severity", "Type", "Req", "Message"], report.findings.map(f => [
|
|
290
|
+
severityIcon(f.severity) + " " + f.severity,
|
|
291
|
+
f.type,
|
|
292
|
+
f.requirementRef ?? "",
|
|
293
|
+
truncate(f.message, 60),
|
|
294
|
+
]));
|
|
295
|
+
console.log(`\n${report.findings.length} finding(s) total.`);
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
// ── Matrix ─────────────────────────────────────────────
|
|
299
|
+
cmd
|
|
300
|
+
.command("matrix")
|
|
301
|
+
.description("Show the verification cross-reference matrix")
|
|
302
|
+
.argument("<tenant>", "Tenant slug")
|
|
303
|
+
.argument("<project>", "Project slug")
|
|
304
|
+
.action(async (tenant, project) => {
|
|
305
|
+
const data = await client.get(`/verification/matrix/${tenant}/${project}`);
|
|
306
|
+
const matrix = data.matrix ?? [];
|
|
307
|
+
if (isJsonMode()) {
|
|
308
|
+
output(matrix);
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
if (matrix.length === 0) {
|
|
312
|
+
console.log("No requirements found.");
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
console.log("Verification Matrix\n");
|
|
316
|
+
for (const row of matrix) {
|
|
317
|
+
const actSummary = row.activities.length === 0
|
|
318
|
+
? " (no activities)"
|
|
319
|
+
: row.activities.map(a => ` ${statusIcon(a.status)} ${a.method}: ${a.title} (${a.evidenceCount} evidence${a.hasPassingEvidence ? ", PASS" : ""})`).join("\n");
|
|
320
|
+
console.log(`${row.requirementRef} ${truncate(row.requirementText, 60)}`);
|
|
321
|
+
console.log(actSummary);
|
|
322
|
+
console.log();
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -21,6 +21,7 @@ import { registerActivityCommands } from "./commands/activity.js";
|
|
|
21
21
|
import { registerImplementationCommands } from "./commands/implementation.js";
|
|
22
22
|
import { registerLintCommands } from "./commands/lint.js";
|
|
23
23
|
import { registerDiffCommand } from "./commands/diff.js";
|
|
24
|
+
import { registerVerifyCommands } from "./commands/verify.js";
|
|
24
25
|
const program = new Command();
|
|
25
26
|
// Lazy-init: only create client when a command actually runs
|
|
26
27
|
let client = null;
|
|
@@ -71,6 +72,7 @@ registerActivityCommands(program, clientProxy);
|
|
|
71
72
|
registerImplementationCommands(program, clientProxy);
|
|
72
73
|
registerLintCommands(program, clientProxy);
|
|
73
74
|
registerDiffCommand(program, clientProxy);
|
|
75
|
+
registerVerifyCommands(program, clientProxy);
|
|
74
76
|
// Handle async errors from Commander action handlers
|
|
75
77
|
process.on("uncaughtException", (err) => {
|
|
76
78
|
console.error(`Error: ${err.message}`);
|