@akiojin/gwt 4.5.0 → 4.6.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/dist/claude.d.ts +1 -2
- package/dist/claude.d.ts.map +1 -1
- package/dist/claude.js +9 -30
- package/dist/claude.js.map +1 -1
- package/dist/cli/ui/components/App.d.ts.map +1 -1
- package/dist/cli/ui/components/App.js +4 -1
- package/dist/cli/ui/components/App.js.map +1 -1
- package/dist/cli/ui/components/screens/BranchListScreen.d.ts +7 -1
- package/dist/cli/ui/components/screens/BranchListScreen.d.ts.map +1 -1
- package/dist/cli/ui/components/screens/BranchListScreen.js +19 -9
- package/dist/cli/ui/components/screens/BranchListScreen.js.map +1 -1
- package/dist/cli/ui/hooks/useToolStatus.d.ts +30 -0
- package/dist/cli/ui/hooks/useToolStatus.d.ts.map +1 -0
- package/dist/cli/ui/hooks/useToolStatus.js +49 -0
- package/dist/cli/ui/hooks/useToolStatus.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/utils/command.d.ts +42 -1
- package/dist/utils/command.d.ts.map +1 -1
- package/dist/utils/command.js +130 -11
- package/dist/utils/command.js.map +1 -1
- package/package.json +1 -1
- package/src/claude.ts +13 -38
- package/src/cli/ui/components/App.tsx +5 -0
- package/src/cli/ui/components/screens/BranchListScreen.tsx +36 -10
- package/src/cli/ui/hooks/useToolStatus.ts +68 -0
- package/src/index.ts +1 -0
- package/src/utils/command.ts +174 -12
package/src/utils/command.ts
CHANGED
|
@@ -1,26 +1,188 @@
|
|
|
1
1
|
import { execa } from "execa";
|
|
2
|
+
import { existsSync } from "fs";
|
|
3
|
+
import { homedir } from "os";
|
|
4
|
+
import { join } from "path";
|
|
2
5
|
|
|
3
6
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
|
|
7
|
+
* Known installation paths for common AI CLI tools.
|
|
8
|
+
* These are checked as fallback when `which`/`where` fails.
|
|
9
|
+
*/
|
|
10
|
+
const KNOWN_INSTALL_PATHS: Record<string, { unix: string[]; win32: string[] }> =
|
|
11
|
+
{
|
|
12
|
+
claude: {
|
|
13
|
+
unix: [
|
|
14
|
+
join(homedir(), ".bun", "bin", "claude"),
|
|
15
|
+
join(homedir(), ".local", "bin", "claude"),
|
|
16
|
+
"/usr/local/bin/claude",
|
|
17
|
+
],
|
|
18
|
+
win32: [
|
|
19
|
+
join(
|
|
20
|
+
process.env.LOCALAPPDATA ?? "",
|
|
21
|
+
"Programs",
|
|
22
|
+
"claude",
|
|
23
|
+
"claude.exe",
|
|
24
|
+
),
|
|
25
|
+
join(homedir(), ".bun", "bin", "claude.exe"),
|
|
26
|
+
],
|
|
27
|
+
},
|
|
28
|
+
codex: {
|
|
29
|
+
unix: [
|
|
30
|
+
join(homedir(), ".bun", "bin", "codex"),
|
|
31
|
+
join(homedir(), ".local", "bin", "codex"),
|
|
32
|
+
"/usr/local/bin/codex",
|
|
33
|
+
],
|
|
34
|
+
win32: [join(homedir(), ".bun", "bin", "codex.exe")],
|
|
35
|
+
},
|
|
36
|
+
gemini: {
|
|
37
|
+
unix: [
|
|
38
|
+
join(homedir(), ".bun", "bin", "gemini"),
|
|
39
|
+
join(homedir(), ".local", "bin", "gemini"),
|
|
40
|
+
"/usr/local/bin/gemini",
|
|
41
|
+
],
|
|
42
|
+
win32: [join(homedir(), ".bun", "bin", "gemini.exe")],
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Builtin AI tools with their command names and display names.
|
|
48
|
+
*/
|
|
49
|
+
const BUILTIN_TOOLS = [
|
|
50
|
+
{ id: "claude-code", commandName: "claude", displayName: "Claude" },
|
|
51
|
+
{ id: "codex-cli", commandName: "codex", displayName: "Codex" },
|
|
52
|
+
{ id: "gemini-cli", commandName: "gemini", displayName: "Gemini" },
|
|
53
|
+
] as const;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Result of command lookup.
|
|
57
|
+
*/
|
|
58
|
+
export interface CommandLookupResult {
|
|
59
|
+
available: boolean;
|
|
60
|
+
path: string | null;
|
|
61
|
+
source: "installed" | "bunx";
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Tool status information for display.
|
|
66
|
+
*/
|
|
67
|
+
export interface ToolStatus {
|
|
68
|
+
id: string;
|
|
69
|
+
name: string;
|
|
70
|
+
status: "installed" | "bunx";
|
|
71
|
+
path: string | null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Module-level cache for command lookup results.
|
|
76
|
+
* This cache persists for the lifetime of the process (FR-020).
|
|
77
|
+
*/
|
|
78
|
+
const commandLookupCache = new Map<string, CommandLookupResult>();
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Clears the command lookup cache.
|
|
82
|
+
* Primarily for testing purposes.
|
|
83
|
+
*/
|
|
84
|
+
export function clearCommandLookupCache(): void {
|
|
85
|
+
commandLookupCache.clear();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Finds a command by checking PATH first, then fallback paths.
|
|
90
|
+
* Results are cached for the lifetime of the process (FR-020).
|
|
7
91
|
*
|
|
8
|
-
* @param commandName - Command name to look up (e.g. `claude`, `
|
|
9
|
-
* @returns
|
|
92
|
+
* @param commandName - Command name to look up (e.g. `claude`, `codex`, `gemini`)
|
|
93
|
+
* @returns CommandLookupResult with availability, path, and source
|
|
10
94
|
*/
|
|
11
|
-
export async function
|
|
95
|
+
export async function findCommand(
|
|
12
96
|
commandName: string,
|
|
13
|
-
): Promise<
|
|
97
|
+
): Promise<CommandLookupResult> {
|
|
98
|
+
// Check cache first (FR-020: 再検出を行わない)
|
|
99
|
+
const cached = commandLookupCache.get(commandName);
|
|
100
|
+
if (cached) {
|
|
101
|
+
return cached;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
let lookupResult: CommandLookupResult | null = null;
|
|
105
|
+
|
|
106
|
+
// Step 1: Try standard which/where lookup
|
|
14
107
|
try {
|
|
15
|
-
const
|
|
16
|
-
await execa(
|
|
108
|
+
const lookupCommand = process.platform === "win32" ? "where" : "which";
|
|
109
|
+
const execResult = await execa(lookupCommand, [commandName], {
|
|
17
110
|
shell: true,
|
|
18
111
|
stdin: "ignore",
|
|
19
|
-
stdout: "
|
|
112
|
+
stdout: "pipe",
|
|
20
113
|
stderr: "ignore",
|
|
21
114
|
});
|
|
22
|
-
|
|
115
|
+
const foundPath = execResult.stdout.trim().split("\n")[0];
|
|
116
|
+
if (foundPath) {
|
|
117
|
+
lookupResult = { available: true, path: foundPath, source: "installed" };
|
|
118
|
+
}
|
|
23
119
|
} catch {
|
|
24
|
-
|
|
120
|
+
// which/where failed, try fallback paths
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Step 2: Check known installation paths as fallback
|
|
124
|
+
if (!lookupResult) {
|
|
125
|
+
const knownPaths = KNOWN_INSTALL_PATHS[commandName];
|
|
126
|
+
if (knownPaths) {
|
|
127
|
+
const pathsToCheck =
|
|
128
|
+
process.platform === "win32" ? knownPaths.win32 : knownPaths.unix;
|
|
129
|
+
|
|
130
|
+
for (const p of pathsToCheck) {
|
|
131
|
+
if (p && existsSync(p)) {
|
|
132
|
+
lookupResult = { available: true, path: p, source: "installed" };
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Step 3: Fall back to bunx (always available for known tools)
|
|
140
|
+
if (!lookupResult) {
|
|
141
|
+
lookupResult = { available: true, path: null, source: "bunx" };
|
|
25
142
|
}
|
|
143
|
+
|
|
144
|
+
// Cache the result (FR-020)
|
|
145
|
+
commandLookupCache.set(commandName, lookupResult);
|
|
146
|
+
|
|
147
|
+
return lookupResult;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Checks whether a command is available in the current PATH.
|
|
152
|
+
*
|
|
153
|
+
* Uses `where` on Windows and `which` on other platforms.
|
|
154
|
+
* If the standard lookup fails, checks known installation paths
|
|
155
|
+
* as a fallback for common tools.
|
|
156
|
+
*
|
|
157
|
+
* @param commandName - Command name to look up (e.g. `claude`, `npx`, `gemini`)
|
|
158
|
+
* @returns true if the command exists in PATH or known paths
|
|
159
|
+
*/
|
|
160
|
+
export async function isCommandAvailable(
|
|
161
|
+
commandName: string,
|
|
162
|
+
): Promise<boolean> {
|
|
163
|
+
const result = await findCommand(commandName);
|
|
164
|
+
return result.available;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Detects installation status for all builtin AI tools.
|
|
169
|
+
*
|
|
170
|
+
* This function is designed to be called once at application startup
|
|
171
|
+
* and cached for the duration of the session.
|
|
172
|
+
*
|
|
173
|
+
* @returns Array of ToolStatus for all builtin tools
|
|
174
|
+
*/
|
|
175
|
+
export async function detectAllToolStatuses(): Promise<ToolStatus[]> {
|
|
176
|
+
const results = await Promise.all(
|
|
177
|
+
BUILTIN_TOOLS.map(async (tool) => {
|
|
178
|
+
const result = await findCommand(tool.commandName);
|
|
179
|
+
return {
|
|
180
|
+
id: tool.id,
|
|
181
|
+
name: tool.displayName,
|
|
182
|
+
status: result.source,
|
|
183
|
+
path: result.path,
|
|
184
|
+
};
|
|
185
|
+
}),
|
|
186
|
+
);
|
|
187
|
+
return results;
|
|
26
188
|
}
|