@toothfairyai/tfcode 1.0.0-beta.3 → 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 +119 -58
  2. package/package.json +1 -1
package/bin/tfcode.js CHANGED
@@ -42,45 +42,35 @@ function info(msg) {
42
42
 
43
43
  function question(rl, prompt, hidden = false) {
44
44
  return new Promise((resolve) => {
45
- if (hidden) {
46
- const stdin = process.stdin;
47
- const wasRaw = stdin.isRaw;
48
-
45
+ if (hidden && process.stdin.isTTY) {
46
+ // Use simpler approach for hidden input
49
47
  process.stdout.write(prompt);
50
48
 
51
- if (stdin.isTTY) {
52
- stdin.setRawMode(true);
53
- }
54
-
55
49
  let input = '';
50
+ const stdin = process.stdin;
56
51
 
52
+ stdin.setRawMode(true);
53
+ stdin.setEncoding('utf8');
57
54
  stdin.resume();
58
- stdin.on('data', function charListener(char) {
59
- const c = char.toString('utf8');
60
-
61
- switch (c) {
62
- case '\n':
63
- case '\r':
64
- case '\u0004':
65
- if (stdin.isTTY) {
66
- stdin.setRawMode(wasRaw || false);
67
- }
68
- stdin.pause();
69
- stdin.removeListener('data', charListener);
70
- process.stdout.write('\n');
71
- resolve(input);
72
- break;
73
- case '\u0003':
74
- process.exit();
75
- break;
76
- case '\u007F':
77
- input = input.slice(0, -1);
78
- break;
79
- default:
80
- input += c;
81
- break;
55
+
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
+ };
72
+
73
+ stdin.on('data', onKeypress);
84
74
  } else {
85
75
  rl.question(prompt, (answer) => {
86
76
  resolve(answer.trim());
@@ -89,7 +79,7 @@ function question(rl, prompt, hidden = false) {
89
79
  });
90
80
  }
91
81
 
92
- function select(rl, prompt, options) {
82
+ function select(prompt, options) {
93
83
  return new Promise((resolve) => {
94
84
  log('');
95
85
  log(prompt);
@@ -99,13 +89,16 @@ function select(rl, prompt, options) {
99
89
  });
100
90
  log('');
101
91
 
102
- rl.question('Select (1-' + options.length + '): ', (answer) => {
92
+ // Create a fresh readline for select
93
+ const rlSelect = readline.createInterface({
94
+ input: process.stdin,
95
+ output: process.stdout
96
+ });
97
+
98
+ rlSelect.question('Select (1-' + options.length + '): ', (answer) => {
99
+ rlSelect.close();
103
100
  const idx = parseInt(answer.trim()) - 1;
104
- if (idx >= 0 && idx < options.length) {
105
- resolve(idx);
106
- } else {
107
- resolve(0);
108
- }
101
+ resolve(idx >= 0 && idx < options.length ? idx : 0);
109
102
  });
110
103
  });
111
104
  }
@@ -257,11 +250,6 @@ function saveToolsCache(tools) {
257
250
  }
258
251
 
259
252
  async function interactiveSetup() {
260
- const rl = readline.createInterface({
261
- input: process.stdin,
262
- output: process.stdout
263
- });
264
-
265
253
  log('');
266
254
  log(`${COLORS.bold}${COLORS.magenta}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${COLORS.reset}`);
267
255
  log(`${COLORS.bold}${COLORS.magenta} tfcode Setup${COLORS.reset}`);
@@ -273,38 +261,86 @@ async function interactiveSetup() {
273
261
  log(`${COLORS.dim} https://app.toothfairyai.com → Settings → API Keys${COLORS.reset}`);
274
262
  log('');
275
263
 
276
- // Workspace ID
264
+ // Step 1: Workspace ID
277
265
  log(`${COLORS.bold}Step 1: Workspace ID${COLORS.reset}`);
278
266
  log(`${COLORS.dim}This is your workspace UUID (e.g., 12345678-1234-1234-1234-123456789012)${COLORS.reset}`);
279
267
  log('');
280
- 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
+ });
281
279
 
282
280
  if (!workspaceId) {
283
281
  error('Workspace ID is required');
284
- rl.close();
285
282
  process.exit(1);
286
283
  }
287
284
 
288
285
  log('');
289
286
 
290
- // API Key
287
+ // Step 2: API Key (hidden)
291
288
  log(`${COLORS.bold}Step 2: API Key${COLORS.reset}`);
292
289
  log(`${COLORS.dim}Your API key will be hidden as you type${COLORS.reset}`);
293
290
  log('');
294
- 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
+ });
295
332
 
296
333
  if (!apiKey) {
297
334
  error('API Key is required');
298
- rl.close();
299
335
  process.exit(1);
300
336
  }
301
337
 
302
338
  log('');
303
339
 
304
- // Region
340
+ // Step 3: Region
305
341
  log(`${COLORS.bold}Step 3: Region${COLORS.reset}`);
306
342
  const regions = ['dev (Development)', 'au (Australia)', 'eu (Europe)', 'us (United States)'];
307
- const regionIdx = await select(rl, 'Select your region:', regions);
343
+ const regionIdx = await select('Select your region:', regions);
308
344
  const regions_map = ['dev', 'au', 'eu', 'us'];
309
345
  const region = regions_map[regionIdx];
310
346
 
@@ -319,7 +355,16 @@ async function interactiveSetup() {
319
355
  log(` Region: ${region}`);
320
356
  log('');
321
357
 
322
- 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
+ });
323
368
 
324
369
  if (confirm.toLowerCase() !== 'n' && confirm.toLowerCase() !== 'no') {
325
370
  const config = { workspace_id: workspaceId, api_key: apiKey, region };
@@ -328,7 +373,16 @@ async function interactiveSetup() {
328
373
  log('');
329
374
 
330
375
  // Validate
331
- 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
+ });
332
386
 
333
387
  if (testNow.toLowerCase() !== 'n' && testNow.toLowerCase() !== 'no') {
334
388
  log('');
@@ -344,7 +398,16 @@ async function interactiveSetup() {
344
398
  log(` Workspace ID: ${result.workspace_id}`);
345
399
  log('');
346
400
 
347
- 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
+ });
348
411
 
349
412
  if (syncNow.toLowerCase() !== 'n' && syncNow.toLowerCase() !== 'no') {
350
413
  log('');
@@ -400,8 +463,6 @@ async function interactiveSetup() {
400
463
  } else {
401
464
  log('Setup cancelled.');
402
465
  }
403
-
404
- rl.close();
405
466
  }
406
467
 
407
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.3",
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",