@akiojin/gwt 2.1.1 → 2.3.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 (149) hide show
  1. package/README.ja.md +4 -4
  2. package/README.md +4 -4
  3. package/dist/cli/ui/components/App.d.ts +4 -4
  4. package/dist/cli/ui/components/App.d.ts.map +1 -1
  5. package/dist/cli/ui/components/App.js +144 -105
  6. package/dist/cli/ui/components/App.js.map +1 -1
  7. package/dist/cli/ui/components/common/Confirm.d.ts +1 -1
  8. package/dist/cli/ui/components/common/Confirm.d.ts.map +1 -1
  9. package/dist/cli/ui/components/common/Confirm.js +7 -7
  10. package/dist/cli/ui/components/common/Confirm.js.map +1 -1
  11. package/dist/cli/ui/components/common/ErrorBoundary.d.ts +1 -1
  12. package/dist/cli/ui/components/common/ErrorBoundary.d.ts.map +1 -1
  13. package/dist/cli/ui/components/common/ErrorBoundary.js +4 -4
  14. package/dist/cli/ui/components/common/ErrorBoundary.js.map +1 -1
  15. package/dist/cli/ui/components/common/Input.d.ts +7 -2
  16. package/dist/cli/ui/components/common/Input.d.ts.map +1 -1
  17. package/dist/cli/ui/components/common/Input.js +12 -4
  18. package/dist/cli/ui/components/common/Input.js.map +1 -1
  19. package/dist/cli/ui/components/common/LoadingIndicator.d.ts +1 -1
  20. package/dist/cli/ui/components/common/LoadingIndicator.d.ts.map +1 -1
  21. package/dist/cli/ui/components/common/LoadingIndicator.js +4 -4
  22. package/dist/cli/ui/components/common/LoadingIndicator.js.map +1 -1
  23. package/dist/cli/ui/components/common/Select.d.ts +1 -1
  24. package/dist/cli/ui/components/common/Select.d.ts.map +1 -1
  25. package/dist/cli/ui/components/common/Select.js +11 -12
  26. package/dist/cli/ui/components/common/Select.js.map +1 -1
  27. package/dist/cli/ui/components/screens/AIToolSelectorScreen.d.ts +2 -2
  28. package/dist/cli/ui/components/screens/AIToolSelectorScreen.d.ts.map +1 -1
  29. package/dist/cli/ui/components/screens/AIToolSelectorScreen.js +11 -11
  30. package/dist/cli/ui/components/screens/AIToolSelectorScreen.js.map +1 -1
  31. package/dist/cli/ui/components/screens/BranchCreatorScreen.d.ts +1 -1
  32. package/dist/cli/ui/components/screens/BranchCreatorScreen.d.ts.map +1 -1
  33. package/dist/cli/ui/components/screens/BranchCreatorScreen.js +39 -36
  34. package/dist/cli/ui/components/screens/BranchCreatorScreen.js.map +1 -1
  35. package/dist/cli/ui/components/screens/BranchListScreen.d.ts +8 -4
  36. package/dist/cli/ui/components/screens/BranchListScreen.d.ts.map +1 -1
  37. package/dist/cli/ui/components/screens/BranchListScreen.js +122 -48
  38. package/dist/cli/ui/components/screens/BranchListScreen.js.map +1 -1
  39. package/dist/cli/ui/components/screens/ExecutionModeSelectorScreen.d.ts +2 -2
  40. package/dist/cli/ui/components/screens/ExecutionModeSelectorScreen.d.ts.map +1 -1
  41. package/dist/cli/ui/components/screens/ExecutionModeSelectorScreen.js +25 -25
  42. package/dist/cli/ui/components/screens/ExecutionModeSelectorScreen.js.map +1 -1
  43. package/dist/cli/ui/components/screens/PRCleanupScreen.d.ts +2 -2
  44. package/dist/cli/ui/components/screens/PRCleanupScreen.js +21 -21
  45. package/dist/cli/ui/components/screens/SessionSelectorScreen.d.ts +1 -1
  46. package/dist/cli/ui/components/screens/SessionSelectorScreen.js +8 -8
  47. package/dist/cli/ui/components/screens/WorktreeManagerScreen.d.ts +1 -1
  48. package/dist/cli/ui/components/screens/WorktreeManagerScreen.js +8 -8
  49. package/dist/cli/ui/screens/BranchActionSelectorScreen.d.ts.map +1 -1
  50. package/dist/cli/ui/screens/BranchActionSelectorScreen.js +7 -4
  51. package/dist/cli/ui/screens/BranchActionSelectorScreen.js.map +1 -1
  52. package/dist/cli/ui/types.d.ts.map +1 -1
  53. package/dist/client/assets/{index-V6hDu9KS.js → index-Difv1Hwu.js} +2 -2
  54. package/dist/client/index.html +1 -1
  55. package/dist/config/builtin-tools.d.ts +10 -2
  56. package/dist/config/builtin-tools.d.ts.map +1 -1
  57. package/dist/config/builtin-tools.js +40 -4
  58. package/dist/config/builtin-tools.js.map +1 -1
  59. package/dist/config/index.d.ts.map +1 -1
  60. package/dist/config/index.js.map +1 -1
  61. package/dist/config/tools.d.ts.map +1 -1
  62. package/dist/config/tools.js +4 -3
  63. package/dist/config/tools.js.map +1 -1
  64. package/dist/gemini.d.ts +12 -0
  65. package/dist/gemini.d.ts.map +1 -0
  66. package/dist/gemini.js +154 -0
  67. package/dist/gemini.js.map +1 -0
  68. package/dist/git.d.ts.map +1 -1
  69. package/dist/git.js.map +1 -1
  70. package/dist/index.d.ts.map +1 -1
  71. package/dist/index.js +30 -0
  72. package/dist/index.js.map +1 -1
  73. package/dist/qwen.d.ts +12 -0
  74. package/dist/qwen.d.ts.map +1 -0
  75. package/dist/qwen.js +154 -0
  76. package/dist/qwen.js.map +1 -0
  77. package/dist/services/git.service.d.ts.map +1 -1
  78. package/dist/services/git.service.js.map +1 -1
  79. package/dist/web/client/src/components/BranchGraph.d.ts.map +1 -1
  80. package/dist/web/client/src/components/BranchGraph.js +1 -1
  81. package/dist/web/client/src/components/BranchGraph.js.map +1 -1
  82. package/dist/web/client/src/components/EnvEditor.d.ts.map +1 -1
  83. package/dist/web/client/src/components/EnvEditor.js +7 -4
  84. package/dist/web/client/src/components/EnvEditor.js.map +1 -1
  85. package/dist/web/client/src/pages/BranchDetailPage.d.ts.map +1 -1
  86. package/dist/web/client/src/pages/BranchDetailPage.js +55 -18
  87. package/dist/web/client/src/pages/BranchDetailPage.js.map +1 -1
  88. package/dist/web/client/src/pages/BranchListPage.d.ts.map +1 -1
  89. package/dist/web/client/src/pages/BranchListPage.js +10 -4
  90. package/dist/web/client/src/pages/BranchListPage.js.map +1 -1
  91. package/dist/web/client/src/pages/ConfigManagementPage.d.ts.map +1 -1
  92. package/dist/web/client/src/pages/ConfigManagementPage.js +4 -2
  93. package/dist/web/client/src/pages/ConfigManagementPage.js.map +1 -1
  94. package/package.json +2 -1
  95. package/src/cli/ui/__tests__/acceptance/navigation.acceptance.test.tsx +69 -50
  96. package/src/cli/ui/__tests__/components/App.protected-branch.test.tsx +67 -45
  97. package/src/cli/ui/__tests__/components/App.shortcuts.test.tsx +117 -75
  98. package/src/cli/ui/__tests__/components/App.test.tsx +45 -37
  99. package/src/cli/ui/__tests__/components/common/Confirm.test.tsx +35 -22
  100. package/src/cli/ui/__tests__/components/common/ErrorBoundary.test.tsx +22 -22
  101. package/src/cli/ui/__tests__/components/common/Input.test.tsx +29 -22
  102. package/src/cli/ui/__tests__/components/common/LoadingIndicator.test.tsx +40 -34
  103. package/src/cli/ui/__tests__/components/common/Select.memo.test.tsx +57 -66
  104. package/src/cli/ui/__tests__/components/common/Select.test.tsx +121 -91
  105. package/src/cli/ui/__tests__/components/parts/Footer.test.tsx +18 -16
  106. package/src/cli/ui/__tests__/components/parts/Header.test.tsx +13 -13
  107. package/src/cli/ui/__tests__/components/parts/ScrollableList.test.tsx +20 -20
  108. package/src/cli/ui/__tests__/components/parts/Stats.test.tsx +38 -26
  109. package/src/cli/ui/__tests__/components/screens/AIToolSelectorScreen.test.tsx +31 -31
  110. package/src/cli/ui/__tests__/components/screens/BranchCreatorScreen.test.tsx +73 -37
  111. package/src/cli/ui/__tests__/components/screens/BranchListScreen.test.tsx +496 -75
  112. package/src/cli/ui/__tests__/components/screens/ExecutionModeSelectorScreen.test.tsx +38 -32
  113. package/src/cli/ui/__tests__/components/screens/PRCleanupScreen.test.tsx +39 -39
  114. package/src/cli/ui/__tests__/components/screens/SessionSelectorScreen.test.tsx +49 -21
  115. package/src/cli/ui/__tests__/components/screens/WorktreeManagerScreen.test.tsx +52 -28
  116. package/src/cli/ui/__tests__/integration/edgeCases.test.tsx +84 -48
  117. package/src/cli/ui/__tests__/integration/navigation.test.tsx +111 -83
  118. package/src/cli/ui/__tests__/integration/realtimeUpdate.test.tsx +111 -108
  119. package/src/cli/ui/__tests__/performance/branchList.performance.test.tsx +50 -37
  120. package/src/cli/ui/__tests__/performance/useMemoOptimization.test.tsx +75 -76
  121. package/src/cli/ui/components/App.tsx +247 -150
  122. package/src/cli/ui/components/common/Confirm.tsx +13 -9
  123. package/src/cli/ui/components/common/ErrorBoundary.tsx +8 -5
  124. package/src/cli/ui/components/common/Input.tsx +26 -4
  125. package/src/cli/ui/components/common/LoadingIndicator.tsx +8 -5
  126. package/src/cli/ui/components/common/Select.tsx +28 -17
  127. package/src/cli/ui/components/parts/Header.test.tsx +5 -15
  128. package/src/cli/ui/components/screens/AIToolSelectorScreen.tsx +19 -13
  129. package/src/cli/ui/components/screens/BranchCreatorScreen.tsx +74 -54
  130. package/src/cli/ui/components/screens/BranchListScreen.tsx +187 -62
  131. package/src/cli/ui/components/screens/ExecutionModeSelectorScreen.tsx +35 -28
  132. package/src/cli/ui/components/screens/PRCleanupScreen.tsx +22 -22
  133. package/src/cli/ui/components/screens/SessionSelectorScreen.tsx +8 -8
  134. package/src/cli/ui/components/screens/WorktreeManagerScreen.tsx +8 -8
  135. package/src/cli/ui/screens/BranchActionSelectorScreen.tsx +9 -4
  136. package/src/cli/ui/types.ts +8 -1
  137. package/src/config/builtin-tools.ts +42 -4
  138. package/src/config/index.ts +2 -12
  139. package/src/config/tools.ts +16 -6
  140. package/src/gemini.ts +202 -0
  141. package/src/git.ts +2 -1
  142. package/src/index.ts +30 -0
  143. package/src/qwen.ts +208 -0
  144. package/src/services/git.service.ts +2 -1
  145. package/src/web/client/src/components/BranchGraph.tsx +3 -2
  146. package/src/web/client/src/components/EnvEditor.tsx +44 -11
  147. package/src/web/client/src/pages/BranchDetailPage.tsx +165 -54
  148. package/src/web/client/src/pages/BranchListPage.tsx +37 -13
  149. package/src/web/client/src/pages/ConfigManagementPage.tsx +28 -9
@@ -1,6 +1,6 @@
1
- import React from 'react';
2
- import { Box, Text } from 'ink';
3
- import { Select, type SelectItem } from './Select.js';
1
+ import React from "react";
2
+ import { Box, Text } from "ink";
3
+ import { Select, type SelectItem } from "./Select.js";
4
4
 
5
5
  export interface ConfirmProps {
6
6
  message: string;
@@ -16,17 +16,17 @@ export interface ConfirmProps {
16
16
  export function Confirm({
17
17
  message,
18
18
  onConfirm,
19
- yesLabel = 'Yes',
20
- noLabel = 'No',
19
+ yesLabel = "Yes",
20
+ noLabel = "No",
21
21
  defaultNo = false,
22
22
  }: ConfirmProps) {
23
23
  const items: SelectItem[] = [
24
- { label: yesLabel, value: 'yes' },
25
- { label: noLabel, value: 'no' },
24
+ { label: yesLabel, value: "yes" },
25
+ { label: noLabel, value: "no" },
26
26
  ];
27
27
 
28
28
  const handleSelect = (item: SelectItem) => {
29
- onConfirm(item.value === 'yes');
29
+ onConfirm(item.value === "yes");
30
30
  };
31
31
 
32
32
  return (
@@ -34,7 +34,11 @@ export function Confirm({
34
34
  <Box marginBottom={1}>
35
35
  <Text>{message}</Text>
36
36
  </Box>
37
- <Select items={items} onSelect={handleSelect} initialIndex={defaultNo ? 1 : 0} />
37
+ <Select
38
+ items={items}
39
+ onSelect={handleSelect}
40
+ initialIndex={defaultNo ? 1 : 0}
41
+ />
38
42
  </Box>
39
43
  );
40
44
  }
@@ -1,5 +1,5 @@
1
- import React, { Component, type ReactNode } from 'react';
2
- import { Box, Text } from 'ink';
1
+ import React, { Component, type ReactNode } from "react";
2
+ import { Box, Text } from "ink";
3
3
 
4
4
  interface ErrorBoundaryProps {
5
5
  children: ReactNode;
@@ -14,7 +14,10 @@ interface ErrorBoundaryState {
14
14
  /**
15
15
  * Error Boundary component to catch and display errors
16
16
  */
17
- export class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
17
+ export class ErrorBoundary extends Component<
18
+ ErrorBoundaryProps,
19
+ ErrorBoundaryState
20
+ > {
18
21
  constructor(props: ErrorBoundaryProps) {
19
22
  super(props);
20
23
  this.state = { hasError: false, error: null };
@@ -25,7 +28,7 @@ export class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundarySt
25
28
  }
26
29
 
27
30
  componentDidCatch(error: Error, errorInfo: React.ErrorInfo): void {
28
- console.error('ErrorBoundary caught an error:', error, errorInfo);
31
+ console.error("ErrorBoundary caught an error:", error, errorInfo);
29
32
  }
30
33
 
31
34
  componentDidUpdate(prevProps: ErrorBoundaryProps): void {
@@ -46,7 +49,7 @@ export class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundarySt
46
49
  return (
47
50
  <Box flexDirection="column" padding={1}>
48
51
  <Text color="red" bold>
49
- Error: {this.state.error.message || 'Unknown error'}
52
+ Error: {this.state.error.message || "Unknown error"}
50
53
  </Text>
51
54
  </Box>
52
55
  );
@@ -1,6 +1,6 @@
1
- import React from 'react';
2
- import { Box, Text } from 'ink';
3
- import TextInput from 'ink-text-input';
1
+ import React from "react";
2
+ import { Box, Text, useInput } from "ink";
3
+ import TextInput from "ink-text-input";
4
4
 
5
5
  export interface InputProps {
6
6
  value: string;
@@ -9,12 +9,34 @@ export interface InputProps {
9
9
  placeholder?: string;
10
10
  label?: string;
11
11
  mask?: string;
12
+ /**
13
+ * Block specific key bindings to prevent parent handlers from processing them
14
+ * Useful for blocking shortcuts like 'c', 'r', 'm' while typing
15
+ */
16
+ blockKeys?: string[];
12
17
  }
13
18
 
14
19
  /**
15
20
  * Input component - wrapper around ink-text-input with optional label
16
21
  */
17
- export function Input({ value, onChange, onSubmit, placeholder, label, mask }: InputProps) {
22
+ export function Input({
23
+ value,
24
+ onChange,
25
+ onSubmit,
26
+ placeholder,
27
+ label,
28
+ mask,
29
+ blockKeys,
30
+ }: InputProps) {
31
+ // Block specific keys from being processed by parent useInput handlers
32
+ // This prevents shortcuts (c/r/m) from triggering while typing in the input
33
+ useInput((input) => {
34
+ if (blockKeys && blockKeys.includes(input)) {
35
+ // Consume the key - don't let it propagate to parent handlers
36
+ return;
37
+ }
38
+ });
39
+
18
40
  return (
19
41
  <Box flexDirection="column">
20
42
  {label && (
@@ -1,5 +1,5 @@
1
- import React, { useEffect, useMemo, useRef, useState } from 'react';
2
- import { Box, Text } from 'ink';
1
+ import React, { useEffect, useMemo, useRef, useState } from "react";
2
+ import { Box, Text } from "ink";
3
3
 
4
4
  export interface LoadingIndicatorProps {
5
5
  /** true にするとローディング表示を開始する */
@@ -14,7 +14,7 @@ export interface LoadingIndicatorProps {
14
14
  frames?: string[];
15
15
  }
16
16
 
17
- const DEFAULT_FRAMES = ['|', '/', '-', '\\'];
17
+ const DEFAULT_FRAMES = ["|", "/", "-", "\\"];
18
18
 
19
19
  /**
20
20
  * ローディング中に簡易スピナーとメッセージを表示するコンポーネント。
@@ -23,7 +23,7 @@ const DEFAULT_FRAMES = ['|', '/', '-', '\\'];
23
23
  export function LoadingIndicator({
24
24
  isLoading,
25
25
  delay = 300,
26
- message = 'Loading... please wait',
26
+ message = "Loading... please wait",
27
27
  interval = 80,
28
28
  frames = DEFAULT_FRAMES,
29
29
  }: LoadingIndicatorProps) {
@@ -33,7 +33,10 @@ export function LoadingIndicator({
33
33
  const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);
34
34
 
35
35
  // スピナーに使用するフレームをキャッシュ
36
- const safeFrames = useMemo(() => (frames.length > 0 ? frames : DEFAULT_FRAMES), [frames]);
36
+ const safeFrames = useMemo(
37
+ () => (frames.length > 0 ? frames : DEFAULT_FRAMES),
38
+ [frames],
39
+ );
37
40
 
38
41
  useEffect(() => {
39
42
  // ローディングが開始したら、delay後に表示を有効化
@@ -1,5 +1,5 @@
1
- import React, { useEffect, useState } from 'react';
2
- import { Box, Text, useInput, useStdout } from 'ink';
1
+ import React, { useEffect, useState } from "react";
2
+ import { Box, Text, useInput, useStdout } from "ink";
3
3
 
4
4
  export interface SelectItem {
5
5
  label: string;
@@ -16,7 +16,7 @@ export interface SelectProps<T extends SelectItem = SelectItem> {
16
16
  renderItem?: (
17
17
  item: T,
18
18
  isSelected: boolean,
19
- context: { columns: number }
19
+ context: { columns: number },
20
20
  ) => React.ReactNode;
21
21
  // Optional controlled component props for cursor position
22
22
  selectedIndex?: number;
@@ -29,7 +29,7 @@ export interface SelectProps<T extends SelectItem = SelectItem> {
29
29
  */
30
30
  function arePropsEqual<T extends SelectItem = SelectItem>(
31
31
  prevProps: SelectProps<T>,
32
- nextProps: SelectProps<T>
32
+ nextProps: SelectProps<T>,
33
33
  ): boolean {
34
34
  // Check if non-array props are the same
35
35
  if (
@@ -59,7 +59,10 @@ function arePropsEqual<T extends SelectItem = SelectItem>(
59
59
  return false;
60
60
  }
61
61
 
62
- if (prevItem.value !== nextItem.value || prevItem.label !== nextItem.label) {
62
+ if (
63
+ prevItem.value !== nextItem.value ||
64
+ prevItem.label !== nextItem.label
65
+ ) {
63
66
  return false;
64
67
  }
65
68
  }
@@ -73,7 +76,7 @@ function arePropsEqual<T extends SelectItem = SelectItem>(
73
76
  * Cursor stops at top and bottom instead of wrapping around
74
77
  * Wrapped with React.memo for performance optimization
75
78
  */
76
- const SelectComponent = <T extends SelectItem = SelectItem,>({
79
+ const SelectComponent = <T extends SelectItem = SelectItem>({
77
80
  items,
78
81
  onSelect,
79
82
  limit,
@@ -85,15 +88,18 @@ const SelectComponent = <T extends SelectItem = SelectItem,>({
85
88
  onSelectedIndexChange,
86
89
  }: SelectProps<T>) => {
87
90
  // Support both controlled and uncontrolled modes
88
- const [internalSelectedIndex, setInternalSelectedIndex] = useState(initialIndex);
91
+ const [internalSelectedIndex, setInternalSelectedIndex] =
92
+ useState(initialIndex);
89
93
  const [offset, setOffset] = useState(0);
90
94
 
91
95
  // Use external selectedIndex if provided (controlled mode), otherwise use internal state
92
96
  const isControlled = externalSelectedIndex !== undefined;
93
- const selectedIndex = isControlled ? externalSelectedIndex : internalSelectedIndex;
97
+ const selectedIndex = isControlled
98
+ ? externalSelectedIndex
99
+ : internalSelectedIndex;
94
100
 
95
101
  const updateSelectedIndex = (value: number | ((prev: number) => number)) => {
96
- const newIndex = typeof value === 'function' ? value(selectedIndex) : value;
102
+ const newIndex = typeof value === "function" ? value(selectedIndex) : value;
97
103
 
98
104
  if (!isControlled) {
99
105
  setInternalSelectedIndex(newIndex);
@@ -134,7 +140,7 @@ const SelectComponent = <T extends SelectItem = SelectItem,>({
134
140
 
135
141
  // Only handle navigation and selection keys
136
142
  // Let other keys (q, m, n, c, etc.) propagate to parent components
137
- if (key.upArrow || input === 'k') {
143
+ if (key.upArrow || input === "k") {
138
144
  // Move up but don't loop - stop at 0
139
145
  updateSelectedIndex((current) => {
140
146
  const newIndex = Math.max(0, current - 1);
@@ -146,7 +152,7 @@ const SelectComponent = <T extends SelectItem = SelectItem,>({
146
152
 
147
153
  return newIndex;
148
154
  });
149
- } else if (key.downArrow || input === 'j') {
155
+ } else if (key.downArrow || input === "j") {
150
156
  // Move down but don't loop - stop at last item
151
157
  updateSelectedIndex((current) => {
152
158
  const newIndex = Math.min(items.length - 1, current + 1);
@@ -189,11 +195,13 @@ const SelectComponent = <T extends SelectItem = SelectItem,>({
189
195
  );
190
196
  }
191
197
 
192
- const indicatorElement = renderIndicator
193
- ? renderIndicator(item, isSelected)
194
- : isSelected
195
- ? <Text color="cyan">›</Text>
196
- : <Text> </Text>;
198
+ const indicatorElement = renderIndicator ? (
199
+ renderIndicator(item, isSelected)
200
+ ) : isSelected ? (
201
+ <Text color="cyan">›</Text>
202
+ ) : (
203
+ <Text> </Text>
204
+ );
197
205
 
198
206
  return (
199
207
  <Box key={item.value} flexDirection="row">
@@ -213,4 +221,7 @@ const SelectComponent = <T extends SelectItem = SelectItem,>({
213
221
  /**
214
222
  * Export memoized Select component
215
223
  */
216
- export const Select = React.memo(SelectComponent, arePropsEqual) as typeof SelectComponent;
224
+ export const Select = React.memo(
225
+ SelectComponent,
226
+ arePropsEqual,
227
+ ) as typeof SelectComponent;
@@ -5,9 +5,7 @@ import { Header } from "./Header.js";
5
5
 
6
6
  describe("Header Component", () => {
7
7
  it("正常系: versionプロップありの場合、タイトルとバージョンを表示する", () => {
8
- const { lastFrame } = render(
9
- <Header title="gwt" version="1.12.3" />
10
- );
8
+ const { lastFrame } = render(<Header title="gwt" version="1.12.3" />);
11
9
 
12
10
  const output = lastFrame();
13
11
 
@@ -27,9 +25,7 @@ describe("Header Component", () => {
27
25
  });
28
26
 
29
27
  it("正常系: version={null}の場合、タイトルのみ表示する", () => {
30
- const { lastFrame } = render(
31
- <Header title="gwt" version={null} />
32
- );
28
+ const { lastFrame } = render(<Header title="gwt" version={null} />);
33
29
 
34
30
  const output = lastFrame();
35
31
 
@@ -46,7 +42,7 @@ describe("Header Component", () => {
46
42
  version="1.12.3"
47
43
  showDivider={true}
48
44
  dividerChar="─"
49
- />
45
+ />,
50
46
  );
51
47
 
52
48
  const output = lastFrame();
@@ -57,11 +53,7 @@ describe("Header Component", () => {
57
53
 
58
54
  it("正常系: showDivider=falseの場合、区切り線が表示されない", () => {
59
55
  const { lastFrame } = render(
60
- <Header
61
- title="gwt"
62
- version="1.12.3"
63
- showDivider={false}
64
- />
56
+ <Header title="gwt" version="1.12.3" showDivider={false} />,
65
57
  );
66
58
 
67
59
  const output = lastFrame();
@@ -73,9 +65,7 @@ describe("Header Component", () => {
73
65
  });
74
66
 
75
67
  it("正常系: プレリリースバージョンも正しく表示される", () => {
76
- const { lastFrame } = render(
77
- <Header title="gwt" version="2.0.0-beta.1" />
78
- );
68
+ const { lastFrame } = render(<Header title="gwt" version="2.0.0-beta.1" />);
79
69
 
80
70
  const output = lastFrame();
81
71
 
@@ -1,11 +1,11 @@
1
- import React, { useState, useEffect } from 'react';
2
- import { Box, Text, useInput } 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 { useTerminalSize } from '../../hooks/useTerminalSize.js';
7
- import { getAllTools } from '../../../../config/tools.js';
8
- import type { AIToolConfig } from '../../../../types/tools.js';
1
+ import React, { useState, useEffect } from "react";
2
+ import { Box, Text, useInput } 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 { useTerminalSize } from "../../hooks/useTerminalSize.js";
7
+ import { getAllTools } from "../../../../config/tools.js";
8
+ import type { AIToolConfig } from "../../../../types/tools.js";
9
9
 
10
10
  export type AITool = string;
11
11
 
@@ -27,7 +27,11 @@ export interface AIToolSelectorScreenProps {
27
27
  *
28
28
  * This screen dynamically loads available tools from the configuration (builtin + custom).
29
29
  */
30
- export function AIToolSelectorScreen({ onBack, onSelect, version }: AIToolSelectorScreenProps) {
30
+ export function AIToolSelectorScreen({
31
+ onBack,
32
+ onSelect,
33
+ version,
34
+ }: AIToolSelectorScreenProps) {
31
35
  const { rows } = useTerminalSize();
32
36
  const [toolItems, setToolItems] = useState<AIToolItem[]>([]);
33
37
  const [isLoading, setIsLoading] = useState(true);
@@ -60,7 +64,7 @@ export function AIToolSelectorScreen({ onBack, onSelect, version }: AIToolSelect
60
64
  setToolItems(items);
61
65
  } catch (error) {
62
66
  // If loading fails, show error in console but don't crash
63
- console.error('Failed to load tools:', error);
67
+ console.error("Failed to load tools:", error);
64
68
  // Fall back to empty array
65
69
  setToolItems([]);
66
70
  } finally {
@@ -86,8 +90,8 @@ export function AIToolSelectorScreen({ onBack, onSelect, version }: AIToolSelect
86
90
 
87
91
  // Footer actions
88
92
  const footerActions = [
89
- { key: 'enter', description: 'Select' },
90
- { key: 'esc', description: 'Back' },
93
+ { key: "enter", description: "Select" },
94
+ { key: "esc", description: "Back" },
91
95
  ];
92
96
 
93
97
  return (
@@ -103,7 +107,9 @@ export function AIToolSelectorScreen({ onBack, onSelect, version }: AIToolSelect
103
107
  {isLoading ? (
104
108
  <Text>Loading tools...</Text>
105
109
  ) : toolItems.length === 0 ? (
106
- <Text color="yellow">No tools available. Please check your configuration.</Text>
110
+ <Text color="yellow">
111
+ No tools available. Please check your configuration.
112
+ </Text>
107
113
  ) : (
108
114
  <Select items={toolItems} onSelect={handleSelect} />
109
115
  )}
@@ -1,16 +1,16 @@
1
- import React, { useState, useCallback, useEffect, useRef } from 'react';
2
- import { Box, Text, useInput } 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 { Input } from '../common/Input.js';
7
- import { useTerminalSize } from '../../hooks/useTerminalSize.js';
8
- import { BRANCH_PREFIXES } from '../../../../config/constants.js';
1
+ import React, { useState, useCallback, useEffect, useRef } from "react";
2
+ import { Box, Text, useInput } 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 { Input } from "../common/Input.js";
7
+ import { useTerminalSize } from "../../hooks/useTerminalSize.js";
8
+ import { BRANCH_PREFIXES } from "../../../../config/constants.js";
9
9
 
10
- type BranchType = 'feature' | 'bugfix' | 'hotfix' | 'release';
11
- type Step = 'type-selection' | 'name-input';
10
+ type BranchType = "feature" | "bugfix" | "hotfix" | "release";
11
+ type Step = "type-selection" | "name-input";
12
12
 
13
- const SPINNER_FRAMES = ['', '', '', '', '', '', '', ''];
13
+ const SPINNER_FRAMES = ["", "", "", "", "", "", "", ""];
14
14
 
15
15
  export interface BranchCreatorScreenProps {
16
16
  onBack: () => void;
@@ -39,11 +39,13 @@ export function BranchCreatorScreen({
39
39
  disableAnimation = false,
40
40
  }: BranchCreatorScreenProps) {
41
41
  const { rows } = useTerminalSize();
42
- const [step, setStep] = useState<Step>('type-selection');
43
- const [selectedType, setSelectedType] = useState<BranchType>('feature');
44
- const [branchName, setBranchName] = useState('');
42
+ const [step, setStep] = useState<Step>("type-selection");
43
+ const [selectedType, setSelectedType] = useState<BranchType>("feature");
44
+ const [branchName, setBranchName] = useState("");
45
45
  const [isCreating, setIsCreating] = useState(false);
46
- const [pendingBranchName, setPendingBranchName] = useState<string | null>(null);
46
+ const [pendingBranchName, setPendingBranchName] = useState<string | null>(
47
+ null,
48
+ );
47
49
  const spinnerIndexRef = useRef(0);
48
50
  const [spinnerIndex, setSpinnerIndex] = useState(0);
49
51
 
@@ -63,40 +65,43 @@ export function BranchCreatorScreen({
63
65
  // Branch type options
64
66
  const branchTypeItems: BranchTypeItem[] = [
65
67
  {
66
- label: 'feature',
67
- value: 'feature',
68
- description: 'New feature development',
68
+ label: "feature",
69
+ value: "feature",
70
+ description: "New feature development",
69
71
  },
70
72
  {
71
- label: 'bugfix',
72
- value: 'bugfix',
73
- description: 'Bug fix',
73
+ label: "bugfix",
74
+ value: "bugfix",
75
+ description: "Bug fix",
74
76
  },
75
77
  {
76
- label: 'hotfix',
77
- value: 'hotfix',
78
- description: 'Critical bug fix',
78
+ label: "hotfix",
79
+ value: "hotfix",
80
+ description: "Critical bug fix",
79
81
  },
80
82
  {
81
- label: 'release',
82
- value: 'release',
83
- description: 'Release preparation',
83
+ label: "release",
84
+ value: "release",
85
+ description: "Release preparation",
84
86
  },
85
87
  ];
86
88
 
87
89
  // Handle branch type selection
88
90
  const handleTypeSelect = useCallback((item: BranchTypeItem) => {
89
91
  setSelectedType(item.value);
90
- setStep('name-input');
92
+ setStep("name-input");
91
93
  }, []);
92
94
 
93
95
  // Handle branch name input
94
- const handleNameChange = useCallback((value: string) => {
95
- if (isCreating) {
96
- return;
97
- }
98
- setBranchName(value);
99
- }, [isCreating]);
96
+ const handleNameChange = useCallback(
97
+ (value: string) => {
98
+ if (isCreating) {
99
+ return;
100
+ }
101
+ setBranchName(value);
102
+ },
103
+ [isCreating],
104
+ );
100
105
 
101
106
  // Handle branch creation
102
107
  const handleCreate = useCallback(async () => {
@@ -109,7 +114,10 @@ export function BranchCreatorScreen({
109
114
  return;
110
115
  }
111
116
 
112
- const prefix = BRANCH_PREFIXES[selectedType.toUpperCase() as keyof typeof BRANCH_PREFIXES];
117
+ const prefix =
118
+ BRANCH_PREFIXES[
119
+ selectedType.toUpperCase() as keyof typeof BRANCH_PREFIXES
120
+ ];
113
121
  const fullBranchName = `${prefix}${trimmedName}`;
114
122
 
115
123
  setIsCreating(true);
@@ -125,18 +133,17 @@ export function BranchCreatorScreen({
125
133
  }, [branchName, selectedType, onCreate, isCreating]);
126
134
 
127
135
  // Footer actions
128
- const footerActions =
129
- isCreating
130
- ? []
131
- : step === 'type-selection'
132
- ? [
133
- { key: 'enter', description: 'Select' },
134
- { key: 'esc', description: 'Back' },
135
- ]
136
- : [
137
- { key: 'enter', description: 'Create' },
138
- { key: 'esc', description: 'Back' },
139
- ];
136
+ const footerActions = isCreating
137
+ ? []
138
+ : step === "type-selection"
139
+ ? [
140
+ { key: "enter", description: "Select" },
141
+ { key: "esc", description: "Back" },
142
+ ]
143
+ : [
144
+ { key: "enter", description: "Create" },
145
+ { key: "esc", description: "Back" },
146
+ ];
140
147
 
141
148
  useEffect(() => {
142
149
  if (!isCreating || disableAnimation) {
@@ -146,7 +153,8 @@ export function BranchCreatorScreen({
146
153
  }
147
154
 
148
155
  const interval = setInterval(() => {
149
- spinnerIndexRef.current = (spinnerIndexRef.current + 1) % SPINNER_FRAMES.length;
156
+ spinnerIndexRef.current =
157
+ (spinnerIndexRef.current + 1) % SPINNER_FRAMES.length;
150
158
  setSpinnerIndex(spinnerIndexRef.current);
151
159
  }, 120);
152
160
 
@@ -167,7 +175,10 @@ export function BranchCreatorScreen({
167
175
  {baseBranch && (
168
176
  <Box marginBottom={1}>
169
177
  <Text>
170
- Base branch: <Text bold color="cyan">{baseBranch}</Text>
178
+ Base branch:{" "}
179
+ <Text bold color="cyan">
180
+ {baseBranch}
181
+ </Text>
171
182
  </Text>
172
183
  </Box>
173
184
  )}
@@ -175,9 +186,9 @@ export function BranchCreatorScreen({
175
186
  <Box flexDirection="column">
176
187
  <Box marginBottom={1}>
177
188
  <Text>
178
- {spinnerFrame}{' '}
189
+ {spinnerFrame}{" "}
179
190
  <Text color="cyan">
180
- Creating branch{' '}
191
+ Creating branch{" "}
181
192
  <Text bold>
182
193
  {pendingBranchName ??
183
194
  `${BRANCH_PREFIXES[selectedType.toUpperCase() as keyof typeof BRANCH_PREFIXES]}${branchName.trim()}`}
@@ -185,9 +196,11 @@ export function BranchCreatorScreen({
185
196
  </Text>
186
197
  </Text>
187
198
  </Box>
188
- <Text color="gray">Please wait while the branch is being created...</Text>
199
+ <Text color="gray">
200
+ Please wait while the branch is being created...
201
+ </Text>
189
202
  </Box>
190
- ) : step === 'type-selection' ? (
203
+ ) : step === "type-selection" ? (
191
204
  <Box flexDirection="column">
192
205
  <Box marginBottom={1}>
193
206
  <Text>Select branch type:</Text>
@@ -198,7 +211,14 @@ export function BranchCreatorScreen({
198
211
  <Box flexDirection="column">
199
212
  <Box marginBottom={1}>
200
213
  <Text>
201
- Branch name prefix: <Text bold>{BRANCH_PREFIXES[selectedType.toUpperCase() as keyof typeof BRANCH_PREFIXES]}</Text>
214
+ Branch name prefix:{" "}
215
+ <Text bold>
216
+ {
217
+ BRANCH_PREFIXES[
218
+ selectedType.toUpperCase() as keyof typeof BRANCH_PREFIXES
219
+ ]
220
+ }
221
+ </Text>
202
222
  </Text>
203
223
  </Box>
204
224
  <Input