mrvn-cli 0.2.8 → 0.2.10
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 +256 -212
- package/dist/index.js.map +1 -1
- package/dist/marvin-serve.js +57 -30
- package/dist/marvin-serve.js.map +1 -1
- package/dist/marvin.js +256 -212
- 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)) {
|
|
@@ -146,6 +166,11 @@ var DocumentStore = class {
|
|
|
146
166
|
const raw = fs3.readFileSync(filePath, "utf-8");
|
|
147
167
|
const doc = parseDocument(raw, filePath);
|
|
148
168
|
if (doc.frontmatter.id) {
|
|
169
|
+
if (this.index.has(doc.frontmatter.id)) {
|
|
170
|
+
console.warn(
|
|
171
|
+
`[marvin] Duplicate ID "${doc.frontmatter.id}" in ${file2} \u2014 conflicts with existing entry. Run ID repair to fix.`
|
|
172
|
+
);
|
|
173
|
+
}
|
|
149
174
|
this.index.set(doc.frontmatter.id, doc.frontmatter);
|
|
150
175
|
}
|
|
151
176
|
}
|
|
@@ -269,14 +294,19 @@ var DocumentStore = class {
|
|
|
269
294
|
const dirName = this.typeDirs[type];
|
|
270
295
|
const dir = path3.join(this.docsDir, dirName);
|
|
271
296
|
if (!fs3.existsSync(dir)) return `${prefix}-001`;
|
|
297
|
+
const idPattern = new RegExp(`^${prefix}-(\\d+)$`);
|
|
272
298
|
const files = fs3.readdirSync(dir).filter((f) => f.endsWith(".md"));
|
|
273
299
|
let maxNum = 0;
|
|
274
300
|
for (const file2 of files) {
|
|
275
|
-
const
|
|
301
|
+
const filePath = path3.join(dir, file2);
|
|
302
|
+
const raw = fs3.readFileSync(filePath, "utf-8");
|
|
303
|
+
const doc = parseDocument(raw, filePath);
|
|
304
|
+
const match = doc.frontmatter.id?.match(idPattern);
|
|
276
305
|
if (match) {
|
|
277
306
|
maxNum = Math.max(maxNum, parseInt(match[1], 10));
|
|
278
307
|
}
|
|
279
308
|
}
|
|
309
|
+
maxNum = Math.max(maxNum, files.length);
|
|
280
310
|
return `${prefix}-${String(maxNum + 1).padStart(3, "0")}`;
|
|
281
311
|
}
|
|
282
312
|
counts() {
|
|
@@ -17322,12 +17352,12 @@ var JiraClient = class {
|
|
|
17322
17352
|
);
|
|
17323
17353
|
}
|
|
17324
17354
|
};
|
|
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;
|
|
17355
|
+
function createJiraClient(jiraUserConfig) {
|
|
17356
|
+
const host = jiraUserConfig?.host ?? process.env.JIRA_HOST;
|
|
17357
|
+
const email3 = jiraUserConfig?.email ?? process.env.JIRA_EMAIL;
|
|
17358
|
+
const apiToken = jiraUserConfig?.apiToken ?? process.env.JIRA_API_TOKEN;
|
|
17329
17359
|
if (!host || !email3 || !apiToken) return null;
|
|
17330
|
-
return new JiraClient({ host, email: email3, apiToken });
|
|
17360
|
+
return { client: new JiraClient({ host, email: email3, apiToken }), host };
|
|
17331
17361
|
}
|
|
17332
17362
|
|
|
17333
17363
|
// src/skills/builtin/jira/tools.ts
|
|
@@ -17337,7 +17367,7 @@ function jiraNotConfiguredError() {
|
|
|
17337
17367
|
content: [
|
|
17338
17368
|
{
|
|
17339
17369
|
type: "text",
|
|
17340
|
-
text:
|
|
17370
|
+
text: 'Jira is not configured. Run "marvin config jira" or set JIRA_HOST, JIRA_EMAIL, and JIRA_API_TOKEN environment variables.'
|
|
17341
17371
|
}
|
|
17342
17372
|
],
|
|
17343
17373
|
isError: true
|
|
@@ -17369,6 +17399,7 @@ function findByJiraKey(store, jiraKey) {
|
|
|
17369
17399
|
return docs.find((d) => d.frontmatter.jiraKey === jiraKey);
|
|
17370
17400
|
}
|
|
17371
17401
|
function createJiraTools(store) {
|
|
17402
|
+
const jiraUserConfig = loadUserConfig().jira;
|
|
17372
17403
|
return [
|
|
17373
17404
|
// --- Local read tools ---
|
|
17374
17405
|
tool19(
|
|
@@ -17439,15 +17470,14 @@ function createJiraTools(store) {
|
|
|
17439
17470
|
key: external_exports.string().describe("Jira issue key (e.g. 'PROJ-123')")
|
|
17440
17471
|
},
|
|
17441
17472
|
async (args) => {
|
|
17442
|
-
const
|
|
17443
|
-
if (!
|
|
17444
|
-
const issue2 = await client.getIssue(args.key);
|
|
17445
|
-
const host = process.env.JIRA_HOST;
|
|
17473
|
+
const jira = createJiraClient(jiraUserConfig);
|
|
17474
|
+
if (!jira) return jiraNotConfiguredError();
|
|
17475
|
+
const issue2 = await jira.client.getIssue(args.key);
|
|
17446
17476
|
const existing = findByJiraKey(store, args.key);
|
|
17447
17477
|
if (existing) {
|
|
17448
17478
|
const fm2 = jiraIssueToFrontmatter(
|
|
17449
17479
|
issue2,
|
|
17450
|
-
host,
|
|
17480
|
+
jira.host,
|
|
17451
17481
|
existing.frontmatter.linkedArtifacts
|
|
17452
17482
|
);
|
|
17453
17483
|
const doc2 = store.update(
|
|
@@ -17464,7 +17494,7 @@ function createJiraTools(store) {
|
|
|
17464
17494
|
]
|
|
17465
17495
|
};
|
|
17466
17496
|
}
|
|
17467
|
-
const fm = jiraIssueToFrontmatter(issue2, host);
|
|
17497
|
+
const fm = jiraIssueToFrontmatter(issue2, jira.host);
|
|
17468
17498
|
const doc = store.create(
|
|
17469
17499
|
JIRA_TYPE,
|
|
17470
17500
|
fm,
|
|
@@ -17488,10 +17518,9 @@ function createJiraTools(store) {
|
|
|
17488
17518
|
maxResults: external_exports.number().optional().describe("Max issues to fetch (default 50)")
|
|
17489
17519
|
},
|
|
17490
17520
|
async (args) => {
|
|
17491
|
-
const
|
|
17492
|
-
if (!
|
|
17493
|
-
const result = await client.searchIssues(args.jql, args.maxResults);
|
|
17494
|
-
const host = process.env.JIRA_HOST;
|
|
17521
|
+
const jira = createJiraClient(jiraUserConfig);
|
|
17522
|
+
if (!jira) return jiraNotConfiguredError();
|
|
17523
|
+
const result = await jira.client.searchIssues(args.jql, args.maxResults);
|
|
17495
17524
|
const created = [];
|
|
17496
17525
|
const updated = [];
|
|
17497
17526
|
for (const issue2 of result.issues) {
|
|
@@ -17499,7 +17528,7 @@ function createJiraTools(store) {
|
|
|
17499
17528
|
if (existing) {
|
|
17500
17529
|
const fm = jiraIssueToFrontmatter(
|
|
17501
17530
|
issue2,
|
|
17502
|
-
host,
|
|
17531
|
+
jira.host,
|
|
17503
17532
|
existing.frontmatter.linkedArtifacts
|
|
17504
17533
|
);
|
|
17505
17534
|
store.update(
|
|
@@ -17509,7 +17538,7 @@ function createJiraTools(store) {
|
|
|
17509
17538
|
);
|
|
17510
17539
|
updated.push(`${existing.frontmatter.id} (${issue2.key})`);
|
|
17511
17540
|
} else {
|
|
17512
|
-
const fm = jiraIssueToFrontmatter(issue2, host);
|
|
17541
|
+
const fm = jiraIssueToFrontmatter(issue2, jira.host);
|
|
17513
17542
|
const doc = store.create(
|
|
17514
17543
|
JIRA_TYPE,
|
|
17515
17544
|
fm,
|
|
@@ -17538,8 +17567,8 @@ function createJiraTools(store) {
|
|
|
17538
17567
|
issueType: external_exports.enum(["Story", "Task", "Bug", "Epic"]).optional().describe("Jira issue type (default: 'Task')")
|
|
17539
17568
|
},
|
|
17540
17569
|
async (args) => {
|
|
17541
|
-
const
|
|
17542
|
-
if (!
|
|
17570
|
+
const jira = createJiraClient(jiraUserConfig);
|
|
17571
|
+
if (!jira) return jiraNotConfiguredError();
|
|
17543
17572
|
const artifact = store.get(args.artifactId);
|
|
17544
17573
|
if (!artifact) {
|
|
17545
17574
|
return {
|
|
@@ -17556,20 +17585,19 @@ function createJiraTools(store) {
|
|
|
17556
17585
|
`Marvin artifact: ${artifact.frontmatter.id} (${artifact.frontmatter.type})`,
|
|
17557
17586
|
`Status: ${artifact.frontmatter.status}`
|
|
17558
17587
|
].join("\n");
|
|
17559
|
-
const jiraResult = await client.createIssue({
|
|
17588
|
+
const jiraResult = await jira.client.createIssue({
|
|
17560
17589
|
project: { key: args.projectKey },
|
|
17561
17590
|
summary: artifact.frontmatter.title,
|
|
17562
17591
|
description,
|
|
17563
17592
|
issuetype: { name: args.issueType ?? "Task" }
|
|
17564
17593
|
});
|
|
17565
|
-
const host = process.env.JIRA_HOST;
|
|
17566
17594
|
const jiDoc = store.create(
|
|
17567
17595
|
JIRA_TYPE,
|
|
17568
17596
|
{
|
|
17569
17597
|
title: artifact.frontmatter.title,
|
|
17570
17598
|
status: "open",
|
|
17571
17599
|
jiraKey: jiraResult.key,
|
|
17572
|
-
jiraUrl: `https://${host}/browse/${jiraResult.key}`,
|
|
17600
|
+
jiraUrl: `https://${jira.host}/browse/${jiraResult.key}`,
|
|
17573
17601
|
issueType: args.issueType ?? "Task",
|
|
17574
17602
|
priority: "Medium",
|
|
17575
17603
|
assignee: "",
|
|
@@ -17598,8 +17626,8 @@ function createJiraTools(store) {
|
|
|
17598
17626
|
id: external_exports.string().describe("Local JI-xxx ID")
|
|
17599
17627
|
},
|
|
17600
17628
|
async (args) => {
|
|
17601
|
-
const
|
|
17602
|
-
if (!
|
|
17629
|
+
const jira = createJiraClient(jiraUserConfig);
|
|
17630
|
+
if (!jira) return jiraNotConfiguredError();
|
|
17603
17631
|
const doc = store.get(args.id);
|
|
17604
17632
|
if (!doc || doc.frontmatter.type !== JIRA_TYPE) {
|
|
17605
17633
|
return {
|
|
@@ -17610,15 +17638,14 @@ function createJiraTools(store) {
|
|
|
17610
17638
|
};
|
|
17611
17639
|
}
|
|
17612
17640
|
const jiraKey = doc.frontmatter.jiraKey;
|
|
17613
|
-
await client.updateIssue(jiraKey, {
|
|
17641
|
+
await jira.client.updateIssue(jiraKey, {
|
|
17614
17642
|
summary: doc.frontmatter.title,
|
|
17615
17643
|
description: doc.content || void 0
|
|
17616
17644
|
});
|
|
17617
|
-
const issue2 = await client.getIssue(jiraKey);
|
|
17618
|
-
const host = process.env.JIRA_HOST;
|
|
17645
|
+
const issue2 = await jira.client.getIssue(jiraKey);
|
|
17619
17646
|
const fm = jiraIssueToFrontmatter(
|
|
17620
17647
|
issue2,
|
|
17621
|
-
host,
|
|
17648
|
+
jira.host,
|
|
17622
17649
|
doc.frontmatter.linkedArtifacts
|
|
17623
17650
|
);
|
|
17624
17651
|
store.update(args.id, fm, issue2.fields.description ?? "");
|