code-squad-cli 1.2.12 → 1.2.14

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.
@@ -24,6 +24,26 @@ export const FILTERED_PATTERNS = new Set([
24
24
  '__pycache__',
25
25
  '.pytest_cache',
26
26
  'target',
27
+ // macOS system folders (prevent FD exhaustion when run from home dir)
28
+ 'Library',
29
+ 'Applications',
30
+ 'Pictures',
31
+ 'Movies',
32
+ 'Music',
33
+ 'Downloads',
34
+ '.Trash',
35
+ '.local',
36
+ '.npm',
37
+ '.nvm',
38
+ '.cargo',
39
+ '.rustup',
40
+ '.volta',
41
+ '.pyenv',
42
+ '.rbenv',
43
+ // Linux system folders
44
+ '.config',
45
+ '.mozilla',
46
+ '.thunderbird',
27
47
  ]);
28
48
  export function isFiltered(name) {
29
49
  return FILTERED_PATTERNS.has(name);
@@ -4,6 +4,8 @@ import path from 'path';
4
4
  import { execSync } from 'child_process';
5
5
  import { copyToClipboard } from './output/clipboard.js';
6
6
  const DEFAULT_PORT = 51234;
7
+ // Use stderr for info messages (stdout goes to terminal input in coprocess mode)
8
+ const log = (...args) => console.error(...args);
7
9
  function formatTime() {
8
10
  const now = new Date();
9
11
  const hours = String(now.getHours()).padStart(2, '0');
@@ -63,34 +65,34 @@ export async function runFlip(args) {
63
65
  case 'serve': {
64
66
  // Daemon mode: start server, keep running
65
67
  const port = await findFreePort(DEFAULT_PORT);
66
- console.log(`Server running at http://localhost:${port}`);
67
- console.log('Press Ctrl+C to stop');
68
- console.log('');
69
- console.log('To open browser, run: csq flip open');
70
- console.log(`Or use hotkey to open: open http://localhost:${port}`);
68
+ log(`Server running at http://localhost:${port}`);
69
+ log('Press Ctrl+C to stop');
70
+ log('');
71
+ log('To open browser, run: csq flip open');
72
+ log(`Or use hotkey to open: open http://localhost:${port}`);
71
73
  // Run server in loop (restarts after each submit/cancel)
72
74
  while (true) {
73
75
  const server = new Server(cwd, port);
74
76
  const result = await server.run();
75
77
  if (result) {
76
- console.log(`[${formatTime()}] Submitted ${result.length} characters`);
78
+ log(`[${formatTime()}] Submitted ${result.length} characters`);
77
79
  }
78
80
  else {
79
- console.log(`[${formatTime()}] Cancelled`);
81
+ log(`[${formatTime()}] Cancelled`);
80
82
  }
81
- console.log(`[${formatTime()}] Ready for next session...`);
83
+ log(`[${formatTime()}] Ready for next session...`);
82
84
  }
83
85
  }
84
86
  case 'open': {
85
87
  // Just open browser to existing server
86
88
  const url = `http://localhost:${DEFAULT_PORT}`;
87
- console.log(`Opening ${url} in browser...`);
89
+ log(`Opening ${url} in browser...`);
88
90
  try {
89
91
  await open(url);
90
92
  }
91
93
  catch (e) {
92
- console.error('Failed to open browser:', e);
93
- console.error('Is the server running? Start with: csq flip serve');
94
+ log('Failed to open browser:', e);
95
+ log('Is the server running? Start with: csq flip serve');
94
96
  }
95
97
  break;
96
98
  }
@@ -101,21 +103,21 @@ export async function runFlip(args) {
101
103
  const url = sessionId
102
104
  ? `http://localhost:${port}?session=${sessionId}`
103
105
  : `http://localhost:${port}`;
104
- console.log(`Opening ${url} in browser...`);
106
+ log(`Opening ${url} in browser...`);
105
107
  try {
106
108
  await open(url);
107
109
  }
108
110
  catch (e) {
109
- console.error('Failed to open browser:', e);
110
- console.log(`Please open ${url} manually`);
111
+ log('Failed to open browser:', e);
112
+ log(`Please open ${url} manually`);
111
113
  }
112
114
  const server = new Server(cwd, port);
113
115
  const result = await server.run();
114
116
  if (result) {
115
- console.log(`\nSubmitted ${result.length} characters`);
117
+ log(`\nSubmitted ${result.length} characters`);
116
118
  }
117
119
  else {
118
- console.log('\nCancelled');
120
+ log('\nCancelled');
119
121
  }
120
122
  break;
121
123
  }
@@ -135,7 +137,6 @@ function printUsage() {
135
137
  console.error(' --session <uuid> Session ID for paste-back tracking');
136
138
  }
137
139
  async function setupHotkey() {
138
- // Get node and csq paths for iTerm2 setup
139
140
  let nodePath;
140
141
  try {
141
142
  nodePath = execSync('which node', { encoding: 'utf-8' }).trim();
@@ -144,39 +145,23 @@ async function setupHotkey() {
144
145
  nodePath = '/usr/local/bin/node';
145
146
  }
146
147
  const csqPath = new URL(import.meta.url).pathname;
148
+ const command = `${nodePath} ${csqPath} flip`;
147
149
  console.log('');
148
150
  console.log('┌─────────────────────────────────────────────────┐');
149
- console.log('│ Flip Hotkey Setup │');
151
+ console.log('│ Flip Hotkey Setup (iTerm2) │');
150
152
  console.log('└─────────────────────────────────────────────────┘');
151
153
  console.log('');
152
- console.log('Choose one of the following options:');
153
- console.log('');
154
- console.log('─────────────────────────────────────────────────');
155
- console.log('Option 1: Zsh keybinding (Alt+;)');
156
- console.log('─────────────────────────────────────────────────');
157
- console.log('Add to ~/.zshrc:');
158
- console.log('');
159
- console.log(' # Flip hotkey (Alt+;)');
160
- console.log(' csq-flip-widget() { (csq flip &); zle reset-prompt; }');
161
- console.log(' zle -N csq-flip-widget');
162
- console.log(" bindkey '\\e;' csq-flip-widget");
163
- console.log('');
164
- console.log('Then run: source ~/.zshrc');
165
- console.log('');
166
- console.log('─────────────────────────────────────────────────');
167
- console.log('Option 2: iTerm2 hotkey (any key combo)');
168
- console.log('─────────────────────────────────────────────────');
169
154
  console.log('1. iTerm2 → Settings → Keys → Key Bindings');
170
- console.log('2. Click + to add new binding');
171
- console.log('3. Set your hotkey (e.g., ⌘⇧F)');
155
+ console.log('2. + 클릭');
156
+ console.log('3. Keyboard Shortcut: ⌘⇧F (Cmd+Shift+F)');
172
157
  console.log('4. Action: "Run Coprocess"');
173
- console.log('5. Command:');
158
+ console.log('5. Command (아래 자동 복사됨):');
174
159
  console.log('');
175
- console.log(` ${nodePath} ${csqPath} flip`);
160
+ console.log(` ${command}`);
176
161
  console.log('');
177
162
  try {
178
- await copyToClipboard(`${nodePath} ${csqPath} flip`);
179
- console.log(' (Copied to clipboard!)');
163
+ await copyToClipboard(command);
164
+ console.log(' (Copied to clipboard!)');
180
165
  }
181
166
  catch {
182
167
  // Ignore clipboard errors
@@ -8,8 +8,7 @@ export function createStaticRouter() {
8
8
  // Get __dirname equivalent in ESM
9
9
  const __filename = fileURLToPath(import.meta.url);
10
10
  const __dirname = path.dirname(__filename);
11
- // First, check for the bundled path. This is more robust than checking the filename.
12
- // - Bundled: dist/flip-ui/dist
11
+ // Bundled: __dirname is dist/, flip-ui is at dist/flip-ui/dist
13
12
  let distPath = path.resolve(__dirname, 'flip-ui/dist');
14
13
  // If the bundled path doesn't exist, fall back to the unbundled (development) path.
15
14
  // - Unbundled: packages/cli/flip-ui/dist
@@ -26,9 +26,9 @@ router.post('/', async (req, res) => {
26
26
  }
27
27
  catch (e) {
28
28
  console.error('Failed to copy to clipboard:', e);
29
- console.log('\n--- Output (copy manually) ---');
30
- console.log(formatted);
31
- console.log('--- End of output ---\n');
29
+ console.error('\n--- Output (copy manually) ---');
30
+ console.error(formatted);
31
+ console.error('--- End of output ---\n');
32
32
  }
33
33
  // Schedule paste only if clipboard copy succeeded
34
34
  if (clipboardSuccess) {
@@ -64,7 +64,7 @@ export class Server {
64
64
  resolve(output);
65
65
  };
66
66
  server.listen(this.port, '127.0.0.1', () => {
67
- console.log(`Server running at http://localhost:${this.port}`);
67
+ console.error(`Server running at http://localhost:${this.port}`);
68
68
  });
69
69
  });
70
70
  }
@@ -16,15 +16,15 @@ export class FileWatcher {
16
16
  this.debounceMs = options.debounceMs ?? 300;
17
17
  }
18
18
  start() {
19
- const ignoredPatterns = Array.from(FILTERED_PATTERNS).map(pattern => {
20
- // .git은 감시하되 내부 변경은 git-changed 이벤트로 처리
21
- if (pattern === '.git') {
22
- return undefined;
23
- }
24
- return `**/${pattern}/**`;
25
- }).filter(Boolean);
19
+ // Use function-based ignore to prevent chokidar from even opening filtered directories
20
+ const shouldIgnore = (filePath) => {
21
+ const relativePath = path.relative(this.cwd, filePath);
22
+ const segments = relativePath.split(path.sep);
23
+ // Ignore if any path segment matches filtered patterns (except .git)
24
+ return segments.some(segment => segment !== '.git' && FILTERED_PATTERNS.has(segment));
25
+ };
26
26
  this.watcher = chokidar.watch(this.cwd, {
27
- ignored: ignoredPatterns,
27
+ ignored: shouldIgnore,
28
28
  persistent: true,
29
29
  ignoreInitial: true,
30
30
  awaitWriteFinish: {
package/dist/index.js CHANGED
@@ -448,7 +448,27 @@ var FILTERED_PATTERNS = /* @__PURE__ */ new Set([
448
448
  "coverage",
449
449
  "__pycache__",
450
450
  ".pytest_cache",
451
- "target"
451
+ "target",
452
+ // macOS system folders (prevent FD exhaustion when run from home dir)
453
+ "Library",
454
+ "Applications",
455
+ "Pictures",
456
+ "Movies",
457
+ "Music",
458
+ "Downloads",
459
+ ".Trash",
460
+ ".local",
461
+ ".npm",
462
+ ".nvm",
463
+ ".cargo",
464
+ ".rustup",
465
+ ".volta",
466
+ ".pyenv",
467
+ ".rbenv",
468
+ // Linux system folders
469
+ ".config",
470
+ ".mozilla",
471
+ ".thunderbird"
452
472
  ]);
453
473
  function isFiltered(name) {
454
474
  return FILTERED_PATTERNS.has(name);
@@ -1069,9 +1089,9 @@ router4.post("/", async (req, res) => {
1069
1089
  clipboardSuccess = true;
1070
1090
  } catch (e) {
1071
1091
  console.error("Failed to copy to clipboard:", e);
1072
- console.log("\n--- Output (copy manually) ---");
1073
- console.log(formatted);
1074
- console.log("--- End of output ---\n");
1092
+ console.error("\n--- Output (copy manually) ---");
1093
+ console.error(formatted);
1094
+ console.error("--- End of output ---\n");
1075
1095
  }
1076
1096
  if (clipboardSuccess) {
1077
1097
  try {
@@ -1169,14 +1189,13 @@ var FileWatcher = class {
1169
1189
  this.debounceMs = options.debounceMs ?? 300;
1170
1190
  }
1171
1191
  start() {
1172
- const ignoredPatterns = Array.from(FILTERED_PATTERNS).map((pattern) => {
1173
- if (pattern === ".git") {
1174
- return void 0;
1175
- }
1176
- return `**/${pattern}/**`;
1177
- }).filter(Boolean);
1192
+ const shouldIgnore = (filePath) => {
1193
+ const relativePath = path8.relative(this.cwd, filePath);
1194
+ const segments = relativePath.split(path8.sep);
1195
+ return segments.some((segment) => segment !== ".git" && FILTERED_PATTERNS.has(segment));
1196
+ };
1178
1197
  this.watcher = chokidar.watch(this.cwd, {
1179
- ignored: ignoredPatterns,
1198
+ ignored: shouldIgnore,
1180
1199
  persistent: true,
1181
1200
  ignoreInitial: true,
1182
1201
  awaitWriteFinish: {
@@ -1352,7 +1371,7 @@ var Server = class {
1352
1371
  resolve2(output);
1353
1372
  };
1354
1373
  server.listen(this.port, "127.0.0.1", () => {
1355
- console.log(`Server running at http://localhost:${this.port}`);
1374
+ console.error(`Server running at http://localhost:${this.port}`);
1356
1375
  });
1357
1376
  });
1358
1377
  }
@@ -1382,6 +1401,7 @@ import open from "open";
1382
1401
  import path9 from "path";
1383
1402
  import { execSync as execSync2 } from "child_process";
1384
1403
  var DEFAULT_PORT = 51234;
1404
+ var log = (...args) => console.error(...args);
1385
1405
  function formatTime() {
1386
1406
  const now = /* @__PURE__ */ new Date();
1387
1407
  const hours = String(now.getHours()).padStart(2, "0");
@@ -1436,30 +1456,30 @@ async function runFlip(args) {
1436
1456
  }
1437
1457
  case "serve": {
1438
1458
  const port = await findFreePort(DEFAULT_PORT);
1439
- console.log(`Server running at http://localhost:${port}`);
1440
- console.log("Press Ctrl+C to stop");
1441
- console.log("");
1442
- console.log("To open browser, run: csq flip open");
1443
- console.log(`Or use hotkey to open: open http://localhost:${port}`);
1459
+ log(`Server running at http://localhost:${port}`);
1460
+ log("Press Ctrl+C to stop");
1461
+ log("");
1462
+ log("To open browser, run: csq flip open");
1463
+ log(`Or use hotkey to open: open http://localhost:${port}`);
1444
1464
  while (true) {
1445
1465
  const server = new Server(cwd, port);
1446
1466
  const result = await server.run();
1447
1467
  if (result) {
1448
- console.log(`[${formatTime()}] Submitted ${result.length} characters`);
1468
+ log(`[${formatTime()}] Submitted ${result.length} characters`);
1449
1469
  } else {
1450
- console.log(`[${formatTime()}] Cancelled`);
1470
+ log(`[${formatTime()}] Cancelled`);
1451
1471
  }
1452
- console.log(`[${formatTime()}] Ready for next session...`);
1472
+ log(`[${formatTime()}] Ready for next session...`);
1453
1473
  }
1454
1474
  }
1455
1475
  case "open": {
1456
1476
  const url = `http://localhost:${DEFAULT_PORT}`;
1457
- console.log(`Opening ${url} in browser...`);
1477
+ log(`Opening ${url} in browser...`);
1458
1478
  try {
1459
1479
  await open(url);
1460
1480
  } catch (e) {
1461
- console.error("Failed to open browser:", e);
1462
- console.error("Is the server running? Start with: csq flip serve");
1481
+ log("Failed to open browser:", e);
1482
+ log("Is the server running? Start with: csq flip serve");
1463
1483
  }
1464
1484
  break;
1465
1485
  }
@@ -1467,20 +1487,20 @@ async function runFlip(args) {
1467
1487
  default: {
1468
1488
  const port = await findFreePort(DEFAULT_PORT);
1469
1489
  const url = sessionId ? `http://localhost:${port}?session=${sessionId}` : `http://localhost:${port}`;
1470
- console.log(`Opening ${url} in browser...`);
1490
+ log(`Opening ${url} in browser...`);
1471
1491
  try {
1472
1492
  await open(url);
1473
1493
  } catch (e) {
1474
- console.error("Failed to open browser:", e);
1475
- console.log(`Please open ${url} manually`);
1494
+ log("Failed to open browser:", e);
1495
+ log(`Please open ${url} manually`);
1476
1496
  }
1477
1497
  const server = new Server(cwd, port);
1478
1498
  const result = await server.run();
1479
1499
  if (result) {
1480
- console.log(`
1500
+ log(`
1481
1501
  Submitted ${result.length} characters`);
1482
1502
  } else {
1483
- console.log("\nCancelled");
1503
+ log("\nCancelled");
1484
1504
  }
1485
1505
  break;
1486
1506
  }
@@ -1507,39 +1527,23 @@ async function setupHotkey() {
1507
1527
  nodePath = "/usr/local/bin/node";
1508
1528
  }
1509
1529
  const csqPath = new URL(import.meta.url).pathname;
1530
+ const command = `${nodePath} ${csqPath} flip`;
1510
1531
  console.log("");
1511
1532
  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");
1512
- console.log("\u2502 Flip Hotkey Setup \u2502");
1533
+ console.log("\u2502 Flip Hotkey Setup (iTerm2) \u2502");
1513
1534
  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");
1514
1535
  console.log("");
1515
- console.log("Choose one of the following options:");
1516
- console.log("");
1517
- console.log("\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");
1518
- console.log("Option 1: Zsh keybinding (Alt+;)");
1519
- console.log("\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");
1520
- console.log("Add to ~/.zshrc:");
1521
- console.log("");
1522
- console.log(" # Flip hotkey (Alt+;)");
1523
- console.log(" csq-flip-widget() { (csq flip &); zle reset-prompt; }");
1524
- console.log(" zle -N csq-flip-widget");
1525
- console.log(" bindkey '\\e;' csq-flip-widget");
1526
- console.log("");
1527
- console.log("Then run: source ~/.zshrc");
1528
- console.log("");
1529
- console.log("\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");
1530
- console.log("Option 2: iTerm2 hotkey (any key combo)");
1531
- console.log("\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");
1532
1536
  console.log("1. iTerm2 \u2192 Settings \u2192 Keys \u2192 Key Bindings");
1533
- console.log("2. Click + to add new binding");
1534
- console.log("3. Set your hotkey (e.g., \u2318\u21E7F)");
1537
+ console.log("2. + \uD074\uB9AD");
1538
+ console.log("3. Keyboard Shortcut: \u2318\u21E7F (Cmd+Shift+F)");
1535
1539
  console.log('4. Action: "Run Coprocess"');
1536
- console.log("5. Command:");
1540
+ console.log("5. Command (\uC544\uB798 \uC790\uB3D9 \uBCF5\uC0AC\uB428):");
1537
1541
  console.log("");
1538
- console.log(` ${nodePath} ${csqPath} flip`);
1542
+ console.log(` ${command}`);
1539
1543
  console.log("");
1540
1544
  try {
1541
- await copyToClipboard(`${nodePath} ${csqPath} flip`);
1542
- console.log(" (Copied to clipboard!)");
1545
+ await copyToClipboard(command);
1546
+ console.log(" (Copied to clipboard!)");
1543
1547
  } catch {
1544
1548
  }
1545
1549
  console.log("");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "code-squad-cli",
3
- "version": "1.2.12",
3
+ "version": "1.2.14",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "csq": "./dist/index.js"