mrvn-cli 0.5.16 → 0.5.17
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.d.ts +29 -8
- package/dist/index.js +136 -82
- package/dist/index.js.map +1 -1
- package/dist/marvin-serve.js +109 -64
- package/dist/marvin-serve.js.map +1 -1
- package/dist/marvin.js +136 -82
- package/dist/marvin.js.map +1 -1
- package/package.json +1 -1
package/dist/marvin-serve.js
CHANGED
|
@@ -14349,11 +14349,25 @@ import { tool as tool2 } from "@anthropic-ai/claude-agent-sdk";
|
|
|
14349
14349
|
|
|
14350
14350
|
// src/storage/progress.ts
|
|
14351
14351
|
var DONE_STATUSES = /* @__PURE__ */ new Set(["done", "closed", "resolved", "cancelled"]);
|
|
14352
|
+
var STATUS_PROGRESS_DEFAULTS = {
|
|
14353
|
+
done: 100,
|
|
14354
|
+
closed: 100,
|
|
14355
|
+
resolved: 100,
|
|
14356
|
+
obsolete: 100,
|
|
14357
|
+
"wont do": 100,
|
|
14358
|
+
cancelled: 100,
|
|
14359
|
+
review: 80,
|
|
14360
|
+
"in-progress": 40,
|
|
14361
|
+
ready: 5,
|
|
14362
|
+
blocked: 10,
|
|
14363
|
+
backlog: 0,
|
|
14364
|
+
open: 0
|
|
14365
|
+
};
|
|
14352
14366
|
function getEffectiveProgress(frontmatter) {
|
|
14353
14367
|
if (DONE_STATUSES.has(frontmatter.status)) return 100;
|
|
14354
14368
|
const raw = frontmatter.progress;
|
|
14355
14369
|
if (typeof raw === "number") return Math.max(0, Math.min(100, Math.round(raw)));
|
|
14356
|
-
return 0;
|
|
14370
|
+
return STATUS_PROGRESS_DEFAULTS[frontmatter.status] ?? 0;
|
|
14357
14371
|
}
|
|
14358
14372
|
function propagateProgressFromTask(store, taskId) {
|
|
14359
14373
|
const updated = [];
|
|
@@ -19178,35 +19192,62 @@ var DEFAULT_TASK_STATUS_MAP = {
|
|
|
19178
19192
|
blocked: ["Blocked"],
|
|
19179
19193
|
backlog: ["To Do", "Open", "Backlog", "New"]
|
|
19180
19194
|
};
|
|
19181
|
-
function
|
|
19182
|
-
|
|
19195
|
+
function isLegacyFormat(statusMap) {
|
|
19196
|
+
if (!statusMap || typeof statusMap !== "object") return false;
|
|
19197
|
+
const keys = Object.keys(statusMap);
|
|
19198
|
+
if (!keys.every((k) => k === "action" || k === "task")) return false;
|
|
19199
|
+
for (const key of keys) {
|
|
19200
|
+
const val = statusMap[key];
|
|
19201
|
+
if (typeof val !== "object" || val === null) return false;
|
|
19202
|
+
for (const innerVal of Object.values(val)) {
|
|
19203
|
+
if (!Array.isArray(innerVal)) return false;
|
|
19204
|
+
if (!innerVal.every((v) => typeof v === "string")) return false;
|
|
19205
|
+
}
|
|
19206
|
+
}
|
|
19207
|
+
return true;
|
|
19183
19208
|
}
|
|
19184
|
-
function
|
|
19185
|
-
const map2 = configMap ?? defaults;
|
|
19209
|
+
function buildLegacyLookup(legacyMap) {
|
|
19186
19210
|
const lookup = /* @__PURE__ */ new Map();
|
|
19187
|
-
for (const [marvinStatus,
|
|
19188
|
-
const
|
|
19189
|
-
for (const js of statuses) {
|
|
19211
|
+
for (const [marvinStatus, jiraStatuses] of Object.entries(legacyMap)) {
|
|
19212
|
+
for (const js of jiraStatuses) {
|
|
19190
19213
|
lookup.set(js.toLowerCase(), marvinStatus);
|
|
19191
19214
|
}
|
|
19192
19215
|
}
|
|
19193
|
-
|
|
19194
|
-
|
|
19195
|
-
|
|
19196
|
-
|
|
19197
|
-
|
|
19198
|
-
|
|
19199
|
-
|
|
19216
|
+
return lookup;
|
|
19217
|
+
}
|
|
19218
|
+
function buildFlatLookup(flatMap, inSprint) {
|
|
19219
|
+
const lookup = /* @__PURE__ */ new Map();
|
|
19220
|
+
for (const [jiraStatus, value] of Object.entries(flatMap)) {
|
|
19221
|
+
if (typeof value === "string") {
|
|
19222
|
+
lookup.set(jiraStatus.toLowerCase(), value);
|
|
19223
|
+
} else {
|
|
19224
|
+
const resolved = inSprint && value.inSprint ? value.inSprint : value.default;
|
|
19225
|
+
lookup.set(jiraStatus.toLowerCase(), resolved);
|
|
19200
19226
|
}
|
|
19201
19227
|
}
|
|
19202
19228
|
return lookup;
|
|
19203
19229
|
}
|
|
19204
|
-
function
|
|
19205
|
-
|
|
19230
|
+
function normalizeStatusMap(statusMap) {
|
|
19231
|
+
if (!statusMap) return {};
|
|
19232
|
+
if (isLegacyFormat(statusMap)) {
|
|
19233
|
+
return { legacy: statusMap };
|
|
19234
|
+
}
|
|
19235
|
+
return { flat: statusMap };
|
|
19236
|
+
}
|
|
19237
|
+
function mapJiraStatusForAction(status, resolved, inSprint = false) {
|
|
19238
|
+
if (resolved.flat) {
|
|
19239
|
+
const lookup2 = buildFlatLookup(resolved.flat, inSprint);
|
|
19240
|
+
return lookup2.get(status.toLowerCase()) ?? "open";
|
|
19241
|
+
}
|
|
19242
|
+
const lookup = buildLegacyLookup(resolved.legacy?.action ?? DEFAULT_ACTION_STATUS_MAP);
|
|
19206
19243
|
return lookup.get(status.toLowerCase()) ?? "open";
|
|
19207
19244
|
}
|
|
19208
|
-
function mapJiraStatusForTask(status,
|
|
19209
|
-
|
|
19245
|
+
function mapJiraStatusForTask(status, resolved, inSprint = false) {
|
|
19246
|
+
if (resolved.flat) {
|
|
19247
|
+
const lookup2 = buildFlatLookup(resolved.flat, inSprint);
|
|
19248
|
+
return lookup2.get(status.toLowerCase()) ?? "backlog";
|
|
19249
|
+
}
|
|
19250
|
+
const lookup = buildLegacyLookup(resolved.legacy?.task ?? DEFAULT_TASK_STATUS_MAP);
|
|
19210
19251
|
return lookup.get(status.toLowerCase()) ?? "backlog";
|
|
19211
19252
|
}
|
|
19212
19253
|
function isInActiveSprint(store, tags) {
|
|
@@ -19267,7 +19308,8 @@ async function fetchJiraStatus(store, client, host, artifactId, statusMap) {
|
|
|
19267
19308
|
try {
|
|
19268
19309
|
const issue2 = await client.getIssueWithLinks(jiraKey);
|
|
19269
19310
|
const inSprint = isInActiveSprint(store, doc.frontmatter.tags);
|
|
19270
|
-
const
|
|
19311
|
+
const resolved = statusMap ?? {};
|
|
19312
|
+
const proposedStatus = artifactType === "task" ? mapJiraStatusForTask(issue2.fields.status.name, resolved, inSprint) : mapJiraStatusForAction(issue2.fields.status.name, resolved, inSprint);
|
|
19271
19313
|
const currentStatus = doc.frontmatter.status;
|
|
19272
19314
|
const linkedIssues = [];
|
|
19273
19315
|
if (issue2.fields.subtasks) {
|
|
@@ -19685,7 +19727,8 @@ async function processIssue(issue2, client, host, dateRange, jiraKeyToArtifacts,
|
|
|
19685
19727
|
const jiraStatus = issue2.fields.status?.name;
|
|
19686
19728
|
if (jiraStatus) {
|
|
19687
19729
|
const inSprint = store ? isInActiveSprint(store, fm.tags) : false;
|
|
19688
|
-
|
|
19730
|
+
const resolved = statusMap ?? {};
|
|
19731
|
+
proposedStatus = artifactType === "task" ? mapJiraStatusForTask(jiraStatus, resolved, inSprint) : mapJiraStatusForAction(jiraStatus, resolved, inSprint);
|
|
19689
19732
|
}
|
|
19690
19733
|
}
|
|
19691
19734
|
marvinArtifacts.push({
|
|
@@ -19814,20 +19857,6 @@ var COMPLEXITY_WEIGHTS = {
|
|
|
19814
19857
|
"very-complex": 8
|
|
19815
19858
|
};
|
|
19816
19859
|
var DEFAULT_WEIGHT = 3;
|
|
19817
|
-
var STATUS_PROGRESS_DEFAULTS = {
|
|
19818
|
-
done: 100,
|
|
19819
|
-
closed: 100,
|
|
19820
|
-
resolved: 100,
|
|
19821
|
-
obsolete: 100,
|
|
19822
|
-
"wont do": 100,
|
|
19823
|
-
cancelled: 100,
|
|
19824
|
-
review: 80,
|
|
19825
|
-
"in-progress": 40,
|
|
19826
|
-
ready: 5,
|
|
19827
|
-
backlog: 0,
|
|
19828
|
-
open: 0
|
|
19829
|
-
};
|
|
19830
|
-
var BLOCKED_DEFAULT_PROGRESS = 10;
|
|
19831
19860
|
function resolveWeight(complexity) {
|
|
19832
19861
|
if (complexity && complexity in COMPLEXITY_WEIGHTS) {
|
|
19833
19862
|
return { weight: COMPLEXITY_WEIGHTS[complexity], weightSource: "complexity" };
|
|
@@ -19843,9 +19872,6 @@ function resolveProgress(frontmatter, commentAnalysisProgress) {
|
|
|
19843
19872
|
return { progress: Math.max(0, Math.min(100, Math.round(commentAnalysisProgress))), progressSource: "comment-analysis" };
|
|
19844
19873
|
}
|
|
19845
19874
|
const status = frontmatter.status;
|
|
19846
|
-
if (status === "blocked") {
|
|
19847
|
-
return { progress: BLOCKED_DEFAULT_PROGRESS, progressSource: "status-default" };
|
|
19848
|
-
}
|
|
19849
19875
|
const defaultProgress = STATUS_PROGRESS_DEFAULTS[status] ?? 0;
|
|
19850
19876
|
return { progress: defaultProgress, progressSource: "status-default" };
|
|
19851
19877
|
}
|
|
@@ -19938,7 +19964,8 @@ async function assessSprintProgress(store, client, host, options = {}) {
|
|
|
19938
19964
|
if (jiraData) {
|
|
19939
19965
|
jiraStatus = jiraData.issue.fields.status.name;
|
|
19940
19966
|
const inSprint = isInActiveSprint(store, fm.tags);
|
|
19941
|
-
|
|
19967
|
+
const resolved = options.statusMap ?? {};
|
|
19968
|
+
proposedMarvinStatus = fm.type === "task" ? mapJiraStatusForTask(jiraStatus, resolved, inSprint) : mapJiraStatusForAction(jiraStatus, resolved, inSprint);
|
|
19942
19969
|
const subtasks = jiraData.issue.fields.subtasks ?? [];
|
|
19943
19970
|
if (subtasks.length > 0) {
|
|
19944
19971
|
jiraSubtaskProgress = computeSubtaskProgress(subtasks);
|
|
@@ -19969,6 +19996,20 @@ async function assessSprintProgress(store, client, host, options = {}) {
|
|
|
19969
19996
|
proposedValue: jiraSubtaskProgress,
|
|
19970
19997
|
reason: `Jira ${jiraKey} subtask progress is ${jiraSubtaskProgress}%`
|
|
19971
19998
|
});
|
|
19999
|
+
} else if (statusDrift && proposedMarvinStatus && !fm.progressOverride) {
|
|
20000
|
+
const hasExplicitProgress = "progress" in fm && typeof fm.progress === "number";
|
|
20001
|
+
if (!hasExplicitProgress) {
|
|
20002
|
+
const proposedProgress = STATUS_PROGRESS_DEFAULTS[proposedMarvinStatus] ?? 0;
|
|
20003
|
+
if (proposedProgress !== currentProgress) {
|
|
20004
|
+
proposedUpdates.push({
|
|
20005
|
+
artifactId: fm.id,
|
|
20006
|
+
field: "progress",
|
|
20007
|
+
currentValue: currentProgress,
|
|
20008
|
+
proposedValue: proposedProgress,
|
|
20009
|
+
reason: `Status changing to "${proposedMarvinStatus}" \u2192 default progress ${proposedProgress}%`
|
|
20010
|
+
});
|
|
20011
|
+
}
|
|
20012
|
+
}
|
|
19972
20013
|
}
|
|
19973
20014
|
const tags = fm.tags ?? [];
|
|
19974
20015
|
const focusTag = tags.find((t) => t.startsWith("focus:"));
|
|
@@ -20347,7 +20388,7 @@ function findByJiraKey(store, jiraKey) {
|
|
|
20347
20388
|
function createJiraTools(store, projectConfig) {
|
|
20348
20389
|
const jiraUserConfig = loadUserConfig().jira;
|
|
20349
20390
|
const defaultProjectKey = projectConfig?.jira?.projectKey;
|
|
20350
|
-
const statusMap = projectConfig?.jira?.statusMap;
|
|
20391
|
+
const statusMap = normalizeStatusMap(projectConfig?.jira?.statusMap);
|
|
20351
20392
|
return [
|
|
20352
20393
|
// --- Local read tools ---
|
|
20353
20394
|
tool20(
|
|
@@ -20974,22 +21015,31 @@ function createJiraTools(store, projectConfig) {
|
|
|
20974
21015
|
const s = issue2.fields.status.name;
|
|
20975
21016
|
statusCounts.set(s, (statusCounts.get(s) ?? 0) + 1);
|
|
20976
21017
|
}
|
|
20977
|
-
const actionMap = statusMap?.action ?? DEFAULT_ACTION_STATUS_MAP;
|
|
20978
|
-
const taskMap = statusMap?.task ?? DEFAULT_TASK_STATUS_MAP;
|
|
20979
21018
|
const actionLookup = /* @__PURE__ */ new Map();
|
|
20980
|
-
for (const [marvin, value] of Object.entries(actionMap)) {
|
|
20981
|
-
const jiraStatuses = Array.isArray(value) ? value : value.default;
|
|
20982
|
-
for (const js of jiraStatuses) actionLookup.set(js.toLowerCase(), marvin);
|
|
20983
|
-
if (!Array.isArray(value) && value.inSprint) {
|
|
20984
|
-
for (const js of value.inSprint) actionLookup.set(js.toLowerCase(), `${marvin} (inSprint)`);
|
|
20985
|
-
}
|
|
20986
|
-
}
|
|
20987
21019
|
const taskLookup = /* @__PURE__ */ new Map();
|
|
20988
|
-
|
|
20989
|
-
const
|
|
20990
|
-
|
|
20991
|
-
|
|
20992
|
-
|
|
21020
|
+
if (statusMap.flat) {
|
|
21021
|
+
for (const [jiraStatus, value] of Object.entries(statusMap.flat)) {
|
|
21022
|
+
const lower = jiraStatus.toLowerCase();
|
|
21023
|
+
if (typeof value === "string") {
|
|
21024
|
+
actionLookup.set(lower, value);
|
|
21025
|
+
taskLookup.set(lower, value);
|
|
21026
|
+
} else {
|
|
21027
|
+
actionLookup.set(lower, value.default);
|
|
21028
|
+
taskLookup.set(lower, value.default);
|
|
21029
|
+
if (value.inSprint) {
|
|
21030
|
+
actionLookup.set(lower, `${value.default} / ${value.inSprint} (inSprint)`);
|
|
21031
|
+
taskLookup.set(lower, `${value.default} / ${value.inSprint} (inSprint)`);
|
|
21032
|
+
}
|
|
21033
|
+
}
|
|
21034
|
+
}
|
|
21035
|
+
} else {
|
|
21036
|
+
const actionMap = statusMap.legacy?.action ?? DEFAULT_ACTION_STATUS_MAP;
|
|
21037
|
+
const taskMap = statusMap.legacy?.task ?? DEFAULT_TASK_STATUS_MAP;
|
|
21038
|
+
for (const [marvin, jiraStatuses] of Object.entries(actionMap)) {
|
|
21039
|
+
for (const js of jiraStatuses) actionLookup.set(js.toLowerCase(), marvin);
|
|
21040
|
+
}
|
|
21041
|
+
for (const [marvin, jiraStatuses] of Object.entries(taskMap)) {
|
|
21042
|
+
for (const js of jiraStatuses) taskLookup.set(js.toLowerCase(), marvin);
|
|
20993
21043
|
}
|
|
20994
21044
|
}
|
|
20995
21045
|
const parts = [
|
|
@@ -21011,25 +21061,20 @@ function createJiraTools(store, projectConfig) {
|
|
|
21011
21061
|
if (!taskTarget) unmappedTask.push(status);
|
|
21012
21062
|
}
|
|
21013
21063
|
if (unmappedAction.length > 0 || unmappedTask.length > 0) {
|
|
21064
|
+
const allUnmapped = [.../* @__PURE__ */ new Set([...unmappedAction, ...unmappedTask])];
|
|
21014
21065
|
parts.push("");
|
|
21015
21066
|
parts.push("To fix unmapped statuses, add jira.statusMap to .marvin/config.yaml:");
|
|
21016
21067
|
parts.push(" jira:");
|
|
21017
21068
|
parts.push(" statusMap:");
|
|
21018
|
-
|
|
21019
|
-
parts.push("
|
|
21020
|
-
parts.push(` # Map these: ${unmappedAction.join(", ")}`);
|
|
21021
|
-
parts.push(" # <marvin-status>: [<jira-status>, ...]");
|
|
21022
|
-
}
|
|
21023
|
-
if (unmappedTask.length > 0) {
|
|
21024
|
-
parts.push(" task:");
|
|
21025
|
-
parts.push(` # Map these: ${unmappedTask.join(", ")}`);
|
|
21026
|
-
parts.push(" # <marvin-status>: [<jira-status>, ...]");
|
|
21069
|
+
for (const s of allUnmapped) {
|
|
21070
|
+
parts.push(` "${s}": <marvin-status>`);
|
|
21027
21071
|
}
|
|
21072
|
+
parts.push(" # Supported marvin statuses: done, in-progress, review, ready, blocked, backlog, open");
|
|
21028
21073
|
} else {
|
|
21029
21074
|
parts.push("");
|
|
21030
21075
|
parts.push("All statuses are mapped.");
|
|
21031
21076
|
}
|
|
21032
|
-
const usingConfig = statusMap
|
|
21077
|
+
const usingConfig = statusMap.flat || statusMap.legacy;
|
|
21033
21078
|
parts.push("");
|
|
21034
21079
|
parts.push(usingConfig ? "Using status maps from .marvin/config.yaml." : "Using built-in default status maps (no jira.statusMap in config).");
|
|
21035
21080
|
return {
|