taskchef 1.0.2 → 2.0.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.
- package/.codex-plugin/plugin.json +37 -0
- package/BACKLOG.md +2 -2
- package/README.md +45 -17
- package/SPEC.md +13 -14
- package/assets/taskchef-dispatcher-instructions.md +1 -1
- package/package.json +5 -1
- package/skills/taskchef-bootstrap/SKILL.md +7 -6
- package/skills/taskchef-delegate/SKILL.md +7 -7
- package/skills/taskchef-reconcile/SKILL.md +2 -2
- package/src/cli.js +1 -1
- package/src/workspace.js +67 -41
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "taskchef",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Dispatch work from a data-only workspace to visible Codex project tasks.",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "Favo Yang",
|
|
7
|
+
"url": "https://github.com/favoyang"
|
|
8
|
+
},
|
|
9
|
+
"homepage": "https://github.com/favoyang/taskchef#readme",
|
|
10
|
+
"repository": "https://github.com/favoyang/taskchef",
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"keywords": [
|
|
13
|
+
"codex",
|
|
14
|
+
"delegation",
|
|
15
|
+
"dispatcher",
|
|
16
|
+
"tasks"
|
|
17
|
+
],
|
|
18
|
+
"skills": "./skills/",
|
|
19
|
+
"interface": {
|
|
20
|
+
"displayName": "TaskChef",
|
|
21
|
+
"shortDescription": "Dispatch work to the right Codex project task.",
|
|
22
|
+
"longDescription": "Create a data-only dispatcher workspace, route actionable requests to independently openable Codex project tasks, and reconcile saved task states on demand.",
|
|
23
|
+
"developerName": "Favo Yang",
|
|
24
|
+
"category": "Productivity",
|
|
25
|
+
"capabilities": [
|
|
26
|
+
"Read",
|
|
27
|
+
"Write"
|
|
28
|
+
],
|
|
29
|
+
"websiteURL": "https://github.com/favoyang/taskchef",
|
|
30
|
+
"defaultPrompt": [
|
|
31
|
+
"$taskchef-bootstrap Set up TaskChef in this folder.",
|
|
32
|
+
"$taskchef-delegate Dispatch this request to the right project.",
|
|
33
|
+
"$taskchef-reconcile Fix outdated TaskChef task states."
|
|
34
|
+
],
|
|
35
|
+
"brandColor": "#D97706"
|
|
36
|
+
}
|
|
37
|
+
}
|
package/BACKLOG.md
CHANGED
|
@@ -84,5 +84,5 @@ This document contains capabilities intentionally excluded from the v1 MVP.
|
|
|
84
84
|
|
|
85
85
|
- Add GitHub automation beyond storing PR and issue URLs.
|
|
86
86
|
- Evaluate automatic project discovery instead of an explicit configured list.
|
|
87
|
-
-
|
|
88
|
-
|
|
87
|
+
- Evaluate marketplace discovery and update automation after the shared plugin
|
|
88
|
+
catalog has real usage.
|
package/README.md
CHANGED
|
@@ -11,33 +11,52 @@ The canonical contract is [SPEC.md](SPEC.md). Deferred ideas are in
|
|
|
11
11
|
|
|
12
12
|
TaskChef requires Node.js 18 or newer and Git.
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
Add the shared Favo Yang plugin marketplace, then install TaskChef:
|
|
15
15
|
|
|
16
16
|
```sh
|
|
17
|
-
|
|
17
|
+
codex plugin marketplace add favoyang/codex-plugins
|
|
18
|
+
codex plugin add taskchef@favoyang-plugins
|
|
18
19
|
```
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
Open or create the folder that will hold TaskChef's dispatcher data, start a
|
|
22
|
+
new Codex task there, and ask:
|
|
23
|
+
|
|
24
|
+
```text
|
|
25
|
+
$taskchef-bootstrap Set up TaskChef in this folder and help me choose projects.
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
The plugin provides all three TaskChef skills and its deterministic CLI runtime;
|
|
29
|
+
dispatcher workspaces do not install or link skills themselves.
|
|
30
|
+
|
|
31
|
+
For headless CLI use, install the npm package with
|
|
32
|
+
`npm install --global taskchef`. Contributors can run `node bin/taskchef.js`
|
|
33
|
+
directly from a source checkout.
|
|
34
|
+
|
|
35
|
+
## Updating
|
|
36
|
+
|
|
37
|
+
Refresh the marketplace and reinstall TaskChef:
|
|
22
38
|
|
|
23
39
|
```sh
|
|
24
|
-
|
|
25
|
-
|
|
40
|
+
codex plugin marketplace upgrade favoyang-plugins
|
|
41
|
+
codex plugin add taskchef@favoyang-plugins
|
|
26
42
|
```
|
|
27
43
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
44
|
+
Start a new Codex task to load the updated skills.
|
|
45
|
+
|
|
46
|
+
### Migrating from 1.x
|
|
47
|
+
|
|
48
|
+
TaskChef 2.x moves skill ownership from dispatcher-workspace symlinks to the
|
|
49
|
+
installed plugin. Run `$taskchef-bootstrap` once in an existing workspace to
|
|
50
|
+
remove the three legacy TaskChef links; unrelated `.agents` content is
|
|
51
|
+
preserved. The deprecated `ensureWorkspaceSkills()` export remains available
|
|
52
|
+
for compatibility but reports plugin-provided skills instead of creating
|
|
53
|
+
workspace links.
|
|
32
54
|
|
|
33
55
|
## Workspace
|
|
34
56
|
|
|
35
57
|
```text
|
|
36
58
|
AGENTS.md
|
|
37
59
|
taskchef.json
|
|
38
|
-
.agents/skills/taskchef-bootstrap -> <source>/skills/taskchef-bootstrap
|
|
39
|
-
.agents/skills/taskchef-delegate -> <source>/skills/taskchef-delegate
|
|
40
|
-
.agents/skills/taskchef-reconcile -> <source>/skills/taskchef-reconcile
|
|
41
60
|
tasks/<task-id>/task.json
|
|
42
61
|
```
|
|
43
62
|
|
|
@@ -49,7 +68,9 @@ taskchef doctor --workspace <workspace>
|
|
|
49
68
|
```
|
|
50
69
|
|
|
51
70
|
Initialization is idempotent. It creates an empty configuration when missing
|
|
52
|
-
and preserves existing configured projects.
|
|
71
|
+
and preserves existing configured projects. When upgrading from the earlier
|
|
72
|
+
workspace-linked distribution, initialization removes the three legacy
|
|
73
|
+
TaskChef skill symlinks and preserves unrelated `.agents` content.
|
|
53
74
|
|
|
54
75
|
## Projects
|
|
55
76
|
|
|
@@ -145,15 +166,22 @@ feat!: change the workspace data contract
|
|
|
145
166
|
|
|
146
167
|
Publishing uses npm trusted publishing from `.github/workflows/release.yml`.
|
|
147
168
|
The workflow runs the test suite, validates the npm tarball, publishes the
|
|
148
|
-
calculated version
|
|
149
|
-
|
|
169
|
+
calculated version after synchronizing the plugin manifest, creates the GitHub
|
|
170
|
+
release, commits the synchronized version files back to `main`, and pins the TaskChef entry
|
|
171
|
+
in `favoyang/codex-plugins` to that exact npm version.
|
|
172
|
+
|
|
173
|
+
The release job requires an Actions secret named `MARKETPLACE_DEPLOY_KEY`.
|
|
174
|
+
Store the private half of a dedicated SSH deploy key there, and add its public
|
|
175
|
+
half to `favoyang/codex-plugins` with write access. The key must be scoped only
|
|
176
|
+
to that catalog repository; the workflow's repository-scoped `GITHUB_TOKEN`
|
|
177
|
+
cannot update another repository.
|
|
150
178
|
|
|
151
179
|
## Development
|
|
152
180
|
|
|
153
181
|
```sh
|
|
154
182
|
npm test
|
|
155
183
|
npm pack --dry-run
|
|
156
|
-
npx -y -p semantic-release@25 -p @semantic-release/git semantic-release --dry-run
|
|
184
|
+
npx -y -p semantic-release@25 -p @semantic-release/exec -p @semantic-release/git semantic-release --dry-run
|
|
157
185
|
```
|
|
158
186
|
|
|
159
187
|
## Boundaries
|
package/SPEC.md
CHANGED
|
@@ -35,11 +35,6 @@ Multiple ongoing tasks may target the same project.
|
|
|
35
35
|
taskchef-workspace/
|
|
36
36
|
├── AGENTS.md
|
|
37
37
|
├── taskchef.json
|
|
38
|
-
├── .agents/
|
|
39
|
-
│ └── skills/
|
|
40
|
-
│ ├── taskchef-bootstrap -> <TaskChef bootstrap skill>
|
|
41
|
-
│ ├── taskchef-delegate -> <TaskChef delegate skill>
|
|
42
|
-
│ └── taskchef-reconcile -> <TaskChef reconciliation skill>
|
|
43
38
|
└── tasks/
|
|
44
39
|
└── <task-id>/
|
|
45
40
|
└── task.json
|
|
@@ -61,12 +56,15 @@ for user-requested refreshes or repairs of outdated task states.
|
|
|
61
56
|
does exist, bootstrap preserves unrelated user content and adds or refreshes
|
|
62
57
|
only the TaskChef managed block. Repeating the merge is idempotent. Malformed
|
|
63
58
|
or duplicate TaskChef markers fail safely instead of overwriting the file.
|
|
64
|
-
|
|
65
|
-
takes no configuration input, creates
|
|
66
|
-
when configuration is missing, and
|
|
59
|
+
The installed TaskChef plugin provides all three skills outside the dispatcher
|
|
60
|
+
workspace. Initialization is idempotent, takes no configuration input, creates
|
|
61
|
+
`{ "schemaVersion": 1, "projects": [] }` when configuration is missing, and
|
|
62
|
+
preserves existing configured projects. It removes only the three legacy
|
|
63
|
+
TaskChef skill symlinks from earlier workspaces and preserves unrelated
|
|
64
|
+
`.agents` content.
|
|
67
65
|
|
|
68
|
-
`doctor` diagnoses configuration, task storage, managed instructions,
|
|
69
|
-
|
|
66
|
+
`doctor` diagnoses configuration, task storage, managed instructions, project
|
|
67
|
+
paths, and task records without modifying the workspace.
|
|
70
68
|
|
|
71
69
|
## Configuration
|
|
72
70
|
|
|
@@ -285,8 +283,9 @@ TaskChef is local, interactive, task-focused, and asynchronous. It does not incl
|
|
|
285
283
|
- one-active-task-per-project restrictions;
|
|
286
284
|
- transcript or hidden-reasoning collection;
|
|
287
285
|
- arbitrary Codex task discovery;
|
|
288
|
-
-
|
|
289
|
-
|
|
286
|
+
- bundled development-only runtimes in dispatcher workspaces. The Codex plugin
|
|
287
|
+
marketplace distributes the three skills, and the npm package provides the
|
|
288
|
+
supported headless CLI.
|
|
290
289
|
|
|
291
290
|
See `BACKLOG.md` for deferred capabilities and experiments.
|
|
292
291
|
|
|
@@ -306,7 +305,7 @@ The MVP is successful when:
|
|
|
306
305
|
without reading finished executor threads; ordinary delegation does not.
|
|
307
306
|
8. Status and result snapshots are updated correctly.
|
|
308
307
|
9. Two ongoing tasks may target the same project without data collisions.
|
|
309
|
-
10. The workspace contains dispatcher instructions, configuration,
|
|
310
|
-
records
|
|
308
|
+
10. The workspace contains only dispatcher instructions, configuration, and
|
|
309
|
+
task records; the installed plugin owns the TaskChef skills.
|
|
311
310
|
11. Bootstrap creates or idempotently merges the TaskChef-managed `AGENTS.md`
|
|
312
311
|
block without overwriting unrelated instructions.
|
|
@@ -5,7 +5,7 @@ This repository is a TaskChef dispatcher workspace.
|
|
|
5
5
|
|
|
6
6
|
- Use `$taskchef-bootstrap` when initializing or refreshing this workspace,
|
|
7
7
|
changing or listing its configured projects, running TaskChef doctor,
|
|
8
|
-
repairing its managed instructions
|
|
8
|
+
or repairing its managed instructions.
|
|
9
9
|
- For every actionable work request, use `$taskchef-delegate`
|
|
10
10
|
automatically, even when the user does not explicitly say "delegate" or
|
|
11
11
|
mention TaskChef.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "taskchef",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "A non-blocking interactive dispatcher for visible Codex tasks.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Favo Yang",
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"taskchef": "bin/taskchef.js"
|
|
19
19
|
},
|
|
20
20
|
"files": [
|
|
21
|
+
".codex-plugin",
|
|
21
22
|
"assets",
|
|
22
23
|
"BACKLOG.md",
|
|
23
24
|
"bin",
|
|
@@ -31,6 +32,9 @@
|
|
|
31
32
|
"engines": {
|
|
32
33
|
"node": ">=18"
|
|
33
34
|
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"yaml": "^2.9.0"
|
|
37
|
+
},
|
|
34
38
|
"scripts": {
|
|
35
39
|
"test": "node --test tests/taskchef.test.js"
|
|
36
40
|
}
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: taskchef-bootstrap
|
|
3
|
-
description: "Initialize, diagnose, or refresh TaskChef dispatcher workspaces, project configuration, managed AGENTS.md instructions
|
|
3
|
+
description: "Initialize, diagnose, or refresh TaskChef dispatcher workspaces, project configuration, and managed AGENTS.md instructions. Use when creating a TaskChef workspace, adding, importing, listing, or removing configured projects, running TaskChef doctor, or repairing dispatcher setup. Do not dispatch user work or reconcile executor threads."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# TaskChef Bootstrap
|
|
7
7
|
|
|
8
8
|
Initialize or refresh a data-only TaskChef dispatcher workspace.
|
|
9
9
|
|
|
10
|
-
Resolve this
|
|
11
|
-
parents above the skill directory. Invoke `<
|
|
10
|
+
Resolve this skill directory with `realpath`. The TaskChef plugin root is two
|
|
11
|
+
parents above the skill directory. Invoke `<plugin-root>/bin/taskchef.js` for
|
|
12
12
|
all deterministic workspace operations.
|
|
13
13
|
|
|
14
14
|
## Boundaries
|
|
15
15
|
|
|
16
16
|
- Keep implementation, tests, and reports in the TaskChef source repository.
|
|
17
|
-
- Keep only `AGENTS.md`, `taskchef.json`, `tasks/*/task.json
|
|
18
|
-
|
|
17
|
+
- Keep only `AGENTS.md`, `taskchef.json`, and `tasks/*/task.json` in a
|
|
18
|
+
dispatcher workspace.
|
|
19
19
|
- Do not dispatch tasks or reconcile executor threads during bootstrap unless
|
|
20
20
|
the user separately requests those actions.
|
|
21
21
|
- Never use collaboration agents, hooks, schedules, polling, or daemons.
|
|
@@ -24,7 +24,8 @@ all deterministic workspace operations.
|
|
|
24
24
|
|
|
25
25
|
1. Run `workspace init --json`. It takes no stdin, creates an empty
|
|
26
26
|
configuration when missing, and idempotently creates or refreshes the task
|
|
27
|
-
directory
|
|
27
|
+
directory and managed instructions. The installed plugin provides all three
|
|
28
|
+
TaskChef skills outside the dispatcher workspace.
|
|
28
29
|
2. Run `doctor --json` after setup or when the user asks to diagnose the
|
|
29
30
|
workspace. Doctor is read-only; rerun `workspace init --json` to repair the
|
|
30
31
|
managed scaffold.
|
|
@@ -7,15 +7,15 @@ description: "Dispatch actionable requests from an initialized TaskChef workspac
|
|
|
7
7
|
|
|
8
8
|
Create real Codex tasks from a TaskChef data workspace and return immediately.
|
|
9
9
|
|
|
10
|
-
Resolve this
|
|
10
|
+
Resolve this skill directory with `realpath`. The TaskChef plugin root is two
|
|
11
11
|
parents above the skill directory. Use the TaskChef executable under that root
|
|
12
12
|
for all deterministic workspace and task-record operations.
|
|
13
13
|
|
|
14
14
|
## Boundaries
|
|
15
15
|
|
|
16
16
|
- Keep implementation, tests, and reports in the TaskChef source repository.
|
|
17
|
-
- Keep only `AGENTS.md`, `taskchef.json`, `tasks/*/task.json
|
|
18
|
-
|
|
17
|
+
- Keep only `AGENTS.md`, `taskchef.json`, and `tasks/*/task.json` in a
|
|
18
|
+
dispatcher workspace.
|
|
19
19
|
- Use real Codex tasks, never collaboration or subagent tools.
|
|
20
20
|
- Never use hooks, callbacks, schedules, polling, daemons, or event logs.
|
|
21
21
|
- Never wait for delegated work after executor creation.
|
|
@@ -24,7 +24,7 @@ for all deterministic workspace and task-record operations.
|
|
|
24
24
|
## Dispatch
|
|
25
25
|
|
|
26
26
|
1. Run
|
|
27
|
-
`<
|
|
27
|
+
`<plugin-root>/bin/taskchef.js project list --json --workspace <workspace>`
|
|
28
28
|
to load and validate the configured routing targets. Use
|
|
29
29
|
`$taskchef-bootstrap` if the workspace is missing or unhealthy.
|
|
30
30
|
2. Split the request into the smallest independently useful outcomes. Include
|
|
@@ -34,15 +34,15 @@ for all deterministic workspace and task-record operations.
|
|
|
34
34
|
clear project match.
|
|
35
35
|
4. Resolve native projects once and require the exact configured path.
|
|
36
36
|
5. For an explicit retry, require the exact task ID and run
|
|
37
|
-
`<
|
|
37
|
+
`<plugin-root>/bin/taskchef.js task show <task-id> --json --workspace <workspace>`.
|
|
38
38
|
Reuse the record only when its status is `pending`; ask for the task ID when
|
|
39
39
|
it is missing and reject retries of non-pending records. For new work, run
|
|
40
|
-
`<
|
|
40
|
+
`<plugin-root>/bin/taskchef.js task create --json --workspace <workspace>`
|
|
41
41
|
with the task record JSON on stdin before executor creation.
|
|
42
42
|
6. Create one real Codex task per record using the exact saved project and a
|
|
43
43
|
local environment on its executor host.
|
|
44
44
|
7. Immediately run
|
|
45
|
-
`<
|
|
45
|
+
`<plugin-root>/bin/taskchef.js task update <task-id> --json --workspace <workspace>`
|
|
46
46
|
with the `running` status and returned `threadId` on stdin. Never persist
|
|
47
47
|
`hostId`.
|
|
48
48
|
8. Leave a failed creation pending. Do not invent an ID or delete the record.
|
|
@@ -7,8 +7,8 @@ description: "Reconcile active TaskChef task records with their visible Codex ex
|
|
|
7
7
|
|
|
8
8
|
Refresh TaskChef's recorded task state from native Codex threads exactly once.
|
|
9
9
|
|
|
10
|
-
Resolve this
|
|
11
|
-
parents above the skill directory. Invoke `<
|
|
10
|
+
Resolve this skill directory with `realpath`. The TaskChef plugin root is two
|
|
11
|
+
parents above the skill directory. Invoke `<plugin-root>/bin/taskchef.js` for
|
|
12
12
|
all deterministic record operations.
|
|
13
13
|
|
|
14
14
|
## Reconcile
|
package/src/cli.js
CHANGED
|
@@ -107,7 +107,7 @@ async function initialize(args) {
|
|
|
107
107
|
`Workspace: ${value.workspace}`,
|
|
108
108
|
`Configuration: ${value.config.action}`,
|
|
109
109
|
`Instructions: ${value.instructions.action}`,
|
|
110
|
-
`
|
|
110
|
+
`Legacy skill links removed: ${value.legacySkills.removed.length}`,
|
|
111
111
|
].join("\n"));
|
|
112
112
|
return 0;
|
|
113
113
|
}
|
package/src/workspace.js
CHANGED
|
@@ -4,13 +4,12 @@ import {
|
|
|
4
4
|
lstat,
|
|
5
5
|
mkdir,
|
|
6
6
|
readFile,
|
|
7
|
-
readlink,
|
|
8
7
|
readdir,
|
|
9
8
|
realpath,
|
|
10
9
|
link,
|
|
11
10
|
rename,
|
|
11
|
+
rmdir,
|
|
12
12
|
stat,
|
|
13
|
-
symlink,
|
|
14
13
|
unlink,
|
|
15
14
|
writeFile,
|
|
16
15
|
} from "node:fs/promises";
|
|
@@ -244,38 +243,73 @@ export async function ensureWorkspaceInstructions(workspaceRoot) {
|
|
|
244
243
|
return { path: filePath, action };
|
|
245
244
|
}
|
|
246
245
|
|
|
247
|
-
async function
|
|
248
|
-
const
|
|
249
|
-
const
|
|
250
|
-
await canonicalDirectory(source);
|
|
251
|
-
const details = await lstat(destination).catch((error) => {
|
|
246
|
+
async function findLegacySkillLinks(workspaceRoot) {
|
|
247
|
+
const agentsDirectory = path.join(workspaceRoot, ".agents");
|
|
248
|
+
const agentsDetails = await lstat(agentsDirectory).catch((error) => {
|
|
252
249
|
if (error.code === "ENOENT") return null;
|
|
253
250
|
throw error;
|
|
254
251
|
});
|
|
255
|
-
if (
|
|
256
|
-
|
|
257
|
-
return {
|
|
252
|
+
if (agentsDetails === null) return { agentsDirectory: null, skillsDirectory: null, links: [] };
|
|
253
|
+
if (agentsDetails.isSymbolicLink() || !agentsDetails.isDirectory()) {
|
|
254
|
+
return { agentsDirectory: null, skillsDirectory: null, links: [] };
|
|
258
255
|
}
|
|
259
|
-
|
|
260
|
-
|
|
256
|
+
|
|
257
|
+
const skillsDirectory = path.join(agentsDirectory, "skills");
|
|
258
|
+
const skillsDetails = await lstat(skillsDirectory).catch((error) => {
|
|
259
|
+
if (error.code === "ENOENT") return null;
|
|
260
|
+
throw error;
|
|
261
|
+
});
|
|
262
|
+
if (skillsDetails === null) return { agentsDirectory, skillsDirectory, links: [] };
|
|
263
|
+
if (skillsDetails.isSymbolicLink() || !skillsDetails.isDirectory()) {
|
|
264
|
+
return { agentsDirectory, skillsDirectory: null, links: [] };
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const links = [];
|
|
268
|
+
for (const skillName of TASKCHEF_SKILL_NAMES) {
|
|
269
|
+
const skillPath = path.join(skillsDirectory, skillName);
|
|
270
|
+
const details = await lstat(skillPath).catch((error) => {
|
|
271
|
+
if (error.code === "ENOENT") return null;
|
|
272
|
+
throw error;
|
|
273
|
+
});
|
|
274
|
+
if (details === null) continue;
|
|
275
|
+
if (!details.isSymbolicLink()) {
|
|
276
|
+
throw new Error(`legacy TaskChef skill path is not a symlink: ${skillPath}`);
|
|
277
|
+
}
|
|
278
|
+
links.push({ name: skillName, path: skillPath });
|
|
279
|
+
}
|
|
280
|
+
return { agentsDirectory, skillsDirectory, links };
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
async function removeLegacySkillLinks(workspaceRoot) {
|
|
284
|
+
const legacy = await findLegacySkillLinks(workspaceRoot);
|
|
285
|
+
for (const link of legacy.links) await unlink(link.path);
|
|
286
|
+
const removedDirectories = [];
|
|
287
|
+
if (legacy.skillsDirectory && (await readdir(legacy.skillsDirectory)).length === 0) {
|
|
288
|
+
await rmdir(legacy.skillsDirectory);
|
|
289
|
+
removedDirectories.push(legacy.skillsDirectory);
|
|
261
290
|
}
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
291
|
+
if (legacy.agentsDirectory && (await readdir(legacy.agentsDirectory).catch((error) => {
|
|
292
|
+
if (error.code === "ENOENT") return null;
|
|
293
|
+
throw error;
|
|
294
|
+
}))?.length === 0) {
|
|
295
|
+
await rmdir(legacy.agentsDirectory);
|
|
296
|
+
removedDirectories.push(legacy.agentsDirectory);
|
|
267
297
|
}
|
|
268
|
-
return {
|
|
298
|
+
return { removed: legacy.links, removedDirectories };
|
|
269
299
|
}
|
|
270
300
|
|
|
271
301
|
export async function ensureWorkspaceSkills(workspaceRoot) {
|
|
272
302
|
const root = await realpath(path.resolve(workspaceRoot));
|
|
273
|
-
const
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
skills.
|
|
277
|
-
|
|
278
|
-
|
|
303
|
+
const legacySkills = await removeLegacySkillLinks(root);
|
|
304
|
+
return {
|
|
305
|
+
directory: null,
|
|
306
|
+
skills: TASKCHEF_SKILL_NAMES.map((name) => ({
|
|
307
|
+
name,
|
|
308
|
+
path: path.join(SKILLS_SOURCE_ROOT, name),
|
|
309
|
+
action: "provided-by-plugin",
|
|
310
|
+
})),
|
|
311
|
+
legacySkills,
|
|
312
|
+
};
|
|
279
313
|
}
|
|
280
314
|
|
|
281
315
|
export async function canonicalGitRoot(projectPath) {
|
|
@@ -485,6 +519,7 @@ export async function initializeWorkspace(workspaceRoot) {
|
|
|
485
519
|
const requestedRoot = path.resolve(workspaceRoot);
|
|
486
520
|
await mkdir(requestedRoot, { recursive: true });
|
|
487
521
|
const root = await realpath(requestedRoot);
|
|
522
|
+
const { legacySkills } = await ensureWorkspaceSkills(root);
|
|
488
523
|
await ensureManagedDirectory(root, "tasks");
|
|
489
524
|
const configPath = path.join(root, "taskchef.json");
|
|
490
525
|
const configExists = await managedRegularFileExists(configPath);
|
|
@@ -496,16 +531,12 @@ export async function initializeWorkspace(workspaceRoot) {
|
|
|
496
531
|
if (!configExists) await unlink(configPath).catch(() => {});
|
|
497
532
|
throw error;
|
|
498
533
|
});
|
|
499
|
-
const skills = await ensureWorkspaceSkills(root).catch(async (error) => {
|
|
500
|
-
if (!configExists) await unlink(configPath).catch(() => {});
|
|
501
|
-
throw error;
|
|
502
|
-
});
|
|
503
534
|
return {
|
|
504
535
|
workspace: root,
|
|
505
536
|
config: { path: configPath, action: configExists ? "unchanged" : "created", value: config },
|
|
506
537
|
tasks: { path: path.join(root, "tasks"), action: "ready" },
|
|
507
538
|
instructions,
|
|
508
|
-
|
|
539
|
+
legacySkills,
|
|
509
540
|
};
|
|
510
541
|
}
|
|
511
542
|
|
|
@@ -834,18 +865,13 @@ export async function doctorWorkspace(workspaceRoot) {
|
|
|
834
865
|
}
|
|
835
866
|
return "managed AGENTS.md instructions current";
|
|
836
867
|
});
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
if (linked !== expected) throw new Error(`unexpected target: ${linked}`);
|
|
845
|
-
await canonicalDirectory(expected);
|
|
846
|
-
return "skill link valid";
|
|
847
|
-
});
|
|
848
|
-
}
|
|
868
|
+
await check("legacy-skill-links", async () => {
|
|
869
|
+
const legacy = await findLegacySkillLinks(root);
|
|
870
|
+
if (legacy.links.length > 0) {
|
|
871
|
+
throw new Error("legacy TaskChef skill links remain; run workspace init to remove them");
|
|
872
|
+
}
|
|
873
|
+
return "no legacy TaskChef skill links";
|
|
874
|
+
});
|
|
849
875
|
await check("task-records", async () => {
|
|
850
876
|
const taskRoot = path.join(root, "tasks");
|
|
851
877
|
const entries = await readdir(taskRoot, { withFileTypes: true });
|