项目文件夹

文件
cloudQuant 4eb891f88f fix(kiro): 修复 Kiro 智能体格式为 .md + YAML frontmatter (#65)
Kiro 自定义子智能体要求 ~/.kiro/agents/<name>.md 格式(带 YAML
frontmatter),而非之前的 .json + prompts/ 分离结构。

修复内容:
- convert.sh/ps1: convert_kiro 直接生成 .md 文件
- install.sh/ps1: install_kiro 复制 .md 文件而非 .json

参考:https://kiro.dev/docs/chat/subagents/
2026-06-01 21:32:56 +08:00

456 行
15 KiB
PowerShell

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
# convert.ps1 — 将智能体 .md 文件转换为各工具专用格式(Windows 版)
#
# 读取所有智能体目录中的 .md 文件,输出到 integrations/<tool>/。
# 添加或修改智能体后运行此脚本重新生成集成文件。
#
# 用法:
# .\scripts\convert.ps1 [-Tool <名称>] [-Out <目录>] [-Help]
#
# 支持的工具:
# antigravity — Antigravity skill 文件
# gemini-cli — Gemini CLI 扩展
# opencode — OpenCode agent 文件
# cursor — Cursor rule 文件
# trae — Trae rule 文件
# aider — 单文件 CONVENTIONS.md
# windsurf — 单文件 .windsurfrules
# openclaw — OpenClaw SOUL.md 文件
# qwen — Qwen Code SubAgent 文件
# codex — OpenAI Codex CLI agent 文件
# deerflow — DeerFlow 2.0 custom skill 文件
# workbuddy — WorkBuddy skill 文件
# hermes — Hermes Agent skill 文件
# kiro — Kiro agent .md 文件(带 YAML frontmatter
# all — 所有工具(默认)
param(
[string]$Tool = "all",
[string]$Out = "",
[switch]$Help
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
# --- 路径 ---
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$RepoRoot = Split-Path -Parent $ScriptDir
$OutDir = if ($Out) { $Out } else { Join-Path $RepoRoot "integrations" }
$Today = Get-Date -Format "yyyy-MM-dd"
$AgentDirs = @(
"academic","design","engineering","finance","game-development","hr","legal",
"marketing","paid-media","sales","product","project-management",
"supply-chain","testing","support","spatial-computing","specialized"
)
$ValidTools = @("antigravity","gemini-cli","opencode","cursor","trae","aider",
"windsurf","openclaw","qwen","codex","deerflow","workbuddy","hermes","kiro","all")
# --- 颜色输出 ---
function Write-OK { param($msg) Write-Host "[OK] $msg" -ForegroundColor Green }
function Write-Warn { param($msg) Write-Host "[!!] $msg" -ForegroundColor Yellow }
function Write-Err { param($msg) Write-Host "[ERR] $msg" -ForegroundColor Red }
function Write-Header { param($msg) Write-Host "`n$msg" -ForegroundColor White }
# --- 用法 ---
if ($Help) {
Get-Content $MyInvocation.MyCommand.Path | Select-Object -Skip 2 -First 20 |
ForEach-Object { $_ -replace '^# ?','' }
exit 0
}
# --- Frontmatter 辅助 ---
function Get-Field {
param([string]$Field, [string[]]$Lines)
$inFm = 0
foreach ($line in $Lines) {
if ($line -eq "---") { $inFm++; continue }
if ($inFm -eq 1 -and $line -match "^${Field}: (.+)$") {
return $Matches[1]
}
if ($inFm -ge 2) { break }
}
return ""
}
function Get-Body {
param([string[]]$Lines)
$fmCount = 0
$body = @()
foreach ($line in $Lines) {
if ($line -eq "---") { $fmCount++; continue }
if ($fmCount -ge 2) { $body += $line }
}
return $body -join "`n"
}
function Get-Slug {
param([string]$FilePath)
return [System.IO.Path]::GetFileNameWithoutExtension($FilePath)
}
function Resolve-OpenCodeColor {
param([string]$c)
$map = @{
"cyan"="00FFFF"; "blue"="3498DB"; "green"="2ECC71"; "red"="E74C3C"
"purple"="9B59B6"; "orange"="F39C12"; "teal"="008080"; "indigo"="6366F1"
"pink"="E84393"; "gold"="EAB308"; "amber"="F59E0B"; "neon-green"="10B981"
"neon-cyan"="06B6D4"; "metallic-blue"="3B82F6"; "yellow"="EAB308"
"violet"="8B5CF6"; "rose"="F43F5E"; "lime"="84CC16"; "gray"="6B7280"
"fuchsia"="D946EF"
}
if ($map.ContainsKey($c)) { return "#" + $map[$c] }
return $c
}
# --- 各工具转换器 ---
function Convert-Antigravity {
param([string]$File, [string[]]$Lines)
$name = Get-Field "name" $Lines
$description = Get-Field "description" $Lines
$slug = "agency-$(Get-Slug $File)"
$body = Get-Body $Lines
$outDir = Join-Path $OutDir "antigravity\$slug"
New-Item -ItemType Directory -Force -Path $outDir | Out-Null
@"
---
name: $slug
description: $description
risk: low
source: community
date_added: '$Today'
---
$body
"@ | Set-Content -Path (Join-Path $outDir "SKILL.md") -Encoding UTF8
}
function Convert-GeminiCli {
param([string]$File, [string[]]$Lines)
$description = Get-Field "description" $Lines
$slug = Get-Slug $File
$body = Get-Body $Lines
$outDir = Join-Path $OutDir "gemini-cli\skills\$slug"
New-Item -ItemType Directory -Force -Path $outDir | Out-Null
@"
---
name: $slug
description: $description
---
$body
"@ | Set-Content -Path (Join-Path $outDir "SKILL.md") -Encoding UTF8
}
function Convert-OpenCode {
param([string]$File, [string[]]$Lines)
$name = Get-Field "name" $Lines
$description = Get-Field "description" $Lines
$rawColor = (Get-Field "color" $Lines).Trim('"')
$color = Resolve-OpenCodeColor $rawColor
$slug = Get-Slug $File
$body = Get-Body $Lines
$agentsDir = Join-Path $OutDir "opencode\agents"
New-Item -ItemType Directory -Force -Path $agentsDir | Out-Null
@"
---
name: $name
description: $description
mode: subagent
color: "$color"
---
$body
"@ | Set-Content -Path (Join-Path $agentsDir "${slug}.md") -Encoding UTF8
}
function Convert-Cursor {
param([string]$File, [string[]]$Lines)
$description = Get-Field "description" $Lines
$slug = Get-Slug $File
$body = Get-Body $Lines
$rulesDir = Join-Path $OutDir "cursor\rules"
New-Item -ItemType Directory -Force -Path $rulesDir | Out-Null
@"
---
description: $description
globs: ""
alwaysApply: false
---
$body
"@ | Set-Content -Path (Join-Path $rulesDir "${slug}.mdc") -Encoding UTF8
}
function Convert-Trae {
param([string]$File, [string[]]$Lines)
$description = Get-Field "description" $Lines
$slug = Get-Slug $File
$body = Get-Body $Lines
$rulesDir = Join-Path $OutDir "trae\rules"
New-Item -ItemType Directory -Force -Path $rulesDir | Out-Null
@"
---
description: $description
globs: ""
alwaysApply: false
---
$body
"@ | Set-Content -Path (Join-Path $rulesDir "${slug}.md") -Encoding UTF8
}
function Convert-OpenClaw {
param([string]$File, [string[]]$Lines)
$name = Get-Field "name" $Lines
$description = Get-Field "description" $Lines
$slug = Get-Slug $File
$body = Get-Body $Lines
$outDir = Join-Path $OutDir "openclaw\$slug"
New-Item -ItemType Directory -Force -Path $outDir | Out-Null
$soulLines = @()
$agentLines = @()
$target = "agents"
$section = @()
foreach ($line in ($body -split "`n")) {
if ($line -match "^## ") {
if ($section.Count -gt 0) {
if ($target -eq "soul") { $soulLines += $section }
else { $agentLines += $section }
}
$section = @()
$lower = $line.ToLower()
if ($lower -match "identity|身份|记忆|communication|沟通|style|风格|critical.rule|关键规则|rules.you.must.follow") {
$target = "soul"
} else {
$target = "agents"
}
}
$section += $line
}
if ($section.Count -gt 0) {
if ($target -eq "soul") { $soulLines += $section }
else { $agentLines += $section }
}
($soulLines -join "`n") | Set-Content -Path (Join-Path $outDir "SOUL.md") -Encoding UTF8
@"
# AGENTS.md - 工作空间规范
$($agentLines -join "`n")
"@ | Set-Content -Path (Join-Path $outDir "AGENTS.md") -Encoding UTF8
"# $name`n$description" | Set-Content -Path (Join-Path $outDir "IDENTITY.md") -Encoding UTF8
}
function Convert-Qwen {
param([string]$File, [string[]]$Lines)
$description = Get-Field "description" $Lines
$tools = Get-Field "tools" $Lines
$slug = Get-Slug $File
$body = Get-Body $Lines
$agentsDir = Join-Path $OutDir "qwen\agents"
New-Item -ItemType Directory -Force -Path $agentsDir | Out-Null
$toolsLine = if ($tools) { "tools: $tools`n" } else { "" }
@"
---
name: $slug
description: $description
${toolsLine}---
$body
"@ | Set-Content -Path (Join-Path $agentsDir "${slug}.md") -Encoding UTF8
}
function Convert-Codex {
param([string]$File, [string[]]$Lines)
$description = Get-Field "description" $Lines
$slug = Get-Slug $File
$body = Get-Body $Lines
$agentsDir = Join-Path $OutDir "codex\agents"
New-Item -ItemType Directory -Force -Path $agentsDir | Out-Null
$escapedDesc = $description -replace '"','\"'
$escapedBody = $body -replace '\\','\\' -replace '"""','\"\"\"'
@"
name = "$slug"
description = "$escapedDesc"
developer_instructions = """
$escapedBody
"""
"@ | Set-Content -Path (Join-Path $agentsDir "${slug}.toml") -Encoding UTF8
}
function Convert-DeerFlow {
param([string]$File, [string[]]$Lines)
$description = Get-Field "description" $Lines
$slug = Get-Slug $File
$body = Get-Body $Lines
$outDir = Join-Path $OutDir "deerflow\$slug"
New-Item -ItemType Directory -Force -Path $outDir | Out-Null
@"
---
name: $slug
description: $description
---
$body
"@ | Set-Content -Path (Join-Path $outDir "SKILL.md") -Encoding UTF8
}
function Convert-WorkBuddy {
param([string]$File, [string[]]$Lines)
$description = Get-Field "description" $Lines
$slug = Get-Slug $File
$body = Get-Body $Lines
$outDir = Join-Path $OutDir "workbuddy\$slug"
New-Item -ItemType Directory -Force -Path $outDir | Out-Null
@"
---
name: $slug
description: $description
allowed-tools: Read Write Edit Bash Grep Glob
---
$body
"@ | Set-Content -Path (Join-Path $outDir "SKILL.md") -Encoding UTF8
}
function Convert-Hermes {
param([string]$File, [string[]]$Lines)
$description = Get-Field "description" $Lines
$slug = Get-Slug $File
$body = Get-Body $Lines
# 从文件路径提取分类目录名
$category = Split-Path -Leaf (Split-Path -Parent $File)
$outDir = Join-Path $OutDir "hermes\$category\$slug"
New-Item -ItemType Directory -Force -Path $outDir | Out-Null
@"
---
name: $slug
description: $description
version: 1.0.0
author: agency-agents-zh
license: MIT
metadata:
hermes:
tags: [$category]
---
$body
"@ | Set-Content -Path (Join-Path $outDir "SKILL.md") -Encoding UTF8
}
function Convert-Kiro {
param([string]$File, [string[]]$Lines)
$description = Get-Field "description" $Lines
$slug = Get-Slug $File
$body = Get-Body $Lines
$kiroDir = Join-Path $OutDir "kiro"
New-Item -ItemType Directory -Force -Path $kiroDir | Out-Null
# Kiro 自定义智能体格式:带 YAML frontmatter 的 .md 文件
# 放置于 ~/.kiro/agents/<slug>.md
# 参考:https://kiro.dev/docs/chat/subagents/
@"
---
name: $slug
description: $description
---
$body
"@ | Set-Content -Path (Join-Path $kiroDir "${slug}.md") -Encoding UTF8
}
# --- Aider / Windsurf 累积 ---
$AiderContent = "# AI 智能体专家团队 — Aider 约定文件`n# 由 scripts/convert.ps1 生成`n"
$WindsurfContent = "# AI 智能体专家团队 — Windsurf 规则文件`n# 由 scripts/convert.ps1 生成`n"
function Accumulate-Aider {
param([string]$File, [string[]]$Lines)
$script:AiderContent += "`n---`n`n## $(Get-Field 'name' $Lines)`n`n> $(Get-Field 'description' $Lines)`n`n$(Get-Body $Lines)`n"
}
function Accumulate-Windsurf {
param([string]$File, [string[]]$Lines)
$name = Get-Field "name" $Lines
$desc = Get-Field "description" $Lines
$body = Get-Body $Lines
$script:WindsurfContent += "`n" + ("=" * 80) + "`n## $name`n$desc`n" + ("=" * 80) + "`n`n$body`n"
}
# --- 主循环 ---
function Run-Conversions {
param([string]$ToolName)
$count = 0
foreach ($dir in $AgentDirs) {
$dirPath = Join-Path $RepoRoot $dir
if (-not (Test-Path $dirPath)) { continue }
$files = Get-ChildItem -Path $dirPath -Filter "*.md" -Recurse
foreach ($file in $files) {
$filePath = $file.FullName
$lines = Get-Content -Path $filePath -Encoding UTF8
if ($lines.Count -eq 0 -or $lines[0] -ne "---") { continue }
$name = Get-Field "name" $lines
if (-not $name) { continue }
switch ($ToolName) {
"antigravity" { Convert-Antigravity $filePath $lines }
"gemini-cli" { Convert-GeminiCli $filePath $lines }
"opencode" { Convert-OpenCode $filePath $lines }
"cursor" { Convert-Cursor $filePath $lines }
"trae" { Convert-Trae $filePath $lines }
"openclaw" { Convert-OpenClaw $filePath $lines }
"qwen" { Convert-Qwen $filePath $lines }
"codex" { Convert-Codex $filePath $lines }
"deerflow" { Convert-DeerFlow $filePath $lines }
"workbuddy" { Convert-WorkBuddy $filePath $lines }
"hermes" { Convert-Hermes $filePath $lines }
"kiro" { Convert-Kiro $filePath $lines }
"aider" { Accumulate-Aider $filePath $lines }
"windsurf" { Accumulate-Windsurf $filePath $lines }
}
$count++
}
}
return $count
}
# --- 入口 ---
if ($Tool -notin $ValidTools) {
Write-Err "未知工具 '$Tool'。可选: $($ValidTools -join ', ')"
exit 1
}
Write-Header "AI 智能体专家团队 -- 转换为工具专用格式"
Write-Host " 仓库: $RepoRoot"
Write-Host " 输出: $OutDir"
Write-Host " 工具: $Tool"
Write-Host " 日期: $Today"
$toolsToRun = if ($Tool -eq "all") { $ValidTools | Where-Object { $_ -ne "all" } } else { @($Tool) }
$total = 0
foreach ($t in $toolsToRun) {
Write-Header "正在转换: $t"
$count = Run-Conversions $t
$total += $count
if ($t -eq "gemini-cli") {
$gDir = Join-Path $OutDir "gemini-cli"
New-Item -ItemType Directory -Force -Path $gDir | Out-Null
'{"name":"agency-agents-zh","version":"1.0.0"}' |
Set-Content -Path (Join-Path $gDir "gemini-extension.json") -Encoding UTF8
Write-OK "已写入 gemini-extension.json"
}
Write-OK "已转换 $count 个智能体 ($t)"
}
if ($Tool -eq "all" -or $Tool -eq "aider") {
$aiderDir = Join-Path $OutDir "aider"
New-Item -ItemType Directory -Force -Path $aiderDir | Out-Null
$AiderContent | Set-Content -Path (Join-Path $aiderDir "CONVENTIONS.md") -Encoding UTF8
Write-OK "已写入 integrations/aider/CONVENTIONS.md"
}
if ($Tool -eq "all" -or $Tool -eq "windsurf") {
$wsDir = Join-Path $OutDir "windsurf"
New-Item -ItemType Directory -Force -Path $wsDir | Out-Null
$WindsurfContent | Set-Content -Path (Join-Path $wsDir ".windsurfrules") -Encoding UTF8
Write-OK "已写入 integrations/windsurf/.windsurfrules"
}
Write-Host ""
Write-OK "完成。共转换: $total"