mrvn-cli 0.4.7 → 0.4.9

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.
@@ -14401,7 +14401,8 @@ function createActionTools(store) {
14401
14401
  priority: external_exports.string().optional().describe("Priority (high, medium, low)"),
14402
14402
  tags: external_exports.array(external_exports.string()).optional().describe("Tags for categorization"),
14403
14403
  dueDate: external_exports.string().optional().describe("Due date in ISO format (e.g. '2026-03-15')"),
14404
- sprints: external_exports.array(external_exports.string()).optional().describe("Sprint IDs to assign (e.g. ['SP-001']). Adds sprint:SP-xxx tags.")
14404
+ sprints: external_exports.array(external_exports.string()).optional().describe("Sprint IDs to assign (e.g. ['SP-001']). Adds sprint:SP-xxx tags."),
14405
+ workStream: external_exports.string().optional().describe("Work stream name (e.g. 'Budget UX'). Adds a stream:<value> tag.")
14405
14406
  },
14406
14407
  async (args) => {
14407
14408
  const tags = [...args.tags ?? []];
@@ -14411,6 +14412,9 @@ function createActionTools(store) {
14411
14412
  if (!tags.includes(tag)) tags.push(tag);
14412
14413
  }
14413
14414
  }
14415
+ if (args.workStream) {
14416
+ tags.push(`stream:${args.workStream}`);
14417
+ }
14414
14418
  const doc = store.create(
14415
14419
  "action",
14416
14420
  {
@@ -14448,10 +14452,11 @@ function createActionTools(store) {
14448
14452
  priority: external_exports.string().optional().describe("New priority"),
14449
14453
  dueDate: external_exports.string().optional().describe("Due date in ISO format (e.g. '2026-03-15')"),
14450
14454
  tags: external_exports.array(external_exports.string()).optional().describe("Replace all tags. When provided with sprints, sprint tags are merged into this array."),
14451
- sprints: external_exports.array(external_exports.string()).optional().describe("Sprint IDs to assign (replaces existing sprint tags). E.g. ['SP-001'].")
14455
+ sprints: external_exports.array(external_exports.string()).optional().describe("Sprint IDs to assign (replaces existing sprint tags). E.g. ['SP-001']."),
14456
+ workStream: external_exports.string().optional().describe("Work stream name (e.g. 'Budget UX'). Replaces existing stream:<value> tag.")
14452
14457
  },
14453
14458
  async (args) => {
14454
- const { id, content, sprints, tags, ...updates } = args;
14459
+ const { id, content, sprints, tags, workStream, ...updates } = args;
14455
14460
  if (tags !== void 0) {
14456
14461
  const merged = [...tags];
14457
14462
  if (sprints) {
@@ -14460,8 +14465,14 @@ function createActionTools(store) {
14460
14465
  if (!merged.includes(tag)) merged.push(tag);
14461
14466
  }
14462
14467
  }
14463
- updates.tags = merged;
14464
- } else if (sprints !== void 0) {
14468
+ if (workStream !== void 0) {
14469
+ const filtered = merged.filter((t) => !t.startsWith("stream:"));
14470
+ filtered.push(`stream:${workStream}`);
14471
+ updates.tags = filtered;
14472
+ } else {
14473
+ updates.tags = merged;
14474
+ }
14475
+ } else if (sprints !== void 0 || workStream !== void 0) {
14465
14476
  const existing = store.get(id);
14466
14477
  if (!existing) {
14467
14478
  return {
@@ -14469,10 +14480,16 @@ function createActionTools(store) {
14469
14480
  isError: true
14470
14481
  };
14471
14482
  }
14472
- const existingTags = existing.frontmatter.tags ?? [];
14473
- const nonSprintTags = existingTags.filter((t) => !t.startsWith("sprint:"));
14474
- const newSprintTags = sprints.map((s) => `sprint:${s}`);
14475
- updates.tags = [...nonSprintTags, ...newSprintTags];
14483
+ let existingTags = existing.frontmatter.tags ?? [];
14484
+ if (sprints !== void 0) {
14485
+ existingTags = existingTags.filter((t) => !t.startsWith("sprint:"));
14486
+ existingTags.push(...sprints.map((s) => `sprint:${s}`));
14487
+ }
14488
+ if (workStream !== void 0) {
14489
+ existingTags = existingTags.filter((t) => !t.startsWith("stream:"));
14490
+ existingTags.push(`stream:${workStream}`);
14491
+ }
14492
+ updates.tags = existingTags;
14476
14493
  }
14477
14494
  const doc = store.update(id, updates, content);
14478
14495
  return {
@@ -14637,14 +14654,19 @@ function createDocumentTools(store) {
14637
14654
  {
14638
14655
  type: external_exports.string().optional().describe(`Filter by document type (registered types: ${store.registeredTypes.join(", ")})`),
14639
14656
  status: external_exports.string().optional().describe("Filter by status"),
14640
- tag: external_exports.string().optional().describe("Filter by tag")
14657
+ tag: external_exports.string().optional().describe("Filter by tag"),
14658
+ workStream: external_exports.string().optional().describe("Filter by work stream name (matches stream:<value> tag)")
14641
14659
  },
14642
14660
  async (args) => {
14643
- const docs = store.list({
14661
+ let docs = store.list({
14644
14662
  type: args.type,
14645
14663
  status: args.status,
14646
14664
  tag: args.tag
14647
14665
  });
14666
+ if (args.workStream) {
14667
+ const streamTag = `stream:${args.workStream}`;
14668
+ docs = docs.filter((d) => d.frontmatter.tags?.includes(streamTag));
14669
+ }
14648
14670
  const summary = docs.map((d) => ({
14649
14671
  id: d.frontmatter.id,
14650
14672
  title: d.frontmatter.title,
@@ -15560,13 +15582,14 @@ function collectSprintSummaryData(store, sprintId) {
15560
15582
  const workItemDocs = allDocs.filter(
15561
15583
  (d) => d.frontmatter.type !== "sprint" && d.frontmatter.type !== "epic" && d.frontmatter.tags?.includes(sprintTag)
15562
15584
  );
15585
+ const primaryDocs = workItemDocs.filter((d) => d.frontmatter.type !== "contribution");
15563
15586
  const byStatus = {};
15564
15587
  const byType = {};
15565
15588
  let doneCount = 0;
15566
15589
  let inProgressCount = 0;
15567
15590
  let openCount = 0;
15568
15591
  let blockedCount = 0;
15569
- for (const doc of workItemDocs) {
15592
+ for (const doc of primaryDocs) {
15570
15593
  const s = doc.frontmatter.status;
15571
15594
  byStatus[s] = (byStatus[s] ?? 0) + 1;
15572
15595
  byType[doc.frontmatter.type] = (byType[doc.frontmatter.type] ?? 0) + 1;
@@ -15575,21 +15598,61 @@ function collectSprintSummaryData(store, sprintId) {
15575
15598
  else if (s === "blocked") blockedCount++;
15576
15599
  else openCount++;
15577
15600
  }
15601
+ const allItemsById = /* @__PURE__ */ new Map();
15602
+ const childrenByParent = /* @__PURE__ */ new Map();
15603
+ const sprintItemIds = new Set(workItemDocs.map((d) => d.frontmatter.id));
15604
+ for (const doc of workItemDocs) {
15605
+ const about = doc.frontmatter.aboutArtifact;
15606
+ const streamTag = (doc.frontmatter.tags ?? []).find((t) => t.startsWith("stream:"));
15607
+ const item = {
15608
+ id: doc.frontmatter.id,
15609
+ title: doc.frontmatter.title,
15610
+ type: doc.frontmatter.type,
15611
+ status: doc.frontmatter.status,
15612
+ workStream: streamTag ? streamTag.slice(7) : void 0,
15613
+ aboutArtifact: about
15614
+ };
15615
+ allItemsById.set(item.id, item);
15616
+ if (about && sprintItemIds.has(about)) {
15617
+ if (!childrenByParent.has(about)) childrenByParent.set(about, []);
15618
+ childrenByParent.get(about).push(item);
15619
+ }
15620
+ }
15621
+ const itemsWithChildren = /* @__PURE__ */ new Set();
15622
+ for (const [parentId, children] of childrenByParent) {
15623
+ const parent = allItemsById.get(parentId);
15624
+ if (parent) {
15625
+ parent.children = children;
15626
+ for (const child of children) itemsWithChildren.add(child.id);
15627
+ }
15628
+ }
15629
+ for (const item of allItemsById.values()) {
15630
+ if (item.children) {
15631
+ for (const child of item.children) {
15632
+ const grandchildren = childrenByParent.get(child.id);
15633
+ if (grandchildren) {
15634
+ child.children = grandchildren;
15635
+ for (const gc of grandchildren) itemsWithChildren.add(gc.id);
15636
+ }
15637
+ }
15638
+ }
15639
+ }
15640
+ const items = [];
15641
+ for (const doc of workItemDocs) {
15642
+ if (!itemsWithChildren.has(doc.frontmatter.id)) {
15643
+ items.push(allItemsById.get(doc.frontmatter.id));
15644
+ }
15645
+ }
15578
15646
  const workItems = {
15579
- total: workItemDocs.length,
15647
+ total: primaryDocs.length,
15580
15648
  done: doneCount,
15581
15649
  inProgress: inProgressCount,
15582
15650
  open: openCount,
15583
15651
  blocked: blockedCount,
15584
- completionPct: workItemDocs.length > 0 ? Math.round(doneCount / workItemDocs.length * 100) : 0,
15652
+ completionPct: primaryDocs.length > 0 ? Math.round(doneCount / primaryDocs.length * 100) : 0,
15585
15653
  byStatus,
15586
15654
  byType,
15587
- items: workItemDocs.map((d) => ({
15588
- id: d.frontmatter.id,
15589
- title: d.frontmatter.title,
15590
- type: d.frontmatter.type,
15591
- status: d.frontmatter.status
15592
- }))
15655
+ items
15593
15656
  };
15594
15657
  const meetings = [];
15595
15658
  if (startDate && endDate) {
@@ -15671,7 +15734,7 @@ function collectSprintSummaryData(store, sprintId) {
15671
15734
  const prev = completedSprints[0];
15672
15735
  const prevTag = `sprint:${prev.frontmatter.id}`;
15673
15736
  const prevWorkItems = allDocs.filter(
15674
- (d) => d.frontmatter.type !== "sprint" && d.frontmatter.type !== "epic" && d.frontmatter.tags?.includes(prevTag)
15737
+ (d) => d.frontmatter.type !== "sprint" && d.frontmatter.type !== "epic" && d.frontmatter.type !== "contribution" && d.frontmatter.tags?.includes(prevTag)
15675
15738
  );
15676
15739
  const prevDone = prevWorkItems.filter((d) => DONE_STATUSES.has(d.frontmatter.status)).length;
15677
15740
  const prevRate = prevWorkItems.length > 0 ? Math.round(prevDone / prevWorkItems.length * 100) : 0;
@@ -16564,19 +16627,22 @@ function createContributionTools(store) {
16564
16627
  content: external_exports.string().describe("Contribution content \u2014 the input from the persona"),
16565
16628
  persona: external_exports.string().describe("Persona making the contribution (e.g. 'tech-lead')"),
16566
16629
  contributionType: external_exports.string().describe("Type of contribution (e.g. 'action-result', 'risk-finding')"),
16567
- aboutArtifact: external_exports.string().optional().describe("Artifact ID this contribution relates to (e.g. 'A-001')"),
16568
- status: external_exports.string().optional().describe("Status (default: 'open')"),
16569
- tags: external_exports.array(external_exports.string()).optional().describe("Tags for categorization")
16630
+ aboutArtifact: external_exports.string().describe("Artifact ID this contribution relates to (e.g. 'A-001', 'T-003')"),
16631
+ status: external_exports.string().optional().describe("Status (default: 'done')"),
16632
+ tags: external_exports.array(external_exports.string()).optional().describe("Tags for categorization"),
16633
+ workStream: external_exports.string().optional().describe("Work stream name (e.g. 'Budget UX'). Adds a stream:<value> tag.")
16570
16634
  },
16571
16635
  async (args) => {
16572
16636
  const frontmatter = {
16573
16637
  title: args.title,
16574
- status: args.status ?? "open",
16638
+ status: args.status ?? "done",
16575
16639
  persona: args.persona,
16576
16640
  contributionType: args.contributionType
16577
16641
  };
16578
- if (args.aboutArtifact) frontmatter.aboutArtifact = args.aboutArtifact;
16579
- if (args.tags) frontmatter.tags = args.tags;
16642
+ frontmatter.aboutArtifact = args.aboutArtifact;
16643
+ const tags = [...args.tags ?? []];
16644
+ if (args.workStream) tags.push(`stream:${args.workStream}`);
16645
+ if (tags.length > 0) frontmatter.tags = tags;
16580
16646
  const doc = store.create("contribution", frontmatter, args.content);
16581
16647
  return {
16582
16648
  content: [
@@ -16595,10 +16661,18 @@ function createContributionTools(store) {
16595
16661
  id: external_exports.string().describe("Contribution ID to update"),
16596
16662
  title: external_exports.string().optional().describe("New title"),
16597
16663
  status: external_exports.string().optional().describe("New status"),
16598
- content: external_exports.string().optional().describe("New content")
16664
+ content: external_exports.string().optional().describe("New content"),
16665
+ workStream: external_exports.string().optional().describe("Work stream name (e.g. 'Budget UX'). Replaces existing stream:<value> tag.")
16599
16666
  },
16600
16667
  async (args) => {
16601
- const { id, content, ...updates } = args;
16668
+ const { id, content, workStream, ...updates } = args;
16669
+ if (workStream !== void 0) {
16670
+ const existing = store.get(id);
16671
+ const existingTags = existing?.frontmatter.tags ?? [];
16672
+ const filtered = existingTags.filter((t) => !t.startsWith("stream:"));
16673
+ filtered.push(`stream:${workStream}`);
16674
+ updates.tags = filtered;
16675
+ }
16602
16676
  const doc = store.update(id, updates, content);
16603
16677
  return {
16604
16678
  content: [
@@ -17059,13 +17133,15 @@ function createTaskTools(store) {
17059
17133
  title: external_exports.string().describe("Task title"),
17060
17134
  content: external_exports.string().describe("Task description and implementation details"),
17061
17135
  linkedEpic: linkedEpicArray.describe("Epic ID(s) to link this task to (e.g. ['E-001'] or ['E-001', 'E-002'])"),
17136
+ aboutArtifact: external_exports.string().optional().describe("Parent artifact this task derives from (e.g. 'A-001')"),
17062
17137
  status: external_exports.enum(["backlog", "ready", "in-progress", "review", "done"]).optional().describe("Task status (default: 'backlog')"),
17063
17138
  acceptanceCriteria: external_exports.string().optional().describe("Acceptance criteria for the task"),
17064
17139
  technicalNotes: external_exports.string().optional().describe("Technical implementation notes"),
17065
17140
  estimatedPoints: external_exports.number().optional().describe("Story point estimate"),
17066
17141
  complexity: external_exports.enum(["trivial", "simple", "moderate", "complex", "very-complex"]).optional().describe("Task complexity"),
17067
17142
  priority: external_exports.enum(["critical", "high", "medium", "low"]).optional().describe("Task priority"),
17068
- tags: external_exports.array(external_exports.string()).optional().describe("Additional tags")
17143
+ tags: external_exports.array(external_exports.string()).optional().describe("Additional tags"),
17144
+ workStream: external_exports.string().optional().describe("Work stream name (e.g. 'Budget UX'). Adds a stream:<value> tag.")
17069
17145
  },
17070
17146
  async (args) => {
17071
17147
  const linkedEpics = normalizeLinkedEpics(args.linkedEpic);
@@ -17078,12 +17154,15 @@ function createTaskTools(store) {
17078
17154
  warnings.push(`Warning: ${epicId} is a ${epic.frontmatter.type}, not an epic`);
17079
17155
  }
17080
17156
  }
17157
+ const baseTags = [...generateEpicTags(linkedEpics), ...args.tags ?? []];
17158
+ if (args.workStream) baseTags.push(`stream:${args.workStream}`);
17081
17159
  const frontmatter = {
17082
17160
  title: args.title,
17083
17161
  status: args.status ?? "backlog",
17084
17162
  linkedEpic: linkedEpics,
17085
- tags: [...generateEpicTags(linkedEpics), ...args.tags ?? []]
17163
+ tags: baseTags
17086
17164
  };
17165
+ if (args.aboutArtifact) frontmatter.aboutArtifact = args.aboutArtifact;
17087
17166
  if (args.acceptanceCriteria) frontmatter.acceptanceCriteria = args.acceptanceCriteria;
17088
17167
  if (args.technicalNotes) frontmatter.technicalNotes = args.technicalNotes;
17089
17168
  if (args.estimatedPoints !== void 0) frontmatter.estimatedPoints = args.estimatedPoints;
@@ -17107,6 +17186,7 @@ function createTaskTools(store) {
17107
17186
  {
17108
17187
  id: external_exports.string().describe("Task ID to update"),
17109
17188
  title: external_exports.string().optional().describe("New title"),
17189
+ aboutArtifact: external_exports.string().optional().describe("Parent artifact this task derives from (e.g. 'A-001')"),
17110
17190
  status: external_exports.enum(["backlog", "ready", "in-progress", "review", "done"]).optional().describe("New status"),
17111
17191
  content: external_exports.string().optional().describe("New content"),
17112
17192
  linkedEpic: linkedEpicArray.optional().describe("New linked epic ID(s)"),
@@ -17115,10 +17195,11 @@ function createTaskTools(store) {
17115
17195
  estimatedPoints: external_exports.number().optional().describe("New story point estimate"),
17116
17196
  complexity: external_exports.enum(["trivial", "simple", "moderate", "complex", "very-complex"]).optional().describe("New complexity"),
17117
17197
  priority: external_exports.enum(["critical", "high", "medium", "low"]).optional().describe("New priority"),
17118
- tags: external_exports.array(external_exports.string()).optional().describe("Replace tags (e.g. remove old tags, add new ones)")
17198
+ tags: external_exports.array(external_exports.string()).optional().describe("Replace tags (e.g. remove old tags, add new ones)"),
17199
+ workStream: external_exports.string().optional().describe("Work stream name (e.g. 'Budget UX'). Replaces existing stream:<value> tag.")
17119
17200
  },
17120
17201
  async (args) => {
17121
- const { id, content, linkedEpic: rawLinkedEpic, tags: userTags, ...updates } = args;
17202
+ const { id, content, linkedEpic: rawLinkedEpic, tags: userTags, workStream, ...updates } = args;
17122
17203
  const warnings = [];
17123
17204
  if (rawLinkedEpic !== void 0) {
17124
17205
  const linkedEpics = normalizeLinkedEpics(rawLinkedEpic);
@@ -17139,6 +17220,12 @@ function createTaskTools(store) {
17139
17220
  } else if (userTags !== void 0) {
17140
17221
  updates.tags = userTags;
17141
17222
  }
17223
+ if (workStream !== void 0) {
17224
+ const currentTags = updates.tags ?? store.get(id)?.frontmatter.tags ?? [];
17225
+ const filtered = currentTags.filter((t) => !t.startsWith("stream:"));
17226
+ filtered.push(`stream:${workStream}`);
17227
+ updates.tags = filtered;
17228
+ }
17142
17229
  const doc = store.update(id, updates, content);
17143
17230
  const parts = [`Updated task ${doc.frontmatter.id}: ${doc.frontmatter.title}`];
17144
17231
  if (warnings.length > 0) {
@@ -20207,6 +20294,17 @@ tr:hover td {
20207
20294
  background: var(--bg-hover);
20208
20295
  }
20209
20296
 
20297
+ /* Hierarchical work-item sub-rows */
20298
+ .child-row td {
20299
+ font-size: 0.8125rem;
20300
+ border-bottom-style: dashed;
20301
+ }
20302
+ .contribution-row td {
20303
+ font-size: 0.8125rem;
20304
+ color: var(--text-dim);
20305
+ border-bottom-style: dashed;
20306
+ }
20307
+
20210
20308
  /* GAR */
20211
20309
  .gar-overall {
20212
20310
  text-align: center;
@@ -21894,22 +21992,36 @@ function sprintSummaryPage(data, cached2) {
21894
21992
  </div>`,
21895
21993
  { titleTag: "h3" }
21896
21994
  ) : "";
21897
- const workItemsSection = data.workItems.total > 0 ? collapsibleSection(
21995
+ function renderItemRows(items, depth = 0) {
21996
+ return items.flatMap((w) => {
21997
+ const isChild = depth > 0;
21998
+ const isContribution = w.type === "contribution";
21999
+ const rowClass = isContribution ? ' class="contribution-row"' : isChild ? ' class="child-row"' : "";
22000
+ const indent = depth > 0 ? ` style="padding-left: ${0.75 + depth * 1}rem"` : "";
22001
+ const streamCell = w.workStream ? `<span class="badge badge-subtle">${escapeHtml(w.workStream)}</span>` : "";
22002
+ const row = `
22003
+ <tr${rowClass}>
22004
+ <td${indent}><a href="/docs/${escapeHtml(w.type)}/${escapeHtml(w.id)}">${escapeHtml(w.id)}</a></td>
22005
+ <td>${escapeHtml(w.title)}</td>
22006
+ <td>${streamCell}</td>
22007
+ <td>${escapeHtml(typeLabel(w.type))}</td>
22008
+ <td>${statusBadge(w.status)}</td>
22009
+ </tr>`;
22010
+ const childRows = w.children ? renderItemRows(w.children, depth + 1) : [];
22011
+ return [row, ...childRows];
22012
+ });
22013
+ }
22014
+ const workItemRows = renderItemRows(data.workItems.items);
22015
+ const workItemsSection = workItemRows.length > 0 ? collapsibleSection(
21898
22016
  "ss-work-items",
21899
22017
  "Work Items",
21900
22018
  `<div class="table-wrap">
21901
22019
  <table>
21902
22020
  <thead>
21903
- <tr><th>ID</th><th>Title</th><th>Type</th><th>Status</th></tr>
22021
+ <tr><th>ID</th><th>Title</th><th>Stream</th><th>Type</th><th>Status</th></tr>
21904
22022
  </thead>
21905
22023
  <tbody>
21906
- ${data.workItems.items.map((w) => `
21907
- <tr>
21908
- <td><a href="/docs/${escapeHtml(w.type)}/${escapeHtml(w.id)}">${escapeHtml(w.id)}</a></td>
21909
- <td>${escapeHtml(w.title)}</td>
21910
- <td>${escapeHtml(typeLabel(w.type))}</td>
21911
- <td>${statusBadge(w.status)}</td>
21912
- </tr>`).join("")}
22024
+ ${workItemRows.join("")}
21913
22025
  </tbody>
21914
22026
  </table>
21915
22027
  </div>`,