opencastle 0.13.0 → 0.15.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.
Files changed (52) hide show
  1. package/dist/cli/convoy/engine.d.ts +38 -0
  2. package/dist/cli/convoy/engine.d.ts.map +1 -0
  3. package/dist/cli/convoy/engine.js +416 -0
  4. package/dist/cli/convoy/engine.js.map +1 -0
  5. package/dist/cli/convoy/engine.test.d.ts +2 -0
  6. package/dist/cli/convoy/engine.test.d.ts.map +1 -0
  7. package/dist/cli/convoy/engine.test.js +1140 -0
  8. package/dist/cli/convoy/engine.test.js.map +1 -0
  9. package/dist/cli/convoy/health.d.ts +23 -0
  10. package/dist/cli/convoy/health.d.ts.map +1 -0
  11. package/dist/cli/convoy/health.js +69 -0
  12. package/dist/cli/convoy/health.js.map +1 -0
  13. package/dist/cli/convoy/health.test.d.ts +2 -0
  14. package/dist/cli/convoy/health.test.d.ts.map +1 -0
  15. package/dist/cli/convoy/health.test.js +392 -0
  16. package/dist/cli/convoy/health.test.js.map +1 -0
  17. package/dist/cli/convoy/store.d.ts +1 -0
  18. package/dist/cli/convoy/store.d.ts.map +1 -1
  19. package/dist/cli/convoy/store.js +5 -0
  20. package/dist/cli/convoy/store.js.map +1 -1
  21. package/dist/cli/run/schema.d.ts +5 -0
  22. package/dist/cli/run/schema.d.ts.map +1 -1
  23. package/dist/cli/run/schema.js +98 -143
  24. package/dist/cli/run/schema.js.map +1 -1
  25. package/dist/cli/run/schema.test.js +53 -215
  26. package/dist/cli/run/schema.test.js.map +1 -1
  27. package/dist/cli/run.d.ts.map +1 -1
  28. package/dist/cli/run.js +202 -104
  29. package/dist/cli/run.js.map +1 -1
  30. package/dist/cli/types.d.ts +2 -58
  31. package/dist/cli/types.d.ts.map +1 -1
  32. package/package.json +1 -1
  33. package/src/cli/convoy/engine.test.ts +1349 -0
  34. package/src/cli/convoy/engine.ts +521 -0
  35. package/src/cli/convoy/health.test.ts +456 -0
  36. package/src/cli/convoy/health.ts +111 -0
  37. package/src/cli/convoy/store.ts +7 -0
  38. package/src/cli/run/schema.test.ts +61 -241
  39. package/src/cli/run/schema.ts +105 -153
  40. package/src/cli/run.ts +216 -105
  41. package/src/cli/types.ts +2 -66
  42. package/src/dashboard/node_modules/.vite/deps/_metadata.json +6 -6
  43. package/dist/cli/run/loop-executor.d.ts +0 -3
  44. package/dist/cli/run/loop-executor.d.ts.map +0 -1
  45. package/dist/cli/run/loop-executor.js +0 -155
  46. package/dist/cli/run/loop-executor.js.map +0 -1
  47. package/dist/cli/run/loop-reporter.d.ts +0 -6
  48. package/dist/cli/run/loop-reporter.d.ts.map +0 -1
  49. package/dist/cli/run/loop-reporter.js +0 -112
  50. package/dist/cli/run/loop-reporter.js.map +0 -1
  51. package/src/cli/run/loop-executor.ts +0 -199
  52. package/src/cli/run/loop-reporter.ts +0 -125
package/src/cli/run.ts CHANGED
@@ -1,26 +1,28 @@
1
1
  import { readFile } from 'node:fs/promises'
2
+ import { existsSync } from 'node:fs'
2
3
  import { resolve } from 'node:path'
3
- import { parseTaskSpec } from './run/schema.js'
4
+ import { parseTaskSpecText, isConvoySpec } from './run/schema.js'
4
5
  import { createExecutor, buildPhases } from './run/executor.js'
5
6
  import { getAdapter, detectAdapter } from './run/adapters/index.js'
6
7
  import { createReporter, printExecutionPlan } from './run/reporter.js'
7
8
  import type { CliContext, RunOptions } from './types.js'
9
+ import type { ConvoyResult } from './convoy/engine.js'
8
10
 
9
11
  const HELP = `
10
12
  opencastle run [options]
11
13
 
12
14
  Process a task queue from a spec file, delegating to AI agents autonomously.
13
- Supports two modes: tasks (default phase-based execution) and loop (iterative Ralph Loop).
15
+ Version 1 specs use the Convoy Engine; legacy specs use the standard executor.
14
16
 
15
17
  Options:
16
18
  --file, -f <path> Task spec file (default: opencastle.tasks.yml)
17
19
  --dry-run Show execution plan without running
18
- --concurrency, -c <n> Override max parallel tasks (tasks mode)
20
+ --concurrency, -c <n> Override max parallel tasks
19
21
  --adapter, -a <name> Override agent runtime adapter
20
22
  --report-dir <path> Where to write run reports (default: .opencastle/runs)
21
23
  --verbose Show full agent output
22
- --mode <name> Execution mode: tasks | loop
23
- --max-iterations <n> Override max loop iterations (loop mode)
24
+ --resume Resume the last interrupted convoy from .opencastle/convoy.db
25
+ --status Print the current convoy state from .opencastle/convoy.db
24
26
  --help, -h Show this help
25
27
  `
26
28
 
@@ -36,8 +38,8 @@ function parseArgs(args: string[]): RunOptions {
36
38
  reportDir: null,
37
39
  verbose: false,
38
40
  help: false,
39
- maxIterations: null,
40
- mode: null,
41
+ resume: false,
42
+ status: false,
41
43
  }
42
44
 
43
45
  for (let i = 0; i < args.length; i++) {
@@ -79,26 +81,12 @@ function parseArgs(args: string[]): RunOptions {
79
81
  case '--verbose':
80
82
  opts.verbose = true
81
83
  break
82
- case '--max-iterations': {
83
- if (i + 1 >= args.length) { console.error(' \u2717 --max-iterations requires a number'); process.exit(1) }
84
- const val = parseInt(args[++i], 10)
85
- if (!Number.isFinite(val) || val < 1) {
86
- console.error(` \u2717 --max-iterations must be an integer >= 1`)
87
- process.exit(1)
88
- }
89
- opts.maxIterations = val
84
+ case '--resume':
85
+ opts.resume = true
90
86
  break
91
- }
92
- case '--mode': {
93
- if (i + 1 >= args.length) { console.error(' \u2717 --mode requires a name'); process.exit(1) }
94
- const modeVal = args[++i]
95
- if (modeVal !== 'tasks' && modeVal !== 'loop') {
96
- console.error(` \u2717 --mode must be one of: tasks, loop`)
97
- process.exit(1)
98
- }
99
- opts.mode = modeVal
87
+ case '--status':
88
+ opts.status = true
100
89
  break
101
- }
102
90
  default:
103
91
  console.error(` ✗ Unknown option: ${arg}`)
104
92
  console.log(HELP)
@@ -109,6 +97,61 @@ function parseArgs(args: string[]): RunOptions {
109
97
  return opts
110
98
  }
111
99
 
100
+ /**
101
+ * Print a user-friendly adapter unavailable error.
102
+ */
103
+ function printAdapterError(detectionFailed: boolean, adapterName: string): void {
104
+ if (detectionFailed) {
105
+ console.error(
106
+ ` ✗ No agent CLI found on your PATH.\n` +
107
+ ` Install one of the following adapters:\n` +
108
+ ` • copilot — https://docs.github.com/en/copilot/how-tos/set-up/install-copilot-cli\n` +
109
+ ` • claude — npm install -g @anthropic-ai/claude-code\n` +
110
+ ` • cursor — https://cursor.com (Cursor > Install CLI)\n` +
111
+ `\n` +
112
+ ` Or specify an adapter explicitly: opencastle run --adapter <name>`
113
+ )
114
+ } else {
115
+ const hints: Record<string, string> = {
116
+ 'claude-code':
117
+ ' Install: npm install -g @anthropic-ai/claude-code\n' +
118
+ ' Docs: https://docs.anthropic.com/en/docs/claude-code',
119
+ copilot:
120
+ ' Requires the Copilot CLI installed and authenticated:\n' +
121
+ ' https://docs.github.com/en/copilot/how-tos/set-up/install-copilot-cli\n' +
122
+ ' Docs: https://docs.github.com/en/copilot',
123
+ cursor:
124
+ ' The Cursor agent CLI ships with the Cursor editor.\n' +
125
+ ' Install Cursor from https://cursor.com and ensure the\n' +
126
+ ' "agent" command is on your PATH (Cursor > Install CLI).',
127
+ }
128
+ const cliName = adapterName === 'claude-code' ? 'claude' : adapterName
129
+ const hint = hints[adapterName] ?? ''
130
+ console.error(
131
+ ` ✗ Adapter "${adapterName}" is not available.\n` +
132
+ ` Make sure the "${cliName}" CLI is installed and on your PATH.\n` +
133
+ hint
134
+ )
135
+ }
136
+ }
137
+
138
+ /**
139
+ * Print a convoy result summary.
140
+ */
141
+ function printConvoyResult(result: ConvoyResult): void {
142
+ console.log(`\n ──────────────────────────────────────`)
143
+ console.log(` Convoy ${result.status}: ${result.duration}`)
144
+ console.log(
145
+ ` Done: ${result.summary.done} | Failed: ${result.summary.failed} | Skipped: ${result.summary.skipped} | Timed out: ${result.summary.timedOut}`
146
+ )
147
+ if (result.gateResults) {
148
+ console.log(` Gates:`)
149
+ for (const g of result.gateResults) {
150
+ console.log(` ${g.passed ? '✓' : '✗'} ${g.command}`)
151
+ }
152
+ }
153
+ }
154
+
112
155
  /**
113
156
  * CLI entry point for the `run` command.
114
157
  */
@@ -120,15 +163,133 @@ export default async function run({ args }: CliContext): Promise<void> {
120
163
  return
121
164
  }
122
165
 
166
+ const dbPath = resolve(process.cwd(), '.opencastle', 'convoy.db')
167
+
168
+ // ── --status flag ─────────────────────────────────────────────
169
+ if (opts.status) {
170
+ if (!existsSync(dbPath)) {
171
+ console.log(' No convoy database found at .opencastle/convoy.db')
172
+ return
173
+ }
174
+ const { createConvoyStore } = await import('./convoy/store.js')
175
+ const store = createConvoyStore(dbPath)
176
+ try {
177
+ const convoy = store.getLatestConvoy()
178
+ if (!convoy) {
179
+ console.log(' No convoy records found.')
180
+ return
181
+ }
182
+ const tasks = store.getTasksByConvoy(convoy.id)
183
+ const byStatus = tasks.reduce((acc, t) => {
184
+ acc[t.status] = (acc[t.status] ?? 0) + 1
185
+ return acc
186
+ }, {} as Record<string, number>)
187
+ console.log(`\n Convoy: ${convoy.name}`)
188
+ console.log(` ID: ${convoy.id}`)
189
+ console.log(` Status: ${convoy.status}`)
190
+ console.log(` Branch: ${convoy.branch ?? '(none)'}`)
191
+ console.log(` Created: ${convoy.created_at}`)
192
+ if (convoy.started_at) console.log(` Started: ${convoy.started_at}`)
193
+ if (convoy.finished_at) console.log(` Finished: ${convoy.finished_at}`)
194
+ console.log(`\n Tasks:`)
195
+ for (const [status, count] of Object.entries(byStatus)) {
196
+ console.log(` ${status}: ${count}`)
197
+ }
198
+ console.log(` total: ${tasks.length}`)
199
+ } finally {
200
+ store.close()
201
+ }
202
+ return
203
+ }
204
+
205
+ // ── --resume flag ─────────────────────────────────────────────
206
+ if (opts.resume) {
207
+ if (!existsSync(dbPath)) {
208
+ console.error(' ✗ No convoy database found at .opencastle/convoy.db')
209
+ console.error(' Run a convoy spec first: opencastle run convoy.yml')
210
+ process.exit(1)
211
+ }
212
+ const { createConvoyStore } = await import('./convoy/store.js')
213
+ const store = createConvoyStore(dbPath)
214
+ const convoy = store.getLatestConvoy()
215
+ store.close()
216
+ if (!convoy) {
217
+ console.error(' ✗ No convoy records found in .opencastle/convoy.db')
218
+ process.exit(1)
219
+ }
220
+ if (convoy.status === 'done' || convoy.status === 'failed') {
221
+ console.error(
222
+ ` ✗ Last convoy "${convoy.name}" already finished with status: ${convoy.status}`
223
+ )
224
+ console.error(` Only interrupted (running/pending) convoys can be resumed.`)
225
+ process.exit(1)
226
+ }
227
+
228
+ const resumeSpec = parseTaskSpecText(convoy.spec_yaml)
229
+ if (opts.concurrency !== null) resumeSpec.concurrency = opts.concurrency
230
+ if (opts.adapter !== null) resumeSpec.adapter = opts.adapter
231
+ if (opts.verbose) resumeSpec._verbose = true
232
+
233
+ let resumeDetectionFailed = false
234
+ if (!resumeSpec.adapter) {
235
+ const detected = await detectAdapter()
236
+ if (detected) {
237
+ resumeSpec.adapter = detected
238
+ console.log(` ℹ Auto-detected adapter: ${detected}`)
239
+ } else {
240
+ resumeDetectionFailed = true
241
+ resumeSpec.adapter = 'claude-code'
242
+ }
243
+ }
244
+
245
+ const resumeAdapter = await getAdapter(resumeSpec.adapter)
246
+ const resumeAvailable = await resumeAdapter.isAvailable()
247
+ if (!resumeAvailable) {
248
+ printAdapterError(resumeDetectionFailed, resumeSpec.adapter)
249
+ process.exit(1)
250
+ }
251
+
252
+ console.log(`\n \uD83C\uDFF0 OpenCastle Convoy (Resume): ${convoy.name}`)
253
+ console.log(` Convoy ID: ${convoy.id}`)
254
+ const { createConvoyEngine } = await import('./convoy/engine.js')
255
+ const resumeEngine = createConvoyEngine({
256
+ spec: resumeSpec,
257
+ specYaml: convoy.spec_yaml,
258
+ adapter: resumeAdapter,
259
+ verbose: opts.verbose,
260
+ })
261
+ const resumeResult = await resumeEngine.resume(convoy.id)
262
+ printConvoyResult(resumeResult)
263
+ process.exit(resumeResult.status !== 'done' ? 1 : 0)
264
+ }
265
+
123
266
  // ── Read and validate spec ────────────────────────────────────
124
267
  const specPath = resolve(process.cwd(), opts.file)
125
- const spec = await parseTaskSpec(specPath)
268
+ let specText = ''
269
+ try {
270
+ specText = await readFile(specPath, 'utf8')
271
+ } catch (err: unknown) {
272
+ const e = err as Error & { code?: string }
273
+ if (e.code === 'ENOENT') {
274
+ console.error(` ✗ Task spec file not found: ${opts.file}`)
275
+ } else {
276
+ console.error(` ✗ Cannot read task spec file: ${e.message}`)
277
+ }
278
+ process.exit(1)
279
+ }
280
+
281
+ let spec
282
+ try {
283
+ spec = parseTaskSpecText(specText)
284
+ } catch (err: unknown) {
285
+ console.error(` ✗ ${(err as Error).message}`)
286
+ process.exit(1)
287
+ }
126
288
 
127
289
  // Apply CLI overrides
128
290
  if (opts.concurrency !== null) spec.concurrency = opts.concurrency
129
291
  if (opts.adapter !== null) spec.adapter = opts.adapter
130
292
  if (opts.verbose) spec._verbose = true
131
- if (opts.mode !== null) spec.mode = opts.mode as 'tasks' | 'loop'
132
293
 
133
294
  // ── Auto-detect adapter if not specified ─────────────────────
134
295
  let detectionFailed = false
@@ -145,22 +306,13 @@ export default async function run({ args }: CliContext): Promise<void> {
145
306
 
146
307
  // ── Dry run ──────────────────────────────────────────────────
147
308
  if (opts.dryRun) {
148
- if (spec.mode === 'loop') {
149
- const loop = spec.loop!
150
- console.log(`\n \uD83C\uDFF0 Loop Plan: ${spec.name}`)
151
- console.log(` Mode: loop`)
152
- console.log(` Prompt: ${loop.prompt}`)
153
- console.log(` Max iterations: ${loop.max_iterations}`)
154
- console.log(` Timeout: ${loop.timeout}`)
155
- if (loop.plan_file) console.log(` Plan file: ${loop.plan_file}`)
156
- if (loop.model) console.log(` Model: ${loop.model}`)
157
- if (loop.backpressure?.length) {
158
- console.log(` Backpressure:`)
159
- for (const cmd of loop.backpressure) {
160
- console.log(` - ${cmd}`)
161
- }
162
- }
163
- return
309
+ if (isConvoySpec(spec)) {
310
+ console.log(`\n \uD83C\uDFF0 Convoy Plan: ${spec.name}`)
311
+ console.log(
312
+ ` Adapter: ${spec.adapter} | Concurrency: ${spec.concurrency} | Tasks: ${spec.tasks!.length}`
313
+ )
314
+ if (spec.branch) console.log(` Branch: ${spec.branch}`)
315
+ if (spec.gates?.length) console.log(` Gates: ${spec.gates.length} validation commands`)
164
316
  }
165
317
  const phases = buildPhases(spec.tasks!)
166
318
  printExecutionPlan(spec, phases)
@@ -171,78 +323,37 @@ export default async function run({ args }: CliContext): Promise<void> {
171
323
  const adapter = await getAdapter(spec.adapter)
172
324
  const available = await adapter.isAvailable()
173
325
  if (!available) {
174
- if (detectionFailed) {
175
- console.error(
176
- ` ✗ No agent CLI found on your PATH.\n` +
177
- ` Install one of the following adapters:\n` +
178
- ` • copilot — https://docs.github.com/en/copilot/how-tos/set-up/install-copilot-cli\n` +
179
- ` • claude — npm install -g @anthropic-ai/claude-code\n` +
180
- ` • cursor — https://cursor.com (Cursor > Install CLI)\n` +
181
- `\n` +
182
- ` Or specify an adapter explicitly: opencastle run --adapter <name>`
183
- )
184
- } else {
185
- const hints: Record<string, string> = {
186
- 'claude-code':
187
- ' Install: npm install -g @anthropic-ai/claude-code\n' +
188
- ' Docs: https://docs.anthropic.com/en/docs/claude-code',
189
- copilot:
190
- ' Requires the Copilot CLI installed and authenticated:\n' +
191
- ' https://docs.github.com/en/copilot/how-tos/set-up/install-copilot-cli\n' +
192
- ' Docs: https://docs.github.com/en/copilot',
193
- cursor:
194
- ' The Cursor agent CLI ships with the Cursor editor.\n' +
195
- ' Install Cursor from https://cursor.com and ensure the\n' +
196
- ' "agent" command is on your PATH (Cursor > Install CLI).',
197
- }
198
- const cliName = spec.adapter === 'claude-code' ? 'claude' : spec.adapter
199
- const hint = hints[spec.adapter] ?? ''
200
- console.error(
201
- ` ✗ Adapter "${spec.adapter}" is not available.\n` +
202
- ` Make sure the "${cliName}" CLI is installed and on your PATH.\n` +
203
- hint
204
- )
205
- }
326
+ printAdapterError(detectionFailed, spec.adapter)
206
327
  process.exit(1)
207
328
  }
208
329
 
209
- // ── Execute ──────────────────────────────────────────────────
210
- if (spec.mode === 'loop') {
211
- const { createLoopExecutor } = await import('./run/loop-executor.js')
212
- const { createLoopReporter } = await import('./run/loop-reporter.js')
330
+ // ── Convoy engine path (version: 1 specs) ────────────────────
331
+ if (isConvoySpec(spec)) {
332
+ const { createConvoyEngine } = await import('./convoy/engine.js')
333
+ console.log(`\n \uD83C\uDFF0 OpenCastle Convoy: ${spec.name}`)
334
+ console.log(
335
+ ` Adapter: ${adapter.name} | Concurrency: ${spec.concurrency} | Tasks: ${spec.tasks!.length}`
336
+ )
337
+ if (spec.branch) console.log(` Branch: ${spec.branch}`)
338
+ if (spec.gates?.length) console.log(` Gates: ${spec.gates.length} validation commands`)
213
339
 
214
- if (opts.maxIterations !== null && spec.loop) {
215
- spec.loop.max_iterations = opts.maxIterations
216
- }
217
-
218
- const promptPath = resolve(process.cwd(), spec.loop!.prompt)
219
- try {
220
- await readFile(promptPath)
221
- } catch {
222
- console.error(` \u2717 Prompt file not found: ${spec.loop!.prompt}`)
223
- process.exit(1)
224
- }
225
-
226
- console.log(`\n \uD83C\uDFF0 OpenCastle Loop: ${spec.name}`)
227
- console.log(` Adapter: ${adapter.name} | Max iterations: ${spec.loop!.max_iterations} | Timeout: ${spec.loop!.timeout}`)
228
- if (spec.loop!.backpressure?.length) {
229
- console.log(` Backpressure: ${spec.loop!.backpressure.join(', ')}`)
230
- }
231
-
232
- const loopReporter = createLoopReporter(spec.name, {
233
- reportDir: opts.reportDir ? resolve(process.cwd(), opts.reportDir) : undefined,
340
+ const engine = createConvoyEngine({
341
+ spec,
342
+ specYaml: specText,
343
+ adapter,
234
344
  verbose: opts.verbose,
235
345
  })
236
346
 
237
- const loopExecutor = createLoopExecutor(spec, adapter, loopReporter)
238
- const loopReport = await loopExecutor.run()
239
-
240
- const failed = loopReport.stoppedReason === 'error' || loopReport.stoppedReason === 'backpressure-fail'
241
- process.exit(failed ? 1 : 0)
347
+ const result = await engine.run()
348
+ printConvoyResult(result)
349
+ process.exit(result.status !== 'done' ? 1 : 0)
242
350
  }
243
351
 
352
+ // ── Legacy executor path ──────────────────────────────────────
244
353
  console.log(`\n \uD83C\uDFF0 OpenCastle Run: ${spec.name}`)
245
- console.log(` Adapter: ${adapter.name} | Concurrency: ${spec.concurrency} | Tasks: ${spec.tasks!.length}`)
354
+ console.log(
355
+ ` Adapter: ${adapter.name} | Concurrency: ${spec.concurrency} | Tasks: ${spec.tasks!.length}`
356
+ )
246
357
 
247
358
  const reporter = createReporter(spec, {
248
359
  reportDir: opts.reportDir
package/src/cli/types.ts CHANGED
@@ -134,22 +134,6 @@ export interface TaskDefaults {
134
134
  agent?: string;
135
135
  }
136
136
 
137
- /** Loop execution configuration. */
138
- export interface LoopConfig {
139
- /** Maximum number of agent iterations (default 20). */
140
- max_iterations: number;
141
- /** Path to the prompt file read each iteration. */
142
- prompt: string;
143
- /** Path to the plan file (default 'IMPLEMENTATION_PLAN.md'). */
144
- plan_file?: string;
145
- /** Per-iteration timeout (default '10m'). */
146
- timeout: string;
147
- /** Model override for loop sessions. */
148
- model?: string;
149
- /** Shell commands that must exit 0 after each iteration. */
150
- backpressure?: string[];
151
- }
152
-
153
137
  /** Validated task spec from YAML. */
154
138
  export interface TaskSpec {
155
139
  name: string;
@@ -157,8 +141,6 @@ export interface TaskSpec {
157
141
  on_failure: 'continue' | 'stop';
158
142
  adapter: string;
159
143
  tasks?: Task[];
160
- mode?: 'tasks' | 'loop';
161
- loop?: LoopConfig;
162
144
  _verbose?: boolean;
163
145
  /** Spec schema version (1 for Convoy Engine format). */
164
146
  version?: number;
@@ -271,8 +253,8 @@ export interface RunOptions {
271
253
  reportDir: string | null;
272
254
  verbose: boolean;
273
255
  help: boolean;
274
- maxIterations: number | null;
275
- mode: string | null;
256
+ resume: boolean;
257
+ status: boolean;
276
258
  }
277
259
 
278
260
  /** Validation result. */
@@ -292,49 +274,3 @@ export interface Executor {
292
274
  run(): Promise<RunReport>;
293
275
  getPhases(): Task[][];
294
276
  }
295
-
296
- // ── Loop executor types ────────────────────────────────────────
297
-
298
- /** Result of a single backpressure command run. */
299
- export interface BackpressureResult {
300
- command: string;
301
- exitCode: number;
302
- output: string;
303
- passed: boolean;
304
- }
305
-
306
- /** Result of a single loop iteration. */
307
- export interface LoopIterationResult {
308
- iteration: number;
309
- status: 'done' | 'failed' | 'backpressure-fail';
310
- duration: number;
311
- output: string;
312
- backpressureResults?: BackpressureResult[];
313
- }
314
-
315
- /** Final report produced by the loop executor. */
316
- export interface LoopRunReport {
317
- name: string;
318
- mode: 'loop';
319
- startedAt: string;
320
- completedAt: string;
321
- duration: string;
322
- totalIterations: number;
323
- completedIterations: number;
324
- stoppedReason: 'max-iterations' | 'plan-empty' | 'backpressure-fail' | 'user-abort' | 'error';
325
- iterations: LoopIterationResult[];
326
- }
327
-
328
- /** Reporter interface for loop execution progress. */
329
- export interface LoopReporter {
330
- onIterationStart(iteration: number, maxIterations: number): void;
331
- onIterationDone(iteration: number, result: LoopIterationResult): void;
332
- onBackpressureStart(command: string): void;
333
- onBackpressureResult(result: BackpressureResult): void;
334
- onComplete(report: LoopRunReport): Promise<void>;
335
- }
336
-
337
- /** Executor for loop-mode run specs. */
338
- export interface LoopExecutor {
339
- run(): Promise<LoopRunReport>;
340
- }
@@ -1,25 +1,25 @@
1
1
  {
2
- "hash": "dbad714b",
2
+ "hash": "4d31b055",
3
3
  "configHash": "30f8ea04",
4
- "lockfileHash": "9d0a6f07",
5
- "browserHash": "5a0dab56",
4
+ "lockfileHash": "cbad4e8c",
5
+ "browserHash": "a1ae7e1e",
6
6
  "optimized": {
7
7
  "astro > cssesc": {
8
8
  "src": "../../../../../node_modules/cssesc/cssesc.js",
9
9
  "file": "astro___cssesc.js",
10
- "fileHash": "8d350b6c",
10
+ "fileHash": "4a8534ee",
11
11
  "needsInterop": true
12
12
  },
13
13
  "astro > aria-query": {
14
14
  "src": "../../../../../node_modules/aria-query/lib/index.js",
15
15
  "file": "astro___aria-query.js",
16
- "fileHash": "218ef9a3",
16
+ "fileHash": "85939f45",
17
17
  "needsInterop": true
18
18
  },
19
19
  "astro > axobject-query": {
20
20
  "src": "../../../../../node_modules/axobject-query/lib/index.js",
21
21
  "file": "astro___axobject-query.js",
22
- "fileHash": "5fceb305",
22
+ "fileHash": "dc75bd79",
23
23
  "needsInterop": true
24
24
  }
25
25
  },
@@ -1,3 +0,0 @@
1
- import type { TaskSpec, AgentAdapter, LoopReporter, LoopExecutor } from '../types.js';
2
- export declare function createLoopExecutor(spec: TaskSpec, adapter: AgentAdapter, reporter: LoopReporter): LoopExecutor;
3
- //# sourceMappingURL=loop-executor.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"loop-executor.d.ts","sourceRoot":"","sources":["../../../src/cli/run/loop-executor.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACV,QAAQ,EAIR,YAAY,EACZ,YAAY,EACZ,YAAY,EAEb,MAAM,aAAa,CAAA;AA2DpB,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,QAAQ,EACd,OAAO,EAAE,YAAY,EACrB,QAAQ,EAAE,YAAY,GACrB,YAAY,CAwHd"}