@oisincoveney/pipeline 1.19.1 → 1.21.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/README.md +49 -45
- package/dist/commands/pipeline-command.js +0 -1
- package/dist/commands/runner-job-command.js +6 -2
- package/dist/config.d.ts +31 -3
- package/dist/config.js +144 -59
- package/dist/gates.js +22 -6
- package/dist/hooks.d.ts +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +26 -23
- package/dist/install-commands.js +8 -2
- package/dist/mcp/gateway.js +102 -2
- package/dist/mcp/repo-local-backends.js +70 -0
- package/dist/mcp/toolhive-vmcp.js +57 -0
- package/dist/path-refs.js +1 -1
- package/dist/pipeline-init.js +10 -256
- package/dist/pipeline-runtime.d.ts +2 -2
- package/dist/pipeline-runtime.js +210 -5
- package/dist/runner-event-sink.js +27 -7
- package/dist/runner-job/delivery.js +108 -31
- package/dist/runner-job/pr-summary.js +117 -0
- package/dist/runner-job/run.js +165 -55
- package/dist/runner-job/workspace.js +39 -5
- package/dist/runner-job-contract.d.ts +6 -19
- package/dist/runner-job-contract.js +5 -25
- package/dist/runner-job-k8s.d.ts +66 -0
- package/dist/runner-job-k8s.js +144 -0
- package/dist/runner-output.js +21 -19
- package/dist/runner.d.ts +1 -1
- package/dist/runner.js +16 -4
- package/dist/runtime/agent-node/agent-node.js +53 -5
- package/dist/runtime/builtins/builtins.js +11 -4
- package/dist/runtime/context/context.js +1 -0
- package/dist/runtime/contracts/contracts.d.ts +17 -1
- package/dist/runtime/contracts/index.d.ts +1 -1
- package/dist/runtime/events/events.js +45 -1
- package/dist/runtime/hooks/hooks.js +1 -1
- package/dist/runtime/json-validation/json-validation.js +8 -2
- package/dist/runtime/parallel-node/parallel-node.js +1 -0
- package/dist/runtime/worktrees/worktrees.js +11 -1
- package/dist/schedule-planner.js +175 -7
- package/dist/standard-output-schemas.js +80 -0
- package/docs/config-architecture.md +13 -15
- package/docs/mcp-gateway.md +30 -9
- package/docs/mcp-host-isolation.md +2 -1
- package/docs/operator-guide.md +134 -51
- package/docs/pipeline-console-runner-contract.md +39 -30
- package/docs/pipeline-smoke-recovery-plan.md +7 -8
- package/docs/slash-command-adapter-contract.md +5 -6
- package/package.json +6 -2
package/README.md
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
# @oisincoveney/pipeline
|
|
2
2
|
|
|
3
|
-
Config-driven multi-agent pipeline runner for repository work.
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
artifacts.
|
|
3
|
+
Config-driven multi-agent pipeline runner for repository work. Runtime config is
|
|
4
|
+
owned by the installed `@oisincoveney/pipeline` package. Repo-local `.pipeline`
|
|
5
|
+
paths are runtime artifact locations only; they are not the runtime config
|
|
6
|
+
source.
|
|
8
7
|
|
|
9
8
|
## Requirements
|
|
10
9
|
|
|
11
10
|
- Bun 1.1 or newer
|
|
12
11
|
- Node.js 22.13 or newer
|
|
13
|
-
- `npx`, `backlog`, `uvx`, and Docker on `PATH` for default skills and MCP setup
|
|
12
|
+
- `npx`, `backlog`, `uvx`, and Docker on `PATH` for default skills and MCP gateway setup
|
|
14
13
|
- At least one configured runner CLI on `PATH`: `codex`, `opencode`, `kimi`,
|
|
15
14
|
`pi`, or a declared command runner
|
|
16
15
|
|
|
@@ -22,33 +21,31 @@ bun install --frozen-lockfile
|
|
|
22
21
|
|
|
23
22
|
## Start A Repository
|
|
24
23
|
|
|
25
|
-
|
|
24
|
+
Initialize package-owned pipeline support:
|
|
26
25
|
|
|
27
26
|
```shell
|
|
28
27
|
pipe init
|
|
29
28
|
```
|
|
30
29
|
|
|
31
30
|
`pipe init` installs default project skills with
|
|
32
|
-
`npx skills add oisincoveney/skills
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
`
|
|
39
|
-
|
|
40
|
-
The default GitHub MCP registration uses GitHub's official container in
|
|
41
|
-
read-only mode and reads `GITHUB_PERSONAL_ACCESS_TOKEN` from the environment.
|
|
42
|
-
The Momokaya gateway endpoint is protected by Traefik HTTP Basic auth. Set
|
|
43
|
-
`PIPELINE_MCP_GATEWAY_AUTHORIZATION` to the full HTTP `Authorization` header
|
|
44
|
-
value before starting Codex or OpenCode:
|
|
31
|
+
`npx skills add oisincoveney/skills`, then writes generated Codex and OpenCode
|
|
32
|
+
command surfaces plus their singleton `pipeline-gateway` MCP entries. It does
|
|
33
|
+
not create repo-local `.pipeline` config files.
|
|
34
|
+
|
|
35
|
+
The default MCP gateway can run locally or point at the hosted Momokaya
|
|
36
|
+
gateway. Set `PIPELINE_MCP_GATEWAY_AUTHORIZATION` to the full HTTP
|
|
37
|
+
`Authorization` header value before starting Codex or OpenCode when using a
|
|
38
|
+
protected gateway:
|
|
45
39
|
|
|
46
40
|
```shell
|
|
47
41
|
export PIPELINE_MCP_GATEWAY_AUTHORIZATION="Basic $(printf '%s' 'user:password' | base64)"
|
|
48
42
|
```
|
|
49
43
|
|
|
50
|
-
|
|
51
|
-
|
|
44
|
+
To refresh or check generated host files later, use:
|
|
45
|
+
|
|
46
|
+
```shell
|
|
47
|
+
pipe install-commands --host all --check
|
|
48
|
+
```
|
|
52
49
|
|
|
53
50
|
Check local prerequisites and config health:
|
|
54
51
|
|
|
@@ -124,8 +121,11 @@ storage, Kueue discovery, and UI rendering. This package owns the in-container
|
|
|
124
121
|
translation, authenticated event posting, signal cancellation, and final event
|
|
125
122
|
flushing.
|
|
126
123
|
|
|
127
|
-
The console starts the image with
|
|
128
|
-
|
|
124
|
+
The console starts the image with the payload as a mounted ConfigMap file and
|
|
125
|
+
the event auth token as a mounted Secret file. The runner reads the payload
|
|
126
|
+
from `--payload-file` and reads the event auth token from the file path
|
|
127
|
+
specified in `events.authTokenFile`. No environment variables are used for
|
|
128
|
+
payload or auth token delivery. The payload contract is documented in
|
|
129
129
|
[`docs/pipeline-console-runner-contract.md`](docs/pipeline-console-runner-contract.md).
|
|
130
130
|
The executable contract is exported from
|
|
131
131
|
`@oisincoveney/pipeline/runner-job-contract` for payload construction,
|
|
@@ -133,9 +133,12 @@ validation, contract-version checks, and JSON Schema generation.
|
|
|
133
133
|
Use `PIPELINE_TARGET_PATH=/path/to/worktree` when the checked-out target repo is
|
|
134
134
|
mounted somewhere other than the process working directory.
|
|
135
135
|
|
|
136
|
-
##
|
|
136
|
+
## Custom YAML Parts
|
|
137
137
|
|
|
138
|
-
|
|
138
|
+
Runtime execution uses package-owned defaults. Tests and advanced embedding code
|
|
139
|
+
can still parse explicit YAML parts with `parsePipelineConfigParts()`.
|
|
140
|
+
|
|
141
|
+
`runners`:
|
|
139
142
|
|
|
140
143
|
```yaml
|
|
141
144
|
version: 1
|
|
@@ -153,7 +156,7 @@ runners:
|
|
|
153
156
|
output_formats: [text, json, jsonl, json_schema]
|
|
154
157
|
```
|
|
155
158
|
|
|
156
|
-
|
|
159
|
+
`profiles`:
|
|
157
160
|
|
|
158
161
|
```yaml
|
|
159
162
|
version: 1
|
|
@@ -179,7 +182,7 @@ profiles:
|
|
|
179
182
|
format: text
|
|
180
183
|
```
|
|
181
184
|
|
|
182
|
-
|
|
185
|
+
Example workflow shape:
|
|
183
186
|
|
|
184
187
|
```yaml
|
|
185
188
|
version: 1
|
|
@@ -212,14 +215,12 @@ workflows:
|
|
|
212
215
|
builtin: typecheck
|
|
213
216
|
```
|
|
214
217
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
when both are set.
|
|
218
|
+
Package-owned defaults declare `entrypoints` that expose stable app or CLI names
|
|
219
|
+
resolving to workflows or schedule policies. Direct `--workflow` selection
|
|
220
|
+
remains available and takes precedence over `--entrypoint` when both are set.
|
|
219
221
|
|
|
220
|
-
The
|
|
221
|
-
workflow. See `docs/config-architecture.md` for
|
|
222
|
-
support matrix.
|
|
222
|
+
The package defaults include a full research, red, green, verify, learn
|
|
223
|
+
workflow. See `docs/config-architecture.md` for the host support matrix.
|
|
223
224
|
|
|
224
225
|
### Structural Parallelism
|
|
225
226
|
|
|
@@ -289,22 +290,22 @@ branches share a base SHA, and merges passing branches into an integration
|
|
|
289
290
|
branch in declaration order. It reports merge conflicts; it does not resolve
|
|
290
291
|
them automatically.
|
|
291
292
|
|
|
292
|
-
Default profile skills
|
|
293
|
-
Runtime MCP projection and host-specific isolation policy live in
|
|
294
|
-
[`docs/mcp-host-isolation.md`](docs/mcp-host-isolation.md) and
|
|
293
|
+
Default profile skills and generated host resources are installed by
|
|
294
|
+
`pipe init`. Runtime MCP projection and host-specific isolation policy live in
|
|
295
|
+
`src/mcp`; see [`docs/mcp-host-isolation.md`](docs/mcp-host-isolation.md) and
|
|
295
296
|
[`docs/mcp-gateway.md`](docs/mcp-gateway.md).
|
|
296
297
|
|
|
297
298
|
## Generated Host Resources
|
|
298
299
|
|
|
299
|
-
Generate native host files
|
|
300
|
+
Generate native host files during setup:
|
|
300
301
|
|
|
301
302
|
```shell
|
|
302
|
-
pipe
|
|
303
|
+
pipe init
|
|
303
304
|
```
|
|
304
305
|
|
|
305
|
-
Generated resources are derived from
|
|
306
|
-
|
|
307
|
-
|
|
306
|
+
Generated resources are derived from package-owned config; they are not separate
|
|
307
|
+
sources of truth. Host resources use exact native agents when the node runner
|
|
308
|
+
matches the host. OpenCode also uses native subagents for cross-runner
|
|
308
309
|
model-backed nodes when the runner/profile provides an OpenCode-compatible
|
|
309
310
|
`model` or `host_models.opencode` value. Otherwise generated instructions
|
|
310
311
|
dispatch to that runner's CLI instead of inventing a host model.
|
|
@@ -333,8 +334,8 @@ runners:
|
|
|
333
334
|
|
|
334
335
|
## Runtime Guarantees
|
|
335
336
|
|
|
336
|
-
- `pipe run`
|
|
337
|
-
`.pipeline
|
|
337
|
+
- `pipe run` loads package-owned `@oisincoveney/pipeline` config even when the
|
|
338
|
+
repository has no repo-local `.pipeline` config files.
|
|
338
339
|
- Multi-agent workflows execute as separate agent boundaries; nodes are not
|
|
339
340
|
merged into one prompt.
|
|
340
341
|
- Native subagent strategy is preferred when the selected runner can represent
|
|
@@ -410,3 +411,6 @@ bun run check
|
|
|
410
411
|
bun run test
|
|
411
412
|
bun run build:cli
|
|
412
413
|
```
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
Runner Kubernetes Jobs can be generated with `buildRunnerJobK8sManifest` from `@oisincoveney/pipeline/runner-job-k8s`.
|
|
@@ -12,7 +12,6 @@ const BUILTIN_PIPE_COMMANDS = new Set([
|
|
|
12
12
|
]);
|
|
13
13
|
function registerConfiguredEntrypointCommands(program, config, runEntrypoint) {
|
|
14
14
|
const registered = /* @__PURE__ */ new Set();
|
|
15
|
-
if (!config) return registered;
|
|
16
15
|
const reservedCommands = new Set(program.commands.map((command) => command.name()));
|
|
17
16
|
for (const [id, entrypoint] of Object.entries(config.entrypoints)) {
|
|
18
17
|
if (reservedCommands.has(id)) continue;
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { runRunnerJob } from "../runner-job/run.js";
|
|
2
2
|
//#region src/commands/runner-job-command.ts
|
|
3
3
|
function registerRunnerJobCommand(program) {
|
|
4
|
-
program.command("runner-job").description("Run an in-pod pipeline runner job from the console payload").action(async () => {
|
|
5
|
-
const
|
|
4
|
+
program.command("runner-job").description("Run an in-pod pipeline runner job from the console payload").option("--payload-file <path>", "Path to the runner job payload JSON file").option("--orchestrator <name>", "Orchestrator runner (codex|opencode)").argument("[orchestrator]", "Orchestrator runner (codex|opencode)").action(async (orchestratorArg, options) => {
|
|
5
|
+
const orchestrator = orchestratorArg ?? options.orchestrator;
|
|
6
|
+
const exitCode = await runRunnerJob({
|
|
7
|
+
payloadFile: options.payloadFile,
|
|
8
|
+
orchestrator
|
|
9
|
+
});
|
|
6
10
|
process.exitCode = exitCode;
|
|
7
11
|
});
|
|
8
12
|
}
|
package/dist/config.d.ts
CHANGED
|
@@ -10,7 +10,13 @@ declare const HOOK_EVENTS: readonly ["workflow.start", "workflow.success", "work
|
|
|
10
10
|
declare const GATE_KINDS: readonly ["acceptance", "artifact", "builtin", "changed_files", "command", "json_schema", "verdict"];
|
|
11
11
|
declare const SCHEDULE_BASELINES: readonly ["epic", "pipe"];
|
|
12
12
|
declare const SCHEDULING_ROLES: readonly ["coverage", "implementation"];
|
|
13
|
-
|
|
13
|
+
declare const MCP_GATEWAY_BACKEND_LOCALITIES: readonly ["repo-local", "repo-scoped-remote", "shared-remote"];
|
|
14
|
+
declare const MCP_GATEWAY_WORKSPACE_PATH_SOURCES: readonly ["PIPELINE_TARGET_PATH", "cwd"];
|
|
15
|
+
declare const DEFAULT_RUNNER_JOB_GIT_COMMITTER: {
|
|
16
|
+
readonly email: "git@oisin.ee";
|
|
17
|
+
readonly name: "oisin-bot";
|
|
18
|
+
};
|
|
19
|
+
type PipelineConfigErrorCode = "PIPELINE_CONFIG_LEGACY_UNSUPPORTED" | "PIPELINE_CONFIG_PARSE_ERROR" | "PIPELINE_CONFIG_VALIDATION_ERROR";
|
|
14
20
|
interface PipelineConfigIssue {
|
|
15
21
|
message: string;
|
|
16
22
|
path?: string;
|
|
@@ -233,6 +239,19 @@ declare const configSchema: z.ZodObject<{
|
|
|
233
239
|
}, z.core.$strict>>;
|
|
234
240
|
}, z.core.$strict>>;
|
|
235
241
|
mcp_gateway: z.ZodOptional<z.ZodObject<{
|
|
242
|
+
backends: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
243
|
+
locality: z.ZodEnum<{
|
|
244
|
+
"repo-local": "repo-local";
|
|
245
|
+
"repo-scoped-remote": "repo-scoped-remote";
|
|
246
|
+
"shared-remote": "shared-remote";
|
|
247
|
+
}>;
|
|
248
|
+
required: z.ZodDefault<z.ZodBoolean>;
|
|
249
|
+
tool_prefixes: z.ZodArray<z.ZodString>;
|
|
250
|
+
workspace_path_source: z.ZodOptional<z.ZodEnum<{
|
|
251
|
+
PIPELINE_TARGET_PATH: "PIPELINE_TARGET_PATH";
|
|
252
|
+
cwd: "cwd";
|
|
253
|
+
}>>;
|
|
254
|
+
}, z.core.$strict>>>;
|
|
236
255
|
default_profile: z.ZodOptional<z.ZodString>;
|
|
237
256
|
mode: z.ZodEnum<{
|
|
238
257
|
hosted: "hosted";
|
|
@@ -298,6 +317,7 @@ declare const configSchema: z.ZodObject<{
|
|
|
298
317
|
implementation: "implementation";
|
|
299
318
|
}>>>;
|
|
300
319
|
skills: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
320
|
+
timeout_ms: z.ZodOptional<z.ZodNumber>;
|
|
301
321
|
tools: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
302
322
|
read: "read";
|
|
303
323
|
list: "list";
|
|
@@ -322,6 +342,12 @@ declare const configSchema: z.ZodObject<{
|
|
|
322
342
|
required: z.ZodDefault<z.ZodBoolean>;
|
|
323
343
|
}, z.core.$strict>>>;
|
|
324
344
|
}, z.core.$strict>>;
|
|
345
|
+
git: z.ZodDefault<z.ZodObject<{
|
|
346
|
+
committer: z.ZodDefault<z.ZodObject<{
|
|
347
|
+
email: z.ZodDefault<z.ZodString>;
|
|
348
|
+
name: z.ZodDefault<z.ZodString>;
|
|
349
|
+
}, z.core.$strict>>;
|
|
350
|
+
}, z.core.$strict>>;
|
|
325
351
|
}, z.core.$strict>>;
|
|
326
352
|
rules: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
327
353
|
path: z.ZodString;
|
|
@@ -400,6 +426,8 @@ type HookEvent = (typeof HOOK_EVENTS)[number];
|
|
|
400
426
|
type GateKind = (typeof GATE_KINDS)[number];
|
|
401
427
|
type ScheduleBaseline = (typeof SCHEDULE_BASELINES)[number];
|
|
402
428
|
type SchedulingRole = (typeof SCHEDULING_ROLES)[number];
|
|
429
|
+
type McpGatewayBackendLocality = (typeof MCP_GATEWAY_BACKEND_LOCALITIES)[number];
|
|
430
|
+
type McpGatewayWorkspacePathSource = (typeof MCP_GATEWAY_WORKSPACE_PATH_SOURCES)[number];
|
|
403
431
|
interface PipelineConfigParts {
|
|
404
432
|
pipeline: string;
|
|
405
433
|
profiles: string;
|
|
@@ -409,9 +437,9 @@ interface PipelineConfigValidationOptions {
|
|
|
409
437
|
allowMissingLintFileReferences?: boolean;
|
|
410
438
|
}
|
|
411
439
|
declare function loadPipelineConfig(projectRoot: string, options?: PipelineConfigValidationOptions): PipelineConfig;
|
|
412
|
-
declare function
|
|
440
|
+
declare function loadPackagePipelineConfig(projectRoot: string, options?: PipelineConfigValidationOptions): PipelineConfig;
|
|
413
441
|
declare function parsePipelineConfigYaml(source: string, sourcePath?: string, projectRoot?: string): PipelineConfig;
|
|
414
442
|
declare function parsePipelineConfigParts(sources: PipelineConfigParts, projectRoot?: string, sourcePaths?: PipelineConfigParts, options?: PipelineConfigValidationOptions): PipelineConfig;
|
|
415
443
|
declare function validatePipelineConfig(rawConfig: PipelineConfig, projectRoot?: string, options?: PipelineConfigValidationOptions): PipelineConfig;
|
|
416
444
|
//#endregion
|
|
417
|
-
export { GateKind, HookEvent, PIPELINE_CONFIG_PATH, PROFILES_CONFIG_PATH, PipelineConfig, PipelineConfigError, PipelineConfigErrorCode, PipelineConfigIssue, PipelineConfigParts, PipelineConfigValidationOptions, RUNNERS_CONFIG_PATH, RunnerType, ScheduleBaseline, SchedulingRole, WorkflowNodeKind, loadPipelineConfig, parsePipelineConfigParts, parsePipelineConfigYaml,
|
|
445
|
+
export { DEFAULT_RUNNER_JOB_GIT_COMMITTER, GateKind, HookEvent, McpGatewayBackendLocality, McpGatewayWorkspacePathSource, PIPELINE_CONFIG_PATH, PROFILES_CONFIG_PATH, PipelineConfig, PipelineConfigError, PipelineConfigErrorCode, PipelineConfigIssue, PipelineConfigParts, PipelineConfigValidationOptions, RUNNERS_CONFIG_PATH, RunnerType, ScheduleBaseline, SchedulingRole, WorkflowNodeKind, loadPackagePipelineConfig, loadPipelineConfig, parsePipelineConfigParts, parsePipelineConfigYaml, validatePipelineConfig, workflowSchema };
|
package/dist/config.js
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { resolveFileReference } from "./path-refs.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { standardOutputSchemaNameFromPath } from "./standard-output-schemas.js";
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
4
4
|
import { parseDocument } from "yaml";
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
//#region src/config.ts
|
|
7
7
|
const PIPELINE_CONFIG_PATH = ".pipeline/pipeline.yaml";
|
|
8
8
|
const RUNNERS_CONFIG_PATH = ".pipeline/runners.yaml";
|
|
9
9
|
const PROFILES_CONFIG_PATH = ".pipeline/profiles.yaml";
|
|
10
|
-
const LEGACY_CONFIG_PATH = ".pipeline/config.toml";
|
|
11
10
|
const ID_RE = /^[a-z][a-z0-9-]*$/;
|
|
12
11
|
const RUNNER_TYPES = [
|
|
13
12
|
"codex",
|
|
@@ -56,7 +55,17 @@ const RETRY_REASONS = [
|
|
|
56
55
|
];
|
|
57
56
|
const SCHEDULE_BASELINES = ["epic", "pipe"];
|
|
58
57
|
const SCHEDULING_ROLES = ["coverage", "implementation"];
|
|
58
|
+
const MCP_GATEWAY_BACKEND_LOCALITIES = [
|
|
59
|
+
"repo-local",
|
|
60
|
+
"repo-scoped-remote",
|
|
61
|
+
"shared-remote"
|
|
62
|
+
];
|
|
63
|
+
const MCP_GATEWAY_WORKSPACE_PATH_SOURCES = ["PIPELINE_TARGET_PATH", "cwd"];
|
|
59
64
|
const PIPELINE_GATEWAY_SERVER_ID = "pipeline-gateway";
|
|
65
|
+
const DEFAULT_RUNNER_JOB_GIT_COMMITTER = {
|
|
66
|
+
email: "git@oisin.ee",
|
|
67
|
+
name: "oisin-bot"
|
|
68
|
+
};
|
|
60
69
|
const PACKAGE_DEFAULT_RUNNERS_YAML = `version: 1
|
|
61
70
|
runners:
|
|
62
71
|
codex:
|
|
@@ -73,7 +82,7 @@ runners:
|
|
|
73
82
|
output_formats: [text, json, jsonl, json_schema]
|
|
74
83
|
opencode:
|
|
75
84
|
type: opencode
|
|
76
|
-
model:
|
|
85
|
+
model: openai/gpt-5.5
|
|
77
86
|
capabilities:
|
|
78
87
|
native_subagents: true
|
|
79
88
|
rules: true
|
|
@@ -102,6 +111,29 @@ mcp_gateway:
|
|
|
102
111
|
url_env: PIPELINE_MCP_GATEWAY_URL
|
|
103
112
|
authorization_env: PIPELINE_MCP_GATEWAY_AUTHORIZATION
|
|
104
113
|
default_profile: default
|
|
114
|
+
backends:
|
|
115
|
+
context7:
|
|
116
|
+
locality: shared-remote
|
|
117
|
+
tool_prefixes: [context7]
|
|
118
|
+
uidotsh:
|
|
119
|
+
locality: shared-remote
|
|
120
|
+
tool_prefixes: [uidotsh]
|
|
121
|
+
qdrant:
|
|
122
|
+
locality: repo-scoped-remote
|
|
123
|
+
tool_prefixes: [qdrant]
|
|
124
|
+
fallow:
|
|
125
|
+
locality: repo-local
|
|
126
|
+
workspace_path_source: PIPELINE_TARGET_PATH
|
|
127
|
+
required: false
|
|
128
|
+
tool_prefixes: [fallow]
|
|
129
|
+
serena:
|
|
130
|
+
locality: repo-local
|
|
131
|
+
workspace_path_source: PIPELINE_TARGET_PATH
|
|
132
|
+
tool_prefixes: [serena]
|
|
133
|
+
backlog:
|
|
134
|
+
locality: repo-local
|
|
135
|
+
workspace_path_source: PIPELINE_TARGET_PATH
|
|
136
|
+
tool_prefixes: [backlog]
|
|
105
137
|
profiles:
|
|
106
138
|
orchestrator:
|
|
107
139
|
runner: codex
|
|
@@ -113,11 +145,16 @@ profiles:
|
|
|
113
145
|
pipeline-researcher:
|
|
114
146
|
runner: codex
|
|
115
147
|
description: Research the requested task and produce structured findings.
|
|
116
|
-
instructions: { inline: "Inspect first-party source, tests, docs, and task context
|
|
148
|
+
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." }
|
|
149
|
+
timeout_ms: 900000
|
|
117
150
|
mcp_servers: [pipeline-gateway]
|
|
118
151
|
tools: [read, list, grep, glob, bash]
|
|
119
152
|
filesystem: { mode: read-only, allow: ["**/*"], deny: ["node_modules/**", "dist/**", ".git/**"] }
|
|
120
153
|
network: { mode: inherit }
|
|
154
|
+
output:
|
|
155
|
+
format: json_schema
|
|
156
|
+
schema_path: .pipeline/schemas/research.schema.json
|
|
157
|
+
repair: { enabled: true, max_attempts: 1 }
|
|
121
158
|
pipeline-inspector:
|
|
122
159
|
runner: codex
|
|
123
160
|
description: Inspect the repository without modifying files.
|
|
@@ -136,12 +173,17 @@ profiles:
|
|
|
136
173
|
network: { mode: inherit }
|
|
137
174
|
pipeline-test-writer:
|
|
138
175
|
runner: codex
|
|
176
|
+
scheduling_roles: [implementation]
|
|
139
177
|
description: Add focused failing tests for the requested behavior.
|
|
140
|
-
instructions: { inline: "Add focused failing tests for the requested behavior only. Do not change production code." }
|
|
178
|
+
instructions: { inline: "Add focused failing tests for the requested behavior only. Do not change production code. 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." }
|
|
141
179
|
mcp_servers: [pipeline-gateway]
|
|
142
180
|
tools: [read, list, grep, glob, bash, edit, write]
|
|
143
181
|
filesystem: { mode: workspace-write, allow: ["**/*"], deny: ["node_modules/**", "dist/**", ".git/**"] }
|
|
144
182
|
network: { mode: inherit }
|
|
183
|
+
output:
|
|
184
|
+
format: json_schema
|
|
185
|
+
schema_path: .pipeline/schemas/implementation.schema.json
|
|
186
|
+
repair: { enabled: true, max_attempts: 1 }
|
|
145
187
|
pipeline-epic-router:
|
|
146
188
|
runner: codex
|
|
147
189
|
description: Route epic sub-tickets into fixed implementation tracks.
|
|
@@ -154,20 +196,28 @@ profiles:
|
|
|
154
196
|
runner: codex
|
|
155
197
|
scheduling_roles: [implementation]
|
|
156
198
|
description: Implement production code until the failing tests pass.
|
|
157
|
-
instructions: { inline: "Implement the smallest production change that satisfies the failing tests." }
|
|
199
|
+
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." }
|
|
158
200
|
mcp_servers: [pipeline-gateway]
|
|
159
201
|
tools: [read, list, grep, glob, bash, edit, write]
|
|
160
202
|
filesystem: { mode: workspace-write, allow: ["**/*"], deny: ["node_modules/**", "dist/**", ".git/**"] }
|
|
161
203
|
network: { mode: inherit }
|
|
204
|
+
output:
|
|
205
|
+
format: json_schema
|
|
206
|
+
schema_path: .pipeline/schemas/implementation.schema.json
|
|
207
|
+
repair: { enabled: true, max_attempts: 1 }
|
|
162
208
|
pipeline-acceptance-reviewer:
|
|
163
209
|
runner: codex
|
|
164
210
|
scheduling_roles: [coverage]
|
|
165
211
|
description: Audit the finished change against every acceptance criterion.
|
|
166
|
-
instructions: { inline:
|
|
212
|
+
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.' }
|
|
167
213
|
mcp_servers: [pipeline-gateway]
|
|
168
214
|
tools: [read, list, grep, glob, bash]
|
|
169
215
|
filesystem: { mode: read-only, allow: ["**/*"], deny: ["node_modules/**", "dist/**", ".git/**"] }
|
|
170
216
|
network: { mode: inherit }
|
|
217
|
+
output:
|
|
218
|
+
format: json_schema
|
|
219
|
+
schema_path: .pipeline/schemas/acceptance.schema.json
|
|
220
|
+
repair: { enabled: true, max_attempts: 1 }
|
|
171
221
|
pipeline-thermo-nuclear-reviewer:
|
|
172
222
|
runner: codex
|
|
173
223
|
scheduling_roles: [coverage]
|
|
@@ -177,15 +227,23 @@ profiles:
|
|
|
177
227
|
tools: [read, list, grep, glob, bash]
|
|
178
228
|
filesystem: { mode: read-only, allow: ["**/*"], deny: ["node_modules/**", "dist/**", ".git/**"] }
|
|
179
229
|
network: { mode: inherit }
|
|
230
|
+
output:
|
|
231
|
+
format: json_schema
|
|
232
|
+
schema_path: .pipeline/schemas/review.schema.json
|
|
233
|
+
repair: { enabled: true, max_attempts: 1 }
|
|
180
234
|
pipeline-verifier:
|
|
181
235
|
runner: codex
|
|
182
236
|
scheduling_roles: [coverage]
|
|
183
237
|
description: Verify checks, implementation fit, and final evidence.
|
|
184
|
-
instructions: { inline:
|
|
238
|
+
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.' }
|
|
185
239
|
mcp_servers: [pipeline-gateway]
|
|
186
240
|
tools: [read, list, grep, glob, bash]
|
|
187
241
|
filesystem: { mode: read-only, allow: ["**/*"], deny: ["node_modules/**", "dist/**", ".git/**"] }
|
|
188
242
|
network: { mode: inherit }
|
|
243
|
+
output:
|
|
244
|
+
format: json_schema
|
|
245
|
+
schema_path: .pipeline/schemas/verify.schema.json
|
|
246
|
+
repair: { enabled: true, max_attempts: 1 }
|
|
189
247
|
pipeline-learner:
|
|
190
248
|
runner: codex
|
|
191
249
|
description: Store durable lessons from the completed run.
|
|
@@ -194,6 +252,10 @@ profiles:
|
|
|
194
252
|
tools: [read, list, grep, glob, bash]
|
|
195
253
|
filesystem: { mode: read-only, allow: ["**/*"], deny: ["node_modules/**", "dist/**", ".git/**"] }
|
|
196
254
|
network: { mode: inherit }
|
|
255
|
+
output:
|
|
256
|
+
format: json_schema
|
|
257
|
+
schema_path: .pipeline/schemas/learn.schema.json
|
|
258
|
+
repair: { enabled: true, max_attempts: 1 }
|
|
197
259
|
`;
|
|
198
260
|
const PACKAGE_DEFAULT_PIPELINE_YAML = `version: 1
|
|
199
261
|
default_workflow: default
|
|
@@ -213,7 +275,7 @@ hooks:
|
|
|
213
275
|
functions:
|
|
214
276
|
generated-defaults-audit:
|
|
215
277
|
kind: command
|
|
216
|
-
command: [node, -e, "process.
|
|
278
|
+
command: [node, -e, "const fs=require('node:fs'); fs.writeFileSync(process.env.PIPELINE_HOOK_RESULT, JSON.stringify({status:'pass',summary:'Generated defaults audit passed'}));"]
|
|
217
279
|
trusted: true
|
|
218
280
|
timeout_ms: 5000
|
|
219
281
|
output_limit_bytes: 4096
|
|
@@ -340,6 +402,10 @@ workflows:
|
|
|
340
402
|
kind: agent
|
|
341
403
|
profile: pipeline-thermo-nuclear-reviewer
|
|
342
404
|
needs: [merge]
|
|
405
|
+
gates:
|
|
406
|
+
- id: review-verdict
|
|
407
|
+
kind: verdict
|
|
408
|
+
target: stdout
|
|
343
409
|
`;
|
|
344
410
|
var PipelineConfigError = class extends Error {
|
|
345
411
|
code;
|
|
@@ -412,7 +478,28 @@ const mcpServerSchema = z.object({
|
|
|
412
478
|
path: ["bearer_token_env_var"]
|
|
413
479
|
});
|
|
414
480
|
});
|
|
481
|
+
const mcpGatewayBackendSchema = z.object({
|
|
482
|
+
locality: z.enum(MCP_GATEWAY_BACKEND_LOCALITIES),
|
|
483
|
+
required: z.boolean().default(true),
|
|
484
|
+
tool_prefixes: z.array(z.string().min(1)).min(1),
|
|
485
|
+
workspace_path_source: z.enum(MCP_GATEWAY_WORKSPACE_PATH_SOURCES).optional()
|
|
486
|
+
}).strict().superRefine((backend, ctx) => {
|
|
487
|
+
if (backend.locality === "repo-local") {
|
|
488
|
+
if (!backend.workspace_path_source) ctx.addIssue({
|
|
489
|
+
code: "custom",
|
|
490
|
+
message: "repo-local gateway backend must declare workspace_path_source as PIPELINE_TARGET_PATH or cwd",
|
|
491
|
+
path: ["workspace_path_source"]
|
|
492
|
+
});
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
if (backend.workspace_path_source) ctx.addIssue({
|
|
496
|
+
code: "custom",
|
|
497
|
+
message: "workspace_path_source is only valid for repo-local gateway backends",
|
|
498
|
+
path: ["workspace_path_source"]
|
|
499
|
+
});
|
|
500
|
+
});
|
|
415
501
|
const mcpGatewaySchema = z.object({
|
|
502
|
+
backends: strictRecord(mcpGatewayBackendSchema).default({}),
|
|
416
503
|
default_profile: z.string().min(1).optional(),
|
|
417
504
|
mode: z.enum(["hosted", "local"]),
|
|
418
505
|
provider: z.literal("toolhive"),
|
|
@@ -515,6 +602,7 @@ const profileSchema = z.object({
|
|
|
515
602
|
runner: z.string(),
|
|
516
603
|
scheduling_roles: z.array(z.enum(SCHEDULING_ROLES)).optional(),
|
|
517
604
|
skills: z.array(z.string()).optional(),
|
|
605
|
+
timeout_ms: z.number().int().positive().optional(),
|
|
518
606
|
tools: z.array(z.enum(TOOL_NAMES)).optional()
|
|
519
607
|
}).strict();
|
|
520
608
|
const orchestratorSchema = z.object({ profile: z.string() }).strict();
|
|
@@ -654,10 +742,18 @@ const runnerJobEnvironmentSchema = z.object({
|
|
|
654
742
|
setup: z.array(runnerJobCommandSchema).default([]),
|
|
655
743
|
smoke: z.array(runnerJobCommandSchema).default([])
|
|
656
744
|
}).strict();
|
|
657
|
-
const
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
})
|
|
745
|
+
const runnerJobGitCommitterSchema = z.object({
|
|
746
|
+
email: z.string().email().default(DEFAULT_RUNNER_JOB_GIT_COMMITTER.email),
|
|
747
|
+
name: z.string().min(1).default(DEFAULT_RUNNER_JOB_GIT_COMMITTER.name)
|
|
748
|
+
}).strict();
|
|
749
|
+
const runnerJobGitSchema = z.object({ committer: runnerJobGitCommitterSchema.default(DEFAULT_RUNNER_JOB_GIT_COMMITTER) }).strict();
|
|
750
|
+
const runnerJobConfigSchema = z.object({
|
|
751
|
+
environment: runnerJobEnvironmentSchema.default({
|
|
752
|
+
setup: [],
|
|
753
|
+
smoke: []
|
|
754
|
+
}),
|
|
755
|
+
git: runnerJobGitSchema.default({ committer: DEFAULT_RUNNER_JOB_GIT_COMMITTER })
|
|
756
|
+
}).strict();
|
|
661
757
|
const runnersFileSchema = z.object({
|
|
662
758
|
runners: strictRecord(runnerSchema).default({}),
|
|
663
759
|
version: z.literal(1)
|
|
@@ -678,10 +774,13 @@ const pipelineFileSchema = z.object({
|
|
|
678
774
|
on: {}
|
|
679
775
|
}),
|
|
680
776
|
orchestrator: orchestratorSchema,
|
|
681
|
-
runner_job: runnerJobConfigSchema.default({
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
777
|
+
runner_job: runnerJobConfigSchema.default({
|
|
778
|
+
environment: {
|
|
779
|
+
setup: [],
|
|
780
|
+
smoke: []
|
|
781
|
+
},
|
|
782
|
+
git: { committer: DEFAULT_RUNNER_JOB_GIT_COMMITTER }
|
|
783
|
+
}),
|
|
685
784
|
schedules: strictRecord(schedulePolicySchema).default({}),
|
|
686
785
|
task_context: taskContextResolverSchema.optional(),
|
|
687
786
|
workflows: strictRecord(workflowSchema).default({}),
|
|
@@ -698,10 +797,13 @@ const configSchema = z.object({
|
|
|
698
797
|
mcp_servers: strictRecord(mcpServerSchema).default({}),
|
|
699
798
|
orchestrator: orchestratorSchema,
|
|
700
799
|
profiles: strictRecord(profileSchema).default({}),
|
|
701
|
-
runner_job: runnerJobConfigSchema.default({
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
800
|
+
runner_job: runnerJobConfigSchema.default({
|
|
801
|
+
environment: {
|
|
802
|
+
setup: [],
|
|
803
|
+
smoke: []
|
|
804
|
+
},
|
|
805
|
+
git: { committer: DEFAULT_RUNNER_JOB_GIT_COMMITTER }
|
|
806
|
+
}),
|
|
705
807
|
rules: strictRecord(pathRefSchema).default({}),
|
|
706
808
|
runners: strictRecord(runnerSchema).default({}),
|
|
707
809
|
schedules: strictRecord(schedulePolicySchema).default({}),
|
|
@@ -769,39 +871,18 @@ function addConfigSchemaIssue(ctx, path, message) {
|
|
|
769
871
|
});
|
|
770
872
|
}
|
|
771
873
|
function loadPipelineConfig(projectRoot, options = {}) {
|
|
772
|
-
|
|
773
|
-
PIPELINE_CONFIG_PATH,
|
|
774
|
-
PROFILES_CONFIG_PATH,
|
|
775
|
-
RUNNERS_CONFIG_PATH
|
|
776
|
-
];
|
|
777
|
-
const missing = paths.filter((path) => !existsSync(join(projectRoot, path)));
|
|
778
|
-
if (missing.length > 0) {
|
|
779
|
-
if (existsSync(join(projectRoot, LEGACY_CONFIG_PATH))) throw new PipelineConfigError("PIPELINE_CONFIG_LEGACY_UNSUPPORTED", `${LEGACY_CONFIG_PATH} is not supported by the v1 pipeline config. Create ${PIPELINE_CONFIG_PATH}.`, [{
|
|
780
|
-
path: LEGACY_CONFIG_PATH,
|
|
781
|
-
message: "legacy TOML config found"
|
|
782
|
-
}]);
|
|
783
|
-
if (missing.length === paths.length) return parsePipelineConfigParts({
|
|
784
|
-
pipeline: PACKAGE_DEFAULT_PIPELINE_YAML,
|
|
785
|
-
profiles: PACKAGE_DEFAULT_PROFILES_YAML,
|
|
786
|
-
runners: PACKAGE_DEFAULT_RUNNERS_YAML
|
|
787
|
-
}, projectRoot, {
|
|
788
|
-
pipeline: "@oisincoveney/pipeline/defaults/pipeline.yaml",
|
|
789
|
-
profiles: "@oisincoveney/pipeline/defaults/profiles.yaml",
|
|
790
|
-
runners: "@oisincoveney/pipeline/defaults/runners.yaml"
|
|
791
|
-
}, options);
|
|
792
|
-
throw new PipelineConfigError("PIPELINE_CONFIG_MISSING", `Missing required pipeline config files: ${missing.join(", ")}`, missing.map((path) => ({
|
|
793
|
-
path,
|
|
794
|
-
message: "file does not exist"
|
|
795
|
-
})));
|
|
796
|
-
}
|
|
797
|
-
return parsePipelineConfigParts({
|
|
798
|
-
pipeline: readFileSync(join(projectRoot, PIPELINE_CONFIG_PATH), "utf8"),
|
|
799
|
-
profiles: readFileSync(join(projectRoot, PROFILES_CONFIG_PATH), "utf8"),
|
|
800
|
-
runners: readFileSync(join(projectRoot, RUNNERS_CONFIG_PATH), "utf8")
|
|
801
|
-
}, projectRoot, void 0, options);
|
|
874
|
+
return loadPackagePipelineConfig(projectRoot, options);
|
|
802
875
|
}
|
|
803
|
-
function
|
|
804
|
-
return
|
|
876
|
+
function loadPackagePipelineConfig(projectRoot, options = {}) {
|
|
877
|
+
return parsePipelineConfigParts({
|
|
878
|
+
pipeline: PACKAGE_DEFAULT_PIPELINE_YAML,
|
|
879
|
+
profiles: PACKAGE_DEFAULT_PROFILES_YAML,
|
|
880
|
+
runners: PACKAGE_DEFAULT_RUNNERS_YAML
|
|
881
|
+
}, projectRoot, {
|
|
882
|
+
pipeline: "@oisincoveney/pipeline/defaults/pipeline.yaml",
|
|
883
|
+
profiles: "@oisincoveney/pipeline/defaults/profiles.yaml",
|
|
884
|
+
runners: "@oisincoveney/pipeline/defaults/runners.yaml"
|
|
885
|
+
}, options);
|
|
805
886
|
}
|
|
806
887
|
function parsePipelineConfigYaml(source, sourcePath = PIPELINE_CONFIG_PATH, projectRoot) {
|
|
807
888
|
return parsePipelineConfigParts({
|
|
@@ -946,10 +1027,13 @@ function validateActor(label, path, actor, runner, config, issues, projectRoot,
|
|
|
946
1027
|
validatePath(`${path}.instructions.path`, actor.instructions.path, projectRoot, issues, options);
|
|
947
1028
|
validateReferences(`${path}.rules`, actor.rules, config.rules, "rule", issues);
|
|
948
1029
|
validateReferences(`${path}.skills`, actor.skills, config.skills, "skill", issues);
|
|
949
|
-
validateReferences(`${path}.mcp_servers`, actor.mcp_servers, config.mcp_gateway ? {
|
|
950
|
-
|
|
951
|
-
[
|
|
952
|
-
|
|
1030
|
+
validateReferences(`${path}.mcp_servers`, actor.mcp_servers, config.mcp_gateway ? { [PIPELINE_GATEWAY_SERVER_ID]: {} } : config.mcp_servers, "MCP server", issues);
|
|
1031
|
+
if (config.mcp_gateway) {
|
|
1032
|
+
for (const serverId of actor.mcp_servers ?? []) if (serverId !== PIPELINE_GATEWAY_SERVER_ID) issues.push({
|
|
1033
|
+
path: `${path}.mcp_servers`,
|
|
1034
|
+
message: `${path}.mcp_servers must only reference ${PIPELINE_GATEWAY_SERVER_ID} when mcp_gateway is configured`
|
|
1035
|
+
});
|
|
1036
|
+
}
|
|
953
1037
|
validateBooleanCapability(`${path}.rules`, actor.rules, runner.capabilities.rules, "rules", issues);
|
|
954
1038
|
validateBooleanCapability(`${path}.skills`, actor.skills, runner.capabilities.skills, "skills", issues);
|
|
955
1039
|
validateBooleanCapability(`${path}.mcp_servers`, actor.mcp_servers, runner.capabilities.mcp_servers, "MCP servers", issues);
|
|
@@ -1048,6 +1132,7 @@ function validateListCapability(path, requested, supported, label, issues) {
|
|
|
1048
1132
|
}
|
|
1049
1133
|
function validatePath(path, value, projectRoot, issues, options = {}) {
|
|
1050
1134
|
if (!(value && projectRoot)) return;
|
|
1135
|
+
if (standardOutputSchemaNameFromPath(value)) return;
|
|
1051
1136
|
if (!existsSync(resolveFileReference(projectRoot, value))) {
|
|
1052
1137
|
if (options.allowMissingLintFileReferences && isLintableMissingFileReferencePath(path)) return;
|
|
1053
1138
|
issues.push({
|
|
@@ -1066,4 +1151,4 @@ function validationError(issues) {
|
|
|
1066
1151
|
return new PipelineConfigError("PIPELINE_CONFIG_VALIDATION_ERROR", ["Invalid pipeline config:", ...issues.map((issue) => issue.path ? `- ${issue.path}: ${issue.message}` : `- ${issue.message}`)].join("\n"), issues);
|
|
1067
1152
|
}
|
|
1068
1153
|
//#endregion
|
|
1069
|
-
export { PIPELINE_CONFIG_PATH, PROFILES_CONFIG_PATH, PipelineConfigError, RUNNERS_CONFIG_PATH, loadPipelineConfig, parsePipelineConfigParts, parsePipelineConfigYaml,
|
|
1154
|
+
export { DEFAULT_RUNNER_JOB_GIT_COMMITTER, PIPELINE_CONFIG_PATH, PROFILES_CONFIG_PATH, PipelineConfigError, RUNNERS_CONFIG_PATH, loadPackagePipelineConfig, loadPipelineConfig, parsePipelineConfigParts, parsePipelineConfigYaml, validatePipelineConfig, workflowSchema };
|