code-squad-cli 1.0.7 → 1.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/flip/index.d.ts +1 -0
- package/dist/flip/index.js +246 -0
- package/dist/flip/output/autopaste.d.ts +5 -0
- package/dist/flip/output/autopaste.js +80 -0
- package/dist/flip/output/clipboard.d.ts +4 -0
- package/dist/flip/output/clipboard.js +7 -0
- package/dist/flip/output/formatter.d.ts +13 -0
- package/dist/flip/output/formatter.js +18 -0
- package/dist/flip/output/index.d.ts +4 -0
- package/dist/flip/output/index.js +3 -0
- package/dist/flip/routes/cancel.d.ts +6 -0
- package/dist/flip/routes/cancel.js +13 -0
- package/dist/flip/routes/file.d.ts +8 -0
- package/dist/flip/routes/file.js +77 -0
- package/dist/flip/routes/files.d.ts +16 -0
- package/dist/flip/routes/files.js +102 -0
- package/dist/flip/routes/git.d.ts +11 -0
- package/dist/flip/routes/git.js +83 -0
- package/dist/flip/routes/static.d.ts +2 -0
- package/dist/flip/routes/static.js +35 -0
- package/dist/flip/routes/submit.d.ts +20 -0
- package/dist/flip/routes/submit.js +42 -0
- package/dist/flip/server/Server.d.ts +11 -0
- package/dist/flip/server/Server.js +63 -0
- package/dist/flip-ui/dist/assets/index-2_ZJL2eQ.css +10 -0
- package/dist/flip-ui/dist/assets/index-BGxWXtBJ.js +66 -0
- package/dist/flip-ui/dist/index.html +13 -0
- package/dist/index.js +723 -21
- package/package.json +14 -4
package/dist/index.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// dist/index.js
|
|
4
|
-
import * as
|
|
5
|
-
import * as
|
|
4
|
+
import * as path7 from "path";
|
|
5
|
+
import * as fs7 from "fs";
|
|
6
|
+
import * as os3 from "os";
|
|
7
|
+
import * as crypto from "crypto";
|
|
6
8
|
import chalk2 from "chalk";
|
|
7
9
|
|
|
8
10
|
// dist/adapters/GitAdapter.js
|
|
@@ -49,11 +51,11 @@ var GitAdapter = class {
|
|
|
49
51
|
const headMatch = headLine.match(/^HEAD (.+)$/);
|
|
50
52
|
const branchMatch = branchLine?.match(/^branch refs\/heads\/(.+)$/);
|
|
51
53
|
if (pathMatch && headMatch) {
|
|
52
|
-
const
|
|
54
|
+
const path8 = pathMatch[1];
|
|
53
55
|
const head = headMatch[1];
|
|
54
56
|
const branch = branchMatch ? branchMatch[1] : "HEAD";
|
|
55
|
-
if (
|
|
56
|
-
worktrees.push({ path:
|
|
57
|
+
if (path8 !== workspaceRoot) {
|
|
58
|
+
worktrees.push({ path: path8, branch, head });
|
|
57
59
|
}
|
|
58
60
|
}
|
|
59
61
|
i += 3;
|
|
@@ -99,14 +101,14 @@ var GitAdapter = class {
|
|
|
99
101
|
});
|
|
100
102
|
});
|
|
101
103
|
}
|
|
102
|
-
async isValidWorktree(
|
|
104
|
+
async isValidWorktree(path8, workspaceRoot) {
|
|
103
105
|
try {
|
|
104
|
-
await fs.promises.access(
|
|
106
|
+
await fs.promises.access(path8, fs.constants.R_OK);
|
|
105
107
|
} catch {
|
|
106
108
|
return false;
|
|
107
109
|
}
|
|
108
110
|
const isGitRepo = await new Promise((resolve) => {
|
|
109
|
-
exec(`cd "${
|
|
111
|
+
exec(`cd "${path8}" && git rev-parse --git-dir`, { maxBuffer: 1024 * 1024 }, (error) => {
|
|
110
112
|
resolve(!error);
|
|
111
113
|
});
|
|
112
114
|
});
|
|
@@ -114,7 +116,7 @@ var GitAdapter = class {
|
|
|
114
116
|
return false;
|
|
115
117
|
}
|
|
116
118
|
const worktrees = await this.listWorktrees(workspaceRoot);
|
|
117
|
-
return worktrees.some((wt) => wt.path ===
|
|
119
|
+
return worktrees.some((wt) => wt.path === path8);
|
|
118
120
|
}
|
|
119
121
|
async getWorktreeBranch(worktreePath) {
|
|
120
122
|
return new Promise((resolve, reject) => {
|
|
@@ -399,7 +401,695 @@ async function confirmDeleteLocal(threadName) {
|
|
|
399
401
|
});
|
|
400
402
|
}
|
|
401
403
|
|
|
404
|
+
// dist/flip/server/Server.js
|
|
405
|
+
import express2 from "express";
|
|
406
|
+
import cors from "cors";
|
|
407
|
+
import http from "http";
|
|
408
|
+
import net from "net";
|
|
409
|
+
|
|
410
|
+
// dist/flip/routes/files.js
|
|
411
|
+
import { Router } from "express";
|
|
412
|
+
import fs2 from "fs";
|
|
413
|
+
import path2 from "path";
|
|
414
|
+
var router = Router();
|
|
415
|
+
var DEFAULT_IGNORES = [
|
|
416
|
+
"node_modules",
|
|
417
|
+
".git",
|
|
418
|
+
".svn",
|
|
419
|
+
".hg",
|
|
420
|
+
"dist",
|
|
421
|
+
"build",
|
|
422
|
+
"out",
|
|
423
|
+
".cache",
|
|
424
|
+
".next",
|
|
425
|
+
".nuxt",
|
|
426
|
+
"coverage",
|
|
427
|
+
"__pycache__",
|
|
428
|
+
".pytest_cache",
|
|
429
|
+
"target",
|
|
430
|
+
"Cargo.lock",
|
|
431
|
+
"package-lock.json",
|
|
432
|
+
"pnpm-lock.yaml",
|
|
433
|
+
"yarn.lock",
|
|
434
|
+
".DS_Store"
|
|
435
|
+
];
|
|
436
|
+
function shouldIgnore(name) {
|
|
437
|
+
if (name.startsWith(".") && name !== ".gitignore" && name !== ".env.example") {
|
|
438
|
+
return true;
|
|
439
|
+
}
|
|
440
|
+
return DEFAULT_IGNORES.includes(name);
|
|
441
|
+
}
|
|
442
|
+
function buildFileTree(rootPath, currentPath, maxDepth, depth = 0) {
|
|
443
|
+
if (depth > maxDepth)
|
|
444
|
+
return [];
|
|
445
|
+
const entries = fs2.readdirSync(currentPath, { withFileTypes: true });
|
|
446
|
+
const nodes = [];
|
|
447
|
+
entries.sort((a, b) => {
|
|
448
|
+
if (a.isDirectory() && !b.isDirectory())
|
|
449
|
+
return -1;
|
|
450
|
+
if (!a.isDirectory() && b.isDirectory())
|
|
451
|
+
return 1;
|
|
452
|
+
return a.name.localeCompare(b.name);
|
|
453
|
+
});
|
|
454
|
+
for (const entry of entries) {
|
|
455
|
+
if (shouldIgnore(entry.name))
|
|
456
|
+
continue;
|
|
457
|
+
const fullPath = path2.join(currentPath, entry.name);
|
|
458
|
+
const relativePath = path2.relative(rootPath, fullPath);
|
|
459
|
+
const node = {
|
|
460
|
+
path: relativePath,
|
|
461
|
+
name: entry.name,
|
|
462
|
+
type: entry.isDirectory() ? "directory" : "file"
|
|
463
|
+
};
|
|
464
|
+
if (entry.isDirectory()) {
|
|
465
|
+
node.children = buildFileTree(rootPath, fullPath, maxDepth, depth + 1);
|
|
466
|
+
}
|
|
467
|
+
nodes.push(node);
|
|
468
|
+
}
|
|
469
|
+
return nodes;
|
|
470
|
+
}
|
|
471
|
+
function collectFlatFiles(rootPath, currentPath, maxDepth, depth = 0) {
|
|
472
|
+
if (depth > maxDepth)
|
|
473
|
+
return [];
|
|
474
|
+
const entries = fs2.readdirSync(currentPath, { withFileTypes: true });
|
|
475
|
+
const files = [];
|
|
476
|
+
for (const entry of entries) {
|
|
477
|
+
if (shouldIgnore(entry.name))
|
|
478
|
+
continue;
|
|
479
|
+
const fullPath = path2.join(currentPath, entry.name);
|
|
480
|
+
const relativePath = path2.relative(rootPath, fullPath);
|
|
481
|
+
if (entry.isFile()) {
|
|
482
|
+
files.push(relativePath);
|
|
483
|
+
} else if (entry.isDirectory()) {
|
|
484
|
+
files.push(...collectFlatFiles(rootPath, fullPath, maxDepth, depth + 1));
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
return files;
|
|
488
|
+
}
|
|
489
|
+
router.get("/", (req, res) => {
|
|
490
|
+
const state = req.app.locals.state;
|
|
491
|
+
const tree = buildFileTree(state.cwd, state.cwd, 10);
|
|
492
|
+
const response = {
|
|
493
|
+
root: state.cwd,
|
|
494
|
+
tree
|
|
495
|
+
};
|
|
496
|
+
res.json(response);
|
|
497
|
+
});
|
|
498
|
+
router.get("/flat", (req, res) => {
|
|
499
|
+
const state = req.app.locals.state;
|
|
500
|
+
const files = collectFlatFiles(state.cwd, state.cwd, 10);
|
|
501
|
+
files.sort();
|
|
502
|
+
const response = {
|
|
503
|
+
files
|
|
504
|
+
};
|
|
505
|
+
res.json(response);
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
// dist/flip/routes/file.js
|
|
509
|
+
import { Router as Router2 } from "express";
|
|
510
|
+
import fs3 from "fs";
|
|
511
|
+
import path3 from "path";
|
|
512
|
+
var router2 = Router2();
|
|
513
|
+
function detectLanguage(filePath) {
|
|
514
|
+
const ext = path3.extname(filePath).slice(1).toLowerCase();
|
|
515
|
+
const languageMap = {
|
|
516
|
+
rs: "rust",
|
|
517
|
+
js: "javascript",
|
|
518
|
+
ts: "typescript",
|
|
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";
|
|
549
|
+
}
|
|
550
|
+
router2.get("/", (req, res) => {
|
|
551
|
+
const state = req.app.locals.state;
|
|
552
|
+
const relativePath = req.query.path;
|
|
553
|
+
if (!relativePath) {
|
|
554
|
+
res.status(400).json({ error: "Missing path parameter" });
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
const filePath = path3.join(state.cwd, relativePath);
|
|
558
|
+
const resolvedPath = path3.resolve(filePath);
|
|
559
|
+
const resolvedCwd = path3.resolve(state.cwd);
|
|
560
|
+
if (!resolvedPath.startsWith(resolvedCwd)) {
|
|
561
|
+
res.status(403).json({ error: "Access denied" });
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
try {
|
|
565
|
+
const content = fs3.readFileSync(resolvedPath, "utf-8");
|
|
566
|
+
const language = detectLanguage(relativePath);
|
|
567
|
+
const response = {
|
|
568
|
+
path: relativePath,
|
|
569
|
+
content,
|
|
570
|
+
language
|
|
571
|
+
};
|
|
572
|
+
res.json(response);
|
|
573
|
+
} catch (err) {
|
|
574
|
+
if (err.code === "ENOENT") {
|
|
575
|
+
res.status(404).json({ error: "File not found" });
|
|
576
|
+
} else {
|
|
577
|
+
res.status(500).json({ error: "Failed to read file" });
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
});
|
|
581
|
+
|
|
582
|
+
// dist/flip/routes/git.js
|
|
583
|
+
import { Router as Router3 } from "express";
|
|
584
|
+
import { execSync } from "child_process";
|
|
585
|
+
var router3 = Router3();
|
|
586
|
+
function parseGitStatus(output) {
|
|
587
|
+
const files = [];
|
|
588
|
+
for (const line of output.split("\n")) {
|
|
589
|
+
if (line.length < 3)
|
|
590
|
+
continue;
|
|
591
|
+
const statusCode = line.substring(0, 2);
|
|
592
|
+
let filePath = line.substring(3);
|
|
593
|
+
filePath = filePath.replace(/^"|"$/g, "");
|
|
594
|
+
let status;
|
|
595
|
+
if (statusCode === "??") {
|
|
596
|
+
status = "untracked";
|
|
597
|
+
} else if (statusCode.includes("M")) {
|
|
598
|
+
status = "modified";
|
|
599
|
+
} else if (statusCode.includes("D")) {
|
|
600
|
+
status = "deleted";
|
|
601
|
+
} else if (statusCode.includes("A")) {
|
|
602
|
+
status = "added";
|
|
603
|
+
} else if (statusCode.includes("R")) {
|
|
604
|
+
status = "renamed";
|
|
605
|
+
} else if (statusCode.includes("C")) {
|
|
606
|
+
status = "copied";
|
|
607
|
+
} else if (statusCode === "UU") {
|
|
608
|
+
status = "unmerged";
|
|
609
|
+
} else {
|
|
610
|
+
status = "modified";
|
|
611
|
+
}
|
|
612
|
+
files.push({ path: filePath, status });
|
|
613
|
+
}
|
|
614
|
+
return files;
|
|
615
|
+
}
|
|
616
|
+
router3.get("/status", (req, res) => {
|
|
617
|
+
const state = req.app.locals.state;
|
|
618
|
+
let isGitRepo = false;
|
|
619
|
+
try {
|
|
620
|
+
execSync("git rev-parse --git-dir", {
|
|
621
|
+
cwd: state.cwd,
|
|
622
|
+
stdio: "pipe"
|
|
623
|
+
});
|
|
624
|
+
isGitRepo = true;
|
|
625
|
+
} catch {
|
|
626
|
+
}
|
|
627
|
+
if (!isGitRepo) {
|
|
628
|
+
const response2 = {
|
|
629
|
+
isGitRepo: false,
|
|
630
|
+
unstaged: []
|
|
631
|
+
};
|
|
632
|
+
res.json(response2);
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
635
|
+
let unstaged = [];
|
|
636
|
+
try {
|
|
637
|
+
const output = execSync("git status --porcelain", {
|
|
638
|
+
cwd: state.cwd,
|
|
639
|
+
encoding: "utf-8"
|
|
640
|
+
});
|
|
641
|
+
unstaged = parseGitStatus(output);
|
|
642
|
+
} catch {
|
|
643
|
+
}
|
|
644
|
+
const response = {
|
|
645
|
+
isGitRepo: true,
|
|
646
|
+
unstaged
|
|
647
|
+
};
|
|
648
|
+
res.json(response);
|
|
649
|
+
});
|
|
650
|
+
|
|
651
|
+
// dist/flip/routes/submit.js
|
|
652
|
+
import { Router as Router4 } from "express";
|
|
653
|
+
|
|
654
|
+
// dist/flip/output/formatter.js
|
|
655
|
+
function formatComments(comments) {
|
|
656
|
+
if (comments.length === 0) {
|
|
657
|
+
return "";
|
|
658
|
+
}
|
|
659
|
+
let output = "The user has annotated the following code locations:\n\n";
|
|
660
|
+
for (const comment of comments) {
|
|
661
|
+
const lineRange = comment.endLine && comment.endLine !== comment.line ? `${comment.line}-${comment.endLine}` : `${comment.line}`;
|
|
662
|
+
const location = `${comment.file}:${lineRange}`;
|
|
663
|
+
output += `- ${location} -> "${comment.text}"
|
|
664
|
+
`;
|
|
665
|
+
}
|
|
666
|
+
output += "\nPlease review these comments and address them.";
|
|
667
|
+
return output;
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
// dist/flip/output/clipboard.js
|
|
671
|
+
import clipboardy from "clipboardy";
|
|
672
|
+
async function copyToClipboard(text) {
|
|
673
|
+
await clipboardy.write(text);
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
// dist/flip/output/autopaste.js
|
|
677
|
+
import { execSync as execSync2 } from "child_process";
|
|
678
|
+
import fs4 from "fs";
|
|
679
|
+
import path4 from "path";
|
|
680
|
+
import os from "os";
|
|
681
|
+
async function schedulePaste(sessionId) {
|
|
682
|
+
if (process.platform !== "darwin") {
|
|
683
|
+
console.error("Auto-paste not supported on this platform. Please paste manually (Ctrl+V).");
|
|
684
|
+
return;
|
|
685
|
+
}
|
|
686
|
+
await pasteToOriginalSession(sessionId);
|
|
687
|
+
}
|
|
688
|
+
async function pasteToOriginalSession(sessionId) {
|
|
689
|
+
const sessionFile = path4.join(os.tmpdir(), `flip-view-session-${sessionId}`);
|
|
690
|
+
let itermSessionId;
|
|
691
|
+
try {
|
|
692
|
+
itermSessionId = fs4.readFileSync(sessionFile, "utf-8").trim();
|
|
693
|
+
} catch {
|
|
694
|
+
}
|
|
695
|
+
let script;
|
|
696
|
+
if (itermSessionId) {
|
|
697
|
+
script = `
|
|
698
|
+
tell application "iTerm2"
|
|
699
|
+
set found to false
|
|
700
|
+
repeat with w in windows
|
|
701
|
+
repeat with t in tabs of w
|
|
702
|
+
repeat with s in sessions of t
|
|
703
|
+
if id of s is "${itermSessionId}" then
|
|
704
|
+
set found to true
|
|
705
|
+
select s
|
|
706
|
+
tell s to write text (the clipboard)
|
|
707
|
+
tell s to write text ""
|
|
708
|
+
return
|
|
709
|
+
end if
|
|
710
|
+
end repeat
|
|
711
|
+
end repeat
|
|
712
|
+
end repeat
|
|
713
|
+
|
|
714
|
+
if not found then
|
|
715
|
+
display notification "Original session closed. Output copied to clipboard." with title "flip"
|
|
716
|
+
end if
|
|
717
|
+
end tell
|
|
718
|
+
`;
|
|
719
|
+
try {
|
|
720
|
+
fs4.unlinkSync(sessionFile);
|
|
721
|
+
} catch {
|
|
722
|
+
}
|
|
723
|
+
} else {
|
|
724
|
+
script = `
|
|
725
|
+
tell application "iTerm2"
|
|
726
|
+
activate
|
|
727
|
+
delay 0.1
|
|
728
|
+
tell current session of current window
|
|
729
|
+
write text (the clipboard)
|
|
730
|
+
write text ""
|
|
731
|
+
end tell
|
|
732
|
+
end tell
|
|
733
|
+
`;
|
|
734
|
+
}
|
|
735
|
+
try {
|
|
736
|
+
execSync2(`osascript -e '${script.replace(/'/g, `'"'"'`)}'`, {
|
|
737
|
+
stdio: "pipe"
|
|
738
|
+
});
|
|
739
|
+
} catch (e) {
|
|
740
|
+
console.error("Failed to paste to iTerm:", e);
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
// dist/flip/routes/submit.js
|
|
745
|
+
var router4 = Router4();
|
|
746
|
+
router4.post("/", async (req, res) => {
|
|
747
|
+
const state = req.app.locals.state;
|
|
748
|
+
const body = req.body;
|
|
749
|
+
if (!body.items || body.items.length === 0) {
|
|
750
|
+
res.status(400).json({ error: "No items to submit" });
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
const comments = body.items.map((item) => ({
|
|
754
|
+
file: item.filePath,
|
|
755
|
+
line: item.startLine,
|
|
756
|
+
endLine: item.endLine !== item.startLine ? item.endLine : void 0,
|
|
757
|
+
text: item.comment
|
|
758
|
+
}));
|
|
759
|
+
const formatted = formatComments(comments);
|
|
760
|
+
try {
|
|
761
|
+
await copyToClipboard(formatted);
|
|
762
|
+
} catch (e) {
|
|
763
|
+
console.error("Failed to copy to clipboard:", e);
|
|
764
|
+
}
|
|
765
|
+
try {
|
|
766
|
+
await schedulePaste(body.session_id);
|
|
767
|
+
} catch (e) {
|
|
768
|
+
console.error("Failed to schedule paste:", e);
|
|
769
|
+
}
|
|
770
|
+
res.json({ status: "ok" });
|
|
771
|
+
if (state.resolve) {
|
|
772
|
+
state.resolve(formatted);
|
|
773
|
+
}
|
|
774
|
+
});
|
|
775
|
+
|
|
776
|
+
// dist/flip/routes/cancel.js
|
|
777
|
+
import { Router as Router5 } from "express";
|
|
778
|
+
var router5 = Router5();
|
|
779
|
+
router5.post("/", (req, res) => {
|
|
780
|
+
const state = req.app.locals.state;
|
|
781
|
+
res.json({ status: "ok" });
|
|
782
|
+
if (state.resolve) {
|
|
783
|
+
state.resolve(null);
|
|
784
|
+
}
|
|
785
|
+
});
|
|
786
|
+
|
|
787
|
+
// dist/flip/routes/static.js
|
|
788
|
+
import { Router as Router6 } from "express";
|
|
789
|
+
import express from "express";
|
|
790
|
+
import path5 from "path";
|
|
791
|
+
import { fileURLToPath } from "url";
|
|
792
|
+
import fs5 from "fs";
|
|
793
|
+
function createStaticRouter() {
|
|
794
|
+
const router6 = Router6();
|
|
795
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
796
|
+
const __dirname = path5.dirname(__filename);
|
|
797
|
+
const distPath = path5.resolve(__dirname, "flip-ui/dist");
|
|
798
|
+
if (fs5.existsSync(distPath)) {
|
|
799
|
+
router6.use(express.static(distPath));
|
|
800
|
+
router6.get("*", (req, res) => {
|
|
801
|
+
const indexPath = path5.join(distPath, "index.html");
|
|
802
|
+
if (fs5.existsSync(indexPath)) {
|
|
803
|
+
res.sendFile(indexPath);
|
|
804
|
+
} else {
|
|
805
|
+
res.status(404).send("Not Found");
|
|
806
|
+
}
|
|
807
|
+
});
|
|
808
|
+
} else {
|
|
809
|
+
router6.get("*", (req, res) => {
|
|
810
|
+
res.redirect(`http://localhost:5173${req.url}`);
|
|
811
|
+
});
|
|
812
|
+
}
|
|
813
|
+
return router6;
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
// dist/flip/server/Server.js
|
|
817
|
+
var Server = class {
|
|
818
|
+
cwd;
|
|
819
|
+
port;
|
|
820
|
+
constructor(cwd, port) {
|
|
821
|
+
this.cwd = cwd;
|
|
822
|
+
this.port = port;
|
|
823
|
+
}
|
|
824
|
+
async run() {
|
|
825
|
+
return new Promise((resolve) => {
|
|
826
|
+
const state = {
|
|
827
|
+
cwd: this.cwd,
|
|
828
|
+
resolve: null
|
|
829
|
+
};
|
|
830
|
+
const app = express2();
|
|
831
|
+
app.use(cors());
|
|
832
|
+
app.use(express2.json());
|
|
833
|
+
app.locals.state = state;
|
|
834
|
+
app.use("/api/files", router);
|
|
835
|
+
app.use("/api/file", router2);
|
|
836
|
+
app.use("/api/git", router3);
|
|
837
|
+
app.use("/api/submit", router4);
|
|
838
|
+
app.use("/api/cancel", router5);
|
|
839
|
+
app.use(createStaticRouter());
|
|
840
|
+
const server = http.createServer(app);
|
|
841
|
+
state.resolve = (output) => {
|
|
842
|
+
server.close();
|
|
843
|
+
resolve(output);
|
|
844
|
+
};
|
|
845
|
+
server.listen(this.port, "127.0.0.1", () => {
|
|
846
|
+
console.log(`Server running at http://localhost:${this.port}`);
|
|
847
|
+
});
|
|
848
|
+
});
|
|
849
|
+
}
|
|
850
|
+
};
|
|
851
|
+
async function findFreePort(preferred) {
|
|
852
|
+
return new Promise((resolve) => {
|
|
853
|
+
const server = net.createServer();
|
|
854
|
+
server.listen(preferred, "127.0.0.1", () => {
|
|
855
|
+
server.close(() => {
|
|
856
|
+
resolve(preferred);
|
|
857
|
+
});
|
|
858
|
+
});
|
|
859
|
+
server.on("error", () => {
|
|
860
|
+
findFreePort(preferred + 1).then(resolve);
|
|
861
|
+
});
|
|
862
|
+
});
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
// dist/flip/index.js
|
|
866
|
+
import open from "open";
|
|
867
|
+
import path6 from "path";
|
|
868
|
+
import fs6 from "fs";
|
|
869
|
+
import os2 from "os";
|
|
870
|
+
import { execSync as execSync3 } from "child_process";
|
|
871
|
+
import { confirm as confirm2 } from "@inquirer/prompts";
|
|
872
|
+
import clipboardy2 from "clipboardy";
|
|
873
|
+
var DEFAULT_PORT = 51234;
|
|
874
|
+
function formatTime() {
|
|
875
|
+
const now = /* @__PURE__ */ new Date();
|
|
876
|
+
const hours = String(now.getHours()).padStart(2, "0");
|
|
877
|
+
const mins = String(now.getMinutes()).padStart(2, "0");
|
|
878
|
+
const secs = String(now.getSeconds()).padStart(2, "0");
|
|
879
|
+
return `${hours}:${mins}:${secs}`;
|
|
880
|
+
}
|
|
881
|
+
async function runFlip(args) {
|
|
882
|
+
let sessionId;
|
|
883
|
+
const sessionIdx = args.indexOf("--session");
|
|
884
|
+
if (sessionIdx !== -1 && args[sessionIdx + 1]) {
|
|
885
|
+
sessionId = args[sessionIdx + 1];
|
|
886
|
+
}
|
|
887
|
+
const filteredArgs = args.filter((arg, idx) => {
|
|
888
|
+
if (arg === "--session")
|
|
889
|
+
return false;
|
|
890
|
+
if (idx > 0 && args[idx - 1] === "--session")
|
|
891
|
+
return false;
|
|
892
|
+
return true;
|
|
893
|
+
});
|
|
894
|
+
let command;
|
|
895
|
+
let pathArg;
|
|
896
|
+
if (filteredArgs.length > 0) {
|
|
897
|
+
switch (filteredArgs[0]) {
|
|
898
|
+
case "serve":
|
|
899
|
+
command = "serve";
|
|
900
|
+
pathArg = filteredArgs[1];
|
|
901
|
+
break;
|
|
902
|
+
case "open":
|
|
903
|
+
command = "open";
|
|
904
|
+
break;
|
|
905
|
+
case "setup":
|
|
906
|
+
command = "setup";
|
|
907
|
+
break;
|
|
908
|
+
case "--help":
|
|
909
|
+
case "-h":
|
|
910
|
+
printUsage();
|
|
911
|
+
return;
|
|
912
|
+
default:
|
|
913
|
+
command = "oneshot";
|
|
914
|
+
pathArg = filteredArgs[0];
|
|
915
|
+
break;
|
|
916
|
+
}
|
|
917
|
+
} else {
|
|
918
|
+
command = "oneshot";
|
|
919
|
+
}
|
|
920
|
+
const cwd = pathArg ? path6.resolve(pathArg) : process.cwd();
|
|
921
|
+
switch (command) {
|
|
922
|
+
case "setup": {
|
|
923
|
+
await setupHotkey();
|
|
924
|
+
return;
|
|
925
|
+
}
|
|
926
|
+
case "serve": {
|
|
927
|
+
const port = await findFreePort(DEFAULT_PORT);
|
|
928
|
+
console.log(`Server running at http://localhost:${port}`);
|
|
929
|
+
console.log("Press Ctrl+C to stop");
|
|
930
|
+
console.log("");
|
|
931
|
+
console.log("To open browser, run: csq flip open");
|
|
932
|
+
console.log(`Or use hotkey to open: open http://localhost:${port}`);
|
|
933
|
+
while (true) {
|
|
934
|
+
const server = new Server(cwd, port);
|
|
935
|
+
const result = await server.run();
|
|
936
|
+
if (result) {
|
|
937
|
+
console.log(`[${formatTime()}] Submitted ${result.length} characters`);
|
|
938
|
+
} else {
|
|
939
|
+
console.log(`[${formatTime()}] Cancelled`);
|
|
940
|
+
}
|
|
941
|
+
console.log(`[${formatTime()}] Ready for next session...`);
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
case "open": {
|
|
945
|
+
const url = `http://localhost:${DEFAULT_PORT}`;
|
|
946
|
+
console.log(`Opening ${url} in browser...`);
|
|
947
|
+
try {
|
|
948
|
+
await open(url);
|
|
949
|
+
} catch (e) {
|
|
950
|
+
console.error("Failed to open browser:", e);
|
|
951
|
+
console.error("Is the server running? Start with: csq flip serve");
|
|
952
|
+
}
|
|
953
|
+
break;
|
|
954
|
+
}
|
|
955
|
+
case "oneshot":
|
|
956
|
+
default: {
|
|
957
|
+
const port = await findFreePort(DEFAULT_PORT);
|
|
958
|
+
const url = sessionId ? `http://localhost:${port}?session=${sessionId}` : `http://localhost:${port}`;
|
|
959
|
+
console.log(`Opening ${url} in browser...`);
|
|
960
|
+
try {
|
|
961
|
+
await open(url);
|
|
962
|
+
} catch (e) {
|
|
963
|
+
console.error("Failed to open browser:", e);
|
|
964
|
+
console.log(`Please open ${url} manually`);
|
|
965
|
+
}
|
|
966
|
+
const server = new Server(cwd, port);
|
|
967
|
+
const result = await server.run();
|
|
968
|
+
if (result) {
|
|
969
|
+
console.log(`
|
|
970
|
+
Submitted ${result.length} characters`);
|
|
971
|
+
} else {
|
|
972
|
+
console.log("\nCancelled");
|
|
973
|
+
}
|
|
974
|
+
break;
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
function printUsage() {
|
|
979
|
+
console.error("Usage: csq flip [command] [options]");
|
|
980
|
+
console.error("");
|
|
981
|
+
console.error("Commands:");
|
|
982
|
+
console.error(" serve [path] Start server in daemon mode (keeps running)");
|
|
983
|
+
console.error(" open Open browser to existing server");
|
|
984
|
+
console.error(" setup Setup iTerm2 hotkey");
|
|
985
|
+
console.error(" (no command) Start server + open browser (one-shot mode)");
|
|
986
|
+
console.error("");
|
|
987
|
+
console.error("Options:");
|
|
988
|
+
console.error(" path Directory to serve (default: current directory)");
|
|
989
|
+
console.error(" --session <uuid> Session ID for paste-back tracking");
|
|
990
|
+
}
|
|
991
|
+
async function setupHotkey() {
|
|
992
|
+
const configDir = path6.join(os2.homedir(), ".config", "flip");
|
|
993
|
+
const applescriptPath = path6.join(configDir, "flip.applescript");
|
|
994
|
+
const shPath = path6.join(configDir, "flip.sh");
|
|
995
|
+
let nodePath;
|
|
996
|
+
try {
|
|
997
|
+
nodePath = execSync3("which node", { encoding: "utf-8" }).trim();
|
|
998
|
+
} catch {
|
|
999
|
+
nodePath = "/usr/local/bin/node";
|
|
1000
|
+
}
|
|
1001
|
+
const csqPath = path6.resolve(path6.dirname(new URL(import.meta.url).pathname), "../../index.js");
|
|
1002
|
+
fs6.mkdirSync(configDir, { recursive: true });
|
|
1003
|
+
const applescriptContent = `#!/usr/bin/osascript
|
|
1004
|
+
|
|
1005
|
+
-- flip hotkey script
|
|
1006
|
+
-- Generated by: csq flip setup
|
|
1007
|
+
|
|
1008
|
+
tell application "iTerm2"
|
|
1009
|
+
tell current session of current window
|
|
1010
|
+
set originalSessionId to id
|
|
1011
|
+
set sessionUUID to do shell script "uuidgen"
|
|
1012
|
+
set currentPath to variable named "path"
|
|
1013
|
+
do shell script "echo '" & originalSessionId & "' > /tmp/flip-view-session-" & sessionUUID
|
|
1014
|
+
do shell script "nohup ${nodePath} ${csqPath} flip --session " & sessionUUID & " " & quoted form of currentPath & " > /tmp/flip.log 2>&1 &"
|
|
1015
|
+
end tell
|
|
1016
|
+
end tell
|
|
1017
|
+
|
|
1018
|
+
return ""`;
|
|
1019
|
+
fs6.writeFileSync(applescriptPath, applescriptContent);
|
|
1020
|
+
fs6.chmodSync(applescriptPath, "755");
|
|
1021
|
+
const shContent = `#!/bin/bash
|
|
1022
|
+
osascript ${applescriptPath} > /dev/null 2>&1
|
|
1023
|
+
`;
|
|
1024
|
+
fs6.writeFileSync(shPath, shContent);
|
|
1025
|
+
fs6.chmodSync(shPath, "755");
|
|
1026
|
+
console.log("");
|
|
1027
|
+
console.log("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510");
|
|
1028
|
+
console.log("\u2502 Flip Hotkey Setup Wizard \u2502");
|
|
1029
|
+
console.log("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518");
|
|
1030
|
+
console.log("");
|
|
1031
|
+
console.log("\u2713 Scripts generated:");
|
|
1032
|
+
console.log(` ${shPath}`);
|
|
1033
|
+
console.log("");
|
|
1034
|
+
const openSettings = await confirm2({
|
|
1035
|
+
message: "Open iTerm2 Settings? (Keys \u2192 Key Bindings)",
|
|
1036
|
+
default: true
|
|
1037
|
+
});
|
|
1038
|
+
if (openSettings) {
|
|
1039
|
+
try {
|
|
1040
|
+
execSync3(`osascript -e 'tell application "iTerm2" to activate' -e 'tell application "System Events" to keystroke "," using command down'`);
|
|
1041
|
+
console.log("");
|
|
1042
|
+
console.log(" \u2192 iTerm2 Settings opened. Navigate to: Keys \u2192 Key Bindings");
|
|
1043
|
+
} catch {
|
|
1044
|
+
console.log(" \u2192 Could not open settings automatically. Open manually: iTerm2 \u2192 Settings \u2192 Keys \u2192 Key Bindings");
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
console.log("");
|
|
1048
|
+
const ready = await confirm2({
|
|
1049
|
+
message: "Ready to add Key Binding? (Click + button in Key Bindings tab)",
|
|
1050
|
+
default: true
|
|
1051
|
+
});
|
|
1052
|
+
if (!ready) {
|
|
1053
|
+
console.log("");
|
|
1054
|
+
console.log("Run `csq flip setup` again when ready.");
|
|
1055
|
+
return;
|
|
1056
|
+
}
|
|
1057
|
+
console.log("");
|
|
1058
|
+
console.log("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510");
|
|
1059
|
+
console.log("\u2502 Configure the Key Binding: \u2502");
|
|
1060
|
+
console.log("\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524");
|
|
1061
|
+
console.log("\u2502 1. Keyboard Shortcut: Press your hotkey \u2502");
|
|
1062
|
+
console.log("\u2502 (e.g., \u2318\u21E7F) \u2502");
|
|
1063
|
+
console.log("\u2502 \u2502");
|
|
1064
|
+
console.log('\u2502 2. Action: Select "Run Coprocess" \u2502');
|
|
1065
|
+
console.log("\u2502 \u2502");
|
|
1066
|
+
console.log("\u2502 3. Command: Paste the path (copied below) \u2502");
|
|
1067
|
+
console.log("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518");
|
|
1068
|
+
console.log("");
|
|
1069
|
+
await clipboardy2.write(shPath);
|
|
1070
|
+
console.log(`\u2713 Copied to clipboard: ${shPath}`);
|
|
1071
|
+
console.log("");
|
|
1072
|
+
await confirm2({
|
|
1073
|
+
message: "Done configuring? (Paste the path and click OK)",
|
|
1074
|
+
default: true
|
|
1075
|
+
});
|
|
1076
|
+
console.log("");
|
|
1077
|
+
console.log("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510");
|
|
1078
|
+
console.log("\u2502 Setup Complete! \u2502");
|
|
1079
|
+
console.log("\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524");
|
|
1080
|
+
console.log("\u2502 Usage: \u2502");
|
|
1081
|
+
console.log("\u2502 1. Focus on any terminal panel \u2502");
|
|
1082
|
+
console.log("\u2502 2. Press your hotkey \u2192 browser opens \u2502");
|
|
1083
|
+
console.log("\u2502 3. Select files, add comments \u2502");
|
|
1084
|
+
console.log("\u2502 4. Submit \u2192 text appears in terminal \u2502");
|
|
1085
|
+
console.log("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518");
|
|
1086
|
+
console.log("");
|
|
1087
|
+
}
|
|
1088
|
+
|
|
402
1089
|
// dist/index.js
|
|
1090
|
+
process.on("SIGINT", () => {
|
|
1091
|
+
process.exit(130);
|
|
1092
|
+
});
|
|
403
1093
|
var gitAdapter = new GitAdapter();
|
|
404
1094
|
async function main() {
|
|
405
1095
|
const args = process.argv.slice(2);
|
|
@@ -410,6 +1100,10 @@ async function main() {
|
|
|
410
1100
|
printShellInit();
|
|
411
1101
|
return;
|
|
412
1102
|
}
|
|
1103
|
+
if (command === "flip") {
|
|
1104
|
+
await runFlip(filteredArgs.slice(1));
|
|
1105
|
+
return;
|
|
1106
|
+
}
|
|
413
1107
|
const workspaceRoot = await findGitRoot(process.cwd());
|
|
414
1108
|
if (!workspaceRoot) {
|
|
415
1109
|
console.error(chalk2.red("Error: Not a git repository"));
|
|
@@ -436,13 +1130,18 @@ async function findGitRoot(cwd) {
|
|
|
436
1130
|
}
|
|
437
1131
|
return cwd;
|
|
438
1132
|
}
|
|
1133
|
+
function getProjectHash(workspaceRoot) {
|
|
1134
|
+
return crypto.createHash("sha256").update(workspaceRoot).digest("hex").slice(0, 8);
|
|
1135
|
+
}
|
|
439
1136
|
function getSessionsPath(workspaceRoot) {
|
|
440
|
-
|
|
1137
|
+
const projectHash = getProjectHash(workspaceRoot);
|
|
1138
|
+
const projectName = path7.basename(workspaceRoot);
|
|
1139
|
+
return path7.join(os3.homedir(), ".code-squad", "sessions", `${projectName}-${projectHash}.json`);
|
|
441
1140
|
}
|
|
442
1141
|
async function loadLocalThreads(workspaceRoot) {
|
|
443
1142
|
const sessionsPath = getSessionsPath(workspaceRoot);
|
|
444
1143
|
try {
|
|
445
|
-
const content = await
|
|
1144
|
+
const content = await fs7.promises.readFile(sessionsPath, "utf-8");
|
|
446
1145
|
const data = JSON.parse(content);
|
|
447
1146
|
return data.localThreads || [];
|
|
448
1147
|
} catch {
|
|
@@ -451,9 +1150,9 @@ async function loadLocalThreads(workspaceRoot) {
|
|
|
451
1150
|
}
|
|
452
1151
|
async function saveLocalThreads(workspaceRoot, threads) {
|
|
453
1152
|
const sessionsPath = getSessionsPath(workspaceRoot);
|
|
454
|
-
const dir =
|
|
455
|
-
await
|
|
456
|
-
await
|
|
1153
|
+
const dir = path7.dirname(sessionsPath);
|
|
1154
|
+
await fs7.promises.mkdir(dir, { recursive: true });
|
|
1155
|
+
await fs7.promises.writeFile(sessionsPath, JSON.stringify({ localThreads: threads }, null, 2));
|
|
457
1156
|
}
|
|
458
1157
|
async function addLocalThread(workspaceRoot, name) {
|
|
459
1158
|
const threads = await loadLocalThreads(workspaceRoot);
|
|
@@ -533,13 +1232,13 @@ async function listThreads(workspaceRoot) {
|
|
|
533
1232
|
}
|
|
534
1233
|
}
|
|
535
1234
|
async function createWorktree(workspaceRoot, name) {
|
|
536
|
-
const repoName =
|
|
537
|
-
const defaultBasePath =
|
|
1235
|
+
const repoName = path7.basename(workspaceRoot);
|
|
1236
|
+
const defaultBasePath = path7.join(path7.dirname(workspaceRoot), `${repoName}.worktree`);
|
|
538
1237
|
let worktreeName;
|
|
539
1238
|
let worktreePath;
|
|
540
1239
|
if (name) {
|
|
541
1240
|
worktreeName = name;
|
|
542
|
-
worktreePath =
|
|
1241
|
+
worktreePath = path7.join(defaultBasePath, name);
|
|
543
1242
|
} else {
|
|
544
1243
|
const form = await newWorktreeForm(defaultBasePath);
|
|
545
1244
|
if (!form) {
|
|
@@ -561,7 +1260,7 @@ async function createWorktree(workspaceRoot, name) {
|
|
|
561
1260
|
async function writeCdFile(targetPath) {
|
|
562
1261
|
const cdFile = process.env.CSQ_CD_FILE;
|
|
563
1262
|
if (cdFile) {
|
|
564
|
-
await
|
|
1263
|
+
await fs7.promises.writeFile(cdFile, targetPath);
|
|
565
1264
|
}
|
|
566
1265
|
}
|
|
567
1266
|
async function openNewTerminal(targetPath) {
|
|
@@ -612,7 +1311,7 @@ end tell`;
|
|
|
612
1311
|
async function interactiveMode(workspaceRoot) {
|
|
613
1312
|
const result = await runInteraction(workspaceRoot);
|
|
614
1313
|
if (result?.cdPath) {
|
|
615
|
-
await
|
|
1314
|
+
await openNewTerminal(result.cdPath);
|
|
616
1315
|
}
|
|
617
1316
|
}
|
|
618
1317
|
async function persistentInteractiveMode(workspaceRoot) {
|
|
@@ -655,7 +1354,7 @@ async function executeDelete(thread, workspaceRoot) {
|
|
|
655
1354
|
}
|
|
656
1355
|
async function runInteraction(workspaceRoot, _persistent = false) {
|
|
657
1356
|
const threads = await getAllThreads(workspaceRoot);
|
|
658
|
-
const repoName =
|
|
1357
|
+
const repoName = path7.basename(workspaceRoot);
|
|
659
1358
|
const choice = await selectThread(threads, repoName);
|
|
660
1359
|
if (choice.type === "exit") {
|
|
661
1360
|
return { exit: true };
|
|
@@ -686,7 +1385,7 @@ async function runInteraction(workspaceRoot, _persistent = false) {
|
|
|
686
1385
|
return null;
|
|
687
1386
|
}
|
|
688
1387
|
if (newType === "worktree") {
|
|
689
|
-
const defaultBasePath =
|
|
1388
|
+
const defaultBasePath = path7.join(path7.dirname(workspaceRoot), `${repoName}.worktree`);
|
|
690
1389
|
const form = await newWorktreeForm(defaultBasePath);
|
|
691
1390
|
if (!form) {
|
|
692
1391
|
return null;
|
|
@@ -716,6 +1415,9 @@ async function runInteraction(workspaceRoot, _persistent = false) {
|
|
|
716
1415
|
return null;
|
|
717
1416
|
}
|
|
718
1417
|
main().catch((error) => {
|
|
1418
|
+
if (error.message?.includes("SIGINT") || error.message?.includes("force closed")) {
|
|
1419
|
+
process.exit(130);
|
|
1420
|
+
}
|
|
719
1421
|
console.error(chalk2.red(`Error: ${error.message}`));
|
|
720
1422
|
process.exit(1);
|
|
721
1423
|
});
|