@toothfairyai/tfcode 2.5.8 → 2.5.10

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/package.json CHANGED
@@ -1,169 +1,33 @@
1
1
  {
2
- "$schema": "https://json.schemastore.org/package.json",
3
- "version": "2.5.8",
4
2
  "name": "@toothfairyai/tfcode",
5
- "type": "module",
6
- "license": "MIT",
7
- "files": [
8
- "bin",
9
- "app",
10
- "postinstall.mjs",
11
- "LICENSE"
12
- ],
13
- "scripts": {
14
- "prepare": "effect-language-service patch || true",
15
- "postinstall": "node scripts/postinstall.cjs",
16
- "typecheck": "tsgo --noEmit",
17
- "test": "bun test --timeout 30000",
18
- "build": "bun run script/build-tfcode.ts",
19
- "build:single": "bun run script/build-tfcode.ts --single",
20
- "publish:upload": "bun run script/publish-tfcode.ts --upload",
21
- "publish:npm": "bun run script/publish-tfcode.ts --npm",
22
- "publish:brew": "bun run script/publish-tfcode.ts --brew",
23
- "publish:all": "bun run script/publish-tfcode.ts --all",
24
- "dev": "bun run --conditions=browser ./src/index.ts",
25
- "db": "bun drizzle-kit"
26
- },
3
+ "version": "2.5.10",
27
4
  "bin": {
28
- "tfcode": "./bin/tfcode"
5
+ "tfcode": "./bin/tfcode.js"
29
6
  },
30
- "exports": {
31
- "./*": "./src/*.ts"
32
- },
33
- "imports": {
34
- "#db": {
35
- "bun": "./src/storage/db.bun.ts",
36
- "node": "./src/storage/db.node.ts",
37
- "default": "./src/storage/db.bun.ts"
38
- }
7
+ "scripts": {
8
+ "postinstall": "node ./postinstall.mjs"
39
9
  },
40
- "devDependencies": {
41
- "@babel/core": "7.28.4",
42
- "@effect/language-service": "0.79.0",
43
- "@octokit/webhooks-types": "7.6.1",
44
- "@opencode-ai/script": "workspace:*",
45
- "@parcel/watcher-darwin-arm64": "2.5.1",
46
- "@parcel/watcher-darwin-x64": "2.5.1",
47
- "@parcel/watcher-linux-arm64-glibc": "2.5.1",
48
- "@parcel/watcher-linux-arm64-musl": "2.5.1",
49
- "@parcel/watcher-linux-x64-glibc": "2.5.1",
50
- "@parcel/watcher-linux-x64-musl": "2.5.1",
51
- "@parcel/watcher-win32-arm64": "2.5.1",
52
- "@parcel/watcher-win32-x64": "2.5.1",
53
- "@standard-schema/spec": "1.0.0",
54
- "@tsconfig/bun": "catalog:",
55
- "@types/babel__core": "7.20.5",
56
- "@types/bun": "1.3.11",
57
- "@types/cross-spawn": "6.0.6",
58
- "@types/mime-types": "3.0.1",
59
- "@types/node": "22.13.9",
60
- "@types/semver": "^7.5.8",
61
- "@types/turndown": "5.0.5",
62
- "@types/which": "3.0.4",
63
- "@types/ws": "8.18.1",
64
- "@types/yargs": "17.0.33",
65
- "@typescript/native-preview": "catalog:",
66
- "drizzle-kit": "catalog:",
67
- "drizzle-orm": "catalog:",
68
- "typescript": "catalog:",
69
- "vscode-languageserver-types": "3.17.5",
70
- "why-is-node-running": "3.2.2",
71
- "zod-to-json-schema": "3.24.5"
10
+ "license": "MIT",
11
+ "optionalDependencies": {
12
+ "@toothfairyai/tfcode-linux-arm64": "2.5.10",
13
+ "@toothfairyai/tfcode-windows-x64": "2.5.10",
14
+ "@toothfairyai/tfcode-linux-x64-baseline-musl": "2.5.10",
15
+ "@toothfairyai/tfcode-darwin-x64-baseline": "2.5.10",
16
+ "@toothfairyai/tfcode-linux-x64-musl": "2.5.10",
17
+ "@toothfairyai/tfcode-windows-x64-baseline": "2.5.10",
18
+ "@toothfairyai/tfcode-linux-arm64-musl": "2.5.10",
19
+ "@toothfairyai/tfcode-windows-arm64": "2.5.10",
20
+ "@toothfairyai/tfcode-linux-x64": "2.5.10",
21
+ "@toothfairyai/tfcode-darwin-x64": "2.5.10",
22
+ "@toothfairyai/tfcode-linux-x64-baseline": "2.5.10",
23
+ "@toothfairyai/tfcode-darwin-arm64": "2.5.10"
72
24
  },
73
- "dependencies": {
74
- "@actions/core": "1.11.1",
75
- "@actions/github": "6.0.1",
76
- "@agentclientprotocol/sdk": "0.14.1",
77
- "@ai-sdk/amazon-bedrock": "3.0.82",
78
- "@ai-sdk/anthropic": "2.0.65",
79
- "@ai-sdk/azure": "2.0.91",
80
- "@ai-sdk/cerebras": "1.0.36",
81
- "@ai-sdk/cohere": "2.0.22",
82
- "@ai-sdk/deepinfra": "1.0.36",
83
- "@ai-sdk/gateway": "2.0.30",
84
- "@ai-sdk/google": "2.0.54",
85
- "@ai-sdk/google-vertex": "3.0.106",
86
- "@ai-sdk/groq": "2.0.34",
87
- "@ai-sdk/mistral": "2.0.27",
88
- "@ai-sdk/openai": "2.0.89",
89
- "@ai-sdk/openai-compatible": "1.0.32",
90
- "@ai-sdk/perplexity": "2.0.23",
91
- "@ai-sdk/provider": "2.0.1",
92
- "@ai-sdk/provider-utils": "3.0.21",
93
- "@ai-sdk/togetherai": "1.0.34",
94
- "@ai-sdk/vercel": "1.0.33",
95
- "@ai-sdk/xai": "2.0.51",
96
- "@aws-sdk/credential-providers": "3.993.0",
97
- "@clack/prompts": "1.0.0-alpha.1",
98
- "@effect/platform-node": "catalog:",
99
- "@hono/standard-validator": "0.1.5",
100
- "@hono/zod-validator": "catalog:",
101
- "@modelcontextprotocol/sdk": "1.29.0",
102
- "@octokit/graphql": "9.0.2",
103
- "@octokit/rest": "catalog:",
104
- "@openauthjs/openauth": "catalog:",
105
- "@opencode-ai/gateway": "workspace:*",
106
- "@opencode-ai/plugin": "workspace:*",
107
- "@opencode-ai/script": "workspace:*",
108
- "@opencode-ai/sdk": "workspace:*",
109
- "@opencode-ai/util": "workspace:*",
110
- "@openrouter/ai-sdk-provider": "1.5.4",
111
- "@opentui/core": "0.2.16",
112
- "@opentui/solid": "0.2.16",
113
- "@parcel/watcher": "2.5.1",
114
- "@pierre/diffs": "catalog:",
115
- "@solid-primitives/event-bus": "1.1.2",
116
- "@solid-primitives/scheduled": "1.5.2",
117
- "@standard-schema/spec": "1.0.0",
118
- "@zip.js/zip.js": "2.7.62",
119
- "ai": "catalog:",
120
- "ai-gateway-provider": "2.3.1",
121
- "bonjour-service": "1.3.0",
122
- "bun-pty": "0.4.8",
123
- "chokidar": "4.0.3",
124
- "clipboardy": "4.0.0",
125
- "croner": "10.0.1",
126
- "cross-spawn": "^7.0.6",
127
- "decimal.js": "10.5.0",
128
- "diff": "catalog:",
129
- "drizzle-orm": "catalog:",
130
- "effect": "catalog:",
131
- "fuzzysort": "3.1.0",
132
- "gitlab-ai-provider": "5.2.2",
133
- "glob": "13.0.5",
134
- "google-auth-library": "10.5.0",
135
- "gray-matter": "4.0.3",
136
- "hono": "catalog:",
137
- "hono-openapi": "catalog:",
138
- "ignore": "7.0.5",
139
- "jsonc-parser": "3.3.1",
140
- "marked": "catalog:",
141
- "mime-types": "3.0.2",
142
- "minimatch": "10.0.3",
143
- "open": "10.1.2",
144
- "opencode-gitlab-auth": "2.0.0",
145
- "opentui-spinner": "0.0.6",
146
- "partial-json": "0.1.7",
147
- "react": "19.2.4",
148
- "react-dom": "19.2.4",
149
- "remeda": "catalog:",
150
- "semver": "^7.6.3",
151
- "solid-js": "catalog:",
152
- "strip-ansi": "7.1.2",
153
- "tree-sitter-bash": "0.25.0",
154
- "tree-sitter-powershell": "0.25.10",
155
- "turndown": "7.2.0",
156
- "ulid": "catalog:",
157
- "vscode-jsonrpc": "8.2.1",
158
- "web-tree-sitter": "0.25.10",
159
- "which": "6.0.1",
160
- "ws": "8.21.0",
161
- "xdg-basedir": "5.1.0",
162
- "yargs": "18.0.0",
163
- "zod": "catalog:",
164
- "zod-to-json-schema": "3.24.5"
25
+ "engines": {
26
+ "node": ">=18"
165
27
  },
166
- "overrides": {
167
- "drizzle-orm": "catalog:"
28
+ "homepage": "https://toothfairyai.com/developers/tfcode",
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "https://gitea.toothfairyai.com/ToothFairyAI/tf_code.git"
168
32
  }
169
- }
33
+ }
@@ -0,0 +1,378 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from "fs"
4
+ import path from "path"
5
+ import os from "os"
6
+ import { spawnSync } from "child_process"
7
+ import { fileURLToPath } from "url"
8
+
9
+ const __filename = fileURLToPath(import.meta.url)
10
+ const __dirname = path.dirname(__filename)
11
+
12
+ const GITEA_HOST = process.env.TFCODE_GITEA_HOST || "gitea.toothfairyai.com"
13
+ const GITEA_REPO = process.env.TFCODE_GITEA_REPO || "ToothFairyAI/tf_code"
14
+
15
+ function detectPlatform() {
16
+ let platform
17
+ switch (os.platform()) {
18
+ case "darwin":
19
+ platform = "darwin"
20
+ break
21
+ case "linux":
22
+ platform = "linux"
23
+ break
24
+ case "win32":
25
+ platform = "windows"
26
+ break
27
+ default:
28
+ platform = os.platform()
29
+ }
30
+
31
+ let arch
32
+ switch (os.arch()) {
33
+ case "x64":
34
+ arch = "x64"
35
+ break
36
+ case "arm64":
37
+ arch = "arm64"
38
+ break
39
+ default:
40
+ arch = os.arch()
41
+ }
42
+
43
+ // Check for AVX2 on x64
44
+ let needsBaseline = false
45
+ if (arch === "x64" && (platform === "linux" || platform === "darwin")) {
46
+ try {
47
+ if (platform === "linux") {
48
+ const cpuinfo = fs.readFileSync("/proc/cpuinfo", "utf8")
49
+ needsBaseline = !cpuinfo.toLowerCase().includes("avx2")
50
+ } else if (platform === "darwin") {
51
+ const result = spawnSync("sysctl", ["-n", "hw.optional.avx2_0"], { encoding: "utf8" })
52
+ needsBaseline = result.stdout.trim() !== "1"
53
+ }
54
+ } catch {}
55
+ }
56
+
57
+ // Check for musl on Linux
58
+ let abi = ""
59
+ if (platform === "linux") {
60
+ try {
61
+ if (fs.existsSync("/etc/alpine-release")) {
62
+ abi = "musl"
63
+ } else {
64
+ const result = spawnSync("ldd", ["--version"], { encoding: "utf8" })
65
+ if ((result.stdout + result.stderr).toLowerCase().includes("musl")) {
66
+ abi = "musl"
67
+ }
68
+ }
69
+ } catch {}
70
+ }
71
+
72
+ return { platform, arch, needsBaseline, abi }
73
+ }
74
+
75
+ async function getVersion() {
76
+ try {
77
+ const res = await fetch(`https://${GITEA_HOST}/api/v1/repos/${GITEA_REPO}/releases/latest`)
78
+ const data = await res.json()
79
+ return data.tag_name?.replace(/^v/, "") || "1.0.0"
80
+ } catch {
81
+ return "1.0.0"
82
+ }
83
+ }
84
+
85
+ // Detect the machine architecture of an existing binary. Returns "arm64",
86
+ // "x64", or null when it can't be determined (null => keep existing binary).
87
+ function detectBinaryMachineArch(binaryPath, platform) {
88
+ try {
89
+ if (platform === "win32") {
90
+ const buf = fs.readFileSync(binaryPath)
91
+ if (buf.length < 0x40) return null
92
+ const pe = buf.readUInt32LE(0x3c)
93
+ if (pe + 6 > buf.length) return null
94
+ const machine = buf.readUInt16LE(pe + 4)
95
+ if (machine === 0x8664) return "x64"
96
+ if (machine === 0xaa64) return "arm64"
97
+ return null
98
+ }
99
+ const out = spawnSync("file", [binaryPath], { encoding: "utf8" }).stdout || ""
100
+ if (/arm64|aarch64/i.test(out)) return "arm64"
101
+ if (/x86-64|x86_64/i.test(out)) return "x64"
102
+ return null
103
+ } catch {
104
+ return null
105
+ }
106
+ }
107
+
108
+ // A missing/unparseable binary is treated as a match so installs never break
109
+ // when detection isn't possible; a real mismatch forces a re-download.
110
+ function binaryArchMatches(binaryPath, expectedArch, platform) {
111
+ const actual = detectBinaryMachineArch(binaryPath, platform)
112
+ if (!actual) return true
113
+ return actual === expectedArch
114
+ }
115
+
116
+ async function downloadBinary() {
117
+ const { platform, arch, needsBaseline, abi } = detectPlatform()
118
+ const version = await getVersion()
119
+
120
+ // Check if binary already exists (included in npm package)
121
+ const binaryName = platform === "windows" ? "tfcode.exe" : "tfcode"
122
+ const binDir = path.join(__dirname, "bin")
123
+ const existingBinary = path.join(binDir, binaryName)
124
+
125
+ if (fs.existsSync(existingBinary) && binaryArchMatches(existingBinary, arch, platform)) {
126
+ console.log(`✓ Binary already exists at ${existingBinary}`)
127
+ // Still need to copy app dir from platform package if missing
128
+ const targetAppDir = path.join(binDir, "app")
129
+ if (!fs.existsSync(path.join(targetAppDir, "dist", "index.html"))) {
130
+ let target = `tfcode-${platform}-${arch}`
131
+ if (needsBaseline) target += "-baseline"
132
+ if (abi) target += `-${abi}`
133
+ const pkgName = `@toothfairyai/${target}`
134
+ try {
135
+ const pkgUrl = import.meta.resolve(`${pkgName}/package.json`)
136
+ const pkgDir = path.dirname(fileURLToPath(pkgUrl))
137
+ const srcAppDir = path.join(pkgDir, "bin", "app")
138
+ if (fs.existsSync(path.join(srcAppDir, "dist", "index.html"))) {
139
+ fs.cpSync(srcAppDir, targetAppDir, { recursive: true })
140
+ console.log("✓ Web app copied from platform package")
141
+ }
142
+ copyComputerAssets(path.join(pkgDir, "bin"), binDir)
143
+ } catch (e) {
144
+ console.log(`! Could not copy web app: ${e.message}`)
145
+ }
146
+ }
147
+ return
148
+ }
149
+
150
+ // Build filename for download
151
+ let target = `tfcode-${platform}-${arch}`
152
+ if (needsBaseline) target += "-baseline"
153
+ if (abi) target += `-${abi}`
154
+
155
+ const ext = platform === "linux" ? ".tar.gz" : ".zip"
156
+ const filename = `${target}${ext}`
157
+ const url = `https://${GITEA_HOST}/${GITEA_REPO}/releases/download/v${version}/${filename}`
158
+
159
+ console.log(`Downloading tfcode v${version} for ${target}...`)
160
+
161
+ // Download
162
+ if (!fs.existsSync(binDir)) fs.mkdirSync(binDir, { recursive: true })
163
+
164
+ const tmpDir = path.join(os.tmpdir(), `tfcode-install-${process.pid}`)
165
+ fs.mkdirSync(tmpDir, { recursive: true })
166
+ const archivePath = path.join(tmpDir, filename)
167
+
168
+ // Use curl to download
169
+ const curlResult = spawnSync("curl", ["-fsSL", "-o", archivePath, url], { stdio: "inherit" })
170
+ if (curlResult.status !== 0) {
171
+ console.error(`Failed to download from ${url}`)
172
+ console.error("Trying npm package fallback...")
173
+
174
+ // Fallback to npm optionalDependencies
175
+ try {
176
+ const pkgName = `@toothfairyai/${target}`
177
+
178
+ // ESM-compatible module resolution
179
+ let pkgDir
180
+ try {
181
+ const pkgUrl = import.meta.resolve(`${pkgName}/package.json`)
182
+ const pkgPath = fileURLToPath(pkgUrl)
183
+ pkgDir = path.dirname(pkgPath)
184
+ } catch (e) {
185
+ console.error(`Could not resolve ${pkgName}:`, e.message)
186
+ throw e
187
+ }
188
+
189
+ const binaryName = platform === "windows" ? "tfcode.exe" : "tfcode"
190
+ const binaryPath = path.join(pkgDir, "bin", binaryName)
191
+
192
+ console.log(`Looking for binary at: ${binaryPath}`)
193
+
194
+ if (fs.existsSync(binaryPath)) {
195
+ console.log(`Found binary at npm package location`)
196
+ setupBinary(binaryPath, platform)
197
+ return
198
+ } else {
199
+ console.error(`Binary not found at ${binaryPath}`)
200
+ }
201
+ } catch (e) {
202
+ console.error("npm package fallback failed:", e.message)
203
+ }
204
+
205
+ console.error("")
206
+ console.error("Installation failed. The binary could not be downloaded or found.")
207
+ console.error("")
208
+ console.error("Possible solutions:")
209
+ console.error(" 1. If this is a private installation, set TFCODE_GITEA_HOST to an accessible host")
210
+ console.error(" 2. Manually download the binary and place it in the bin/ directory")
211
+ console.error(" 3. Contact ToothFairyAI support for assistance")
212
+ console.error("")
213
+ process.exit(1)
214
+ }
215
+
216
+ // Extract
217
+ console.log("Extracting...")
218
+ if (platform === "linux") {
219
+ spawnSync("tar", ["-xzf", archivePath, "-C", tmpDir], { stdio: "inherit" })
220
+ } else {
221
+ spawnSync("unzip", ["-q", archivePath, "-d", tmpDir], { stdio: "inherit" })
222
+ }
223
+
224
+ // Move binary
225
+ const extractedBinary = path.join(tmpDir, "tfcode")
226
+ const targetBinary = path.join(binDir, binaryName)
227
+
228
+ if (fs.existsSync(extractedBinary)) {
229
+ if (fs.existsSync(targetBinary)) fs.unlinkSync(targetBinary)
230
+ fs.copyFileSync(extractedBinary, targetBinary)
231
+ fs.chmodSync(targetBinary, 0o755)
232
+ console.log(`Installed tfcode to ${targetBinary}`)
233
+ }
234
+
235
+ // Move app directory
236
+ const extractedAppDir = path.join(tmpDir, "app")
237
+ const targetAppDir = path.join(binDir, "app")
238
+ if (fs.existsSync(path.join(extractedAppDir, "dist", "index.html"))) {
239
+ if (fs.existsSync(targetAppDir)) fs.rmSync(targetAppDir, { recursive: true, force: true })
240
+ fs.cpSync(extractedAppDir, targetAppDir, { recursive: true })
241
+ console.log("Installed web app")
242
+ }
243
+
244
+ // Move bundled computer-use helpers (cliclick on macOS, if archived)
245
+ copyComputerAssets(tmpDir, binDir)
246
+
247
+ // Cleanup
248
+ fs.rmSync(tmpDir, { recursive: true, force: true })
249
+ }
250
+
251
+ // Copy bundled computer-use helper binaries (e.g. cliclick on macOS) from a
252
+ // source bin dir into the installed bin dir, so `tfcode computer setup` finds
253
+ // them next to the binary without Homebrew/apt. No-op when absent. npm may
254
+ // strip the exec bit, so chmod the helpers back to 0755.
255
+ function copyComputerAssets(srcBinDir, dstBinDir) {
256
+ const srcComputer = path.join(srcBinDir, "computer")
257
+ if (!fs.existsSync(srcComputer)) return
258
+ const dstComputer = path.join(dstBinDir, "computer")
259
+ if (fs.existsSync(dstComputer)) fs.rmSync(dstComputer, { recursive: true, force: true })
260
+ fs.cpSync(srcComputer, dstComputer, { recursive: true })
261
+ for (const name of fs.readdirSync(dstComputer)) {
262
+ try {
263
+ fs.chmodSync(path.join(dstComputer, name), 0o755)
264
+ } catch {}
265
+ }
266
+ console.log("✓ Computer-use helpers copied from platform package")
267
+ }
268
+
269
+ function setupBinary(sourcePath, platform) {
270
+ const binDir = path.join(__dirname, "bin")
271
+ const binaryName = platform === "windows" ? "tfcode.exe" : "tfcode"
272
+ const targetBinary = path.join(binDir, binaryName)
273
+
274
+ if (!fs.existsSync(binDir)) fs.mkdirSync(binDir, { recursive: true })
275
+ if (fs.existsSync(targetBinary)) fs.unlinkSync(targetBinary)
276
+
277
+ // Try hardlink, fall back to copy
278
+ try {
279
+ fs.linkSync(sourcePath, targetBinary)
280
+ } catch {
281
+ fs.copyFileSync(sourcePath, targetBinary)
282
+ }
283
+
284
+ fs.chmodSync(targetBinary, 0o755)
285
+ console.log(`tfcode installed to ${targetBinary}`)
286
+
287
+ // Also copy app dir from platform package
288
+ const sourceDir = path.dirname(sourcePath)
289
+ const srcAppDir = path.join(sourceDir, "app")
290
+ const targetAppDir = path.join(binDir, "app")
291
+ if (fs.existsSync(path.join(srcAppDir, "dist", "index.html"))) {
292
+ if (fs.existsSync(targetAppDir)) fs.rmSync(targetAppDir, { recursive: true, force: true })
293
+ fs.cpSync(srcAppDir, targetAppDir, { recursive: true })
294
+ console.log("Web app copied from platform package")
295
+ }
296
+ // Also copy bundled computer-use helpers (cliclick on macOS) if present.
297
+ copyComputerAssets(sourceDir, binDir)
298
+ }
299
+
300
+ async function main() {
301
+ console.log("")
302
+ console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
303
+ console.log(" tfcode - ToothFairyAI's official coding agent")
304
+ console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
305
+ console.log("")
306
+
307
+ // Download and setup binary
308
+ await downloadBinary()
309
+
310
+ // Check for Python (needed for TF integration)
311
+ try {
312
+ const result = spawnSync("python3", ["--version"], { encoding: "utf8" })
313
+ if (result.status === 0) {
314
+ console.log(`✓ Found ${result.stdout.trim()}`)
315
+
316
+ // Install Python SDK
317
+ console.log("Installing ToothFairyAI Python SDK...")
318
+ const pipResult = spawnSync(
319
+ "python3",
320
+ ["-m", "pip", "install", "--user", "--break-system-packages", "toothfairyai", "pydantic", "httpx", "rich"],
321
+ {
322
+ stdio: "inherit",
323
+ },
324
+ )
325
+
326
+ if (pipResult.status === 0) {
327
+ console.log("✓ Python SDK installed")
328
+ } else {
329
+ console.log("! Python SDK install failed, run manually:")
330
+ console.log(" pip install toothfairyai pydantic httpx rich")
331
+ }
332
+ }
333
+ } catch {
334
+ console.log("! Python 3.10+ not found. Install with:")
335
+ console.log(" macOS: brew install python@3.12")
336
+ console.log(" Ubuntu: sudo apt install python3.12")
337
+ console.log(" Windows: Download from python.org/downloads")
338
+ console.log("")
339
+ }
340
+
341
+ console.log("")
342
+ console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
343
+ console.log("✓ tfcode installed successfully!")
344
+ console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
345
+ console.log("")
346
+ console.log("Quick Start:")
347
+ console.log("")
348
+ console.log(" 1. Set credentials:")
349
+ console.log(' export TF_WORKSPACE_ID="your-workspace-id"')
350
+ console.log(' export TF_API_KEY="your-api-key"')
351
+ console.log("")
352
+ console.log(" 2. Validate:")
353
+ console.log(" tfcode validate")
354
+ console.log("")
355
+ console.log(" 3. Sync tools:")
356
+ console.log(" tfcode sync")
357
+ console.log("")
358
+ console.log(" 4. Start coding:")
359
+ console.log(" tfcode")
360
+ console.log("")
361
+ // Optional feature — keep it short and clearly labeled so the coding-first
362
+ // Quick Start isn't noised up. Point npm installers at the one-command setup.
363
+ const cuPlatform = os.platform()
364
+ const cuHint =
365
+ cuPlatform === "darwin"
366
+ ? "a permissions dialog will appear (Accessibility & Screen Recording); cliclick is bundled."
367
+ : cuPlatform === "win32"
368
+ ? "no extra installs needed — pre-compiles the input helper."
369
+ : "installs xdotool + a screenshot tool via your package manager."
370
+ console.log(" Computer use (optional — control the OS GUI: mouse, keyboard, screen):")
371
+ console.log(" tfcode computer setup " + cuHint)
372
+ console.log(" tfcode computer doctor verify it works")
373
+ console.log("")
374
+ console.log("Documentation: https://toothfairyai.com/developers/tfcode")
375
+ console.log("")
376
+ }
377
+
378
+ main().catch(console.error)
@@ -0,0 +1,21 @@
1
+ """
2
+ tf-sync: ToothFairyAI workspace sync layer for tfcode
3
+ """
4
+
5
+ from tf_sync.agents import sync_agents
6
+ from tf_sync.mcp import sync_mcp_servers
7
+ from tf_sync.tools import sync_tools, ToolType
8
+ from tf_sync.config import TFConfig, load_config, validate_credentials, get_region_urls
9
+
10
+ __all__ = [
11
+ "sync_agents",
12
+ "sync_mcp_servers",
13
+ "sync_tools",
14
+ "ToolType",
15
+ "TFConfig",
16
+ "load_config",
17
+ "validate_credentials",
18
+ "get_region_urls",
19
+ ]
20
+
21
+ __version__ = "0.1.0"
@@ -0,0 +1,39 @@
1
+ """
2
+ Agent sync module for tfcode.
3
+
4
+ NOTE: This module is reserved for future implementation.
5
+ Currently, tfcode only syncs tools (MCP, Skills, Database, Functions).
6
+ Agent sync will be added in a later phase.
7
+ """
8
+
9
+ from typing import Any
10
+
11
+ from pydantic import BaseModel
12
+
13
+ from tf_sync.config import TFConfig
14
+
15
+
16
+ class AgentSyncResult(BaseModel):
17
+ """Result of agent sync operation."""
18
+
19
+ success: bool
20
+ agents: list[dict[str, Any]] = []
21
+ error: str | None = None
22
+
23
+
24
+ def sync_agents(config: TFConfig) -> AgentSyncResult:
25
+ """
26
+ Sync agents from ToothFairyAI workspace.
27
+
28
+ NOTE: Currently not implemented. Reserved for future use.
29
+
30
+ Args:
31
+ config: TFConfig instance
32
+
33
+ Returns:
34
+ AgentSyncResult (currently always returns not implemented)
35
+ """
36
+ return AgentSyncResult(
37
+ success=False,
38
+ error="Agent sync not yet implemented. Use tools sync for now.",
39
+ )