@braingrid/cli 0.2.26 → 0.2.27

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/CHANGELOG.md CHANGED
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.2.27] - 2025-01-27
11
+
12
+ ### Added
13
+
14
+ - **Requirement tagging support**
15
+ - New `--tags` option for `requirement create` and `specify` commands
16
+ - Accepts comma-separated tags (max 5 per requirement)
17
+ - Tags are validated, trimmed, and empty values filtered
18
+ - Tags displayed in all output formats (table, markdown, XML, JSON)
19
+
10
20
  ## [0.2.26] - 2025-01-20
11
21
 
12
22
  ### Added
package/dist/cli.js CHANGED
@@ -25,7 +25,7 @@ import axios from "axios";
25
25
 
26
26
  // src/build-config.ts
27
27
  var BUILD_ENV = true ? "production" : process.env.NODE_ENV === "test" ? "development" : "production";
28
- var CLI_VERSION = true ? "0.2.26" : "0.0.0-test";
28
+ var CLI_VERSION = true ? "0.2.27" : "0.0.0-test";
29
29
  var PRODUCTION_CONFIG = {
30
30
  apiUrl: "https://app.braingrid.ai",
31
31
  workosAuthUrl: "https://auth.braingrid.ai",
@@ -2744,6 +2744,11 @@ function formatRequirementOutput(requirement2, options) {
2744
2744
  }
2745
2745
  message += `${chalk5.bold("Status:")} ${requirement2.status}
2746
2746
  `;
2747
+ if (requirement2.tags && requirement2.tags.length > 0) {
2748
+ const tagNames = requirement2.tags.map((tag) => tag.name).join(", ");
2749
+ message += `${chalk5.bold("Tags:")} ${tagNames}
2750
+ `;
2751
+ }
2747
2752
  if (requirement2.assignee) {
2748
2753
  const assigneeName = requirement2.assignee.first_name || requirement2.assignee.last_name ? `${requirement2.assignee.first_name || ""} ${requirement2.assignee.last_name || ""}`.trim() : requirement2.assignee.email;
2749
2754
  message += `${chalk5.bold("Assigned to:")} ${assigneeName} (${requirement2.assignee.email})
@@ -2890,15 +2895,16 @@ function formatRequirementListMarkdown(requirements, pagination) {
2890
2895
  md += "_No requirements found._\n";
2891
2896
  return md;
2892
2897
  }
2893
- md += "| Short ID | Status | Name | Branch | Progress |\n";
2894
- md += "|----------|--------|------|--------|----------|\n";
2898
+ md += "| Short ID | Status | Name | Branch | Tags | Progress |\n";
2899
+ md += "|----------|--------|------|--------|------|----------|\n";
2895
2900
  for (const req of requirements) {
2896
2901
  const shortId = req.short_id || req.id.slice(0, 11);
2897
2902
  const status = req.status;
2898
2903
  const name = req.name;
2899
2904
  const branch = req.branch || "N/A";
2905
+ const tags = req.tags && req.tags.length > 0 ? req.tags.map((t) => t.name).join(", ") : "-";
2900
2906
  const progress = req.task_progress ? `${req.task_progress.progress_percentage}%` : "N/A";
2901
- md += `| ${shortId} | ${status} | ${name} | ${branch} | ${progress} |
2907
+ md += `| ${shortId} | ${status} | ${name} | ${branch} | ${tags} | ${progress} |
2902
2908
  `;
2903
2909
  }
2904
2910
  if (pagination) {
@@ -2971,6 +2977,15 @@ function formatRequirementListXml(requirements, pagination) {
2971
2977
  `;
2972
2978
  xml += " </assignee>\n";
2973
2979
  }
2980
+ if (req.tags && req.tags.length > 0) {
2981
+ xml += ` <tags count="${req.tags.length}">
2982
+ `;
2983
+ for (const tag of req.tags) {
2984
+ xml += ` <tag>${escapeXml(tag.name)}</tag>
2985
+ `;
2986
+ }
2987
+ xml += " </tags>\n";
2988
+ }
2974
2989
  xml += ` <created_at>${escapeXml(req.created_at)}</created_at>
2975
2990
  `;
2976
2991
  xml += ` <updated_at>${escapeXml(req.updated_at)}</updated_at>
@@ -3087,6 +3102,12 @@ function formatRequirementBuildMarkdown(requirement2, options) {
3087
3102
  md += `**Status:** ${requirement2.status}
3088
3103
 
3089
3104
  `;
3105
+ if (requirement2.tags && requirement2.tags.length > 0) {
3106
+ const tagNames = requirement2.tags.map((tag) => tag.name).join(", ");
3107
+ md += `**Tags:** ${tagNames}
3108
+
3109
+ `;
3110
+ }
3090
3111
  if (requirement2.assignee) {
3091
3112
  const assigneeName = requirement2.assignee.first_name || requirement2.assignee.last_name ? `${requirement2.assignee.first_name || ""} ${requirement2.assignee.last_name || ""}`.trim() : requirement2.assignee.email;
3092
3113
  md += `**Assigned to:** ${assigneeName} (${requirement2.assignee.email})
@@ -3223,6 +3244,15 @@ function formatRequirementBuildXml(requirement2) {
3223
3244
  xml += ` <branch>${escapeXml(requirement2.branch)}</branch>
3224
3245
  `;
3225
3246
  }
3247
+ if (requirement2.tags && requirement2.tags.length > 0) {
3248
+ xml += ` <tags count="${requirement2.tags.length}">
3249
+ `;
3250
+ for (const tag of requirement2.tags) {
3251
+ xml += ` <tag>${escapeXml(tag.name)}</tag>
3252
+ `;
3253
+ }
3254
+ xml += " </tags>\n";
3255
+ }
3226
3256
  if (requirement2.assignee) {
3227
3257
  xml += " <assignee>\n";
3228
3258
  xml += ` <email>${escapeXml(requirement2.assignee.email)}</email>
@@ -3976,6 +4006,22 @@ var RequirementService = class {
3976
4006
  }
3977
4007
  };
3978
4008
 
4009
+ // src/utils/tag-validation.ts
4010
+ var MAX_TAGS = 5;
4011
+ function validateTags(tagsString) {
4012
+ if (!tagsString || tagsString.trim().length === 0) {
4013
+ return { valid: true, tags: [] };
4014
+ }
4015
+ const tags = tagsString.split(",").map((tag) => tag.trim()).filter((tag) => tag.length > 0);
4016
+ if (tags.length > MAX_TAGS) {
4017
+ return {
4018
+ valid: false,
4019
+ error: `Maximum ${MAX_TAGS} tags allowed`
4020
+ };
4021
+ }
4022
+ return { valid: true, tags };
4023
+ }
4024
+
3979
4025
  // src/handlers/requirement.handlers.ts
3980
4026
  function getServices2() {
3981
4027
  const config = getConfig();
@@ -4203,11 +4249,23 @@ async function handleRequirementCreate(opts) {
4203
4249
  };
4204
4250
  }
4205
4251
  }
4252
+ let validatedTags;
4253
+ if (opts.tags) {
4254
+ const tagResult = validateTags(opts.tags);
4255
+ if (!tagResult.valid) {
4256
+ return {
4257
+ success: false,
4258
+ message: chalk7.red(`\u274C ${tagResult.error}`)
4259
+ };
4260
+ }
4261
+ validatedTags = tagResult.tags;
4262
+ }
4206
4263
  stopSpinner = showSpinner("Creating requirement", chalk7.gray);
4207
4264
  const requirement2 = await requirementService.createProjectRequirement(projectId, {
4208
4265
  name: opts.name,
4209
4266
  content: opts.content || null,
4210
- assigned_to: opts.assignedTo || null
4267
+ assigned_to: opts.assignedTo || null,
4268
+ tags: validatedTags
4211
4269
  });
4212
4270
  stopSpinner();
4213
4271
  stopSpinner = null;
@@ -4280,9 +4338,21 @@ async function handleRequirementSpecify(opts) {
4280
4338
  message: chalk7.red("\u274C Prompt must be no more than 5000 characters long")
4281
4339
  };
4282
4340
  }
4341
+ let validatedTags;
4342
+ if (opts.tags) {
4343
+ const tagResult = validateTags(opts.tags);
4344
+ if (!tagResult.valid) {
4345
+ return {
4346
+ success: false,
4347
+ message: chalk7.red(`\u274C ${tagResult.error}`)
4348
+ };
4349
+ }
4350
+ validatedTags = tagResult.tags;
4351
+ }
4283
4352
  stopSpinner = showSpinner("Specifying requirement...");
4284
4353
  const requirement2 = await requirementService.specifyRequirement(projectId, {
4285
- prompt: opts.prompt
4354
+ prompt: opts.prompt,
4355
+ tags: validatedTags
4286
4356
  });
4287
4357
  stopSpinner();
4288
4358
  stopSpinner = null;
@@ -7610,7 +7680,7 @@ program.command("update").description("Update BrainGrid CLI to the latest versio
7610
7680
  program.command("specify").description("Create AI-refined requirement from prompt").option(
7611
7681
  "-p, --project <id>",
7612
7682
  "project ID (auto-detects from .braingrid/project.json if not provided)"
7613
- ).requiredOption("--prompt <prompt>", "requirement description (10-5000 characters)").option("--format <format>", "output format (table, json, xml, markdown)", "table").action(async (opts) => {
7683
+ ).requiredOption("--prompt <prompt>", "requirement description (10-5000 characters)").option("-t, --tags <tags>", "comma-separated tags (max 5)").option("--format <format>", "output format (table, json, xml, markdown)", "table").action(async (opts) => {
7614
7684
  const result = await handleRequirementSpecify(opts);
7615
7685
  console.log(result.message);
7616
7686
  if (!result.success) {
@@ -7695,7 +7765,7 @@ requirement.command("show [id]").description("Show requirement details (auto-det
7695
7765
  requirement.command("create").description("Create a new requirement").option(
7696
7766
  "-p, --project <id>",
7697
7767
  "project ID (auto-detects from .braingrid/project.json if not provided)"
7698
- ).requiredOption("-n, --name <name>", "requirement name").option("-c, --content <content>", "requirement content/description").option("-a, --assigned-to <uuid>", "user UUID to assign the requirement to").action(async (opts) => {
7768
+ ).requiredOption("-n, --name <name>", "requirement name").option("-c, --content <content>", "requirement content/description").option("-a, --assigned-to <uuid>", "user UUID to assign the requirement to").option("-t, --tags <tags>", "comma-separated tags (max 5)").action(async (opts) => {
7699
7769
  const result = await handleRequirementCreate(opts);
7700
7770
  console.log(result.message);
7701
7771
  if (!result.success) {