cursor-opencode-provider 0.8.0 → 0.8.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/CHANGELOG.md +174 -0
- package/README.md +230 -44
- package/SECURITY.md +60 -12
- package/dist/activity.d.ts +73 -1
- package/dist/activity.js +231 -5
- package/dist/agent-url.js +37 -9
- package/dist/auth-renewal.d.ts +1 -1
- package/dist/auth-renewal.js +58 -26
- package/dist/billing.d.ts +105 -0
- package/dist/billing.js +212 -0
- package/dist/classic-tool-results.d.ts +1 -0
- package/dist/classic-tool-results.js +42 -0
- package/dist/context/build.d.ts +23 -4
- package/dist/context/build.js +65 -10
- package/dist/context/dynamic-catalog.js +6 -1
- package/dist/context/env.js +27 -3
- package/dist/context/epoch.js +9 -5
- package/dist/context/frozen.js +6 -2
- package/dist/context/git.d.ts +5 -2
- package/dist/context/git.js +17 -7
- package/dist/context/index.d.ts +2 -0
- package/dist/context/index.js +2 -0
- package/dist/context/paths.d.ts +45 -18
- package/dist/context/paths.js +72 -22
- package/dist/context/skills-bridge.d.ts +22 -0
- package/dist/context/skills-bridge.js +20 -0
- package/dist/context/skills.d.ts +34 -0
- package/dist/context/skills.js +211 -0
- package/dist/debug.js +13 -2
- package/dist/early-tool-rows.d.ts +60 -0
- package/dist/early-tool-rows.js +96 -0
- package/dist/errors.d.ts +49 -7
- package/dist/errors.js +163 -8
- package/dist/host-agent-mode.d.ts +53 -4
- package/dist/host-agent-mode.js +117 -13
- package/dist/host-interrupt.d.ts +12 -0
- package/dist/host-interrupt.js +18 -0
- package/dist/host-plan-file.d.ts +28 -0
- package/dist/host-plan-file.js +76 -0
- package/dist/host-steer.d.ts +71 -0
- package/dist/host-steer.js +232 -0
- package/dist/image-input.d.ts +18 -0
- package/dist/image-input.js +122 -30
- package/dist/image-save.d.ts +10 -2
- package/dist/image-save.js +33 -21
- package/dist/index.d.ts +2 -2
- package/dist/language-model.d.ts +149 -30
- package/dist/language-model.js +3127 -673
- package/dist/lost-request.d.ts +39 -0
- package/dist/lost-request.js +111 -0
- package/dist/model-config.d.ts +12 -1
- package/dist/model-config.js +78 -30
- package/dist/models.d.ts +26 -3
- package/dist/models.js +101 -45
- package/dist/opencode2/catalog.d.ts +1 -0
- package/dist/opencode2/catalog.js +2 -0
- package/dist/opencode2/image-save-tool.d.ts +8 -0
- package/dist/opencode2/image-save-tool.js +84 -0
- package/dist/opencode2/tool-results.d.ts +18 -0
- package/dist/opencode2/tool-results.js +66 -0
- package/dist/opencode2/types.d.ts +15 -0
- package/dist/parallel-step.d.ts +65 -0
- package/dist/parallel-step.js +102 -0
- package/dist/plugin-opencode2.js +233 -65
- package/dist/plugin.js +97 -23
- package/dist/pricing-data.d.ts +40 -3
- package/dist/pricing-data.js +40 -3
- package/dist/pricing.d.ts +1 -1
- package/dist/pricing.js +7 -4
- package/dist/process-shared.d.ts +14 -0
- package/dist/process-shared.js +18 -0
- package/dist/protocol/apply-patch.d.ts +25 -13
- package/dist/protocol/apply-patch.js +79 -30
- package/dist/protocol/ask-question.d.ts +12 -1
- package/dist/protocol/ask-question.js +77 -3
- package/dist/protocol/blob-store.d.ts +5 -1
- package/dist/protocol/blob-store.js +10 -6
- package/dist/protocol/client-version.js +6 -4
- package/dist/protocol/conversation-cache-header.d.ts +23 -0
- package/dist/protocol/conversation-cache-header.js +149 -0
- package/dist/protocol/conversation-persistence.d.ts +27 -1
- package/dist/protocol/conversation-persistence.js +628 -126
- package/dist/protocol/conversation-state.d.ts +8 -1
- package/dist/protocol/conversation-state.js +28 -7
- package/dist/protocol/create-plan.d.ts +47 -25
- package/dist/protocol/create-plan.js +33 -23
- package/dist/protocol/host-notes.d.ts +31 -0
- package/dist/protocol/host-notes.js +85 -0
- package/dist/protocol/interactions.d.ts +26 -17
- package/dist/protocol/interactions.js +80 -31
- package/dist/protocol/messages.js +164 -21
- package/dist/protocol/request.d.ts +40 -19
- package/dist/protocol/request.js +55 -28
- package/dist/protocol/sent-history-images.d.ts +49 -0
- package/dist/protocol/sent-history-images.js +102 -0
- package/dist/protocol/switch-mode.d.ts +66 -27
- package/dist/protocol/switch-mode.js +147 -73
- package/dist/protocol/token-details.d.ts +2 -0
- package/dist/protocol/token-details.js +11 -1
- package/dist/protocol/tool-call-bridge.d.ts +6 -0
- package/dist/protocol/tool-call-bridge.js +24 -0
- package/dist/protocol/tools.d.ts +90 -9
- package/dist/protocol/tools.js +543 -151
- package/dist/protocol/turn-provenance.d.ts +19 -4
- package/dist/protocol/turn-provenance.js +77 -20
- package/dist/replay-safety.d.ts +22 -4
- package/dist/replay-safety.js +86 -27
- package/dist/retry-budget.d.ts +21 -0
- package/dist/retry-budget.js +94 -0
- package/dist/runtime-files.d.ts +23 -0
- package/dist/runtime-files.js +581 -0
- package/dist/session.d.ts +129 -10
- package/dist/session.js +198 -35
- package/dist/shared.d.ts +1 -1
- package/dist/shared.js +1 -1
- package/dist/shell-timeout.d.ts +54 -10
- package/dist/shell-timeout.js +362 -92
- package/dist/stream-write.d.ts +20 -0
- package/dist/stream-write.js +87 -0
- package/dist/terminal-runner.d.ts +15 -0
- package/dist/terminal-runner.js +528 -0
- package/dist/todo-store.d.ts +2 -0
- package/dist/todo-store.js +19 -0
- package/dist/transport/connect.d.ts +10 -1
- package/dist/transport/connect.js +124 -27
- package/dist/usage.d.ts +37 -17
- package/dist/usage.js +85 -34
- package/package.json +5 -3
- package/dist/plan-execution-kickoff.d.ts +0 -57
- package/dist/plan-execution-kickoff.js +0 -186
- package/dist/protocol/thinking.d.ts +0 -15
- package/dist/protocol/thinking.js +0 -17
package/dist/context/build.js
CHANGED
|
@@ -7,7 +7,8 @@ import { collectProjectLayout } from "./layout.js";
|
|
|
7
7
|
import { buildEnv } from "./env.js";
|
|
8
8
|
import { ensureOpencodeProjectDir } from "./paths.js";
|
|
9
9
|
import { holdCapabilityOverlay } from "./overlay.js";
|
|
10
|
-
import {
|
|
10
|
+
import { agentSkillsForCursor, applyAgentSkillsToContext, hostSkillFiles, loadBridgeSkills, skillToolAdvertised, } from "./skills.js";
|
|
11
|
+
import { trace, traceRequestContextPaths } from "../debug.js";
|
|
11
12
|
/** Rule name Cursor shows for the host system context (not a file). */
|
|
12
13
|
export const SYSTEM_INSTRUCTIONS_RULE_PATH = "OpenCode system instructions";
|
|
13
14
|
/**
|
|
@@ -74,8 +75,8 @@ export const DYNAMIC_REQUEST_CONTEXT_KEYS = [
|
|
|
74
75
|
"hooks_additional_context",
|
|
75
76
|
];
|
|
76
77
|
/**
|
|
77
|
-
*
|
|
78
|
-
*
|
|
78
|
+
* Derived from the system-instructions rule on every materialization (see
|
|
79
|
+
* `skills.ts`); never kept on a frozen base.
|
|
79
80
|
*/
|
|
80
81
|
export const HOST_DUPLICATED_REQUEST_CONTEXT_KEYS = [
|
|
81
82
|
"agent_skills",
|
|
@@ -94,6 +95,34 @@ function stripHostDuplicatedRequestContextFields(context) {
|
|
|
94
95
|
delete context[key];
|
|
95
96
|
keepSystemInstructionsRule(context);
|
|
96
97
|
}
|
|
98
|
+
/**
|
|
99
|
+
* How long one workspace's git and layout discovery serves later builds. A new
|
|
100
|
+
* session's first message opens two Runs moments apart (OpenCode's title
|
|
101
|
+
* request and the turn itself) on separate conversations; each froze its own
|
|
102
|
+
* copy, and `git status` dominates on a large repository (1.3 s warm on 35k
|
|
103
|
+
* tracked files; both discoveries of one observed session took about 5 s).
|
|
104
|
+
* Each conversation freezes what it got anyway.
|
|
105
|
+
*/
|
|
106
|
+
const WORKSPACE_FACTS_REUSE_MS = 30_000;
|
|
107
|
+
const workspaceFacts = new Map();
|
|
108
|
+
function collectWorkspaceFacts(workspaceRoot) {
|
|
109
|
+
const now = Date.now();
|
|
110
|
+
for (const [root, entry] of workspaceFacts) {
|
|
111
|
+
if (now - entry.at >= WORKSPACE_FACTS_REUSE_MS)
|
|
112
|
+
workspaceFacts.delete(root);
|
|
113
|
+
}
|
|
114
|
+
const cached = workspaceFacts.get(workspaceRoot);
|
|
115
|
+
if (cached) {
|
|
116
|
+
trace(`workspace facts: reused ageMs=${now - cached.at}`);
|
|
117
|
+
return cached.facts;
|
|
118
|
+
}
|
|
119
|
+
const facts = Promise.all([collectGit(workspaceRoot), collectProjectLayout(workspaceRoot)]);
|
|
120
|
+
workspaceFacts.set(workspaceRoot, { at: now, facts });
|
|
121
|
+
return facts;
|
|
122
|
+
}
|
|
123
|
+
export function resetWorkspaceFactsForTests() {
|
|
124
|
+
workspaceFacts.clear();
|
|
125
|
+
}
|
|
97
126
|
/**
|
|
98
127
|
* Full RequestContext payload for live UMA + exec #10 reply.
|
|
99
128
|
* Workspace env/git/layout, the host system-instructions rule, and
|
|
@@ -103,10 +132,9 @@ function stripHostDuplicatedRequestContextFields(context) {
|
|
|
103
132
|
export async function buildRequestContext(input) {
|
|
104
133
|
const workspaceRoot = path.resolve(input.workspaceRoot || process.cwd());
|
|
105
134
|
const config = input.mergedConfig ?? await loadMergedConfig(workspaceRoot);
|
|
106
|
-
const [dynamic, git, layout] = await Promise.all([
|
|
135
|
+
const [dynamic, [git, layout]] = await Promise.all([
|
|
107
136
|
buildDynamicRequestContextFromDiscovery(input, workspaceRoot, config),
|
|
108
|
-
|
|
109
|
-
collectProjectLayout(workspaceRoot),
|
|
137
|
+
collectWorkspaceFacts(workspaceRoot),
|
|
110
138
|
]);
|
|
111
139
|
const workspace = {
|
|
112
140
|
env: buildEnv(workspaceRoot),
|
|
@@ -117,13 +145,29 @@ export async function buildRequestContext(input) {
|
|
|
117
145
|
env_info_complete: true,
|
|
118
146
|
repository_info_complete: true,
|
|
119
147
|
git_repo_info_complete: true,
|
|
120
|
-
git_status_info_complete:
|
|
148
|
+
git_status_info_complete: git.statusComplete,
|
|
121
149
|
};
|
|
122
150
|
const base = withSystemInstructions(workspace, input.systemInstructions);
|
|
123
|
-
const
|
|
151
|
+
const skillLocations = await resolveSkillLocations(input, workspaceRoot);
|
|
152
|
+
const ctx = materializeRequestContext(base, dynamic, {
|
|
153
|
+
tools: input.tools,
|
|
154
|
+
...skillLocations,
|
|
155
|
+
});
|
|
124
156
|
traceRequestContextPaths("buildRequestContext", ctx);
|
|
125
157
|
return ctx;
|
|
126
158
|
}
|
|
159
|
+
/** Locations for path-desc materialization (bridge, then remembered OC2 files). */
|
|
160
|
+
export async function resolveSkillLocations(input, workspaceRoot) {
|
|
161
|
+
if (!skillToolAdvertised(input.tools))
|
|
162
|
+
return {};
|
|
163
|
+
return {
|
|
164
|
+
bridgeSkills: await loadBridgeSkills({
|
|
165
|
+
workspaceRoot,
|
|
166
|
+
sessionID: input.sessionID,
|
|
167
|
+
}),
|
|
168
|
+
skillFiles: hostSkillFiles(workspaceRoot),
|
|
169
|
+
};
|
|
170
|
+
}
|
|
127
171
|
async function buildDynamicRequestContextFromDiscovery(input, workspaceRoot, config) {
|
|
128
172
|
const providerIdentifier = input.providerIdentifier ?? "opencode";
|
|
129
173
|
const tools = input.tools ?? [];
|
|
@@ -184,8 +228,13 @@ export async function buildDynamicRequestContext(input) {
|
|
|
184
228
|
: await loadMergedConfig(workspaceRoot);
|
|
185
229
|
return buildDynamicRequestContextFromDiscovery(input, workspaceRoot, config);
|
|
186
230
|
}
|
|
187
|
-
/**
|
|
188
|
-
|
|
231
|
+
/**
|
|
232
|
+
* Keep expensive workspace state frozen while replacing every live capability
|
|
233
|
+
* field. Skill locations (bridge / OpenCode 2 files / OpenCode 1 catalog
|
|
234
|
+
* paths) turn the skill catalog in the system-instructions rule into Cursor's
|
|
235
|
+
* path-desc `agent_skills`.
|
|
236
|
+
*/
|
|
237
|
+
export function materializeRequestContext(base, dynamic, options) {
|
|
189
238
|
const context = structuredClone(base);
|
|
190
239
|
stripHostDuplicatedRequestContextFields(context);
|
|
191
240
|
for (const key of DYNAMIC_REQUEST_CONTEXT_KEYS)
|
|
@@ -194,6 +243,12 @@ export function materializeRequestContext(base, dynamic) {
|
|
|
194
243
|
if (Object.hasOwn(dynamic, key))
|
|
195
244
|
context[key] = structuredClone(dynamic[key]);
|
|
196
245
|
}
|
|
246
|
+
const skills = agentSkillsForCursor(systemInstructionsRuleText(context), {
|
|
247
|
+
skillToolAdvertised: skillToolAdvertised(options?.tools),
|
|
248
|
+
skillFiles: options?.skillFiles,
|
|
249
|
+
bridgeSkills: options?.bridgeSkills,
|
|
250
|
+
});
|
|
251
|
+
applyAgentSkillsToContext(context, skills);
|
|
197
252
|
return context;
|
|
198
253
|
}
|
|
199
254
|
/** Strip live capability fields before retaining/persisting a conversation base. */
|
|
@@ -64,7 +64,12 @@ export function buildDynamicCatalogRoutingInstruction(options) {
|
|
|
64
64
|
`- OpenCode host tools that are not in Cursor's native top-level list (including ${extras.join(" and ")}) ` +
|
|
65
65
|
"are reached through GetDynamicTools / CallDynamicTool (or the host's equivalent dynamic catalog). " +
|
|
66
66
|
"When a skill matches or project rules name an MCP server, discover and call those tools that way " +
|
|
67
|
-
"before Grep/Shell fallbacks. Do not narrate that they are unavailable."
|
|
67
|
+
"before Grep/Shell fallbacks. Do not narrate that they are unavailable. " +
|
|
68
|
+
// Cursor's server answers lookups and calls in its own `cursor` namespace
|
|
69
|
+
// (built-in tools such as GenerateImage) and rejects any other name there.
|
|
70
|
+
`Host tools are in namespace \`${DEFAULT_TOOL_SERVER}\`` +
|
|
71
|
+
(mcpServers.length > 0 ? " and MCP tools in their server's namespace" : "") +
|
|
72
|
+
"; namespace `cursor` holds only Cursor's built-in tools.",
|
|
68
73
|
];
|
|
69
74
|
if (hasSkill) {
|
|
70
75
|
// Mirror OpenCode 1 SystemPrompt.skills / OC2 SkillInstructions.render.
|
package/dist/context/env.js
CHANGED
|
@@ -1,7 +1,21 @@
|
|
|
1
|
+
import { statSync } from "node:fs";
|
|
1
2
|
import { homedir } from "node:os";
|
|
2
3
|
import path from "node:path";
|
|
3
4
|
import { trace } from "../debug.js";
|
|
4
5
|
import { ensureOpencodeProjectDir } from "./paths.js";
|
|
6
|
+
import { maintainRuntimeFiles, runtimeLayout } from "../runtime-files.js";
|
|
7
|
+
function agentTranscriptsFolder(workspaceRoot) {
|
|
8
|
+
const configured = process.env.CURSOR_AGENT_TRANSCRIPTS_FOLDER;
|
|
9
|
+
if (!configured?.trim())
|
|
10
|
+
return undefined;
|
|
11
|
+
const folder = path.resolve(workspaceRoot, configured);
|
|
12
|
+
try {
|
|
13
|
+
return statSync(folder).isDirectory() ? folder : undefined;
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
5
19
|
export function buildEnv(workspaceRoot) {
|
|
6
20
|
const cwd = path.resolve(workspaceRoot);
|
|
7
21
|
let timeZone = "UTC";
|
|
@@ -20,6 +34,16 @@ export function buildEnv(workspaceRoot) {
|
|
|
20
34
|
// Cursor's project_folder is a metadata root (agent-tools, terminals, …),
|
|
21
35
|
// not the git workspace. Keep dumps under OpenCode cache.
|
|
22
36
|
const projectFolder = ensureOpencodeProjectDir(cwd);
|
|
37
|
+
let terminalsFolder;
|
|
38
|
+
try {
|
|
39
|
+
const layout = runtimeLayout(projectFolder, true);
|
|
40
|
+
terminalsFolder = layout.terminals;
|
|
41
|
+
maintainRuntimeFiles(layout);
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
trace(`runtime folders unavailable: ${error instanceof Error ? error.message : String(error)}`);
|
|
45
|
+
}
|
|
46
|
+
const transcriptsFolder = agentTranscriptsFolder(cwd);
|
|
23
47
|
// process_working_directory must match the workspace, not the host process
|
|
24
48
|
// cwd. OpenCode 2.0's daemon is long-lived and often started from $HOME, so
|
|
25
49
|
// process.cwd() would advertise the home folder to Cursor and the model would
|
|
@@ -32,15 +56,15 @@ export function buildEnv(workspaceRoot) {
|
|
|
32
56
|
sandbox_supported: false,
|
|
33
57
|
time_zone: timeZone,
|
|
34
58
|
project_folder: projectFolder,
|
|
35
|
-
terminals_folder:
|
|
36
|
-
agent_transcripts_folder:
|
|
59
|
+
...(terminalsFolder ? { terminals_folder: terminalsFolder } : {}),
|
|
60
|
+
...(transcriptsFolder ? { agent_transcripts_folder: transcriptsFolder } : {}),
|
|
37
61
|
process_working_directory: cwd,
|
|
38
62
|
is_working_dir_home_dir: cwd === path.resolve(home),
|
|
39
63
|
};
|
|
40
64
|
trace(`buildEnv: workspace_paths=${JSON.stringify(env.workspace_paths)} ` +
|
|
41
65
|
`project_folder=${env.project_folder} ` +
|
|
42
66
|
`terminals_folder=${env.terminals_folder} ` +
|
|
43
|
-
`agent_transcripts_folder=${env.agent_transcripts_folder} ` +
|
|
67
|
+
`agent_transcripts_folder=${env.agent_transcripts_folder ?? "(none)"} ` +
|
|
44
68
|
`process_working_directory=${env.process_working_directory}`);
|
|
45
69
|
return env;
|
|
46
70
|
}
|
package/dist/context/epoch.js
CHANGED
|
@@ -153,10 +153,13 @@ export function admitContextEpoch(input) {
|
|
|
153
153
|
workspaceRoot: nextSnap.workspaceRoot || existing.snapshot.workspaceRoot,
|
|
154
154
|
};
|
|
155
155
|
const updates = renderSourceUpdates(existing.snapshot, observedSnap, input);
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
156
|
+
const changedSources = [
|
|
157
|
+
existing.snapshot.hostSystemHash !== observedSnap.hostSystemHash ? "hostSystem" : "",
|
|
158
|
+
existing.snapshot.guidanceHash !== observedSnap.guidanceHash ? "guidance" : "",
|
|
159
|
+
existing.snapshot.hostAgent !== observedSnap.hostAgent ? "hostAgent" : "",
|
|
160
|
+
existing.snapshot.workspaceRoot !== observedSnap.workspaceRoot ? "workspaceRoot" : "",
|
|
161
|
+
].filter(Boolean);
|
|
162
|
+
const changed = changedSources.length > 0;
|
|
160
163
|
if (changed) {
|
|
161
164
|
existing.snapshot = observedSnap;
|
|
162
165
|
}
|
|
@@ -165,7 +168,8 @@ export function admitContextEpoch(input) {
|
|
|
165
168
|
const midConversationMessage = combineMessages([...updates, ...oneShots]);
|
|
166
169
|
if (changed || oneShots.length) {
|
|
167
170
|
trace(`context epoch: ${changed ? "updated" : "unchanged"} conversationId=${conversationId} ` +
|
|
168
|
-
`checkpoint=1 updates=${updates.length} oneShots=${oneShots.length}`
|
|
171
|
+
`checkpoint=1 updates=${updates.length} oneShots=${oneShots.length}` +
|
|
172
|
+
(changedSources.length ? ` changed=[${changedSources.join(",")}]` : ""));
|
|
169
173
|
}
|
|
170
174
|
return {
|
|
171
175
|
action: changed ? "updated" : "unchanged",
|
package/dist/context/frozen.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { buildDynamicRequestContext, buildRequestContext, materializeRequestContext, requestContextBase, withSystemInstructions, } from "./build.js";
|
|
1
|
+
import { buildDynamicRequestContext, buildRequestContext, materializeRequestContext, requestContextBase, resolveSkillLocations, withSystemInstructions, } from "./build.js";
|
|
2
2
|
import { clearContextEpoch, endContextEpoch, resetContextEpochsForTests } from "./epoch.js";
|
|
3
3
|
import { clearOverlayHold, resetOverlayHoldsForTests, transferOverlayHold, } from "./overlay.js";
|
|
4
4
|
import { trace } from "../debug.js";
|
|
@@ -174,7 +174,11 @@ export async function getOrBuildRequestContext(conversationId, input, opts) {
|
|
|
174
174
|
`authoritative=${input.systemInstructions?.authoritative ?? false}`);
|
|
175
175
|
}
|
|
176
176
|
const dynamic = await buildDynamicRequestContext(scoped);
|
|
177
|
-
const
|
|
177
|
+
const skillLocations = await resolveSkillLocations(scoped, scoped.workspaceRoot);
|
|
178
|
+
const materialized = rememberMaterialized(conversationId, materializeRequestContext(base, dynamic, {
|
|
179
|
+
tools: scoped.tools,
|
|
180
|
+
...skillLocations,
|
|
181
|
+
}));
|
|
178
182
|
trace(`request_context: materialized conversationId=${conversationId} ` +
|
|
179
183
|
`tools=${advertisedMetaToolCount(materialized.context)} ` +
|
|
180
184
|
`reused=${materialized.reused}`);
|
package/dist/context/git.d.ts
CHANGED
|
@@ -14,7 +14,10 @@ export type GitRepoInfo = {
|
|
|
14
14
|
branch_name: string;
|
|
15
15
|
remote_url?: string;
|
|
16
16
|
};
|
|
17
|
-
export
|
|
17
|
+
export type GitFacts = {
|
|
18
18
|
repositoryInfo: RepoInfo[];
|
|
19
19
|
gitRepos: GitRepoInfo[];
|
|
20
|
-
|
|
20
|
+
/** False when `git status` failed or timed out, so the status is not the real one. */
|
|
21
|
+
statusComplete: boolean;
|
|
22
|
+
};
|
|
23
|
+
export declare function collectGit(workspaceRoot: string): Promise<GitFacts>;
|
package/dist/context/git.js
CHANGED
|
@@ -1,21 +1,29 @@
|
|
|
1
1
|
import { execFile } from "node:child_process";
|
|
2
2
|
import { promisify } from "node:util";
|
|
3
3
|
import path from "node:path";
|
|
4
|
+
import { errorMessage, trace } from "../debug.js";
|
|
4
5
|
const execFileAsync = promisify(execFile);
|
|
6
|
+
const GIT_TIMEOUT_MS = 5_000;
|
|
7
|
+
/** Trimmed stdout, or `undefined` when git failed or timed out. */
|
|
5
8
|
async function git(cwd, args) {
|
|
9
|
+
const started = Date.now();
|
|
6
10
|
try {
|
|
7
|
-
const { stdout } = await execFileAsync("git", args, { cwd, encoding: "utf-8", timeout:
|
|
11
|
+
const { stdout } = await execFileAsync("git", args, { cwd, encoding: "utf-8", timeout: GIT_TIMEOUT_MS });
|
|
8
12
|
return stdout.trim();
|
|
9
13
|
}
|
|
10
|
-
catch {
|
|
11
|
-
|
|
14
|
+
catch (error) {
|
|
15
|
+
const killed = error.killed === true;
|
|
16
|
+
trace(`git: \`${args.join(" ")}\` ${killed ? `timed out after ${GIT_TIMEOUT_MS}ms` : "failed"} ` +
|
|
17
|
+
`elapsedMs=${Date.now() - started}${killed ? "" : `: ${errorMessage(error).split("\n")[0]}`}`);
|
|
18
|
+
return undefined;
|
|
12
19
|
}
|
|
13
20
|
}
|
|
14
21
|
export async function collectGit(workspaceRoot) {
|
|
22
|
+
const started = Date.now();
|
|
15
23
|
const root = await git(workspaceRoot, ["rev-parse", "--show-toplevel"]);
|
|
16
24
|
if (!root)
|
|
17
|
-
return { repositoryInfo: [], gitRepos: [] };
|
|
18
|
-
const remotesRaw = await git(root, ["remote", "-v"]);
|
|
25
|
+
return { repositoryInfo: [], gitRepos: [], statusComplete: true };
|
|
26
|
+
const remotesRaw = (await git(root, ["remote", "-v"])) ?? "";
|
|
19
27
|
const remote_urls = [];
|
|
20
28
|
const remote_names = [];
|
|
21
29
|
for (const line of remotesRaw.split("\n")) {
|
|
@@ -34,7 +42,9 @@ export async function collectGit(workspaceRoot) {
|
|
|
34
42
|
repo_name = gh[2];
|
|
35
43
|
}
|
|
36
44
|
const branch = (await git(root, ["rev-parse", "--abbrev-ref", "HEAD"])) || "HEAD";
|
|
45
|
+
const statusStarted = Date.now();
|
|
37
46
|
const status = await git(root, ["status", "--porcelain", "-b"]);
|
|
47
|
+
trace(`git: discovery elapsedMs=${Date.now() - started} statusMs=${Date.now() - statusStarted} statusComplete=${status !== undefined}`);
|
|
38
48
|
const repositoryInfo = [
|
|
39
49
|
{
|
|
40
50
|
relative_workspace_path: ".",
|
|
@@ -50,10 +60,10 @@ export async function collectGit(workspaceRoot) {
|
|
|
50
60
|
const gitRepos = [
|
|
51
61
|
{
|
|
52
62
|
path: root,
|
|
53
|
-
status: status.slice(0, 4000),
|
|
63
|
+
status: (status ?? "").slice(0, 4000),
|
|
54
64
|
branch_name: branch,
|
|
55
65
|
...(primary ? { remote_url: primary } : {}),
|
|
56
66
|
},
|
|
57
67
|
];
|
|
58
|
-
return { repositoryInfo, gitRepos };
|
|
68
|
+
return { repositoryInfo, gitRepos, statusComplete: status !== undefined };
|
|
59
69
|
}
|
package/dist/context/index.d.ts
CHANGED
|
@@ -4,3 +4,5 @@ export { clearFrozenRequestContext, getFrozenRequestContext, getOrBuildRequestCo
|
|
|
4
4
|
export { MAX_OVERLAY_HOLDS, holdCapabilityOverlay, resetOverlayHoldsForTests, } from "./overlay.js";
|
|
5
5
|
export { admitContextEpoch, appendMidConversationMessage, clearContextEpoch, endContextEpoch, getContextEpoch, MAX_CONTEXT_EPOCHS, resetContextEpochsForTests, type AdmitContextEpochInput, type AdmitContextEpochResult, type ContextEpoch, type ContextSourceSnapshot, } from "./epoch.js";
|
|
6
6
|
export { HOST_PATH_BRIDGE, getHostCacheDirOverride, opencodeGlobalCacheDir, opencodeGlobalConfigDir, opencodeGlobalDataDir, hostGlobalDataDir, resolveHostCacheDir, setHostCacheDirOverride, } from "./paths.js";
|
|
7
|
+
export { HOST_SKILLS_BRIDGE, setHostSkillsBridgeForTests, type HostSkill, type OpenCodeSkillsBridge, } from "./skills-bridge.js";
|
|
8
|
+
export { agentSkillsForCursor, hostSkillFiles, rememberHostSkillFiles, resetHostSkillFilesForTests, skillToolAdvertised, } from "./skills.js";
|
package/dist/context/index.js
CHANGED
|
@@ -4,3 +4,5 @@ export { clearFrozenRequestContext, getFrozenRequestContext, getOrBuildRequestCo
|
|
|
4
4
|
export { MAX_OVERLAY_HOLDS, holdCapabilityOverlay, resetOverlayHoldsForTests, } from "./overlay.js";
|
|
5
5
|
export { admitContextEpoch, appendMidConversationMessage, clearContextEpoch, endContextEpoch, getContextEpoch, MAX_CONTEXT_EPOCHS, resetContextEpochsForTests, } from "./epoch.js";
|
|
6
6
|
export { HOST_PATH_BRIDGE, getHostCacheDirOverride, opencodeGlobalCacheDir, opencodeGlobalConfigDir, opencodeGlobalDataDir, hostGlobalDataDir, resolveHostCacheDir, setHostCacheDirOverride, } from "./paths.js";
|
|
7
|
+
export { HOST_SKILLS_BRIDGE, setHostSkillsBridgeForTests, } from "./skills-bridge.js";
|
|
8
|
+
export { agentSkillsForCursor, hostSkillFiles, rememberHostSkillFiles, resetHostSkillFilesForTests, skillToolAdvertised, } from "./skills.js";
|
package/dist/context/paths.d.ts
CHANGED
|
@@ -9,7 +9,31 @@ export type OpenCodePathBridge = {
|
|
|
9
9
|
/** Optional host-owned cache root; absent means native OpenCode defaults. */
|
|
10
10
|
globalCacheDir?: () => string;
|
|
11
11
|
configFileNames?: string[];
|
|
12
|
+
/**
|
|
13
|
+
* Optional host plan file for a session (the host's own plan location, which
|
|
14
|
+
* its `plan_exit` review reads). Absent on an installed bridge means the host
|
|
15
|
+
* defines none; without a bridge OpenCode's own location applies.
|
|
16
|
+
*/
|
|
17
|
+
planFile?: (input: {
|
|
18
|
+
worktree: string;
|
|
19
|
+
vcs: boolean;
|
|
20
|
+
created: number;
|
|
21
|
+
slug: string;
|
|
22
|
+
}) => string | undefined;
|
|
12
23
|
};
|
|
24
|
+
/**
|
|
25
|
+
* The plan file of a session: OpenCode's own `Session.plan` location
|
|
26
|
+
* (`<worktree>/.opencode/plans` in a VCS project, else `<data>/plans`, named
|
|
27
|
+
* `<created>-<slug>.md`), which its plan agent and `plan_exit` use. An injected
|
|
28
|
+
* host path bridge owns host paths, so with one installed only its `planFile`
|
|
29
|
+
* defines the location.
|
|
30
|
+
*/
|
|
31
|
+
export declare function hostPlanFilePath(input: {
|
|
32
|
+
worktree: string;
|
|
33
|
+
vcs: boolean;
|
|
34
|
+
created: number;
|
|
35
|
+
slug: string;
|
|
36
|
+
}, env?: HostPathEnv): string | undefined;
|
|
13
37
|
export declare function opencodeProjectConfigDirs(workspaceRoot: string): string[];
|
|
14
38
|
export declare function opencodeGlobalConfigDirs(): string[];
|
|
15
39
|
export declare function opencodeConfigFileNames(): string[];
|
|
@@ -41,25 +65,28 @@ export declare function opencodeGlobalDataDir(env?: HostPathEnv): string;
|
|
|
41
65
|
/** Host-portable durable data root; falls back to native OpenCode. */
|
|
42
66
|
export declare function hostGlobalDataDir(env?: HostPathEnv): string;
|
|
43
67
|
/**
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
|
|
68
|
+
* Select the entrypoint's native OpenCode plan directory. OpenCode 2.0 sets its
|
|
69
|
+
* Plan directory ({@link opencode2PlanDir}); OpenCode 1.x keeps the default.
|
|
70
|
+
* The returned disposer removes only this selection, so an older plugin setup
|
|
71
|
+
* disposed after a newer one leaves the newer selection in place. `undefined`
|
|
72
|
+
* removes all.
|
|
73
|
+
*/
|
|
74
|
+
export declare function setNativePlansDir(dir: string | undefined): () => void;
|
|
75
|
+
/**
|
|
76
|
+
* OpenCode 2.0's Plan directory: `<home>/.opencode/plan`, where its Plan agent
|
|
77
|
+
* may write plan files (home is `OPENCODE_TEST_HOME`, else the OS home).
|
|
78
|
+
*/
|
|
79
|
+
export declare function opencode2PlanDir(env?: HostPathEnv): string;
|
|
80
|
+
/**
|
|
81
|
+
* Directory for a new plan file when the session's own plan file
|
|
82
|
+
* ({@link hostPlanFilePath}) is not known.
|
|
55
83
|
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
* bridge it is the native OpenCode data root.
|
|
84
|
+
* An injected host path bridge owns host paths: `<globalDataDir()>/plans`.
|
|
85
|
+
* Otherwise the native OpenCode location of the running entrypoint: OpenCode
|
|
86
|
+
* 2.0's Plan directory, or OpenCode 1.x's no-VCS `Session.plan` base
|
|
87
|
+
* (`<data>/plans`).
|
|
61
88
|
*/
|
|
62
|
-
export declare function hostPlansDir(_workspaceRoot?: string): string;
|
|
89
|
+
export declare function hostPlansDir(_workspaceRoot?: string, env?: HostPathEnv): string;
|
|
63
90
|
/**
|
|
64
91
|
* Cursor-compatible path slug (`/workspace/a/b` → `workspace-a-b`).
|
|
65
92
|
* Used for per-workspace metadata under the host cache.
|
|
@@ -70,7 +97,7 @@ export declare function slugifyWorkspacePath(workspaceRoot: string): string;
|
|
|
70
97
|
* Lives at `<host-cache>/projects/<slug>/` under the resolved OpenCode/host cache root.
|
|
71
98
|
*
|
|
72
99
|
* This is what Cursor's RequestContextEnv.project_folder / MCP
|
|
73
|
-
* workspace_project_dir point at — agent-tools, terminals,
|
|
100
|
+
* workspace_project_dir point at — agent-tools, terminals, assets, etc.
|
|
74
101
|
* Must NOT be the git workspace, or those dumps land in the repo.
|
|
75
102
|
*/
|
|
76
103
|
export declare function opencodeProjectDir(workspaceRoot: string): string;
|
package/dist/context/paths.js
CHANGED
|
@@ -27,6 +27,31 @@ function bridgeGlobalDataDir() {
|
|
|
27
27
|
const value = pathBridge()?.globalDataDir?.();
|
|
28
28
|
return typeof value === "string" && value.length > 0 ? path.resolve(value) : undefined;
|
|
29
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* The plan file of a session: OpenCode's own `Session.plan` location
|
|
32
|
+
* (`<worktree>/.opencode/plans` in a VCS project, else `<data>/plans`, named
|
|
33
|
+
* `<created>-<slug>.md`), which its plan agent and `plan_exit` use. An injected
|
|
34
|
+
* host path bridge owns host paths, so with one installed only its `planFile`
|
|
35
|
+
* defines the location.
|
|
36
|
+
*/
|
|
37
|
+
export function hostPlanFilePath(input, env = process.env) {
|
|
38
|
+
const bridge = pathBridge();
|
|
39
|
+
if (bridge) {
|
|
40
|
+
if (typeof bridge.planFile !== "function")
|
|
41
|
+
return undefined;
|
|
42
|
+
const value = bridge.planFile(input);
|
|
43
|
+
return typeof value === "string" && value.length > 0 ? path.resolve(value) : undefined;
|
|
44
|
+
}
|
|
45
|
+
const slug = input.slug.trim();
|
|
46
|
+
if (!slug || slug.includes("/") || slug.includes("\\") || slug.startsWith("."))
|
|
47
|
+
return undefined;
|
|
48
|
+
if (!Number.isSafeInteger(input.created) || input.created <= 0)
|
|
49
|
+
return undefined;
|
|
50
|
+
const base = input.vcs
|
|
51
|
+
? path.join(path.resolve(input.worktree), ".opencode", "plans")
|
|
52
|
+
: path.join(openCodeGlobalDataDir(env), "plans");
|
|
53
|
+
return path.join(base, `${input.created}-${slug}.md`);
|
|
54
|
+
}
|
|
30
55
|
function bridgeGlobalCacheDir() {
|
|
31
56
|
const value = pathBridge()?.globalCacheDir?.();
|
|
32
57
|
return typeof value === "string" && value.length > 0 ? path.resolve(value) : undefined;
|
|
@@ -102,27 +127,49 @@ export function opencodeGlobalDataDir(env = process.env) {
|
|
|
102
127
|
export function hostGlobalDataDir(env = process.env) {
|
|
103
128
|
return bridgeGlobalDataDir() ?? openCodeGlobalDataDir(env);
|
|
104
129
|
}
|
|
130
|
+
/** Entrypoint plan directories, newest last (see {@link setNativePlansDir}). */
|
|
131
|
+
const nativePlansDirs = [];
|
|
105
132
|
/**
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
133
|
+
* Select the entrypoint's native OpenCode plan directory. OpenCode 2.0 sets its
|
|
134
|
+
* Plan directory ({@link opencode2PlanDir}); OpenCode 1.x keeps the default.
|
|
135
|
+
* The returned disposer removes only this selection, so an older plugin setup
|
|
136
|
+
* disposed after a newer one leaves the newer selection in place. `undefined`
|
|
137
|
+
* removes all.
|
|
138
|
+
*/
|
|
139
|
+
export function setNativePlansDir(dir) {
|
|
140
|
+
if (!dir) {
|
|
141
|
+
nativePlansDirs.length = 0;
|
|
142
|
+
return () => { };
|
|
143
|
+
}
|
|
144
|
+
const entry = { dir: path.resolve(dir) };
|
|
145
|
+
nativePlansDirs.push(entry);
|
|
146
|
+
return () => {
|
|
147
|
+
const index = nativePlansDirs.lastIndexOf(entry);
|
|
148
|
+
if (index >= 0)
|
|
149
|
+
nativePlansDirs.splice(index, 1);
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* OpenCode 2.0's Plan directory: `<home>/.opencode/plan`, where its Plan agent
|
|
154
|
+
* may write plan files (home is `OPENCODE_TEST_HOME`, else the OS home).
|
|
155
|
+
*/
|
|
156
|
+
export function opencode2PlanDir(env = process.env) {
|
|
157
|
+
return path.join(env.OPENCODE_TEST_HOME ?? homedir(), ".opencode", "plan");
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Directory for a new plan file when the session's own plan file
|
|
161
|
+
* ({@link hostPlanFilePath}) is not known.
|
|
117
162
|
*
|
|
118
|
-
*
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
* bridge it is the native OpenCode data root.
|
|
163
|
+
* An injected host path bridge owns host paths: `<globalDataDir()>/plans`.
|
|
164
|
+
* Otherwise the native OpenCode location of the running entrypoint: OpenCode
|
|
165
|
+
* 2.0's Plan directory, or OpenCode 1.x's no-VCS `Session.plan` base
|
|
166
|
+
* (`<data>/plans`).
|
|
123
167
|
*/
|
|
124
|
-
export function hostPlansDir(_workspaceRoot) {
|
|
125
|
-
|
|
168
|
+
export function hostPlansDir(_workspaceRoot, env = process.env) {
|
|
169
|
+
const bridged = bridgeGlobalDataDir();
|
|
170
|
+
if (bridged)
|
|
171
|
+
return path.join(bridged, "plans");
|
|
172
|
+
return nativePlansDirs[nativePlansDirs.length - 1]?.dir ?? path.join(openCodeGlobalDataDir(env), "plans");
|
|
126
173
|
}
|
|
127
174
|
/**
|
|
128
175
|
* Cursor-compatible path slug (`/workspace/a/b` → `workspace-a-b`).
|
|
@@ -141,17 +188,20 @@ export function slugifyWorkspacePath(workspaceRoot) {
|
|
|
141
188
|
* Lives at `<host-cache>/projects/<slug>/` under the resolved OpenCode/host cache root.
|
|
142
189
|
*
|
|
143
190
|
* This is what Cursor's RequestContextEnv.project_folder / MCP
|
|
144
|
-
* workspace_project_dir point at — agent-tools, terminals,
|
|
191
|
+
* workspace_project_dir point at — agent-tools, terminals, assets, etc.
|
|
145
192
|
* Must NOT be the git workspace, or those dumps land in the repo.
|
|
146
193
|
*/
|
|
147
194
|
export function opencodeProjectDir(workspaceRoot) {
|
|
148
195
|
const projectsRoot = path.join(opencodeGlobalCacheDir(), "projects");
|
|
149
196
|
const slug = slugifyWorkspacePath(workspaceRoot);
|
|
150
|
-
|
|
151
|
-
//
|
|
197
|
+
const dir = path.join(projectsRoot, slug);
|
|
198
|
+
// Keep nested agent-tools paths short by cutting the name to 84 characters.
|
|
199
|
+
// While that cut falls after `projects/`, it only shortens the slug, and it
|
|
200
|
+
// is the name existing folders already have. With a longer root it would
|
|
201
|
+
// fall inside the root, so the folder is named by the hash alone.
|
|
152
202
|
if (dir.length > 92) {
|
|
153
203
|
const hash = createHash("sha256").update(dir).digest("hex").slice(0, 7);
|
|
154
|
-
|
|
204
|
+
return projectsRoot.length < 84 ? `${dir.slice(0, 84)}-${hash}` : path.join(projectsRoot, hash);
|
|
155
205
|
}
|
|
156
206
|
return dir;
|
|
157
207
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structural host-skills capability installed before an unchanged provider loads.
|
|
3
|
+
* Same install timing as `opencode.host.path-bridge`.
|
|
4
|
+
*/
|
|
5
|
+
export declare const HOST_SKILLS_BRIDGE: unique symbol;
|
|
6
|
+
/** One host catalog skill the provider may advertise to Cursor. */
|
|
7
|
+
export type HostSkill = {
|
|
8
|
+
name: string;
|
|
9
|
+
id?: string;
|
|
10
|
+
description: string;
|
|
11
|
+
/** Absolute path, `file:` URL, or host URI (`skill://…`). Omit built-ins / markers. */
|
|
12
|
+
location?: string;
|
|
13
|
+
};
|
|
14
|
+
export type OpenCodeSkillsBridge = {
|
|
15
|
+
list(input: {
|
|
16
|
+
directory: string;
|
|
17
|
+
sessionID?: string;
|
|
18
|
+
}): Promise<HostSkill[]>;
|
|
19
|
+
};
|
|
20
|
+
export declare function skillsBridge(): OpenCodeSkillsBridge | undefined;
|
|
21
|
+
/** Test helper: install or clear the structural skills bridge. */
|
|
22
|
+
export declare function setHostSkillsBridgeForTests(bridge: OpenCodeSkillsBridge | undefined): void;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structural host-skills capability installed before an unchanged provider loads.
|
|
3
|
+
* Same install timing as `opencode.host.path-bridge`.
|
|
4
|
+
*/
|
|
5
|
+
export const HOST_SKILLS_BRIDGE = Symbol.for("opencode.host.skills");
|
|
6
|
+
export function skillsBridge() {
|
|
7
|
+
const value = globalThis[HOST_SKILLS_BRIDGE];
|
|
8
|
+
if (!value || typeof value !== "object")
|
|
9
|
+
return undefined;
|
|
10
|
+
const bridge = value;
|
|
11
|
+
return typeof bridge.list === "function" ? bridge : undefined;
|
|
12
|
+
}
|
|
13
|
+
/** Test helper: install or clear the structural skills bridge. */
|
|
14
|
+
export function setHostSkillsBridgeForTests(bridge) {
|
|
15
|
+
const globals = globalThis;
|
|
16
|
+
if (bridge)
|
|
17
|
+
globals[HOST_SKILLS_BRIDGE] = bridge;
|
|
18
|
+
else
|
|
19
|
+
delete globals[HOST_SKILLS_BRIDGE];
|
|
20
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { OpencodeToolDef } from "../protocol/tools.js";
|
|
2
|
+
import { type HostSkill } from "./skills-bridge.js";
|
|
3
|
+
export type HostSkillFileEntry = {
|
|
4
|
+
readonly id: string;
|
|
5
|
+
readonly path: string;
|
|
6
|
+
};
|
|
7
|
+
export type AgentSkillWire = {
|
|
8
|
+
readonly full_path: string;
|
|
9
|
+
readonly description: string;
|
|
10
|
+
};
|
|
11
|
+
/** Locations Cursor can advertise; markers / builtins are omitted. */
|
|
12
|
+
export declare function isUsableSkillLocation(location: string | undefined): boolean;
|
|
13
|
+
export declare function normalizeSkillLocation(location: string): string;
|
|
14
|
+
export declare function skillToolAdvertised(tools: readonly OpencodeToolDef[] | undefined): boolean;
|
|
15
|
+
/** Record skill files a Location's host currently registers (OpenCode 2 plugin). */
|
|
16
|
+
export declare function rememberHostSkillFiles(directory: string, skills: readonly HostSkillFileEntry[]): void;
|
|
17
|
+
export declare function hostSkillFiles(directory: string | undefined): ReadonlyMap<string, string> | undefined;
|
|
18
|
+
/** Test helper. */
|
|
19
|
+
export declare function resetHostSkillFilesForTests(): void;
|
|
20
|
+
/**
|
|
21
|
+
* path-desc `agent_skills` for cataloged skills that have a usable location,
|
|
22
|
+
* in catalog order. Descriptions come from the catalog (epoch-stable).
|
|
23
|
+
*/
|
|
24
|
+
export declare function agentSkillsForCursor(systemText: string | undefined, options: {
|
|
25
|
+
skillToolAdvertised: boolean;
|
|
26
|
+
skillFiles?: ReadonlyMap<string, string>;
|
|
27
|
+
bridgeSkills?: readonly HostSkill[];
|
|
28
|
+
}): AgentSkillWire[];
|
|
29
|
+
/** Fetch locations from the structural bridge when installed. */
|
|
30
|
+
export declare function loadBridgeSkills(input: {
|
|
31
|
+
workspaceRoot: string;
|
|
32
|
+
sessionID?: string;
|
|
33
|
+
}): Promise<HostSkill[] | undefined>;
|
|
34
|
+
export declare function applyAgentSkillsToContext(context: Record<string, unknown>, skills: readonly AgentSkillWire[]): void;
|