@akiojin/gwt 4.9.0 → 4.10.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.
Files changed (180) hide show
  1. package/README.ja.md +58 -34
  2. package/README.md +18 -34
  3. package/dist/cli/ui/components/App.d.ts +2 -2
  4. package/dist/cli/ui/components/App.d.ts.map +1 -1
  5. package/dist/cli/ui/components/App.js +27 -9
  6. package/dist/cli/ui/components/App.js.map +1 -1
  7. package/dist/cli/ui/components/screens/BranchQuickStartScreen.d.ts +1 -1
  8. package/dist/cli/ui/components/screens/BranchQuickStartScreen.d.ts.map +1 -1
  9. package/dist/cli/ui/components/screens/BranchQuickStartScreen.js +4 -1
  10. package/dist/cli/ui/components/screens/BranchQuickStartScreen.js.map +1 -1
  11. package/dist/cli/ui/components/screens/CodingAgentSelectorScreen.d.ts +27 -0
  12. package/dist/cli/ui/components/screens/CodingAgentSelectorScreen.d.ts.map +1 -0
  13. package/dist/cli/ui/components/screens/{AIToolSelectorScreen.js → CodingAgentSelectorScreen.js} +35 -35
  14. package/dist/cli/ui/components/screens/CodingAgentSelectorScreen.js.map +1 -0
  15. package/dist/cli/ui/components/screens/ModelSelectorScreen.d.ts +2 -2
  16. package/dist/cli/ui/components/screens/ModelSelectorScreen.d.ts.map +1 -1
  17. package/dist/cli/ui/components/screens/ModelSelectorScreen.js.map +1 -1
  18. package/dist/cli/ui/types.d.ts +2 -2
  19. package/dist/cli/ui/types.d.ts.map +1 -1
  20. package/dist/cli/ui/utils/modelOptions.d.ts +4 -4
  21. package/dist/cli/ui/utils/modelOptions.d.ts.map +1 -1
  22. package/dist/cli/ui/utils/modelOptions.js.map +1 -1
  23. package/dist/client/assets/{index-PqK9jkug.js → index-LNPtOrn3.js} +17 -17
  24. package/dist/client/index.html +1 -1
  25. package/dist/config/builtin-coding-agents.d.ts +27 -0
  26. package/dist/config/builtin-coding-agents.d.ts.map +1 -0
  27. package/dist/config/{builtin-tools.js → builtin-coding-agents.js} +21 -6
  28. package/dist/config/builtin-coding-agents.js.map +1 -0
  29. package/dist/config/tools.d.ts +16 -16
  30. package/dist/config/tools.d.ts.map +1 -1
  31. package/dist/config/tools.js +92 -78
  32. package/dist/config/tools.js.map +1 -1
  33. package/dist/index.js +19 -19
  34. package/dist/index.js.map +1 -1
  35. package/dist/launcher.d.ts +8 -8
  36. package/dist/launcher.d.ts.map +1 -1
  37. package/dist/launcher.js +32 -28
  38. package/dist/launcher.js.map +1 -1
  39. package/dist/services/codingAgentCommandResolver.d.ts +10 -0
  40. package/dist/services/codingAgentCommandResolver.d.ts.map +1 -0
  41. package/dist/services/{customToolResolver.js → codingAgentCommandResolver.js} +25 -20
  42. package/dist/services/codingAgentCommandResolver.js.map +1 -0
  43. package/dist/services/{aiToolResolver.d.ts → codingAgentResolver.d.ts} +6 -6
  44. package/dist/services/codingAgentResolver.d.ts.map +1 -0
  45. package/dist/services/{aiToolResolver.js → codingAgentResolver.js} +23 -23
  46. package/dist/services/codingAgentResolver.js.map +1 -0
  47. package/dist/shared/{aiToolConstants.d.ts → codingAgentConstants.d.ts} +2 -2
  48. package/dist/shared/codingAgentConstants.d.ts.map +1 -0
  49. package/dist/shared/{aiToolConstants.js → codingAgentConstants.js} +2 -2
  50. package/dist/shared/codingAgentConstants.js.map +1 -0
  51. package/dist/types/api.d.ts +12 -12
  52. package/dist/types/api.d.ts.map +1 -1
  53. package/dist/types/tools.d.ts +30 -30
  54. package/dist/types/tools.d.ts.map +1 -1
  55. package/dist/types/tools.js +1 -1
  56. package/dist/utils/command.d.ts.map +1 -1
  57. package/dist/utils/command.js +9 -0
  58. package/dist/utils/command.js.map +1 -1
  59. package/dist/utils/session/index.d.ts +5 -3
  60. package/dist/utils/session/index.d.ts.map +1 -1
  61. package/dist/utils/session/index.js +5 -2
  62. package/dist/utils/session/index.js.map +1 -1
  63. package/dist/utils/session/parsers/index.d.ts +1 -0
  64. package/dist/utils/session/parsers/index.d.ts.map +1 -1
  65. package/dist/utils/session/parsers/index.js +2 -0
  66. package/dist/utils/session/parsers/index.js.map +1 -1
  67. package/dist/utils/session/parsers/opencode.d.ts +23 -0
  68. package/dist/utils/session/parsers/opencode.d.ts.map +1 -0
  69. package/dist/utils/session/parsers/opencode.js +89 -0
  70. package/dist/utils/session/parsers/opencode.js.map +1 -0
  71. package/dist/utils/session/types.d.ts +4 -0
  72. package/dist/utils/session/types.d.ts.map +1 -1
  73. package/dist/web/client/src/components/CodingAgentLaunchModal.d.ts +9 -0
  74. package/dist/web/client/src/components/CodingAgentLaunchModal.d.ts.map +1 -0
  75. package/dist/web/client/src/components/{AIToolLaunchModal.js → CodingAgentLaunchModal.js} +58 -58
  76. package/dist/web/client/src/components/CodingAgentLaunchModal.js.map +1 -0
  77. package/dist/web/client/src/components/CustomCodingAgentForm.d.ts +23 -0
  78. package/dist/web/client/src/components/CustomCodingAgentForm.d.ts.map +1 -0
  79. package/dist/web/client/src/components/{CustomToolForm.js → CustomCodingAgentForm.js} +5 -5
  80. package/dist/web/client/src/components/CustomCodingAgentForm.js.map +1 -0
  81. package/dist/web/client/src/components/CustomCodingAgentList.d.ts +10 -0
  82. package/dist/web/client/src/components/CustomCodingAgentList.d.ts.map +1 -0
  83. package/dist/web/client/src/components/{CustomToolList.js → CustomCodingAgentList.js} +17 -17
  84. package/dist/web/client/src/components/CustomCodingAgentList.js.map +1 -0
  85. package/dist/web/client/src/components/branch-detail/SessionHistoryTable.d.ts +2 -2
  86. package/dist/web/client/src/components/branch-detail/SessionHistoryTable.d.ts.map +1 -1
  87. package/dist/web/client/src/components/branch-detail/SessionHistoryTable.js +6 -6
  88. package/dist/web/client/src/components/branch-detail/SessionHistoryTable.js.map +1 -1
  89. package/dist/web/client/src/components/branch-detail/ToolLauncher.d.ts +2 -2
  90. package/dist/web/client/src/components/branch-detail/ToolLauncher.d.ts.map +1 -1
  91. package/dist/web/client/src/components/branch-detail/ToolLauncher.js +5 -5
  92. package/dist/web/client/src/components/branch-detail/ToolLauncher.js.map +1 -1
  93. package/dist/web/client/src/hooks/useSessions.d.ts +4 -4
  94. package/dist/web/client/src/hooks/useSessions.d.ts.map +1 -1
  95. package/dist/web/client/src/hooks/useSessions.js.map +1 -1
  96. package/dist/web/client/src/lib/api.d.ts +5 -5
  97. package/dist/web/client/src/lib/api.d.ts.map +1 -1
  98. package/dist/web/client/src/lib/api.js +1 -1
  99. package/dist/web/client/src/lib/api.js.map +1 -1
  100. package/dist/web/client/src/pages/BranchDetailPage.js +24 -24
  101. package/dist/web/client/src/pages/BranchDetailPage.js.map +1 -1
  102. package/dist/web/client/src/pages/ConfigManagementPage.d.ts.map +1 -1
  103. package/dist/web/client/src/pages/ConfigManagementPage.js +15 -15
  104. package/dist/web/client/src/pages/ConfigManagementPage.js.map +1 -1
  105. package/dist/web/client/src/pages/ConfigPage.d.ts.map +1 -1
  106. package/dist/web/client/src/pages/ConfigPage.js +43 -39
  107. package/dist/web/client/src/pages/ConfigPage.js.map +1 -1
  108. package/dist/web/server/env/importer.d.ts.map +1 -1
  109. package/dist/web/server/env/importer.js +3 -3
  110. package/dist/web/server/env/importer.js.map +1 -1
  111. package/dist/web/server/pty/manager.d.ts +6 -6
  112. package/dist/web/server/pty/manager.d.ts.map +1 -1
  113. package/dist/web/server/pty/manager.js +11 -11
  114. package/dist/web/server/pty/manager.js.map +1 -1
  115. package/dist/web/server/routes/config.d.ts.map +1 -1
  116. package/dist/web/server/routes/config.js +34 -34
  117. package/dist/web/server/routes/config.js.map +1 -1
  118. package/dist/web/server/routes/sessions.d.ts +1 -1
  119. package/dist/web/server/routes/sessions.d.ts.map +1 -1
  120. package/dist/web/server/routes/sessions.js +20 -20
  121. package/dist/web/server/routes/sessions.js.map +1 -1
  122. package/package.json +2 -2
  123. package/src/cli/ui/__tests__/components/screens/{AIToolSelectorScreen.test.tsx → CodingAgentSelectorScreen.test.tsx} +38 -38
  124. package/src/cli/ui/components/App.tsx +44 -20
  125. package/src/cli/ui/components/screens/BranchQuickStartScreen.tsx +6 -3
  126. package/src/cli/ui/components/screens/CodingAgentSelectorScreen.tsx +159 -0
  127. package/src/cli/ui/components/screens/ModelSelectorScreen.tsx +6 -2
  128. package/src/cli/ui/types.ts +2 -2
  129. package/src/cli/ui/utils/modelOptions.ts +6 -4
  130. package/src/config/{builtin-tools.ts → builtin-coding-agents.ts} +25 -9
  131. package/src/config/tools.ts +120 -92
  132. package/src/index.ts +19 -19
  133. package/src/launcher.ts +38 -31
  134. package/src/services/{customToolResolver.ts → codingAgentCommandResolver.ts} +33 -28
  135. package/src/services/{aiToolResolver.ts → codingAgentResolver.ts} +28 -28
  136. package/src/shared/{aiToolConstants.ts → codingAgentConstants.ts} +1 -1
  137. package/src/types/api.ts +12 -12
  138. package/src/types/tools.ts +30 -30
  139. package/src/utils/command.ts +9 -0
  140. package/src/utils/session/index.ts +10 -2
  141. package/src/utils/session/parsers/index.ts +6 -0
  142. package/src/utils/session/parsers/opencode.ts +110 -0
  143. package/src/utils/session/types.ts +5 -0
  144. package/src/web/client/src/components/{AIToolLaunchModal.tsx → CodingAgentLaunchModal.tsx} +74 -70
  145. package/src/web/client/src/components/{CustomToolForm.tsx → CustomCodingAgentForm.tsx} +14 -14
  146. package/src/web/client/src/components/{CustomToolList.tsx → CustomCodingAgentList.tsx} +26 -26
  147. package/src/web/client/src/components/branch-detail/SessionHistoryTable.tsx +7 -7
  148. package/src/web/client/src/components/branch-detail/ToolLauncher.tsx +9 -9
  149. package/src/web/client/src/hooks/useSessions.ts +5 -5
  150. package/src/web/client/src/lib/api.ts +8 -8
  151. package/src/web/client/src/pages/BranchDetailPage.tsx +26 -26
  152. package/src/web/client/src/pages/ConfigManagementPage.tsx +32 -24
  153. package/src/web/client/src/pages/ConfigPage.tsx +55 -49
  154. package/src/web/server/env/importer.ts +6 -3
  155. package/src/web/server/pty/manager.ts +20 -20
  156. package/src/web/server/routes/config.ts +45 -39
  157. package/src/web/server/routes/sessions.ts +29 -26
  158. package/dist/cli/ui/components/screens/AIToolSelectorScreen.d.ts +0 -27
  159. package/dist/cli/ui/components/screens/AIToolSelectorScreen.d.ts.map +0 -1
  160. package/dist/cli/ui/components/screens/AIToolSelectorScreen.js.map +0 -1
  161. package/dist/config/builtin-tools.d.ts +0 -23
  162. package/dist/config/builtin-tools.d.ts.map +0 -1
  163. package/dist/config/builtin-tools.js.map +0 -1
  164. package/dist/services/aiToolResolver.d.ts.map +0 -1
  165. package/dist/services/aiToolResolver.js.map +0 -1
  166. package/dist/services/customToolResolver.d.ts +0 -10
  167. package/dist/services/customToolResolver.d.ts.map +0 -1
  168. package/dist/services/customToolResolver.js.map +0 -1
  169. package/dist/shared/aiToolConstants.d.ts.map +0 -1
  170. package/dist/shared/aiToolConstants.js.map +0 -1
  171. package/dist/web/client/src/components/AIToolLaunchModal.d.ts +0 -9
  172. package/dist/web/client/src/components/AIToolLaunchModal.d.ts.map +0 -1
  173. package/dist/web/client/src/components/AIToolLaunchModal.js.map +0 -1
  174. package/dist/web/client/src/components/CustomToolForm.d.ts +0 -23
  175. package/dist/web/client/src/components/CustomToolForm.d.ts.map +0 -1
  176. package/dist/web/client/src/components/CustomToolForm.js.map +0 -1
  177. package/dist/web/client/src/components/CustomToolList.d.ts +0 -10
  178. package/dist/web/client/src/components/CustomToolList.d.ts.map +0 -1
  179. package/dist/web/client/src/components/CustomToolList.js.map +0 -1
  180. package/src/cli/ui/components/screens/AIToolSelectorScreen.tsx +0 -153
@@ -0,0 +1,159 @@
1
+ import React, { useState, useEffect } from "react";
2
+ import { Box, Text } from "ink";
3
+ import { Header } from "../parts/Header.js";
4
+ import { Footer } from "../parts/Footer.js";
5
+ import { Select } from "../common/Select.js";
6
+ import { useAppInput } from "../../hooks/useAppInput.js";
7
+ import { useTerminalSize } from "../../hooks/useTerminalSize.js";
8
+ import { getAllCodingAgents } from "../../../../config/tools.js";
9
+ import type { CodingAgentConfig } from "../../../../types/tools.js";
10
+ import type { CodingAgentId } from "../../types.js";
11
+
12
+ /**
13
+ * Renderable item for the coding agent selector list.
14
+ */
15
+ export interface CodingAgentItem {
16
+ label: string;
17
+ value: CodingAgentId;
18
+ description: string;
19
+ }
20
+
21
+ /**
22
+ * Props for `CodingAgentSelectorScreen`.
23
+ */
24
+ export interface CodingAgentSelectorScreenProps {
25
+ onBack: () => void;
26
+ onSelect: (agentId: CodingAgentId) => void;
27
+ version?: string | null;
28
+ initialAgentId?: CodingAgentId | null;
29
+ }
30
+
31
+ /**
32
+ * CodingAgentSelectorScreen - Screen for selecting coding agent (Claude Code, Codex CLI, or custom agents)
33
+ * Layout: Header + Agent Selection + Footer
34
+ *
35
+ * This screen dynamically loads available agents from the configuration (builtin + custom).
36
+ */
37
+ export function CodingAgentSelectorScreen({
38
+ onBack,
39
+ onSelect,
40
+ version,
41
+ initialAgentId,
42
+ }: CodingAgentSelectorScreenProps) {
43
+ const { rows } = useTerminalSize();
44
+ const [agentItems, setAgentItems] = useState<CodingAgentItem[]>([]);
45
+ const [isLoading, setIsLoading] = useState(true);
46
+ const [selectedIndex, setSelectedIndex] = useState<number>(0);
47
+
48
+ // Load agents from getAllCodingAgents()
49
+ useEffect(() => {
50
+ const loadAgents = async () => {
51
+ try {
52
+ const agents = await getAllCodingAgents();
53
+
54
+ // Convert CodingAgentConfig[] to CodingAgentItem[]
55
+ const items: CodingAgentItem[] = agents.map(
56
+ (agent: CodingAgentConfig) => {
57
+ // Generate description based on whether it's builtin or custom
58
+ const description = agent.isBuiltin
59
+ ? `Official ${agent.displayName} agent`
60
+ : `Custom coding agent`;
61
+
62
+ // Add icon to label if present
63
+ const label = agent.icon
64
+ ? `${agent.icon} ${agent.displayName}`
65
+ : agent.displayName;
66
+
67
+ return {
68
+ label,
69
+ value: agent.id,
70
+ description,
71
+ };
72
+ },
73
+ );
74
+
75
+ setAgentItems(items);
76
+
77
+ // Decide initial cursor position based on last used agent
78
+ const idx =
79
+ initialAgentId && items.length > 0
80
+ ? items.findIndex((item) => item.value === initialAgentId)
81
+ : 0;
82
+ setSelectedIndex(idx >= 0 ? idx : 0);
83
+ } catch (error) {
84
+ // If loading fails, show error in console but don't crash
85
+ console.error("Failed to load coding agents:", error);
86
+ // Fall back to empty array
87
+ setAgentItems([]);
88
+ } finally {
89
+ setIsLoading(false);
90
+ }
91
+ };
92
+
93
+ loadAgents();
94
+ }, [initialAgentId]);
95
+
96
+ // Update selection when props or items change
97
+ useEffect(() => {
98
+ if (isLoading || agentItems.length === 0) return;
99
+ const idx =
100
+ initialAgentId && agentItems.length > 0
101
+ ? agentItems.findIndex((item) => item.value === initialAgentId)
102
+ : 0;
103
+ setSelectedIndex(idx >= 0 ? idx : 0);
104
+ }, [initialAgentId, agentItems, isLoading]);
105
+
106
+ // Handle keyboard input
107
+ // Note: Select component handles Enter and arrow keys
108
+ useAppInput((input, key) => {
109
+ if (key.escape) {
110
+ onBack();
111
+ }
112
+ });
113
+
114
+ // Handle agent selection
115
+ const handleSelect = (item: CodingAgentItem) => {
116
+ onSelect(item.value);
117
+ };
118
+
119
+ // Footer actions
120
+ const footerActions = [
121
+ { key: "enter", description: "Select" },
122
+ { key: "esc", description: "Back" },
123
+ ];
124
+
125
+ return (
126
+ <Box flexDirection="column" height={rows}>
127
+ {/* Header */}
128
+ <Header
129
+ title="Coding Agent Selection"
130
+ titleColor="blue"
131
+ version={version}
132
+ />
133
+
134
+ {/* Content */}
135
+ <Box flexDirection="column" flexGrow={1} marginTop={1}>
136
+ <Box marginBottom={1}>
137
+ <Text>Select coding agent to use:</Text>
138
+ </Box>
139
+ {isLoading ? (
140
+ <Text>Loading coding agents...</Text>
141
+ ) : agentItems.length === 0 ? (
142
+ <Text color="yellow">
143
+ No coding agents available. Please check your configuration.
144
+ </Text>
145
+ ) : (
146
+ <Select
147
+ items={agentItems}
148
+ onSelect={handleSelect}
149
+ selectedIndex={selectedIndex}
150
+ onSelectedIndexChange={setSelectedIndex}
151
+ />
152
+ )}
153
+ </Box>
154
+
155
+ {/* Footer */}
156
+ <Footer actions={footerActions} />
157
+ </Box>
158
+ );
159
+ }
@@ -5,7 +5,11 @@ import { Footer } from "../parts/Footer.js";
5
5
  import { Select, type SelectItem } from "../common/Select.js";
6
6
  import { useAppInput } from "../../hooks/useAppInput.js";
7
7
  import { useTerminalSize } from "../../hooks/useTerminalSize.js";
8
- import type { AITool, InferenceLevel, ModelOption } from "../../types.js";
8
+ import type {
9
+ CodingAgentId,
10
+ InferenceLevel,
11
+ ModelOption,
12
+ } from "../../types.js";
9
13
  import {
10
14
  getDefaultInferenceForModel,
11
15
  getDefaultModelOption,
@@ -33,7 +37,7 @@ interface InferenceSelectItem extends SelectItem {
33
37
  * Props for `ModelSelectorScreen`.
34
38
  */
35
39
  export interface ModelSelectorScreenProps {
36
- tool: AITool;
40
+ tool: CodingAgentId;
37
41
  onBack: () => void;
38
42
  onSelect: (selection: ModelSelectionResult) => void;
39
43
  version?: string | null;
@@ -9,7 +9,7 @@ export interface WorktreeInfo {
9
9
  hasUncommittedChanges?: boolean;
10
10
  }
11
11
 
12
- export type AITool = string;
12
+ export type CodingAgentId = string;
13
13
  export type InferenceLevel = "low" | "medium" | "high" | "xhigh";
14
14
 
15
15
  /**
@@ -194,7 +194,7 @@ export type ScreenType =
194
194
  | "branch-creator"
195
195
  | "branch-action-selector"
196
196
  | "branch-quick-start"
197
- | "ai-tool-selector"
197
+ | "coding-agent-selector"
198
198
  | "model-selector"
199
199
  | "session-selector"
200
200
  | "execution-mode-selector"
@@ -1,4 +1,4 @@
1
- import type { AITool, InferenceLevel, ModelOption } from "../types.js";
1
+ import type { CodingAgentId, InferenceLevel, ModelOption } from "../types.js";
2
2
 
3
3
  const CODEX_BASE_LEVELS: InferenceLevel[] = ["high", "medium", "low"];
4
4
  const CODEX_MAX_LEVELS: InferenceLevel[] = ["xhigh", "high", "medium", "low"];
@@ -105,11 +105,13 @@ const MODEL_OPTIONS: Record<string, ModelOption[]> = {
105
105
  ],
106
106
  };
107
107
 
108
- export function getModelOptions(tool: AITool): ModelOption[] {
108
+ export function getModelOptions(tool: CodingAgentId): ModelOption[] {
109
109
  return MODEL_OPTIONS[tool] ?? [];
110
110
  }
111
111
 
112
- export function getDefaultModelOption(tool: AITool): ModelOption | undefined {
112
+ export function getDefaultModelOption(
113
+ tool: CodingAgentId,
114
+ ): ModelOption | undefined {
113
115
  const options = getModelOptions(tool);
114
116
  return options.find((opt) => opt.isDefault) ?? options[0];
115
117
  }
@@ -136,7 +138,7 @@ export function getDefaultInferenceForModel(
136
138
  * Normalize a model identifier for consistent display and persistence.
137
139
  */
138
140
  export function normalizeModelId(
139
- tool: AITool,
141
+ tool: CodingAgentId,
140
142
  model?: string | null,
141
143
  ): string | null {
142
144
  if (model === null || model === undefined) return model ?? null;
@@ -1,19 +1,19 @@
1
1
  /**
2
- * ビルトインAIツール定義
2
+ * ビルトインコーディングエージェント定義
3
3
  *
4
- * Claude Code、Codex、Gemini の CustomAITool 形式定義
4
+ * Claude Code、Codex、Gemini の CodingAgent 形式定義
5
5
  */
6
6
 
7
- import type { CustomAITool } from "../types/tools.js";
7
+ import type { CodingAgent } from "../types/tools.js";
8
8
  import {
9
9
  CLAUDE_PERMISSION_SKIP_ARGS,
10
10
  CODEX_DEFAULT_ARGS,
11
- } from "../shared/aiToolConstants.js";
11
+ } from "../shared/codingAgentConstants.js";
12
12
 
13
13
  /**
14
14
  * Claude Code のビルトイン定義
15
15
  */
16
- export const CLAUDE_CODE_TOOL: CustomAITool = {
16
+ export const CLAUDE_CODE_TOOL: CodingAgent = {
17
17
  id: "claude-code",
18
18
  displayName: "Claude Code",
19
19
  type: "bunx",
@@ -32,7 +32,7 @@ export const CLAUDE_CODE_TOOL: CustomAITool = {
32
32
  /**
33
33
  * Codex のビルトイン定義
34
34
  */
35
- export const CODEX_CLI_TOOL: CustomAITool = {
35
+ export const CODEX_CLI_TOOL: CodingAgent = {
36
36
  id: "codex-cli",
37
37
  displayName: "Codex",
38
38
  type: "bunx",
@@ -48,7 +48,7 @@ export const CODEX_CLI_TOOL: CustomAITool = {
48
48
  /**
49
49
  * Gemini のビルトイン定義
50
50
  */
51
- export const GEMINI_CLI_TOOL: CustomAITool = {
51
+ export const GEMINI_CLI_TOOL: CodingAgent = {
52
52
  id: "gemini-cli",
53
53
  displayName: "Gemini",
54
54
  type: "bunx",
@@ -62,10 +62,26 @@ export const GEMINI_CLI_TOOL: CustomAITool = {
62
62
  };
63
63
 
64
64
  /**
65
- * すべてのビルトインツール
65
+ * OpenCode のビルトイン定義
66
66
  */
67
- export const BUILTIN_TOOLS: CustomAITool[] = [
67
+ export const OPENCODE_TOOL: CodingAgent = {
68
+ id: "opencode",
69
+ displayName: "OpenCode",
70
+ type: "bunx",
71
+ command: "opencode-ai@latest",
72
+ modeArgs: {
73
+ normal: [],
74
+ continue: ["-c"],
75
+ resume: ["-s"],
76
+ },
77
+ };
78
+
79
+ /**
80
+ * すべてのビルトインコーディングエージェント
81
+ */
82
+ export const BUILTIN_CODING_AGENTS: CodingAgent[] = [
68
83
  CLAUDE_CODE_TOOL,
69
84
  CODEX_CLI_TOOL,
70
85
  GEMINI_CLI_TOOL,
86
+ OPENCODE_TOOL,
71
87
  ];
@@ -1,26 +1,26 @@
1
1
  /**
2
- * カスタムツール設定管理
2
+ * コーディングエージェント設定管理
3
3
  *
4
4
  * ~/.gwt/tools.jsonから設定を読み込み、
5
- * ビルトインツールと統合して管理します。
5
+ * ビルトインエージェントと統合して管理します。
6
6
  */
7
7
 
8
8
  import { homedir } from "node:os";
9
9
  import path from "node:path";
10
10
  import { readFile, writeFile, mkdir, rename } from "node:fs/promises";
11
11
  import type {
12
- ToolsConfig,
13
- CustomAITool,
14
- AIToolConfig,
12
+ CodingAgentsConfig,
13
+ CodingAgent,
14
+ CodingAgentConfig,
15
15
  } from "../types/tools.js";
16
- import { BUILTIN_TOOLS } from "./builtin-tools.js";
16
+ import { BUILTIN_CODING_AGENTS } from "./builtin-coding-agents.js";
17
17
  import { createLogger } from "../logging/logger.js";
18
18
  import { resolveProfileEnv } from "./profiles.js";
19
19
 
20
20
  const logger = createLogger({ category: "config" });
21
21
 
22
22
  /**
23
- * ツール設定ファイルのパス
23
+ * コーディングエージェント設定ファイルのパス
24
24
  * 環境変数 GWT_HOME が設定されている場合はそれを使用、それ以外はホームディレクトリ
25
25
  */
26
26
  export const WORKTREE_HOME =
@@ -32,32 +32,51 @@ export const CONFIG_DIR = path.join(WORKTREE_HOME, ".gwt");
32
32
  export const TOOLS_CONFIG_PATH = path.join(CONFIG_DIR, "tools.json");
33
33
  const TEMP_CONFIG_PATH = `${TOOLS_CONFIG_PATH}.tmp`;
34
34
 
35
- const DEFAULT_CONFIG: ToolsConfig = {
35
+ const DEFAULT_CONFIG: CodingAgentsConfig = {
36
36
  version: "1.0.0",
37
37
  env: {},
38
- customTools: [],
38
+ customCodingAgents: [],
39
39
  };
40
40
 
41
41
  /**
42
- * ツール設定を読み込む
42
+ * コーディングエージェント設定を読み込む
43
43
  *
44
44
  * ~/.gwt/tools.jsonから設定を読み込みます。
45
45
  * ファイルが存在しない場合は空配列を返します。
46
46
  *
47
- * @returns ToolsConfig
47
+ * @returns CodingAgentsConfig
48
48
  * @throws JSON構文エラー時
49
49
  */
50
- export async function loadToolsConfig(): Promise<ToolsConfig> {
50
+ export async function loadCodingAgentsConfig(): Promise<CodingAgentsConfig> {
51
51
  try {
52
52
  const content = await readFile(TOOLS_CONFIG_PATH, "utf-8");
53
- const config = JSON.parse(content) as ToolsConfig;
53
+ const config = JSON.parse(content) as CodingAgentsConfig;
54
+
55
+ // マイグレーション: customTools → customCodingAgents (後方互換性)
56
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
57
+ const legacyConfig = config as any;
58
+ if (!config.customCodingAgents && legacyConfig.customTools) {
59
+ config.customCodingAgents = legacyConfig.customTools;
60
+ logger.warn(
61
+ { path: TOOLS_CONFIG_PATH },
62
+ "Migrating deprecated 'customTools' to 'customCodingAgents'",
63
+ );
64
+ }
65
+
66
+ // フォールバック: undefined/null → 空配列
67
+ if (!config.customCodingAgents) {
68
+ config.customCodingAgents = [];
69
+ }
54
70
 
55
71
  // 検証
56
- validateToolsConfig(config);
72
+ validateCodingAgentsConfig(config);
57
73
 
58
74
  logger.debug(
59
- { path: TOOLS_CONFIG_PATH, toolCount: config.customTools.length },
60
- "Tools config loaded",
75
+ {
76
+ path: TOOLS_CONFIG_PATH,
77
+ agentCount: config.customCodingAgents.length,
78
+ },
79
+ "Coding agents config loaded",
61
80
  );
62
81
 
63
82
  return {
@@ -69,7 +88,7 @@ export async function loadToolsConfig(): Promise<ToolsConfig> {
69
88
  if (error instanceof Error && "code" in error && error.code === "ENOENT") {
70
89
  logger.debug(
71
90
  { path: TOOLS_CONFIG_PATH },
72
- "Tools config not found, using defaults",
91
+ "Coding agents config not found, using defaults",
73
92
  );
74
93
  return { ...DEFAULT_CONFIG };
75
94
  }
@@ -78,7 +97,7 @@ export async function loadToolsConfig(): Promise<ToolsConfig> {
78
97
  if (error instanceof SyntaxError) {
79
98
  logger.error(
80
99
  { path: TOOLS_CONFIG_PATH, error: error.message },
81
- "Tools config parse error",
100
+ "Coding agents config parse error",
82
101
  );
83
102
  throw new Error(
84
103
  `Failed to parse tools.json: ${error.message}\n` +
@@ -92,20 +111,20 @@ export async function loadToolsConfig(): Promise<ToolsConfig> {
92
111
  }
93
112
 
94
113
  /**
95
- * ToolsConfig全体を検証
114
+ * CodingAgentsConfig全体を検証
96
115
  *
97
116
  * @param config - 検証対象の設定
98
117
  * @throws 検証エラー時
99
118
  */
100
- function validateToolsConfig(config: ToolsConfig): void {
119
+ function validateCodingAgentsConfig(config: CodingAgentsConfig): void {
101
120
  // versionフィールドの検証
102
121
  if (!config.version || typeof config.version !== "string") {
103
122
  throw new Error("version field is required and must be a string");
104
123
  }
105
124
 
106
- // customToolsフィールドの検証
107
- if (!Array.isArray(config.customTools)) {
108
- throw new Error("customTools field must be an array");
125
+ // customCodingAgentsフィールドの検証
126
+ if (!Array.isArray(config.customCodingAgents)) {
127
+ throw new Error("customCodingAgents field must be an array");
109
128
  }
110
129
 
111
130
  if (config.env && typeof config.env !== "object") {
@@ -123,40 +142,42 @@ function validateToolsConfig(config: ToolsConfig): void {
123
142
  }
124
143
  }
125
144
 
126
- // 各ツールの検証
145
+ // 各エージェントの検証
127
146
  const seenIds = new Set<string>();
128
- for (const tool of config.customTools) {
129
- validateCustomAITool(tool);
147
+ for (const agent of config.customCodingAgents) {
148
+ validateCodingAgent(agent);
130
149
 
131
150
  // ID重複チェック
132
- if (seenIds.has(tool.id)) {
151
+ if (seenIds.has(agent.id)) {
133
152
  throw new Error(
134
- `Duplicate tool ID found: "${tool.id}"\n` +
135
- `Each tool must have a unique ID in ${TOOLS_CONFIG_PATH}`,
153
+ `Duplicate agent ID found: "${agent.id}"\n` +
154
+ `Each agent must have a unique ID in ${TOOLS_CONFIG_PATH}`,
136
155
  );
137
156
  }
138
- seenIds.add(tool.id);
157
+ seenIds.add(agent.id);
139
158
 
140
- // ビルトインツールとのID重複チェック
141
- const builtinIds = BUILTIN_TOOLS.map((t) => t.id);
142
- if (builtinIds.includes(tool.id)) {
159
+ // ビルトインエージェントとのID重複チェック
160
+ const builtinIds = BUILTIN_CODING_AGENTS.map((t) => t.id);
161
+ if (builtinIds.includes(agent.id)) {
143
162
  throw new Error(
144
- `Tool ID "${tool.id}" conflicts with builtin tool\n` +
145
- `Builtin tool IDs: ${builtinIds.join(", ")}`,
163
+ `Agent ID "${agent.id}" conflicts with builtin agent\n` +
164
+ `Builtin agent IDs: ${builtinIds.join(", ")}`,
146
165
  );
147
166
  }
148
167
  }
149
168
  }
150
169
 
151
- export async function saveToolsConfig(config: ToolsConfig): Promise<void> {
152
- const normalized: ToolsConfig = {
170
+ export async function saveCodingAgentsConfig(
171
+ config: CodingAgentsConfig,
172
+ ): Promise<void> {
173
+ const normalized: CodingAgentsConfig = {
153
174
  version: config.version,
154
175
  updatedAt: config.updatedAt ?? new Date().toISOString(),
155
176
  env: config.env ?? {},
156
- customTools: config.customTools,
177
+ customCodingAgents: config.customCodingAgents,
157
178
  };
158
179
 
159
- validateToolsConfig(normalized);
180
+ validateCodingAgentsConfig(normalized);
160
181
 
161
182
  await mkdir(CONFIG_DIR, { recursive: true });
162
183
  const payload = JSON.stringify(normalized, null, 2);
@@ -167,7 +188,7 @@ export async function saveToolsConfig(config: ToolsConfig): Promise<void> {
167
188
  /**
168
189
  * 共有環境変数を取得
169
190
  *
170
- * AIツール起動時に適用される環境変数を返します。
191
+ * コーディングエージェント起動時に適用される環境変数を返します。
171
192
  * マージ優先順位(後勝ち):
172
193
  * 1. tools.json の env フィールド
173
194
  * 2. profiles.yaml のアクティブプロファイル
@@ -176,7 +197,7 @@ export async function saveToolsConfig(config: ToolsConfig): Promise<void> {
176
197
  */
177
198
  export async function getSharedEnvironment(): Promise<Record<string, string>> {
178
199
  const [config, profileEnv] = await Promise.all([
179
- loadToolsConfig(),
200
+ loadCodingAgentsConfig(),
180
201
  resolveProfileEnv(),
181
202
  ]);
182
203
 
@@ -187,104 +208,111 @@ export async function getSharedEnvironment(): Promise<Record<string, string>> {
187
208
  }
188
209
 
189
210
  /**
190
- * CustomAITool単体を検証
211
+ * CodingAgent単体を検証
191
212
  *
192
- * @param tool - 検証対象のツール
213
+ * @param agent - 検証対象のエージェント
193
214
  * @throws 検証エラー時
194
215
  */
195
- function validateCustomAITool(tool: unknown): asserts tool is CustomAITool {
216
+ function validateCodingAgent(agent: unknown): asserts agent is CodingAgent {
196
217
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
197
- const t = tool as any;
218
+ const a = agent as any;
198
219
 
199
220
  // 必須フィールドの存在チェック
200
221
  const requiredFields = ["id", "displayName", "type", "command", "modeArgs"];
201
222
  for (const field of requiredFields) {
202
- if (!t[field]) {
223
+ if (!a[field]) {
203
224
  throw new Error(
204
- `Required field "${field}" is missing in tool configuration`,
225
+ `Required field "${field}" is missing in agent configuration`,
205
226
  );
206
227
  }
207
228
  }
208
229
 
209
230
  // id形式の検証(小文字英数字とハイフンのみ)
210
- if (!/^[a-z0-9-]+$/.test(t.id)) {
231
+ if (!/^[a-z0-9-]+$/.test(a.id)) {
211
232
  throw new Error(
212
- `Invalid tool ID format: "${t.id}"\n` +
213
- `Tool ID must contain only lowercase letters, numbers, and hyphens (pattern: ^[a-z0-9-]+$)`,
233
+ `Invalid agent ID format: "${a.id}"\n` +
234
+ `Agent ID must contain only lowercase letters, numbers, and hyphens (pattern: ^[a-z0-9-]+$)`,
214
235
  );
215
236
  }
216
237
 
217
238
  // typeフィールドの値検証
218
239
  const validTypes = ["path", "bunx", "command"];
219
- if (!validTypes.includes(t.type)) {
240
+ if (!validTypes.includes(a.type)) {
220
241
  throw new Error(
221
- `Invalid type: "${t.type}"\n` +
242
+ `Invalid type: "${a.type}"\n` +
222
243
  `Type must be one of: ${validTypes.join(", ")}`,
223
244
  );
224
245
  }
225
246
 
226
247
  // type='path'の場合、commandが絶対パスであることを確認
227
- if (t.type === "path" && !path.isAbsolute(t.command)) {
248
+ if (a.type === "path" && !path.isAbsolute(a.command)) {
228
249
  throw new Error(
229
- `For type="path", command must be an absolute path: "${t.command}"`,
250
+ `For type="path", command must be an absolute path: "${a.command}"`,
230
251
  );
231
252
  }
232
253
 
233
254
  // modeArgsの検証(少なくとも1つのモードが定義されている)
234
- if (!t.modeArgs.normal && !t.modeArgs.continue && !t.modeArgs.resume) {
255
+ if (!a.modeArgs.normal && !a.modeArgs.continue && !a.modeArgs.resume) {
235
256
  throw new Error(
236
- `modeArgs must define at least one mode (normal, continue, or resume) for tool "${t.id}"`,
257
+ `modeArgs must define at least one mode (normal, continue, or resume) for agent "${a.id}"`,
237
258
  );
238
259
  }
239
260
  }
240
261
 
241
262
  /**
242
- * IDでツールを検索
263
+ * IDでコーディングエージェントを検索
243
264
  *
244
- * @param id - ツールID
245
- * @returns ツール設定(見つからない場合はundefined)
265
+ * @param id - エージェントID
266
+ * @returns エージェント設定(見つからない場合はundefined)
246
267
  */
247
- export async function getToolById(
268
+ export async function getCodingAgentById(
248
269
  id: string,
249
- ): Promise<CustomAITool | undefined> {
250
- // ビルトインツールから検索
251
- const builtinTool = BUILTIN_TOOLS.find((t) => t.id === id);
252
- if (builtinTool) {
253
- logger.debug({ id, found: true, isBuiltin: true }, "Tool lookup");
254
- return builtinTool;
270
+ ): Promise<CodingAgent | undefined> {
271
+ // ビルトインエージェントから検索
272
+ const builtinAgent = BUILTIN_CODING_AGENTS.find((a) => a.id === id);
273
+ if (builtinAgent) {
274
+ logger.debug({ id, found: true, isBuiltin: true }, "Coding agent lookup");
275
+ return builtinAgent;
255
276
  }
256
277
 
257
- // カスタムツールから検索
258
- const config = await loadToolsConfig();
259
- const customTool = config.customTools.find((t) => t.id === id);
260
- logger.debug({ id, found: !!customTool, isBuiltin: false }, "Tool lookup");
261
- return customTool;
278
+ // カスタムエージェントから検索
279
+ const config = await loadCodingAgentsConfig();
280
+ const customAgent = config.customCodingAgents.find((a) => a.id === id);
281
+ logger.debug(
282
+ { id, found: !!customAgent, isBuiltin: false },
283
+ "Coding agent lookup",
284
+ );
285
+ return customAgent;
262
286
  }
263
287
 
264
288
  /**
265
- * すべてのツール(ビルトイン+カスタム)を取得
289
+ * すべてのコーディングエージェント(ビルトイン+カスタム)を取得
266
290
  *
267
- * @returns AIToolConfigの配列
291
+ * @returns CodingAgentConfigの配列
268
292
  */
269
- export async function getAllTools(): Promise<AIToolConfig[]> {
270
- const config = await loadToolsConfig();
271
-
272
- // ビルトインツールをAIToolConfig形式に変換
273
- const builtinConfigs: AIToolConfig[] = BUILTIN_TOOLS.map((tool) => ({
274
- id: tool.id,
275
- displayName: tool.displayName,
276
- ...(tool.icon ? { icon: tool.icon } : {}),
277
- isBuiltin: true,
278
- }));
279
-
280
- // カスタムツールをAIToolConfig形式に変換
281
- const customConfigs: AIToolConfig[] = config.customTools.map((tool) => ({
282
- id: tool.id,
283
- displayName: tool.displayName,
284
- ...(tool.icon ? { icon: tool.icon } : {}),
285
- isBuiltin: false,
286
- customConfig: tool,
287
- }));
293
+ export async function getAllCodingAgents(): Promise<CodingAgentConfig[]> {
294
+ const config = await loadCodingAgentsConfig();
295
+
296
+ // ビルトインエージェントをCodingAgentConfig形式に変換
297
+ const builtinConfigs: CodingAgentConfig[] = BUILTIN_CODING_AGENTS.map(
298
+ (agent) => ({
299
+ id: agent.id,
300
+ displayName: agent.displayName,
301
+ ...(agent.icon ? { icon: agent.icon } : {}),
302
+ isBuiltin: true,
303
+ }),
304
+ );
305
+
306
+ // カスタムエージェントをCodingAgentConfig形式に変換
307
+ const customConfigs: CodingAgentConfig[] = config.customCodingAgents.map(
308
+ (agent) => ({
309
+ id: agent.id,
310
+ displayName: agent.displayName,
311
+ ...(agent.icon ? { icon: agent.icon } : {}),
312
+ isBuiltin: false,
313
+ customConfig: agent,
314
+ }),
315
+ );
288
316
 
289
317
  // ビルトイン + カスタム の順で統合
290
318
  return [...builtinConfigs, ...customConfigs];