mirror of
https://gitea.toothfairyai.com/ToothFairyAI/tf_code.git
synced 2026-03-29 21:33:54 +00:00
feat: initialize tfcode project structure
- Add README.md as living documentation - Add tfcode.json schema and config template - Add FORK_MANAGEMENT.md with mirror-based fork strategy - Add scripts/rebrand.sh for reapplying branding after upstream merges - Add packages/tf-sync Python module using official ToothFairyAI SDK - Add packages/tf-mcp-bridge TypeScript module (stub) - Multi-region support (AU, EU, US) - Tool sync: MCP servers, Agent Skills, Database Scripts, API Functions
This commit is contained in:
parent
7bb69038ec
commit
abdfa7330e
218
FORK_MANAGEMENT.md
Normal file
218
FORK_MANAGEMENT.md
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
# tfcode Fork Management Strategy
|
||||||
|
|
||||||
|
> How we manage the soft fork of opencode for tfcode development
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Repository Architecture
|
||||||
|
|
||||||
|
### Three-Repo Model
|
||||||
|
|
||||||
|
| Repo | URL | Purpose | Access |
|
||||||
|
|------|-----|---------|--------|
|
||||||
|
| **Upstream** | `github.com/anomalyco/opencode` | Official source, releases | Read-only |
|
||||||
|
| **Mirror** | `gitea.toothfairyai.com/GitHub/opencode` | Auto-mirror of upstream | Read-only |
|
||||||
|
| **Development** | `gitea.toothfairyai.com/ToothFairyAI/tf_code` | Our fork, tfcode product | Read-write |
|
||||||
|
|
||||||
|
### Visual Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────────────┐
|
||||||
|
│ │
|
||||||
|
│ UPSTREAM MIRROR DEV REPO │
|
||||||
|
│ (Official) (Reference) (Our Fork) │
|
||||||
|
│ │
|
||||||
|
│ ┌───────────┐ ┌───────────┐ ┌───────────┐│
|
||||||
|
│ │ │ │ │ │ ││
|
||||||
|
│ │ opencode │───auto────▶│ opencode │──manual──▶│ tf_code ││
|
||||||
|
│ │ (github) │ mirror │ (gitea) │ PR sync │ (gitea) ││
|
||||||
|
│ │ │ │ │ │ ││
|
||||||
|
│ └───────────┘ └───────────┘ └───────────┘│
|
||||||
|
│ │ │ │ │
|
||||||
|
│ │ │ │ │
|
||||||
|
│ Releases Releases + tfcode │
|
||||||
|
│ (v1.x.x) Commit history product │
|
||||||
|
│ + TF integrations│
|
||||||
|
│ │
|
||||||
|
└─────────────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Sync Workflow
|
||||||
|
|
||||||
|
### Per-Release Sync Process
|
||||||
|
|
||||||
|
When a new opencode release is published:
|
||||||
|
|
||||||
|
**1. Mirror Updates** (Automatic)
|
||||||
|
- Gitea automatically mirrors the new release
|
||||||
|
- Tags appear in `GitHub/opencode` repo
|
||||||
|
|
||||||
|
**2. Create Sync PR** (Manual)
|
||||||
|
```bash
|
||||||
|
# In tf_code repo
|
||||||
|
git remote add mirror https://gitea.toothfairyai.com/GitHub/opencode.git
|
||||||
|
git fetch mirror --tags
|
||||||
|
|
||||||
|
# Create sync branch from latest release
|
||||||
|
git checkout -b sync/v1.3.0
|
||||||
|
git merge mirror/dev --no-commit
|
||||||
|
|
||||||
|
# Review conflicts, resolve manually
|
||||||
|
# Reapply branding changes if needed
|
||||||
|
|
||||||
|
# Push and create PR
|
||||||
|
git push origin sync/v1.3.0
|
||||||
|
```
|
||||||
|
|
||||||
|
**3. Evaluate Merge**
|
||||||
|
- Review all changes from upstream
|
||||||
|
- Check for breaking changes
|
||||||
|
- Verify branding still applies
|
||||||
|
- Test ToothFairyAI integrations
|
||||||
|
|
||||||
|
**4. Merge to Main**
|
||||||
|
- If approved, merge PR to `main`
|
||||||
|
- If rejected, note incompatibilities for later work
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Rebrand Tracking
|
||||||
|
|
||||||
|
### What Needs Rebranding
|
||||||
|
|
||||||
|
| Category | Original | Rebranded | Files Affected |
|
||||||
|
|----------|----------|-----------|----------------|
|
||||||
|
| **Command** | `opencode` | `tfcode` | CLI, scripts |
|
||||||
|
| **Package** | `opencode-ai` | `tfcode` | package.json, pyproject.toml |
|
||||||
|
| **Config** | `opencode.json` | `tfcode.json` | config loaders |
|
||||||
|
| **Env vars** | `OPENCODE_*` | `TFCODE_*` | documentation, code |
|
||||||
|
| **URLs** | `opencode.ai` | `toothfairyai.com` | docs, links |
|
||||||
|
| **Directories** | `.opencode/` | `.tfcode/` | config paths |
|
||||||
|
| **Branding** | opencode logos | tfcode logos | assets/ |
|
||||||
|
|
||||||
|
### Rebrand Script
|
||||||
|
|
||||||
|
After each upstream merge, run rebrand script:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./scripts/rebrand.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Gitea Configuration
|
||||||
|
|
||||||
|
### Mirror Setup
|
||||||
|
|
||||||
|
In Gitea admin panel for `GitHub/opencode`:
|
||||||
|
|
||||||
|
```
|
||||||
|
Mirror Settings:
|
||||||
|
- URL: https://github.com/anomalyco/opencode.git
|
||||||
|
- Sync Interval: 30 minutes
|
||||||
|
- Sync: Enable
|
||||||
|
- LFS: Enable if used
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Conflict Resolution Guidelines
|
||||||
|
|
||||||
|
### Common Conflict Areas
|
||||||
|
|
||||||
|
| Area | Conflict Likelihood | Resolution Strategy |
|
||||||
|
|------|---------------------|---------------------|
|
||||||
|
| Package.json | High | Keep tfcode name, merge deps |
|
||||||
|
| Config schemas | Medium | Merge both schemas |
|
||||||
|
| CLI commands | High | Keep tfcode branding |
|
||||||
|
| Core engine | Low | Usually clean merge |
|
||||||
|
| UI/Themes | Medium | Keep tfcode branding |
|
||||||
|
|
||||||
|
### Conflict Resolution Process
|
||||||
|
|
||||||
|
1. **Identify conflict type**:
|
||||||
|
- Branding conflict (expected, reapply our changes)
|
||||||
|
- Feature conflict (evaluate which to keep)
|
||||||
|
- Breaking change (may require code updates)
|
||||||
|
|
||||||
|
2. **Resolution priority**:
|
||||||
|
- Keep upstream core changes (features, fixes)
|
||||||
|
- Reapply our branding on top
|
||||||
|
- Preserve TF integrations
|
||||||
|
|
||||||
|
3. **Test after merge**:
|
||||||
|
- Run test suite
|
||||||
|
- Verify tfcode branding intact
|
||||||
|
- Test TF tool sync functionality
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Release Schedule
|
||||||
|
|
||||||
|
### Upstream Sync Cadence
|
||||||
|
|
||||||
|
| Trigger | Action |
|
||||||
|
|---------|--------|
|
||||||
|
| **Minor release** (v1.x.0) | Full sync PR, thorough review |
|
||||||
|
| **Patch release** (v1.x.y) | Quick sync PR, focus on bug fixes |
|
||||||
|
| **Major release** (v2.0.0) | Careful evaluation, may defer |
|
||||||
|
|
||||||
|
### Our Release Process
|
||||||
|
|
||||||
|
1. Sync from upstream (if available)
|
||||||
|
2. Test tfcode functionality
|
||||||
|
3. Update version in package files
|
||||||
|
4. Tag release in tf_code repo
|
||||||
|
5. Build and publish packages
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Do's and Don'ts
|
||||||
|
|
||||||
|
### ✅ Do
|
||||||
|
|
||||||
|
- Keep upstream code changes when merging
|
||||||
|
- Reapply branding after merges
|
||||||
|
- Test thoroughly after each sync
|
||||||
|
- Document any breaking changes from upstream
|
||||||
|
- Maintain separate release notes for tfcode
|
||||||
|
|
||||||
|
### ❌ Don't
|
||||||
|
|
||||||
|
- Don't modify mirror repo (read-only)
|
||||||
|
- Don't skip conflict resolution
|
||||||
|
- Don't auto-merge without review
|
||||||
|
- Don't lose TF integration changes during merge
|
||||||
|
- Don't contribute back to opencode directly (unless agreed)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Quick Reference Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Add mirror remote (one-time setup)
|
||||||
|
git remote add mirror https://gitea.toothfairyai.com/GitHub/opencode.git
|
||||||
|
|
||||||
|
# Fetch latest from mirror
|
||||||
|
git fetch mirror --tags
|
||||||
|
|
||||||
|
# Check what's new since last sync
|
||||||
|
git log HEAD..mirror/dev --oneline
|
||||||
|
|
||||||
|
# Create sync PR branch
|
||||||
|
git checkout -b sync/v1.3.0
|
||||||
|
git merge mirror/dev
|
||||||
|
|
||||||
|
# After resolving conflicts
|
||||||
|
./scripts/rebrand.sh # Reapply branding
|
||||||
|
npm test # Test
|
||||||
|
git push origin sync/v1.3.0
|
||||||
|
|
||||||
|
# Merge PR after approval
|
||||||
|
git checkout main
|
||||||
|
git merge --no-ff sync/v1.3.0
|
||||||
|
git push origin main
|
||||||
|
git tag v1.3.0-tf.1
|
||||||
|
```
|
||||||
452
README.md
Normal file
452
README.md
Normal file
@ -0,0 +1,452 @@
|
|||||||
|
# tfcode
|
||||||
|
|
||||||
|
> ToothFairyAI's official coding agent
|
||||||
|
|
||||||
|
**Status**: 🟡 Planning Phase | **Last Updated**: 2026-03-24
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Product Overview
|
||||||
|
|
||||||
|
**tfcode** is ToothFairyAI's official AI coding agent - a terminal-based coding assistant that integrates seamlessly with your TF workspace tools, MCP servers, agent skills, and database connections.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Product Identity
|
||||||
|
|
||||||
|
| Aspect | Details |
|
||||||
|
|--------|---------|
|
||||||
|
| **Name** | tfcode |
|
||||||
|
| **Positioning** | ToothFairyAI's official coding agent |
|
||||||
|
| **Target Users** | Existing TF customers |
|
||||||
|
| **Config Compatibility** | Supports opencode.json for migration |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Key Features
|
||||||
|
|
||||||
|
### Core Capabilities
|
||||||
|
- Terminal-based AI coding assistant
|
||||||
|
- File read/write/edit operations
|
||||||
|
- Bash command execution
|
||||||
|
- Code search (glob, grep)
|
||||||
|
- Multi-agent support (Tab switching)
|
||||||
|
|
||||||
|
### ToothFairyAI Integration
|
||||||
|
- **MCP Servers** from TF workspace (`isMCPServer`)
|
||||||
|
- **Agent Skills** from TF workspace (`isAgentSkill`)
|
||||||
|
- **Database Scripts** from TF workspace (`isDatabaseScript`)
|
||||||
|
- **API Functions** from TF workspace (`requestType`)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Fork Management
|
||||||
|
|
||||||
|
This is a **soft fork** of the opencode project, managed via a mirror-based strategy.
|
||||||
|
|
||||||
|
| Repo | Purpose |
|
||||||
|
|------|---------|
|
||||||
|
| `github.com/anomalyco/opencode` | Upstream (official source) |
|
||||||
|
| `gitea.toothfairyai.com/GitHub/opencode` | Mirror (auto-synced) |
|
||||||
|
| `gitea.toothfairyai.com/ToothFairyAI/tf_code` | Development (this repo) |
|
||||||
|
|
||||||
|
**Sync Strategy**: Per-release manual PR-based sync from mirror to dev repo.
|
||||||
|
|
||||||
|
See [FORK_MANAGEMENT.md](./FORK_MANAGEMENT.md) for full details.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||||
|
│ tfcode │
|
||||||
|
├─────────────────────────────────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
||||||
|
│ │ CORE ENGINE │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ • TUI (Terminal UI) │ │
|
||||||
|
│ │ • Agent orchestration │ │
|
||||||
|
│ │ • Tool execution │ │
|
||||||
|
│ │ • File operations │ │
|
||||||
|
│ │ • Code search │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ └─────────────────────────────────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ ┌─────────────────────────────────────────────────────────────────────┐ │
|
||||||
|
│ │ TOOTHFAIRYAI INTEGRATION │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ TF WORKSPACE TFCODE │ │
|
||||||
|
│ │ ┌──────────────────┐ ┌──────────────────┐ │ │
|
||||||
|
│ │ │ Tools: │ │ Synced Tools: │ │ │
|
||||||
|
│ │ │ │ │ │ │ │
|
||||||
|
│ │ │ ┌──────────────┐ │ │ ┌──────────────┐ │ │ │
|
||||||
|
│ │ │ │ MCP Servers │ │ SYNC │ │ github-mcp │ │ │ │
|
||||||
|
│ │ │ │ (isMCPServer)│ │ ──────▶ │ │ auth: proxy │ │ │ │
|
||||||
|
│ │ │ └──────────────┘ │ │ └──────────────┘ │ │ │
|
||||||
|
│ │ │ ┌──────────────┐ │ │ ┌──────────────┐ │ │ │
|
||||||
|
│ │ │ │ Agent Skills │ │ SYNC │ │ code-reviewer│ │ │ │
|
||||||
|
│ │ │ │(isAgentSkill)│ │ ──────▶ │ │ auth: proxy │ │ │ │
|
||||||
|
│ │ │ └──────────────┘ │ │ └──────────────┘ │ │ │
|
||||||
|
│ │ │ ┌──────────────┐ │ │ ┌──────────────┐ │ │ │
|
||||||
|
│ │ │ │ DB Scripts │ │ SYNC │ │ postgres-main│ │ │ │
|
||||||
|
│ │ │ │(isDatabase) │ │ ──────▶ │ │ auth: proxy │ │ │ │
|
||||||
|
│ │ │ └──────────────┘ │ │ └──────────────┘ │ │ │
|
||||||
|
│ │ │ ┌──────────────┐ │ │ ┌──────────────┐ │ │ │
|
||||||
|
│ │ │ │ API Functions│ │ SYNC │ │ weather_api │ │ │ │
|
||||||
|
│ │ │ │(requestType)│ │ ──────▶ │ │ auth: user │ │ │ │
|
||||||
|
│ │ │ └──────────────┘ │ │ └──────────────┘ │ │ │
|
||||||
|
│ │ └──────────────────┘ └──────────────────┘ │ │
|
||||||
|
│ │ │ │ │ │
|
||||||
|
│ │ ▼ ▼ │ │
|
||||||
|
│ │ CREDENTIALS TOOL CALLS │ │
|
||||||
|
│ │ (Stay in TF) │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ ┌─────────────────────────────────────────────────────┐ │ │
|
||||||
|
│ │ │ │ │ │
|
||||||
|
│ │ │ MCP/Skill/DB Tools API Function Tools │ │ │
|
||||||
|
│ │ │ │ │ │ │ │
|
||||||
|
│ │ │ ▼ ▼ │ │ │
|
||||||
|
│ │ │ ┌─────────────────┐ ┌─────────────────┐ │ │ │
|
||||||
|
│ │ │ │ TF Proxy │ │ Direct HTTP │ │ │ │
|
||||||
|
│ │ │ │ • Inject creds │ │ • User api_key │ │ │ │
|
||||||
|
│ │ │ │ • Route to tool │ │ • Config-based │ │ │ │
|
||||||
|
│ │ │ └─────────────────┘ └─────────────────┘ │ │ │
|
||||||
|
│ │ │ │ │ │
|
||||||
|
│ │ └─────────────────────────────────────────────────────┘ │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ └─────────────────────────────────────────────────────────────────────┘ │
|
||||||
|
│ │
|
||||||
|
└─────────────────────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Tool Types
|
||||||
|
|
||||||
|
### From ToothFairyAI Workspace
|
||||||
|
|
||||||
|
| Type | Flag/Field | Credential Handling |
|
||||||
|
|------|------------|---------------------|
|
||||||
|
| MCP Servers | `isMCPServer: true` | TF Proxy (secure) |
|
||||||
|
| Agent Skills | `isAgentSkill: true` | TF Proxy (secure) |
|
||||||
|
| Database Scripts | `isDatabaseScript: true` | TF Proxy (secure) |
|
||||||
|
| API Functions | `requestType` enum | User provides in config |
|
||||||
|
|
||||||
|
### requestType Values
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
enum FunctionRequestType {
|
||||||
|
get = "get",
|
||||||
|
post = "post",
|
||||||
|
put = "put",
|
||||||
|
delete = "delete",
|
||||||
|
patch = "patch",
|
||||||
|
custom = "custom",
|
||||||
|
graphql_query = "graphql_query",
|
||||||
|
graphql_mutation = "graphql_mutation"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
### tfcode.json (Primary)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"$schema": "https://toothfairyai.com/schemas/tfcode.json",
|
||||||
|
|
||||||
|
"toothfairy": {
|
||||||
|
"workspace_id": "{env:TF_WORKSPACE_ID}",
|
||||||
|
"api_key": "{env:TF_API_KEY}",
|
||||||
|
"region": "au"
|
||||||
|
},
|
||||||
|
|
||||||
|
"tools": {
|
||||||
|
"github-mcp": {
|
||||||
|
"type": "mcp_server",
|
||||||
|
"isMCPServer": true,
|
||||||
|
"auth_via": "tf_proxy"
|
||||||
|
},
|
||||||
|
|
||||||
|
"code-reviewer": {
|
||||||
|
"type": "agent_skill",
|
||||||
|
"isAgentSkill": true,
|
||||||
|
"auth_via": "tf_proxy"
|
||||||
|
},
|
||||||
|
|
||||||
|
"postgres-main": {
|
||||||
|
"type": "database_script",
|
||||||
|
"isDatabaseScript": true,
|
||||||
|
"auth_via": "tf_proxy"
|
||||||
|
},
|
||||||
|
|
||||||
|
"weather_api": {
|
||||||
|
"type": "api_function",
|
||||||
|
"requestType": "get",
|
||||||
|
"url": "https://api.weatherapi.com/v1/current.json",
|
||||||
|
"api_key": "{env:WEATHER_API_KEY}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### opencode.json (Migration Support)
|
||||||
|
|
||||||
|
For users migrating from opencode, tfcode supports the legacy schema:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"$schema": "https://opencode.ai/config.json",
|
||||||
|
|
||||||
|
"toothfairy": {
|
||||||
|
"workspace_id": "{env:TF_WORKSPACE_ID}",
|
||||||
|
"api_key": "{env:TF_API_KEY}",
|
||||||
|
"region": "au"
|
||||||
|
},
|
||||||
|
|
||||||
|
"mcp": {
|
||||||
|
"github-mcp": {
|
||||||
|
"type": "remote",
|
||||||
|
"url": "https://mcp.github.com",
|
||||||
|
"auth_via": "tf_proxy"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
tfcode automatically detects and converts opencode.json to tfcode.json on first run.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Via curl (recommended)
|
||||||
|
curl -fsSL https://toothfairyai.com/install/tfcode | bash
|
||||||
|
|
||||||
|
# Via npm
|
||||||
|
npm install -g tfcode
|
||||||
|
|
||||||
|
# Via pip
|
||||||
|
pip install tfcode-cli
|
||||||
|
|
||||||
|
# Via brew
|
||||||
|
brew install toothfairyai/tap/tfcode
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Set environment variables
|
||||||
|
export TF_API_KEY="tf_live_xxx"
|
||||||
|
export TF_WORKSPACE_ID="your-workspace-uuid"
|
||||||
|
|
||||||
|
# Validate credentials
|
||||||
|
tfcode validate
|
||||||
|
|
||||||
|
# Sync tools from TF workspace
|
||||||
|
tfcode sync
|
||||||
|
|
||||||
|
# Start tfcode
|
||||||
|
tfcode
|
||||||
|
|
||||||
|
# Switch agents with Tab
|
||||||
|
# Use TF tools with @tool-name:operation
|
||||||
|
> @github-mcp:repo_list owner="myorg"
|
||||||
|
> @postgres-main:query sql="SELECT * FROM users"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## CLI Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Credential Management
|
||||||
|
tfcode validate # Test TF credentials
|
||||||
|
tfcode connect # Interactive credential setup
|
||||||
|
|
||||||
|
# Tool Sync
|
||||||
|
tfcode sync # Sync all tools from TF
|
||||||
|
tfcode sync --force # Force re-sync
|
||||||
|
tfcode tools list # List synced tools
|
||||||
|
tfcode tools list --type mcp # List MCP servers
|
||||||
|
tfcode tools list --type skill # List agent skills
|
||||||
|
tfcode tools list --type database # List DB scripts
|
||||||
|
tfcode tools list --type function # List API functions
|
||||||
|
|
||||||
|
# API Function Credentials
|
||||||
|
tfcode tools credentials <name> --set # Set API key
|
||||||
|
tfcode tools credentials <name> --show # Show stored key
|
||||||
|
|
||||||
|
# Debug
|
||||||
|
tfcode tools debug <name> # Debug tool connection
|
||||||
|
tfcode tools test <name> # Test tool call
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Implementation Phases
|
||||||
|
|
||||||
|
### Phase 1: Rebrand & Foundation ⏳ IN PROGRESS
|
||||||
|
|
||||||
|
**Tasks**:
|
||||||
|
- [x] Fork repository structure
|
||||||
|
- [ ] Rebrand opencode → tfcode
|
||||||
|
- [ ] Replace all string references
|
||||||
|
- [ ] Replace URLs (opencode.ai → toothfairyai.com)
|
||||||
|
- [ ] Replace logos and branding assets
|
||||||
|
- [ ] Update package name to `tfcode`
|
||||||
|
- [x] Create new config schema (`tfcode.json`)
|
||||||
|
- [ ] Add opencode.json → tfcode.json migration
|
||||||
|
- [x] Define tool type schema
|
||||||
|
- [x] `isMCPServer` detection
|
||||||
|
- [x] `isAgentSkill` detection
|
||||||
|
- [x] `isDatabaseScript` detection
|
||||||
|
- [x] `requestType` enum handling
|
||||||
|
- [x] Implement credential validation
|
||||||
|
- [x] Create TF SDK integration layer
|
||||||
|
- [x] Multi-region support (AU, EU, US)
|
||||||
|
|
||||||
|
### Phase 2: Tool Sync ⏳ IN PROGRESS
|
||||||
|
|
||||||
|
**Tasks**:
|
||||||
|
- [x] Implement TF workspace tool listing via SDK
|
||||||
|
- [x] Build tool type classifier
|
||||||
|
- [x] `isMCPServer` detection
|
||||||
|
- [x] `isAgentSkill` detection
|
||||||
|
- [x] `isDatabaseScript` detection
|
||||||
|
- [x] `requestType` enum handling
|
||||||
|
- [x] Create tool metadata sync using SDK
|
||||||
|
- [ ] Handle API functions with user credentials
|
||||||
|
- [ ] `isAgentSkill` detection
|
||||||
|
- [ ] `isDatabaseScript` detection
|
||||||
|
- [ ] `requestType` enum handling
|
||||||
|
- [ ] Create tool metadata sync
|
||||||
|
- [ ] Handle API functions with user credentials
|
||||||
|
|
||||||
|
### Phase 3: TF Proxy Integration
|
||||||
|
|
||||||
|
**Tasks**:
|
||||||
|
- [ ] Build TF Proxy client
|
||||||
|
- [ ] Implement tool call routing
|
||||||
|
- [ ] MCP/Skill/DB → TF Proxy
|
||||||
|
- [ ] API Functions → Direct HTTP
|
||||||
|
- [ ] Add tool namespace (`@tool-name:operation`)
|
||||||
|
- [ ] Implement permission checks
|
||||||
|
|
||||||
|
### Phase 4: CLI & UX
|
||||||
|
|
||||||
|
**Tasks**:
|
||||||
|
- [ ] Implement tfcode CLI commands
|
||||||
|
- [ ] Build interactive credential setup
|
||||||
|
- [ ] Add tool status reporting
|
||||||
|
- [ ] Create user-friendly error messages
|
||||||
|
|
||||||
|
### Phase 5: Documentation & Release
|
||||||
|
|
||||||
|
**Tasks**:
|
||||||
|
- [ ] User documentation
|
||||||
|
- [ ] Migration guide (opencode → tfcode)
|
||||||
|
- [ ] Installation scripts
|
||||||
|
- [ ] Package publishing (npm, pip, brew)
|
||||||
|
- [ ] First release
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Rebrand Checklist
|
||||||
|
|
||||||
|
### Code References
|
||||||
|
- [ ] `opencode` → `tfcode` in all source files
|
||||||
|
- [ ] `OPENCODE_` → `TFCODE_` in environment variables
|
||||||
|
- [ ] `opencode.json` support (migration) + `tfcode.json` (primary)
|
||||||
|
- [ ] Package name: `tfcode` (npm, pip)
|
||||||
|
|
||||||
|
### URLs & Branding
|
||||||
|
- [ ] `opencode.ai` → `toothfairyai.com`
|
||||||
|
- [ ] `github.com/anomalyco/opencode` → new repo
|
||||||
|
- [ ] Logo replacement
|
||||||
|
- [ ] Theme colors (toothfairyai palette)
|
||||||
|
- [ ] Command: `opencode` → `tfcode`
|
||||||
|
|
||||||
|
### Config
|
||||||
|
- [ ] Schema URL: `toothfairyai.com/schemas/tfcode.json`
|
||||||
|
- [ ] Config directory: `~/.config/tfcode/`
|
||||||
|
- [ ] Data directory: `~/.local/share/tfcode/`
|
||||||
|
- [ ] Support `opencode.json` for migration
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Implementation Notes
|
||||||
|
|
||||||
|
### 2026-03-24: Planning & Phase 1 Start
|
||||||
|
|
||||||
|
**Product Decision**:
|
||||||
|
- **Name**: tfcode
|
||||||
|
- **Positioning**: ToothFairyAI's official coding agent
|
||||||
|
- **Target**: Existing TF customers
|
||||||
|
- **Config**: tfcode.json primary, opencode.json supported for migration
|
||||||
|
|
||||||
|
**Fork Management**:
|
||||||
|
- **Upstream**: `github.com/anomalyco/opencode`
|
||||||
|
- **Mirror**: `gitea.toothfairyai.com/GitHub/opencode` (auto-synced)
|
||||||
|
- **Development**: `gitea.toothfairyai.com/ToothFairyAI/tf_code` (this repo)
|
||||||
|
- **Sync Strategy**: Per-release manual PR-based sync
|
||||||
|
- **Rebrand Script**: `scripts/rebrand.sh` reapplies branding after merges
|
||||||
|
|
||||||
|
**Scope**:
|
||||||
|
- Full rebrand of codebase
|
||||||
|
- Tool integration: MCP, Skills, Database, API Functions
|
||||||
|
- Credentials: TF Proxy for secure types, user-provided for API functions
|
||||||
|
|
||||||
|
**Architecture**:
|
||||||
|
- Core engine (TUI, agents, file ops)
|
||||||
|
- TF integration layer (tool sync, proxy routing)
|
||||||
|
|
||||||
|
**Implementation Progress**:
|
||||||
|
- [x] README created as living document
|
||||||
|
- [x] tfcode.json schema defined
|
||||||
|
- [x] Project structure created (packages/tf-sync, packages/tf-mcp-bridge)
|
||||||
|
- [x] Python SDK integration (using toothfairyai SDK for multi-region support)
|
||||||
|
- [x] Tool sync module using SDK's agent_functions.list()
|
||||||
|
- [x] Multi-region URL configuration (AU, EU, US)
|
||||||
|
- [x] Fork management strategy documented (FORK_MANAGEMENT.md)
|
||||||
|
- [x] Rebrand script created (scripts/rebrand.sh)
|
||||||
|
- [ ] TypeScript/Node.js bridge module
|
||||||
|
- [ ] MCP proxy client
|
||||||
|
- [ ] CLI commands
|
||||||
|
|
||||||
|
**Key Technical Decisions**:
|
||||||
|
- Use official ToothFairyAI Python SDK for all TF API interactions
|
||||||
|
- Multi-region support via Region enum and REGION_URLS config
|
||||||
|
- Tool types classified by: `isMCPServer`, `isAgentSkill`, `isDatabaseScript`, `requestType`
|
||||||
|
- Sync functions are synchronous (SDK handles async internally)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Resources
|
||||||
|
|
||||||
|
### ToothFairyAI
|
||||||
|
- [Developer Portal](https://toothfairyai.com/developers)
|
||||||
|
- [API Documentation](https://apidocs.toothfairyai.com)
|
||||||
|
- [Functions/Tools](https://docs.toothfairyai.com/docs/Settings/functions)
|
||||||
|
|
||||||
|
### Downloads
|
||||||
|
- [tfcode CLI](https://toothfairyai.com/install/tfcode)
|
||||||
|
- [npm package](https://www.npmjs.com/package/tfcode)
|
||||||
|
- [PyPI package](https://pypi.org/project/tfcode-cli/)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Living document - Updated: 2026-03-24*
|
||||||
32
opencode.json
Normal file
32
opencode.json
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://opencode.ai/config.json",
|
||||||
|
"autoupdate": true,
|
||||||
|
"model": "fireworks-ai/accounts/fireworks/models/glm-5",
|
||||||
|
"mcp": {
|
||||||
|
"toothfairy": {
|
||||||
|
"type": "remote",
|
||||||
|
"url": "https://mcp.toothfairyai.com/sse",
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
"toothfairy_dev": {
|
||||||
|
"type": "remote",
|
||||||
|
"url": "https://mcp.toothfairylab.link/sse",
|
||||||
|
"enabled": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"agent": {
|
||||||
|
"tf": {
|
||||||
|
"mode": "primary",
|
||||||
|
"model": "fireworks-ai/accounts/fireworks/models/glm-5",
|
||||||
|
"temperature": 1,
|
||||||
|
"description": "ToothFairy agent with high creativity",
|
||||||
|
"prompt": "You are a talented code co-author that never performs rollbacks of files especially in the git stack whether they are staged or stashed. You must help the human coder in making specific changes that can be inspected leveraging git changes in VS code"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"formatter": {
|
||||||
|
"black": {
|
||||||
|
"command": ["black", "$FILE"],
|
||||||
|
"extensions": [".py", ".pyi"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
27
packages/tf-mcp-bridge/package.json
Normal file
27
packages/tf-mcp-bridge/package.json
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"name": "tf-mcp-bridge",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "MCP proxy bridge for tfcode",
|
||||||
|
"main": "dist/index.js",
|
||||||
|
"types": "dist/index.d.ts",
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsup src/index.ts --dts",
|
||||||
|
"dev": "tsup src/index.ts --dts --watch",
|
||||||
|
"typecheck": "tsc --noEmit",
|
||||||
|
"lint": "eslint src/",
|
||||||
|
"test": "vitest"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"zod": "^3.22.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^20.0.0",
|
||||||
|
"tsup": "^8.0.0",
|
||||||
|
"typescript": "^5.3.0",
|
||||||
|
"vitest": "^1.0.0",
|
||||||
|
"eslint": "^8.0.0"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist"
|
||||||
|
]
|
||||||
|
}
|
||||||
10
packages/tf-mcp-bridge/src/index.ts
Normal file
10
packages/tf-mcp-bridge/src/index.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
/**
|
||||||
|
* tf-mcp-bridge: MCP proxy bridge for tfcode
|
||||||
|
*
|
||||||
|
* Handles communication with ToothFairyAI MCP proxy for secure tool calls.
|
||||||
|
* Credentials stay in TF - this module routes calls through the proxy.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export * from './proxy';
|
||||||
|
export * from './router';
|
||||||
|
export * from './types';
|
||||||
35
packages/tf-sync/pyproject.toml
Normal file
35
packages/tf-sync/pyproject.toml
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
[build-system]
|
||||||
|
requires = ["hatchling"]
|
||||||
|
build-backend = "hatchling.build"
|
||||||
|
|
||||||
|
[project]
|
||||||
|
name = "tf-sync"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "ToothFairyAI workspace sync layer for tfcode"
|
||||||
|
readme = "README.md"
|
||||||
|
requires-python = ">=3.10"
|
||||||
|
dependencies = [
|
||||||
|
"toothfairyai>=1.0.0",
|
||||||
|
"pydantic>=2.0.0",
|
||||||
|
"httpx>=0.25.0",
|
||||||
|
"rich>=13.0.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[project.optional-dependencies]
|
||||||
|
dev = [
|
||||||
|
"pytest>=7.0.0",
|
||||||
|
"pytest-asyncio>=0.21.0",
|
||||||
|
"mypy>=1.0.0",
|
||||||
|
"ruff>=0.1.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.hatch.build.targets.wheel]
|
||||||
|
packages = ["src/tf_sync"]
|
||||||
|
|
||||||
|
[tool.ruff]
|
||||||
|
line-length = 100
|
||||||
|
target-version = "py310"
|
||||||
|
|
||||||
|
[tool.mypy]
|
||||||
|
python_version = "3.10"
|
||||||
|
strict = true
|
||||||
21
packages/tf-sync/src/tf_sync/__init__.py
Normal file
21
packages/tf-sync/src/tf_sync/__init__.py
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
"""
|
||||||
|
tf-sync: ToothFairyAI workspace sync layer for tfcode
|
||||||
|
"""
|
||||||
|
|
||||||
|
from tf_sync.agents import sync_agents
|
||||||
|
from tf_sync.mcp import sync_mcp_servers
|
||||||
|
from tf_sync.tools import sync_tools, ToolType
|
||||||
|
from tf_sync.config import TFConfig, load_config, validate_credentials, get_region_urls
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"sync_agents",
|
||||||
|
"sync_mcp_servers",
|
||||||
|
"sync_tools",
|
||||||
|
"ToolType",
|
||||||
|
"TFConfig",
|
||||||
|
"load_config",
|
||||||
|
"validate_credentials",
|
||||||
|
"get_region_urls",
|
||||||
|
]
|
||||||
|
|
||||||
|
__version__ = "0.1.0"
|
||||||
39
packages/tf-sync/src/tf_sync/agents.py
Normal file
39
packages/tf-sync/src/tf_sync/agents.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
"""
|
||||||
|
Agent sync module for tfcode.
|
||||||
|
|
||||||
|
NOTE: This module is reserved for future implementation.
|
||||||
|
Currently, tfcode only syncs tools (MCP, Skills, Database, Functions).
|
||||||
|
Agent sync will be added in a later phase.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from tf_sync.config import TFConfig
|
||||||
|
|
||||||
|
|
||||||
|
class AgentSyncResult(BaseModel):
|
||||||
|
"""Result of agent sync operation."""
|
||||||
|
|
||||||
|
success: bool
|
||||||
|
agents: list[dict[str, Any]] = []
|
||||||
|
error: str | None = None
|
||||||
|
|
||||||
|
|
||||||
|
def sync_agents(config: TFConfig) -> AgentSyncResult:
|
||||||
|
"""
|
||||||
|
Sync agents from ToothFairyAI workspace.
|
||||||
|
|
||||||
|
NOTE: Currently not implemented. Reserved for future use.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
config: TFConfig instance
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
AgentSyncResult (currently always returns not implemented)
|
||||||
|
"""
|
||||||
|
return AgentSyncResult(
|
||||||
|
success=False,
|
||||||
|
error="Agent sync not yet implemented. Use tools sync for now.",
|
||||||
|
)
|
||||||
211
packages/tf-sync/src/tf_sync/config.py
Normal file
211
packages/tf-sync/src/tf_sync/config.py
Normal file
@ -0,0 +1,211 @@
|
|||||||
|
"""
|
||||||
|
Configuration management for tfcode ToothFairyAI integration.
|
||||||
|
Uses the official ToothFairyAI Python SDK for multi-region support.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
from enum import Enum
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from pydantic import BaseModel, Field, SecretStr
|
||||||
|
from toothfairyai import ToothFairyClient
|
||||||
|
from toothfairyai.errors import ToothFairyError
|
||||||
|
|
||||||
|
|
||||||
|
class Region(str, Enum):
|
||||||
|
AU = "au"
|
||||||
|
EU = "eu"
|
||||||
|
US = "us"
|
||||||
|
|
||||||
|
|
||||||
|
class ToolType(str, Enum):
|
||||||
|
MCP_SERVER = "mcp_server"
|
||||||
|
AGENT_SKILL = "agent_skill"
|
||||||
|
DATABASE_SCRIPT = "database_script"
|
||||||
|
API_FUNCTION = "api_function"
|
||||||
|
|
||||||
|
|
||||||
|
class FunctionRequestType(str, Enum):
|
||||||
|
GET = "get"
|
||||||
|
POST = "post"
|
||||||
|
PUT = "put"
|
||||||
|
DELETE = "delete"
|
||||||
|
PATCH = "patch"
|
||||||
|
CUSTOM = "custom"
|
||||||
|
GRAPHQL_QUERY = "graphql_query"
|
||||||
|
GRAPHQL_MUTATION = "graphql_mutation"
|
||||||
|
|
||||||
|
|
||||||
|
# Region-specific URL configurations
|
||||||
|
REGION_URLS = {
|
||||||
|
Region.AU: {
|
||||||
|
"base_url": "https://api.au.toothfairyai.com",
|
||||||
|
"ai_url": "https://ai.au.toothfairyai.com",
|
||||||
|
"ai_stream_url": "https://ais.au.toothfairyai.com",
|
||||||
|
"mcp_url": "https://mcp.au.toothfairyai.com/sse",
|
||||||
|
"mcp_proxy_url": "https://mcp-proxy.au.toothfairyai.com",
|
||||||
|
},
|
||||||
|
Region.EU: {
|
||||||
|
"base_url": "https://api.eu.toothfairyai.com",
|
||||||
|
"ai_url": "https://ai.eu.toothfairyai.com",
|
||||||
|
"ai_stream_url": "https://ais.eu.toothfairyai.com",
|
||||||
|
"mcp_url": "https://mcp.eu.toothfairyai.com/sse",
|
||||||
|
"mcp_proxy_url": "https://mcp-proxy.eu.toothfairyai.com",
|
||||||
|
},
|
||||||
|
Region.US: {
|
||||||
|
"base_url": "https://api.us.toothfairyai.com",
|
||||||
|
"ai_url": "https://ai.us.toothfairyai.com",
|
||||||
|
"ai_stream_url": "https://ais.us.toothfairyai.com",
|
||||||
|
"mcp_url": "https://mcp.us.toothfairyai.com/sse",
|
||||||
|
"mcp_proxy_url": "https://mcp-proxy.us.toothfairyai.com",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def get_region_urls(region: Region) -> dict[str, str]:
|
||||||
|
"""Get URLs for a specific region."""
|
||||||
|
return REGION_URLS.get(region, REGION_URLS[Region.AU])
|
||||||
|
|
||||||
|
|
||||||
|
class TFConfig(BaseModel):
|
||||||
|
"""ToothFairyAI workspace configuration."""
|
||||||
|
|
||||||
|
workspace_id: str
|
||||||
|
api_key: SecretStr
|
||||||
|
region: Region = Region.AU
|
||||||
|
enabled: bool = True
|
||||||
|
|
||||||
|
sync_interval: int = Field(default=3600, ge=60)
|
||||||
|
mcp_proxy_timeout: int = Field(default=30000, ge=1000)
|
||||||
|
|
||||||
|
_client: Optional[ToothFairyClient] = None
|
||||||
|
|
||||||
|
def get_client(self) -> ToothFairyClient:
|
||||||
|
"""
|
||||||
|
Get or create a ToothFairyClient instance configured for this region.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
ToothFairyClient configured with region-specific URLs
|
||||||
|
"""
|
||||||
|
if self._client is None:
|
||||||
|
urls = get_region_urls(self.region)
|
||||||
|
self._client = ToothFairyClient(
|
||||||
|
api_key=self.api_key.get_secret_value(),
|
||||||
|
workspace_id=self.workspace_id,
|
||||||
|
base_url=urls["base_url"],
|
||||||
|
ai_url=urls["ai_url"],
|
||||||
|
ai_stream_url=urls["ai_stream_url"],
|
||||||
|
)
|
||||||
|
return self._client
|
||||||
|
|
||||||
|
@property
|
||||||
|
def mcp_sse_url(self) -> str:
|
||||||
|
"""Get the MCP SSE endpoint URL for this region."""
|
||||||
|
return get_region_urls(self.region)["mcp_url"]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def mcp_proxy_url(self) -> str:
|
||||||
|
"""Get the MCP proxy URL for this region."""
|
||||||
|
return get_region_urls(self.region)["mcp_proxy_url"]
|
||||||
|
|
||||||
|
|
||||||
|
class CredentialValidationResult(BaseModel):
|
||||||
|
"""Result of credential validation."""
|
||||||
|
success: bool
|
||||||
|
workspace_id: Optional[str] = None
|
||||||
|
workspace_name: Optional[str] = None
|
||||||
|
error: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
def load_config(
|
||||||
|
workspace_id: Optional[str] = None,
|
||||||
|
api_key: Optional[str] = None,
|
||||||
|
region: Optional[Region] = None,
|
||||||
|
) -> TFConfig:
|
||||||
|
"""
|
||||||
|
Load ToothFairyAI configuration from environment or parameters.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
workspace_id: Workspace UUID (defaults to TF_WORKSPACE_ID env var)
|
||||||
|
api_key: API key (defaults to TF_API_KEY env var)
|
||||||
|
region: Region (defaults to TF_REGION env var or 'au')
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
TFConfig instance
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError: If required configuration is missing
|
||||||
|
"""
|
||||||
|
ws_id = workspace_id or os.environ.get("TF_WORKSPACE_ID")
|
||||||
|
key = api_key or os.environ.get("TF_API_KEY")
|
||||||
|
|
||||||
|
# Parse region from env or use provided/default
|
||||||
|
region_str = os.environ.get("TF_REGION", "au")
|
||||||
|
reg = region or Region(region_str)
|
||||||
|
|
||||||
|
if not ws_id:
|
||||||
|
raise ValueError("TF_WORKSPACE_ID not set. Set environment variable or pass workspace_id.")
|
||||||
|
if not key:
|
||||||
|
raise ValueError("TF_API_KEY not set. Set environment variable or pass api_key.")
|
||||||
|
|
||||||
|
return TFConfig(
|
||||||
|
workspace_id=ws_id,
|
||||||
|
api_key=SecretStr(key),
|
||||||
|
region=reg,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def validate_credentials(config: TFConfig) -> CredentialValidationResult:
|
||||||
|
"""
|
||||||
|
Validate ToothFairyAI credentials using the SDK.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
config: TFConfig instance
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
CredentialValidationResult indicating success or failure
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
client = config.get_client()
|
||||||
|
|
||||||
|
# Test connection by listing chats (lightweight operation)
|
||||||
|
if client.test_connection():
|
||||||
|
return CredentialValidationResult(
|
||||||
|
success=True,
|
||||||
|
workspace_id=config.workspace_id,
|
||||||
|
workspace_name="Connected",
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return CredentialValidationResult(
|
||||||
|
success=False,
|
||||||
|
error="Connection test failed. Check credentials and region.",
|
||||||
|
)
|
||||||
|
|
||||||
|
except ToothFairyError as e:
|
||||||
|
error_msg = str(e)
|
||||||
|
|
||||||
|
if "401" in error_msg or "Unauthorized" in error_msg:
|
||||||
|
return CredentialValidationResult(
|
||||||
|
success=False,
|
||||||
|
error="Invalid API key. Check TF_API_KEY environment variable.",
|
||||||
|
)
|
||||||
|
elif "403" in error_msg or "Forbidden" in error_msg:
|
||||||
|
return CredentialValidationResult(
|
||||||
|
success=False,
|
||||||
|
error="API access not allowed. Business or Enterprise subscription required.",
|
||||||
|
)
|
||||||
|
elif "404" in error_msg or "Not Found" in error_msg:
|
||||||
|
return CredentialValidationResult(
|
||||||
|
success=False,
|
||||||
|
error="Workspace not found. Check TF_WORKSPACE_ID environment variable.",
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return CredentialValidationResult(
|
||||||
|
success=False,
|
||||||
|
error=f"API error: {error_msg}",
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
return CredentialValidationResult(
|
||||||
|
success=False,
|
||||||
|
error=f"Unexpected error: {str(e)}",
|
||||||
|
)
|
||||||
54
packages/tf-sync/src/tf_sync/mcp.py
Normal file
54
packages/tf-sync/src/tf_sync/mcp.py
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
"""
|
||||||
|
MCP server sync module for tfcode.
|
||||||
|
Uses the official ToothFairyAI Python SDK.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from tf_sync.config import TFConfig
|
||||||
|
from tf_sync.tools import SyncedTool, sync_tools, ToolType
|
||||||
|
|
||||||
|
|
||||||
|
class MCPServerSyncResult(BaseModel):
|
||||||
|
"""Result of MCP server sync operation."""
|
||||||
|
|
||||||
|
success: bool
|
||||||
|
servers: list[SyncedTool] = []
|
||||||
|
error: str | None = None
|
||||||
|
|
||||||
|
|
||||||
|
def sync_mcp_servers(config: TFConfig) -> MCPServerSyncResult:
|
||||||
|
"""
|
||||||
|
Sync MCP servers from ToothFairyAI workspace.
|
||||||
|
|
||||||
|
MCP servers are tools with isMCPServer=true.
|
||||||
|
Credentials stay in TF and are accessed via tf_proxy.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
config: TFConfig instance
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
MCPServerSyncResult with synced MCP servers
|
||||||
|
"""
|
||||||
|
result = sync_tools_by_type(config, [ToolType.MCP_SERVER])
|
||||||
|
|
||||||
|
if not result.success:
|
||||||
|
return MCPServerSyncResult(
|
||||||
|
success=False,
|
||||||
|
error=result.error,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Get MCP servers from tools with isMCPServer
|
||||||
|
mcp_servers = [
|
||||||
|
t for t in result.tools
|
||||||
|
if t.is_mcp_server
|
||||||
|
]
|
||||||
|
|
||||||
|
return MCPServerSyncResult(
|
||||||
|
success=True,
|
||||||
|
servers=mcp_servers,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Import from tools module
|
||||||
|
from tf_sync.tools import sync_tools_by_type
|
||||||
185
packages/tf-sync/src/tf_sync/tools.py
Normal file
185
packages/tf-sync/src/tf_sync/tools.py
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
"""
|
||||||
|
Tool sync module for tfcode.
|
||||||
|
Syncs MCP servers, Agent Skills, Database Scripts, and API Functions from ToothFairyAI workspace.
|
||||||
|
Uses the official ToothFairyAI Python SDK.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import Any, Optional
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
from toothfairyai.types import AgentFunction
|
||||||
|
|
||||||
|
from tf_sync.config import TFConfig, ToolType, FunctionRequestType
|
||||||
|
|
||||||
|
|
||||||
|
class SyncedTool(BaseModel):
|
||||||
|
"""A tool synced from ToothFairyAI workspace."""
|
||||||
|
|
||||||
|
id: str
|
||||||
|
name: str
|
||||||
|
description: Optional[str] = None
|
||||||
|
tool_type: ToolType
|
||||||
|
|
||||||
|
is_mcp_server: bool = False
|
||||||
|
is_agent_skill: bool = False
|
||||||
|
is_database_script: bool = False
|
||||||
|
|
||||||
|
request_type: Optional[FunctionRequestType] = None
|
||||||
|
url: Optional[str] = None
|
||||||
|
tools: list[str] = []
|
||||||
|
|
||||||
|
auth_via: str = "tf_proxy"
|
||||||
|
|
||||||
|
|
||||||
|
class ToolSyncResult(BaseModel):
|
||||||
|
"""Result of tool sync operation."""
|
||||||
|
|
||||||
|
success: bool
|
||||||
|
tools: list[SyncedTool] = []
|
||||||
|
by_type: dict[str, int] = {}
|
||||||
|
error: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
|
def classify_tool(tool: AgentFunction) -> ToolType:
|
||||||
|
"""
|
||||||
|
Classify a tool based on its flags and fields.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
tool: AgentFunction from TF SDK
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
ToolType enum value
|
||||||
|
"""
|
||||||
|
if tool.is_mcp_server:
|
||||||
|
return ToolType.MCP_SERVER
|
||||||
|
if tool.is_agent_skill:
|
||||||
|
return ToolType.AGENT_SKILL
|
||||||
|
if tool.is_database_script:
|
||||||
|
return ToolType.DATABASE_SCRIPT
|
||||||
|
if tool.request_type:
|
||||||
|
return ToolType.API_FUNCTION
|
||||||
|
|
||||||
|
return ToolType.API_FUNCTION
|
||||||
|
|
||||||
|
|
||||||
|
def parse_tool(tool: AgentFunction) -> SyncedTool:
|
||||||
|
"""
|
||||||
|
Parse AgentFunction from SDK into SyncedTool.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
tool: AgentFunction from TF SDK
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
SyncedTool instance
|
||||||
|
"""
|
||||||
|
tool_type = classify_tool(tool)
|
||||||
|
|
||||||
|
request_type_enum = None
|
||||||
|
if tool.request_type:
|
||||||
|
try:
|
||||||
|
request_type_enum = FunctionRequestType(tool.request_type)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
auth_via = "user_provided" if tool_type == ToolType.API_FUNCTION else "tf_proxy"
|
||||||
|
|
||||||
|
return SyncedTool(
|
||||||
|
id=tool.id,
|
||||||
|
name=tool.name,
|
||||||
|
description=tool.description,
|
||||||
|
tool_type=tool_type,
|
||||||
|
is_mcp_server=tool.is_mcp_server or False,
|
||||||
|
is_agent_skill=tool.is_agent_skill or False,
|
||||||
|
is_database_script=tool.is_database_script or False,
|
||||||
|
request_type=request_type_enum,
|
||||||
|
url=tool.url,
|
||||||
|
tools=[],
|
||||||
|
auth_via=auth_via,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def sync_tools(config: TFConfig) -> ToolSyncResult:
|
||||||
|
"""
|
||||||
|
Sync all tools from ToothFairyAI workspace using SDK.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
config: TFConfig instance
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
ToolSyncResult with synced tools
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
client = config.get_client()
|
||||||
|
result = client.agent_functions.list()
|
||||||
|
|
||||||
|
tools = [parse_tool(f) for f in result.items]
|
||||||
|
|
||||||
|
by_type = {}
|
||||||
|
for tool in tools:
|
||||||
|
type_name = tool.tool_type.value
|
||||||
|
by_type[type_name] = by_type.get(type_name, 0) + 1
|
||||||
|
|
||||||
|
return ToolSyncResult(
|
||||||
|
success=True,
|
||||||
|
tools=tools,
|
||||||
|
by_type=by_type,
|
||||||
|
)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return ToolSyncResult(
|
||||||
|
success=False,
|
||||||
|
error=f"Sync failed: {str(e)}",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def sync_tools_by_type(
|
||||||
|
config: TFConfig,
|
||||||
|
tool_types: Optional[list[ToolType]] = None,
|
||||||
|
) -> ToolSyncResult:
|
||||||
|
"""
|
||||||
|
Sync tools of specific types from ToothFairyAI workspace.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
config: TFConfig instance
|
||||||
|
tool_types: List of ToolType to sync (None = all)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
ToolSyncResult with filtered tools
|
||||||
|
"""
|
||||||
|
result = sync_tools(config)
|
||||||
|
|
||||||
|
if not result.success or not tool_types:
|
||||||
|
return result
|
||||||
|
|
||||||
|
filtered = [t for t in result.tools if t.tool_type in tool_types]
|
||||||
|
|
||||||
|
by_type = {}
|
||||||
|
for tool in filtered:
|
||||||
|
type_name = tool.tool_type.value
|
||||||
|
by_type[type_name] = by_type.get(type_name, 0) + 1
|
||||||
|
|
||||||
|
return ToolSyncResult(
|
||||||
|
success=True,
|
||||||
|
tools=filtered,
|
||||||
|
by_type=by_type,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def sync_mcp_servers_only(config: TFConfig) -> ToolSyncResult:
|
||||||
|
"""Sync only MCP servers (isMCPServer=true)."""
|
||||||
|
return sync_tools_by_type(config, [ToolType.MCP_SERVER])
|
||||||
|
|
||||||
|
|
||||||
|
def sync_agent_skills_only(config: TFConfig) -> ToolSyncResult:
|
||||||
|
"""Sync only Agent Skills (isAgentSkill=true)."""
|
||||||
|
return sync_tools_by_type(config, [ToolType.AGENT_SKILL])
|
||||||
|
|
||||||
|
|
||||||
|
def sync_database_scripts_only(config: TFConfig) -> ToolSyncResult:
|
||||||
|
"""Sync only Database Scripts (isDatabaseScript=true)."""
|
||||||
|
return sync_tools_by_type(config, [ToolType.DATABASE_SCRIPT])
|
||||||
|
|
||||||
|
|
||||||
|
def sync_api_functions_only(config: TFConfig) -> ToolSyncResult:
|
||||||
|
"""Sync only API Functions (has requestType)."""
|
||||||
|
return sync_tools_by_type(config, [ToolType.API_FUNCTION])
|
||||||
119
schemas/tfcode.schema.json
Normal file
119
schemas/tfcode.schema.json
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://toothfairyai.com/schemas/tfcode.json",
|
||||||
|
"$defs": {
|
||||||
|
"ToolType": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["mcp_server", "agent_skill", "database_script", "api_function"]
|
||||||
|
},
|
||||||
|
"FunctionRequestType": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["get", "post", "put", "delete", "patch", "custom", "graphql_query", "graphql_mutation"]
|
||||||
|
},
|
||||||
|
"AuthVia": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["tf_proxy", "user_provided"]
|
||||||
|
},
|
||||||
|
"Tool": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"type": { "$ref": "#/$defs/ToolType" },
|
||||||
|
"isMCPServer": { "type": "boolean" },
|
||||||
|
"isAgentSkill": { "type": "boolean" },
|
||||||
|
"isDatabaseScript": { "type": "boolean" },
|
||||||
|
"requestType": { "$ref": "#/$defs/FunctionRequestType" },
|
||||||
|
"url": { "type": "string" },
|
||||||
|
"auth_via": { "$ref": "#/$defs/AuthVia" },
|
||||||
|
"api_key": { "type": "string" },
|
||||||
|
"tools": {
|
||||||
|
"type": "array",
|
||||||
|
"items": { "type": "string" }
|
||||||
|
},
|
||||||
|
"description": { "type": "string" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"$schema": { "type": "string" },
|
||||||
|
"toothfairy": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"enabled": { "type": "boolean", "default": true },
|
||||||
|
"workspace_id": { "type": "string" },
|
||||||
|
"api_key": { "type": "string" },
|
||||||
|
"region": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["au", "eu", "us"],
|
||||||
|
"default": "au"
|
||||||
|
},
|
||||||
|
"sync": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"on_startup": { "type": "boolean", "default": true },
|
||||||
|
"interval": { "type": "integer", "default": 3600 },
|
||||||
|
"tools": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"types": {
|
||||||
|
"type": "array",
|
||||||
|
"items": { "type": "string", "enum": ["mcp", "skill", "database", "function"] }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mcp_proxy": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"url": { "type": "string" },
|
||||||
|
"timeout": { "type": "integer", "default": 30000 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["workspace_id", "api_key"]
|
||||||
|
},
|
||||||
|
"tools": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": { "$ref": "#/$defs/Tool" }
|
||||||
|
},
|
||||||
|
"agent": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"mode": { "type": "string", "enum": ["primary", "subagent"] },
|
||||||
|
"model": { "type": "string" },
|
||||||
|
"temperature": { "type": "number" },
|
||||||
|
"description": { "type": "string" },
|
||||||
|
"prompt": { "type": "string" },
|
||||||
|
"tools": {
|
||||||
|
"type": "array",
|
||||||
|
"items": { "type": "string" }
|
||||||
|
},
|
||||||
|
"mcp": {
|
||||||
|
"type": "array",
|
||||||
|
"items": { "type": "string" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"model": { "type": "string" },
|
||||||
|
"autoupdate": { "type": "boolean" },
|
||||||
|
"formatter": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"command": {
|
||||||
|
"type": "array",
|
||||||
|
"items": { "type": "string" }
|
||||||
|
},
|
||||||
|
"extensions": {
|
||||||
|
"type": "array",
|
||||||
|
"items": { "type": "string" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
196
scripts/rebrand.sh
Executable file
196
scripts/rebrand.sh
Executable file
@ -0,0 +1,196 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# tfcode Rebrand Script
|
||||||
|
# Reapplies all branding changes after upstream merge
|
||||||
|
# Run this after merging from opencode mirror
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "🔧 Applying tfcode branding..."
|
||||||
|
|
||||||
|
# Colors for output
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# Track changes
|
||||||
|
CHANGES=0
|
||||||
|
|
||||||
|
# Function to count changes
|
||||||
|
count_changes() {
|
||||||
|
CHANGES=$((CHANGES + 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to sed with both extensions (GNU and BSD)
|
||||||
|
safe_sed() {
|
||||||
|
local pattern="$1"
|
||||||
|
local file="$2"
|
||||||
|
|
||||||
|
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||||
|
# macOS BSD sed
|
||||||
|
sed -i '' "$pattern" "$file" 2>/dev/null || true
|
||||||
|
else
|
||||||
|
# GNU sed
|
||||||
|
sed -i "$pattern" "$file" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
count_changes
|
||||||
|
}
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "📁 Processing TypeScript/JavaScript files..."
|
||||||
|
|
||||||
|
# TypeScript and JavaScript files
|
||||||
|
find . -type f \( -name "*.ts" -o -name "*.tsx" -o -name "*.js" -o -name "*.jsx" -o -name "*.mjs" -o -name "*.cjs" \) \
|
||||||
|
-not -path "*/node_modules/*" \
|
||||||
|
-not -path "*/.git/*" \
|
||||||
|
-not -path "*/dist/*" \
|
||||||
|
-not -path "*/build/*" | while read file; do
|
||||||
|
|
||||||
|
# Command name: opencode -> tfcode
|
||||||
|
safe_sed 's/opencode/tfcode/g' "$file"
|
||||||
|
|
||||||
|
# Package name: opencode-ai -> tfcode
|
||||||
|
safe_sed 's/opencode-ai/tfcode/g' "$file"
|
||||||
|
|
||||||
|
# Env vars: OPENCODE_ -> TFCODE_
|
||||||
|
safe_sed 's/OPENCODE_/TFCODE_/g' "$file"
|
||||||
|
|
||||||
|
# URLs: opencode.ai -> toothfairyai.com
|
||||||
|
safe_sed 's/opencode\.ai/toothfairyai.com/g' "$file"
|
||||||
|
|
||||||
|
# Config directory: .opencode -> .tfcode
|
||||||
|
safe_sed 's/\.opencode/.tfcode/g' "$file"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "📁 Processing JSON files..."
|
||||||
|
|
||||||
|
# JSON files
|
||||||
|
find . -type f -name "*.json" \
|
||||||
|
-not -path "*/node_modules/*" \
|
||||||
|
-not -path "*/.git/*" \
|
||||||
|
-not -path "*/dist/*" \
|
||||||
|
-not -path "*/build/*" \
|
||||||
|
-not -name "package-lock.json" | while read file; do
|
||||||
|
|
||||||
|
safe_sed 's/opencode/tfcode/g' "$file"
|
||||||
|
safe_sed 's/opencode-ai/tfcode/g' "$file"
|
||||||
|
safe_sed 's/OPENCODE_/TFCODE_/g' "$file"
|
||||||
|
safe_sed 's/opencode\.ai/toothfairyai.com/g' "$file"
|
||||||
|
safe_sed 's/\.opencode/.tfcode/g' "$file"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "📁 Processing Markdown files..."
|
||||||
|
|
||||||
|
# Markdown files
|
||||||
|
find . -type f -name "*.md" \
|
||||||
|
-not -path "*/node_modules/*" \
|
||||||
|
-not -path "*/.git/*" | while read file; do
|
||||||
|
|
||||||
|
safe_sed 's/opencode/tfcode/g' "$file"
|
||||||
|
safe_sed 's/opencode-ai/tfcode/g' "$file"
|
||||||
|
safe_sed 's/OPENCODE_/TFCODE_/g' "$file"
|
||||||
|
safe_sed 's/opencode\.ai/toothfairyai.com/g' "$file"
|
||||||
|
safe_sed 's/\.opencode/.tfcode/g' "$file"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "📁 Processing Shell scripts..."
|
||||||
|
|
||||||
|
# Shell scripts
|
||||||
|
find . -type f \( -name "*.sh" -o -name "*.bash" \) \
|
||||||
|
-not -path "*/.git/*" | while read file; do
|
||||||
|
|
||||||
|
safe_sed 's/opencode/tfcode/g' "$file"
|
||||||
|
safe_sed 's/opencode-ai/tfcode/g' "$file"
|
||||||
|
safe_sed 's/OPENCODE_/TFCODE_/g' "$file"
|
||||||
|
safe_sed 's/opencode\.ai/toothfairyai.com/g' "$file"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "📁 Processing Python files..."
|
||||||
|
|
||||||
|
# Python files
|
||||||
|
find . -type f -name "*.py" \
|
||||||
|
-not -path "*/.git/*" \
|
||||||
|
-not -path "*/__pycache__/*" \
|
||||||
|
-not -path "*/.venv/*" \
|
||||||
|
-not -path "*/venv/*" | while read file; do
|
||||||
|
|
||||||
|
safe_sed 's/opencode/tfcode/g' "$file"
|
||||||
|
safe_sed 's/opencode-ai/tfcode/g' "$file"
|
||||||
|
safe_sed 's/OPENCODE_/TFCODE_/g' "$file"
|
||||||
|
safe_sed 's/opencode\.ai/toothfairyai.com/g' "$file"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "📁 Processing YAML files..."
|
||||||
|
|
||||||
|
# YAML files
|
||||||
|
find . -type f \( -name "*.yml" -o -name "*.yaml" \) \
|
||||||
|
-not -path "*/node_modules/*" \
|
||||||
|
-not -path "*/.git/*" | while read file; do
|
||||||
|
|
||||||
|
safe_sed 's/opencode/tfcode/g' "$file"
|
||||||
|
safe_sed 's/opencode-ai/tfcode/g' "$file"
|
||||||
|
safe_sed 's/OPENCODE_/TFCODE_/g' "$file"
|
||||||
|
safe_sed 's/opencode\.ai/toothfairyai.com/g' "$file"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "📁 Processing config files..."
|
||||||
|
|
||||||
|
# Config files (no extension or specific names)
|
||||||
|
find . -type f \( \
|
||||||
|
-name "Makefile" -o \
|
||||||
|
-name "Dockerfile*" -o \
|
||||||
|
-name ".env*" -o \
|
||||||
|
-name "LICENSE" -o \
|
||||||
|
-name "AUTHORS" -o \
|
||||||
|
-name "CONTRIBUTORS" \
|
||||||
|
\) -not -path "*/.git/*" | while read file; do
|
||||||
|
|
||||||
|
safe_sed 's/opencode/tfcode/g' "$file"
|
||||||
|
safe_sed 's/opencode-ai/tfcode/g' "$file"
|
||||||
|
safe_sed 's/OPENCODE_/TFCODE_/g' "$file"
|
||||||
|
safe_sed 's/opencode\.ai/toothfairyai.com/g' "$file"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "🔄 Renaming config files..."
|
||||||
|
|
||||||
|
# Rename opencode.json to tfcode.json if it exists
|
||||||
|
if [ -f "opencode.json" ]; then
|
||||||
|
if [ ! -f "tfcode.json" ]; then
|
||||||
|
mv opencode.json tfcode.json
|
||||||
|
echo " ✓ Renamed opencode.json -> tfcode.json"
|
||||||
|
else
|
||||||
|
echo " ⚠ tfcode.json already exists, keeping both files"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Rename .opencode directory to .tfcode if it exists
|
||||||
|
if [ -d ".opencode" ]; then
|
||||||
|
if [ ! -d ".tfcode" ]; then
|
||||||
|
mv .opencode .tfcode
|
||||||
|
echo " ✓ Renamed .opencode/ -> .tfcode/"
|
||||||
|
else
|
||||||
|
echo " ⚠ .tfcode/ already exists, merging directories"
|
||||||
|
cp -r .opencode/* .tfcode/ 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "${GREEN}✅ Rebranding complete!${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Summary:"
|
||||||
|
echo " - Processed files and applied branding changes"
|
||||||
|
echo " - Renamed config files where applicable"
|
||||||
|
echo ""
|
||||||
|
echo "${YELLOW}Next steps:${NC}"
|
||||||
|
echo " 1. Review changes: git diff"
|
||||||
|
echo " 2. Run tests: npm test (or equivalent)"
|
||||||
|
echo " 3. Commit changes: git add . && git commit -m 'chore: reapply tfcode branding'"
|
||||||
|
echo ""
|
||||||
28
tfcode.json
Normal file
28
tfcode.json
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"$schema": "./schemas/tfcode.schema.json",
|
||||||
|
"toothfairy": {
|
||||||
|
"workspace_id": "{env:TF_WORKSPACE_ID}",
|
||||||
|
"api_key": "{env:TF_API_KEY}",
|
||||||
|
"region": "au",
|
||||||
|
"sync": {
|
||||||
|
"on_startup": true,
|
||||||
|
"interval": 3600,
|
||||||
|
"tools": {
|
||||||
|
"types": ["mcp", "skill", "database", "function"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mcp_proxy": {
|
||||||
|
"url": "https://mcp-proxy.{region}.toothfairyai.com",
|
||||||
|
"timeout": 30000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tools": {},
|
||||||
|
"agent": {
|
||||||
|
"build": {
|
||||||
|
"mode": "primary",
|
||||||
|
"description": "Default coding agent",
|
||||||
|
"mcp": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoupdate": true
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user