taskchef 4.0.0 → 4.1.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": "4.0.0",
3
+ "version": "4.1.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
@@ -253,6 +253,17 @@ taskchef project list --workspace <workspace>
253
253
  taskchef project remove payments --workspace <workspace>
254
254
  ```
255
255
 
256
+ Human-readable project listings use one row per GitHub repository. Project
257
+ details repeat on each row so multi-repository projects remain clear, while a
258
+ project without a configured repository has one row containing `-`:
259
+
260
+ ```text
261
+ NAME KIND GITHUB REPOSITORY PATH
262
+ payments git https://github.com/example/payments-api /workspace/payments
263
+ payments git https://github.com/example/payments-sdk /workspace/payments
264
+ notes folder - /workspace/notes
265
+ ```
266
+
256
267
  Import projects as a JSON array from a file or standard input:
257
268
 
258
269
  ```sh
@@ -296,9 +307,20 @@ Inspect the task history without querying Codex tasks:
296
307
  taskchef task show t1 --workspace <workspace>
297
308
  taskchef task list --workspace <workspace>
298
309
  taskchef task list --project payments --workspace <workspace>
310
+ taskchef task list --ascending --workspace <workspace>
299
311
  taskchef task summary --workspace <workspace>
300
312
  ```
301
313
 
314
+ Human-readable task listings put the scannable fields first and the durable ID
315
+ last. Values are kept in full and aligned in columns. Tasks are newest-first by
316
+ default; pass `--ascending` to list them from oldest to newest. The same order
317
+ applies to the `tasks` array in `--json` output.
318
+
319
+ ```text
320
+ TITLE PROJECT CREATED ID
321
+ Add retry logs payments 2026-08-12T10:00:00.000Z c0f010ff-84f2-4838-a69d-0ff1f5d721d7
322
+ ```
323
+
302
324
  The complete data contract is in [SPEC.md](SPEC.md). Deferred ideas are in
303
325
  [BACKLOG.md](BACKLOG.md).
304
326
 
package/SPEC.md CHANGED
@@ -201,8 +201,9 @@ it was not recorded.
201
201
  The CLI reads persisted history without contacting Codex:
202
202
 
203
203
  - `task show <id>` returns one entry.
204
- - `task list` returns entries in append order, optionally filtered by
205
- historical project name or exact path.
204
+ - `task list` returns entries newest-first by creation time, optionally filtered
205
+ by historical project name or exact path. `--ascending` returns oldest-first.
206
+ The selected order applies to both human rows and the JSON `tasks` array.
206
207
  - `task summary` returns the total and per-project counts.
207
208
  - `task resolve <id> --thread-id <thread-id>` atomically fills one nullable
208
209
  thread ID after Codex verifies the exact structured marker match.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "taskchef",
3
- "version": "4.0.0",
3
+ "version": "4.1.0",
4
4
  "description": "A non-blocking interactive dispatcher for visible Codex tasks.",
5
5
  "license": "MIT",
6
6
  "author": "Favo Yang",
package/src/cli.js CHANGED
@@ -88,12 +88,44 @@ function print(value, args, human) {
88
88
  }
89
89
 
90
90
  function table(headers, rows) {
91
+ const display = (value) => value === null || value === undefined || value === ""
92
+ ? "-"
93
+ : String(value);
91
94
  const widths = headers.map((header, index) =>
92
- Math.max(header.length, ...rows.map((row) => String(row[index]).length)));
93
- const format = (row) => row.map((value, index) => String(value).padEnd(widths[index])).join(" ");
95
+ Math.max(header.length, ...rows.map((row) => display(row[index]).length)));
96
+ const format = (row) => row.map((value, index) => index === row.length - 1
97
+ ? display(value)
98
+ : display(value).padEnd(widths[index])).join(" ");
94
99
  return [format(headers), ...rows.map(format)].join("\n");
95
100
  }
96
101
 
102
+ function projectRows(projects) {
103
+ return projects.flatMap((project) => {
104
+ const repositories = project.githubRepos.length > 0 ? project.githubRepos : [null];
105
+ return repositories.map((repository) => [
106
+ project.name,
107
+ project.isGitRepository ? "git" : "folder",
108
+ repository,
109
+ project.path,
110
+ ]);
111
+ });
112
+ }
113
+
114
+ function sortTasksByCreatedAt(tasks, ascending) {
115
+ return tasks
116
+ .map((task, index) => ({ task, index }))
117
+ .sort((left, right) => {
118
+ const leftCreatedAt = left.task.createdAt;
119
+ const rightCreatedAt = right.task.createdAt;
120
+ if (!leftCreatedAt && !rightCreatedAt) return left.index - right.index;
121
+ if (!leftCreatedAt) return 1;
122
+ if (!rightCreatedAt) return -1;
123
+ const chronological = Date.parse(leftCreatedAt) - Date.parse(rightCreatedAt);
124
+ return (ascending ? chronological : -chronological) || left.index - right.index;
125
+ })
126
+ .map(({ task }) => task);
127
+ }
128
+
97
129
  async function initialize(args) {
98
130
  validateCommandArgs(args, 2, { values: ["--workspace"], switches: ["--json"] });
99
131
  const result = await initializeWorkspace(workspaceRoot(args));
@@ -161,13 +193,8 @@ async function projectList(args) {
161
193
  validateCommandArgs(args, 2, { values: ["--workspace"], switches: ["--json"] });
162
194
  const projects = await listProjects(workspaceRoot(args));
163
195
  print({ projectCount: projects.length, projects }, args, (value) => table(
164
- ["NAME", "KIND", "GITHUB REPOSITORIES", "PATH"],
165
- value.projects.map((project) => [
166
- project.name,
167
- project.isGitRepository ? "git" : "folder",
168
- project.githubRepos.join(", ") || "-",
169
- project.path,
170
- ]),
196
+ ["NAME", "KIND", "GITHUB REPOSITORY", "PATH"],
197
+ projectRows(value.projects),
171
198
  ));
172
199
  return 0;
173
200
  }
@@ -215,19 +242,20 @@ async function taskShow(args) {
215
242
  async function taskList(args) {
216
243
  validateCommandArgs(args, 2, {
217
244
  values: ["--workspace", "--project"],
218
- switches: ["--json"],
245
+ switches: ["--ascending", "--json"],
219
246
  });
220
- const dispatches = await filterTasks(workspaceRoot(args), {
247
+ const filtered = await filterTasks(workspaceRoot(args), {
221
248
  project: option(args, "--project", null),
222
249
  });
250
+ const dispatches = sortTasksByCreatedAt(filtered, args.includes("--ascending"));
223
251
  const result = { taskCount: dispatches.length, tasks: dispatches };
224
252
  print(result, args, (value) => table(
225
- ["ID", "CREATED", "PROJECT", "TITLE"],
253
+ ["TITLE", "PROJECT", "CREATED", "ID"],
226
254
  value.tasks.map((dispatch) => [
227
- dispatch.id,
228
- dispatch.createdAt,
229
- dispatch.project.name,
230
255
  dispatch.title,
256
+ dispatch.project?.name,
257
+ dispatch.createdAt,
258
+ dispatch.id,
231
259
  ]),
232
260
  ));
233
261
  return 0;
@@ -257,7 +285,7 @@ Usage:
257
285
  taskchef task record [--json] [--workspace <path>]
258
286
  taskchef task resolve <task-id> --thread-id <thread-id> [--json] [--workspace <path>]
259
287
  taskchef task show <task-id> [--json] [--workspace <path>]
260
- taskchef task list [--project <name-or-path>] [--json] [--workspace <path>]
288
+ taskchef task list [--project <name-or-path>] [--ascending] [--json] [--workspace <path>]
261
289
  taskchef task summary [--json] [--workspace <path>]
262
290
 
263
291
  Task record reads JSON from standard input. Project import reads a JSON