mirror of
https://github.com/anomalyco/opencode.git
synced 2026-08-06 17:19:49 -04:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 180109d8b6 |
@@ -38,6 +38,7 @@ import { QuestionTool } from "../tool/question"
|
|||||||
import { ReadToolFileSystem } from "../tool/read-filesystem"
|
import { ReadToolFileSystem } from "../tool/read-filesystem"
|
||||||
import { ReadTool } from "../tool/read"
|
import { ReadTool } from "../tool/read"
|
||||||
import { ShellTool } from "../tool/shell"
|
import { ShellTool } from "../tool/shell"
|
||||||
|
import { SearchFixtureTool } from "../tool/search-fixture"
|
||||||
import { SkillTool } from "../tool/skill"
|
import { SkillTool } from "../tool/skill"
|
||||||
import { SubagentTool } from "../tool/subagent"
|
import { SubagentTool } from "../tool/subagent"
|
||||||
import { Tools } from "../tool/tools"
|
import { Tools } from "../tool/tools"
|
||||||
@@ -129,6 +130,7 @@ const pre = [
|
|||||||
GrepTool.Plugin,
|
GrepTool.Plugin,
|
||||||
QuestionTool.Plugin,
|
QuestionTool.Plugin,
|
||||||
ReadTool.Plugin,
|
ReadTool.Plugin,
|
||||||
|
SearchFixtureTool.Plugin,
|
||||||
ShellTool.Plugin,
|
ShellTool.Plugin,
|
||||||
SkillTool.Plugin,
|
SkillTool.Plugin,
|
||||||
SubagentTool.Plugin,
|
SubagentTool.Plugin,
|
||||||
|
|||||||
@@ -0,0 +1,355 @@
|
|||||||
|
export * as SearchFixtureTool from "./search-fixture"
|
||||||
|
|
||||||
|
import type { Context as PluginContext } from "@opencode-ai/plugin/v2/effect/plugin"
|
||||||
|
import { Effect, Schema } from "effect"
|
||||||
|
import { Tool } from "./tool"
|
||||||
|
|
||||||
|
export const catalog = [
|
||||||
|
{
|
||||||
|
namespace: "slack",
|
||||||
|
label: "Slack",
|
||||||
|
operations: [
|
||||||
|
"send_message",
|
||||||
|
"schedule_message",
|
||||||
|
"create_channel",
|
||||||
|
"archive_channel",
|
||||||
|
"invite_user",
|
||||||
|
"add_reaction",
|
||||||
|
"pin_message",
|
||||||
|
"set_topic",
|
||||||
|
"start_huddle",
|
||||||
|
"export_thread",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
namespace: "github",
|
||||||
|
label: "GitHub",
|
||||||
|
operations: [
|
||||||
|
"create_issue",
|
||||||
|
"close_issue",
|
||||||
|
"assign_issue",
|
||||||
|
"label_issue",
|
||||||
|
"create_pull_request",
|
||||||
|
"request_review",
|
||||||
|
"merge_pull_request",
|
||||||
|
"create_release",
|
||||||
|
"dispatch_workflow",
|
||||||
|
"protect_branch",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
namespace: "linear",
|
||||||
|
label: "Linear",
|
||||||
|
operations: [
|
||||||
|
"create_issue",
|
||||||
|
"update_issue",
|
||||||
|
"assign_issue",
|
||||||
|
"move_issue",
|
||||||
|
"add_comment",
|
||||||
|
"create_project",
|
||||||
|
"update_project",
|
||||||
|
"create_cycle",
|
||||||
|
"add_label",
|
||||||
|
"archive_issue",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
namespace: "notion",
|
||||||
|
label: "Notion",
|
||||||
|
operations: [
|
||||||
|
"create_page",
|
||||||
|
"update_page",
|
||||||
|
"archive_page",
|
||||||
|
"search_workspace",
|
||||||
|
"append_blocks",
|
||||||
|
"create_database",
|
||||||
|
"add_database_row",
|
||||||
|
"update_database_row",
|
||||||
|
"duplicate_page",
|
||||||
|
"share_page",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
namespace: "jira",
|
||||||
|
label: "Jira",
|
||||||
|
operations: [
|
||||||
|
"create_ticket",
|
||||||
|
"transition_ticket",
|
||||||
|
"assign_ticket",
|
||||||
|
"add_comment",
|
||||||
|
"link_ticket",
|
||||||
|
"create_epic",
|
||||||
|
"add_to_sprint",
|
||||||
|
"log_work",
|
||||||
|
"set_priority",
|
||||||
|
"close_ticket",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
namespace: "google_drive",
|
||||||
|
label: "Google Drive",
|
||||||
|
operations: [
|
||||||
|
"create_folder",
|
||||||
|
"upload_file",
|
||||||
|
"move_file",
|
||||||
|
"copy_file",
|
||||||
|
"share_file",
|
||||||
|
"revoke_access",
|
||||||
|
"rename_file",
|
||||||
|
"trash_file",
|
||||||
|
"restore_file",
|
||||||
|
"search_files",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
namespace: "gmail",
|
||||||
|
label: "Gmail",
|
||||||
|
operations: [
|
||||||
|
"send_email",
|
||||||
|
"create_draft",
|
||||||
|
"reply_thread",
|
||||||
|
"forward_email",
|
||||||
|
"add_label",
|
||||||
|
"remove_label",
|
||||||
|
"archive_thread",
|
||||||
|
"mark_read",
|
||||||
|
"mark_unread",
|
||||||
|
"schedule_email",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
namespace: "calendar",
|
||||||
|
label: "Google Calendar",
|
||||||
|
operations: [
|
||||||
|
"create_event",
|
||||||
|
"update_event",
|
||||||
|
"cancel_event",
|
||||||
|
"invite_attendee",
|
||||||
|
"remove_attendee",
|
||||||
|
"find_availability",
|
||||||
|
"reserve_room",
|
||||||
|
"add_conference",
|
||||||
|
"set_reminder",
|
||||||
|
"list_events",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
namespace: "stripe",
|
||||||
|
label: "Stripe",
|
||||||
|
operations: [
|
||||||
|
"create_customer",
|
||||||
|
"update_customer",
|
||||||
|
"create_invoice",
|
||||||
|
"finalize_invoice",
|
||||||
|
"refund_payment",
|
||||||
|
"cancel_subscription",
|
||||||
|
"pause_subscription",
|
||||||
|
"resume_subscription",
|
||||||
|
"create_coupon",
|
||||||
|
"send_receipt",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
namespace: "salesforce",
|
||||||
|
label: "Salesforce",
|
||||||
|
operations: [
|
||||||
|
"create_lead",
|
||||||
|
"convert_lead",
|
||||||
|
"update_contact",
|
||||||
|
"create_account",
|
||||||
|
"update_opportunity",
|
||||||
|
"advance_opportunity",
|
||||||
|
"add_activity",
|
||||||
|
"create_case",
|
||||||
|
"close_case",
|
||||||
|
"assign_owner",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
namespace: "hubspot",
|
||||||
|
label: "HubSpot",
|
||||||
|
operations: [
|
||||||
|
"create_contact",
|
||||||
|
"update_contact",
|
||||||
|
"create_company",
|
||||||
|
"create_deal",
|
||||||
|
"move_deal_stage",
|
||||||
|
"enroll_workflow",
|
||||||
|
"add_note",
|
||||||
|
"create_task",
|
||||||
|
"merge_contacts",
|
||||||
|
"assign_owner",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
namespace: "zendesk",
|
||||||
|
label: "Zendesk",
|
||||||
|
operations: [
|
||||||
|
"create_ticket",
|
||||||
|
"update_ticket",
|
||||||
|
"assign_ticket",
|
||||||
|
"add_internal_note",
|
||||||
|
"reply_ticket",
|
||||||
|
"set_priority",
|
||||||
|
"add_tag",
|
||||||
|
"remove_tag",
|
||||||
|
"merge_ticket",
|
||||||
|
"solve_ticket",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
namespace: "datadog",
|
||||||
|
label: "Datadog",
|
||||||
|
operations: [
|
||||||
|
"create_monitor",
|
||||||
|
"mute_monitor",
|
||||||
|
"unmute_monitor",
|
||||||
|
"create_dashboard",
|
||||||
|
"add_dashboard_widget",
|
||||||
|
"annotate_event",
|
||||||
|
"create_incident",
|
||||||
|
"update_incident",
|
||||||
|
"resolve_incident",
|
||||||
|
"schedule_downtime",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
namespace: "pagerduty",
|
||||||
|
label: "PagerDuty",
|
||||||
|
operations: [
|
||||||
|
"trigger_incident",
|
||||||
|
"acknowledge_incident",
|
||||||
|
"resolve_incident",
|
||||||
|
"reassign_incident",
|
||||||
|
"add_responder",
|
||||||
|
"add_note",
|
||||||
|
"create_escalation_policy",
|
||||||
|
"override_schedule",
|
||||||
|
"create_maintenance_window",
|
||||||
|
"snooze_incident",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
namespace: "aws",
|
||||||
|
label: "AWS",
|
||||||
|
operations: [
|
||||||
|
"launch_instance",
|
||||||
|
"stop_instance",
|
||||||
|
"restart_instance",
|
||||||
|
"scale_service",
|
||||||
|
"deploy_lambda",
|
||||||
|
"invalidate_cache",
|
||||||
|
"rotate_secret",
|
||||||
|
"create_queue",
|
||||||
|
"publish_topic",
|
||||||
|
"snapshot_database",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
namespace: "vercel",
|
||||||
|
label: "Vercel",
|
||||||
|
operations: [
|
||||||
|
"create_project",
|
||||||
|
"deploy_project",
|
||||||
|
"promote_deployment",
|
||||||
|
"rollback_deployment",
|
||||||
|
"add_domain",
|
||||||
|
"remove_domain",
|
||||||
|
"set_env_variable",
|
||||||
|
"remove_env_variable",
|
||||||
|
"purge_cache",
|
||||||
|
"inspect_deployment",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
namespace: "figma",
|
||||||
|
label: "Figma",
|
||||||
|
operations: [
|
||||||
|
"create_file",
|
||||||
|
"duplicate_file",
|
||||||
|
"create_page",
|
||||||
|
"rename_layer",
|
||||||
|
"add_comment",
|
||||||
|
"resolve_comment",
|
||||||
|
"publish_library",
|
||||||
|
"create_branch",
|
||||||
|
"merge_branch",
|
||||||
|
"export_frame",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
namespace: "airtable",
|
||||||
|
label: "Airtable",
|
||||||
|
operations: [
|
||||||
|
"create_base",
|
||||||
|
"create_table",
|
||||||
|
"add_field",
|
||||||
|
"create_record",
|
||||||
|
"update_record",
|
||||||
|
"delete_record",
|
||||||
|
"find_records",
|
||||||
|
"link_records",
|
||||||
|
"create_view",
|
||||||
|
"sort_view",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
namespace: "dropbox",
|
||||||
|
label: "Dropbox",
|
||||||
|
operations: [
|
||||||
|
"upload_file",
|
||||||
|
"download_file",
|
||||||
|
"move_file",
|
||||||
|
"copy_file",
|
||||||
|
"delete_file",
|
||||||
|
"restore_file",
|
||||||
|
"share_link",
|
||||||
|
"revoke_link",
|
||||||
|
"create_folder",
|
||||||
|
"search_files",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
namespace: "snowflake",
|
||||||
|
label: "Snowflake",
|
||||||
|
operations: [
|
||||||
|
"create_warehouse",
|
||||||
|
"resize_warehouse",
|
||||||
|
"suspend_warehouse",
|
||||||
|
"resume_warehouse",
|
||||||
|
"create_database",
|
||||||
|
"create_schema",
|
||||||
|
"create_table",
|
||||||
|
"load_stage",
|
||||||
|
"run_query",
|
||||||
|
"grant_role",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
] as const
|
||||||
|
|
||||||
|
const Input = Schema.Struct({})
|
||||||
|
|
||||||
|
export const Plugin = {
|
||||||
|
id: "opencode.tool.search-fixture",
|
||||||
|
effect: Effect.fn("SearchFixtureTool.Plugin")(function* (ctx: PluginContext) {
|
||||||
|
yield* ctx.tool
|
||||||
|
.transform((draft) => {
|
||||||
|
catalog.forEach((group) =>
|
||||||
|
group.operations.forEach((operation) => {
|
||||||
|
const action = operation.replaceAll("_", " ")
|
||||||
|
draft.add(
|
||||||
|
operation,
|
||||||
|
Tool.make({
|
||||||
|
description: `${action[0]?.toUpperCase()}${action.slice(1)} in ${group.label}.`,
|
||||||
|
input: Input,
|
||||||
|
output: Schema.String,
|
||||||
|
execute: () => Effect.succeed(`Completed ${action}.`),
|
||||||
|
}),
|
||||||
|
{ group: group.namespace, deferred: true },
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.pipe(Effect.orDie)
|
||||||
|
}),
|
||||||
|
}
|
||||||
@@ -550,6 +550,7 @@ describe("LocationServiceMap", () => {
|
|||||||
expect(blockedState.providers.some((provider) => provider.id === allowedID)).toBe(false)
|
expect(blockedState.providers.some((provider) => provider.id === allowedID)).toBe(false)
|
||||||
expect(blockedState.tools.map((tool) => tool.name).sort()).toEqual([
|
expect(blockedState.tools.map((tool) => tool.name).sort()).toEqual([
|
||||||
"edit",
|
"edit",
|
||||||
|
"execute",
|
||||||
"glob",
|
"glob",
|
||||||
"grep",
|
"grep",
|
||||||
"patch",
|
"patch",
|
||||||
@@ -567,6 +568,7 @@ describe("LocationServiceMap", () => {
|
|||||||
expect(allowedState.providers.some((provider) => provider.id === blockedID)).toBe(false)
|
expect(allowedState.providers.some((provider) => provider.id === blockedID)).toBe(false)
|
||||||
expect(allowedState.tools.map((tool) => tool.name).sort()).toEqual([
|
expect(allowedState.tools.map((tool) => tool.name).sort()).toEqual([
|
||||||
"edit",
|
"edit",
|
||||||
|
"execute",
|
||||||
"glob",
|
"glob",
|
||||||
"grep",
|
"grep",
|
||||||
"patch",
|
"patch",
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
import { expect } from "bun:test"
|
||||||
|
import { AgentV2 } from "@opencode-ai/core/agent"
|
||||||
|
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
|
||||||
|
import { SessionV2 } from "@opencode-ai/core/session"
|
||||||
|
import { SessionMessage } from "@opencode-ai/core/session/message"
|
||||||
|
import { ToolOutputStore } from "@opencode-ai/core/tool-output-store"
|
||||||
|
import { ToolRegistry } from "@opencode-ai/core/tool/registry"
|
||||||
|
import { SearchFixtureTool } from "@opencode-ai/core/tool/search-fixture"
|
||||||
|
import { Effect, Layer } from "effect"
|
||||||
|
import { testEffect } from "./lib/effect"
|
||||||
|
import { registerToolPlugin } from "./lib/tool"
|
||||||
|
|
||||||
|
const outputStore = Layer.mock(ToolOutputStore.Service, {
|
||||||
|
bound: (input) => Effect.succeed({ output: input.output, outputPaths: [] }),
|
||||||
|
})
|
||||||
|
const it = testEffect(AppNodeBuilder.build(ToolRegistry.node, [[ToolOutputStore.node, outputStore]]))
|
||||||
|
|
||||||
|
it.effect("registers 200 searchable no-op tools across namespaces", () =>
|
||||||
|
Effect.gen(function* () {
|
||||||
|
expect(SearchFixtureTool.catalog).toHaveLength(20)
|
||||||
|
expect(SearchFixtureTool.catalog.flatMap((group) => group.operations)).toHaveLength(200)
|
||||||
|
|
||||||
|
yield* registerToolPlugin(SearchFixtureTool.Plugin)
|
||||||
|
const registry = yield* ToolRegistry.Service
|
||||||
|
const tools = yield* registry.materialize()
|
||||||
|
expect(tools.definitions.map((tool) => tool.name)).toEqual(["execute"])
|
||||||
|
|
||||||
|
const invoke = (code: string, id: string) =>
|
||||||
|
tools.settle({
|
||||||
|
sessionID: SessionV2.ID.make("ses_search_fixture"),
|
||||||
|
agent: AgentV2.ID.make("build"),
|
||||||
|
assistantMessageID: SessionMessage.ID.make("msg_search_fixture"),
|
||||||
|
call: { type: "tool-call", id, name: "execute", input: { code } },
|
||||||
|
})
|
||||||
|
|
||||||
|
const searched = yield* invoke(
|
||||||
|
'return await tools.$codemode.search({ query: "refund payment", namespace: "stripe" })',
|
||||||
|
"call_search_fixture",
|
||||||
|
)
|
||||||
|
expect(searched.result.type).toBe("text")
|
||||||
|
if (searched.result.type !== "text") return
|
||||||
|
expect(JSON.parse(String(searched.result.value)).items[0]).toMatchObject({
|
||||||
|
path: "tools.stripe.refund_payment",
|
||||||
|
description: "Refund payment in Stripe.",
|
||||||
|
})
|
||||||
|
|
||||||
|
const completed = yield* invoke("return await tools.stripe.refund_payment({})", "call_run_fixture")
|
||||||
|
expect(completed.result).toEqual({ type: "text", value: "Completed refund payment." })
|
||||||
|
}),
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user