code-squad-cli 2.0.0 → 2.1.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/index.js CHANGED
@@ -1,155 +1,384 @@
1
1
  #!/usr/bin/env node
2
-
3
- // dist/index.js
4
- import * as path11 from "path";
5
- import chalk from "chalk";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __esm = (fn, res) => function __init() {
5
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
6
+ };
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
6
11
 
7
12
  // dist/adapters/GitAdapter.js
8
13
  import { exec as execCallback } from "child_process";
9
14
  import { promisify } from "util";
10
15
  import * as fs from "fs";
11
- var exec = promisify(execCallback);
12
- var execOptions = { maxBuffer: 1024 * 1024 };
13
- var GitAdapter = class {
14
- async isGitRepository(workspaceRoot) {
15
- try {
16
- await exec(`cd "${workspaceRoot}" && git rev-parse --git-dir`, execOptions);
17
- return true;
18
- } catch {
19
- return false;
20
- }
21
- }
22
- async getCurrentBranch(workspaceRoot) {
23
- try {
24
- const { stdout } = await exec(`cd "${workspaceRoot}" && git rev-parse --abbrev-ref HEAD`, execOptions);
25
- return stdout.trim();
26
- } catch {
27
- try {
28
- const { stdout } = await exec(`cd "${workspaceRoot}" && git symbolic-ref --short HEAD`, execOptions);
29
- return stdout.trim();
30
- } catch {
31
- return "";
16
+ var exec, execOptions, GitAdapter;
17
+ var init_GitAdapter = __esm({
18
+ "dist/adapters/GitAdapter.js"() {
19
+ "use strict";
20
+ exec = promisify(execCallback);
21
+ execOptions = { maxBuffer: 1024 * 1024 };
22
+ GitAdapter = class {
23
+ async isGitRepository(workspaceRoot) {
24
+ try {
25
+ await exec(`cd "${workspaceRoot}" && git rev-parse --git-dir`, execOptions);
26
+ return true;
27
+ } catch {
28
+ return false;
29
+ }
32
30
  }
33
- }
34
- }
35
- async listWorktrees(workspaceRoot) {
36
- try {
37
- const { stdout } = await exec(`cd "${workspaceRoot}" && git worktree list --porcelain`, execOptions);
38
- const worktrees = [];
39
- const lines = stdout.split("\n").filter((line) => line.trim());
40
- let i = 0;
41
- while (i < lines.length) {
42
- const worktreeLine = lines[i];
43
- const headLine = lines[i + 1];
44
- const branchLine = lines[i + 2];
45
- if (!worktreeLine || !headLine) {
46
- i++;
47
- continue;
31
+ async getCurrentBranch(workspaceRoot) {
32
+ try {
33
+ const { stdout } = await exec(`cd "${workspaceRoot}" && git rev-parse --abbrev-ref HEAD`, execOptions);
34
+ return stdout.trim();
35
+ } catch {
36
+ try {
37
+ const { stdout } = await exec(`cd "${workspaceRoot}" && git symbolic-ref --short HEAD`, execOptions);
38
+ return stdout.trim();
39
+ } catch {
40
+ return "";
41
+ }
48
42
  }
49
- const pathMatch = worktreeLine.match(/^worktree (.+)$/);
50
- const headMatch = headLine.match(/^HEAD (.+)$/);
51
- const branchMatch = branchLine?.match(/^branch refs\/heads\/(.+)$/);
52
- if (pathMatch && headMatch) {
53
- const path12 = pathMatch[1];
54
- const head = headMatch[1];
55
- const branch = branchMatch ? branchMatch[1] : "HEAD";
56
- if (path12 !== workspaceRoot) {
57
- worktrees.push({ path: path12, branch, head });
43
+ }
44
+ async listWorktrees(workspaceRoot) {
45
+ try {
46
+ const { stdout } = await exec(`cd "${workspaceRoot}" && git worktree list --porcelain`, execOptions);
47
+ const worktrees = [];
48
+ const lines = stdout.split("\n").filter((line) => line.trim());
49
+ let i = 0;
50
+ while (i < lines.length) {
51
+ const worktreeLine = lines[i];
52
+ const headLine = lines[i + 1];
53
+ const branchLine = lines[i + 2];
54
+ if (!worktreeLine || !headLine) {
55
+ i++;
56
+ continue;
57
+ }
58
+ const pathMatch = worktreeLine.match(/^worktree (.+)$/);
59
+ const headMatch = headLine.match(/^HEAD (.+)$/);
60
+ const branchMatch = branchLine?.match(/^branch refs\/heads\/(.+)$/);
61
+ if (pathMatch && headMatch) {
62
+ const path13 = pathMatch[1];
63
+ const head = headMatch[1];
64
+ const branch = branchMatch ? branchMatch[1] : "HEAD";
65
+ if (path13 !== workspaceRoot) {
66
+ worktrees.push({ path: path13, branch, head });
67
+ }
68
+ }
69
+ i += 3;
58
70
  }
71
+ return worktrees;
72
+ } catch {
73
+ return [];
59
74
  }
60
- i += 3;
61
75
  }
62
- return worktrees;
63
- } catch {
64
- return [];
65
- }
66
- }
67
- async createWorktree(worktreePath, branch, workspaceRoot) {
68
- await exec(`cd "${workspaceRoot}" && git worktree prune`, execOptions).catch(() => {
69
- });
70
- const parentDir = worktreePath.substring(0, worktreePath.lastIndexOf("/"));
71
- const mkdirCmd = parentDir ? `mkdir -p "${parentDir}" && ` : "";
72
- await exec(`cd "${workspaceRoot}" && ${mkdirCmd}git worktree add -f "${worktreePath}" -b "${branch}"`, execOptions);
76
+ async createWorktree(worktreePath, branch, workspaceRoot) {
77
+ await exec(`cd "${workspaceRoot}" && git worktree prune`, execOptions).catch(() => {
78
+ });
79
+ const parentDir = worktreePath.substring(0, worktreePath.lastIndexOf("/"));
80
+ const mkdirCmd = parentDir ? `mkdir -p "${parentDir}" && ` : "";
81
+ await exec(`cd "${workspaceRoot}" && ${mkdirCmd}git worktree add -f "${worktreePath}" -b "${branch}"`, execOptions);
82
+ }
83
+ async removeWorktree(worktreePath, workspaceRoot, force = false) {
84
+ const forceFlag = force ? " --force" : "";
85
+ try {
86
+ await exec(`cd "${workspaceRoot}" && git worktree remove "${worktreePath}"${forceFlag}`, execOptions);
87
+ } catch (error) {
88
+ throw new Error(`Failed to remove worktree: ${error.message}`);
89
+ }
90
+ }
91
+ async deleteBranch(branchName, workspaceRoot, force = false) {
92
+ const deleteFlag = force ? "-D" : "-d";
93
+ try {
94
+ await exec(`cd "${workspaceRoot}" && git branch ${deleteFlag} "${branchName}"`, execOptions);
95
+ } catch (error) {
96
+ throw new Error(`Failed to delete branch: ${error.message}`);
97
+ }
98
+ }
99
+ async isValidWorktree(path13, workspaceRoot) {
100
+ try {
101
+ await fs.promises.access(path13, fs.constants.R_OK);
102
+ } catch {
103
+ return false;
104
+ }
105
+ try {
106
+ await exec(`cd "${path13}" && git rev-parse --git-dir`, execOptions);
107
+ } catch {
108
+ return false;
109
+ }
110
+ const worktrees = await this.listWorktrees(workspaceRoot);
111
+ return worktrees.some((wt) => wt.path === path13);
112
+ }
113
+ async getWorktreeBranch(worktreePath) {
114
+ try {
115
+ const { stdout } = await exec(`cd "${worktreePath}" && git rev-parse --abbrev-ref HEAD`, execOptions);
116
+ return stdout.trim();
117
+ } catch (error) {
118
+ throw new Error(`Failed to get branch name: ${error.message}`);
119
+ }
120
+ }
121
+ /**
122
+ * 현재 디렉토리가 워크트리인지 확인하고 컨텍스트 반환
123
+ */
124
+ async getWorktreeContext(cwd) {
125
+ let commonDir = null;
126
+ try {
127
+ const { stdout } = await exec(`cd "${cwd}" && git rev-parse --git-common-dir`, execOptions);
128
+ commonDir = stdout.trim();
129
+ } catch {
130
+ return { isWorktree: false, mainRoot: null, currentPath: cwd, branch: null };
131
+ }
132
+ const isWorktree = commonDir !== ".git";
133
+ if (!isWorktree) {
134
+ return { isWorktree: false, mainRoot: cwd, currentPath: cwd, branch: null };
135
+ }
136
+ let mainRoot = null;
137
+ try {
138
+ const { stdout } = await exec(`cd "${cwd}" && git worktree list --porcelain`, execOptions);
139
+ const match = stdout.match(/^worktree (.+)$/m);
140
+ mainRoot = match ? match[1] : null;
141
+ } catch {
142
+ }
143
+ const branch = await this.getWorktreeBranch(cwd).catch(() => null);
144
+ return { isWorktree, mainRoot, currentPath: cwd, branch };
145
+ }
146
+ /**
147
+ * staged 또는 unstaged 변경사항이 있는지 확인 (untracked 제외)
148
+ */
149
+ async hasDirtyState(workspacePath) {
150
+ try {
151
+ const { stdout } = await exec(`cd "${workspacePath}" && git status --porcelain`, execOptions);
152
+ const lines = stdout.split("\n").filter((line) => line.trim());
153
+ const dirtyLines = lines.filter((line) => !line.startsWith("??"));
154
+ return dirtyLines.length > 0;
155
+ } catch {
156
+ return true;
157
+ }
158
+ }
159
+ };
73
160
  }
74
- async removeWorktree(worktreePath, workspaceRoot, force = false) {
75
- const forceFlag = force ? " --force" : "";
76
- try {
77
- await exec(`cd "${workspaceRoot}" && git worktree remove "${worktreePath}"${forceFlag}`, execOptions);
78
- } catch (error) {
79
- throw new Error(`Failed to remove worktree: ${error.message}`);
161
+ });
162
+
163
+ // dist/config.js
164
+ import * as fs9 from "fs";
165
+ import * as os4 from "os";
166
+ import * as path9 from "path";
167
+ async function loadGlobalConfig() {
168
+ try {
169
+ const content = await fs9.promises.readFile(GLOBAL_CONFIG_PATH, "utf-8");
170
+ return JSON.parse(content);
171
+ } catch (error) {
172
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") {
173
+ return {};
80
174
  }
175
+ console.warn(`[Code Squad] Warning: Could not load global config at ${GLOBAL_CONFIG_PATH}.`, error);
176
+ return {};
81
177
  }
82
- async deleteBranch(branchName, workspaceRoot, force = false) {
83
- const deleteFlag = force ? "-D" : "-d";
84
- try {
85
- await exec(`cd "${workspaceRoot}" && git branch ${deleteFlag} "${branchName}"`, execOptions);
86
- } catch (error) {
87
- throw new Error(`Failed to delete branch: ${error.message}`);
88
- }
178
+ }
179
+ async function loadConfig(workspaceRoot) {
180
+ const globalConfig = await loadGlobalConfig();
181
+ const normalizedPath = path9.resolve(workspaceRoot);
182
+ const projectConfig = globalConfig.projects?.[normalizedPath] ?? {};
183
+ const defaults = globalConfig.defaults ?? {};
184
+ return {
185
+ ...defaults,
186
+ ...projectConfig,
187
+ worktreeCopyPatterns: [
188
+ .../* @__PURE__ */ new Set([
189
+ ...defaults.worktreeCopyPatterns ?? [],
190
+ ...projectConfig.worktreeCopyPatterns ?? []
191
+ ])
192
+ ]
193
+ };
194
+ }
195
+ function getWorktreeCopyPatterns(config) {
196
+ return config.worktreeCopyPatterns ?? [];
197
+ }
198
+ var GLOBAL_CONFIG_PATH;
199
+ var init_config = __esm({
200
+ "dist/config.js"() {
201
+ "use strict";
202
+ GLOBAL_CONFIG_PATH = path9.join(os4.homedir(), ".code-squad", "config.json");
89
203
  }
90
- async isValidWorktree(path12, workspaceRoot) {
91
- try {
92
- await fs.promises.access(path12, fs.constants.R_OK);
93
- } catch {
94
- return false;
95
- }
204
+ });
205
+
206
+ // dist/fileUtils.js
207
+ import * as fs10 from "fs";
208
+ import * as path10 from "path";
209
+ import fg from "fast-glob";
210
+ async function copyFilesWithPatterns(sourceRoot, destRoot, patterns) {
211
+ const copied = [];
212
+ const failed = [];
213
+ if (patterns.length === 0) {
214
+ return { copied, failed };
215
+ }
216
+ for (const pattern of patterns) {
96
217
  try {
97
- await exec(`cd "${path12}" && git rev-parse --git-dir`, execOptions);
218
+ const files = await fg(pattern, {
219
+ cwd: sourceRoot,
220
+ absolute: true,
221
+ onlyFiles: true,
222
+ dot: true
223
+ // .env 같은 dotfile도 매칭
224
+ });
225
+ for (const absolutePath of files) {
226
+ try {
227
+ await copySingleFile(absolutePath, sourceRoot, destRoot);
228
+ const relativePath = path10.relative(sourceRoot, absolutePath);
229
+ copied.push(relativePath);
230
+ } catch {
231
+ const relativePath = path10.relative(sourceRoot, absolutePath);
232
+ failed.push(relativePath);
233
+ }
234
+ }
98
235
  } catch {
99
- return false;
100
236
  }
101
- const worktrees = await this.listWorktrees(workspaceRoot);
102
- return worktrees.some((wt) => wt.path === path12);
103
237
  }
104
- async getWorktreeBranch(worktreePath) {
105
- try {
106
- const { stdout } = await exec(`cd "${worktreePath}" && git rev-parse --abbrev-ref HEAD`, execOptions);
107
- return stdout.trim();
108
- } catch (error) {
109
- throw new Error(`Failed to get branch name: ${error.message}`);
110
- }
238
+ return { copied, failed };
239
+ }
240
+ async function copySingleFile(absolutePath, sourceRoot, destRoot) {
241
+ const relativePath = path10.relative(sourceRoot, absolutePath);
242
+ const destPath = path10.join(destRoot, relativePath);
243
+ const destDir = path10.dirname(destPath);
244
+ await fs10.promises.mkdir(destDir, { recursive: true });
245
+ await fs10.promises.copyFile(absolutePath, destPath);
246
+ }
247
+ var init_fileUtils = __esm({
248
+ "dist/fileUtils.js"() {
249
+ "use strict";
111
250
  }
112
- /**
113
- * 현재 디렉토리가 워크트리인지 확인하고 컨텍스트 반환
114
- */
115
- async getWorktreeContext(cwd) {
116
- let commonDir = null;
117
- try {
118
- const { stdout } = await exec(`cd "${cwd}" && git rev-parse --git-common-dir`, execOptions);
119
- commonDir = stdout.trim();
120
- } catch {
121
- return { isWorktree: false, mainRoot: null, currentPath: cwd, branch: null };
122
- }
123
- const isWorktree = commonDir !== ".git";
124
- if (!isWorktree) {
125
- return { isWorktree: false, mainRoot: cwd, currentPath: cwd, branch: null };
251
+ });
252
+
253
+ // dist/tui/App.js
254
+ var App_exports = {};
255
+ __export(App_exports, {
256
+ runTui: () => runTui
257
+ });
258
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
259
+ import { useState, useEffect, useCallback } from "react";
260
+ import { render, Box, Text, useInput, useApp } from "ink";
261
+ import * as path11 from "path";
262
+ import * as os5 from "os";
263
+ function shorten(p) {
264
+ const home = os5.homedir();
265
+ return p.startsWith(home) ? "~" + p.slice(home.length) : p;
266
+ }
267
+ function App({ initialWorktrees, root }) {
268
+ const { exit } = useApp();
269
+ const [view, setView] = useState("list");
270
+ const [worktrees, setWorktrees] = useState(initialWorktrees);
271
+ const [cursor, setCursor] = useState(0);
272
+ const [input, setInput] = useState("");
273
+ const [msg, setMsg] = useState(null);
274
+ const [busy, setBusy] = useState(false);
275
+ useEffect(() => {
276
+ if (!msg)
277
+ return;
278
+ const t = setTimeout(() => setMsg(null), 2e3);
279
+ return () => clearTimeout(t);
280
+ }, [msg]);
281
+ const refresh = useCallback(async () => {
282
+ const wts = await git.listWorktrees(root);
283
+ setWorktrees(wts);
284
+ setCursor((c) => Math.min(c, Math.max(0, wts.length - 1)));
285
+ }, [root]);
286
+ useInput((ch, key) => {
287
+ if (busy)
288
+ return;
289
+ if (view === "list") {
290
+ if (key.upArrow) {
291
+ setCursor((c) => Math.max(0, c - 1));
292
+ } else if (key.downArrow) {
293
+ setCursor((c) => Math.min(worktrees.length - 1, c + 1));
294
+ } else if (key.return && worktrees.length > 0) {
295
+ process.stdout.write(worktrees[cursor].path + "\n");
296
+ exit();
297
+ } else if (ch === "n") {
298
+ setView("create");
299
+ setInput("");
300
+ } else if (ch === "d" && worktrees.length > 0) {
301
+ setView("delete");
302
+ } else if (ch === "q" || key.escape) {
303
+ exit();
304
+ }
305
+ return;
126
306
  }
127
- let mainRoot = null;
128
- try {
129
- const { stdout } = await exec(`cd "${cwd}" && git worktree list --porcelain`, execOptions);
130
- const match = stdout.match(/^worktree (.+)$/m);
131
- mainRoot = match ? match[1] : null;
132
- } catch {
307
+ if (view === "create") {
308
+ if (key.escape) {
309
+ setView("list");
310
+ return;
311
+ }
312
+ if (key.return && input.trim()) {
313
+ const name = input.trim();
314
+ setBusy(true);
315
+ const base = path11.join(path11.dirname(root), `${path11.basename(root)}.worktree`);
316
+ const wtPath = path11.join(base, name);
317
+ git.createWorktree(wtPath, name, root).then(async () => {
318
+ const config = await loadConfig(root);
319
+ const patterns = getWorktreeCopyPatterns(config);
320
+ if (patterns.length > 0) {
321
+ await copyFilesWithPatterns(root, wtPath, patterns);
322
+ }
323
+ process.stdout.write(wtPath + "\n");
324
+ exit();
325
+ }).catch((e) => {
326
+ setMsg({ text: e.message, color: "red" });
327
+ setView("list");
328
+ setBusy(false);
329
+ });
330
+ return;
331
+ }
332
+ if (key.backspace || key.delete) {
333
+ setInput((v) => v.slice(0, -1));
334
+ return;
335
+ }
336
+ if (ch && !key.ctrl && !key.meta) {
337
+ setInput((v) => v + ch);
338
+ }
339
+ return;
133
340
  }
134
- const branch = await this.getWorktreeBranch(cwd).catch(() => null);
135
- return { isWorktree, mainRoot, currentPath: cwd, branch };
136
- }
137
- /**
138
- * staged 또는 unstaged 변경사항이 있는지 확인 (untracked 제외)
139
- */
140
- async hasDirtyState(workspacePath) {
141
- try {
142
- const { stdout } = await exec(`cd "${workspacePath}" && git status --porcelain`, execOptions);
143
- const lines = stdout.split("\n").filter((line) => line.trim());
144
- const dirtyLines = lines.filter((line) => !line.startsWith("??"));
145
- return dirtyLines.length > 0;
146
- } catch {
147
- return true;
341
+ if (view === "delete") {
342
+ if (ch === "y" || key.return) {
343
+ const target = worktrees[cursor];
344
+ setBusy(true);
345
+ git.removeWorktree(target.path, root, true).then(() => git.deleteBranch(target.branch, root, true)).then(async () => {
346
+ setMsg({ text: `Deleted ${target.branch}`, color: "green" });
347
+ await refresh();
348
+ setBusy(false);
349
+ setView("list");
350
+ }).catch((e) => {
351
+ setMsg({ text: e.message, color: "red" });
352
+ setBusy(false);
353
+ setView("list");
354
+ });
355
+ } else if (ch === "n" || key.escape) {
356
+ setView("list");
357
+ }
148
358
  }
359
+ });
360
+ return _jsxs(Box, { flexDirection: "column", paddingX: 1, paddingY: 1, children: [_jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: "Code Squad" }), busy && _jsx(Text, { color: "yellow", children: " ..." })] }), view === "list" && (worktrees.length === 0 ? _jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { dimColor: true, children: "No worktrees yet. Press " }), _jsx(Text, { color: "yellow", children: "n" }), _jsx(Text, { dimColor: true, children: " to create one." })] }) : _jsx(Box, { flexDirection: "column", marginBottom: 1, children: worktrees.map((wt, i) => _jsxs(Box, { children: [_jsx(Text, { color: i === cursor ? "cyan" : void 0, children: i === cursor ? "\u276F " : " " }), _jsx(Text, { bold: i === cursor, children: wt.branch.padEnd(20) }), _jsxs(Text, { dimColor: true, children: [" ", shorten(wt.path)] })] }, wt.path)) })), view === "create" && _jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsx(Text, { bold: true, children: "New Worktree" }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { dimColor: true, children: "> " }), _jsx(Text, { color: "cyan", children: input }), _jsx(Text, { dimColor: true, children: "\u2588" })] })] }), view === "delete" && worktrees[cursor] && _jsx(Box, { flexDirection: "column", marginBottom: 1, children: _jsxs(Text, { children: ["Delete ", _jsx(Text, { bold: true, color: "yellow", children: worktrees[cursor].branch }), "?"] }) }), msg && _jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: msg.color, children: msg.text }) }), _jsxs(Box, { gap: 2, children: [view === "list" && _jsxs(_Fragment, { children: [worktrees.length > 0 && _jsxs(_Fragment, { children: [_jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: "\u2191\u2193" }), " ", _jsx(Text, { dimColor: true, children: "navigate" })] }), _jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: "\u21B5" }), " ", _jsx(Text, { dimColor: true, children: "switch" })] })] }), _jsxs(Text, { children: [_jsx(Text, { color: "yellow", children: "n" }), " ", _jsx(Text, { dimColor: true, children: "new" })] }), worktrees.length > 0 && _jsxs(Text, { children: [_jsx(Text, { color: "yellow", children: "d" }), " ", _jsx(Text, { dimColor: true, children: "delete" })] }), _jsxs(Text, { children: [_jsx(Text, { color: "yellow", children: "q" }), " ", _jsx(Text, { dimColor: true, children: "quit" })] })] }), view === "create" && _jsxs(_Fragment, { children: [_jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: "\u21B5" }), " ", _jsx(Text, { dimColor: true, children: "create" })] }), _jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: "esc" }), " ", _jsx(Text, { dimColor: true, children: "cancel" })] })] }), view === "delete" && _jsxs(_Fragment, { children: [_jsxs(Text, { children: [_jsx(Text, { color: "yellow", children: "y" }), " ", _jsx(Text, { dimColor: true, children: "confirm" })] }), _jsxs(Text, { children: [_jsx(Text, { color: "yellow", children: "n" }), " ", _jsx(Text, { dimColor: true, children: "cancel" })] })] })] })] });
361
+ }
362
+ async function runTui(workspaceRoot) {
363
+ const worktrees = await git.listWorktrees(workspaceRoot);
364
+ const { waitUntilExit } = render(_jsx(App, { initialWorktrees: worktrees, root: workspaceRoot }), { stdout: process.stderr });
365
+ await waitUntilExit();
366
+ }
367
+ var git;
368
+ var init_App = __esm({
369
+ "dist/tui/App.js"() {
370
+ "use strict";
371
+ init_GitAdapter();
372
+ init_config();
373
+ init_fileUtils();
374
+ git = new GitAdapter();
149
375
  }
150
- };
376
+ });
151
377
 
152
378
  // dist/index.js
379
+ init_GitAdapter();
380
+ import * as path12 from "path";
381
+ import chalk from "chalk";
153
382
  import { confirm } from "@inquirer/prompts";
154
383
 
155
384
  // dist/flip/server/Server.js
@@ -433,9 +662,9 @@ var filenameMap = {
433
662
  "CODEOWNERS": "gitignore"
434
663
  };
435
664
  function detectLanguage(filePath) {
436
- const basename2 = path2.basename(filePath);
437
- if (filenameMap[basename2]) {
438
- return filenameMap[basename2];
665
+ const basename3 = path2.basename(filePath);
666
+ if (filenameMap[basename3]) {
667
+ return filenameMap[basename3];
439
668
  }
440
669
  const ext = path2.extname(filePath).slice(1).toLowerCase();
441
670
  if (extensionMap[ext]) {
@@ -1744,86 +1973,9 @@ fi
1744
1973
  console.log("");
1745
1974
  }
1746
1975
 
1747
- // dist/config.js
1748
- import * as fs9 from "fs";
1749
- import * as os4 from "os";
1750
- import * as path9 from "path";
1751
- var GLOBAL_CONFIG_PATH = path9.join(os4.homedir(), ".code-squad", "config.json");
1752
- async function loadGlobalConfig() {
1753
- try {
1754
- const content = await fs9.promises.readFile(GLOBAL_CONFIG_PATH, "utf-8");
1755
- return JSON.parse(content);
1756
- } catch (error) {
1757
- if (error instanceof Error && "code" in error && error.code === "ENOENT") {
1758
- return {};
1759
- }
1760
- console.warn(`[Code Squad] Warning: Could not load global config at ${GLOBAL_CONFIG_PATH}.`, error);
1761
- return {};
1762
- }
1763
- }
1764
- async function loadConfig(workspaceRoot) {
1765
- const globalConfig = await loadGlobalConfig();
1766
- const normalizedPath = path9.resolve(workspaceRoot);
1767
- const projectConfig = globalConfig.projects?.[normalizedPath] ?? {};
1768
- const defaults = globalConfig.defaults ?? {};
1769
- return {
1770
- ...defaults,
1771
- ...projectConfig,
1772
- worktreeCopyPatterns: [
1773
- .../* @__PURE__ */ new Set([
1774
- ...defaults.worktreeCopyPatterns ?? [],
1775
- ...projectConfig.worktreeCopyPatterns ?? []
1776
- ])
1777
- ]
1778
- };
1779
- }
1780
- function getWorktreeCopyPatterns(config) {
1781
- return config.worktreeCopyPatterns ?? [];
1782
- }
1783
-
1784
- // dist/fileUtils.js
1785
- import * as fs10 from "fs";
1786
- import * as path10 from "path";
1787
- import fg from "fast-glob";
1788
- async function copyFilesWithPatterns(sourceRoot, destRoot, patterns) {
1789
- const copied = [];
1790
- const failed = [];
1791
- if (patterns.length === 0) {
1792
- return { copied, failed };
1793
- }
1794
- for (const pattern of patterns) {
1795
- try {
1796
- const files = await fg(pattern, {
1797
- cwd: sourceRoot,
1798
- absolute: true,
1799
- onlyFiles: true,
1800
- dot: true
1801
- // .env 같은 dotfile도 매칭
1802
- });
1803
- for (const absolutePath of files) {
1804
- try {
1805
- await copySingleFile(absolutePath, sourceRoot, destRoot);
1806
- const relativePath = path10.relative(sourceRoot, absolutePath);
1807
- copied.push(relativePath);
1808
- } catch {
1809
- const relativePath = path10.relative(sourceRoot, absolutePath);
1810
- failed.push(relativePath);
1811
- }
1812
- }
1813
- } catch {
1814
- }
1815
- }
1816
- return { copied, failed };
1817
- }
1818
- async function copySingleFile(absolutePath, sourceRoot, destRoot) {
1819
- const relativePath = path10.relative(sourceRoot, absolutePath);
1820
- const destPath = path10.join(destRoot, relativePath);
1821
- const destDir = path10.dirname(destPath);
1822
- await fs10.promises.mkdir(destDir, { recursive: true });
1823
- await fs10.promises.copyFile(absolutePath, destPath);
1824
- }
1825
-
1826
1976
  // dist/index.js
1977
+ init_config();
1978
+ init_fileUtils();
1827
1979
  process.on("SIGINT", () => {
1828
1980
  process.exit(130);
1829
1981
  });
@@ -1852,8 +2004,15 @@ async function main() {
1852
2004
  await quitWorktreeCommand();
1853
2005
  break;
1854
2006
  case "list":
1855
- default:
1856
2007
  await listWorktrees(workspaceRoot);
2008
+ break;
2009
+ default:
2010
+ if (process.stdin.isTTY) {
2011
+ const { runTui: runTui2 } = await Promise.resolve().then(() => (init_App(), App_exports));
2012
+ await runTui2(workspaceRoot);
2013
+ } else {
2014
+ await listWorktrees(workspaceRoot);
2015
+ }
1857
2016
  }
1858
2017
  }
1859
2018
  async function findGitRoot(cwd) {
@@ -1871,7 +2030,7 @@ csq() {
1871
2030
  fi
1872
2031
 
1873
2032
  local output
1874
- output=$(command csq "$@" 2>&1)
2033
+ output=$(command csq "$@")
1875
2034
  local exit_code=$?
1876
2035
 
1877
2036
  if [[ $exit_code -ne 0 ]]; then
@@ -1909,9 +2068,9 @@ async function createWorktreeCommand(workspaceRoot, args) {
1909
2068
  console.error(chalk.dim("Usage: csq new <name>"));
1910
2069
  process.exit(1);
1911
2070
  }
1912
- const repoName = path11.basename(workspaceRoot);
1913
- const defaultBasePath = path11.join(path11.dirname(workspaceRoot), `${repoName}.worktree`);
1914
- const worktreePath = path11.join(defaultBasePath, name);
2071
+ const repoName = path12.basename(workspaceRoot);
2072
+ const defaultBasePath = path12.join(path12.dirname(workspaceRoot), `${repoName}.worktree`);
2073
+ const worktreePath = path12.join(defaultBasePath, name);
1915
2074
  try {
1916
2075
  await gitAdapter.createWorktree(worktreePath, name, workspaceRoot);
1917
2076
  console.log(chalk.green(`\u2713 Created worktree: ${name}`));
@@ -0,0 +1 @@
1
+ export declare function runTui(workspaceRoot: string): Promise<void>;