-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathgenerate-agents.js
More file actions
155 lines (138 loc) · 4.62 KB
/
Copy pathgenerate-agents.js
File metadata and controls
155 lines (138 loc) · 4.62 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
import { readFile, writeFile, mkdir, rm, readdir } from "node:fs/promises"
import { join, dirname, basename } from "node:path"
import { fileURLToPath } from "node:url"
const ROOT = join(dirname(fileURLToPath(import.meta.url)), "..", "..")
const MERGED_CATALOG = join(ROOT, "dist", "catalog", "agency-agents")
const CATALOG_DIR = MERGED_CATALOG
const DEFAULT_outputDir = join(ROOT, ".opencode", "agents")
const PRIMARY_outputDir = join(ROOT, "src", "agents")
const outputDir = process.argv[2] || DEFAULT_outputDir
function parseFrontmatter(raw) {
const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/)
if (!match) return { data: {}, body: raw }
const data = {}
for (const line of match[1].split("\n")) {
const idx = line.indexOf(":")
if (idx === -1) continue
const key = line.slice(0, idx).trim()
let value = line.slice(idx + 1).trim()
if (value.startsWith('"') && value.endsWith('"')) value = value.slice(1, -1)
if (key === "tools" && typeof value === "string" && value.includes(",")) {
data[key] = value.split(",").map((t) => t.trim())
} else {
data[key] = value
}
}
return { data, body: match[2].trim() }
}
function sanitizeId(filename) {
return basename(filename, ".md")
}
async function loadCatalogPersonas() {
const personas = []
const entries = await readdir(CATALOG_DIR, { withFileTypes: true })
for (const entry of entries) {
if (!entry.isDirectory() || entry.name.startsWith(".")) continue
if (["examples", "scripts"].includes(entry.name)) continue
const divisionDir = join(CATALOG_DIR, entry.name)
const files = await readdir(divisionDir)
for (const file of files.filter((f) => f.endsWith(".md"))) {
const raw = await readFile(join(divisionDir, file), "utf-8")
const { data, body } = parseFrontmatter(raw)
if (!data.description) continue
personas.push({
id: sanitizeId(file),
name: data.name || sanitizeId(file),
description: data.description,
division: entry.name,
emoji: data.emoji || "",
systemPrompt: body,
})
}
}
return personas
}
async function writePrimaryAgents() {
const agents = [
{
filename: "briefing-writer.md",
name: "Briefing Writer",
description:
"Briefing Writer - Conducts structured discovery sessions to produce professional briefings",
mode: "primary",
sourceFile: join(PRIMARY_outputDir, "briefing-writer.md"),
},
{
filename: "manager.md",
name: "Manager",
description:
"Manager - Orchestrates specialist teams for structured discussion and specification",
mode: "primary",
sourceFile: join(PRIMARY_outputDir, "manager.md"),
},
]
const generated = []
for (const agent of agents) {
const promptBody = await readFile(agent.sourceFile, "utf-8")
const content = [
"---",
`description: ${agent.description}`,
`mode: ${agent.mode}`,
"---",
"",
promptBody.trim(),
"",
].join("\n")
const outPath = join(outputDir, agent.filename)
await writeFile(outPath, content, "utf-8")
generated.push(agent.name)
}
return generated
}
async function writeSubagents(personas) {
const subagentsDir = join(outputDir, "mesa")
await rm(subagentsDir, { recursive: true, force: true })
await mkdir(subagentsDir, { recursive: true })
const generated = []
for (const persona of personas) {
const description = persona.emoji
? `${persona.emoji} ${persona.description}`
: persona.description
const frontmatter = [
"---",
`description: ${description}`,
"mode: subagent",
"hidden: true",
"permission:",
" edit: allow",
" write: allow",
" bash: allow",
" task:",
' "mesa/*": ask',
' "*": deny',
"---",
"",
].join("\n")
const body =
persona.systemPrompt || `You are ${persona.name}. ${persona.description}`
const content = frontmatter + body + "\n"
const outPath = join(subagentsDir, `${persona.id}.md`)
await writeFile(outPath, content, "utf-8")
generated.push(persona.id)
}
return generated
}
async function main() {
await mkdir(outputDir, { recursive: true })
const primaries = await writePrimaryAgents()
console.log(`Primary agents: ${primaries.join(", ")}`)
const personas = await loadCatalogPersonas()
const subagents = await writeSubagents(personas)
console.log(`Subagents generated: ${subagents.length} in mesa/ (hidden, mode: subagent)`)
console.log(`\nOutput: ${outputDir}`)
console.log("Restart opencode to load the new agents.")
}
main().catch((err) => {
console.error(err)
process.exit(1)
})