@oisincoveney/pipeline 2.8.0 → 2.8.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/dist/argo-submit.d.ts +2 -4
- package/dist/argo-submit.js +80 -80
- package/dist/cluster-doctor.js +89 -101
- package/dist/config/defaults.js +9 -19
- package/dist/config/load.js +32 -39
- package/dist/config/schemas.d.ts +1 -1
- package/dist/gates.js +6 -225
- package/dist/mcp/gateway-error.js +15 -0
- package/dist/mcp/gateway.js +119 -220
- package/dist/moka-global-config.js +20 -20
- package/dist/moka-submit.d.ts +1 -1
- package/dist/pipeline-runtime.js +580 -371
- package/dist/run-state/git-refs.js +124 -94
- package/dist/runner-command-contract.d.ts +2 -2
- package/dist/runner-event-sink.js +37 -69
- package/dist/runtime/agent-node/agent-node.js +214 -173
- package/dist/runtime/builtins/builtins.js +344 -57
- package/dist/runtime/changed-files/changed-files.js +15 -27
- package/dist/runtime/changed-files/index.js +2 -0
- package/dist/runtime/drain-merge/drain-merge.js +124 -82
- package/dist/runtime/gates/gates.js +46 -28
- package/dist/runtime/hooks/hooks.js +74 -29
- package/dist/runtime/opencode-server.js +27 -21
- package/dist/runtime/opencode-session-executor.js +101 -44
- package/dist/runtime/parallel-node/parallel-node.js +24 -5
- package/dist/runtime/select-candidate/select-candidate.js +45 -29
- package/dist/runtime/services/agent-node-runtime-service.js +15 -0
- package/dist/runtime/services/command-executor-service.js +8 -0
- package/dist/runtime/services/config-io-service.js +42 -0
- package/dist/runtime/services/drain-merge-git-service.js +10 -0
- package/dist/runtime/services/git-porcelain-service.js +38 -0
- package/dist/runtime/services/kubernetes-argo-service.d.ts +13 -0
- package/dist/runtime/services/kubernetes-argo-service.js +81 -0
- package/dist/runtime/services/mcp-gateway-service.js +184 -0
- package/dist/runtime/services/opencode-sdk-service.js +27 -0
- package/dist/runtime/services/runner-event-sink-http-service.js +80 -0
- package/dist/runtime/services/select-candidate-service.js +13 -0
- package/package.json +1 -1
|
@@ -6,80 +6,89 @@ import { normalizeJsonSource, readJsonSchemaSource, validateJsonSchemaSource } f
|
|
|
6
6
|
import "../json-validation/index.js";
|
|
7
7
|
import { emit, emitAgentFinish, emitAgentStart } from "../events/events.js";
|
|
8
8
|
import "../events/index.js";
|
|
9
|
-
import { estimateTokens } from "../../token-estimator.js";
|
|
10
|
-
import { buildRepoMapContext } from "../../context/repo-map.js";
|
|
11
9
|
import { gatewayServerForProfile } from "../../mcp/gateway.js";
|
|
12
10
|
import { selectNodeModel } from "../../model-resolver.js";
|
|
11
|
+
import { estimateTokens } from "../../token-estimator.js";
|
|
13
12
|
import { handoffFinalizerPrompt, parseHandoff, renderHandoff, synthesizeMinimalHandoff } from "../handoff.js";
|
|
14
|
-
import {
|
|
13
|
+
import { AgentNodeRuntimeService, AgentNodeRuntimeServiceLive } from "../services/agent-node-runtime-service.js";
|
|
14
|
+
import { Effect } from "effect";
|
|
15
15
|
//#region src/runtime/agent-node/agent-node.ts
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
16
|
+
function executeAgentNode(node, context, attempt) {
|
|
17
|
+
const program = executeAgentNodeEffect(node, context, attempt);
|
|
18
|
+
return Effect.runPromise(Effect.provide(program, AgentNodeRuntimeServiceLive));
|
|
19
|
+
}
|
|
20
|
+
function executeAgentNodeEffect(node, context, attempt) {
|
|
21
|
+
return Effect.gen(function* () {
|
|
22
|
+
if (!node.profile) return {
|
|
23
|
+
evidence: [`node '${node.id}' has no profile`],
|
|
24
|
+
exitCode: 1,
|
|
25
|
+
output: ""
|
|
26
|
+
};
|
|
27
|
+
const prompt = yield* renderAgentPromptEffect(node, context);
|
|
28
|
+
const decision = decideNodeModel(prompt, node, context.config.token_budget);
|
|
29
|
+
if (decision.overBudget) return {
|
|
30
|
+
evidence: [
|
|
31
|
+
`agent boundary node=${node.id} profile=${node.profile}`,
|
|
32
|
+
`over token budget: ${decision.selection.reason}`,
|
|
33
|
+
...decision.selection.skipped.length ? [`model fallbacks skipped: ${decision.selection.skipped.join(", ")}`] : []
|
|
34
|
+
],
|
|
35
|
+
exitCode: 1,
|
|
36
|
+
output: ""
|
|
37
|
+
};
|
|
38
|
+
const modelSelection = decision.selection;
|
|
39
|
+
const plan = createRunnerLaunchPlan(context.config, {
|
|
40
|
+
model: modelSelection.model,
|
|
41
|
+
nodeId: node.id,
|
|
42
|
+
profileId: node.profile,
|
|
43
|
+
prompt,
|
|
44
|
+
worktreePath: context.worktreePath
|
|
45
|
+
});
|
|
46
|
+
if (node.timeoutMs) plan.timeoutMs = node.timeoutMs;
|
|
47
|
+
context.agentInvocations.push(plan);
|
|
48
|
+
emitAgentStart(context, plan, attempt);
|
|
49
|
+
const result = yield* (yield* AgentNodeRuntimeService).executeRunner(context.executor, plan, {
|
|
50
|
+
onOutput: agentOutputRecorder(context, node, attempt),
|
|
51
|
+
signal: context.signal
|
|
52
|
+
});
|
|
53
|
+
emitAgentFinish(context, plan, attempt, result);
|
|
54
|
+
if (result.sessionId) context.nodeStateStore.recordSessionId(node.id, result.sessionId);
|
|
55
|
+
const finalized = yield* finalizeAgentOutputEffect({
|
|
56
|
+
context,
|
|
57
|
+
node,
|
|
58
|
+
normalized: normalizeAgentOutput(plan, result.stdout),
|
|
59
|
+
plan,
|
|
60
|
+
result,
|
|
61
|
+
attempt
|
|
62
|
+
});
|
|
63
|
+
const handoff = yield* maybeDeriveHandoffEffect(context, node, finalized.output, attempt);
|
|
64
|
+
return withOptionalHandoff({
|
|
65
|
+
evidence: [
|
|
66
|
+
`agent boundary node=${node.id} profile=${node.profile} runner=${plan.runnerId}`,
|
|
67
|
+
`estimated context tokens: ${decision.estimatedTokens}`,
|
|
68
|
+
`model selection: ${modelSelection.model ?? "profile/default"} (${modelSelection.reason})`,
|
|
69
|
+
...modelSelection.skipped.length ? [`model fallbacks skipped: ${modelSelection.skipped.join(", ")}`] : [],
|
|
70
|
+
...finalized.evidence,
|
|
71
|
+
...result.stderr ? [`stderr: ${result.stderr}`] : [],
|
|
72
|
+
...result.timedOut ? ["agent timed out"] : []
|
|
73
|
+
],
|
|
74
|
+
exitCode: result.exitCode,
|
|
75
|
+
output: finalized.output,
|
|
76
|
+
timedOut: result.timedOut
|
|
77
|
+
}, handoff);
|
|
67
78
|
});
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
...
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
timedOut: result.timedOut
|
|
82
|
-
}, handoff);
|
|
79
|
+
}
|
|
80
|
+
function agentOutputRecorder(context, node, attempt) {
|
|
81
|
+
return (event) => {
|
|
82
|
+
if (event.stream !== "stdout") return;
|
|
83
|
+
emit(context, {
|
|
84
|
+
attempt,
|
|
85
|
+
format: "text",
|
|
86
|
+
nodeId: node.id,
|
|
87
|
+
output: event.chunk,
|
|
88
|
+
...node.profile ? { profile: node.profile } : {},
|
|
89
|
+
type: "node.output.recorded"
|
|
90
|
+
});
|
|
91
|
+
};
|
|
83
92
|
}
|
|
84
93
|
function withOptionalHandoff(result, handoff) {
|
|
85
94
|
return handoff ? {
|
|
@@ -97,19 +106,22 @@ function profileRunner(context, node) {
|
|
|
97
106
|
* raw output, falling back to a synthesized minimal handoff. Returns undefined
|
|
98
107
|
* when disabled so behaviour is unchanged.
|
|
99
108
|
*/
|
|
100
|
-
|
|
101
|
-
if (!context.config.context_handoff?.enabled) return;
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
109
|
+
function maybeDeriveHandoffEffect(context, node, rawOutput, attempt) {
|
|
110
|
+
if (!context.config.context_handoff?.enabled) return Effect.succeed(void 0);
|
|
111
|
+
const handoff = parseHandoff(rawOutput);
|
|
112
|
+
return handoff ? Effect.succeed(handoff) : runHandoffFinalizerEffect(context, node, rawOutput, attempt);
|
|
113
|
+
}
|
|
114
|
+
function runHandoffFinalizerEffect(context, node, rawOutput, attempt) {
|
|
115
|
+
return Effect.gen(function* () {
|
|
116
|
+
const runner = profileRunner(context, node);
|
|
117
|
+
if (!(runner && rawOutput.trim())) return synthesizeMinimalHandoff(rawOutput);
|
|
118
|
+
const plan = createHandoffFinalizerPlan(context, node, runner, rawOutput);
|
|
119
|
+
context.agentInvocations.push(plan);
|
|
120
|
+
emitAgentStart(context, plan, attempt);
|
|
121
|
+
const result = yield* (yield* AgentNodeRuntimeService).executeRunner(context.executor, plan, { signal: context.signal });
|
|
122
|
+
emitAgentFinish(context, plan, attempt, result);
|
|
123
|
+
return parseHandoff(normalizeAgentOutput(plan, result.stdout).output) ?? synthesizeMinimalHandoff(rawOutput);
|
|
124
|
+
}).pipe(Effect.catchAll(() => Effect.succeed(synthesizeMinimalHandoff(rawOutput))));
|
|
113
125
|
}
|
|
114
126
|
function createHandoffFinalizerPlan(context, node, runner, rawOutput) {
|
|
115
127
|
const finalizerProfileId = `${node.id}:handoff`;
|
|
@@ -160,13 +172,15 @@ function decideNodeModel(prompt, node, budget) {
|
|
|
160
172
|
selection
|
|
161
173
|
};
|
|
162
174
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
175
|
+
function finalizeAgentOutputEffect(inputs) {
|
|
176
|
+
return Effect.gen(function* () {
|
|
177
|
+
const { attempt, context, node, normalized, plan, result } = inputs;
|
|
178
|
+
const validStructuredOutput = selectValidStructuredOutput(context, node, normalized, plan, result.stdout);
|
|
179
|
+
if (validStructuredOutput) return validStructuredOutput;
|
|
180
|
+
const repairContext = outputRepairContext(context, node, normalized, result);
|
|
181
|
+
if (!repairContext) return normalized;
|
|
182
|
+
return yield* runOutputRepairEffect(context, node, normalized, repairContext, attempt);
|
|
183
|
+
});
|
|
170
184
|
}
|
|
171
185
|
function selectValidStructuredOutput(context, node, normalized, plan, stdout) {
|
|
172
186
|
const output = (node.profile ? context.config.profiles[node.profile] : void 0)?.output;
|
|
@@ -211,53 +225,56 @@ function outputRepairContext(context, node, normalized, result) {
|
|
|
211
225
|
validation: firstValidation
|
|
212
226
|
};
|
|
213
227
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
const
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
attempt
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
228
|
+
function runOutputRepairEffect(context, node, normalized, repairContext, nodeAttempt) {
|
|
229
|
+
return Effect.gen(function* () {
|
|
230
|
+
let latest = normalized;
|
|
231
|
+
let latestValidation = repairContext.validation;
|
|
232
|
+
const evidence = [...repairContext.evidence];
|
|
233
|
+
const service = yield* AgentNodeRuntimeService;
|
|
234
|
+
for (let attempt = 1; attempt <= repairContext.maxAttempts; attempt += 1) {
|
|
235
|
+
const repairPlan = createOutputRepairPlan({
|
|
236
|
+
context,
|
|
237
|
+
node,
|
|
238
|
+
originalOutput: latest.output,
|
|
239
|
+
repairRunner: repairContext.runner,
|
|
240
|
+
schemaPath: repairContext.schemaPath,
|
|
241
|
+
validation: latestValidation
|
|
242
|
+
});
|
|
243
|
+
context.agentInvocations.push(repairPlan);
|
|
244
|
+
emitAgentStart(context, repairPlan, nodeAttempt);
|
|
245
|
+
const repairResult = yield* service.executeRunner(context.executor, repairPlan, { signal: context.signal });
|
|
246
|
+
emitAgentFinish(context, repairPlan, nodeAttempt, repairResult);
|
|
247
|
+
const repaired = normalizeAgentOutput(repairPlan, repairResult.stdout);
|
|
248
|
+
const repairedOutput = normalizeJsonSource(repaired.output);
|
|
249
|
+
const repairedValidation = validateJsonSchemaSource(repairedOutput, repairContext.schemaPath, context.worktreePath);
|
|
250
|
+
latest = {
|
|
251
|
+
evidence: [
|
|
252
|
+
...repaired.evidence,
|
|
253
|
+
...repairResult.stderr ? [`repair stderr: ${repairResult.stderr}`] : [],
|
|
254
|
+
...repairResult.timedOut ? ["output repair timed out"] : []
|
|
255
|
+
],
|
|
256
|
+
output: repairedOutput
|
|
257
|
+
};
|
|
258
|
+
latestValidation = repairedValidation;
|
|
259
|
+
const passed = repairResult.exitCode === 0 && repairedValidation.passed;
|
|
260
|
+
evidence.push(...repaired.evidence, passed ? `output repair passed for ${node.id} after attempt ${attempt}` : `output repair failed for ${node.id} after attempt ${attempt}`, ...repairedValidation.evidence.map((item) => `repaired output: ${item}`));
|
|
261
|
+
emit(context, {
|
|
262
|
+
attempt,
|
|
263
|
+
nodeId: node.id,
|
|
264
|
+
passed,
|
|
265
|
+
type: "output.repair",
|
|
266
|
+
...passed ? {} : { reason: repairedValidation.reason ?? "repair failed" }
|
|
267
|
+
});
|
|
268
|
+
if (passed) return {
|
|
269
|
+
evidence,
|
|
270
|
+
output: repairedOutput
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
return {
|
|
253
274
|
evidence,
|
|
254
|
-
output:
|
|
275
|
+
output: latest.output
|
|
255
276
|
};
|
|
256
|
-
}
|
|
257
|
-
return {
|
|
258
|
-
evidence,
|
|
259
|
-
output: latest.output
|
|
260
|
-
};
|
|
277
|
+
});
|
|
261
278
|
}
|
|
262
279
|
function outputRepairOptions(output) {
|
|
263
280
|
const repair = output.repair;
|
|
@@ -308,24 +325,39 @@ function createOutputRepairPlan(inputs) {
|
|
|
308
325
|
function normalizeAgentOutput(plan, stdout) {
|
|
309
326
|
return normalizeRunnerOutput(plan, stdout);
|
|
310
327
|
}
|
|
311
|
-
|
|
328
|
+
function repoMapSectionEffect(node, context) {
|
|
312
329
|
const repoMap = context.config.repo_map;
|
|
313
|
-
if (!repoMap?.enabled) return "";
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
330
|
+
if (!repoMap?.enabled) return Effect.succeed("");
|
|
331
|
+
return Effect.gen(function* () {
|
|
332
|
+
const service = yield* AgentNodeRuntimeService;
|
|
333
|
+
return (yield* Effect.tryPromise({
|
|
334
|
+
catch: () => "",
|
|
335
|
+
try: () => service.buildRepoMap({
|
|
336
|
+
artifacts: node.needs.flatMap((need) => context.nodeStateStore.handoff(need)?.artifacts ?? []),
|
|
337
|
+
taskText: context.task,
|
|
338
|
+
tokenBudget: repoMap.token_budget,
|
|
339
|
+
worktreePath: context.worktreePath
|
|
340
|
+
})
|
|
320
341
|
})).context;
|
|
321
|
-
}
|
|
322
|
-
return "";
|
|
323
|
-
}
|
|
342
|
+
}).pipe(Effect.catchAll(() => Effect.succeed("")));
|
|
324
343
|
}
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
344
|
+
function renderAgentPromptEffect(node, context) {
|
|
345
|
+
return Effect.gen(function* () {
|
|
346
|
+
const profile = node.profile ? context.config.profiles[node.profile] : void 0;
|
|
347
|
+
const instructions = profile ? yield* readInstructionsEffect(context.worktreePath, profile.instructions) : "";
|
|
348
|
+
const repoMap = yield* repoMapSectionEffect(node, context);
|
|
349
|
+
return agentPromptSections({
|
|
350
|
+
context,
|
|
351
|
+
instructions,
|
|
352
|
+
node,
|
|
353
|
+
pathReferences: yield* renderProfilePathReferences(profile, context),
|
|
354
|
+
profile,
|
|
355
|
+
repoMap
|
|
356
|
+
}).filter(Boolean).join("\n");
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
function agentPromptSections(inputs) {
|
|
360
|
+
const { context, instructions, node, pathReferences, profile, repoMap } = inputs;
|
|
329
361
|
return [
|
|
330
362
|
instructions.trim(),
|
|
331
363
|
"",
|
|
@@ -343,14 +375,16 @@ async function renderAgentPrompt(node, context) {
|
|
|
343
375
|
`- rules: ${(profile?.rules ?? []).join(", ") || "none"}`,
|
|
344
376
|
`- skills: ${(profile?.skills ?? []).join(", ") || "none"}`,
|
|
345
377
|
`- mcp_servers: ${(profile?.mcp_servers ?? []).join(", ") || "none"}`,
|
|
346
|
-
|
|
347
|
-
renderPathReferences("Loaded skills", profile?.skills, context.config.skills, context.worktreePath),
|
|
378
|
+
...pathReferences,
|
|
348
379
|
renderMcpReferences(context.config, profile),
|
|
349
380
|
"",
|
|
350
381
|
...inheritedOutputSections(node, context),
|
|
351
382
|
"Dependency outputs:",
|
|
352
383
|
...node.needs.map((need) => renderDependencySection(need, context))
|
|
353
|
-
]
|
|
384
|
+
];
|
|
385
|
+
}
|
|
386
|
+
function renderProfilePathReferences(profile, context) {
|
|
387
|
+
return Effect.all([renderPathReferencesEffect("Loaded rules", profile?.rules, context.config.rules, context.worktreePath), renderPathReferencesEffect("Loaded skills", profile?.skills, context.config.skills, context.worktreePath)]);
|
|
354
388
|
}
|
|
355
389
|
/**
|
|
356
390
|
* PIPE-83.5: render a dependency's curated NodeHandoff when one was derived
|
|
@@ -420,31 +454,38 @@ function renderTaskContext(taskContext) {
|
|
|
420
454
|
...acceptance.map((criterion) => `- ${criterion.id}: ${criterion.text}`)
|
|
421
455
|
].filter(Boolean).join("\n");
|
|
422
456
|
}
|
|
423
|
-
function
|
|
424
|
-
if (instructions.inline) return instructions.inline;
|
|
425
|
-
if (instructions.path)
|
|
426
|
-
|
|
457
|
+
function readInstructionsEffect(worktreePath, instructions) {
|
|
458
|
+
if (instructions.inline) return Effect.succeed(instructions.inline);
|
|
459
|
+
if (instructions.path) {
|
|
460
|
+
const instructionPath = instructions.path;
|
|
461
|
+
return AgentNodeRuntimeService.pipe(Effect.flatMap((service) => service.readText(resolveFileReference(worktreePath, instructionPath))));
|
|
462
|
+
}
|
|
463
|
+
return Effect.succeed("");
|
|
427
464
|
}
|
|
428
|
-
function
|
|
429
|
-
if (!ids?.length) return "";
|
|
430
|
-
return
|
|
465
|
+
function renderPathReferencesEffect(heading, ids, registry, worktreePath) {
|
|
466
|
+
if (!ids?.length) return Effect.succeed("");
|
|
467
|
+
return Effect.gen(function* () {
|
|
468
|
+
const sections = yield* Effect.all(ids.map((id) => renderPathReferenceEffect(id, registry, worktreePath)));
|
|
469
|
+
return [
|
|
470
|
+
"",
|
|
471
|
+
`${heading}:`,
|
|
472
|
+
...sections
|
|
473
|
+
].join("\n");
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
function renderPathReferenceEffect(id, registry, worktreePath) {
|
|
477
|
+
const ref = registry[id];
|
|
478
|
+
const path = ref?.path ?? "";
|
|
479
|
+
const resolved = resolveRuntimePathReference(worktreePath, ref);
|
|
480
|
+
return AgentNodeRuntimeService.pipe(Effect.flatMap((service) => service.readText(resolved)), Effect.map((content) => [
|
|
481
|
+
`## ${id}`,
|
|
482
|
+
`Path: ${path}`,
|
|
431
483
|
"",
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
const ref = registry[id];
|
|
435
|
-
const path = ref?.path ?? "";
|
|
436
|
-
const content = readFileSync(resolveRuntimePathReference(worktreePath, ref), "utf8").trimEnd();
|
|
437
|
-
return [
|
|
438
|
-
`## ${id}`,
|
|
439
|
-
`Path: ${path}`,
|
|
440
|
-
"",
|
|
441
|
-
content
|
|
442
|
-
].join("\n");
|
|
443
|
-
})
|
|
444
|
-
].join("\n");
|
|
484
|
+
content.trimEnd()
|
|
485
|
+
].join("\n")));
|
|
445
486
|
}
|
|
446
487
|
function resolveRuntimePathReference(worktreePath, ref) {
|
|
447
|
-
if (ref?.source_root === "package") return resolvePackageAssetPath(ref.path);
|
|
488
|
+
if (ref?.source_root === "package") return resolvePackageAssetPath(ref.path ?? "");
|
|
448
489
|
return resolveFileReference(worktreePath, ref?.path ?? "");
|
|
449
490
|
}
|
|
450
491
|
function renderMcpReferences(config, profile) {
|