@akiojin/gwt 4.8.0 → 4.9.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akiojin/gwt",
3
- "version": "4.8.0",
3
+ "version": "4.9.0",
4
4
  "description": "Interactive Git worktree manager for Claude Code with graphical branch selection",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -640,7 +640,7 @@ export const BranchListScreen = React.memo(function BranchListScreen({
640
640
  )}
641
641
  </Box>
642
642
 
643
- {/* Tool Status - FR-019, FR-021 */}
643
+ {/* Tool Status - FR-019, FR-021, FR-022 */}
644
644
  {toolStatuses && toolStatuses.length > 0 && (
645
645
  <Box>
646
646
  <Text dimColor>Tools: </Text>
@@ -648,7 +648,9 @@ export const BranchListScreen = React.memo(function BranchListScreen({
648
648
  <React.Fragment key={tool.id}>
649
649
  <Text>{tool.name}: </Text>
650
650
  <Text color={tool.status === "installed" ? "green" : "yellow"}>
651
- {tool.status}
651
+ {tool.status === "installed" && tool.version
652
+ ? tool.version
653
+ : tool.status}
652
654
  </Text>
653
655
  {index < toolStatuses.length - 1 && <Text dimColor> | </Text>}
654
656
  </React.Fragment>
@@ -52,11 +52,8 @@ const DEFAULT_CONFIG: AppConfig = {
52
52
  export async function loadConfig(): Promise<AppConfig> {
53
53
  const configPaths = [
54
54
  path.join(process.cwd(), ".gwt.json"),
55
- path.join(process.cwd(), ".claude-worktree.json"), // 後方互換性
56
55
  path.join(homedir(), ".config", "gwt", "config.json"),
57
- path.join(homedir(), ".config", "claude-worktree", "config.json"), // 後方互換性
58
56
  path.join(homedir(), ".gwt.json"),
59
- path.join(homedir(), ".claude-worktree.json"), // 後方互換性
60
57
  ];
61
58
 
62
59
  for (const configPath of configPaths) {
@@ -79,10 +76,9 @@ export async function loadConfig(): Promise<AppConfig> {
79
76
  return {
80
77
  ...DEFAULT_CONFIG,
81
78
  defaultBaseBranch:
82
- process.env.CLAUDE_WORKTREE_BASE_BRANCH ||
83
- DEFAULT_CONFIG.defaultBaseBranch,
84
- skipPermissions: process.env.CLAUDE_WORKTREE_SKIP_PERMISSIONS === "true",
85
- enableGitHubIntegration: process.env.CLAUDE_WORKTREE_GITHUB !== "false",
79
+ process.env.GWT_BASE_BRANCH || DEFAULT_CONFIG.defaultBaseBranch,
80
+ skipPermissions: process.env.GWT_SKIP_PERMISSIONS === "true",
81
+ enableGitHubIntegration: process.env.GWT_GITHUB !== "false",
86
82
  enableDebugMode:
87
83
  process.env.DEBUG_CLEANUP === "true" || process.env.DEBUG === "true",
88
84
  };
@@ -29,16 +29,13 @@ import {
29
29
  /**
30
30
  * 設定ディレクトリのパスを取得
31
31
  *
32
- * 環境変数の優先順位: GWT_HOME > CLAUDE_WORKTREE_HOME (後方互換性) > ホームディレクトリ
32
+ * 環境変数 GWT_HOME が設定されている場合はそれを使用、それ以外はホームディレクトリ
33
33
  */
34
34
  function getConfigDir(): string {
35
35
  const worktreeHome =
36
36
  process.env.GWT_HOME && process.env.GWT_HOME.trim().length > 0
37
37
  ? process.env.GWT_HOME
38
- : process.env.CLAUDE_WORKTREE_HOME &&
39
- process.env.CLAUDE_WORKTREE_HOME.trim().length > 0
40
- ? process.env.CLAUDE_WORKTREE_HOME
41
- : homedir();
38
+ : homedir();
42
39
  return path.join(worktreeHome, ".gwt");
43
40
  }
44
41
 
@@ -133,11 +130,11 @@ async function mutateProfiles(
133
130
  }
134
131
 
135
132
  /**
136
- * プロファイル設定ファイルのパス(後方互換性のためエクスポート)
133
+ * プロファイル設定ファイルのパス
137
134
  *
138
135
  * @deprecated 内部では getProfilesConfigPath() を使用してください。
139
136
  * この定数はモジュールロード時に評価されるため、
140
- * 実行中に環境変数(GWT_HOME/CLAUDE_WORKTREE_HOME)を変更しても反映されません。
137
+ * 実行中に環境変数(GWT_HOME)を変更しても反映されません。
141
138
  */
142
139
  export const PROFILES_CONFIG_PATH = getProfilesConfigPath();
143
140
 
@@ -7,14 +7,7 @@
7
7
 
8
8
  import { homedir } from "node:os";
9
9
  import path from "node:path";
10
- import {
11
- readFile,
12
- writeFile,
13
- mkdir,
14
- rename,
15
- access,
16
- cp,
17
- } from "node:fs/promises";
10
+ import { readFile, writeFile, mkdir, rename } from "node:fs/promises";
18
11
  import type {
19
12
  ToolsConfig,
20
13
  CustomAITool,
@@ -28,60 +21,17 @@ const logger = createLogger({ category: "config" });
28
21
 
29
22
  /**
30
23
  * ツール設定ファイルのパス
31
- * 環境変数の優先順位: GWT_HOME > CLAUDE_WORKTREE_HOME (後方互換性) > ホームディレクトリ
24
+ * 環境変数 GWT_HOME が設定されている場合はそれを使用、それ以外はホームディレクトリ
32
25
  */
33
26
  export const WORKTREE_HOME =
34
27
  process.env.GWT_HOME && process.env.GWT_HOME.trim().length > 0
35
28
  ? process.env.GWT_HOME
36
- : process.env.CLAUDE_WORKTREE_HOME &&
37
- process.env.CLAUDE_WORKTREE_HOME.trim().length > 0
38
- ? process.env.CLAUDE_WORKTREE_HOME
39
- : homedir();
29
+ : homedir();
40
30
 
41
- const LEGACY_CONFIG_DIR = path.join(homedir(), ".claude-worktree");
42
31
  export const CONFIG_DIR = path.join(WORKTREE_HOME, ".gwt");
43
32
  export const TOOLS_CONFIG_PATH = path.join(CONFIG_DIR, "tools.json");
44
33
  const TEMP_CONFIG_PATH = `${TOOLS_CONFIG_PATH}.tmp`;
45
34
 
46
- /**
47
- * レガシー設定ディレクトリから新しいディレクトリへ移行
48
- */
49
- async function migrateLegacyConfig(): Promise<void> {
50
- try {
51
- // 新しいディレクトリが既に存在する場合は移行不要
52
- try {
53
- await access(CONFIG_DIR);
54
- return;
55
- } catch {
56
- // 新しいディレクトリが存在しない場合は続行
57
- }
58
-
59
- // レガシーディレクトリの存在を確認
60
- try {
61
- await access(LEGACY_CONFIG_DIR);
62
- } catch {
63
- // レガシーディレクトリも存在しない場合は移行不要
64
- return;
65
- }
66
-
67
- // レガシーディレクトリを新しいディレクトリにコピー
68
- await mkdir(path.dirname(CONFIG_DIR), { recursive: true });
69
- await cp(LEGACY_CONFIG_DIR, CONFIG_DIR, { recursive: true });
70
- logger.info(
71
- { from: LEGACY_CONFIG_DIR, to: CONFIG_DIR },
72
- "Legacy config migrated",
73
- );
74
- console.log(
75
- `✅ Migrated configuration from ${LEGACY_CONFIG_DIR} to ${CONFIG_DIR}`,
76
- );
77
- } catch (error) {
78
- // 移行に失敗しても継続(エラーログのみ)
79
- if (process.env.DEBUG) {
80
- console.error("Failed to migrate legacy config:", error);
81
- }
82
- }
83
- }
84
-
85
35
  const DEFAULT_CONFIG: ToolsConfig = {
86
36
  version: "1.0.0",
87
37
  env: {},
@@ -98,9 +48,6 @@ const DEFAULT_CONFIG: ToolsConfig = {
98
48
  * @throws JSON構文エラー時
99
49
  */
100
50
  export async function loadToolsConfig(): Promise<ToolsConfig> {
101
- // 最初の呼び出し時にレガシー設定の移行を試行
102
- await migrateLegacyConfig();
103
-
104
51
  try {
105
52
  const content = await readFile(TOOLS_CONFIG_PATH, "utf-8");
106
53
  const config = JSON.parse(content) as ToolsConfig;
@@ -59,6 +59,7 @@ export interface CommandLookupResult {
59
59
  available: boolean;
60
60
  path: string | null;
61
61
  source: "installed" | "bunx";
62
+ version?: string | null;
62
63
  }
63
64
 
64
65
  /**
@@ -69,6 +70,7 @@ export interface ToolStatus {
69
70
  name: string;
70
71
  status: "installed" | "bunx";
71
72
  path: string | null;
73
+ version?: string | null;
72
74
  }
73
75
 
74
76
  /**
@@ -85,6 +87,33 @@ export function clearCommandLookupCache(): void {
85
87
  commandLookupCache.clear();
86
88
  }
87
89
 
90
+ /**
91
+ * Gets the version of a command by running it with --version.
92
+ * FR-022: Returns version in "v{version}" format, null on failure.
93
+ * FR-023: Times out after 3 seconds to minimize startup delay.
94
+ *
95
+ * @param commandPath - Full path to the command
96
+ * @returns Version string (e.g., "v1.0.3") or null on failure
97
+ */
98
+ export async function getCommandVersion(
99
+ commandPath: string,
100
+ ): Promise<string | null> {
101
+ try {
102
+ const result = await execa(commandPath, ["--version"], {
103
+ timeout: 3000,
104
+ stdin: "ignore",
105
+ stdout: "pipe",
106
+ stderr: "ignore",
107
+ });
108
+ // Extract version number from output
109
+ // Examples: "claude 1.0.3", "codex 0.77.0", "gemini 0.1.0"
110
+ const match = result.stdout.match(/(\d+\.\d+(?:\.\d+)?(?:-[\w.]+)?)/);
111
+ return match ? `v${match[1]}` : null;
112
+ } catch {
113
+ return null;
114
+ }
115
+ }
116
+
88
117
  /**
89
118
  * Finds a command by checking PATH first, then fallback paths.
90
119
  * Results are cached for the lifetime of the process (FR-020).
@@ -141,6 +170,13 @@ export async function findCommand(
141
170
  lookupResult = { available: true, path: null, source: "bunx" };
142
171
  }
143
172
 
173
+ // Step 4: Get version for installed commands (FR-022)
174
+ if (lookupResult.source === "installed" && lookupResult.path) {
175
+ lookupResult.version = await getCommandVersion(lookupResult.path);
176
+ } else {
177
+ lookupResult.version = null;
178
+ }
179
+
144
180
  // Cache the result (FR-020)
145
181
  commandLookupCache.set(commandName, lookupResult);
146
182
 
@@ -181,6 +217,7 @@ export async function detectAllToolStatuses(): Promise<ToolStatus[]> {
181
217
  name: tool.displayName,
182
218
  status: result.source,
183
219
  path: result.path,
220
+ version: result.version ?? null,
184
221
  };
185
222
  }),
186
223
  );
@@ -275,8 +275,8 @@ export function ConfigPage() {
275
275
  </p>
276
276
  <h3 className="mt-1 text-lg font-semibold">登録済みツール</h3>
277
277
  <p className="mt-2 text-sm text-muted-foreground">
278
- CLI と Web UI は同じ設定を参照します。更新すると
279
- ~/.claude-worktree/tools.json に保存されます。
278
+ CLI と Web UI は同じ設定を参照します。更新すると ~/.gwt/tools.json
279
+ に保存されます。
280
280
  </p>
281
281
  </CardHeader>
282
282
  <CardContent>