Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

<p align="center">
<strong>A multi-platform AI coding framework that rules</strong><br/>
<sub>Supports Claude Code, Cursor, OpenCode, iFlow, Codex, Kilo, Kiro, Gemini CLI, Antigravity, Qoder, and CodeBuddy.</sub>
<sub>Supports Claude Code, Cursor, OpenCode, iFlow, Codex, Kilo, Kiro, Gemini CLI, Antigravity, Windsurf, Qoder, and CodeBuddy.</sub>
</p>

<p align="center">
Expand Down Expand Up @@ -45,7 +45,7 @@
| **Parallel agent execution** | Run multiple AI tasks side by side with git worktrees instead of turning one branch into a traffic jam. |
| **Project memory** | Journals in `.trellis/workspace/` preserve what happened last time, so each new session starts with real context. |
| **Team-shared standards** | Specs live in the repo, so one person’s hard-won workflow or rule can benefit the whole team. |
| **Multi-platform setup** | Bring the same Trellis structure to 10 AI coding platforms instead of rebuilding your workflow per tool. |
| **Multi-platform setup** | Bring the same Trellis structure to 11 AI coding platforms instead of rebuilding your workflow per tool. |

## Quick Start

Expand All @@ -61,7 +61,7 @@ trellis init --cursor --opencode --codex -u your-name
```

- `-u your-name` creates `.trellis/workspace/your-name/` for personal journals and session continuity.
- Platform flags can be mixed and matched. Current options include `--cursor`, `--opencode`, `--iflow`, `--codex`, `--kilo`, `--kiro`, `--gemini`, `--antigravity`, `--qoder`, and `--codebuddy`.
- Platform flags can be mixed and matched. Current options include `--cursor`, `--opencode`, `--iflow`, `--codex`, `--kilo`, `--kiro`, `--gemini`, `--antigravity`, `--windsurf`, `--qoder`, and `--codebuddy`.
- For platform-specific setup, entry commands, and upgrade paths, use the docs:
[Quick Start](https://docs.trytrellis.app/guide/ch02-quick-start) •
[Supported Platforms](https://docs.trytrellis.app/guide/ch13-multi-platform) •
Expand Down Expand Up @@ -138,7 +138,7 @@ Those files are useful, but they tend to become monolithic. Trellis adds structu
<details>
<summary><strong>Is Trellis only for Claude Code?</strong></summary>

No. Trellis currently supports Claude Code, Cursor, OpenCode, iFlow, Codex, Kilo, Kiro, Gemini CLI, and Antigravity. The detailed setup and entry command for each tool lives in the supported platforms guide.
No. Trellis currently supports Claude Code, Cursor, OpenCode, iFlow, Codex, Kilo, Kiro, Gemini CLI, Antigravity, and Windsurf. The detailed setup and entry command for each tool lives in the supported platforms guide.

</details>

Expand Down
8 changes: 4 additions & 4 deletions README_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

<p align="center">
<strong>给 AI 立规矩的开源框架</strong><br/>
<sub>支持 Claude Code、Cursor、OpenCode、iFlow、Codex、Kilo、Kiro、Gemini CLI、Antigravity、Qoder 和 CodeBuddy。</sub>
<sub>支持 Claude Code、Cursor、OpenCode、iFlow、Codex、Kilo、Kiro、Gemini CLI、Antigravity、Windsurf、Qoder 和 CodeBuddy。</sub>
</p>

<p align="center">
Expand Down Expand Up @@ -50,7 +50,7 @@
| **并行 Agent 执行** | 用 git worktree 同时推进多个 AI 任务,不需要把一个分支挤成大杂烩。 |
| **项目记忆** | `.trellis/workspace/` 里的 journal 会保留上一次工作的脉络,让新会话不是从空白开始。 |
| **团队共享标准** | Spec 跟着仓库一起版本化,一个人总结出来的规则和流程,可以直接变成整个团队的基础设施。 |
| **多平台复用** | 同一套 Trellis 结构可以带到 10 个 AI coding 平台上,而不是每换一个工具就重搭一次工作流。 |
| **多平台复用** | 同一套 Trellis 结构可以带到 11 个 AI coding 平台上,而不是每换一个工具就重搭一次工作流。 |

## 快速开始

Expand All @@ -66,7 +66,7 @@ trellis init --cursor --opencode --codex -u your-name
```

- `-u your-name` 会创建 `.trellis/workspace/your-name/`,用来保存个人 journal 和会话连续性。
- 平台参数可以自由组合。当前可选项包括 `--cursor`、`--opencode`、`--iflow`、`--codex`、`--kilo`、`--kiro`、`--gemini`、`--antigravity`、`--qoder` 和 `--codebuddy`。
- 平台参数可以自由组合。当前可选项包括 `--cursor`、`--opencode`、`--iflow`、`--codex`、`--kilo`、`--kiro`、`--gemini`、`--antigravity`、`--windsurf`、`--qoder` 和 `--codebuddy`。
- 更完整的安装步骤、各平台入口命令和升级方式放在文档站:
[快速开始](https://docs.trytrellis.app/zh/guide/ch02-quick-start) •
[支持平台](https://docs.trytrellis.app/zh/guide/ch13-multi-platform) •
Expand Down Expand Up @@ -143,7 +143,7 @@ trellis init --registry https://github.com/your-org/your-spec-templates
<details>
<summary><strong>Trellis 只适合 Claude Code 吗?</strong></summary>

不是。Trellis 目前支持 Claude Code、Cursor、OpenCode、iFlow、Codex、Kilo、Kiro、Gemini CLI 和 Antigravity。每个平台的具体接入方式和入口命令,文档站都有单独说明。
不是。Trellis 目前支持 Claude Code、Cursor、OpenCode、iFlow、Codex、Kilo、Kiro、Gemini CLI、AntigravityWindsurf。每个平台的具体接入方式和入口命令,文档站都有单独说明。

</details>

Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ program
.option("--kiro", "Include Kiro Code skills")
.option("--gemini", "Include Gemini CLI commands")
.option("--antigravity", "Include Antigravity workflows")
.option("--windsurf", "Include Windsurf workflows")
.option("--qoder", "Include Qoder commands")
.option("--codebuddy", "Include CodeBuddy commands")
.option("-y, --yes", "Skip prompts and use defaults")
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/commands/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,7 @@ interface InitOptions {
kiro?: boolean;
gemini?: boolean;
antigravity?: boolean;
windsurf?: boolean;
qoder?: boolean;
codebuddy?: boolean;
yes?: boolean;
Expand Down
12 changes: 12 additions & 0 deletions packages/cli/src/configurators/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { configureKilo } from "./kilo.js";
import { configureKiro } from "./kiro.js";
import { configureGemini } from "./gemini.js";
import { configureAntigravity } from "./antigravity.js";
import { configureWindsurf } from "./windsurf.js";
import { configureQoder } from "./qoder.js";
import { configureCodebuddy } from "./codebuddy.js";

Expand Down Expand Up @@ -59,6 +60,7 @@ import { getAllWorkflows as getKiloWorkflows } from "../templates/kilo/index.js"
import { getAllSkills as getKiroSkills } from "../templates/kiro/index.js";
import { getAllCommands as getGeminiCommands } from "../templates/gemini/index.js";
import { getAllWorkflows as getAntigravityWorkflows } from "../templates/antigravity/index.js";
import { getAllWorkflows as getAllWindsurfWorkflows } from "../templates/windsurf/index.js";
import { getAllSkills as getQoderSkills } from "../templates/qoder/index.js";
import { getAllCommands as getCodebuddyCommands } from "../templates/codebuddy/index.js";

Expand Down Expand Up @@ -208,6 +210,16 @@ const PLATFORM_FUNCTIONS: Record<AITool, PlatformFunctions> = {
return files;
},
},
windsurf: {
configure: configureWindsurf,
collectTemplates: () => {
const files = new Map<string, string>();
for (const workflow of getAllWindsurfWorkflows()) {
files.set(`.windsurf/workflows/${workflow.name}.md`, workflow.content);
}
return files;
},
},
qoder: {
configure: configureQoder,
collectTemplates: () => {
Expand Down
19 changes: 19 additions & 0 deletions packages/cli/src/configurators/windsurf.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import path from "node:path";
import { getAllWorkflows } from "../templates/windsurf/index.js";
import { ensureDir, writeFile } from "../utils/file-writer.js";

/**
* Configure Windsurf by writing workflow templates.
*
* Output:
* - .windsurf/workflows/<workflow-name>.md
*/
export async function configureWindsurf(cwd: string): Promise<void> {
const workflowRoot = path.join(cwd, ".windsurf", "workflows");
ensureDir(workflowRoot);

for (const workflow of getAllWorkflows()) {
const targetPath = path.join(workflowRoot, `${workflow.name}.md`);
await writeFile(targetPath, workflow.content);
}
}
24 changes: 24 additions & 0 deletions packages/cli/src/templates/extract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,30 @@ export function getAntigravitySourcePath(): string {
return getAntigravityTemplatePath();
}

/**
* Get the path to the windsurf templates directory.
*
* This reads from src/templates/windsurf/ (development) or dist/templates/windsurf/ (production).
* These are GENERIC templates, not the Trellis project's own .windsurf/workflows configuration.
*/
export function getWindsurfTemplatePath(): string {
const templatePath = path.join(__dirname, "windsurf");
if (fs.existsSync(templatePath)) {
return templatePath;
}

throw new Error(
"Could not find windsurf templates directory. Expected at templates/windsurf/",
);
}

/**
* @deprecated Use getWindsurfTemplatePath() instead.
*/
export function getWindsurfSourcePath(): string {
return getWindsurfTemplatePath();
}

/**
* Get the path to the qoder templates directory.
*
Expand Down
59 changes: 50 additions & 9 deletions packages/cli/src/templates/trellis/scripts/common/cli_adapter.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""
CLI Adapter for Multi-Platform Support.

Abstracts differences between Claude Code, OpenCode, Cursor, iFlow, Codex, Kilo, Kiro Code, Gemini CLI, Antigravity, Qoder, and CodeBuddy interfaces.
Abstracts differences between Claude Code, OpenCode, Cursor, iFlow, Codex, Kilo, Kiro Code, Gemini CLI, Antigravity, Windsurf, Qoder, and CodeBuddy interfaces.

Supported platforms:
- claude: Claude Code (default)
Expand All @@ -13,6 +13,7 @@
- kiro: Kiro Code (skills-based)
- gemini: Gemini CLI
- antigravity: Antigravity (workflow-based)
- windsurf: Windsurf (workflow-based)
- qoder: Qoder
- codebuddy: CodeBuddy

Expand Down Expand Up @@ -43,6 +44,7 @@
"kiro",
"gemini",
"antigravity",
"windsurf",
"qoder",
"codebuddy",
]
Expand Down Expand Up @@ -89,7 +91,7 @@ def config_dir_name(self) -> str:
"""Get platform-specific config directory name.

Returns:
Directory name ('.claude', '.opencode', '.cursor', '.iflow', '.codex', '.kilocode', '.kiro', '.gemini', '.agent', '.qoder', or '.codebuddy')
Directory name ('.claude', '.opencode', '.cursor', '.iflow', '.codex', '.kilocode', '.kiro', '.gemini', '.agent', '.windsurf', '.qoder', or '.codebuddy')
"""
if self.platform == "opencode":
return ".opencode"
Expand All @@ -107,6 +109,8 @@ def config_dir_name(self) -> str:
return ".gemini"
elif self.platform == "antigravity":
return ".agent"
elif self.platform == "windsurf":
return ".windsurf"
elif self.platform == "qoder":
return ".qoder"
elif self.platform == "codebuddy":
Expand All @@ -121,7 +125,7 @@ def get_config_dir(self, project_root: Path) -> Path:
project_root: Project root directory

Returns:
Path to config directory (.claude, .opencode, .cursor, .iflow, .codex, .kilocode, .kiro, .gemini, .agent, .qoder, or .codebuddy)
Path to config directory (.claude, .opencode, .cursor, .iflow, .codex, .kilocode, .kiro, .gemini, .agent, .windsurf, .qoder, or .codebuddy)
"""
return project_root / self.config_dir_name

Expand Down Expand Up @@ -153,8 +157,18 @@ def get_commands_path(self, project_root: Path, *parts: str) -> Path:
Note:
Cursor uses prefix naming: .cursor/commands/trellis-<name>.md
Antigravity uses workflow directory: .agent/workflows/<name>.md
Windsurf uses workflow directory: .windsurf/workflows/trellis-<name>.md
Claude/OpenCode use subdirectory: .claude/commands/trellis/<name>.md
"""
if self.platform == "windsurf":
workflow_dir = self.get_config_dir(project_root) / "workflows"
if not parts:
return workflow_dir
if len(parts) >= 2 and parts[0] == "trellis":
filename = parts[-1]
return workflow_dir / f"trellis-{filename}"
return workflow_dir / Path(*parts)

if self.platform in ("antigravity", "kilo"):
workflow_dir = self.get_config_dir(project_root) / "workflows"
if not parts:
Expand Down Expand Up @@ -192,6 +206,7 @@ def get_trellis_command_path(self, name: str) -> str:
Kiro: .kiro/skills/<name>/SKILL.md
Gemini: .gemini/commands/trellis/<name>.toml
Antigravity: .agent/workflows/<name>.md
Windsurf: .windsurf/workflows/trellis-<name>.md
Others: .{platform}/commands/trellis/<name>.md
"""
if self.platform == "cursor":
Expand All @@ -204,6 +219,8 @@ def get_trellis_command_path(self, name: str) -> str:
return f".gemini/commands/trellis/{name}.toml"
elif self.platform == "antigravity":
return f".agent/workflows/{name}.md"
elif self.platform == "windsurf":
return f".windsurf/workflows/trellis-{name}.md"
elif self.platform == "kilo":
return f".kilocode/workflows/{name}.md"
else:
Expand Down Expand Up @@ -231,6 +248,8 @@ def get_non_interactive_env(self) -> dict[str, str]:
return {} # Gemini CLI doesn't have a non-interactive env var
elif self.platform == "antigravity":
return {}
elif self.platform == "windsurf":
return {}
elif self.platform == "qoder":
return {}
elif self.platform == "codebuddy":
Expand Down Expand Up @@ -300,6 +319,10 @@ def build_run_command(
raise ValueError(
"Antigravity workflows are UI slash commands; CLI agent run is not supported."
)
elif self.platform == "windsurf":
raise ValueError(
"Windsurf workflows are UI slash commands; CLI agent run is not supported."
)
elif self.platform == "qoder":
cmd = ["qodercli", "-p", prompt]
elif self.platform == "codebuddy":
Expand Down Expand Up @@ -352,6 +375,10 @@ def build_resume_command(self, session_id: str) -> list[str]:
raise ValueError(
"Antigravity workflows are UI slash commands; CLI resume is not supported."
)
elif self.platform == "windsurf":
raise ValueError(
"Windsurf workflows are UI slash commands; CLI resume is not supported."
)
elif self.platform == "qoder":
return ["qodercli", "--resume", session_id]
elif self.platform == "codebuddy":
Expand Down Expand Up @@ -420,6 +447,8 @@ def cli_name(self) -> str:
return "gemini"
elif self.platform == "antigravity":
return "agy"
elif self.platform == "windsurf":
return "windsurf"
elif self.platform == "qoder":
return "qodercli"
elif self.platform == "codebuddy":
Expand Down Expand Up @@ -488,7 +517,7 @@ def get_cli_adapter(platform: str = "claude") -> CLIAdapter:
"""Get CLI adapter for the specified platform.

Args:
platform: Platform name ('claude', 'opencode', 'cursor', 'iflow', 'codex', 'kilo', 'kiro', 'gemini', 'antigravity', 'qoder', or 'codebuddy')
platform: Platform name ('claude', 'opencode', 'cursor', 'iflow', 'codex', 'kilo', 'kiro', 'gemini', 'antigravity', 'windsurf', 'qoder', or 'codebuddy')

Returns:
CLIAdapter instance
Expand All @@ -506,11 +535,12 @@ def get_cli_adapter(platform: str = "claude") -> CLIAdapter:
"kiro",
"gemini",
"antigravity",
"windsurf",
"qoder",
"codebuddy",
):
raise ValueError(
f"Unsupported platform: {platform} (must be 'claude', 'opencode', 'cursor', 'iflow', 'codex', 'kilo', 'kiro', 'gemini', 'antigravity', 'qoder', or 'codebuddy')"
f"Unsupported platform: {platform} (must be 'claude', 'opencode', 'cursor', 'iflow', 'codex', 'kilo', 'kiro', 'gemini', 'antigravity', 'windsurf', 'qoder', or 'codebuddy')"
)

return CLIAdapter(platform=platform) # type: ignore
Expand All @@ -527,6 +557,7 @@ def get_cli_adapter(platform: str = "claude") -> CLIAdapter:
".kiro",
".gemini",
".agent",
".windsurf",
".qoder",
".codebuddy",
)
Expand Down Expand Up @@ -555,15 +586,16 @@ def detect_platform(project_root: Path) -> Platform:
7. .kiro/skills exists and no other platform dirs → kiro
8. .gemini directory exists → gemini
9. .agent/workflows exists and no other platform dirs → antigravity
10. .codebuddy directory exists → codebuddy
11. .qoder directory exists → qoder
12. Default → claude
10. .windsurf/workflows exists and no other platform dirs → windsurf
11. .codebuddy directory exists → codebuddy
12. .qoder directory exists → qoder
13. Default → claude

Args:
project_root: Project root directory

Returns:
Detected platform ('claude', 'opencode', 'cursor', 'iflow', 'codex', 'kilo', 'kiro', 'gemini', 'antigravity', 'qoder', 'codebuddy', or default 'claude')
Detected platform ('claude', 'opencode', 'cursor', 'iflow', 'codex', 'kilo', 'kiro', 'gemini', 'antigravity', 'windsurf', 'qoder', 'codebuddy', or default 'claude')
"""
import os

Expand All @@ -579,6 +611,7 @@ def detect_platform(project_root: Path) -> Platform:
"kiro",
"gemini",
"antigravity",
"windsurf",
"qoder",
"codebuddy",
):
Expand Down Expand Up @@ -626,6 +659,14 @@ def detect_platform(project_root: Path) -> Platform:
):
return "antigravity"

# Check for Windsurf workflow directory only when no other platform config exists
if (
project_root / ".windsurf" / "workflows"
).is_dir() and not _has_other_platform_dir(
project_root, {".windsurf"}
):
return "windsurf"

# Check for .codebuddy directory (CodeBuddy-specific)
if (project_root / ".codebuddy").is_dir():
return "codebuddy"
Expand Down
Loading