refactor(coding-agent): moved swarm extension into workspace package

This commit is contained in:
can1357
2026-02-12 09:08:37 +01:00
parent f26091f288
commit b987f0ed75
13 changed files with 31 additions and 27 deletions
@@ -9,7 +9,7 @@ Use it for anything: research pipelines, code generation, data processing, conte
## Setup
```bash
cd packages/coding-agent/extensions/swarm
cd packages/omp-extension-swarm
bun install
```
@@ -19,10 +19,10 @@ bun install
```bash
# Foreground — runs until complete, no timeout:
bun packages/coding-agent/extensions/swarm/run-pipeline.ts path/to/swarm.yaml
bun packages/omp-extension-swarm/run-pipeline.ts path/to/swarm.yaml
# Background — survives terminal close:
nohup bun packages/coding-agent/extensions/swarm/run-pipeline.ts path/to/swarm.yaml \
nohup bun packages/omp-extension-swarm/run-pipeline.ts path/to/swarm.yaml \
> pipeline.log 2>&1 & disown
```
@@ -34,7 +34,7 @@ Register the extension in your config (`~/.omp/config.json` or `.omp/config.json
```json
{
"extensions": ["packages/coding-agent/extensions/swarm"]
"extensions": ["packages/omp-extension-swarm"]
}
```
@@ -8,26 +8,25 @@
* Usage: Add this extension's directory to your extensions config,
* then use /swarm in any oh-my-pi session.
*/
import * as path from "node:path";
import * as fs from "node:fs/promises";
import * as path from "node:path";
import type { ExtensionAPI, ExtensionCommandContext } from "@oh-my-pi/pi-coding-agent";
import { parseSwarmYaml, validateSwarmDefinition, type SwarmDefinition } from "./swarm/schema";
import { buildDependencyGraph, detectCycles, buildExecutionWaves } from "./swarm/dag";
import { StateTracker } from "./swarm/state";
import { buildDependencyGraph, buildExecutionWaves, detectCycles } from "./swarm/dag";
import { PipelineController } from "./swarm/pipeline";
import { renderSwarmProgress } from "./swarm/render";
import { parseSwarmYaml, type SwarmDefinition, validateSwarmDefinition } from "./swarm/schema";
import { StateTracker } from "./swarm/state";
export default function swarmExtension(pi: ExtensionAPI): void {
pi.setLabel("Swarm Orchestrator");
pi.registerCommand("swarm", {
description: "Run a multi-agent swarm pipeline from YAML",
getArgumentCompletions: (prefix) => {
getArgumentCompletions: prefix => {
const subcommands = ["run", "status", "help"];
if (!prefix) return subcommands.map((s) => ({ label: s, value: s }));
return subcommands
.filter((s) => s.startsWith(prefix))
.map((s) => ({ label: s, value: s }));
if (!prefix) return subcommands.map(s => ({ label: s, value: s }));
return subcommands.filter(s => s.startsWith(prefix)).map(s => ({ label: s, value: s }));
},
handler: async (args: string, ctx: ExtensionCommandContext) => {
const parts = args.trim().split(/\s+/);
@@ -47,7 +46,6 @@ export default function swarmExtension(pi: ExtensionAPI): void {
await handleStatus(parts[1], ctx);
return;
}
case "help":
default:
ctx.ui.notify(
[
@@ -93,7 +91,7 @@ async function handleRun(yamlPath: string, ctx: ExtensionCommandContext, pi: Ext
// 3. Validate
const validationErrors = validateSwarmDefinition(def);
if (validationErrors.length > 0) {
ctx.ui.notify(`Validation errors:\n${validationErrors.map((e) => ` - ${e}`).join("\n")}`, "error");
ctx.ui.notify(`Validation errors:\n${validationErrors.map(e => ` - ${e}`).join("\n")}`, "error");
return;
}
@@ -249,9 +247,7 @@ function buildSummaryMessage(
lines.push("");
for (const [name, agent] of Object.entries(stateTracker.state.agents)) {
const duration =
agent.startedAt && agent.completedAt
? formatDuration(agent.completedAt - agent.startedAt)
: "n/a";
agent.startedAt && agent.completedAt ? formatDuration(agent.completedAt - agent.startedAt) : "n/a";
lines.push(`- **${name}**: ${agent.status} (${duration})${agent.error ? ` — ${agent.error}` : ""}`);
}
@@ -3,16 +3,17 @@
*
* Usage: bun run-pipeline.ts <path-to-yaml>
*/
import * as path from "node:path";
import * as fs from "node:fs/promises";
import { parseSwarmYaml, validateSwarmDefinition } from "./swarm/schema";
import { buildDependencyGraph, detectCycles, buildExecutionWaves } from "./swarm/dag";
import { StateTracker } from "./swarm/state";
import { PipelineController } from "./swarm/pipeline";
import { renderSwarmProgress } from "./swarm/render";
import * as path from "node:path";
import { discoverAuthStorage } from "@oh-my-pi/pi-coding-agent";
import { ModelRegistry } from "@oh-my-pi/pi-coding-agent/config/model-registry";
import { Settings } from "@oh-my-pi/pi-coding-agent/config/settings";
import { buildDependencyGraph, buildExecutionWaves, detectCycles } from "./swarm/dag";
import { PipelineController } from "./swarm/pipeline";
import { renderSwarmProgress } from "./swarm/render";
import { parseSwarmYaml, validateSwarmDefinition } from "./swarm/schema";
import { StateTracker } from "./swarm/state";
const yamlPath = process.argv[2];
if (!yamlPath) {
@@ -55,6 +55,7 @@ export interface SwarmDefinition {
// ============================================================================
const VALID_MODES = new Set<string>(["pipeline", "parallel", "sequential"]);
const VALID_SWARM_NAME = /^[a-zA-Z0-9._-]+$/;
export function parseSwarmYaml(content: string): SwarmDefinition {
const raw = YAML.parse(content) as { swarm?: RawSwarmConfig } | null;
@@ -66,6 +67,9 @@ export function parseSwarmYaml(content: string): SwarmDefinition {
if (!swarm.name || typeof swarm.name !== "string") {
throw new Error("swarm.name is required and must be a string");
}
if (!VALID_SWARM_NAME.test(swarm.name)) {
throw new Error("swarm.name may only contain letters, numbers, dot, underscore, and dash");
}
if (!swarm.workspace || typeof swarm.workspace !== "string") {
throw new Error("swarm.workspace is required and must be a string");
}
@@ -141,6 +145,9 @@ export function validateSwarmDefinition(def: SwarmDefinition): string[] {
if (def.targetCount < 1) {
errors.push("target_count must be at least 1");
}
if (def.mode !== "pipeline" && def.targetCount !== 1) {
errors.push("target_count is only supported in pipeline mode");
}
return errors;
}
@@ -8,9 +8,9 @@
"skipLibCheck": true,
"types": ["bun-types"],
"paths": {
"@oh-my-pi/pi-coding-agent": ["../../src/index.ts"],
"@oh-my-pi/pi-coding-agent/*": ["../../src/*.ts"]
"@oh-my-pi/pi-coding-agent": ["../coding-agent/src/index.ts"],
"@oh-my-pi/pi-coding-agent/*": ["../coding-agent/src/*.ts"]
}
},
"include": ["extension.ts", "swarm/**/*.ts"]
"include": ["extension.ts", "run-pipeline.ts", "swarm/**/*.ts"]
}