@toothfairyai/tfcode 1.0.0-beta.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/README.md ADDED
@@ -0,0 +1,127 @@
1
+ # tfcode
2
+
3
+ **ToothFairyAI's official AI coding agent** - A terminal-based coding assistant with seamless integration to your ToothFairyAI workspace.
4
+
5
+ ## Features
6
+
7
+ - 🤖 **AI-Powered Coding** - Intelligent code generation, refactoring, and debugging
8
+ - 🔌 **Tool Integration** - Sync and use tools from your ToothFairyAI workspace
9
+ - 💻 **Terminal-Based** - Fast, lightweight, runs in your terminal
10
+ - 🔐 **Secure** - Credentials stay in ToothFairyAI, route via TF Proxy
11
+
12
+ ## Requirements
13
+
14
+ - **Python 3.10+** - Required for ToothFairyAI integration
15
+ - **Node.js 18+** - Required to run tfcode
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ npm install -g tfcode
21
+ ```
22
+
23
+ The installer will:
24
+ - Check Python is installed
25
+ - Install the ToothFairyAI Python SDK automatically
26
+
27
+ ## Quick Start
28
+
29
+ ```bash
30
+ # 1. Set your ToothFairyAI credentials
31
+ export TF_WORKSPACE_ID="your-workspace-id"
32
+ export TF_API_KEY="your-api-key"
33
+ export TF_REGION="au"
34
+
35
+ # 2. Validate your credentials
36
+ tfcode validate
37
+
38
+ # 3. Sync tools from your workspace
39
+ tfcode sync
40
+
41
+ # 4. Start coding!
42
+ tfcode
43
+ ```
44
+
45
+ ## Commands
46
+
47
+ | Command | Description |
48
+ |---------|-------------|
49
+ | `tfcode` | Start the AI coding assistant |
50
+ | `tfcode quickstart` | Show quick start guide |
51
+ | `tfcode validate` | Test your credentials |
52
+ | `tfcode sync` | Sync tools from workspace |
53
+ | `tfcode tools list` | List synced tools |
54
+ | `tfcode tools list --type mcp` | List MCP servers |
55
+ | `tfcode tools credentials <name> --set` | Set API key for a tool |
56
+
57
+ ## Regions
58
+
59
+ | Region | URL | Use Case |
60
+ |--------|-----|----------|
61
+ | `dev` | api.toothfairylab.link | Development/Testing |
62
+ | `au` | api.toothfairyai.com | Australia (Default) |
63
+ | `eu` | api.eu.toothfairyai.com | Europe |
64
+ | `us` | api.us.toothfairyai.com | United States |
65
+
66
+ ## Configuration
67
+
68
+ Credentials can be set via environment variables:
69
+
70
+ ```bash
71
+ export TF_WORKSPACE_ID="your-workspace-id"
72
+ export TF_API_KEY="your-api-key"
73
+ export TF_REGION="au"
74
+ ```
75
+
76
+ Or in `~/.tfcode/tfcode.json`:
77
+
78
+ ```json
79
+ {
80
+ "toothfairy": {
81
+ "workspace_id": "your-workspace-id",
82
+ "api_key": "your-api-key",
83
+ "region": "au"
84
+ }
85
+ }
86
+ ```
87
+
88
+ ## Getting Your Credentials
89
+
90
+ 1. Log in to [ToothFairyAI](https://app.toothfairyai.com)
91
+ 2. Go to **Settings** → **API Keys**
92
+ 3. Copy your **Workspace ID** and **API Key**
93
+
94
+ ## Troubleshooting
95
+
96
+ ### "Python 3.10+ is required but not found"
97
+
98
+ Install Python:
99
+ - **macOS**: `brew install python@3.12`
100
+ - **Windows**: Download from https://python.org/downloads
101
+ - **Linux**: `sudo apt install python3.12`
102
+
103
+ ### "Failed to validate: Invalid API key"
104
+
105
+ - Check your API key is correct
106
+ - Generate a new key in ToothFairyAI Settings → API Keys
107
+
108
+ ### "Failed to validate: Connection test failed"
109
+
110
+ Try a different region:
111
+ ```bash
112
+ export TF_REGION="eu" # or us, au
113
+ ```
114
+
115
+ ## Documentation
116
+
117
+ - **Full Guide**: https://toothfairyai.com/developers/tfcode
118
+ - **API Docs**: https://apidocs.toothfairyai.com
119
+ - **Issues**: https://github.com/ToothFairyAI/tfcode/issues
120
+
121
+ ## Credits
122
+
123
+ tfcode is based on [opencode](https://github.com/anomalyco/opencode) by [anomalyco](https://github.com/anomalyco).
124
+
125
+ ## License
126
+
127
+ MIT
package/bin/tfcode ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { spawn } = require("child_process")
4
+ const path = require("path")
5
+
6
+ const scriptDir = __dirname
7
+ const srcPath = path.join(scriptDir, "..", "src", "index.ts")
8
+
9
+ const child = spawn("npx", ["tsx", srcPath, ...process.argv.slice(2)], {
10
+ stdio: "inherit",
11
+ shell: process.platform === "win32",
12
+ cwd: scriptDir
13
+ })
14
+
15
+ child.on("exit", (code) => {
16
+ process.exit(code || 0)
17
+ })
package/bin/tfcode.js ADDED
@@ -0,0 +1,333 @@
1
+ #!/usr/bin/env node
2
+
3
+ import yargs from 'yargs';
4
+ import { hideBin } from 'yargs/helpers';
5
+ import { spawn } from 'child_process';
6
+ import { existsSync, mkdirSync, writeFileSync, readFileSync } from 'fs';
7
+ import { join } from 'path';
8
+ import { homedir } from 'os';
9
+
10
+ const TFCODE_DIR = join(homedir(), '.tfcode');
11
+ const TOOLS_FILE = join(TFCODE_DIR, 'tools.json');
12
+ const CREDENTIALS_FILE = join(TFCODE_DIR, 'credentials.json');
13
+
14
+ const COLORS = {
15
+ reset: '\x1b[0m',
16
+ bold: '\x1b[1m',
17
+ green: '\x1b[32m',
18
+ red: '\x1b[31m',
19
+ cyan: '\x1b[36m',
20
+ dim: '\x1b[90m',
21
+ yellow: '\x1b[33m'
22
+ };
23
+
24
+ function log(msg) {
25
+ console.log(msg);
26
+ }
27
+
28
+ function success(msg) {
29
+ console.log(`${COLORS.green}✓${COLORS.reset} ${msg}`);
30
+ }
31
+
32
+ function error(msg) {
33
+ console.error(`${COLORS.red}✗${COLORS.reset} ${msg}`);
34
+ }
35
+
36
+ function info(msg) {
37
+ console.log(`${COLORS.cyan}ℹ${COLORS.reset} ${msg}`);
38
+ }
39
+
40
+ function runPythonSync(method) {
41
+ const pythonCode = `
42
+ import json
43
+ import sys
44
+ import os
45
+
46
+ try:
47
+ from tf_sync.config import load_config, validate_credentials, Region
48
+ from tf_sync.tools import sync_tools
49
+ from tf_sync.config import get_region_urls
50
+
51
+ method = "${method}"
52
+
53
+ if method == "validate":
54
+ config = load_config()
55
+ result = validate_credentials(config)
56
+ urls = get_region_urls(config.region)
57
+ print(json.dumps({
58
+ "success": result.success,
59
+ "workspace_id": result.workspace_id,
60
+ "workspace_name": result.workspace_name,
61
+ "error": result.error,
62
+ "base_url": urls["base_url"]
63
+ }))
64
+
65
+ elif method == "sync":
66
+ config = load_config()
67
+ result = sync_tools(config)
68
+
69
+ tools_data = []
70
+ for tool in result.tools:
71
+ tools_data.append({
72
+ "id": tool.id,
73
+ "name": tool.name,
74
+ "description": tool.description,
75
+ "tool_type": tool.tool_type.value,
76
+ "request_type": tool.request_type.value if tool.request_type else None,
77
+ "url": tool.url,
78
+ "auth_via": tool.auth_via
79
+ })
80
+
81
+ print(json.dumps({
82
+ "success": result.success,
83
+ "tools": tools_data,
84
+ "by_type": result.by_type,
85
+ "error": result.error
86
+ }))
87
+
88
+ except Exception as e:
89
+ print(json.dumps({"success": False, "error": str(e)}))
90
+ sys.exit(0)
91
+ `;
92
+
93
+ return new Promise((resolve, reject) => {
94
+ const pythonPath = process.env.TFCODE_PYTHON_PATH || 'python3';
95
+ const proc = spawn(pythonPath, ['-c', pythonCode], {
96
+ env: {
97
+ ...process.env,
98
+ PYTHONPATH: process.env.TFCODE_PYTHONPATH || ''
99
+ }
100
+ });
101
+
102
+ let stdout = '';
103
+ let stderr = '';
104
+
105
+ proc.stdout.on('data', (data) => {
106
+ stdout += data.toString();
107
+ });
108
+
109
+ proc.stderr.on('data', (data) => {
110
+ stderr += data.toString();
111
+ });
112
+
113
+ proc.on('close', (code) => {
114
+ if (code !== 0 && !stdout) {
115
+ reject(new Error(`Python sync failed: ${stderr}`));
116
+ return;
117
+ }
118
+
119
+ try {
120
+ const result = JSON.parse(stdout.trim());
121
+ resolve(result);
122
+ } catch (e) {
123
+ reject(new Error(`Failed to parse Python output: ${stdout}\nstderr: ${stderr}`));
124
+ }
125
+ });
126
+
127
+ proc.on('error', (err) => {
128
+ reject(err);
129
+ });
130
+ });
131
+ }
132
+
133
+ function ensureConfigDir() {
134
+ if (!existsSync(TFCODE_DIR)) {
135
+ mkdirSync(TFCODE_DIR, { recursive: true });
136
+ }
137
+ }
138
+
139
+ function loadCachedTools() {
140
+ if (!existsSync(TOOLS_FILE)) {
141
+ return null;
142
+ }
143
+ try {
144
+ return JSON.parse(readFileSync(TOOLS_FILE, 'utf-8'));
145
+ } catch {
146
+ return null;
147
+ }
148
+ }
149
+
150
+ function saveToolsCache(tools) {
151
+ ensureConfigDir();
152
+ writeFileSync(TOOLS_FILE, JSON.stringify(tools, null, 2));
153
+ }
154
+
155
+ const cli = yargs(hideBin(process.argv))
156
+ .scriptName('tfcode')
157
+ .wrap(100)
158
+ .help()
159
+ .alias('help', 'h')
160
+ .command({
161
+ command: 'quickstart',
162
+ describe: 'show quick start guide',
163
+ handler: () => {
164
+ log('');
165
+ log(`${COLORS.bold}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${COLORS.reset}`);
166
+ log(`${COLORS.bold} tfcode - Quick Start Guide${COLORS.reset}`);
167
+ log(`${COLORS.bold}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${COLORS.reset}`);
168
+ log('');
169
+ log('Welcome to tfcode! Follow these steps to get started:');
170
+ log('');
171
+ log(`${COLORS.cyan}STEP 1: Set Your Credentials${COLORS.reset}`);
172
+ log(`${COLORS.dim} export TF_WORKSPACE_ID="your-workspace-id"${COLORS.reset}`);
173
+ log(`${COLORS.dim} export TF_API_KEY="your-api-key"${COLORS.reset}`);
174
+ log(`${COLORS.dim} export TF_REGION="au"${COLORS.reset}`);
175
+ log(`${COLORS.dim} Regions: dev, au, eu, us${COLORS.reset}`);
176
+ log('');
177
+ log(`${COLORS.cyan}STEP 2: Validate Connection${COLORS.reset}`);
178
+ log(`${COLORS.dim} tfcode validate${COLORS.reset}`);
179
+ log('');
180
+ log(`${COLORS.cyan}STEP 3: Sync Your Tools${COLORS.reset}`);
181
+ log(`${COLORS.dim} tfcode sync${COLORS.reset}`);
182
+ log('');
183
+ log(`${COLORS.cyan}STEP 4: Start Coding!${COLORS.reset}`);
184
+ log(`${COLORS.dim} tfcode${COLORS.reset}`);
185
+ log('');
186
+ log(`${COLORS.bold}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${COLORS.reset}`);
187
+ log('');
188
+ log(' Useful Commands:');
189
+ log('');
190
+ log(' tfcode validate Test your credentials');
191
+ log(' tfcode sync Sync tools from workspace');
192
+ log(' tfcode tools list Show all your tools');
193
+ log('');
194
+ log(`${COLORS.bold}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${COLORS.reset}`);
195
+ log('');
196
+ log(' Need help? https://toothfairyai.com/developers/tfcode');
197
+ log('');
198
+ }
199
+ })
200
+ .command({
201
+ command: 'validate',
202
+ describe: 'validate ToothFairyAI credentials',
203
+ handler: async () => {
204
+ info('Validating ToothFairyAI credentials...');
205
+ log('');
206
+
207
+ try {
208
+ const result = await runPythonSync('validate');
209
+
210
+ if (result.success) {
211
+ success('Credentials valid');
212
+ if (result.base_url) {
213
+ log(`${COLORS.dim} API URL: ${result.base_url}${COLORS.reset}`);
214
+ }
215
+ if (result.workspace_id) {
216
+ log(`${COLORS.dim} Workspace ID: ${result.workspace_id}${COLORS.reset}`);
217
+ }
218
+ } else {
219
+ error(`Validation failed: ${result.error || 'Unknown error'}`);
220
+ log('');
221
+ log(`${COLORS.dim}Check your credentials:${COLORS.reset}`);
222
+ log(`${COLORS.dim} TF_WORKSPACE_ID: ${process.env.TF_WORKSPACE_ID || 'not set'}${COLORS.reset}`);
223
+ log(`${COLORS.dim} TF_API_KEY: ${process.env.TF_API_KEY ? '***' + process.env.TF_API_KEY.slice(-4) : 'not set'}${COLORS.reset}`);
224
+ log(`${COLORS.dim} TF_REGION: ${process.env.TF_REGION || 'au (default)'}${COLORS.reset}`);
225
+ process.exit(1);
226
+ }
227
+ } catch (e) {
228
+ error(`Failed to validate: ${e.message}`);
229
+ log('');
230
+ log(`${COLORS.dim}Make sure Python 3.10+ and the ToothFairyAI SDK are installed:${COLORS.reset}`);
231
+ log(`${COLORS.dim} pip install toothfairyai pydantic httpx rich${COLORS.reset}`);
232
+ process.exit(1);
233
+ }
234
+ }
235
+ })
236
+ .command({
237
+ command: 'sync',
238
+ describe: 'sync tools from ToothFairyAI workspace',
239
+ handler: async () => {
240
+ info('Syncing tools from ToothFairyAI workspace...');
241
+ log('');
242
+
243
+ try {
244
+ const result = await runPythonSync('sync');
245
+
246
+ if (result.success) {
247
+ saveToolsCache(result);
248
+ success(`Synced ${result.tools.length} tools`);
249
+ log('');
250
+
251
+ if (result.by_type && Object.keys(result.by_type).length > 0) {
252
+ log('By type:');
253
+ for (const [type, count] of Object.entries(result.by_type)) {
254
+ log(` ${type}: ${count}`);
255
+ }
256
+ log('');
257
+ }
258
+ } else {
259
+ error(`Sync failed: ${result.error || 'Unknown error'}`);
260
+ process.exit(1);
261
+ }
262
+ } catch (e) {
263
+ error(`Failed to sync: ${e.message}`);
264
+ process.exit(1);
265
+ }
266
+ }
267
+ })
268
+ .command({
269
+ command: 'tools',
270
+ describe: 'manage tools',
271
+ builder: (yargs) => {
272
+ return yargs
273
+ .command({
274
+ command: 'list',
275
+ describe: 'list synced tools',
276
+ builder: (yargs) => {
277
+ return yargs.option('type', {
278
+ type: 'string',
279
+ describe: 'filter by type (api_function)'
280
+ });
281
+ },
282
+ handler: (args) => {
283
+ const cached = loadCachedTools();
284
+
285
+ if (!cached || !cached.success) {
286
+ error('No tools synced. Run \'tfcode sync\' first.');
287
+ process.exit(1);
288
+ return;
289
+ }
290
+
291
+ let tools = cached.tools;
292
+
293
+ if (args.type) {
294
+ tools = tools.filter(t => t.tool_type === args.type);
295
+ }
296
+
297
+ if (tools.length === 0) {
298
+ log('No tools found.');
299
+ return;
300
+ }
301
+
302
+ log('');
303
+ log(`${tools.length} tool(s):`);
304
+ log('');
305
+
306
+ for (const tool of tools) {
307
+ log(` ${COLORS.cyan}${tool.name}${COLORS.reset}`);
308
+ log(` Type: ${tool.tool_type}`);
309
+ if (tool.description) {
310
+ log(` ${COLORS.dim}${tool.description.slice(0, 60)}${tool.description.length > 60 ? '...' : ''}${COLORS.reset}`);
311
+ }
312
+ log(` Auth: ${tool.auth_via}`);
313
+ log('');
314
+ }
315
+ }
316
+ })
317
+ .demandCommand();
318
+ },
319
+ handler: () => {}
320
+ })
321
+ .demandCommand()
322
+ .strict()
323
+ .fail((msg, err) => {
324
+ if (msg) {
325
+ error(msg);
326
+ }
327
+ if (err) {
328
+ error(err.message);
329
+ }
330
+ process.exit(1);
331
+ });
332
+
333
+ cli.parse();
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@toothfairyai/tfcode",
3
+ "version": "1.0.0-beta.1",
4
+ "description": "ToothFairyAI's official AI coding agent",
5
+ "keywords": ["toothfairyai", "ai", "coding", "cli"],
6
+ "author": "ToothFairyAI",
7
+ "license": "MIT",
8
+ "type": "module",
9
+ "bin": {
10
+ "tfcode": "./bin/tfcode.js"
11
+ },
12
+ "files": [
13
+ "bin/",
14
+ "scripts/"
15
+ ],
16
+ "scripts": {
17
+ "postinstall": "node scripts/postinstall.cjs"
18
+ },
19
+ "dependencies": {
20
+ "yargs": "^17.7.2"
21
+ },
22
+ "engines": {
23
+ "node": ">=18"
24
+ },
25
+ "homepage": "https://toothfairyai.com/developers/tfcode",
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "https://github.com/ToothFairyAI/tfcode.git"
29
+ }
30
+ }
@@ -0,0 +1,171 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { spawn, execSync } = require('child_process');
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+
7
+ const RESET = '\x1b[0m';
8
+ const BOLD = '\x1b[1m';
9
+ const GREEN = '\x1b[32m';
10
+ const YELLOW = '\x1b[33m';
11
+ const RED = '\x1b[31m';
12
+ const CYAN = '\x1b[36m';
13
+ const DIM = '\x1b[90m';
14
+
15
+ function log(msg) {
16
+ console.log(msg);
17
+ }
18
+
19
+ function logSuccess(msg) {
20
+ console.log(`${GREEN}✓${RESET} ${msg}`);
21
+ }
22
+
23
+ function logError(msg) {
24
+ console.error(`${RED}✗${RESET} ${msg}`);
25
+ }
26
+
27
+ function logInfo(msg) {
28
+ console.log(`${CYAN}ℹ${RESET} ${msg}`);
29
+ }
30
+
31
+ function logWarning(msg) {
32
+ console.log(`${YELLOW}!${RESET} ${msg}`);
33
+ }
34
+
35
+ function checkPython() {
36
+ const commands = ['python3', 'python'];
37
+
38
+ for (const cmd of commands) {
39
+ try {
40
+ const result = execSync(`${cmd} --version`, { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] });
41
+ const match = result.match(/Python (\d+)\.(\d+)/);
42
+ if (match) {
43
+ const major = parseInt(match[1]);
44
+ const minor = parseInt(match[2]);
45
+ if (major >= 3 && minor >= 10) {
46
+ return { cmd, version: result.trim() };
47
+ }
48
+ }
49
+ } catch {}
50
+ }
51
+
52
+ return null;
53
+ }
54
+
55
+ function installPythonDeps(pythonCmd) {
56
+ return new Promise((resolve, reject) => {
57
+ const packages = ['toothfairyai', 'pydantic', 'httpx', 'rich'];
58
+
59
+ log(`${DIM}Installing Python packages: ${packages.join(', ')}...${RESET}`);
60
+
61
+ // Try with --user first, then --break-system-packages if needed
62
+ const args = ['-m', 'pip', 'install', '--user', ...packages];
63
+
64
+ const proc = spawn(pythonCmd, args, {
65
+ stdio: 'inherit',
66
+ shell: process.platform === 'win32'
67
+ });
68
+
69
+ proc.on('close', (code) => {
70
+ if (code === 0) {
71
+ resolve();
72
+ } else {
73
+ // Try with --break-system-packages flag
74
+ log(`${DIM}Retrying with --break-system-packages...${RESET}`);
75
+ const retryArgs = ['-m', 'pip', 'install', '--user', '--break-system-packages', ...packages];
76
+ const retry = spawn(pythonCmd, retryArgs, {
77
+ stdio: 'inherit',
78
+ shell: process.platform === 'win32'
79
+ });
80
+
81
+ retry.on('close', (retryCode) => {
82
+ if (retryCode === 0) {
83
+ resolve();
84
+ } else {
85
+ reject(new Error(`pip install exited with code ${retryCode}`));
86
+ }
87
+ });
88
+
89
+ retry.on('error', (err) => {
90
+ reject(err);
91
+ });
92
+ }
93
+ });
94
+
95
+ proc.on('error', (err) => {
96
+ reject(err);
97
+ });
98
+ });
99
+ }
100
+
101
+ async function main() {
102
+ log('');
103
+ log(`${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}`);
104
+ log(`${BOLD} tfcode - ToothFairyAI's official coding agent${RESET}`);
105
+ log(`${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}`);
106
+ log('');
107
+
108
+ // Check for Python
109
+ logInfo('Checking Python installation...');
110
+ const python = checkPython();
111
+
112
+ if (!python) {
113
+ log('');
114
+ logError('Python 3.10+ is required but not found.');
115
+ log('');
116
+ log(`${BOLD}Please install Python 3.10 or later:${RESET}`);
117
+ log('');
118
+ log(` ${CYAN}macOS:${RESET} brew install python@3.12`);
119
+ log(` ${CYAN}Ubuntu:${RESET} sudo apt-get install python3.12`);
120
+ log(` ${CYAN}Windows:${RESET} Download from https://python.org/downloads`);
121
+ log('');
122
+ log(`${DIM}After installing Python, run: npm rebuild tfcode${RESET}`);
123
+ log('');
124
+ process.exit(1);
125
+ }
126
+
127
+ logSuccess(`Found ${python.version} (${python.cmd})`);
128
+ log('');
129
+
130
+ // Install Python dependencies
131
+ logInfo('Installing ToothFairyAI Python SDK...');
132
+ try {
133
+ await installPythonDeps(python.cmd);
134
+ logSuccess('Python dependencies installed');
135
+ } catch (err) {
136
+ logWarning(`Failed to install Python dependencies: ${err.message}`);
137
+ log('');
138
+ log(`${DIM}You can install them manually with:${RESET}`);
139
+ log(` ${CYAN}${python.cmd} -m pip install toothfairyai pydantic httpx rich${RESET}`);
140
+ log('');
141
+ // Don't exit - user might install manually later
142
+ }
143
+
144
+ log('');
145
+ log(`${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}`);
146
+ log(`${GREEN}✓ tfcode installed successfully!${RESET}`);
147
+ log(`${BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}`);
148
+ log('');
149
+ log(`${BOLD}Quick Start:${RESET}`);
150
+ log('');
151
+ log(` ${CYAN}1.${RESET} Set your ToothFairyAI credentials:`);
152
+ log(` ${DIM}export TF_WORKSPACE_ID="your-workspace-id"${RESET}`);
153
+ log(` ${DIM}export TF_API_KEY="your-api-key"${RESET}`);
154
+ log('');
155
+ log(` ${CYAN}2.${RESET} Validate your credentials:`);
156
+ log(` ${DIM}tfcode validate${RESET}`);
157
+ log('');
158
+ log(` ${CYAN}3.${RESET} Sync tools from your workspace:`);
159
+ log(` ${DIM}tfcode sync${RESET}`);
160
+ log('');
161
+ log(` ${CYAN}4.${RESET} Start coding:`);
162
+ log(` ${DIM}tfcode${RESET}`);
163
+ log('');
164
+ log(`${DIM}Documentation: https://toothfairyai.com/developers/tfcode${RESET}`);
165
+ log('');
166
+ }
167
+
168
+ main().catch((err) => {
169
+ logError(`Installation failed: ${err.message}`);
170
+ process.exit(1);
171
+ });
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env node
2
+
3
+ console.log(`
4
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
5
+ tfcode - Quick Start Guide
6
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
7
+
8
+ Welcome to tfcode! Follow these steps to get started:
9
+
10
+ ┌─────────────────────────────────────────────────────────────┐
11
+ │ STEP 1: Set Your Credentials │
12
+ ├─────────────────────────────────────────────────────────────┤
13
+ │ │
14
+ │ export TF_WORKSPACE_ID="your-workspace-id" │
15
+ │ export TF_API_KEY="your-api-key" │
16
+ │ export TF_REGION="au" │
17
+ │ │
18
+ │ Regions: au (Australia), eu (Europe), us (United States) │
19
+ │ │
20
+ └─────────────────────────────────────────────────────────────┘
21
+
22
+ ┌─────────────────────────────────────────────────────────────┐
23
+ │ STEP 2: Validate Connection │
24
+ ├─────────────────────────────────────────────────────────────┤
25
+ │ │
26
+ │ tfcode validate │
27
+ │ │
28
+ │ This checks your credentials are correct. │
29
+ │ │
30
+ └─────────────────────────────────────────────────────────────┘
31
+
32
+ ┌─────────────────────────────────────────────────────────────┐
33
+ │ STEP 3: Sync Your Tools │
34
+ ├─────────────────────────────────────────────────────────────┤
35
+ │ │
36
+ │ tfcode sync │
37
+ │ │
38
+ │ This downloads your tools from ToothFairyAI. │
39
+ │ │
40
+ └─────────────────────────────────────────────────────────────┘
41
+
42
+ ┌─────────────────────────────────────────────────────────────┐
43
+ │ STEP 4: Start Coding! │
44
+ ├─────────────────────────────────────────────────────────────┤
45
+ │ │
46
+ │ tfcode │
47
+ │ │
48
+ │ Start the AI coding assistant. │
49
+ │ │
50
+ └─────────────────────────────────────────────────────────────┘
51
+
52
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
53
+
54
+ Useful Commands:
55
+
56
+ tfcode validate Test your credentials
57
+ tfcode sync Sync tools from workspace
58
+ tfcode tools list Show all your tools
59
+ tfcode Start coding assistant
60
+
61
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
62
+
63
+ Need help? https://toothfairyai.com/developers/tfcode
64
+
65
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
66
+ `);