code-squad-cli 2.0.0 → 2.1.1
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 +379 -214
- package/dist/tui/App.d.ts +1 -0
- package/dist/tui/App.js +132 -0
- package/package.json +7 -6
- package/dist/flip-ui/dist/assets/index-DYY1gRRa.css +0 -1
- package/dist/flip-ui/dist/assets/index-KAtdqB2p.js +0 -217
- package/dist/flip-ui/dist/index.html +0 -13
package/dist/index.js
CHANGED
|
@@ -1,155 +1,390 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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
|
|
12
|
-
var
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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
|
|
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
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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
|
+
import * as fs11 from "fs";
|
|
264
|
+
import * as tty from "tty";
|
|
265
|
+
function shorten(p) {
|
|
266
|
+
const home = os5.homedir();
|
|
267
|
+
return p.startsWith(home) ? "~" + p.slice(home.length) : p;
|
|
268
|
+
}
|
|
269
|
+
function App({ initialWorktrees, root }) {
|
|
270
|
+
const { exit } = useApp();
|
|
271
|
+
const [view, setView] = useState("list");
|
|
272
|
+
const [worktrees, setWorktrees] = useState(initialWorktrees);
|
|
273
|
+
const [cursor, setCursor] = useState(0);
|
|
274
|
+
const [input, setInput] = useState("");
|
|
275
|
+
const [msg, setMsg] = useState(null);
|
|
276
|
+
const [busy, setBusy] = useState(false);
|
|
277
|
+
useEffect(() => {
|
|
278
|
+
if (!msg)
|
|
279
|
+
return;
|
|
280
|
+
const t = setTimeout(() => setMsg(null), 2e3);
|
|
281
|
+
return () => clearTimeout(t);
|
|
282
|
+
}, [msg]);
|
|
283
|
+
const refresh = useCallback(async () => {
|
|
284
|
+
const wts = await git.listWorktrees(root);
|
|
285
|
+
setWorktrees(wts);
|
|
286
|
+
setCursor((c) => Math.min(c, Math.max(0, wts.length - 1)));
|
|
287
|
+
}, [root]);
|
|
288
|
+
useInput((ch, key) => {
|
|
289
|
+
if (busy)
|
|
290
|
+
return;
|
|
291
|
+
if (view === "list") {
|
|
292
|
+
if (key.upArrow) {
|
|
293
|
+
setCursor((c) => Math.max(0, c - 1));
|
|
294
|
+
} else if (key.downArrow) {
|
|
295
|
+
setCursor((c) => Math.min(worktrees.length - 1, c + 1));
|
|
296
|
+
} else if (key.return && worktrees.length > 0) {
|
|
297
|
+
process.stdout.write(worktrees[cursor].path + "\n");
|
|
298
|
+
exit();
|
|
299
|
+
} else if (ch === "n") {
|
|
300
|
+
setView("create");
|
|
301
|
+
setInput("");
|
|
302
|
+
} else if (ch === "d" && worktrees.length > 0) {
|
|
303
|
+
setView("delete");
|
|
304
|
+
} else if (ch === "q" || key.escape) {
|
|
305
|
+
exit();
|
|
306
|
+
}
|
|
307
|
+
return;
|
|
126
308
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
309
|
+
if (view === "create") {
|
|
310
|
+
if (key.escape) {
|
|
311
|
+
setView("list");
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
if (key.return && input.trim()) {
|
|
315
|
+
const name = input.trim();
|
|
316
|
+
setBusy(true);
|
|
317
|
+
const base = path11.join(path11.dirname(root), `${path11.basename(root)}.worktree`);
|
|
318
|
+
const wtPath = path11.join(base, name);
|
|
319
|
+
git.createWorktree(wtPath, name, root).then(async () => {
|
|
320
|
+
const config = await loadConfig(root);
|
|
321
|
+
const patterns = getWorktreeCopyPatterns(config);
|
|
322
|
+
if (patterns.length > 0) {
|
|
323
|
+
await copyFilesWithPatterns(root, wtPath, patterns);
|
|
324
|
+
}
|
|
325
|
+
process.stdout.write(wtPath + "\n");
|
|
326
|
+
exit();
|
|
327
|
+
}).catch((e) => {
|
|
328
|
+
setMsg({ text: e.message, color: "red" });
|
|
329
|
+
setView("list");
|
|
330
|
+
setBusy(false);
|
|
331
|
+
});
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
if (key.backspace || key.delete) {
|
|
335
|
+
setInput((v) => v.slice(0, -1));
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
if (ch && !key.ctrl && !key.meta) {
|
|
339
|
+
setInput((v) => v + ch);
|
|
340
|
+
}
|
|
341
|
+
return;
|
|
133
342
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
343
|
+
if (view === "delete") {
|
|
344
|
+
if (ch === "y" || key.return) {
|
|
345
|
+
const target = worktrees[cursor];
|
|
346
|
+
setBusy(true);
|
|
347
|
+
git.removeWorktree(target.path, root, true).then(() => git.deleteBranch(target.branch, root, true)).then(async () => {
|
|
348
|
+
setMsg({ text: `Deleted ${target.branch}`, color: "green" });
|
|
349
|
+
await refresh();
|
|
350
|
+
setBusy(false);
|
|
351
|
+
setView("list");
|
|
352
|
+
}).catch((e) => {
|
|
353
|
+
setMsg({ text: e.message, color: "red" });
|
|
354
|
+
setBusy(false);
|
|
355
|
+
setView("list");
|
|
356
|
+
});
|
|
357
|
+
} else if (ch === "n" || key.escape) {
|
|
358
|
+
setView("list");
|
|
359
|
+
}
|
|
148
360
|
}
|
|
361
|
+
});
|
|
362
|
+
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" })] })] })] })] });
|
|
363
|
+
}
|
|
364
|
+
async function runTui(workspaceRoot) {
|
|
365
|
+
const worktrees = await git.listWorktrees(workspaceRoot);
|
|
366
|
+
const fd = fs11.openSync("/dev/tty", "w");
|
|
367
|
+
const ttyStream = new tty.WriteStream(fd);
|
|
368
|
+
const { waitUntilExit } = render(_jsx(App, { initialWorktrees: worktrees, root: workspaceRoot }), { stdout: ttyStream });
|
|
369
|
+
await waitUntilExit();
|
|
370
|
+
ttyStream.destroy();
|
|
371
|
+
fs11.closeSync(fd);
|
|
372
|
+
}
|
|
373
|
+
var git;
|
|
374
|
+
var init_App = __esm({
|
|
375
|
+
"dist/tui/App.js"() {
|
|
376
|
+
"use strict";
|
|
377
|
+
init_GitAdapter();
|
|
378
|
+
init_config();
|
|
379
|
+
init_fileUtils();
|
|
380
|
+
git = new GitAdapter();
|
|
149
381
|
}
|
|
150
|
-
};
|
|
382
|
+
});
|
|
151
383
|
|
|
152
384
|
// dist/index.js
|
|
385
|
+
init_GitAdapter();
|
|
386
|
+
import * as path12 from "path";
|
|
387
|
+
import chalk from "chalk";
|
|
153
388
|
import { confirm } from "@inquirer/prompts";
|
|
154
389
|
|
|
155
390
|
// dist/flip/server/Server.js
|
|
@@ -433,9 +668,9 @@ var filenameMap = {
|
|
|
433
668
|
"CODEOWNERS": "gitignore"
|
|
434
669
|
};
|
|
435
670
|
function detectLanguage(filePath) {
|
|
436
|
-
const
|
|
437
|
-
if (filenameMap[
|
|
438
|
-
return filenameMap[
|
|
671
|
+
const basename3 = path2.basename(filePath);
|
|
672
|
+
if (filenameMap[basename3]) {
|
|
673
|
+
return filenameMap[basename3];
|
|
439
674
|
}
|
|
440
675
|
const ext = path2.extname(filePath).slice(1).toLowerCase();
|
|
441
676
|
if (extensionMap[ext]) {
|
|
@@ -1500,8 +1735,8 @@ function formatTime() {
|
|
|
1500
1735
|
}
|
|
1501
1736
|
function getSessionId() {
|
|
1502
1737
|
try {
|
|
1503
|
-
const
|
|
1504
|
-
return
|
|
1738
|
+
const tty2 = execSync2("tty", { encoding: "utf-8" }).trim();
|
|
1739
|
+
return tty2;
|
|
1505
1740
|
} catch {
|
|
1506
1741
|
return `session-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
1507
1742
|
}
|
|
@@ -1744,86 +1979,9 @@ fi
|
|
|
1744
1979
|
console.log("");
|
|
1745
1980
|
}
|
|
1746
1981
|
|
|
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
1982
|
// dist/index.js
|
|
1983
|
+
init_config();
|
|
1984
|
+
init_fileUtils();
|
|
1827
1985
|
process.on("SIGINT", () => {
|
|
1828
1986
|
process.exit(130);
|
|
1829
1987
|
});
|
|
@@ -1852,8 +2010,15 @@ async function main() {
|
|
|
1852
2010
|
await quitWorktreeCommand();
|
|
1853
2011
|
break;
|
|
1854
2012
|
case "list":
|
|
1855
|
-
default:
|
|
1856
2013
|
await listWorktrees(workspaceRoot);
|
|
2014
|
+
break;
|
|
2015
|
+
default:
|
|
2016
|
+
if (process.stdin.isTTY) {
|
|
2017
|
+
const { runTui: runTui2 } = await Promise.resolve().then(() => (init_App(), App_exports));
|
|
2018
|
+
await runTui2(workspaceRoot);
|
|
2019
|
+
} else {
|
|
2020
|
+
await listWorktrees(workspaceRoot);
|
|
2021
|
+
}
|
|
1857
2022
|
}
|
|
1858
2023
|
}
|
|
1859
2024
|
async function findGitRoot(cwd) {
|
|
@@ -1909,9 +2074,9 @@ async function createWorktreeCommand(workspaceRoot, args) {
|
|
|
1909
2074
|
console.error(chalk.dim("Usage: csq new <name>"));
|
|
1910
2075
|
process.exit(1);
|
|
1911
2076
|
}
|
|
1912
|
-
const repoName =
|
|
1913
|
-
const defaultBasePath =
|
|
1914
|
-
const worktreePath =
|
|
2077
|
+
const repoName = path12.basename(workspaceRoot);
|
|
2078
|
+
const defaultBasePath = path12.join(path12.dirname(workspaceRoot), `${repoName}.worktree`);
|
|
2079
|
+
const worktreePath = path12.join(defaultBasePath, name);
|
|
1915
2080
|
try {
|
|
1916
2081
|
await gitAdapter.createWorktree(worktreePath, name, workspaceRoot);
|
|
1917
2082
|
console.log(chalk.green(`\u2713 Created worktree: ${name}`));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runTui(workspaceRoot: string): Promise<void>;
|