@oisincoveney/pipeline 2.1.0 → 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.
Files changed (39) hide show
  1. package/defaults/pipeline.yaml +111 -0
  2. package/defaults/profiles.yaml +212 -0
  3. package/defaults/runners.yaml +24 -0
  4. package/dist/argo-graph.js +59 -27
  5. package/dist/argo-submit.js +28 -4
  6. package/dist/claude-settings-config.js +44 -0
  7. package/dist/cli/program.js +1 -1
  8. package/dist/config/defaults.js +7 -353
  9. package/dist/git-remote-url.js +30 -0
  10. package/dist/hooks.d.ts +1 -1
  11. package/dist/install-commands/claude-code.js +153 -17
  12. package/dist/install-commands/opencode.js +37 -33
  13. package/dist/install-commands/shared.js +27 -6
  14. package/dist/install-commands.js +26 -22
  15. package/dist/json-config-merge.js +47 -0
  16. package/dist/mcp/gateway.js +9 -1
  17. package/dist/moka-submit.d.ts +6 -6
  18. package/dist/moka-submit.js +4 -3
  19. package/dist/opencode-project-config.js +2 -45
  20. package/dist/pipeline-runtime.js +50 -8
  21. package/dist/runner-event-schema.d.ts +349 -0
  22. package/dist/runner-event-schema.js +185 -0
  23. package/dist/runner-output.js +2 -2
  24. package/dist/runner.d.ts +2 -0
  25. package/dist/runtime/agent-node/agent-node.js +1 -0
  26. package/dist/runtime/contracts/contracts.d.ts +2 -0
  27. package/dist/runtime/node-state-store.js +7 -0
  28. package/dist/runtime/opencode-adapter.js +28 -8
  29. package/dist/runtime/opencode-agent-name.js +18 -0
  30. package/dist/runtime/opencode-runtime.js +62 -0
  31. package/dist/runtime/opencode-server.js +67 -0
  32. package/dist/runtime/opencode-session-executor.js +206 -0
  33. package/docs/adr-opencode-first-goal-loop-runtime.md +1 -1
  34. package/docs/config-architecture.md +11 -6
  35. package/docs/mcp-gateway.md +4 -4
  36. package/docs/operator-guide.md +7 -5
  37. package/docs/slash-command-adapter-contract.md +1 -0
  38. package/package.json +6 -1
  39. package/.agents/skills/claude-code-opencode-execute/SKILL.md +0 -116
@@ -7,359 +7,13 @@ const PIPELINE_CONFIG_PATH = ".pipeline/pipeline.yaml";
7
7
  const RUNNERS_CONFIG_PATH = ".pipeline/runners.yaml";
8
8
  const PROFILES_CONFIG_PATH = ".pipeline/profiles.yaml";
9
9
  const OPENCODE_ECOSYSTEM_MANIFEST_PATH = "defaults/opencode-ecosystem.yaml";
10
- const PACKAGE_DEFAULT_RUNNERS_YAML = `version: 1
11
- runners:
12
- opencode:
13
- type: opencode
14
- capabilities:
15
- native_subagents: true
16
- rules: true
17
- skills: true
18
- mcp_servers: true
19
- tools: [read, list, grep, glob, bash, edit, write]
20
- filesystem: [read-only, workspace-write]
21
- network: [inherit, disabled]
22
- output_formats: [text, json, jsonl, json_schema]
23
- command:
24
- type: command
25
- capabilities:
26
- native_subagents: false
27
- rules: false
28
- skills: false
29
- mcp_servers: false
30
- tools: [bash]
31
- filesystem: [read-only, workspace-write]
32
- network: [inherit, disabled]
33
- output_formats: [text, json]
34
- `;
35
- const PACKAGE_DEFAULT_PROFILES_YAML = `version: 1
36
- mcp_gateway:
37
- provider: toolhive
38
- mode: local
39
- url: https://pipeline-mcp.momokaya.ee/mcp/
40
- url_env: PIPELINE_MCP_GATEWAY_URL
41
- authorization_env: PIPELINE_MCP_GATEWAY_AUTHORIZATION
42
- default_profile: default
43
- backends:
44
- context7:
45
- locality: shared-remote
46
- tool_prefixes: [context7]
47
- uidotsh:
48
- locality: shared-remote
49
- tool_prefixes: [uidotsh]
50
- qdrant:
51
- locality: repo-scoped-remote
52
- tool_prefixes: [qdrant]
53
- fallow:
54
- locality: repo-local
55
- workspace_path_source: PIPELINE_TARGET_PATH
56
- required: false
57
- tool_prefixes: [fallow]
58
- serena:
59
- locality: repo-local
60
- workspace_path_source: PIPELINE_TARGET_PATH
61
- tool_prefixes: [serena]
62
- backlog:
63
- locality: repo-local
64
- workspace_path_source: PIPELINE_TARGET_PATH
65
- tool_prefixes: [backlog]
66
- skills:
67
- execute:
68
- path: .agents/skills/execute/SKILL.md
69
- source_root: package
70
- inspect:
71
- path: .agents/skills/inspect/SKILL.md
72
- source_root: package
73
- quick:
74
- path: .agents/skills/quick/SKILL.md
75
- source_root: package
76
- claude-code-opencode-execute:
77
- path: .agents/skills/claude-code-opencode-execute/SKILL.md
78
- source_root: package
79
- critique:
80
- path: .agents/skills/critique/SKILL.md
81
- source_root: package
82
- doubt:
83
- path: .agents/skills/doubt/SKILL.md
84
- source_root: package
85
- fix:
86
- path: .agents/skills/fix/SKILL.md
87
- source_root: package
88
- library-first-development:
89
- path: .agents/skills/library-first-development/SKILL.md
90
- source_root: package
91
- migrate:
92
- path: .agents/skills/migrate/SKILL.md
93
- source_root: package
94
- optimize:
95
- path: .agents/skills/optimize/SKILL.md
96
- source_root: package
97
- research:
98
- path: .agents/skills/research/SKILL.md
99
- source_root: package
100
- schedule-graph-shaping:
101
- path: .agents/skills/schedule-graph-shaping/SKILL.md
102
- source_root: package
103
- scope:
104
- path: .agents/skills/scope/SKILL.md
105
- source_root: package
106
- secure:
107
- path: .agents/skills/secure/SKILL.md
108
- source_root: package
109
- spec:
110
- path: .agents/skills/spec/SKILL.md
111
- source_root: package
112
- test:
113
- path: .agents/skills/test/SKILL.md
114
- source_root: package
115
- trace:
116
- path: .agents/skills/trace/SKILL.md
117
- source_root: package
118
- verify:
119
- path: .agents/skills/verify/SKILL.md
120
- source_root: package
121
- profiles:
122
- moka-orchestrator:
123
- runner: opencode
124
- description: Orchestrate the configured pipeline and enforce gates.
125
- instructions: { inline: "Orchestrate the configured pipeline through package-defined entrypoints, native agents, and gates. Do not bypass configured runner subprocesses or package-configured gates." }
126
- skills: [execute, quick, inspect]
127
- mcp_servers: [pipeline-gateway]
128
- tools: [read, list, grep, glob, bash]
129
- filesystem: { mode: read-only, allow: ["**/*"], deny: ["node_modules/**", "dist/**", ".git/**"] }
130
- network: { mode: inherit }
131
- moka-researcher:
132
- runner: opencode
133
- description: Research the requested task and produce structured findings.
134
- instructions: { inline: "Inspect first-party source, tests, docs, and task context for the current task only. Produce concise findings with file references and stop; do not perform open-ended repository exploration." }
135
- timeout_ms: 900000
136
- skills: [research, spec, scope]
137
- mcp_servers: [pipeline-gateway]
138
- tools: [read, list, grep, glob, bash]
139
- filesystem: { mode: read-only, allow: ["**/*"], deny: ["node_modules/**", "dist/**", ".git/**"] }
140
- network: { mode: inherit }
141
- output:
142
- format: json_schema
143
- schema_path: .pipeline/schemas/research.schema.json
144
- repair: { enabled: true, max_attempts: 1 }
145
- moka-inspector:
146
- runner: opencode
147
- model: openai/gpt-5.5-low
148
- description: Inspect the repository without modifying files.
149
- instructions: { inline: "Inspect the repository without modifying files." }
150
- skills: [research]
151
- mcp_servers: [pipeline-gateway]
152
- tools: [read, list, grep, glob, bash]
153
- filesystem: { mode: read-only, allow: ["**/*"], deny: ["node_modules/**", "dist/**", ".git/**"] }
154
- network: { mode: inherit }
155
- moka-schedule-planner:
156
- runner: opencode
157
- model: openai/gpt-5.5-xhigh
158
- description: Refine a baseline schedule into a specialized approved-plan artifact.
159
- instructions: { inline: "Generate exactly one workflow named root as an explicit schedule graph. Return YAML only." }
160
- timeout_ms: 300000
161
- skills: [schedule-graph-shaping]
162
- mcp_servers: [pipeline-gateway]
163
- tools: [read, list, grep, glob, bash]
164
- filesystem: { mode: read-only, allow: ["**/*"], deny: ["node_modules/**", "dist/**", ".git/**"] }
165
- network: { mode: inherit }
166
- moka-test-writer:
167
- runner: opencode
168
- scheduling_roles: [implementation]
169
- description: Add focused failing tests for the requested behavior.
170
- instructions: { inline: "Add focused failing tests for the requested behavior only. Do not change production code. Only edit files matching test paths such as **/*.test.*, **/*.spec.*, **/*_test.*, **/__tests__/**, test/**, or tests/**. Return only valid JSON with top-level changes and verification. Every changes entry must include summary, why, and files. Include risks, followups, and lessons when present. Do not use Markdown fences or prose outside the JSON object." }
171
- skills: [test]
172
- mcp_servers: [pipeline-gateway]
173
- tools: [read, list, grep, glob, bash, edit, write]
174
- filesystem: { mode: workspace-write, allow: ["**/*"], deny: ["node_modules/**", "dist/**", ".git/**"] }
175
- network: { mode: inherit }
176
- output:
177
- format: json_schema
178
- schema_path: .pipeline/schemas/implementation.schema.json
179
- repair: { enabled: true, max_attempts: 1 }
180
- moka-code-writer:
181
- runner: opencode
182
- scheduling_roles: [implementation]
183
- description: Implement production code until the failing tests pass.
184
- instructions: { inline: "Implement the smallest production change that satisfies the failing tests. Return only valid JSON with top-level changes and verification. Every changes entry must include summary, why, and files. Include risks, followups, and lessons when present. Do not use Markdown fences or prose outside the JSON object." }
185
- skills: [trace, test, fix, library-first-development]
186
- mcp_servers: [pipeline-gateway]
187
- tools: [read, list, grep, glob, bash, edit, write]
188
- filesystem: { mode: workspace-write, allow: ["**/*"], deny: ["node_modules/**", "dist/**", ".git/**"] }
189
- network: { mode: inherit }
190
- output:
191
- format: json_schema
192
- schema_path: .pipeline/schemas/implementation.schema.json
193
- repair: { enabled: true, max_attempts: 1 }
194
- moka-acceptance-reviewer:
195
- runner: opencode
196
- scheduling_roles: [coverage]
197
- description: Audit the finished change against every acceptance criterion.
198
- instructions: { inline: 'Audit the completed change against each canonical acceptance criterion independently. Return only valid JSON with top-level "verdict", "evidence", "acceptance", and optional "violations". Each "acceptance" entry must include "id", "verdict", and non-empty "evidence". Do not use Markdown fences or prose outside the JSON object.' }
199
- skills: [critique, doubt]
200
- mcp_servers: [pipeline-gateway]
201
- tools: [read, list, grep, glob, bash]
202
- filesystem: { mode: read-only, allow: ["**/*"], deny: ["node_modules/**", "dist/**", ".git/**"] }
203
- network: { mode: inherit }
204
- output:
205
- format: json_schema
206
- schema_path: .pipeline/schemas/acceptance.schema.json
207
- repair: { enabled: true, max_attempts: 1 }
208
- moka-thermo-nuclear-reviewer:
209
- runner: opencode
210
- scheduling_roles: [coverage]
211
- description: Perform the final thermo-nuclear code quality review of the integration branch.
212
- instructions: { inline: "Perform the final code quality review of the integration branch." }
213
- skills: [critique]
214
- mcp_servers: [pipeline-gateway]
215
- tools: [read, list, grep, glob, bash]
216
- filesystem: { mode: read-only, allow: ["**/*"], deny: ["node_modules/**", "dist/**", ".git/**"] }
217
- network: { mode: inherit }
218
- output:
219
- format: json_schema
220
- schema_path: .pipeline/schemas/review.schema.json
221
- repair: { enabled: true, max_attempts: 1 }
222
- moka-verifier:
223
- runner: opencode
224
- scheduling_roles: [coverage]
225
- description: Verify checks, implementation fit, and final evidence.
226
- instructions: { inline: 'Verify checks, implementation fit, and final evidence. Return only valid JSON with top-level "verdict", "evidence", and optional "violations". Do not use Markdown fences or prose outside the JSON object.' }
227
- skills: [verify, critique, secure, optimize]
228
- mcp_servers: [pipeline-gateway]
229
- tools: [read, list, grep, glob, bash]
230
- filesystem: { mode: read-only, allow: ["**/*"], deny: ["node_modules/**", "dist/**", ".git/**"] }
231
- network: { mode: inherit }
232
- output:
233
- format: json_schema
234
- schema_path: .pipeline/schemas/verify.schema.json
235
- repair: { enabled: true, max_attempts: 1 }
236
- moka-learner:
237
- runner: opencode
238
- model: openai/gpt-5.5-low
239
- description: Store durable lessons from the completed run.
240
- instructions: { inline: "Store durable lessons from the completed run when useful." }
241
- skills: [migrate]
242
- mcp_servers: [pipeline-gateway]
243
- tools: [read, list, grep, glob, bash]
244
- filesystem: { mode: read-only, allow: ["**/*"], deny: ["node_modules/**", "dist/**", ".git/**"] }
245
- network: { mode: inherit }
246
- output:
247
- format: json_schema
248
- schema_path: .pipeline/schemas/learn.schema.json
249
- repair: { enabled: true, max_attempts: 1 }
250
- `;
251
- const PACKAGE_DEFAULT_PIPELINE_YAML = `version: 1
252
- default_workflow: inspect
253
- orchestrator:
254
- profile: moka-orchestrator
255
- entrypoints:
256
- quick:
257
- schedule: quick-schedule
258
- description: Compact planner-generated pipeline for small work
259
- execute:
260
- schedule: execute-schedule
261
- description: Full planner-generated pipeline for repository work
262
- inspect:
263
- workflow: inspect
264
- description: Read-only repository inspection
265
- hooks:
266
- functions:
267
- generated-defaults-audit:
268
- kind: command
269
- command: [node, -e, "const fs=require('node:fs'); fs.writeFileSync(process.env.PIPELINE_HOOK_RESULT, JSON.stringify({status:'pass',summary:'Generated defaults audit passed'}));"]
270
- trusted: true
271
- timeout_ms: 5000
272
- output_limit_bytes: 4096
273
- on:
274
- workflow.start:
275
- - id: generated-defaults-audit
276
- function: generated-defaults-audit
277
- failure: fail
278
- runner_command:
279
- environment:
280
- setup:
281
- - command: bun
282
- args: [install, --frozen-lockfile]
283
- scheduler:
284
- commands:
285
- quick:
286
- schedule: quick-schedule
287
- catalog: quick
288
- execute:
289
- schedule: execute-schedule
290
- catalog: execute
291
- node_catalogs:
292
- quick:
293
- required_categories: [intake, red, green, mechanical, verification]
294
- nodes:
295
- backlog-intake:
296
- category: intake
297
- profile: moka-researcher
298
- models: [openai/gpt-5.5-medium]
299
- red-tests:
300
- category: red
301
- profile: moka-test-writer
302
- models: [openai/gpt-5.5-high, kimi-for-coding/kimi-k2-thinking]
303
- green-implementation:
304
- category: green
305
- profile: moka-code-writer
306
- models: [openai/gpt-5.5-high, kimi-for-coding/k2p6, opencode-go/qwen3.7-max]
307
- verification:
308
- category: verification
309
- profile: moka-verifier
310
- models: [openai/gpt-5.5-medium]
311
- execute:
312
- required_categories: [intake, research, red, green, mechanical, acceptance, verification, learn]
313
- nodes:
314
- backlog-intake:
315
- category: intake
316
- profile: moka-researcher
317
- models: [openai/gpt-5.5-medium]
318
- research:
319
- category: research
320
- profile: moka-researcher
321
- models: [openai/gpt-5.5-medium, kimi-for-coding/k2p6]
322
- red-tests:
323
- category: red
324
- profile: moka-test-writer
325
- models: [openai/gpt-5.5-high, kimi-for-coding/kimi-k2-thinking]
326
- green-backend:
327
- category: green
328
- profile: moka-code-writer
329
- models: [openai/gpt-5.5-high, kimi-for-coding/k2p6, opencode-go/qwen3.7-max]
330
- green-frontend:
331
- category: green
332
- profile: moka-code-writer
333
- models: [openai/gpt-5.5-high, kimi-for-coding/k2p6, opencode-go/qwen3.7-max]
334
- acceptance-review:
335
- category: acceptance
336
- profile: moka-acceptance-reviewer
337
- models: [openai/gpt-5.5-medium]
338
- verification:
339
- category: verification
340
- profile: moka-verifier
341
- models: [openai/gpt-5.5-medium]
342
- learn:
343
- category: learn
344
- profile: moka-learner
345
- models: [openai/gpt-5.5-low]
346
- schedules:
347
- quick-schedule:
348
- baseline: quick
349
- planner_profile: moka-schedule-planner
350
- node_catalog: quick
351
- execute-schedule:
352
- baseline: execute
353
- planner_profile: moka-schedule-planner
354
- node_catalog: execute
355
- workflows:
356
- inspect:
357
- description: Read-only repository inspection workflow.
358
- nodes:
359
- - id: inspect
360
- kind: agent
361
- profile: moka-inspector
362
- `;
10
+ const DEFAULT_PACKAGE_DEFAULTS_ROOT = new URL("../../defaults/", import.meta.url);
11
+ function loadDefaultYaml(filename) {
12
+ return readFileSync(new URL(filename, DEFAULT_PACKAGE_DEFAULTS_ROOT), "utf8");
13
+ }
14
+ const PACKAGE_DEFAULT_RUNNERS_YAML = loadDefaultYaml("runners.yaml");
15
+ const PACKAGE_DEFAULT_PROFILES_YAML = loadDefaultYaml("profiles.yaml");
16
+ const PACKAGE_DEFAULT_PIPELINE_YAML = loadDefaultYaml("pipeline.yaml");
363
17
  const DEFAULT_OPENCODE_ECOSYSTEM_MANIFEST_URL = new URL(`../../${OPENCODE_ECOSYSTEM_MANIFEST_PATH}`, import.meta.url);
364
18
  const ecosystemStringArraySchema = z.array(z.string().min(1));
365
19
  const ecosystemRuntimeSchema = z.object({
@@ -0,0 +1,30 @@
1
+ import parseGitUrl from "git-url-parse";
2
+ //#region src/git-remote-url.ts
3
+ const GITHUB_SOURCE = "github.com";
4
+ function normalizeRunnerRepositoryForSubmit(repository) {
5
+ const url = normalizeRepositoryUrlForSubmit(repository.url);
6
+ if (url === repository.url) return repository;
7
+ return {
8
+ ...repository,
9
+ url
10
+ };
11
+ }
12
+ function normalizeRepositoryUrlForSubmit(remoteUrl) {
13
+ const parsed = parseGitUrl(remoteUrl);
14
+ if (!isSshRemote(parsed)) return remoteUrl;
15
+ if (parsed.source !== GITHUB_SOURCE) throw new Error(`SSH git remote ${remoteUrl} is not supported for moka submit; use an HTTPS GitHub remote`);
16
+ return `https://${GITHUB_SOURCE}/${gitHubRepositoryPath(parsed, remoteUrl)}`;
17
+ }
18
+ function gitHubRepositoryPath(parsed, remoteUrl) {
19
+ return `${requiredGitHubPathSegment(parsed.owner, remoteUrl)}/${requiredGitHubPathSegment(parsed.name, remoteUrl)}.git`;
20
+ }
21
+ function requiredGitHubPathSegment(value, remoteUrl) {
22
+ const segment = value.trim();
23
+ if (segment.length > 0) return segment;
24
+ throw new Error(`GitHub SSH git remote ${remoteUrl} must include an owner and repository name`);
25
+ }
26
+ function isSshRemote(parsed) {
27
+ return parsed.protocols.includes("ssh");
28
+ }
29
+ //#endregion
30
+ export { normalizeRunnerRepositoryForSubmit };
package/dist/hooks.d.ts CHANGED
@@ -13,8 +13,8 @@ declare const hookResultSchema: z.ZodObject<{
13
13
  taskContext: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
14
14
  }, z.core.$strict>>;
15
15
  status: z.ZodEnum<{
16
- pass: "pass";
17
16
  fail: "fail";
17
+ pass: "pass";
18
18
  skip: "skip";
19
19
  }>;
20
20
  summary: z.ZodOptional<z.ZodString>;
@@ -1,24 +1,160 @@
1
- import { resolvePackageAssetPath } from "../package-assets.js";
2
- import { GENERATED_MARKER, OWNER_MARKER_PREFIX, invocationForHost } from "./shared.js";
3
- import { readFileSync } from "node:fs";
1
+ import { renderClaudeGatewayMcpServers } from "../mcp/gateway.js";
2
+ import { opencodeAgentName } from "../runtime/opencode-agent-name.js";
3
+ import { mergeClaudeSettings } from "../claude-settings-config.js";
4
+ import { CLAUDE_PROJECT_CONFIG_PATH, commandIdForHost, compactLines, entrypointDescription, entrypointEntries, instructionsPointer, invocationForHost } from "./shared.js";
5
+ import { agentDispatchRoutes, entrypointDispatchBlock, grants, header, markdown, projectAgentsMdDefinition, resolvedHostModel, scheduledEntrypointK8sNote } from "./opencode.js";
4
6
  //#region src/install-commands/claude-code.ts
5
- const CLAUDE_CODE_OPENCODE_EXECUTE_SKILL = "claude-code-opencode-execute";
6
- const PACKAGE_SKILL_PATH = `.agents/skills/${CLAUDE_CODE_OPENCODE_EXECUTE_SKILL}/SKILL.md`;
7
- function generatedClaudeSkillContent() {
8
- return readFileSync(resolvePackageAssetPath(PACKAGE_SKILL_PATH), "utf8").replace("# Claude Code OpenCode Execute", [
9
- GENERATED_MARKER,
10
- `${OWNER_MARKER_PREFIX}host=claude-code -->`,
7
+ const CLAUDE_CODE_HOST = "claude-code";
8
+ const CLAUDE_ALLOWED_TOOLS = "Task, Bash(opencode run *)";
9
+ const CLAUDE_AGENT_TOOLS = "Bash, Read";
10
+ const MOKA_PROFILE_PREFIX = "moka-";
11
+ function claudeAgentNameForProfile(profileId) {
12
+ return profileId.startsWith(MOKA_PROFILE_PREFIX) ? profileId : `${MOKA_PROFILE_PREFIX}${profileId}`;
13
+ }
14
+ function cliRoutesForConfig(config) {
15
+ return entrypointEntries(config).flatMap(([, entrypoint]) => "workflow" in entrypoint ? agentDispatchRoutes(CLAUDE_CODE_HOST, config, entrypoint.workflow) : []);
16
+ }
17
+ function distinctCliProfiles(config) {
18
+ const seen = /* @__PURE__ */ new Set();
19
+ const profiles = [];
20
+ for (const route of cliRoutesForConfig(config)) {
21
+ if (route.kind !== "cli" || seen.has(route.profileId)) continue;
22
+ seen.add(route.profileId);
23
+ profiles.push(route);
24
+ }
25
+ return profiles.sort((a, b) => a.profileId.localeCompare(b.profileId));
26
+ }
27
+ function commandDispatchBody(config, id, entrypoint) {
28
+ if (!("workflow" in entrypoint)) return entrypointDispatchBlock(CLAUDE_CODE_HOST, config, id, entrypoint) ?? "";
29
+ const routes = agentDispatchRoutes(CLAUDE_CODE_HOST, config, entrypoint.workflow);
30
+ return [
31
+ `Run workflow \`${entrypoint.workflow}\` for the user task.`,
32
+ "",
33
+ "Delegate each agent node to a Claude Code `Task` subagent that wraps a local `opencode run` subprocess.",
34
+ "Spawn one node at a time respecting `needs`; run nodes whose dependencies are satisfied in parallel.",
35
+ "",
36
+ "Node routes:",
37
+ ...routes.map(claudeNodeRouteLine),
38
+ "",
39
+ "For each node prompt include:",
40
+ "- user task",
41
+ `- workflow id: ${entrypoint.workflow}`,
42
+ "- node id",
43
+ "- profile id",
44
+ "- profile instructions reference",
45
+ "- profile grants",
46
+ "- dependency outputs",
11
47
  "",
12
- "# Claude Code OpenCode Execute"
13
- ].join("\n"));
48
+ "Only package-configured gates are blocking. Do not invent RED, GREEN, full-suite, typecheck, or unrelated-drift gates.",
49
+ "If a node returns targeted evidence and has no configured blocking gate, advance to the next node.",
50
+ "The Task subagents wrap `opencode run` subprocesses; do not claim these worker nodes are Claude Code native agents."
51
+ ].join("\n");
14
52
  }
15
- function claudeCodeDefinitions() {
53
+ function claudeNodeRouteLine(route) {
54
+ const needs = route.needs.length > 0 ? route.needs.join(",") : "none";
55
+ return `- ${route.nodeId}: Task subagent_type=${claudeAgentNameForProfile(route.profileId)} runner=${route.runnerId} agent="${opencodeAgentName(route.profileId)}" needs=${needs}`;
56
+ }
57
+ function commandDefinitions(config) {
58
+ return entrypointEntries(config).map(([id, entrypoint]) => ({
59
+ content: markdown({
60
+ "argument-hint": "<task description>",
61
+ "allowed-tools": CLAUDE_ALLOWED_TOOLS,
62
+ description: entrypointDescription(id, entrypoint)
63
+ }, compactLines([
64
+ header(CLAUDE_CODE_HOST).trimEnd(),
65
+ "",
66
+ `Invoke this command with \`${invocationForHost(CLAUDE_CODE_HOST, id)}\`.`,
67
+ "",
68
+ "Load and follow the `execute` skill for the execution doctrine before dispatching work.",
69
+ "",
70
+ scheduledEntrypointK8sNote(entrypoint),
71
+ scheduledEntrypointK8sNote(entrypoint) ? "" : void 0,
72
+ commandDispatchBody(config, id, entrypoint)
73
+ ]).join("\n")),
74
+ host: CLAUDE_CODE_HOST,
75
+ invocation: invocationForHost(CLAUDE_CODE_HOST, id),
76
+ path: `.claude/commands/${commandIdForHost(CLAUDE_CODE_HOST, id)}.md`
77
+ }));
78
+ }
79
+ function agentModelProjection(config, profile) {
80
+ const model = resolvedHostModel(config, CLAUDE_CODE_HOST, profile);
81
+ return model ? { model } : {};
82
+ }
83
+ function agentDefinitions(config) {
84
+ return distinctCliProfiles(config).map((route) => {
85
+ const profile = route.profile;
86
+ const agentName = claudeAgentNameForProfile(route.profileId);
87
+ const displayName = opencodeAgentName(route.profileId);
88
+ return {
89
+ content: markdown({
90
+ name: agentName,
91
+ description: profile.description ?? route.profileId,
92
+ tools: CLAUDE_AGENT_TOOLS,
93
+ ...agentModelProjection(config, profile)
94
+ }, [
95
+ header(CLAUDE_CODE_HOST).trimEnd(),
96
+ "",
97
+ profile.description ?? route.profileId,
98
+ "",
99
+ `Run EXACTLY ONE \`opencode run --agent "${displayName}" --format json --dir "$PWD" '<node prompt>'\` subprocess for this node.`,
100
+ "Stay inside this node's scope and do not branch into adjacent nodes.",
101
+ "Do not claim completion without fresh evidence from the subprocess output.",
102
+ "Return only: { command, exit status, parsed evidence, touched files, blockers }.",
103
+ "",
104
+ "Configured grants:",
105
+ grants(profile),
106
+ "",
107
+ instructionsPointer(profile)
108
+ ].join("\n")),
109
+ host: CLAUDE_CODE_HOST,
110
+ invocation: invocationForHost(CLAUDE_CODE_HOST),
111
+ path: `.claude/agents/${agentName}.md`
112
+ };
113
+ });
114
+ }
115
+ function settingsDefinition(config) {
116
+ const settings = { permissions: { allow: ["Task", "Bash(opencode run *)"] } };
117
+ if (config.mcp_gateway) settings.mcpServers = renderClaudeGatewayMcpServers(config);
16
118
  return [{
17
- content: generatedClaudeSkillContent(),
18
- host: "claude-code",
19
- invocation: invocationForHost("claude-code", CLAUDE_CODE_OPENCODE_EXECUTE_SKILL),
20
- path: `.claude/skills/${CLAUDE_CODE_OPENCODE_EXECUTE_SKILL}/SKILL.md`
119
+ content: `${JSON.stringify(settings, null, 2)}\n`,
120
+ host: CLAUDE_CODE_HOST,
121
+ invocation: invocationForHost(CLAUDE_CODE_HOST),
122
+ path: CLAUDE_PROJECT_CONFIG_PATH
21
123
  }];
22
124
  }
125
+ function claudeCodeDefinitions(config, cwd) {
126
+ return [
127
+ ...commandDefinitions(config),
128
+ ...agentDefinitions(config),
129
+ ...settingsDefinition(config),
130
+ projectAgentsMdDefinition(cwd, CLAUDE_CODE_HOST)
131
+ ];
132
+ }
133
+ /**
134
+ * The claude-code HostAdapter. Encapsulates all Claude Code-specific command
135
+ * generation, resource roots, and settings-merge behaviour.
136
+ */
137
+ const claudeCodeAdapter = {
138
+ host: "claude-code",
139
+ resourceRoots: [".claude/commands", ".claude/agents"],
140
+ definitions(config, cwd) {
141
+ return claudeCodeDefinitions(config, cwd);
142
+ },
143
+ mergeDefinition(definition, existingContent) {
144
+ if (definition.path !== ".claude/settings.json") return;
145
+ const merged = mergeClaudeSettings(existingContent, JSON.parse(definition.content));
146
+ if (!merged.ok) return {
147
+ ok: false,
148
+ content: definition.content
149
+ };
150
+ return {
151
+ ok: true,
152
+ content: merged.content
153
+ };
154
+ },
155
+ isAlwaysForced(definition) {
156
+ return definition.path === CLAUDE_PROJECT_CONFIG_PATH;
157
+ }
158
+ };
23
159
  //#endregion
24
- export { claudeCodeDefinitions };
160
+ export { claudeCodeAdapter };