mrvn-cli 0.5.9 → 0.5.11
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 +102 -68
- package/dist/index.js.map +1 -1
- package/dist/marvin-serve.js +101 -67
- package/dist/marvin-serve.js.map +1 -1
- package/dist/marvin.js +102 -68
- package/dist/marvin.js.map +1 -1
- package/package.json +1 -1
package/dist/marvin-serve.js
CHANGED
|
@@ -15709,7 +15709,9 @@ function collectSprintSummaryData(store, sprintId) {
|
|
|
15709
15709
|
progress: getEffectiveProgress(doc.frontmatter),
|
|
15710
15710
|
owner: doc.frontmatter.owner,
|
|
15711
15711
|
workFocus: focusTag ? focusTag.slice(6) : void 0,
|
|
15712
|
-
aboutArtifact: about
|
|
15712
|
+
aboutArtifact: about,
|
|
15713
|
+
jiraKey: doc.frontmatter.jiraKey,
|
|
15714
|
+
jiraUrl: doc.frontmatter.jiraUrl
|
|
15713
15715
|
};
|
|
15714
15716
|
allItemsById.set(item.id, item);
|
|
15715
15717
|
if (about && sprintItemIds.has(about)) {
|
|
@@ -19833,6 +19835,58 @@ function createJiraTools(store, projectConfig) {
|
|
|
19833
19835
|
},
|
|
19834
19836
|
{ annotations: { readOnlyHint: true } }
|
|
19835
19837
|
),
|
|
19838
|
+
// --- Jira search (read-only) ---
|
|
19839
|
+
tool20(
|
|
19840
|
+
"search_jira",
|
|
19841
|
+
"Search Jira issues via JQL query. Read-only \u2014 returns results without creating any local documents. Use this to preview before importing or to find issues for linking.",
|
|
19842
|
+
{
|
|
19843
|
+
jql: external_exports.string().describe(`JQL query (e.g. 'project = PROJ AND status = "In Progress"')`),
|
|
19844
|
+
maxResults: external_exports.number().optional().describe("Max issues to return (default 20)")
|
|
19845
|
+
},
|
|
19846
|
+
async (args) => {
|
|
19847
|
+
const jira = createJiraClient(jiraUserConfig);
|
|
19848
|
+
if (!jira) return jiraNotConfiguredError();
|
|
19849
|
+
const result = await jira.client.searchIssuesV3(
|
|
19850
|
+
args.jql,
|
|
19851
|
+
["summary", "status", "issuetype", "priority", "assignee", "labels"],
|
|
19852
|
+
args.maxResults ?? 20
|
|
19853
|
+
);
|
|
19854
|
+
const allDocs = store.registeredTypes.flatMap((t) => store.list({ type: t }));
|
|
19855
|
+
const jiraKeyToArtifact = /* @__PURE__ */ new Map();
|
|
19856
|
+
for (const doc of allDocs) {
|
|
19857
|
+
const jk = doc.frontmatter.jiraKey;
|
|
19858
|
+
if (jk) jiraKeyToArtifact.set(jk, doc.frontmatter.id);
|
|
19859
|
+
}
|
|
19860
|
+
const issues = result.issues.map((issue2) => {
|
|
19861
|
+
const marvinId = jiraKeyToArtifact.get(issue2.key);
|
|
19862
|
+
return {
|
|
19863
|
+
key: issue2.key,
|
|
19864
|
+
summary: issue2.fields.summary,
|
|
19865
|
+
status: issue2.fields.status.name,
|
|
19866
|
+
issueType: issue2.fields.issuetype.name,
|
|
19867
|
+
priority: issue2.fields.priority?.name ?? "None",
|
|
19868
|
+
assignee: issue2.fields.assignee?.displayName ?? "unassigned",
|
|
19869
|
+
labels: issue2.fields.labels ?? [],
|
|
19870
|
+
marvinArtifact: marvinId ?? null
|
|
19871
|
+
};
|
|
19872
|
+
});
|
|
19873
|
+
const parts = [
|
|
19874
|
+
`Found ${result.total ?? issues.length} issues (showing ${issues.length}).`,
|
|
19875
|
+
""
|
|
19876
|
+
];
|
|
19877
|
+
for (const issue2 of issues) {
|
|
19878
|
+
const linked = issue2.marvinArtifact ? ` \u2192 ${issue2.marvinArtifact}` : " (not linked)";
|
|
19879
|
+
parts.push(`${issue2.key} \u2014 ${issue2.summary} [${issue2.status}]${linked}`);
|
|
19880
|
+
parts.push(` Type: ${issue2.issueType} | Priority: ${issue2.priority} | Assignee: ${issue2.assignee}`);
|
|
19881
|
+
}
|
|
19882
|
+
parts.push("");
|
|
19883
|
+
parts.push("This is read-only. Use link_to_jira to link issues to Marvin artifacts, or pull_jira_issue to import as JI-xxx documents.");
|
|
19884
|
+
return {
|
|
19885
|
+
content: [{ type: "text", text: parts.join("\n") }]
|
|
19886
|
+
};
|
|
19887
|
+
},
|
|
19888
|
+
{ annotations: { readOnlyHint: true } }
|
|
19889
|
+
),
|
|
19836
19890
|
// --- Jira → Local tools ---
|
|
19837
19891
|
tool20(
|
|
19838
19892
|
"pull_jira_issue",
|
|
@@ -19931,7 +19985,7 @@ function createJiraTools(store, projectConfig) {
|
|
|
19931
19985
|
// --- Local → Jira tools ---
|
|
19932
19986
|
tool20(
|
|
19933
19987
|
"push_artifact_to_jira",
|
|
19934
|
-
"Create a Jira issue from
|
|
19988
|
+
"Create a Jira issue from any Marvin artifact and link it directly via jiraKey on the artifact.",
|
|
19935
19989
|
{
|
|
19936
19990
|
artifactId: external_exports.string().describe("Marvin artifact ID (e.g. 'D-001', 'A-003', 'T-002')"),
|
|
19937
19991
|
projectKey: external_exports.string().optional().describe("Jira project key (e.g. 'PROJ'). Falls back to jira.projectKey from .marvin/config.yaml if not provided."),
|
|
@@ -19974,46 +20028,18 @@ function createJiraTools(store, projectConfig) {
|
|
|
19974
20028
|
description,
|
|
19975
20029
|
issuetype: { name: args.issueType ?? "Task" }
|
|
19976
20030
|
});
|
|
19977
|
-
const
|
|
19978
|
-
|
|
19979
|
-
|
|
19980
|
-
|
|
19981
|
-
|
|
19982
|
-
|
|
19983
|
-
|
|
19984
|
-
tags: [...existingTags.filter((t) => !t.startsWith("jira:")), `jira:${jiraResult.key}`]
|
|
19985
|
-
});
|
|
19986
|
-
return {
|
|
19987
|
-
content: [
|
|
19988
|
-
{
|
|
19989
|
-
type: "text",
|
|
19990
|
-
text: `Created Jira ${jiraResult.key} from ${args.artifactId}. Linked directly on the artifact.`
|
|
19991
|
-
}
|
|
19992
|
-
]
|
|
19993
|
-
};
|
|
19994
|
-
}
|
|
19995
|
-
const jiDoc = store.create(
|
|
19996
|
-
JIRA_TYPE,
|
|
19997
|
-
{
|
|
19998
|
-
title: artifact.frontmatter.title,
|
|
19999
|
-
status: "open",
|
|
20000
|
-
jiraKey: jiraResult.key,
|
|
20001
|
-
jiraUrl: `https://${jira.host}/browse/${jiraResult.key}`,
|
|
20002
|
-
issueType: args.issueType ?? "Task",
|
|
20003
|
-
priority: "Medium",
|
|
20004
|
-
assignee: "",
|
|
20005
|
-
labels: [],
|
|
20006
|
-
linkedArtifacts: [args.artifactId],
|
|
20007
|
-
tags: [`jira:${jiraResult.key}`],
|
|
20008
|
-
lastSyncedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
20009
|
-
},
|
|
20010
|
-
""
|
|
20011
|
-
);
|
|
20031
|
+
const existingTags = artifact.frontmatter.tags ?? [];
|
|
20032
|
+
store.update(args.artifactId, {
|
|
20033
|
+
jiraKey: jiraResult.key,
|
|
20034
|
+
jiraUrl: `https://${jira.host}/browse/${jiraResult.key}`,
|
|
20035
|
+
lastJiraSyncAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
20036
|
+
tags: [...existingTags.filter((t) => !t.startsWith("jira:")), `jira:${jiraResult.key}`]
|
|
20037
|
+
});
|
|
20012
20038
|
return {
|
|
20013
20039
|
content: [
|
|
20014
20040
|
{
|
|
20015
20041
|
type: "text",
|
|
20016
|
-
text: `Created Jira ${jiraResult.key} from ${args.artifactId}.
|
|
20042
|
+
text: `Created Jira ${jiraResult.key} from ${args.artifactId}. Linked directly on the artifact.`
|
|
20017
20043
|
}
|
|
20018
20044
|
]
|
|
20019
20045
|
};
|
|
@@ -20117,9 +20143,9 @@ function createJiraTools(store, projectConfig) {
|
|
|
20117
20143
|
// --- Direct Jira linking for actions/tasks ---
|
|
20118
20144
|
tool20(
|
|
20119
20145
|
"link_to_jira",
|
|
20120
|
-
"Link an existing Jira issue to
|
|
20146
|
+
"Link an existing Jira issue to any Marvin artifact (sets jiraKey directly on the artifact)",
|
|
20121
20147
|
{
|
|
20122
|
-
artifactId: external_exports.string().describe("Marvin artifact ID (e.g. 'A-001', '
|
|
20148
|
+
artifactId: external_exports.string().describe("Marvin artifact ID (e.g. 'A-001', 'D-003', 'T-002', 'Q-005')"),
|
|
20123
20149
|
jiraKey: external_exports.string().describe("Jira issue key (e.g. 'PROJ-123')")
|
|
20124
20150
|
},
|
|
20125
20151
|
async (args) => {
|
|
@@ -20134,17 +20160,6 @@ function createJiraTools(store, projectConfig) {
|
|
|
20134
20160
|
isError: true
|
|
20135
20161
|
};
|
|
20136
20162
|
}
|
|
20137
|
-
if (artifact.frontmatter.type !== "action" && artifact.frontmatter.type !== "task") {
|
|
20138
|
-
return {
|
|
20139
|
-
content: [
|
|
20140
|
-
{
|
|
20141
|
-
type: "text",
|
|
20142
|
-
text: `link_to_jira only supports action and task artifacts. ${args.artifactId} is type "${artifact.frontmatter.type}". Use link_artifact_to_jira for JI-xxx documents instead.`
|
|
20143
|
-
}
|
|
20144
|
-
],
|
|
20145
|
-
isError: true
|
|
20146
|
-
};
|
|
20147
|
-
}
|
|
20148
20163
|
const issue2 = await jira.client.getIssue(args.jiraKey);
|
|
20149
20164
|
const existingTags = artifact.frontmatter.tags ?? [];
|
|
20150
20165
|
store.update(args.artifactId, {
|
|
@@ -20471,15 +20486,16 @@ function formatIssueEntry(issue2) {
|
|
|
20471
20486
|
|
|
20472
20487
|
// src/skills/builtin/jira/index.ts
|
|
20473
20488
|
var COMMON_TOOLS = `**Available tools:**
|
|
20474
|
-
- \`
|
|
20475
|
-
- \`
|
|
20476
|
-
- \`push_artifact_to_jira\` \u2014 create a Jira issue from a Marvin artifact. For **actions and tasks**, links directly via \`jiraKey\` on the artifact (no JI-xxx intermediary). For other types, creates a JI-xxx tracking document.
|
|
20477
|
-
- \`link_to_jira\` \u2014 link an existing Jira issue to a Marvin action or task (sets \`jiraKey\` directly on the artifact)
|
|
20489
|
+
- \`push_artifact_to_jira\` \u2014 create a Jira issue from any Marvin artifact and link it directly via \`jiraKey\` on the artifact.
|
|
20490
|
+
- \`link_to_jira\` \u2014 link an existing Jira issue to any Marvin artifact (sets \`jiraKey\` directly on the artifact).
|
|
20478
20491
|
- \`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.
|
|
20479
20492
|
- \`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).
|
|
20480
20493
|
- \`fetch_jira_statuses\` \u2014 **read-only**: discover all Jira statuses in a project and show their Marvin mappings (mapped vs unmapped).
|
|
20481
|
-
- \`
|
|
20482
|
-
- \`
|
|
20494
|
+
- \`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.
|
|
20495
|
+
- \`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).
|
|
20496
|
+
- \`list_jira_issues\` / \`get_jira_issue\` \u2014 browse locally imported JI-xxx documents.
|
|
20497
|
+
- \`sync_jira_issue\` \u2014 bidirectional sync of a local JI-xxx with Jira.
|
|
20498
|
+
- \`link_artifact_to_jira\` \u2014 link a Marvin artifact to an existing JI-xxx document.`;
|
|
20483
20499
|
var COMMON_WORKFLOW = `**Jira sync workflow:**
|
|
20484
20500
|
1. Call \`fetch_jira_status\` to see what Jira reports for linked artifacts
|
|
20485
20501
|
2. Analyze the proposed changes (status transitions, subtask progress, blockers from linked issues)
|
|
@@ -21172,6 +21188,12 @@ function formatDate(iso) {
|
|
|
21172
21188
|
function typeLabel(type) {
|
|
21173
21189
|
return type.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
21174
21190
|
}
|
|
21191
|
+
function jiraIcon(jiraKey, jiraUrl) {
|
|
21192
|
+
if (!jiraKey) return "";
|
|
21193
|
+
const href = jiraUrl ?? "#";
|
|
21194
|
+
const title = escapeHtml(jiraKey);
|
|
21195
|
+
return `<a href="${escapeHtml(href)}" target="_blank" rel="noopener" title="Jira: ${title}" class="jira-link"><svg class="jira-icon" viewBox="0 0 24 24" width="14" height="14" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M12.005 2L6.5 7.505l5.505 5.505L17.51 7.505 12.005 2z" fill="#2684FF"/><path d="M6.5 7.505L1 13.01l5.505 5.505L12.01 13.01 6.5 7.505z" fill="url(#jira-g1)"/><path d="M17.51 7.505L12.005 13.01l5.505 5.505L23.015 13.01 17.51 7.505z" fill="url(#jira-g2)"/><path d="M12.005 13.01L6.5 18.515l5.505 5.505 5.505-5.505-5.505-5.505z" fill="#2684FF"/><defs><linearGradient id="jira-g1" x1="9.25" y1="7.51" x2="3.85" y2="12.91" gradientUnits="userSpaceOnUse"><stop stop-color="#0052CC"/><stop offset="1" stop-color="#2684FF"/></linearGradient><linearGradient id="jira-g2" x1="14.76" y1="7.51" x2="20.16" y2="12.91" gradientUnits="userSpaceOnUse"><stop stop-color="#0052CC"/><stop offset="1" stop-color="#2684FF"/></linearGradient></defs></svg></a>`;
|
|
21196
|
+
}
|
|
21175
21197
|
function renderMarkdown(md) {
|
|
21176
21198
|
const lines = md.split("\n");
|
|
21177
21199
|
const out = [];
|
|
@@ -23037,6 +23059,18 @@ tr:hover td {
|
|
|
23037
23059
|
.owner-badge-dm { background: rgba(52, 211, 153, 0.18); color: #34d399; }
|
|
23038
23060
|
.owner-badge-other { background: rgba(139, 143, 164, 0.12); color: var(--text-dim); }
|
|
23039
23061
|
|
|
23062
|
+
/* Jira link icon */
|
|
23063
|
+
.jira-link {
|
|
23064
|
+
display: inline-flex;
|
|
23065
|
+
align-items: center;
|
|
23066
|
+
vertical-align: middle;
|
|
23067
|
+
margin-left: 0.35rem;
|
|
23068
|
+
opacity: 0.7;
|
|
23069
|
+
transition: opacity 0.15s;
|
|
23070
|
+
}
|
|
23071
|
+
.jira-link:hover { opacity: 1; }
|
|
23072
|
+
.jira-icon { vertical-align: middle; }
|
|
23073
|
+
|
|
23040
23074
|
/* Group header rows (PO dashboard decisions/deps) */
|
|
23041
23075
|
.group-header-row td {
|
|
23042
23076
|
background: var(--bg-hover);
|
|
@@ -23061,7 +23095,7 @@ function documentsPage(data) {
|
|
|
23061
23095
|
(doc) => `
|
|
23062
23096
|
<tr>
|
|
23063
23097
|
<td><a href="/docs/${data.type}/${doc.frontmatter.id}">${escapeHtml(doc.frontmatter.id)}</a></td>
|
|
23064
|
-
<td><a href="/docs/${data.type}/${doc.frontmatter.id}">${escapeHtml(doc.frontmatter.title)}</a
|
|
23098
|
+
<td><a href="/docs/${data.type}/${doc.frontmatter.id}">${escapeHtml(doc.frontmatter.title)}</a>${jiraIcon(doc.frontmatter.jiraKey, doc.frontmatter.jiraUrl)}</td>
|
|
23065
23099
|
<td>${statusBadge(doc.frontmatter.status)}</td>
|
|
23066
23100
|
<td>${escapeHtml(doc.frontmatter.owner ?? "\u2014")}</td>
|
|
23067
23101
|
<td>${doc.frontmatter.priority ? `<span class="priority-${doc.frontmatter.priority.toLowerCase()}">${escapeHtml(doc.frontmatter.priority)}</span>` : "\u2014"}</td>
|
|
@@ -23150,7 +23184,7 @@ function documentDetailPage(doc) {
|
|
|
23150
23184
|
</div>
|
|
23151
23185
|
|
|
23152
23186
|
<div class="page-header">
|
|
23153
|
-
<h2>${escapeHtml(fm.title)}</h2>
|
|
23187
|
+
<h2>${escapeHtml(fm.title)}${jiraIcon(fm.jiraKey, fm.jiraUrl)}</h2>
|
|
23154
23188
|
<div class="subtitle">${escapeHtml(fm.id)} · ${escapeHtml(label)}</div>
|
|
23155
23189
|
</div>
|
|
23156
23190
|
|
|
@@ -24581,7 +24615,7 @@ function renderItemRows(items, borderColor, showOwner, depth = 0) {
|
|
|
24581
24615
|
const row = `
|
|
24582
24616
|
<tr class="${classes.join(" ")}" style="--focus-color: ${borderColor}">
|
|
24583
24617
|
<td${indent}><a href="/docs/${escapeHtml(w.type)}/${escapeHtml(w.id)}">${escapeHtml(w.id)}</a></td>
|
|
24584
|
-
<td>${escapeHtml(w.title)}</td>
|
|
24618
|
+
<td>${escapeHtml(w.title)}${jiraIcon(w.jiraKey, w.jiraUrl)}</td>
|
|
24585
24619
|
${ownerCell}
|
|
24586
24620
|
<td>${statusBadge(w.status)}</td>
|
|
24587
24621
|
<td>${progressCell}</td>
|
|
@@ -26206,7 +26240,7 @@ function boardPage(data, basePath = "/board") {
|
|
|
26206
26240
|
<div class="board-card">
|
|
26207
26241
|
<a href="/docs/${doc.frontmatter.type}/${doc.frontmatter.id}">
|
|
26208
26242
|
<div class="bc-id">${escapeHtml(doc.frontmatter.id)}</div>
|
|
26209
|
-
<div class="bc-title">${escapeHtml(doc.frontmatter.title)}</div>
|
|
26243
|
+
<div class="bc-title">${escapeHtml(doc.frontmatter.title)}${jiraIcon(doc.frontmatter.jiraKey, doc.frontmatter.jiraUrl)}</div>
|
|
26210
26244
|
${doc.frontmatter.owner ? `<div class="bc-owner">${escapeHtml(doc.frontmatter.owner)}</div>` : ""}
|
|
26211
26245
|
</a>
|
|
26212
26246
|
</div>`
|
|
@@ -28112,22 +28146,22 @@ ${block.text}` };
|
|
|
28112
28146
|
function collectTools(marvinDir) {
|
|
28113
28147
|
const config2 = loadProjectConfig(marvinDir);
|
|
28114
28148
|
const plugin = resolvePlugin(config2.methodology);
|
|
28115
|
-
const
|
|
28116
|
-
const
|
|
28149
|
+
const pluginRegistrations = plugin?.documentTypeRegistrations ?? [];
|
|
28150
|
+
const allSkills = loadAllSkills(marvinDir);
|
|
28151
|
+
const allSkillIds = [...allSkills.keys()];
|
|
28152
|
+
const allSkillRegs = collectSkillRegistrations(allSkillIds, allSkills);
|
|
28153
|
+
const store = new DocumentStore(marvinDir, [...pluginRegistrations, ...allSkillRegs]);
|
|
28117
28154
|
const sourcesDir = path9.join(marvinDir, "sources");
|
|
28118
28155
|
const hasSourcesDir = fs9.existsSync(sourcesDir);
|
|
28119
28156
|
const manifest = hasSourcesDir ? new SourceManifestManager(marvinDir) : void 0;
|
|
28120
28157
|
const pluginTools = plugin ? getPluginTools(plugin, store, marvinDir) : [];
|
|
28121
28158
|
const sessionStore = new SessionStore(marvinDir);
|
|
28122
|
-
const allSkills = loadAllSkills(marvinDir);
|
|
28123
|
-
const allSkillIds = [...allSkills.keys()];
|
|
28124
28159
|
const codeSkillTools = getSkillTools(allSkillIds, allSkills, store, config2);
|
|
28125
28160
|
const skillsWithActions = allSkillIds.map((id) => allSkills.get(id)).filter((s) => s.actions && s.actions.length > 0);
|
|
28126
28161
|
const projectRoot = path9.dirname(marvinDir);
|
|
28127
28162
|
const actionTools = createSkillActionTools(skillsWithActions, { store, marvinDir, projectRoot });
|
|
28128
|
-
const allSkillRegs = collectSkillRegistrations(allSkillIds, allSkills);
|
|
28129
28163
|
const navGroups = buildNavGroups({
|
|
28130
|
-
pluginRegs:
|
|
28164
|
+
pluginRegs: pluginRegistrations,
|
|
28131
28165
|
skillRegs: allSkillRegs,
|
|
28132
28166
|
pluginName: plugin?.name
|
|
28133
28167
|
});
|