mrvn-cli 0.5.15 → 0.5.16
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 +5 -1
- package/dist/index.js +59 -17
- package/dist/index.js.map +1 -1
- package/dist/marvin-serve.js +54 -14
- package/dist/marvin-serve.js.map +1 -1
- package/dist/marvin.js +59 -17
- package/dist/marvin.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -16,8 +16,12 @@ interface MarvinUserConfig {
|
|
|
16
16
|
interface GitConfig {
|
|
17
17
|
remote?: string;
|
|
18
18
|
}
|
|
19
|
+
interface ConditionalJiraStatusEntry {
|
|
20
|
+
default: string[];
|
|
21
|
+
inSprint?: string[];
|
|
22
|
+
}
|
|
19
23
|
interface JiraStatusMap {
|
|
20
|
-
[marvinStatus: string]: string[];
|
|
24
|
+
[marvinStatus: string]: string[] | ConditionalJiraStatusEntry;
|
|
21
25
|
}
|
|
22
26
|
interface JiraProjectConfig {
|
|
23
27
|
projectKey?: string;
|
package/dist/index.js
CHANGED
|
@@ -25092,24 +25092,53 @@ var DEFAULT_TASK_STATUS_MAP = {
|
|
|
25092
25092
|
blocked: ["Blocked"],
|
|
25093
25093
|
backlog: ["To Do", "Open", "Backlog", "New"]
|
|
25094
25094
|
};
|
|
25095
|
-
function
|
|
25095
|
+
function isConditionalEntry(value) {
|
|
25096
|
+
return !Array.isArray(value) && typeof value === "object" && "default" in value;
|
|
25097
|
+
}
|
|
25098
|
+
function buildStatusLookup(configMap, defaults, inSprint = false) {
|
|
25096
25099
|
const map2 = configMap ?? defaults;
|
|
25097
25100
|
const lookup = /* @__PURE__ */ new Map();
|
|
25098
|
-
for (const [marvinStatus,
|
|
25099
|
-
|
|
25101
|
+
for (const [marvinStatus, value] of Object.entries(map2)) {
|
|
25102
|
+
const statuses = isConditionalEntry(value) ? value.default : value;
|
|
25103
|
+
for (const js of statuses) {
|
|
25100
25104
|
lookup.set(js.toLowerCase(), marvinStatus);
|
|
25101
25105
|
}
|
|
25102
25106
|
}
|
|
25107
|
+
if (inSprint) {
|
|
25108
|
+
for (const [marvinStatus, value] of Object.entries(map2)) {
|
|
25109
|
+
if (isConditionalEntry(value) && value.inSprint) {
|
|
25110
|
+
for (const js of value.inSprint) {
|
|
25111
|
+
lookup.set(js.toLowerCase(), marvinStatus);
|
|
25112
|
+
}
|
|
25113
|
+
}
|
|
25114
|
+
}
|
|
25115
|
+
}
|
|
25103
25116
|
return lookup;
|
|
25104
25117
|
}
|
|
25105
|
-
function mapJiraStatusForAction(status, configMap) {
|
|
25106
|
-
const lookup = buildStatusLookup(configMap, DEFAULT_ACTION_STATUS_MAP);
|
|
25118
|
+
function mapJiraStatusForAction(status, configMap, inSprint) {
|
|
25119
|
+
const lookup = buildStatusLookup(configMap, DEFAULT_ACTION_STATUS_MAP, inSprint ?? false);
|
|
25107
25120
|
return lookup.get(status.toLowerCase()) ?? "open";
|
|
25108
25121
|
}
|
|
25109
|
-
function mapJiraStatusForTask(status, configMap) {
|
|
25110
|
-
const lookup = buildStatusLookup(configMap, DEFAULT_TASK_STATUS_MAP);
|
|
25122
|
+
function mapJiraStatusForTask(status, configMap, inSprint) {
|
|
25123
|
+
const lookup = buildStatusLookup(configMap, DEFAULT_TASK_STATUS_MAP, inSprint ?? false);
|
|
25111
25124
|
return lookup.get(status.toLowerCase()) ?? "backlog";
|
|
25112
25125
|
}
|
|
25126
|
+
function isInActiveSprint(store, tags) {
|
|
25127
|
+
if (!tags) return false;
|
|
25128
|
+
const sprintTags = tags.filter((t) => t.startsWith("sprint:"));
|
|
25129
|
+
if (sprintTags.length === 0) return false;
|
|
25130
|
+
for (const tag of sprintTags) {
|
|
25131
|
+
const sprintId = tag.slice(7);
|
|
25132
|
+
const sprintDoc = store.get(sprintId);
|
|
25133
|
+
if (sprintDoc) {
|
|
25134
|
+
const status = sprintDoc.frontmatter.status;
|
|
25135
|
+
if (status === "active" || status === "completed") {
|
|
25136
|
+
return true;
|
|
25137
|
+
}
|
|
25138
|
+
}
|
|
25139
|
+
}
|
|
25140
|
+
return false;
|
|
25141
|
+
}
|
|
25113
25142
|
function extractJiraKeyFromTags(tags) {
|
|
25114
25143
|
if (!tags) return void 0;
|
|
25115
25144
|
const tag = tags.find((t) => /^jira:[A-Z]+-\d+$/i.test(t));
|
|
@@ -25151,7 +25180,8 @@ async function fetchJiraStatus(store, client, host, artifactId, statusMap) {
|
|
|
25151
25180
|
const artifactType = doc.frontmatter.type;
|
|
25152
25181
|
try {
|
|
25153
25182
|
const issue2 = await client.getIssueWithLinks(jiraKey);
|
|
25154
|
-
const
|
|
25183
|
+
const inSprint = isInActiveSprint(store, doc.frontmatter.tags);
|
|
25184
|
+
const proposedStatus = artifactType === "task" ? mapJiraStatusForTask(issue2.fields.status.name, statusMap?.task, inSprint) : mapJiraStatusForAction(issue2.fields.status.name, statusMap?.action, inSprint);
|
|
25155
25185
|
const currentStatus = doc.frontmatter.status;
|
|
25156
25186
|
const linkedIssues = [];
|
|
25157
25187
|
if (issue2.fields.subtasks) {
|
|
@@ -25503,7 +25533,7 @@ async function fetchJiraDaily(store, client, host, projectKey, dateRange, status
|
|
|
25503
25533
|
const batch = issues.slice(i, i + BATCH_SIZE2);
|
|
25504
25534
|
const results = await Promise.allSettled(
|
|
25505
25535
|
batch.map(
|
|
25506
|
-
(issue2) => processIssue(issue2, client, host, dateRange, jiraKeyToArtifacts, allDocs, statusMap)
|
|
25536
|
+
(issue2) => processIssue(issue2, client, host, dateRange, jiraKeyToArtifacts, allDocs, statusMap, store)
|
|
25507
25537
|
)
|
|
25508
25538
|
);
|
|
25509
25539
|
for (let j = 0; j < results.length; j++) {
|
|
@@ -25520,7 +25550,7 @@ async function fetchJiraDaily(store, client, host, projectKey, dateRange, status
|
|
|
25520
25550
|
summary.proposedActions = generateProposedActions(summary.issues);
|
|
25521
25551
|
return summary;
|
|
25522
25552
|
}
|
|
25523
|
-
async function processIssue(issue2, client, host, dateRange, jiraKeyToArtifacts, allDocs, statusMap) {
|
|
25553
|
+
async function processIssue(issue2, client, host, dateRange, jiraKeyToArtifacts, allDocs, statusMap, store) {
|
|
25524
25554
|
const [changelogResult, commentsResult, remoteLinksResult, issueWithLinks] = await Promise.all([
|
|
25525
25555
|
client.getChangelog(issue2.key).catch(() => []),
|
|
25526
25556
|
client.getComments(issue2.key).catch(() => []),
|
|
@@ -25608,7 +25638,8 @@ async function processIssue(issue2, client, host, dateRange, jiraKeyToArtifacts,
|
|
|
25608
25638
|
if (artifactType === "action" || artifactType === "task") {
|
|
25609
25639
|
const jiraStatus = issue2.fields.status?.name;
|
|
25610
25640
|
if (jiraStatus) {
|
|
25611
|
-
|
|
25641
|
+
const inSprint = store ? isInActiveSprint(store, fm.tags) : false;
|
|
25642
|
+
proposedStatus = artifactType === "task" ? mapJiraStatusForTask(jiraStatus, statusMap?.task, inSprint) : mapJiraStatusForAction(jiraStatus, statusMap?.action, inSprint);
|
|
25612
25643
|
}
|
|
25613
25644
|
}
|
|
25614
25645
|
marvinArtifacts.push({
|
|
@@ -25860,7 +25891,8 @@ async function assessSprintProgress(store, client, host, options = {}) {
|
|
|
25860
25891
|
const commentSignals = [];
|
|
25861
25892
|
if (jiraData) {
|
|
25862
25893
|
jiraStatus = jiraData.issue.fields.status.name;
|
|
25863
|
-
|
|
25894
|
+
const inSprint = isInActiveSprint(store, fm.tags);
|
|
25895
|
+
proposedMarvinStatus = fm.type === "task" ? mapJiraStatusForTask(jiraStatus, options.statusMap?.task, inSprint) : mapJiraStatusForAction(jiraStatus, options.statusMap?.action, inSprint);
|
|
25864
25896
|
const subtasks = jiraData.issue.fields.subtasks ?? [];
|
|
25865
25897
|
if (subtasks.length > 0) {
|
|
25866
25898
|
jiraSubtaskProgress = computeSubtaskProgress(subtasks);
|
|
@@ -26899,12 +26931,20 @@ function createJiraTools(store, projectConfig) {
|
|
|
26899
26931
|
const actionMap = statusMap?.action ?? DEFAULT_ACTION_STATUS_MAP;
|
|
26900
26932
|
const taskMap = statusMap?.task ?? DEFAULT_TASK_STATUS_MAP;
|
|
26901
26933
|
const actionLookup = /* @__PURE__ */ new Map();
|
|
26902
|
-
for (const [marvin,
|
|
26934
|
+
for (const [marvin, value] of Object.entries(actionMap)) {
|
|
26935
|
+
const jiraStatuses = Array.isArray(value) ? value : value.default;
|
|
26903
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
|
+
}
|
|
26904
26940
|
}
|
|
26905
26941
|
const taskLookup = /* @__PURE__ */ new Map();
|
|
26906
|
-
for (const [marvin,
|
|
26942
|
+
for (const [marvin, value] of Object.entries(taskMap)) {
|
|
26943
|
+
const jiraStatuses = Array.isArray(value) ? value : value.default;
|
|
26907
26944
|
for (const js of jiraStatuses) taskLookup.set(js.toLowerCase(), marvin);
|
|
26945
|
+
if (!Array.isArray(value) && value.inSprint) {
|
|
26946
|
+
for (const js of value.inSprint) taskLookup.set(js.toLowerCase(), `${marvin} (inSprint)`);
|
|
26947
|
+
}
|
|
26908
26948
|
}
|
|
26909
26949
|
const parts = [
|
|
26910
26950
|
`Found ${statusCounts.size} distinct statuses in ${resolvedProjectKey} (scanned ${data.issues.length} of ${data.total} issues):`,
|
|
@@ -32475,11 +32515,13 @@ async function jiraStatusesCommand(projectKey) {
|
|
|
32475
32515
|
statusCounts.set(s, (statusCounts.get(s) ?? 0) + 1);
|
|
32476
32516
|
}
|
|
32477
32517
|
const actionLookup = /* @__PURE__ */ new Map();
|
|
32478
|
-
for (const [marvin,
|
|
32518
|
+
for (const [marvin, value] of Object.entries(actionMap)) {
|
|
32519
|
+
const jiraStatuses = Array.isArray(value) ? value : value.default;
|
|
32479
32520
|
for (const js of jiraStatuses) actionLookup.set(js.toLowerCase(), marvin);
|
|
32480
32521
|
}
|
|
32481
32522
|
const taskLookup = /* @__PURE__ */ new Map();
|
|
32482
|
-
for (const [marvin,
|
|
32523
|
+
for (const [marvin, value] of Object.entries(taskMap)) {
|
|
32524
|
+
const jiraStatuses = Array.isArray(value) ? value : value.default;
|
|
32483
32525
|
for (const js of jiraStatuses) taskLookup.set(js.toLowerCase(), marvin);
|
|
32484
32526
|
}
|
|
32485
32527
|
console.log(
|
|
@@ -32662,7 +32704,7 @@ function createProgram() {
|
|
|
32662
32704
|
const program = new Command();
|
|
32663
32705
|
program.name("marvin").description(
|
|
32664
32706
|
"AI-powered product development assistant with Product Owner, Delivery Manager, and Technical Lead personas"
|
|
32665
|
-
).version("0.5.
|
|
32707
|
+
).version("0.5.16");
|
|
32666
32708
|
program.command("init").description("Initialize a new Marvin project in the current directory").action(async () => {
|
|
32667
32709
|
await initCommand();
|
|
32668
32710
|
});
|