@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,111 @@
|
|
|
1
|
+
version: 1
|
|
2
|
+
default_workflow: inspect
|
|
3
|
+
orchestrator:
|
|
4
|
+
profile: moka-orchestrator
|
|
5
|
+
entrypoints:
|
|
6
|
+
quick:
|
|
7
|
+
schedule: quick-schedule
|
|
8
|
+
description: Compact planner-generated pipeline for small work
|
|
9
|
+
execute:
|
|
10
|
+
schedule: execute-schedule
|
|
11
|
+
description: Full planner-generated pipeline for repository work
|
|
12
|
+
inspect:
|
|
13
|
+
workflow: inspect
|
|
14
|
+
description: Read-only repository inspection
|
|
15
|
+
hooks:
|
|
16
|
+
functions:
|
|
17
|
+
generated-defaults-audit:
|
|
18
|
+
kind: command
|
|
19
|
+
command: [node, -e, "const fs=require('node:fs'); fs.writeFileSync(process.env.PIPELINE_HOOK_RESULT, JSON.stringify({status:'pass',summary:'Generated defaults audit passed'}));"]
|
|
20
|
+
trusted: true
|
|
21
|
+
timeout_ms: 5000
|
|
22
|
+
output_limit_bytes: 4096
|
|
23
|
+
on:
|
|
24
|
+
workflow.start:
|
|
25
|
+
- id: generated-defaults-audit
|
|
26
|
+
function: generated-defaults-audit
|
|
27
|
+
failure: fail
|
|
28
|
+
runner_command:
|
|
29
|
+
environment:
|
|
30
|
+
setup:
|
|
31
|
+
- command: bun
|
|
32
|
+
args: [install, --frozen-lockfile]
|
|
33
|
+
scheduler:
|
|
34
|
+
commands:
|
|
35
|
+
quick:
|
|
36
|
+
schedule: quick-schedule
|
|
37
|
+
catalog: quick
|
|
38
|
+
execute:
|
|
39
|
+
schedule: execute-schedule
|
|
40
|
+
catalog: execute
|
|
41
|
+
node_catalogs:
|
|
42
|
+
quick:
|
|
43
|
+
required_categories: [intake, red, green, mechanical, verification]
|
|
44
|
+
nodes:
|
|
45
|
+
backlog-intake:
|
|
46
|
+
category: intake
|
|
47
|
+
profile: moka-researcher
|
|
48
|
+
models: [openai/gpt-5.5-medium]
|
|
49
|
+
red-tests:
|
|
50
|
+
category: red
|
|
51
|
+
profile: moka-test-writer
|
|
52
|
+
models: [openai/gpt-5.5-high, kimi-for-coding/kimi-k2-thinking]
|
|
53
|
+
green-implementation:
|
|
54
|
+
category: green
|
|
55
|
+
profile: moka-code-writer
|
|
56
|
+
models: [openai/gpt-5.5-high, kimi-for-coding/k2p6, opencode-go/qwen3.7-max]
|
|
57
|
+
verification:
|
|
58
|
+
category: verification
|
|
59
|
+
profile: moka-verifier
|
|
60
|
+
models: [openai/gpt-5.5-medium]
|
|
61
|
+
execute:
|
|
62
|
+
required_categories: [intake, research, red, green, mechanical, acceptance, verification, learn]
|
|
63
|
+
nodes:
|
|
64
|
+
backlog-intake:
|
|
65
|
+
category: intake
|
|
66
|
+
profile: moka-researcher
|
|
67
|
+
models: [openai/gpt-5.5-medium]
|
|
68
|
+
research:
|
|
69
|
+
category: research
|
|
70
|
+
profile: moka-researcher
|
|
71
|
+
models: [openai/gpt-5.5-medium, kimi-for-coding/k2p6]
|
|
72
|
+
red-tests:
|
|
73
|
+
category: red
|
|
74
|
+
profile: moka-test-writer
|
|
75
|
+
models: [openai/gpt-5.5-high, kimi-for-coding/kimi-k2-thinking]
|
|
76
|
+
green-backend:
|
|
77
|
+
category: green
|
|
78
|
+
profile: moka-code-writer
|
|
79
|
+
models: [openai/gpt-5.5-high, kimi-for-coding/k2p6, opencode-go/qwen3.7-max]
|
|
80
|
+
green-frontend:
|
|
81
|
+
category: green
|
|
82
|
+
profile: moka-code-writer
|
|
83
|
+
models: [openai/gpt-5.5-high, kimi-for-coding/k2p6, opencode-go/qwen3.7-max]
|
|
84
|
+
acceptance-review:
|
|
85
|
+
category: acceptance
|
|
86
|
+
profile: moka-acceptance-reviewer
|
|
87
|
+
models: [openai/gpt-5.5-medium]
|
|
88
|
+
verification:
|
|
89
|
+
category: verification
|
|
90
|
+
profile: moka-verifier
|
|
91
|
+
models: [openai/gpt-5.5-medium]
|
|
92
|
+
learn:
|
|
93
|
+
category: learn
|
|
94
|
+
profile: moka-learner
|
|
95
|
+
models: [openai/gpt-5.5-low]
|
|
96
|
+
schedules:
|
|
97
|
+
quick-schedule:
|
|
98
|
+
baseline: quick
|
|
99
|
+
planner_profile: moka-schedule-planner
|
|
100
|
+
node_catalog: quick
|
|
101
|
+
execute-schedule:
|
|
102
|
+
baseline: execute
|
|
103
|
+
planner_profile: moka-schedule-planner
|
|
104
|
+
node_catalog: execute
|
|
105
|
+
workflows:
|
|
106
|
+
inspect:
|
|
107
|
+
description: Read-only repository inspection workflow.
|
|
108
|
+
nodes:
|
|
109
|
+
- id: inspect
|
|
110
|
+
kind: agent
|
|
111
|
+
profile: moka-inspector
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
version: 1
|
|
2
|
+
mcp_gateway:
|
|
3
|
+
provider: toolhive
|
|
4
|
+
mode: local
|
|
5
|
+
url: https://pipeline-mcp.momokaya.ee/mcp/
|
|
6
|
+
url_env: PIPELINE_MCP_GATEWAY_URL
|
|
7
|
+
authorization_env: PIPELINE_MCP_GATEWAY_AUTHORIZATION
|
|
8
|
+
default_profile: default
|
|
9
|
+
backends:
|
|
10
|
+
context7:
|
|
11
|
+
locality: shared-remote
|
|
12
|
+
tool_prefixes: [context7]
|
|
13
|
+
uidotsh:
|
|
14
|
+
locality: shared-remote
|
|
15
|
+
tool_prefixes: [uidotsh]
|
|
16
|
+
qdrant:
|
|
17
|
+
locality: repo-scoped-remote
|
|
18
|
+
tool_prefixes: [qdrant]
|
|
19
|
+
fallow:
|
|
20
|
+
locality: repo-local
|
|
21
|
+
workspace_path_source: PIPELINE_TARGET_PATH
|
|
22
|
+
required: false
|
|
23
|
+
tool_prefixes: [fallow]
|
|
24
|
+
serena:
|
|
25
|
+
locality: repo-local
|
|
26
|
+
workspace_path_source: PIPELINE_TARGET_PATH
|
|
27
|
+
tool_prefixes: [serena]
|
|
28
|
+
backlog:
|
|
29
|
+
locality: repo-local
|
|
30
|
+
workspace_path_source: PIPELINE_TARGET_PATH
|
|
31
|
+
tool_prefixes: [backlog]
|
|
32
|
+
skills:
|
|
33
|
+
execute:
|
|
34
|
+
path: .agents/skills/execute/SKILL.md
|
|
35
|
+
source_root: package
|
|
36
|
+
inspect:
|
|
37
|
+
path: .agents/skills/inspect/SKILL.md
|
|
38
|
+
source_root: package
|
|
39
|
+
quick:
|
|
40
|
+
path: .agents/skills/quick/SKILL.md
|
|
41
|
+
source_root: package
|
|
42
|
+
critique:
|
|
43
|
+
path: .agents/skills/critique/SKILL.md
|
|
44
|
+
source_root: package
|
|
45
|
+
doubt:
|
|
46
|
+
path: .agents/skills/doubt/SKILL.md
|
|
47
|
+
source_root: package
|
|
48
|
+
fix:
|
|
49
|
+
path: .agents/skills/fix/SKILL.md
|
|
50
|
+
source_root: package
|
|
51
|
+
library-first-development:
|
|
52
|
+
path: .agents/skills/library-first-development/SKILL.md
|
|
53
|
+
source_root: package
|
|
54
|
+
migrate:
|
|
55
|
+
path: .agents/skills/migrate/SKILL.md
|
|
56
|
+
source_root: package
|
|
57
|
+
optimize:
|
|
58
|
+
path: .agents/skills/optimize/SKILL.md
|
|
59
|
+
source_root: package
|
|
60
|
+
research:
|
|
61
|
+
path: .agents/skills/research/SKILL.md
|
|
62
|
+
source_root: package
|
|
63
|
+
schedule-graph-shaping:
|
|
64
|
+
path: .agents/skills/schedule-graph-shaping/SKILL.md
|
|
65
|
+
source_root: package
|
|
66
|
+
scope:
|
|
67
|
+
path: .agents/skills/scope/SKILL.md
|
|
68
|
+
source_root: package
|
|
69
|
+
secure:
|
|
70
|
+
path: .agents/skills/secure/SKILL.md
|
|
71
|
+
source_root: package
|
|
72
|
+
spec:
|
|
73
|
+
path: .agents/skills/spec/SKILL.md
|
|
74
|
+
source_root: package
|
|
75
|
+
test:
|
|
76
|
+
path: .agents/skills/test/SKILL.md
|
|
77
|
+
source_root: package
|
|
78
|
+
trace:
|
|
79
|
+
path: .agents/skills/trace/SKILL.md
|
|
80
|
+
source_root: package
|
|
81
|
+
verify:
|
|
82
|
+
path: .agents/skills/verify/SKILL.md
|
|
83
|
+
source_root: package
|
|
84
|
+
profiles:
|
|
85
|
+
moka-orchestrator:
|
|
86
|
+
runner: opencode
|
|
87
|
+
description: Orchestrate the configured pipeline and enforce gates.
|
|
88
|
+
instructions: { inline: "Orchestrate the configured pipeline through package-defined entrypoints, native agents, and gates. Do not bypass configured runner subprocesses or package-configured gates." }
|
|
89
|
+
skills: [execute, quick, inspect]
|
|
90
|
+
mcp_servers: [pipeline-gateway]
|
|
91
|
+
tools: [read, list, grep, glob, bash]
|
|
92
|
+
filesystem: { mode: read-only, allow: ["**/*"], deny: ["node_modules/**", "dist/**", ".git/**"] }
|
|
93
|
+
network: { mode: inherit }
|
|
94
|
+
moka-researcher:
|
|
95
|
+
runner: opencode
|
|
96
|
+
description: Research the requested task and produce structured findings.
|
|
97
|
+
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." }
|
|
98
|
+
timeout_ms: 900000
|
|
99
|
+
skills: [research, spec, scope]
|
|
100
|
+
mcp_servers: [pipeline-gateway]
|
|
101
|
+
tools: [read, list, grep, glob, bash]
|
|
102
|
+
filesystem: { mode: read-only, allow: ["**/*"], deny: ["node_modules/**", "dist/**", ".git/**"] }
|
|
103
|
+
network: { mode: inherit }
|
|
104
|
+
output:
|
|
105
|
+
format: json_schema
|
|
106
|
+
schema_path: .pipeline/schemas/research.schema.json
|
|
107
|
+
repair: { enabled: true, max_attempts: 1 }
|
|
108
|
+
moka-inspector:
|
|
109
|
+
runner: opencode
|
|
110
|
+
model: openai/gpt-5.5-low
|
|
111
|
+
description: Inspect the repository without modifying files.
|
|
112
|
+
instructions: { inline: "Inspect the repository without modifying files." }
|
|
113
|
+
skills: [research]
|
|
114
|
+
mcp_servers: [pipeline-gateway]
|
|
115
|
+
tools: [read, list, grep, glob, bash]
|
|
116
|
+
filesystem: { mode: read-only, allow: ["**/*"], deny: ["node_modules/**", "dist/**", ".git/**"] }
|
|
117
|
+
network: { mode: inherit }
|
|
118
|
+
moka-schedule-planner:
|
|
119
|
+
runner: opencode
|
|
120
|
+
model: openai/gpt-5.5-xhigh
|
|
121
|
+
description: Refine a baseline schedule into a specialized approved-plan artifact.
|
|
122
|
+
instructions: { inline: "Generate exactly one workflow named root as an explicit schedule graph. Return YAML only." }
|
|
123
|
+
timeout_ms: 300000
|
|
124
|
+
skills: [schedule-graph-shaping]
|
|
125
|
+
mcp_servers: [pipeline-gateway]
|
|
126
|
+
tools: [read, list, grep, glob, bash]
|
|
127
|
+
filesystem: { mode: read-only, allow: ["**/*"], deny: ["node_modules/**", "dist/**", ".git/**"] }
|
|
128
|
+
network: { mode: inherit }
|
|
129
|
+
moka-test-writer:
|
|
130
|
+
runner: opencode
|
|
131
|
+
scheduling_roles: [implementation]
|
|
132
|
+
description: Add focused failing tests for the requested behavior.
|
|
133
|
+
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." }
|
|
134
|
+
skills: [test]
|
|
135
|
+
mcp_servers: [pipeline-gateway]
|
|
136
|
+
tools: [read, list, grep, glob, bash, edit, write]
|
|
137
|
+
filesystem: { mode: workspace-write, allow: ["**/*"], deny: ["node_modules/**", "dist/**", ".git/**"] }
|
|
138
|
+
network: { mode: inherit }
|
|
139
|
+
output:
|
|
140
|
+
format: json_schema
|
|
141
|
+
schema_path: .pipeline/schemas/implementation.schema.json
|
|
142
|
+
repair: { enabled: true, max_attempts: 1 }
|
|
143
|
+
moka-code-writer:
|
|
144
|
+
runner: opencode
|
|
145
|
+
scheduling_roles: [implementation]
|
|
146
|
+
description: Implement production code until the failing tests pass.
|
|
147
|
+
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." }
|
|
148
|
+
skills: [trace, test, fix, library-first-development]
|
|
149
|
+
mcp_servers: [pipeline-gateway]
|
|
150
|
+
tools: [read, list, grep, glob, bash, edit, write]
|
|
151
|
+
filesystem: { mode: workspace-write, allow: ["**/*"], deny: ["node_modules/**", "dist/**", ".git/**"] }
|
|
152
|
+
network: { mode: inherit }
|
|
153
|
+
output:
|
|
154
|
+
format: json_schema
|
|
155
|
+
schema_path: .pipeline/schemas/implementation.schema.json
|
|
156
|
+
repair: { enabled: true, max_attempts: 1 }
|
|
157
|
+
moka-acceptance-reviewer:
|
|
158
|
+
runner: opencode
|
|
159
|
+
scheduling_roles: [coverage]
|
|
160
|
+
description: Audit the finished change against every acceptance criterion.
|
|
161
|
+
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.' }
|
|
162
|
+
skills: [critique, doubt]
|
|
163
|
+
mcp_servers: [pipeline-gateway]
|
|
164
|
+
tools: [read, list, grep, glob, bash]
|
|
165
|
+
filesystem: { mode: read-only, allow: ["**/*"], deny: ["node_modules/**", "dist/**", ".git/**"] }
|
|
166
|
+
network: { mode: inherit }
|
|
167
|
+
output:
|
|
168
|
+
format: json_schema
|
|
169
|
+
schema_path: .pipeline/schemas/acceptance.schema.json
|
|
170
|
+
repair: { enabled: true, max_attempts: 1 }
|
|
171
|
+
moka-thermo-nuclear-reviewer:
|
|
172
|
+
runner: opencode
|
|
173
|
+
scheduling_roles: [coverage]
|
|
174
|
+
description: Perform the final thermo-nuclear code quality review of the integration branch.
|
|
175
|
+
instructions: { inline: "Perform the final code quality review of the integration branch." }
|
|
176
|
+
skills: [critique]
|
|
177
|
+
mcp_servers: [pipeline-gateway]
|
|
178
|
+
tools: [read, list, grep, glob, bash]
|
|
179
|
+
filesystem: { mode: read-only, allow: ["**/*"], deny: ["node_modules/**", "dist/**", ".git/**"] }
|
|
180
|
+
network: { mode: inherit }
|
|
181
|
+
output:
|
|
182
|
+
format: json_schema
|
|
183
|
+
schema_path: .pipeline/schemas/review.schema.json
|
|
184
|
+
repair: { enabled: true, max_attempts: 1 }
|
|
185
|
+
moka-verifier:
|
|
186
|
+
runner: opencode
|
|
187
|
+
scheduling_roles: [coverage]
|
|
188
|
+
description: Verify checks, implementation fit, and final evidence.
|
|
189
|
+
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.' }
|
|
190
|
+
skills: [verify, critique, secure, optimize]
|
|
191
|
+
mcp_servers: [pipeline-gateway]
|
|
192
|
+
tools: [read, list, grep, glob, bash]
|
|
193
|
+
filesystem: { mode: read-only, allow: ["**/*"], deny: ["node_modules/**", "dist/**", ".git/**"] }
|
|
194
|
+
network: { mode: inherit }
|
|
195
|
+
output:
|
|
196
|
+
format: json_schema
|
|
197
|
+
schema_path: .pipeline/schemas/verify.schema.json
|
|
198
|
+
repair: { enabled: true, max_attempts: 1 }
|
|
199
|
+
moka-learner:
|
|
200
|
+
runner: opencode
|
|
201
|
+
model: openai/gpt-5.5-low
|
|
202
|
+
description: Store durable lessons from the completed run.
|
|
203
|
+
instructions: { inline: "Store durable lessons from the completed run when useful." }
|
|
204
|
+
skills: [migrate]
|
|
205
|
+
mcp_servers: [pipeline-gateway]
|
|
206
|
+
tools: [read, list, grep, glob, bash]
|
|
207
|
+
filesystem: { mode: read-only, allow: ["**/*"], deny: ["node_modules/**", "dist/**", ".git/**"] }
|
|
208
|
+
network: { mode: inherit }
|
|
209
|
+
output:
|
|
210
|
+
format: json_schema
|
|
211
|
+
schema_path: .pipeline/schemas/learn.schema.json
|
|
212
|
+
repair: { enabled: true, max_attempts: 1 }
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
version: 1
|
|
2
|
+
runners:
|
|
3
|
+
opencode:
|
|
4
|
+
type: opencode
|
|
5
|
+
capabilities:
|
|
6
|
+
native_subagents: true
|
|
7
|
+
rules: true
|
|
8
|
+
skills: true
|
|
9
|
+
mcp_servers: true
|
|
10
|
+
tools: [read, list, grep, glob, bash, edit, write]
|
|
11
|
+
filesystem: [read-only, workspace-write]
|
|
12
|
+
network: [inherit, disabled]
|
|
13
|
+
output_formats: [text, json, jsonl, json_schema]
|
|
14
|
+
command:
|
|
15
|
+
type: command
|
|
16
|
+
capabilities:
|
|
17
|
+
native_subagents: false
|
|
18
|
+
rules: false
|
|
19
|
+
skills: false
|
|
20
|
+
mcp_servers: false
|
|
21
|
+
tools: [bash]
|
|
22
|
+
filesystem: [read-only, workspace-write]
|
|
23
|
+
network: [inherit, disabled]
|
|
24
|
+
output_formats: [text, json]
|
package/dist/argo-graph.js
CHANGED
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
import { uniqueStrings } from "./strings.js";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
//#region src/argo-graph.ts
|
|
4
|
-
const EXECUTABLE_NODE_KINDS = [
|
|
5
|
-
"agent",
|
|
6
|
-
"builtin",
|
|
7
|
-
"command"
|
|
8
|
-
];
|
|
9
4
|
const argoExecutableTaskSchema = z.object({
|
|
10
5
|
dependencies: z.array(z.string().min(1)),
|
|
11
6
|
nodeId: z.string().min(1),
|
|
@@ -18,6 +13,21 @@ const argoExecutionGraphSchema = z.object({
|
|
|
18
13
|
terminalTaskNames: z.array(z.string().min(1)),
|
|
19
14
|
workflowId: z.string().min(1)
|
|
20
15
|
}).strict();
|
|
16
|
+
/**
|
|
17
|
+
* Thrown when the Argo graph compiler encounters a node kind that cannot be
|
|
18
|
+
* lowered to an Argo DAG task. Callers should surface this as a validation
|
|
19
|
+
* failure before attempting a cluster submission.
|
|
20
|
+
*/
|
|
21
|
+
var ArgoGraphCompilerError = class extends Error {
|
|
22
|
+
kind;
|
|
23
|
+
nodeId;
|
|
24
|
+
constructor(kind, nodeId) {
|
|
25
|
+
super(`Argo graph compiler: node kind '${kind}' on node '${nodeId}' cannot be lowered to an Argo DAG task`);
|
|
26
|
+
this.name = "ArgoGraphCompilerError";
|
|
27
|
+
this.kind = kind;
|
|
28
|
+
this.nodeId = nodeId;
|
|
29
|
+
}
|
|
30
|
+
};
|
|
21
31
|
function compileArgoExecutionGraph(plan) {
|
|
22
32
|
const compiler = new ArgoGraphCompiler(plan);
|
|
23
33
|
return argoExecutionGraphSchema.parse(compiler.compile());
|
|
@@ -47,41 +57,63 @@ var ArgoGraphCompiler = class {
|
|
|
47
57
|
}
|
|
48
58
|
}
|
|
49
59
|
compileNodes(nodes, inheritedNeeds) {
|
|
50
|
-
for (const node of nodes)
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
60
|
+
for (const node of nodes) this.compileNode(node, inheritedNeeds);
|
|
61
|
+
}
|
|
62
|
+
compileNode(node, inheritedNeeds) {
|
|
63
|
+
const kind = node.kind;
|
|
64
|
+
switch (kind) {
|
|
65
|
+
case "agent":
|
|
66
|
+
case "builtin":
|
|
67
|
+
case "command":
|
|
68
|
+
this.compileExecutableNode(node, inheritedNeeds);
|
|
69
|
+
return;
|
|
70
|
+
case "group": return;
|
|
71
|
+
case "parallel":
|
|
72
|
+
this.compileParallelNode(node, inheritedNeeds);
|
|
73
|
+
return;
|
|
74
|
+
default: throw new ArgoGraphCompilerError(String(kind), node.id);
|
|
64
75
|
}
|
|
65
76
|
}
|
|
77
|
+
compileExecutableNode(node, inheritedNeeds) {
|
|
78
|
+
const dependencies = this.resolveDependencyTaskNames([...inheritedNeeds, ...node.needs]);
|
|
79
|
+
const task = argoExecutableTaskSchema.parse({
|
|
80
|
+
dependencies,
|
|
81
|
+
nodeId: node.id,
|
|
82
|
+
taskName: argoTaskName(node.id),
|
|
83
|
+
templateName: argoTemplateName(node.id)
|
|
84
|
+
});
|
|
85
|
+
this.tasks.push(task);
|
|
86
|
+
}
|
|
87
|
+
compileParallelNode(node, inheritedNeeds) {
|
|
88
|
+
this.compileNodes(node.children ?? [], [...inheritedNeeds, ...node.needs]);
|
|
89
|
+
}
|
|
66
90
|
resolveDependencyTaskNames(nodeIds) {
|
|
67
91
|
return uniqueStrings(nodeIds.flatMap((nodeId) => this.resolveDependencyNodeIds(nodeId).map((id) => argoTaskName(id))));
|
|
68
92
|
}
|
|
69
93
|
resolveDependencyNodeIds(nodeId) {
|
|
70
94
|
const node = this.nodeById.get(nodeId);
|
|
71
95
|
if (!node) return [];
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
96
|
+
const kind = node.kind;
|
|
97
|
+
switch (kind) {
|
|
98
|
+
case "agent":
|
|
99
|
+
case "builtin":
|
|
100
|
+
case "command": return [node.id];
|
|
101
|
+
case "group": return this.resolveGroupNodeIds(node);
|
|
102
|
+
case "parallel": return this.resolveParallelNodeIds(node);
|
|
103
|
+
default: throw new ArgoGraphCompilerError(String(kind), node.id);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
resolveGroupNodeIds(node) {
|
|
107
|
+
return uniqueStrings([...node.nodes ?? [], ...node.needs].flatMap((id) => this.resolveDependencyNodeIds(id)));
|
|
108
|
+
}
|
|
109
|
+
resolveParallelNodeIds(node) {
|
|
110
|
+
return uniqueStrings((node.children ?? []).flatMap((child) => this.resolveDependencyNodeIds(child.id)));
|
|
76
111
|
}
|
|
77
112
|
terminalTasks() {
|
|
78
113
|
const dependedOn = new Set(this.tasks.flatMap((task) => task.dependencies));
|
|
79
114
|
return this.tasks.filter((task) => !dependedOn.has(task.taskName));
|
|
80
115
|
}
|
|
81
116
|
};
|
|
82
|
-
function isExecutableNode(node) {
|
|
83
|
-
return EXECUTABLE_NODE_KINDS.includes(node.kind);
|
|
84
|
-
}
|
|
85
117
|
function argoTaskName(nodeId) {
|
|
86
118
|
return `node-${nodeId}`;
|
|
87
119
|
}
|
|
@@ -89,4 +121,4 @@ function argoTemplateName(nodeId) {
|
|
|
89
121
|
return `task-${nodeId}`;
|
|
90
122
|
}
|
|
91
123
|
//#endregion
|
|
92
|
-
export { compileArgoExecutionGraph };
|
|
124
|
+
export { ArgoGraphCompilerError, compileArgoExecutionGraph };
|
package/dist/argo-submit.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { compileArgoExecutionGraph } from "./argo-graph.js";
|
|
1
|
+
import { ArgoGraphCompilerError, compileArgoExecutionGraph } from "./argo-graph.js";
|
|
2
2
|
import { buildRunnerTaskDescriptor } from "./runner-command/task-descriptor.js";
|
|
3
3
|
import { buildRunnerArgoWorkflowManifest, runnerArgoWorkflowManifestSchema } from "./argo-workflow.js";
|
|
4
4
|
import { normalizeRunnerRepositoryForSubmit } from "./git-remote-url.js";
|
|
@@ -62,7 +62,7 @@ async function submitRunnerArgoWorkflow(rawOptions, dependencies = {}) {
|
|
|
62
62
|
const scheduleArtifactConfigMapName = `pipeline-schedule-${randomBytes(6).toString("hex")}`;
|
|
63
63
|
const taskDescriptorConfigMapName = `pipeline-task-descriptors-${randomBytes(6).toString("hex")}`;
|
|
64
64
|
if (payload.workflow.id !== compiled.workflowId) throw new Error(`Runner payload workflow '${payload.workflow.id}' does not match schedule workflow '${compiled.workflowId}'`);
|
|
65
|
-
const graph =
|
|
65
|
+
const graph = compileSubmitArgoGraph(compiled);
|
|
66
66
|
const labels = {
|
|
67
67
|
"pipeline.oisin.dev/project": payload.run.project,
|
|
68
68
|
"pipeline.oisin.dev/run-id": payload.run.id,
|
|
@@ -184,6 +184,14 @@ function normalizeRunnerPayloadForSubmit(input) {
|
|
|
184
184
|
payloadJson: JSON.stringify(payload)
|
|
185
185
|
};
|
|
186
186
|
}
|
|
187
|
+
function compileSubmitArgoGraph(compiled) {
|
|
188
|
+
try {
|
|
189
|
+
return compileArgoExecutionGraph(compiled.plan);
|
|
190
|
+
} catch (err) {
|
|
191
|
+
if (err instanceof ArgoGraphCompilerError) throw new Error(`Schedule '${compiled.workflowId}' cannot be submitted: ${err.message}`);
|
|
192
|
+
throw err;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
187
195
|
function apiClients(options, dependencies) {
|
|
188
196
|
if (dependencies.coreApi && dependencies.workflowApi) return {
|
|
189
197
|
coreApi: dependencies.coreApi,
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { applyJsonEdit, ensureTrailingNewline, formatJson, isRecord, parseJsonRecord, setIfMissing } from "./json-config-merge.js";
|
|
2
|
+
//#region src/claude-settings-config.ts
|
|
3
|
+
function mergeClaudeSettings(currentText, projection) {
|
|
4
|
+
if (currentText === void 0) return {
|
|
5
|
+
content: formatJson(projection),
|
|
6
|
+
ok: true
|
|
7
|
+
};
|
|
8
|
+
const parsed = parseJsonRecord(currentText);
|
|
9
|
+
if (!parsed.ok) return parsed;
|
|
10
|
+
return {
|
|
11
|
+
content: ensureTrailingNewline(applyClaudeProjection(currentText, parsed.value, projection)),
|
|
12
|
+
ok: true
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
function applyClaudeProjection(currentText, parsed, projection) {
|
|
16
|
+
return applyPermissionsAllow(applyMcpServersProjection(currentText, parsed, projection), parsed, projection);
|
|
17
|
+
}
|
|
18
|
+
function applyMcpServersProjection(content, parsed, projection) {
|
|
19
|
+
return Object.entries(projection.mcpServers ?? {}).reduce((nextContent, [name, server]) => setIfMissing(nextContent, parsed, ["mcpServers", name], server), content);
|
|
20
|
+
}
|
|
21
|
+
function applyPermissionsAllow(content, parsed, projection) {
|
|
22
|
+
const existing = permissionAllowList(parsed);
|
|
23
|
+
const merged = unionPreservingOrder(existing, projectedAllowList(projection));
|
|
24
|
+
return sameOrderedList(existing, merged) ? content : applyJsonEdit(content, ["permissions", "allow"], merged);
|
|
25
|
+
}
|
|
26
|
+
function projectedAllowList(projection) {
|
|
27
|
+
return projection.permissions?.allow ?? [];
|
|
28
|
+
}
|
|
29
|
+
function unionPreservingOrder(existing, extra) {
|
|
30
|
+
const merged = [...existing];
|
|
31
|
+
for (const entry of extra) if (!merged.includes(entry)) merged.push(entry);
|
|
32
|
+
return merged;
|
|
33
|
+
}
|
|
34
|
+
function sameOrderedList(a, b) {
|
|
35
|
+
return a.length === b.length && a.every((value, index) => value === b[index]);
|
|
36
|
+
}
|
|
37
|
+
function permissionAllowList(parsed) {
|
|
38
|
+
const permissions = parsed.permissions;
|
|
39
|
+
if (!isRecord(permissions)) return [];
|
|
40
|
+
const allow = permissions.allow;
|
|
41
|
+
return Array.isArray(allow) ? allow.filter((entry) => typeof entry === "string") : [];
|
|
42
|
+
}
|
|
43
|
+
//#endregion
|
|
44
|
+
export { mergeClaudeSettings };
|
package/dist/cli/program.js
CHANGED
|
@@ -150,7 +150,7 @@ function createCliProgram() {
|
|
|
150
150
|
const config = loadPipelineConfig(process.env.PIPELINE_TARGET_PATH ?? process.cwd(), { allowMissingLintFileReferences: true });
|
|
151
151
|
console.log(renderGatewayConfig(config));
|
|
152
152
|
});
|
|
153
|
-
gatewayCommand.command("configure-host").description("Rewrite host MCP config to the singleton pipeline gateway").addOption(new Option("--host <host>", "host config to update").choices(["all", "opencode"]).default("all").argParser(
|
|
153
|
+
gatewayCommand.command("configure-host").description("Rewrite host MCP config to the singleton pipeline gateway").addOption(new Option("--host <host>", "host config to update").choices(["all", "opencode"]).default("all").argParser(parseGatewayHost)).addOption(new Option("--scope <scope>", "config scope to update").choices(["project", "global"]).default("project").argParser(parseGatewayHostScope)).action((flags) => {
|
|
154
154
|
const cwd = process.env.PIPELINE_TARGET_PATH ?? process.cwd();
|
|
155
155
|
const result = configureGatewayHosts(loadPipelineConfig(cwd, { allowMissingLintFileReferences: true }), {
|
|
156
156
|
cwd,
|
|
@@ -182,7 +182,11 @@ function createCliProgram() {
|
|
|
182
182
|
const result = await initPipelineProject({ cwd: process.env.PIPELINE_TARGET_PATH ?? process.cwd() });
|
|
183
183
|
console.log(formatPipelineInitResult(result));
|
|
184
184
|
});
|
|
185
|
-
program.command("install-commands").description("Install generated slash-command adapters into this repository").addOption(new Option("--host <host>", "host command set to install").choices([
|
|
185
|
+
program.command("install-commands").description("Install generated slash-command adapters into this repository").addOption(new Option("--host <host>", "host command set to install").choices([
|
|
186
|
+
"all",
|
|
187
|
+
"opencode",
|
|
188
|
+
"claude-code"
|
|
189
|
+
]).default("all").argParser(parseCommandHost)).option("--dry-run", "show planned changes without writing files").option("--check", "fail if generated command files are missing or stale").option("--force", "overwrite manually edited command files").action(async (flags) => {
|
|
186
190
|
const result = await installCommands({
|
|
187
191
|
...flags,
|
|
188
192
|
cwd: process.env.PIPELINE_TARGET_PATH ?? process.cwd()
|
|
@@ -232,6 +236,10 @@ function parseGatewayHostScope(value) {
|
|
|
232
236
|
if (value === "project" || value === "global") return value;
|
|
233
237
|
throw new Error("scope must be project or global");
|
|
234
238
|
}
|
|
239
|
+
function parseGatewayHost(value) {
|
|
240
|
+
if (value === "all" || value === "opencode") return value;
|
|
241
|
+
throw new Error("host must be all or opencode");
|
|
242
|
+
}
|
|
235
243
|
async function runCli(argv) {
|
|
236
244
|
await createCliProgram().parseAsync(argv, { from: "node" });
|
|
237
245
|
}
|