@toothfairyai/tfcode 2.0.0-beta.4 → 2.0.0
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 +201 -0
- package/bin/tfcode +0 -0
- package/package.json +13 -2
- package/postinstall.mjs +70 -1
- package/python/tf_sync/config.py +1 -0
package/README.md
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# TF Code
|
|
2
|
+
|
|
3
|
+
**ToothFairyAI's AI coding agent** — a command-line interface that brings AI-powered coding assistance to your terminal with deep ToothFairyAI workspace integration.
|
|
4
|
+
|
|
5
|
+
## Agents
|
|
6
|
+
|
|
7
|
+
TF Code ships with three built-in agents:
|
|
8
|
+
|
|
9
|
+
| Agent | Purpose | Key Ability |
|
|
10
|
+
| --------------- | ----------------------------- | ----------------------------------------------------------- |
|
|
11
|
+
| **Build** | Execute and implement | Full read/write access, the default agent |
|
|
12
|
+
| **Plan** | Explore and plan | Read-only analysis, writes plans to `.tfcode/plans/` |
|
|
13
|
+
| **TF Engineer** | Manage ToothFairyAI workspace | Exclusive access to live MCP tools for workspace operations |
|
|
14
|
+
|
|
15
|
+
Switch between them at any time with **Tab** or the `--agent` flag.
|
|
16
|
+
|
|
17
|
+
## Prerequisites
|
|
18
|
+
|
|
19
|
+
- **Node.js** 18+
|
|
20
|
+
- A modern terminal emulator (WezTerm, Alacritty, Kitty, Ghostty recommended)
|
|
21
|
+
- ToothFairyAI API credentials (from **Admin → TFCode & API Integration**)
|
|
22
|
+
|
|
23
|
+
## Install
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
npm install -g @toothfairyai/tfcode
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Verify:
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
tfcode --version
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Configure
|
|
36
|
+
|
|
37
|
+
Run the interactive setup to create a profile:
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
tfcode setup
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
This prompts for profile name, workspace ID, API key, and region. Alternatively, set environment variables:
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
export TF_WORKSPACE_ID="your-workspace-id"
|
|
47
|
+
export TF_API_KEY="your-api-key"
|
|
48
|
+
export TF_REGION="au"
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Validate:
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
tfcode validate
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Usage
|
|
58
|
+
|
|
59
|
+
### Interactive session
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
tfcode
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Single command
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
tfcode run "Explain this codebase"
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Plan first, then build
|
|
72
|
+
|
|
73
|
+
Use **Tab** to switch to Plan mode, describe what you want, then switch back to Build to implement.
|
|
74
|
+
|
|
75
|
+
1. **Plan** — describe the feature, review the plan
|
|
76
|
+
2. **Build** — switch back and ask it to implement
|
|
77
|
+
|
|
78
|
+
### Workspace management
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
tfcode run --agent "TF Engineer" "Create a retriever agent called SupportBot"
|
|
82
|
+
tfcode run --agent "TF Engineer" "Search documents about refund policy"
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Undo changes
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
/undo
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Profiles
|
|
92
|
+
|
|
93
|
+
Profiles store multiple sets of ToothFairyAI credentials so you can switch between workspaces instantly.
|
|
94
|
+
|
|
95
|
+
### Create
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
tfcode setup
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Non-interactive:
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
tfcode setup --profile dev --workspace-id dev-ws-456 --api-key dev-key-xxx --region dev --name dev
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Switch
|
|
108
|
+
|
|
109
|
+
```
|
|
110
|
+
tfcode profile prod
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Use in commands
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
tfcode -p prod run "Deploy to production"
|
|
117
|
+
TF_PROFILE=prod tfcode run "Force prod workspace"
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Regions
|
|
121
|
+
|
|
122
|
+
| Region | Base URL | Streaming URL |
|
|
123
|
+
| ------ | ------------------------ | ------------------------- |
|
|
124
|
+
| `dev` | `ai.toothfairylab.link` | `ais.toothfairylab.link` |
|
|
125
|
+
| `au` | `ai.toothfairyai.com` | `ais.toothfairyai.com` |
|
|
126
|
+
| `eu` | `ai.eu.toothfairyai.com` | `ais.eu.toothfairyai.com` |
|
|
127
|
+
| `us` | `ai.us.toothfairyai.com` | `ais.us.toothfairyai.com` |
|
|
128
|
+
|
|
129
|
+
## Environment Variables
|
|
130
|
+
|
|
131
|
+
| Variable | Description |
|
|
132
|
+
| ------------------------- | ----------------------------------------------- |
|
|
133
|
+
| `TF_WORKSPACE_ID` | ToothFairyAI workspace ID |
|
|
134
|
+
| `TF_API_KEY` | ToothFairyAI API key |
|
|
135
|
+
| `TF_REGION` | ToothFairyAI region: dev, au, eu, us |
|
|
136
|
+
| `TF_PROFILE` | Profile name to use (overrides default profile) |
|
|
137
|
+
| `TFCODE_SERVER_PASSWORD` | HTTP basic auth password for server mode |
|
|
138
|
+
| `TFCODE_SERVER_USERNAME` | HTTP basic auth username (default: tfcode) |
|
|
139
|
+
| `OPENCODE_CONFIG` | Path to custom config file |
|
|
140
|
+
| `OPENCODE_CONFIG_CONTENT` | Inline config content (highest priority) |
|
|
141
|
+
|
|
142
|
+
## CLI Commands
|
|
143
|
+
|
|
144
|
+
### Top-level
|
|
145
|
+
|
|
146
|
+
| Command | Description |
|
|
147
|
+
| --------------------- | ----------------------------------- |
|
|
148
|
+
| `tfcode` | Start interactive session |
|
|
149
|
+
| `tfcode run <prompt>` | Execute a single task and exit |
|
|
150
|
+
| `tfcode serve` | Start headless HTTP server |
|
|
151
|
+
| `tfcode web` | Start server and open web interface |
|
|
152
|
+
| `tfcode attach <url>` | Attach TUI to a running server |
|
|
153
|
+
|
|
154
|
+
### Setup & credentials
|
|
155
|
+
|
|
156
|
+
| Command | Description |
|
|
157
|
+
| -------------------------- | ------------------------------------------------------ |
|
|
158
|
+
| `tfcode setup` | Interactive credential setup (creates a profile) |
|
|
159
|
+
| `tfcode validate` | Validate credentials (`--profile <name>` for specific) |
|
|
160
|
+
| `tfcode profile` | Show current profile and list all |
|
|
161
|
+
| `tfcode profile <name>` | Switch to a different profile |
|
|
162
|
+
| `tfcode sync` | Sync workspace tools metadata |
|
|
163
|
+
| `tfcode providers list` | List available providers |
|
|
164
|
+
| `tfcode models [provider]` | List available models (optional provider filter) |
|
|
165
|
+
| `tfcode models --verbose` | List models with metadata |
|
|
166
|
+
| `tfcode models --refresh` | Refresh the models cache |
|
|
167
|
+
|
|
168
|
+
### MCP
|
|
169
|
+
|
|
170
|
+
| Command | Description |
|
|
171
|
+
| ------------------- | ---------------------------------- |
|
|
172
|
+
| `tfcode mcp list` | List MCP servers and tools |
|
|
173
|
+
| `tfcode mcp add` | Add a local MCP server |
|
|
174
|
+
| `tfcode mcp auth` | Authenticate with OAuth MCP server |
|
|
175
|
+
| `tfcode mcp logout` | Logout from MCP server |
|
|
176
|
+
|
|
177
|
+
### Agents
|
|
178
|
+
|
|
179
|
+
| Command | Description |
|
|
180
|
+
| ---------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
|
|
181
|
+
| `tfcode agent list` | List available agents |
|
|
182
|
+
| `tfcode agent create [--description ...]` | Generate a new local agent (`.tfcode/agents/<name>.md`) |
|
|
183
|
+
| `tfcode agent share <agent> [--force] [--profile p] [--dry-run]` | Upload a local agent (and its referenced skills) to the workspace as a coder agent |
|
|
184
|
+
|
|
185
|
+
See [docs/agent-share-flow.md](../../docs/agent-share-flow.md) for the full local→cloud agent flow.
|
|
186
|
+
|
|
187
|
+
### Chat commands (interactive)
|
|
188
|
+
|
|
189
|
+
| Command | Description |
|
|
190
|
+
| ------------------------- | --------------------------------------- |
|
|
191
|
+
| `/compact [instructions]` | Compact context with preservation hints |
|
|
192
|
+
| `/help` | Show available commands |
|
|
193
|
+
| `/undo` | Undo last changes |
|
|
194
|
+
|
|
195
|
+
## Documentation
|
|
196
|
+
|
|
197
|
+
Full documentation at [https://docs.toothfairyai.com/tfcode/intro](https://docs.toothfairyai.com/tfcode/intro)
|
|
198
|
+
|
|
199
|
+
## License
|
|
200
|
+
|
|
201
|
+
MIT
|
package/bin/tfcode
CHANGED
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@toothfairyai/tfcode",
|
|
3
|
-
"version": "2.0.0
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"bin": {
|
|
5
5
|
"tfcode": "./bin/tfcode.js"
|
|
6
6
|
},
|
|
@@ -9,7 +9,18 @@
|
|
|
9
9
|
},
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"optionalDependencies": {
|
|
12
|
-
"@toothfairyai/tfcode-
|
|
12
|
+
"@toothfairyai/tfcode-linux-arm64": "2.0.0",
|
|
13
|
+
"@toothfairyai/tfcode-windows-x64": "2.0.0",
|
|
14
|
+
"@toothfairyai/tfcode-linux-x64-baseline-musl": "2.0.0",
|
|
15
|
+
"@toothfairyai/tfcode-darwin-x64-baseline": "2.0.0",
|
|
16
|
+
"@toothfairyai/tfcode-linux-x64-musl": "2.0.0",
|
|
17
|
+
"@toothfairyai/tfcode-windows-x64-baseline": "2.0.0",
|
|
18
|
+
"@toothfairyai/tfcode-linux-arm64-musl": "2.0.0",
|
|
19
|
+
"@toothfairyai/tfcode-windows-arm64": "2.0.0",
|
|
20
|
+
"@toothfairyai/tfcode-linux-x64": "2.0.0",
|
|
21
|
+
"@toothfairyai/tfcode-darwin-x64": "2.0.0",
|
|
22
|
+
"@toothfairyai/tfcode-linux-x64-baseline": "2.0.0",
|
|
23
|
+
"@toothfairyai/tfcode-darwin-arm64": "2.0.0"
|
|
13
24
|
},
|
|
14
25
|
"engines": {
|
|
15
26
|
"node": ">=18"
|
package/postinstall.mjs
CHANGED
|
@@ -82,6 +82,37 @@ async function getVersion() {
|
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
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
|
+
|
|
85
116
|
async function downloadBinary() {
|
|
86
117
|
const { platform, arch, needsBaseline, abi } = detectPlatform()
|
|
87
118
|
const version = await getVersion()
|
|
@@ -91,8 +122,27 @@ async function downloadBinary() {
|
|
|
91
122
|
const binDir = path.join(__dirname, "bin")
|
|
92
123
|
const existingBinary = path.join(binDir, binaryName)
|
|
93
124
|
|
|
94
|
-
if (fs.existsSync(existingBinary)) {
|
|
125
|
+
if (fs.existsSync(existingBinary) && binaryArchMatches(existingBinary, arch, platform)) {
|
|
95
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
|
+
} catch (e) {
|
|
143
|
+
console.log(`! Could not copy web app: ${e.message}`)
|
|
144
|
+
}
|
|
145
|
+
}
|
|
96
146
|
return
|
|
97
147
|
}
|
|
98
148
|
|
|
@@ -181,6 +231,15 @@ async function downloadBinary() {
|
|
|
181
231
|
console.log(`Installed tfcode to ${targetBinary}`)
|
|
182
232
|
}
|
|
183
233
|
|
|
234
|
+
// Move app directory
|
|
235
|
+
const extractedAppDir = path.join(tmpDir, "app")
|
|
236
|
+
const targetAppDir = path.join(binDir, "app")
|
|
237
|
+
if (fs.existsSync(path.join(extractedAppDir, "dist", "index.html"))) {
|
|
238
|
+
if (fs.existsSync(targetAppDir)) fs.rmSync(targetAppDir, { recursive: true, force: true })
|
|
239
|
+
fs.cpSync(extractedAppDir, targetAppDir, { recursive: true })
|
|
240
|
+
console.log("Installed web app")
|
|
241
|
+
}
|
|
242
|
+
|
|
184
243
|
// Cleanup
|
|
185
244
|
fs.rmSync(tmpDir, { recursive: true, force: true })
|
|
186
245
|
}
|
|
@@ -202,6 +261,16 @@ function setupBinary(sourcePath, platform) {
|
|
|
202
261
|
|
|
203
262
|
fs.chmodSync(targetBinary, 0o755)
|
|
204
263
|
console.log(`tfcode installed to ${targetBinary}`)
|
|
264
|
+
|
|
265
|
+
// Also copy app dir from platform package
|
|
266
|
+
const sourceDir = path.dirname(sourcePath)
|
|
267
|
+
const srcAppDir = path.join(sourceDir, "app")
|
|
268
|
+
const targetAppDir = path.join(binDir, "app")
|
|
269
|
+
if (fs.existsSync(path.join(srcAppDir, "dist", "index.html"))) {
|
|
270
|
+
if (fs.existsSync(targetAppDir)) fs.rmSync(targetAppDir, { recursive: true, force: true })
|
|
271
|
+
fs.cpSync(srcAppDir, targetAppDir, { recursive: true })
|
|
272
|
+
console.log("Web app copied from platform package")
|
|
273
|
+
}
|
|
205
274
|
}
|
|
206
275
|
|
|
207
276
|
async function main() {
|