@oisincoveney/pipeline 2.1.1 → 2.2.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/defaults/pipeline.yaml +111 -0
- package/defaults/profiles.yaml +212 -0
- package/defaults/runners.yaml +24 -0
- package/dist/argo-graph.js +59 -27
- package/dist/argo-submit.js +10 -2
- package/dist/claude-settings-config.js +44 -0
- package/dist/cli/program.js +10 -2
- package/dist/config/defaults.js +7 -350
- package/dist/config/schemas.d.ts +5 -5
- package/dist/install-commands/claude-code.js +160 -0
- package/dist/install-commands/opencode.js +39 -32
- package/dist/install-commands/shared.js +32 -5
- package/dist/install-commands.js +26 -15
- package/dist/json-config-merge.js +47 -0
- package/dist/mcp/gateway.js +9 -1
- package/dist/moka-submit.d.ts +7 -7
- package/dist/opencode-project-config.js +2 -45
- package/dist/pipeline-runtime.js +50 -8
- package/dist/runner-command-contract.d.ts +2 -2
- package/dist/runner-event-schema.d.ts +349 -0
- package/dist/runner-event-schema.js +185 -0
- package/dist/runner-output.js +2 -2
- package/dist/runner.d.ts +2 -0
- package/dist/runtime/agent-node/agent-node.js +1 -0
- package/dist/runtime/contracts/contracts.d.ts +2 -0
- package/dist/runtime/node-state-store.js +7 -0
- package/dist/runtime/opencode-adapter.js +28 -8
- package/dist/runtime/opencode-agent-name.js +18 -0
- package/dist/runtime/opencode-runtime.js +62 -0
- package/dist/runtime/opencode-server.js +67 -0
- package/dist/runtime/opencode-session-executor.js +206 -0
- package/docs/adr-opencode-first-goal-loop-runtime.md +1 -1
- package/docs/config-architecture.md +11 -6
- package/docs/mcp-gateway.md +4 -4
- package/docs/operator-guide.md +7 -5
- package/docs/slash-command-adapter-contract.md +1 -0
- package/package.json +6 -1
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { OpencodeServerStartupError, openOpencodeServer } from "./opencode-server.js";
|
|
2
|
+
import { createOpencodeExecutor, createOpencodeSessionRegistry } from "./opencode-session-executor.js";
|
|
3
|
+
//#region src/runtime/opencode-runtime.ts
|
|
4
|
+
/**
|
|
5
|
+
* True when the config declares any opencode runner, i.e. the SDK transport is
|
|
6
|
+
* relevant for this run. Command-only configs never start a server.
|
|
7
|
+
*/
|
|
8
|
+
function configUsesOpencode(config) {
|
|
9
|
+
return Object.values(config.runners).some((runner) => runner.type === "opencode");
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Return an SDK-backed executor plus a release() that tears the server down.
|
|
13
|
+
* The opencode server is started LAZILY on the first executor call, not at
|
|
14
|
+
* lease time: a run whose ready nodes are all command/builtin (or that fails
|
|
15
|
+
* before any agent node executes) never spawns opencode, so the binary is only
|
|
16
|
+
* required when an agent node actually runs. release() is a no-op if the server
|
|
17
|
+
* was never started. Caller owns the lifecycle:
|
|
18
|
+
*
|
|
19
|
+
* const lease = await leaseOpencodeRuntime({ config, worktreePath });
|
|
20
|
+
* try { ...run with lease.executor... } finally { await lease.release(); }
|
|
21
|
+
*/
|
|
22
|
+
function leaseOpencodeRuntime(input) {
|
|
23
|
+
const registry = createOpencodeSessionRegistry();
|
|
24
|
+
const openHandle = input.openServer ?? startServer;
|
|
25
|
+
let handle;
|
|
26
|
+
let pending;
|
|
27
|
+
const ensureExecutor = () => {
|
|
28
|
+
pending ??= openHandle(input).then((started) => {
|
|
29
|
+
handle = started;
|
|
30
|
+
return createOpencodeExecutor({
|
|
31
|
+
client: started.client,
|
|
32
|
+
directory: input.worktreePath,
|
|
33
|
+
registry
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
return pending;
|
|
37
|
+
};
|
|
38
|
+
return Promise.resolve({
|
|
39
|
+
executor: async (plan, options) => {
|
|
40
|
+
return await (await ensureExecutor())(plan, options);
|
|
41
|
+
},
|
|
42
|
+
release: async () => {
|
|
43
|
+
if (handle) {
|
|
44
|
+
await handle.close();
|
|
45
|
+
handle = void 0;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
async function startServer(input) {
|
|
51
|
+
try {
|
|
52
|
+
return await openOpencodeServer({
|
|
53
|
+
directory: input.worktreePath,
|
|
54
|
+
...input.signal ? { signal: input.signal } : {}
|
|
55
|
+
});
|
|
56
|
+
} catch (error) {
|
|
57
|
+
if (error instanceof OpencodeServerStartupError) throw new OpencodeServerStartupError(`${error.message}. Confirm the opencode binary is installed and recent enough to expose 'opencode serve', or set OPENCODE_SERVER_URL to an already-running server.`, { cause: error });
|
|
58
|
+
throw error;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
//#endregion
|
|
62
|
+
export { configUsesOpencode, leaseOpencodeRuntime };
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { createOpencode, createOpencodeClient } from "@opencode-ai/sdk";
|
|
2
|
+
//#region src/runtime/opencode-server.ts
|
|
3
|
+
const DEFAULT_STARTUP_TIMEOUT_MS = 3e4;
|
|
4
|
+
var OpencodeServerStartupError = class extends Error {
|
|
5
|
+
constructor(message, options) {
|
|
6
|
+
super(message, options);
|
|
7
|
+
this.name = "OpencodeServerStartupError";
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
async function openOpencodeServer(options) {
|
|
11
|
+
const externalUrl = options.serverUrl ?? process.env.OPENCODE_SERVER_URL ?? "";
|
|
12
|
+
if (externalUrl.length > 0) return connectExternalServer(externalUrl, options.directory);
|
|
13
|
+
return await spawnOwnedServer(options);
|
|
14
|
+
}
|
|
15
|
+
function connectExternalServer(url, directory) {
|
|
16
|
+
return {
|
|
17
|
+
close: () => Promise.resolve(),
|
|
18
|
+
client: createOpencodeClient({
|
|
19
|
+
baseUrl: url,
|
|
20
|
+
directory
|
|
21
|
+
}),
|
|
22
|
+
owned: false,
|
|
23
|
+
url
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
async function spawnOwnedServer(options) {
|
|
27
|
+
const spawn = options.spawn ?? createOpencode;
|
|
28
|
+
try {
|
|
29
|
+
const { client, server } = await spawn(spawnArgs(options));
|
|
30
|
+
return ownedHandle(client, server, options.directory);
|
|
31
|
+
} catch (error) {
|
|
32
|
+
throw new OpencodeServerStartupError(`Failed to start opencode server: ${errorText(error)}`, { cause: error });
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function spawnArgs(options) {
|
|
36
|
+
return {
|
|
37
|
+
port: 0,
|
|
38
|
+
...options.signal ? { signal: options.signal } : {},
|
|
39
|
+
timeout: options.startupTimeoutMs ?? DEFAULT_STARTUP_TIMEOUT_MS
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function ownedHandle(client, server, directory) {
|
|
43
|
+
return {
|
|
44
|
+
close: () => {
|
|
45
|
+
server.close();
|
|
46
|
+
return Promise.resolve();
|
|
47
|
+
},
|
|
48
|
+
client: withDirectory(client, server.url, directory),
|
|
49
|
+
owned: true,
|
|
50
|
+
url: server.url
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function errorText(error) {
|
|
54
|
+
return error instanceof Error ? error.message : String(error);
|
|
55
|
+
}
|
|
56
|
+
function withDirectory(fallback, url, directory) {
|
|
57
|
+
try {
|
|
58
|
+
return createOpencodeClient({
|
|
59
|
+
baseUrl: url,
|
|
60
|
+
directory
|
|
61
|
+
});
|
|
62
|
+
} catch {
|
|
63
|
+
return fallback;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
//#endregion
|
|
67
|
+
export { OpencodeServerStartupError, openOpencodeServer };
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { opencodeAgentName } from "./opencode-agent-name.js";
|
|
2
|
+
//#region src/runtime/opencode-session-executor.ts
|
|
3
|
+
function createOpencodeSessionRegistry() {
|
|
4
|
+
return { sessions: /* @__PURE__ */ new Map() };
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Distinguish infra failure (server/session error -> retry-eligible exit 70)
|
|
8
|
+
* from a normal agent completion (the agent may still have produced a wrong
|
|
9
|
+
* answer; gates decide that, exit 0). This mirrors the EXIT_STARTUP convention
|
|
10
|
+
* in runner-command/run.ts and feeds retry.ts via the node's retry policy.
|
|
11
|
+
*/
|
|
12
|
+
const EXIT_OK = 0;
|
|
13
|
+
const EXIT_AGENT_ERROR = 1;
|
|
14
|
+
const EXIT_INFRA = 70;
|
|
15
|
+
/**
|
|
16
|
+
* SDK-backed replacement for the subprocess `runLaunchPlan`. Conforms to the
|
|
17
|
+
* RuntimeContext.executor seam so agent-node never learns the transport.
|
|
18
|
+
*/
|
|
19
|
+
function createOpencodeExecutor(deps) {
|
|
20
|
+
return async function execute(plan, options = {}) {
|
|
21
|
+
if (plan.type !== "opencode") throw new Error(`opencode executor cannot drive runner type '${plan.type}'`);
|
|
22
|
+
try {
|
|
23
|
+
return successResult(plan, await driveSession(deps, plan, options));
|
|
24
|
+
} catch (error) {
|
|
25
|
+
return failureResult(plan, error);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
async function driveSession(deps, plan, options) {
|
|
30
|
+
const sessionId = await resolveSessionId(deps, plan);
|
|
31
|
+
deps.onSession?.(plan.nodeId, sessionId);
|
|
32
|
+
const stream = await streamEventsToOutput(deps, sessionId, plan, options);
|
|
33
|
+
try {
|
|
34
|
+
const data = unwrap(await deps.client.session.prompt({
|
|
35
|
+
body: promptBody(plan),
|
|
36
|
+
path: { id: sessionId },
|
|
37
|
+
query: { directory: deps.directory }
|
|
38
|
+
}));
|
|
39
|
+
return {
|
|
40
|
+
...data.info ? { assistant: data.info } : {},
|
|
41
|
+
parts: data.parts ?? [],
|
|
42
|
+
sessionId
|
|
43
|
+
};
|
|
44
|
+
} finally {
|
|
45
|
+
await stream.stop();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
async function resolveSessionId(deps, plan) {
|
|
49
|
+
const existing = deps.registry.sessions.get(plan.nodeId);
|
|
50
|
+
if (existing) return existing;
|
|
51
|
+
const session = unwrap(await deps.client.session.create({
|
|
52
|
+
body: { title: `moka:${plan.nodeId}` },
|
|
53
|
+
query: { directory: deps.directory }
|
|
54
|
+
}));
|
|
55
|
+
deps.registry.sessions.set(plan.nodeId, session.id);
|
|
56
|
+
return session.id;
|
|
57
|
+
}
|
|
58
|
+
function promptBody(plan) {
|
|
59
|
+
const prompt = promptText(plan);
|
|
60
|
+
const model = parseModel(plan.model);
|
|
61
|
+
const agent = plan.profileId ? opencodeAgentName(plan.profileId) : void 0;
|
|
62
|
+
return {
|
|
63
|
+
parts: [{
|
|
64
|
+
text: prompt,
|
|
65
|
+
type: "text"
|
|
66
|
+
}],
|
|
67
|
+
...agent ? { agent } : {},
|
|
68
|
+
...model ? { model } : {}
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
const FLAGS_TAKING_VALUE = new Set([
|
|
72
|
+
"--model",
|
|
73
|
+
"--dir",
|
|
74
|
+
"--file",
|
|
75
|
+
"--format"
|
|
76
|
+
]);
|
|
77
|
+
/**
|
|
78
|
+
* The launch plan carries the prompt inside the CLI argv (`run <prompt>` or
|
|
79
|
+
* `run <prompt> --file <ctx>`). Recover it as the trailing positional arg
|
|
80
|
+
* (skipping flags and their values) so the adapter boundary is identical
|
|
81
|
+
* regardless of transport.
|
|
82
|
+
*/
|
|
83
|
+
function promptText(plan) {
|
|
84
|
+
return plan.args.filter((arg, index) => index > 0 && !arg.startsWith("-") && !FLAGS_TAKING_VALUE.has(plan.args[index - 1] ?? "")).at(-1) ?? "";
|
|
85
|
+
}
|
|
86
|
+
function parseModel(model) {
|
|
87
|
+
if (!model) return;
|
|
88
|
+
const slash = model.indexOf("/");
|
|
89
|
+
if (slash === -1) return;
|
|
90
|
+
return {
|
|
91
|
+
modelID: model.slice(slash + 1),
|
|
92
|
+
providerID: model.slice(0, slash)
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
async function streamEventsToOutput(deps, sessionId, plan, options) {
|
|
96
|
+
if (!options.onOutput) return { stop: () => Promise.resolve() };
|
|
97
|
+
const iterator = (await deps.client.event.subscribe()).stream;
|
|
98
|
+
const pump = (async () => {
|
|
99
|
+
try {
|
|
100
|
+
for await (const event of iterator) forwardEvent(event, sessionId, plan, options);
|
|
101
|
+
} catch (error) {
|
|
102
|
+
options.onOutput?.({
|
|
103
|
+
chunk: `opencode event stream dropped: ${errorMessage(error)}\n`,
|
|
104
|
+
nodeId: plan.nodeId,
|
|
105
|
+
stream: "stderr"
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
})();
|
|
109
|
+
return { stop: async () => {
|
|
110
|
+
await iterator.return?.(void 0);
|
|
111
|
+
await pump;
|
|
112
|
+
} };
|
|
113
|
+
}
|
|
114
|
+
function forwardEvent(event, sessionId, plan, options) {
|
|
115
|
+
const forwarded = eventChunk(event, sessionId);
|
|
116
|
+
if (forwarded) options.onOutput?.({
|
|
117
|
+
chunk: forwarded.chunk,
|
|
118
|
+
nodeId: plan.nodeId,
|
|
119
|
+
stream: forwarded.stream
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
function eventChunk(event, sessionId) {
|
|
123
|
+
if (event.type === "message.part.updated") return partChunk(event.properties.part, sessionId);
|
|
124
|
+
if (event.type === "session.error" && belongsToSession(event, sessionId)) return {
|
|
125
|
+
chunk: `opencode session error: ${describeMessageError(event.properties.error)}\n`,
|
|
126
|
+
stream: "stderr"
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
function belongsToSession(event, sessionId) {
|
|
130
|
+
return event.properties.sessionID === void 0 || event.properties.sessionID === sessionId;
|
|
131
|
+
}
|
|
132
|
+
function partChunk(part, sessionId) {
|
|
133
|
+
if (part.sessionID !== sessionId) return;
|
|
134
|
+
if (part.type === "text") return {
|
|
135
|
+
chunk: `${JSON.stringify({ part: {
|
|
136
|
+
text: part.text,
|
|
137
|
+
type: "text"
|
|
138
|
+
} })}\n`,
|
|
139
|
+
stream: "stdout"
|
|
140
|
+
};
|
|
141
|
+
if (part.type === "tool") return {
|
|
142
|
+
chunk: `opencode tool ${part.tool} ${part.state.status}\n`,
|
|
143
|
+
stream: "stderr"
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Reconstruct the JSONL stdout the existing normalizeOutput/outputCandidates
|
|
148
|
+
* parser already understands, so the structured-output and repair passes work
|
|
149
|
+
* unchanged on top of SDK responses.
|
|
150
|
+
*/
|
|
151
|
+
function successResult(plan, drive) {
|
|
152
|
+
const stdout = drive.parts.filter((part) => part.type === "text").map((part) => JSON.stringify({ part: {
|
|
153
|
+
text: part.text,
|
|
154
|
+
type: "text"
|
|
155
|
+
} })).join("\n");
|
|
156
|
+
const assistantError = drive.assistant?.error;
|
|
157
|
+
if (assistantError) return {
|
|
158
|
+
argv: plan.args,
|
|
159
|
+
exitCode: infraErrorExitCode(assistantError),
|
|
160
|
+
sessionId: drive.sessionId,
|
|
161
|
+
stderr: describeMessageError(assistantError),
|
|
162
|
+
stdout
|
|
163
|
+
};
|
|
164
|
+
return {
|
|
165
|
+
argv: plan.args,
|
|
166
|
+
exitCode: EXIT_OK,
|
|
167
|
+
sessionId: drive.sessionId,
|
|
168
|
+
stdout
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
function failureResult(plan, error) {
|
|
172
|
+
return {
|
|
173
|
+
argv: plan.args,
|
|
174
|
+
exitCode: EXIT_INFRA,
|
|
175
|
+
stderr: `opencode session failed: ${errorMessage(error)}`,
|
|
176
|
+
stdout: ""
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Map opencode message errors to the runner's infra-vs-agent exit convention.
|
|
181
|
+
* Output-length / aborted are agent-task outcomes (exit 1, gate territory);
|
|
182
|
+
* provider-auth, API, and unknown are infra (exit 70, retry-eligible).
|
|
183
|
+
*/
|
|
184
|
+
function infraErrorExitCode(error) {
|
|
185
|
+
switch (error.name) {
|
|
186
|
+
case "MessageOutputLengthError":
|
|
187
|
+
case "MessageAbortedError": return EXIT_AGENT_ERROR;
|
|
188
|
+
default: return EXIT_INFRA;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
function describeMessageError(error) {
|
|
192
|
+
if (!error) return "unknown opencode error";
|
|
193
|
+
const data = error.data;
|
|
194
|
+
const detail = data && typeof data.message === "string" ? `: ${data.message}` : "";
|
|
195
|
+
return `${error.name}${detail}`;
|
|
196
|
+
}
|
|
197
|
+
function unwrap(response) {
|
|
198
|
+
if (response.error) throw new Error(typeof response.error === "string" ? response.error : JSON.stringify(response.error));
|
|
199
|
+
if (response.data === void 0) throw new Error("opencode response contained no data");
|
|
200
|
+
return response.data;
|
|
201
|
+
}
|
|
202
|
+
function errorMessage(error) {
|
|
203
|
+
return error instanceof Error ? error.message : String(error);
|
|
204
|
+
}
|
|
205
|
+
//#endregion
|
|
206
|
+
export { createOpencodeExecutor, createOpencodeSessionRegistry };
|
|
@@ -306,14 +306,19 @@ timeouts, output limits, sanitized env, and explicit trust flags.
|
|
|
306
306
|
|
|
307
307
|
## Host Support Matrix
|
|
308
308
|
|
|
309
|
-
| Runner
|
|
310
|
-
|
|
|
311
|
-
| OpenCode
|
|
312
|
-
|
|
|
309
|
+
| Runner | Native subagents | Rules | Skills | MCP | Outputs | Generated resources |
|
|
310
|
+
| ----------- | ---------------- | ----- | ------ | --- | ------------------------- | ------------------------------- |
|
|
311
|
+
| OpenCode | yes | yes | yes | yes | text, JSON, JSONL, schema | commands, agents, skills, plugins, LSP |
|
|
312
|
+
| Claude Code | via `opencode run` | yes (skill) | yes | yes | declared by runner | commands, wrapper agents, settings |
|
|
313
|
+
| command | no | no | no | no | declared by runner | subprocess argv |
|
|
313
314
|
|
|
314
315
|
Generated host resources follow a native runner rule. OpenCode runner nodes use
|
|
315
|
-
OpenCode native agents.
|
|
316
|
-
|
|
316
|
+
OpenCode native agents. The Claude Code host does not run MoKa profiles natively:
|
|
317
|
+
its generated `.claude/agents/moka-<role>.md` subagents each wrap a single
|
|
318
|
+
`opencode run --agent "MoKa <role>"` subprocess, so the OpenCode runner remains
|
|
319
|
+
the execution surface while Claude Code orchestrates. Unsupported runner or host
|
|
320
|
+
mappings fail closed instead of doing instruction-only translation or generic
|
|
321
|
+
worker substitution.
|
|
317
322
|
|
|
318
323
|
## Troubleshooting
|
|
319
324
|
|
package/docs/mcp-gateway.md
CHANGED
|
@@ -9,7 +9,7 @@ Playwright, Qdrant, or Neon.
|
|
|
9
9
|
## Target Shape
|
|
10
10
|
|
|
11
11
|
```text
|
|
12
|
-
|
|
12
|
+
OpenCode / Claude Code
|
|
13
13
|
|
|
|
14
14
|
| project-level MCP config
|
|
15
15
|
v
|
|
@@ -47,7 +47,7 @@ host-specific MCP config.
|
|
|
47
47
|
3. Configure `mcp_gateway` in package-owned profile config.
|
|
48
48
|
4. Run `moka mcp gateway reconcile` to render and apply the full ToolHive vMCP
|
|
49
49
|
backend inventory for the current workspace.
|
|
50
|
-
5. Run `moka init` to write project
|
|
50
|
+
5. Run `moka init` to write project OpenCode and Claude Code command surfaces and host
|
|
51
51
|
MCP config.
|
|
52
52
|
6. Run `moka mcp gateway doctor` to verify gateway health and required tools.
|
|
53
53
|
7. Keep high-risk upstream capabilities controlled by gateway-side policy, not
|
|
@@ -88,7 +88,7 @@ orchestrator MCP set * subagent count * host config layers
|
|
|
88
88
|
```
|
|
89
89
|
|
|
90
90
|
With a gateway, the runtime launches zero local upstream MCP processes for
|
|
91
|
-
agents.
|
|
91
|
+
agents. OpenCode and Claude Code read the same project-level host config, which
|
|
92
92
|
contains only `pipeline-gateway`.
|
|
93
93
|
|
|
94
94
|
OpenCode receives that gateway through `.opencode/opencode.json` alongside the
|
|
@@ -114,7 +114,7 @@ adding one backend must not replace the existing Context7, uidotsh, Qdrant,
|
|
|
114
114
|
Fallow, Serena, or Backlog declarations. Use `moka mcp gateway doctor` to check
|
|
115
115
|
required environment variables, gateway health, required `tools/list` prefixes,
|
|
116
116
|
local ToolHive availability for local mode, and legacy direct MCP entries. Use
|
|
117
|
-
`moka init` to install generated
|
|
117
|
+
`moka init` to install generated OpenCode and Claude Code host surfaces with the
|
|
118
118
|
singleton `pipeline-gateway` remote entry. Use `moka install-commands --host all`
|
|
119
119
|
to refresh generated host files after package upgrades, and use
|
|
120
120
|
`moka mcp gateway configure-host` as an explicit migration or repair command
|
package/docs/operator-guide.md
CHANGED
|
@@ -302,8 +302,8 @@ Troubleshooting:
|
|
|
302
302
|
Generated invocations include:
|
|
303
303
|
|
|
304
304
|
```text
|
|
305
|
-
OpenCode:
|
|
306
|
-
|
|
305
|
+
OpenCode: /moka-quick, /moka-execute, /moka-inspect
|
|
306
|
+
Claude Code: /moka-quick, /moka-execute, /moka-inspect
|
|
307
307
|
```
|
|
308
308
|
|
|
309
309
|
`moka init` and `moka install-commands --host opencode` generate:
|
|
@@ -316,9 +316,11 @@ Codex: $quick, $execute, $inspect
|
|
|
316
316
|
- `.opencode/opencode.json` with LSP, the singleton `pipeline-gateway` MCP
|
|
317
317
|
server, and pinned package-selected plugins
|
|
318
318
|
|
|
319
|
+
`moka install-commands --host claude-code` generates `.claude/commands/moka-<entrypoint>.md`
|
|
320
|
+
slash commands for Claude Code.
|
|
321
|
+
|
|
319
322
|
Package defaults select OpenCode for built-in profiles and runner-command
|
|
320
|
-
orchestration. Codex
|
|
321
|
-
`$inspect`, and Codex agent config, but it is not the default package runtime.
|
|
323
|
+
orchestration. Codex is not a supported runtime host.
|
|
322
324
|
|
|
323
325
|
## How The Package Works
|
|
324
326
|
|
|
@@ -373,7 +375,7 @@ profiles, workflows, or node-level skill overrides.
|
|
|
373
375
|
|
|
374
376
|
## Configuring MCP Gateway
|
|
375
377
|
|
|
376
|
-
MCP access is host-level and gateway-only.
|
|
378
|
+
MCP access is host-level and gateway-only. OpenCode, Claude Code, pipeline agents,
|
|
377
379
|
manual sessions, and CI all connect to one ToolHive/vMCP gateway URL. Do not
|
|
378
380
|
start upstream MCP servers from individual sessions.
|
|
379
381
|
|
|
@@ -16,6 +16,7 @@ moka install-commands --host all --check
|
|
|
16
16
|
| Host | Generated resources | Invocation | Mechanical path |
|
|
17
17
|
| -------- | -------------------------------------------------------------- | --------------------------------- | ----------------------------------------------------------------------------------------------------- |
|
|
18
18
|
| OpenCode | `.opencode/commands/moka-<entrypoint>.md`, `.opencode/agents/*.md`, `.opencode/opencode.json` | `/moka-quick <task>`, `/moka-execute <task>`, `/moka-inspect <task>` | Project commands run a primary orchestrator and OpenCode native subagents with package-owned skill, MCP, permission, and LSP projection. |
|
|
19
|
+
| Claude Code | `.claude/commands/moka-<entrypoint>.md`, `.claude/agents/moka-<role>.md`, `.claude/settings.json` | `/moka-quick <task>`, `/moka-execute <task>`, `/moka-inspect <task>` | Slash commands load the `execute` skill, then dispatch each agent node to a Claude Code `Task` subagent that wraps a single `opencode run --agent "MoKa <role>"` subprocess. `.claude/settings.json` is merged (gateway MCP + `Bash(opencode run *)` permission), never clobbered. |
|
|
19
20
|
|
|
20
21
|
## Projection Rules
|
|
21
22
|
|
package/package.json
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
"dependencies": {
|
|
3
3
|
"@dagrejs/graphlib": "^4.0.1",
|
|
4
4
|
"@kubernetes/client-node": "^1.4.0",
|
|
5
|
+
"@opencode-ai/sdk": "1.17.4",
|
|
5
6
|
"ajv": "^8.20.0",
|
|
6
7
|
"ajv-formats": "^3.0.1",
|
|
7
8
|
"commander": "^14.0.3",
|
|
@@ -62,6 +63,10 @@
|
|
|
62
63
|
"types": "./dist/config.d.ts",
|
|
63
64
|
"import": "./dist/config.js"
|
|
64
65
|
},
|
|
66
|
+
"./events": {
|
|
67
|
+
"types": "./dist/runner-event-schema.d.ts",
|
|
68
|
+
"import": "./dist/runner-event-schema.js"
|
|
69
|
+
},
|
|
65
70
|
"./hooks": {
|
|
66
71
|
"types": "./dist/hooks.d.ts",
|
|
67
72
|
"import": "./dist/hooks.js"
|
|
@@ -115,7 +120,7 @@
|
|
|
115
120
|
"prepack": "bun run build:cli"
|
|
116
121
|
},
|
|
117
122
|
"type": "module",
|
|
118
|
-
"version": "2.
|
|
123
|
+
"version": "2.2.0",
|
|
119
124
|
"description": "Config-driven multi-agent pipeline runner for repository work",
|
|
120
125
|
"main": "./dist/index.js",
|
|
121
126
|
"types": "./dist/index.d.ts",
|