@toothfairyai/tfcode 1.0.0-beta.4 → 1.0.0-beta.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.
Files changed (2) hide show
  1. package/bin/tfcode.js +114 -86
  2. package/package.json +1 -1
package/bin/tfcode.js CHANGED
@@ -42,47 +42,35 @@ function info(msg) {
42
42
 
43
43
  function question(rl, prompt, hidden = false) {
44
44
  return new Promise((resolve) => {
45
- if (hidden) {
45
+ if (hidden && process.stdin.isTTY) {
46
+ // Use simpler approach for hidden input
46
47
  process.stdout.write(prompt);
47
48
 
48
49
  let input = '';
49
50
  const stdin = process.stdin;
50
51
 
51
- if (stdin.isTTY) {
52
- stdin.setRawMode(true);
53
- }
52
+ stdin.setRawMode(true);
53
+ stdin.setEncoding('utf8');
54
54
  stdin.resume();
55
55
 
56
- const onData = (char) => {
57
- const c = char.toString('utf8');
58
-
59
- switch (c) {
60
- case '\n':
61
- case '\r':
62
- case '\u0004':
63
- if (stdin.isTTY) {
64
- stdin.setRawMode(false);
65
- }
66
- stdin.pause();
67
- stdin.removeListener('data', onData);
68
- process.stdout.write('\n');
69
- resolve(input);
70
- break;
71
- case '\u0003':
72
- process.stdout.write('\n');
73
- process.exit();
74
- break;
75
- case '\u007F':
76
- case '\b':
77
- input = input.slice(0, -1);
78
- break;
79
- default:
80
- input += c;
81
- break;
56
+ const onKeypress = (str) => {
57
+ if (str === '\n' || str === '\r' || str === '\u0004') {
58
+ stdin.setRawMode(false);
59
+ stdin.pause();
60
+ stdin.removeListener('data', onKeypress);
61
+ process.stdout.write('\n');
62
+ resolve(input);
63
+ } else if (str === '\u0003') {
64
+ process.stdout.write('\n');
65
+ process.exit();
66
+ } else if (str === '\u007F' || str === '\b') {
67
+ input = input.slice(0, -1);
68
+ } else if (str.length === 1 && str.charCodeAt(0) >= 32) {
69
+ input += str;
82
70
  }
83
71
  };
84
72
 
85
- stdin.on('data', onData);
73
+ stdin.on('data', onKeypress);
86
74
  } else {
87
75
  rl.question(prompt, (answer) => {
88
76
  resolve(answer.trim());
@@ -101,42 +89,17 @@ function select(prompt, options) {
101
89
  });
102
90
  log('');
103
91
 
104
- process.stdout.write('Select (1-' + options.length + '): ');
105
-
106
- let input = '';
107
- const stdin = process.stdin;
108
-
109
- if (stdin.isTTY) {
110
- stdin.setRawMode(true);
111
- }
112
- stdin.resume();
113
-
114
- const onData = (char) => {
115
- const c = char.toString('utf8');
116
-
117
- if (c === '\n' || c === '\r' || c === '\u0004') {
118
- if (stdin.isTTY) {
119
- stdin.setRawMode(false);
120
- }
121
- stdin.pause();
122
- stdin.removeListener('data', onData);
123
- process.stdout.write('\n');
124
-
125
- const idx = parseInt(input) - 1;
126
- resolve(idx >= 0 && idx < options.length ? idx : 0);
127
- } else if (c === '\u0003') {
128
- process.stdout.write('\n');
129
- process.exit();
130
- } else if (c >= '1' && c <= '9') {
131
- input += c;
132
- process.stdout.write(c);
133
- } else if (c === '\u007F' || c === '\b') {
134
- input = input.slice(0, -1);
135
- process.stdout.write('\b \b');
136
- }
137
- };
92
+ // Create a fresh readline for select
93
+ const rlSelect = readline.createInterface({
94
+ input: process.stdin,
95
+ output: process.stdout
96
+ });
138
97
 
139
- stdin.on('data', onData);
98
+ rlSelect.question('Select (1-' + options.length + '): ', (answer) => {
99
+ rlSelect.close();
100
+ const idx = parseInt(answer.trim()) - 1;
101
+ resolve(idx >= 0 && idx < options.length ? idx : 0);
102
+ });
140
103
  });
141
104
  }
142
105
 
@@ -287,11 +250,6 @@ function saveToolsCache(tools) {
287
250
  }
288
251
 
289
252
  async function interactiveSetup() {
290
- const rl = readline.createInterface({
291
- input: process.stdin,
292
- output: process.stdout
293
- });
294
-
295
253
  log('');
296
254
  log(`${COLORS.bold}${COLORS.magenta}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${COLORS.reset}`);
297
255
  log(`${COLORS.bold}${COLORS.magenta} tfcode Setup${COLORS.reset}`);
@@ -303,35 +261,83 @@ async function interactiveSetup() {
303
261
  log(`${COLORS.dim} https://app.toothfairyai.com → Settings → API Keys${COLORS.reset}`);
304
262
  log('');
305
263
 
306
- // Workspace ID
264
+ // Step 1: Workspace ID
307
265
  log(`${COLORS.bold}Step 1: Workspace ID${COLORS.reset}`);
308
266
  log(`${COLORS.dim}This is your workspace UUID (e.g., 12345678-1234-1234-1234-123456789012)${COLORS.reset}`);
309
267
  log('');
310
- const workspaceId = await question(rl, 'Enter your Workspace ID: ');
268
+
269
+ const workspaceId = await new Promise((resolve) => {
270
+ const rl = readline.createInterface({
271
+ input: process.stdin,
272
+ output: process.stdout
273
+ });
274
+ rl.question('Enter your Workspace ID: ', (answer) => {
275
+ rl.close();
276
+ resolve(answer.trim());
277
+ });
278
+ });
311
279
 
312
280
  if (!workspaceId) {
313
281
  error('Workspace ID is required');
314
- rl.close();
315
282
  process.exit(1);
316
283
  }
317
284
 
318
285
  log('');
319
286
 
320
- // API Key
287
+ // Step 2: API Key (hidden)
321
288
  log(`${COLORS.bold}Step 2: API Key${COLORS.reset}`);
322
289
  log(`${COLORS.dim}Your API key will be hidden as you type${COLORS.reset}`);
323
290
  log('');
324
- const apiKey = await question(rl, 'Enter your API Key: ', true);
291
+
292
+ const apiKey = await new Promise((resolve) => {
293
+ if (!process.stdin.isTTY) {
294
+ const rl = readline.createInterface({
295
+ input: process.stdin,
296
+ output: process.stdout
297
+ });
298
+ rl.question('Enter your API Key: ', (answer) => {
299
+ rl.close();
300
+ resolve(answer.trim());
301
+ });
302
+ return;
303
+ }
304
+
305
+ process.stdout.write('Enter your API Key: ');
306
+ let input = '';
307
+ const stdin = process.stdin;
308
+
309
+ stdin.setRawMode(true);
310
+ stdin.setEncoding('utf8');
311
+ stdin.resume();
312
+
313
+ const onKeypress = (str) => {
314
+ if (str === '\n' || str === '\r' || str === '\u0004') {
315
+ stdin.setRawMode(false);
316
+ stdin.pause();
317
+ stdin.removeListener('data', onKeypress);
318
+ process.stdout.write('\n');
319
+ resolve(input);
320
+ } else if (str === '\u0003') {
321
+ process.stdout.write('\n');
322
+ process.exit();
323
+ } else if (str === '\u007F' || str === '\b') {
324
+ input = input.slice(0, -1);
325
+ } else if (str.length === 1 && str.charCodeAt(0) >= 32) {
326
+ input += str;
327
+ }
328
+ };
329
+
330
+ stdin.on('data', onKeypress);
331
+ });
325
332
 
326
333
  if (!apiKey) {
327
334
  error('API Key is required');
328
- rl.close();
329
335
  process.exit(1);
330
336
  }
331
337
 
332
338
  log('');
333
339
 
334
- // Region
340
+ // Step 3: Region
335
341
  log(`${COLORS.bold}Step 3: Region${COLORS.reset}`);
336
342
  const regions = ['dev (Development)', 'au (Australia)', 'eu (Europe)', 'us (United States)'];
337
343
  const regionIdx = await select('Select your region:', regions);
@@ -349,8 +355,16 @@ async function interactiveSetup() {
349
355
  log(` Region: ${region}`);
350
356
  log('');
351
357
 
352
- rl.resume();
353
- const confirm = await question(rl, 'Save these credentials? (Y/n): ');
358
+ const confirm = await new Promise((resolve) => {
359
+ const rl = readline.createInterface({
360
+ input: process.stdin,
361
+ output: process.stdout
362
+ });
363
+ rl.question('Save these credentials? (Y/n): ', (answer) => {
364
+ rl.close();
365
+ resolve(answer.trim());
366
+ });
367
+ });
354
368
 
355
369
  if (confirm.toLowerCase() !== 'n' && confirm.toLowerCase() !== 'no') {
356
370
  const config = { workspace_id: workspaceId, api_key: apiKey, region };
@@ -359,8 +373,16 @@ async function interactiveSetup() {
359
373
  log('');
360
374
 
361
375
  // Validate
362
- rl.resume();
363
- const testNow = await question(rl, 'Validate credentials now? (Y/n): ');
376
+ const testNow = await new Promise((resolve) => {
377
+ const rl = readline.createInterface({
378
+ input: process.stdin,
379
+ output: process.stdout
380
+ });
381
+ rl.question('Validate credentials now? (Y/n): ', (answer) => {
382
+ rl.close();
383
+ resolve(answer.trim());
384
+ });
385
+ });
364
386
 
365
387
  if (testNow.toLowerCase() !== 'n' && testNow.toLowerCase() !== 'no') {
366
388
  log('');
@@ -376,8 +398,16 @@ async function interactiveSetup() {
376
398
  log(` Workspace ID: ${result.workspace_id}`);
377
399
  log('');
378
400
 
379
- rl.resume();
380
- const syncNow = await question(rl, 'Sync tools now? (Y/n): ');
401
+ const syncNow = await new Promise((resolve) => {
402
+ const rl = readline.createInterface({
403
+ input: process.stdin,
404
+ output: process.stdout
405
+ });
406
+ rl.question('Sync tools now? (Y/n): ', (answer) => {
407
+ rl.close();
408
+ resolve(answer.trim());
409
+ });
410
+ });
381
411
 
382
412
  if (syncNow.toLowerCase() !== 'n' && syncNow.toLowerCase() !== 'no') {
383
413
  log('');
@@ -433,8 +463,6 @@ async function interactiveSetup() {
433
463
  } else {
434
464
  log('Setup cancelled.');
435
465
  }
436
-
437
- rl.close();
438
466
  }
439
467
 
440
468
  const cli = yargs(hideBin(process.argv))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toothfairyai/tfcode",
3
- "version": "1.0.0-beta.4",
3
+ "version": "1.0.0-beta.5",
4
4
  "description": "ToothFairyAI's official AI coding agent",
5
5
  "keywords": ["toothfairyai", "ai", "coding", "cli"],
6
6
  "author": "ToothFairyAI",