@sjawhar/opencode-legion-envoy 0.6.1 → 0.8.0
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/dist/bin/dispatch-mcp-shim.js +0 -6
- package/dist/src/server.js +40 -38
- package/package.json +6 -4
- package/skills/AGENTS.md +38 -0
- package/skills/envoy/SKILL.md +396 -0
- package/skills/github/SKILL.md +203 -0
- package/skills/legion-architect/SKILL.md +201 -0
- package/skills/legion-controller/SKILL.md +136 -0
- package/skills/legion-oracle/SKILL.md +63 -0
- package/skills/legion-retro/SKILL.md +87 -0
- package/skills/legion-worker/SKILL.md +177 -0
- package/skills/legion-worker/references/config.md +259 -0
- package/skills/legion-worker/references/knowledge-injection.md +98 -0
- package/skills/legion-worker/resources/strategies/cleanup-deletion.md +22 -0
- package/skills/legion-worker/resources/strategies/systematic-rename.md +19 -0
- package/skills/linear/SKILL.md +76 -0
- package/src/server.ts +30 -2
- package/src/dispatch-subscribe.ts +0 -52
package/src/server.ts
CHANGED
|
@@ -1,15 +1,30 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
1
4
|
import { agentSubject } from "@legion/contracts";
|
|
2
5
|
import { envoyDefaultsFromEnvironment } from "@legion/envoy-client/defaults";
|
|
6
|
+
import { dispatchSubscriptionTopic } from "@legion/envoy-client/dispatch-subscribe";
|
|
3
7
|
import { machineID } from "@legion/envoy-client/machine";
|
|
4
8
|
import { envoyToolSpecs } from "@legion/envoy-client/tool-contract";
|
|
5
9
|
import { createEnvoyClient } from "@legion/envoy-client/transport";
|
|
6
10
|
import { tool } from "@opencode-ai/plugin/tool";
|
|
7
11
|
import { loadEnvoyConfig } from "./config";
|
|
8
12
|
import { buildDispatchMcpEntry, injectEnvoyMcp } from "./dispatch-mcp";
|
|
9
|
-
import { dispatchSubscriptionTopic } from "./dispatch-subscribe";
|
|
10
13
|
import { logger } from "./log";
|
|
11
14
|
import { resolvePort } from "./port";
|
|
12
15
|
|
|
16
|
+
const moduleDirectory = path.dirname(fileURLToPath(import.meta.url));
|
|
17
|
+
// Legion skills ship inside this package so OpenCode carries them without a
|
|
18
|
+
// vendor checkout. OpenCode discovers skills only from fixed directories and
|
|
19
|
+
// `config.skills.paths` — never from plugin package dirs — so the config hook
|
|
20
|
+
// below must inject this path itself.
|
|
21
|
+
// Published tarball: dist/src/server.js → <pkg>/skills (staged at prepack).
|
|
22
|
+
// Repo checkout: src/server.ts → <repo>/skills.
|
|
23
|
+
const skillsDirectory = [
|
|
24
|
+
path.resolve(moduleDirectory, "../../skills"),
|
|
25
|
+
path.resolve(moduleDirectory, "../../../skills"),
|
|
26
|
+
].find((dir) => existsSync(dir));
|
|
27
|
+
|
|
13
28
|
const [
|
|
14
29
|
subscribeSpec,
|
|
15
30
|
unsubscribeSpec,
|
|
@@ -139,7 +154,20 @@ export default async (input: { serverUrl: URL }) => {
|
|
|
139
154
|
});
|
|
140
155
|
|
|
141
156
|
return {
|
|
142
|
-
config: (
|
|
157
|
+
config: (
|
|
158
|
+
cfg: { mcp?: Record<string, unknown>; skills?: { paths?: string[] } } & Record<
|
|
159
|
+
string,
|
|
160
|
+
unknown
|
|
161
|
+
>
|
|
162
|
+
) => {
|
|
163
|
+
// Serve the bundled legion skills to every session on this serve.
|
|
164
|
+
if (skillsDirectory) {
|
|
165
|
+
cfg.skills ??= {};
|
|
166
|
+
cfg.skills.paths ??= [];
|
|
167
|
+
if (!cfg.skills.paths.includes(skillsDirectory)) {
|
|
168
|
+
cfg.skills.paths.push(skillsDirectory);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
143
171
|
// Inject the envoy MCP entry into the OpenCode config when
|
|
144
172
|
// dispatch is enabled. Centralizing this in the plugin (instead of
|
|
145
173
|
// each user's opencode.json) means:
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
// Auto-subscription wiring for the envoy_dispatch MCP tool.
|
|
2
|
-
//
|
|
3
|
-
// When an agent opens a Dispatch thread via the envoy_dispatch MCP tool, the
|
|
4
|
-
// human answers by commenting on the resulting GitHub sub-issue. For the agent
|
|
5
|
-
// to RECEIVE that answer, its session must be subscribed to the thread's Envoy
|
|
6
|
-
// topic (notifications.github.<owner>.<repo>.issue.<thread>.>). The dispatch
|
|
7
|
-
// tool is served by the Go dispatch server and has no OpenCode session context,
|
|
8
|
-
// so we close the loop in the plugin (which does know the session id) from the
|
|
9
|
-
// tool.execute.after hook.
|
|
10
|
-
//
|
|
11
|
-
// This module is the pure, testable core: it turns a completed tool execution
|
|
12
|
-
// into the topic the calling session should subscribe to, or null when the
|
|
13
|
-
// execution isn't a successful envoy_dispatch call.
|
|
14
|
-
|
|
15
|
-
// Matches a GitHub issue URL anywhere in the tool output and captures
|
|
16
|
-
// owner / repo / number. The dispatch tool returns {"thread":N,"url":"…"} as
|
|
17
|
-
// its text content, so the URL is always present on success.
|
|
18
|
-
const ISSUE_URL_RE = /https?:\/\/github\.com\/([^/\s"]+)\/([^/\s"]+)\/issues\/(\d+)/i;
|
|
19
|
-
|
|
20
|
-
// The tool is registered on the Go MCP server as "dispatch" and exposed to
|
|
21
|
-
// OpenCode under the "envoy" MCP server (so commonly "envoy_dispatch"). Accept
|
|
22
|
-
// any separator a client might use while still excluding unrelated tools.
|
|
23
|
-
const DISPATCH_TOOL_RE = /(^|[-._])dispatch$/i;
|
|
24
|
-
|
|
25
|
-
export function isDispatchTool(tool: string): boolean {
|
|
26
|
-
return DISPATCH_TOOL_RE.test(tool);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/** Build the Envoy topic carrying every event on a dispatch thread issue. */
|
|
30
|
-
export function dispatchThreadTopic(owner: string, repo: string, thread: number): string {
|
|
31
|
-
return `notifications.github.${owner}.${repo}.issue.${thread}.>`;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Given a completed tool execution (name + textual output), return the Envoy
|
|
36
|
-
* topic the calling session should subscribe to so it receives replies on the
|
|
37
|
-
* dispatch thread — or null when this isn't a successful envoy_dispatch call.
|
|
38
|
-
*
|
|
39
|
-
* Parsing the GitHub issue URL out of the output (rather than trusting a JSON
|
|
40
|
-
* field) keeps this robust to however OpenCode surfaces the MCP result: owner,
|
|
41
|
-
* repo, and thread number all come from the canonical issue URL.
|
|
42
|
-
*/
|
|
43
|
-
export function dispatchSubscriptionTopic(tool: string, output: string): string | null {
|
|
44
|
-
if (!isDispatchTool(tool)) return null;
|
|
45
|
-
const match = ISSUE_URL_RE.exec(output);
|
|
46
|
-
if (!match) return null;
|
|
47
|
-
const owner = match[1] as string;
|
|
48
|
-
const repo = match[2] as string;
|
|
49
|
-
const thread = Number(match[3]);
|
|
50
|
-
if (!Number.isInteger(thread) || thread <= 0) return null;
|
|
51
|
-
return dispatchThreadTopic(owner, repo, thread);
|
|
52
|
-
}
|