airgen-cli 0.20.2 → 0.20.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.
@@ -139,8 +139,10 @@ export function registerRequirementCommands(program, client) {
139
139
  .option("--idempotency-key <key>", "Prevent duplicates on retry — returns existing if key was already used")
140
140
  .action(async (tenant, projectKey, opts) => {
141
141
  if (!opts.section && !opts.document) {
142
- console.error("Warning: No --section or --document specified. Requirement will be project-level with a generic ref (e.g., REQ-PROJECTNAME-001).");
143
- console.error(" Use --section <id> to assign to a document section for proper ref prefixing.");
142
+ console.error("Error: --section or --document is required. Without it, the requirement gets a project-level ref");
143
+ console.error(" (e.g., REQ-SEEMERGENCYDIESEL...-001) that breaks delete/reassign on long project slugs.");
144
+ console.error(" Use: airgen docs sections list <tenant> <project> <document> to find section IDs.");
145
+ process.exit(1);
144
146
  }
145
147
  const data = await client.post("/requirements", {
146
148
  tenant,
@@ -199,8 +201,8 @@ export function registerRequirementCommands(program, client) {
199
201
  if (opts.section)
200
202
  body.sectionId = opts.section;
201
203
  if (opts.addTags || opts.removeTags) {
202
- // Read-modify-write for add/remove tags — use original ref for GET (resolvedId may be composite)
203
- const existing = await client.get(`/requirements/${tenant}/${project}/${encodeURIComponent(id)}`);
204
+ // Read-modify-write for add/remove tags
205
+ const existing = await client.get(`/requirements/${tenant}/${project}/${encodeURIComponent(resolvedId)}`);
204
206
  let currentTags = existing.record?.tags ?? [];
205
207
  if (opts.addTags) {
206
208
  const toAdd = opts.addTags.split(",").map(t => t.trim());
@@ -378,6 +380,11 @@ export function registerRequirementCommands(program, client) {
378
380
  skipped++;
379
381
  continue;
380
382
  }
383
+ if (!item.document && !item.documentSlug && !item.section && !item.sectionId) {
384
+ console.error(` [${i}] Skipped: missing document/section (would create homeless requirement)`);
385
+ skipped++;
386
+ continue;
387
+ }
381
388
  if (opts.dryRun) {
382
389
  console.log(` [dry-run] ${truncate(text, 80)}`);
383
390
  created++;
@@ -211,6 +211,12 @@ export function registerTraceabilityCommands(program, client) {
211
211
  try {
212
212
  // Delete old link, create reversed
213
213
  await client.delete(`/trace-links/${tenant}/${project}/${link.id}`);
214
+ }
215
+ catch {
216
+ console.error(` Skipped: link ${link.id} not deletable (may be an embedded linkset link)`);
217
+ continue;
218
+ }
219
+ try {
214
220
  await client.post("/trace-links", {
215
221
  tenant, projectKey: project,
216
222
  sourceRequirementId: link.targetRequirementId,
@@ -222,7 +228,7 @@ export function registerTraceabilityCommands(program, client) {
222
228
  console.log(` Fixed: reversed to ${targetRef(link)} --${link.linkType}--> ${sourceRef(link)}`);
223
229
  }
224
230
  catch (err) {
225
- console.error(` Failed to fix: ${err.message}`);
231
+ console.error(` Delete succeeded but reverse create failed: ${err.message}`);
226
232
  }
227
233
  }
228
234
  }
package/dist/resolve.d.ts CHANGED
@@ -1,8 +1,11 @@
1
1
  /**
2
- * Resolve any requirement identifier to its full ID.
2
+ * Resolve any requirement identifier to a ref suitable for API paths.
3
3
  *
4
- * Accepts: full ID, ref, short ID (REQ-XXX), or hashId.
5
- * Returns the full colon-separated ID (tenant:project:REQ-XXX).
4
+ * Accepts: full composite ID (tenant:project:REF), ref (STK-REQ-001),
5
+ * short ID, or hashId.
6
+ *
7
+ * Returns the ref string (e.g. STK-REQ-001) — NOT the full composite ID,
8
+ * since the API route already includes tenant and project in the path.
6
9
  */
7
10
  import type { AirgenClient } from "./client.js";
8
11
  export declare function resolveRequirementId(client: AirgenClient, tenant: string, project: string, identifier: string): Promise<string>;
package/dist/resolve.js CHANGED
@@ -1,19 +1,23 @@
1
1
  /**
2
- * Resolve any requirement identifier to its full ID.
2
+ * Resolve any requirement identifier to a ref suitable for API paths.
3
3
  *
4
- * Accepts: full ID, ref, short ID (REQ-XXX), or hashId.
5
- * Returns the full colon-separated ID (tenant:project:REQ-XXX).
4
+ * Accepts: full composite ID (tenant:project:REF), ref (STK-REQ-001),
5
+ * short ID, or hashId.
6
+ *
7
+ * Returns the ref string (e.g. STK-REQ-001) — NOT the full composite ID,
8
+ * since the API route already includes tenant and project in the path.
6
9
  */
7
10
  export async function resolveRequirementId(client, tenant, project, identifier) {
8
- // Already a full ID (contains colons)
11
+ // Full composite ID (tenant:project:REF) — extract the ref part
9
12
  if (identifier.includes(":")) {
10
- return identifier;
13
+ const parts = identifier.split(":");
14
+ return parts[parts.length - 1];
11
15
  }
12
16
  // Try as ref first (GET /requirements/{tenant}/{project}/{ref})
13
17
  try {
14
- const data = await client.get(`/requirements/${tenant}/${project}/${identifier}`);
15
- if (data.record?.id)
16
- return data.record.id;
18
+ const data = await client.get(`/requirements/${tenant}/${project}/${encodeURIComponent(identifier)}`);
19
+ if (data.record?.ref)
20
+ return data.record.ref;
17
21
  }
18
22
  catch {
19
23
  // Not found by ref — continue
@@ -27,10 +31,10 @@ export async function resolveRequirementId(client, tenant, project, identifier)
27
31
  for (const r of reqs) {
28
32
  // Match by short ID (the REQ-XXX part of tenant:project:REQ-XXX)
29
33
  if (r.id?.endsWith(`:${identifier}`))
30
- return r.id;
34
+ return r.ref ?? identifier;
31
35
  // Match by hashId
32
36
  if (r.hashId === identifier)
33
- return r.id;
37
+ return r.ref ?? identifier;
34
38
  }
35
39
  if (page >= (data.meta?.totalPages ?? 1))
36
40
  break;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "airgen-cli",
3
- "version": "0.20.2",
3
+ "version": "0.20.4",
4
4
  "description": "AIRGen CLI — requirements engineering from the command line",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",