mrvn-cli 0.5.12 → 0.5.14
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/dist/index.js +492 -28
- package/dist/index.js.map +1 -1
- package/dist/marvin-serve.js +516 -52
- package/dist/marvin-serve.js.map +1 -1
- package/dist/marvin.js +492 -28
- package/dist/marvin.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -223,9 +223,9 @@ var DocumentStore = class {
|
|
|
223
223
|
}
|
|
224
224
|
}
|
|
225
225
|
}
|
|
226
|
-
list(
|
|
226
|
+
list(query10) {
|
|
227
227
|
const results = [];
|
|
228
|
-
const types =
|
|
228
|
+
const types = query10?.type ? [query10.type] : Object.keys(this.typeDirs);
|
|
229
229
|
for (const type of types) {
|
|
230
230
|
const dirName = this.typeDirs[type];
|
|
231
231
|
if (!dirName) continue;
|
|
@@ -236,9 +236,9 @@ var DocumentStore = class {
|
|
|
236
236
|
const filePath = path3.join(dir, file2);
|
|
237
237
|
const raw = fs3.readFileSync(filePath, "utf-8");
|
|
238
238
|
const doc = parseDocument(raw, filePath);
|
|
239
|
-
if (
|
|
240
|
-
if (
|
|
241
|
-
if (
|
|
239
|
+
if (query10?.status && doc.frontmatter.status !== query10.status) continue;
|
|
240
|
+
if (query10?.owner && doc.frontmatter.owner !== query10.owner) continue;
|
|
241
|
+
if (query10?.tag && (!doc.frontmatter.tags || !doc.frontmatter.tags.includes(query10.tag)))
|
|
242
242
|
continue;
|
|
243
243
|
results.push(doc);
|
|
244
244
|
}
|
|
@@ -15740,9 +15740,14 @@ function collectSprintSummaryData(store, sprintId) {
|
|
|
15740
15740
|
};
|
|
15741
15741
|
});
|
|
15742
15742
|
const sprintTag = `sprint:${fm.id}`;
|
|
15743
|
-
const
|
|
15743
|
+
const sprintTaggedDocs = allDocs.filter(
|
|
15744
15744
|
(d) => d.frontmatter.type !== "sprint" && d.frontmatter.type !== "epic" && d.frontmatter.type !== "meeting" && d.frontmatter.type !== "decision" && d.frontmatter.type !== "question" && d.frontmatter.tags?.includes(sprintTag)
|
|
15745
15745
|
);
|
|
15746
|
+
const sprintTaggedIds = new Set(sprintTaggedDocs.map((d) => d.frontmatter.id));
|
|
15747
|
+
const orphanContributions = allDocs.filter(
|
|
15748
|
+
(d) => d.frontmatter.type === "contribution" && !sprintTaggedIds.has(d.frontmatter.id) && d.frontmatter.aboutArtifact && sprintTaggedIds.has(d.frontmatter.aboutArtifact)
|
|
15749
|
+
);
|
|
15750
|
+
const workItemDocs = [...sprintTaggedDocs, ...orphanContributions];
|
|
15746
15751
|
const primaryDocs = workItemDocs.filter((d) => d.frontmatter.type !== "contribution");
|
|
15747
15752
|
const byStatus = {};
|
|
15748
15753
|
const byType = {};
|
|
@@ -15761,7 +15766,7 @@ function collectSprintSummaryData(store, sprintId) {
|
|
|
15761
15766
|
}
|
|
15762
15767
|
const allItemsById = /* @__PURE__ */ new Map();
|
|
15763
15768
|
const childrenByParent = /* @__PURE__ */ new Map();
|
|
15764
|
-
const
|
|
15769
|
+
const workItemIds = new Set(workItemDocs.map((d) => d.frontmatter.id));
|
|
15765
15770
|
for (const doc of workItemDocs) {
|
|
15766
15771
|
const about = doc.frontmatter.aboutArtifact;
|
|
15767
15772
|
const focusTag = (doc.frontmatter.tags ?? []).find((t) => t.startsWith("focus:"));
|
|
@@ -15780,7 +15785,7 @@ function collectSprintSummaryData(store, sprintId) {
|
|
|
15780
15785
|
confluenceTitle: doc.frontmatter.confluenceTitle
|
|
15781
15786
|
};
|
|
15782
15787
|
allItemsById.set(item.id, item);
|
|
15783
|
-
if (about &&
|
|
15788
|
+
if (about && workItemIds.has(about)) {
|
|
15784
15789
|
if (!childrenByParent.has(about)) childrenByParent.set(about, []);
|
|
15785
15790
|
childrenByParent.get(about).push(item);
|
|
15786
15791
|
}
|
|
@@ -18182,9 +18187,9 @@ tr:hover td {
|
|
|
18182
18187
|
.integration-icons {
|
|
18183
18188
|
display: inline-flex;
|
|
18184
18189
|
align-items: center;
|
|
18185
|
-
gap: 0.
|
|
18190
|
+
gap: 0.25rem;
|
|
18186
18191
|
vertical-align: middle;
|
|
18187
|
-
margin-left: 0.
|
|
18192
|
+
margin-left: 0.5rem;
|
|
18188
18193
|
}
|
|
18189
18194
|
.integration-link {
|
|
18190
18195
|
display: inline-flex;
|
|
@@ -19438,7 +19443,7 @@ function poBacklogPage(ctx) {
|
|
|
19438
19443
|
}
|
|
19439
19444
|
}
|
|
19440
19445
|
}
|
|
19441
|
-
const
|
|
19446
|
+
const DONE_STATUSES17 = /* @__PURE__ */ new Set(["done", "closed", "resolved", "cancelled"]);
|
|
19442
19447
|
function featureTaskStats(featureId) {
|
|
19443
19448
|
const fEpics = featureToEpics.get(featureId) ?? [];
|
|
19444
19449
|
let total = 0;
|
|
@@ -19447,7 +19452,7 @@ function poBacklogPage(ctx) {
|
|
|
19447
19452
|
for (const epic of fEpics) {
|
|
19448
19453
|
for (const t of epicToTasks.get(epic.frontmatter.id) ?? []) {
|
|
19449
19454
|
total++;
|
|
19450
|
-
if (
|
|
19455
|
+
if (DONE_STATUSES17.has(t.frontmatter.status)) done++;
|
|
19451
19456
|
progressSum += getEffectiveProgress(t.frontmatter);
|
|
19452
19457
|
}
|
|
19453
19458
|
}
|
|
@@ -25105,6 +25110,11 @@ function mapJiraStatusForTask(status, configMap) {
|
|
|
25105
25110
|
const lookup = buildStatusLookup(configMap, DEFAULT_TASK_STATUS_MAP);
|
|
25106
25111
|
return lookup.get(status.toLowerCase()) ?? "backlog";
|
|
25107
25112
|
}
|
|
25113
|
+
function extractJiraKeyFromTags(tags) {
|
|
25114
|
+
if (!tags) return void 0;
|
|
25115
|
+
const tag = tags.find((t) => /^jira:[A-Z]+-\d+$/i.test(t));
|
|
25116
|
+
return tag ? tag.slice(5) : void 0;
|
|
25117
|
+
}
|
|
25108
25118
|
function computeSubtaskProgress(subtasks) {
|
|
25109
25119
|
if (subtasks.length === 0) return 0;
|
|
25110
25120
|
const done = subtasks.filter(
|
|
@@ -25487,10 +25497,10 @@ async function fetchJiraDaily(store, client, host, projectKey, dateRange, status
|
|
|
25487
25497
|
jiraKeyToArtifacts.set(jk, list);
|
|
25488
25498
|
}
|
|
25489
25499
|
}
|
|
25490
|
-
const
|
|
25500
|
+
const BATCH_SIZE2 = 5;
|
|
25491
25501
|
const issues = searchResult.issues;
|
|
25492
|
-
for (let i = 0; i < issues.length; i +=
|
|
25493
|
-
const batch = issues.slice(i, i +
|
|
25502
|
+
for (let i = 0; i < issues.length; i += BATCH_SIZE2) {
|
|
25503
|
+
const batch = issues.slice(i, i + BATCH_SIZE2);
|
|
25494
25504
|
const results = await Promise.allSettled(
|
|
25495
25505
|
batch.map(
|
|
25496
25506
|
(issue2) => processIssue(issue2, client, host, dateRange, jiraKeyToArtifacts, allDocs, statusMap)
|
|
@@ -25714,6 +25724,420 @@ function generateProposedActions(issues) {
|
|
|
25714
25724
|
return actions;
|
|
25715
25725
|
}
|
|
25716
25726
|
|
|
25727
|
+
// src/skills/builtin/jira/sprint-progress.ts
|
|
25728
|
+
import { query as query3 } from "@anthropic-ai/claude-agent-sdk";
|
|
25729
|
+
var DONE_STATUSES16 = /* @__PURE__ */ new Set(["done", "closed", "resolved", "obsolete", "wont do", "cancelled"]);
|
|
25730
|
+
var BATCH_SIZE = 5;
|
|
25731
|
+
async function assessSprintProgress(store, client, host, options = {}) {
|
|
25732
|
+
const errors = [];
|
|
25733
|
+
const sprintData = collectSprintSummaryData(store, options.sprintId);
|
|
25734
|
+
if (!sprintData) {
|
|
25735
|
+
return {
|
|
25736
|
+
sprintId: options.sprintId ?? "unknown",
|
|
25737
|
+
sprintTitle: "Sprint not found",
|
|
25738
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
25739
|
+
timeline: { startDate: null, endDate: null, daysRemaining: 0, totalDays: 0, percentComplete: 0 },
|
|
25740
|
+
overallProgress: 0,
|
|
25741
|
+
itemReports: [],
|
|
25742
|
+
focusAreas: [],
|
|
25743
|
+
driftItems: [],
|
|
25744
|
+
blockers: [],
|
|
25745
|
+
proposedUpdates: [],
|
|
25746
|
+
appliedUpdates: [],
|
|
25747
|
+
errors: [`Sprint ${options.sprintId ?? "(active)"} not found. Create a sprint artifact first.`]
|
|
25748
|
+
};
|
|
25749
|
+
}
|
|
25750
|
+
const sprintTag = `sprint:${sprintData.sprint.id}`;
|
|
25751
|
+
const actions = store.list({ type: "action", tag: sprintTag });
|
|
25752
|
+
const tasks = store.list({ type: "task", tag: sprintTag });
|
|
25753
|
+
const sprintItemIds = new Set([...actions, ...tasks].map((d) => d.frontmatter.id));
|
|
25754
|
+
const allTasks = store.list({ type: "task" });
|
|
25755
|
+
const allActions = store.list({ type: "action" });
|
|
25756
|
+
const nestedTasks = allTasks.filter(
|
|
25757
|
+
(d) => !sprintItemIds.has(d.frontmatter.id) && d.frontmatter.aboutArtifact && sprintItemIds.has(d.frontmatter.aboutArtifact)
|
|
25758
|
+
);
|
|
25759
|
+
const nestedActions = allActions.filter(
|
|
25760
|
+
(d) => !sprintItemIds.has(d.frontmatter.id) && d.frontmatter.aboutArtifact && sprintItemIds.has(d.frontmatter.aboutArtifact)
|
|
25761
|
+
);
|
|
25762
|
+
const allItems = [...actions, ...tasks, ...nestedTasks, ...nestedActions];
|
|
25763
|
+
const itemJiraKeys = /* @__PURE__ */ new Map();
|
|
25764
|
+
for (const doc of allItems) {
|
|
25765
|
+
const jiraKey = doc.frontmatter.jiraKey ?? extractJiraKeyFromTags(doc.frontmatter.tags);
|
|
25766
|
+
if (jiraKey) {
|
|
25767
|
+
itemJiraKeys.set(doc.frontmatter.id, jiraKey);
|
|
25768
|
+
}
|
|
25769
|
+
}
|
|
25770
|
+
const jiraKeys = [...new Set(itemJiraKeys.values())];
|
|
25771
|
+
const jiraIssues = /* @__PURE__ */ new Map();
|
|
25772
|
+
for (let i = 0; i < jiraKeys.length; i += BATCH_SIZE) {
|
|
25773
|
+
const batch = jiraKeys.slice(i, i + BATCH_SIZE);
|
|
25774
|
+
const results = await Promise.allSettled(
|
|
25775
|
+
batch.map(async (key) => {
|
|
25776
|
+
const [issue2, comments] = await Promise.all([
|
|
25777
|
+
client.getIssueWithLinks(key),
|
|
25778
|
+
client.getComments(key)
|
|
25779
|
+
]);
|
|
25780
|
+
return { key, issue: issue2, comments };
|
|
25781
|
+
})
|
|
25782
|
+
);
|
|
25783
|
+
for (const result of results) {
|
|
25784
|
+
if (result.status === "fulfilled") {
|
|
25785
|
+
jiraIssues.set(result.value.key, {
|
|
25786
|
+
issue: result.value.issue,
|
|
25787
|
+
comments: result.value.comments
|
|
25788
|
+
});
|
|
25789
|
+
} else {
|
|
25790
|
+
const batchKey = batch[results.indexOf(result)];
|
|
25791
|
+
errors.push(`Failed to fetch ${batchKey}: ${result.reason instanceof Error ? result.reason.message : String(result.reason)}`);
|
|
25792
|
+
}
|
|
25793
|
+
}
|
|
25794
|
+
}
|
|
25795
|
+
const proposedUpdates = [];
|
|
25796
|
+
const itemReports = [];
|
|
25797
|
+
const childReportsByParent = /* @__PURE__ */ new Map();
|
|
25798
|
+
for (const doc of allItems) {
|
|
25799
|
+
const fm = doc.frontmatter;
|
|
25800
|
+
const jiraKey = itemJiraKeys.get(fm.id) ?? null;
|
|
25801
|
+
const jiraData = jiraKey ? jiraIssues.get(jiraKey) : null;
|
|
25802
|
+
let jiraStatus = null;
|
|
25803
|
+
let proposedMarvinStatus = null;
|
|
25804
|
+
let jiraSubtaskProgress = null;
|
|
25805
|
+
const commentSignals = [];
|
|
25806
|
+
if (jiraData) {
|
|
25807
|
+
jiraStatus = jiraData.issue.fields.status.name;
|
|
25808
|
+
proposedMarvinStatus = fm.type === "task" ? mapJiraStatusForTask(jiraStatus, options.statusMap?.task) : mapJiraStatusForAction(jiraStatus, options.statusMap?.action);
|
|
25809
|
+
const subtasks = jiraData.issue.fields.subtasks ?? [];
|
|
25810
|
+
if (subtasks.length > 0) {
|
|
25811
|
+
jiraSubtaskProgress = computeSubtaskProgress(subtasks);
|
|
25812
|
+
}
|
|
25813
|
+
for (const comment of jiraData.comments) {
|
|
25814
|
+
const text = extractCommentText(comment.body);
|
|
25815
|
+
const signals = detectCommentSignals(text);
|
|
25816
|
+
commentSignals.push(...signals);
|
|
25817
|
+
}
|
|
25818
|
+
}
|
|
25819
|
+
const statusDrift = proposedMarvinStatus !== null && proposedMarvinStatus !== fm.status;
|
|
25820
|
+
const currentProgress = getEffectiveProgress(fm);
|
|
25821
|
+
const progressDrift = jiraSubtaskProgress !== null && !fm.progressOverride && jiraSubtaskProgress !== currentProgress;
|
|
25822
|
+
if (statusDrift && proposedMarvinStatus) {
|
|
25823
|
+
proposedUpdates.push({
|
|
25824
|
+
artifactId: fm.id,
|
|
25825
|
+
field: "status",
|
|
25826
|
+
currentValue: fm.status,
|
|
25827
|
+
proposedValue: proposedMarvinStatus,
|
|
25828
|
+
reason: `Jira ${jiraKey} is "${jiraStatus}" \u2192 maps to "${proposedMarvinStatus}"`
|
|
25829
|
+
});
|
|
25830
|
+
}
|
|
25831
|
+
if (progressDrift && jiraSubtaskProgress !== null) {
|
|
25832
|
+
proposedUpdates.push({
|
|
25833
|
+
artifactId: fm.id,
|
|
25834
|
+
field: "progress",
|
|
25835
|
+
currentValue: currentProgress,
|
|
25836
|
+
proposedValue: jiraSubtaskProgress,
|
|
25837
|
+
reason: `Jira ${jiraKey} subtask progress is ${jiraSubtaskProgress}%`
|
|
25838
|
+
});
|
|
25839
|
+
}
|
|
25840
|
+
const tags = fm.tags ?? [];
|
|
25841
|
+
const focusTag = tags.find((t) => t.startsWith("focus:"));
|
|
25842
|
+
const report = {
|
|
25843
|
+
id: fm.id,
|
|
25844
|
+
title: fm.title,
|
|
25845
|
+
type: fm.type,
|
|
25846
|
+
marvinStatus: fm.status,
|
|
25847
|
+
marvinProgress: currentProgress,
|
|
25848
|
+
jiraKey,
|
|
25849
|
+
jiraStatus,
|
|
25850
|
+
jiraSubtaskProgress,
|
|
25851
|
+
proposedMarvinStatus,
|
|
25852
|
+
statusDrift,
|
|
25853
|
+
progressDrift,
|
|
25854
|
+
commentSignals,
|
|
25855
|
+
commentSummary: null,
|
|
25856
|
+
children: [],
|
|
25857
|
+
owner: fm.owner ?? null,
|
|
25858
|
+
focusArea: focusTag ? focusTag.slice(6) : null
|
|
25859
|
+
};
|
|
25860
|
+
const aboutArtifact = fm.aboutArtifact;
|
|
25861
|
+
if (aboutArtifact && sprintItemIds.has(aboutArtifact)) {
|
|
25862
|
+
if (!childReportsByParent.has(aboutArtifact)) {
|
|
25863
|
+
childReportsByParent.set(aboutArtifact, []);
|
|
25864
|
+
}
|
|
25865
|
+
childReportsByParent.get(aboutArtifact).push(report);
|
|
25866
|
+
}
|
|
25867
|
+
itemReports.push(report);
|
|
25868
|
+
}
|
|
25869
|
+
for (const report of itemReports) {
|
|
25870
|
+
const children = childReportsByParent.get(report.id);
|
|
25871
|
+
if (children) {
|
|
25872
|
+
report.children = children;
|
|
25873
|
+
}
|
|
25874
|
+
}
|
|
25875
|
+
const childIds = /* @__PURE__ */ new Set();
|
|
25876
|
+
for (const children of childReportsByParent.values()) {
|
|
25877
|
+
for (const child of children) childIds.add(child.id);
|
|
25878
|
+
}
|
|
25879
|
+
const rootReports = itemReports.filter((r) => !childIds.has(r.id));
|
|
25880
|
+
const focusAreaMap = /* @__PURE__ */ new Map();
|
|
25881
|
+
for (const report of rootReports) {
|
|
25882
|
+
const area = report.focusArea ?? "Uncategorized";
|
|
25883
|
+
if (!focusAreaMap.has(area)) focusAreaMap.set(area, []);
|
|
25884
|
+
focusAreaMap.get(area).push(report);
|
|
25885
|
+
}
|
|
25886
|
+
const focusAreas = [];
|
|
25887
|
+
for (const [name, items] of focusAreaMap) {
|
|
25888
|
+
const allFlatItems = items.flatMap((i) => [i, ...i.children]);
|
|
25889
|
+
const doneCount = allFlatItems.filter((i) => DONE_STATUSES16.has(i.marvinStatus)).length;
|
|
25890
|
+
const blockedCount = allFlatItems.filter((i) => i.marvinStatus === "blocked").length;
|
|
25891
|
+
const avgProgress = allFlatItems.length > 0 ? Math.round(allFlatItems.reduce((s, i) => s + i.marvinProgress, 0) / allFlatItems.length) : 0;
|
|
25892
|
+
focusAreas.push({
|
|
25893
|
+
name,
|
|
25894
|
+
items,
|
|
25895
|
+
totalCount: allFlatItems.length,
|
|
25896
|
+
doneCount,
|
|
25897
|
+
blockedCount,
|
|
25898
|
+
avgProgress
|
|
25899
|
+
});
|
|
25900
|
+
}
|
|
25901
|
+
focusAreas.sort((a, b) => {
|
|
25902
|
+
if (a.name === "Uncategorized") return 1;
|
|
25903
|
+
if (b.name === "Uncategorized") return -1;
|
|
25904
|
+
return a.name.localeCompare(b.name);
|
|
25905
|
+
});
|
|
25906
|
+
const driftItems = itemReports.filter((r) => r.statusDrift || r.progressDrift);
|
|
25907
|
+
const blockers = itemReports.filter(
|
|
25908
|
+
(r) => r.marvinStatus === "blocked" || r.commentSignals.some((s) => s.type === "blocker")
|
|
25909
|
+
);
|
|
25910
|
+
if (options.analyzeComments) {
|
|
25911
|
+
const itemsWithComments = itemReports.filter((r) => r.commentSignals.length > 0 && r.jiraKey);
|
|
25912
|
+
if (itemsWithComments.length > 0) {
|
|
25913
|
+
try {
|
|
25914
|
+
const summaries = await analyzeCommentsForProgress(
|
|
25915
|
+
itemsWithComments,
|
|
25916
|
+
jiraIssues,
|
|
25917
|
+
itemJiraKeys
|
|
25918
|
+
);
|
|
25919
|
+
for (const [artifactId, summary] of summaries) {
|
|
25920
|
+
const report = itemReports.find((r) => r.id === artifactId);
|
|
25921
|
+
if (report) report.commentSummary = summary;
|
|
25922
|
+
}
|
|
25923
|
+
} catch (err) {
|
|
25924
|
+
errors.push(`Comment analysis failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
25925
|
+
}
|
|
25926
|
+
}
|
|
25927
|
+
}
|
|
25928
|
+
const appliedUpdates = [];
|
|
25929
|
+
if (options.applyUpdates && proposedUpdates.length > 0) {
|
|
25930
|
+
for (const update of proposedUpdates) {
|
|
25931
|
+
try {
|
|
25932
|
+
store.update(update.artifactId, {
|
|
25933
|
+
[update.field]: update.proposedValue,
|
|
25934
|
+
lastJiraSyncAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
25935
|
+
});
|
|
25936
|
+
const doc = store.get(update.artifactId);
|
|
25937
|
+
if (doc) {
|
|
25938
|
+
if (doc.frontmatter.type === "task") {
|
|
25939
|
+
propagateProgressFromTask(store, update.artifactId);
|
|
25940
|
+
} else if (doc.frontmatter.type === "action") {
|
|
25941
|
+
propagateProgressToAction(store, update.artifactId);
|
|
25942
|
+
}
|
|
25943
|
+
}
|
|
25944
|
+
appliedUpdates.push(update);
|
|
25945
|
+
} catch (err) {
|
|
25946
|
+
errors.push(
|
|
25947
|
+
`Failed to apply update to ${update.artifactId}: ${err instanceof Error ? err.message : String(err)}`
|
|
25948
|
+
);
|
|
25949
|
+
}
|
|
25950
|
+
}
|
|
25951
|
+
}
|
|
25952
|
+
return {
|
|
25953
|
+
sprintId: sprintData.sprint.id,
|
|
25954
|
+
sprintTitle: sprintData.sprint.title,
|
|
25955
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
25956
|
+
timeline: {
|
|
25957
|
+
startDate: sprintData.sprint.startDate ?? null,
|
|
25958
|
+
endDate: sprintData.sprint.endDate ?? null,
|
|
25959
|
+
daysRemaining: sprintData.timeline.daysRemaining,
|
|
25960
|
+
totalDays: sprintData.timeline.totalDays,
|
|
25961
|
+
percentComplete: sprintData.timeline.percentComplete
|
|
25962
|
+
},
|
|
25963
|
+
overallProgress: sprintData.workItems.completionPct,
|
|
25964
|
+
itemReports: rootReports,
|
|
25965
|
+
focusAreas,
|
|
25966
|
+
driftItems,
|
|
25967
|
+
blockers,
|
|
25968
|
+
proposedUpdates: options.applyUpdates ? [] : proposedUpdates,
|
|
25969
|
+
appliedUpdates,
|
|
25970
|
+
errors
|
|
25971
|
+
};
|
|
25972
|
+
}
|
|
25973
|
+
var COMMENT_ANALYSIS_PROMPT = `You are a delivery management assistant analyzing Jira comments for progress signals.
|
|
25974
|
+
|
|
25975
|
+
For each item below, read the Jira comments and produce a 1-2 sentence progress summary.
|
|
25976
|
+
Focus on: what work was done, what's pending, any blockers or decisions mentioned.
|
|
25977
|
+
|
|
25978
|
+
Return your response as a JSON object mapping artifact IDs to summary strings.
|
|
25979
|
+
Example: {"T-001": "Backend API completed and deployed. Frontend integration pending review.", "A-003": "Blocked on infrastructure team approval."}
|
|
25980
|
+
|
|
25981
|
+
IMPORTANT: Only return the JSON object, no other text.`;
|
|
25982
|
+
async function analyzeCommentsForProgress(items, jiraIssues, itemJiraKeys) {
|
|
25983
|
+
const summaries = /* @__PURE__ */ new Map();
|
|
25984
|
+
const MAX_ITEMS_PER_CALL = 20;
|
|
25985
|
+
const itemsToAnalyze = items.slice(0, MAX_ITEMS_PER_CALL);
|
|
25986
|
+
const promptParts = [];
|
|
25987
|
+
for (const item of itemsToAnalyze) {
|
|
25988
|
+
const jiraKey = itemJiraKeys.get(item.id);
|
|
25989
|
+
if (!jiraKey) continue;
|
|
25990
|
+
const jiraData = jiraIssues.get(jiraKey);
|
|
25991
|
+
if (!jiraData || jiraData.comments.length === 0) continue;
|
|
25992
|
+
const commentTexts = jiraData.comments.map((c) => {
|
|
25993
|
+
const text = extractCommentText(c.body);
|
|
25994
|
+
return ` [${c.author.displayName}, ${c.created.slice(0, 10)}]: ${text.slice(0, 500)}`;
|
|
25995
|
+
}).join("\n");
|
|
25996
|
+
promptParts.push(`## ${item.id} \u2014 ${item.title} (${jiraKey}, Jira status: ${item.jiraStatus})
|
|
25997
|
+
Comments:
|
|
25998
|
+
${commentTexts}`);
|
|
25999
|
+
}
|
|
26000
|
+
if (promptParts.length === 0) return summaries;
|
|
26001
|
+
const prompt = promptParts.join("\n\n");
|
|
26002
|
+
const result = query3({
|
|
26003
|
+
prompt,
|
|
26004
|
+
options: {
|
|
26005
|
+
systemPrompt: COMMENT_ANALYSIS_PROMPT,
|
|
26006
|
+
maxTurns: 1,
|
|
26007
|
+
tools: [],
|
|
26008
|
+
allowedTools: []
|
|
26009
|
+
}
|
|
26010
|
+
});
|
|
26011
|
+
for await (const msg of result) {
|
|
26012
|
+
if (msg.type === "assistant") {
|
|
26013
|
+
const textBlock = msg.message.content.find(
|
|
26014
|
+
(b) => b.type === "text"
|
|
26015
|
+
);
|
|
26016
|
+
if (textBlock) {
|
|
26017
|
+
try {
|
|
26018
|
+
const parsed = JSON.parse(textBlock.text);
|
|
26019
|
+
for (const [id, summary] of Object.entries(parsed)) {
|
|
26020
|
+
if (typeof summary === "string") {
|
|
26021
|
+
summaries.set(id, summary);
|
|
26022
|
+
}
|
|
26023
|
+
}
|
|
26024
|
+
} catch {
|
|
26025
|
+
const match = textBlock.text.match(/```(?:json)?\s*([\s\S]*?)```/);
|
|
26026
|
+
if (match) {
|
|
26027
|
+
try {
|
|
26028
|
+
const parsed = JSON.parse(match[1]);
|
|
26029
|
+
for (const [id, summary] of Object.entries(parsed)) {
|
|
26030
|
+
if (typeof summary === "string") {
|
|
26031
|
+
summaries.set(id, summary);
|
|
26032
|
+
}
|
|
26033
|
+
}
|
|
26034
|
+
} catch {
|
|
26035
|
+
}
|
|
26036
|
+
}
|
|
26037
|
+
}
|
|
26038
|
+
}
|
|
26039
|
+
}
|
|
26040
|
+
}
|
|
26041
|
+
return summaries;
|
|
26042
|
+
}
|
|
26043
|
+
function formatProgressReport(report) {
|
|
26044
|
+
const parts = [];
|
|
26045
|
+
parts.push(`# Sprint Progress Assessment \u2014 ${report.sprintId}`);
|
|
26046
|
+
parts.push(`${report.sprintTitle}`);
|
|
26047
|
+
parts.push(`Generated: ${report.generatedAt.slice(0, 16)}`);
|
|
26048
|
+
parts.push("");
|
|
26049
|
+
if (report.timeline.startDate && report.timeline.endDate) {
|
|
26050
|
+
parts.push(`## Timeline`);
|
|
26051
|
+
parts.push(`${report.timeline.startDate} \u2192 ${report.timeline.endDate}`);
|
|
26052
|
+
parts.push(`Days remaining: ${report.timeline.daysRemaining} / ${report.timeline.totalDays} (${report.timeline.percentComplete}% elapsed)`);
|
|
26053
|
+
parts.push(`Overall progress: ${report.overallProgress}%`);
|
|
26054
|
+
parts.push("");
|
|
26055
|
+
}
|
|
26056
|
+
if (report.focusAreas.length > 0) {
|
|
26057
|
+
parts.push(`## Focus Areas`);
|
|
26058
|
+
parts.push("");
|
|
26059
|
+
for (const area of report.focusAreas) {
|
|
26060
|
+
const bar = progressBar6(area.avgProgress);
|
|
26061
|
+
parts.push(`### ${area.name} ${bar} ${area.avgProgress}%`);
|
|
26062
|
+
parts.push(`${area.doneCount}/${area.totalCount} done${area.blockedCount > 0 ? ` | ${area.blockedCount} blocked` : ""}`);
|
|
26063
|
+
parts.push("");
|
|
26064
|
+
for (const item of area.items) {
|
|
26065
|
+
formatItemLine(parts, item, 0);
|
|
26066
|
+
}
|
|
26067
|
+
parts.push("");
|
|
26068
|
+
}
|
|
26069
|
+
}
|
|
26070
|
+
if (report.driftItems.length > 0) {
|
|
26071
|
+
parts.push(`## Status Drift (${report.driftItems.length} items)`);
|
|
26072
|
+
for (const item of report.driftItems) {
|
|
26073
|
+
const driftParts = [];
|
|
26074
|
+
if (item.statusDrift) {
|
|
26075
|
+
driftParts.push(`status: ${item.marvinStatus} \u2192 ${item.proposedMarvinStatus}`);
|
|
26076
|
+
}
|
|
26077
|
+
if (item.progressDrift && item.jiraSubtaskProgress !== null) {
|
|
26078
|
+
driftParts.push(`progress: ${item.marvinProgress}% \u2192 ${item.jiraSubtaskProgress}%`);
|
|
26079
|
+
}
|
|
26080
|
+
parts.push(` \u26A0 ${item.id} (${item.jiraKey}) \u2014 ${driftParts.join(", ")}`);
|
|
26081
|
+
}
|
|
26082
|
+
parts.push("");
|
|
26083
|
+
}
|
|
26084
|
+
if (report.blockers.length > 0) {
|
|
26085
|
+
parts.push(`## Blockers (${report.blockers.length})`);
|
|
26086
|
+
for (const item of report.blockers) {
|
|
26087
|
+
const blockerSignals = item.commentSignals.filter((s) => s.type === "blocker");
|
|
26088
|
+
parts.push(` \u{1F6AB} ${item.id} \u2014 ${item.title}${item.jiraKey ? ` (${item.jiraKey})` : ""}`);
|
|
26089
|
+
for (const signal of blockerSignals) {
|
|
26090
|
+
parts.push(` "${signal.snippet}"`);
|
|
26091
|
+
}
|
|
26092
|
+
}
|
|
26093
|
+
parts.push("");
|
|
26094
|
+
}
|
|
26095
|
+
if (report.proposedUpdates.length > 0) {
|
|
26096
|
+
parts.push(`## Proposed Updates (${report.proposedUpdates.length})`);
|
|
26097
|
+
for (const update of report.proposedUpdates) {
|
|
26098
|
+
parts.push(` ${update.artifactId}.${update.field}: ${String(update.currentValue)} \u2192 ${String(update.proposedValue)}`);
|
|
26099
|
+
parts.push(` Reason: ${update.reason}`);
|
|
26100
|
+
}
|
|
26101
|
+
parts.push("");
|
|
26102
|
+
parts.push("Run with applyUpdates=true to apply these changes.");
|
|
26103
|
+
parts.push("");
|
|
26104
|
+
}
|
|
26105
|
+
if (report.appliedUpdates.length > 0) {
|
|
26106
|
+
parts.push(`## Applied Updates (${report.appliedUpdates.length})`);
|
|
26107
|
+
for (const update of report.appliedUpdates) {
|
|
26108
|
+
parts.push(` \u2713 ${update.artifactId}.${update.field}: ${String(update.currentValue)} \u2192 ${String(update.proposedValue)}`);
|
|
26109
|
+
}
|
|
26110
|
+
parts.push("");
|
|
26111
|
+
}
|
|
26112
|
+
if (report.errors.length > 0) {
|
|
26113
|
+
parts.push(`## Errors`);
|
|
26114
|
+
for (const err of report.errors) {
|
|
26115
|
+
parts.push(` ${err}`);
|
|
26116
|
+
}
|
|
26117
|
+
parts.push("");
|
|
26118
|
+
}
|
|
26119
|
+
return parts.join("\n");
|
|
26120
|
+
}
|
|
26121
|
+
function formatItemLine(parts, item, depth) {
|
|
26122
|
+
const indent = " ".repeat(depth + 1);
|
|
26123
|
+
const statusIcon = DONE_STATUSES16.has(item.marvinStatus) ? "\u2713" : item.marvinStatus === "blocked" ? "\u{1F6AB}" : item.marvinStatus === "in-progress" ? "\u25B6" : "\u25CB";
|
|
26124
|
+
const jiraLabel = item.jiraKey ? ` [${item.jiraKey}: ${item.jiraStatus}]` : "";
|
|
26125
|
+
const driftFlag = item.statusDrift ? " \u26A0drift" : "";
|
|
26126
|
+
const progressLabel = item.marvinProgress > 0 ? ` ${item.marvinProgress}%` : "";
|
|
26127
|
+
parts.push(`${indent}${statusIcon} ${item.id} \u2014 ${item.title} [${item.marvinStatus}]${progressLabel}${jiraLabel}${driftFlag}`);
|
|
26128
|
+
if (item.commentSummary) {
|
|
26129
|
+
parts.push(`${indent} \u{1F4AC} ${item.commentSummary}`);
|
|
26130
|
+
}
|
|
26131
|
+
for (const child of item.children) {
|
|
26132
|
+
formatItemLine(parts, child, depth + 1);
|
|
26133
|
+
}
|
|
26134
|
+
}
|
|
26135
|
+
function progressBar6(pct) {
|
|
26136
|
+
const filled = Math.round(pct / 10);
|
|
26137
|
+
const empty = 10 - filled;
|
|
26138
|
+
return `[${"\u2588".repeat(filled)}${"\u2591".repeat(empty)}]`;
|
|
26139
|
+
}
|
|
26140
|
+
|
|
25717
26141
|
// src/skills/builtin/jira/tools.ts
|
|
25718
26142
|
var JIRA_TYPE = "jira-issue";
|
|
25719
26143
|
function jiraNotConfiguredError() {
|
|
@@ -26479,6 +26903,36 @@ function createJiraTools(store, projectConfig) {
|
|
|
26479
26903
|
};
|
|
26480
26904
|
},
|
|
26481
26905
|
{ annotations: { readOnlyHint: true } }
|
|
26906
|
+
),
|
|
26907
|
+
// --- Sprint progress assessment ---
|
|
26908
|
+
tool20(
|
|
26909
|
+
"assess_sprint_progress",
|
|
26910
|
+
"Assess sprint progress by fetching live Jira statuses for all sprint-scoped items, detecting drift between Marvin and Jira, grouping by focus area with rollup progress, and extracting comment signals. Optionally applies updates and uses LLM for comment analysis.",
|
|
26911
|
+
{
|
|
26912
|
+
sprintId: external_exports.string().optional().describe("Sprint ID (e.g. 'SP-001'). Defaults to active sprint."),
|
|
26913
|
+
analyzeComments: external_exports.boolean().optional().describe("Use LLM to summarize Jira comments for progress signals (default false)"),
|
|
26914
|
+
applyUpdates: external_exports.boolean().optional().describe("Apply proposed status/progress updates to Marvin artifacts (default false)")
|
|
26915
|
+
},
|
|
26916
|
+
async (args) => {
|
|
26917
|
+
const jira = createJiraClient(jiraUserConfig);
|
|
26918
|
+
if (!jira) return jiraNotConfiguredError();
|
|
26919
|
+
const report = await assessSprintProgress(
|
|
26920
|
+
store,
|
|
26921
|
+
jira.client,
|
|
26922
|
+
jira.host,
|
|
26923
|
+
{
|
|
26924
|
+
sprintId: args.sprintId,
|
|
26925
|
+
analyzeComments: args.analyzeComments ?? false,
|
|
26926
|
+
applyUpdates: args.applyUpdates ?? false,
|
|
26927
|
+
statusMap
|
|
26928
|
+
}
|
|
26929
|
+
);
|
|
26930
|
+
return {
|
|
26931
|
+
content: [{ type: "text", text: formatProgressReport(report) }],
|
|
26932
|
+
isError: report.errors.length > 0 && report.itemReports.length === 0
|
|
26933
|
+
};
|
|
26934
|
+
},
|
|
26935
|
+
{ annotations: { readOnlyHint: false } }
|
|
26482
26936
|
)
|
|
26483
26937
|
];
|
|
26484
26938
|
}
|
|
@@ -26580,6 +27034,7 @@ var COMMON_TOOLS = `**Available tools:**
|
|
|
26580
27034
|
- \`read_confluence_page\` \u2014 **read-only**: fetch and return the content of a Confluence page by URL or page ID. Use this to review Confluence content for updating tasks, generating contributions, or answering questions.
|
|
26581
27035
|
- \`fetch_jira_status\` \u2014 **read-only**: fetch current Jira status, subtask progress, and linked issues for Jira-linked actions/tasks. Returns proposed changes without applying them.
|
|
26582
27036
|
- \`fetch_jira_daily\` \u2014 **read-only**: fetch a daily/range summary of all Jira changes \u2014 status transitions, comments, linked Confluence pages, and cross-references with Marvin artifacts. Returns proposed actions (status updates, unlinked issues, question candidates, Confluence pages to review).
|
|
27037
|
+
- \`assess_sprint_progress\` \u2014 fetch live Jira statuses for all sprint-scoped items, detect drift, group by focus area with rollup progress, and extract comment signals. Use \`analyzeComments=true\` for LLM summaries, \`applyUpdates=true\` to apply changes.
|
|
26583
27038
|
- \`fetch_jira_statuses\` \u2014 **read-only**: discover all Jira statuses in a project and show their Marvin mappings (mapped vs unmapped).
|
|
26584
27039
|
- \`search_jira\` \u2014 **read-only**: search Jira via JQL and return results with Marvin cross-references. No documents created \u2014 use to preview before importing or find issues for linking.
|
|
26585
27040
|
- \`pull_jira_issue\` / \`pull_jira_issues_jql\` \u2014 import Jira issues as local JI-xxx documents (for Jira-originated items with no existing Marvin artifact).
|
|
@@ -26591,6 +27046,11 @@ var COMMON_WORKFLOW = `**Jira sync workflow:**
|
|
|
26591
27046
|
2. Analyze the proposed changes (status transitions, subtask progress, blockers from linked issues)
|
|
26592
27047
|
3. Use \`update_action\` / \`update_task\` to apply the changes you agree with
|
|
26593
27048
|
|
|
27049
|
+
**Sprint progress workflow:**
|
|
27050
|
+
1. Call \`assess_sprint_progress\` to get a comprehensive view of all sprint items with live Jira data
|
|
27051
|
+
2. Review focus area rollups, status drift, and blockers
|
|
27052
|
+
3. Optionally run with \`applyUpdates=true\` to bulk-sync statuses, or \`analyzeComments=true\` for LLM-powered comment summaries
|
|
27053
|
+
|
|
26594
27054
|
**Daily review workflow:**
|
|
26595
27055
|
1. Call \`fetch_jira_daily\` (optionally with \`from\`/\`to\` date range) to get a summary of all Jira activity
|
|
26596
27056
|
2. Review the proposed actions: status updates, unlinked issues to track, questions that may be answered, Confluence pages to review
|
|
@@ -26614,6 +27074,7 @@ ${COMMON_WORKFLOW}
|
|
|
26614
27074
|
|
|
26615
27075
|
**As Product Owner, use Jira integration to:**
|
|
26616
27076
|
- Use \`fetch_jira_daily\` for daily standups \u2014 review what changed, identify status drift, spot untracked work
|
|
27077
|
+
- Use \`assess_sprint_progress\` for sprint reviews \u2014 see overall progress by focus area, detect drift, and identify blockers
|
|
26617
27078
|
- Pull stakeholder-reported issues for triage and prioritization
|
|
26618
27079
|
- Push approved features as Stories for development tracking
|
|
26619
27080
|
- Link decisions to Jira issues for audit trail and traceability
|
|
@@ -26626,6 +27087,7 @@ ${COMMON_WORKFLOW}
|
|
|
26626
27087
|
|
|
26627
27088
|
**As Tech Lead, use Jira integration to:**
|
|
26628
27089
|
- Use \`fetch_jira_daily\` to review technical progress \u2014 status transitions, new comments, Confluence design docs
|
|
27090
|
+
- Use \`assess_sprint_progress\` for sprint health checks \u2014 focus area rollups, Jira drift detection, blocker tracking
|
|
26629
27091
|
- Pull technical issues and bugs for sprint planning and estimation
|
|
26630
27092
|
- Push epics, tasks, and technical decisions to Jira for cross-team visibility
|
|
26631
27093
|
- Use \`link_to_jira\` to connect Marvin tasks to existing Jira tickets
|
|
@@ -26639,6 +27101,8 @@ This is a third path for progress tracking alongside Contributions and Meetings.
|
|
|
26639
27101
|
|
|
26640
27102
|
**As Delivery Manager, use Jira integration to:**
|
|
26641
27103
|
- Use \`fetch_jira_daily\` for daily progress reports \u2014 track what moved, identify blockers, spot untracked work
|
|
27104
|
+
- Use \`assess_sprint_progress\` for sprint reviews and stakeholder updates \u2014 comprehensive progress by focus area with Jira enrichment
|
|
27105
|
+
- Use \`assess_sprint_progress\` with \`applyUpdates=true\` to bulk-sync Marvin statuses from Jira
|
|
26642
27106
|
- Pull sprint issues for tracking progress and blockers
|
|
26643
27107
|
- Push actions and tasks to Jira for stakeholder visibility
|
|
26644
27108
|
- Use \`fetch_jira_daily\` with a date range for sprint retrospectives (e.g. \`from: "2026-03-10", to: "2026-03-21"\`)
|
|
@@ -28143,7 +28607,7 @@ import * as readline from "readline";
|
|
|
28143
28607
|
import chalk from "chalk";
|
|
28144
28608
|
import ora from "ora";
|
|
28145
28609
|
import {
|
|
28146
|
-
query as
|
|
28610
|
+
query as query5
|
|
28147
28611
|
} from "@anthropic-ai/claude-agent-sdk";
|
|
28148
28612
|
|
|
28149
28613
|
// src/storage/session-store.ts
|
|
@@ -28214,11 +28678,11 @@ var SessionStore = class {
|
|
|
28214
28678
|
};
|
|
28215
28679
|
|
|
28216
28680
|
// src/agent/session-namer.ts
|
|
28217
|
-
import { query as
|
|
28681
|
+
import { query as query4 } from "@anthropic-ai/claude-agent-sdk";
|
|
28218
28682
|
async function generateSessionName(turns) {
|
|
28219
28683
|
try {
|
|
28220
28684
|
const transcript = turns.slice(-20).map((t) => `${t.role}: ${t.content.slice(0, 200)}`).join("\n");
|
|
28221
|
-
const result =
|
|
28685
|
+
const result = query4({
|
|
28222
28686
|
prompt: `Summarize this conversation in 3-5 words as a kebab-case name suitable for a filename. Output ONLY the name, nothing else.
|
|
28223
28687
|
|
|
28224
28688
|
${transcript}`,
|
|
@@ -28496,7 +28960,7 @@ Marvin \u2014 ${persona.name}
|
|
|
28496
28960
|
if (existingSession) {
|
|
28497
28961
|
queryOptions.resume = existingSession.id;
|
|
28498
28962
|
}
|
|
28499
|
-
const conversation =
|
|
28963
|
+
const conversation = query5({
|
|
28500
28964
|
prompt,
|
|
28501
28965
|
options: queryOptions
|
|
28502
28966
|
});
|
|
@@ -28588,7 +29052,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
28588
29052
|
import { tool as tool24 } from "@anthropic-ai/claude-agent-sdk";
|
|
28589
29053
|
|
|
28590
29054
|
// src/skills/action-runner.ts
|
|
28591
|
-
import { query as
|
|
29055
|
+
import { query as query6 } from "@anthropic-ai/claude-agent-sdk";
|
|
28592
29056
|
var GOVERNANCE_TOOL_NAMES2 = [
|
|
28593
29057
|
"mcp__marvin-governance__list_decisions",
|
|
28594
29058
|
"mcp__marvin-governance__get_decision",
|
|
@@ -28610,7 +29074,7 @@ async function runSkillAction(action, userPrompt, context) {
|
|
|
28610
29074
|
try {
|
|
28611
29075
|
const mcpServer = createMarvinMcpServer(context.store);
|
|
28612
29076
|
const allowedTools = action.allowGovernanceTools !== false ? GOVERNANCE_TOOL_NAMES2 : [];
|
|
28613
|
-
const conversation =
|
|
29077
|
+
const conversation = query6({
|
|
28614
29078
|
prompt: userPrompt,
|
|
28615
29079
|
options: {
|
|
28616
29080
|
systemPrompt: action.systemPrompt,
|
|
@@ -29404,7 +29868,7 @@ import * as fs13 from "fs";
|
|
|
29404
29868
|
import * as path13 from "path";
|
|
29405
29869
|
import chalk7 from "chalk";
|
|
29406
29870
|
import ora2 from "ora";
|
|
29407
|
-
import { query as
|
|
29871
|
+
import { query as query7 } from "@anthropic-ai/claude-agent-sdk";
|
|
29408
29872
|
|
|
29409
29873
|
// src/sources/prompts.ts
|
|
29410
29874
|
function buildIngestSystemPrompt(persona, projectConfig, isDraft) {
|
|
@@ -29537,7 +30001,7 @@ async function ingestFile(options) {
|
|
|
29537
30001
|
const spinner = ora2({ text: `Analyzing ${fileName}...`, color: "cyan" });
|
|
29538
30002
|
spinner.start();
|
|
29539
30003
|
try {
|
|
29540
|
-
const conversation =
|
|
30004
|
+
const conversation = query7({
|
|
29541
30005
|
prompt: userPrompt,
|
|
29542
30006
|
options: {
|
|
29543
30007
|
systemPrompt,
|
|
@@ -30758,7 +31222,7 @@ import chalk13 from "chalk";
|
|
|
30758
31222
|
// src/analysis/analyze.ts
|
|
30759
31223
|
import chalk12 from "chalk";
|
|
30760
31224
|
import ora4 from "ora";
|
|
30761
|
-
import { query as
|
|
31225
|
+
import { query as query8 } from "@anthropic-ai/claude-agent-sdk";
|
|
30762
31226
|
|
|
30763
31227
|
// src/analysis/prompts.ts
|
|
30764
31228
|
function buildAnalyzeSystemPrompt(persona, projectConfig, isDraft) {
|
|
@@ -30888,7 +31352,7 @@ async function analyzeMeeting(options) {
|
|
|
30888
31352
|
const spinner = ora4({ text: `Analyzing meeting ${meetingId}...`, color: "cyan" });
|
|
30889
31353
|
spinner.start();
|
|
30890
31354
|
try {
|
|
30891
|
-
const conversation =
|
|
31355
|
+
const conversation = query8({
|
|
30892
31356
|
prompt: userPrompt,
|
|
30893
31357
|
options: {
|
|
30894
31358
|
systemPrompt,
|
|
@@ -31015,7 +31479,7 @@ import chalk15 from "chalk";
|
|
|
31015
31479
|
// src/contributions/contribute.ts
|
|
31016
31480
|
import chalk14 from "chalk";
|
|
31017
31481
|
import ora5 from "ora";
|
|
31018
|
-
import { query as
|
|
31482
|
+
import { query as query9 } from "@anthropic-ai/claude-agent-sdk";
|
|
31019
31483
|
|
|
31020
31484
|
// src/contributions/prompts.ts
|
|
31021
31485
|
function buildContributeSystemPrompt(persona, contributionType, projectConfig, isDraft) {
|
|
@@ -31269,7 +31733,7 @@ async function contributeFromPersona(options) {
|
|
|
31269
31733
|
"mcp__marvin-governance__get_action",
|
|
31270
31734
|
"mcp__marvin-governance__get_question"
|
|
31271
31735
|
];
|
|
31272
|
-
const conversation =
|
|
31736
|
+
const conversation = query9({
|
|
31273
31737
|
prompt: userPrompt,
|
|
31274
31738
|
options: {
|
|
31275
31739
|
systemPrompt,
|
|
@@ -32108,7 +32572,7 @@ function createProgram() {
|
|
|
32108
32572
|
const program = new Command();
|
|
32109
32573
|
program.name("marvin").description(
|
|
32110
32574
|
"AI-powered product development assistant with Product Owner, Delivery Manager, and Technical Lead personas"
|
|
32111
|
-
).version("0.5.
|
|
32575
|
+
).version("0.5.14");
|
|
32112
32576
|
program.command("init").description("Initialize a new Marvin project in the current directory").action(async () => {
|
|
32113
32577
|
await initCommand();
|
|
32114
32578
|
});
|