saeeol 1.2.1 → 1.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/saeeol.cjs +187 -0
- package/npm/bin/saeeol +0 -0
- package/package.json +12 -12
- package/src/cli/cmd/tui/component/dialog/dialog-agent.tsx +32 -0
- package/src/cli/cmd/tui/component/dialog/dialog-command.tsx +190 -0
- package/src/cli/cmd/tui/component/dialog/dialog-console-org.tsx +103 -0
- package/src/cli/cmd/tui/component/dialog/dialog-go-upsell.tsx +159 -0
- package/src/cli/cmd/tui/component/dialog/dialog-mcp.tsx +86 -0
- package/src/cli/cmd/tui/component/dialog/dialog-model.tsx +238 -0
- package/src/cli/cmd/tui/component/dialog/dialog-provider.tsx +343 -0
- package/src/cli/cmd/tui/component/dialog/dialog-session-delete-failed.tsx +103 -0
- package/src/cli/cmd/tui/component/dialog/dialog-session-list.tsx +301 -0
- package/src/cli/cmd/tui/component/dialog/dialog-session-rename.tsx +35 -0
- package/src/cli/cmd/tui/component/dialog/dialog-skill.tsx +37 -0
- package/src/cli/cmd/tui/component/dialog/dialog-stash.tsx +87 -0
- package/src/cli/cmd/tui/component/dialog/dialog-status.tsx +190 -0
- package/src/cli/cmd/tui/component/dialog/dialog-tag.tsx +44 -0
- package/src/cli/cmd/tui/component/dialog/dialog-theme-list.tsx +50 -0
- package/src/cli/cmd/tui/component/dialog/dialog-variant.tsx +39 -0
- package/src/cli/cmd/tui/component/dialog/dialog-workspace-create.tsx +200 -0
- package/src/cli/cmd/tui/component/dialog/dialog-workspace-unavailable.tsx +81 -0
- package/src/cli/cmd/tui/component/dialog-agent.tsx +1 -32
- package/src/cli/cmd/tui/component/dialog-command.tsx +1 -190
- package/src/cli/cmd/tui/component/dialog-console-org.tsx +1 -103
- package/src/cli/cmd/tui/component/dialog-go-upsell.tsx +1 -159
- package/src/cli/cmd/tui/component/dialog-mcp.tsx +1 -86
- package/src/cli/cmd/tui/component/dialog-model.tsx +1 -238
- package/src/cli/cmd/tui/component/dialog-provider.tsx +1 -343
- package/src/cli/cmd/tui/component/dialog-session-delete-failed.tsx +1 -103
- package/src/cli/cmd/tui/component/dialog-session-list.tsx +1 -301
- package/src/cli/cmd/tui/component/dialog-session-rename.tsx +1 -35
- package/src/cli/cmd/tui/component/dialog-skill.tsx +1 -37
- package/src/cli/cmd/tui/component/dialog-stash.tsx +1 -87
- package/src/cli/cmd/tui/component/dialog-status.tsx +1 -190
- package/src/cli/cmd/tui/component/dialog-tag.tsx +1 -44
- package/src/cli/cmd/tui/component/dialog-theme-list.tsx +1 -50
- package/src/cli/cmd/tui/component/dialog-variant.tsx +1 -39
- package/src/cli/cmd/tui/component/dialog-workspace-create.tsx +1 -200
- package/src/cli/cmd/tui/component/dialog-workspace-unavailable.tsx +1 -81
- package/src/cli/cmd/tui/context/app/args.tsx +15 -0
- package/src/cli/cmd/tui/context/app/directory.ts +15 -0
- package/src/cli/cmd/tui/context/app/editor-zed.ts +281 -0
- package/src/cli/cmd/tui/context/app/editor.ts +425 -0
- package/src/cli/cmd/tui/context/app/helper.tsx +25 -0
- package/src/cli/cmd/tui/context/app/project.tsx +109 -0
- package/src/cli/cmd/tui/context/app/route.tsx +67 -0
- package/src/cli/cmd/tui/context/app/sdk.tsx +142 -0
- package/src/cli/cmd/tui/context/app/sync.tsx +713 -0
- package/src/cli/cmd/tui/context/app/theme.tsx +307 -0
- package/src/cli/cmd/tui/context/app/tui-config.tsx +9 -0
- package/src/cli/cmd/tui/context/args.tsx +1 -15
- package/src/cli/cmd/tui/context/directory.ts +1 -15
- package/src/cli/cmd/tui/context/editor-zed.ts +1 -281
- package/src/cli/cmd/tui/context/editor.ts +1 -425
- package/src/cli/cmd/tui/context/event.ts +1 -45
- package/src/cli/cmd/tui/context/exit.tsx +1 -67
- package/src/cli/cmd/tui/context/helper.tsx +1 -25
- package/src/cli/cmd/tui/context/keybind.tsx +1 -105
- package/src/cli/cmd/tui/context/kv.tsx +1 -76
- package/src/cli/cmd/tui/context/local.tsx +1 -478
- package/src/cli/cmd/tui/context/plugin-keybinds.ts +1 -41
- package/src/cli/cmd/tui/context/project.tsx +1 -109
- package/src/cli/cmd/tui/context/prompt.tsx +1 -18
- package/src/cli/cmd/tui/context/route.tsx +1 -67
- package/src/cli/cmd/tui/context/runtime/event.ts +45 -0
- package/src/cli/cmd/tui/context/runtime/exit.tsx +67 -0
- package/src/cli/cmd/tui/context/runtime/keybind.tsx +105 -0
- package/src/cli/cmd/tui/context/runtime/kv.tsx +76 -0
- package/src/cli/cmd/tui/context/runtime/local.tsx +478 -0
- package/src/cli/cmd/tui/context/runtime/plugin-keybinds.ts +41 -0
- package/src/cli/cmd/tui/context/sdk.tsx +1 -142
- package/src/cli/cmd/tui/context/session/prompt.tsx +18 -0
- package/src/cli/cmd/tui/context/sync.tsx +1 -713
- package/src/cli/cmd/tui/context/theme.tsx +1 -307
- package/src/cli/cmd/tui/context/tui-config.tsx +1 -9
- package/src/tool/apply_patch.ts +1 -334
- package/src/tool/bash.ts +1 -656
- package/src/tool/core/external-directory.ts +55 -0
- package/src/tool/core/invalid.ts +21 -0
- package/src/tool/core/recall.ts +164 -0
- package/src/tool/core/recall.txt +12 -0
- package/src/tool/core/schema.ts +16 -0
- package/src/tool/core/tool.ts +162 -0
- package/src/tool/core/truncate.ts +160 -0
- package/src/tool/core/truncation-dir.ts +4 -0
- package/src/tool/diagnostics.ts +1 -20
- package/src/tool/edit-replacers.ts +1 -288
- package/src/tool/edit-utils.ts +1 -86
- package/src/tool/edit.ts +1 -262
- package/src/tool/external-directory.ts +1 -55
- package/src/tool/file/apply_patch.ts +334 -0
- package/src/tool/file/apply_patch.txt +33 -0
- package/src/tool/file/bash.ts +656 -0
- package/src/tool/file/bash.txt +119 -0
- package/src/tool/file/edit-replacers.ts +288 -0
- package/src/tool/file/edit-utils.ts +86 -0
- package/src/tool/file/edit.ts +262 -0
- package/src/tool/file/edit.txt +10 -0
- package/src/tool/file/read.ts +389 -0
- package/src/tool/file/read.txt +14 -0
- package/src/tool/file/write.ts +114 -0
- package/src/tool/file/write.txt +8 -0
- package/src/tool/glob.ts +1 -115
- package/src/tool/grep.ts +1 -151
- package/src/tool/integration/diagnostics.ts +20 -0
- package/src/tool/integration/lsp.ts +113 -0
- package/src/tool/integration/lsp.txt +24 -0
- package/src/tool/integration/mcp-exa.ts +73 -0
- package/src/tool/integration/package.ts +168 -0
- package/src/tool/integration/registry.ts +375 -0
- package/src/tool/invalid.ts +1 -21
- package/src/tool/lsp.ts +1 -113
- package/src/tool/mcp-exa.ts +1 -73
- package/src/tool/package.ts +1 -168
- package/src/tool/plan.ts +1 -30
- package/src/tool/question.ts +1 -52
- package/src/tool/read.ts +1 -389
- package/src/tool/recall.ts +1 -164
- package/src/tool/registry.ts +1 -375
- package/src/tool/schema.ts +1 -16
- package/src/tool/search/glob.ts +115 -0
- package/src/tool/search/glob.txt +6 -0
- package/src/tool/search/grep.ts +151 -0
- package/src/tool/search/grep.txt +8 -0
- package/src/tool/search/warpgrep.ts +107 -0
- package/src/tool/search/warpgrep.txt +10 -0
- package/src/tool/search/webfetch.ts +202 -0
- package/src/tool/search/webfetch.txt +13 -0
- package/src/tool/search/websearch.ts +71 -0
- package/src/tool/search/websearch.txt +14 -0
- package/src/tool/skill.ts +1 -91
- package/src/tool/task.ts +1 -197
- package/src/tool/todo.ts +1 -62
- package/src/tool/tool.ts +1 -162
- package/src/tool/truncate.ts +1 -160
- package/src/tool/truncation-dir.ts +1 -4
- package/src/tool/warpgrep.ts +1 -107
- package/src/tool/webfetch.ts +1 -202
- package/src/tool/websearch.ts +1 -71
- package/src/tool/workflow/plan-enter.txt +14 -0
- package/src/tool/workflow/plan-exit.txt +13 -0
- package/src/tool/workflow/plan.ts +30 -0
- package/src/tool/workflow/question.ts +52 -0
- package/src/tool/workflow/question.txt +11 -0
- package/src/tool/workflow/skill.ts +91 -0
- package/src/tool/workflow/skill.txt +5 -0
- package/src/tool/workflow/task.ts +197 -0
- package/src/tool/workflow/task.txt +57 -0
- package/src/tool/workflow/todo.ts +62 -0
- package/src/tool/workflow/todowrite.txt +167 -0
- package/src/tool/write.ts +1 -114
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { Effect, Schema } from "effect"
|
|
2
|
+
import { Npm } from "@saeeol/core/npm"
|
|
3
|
+
import * as Bus from "@/bus"
|
|
4
|
+
import * as Tool from "../core/tool"
|
|
5
|
+
import { ProviderInstallEvent } from "@/provider/provider-events"
|
|
6
|
+
|
|
7
|
+
const Parameters = Schema.Struct({
|
|
8
|
+
action: Schema.Literals(["install", "list"]).annotate({
|
|
9
|
+
description: '"install" to install provider packages, "list" to show available providers',
|
|
10
|
+
}),
|
|
11
|
+
packages: Schema.optional(Schema.Array(Schema.String)).annotate({
|
|
12
|
+
description:
|
|
13
|
+
'Package names to install. npm names like "@ai-sdk/anthropic" or short names like "anthropic".',
|
|
14
|
+
}),
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
type Params = Schema.Schema.Type<typeof Parameters>
|
|
18
|
+
|
|
19
|
+
const SHORT_NAME_MAP: Record<string, string> = {
|
|
20
|
+
anthropic: "@ai-sdk/anthropic",
|
|
21
|
+
openai: "@ai-sdk/openai",
|
|
22
|
+
"openai-compatible": "@ai-sdk/openai-compatible",
|
|
23
|
+
google: "@ai-sdk/google",
|
|
24
|
+
"google-vertex": "@ai-sdk/google-vertex",
|
|
25
|
+
bedrock: "@ai-sdk/amazon-bedrock",
|
|
26
|
+
"amazon-bedrock": "@ai-sdk/amazon-bedrock",
|
|
27
|
+
azure: "@ai-sdk/azure",
|
|
28
|
+
openrouter: "@openrouter/ai-sdk-provider",
|
|
29
|
+
groq: "@ai-sdk/groq",
|
|
30
|
+
deepinfra: "@ai-sdk/deepinfra",
|
|
31
|
+
gateway: "@ai-sdk/gateway",
|
|
32
|
+
alibaba: "@ai-sdk/alibaba",
|
|
33
|
+
cerebras: "@ai-sdk/cerebras",
|
|
34
|
+
xai: "@ai-sdk/xai",
|
|
35
|
+
mistral: "@ai-sdk/mistral",
|
|
36
|
+
cohere: "@ai-sdk/cohere",
|
|
37
|
+
togetherai: "@ai-sdk/togetherai",
|
|
38
|
+
perplexity: "@ai-sdk/perplexity",
|
|
39
|
+
vercel: "@ai-sdk/vercel",
|
|
40
|
+
gitlab: "gitlab-ai-provider",
|
|
41
|
+
venice: "venice-ai-sdk-provider",
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const KNOWN_PROVIDERS = [
|
|
45
|
+
{ npm: "@saeeol/gateway", label: "Saeeol Gateway (bundled)" },
|
|
46
|
+
{ npm: "@ai-sdk/anthropic", label: "Anthropic (Claude)" },
|
|
47
|
+
{ npm: "@ai-sdk/openai", label: "OpenAI (GPT)" },
|
|
48
|
+
{ npm: "@ai-sdk/openai-compatible", label: "OpenAI Compatible" },
|
|
49
|
+
{ npm: "@ai-sdk/google", label: "Google (Gemini)" },
|
|
50
|
+
{ npm: "@ai-sdk/github-copilot", label: "GitHub Copilot" },
|
|
51
|
+
{ npm: "@ai-sdk/amazon-bedrock", label: "Amazon Bedrock" },
|
|
52
|
+
{ npm: "@ai-sdk/azure", label: "Azure OpenAI" },
|
|
53
|
+
{ npm: "@ai-sdk/google-vertex", label: "Google Vertex AI" },
|
|
54
|
+
{ npm: "@openrouter/ai-sdk-provider", label: "OpenRouter" },
|
|
55
|
+
{ npm: "@ai-sdk/groq", label: "Groq" },
|
|
56
|
+
{ npm: "@ai-sdk/deepinfra", label: "DeepInfra" },
|
|
57
|
+
{ npm: "@ai-sdk/gateway", label: "Vercel AI Gateway" },
|
|
58
|
+
{ npm: "@ai-sdk/alibaba", label: "Alibaba (Qwen)" },
|
|
59
|
+
{ npm: "@ai-sdk/cerebras", label: "Cerebras" },
|
|
60
|
+
{ npm: "@ai-sdk/xai", label: "xAI (Grok)" },
|
|
61
|
+
{ npm: "@ai-sdk/mistral", label: "Mistral" },
|
|
62
|
+
{ npm: "@ai-sdk/cohere", label: "Cohere" },
|
|
63
|
+
{ npm: "@ai-sdk/togetherai", label: "Together AI" },
|
|
64
|
+
{ npm: "@ai-sdk/perplexity", label: "Perplexity" },
|
|
65
|
+
{ npm: "@ai-sdk/vercel", label: "Vercel" },
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
function resolve(pkg: string) {
|
|
69
|
+
if (pkg.startsWith("@") || pkg.startsWith("file://")) return pkg
|
|
70
|
+
return SHORT_NAME_MAP[pkg] ?? pkg
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export const PackageTool = Tool.define(
|
|
74
|
+
"package",
|
|
75
|
+
Effect.gen(function* () {
|
|
76
|
+
return {
|
|
77
|
+
description: [
|
|
78
|
+
"Manage saeeol provider SDK packages.",
|
|
79
|
+
"",
|
|
80
|
+
"Use this tool when the user asks to install additional AI providers,",
|
|
81
|
+
"or when you detect that a required provider is not available.",
|
|
82
|
+
"",
|
|
83
|
+
'Actions: "install" — install provider packages, "list" — show available providers',
|
|
84
|
+
"",
|
|
85
|
+
"Short names: anthropic, openai, google, bedrock, azure, openrouter, groq,",
|
|
86
|
+
"deepinfra, cerebras, xai, mistral, cohere, togetherai, perplexity, vercel",
|
|
87
|
+
].join("\n"),
|
|
88
|
+
parameters: Parameters,
|
|
89
|
+
execute: (params: Params, ctx: Tool.Context) =>
|
|
90
|
+
Effect.gen(function* () {
|
|
91
|
+
if (params.action === "list") {
|
|
92
|
+
const lines = [
|
|
93
|
+
"Available provider SDKs:",
|
|
94
|
+
"",
|
|
95
|
+
...KNOWN_PROVIDERS.map((p) => ` ${p.label}\n npm: ${p.npm}`),
|
|
96
|
+
"",
|
|
97
|
+
'Install: { action: "install", packages: ["anthropic"] }',
|
|
98
|
+
]
|
|
99
|
+
return { title: "Available Providers", output: lines.join("\n"), metadata: {} }
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// action === "install"
|
|
103
|
+
const packages = params.packages
|
|
104
|
+
if (!packages || packages.length === 0) {
|
|
105
|
+
return {
|
|
106
|
+
title: "Package Install",
|
|
107
|
+
output: "No packages specified. Provide package names in the 'packages' array.",
|
|
108
|
+
metadata: {},
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const resolved = packages.map(resolve)
|
|
113
|
+
|
|
114
|
+
yield* ctx.ask({
|
|
115
|
+
permission: "package",
|
|
116
|
+
patterns: resolved,
|
|
117
|
+
always: resolved,
|
|
118
|
+
metadata: {},
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
const results: string[] = []
|
|
122
|
+
for (const pkg of resolved) {
|
|
123
|
+
void Bus.publish(ProviderInstallEvent.Started, {
|
|
124
|
+
providerID: pkg,
|
|
125
|
+
pkg,
|
|
126
|
+
})
|
|
127
|
+
try {
|
|
128
|
+
const entry = yield* Effect.promise(() => Npm.add(pkg))
|
|
129
|
+
if (!entry.entrypoint) {
|
|
130
|
+
void Bus.publish(ProviderInstallEvent.Failed, {
|
|
131
|
+
providerID: pkg,
|
|
132
|
+
pkg,
|
|
133
|
+
error: "No import entrypoint found",
|
|
134
|
+
})
|
|
135
|
+
results.push(`X ${pkg}: no import entrypoint`)
|
|
136
|
+
continue
|
|
137
|
+
}
|
|
138
|
+
void Bus.publish(ProviderInstallEvent.Completed, {
|
|
139
|
+
providerID: pkg,
|
|
140
|
+
pkg,
|
|
141
|
+
})
|
|
142
|
+
results.push(`OK ${pkg}`)
|
|
143
|
+
} catch (err) {
|
|
144
|
+
const msg = err instanceof Error ? err.message : String(err)
|
|
145
|
+
void Bus.publish(ProviderInstallEvent.Failed, {
|
|
146
|
+
providerID: pkg,
|
|
147
|
+
pkg,
|
|
148
|
+
error: msg,
|
|
149
|
+
})
|
|
150
|
+
results.push(`X ${pkg}: ${msg}`)
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return {
|
|
155
|
+
title: "Package Install",
|
|
156
|
+
output: [
|
|
157
|
+
`${resolved.length} package(s) processed:`,
|
|
158
|
+
"",
|
|
159
|
+
...results,
|
|
160
|
+
"",
|
|
161
|
+
"Installed providers are now available.",
|
|
162
|
+
].join("\n"),
|
|
163
|
+
metadata: {},
|
|
164
|
+
}
|
|
165
|
+
}),
|
|
166
|
+
}
|
|
167
|
+
}),
|
|
168
|
+
)
|
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
import { PlanExitTool } from "../workflow/plan"
|
|
2
|
+
import { Session } from "@/session/session"
|
|
3
|
+
import { QuestionTool } from "../workflow/question"
|
|
4
|
+
import { SuggestTool } from "../../overlay/suggestion/tool"
|
|
5
|
+
import { BashTool } from "../file/bash"
|
|
6
|
+
import { EditTool } from "../file/edit"
|
|
7
|
+
import { GlobTool } from "../search/glob"
|
|
8
|
+
import { GrepTool } from "../search/grep"
|
|
9
|
+
import { ReadTool } from "../file/read"
|
|
10
|
+
import { TaskTool } from "../workflow/task"
|
|
11
|
+
import { TodoWriteTool } from "../workflow/todo"
|
|
12
|
+
import { WebFetchTool } from "../search/webfetch"
|
|
13
|
+
import { WriteTool } from "../file/write"
|
|
14
|
+
import { InvalidTool } from "../core/invalid"
|
|
15
|
+
import { SkillTool } from "../workflow/skill"
|
|
16
|
+
import { PackageTool } from "./package"
|
|
17
|
+
import * as Tool from "../core/tool"
|
|
18
|
+
import { Config } from "@/config/config"
|
|
19
|
+
import { type ToolContext as PluginToolContext, type ToolDefinition } from "@saeeol/plugin"
|
|
20
|
+
import { Schema } from "effect"
|
|
21
|
+
import z from "zod"
|
|
22
|
+
import { ZodOverride } from "@/util/effect-zod"
|
|
23
|
+
import { Plugin } from "../../plugin"
|
|
24
|
+
import { Provider } from "@/provider/provider"
|
|
25
|
+
import { ProviderID, type ModelID } from "../../provider/schema"
|
|
26
|
+
import { WebSearchTool } from "../search/websearch"
|
|
27
|
+
import { SaeeolToolRegistry } from "../../overlay/tool/registry"
|
|
28
|
+
import { makeRuntime } from "@/effect/run-service"
|
|
29
|
+
import { Flag } from "@saeeol/core/flag/flag"
|
|
30
|
+
import * as Log from "@saeeol/core/util/log"
|
|
31
|
+
import { LspTool } from "./lsp"
|
|
32
|
+
import * as Truncate from "../core/truncate"
|
|
33
|
+
import { ApplyPatchTool } from "../file/apply_patch"
|
|
34
|
+
import { Glob } from "@saeeol/core/util/glob"
|
|
35
|
+
import path from "path"
|
|
36
|
+
import { pathToFileURL } from "url"
|
|
37
|
+
import { Effect, Layer, Context } from "effect"
|
|
38
|
+
import { FetchHttpClient, HttpClient } from "effect/unstable/http"
|
|
39
|
+
import { ChildProcessSpawner } from "effect/unstable/process/ChildProcessSpawner"
|
|
40
|
+
import { CrossSpawnSpawner } from "@saeeol/core/cross-spawn-spawner"
|
|
41
|
+
import { Ripgrep } from "../../file/ripgrep"
|
|
42
|
+
import { Format } from "../../format"
|
|
43
|
+
import { InstanceState } from "@/effect/instance-state"
|
|
44
|
+
import { Question } from "../../question"
|
|
45
|
+
import { Todo } from "../../session/todo"
|
|
46
|
+
import { LSP } from "@/lsp/lsp"
|
|
47
|
+
import { Instruction } from "../../session/instruction"
|
|
48
|
+
import { AppFileSystem } from "@saeeol/core/filesystem"
|
|
49
|
+
import { Bus } from "../../bus"
|
|
50
|
+
import { Agent } from "../../agent/agent"
|
|
51
|
+
import { Skill } from "../../skill"
|
|
52
|
+
import { Permission } from "@/permission"
|
|
53
|
+
|
|
54
|
+
const log = Log.create({ service: "tool.registry" })
|
|
55
|
+
|
|
56
|
+
type TaskDef = Tool.InferDef<typeof TaskTool>
|
|
57
|
+
type ReadDef = Tool.InferDef<typeof ReadTool>
|
|
58
|
+
|
|
59
|
+
type State = {
|
|
60
|
+
custom: Tool.Def[]
|
|
61
|
+
builtin: Tool.Def[]
|
|
62
|
+
task: TaskDef
|
|
63
|
+
read: ReadDef
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface Interface {
|
|
67
|
+
readonly ids: () => Effect.Effect<string[]>
|
|
68
|
+
readonly all: () => Effect.Effect<Tool.Def[]>
|
|
69
|
+
readonly named: () => Effect.Effect<{ task: TaskDef; read: ReadDef }>
|
|
70
|
+
readonly tools: (model: { providerID: ProviderID; modelID: ModelID; agent: Agent.Info }) => Effect.Effect<Tool.Def[]>
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export class Service extends Context.Service<Service, Interface>()("@saeeol/ToolRegistry") {}
|
|
74
|
+
|
|
75
|
+
export const layer: Layer.Layer<
|
|
76
|
+
Service,
|
|
77
|
+
never,
|
|
78
|
+
| Config.Service
|
|
79
|
+
| Plugin.Service
|
|
80
|
+
| Question.Service
|
|
81
|
+
| Todo.Service
|
|
82
|
+
| Agent.Service
|
|
83
|
+
| Skill.Service
|
|
84
|
+
| Session.Service
|
|
85
|
+
| Provider.Service
|
|
86
|
+
| LSP.Service
|
|
87
|
+
| Instruction.Service
|
|
88
|
+
| AppFileSystem.Service
|
|
89
|
+
| Bus.Service
|
|
90
|
+
| HttpClient.HttpClient
|
|
91
|
+
| ChildProcessSpawner
|
|
92
|
+
| Ripgrep.Service
|
|
93
|
+
| Format.Service
|
|
94
|
+
| Truncate.Service
|
|
95
|
+
> = Layer.effect(
|
|
96
|
+
Service,
|
|
97
|
+
Effect.gen(function* () {
|
|
98
|
+
const config = yield* Config.Service
|
|
99
|
+
const plugin = yield* Plugin.Service
|
|
100
|
+
const agents = yield* Agent.Service
|
|
101
|
+
const skill = yield* Skill.Service
|
|
102
|
+
const truncate = yield* Truncate.Service
|
|
103
|
+
|
|
104
|
+
const invalid = yield* InvalidTool
|
|
105
|
+
const task = yield* TaskTool
|
|
106
|
+
const read = yield* ReadTool
|
|
107
|
+
const question = yield* QuestionTool
|
|
108
|
+
const todo = yield* TodoWriteTool
|
|
109
|
+
const lsptool = yield* LspTool
|
|
110
|
+
const plan = yield* PlanExitTool
|
|
111
|
+
const webfetch = yield* WebFetchTool
|
|
112
|
+
const websearch = yield* WebSearchTool
|
|
113
|
+
const bash = yield* BashTool
|
|
114
|
+
const globtool = yield* GlobTool
|
|
115
|
+
const writetool = yield* WriteTool
|
|
116
|
+
const edit = yield* EditTool
|
|
117
|
+
const greptool = yield* GrepTool
|
|
118
|
+
const patchtool = yield* ApplyPatchTool
|
|
119
|
+
const skilltool = yield* SkillTool
|
|
120
|
+
const packagetool = yield* PackageTool
|
|
121
|
+
const agent = yield* Agent.Service
|
|
122
|
+
const suggesttool = yield* SuggestTool
|
|
123
|
+
const saeeolToolInfos = yield* SaeeolToolRegistry.infos()
|
|
124
|
+
|
|
125
|
+
const state = yield* InstanceState.make<State>(
|
|
126
|
+
Effect.fn("ToolRegistry.state")(function* (ctx) {
|
|
127
|
+
const custom: Tool.Def[] = []
|
|
128
|
+
|
|
129
|
+
function fromPlugin(id: string, def: ToolDefinition): Tool.Def {
|
|
130
|
+
// Plugin tools define their args as a raw Zod shape. Wrap the
|
|
131
|
+
// derived Zod object in a `Schema.declare` so it slots into the
|
|
132
|
+
// Schema-typed framework, and annotate with `ZodOverride` so the
|
|
133
|
+
// walker emits the original Zod object for LLM JSON Schema.
|
|
134
|
+
const zodParams = z.object(def.args)
|
|
135
|
+
const parameters = Schema.declare<unknown>((u): u is unknown => zodParams.safeParse(u).success).annotate({
|
|
136
|
+
[ZodOverride]: zodParams,
|
|
137
|
+
})
|
|
138
|
+
return {
|
|
139
|
+
id,
|
|
140
|
+
parameters,
|
|
141
|
+
description: def.description,
|
|
142
|
+
execute: (args, toolCtx) =>
|
|
143
|
+
Effect.gen(function* () {
|
|
144
|
+
const pluginCtx: PluginToolContext = {
|
|
145
|
+
...toolCtx,
|
|
146
|
+
ask: (req) => toolCtx.ask(req),
|
|
147
|
+
directory: ctx.directory,
|
|
148
|
+
worktree: ctx.worktree,
|
|
149
|
+
}
|
|
150
|
+
const result = yield* Effect.promise(() => def.execute(args as any, pluginCtx))
|
|
151
|
+
const output = typeof result === "string" ? result : result.output
|
|
152
|
+
const metadata = typeof result === "string" ? {} : (result.metadata ?? {})
|
|
153
|
+
const info = yield* agent.get(toolCtx.agent)
|
|
154
|
+
const out = yield* truncate.output(output, {}, info)
|
|
155
|
+
return {
|
|
156
|
+
title: "",
|
|
157
|
+
output: out.truncated ? out.content : output,
|
|
158
|
+
metadata: {
|
|
159
|
+
...metadata,
|
|
160
|
+
truncated: out.truncated,
|
|
161
|
+
...(out.truncated && { outputPath: out.outputPath }),
|
|
162
|
+
},
|
|
163
|
+
}
|
|
164
|
+
}).pipe(
|
|
165
|
+
Effect.withSpan("Tool.execute", {
|
|
166
|
+
attributes: {
|
|
167
|
+
"tool.name": id,
|
|
168
|
+
"session.id": toolCtx.sessionID,
|
|
169
|
+
"message.id": toolCtx.messageID,
|
|
170
|
+
...(toolCtx.callID ? { "tool.call_id": toolCtx.callID } : {}),
|
|
171
|
+
},
|
|
172
|
+
}),
|
|
173
|
+
),
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const dirs = yield* config.directories()
|
|
178
|
+
const matches = dirs.flatMap((dir) =>
|
|
179
|
+
Glob.scanSync("{tool,tools}/*.{js,ts}", { cwd: dir, absolute: true, dot: true, symlink: true }),
|
|
180
|
+
)
|
|
181
|
+
if (matches.length) yield* config.waitForDependencies()
|
|
182
|
+
for (const match of matches) {
|
|
183
|
+
const namespace = path.basename(match, path.extname(match))
|
|
184
|
+
// `match` is an absolute filesystem path from `Glob.scanSync(..., { absolute: true })`.
|
|
185
|
+
// Import it as `file://` so Node on Windows accepts the dynamic import.
|
|
186
|
+
const mod = yield* Effect.promise(() => import(pathToFileURL(match).href))
|
|
187
|
+
for (const [id, def] of Object.entries<ToolDefinition>(mod)) {
|
|
188
|
+
custom.push(fromPlugin(id === "default" ? namespace : `${namespace}_${id}`, def))
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const plugins = yield* plugin.list()
|
|
193
|
+
for (const p of plugins) {
|
|
194
|
+
for (const [id, def] of Object.entries(p.tool ?? {})) {
|
|
195
|
+
custom.push(fromPlugin(id, def))
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const cfg = yield* config.get()
|
|
200
|
+
const questionEnabled =
|
|
201
|
+
["app", "cli", "desktop", "vscode"].includes(Flag.SAEEOL_CLIENT) || Flag.SAEEOL_ENABLE_QUESTION_TOOL
|
|
202
|
+
|
|
203
|
+
const tool = yield* Effect.all({
|
|
204
|
+
invalid: Tool.init(invalid),
|
|
205
|
+
bash: Tool.init(bash),
|
|
206
|
+
read: Tool.init(read),
|
|
207
|
+
glob: Tool.init(globtool),
|
|
208
|
+
grep: Tool.init(greptool),
|
|
209
|
+
edit: Tool.init(edit),
|
|
210
|
+
write: Tool.init(writetool),
|
|
211
|
+
task: Tool.init(task),
|
|
212
|
+
fetch: Tool.init(webfetch),
|
|
213
|
+
todo: Tool.init(todo),
|
|
214
|
+
search: Tool.init(websearch),
|
|
215
|
+
skill: Tool.init(skilltool),
|
|
216
|
+
package: Tool.init(packagetool),
|
|
217
|
+
patch: Tool.init(patchtool),
|
|
218
|
+
question: Tool.init(question),
|
|
219
|
+
lsp: Tool.init(lsptool),
|
|
220
|
+
plan: Tool.init(plan),
|
|
221
|
+
suggest: Tool.init(suggesttool),
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
const saeeol = yield* SaeeolToolRegistry.build(saeeolToolInfos, { agent: agents, truncate })
|
|
225
|
+
|
|
226
|
+
return {
|
|
227
|
+
custom,
|
|
228
|
+
builtin: SaeeolToolRegistry.describe(
|
|
229
|
+
[
|
|
230
|
+
tool.invalid,
|
|
231
|
+
...(questionEnabled ? [tool.question] : []),
|
|
232
|
+
tool.bash,
|
|
233
|
+
tool.read,
|
|
234
|
+
tool.glob,
|
|
235
|
+
tool.grep,
|
|
236
|
+
tool.edit,
|
|
237
|
+
tool.write,
|
|
238
|
+
tool.task,
|
|
239
|
+
tool.fetch,
|
|
240
|
+
tool.todo,
|
|
241
|
+
tool.search,
|
|
242
|
+
tool.skill,
|
|
243
|
+
tool.package,
|
|
244
|
+
tool.patch,
|
|
245
|
+
tool.plan,
|
|
246
|
+
...(["cli", "vscode"].includes(Flag.SAEEOL_CLIENT) ? [tool.suggest] : []),
|
|
247
|
+
...SaeeolToolRegistry.extra(saeeol, cfg),
|
|
248
|
+
...(Flag.SAEEOL_EXPERIMENTAL_LSP_TOOL ? [tool.lsp] : []),
|
|
249
|
+
],
|
|
250
|
+
saeeol,
|
|
251
|
+
),
|
|
252
|
+
task: tool.task,
|
|
253
|
+
read: tool.read,
|
|
254
|
+
}
|
|
255
|
+
}),
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
const all: Interface["all"] = Effect.fn("ToolRegistry.all")(function* () {
|
|
259
|
+
const s = yield* InstanceState.get(state)
|
|
260
|
+
return [...s.builtin, ...s.custom] as Tool.Def[]
|
|
261
|
+
})
|
|
262
|
+
|
|
263
|
+
const ids: Interface["ids"] = Effect.fn("ToolRegistry.ids")(function* () {
|
|
264
|
+
return (yield* all()).map((tool) => tool.id)
|
|
265
|
+
})
|
|
266
|
+
|
|
267
|
+
const describeSkill = Effect.fn("ToolRegistry.describeSkill")(function* (agent: Agent.Info) {
|
|
268
|
+
const list = yield* skill.available(agent)
|
|
269
|
+
if (list.length === 0) return "No skills are currently available."
|
|
270
|
+
return [
|
|
271
|
+
"Load a specialized skill that provides domain-specific instructions and workflows.",
|
|
272
|
+
"",
|
|
273
|
+
"When you recognize that a task matches one of the available skills listed below, use this tool to load the full skill instructions.",
|
|
274
|
+
"",
|
|
275
|
+
"The skill will inject detailed instructions, workflows, and access to bundled resources (scripts, references, templates) into the conversation context.",
|
|
276
|
+
"",
|
|
277
|
+
'Tool output includes a `<skill_content name="...">` block with the loaded content.',
|
|
278
|
+
"",
|
|
279
|
+
"The following skills provide specialized sets of instructions for particular tasks",
|
|
280
|
+
"Invoke this tool to load a skill when a task matches one of the available skills listed below:",
|
|
281
|
+
"",
|
|
282
|
+
Skill.fmt(list, { verbose: false }),
|
|
283
|
+
].join("\n")
|
|
284
|
+
})
|
|
285
|
+
|
|
286
|
+
const describeTask = Effect.fn("ToolRegistry.describeTask")(function* (agent: Agent.Info) {
|
|
287
|
+
const items = (yield* agents.list()).filter((item) => item.mode !== "primary")
|
|
288
|
+
const filtered = items.filter(
|
|
289
|
+
(item) => Permission.evaluate("task", item.name, agent.permission).action !== "deny",
|
|
290
|
+
)
|
|
291
|
+
const list = filtered.toSorted((a, b) => a.name.localeCompare(b.name))
|
|
292
|
+
const description = list
|
|
293
|
+
.map(
|
|
294
|
+
(item) =>
|
|
295
|
+
`- ${item.name}: ${item.description ?? "This subagent should only be called manually by the user."}`,
|
|
296
|
+
)
|
|
297
|
+
.join("\n")
|
|
298
|
+
return ["Available agent types and the tools they have access to:", description].join("\n")
|
|
299
|
+
})
|
|
300
|
+
|
|
301
|
+
const tools: Interface["tools"] = Effect.fn("ToolRegistry.tools")(function* (input) {
|
|
302
|
+
const filtered = (yield* all()).filter((tool) => {
|
|
303
|
+
if (tool.id === WebSearchTool.id) {
|
|
304
|
+
return input.providerID === ProviderID.saeeol || Flag.SAEEOL_ENABLE_EXA
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const usePatch =
|
|
308
|
+
!!process.env["SAEEOL_E2E_LLM_URL"] ||
|
|
309
|
+
(input.modelID.includes("gpt-") && !input.modelID.includes("oss") && !input.modelID.includes("gpt-4"))
|
|
310
|
+
if (tool.id === ApplyPatchTool.id) return usePatch
|
|
311
|
+
if (tool.id === EditTool.id) return !usePatch
|
|
312
|
+
|
|
313
|
+
return true
|
|
314
|
+
})
|
|
315
|
+
|
|
316
|
+
return yield* Effect.forEach(
|
|
317
|
+
filtered,
|
|
318
|
+
Effect.fnUntraced(function* (tool: Tool.Def) {
|
|
319
|
+
using _ = log.time(tool.id)
|
|
320
|
+
const output = {
|
|
321
|
+
description: tool.description,
|
|
322
|
+
parameters: tool.parameters,
|
|
323
|
+
}
|
|
324
|
+
yield* plugin.trigger("tool.definition", { toolID: tool.id }, output)
|
|
325
|
+
return {
|
|
326
|
+
id: tool.id,
|
|
327
|
+
description: [
|
|
328
|
+
output.description,
|
|
329
|
+
tool.id === TaskTool.id ? yield* describeTask(input.agent) : undefined,
|
|
330
|
+
tool.id === SkillTool.id ? yield* describeSkill(input.agent) : undefined,
|
|
331
|
+
]
|
|
332
|
+
.filter(Boolean)
|
|
333
|
+
.join("\n"),
|
|
334
|
+
parameters: output.parameters,
|
|
335
|
+
execute: tool.execute,
|
|
336
|
+
formatValidationError: tool.formatValidationError,
|
|
337
|
+
}
|
|
338
|
+
}),
|
|
339
|
+
{ concurrency: "unbounded" },
|
|
340
|
+
)
|
|
341
|
+
})
|
|
342
|
+
|
|
343
|
+
const named: Interface["named"] = Effect.fn("ToolRegistry.named")(function* () {
|
|
344
|
+
const s = yield* InstanceState.get(state)
|
|
345
|
+
return { task: s.task, read: s.read }
|
|
346
|
+
})
|
|
347
|
+
|
|
348
|
+
return Service.of({ ids, all, named, tools })
|
|
349
|
+
}),
|
|
350
|
+
)
|
|
351
|
+
|
|
352
|
+
export const defaultLayer = Layer.suspend(() =>
|
|
353
|
+
layer.pipe(
|
|
354
|
+
Layer.provide(Config.defaultLayer),
|
|
355
|
+
Layer.provide(Plugin.defaultLayer),
|
|
356
|
+
Layer.provide(Question.defaultLayer),
|
|
357
|
+
Layer.provide(Todo.defaultLayer),
|
|
358
|
+
Layer.provide(Skill.defaultLayer),
|
|
359
|
+
Layer.provide(Agent.defaultLayer),
|
|
360
|
+
Layer.provide(Session.defaultLayer),
|
|
361
|
+
Layer.provide(Provider.defaultLayer),
|
|
362
|
+
Layer.provide(LSP.defaultLayer),
|
|
363
|
+
Layer.provide(Instruction.defaultLayer),
|
|
364
|
+
Layer.provide(AppFileSystem.defaultLayer),
|
|
365
|
+
Layer.provide(Bus.layer),
|
|
366
|
+
Layer.provide(FetchHttpClient.layer),
|
|
367
|
+
Layer.provide(Format.defaultLayer),
|
|
368
|
+
Layer.provide(CrossSpawnSpawner.defaultLayer),
|
|
369
|
+
Layer.provide(Ripgrep.defaultLayer),
|
|
370
|
+
Layer.provide(Truncate.defaultLayer),
|
|
371
|
+
),
|
|
372
|
+
)
|
|
373
|
+
const { runPromise } = makeRuntime(Service, defaultLayer)
|
|
374
|
+
export const ids = () => runPromise((svc) => svc.ids())
|
|
375
|
+
export * as ToolRegistry from "./registry"
|
package/src/tool/invalid.ts
CHANGED
|
@@ -1,21 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import * as Tool from "./tool"
|
|
3
|
-
|
|
4
|
-
export const Parameters = Schema.Struct({
|
|
5
|
-
tool: Schema.String,
|
|
6
|
-
error: Schema.String,
|
|
7
|
-
})
|
|
8
|
-
|
|
9
|
-
export const InvalidTool = Tool.define(
|
|
10
|
-
"invalid",
|
|
11
|
-
Effect.succeed({
|
|
12
|
-
description: "Do not use",
|
|
13
|
-
parameters: Parameters,
|
|
14
|
-
execute: (params: { tool: string; error: string }) =>
|
|
15
|
-
Effect.succeed({
|
|
16
|
-
title: "Invalid Tool",
|
|
17
|
-
output: `The arguments provided to the tool are invalid: ${params.error}`,
|
|
18
|
-
metadata: {},
|
|
19
|
-
}),
|
|
20
|
-
}),
|
|
21
|
-
)
|
|
1
|
+
export * from "./core/invalid"
|
package/src/tool/lsp.ts
CHANGED
|
@@ -1,113 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import * as Tool from "./tool"
|
|
3
|
-
import path from "path"
|
|
4
|
-
import { LSP } from "@/lsp/lsp"
|
|
5
|
-
import DESCRIPTION from "./lsp.txt"
|
|
6
|
-
import { InstanceState } from "@/effect/instance-state"
|
|
7
|
-
import { pathToFileURL } from "url"
|
|
8
|
-
import { assertExternalDirectoryEffect } from "./external-directory"
|
|
9
|
-
import { AppFileSystem } from "@saeeol/core/filesystem"
|
|
10
|
-
|
|
11
|
-
const operations = [
|
|
12
|
-
"goToDefinition",
|
|
13
|
-
"findReferences",
|
|
14
|
-
"hover",
|
|
15
|
-
"documentSymbol",
|
|
16
|
-
"workspaceSymbol",
|
|
17
|
-
"goToImplementation",
|
|
18
|
-
"prepareCallHierarchy",
|
|
19
|
-
"incomingCalls",
|
|
20
|
-
"outgoingCalls",
|
|
21
|
-
] as const
|
|
22
|
-
|
|
23
|
-
export const Parameters = Schema.Struct({
|
|
24
|
-
operation: Schema.Literals(operations).annotate({ description: "The LSP operation to perform" }),
|
|
25
|
-
filePath: Schema.String.annotate({ description: "The absolute or relative path to the file" }),
|
|
26
|
-
line: Schema.Int.check(Schema.isGreaterThanOrEqualTo(1)).annotate({
|
|
27
|
-
description: "The line number (1-based, as shown in editors)",
|
|
28
|
-
}),
|
|
29
|
-
character: Schema.Int.check(Schema.isGreaterThanOrEqualTo(1)).annotate({
|
|
30
|
-
description: "The character offset (1-based, as shown in editors)",
|
|
31
|
-
}),
|
|
32
|
-
query: Schema.optional(Schema.String).annotate({
|
|
33
|
-
description: "Search query for workspaceSymbol. Empty string requests all symbols.",
|
|
34
|
-
}),
|
|
35
|
-
})
|
|
36
|
-
|
|
37
|
-
export const LspTool = Tool.define(
|
|
38
|
-
"lsp",
|
|
39
|
-
Effect.gen(function* () {
|
|
40
|
-
const lsp = yield* LSP.Service
|
|
41
|
-
const fs = yield* AppFileSystem.Service
|
|
42
|
-
return {
|
|
43
|
-
description: DESCRIPTION,
|
|
44
|
-
parameters: Parameters,
|
|
45
|
-
execute: (args: Schema.Schema.Type<typeof Parameters>, ctx: Tool.Context) =>
|
|
46
|
-
Effect.gen(function* () {
|
|
47
|
-
const instance = yield* InstanceState.context
|
|
48
|
-
const file = path.isAbsolute(args.filePath) ? args.filePath : path.join(instance.directory, args.filePath)
|
|
49
|
-
yield* assertExternalDirectoryEffect(ctx, file)
|
|
50
|
-
const meta =
|
|
51
|
-
args.operation === "workspaceSymbol"
|
|
52
|
-
? { operation: args.operation }
|
|
53
|
-
: args.operation === "documentSymbol"
|
|
54
|
-
? { operation: args.operation, filePath: file }
|
|
55
|
-
: { operation: args.operation, filePath: file, line: args.line, character: args.character }
|
|
56
|
-
yield* ctx.ask({
|
|
57
|
-
permission: "lsp",
|
|
58
|
-
patterns: ["*"],
|
|
59
|
-
always: ["*"],
|
|
60
|
-
metadata: meta,
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
const uri = pathToFileURL(file).href
|
|
64
|
-
const position = { file, line: args.line - 1, character: args.character - 1 }
|
|
65
|
-
const relPath = path.relative(instance.worktree, file)
|
|
66
|
-
const detail =
|
|
67
|
-
args.operation === "workspaceSymbol"
|
|
68
|
-
? ""
|
|
69
|
-
: args.operation === "documentSymbol"
|
|
70
|
-
? relPath
|
|
71
|
-
: `${relPath}:${args.line}:${args.character}`
|
|
72
|
-
const title = detail ? `${args.operation} ${detail}` : args.operation
|
|
73
|
-
|
|
74
|
-
const exists = yield* fs.existsSafe(file)
|
|
75
|
-
if (!exists) throw new Error(`File not found: ${file}`)
|
|
76
|
-
|
|
77
|
-
const available = yield* lsp.hasClients(file)
|
|
78
|
-
if (!available) throw new Error("No LSP server available for this file type.")
|
|
79
|
-
|
|
80
|
-
yield* lsp.touchFile(file, "document")
|
|
81
|
-
|
|
82
|
-
const result: unknown[] = yield* (() => {
|
|
83
|
-
switch (args.operation) {
|
|
84
|
-
case "goToDefinition":
|
|
85
|
-
return lsp.definition(position)
|
|
86
|
-
case "findReferences":
|
|
87
|
-
return lsp.references(position)
|
|
88
|
-
case "hover":
|
|
89
|
-
return lsp.hover(position)
|
|
90
|
-
case "documentSymbol":
|
|
91
|
-
return lsp.documentSymbol(uri)
|
|
92
|
-
case "workspaceSymbol":
|
|
93
|
-
return lsp.workspaceSymbol(args.query ?? "")
|
|
94
|
-
case "goToImplementation":
|
|
95
|
-
return lsp.implementation(position)
|
|
96
|
-
case "prepareCallHierarchy":
|
|
97
|
-
return lsp.prepareCallHierarchy(position)
|
|
98
|
-
case "incomingCalls":
|
|
99
|
-
return lsp.incomingCalls(position)
|
|
100
|
-
case "outgoingCalls":
|
|
101
|
-
return lsp.outgoingCalls(position)
|
|
102
|
-
}
|
|
103
|
-
})()
|
|
104
|
-
|
|
105
|
-
return {
|
|
106
|
-
title,
|
|
107
|
-
metadata: { result },
|
|
108
|
-
output: result.length === 0 ? `No results found for ${args.operation}` : JSON.stringify(result, null, 2),
|
|
109
|
-
}
|
|
110
|
-
}).pipe(Effect.orDie),
|
|
111
|
-
}
|
|
112
|
-
}),
|
|
113
|
-
)
|
|
1
|
+
export * from "./integration/lsp"
|