task-while 0.0.3 → 0.0.4
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 +3 -1
- package/package.json +1 -1
- package/src/commands/batch.ts +98 -0
package/README.md
CHANGED
|
@@ -127,6 +127,8 @@ cd /path/to/workspace
|
|
|
127
127
|
pnpm exec task-while batch --config ./batch.yaml
|
|
128
128
|
```
|
|
129
129
|
|
|
130
|
+
This repository also includes a repo-local skill at `skills/generate-batch-yaml/` for generating batch configs from natural-language requirements.
|
|
131
|
+
|
|
130
132
|
Batch config example:
|
|
131
133
|
|
|
132
134
|
```yaml
|
|
@@ -163,7 +165,7 @@ Batch behavior:
|
|
|
163
165
|
- structured results are written beside the YAML file in `results.json`
|
|
164
166
|
- internal harness state is written under `.while/harness/` beside the YAML file
|
|
165
167
|
- result keys are relative to the directory that contains `batch.yaml`
|
|
166
|
-
- `--verbose` streams direct provider details to `stderr` during batch execution, including Claude init/task/tool/result summaries and Codex thinking, commands, MCP tools, file updates, todo changes, messages, and final usage
|
|
168
|
+
- `--verbose` streams batch-level progress and direct provider details to `stderr` during batch execution, including the current file, completion counts, Claude init/task/tool/result summaries, and Codex thinking, commands, MCP tools, file updates, todo changes, messages, and final usage
|
|
167
169
|
- rerunning the command resumes unfinished work and skips files that already have accepted results
|
|
168
170
|
- failed files are suspended and retried after all pending files are processed
|
|
169
171
|
- file-level retries are limited by `maxRetries` (default 3); exhausted files are marked blocked
|
package/package.json
CHANGED
package/src/commands/batch.ts
CHANGED
|
@@ -42,6 +42,38 @@ export interface RunBatchCommandResult {
|
|
|
42
42
|
resultsFilePath: string
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
function writeBatchVerboseLine(verbose: boolean | undefined, line: string) {
|
|
46
|
+
if (!verbose) {
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
process.stderr.write(`[batch] ${line}\n`)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function readSessionProgress(detail: unknown) {
|
|
53
|
+
if (typeof detail !== 'object' || detail === null) {
|
|
54
|
+
return null
|
|
55
|
+
}
|
|
56
|
+
const progress = (detail as { progress?: unknown }).progress
|
|
57
|
+
if (typeof progress !== 'object' || progress === null) {
|
|
58
|
+
return null
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const blocked =
|
|
62
|
+
typeof (progress as { blocked?: unknown }).blocked === 'number'
|
|
63
|
+
? (progress as { blocked: number }).blocked
|
|
64
|
+
: 0
|
|
65
|
+
const completed =
|
|
66
|
+
typeof (progress as { completed?: unknown }).completed === 'number'
|
|
67
|
+
? (progress as { completed: number }).completed
|
|
68
|
+
: 0
|
|
69
|
+
const suspended =
|
|
70
|
+
typeof (progress as { suspended?: unknown }).suspended === 'number'
|
|
71
|
+
? (progress as { suspended: number }).suspended
|
|
72
|
+
: 0
|
|
73
|
+
|
|
74
|
+
return { blocked, completed, suspended }
|
|
75
|
+
}
|
|
76
|
+
|
|
45
77
|
async function readJsonFileIfExists(filePath: string) {
|
|
46
78
|
const exists = await fsExtra.pathExists(filePath)
|
|
47
79
|
if (!exists) {
|
|
@@ -138,6 +170,10 @@ export async function runBatchCommand(
|
|
|
138
170
|
})
|
|
139
171
|
|
|
140
172
|
const processedFiles: string[] = []
|
|
173
|
+
const totalFiles = discoveredFiles.length
|
|
174
|
+
let blockedCount = 0
|
|
175
|
+
let completedCount = 0
|
|
176
|
+
let suspendedCount = 0
|
|
141
177
|
|
|
142
178
|
for await (const event of runSession({
|
|
143
179
|
config: {},
|
|
@@ -153,8 +189,70 @@ export async function runBatchCommand(
|
|
|
153
189
|
}),
|
|
154
190
|
},
|
|
155
191
|
})) {
|
|
192
|
+
if (event.type === SessionEventType.SessionStarted) {
|
|
193
|
+
const progress = readSessionProgress(event.detail)
|
|
194
|
+
if (progress) {
|
|
195
|
+
blockedCount = progress.blocked
|
|
196
|
+
completedCount = progress.completed
|
|
197
|
+
suspendedCount = progress.suspended
|
|
198
|
+
}
|
|
199
|
+
writeBatchVerboseLine(
|
|
200
|
+
input.verbose,
|
|
201
|
+
`resume total=${totalFiles} completed=${completedCount} blocked=${blockedCount} suspended=${suspendedCount}`,
|
|
202
|
+
)
|
|
203
|
+
continue
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (event.type === SessionEventType.SubjectStarted) {
|
|
207
|
+
writeBatchVerboseLine(
|
|
208
|
+
input.verbose,
|
|
209
|
+
`start completed=${completedCount}/${totalFiles} file=${event.subjectId}`,
|
|
210
|
+
)
|
|
211
|
+
continue
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (event.type === SessionEventType.SubjectResumed) {
|
|
215
|
+
suspendedCount = Math.max(0, suspendedCount - 1)
|
|
216
|
+
writeBatchVerboseLine(
|
|
217
|
+
input.verbose,
|
|
218
|
+
`resume-file completed=${completedCount}/${totalFiles} file=${event.subjectId}`,
|
|
219
|
+
)
|
|
220
|
+
continue
|
|
221
|
+
}
|
|
222
|
+
|
|
156
223
|
if (event.type === SessionEventType.SubjectDone) {
|
|
224
|
+
completedCount += 1
|
|
157
225
|
processedFiles.push(event.subjectId)
|
|
226
|
+
writeBatchVerboseLine(
|
|
227
|
+
input.verbose,
|
|
228
|
+
`done completed=${completedCount}/${totalFiles} file=${event.subjectId}`,
|
|
229
|
+
)
|
|
230
|
+
continue
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (event.type === SessionEventType.SubjectBlocked) {
|
|
234
|
+
blockedCount += 1
|
|
235
|
+
writeBatchVerboseLine(
|
|
236
|
+
input.verbose,
|
|
237
|
+
`blocked completed=${completedCount}/${totalFiles} file=${event.subjectId}`,
|
|
238
|
+
)
|
|
239
|
+
continue
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (event.type === SessionEventType.SubjectSuspended) {
|
|
243
|
+
suspendedCount += 1
|
|
244
|
+
writeBatchVerboseLine(
|
|
245
|
+
input.verbose,
|
|
246
|
+
`suspended completed=${completedCount}/${totalFiles} file=${event.subjectId}`,
|
|
247
|
+
)
|
|
248
|
+
continue
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (event.type === SessionEventType.SessionDone) {
|
|
252
|
+
writeBatchVerboseLine(
|
|
253
|
+
input.verbose,
|
|
254
|
+
`session-done total=${totalFiles} completed=${completedCount} blocked=${blockedCount} suspended=${suspendedCount}`,
|
|
255
|
+
)
|
|
158
256
|
}
|
|
159
257
|
}
|
|
160
258
|
|