mrvn-cli 0.5.13 → 0.5.15
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 +572 -23
- package/dist/index.js.map +1 -1
- package/dist/marvin-serve.js +596 -47
- package/dist/marvin-serve.js.map +1 -1
- package/dist/marvin.js +572 -23
- 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
|
}
|
|
@@ -19443,7 +19443,7 @@ function poBacklogPage(ctx) {
|
|
|
19443
19443
|
}
|
|
19444
19444
|
}
|
|
19445
19445
|
}
|
|
19446
|
-
const
|
|
19446
|
+
const DONE_STATUSES17 = /* @__PURE__ */ new Set(["done", "closed", "resolved", "cancelled"]);
|
|
19447
19447
|
function featureTaskStats(featureId) {
|
|
19448
19448
|
const fEpics = featureToEpics.get(featureId) ?? [];
|
|
19449
19449
|
let total = 0;
|
|
@@ -19452,7 +19452,7 @@ function poBacklogPage(ctx) {
|
|
|
19452
19452
|
for (const epic of fEpics) {
|
|
19453
19453
|
for (const t of epicToTasks.get(epic.frontmatter.id) ?? []) {
|
|
19454
19454
|
total++;
|
|
19455
|
-
if (
|
|
19455
|
+
if (DONE_STATUSES17.has(t.frontmatter.status)) done++;
|
|
19456
19456
|
progressSum += getEffectiveProgress(t.frontmatter);
|
|
19457
19457
|
}
|
|
19458
19458
|
}
|
|
@@ -25110,6 +25110,11 @@ function mapJiraStatusForTask(status, configMap) {
|
|
|
25110
25110
|
const lookup = buildStatusLookup(configMap, DEFAULT_TASK_STATUS_MAP);
|
|
25111
25111
|
return lookup.get(status.toLowerCase()) ?? "backlog";
|
|
25112
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
|
+
}
|
|
25113
25118
|
function computeSubtaskProgress(subtasks) {
|
|
25114
25119
|
if (subtasks.length === 0) return 0;
|
|
25115
25120
|
const done = subtasks.filter(
|
|
@@ -25492,10 +25497,10 @@ async function fetchJiraDaily(store, client, host, projectKey, dateRange, status
|
|
|
25492
25497
|
jiraKeyToArtifacts.set(jk, list);
|
|
25493
25498
|
}
|
|
25494
25499
|
}
|
|
25495
|
-
const
|
|
25500
|
+
const BATCH_SIZE2 = 5;
|
|
25496
25501
|
const issues = searchResult.issues;
|
|
25497
|
-
for (let i = 0; i < issues.length; i +=
|
|
25498
|
-
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);
|
|
25499
25504
|
const results = await Promise.allSettled(
|
|
25500
25505
|
batch.map(
|
|
25501
25506
|
(issue2) => processIssue(issue2, client, host, dateRange, jiraKeyToArtifacts, allDocs, statusMap)
|
|
@@ -25719,6 +25724,510 @@ function generateProposedActions(issues) {
|
|
|
25719
25724
|
return actions;
|
|
25720
25725
|
}
|
|
25721
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
|
+
var BLOCKED_WEIGHT_RISK_THRESHOLD = 0.3;
|
|
25732
|
+
var COMPLEXITY_WEIGHTS = {
|
|
25733
|
+
trivial: 1,
|
|
25734
|
+
simple: 2,
|
|
25735
|
+
moderate: 3,
|
|
25736
|
+
complex: 5,
|
|
25737
|
+
"very-complex": 8
|
|
25738
|
+
};
|
|
25739
|
+
var DEFAULT_WEIGHT = 3;
|
|
25740
|
+
var STATUS_PROGRESS_DEFAULTS = {
|
|
25741
|
+
done: 100,
|
|
25742
|
+
closed: 100,
|
|
25743
|
+
resolved: 100,
|
|
25744
|
+
obsolete: 100,
|
|
25745
|
+
"wont do": 100,
|
|
25746
|
+
cancelled: 100,
|
|
25747
|
+
review: 80,
|
|
25748
|
+
"in-progress": 40,
|
|
25749
|
+
ready: 5,
|
|
25750
|
+
backlog: 0,
|
|
25751
|
+
open: 0
|
|
25752
|
+
};
|
|
25753
|
+
var BLOCKED_DEFAULT_PROGRESS = 10;
|
|
25754
|
+
function resolveWeight(complexity) {
|
|
25755
|
+
if (complexity && complexity in COMPLEXITY_WEIGHTS) {
|
|
25756
|
+
return { weight: COMPLEXITY_WEIGHTS[complexity], weightSource: "complexity" };
|
|
25757
|
+
}
|
|
25758
|
+
return { weight: DEFAULT_WEIGHT, weightSource: "default" };
|
|
25759
|
+
}
|
|
25760
|
+
function resolveProgress(frontmatter, commentAnalysisProgress) {
|
|
25761
|
+
const hasExplicitProgress = "progress" in frontmatter && typeof frontmatter.progress === "number";
|
|
25762
|
+
if (hasExplicitProgress) {
|
|
25763
|
+
return { progress: Math.max(0, Math.min(100, Math.round(frontmatter.progress))), progressSource: "explicit" };
|
|
25764
|
+
}
|
|
25765
|
+
if (commentAnalysisProgress !== null) {
|
|
25766
|
+
return { progress: Math.max(0, Math.min(100, Math.round(commentAnalysisProgress))), progressSource: "comment-analysis" };
|
|
25767
|
+
}
|
|
25768
|
+
const status = frontmatter.status;
|
|
25769
|
+
if (status === "blocked") {
|
|
25770
|
+
return { progress: BLOCKED_DEFAULT_PROGRESS, progressSource: "status-default" };
|
|
25771
|
+
}
|
|
25772
|
+
const defaultProgress = STATUS_PROGRESS_DEFAULTS[status] ?? 0;
|
|
25773
|
+
return { progress: defaultProgress, progressSource: "status-default" };
|
|
25774
|
+
}
|
|
25775
|
+
function computeWeightedProgress(items) {
|
|
25776
|
+
if (items.length === 0) return 0;
|
|
25777
|
+
let totalWeight = 0;
|
|
25778
|
+
let weightedSum = 0;
|
|
25779
|
+
for (const item of items) {
|
|
25780
|
+
totalWeight += item.weight;
|
|
25781
|
+
weightedSum += item.weight * item.progress;
|
|
25782
|
+
}
|
|
25783
|
+
if (totalWeight === 0) return 0;
|
|
25784
|
+
return Math.round(weightedSum / totalWeight);
|
|
25785
|
+
}
|
|
25786
|
+
async function assessSprintProgress(store, client, host, options = {}) {
|
|
25787
|
+
const errors = [];
|
|
25788
|
+
const sprintData = collectSprintSummaryData(store, options.sprintId);
|
|
25789
|
+
if (!sprintData) {
|
|
25790
|
+
return {
|
|
25791
|
+
sprintId: options.sprintId ?? "unknown",
|
|
25792
|
+
sprintTitle: "Sprint not found",
|
|
25793
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
25794
|
+
timeline: { startDate: null, endDate: null, daysRemaining: 0, totalDays: 0, percentComplete: 0 },
|
|
25795
|
+
overallProgress: 0,
|
|
25796
|
+
itemReports: [],
|
|
25797
|
+
focusAreas: [],
|
|
25798
|
+
driftItems: [],
|
|
25799
|
+
blockers: [],
|
|
25800
|
+
proposedUpdates: [],
|
|
25801
|
+
appliedUpdates: [],
|
|
25802
|
+
errors: [`Sprint ${options.sprintId ?? "(active)"} not found. Create a sprint artifact first.`]
|
|
25803
|
+
};
|
|
25804
|
+
}
|
|
25805
|
+
const sprintTag = `sprint:${sprintData.sprint.id}`;
|
|
25806
|
+
const actions = store.list({ type: "action", tag: sprintTag });
|
|
25807
|
+
const tasks = store.list({ type: "task", tag: sprintTag });
|
|
25808
|
+
const sprintItemIds = new Set([...actions, ...tasks].map((d) => d.frontmatter.id));
|
|
25809
|
+
const allTasks = store.list({ type: "task" });
|
|
25810
|
+
const allActions = store.list({ type: "action" });
|
|
25811
|
+
const nestedTasks = allTasks.filter(
|
|
25812
|
+
(d) => !sprintItemIds.has(d.frontmatter.id) && d.frontmatter.aboutArtifact && sprintItemIds.has(d.frontmatter.aboutArtifact)
|
|
25813
|
+
);
|
|
25814
|
+
const nestedActions = allActions.filter(
|
|
25815
|
+
(d) => !sprintItemIds.has(d.frontmatter.id) && d.frontmatter.aboutArtifact && sprintItemIds.has(d.frontmatter.aboutArtifact)
|
|
25816
|
+
);
|
|
25817
|
+
const allItems = [...actions, ...tasks, ...nestedTasks, ...nestedActions];
|
|
25818
|
+
const itemJiraKeys = /* @__PURE__ */ new Map();
|
|
25819
|
+
for (const doc of allItems) {
|
|
25820
|
+
const jiraKey = doc.frontmatter.jiraKey ?? extractJiraKeyFromTags(doc.frontmatter.tags);
|
|
25821
|
+
if (jiraKey) {
|
|
25822
|
+
itemJiraKeys.set(doc.frontmatter.id, jiraKey);
|
|
25823
|
+
}
|
|
25824
|
+
}
|
|
25825
|
+
const jiraKeys = [...new Set(itemJiraKeys.values())];
|
|
25826
|
+
const jiraIssues = /* @__PURE__ */ new Map();
|
|
25827
|
+
for (let i = 0; i < jiraKeys.length; i += BATCH_SIZE) {
|
|
25828
|
+
const batch = jiraKeys.slice(i, i + BATCH_SIZE);
|
|
25829
|
+
const results = await Promise.allSettled(
|
|
25830
|
+
batch.map(async (key) => {
|
|
25831
|
+
const [issue2, comments] = await Promise.all([
|
|
25832
|
+
client.getIssueWithLinks(key),
|
|
25833
|
+
client.getComments(key)
|
|
25834
|
+
]);
|
|
25835
|
+
return { key, issue: issue2, comments };
|
|
25836
|
+
})
|
|
25837
|
+
);
|
|
25838
|
+
for (const result of results) {
|
|
25839
|
+
if (result.status === "fulfilled") {
|
|
25840
|
+
jiraIssues.set(result.value.key, {
|
|
25841
|
+
issue: result.value.issue,
|
|
25842
|
+
comments: result.value.comments
|
|
25843
|
+
});
|
|
25844
|
+
} else {
|
|
25845
|
+
const batchKey = batch[results.indexOf(result)];
|
|
25846
|
+
errors.push(`Failed to fetch ${batchKey}: ${result.reason instanceof Error ? result.reason.message : String(result.reason)}`);
|
|
25847
|
+
}
|
|
25848
|
+
}
|
|
25849
|
+
}
|
|
25850
|
+
const proposedUpdates = [];
|
|
25851
|
+
const itemReports = [];
|
|
25852
|
+
const childReportsByParent = /* @__PURE__ */ new Map();
|
|
25853
|
+
for (const doc of allItems) {
|
|
25854
|
+
const fm = doc.frontmatter;
|
|
25855
|
+
const jiraKey = itemJiraKeys.get(fm.id) ?? null;
|
|
25856
|
+
const jiraData = jiraKey ? jiraIssues.get(jiraKey) : null;
|
|
25857
|
+
let jiraStatus = null;
|
|
25858
|
+
let proposedMarvinStatus = null;
|
|
25859
|
+
let jiraSubtaskProgress = null;
|
|
25860
|
+
const commentSignals = [];
|
|
25861
|
+
if (jiraData) {
|
|
25862
|
+
jiraStatus = jiraData.issue.fields.status.name;
|
|
25863
|
+
proposedMarvinStatus = fm.type === "task" ? mapJiraStatusForTask(jiraStatus, options.statusMap?.task) : mapJiraStatusForAction(jiraStatus, options.statusMap?.action);
|
|
25864
|
+
const subtasks = jiraData.issue.fields.subtasks ?? [];
|
|
25865
|
+
if (subtasks.length > 0) {
|
|
25866
|
+
jiraSubtaskProgress = computeSubtaskProgress(subtasks);
|
|
25867
|
+
}
|
|
25868
|
+
for (const comment of jiraData.comments) {
|
|
25869
|
+
const text = extractCommentText(comment.body);
|
|
25870
|
+
const signals = detectCommentSignals(text);
|
|
25871
|
+
commentSignals.push(...signals);
|
|
25872
|
+
}
|
|
25873
|
+
}
|
|
25874
|
+
const statusDrift = proposedMarvinStatus !== null && proposedMarvinStatus !== fm.status;
|
|
25875
|
+
const currentProgress = getEffectiveProgress(fm);
|
|
25876
|
+
const progressDrift = jiraSubtaskProgress !== null && !fm.progressOverride && jiraSubtaskProgress !== currentProgress;
|
|
25877
|
+
if (statusDrift && proposedMarvinStatus) {
|
|
25878
|
+
proposedUpdates.push({
|
|
25879
|
+
artifactId: fm.id,
|
|
25880
|
+
field: "status",
|
|
25881
|
+
currentValue: fm.status,
|
|
25882
|
+
proposedValue: proposedMarvinStatus,
|
|
25883
|
+
reason: `Jira ${jiraKey} is "${jiraStatus}" \u2192 maps to "${proposedMarvinStatus}"`
|
|
25884
|
+
});
|
|
25885
|
+
}
|
|
25886
|
+
if (progressDrift && jiraSubtaskProgress !== null) {
|
|
25887
|
+
proposedUpdates.push({
|
|
25888
|
+
artifactId: fm.id,
|
|
25889
|
+
field: "progress",
|
|
25890
|
+
currentValue: currentProgress,
|
|
25891
|
+
proposedValue: jiraSubtaskProgress,
|
|
25892
|
+
reason: `Jira ${jiraKey} subtask progress is ${jiraSubtaskProgress}%`
|
|
25893
|
+
});
|
|
25894
|
+
}
|
|
25895
|
+
const tags = fm.tags ?? [];
|
|
25896
|
+
const focusTag = tags.find((t) => t.startsWith("focus:"));
|
|
25897
|
+
const { weight, weightSource } = resolveWeight(fm.complexity);
|
|
25898
|
+
const { progress: resolvedProgress, progressSource } = resolveProgress(fm, null);
|
|
25899
|
+
const report = {
|
|
25900
|
+
id: fm.id,
|
|
25901
|
+
title: fm.title,
|
|
25902
|
+
type: fm.type,
|
|
25903
|
+
marvinStatus: fm.status,
|
|
25904
|
+
marvinProgress: currentProgress,
|
|
25905
|
+
progress: resolvedProgress,
|
|
25906
|
+
progressSource,
|
|
25907
|
+
weight,
|
|
25908
|
+
weightSource,
|
|
25909
|
+
jiraKey,
|
|
25910
|
+
jiraStatus,
|
|
25911
|
+
jiraSubtaskProgress,
|
|
25912
|
+
proposedMarvinStatus,
|
|
25913
|
+
statusDrift,
|
|
25914
|
+
progressDrift,
|
|
25915
|
+
commentSignals,
|
|
25916
|
+
commentSummary: null,
|
|
25917
|
+
children: [],
|
|
25918
|
+
owner: fm.owner ?? null,
|
|
25919
|
+
focusArea: focusTag ? focusTag.slice(6) : null
|
|
25920
|
+
};
|
|
25921
|
+
const aboutArtifact = fm.aboutArtifact;
|
|
25922
|
+
if (aboutArtifact && sprintItemIds.has(aboutArtifact)) {
|
|
25923
|
+
if (!childReportsByParent.has(aboutArtifact)) {
|
|
25924
|
+
childReportsByParent.set(aboutArtifact, []);
|
|
25925
|
+
}
|
|
25926
|
+
childReportsByParent.get(aboutArtifact).push(report);
|
|
25927
|
+
}
|
|
25928
|
+
itemReports.push(report);
|
|
25929
|
+
}
|
|
25930
|
+
for (const report of itemReports) {
|
|
25931
|
+
const children = childReportsByParent.get(report.id);
|
|
25932
|
+
if (children) {
|
|
25933
|
+
report.children = children;
|
|
25934
|
+
}
|
|
25935
|
+
}
|
|
25936
|
+
const childIds = /* @__PURE__ */ new Set();
|
|
25937
|
+
for (const children of childReportsByParent.values()) {
|
|
25938
|
+
for (const child of children) childIds.add(child.id);
|
|
25939
|
+
}
|
|
25940
|
+
const rootReports = itemReports.filter((r) => !childIds.has(r.id));
|
|
25941
|
+
for (const report of rootReports) {
|
|
25942
|
+
if (report.children.length > 0) {
|
|
25943
|
+
const doc = store.get(report.id);
|
|
25944
|
+
const hasExplicitOverride = doc?.frontmatter.progressOverride;
|
|
25945
|
+
if (!hasExplicitOverride) {
|
|
25946
|
+
report.progress = computeWeightedProgress(report.children);
|
|
25947
|
+
report.progressSource = "status-default";
|
|
25948
|
+
}
|
|
25949
|
+
}
|
|
25950
|
+
}
|
|
25951
|
+
const focusAreaMap = /* @__PURE__ */ new Map();
|
|
25952
|
+
for (const report of rootReports) {
|
|
25953
|
+
if (!report.focusArea) continue;
|
|
25954
|
+
if (!focusAreaMap.has(report.focusArea)) focusAreaMap.set(report.focusArea, []);
|
|
25955
|
+
focusAreaMap.get(report.focusArea).push(report);
|
|
25956
|
+
}
|
|
25957
|
+
const focusAreas = [];
|
|
25958
|
+
for (const [name, items] of focusAreaMap) {
|
|
25959
|
+
const allFlatItems = items.flatMap((i) => [i, ...i.children]);
|
|
25960
|
+
const doneCount = allFlatItems.filter((i) => DONE_STATUSES16.has(i.marvinStatus)).length;
|
|
25961
|
+
const blockedCount = allFlatItems.filter((i) => i.marvinStatus === "blocked").length;
|
|
25962
|
+
const progress = computeWeightedProgress(items);
|
|
25963
|
+
const totalWeight = items.reduce((s, i) => s + i.weight, 0);
|
|
25964
|
+
const blockedWeight = items.filter((i) => i.marvinStatus === "blocked").reduce((s, i) => s + i.weight, 0);
|
|
25965
|
+
const blockedWeightPct = totalWeight > 0 ? Math.round(blockedWeight / totalWeight * 100) : 0;
|
|
25966
|
+
const riskWarning = blockedWeightPct > BLOCKED_WEIGHT_RISK_THRESHOLD * 100 ? `${blockedWeightPct}% of scope is blocked` : null;
|
|
25967
|
+
focusAreas.push({
|
|
25968
|
+
name,
|
|
25969
|
+
progress,
|
|
25970
|
+
taskCount: allFlatItems.length,
|
|
25971
|
+
doneCount,
|
|
25972
|
+
blockedCount,
|
|
25973
|
+
blockedWeightPct,
|
|
25974
|
+
riskWarning,
|
|
25975
|
+
items
|
|
25976
|
+
});
|
|
25977
|
+
}
|
|
25978
|
+
focusAreas.sort((a, b) => a.name.localeCompare(b.name));
|
|
25979
|
+
const driftItems = itemReports.filter((r) => r.statusDrift || r.progressDrift);
|
|
25980
|
+
const blockers = itemReports.filter(
|
|
25981
|
+
(r) => r.marvinStatus === "blocked" || r.commentSignals.some((s) => s.type === "blocker")
|
|
25982
|
+
);
|
|
25983
|
+
if (options.analyzeComments) {
|
|
25984
|
+
const itemsWithComments = itemReports.filter((r) => r.commentSignals.length > 0 && r.jiraKey);
|
|
25985
|
+
if (itemsWithComments.length > 0) {
|
|
25986
|
+
try {
|
|
25987
|
+
const summaries = await analyzeCommentsForProgress(
|
|
25988
|
+
itemsWithComments,
|
|
25989
|
+
jiraIssues,
|
|
25990
|
+
itemJiraKeys
|
|
25991
|
+
);
|
|
25992
|
+
for (const [artifactId, summary] of summaries) {
|
|
25993
|
+
const report = itemReports.find((r) => r.id === artifactId);
|
|
25994
|
+
if (report) {
|
|
25995
|
+
report.commentSummary = summary;
|
|
25996
|
+
if (report.progressSource === "status-default") {
|
|
25997
|
+
const pctMatch = summary.match(/(\d{1,3})%/);
|
|
25998
|
+
if (pctMatch) {
|
|
25999
|
+
const pct = parseInt(pctMatch[1], 10);
|
|
26000
|
+
if (pct >= 0 && pct <= 100) {
|
|
26001
|
+
report.progress = pct;
|
|
26002
|
+
report.progressSource = "comment-analysis";
|
|
26003
|
+
}
|
|
26004
|
+
}
|
|
26005
|
+
}
|
|
26006
|
+
}
|
|
26007
|
+
}
|
|
26008
|
+
} catch (err) {
|
|
26009
|
+
errors.push(`Comment analysis failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
26010
|
+
}
|
|
26011
|
+
}
|
|
26012
|
+
}
|
|
26013
|
+
const appliedUpdates = [];
|
|
26014
|
+
if (options.applyUpdates && proposedUpdates.length > 0) {
|
|
26015
|
+
for (const update of proposedUpdates) {
|
|
26016
|
+
try {
|
|
26017
|
+
store.update(update.artifactId, {
|
|
26018
|
+
[update.field]: update.proposedValue,
|
|
26019
|
+
lastJiraSyncAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
26020
|
+
});
|
|
26021
|
+
const doc = store.get(update.artifactId);
|
|
26022
|
+
if (doc) {
|
|
26023
|
+
if (doc.frontmatter.type === "task") {
|
|
26024
|
+
propagateProgressFromTask(store, update.artifactId);
|
|
26025
|
+
} else if (doc.frontmatter.type === "action") {
|
|
26026
|
+
propagateProgressToAction(store, update.artifactId);
|
|
26027
|
+
}
|
|
26028
|
+
}
|
|
26029
|
+
appliedUpdates.push(update);
|
|
26030
|
+
} catch (err) {
|
|
26031
|
+
errors.push(
|
|
26032
|
+
`Failed to apply update to ${update.artifactId}: ${err instanceof Error ? err.message : String(err)}`
|
|
26033
|
+
);
|
|
26034
|
+
}
|
|
26035
|
+
}
|
|
26036
|
+
}
|
|
26037
|
+
return {
|
|
26038
|
+
sprintId: sprintData.sprint.id,
|
|
26039
|
+
sprintTitle: sprintData.sprint.title,
|
|
26040
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
26041
|
+
timeline: {
|
|
26042
|
+
startDate: sprintData.sprint.startDate ?? null,
|
|
26043
|
+
endDate: sprintData.sprint.endDate ?? null,
|
|
26044
|
+
daysRemaining: sprintData.timeline.daysRemaining,
|
|
26045
|
+
totalDays: sprintData.timeline.totalDays,
|
|
26046
|
+
percentComplete: sprintData.timeline.percentComplete
|
|
26047
|
+
},
|
|
26048
|
+
overallProgress: rootReports.length > 0 ? computeWeightedProgress(rootReports) : sprintData.workItems.completionPct,
|
|
26049
|
+
itemReports: rootReports,
|
|
26050
|
+
focusAreas,
|
|
26051
|
+
driftItems,
|
|
26052
|
+
blockers,
|
|
26053
|
+
proposedUpdates: options.applyUpdates ? [] : proposedUpdates,
|
|
26054
|
+
appliedUpdates,
|
|
26055
|
+
errors
|
|
26056
|
+
};
|
|
26057
|
+
}
|
|
26058
|
+
var COMMENT_ANALYSIS_PROMPT = `You are a delivery management assistant analyzing Jira comments for progress signals.
|
|
26059
|
+
|
|
26060
|
+
For each item below, read the Jira comments and produce a 1-2 sentence progress summary.
|
|
26061
|
+
Focus on: what work was done, what's pending, any blockers or decisions mentioned.
|
|
26062
|
+
|
|
26063
|
+
Return your response as a JSON object mapping artifact IDs to summary strings.
|
|
26064
|
+
Example: {"T-001": "Backend API completed and deployed. Frontend integration pending review.", "A-003": "Blocked on infrastructure team approval."}
|
|
26065
|
+
|
|
26066
|
+
IMPORTANT: Only return the JSON object, no other text.`;
|
|
26067
|
+
async function analyzeCommentsForProgress(items, jiraIssues, itemJiraKeys) {
|
|
26068
|
+
const summaries = /* @__PURE__ */ new Map();
|
|
26069
|
+
const MAX_ITEMS_PER_CALL = 20;
|
|
26070
|
+
const itemsToAnalyze = items.slice(0, MAX_ITEMS_PER_CALL);
|
|
26071
|
+
const promptParts = [];
|
|
26072
|
+
for (const item of itemsToAnalyze) {
|
|
26073
|
+
const jiraKey = itemJiraKeys.get(item.id);
|
|
26074
|
+
if (!jiraKey) continue;
|
|
26075
|
+
const jiraData = jiraIssues.get(jiraKey);
|
|
26076
|
+
if (!jiraData || jiraData.comments.length === 0) continue;
|
|
26077
|
+
const commentTexts = jiraData.comments.map((c) => {
|
|
26078
|
+
const text = extractCommentText(c.body);
|
|
26079
|
+
return ` [${c.author.displayName}, ${c.created.slice(0, 10)}]: ${text.slice(0, 500)}`;
|
|
26080
|
+
}).join("\n");
|
|
26081
|
+
promptParts.push(`## ${item.id} \u2014 ${item.title} (${jiraKey}, Jira status: ${item.jiraStatus})
|
|
26082
|
+
Comments:
|
|
26083
|
+
${commentTexts}`);
|
|
26084
|
+
}
|
|
26085
|
+
if (promptParts.length === 0) return summaries;
|
|
26086
|
+
const prompt = promptParts.join("\n\n");
|
|
26087
|
+
const result = query3({
|
|
26088
|
+
prompt,
|
|
26089
|
+
options: {
|
|
26090
|
+
systemPrompt: COMMENT_ANALYSIS_PROMPT,
|
|
26091
|
+
maxTurns: 1,
|
|
26092
|
+
tools: [],
|
|
26093
|
+
allowedTools: []
|
|
26094
|
+
}
|
|
26095
|
+
});
|
|
26096
|
+
for await (const msg of result) {
|
|
26097
|
+
if (msg.type === "assistant") {
|
|
26098
|
+
const textBlock = msg.message.content.find(
|
|
26099
|
+
(b) => b.type === "text"
|
|
26100
|
+
);
|
|
26101
|
+
if (textBlock) {
|
|
26102
|
+
try {
|
|
26103
|
+
const parsed = JSON.parse(textBlock.text);
|
|
26104
|
+
for (const [id, summary] of Object.entries(parsed)) {
|
|
26105
|
+
if (typeof summary === "string") {
|
|
26106
|
+
summaries.set(id, summary);
|
|
26107
|
+
}
|
|
26108
|
+
}
|
|
26109
|
+
} catch {
|
|
26110
|
+
const match = textBlock.text.match(/```(?:json)?\s*([\s\S]*?)```/);
|
|
26111
|
+
if (match) {
|
|
26112
|
+
try {
|
|
26113
|
+
const parsed = JSON.parse(match[1]);
|
|
26114
|
+
for (const [id, summary] of Object.entries(parsed)) {
|
|
26115
|
+
if (typeof summary === "string") {
|
|
26116
|
+
summaries.set(id, summary);
|
|
26117
|
+
}
|
|
26118
|
+
}
|
|
26119
|
+
} catch {
|
|
26120
|
+
}
|
|
26121
|
+
}
|
|
26122
|
+
}
|
|
26123
|
+
}
|
|
26124
|
+
}
|
|
26125
|
+
}
|
|
26126
|
+
return summaries;
|
|
26127
|
+
}
|
|
26128
|
+
function formatProgressReport(report) {
|
|
26129
|
+
const parts = [];
|
|
26130
|
+
parts.push(`# Sprint Progress Assessment \u2014 ${report.sprintId}`);
|
|
26131
|
+
parts.push(`${report.sprintTitle}`);
|
|
26132
|
+
parts.push(`Generated: ${report.generatedAt.slice(0, 16)}`);
|
|
26133
|
+
parts.push("");
|
|
26134
|
+
if (report.timeline.startDate && report.timeline.endDate) {
|
|
26135
|
+
parts.push(`## Timeline`);
|
|
26136
|
+
parts.push(`${report.timeline.startDate} \u2192 ${report.timeline.endDate}`);
|
|
26137
|
+
parts.push(`Days remaining: ${report.timeline.daysRemaining} / ${report.timeline.totalDays} (${report.timeline.percentComplete}% elapsed)`);
|
|
26138
|
+
parts.push(`Overall progress: ${report.overallProgress}%`);
|
|
26139
|
+
parts.push("");
|
|
26140
|
+
}
|
|
26141
|
+
if (report.focusAreas.length > 0) {
|
|
26142
|
+
parts.push(`## Focus Areas`);
|
|
26143
|
+
parts.push("");
|
|
26144
|
+
for (const area of report.focusAreas) {
|
|
26145
|
+
const bar = progressBar6(area.progress);
|
|
26146
|
+
parts.push(`### ${area.name} ${bar} ${area.progress}%`);
|
|
26147
|
+
parts.push(`${area.doneCount}/${area.taskCount} done${area.blockedCount > 0 ? ` | ${area.blockedCount} blocked` : ""}`);
|
|
26148
|
+
if (area.riskWarning) {
|
|
26149
|
+
parts.push(` \u26A0 ${area.riskWarning}`);
|
|
26150
|
+
}
|
|
26151
|
+
parts.push("");
|
|
26152
|
+
for (const item of area.items) {
|
|
26153
|
+
formatItemLine(parts, item, 0);
|
|
26154
|
+
}
|
|
26155
|
+
parts.push("");
|
|
26156
|
+
}
|
|
26157
|
+
}
|
|
26158
|
+
if (report.driftItems.length > 0) {
|
|
26159
|
+
parts.push(`## Status Drift (${report.driftItems.length} items)`);
|
|
26160
|
+
for (const item of report.driftItems) {
|
|
26161
|
+
const driftParts = [];
|
|
26162
|
+
if (item.statusDrift) {
|
|
26163
|
+
driftParts.push(`status: ${item.marvinStatus} \u2192 ${item.proposedMarvinStatus}`);
|
|
26164
|
+
}
|
|
26165
|
+
if (item.progressDrift && item.jiraSubtaskProgress !== null) {
|
|
26166
|
+
driftParts.push(`progress: ${item.marvinProgress}% \u2192 ${item.jiraSubtaskProgress}%`);
|
|
26167
|
+
}
|
|
26168
|
+
parts.push(` \u26A0 ${item.id} (${item.jiraKey}) \u2014 ${driftParts.join(", ")}`);
|
|
26169
|
+
}
|
|
26170
|
+
parts.push("");
|
|
26171
|
+
}
|
|
26172
|
+
if (report.blockers.length > 0) {
|
|
26173
|
+
parts.push(`## Blockers (${report.blockers.length})`);
|
|
26174
|
+
for (const item of report.blockers) {
|
|
26175
|
+
const blockerSignals = item.commentSignals.filter((s) => s.type === "blocker");
|
|
26176
|
+
parts.push(` \u{1F6AB} ${item.id} \u2014 ${item.title}${item.jiraKey ? ` (${item.jiraKey})` : ""}`);
|
|
26177
|
+
for (const signal of blockerSignals) {
|
|
26178
|
+
parts.push(` "${signal.snippet}"`);
|
|
26179
|
+
}
|
|
26180
|
+
}
|
|
26181
|
+
parts.push("");
|
|
26182
|
+
}
|
|
26183
|
+
if (report.proposedUpdates.length > 0) {
|
|
26184
|
+
parts.push(`## Proposed Updates (${report.proposedUpdates.length})`);
|
|
26185
|
+
for (const update of report.proposedUpdates) {
|
|
26186
|
+
parts.push(` ${update.artifactId}.${update.field}: ${String(update.currentValue)} \u2192 ${String(update.proposedValue)}`);
|
|
26187
|
+
parts.push(` Reason: ${update.reason}`);
|
|
26188
|
+
}
|
|
26189
|
+
parts.push("");
|
|
26190
|
+
parts.push("Run with applyUpdates=true to apply these changes.");
|
|
26191
|
+
parts.push("");
|
|
26192
|
+
}
|
|
26193
|
+
if (report.appliedUpdates.length > 0) {
|
|
26194
|
+
parts.push(`## Applied Updates (${report.appliedUpdates.length})`);
|
|
26195
|
+
for (const update of report.appliedUpdates) {
|
|
26196
|
+
parts.push(` \u2713 ${update.artifactId}.${update.field}: ${String(update.currentValue)} \u2192 ${String(update.proposedValue)}`);
|
|
26197
|
+
}
|
|
26198
|
+
parts.push("");
|
|
26199
|
+
}
|
|
26200
|
+
if (report.errors.length > 0) {
|
|
26201
|
+
parts.push(`## Errors`);
|
|
26202
|
+
for (const err of report.errors) {
|
|
26203
|
+
parts.push(` ${err}`);
|
|
26204
|
+
}
|
|
26205
|
+
parts.push("");
|
|
26206
|
+
}
|
|
26207
|
+
return parts.join("\n");
|
|
26208
|
+
}
|
|
26209
|
+
function formatItemLine(parts, item, depth) {
|
|
26210
|
+
const indent = " ".repeat(depth + 1);
|
|
26211
|
+
const statusIcon = DONE_STATUSES16.has(item.marvinStatus) ? "\u2713" : item.marvinStatus === "blocked" ? "\u{1F6AB}" : item.marvinStatus === "in-progress" ? "\u25B6" : "\u25CB";
|
|
26212
|
+
const jiraLabel = item.jiraKey ? ` [${item.jiraKey}: ${item.jiraStatus}]` : "";
|
|
26213
|
+
const driftFlag = item.statusDrift ? " \u26A0drift" : "";
|
|
26214
|
+
const progressLabel = ` ${item.progress}%`;
|
|
26215
|
+
const weightLabel = `w${item.weight}`;
|
|
26216
|
+
const sourceLabel = item.progressSource === "explicit" ? "" : item.progressSource === "comment-analysis" ? " (llm)" : " (est)";
|
|
26217
|
+
parts.push(`${indent}${statusIcon} ${item.id} \u2014 ${item.title} [${item.marvinStatus}]${progressLabel}${sourceLabel} (${weightLabel})${jiraLabel}${driftFlag}`);
|
|
26218
|
+
if (item.commentSummary) {
|
|
26219
|
+
parts.push(`${indent} \u{1F4AC} ${item.commentSummary}`);
|
|
26220
|
+
}
|
|
26221
|
+
for (const child of item.children) {
|
|
26222
|
+
formatItemLine(parts, child, depth + 1);
|
|
26223
|
+
}
|
|
26224
|
+
}
|
|
26225
|
+
function progressBar6(pct) {
|
|
26226
|
+
const filled = Math.round(pct / 10);
|
|
26227
|
+
const empty = 10 - filled;
|
|
26228
|
+
return `[${"\u2588".repeat(filled)}${"\u2591".repeat(empty)}]`;
|
|
26229
|
+
}
|
|
26230
|
+
|
|
25722
26231
|
// src/skills/builtin/jira/tools.ts
|
|
25723
26232
|
var JIRA_TYPE = "jira-issue";
|
|
25724
26233
|
function jiraNotConfiguredError() {
|
|
@@ -26484,6 +26993,36 @@ function createJiraTools(store, projectConfig) {
|
|
|
26484
26993
|
};
|
|
26485
26994
|
},
|
|
26486
26995
|
{ annotations: { readOnlyHint: true } }
|
|
26996
|
+
),
|
|
26997
|
+
// --- Sprint progress assessment ---
|
|
26998
|
+
tool20(
|
|
26999
|
+
"assess_sprint_progress",
|
|
27000
|
+
"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.",
|
|
27001
|
+
{
|
|
27002
|
+
sprintId: external_exports.string().optional().describe("Sprint ID (e.g. 'SP-001'). Defaults to active sprint."),
|
|
27003
|
+
analyzeComments: external_exports.boolean().optional().describe("Use LLM to summarize Jira comments for progress signals (default false)"),
|
|
27004
|
+
applyUpdates: external_exports.boolean().optional().describe("Apply proposed status/progress updates to Marvin artifacts (default false)")
|
|
27005
|
+
},
|
|
27006
|
+
async (args) => {
|
|
27007
|
+
const jira = createJiraClient(jiraUserConfig);
|
|
27008
|
+
if (!jira) return jiraNotConfiguredError();
|
|
27009
|
+
const report = await assessSprintProgress(
|
|
27010
|
+
store,
|
|
27011
|
+
jira.client,
|
|
27012
|
+
jira.host,
|
|
27013
|
+
{
|
|
27014
|
+
sprintId: args.sprintId,
|
|
27015
|
+
analyzeComments: args.analyzeComments ?? false,
|
|
27016
|
+
applyUpdates: args.applyUpdates ?? false,
|
|
27017
|
+
statusMap
|
|
27018
|
+
}
|
|
27019
|
+
);
|
|
27020
|
+
return {
|
|
27021
|
+
content: [{ type: "text", text: formatProgressReport(report) }],
|
|
27022
|
+
isError: report.errors.length > 0 && report.itemReports.length === 0
|
|
27023
|
+
};
|
|
27024
|
+
},
|
|
27025
|
+
{ annotations: { readOnlyHint: false } }
|
|
26487
27026
|
)
|
|
26488
27027
|
];
|
|
26489
27028
|
}
|
|
@@ -26585,6 +27124,7 @@ var COMMON_TOOLS = `**Available tools:**
|
|
|
26585
27124
|
- \`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.
|
|
26586
27125
|
- \`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.
|
|
26587
27126
|
- \`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).
|
|
27127
|
+
- \`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.
|
|
26588
27128
|
- \`fetch_jira_statuses\` \u2014 **read-only**: discover all Jira statuses in a project and show their Marvin mappings (mapped vs unmapped).
|
|
26589
27129
|
- \`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.
|
|
26590
27130
|
- \`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).
|
|
@@ -26596,6 +27136,11 @@ var COMMON_WORKFLOW = `**Jira sync workflow:**
|
|
|
26596
27136
|
2. Analyze the proposed changes (status transitions, subtask progress, blockers from linked issues)
|
|
26597
27137
|
3. Use \`update_action\` / \`update_task\` to apply the changes you agree with
|
|
26598
27138
|
|
|
27139
|
+
**Sprint progress workflow:**
|
|
27140
|
+
1. Call \`assess_sprint_progress\` to get a comprehensive view of all sprint items with live Jira data
|
|
27141
|
+
2. Review focus area rollups, status drift, and blockers
|
|
27142
|
+
3. Optionally run with \`applyUpdates=true\` to bulk-sync statuses, or \`analyzeComments=true\` for LLM-powered comment summaries
|
|
27143
|
+
|
|
26599
27144
|
**Daily review workflow:**
|
|
26600
27145
|
1. Call \`fetch_jira_daily\` (optionally with \`from\`/\`to\` date range) to get a summary of all Jira activity
|
|
26601
27146
|
2. Review the proposed actions: status updates, unlinked issues to track, questions that may be answered, Confluence pages to review
|
|
@@ -26619,6 +27164,7 @@ ${COMMON_WORKFLOW}
|
|
|
26619
27164
|
|
|
26620
27165
|
**As Product Owner, use Jira integration to:**
|
|
26621
27166
|
- Use \`fetch_jira_daily\` for daily standups \u2014 review what changed, identify status drift, spot untracked work
|
|
27167
|
+
- Use \`assess_sprint_progress\` for sprint reviews \u2014 see overall progress by focus area, detect drift, and identify blockers
|
|
26622
27168
|
- Pull stakeholder-reported issues for triage and prioritization
|
|
26623
27169
|
- Push approved features as Stories for development tracking
|
|
26624
27170
|
- Link decisions to Jira issues for audit trail and traceability
|
|
@@ -26631,6 +27177,7 @@ ${COMMON_WORKFLOW}
|
|
|
26631
27177
|
|
|
26632
27178
|
**As Tech Lead, use Jira integration to:**
|
|
26633
27179
|
- Use \`fetch_jira_daily\` to review technical progress \u2014 status transitions, new comments, Confluence design docs
|
|
27180
|
+
- Use \`assess_sprint_progress\` for sprint health checks \u2014 focus area rollups, Jira drift detection, blocker tracking
|
|
26634
27181
|
- Pull technical issues and bugs for sprint planning and estimation
|
|
26635
27182
|
- Push epics, tasks, and technical decisions to Jira for cross-team visibility
|
|
26636
27183
|
- Use \`link_to_jira\` to connect Marvin tasks to existing Jira tickets
|
|
@@ -26644,6 +27191,8 @@ This is a third path for progress tracking alongside Contributions and Meetings.
|
|
|
26644
27191
|
|
|
26645
27192
|
**As Delivery Manager, use Jira integration to:**
|
|
26646
27193
|
- Use \`fetch_jira_daily\` for daily progress reports \u2014 track what moved, identify blockers, spot untracked work
|
|
27194
|
+
- Use \`assess_sprint_progress\` for sprint reviews and stakeholder updates \u2014 comprehensive progress by focus area with Jira enrichment
|
|
27195
|
+
- Use \`assess_sprint_progress\` with \`applyUpdates=true\` to bulk-sync Marvin statuses from Jira
|
|
26647
27196
|
- Pull sprint issues for tracking progress and blockers
|
|
26648
27197
|
- Push actions and tasks to Jira for stakeholder visibility
|
|
26649
27198
|
- Use \`fetch_jira_daily\` with a date range for sprint retrospectives (e.g. \`from: "2026-03-10", to: "2026-03-21"\`)
|
|
@@ -28148,7 +28697,7 @@ import * as readline from "readline";
|
|
|
28148
28697
|
import chalk from "chalk";
|
|
28149
28698
|
import ora from "ora";
|
|
28150
28699
|
import {
|
|
28151
|
-
query as
|
|
28700
|
+
query as query5
|
|
28152
28701
|
} from "@anthropic-ai/claude-agent-sdk";
|
|
28153
28702
|
|
|
28154
28703
|
// src/storage/session-store.ts
|
|
@@ -28219,11 +28768,11 @@ var SessionStore = class {
|
|
|
28219
28768
|
};
|
|
28220
28769
|
|
|
28221
28770
|
// src/agent/session-namer.ts
|
|
28222
|
-
import { query as
|
|
28771
|
+
import { query as query4 } from "@anthropic-ai/claude-agent-sdk";
|
|
28223
28772
|
async function generateSessionName(turns) {
|
|
28224
28773
|
try {
|
|
28225
28774
|
const transcript = turns.slice(-20).map((t) => `${t.role}: ${t.content.slice(0, 200)}`).join("\n");
|
|
28226
|
-
const result =
|
|
28775
|
+
const result = query4({
|
|
28227
28776
|
prompt: `Summarize this conversation in 3-5 words as a kebab-case name suitable for a filename. Output ONLY the name, nothing else.
|
|
28228
28777
|
|
|
28229
28778
|
${transcript}`,
|
|
@@ -28501,7 +29050,7 @@ Marvin \u2014 ${persona.name}
|
|
|
28501
29050
|
if (existingSession) {
|
|
28502
29051
|
queryOptions.resume = existingSession.id;
|
|
28503
29052
|
}
|
|
28504
|
-
const conversation =
|
|
29053
|
+
const conversation = query5({
|
|
28505
29054
|
prompt,
|
|
28506
29055
|
options: queryOptions
|
|
28507
29056
|
});
|
|
@@ -28593,7 +29142,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
28593
29142
|
import { tool as tool24 } from "@anthropic-ai/claude-agent-sdk";
|
|
28594
29143
|
|
|
28595
29144
|
// src/skills/action-runner.ts
|
|
28596
|
-
import { query as
|
|
29145
|
+
import { query as query6 } from "@anthropic-ai/claude-agent-sdk";
|
|
28597
29146
|
var GOVERNANCE_TOOL_NAMES2 = [
|
|
28598
29147
|
"mcp__marvin-governance__list_decisions",
|
|
28599
29148
|
"mcp__marvin-governance__get_decision",
|
|
@@ -28615,7 +29164,7 @@ async function runSkillAction(action, userPrompt, context) {
|
|
|
28615
29164
|
try {
|
|
28616
29165
|
const mcpServer = createMarvinMcpServer(context.store);
|
|
28617
29166
|
const allowedTools = action.allowGovernanceTools !== false ? GOVERNANCE_TOOL_NAMES2 : [];
|
|
28618
|
-
const conversation =
|
|
29167
|
+
const conversation = query6({
|
|
28619
29168
|
prompt: userPrompt,
|
|
28620
29169
|
options: {
|
|
28621
29170
|
systemPrompt: action.systemPrompt,
|
|
@@ -29409,7 +29958,7 @@ import * as fs13 from "fs";
|
|
|
29409
29958
|
import * as path13 from "path";
|
|
29410
29959
|
import chalk7 from "chalk";
|
|
29411
29960
|
import ora2 from "ora";
|
|
29412
|
-
import { query as
|
|
29961
|
+
import { query as query7 } from "@anthropic-ai/claude-agent-sdk";
|
|
29413
29962
|
|
|
29414
29963
|
// src/sources/prompts.ts
|
|
29415
29964
|
function buildIngestSystemPrompt(persona, projectConfig, isDraft) {
|
|
@@ -29542,7 +30091,7 @@ async function ingestFile(options) {
|
|
|
29542
30091
|
const spinner = ora2({ text: `Analyzing ${fileName}...`, color: "cyan" });
|
|
29543
30092
|
spinner.start();
|
|
29544
30093
|
try {
|
|
29545
|
-
const conversation =
|
|
30094
|
+
const conversation = query7({
|
|
29546
30095
|
prompt: userPrompt,
|
|
29547
30096
|
options: {
|
|
29548
30097
|
systemPrompt,
|
|
@@ -30763,7 +31312,7 @@ import chalk13 from "chalk";
|
|
|
30763
31312
|
// src/analysis/analyze.ts
|
|
30764
31313
|
import chalk12 from "chalk";
|
|
30765
31314
|
import ora4 from "ora";
|
|
30766
|
-
import { query as
|
|
31315
|
+
import { query as query8 } from "@anthropic-ai/claude-agent-sdk";
|
|
30767
31316
|
|
|
30768
31317
|
// src/analysis/prompts.ts
|
|
30769
31318
|
function buildAnalyzeSystemPrompt(persona, projectConfig, isDraft) {
|
|
@@ -30893,7 +31442,7 @@ async function analyzeMeeting(options) {
|
|
|
30893
31442
|
const spinner = ora4({ text: `Analyzing meeting ${meetingId}...`, color: "cyan" });
|
|
30894
31443
|
spinner.start();
|
|
30895
31444
|
try {
|
|
30896
|
-
const conversation =
|
|
31445
|
+
const conversation = query8({
|
|
30897
31446
|
prompt: userPrompt,
|
|
30898
31447
|
options: {
|
|
30899
31448
|
systemPrompt,
|
|
@@ -31020,7 +31569,7 @@ import chalk15 from "chalk";
|
|
|
31020
31569
|
// src/contributions/contribute.ts
|
|
31021
31570
|
import chalk14 from "chalk";
|
|
31022
31571
|
import ora5 from "ora";
|
|
31023
|
-
import { query as
|
|
31572
|
+
import { query as query9 } from "@anthropic-ai/claude-agent-sdk";
|
|
31024
31573
|
|
|
31025
31574
|
// src/contributions/prompts.ts
|
|
31026
31575
|
function buildContributeSystemPrompt(persona, contributionType, projectConfig, isDraft) {
|
|
@@ -31274,7 +31823,7 @@ async function contributeFromPersona(options) {
|
|
|
31274
31823
|
"mcp__marvin-governance__get_action",
|
|
31275
31824
|
"mcp__marvin-governance__get_question"
|
|
31276
31825
|
];
|
|
31277
|
-
const conversation =
|
|
31826
|
+
const conversation = query9({
|
|
31278
31827
|
prompt: userPrompt,
|
|
31279
31828
|
options: {
|
|
31280
31829
|
systemPrompt,
|
|
@@ -32113,7 +32662,7 @@ function createProgram() {
|
|
|
32113
32662
|
const program = new Command();
|
|
32114
32663
|
program.name("marvin").description(
|
|
32115
32664
|
"AI-powered product development assistant with Product Owner, Delivery Manager, and Technical Lead personas"
|
|
32116
|
-
).version("0.5.
|
|
32665
|
+
).version("0.5.15");
|
|
32117
32666
|
program.command("init").description("Initialize a new Marvin project in the current directory").action(async () => {
|
|
32118
32667
|
await initCommand();
|
|
32119
32668
|
});
|