@oisincoveney/pipeline 1.5.1 → 1.5.2
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 +35 -14
- package/dist/config.d.ts +2 -0
- package/dist/config.js +2 -0
- package/dist/index.js +353 -104
- package/dist/install-commands.d.ts +1 -1
- package/dist/pipeline-runtime.js +2 -2
- package/dist/runner.js +0 -4
- package/docs/config-architecture.md +20 -10
- package/docs/slash-command-adapter-contract.md +19 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -152,22 +152,36 @@ Generate native host files from the YAML config:
|
|
|
152
152
|
pipe install-commands --host all
|
|
153
153
|
```
|
|
154
154
|
|
|
155
|
-
Generated resources are
|
|
156
|
-
separate sources of truth.
|
|
157
|
-
host
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
|
163
|
-
|
|
|
164
|
-
|
|
|
165
|
-
|
|
|
166
|
-
|
|
|
155
|
+
Generated resources are derived from the three config files; they are not
|
|
156
|
+
separate sources of truth. Host resources use exact native agents when the node
|
|
157
|
+
runner matches the host. Cross-runner nodes use host-native execution only when
|
|
158
|
+
the host can run the requested model explicitly, such as OpenCode subagents with
|
|
159
|
+
a resolved `model:`; otherwise generated instructions dispatch to that runner's
|
|
160
|
+
CLI instead of doing instruction-only translation.
|
|
161
|
+
|
|
162
|
+
| Host | Generated files | Invocation |
|
|
163
|
+
| ----------- | ------------------------------------------------------ | -------------------- |
|
|
164
|
+
| Claude Code | `.claude/commands/pipe.md`, `.claude/agents/*.md` | `/pipe <task>` |
|
|
165
|
+
| Codex | `.agents/skills/pipe/SKILL.md`, `.codex/agents/*.toml` | `$pipe <task>` |
|
|
166
|
+
| OpenCode | `.opencode/commands/pipe.md`, `.opencode/agents/*.md` | `/pipe <task>` |
|
|
167
|
+
| Kimi | `.kimi/skills/pipe/SKILL.md`, `.kimi/agents/*.yaml` | `/skill:pipe <task>` |
|
|
168
|
+
| Pi | `.pi/prompts/pipe.md`, `.pi/extensions/pipe.ts` | `/pipe <task>` |
|
|
167
169
|
|
|
168
170
|
The installer is idempotent, supports `--check` and `--dry-run`, and refuses to
|
|
169
171
|
overwrite manually edited files unless `--force` is supplied.
|
|
170
172
|
|
|
173
|
+
Runner `model` is the canonical model id. Optional
|
|
174
|
+
`host_models.<host>` entries are only needed when a host uses a different model
|
|
175
|
+
identifier:
|
|
176
|
+
|
|
177
|
+
```yaml
|
|
178
|
+
runners:
|
|
179
|
+
kimi:
|
|
180
|
+
type: kimi
|
|
181
|
+
command: kimi
|
|
182
|
+
model: moonshot/kimi-k2.6
|
|
183
|
+
```
|
|
184
|
+
|
|
171
185
|
## Runtime Guarantees
|
|
172
186
|
|
|
173
187
|
- `pipe run` fails without `.pipeline/pipeline.yaml`,
|
|
@@ -189,9 +203,16 @@ External apps can import the stable config, planner, and runtime surfaces
|
|
|
189
203
|
without deep-importing private source paths:
|
|
190
204
|
|
|
191
205
|
```ts
|
|
192
|
-
import {
|
|
206
|
+
import {
|
|
207
|
+
loadPipelineConfig,
|
|
208
|
+
parsePipelineConfigParts,
|
|
209
|
+
} from "@oisincoveney/pipeline/config";
|
|
193
210
|
import { compileWorkflowPlan } from "@oisincoveney/pipeline/planner";
|
|
194
|
-
import {
|
|
211
|
+
import {
|
|
212
|
+
runPipelineFromConfig,
|
|
213
|
+
type PipelineRuntimeResult,
|
|
214
|
+
type PipelineTaskContext,
|
|
215
|
+
} from "@oisincoveney/pipeline/runtime";
|
|
195
216
|
```
|
|
196
217
|
|
|
197
218
|
## Verification
|
package/dist/config.d.ts
CHANGED
|
@@ -74,6 +74,7 @@ declare const configSchema: z.ZodObject<{
|
|
|
74
74
|
"workspace-write": "workspace-write";
|
|
75
75
|
}>;
|
|
76
76
|
}, z.core.$strict>>;
|
|
77
|
+
host_models: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
77
78
|
instructions: z.ZodObject<{
|
|
78
79
|
inline: z.ZodOptional<z.ZodString>;
|
|
79
80
|
path: z.ZodOptional<z.ZodString>;
|
|
@@ -150,6 +151,7 @@ declare const configSchema: z.ZodObject<{
|
|
|
150
151
|
}>>>;
|
|
151
152
|
}, z.core.$strict>;
|
|
152
153
|
command: z.ZodOptional<z.ZodString>;
|
|
154
|
+
host_models: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
153
155
|
model: z.ZodOptional<z.ZodString>;
|
|
154
156
|
type: z.ZodEnum<{
|
|
155
157
|
claude: "claude";
|
package/dist/config.js
CHANGED
|
@@ -21357,6 +21357,7 @@ var runnerSchema = exports_external.object({
|
|
|
21357
21357
|
args: exports_external.array(exports_external.string()).optional(),
|
|
21358
21358
|
capabilities: runnerCapabilitiesSchema,
|
|
21359
21359
|
command: exports_external.string().optional(),
|
|
21360
|
+
host_models: exports_external.record(exports_external.string(), exports_external.string().min(1)).optional(),
|
|
21360
21361
|
model: exports_external.string().optional(),
|
|
21361
21362
|
type: exports_external.enum(RUNNER_TYPES)
|
|
21362
21363
|
}).strict();
|
|
@@ -21422,6 +21423,7 @@ var retriesSchema = exports_external.object({
|
|
|
21422
21423
|
var profileSchema = exports_external.object({
|
|
21423
21424
|
description: exports_external.string().optional(),
|
|
21424
21425
|
filesystem: filesystemSchema.optional(),
|
|
21426
|
+
host_models: exports_external.record(exports_external.string(), exports_external.string().min(1)).optional(),
|
|
21425
21427
|
instructions: instructionsSchema,
|
|
21426
21428
|
mcp_servers: exports_external.array(exports_external.string()).optional(),
|
|
21427
21429
|
model: exports_external.string().optional(),
|
package/dist/index.js
CHANGED
|
@@ -27430,6 +27430,7 @@ var runnerSchema = exports_external.object({
|
|
|
27430
27430
|
args: exports_external.array(exports_external.string()).optional(),
|
|
27431
27431
|
capabilities: runnerCapabilitiesSchema,
|
|
27432
27432
|
command: exports_external.string().optional(),
|
|
27433
|
+
host_models: exports_external.record(exports_external.string(), exports_external.string().min(1)).optional(),
|
|
27433
27434
|
model: exports_external.string().optional(),
|
|
27434
27435
|
type: exports_external.enum(RUNNER_TYPES)
|
|
27435
27436
|
}).strict();
|
|
@@ -27495,6 +27496,7 @@ var retriesSchema = exports_external.object({
|
|
|
27495
27496
|
var profileSchema = exports_external.object({
|
|
27496
27497
|
description: exports_external.string().optional(),
|
|
27497
27498
|
filesystem: filesystemSchema.optional(),
|
|
27499
|
+
host_models: exports_external.record(exports_external.string(), exports_external.string().min(1)).optional(),
|
|
27498
27500
|
instructions: instructionsSchema,
|
|
27499
27501
|
mcp_servers: exports_external.array(exports_external.string()).optional(),
|
|
27500
27502
|
model: exports_external.string().optional(),
|
|
@@ -27938,8 +27940,8 @@ function validationError(issues) {
|
|
|
27938
27940
|
// src/install-commands.ts
|
|
27939
27941
|
var import_gray_matter = __toESM(require_gray_matter(), 1);
|
|
27940
27942
|
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
|
|
27941
|
-
import { mkdir, writeFile } from "node:fs/promises";
|
|
27942
|
-
import { dirname, join as join2 } from "node:path";
|
|
27943
|
+
import { mkdir, readdir, rm, writeFile } from "node:fs/promises";
|
|
27944
|
+
import { dirname, join as join2, relative } from "node:path";
|
|
27943
27945
|
|
|
27944
27946
|
// node_modules/smol-toml/dist/error.js
|
|
27945
27947
|
/*!
|
|
@@ -28663,8 +28665,10 @@ function codeForIssue(message) {
|
|
|
28663
28665
|
// src/install-commands.ts
|
|
28664
28666
|
var GENERATED_MARKER = "<!-- Generated by @oisincoveney/pipeline. -->";
|
|
28665
28667
|
var GENERATED_TS_MARKER = "// Generated by @oisincoveney/pipeline.";
|
|
28668
|
+
var GENERATED_YAML_MARKER = "# Generated by @oisincoveney/pipeline.";
|
|
28666
28669
|
var OWNER_MARKER_PREFIX = "<!-- @oisincoveney/pipeline:";
|
|
28667
28670
|
var OWNER_TS_MARKER_PREFIX = "// @oisincoveney/pipeline:";
|
|
28671
|
+
var OWNER_YAML_MARKER_PREFIX = "# @oisincoveney/pipeline:";
|
|
28668
28672
|
var COMMAND_HOSTS = [
|
|
28669
28673
|
"claude",
|
|
28670
28674
|
"opencode",
|
|
@@ -28684,6 +28688,17 @@ function tsHeader(host) {
|
|
|
28684
28688
|
].join(`
|
|
28685
28689
|
`);
|
|
28686
28690
|
}
|
|
28691
|
+
function yamlHeader(host) {
|
|
28692
|
+
return [
|
|
28693
|
+
GENERATED_YAML_MARKER,
|
|
28694
|
+
`${OWNER_YAML_MARKER_PREFIX}host=${host}`,
|
|
28695
|
+
""
|
|
28696
|
+
].join(`
|
|
28697
|
+
`);
|
|
28698
|
+
}
|
|
28699
|
+
function hashHeader(host) {
|
|
28700
|
+
return yamlHeader(host);
|
|
28701
|
+
}
|
|
28687
28702
|
function markdown(data, body) {
|
|
28688
28703
|
return `${import_gray_matter.default.stringify(body.trimEnd(), data).trimEnd()}
|
|
28689
28704
|
`;
|
|
@@ -28699,6 +28714,9 @@ function profileEntries(config2) {
|
|
|
28699
28714
|
}
|
|
28700
28715
|
return [...profileIds].sort((a, b) => a.localeCompare(b)).map((id) => [id, config2.profiles[id]]);
|
|
28701
28716
|
}
|
|
28717
|
+
function nativeProfileEntries(host, config2) {
|
|
28718
|
+
return profileEntries(config2).filter(([_, profile]) => canRunNatively(host, config2, profile));
|
|
28719
|
+
}
|
|
28702
28720
|
function hasAgentWorkflowNodes(config2) {
|
|
28703
28721
|
return compileWorkflowPlan(config2).topologicalOrder.some((node) => node.kind === "agent");
|
|
28704
28722
|
}
|
|
@@ -28726,6 +28744,7 @@ function workflowSummary(config2) {
|
|
|
28726
28744
|
`- ${node.id}`,
|
|
28727
28745
|
`kind=${node.kind}`,
|
|
28728
28746
|
node.profile ? `profile=${node.profile}` : "",
|
|
28747
|
+
runnerForNode(config2, node.profile) ? `runner=${runnerForNode(config2, node.profile)}` : "",
|
|
28729
28748
|
node.needs.length ? `needs=${node.needs.join(",")}` : "needs=none"
|
|
28730
28749
|
].filter(Boolean);
|
|
28731
28750
|
return parts.join(" ");
|
|
@@ -28733,6 +28752,62 @@ function workflowSummary(config2) {
|
|
|
28733
28752
|
].join(`
|
|
28734
28753
|
`);
|
|
28735
28754
|
}
|
|
28755
|
+
function runnerForNode(config2, profileId) {
|
|
28756
|
+
return profileId ? config2.profiles[profileId]?.runner : undefined;
|
|
28757
|
+
}
|
|
28758
|
+
function resolvedHostModel(config2, host, profile) {
|
|
28759
|
+
const runner = config2.runners[profile.runner];
|
|
28760
|
+
return profile.host_models?.[host] ?? runner?.host_models?.[host] ?? profile.model ?? runner?.model;
|
|
28761
|
+
}
|
|
28762
|
+
function canRunNatively(host, config2, profile) {
|
|
28763
|
+
if (profile.runner === host) {
|
|
28764
|
+
return true;
|
|
28765
|
+
}
|
|
28766
|
+
return host === "opencode" && isModelRunner(profile.runner) && resolvedHostModel(config2, host, profile) !== undefined;
|
|
28767
|
+
}
|
|
28768
|
+
function isModelRunner(runnerId) {
|
|
28769
|
+
return COMMAND_HOSTS.some((host) => host === runnerId);
|
|
28770
|
+
}
|
|
28771
|
+
function runnerCliLabel(runnerId) {
|
|
28772
|
+
return `${runnerId} CLI`;
|
|
28773
|
+
}
|
|
28774
|
+
function dispatchSummary(host, config2) {
|
|
28775
|
+
const plan = compileWorkflowPlan(config2);
|
|
28776
|
+
const lines = plan.topologicalOrder.flatMap((node) => {
|
|
28777
|
+
if (!(node.kind === "agent" && node.profile)) {
|
|
28778
|
+
return [];
|
|
28779
|
+
}
|
|
28780
|
+
const profile = config2.profiles[node.profile];
|
|
28781
|
+
if (!profile) {
|
|
28782
|
+
return [];
|
|
28783
|
+
}
|
|
28784
|
+
return [dispatchLineForAgent(host, config2, node.id, node.profile, profile)];
|
|
28785
|
+
});
|
|
28786
|
+
return ["Node dispatch:", ...lines.length > 0 ? lines : ["- none"]].join(`
|
|
28787
|
+
`);
|
|
28788
|
+
}
|
|
28789
|
+
function dispatchLineForAgent(host, config2, nodeId, profileId, profile) {
|
|
28790
|
+
if (profile.runner === host) {
|
|
28791
|
+
return sameHostDispatchLine(host, nodeId, profileId, profile.runner);
|
|
28792
|
+
}
|
|
28793
|
+
if (host === "opencode" && isModelRunner(profile.runner)) {
|
|
28794
|
+
const model = resolvedHostModel(config2, host, profile);
|
|
28795
|
+
if (model) {
|
|
28796
|
+
return `- ${nodeId}: OpenCode native subagent profile=${profileId} model=${model} runner=${profile.runner}`;
|
|
28797
|
+
}
|
|
28798
|
+
}
|
|
28799
|
+
return `- ${nodeId}: ${runnerCliLabel(profile.runner)} profile=${profileId} runner=${profile.runner}`;
|
|
28800
|
+
}
|
|
28801
|
+
function sameHostDispatchLine(host, nodeId, profileId, runnerId) {
|
|
28802
|
+
const labels = {
|
|
28803
|
+
claude: `Claude native subagent subagent_type=${profileId}`,
|
|
28804
|
+
codex: `Codex native worker subagent profile=${profileId} agent_type=worker`,
|
|
28805
|
+
kimi: `Kimi native Agent subagent subagent_type=${profileId}`,
|
|
28806
|
+
opencode: `OpenCode native subagent subagent_type=${profileId}`,
|
|
28807
|
+
pi: `Pi native subagent chain profile=${profileId}`
|
|
28808
|
+
};
|
|
28809
|
+
return `- ${nodeId}: ${labels[host]} runner=${runnerId}`;
|
|
28810
|
+
}
|
|
28736
28811
|
function entrypointSummary(config2) {
|
|
28737
28812
|
const entries = Object.entries(config2.entrypoints);
|
|
28738
28813
|
if (entries.length === 0) {
|
|
@@ -28772,9 +28847,36 @@ function nativeDelegationInstruction(host, config2) {
|
|
|
28772
28847
|
return;
|
|
28773
28848
|
}
|
|
28774
28849
|
if (host === "opencode") {
|
|
28775
|
-
return "
|
|
28850
|
+
return "Dispatch each agent workflow node by its runner. For lines marked `OpenCode native subagent`, use OpenCode's task tool with `subagent_type` exactly equal to the configured profile id. For cross-runner nodes with an explicit resolved OpenCode model, use the generated OpenCode native subagent. For lines marked CLI, invoke that runner's CLI directly. Do not substitute a default subagent. Do not use instruction-only translation. Do not invoke package scripts or the pipeline CLI to run this workflow.";
|
|
28851
|
+
}
|
|
28852
|
+
if (host === "codex") {
|
|
28853
|
+
return 'Dispatch each agent workflow node by its runner. For lines marked `Codex native worker subagent`, call `spawn_agent` with `agent_type: "worker"` and `fork_context: false`, then pass a node prompt containing the task, workflow id, node id, profile id, runner id, configured profile instructions, grants, and dependency outputs. Codex `exec` exposes native built-in agent types (`default`, `explorer`, `worker`) rather than arbitrary generated project profile ids, so do not call `spawn_agent` with the profile id as `agent_type`. Do not spawn the default agent for configured Codex nodes. For lines marked CLI, invoke that runner\'s CLI directly. Do not use instruction-only translation. Do not invoke package scripts or the pipeline CLI to run this workflow.';
|
|
28854
|
+
}
|
|
28855
|
+
if (host === "claude") {
|
|
28856
|
+
return "Dispatch each agent workflow node by its runner. For lines marked `Claude native subagent`, use Claude's Task tool with `subagent_type` exactly equal to the configured profile id. For lines marked CLI, invoke that runner's CLI directly. Do not substitute a default subagent. Do not use instruction-only translation. Do not invoke package scripts or the pipeline CLI to run this workflow.";
|
|
28776
28857
|
}
|
|
28777
|
-
|
|
28858
|
+
if (host === "kimi") {
|
|
28859
|
+
return "Dispatch each agent workflow node by its runner. For lines marked `Kimi native Agent subagent`, use Kimi's Agent tool with `subagent_type` exactly equal to the configured profile id when the current Kimi root agent exposes that subagent type. If that native subagent type is not available, invoke Kimi CLI with the generated `.kimi/agents/<profile>.yaml` agent spec. For non-Kimi nodes, invoke that runner's CLI directly. Do not substitute a default subagent. Do not use instruction-only translation. Do not invoke package scripts or the pipeline CLI to run this workflow.";
|
|
28860
|
+
}
|
|
28861
|
+
if (host === "pi") {
|
|
28862
|
+
return "Dispatch each agent workflow node by its runner. Use Pi's native subagent chain only when every agent node is a Pi runner node and the Pi subagent commands are available. If Pi native subagent commands are unavailable, invoke each configured runner through its CLI directly. For mixed-runner workflows, invoke each configured runner through its CLI directly. Do not use instruction-only translation. Do not invoke package scripts or the pipeline CLI to run this workflow.";
|
|
28863
|
+
}
|
|
28864
|
+
return "Dispatch each agent workflow node by its runner. Use this host's native subagent mechanism only when it can actually run the requested runner/model. For cross-runner nodes that cannot be represented natively, invoke that runner's CLI directly. Do not use instruction-only translation. Do not invoke package scripts or the pipeline CLI to run this workflow.";
|
|
28865
|
+
}
|
|
28866
|
+
function cliDispatchInstructions(config2) {
|
|
28867
|
+
if (!hasAgentWorkflowNodes(config2)) {
|
|
28868
|
+
return;
|
|
28869
|
+
}
|
|
28870
|
+
return [
|
|
28871
|
+
"For CLI-dispatched nodes, construct the node prompt with the task, workflow id, node id, profile id, runner id, and dependency outputs.",
|
|
28872
|
+
"CLI dispatch command shapes:",
|
|
28873
|
+
"- codex: `codex exec --json -C <repo-root> --sandbox <mode> --config 'approval_policy=\"never\"' --skip-git-repo-check <node prompt>`",
|
|
28874
|
+
"- kimi: `kimi --print --agent-file .kimi/agents/<profile>.yaml --work-dir <repo-root> --final-message-only --prompt <node prompt>`",
|
|
28875
|
+
"- opencode: `opencode run --agent <profile> --format json --dir <repo-root> <node prompt>`",
|
|
28876
|
+
"- claude: `claude --print -p <node prompt>`",
|
|
28877
|
+
"- pi: `pi --print --no-session <node prompt>`"
|
|
28878
|
+
].join(`
|
|
28879
|
+
`);
|
|
28778
28880
|
}
|
|
28779
28881
|
function compactLines(lines) {
|
|
28780
28882
|
return lines.filter((line) => line !== undefined);
|
|
@@ -28805,10 +28907,14 @@ function claudeDefinitions(config2) {
|
|
|
28805
28907
|
"",
|
|
28806
28908
|
workflowSummary(config2),
|
|
28807
28909
|
"",
|
|
28910
|
+
dispatchSummary("claude", config2),
|
|
28911
|
+
"",
|
|
28808
28912
|
orchestratorBlock(config2),
|
|
28809
28913
|
"",
|
|
28810
28914
|
nativeDelegationInstruction("claude", config2),
|
|
28811
28915
|
"",
|
|
28916
|
+
cliDispatchInstructions(config2),
|
|
28917
|
+
"",
|
|
28812
28918
|
`Delegate work only to configured profiles: ${profileNames(config2)}.`
|
|
28813
28919
|
]).join(`
|
|
28814
28920
|
`)),
|
|
@@ -28816,7 +28922,7 @@ function claudeDefinitions(config2) {
|
|
|
28816
28922
|
invocation: "/pipe <task description>",
|
|
28817
28923
|
path: ".claude/commands/pipe.md"
|
|
28818
28924
|
},
|
|
28819
|
-
...
|
|
28925
|
+
...nativeProfileEntries("claude", config2).map(([id, profile]) => ({
|
|
28820
28926
|
content: markdown({
|
|
28821
28927
|
description: profile.description ?? id,
|
|
28822
28928
|
name: id,
|
|
@@ -28863,10 +28969,14 @@ function opencodeDefinitions(config2) {
|
|
|
28863
28969
|
"",
|
|
28864
28970
|
workflowSummary(config2),
|
|
28865
28971
|
"",
|
|
28972
|
+
dispatchSummary("opencode", config2),
|
|
28973
|
+
"",
|
|
28866
28974
|
orchestratorBlock(config2),
|
|
28867
28975
|
"",
|
|
28868
28976
|
nativeDelegationInstruction("opencode", config2),
|
|
28869
28977
|
"",
|
|
28978
|
+
cliDispatchInstructions(config2),
|
|
28979
|
+
"",
|
|
28870
28980
|
`Delegate work only to configured profiles: ${profileNames(config2)}.`
|
|
28871
28981
|
]).join(`
|
|
28872
28982
|
`)),
|
|
@@ -28886,17 +28996,22 @@ function opencodeDefinitions(config2) {
|
|
|
28886
28996
|
"",
|
|
28887
28997
|
orchestratorBlock(config2),
|
|
28888
28998
|
"",
|
|
28889
|
-
|
|
28999
|
+
dispatchSummary("opencode", config2),
|
|
29000
|
+
"",
|
|
29001
|
+
nativeDelegationInstruction("opencode", config2),
|
|
29002
|
+
"",
|
|
29003
|
+
cliDispatchInstructions(config2)
|
|
28890
29004
|
]).join(`
|
|
28891
29005
|
`)),
|
|
28892
29006
|
host: "opencode",
|
|
28893
29007
|
invocation: "/pipe <task description>",
|
|
28894
29008
|
path: ".opencode/agents/pipeline-orchestrator.md"
|
|
28895
29009
|
},
|
|
28896
|
-
...
|
|
29010
|
+
...nativeProfileEntries("opencode", config2).map(([id, profile]) => ({
|
|
28897
29011
|
content: markdown({
|
|
28898
29012
|
description: profile.description ?? id,
|
|
28899
29013
|
mode: "subagent",
|
|
29014
|
+
...opencodeModelProjection(config2, profile),
|
|
28900
29015
|
permission: opencodePermission(profile)
|
|
28901
29016
|
}, [
|
|
28902
29017
|
header("opencode").trimEnd(),
|
|
@@ -28928,10 +29043,14 @@ function codexDefinitions(config2) {
|
|
|
28928
29043
|
"",
|
|
28929
29044
|
workflowSummary(config2),
|
|
28930
29045
|
"",
|
|
29046
|
+
dispatchSummary("codex", config2),
|
|
29047
|
+
"",
|
|
28931
29048
|
orchestratorBlock(config2),
|
|
28932
29049
|
"",
|
|
28933
29050
|
nativeDelegationInstruction("codex", config2),
|
|
28934
29051
|
"",
|
|
29052
|
+
cliDispatchInstructions(config2),
|
|
29053
|
+
"",
|
|
28935
29054
|
`Use separate configured profiles: ${profileNames(config2)}.`
|
|
28936
29055
|
]).join(`
|
|
28937
29056
|
`)),
|
|
@@ -28939,8 +29058,8 @@ function codexDefinitions(config2) {
|
|
|
28939
29058
|
invocation: "$pipe <task description>",
|
|
28940
29059
|
path: ".agents/skills/pipe/SKILL.md"
|
|
28941
29060
|
},
|
|
28942
|
-
...
|
|
28943
|
-
content: `${stringify({
|
|
29061
|
+
...nativeProfileEntries("codex", config2).map(([id, profile]) => ({
|
|
29062
|
+
content: `${hashHeader("codex")}${stringify({
|
|
28944
29063
|
description: profile.description ?? id,
|
|
28945
29064
|
developer_instructions: [
|
|
28946
29065
|
profile.description ?? id,
|
|
@@ -28963,39 +29082,164 @@ function kimiDefinitions(config2) {
|
|
|
28963
29082
|
return [
|
|
28964
29083
|
{
|
|
28965
29084
|
content: markdown({
|
|
29085
|
+
name: "pipe",
|
|
28966
29086
|
description: "Run the configured pipeline workflow with Kimi agents"
|
|
28967
29087
|
}, compactLines([
|
|
28968
29088
|
header("kimi").trimEnd(),
|
|
28969
29089
|
"",
|
|
28970
29090
|
workflowSummary(config2),
|
|
28971
29091
|
"",
|
|
29092
|
+
dispatchSummary("kimi", config2),
|
|
29093
|
+
"",
|
|
28972
29094
|
orchestratorBlock(config2),
|
|
28973
29095
|
"",
|
|
28974
29096
|
nativeDelegationInstruction("kimi", config2),
|
|
28975
29097
|
"",
|
|
29098
|
+
cliDispatchInstructions(config2),
|
|
29099
|
+
"",
|
|
28976
29100
|
`Use separate configured profiles: ${profileNames(config2)}.`
|
|
28977
29101
|
]).join(`
|
|
28978
29102
|
`)),
|
|
28979
29103
|
host: "kimi",
|
|
28980
|
-
invocation: "/pipe <task description>",
|
|
28981
|
-
path: ".kimi/
|
|
29104
|
+
invocation: "/skill:pipe <task description>",
|
|
29105
|
+
path: ".kimi/skills/pipe/SKILL.md"
|
|
28982
29106
|
},
|
|
28983
|
-
...
|
|
28984
|
-
|
|
29107
|
+
...kimiOrchestratorAgentDefinitions(config2),
|
|
29108
|
+
...nativeProfileEntries("kimi", config2).flatMap(([id, profile]) => kimiAgentDefinitions(id, profile))
|
|
29109
|
+
];
|
|
29110
|
+
}
|
|
29111
|
+
function opencodeModelProjection(config2, profile) {
|
|
29112
|
+
const model = resolvedHostModel(config2, "opencode", profile);
|
|
29113
|
+
return model ? { model } : {};
|
|
29114
|
+
}
|
|
29115
|
+
function kimiAgentDefinitions(id, profile) {
|
|
29116
|
+
const agentPath = `.kimi/agents/${id}.yaml`;
|
|
29117
|
+
const promptPath = `.kimi/agents/${id}.prompt.md`;
|
|
29118
|
+
const promptDefinitions = profile.instructions.inline ? [
|
|
29119
|
+
{
|
|
29120
|
+
content: [
|
|
28985
29121
|
header("kimi").trimEnd(),
|
|
28986
29122
|
"",
|
|
28987
|
-
profile.
|
|
29123
|
+
profile.instructions.inline,
|
|
29124
|
+
""
|
|
29125
|
+
].join(`
|
|
29126
|
+
`),
|
|
29127
|
+
host: "kimi",
|
|
29128
|
+
invocation: "/skill:pipe <task description>",
|
|
29129
|
+
path: promptPath
|
|
29130
|
+
}
|
|
29131
|
+
] : [];
|
|
29132
|
+
const systemPromptPath = profile.instructions.path ? relative(dirname(agentPath), profile.instructions.path).replaceAll("\\", "/") : `./${id}.prompt.md`;
|
|
29133
|
+
return [
|
|
29134
|
+
{
|
|
29135
|
+
content: [
|
|
29136
|
+
yamlHeader("kimi").trimEnd(),
|
|
28988
29137
|
"",
|
|
28989
|
-
|
|
28990
|
-
|
|
29138
|
+
$stringify({
|
|
29139
|
+
version: 1,
|
|
29140
|
+
agent: {
|
|
29141
|
+
allowed_tools: kimiAllowedTools(profile),
|
|
29142
|
+
extend: "default",
|
|
29143
|
+
name: id,
|
|
29144
|
+
system_prompt_path: systemPromptPath
|
|
29145
|
+
}
|
|
29146
|
+
}).trimEnd(),
|
|
29147
|
+
""
|
|
29148
|
+
].join(`
|
|
29149
|
+
`),
|
|
29150
|
+
host: "kimi",
|
|
29151
|
+
invocation: "/skill:pipe <task description>",
|
|
29152
|
+
path: agentPath
|
|
29153
|
+
},
|
|
29154
|
+
...promptDefinitions
|
|
29155
|
+
];
|
|
29156
|
+
}
|
|
29157
|
+
function kimiOrchestratorAgentDefinitions(config2) {
|
|
29158
|
+
const agentPath = ".kimi/agents/pipeline-orchestrator.yaml";
|
|
29159
|
+
const promptPath = ".kimi/agents/pipeline-orchestrator.prompt.md";
|
|
29160
|
+
const nativeKimiProfiles = nativeProfileEntries("kimi", config2);
|
|
29161
|
+
const subagents = Object.fromEntries(nativeKimiProfiles.map(([id, profile]) => [
|
|
29162
|
+
id,
|
|
29163
|
+
{
|
|
29164
|
+
description: profile.description ?? id,
|
|
29165
|
+
path: `./${id}.yaml`
|
|
29166
|
+
}
|
|
29167
|
+
]));
|
|
29168
|
+
const plan = compileWorkflowPlan(config2);
|
|
29169
|
+
const hasKimiNodes = plan.topologicalOrder.some((node) => {
|
|
29170
|
+
if (!(node.kind === "agent" && node.profile)) {
|
|
29171
|
+
return false;
|
|
29172
|
+
}
|
|
29173
|
+
return config2.profiles[node.profile]?.runner === "kimi";
|
|
29174
|
+
});
|
|
29175
|
+
const hasNonKimiNodes = plan.topologicalOrder.some((node) => {
|
|
29176
|
+
if (!(node.kind === "agent" && node.profile)) {
|
|
29177
|
+
return false;
|
|
29178
|
+
}
|
|
29179
|
+
return config2.profiles[node.profile]?.runner !== "kimi";
|
|
29180
|
+
});
|
|
29181
|
+
const allowedTools = [
|
|
29182
|
+
...hasKimiNodes ? ["kimi_cli.tools.agent:Agent"] : [],
|
|
29183
|
+
...hasNonKimiNodes ? ["kimi_cli.tools.shell:Shell"] : []
|
|
29184
|
+
];
|
|
29185
|
+
return [
|
|
29186
|
+
{
|
|
29187
|
+
content: [
|
|
29188
|
+
yamlHeader("kimi").trimEnd(),
|
|
28991
29189
|
"",
|
|
28992
|
-
|
|
29190
|
+
$stringify({
|
|
29191
|
+
version: 1,
|
|
29192
|
+
agent: {
|
|
29193
|
+
allowed_tools: allowedTools,
|
|
29194
|
+
extend: "default",
|
|
29195
|
+
name: "pipeline-orchestrator",
|
|
29196
|
+
...Object.keys(subagents).length > 0 ? { subagents } : {},
|
|
29197
|
+
system_prompt_path: "./pipeline-orchestrator.prompt.md"
|
|
29198
|
+
}
|
|
29199
|
+
}).trimEnd(),
|
|
29200
|
+
""
|
|
28993
29201
|
].join(`
|
|
28994
|
-
`)
|
|
29202
|
+
`),
|
|
28995
29203
|
host: "kimi",
|
|
28996
|
-
invocation: "/pipe <task description>",
|
|
28997
|
-
path:
|
|
28998
|
-
}
|
|
29204
|
+
invocation: 'kimi --agent-file .kimi/agents/pipeline-orchestrator.yaml --work-dir <repo-root> --prompt "/skill:pipe <task description>"',
|
|
29205
|
+
path: agentPath
|
|
29206
|
+
},
|
|
29207
|
+
{
|
|
29208
|
+
content: compactLines([
|
|
29209
|
+
header("kimi").trimEnd(),
|
|
29210
|
+
"",
|
|
29211
|
+
workflowSummary(config2),
|
|
29212
|
+
"",
|
|
29213
|
+
dispatchSummary("kimi", config2),
|
|
29214
|
+
"",
|
|
29215
|
+
orchestratorBlock(config2),
|
|
29216
|
+
"",
|
|
29217
|
+
nativeDelegationInstruction("kimi", config2),
|
|
29218
|
+
"",
|
|
29219
|
+
cliDispatchInstructions(config2),
|
|
29220
|
+
"",
|
|
29221
|
+
"This agent file is the Kimi-native orchestrator surface. Launch Kimi with `--agent-file .kimi/agents/pipeline-orchestrator.yaml` before using `/skill:pipe` when you want Kimi runner nodes to run through Kimi's native Agent tool."
|
|
29222
|
+
]).join(`
|
|
29223
|
+
`),
|
|
29224
|
+
host: "kimi",
|
|
29225
|
+
invocation: 'kimi --agent-file .kimi/agents/pipeline-orchestrator.yaml --work-dir <repo-root> --prompt "/skill:pipe <task description>"',
|
|
29226
|
+
path: promptPath
|
|
29227
|
+
}
|
|
29228
|
+
];
|
|
29229
|
+
}
|
|
29230
|
+
function kimiAllowedTools(profile) {
|
|
29231
|
+
const mapped = new Map([
|
|
29232
|
+
["bash", "kimi_cli.tools.shell:Shell"],
|
|
29233
|
+
["edit", "kimi_cli.tools.file:StrReplaceFile"],
|
|
29234
|
+
["glob", "kimi_cli.tools.file:Glob"],
|
|
29235
|
+
["grep", "kimi_cli.tools.file:Grep"],
|
|
29236
|
+
["list", "kimi_cli.tools.file:Glob"],
|
|
29237
|
+
["read", "kimi_cli.tools.file:ReadFile"],
|
|
29238
|
+
["task", "kimi_cli.tools.agent:Agent"],
|
|
29239
|
+
["write", "kimi_cli.tools.file:WriteFile"]
|
|
29240
|
+
]);
|
|
29241
|
+
return [
|
|
29242
|
+
...new Set((profile.tools ?? []).flatMap((tool) => mapped.get(tool) ?? []))
|
|
28999
29243
|
];
|
|
29000
29244
|
}
|
|
29001
29245
|
function piDefinitions(config2) {
|
|
@@ -29009,9 +29253,13 @@ function piDefinitions(config2) {
|
|
|
29009
29253
|
"",
|
|
29010
29254
|
workflowSummary(config2),
|
|
29011
29255
|
"",
|
|
29256
|
+
dispatchSummary("pi", config2),
|
|
29257
|
+
"",
|
|
29012
29258
|
orchestratorBlock(config2),
|
|
29013
29259
|
"",
|
|
29014
|
-
nativeDelegationInstruction("pi", config2)
|
|
29260
|
+
nativeDelegationInstruction("pi", config2),
|
|
29261
|
+
"",
|
|
29262
|
+
cliDispatchInstructions(config2)
|
|
29015
29263
|
]).join(`
|
|
29016
29264
|
`)),
|
|
29017
29265
|
host: "pi",
|
|
@@ -29022,58 +29270,12 @@ function piDefinitions(config2) {
|
|
|
29022
29270
|
content: [
|
|
29023
29271
|
tsHeader("pi").trimEnd(),
|
|
29024
29272
|
"",
|
|
29025
|
-
"
|
|
29026
|
-
"
|
|
29027
|
-
"
|
|
29028
|
-
"
|
|
29029
|
-
"
|
|
29030
|
-
"
|
|
29031
|
-
"}",
|
|
29032
|
-
"interface PiExtensionApi {",
|
|
29033
|
-
" getCommands?(): PiCommand[];",
|
|
29034
|
-
" registerCommand(",
|
|
29035
|
-
" name: string,",
|
|
29036
|
-
" command: {",
|
|
29037
|
-
" description: string;",
|
|
29038
|
-
" handler(args: string, ctx: PiCommandContext): Promise<void> | void;",
|
|
29039
|
-
" }",
|
|
29040
|
-
" ): void;",
|
|
29041
|
-
"}",
|
|
29042
|
-
"",
|
|
29043
|
-
...piOrchestratorComment(config2),
|
|
29044
|
-
"",
|
|
29045
|
-
...piWorkflowNodesLiteral(config2),
|
|
29046
|
-
"",
|
|
29047
|
-
"function renderSubagentCommand(task: string): string {",
|
|
29048
|
-
' const chain = WORKFLOW_NODES.filter((node) => node.kind === "agent")',
|
|
29049
|
-
" .map((node) => `" + "$" + "{String(node.profile)}: " + "$" + "{node.id} " + "$" + "{task}" + "`)",
|
|
29050
|
-
' .join(" -> ");',
|
|
29051
|
-
[" return `/chain ", "$", "{JSON.stringify(chain)}`;"].join(""),
|
|
29052
|
-
"}",
|
|
29053
|
-
"",
|
|
29054
|
-
"export default function pipelineWorkNext(pi: PiExtensionApi): void {",
|
|
29055
|
-
' pi.registerCommand("pipe", {',
|
|
29056
|
-
' description: "Run the configured pipeline with Pi subagents",',
|
|
29057
|
-
" handler: async (args: string, ctx) => {",
|
|
29058
|
-
' const task = String(args ?? "").trim();',
|
|
29059
|
-
" if (!task) {",
|
|
29060
|
-
' ctx.ui.notify("Usage: /pipe <task description>", "error");',
|
|
29061
|
-
" return;",
|
|
29062
|
-
" }",
|
|
29063
|
-
" const commands =",
|
|
29064
|
-
' typeof pi.getCommands === "function" ? pi.getCommands() : [];',
|
|
29065
|
-
" const hasPiSubagents = commands.some((command) =>",
|
|
29066
|
-
' ["run", "chain", "parallel", "run-chain", "subagents-doctor"].some(',
|
|
29067
|
-
" (name) => command.name === name || command.name.startsWith(`" + "$" + "{name}:" + "`)",
|
|
29068
|
-
" )",
|
|
29069
|
-
" );",
|
|
29070
|
-
" if (!hasPiSubagents) {",
|
|
29071
|
-
' ctx.ui.notify("Install pi-subagents before running /pipe.", "error");',
|
|
29072
|
-
" return;",
|
|
29073
|
-
" }",
|
|
29074
|
-
" await ctx.sendUserMessage(renderSubagentCommand(task));",
|
|
29075
|
-
" },",
|
|
29076
|
-
" });",
|
|
29273
|
+
"// Pi exposes project prompt templates as slash commands.",
|
|
29274
|
+
"// The real /pipe command is .pi/prompts/pipe.md.",
|
|
29275
|
+
"// This generated shim intentionally registers no command so it cannot",
|
|
29276
|
+
"// intercept /pipe in print mode before the prompt-template expansion runs.",
|
|
29277
|
+
"export default function pipelineWorkNext(): void {",
|
|
29278
|
+
" return;",
|
|
29077
29279
|
"}",
|
|
29078
29280
|
""
|
|
29079
29281
|
].join(`
|
|
@@ -29084,30 +29286,6 @@ function piDefinitions(config2) {
|
|
|
29084
29286
|
}
|
|
29085
29287
|
];
|
|
29086
29288
|
}
|
|
29087
|
-
function piWorkflowNodesLiteral(config2) {
|
|
29088
|
-
const plan = compileWorkflowPlan(config2);
|
|
29089
|
-
return [
|
|
29090
|
-
"const WORKFLOW_NODES = [",
|
|
29091
|
-
...plan.topologicalOrder.flatMap((node) => [
|
|
29092
|
-
" {",
|
|
29093
|
-
` id: ${JSON.stringify(node.id)},`,
|
|
29094
|
-
` kind: ${JSON.stringify(node.kind)},`,
|
|
29095
|
-
` needs: ${JSON.stringify(node.needs)},`,
|
|
29096
|
-
` profile: ${JSON.stringify(node.profile ?? null)},`,
|
|
29097
|
-
" },"
|
|
29098
|
-
]),
|
|
29099
|
-
"] as const;"
|
|
29100
|
-
];
|
|
29101
|
-
}
|
|
29102
|
-
function piOrchestratorComment(config2) {
|
|
29103
|
-
return compactLines([
|
|
29104
|
-
"/*",
|
|
29105
|
-
orchestratorBlock(config2),
|
|
29106
|
-
"",
|
|
29107
|
-
nativeDelegationInstruction("pi", config2),
|
|
29108
|
-
"*/"
|
|
29109
|
-
]);
|
|
29110
|
-
}
|
|
29111
29289
|
function instructionsPointer(actor) {
|
|
29112
29290
|
if (actor.instructions.path) {
|
|
29113
29291
|
return `Instructions: ${actor.instructions.path}`;
|
|
@@ -29126,6 +29304,67 @@ function definitionsFor(host, config2) {
|
|
|
29126
29304
|
const hosts = host === "all" ? COMMAND_HOSTS : [host];
|
|
29127
29305
|
return hosts.flatMap((name) => definitions[name]());
|
|
29128
29306
|
}
|
|
29307
|
+
function selectedHosts(host) {
|
|
29308
|
+
return host === "all" ? [...COMMAND_HOSTS] : [host];
|
|
29309
|
+
}
|
|
29310
|
+
var GENERATED_RESOURCE_ROOTS = {
|
|
29311
|
+
claude: [".claude/commands", ".claude/agents"],
|
|
29312
|
+
codex: [".agents/skills", ".codex/agents"],
|
|
29313
|
+
kimi: [".kimi/skills", ".kimi/agents", ".kimi/commands"],
|
|
29314
|
+
opencode: [".opencode/commands", ".opencode/agents"],
|
|
29315
|
+
pi: [".pi/prompts", ".pi/extensions"]
|
|
29316
|
+
};
|
|
29317
|
+
async function listFiles(root) {
|
|
29318
|
+
if (!existsSync2(root)) {
|
|
29319
|
+
return [];
|
|
29320
|
+
}
|
|
29321
|
+
const entries = await readdir(root, { withFileTypes: true });
|
|
29322
|
+
const files = await Promise.all(entries.map((entry) => {
|
|
29323
|
+
const path = join2(root, entry.name);
|
|
29324
|
+
if (entry.isDirectory()) {
|
|
29325
|
+
return listFiles(path);
|
|
29326
|
+
}
|
|
29327
|
+
return [path];
|
|
29328
|
+
}));
|
|
29329
|
+
return files.flat();
|
|
29330
|
+
}
|
|
29331
|
+
function generatedHostFor(content) {
|
|
29332
|
+
return COMMAND_HOSTS.find((host) => content.includes(`${OWNER_MARKER_PREFIX}host=${host} -->`) || content.includes(`${OWNER_TS_MARKER_PREFIX}host=${host}`) || content.includes(`${OWNER_YAML_MARKER_PREFIX}host=${host}`));
|
|
29333
|
+
}
|
|
29334
|
+
async function obsoleteGeneratedItems(cwd, host, wantedPaths) {
|
|
29335
|
+
const hosts = new Set(selectedHosts(host));
|
|
29336
|
+
const roots = selectedHosts(host).flatMap((selectedHost) => GENERATED_RESOURCE_ROOTS[selectedHost]);
|
|
29337
|
+
const files = await Promise.all(roots.map((root) => listFiles(join2(cwd, root))));
|
|
29338
|
+
return files.flat().flatMap((absolutePath) => {
|
|
29339
|
+
const content = readFileSync2(absolutePath, "utf8");
|
|
29340
|
+
const generatedHost = generatedHostFor(content);
|
|
29341
|
+
if (!(generatedHost && hosts.has(generatedHost))) {
|
|
29342
|
+
return [];
|
|
29343
|
+
}
|
|
29344
|
+
const path = relative(cwd, absolutePath).replaceAll("\\", "/");
|
|
29345
|
+
if (wantedPaths.has(path)) {
|
|
29346
|
+
return [];
|
|
29347
|
+
}
|
|
29348
|
+
return [
|
|
29349
|
+
{
|
|
29350
|
+
action: "delete",
|
|
29351
|
+
host: generatedHost,
|
|
29352
|
+
invocation: invocationForHost(generatedHost),
|
|
29353
|
+
path
|
|
29354
|
+
}
|
|
29355
|
+
];
|
|
29356
|
+
}).sort((a, b) => a.path.localeCompare(b.path));
|
|
29357
|
+
}
|
|
29358
|
+
function invocationForHost(host) {
|
|
29359
|
+
const invocations = {
|
|
29360
|
+
claude: "/pipe <task description>",
|
|
29361
|
+
codex: "$pipe <task description>",
|
|
29362
|
+
kimi: "/skill:pipe <task description>",
|
|
29363
|
+
opencode: "/pipe <task description>",
|
|
29364
|
+
pi: "/pipe <task description>"
|
|
29365
|
+
};
|
|
29366
|
+
return invocations[host];
|
|
29367
|
+
}
|
|
29129
29368
|
function actionFor(path, content, force) {
|
|
29130
29369
|
if (!existsSync2(path)) {
|
|
29131
29370
|
return "create";
|
|
@@ -29134,7 +29373,7 @@ function actionFor(path, content, force) {
|
|
|
29134
29373
|
if (current === content) {
|
|
29135
29374
|
return "unchanged";
|
|
29136
29375
|
}
|
|
29137
|
-
if (!(current.includes(GENERATED_MARKER) || current.includes(GENERATED_TS_MARKER) || force)) {
|
|
29376
|
+
if (!(current.includes(GENERATED_MARKER) || current.includes(GENERATED_TS_MARKER) || current.includes(GENERATED_YAML_MARKER) || force)) {
|
|
29138
29377
|
return "conflict";
|
|
29139
29378
|
}
|
|
29140
29379
|
return "update";
|
|
@@ -29144,7 +29383,9 @@ async function installCommands(options2 = {}) {
|
|
|
29144
29383
|
const host = options2.host ?? "all";
|
|
29145
29384
|
const config2 = loadPipelineConfig(cwd);
|
|
29146
29385
|
const items = [];
|
|
29147
|
-
|
|
29386
|
+
const definitions = definitionsFor(host, config2);
|
|
29387
|
+
const wantedPaths = new Set(definitions.map((definition) => definition.path));
|
|
29388
|
+
for (const definition of definitions) {
|
|
29148
29389
|
const target = join2(cwd, definition.path);
|
|
29149
29390
|
const action = actionFor(target, definition.content, Boolean(options2.force));
|
|
29150
29391
|
items.push({
|
|
@@ -29162,6 +29403,13 @@ async function installCommands(options2 = {}) {
|
|
|
29162
29403
|
await mkdir(dirname(target), { recursive: true });
|
|
29163
29404
|
await writeFile(target, definition.content);
|
|
29164
29405
|
}
|
|
29406
|
+
const obsoleteItems = await obsoleteGeneratedItems(cwd, host, wantedPaths);
|
|
29407
|
+
items.push(...obsoleteItems);
|
|
29408
|
+
if (!(options2.check || options2.dryRun)) {
|
|
29409
|
+
for (const item of obsoleteItems) {
|
|
29410
|
+
await rm(join2(cwd, item.path), { force: true });
|
|
29411
|
+
}
|
|
29412
|
+
}
|
|
29165
29413
|
if (!options2.dryRun && items.some((item) => item.action === "conflict")) {
|
|
29166
29414
|
throw new Error([
|
|
29167
29415
|
"Refusing to overwrite manually edited command files.",
|
|
@@ -29619,7 +29867,7 @@ function hostResourceInput(host) {
|
|
|
29619
29867
|
return [
|
|
29620
29868
|
`# ${host} Resource Input`,
|
|
29621
29869
|
"",
|
|
29622
|
-
"This file is scaffolded input for host-specific
|
|
29870
|
+
"This file is scaffolded input for host-specific generated resources.",
|
|
29623
29871
|
"The source of truth is `.pipeline/pipeline.yaml` plus `.pipeline/profiles.yaml` and `.pipeline/runners.yaml`; generated host resources must preserve the profiles, prompts, rules, tools, filesystem policy, network policy, and output contracts declared there.",
|
|
29624
29872
|
""
|
|
29625
29873
|
].join(`
|
|
@@ -36912,8 +37160,6 @@ function createActorLaunchPlan(config2, input, actor, runnerId) {
|
|
|
36912
37160
|
function piArgv(prompt, config2, actor, worktreePath = process.cwd(), runner) {
|
|
36913
37161
|
return [
|
|
36914
37162
|
"--print",
|
|
36915
|
-
"--mode",
|
|
36916
|
-
"json",
|
|
36917
37163
|
...optionalModelArgs("pi", runner, actor),
|
|
36918
37164
|
...piToolArgs(actor?.tools ?? []),
|
|
36919
37165
|
...skillArgsFor("pi", config2, actor, worktreePath),
|
|
@@ -38642,7 +38888,10 @@ function createCliProgram() {
|
|
|
38642
38888
|
console.log(formatPipelineInitResult(result));
|
|
38643
38889
|
});
|
|
38644
38890
|
program2.command("install-commands").description("Install generated slash-command adapters into this repository").addOption(new Option("--host <host>", "host command set to install").choices(["all", "claude", "opencode", "codex", "kimi", "pi"]).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) => {
|
|
38645
|
-
const result = await installCommands(
|
|
38891
|
+
const result = await installCommands({
|
|
38892
|
+
...flags,
|
|
38893
|
+
cwd: process.env.PIPELINE_TARGET_PATH ?? process.cwd()
|
|
38894
|
+
});
|
|
38646
38895
|
console.log(formatInstallCommandsResult(result));
|
|
38647
38896
|
});
|
|
38648
38897
|
return program2;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export declare const COMMAND_HOSTS: readonly ["claude", "opencode", "codex", "kimi", "pi"];
|
|
2
2
|
export type CommandHost = (typeof COMMAND_HOSTS)[number];
|
|
3
3
|
export type CommandHostSelection = CommandHost | "all";
|
|
4
|
-
export type InstallAction = "conflict" | "create" | "unchanged" | "update";
|
|
4
|
+
export type InstallAction = "conflict" | "create" | "delete" | "unchanged" | "update";
|
|
5
5
|
export interface CommandInstallPlanItem {
|
|
6
6
|
action: InstallAction;
|
|
7
7
|
host: CommandHost;
|
package/dist/pipeline-runtime.js
CHANGED
|
@@ -28560,6 +28560,7 @@ var runnerSchema = exports_external.object({
|
|
|
28560
28560
|
args: exports_external.array(exports_external.string()).optional(),
|
|
28561
28561
|
capabilities: runnerCapabilitiesSchema,
|
|
28562
28562
|
command: exports_external.string().optional(),
|
|
28563
|
+
host_models: exports_external.record(exports_external.string(), exports_external.string().min(1)).optional(),
|
|
28563
28564
|
model: exports_external.string().optional(),
|
|
28564
28565
|
type: exports_external.enum(RUNNER_TYPES)
|
|
28565
28566
|
}).strict();
|
|
@@ -28625,6 +28626,7 @@ var retriesSchema = exports_external.object({
|
|
|
28625
28626
|
var profileSchema = exports_external.object({
|
|
28626
28627
|
description: exports_external.string().optional(),
|
|
28627
28628
|
filesystem: filesystemSchema.optional(),
|
|
28629
|
+
host_models: exports_external.record(exports_external.string(), exports_external.string().min(1)).optional(),
|
|
28628
28630
|
instructions: instructionsSchema,
|
|
28629
28631
|
mcp_servers: exports_external.array(exports_external.string()).optional(),
|
|
28630
28632
|
model: exports_external.string().optional(),
|
|
@@ -29627,8 +29629,6 @@ function createActorLaunchPlan(config2, input, actor, runnerId) {
|
|
|
29627
29629
|
function piArgv(prompt, config2, actor, worktreePath = process.cwd(), runner) {
|
|
29628
29630
|
return [
|
|
29629
29631
|
"--print",
|
|
29630
|
-
"--mode",
|
|
29631
|
-
"json",
|
|
29632
29632
|
...optionalModelArgs("pi", runner, actor),
|
|
29633
29633
|
...piToolArgs(actor?.tools ?? []),
|
|
29634
29634
|
...skillArgsFor("pi", config2, actor, worktreePath),
|
package/dist/runner.js
CHANGED
|
@@ -7381,8 +7381,6 @@ async function execaHarnessPi(prompt, contextFile, worktreePath) {
|
|
|
7381
7381
|
${prompt}` : prompt;
|
|
7382
7382
|
const argv = [
|
|
7383
7383
|
"--print",
|
|
7384
|
-
"--mode",
|
|
7385
|
-
"json",
|
|
7386
7384
|
...optionalModelArgs("pi"),
|
|
7387
7385
|
"--no-session",
|
|
7388
7386
|
effectivePrompt
|
|
@@ -7477,8 +7475,6 @@ function createActorLaunchPlan(config, input, actor, runnerId) {
|
|
|
7477
7475
|
function piArgv(prompt, config, actor, worktreePath = process.cwd(), runner) {
|
|
7478
7476
|
return [
|
|
7479
7477
|
"--print",
|
|
7480
|
-
"--mode",
|
|
7481
|
-
"json",
|
|
7482
7478
|
...optionalModelArgs("pi", runner, actor),
|
|
7483
7479
|
...piToolArgs(actor?.tools ?? []),
|
|
7484
7480
|
...skillArgsFor("pi", config, actor, worktreePath),
|
|
@@ -189,14 +189,14 @@ timeouts, output limits, sanitized env, and explicit trust flags.
|
|
|
189
189
|
|
|
190
190
|
## Host Support Matrix
|
|
191
191
|
|
|
192
|
-
| Runner
|
|
193
|
-
|
|
|
194
|
-
| Claude
|
|
195
|
-
| Codex
|
|
196
|
-
| OpenCode | yes
|
|
197
|
-
| Kimi
|
|
198
|
-
| Pi
|
|
199
|
-
| command
|
|
192
|
+
| Runner | Native subagents | Rules | Skills | MCP | Outputs | Generated resources |
|
|
193
|
+
| -------- | ---------------------- | ----- | --------------------------------------------- | --- | ------------------------- | -------------------------------- |
|
|
194
|
+
| Claude | yes | yes | included in generated profile text | yes | text, JSON, schema | command plus `.claude/agents` |
|
|
195
|
+
| Codex | yes | yes | yes | yes | text, JSON, JSONL, schema | skill plus `.codex/agents` |
|
|
196
|
+
| OpenCode | yes | yes | included in generated profile text | yes | text, JSON, JSONL, schema | command plus `.opencode/agents` |
|
|
197
|
+
| Kimi | yes | yes | surfaced through project skills when declared | no | text, JSON | skill plus `.kimi/agents/*.yaml` |
|
|
198
|
+
| Pi | yes, with pi-subagents | yes | included in generated prompt text | no | text, JSON | prompt plus no-op extension shim |
|
|
199
|
+
| command | no | no | no | no | declared by runner | subprocess argv |
|
|
200
200
|
|
|
201
201
|
The runtime prefers native subagents when the runner advertises
|
|
202
202
|
`native_subagents: true` and the configured permissions, runner, output, and
|
|
@@ -204,6 +204,15 @@ resource grants can be represented safely. Otherwise it uses a subprocess for
|
|
|
204
204
|
the agent node. In both cases each agent node records a separate invocation
|
|
205
205
|
boundary; multi-agent workflows are never collapsed into one prompt.
|
|
206
206
|
|
|
207
|
+
Generated host resources follow a native-first, runner-correct rule. Same-host
|
|
208
|
+
agent nodes use exact native subagents. Cross-runner nodes use host-native
|
|
209
|
+
execution only when the host can explicitly run the requested model; OpenCode
|
|
210
|
+
does this through per-agent `model:` values resolved from profile or runner
|
|
211
|
+
`model`, with optional `host_models.opencode` overrides when model ids differ.
|
|
212
|
+
If native execution cannot represent the requested runner/model, generated
|
|
213
|
+
instructions dispatch to that runner's CLI instead of doing instruction-only
|
|
214
|
+
translation.
|
|
215
|
+
|
|
207
216
|
## Troubleshooting
|
|
208
217
|
|
|
209
218
|
- Missing config: run `pipe init`; `pipe run` requires
|
|
@@ -212,8 +221,9 @@ boundary; multi-agent workflows are never collapsed into one prompt.
|
|
|
212
221
|
- Capability error: reduce the profile grants or choose a runner whose declared
|
|
213
222
|
capabilities include the requested tools, filesystem, network, output, rules,
|
|
214
223
|
skills, or MCP access.
|
|
215
|
-
- Pi native execution
|
|
216
|
-
|
|
224
|
+
- Pi native execution unavailable: install and enable `pi-subagents` if you want
|
|
225
|
+
Pi-native chains. Otherwise `/pipe` uses the CLI dispatch instructions in the
|
|
226
|
+
generated Pi prompt.
|
|
217
227
|
- Gate failure: inspect `pipe run` output for node, gate, reason, and evidence.
|
|
218
228
|
Dependent nodes are not executed after a required gate fails.
|
|
219
229
|
- Schema failure: ensure the agent emits valid JSON and that `schema_path`
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
# Host Resource Adapter Contract
|
|
2
2
|
|
|
3
|
-
Generated host resources are
|
|
4
|
-
`.pipeline/profiles.yaml`, and `.pipeline/pipeline.yaml`. They do not
|
|
5
|
-
|
|
3
|
+
Generated host resources are derived from `.pipeline/runners.yaml`,
|
|
4
|
+
`.pipeline/profiles.yaml`, and `.pipeline/pipeline.yaml`. They do not maintain
|
|
5
|
+
independent profile definitions or silently translate one runner into another
|
|
6
|
+
host's default agent.
|
|
6
7
|
|
|
7
8
|
Install or check generated resources with:
|
|
8
9
|
|
|
@@ -13,23 +14,30 @@ pipe install-commands --host all --check
|
|
|
13
14
|
|
|
14
15
|
## Host Mappings
|
|
15
16
|
|
|
16
|
-
| Host
|
|
17
|
-
|
|
|
18
|
-
| Claude Code | `.claude/commands/pipe.md`, `.claude/agents/*.md`
|
|
19
|
-
| Codex
|
|
20
|
-
| OpenCode
|
|
21
|
-
| Kimi
|
|
22
|
-
| Pi
|
|
17
|
+
| Host | Generated resources | Invocation | Mechanical path |
|
|
18
|
+
| ----------- | ------------------------------------------------------ | -------------------- | ------------------------------------------------------------------------------------------------------ |
|
|
19
|
+
| Claude Code | `.claude/commands/pipe.md`, `.claude/agents/*.md` | `/pipe <task>` | Project command delegates to configured Claude agents. |
|
|
20
|
+
| Codex | `.agents/skills/pipe/SKILL.md`, `.codex/agents/*.toml` | `$pipe <task>` | Skill instructs Codex to use generated Codex agents for Codex runner nodes. |
|
|
21
|
+
| OpenCode | `.opencode/commands/pipe.md`, `.opencode/agents/*.md` | `/pipe <task>` | Project command runs a primary orchestrator and native subagents when the requested model is resolved. |
|
|
22
|
+
| Kimi | `.kimi/skills/pipe/SKILL.md`, `.kimi/agents/*.yaml` | `/skill:pipe <task>` | Kimi discovers project skills as `/skill:<name>` commands; Kimi agents are generated as YAML specs. |
|
|
23
|
+
| Pi | `.pi/prompts/pipe.md`, `.pi/extensions/pipe.ts` | `/pipe <task>` | Pi discovers project prompt templates as slash commands; the generated extension is a no-op shim. |
|
|
23
24
|
|
|
24
25
|
## Projection Rules
|
|
25
26
|
|
|
26
27
|
- Profile names, descriptions, instructions, tools, rules, skills, MCP servers,
|
|
27
28
|
filesystem mode, network mode, and output contracts are read from YAML.
|
|
29
|
+
- Exact native dispatch is used when a node runner matches the host.
|
|
30
|
+
- OpenCode can run mixed native subagents when the node runner has a resolved
|
|
31
|
+
model from `profile.host_models.opencode`, `runner.host_models.opencode`,
|
|
32
|
+
`profile.model`, or `runner.model`.
|
|
33
|
+
- Cross-runner nodes that cannot be represented natively are dispatched through
|
|
34
|
+
that runner's CLI. Instruction-only translation is not runner-correct and is
|
|
35
|
+
not used as an implicit fallback.
|
|
28
36
|
- Host-specific formats can omit unsupported capabilities, but they must not
|
|
29
37
|
grant broader access than requested.
|
|
30
38
|
- Regeneration is idempotent for generated files. Manual edits are protected
|
|
31
39
|
unless `--force` is supplied.
|
|
32
40
|
|
|
33
|
-
The CLI runtime and host
|
|
41
|
+
The CLI runtime and generated host resources share the same workflow plan. Multi-agent
|
|
34
42
|
workflows require separate agent boundaries; host resources must not collapse
|
|
35
43
|
the workflow into a single prompt.
|