* feat: chroots * wip * Update workspace templates and Playwright tests * Fix thinking panel close button not working during active thinking The auto-show useEffect was including showThinkingPanel in its dependency array, causing the panel to immediately reopen when closed since the state change would trigger the effect while hasActiveThinking was still true. Changed to use a ref to track previous state and only auto-show on transition from inactive to active thinking. * wip * wip * wip * Cleanup web search tool and remove hardcoded OAuth credentials * Ralph iteration 1: work in progress * Ralph iteration 2: work in progress * Ralph iteration 3: work in progress * Ralph iteration 4: work in progress * Ralph iteration 5: work in progress * Ralph iteration 6: work in progress * Ralph iteration 1: work in progress * Ralph iteration 2: work in progress * Ralph iteration 3: work in progress * Ralph iteration 4: work in progress * Ralph iteration 5: work in progress * Ralph iteration 6: work in progress * Ralph iteration 7: work in progress * Ralph iteration 1: work in progress * Ralph iteration 2: work in progress * improve readme * fix: remove unused file * feat: hero screenshot * Update README with cleaner vision and hero screenshot Simplified the vision section with "what if" framing, removed architecture diagram, added hero screenshot showing mission view.
103 lines
3.6 KiB
TypeScript
103 lines
3.6 KiB
TypeScript
import { tool } from "@opencode-ai/plugin"
|
|
|
|
// The Open Agent API URL - the backend handles library configuration internally
|
|
const API_BASE = "http://127.0.0.1:3000"
|
|
|
|
async function apiRequest(endpoint: string, options: RequestInit = {}) {
|
|
const url = `${API_BASE}/api/library${endpoint}`
|
|
const response = await fetch(url, {
|
|
...options,
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
...options.headers,
|
|
},
|
|
})
|
|
|
|
if (!response.ok) {
|
|
const text = await response.text()
|
|
throw new Error(`API error ${response.status}: ${text}`)
|
|
}
|
|
|
|
const contentType = response.headers.get("content-type")
|
|
if (contentType?.includes("application/json")) {
|
|
return response.json()
|
|
}
|
|
return response.text()
|
|
}
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
// Skills
|
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
export const list_skills = tool({
|
|
description: "List all skills in the library with their names and descriptions",
|
|
args: {},
|
|
async execute() {
|
|
const skills = await apiRequest("/skill")
|
|
if (!skills || skills.length === 0) {
|
|
return "No skills found in the library."
|
|
}
|
|
return skills.map((s: { name: string; description?: string }) =>
|
|
`- ${s.name}: ${s.description || "(no description)"}`
|
|
).join("\n")
|
|
},
|
|
})
|
|
|
|
export const get_skill = tool({
|
|
description: "Get the full content of a skill by name, including SKILL.md and any additional files",
|
|
args: {
|
|
name: tool.schema.string().describe("The skill name (e.g., 'git-release')"),
|
|
},
|
|
async execute(args) {
|
|
const skill = await apiRequest(`/skill/${encodeURIComponent(args.name)}`)
|
|
let result = `# Skill: ${skill.name}\n\n`
|
|
result += `**Path:** ${skill.path}\n`
|
|
if (skill.description) {
|
|
result += `**Description:** ${skill.description}\n`
|
|
}
|
|
result += `\n## SKILL.md Content\n\n${skill.content}`
|
|
|
|
if (skill.files && skill.files.length > 0) {
|
|
result += "\n\n## Additional Files\n"
|
|
for (const file of skill.files) {
|
|
result += `\n### ${file.path}\n\n${file.content}`
|
|
}
|
|
}
|
|
|
|
if (skill.references && skill.references.length > 0) {
|
|
result += "\n\n## Reference Files\n"
|
|
result += skill.references.map((r: string) => `- ${r}`).join("\n")
|
|
}
|
|
|
|
return result
|
|
},
|
|
})
|
|
|
|
export const save_skill = tool({
|
|
description: "Create or update a skill in the library. Provide the full SKILL.md content including YAML frontmatter.",
|
|
args: {
|
|
name: tool.schema.string().describe("The skill name (lowercase, hyphens allowed, 1-64 chars)"),
|
|
content: tool.schema.string().describe("Full SKILL.md content including YAML frontmatter with name and description"),
|
|
},
|
|
async execute(args) {
|
|
await apiRequest(`/skill/${encodeURIComponent(args.name)}`, {
|
|
method: "PUT",
|
|
body: JSON.stringify({ content: args.content }),
|
|
})
|
|
return `Skill '${args.name}' saved successfully. Remember to commit and push your changes.`
|
|
},
|
|
})
|
|
|
|
export const delete_skill = tool({
|
|
description: "Delete a skill from the library",
|
|
args: {
|
|
name: tool.schema.string().describe("The skill name to delete"),
|
|
},
|
|
async execute(args) {
|
|
await apiRequest(`/skill/${encodeURIComponent(args.name)}`, {
|
|
method: "DELETE",
|
|
})
|
|
return `Skill '${args.name}' deleted. Remember to commit and push your changes.`
|
|
},
|
|
})
|