@arii/boomtick-mcp 0.7.0 → 0.7.1

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.
@@ -4,20 +4,70 @@
4
4
  "parameters": {
5
5
  "properties": {
6
6
  "prNumber": {
7
+ "anyOf": [
8
+ {
9
+ "type": "integer"
10
+ },
11
+ {
12
+ "type": "null"
13
+ }
14
+ ],
15
+ "default": null,
7
16
  "description": "The number of the pull request to check for conflicts.",
8
- "title": "Prnumber",
9
- "type": "integer"
17
+ "title": "Prnumber"
10
18
  },
11
19
  "baseBranch": {
12
20
  "default": "main",
13
21
  "description": "The base branch to check against (default: 'main').",
14
22
  "title": "Basebranch",
15
23
  "type": "string"
24
+ },
25
+ "headRef": {
26
+ "default": "HEAD",
27
+ "description": "The head ref or branch to check.",
28
+ "title": "Headref",
29
+ "type": "string"
30
+ },
31
+ "repo": {
32
+ "anyOf": [
33
+ {
34
+ "type": "string"
35
+ },
36
+ {
37
+ "type": "null"
38
+ }
39
+ ],
40
+ "default": null,
41
+ "description": "Target GitHub repository override.",
42
+ "title": "Repo"
43
+ },
44
+ "path": {
45
+ "anyOf": [
46
+ {
47
+ "type": "string"
48
+ },
49
+ {
50
+ "type": "null"
51
+ }
52
+ ],
53
+ "default": null,
54
+ "description": "Local filesystem path to target repository root.",
55
+ "title": "Path"
56
+ },
57
+ "repoPath": {
58
+ "anyOf": [
59
+ {
60
+ "type": "string"
61
+ },
62
+ {
63
+ "type": "null"
64
+ }
65
+ ],
66
+ "default": null,
67
+ "description": "Alias for path to target repository root.",
68
+ "title": "Repopath"
16
69
  }
17
70
  },
18
- "required": [
19
- "prNumber"
20
- ],
21
71
  "title": "GetMergeConflictFilesInput",
22
72
  "type": "object"
23
73
  },
@@ -4,20 +4,70 @@
4
4
  "parameters": {
5
5
  "properties": {
6
6
  "prNumber": {
7
+ "anyOf": [
8
+ {
9
+ "type": "integer"
10
+ },
11
+ {
12
+ "type": "null"
13
+ }
14
+ ],
15
+ "default": null,
7
16
  "description": "The number of the pull request to check for conflicts.",
8
- "title": "Prnumber",
9
- "type": "integer"
17
+ "title": "Prnumber"
10
18
  },
11
19
  "baseBranch": {
12
20
  "default": "main",
13
21
  "description": "The base branch to check against (default: 'main').",
14
22
  "title": "Basebranch",
15
23
  "type": "string"
24
+ },
25
+ "headRef": {
26
+ "default": "HEAD",
27
+ "description": "The head ref or branch to check.",
28
+ "title": "Headref",
29
+ "type": "string"
30
+ },
31
+ "repo": {
32
+ "anyOf": [
33
+ {
34
+ "type": "string"
35
+ },
36
+ {
37
+ "type": "null"
38
+ }
39
+ ],
40
+ "default": null,
41
+ "description": "Target GitHub repository override.",
42
+ "title": "Repo"
43
+ },
44
+ "path": {
45
+ "anyOf": [
46
+ {
47
+ "type": "string"
48
+ },
49
+ {
50
+ "type": "null"
51
+ }
52
+ ],
53
+ "default": null,
54
+ "description": "Local filesystem path to target repository root.",
55
+ "title": "Path"
56
+ },
57
+ "repoPath": {
58
+ "anyOf": [
59
+ {
60
+ "type": "string"
61
+ },
62
+ {
63
+ "type": "null"
64
+ }
65
+ ],
66
+ "default": null,
67
+ "description": "Alias for path to target repository root.",
68
+ "title": "Repopath"
16
69
  }
17
70
  },
18
- "required": [
19
- "prNumber"
20
- ],
21
71
  "title": "GetMergeConflictFilesInput",
22
72
  "type": "object"
23
73
  },
@@ -13,6 +13,45 @@
13
13
  "description": "Branch to branch off from",
14
14
  "title": "Basebranch",
15
15
  "type": "string"
16
+ },
17
+ "repo": {
18
+ "anyOf": [
19
+ {
20
+ "type": "string"
21
+ },
22
+ {
23
+ "type": "null"
24
+ }
25
+ ],
26
+ "default": null,
27
+ "description": "Target repository override (e.g. org/repo or repo).",
28
+ "title": "Repo"
29
+ },
30
+ "path": {
31
+ "anyOf": [
32
+ {
33
+ "type": "string"
34
+ },
35
+ {
36
+ "type": "null"
37
+ }
38
+ ],
39
+ "default": null,
40
+ "description": "Local filesystem path to target repository root.",
41
+ "title": "Path"
42
+ },
43
+ "repoPath": {
44
+ "anyOf": [
45
+ {
46
+ "type": "string"
47
+ },
48
+ {
49
+ "type": "null"
50
+ }
51
+ ],
52
+ "default": null,
53
+ "description": "Alias for path to target repository root.",
54
+ "title": "Repopath"
16
55
  }
17
56
  },
18
57
  "required": [
@@ -43,6 +82,30 @@
43
82
  ],
44
83
  "default": null,
45
84
  "title": "Basebranch"
85
+ },
86
+ "repo": {
87
+ "anyOf": [
88
+ {
89
+ "type": "string"
90
+ },
91
+ {
92
+ "type": "null"
93
+ }
94
+ ],
95
+ "default": null,
96
+ "title": "Repo"
97
+ },
98
+ "path": {
99
+ "anyOf": [
100
+ {
101
+ "type": "string"
102
+ },
103
+ {
104
+ "type": "null"
105
+ }
106
+ ],
107
+ "default": null,
108
+ "title": "Path"
46
109
  }
47
110
  },
48
111
  "required": [
@@ -25,6 +25,45 @@
25
25
  "description": "Safety gate: Must be true to perform branch creation and worktree setup.",
26
26
  "title": "Writemode",
27
27
  "type": "boolean"
28
+ },
29
+ "repo": {
30
+ "anyOf": [
31
+ {
32
+ "type": "string"
33
+ },
34
+ {
35
+ "type": "null"
36
+ }
37
+ ],
38
+ "default": null,
39
+ "description": "Target repository override (e.g. org/repo or repo).",
40
+ "title": "Repo"
41
+ },
42
+ "path": {
43
+ "anyOf": [
44
+ {
45
+ "type": "string"
46
+ },
47
+ {
48
+ "type": "null"
49
+ }
50
+ ],
51
+ "default": null,
52
+ "description": "Local filesystem path to target repository root.",
53
+ "title": "Path"
54
+ },
55
+ "repoPath": {
56
+ "anyOf": [
57
+ {
58
+ "type": "string"
59
+ },
60
+ {
61
+ "type": "null"
62
+ }
63
+ ],
64
+ "default": null,
65
+ "description": "Alias for path to target repository root.",
66
+ "title": "Repopath"
28
67
  }
29
68
  },
30
69
  "required": [
@@ -55,6 +94,30 @@
55
94
  "worktreePath": {
56
95
  "title": "Worktreepath",
57
96
  "type": "string"
97
+ },
98
+ "repo": {
99
+ "anyOf": [
100
+ {
101
+ "type": "string"
102
+ },
103
+ {
104
+ "type": "null"
105
+ }
106
+ ],
107
+ "default": null,
108
+ "title": "Repo"
109
+ },
110
+ "path": {
111
+ "anyOf": [
112
+ {
113
+ "type": "string"
114
+ },
115
+ {
116
+ "type": "null"
117
+ }
118
+ ],
119
+ "default": null,
120
+ "title": "Path"
58
121
  }
59
122
  },
60
123
  "required": [
@@ -14,6 +14,45 @@
14
14
  "description": "The head ref to compare to (default: 'HEAD').",
15
15
  "title": "Head",
16
16
  "type": "string"
17
+ },
18
+ "repo": {
19
+ "anyOf": [
20
+ {
21
+ "type": "string"
22
+ },
23
+ {
24
+ "type": "null"
25
+ }
26
+ ],
27
+ "default": null,
28
+ "description": "Target GitHub repository override.",
29
+ "title": "Repo"
30
+ },
31
+ "path": {
32
+ "anyOf": [
33
+ {
34
+ "type": "string"
35
+ },
36
+ {
37
+ "type": "null"
38
+ }
39
+ ],
40
+ "default": null,
41
+ "description": "Local filesystem path to target repository root.",
42
+ "title": "Path"
43
+ },
44
+ "repoPath": {
45
+ "anyOf": [
46
+ {
47
+ "type": "string"
48
+ },
49
+ {
50
+ "type": "null"
51
+ }
52
+ ],
53
+ "default": null,
54
+ "description": "Alias for path to target repository root.",
55
+ "title": "Repopath"
17
56
  }
18
57
  },
19
58
  "title": "GetChangedFilesInput",
@@ -13,6 +13,45 @@
13
13
  "description": "Branch to branch off from",
14
14
  "title": "Basebranch",
15
15
  "type": "string"
16
+ },
17
+ "repo": {
18
+ "anyOf": [
19
+ {
20
+ "type": "string"
21
+ },
22
+ {
23
+ "type": "null"
24
+ }
25
+ ],
26
+ "default": null,
27
+ "description": "Target repository override (e.g. org/repo or repo).",
28
+ "title": "Repo"
29
+ },
30
+ "path": {
31
+ "anyOf": [
32
+ {
33
+ "type": "string"
34
+ },
35
+ {
36
+ "type": "null"
37
+ }
38
+ ],
39
+ "default": null,
40
+ "description": "Local filesystem path to target repository root.",
41
+ "title": "Path"
42
+ },
43
+ "repoPath": {
44
+ "anyOf": [
45
+ {
46
+ "type": "string"
47
+ },
48
+ {
49
+ "type": "null"
50
+ }
51
+ ],
52
+ "default": null,
53
+ "description": "Alias for path to target repository root.",
54
+ "title": "Repopath"
16
55
  }
17
56
  },
18
57
  "required": [
@@ -43,6 +82,30 @@
43
82
  ],
44
83
  "default": null,
45
84
  "title": "Basebranch"
85
+ },
86
+ "repo": {
87
+ "anyOf": [
88
+ {
89
+ "type": "string"
90
+ },
91
+ {
92
+ "type": "null"
93
+ }
94
+ ],
95
+ "default": null,
96
+ "title": "Repo"
97
+ },
98
+ "path": {
99
+ "anyOf": [
100
+ {
101
+ "type": "string"
102
+ },
103
+ {
104
+ "type": "null"
105
+ }
106
+ ],
107
+ "default": null,
108
+ "title": "Path"
46
109
  }
47
110
  },
48
111
  "required": [
@@ -25,6 +25,45 @@
25
25
  "description": "Safety gate: Must be true to perform branch creation and worktree setup.",
26
26
  "title": "Writemode",
27
27
  "type": "boolean"
28
+ },
29
+ "repo": {
30
+ "anyOf": [
31
+ {
32
+ "type": "string"
33
+ },
34
+ {
35
+ "type": "null"
36
+ }
37
+ ],
38
+ "default": null,
39
+ "description": "Target repository override (e.g. org/repo or repo).",
40
+ "title": "Repo"
41
+ },
42
+ "path": {
43
+ "anyOf": [
44
+ {
45
+ "type": "string"
46
+ },
47
+ {
48
+ "type": "null"
49
+ }
50
+ ],
51
+ "default": null,
52
+ "description": "Local filesystem path to target repository root.",
53
+ "title": "Path"
54
+ },
55
+ "repoPath": {
56
+ "anyOf": [
57
+ {
58
+ "type": "string"
59
+ },
60
+ {
61
+ "type": "null"
62
+ }
63
+ ],
64
+ "default": null,
65
+ "description": "Alias for path to target repository root.",
66
+ "title": "Repopath"
28
67
  }
29
68
  },
30
69
  "required": [
@@ -55,6 +94,30 @@
55
94
  "worktreePath": {
56
95
  "title": "Worktreepath",
57
96
  "type": "string"
97
+ },
98
+ "repo": {
99
+ "anyOf": [
100
+ {
101
+ "type": "string"
102
+ },
103
+ {
104
+ "type": "null"
105
+ }
106
+ ],
107
+ "default": null,
108
+ "title": "Repo"
109
+ },
110
+ "path": {
111
+ "anyOf": [
112
+ {
113
+ "type": "string"
114
+ },
115
+ {
116
+ "type": "null"
117
+ }
118
+ ],
119
+ "default": null,
120
+ "title": "Path"
58
121
  }
59
122
  },
60
123
  "required": [
@@ -14,6 +14,45 @@
14
14
  "description": "The head ref to compare to (default: 'HEAD').",
15
15
  "title": "Head",
16
16
  "type": "string"
17
+ },
18
+ "repo": {
19
+ "anyOf": [
20
+ {
21
+ "type": "string"
22
+ },
23
+ {
24
+ "type": "null"
25
+ }
26
+ ],
27
+ "default": null,
28
+ "description": "Target GitHub repository override.",
29
+ "title": "Repo"
30
+ },
31
+ "path": {
32
+ "anyOf": [
33
+ {
34
+ "type": "string"
35
+ },
36
+ {
37
+ "type": "null"
38
+ }
39
+ ],
40
+ "default": null,
41
+ "description": "Local filesystem path to target repository root.",
42
+ "title": "Path"
43
+ },
44
+ "repoPath": {
45
+ "anyOf": [
46
+ {
47
+ "type": "string"
48
+ },
49
+ {
50
+ "type": "null"
51
+ }
52
+ ],
53
+ "default": null,
54
+ "description": "Alias for path to target repository root.",
55
+ "title": "Repopath"
17
56
  }
18
57
  },
19
58
  "title": "GetChangedFilesInput",
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.7.1](https://github.com/arii/boomtick/compare/mcp-v0.7.0...mcp-v0.7.1) (2026-10-29)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * **mcp:** resolve ecosystem_repos without requiring local path existence during isolated test runs ([53ab66a](https://github.com/arii/boomtick/commit/53ab66ab6503c93ff07bfd4e525a5f490df16386))
9
+ * **mcp:** resolve target workspace dynamically in repo.create_branch and worktree tools ([#990](https://github.com/arii/boomtick/issues/990)) ([cd2f34e](https://github.com/arii/boomtick/commit/cd2f34ea8607f037b855d41f899bf5152dc13c1f))
10
+
3
11
  ## [0.7.0](https://github.com/arii/boomtick/compare/mcp-v0.6.2...mcp-v0.7.0) (2026-10-29)
4
12
 
5
13
 
package/dist/lib/git.js CHANGED
@@ -2,6 +2,7 @@ import { runCommand } from "./shell.js";
2
2
  import { config } from "../config.js";
3
3
  import path from "path";
4
4
  import fs from "fs/promises";
5
+ import * as syncFs from "fs";
5
6
  import { execSync } from "child_process";
6
7
  const repoCache = new Map();
7
8
  export function resolveRepo(explicitRepo, cwd) {
@@ -31,6 +32,75 @@ export function resolveRepo(explicitRepo, cwd) {
31
32
  repoCache.set(targetCwd, fallback);
32
33
  return fallback;
33
34
  }
35
+ export function resolveRepoPath(options = {}) {
36
+ // 1. Explicit path parameter has highest precedence
37
+ if (options.path && options.path.trim().length > 0) {
38
+ return path.resolve(options.path.trim());
39
+ }
40
+ // 2. repoPath alias parameter
41
+ if (options.repoPath && options.repoPath.trim().length > 0) {
42
+ return path.resolve(options.repoPath.trim());
43
+ }
44
+ // 3. Repository name override (e.g. "arii/tech-dancer" or "tech-dancer")
45
+ if (options.repo && options.repo.trim().length > 0) {
46
+ const rawRepo = options.repo.trim();
47
+ const repoName = rawRepo.includes("/") ? rawRepo.split("/")[1] : rawRepo;
48
+ // First check project_config.json ecosystem_repos if available
49
+ let rootPath = process.cwd();
50
+ try {
51
+ if (options.cwd && options.cwd.trim().length > 0) {
52
+ rootPath = options.cwd.trim();
53
+ }
54
+ else if (config.repoPath) {
55
+ rootPath = config.repoPath;
56
+ }
57
+ }
58
+ catch {
59
+ rootPath = process.cwd();
60
+ }
61
+ const projectConfigPath = path.join(rootPath, "project_config.json");
62
+ if (syncFs.existsSync(projectConfigPath)) {
63
+ try {
64
+ const parsed = JSON.parse(syncFs.readFileSync(projectConfigPath, "utf-8"));
65
+ if (parsed?.ecosystem_repos && typeof parsed.ecosystem_repos === "object") {
66
+ for (const [key, relPath] of Object.entries(parsed.ecosystem_repos)) {
67
+ const keyRepoName = key.includes("/") ? key.split("/")[1] : key;
68
+ if (key === rawRepo || keyRepoName === repoName) {
69
+ return path.resolve(rootPath, relPath);
70
+ }
71
+ }
72
+ }
73
+ }
74
+ catch {
75
+ // Fallback to directory inspection
76
+ }
77
+ }
78
+ // Check sibling directory relative to cwd or rootPath
79
+ const candidateDirs = [
80
+ path.resolve(process.cwd(), "..", repoName),
81
+ path.resolve(rootPath, "..", repoName),
82
+ ];
83
+ for (const cand of candidateDirs) {
84
+ if (syncFs.existsSync(cand)) {
85
+ return cand;
86
+ }
87
+ }
88
+ }
89
+ // 4. Explicit cwd parameter
90
+ if (options.cwd && options.cwd.trim().length > 0) {
91
+ return path.resolve(options.cwd.trim());
92
+ }
93
+ // 5. Configured repoPath or process.cwd()
94
+ try {
95
+ if (config.repoPath && syncFs.existsSync(config.repoPath)) {
96
+ return config.repoPath;
97
+ }
98
+ }
99
+ catch {
100
+ // Fall back to process.cwd()
101
+ }
102
+ return process.cwd();
103
+ }
34
104
  function validatePrNumber(value) {
35
105
  const prNumber = Number(value);
36
106
  if (!Number.isInteger(prNumber) || prNumber <= 0) {
@@ -38,26 +108,28 @@ function validatePrNumber(value) {
38
108
  }
39
109
  return prNumber;
40
110
  }
41
- export async function createWorktree(branch, prNumber) {
111
+ export async function createWorktree(branch, prNumber, baseRepoPath) {
42
112
  const safePrNumber = validatePrNumber(prNumber);
43
113
  const workspaceRoot = "/tmp/boomtick-worktrees";
44
114
  // nosemgrep
45
115
  const worktreePath = path.join(workspaceRoot, `boomtick-mcp-rescue-${safePrNumber}`);
116
+ const cwdOption = baseRepoPath ? { cwd: baseRepoPath } : undefined;
46
117
  // Clean up if exists
47
118
  try {
48
119
  await fs.rm(worktreePath, { recursive: true, force: true });
49
- await runCommand("git", ["worktree", "prune"]);
120
+ await runCommand("git", ["worktree", "prune"], cwdOption);
50
121
  }
51
122
  catch (e) { /* ignore if no previous worktree */ }
52
- const result = await runCommand("git", ["worktree", "add", "-b", `repair-pr-${prNumber}-${Date.now()}`, worktreePath, branch]);
123
+ const result = await runCommand("git", ["worktree", "add", "-b", `repair-pr-${prNumber}-${Date.now()}`, worktreePath, branch], cwdOption);
53
124
  if (result.exitCode !== 0) {
54
125
  throw new Error(`Failed to create worktree: ${result.stderr}`);
55
126
  }
56
127
  return worktreePath;
57
128
  }
58
- export async function removeWorktree(worktreePath) {
59
- await runCommand("git", ["worktree", "remove", "--force", worktreePath]);
60
- await runCommand("git", ["worktree", "prune"]);
129
+ export async function removeWorktree(worktreePath, baseRepoPath) {
130
+ const cwdOption = baseRepoPath ? { cwd: baseRepoPath } : undefined;
131
+ await runCommand("git", ["worktree", "remove", "--force", worktreePath], cwdOption);
132
+ await runCommand("git", ["worktree", "prune"], cwdOption);
61
133
  }
62
134
  export async function getChangedFiles(base = "main", head = "HEAD", cwd) {
63
135
  const result = await runCommand("git", ["diff", "--name-status", "-z", `${base}...${head}`], { cwd });