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.
- package/README.md +7 -1
- package/dist/index.js +155 -43
- package/dist/index.js.map +1 -1
- package/dist/marvin-serve.js +154 -42
- package/dist/marvin-serve.js.map +1 -1
- package/dist/marvin.js +155 -43
- 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
|
@@ -14557,7 +14557,8 @@ function createActionTools(store) {
|
|
|
14557
14557
|
priority: external_exports.string().optional().describe("Priority (high, medium, low)"),
|
|
14558
14558
|
tags: external_exports.array(external_exports.string()).optional().describe("Tags for categorization"),
|
|
14559
14559
|
dueDate: external_exports.string().optional().describe("Due date in ISO format (e.g. '2026-03-15')"),
|
|
14560
|
-
sprints: external_exports.array(external_exports.string()).optional().describe("Sprint IDs to assign (e.g. ['SP-001']). Adds sprint:SP-xxx tags.")
|
|
14560
|
+
sprints: external_exports.array(external_exports.string()).optional().describe("Sprint IDs to assign (e.g. ['SP-001']). Adds sprint:SP-xxx tags."),
|
|
14561
|
+
workStream: external_exports.string().optional().describe("Work stream name (e.g. 'Budget UX'). Adds a stream:<value> tag.")
|
|
14561
14562
|
},
|
|
14562
14563
|
async (args) => {
|
|
14563
14564
|
const tags = [...args.tags ?? []];
|
|
@@ -14567,6 +14568,9 @@ function createActionTools(store) {
|
|
|
14567
14568
|
if (!tags.includes(tag)) tags.push(tag);
|
|
14568
14569
|
}
|
|
14569
14570
|
}
|
|
14571
|
+
if (args.workStream) {
|
|
14572
|
+
tags.push(`stream:${args.workStream}`);
|
|
14573
|
+
}
|
|
14570
14574
|
const doc = store.create(
|
|
14571
14575
|
"action",
|
|
14572
14576
|
{
|
|
@@ -14604,10 +14608,11 @@ function createActionTools(store) {
|
|
|
14604
14608
|
priority: external_exports.string().optional().describe("New priority"),
|
|
14605
14609
|
dueDate: external_exports.string().optional().describe("Due date in ISO format (e.g. '2026-03-15')"),
|
|
14606
14610
|
tags: external_exports.array(external_exports.string()).optional().describe("Replace all tags. When provided with sprints, sprint tags are merged into this array."),
|
|
14607
|
-
sprints: external_exports.array(external_exports.string()).optional().describe("Sprint IDs to assign (replaces existing sprint tags). E.g. ['SP-001'].")
|
|
14611
|
+
sprints: external_exports.array(external_exports.string()).optional().describe("Sprint IDs to assign (replaces existing sprint tags). E.g. ['SP-001']."),
|
|
14612
|
+
workStream: external_exports.string().optional().describe("Work stream name (e.g. 'Budget UX'). Replaces existing stream:<value> tag.")
|
|
14608
14613
|
},
|
|
14609
14614
|
async (args) => {
|
|
14610
|
-
const { id, content, sprints, tags, ...updates } = args;
|
|
14615
|
+
const { id, content, sprints, tags, workStream, ...updates } = args;
|
|
14611
14616
|
if (tags !== void 0) {
|
|
14612
14617
|
const merged = [...tags];
|
|
14613
14618
|
if (sprints) {
|
|
@@ -14616,8 +14621,14 @@ function createActionTools(store) {
|
|
|
14616
14621
|
if (!merged.includes(tag)) merged.push(tag);
|
|
14617
14622
|
}
|
|
14618
14623
|
}
|
|
14619
|
-
|
|
14620
|
-
|
|
14624
|
+
if (workStream !== void 0) {
|
|
14625
|
+
const filtered = merged.filter((t) => !t.startsWith("stream:"));
|
|
14626
|
+
filtered.push(`stream:${workStream}`);
|
|
14627
|
+
updates.tags = filtered;
|
|
14628
|
+
} else {
|
|
14629
|
+
updates.tags = merged;
|
|
14630
|
+
}
|
|
14631
|
+
} else if (sprints !== void 0 || workStream !== void 0) {
|
|
14621
14632
|
const existing = store.get(id);
|
|
14622
14633
|
if (!existing) {
|
|
14623
14634
|
return {
|
|
@@ -14625,10 +14636,16 @@ function createActionTools(store) {
|
|
|
14625
14636
|
isError: true
|
|
14626
14637
|
};
|
|
14627
14638
|
}
|
|
14628
|
-
|
|
14629
|
-
|
|
14630
|
-
|
|
14631
|
-
|
|
14639
|
+
let existingTags = existing.frontmatter.tags ?? [];
|
|
14640
|
+
if (sprints !== void 0) {
|
|
14641
|
+
existingTags = existingTags.filter((t) => !t.startsWith("sprint:"));
|
|
14642
|
+
existingTags.push(...sprints.map((s) => `sprint:${s}`));
|
|
14643
|
+
}
|
|
14644
|
+
if (workStream !== void 0) {
|
|
14645
|
+
existingTags = existingTags.filter((t) => !t.startsWith("stream:"));
|
|
14646
|
+
existingTags.push(`stream:${workStream}`);
|
|
14647
|
+
}
|
|
14648
|
+
updates.tags = existingTags;
|
|
14632
14649
|
}
|
|
14633
14650
|
const doc = store.update(id, updates, content);
|
|
14634
14651
|
return {
|
|
@@ -14793,14 +14810,19 @@ function createDocumentTools(store) {
|
|
|
14793
14810
|
{
|
|
14794
14811
|
type: external_exports.string().optional().describe(`Filter by document type (registered types: ${store.registeredTypes.join(", ")})`),
|
|
14795
14812
|
status: external_exports.string().optional().describe("Filter by status"),
|
|
14796
|
-
tag: external_exports.string().optional().describe("Filter by tag")
|
|
14813
|
+
tag: external_exports.string().optional().describe("Filter by tag"),
|
|
14814
|
+
workStream: external_exports.string().optional().describe("Filter by work stream name (matches stream:<value> tag)")
|
|
14797
14815
|
},
|
|
14798
14816
|
async (args) => {
|
|
14799
|
-
|
|
14817
|
+
let docs = store.list({
|
|
14800
14818
|
type: args.type,
|
|
14801
14819
|
status: args.status,
|
|
14802
14820
|
tag: args.tag
|
|
14803
14821
|
});
|
|
14822
|
+
if (args.workStream) {
|
|
14823
|
+
const streamTag = `stream:${args.workStream}`;
|
|
14824
|
+
docs = docs.filter((d) => d.frontmatter.tags?.includes(streamTag));
|
|
14825
|
+
}
|
|
14804
14826
|
const summary = docs.map((d) => ({
|
|
14805
14827
|
id: d.frontmatter.id,
|
|
14806
14828
|
title: d.frontmatter.title,
|
|
@@ -15472,13 +15494,14 @@ function collectSprintSummaryData(store, sprintId) {
|
|
|
15472
15494
|
const workItemDocs = allDocs.filter(
|
|
15473
15495
|
(d) => d.frontmatter.type !== "sprint" && d.frontmatter.type !== "epic" && d.frontmatter.tags?.includes(sprintTag)
|
|
15474
15496
|
);
|
|
15497
|
+
const primaryDocs = workItemDocs.filter((d) => d.frontmatter.type !== "contribution");
|
|
15475
15498
|
const byStatus = {};
|
|
15476
15499
|
const byType = {};
|
|
15477
15500
|
let doneCount = 0;
|
|
15478
15501
|
let inProgressCount = 0;
|
|
15479
15502
|
let openCount = 0;
|
|
15480
15503
|
let blockedCount = 0;
|
|
15481
|
-
for (const doc of
|
|
15504
|
+
for (const doc of primaryDocs) {
|
|
15482
15505
|
const s = doc.frontmatter.status;
|
|
15483
15506
|
byStatus[s] = (byStatus[s] ?? 0) + 1;
|
|
15484
15507
|
byType[doc.frontmatter.type] = (byType[doc.frontmatter.type] ?? 0) + 1;
|
|
@@ -15487,21 +15510,61 @@ function collectSprintSummaryData(store, sprintId) {
|
|
|
15487
15510
|
else if (s === "blocked") blockedCount++;
|
|
15488
15511
|
else openCount++;
|
|
15489
15512
|
}
|
|
15513
|
+
const allItemsById = /* @__PURE__ */ new Map();
|
|
15514
|
+
const childrenByParent = /* @__PURE__ */ new Map();
|
|
15515
|
+
const sprintItemIds = new Set(workItemDocs.map((d) => d.frontmatter.id));
|
|
15516
|
+
for (const doc of workItemDocs) {
|
|
15517
|
+
const about = doc.frontmatter.aboutArtifact;
|
|
15518
|
+
const streamTag = (doc.frontmatter.tags ?? []).find((t) => t.startsWith("stream:"));
|
|
15519
|
+
const item = {
|
|
15520
|
+
id: doc.frontmatter.id,
|
|
15521
|
+
title: doc.frontmatter.title,
|
|
15522
|
+
type: doc.frontmatter.type,
|
|
15523
|
+
status: doc.frontmatter.status,
|
|
15524
|
+
workStream: streamTag ? streamTag.slice(7) : void 0,
|
|
15525
|
+
aboutArtifact: about
|
|
15526
|
+
};
|
|
15527
|
+
allItemsById.set(item.id, item);
|
|
15528
|
+
if (about && sprintItemIds.has(about)) {
|
|
15529
|
+
if (!childrenByParent.has(about)) childrenByParent.set(about, []);
|
|
15530
|
+
childrenByParent.get(about).push(item);
|
|
15531
|
+
}
|
|
15532
|
+
}
|
|
15533
|
+
const itemsWithChildren = /* @__PURE__ */ new Set();
|
|
15534
|
+
for (const [parentId, children] of childrenByParent) {
|
|
15535
|
+
const parent = allItemsById.get(parentId);
|
|
15536
|
+
if (parent) {
|
|
15537
|
+
parent.children = children;
|
|
15538
|
+
for (const child of children) itemsWithChildren.add(child.id);
|
|
15539
|
+
}
|
|
15540
|
+
}
|
|
15541
|
+
for (const item of allItemsById.values()) {
|
|
15542
|
+
if (item.children) {
|
|
15543
|
+
for (const child of item.children) {
|
|
15544
|
+
const grandchildren = childrenByParent.get(child.id);
|
|
15545
|
+
if (grandchildren) {
|
|
15546
|
+
child.children = grandchildren;
|
|
15547
|
+
for (const gc of grandchildren) itemsWithChildren.add(gc.id);
|
|
15548
|
+
}
|
|
15549
|
+
}
|
|
15550
|
+
}
|
|
15551
|
+
}
|
|
15552
|
+
const items = [];
|
|
15553
|
+
for (const doc of workItemDocs) {
|
|
15554
|
+
if (!itemsWithChildren.has(doc.frontmatter.id)) {
|
|
15555
|
+
items.push(allItemsById.get(doc.frontmatter.id));
|
|
15556
|
+
}
|
|
15557
|
+
}
|
|
15490
15558
|
const workItems = {
|
|
15491
|
-
total:
|
|
15559
|
+
total: primaryDocs.length,
|
|
15492
15560
|
done: doneCount,
|
|
15493
15561
|
inProgress: inProgressCount,
|
|
15494
15562
|
open: openCount,
|
|
15495
15563
|
blocked: blockedCount,
|
|
15496
|
-
completionPct:
|
|
15564
|
+
completionPct: primaryDocs.length > 0 ? Math.round(doneCount / primaryDocs.length * 100) : 0,
|
|
15497
15565
|
byStatus,
|
|
15498
15566
|
byType,
|
|
15499
|
-
items
|
|
15500
|
-
id: d.frontmatter.id,
|
|
15501
|
-
title: d.frontmatter.title,
|
|
15502
|
-
type: d.frontmatter.type,
|
|
15503
|
-
status: d.frontmatter.status
|
|
15504
|
-
}))
|
|
15567
|
+
items
|
|
15505
15568
|
};
|
|
15506
15569
|
const meetings = [];
|
|
15507
15570
|
if (startDate && endDate) {
|
|
@@ -15583,7 +15646,7 @@ function collectSprintSummaryData(store, sprintId) {
|
|
|
15583
15646
|
const prev = completedSprints[0];
|
|
15584
15647
|
const prevTag = `sprint:${prev.frontmatter.id}`;
|
|
15585
15648
|
const prevWorkItems = allDocs.filter(
|
|
15586
|
-
(d) => d.frontmatter.type !== "sprint" && d.frontmatter.type !== "epic" && d.frontmatter.tags?.includes(prevTag)
|
|
15649
|
+
(d) => d.frontmatter.type !== "sprint" && d.frontmatter.type !== "epic" && d.frontmatter.type !== "contribution" && d.frontmatter.tags?.includes(prevTag)
|
|
15587
15650
|
);
|
|
15588
15651
|
const prevDone = prevWorkItems.filter((d) => DONE_STATUSES.has(d.frontmatter.status)).length;
|
|
15589
15652
|
const prevRate = prevWorkItems.length > 0 ? Math.round(prevDone / prevWorkItems.length * 100) : 0;
|
|
@@ -16453,6 +16516,17 @@ tr:hover td {
|
|
|
16453
16516
|
background: var(--bg-hover);
|
|
16454
16517
|
}
|
|
16455
16518
|
|
|
16519
|
+
/* Hierarchical work-item sub-rows */
|
|
16520
|
+
.child-row td {
|
|
16521
|
+
font-size: 0.8125rem;
|
|
16522
|
+
border-bottom-style: dashed;
|
|
16523
|
+
}
|
|
16524
|
+
.contribution-row td {
|
|
16525
|
+
font-size: 0.8125rem;
|
|
16526
|
+
color: var(--text-dim);
|
|
16527
|
+
border-bottom-style: dashed;
|
|
16528
|
+
}
|
|
16529
|
+
|
|
16456
16530
|
/* GAR */
|
|
16457
16531
|
.gar-overall {
|
|
16458
16532
|
text-align: center;
|
|
@@ -18140,22 +18214,36 @@ function sprintSummaryPage(data, cached2) {
|
|
|
18140
18214
|
</div>`,
|
|
18141
18215
|
{ titleTag: "h3" }
|
|
18142
18216
|
) : "";
|
|
18143
|
-
|
|
18217
|
+
function renderItemRows(items, depth = 0) {
|
|
18218
|
+
return items.flatMap((w) => {
|
|
18219
|
+
const isChild = depth > 0;
|
|
18220
|
+
const isContribution = w.type === "contribution";
|
|
18221
|
+
const rowClass = isContribution ? ' class="contribution-row"' : isChild ? ' class="child-row"' : "";
|
|
18222
|
+
const indent = depth > 0 ? ` style="padding-left: ${0.75 + depth * 1}rem"` : "";
|
|
18223
|
+
const streamCell = w.workStream ? `<span class="badge badge-subtle">${escapeHtml(w.workStream)}</span>` : "";
|
|
18224
|
+
const row = `
|
|
18225
|
+
<tr${rowClass}>
|
|
18226
|
+
<td${indent}><a href="/docs/${escapeHtml(w.type)}/${escapeHtml(w.id)}">${escapeHtml(w.id)}</a></td>
|
|
18227
|
+
<td>${escapeHtml(w.title)}</td>
|
|
18228
|
+
<td>${streamCell}</td>
|
|
18229
|
+
<td>${escapeHtml(typeLabel(w.type))}</td>
|
|
18230
|
+
<td>${statusBadge(w.status)}</td>
|
|
18231
|
+
</tr>`;
|
|
18232
|
+
const childRows = w.children ? renderItemRows(w.children, depth + 1) : [];
|
|
18233
|
+
return [row, ...childRows];
|
|
18234
|
+
});
|
|
18235
|
+
}
|
|
18236
|
+
const workItemRows = renderItemRows(data.workItems.items);
|
|
18237
|
+
const workItemsSection = workItemRows.length > 0 ? collapsibleSection(
|
|
18144
18238
|
"ss-work-items",
|
|
18145
18239
|
"Work Items",
|
|
18146
18240
|
`<div class="table-wrap">
|
|
18147
18241
|
<table>
|
|
18148
18242
|
<thead>
|
|
18149
|
-
<tr><th>ID</th><th>Title</th><th>Type</th><th>Status</th></tr>
|
|
18243
|
+
<tr><th>ID</th><th>Title</th><th>Stream</th><th>Type</th><th>Status</th></tr>
|
|
18150
18244
|
</thead>
|
|
18151
18245
|
<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("")}
|
|
18246
|
+
${workItemRows.join("")}
|
|
18159
18247
|
</tbody>
|
|
18160
18248
|
</table>
|
|
18161
18249
|
</div>`,
|
|
@@ -19449,19 +19537,22 @@ function createContributionTools(store) {
|
|
|
19449
19537
|
content: external_exports.string().describe("Contribution content \u2014 the input from the persona"),
|
|
19450
19538
|
persona: external_exports.string().describe("Persona making the contribution (e.g. 'tech-lead')"),
|
|
19451
19539
|
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: '
|
|
19454
|
-
tags: external_exports.array(external_exports.string()).optional().describe("Tags for categorization")
|
|
19540
|
+
aboutArtifact: external_exports.string().describe("Artifact ID this contribution relates to (e.g. 'A-001', 'T-003')"),
|
|
19541
|
+
status: external_exports.string().optional().describe("Status (default: 'done')"),
|
|
19542
|
+
tags: external_exports.array(external_exports.string()).optional().describe("Tags for categorization"),
|
|
19543
|
+
workStream: external_exports.string().optional().describe("Work stream name (e.g. 'Budget UX'). Adds a stream:<value> tag.")
|
|
19455
19544
|
},
|
|
19456
19545
|
async (args) => {
|
|
19457
19546
|
const frontmatter = {
|
|
19458
19547
|
title: args.title,
|
|
19459
|
-
status: args.status ?? "
|
|
19548
|
+
status: args.status ?? "done",
|
|
19460
19549
|
persona: args.persona,
|
|
19461
19550
|
contributionType: args.contributionType
|
|
19462
19551
|
};
|
|
19463
|
-
|
|
19464
|
-
|
|
19552
|
+
frontmatter.aboutArtifact = args.aboutArtifact;
|
|
19553
|
+
const tags = [...args.tags ?? []];
|
|
19554
|
+
if (args.workStream) tags.push(`stream:${args.workStream}`);
|
|
19555
|
+
if (tags.length > 0) frontmatter.tags = tags;
|
|
19465
19556
|
const doc = store.create("contribution", frontmatter, args.content);
|
|
19466
19557
|
return {
|
|
19467
19558
|
content: [
|
|
@@ -19480,10 +19571,18 @@ function createContributionTools(store) {
|
|
|
19480
19571
|
id: external_exports.string().describe("Contribution ID to update"),
|
|
19481
19572
|
title: external_exports.string().optional().describe("New title"),
|
|
19482
19573
|
status: external_exports.string().optional().describe("New status"),
|
|
19483
|
-
content: external_exports.string().optional().describe("New content")
|
|
19574
|
+
content: external_exports.string().optional().describe("New content"),
|
|
19575
|
+
workStream: external_exports.string().optional().describe("Work stream name (e.g. 'Budget UX'). Replaces existing stream:<value> tag.")
|
|
19484
19576
|
},
|
|
19485
19577
|
async (args) => {
|
|
19486
|
-
const { id, content, ...updates } = args;
|
|
19578
|
+
const { id, content, workStream, ...updates } = args;
|
|
19579
|
+
if (workStream !== void 0) {
|
|
19580
|
+
const existing = store.get(id);
|
|
19581
|
+
const existingTags = existing?.frontmatter.tags ?? [];
|
|
19582
|
+
const filtered = existingTags.filter((t) => !t.startsWith("stream:"));
|
|
19583
|
+
filtered.push(`stream:${workStream}`);
|
|
19584
|
+
updates.tags = filtered;
|
|
19585
|
+
}
|
|
19487
19586
|
const doc = store.update(id, updates, content);
|
|
19488
19587
|
return {
|
|
19489
19588
|
content: [
|
|
@@ -19944,13 +20043,15 @@ function createTaskTools(store) {
|
|
|
19944
20043
|
title: external_exports.string().describe("Task title"),
|
|
19945
20044
|
content: external_exports.string().describe("Task description and implementation details"),
|
|
19946
20045
|
linkedEpic: linkedEpicArray.describe("Epic ID(s) to link this task to (e.g. ['E-001'] or ['E-001', 'E-002'])"),
|
|
20046
|
+
aboutArtifact: external_exports.string().optional().describe("Parent artifact this task derives from (e.g. 'A-001')"),
|
|
19947
20047
|
status: external_exports.enum(["backlog", "ready", "in-progress", "review", "done"]).optional().describe("Task status (default: 'backlog')"),
|
|
19948
20048
|
acceptanceCriteria: external_exports.string().optional().describe("Acceptance criteria for the task"),
|
|
19949
20049
|
technicalNotes: external_exports.string().optional().describe("Technical implementation notes"),
|
|
19950
20050
|
estimatedPoints: external_exports.number().optional().describe("Story point estimate"),
|
|
19951
20051
|
complexity: external_exports.enum(["trivial", "simple", "moderate", "complex", "very-complex"]).optional().describe("Task complexity"),
|
|
19952
20052
|
priority: external_exports.enum(["critical", "high", "medium", "low"]).optional().describe("Task priority"),
|
|
19953
|
-
tags: external_exports.array(external_exports.string()).optional().describe("Additional tags")
|
|
20053
|
+
tags: external_exports.array(external_exports.string()).optional().describe("Additional tags"),
|
|
20054
|
+
workStream: external_exports.string().optional().describe("Work stream name (e.g. 'Budget UX'). Adds a stream:<value> tag.")
|
|
19954
20055
|
},
|
|
19955
20056
|
async (args) => {
|
|
19956
20057
|
const linkedEpics = normalizeLinkedEpics(args.linkedEpic);
|
|
@@ -19963,12 +20064,15 @@ function createTaskTools(store) {
|
|
|
19963
20064
|
warnings.push(`Warning: ${epicId} is a ${epic.frontmatter.type}, not an epic`);
|
|
19964
20065
|
}
|
|
19965
20066
|
}
|
|
20067
|
+
const baseTags = [...generateEpicTags(linkedEpics), ...args.tags ?? []];
|
|
20068
|
+
if (args.workStream) baseTags.push(`stream:${args.workStream}`);
|
|
19966
20069
|
const frontmatter = {
|
|
19967
20070
|
title: args.title,
|
|
19968
20071
|
status: args.status ?? "backlog",
|
|
19969
20072
|
linkedEpic: linkedEpics,
|
|
19970
|
-
tags:
|
|
20073
|
+
tags: baseTags
|
|
19971
20074
|
};
|
|
20075
|
+
if (args.aboutArtifact) frontmatter.aboutArtifact = args.aboutArtifact;
|
|
19972
20076
|
if (args.acceptanceCriteria) frontmatter.acceptanceCriteria = args.acceptanceCriteria;
|
|
19973
20077
|
if (args.technicalNotes) frontmatter.technicalNotes = args.technicalNotes;
|
|
19974
20078
|
if (args.estimatedPoints !== void 0) frontmatter.estimatedPoints = args.estimatedPoints;
|
|
@@ -19992,6 +20096,7 @@ function createTaskTools(store) {
|
|
|
19992
20096
|
{
|
|
19993
20097
|
id: external_exports.string().describe("Task ID to update"),
|
|
19994
20098
|
title: external_exports.string().optional().describe("New title"),
|
|
20099
|
+
aboutArtifact: external_exports.string().optional().describe("Parent artifact this task derives from (e.g. 'A-001')"),
|
|
19995
20100
|
status: external_exports.enum(["backlog", "ready", "in-progress", "review", "done"]).optional().describe("New status"),
|
|
19996
20101
|
content: external_exports.string().optional().describe("New content"),
|
|
19997
20102
|
linkedEpic: linkedEpicArray.optional().describe("New linked epic ID(s)"),
|
|
@@ -20000,10 +20105,11 @@ function createTaskTools(store) {
|
|
|
20000
20105
|
estimatedPoints: external_exports.number().optional().describe("New story point estimate"),
|
|
20001
20106
|
complexity: external_exports.enum(["trivial", "simple", "moderate", "complex", "very-complex"]).optional().describe("New complexity"),
|
|
20002
20107
|
priority: external_exports.enum(["critical", "high", "medium", "low"]).optional().describe("New priority"),
|
|
20003
|
-
tags: external_exports.array(external_exports.string()).optional().describe("Replace tags (e.g. remove old tags, add new ones)")
|
|
20108
|
+
tags: external_exports.array(external_exports.string()).optional().describe("Replace tags (e.g. remove old tags, add new ones)"),
|
|
20109
|
+
workStream: external_exports.string().optional().describe("Work stream name (e.g. 'Budget UX'). Replaces existing stream:<value> tag.")
|
|
20004
20110
|
},
|
|
20005
20111
|
async (args) => {
|
|
20006
|
-
const { id, content, linkedEpic: rawLinkedEpic, tags: userTags, ...updates } = args;
|
|
20112
|
+
const { id, content, linkedEpic: rawLinkedEpic, tags: userTags, workStream, ...updates } = args;
|
|
20007
20113
|
const warnings = [];
|
|
20008
20114
|
if (rawLinkedEpic !== void 0) {
|
|
20009
20115
|
const linkedEpics = normalizeLinkedEpics(rawLinkedEpic);
|
|
@@ -20024,6 +20130,12 @@ function createTaskTools(store) {
|
|
|
20024
20130
|
} else if (userTags !== void 0) {
|
|
20025
20131
|
updates.tags = userTags;
|
|
20026
20132
|
}
|
|
20133
|
+
if (workStream !== void 0) {
|
|
20134
|
+
const currentTags = updates.tags ?? store.get(id)?.frontmatter.tags ?? [];
|
|
20135
|
+
const filtered = currentTags.filter((t) => !t.startsWith("stream:"));
|
|
20136
|
+
filtered.push(`stream:${workStream}`);
|
|
20137
|
+
updates.tags = filtered;
|
|
20138
|
+
}
|
|
20027
20139
|
const doc = store.update(id, updates, content);
|
|
20028
20140
|
const parts = [`Updated task ${doc.frontmatter.id}: ${doc.frontmatter.title}`];
|
|
20029
20141
|
if (warnings.length > 0) {
|
|
@@ -26165,7 +26277,7 @@ function createProgram() {
|
|
|
26165
26277
|
const program = new Command();
|
|
26166
26278
|
program.name("marvin").description(
|
|
26167
26279
|
"AI-powered product development assistant with Product Owner, Delivery Manager, and Technical Lead personas"
|
|
26168
|
-
).version("0.4.
|
|
26280
|
+
).version("0.4.9");
|
|
26169
26281
|
program.command("init").description("Initialize a new Marvin project in the current directory").action(async () => {
|
|
26170
26282
|
await initCommand();
|
|
26171
26283
|
});
|