nightralph 0.0.25 → 0.0.29
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 +4 -0
- package/dist/display.js +4 -2
- package/dist/display.js.map +2 -2
- package/dist/index.js +92 -35
- package/dist/index.js.map +2 -2
- package/dist/meta.json +8 -8
- package/dist/orchestrator.js +78 -25
- package/dist/orchestrator.js.map +2 -2
- package/dist/src/display.d.ts.map +1 -1
- package/dist/src/orchestrator.d.ts +14 -8
- package/dist/src/orchestrator.d.ts.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -130,6 +130,9 @@ npx nightralph claude -m claude-opus-4-6 --spec my-feature
|
|
|
130
130
|
# Cap each agent at 30 agentic turns
|
|
131
131
|
npx nightralph claude -m claude-opus-4-6 --max-turns 30
|
|
132
132
|
|
|
133
|
+
# Same thing, using the positional shorthand
|
|
134
|
+
npx nightralph claude -m claude-opus-4-6 30
|
|
135
|
+
|
|
133
136
|
# Start pi at medium thinking and allow one escalated retry
|
|
134
137
|
npx nightralph pi -m openrouter/z-ai/glm-5.3-flash --thinking medium --retries 1
|
|
135
138
|
```
|
|
@@ -145,6 +148,7 @@ Flags:
|
|
|
145
148
|
| Flag | Required | Default | Description |
|
|
146
149
|
|------|----------|---------|-------------|
|
|
147
150
|
| `<provider>` | yes | -- | Agent harness to spawn (e.g. `claude`, `codex`, `pi`) |
|
|
151
|
+
| `[turns]` | no | -- | Max agentic turns per ticket (positional shorthand for `--max-turns`) |
|
|
148
152
|
| `-m`, `--model` | no | -- | Model flag passed to the agent |
|
|
149
153
|
| `--spec <name>` | no | auto | Feature name (resolves `.scratch/<name>/`) |
|
|
150
154
|
| `--dry-run` | no | -- | Show wave order and prompt without running |
|
package/dist/display.js
CHANGED
|
@@ -144,8 +144,10 @@ class StatusDisplay {
|
|
|
144
144
|
}
|
|
145
145
|
}
|
|
146
146
|
log(message) {
|
|
147
|
-
|
|
148
|
-
|
|
147
|
+
for (const line of message.split("\n")) {
|
|
148
|
+
this.messages.push(line);
|
|
149
|
+
}
|
|
150
|
+
while (this.messages.length > MAX_MESSAGE_BUFFER) {
|
|
149
151
|
this.messages.shift();
|
|
150
152
|
}
|
|
151
153
|
this.render();
|
package/dist/display.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/display.ts"],
|
|
4
|
-
"sourcesContent": ["export type TicketStatus =\n | 'pending'\n | 'in-progress'\n | 'done'\n | 'failed'\n\nexport type DisplayTicket = {\n readonly num:number\n readonly filename:string\n status:TicketStatus\n}\n\nexport type WaveState = {\n readonly waveNum:number\n readonly totalWaves:number\n readonly tickets:Array<DisplayTicket>\n}\n\nexport type DisplayConfig = {\n readonly stream:NodeJS.WriteStream\n}\n\n/**\n * Progress across the whole run, not just the current wave.\n * `completed` counts tickets done before the wave started;\n * tickets that finish during the wave are added on top.\n */\nexport type RunProgress = {\n readonly completed:number\n readonly total:number\n}\n\nexport type Display = {\n startWave(\n waveNum:number,\n totalWaves:number,\n tickets:ReadonlyArray<{\n num:number\n filename:string\n }>,\n progress?:RunProgress,\n remaining?:ReadonlyArray<{\n num:number\n filename:string\n }>,\n ):void\n setTicketStatus(num:number, status:TicketStatus):void\n log(message:string):void\n cleanup():void\n}\n\nconst ESC = '\\x1b['\n\nexport const ansi = {\n cursorUp (n:number):string {\n return `${ESC}${n}A`\n },\n cursorTo (col:number):string {\n return `${ESC}${col}G`\n },\n eraseLine ():string {\n return `${ESC}2K`\n },\n eraseLines (n:number):string {\n let out = ''\n for (let i = 0; i < n; i++) {\n out += ansi.eraseLine()\n if (i < n - 1) out += ansi.cursorUp(1)\n }\n return out\n },\n cursorHide ():string {\n return `${ESC}?25l`\n },\n cursorShow ():string {\n return `${ESC}?25h`\n },\n} as const\n\nexport const color = {\n green (text:string):string {\n return `${ESC}32m${text}${ESC}0m`\n },\n yellow (text:string):string {\n return `${ESC}33m${text}${ESC}0m`\n },\n red (text:string):string {\n return `${ESC}31m${text}${ESC}0m`\n },\n dim (text:string):string {\n return `${ESC}2m${text}${ESC}0m`\n },\n} as const\n\nconst FILLED_DOT = '\u25CF'\nconst EMPTY_DOT = '\u25CB'\nconst SEPARATOR_WIDTH = 50\nconst DEFAULT_COLUMNS = 80\nconst MIN_LOG_ROWS = 1\nconst MAX_MESSAGE_BUFFER = 200\n\n// eslint-disable-next-line no-control-regex\nconst ANSI_RE = /\\x1b\\[[0-9;?]*[A-Za-z]/g\n\n/**\n * Clamp a line to `width` visible columns so it can never wrap.\n * A wrapped line occupies more terminal rows than `render`\n * counts, which leaves stale rows behind on the next erase.\n */\nexport function fitLine (line:string, width:number):string {\n const visible = line.replace(ANSI_RE, '')\n if (visible.length <= width) return line\n return visible.slice(0, width)\n}\n\nexport function renderDot (status:TicketStatus):string {\n switch (status) {\n case 'pending':\n return color.dim(EMPTY_DOT)\n case 'in-progress':\n return color.yellow(FILLED_DOT)\n case 'done':\n return color.green(FILLED_DOT)\n case 'failed':\n return color.red(FILLED_DOT)\n }\n}\n\nexport class StatusDisplay implements Display {\n private wave:WaveState|null = null\n private progress:RunProgress|null = null\n private remaining:Array<DisplayTicket> = []\n private messages:Array<string> = []\n private renderedLines:number = 0\n private config:DisplayConfig\n private viewportStart:number = 0\n private resizeListener:(() => void)|null = null\n\n constructor (config?:Readonly<Partial<DisplayConfig>>) {\n this.config = {\n stream:config?.stream ?? process.stdout,\n }\n }\n\n private getAvailableRows ():number {\n const termHeight = this.config.stream.rows ?? 24\n const overhead = 2 + MIN_LOG_ROWS\n const available = termHeight - overhead\n return Math.max(1, available)\n }\n\n startWave (\n waveNum:number,\n totalWaves:number,\n tickets:Array<Readonly<{ num:number; filename:string }>>,\n progress?:RunProgress,\n remaining?:ReadonlyArray<{\n num:number\n filename:string\n }>,\n ):void {\n this.wave = {\n waveNum,\n totalWaves,\n tickets:tickets.map(t => ({\n ...t,\n status:'pending' as const,\n })),\n }\n this.remaining = (remaining ?? []).map(t => ({\n ...t,\n status:'pending' as const,\n }))\n this.progress = progress ?? null\n this.messages = []\n this.viewportStart = 0\n this.config.stream.write(ansi.cursorHide())\n\n if (this.config.stream.isTTY) {\n if (this.resizeListener) {\n this.config.stream.removeListener(\n 'resize', this.resizeListener,\n )\n }\n this.resizeListener = () => this.render()\n this.config.stream.on(\n 'resize', this.resizeListener,\n )\n }\n\n this.render()\n }\n\n setTicketStatus (num:number, status:TicketStatus):void {\n if (!this.wave) return\n\n const ticket = this.wave.tickets.find(t => t.num === num)\n if (ticket) {\n ticket.status = status\n\n if (status === 'in-progress') {\n const ticketIndex = this.wave.tickets.indexOf(ticket)\n const availableRows = this.getAvailableRows()\n const totalTickets = this.wave.tickets.length\n\n if (ticketIndex < this.viewportStart) {\n this.viewportStart = ticketIndex\n } else if (ticketIndex >= this.viewportStart + availableRows) {\n // offset by 3 to keep context visible below\n // the active ticket\n this.viewportStart = ticketIndex - availableRows + 3\n }\n this.viewportStart = Math.max(\n 0,\n Math.min(this.viewportStart, totalTickets - 1),\n )\n }\n\n this.render()\n }\n }\n\n log (message:string):void {\n this.messages.push(message)\n if (this.messages.length > MAX_MESSAGE_BUFFER) {\n this.messages.shift()\n }\n this.render()\n }\n\n cleanup ():void {\n // Remove resize listener if it exists\n if (this.resizeListener && this.config.stream.isTTY) {\n this.config.stream.removeListener('resize', this.resizeListener)\n }\n this.config.stream.write(ansi.cursorShow())\n }\n\n private render ():void {\n if (!this.wave) return\n\n let output = ''\n\n if (this.renderedLines > 0) {\n output += ansi.eraseLines(this.renderedLines + 1)\n }\n\n const doneInWave = this.wave.tickets.filter(\n t => t.status === 'done',\n ).length\n const completed = doneInWave + (this.progress?.completed ?? 0)\n const total = this.progress?.total ?? this.wave.tickets.length\n const pct = total === 0 ? 0 : Math.round((completed / total) * 100)\n const width = Math.max(\n 1,\n (this.config.stream.columns ?? DEFAULT_COLUMNS) - 1,\n )\n\n output += `Wave ${this.wave.waveNum}/` +\n `${this.wave.totalWaves} - ` +\n `${pct}% complete\\n`\n\n // Pagination logic\n const availableRows = this.getAvailableRows()\n const tickets:ReadonlyArray<DisplayTicket> = [\n ...this.wave.tickets,\n ...this.remaining,\n ]\n let renderedTicketCount = 0\n\n if (tickets.length <= availableRows) {\n // All tickets fit, no pagination\n for (const ticket of tickets) {\n const dot = renderDot(ticket.status)\n const name = fitLine(ticket.filename, width - 2)\n output += `${dot} ${name}\\n`\n }\n renderedTicketCount = tickets.length\n } else {\n // Need pagination -- iterative clamp\n this.viewportStart = Math.max(\n 0,\n Math.min(\n this.viewportStart,\n tickets.length - 1,\n ),\n )\n\n let hasMoreAbove = this.viewportStart > 0\n let tentativeCount =\n availableRows - (hasMoreAbove ? 1 : 0)\n let hasMoreBelow =\n this.viewportStart + tentativeCount <\n tickets.length\n let ticketsToShow = Math.max(\n 0,\n tentativeCount - (hasMoreBelow ? 1 : 0),\n )\n\n const maxStart = Math.max(\n 0,\n tickets.length - Math.max(1, ticketsToShow),\n )\n if (this.viewportStart > maxStart) {\n this.viewportStart = maxStart\n hasMoreAbove = this.viewportStart > 0\n tentativeCount = availableRows - (hasMoreAbove ? 1 : 0)\n hasMoreBelow = (\n this.viewportStart +\n tentativeCount < tickets.length\n )\n ticketsToShow = Math.max(\n 0,\n tentativeCount - (hasMoreBelow ? 1 : 0)\n )\n }\n\n // Show \"more above\" indicator if needed\n if (hasMoreAbove) {\n const moreCount = this.viewportStart\n output += ` ... ${moreCount} more above\\n`\n }\n\n // Get visible tickets and render them\n const visibleTickets = tickets.slice(\n this.viewportStart,\n this.viewportStart + ticketsToShow,\n )\n\n for (const ticket of visibleTickets) {\n const dot = renderDot(ticket.status)\n const name = fitLine(ticket.filename, width - 2)\n output += `${dot} ${name}\\n`\n }\n\n // Show \"more below\" indicator if needed\n if (hasMoreBelow) {\n const moreCount = (\n tickets.length -\n (this.viewportStart + ticketsToShow)\n )\n output += ` ... ${moreCount} more below\\n`\n }\n\n renderedTicketCount = (hasMoreAbove ? 1 : 0) +\n visibleTickets.length +\n (hasMoreBelow ? 1 : 0)\n }\n\n output += '-'.repeat(Math.min(SEPARATOR_WIDTH, width)) + '\\n'\n\n const termHeight = this.config.stream.rows ?? 24\n const logRows = Math.max(\n 0,\n termHeight - 3 - renderedTicketCount,\n )\n const visibleMessages = this.messages.slice(-logRows)\n for (const msg of visibleMessages) {\n output += `${fitLine(msg, width)}\\n`\n }\n const emptyRows = logRows - visibleMessages.length\n for (let i = 0; i < emptyRows; i++) {\n output += '\\n'\n }\n\n this.renderedLines = 2 + renderedTicketCount + logRows\n this.config.stream.write(output)\n }\n}\n\nexport class PlainDisplay implements Display {\n startWave (\n waveNum:number,\n _totalWaves:number,\n tickets:ReadonlyArray<{ num:number; filename:string }>,\n progress?:RunProgress,\n _remaining?:ReadonlyArray<{\n num:number\n filename:string\n }>,\n ):void {\n const pct = progress && progress.total > 0 ?\n ` (${progress.completed}/${progress.total} done)` :\n ''\n console.log(\n `\\nWave ${waveNum}${pct}: ` +\n tickets.map(t => t.filename).join(', ')\n )\n }\n\n setTicketStatus (_num:number, _status:TicketStatus):void {\n // no-op: plain mode reports via log()\n }\n\n log (message:string):void {\n console.log(message)\n }\n\n cleanup ():void {\n // no-op\n }\n}\n\nexport function createDisplay (stream?:NodeJS.WriteStream):Display {\n const s = stream ?? process.stdout\n if (s.isTTY) {\n return new StatusDisplay({ stream:s })\n }\n\n return new PlainDisplay()\n}\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["export type TicketStatus =\n | 'pending'\n | 'in-progress'\n | 'done'\n | 'failed'\n\nexport type DisplayTicket = {\n readonly num:number\n readonly filename:string\n status:TicketStatus\n}\n\nexport type WaveState = {\n readonly waveNum:number\n readonly totalWaves:number\n readonly tickets:Array<DisplayTicket>\n}\n\nexport type DisplayConfig = {\n readonly stream:NodeJS.WriteStream\n}\n\n/**\n * Progress across the whole run, not just the current wave.\n * `completed` counts tickets done before the wave started;\n * tickets that finish during the wave are added on top.\n */\nexport type RunProgress = {\n readonly completed:number\n readonly total:number\n}\n\nexport type Display = {\n startWave(\n waveNum:number,\n totalWaves:number,\n tickets:ReadonlyArray<{\n num:number\n filename:string\n }>,\n progress?:RunProgress,\n remaining?:ReadonlyArray<{\n num:number\n filename:string\n }>,\n ):void\n setTicketStatus(num:number, status:TicketStatus):void\n log(message:string):void\n cleanup():void\n}\n\nconst ESC = '\\x1b['\n\nexport const ansi = {\n cursorUp (n:number):string {\n return `${ESC}${n}A`\n },\n cursorTo (col:number):string {\n return `${ESC}${col}G`\n },\n eraseLine ():string {\n return `${ESC}2K`\n },\n eraseLines (n:number):string {\n let out = ''\n for (let i = 0; i < n; i++) {\n out += ansi.eraseLine()\n if (i < n - 1) out += ansi.cursorUp(1)\n }\n return out\n },\n cursorHide ():string {\n return `${ESC}?25l`\n },\n cursorShow ():string {\n return `${ESC}?25h`\n },\n} as const\n\nexport const color = {\n green (text:string):string {\n return `${ESC}32m${text}${ESC}0m`\n },\n yellow (text:string):string {\n return `${ESC}33m${text}${ESC}0m`\n },\n red (text:string):string {\n return `${ESC}31m${text}${ESC}0m`\n },\n dim (text:string):string {\n return `${ESC}2m${text}${ESC}0m`\n },\n} as const\n\nconst FILLED_DOT = '\u25CF'\nconst EMPTY_DOT = '\u25CB'\nconst SEPARATOR_WIDTH = 50\nconst DEFAULT_COLUMNS = 80\nconst MIN_LOG_ROWS = 1\nconst MAX_MESSAGE_BUFFER = 200\n\n// eslint-disable-next-line no-control-regex\nconst ANSI_RE = /\\x1b\\[[0-9;?]*[A-Za-z]/g\n\n/**\n * Clamp a line to `width` visible columns so it can never wrap.\n * A wrapped line occupies more terminal rows than `render`\n * counts, which leaves stale rows behind on the next erase.\n */\nexport function fitLine (line:string, width:number):string {\n const visible = line.replace(ANSI_RE, '')\n if (visible.length <= width) return line\n return visible.slice(0, width)\n}\n\nexport function renderDot (status:TicketStatus):string {\n switch (status) {\n case 'pending':\n return color.dim(EMPTY_DOT)\n case 'in-progress':\n return color.yellow(FILLED_DOT)\n case 'done':\n return color.green(FILLED_DOT)\n case 'failed':\n return color.red(FILLED_DOT)\n }\n}\n\nexport class StatusDisplay implements Display {\n private wave:WaveState|null = null\n private progress:RunProgress|null = null\n private remaining:Array<DisplayTicket> = []\n private messages:Array<string> = []\n private renderedLines:number = 0\n private config:DisplayConfig\n private viewportStart:number = 0\n private resizeListener:(() => void)|null = null\n\n constructor (config?:Readonly<Partial<DisplayConfig>>) {\n this.config = {\n stream:config?.stream ?? process.stdout,\n }\n }\n\n private getAvailableRows ():number {\n const termHeight = this.config.stream.rows ?? 24\n const overhead = 2 + MIN_LOG_ROWS\n const available = termHeight - overhead\n return Math.max(1, available)\n }\n\n startWave (\n waveNum:number,\n totalWaves:number,\n tickets:Array<Readonly<{ num:number; filename:string }>>,\n progress?:RunProgress,\n remaining?:ReadonlyArray<{\n num:number\n filename:string\n }>,\n ):void {\n this.wave = {\n waveNum,\n totalWaves,\n tickets:tickets.map(t => ({\n ...t,\n status:'pending' as const,\n })),\n }\n this.remaining = (remaining ?? []).map(t => ({\n ...t,\n status:'pending' as const,\n }))\n this.progress = progress ?? null\n this.messages = []\n this.viewportStart = 0\n this.config.stream.write(ansi.cursorHide())\n\n if (this.config.stream.isTTY) {\n if (this.resizeListener) {\n this.config.stream.removeListener(\n 'resize', this.resizeListener,\n )\n }\n this.resizeListener = () => this.render()\n this.config.stream.on(\n 'resize', this.resizeListener,\n )\n }\n\n this.render()\n }\n\n setTicketStatus (num:number, status:TicketStatus):void {\n if (!this.wave) return\n\n const ticket = this.wave.tickets.find(t => t.num === num)\n if (ticket) {\n ticket.status = status\n\n if (status === 'in-progress') {\n const ticketIndex = this.wave.tickets.indexOf(ticket)\n const availableRows = this.getAvailableRows()\n const totalTickets = this.wave.tickets.length\n\n if (ticketIndex < this.viewportStart) {\n this.viewportStart = ticketIndex\n } else if (ticketIndex >= this.viewportStart + availableRows) {\n // offset by 3 to keep context visible below\n // the active ticket\n this.viewportStart = ticketIndex - availableRows + 3\n }\n this.viewportStart = Math.max(\n 0,\n Math.min(this.viewportStart, totalTickets - 1),\n )\n }\n\n this.render()\n }\n }\n\n log (message:string):void {\n for (const line of message.split('\\n')) {\n this.messages.push(line)\n }\n while (this.messages.length > MAX_MESSAGE_BUFFER) {\n this.messages.shift()\n }\n this.render()\n }\n\n cleanup ():void {\n // Remove resize listener if it exists\n if (this.resizeListener && this.config.stream.isTTY) {\n this.config.stream.removeListener('resize', this.resizeListener)\n }\n this.config.stream.write(ansi.cursorShow())\n }\n\n private render ():void {\n if (!this.wave) return\n\n let output = ''\n\n if (this.renderedLines > 0) {\n output += ansi.eraseLines(this.renderedLines + 1)\n }\n\n const doneInWave = this.wave.tickets.filter(\n t => t.status === 'done',\n ).length\n const completed = doneInWave + (this.progress?.completed ?? 0)\n const total = this.progress?.total ?? this.wave.tickets.length\n const pct = total === 0 ? 0 : Math.round((completed / total) * 100)\n const width = Math.max(\n 1,\n (this.config.stream.columns ?? DEFAULT_COLUMNS) - 1,\n )\n\n output += `Wave ${this.wave.waveNum}/` +\n `${this.wave.totalWaves} - ` +\n `${pct}% complete\\n`\n\n // Pagination logic\n const availableRows = this.getAvailableRows()\n const tickets:ReadonlyArray<DisplayTicket> = [\n ...this.wave.tickets,\n ...this.remaining,\n ]\n let renderedTicketCount = 0\n\n if (tickets.length <= availableRows) {\n // All tickets fit, no pagination\n for (const ticket of tickets) {\n const dot = renderDot(ticket.status)\n const name = fitLine(ticket.filename, width - 2)\n output += `${dot} ${name}\\n`\n }\n renderedTicketCount = tickets.length\n } else {\n // Need pagination -- iterative clamp\n this.viewportStart = Math.max(\n 0,\n Math.min(\n this.viewportStart,\n tickets.length - 1,\n ),\n )\n\n let hasMoreAbove = this.viewportStart > 0\n let tentativeCount =\n availableRows - (hasMoreAbove ? 1 : 0)\n let hasMoreBelow =\n this.viewportStart + tentativeCount <\n tickets.length\n let ticketsToShow = Math.max(\n 0,\n tentativeCount - (hasMoreBelow ? 1 : 0),\n )\n\n const maxStart = Math.max(\n 0,\n tickets.length - Math.max(1, ticketsToShow),\n )\n if (this.viewportStart > maxStart) {\n this.viewportStart = maxStart\n hasMoreAbove = this.viewportStart > 0\n tentativeCount = availableRows - (hasMoreAbove ? 1 : 0)\n hasMoreBelow = (\n this.viewportStart +\n tentativeCount < tickets.length\n )\n ticketsToShow = Math.max(\n 0,\n tentativeCount - (hasMoreBelow ? 1 : 0)\n )\n }\n\n // Show \"more above\" indicator if needed\n if (hasMoreAbove) {\n const moreCount = this.viewportStart\n output += ` ... ${moreCount} more above\\n`\n }\n\n // Get visible tickets and render them\n const visibleTickets = tickets.slice(\n this.viewportStart,\n this.viewportStart + ticketsToShow,\n )\n\n for (const ticket of visibleTickets) {\n const dot = renderDot(ticket.status)\n const name = fitLine(ticket.filename, width - 2)\n output += `${dot} ${name}\\n`\n }\n\n // Show \"more below\" indicator if needed\n if (hasMoreBelow) {\n const moreCount = (\n tickets.length -\n (this.viewportStart + ticketsToShow)\n )\n output += ` ... ${moreCount} more below\\n`\n }\n\n renderedTicketCount = (hasMoreAbove ? 1 : 0) +\n visibleTickets.length +\n (hasMoreBelow ? 1 : 0)\n }\n\n output += '-'.repeat(Math.min(SEPARATOR_WIDTH, width)) + '\\n'\n\n const termHeight = this.config.stream.rows ?? 24\n const logRows = Math.max(\n 0,\n termHeight - 3 - renderedTicketCount,\n )\n const visibleMessages = this.messages.slice(-logRows)\n for (const msg of visibleMessages) {\n output += `${fitLine(msg, width)}\\n`\n }\n const emptyRows = logRows - visibleMessages.length\n for (let i = 0; i < emptyRows; i++) {\n output += '\\n'\n }\n\n this.renderedLines = 2 + renderedTicketCount + logRows\n this.config.stream.write(output)\n }\n}\n\nexport class PlainDisplay implements Display {\n startWave (\n waveNum:number,\n _totalWaves:number,\n tickets:ReadonlyArray<{ num:number; filename:string }>,\n progress?:RunProgress,\n _remaining?:ReadonlyArray<{\n num:number\n filename:string\n }>,\n ):void {\n const pct = progress && progress.total > 0 ?\n ` (${progress.completed}/${progress.total} done)` :\n ''\n console.log(\n `\\nWave ${waveNum}${pct}: ` +\n tickets.map(t => t.filename).join(', ')\n )\n }\n\n setTicketStatus (_num:number, _status:TicketStatus):void {\n // no-op: plain mode reports via log()\n }\n\n log (message:string):void {\n console.log(message)\n }\n\n cleanup ():void {\n // no-op\n }\n}\n\nexport function createDisplay (stream?:NodeJS.WriteStream):Display {\n const s = stream ?? process.stdout\n if (s.isTTY) {\n return new StatusDisplay({ stream:s })\n }\n\n return new PlainDisplay()\n}\n"],
|
|
5
|
+
"mappings": ";;AAmDA,MAAM,MAAM;AAEL,MAAM,OAAO;AAAA,EAChB,SAAU,GAAiB;AACvB,WAAO,GAAG,GAAG,GAAG,CAAC;AAAA,EACrB;AAAA,EACA,SAAU,KAAmB;AACzB,WAAO,GAAG,GAAG,GAAG,GAAG;AAAA,EACvB;AAAA,EACA,YAAoB;AAChB,WAAO,GAAG,GAAG;AAAA,EACjB;AAAA,EACA,WAAY,GAAiB;AACzB,QAAI,MAAM;AACV,aAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AACxB,aAAO,KAAK,UAAU;AACtB,UAAI,IAAI,IAAI,EAAG,QAAO,KAAK,SAAS,CAAC;AAAA,IACzC;AACA,WAAO;AAAA,EACX;AAAA,EACA,aAAqB;AACjB,WAAO,GAAG,GAAG;AAAA,EACjB;AAAA,EACA,aAAqB;AACjB,WAAO,GAAG,GAAG;AAAA,EACjB;AACJ;AAEO,MAAM,QAAQ;AAAA,EACjB,MAAO,MAAoB;AACvB,WAAO,GAAG,GAAG,MAAM,IAAI,GAAG,GAAG;AAAA,EACjC;AAAA,EACA,OAAQ,MAAoB;AACxB,WAAO,GAAG,GAAG,MAAM,IAAI,GAAG,GAAG;AAAA,EACjC;AAAA,EACA,IAAK,MAAoB;AACrB,WAAO,GAAG,GAAG,MAAM,IAAI,GAAG,GAAG;AAAA,EACjC;AAAA,EACA,IAAK,MAAoB;AACrB,WAAO,GAAG,GAAG,KAAK,IAAI,GAAG,GAAG;AAAA,EAChC;AACJ;AAEA,MAAM,aAAa;AACnB,MAAM,YAAY;AAClB,MAAM,kBAAkB;AACxB,MAAM,kBAAkB;AACxB,MAAM,eAAe;AACrB,MAAM,qBAAqB;AAG3B,MAAM,UAAU;AAOT,SAAS,QAAS,MAAa,OAAqB;AACvD,QAAM,UAAU,KAAK,QAAQ,SAAS,EAAE;AACxC,MAAI,QAAQ,UAAU,MAAO,QAAO;AACpC,SAAO,QAAQ,MAAM,GAAG,KAAK;AACjC;AAJgB;AAMT,SAAS,UAAW,QAA4B;AACnD,UAAQ,QAAQ;AAAA,IACZ,KAAK;AACD,aAAO,MAAM,IAAI,SAAS;AAAA,IAC9B,KAAK;AACD,aAAO,MAAM,OAAO,UAAU;AAAA,IAClC,KAAK;AACD,aAAO,MAAM,MAAM,UAAU;AAAA,IACjC,KAAK;AACD,aAAO,MAAM,IAAI,UAAU;AAAA,EACnC;AACJ;AAXgB;AAaT,MAAM,cAAiC;AAAA,EAhI9C,OAgI8C;AAAA;AAAA;AAAA,EAClC,OAAsB;AAAA,EACtB,WAA4B;AAAA,EAC5B,YAAiC,CAAC;AAAA,EAClC,WAAyB,CAAC;AAAA,EAC1B,gBAAuB;AAAA,EACvB;AAAA,EACA,gBAAuB;AAAA,EACvB,iBAAmC;AAAA,EAE3C,YAAa,QAA0C;AACnD,SAAK,SAAS;AAAA,MACV,QAAO,QAAQ,UAAU,QAAQ;AAAA,IACrC;AAAA,EACJ;AAAA,EAEQ,mBAA2B;AAC/B,UAAM,aAAa,KAAK,OAAO,OAAO,QAAQ;AAC9C,UAAM,WAAW,IAAI;AACrB,UAAM,YAAY,aAAa;AAC/B,WAAO,KAAK,IAAI,GAAG,SAAS;AAAA,EAChC;AAAA,EAEA,UACI,SACA,YACA,SACA,UACA,WAIG;AACH,SAAK,OAAO;AAAA,MACR;AAAA,MACA;AAAA,MACA,SAAQ,QAAQ,IAAI,QAAM;AAAA,QACtB,GAAG;AAAA,QACH,QAAO;AAAA,MACX,EAAE;AAAA,IACN;AACA,SAAK,aAAa,aAAa,CAAC,GAAG,IAAI,QAAM;AAAA,MACzC,GAAG;AAAA,MACH,QAAO;AAAA,IACX,EAAE;AACF,SAAK,WAAW,YAAY;AAC5B,SAAK,WAAW,CAAC;AACjB,SAAK,gBAAgB;AACrB,SAAK,OAAO,OAAO,MAAM,KAAK,WAAW,CAAC;AAE1C,QAAI,KAAK,OAAO,OAAO,OAAO;AAC1B,UAAI,KAAK,gBAAgB;AACrB,aAAK,OAAO,OAAO;AAAA,UACf;AAAA,UAAU,KAAK;AAAA,QACnB;AAAA,MACJ;AACA,WAAK,iBAAiB,MAAM,KAAK,OAAO;AACxC,WAAK,OAAO,OAAO;AAAA,QACf;AAAA,QAAU,KAAK;AAAA,MACnB;AAAA,IACJ;AAEA,SAAK,OAAO;AAAA,EAChB;AAAA,EAEA,gBAAiB,KAAY,QAA0B;AACnD,QAAI,CAAC,KAAK,KAAM;AAEhB,UAAM,SAAS,KAAK,KAAK,QAAQ,KAAK,OAAK,EAAE,QAAQ,GAAG;AACxD,QAAI,QAAQ;AACR,aAAO,SAAS;AAEhB,UAAI,WAAW,eAAe;AAC1B,cAAM,cAAc,KAAK,KAAK,QAAQ,QAAQ,MAAM;AACpD,cAAM,gBAAgB,KAAK,iBAAiB;AAC5C,cAAM,eAAe,KAAK,KAAK,QAAQ;AAEvC,YAAI,cAAc,KAAK,eAAe;AAClC,eAAK,gBAAgB;AAAA,QACzB,WAAW,eAAe,KAAK,gBAAgB,eAAe;AAG1D,eAAK,gBAAgB,cAAc,gBAAgB;AAAA,QACvD;AACA,aAAK,gBAAgB,KAAK;AAAA,UACtB;AAAA,UACA,KAAK,IAAI,KAAK,eAAe,eAAe,CAAC;AAAA,QACjD;AAAA,MACJ;AAEA,WAAK,OAAO;AAAA,IAChB;AAAA,EACJ;AAAA,EAEA,IAAK,SAAqB;AACtB,eAAW,QAAQ,QAAQ,MAAM,IAAI,GAAG;AACpC,WAAK,SAAS,KAAK,IAAI;AAAA,IAC3B;AACA,WAAO,KAAK,SAAS,SAAS,oBAAoB;AAC9C,WAAK,SAAS,MAAM;AAAA,IACxB;AACA,SAAK,OAAO;AAAA,EAChB;AAAA,EAEA,UAAgB;AAEZ,QAAI,KAAK,kBAAkB,KAAK,OAAO,OAAO,OAAO;AACjD,WAAK,OAAO,OAAO,eAAe,UAAU,KAAK,cAAc;AAAA,IACnE;AACA,SAAK,OAAO,OAAO,MAAM,KAAK,WAAW,CAAC;AAAA,EAC9C;AAAA,EAEQ,SAAe;AACnB,QAAI,CAAC,KAAK,KAAM;AAEhB,QAAI,SAAS;AAEb,QAAI,KAAK,gBAAgB,GAAG;AACxB,gBAAU,KAAK,WAAW,KAAK,gBAAgB,CAAC;AAAA,IACpD;AAEA,UAAM,aAAa,KAAK,KAAK,QAAQ;AAAA,MACjC,OAAK,EAAE,WAAW;AAAA,IACtB,EAAE;AACF,UAAM,YAAY,cAAc,KAAK,UAAU,aAAa;AAC5D,UAAM,QAAQ,KAAK,UAAU,SAAS,KAAK,KAAK,QAAQ;AACxD,UAAM,MAAM,UAAU,IAAI,IAAI,KAAK,MAAO,YAAY,QAAS,GAAG;AAClE,UAAM,QAAQ,KAAK;AAAA,MACf;AAAA,OACC,KAAK,OAAO,OAAO,WAAW,mBAAmB;AAAA,IACtD;AAEA,cAAU,QAAQ,KAAK,KAAK,OAAO,IAC5B,KAAK,KAAK,UAAU,MACpB,GAAG;AAAA;AAGV,UAAM,gBAAgB,KAAK,iBAAiB;AAC5C,UAAM,UAAuC;AAAA,MACzC,GAAG,KAAK,KAAK;AAAA,MACb,GAAG,KAAK;AAAA,IACZ;AACA,QAAI,sBAAsB;AAE1B,QAAI,QAAQ,UAAU,eAAe;AAEjC,iBAAW,UAAU,SAAS;AAC1B,cAAM,MAAM,UAAU,OAAO,MAAM;AACnC,cAAM,OAAO,QAAQ,OAAO,UAAU,QAAQ,CAAC;AAC/C,kBAAU,GAAG,GAAG,IAAI,IAAI;AAAA;AAAA,MAC5B;AACA,4BAAsB,QAAQ;AAAA,IAClC,OAAO;AAEH,WAAK,gBAAgB,KAAK;AAAA,QACtB;AAAA,QACA,KAAK;AAAA,UACD,KAAK;AAAA,UACL,QAAQ,SAAS;AAAA,QACrB;AAAA,MACJ;AAEA,UAAI,eAAe,KAAK,gBAAgB;AACxC,UAAI,iBACA,iBAAiB,eAAe,IAAI;AACxC,UAAI,eACA,KAAK,gBAAgB,iBACrB,QAAQ;AACZ,UAAI,gBAAgB,KAAK;AAAA,QACrB;AAAA,QACA,kBAAkB,eAAe,IAAI;AAAA,MACzC;AAEA,YAAM,WAAW,KAAK;AAAA,QAClB;AAAA,QACA,QAAQ,SAAS,KAAK,IAAI,GAAG,aAAa;AAAA,MAC9C;AACA,UAAI,KAAK,gBAAgB,UAAU;AAC/B,aAAK,gBAAgB;AACrB,uBAAe,KAAK,gBAAgB;AACpC,yBAAiB,iBAAiB,eAAe,IAAI;AACrD,uBACI,KAAK,gBACL,iBAAiB,QAAQ;AAE7B,wBAAgB,KAAK;AAAA,UACjB;AAAA,UACA,kBAAkB,eAAe,IAAI;AAAA,QACzC;AAAA,MACJ;AAGA,UAAI,cAAc;AACd,cAAM,YAAY,KAAK;AACvB,kBAAU,SAAS,SAAS;AAAA;AAAA,MAChC;AAGA,YAAM,iBAAiB,QAAQ;AAAA,QAC3B,KAAK;AAAA,QACL,KAAK,gBAAgB;AAAA,MACzB;AAEA,iBAAW,UAAU,gBAAgB;AACjC,cAAM,MAAM,UAAU,OAAO,MAAM;AACnC,cAAM,OAAO,QAAQ,OAAO,UAAU,QAAQ,CAAC;AAC/C,kBAAU,GAAG,GAAG,IAAI,IAAI;AAAA;AAAA,MAC5B;AAGA,UAAI,cAAc;AACd,cAAM,YACF,QAAQ,UACP,KAAK,gBAAgB;AAE1B,kBAAU,SAAS,SAAS;AAAA;AAAA,MAChC;AAEA,6BAAuB,eAAe,IAAI,KACtC,eAAe,UACd,eAAe,IAAI;AAAA,IAC5B;AAEA,cAAU,IAAI,OAAO,KAAK,IAAI,iBAAiB,KAAK,CAAC,IAAI;AAEzD,UAAM,aAAa,KAAK,OAAO,OAAO,QAAQ;AAC9C,UAAM,UAAU,KAAK;AAAA,MACjB;AAAA,MACA,aAAa,IAAI;AAAA,IACrB;AACA,UAAM,kBAAkB,KAAK,SAAS,MAAM,CAAC,OAAO;AACpD,eAAW,OAAO,iBAAiB;AAC/B,gBAAU,GAAG,QAAQ,KAAK,KAAK,CAAC;AAAA;AAAA,IACpC;AACA,UAAM,YAAY,UAAU,gBAAgB;AAC5C,aAAS,IAAI,GAAG,IAAI,WAAW,KAAK;AAChC,gBAAU;AAAA,IACd;AAEA,SAAK,gBAAgB,IAAI,sBAAsB;AAC/C,SAAK,OAAO,OAAO,MAAM,MAAM;AAAA,EACnC;AACJ;AAEO,MAAM,aAAgC;AAAA,EApX7C,OAoX6C;AAAA;AAAA;AAAA,EACzC,UACI,SACA,aACA,SACA,UACA,YAIG;AACH,UAAM,MAAM,YAAY,SAAS,QAAQ,IACrC,KAAK,SAAS,SAAS,IAAI,SAAS,KAAK,WACzC;AACJ,YAAQ;AAAA,MACJ;AAAA,OAAU,OAAO,GAAG,GAAG,OACvB,QAAQ,IAAI,OAAK,EAAE,QAAQ,EAAE,KAAK,IAAI;AAAA,IAC1C;AAAA,EACJ;AAAA,EAEA,gBAAiB,MAAa,SAA2B;AAAA,EAEzD;AAAA,EAEA,IAAK,SAAqB;AACtB,YAAQ,IAAI,OAAO;AAAA,EACvB;AAAA,EAEA,UAAgB;AAAA,EAEhB;AACJ;AAEO,SAAS,cAAe,QAAoC;AAC/D,QAAM,IAAI,UAAU,QAAQ;AAC5B,MAAI,EAAE,OAAO;AACT,WAAO,IAAI,cAAc,EAAE,QAAO,EAAE,CAAC;AAAA,EACzC;AAEA,SAAO,IAAI,aAAa;AAC5B;AAPgB;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/index.js
CHANGED
|
@@ -864,8 +864,10 @@ var StatusDisplay = class {
|
|
|
864
864
|
}
|
|
865
865
|
}
|
|
866
866
|
log(message) {
|
|
867
|
-
|
|
868
|
-
|
|
867
|
+
for (const line of message.split("\n")) {
|
|
868
|
+
this.messages.push(line);
|
|
869
|
+
}
|
|
870
|
+
while (this.messages.length > MAX_MESSAGE_BUFFER) {
|
|
869
871
|
this.messages.shift();
|
|
870
872
|
}
|
|
871
873
|
this.render();
|
|
@@ -1357,7 +1359,32 @@ function getProviderArgs(cmd) {
|
|
|
1357
1359
|
return PROVIDER_ARGS[name] ?? [];
|
|
1358
1360
|
}
|
|
1359
1361
|
__name(getProviderArgs, "getProviderArgs");
|
|
1360
|
-
var
|
|
1362
|
+
var EFFORT_LADDERS = {
|
|
1363
|
+
pi: [
|
|
1364
|
+
"off",
|
|
1365
|
+
"minimal",
|
|
1366
|
+
"low",
|
|
1367
|
+
"medium",
|
|
1368
|
+
"high",
|
|
1369
|
+
"xhigh",
|
|
1370
|
+
"max"
|
|
1371
|
+
],
|
|
1372
|
+
claude: [
|
|
1373
|
+
"low",
|
|
1374
|
+
"medium",
|
|
1375
|
+
"high",
|
|
1376
|
+
"xhigh",
|
|
1377
|
+
"max"
|
|
1378
|
+
],
|
|
1379
|
+
codex: [
|
|
1380
|
+
"minimal",
|
|
1381
|
+
"low",
|
|
1382
|
+
"medium",
|
|
1383
|
+
"high",
|
|
1384
|
+
"xhigh"
|
|
1385
|
+
]
|
|
1386
|
+
};
|
|
1387
|
+
var ALL_EFFORT_LEVELS = [
|
|
1361
1388
|
"off",
|
|
1362
1389
|
"minimal",
|
|
1363
1390
|
"low",
|
|
@@ -1366,12 +1393,14 @@ var THINKING_LEVELS = [
|
|
|
1366
1393
|
"xhigh",
|
|
1367
1394
|
"max"
|
|
1368
1395
|
];
|
|
1369
|
-
function
|
|
1396
|
+
function escalateEffort(provider, level) {
|
|
1370
1397
|
if (!level) return "high";
|
|
1371
|
-
const
|
|
1372
|
-
|
|
1398
|
+
const ladder = EFFORT_LADDERS[provider];
|
|
1399
|
+
const i = ladder.indexOf(level);
|
|
1400
|
+
if (i === -1) return level;
|
|
1401
|
+
return ladder[Math.min(i + 1, ladder.length - 1)];
|
|
1373
1402
|
}
|
|
1374
|
-
__name(
|
|
1403
|
+
__name(escalateEffort, "escalateEffort");
|
|
1375
1404
|
function formatStreamLine(line) {
|
|
1376
1405
|
let obj;
|
|
1377
1406
|
try {
|
|
@@ -1513,8 +1542,21 @@ function spawnAgent(opts) {
|
|
|
1513
1542
|
const providerName = basename2(opts.agentCmd);
|
|
1514
1543
|
const isClaude = providerName === "claude";
|
|
1515
1544
|
const isPi = providerName === "pi";
|
|
1516
|
-
if (
|
|
1517
|
-
|
|
1545
|
+
if (opts.effort && providerName in EFFORT_LADDERS) {
|
|
1546
|
+
const ladder = EFFORT_LADDERS[providerName];
|
|
1547
|
+
if (!ladder.includes(opts.effort)) {
|
|
1548
|
+
if (opts.onLine) {
|
|
1549
|
+
opts.onLine(
|
|
1550
|
+
`Warning: effort '${opts.effort}' not supported by ${providerName}, skipping`
|
|
1551
|
+
);
|
|
1552
|
+
}
|
|
1553
|
+
} else if (isPi) {
|
|
1554
|
+
args.push("--thinking", opts.effort);
|
|
1555
|
+
} else if (isClaude) {
|
|
1556
|
+
args.push("--effort", opts.effort);
|
|
1557
|
+
} else if (providerName === "codex") {
|
|
1558
|
+
args.push("-e", opts.effort);
|
|
1559
|
+
}
|
|
1518
1560
|
}
|
|
1519
1561
|
const prefix = opts.label ? ` [${opts.label}] ` : " ";
|
|
1520
1562
|
let promptFile = null;
|
|
@@ -1745,7 +1787,7 @@ Prompt for first ready ticket (${firstReady[0].filename}):
|
|
|
1745
1787
|
Test command: ${opts.testCmd ?? "(none)"}`
|
|
1746
1788
|
);
|
|
1747
1789
|
console.log(
|
|
1748
|
-
`
|
|
1790
|
+
`Effort: ${opts.effort ?? "(provider default)"}`
|
|
1749
1791
|
);
|
|
1750
1792
|
console.log(
|
|
1751
1793
|
`Retries: ${opts.retries ?? DEFAULT_RETRIES}`
|
|
@@ -1874,7 +1916,7 @@ async function respawnAndRetryMerge(opts) {
|
|
|
1874
1916
|
return mergeResult.success;
|
|
1875
1917
|
}
|
|
1876
1918
|
__name(respawnAndRetryMerge, "respawnAndRetryMerge");
|
|
1877
|
-
var DEFAULT_RETRIES =
|
|
1919
|
+
var DEFAULT_RETRIES = 3;
|
|
1878
1920
|
var DEFAULT_RETRY_DELAY = 30;
|
|
1879
1921
|
function retryDelayFor(base, attempt) {
|
|
1880
1922
|
return base * 2 ** (attempt - 2);
|
|
@@ -1922,7 +1964,7 @@ async function runTicketAttempt(opts) {
|
|
|
1922
1964
|
cwd: worktree.worktreePath,
|
|
1923
1965
|
label: ticket.filename.replace(/\.md$/, ""),
|
|
1924
1966
|
maxTurns: opts.maxTurns,
|
|
1925
|
-
|
|
1967
|
+
effort: opts.effort,
|
|
1926
1968
|
onLine: /* @__PURE__ */ __name((line) => d.log(line), "onLine")
|
|
1927
1969
|
});
|
|
1928
1970
|
if (agentResult.timedOut) {
|
|
@@ -2001,8 +2043,9 @@ __name(runTicketAttempt, "runTicketAttempt");
|
|
|
2001
2043
|
async function runTicketAttempts(opts) {
|
|
2002
2044
|
const { ticket, d } = opts;
|
|
2003
2045
|
const maxAttempts = opts.retries + 1;
|
|
2004
|
-
const
|
|
2005
|
-
|
|
2046
|
+
const providerName = basename2(opts.agentCmd);
|
|
2047
|
+
const isKnown = providerName in EFFORT_LADDERS;
|
|
2048
|
+
let effort = opts.effort;
|
|
2006
2049
|
let outcome = {
|
|
2007
2050
|
kind: "no-commits",
|
|
2008
2051
|
worktreePath: "",
|
|
@@ -2012,10 +2055,15 @@ async function runTicketAttempts(opts) {
|
|
|
2012
2055
|
if (attempt === 1) {
|
|
2013
2056
|
d.log(` Starting ${ticket.filename}`);
|
|
2014
2057
|
} else {
|
|
2015
|
-
|
|
2058
|
+
if (isKnown) {
|
|
2059
|
+
effort = escalateEffort(
|
|
2060
|
+
providerName,
|
|
2061
|
+
effort
|
|
2062
|
+
);
|
|
2063
|
+
}
|
|
2016
2064
|
const delay = retryDelayFor(opts.retryDelay, attempt);
|
|
2017
2065
|
d.log(
|
|
2018
|
-
` ${ticket.filename}: retrying (attempt ${attempt}/${maxAttempts}` + (
|
|
2066
|
+
` ${ticket.filename}: retrying (attempt ${attempt}/${maxAttempts}` + (isKnown && effort ? `, effort ${effort}` : "") + (delay > 0 ? `, waiting ${delay}s` : "") + ")"
|
|
2019
2067
|
);
|
|
2020
2068
|
if (delay > 0) await sleep(delay * 1e3);
|
|
2021
2069
|
}
|
|
@@ -2023,7 +2071,7 @@ async function runTicketAttempts(opts) {
|
|
|
2023
2071
|
outcome = await runTicketAttempt({
|
|
2024
2072
|
...opts,
|
|
2025
2073
|
attempt,
|
|
2026
|
-
|
|
2074
|
+
effort
|
|
2027
2075
|
});
|
|
2028
2076
|
} catch (error) {
|
|
2029
2077
|
d.log(
|
|
@@ -2056,12 +2104,13 @@ function runProgress(tickets) {
|
|
|
2056
2104
|
};
|
|
2057
2105
|
}
|
|
2058
2106
|
__name(runProgress, "runProgress");
|
|
2059
|
-
async function runWaves(tickets, specBody, agentCmd, model, timeout, logsDir, display, maxTurns,
|
|
2107
|
+
async function runWaves(tickets, specBody, agentCmd, model, timeout, logsDir, display, maxTurns, effort, retries, retryDelay) {
|
|
2060
2108
|
mkdirSync2(logsDir, { recursive: true });
|
|
2061
2109
|
const d = display ?? createDisplay();
|
|
2062
2110
|
const maxAttempts = (retries ?? DEFAULT_RETRIES) + 1;
|
|
2063
2111
|
const delayBase = retryDelay ?? 0;
|
|
2064
|
-
const
|
|
2112
|
+
const providerName = basename2(agentCmd);
|
|
2113
|
+
const isKnown = providerName in EFFORT_LADDERS;
|
|
2065
2114
|
let totalWaves = 0;
|
|
2066
2115
|
for (const _ of simulateWaves(tickets)) totalWaves++;
|
|
2067
2116
|
let totalCompleted = 0;
|
|
@@ -2099,7 +2148,7 @@ async function runWaves(tickets, specBody, agentCmd, model, timeout, logsDir, di
|
|
|
2099
2148
|
);
|
|
2100
2149
|
d.setTicketStatus(t.num, "in-progress");
|
|
2101
2150
|
d.log(` Starting ${t.filename}`);
|
|
2102
|
-
let level =
|
|
2151
|
+
let level = effort;
|
|
2103
2152
|
let result = await spawnAgent({
|
|
2104
2153
|
prompt,
|
|
2105
2154
|
agentCmd,
|
|
@@ -2108,14 +2157,19 @@ async function runWaves(tickets, specBody, agentCmd, model, timeout, logsDir, di
|
|
|
2108
2157
|
logPath: attemptLogPath(logsDir, t, 1, ".log"),
|
|
2109
2158
|
label,
|
|
2110
2159
|
maxTurns,
|
|
2111
|
-
|
|
2160
|
+
effort: level,
|
|
2112
2161
|
onLine: /* @__PURE__ */ __name((line) => d.log(line), "onLine")
|
|
2113
2162
|
});
|
|
2114
2163
|
for (let attempt = 2; result.exitCode !== 0 && !result.timedOut && attempt <= maxAttempts; attempt++) {
|
|
2115
|
-
|
|
2164
|
+
if (isKnown) {
|
|
2165
|
+
level = escalateEffort(
|
|
2166
|
+
providerName,
|
|
2167
|
+
level
|
|
2168
|
+
);
|
|
2169
|
+
}
|
|
2116
2170
|
const delay = retryDelayFor(delayBase, attempt);
|
|
2117
2171
|
d.log(
|
|
2118
|
-
` ${t.filename}: agent exited ${result.exitCode}; retrying (attempt ${attempt}/${maxAttempts}` + (
|
|
2172
|
+
` ${t.filename}: agent exited ${result.exitCode}; retrying (attempt ${attempt}/${maxAttempts}` + (isKnown && level ? `, effort ${level}` : "") + (delay > 0 ? `, waiting ${delay}s` : "") + ")"
|
|
2119
2173
|
);
|
|
2120
2174
|
if (delay > 0) await sleep(delay * 1e3);
|
|
2121
2175
|
result = await spawnAgent({
|
|
@@ -2131,7 +2185,7 @@ async function runWaves(tickets, specBody, agentCmd, model, timeout, logsDir, di
|
|
|
2131
2185
|
),
|
|
2132
2186
|
label,
|
|
2133
2187
|
maxTurns,
|
|
2134
|
-
|
|
2188
|
+
effort: level,
|
|
2135
2189
|
onLine: /* @__PURE__ */ __name((line) => d.log(line), "onLine")
|
|
2136
2190
|
});
|
|
2137
2191
|
}
|
|
@@ -2249,7 +2303,7 @@ async function runWavesIsolated(opts) {
|
|
|
2249
2303
|
logsDir: opts.logsDir,
|
|
2250
2304
|
testCmd: opts.testCmd,
|
|
2251
2305
|
maxTurns: opts.maxTurns,
|
|
2252
|
-
|
|
2306
|
+
effort: opts.effort,
|
|
2253
2307
|
retries,
|
|
2254
2308
|
retryDelay,
|
|
2255
2309
|
d
|
|
@@ -2626,12 +2680,15 @@ yargs(hideBin(process.argv)).scriptName("nightralph").usage("$0 <command>").comm
|
|
|
2626
2680
|
});
|
|
2627
2681
|
}
|
|
2628
2682
|
).command(
|
|
2629
|
-
"$0 <provider>",
|
|
2683
|
+
"$0 <provider> [turns]",
|
|
2630
2684
|
"Execute tickets with the given provider",
|
|
2631
2685
|
(y) => y.positional("provider", {
|
|
2632
2686
|
type: "string",
|
|
2633
2687
|
describe: "Agent CLI to spawn (e.g. claude, codex, pi)",
|
|
2634
2688
|
demandOption: true
|
|
2689
|
+
}).positional("turns", {
|
|
2690
|
+
type: "number",
|
|
2691
|
+
describe: "Max agentic turns per ticket (shorthand for --max-turns)"
|
|
2635
2692
|
}).option("m", {
|
|
2636
2693
|
alias: "model",
|
|
2637
2694
|
type: "string",
|
|
@@ -2657,14 +2714,14 @@ yargs(hideBin(process.argv)).scriptName("nightralph").usage("$0 <command>").comm
|
|
|
2657
2714
|
}).option("max-turns", {
|
|
2658
2715
|
type: "number",
|
|
2659
2716
|
describe: "Max agentic turns per ticket (forwarded to the agent CLI)"
|
|
2660
|
-
}).option("
|
|
2717
|
+
}).option("effort", {
|
|
2661
2718
|
type: "string",
|
|
2662
|
-
choices:
|
|
2663
|
-
describe: "Starting
|
|
2719
|
+
choices: ALL_EFFORT_LEVELS,
|
|
2720
|
+
describe: "Starting effort level. Omitted = provider default. Retries step up from here."
|
|
2664
2721
|
}).option("retries", {
|
|
2665
2722
|
type: "number",
|
|
2666
2723
|
default: DEFAULT_RETRIES,
|
|
2667
|
-
describe: "Extra attempts per ticket after a failed run, each with a higher
|
|
2724
|
+
describe: "Extra attempts per ticket after a failed run, each with a higher effort level. 0 disables retries."
|
|
2668
2725
|
}).option("retry-delay", {
|
|
2669
2726
|
type: "number",
|
|
2670
2727
|
default: DEFAULT_RETRY_DELAY,
|
|
@@ -2679,8 +2736,8 @@ yargs(hideBin(process.argv)).scriptName("nightralph").usage("$0 <command>").comm
|
|
|
2679
2736
|
timeout: argv.timeout,
|
|
2680
2737
|
testCmd: argv.testCmd,
|
|
2681
2738
|
stopOnConflict: argv.X,
|
|
2682
|
-
maxTurns: argv.maxTurns,
|
|
2683
|
-
|
|
2739
|
+
maxTurns: argv.maxTurns ?? argv.turns,
|
|
2740
|
+
effort: argv.effort,
|
|
2684
2741
|
retries: argv.retries,
|
|
2685
2742
|
retryDelay: argv.retryDelay
|
|
2686
2743
|
});
|
|
@@ -2763,7 +2820,7 @@ async function runExecute(argv) {
|
|
|
2763
2820
|
conflictStrategy,
|
|
2764
2821
|
progressPath,
|
|
2765
2822
|
testCmd,
|
|
2766
|
-
|
|
2823
|
+
effort: argv.effort,
|
|
2767
2824
|
retries: argv.retries,
|
|
2768
2825
|
retryDelay: argv.retryDelay
|
|
2769
2826
|
});
|
|
@@ -2795,7 +2852,7 @@ async function runExecute(argv) {
|
|
|
2795
2852
|
display,
|
|
2796
2853
|
testCmd,
|
|
2797
2854
|
maxTurns: argv.maxTurns,
|
|
2798
|
-
|
|
2855
|
+
effort: argv.effort,
|
|
2799
2856
|
retries: argv.retries,
|
|
2800
2857
|
retryDelay: argv.retryDelay
|
|
2801
2858
|
});
|
|
@@ -2809,7 +2866,7 @@ async function runExecute(argv) {
|
|
|
2809
2866
|
logsDir,
|
|
2810
2867
|
display,
|
|
2811
2868
|
argv.maxTurns,
|
|
2812
|
-
argv.
|
|
2869
|
+
argv.effort,
|
|
2813
2870
|
argv.retries,
|
|
2814
2871
|
argv.retryDelay
|
|
2815
2872
|
);
|