@winton979/task-cli 1.0.3 → 1.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.
- package/README.md +55 -16
- package/package.json +1 -1
- package/src/cli.js +16 -3
- package/src/init.js +325 -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, 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 + archive
|
|
72
74
|
↓
|
|
73
75
|
/task-review
|
|
74
76
|
```
|
|
@@ -80,10 +82,10 @@ task brief generated
|
|
|
80
82
|
↓
|
|
81
83
|
TASK_READY
|
|
82
84
|
↓
|
|
83
|
-
/task-brief
|
|
84
|
-
↓
|
|
85
85
|
/task-implement
|
|
86
86
|
↓
|
|
87
|
+
/task archived
|
|
88
|
+
↓
|
|
87
89
|
/task-review
|
|
88
90
|
```
|
|
89
91
|
|
|
@@ -94,10 +96,10 @@ TASK_READY
|
|
|
94
96
|
↓
|
|
95
97
|
BUG_READY
|
|
96
98
|
↓
|
|
97
|
-
/bug-brief
|
|
98
|
-
↓
|
|
99
99
|
/bug-fix
|
|
100
100
|
↓
|
|
101
|
+
/bug archived
|
|
102
|
+
↓
|
|
101
103
|
/bug-review
|
|
102
104
|
```
|
|
103
105
|
|
|
@@ -109,14 +111,12 @@ BUG_READY
|
|
|
109
111
|
|
|
110
112
|
* task-fast
|
|
111
113
|
* task-explore
|
|
112
|
-
* task-brief
|
|
113
114
|
* task-implement
|
|
114
115
|
* task-review
|
|
115
116
|
|
|
116
117
|
### Bug Workflow
|
|
117
118
|
|
|
118
119
|
* bug-explore
|
|
119
|
-
* bug-brief
|
|
120
120
|
* bug-fix
|
|
121
121
|
* bug-review
|
|
122
122
|
|
|
@@ -141,7 +141,8 @@ BUG_READY
|
|
|
141
141
|
├── decisions/
|
|
142
142
|
│ └── decisions.md
|
|
143
143
|
│
|
|
144
|
-
|
|
144
|
+
├── .claude/skills/
|
|
145
|
+
└── .codex/skills/
|
|
145
146
|
```
|
|
146
147
|
|
|
147
148
|
---
|
|
@@ -154,11 +155,49 @@ Instead of maintaining large specifications, it focuses on:
|
|
|
154
155
|
|
|
155
156
|
1. Clarifying requirements before coding
|
|
156
157
|
2. Capturing execution context in concise briefs
|
|
157
|
-
3.
|
|
158
|
-
4.
|
|
158
|
+
3. Executing with validation and lightweight archiving
|
|
159
|
+
4. Reviewing work against acceptance criteria
|
|
160
|
+
5. Keeping a lightweight decision history
|
|
159
161
|
|
|
160
162
|
The goal is to improve quality without slowing down iteration speed.
|
|
161
163
|
|
|
162
164
|
|
|
165
|
+
## Can This Be Simpler?
|
|
166
|
+
|
|
167
|
+
Yes. The main simplification is to collapse the old 4-step paths:
|
|
168
|
+
|
|
169
|
+
* `task-explore` now includes brief generation.
|
|
170
|
+
* `bug-explore` now includes bug brief generation.
|
|
171
|
+
* `task-implement` and `bug-fix` now validate and archive the brief when the work is complete.
|
|
172
|
+
|
|
173
|
+
That leaves these practical flows:
|
|
174
|
+
|
|
175
|
+
* Explore only: `/task-explore` or `/bug-explore`
|
|
176
|
+
* Execute and archive: `/task-implement` or `/bug-fix`
|
|
177
|
+
* Review: `/task-review` or `/bug-review`
|
|
178
|
+
* One-shot small work: `/task-fast`
|
|
179
|
+
|
|
180
|
+
## Upgrading Existing Projects
|
|
181
|
+
|
|
182
|
+
If a project was initialized with an older version of task-cli, run:
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
task refresh
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
This will:
|
|
189
|
+
|
|
190
|
+
* keep `.ai/tasks`, `.ai/bugs`, and `.ai/decisions`
|
|
191
|
+
* 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`
|
|
192
|
+
* reinstall the latest versions of those skills
|
|
193
|
+
|
|
194
|
+
This avoids touching unrelated custom skills in the same project.
|
|
195
|
+
|
|
196
|
+
Before refreshing, you can inspect the current setup with:
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
task doctor
|
|
200
|
+
```
|
|
201
|
+
|
|
163
202
|
> Task CLI does not install Grill Me automatically.
|
|
164
|
-
> Users remain free to choose any Grill Me compatible implementation.
|
|
203
|
+
> 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, verify, and archive.',
|
|
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, verify, and archive.
|
|
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. Move the brief 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
|
-
|
|
90
|
+
Clarify requirements and leave behind a ready-to-execute brief.
|
|
74
91
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
.ai/tasks/active/YYYY-MM-DD-task-name.md
|
|
78
|
-
|
|
79
|
-
Naming Convention
|
|
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
|
|
141
|
+
description: 'Implement the latest active task brief, validate it, and archive it when complete.',
|
|
128
142
|
content: `---
|
|
129
143
|
name: task-implement
|
|
130
|
-
description: Implement the
|
|
144
|
+
description: Implement the latest active task brief, validate it, and archive it 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, move the brief to .ai/tasks/archive/.
|
|
147
162
|
|
|
148
163
|
Output
|
|
149
164
|
|
|
@@ -158,21 +173,30 @@ Files modified.
|
|
|
158
173
|
## Validation
|
|
159
174
|
|
|
160
175
|
How acceptance criteria were satisfied.
|
|
176
|
+
|
|
177
|
+
## Archive
|
|
178
|
+
|
|
179
|
+
Whether the brief was archived.
|
|
161
180
|
`,
|
|
162
181
|
},
|
|
163
182
|
|
|
164
183
|
'task-review': {
|
|
165
184
|
name: 'task-review',
|
|
166
|
-
description: 'Review
|
|
185
|
+
description: 'Review the latest task implementation against the active or archived task brief.',
|
|
167
186
|
content: `---
|
|
168
187
|
name: task-review
|
|
169
|
-
description: Review
|
|
188
|
+
description: Review the latest task implementation against the active or archived task brief.
|
|
170
189
|
user-invocable: true
|
|
171
190
|
---
|
|
172
191
|
|
|
173
192
|
Purpose
|
|
174
193
|
|
|
175
|
-
Review implementation against task.
|
|
194
|
+
Review implementation against the corresponding task brief.
|
|
195
|
+
|
|
196
|
+
Rules
|
|
197
|
+
|
|
198
|
+
1. Use the latest matching brief from .ai/tasks/active/ or .ai/tasks/archive/.
|
|
199
|
+
2. Review the actual changes, not just the intent.
|
|
176
200
|
|
|
177
201
|
Evaluate
|
|
178
202
|
|
|
@@ -206,23 +230,23 @@ Anything not fully implemented.
|
|
|
206
230
|
|
|
207
231
|
'bug-explore': {
|
|
208
232
|
name: 'bug-explore',
|
|
209
|
-
description: 'Investigate a bug
|
|
233
|
+
description: 'Investigate a bug and generate a fix brief in one step, without writing code.',
|
|
210
234
|
content: `---
|
|
211
235
|
name: bug-explore
|
|
212
|
-
description: Investigate a bug
|
|
236
|
+
description: Investigate a bug and generate a fix brief in one step, without writing code.
|
|
213
237
|
user-invocable: true
|
|
214
238
|
---
|
|
215
239
|
|
|
216
240
|
Purpose
|
|
217
241
|
|
|
218
|
-
Investigate a bug
|
|
242
|
+
Investigate a bug and leave behind a ready-to-fix brief.
|
|
219
243
|
|
|
220
244
|
Rules
|
|
221
245
|
|
|
222
246
|
1. Do not write code.
|
|
223
|
-
2. Do not suggest fixes
|
|
247
|
+
2. Do not suggest fixes before enough evidence exists.
|
|
224
248
|
3. Identify root cause candidates.
|
|
225
|
-
4. Ask
|
|
249
|
+
4. Ask focused questions.
|
|
226
250
|
5. Request evidence whenever possible.
|
|
227
251
|
6. Separate:
|
|
228
252
|
|
|
@@ -230,38 +254,13 @@ Rules
|
|
|
230
254
|
* expected behavior
|
|
231
255
|
* assumptions
|
|
232
256
|
|
|
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
|
|
257
|
+
7. Once the bug is sufficiently understood, generate a brief and save it to:
|
|
253
258
|
|
|
254
259
|
.ai/bugs/active/YYYY-MM-DD-bug-name.md
|
|
255
260
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
YYYY-MM-DD-short-bug-name.md
|
|
259
|
-
|
|
260
|
-
Example
|
|
261
|
-
|
|
262
|
-
2026-06-10-iframe-resize.md
|
|
261
|
+
8. Show the saved brief and stop.
|
|
263
262
|
|
|
264
|
-
|
|
263
|
+
Bug Brief Format
|
|
265
264
|
|
|
266
265
|
# Problem
|
|
267
266
|
|
|
@@ -287,34 +286,35 @@ Technical limitations.
|
|
|
287
286
|
|
|
288
287
|
Conditions proving the bug is fixed.
|
|
289
288
|
|
|
290
|
-
|
|
289
|
+
When sufficient evidence exists output:
|
|
291
290
|
|
|
292
|
-
|
|
293
|
-
2. Show the content.
|
|
294
|
-
3. Wait for approval.
|
|
291
|
+
BUG_READY
|
|
295
292
|
`,
|
|
296
293
|
},
|
|
297
294
|
|
|
298
295
|
'bug-fix': {
|
|
299
296
|
name: 'bug-fix',
|
|
300
|
-
description: 'Fix the
|
|
297
|
+
description: 'Fix the latest active bug brief, validate the result, and archive the brief when complete.',
|
|
301
298
|
content: `---
|
|
302
299
|
name: bug-fix
|
|
303
|
-
description: Fix the
|
|
300
|
+
description: Fix the latest active bug brief, validate the result, and archive the brief when complete.
|
|
304
301
|
user-invocable: true
|
|
305
302
|
---
|
|
306
303
|
|
|
307
304
|
Purpose
|
|
308
305
|
|
|
309
|
-
Fix
|
|
306
|
+
Fix a bug from the latest file in .ai/bugs/active/.
|
|
310
307
|
|
|
311
308
|
Rules
|
|
312
309
|
|
|
313
|
-
1.
|
|
314
|
-
2.
|
|
315
|
-
3.
|
|
316
|
-
4.
|
|
317
|
-
5.
|
|
310
|
+
1. Read the latest relevant brief from .ai/bugs/active/.
|
|
311
|
+
2. Minimize changes.
|
|
312
|
+
3. Avoid unrelated refactoring.
|
|
313
|
+
4. Fix root cause, not symptoms.
|
|
314
|
+
5. Preserve existing behavior.
|
|
315
|
+
6. Explain reasoning.
|
|
316
|
+
7. Validate the fix before stopping.
|
|
317
|
+
8. If the bug is fixed, move the brief to .ai/bugs/archive/.
|
|
318
318
|
|
|
319
319
|
Output
|
|
320
320
|
|
|
@@ -329,21 +329,30 @@ Changes made.
|
|
|
329
329
|
## Validation
|
|
330
330
|
|
|
331
331
|
Verification performed.
|
|
332
|
+
|
|
333
|
+
## Archive
|
|
334
|
+
|
|
335
|
+
Whether the brief was archived.
|
|
332
336
|
`,
|
|
333
337
|
},
|
|
334
338
|
|
|
335
339
|
'bug-review': {
|
|
336
340
|
name: 'bug-review',
|
|
337
|
-
description: 'Review bug fix against
|
|
341
|
+
description: 'Review the latest bug fix against the active or archived bug brief.',
|
|
338
342
|
content: `---
|
|
339
343
|
name: bug-review
|
|
340
|
-
description: Review bug fix against
|
|
344
|
+
description: Review the latest bug fix against the active or archived bug brief.
|
|
341
345
|
user-invocable: true
|
|
342
346
|
---
|
|
343
347
|
|
|
344
348
|
Purpose
|
|
345
349
|
|
|
346
|
-
Review implementation against bug.
|
|
350
|
+
Review implementation against the corresponding bug brief.
|
|
351
|
+
|
|
352
|
+
Rules
|
|
353
|
+
|
|
354
|
+
1. Use the latest matching brief from .ai/bugs/active/ or .ai/bugs/archive/.
|
|
355
|
+
2. Check whether the reported root cause was truly addressed.
|
|
347
356
|
|
|
348
357
|
Check
|
|
349
358
|
|
|
@@ -415,43 +424,44 @@ Requirements
|
|
|
415
424
|
},
|
|
416
425
|
};
|
|
417
426
|
|
|
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
|
-
];
|
|
427
|
+
const MANAGED_SKILL_NAMES = Object.values(SKILLS).map((skill) => skill.name);
|
|
429
428
|
|
|
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
|
-
}
|
|
429
|
+
function skillFilePath(path, cwd, skillRoot, skillName) {
|
|
430
|
+
return path.join(cwd, skillRoot, skillName, 'SKILL.md');
|
|
431
|
+
}
|
|
440
432
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
log.chalk.green(' ✓ .ai/decisions/decisions.md');
|
|
446
|
-
} else {
|
|
447
|
-
log.chalk.dim(' - .ai/decisions/decisions.md (exists)');
|
|
433
|
+
function logCheck(log, ok, label, detail) {
|
|
434
|
+
if (ok) {
|
|
435
|
+
log.chalk.green(` OK ${label}${detail ? ` - ${detail}` : ''}`);
|
|
436
|
+
return;
|
|
448
437
|
}
|
|
438
|
+
console.log(chalk.yellow(` WARN ${label}${detail ? ` - ${detail}` : ''}`));
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
function ensureDir(fs, path, baseDir, relativeDir, log) {
|
|
442
|
+
const full = path.join(baseDir, relativeDir);
|
|
443
|
+
if (!fs.existsSync(full)) {
|
|
444
|
+
fs.mkdirSync(full, { recursive: true });
|
|
445
|
+
log.chalk.green(` ✓ ${relativeDir}`);
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
log.chalk.dim(` - ${relativeDir} (exists)`);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
function ensureFile(fs, path, filePath, content, log) {
|
|
452
|
+
if (!fs.existsSync(filePath)) {
|
|
453
|
+
fs.writeFileSync(filePath, content);
|
|
454
|
+
log.chalk.green(` ✓ ${path.relative(process.cwd(), filePath)}`);
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
log.chalk.dim(` - ${path.relative(process.cwd(), filePath)} (exists)`);
|
|
458
|
+
}
|
|
449
459
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
for (const
|
|
453
|
-
const skillDir = path.join(cwd,
|
|
454
|
-
const skillFile = path
|
|
460
|
+
function installSkills(fs, path, cwd, skillRoot, log) {
|
|
461
|
+
ensureDir(fs, path, cwd, skillRoot, log);
|
|
462
|
+
for (const skill of Object.values(SKILLS)) {
|
|
463
|
+
const skillDir = path.join(cwd, skillRoot, skill.name);
|
|
464
|
+
const skillFile = skillFilePath(path, cwd, skillRoot, skill.name);
|
|
455
465
|
|
|
456
466
|
if (!fs.existsSync(skillDir)) {
|
|
457
467
|
fs.mkdirSync(skillDir, { recursive: true });
|
|
@@ -459,26 +469,185 @@ export function init(cwd, { fs, path, log }) {
|
|
|
459
469
|
|
|
460
470
|
if (!fs.existsSync(skillFile)) {
|
|
461
471
|
fs.writeFileSync(skillFile, skill.content);
|
|
462
|
-
log.chalk.green(` ✓ ${skill.name}`);
|
|
472
|
+
log.chalk.green(` ✓ ${skillRoot}/${skill.name}`);
|
|
463
473
|
} else {
|
|
464
|
-
log.chalk.dim(` - ${skill.name} (exists)`);
|
|
474
|
+
log.chalk.dim(` - ${skillRoot}/${skill.name} (exists)`);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
function removeManagedSkills(fs, path, cwd, skillRoot, log) {
|
|
480
|
+
const rootPath = path.join(cwd, skillRoot);
|
|
481
|
+
if (!fs.existsSync(rootPath)) {
|
|
482
|
+
log.chalk.dim(` - ${skillRoot} (missing)`);
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
for (const skillName of MANAGED_SKILL_NAMES) {
|
|
487
|
+
const skillDir = path.join(rootPath, skillName);
|
|
488
|
+
if (!fs.existsSync(skillDir)) {
|
|
489
|
+
log.chalk.dim(` - ${skillRoot}/${skillName} (missing)`);
|
|
490
|
+
continue;
|
|
465
491
|
}
|
|
492
|
+
|
|
493
|
+
fs.rmSync(skillDir, { recursive: true, force: true });
|
|
494
|
+
log.chalk.green(` ✓ removed ${skillRoot}/${skillName}`);
|
|
466
495
|
}
|
|
496
|
+
}
|
|
467
497
|
|
|
468
|
-
|
|
498
|
+
function updateGitignore(fs, path, cwd, log) {
|
|
469
499
|
const gitignorePath = path.join(cwd, '.gitignore');
|
|
470
|
-
const
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
500
|
+
const existing = fs.existsSync(gitignorePath)
|
|
501
|
+
? fs.readFileSync(gitignorePath, 'utf-8')
|
|
502
|
+
: '';
|
|
503
|
+
|
|
504
|
+
if (existing.includes('# task workflow')) {
|
|
505
|
+
log.chalk.dim(' - .gitignore (task workflow block exists)');
|
|
506
|
+
return;
|
|
474
507
|
}
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
508
|
+
|
|
509
|
+
const prefix = existing.trimEnd();
|
|
510
|
+
const next = prefix ? `${prefix}\n\n${GITIGNORE_BLOCK}\n` : `${GITIGNORE_BLOCK}\n`;
|
|
511
|
+
fs.writeFileSync(gitignorePath, next);
|
|
512
|
+
log.chalk.green(' ✓ .gitignore updated');
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
export function init(cwd, { fs, path, log }) {
|
|
516
|
+
const dirs = [
|
|
517
|
+
'.ai',
|
|
518
|
+
TASK_ACTIVE_DIR,
|
|
519
|
+
TASK_ARCHIVE_DIR,
|
|
520
|
+
BUG_ACTIVE_DIR,
|
|
521
|
+
BUG_ARCHIVE_DIR,
|
|
522
|
+
'.ai/decisions',
|
|
523
|
+
];
|
|
524
|
+
|
|
525
|
+
log.info('Creating directory structure...');
|
|
526
|
+
for (const dir of dirs) {
|
|
527
|
+
ensureDir(fs, path, cwd, dir, log);
|
|
478
528
|
}
|
|
479
529
|
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
530
|
+
const decisionsPath = path.join(cwd, DECISIONS_FILE);
|
|
531
|
+
ensureFile(fs, path, decisionsPath, '# Decisions Log\n\n', log);
|
|
532
|
+
|
|
533
|
+
log.info('\nInstalling workflow skills...');
|
|
534
|
+
installSkills(fs, path, cwd, CLAUDE_SKILLS_DIR, log);
|
|
535
|
+
installSkills(fs, path, cwd, CODEX_SKILLS_DIR, log);
|
|
536
|
+
|
|
537
|
+
log.info('\nUpdating ignore rules...');
|
|
538
|
+
updateGitignore(fs, path, cwd, log);
|
|
539
|
+
|
|
540
|
+
log.info(`\nTask workflow initialized. Recommended flows:
|
|
541
|
+
fast: task-fast
|
|
542
|
+
task: task-explore -> task-implement -> task-review
|
|
543
|
+
bug: bug-explore -> bug-fix -> bug-review
|
|
483
544
|
other: decision-log`);
|
|
484
|
-
}
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
export function refresh(cwd, { fs, path, log }) {
|
|
548
|
+
const dirs = [
|
|
549
|
+
'.ai',
|
|
550
|
+
TASK_ACTIVE_DIR,
|
|
551
|
+
TASK_ARCHIVE_DIR,
|
|
552
|
+
BUG_ACTIVE_DIR,
|
|
553
|
+
BUG_ARCHIVE_DIR,
|
|
554
|
+
'.ai/decisions',
|
|
555
|
+
];
|
|
556
|
+
|
|
557
|
+
log.info('Ensuring directory structure...');
|
|
558
|
+
for (const dir of dirs) {
|
|
559
|
+
ensureDir(fs, path, cwd, dir, log);
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
const decisionsPath = path.join(cwd, DECISIONS_FILE);
|
|
563
|
+
ensureFile(fs, path, decisionsPath, '# Decisions Log\n\n', log);
|
|
564
|
+
|
|
565
|
+
log.info('\nRefreshing managed workflow skills...');
|
|
566
|
+
removeManagedSkills(fs, path, cwd, CLAUDE_SKILLS_DIR, log);
|
|
567
|
+
removeManagedSkills(fs, path, cwd, CODEX_SKILLS_DIR, log);
|
|
568
|
+
installSkills(fs, path, cwd, CLAUDE_SKILLS_DIR, log);
|
|
569
|
+
installSkills(fs, path, cwd, CODEX_SKILLS_DIR, log);
|
|
570
|
+
|
|
571
|
+
log.info('\nUpdating ignore rules...');
|
|
572
|
+
updateGitignore(fs, path, cwd, log);
|
|
573
|
+
|
|
574
|
+
log.info(`\nTask workflow refreshed. Managed skills reinstalled:
|
|
575
|
+
fast: task-fast
|
|
576
|
+
task: task-explore -> task-implement -> task-review
|
|
577
|
+
bug: bug-explore -> bug-fix -> bug-review
|
|
578
|
+
other: decision-log`);
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
export function doctor(cwd, { fs, path, log }) {
|
|
582
|
+
const checks = [];
|
|
583
|
+
const requiredDirs = [
|
|
584
|
+
'.ai',
|
|
585
|
+
TASK_ACTIVE_DIR,
|
|
586
|
+
TASK_ARCHIVE_DIR,
|
|
587
|
+
BUG_ACTIVE_DIR,
|
|
588
|
+
BUG_ARCHIVE_DIR,
|
|
589
|
+
'.ai/decisions',
|
|
590
|
+
CLAUDE_SKILLS_DIR,
|
|
591
|
+
CODEX_SKILLS_DIR,
|
|
592
|
+
];
|
|
593
|
+
|
|
594
|
+
log.info('Checking task workflow setup...');
|
|
595
|
+
|
|
596
|
+
for (const dir of requiredDirs) {
|
|
597
|
+
const full = path.join(cwd, dir);
|
|
598
|
+
const exists = fs.existsSync(full);
|
|
599
|
+
checks.push(exists);
|
|
600
|
+
logCheck(log, exists, dir, exists ? 'present' : 'missing');
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
const decisionsPath = path.join(cwd, DECISIONS_FILE);
|
|
604
|
+
const decisionsExists = fs.existsSync(decisionsPath);
|
|
605
|
+
checks.push(decisionsExists);
|
|
606
|
+
logCheck(log, decisionsExists, DECISIONS_FILE, decisionsExists ? 'present' : 'missing');
|
|
607
|
+
|
|
608
|
+
for (const skillRoot of [CLAUDE_SKILLS_DIR, CODEX_SKILLS_DIR]) {
|
|
609
|
+
for (const skill of Object.values(SKILLS)) {
|
|
610
|
+
const skillPath = skillFilePath(path, cwd, skillRoot, skill.name);
|
|
611
|
+
if (!fs.existsSync(skillPath)) {
|
|
612
|
+
checks.push(false);
|
|
613
|
+
logCheck(log, false, `${skillRoot}/${skill.name}`, 'missing');
|
|
614
|
+
continue;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
const content = fs.readFileSync(skillPath, 'utf-8');
|
|
618
|
+
const matches = content === skill.content;
|
|
619
|
+
checks.push(matches);
|
|
620
|
+
logCheck(
|
|
621
|
+
log,
|
|
622
|
+
matches,
|
|
623
|
+
`${skillRoot}/${skill.name}`,
|
|
624
|
+
matches ? 'current' : 'outdated, run `task refresh`'
|
|
625
|
+
);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
const gitignorePath = path.join(cwd, '.gitignore');
|
|
630
|
+
const gitignore = fs.existsSync(gitignorePath)
|
|
631
|
+
? fs.readFileSync(gitignorePath, 'utf-8')
|
|
632
|
+
: '';
|
|
633
|
+
const hasGitignoreBlock = gitignore.includes(GITIGNORE_BLOCK);
|
|
634
|
+
checks.push(hasGitignoreBlock);
|
|
635
|
+
logCheck(
|
|
636
|
+
log,
|
|
637
|
+
hasGitignoreBlock,
|
|
638
|
+
'.gitignore',
|
|
639
|
+
hasGitignoreBlock ? 'task workflow rules present' : 'missing task workflow rules'
|
|
640
|
+
);
|
|
641
|
+
|
|
642
|
+
const okCount = checks.filter(Boolean).length;
|
|
643
|
+
const totalCount = checks.length;
|
|
644
|
+
const allGood = okCount === totalCount;
|
|
645
|
+
|
|
646
|
+
log.info(`\nSummary: ${okCount}/${totalCount} checks passed.`);
|
|
647
|
+
if (allGood) {
|
|
648
|
+
log.chalk.green('Task workflow is healthy.');
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
console.log(chalk.yellow('Recommended next step: run `task refresh` to reinstall managed skills and repair setup.'));
|
|
653
|
+
}
|