@oisincoveney/pipeline 1.2.1 → 1.3.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 +6 -0
- package/dist/index.js +140 -6
- package/docs/config-architecture.md +5 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -45,6 +45,12 @@ Run the default workflow:
|
|
|
45
45
|
pipe run "Implement PIPE-123 user-facing behavior"
|
|
46
46
|
```
|
|
47
47
|
|
|
48
|
+
Run a read-only repository inspection:
|
|
49
|
+
|
|
50
|
+
```shell
|
|
51
|
+
pipe run --workflow inspect "Report the app structure and available checks. Do not modify files."
|
|
52
|
+
```
|
|
53
|
+
|
|
48
54
|
The `pipe` binary also accepts the task directly:
|
|
49
55
|
|
|
50
56
|
```shell
|
package/dist/index.js
CHANGED
|
@@ -36123,6 +36123,12 @@ orchestrator:
|
|
|
36123
36123
|
hooks: {}
|
|
36124
36124
|
|
|
36125
36125
|
workflows:
|
|
36126
|
+
inspect:
|
|
36127
|
+
description: Read-only repository inspection workflow.
|
|
36128
|
+
nodes:
|
|
36129
|
+
- id: inspect
|
|
36130
|
+
kind: agent
|
|
36131
|
+
profile: pipeline-inspector
|
|
36126
36132
|
default:
|
|
36127
36133
|
description: Default research, red, green, verify, learn workflow.
|
|
36128
36134
|
nodes:
|
|
@@ -36261,6 +36267,20 @@ profiles:
|
|
|
36261
36267
|
output:
|
|
36262
36268
|
format: json_schema
|
|
36263
36269
|
schema_path: .pipeline/schemas/research.schema.json
|
|
36270
|
+
pipeline-inspector:
|
|
36271
|
+
runner: codex
|
|
36272
|
+
description: Inspect the repository without modifying files.
|
|
36273
|
+
instructions:
|
|
36274
|
+
path: .pipeline/prompts/inspector.md
|
|
36275
|
+
tools: [read, list, grep, glob, bash]
|
|
36276
|
+
filesystem:
|
|
36277
|
+
mode: read-only
|
|
36278
|
+
allow: ["**/*"]
|
|
36279
|
+
deny: ["node_modules/**", "dist/**", ".git/**"]
|
|
36280
|
+
network:
|
|
36281
|
+
mode: inherit
|
|
36282
|
+
output:
|
|
36283
|
+
format: text
|
|
36264
36284
|
pipeline-test-writer:
|
|
36265
36285
|
runner: codex
|
|
36266
36286
|
description: Add focused failing tests for the requested behavior.
|
|
@@ -36377,6 +36397,16 @@ var SCAFFOLD_FILES = {
|
|
|
36377
36397
|
"You are the research phase for the pipeline.",
|
|
36378
36398
|
"Inspect first-party source, tests, docs, and task context before proposing changes.",
|
|
36379
36399
|
"Write structured findings that identify relevant files, existing patterns, acceptance criteria, and risks.",
|
|
36400
|
+
"Return only valid JSON matching `.pipeline/schemas/research.schema.json`: an object with `findings` and `ac` arrays, plus optional `files`, `risks`, and `target`.",
|
|
36401
|
+
"Do not wrap the JSON in Markdown fences or add prose outside the JSON object.",
|
|
36402
|
+
""
|
|
36403
|
+
].join(`
|
|
36404
|
+
`),
|
|
36405
|
+
".pipeline/prompts/inspector.md": [
|
|
36406
|
+
"You are the read-only inspection phase for the pipeline.",
|
|
36407
|
+
"Inspect first-party source, tests, docs, and task context.",
|
|
36408
|
+
"Report the repository structure, available checks, important files, and notable risks.",
|
|
36409
|
+
"Do not modify files.",
|
|
36380
36410
|
""
|
|
36381
36411
|
].join(`
|
|
36382
36412
|
`),
|
|
@@ -36400,6 +36430,8 @@ var SCAFFOLD_FILES = {
|
|
|
36400
36430
|
"You are the VERIFY phase for the pipeline.",
|
|
36401
36431
|
"Run configured checks, review implementation fit, and report PASS or FAIL with evidence.",
|
|
36402
36432
|
"Do not mark the workflow passing without concrete verification evidence.",
|
|
36433
|
+
"Return only valid JSON matching `.pipeline/schemas/verify.schema.json`: an object with `verdict`, `evidence`, and optional `violations`.",
|
|
36434
|
+
"Do not wrap the JSON in Markdown fences or add prose outside the JSON object.",
|
|
36403
36435
|
""
|
|
36404
36436
|
].join(`
|
|
36405
36437
|
`),
|
|
@@ -36407,6 +36439,8 @@ var SCAFFOLD_FILES = {
|
|
|
36407
36439
|
"You are the LEARN phase for the pipeline.",
|
|
36408
36440
|
"Store durable lessons from the run when useful and report qdrant-store evidence.",
|
|
36409
36441
|
"Do not write local markdown knowledge as the durable sink.",
|
|
36442
|
+
"Return only valid JSON matching `.pipeline/schemas/learn.schema.json`: an object with `qdrant` and `evidence`.",
|
|
36443
|
+
"Do not wrap the JSON in Markdown fences or add prose outside the JSON object.",
|
|
36410
36444
|
""
|
|
36411
36445
|
].join(`
|
|
36412
36446
|
`),
|
|
@@ -36882,6 +36916,7 @@ async function runJscpd(worktreePath) {
|
|
|
36882
36916
|
}
|
|
36883
36917
|
|
|
36884
36918
|
// src/pipeline-runtime.ts
|
|
36919
|
+
var LINE_RE = /\r?\n/;
|
|
36885
36920
|
async function runPipelineFromConfig(options2) {
|
|
36886
36921
|
const worktreePath = options2.worktreePath ?? process.cwd();
|
|
36887
36922
|
const config2 = options2.config ?? loadPipelineConfig(worktreePath);
|
|
@@ -37037,16 +37072,74 @@ async function executeAgentNode(node, context) {
|
|
|
37037
37072
|
});
|
|
37038
37073
|
context.agentInvocations.push(plan);
|
|
37039
37074
|
const result = await context.executor(plan);
|
|
37075
|
+
const normalized = normalizeAgentOutput(plan, result.stdout);
|
|
37040
37076
|
return {
|
|
37041
37077
|
evidence: [
|
|
37042
37078
|
`agent boundary node=${node.id} profile=${node.profile} runner=${plan.runnerId} strategy=${plan.strategy}`,
|
|
37079
|
+
...normalized.evidence,
|
|
37043
37080
|
...result.stderr ? [`stderr: ${result.stderr}`] : [],
|
|
37044
37081
|
...result.timedOut ? ["agent timed out"] : []
|
|
37045
37082
|
],
|
|
37046
37083
|
exitCode: result.exitCode,
|
|
37047
|
-
output:
|
|
37084
|
+
output: normalized.output
|
|
37048
37085
|
};
|
|
37049
37086
|
}
|
|
37087
|
+
function normalizeAgentOutput(plan, stdout) {
|
|
37088
|
+
if (plan.type === "codex") {
|
|
37089
|
+
const text = lastJsonLineValue(stdout, (value) => {
|
|
37090
|
+
if (!isRecord(value)) {
|
|
37091
|
+
return;
|
|
37092
|
+
}
|
|
37093
|
+
const item = value.item;
|
|
37094
|
+
if (isRecord(item) && item.type === "agent_message") {
|
|
37095
|
+
return typeof item.text === "string" ? item.text : undefined;
|
|
37096
|
+
}
|
|
37097
|
+
if (value.type === "agent_message") {
|
|
37098
|
+
return typeof value.text === "string" ? value.text : undefined;
|
|
37099
|
+
}
|
|
37100
|
+
});
|
|
37101
|
+
if (text) {
|
|
37102
|
+
return {
|
|
37103
|
+
evidence: ["normalized runner output from codex JSONL"],
|
|
37104
|
+
output: text
|
|
37105
|
+
};
|
|
37106
|
+
}
|
|
37107
|
+
}
|
|
37108
|
+
if (plan.type === "opencode") {
|
|
37109
|
+
const text = lastJsonLineValue(stdout, (value) => {
|
|
37110
|
+
if (!isRecord(value)) {
|
|
37111
|
+
return;
|
|
37112
|
+
}
|
|
37113
|
+
const part = value.part;
|
|
37114
|
+
if (isRecord(part) && part.type === "text") {
|
|
37115
|
+
return typeof part.text === "string" ? part.text : undefined;
|
|
37116
|
+
}
|
|
37117
|
+
});
|
|
37118
|
+
if (text) {
|
|
37119
|
+
return {
|
|
37120
|
+
evidence: ["normalized runner output from opencode JSON events"],
|
|
37121
|
+
output: text
|
|
37122
|
+
};
|
|
37123
|
+
}
|
|
37124
|
+
}
|
|
37125
|
+
return { evidence: [], output: stdout };
|
|
37126
|
+
}
|
|
37127
|
+
function lastJsonLineValue(text, extract) {
|
|
37128
|
+
let latest;
|
|
37129
|
+
for (const line of text.split(LINE_RE)) {
|
|
37130
|
+
const trimmed = line.trim();
|
|
37131
|
+
if (!trimmed) {
|
|
37132
|
+
continue;
|
|
37133
|
+
}
|
|
37134
|
+
try {
|
|
37135
|
+
const extracted = extract(JSON.parse(trimmed));
|
|
37136
|
+
if (extracted) {
|
|
37137
|
+
latest = extracted;
|
|
37138
|
+
}
|
|
37139
|
+
} catch {}
|
|
37140
|
+
}
|
|
37141
|
+
return latest;
|
|
37142
|
+
}
|
|
37050
37143
|
function renderAgentPrompt(node, context) {
|
|
37051
37144
|
const profile = node.profile ? context.config.profiles[node.profile] : undefined;
|
|
37052
37145
|
const instructions = profile ? readInstructions(context.worktreePath, profile.instructions) : "";
|
|
@@ -37440,6 +37533,7 @@ function formatConfigError(err) {
|
|
|
37440
37533
|
|
|
37441
37534
|
// src/index.ts
|
|
37442
37535
|
var PATH_SEPARATOR_RE = /[\\/]/;
|
|
37536
|
+
var LINE_RE2 = /\r?\n/;
|
|
37443
37537
|
function pipe2(description, options2 = {}) {
|
|
37444
37538
|
try {
|
|
37445
37539
|
if (!description.trim()) {
|
|
@@ -37466,11 +37560,7 @@ async function runConfiguredPipeline(inputs) {
|
|
|
37466
37560
|
});
|
|
37467
37561
|
console.log(formatRuntimeResult(result));
|
|
37468
37562
|
if (result.outcome === "FAIL") {
|
|
37469
|
-
throw new Error(
|
|
37470
|
-
"Pipeline failed.",
|
|
37471
|
-
...result.failureDetails.map((failure) => failure.nodeId ? `- ${failure.nodeId}: ${failure.reason}` : `- ${failure.reason}`)
|
|
37472
|
-
].join(`
|
|
37473
|
-
`));
|
|
37563
|
+
throw new Error(formatRuntimeFailure(result));
|
|
37474
37564
|
}
|
|
37475
37565
|
}
|
|
37476
37566
|
function formatRuntimeResult(result) {
|
|
@@ -37482,6 +37572,50 @@ function formatRuntimeResult(result) {
|
|
|
37482
37572
|
].join(`
|
|
37483
37573
|
`);
|
|
37484
37574
|
}
|
|
37575
|
+
function formatRuntimeFailure(result) {
|
|
37576
|
+
const lines = ["Pipeline failed."];
|
|
37577
|
+
for (const failure of result.failureDetails) {
|
|
37578
|
+
lines.push(failure.nodeId ? `- ${failure.nodeId}: ${failure.reason}` : `- ${failure.reason}`);
|
|
37579
|
+
appendIndentedSection(lines, "Evidence", failure.evidence);
|
|
37580
|
+
const node = failure.nodeId ? result.nodes.find((item) => item.nodeId === failure.nodeId) : undefined;
|
|
37581
|
+
if (node) {
|
|
37582
|
+
lines.push(` Node: status=${node.status} attempts=${node.attempts} exit=${node.exitCode}`);
|
|
37583
|
+
appendIndentedSection(lines, "Node evidence", node.evidence);
|
|
37584
|
+
appendIndentedSection(lines, "Node output", [node.output]);
|
|
37585
|
+
}
|
|
37586
|
+
}
|
|
37587
|
+
if (result.gates.length > 0) {
|
|
37588
|
+
lines.push("Gates:");
|
|
37589
|
+
for (const gate of result.gates) {
|
|
37590
|
+
lines.push(` - ${gate.nodeId}/${gate.gateId}: ${gate.passed ? "PASS" : "FAIL"}${gate.reason ? ` (${gate.reason})` : ""}`);
|
|
37591
|
+
appendIndentedSection(lines, "Gate evidence", gate.evidence);
|
|
37592
|
+
}
|
|
37593
|
+
}
|
|
37594
|
+
return lines.join(`
|
|
37595
|
+
`);
|
|
37596
|
+
}
|
|
37597
|
+
function appendIndentedSection(lines, label, values) {
|
|
37598
|
+
const text = values.filter(Boolean).join(`
|
|
37599
|
+
`).trim();
|
|
37600
|
+
if (!text) {
|
|
37601
|
+
return;
|
|
37602
|
+
}
|
|
37603
|
+
lines.push(` ${label}:`);
|
|
37604
|
+
lines.push(indent(truncateMiddle(text, 4000), " "));
|
|
37605
|
+
}
|
|
37606
|
+
function indent(text, prefix) {
|
|
37607
|
+
return text.split(LINE_RE2).map((line) => `${prefix}${line}`).join(`
|
|
37608
|
+
`);
|
|
37609
|
+
}
|
|
37610
|
+
function truncateMiddle(text, maxLength) {
|
|
37611
|
+
if (text.length <= maxLength) {
|
|
37612
|
+
return text;
|
|
37613
|
+
}
|
|
37614
|
+
const keep = Math.floor((maxLength - 32) / 2);
|
|
37615
|
+
return `${text.slice(0, keep)}
|
|
37616
|
+
... truncated ...
|
|
37617
|
+
${text.slice(-keep)}`;
|
|
37618
|
+
}
|
|
37485
37619
|
function createCliProgram() {
|
|
37486
37620
|
const program2 = new Command;
|
|
37487
37621
|
program2.name("@oisincoveney/pipeline").description("Run and install the oisin pipeline").exitOverride();
|