mrvn-cli 0.2.8 → 0.2.9
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 +7 -1
- package/dist/index.js +248 -217
- package/dist/index.js.map +1 -1
- package/dist/marvin-serve.js +49 -35
- package/dist/marvin-serve.js.map +1 -1
- package/dist/marvin.js +248 -217
- package/dist/marvin.js.map +1 -1
- package/package.json +1 -1
package/dist/marvin-serve.js
CHANGED
|
@@ -39,6 +39,26 @@ import * as fs from "fs";
|
|
|
39
39
|
import * as path from "path";
|
|
40
40
|
import * as os from "os";
|
|
41
41
|
import * as YAML from "yaml";
|
|
42
|
+
function userConfigDir() {
|
|
43
|
+
return path.join(os.homedir(), ".config", "marvin");
|
|
44
|
+
}
|
|
45
|
+
function userConfigPath() {
|
|
46
|
+
return path.join(userConfigDir(), "config.yaml");
|
|
47
|
+
}
|
|
48
|
+
function loadUserConfig() {
|
|
49
|
+
const configPath = userConfigPath();
|
|
50
|
+
if (!fs.existsSync(configPath)) {
|
|
51
|
+
return {};
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
const raw = fs.readFileSync(configPath, "utf-8");
|
|
55
|
+
return YAML.parse(raw) ?? {};
|
|
56
|
+
} catch (err) {
|
|
57
|
+
throw new ConfigError(
|
|
58
|
+
`Failed to parse user config at ${configPath}: ${err}`
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
42
62
|
function loadProjectConfig(marvinDir) {
|
|
43
63
|
const configPath = path.join(marvinDir, "config.yaml");
|
|
44
64
|
if (!fs.existsSync(configPath)) {
|
|
@@ -266,13 +286,10 @@ var DocumentStore = class {
|
|
|
266
286
|
if (!prefix) {
|
|
267
287
|
throw new Error(`Unknown document type: ${type}`);
|
|
268
288
|
}
|
|
269
|
-
const
|
|
270
|
-
const dir = path3.join(this.docsDir, dirName);
|
|
271
|
-
if (!fs3.existsSync(dir)) return `${prefix}-001`;
|
|
272
|
-
const files = fs3.readdirSync(dir).filter((f) => f.endsWith(".md"));
|
|
289
|
+
const pattern = new RegExp(`^${prefix}-(\\d+)$`);
|
|
273
290
|
let maxNum = 0;
|
|
274
|
-
for (const
|
|
275
|
-
const match =
|
|
291
|
+
for (const id of this.index.keys()) {
|
|
292
|
+
const match = id.match(pattern);
|
|
276
293
|
if (match) {
|
|
277
294
|
maxNum = Math.max(maxNum, parseInt(match[1], 10));
|
|
278
295
|
}
|
|
@@ -17322,12 +17339,12 @@ var JiraClient = class {
|
|
|
17322
17339
|
);
|
|
17323
17340
|
}
|
|
17324
17341
|
};
|
|
17325
|
-
function createJiraClient() {
|
|
17326
|
-
const host = process.env.JIRA_HOST;
|
|
17327
|
-
const email3 = process.env.JIRA_EMAIL;
|
|
17328
|
-
const apiToken = process.env.JIRA_API_TOKEN;
|
|
17342
|
+
function createJiraClient(jiraUserConfig) {
|
|
17343
|
+
const host = jiraUserConfig?.host ?? process.env.JIRA_HOST;
|
|
17344
|
+
const email3 = jiraUserConfig?.email ?? process.env.JIRA_EMAIL;
|
|
17345
|
+
const apiToken = jiraUserConfig?.apiToken ?? process.env.JIRA_API_TOKEN;
|
|
17329
17346
|
if (!host || !email3 || !apiToken) return null;
|
|
17330
|
-
return new JiraClient({ host, email: email3, apiToken });
|
|
17347
|
+
return { client: new JiraClient({ host, email: email3, apiToken }), host };
|
|
17331
17348
|
}
|
|
17332
17349
|
|
|
17333
17350
|
// src/skills/builtin/jira/tools.ts
|
|
@@ -17337,7 +17354,7 @@ function jiraNotConfiguredError() {
|
|
|
17337
17354
|
content: [
|
|
17338
17355
|
{
|
|
17339
17356
|
type: "text",
|
|
17340
|
-
text:
|
|
17357
|
+
text: 'Jira is not configured. Run "marvin config jira" or set JIRA_HOST, JIRA_EMAIL, and JIRA_API_TOKEN environment variables.'
|
|
17341
17358
|
}
|
|
17342
17359
|
],
|
|
17343
17360
|
isError: true
|
|
@@ -17369,6 +17386,7 @@ function findByJiraKey(store, jiraKey) {
|
|
|
17369
17386
|
return docs.find((d) => d.frontmatter.jiraKey === jiraKey);
|
|
17370
17387
|
}
|
|
17371
17388
|
function createJiraTools(store) {
|
|
17389
|
+
const jiraUserConfig = loadUserConfig().jira;
|
|
17372
17390
|
return [
|
|
17373
17391
|
// --- Local read tools ---
|
|
17374
17392
|
tool19(
|
|
@@ -17439,15 +17457,14 @@ function createJiraTools(store) {
|
|
|
17439
17457
|
key: external_exports.string().describe("Jira issue key (e.g. 'PROJ-123')")
|
|
17440
17458
|
},
|
|
17441
17459
|
async (args) => {
|
|
17442
|
-
const
|
|
17443
|
-
if (!
|
|
17444
|
-
const issue2 = await client.getIssue(args.key);
|
|
17445
|
-
const host = process.env.JIRA_HOST;
|
|
17460
|
+
const jira = createJiraClient(jiraUserConfig);
|
|
17461
|
+
if (!jira) return jiraNotConfiguredError();
|
|
17462
|
+
const issue2 = await jira.client.getIssue(args.key);
|
|
17446
17463
|
const existing = findByJiraKey(store, args.key);
|
|
17447
17464
|
if (existing) {
|
|
17448
17465
|
const fm2 = jiraIssueToFrontmatter(
|
|
17449
17466
|
issue2,
|
|
17450
|
-
host,
|
|
17467
|
+
jira.host,
|
|
17451
17468
|
existing.frontmatter.linkedArtifacts
|
|
17452
17469
|
);
|
|
17453
17470
|
const doc2 = store.update(
|
|
@@ -17464,7 +17481,7 @@ function createJiraTools(store) {
|
|
|
17464
17481
|
]
|
|
17465
17482
|
};
|
|
17466
17483
|
}
|
|
17467
|
-
const fm = jiraIssueToFrontmatter(issue2, host);
|
|
17484
|
+
const fm = jiraIssueToFrontmatter(issue2, jira.host);
|
|
17468
17485
|
const doc = store.create(
|
|
17469
17486
|
JIRA_TYPE,
|
|
17470
17487
|
fm,
|
|
@@ -17488,10 +17505,9 @@ function createJiraTools(store) {
|
|
|
17488
17505
|
maxResults: external_exports.number().optional().describe("Max issues to fetch (default 50)")
|
|
17489
17506
|
},
|
|
17490
17507
|
async (args) => {
|
|
17491
|
-
const
|
|
17492
|
-
if (!
|
|
17493
|
-
const result = await client.searchIssues(args.jql, args.maxResults);
|
|
17494
|
-
const host = process.env.JIRA_HOST;
|
|
17508
|
+
const jira = createJiraClient(jiraUserConfig);
|
|
17509
|
+
if (!jira) return jiraNotConfiguredError();
|
|
17510
|
+
const result = await jira.client.searchIssues(args.jql, args.maxResults);
|
|
17495
17511
|
const created = [];
|
|
17496
17512
|
const updated = [];
|
|
17497
17513
|
for (const issue2 of result.issues) {
|
|
@@ -17499,7 +17515,7 @@ function createJiraTools(store) {
|
|
|
17499
17515
|
if (existing) {
|
|
17500
17516
|
const fm = jiraIssueToFrontmatter(
|
|
17501
17517
|
issue2,
|
|
17502
|
-
host,
|
|
17518
|
+
jira.host,
|
|
17503
17519
|
existing.frontmatter.linkedArtifacts
|
|
17504
17520
|
);
|
|
17505
17521
|
store.update(
|
|
@@ -17509,7 +17525,7 @@ function createJiraTools(store) {
|
|
|
17509
17525
|
);
|
|
17510
17526
|
updated.push(`${existing.frontmatter.id} (${issue2.key})`);
|
|
17511
17527
|
} else {
|
|
17512
|
-
const fm = jiraIssueToFrontmatter(issue2, host);
|
|
17528
|
+
const fm = jiraIssueToFrontmatter(issue2, jira.host);
|
|
17513
17529
|
const doc = store.create(
|
|
17514
17530
|
JIRA_TYPE,
|
|
17515
17531
|
fm,
|
|
@@ -17538,8 +17554,8 @@ function createJiraTools(store) {
|
|
|
17538
17554
|
issueType: external_exports.enum(["Story", "Task", "Bug", "Epic"]).optional().describe("Jira issue type (default: 'Task')")
|
|
17539
17555
|
},
|
|
17540
17556
|
async (args) => {
|
|
17541
|
-
const
|
|
17542
|
-
if (!
|
|
17557
|
+
const jira = createJiraClient(jiraUserConfig);
|
|
17558
|
+
if (!jira) return jiraNotConfiguredError();
|
|
17543
17559
|
const artifact = store.get(args.artifactId);
|
|
17544
17560
|
if (!artifact) {
|
|
17545
17561
|
return {
|
|
@@ -17556,20 +17572,19 @@ function createJiraTools(store) {
|
|
|
17556
17572
|
`Marvin artifact: ${artifact.frontmatter.id} (${artifact.frontmatter.type})`,
|
|
17557
17573
|
`Status: ${artifact.frontmatter.status}`
|
|
17558
17574
|
].join("\n");
|
|
17559
|
-
const jiraResult = await client.createIssue({
|
|
17575
|
+
const jiraResult = await jira.client.createIssue({
|
|
17560
17576
|
project: { key: args.projectKey },
|
|
17561
17577
|
summary: artifact.frontmatter.title,
|
|
17562
17578
|
description,
|
|
17563
17579
|
issuetype: { name: args.issueType ?? "Task" }
|
|
17564
17580
|
});
|
|
17565
|
-
const host = process.env.JIRA_HOST;
|
|
17566
17581
|
const jiDoc = store.create(
|
|
17567
17582
|
JIRA_TYPE,
|
|
17568
17583
|
{
|
|
17569
17584
|
title: artifact.frontmatter.title,
|
|
17570
17585
|
status: "open",
|
|
17571
17586
|
jiraKey: jiraResult.key,
|
|
17572
|
-
jiraUrl: `https://${host}/browse/${jiraResult.key}`,
|
|
17587
|
+
jiraUrl: `https://${jira.host}/browse/${jiraResult.key}`,
|
|
17573
17588
|
issueType: args.issueType ?? "Task",
|
|
17574
17589
|
priority: "Medium",
|
|
17575
17590
|
assignee: "",
|
|
@@ -17598,8 +17613,8 @@ function createJiraTools(store) {
|
|
|
17598
17613
|
id: external_exports.string().describe("Local JI-xxx ID")
|
|
17599
17614
|
},
|
|
17600
17615
|
async (args) => {
|
|
17601
|
-
const
|
|
17602
|
-
if (!
|
|
17616
|
+
const jira = createJiraClient(jiraUserConfig);
|
|
17617
|
+
if (!jira) return jiraNotConfiguredError();
|
|
17603
17618
|
const doc = store.get(args.id);
|
|
17604
17619
|
if (!doc || doc.frontmatter.type !== JIRA_TYPE) {
|
|
17605
17620
|
return {
|
|
@@ -17610,15 +17625,14 @@ function createJiraTools(store) {
|
|
|
17610
17625
|
};
|
|
17611
17626
|
}
|
|
17612
17627
|
const jiraKey = doc.frontmatter.jiraKey;
|
|
17613
|
-
await client.updateIssue(jiraKey, {
|
|
17628
|
+
await jira.client.updateIssue(jiraKey, {
|
|
17614
17629
|
summary: doc.frontmatter.title,
|
|
17615
17630
|
description: doc.content || void 0
|
|
17616
17631
|
});
|
|
17617
|
-
const issue2 = await client.getIssue(jiraKey);
|
|
17618
|
-
const host = process.env.JIRA_HOST;
|
|
17632
|
+
const issue2 = await jira.client.getIssue(jiraKey);
|
|
17619
17633
|
const fm = jiraIssueToFrontmatter(
|
|
17620
17634
|
issue2,
|
|
17621
|
-
host,
|
|
17635
|
+
jira.host,
|
|
17622
17636
|
doc.frontmatter.linkedArtifacts
|
|
17623
17637
|
);
|
|
17624
17638
|
store.update(args.id, fm, issue2.fields.description ?? "");
|