airgen-cli 0.1.8 → 0.2.1

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/README.md ADDED
@@ -0,0 +1,268 @@
1
+ # airgen-cli
2
+
3
+ Requirements engineering from the command line. Manage requirements, architecture diagrams, traceability, baselines, and more — all from your terminal.
4
+
5
+ Pairs with [AIRGen Studio](https://airgen.studio) and the AIRGen MCP server.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install -g airgen-cli
11
+ ```
12
+
13
+ ## Configuration
14
+
15
+ Set credentials via environment variables or `~/.airgenrc`:
16
+
17
+ ```bash
18
+ # Environment variables
19
+ export AIRGEN_API_URL=https://api.airgen.studio/api
20
+ export AIRGEN_EMAIL=you@example.com
21
+ export AIRGEN_PASSWORD=your-password
22
+ ```
23
+
24
+ Or create `~/.airgenrc`:
25
+
26
+ ```json
27
+ {
28
+ "apiUrl": "https://api.airgen.studio/api",
29
+ "email": "you@example.com",
30
+ "password": "your-password"
31
+ }
32
+ ```
33
+
34
+ For semantic linting, also set a UHT token:
35
+
36
+ ```bash
37
+ export UHT_API_KEY=your-token # or UHT_TOKEN
38
+ ```
39
+
40
+ ## Quick start
41
+
42
+ ```bash
43
+ # List your tenants and projects
44
+ airgen tenants list
45
+ airgen projects list my-tenant
46
+
47
+ # List requirements
48
+ airgen reqs list my-tenant my-project
49
+
50
+ # Render a diagram in the terminal
51
+ airgen diag list my-tenant my-project
52
+ airgen diag render my-tenant my-project diagram-123
53
+
54
+ # Run semantic lint
55
+ airgen lint my-tenant my-project
56
+
57
+ # Get a compliance report
58
+ airgen report compliance my-tenant my-project
59
+ ```
60
+
61
+ ## Global options
62
+
63
+ | Flag | Description |
64
+ |---|---|
65
+ | `--json` | Output as JSON (works with any command) |
66
+ | `-V, --version` | Print version |
67
+ | `-h, --help` | Show help |
68
+
69
+ ## Commands
70
+
71
+ ### Tenants & Projects
72
+
73
+ ```bash
74
+ airgen tenants list # List all tenants
75
+ airgen projects list <tenant> # List projects in a tenant
76
+ airgen projects create <tenant> --name "X" # Create a project
77
+ airgen projects delete <tenant> <project> # Delete a project
78
+ ```
79
+
80
+ ### Requirements
81
+
82
+ ```bash
83
+ airgen reqs list <tenant> <project> # List (paginated)
84
+ airgen reqs list <tenant> <project> --page 2 --limit 50
85
+ airgen reqs get <tenant> <project> <ref> # Full detail
86
+ airgen reqs create <tenant> <project> --text "The system shall..."
87
+ airgen reqs update <tenant> <project> <id> --text "..." --tags safety,critical
88
+ airgen reqs delete <tenant> <project> <id> # Soft-delete
89
+ airgen reqs history <tenant> <project> <id> # Version history
90
+ airgen reqs search <tenant> <project> --query "thermal" --mode semantic
91
+ airgen reqs filter <tenant> <project> --pattern functional --tag safety
92
+ ```
93
+
94
+ ### Architecture Diagrams
95
+
96
+ ```bash
97
+ airgen diag list <tenant> <project> # List diagrams
98
+ airgen diag get <tenant> <project> <id> # Blocks + connectors JSON
99
+ airgen diag render <tenant> <project> <id> # Terminal display (default)
100
+ airgen diag render <tenant> <project> <id> --format mermaid # Mermaid syntax
101
+ airgen diag render <tenant> <project> <id> --format mermaid --wrap -o diagram.md
102
+ airgen diag create <tenant> <project> --name "X" --view block
103
+ airgen diag update <tenant> <project> <id> --name "Y"
104
+ airgen diag delete <tenant> <project> <id>
105
+ ```
106
+
107
+ **Blocks:**
108
+
109
+ ```bash
110
+ airgen diag blocks library <tenant> <project>
111
+ airgen diag blocks create <tenant> <project> --diagram <id> --name "X" --kind subsystem
112
+ airgen diag blocks delete <tenant> <project> <block-id>
113
+ ```
114
+
115
+ **Connectors:**
116
+
117
+ ```bash
118
+ airgen diag conn create <tenant> <project> --diagram <id> --source <id> --target <id> --kind flow --label "data"
119
+ airgen diag conn delete <tenant> <project> <conn-id> --diagram <id>
120
+ ```
121
+
122
+ ### Traceability
123
+
124
+ ```bash
125
+ airgen trace list <tenant> <project> # List trace links
126
+ airgen trace create <tenant> <project> --source <id> --target <id> --type derives
127
+ airgen trace delete <tenant> <project> <link-id>
128
+ airgen trace linksets list <tenant> <project> # Document linksets
129
+ ```
130
+
131
+ ### Baselines & Diff
132
+
133
+ ```bash
134
+ airgen bl list <tenant> <project>
135
+ airgen bl create <tenant> <project> --name "v1.0"
136
+ airgen bl compare <tenant> <project> --from <id1> --to <id2>
137
+
138
+ # Rich diff between baselines
139
+ airgen diff <tenant> <project> --from <bl1> --to <bl2> # Pretty terminal output
140
+ airgen diff <tenant> <project> --from <bl1> --to <bl2> --json # Structured JSON
141
+ airgen diff <tenant> <project> --from <bl1> --to <bl2> --format markdown -o diff.md
142
+ ```
143
+
144
+ `diff` shows added, modified, and removed requirements with full text, plus a summary of changes to documents, trace links, diagrams, blocks, and connectors.
145
+
146
+ ### Quality & AI
147
+
148
+ ```bash
149
+ airgen qa analyze "The system shall..." # Analyze single requirement
150
+ airgen qa score start <tenant> <project> # Background QA scoring
151
+ airgen qa draft "user needs thermal imaging" # Draft requirements from NL
152
+
153
+ airgen ai generate <tenant> <project> --prompt "..." # Generate candidates
154
+ airgen ai candidates <tenant> <project> # List pending candidates
155
+ airgen ai accept <candidate-id> # Promote to requirement
156
+ airgen ai reject <candidate-id> # Reject candidate
157
+ ```
158
+
159
+ ### Reports
160
+
161
+ ```bash
162
+ airgen report stats <tenant> <project> # Overview statistics
163
+ airgen report quality <tenant> <project> # QA score summary
164
+ airgen report compliance <tenant> <project> # Compliance + impl status
165
+ airgen report orphans <tenant> <project> # Untraced requirements
166
+ ```
167
+
168
+ All report commands auto-paginate through the full requirement set (up to 5000).
169
+
170
+ ### Implementation Tracking
171
+
172
+ ```bash
173
+ airgen impl status <tenant> <project> <req> --status implemented --notes "done in v2"
174
+ airgen impl summary <tenant> <project> # Coverage breakdown
175
+ airgen impl list <tenant> <project> --status blocked # Filter by status
176
+ airgen impl bulk-update <tenant> <project> --file updates.json
177
+
178
+ # Artifact linking
179
+ airgen impl link <tenant> <project> <req> --type file --path src/engine.ts
180
+ airgen impl unlink <tenant> <project> <req> --artifact <id>
181
+ ```
182
+
183
+ **Statuses:** `not_started`, `in_progress`, `implemented`, `verified`, `blocked`
184
+
185
+ **Bulk update file format:**
186
+
187
+ ```json
188
+ [
189
+ { "ref": "REQ-001", "status": "implemented", "notes": "shipped" },
190
+ { "ref": "REQ-002", "status": "in_progress" }
191
+ ]
192
+ ```
193
+
194
+ ### Import / Export
195
+
196
+ ```bash
197
+ airgen import requirements <tenant> <project> --file reqs.csv
198
+ airgen export requirements <tenant> <project> # Markdown
199
+ airgen export requirements <tenant> <project> --json # JSON
200
+ ```
201
+
202
+ ### Activity
203
+
204
+ ```bash
205
+ airgen activity list <tenant> <project> # Recent activity
206
+ airgen activity list <tenant> <project> --limit 50
207
+ ```
208
+
209
+ ### Documents
210
+
211
+ ```bash
212
+ airgen docs list <tenant> <project>
213
+ airgen docs get <tenant> <project> <slug>
214
+ airgen docs create <tenant> <project> --title "X" --kind structured
215
+ airgen docs delete <tenant> <project> <slug>
216
+ airgen docs export <tenant> <project> <slug> # Markdown export
217
+ airgen docs sec list <tenant> <project> <slug> # List sections
218
+ ```
219
+
220
+ ### Semantic Lint
221
+
222
+ Classifies domain concepts from your requirements using the [Universal Hex Taxonomy](https://universalhex.org) and flags ontological issues, structural problems, and coverage gaps.
223
+
224
+ ```bash
225
+ airgen lint <tenant> <project> # Full lint (top 15 concepts)
226
+ airgen lint <tenant> <project> --concepts 20 # Classify more concepts
227
+ airgen lint <tenant> <project> --format markdown -o lint-report.md
228
+ airgen lint <tenant> <project> --format json # Machine-readable
229
+ ```
230
+
231
+ **What it detects:**
232
+
233
+ - Ontological mismatches (e.g., non-physical entity with physical constraints)
234
+ - Abstract metrics missing statistical parameters
235
+ - Verification requirements mixed with functional requirements
236
+ - Degraded modes without performance criteria
237
+ - Ontological ambiguity between similar concepts
238
+ - Requirements lacking "shall" keyword
239
+
240
+ **Requires:** `UHT_TOKEN` or `UHT_API_KEY` environment variable. Get a token at [universalhex.org](https://universalhex.org).
241
+
242
+ ## JSON mode
243
+
244
+ Append `--json` to any command for machine-readable output:
245
+
246
+ ```bash
247
+ airgen reqs list my-tenant my-project --json | jq '.[].ref'
248
+ airgen report compliance my-tenant my-project --json | jq '.summary'
249
+ ```
250
+
251
+ ## Aliases
252
+
253
+ | Full command | Alias |
254
+ |---|---|
255
+ | `requirements` | `reqs` |
256
+ | `diagrams` | `diag` |
257
+ | `documents` | `docs` |
258
+ | `connectors` | `conn` |
259
+ | `baselines` | `bl` |
260
+ | `traces` | `trace` |
261
+ | `quality` | `qa` |
262
+ | `reports` | `report` |
263
+ | `projects` | `proj` |
264
+ | `sections` | `sec` |
265
+
266
+ ## License
267
+
268
+ MIT
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ import type { AirgenClient } from "../client.js";
3
+ export declare function registerDiffCommand(program: Command, client: AirgenClient): void;
@@ -0,0 +1,185 @@
1
+ import { writeFileSync } from "node:fs";
2
+ import { isJsonMode, truncate } from "../output.js";
3
+ // ── Helpers ───────────────────────────────────────────────────
4
+ /** Extract short ref from "tenant:project:REQ-001" → "REQ-001" */
5
+ function shortRef(id) {
6
+ return id?.split(":").pop() ?? "?";
7
+ }
8
+ function counts(comp) {
9
+ return {
10
+ added: comp?.added?.length ?? 0,
11
+ removed: comp?.removed?.length ?? 0,
12
+ modified: comp?.modified?.length ?? 0,
13
+ unchanged: comp?.unchanged?.length ?? 0,
14
+ };
15
+ }
16
+ // ── Structured output ────────────────────────────────────────
17
+ function buildStructured(data) {
18
+ const reqs = data.requirements ?? { added: [], removed: [], modified: [], unchanged: [] };
19
+ const r = counts(reqs);
20
+ return {
21
+ summary: {
22
+ from: data.fromBaseline?.ref ?? "?",
23
+ to: data.toBaseline?.ref ?? "?",
24
+ requirements: r,
25
+ },
26
+ added: reqs.added.map(v => ({ ref: shortRef(v.requirementId), text: v.text ?? "" })),
27
+ removed: reqs.removed.map(v => ({ ref: shortRef(v.requirementId), text: v.text ?? "" })),
28
+ modified: reqs.modified.map(v => ({ ref: shortRef(v.requirementId), text: v.text ?? "" })),
29
+ };
30
+ }
31
+ // ── Pretty text ──────────────────────────────────────────────
32
+ function formatPretty(data) {
33
+ const from = data.fromBaseline?.ref ?? "?";
34
+ const to = data.toBaseline?.ref ?? "?";
35
+ const reqs = data.requirements ?? { added: [], removed: [], modified: [], unchanged: [] };
36
+ const r = counts(reqs);
37
+ const lines = [];
38
+ lines.push(` Baseline Diff: ${from} → ${to}`);
39
+ lines.push(` ${"═".repeat(20 + from.length + to.length)}`);
40
+ lines.push(` ${r.added} added, ${r.modified} modified, ${r.removed} removed, ${r.unchanged} unchanged`);
41
+ lines.push("");
42
+ if (reqs.added.length > 0) {
43
+ lines.push(" ┄┄ Added ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄");
44
+ for (const v of reqs.added) {
45
+ lines.push(` + ${shortRef(v.requirementId)}`);
46
+ lines.push(` ${truncate(v.text ?? "", 100)}`);
47
+ }
48
+ lines.push("");
49
+ }
50
+ if (reqs.modified.length > 0) {
51
+ lines.push(" ┄┄ Modified ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄");
52
+ for (const v of reqs.modified) {
53
+ lines.push(` ~ ${shortRef(v.requirementId)}`);
54
+ lines.push(` ${truncate(v.text ?? "", 100)}`);
55
+ }
56
+ lines.push("");
57
+ }
58
+ if (reqs.removed.length > 0) {
59
+ lines.push(" ┄┄ Removed ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄");
60
+ for (const v of reqs.removed) {
61
+ lines.push(` - ${shortRef(v.requirementId)}`);
62
+ lines.push(` ${truncate(v.text ?? "", 100)}`);
63
+ }
64
+ lines.push("");
65
+ }
66
+ // Non-requirement entity summary
67
+ const others = [
68
+ ["Documents", data.documents],
69
+ ["Trace Links", data.traceLinks],
70
+ ["Diagrams", data.diagrams],
71
+ ["Blocks", data.blocks],
72
+ ["Connectors", data.connectors],
73
+ ];
74
+ const changed = others.filter(([, c]) => {
75
+ const n = counts(c);
76
+ return n.added + n.modified + n.removed > 0;
77
+ });
78
+ if (changed.length > 0) {
79
+ lines.push(" ┄┄ Other Changes ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄");
80
+ for (const [label, comp] of changed) {
81
+ const n = counts(comp);
82
+ lines.push(` ${label}: +${n.added} ~${n.modified} -${n.removed}`);
83
+ }
84
+ lines.push("");
85
+ }
86
+ if (r.added + r.modified + r.removed === 0 && changed.length === 0) {
87
+ lines.push(" No changes between baselines.");
88
+ lines.push("");
89
+ }
90
+ return lines.join("\n");
91
+ }
92
+ // ── Markdown ─────────────────────────────────────────────────
93
+ function formatMarkdown(data) {
94
+ const from = data.fromBaseline?.ref ?? "?";
95
+ const to = data.toBaseline?.ref ?? "?";
96
+ const reqs = data.requirements ?? { added: [], removed: [], modified: [], unchanged: [] };
97
+ const r = counts(reqs);
98
+ const lines = [];
99
+ lines.push(`## Baseline Diff: ${from} → ${to}`);
100
+ lines.push("");
101
+ lines.push(`**${r.added}** added, **${r.modified}** modified, **${r.removed}** removed, **${r.unchanged}** unchanged`);
102
+ lines.push("");
103
+ if (reqs.added.length > 0) {
104
+ lines.push("### Added");
105
+ lines.push("| Ref | Text |");
106
+ lines.push("|---|---|");
107
+ for (const v of reqs.added) {
108
+ lines.push(`| ${shortRef(v.requirementId)} | ${truncate(v.text ?? "", 120)} |`);
109
+ }
110
+ lines.push("");
111
+ }
112
+ if (reqs.modified.length > 0) {
113
+ lines.push("### Modified");
114
+ lines.push("| Ref | Text (current) |");
115
+ lines.push("|---|---|");
116
+ for (const v of reqs.modified) {
117
+ lines.push(`| ${shortRef(v.requirementId)} | ${truncate(v.text ?? "", 120)} |`);
118
+ }
119
+ lines.push("");
120
+ }
121
+ if (reqs.removed.length > 0) {
122
+ lines.push("### Removed");
123
+ lines.push("| Ref | Text |");
124
+ lines.push("|---|---|");
125
+ for (const v of reqs.removed) {
126
+ lines.push(`| ${shortRef(v.requirementId)} | ${truncate(v.text ?? "", 120)} |`);
127
+ }
128
+ lines.push("");
129
+ }
130
+ // Non-requirement entity summary
131
+ const others = [
132
+ ["Documents", data.documents],
133
+ ["Trace Links", data.traceLinks],
134
+ ["Diagrams", data.diagrams],
135
+ ["Blocks", data.blocks],
136
+ ["Connectors", data.connectors],
137
+ ];
138
+ const changed = others.filter(([, c]) => {
139
+ const n = counts(c);
140
+ return n.added + n.modified + n.removed > 0;
141
+ });
142
+ if (changed.length > 0) {
143
+ lines.push("### Other Changes");
144
+ lines.push("| Entity | Added | Modified | Removed |");
145
+ lines.push("|---|---|---|---|");
146
+ for (const [label, comp] of changed) {
147
+ const n = counts(comp);
148
+ lines.push(`| ${label} | ${n.added} | ${n.modified} | ${n.removed} |`);
149
+ }
150
+ lines.push("");
151
+ }
152
+ return lines.join("\n");
153
+ }
154
+ // ── Command registration ─────────────────────────────────────
155
+ export function registerDiffCommand(program, client) {
156
+ program
157
+ .command("diff")
158
+ .description("Show what changed between two baselines")
159
+ .argument("<tenant>", "Tenant slug")
160
+ .argument("<project>", "Project slug")
161
+ .requiredOption("--from <ref>", "Source baseline ref (earlier)")
162
+ .requiredOption("--to <ref>", "Target baseline ref (later)")
163
+ .option("--format <fmt>", "Output format: text, markdown", "text")
164
+ .option("-o, --output <file>", "Write report to file")
165
+ .action(async (tenant, project, opts) => {
166
+ const data = await client.get(`/baselines/${tenant}/${project}/compare`, { from: opts.from, to: opts.to });
167
+ let result;
168
+ if (isJsonMode()) {
169
+ result = JSON.stringify(buildStructured(data), null, 2);
170
+ }
171
+ else if (opts.format === "markdown") {
172
+ result = formatMarkdown(data);
173
+ }
174
+ else {
175
+ result = formatPretty(data);
176
+ }
177
+ if (opts.output) {
178
+ writeFileSync(opts.output, result + "\n", "utf-8");
179
+ console.log(`Diff written to ${opts.output}`);
180
+ }
181
+ else {
182
+ console.log(result);
183
+ }
184
+ });
185
+ }
package/dist/index.js CHANGED
@@ -20,6 +20,7 @@ import { registerImportExportCommands } from "./commands/import-export.js";
20
20
  import { registerActivityCommands } from "./commands/activity.js";
21
21
  import { registerImplementationCommands } from "./commands/implementation.js";
22
22
  import { registerLintCommands } from "./commands/lint.js";
23
+ import { registerDiffCommand } from "./commands/diff.js";
23
24
  const program = new Command();
24
25
  // Lazy-init: only create client when a command actually runs
25
26
  let client = null;
@@ -69,6 +70,7 @@ registerImportExportCommands(program, clientProxy);
69
70
  registerActivityCommands(program, clientProxy);
70
71
  registerImplementationCommands(program, clientProxy);
71
72
  registerLintCommands(program, clientProxy);
73
+ registerDiffCommand(program, clientProxy);
72
74
  // Handle async errors from Commander action handlers
73
75
  process.on("uncaughtException", (err) => {
74
76
  console.error(`Error: ${err.message}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "airgen-cli",
3
- "version": "0.1.8",
3
+ "version": "0.2.1",
4
4
  "description": "AIRGen CLI — requirements engineering from the command line",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -9,7 +9,8 @@
9
9
  },
10
10
  "files": [
11
11
  "dist",
12
- "package.json"
12
+ "package.json",
13
+ "README.md"
13
14
  ],
14
15
  "scripts": {
15
16
  "build": "tsc -p tsconfig.json",
@@ -21,7 +22,12 @@
21
22
  "requirements",
22
23
  "engineering",
23
24
  "cli",
24
- "requirements-management"
25
+ "requirements-management",
26
+ "systems-engineering",
27
+ "traceability",
28
+ "sysml",
29
+ "mbse",
30
+ "uht"
25
31
  ],
26
32
  "license": "MIT",
27
33
  "repository": {