@winton979/task-cli 1.0.2 → 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.
Files changed (4) hide show
  1. package/README.md +177 -12
  2. package/package.json +1 -1
  3. package/src/cli.js +16 -3
  4. package/src/init.js +325 -156
package/README.md CHANGED
@@ -1,38 +1,203 @@
1
1
  # task-cli
2
2
 
3
- Lightweight task workflow CLI for AI-assisted development.
3
+ Lightweight task workflow for AI-assisted development.
4
4
 
5
- ## Install
5
+ Designed for:
6
+
7
+ * Claude Code
8
+ * Codex CLI
9
+ * Mature projects with frequent bug fixes and small feature iterations
10
+
11
+ Task CLI provides a lightweight alternative to heavyweight spec-driven workflows by combining:
12
+
13
+ * Requirement clarification (via Grill Me)
14
+ * Brief generation
15
+ * Implementation
16
+ * Review
17
+ * Decision logging
18
+
19
+ ---
20
+
21
+ ## Installation
6
22
 
7
23
  ```bash
8
24
  npm install -g @winton979/task-cli
9
25
  ```
10
26
 
27
+ Initialize the workflow in your project:
28
+
29
+ ```bash
30
+ task init
31
+ ```
32
+
33
+ ---
34
+
35
+ ## Prerequisites
36
+
37
+ Task CLI relies on a Grill Me compatible skill for requirement exploration.
38
+
39
+ Recommended:
40
+
41
+ ```bash
42
+ npx add-skill PJ-SBN-593844/skill-grill-me
43
+ ```
44
+
45
+ Compatible Grill Me implementations may also work.
46
+
47
+ ---
48
+
11
49
  ## Usage
12
50
 
13
51
  ```bash
14
- task init # Initialize .ai/ workflow structure in current directory
15
- task --help # Show help
52
+ task init
53
+ task refresh
54
+ task doctor
55
+ task --help
16
56
  ```
17
57
 
18
- After `task init`, the following skills become available in Claude Code:
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.
19
63
 
20
- **Task workflow:** `task-fast` → `task-explore` → `task-brief` → `task-implement` → `task-review`
64
+ ---
21
65
 
22
- **Bug workflow:** `bug-explore` → `bug-brief` → `bug-fix` → `bug-review`
66
+ ## Recommended Workflow
23
67
 
24
- **Other:** `decision-log`
68
+ ### Small Feature / Enhancement
25
69
 
26
- ## Directory structure
70
+ ```text
71
+ /task-fast
72
+
73
+ clarify + brief + implement + validate + archive
74
+
75
+ /task-review
76
+ ```
77
+
78
+ ### Larger Requirement
27
79
 
80
+ ```text
81
+ /task-explore
82
+
83
+ TASK_READY
84
+
85
+ /task-implement
86
+
87
+ /task archived
88
+
89
+ /task-review
28
90
  ```
91
+
92
+ ### Bug Fix
93
+
94
+ ```text
95
+ /bug-explore
96
+
97
+ BUG_READY
98
+
99
+ /bug-fix
100
+
101
+ /bug archived
102
+
103
+ /bug-review
104
+ ```
105
+
106
+ ---
107
+
108
+ ## Available Skills
109
+
110
+ ### Task Workflow
111
+
112
+ * task-fast
113
+ * task-explore
114
+ * task-implement
115
+ * task-review
116
+
117
+ ### Bug Workflow
118
+
119
+ * bug-explore
120
+ * bug-fix
121
+ * bug-review
122
+
123
+ ### Other
124
+
125
+ * decision-log
126
+
127
+ ---
128
+
129
+ ## Directory Structure
130
+
131
+ ```text
29
132
  .ai/
30
133
  ├── tasks/
31
134
  │ ├── active/
32
135
  │ └── archive/
136
+
33
137
  ├── bugs/
34
138
  │ ├── active/
35
139
  │ └── archive/
36
- └── decisions/
37
- └── decisions.md
38
- ```
140
+
141
+ ├── decisions/
142
+ │ └── decisions.md
143
+
144
+ ├── .claude/skills/
145
+ └── .codex/skills/
146
+ ```
147
+
148
+ ---
149
+
150
+ ## Philosophy
151
+
152
+ Task CLI is intentionally lightweight.
153
+
154
+ Instead of maintaining large specifications, it focuses on:
155
+
156
+ 1. Clarifying requirements before coding
157
+ 2. Capturing execution context in concise briefs
158
+ 3. Executing with validation and lightweight archiving
159
+ 4. Reviewing work against acceptance criteria
160
+ 5. Keeping a lightweight decision history
161
+
162
+ The goal is to improve quality without slowing down iteration speed.
163
+
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
+
202
+ > Task CLI does not install Grill Me automatically.
203
+ > 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.2",
3
+ "version": "1.1.0",
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, verify, and archive.',
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, verify, and archive.
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. Move the brief 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.
90
+ Clarify requirements and leave behind a ready-to-execute brief.
74
91
 
75
- Save Location
76
-
77
- .ai/tasks/active/YYYY-MM-DD-task-name.md
78
-
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, validate it, and archive it 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, validate it, and archive it 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, 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 implementation against task.md. Evaluate completion, edge cases, maintainability, and risks.',
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 implementation against task.md. Evaluate completion, edge cases, maintainability, and risks.
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.md.
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 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.',
210
234
  content: `---
211
235
  name: bug-explore
212
- description: Investigate a bug before fixing it. Identify root cause candidates, separate observed vs expected behavior.
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 before fixing it.
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 immediately.
247
+ 2. Do not suggest fixes before enough evidence exists.
224
248
  3. Identify root cause candidates.
225
- 4. Ask one question at a time.
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
- 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
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
- Naming Convention
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
- Output Format
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
- After generation
289
+ When sufficient evidence exists output:
291
290
 
292
- 1. Save the file.
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 bug using bug.md. Minimize changes, fix root cause not symptoms.',
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 bug using bug.md. Minimize changes, fix root cause not symptoms.
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 the bug using bug.md.
306
+ Fix a bug from the latest file in .ai/bugs/active/.
310
307
 
311
308
  Rules
312
309
 
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.
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 bug.md. Check root cause, regression risks, side effects.',
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 bug.md. Check root cause, regression risks, side effects.
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.md.
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
- 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
- ];
427
+ const MANAGED_SKILL_NAMES = Object.values(SKILLS).map((skill) => skill.name);
429
428
 
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
- }
429
+ function skillFilePath(path, cwd, skillRoot, skillName) {
430
+ return path.join(cwd, skillRoot, skillName, 'SKILL.md');
431
+ }
440
432
 
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)');
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
- // 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');
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
- // Create or append .gitignore
498
+ function updateGitignore(fs, path, cwd, log) {
469
499
  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');
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
- if (!existing.includes('# task workflow')) {
476
- fs.writeFileSync(gitignorePath, existing.trimEnd() + gitignoreEntry);
477
- log.chalk.green(' .gitignore updated');
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
- log.info(`\nTask workflow initialized. Skills available:
481
- task: fast, explore, brief, implement, review
482
- bug: explore, brief, fix, review
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
+ }