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/index.d.ts
CHANGED
|
@@ -16,18 +16,39 @@ interface MarvinUserConfig {
|
|
|
16
16
|
interface GitConfig {
|
|
17
17
|
remote?: string;
|
|
18
18
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Conditional status mapping: Jira status → Marvin status with sprint context.
|
|
21
|
+
* Used when a Jira status should map differently based on sprint membership.
|
|
22
|
+
*/
|
|
23
|
+
interface ConditionalJiraStatusMapping {
|
|
24
|
+
default: string;
|
|
25
|
+
inSprint?: string;
|
|
22
26
|
}
|
|
23
|
-
|
|
24
|
-
|
|
27
|
+
/**
|
|
28
|
+
* Flat Jira→Marvin status map (spec format).
|
|
29
|
+
* Keys are Jira status names, values are Marvin status strings or conditional objects.
|
|
30
|
+
*
|
|
31
|
+
* Example:
|
|
32
|
+
* "In Progress": in-progress
|
|
33
|
+
* "To Do":
|
|
34
|
+
* default: backlog
|
|
35
|
+
* inSprint: ready
|
|
36
|
+
*/
|
|
37
|
+
interface FlatJiraStatusMap {
|
|
38
|
+
[jiraStatus: string]: string | ConditionalJiraStatusMapping;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Legacy Marvin→Jira[] status map (nested format).
|
|
42
|
+
* Keys are Marvin status names, values are arrays of Jira status names.
|
|
43
|
+
*/
|
|
44
|
+
interface LegacyJiraStatusMap {
|
|
45
|
+
[marvinStatus: string]: string[];
|
|
25
46
|
}
|
|
26
47
|
interface JiraProjectConfig {
|
|
27
48
|
projectKey?: string;
|
|
28
|
-
statusMap?: {
|
|
29
|
-
action?:
|
|
30
|
-
task?:
|
|
49
|
+
statusMap?: FlatJiraStatusMap | {
|
|
50
|
+
action?: LegacyJiraStatusMap;
|
|
51
|
+
task?: LegacyJiraStatusMap;
|
|
31
52
|
};
|
|
32
53
|
}
|
|
33
54
|
interface MarvinProjectConfig {
|
package/dist/index.js
CHANGED
|
@@ -14505,11 +14505,25 @@ import { tool as tool2 } from "@anthropic-ai/claude-agent-sdk";
|
|
|
14505
14505
|
|
|
14506
14506
|
// src/storage/progress.ts
|
|
14507
14507
|
var DONE_STATUSES = /* @__PURE__ */ new Set(["done", "closed", "resolved", "cancelled"]);
|
|
14508
|
+
var STATUS_PROGRESS_DEFAULTS = {
|
|
14509
|
+
done: 100,
|
|
14510
|
+
closed: 100,
|
|
14511
|
+
resolved: 100,
|
|
14512
|
+
obsolete: 100,
|
|
14513
|
+
"wont do": 100,
|
|
14514
|
+
cancelled: 100,
|
|
14515
|
+
review: 80,
|
|
14516
|
+
"in-progress": 40,
|
|
14517
|
+
ready: 5,
|
|
14518
|
+
blocked: 10,
|
|
14519
|
+
backlog: 0,
|
|
14520
|
+
open: 0
|
|
14521
|
+
};
|
|
14508
14522
|
function getEffectiveProgress(frontmatter) {
|
|
14509
14523
|
if (DONE_STATUSES.has(frontmatter.status)) return 100;
|
|
14510
14524
|
const raw = frontmatter.progress;
|
|
14511
14525
|
if (typeof raw === "number") return Math.max(0, Math.min(100, Math.round(raw)));
|
|
14512
|
-
return 0;
|
|
14526
|
+
return STATUS_PROGRESS_DEFAULTS[frontmatter.status] ?? 0;
|
|
14513
14527
|
}
|
|
14514
14528
|
function propagateProgressFromTask(store, taskId) {
|
|
14515
14529
|
const updated = [];
|
|
@@ -25092,35 +25106,62 @@ var DEFAULT_TASK_STATUS_MAP = {
|
|
|
25092
25106
|
blocked: ["Blocked"],
|
|
25093
25107
|
backlog: ["To Do", "Open", "Backlog", "New"]
|
|
25094
25108
|
};
|
|
25095
|
-
function
|
|
25096
|
-
|
|
25109
|
+
function isLegacyFormat(statusMap) {
|
|
25110
|
+
if (!statusMap || typeof statusMap !== "object") return false;
|
|
25111
|
+
const keys = Object.keys(statusMap);
|
|
25112
|
+
if (!keys.every((k) => k === "action" || k === "task")) return false;
|
|
25113
|
+
for (const key of keys) {
|
|
25114
|
+
const val = statusMap[key];
|
|
25115
|
+
if (typeof val !== "object" || val === null) return false;
|
|
25116
|
+
for (const innerVal of Object.values(val)) {
|
|
25117
|
+
if (!Array.isArray(innerVal)) return false;
|
|
25118
|
+
if (!innerVal.every((v) => typeof v === "string")) return false;
|
|
25119
|
+
}
|
|
25120
|
+
}
|
|
25121
|
+
return true;
|
|
25097
25122
|
}
|
|
25098
|
-
function
|
|
25099
|
-
const map2 = configMap ?? defaults;
|
|
25123
|
+
function buildLegacyLookup(legacyMap) {
|
|
25100
25124
|
const lookup = /* @__PURE__ */ new Map();
|
|
25101
|
-
for (const [marvinStatus,
|
|
25102
|
-
const
|
|
25103
|
-
for (const js of statuses) {
|
|
25125
|
+
for (const [marvinStatus, jiraStatuses] of Object.entries(legacyMap)) {
|
|
25126
|
+
for (const js of jiraStatuses) {
|
|
25104
25127
|
lookup.set(js.toLowerCase(), marvinStatus);
|
|
25105
25128
|
}
|
|
25106
25129
|
}
|
|
25107
|
-
|
|
25108
|
-
|
|
25109
|
-
|
|
25110
|
-
|
|
25111
|
-
|
|
25112
|
-
|
|
25113
|
-
|
|
25130
|
+
return lookup;
|
|
25131
|
+
}
|
|
25132
|
+
function buildFlatLookup(flatMap, inSprint) {
|
|
25133
|
+
const lookup = /* @__PURE__ */ new Map();
|
|
25134
|
+
for (const [jiraStatus, value] of Object.entries(flatMap)) {
|
|
25135
|
+
if (typeof value === "string") {
|
|
25136
|
+
lookup.set(jiraStatus.toLowerCase(), value);
|
|
25137
|
+
} else {
|
|
25138
|
+
const resolved = inSprint && value.inSprint ? value.inSprint : value.default;
|
|
25139
|
+
lookup.set(jiraStatus.toLowerCase(), resolved);
|
|
25114
25140
|
}
|
|
25115
25141
|
}
|
|
25116
25142
|
return lookup;
|
|
25117
25143
|
}
|
|
25118
|
-
function
|
|
25119
|
-
|
|
25144
|
+
function normalizeStatusMap(statusMap) {
|
|
25145
|
+
if (!statusMap) return {};
|
|
25146
|
+
if (isLegacyFormat(statusMap)) {
|
|
25147
|
+
return { legacy: statusMap };
|
|
25148
|
+
}
|
|
25149
|
+
return { flat: statusMap };
|
|
25150
|
+
}
|
|
25151
|
+
function mapJiraStatusForAction(status, resolved, inSprint = false) {
|
|
25152
|
+
if (resolved.flat) {
|
|
25153
|
+
const lookup2 = buildFlatLookup(resolved.flat, inSprint);
|
|
25154
|
+
return lookup2.get(status.toLowerCase()) ?? "open";
|
|
25155
|
+
}
|
|
25156
|
+
const lookup = buildLegacyLookup(resolved.legacy?.action ?? DEFAULT_ACTION_STATUS_MAP);
|
|
25120
25157
|
return lookup.get(status.toLowerCase()) ?? "open";
|
|
25121
25158
|
}
|
|
25122
|
-
function mapJiraStatusForTask(status,
|
|
25123
|
-
|
|
25159
|
+
function mapJiraStatusForTask(status, resolved, inSprint = false) {
|
|
25160
|
+
if (resolved.flat) {
|
|
25161
|
+
const lookup2 = buildFlatLookup(resolved.flat, inSprint);
|
|
25162
|
+
return lookup2.get(status.toLowerCase()) ?? "backlog";
|
|
25163
|
+
}
|
|
25164
|
+
const lookup = buildLegacyLookup(resolved.legacy?.task ?? DEFAULT_TASK_STATUS_MAP);
|
|
25124
25165
|
return lookup.get(status.toLowerCase()) ?? "backlog";
|
|
25125
25166
|
}
|
|
25126
25167
|
function isInActiveSprint(store, tags) {
|
|
@@ -25181,7 +25222,8 @@ async function fetchJiraStatus(store, client, host, artifactId, statusMap) {
|
|
|
25181
25222
|
try {
|
|
25182
25223
|
const issue2 = await client.getIssueWithLinks(jiraKey);
|
|
25183
25224
|
const inSprint = isInActiveSprint(store, doc.frontmatter.tags);
|
|
25184
|
-
const
|
|
25225
|
+
const resolved = statusMap ?? {};
|
|
25226
|
+
const proposedStatus = artifactType === "task" ? mapJiraStatusForTask(issue2.fields.status.name, resolved, inSprint) : mapJiraStatusForAction(issue2.fields.status.name, resolved, inSprint);
|
|
25185
25227
|
const currentStatus = doc.frontmatter.status;
|
|
25186
25228
|
const linkedIssues = [];
|
|
25187
25229
|
if (issue2.fields.subtasks) {
|
|
@@ -25639,7 +25681,8 @@ async function processIssue(issue2, client, host, dateRange, jiraKeyToArtifacts,
|
|
|
25639
25681
|
const jiraStatus = issue2.fields.status?.name;
|
|
25640
25682
|
if (jiraStatus) {
|
|
25641
25683
|
const inSprint = store ? isInActiveSprint(store, fm.tags) : false;
|
|
25642
|
-
|
|
25684
|
+
const resolved = statusMap ?? {};
|
|
25685
|
+
proposedStatus = artifactType === "task" ? mapJiraStatusForTask(jiraStatus, resolved, inSprint) : mapJiraStatusForAction(jiraStatus, resolved, inSprint);
|
|
25643
25686
|
}
|
|
25644
25687
|
}
|
|
25645
25688
|
marvinArtifacts.push({
|
|
@@ -25768,20 +25811,6 @@ var COMPLEXITY_WEIGHTS = {
|
|
|
25768
25811
|
"very-complex": 8
|
|
25769
25812
|
};
|
|
25770
25813
|
var DEFAULT_WEIGHT = 3;
|
|
25771
|
-
var STATUS_PROGRESS_DEFAULTS = {
|
|
25772
|
-
done: 100,
|
|
25773
|
-
closed: 100,
|
|
25774
|
-
resolved: 100,
|
|
25775
|
-
obsolete: 100,
|
|
25776
|
-
"wont do": 100,
|
|
25777
|
-
cancelled: 100,
|
|
25778
|
-
review: 80,
|
|
25779
|
-
"in-progress": 40,
|
|
25780
|
-
ready: 5,
|
|
25781
|
-
backlog: 0,
|
|
25782
|
-
open: 0
|
|
25783
|
-
};
|
|
25784
|
-
var BLOCKED_DEFAULT_PROGRESS = 10;
|
|
25785
25814
|
function resolveWeight(complexity) {
|
|
25786
25815
|
if (complexity && complexity in COMPLEXITY_WEIGHTS) {
|
|
25787
25816
|
return { weight: COMPLEXITY_WEIGHTS[complexity], weightSource: "complexity" };
|
|
@@ -25797,9 +25826,6 @@ function resolveProgress(frontmatter, commentAnalysisProgress) {
|
|
|
25797
25826
|
return { progress: Math.max(0, Math.min(100, Math.round(commentAnalysisProgress))), progressSource: "comment-analysis" };
|
|
25798
25827
|
}
|
|
25799
25828
|
const status = frontmatter.status;
|
|
25800
|
-
if (status === "blocked") {
|
|
25801
|
-
return { progress: BLOCKED_DEFAULT_PROGRESS, progressSource: "status-default" };
|
|
25802
|
-
}
|
|
25803
25829
|
const defaultProgress = STATUS_PROGRESS_DEFAULTS[status] ?? 0;
|
|
25804
25830
|
return { progress: defaultProgress, progressSource: "status-default" };
|
|
25805
25831
|
}
|
|
@@ -25892,7 +25918,8 @@ async function assessSprintProgress(store, client, host, options = {}) {
|
|
|
25892
25918
|
if (jiraData) {
|
|
25893
25919
|
jiraStatus = jiraData.issue.fields.status.name;
|
|
25894
25920
|
const inSprint = isInActiveSprint(store, fm.tags);
|
|
25895
|
-
|
|
25921
|
+
const resolved = options.statusMap ?? {};
|
|
25922
|
+
proposedMarvinStatus = fm.type === "task" ? mapJiraStatusForTask(jiraStatus, resolved, inSprint) : mapJiraStatusForAction(jiraStatus, resolved, inSprint);
|
|
25896
25923
|
const subtasks = jiraData.issue.fields.subtasks ?? [];
|
|
25897
25924
|
if (subtasks.length > 0) {
|
|
25898
25925
|
jiraSubtaskProgress = computeSubtaskProgress(subtasks);
|
|
@@ -25923,6 +25950,20 @@ async function assessSprintProgress(store, client, host, options = {}) {
|
|
|
25923
25950
|
proposedValue: jiraSubtaskProgress,
|
|
25924
25951
|
reason: `Jira ${jiraKey} subtask progress is ${jiraSubtaskProgress}%`
|
|
25925
25952
|
});
|
|
25953
|
+
} else if (statusDrift && proposedMarvinStatus && !fm.progressOverride) {
|
|
25954
|
+
const hasExplicitProgress = "progress" in fm && typeof fm.progress === "number";
|
|
25955
|
+
if (!hasExplicitProgress) {
|
|
25956
|
+
const proposedProgress = STATUS_PROGRESS_DEFAULTS[proposedMarvinStatus] ?? 0;
|
|
25957
|
+
if (proposedProgress !== currentProgress) {
|
|
25958
|
+
proposedUpdates.push({
|
|
25959
|
+
artifactId: fm.id,
|
|
25960
|
+
field: "progress",
|
|
25961
|
+
currentValue: currentProgress,
|
|
25962
|
+
proposedValue: proposedProgress,
|
|
25963
|
+
reason: `Status changing to "${proposedMarvinStatus}" \u2192 default progress ${proposedProgress}%`
|
|
25964
|
+
});
|
|
25965
|
+
}
|
|
25966
|
+
}
|
|
25926
25967
|
}
|
|
25927
25968
|
const tags = fm.tags ?? [];
|
|
25928
25969
|
const focusTag = tags.find((t) => t.startsWith("focus:"));
|
|
@@ -26301,7 +26342,7 @@ function findByJiraKey(store, jiraKey) {
|
|
|
26301
26342
|
function createJiraTools(store, projectConfig) {
|
|
26302
26343
|
const jiraUserConfig = loadUserConfig().jira;
|
|
26303
26344
|
const defaultProjectKey = projectConfig?.jira?.projectKey;
|
|
26304
|
-
const statusMap = projectConfig?.jira?.statusMap;
|
|
26345
|
+
const statusMap = normalizeStatusMap(projectConfig?.jira?.statusMap);
|
|
26305
26346
|
return [
|
|
26306
26347
|
// --- Local read tools ---
|
|
26307
26348
|
tool20(
|
|
@@ -26928,22 +26969,31 @@ function createJiraTools(store, projectConfig) {
|
|
|
26928
26969
|
const s = issue2.fields.status.name;
|
|
26929
26970
|
statusCounts.set(s, (statusCounts.get(s) ?? 0) + 1);
|
|
26930
26971
|
}
|
|
26931
|
-
const actionMap = statusMap?.action ?? DEFAULT_ACTION_STATUS_MAP;
|
|
26932
|
-
const taskMap = statusMap?.task ?? DEFAULT_TASK_STATUS_MAP;
|
|
26933
26972
|
const actionLookup = /* @__PURE__ */ new Map();
|
|
26934
|
-
for (const [marvin, value] of Object.entries(actionMap)) {
|
|
26935
|
-
const jiraStatuses = Array.isArray(value) ? value : value.default;
|
|
26936
|
-
for (const js of jiraStatuses) actionLookup.set(js.toLowerCase(), marvin);
|
|
26937
|
-
if (!Array.isArray(value) && value.inSprint) {
|
|
26938
|
-
for (const js of value.inSprint) actionLookup.set(js.toLowerCase(), `${marvin} (inSprint)`);
|
|
26939
|
-
}
|
|
26940
|
-
}
|
|
26941
26973
|
const taskLookup = /* @__PURE__ */ new Map();
|
|
26942
|
-
|
|
26943
|
-
const
|
|
26944
|
-
|
|
26945
|
-
|
|
26946
|
-
|
|
26974
|
+
if (statusMap.flat) {
|
|
26975
|
+
for (const [jiraStatus, value] of Object.entries(statusMap.flat)) {
|
|
26976
|
+
const lower = jiraStatus.toLowerCase();
|
|
26977
|
+
if (typeof value === "string") {
|
|
26978
|
+
actionLookup.set(lower, value);
|
|
26979
|
+
taskLookup.set(lower, value);
|
|
26980
|
+
} else {
|
|
26981
|
+
actionLookup.set(lower, value.default);
|
|
26982
|
+
taskLookup.set(lower, value.default);
|
|
26983
|
+
if (value.inSprint) {
|
|
26984
|
+
actionLookup.set(lower, `${value.default} / ${value.inSprint} (inSprint)`);
|
|
26985
|
+
taskLookup.set(lower, `${value.default} / ${value.inSprint} (inSprint)`);
|
|
26986
|
+
}
|
|
26987
|
+
}
|
|
26988
|
+
}
|
|
26989
|
+
} else {
|
|
26990
|
+
const actionMap = statusMap.legacy?.action ?? DEFAULT_ACTION_STATUS_MAP;
|
|
26991
|
+
const taskMap = statusMap.legacy?.task ?? DEFAULT_TASK_STATUS_MAP;
|
|
26992
|
+
for (const [marvin, jiraStatuses] of Object.entries(actionMap)) {
|
|
26993
|
+
for (const js of jiraStatuses) actionLookup.set(js.toLowerCase(), marvin);
|
|
26994
|
+
}
|
|
26995
|
+
for (const [marvin, jiraStatuses] of Object.entries(taskMap)) {
|
|
26996
|
+
for (const js of jiraStatuses) taskLookup.set(js.toLowerCase(), marvin);
|
|
26947
26997
|
}
|
|
26948
26998
|
}
|
|
26949
26999
|
const parts = [
|
|
@@ -26965,25 +27015,20 @@ function createJiraTools(store, projectConfig) {
|
|
|
26965
27015
|
if (!taskTarget) unmappedTask.push(status);
|
|
26966
27016
|
}
|
|
26967
27017
|
if (unmappedAction.length > 0 || unmappedTask.length > 0) {
|
|
27018
|
+
const allUnmapped = [.../* @__PURE__ */ new Set([...unmappedAction, ...unmappedTask])];
|
|
26968
27019
|
parts.push("");
|
|
26969
27020
|
parts.push("To fix unmapped statuses, add jira.statusMap to .marvin/config.yaml:");
|
|
26970
27021
|
parts.push(" jira:");
|
|
26971
27022
|
parts.push(" statusMap:");
|
|
26972
|
-
|
|
26973
|
-
parts.push("
|
|
26974
|
-
parts.push(` # Map these: ${unmappedAction.join(", ")}`);
|
|
26975
|
-
parts.push(" # <marvin-status>: [<jira-status>, ...]");
|
|
26976
|
-
}
|
|
26977
|
-
if (unmappedTask.length > 0) {
|
|
26978
|
-
parts.push(" task:");
|
|
26979
|
-
parts.push(` # Map these: ${unmappedTask.join(", ")}`);
|
|
26980
|
-
parts.push(" # <marvin-status>: [<jira-status>, ...]");
|
|
27023
|
+
for (const s of allUnmapped) {
|
|
27024
|
+
parts.push(` "${s}": <marvin-status>`);
|
|
26981
27025
|
}
|
|
27026
|
+
parts.push(" # Supported marvin statuses: done, in-progress, review, ready, blocked, backlog, open");
|
|
26982
27027
|
} else {
|
|
26983
27028
|
parts.push("");
|
|
26984
27029
|
parts.push("All statuses are mapped.");
|
|
26985
27030
|
}
|
|
26986
|
-
const usingConfig = statusMap
|
|
27031
|
+
const usingConfig = statusMap.flat || statusMap.legacy;
|
|
26987
27032
|
parts.push("");
|
|
26988
27033
|
parts.push(usingConfig ? "Using status maps from .marvin/config.yaml." : "Using built-in default status maps (no jira.statusMap in config).");
|
|
26989
27034
|
return {
|
|
@@ -32372,7 +32417,7 @@ async function jiraSyncCommand(artifactId, options = {}) {
|
|
|
32372
32417
|
);
|
|
32373
32418
|
return;
|
|
32374
32419
|
}
|
|
32375
|
-
const statusMap = project.config.jira?.statusMap;
|
|
32420
|
+
const statusMap = normalizeStatusMap(project.config.jira?.statusMap);
|
|
32376
32421
|
const label = artifactId ? `Checking ${artifactId} against Jira...` : "Checking all Jira-linked actions/tasks...";
|
|
32377
32422
|
console.log(chalk20.dim(label));
|
|
32378
32423
|
if (options.dryRun) {
|
|
@@ -32489,9 +32534,7 @@ async function jiraStatusesCommand(projectKey) {
|
|
|
32489
32534
|
return;
|
|
32490
32535
|
}
|
|
32491
32536
|
console.log(chalk20.dim(`Fetching statuses from Jira project ${resolvedProjectKey}...`));
|
|
32492
|
-
const statusMap = project.config.jira?.statusMap;
|
|
32493
|
-
const actionMap = statusMap?.action ?? DEFAULT_ACTION_STATUS_MAP;
|
|
32494
|
-
const taskMap = statusMap?.task ?? DEFAULT_TASK_STATUS_MAP;
|
|
32537
|
+
const statusMap = normalizeStatusMap(project.config.jira?.statusMap);
|
|
32495
32538
|
const email3 = jiraUserConfig?.email ?? process.env.JIRA_EMAIL;
|
|
32496
32539
|
const apiToken = jiraUserConfig?.apiToken ?? process.env.JIRA_API_TOKEN;
|
|
32497
32540
|
const auth = "Basic " + Buffer.from(`${email3}:${apiToken}`).toString("base64");
|
|
@@ -32515,14 +32558,28 @@ async function jiraStatusesCommand(projectKey) {
|
|
|
32515
32558
|
statusCounts.set(s, (statusCounts.get(s) ?? 0) + 1);
|
|
32516
32559
|
}
|
|
32517
32560
|
const actionLookup = /* @__PURE__ */ new Map();
|
|
32518
|
-
for (const [marvin, value] of Object.entries(actionMap)) {
|
|
32519
|
-
const jiraStatuses = Array.isArray(value) ? value : value.default;
|
|
32520
|
-
for (const js of jiraStatuses) actionLookup.set(js.toLowerCase(), marvin);
|
|
32521
|
-
}
|
|
32522
32561
|
const taskLookup = /* @__PURE__ */ new Map();
|
|
32523
|
-
|
|
32524
|
-
const
|
|
32525
|
-
|
|
32562
|
+
if (statusMap.flat) {
|
|
32563
|
+
for (const [jiraStatus, value] of Object.entries(statusMap.flat)) {
|
|
32564
|
+
const lower = jiraStatus.toLowerCase();
|
|
32565
|
+
if (typeof value === "string") {
|
|
32566
|
+
actionLookup.set(lower, value);
|
|
32567
|
+
taskLookup.set(lower, value);
|
|
32568
|
+
} else {
|
|
32569
|
+
const label = value.inSprint ? `${value.default} / ${value.inSprint} (inSprint)` : value.default;
|
|
32570
|
+
actionLookup.set(lower, label);
|
|
32571
|
+
taskLookup.set(lower, label);
|
|
32572
|
+
}
|
|
32573
|
+
}
|
|
32574
|
+
} else {
|
|
32575
|
+
const actionMap = statusMap.legacy?.action ?? DEFAULT_ACTION_STATUS_MAP;
|
|
32576
|
+
const taskMap = statusMap.legacy?.task ?? DEFAULT_TASK_STATUS_MAP;
|
|
32577
|
+
for (const [marvin, jiraStatuses] of Object.entries(actionMap)) {
|
|
32578
|
+
for (const js of jiraStatuses) actionLookup.set(js.toLowerCase(), marvin);
|
|
32579
|
+
}
|
|
32580
|
+
for (const [marvin, jiraStatuses] of Object.entries(taskMap)) {
|
|
32581
|
+
for (const js of jiraStatuses) taskLookup.set(js.toLowerCase(), marvin);
|
|
32582
|
+
}
|
|
32526
32583
|
}
|
|
32527
32584
|
console.log(
|
|
32528
32585
|
`
|
|
@@ -32545,14 +32602,11 @@ Found ${chalk20.bold(String(statusCounts.size))} distinct statuses in ${chalk20.
|
|
|
32545
32602
|
console.log(chalk20.yellow("\nSome statuses are unmapped. Add jira.statusMap to .marvin/config.yaml:"));
|
|
32546
32603
|
console.log(chalk20.dim(" jira:"));
|
|
32547
32604
|
console.log(chalk20.dim(" statusMap:"));
|
|
32548
|
-
console.log(chalk20.dim("
|
|
32549
|
-
console.log(chalk20.dim(' <marvin-status>: ["<Jira Status>", ...]'));
|
|
32550
|
-
console.log(chalk20.dim(" task:"));
|
|
32551
|
-
console.log(chalk20.dim(' <marvin-status>: ["<Jira Status>", ...]'));
|
|
32605
|
+
console.log(chalk20.dim(' "<Jira Status>": <marvin-status>'));
|
|
32552
32606
|
} else {
|
|
32553
32607
|
console.log(chalk20.green("\nAll statuses are mapped."));
|
|
32554
32608
|
}
|
|
32555
|
-
const usingConfig = statusMap
|
|
32609
|
+
const usingConfig = statusMap.flat || statusMap.legacy;
|
|
32556
32610
|
console.log(
|
|
32557
32611
|
chalk20.dim(
|
|
32558
32612
|
usingConfig ? "\nUsing status maps from .marvin/config.yaml." : "\nUsing built-in default status maps (no jira.statusMap in config)."
|
|
@@ -32587,7 +32641,7 @@ async function jiraDailyCommand(options) {
|
|
|
32587
32641
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
32588
32642
|
const fromDate = options.from ?? today;
|
|
32589
32643
|
const toDate = options.to ?? fromDate;
|
|
32590
|
-
const statusMap = proj.config.jira?.statusMap;
|
|
32644
|
+
const statusMap = normalizeStatusMap(proj.config.jira?.statusMap);
|
|
32591
32645
|
const rangeLabel = fromDate === toDate ? fromDate : `${fromDate} to ${toDate}`;
|
|
32592
32646
|
console.log(
|
|
32593
32647
|
chalk20.dim(`Fetching Jira daily summary for ${resolvedProjectKey} \u2014 ${rangeLabel}...`)
|
|
@@ -32704,7 +32758,7 @@ function createProgram() {
|
|
|
32704
32758
|
const program = new Command();
|
|
32705
32759
|
program.name("marvin").description(
|
|
32706
32760
|
"AI-powered product development assistant with Product Owner, Delivery Manager, and Technical Lead personas"
|
|
32707
|
-
).version("0.5.
|
|
32761
|
+
).version("0.5.17");
|
|
32708
32762
|
program.command("init").description("Initialize a new Marvin project in the current directory").action(async () => {
|
|
32709
32763
|
await initCommand();
|
|
32710
32764
|
});
|