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.
@@ -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 dirName = this.typeDirs[type];
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 file2 of files) {
275
- const match = file2.match(new RegExp(`^${prefix}-(\\d+)\\.md$`));
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: "Jira is not configured. Set JIRA_HOST, JIRA_EMAIL, and JIRA_API_TOKEN environment variables."
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 client = createJiraClient();
17443
- if (!client) return jiraNotConfiguredError();
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 client = createJiraClient();
17492
- if (!client) return jiraNotConfiguredError();
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 client = createJiraClient();
17542
- if (!client) return jiraNotConfiguredError();
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 client = createJiraClient();
17602
- if (!client) return jiraNotConfiguredError();
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 ?? "");