@snappedly-tools/shipyard 0.11.1 → 0.11.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/main.js
CHANGED
|
@@ -2400,7 +2400,7 @@ var runnerCommand = Command.make(
|
|
|
2400
2400
|
purgeRunnerCommand
|
|
2401
2401
|
])
|
|
2402
2402
|
);
|
|
2403
|
-
var VERSION = "0.11.
|
|
2403
|
+
var VERSION = "0.11.2" ;
|
|
2404
2404
|
var SHIPYARD_PACKAGE_NAME = "@snappedly-tools/shipyard";
|
|
2405
2405
|
var isMissingPath = (error) => error !== null && typeof error === "object" && "code" in error && error.code === "ENOENT";
|
|
2406
2406
|
var lstatIfPresent = async (path) => {
|
|
@@ -96,7 +96,7 @@ const closeClean = async (sandbox: {
|
|
|
96
96
|
if (preservedWorktreePath)
|
|
97
97
|
throw new Error(`Sandbox has uncommitted work at ${preservedWorktreePath}`);
|
|
98
98
|
};
|
|
99
|
-
const verifyTriage = (ticketId: string) => {
|
|
99
|
+
const verifyTriage = (ticketId: string, triageOutput?: string) => {
|
|
100
100
|
try {
|
|
101
101
|
execFileSync("bash", [".shipyard/verify-triage.sh", ticketId, repository], {
|
|
102
102
|
encoding: "utf8",
|
|
@@ -105,9 +105,31 @@ const verifyTriage = (ticketId: string) => {
|
|
|
105
105
|
const detail = (error as { stderr?: string | Buffer }).stderr
|
|
106
106
|
?.toString()
|
|
107
107
|
.trim();
|
|
108
|
-
|
|
108
|
+
const reason = detail || `Could not verify triage for #${ticketId}`;
|
|
109
|
+
const output = triageOutput?.trim();
|
|
110
|
+
throw new Error(
|
|
111
|
+
output ? `${reason}; triage output: ${output.slice(-1200)}` : reason,
|
|
112
|
+
);
|
|
109
113
|
}
|
|
110
114
|
};
|
|
115
|
+
const verifyTriageAccess = async (
|
|
116
|
+
sandbox: {
|
|
117
|
+
exec: (
|
|
118
|
+
command: string,
|
|
119
|
+
) => Promise<{ exitCode: number; stdout: string; stderr: string }>;
|
|
120
|
+
},
|
|
121
|
+
ticketId: string,
|
|
122
|
+
) => {
|
|
123
|
+
if (!/^\d+$/.test(ticketId))
|
|
124
|
+
throw new Error(`Invalid issue number: ${ticketId}`);
|
|
125
|
+
const result = await sandbox.exec(
|
|
126
|
+
`gh issue view ${ticketId} --repo ${repository} --json number --jq .number`,
|
|
127
|
+
);
|
|
128
|
+
if (result.exitCode !== 0)
|
|
129
|
+
throw new Error(
|
|
130
|
+
`Cannot triage issue #${ticketId}: GitHub access failed in the sandbox: ${result.stderr.trim() || result.stdout.trim() || `exit ${result.exitCode}`}`,
|
|
131
|
+
);
|
|
132
|
+
};
|
|
111
133
|
const planSchema = z.object({ issues: z.array(z.object({ id: z.string() })) });
|
|
112
134
|
type Ticket = {
|
|
113
135
|
id: string;
|
|
@@ -174,15 +196,19 @@ const runWorker = async (scope: Scope, ticket: Ticket) => {
|
|
|
174
196
|
copyToWorktree: [".shipyard/setup.sh"],
|
|
175
197
|
});
|
|
176
198
|
try {
|
|
177
|
-
|
|
178
|
-
|
|
199
|
+
let triageOutput: string | undefined;
|
|
200
|
+
if (!ticket.triageReady) {
|
|
201
|
+
await verifyTriageAccess(sandbox, ticket.id);
|
|
202
|
+
const triage = await sandbox.run({
|
|
179
203
|
name: `triage #${ticket.id}`,
|
|
180
204
|
maxIterations: 1,
|
|
181
205
|
agent: roleAgent("routine", shipyard.CODEX_MODELS.routine),
|
|
182
206
|
promptFile: "./.shipyard/triage-prompt.md",
|
|
183
207
|
promptArgs: { TASK_ID: ticket.id, BASE_BRANCH: targetBranch },
|
|
184
208
|
});
|
|
185
|
-
|
|
209
|
+
triageOutput = triage.stdout;
|
|
210
|
+
}
|
|
211
|
+
verifyTriage(ticket.id, triageOutput);
|
|
186
212
|
const implementation = await sandbox.run({
|
|
187
213
|
name: "implementer",
|
|
188
214
|
maxIterations: 100,
|
|
@@ -447,15 +473,19 @@ for (let iteration = 0; iteration < 10; iteration++) {
|
|
|
447
473
|
let handoffEvidence: string;
|
|
448
474
|
try {
|
|
449
475
|
if (scope.kind === "standalone") {
|
|
450
|
-
|
|
451
|
-
|
|
476
|
+
let triageOutput: string | undefined;
|
|
477
|
+
if (!scope.triageReady) {
|
|
478
|
+
await verifyTriageAccess(integration, id);
|
|
479
|
+
const triage = await integration.run({
|
|
452
480
|
name: `triage #${id}`,
|
|
453
481
|
maxIterations: 1,
|
|
454
482
|
agent: roleAgent("routine", shipyard.CODEX_MODELS.routine),
|
|
455
483
|
promptFile: "./.shipyard/triage-prompt.md",
|
|
456
484
|
promptArgs: { TASK_ID: id, BASE_BRANCH: targetBranch },
|
|
457
485
|
});
|
|
458
|
-
|
|
486
|
+
triageOutput = triage.stdout;
|
|
487
|
+
}
|
|
488
|
+
verifyTriage(id, triageOutput);
|
|
459
489
|
const standalone = await integration.run({
|
|
460
490
|
name: "implementer",
|
|
461
491
|
maxIterations: 100,
|
|
@@ -96,7 +96,7 @@ const closeClean = async (sandbox: {
|
|
|
96
96
|
if (preservedWorktreePath)
|
|
97
97
|
throw new Error(`Sandbox has uncommitted work at ${preservedWorktreePath}`);
|
|
98
98
|
};
|
|
99
|
-
const verifyTriage = (ticketId: string) => {
|
|
99
|
+
const verifyTriage = (ticketId: string, triageOutput?: string) => {
|
|
100
100
|
try {
|
|
101
101
|
execFileSync("bash", [".shipyard/verify-triage.sh", ticketId, repository], {
|
|
102
102
|
encoding: "utf8",
|
|
@@ -105,9 +105,31 @@ const verifyTriage = (ticketId: string) => {
|
|
|
105
105
|
const detail = (error as { stderr?: string | Buffer }).stderr
|
|
106
106
|
?.toString()
|
|
107
107
|
.trim();
|
|
108
|
-
|
|
108
|
+
const reason = detail || `Could not verify triage for #${ticketId}`;
|
|
109
|
+
const output = triageOutput?.trim();
|
|
110
|
+
throw new Error(
|
|
111
|
+
output ? `${reason}; triage output: ${output.slice(-1200)}` : reason,
|
|
112
|
+
);
|
|
109
113
|
}
|
|
110
114
|
};
|
|
115
|
+
const verifyTriageAccess = async (
|
|
116
|
+
sandbox: {
|
|
117
|
+
exec: (
|
|
118
|
+
command: string,
|
|
119
|
+
) => Promise<{ exitCode: number; stdout: string; stderr: string }>;
|
|
120
|
+
},
|
|
121
|
+
ticketId: string,
|
|
122
|
+
) => {
|
|
123
|
+
if (!/^\d+$/.test(ticketId))
|
|
124
|
+
throw new Error(`Invalid issue number: ${ticketId}`);
|
|
125
|
+
const result = await sandbox.exec(
|
|
126
|
+
`gh issue view ${ticketId} --repo ${repository} --json number --jq .number`,
|
|
127
|
+
);
|
|
128
|
+
if (result.exitCode !== 0)
|
|
129
|
+
throw new Error(
|
|
130
|
+
`Cannot triage issue #${ticketId}: GitHub access failed in the sandbox: ${result.stderr.trim() || result.stdout.trim() || `exit ${result.exitCode}`}`,
|
|
131
|
+
);
|
|
132
|
+
};
|
|
111
133
|
const planSchema = z.object({ issues: z.array(z.object({ id: z.string() })) });
|
|
112
134
|
type Ticket = {
|
|
113
135
|
id: string;
|
|
@@ -184,15 +206,19 @@ const runWorker = async (scope: Scope, ticket: Ticket) => {
|
|
|
184
206
|
copyToWorktree: [".shipyard/setup.sh"],
|
|
185
207
|
});
|
|
186
208
|
try {
|
|
187
|
-
|
|
188
|
-
|
|
209
|
+
let triageOutput: string | undefined;
|
|
210
|
+
if (!ticket.triageReady) {
|
|
211
|
+
await verifyTriageAccess(sandbox, ticket.id);
|
|
212
|
+
const triage = await sandbox.run({
|
|
189
213
|
name: `triage #${ticket.id}`,
|
|
190
214
|
maxIterations: 1,
|
|
191
215
|
agent: roleAgent("routine", shipyard.CODEX_MODELS.routine),
|
|
192
216
|
promptFile: "./.shipyard/triage-prompt.md",
|
|
193
217
|
promptArgs: { TASK_ID: ticket.id, BASE_BRANCH: targetBranch },
|
|
194
218
|
});
|
|
195
|
-
|
|
219
|
+
triageOutput = triage.stdout;
|
|
220
|
+
}
|
|
221
|
+
verifyTriage(ticket.id, triageOutput);
|
|
196
222
|
const implementation = await sandbox.run({
|
|
197
223
|
name: "implementer",
|
|
198
224
|
maxIterations: 100,
|
|
@@ -470,15 +496,19 @@ for (let iteration = 0; iteration < 10; iteration++) {
|
|
|
470
496
|
let handoffEvidence: string;
|
|
471
497
|
try {
|
|
472
498
|
if (scope.kind === "standalone") {
|
|
473
|
-
|
|
474
|
-
|
|
499
|
+
let triageOutput: string | undefined;
|
|
500
|
+
if (!scope.triageReady) {
|
|
501
|
+
await verifyTriageAccess(integration, id);
|
|
502
|
+
const triage = await integration.run({
|
|
475
503
|
name: `triage #${id}`,
|
|
476
504
|
maxIterations: 1,
|
|
477
505
|
agent: roleAgent("routine", shipyard.CODEX_MODELS.routine),
|
|
478
506
|
promptFile: "./.shipyard/triage-prompt.md",
|
|
479
507
|
promptArgs: { TASK_ID: id, BASE_BRANCH: targetBranch },
|
|
480
508
|
});
|
|
481
|
-
|
|
509
|
+
triageOutput = triage.stdout;
|
|
510
|
+
}
|
|
511
|
+
verifyTriage(id, triageOutput);
|
|
482
512
|
const standalone = await integration.run({
|
|
483
513
|
name: "implementer",
|
|
484
514
|
maxIterations: 100,
|
|
@@ -93,7 +93,7 @@ const closeClean = async (sandbox: {
|
|
|
93
93
|
if (preservedWorktreePath)
|
|
94
94
|
throw new Error(`Sandbox has uncommitted work at ${preservedWorktreePath}`);
|
|
95
95
|
};
|
|
96
|
-
const verifyTriage = (ticketId: string) => {
|
|
96
|
+
const verifyTriage = (ticketId: string, triageOutput?: string) => {
|
|
97
97
|
try {
|
|
98
98
|
execFileSync("bash", [".shipyard/verify-triage.sh", ticketId, repository], {
|
|
99
99
|
encoding: "utf8",
|
|
@@ -102,9 +102,31 @@ const verifyTriage = (ticketId: string) => {
|
|
|
102
102
|
const detail = (error as { stderr?: string | Buffer }).stderr
|
|
103
103
|
?.toString()
|
|
104
104
|
.trim();
|
|
105
|
-
|
|
105
|
+
const reason = detail || `Could not verify triage for #${ticketId}`;
|
|
106
|
+
const output = triageOutput?.trim();
|
|
107
|
+
throw new Error(
|
|
108
|
+
output ? `${reason}; triage output: ${output.slice(-1200)}` : reason,
|
|
109
|
+
);
|
|
106
110
|
}
|
|
107
111
|
};
|
|
112
|
+
const verifyTriageAccess = async (
|
|
113
|
+
sandbox: {
|
|
114
|
+
exec: (
|
|
115
|
+
command: string,
|
|
116
|
+
) => Promise<{ exitCode: number; stdout: string; stderr: string }>;
|
|
117
|
+
},
|
|
118
|
+
ticketId: string,
|
|
119
|
+
) => {
|
|
120
|
+
if (!/^\d+$/.test(ticketId))
|
|
121
|
+
throw new Error(`Invalid issue number: ${ticketId}`);
|
|
122
|
+
const result = await sandbox.exec(
|
|
123
|
+
`gh issue view ${ticketId} --repo ${repository} --json number --jq .number`,
|
|
124
|
+
);
|
|
125
|
+
if (result.exitCode !== 0)
|
|
126
|
+
throw new Error(
|
|
127
|
+
`Cannot triage issue #${ticketId}: GitHub access failed in the sandbox: ${result.stderr.trim() || result.stdout.trim() || `exit ${result.exitCode}`}`,
|
|
128
|
+
);
|
|
129
|
+
};
|
|
108
130
|
|
|
109
131
|
const handoffEvidence = (stdout: string): string | undefined =>
|
|
110
132
|
[...stdout.matchAll(/<handoff>([\s\S]*?)<\/handoff>/g)].at(-1)?.[1]?.trim();
|
|
@@ -200,15 +222,19 @@ for (let iteration = 0; iteration < MAX_ITERATIONS; iteration++) {
|
|
|
200
222
|
? issue.tickets?.find((ticket) => ticket.id === ticketId)
|
|
201
223
|
?.triageReady
|
|
202
224
|
: issue.triageReady;
|
|
203
|
-
|
|
204
|
-
|
|
225
|
+
let triageOutput: string | undefined;
|
|
226
|
+
if (!ready) {
|
|
227
|
+
await verifyTriageAccess(sandbox, ticketId);
|
|
228
|
+
const triage = await sandbox.run({
|
|
205
229
|
name: `triage #${ticketId}`,
|
|
206
230
|
agent: roleAgent("routine", shipyard.CODEX_MODELS.routine),
|
|
207
231
|
maxIterations: 1,
|
|
208
232
|
promptFile: "./.shipyard/triage-prompt.md",
|
|
209
233
|
promptArgs: { TASK_ID: ticketId, BASE_BRANCH: targetBranch },
|
|
210
234
|
});
|
|
211
|
-
|
|
235
|
+
triageOutput = triage.stdout;
|
|
236
|
+
}
|
|
237
|
+
verifyTriage(ticketId, triageOutput);
|
|
212
238
|
}
|
|
213
239
|
const implement = await sandbox.run({
|
|
214
240
|
name: "implementer",
|
|
@@ -92,7 +92,7 @@ const closeClean = async (sandbox: {
|
|
|
92
92
|
if (preservedWorktreePath)
|
|
93
93
|
throw new Error(`Sandbox has uncommitted work at ${preservedWorktreePath}`);
|
|
94
94
|
};
|
|
95
|
-
const verifyTriage = (ticketId: string) => {
|
|
95
|
+
const verifyTriage = (ticketId: string, triageOutput?: string) => {
|
|
96
96
|
try {
|
|
97
97
|
execFileSync("bash", [".shipyard/verify-triage.sh", ticketId, repository], {
|
|
98
98
|
encoding: "utf8",
|
|
@@ -101,9 +101,31 @@ const verifyTriage = (ticketId: string) => {
|
|
|
101
101
|
const detail = (error as { stderr?: string | Buffer }).stderr
|
|
102
102
|
?.toString()
|
|
103
103
|
.trim();
|
|
104
|
-
|
|
104
|
+
const reason = detail || `Could not verify triage for #${ticketId}`;
|
|
105
|
+
const output = triageOutput?.trim();
|
|
106
|
+
throw new Error(
|
|
107
|
+
output ? `${reason}; triage output: ${output.slice(-1200)}` : reason,
|
|
108
|
+
);
|
|
105
109
|
}
|
|
106
110
|
};
|
|
111
|
+
const verifyTriageAccess = async (
|
|
112
|
+
sandbox: {
|
|
113
|
+
exec: (
|
|
114
|
+
command: string,
|
|
115
|
+
) => Promise<{ exitCode: number; stdout: string; stderr: string }>;
|
|
116
|
+
},
|
|
117
|
+
ticketId: string,
|
|
118
|
+
) => {
|
|
119
|
+
if (!/^\d+$/.test(ticketId))
|
|
120
|
+
throw new Error(`Invalid issue number: ${ticketId}`);
|
|
121
|
+
const result = await sandbox.exec(
|
|
122
|
+
`gh issue view ${ticketId} --repo ${repository} --json number --jq .number`,
|
|
123
|
+
);
|
|
124
|
+
if (result.exitCode !== 0)
|
|
125
|
+
throw new Error(
|
|
126
|
+
`Cannot triage issue #${ticketId}: GitHub access failed in the sandbox: ${result.stderr.trim() || result.stdout.trim() || `exit ${result.exitCode}`}`,
|
|
127
|
+
);
|
|
128
|
+
};
|
|
107
129
|
|
|
108
130
|
const blockScope = (
|
|
109
131
|
scope: { id: string; branch: string; tickets?: Array<{ id: string }> },
|
|
@@ -197,15 +219,19 @@ for (let iteration = 0; iteration < 3; iteration++) {
|
|
|
197
219
|
? issue.tickets?.find((ticket) => ticket.id === ticketId)
|
|
198
220
|
?.triageReady
|
|
199
221
|
: issue.triageReady;
|
|
200
|
-
|
|
201
|
-
|
|
222
|
+
let triageOutput: string | undefined;
|
|
223
|
+
if (!ready) {
|
|
224
|
+
await verifyTriageAccess(sandbox, ticketId);
|
|
225
|
+
const triage = await sandbox.run({
|
|
202
226
|
name: `triage #${ticketId}`,
|
|
203
227
|
agent: roleAgent("routine", shipyard.CODEX_MODELS.routine),
|
|
204
228
|
maxIterations: 1,
|
|
205
229
|
promptFile: "./.shipyard/triage-prompt.md",
|
|
206
230
|
promptArgs: { TASK_ID: ticketId, BASE_BRANCH: targetBranch },
|
|
207
231
|
});
|
|
208
|
-
|
|
232
|
+
triageOutput = triage.stdout;
|
|
233
|
+
}
|
|
234
|
+
verifyTriage(ticketId, triageOutput);
|
|
209
235
|
}
|
|
210
236
|
const result = await sandbox.run({
|
|
211
237
|
name: "implementer",
|