@iflow-mcp/camoneart-maestro 5.0.0

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.
@@ -0,0 +1,847 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/mcp/server.ts
4
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
5
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
+ import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
7
+ import { z as z2 } from "zod";
8
+
9
+ // src/core/git.ts
10
+ import simpleGit from "simple-git";
11
+
12
+ // src/core/config.ts
13
+ import { z } from "zod";
14
+ import Conf from "conf";
15
+ import path from "path";
16
+ import fs from "fs/promises";
17
+ var ConfigSchema = z.object({
18
+ // Git worktree設定
19
+ worktrees: z.object({
20
+ // worktreeを作成するディレクトリ(デフォルト: .git/orchestrations)
21
+ path: z.string().optional(),
22
+ // ブランチ名のプレフィックス
23
+ branchPrefix: z.string().optional(),
24
+ // ディレクトリ名のプレフィックス(デフォルト: 空文字列)
25
+ directoryPrefix: z.string().optional()
26
+ }).optional(),
27
+ // 開発環境設定
28
+ development: z.object({
29
+ // 自動でnpm installを実行
30
+ autoSetup: z.boolean().default(true),
31
+ // 同期するファイル(.envなど)
32
+ syncFiles: z.array(z.string()).default([".env", ".env.local"]),
33
+ // デフォルトのエディタ
34
+ defaultEditor: z.enum(["vscode", "cursor", "none"]).default("cursor")
35
+ }).optional(),
36
+ // tmux統合設定
37
+ tmux: z.object({
38
+ enabled: z.boolean().default(false),
39
+ // 新規ウィンドウかペインか
40
+ openIn: z.enum(["window", "pane"]).default("window"),
41
+ // セッション名の命名規則
42
+ sessionNaming: z.string().default("{branch}")
43
+ }).optional(),
44
+ // Claude Code統合設定
45
+ claude: z.object({
46
+ // CLAUDE.mdの処理方法
47
+ markdownMode: z.enum(["shared", "split"]).default("shared")
48
+ }).optional(),
49
+ // GitHub統合設定
50
+ github: z.object({
51
+ // 自動でfetchを実行
52
+ autoFetch: z.boolean().default(true),
53
+ // ブランチ命名規則
54
+ branchNaming: z.object({
55
+ // PR用のテンプレート (例: "pr-{number}-{title}")
56
+ prTemplate: z.string().default("pr-{number}"),
57
+ // Issue用のテンプレート (例: "issue-{number}-{title}")
58
+ issueTemplate: z.string().default("issue-{number}")
59
+ }).optional()
60
+ }).optional(),
61
+ // UI表示設定
62
+ ui: z.object({
63
+ // パス表示形式 ('absolute' | 'relative')
64
+ pathDisplay: z.enum(["absolute", "relative"]).default("absolute")
65
+ }).optional(),
66
+ // カスタムコマンドとファイルコピー設定
67
+ hooks: z.object({
68
+ // worktree作成後に実行(文字列または配列)
69
+ afterCreate: z.union([z.string(), z.array(z.string())]).optional(),
70
+ // worktree削除前に実行
71
+ beforeDelete: z.string().optional()
72
+ }).optional(),
73
+ // worktree作成時の処理
74
+ postCreate: z.object({
75
+ // コピーするファイル(gitignoreファイルも含む)
76
+ copyFiles: z.array(z.string()).optional(),
77
+ // 実行するコマンド
78
+ commands: z.array(z.string()).optional()
79
+ }).optional()
80
+ });
81
+ var DEFAULT_CONFIG = {
82
+ worktrees: {
83
+ path: "../maestro-{branch}",
84
+ directoryPrefix: ""
85
+ },
86
+ development: {
87
+ autoSetup: true,
88
+ syncFiles: [".env", ".env.local"],
89
+ defaultEditor: "cursor"
90
+ },
91
+ tmux: {
92
+ enabled: false,
93
+ openIn: "window",
94
+ sessionNaming: "{branch}"
95
+ },
96
+ claude: {
97
+ markdownMode: "shared"
98
+ },
99
+ github: {
100
+ autoFetch: true
101
+ },
102
+ ui: {
103
+ pathDisplay: "absolute"
104
+ },
105
+ hooks: {}
106
+ };
107
+ var ConfigManager = class {
108
+ conf;
109
+ projectConfig = null;
110
+ userConfig = null;
111
+ constructor() {
112
+ this.conf = new Conf({
113
+ projectName: "maestro",
114
+ defaults: DEFAULT_CONFIG
115
+ });
116
+ }
117
+ async loadProjectConfig() {
118
+ try {
119
+ await this.loadUserConfig();
120
+ const configPaths = [
121
+ path.join(process.cwd(), ".maestro.json"),
122
+ path.join(process.cwd(), ".maestrorc.json"),
123
+ path.join(process.cwd(), "maestro.config.json"),
124
+ // グローバル設定ファイル
125
+ path.join(process.env.HOME || "~", ".maestrorc"),
126
+ path.join(process.env.HOME || "~", ".maestrorc.json")
127
+ ];
128
+ for (const configPath of configPaths) {
129
+ try {
130
+ const configData = await fs.readFile(configPath, "utf-8");
131
+ const parsedConfig = JSON.parse(configData);
132
+ this.projectConfig = ConfigSchema.parse(parsedConfig);
133
+ return;
134
+ } catch {
135
+ }
136
+ }
137
+ } catch (error) {
138
+ console.error("\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u8A2D\u5B9A\u306E\u8AAD\u307F\u8FBC\u307F\u306B\u5931\u6557\u3057\u307E\u3057\u305F:", error);
139
+ }
140
+ }
141
+ async loadUserConfig() {
142
+ try {
143
+ const userConfigPath = path.join(process.cwd(), ".maestro.local.json");
144
+ const configData = await fs.readFile(userConfigPath, "utf-8");
145
+ const parsedConfig = JSON.parse(configData);
146
+ this.userConfig = ConfigSchema.parse(parsedConfig);
147
+ } catch {
148
+ this.userConfig = null;
149
+ }
150
+ }
151
+ // 設定を取得(ユーザー設定 > プロジェクト設定 > グローバル設定 > デフォルト)
152
+ get(key) {
153
+ if (this.userConfig && this.userConfig[key] !== void 0) {
154
+ return this.userConfig[key];
155
+ }
156
+ if (this.projectConfig && this.projectConfig[key] !== void 0) {
157
+ return this.projectConfig[key];
158
+ }
159
+ return this.conf.get(key) ?? DEFAULT_CONFIG[key];
160
+ }
161
+ // 設定を更新(グローバル設定のみ)
162
+ set(key, value) {
163
+ this.conf.set(key, value);
164
+ }
165
+ // 全設定を取得
166
+ getAll() {
167
+ const globalConfig = this.conf.store;
168
+ return {
169
+ ...DEFAULT_CONFIG,
170
+ ...globalConfig,
171
+ ...this.projectConfig || {},
172
+ ...this.userConfig || {}
173
+ };
174
+ }
175
+ // 設定ファイルのパスを取得
176
+ getConfigPath() {
177
+ return this.conf.path;
178
+ }
179
+ // ドット記法で設定値を取得
180
+ getConfigValue(keyPath) {
181
+ const keys = keyPath.split(".");
182
+ const config = this.getAll();
183
+ return keys.reduce((obj, key) => {
184
+ if (obj && typeof obj === "object" && key in obj) {
185
+ return obj[key];
186
+ }
187
+ return void 0;
188
+ }, config);
189
+ }
190
+ // ドット記法で設定値を設定
191
+ async setConfigValue(keyPath, value, target = "project") {
192
+ if (target === "user") {
193
+ await this.setUserConfigValue(keyPath, value);
194
+ } else {
195
+ await this.setProjectConfigValue(keyPath, value);
196
+ }
197
+ }
198
+ // ユーザー設定を設定
199
+ async setUserConfigValue(keyPath, value) {
200
+ const configPath = path.join(process.cwd(), ".maestro.local.json");
201
+ let userConfig = {};
202
+ try {
203
+ const configData = await fs.readFile(configPath, "utf-8");
204
+ userConfig = JSON.parse(configData);
205
+ } catch {
206
+ }
207
+ const keys = keyPath.split(".");
208
+ let current = userConfig;
209
+ for (let i = 0; i < keys.length - 1; i++) {
210
+ const key = keys[i];
211
+ if (!key) continue;
212
+ if (!current[key] || typeof current[key] !== "object") {
213
+ current[key] = {};
214
+ }
215
+ current = current[key];
216
+ }
217
+ const lastKey = keys[keys.length - 1];
218
+ if (lastKey) {
219
+ current[lastKey] = this.parseValue(value);
220
+ }
221
+ const validatedConfig = ConfigSchema.parse(userConfig);
222
+ await fs.writeFile(configPath, JSON.stringify(validatedConfig, null, 2) + "\n", "utf-8");
223
+ this.userConfig = validatedConfig;
224
+ }
225
+ // プロジェクト設定を設定
226
+ async setProjectConfigValue(keyPath, value) {
227
+ const configPath = path.join(process.cwd(), ".maestro.json");
228
+ let projectConfig = {};
229
+ try {
230
+ const configData = await fs.readFile(configPath, "utf-8");
231
+ projectConfig = JSON.parse(configData);
232
+ } catch {
233
+ }
234
+ const keys = keyPath.split(".");
235
+ let current = projectConfig;
236
+ for (let i = 0; i < keys.length - 1; i++) {
237
+ const key = keys[i];
238
+ if (!key) continue;
239
+ if (!current[key] || typeof current[key] !== "object") {
240
+ current[key] = {};
241
+ }
242
+ current = current[key];
243
+ }
244
+ const lastKey = keys[keys.length - 1];
245
+ if (lastKey) {
246
+ current[lastKey] = this.parseValue(value);
247
+ }
248
+ const validatedConfig = ConfigSchema.parse(projectConfig);
249
+ await fs.writeFile(configPath, JSON.stringify(validatedConfig, null, 2) + "\n", "utf-8");
250
+ this.projectConfig = validatedConfig;
251
+ }
252
+ // 設定値をリセット(デフォルトに戻す)
253
+ async resetConfigValue(keyPath) {
254
+ const configPath = path.join(process.cwd(), ".maestro.json");
255
+ let projectConfig = {};
256
+ try {
257
+ const configData = await fs.readFile(configPath, "utf-8");
258
+ projectConfig = JSON.parse(configData);
259
+ } catch {
260
+ return;
261
+ }
262
+ const keys = keyPath.split(".");
263
+ let current = projectConfig;
264
+ const parents = [];
265
+ for (let i = 0; i < keys.length - 1; i++) {
266
+ const key = keys[i];
267
+ if (!key || !current[key]) {
268
+ return;
269
+ }
270
+ parents.push({ obj: current, key });
271
+ current = current[key];
272
+ }
273
+ const lastKey = keys[keys.length - 1];
274
+ if (lastKey && current[lastKey] !== void 0) {
275
+ delete current[lastKey];
276
+ }
277
+ this.cleanEmptyObjects(projectConfig, keyPath.split(".").slice(0, -1));
278
+ await fs.writeFile(configPath, JSON.stringify(projectConfig, null, 2) + "\n", "utf-8");
279
+ this.projectConfig = Object.keys(projectConfig).length > 0 ? projectConfig : null;
280
+ }
281
+ // 値の型変換
282
+ parseValue(value) {
283
+ if (typeof value === "string") {
284
+ if (value === "true") return true;
285
+ if (value === "false") return false;
286
+ if (/^\d+$/.test(value)) return parseInt(value, 10);
287
+ if (/^\d+\.\d+$/.test(value)) return parseFloat(value);
288
+ }
289
+ return value;
290
+ }
291
+ // 空のオブジェクトを削除
292
+ cleanEmptyObjects(obj, keys) {
293
+ if (keys.length === 0) return;
294
+ let current = obj;
295
+ for (let i = 0; i < keys.length - 1; i++) {
296
+ const key = keys[i];
297
+ if (!key || !current[key]) return;
298
+ current = current[key];
299
+ }
300
+ const lastKey = keys[keys.length - 1];
301
+ if (lastKey && current[lastKey] && typeof current[lastKey] === "object" && Object.keys(current[lastKey]).length === 0) {
302
+ delete current[lastKey];
303
+ this.cleanEmptyObjects(obj, keys.slice(0, -1));
304
+ }
305
+ }
306
+ // プロジェクト設定ファイルの作成
307
+ async createProjectConfig(configPath) {
308
+ const targetPath = configPath || path.join(process.cwd(), ".maestro.json");
309
+ const exampleConfig = {
310
+ worktrees: {
311
+ path: "../maestro-{branch}",
312
+ branchPrefix: "feature/",
313
+ directoryPrefix: "maestro-"
314
+ },
315
+ development: {
316
+ autoSetup: true,
317
+ syncFiles: [".env", ".env.local"],
318
+ defaultEditor: "cursor"
319
+ },
320
+ tmux: {
321
+ enabled: true,
322
+ openIn: "window",
323
+ sessionNaming: "{branch}"
324
+ },
325
+ claude: {
326
+ markdownMode: "shared"
327
+ },
328
+ github: {
329
+ autoFetch: true,
330
+ branchNaming: {
331
+ prTemplate: "pr-{number}",
332
+ issueTemplate: "issue-{number}"
333
+ }
334
+ },
335
+ ui: {
336
+ pathDisplay: "absolute"
337
+ },
338
+ hooks: {
339
+ afterCreate: "npm install",
340
+ beforeDelete: 'echo "\u30AA\u30FC\u30B1\u30B9\u30C8\u30E9\u30E1\u30F3\u30D0\u30FC\u3092\u89E3\u6563\u3057\u307E\u3059: $MAESTRO_BRANCH"'
341
+ },
342
+ postCreate: {
343
+ copyFiles: [".env", ".env.local"],
344
+ commands: ["pnpm install", "pnpm run dev"]
345
+ }
346
+ };
347
+ await fs.writeFile(targetPath, JSON.stringify(exampleConfig, null, 2) + "\n", "utf-8");
348
+ }
349
+ };
350
+
351
+ // src/core/git.ts
352
+ import path2 from "path";
353
+ import fs2 from "fs/promises";
354
+ import chalk from "chalk";
355
+ import inquirer from "inquirer";
356
+ var GitWorktreeManager = class {
357
+ git;
358
+ configManager;
359
+ constructor(baseDir) {
360
+ this.git = simpleGit(baseDir || process.cwd());
361
+ this.configManager = new ConfigManager();
362
+ }
363
+ async createWorktree(branchName, baseBranch, skipDirCheck) {
364
+ await this.checkBranchNameCollision(branchName);
365
+ await this.configManager.loadProjectConfig();
366
+ const worktreeConfig = this.configManager.get("worktrees");
367
+ const directoryPrefix = worktreeConfig?.directoryPrefix || "";
368
+ const repoRoot = await this.getRepositoryRoot();
369
+ const worktreePath = path2.join(repoRoot, "..", `${directoryPrefix}${branchName}`);
370
+ if (!skipDirCheck) {
371
+ const dirExists = await this.checkDirectoryExists(worktreePath);
372
+ if (dirExists) {
373
+ const action = await this.handleExistingDirectory(worktreePath, branchName);
374
+ if (action === "cancel") {
375
+ throw new Error("\u30EF\u30FC\u30AF\u30C4\u30EA\u30FC\u306E\u4F5C\u6210\u304C\u30AD\u30E3\u30F3\u30BB\u30EB\u3055\u308C\u307E\u3057\u305F");
376
+ } else if (action === "rename") {
377
+ const branches = await this.getAllBranches();
378
+ const allBranches = [
379
+ ...branches.local,
380
+ ...branches.remote.map((r) => r.replace(/^[^/]+\//, ""))
381
+ ];
382
+ const alternativeName = this.generateAlternativeBranchName(branchName, allBranches);
383
+ console.log(chalk.yellow(`
384
+ \u65B0\u3057\u3044\u30D6\u30E9\u30F3\u30C1\u540D: ${alternativeName}`));
385
+ return this.createWorktree(alternativeName, baseBranch, true);
386
+ } else if (action === "delete") {
387
+ await fs2.rm(worktreePath, { recursive: true, force: true });
388
+ console.log(
389
+ chalk.gray(`\u{1F5D1}\uFE0F \u65E2\u5B58\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u3092\u524A\u9664\u3057\u307E\u3057\u305F: ${path2.basename(worktreePath)}`)
390
+ );
391
+ }
392
+ }
393
+ }
394
+ if (!baseBranch) {
395
+ const status = await this.git.status();
396
+ baseBranch = status.current || "main";
397
+ }
398
+ await this.git.raw(["worktree", "add", "-b", branchName, worktreePath, baseBranch]);
399
+ return path2.resolve(worktreePath);
400
+ }
401
+ async attachWorktree(existingBranch, skipDirCheck) {
402
+ await this.configManager.loadProjectConfig();
403
+ const worktreeConfig = this.configManager.get("worktrees");
404
+ const directoryPrefix = worktreeConfig?.directoryPrefix || "";
405
+ const repoRoot = await this.getRepositoryRoot();
406
+ const safeBranchName = existingBranch.replace(/\//g, "-");
407
+ const worktreePath = path2.join(repoRoot, "..", `${directoryPrefix}${safeBranchName}`);
408
+ if (!skipDirCheck) {
409
+ const dirExists = await this.checkDirectoryExists(worktreePath);
410
+ if (dirExists) {
411
+ const action = await this.handleExistingDirectory(worktreePath, safeBranchName);
412
+ if (action === "cancel") {
413
+ throw new Error("\u30EF\u30FC\u30AF\u30C4\u30EA\u30FC\u306E\u4F5C\u6210\u304C\u30AD\u30E3\u30F3\u30BB\u30EB\u3055\u308C\u307E\u3057\u305F");
414
+ } else if (action === "rename") {
415
+ const branches = await this.getAllBranches();
416
+ const allBranches = [
417
+ ...branches.local,
418
+ ...branches.remote.map((r) => r.replace(/^[^/]+\//, ""))
419
+ ];
420
+ const alternativeName = this.generateAlternativeBranchName(safeBranchName, allBranches);
421
+ const newWorktreePath = path2.join(repoRoot, "..", `${directoryPrefix}${alternativeName}`);
422
+ console.log(chalk.yellow(`
423
+ \u65B0\u3057\u3044\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u540D: ${alternativeName}`));
424
+ await this.git.raw(["worktree", "add", newWorktreePath, existingBranch]);
425
+ return path2.resolve(newWorktreePath);
426
+ } else if (action === "delete") {
427
+ await fs2.rm(worktreePath, { recursive: true, force: true });
428
+ console.log(
429
+ chalk.gray(`\u{1F5D1}\uFE0F \u65E2\u5B58\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u3092\u524A\u9664\u3057\u307E\u3057\u305F: ${path2.basename(worktreePath)}`)
430
+ );
431
+ }
432
+ }
433
+ }
434
+ await this.git.raw(["worktree", "add", worktreePath, existingBranch]);
435
+ return path2.resolve(worktreePath);
436
+ }
437
+ async listWorktrees() {
438
+ const output = await this.git.raw(["worktree", "list", "--porcelain"]);
439
+ const worktrees = [];
440
+ const lines = output.split("\n").filter((line) => line.trim());
441
+ let currentWorktree = {};
442
+ for (const line of lines) {
443
+ if (line.startsWith("worktree ")) {
444
+ if (currentWorktree.path) {
445
+ worktrees.push(currentWorktree);
446
+ }
447
+ currentWorktree = {
448
+ path: line.substring(9),
449
+ detached: false,
450
+ prunable: false,
451
+ locked: false
452
+ };
453
+ } else if (line.startsWith("HEAD ")) {
454
+ currentWorktree.head = line.substring(5);
455
+ } else if (line.startsWith("branch ")) {
456
+ currentWorktree.branch = line.substring(7);
457
+ } else if (line === "detached") {
458
+ currentWorktree.detached = true;
459
+ } else if (line === "prunable") {
460
+ currentWorktree.prunable = true;
461
+ } else if (line.startsWith("locked")) {
462
+ currentWorktree.locked = true;
463
+ if (line.includes(" ")) {
464
+ currentWorktree.reason = line.substring(line.indexOf(" ") + 1);
465
+ }
466
+ }
467
+ }
468
+ if (currentWorktree.path) {
469
+ worktrees.push(currentWorktree);
470
+ }
471
+ return worktrees;
472
+ }
473
+ async deleteWorktree(branchName, force = false) {
474
+ const worktrees = await this.listWorktrees();
475
+ const worktree = worktrees.find((wt) => {
476
+ const branch = wt.branch?.replace("refs/heads/", "");
477
+ return branch === branchName;
478
+ });
479
+ if (!worktree) {
480
+ throw new Error(`\u30EF\u30FC\u30AF\u30C4\u30EA\u30FC '${branchName}' \u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093`);
481
+ }
482
+ const args = ["worktree", "remove"];
483
+ if (force) args.push("--force");
484
+ args.push(worktree.path);
485
+ await this.git.raw(args);
486
+ await this.cleanupEmptyDirectories(worktree.path);
487
+ try {
488
+ await this.git.branch(["-d", branchName]);
489
+ } catch (error) {
490
+ if (error instanceof Error && error.message.includes("not fully merged")) {
491
+ await this.git.branch(["-D", branchName]);
492
+ } else {
493
+ throw error;
494
+ }
495
+ }
496
+ }
497
+ async getCurrentBranch() {
498
+ const status = await this.git.status();
499
+ return status.current;
500
+ }
501
+ async isGitRepository() {
502
+ try {
503
+ await this.git.status();
504
+ return true;
505
+ } catch {
506
+ return false;
507
+ }
508
+ }
509
+ async getAllBranches() {
510
+ const localBranches = await this.git.branchLocal();
511
+ const remoteBranches = await this.git.branch(["-r"]);
512
+ return {
513
+ local: localBranches.all.filter((b) => !b.startsWith("remotes/")),
514
+ remote: remoteBranches.all.filter((b) => b.startsWith("remotes/")).map((b) => b.replace("remotes/", ""))
515
+ };
516
+ }
517
+ async listLocalBranches() {
518
+ const localBranches = await this.git.branchLocal();
519
+ return localBranches.all.filter((b) => !b.startsWith("remotes/"));
520
+ }
521
+ async fetchAll() {
522
+ await this.git.fetch(["--all"]);
523
+ }
524
+ async getLastCommit(worktreePath) {
525
+ try {
526
+ const gitInWorktree = simpleGit(worktreePath);
527
+ const log = await gitInWorktree.log({ maxCount: 1 });
528
+ if (log.latest) {
529
+ return {
530
+ date: log.latest.date,
531
+ message: log.latest.message,
532
+ hash: log.latest.hash.substring(0, 7)
533
+ };
534
+ }
535
+ return null;
536
+ } catch {
537
+ return null;
538
+ }
539
+ }
540
+ async getRepositoryRoot() {
541
+ try {
542
+ const output = await this.git.raw(["rev-parse", "--show-toplevel"]);
543
+ return output.trim();
544
+ } catch {
545
+ throw new Error("\u30EA\u30DD\u30B8\u30C8\u30EA\u30EB\u30FC\u30C8\u306E\u53D6\u5F97\u306B\u5931\u6557\u3057\u307E\u3057\u305F");
546
+ }
547
+ }
548
+ async isGitignored(filePath) {
549
+ try {
550
+ await this.git.raw(["check-ignore", filePath]);
551
+ return true;
552
+ } catch {
553
+ return false;
554
+ }
555
+ }
556
+ async checkBranchNameCollision(branchName) {
557
+ const branches = await this.getAllBranches();
558
+ const allBranches = [...branches.local, ...branches.remote.map((r) => r.replace(/^[^/]+\//, ""))];
559
+ if (allBranches.includes(branchName)) {
560
+ throw new Error(`\u30D6\u30E9\u30F3\u30C1 '${branchName}' \u306F\u65E2\u306B\u5B58\u5728\u3057\u307E\u3059`);
561
+ }
562
+ const conflictingBranches = allBranches.filter(
563
+ (existing) => existing.startsWith(branchName + "/")
564
+ );
565
+ if (conflictingBranches.length > 0) {
566
+ const examples = conflictingBranches.slice(0, 3).join(", ");
567
+ throw new Error(
568
+ `\u30D6\u30E9\u30F3\u30C1 '${branchName}' \u3092\u4F5C\u6210\u3067\u304D\u307E\u305B\u3093\u3002\u4EE5\u4E0B\u306E\u65E2\u5B58\u30D6\u30E9\u30F3\u30C1\u3068\u7AF6\u5408\u3057\u307E\u3059: ${examples}${conflictingBranches.length > 3 ? ` \u306A\u3069 (${conflictingBranches.length}\u4EF6)` : ""}`
569
+ );
570
+ }
571
+ const parentConflicts = allBranches.filter((existing) => branchName.startsWith(existing + "/"));
572
+ if (parentConflicts.length > 0) {
573
+ const examples = parentConflicts.slice(0, 3).join(", ");
574
+ throw new Error(
575
+ `\u30D6\u30E9\u30F3\u30C1 '${branchName}' \u3092\u4F5C\u6210\u3067\u304D\u307E\u305B\u3093\u3002\u4EE5\u4E0B\u306E\u65E2\u5B58\u30D6\u30E9\u30F3\u30C1\u306E\u30B5\u30D6\u30D6\u30E9\u30F3\u30C1\u306B\u306A\u308A\u307E\u3059: ${examples}${parentConflicts.length > 3 ? ` \u306A\u3069 (${parentConflicts.length}\u4EF6)` : ""}`
576
+ );
577
+ }
578
+ }
579
+ generateAlternativeBranchName(originalName, allBranches) {
580
+ let counter = 1;
581
+ let alternativeName = `${originalName}-${counter}`;
582
+ while (allBranches.includes(alternativeName) || allBranches.some(
583
+ (b) => b.startsWith(alternativeName + "/") || alternativeName.startsWith(b + "/")
584
+ )) {
585
+ counter++;
586
+ alternativeName = `${originalName}-${counter}`;
587
+ }
588
+ return alternativeName;
589
+ }
590
+ async cleanupEmptyDirectories(worktreePath) {
591
+ const repoRoot = await this.getRepositoryRoot();
592
+ const baseDir = path2.join(repoRoot, "..");
593
+ let currentDir = path2.dirname(worktreePath);
594
+ while (currentDir !== baseDir && currentDir !== path2.dirname(currentDir)) {
595
+ try {
596
+ const entries = await fs2.readdir(currentDir);
597
+ if (entries.length === 0) {
598
+ await fs2.rmdir(currentDir);
599
+ console.log(chalk.gray(`\u{1F9F9} Removed empty directory: ${path2.basename(currentDir)}`));
600
+ currentDir = path2.dirname(currentDir);
601
+ } else {
602
+ break;
603
+ }
604
+ } catch {
605
+ break;
606
+ }
607
+ }
608
+ }
609
+ async checkDirectoryExists(dirPath) {
610
+ try {
611
+ const stats = await fs2.stat(dirPath);
612
+ return stats.isDirectory();
613
+ } catch {
614
+ return false;
615
+ }
616
+ }
617
+ async handleExistingDirectory(dirPath, branchName) {
618
+ const repoRoot = await this.getRepositoryRoot();
619
+ const relativePath = path2.relative(repoRoot, dirPath);
620
+ console.log(chalk.yellow(`
621
+ \u26A0\uFE0F \u30C7\u30A3\u30EC\u30AF\u30C8\u30EA '${relativePath}' \u306F\u65E2\u306B\u5B58\u5728\u3057\u307E\u3059`));
622
+ const choices = [
623
+ { name: "\u65E2\u5B58\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u3092\u524A\u9664\u3057\u3066\u65B0\u898F\u4F5C\u6210", value: "delete" },
624
+ { name: `\u5225\u306E\u540D\u524D\u3092\u4F7F\u7528\uFF08${branchName}-2\u306A\u3069\uFF09`, value: "rename" },
625
+ { name: "\u30AD\u30E3\u30F3\u30BB\u30EB", value: "cancel" }
626
+ ];
627
+ const answer = await inquirer.prompt([
628
+ {
629
+ type: "list",
630
+ name: "action",
631
+ message: "\u3069\u306E\u3088\u3046\u306B\u51E6\u7406\u3057\u307E\u3059\u304B\uFF1F",
632
+ choices
633
+ }
634
+ ]);
635
+ return answer.action;
636
+ }
637
+ };
638
+
639
+ // src/mcp/server.ts
640
+ import { readFileSync } from "fs";
641
+ import { fileURLToPath } from "url";
642
+ import { dirname, join } from "path";
643
+ var isTestEnvironment = process.env.NODE_ENV === "test";
644
+ var __dirname2 = dirname(fileURLToPath(import.meta.url));
645
+ var packageJson = JSON.parse(readFileSync(join(__dirname2, "../../package.json"), "utf-8"));
646
+ var CreateWorktreeArgsSchema = z2.object({
647
+ branchName: z2.string().describe("\u4F5C\u6210\u3059\u308B\u30D6\u30E9\u30F3\u30C1\u540D"),
648
+ baseBranch: z2.string().optional().describe("\u30D9\u30FC\u30B9\u30D6\u30E9\u30F3\u30C1\uFF08\u7701\u7565\u6642\u306F\u73FE\u5728\u306E\u30D6\u30E9\u30F3\u30C1\uFF09")
649
+ });
650
+ var DeleteWorktreeArgsSchema = z2.object({
651
+ branchName: z2.string().describe("\u524A\u9664\u3059\u308B\u30D6\u30E9\u30F3\u30C1\u540D"),
652
+ force: z2.boolean().optional().describe("\u5F37\u5236\u524A\u9664\u30D5\u30E9\u30B0")
653
+ });
654
+ var ExecInWorktreeArgsSchema = z2.object({
655
+ branchName: z2.string().describe("\u5B9F\u884C\u5BFE\u8C61\u306E\u30D6\u30E9\u30F3\u30C1\u540D"),
656
+ command: z2.string().describe("\u5B9F\u884C\u3059\u308B\u30B3\u30DE\u30F3\u30C9")
657
+ });
658
+ var server = new Server(
659
+ {
660
+ name: "maestro",
661
+ version: packageJson.version
662
+ },
663
+ {
664
+ capabilities: {
665
+ tools: {}
666
+ }
667
+ }
668
+ );
669
+ var gitManager = new GitWorktreeManager();
670
+ var TOOLS = [
671
+ {
672
+ name: "create_orchestra_member",
673
+ description: "\u65B0\u3057\u3044\u6F14\u594F\u8005\uFF08Git worktree\uFF09\u3092\u62DB\u96C6\u3059\u308B",
674
+ inputSchema: {
675
+ type: "object",
676
+ properties: {
677
+ branchName: {
678
+ type: "string",
679
+ description: "\u4F5C\u6210\u3059\u308B\u30D6\u30E9\u30F3\u30C1\u540D"
680
+ },
681
+ baseBranch: {
682
+ type: "string",
683
+ description: "\u30D9\u30FC\u30B9\u30D6\u30E9\u30F3\u30C1\uFF08\u7701\u7565\u6642\u306F\u73FE\u5728\u306E\u30D6\u30E9\u30F3\u30C1\uFF09"
684
+ }
685
+ },
686
+ required: ["branchName"]
687
+ }
688
+ },
689
+ {
690
+ name: "list_orchestra_members",
691
+ description: "\u3059\u3079\u3066\u306E\u6F14\u594F\u8005\uFF08Git worktree\uFF09\u3092\u4E00\u89A7\u8868\u793A",
692
+ inputSchema: {
693
+ type: "object",
694
+ properties: {}
695
+ }
696
+ },
697
+ {
698
+ name: "delete_orchestra_member",
699
+ description: "\u6F14\u594F\u8005\uFF08Git worktree\uFF09\u3092\u89E3\u6563",
700
+ inputSchema: {
701
+ type: "object",
702
+ properties: {
703
+ branchName: {
704
+ type: "string",
705
+ description: "\u524A\u9664\u3059\u308B\u30D6\u30E9\u30F3\u30C1\u540D"
706
+ },
707
+ force: {
708
+ type: "boolean",
709
+ description: "\u5F37\u5236\u524A\u9664\u30D5\u30E9\u30B0"
710
+ }
711
+ },
712
+ required: ["branchName"]
713
+ }
714
+ },
715
+ {
716
+ name: "exec_in_orchestra_member",
717
+ description: "\u6F14\u594F\u8005\u3067\u30B3\u30DE\u30F3\u30C9\u3092\u5B9F\u884C",
718
+ inputSchema: {
719
+ type: "object",
720
+ properties: {
721
+ branchName: {
722
+ type: "string",
723
+ description: "\u5B9F\u884C\u5BFE\u8C61\u306E\u30D6\u30E9\u30F3\u30C1\u540D"
724
+ },
725
+ command: {
726
+ type: "string",
727
+ description: "\u5B9F\u884C\u3059\u308B\u30B3\u30DE\u30F3\u30C9"
728
+ }
729
+ },
730
+ required: ["branchName", "command"]
731
+ }
732
+ }
733
+ ];
734
+ if (!isTestEnvironment) {
735
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
736
+ return {
737
+ tools: TOOLS
738
+ };
739
+ });
740
+ }
741
+ if (!isTestEnvironment) {
742
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
743
+ const { name, arguments: args } = request.params;
744
+ try {
745
+ switch (name) {
746
+ case "create_orchestra_member": {
747
+ const validatedArgs = CreateWorktreeArgsSchema.parse(args);
748
+ const worktreePath = await gitManager.createWorktree(
749
+ validatedArgs.branchName,
750
+ validatedArgs.baseBranch
751
+ );
752
+ return {
753
+ content: [
754
+ {
755
+ type: "text",
756
+ text: `\u2705 \u6F14\u594F\u8005 '${validatedArgs.branchName}' \u3092\u62DB\u96C6\u3057\u307E\u3057\u305F\uFF01
757
+ \u{1F4C1} ${worktreePath}`
758
+ }
759
+ ]
760
+ };
761
+ }
762
+ case "list_orchestra_members": {
763
+ const worktrees = await gitManager.listWorktrees();
764
+ const orchestraMembers = worktrees.filter((wt) => !wt.path.endsWith("."));
765
+ const list = orchestraMembers.map((wt) => {
766
+ const branchName = wt.branch?.replace("refs/heads/", "") || wt.branch;
767
+ return `\u2022 ${branchName} (${wt.path})`;
768
+ }).join("\n");
769
+ return {
770
+ content: [
771
+ {
772
+ type: "text",
773
+ text: orchestraMembers.length > 0 ? `\u{1F3BC} \u30AA\u30FC\u30B1\u30B9\u30C8\u30E9\u7DE8\u6210(worktree):
774
+ ${list}
775
+
776
+ \u5408\u8A08: ${orchestraMembers.length} \u540D\u306E\u6F14\u594F\u8005` : "\u6F14\u594F\u8005\u304C\u5B58\u5728\u3057\u307E\u305B\u3093"
777
+ }
778
+ ]
779
+ };
780
+ }
781
+ case "delete_orchestra_member": {
782
+ const validatedArgs = DeleteWorktreeArgsSchema.parse(args);
783
+ await gitManager.deleteWorktree(validatedArgs.branchName, validatedArgs.force);
784
+ return {
785
+ content: [
786
+ {
787
+ type: "text",
788
+ text: `\u2705 \u6F14\u594F\u8005 '${validatedArgs.branchName}' \u3092\u89E3\u6563\u3057\u307E\u3057\u305F`
789
+ }
790
+ ]
791
+ };
792
+ }
793
+ case "exec_in_orchestra_member": {
794
+ const validatedArgs = ExecInWorktreeArgsSchema.parse(args);
795
+ const { execa } = await import("execa");
796
+ const worktrees = await gitManager.listWorktrees();
797
+ const targetWorktree = worktrees.find((wt) => {
798
+ const branch = wt.branch?.replace("refs/heads/", "");
799
+ return branch === validatedArgs.branchName || wt.branch === validatedArgs.branchName;
800
+ });
801
+ if (!targetWorktree) {
802
+ throw new Error(`\u6F14\u594F\u8005 '${validatedArgs.branchName}' \u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093`);
803
+ }
804
+ const result = await execa("sh", ["-c", validatedArgs.command], {
805
+ cwd: targetWorktree.path
806
+ });
807
+ return {
808
+ content: [
809
+ {
810
+ type: "text",
811
+ text: `\u{1F4CD} ${validatedArgs.branchName} \u3067\u5B9F\u884C: ${validatedArgs.command}
812
+
813
+ ${result.stdout}`
814
+ }
815
+ ]
816
+ };
817
+ }
818
+ default:
819
+ throw new Error(`\u4E0D\u660E\u306A\u30C4\u30FC\u30EB: ${name}`);
820
+ }
821
+ } catch (error) {
822
+ return {
823
+ content: [
824
+ {
825
+ type: "text",
826
+ text: `\u274C \u30A8\u30E9\u30FC: ${error instanceof Error ? error.message : "\u4E0D\u660E\u306A\u30A8\u30E9\u30FC"}`
827
+ }
828
+ ]
829
+ };
830
+ }
831
+ });
832
+ }
833
+ async function main() {
834
+ const transport = new StdioServerTransport();
835
+ await server.connect(transport);
836
+ console.error("\u{1F3BC} Maestro MCP server started");
837
+ }
838
+ if (!isTestEnvironment) {
839
+ main().catch((error) => {
840
+ console.error("Fatal error:", error);
841
+ process.exit(1);
842
+ });
843
+ }
844
+ export {
845
+ server
846
+ };
847
+ //# sourceMappingURL=server.js.map