airgen-cli 0.1.1 → 0.1.2
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/implementation.js +19 -16
- package/dist/commands/requirements.js +10 -6
- package/dist/resolve.d.ts +8 -0
- package/dist/resolve.js +41 -0
- package/package.json +1 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { output } from "../output.js";
|
|
2
|
+
import { resolveRequirementId } from "../resolve.js";
|
|
2
3
|
export function registerImplementationCommands(program, client) {
|
|
3
4
|
const cmd = program.command("impl").description("Implementation tracking");
|
|
4
5
|
cmd
|
|
@@ -6,12 +7,12 @@ export function registerImplementationCommands(program, client) {
|
|
|
6
7
|
.description("Set implementation status on a requirement")
|
|
7
8
|
.argument("<tenant>", "Tenant slug")
|
|
8
9
|
.argument("<project>", "Project slug")
|
|
9
|
-
.argument("<requirement
|
|
10
|
+
.argument("<requirement>", "Requirement ref, ID, or hashId")
|
|
10
11
|
.requiredOption("--status <s>", "Status: not_started, in_progress, implemented, verified, blocked")
|
|
11
12
|
.option("--notes <text>", "Notes")
|
|
12
|
-
.action(async (tenant, project,
|
|
13
|
-
|
|
14
|
-
const data = await client.patch(`/requirements/${tenant}/${project}/${
|
|
13
|
+
.action(async (tenant, project, requirement, opts) => {
|
|
14
|
+
const resolvedId = await resolveRequirementId(client, tenant, project, requirement);
|
|
15
|
+
const data = await client.patch(`/requirements/${tenant}/${project}/${resolvedId}`, {
|
|
15
16
|
attributes: {
|
|
16
17
|
implementationStatus: opts.status,
|
|
17
18
|
implementationNotes: opts.notes,
|
|
@@ -24,17 +25,18 @@ export function registerImplementationCommands(program, client) {
|
|
|
24
25
|
.description("Link a code artifact to a requirement")
|
|
25
26
|
.argument("<tenant>", "Tenant slug")
|
|
26
27
|
.argument("<project>", "Project slug")
|
|
27
|
-
.argument("<requirement
|
|
28
|
+
.argument("<requirement>", "Requirement ref, ID, or hashId")
|
|
28
29
|
.requiredOption("--type <type>", "Artifact type: file, commit, pr, issue, test, url")
|
|
29
30
|
.requiredOption("--path <path>", "Artifact path/reference")
|
|
30
31
|
.option("--label <text>", "Label")
|
|
31
32
|
.option("--line <n>", "Line number (for file type)")
|
|
32
|
-
.action(async (tenant, project,
|
|
33
|
+
.action(async (tenant, project, requirement, opts) => {
|
|
34
|
+
const resolvedId = await resolveRequirementId(client, tenant, project, requirement);
|
|
33
35
|
// Fetch current requirement to get existing artifacts
|
|
34
|
-
const reqData = await client.get(`/requirements/${tenant}/${project}`, { page: "1", limit: "
|
|
35
|
-
const req = (reqData.data ?? []).find(r => r.id ===
|
|
36
|
+
const reqData = await client.get(`/requirements/${tenant}/${project}`, { page: "1", limit: "100" });
|
|
37
|
+
const req = (reqData.data ?? []).find(r => r.id === resolvedId);
|
|
36
38
|
if (!req) {
|
|
37
|
-
console.error(`Requirement ${
|
|
39
|
+
console.error(`Requirement ${requirement} not found.`);
|
|
38
40
|
process.exit(1);
|
|
39
41
|
}
|
|
40
42
|
const attrs = req.attributes ?? {};
|
|
@@ -45,7 +47,7 @@ export function registerImplementationCommands(program, client) {
|
|
|
45
47
|
if (opts.line)
|
|
46
48
|
newArtifact.line = parseInt(opts.line, 10);
|
|
47
49
|
artifacts.push(newArtifact);
|
|
48
|
-
await client.patch(`/requirements/${tenant}/${project}/${
|
|
50
|
+
await client.patch(`/requirements/${tenant}/${project}/${resolvedId}`, {
|
|
49
51
|
attributes: { ...attrs, artifacts },
|
|
50
52
|
});
|
|
51
53
|
console.log("Artifact linked.");
|
|
@@ -55,20 +57,21 @@ export function registerImplementationCommands(program, client) {
|
|
|
55
57
|
.description("Remove an artifact link from a requirement")
|
|
56
58
|
.argument("<tenant>", "Tenant slug")
|
|
57
59
|
.argument("<project>", "Project slug")
|
|
58
|
-
.argument("<requirement
|
|
60
|
+
.argument("<requirement>", "Requirement ref, ID, or hashId")
|
|
59
61
|
.requiredOption("--type <type>", "Artifact type")
|
|
60
62
|
.requiredOption("--path <path>", "Artifact path")
|
|
61
|
-
.action(async (tenant, project,
|
|
62
|
-
const
|
|
63
|
-
const
|
|
63
|
+
.action(async (tenant, project, requirement, opts) => {
|
|
64
|
+
const resolvedId = await resolveRequirementId(client, tenant, project, requirement);
|
|
65
|
+
const reqData = await client.get(`/requirements/${tenant}/${project}`, { page: "1", limit: "100" });
|
|
66
|
+
const req = (reqData.data ?? []).find(r => r.id === resolvedId);
|
|
64
67
|
if (!req) {
|
|
65
|
-
console.error(`Requirement ${
|
|
68
|
+
console.error(`Requirement ${requirement} not found.`);
|
|
66
69
|
process.exit(1);
|
|
67
70
|
}
|
|
68
71
|
const attrs = req.attributes ?? {};
|
|
69
72
|
const artifacts = attrs.artifacts ?? [];
|
|
70
73
|
const filtered = artifacts.filter(a => !(a.type === opts.type && a.path === opts.path));
|
|
71
|
-
await client.patch(`/requirements/${tenant}/${project}/${
|
|
74
|
+
await client.patch(`/requirements/${tenant}/${project}/${resolvedId}`, {
|
|
72
75
|
attributes: { ...attrs, artifacts: filtered },
|
|
73
76
|
});
|
|
74
77
|
console.log("Artifact unlinked.");
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { output, printTable, isJsonMode, truncate } from "../output.js";
|
|
2
|
+
import { resolveRequirementId } from "../resolve.js";
|
|
2
3
|
export function registerRequirementCommands(program, client) {
|
|
3
4
|
const cmd = program.command("requirements").alias("reqs").description("Manage requirements");
|
|
4
5
|
cmd
|
|
@@ -96,7 +97,7 @@ export function registerRequirementCommands(program, client) {
|
|
|
96
97
|
.description("Update a requirement")
|
|
97
98
|
.argument("<tenant>", "Tenant slug")
|
|
98
99
|
.argument("<project>", "Project slug")
|
|
99
|
-
.argument("<id>", "Requirement ID")
|
|
100
|
+
.argument("<id>", "Requirement ref, ID, or hashId")
|
|
100
101
|
.option("--text <text>", "Requirement text")
|
|
101
102
|
.option("--pattern <p>", "Pattern")
|
|
102
103
|
.option("--verification <v>", "Verification method")
|
|
@@ -105,6 +106,7 @@ export function registerRequirementCommands(program, client) {
|
|
|
105
106
|
.option("--section <id>", "Section ID")
|
|
106
107
|
.option("--tags <tags>", "Comma-separated tags")
|
|
107
108
|
.action(async (tenant, project, id, opts) => {
|
|
109
|
+
const resolvedId = await resolveRequirementId(client, tenant, project, id);
|
|
108
110
|
const body = {};
|
|
109
111
|
if (opts.text)
|
|
110
112
|
body.text = opts.text;
|
|
@@ -120,7 +122,7 @@ export function registerRequirementCommands(program, client) {
|
|
|
120
122
|
body.sectionId = opts.section;
|
|
121
123
|
if (opts.tags)
|
|
122
124
|
body.tags = opts.tags.split(",").map(t => t.trim());
|
|
123
|
-
await client.patch(`/requirements/${tenant}/${project}/${
|
|
125
|
+
await client.patch(`/requirements/${tenant}/${project}/${resolvedId}`, body);
|
|
124
126
|
if (isJsonMode()) {
|
|
125
127
|
output({ ok: true });
|
|
126
128
|
}
|
|
@@ -133,9 +135,10 @@ export function registerRequirementCommands(program, client) {
|
|
|
133
135
|
.description("Soft-delete a requirement")
|
|
134
136
|
.argument("<tenant>", "Tenant slug")
|
|
135
137
|
.argument("<project>", "Project slug")
|
|
136
|
-
.argument("<id>", "Requirement ID")
|
|
138
|
+
.argument("<id>", "Requirement ref, ID, or hashId")
|
|
137
139
|
.action(async (tenant, project, id) => {
|
|
138
|
-
await client
|
|
140
|
+
const resolvedId = await resolveRequirementId(client, tenant, project, id);
|
|
141
|
+
await client.delete(`/requirements/${tenant}/${project}/${resolvedId}`);
|
|
139
142
|
console.log("Requirement deleted.");
|
|
140
143
|
});
|
|
141
144
|
cmd
|
|
@@ -143,9 +146,10 @@ export function registerRequirementCommands(program, client) {
|
|
|
143
146
|
.description("Get version history of a requirement")
|
|
144
147
|
.argument("<tenant>", "Tenant slug")
|
|
145
148
|
.argument("<project>", "Project slug")
|
|
146
|
-
.argument("<id>", "Requirement ID")
|
|
149
|
+
.argument("<id>", "Requirement ref, ID, or hashId")
|
|
147
150
|
.action(async (tenant, project, id) => {
|
|
148
|
-
const
|
|
151
|
+
const resolvedId = await resolveRequirementId(client, tenant, project, id);
|
|
152
|
+
const data = await client.get(`/requirements/${tenant}/${project}/${resolvedId}/history`);
|
|
149
153
|
output(data);
|
|
150
154
|
});
|
|
151
155
|
cmd
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve any requirement identifier to its full ID.
|
|
3
|
+
*
|
|
4
|
+
* Accepts: full ID, ref, short ID (REQ-XXX), or hashId.
|
|
5
|
+
* Returns the full colon-separated ID (tenant:project:REQ-XXX).
|
|
6
|
+
*/
|
|
7
|
+
import type { AirgenClient } from "./client.js";
|
|
8
|
+
export declare function resolveRequirementId(client: AirgenClient, tenant: string, project: string, identifier: string): Promise<string>;
|
package/dist/resolve.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve any requirement identifier to its full ID.
|
|
3
|
+
*
|
|
4
|
+
* Accepts: full ID, ref, short ID (REQ-XXX), or hashId.
|
|
5
|
+
* Returns the full colon-separated ID (tenant:project:REQ-XXX).
|
|
6
|
+
*/
|
|
7
|
+
export async function resolveRequirementId(client, tenant, project, identifier) {
|
|
8
|
+
// Already a full ID (contains colons)
|
|
9
|
+
if (identifier.includes(":")) {
|
|
10
|
+
return identifier;
|
|
11
|
+
}
|
|
12
|
+
// Try as ref first (GET /requirements/{tenant}/{project}/{ref})
|
|
13
|
+
try {
|
|
14
|
+
const data = await client.get(`/requirements/${tenant}/${project}/${identifier}`);
|
|
15
|
+
if (data.record?.id)
|
|
16
|
+
return data.record.id;
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
// Not found by ref — continue
|
|
20
|
+
}
|
|
21
|
+
// Search through requirements for short ID or hashId match
|
|
22
|
+
let page = 1;
|
|
23
|
+
const limit = 100;
|
|
24
|
+
while (true) {
|
|
25
|
+
const data = await client.get(`/requirements/${tenant}/${project}`, { page: String(page), limit: String(limit) });
|
|
26
|
+
const reqs = data.data ?? [];
|
|
27
|
+
for (const r of reqs) {
|
|
28
|
+
// Match by short ID (the REQ-XXX part of tenant:project:REQ-XXX)
|
|
29
|
+
if (r.id?.endsWith(`:${identifier}`))
|
|
30
|
+
return r.id;
|
|
31
|
+
// Match by hashId
|
|
32
|
+
if (r.hashId === identifier)
|
|
33
|
+
return r.id;
|
|
34
|
+
}
|
|
35
|
+
if (page >= (data.meta?.totalPages ?? 1))
|
|
36
|
+
break;
|
|
37
|
+
page++;
|
|
38
|
+
}
|
|
39
|
+
// Nothing found — return as-is and let the API error
|
|
40
|
+
return identifier;
|
|
41
|
+
}
|