stageflow 0.3.0 → 0.9.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 +44 -14
- package/dist/agent/activity.d.ts +2 -0
- package/dist/agent/cursorProvider.d.ts +1 -10
- package/dist/agent/cursorProvider.js +18 -10
- package/dist/agent/fakeAgent.js +11 -7
- package/dist/agent/piAdapter.d.ts +2 -1
- package/dist/agent/piAdapter.js +28 -25
- package/dist/agent/port.d.ts +6 -1
- package/dist/agent/port.js +3 -0
- package/dist/cli/artifactCommand.d.ts +10 -0
- package/dist/cli/artifactCommand.js +120 -0
- package/dist/cli/envelopeCommand.d.ts +32 -0
- package/dist/cli/envelopeCommand.js +285 -0
- package/dist/cli/exportRunCommand.d.ts +10 -0
- package/dist/cli/exportRunCommand.js +150 -0
- package/dist/cli/handoffFormat.d.ts +22 -0
- package/dist/cli/handoffFormat.js +61 -0
- package/dist/cli/runCommand.d.ts +5 -1
- package/dist/cli/runCommand.js +36 -5
- package/dist/cli/runOutput.d.ts +4 -1
- package/dist/cli/runOutput.js +36 -8
- package/dist/cli/runsCommand.d.ts +22 -0
- package/dist/cli/runsCommand.js +591 -0
- package/dist/cli/skillsCommand.d.ts +16 -0
- package/dist/cli/skillsCommand.js +459 -0
- package/dist/cli/validateOutput.d.ts +2 -2
- package/dist/cli/validateOutput.js +2 -2
- package/dist/cli.js +110 -5
- package/dist/config/loadPipeline.js +5 -1
- package/dist/config/normalizePipelineStageEntry.d.ts +2 -0
- package/dist/config/normalizePipelineStageEntry.js +25 -1
- package/dist/config/pipelineStageKeys.js +1 -1
- package/dist/config/resolveForkEmitContext.d.ts +2 -1
- package/dist/config/resolveForkEmitContext.js +23 -1
- package/dist/config/resolvePipelineDag.js +36 -4
- package/dist/envelope/check.d.ts +2 -0
- package/dist/envelope/check.js +51 -0
- package/dist/envelope/cloneForks.d.ts +2 -0
- package/dist/envelope/cloneForks.js +62 -0
- package/dist/envelope/forkChoice.js +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/dist/mcp/catalogTools.d.ts +3 -0
- package/dist/mcp/catalogTools.js +187 -0
- package/dist/mcp/controlTools.d.ts +3 -0
- package/dist/mcp/controlTools.js +147 -0
- package/dist/mcp/deps.d.ts +11 -0
- package/dist/mcp/deps.js +1 -0
- package/dist/mcp/projectRun.d.ts +1 -40
- package/dist/mcp/projectRun.js +1 -64
- package/dist/mcp/resources.d.ts +5 -0
- package/dist/mcp/resources.js +45 -0
- package/dist/mcp/server.d.ts +16 -3
- package/dist/mcp/server.js +159 -3
- package/dist/mcp/toolResults.d.ts +7 -0
- package/dist/mcp/toolResults.js +6 -0
- package/dist/mcp/tools.d.ts +2 -7
- package/dist/mcp/tools.js +52 -103
- package/dist/mcp/waitRun.d.ts +46 -0
- package/dist/mcp/waitRun.js +146 -0
- package/dist/mcp/waitingGates.d.ts +12 -0
- package/dist/mcp/waitingGates.js +83 -0
- package/dist/package-meta.d.ts +1 -0
- package/dist/package-meta.js +1 -0
- package/dist/projection/projectRun.d.ts +44 -0
- package/dist/projection/projectRun.js +71 -0
- package/dist/prompt/priorEnvelope.d.ts +1 -1
- package/dist/prompt/priorEnvelope.js +4 -1
- package/dist/runstore/paths.js +2 -0
- package/dist/runstore/pipelineDagSnapshot.d.ts +9 -0
- package/dist/runstore/pipelineDagSnapshot.js +90 -1
- package/dist/runstore/port.d.ts +13 -2
- package/dist/runstore/port.js +4 -1
- package/dist/runstore/runProjection.d.ts +1 -1
- package/dist/runstore/runProjection.js +14 -3
- package/dist/runstore/sqlite/SqliteRunStore.d.ts +3 -2
- package/dist/runstore/sqlite/SqliteRunStore.js +35 -3
- package/dist/runstore/stageInstanceId.d.ts +4 -0
- package/dist/runstore/stageInstanceId.js +23 -0
- package/dist/runstore/syntheticStageSnapshot.d.ts +1 -1
- package/dist/runstore/syntheticStageSnapshot.js +2 -1
- package/dist/runstore/trackProjection.js +28 -12
- package/dist/runstore/workspaceLayout.d.ts +1 -0
- package/dist/runstore/workspaceLayout.js +1 -1
- package/dist/runtime/cloneSchedule.d.ts +18 -0
- package/dist/runtime/cloneSchedule.js +233 -0
- package/dist/runtime/envelopeRouting.d.ts +2 -1
- package/dist/runtime/envelopeRouting.js +80 -13
- package/dist/runtime/pipelineScheduler.d.ts +6 -2
- package/dist/runtime/pipelineScheduler.js +166 -30
- package/dist/runtime/resumeReconstruct.js +8 -3
- package/dist/runtime/runChangeBus.d.ts +18 -0
- package/dist/runtime/runChangeBus.js +83 -0
- package/dist/runtime/runManager.d.ts +7 -0
- package/dist/runtime/runManager.js +31 -2
- package/dist/runtime/runRetryCoordinator.d.ts +2 -0
- package/dist/runtime/runRetryCoordinator.js +4 -1
- package/dist/runtime/stageAttemptBootstrap.d.ts +1 -0
- package/dist/runtime/stageAttemptBootstrap.js +17 -9
- package/dist/runtime/stageRunner.d.ts +1 -0
- package/dist/runtime/stageRunner.js +17 -15
- package/dist/runtime/stageWorker.js +11 -4
- package/dist/server/bootstrap.d.ts +32 -0
- package/dist/server/bootstrap.js +56 -0
- package/dist/server/createHttpHost.d.ts +34 -0
- package/dist/server/createHttpHost.js +70 -0
- package/dist/server/http.d.ts +6 -13
- package/dist/server/http.js +295 -374
- package/dist/server/mcpHost.d.ts +22 -0
- package/dist/server/mcpHost.js +21 -0
- package/dist/server/operatorResults.d.ts +13 -0
- package/dist/server/operatorResults.js +22 -0
- package/dist/tools/emitStageEnvelope.d.ts +11 -2
- package/dist/tools/emitStageEnvelope.js +22 -6
- package/dist/types/envelope.d.ts +2 -0
- package/dist/types/forkChoice.d.ts +22 -0
- package/dist/types/pipeline.d.ts +8 -0
- package/dist/ui/assets/{index-DefBlEvN.css → index-C3N7MXAC.css} +1 -1
- package/dist/ui/assets/index-CSoGDA3A.js +118 -0
- package/dist/ui/index.html +2 -2
- package/package.json +2 -1
- package/skills/install-suite.sh +107 -0
- package/skills/stageflow/SKILL.md +26 -0
- package/skills/stageflow/references/control-surface.md +28 -0
- package/skills/stageflow/scripts/detect-host.mjs +54 -0
- package/skills/stageflow/scripts/detect-host.test.mjs +97 -0
- package/skills/stageflow-author/SKILL.md +58 -0
- package/skills/stageflow-author/assets/examples/branch-decision/hotfix.yaml +10 -0
- package/skills/stageflow-author/assets/examples/branch-decision/release-gate.pipeline.yaml +12 -0
- package/skills/stageflow-author/assets/examples/branch-decision/run-tests.yaml +13 -0
- package/skills/stageflow-author/assets/examples/branch-decision/ship.yaml +10 -0
- package/skills/stageflow-author/assets/examples/linear-review/draft.yaml +9 -0
- package/skills/stageflow-author/assets/examples/linear-review/publish.yaml +10 -0
- package/skills/stageflow-author/assets/examples/linear-review/review-loop.pipeline.yaml +10 -0
- package/skills/stageflow-author/assets/examples/linear-review/review.yaml +15 -0
- package/skills/stageflow-author/assets/examples/non-sdlc-digest/gather.yaml +9 -0
- package/skills/stageflow-author/assets/examples/non-sdlc-digest/research-digest.pipeline.yaml +10 -0
- package/skills/stageflow-author/assets/examples/non-sdlc-digest/send.yaml +10 -0
- package/skills/stageflow-author/assets/examples/non-sdlc-digest/summarize.yaml +9 -0
- package/skills/stageflow-author/references/catalog-mapping.md +131 -0
- package/skills/stageflow-author/references/catalog-write-conventions.md +29 -0
- package/skills/stageflow-author/references/stage-prompt-template.md +55 -0
- package/skills/stageflow-author/references/validate-and-report.md +38 -0
- package/skills/stageflow-delegate/SKILL.md +18 -0
- package/skills/stageflow-delegate/references/authoring-or-run.md +15 -0
- package/skills/stageflow-delegate/references/example-walkthrough.md +30 -0
- package/skills/stageflow-delegate/references/pattern-detection.md +44 -0
- package/skills/stageflow-run/SKILL.md +182 -0
- package/skills/stageflow-run/references/mcp-call.md +17 -0
- package/skills/stageflow-run/references/native-question-ui.md +78 -0
- package/skills/stageflow-run/references/task-and-pipeline-selection.md +52 -0
- package/skills/stageflow-run/scripts/mcp-call.mjs +299 -0
- package/skills/stageflow-run/scripts/mcp-call.test.mjs +271 -0
- package/skills/stageflow-session-capture/SKILL.md +94 -0
- package/skills/stageflow-session-capture/assets/example-pipeline/example.pipeline.yaml +7 -0
- package/skills/stageflow-session-capture/assets/example-pipeline/implement.yaml +8 -0
- package/skills/stageflow-session-capture/assets/example-pipeline/research.yaml +8 -0
- package/skills/stageflow-session-capture/references/catalog-authoring.md +40 -0
- package/skills/stageflow-session-capture/references/transcript-sources.md +25 -0
- package/skills/stageflow-session-capture/scripts/check-provider-gate.mjs +43 -0
- package/skills/stageflow-session-capture/scripts/locate-session-transcript.mjs +141 -0
- package/skills/stageflow-session-capture/scripts/resolve-catalog-id.mjs +119 -0
- package/skills/stageflow-setup/SKILL.md +110 -0
- package/dist/ui/assets/index-CFSzDZje.js +0 -118
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
import { access, cp, mkdir, mkdtemp, readFile, readdir, rm, stat, writeFile, } from "node:fs/promises";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { inflateRawSync } from "node:zlib";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import { findProjectRoot } from "../project/findProjectRoot.js";
|
|
8
|
+
export const SKILLS_USAGE = `Usage:
|
|
9
|
+
sf skills list
|
|
10
|
+
sf skills install --from-path <dir> [--skill-name <name>]
|
|
11
|
+
sf skills install --from-zip <url-or-path> [--skill-name <name>] [--checksum sha256:<hex>]`;
|
|
12
|
+
const defaultIo = {
|
|
13
|
+
log: (line) => console.log(line),
|
|
14
|
+
error: (line) => console.error(line),
|
|
15
|
+
fetch: globalThis.fetch.bind(globalThis),
|
|
16
|
+
runDoctor: (scriptPath, cwd) => {
|
|
17
|
+
const result = spawnSync(process.execPath, [scriptPath, "doctor"], {
|
|
18
|
+
cwd,
|
|
19
|
+
stdio: "pipe",
|
|
20
|
+
});
|
|
21
|
+
return result.status ?? 1;
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
function parseSkillsArgs(args) {
|
|
25
|
+
if (args.length === 0) {
|
|
26
|
+
return { help: false };
|
|
27
|
+
}
|
|
28
|
+
if (args[0] === "--help" || args[0] === "-h") {
|
|
29
|
+
return { help: true };
|
|
30
|
+
}
|
|
31
|
+
const subcommand = args[0];
|
|
32
|
+
if (subcommand !== "list" && subcommand !== "install") {
|
|
33
|
+
throw new Error(`Unknown skills subcommand: ${subcommand}`);
|
|
34
|
+
}
|
|
35
|
+
let fromPath;
|
|
36
|
+
let fromZip;
|
|
37
|
+
let skillName;
|
|
38
|
+
let checksum;
|
|
39
|
+
let help = false;
|
|
40
|
+
for (let i = 1; i < args.length; i++) {
|
|
41
|
+
const arg = args[i];
|
|
42
|
+
if (arg === "--help" || arg === "-h") {
|
|
43
|
+
help = true;
|
|
44
|
+
}
|
|
45
|
+
else if (arg === "--from-path") {
|
|
46
|
+
const value = args[++i];
|
|
47
|
+
if (value === undefined || value.length === 0) {
|
|
48
|
+
throw new Error("Missing value for --from-path");
|
|
49
|
+
}
|
|
50
|
+
fromPath = value;
|
|
51
|
+
}
|
|
52
|
+
else if (arg === "--from-zip") {
|
|
53
|
+
const value = args[++i];
|
|
54
|
+
if (value === undefined || value.length === 0) {
|
|
55
|
+
throw new Error("Missing value for --from-zip");
|
|
56
|
+
}
|
|
57
|
+
fromZip = value;
|
|
58
|
+
}
|
|
59
|
+
else if (arg === "--skill-name") {
|
|
60
|
+
const value = args[++i];
|
|
61
|
+
if (value === undefined || value.length === 0) {
|
|
62
|
+
throw new Error("Missing value for --skill-name");
|
|
63
|
+
}
|
|
64
|
+
skillName = value;
|
|
65
|
+
}
|
|
66
|
+
else if (arg === "--checksum") {
|
|
67
|
+
const value = args[++i];
|
|
68
|
+
if (value === undefined || value.length === 0) {
|
|
69
|
+
throw new Error("Missing value for --checksum");
|
|
70
|
+
}
|
|
71
|
+
checksum = value;
|
|
72
|
+
}
|
|
73
|
+
else if (arg.startsWith("-")) {
|
|
74
|
+
throw new Error(`Unknown flag: ${arg}`);
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
throw new Error(`Unexpected argument: ${arg}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
help,
|
|
82
|
+
subcommand,
|
|
83
|
+
fromPath,
|
|
84
|
+
fromZip,
|
|
85
|
+
skillName,
|
|
86
|
+
checksum,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
function skillsDirForRoot(projectRoot) {
|
|
90
|
+
return path.join(projectRoot, ".pi", "skills");
|
|
91
|
+
}
|
|
92
|
+
function shouldCopyRelative(rel) {
|
|
93
|
+
const normalized = rel.split(path.sep).join("/");
|
|
94
|
+
if (normalized === "" || normalized === ".") {
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
const segments = normalized.split("/");
|
|
98
|
+
if (segments.some((segment) => segment === "test" || segment === "node_modules")) {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
if (segments[0] === "scripts") {
|
|
102
|
+
const base = segments[segments.length - 1] ?? "";
|
|
103
|
+
if (base.startsWith("generate-")) {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
async function copySkillTree(sourceRoot, destRoot) {
|
|
110
|
+
await rm(destRoot, { recursive: true, force: true });
|
|
111
|
+
await mkdir(destRoot, { recursive: true });
|
|
112
|
+
await cp(sourceRoot, destRoot, {
|
|
113
|
+
recursive: true,
|
|
114
|
+
filter: (src) => {
|
|
115
|
+
const rel = path.relative(sourceRoot, src);
|
|
116
|
+
return shouldCopyRelative(rel);
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
function inferSkillNameFromPath(sourcePath) {
|
|
121
|
+
return path.basename(path.resolve(sourcePath));
|
|
122
|
+
}
|
|
123
|
+
function inferSkillNameFromZipRef(zipRef) {
|
|
124
|
+
const base = path.basename(zipRef.split("?")[0] ?? zipRef);
|
|
125
|
+
return base.replace(/\.zip$/i, "");
|
|
126
|
+
}
|
|
127
|
+
function parseChecksum(value) {
|
|
128
|
+
const match = /^sha256:([a-f0-9]{64})$/i.exec(value);
|
|
129
|
+
if (!match) {
|
|
130
|
+
throw new Error("--checksum must be sha256:<64 hex chars>");
|
|
131
|
+
}
|
|
132
|
+
return match[1].toLowerCase();
|
|
133
|
+
}
|
|
134
|
+
function sha256Hex(data) {
|
|
135
|
+
return createHash("sha256").update(data).digest("hex");
|
|
136
|
+
}
|
|
137
|
+
function isZipSlipEntry(name) {
|
|
138
|
+
const normalized = name.replace(/\\/g, "/");
|
|
139
|
+
if (path.posix.isAbsolute(normalized)) {
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
return normalized.split("/").some((segment) => segment === "..");
|
|
143
|
+
}
|
|
144
|
+
function findEndOfCentralDirectory(data) {
|
|
145
|
+
for (let i = data.length - 22; i >= 0; i--) {
|
|
146
|
+
if (data.readUInt32LE(i) === 0x06054b50) {
|
|
147
|
+
return i;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
throw new Error("invalid zip archive: end of central directory not found");
|
|
151
|
+
}
|
|
152
|
+
function readZipEntries(data) {
|
|
153
|
+
const eocdOffset = findEndOfCentralDirectory(data);
|
|
154
|
+
const centralDirOffset = data.readUInt32LE(eocdOffset + 16);
|
|
155
|
+
const totalEntries = data.readUInt16LE(eocdOffset + 10);
|
|
156
|
+
const entries = [];
|
|
157
|
+
let offset = centralDirOffset;
|
|
158
|
+
for (let i = 0; i < totalEntries; i++) {
|
|
159
|
+
if (data.readUInt32LE(offset) !== 0x02014b50) {
|
|
160
|
+
throw new Error("invalid zip archive: central directory entry missing");
|
|
161
|
+
}
|
|
162
|
+
const compressionMethod = data.readUInt16LE(offset + 10);
|
|
163
|
+
const compressedSize = data.readUInt32LE(offset + 20);
|
|
164
|
+
const uncompressedSize = data.readUInt32LE(offset + 24);
|
|
165
|
+
const nameLength = data.readUInt16LE(offset + 28);
|
|
166
|
+
const extraLength = data.readUInt16LE(offset + 30);
|
|
167
|
+
const commentLength = data.readUInt16LE(offset + 32);
|
|
168
|
+
const localHeaderOffset = data.readUInt32LE(offset + 42);
|
|
169
|
+
const name = data.toString("utf8", offset + 46, offset + 46 + nameLength);
|
|
170
|
+
entries.push({
|
|
171
|
+
name,
|
|
172
|
+
compressionMethod,
|
|
173
|
+
compressedSize,
|
|
174
|
+
uncompressedSize,
|
|
175
|
+
localHeaderOffset,
|
|
176
|
+
});
|
|
177
|
+
offset += 46 + nameLength + extraLength + commentLength;
|
|
178
|
+
}
|
|
179
|
+
return entries;
|
|
180
|
+
}
|
|
181
|
+
async function extractZipBuffer(data, destDir) {
|
|
182
|
+
await mkdir(destDir, { recursive: true });
|
|
183
|
+
const entries = readZipEntries(data);
|
|
184
|
+
for (const entry of entries) {
|
|
185
|
+
if (entry.name.endsWith("/")) {
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
if (isZipSlipEntry(entry.name)) {
|
|
189
|
+
throw new Error("zip entry path rejected (zip-slip)");
|
|
190
|
+
}
|
|
191
|
+
const localOffset = entry.localHeaderOffset;
|
|
192
|
+
if (data.readUInt32LE(localOffset) !== 0x04034b50) {
|
|
193
|
+
throw new Error("invalid zip archive: local file header missing");
|
|
194
|
+
}
|
|
195
|
+
const nameLength = data.readUInt16LE(localOffset + 26);
|
|
196
|
+
const extraLength = data.readUInt16LE(localOffset + 28);
|
|
197
|
+
const dataOffset = localOffset + 30 + nameLength + extraLength;
|
|
198
|
+
const compressed = data.subarray(dataOffset, dataOffset + entry.compressedSize);
|
|
199
|
+
let content;
|
|
200
|
+
if (entry.compressionMethod === 0) {
|
|
201
|
+
content = compressed;
|
|
202
|
+
}
|
|
203
|
+
else if (entry.compressionMethod === 8) {
|
|
204
|
+
content = inflateRawSync(compressed);
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
throw new Error(`unsupported zip compression method: ${entry.compressionMethod}`);
|
|
208
|
+
}
|
|
209
|
+
if (content.length !== entry.uncompressedSize) {
|
|
210
|
+
throw new Error("invalid zip archive: uncompressed size mismatch");
|
|
211
|
+
}
|
|
212
|
+
const outPath = path.join(destDir, entry.name);
|
|
213
|
+
await mkdir(path.dirname(outPath), { recursive: true });
|
|
214
|
+
await writeFile(outPath, content);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
async function resolveZipBytes(zipRef, io) {
|
|
218
|
+
if (zipRef.startsWith("https://")) {
|
|
219
|
+
const response = await io.fetch(zipRef);
|
|
220
|
+
if (!response.ok) {
|
|
221
|
+
throw new Error(`failed to fetch zip: HTTP ${response.status}`);
|
|
222
|
+
}
|
|
223
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
224
|
+
return Buffer.from(arrayBuffer);
|
|
225
|
+
}
|
|
226
|
+
if (/^https?:\/\//i.test(zipRef)) {
|
|
227
|
+
throw new Error("only HTTPS URLs are supported for --from-zip");
|
|
228
|
+
}
|
|
229
|
+
return readFile(path.resolve(zipRef));
|
|
230
|
+
}
|
|
231
|
+
async function pathExists(filePath) {
|
|
232
|
+
try {
|
|
233
|
+
await access(filePath);
|
|
234
|
+
return true;
|
|
235
|
+
}
|
|
236
|
+
catch {
|
|
237
|
+
return false;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
async function findSkillRoot(extractRoot, skillName) {
|
|
241
|
+
const directBin = path.join(extractRoot, "bin", `${skillName}.mjs`);
|
|
242
|
+
if (await pathExists(directBin)) {
|
|
243
|
+
return extractRoot;
|
|
244
|
+
}
|
|
245
|
+
const nestedRoot = path.join(extractRoot, skillName);
|
|
246
|
+
const nestedBin = path.join(nestedRoot, "bin", `${skillName}.mjs`);
|
|
247
|
+
if (await pathExists(nestedBin)) {
|
|
248
|
+
return nestedRoot;
|
|
249
|
+
}
|
|
250
|
+
const found = await findSkillRootByBin(extractRoot, skillName);
|
|
251
|
+
if (found !== undefined) {
|
|
252
|
+
return found;
|
|
253
|
+
}
|
|
254
|
+
const bySkillMd = await findSkillRootBySkillMd(extractRoot);
|
|
255
|
+
if (bySkillMd !== undefined) {
|
|
256
|
+
return bySkillMd;
|
|
257
|
+
}
|
|
258
|
+
throw new Error(`could not locate skill root in extracted archive`);
|
|
259
|
+
}
|
|
260
|
+
async function findSkillRootByBin(dir, skillName) {
|
|
261
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
262
|
+
for (const entry of entries) {
|
|
263
|
+
if (!entry.isDirectory()) {
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
const fullPath = path.join(dir, entry.name);
|
|
267
|
+
const binScript = path.join(fullPath, "bin", `${skillName}.mjs`);
|
|
268
|
+
if (await pathExists(binScript)) {
|
|
269
|
+
return fullPath;
|
|
270
|
+
}
|
|
271
|
+
const nested = await findSkillRootByBin(fullPath, skillName);
|
|
272
|
+
if (nested !== undefined) {
|
|
273
|
+
return nested;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
return undefined;
|
|
277
|
+
}
|
|
278
|
+
async function findSkillRootBySkillMd(dir) {
|
|
279
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
280
|
+
for (const entry of entries) {
|
|
281
|
+
const fullPath = path.join(dir, entry.name);
|
|
282
|
+
if (entry.isFile() && entry.name === "SKILL.md") {
|
|
283
|
+
return dir;
|
|
284
|
+
}
|
|
285
|
+
if (entry.isDirectory()) {
|
|
286
|
+
const nested = await findSkillRootBySkillMd(fullPath);
|
|
287
|
+
if (nested !== undefined) {
|
|
288
|
+
return nested;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
return undefined;
|
|
293
|
+
}
|
|
294
|
+
async function resolveInstallSource(parsed, io) {
|
|
295
|
+
const hasPath = parsed.fromPath !== undefined;
|
|
296
|
+
const hasZip = parsed.fromZip !== undefined;
|
|
297
|
+
if (hasPath === hasZip) {
|
|
298
|
+
throw new Error("specify exactly one of --from-path or --from-zip");
|
|
299
|
+
}
|
|
300
|
+
if (parsed.fromPath !== undefined) {
|
|
301
|
+
const sourceRoot = path.resolve(parsed.fromPath);
|
|
302
|
+
try {
|
|
303
|
+
const sourceStat = await stat(sourceRoot);
|
|
304
|
+
if (!sourceStat.isDirectory()) {
|
|
305
|
+
throw new Error("source path not found");
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
catch {
|
|
309
|
+
throw new Error("source path not found");
|
|
310
|
+
}
|
|
311
|
+
const skillName = parsed.skillName ?? inferSkillNameFromPath(parsed.fromPath);
|
|
312
|
+
return { sourceRoot, skillName };
|
|
313
|
+
}
|
|
314
|
+
const zipRef = parsed.fromZip;
|
|
315
|
+
const zipBytes = await resolveZipBytes(zipRef, io);
|
|
316
|
+
if (parsed.checksum !== undefined) {
|
|
317
|
+
const expected = parseChecksum(parsed.checksum);
|
|
318
|
+
const actual = sha256Hex(zipBytes);
|
|
319
|
+
if (actual !== expected) {
|
|
320
|
+
throw new Error("checksum mismatch");
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
const extractDir = await mkdtemp(path.join(tmpdir(), "sf-skill-zip-"));
|
|
324
|
+
await extractZipBuffer(zipBytes, extractDir);
|
|
325
|
+
const skillName = parsed.skillName ?? inferSkillNameFromZipRef(zipRef);
|
|
326
|
+
const sourceRoot = await findSkillRoot(extractDir, skillName);
|
|
327
|
+
return {
|
|
328
|
+
sourceRoot,
|
|
329
|
+
skillName,
|
|
330
|
+
cleanup: async () => {
|
|
331
|
+
await rm(extractDir, { recursive: true, force: true });
|
|
332
|
+
},
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
async function listSkills(projectRoot, io) {
|
|
336
|
+
const skillsDir = skillsDirForRoot(projectRoot);
|
|
337
|
+
let names;
|
|
338
|
+
try {
|
|
339
|
+
names = await readdir(skillsDir);
|
|
340
|
+
}
|
|
341
|
+
catch (err) {
|
|
342
|
+
if (err.code === "ENOENT") {
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
throw err;
|
|
346
|
+
}
|
|
347
|
+
for (const name of names.sort()) {
|
|
348
|
+
const skillDir = path.join(skillsDir, name);
|
|
349
|
+
const skillStat = await stat(skillDir);
|
|
350
|
+
if (!skillStat.isDirectory()) {
|
|
351
|
+
continue;
|
|
352
|
+
}
|
|
353
|
+
let version = "—";
|
|
354
|
+
try {
|
|
355
|
+
const pkgRaw = await readFile(path.join(skillDir, "package.json"), "utf8");
|
|
356
|
+
const pkg = JSON.parse(pkgRaw);
|
|
357
|
+
if (typeof pkg.version === "string" && pkg.version.length > 0) {
|
|
358
|
+
version = pkg.version;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
catch {
|
|
362
|
+
// no package.json
|
|
363
|
+
}
|
|
364
|
+
io.log(`${name}\t${version}\tbin/${name}.mjs`);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
async function installSkill(projectRoot, parsed, io) {
|
|
368
|
+
const resolved = await resolveInstallSource(parsed, io);
|
|
369
|
+
try {
|
|
370
|
+
const destRoot = path.join(skillsDirForRoot(projectRoot), resolved.skillName);
|
|
371
|
+
await copySkillTree(resolved.sourceRoot, destRoot);
|
|
372
|
+
const doctorScript = path.join(destRoot, "bin", `${resolved.skillName}.mjs`);
|
|
373
|
+
const doctorCode = io.runDoctor(doctorScript, projectRoot);
|
|
374
|
+
if (doctorCode !== 0) {
|
|
375
|
+
io.error("skill doctor failed");
|
|
376
|
+
return 1;
|
|
377
|
+
}
|
|
378
|
+
io.log(`Installed skill ${resolved.skillName} to ${destRoot}`);
|
|
379
|
+
return 0;
|
|
380
|
+
}
|
|
381
|
+
finally {
|
|
382
|
+
if (resolved.cleanup) {
|
|
383
|
+
await resolved.cleanup();
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
export async function runSkillsCommand(args, options = {}) {
|
|
388
|
+
const cwd = options.cwd ?? process.cwd();
|
|
389
|
+
const projectRoot = options.projectRoot ?? findProjectRoot(cwd) ?? path.resolve(cwd);
|
|
390
|
+
const io = { ...defaultIo, ...options.io };
|
|
391
|
+
try {
|
|
392
|
+
const parsed = parseSkillsArgs(args);
|
|
393
|
+
if (parsed.help) {
|
|
394
|
+
io.error(SKILLS_USAGE);
|
|
395
|
+
return 0;
|
|
396
|
+
}
|
|
397
|
+
if (parsed.subcommand === undefined) {
|
|
398
|
+
io.error(SKILLS_USAGE);
|
|
399
|
+
return 1;
|
|
400
|
+
}
|
|
401
|
+
if (parsed.subcommand === "list") {
|
|
402
|
+
await listSkills(projectRoot, io);
|
|
403
|
+
return 0;
|
|
404
|
+
}
|
|
405
|
+
return await installSkill(projectRoot, parsed, io);
|
|
406
|
+
}
|
|
407
|
+
catch (err) {
|
|
408
|
+
io.error(err instanceof Error ? err.message : String(err));
|
|
409
|
+
return 1;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
export function createStoredZip(files) {
|
|
413
|
+
const localParts = [];
|
|
414
|
+
const centralParts = [];
|
|
415
|
+
let offset = 0;
|
|
416
|
+
for (const file of files) {
|
|
417
|
+
const nameBuf = Buffer.from(file.name, "utf8");
|
|
418
|
+
const content = Buffer.isBuffer(file.content)
|
|
419
|
+
? file.content
|
|
420
|
+
: Buffer.from(file.content, "utf8");
|
|
421
|
+
const localHeader = Buffer.alloc(30);
|
|
422
|
+
localHeader.writeUInt32LE(0x04034b50, 0);
|
|
423
|
+
localHeader.writeUInt16LE(20, 4);
|
|
424
|
+
localHeader.writeUInt16LE(0, 6);
|
|
425
|
+
localHeader.writeUInt16LE(0, 8);
|
|
426
|
+
localHeader.writeUInt32LE(0, 14);
|
|
427
|
+
localHeader.writeUInt32LE(content.length, 18);
|
|
428
|
+
localHeader.writeUInt32LE(content.length, 22);
|
|
429
|
+
localHeader.writeUInt16LE(nameBuf.length, 26);
|
|
430
|
+
localHeader.writeUInt16LE(0, 28);
|
|
431
|
+
localParts.push(localHeader, nameBuf, content);
|
|
432
|
+
const central = Buffer.alloc(46);
|
|
433
|
+
central.writeUInt32LE(0x02014b50, 0);
|
|
434
|
+
central.writeUInt16LE(20, 4);
|
|
435
|
+
central.writeUInt16LE(20, 6);
|
|
436
|
+
central.writeUInt16LE(0, 8);
|
|
437
|
+
central.writeUInt16LE(0, 10);
|
|
438
|
+
central.writeUInt32LE(0, 16);
|
|
439
|
+
central.writeUInt32LE(content.length, 20);
|
|
440
|
+
central.writeUInt32LE(content.length, 24);
|
|
441
|
+
central.writeUInt16LE(nameBuf.length, 28);
|
|
442
|
+
central.writeUInt16LE(0, 30);
|
|
443
|
+
central.writeUInt16LE(0, 32);
|
|
444
|
+
central.writeUInt32LE(offset, 42);
|
|
445
|
+
centralParts.push(central, nameBuf);
|
|
446
|
+
offset += 30 + nameBuf.length + content.length;
|
|
447
|
+
}
|
|
448
|
+
const centralDir = Buffer.concat(centralParts);
|
|
449
|
+
const eocd = Buffer.alloc(22);
|
|
450
|
+
eocd.writeUInt32LE(0x06054b50, 0);
|
|
451
|
+
eocd.writeUInt16LE(0, 4);
|
|
452
|
+
eocd.writeUInt16LE(0, 6);
|
|
453
|
+
eocd.writeUInt16LE(files.length, 8);
|
|
454
|
+
eocd.writeUInt16LE(files.length, 10);
|
|
455
|
+
eocd.writeUInt32LE(centralDir.length, 12);
|
|
456
|
+
eocd.writeUInt32LE(offset, 16);
|
|
457
|
+
eocd.writeUInt16LE(0, 20);
|
|
458
|
+
return Buffer.concat([...localParts, centralDir, eocd]);
|
|
459
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ValidationResult } from "../config/validateCatalog.js";
|
|
2
|
-
export declare const VALIDATION_SCOPE_LINE = "Scope:
|
|
3
|
-
export declare const VALIDATION_CHECKS = "
|
|
2
|
+
export declare const VALIDATION_SCOPE_LINE = "Scope: catalog YAML (pipelines, stages, and tasks as selected by flags). Provider auth and checkout paths are not checked.";
|
|
3
|
+
export declare const VALIDATION_CHECKS = "catalog YAML (pipelines, stages, tasks as selected by flags)";
|
|
4
4
|
export declare function formatValidationHuman(result: ValidationResult, options?: {
|
|
5
5
|
strict?: boolean;
|
|
6
6
|
}): string;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { effectiveSeverity } from "../config/validateCatalog.js";
|
|
2
|
-
export const VALIDATION_SCOPE_LINE = "Scope:
|
|
3
|
-
export const VALIDATION_CHECKS = "
|
|
2
|
+
export const VALIDATION_SCOPE_LINE = "Scope: catalog YAML (pipelines, stages, and tasks as selected by flags). Provider auth and checkout paths are not checked.";
|
|
3
|
+
export const VALIDATION_CHECKS = "catalog YAML (pipelines, stages, tasks as selected by flags)";
|
|
4
4
|
function effectiveSeverityRank(finding, strict) {
|
|
5
5
|
return effectiveSeverity(finding, strict) === "error" ? 0 : 1;
|
|
6
6
|
}
|
package/dist/cli.js
CHANGED
|
@@ -4,27 +4,52 @@ import { realpathSync } from "node:fs";
|
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { PiAgentAdapter } from "./agent/piAdapter.js";
|
|
7
|
+
import { ARTIFACT_USAGE, runArtifactCommand } from "./cli/artifactCommand.js";
|
|
8
|
+
import { ENVELOPE_USAGE, runEnvelopeCommand, } from "./cli/envelopeCommand.js";
|
|
9
|
+
import { EXPORT_RUN_USAGE, runExportRunCommand, } from "./cli/exportRunCommand.js";
|
|
7
10
|
import { INIT_USAGE, runInitCommand } from "./cli/initCommand.js";
|
|
8
11
|
import { PROVIDERS_USAGE, runProvidersCommand } from "./cli/providersCommand.js";
|
|
9
12
|
import { RUN_USAGE, runRunCommand } from "./cli/runCommand.js";
|
|
13
|
+
import { RUNS_USAGE, runRunsCommand } from "./cli/runsCommand.js";
|
|
10
14
|
import { resolveOperatorCatalog } from "./cli/operatorCatalog.js";
|
|
15
|
+
import { SKILLS_USAGE, runSkillsCommand } from "./cli/skillsCommand.js";
|
|
11
16
|
import { VALIDATE_USAGE, runValidateCommand } from "./cli/validateCommand.js";
|
|
12
17
|
import { createRunStore } from "./runstore/createStore.js";
|
|
13
18
|
import { resolveStageflowContext } from "./project/resolveStageflowContext.js";
|
|
14
19
|
import { exitForOutcome, runStageWorker } from "./runtime/stageWorker.js";
|
|
15
20
|
import { SF_STAGE_WORKER } from "./runtime/stageWorkerProtocol.js";
|
|
16
21
|
import { DEFAULT_PORT, startUiServer } from "./server/http.js";
|
|
22
|
+
import { startMcpServer } from "./server/mcpHost.js";
|
|
23
|
+
import { resolveMcpStateless } from "./mcp/server.js";
|
|
24
|
+
import { PACKAGE_VERSION } from "./package-meta.js";
|
|
17
25
|
const USAGE = `Usage:
|
|
18
26
|
sf init
|
|
19
|
-
sf run --task <path> --pipeline <path> [--checkout <path>] [--json] [--skip-gates] [--git-sha <sha>] [--ci-pr-url <url>] [--ci-job-url <url>] [--operator-cwd <path>] [--operator-agent-dir <path>]
|
|
27
|
+
sf run --task <path> --pipeline <path> [--checkout <path>] [--json] [--include stages] [--skip-gates] [--git-sha <sha>] [--ci-pr-url <url>] [--ci-job-url <url>] [--operator-cwd <path>] [--operator-agent-dir <path>]
|
|
20
28
|
sf validate [--pipeline <path>] [--task <path>] [--strict] [--json]
|
|
21
|
-
sf
|
|
29
|
+
sf artifact read --run <runId> --path <relPath> [--out <file>]
|
|
30
|
+
sf envelope get --run <runId> --stage <stageId> [--json] [--from <sf-run.json>] [--detect-stage <id>] [--format envelope|handoff]
|
|
31
|
+
sf export-run --run <runId> [--from <sf-run.json>] [--out <file>]
|
|
32
|
+
sf runs list [--status created|running|succeeded|failed] [--since <iso>] [--pipeline <id-or-path>] [--json]
|
|
33
|
+
sf runs show --run <runId> [--from <sf-run.json>] [--json]
|
|
34
|
+
sf runs waiting [--run <runId>] [--json]
|
|
35
|
+
sf runs wait --run <runId> [--from <sf-run.json>] [--until any|waiting|terminal] [--timeout-ms <n>] [--json]
|
|
36
|
+
sf runs answer --run <runId> --stage <stageId> [--answer '<json>'] [--json]
|
|
37
|
+
sf runs retry --run <runId> --stage <stageId> [--json]
|
|
38
|
+
sf runs abandon --run <runId> --stage <stageId> [--json]
|
|
39
|
+
sf runs rerun --run <runId> [--json]
|
|
40
|
+
sf ui [--port ${DEFAULT_PORT}] [--mcp-stateless]
|
|
41
|
+
sf mcp [--port ${DEFAULT_PORT}] [--mcp-stateless]
|
|
22
42
|
sf providers list
|
|
23
43
|
sf providers status [--provider <id>]
|
|
24
44
|
sf providers detect
|
|
25
45
|
sf providers source [get | set <pi_home|sf_owned>]
|
|
26
46
|
sf providers login <providerId> [--type api_key|oauth] [--api-key-env <VAR>]
|
|
27
47
|
sf providers logout <providerId>
|
|
48
|
+
sf skills list
|
|
49
|
+
sf skills install --from-path <dir> [--skill-name <name>]
|
|
50
|
+
sf skills install --from-zip <url-or-path> [--skill-name <name>] [--checksum sha256:<hex>]
|
|
51
|
+
sf --version
|
|
52
|
+
sf -V
|
|
28
53
|
sf --help
|
|
29
54
|
|
|
30
55
|
Stageflow (sf) runs YAML-defined automatic stage pipelines.
|
|
@@ -37,7 +62,17 @@ ${RUN_USAGE}
|
|
|
37
62
|
|
|
38
63
|
${VALIDATE_USAGE}
|
|
39
64
|
|
|
40
|
-
${
|
|
65
|
+
${ARTIFACT_USAGE}
|
|
66
|
+
|
|
67
|
+
${ENVELOPE_USAGE}
|
|
68
|
+
|
|
69
|
+
${EXPORT_RUN_USAGE}
|
|
70
|
+
|
|
71
|
+
${RUNS_USAGE}
|
|
72
|
+
|
|
73
|
+
${PROVIDERS_USAGE}
|
|
74
|
+
|
|
75
|
+
${SKILLS_USAGE}`;
|
|
41
76
|
function parseArgs(argv) {
|
|
42
77
|
const args = argv.slice(2);
|
|
43
78
|
if (args.length === 0) {
|
|
@@ -47,11 +82,22 @@ function parseArgs(argv) {
|
|
|
47
82
|
args.length === 1) {
|
|
48
83
|
return { help: true };
|
|
49
84
|
}
|
|
85
|
+
if (args[0] === "--version" || args[0] === "-V") {
|
|
86
|
+
if (args.length > 1) {
|
|
87
|
+
throw new Error(`Unexpected argument: ${args[1]}`);
|
|
88
|
+
}
|
|
89
|
+
return { help: false, version: true };
|
|
90
|
+
}
|
|
50
91
|
const command = args[0];
|
|
51
92
|
if (command === "providers" ||
|
|
52
93
|
command === "validate" ||
|
|
53
94
|
command === "run" ||
|
|
54
|
-
command === "init"
|
|
95
|
+
command === "init" ||
|
|
96
|
+
command === "artifact" ||
|
|
97
|
+
command === "envelope" ||
|
|
98
|
+
command === "export-run" ||
|
|
99
|
+
command === "runs" ||
|
|
100
|
+
command === "skills") {
|
|
55
101
|
return { help: false, command };
|
|
56
102
|
}
|
|
57
103
|
if (args.includes("--help") || args.includes("-h")) {
|
|
@@ -61,6 +107,7 @@ function parseArgs(argv) {
|
|
|
61
107
|
let pipeline;
|
|
62
108
|
let port;
|
|
63
109
|
let checkout;
|
|
110
|
+
let mcpStateless = false;
|
|
64
111
|
for (let i = 1; i < args.length; i++) {
|
|
65
112
|
if (args[i] === "--task") {
|
|
66
113
|
task = args[++i];
|
|
@@ -82,8 +129,11 @@ function parseArgs(argv) {
|
|
|
82
129
|
throw new Error(`Invalid --port: ${raw}`);
|
|
83
130
|
}
|
|
84
131
|
}
|
|
132
|
+
else if (args[i] === "--mcp-stateless") {
|
|
133
|
+
mcpStateless = true;
|
|
134
|
+
}
|
|
85
135
|
}
|
|
86
|
-
return { help: false, command, task, pipeline, port, checkout };
|
|
136
|
+
return { help: false, command, task, pipeline, port, checkout, mcpStateless };
|
|
87
137
|
}
|
|
88
138
|
export function parseRunStageArgs(argv) {
|
|
89
139
|
let runId;
|
|
@@ -211,6 +261,10 @@ async function main(argv) {
|
|
|
211
261
|
console.log(USAGE);
|
|
212
262
|
return argv.slice(2).length === 0 ? 1 : 0;
|
|
213
263
|
}
|
|
264
|
+
if (parsed.version) {
|
|
265
|
+
console.log(PACKAGE_VERSION);
|
|
266
|
+
return 0;
|
|
267
|
+
}
|
|
214
268
|
const ctx = await resolveStageflowContext(process.cwd());
|
|
215
269
|
if (parsed.command === "init") {
|
|
216
270
|
return runInitCommand(argv.slice(3), { cwd: ctx.invocationCwd });
|
|
@@ -225,17 +279,52 @@ async function main(argv) {
|
|
|
225
279
|
isGitProject: ctx.isGitProject,
|
|
226
280
|
});
|
|
227
281
|
}
|
|
282
|
+
if (parsed.command === "artifact") {
|
|
283
|
+
return runArtifactCommand(argv.slice(3), {
|
|
284
|
+
cwd: ctx.invocationCwd,
|
|
285
|
+
projectRoot: ctx.projectRoot,
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
if (parsed.command === "envelope") {
|
|
289
|
+
return runEnvelopeCommand(argv.slice(3), {
|
|
290
|
+
cwd: ctx.invocationCwd,
|
|
291
|
+
projectRoot: ctx.projectRoot,
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
if (parsed.command === "export-run") {
|
|
295
|
+
return runExportRunCommand(argv.slice(3), {
|
|
296
|
+
cwd: ctx.invocationCwd,
|
|
297
|
+
projectRoot: ctx.projectRoot,
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
if (parsed.command === "runs") {
|
|
301
|
+
return runRunsCommand(argv.slice(3), {
|
|
302
|
+
cwd: ctx.invocationCwd,
|
|
303
|
+
projectRoot: ctx.projectRoot,
|
|
304
|
+
isGitProject: ctx.isGitProject,
|
|
305
|
+
});
|
|
306
|
+
}
|
|
228
307
|
if (parsed.command === "providers") {
|
|
229
308
|
return runProvidersCommand(argv.slice(3), ctx.invocationCwd);
|
|
230
309
|
}
|
|
310
|
+
if (parsed.command === "skills") {
|
|
311
|
+
return runSkillsCommand(argv.slice(3), {
|
|
312
|
+
cwd: ctx.invocationCwd,
|
|
313
|
+
projectRoot: ctx.projectRoot,
|
|
314
|
+
});
|
|
315
|
+
}
|
|
231
316
|
const store = createRunStore({ rootDir: ctx.projectRoot });
|
|
232
317
|
if (parsed.command === "ui") {
|
|
318
|
+
const mcpStateless = resolveMcpStateless({
|
|
319
|
+
mcpStateless: parsed.mcpStateless,
|
|
320
|
+
});
|
|
233
321
|
const { url, mcpUrl } = await startUiServer({
|
|
234
322
|
agent: new PiAgentAdapter(),
|
|
235
323
|
store,
|
|
236
324
|
cwd: ctx.invocationCwd,
|
|
237
325
|
rootDir: ctx.projectRoot,
|
|
238
326
|
port: parsed.port,
|
|
327
|
+
mcpStateless,
|
|
239
328
|
});
|
|
240
329
|
console.log(`Operator console: ${url}`);
|
|
241
330
|
console.log(`MCP endpoint: ${mcpUrl}`);
|
|
@@ -243,6 +332,22 @@ async function main(argv) {
|
|
|
243
332
|
await new Promise(() => undefined);
|
|
244
333
|
return 0;
|
|
245
334
|
}
|
|
335
|
+
if (parsed.command === "mcp") {
|
|
336
|
+
const mcpStateless = resolveMcpStateless({
|
|
337
|
+
mcpStateless: parsed.mcpStateless,
|
|
338
|
+
});
|
|
339
|
+
const { mcpUrl } = await startMcpServer({
|
|
340
|
+
agent: new PiAgentAdapter(),
|
|
341
|
+
store,
|
|
342
|
+
cwd: ctx.invocationCwd,
|
|
343
|
+
rootDir: ctx.projectRoot,
|
|
344
|
+
port: parsed.port,
|
|
345
|
+
mcpStateless,
|
|
346
|
+
});
|
|
347
|
+
console.log(`MCP endpoint: ${mcpUrl}`);
|
|
348
|
+
await new Promise(() => undefined);
|
|
349
|
+
return 0;
|
|
350
|
+
}
|
|
246
351
|
if (parsed.command === "internal") {
|
|
247
352
|
const subArgs = argv.slice(3);
|
|
248
353
|
const subcommand = subArgs[0];
|
|
@@ -96,7 +96,11 @@ async function loadPipelineFromPath(pipelinePath) {
|
|
|
96
96
|
},
|
|
97
97
|
]);
|
|
98
98
|
}
|
|
99
|
-
|
|
99
|
+
const stage = {
|
|
100
|
+
...stageOutcome.value,
|
|
101
|
+
...(entry.skill !== undefined ? { skill: entry.skill } : {}),
|
|
102
|
+
};
|
|
103
|
+
stages.push(stage);
|
|
100
104
|
stageSources[stageId] = { kind: "file", path: entry.body.absolutePath };
|
|
101
105
|
}
|
|
102
106
|
const pipeline = {
|