mrvn-cli 0.4.7 → 0.4.8

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 CHANGED
@@ -751,4 +751,10 @@ npm run typecheck # TypeScript check without emitting
751
751
 
752
752
  ## License
753
753
 
754
- MIT
754
+ Marvin CLI is open-source and released under the MIT License with Commons Clause. This means you can:
755
+
756
+ - Use Marvin for personal and commercial projects
757
+ - Modify the source code to fit your needs
758
+ - Distribute copies of the software
759
+ - Contribute improvements back to the community
760
+ - The Commons Clause adds one important restriction: you cannot sell Marvin itself as a service or product, but you can use it in your own applications and services.
package/dist/index.js CHANGED
@@ -15472,13 +15472,14 @@ function collectSprintSummaryData(store, sprintId) {
15472
15472
  const workItemDocs = allDocs.filter(
15473
15473
  (d) => d.frontmatter.type !== "sprint" && d.frontmatter.type !== "epic" && d.frontmatter.tags?.includes(sprintTag)
15474
15474
  );
15475
+ const primaryDocs = workItemDocs.filter((d) => d.frontmatter.type !== "contribution");
15475
15476
  const byStatus = {};
15476
15477
  const byType = {};
15477
15478
  let doneCount = 0;
15478
15479
  let inProgressCount = 0;
15479
15480
  let openCount = 0;
15480
15481
  let blockedCount = 0;
15481
- for (const doc of workItemDocs) {
15482
+ for (const doc of primaryDocs) {
15482
15483
  const s = doc.frontmatter.status;
15483
15484
  byStatus[s] = (byStatus[s] ?? 0) + 1;
15484
15485
  byType[doc.frontmatter.type] = (byType[doc.frontmatter.type] ?? 0) + 1;
@@ -15487,21 +15488,59 @@ function collectSprintSummaryData(store, sprintId) {
15487
15488
  else if (s === "blocked") blockedCount++;
15488
15489
  else openCount++;
15489
15490
  }
15491
+ const allItemsById = /* @__PURE__ */ new Map();
15492
+ const childrenByParent = /* @__PURE__ */ new Map();
15493
+ const sprintItemIds = new Set(workItemDocs.map((d) => d.frontmatter.id));
15494
+ for (const doc of workItemDocs) {
15495
+ const about = doc.frontmatter.aboutArtifact;
15496
+ const item = {
15497
+ id: doc.frontmatter.id,
15498
+ title: doc.frontmatter.title,
15499
+ type: doc.frontmatter.type,
15500
+ status: doc.frontmatter.status,
15501
+ aboutArtifact: about
15502
+ };
15503
+ allItemsById.set(item.id, item);
15504
+ if (about && sprintItemIds.has(about)) {
15505
+ if (!childrenByParent.has(about)) childrenByParent.set(about, []);
15506
+ childrenByParent.get(about).push(item);
15507
+ }
15508
+ }
15509
+ const itemsWithChildren = /* @__PURE__ */ new Set();
15510
+ for (const [parentId, children] of childrenByParent) {
15511
+ const parent = allItemsById.get(parentId);
15512
+ if (parent) {
15513
+ parent.children = children;
15514
+ for (const child of children) itemsWithChildren.add(child.id);
15515
+ }
15516
+ }
15517
+ for (const item of allItemsById.values()) {
15518
+ if (item.children) {
15519
+ for (const child of item.children) {
15520
+ const grandchildren = childrenByParent.get(child.id);
15521
+ if (grandchildren) {
15522
+ child.children = grandchildren;
15523
+ for (const gc of grandchildren) itemsWithChildren.add(gc.id);
15524
+ }
15525
+ }
15526
+ }
15527
+ }
15528
+ const items = [];
15529
+ for (const doc of workItemDocs) {
15530
+ if (!itemsWithChildren.has(doc.frontmatter.id)) {
15531
+ items.push(allItemsById.get(doc.frontmatter.id));
15532
+ }
15533
+ }
15490
15534
  const workItems = {
15491
- total: workItemDocs.length,
15535
+ total: primaryDocs.length,
15492
15536
  done: doneCount,
15493
15537
  inProgress: inProgressCount,
15494
15538
  open: openCount,
15495
15539
  blocked: blockedCount,
15496
- completionPct: workItemDocs.length > 0 ? Math.round(doneCount / workItemDocs.length * 100) : 0,
15540
+ completionPct: primaryDocs.length > 0 ? Math.round(doneCount / primaryDocs.length * 100) : 0,
15497
15541
  byStatus,
15498
15542
  byType,
15499
- items: workItemDocs.map((d) => ({
15500
- id: d.frontmatter.id,
15501
- title: d.frontmatter.title,
15502
- type: d.frontmatter.type,
15503
- status: d.frontmatter.status
15504
- }))
15543
+ items
15505
15544
  };
15506
15545
  const meetings = [];
15507
15546
  if (startDate && endDate) {
@@ -15583,7 +15622,7 @@ function collectSprintSummaryData(store, sprintId) {
15583
15622
  const prev = completedSprints[0];
15584
15623
  const prevTag = `sprint:${prev.frontmatter.id}`;
15585
15624
  const prevWorkItems = allDocs.filter(
15586
- (d) => d.frontmatter.type !== "sprint" && d.frontmatter.type !== "epic" && d.frontmatter.tags?.includes(prevTag)
15625
+ (d) => d.frontmatter.type !== "sprint" && d.frontmatter.type !== "epic" && d.frontmatter.type !== "contribution" && d.frontmatter.tags?.includes(prevTag)
15587
15626
  );
15588
15627
  const prevDone = prevWorkItems.filter((d) => DONE_STATUSES.has(d.frontmatter.status)).length;
15589
15628
  const prevRate = prevWorkItems.length > 0 ? Math.round(prevDone / prevWorkItems.length * 100) : 0;
@@ -16453,6 +16492,17 @@ tr:hover td {
16453
16492
  background: var(--bg-hover);
16454
16493
  }
16455
16494
 
16495
+ /* Hierarchical work-item sub-rows */
16496
+ .child-row td {
16497
+ font-size: 0.8125rem;
16498
+ border-bottom-style: dashed;
16499
+ }
16500
+ .contribution-row td {
16501
+ font-size: 0.8125rem;
16502
+ color: var(--text-dim);
16503
+ border-bottom-style: dashed;
16504
+ }
16505
+
16456
16506
  /* GAR */
16457
16507
  .gar-overall {
16458
16508
  text-align: center;
@@ -18140,7 +18190,25 @@ function sprintSummaryPage(data, cached2) {
18140
18190
  </div>`,
18141
18191
  { titleTag: "h3" }
18142
18192
  ) : "";
18143
- const workItemsSection = data.workItems.total > 0 ? collapsibleSection(
18193
+ function renderItemRows(items, depth = 0) {
18194
+ return items.flatMap((w) => {
18195
+ const isChild = depth > 0;
18196
+ const isContribution = w.type === "contribution";
18197
+ const rowClass = isContribution ? ' class="contribution-row"' : isChild ? ' class="child-row"' : "";
18198
+ const indent = depth > 0 ? ` style="padding-left: ${0.75 + depth * 1}rem"` : "";
18199
+ const row = `
18200
+ <tr${rowClass}>
18201
+ <td${indent}><a href="/docs/${escapeHtml(w.type)}/${escapeHtml(w.id)}">${escapeHtml(w.id)}</a></td>
18202
+ <td>${escapeHtml(w.title)}</td>
18203
+ <td>${escapeHtml(typeLabel(w.type))}</td>
18204
+ <td>${statusBadge(w.status)}</td>
18205
+ </tr>`;
18206
+ const childRows = w.children ? renderItemRows(w.children, depth + 1) : [];
18207
+ return [row, ...childRows];
18208
+ });
18209
+ }
18210
+ const workItemRows = renderItemRows(data.workItems.items);
18211
+ const workItemsSection = workItemRows.length > 0 ? collapsibleSection(
18144
18212
  "ss-work-items",
18145
18213
  "Work Items",
18146
18214
  `<div class="table-wrap">
@@ -18149,13 +18217,7 @@ function sprintSummaryPage(data, cached2) {
18149
18217
  <tr><th>ID</th><th>Title</th><th>Type</th><th>Status</th></tr>
18150
18218
  </thead>
18151
18219
  <tbody>
18152
- ${data.workItems.items.map((w) => `
18153
- <tr>
18154
- <td><a href="/docs/${escapeHtml(w.type)}/${escapeHtml(w.id)}">${escapeHtml(w.id)}</a></td>
18155
- <td>${escapeHtml(w.title)}</td>
18156
- <td>${escapeHtml(typeLabel(w.type))}</td>
18157
- <td>${statusBadge(w.status)}</td>
18158
- </tr>`).join("")}
18220
+ ${workItemRows.join("")}
18159
18221
  </tbody>
18160
18222
  </table>
18161
18223
  </div>`,
@@ -19449,18 +19511,18 @@ function createContributionTools(store) {
19449
19511
  content: external_exports.string().describe("Contribution content \u2014 the input from the persona"),
19450
19512
  persona: external_exports.string().describe("Persona making the contribution (e.g. 'tech-lead')"),
19451
19513
  contributionType: external_exports.string().describe("Type of contribution (e.g. 'action-result', 'risk-finding')"),
19452
- aboutArtifact: external_exports.string().optional().describe("Artifact ID this contribution relates to (e.g. 'A-001')"),
19453
- status: external_exports.string().optional().describe("Status (default: 'open')"),
19514
+ aboutArtifact: external_exports.string().describe("Artifact ID this contribution relates to (e.g. 'A-001', 'T-003')"),
19515
+ status: external_exports.string().optional().describe("Status (default: 'done')"),
19454
19516
  tags: external_exports.array(external_exports.string()).optional().describe("Tags for categorization")
19455
19517
  },
19456
19518
  async (args) => {
19457
19519
  const frontmatter = {
19458
19520
  title: args.title,
19459
- status: args.status ?? "open",
19521
+ status: args.status ?? "done",
19460
19522
  persona: args.persona,
19461
19523
  contributionType: args.contributionType
19462
19524
  };
19463
- if (args.aboutArtifact) frontmatter.aboutArtifact = args.aboutArtifact;
19525
+ frontmatter.aboutArtifact = args.aboutArtifact;
19464
19526
  if (args.tags) frontmatter.tags = args.tags;
19465
19527
  const doc = store.create("contribution", frontmatter, args.content);
19466
19528
  return {
@@ -19944,6 +20006,7 @@ function createTaskTools(store) {
19944
20006
  title: external_exports.string().describe("Task title"),
19945
20007
  content: external_exports.string().describe("Task description and implementation details"),
19946
20008
  linkedEpic: linkedEpicArray.describe("Epic ID(s) to link this task to (e.g. ['E-001'] or ['E-001', 'E-002'])"),
20009
+ aboutArtifact: external_exports.string().optional().describe("Parent artifact this task derives from (e.g. 'A-001')"),
19947
20010
  status: external_exports.enum(["backlog", "ready", "in-progress", "review", "done"]).optional().describe("Task status (default: 'backlog')"),
19948
20011
  acceptanceCriteria: external_exports.string().optional().describe("Acceptance criteria for the task"),
19949
20012
  technicalNotes: external_exports.string().optional().describe("Technical implementation notes"),
@@ -19969,6 +20032,7 @@ function createTaskTools(store) {
19969
20032
  linkedEpic: linkedEpics,
19970
20033
  tags: [...generateEpicTags(linkedEpics), ...args.tags ?? []]
19971
20034
  };
20035
+ if (args.aboutArtifact) frontmatter.aboutArtifact = args.aboutArtifact;
19972
20036
  if (args.acceptanceCriteria) frontmatter.acceptanceCriteria = args.acceptanceCriteria;
19973
20037
  if (args.technicalNotes) frontmatter.technicalNotes = args.technicalNotes;
19974
20038
  if (args.estimatedPoints !== void 0) frontmatter.estimatedPoints = args.estimatedPoints;
@@ -19992,6 +20056,7 @@ function createTaskTools(store) {
19992
20056
  {
19993
20057
  id: external_exports.string().describe("Task ID to update"),
19994
20058
  title: external_exports.string().optional().describe("New title"),
20059
+ aboutArtifact: external_exports.string().optional().describe("Parent artifact this task derives from (e.g. 'A-001')"),
19995
20060
  status: external_exports.enum(["backlog", "ready", "in-progress", "review", "done"]).optional().describe("New status"),
19996
20061
  content: external_exports.string().optional().describe("New content"),
19997
20062
  linkedEpic: linkedEpicArray.optional().describe("New linked epic ID(s)"),
@@ -26165,7 +26230,7 @@ function createProgram() {
26165
26230
  const program = new Command();
26166
26231
  program.name("marvin").description(
26167
26232
  "AI-powered product development assistant with Product Owner, Delivery Manager, and Technical Lead personas"
26168
- ).version("0.4.7");
26233
+ ).version("0.4.8");
26169
26234
  program.command("init").description("Initialize a new Marvin project in the current directory").action(async () => {
26170
26235
  await initCommand();
26171
26236
  });