code-squad-cli 1.2.3 → 1.2.5
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/adapters/GitAdapter.d.ts +13 -0
- package/dist/adapters/GitAdapter.js +124 -99
- package/dist/flip/routes/file.js +122 -34
- package/dist/flip/routes/files.js +29 -2
- package/dist/flip-ui/dist/assets/index-DTf1sRAX.css +1 -0
- package/dist/flip-ui/dist/assets/index-DmJdj9bX.js +154 -0
- package/dist/flip-ui/dist/index.html +2 -2
- package/dist/index.js +311 -152
- package/package.json +1 -1
- package/dist/flip-ui/dist/assets/index-BqoCRrTA.css +0 -1
- package/dist/flip-ui/dist/assets/index-CgNq-jV5.js +0 -69
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>Flip</title>
|
|
7
|
-
<script type="module" crossorigin src="/assets/index-
|
|
8
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
7
|
+
<script type="module" crossorigin src="/assets/index-DmJdj9bX.js"></script>
|
|
8
|
+
<link rel="stylesheet" crossorigin href="/assets/index-DTf1sRAX.css">
|
|
9
9
|
</head>
|
|
10
10
|
<body>
|
|
11
11
|
<div id="root"></div>
|
package/dist/index.js
CHANGED
|
@@ -8,98 +8,76 @@ import * as crypto from "crypto";
|
|
|
8
8
|
import chalk2 from "chalk";
|
|
9
9
|
|
|
10
10
|
// dist/adapters/GitAdapter.js
|
|
11
|
-
import { exec } from "child_process";
|
|
11
|
+
import { exec as execCallback } from "child_process";
|
|
12
|
+
import { promisify } from "util";
|
|
12
13
|
import * as fs from "fs";
|
|
14
|
+
var exec = promisify(execCallback);
|
|
15
|
+
var execOptions = { maxBuffer: 1024 * 1024 };
|
|
13
16
|
var GitAdapter = class {
|
|
14
17
|
async isGitRepository(workspaceRoot) {
|
|
15
|
-
|
|
16
|
-
exec(`cd "${workspaceRoot}" && git rev-parse --git-dir`,
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
try {
|
|
19
|
+
await exec(`cd "${workspaceRoot}" && git rev-parse --git-dir`, execOptions);
|
|
20
|
+
return true;
|
|
21
|
+
} catch {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
20
24
|
}
|
|
21
25
|
async getCurrentBranch(workspaceRoot) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
if (error) {
|
|
25
|
-
reject(error);
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
resolve2(stdout.trim());
|
|
29
|
-
});
|
|
30
|
-
});
|
|
26
|
+
const { stdout } = await exec(`cd "${workspaceRoot}" && git rev-parse --abbrev-ref HEAD`, execOptions);
|
|
27
|
+
return stdout.trim();
|
|
31
28
|
}
|
|
32
29
|
async listWorktrees(workspaceRoot) {
|
|
33
|
-
|
|
34
|
-
exec(`cd "${workspaceRoot}" && git worktree list --porcelain`,
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
30
|
+
try {
|
|
31
|
+
const { stdout } = await exec(`cd "${workspaceRoot}" && git worktree list --porcelain`, execOptions);
|
|
32
|
+
const worktrees = [];
|
|
33
|
+
const lines = stdout.split("\n").filter((line) => line.trim());
|
|
34
|
+
let i = 0;
|
|
35
|
+
while (i < lines.length) {
|
|
36
|
+
const worktreeLine = lines[i];
|
|
37
|
+
const headLine = lines[i + 1];
|
|
38
|
+
const branchLine = lines[i + 2];
|
|
39
|
+
if (!worktreeLine || !headLine) {
|
|
40
|
+
i++;
|
|
41
|
+
continue;
|
|
38
42
|
}
|
|
39
|
-
const
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const
|
|
44
|
-
const
|
|
45
|
-
const
|
|
46
|
-
if (
|
|
47
|
-
|
|
48
|
-
continue;
|
|
49
|
-
}
|
|
50
|
-
const pathMatch = worktreeLine.match(/^worktree (.+)$/);
|
|
51
|
-
const headMatch = headLine.match(/^HEAD (.+)$/);
|
|
52
|
-
const branchMatch = branchLine?.match(/^branch refs\/heads\/(.+)$/);
|
|
53
|
-
if (pathMatch && headMatch) {
|
|
54
|
-
const path11 = pathMatch[1];
|
|
55
|
-
const head = headMatch[1];
|
|
56
|
-
const branch = branchMatch ? branchMatch[1] : "HEAD";
|
|
57
|
-
if (path11 !== workspaceRoot) {
|
|
58
|
-
worktrees.push({ path: path11, branch, head });
|
|
59
|
-
}
|
|
43
|
+
const pathMatch = worktreeLine.match(/^worktree (.+)$/);
|
|
44
|
+
const headMatch = headLine.match(/^HEAD (.+)$/);
|
|
45
|
+
const branchMatch = branchLine?.match(/^branch refs\/heads\/(.+)$/);
|
|
46
|
+
if (pathMatch && headMatch) {
|
|
47
|
+
const path11 = pathMatch[1];
|
|
48
|
+
const head = headMatch[1];
|
|
49
|
+
const branch = branchMatch ? branchMatch[1] : "HEAD";
|
|
50
|
+
if (path11 !== workspaceRoot) {
|
|
51
|
+
worktrees.push({ path: path11, branch, head });
|
|
60
52
|
}
|
|
61
|
-
i += 3;
|
|
62
53
|
}
|
|
63
|
-
|
|
64
|
-
}
|
|
65
|
-
|
|
54
|
+
i += 3;
|
|
55
|
+
}
|
|
56
|
+
return worktrees;
|
|
57
|
+
} catch {
|
|
58
|
+
return [];
|
|
59
|
+
}
|
|
66
60
|
}
|
|
67
61
|
async createWorktree(worktreePath, branch, workspaceRoot) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
exec(`cd "${workspaceRoot}" && ${mkdirCmd}git worktree add "${worktreePath}" -b "${branch}"`, { maxBuffer: 1024 * 1024 }, (error) => {
|
|
72
|
-
if (error) {
|
|
73
|
-
reject(error);
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
resolve2();
|
|
77
|
-
});
|
|
78
|
-
});
|
|
62
|
+
const parentDir = worktreePath.substring(0, worktreePath.lastIndexOf("/"));
|
|
63
|
+
const mkdirCmd = parentDir ? `mkdir -p "${parentDir}" && ` : "";
|
|
64
|
+
await exec(`cd "${workspaceRoot}" && ${mkdirCmd}git worktree add "${worktreePath}" -b "${branch}"`, execOptions);
|
|
79
65
|
}
|
|
80
66
|
async removeWorktree(worktreePath, workspaceRoot, force = false) {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
exec(`cd "${workspaceRoot}" && git worktree remove "${worktreePath}"${forceFlag}`,
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
88
|
-
resolve2();
|
|
89
|
-
});
|
|
90
|
-
});
|
|
67
|
+
const forceFlag = force ? " --force" : "";
|
|
68
|
+
try {
|
|
69
|
+
await exec(`cd "${workspaceRoot}" && git worktree remove "${worktreePath}"${forceFlag}`, execOptions);
|
|
70
|
+
} catch (error) {
|
|
71
|
+
throw new Error(`Failed to remove worktree: ${error.message}`);
|
|
72
|
+
}
|
|
91
73
|
}
|
|
92
74
|
async deleteBranch(branchName, workspaceRoot, force = false) {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
exec(`cd "${workspaceRoot}" && git branch ${deleteFlag} "${branchName}"`,
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
}
|
|
100
|
-
resolve2();
|
|
101
|
-
});
|
|
102
|
-
});
|
|
75
|
+
const deleteFlag = force ? "-D" : "-d";
|
|
76
|
+
try {
|
|
77
|
+
await exec(`cd "${workspaceRoot}" && git branch ${deleteFlag} "${branchName}"`, execOptions);
|
|
78
|
+
} catch (error) {
|
|
79
|
+
throw new Error(`Failed to delete branch: ${error.message}`);
|
|
80
|
+
}
|
|
103
81
|
}
|
|
104
82
|
async isValidWorktree(path11, workspaceRoot) {
|
|
105
83
|
try {
|
|
@@ -107,28 +85,59 @@ var GitAdapter = class {
|
|
|
107
85
|
} catch {
|
|
108
86
|
return false;
|
|
109
87
|
}
|
|
110
|
-
|
|
111
|
-
exec(`cd "${path11}" && git rev-parse --git-dir`,
|
|
112
|
-
|
|
113
|
-
});
|
|
114
|
-
});
|
|
115
|
-
if (!isGitRepo) {
|
|
88
|
+
try {
|
|
89
|
+
await exec(`cd "${path11}" && git rev-parse --git-dir`, execOptions);
|
|
90
|
+
} catch {
|
|
116
91
|
return false;
|
|
117
92
|
}
|
|
118
93
|
const worktrees = await this.listWorktrees(workspaceRoot);
|
|
119
94
|
return worktrees.some((wt) => wt.path === path11);
|
|
120
95
|
}
|
|
121
96
|
async getWorktreeBranch(worktreePath) {
|
|
122
|
-
|
|
123
|
-
exec(`cd "${worktreePath}" && git rev-parse --abbrev-ref HEAD`,
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
97
|
+
try {
|
|
98
|
+
const { stdout } = await exec(`cd "${worktreePath}" && git rev-parse --abbrev-ref HEAD`, execOptions);
|
|
99
|
+
return stdout.trim();
|
|
100
|
+
} catch (error) {
|
|
101
|
+
throw new Error(`Failed to get branch name: ${error.message}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* 현재 디렉토리가 워크트리인지 확인하고 컨텍스트 반환
|
|
106
|
+
*/
|
|
107
|
+
async getWorktreeContext(cwd) {
|
|
108
|
+
let commonDir = null;
|
|
109
|
+
try {
|
|
110
|
+
const { stdout } = await exec(`cd "${cwd}" && git rev-parse --git-common-dir`, execOptions);
|
|
111
|
+
commonDir = stdout.trim();
|
|
112
|
+
} catch {
|
|
113
|
+
return { isWorktree: false, mainRoot: null, currentPath: cwd, branch: null };
|
|
114
|
+
}
|
|
115
|
+
const isWorktree = commonDir !== ".git";
|
|
116
|
+
if (!isWorktree) {
|
|
117
|
+
return { isWorktree: false, mainRoot: cwd, currentPath: cwd, branch: null };
|
|
118
|
+
}
|
|
119
|
+
let mainRoot = null;
|
|
120
|
+
try {
|
|
121
|
+
const { stdout } = await exec(`cd "${cwd}" && git worktree list --porcelain`, execOptions);
|
|
122
|
+
const match = stdout.match(/^worktree (.+)$/m);
|
|
123
|
+
mainRoot = match ? match[1] : null;
|
|
124
|
+
} catch {
|
|
125
|
+
}
|
|
126
|
+
const branch = await this.getWorktreeBranch(cwd).catch(() => null);
|
|
127
|
+
return { isWorktree, mainRoot, currentPath: cwd, branch };
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* staged 또는 unstaged 변경사항이 있는지 확인 (untracked 제외)
|
|
131
|
+
*/
|
|
132
|
+
async hasDirtyState(workspacePath) {
|
|
133
|
+
try {
|
|
134
|
+
const { stdout } = await exec(`cd "${workspacePath}" && git status --porcelain`, execOptions);
|
|
135
|
+
const lines = stdout.split("\n").filter((line) => line.trim());
|
|
136
|
+
const dirtyLines = lines.filter((line) => !line.startsWith("??"));
|
|
137
|
+
return dirtyLines.length > 0;
|
|
138
|
+
} catch {
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
132
141
|
}
|
|
133
142
|
};
|
|
134
143
|
|
|
@@ -401,6 +410,9 @@ async function confirmDeleteLocal(threadName) {
|
|
|
401
410
|
});
|
|
402
411
|
}
|
|
403
412
|
|
|
413
|
+
// dist/index.js
|
|
414
|
+
import { confirm as confirm3 } from "@inquirer/prompts";
|
|
415
|
+
|
|
404
416
|
// dist/flip/server/Server.js
|
|
405
417
|
import express2 from "express";
|
|
406
418
|
import cors from "cors";
|
|
@@ -433,9 +445,34 @@ var DEFAULT_IGNORES = [
|
|
|
433
445
|
"yarn.lock",
|
|
434
446
|
".DS_Store"
|
|
435
447
|
];
|
|
448
|
+
var VISIBLE_DOTFILES = /* @__PURE__ */ new Set([
|
|
449
|
+
".gitignore",
|
|
450
|
+
".gitattributes",
|
|
451
|
+
".env.example",
|
|
452
|
+
".env.local.example",
|
|
453
|
+
".eslintrc",
|
|
454
|
+
".eslintrc.js",
|
|
455
|
+
".eslintrc.cjs",
|
|
456
|
+
".eslintrc.json",
|
|
457
|
+
".eslintrc.yml",
|
|
458
|
+
".prettierrc",
|
|
459
|
+
".prettierrc.js",
|
|
460
|
+
".prettierrc.cjs",
|
|
461
|
+
".prettierrc.json",
|
|
462
|
+
".prettierrc.yml",
|
|
463
|
+
".editorconfig",
|
|
464
|
+
".npmrc",
|
|
465
|
+
".nvmrc",
|
|
466
|
+
".node-version",
|
|
467
|
+
".dockerignore",
|
|
468
|
+
".browserslistrc",
|
|
469
|
+
".babelrc",
|
|
470
|
+
".babelrc.js",
|
|
471
|
+
".babelrc.json"
|
|
472
|
+
]);
|
|
436
473
|
function shouldIgnore(name) {
|
|
437
|
-
if (name.startsWith(".")
|
|
438
|
-
return
|
|
474
|
+
if (name.startsWith(".")) {
|
|
475
|
+
return !VISIBLE_DOTFILES.has(name);
|
|
439
476
|
}
|
|
440
477
|
return DEFAULT_IGNORES.includes(name);
|
|
441
478
|
}
|
|
@@ -510,42 +547,126 @@ import { Router as Router2 } from "express";
|
|
|
510
547
|
import fs3 from "fs";
|
|
511
548
|
import path3 from "path";
|
|
512
549
|
var router2 = Router2();
|
|
550
|
+
var extensionMap = {
|
|
551
|
+
// JavaScript/TypeScript
|
|
552
|
+
js: "javascript",
|
|
553
|
+
mjs: "javascript",
|
|
554
|
+
cjs: "javascript",
|
|
555
|
+
ts: "typescript",
|
|
556
|
+
mts: "typescript",
|
|
557
|
+
cts: "typescript",
|
|
558
|
+
tsx: "tsx",
|
|
559
|
+
jsx: "jsx",
|
|
560
|
+
// Systems languages
|
|
561
|
+
rs: "rust",
|
|
562
|
+
go: "go",
|
|
563
|
+
c: "c",
|
|
564
|
+
cpp: "cpp",
|
|
565
|
+
cc: "cpp",
|
|
566
|
+
cxx: "cpp",
|
|
567
|
+
h: "cpp",
|
|
568
|
+
hpp: "cpp",
|
|
569
|
+
hxx: "cpp",
|
|
570
|
+
// JVM languages
|
|
571
|
+
java: "java",
|
|
572
|
+
kt: "kotlin",
|
|
573
|
+
kts: "kotlin",
|
|
574
|
+
scala: "scala",
|
|
575
|
+
groovy: "groovy",
|
|
576
|
+
// Scripting languages
|
|
577
|
+
py: "python",
|
|
578
|
+
rb: "ruby",
|
|
579
|
+
php: "php",
|
|
580
|
+
pl: "perl",
|
|
581
|
+
lua: "lua",
|
|
582
|
+
// Mobile
|
|
583
|
+
swift: "swift",
|
|
584
|
+
m: "objective-c",
|
|
585
|
+
mm: "objective-cpp",
|
|
586
|
+
dart: "dart",
|
|
587
|
+
// Web
|
|
588
|
+
html: "html",
|
|
589
|
+
htm: "html",
|
|
590
|
+
css: "css",
|
|
591
|
+
scss: "scss",
|
|
592
|
+
sass: "sass",
|
|
593
|
+
less: "less",
|
|
594
|
+
vue: "vue",
|
|
595
|
+
svelte: "svelte",
|
|
596
|
+
astro: "astro",
|
|
597
|
+
// Data formats
|
|
598
|
+
json: "json",
|
|
599
|
+
jsonc: "jsonc",
|
|
600
|
+
yaml: "yaml",
|
|
601
|
+
yml: "yaml",
|
|
602
|
+
toml: "toml",
|
|
603
|
+
xml: "xml",
|
|
604
|
+
csv: "csv",
|
|
605
|
+
// Documentation
|
|
606
|
+
md: "markdown",
|
|
607
|
+
mdx: "mdx",
|
|
608
|
+
rst: "rst",
|
|
609
|
+
tex: "latex",
|
|
610
|
+
// Shell
|
|
611
|
+
sh: "bash",
|
|
612
|
+
bash: "bash",
|
|
613
|
+
zsh: "bash",
|
|
614
|
+
fish: "fish",
|
|
615
|
+
ps1: "powershell",
|
|
616
|
+
bat: "batch",
|
|
617
|
+
cmd: "batch",
|
|
618
|
+
// Database
|
|
619
|
+
sql: "sql",
|
|
620
|
+
prisma: "prisma",
|
|
621
|
+
graphql: "graphql",
|
|
622
|
+
gql: "graphql",
|
|
623
|
+
// Config
|
|
624
|
+
ini: "ini",
|
|
625
|
+
conf: "ini",
|
|
626
|
+
cfg: "ini",
|
|
627
|
+
env: "dotenv",
|
|
628
|
+
// Other
|
|
629
|
+
dockerfile: "dockerfile",
|
|
630
|
+
makefile: "makefile",
|
|
631
|
+
cmake: "cmake",
|
|
632
|
+
diff: "diff",
|
|
633
|
+
patch: "diff"
|
|
634
|
+
};
|
|
635
|
+
var filenameMap = {
|
|
636
|
+
"Makefile": "makefile",
|
|
637
|
+
"makefile": "makefile",
|
|
638
|
+
"GNUmakefile": "makefile",
|
|
639
|
+
"Dockerfile": "dockerfile",
|
|
640
|
+
"dockerfile": "dockerfile",
|
|
641
|
+
"Containerfile": "dockerfile",
|
|
642
|
+
"Jenkinsfile": "groovy",
|
|
643
|
+
"Vagrantfile": "ruby",
|
|
644
|
+
"Gemfile": "ruby",
|
|
645
|
+
"Rakefile": "ruby",
|
|
646
|
+
"Brewfile": "ruby",
|
|
647
|
+
"Podfile": "ruby",
|
|
648
|
+
"Fastfile": "ruby",
|
|
649
|
+
"Guardfile": "ruby",
|
|
650
|
+
".gitignore": "gitignore",
|
|
651
|
+
".gitattributes": "gitattributes",
|
|
652
|
+
".editorconfig": "editorconfig",
|
|
653
|
+
".bashrc": "bash",
|
|
654
|
+
".zshrc": "bash",
|
|
655
|
+
".bash_profile": "bash",
|
|
656
|
+
".profile": "bash",
|
|
657
|
+
"CMakeLists.txt": "cmake",
|
|
658
|
+
"CODEOWNERS": "gitignore"
|
|
659
|
+
};
|
|
513
660
|
function detectLanguage(filePath) {
|
|
661
|
+
const basename2 = path3.basename(filePath);
|
|
662
|
+
if (filenameMap[basename2]) {
|
|
663
|
+
return filenameMap[basename2];
|
|
664
|
+
}
|
|
514
665
|
const ext = path3.extname(filePath).slice(1).toLowerCase();
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
tsx: "tsx",
|
|
520
|
-
jsx: "jsx",
|
|
521
|
-
py: "python",
|
|
522
|
-
go: "go",
|
|
523
|
-
java: "java",
|
|
524
|
-
c: "c",
|
|
525
|
-
cpp: "cpp",
|
|
526
|
-
cc: "cpp",
|
|
527
|
-
cxx: "cpp",
|
|
528
|
-
h: "cpp",
|
|
529
|
-
hpp: "cpp",
|
|
530
|
-
md: "markdown",
|
|
531
|
-
json: "json",
|
|
532
|
-
yaml: "yaml",
|
|
533
|
-
yml: "yaml",
|
|
534
|
-
toml: "toml",
|
|
535
|
-
html: "html",
|
|
536
|
-
css: "css",
|
|
537
|
-
scss: "scss",
|
|
538
|
-
sh: "bash",
|
|
539
|
-
bash: "bash",
|
|
540
|
-
sql: "sql",
|
|
541
|
-
rb: "ruby",
|
|
542
|
-
swift: "swift",
|
|
543
|
-
kt: "kotlin",
|
|
544
|
-
kts: "kotlin",
|
|
545
|
-
xml: "xml",
|
|
546
|
-
vue: "vue"
|
|
547
|
-
};
|
|
548
|
-
return languageMap[ext] || "plaintext";
|
|
666
|
+
if (extensionMap[ext]) {
|
|
667
|
+
return extensionMap[ext];
|
|
668
|
+
}
|
|
669
|
+
return "text";
|
|
549
670
|
}
|
|
550
671
|
router2.get("/", (req, res) => {
|
|
551
672
|
const state = req.app.locals.state;
|
|
@@ -1324,7 +1445,10 @@ async function main() {
|
|
|
1324
1445
|
await listThreads(workspaceRoot);
|
|
1325
1446
|
break;
|
|
1326
1447
|
case "new":
|
|
1327
|
-
await
|
|
1448
|
+
await createWorktreeCommand(workspaceRoot, filteredArgs.slice(1));
|
|
1449
|
+
break;
|
|
1450
|
+
case "quit":
|
|
1451
|
+
await quitWorktreeCommand();
|
|
1328
1452
|
break;
|
|
1329
1453
|
default:
|
|
1330
1454
|
if (persistentMode) {
|
|
@@ -1441,30 +1565,71 @@ async function listThreads(workspaceRoot) {
|
|
|
1441
1565
|
console.log(`${typeLabel} ${t.name.padEnd(20)} ${chalk2.dim(t.path)}`);
|
|
1442
1566
|
}
|
|
1443
1567
|
}
|
|
1444
|
-
|
|
1568
|
+
function parseNewArgs(args) {
|
|
1569
|
+
let name;
|
|
1570
|
+
let split = false;
|
|
1571
|
+
for (const arg of args) {
|
|
1572
|
+
if (arg === "-s" || arg === "--split") {
|
|
1573
|
+
split = true;
|
|
1574
|
+
} else if (!arg.startsWith("-") && !name) {
|
|
1575
|
+
name = arg;
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
return { name, split };
|
|
1579
|
+
}
|
|
1580
|
+
async function createWorktreeCommand(workspaceRoot, args) {
|
|
1581
|
+
const { name, split } = parseNewArgs(args);
|
|
1582
|
+
if (!name) {
|
|
1583
|
+
console.error(chalk2.red("Error: Name is required"));
|
|
1584
|
+
console.error(chalk2.dim("Usage: csq new <name> [-s|--split]"));
|
|
1585
|
+
process.exit(1);
|
|
1586
|
+
}
|
|
1445
1587
|
const repoName = path10.basename(workspaceRoot);
|
|
1446
1588
|
const defaultBasePath = path10.join(path10.dirname(workspaceRoot), `${repoName}.worktree`);
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1589
|
+
const worktreePath = path10.join(defaultBasePath, name);
|
|
1590
|
+
try {
|
|
1591
|
+
await gitAdapter.createWorktree(worktreePath, name, workspaceRoot);
|
|
1592
|
+
console.log(chalk2.green(`\u2713 Created worktree: ${name}`));
|
|
1593
|
+
await copyWorktreeFiles(workspaceRoot, worktreePath);
|
|
1594
|
+
if (split) {
|
|
1595
|
+
await openNewTerminal(worktreePath);
|
|
1596
|
+
} else {
|
|
1597
|
+
console.log(worktreePath);
|
|
1598
|
+
}
|
|
1599
|
+
} catch (error) {
|
|
1600
|
+
console.error(chalk2.red(`Failed to create worktree: ${error.message}`));
|
|
1601
|
+
process.exit(1);
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1604
|
+
async function quitWorktreeCommand() {
|
|
1605
|
+
const cwd = process.cwd();
|
|
1606
|
+
const context = await gitAdapter.getWorktreeContext(cwd);
|
|
1607
|
+
if (!context.isWorktree) {
|
|
1608
|
+
console.error(chalk2.red("Error: Not in a worktree"));
|
|
1609
|
+
process.exit(1);
|
|
1610
|
+
}
|
|
1611
|
+
if (!context.mainRoot || !context.branch) {
|
|
1612
|
+
console.error(chalk2.red("Error: Could not determine worktree context"));
|
|
1613
|
+
process.exit(1);
|
|
1614
|
+
}
|
|
1615
|
+
const isDirty = await gitAdapter.hasDirtyState(cwd);
|
|
1616
|
+
if (isDirty) {
|
|
1617
|
+
const confirmed = await confirm3({
|
|
1618
|
+
message: "Uncommitted changes detected. Delete anyway?",
|
|
1619
|
+
default: false
|
|
1620
|
+
});
|
|
1621
|
+
if (!confirmed) {
|
|
1455
1622
|
console.log(chalk2.dim("Cancelled."));
|
|
1456
|
-
|
|
1623
|
+
process.exit(0);
|
|
1457
1624
|
}
|
|
1458
|
-
worktreeName = form.name;
|
|
1459
|
-
worktreePath = form.path;
|
|
1460
1625
|
}
|
|
1461
1626
|
try {
|
|
1462
|
-
await gitAdapter.
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1627
|
+
await gitAdapter.removeWorktree(context.currentPath, context.mainRoot, true);
|
|
1628
|
+
await gitAdapter.deleteBranch(context.branch, context.mainRoot, true);
|
|
1629
|
+
console.log(chalk2.green(`\u2713 Deleted worktree and branch: ${context.branch}`));
|
|
1630
|
+
console.log(context.mainRoot);
|
|
1466
1631
|
} catch (error) {
|
|
1467
|
-
console.error(chalk2.red(`Failed to
|
|
1632
|
+
console.error(chalk2.red(`Failed to quit: ${error.message}`));
|
|
1468
1633
|
process.exit(1);
|
|
1469
1634
|
}
|
|
1470
1635
|
}
|
|
@@ -1482,12 +1647,6 @@ async function copyWorktreeFiles(sourceRoot, destRoot) {
|
|
|
1482
1647
|
console.log(chalk2.yellow(`\u26A0 Failed to copy ${failed.length} file(s)`));
|
|
1483
1648
|
}
|
|
1484
1649
|
}
|
|
1485
|
-
async function writeCdFile(targetPath) {
|
|
1486
|
-
const cdFile = process.env.CSQ_CD_FILE;
|
|
1487
|
-
if (cdFile) {
|
|
1488
|
-
await fs10.promises.writeFile(cdFile, targetPath);
|
|
1489
|
-
}
|
|
1490
|
-
}
|
|
1491
1650
|
async function cdInCurrentTerminal(targetPath) {
|
|
1492
1651
|
const { exec: exec2 } = await import("child_process");
|
|
1493
1652
|
const escapedPath = targetPath.replace(/'/g, "'\\''");
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
*{box-sizing:border-box;margin:0;padding:0}:root{--bg-primary: #0d1117;--bg-secondary: #161b22;--bg-tertiary: #21262d;--bg-hover: #30363d;--bg-selected: #1f6feb26;--text-primary: #e6edf3;--text-secondary: #8b949e;--text-muted: #6e7681;--border-color: #30363d;--border-subtle: #21262d;--accent-primary: #238636;--accent-primary-hover: #2ea043;--accent-secondary: #1f6feb;--git-modified: #d29922;--git-untracked: #3fb950;--git-deleted: #f85149;--git-added: #3fb950;--line-number-color: #6e7681;--selection-bg: #264f78;--diff-add-bg: rgba(46, 160, 67, .15);--diff-add-text: #7ee787;--diff-add-border: rgba(46, 160, 67, .4);--diff-delete-bg: rgba(248, 81, 73, .15);--diff-delete-text: #ffa198;--diff-delete-border: rgba(248, 81, 73, .4);--diff-hunk-bg: rgba(56, 139, 253, .1);--font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif;--font-mono: "SF Mono", "Menlo", "Monaco", "Consolas", monospace;--space-1: 4px;--space-2: 8px;--space-3: 12px;--space-4: 16px;--space-5: 24px;--transition-fast: .15s ease;--transition-normal: .2s ease}body{font-family:var(--font-sans);font-size:14px;background-color:var(--bg-primary);color:var(--text-primary);height:100vh;overflow:hidden}#root{height:100%}.app{display:flex;flex-direction:column;height:100%}.main-content{display:flex;flex:1;overflow:hidden}.toolbar{display:flex;align-items:center;justify-content:space-between;height:40px;padding:0 var(--space-3);background-color:var(--bg-secondary);border-bottom:1px solid var(--border-subtle)}.toolbar-left,.toolbar-center,.toolbar-right{display:flex;align-items:center;gap:var(--space-2)}.toolbar-btn{display:flex;align-items:center;justify-content:center;gap:var(--space-1);height:28px;padding:0 var(--space-2);border:none;background:transparent;border-radius:6px;color:var(--text-secondary);cursor:pointer;transition:all var(--transition-fast)}.toolbar-btn:hover{background-color:var(--bg-hover);color:var(--text-primary)}.toolbar-btn.active{background-color:var(--bg-tertiary);color:var(--text-primary)}.toolbar-btn svg{flex-shrink:0}.diff-toggle{min-width:100px}.diff-mode-label{font-size:12px}.sidebar{width:280px;background-color:var(--bg-secondary);border-right:1px solid var(--border-subtle);overflow:hidden;display:flex;flex-direction:column;transition:width var(--transition-normal),opacity var(--transition-fast)}.sidebar.collapsed{width:0;opacity:0;border-right:none}.content{flex:1;overflow:hidden;display:flex;flex-direction:column;min-width:0}.staging-panel{width:320px;background-color:var(--bg-secondary);border-left:1px solid var(--border-subtle);overflow:hidden;display:flex;flex-direction:column;transition:width var(--transition-normal),opacity var(--transition-fast)}.staging-panel.collapsed{width:0;opacity:0;border-left:none}.footer{display:flex;align-items:center;justify-content:space-between;padding:var(--space-2) var(--space-4);background-color:var(--bg-secondary);border-top:1px solid var(--border-subtle);gap:var(--space-4)}.footer-actions{display:flex;gap:var(--space-2)}.file-tree{display:flex;flex-direction:column;height:100%}.file-tree-header{display:flex;align-items:center;justify-content:space-between;padding:var(--space-3) var(--space-4);font-weight:600;font-size:11px;text-transform:uppercase;letter-spacing:.5px;color:var(--text-secondary);border-bottom:1px solid var(--border-subtle)}.file-tree-header-actions{display:flex;gap:var(--space-1)}.filter-btn{display:flex;align-items:center;gap:var(--space-1);padding:2px 8px;border:none;background:transparent;border-radius:4px;color:var(--text-muted);font-size:10px;font-weight:600;cursor:pointer;transition:all var(--transition-fast)}.filter-btn:hover{background-color:var(--bg-hover);color:var(--text-secondary)}.filter-btn.active{background-color:var(--accent-secondary);color:#fff}.filter-btn .count{background-color:var(--bg-hover);padding:0 4px;border-radius:3px;font-size:9px}.filter-btn.active .count{background-color:#fff3}.file-tree-content{flex:1;overflow-y:auto;padding:var(--space-1) 0}.tree-item{display:flex;align-items:center;padding:var(--space-1) var(--space-2);cursor:pointer;-webkit-user-select:none;user-select:none;gap:6px}.tree-item:hover{background-color:var(--bg-hover)}.tree-icon{font-size:10px;width:14px;text-align:center;color:var(--text-secondary)}.tree-name{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.git-badge{font-size:10px;font-weight:600;padding:1px 4px;border-radius:3px}.git-badge-modified{color:var(--git-modified);background-color:#d2992226}.git-badge-untracked{color:var(--git-untracked);background-color:#3fb95026}.git-badge-deleted{color:var(--git-deleted);background-color:#f8514926}.git-badge-added{color:var(--git-added);background-color:#3fb95026}.code-viewer{display:flex;flex-direction:column;height:100%;overflow:hidden}.code-viewer-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;color:var(--text-secondary);gap:var(--space-2)}.code-viewer-empty .hint{font-size:12px;color:var(--text-muted)}.code-viewer-header{display:flex;align-items:center;justify-content:space-between;padding:var(--space-2) var(--space-4);background-color:var(--bg-secondary);border-bottom:1px solid var(--border-subtle)}.file-path{font-size:13px;font-family:var(--font-mono)}.file-language{font-size:11px;color:var(--text-secondary);text-transform:uppercase}.code-viewer-content{flex:1;overflow:auto;font-family:var(--font-mono);font-size:13px;line-height:1.5}.code-line{display:flex;min-height:20px;cursor:pointer}.code-line:hover{background-color:var(--bg-hover)}.code-line-selected{background-color:var(--selection-bg)!important}.code-line-added{background-color:var(--diff-add-bg)!important}.code-line-added:hover{background-color:#2ea04340!important}.code-line-deleted{background-color:var(--diff-delete-bg)!important}.code-line-deleted:hover{background-color:#f8514940!important}.line-number{display:inline-block;min-width:50px;padding:0 var(--space-3);text-align:right;color:var(--line-number-color);-webkit-user-select:none;user-select:none;background-color:var(--bg-tertiary);border-right:1px solid var(--border-subtle)}.code-line code{flex:1;padding:0 var(--space-3);white-space:pre;background:transparent}.code-line code.hljs{background:transparent;padding:0 var(--space-3)}.code-content{flex:1;padding:0 var(--space-3);white-space:pre}.staging-list{display:flex;flex-direction:column;height:100%}.staging-list-empty{padding:var(--space-4)}.staging-header{display:flex;align-items:center;justify-content:space-between;padding:var(--space-3) var(--space-4);font-weight:600;font-size:11px;text-transform:uppercase;letter-spacing:.5px;color:var(--text-secondary);border-bottom:1px solid var(--border-subtle)}.staging-hint{font-size:12px;color:var(--text-muted);line-height:1.5}.staging-items{flex:1;overflow-y:auto;padding:var(--space-2)}.staging-item{background-color:var(--bg-tertiary);border-radius:6px;padding:var(--space-2) var(--space-3);margin-bottom:var(--space-2)}.staging-item-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:var(--space-1)}.staging-location{font-family:var(--font-mono);font-size:12px;color:var(--text-secondary)}.staging-comment{font-size:13px;line-height:1.4}.comment-input{display:flex;align-items:center;gap:var(--space-3);flex:1}.comment-selection{font-family:var(--font-mono);font-size:12px;color:var(--accent-secondary);white-space:nowrap}.comment-field{flex:1;padding:var(--space-2) var(--space-3);background-color:var(--bg-tertiary);border:1px solid var(--border-color);border-radius:6px;color:var(--text-primary);font-size:14px;transition:border-color var(--transition-fast)}.comment-field:focus{outline:none;border-color:var(--accent-secondary)}.comment-hint{color:var(--text-muted);font-size:13px}.fuzzy-finder-overlay{position:fixed;top:0;right:0;bottom:0;left:0;background-color:#0009;display:flex;justify-content:center;padding-top:80px;z-index:100}.fuzzy-finder{background-color:var(--bg-secondary);border:1px solid var(--border-color);border-radius:12px;box-shadow:0 16px 48px #00000080;width:600px;max-height:400px;display:flex;flex-direction:column;overflow:hidden}.fuzzy-finder-input{padding:var(--space-4);background-color:var(--bg-tertiary);border:none;border-bottom:1px solid var(--border-subtle);color:var(--text-primary);font-size:16px}.fuzzy-finder-input:focus{outline:none}.fuzzy-finder-results{flex:1;overflow-y:auto}.fuzzy-finder-item{padding:var(--space-3) var(--space-4);cursor:pointer;font-family:var(--font-mono);font-size:13px}.fuzzy-finder-item:hover,.fuzzy-finder-item-selected{background-color:var(--bg-selected)}.fuzzy-finder-empty{padding:var(--space-4);color:var(--text-muted);text-align:center}.btn{padding:var(--space-2) var(--space-4);border:none;border-radius:6px;font-size:13px;font-weight:500;cursor:pointer;transition:background-color var(--transition-fast)}.btn:disabled{opacity:.5;cursor:not-allowed}.btn-primary{background-color:var(--accent-primary);color:#fff}.btn-primary:hover:not(:disabled){background-color:var(--accent-primary-hover)}.btn-secondary{background-color:var(--bg-tertiary);color:var(--text-primary)}.btn-secondary:hover:not(:disabled){background-color:var(--bg-hover)}.btn-small{padding:var(--space-1) var(--space-3)}.btn-link{background:none;border:none;color:var(--accent-secondary);cursor:pointer;font-size:11px}.btn-link:hover{text-decoration:underline}.btn-icon{background:none;border:none;color:var(--text-muted);cursor:pointer;font-size:14px;padding:2px 6px}.btn-icon:hover{color:var(--text-primary)}.diff-side-by-side{display:flex;height:100%;overflow:hidden}.diff-pane{flex:1;overflow:auto;font-family:var(--font-mono);font-size:13px;line-height:1.5}.diff-pane-left{border-right:1px solid var(--border-color)}.diff-pane-header{padding:var(--space-2) var(--space-3);background-color:var(--bg-tertiary);border-bottom:1px solid var(--border-subtle);font-size:11px;color:var(--text-secondary);text-transform:uppercase;letter-spacing:.5px}.code-viewer-loading{display:flex;align-items:center;justify-content:center;height:100%;color:var(--text-muted)}::-webkit-scrollbar{width:8px;height:8px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:var(--bg-hover);border-radius:4px}::-webkit-scrollbar-thumb:hover{background:#484f58}::-webkit-scrollbar-corner{background:transparent}
|