@winton979/task-cli 1.0.3 → 1.1.1
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 +53 -16
- package/package.json +1 -1
- package/src/cli.js +16 -3
- package/src/init.js +319 -156
package/README.md
CHANGED
|
@@ -50,10 +50,16 @@ Compatible Grill Me implementations may also work.
|
|
|
50
50
|
|
|
51
51
|
```bash
|
|
52
52
|
task init
|
|
53
|
+
task refresh
|
|
54
|
+
task doctor
|
|
53
55
|
task --help
|
|
54
56
|
```
|
|
55
57
|
|
|
56
|
-
After initialization, Task CLI creates the `.ai/` workspace and installs workflow skills
|
|
58
|
+
After initialization, Task CLI creates the `.ai/` workspace and installs workflow skills into both `.claude/skills/` and `.codex/skills/`.
|
|
59
|
+
|
|
60
|
+
Use `task refresh` in existing projects to remove and reinstall only the workflow skills managed by task-cli. It does not delete your `.ai` briefs, internal archives, or decision log.
|
|
61
|
+
|
|
62
|
+
Use `task doctor` to check whether the required directories exist, whether managed skills are missing or outdated, and whether the `.gitignore` rules are present.
|
|
57
63
|
|
|
58
64
|
---
|
|
59
65
|
|
|
@@ -64,11 +70,7 @@ After initialization, Task CLI creates the `.ai/` workspace and installs workflo
|
|
|
64
70
|
```text
|
|
65
71
|
/task-fast
|
|
66
72
|
↓
|
|
67
|
-
|
|
68
|
-
↓
|
|
69
|
-
task brief generated
|
|
70
|
-
↓
|
|
71
|
-
/task-implement
|
|
73
|
+
clarify + brief + implement + validate
|
|
72
74
|
↓
|
|
73
75
|
/task-review
|
|
74
76
|
```
|
|
@@ -80,8 +82,6 @@ task brief generated
|
|
|
80
82
|
↓
|
|
81
83
|
TASK_READY
|
|
82
84
|
↓
|
|
83
|
-
/task-brief
|
|
84
|
-
↓
|
|
85
85
|
/task-implement
|
|
86
86
|
↓
|
|
87
87
|
/task-review
|
|
@@ -94,8 +94,6 @@ TASK_READY
|
|
|
94
94
|
↓
|
|
95
95
|
BUG_READY
|
|
96
96
|
↓
|
|
97
|
-
/bug-brief
|
|
98
|
-
↓
|
|
99
97
|
/bug-fix
|
|
100
98
|
↓
|
|
101
99
|
/bug-review
|
|
@@ -109,14 +107,12 @@ BUG_READY
|
|
|
109
107
|
|
|
110
108
|
* task-fast
|
|
111
109
|
* task-explore
|
|
112
|
-
* task-brief
|
|
113
110
|
* task-implement
|
|
114
111
|
* task-review
|
|
115
112
|
|
|
116
113
|
### Bug Workflow
|
|
117
114
|
|
|
118
115
|
* bug-explore
|
|
119
|
-
* bug-brief
|
|
120
116
|
* bug-fix
|
|
121
117
|
* bug-review
|
|
122
118
|
|
|
@@ -141,7 +137,8 @@ BUG_READY
|
|
|
141
137
|
├── decisions/
|
|
142
138
|
│ └── decisions.md
|
|
143
139
|
│
|
|
144
|
-
|
|
140
|
+
├── .claude/skills/
|
|
141
|
+
└── .codex/skills/
|
|
145
142
|
```
|
|
146
143
|
|
|
147
144
|
---
|
|
@@ -154,11 +151,51 @@ Instead of maintaining large specifications, it focuses on:
|
|
|
154
151
|
|
|
155
152
|
1. Clarifying requirements before coding
|
|
156
153
|
2. Capturing execution context in concise briefs
|
|
157
|
-
3.
|
|
158
|
-
4.
|
|
154
|
+
3. Executing with validation while archiving automatically in the background
|
|
155
|
+
4. Reviewing work against acceptance criteria
|
|
156
|
+
5. Keeping a lightweight decision history
|
|
159
157
|
|
|
160
158
|
The goal is to improve quality without slowing down iteration speed.
|
|
161
159
|
|
|
162
160
|
|
|
161
|
+
## Can This Be Simpler?
|
|
162
|
+
|
|
163
|
+
Yes. The main simplification is to collapse the old 4-step paths:
|
|
164
|
+
|
|
165
|
+
* `task-explore` now includes brief generation.
|
|
166
|
+
* `bug-explore` now includes bug brief generation.
|
|
167
|
+
* `task-implement` and `bug-fix` now validate the work and archive the brief automatically when complete.
|
|
168
|
+
|
|
169
|
+
That leaves these practical flows:
|
|
170
|
+
|
|
171
|
+
* Explore only: `/task-explore` or `/bug-explore`
|
|
172
|
+
* Execute: `/task-implement` or `/bug-fix`
|
|
173
|
+
* Review: `/task-review` or `/bug-review`
|
|
174
|
+
* One-shot small work: `/task-fast`
|
|
175
|
+
|
|
176
|
+
## Upgrading Existing Projects
|
|
177
|
+
|
|
178
|
+
If a project was initialized with an older version of task-cli, run:
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
task refresh
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
This will:
|
|
185
|
+
|
|
186
|
+
* keep `.ai/tasks`, `.ai/bugs`, and `.ai/decisions`
|
|
187
|
+
* remove only these managed skills from `.claude/skills/` and `.codex/skills/`: `task-fast`, `task-explore`, `task-implement`, `task-review`, `bug-explore`, `bug-fix`, `bug-review`, `decision-log`
|
|
188
|
+
* reinstall the latest versions of those skills
|
|
189
|
+
|
|
190
|
+
This avoids touching unrelated custom skills in the same project.
|
|
191
|
+
|
|
192
|
+
The `archive/` directories remain as internal storage. They are not separate user steps in the recommended workflow.
|
|
193
|
+
|
|
194
|
+
Before refreshing, you can inspect the current setup with:
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
task doctor
|
|
198
|
+
```
|
|
199
|
+
|
|
163
200
|
> Task CLI does not install Grill Me automatically.
|
|
164
|
-
> Users remain free to choose any Grill Me compatible implementation.
|
|
201
|
+
> Users remain free to choose any Grill Me compatible implementation.
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import chalk from 'chalk';
|
|
4
|
-
import { init } from './init.js';
|
|
4
|
+
import { init, refresh, doctor } from './init.js';
|
|
5
5
|
|
|
6
6
|
const cwd = process.cwd();
|
|
7
7
|
const cmd = process.argv[2];
|
|
@@ -20,17 +20,30 @@ task - Lightweight AI-assisted task workflow
|
|
|
20
20
|
|
|
21
21
|
Usage:
|
|
22
22
|
task init Initialize task workflow in current directory
|
|
23
|
-
task
|
|
23
|
+
task refresh Reinstall task-cli managed workflow skills
|
|
24
|
+
task doctor Check workflow setup and skill freshness
|
|
25
|
+
task --help Show this help
|
|
26
|
+
|
|
27
|
+
Recommended flows after init:
|
|
28
|
+
fast: task-fast
|
|
29
|
+
task: task-explore -> task-implement -> task-review
|
|
30
|
+
bug: bug-explore -> bug-fix -> bug-review`);
|
|
24
31
|
}
|
|
25
32
|
|
|
26
33
|
switch (cmd) {
|
|
27
34
|
case 'init':
|
|
28
35
|
init(cwd, { fs, path, log });
|
|
29
36
|
break;
|
|
37
|
+
case 'refresh':
|
|
38
|
+
refresh(cwd, { fs, path, log });
|
|
39
|
+
break;
|
|
40
|
+
case 'doctor':
|
|
41
|
+
doctor(cwd, { fs, path, log });
|
|
42
|
+
break;
|
|
30
43
|
case undefined:
|
|
31
44
|
case '--help':
|
|
32
45
|
case '-h':
|
|
33
46
|
default:
|
|
34
47
|
showHelp();
|
|
35
48
|
break;
|
|
36
|
-
}
|
|
49
|
+
}
|
package/src/init.js
CHANGED
|
@@ -1,90 +1,107 @@
|
|
|
1
|
-
|
|
1
|
+
const TASK_ACTIVE_DIR = '.ai/tasks/active';
|
|
2
|
+
const TASK_ARCHIVE_DIR = '.ai/tasks/archive';
|
|
3
|
+
const BUG_ACTIVE_DIR = '.ai/bugs/active';
|
|
4
|
+
const BUG_ARCHIVE_DIR = '.ai/bugs/archive';
|
|
5
|
+
const DECISIONS_FILE = '.ai/decisions/decisions.md';
|
|
6
|
+
const CLAUDE_SKILLS_DIR = '.claude/skills';
|
|
7
|
+
const CODEX_SKILLS_DIR = '.codex/skills';
|
|
8
|
+
const GITIGNORE_BLOCK = [
|
|
9
|
+
'# task workflow',
|
|
10
|
+
'.ai/tasks/active/*.md',
|
|
11
|
+
'.ai/bugs/active/*.md',
|
|
12
|
+
].join('\n');
|
|
2
13
|
|
|
3
14
|
const SKILLS = {
|
|
4
15
|
'task-fast': {
|
|
5
16
|
name: 'task-fast',
|
|
6
|
-
description: 'Fast path for small requirements.
|
|
17
|
+
description: 'Fast path for small requirements. Clarify quickly, create the brief, implement, and verify. Archive automatically on completion.',
|
|
7
18
|
content: `---
|
|
8
19
|
name: task-fast
|
|
9
|
-
description: Fast path for small requirements.
|
|
20
|
+
description: Fast path for small requirements. Clarify quickly, create the brief, implement, and verify. Archive automatically on completion.
|
|
10
21
|
user-invocable: true
|
|
11
22
|
---
|
|
12
23
|
|
|
13
24
|
Purpose
|
|
14
25
|
|
|
15
|
-
|
|
26
|
+
Handle a small requirement in one continuous workflow with minimal ceremony.
|
|
16
27
|
|
|
17
28
|
Workflow
|
|
18
29
|
|
|
19
|
-
1.
|
|
20
|
-
2.
|
|
21
|
-
3. Generate a task brief automatically.
|
|
22
|
-
4. Save the brief to:
|
|
30
|
+
1. Clarify the requirement just enough to remove ambiguity.
|
|
31
|
+
2. Create a concise task brief and save it to:
|
|
23
32
|
|
|
24
33
|
.ai/tasks/active/YYYY-MM-DD-task-name.md
|
|
25
34
|
|
|
26
|
-
|
|
27
|
-
|
|
35
|
+
3. Show the brief before coding.
|
|
36
|
+
4. If the user does not object, implement immediately.
|
|
37
|
+
5. Verify the result against the acceptance criteria.
|
|
38
|
+
6. Archive the brief automatically by moving it to:
|
|
28
39
|
|
|
29
|
-
|
|
40
|
+
.ai/tasks/archive/YYYY-MM-DD-task-name.md
|
|
30
41
|
|
|
31
|
-
|
|
32
|
-
`,
|
|
33
|
-
},
|
|
42
|
+
7. Summarize the outcome and any follow-up risks.
|
|
34
43
|
|
|
35
|
-
|
|
36
|
-
name: 'task-explore',
|
|
37
|
-
description: 'Clarify requirements before implementation. Invoke Grill Me until the task is understood.',
|
|
38
|
-
content: `---
|
|
39
|
-
name: task-explore
|
|
40
|
-
description: Clarify requirements before implementation. Invoke Grill Me until the task is understood.
|
|
41
|
-
user-invocable: true
|
|
42
|
-
---
|
|
44
|
+
Task Brief Format
|
|
43
45
|
|
|
44
|
-
|
|
46
|
+
# Goal
|
|
45
47
|
|
|
46
|
-
|
|
48
|
+
What should be achieved.
|
|
47
49
|
|
|
48
|
-
|
|
50
|
+
# Context
|
|
49
51
|
|
|
50
|
-
|
|
51
|
-
2. Continue until Grill Me determines the task is sufficiently understood.
|
|
52
|
-
3. Do not write code.
|
|
53
|
-
4. Do not create implementation plans.
|
|
54
|
-
5. Focus only on requirement clarification.
|
|
52
|
+
Relevant project background.
|
|
55
53
|
|
|
56
|
-
|
|
54
|
+
# Constraints
|
|
57
55
|
|
|
58
|
-
|
|
56
|
+
Business or technical limitations.
|
|
57
|
+
|
|
58
|
+
# Risks
|
|
59
|
+
|
|
60
|
+
Potential pitfalls.
|
|
61
|
+
|
|
62
|
+
# Acceptance Criteria
|
|
63
|
+
|
|
64
|
+
Clear success conditions.
|
|
65
|
+
|
|
66
|
+
Requirements
|
|
67
|
+
|
|
68
|
+
* Maximum 500 words
|
|
69
|
+
* No code
|
|
70
|
+
* No architecture digression
|
|
71
|
+
* Only information required for execution
|
|
72
|
+
|
|
73
|
+
Output
|
|
74
|
+
|
|
75
|
+
TASK_DONE
|
|
59
76
|
`,
|
|
60
77
|
},
|
|
61
78
|
|
|
62
|
-
'task-
|
|
63
|
-
name: 'task-
|
|
64
|
-
description: '
|
|
79
|
+
'task-explore': {
|
|
80
|
+
name: 'task-explore',
|
|
81
|
+
description: 'Clarify a requirement and generate the execution brief in one step, without implementing.',
|
|
65
82
|
content: `---
|
|
66
|
-
name: task-
|
|
67
|
-
description:
|
|
83
|
+
name: task-explore
|
|
84
|
+
description: Clarify a requirement and generate the execution brief in one step, without implementing.
|
|
68
85
|
user-invocable: true
|
|
69
86
|
---
|
|
70
87
|
|
|
71
88
|
Purpose
|
|
72
89
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
Save Location
|
|
76
|
-
|
|
77
|
-
.ai/tasks/active/YYYY-MM-DD-task-name.md
|
|
90
|
+
Clarify requirements and leave behind a ready-to-execute brief.
|
|
78
91
|
|
|
79
|
-
|
|
92
|
+
Workflow
|
|
80
93
|
|
|
81
|
-
|
|
94
|
+
1. Explore the requirement through focused questions.
|
|
95
|
+
2. Continue until the task is sufficiently understood.
|
|
96
|
+
3. Do not write code.
|
|
97
|
+
4. Do not create implementation details.
|
|
98
|
+
5. Once the requirement is clear, generate a concise task brief and save it to:
|
|
82
99
|
|
|
83
|
-
|
|
100
|
+
.ai/tasks/active/YYYY-MM-DD-task-name.md
|
|
84
101
|
|
|
85
|
-
|
|
102
|
+
6. Show the saved brief and stop.
|
|
86
103
|
|
|
87
|
-
|
|
104
|
+
Task Brief Format
|
|
88
105
|
|
|
89
106
|
# Goal
|
|
90
107
|
|
|
@@ -110,40 +127,38 @@ Requirements
|
|
|
110
127
|
|
|
111
128
|
* Maximum 500 words
|
|
112
129
|
* No code
|
|
113
|
-
* No implementation details
|
|
114
130
|
* No architecture design
|
|
115
131
|
* Only information required for execution
|
|
116
132
|
|
|
117
|
-
|
|
133
|
+
When complete output:
|
|
118
134
|
|
|
119
|
-
|
|
120
|
-
2. Show the content.
|
|
121
|
-
3. Wait for approval.
|
|
135
|
+
TASK_READY
|
|
122
136
|
`,
|
|
123
137
|
},
|
|
124
138
|
|
|
125
139
|
'task-implement': {
|
|
126
140
|
name: 'task-implement',
|
|
127
|
-
description: 'Implement the task
|
|
141
|
+
description: 'Implement the latest active task brief and validate it. Archive automatically when complete.',
|
|
128
142
|
content: `---
|
|
129
143
|
name: task-implement
|
|
130
|
-
description: Implement the task
|
|
144
|
+
description: Implement the latest active task brief and validate it. Archive automatically when complete.
|
|
131
145
|
user-invocable: true
|
|
132
146
|
---
|
|
133
147
|
|
|
134
148
|
Purpose
|
|
135
149
|
|
|
136
|
-
Implement
|
|
150
|
+
Implement a task from the latest file in .ai/tasks/active/.
|
|
137
151
|
|
|
138
152
|
Rules
|
|
139
153
|
|
|
140
|
-
1.
|
|
141
|
-
2. Follow acceptance criteria strictly.
|
|
154
|
+
1. Read the latest relevant brief from .ai/tasks/active/.
|
|
155
|
+
2. Follow the acceptance criteria strictly.
|
|
142
156
|
3. Prefer minimal changes.
|
|
143
157
|
4. Respect existing project conventions.
|
|
144
158
|
5. Avoid unnecessary refactoring.
|
|
145
159
|
6. State assumptions explicitly.
|
|
146
|
-
7.
|
|
160
|
+
7. Validate the result before stopping.
|
|
161
|
+
8. If the work is complete, archive the brief automatically by moving it to .ai/tasks/archive/.
|
|
147
162
|
|
|
148
163
|
Output
|
|
149
164
|
|
|
@@ -158,21 +173,27 @@ Files modified.
|
|
|
158
173
|
## Validation
|
|
159
174
|
|
|
160
175
|
How acceptance criteria were satisfied.
|
|
176
|
+
|
|
161
177
|
`,
|
|
162
178
|
},
|
|
163
179
|
|
|
164
180
|
'task-review': {
|
|
165
181
|
name: 'task-review',
|
|
166
|
-
description: 'Review
|
|
182
|
+
description: 'Review the latest task implementation against the corresponding task brief.',
|
|
167
183
|
content: `---
|
|
168
184
|
name: task-review
|
|
169
|
-
description: Review
|
|
185
|
+
description: Review the latest task implementation against the corresponding task brief.
|
|
170
186
|
user-invocable: true
|
|
171
187
|
---
|
|
172
188
|
|
|
173
189
|
Purpose
|
|
174
190
|
|
|
175
|
-
Review implementation against task.
|
|
191
|
+
Review implementation against the corresponding task brief.
|
|
192
|
+
|
|
193
|
+
Rules
|
|
194
|
+
|
|
195
|
+
1. Use the latest matching brief from .ai/tasks/active/ or .ai/tasks/archive/.
|
|
196
|
+
2. Review the actual changes, not just the intent.
|
|
176
197
|
|
|
177
198
|
Evaluate
|
|
178
199
|
|
|
@@ -206,23 +227,23 @@ Anything not fully implemented.
|
|
|
206
227
|
|
|
207
228
|
'bug-explore': {
|
|
208
229
|
name: 'bug-explore',
|
|
209
|
-
description: 'Investigate a bug
|
|
230
|
+
description: 'Investigate a bug and generate a fix brief in one step, without writing code.',
|
|
210
231
|
content: `---
|
|
211
232
|
name: bug-explore
|
|
212
|
-
description: Investigate a bug
|
|
233
|
+
description: Investigate a bug and generate a fix brief in one step, without writing code.
|
|
213
234
|
user-invocable: true
|
|
214
235
|
---
|
|
215
236
|
|
|
216
237
|
Purpose
|
|
217
238
|
|
|
218
|
-
Investigate a bug
|
|
239
|
+
Investigate a bug and leave behind a ready-to-fix brief.
|
|
219
240
|
|
|
220
241
|
Rules
|
|
221
242
|
|
|
222
243
|
1. Do not write code.
|
|
223
|
-
2. Do not suggest fixes
|
|
244
|
+
2. Do not suggest fixes before enough evidence exists.
|
|
224
245
|
3. Identify root cause candidates.
|
|
225
|
-
4. Ask
|
|
246
|
+
4. Ask focused questions.
|
|
226
247
|
5. Request evidence whenever possible.
|
|
227
248
|
6. Separate:
|
|
228
249
|
|
|
@@ -230,38 +251,13 @@ Rules
|
|
|
230
251
|
* expected behavior
|
|
231
252
|
* assumptions
|
|
232
253
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
BUG_READY
|
|
236
|
-
`,
|
|
237
|
-
},
|
|
238
|
-
|
|
239
|
-
'bug-brief': {
|
|
240
|
-
name: 'bug-brief',
|
|
241
|
-
description: 'Summarize bug investigation into a brief saved to .ai/bugs/active/.',
|
|
242
|
-
content: `---
|
|
243
|
-
name: bug-brief
|
|
244
|
-
description: Summarize bug investigation into a brief saved to .ai/bugs/active/.
|
|
245
|
-
user-invocable: true
|
|
246
|
-
---
|
|
247
|
-
|
|
248
|
-
Purpose
|
|
249
|
-
|
|
250
|
-
Summarize bug investigation.
|
|
251
|
-
|
|
252
|
-
Save Location
|
|
254
|
+
7. Once the bug is sufficiently understood, generate a brief and save it to:
|
|
253
255
|
|
|
254
256
|
.ai/bugs/active/YYYY-MM-DD-bug-name.md
|
|
255
257
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
YYYY-MM-DD-short-bug-name.md
|
|
259
|
-
|
|
260
|
-
Example
|
|
261
|
-
|
|
262
|
-
2026-06-10-iframe-resize.md
|
|
258
|
+
8. Show the saved brief and stop.
|
|
263
259
|
|
|
264
|
-
|
|
260
|
+
Bug Brief Format
|
|
265
261
|
|
|
266
262
|
# Problem
|
|
267
263
|
|
|
@@ -287,34 +283,35 @@ Technical limitations.
|
|
|
287
283
|
|
|
288
284
|
Conditions proving the bug is fixed.
|
|
289
285
|
|
|
290
|
-
|
|
286
|
+
When sufficient evidence exists output:
|
|
291
287
|
|
|
292
|
-
|
|
293
|
-
2. Show the content.
|
|
294
|
-
3. Wait for approval.
|
|
288
|
+
BUG_READY
|
|
295
289
|
`,
|
|
296
290
|
},
|
|
297
291
|
|
|
298
292
|
'bug-fix': {
|
|
299
293
|
name: 'bug-fix',
|
|
300
|
-
description: 'Fix the
|
|
294
|
+
description: 'Fix the latest active bug brief and validate the result. Archive automatically when complete.',
|
|
301
295
|
content: `---
|
|
302
296
|
name: bug-fix
|
|
303
|
-
description: Fix the
|
|
297
|
+
description: Fix the latest active bug brief and validate the result. Archive automatically when complete.
|
|
304
298
|
user-invocable: true
|
|
305
299
|
---
|
|
306
300
|
|
|
307
301
|
Purpose
|
|
308
302
|
|
|
309
|
-
Fix
|
|
303
|
+
Fix a bug from the latest file in .ai/bugs/active/.
|
|
310
304
|
|
|
311
305
|
Rules
|
|
312
306
|
|
|
313
|
-
1.
|
|
314
|
-
2.
|
|
315
|
-
3.
|
|
316
|
-
4.
|
|
317
|
-
5.
|
|
307
|
+
1. Read the latest relevant brief from .ai/bugs/active/.
|
|
308
|
+
2. Minimize changes.
|
|
309
|
+
3. Avoid unrelated refactoring.
|
|
310
|
+
4. Fix root cause, not symptoms.
|
|
311
|
+
5. Preserve existing behavior.
|
|
312
|
+
6. Explain reasoning.
|
|
313
|
+
7. Validate the fix before stopping.
|
|
314
|
+
8. If the bug is fixed, archive the brief automatically by moving it to .ai/bugs/archive/.
|
|
318
315
|
|
|
319
316
|
Output
|
|
320
317
|
|
|
@@ -329,21 +326,27 @@ Changes made.
|
|
|
329
326
|
## Validation
|
|
330
327
|
|
|
331
328
|
Verification performed.
|
|
329
|
+
|
|
332
330
|
`,
|
|
333
331
|
},
|
|
334
332
|
|
|
335
333
|
'bug-review': {
|
|
336
334
|
name: 'bug-review',
|
|
337
|
-
description: 'Review bug fix against
|
|
335
|
+
description: 'Review the latest bug fix against the corresponding bug brief.',
|
|
338
336
|
content: `---
|
|
339
337
|
name: bug-review
|
|
340
|
-
description: Review bug fix against
|
|
338
|
+
description: Review the latest bug fix against the corresponding bug brief.
|
|
341
339
|
user-invocable: true
|
|
342
340
|
---
|
|
343
341
|
|
|
344
342
|
Purpose
|
|
345
343
|
|
|
346
|
-
Review implementation against bug.
|
|
344
|
+
Review implementation against the corresponding bug brief.
|
|
345
|
+
|
|
346
|
+
Rules
|
|
347
|
+
|
|
348
|
+
1. Use the latest matching brief from .ai/bugs/active/ or .ai/bugs/archive/.
|
|
349
|
+
2. Check whether the reported root cause was truly addressed.
|
|
347
350
|
|
|
348
351
|
Check
|
|
349
352
|
|
|
@@ -415,43 +418,44 @@ Requirements
|
|
|
415
418
|
},
|
|
416
419
|
};
|
|
417
420
|
|
|
418
|
-
|
|
419
|
-
// Directory structure
|
|
420
|
-
const dirs = [
|
|
421
|
-
'.ai',
|
|
422
|
-
'.ai/tasks/active',
|
|
423
|
-
'.ai/tasks/archive',
|
|
424
|
-
'.ai/bugs/active',
|
|
425
|
-
'.ai/bugs/archive',
|
|
426
|
-
'.ai/decisions',
|
|
427
|
-
'.claude/skills',
|
|
428
|
-
];
|
|
421
|
+
const MANAGED_SKILL_NAMES = Object.values(SKILLS).map((skill) => skill.name);
|
|
429
422
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
if (!fs.existsSync(full)) {
|
|
434
|
-
fs.mkdirSync(full, { recursive: true });
|
|
435
|
-
log.chalk.green(` ✓ ${dir}`);
|
|
436
|
-
} else {
|
|
437
|
-
log.chalk.dim(` - ${dir} (exists)`);
|
|
438
|
-
}
|
|
439
|
-
}
|
|
423
|
+
function skillFilePath(path, cwd, skillRoot, skillName) {
|
|
424
|
+
return path.join(cwd, skillRoot, skillName, 'SKILL.md');
|
|
425
|
+
}
|
|
440
426
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
}
|
|
447
|
-
|
|
427
|
+
function logCheck(log, ok, label, detail) {
|
|
428
|
+
if (ok) {
|
|
429
|
+
log.chalk.green(` OK ${label}${detail ? ` - ${detail}` : ''}`);
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
console.log(chalk.yellow(` WARN ${label}${detail ? ` - ${detail}` : ''}`));
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
function ensureDir(fs, path, baseDir, relativeDir, log) {
|
|
436
|
+
const full = path.join(baseDir, relativeDir);
|
|
437
|
+
if (!fs.existsSync(full)) {
|
|
438
|
+
fs.mkdirSync(full, { recursive: true });
|
|
439
|
+
log.chalk.green(` ✓ ${relativeDir}`);
|
|
440
|
+
return;
|
|
448
441
|
}
|
|
442
|
+
log.chalk.dim(` - ${relativeDir} (exists)`);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
function ensureFile(fs, path, filePath, content, log) {
|
|
446
|
+
if (!fs.existsSync(filePath)) {
|
|
447
|
+
fs.writeFileSync(filePath, content);
|
|
448
|
+
log.chalk.green(` ✓ ${path.relative(process.cwd(), filePath)}`);
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
log.chalk.dim(` - ${path.relative(process.cwd(), filePath)} (exists)`);
|
|
452
|
+
}
|
|
449
453
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
for (const
|
|
453
|
-
const skillDir = path.join(cwd,
|
|
454
|
-
const skillFile = path
|
|
454
|
+
function installSkills(fs, path, cwd, skillRoot, log) {
|
|
455
|
+
ensureDir(fs, path, cwd, skillRoot, log);
|
|
456
|
+
for (const skill of Object.values(SKILLS)) {
|
|
457
|
+
const skillDir = path.join(cwd, skillRoot, skill.name);
|
|
458
|
+
const skillFile = skillFilePath(path, cwd, skillRoot, skill.name);
|
|
455
459
|
|
|
456
460
|
if (!fs.existsSync(skillDir)) {
|
|
457
461
|
fs.mkdirSync(skillDir, { recursive: true });
|
|
@@ -459,26 +463,185 @@ export function init(cwd, { fs, path, log }) {
|
|
|
459
463
|
|
|
460
464
|
if (!fs.existsSync(skillFile)) {
|
|
461
465
|
fs.writeFileSync(skillFile, skill.content);
|
|
462
|
-
log.chalk.green(` ✓ ${skill.name}`);
|
|
466
|
+
log.chalk.green(` ✓ ${skillRoot}/${skill.name}`);
|
|
463
467
|
} else {
|
|
464
|
-
log.chalk.dim(` - ${skill.name} (exists)`);
|
|
468
|
+
log.chalk.dim(` - ${skillRoot}/${skill.name} (exists)`);
|
|
465
469
|
}
|
|
466
470
|
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
function removeManagedSkills(fs, path, cwd, skillRoot, log) {
|
|
474
|
+
const rootPath = path.join(cwd, skillRoot);
|
|
475
|
+
if (!fs.existsSync(rootPath)) {
|
|
476
|
+
log.chalk.dim(` - ${skillRoot} (missing)`);
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
for (const skillName of MANAGED_SKILL_NAMES) {
|
|
481
|
+
const skillDir = path.join(rootPath, skillName);
|
|
482
|
+
if (!fs.existsSync(skillDir)) {
|
|
483
|
+
log.chalk.dim(` - ${skillRoot}/${skillName} (missing)`);
|
|
484
|
+
continue;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
fs.rmSync(skillDir, { recursive: true, force: true });
|
|
488
|
+
log.chalk.green(` ✓ removed ${skillRoot}/${skillName}`);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
467
491
|
|
|
468
|
-
|
|
492
|
+
function updateGitignore(fs, path, cwd, log) {
|
|
469
493
|
const gitignorePath = path.join(cwd, '.gitignore');
|
|
470
|
-
const
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
494
|
+
const existing = fs.existsSync(gitignorePath)
|
|
495
|
+
? fs.readFileSync(gitignorePath, 'utf-8')
|
|
496
|
+
: '';
|
|
497
|
+
|
|
498
|
+
if (existing.includes('# task workflow')) {
|
|
499
|
+
log.chalk.dim(' - .gitignore (task workflow block exists)');
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
const prefix = existing.trimEnd();
|
|
504
|
+
const next = prefix ? `${prefix}\n\n${GITIGNORE_BLOCK}\n` : `${GITIGNORE_BLOCK}\n`;
|
|
505
|
+
fs.writeFileSync(gitignorePath, next);
|
|
506
|
+
log.chalk.green(' ✓ .gitignore updated');
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
export function init(cwd, { fs, path, log }) {
|
|
510
|
+
const dirs = [
|
|
511
|
+
'.ai',
|
|
512
|
+
TASK_ACTIVE_DIR,
|
|
513
|
+
TASK_ARCHIVE_DIR,
|
|
514
|
+
BUG_ACTIVE_DIR,
|
|
515
|
+
BUG_ARCHIVE_DIR,
|
|
516
|
+
'.ai/decisions',
|
|
517
|
+
];
|
|
518
|
+
|
|
519
|
+
log.info('Creating directory structure...');
|
|
520
|
+
for (const dir of dirs) {
|
|
521
|
+
ensureDir(fs, path, cwd, dir, log);
|
|
474
522
|
}
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
523
|
+
|
|
524
|
+
const decisionsPath = path.join(cwd, DECISIONS_FILE);
|
|
525
|
+
ensureFile(fs, path, decisionsPath, '# Decisions Log\n\n', log);
|
|
526
|
+
|
|
527
|
+
log.info('\nInstalling workflow skills...');
|
|
528
|
+
installSkills(fs, path, cwd, CLAUDE_SKILLS_DIR, log);
|
|
529
|
+
installSkills(fs, path, cwd, CODEX_SKILLS_DIR, log);
|
|
530
|
+
|
|
531
|
+
log.info('\nUpdating ignore rules...');
|
|
532
|
+
updateGitignore(fs, path, cwd, log);
|
|
533
|
+
|
|
534
|
+
log.info(`\nTask workflow initialized. Recommended flows:
|
|
535
|
+
fast: task-fast
|
|
536
|
+
task: task-explore -> task-implement -> task-review
|
|
537
|
+
bug: bug-explore -> bug-fix -> bug-review
|
|
538
|
+
other: decision-log`);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
export function refresh(cwd, { fs, path, log }) {
|
|
542
|
+
const dirs = [
|
|
543
|
+
'.ai',
|
|
544
|
+
TASK_ACTIVE_DIR,
|
|
545
|
+
TASK_ARCHIVE_DIR,
|
|
546
|
+
BUG_ACTIVE_DIR,
|
|
547
|
+
BUG_ARCHIVE_DIR,
|
|
548
|
+
'.ai/decisions',
|
|
549
|
+
];
|
|
550
|
+
|
|
551
|
+
log.info('Ensuring directory structure...');
|
|
552
|
+
for (const dir of dirs) {
|
|
553
|
+
ensureDir(fs, path, cwd, dir, log);
|
|
478
554
|
}
|
|
479
555
|
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
556
|
+
const decisionsPath = path.join(cwd, DECISIONS_FILE);
|
|
557
|
+
ensureFile(fs, path, decisionsPath, '# Decisions Log\n\n', log);
|
|
558
|
+
|
|
559
|
+
log.info('\nRefreshing managed workflow skills...');
|
|
560
|
+
removeManagedSkills(fs, path, cwd, CLAUDE_SKILLS_DIR, log);
|
|
561
|
+
removeManagedSkills(fs, path, cwd, CODEX_SKILLS_DIR, log);
|
|
562
|
+
installSkills(fs, path, cwd, CLAUDE_SKILLS_DIR, log);
|
|
563
|
+
installSkills(fs, path, cwd, CODEX_SKILLS_DIR, log);
|
|
564
|
+
|
|
565
|
+
log.info('\nUpdating ignore rules...');
|
|
566
|
+
updateGitignore(fs, path, cwd, log);
|
|
567
|
+
|
|
568
|
+
log.info(`\nTask workflow refreshed. Managed skills reinstalled:
|
|
569
|
+
fast: task-fast
|
|
570
|
+
task: task-explore -> task-implement -> task-review
|
|
571
|
+
bug: bug-explore -> bug-fix -> bug-review
|
|
483
572
|
other: decision-log`);
|
|
484
|
-
}
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
export function doctor(cwd, { fs, path, log }) {
|
|
576
|
+
const checks = [];
|
|
577
|
+
const requiredDirs = [
|
|
578
|
+
'.ai',
|
|
579
|
+
TASK_ACTIVE_DIR,
|
|
580
|
+
TASK_ARCHIVE_DIR,
|
|
581
|
+
BUG_ACTIVE_DIR,
|
|
582
|
+
BUG_ARCHIVE_DIR,
|
|
583
|
+
'.ai/decisions',
|
|
584
|
+
CLAUDE_SKILLS_DIR,
|
|
585
|
+
CODEX_SKILLS_DIR,
|
|
586
|
+
];
|
|
587
|
+
|
|
588
|
+
log.info('Checking task workflow setup...');
|
|
589
|
+
|
|
590
|
+
for (const dir of requiredDirs) {
|
|
591
|
+
const full = path.join(cwd, dir);
|
|
592
|
+
const exists = fs.existsSync(full);
|
|
593
|
+
checks.push(exists);
|
|
594
|
+
logCheck(log, exists, dir, exists ? 'present' : 'missing');
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
const decisionsPath = path.join(cwd, DECISIONS_FILE);
|
|
598
|
+
const decisionsExists = fs.existsSync(decisionsPath);
|
|
599
|
+
checks.push(decisionsExists);
|
|
600
|
+
logCheck(log, decisionsExists, DECISIONS_FILE, decisionsExists ? 'present' : 'missing');
|
|
601
|
+
|
|
602
|
+
for (const skillRoot of [CLAUDE_SKILLS_DIR, CODEX_SKILLS_DIR]) {
|
|
603
|
+
for (const skill of Object.values(SKILLS)) {
|
|
604
|
+
const skillPath = skillFilePath(path, cwd, skillRoot, skill.name);
|
|
605
|
+
if (!fs.existsSync(skillPath)) {
|
|
606
|
+
checks.push(false);
|
|
607
|
+
logCheck(log, false, `${skillRoot}/${skill.name}`, 'missing');
|
|
608
|
+
continue;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
const content = fs.readFileSync(skillPath, 'utf-8');
|
|
612
|
+
const matches = content === skill.content;
|
|
613
|
+
checks.push(matches);
|
|
614
|
+
logCheck(
|
|
615
|
+
log,
|
|
616
|
+
matches,
|
|
617
|
+
`${skillRoot}/${skill.name}`,
|
|
618
|
+
matches ? 'current' : 'outdated, run `task refresh`'
|
|
619
|
+
);
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
const gitignorePath = path.join(cwd, '.gitignore');
|
|
624
|
+
const gitignore = fs.existsSync(gitignorePath)
|
|
625
|
+
? fs.readFileSync(gitignorePath, 'utf-8')
|
|
626
|
+
: '';
|
|
627
|
+
const hasGitignoreBlock = gitignore.includes(GITIGNORE_BLOCK);
|
|
628
|
+
checks.push(hasGitignoreBlock);
|
|
629
|
+
logCheck(
|
|
630
|
+
log,
|
|
631
|
+
hasGitignoreBlock,
|
|
632
|
+
'.gitignore',
|
|
633
|
+
hasGitignoreBlock ? 'task workflow rules present' : 'missing task workflow rules'
|
|
634
|
+
);
|
|
635
|
+
|
|
636
|
+
const okCount = checks.filter(Boolean).length;
|
|
637
|
+
const totalCount = checks.length;
|
|
638
|
+
const allGood = okCount === totalCount;
|
|
639
|
+
|
|
640
|
+
log.info(`\nSummary: ${okCount}/${totalCount} checks passed.`);
|
|
641
|
+
if (allGood) {
|
|
642
|
+
log.chalk.green('Task workflow is healthy.');
|
|
643
|
+
return;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
console.log(chalk.yellow('Recommended next step: run `task refresh` to reinstall managed skills and repair setup.'));
|
|
647
|
+
}
|