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 +7 -1
- package/dist/index.js +88 -23
- package/dist/index.js.map +1 -1
- package/dist/marvin-serve.js +87 -22
- package/dist/marvin-serve.js.map +1 -1
- package/dist/marvin.js +88 -23
- package/dist/marvin.js.map +1 -1
- package/package.json +1 -1
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
|
|
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:
|
|
15535
|
+
total: primaryDocs.length,
|
|
15492
15536
|
done: doneCount,
|
|
15493
15537
|
inProgress: inProgressCount,
|
|
15494
15538
|
open: openCount,
|
|
15495
15539
|
blocked: blockedCount,
|
|
15496
|
-
completionPct:
|
|
15540
|
+
completionPct: primaryDocs.length > 0 ? Math.round(doneCount / primaryDocs.length * 100) : 0,
|
|
15497
15541
|
byStatus,
|
|
15498
15542
|
byType,
|
|
15499
|
-
items
|
|
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
|
-
|
|
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
|
-
${
|
|
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().
|
|
19453
|
-
status: external_exports.string().optional().describe("Status (default: '
|
|
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 ?? "
|
|
19521
|
+
status: args.status ?? "done",
|
|
19460
19522
|
persona: args.persona,
|
|
19461
19523
|
contributionType: args.contributionType
|
|
19462
19524
|
};
|
|
19463
|
-
|
|
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.
|
|
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
|
});
|