taskchef 5.7.2 → 5.9.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-delegate
3
- description: "Dispatch actionable requests through the per-user TaskChef workspace into independently openable Codex project tasks. Use automatically for actionable work received in the canonical TaskChef dispatcher workspace. From any other project, use only when the user explicitly asks to delegate or split separate work into Codex tasks; TaskChef-related subject matter alone is not delegation intent. Preserve unresolved delegations for later marker-based recovery, and never use subagents, hooks, schedules, daemons, or executor-completion waiting."
3
+ description: "Dispatch actionable requests through the per-user TaskChef workspace into independently openable Codex project tasks. Use automatically for actionable work received in the canonical TaskChef dispatcher workspace. From any other project, use only when the user explicitly asks to delegate or split separate work into Codex tasks; TaskChef-related subject matter alone is not delegation intent. Record before creation, rely on the initial TaskChef hook for provisional identity, and never wait for executor completion."
4
4
  ---
5
5
 
6
6
  # TaskChef Delegate
@@ -19,7 +19,7 @@ This does not prevent the task from using TaskChef later. Use this skill
19
19
  normally when the initial assignment explicitly asks to delegate separate
20
20
  work, or when the user later explicitly requests a new delegation.
21
21
 
22
- Use the bundled TaskChef `prepare_dispatch`, `record_task`, and `resolve_task`
22
+ Use the bundled TaskChef `prepare_dispatch`, `record_task`, `resolve_task`, and `report_result`
23
23
  tools for deterministic workspace and task-record operations. Call them
24
24
  directly; never probe for them or fall back to shell CLI writes. If a required
25
25
  tool is unavailable, stop and report that the TaskChef plugin must be reloaded
@@ -32,27 +32,29 @@ explicitly requested benchmark artifact.
32
32
  - Keep only `AGENTS.md`, `taskchef.json`, and `tasks.jsonl` in a dispatcher
33
33
  workspace.
34
34
  - Use real Codex tasks, never collaboration or subagent tools.
35
- - Never use hooks, callbacks, schedules, daemons, indefinite polling, or
36
- background monitors.
37
- - Use only bounded provisional-ID resolution after `create_thread` returns a
38
- provisional client ID. Use the fixed marker-based workflow below.
35
+ - Use only TaskChef's `UserPromptSubmit` hook: it resolves initial identity and
36
+ provides read-only current-turn context on follow-up prompts. Never infer
37
+ lifecycle state from hooks.
38
+ - Never use schedules, daemons, polling, thread-discovery retries, or background
39
+ monitors.
39
40
  - Never wait for delegated work after executor creation.
40
41
  - Never collect transcripts or hidden reasoning.
41
42
 
42
43
  ## Dispatch
43
44
 
44
- 1. In parallel, call `prepare_dispatch` and list the native Codex projects
45
- once. The preparation tool resolves the
46
- canonical workspace, loads and validates the configured routing targets,
45
+ 1. Split the request into the smallest independently useful outcomes. Include
46
+ constraints, expected testing, and reporting in every instruction.
47
+ 2. In parallel, list the native Codex projects once and call `prepare_dispatch`
48
+ exactly once for each outcome. Every preparation produces a distinct task
49
+ UUID and marker; never reuse either across executors. The preparation tool
50
+ resolves the canonical workspace, loads and validates the configured routing targets,
47
51
  generates the lowercase full UUID task ID, and returns `preparedAt` plus the
48
52
  exact first-line marker. Use
49
53
  `$taskchef-bootstrap` if the workspace is missing or unhealthy.
50
- The tool resolves `TASKCHEF_WORKSPACE`, then
54
+ The tool resolves an absolute (or `~/`-prefixed) `TASKCHEF_WORKSPACE`, then
51
55
  `~/.agents/taskchef`; do not substitute the current project. Reject the
52
56
  dispatcher workspace itself as a target. Never take a pre-creation thread
53
57
  snapshot.
54
- 2. Split the request into the smallest independently useful outcomes. Include
55
- constraints, expected testing, and reporting in every instruction.
56
58
  3. Classify against configured `name`, every URL in the `githubRepos` list, and
57
59
  `description`. Use `path` only as checkout identity. Managed `*-workspace`
58
60
  projects advertise their child or sub-repositories in this list.
@@ -64,103 +66,59 @@ explicitly requested benchmark artifact.
64
66
  matches. Ask instead of guessing when no project or several projects match.
65
67
  4. Resolve the selected configured path against the already-loaded native
66
68
  projects and require an exact match. Do not list native projects again.
67
- 5. Use the task ID, preparation time, and marker returned by the preparation
68
- tool.
69
+ 5. For each outcome, use only its corresponding task ID, preparation time, and
70
+ marker returned by its own preparation call.
69
71
  Prefix the complete executor instruction with
70
72
  exactly `<!-- taskchef_id=<full UUID> -->` as the first line, followed by a
71
- blank line, this executor-role paragraph, another blank line, and the
72
- instruction body. Add the paragraph as one line in the created input:
73
+ blank line, this executor-role paragraph, another blank line, the result
74
+ paragraph below, another blank line, and the instruction body:
73
75
 
74
76
  > This task owns the delegated assignment. Execute it in this task; do not re-dispatch it merely because it concerns TaskChef or a configured project. Explicit requests to delegate separate work remain valid.
75
77
 
78
+ > Before ending, call the TaskChef report_result MCP tool with completed, needs_input, or failed and a concise summary. Use needs_input only for a semantic decision or information the user must provide; a native approval prompt is live Codex state, not a TaskChef result. Do not include secrets, transcripts, or raw command output.
79
+
76
80
  Preserve this
77
81
  marked instruction for recording, and note the creation time. The exact
78
82
  random marker is the sole correlation proof.
79
- 6. Create one real Codex task using the exact configured project, a local
83
+ 6. Before creating each executor, call `record_task` exactly once with `id`,
84
+ `project`, `title`, the marked `instruction`, and `threadId: null`. This
85
+ closes the hook race: the entry exists before the executor can submit its
86
+ initial prompt.
87
+ 7. Create one real Codex task using the exact configured project, a local
80
88
  environment on its executor host, the marked instruction, and a short title.
81
- 7. When `create_thread` returns a durable `threadId`, immediately call
82
- `record_task` once. Send exactly `id`,
83
- `project`, `title`, `instruction`, and `threadId`. Use the configured project
84
- path for `project`, and send the marked instruction unchanged.
85
- Never persist a provisional `clientThreadId` or
86
- `pendingWorktreeId` as `threadId`. Never persist `hostId`, status, results,
87
- transcripts, or hidden reasoning.
88
- 8. When creation returns only `clientThreadId` or `pendingWorktreeId`, keep it
89
- only for the created-thread directive and diagnostic reporting. It is not a
90
- durable ID and cannot be passed to thread tools or converted directly.
91
- Immediately record the marked instruction with `threadId: null` using the
92
- tool from step 7, then resolve the durable ID with this bounded workflow:
93
-
94
- - The supported Codex surface has no provisional-ID resolver. Do not inspect
95
- the tool surface for one after creation and do not pass the provisional ID
96
- to tools that require `threadId`.
97
- - Take at most two post-creation `list_threads` snapshots with limit 50. The
98
- nominal schedule is 10 and 30 seconds after
99
- the provisional result. Treat the first checkpoint as due, not expired: if
100
- nullable recording or other required work finishes after 10 seconds, take
101
- the first snapshot immediately. Start the second snapshot no sooner than
102
- 20 seconds after the first snapshot actually started. Useful candidate
103
- work counts toward that interval; when it finishes sooner, wait only the
104
- remainder. Never suppress either snapshot merely because an earlier step
105
- ran late, and never take a third snapshot.
106
- - Filter Codex candidates by the expected host, project, creation time
107
- (allow five seconds of clock skew), and worktree environment whenever
108
- those fields are present. Use the title only to prioritize reads; Codex
109
- may normalize it, so never exclude a candidate because its title differs.
110
- - Read every remaining candidate with `read_thread`, requesting one turn and
111
- no command output. Issue all independent reads together in exactly one
112
- programmatic batch per snapshot. Do not probe for batching support and do
113
- not fall back to serial tool-call round trips. When the required batch
114
- cannot be executed, preserve the nullable record as unresolved. Inspect
115
- only the
116
- structured
117
- `userMessage.content[].codexDelegation.input`; do not trust titles,
118
- summaries, previews, plain-text echoes, or assistant output as proof.
119
- - Accept a candidate only when the structured input's first line is exactly
120
- the task's `<!-- taskchef_id=<full UUID> -->` marker and exactly one
121
- candidate matches. Require an immediately following blank line. Reject old
122
- heading-style markers, malformed comments, missing blank separators, and
123
- marker-like text anywhere else. Reject any discovered thread ID equal to
124
- the provisional identifier or in its `local:` namespace. Then call
125
- `resolve_task` once to atomically fill the nullable field.
126
- - Treat snapshot, candidate-read, wait, and task-resolution
127
- failures as indeterminate. If the workflow ends with zero exact matches,
128
- multiple matches, or errors, leave the already-recorded `threadId: null`,
129
- clearly report the unresolved reason and provisional diagnostic ID, and
130
- never guess.
131
-
132
- 9. If executor creation fails, do not record a task. If recording fails
133
- after creation, still return the created task and clearly say that it is not
134
- in the task log. Do not delete the executor.
135
- 10. Return immediately after immediate recording or the bounded ID-resolution
136
- workflow. Emit the appropriate created-thread directive, but label a
137
- client-thread directive as provisional when resolution failed. Treat a
138
- nullable record as preserved but unresolved, not as a durable task link. Do
139
- not read an executor for progress and never wait for executor work
140
- completion.
89
+ 8. If creation returns a durable `threadId`, call `resolve_task` immediately.
90
+ If creation returns only `clientThreadId`, `pendingWorktreeId`, or a `local:`
91
+ ID, retain it only for the created-thread directive. Return immediately; the
92
+ initial TaskChef hook will atomically fill the durable root thread ID. Never
93
+ list, read, wait for, or poll threads to resolve it.
94
+
95
+ 9. If executor creation fails, call `report_result` for the already-recorded
96
+ task with `failed`, null thread/turn IDs, and a concise creation error.
97
+ 10. Emit the appropriate created-thread directive and return immediately.
98
+ Treat a nullable record as preserved but not yet linked. Do not read an
99
+ executor for progress and never wait for executor work completion.
141
100
 
142
101
  The bundled structured tools are the only dispatch-time path to the canonical
143
102
  workspace. They reuse the same exact-field validation, locking, atomic writes,
144
103
  and one-way nullable resolution as the CLI without shell parsing, stdin, or
145
- per-command filesystem escalation. The package exports pure repository
146
- canonicalization and unique URL matching helpers from `src/github.js`, plus
147
- marker, candidate-filtering, and injected-adapter orchestration helpers from
148
- `src/delegation.js`, for deterministic tests and hosts that can supply
149
- thread-tool callbacks. The standalone Node CLI cannot call desktop thread
150
- tools; perform thread discovery in Codex. Keep the CLI for bootstrap, manual
151
- inspection, and recovery outside delegation.
104
+ per-command filesystem escalation. The package exports repository routing,
105
+ marker, and record-before-create orchestration helpers for deterministic tests.
106
+ Keep the CLI for bootstrap, manual inspection, and recovery outside delegation.
152
107
 
153
108
  ## Later resolution
154
109
 
155
- When a later Codex workflow finds exactly one durable thread whose structured
156
- delegated input contains an unresolved task's exact marker, call `resolve_task`
157
- once. Never edit `tasks.jsonl` directly. The tool permits only an idempotent
158
- one-way transition from `threadId: null` to one unique durable thread ID.
110
+ The trusted initial hook normally resolves an unresolved task. For manual
111
+ recovery, require one exact structured marker match, then call `resolve_task`
112
+ once. Never edit `tasks.jsonl` directly. Resolution is an idempotent one-way
113
+ transition from `threadId: null` to one unique durable root thread ID.
159
114
 
160
- ## End-to-end evaluation
115
+ ## Legacy benchmark compatibility
161
116
 
162
- When explicitly asked to benchmark delegation, measure one real task without
163
- waiting for executor completion. Start from
117
+ The schema-v1 benchmark fixtures below describe the removed snapshot resolver
118
+ and remain only for historical-result validation. Do not use that resolver for
119
+ new delegation benchmarks. A future benchmark schema should measure record,
120
+ creation, initial-hook resolution, and result callback without polling. The
121
+ legacy fixture starts from
164
122
  `<plugin-root>/assets/e2e-benchmark-example.json`. Capture one ISO start/end
165
123
  interval for the parallel preparation/project-list operation, followed by
166
124
  sequential non-overlapping creation, recording, and optional provisional
@@ -1,12 +1,12 @@
1
1
  ---
2
2
  name: taskchef-report
3
- description: "Report the live state of Codex tasks recorded in a TaskChef task history. Use only when the user asks for status, outcomes, or a report about delegated work. Queries each relevant task once, may resolve a nullable thread ID from one exact marker match, never polls or waits, and never persists status or results."
3
+ description: "Report useful current state for Codex tasks recorded by TaskChef. Use only when the user asks for delegated-task status, outcomes, or a report. Prefer cached semantic results, filter old terminal tasks from overviews, use one cheap live metadata snapshot to override active or approval-waiting tasks, and reserve detailed reads for anomalies. Never poll or wait."
4
4
  ---
5
5
 
6
6
  # TaskChef Report
7
7
 
8
- Read the canonical per-user TaskChef task history and report the current state
9
- of its Codex tasks once.
8
+ Read the canonical per-user TaskChef task snapshots and report useful current
9
+ state once. A stored result is cached semantic evidence, not permanent truth.
10
10
 
11
11
  Resolve this skill directory with `realpath`. The TaskChef plugin root is two
12
12
  parents above the skill directory. Invoke `<plugin-root>/bin/taskchef.js` for
@@ -15,7 +15,8 @@ all deterministic task-log operations.
15
15
  ## Report
16
16
 
17
17
  1. Run `<plugin-root>/bin/taskchef.js workspace path --json`. The CLI resolves
18
- `--workspace`, then `TASKCHEF_WORKSPACE`, then `~/.agents/taskchef`; never
18
+ `--workspace`, then an absolute (or `~/`-prefixed) `TASKCHEF_WORKSPACE`, then
19
+ `~/.agents/taskchef`; never
19
20
  infer the history from the current project. Select only the tasks the user
20
21
  asked about:
21
22
  - For an exact task ID, run
@@ -26,28 +27,60 @@ all deterministic task-log operations.
26
27
  `<plugin-root>/bin/taskchef.js task list --json`
27
28
  once, then select matching entries. Ask the user if the match is ambiguous.
28
29
  - Use the full list only when the user asks for an overview of the task history.
29
- 2. Separate entries whose `threadId` is `null`. For those entries, take one
30
- `list_threads` snapshot with limit 50, filter by available project metadata,
31
- and inspect candidate structured delegated inputs. Use title only to
32
- prioritize candidates, never to exclude them. Require the exact first line
33
- `<!-- taskchef_id=<full lowercase UUID> -->` and an immediately following
34
- blank line; reject old heading-style markers, malformed comments, and missing
35
- blank separators. When exactly one candidate has that exact prefix, run
36
- `<plugin-root>/bin/taskchef.js task resolve <task-id> --thread-id <thread-id> --json`.
37
- Do not resolve zero or multiple matches. Report unmatched entries as
38
- recorded but unresolved and do not pass them to native thread tools.
39
- 3. Query every resolved or previously durable thread exactly once using
40
- immediate native snapshots, with no more than eight targets per call.
41
- 4. Summarize the live state and any reported outcome for each requested task.
42
- Distinguish active work, requests for user input, completed work, and failed
43
- or partial attempts.
44
- 5. Treat each Codex task as the source of truth. The task log records what
45
- TaskChef submitted, but it does not contain the task's current state.
46
- 6. Never edit `tasks.jsonl` directly. Use `task resolve` only for one exact
47
- marker match. Never persist status, results, transcripts, or hidden
48
- reasoning. Do not poll or wait for future activity.
30
+ 2. For an overview, select only attention-worthy candidates before detailed
31
+ reads:
32
+ - always include `working`, `needs_input`, null-status legacy entries, and
33
+ entries with a null `threadId`;
34
+ - include `completed` and `failed` entries updated during the last seven
35
+ days;
36
+ - omit older terminal entries by default and report the omitted count;
37
+ - include any omitted task whose Codex metadata appears as active or awaiting
38
+ native approval in the recent-thread snapshot.
39
+ Explicit task, title, or project requests override this age filter.
40
+ 3. Take one recent `list_threads` metadata snapshot for the whole report. This
41
+ is a cheap contradiction check across many tasks, not one call per task.
42
+ Match only exact durable `threadId` values. A metadata status that is active
43
+ or awaiting native approval immediately overrides a cached result without a
44
+ detailed read. Native approval is live Codex state, not a `needs_input`
45
+ callback. An inactive status never proves semantic completion; it only
46
+ permits a trustworthy cached MCP result to stand.
47
+ 4. Treat `updatedBy: mcp`, `status: failed`, and null thread/turn IDs as a fresh
48
+ executor-creation failure. No live read is possible or needed; report the
49
+ stored failure summary, not unresolved. Otherwise, only a snapshot with
50
+ `updatedBy: mcp` is a cached semantic result. A
51
+ dispatcher- or hook-written `working` snapshot has no semantic callback and
52
+ requires one live task query when selected; if the task is inactive and no
53
+ callback exists, report the outcome as unknown rather than treating
54
+ `working` as fresh. When identity is certain and metadata says the thread is
55
+ inactive, trust the latest MCP result by default in a broad overview. Do not
56
+ read every idle terminal task in an overview merely because native
57
+ `updatedAt` is later: callbacks normally run before Codex finalizes the same
58
+ turn, and overview performance matters more than investigating every rare
59
+ missed callback.
60
+
61
+ For a focused task, title, or project report, perform at most one detailed
62
+ read for each selected inactive task when matched metadata `updatedAt` is
63
+ later than the cached result `updatedAt`, by any amount. Read once as well
64
+ when there is no semantic callback, identity or metadata is uncertain or
65
+ contradictory, or the user explicitly requests a fully live result. If
66
+ focused metadata is not newer, trust the cache. Absence from the bounded
67
+ recent snapshot is not by itself a reason to read every cached terminal
68
+ overview entry. Batch immediate native reads with no more than eight targets
69
+ per call. When a detailed read occurs, compare the latest structured turn ID
70
+ and native turn state with stored `turnId`: a newer turn without a callback
71
+ makes the cache stale, while an interrupted or cancelled callback turn
72
+ cannot prove completion. Never classify assistant prose.
73
+ 5. Report each task as one of: working, needs input, awaiting native approval,
74
+ completed, failed, unresolved, or unknown. Show the cached summary when it
75
+ remains fresh. If a newer turn exists without a callback, describe the live
76
+ state and label the cached result stale rather than overwriting it.
77
+ 6. Never edit `tasks.jsonl` directly during reporting. The initial hook normally
78
+ resolves null identity; manual recovery may call `task resolve` only after
79
+ one exact structured marker match. Never persist inferred status,
80
+ transcripts, prose classifications, or hidden reasoning. Do not poll or wait.
49
81
 
50
82
  If the task history is empty, say that TaskChef has not recorded any tasks. If
51
- a task has no durable thread ID, identify it by task ID and say that its marker
52
- remains available for later recovery. If a recorded thread cannot be read,
53
- identify it by task ID and thread ID, then continue with the remaining entries.
83
+ a task has no durable thread ID and is not a stored creation failure, identify
84
+ it by task ID and say that its marker remains available for recovery. If live
85
+ metadata or a targeted read fails, use the cached result with an explicit
86
+ freshness warning and continue.
package/src/cli.js CHANGED
@@ -2,6 +2,7 @@ import { access, readFile, realpath } from "node:fs/promises";
2
2
  import path from "node:path";
3
3
 
4
4
  import { openWorkspaceInCodex } from "./codex-app.js";
5
+ import { createDashboardServer } from "./dashboard.js";
5
6
  import { resolveWorkspacePath } from "./workspace-path.js";
6
7
 
7
8
  import {
@@ -169,10 +170,15 @@ function taskDetails(task) {
169
170
  return [
170
171
  `Title: ${singleLineDetail(task.title)}`,
171
172
  `Project: ${singleLineDetail(task.project.name)}`,
173
+ `Status: ${singleLineDetail(task.status ?? "unknown")}`,
174
+ `Summary: ${singleLineDetail(task.summary ?? "-")}`,
172
175
  `Project path: ${singleLineDetail(task.project.path)}`,
173
176
  `Created: ${singleLineDetail(task.createdAt)}`,
177
+ `Updated: ${singleLineDetail(task.updatedAt ?? "-")}`,
178
+ `Updated by: ${singleLineDetail(task.updatedBy ?? "-")}`,
174
179
  `Task ID: ${singleLineDetail(task.id)}`,
175
180
  `Thread ID: ${singleLineDetail(task.threadId ?? "-")}`,
181
+ `Turn ID: ${singleLineDetail(task.turnId ?? "-")}`,
176
182
  "Instruction:",
177
183
  task.instruction,
178
184
  ].join("\n");
@@ -381,11 +387,12 @@ async function taskList(args) {
381
387
  const result = { taskCount: dispatches.length, tasks: dispatches };
382
388
  const fullId = args.includes("--full-id");
383
389
  print(result, args, (value) => table(
384
- ["TITLE", "PROJECT", "CREATED", "ID", "THREAD ID"],
390
+ ["TITLE", "PROJECT", "STATUS", "UPDATED", "ID", "THREAD ID"],
385
391
  value.tasks.map((dispatch) => [
386
392
  dispatch.title,
387
393
  dispatch.project?.name,
388
- dispatch.createdAt,
394
+ dispatch.status ?? "unknown",
395
+ dispatch.updatedAt ?? dispatch.createdAt,
389
396
  displayId(dispatch.id, fullId),
390
397
  displayId(dispatch.threadId, fullId),
391
398
  ]),
@@ -403,11 +410,51 @@ async function taskSummary(args) {
403
410
  return 0;
404
411
  }
405
412
 
413
+ function dashboardPort(args) {
414
+ const value = option(args, "--port", "3210");
415
+ if (!/^\d+$/.test(value)) throw new Error("--port must be an integer from 0 to 65535");
416
+ const port = Number(value);
417
+ if (port > 65_535) throw new Error("--port must be an integer from 0 to 65535");
418
+ return port;
419
+ }
420
+
421
+ async function dashboard(args) {
422
+ validateCommandArgs(args, 1, {
423
+ values: ["--port", "--workspace"],
424
+ switches: ["--json"],
425
+ });
426
+ const server = await createDashboardServer({
427
+ workspace: workspaceRoot(args),
428
+ port: dashboardPort(args),
429
+ });
430
+ print({
431
+ schemaVersion: 1,
432
+ url: server.url,
433
+ workspace: server.monitor.workspace,
434
+ }, args, (value) => [
435
+ `TaskChef dashboard: ${value.url}`,
436
+ `Workspace: ${value.workspace}`,
437
+ "Press Ctrl+C to stop.",
438
+ ].join("\n"));
439
+
440
+ let stop;
441
+ await new Promise((resolve) => {
442
+ stop = resolve;
443
+ process.once("SIGINT", stop);
444
+ process.once("SIGTERM", stop);
445
+ });
446
+ process.off("SIGINT", stop);
447
+ process.off("SIGTERM", stop);
448
+ await server.close();
449
+ return 0;
450
+ }
451
+
406
452
  function usage() {
407
453
  process.stdout.write(`TaskChef workspace utility
408
454
 
409
455
  Usage:
410
456
  taskchef help
457
+ taskchef dashboard [--port <number>] [--json] [--workspace <path>]
411
458
  taskchef doctor [--json] [--workspace <path>]
412
459
  taskchef workspace path [--json] [--workspace <path>]
413
460
  taskchef workspace init [--register-codex] [--codex-cli <path>] [--json] [--workspace <path>]
@@ -429,6 +476,8 @@ Project import reads a JSON
429
476
  array from a file, or from standard input when the source is '-' or omitted.
430
477
  Workspace resolution precedence is --workspace, TASKCHEF_WORKSPACE, then
431
478
  ~/.agents/taskchef.
479
+ The dashboard binds to 127.0.0.1 and reads the canonical task log without
480
+ modifying dispatcher-workspace files.
432
481
  `);
433
482
  }
434
483
 
@@ -437,6 +486,7 @@ export async function runCli(args) {
437
486
  usage();
438
487
  return 0;
439
488
  }
489
+ if (args[0] === "dashboard") return dashboard(args);
440
490
  if (args[0] === "doctor") return doctor(args);
441
491
  if (args[0] === "workspace" && args[1] === "path") return workspacePath(args);
442
492
  if (args[0] === "workspace" && args[1] === "init") return initialize(args);