@toothfairyai/tfcode 1.0.0-beta.4 → 1.0.0-beta.6

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 +88 -87
  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,56 @@ 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
321
288
  log(`${COLORS.bold}Step 2: API Key${COLORS.reset}`);
322
- log(`${COLORS.dim}Your API key will be hidden as you type${COLORS.reset}`);
289
+ log(`${COLORS.dim}Paste or type your API key${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
+ const rl = readline.createInterface({
294
+ input: process.stdin,
295
+ output: process.stdout
296
+ });
297
+
298
+ // Simple approach - just show the input (API keys are long anyway)
299
+ // This allows paste and works reliably
300
+ rl.question('Enter your API Key: ', (answer) => {
301
+ rl.close();
302
+ resolve(answer.trim());
303
+ });
304
+ });
325
305
 
326
306
  if (!apiKey) {
327
307
  error('API Key is required');
328
- rl.close();
329
308
  process.exit(1);
330
309
  }
331
310
 
332
311
  log('');
333
312
 
334
- // Region
313
+ // Step 3: Region
335
314
  log(`${COLORS.bold}Step 3: Region${COLORS.reset}`);
336
315
  const regions = ['dev (Development)', 'au (Australia)', 'eu (Europe)', 'us (United States)'];
337
316
  const regionIdx = await select('Select your region:', regions);
@@ -349,8 +328,16 @@ async function interactiveSetup() {
349
328
  log(` Region: ${region}`);
350
329
  log('');
351
330
 
352
- rl.resume();
353
- const confirm = await question(rl, 'Save these credentials? (Y/n): ');
331
+ const confirm = await new Promise((resolve) => {
332
+ const rl = readline.createInterface({
333
+ input: process.stdin,
334
+ output: process.stdout
335
+ });
336
+ rl.question('Save these credentials? (Y/n): ', (answer) => {
337
+ rl.close();
338
+ resolve(answer.trim());
339
+ });
340
+ });
354
341
 
355
342
  if (confirm.toLowerCase() !== 'n' && confirm.toLowerCase() !== 'no') {
356
343
  const config = { workspace_id: workspaceId, api_key: apiKey, region };
@@ -359,8 +346,16 @@ async function interactiveSetup() {
359
346
  log('');
360
347
 
361
348
  // Validate
362
- rl.resume();
363
- const testNow = await question(rl, 'Validate credentials now? (Y/n): ');
349
+ const testNow = await new Promise((resolve) => {
350
+ const rl = readline.createInterface({
351
+ input: process.stdin,
352
+ output: process.stdout
353
+ });
354
+ rl.question('Validate credentials now? (Y/n): ', (answer) => {
355
+ rl.close();
356
+ resolve(answer.trim());
357
+ });
358
+ });
364
359
 
365
360
  if (testNow.toLowerCase() !== 'n' && testNow.toLowerCase() !== 'no') {
366
361
  log('');
@@ -376,8 +371,16 @@ async function interactiveSetup() {
376
371
  log(` Workspace ID: ${result.workspace_id}`);
377
372
  log('');
378
373
 
379
- rl.resume();
380
- const syncNow = await question(rl, 'Sync tools now? (Y/n): ');
374
+ const syncNow = await new Promise((resolve) => {
375
+ const rl = readline.createInterface({
376
+ input: process.stdin,
377
+ output: process.stdout
378
+ });
379
+ rl.question('Sync tools now? (Y/n): ', (answer) => {
380
+ rl.close();
381
+ resolve(answer.trim());
382
+ });
383
+ });
381
384
 
382
385
  if (syncNow.toLowerCase() !== 'n' && syncNow.toLowerCase() !== 'no') {
383
386
  log('');
@@ -433,8 +436,6 @@ async function interactiveSetup() {
433
436
  } else {
434
437
  log('Setup cancelled.');
435
438
  }
436
-
437
- rl.close();
438
439
  }
439
440
 
440
441
  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.6",
4
4
  "description": "ToothFairyAI's official AI coding agent",
5
5
  "keywords": ["toothfairyai", "ai", "coding", "cli"],
6
6
  "author": "ToothFairyAI",