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 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "task-while",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
4
4
  "packageManager": "pnpm@10.32.1",
5
5
  "description": "Git-first task orchestrator for task-source workspaces",
6
6
  "author": "Zhang Yu",
@@ -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