taskchef 7.4.0 → 7.6.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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "taskchef",
3
- "version": "7.4.0",
3
+ "version": "7.6.0",
4
4
  "description": "Dispatch work from a data-only workspace to visible Codex project tasks.",
5
5
  "author": {
6
6
  "name": "Favo Yang",
package/README.md CHANGED
@@ -3,11 +3,11 @@
3
3
  TaskChef is a local dispatch desk for Codex. Give one dispatcher a request and
4
4
  it records each independently useful outcome, creates a normal Codex task in
5
5
  the right project, and returns immediately. The executor task is where live
6
- work, approvals, and follow-ups happen; TaskChef keeps the latest compact
7
- snapshot for navigation and reporting.
6
+ work, approvals, and follow-ups happen; TaskChef keeps every semantic result
7
+ while projecting the latest one for compact navigation and reporting.
8
8
 
9
9
  ```text
10
- request -> recorded TaskChef task -> Codex executor -> current state + last result
10
+ request -> recorded TaskChef task -> Codex executor -> current state + result history
11
11
  ```
12
12
 
13
13
  ## Which document should I read?
@@ -49,7 +49,7 @@ The canonical workspace is `~/.agents/taskchef`. TaskChef owns only:
49
49
  ```text
50
50
  AGENTS.md managed dispatcher instructions plus user additions
51
51
  taskchef.json schema-2 configured projects and routing metadata
52
- tasks.jsonl one schema-4/5 snapshot per task (new writes use schema 5)
52
+ tasks.jsonl one task snapshot per line (schema 6; schema 4/5 migration supported)
53
53
  ```
54
54
 
55
55
  List or change routing targets conversationally:
@@ -119,10 +119,12 @@ when a turn starts and one semantic outcome before that same turn ends:
119
119
  - `failed`: the executor or creation attempt ended unsuccessfully.
120
120
 
121
121
  A native approval prompt is live Codex state, not `needs_input`.
122
- TaskChef stores the current reported execution state and separately preserves
123
- the last concise semantic result. A follow-up therefore appears as `working`
124
- immediately without erasing the previous outcome. TaskChef does not store the
125
- transcript or a lifecycle event log.
122
+ TaskChef stores the current reported execution state and appends every concise
123
+ semantic result to `results`. A follow-up therefore appears as `working`
124
+ immediately without erasing any prior outcome. The final entry is exposed as a
125
+ derived `lastResult` compatibility alias; it is not persisted independently and
126
+ is planned for removal in the next major version after callers move to
127
+ `results.at(-1)`. TaskChef does not store transcripts or non-semantic events.
126
128
 
127
129
  Delegated tasks created by earlier TaskChef versions remain compatible: their
128
130
  inline executor protocol still parses, self-links, and may use the deprecated
@@ -172,7 +174,13 @@ taskchef dashboard --port 3211
172
174
  ```
173
175
 
174
176
  The loopback dashboard watches `tasks.jsonl`, groups current states, and opens
175
- linked Codex tasks. It does not mutate TaskChef data and prints its local URL.
177
+ linked Codex tasks. List snapshots and SSE events carry only the latest-result
178
+ projection; opening task details fetches the full newest-first result history.
179
+ Task and result times are relative through 29 days (with minute detail for the
180
+ first six hours), then use a locale-aware calendar date. Each time is a keyboard-
181
+ accessible toggle for its full locale-aware date and time, and one shared
182
+ 30-second timer keeps relative labels current without reloading the page.
183
+ It does not mutate TaskChef data and prints its local URL.
176
184
  When a compatible foreground dashboard already owns port 3210,
177
185
  `ensure_dashboard` reuses it but does not take ownership. If an unknown,
178
186
  different-workspace, or stale-version process owns the port, TaskChef reports a
@@ -183,6 +191,8 @@ The health endpoint contains only a fixed service marker, health schema,
183
191
  TaskChef version, dashboard-server version, and canonical workspace. It exposes
184
192
  no task data, credentials, environment variables, process control, or secrets.
185
193
 
194
+ ![Task detail result history](docs/images/result-history-dashboard.jpg)
195
+
186
196
  ## Common recovery
187
197
 
188
198
  Check the managed workspace:
@@ -190,20 +200,27 @@ Check the managed workspace:
190
200
  ```sh
191
201
  taskchef doctor
192
202
  taskchef workspace init
203
+ taskchef workspace migrate
193
204
  taskchef doctor
194
205
  ```
195
206
 
196
- `doctor` is read-only. `workspace init` creates missing current-schema files
197
- and refreshes managed instructions; it does not migrate unsupported
198
- configuration or task records.
207
+ `doctor` is read-only. `workspace init` creates missing files and refreshes
208
+ managed instructions. `workspace migrate` explicitly upgrades supported schema
209
+ 4/5 task lines to schema 6 under the workspace lock. It validates the complete
210
+ source and converted log before writing, creates an exclusive `tasks.jsonl.pre-v6-*.bak`
211
+ backup, atomically replaces the log, validates the result, and becomes an
212
+ idempotent no-op after migration. If replacement fails, the original remains
213
+ or the reported backup can be restored; unsupported or invalid input is rejected
214
+ before a backup or rewrite.
199
215
 
200
216
  If a new record has no thread ID, the executor is link-pending. Reopen that
201
217
  executor so its first action can retry `link_task`. Do not guess an identity
202
218
  or edit `tasks.jsonl`. If native task creation failed, the record is retained
203
219
  as `failed` with null thread and turn IDs.
204
220
 
205
- If an unsupported workspace must be retained, keep it as a backup and create a
206
- new current workspace. TaskChef provides no conversion or merge command.
221
+ Schemas other than 4, 5, and 6 remain unsupported. Retain such a workspace
222
+ unchanged and create a current workspace; the migration command deliberately
223
+ does not guess how to convert unknown formats.
207
224
 
208
225
  ## Boundaries
209
226
 
@@ -0,0 +1,3 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
2
+ <path fill="currentColor" d="M3 1.5h10A1.5 1.5 0 0 1 14.5 3v10a1.5 1.5 0 0 1-1.5 1.5H3A1.5 1.5 0 0 1 1.5 13V3A1.5 1.5 0 0 1 3 1.5Zm0 1a.5.5 0 0 0-.5.5v10a.5.5 0 0 0 .5.5h10a.5.5 0 0 0 .5-.5V3a.5.5 0 0 0-.5-.5H3Zm1.15 3.1.7-.7L7.95 8l-3.1 3.1-.7-.7L6.55 8l-2.4-2.4ZM8 10h4v1H8v-1Z"/>
3
+ </svg>
package/docs/spec.md CHANGED
@@ -23,7 +23,8 @@ is dated research, not contract.
23
23
  | **Self-linking** | The executor's one-way registration of its own canonical Codex UUIDv7 from `CODEX_THREAD_ID`. |
24
24
  | **Link-pending** | A working task whose `threadId` is null and `updatedBy` is `dispatcher`. |
25
25
  | **Current execution state** | The latest reported executor turn and its `working`, `needs_input`, `completed`, or `failed` status. |
26
- | **Last semantic result** | The most recent `completed`, `needs_input`, or `failed` outcome, preserved separately while a newer turn is working. |
26
+ | **Result history** | The ordered collection of every accepted `completed`, `needs_input`, or `failed` per-turn outcome. |
27
+ | **Last semantic result** | The final result-history entry, exposed through the derived `lastResult` compatibility alias. |
27
28
  | **Current turn ID** | The canonical Codex UUIDv7 returned by an exact native read of the linked executor for the turn being reported. |
28
29
  | **Dashboard** | The loopback, read-only UI derived from validated workspace snapshots and bounded native actions. |
29
30
  | **Skill** | One packaged agent procedure: `taskchef-bootstrap`, `taskchef-delegate`, `taskchef-executor`, or `taskchef-report`. |
@@ -67,10 +68,10 @@ Names and paths MUST be unique. Git projects MUST be exact Git roots.
67
68
  Repository URLs MUST canonicalize to `https://github.com/<owner>/<repository>`
68
69
  and be case-insensitively deduplicated.
69
70
 
70
- `tasks.jsonl` MUST contain zero or more newline-terminated schema-4 or schema-5
71
- records, one per line. Schema 4 is read compatibility for the previously
72
- released format; every new record and state mutation MUST write schema 5.
73
- Other schemas or unsupported fields MUST be rejected without conversion.
71
+ `tasks.jsonl` MUST contain zero or more newline-terminated schema-4, schema-5,
72
+ or schema-6 records, one per line. Schemas 4 and 5 are supported migration/read
73
+ formats; every new record and state mutation MUST write schema 6. Other schemas
74
+ or unsupported fields MUST be rejected without conversion.
74
75
  Reads and writes MUST reject symlinked managed files. Mutations
75
76
  MUST hold the shared workspace lock and replace state atomically; read-only
76
77
  operations MUST NOT require write permission.
@@ -81,7 +82,7 @@ Every record MUST contain exactly these fields:
81
82
 
82
83
  | Field | Contract |
83
84
  | --- | --- |
84
- | `schemaVersion` | Integer `5`; schema-4 records remain readable until their next mutation. |
85
+ | `schemaVersion` | Integer `6`; schema-4/5 records remain readable until explicit migration or their next mutation. |
85
86
  | `id` | Unique safe TaskChef ID; delegation uses a lowercase full UUID. |
86
87
  | `project` | Immutable configured-project snapshot. |
87
88
  | `title` | Non-empty display title. |
@@ -93,7 +94,13 @@ Every record MUST contain exactly these fields:
93
94
  | `turnId` | Null before turn reporting; otherwise the current reported turn. Linked MCP journeys use a canonical Codex UUIDv7. |
94
95
  | `updatedAt` | ISO 8601 timestamp not earlier than `createdAt` or the prior `updatedAt`; clock rollback cannot backdate a transition. |
95
96
  | `updatedBy` | `dispatcher` or `mcp`. |
96
- | `lastResult` | Null before a semantic result; otherwise `{status, summary, turnId, updatedAt}` preserving the latest semantic result. |
97
+ | `results` | Ordered oldest-first array of `{status, summary, turnId, updatedAt}` semantic results, with unique turn IDs and nondecreasing timestamps. |
98
+
99
+ Returned Task objects MUST additionally expose `lastResult` as null for an empty
100
+ history or the final `results` entry. `lastResult` MUST be derived and MUST NOT
101
+ be persisted in schema 6. It is a compatibility alias for schema-4/5, CLI,
102
+ MCP, reporting-skill, and dashboard-list callers, with removal planned for the
103
+ next major version after callers migrate to `results.at(-1)`.
97
104
 
98
105
  Task IDs and non-null thread identities MUST be unique. The immutable intent
99
106
  fields MUST NOT change after recording.
@@ -231,7 +238,8 @@ new preparation values, though it writes no state.
231
238
 
232
239
  **Structured output:** `{ task: Task }`.
233
240
 
234
- The returned task has schema 5, `working`, null summary/turn/thread/lastResult,
241
+ The returned task has schema 6, `working`, null summary/turn/thread/lastResult,
242
+ an empty `results` array,
235
243
  `updatedBy: dispatcher`, and equal creation/update timestamps. Duplicate IDs,
236
244
  unknown projects, malformed markers, and invalid input fail. Repeating a
237
245
  successful call is not idempotent; it fails as a duplicate.
@@ -260,7 +268,7 @@ marker, or ineligible state fails.
260
268
  ### `report_state`
261
269
 
262
270
  **Caller:** executor, or dispatcher only for native creation failure.
263
- **Mutation:** replaces the current state atomically and preserves `lastResult`.
271
+ **Mutation:** replaces the current state atomically and preserves `results`.
264
272
 
265
273
  **Input:**
266
274
 
@@ -279,8 +287,10 @@ the current turn and last semantic result. A semantic state MUST match the
279
287
  current working turn. Repeating an identical state is idempotent; conflicting
280
288
  or older state fails. A null-identity record accepts only a fresh executor
281
289
  creation `failed` state with both IDs null. Success sets the current state and
282
- preserves the semantic state in `lastResult`; starting newer work does not erase
283
- that result.
290
+ appends a semantic result to `results`; starting newer work preserves the entire
291
+ collection. An identical retry for any settled turn returns success without an
292
+ append. A different result for a settled turn, a stale turn, or a semantic
293
+ result that does not match the active working turn MUST fail.
284
294
 
285
295
  **Annotations:** `readOnlyHint: false`, `destructiveHint: true`,
286
296
  `openWorldHint: false`.
@@ -289,10 +299,10 @@ that result.
289
299
 
290
300
  `report_result` retains the prior semantic-only input shape and statuses as a
291
301
  temporary compatibility alias. It implicitly accepts a fresh supplied turn and
292
- stores its semantic result, including for supported schema-4 records and
302
+ stores its semantic result, including for supported schema-4/5 records and
293
303
  low-level opaque direct records. It does not accept `working`. New executor
294
- instructions MUST use `report_state`. Successful mutation upgrades schema 4 to
295
- schema 5; unsupported schemas remain rejected.
304
+ instructions MUST use `report_state`. Successful mutation upgrades schema 4/5
305
+ to schema 6; unsupported schemas remain rejected.
296
306
 
297
307
  ## Reporting and dashboard
298
308
 
@@ -302,6 +312,7 @@ state overrides cache. An inactive task does not prove completion. Focused
302
312
  reports MAY read a selected task once when metadata is newer or evidence is
303
313
  uncertain. Reports MUST NOT poll or classify assistant prose.
304
314
 
315
+ Task lists, summaries, and broad reports MUST use the final result by default.
305
316
  The dashboard MUST bind only to loopback, validate the current workspace
306
317
  snapshot, and avoid sessions or shared client state. `GET /api/health` MUST
307
318
  return only the bounded service identity, health schema, exact TaskChef and
@@ -313,6 +324,21 @@ startup safely. Direct thread navigation
313
324
  MUST require a canonical Codex UUIDv7. Otherwise it MAY open the revalidated
314
325
  configured project. Project paths from task history MUST be matched against
315
326
  current configuration before use.
327
+ Snapshot and SSE list payloads MUST omit full `results` history and include the
328
+ derived latest-result projection. The bounded per-task detail endpoint MAY
329
+ return the full validated task so the dialog can render history newest first.
330
+
331
+ ## Task-log migration
332
+
333
+ `workspace migrate` MUST explicitly convert every supported schema-4/5 record
334
+ under the shared lock. A legacy `lastResult`, or a schema-4 current semantic
335
+ state, becomes zero or one initial `results` entry. Migration MUST validate the
336
+ complete source and complete schema-6 candidate before changing the task log,
337
+ create and read back an exclusive recovery backup, atomically replace the log,
338
+ and validate the installed result. A fully schema-6 log MUST be an idempotent
339
+ no-op without another backup. Invalid/unsupported input MUST remain untouched;
340
+ failures after backup creation MUST report the backup path and MUST never
341
+ partially rewrite individual lines.
316
342
 
317
343
  ## Concurrency and trust
318
344
 
package/docs/workflows.md CHANGED
@@ -18,7 +18,7 @@ research.
18
18
  | `src/delegation.js` | UUID marker, concise executor-skill invocation shape, and creation-failure handling. |
19
19
  | `src/workspace.js` | Current schemas, validation, locking, atomic JSONL writes, linking, and result freshness. |
20
20
  | `src/cli.js` | Administration, inspection, diagnostics, and dashboard startup. |
21
- | `src/dashboard.js` | Versioned health identity, validated snapshots, SSE fan-out, and bounded open actions. |
21
+ | `src/dashboard.js` | Versioned health identity, validated compact snapshots, SSE fan-out, on-demand details, and bounded open actions. |
22
22
  | `src/dashboard-manager.js` | Concurrent singleton ensure, exact listener reuse, conflicts, and owned shutdown. |
23
23
 
24
24
  The MCP process resolves `TASKCHEF_WORKSPACE` once and never accepts a model
@@ -89,7 +89,7 @@ sequenceDiagram
89
89
  D->>D: Choose one configured and native project
90
90
  D->>M: record_task(id, project, title, instruction, null)
91
91
  M->>W: recordTask()
92
- W->>W: Lock, validate, append schema-5 snapshot
92
+ W->>W: Lock, validate, append schema-6 snapshot
93
93
  W-->>M: working link-pending task
94
94
  M-->>D: task
95
95
  D->>C: Create executor with marked instruction
@@ -116,8 +116,8 @@ semantic callbacks.
116
116
  ## State reporting
117
117
 
118
118
  The executor obtains the turn identity from an exact native read of its own
119
- linked task. `report_state` records live turn state while preserving the last
120
- semantic result separately.
119
+ linked task. `report_state` records live turn state while preserving the ordered
120
+ semantic result history.
121
121
 
122
122
  ```mermaid
123
123
  sequenceDiagram
@@ -130,13 +130,13 @@ sequenceDiagram
130
130
  C-->>E: Current turn ID
131
131
  E->>M: report_state(..., working, null)
132
132
  M->>W: reportTaskState()
133
- W->>W: Store current turn and preserve lastResult
133
+ W->>W: Store current turn and preserve results
134
134
  E->>E: Work, finish, or reach semantic decision
135
135
  E->>M: report_state(..., semantic status, summary)
136
136
  M->>W: reportTaskState()
137
137
  W->>W: Lock and validate identity and freshness
138
138
  alt Same current working turn
139
- W->>W: Store semantic state and lastResult
139
+ W->>W: Append semantic result and derive lastResult
140
140
  W-->>M: Updated task
141
141
  M-->>E: Recorded result
142
142
  else Same turn and same result
@@ -173,9 +173,9 @@ sequenceDiagram
173
173
  C-->>E: turnB
174
174
  E->>M: report_state(..., turnB, working, null)
175
175
  M->>W: reportTaskState()
176
- W->>W: Require turnB greater and preserve result A
177
- W-->>M: working snapshot plus lastResult A
178
- M-->>E: working snapshot plus lastResult A
176
+ W->>W: Require turnB greater and preserve results
177
+ W-->>M: working snapshot plus history A
178
+ M-->>E: working snapshot plus history A
179
179
  E->>M: report_state(..., turnB, completed, summaryB)
180
180
  M->>W: reportTaskState()
181
181
  W-->>M: completed snapshot plus result B
@@ -263,7 +263,9 @@ sequenceDiagram
263
263
  F-->>D: Filesystem change
264
264
  D->>F: Bounded read from one descriptor
265
265
  D->>D: Validate current schema and sort
266
- D-->>B: SSE snapshot
266
+ D-->>B: Compact SSE snapshot without results
267
+ B->>D: GET task detail on demand
268
+ D-->>B: Full validated history
267
269
  B->>D: Open task action
268
270
  alt Canonical Codex UUIDv7
269
271
  D->>C: Direct thread navigation
@@ -273,10 +275,25 @@ sequenceDiagram
273
275
  end
274
276
  ```
275
277
 
276
- The dashboard binds to `127.0.0.1`, has no shared session state, limits
277
- request bodies, and checks origin/authority for stateful local actions. Its
278
- bounded identity endpoint contains no task data or secrets. Historical project
279
- paths are untrusted until matched against current configuration.
278
+ The dashboard binds to `127.0.0.1`, has no shared session state, limits task
279
+ count, file size, result count, and display fields, and checks origin/authority
280
+ for stateful local actions. Its bounded identity endpoint contains no task data
281
+ or secrets. The monitor already validates the complete log, but
282
+ snapshot/SSE list projections omit `results` so repeated updates do not resend
283
+ unnecessary history. A read-only per-task endpoint returns full history only
284
+ when the dialog opens. Historical project paths are untrusted until matched
285
+ against current configuration.
286
+
287
+ ## Schema 4/5 migration
288
+
289
+ `taskchef workspace migrate` acquires the same workspace lock as lifecycle
290
+ writers, validates the complete legacy log, converts each schema-4/5 latest
291
+ result into zero or one initial schema-6 `results` entry, then validates the
292
+ complete candidate. Before replacement it writes and reads back an exclusive
293
+ `tasks.jsonl.pre-v6-*.bak` file. The task log is replaced atomically and
294
+ validated again. A second run sees only schema 6 and returns unchanged without
295
+ another backup. Unsupported or malformed input fails before backup/rewrite;
296
+ after a later filesystem failure, the reported backup is the recovery source.
280
297
 
281
298
  ## Concurrency and trust boundaries
282
299
 
@@ -291,6 +308,7 @@ summary is cryptographically authenticated; this is a local single-user trust
291
308
  model. Managed files, instructions, project snapshots, MCP inputs, and dashboard
292
309
  requests are validated at every action boundary.
293
310
 
294
- Configuration schema 2 and task schemas 4 and 5 are accepted. Schema 4 is
295
- read-only compatibility until a lifecycle mutation upgrades that record to
296
- schema 5. Other schemas are rejected without rewrite.
311
+ Configuration schema 2 and task schemas 4, 5, and 6 are accepted. Schemas 4/5
312
+ are read/migration compatibility until an explicit migration or lifecycle
313
+ mutation upgrades each record to schema 6. Schema 6 persists `results` only and
314
+ derives `lastResult` for compatibility. Other schemas are rejected without rewrite.
package/index.js CHANGED
@@ -10,6 +10,7 @@ export {
10
10
  initializeWorkspace,
11
11
  linkTask,
12
12
  listProjects,
13
+ migrateTaskLog,
13
14
  prepareDispatch,
14
15
  readConfig,
15
16
  listTasks,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "taskchef",
3
- "version": "7.4.0",
3
+ "version": "7.6.0",
4
4
  "description": "A non-blocking interactive dispatcher for visible Codex tasks.",
5
5
  "license": "MIT",
6
6
  "author": "Favo Yang",
@@ -26,6 +26,7 @@
26
26
  "docs/spec.md",
27
27
  "docs/workflows.md",
28
28
  "docs/firstmate-taskchef-comparison.md",
29
+ "docs/images/result-history-dashboard.jpg",
29
30
  "index.js",
30
31
  "mcp",
31
32
  "scripts/benchmark-dispatch-prepare.js",
@@ -44,14 +44,16 @@ all deterministic task-log operations.
44
44
  detailed read. Native approval is live Codex state, not a `needs_input`
45
45
  callback. An inactive status never proves semantic completion; it only
46
46
  permits a trustworthy cached MCP result to stand.
47
- 4. In schema 5, treat `status`, `turnId`, and `updatedAt` as the latest reported
48
- execution state and treat `lastResult` as the separately preserved semantic
49
- result. A `working` state with a non-null `lastResult` means a newer executor
47
+ 4. In schema 6, treat `status`, `turnId`, and `updatedAt` as the latest reported
48
+ execution state and treat `results` as the ordered semantic history.
49
+ `lastResult` is the derived compatibility alias for `results.at(-1)`; prefer
50
+ the collection when full history matters and the alias for compact latest-state
51
+ reporting. A `working` state with a non-null `lastResult` means a newer executor
50
52
  turn started after that result; show the prior result as history, not as the
51
53
  current outcome. Treat a failed `lastResult` with null thread and turn IDs as
52
- a fresh executor-creation failure. No live read is possible or needed.
53
- Schema 4 snapshots normalize a structurally complete result into
54
- `lastResult` without rewriting their log line. When identity is certain and
54
+ a fresh executor-creation failure. Schema 4/5 snapshots normalize zero or one
55
+ semantic result into `results` and `lastResult` without rewriting their log line.
56
+ No live read is possible or needed for that creation failure. When identity is certain and
55
57
  metadata says the thread is inactive, trust the latest semantic result by
56
58
  default in a broad overview unless a newer working state makes it historical.
57
59
  Do not read every idle terminal task in an overview merely because native
package/src/cli.js CHANGED
@@ -14,6 +14,7 @@ import {
14
14
  initializeWorkspace,
15
15
  listProjects,
16
16
  listTasks,
17
+ migrateTaskLog,
17
18
  prepareDispatch,
18
19
  recordTask,
19
20
  removeProject,
@@ -182,11 +183,28 @@ function taskDetails(task) {
182
183
  `Updated by: ${singleLineDetail(task.updatedBy ?? "-")}`,
183
184
  `Task ID: ${singleLineDetail(task.id)}`,
184
185
  `Thread ID: ${singleLineDetail(task.threadId ?? "-")}`,
186
+ `Result count: ${task.results.length}`,
187
+ "Result history (newest first):",
188
+ ...[...task.results].reverse().map((result) => (
189
+ `- ${singleLineDetail(result.updatedAt)} | ${singleLineDetail(result.status)} | turn ${singleLineDetail(result.turnId ?? "-")} | ${singleLineDetail(result.summary)}`
190
+ )),
185
191
  "Instruction:",
186
192
  task.instruction,
187
193
  ].join("\n");
188
194
  }
189
195
 
196
+ async function migrate(args) {
197
+ validateCommandArgs(args, 2, { values: ["--workspace"], switches: ["--json"] });
198
+ const result = await migrateTaskLog(workspaceRoot(args));
199
+ print(result, args, (value) => [
200
+ `Task log: ${value.action}`,
201
+ `Tasks: ${value.taskCount}`,
202
+ `Migrated: ${value.migratedCount}`,
203
+ `Backup: ${value.backupPath ?? "not needed"}`,
204
+ ].join("\n"));
205
+ return 0;
206
+ }
207
+
190
208
  async function readTaskForShow(workspace, taskId) {
191
209
  const id = requireSafeId(taskId, "taskId");
192
210
  const tasks = await listTasks(workspace);
@@ -456,6 +474,7 @@ Usage:
456
474
  taskchef doctor [--json] [--workspace <path>]
457
475
  taskchef workspace path [--json] [--workspace <path>]
458
476
  taskchef workspace init [--register-codex] [--codex-cli <path>] [--json] [--workspace <path>]
477
+ taskchef workspace migrate [--json] [--workspace <path>]
459
478
  taskchef project add <path> [--name <name>] [--description <text>] [--github-repo <url> ... | --no-github] [--json] [--workspace <path>]
460
479
  taskchef project import [<file> | -] [--replace] [--json] [--workspace <path>]
461
480
  taskchef project list [--json] [--workspace <path>]
@@ -489,6 +508,7 @@ export async function runCli(args) {
489
508
  if (args[0] === "doctor") return doctor(args);
490
509
  if (args[0] === "workspace" && args[1] === "path") return workspacePath(args);
491
510
  if (args[0] === "workspace" && args[1] === "init") return initialize(args);
511
+ if (args[0] === "workspace" && args[1] === "migrate") return migrate(args);
492
512
  if (args[0] === "project" && args[1] === "add") return projectAdd(args);
493
513
  if (args[0] === "project" && args[1] === "import") return projectImport(args);
494
514
  if (args[0] === "project" && args[1] === "list") return projectList(args);