@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.
Files changed (4) hide show
  1. package/README.md +53 -16
  2. package/package.json +1 -1
  3. package/src/cli.js +16 -3
  4. 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
- Grill Me
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
- └── skills/
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. Reviewing work against acceptance criteria
158
- 4. Keeping a lightweight decision history
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@winton979/task-cli",
3
- "version": "1.0.3",
3
+ "version": "1.1.1",
4
4
  "description": "Lightweight task workflow CLI for AI-assisted development",
5
5
  "type": "module",
6
6
  "bin": {
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 --help Show this help`);
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
- import { readFileSync } from 'node:fs';
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. Invoke Grill Me, generate task brief, save to .ai/tasks/active/.',
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. Invoke Grill Me, generate task brief, save to .ai/tasks/active/.
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
- Fast path for small requirements and optimizations.
26
+ Handle a small requirement in one continuous workflow with minimal ceremony.
16
27
 
17
28
  Workflow
18
29
 
19
- 1. Invoke the installed Grill Me skill.
20
- 2. Continue requirement clarification until Grill Me completes.
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
- 5. Present the brief.
27
- 6. Wait for approval before implementation.
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
- Output
40
+ .ai/tasks/archive/YYYY-MM-DD-task-name.md
30
41
 
31
- TASK_BRIEF_READY
32
- `,
33
- },
42
+ 7. Summarize the outcome and any follow-up risks.
34
43
 
35
- 'task-explore': {
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
- Purpose
46
+ # Goal
45
47
 
46
- Clarify requirements before implementation.
48
+ What should be achieved.
47
49
 
48
- Workflow
50
+ # Context
49
51
 
50
- 1. Invoke the installed Grill Me skill.
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
- When complete output:
54
+ # Constraints
57
55
 
58
- TASK_READY
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-brief': {
63
- name: 'task-brief',
64
- description: 'Convert a completed Grill Me discussion into a concise task brief saved to .ai/tasks/active/.',
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-brief
67
- description: Convert a completed Grill Me discussion into a concise task brief saved to .ai/tasks/active/.
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
- Convert the completed Grill Me discussion into a concise execution brief.
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
- Naming Convention
92
+ Workflow
80
93
 
81
- YYYY-MM-DD-short-task-name.md
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
- Example
100
+ .ai/tasks/active/YYYY-MM-DD-task-name.md
84
101
 
85
- 2026-06-10-resize-handle.md
102
+ 6. Show the saved brief and stop.
86
103
 
87
- Output Format
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
- After generation:
133
+ When complete output:
118
134
 
119
- 1. Save the file.
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 using task.md. Follow acceptance criteria strictly.',
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 using task.md. Follow acceptance criteria strictly.
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 the task using task.md.
150
+ Implement a task from the latest file in .ai/tasks/active/.
137
151
 
138
152
  Rules
139
153
 
140
- 1. Follow task.md strictly.
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. Keep implementation focused.
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 implementation against task.md. Evaluate completion, edge cases, maintainability, and risks.',
182
+ description: 'Review the latest task implementation against the corresponding task brief.',
167
183
  content: `---
168
184
  name: task-review
169
- description: Review implementation against task.md. Evaluate completion, edge cases, maintainability, and risks.
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.md.
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 before fixing it. Identify root cause candidates, separate observed vs expected behavior.',
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 before fixing it. Identify root cause candidates, separate observed vs expected behavior.
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 before fixing it.
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 immediately.
244
+ 2. Do not suggest fixes before enough evidence exists.
224
245
  3. Identify root cause candidates.
225
- 4. Ask one question at a time.
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
- When sufficient evidence exists output:
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
- Naming Convention
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
- Output Format
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
- After generation
286
+ When sufficient evidence exists output:
291
287
 
292
- 1. Save the file.
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 bug using bug.md. Minimize changes, fix root cause not symptoms.',
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 bug using bug.md. Minimize changes, fix root cause not symptoms.
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 the bug using bug.md.
303
+ Fix a bug from the latest file in .ai/bugs/active/.
310
304
 
311
305
  Rules
312
306
 
313
- 1. Minimize changes.
314
- 2. Avoid unrelated refactoring.
315
- 3. Fix root cause, not symptoms.
316
- 4. Preserve existing behavior.
317
- 5. Explain reasoning.
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 bug.md. Check root cause, regression risks, side effects.',
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 bug.md. Check root cause, regression risks, side effects.
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.md.
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
- export function init(cwd, { fs, path, log }) {
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
- log.info('Creating directory structure...');
431
- for (const dir of dirs) {
432
- const full = path.join(cwd, dir);
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
- // Create decisions.md placeholder
442
- const decisionsPath = path.join(cwd, '.ai/decisions/decisions.md');
443
- if (!fs.existsSync(decisionsPath)) {
444
- fs.writeFileSync(decisionsPath, '# Decisions Log\n\n');
445
- log.chalk.green(' ✓ .ai/decisions/decisions.md');
446
- } else {
447
- log.chalk.dim(' - .ai/decisions/decisions.md (exists)');
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
- // Create skill files
451
- log.info('\nInstalling workflow skills...');
452
- for (const [key, skill] of Object.entries(SKILLS)) {
453
- const skillDir = path.join(cwd, '.claude/skills', skill.name);
454
- const skillFile = path.join(skillDir, 'SKILL.md');
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
- // Create or append .gitignore
492
+ function updateGitignore(fs, path, cwd, log) {
469
493
  const gitignorePath = path.join(cwd, '.gitignore');
470
- const gitignoreEntry = '\n# task workflow\n.ai/tasks/active/*.md\n.ai/bugs/active/*.md\n';
471
- let existing = '';
472
- if (fs.existsSync(gitignorePath)) {
473
- existing = fs.readFileSync(gitignorePath, 'utf-8');
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
- if (!existing.includes('# task workflow')) {
476
- fs.writeFileSync(gitignorePath, existing.trimEnd() + gitignoreEntry);
477
- log.chalk.green(' .gitignore updated');
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
- log.info(`\nTask workflow initialized. Skills available:
481
- task: fast, explore, brief, implement, review
482
- bug: explore, brief, fix, review
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
+ }