rbin-task-flow 1.19.1 → 1.19.3
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/.cursor/rules/task_estimate.mdc +36 -36
- package/.cursor/rules/task_execution.mdc +1 -1
- package/.cursor/rules/task_generate_flow.mdc +67 -49
- package/.task-flow/README.md +3 -3
- package/CLAUDE.md +2 -2
- package/lib/estimate.js +104 -7
- package/lib/install.js +24 -7
- package/package.json +1 -1
|
@@ -10,52 +10,52 @@ alwaysApply: true
|
|
|
10
10
|
- **ALL TASKS**: `task-flow: estimate all` → Estimate all tasks
|
|
11
11
|
- When user says "task-flow: estimate X", "estimate X", "estimate X,Y", "estimate all", or "how long will task X take":
|
|
12
12
|
- **READ**: `.task-flow/.internal/tasks.json` to get task details
|
|
13
|
-
- **CALCULATE**: Time estimate based on
|
|
13
|
+
- **CALCULATE**: Time estimate based on task level, complexity signals, and developer experience level
|
|
14
14
|
- **DISPLAY**: Show time estimate with intervals for 3 experience levels (junior, mid, senior)
|
|
15
|
-
- **FORMAT**: Show estimates in hours with ranges (e.g., "10-14 hours" for
|
|
15
|
+
- **FORMAT**: Show estimates in hours with ranges (e.g., "10-14 hours" for intermediate)
|
|
16
16
|
|
|
17
17
|
- **Estimation Rules:**
|
|
18
|
-
1. **Base
|
|
19
|
-
2. **Experience Levels
|
|
18
|
+
1. **Base Analysis**: Evaluate the task title and subtasks to infer real task level
|
|
19
|
+
2. **Experience Levels** (use internally; display "Intermediate" not "Mid-level" to user):
|
|
20
20
|
- **Junior** (0-2 years): Base time × 1.5 multiplier
|
|
21
|
-
- **
|
|
21
|
+
- **Intermediate** (3-5 years): Base time × 1.0 multiplier (baseline)
|
|
22
22
|
- **Senior** (6+ years): Base time × 0.7 multiplier
|
|
23
|
-
3. **
|
|
24
|
-
-
|
|
25
|
-
- Medium
|
|
26
|
-
-
|
|
27
|
-
4. **
|
|
28
|
-
5. **
|
|
29
|
-
-
|
|
30
|
-
-
|
|
23
|
+
3. **Task Level Heuristics**:
|
|
24
|
+
- **Low**: localized change, clear requirement, low ambiguity, low regression risk
|
|
25
|
+
- **Medium**: multi-file implementation, moderate ambiguity, moderate validation needs
|
|
26
|
+
- **High**: architectural, cross-cutting, migration, security, performance, or high-risk change
|
|
27
|
+
4. **Subtask count is only one signal**: use it to inform scope, but never as the sole estimate driver
|
|
28
|
+
5. **Intermediate baseline per subtask**:
|
|
29
|
+
- Low-level task: 1.5-2.5 hours
|
|
30
|
+
- Medium-level task: 2.5-4 hours
|
|
31
|
+
- High-level task: 4-6.5 hours
|
|
32
|
+
6. **Risk and scope adjustment**:
|
|
33
|
+
- Increase estimate when the task mentions integrations, migrations, refactors, security, performance, billing, or architecture work
|
|
34
|
+
7. **Range Calculation**:
|
|
35
|
+
- Lower bound: (subtasks × baseline lower) × risk factor × experience multiplier
|
|
36
|
+
- Upper bound: (subtasks × baseline upper) × risk factor × experience multiplier
|
|
31
37
|
- Round to nearest hour
|
|
32
38
|
|
|
33
39
|
- **Estimation Formula:**
|
|
34
40
|
```
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
Lower: subtasks × 2
|
|
43
|
-
Upper: subtasks × 3
|
|
44
|
-
|
|
45
|
-
Senior (6+ years):
|
|
46
|
-
Lower: (subtasks × 2) × 0.7
|
|
47
|
-
Upper: (subtasks × 3) × 0.7
|
|
41
|
+
1. Infer task level: low, medium, or high
|
|
42
|
+
2. Choose intermediate baseline per subtask from task level
|
|
43
|
+
3. Apply risk/scope multiplier when complexity indicators exist
|
|
44
|
+
4. Apply experience multiplier:
|
|
45
|
+
Junior: × 1.5
|
|
46
|
+
Intermediate: × 1.0
|
|
47
|
+
Senior: × 0.7
|
|
48
48
|
```
|
|
49
49
|
|
|
50
50
|
- **Display Format:**
|
|
51
51
|
```
|
|
52
52
|
📊 Time Estimation for Task X: [Task Title]
|
|
53
53
|
|
|
54
|
-
Based on [N] subtasks:
|
|
54
|
+
Based on [N] subtasks and task-level analysis:
|
|
55
55
|
|
|
56
|
-
👶 Junior (0-2 years):
|
|
57
|
-
👨💼
|
|
58
|
-
👴 Senior (6+ years):
|
|
56
|
+
👶 Junior (0-2 years): [X-Y] hours
|
|
57
|
+
👨💼 Intermediate (3-5 years): [X-Y] hours
|
|
58
|
+
👴 Senior (6+ years): [X-Y] hours
|
|
59
59
|
```
|
|
60
60
|
|
|
61
61
|
- **Natural Language Phrases:**
|
|
@@ -72,15 +72,15 @@ alwaysApply: true
|
|
|
72
72
|
```
|
|
73
73
|
User: "task-flow: estimate 1"
|
|
74
74
|
|
|
75
|
-
Task 1 has 5 subtasks:
|
|
75
|
+
Task 1 has 5 subtasks and medium complexity signals:
|
|
76
76
|
|
|
77
77
|
📊 Time Estimation for Task 1: Create authentication system
|
|
78
78
|
|
|
79
|
-
Based on 5 subtasks:
|
|
79
|
+
Based on 5 subtasks and task-level analysis:
|
|
80
80
|
|
|
81
|
-
👶 Junior (0-2 years):
|
|
82
|
-
👨💼
|
|
83
|
-
👴 Senior (6+ years):
|
|
81
|
+
👶 Junior (0-2 years): 15-23 hours
|
|
82
|
+
👨💼 Intermediate (3-5 years): 10-15 hours
|
|
83
|
+
👴 Senior (6+ years): 7-11 hours
|
|
84
84
|
```
|
|
85
85
|
|
|
86
86
|
- **Integration:**
|
|
@@ -89,4 +89,4 @@ alwaysApply: true
|
|
|
89
89
|
- Estimates are informational only (not stored)
|
|
90
90
|
|
|
91
91
|
- **Principle:**
|
|
92
|
-
> **Provide realistic time estimates based on task
|
|
92
|
+
> **Provide realistic time estimates based on real task level, scope, ambiguity, and risk. Subtask count informs scope, but must not be the sole estimation criterion.**
|
|
@@ -37,7 +37,7 @@ alwaysApply: true
|
|
|
37
37
|
- `task-flow: report X` - Generate implementation report for task X (simplified - no "task" needed)
|
|
38
38
|
- `task-flow: report X,Y` - Generate reports for multiple tasks (comma-separated)
|
|
39
39
|
- `task-flow: report all` - Generate reports for all tasks
|
|
40
|
-
- `task-flow: generate flow` - Populate tasks.flow.md with dependencies, estimated hours
|
|
40
|
+
- `task-flow: generate flow` - Populate tasks.flow.md with dependencies, estimated hours, and AI model recommendations
|
|
41
41
|
|
|
42
42
|
- **Workflow Example:**
|
|
43
43
|
```markdown
|
|
@@ -1,73 +1,91 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Generate task flow with dependencies, estimated hours
|
|
2
|
+
description: Generate task flow with dependencies, estimated hours, and AI model recommendations
|
|
3
3
|
globs: **/*
|
|
4
4
|
alwaysApply: true
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
- **Task Flow Generation - Dependencies, Hours & Model Recommendations:**
|
|
8
|
-
- **FAST FORMAT**: `task-flow: generate flow` → Populate tasks.flow.md
|
|
8
|
+
- **FAST FORMAT**: `task-flow: generate flow` → Populate tasks.flow.md
|
|
9
9
|
- When user says "task-flow: generate flow", "generate flow", "gerar flow", or "flow":
|
|
10
|
-
- **READ**: `.task-flow/.internal/tasks.json`
|
|
11
|
-
- **
|
|
12
|
-
- **ANALYZE**: For each task, determine dependencies, estimated hours, and model recommendations
|
|
10
|
+
- **READ**: `.task-flow/.internal/tasks.json` and optionally `status.json`
|
|
11
|
+
- **SEARCH**: Current versions of GPT-5.x, Composer, Claude (Haiku, Sonnet) via web search
|
|
13
12
|
- **WRITE**: Populate `.task-flow/tasks.flow.md` with the generated flow
|
|
14
|
-
- **DO NOT**: Populate tasks.flow.md when running `task-flow: sync`
|
|
13
|
+
- **DO NOT**: Populate tasks.flow.md when running `task-flow: sync`
|
|
15
14
|
|
|
16
15
|
- **Generation Process:**
|
|
17
|
-
1. **Search for current model versions** (
|
|
18
|
-
2. Read
|
|
16
|
+
1. **Search for current model versions** (web search): one GPT-5.x coding-capable model, Composer (Cursor), Claude Haiku, Claude Sonnet. Include version next to each model (e.g. "Claude Sonnet 4.6").
|
|
17
|
+
2. Read tasks from tasks.json
|
|
19
18
|
3. For each task, determine:
|
|
20
|
-
- **
|
|
21
|
-
- **
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
-
|
|
33
|
-
-
|
|
19
|
+
- **Dependencies**: Which tasks must be completed first (or "—" if none)
|
|
20
|
+
- **Estimated hours** (for billing): infer from real task scope, ambiguity, dependencies, and validation needs. Subtask count informs scope, but do not use a fixed subtasks × hours formula. Do NOT show "dev mediano" or "mid-level".
|
|
21
|
+
- **3 model options** (in order of priority): one GPT-5.x model, one Composer, one Claude — always these 3. Include version and effort for each.
|
|
22
|
+
- **Model priority must be defined by AI judgment**, based on task nature, implementation risk, ambiguity, architecture impact, validation needs, and expected autonomy. Do not use a fixed ranking.
|
|
23
|
+
- **Effort** per model: low, medium, or high, defined by task level and difficulty. Do not infer effort from subtask count alone.
|
|
24
|
+
4. **Claude**: Use only Haiku or Sonnet — **never Opus**
|
|
25
|
+
5. **Effort heuristics**:
|
|
26
|
+
- **low**: localized change, clear requirement, low ambiguity, low regression risk, limited validation
|
|
27
|
+
- **medium**: multi-file implementation, moderate ambiguity, moderate regression risk, requires broader validation
|
|
28
|
+
- **high**: architecture or cross-cutting change, high ambiguity, high regression risk, sequencing concerns, or deep investigation/refactor
|
|
29
|
+
6. **Model heuristics**:
|
|
30
|
+
- **GPT-5.x**: prioritize when the task needs strong implementation, refactoring, debugging, or broader code reasoning
|
|
31
|
+
- **Composer**: prioritize when the task benefits from editor-native iteration, repo-wide navigation, and fast execution in the coding flow
|
|
32
|
+
- **Claude Haiku**: prioritize for simpler, well-bounded tasks with low effort
|
|
33
|
+
- **Claude Sonnet**: prioritize for tasks that need more synthesis, reasoning depth, or architectural analysis
|
|
34
|
+
- The first recommendation must reflect the best fit for that specific task, not a template order reused across all tasks
|
|
34
35
|
|
|
35
|
-
- **Output Format
|
|
36
|
+
- **Output Format (direct blocks per task):**
|
|
36
37
|
```markdown
|
|
37
38
|
# Task Flow — Dependencies, Hours & Model Recommendations
|
|
38
39
|
|
|
39
40
|
<!-- Populated by task-flow: generate flow. Do not edit manually. -->
|
|
40
|
-
<!-- Horas:
|
|
41
|
+
<!-- Horas: uso para cobrança ao cliente -->
|
|
41
42
|
|
|
42
|
-
##
|
|
43
|
+
## Legenda
|
|
43
44
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
| Símbolo | Significado |
|
|
46
|
+
|---------|-------------|
|
|
47
|
+
| ✅ | Task concluída |
|
|
48
|
+
| ⚡ | Pode iniciar agora (deps satisfeitas) |
|
|
49
|
+
| 🔒 | Bloqueada por dependências |
|
|
50
|
+
|
|
51
|
+
**Esforço (Effort):** definido pelo nível e dificuldade real da task, não pela quantidade de subtasks
|
|
52
|
+
**Claude:** Haiku ou Sonnet — **não use Opus**
|
|
53
|
+
|
|
54
|
+
## Tasks (blocos diretos por task)
|
|
48
55
|
|
|
49
|
-
|
|
50
|
-
- **
|
|
51
|
-
- **
|
|
52
|
-
- **
|
|
53
|
-
|
|
54
|
-
|
|
56
|
+
### ✅ Task 1 — [Título]
|
|
57
|
+
- **Depende de:** —
|
|
58
|
+
- **Horas cobrança:** 10-15h
|
|
59
|
+
- **Modelos sugeridos** (ordem de prioridade):
|
|
60
|
+
1. GPT-5.x [versão] — effort medium
|
|
61
|
+
2. Composer [versão] — effort medium
|
|
62
|
+
3. Claude Sonnet [versão] — effort medium
|
|
55
63
|
|
|
56
|
-
|
|
57
|
-
-
|
|
58
|
-
-
|
|
59
|
-
-
|
|
64
|
+
### ⚡ Task 2 — [Título]
|
|
65
|
+
- **Depende de:** Task 1 ✅
|
|
66
|
+
- **Horas cobrança:** 10-15h
|
|
67
|
+
- **Modelos sugeridos** (ordem de prioridade):
|
|
68
|
+
1. Composer [versão] — effort medium
|
|
69
|
+
2. GPT-5.x [versão] — effort medium
|
|
70
|
+
3. Claude Sonnet [versão] — effort medium
|
|
71
|
+
|
|
72
|
+
### 🔒 Task 3 — [Título]
|
|
73
|
+
- **Depende de:** Task 2
|
|
74
|
+
- **Horas cobrança:** 8-12h
|
|
75
|
+
- **Modelos sugeridos** (ordem de prioridade):
|
|
76
|
+
1. Claude Haiku [versão] — effort low
|
|
77
|
+
2. GPT-5.x [versão] — effort low
|
|
78
|
+
3. Composer [versão] — effort low
|
|
79
|
+
```
|
|
60
80
|
|
|
61
|
-
- **
|
|
62
|
-
- **
|
|
63
|
-
-
|
|
64
|
-
-
|
|
81
|
+
- **Model Rules:**
|
|
82
|
+
- **3 models per task**: One GPT-5.x model, one Composer, one Claude — always in order of priority
|
|
83
|
+
- **Version required**: Include current version next to each model name (from web search)
|
|
84
|
+
- **Effort required**: low, medium, or high for each model
|
|
85
|
+
- **AI-defined ranking**: order models by best fit for the task, not by a fixed global priority
|
|
86
|
+
- **No Opus**: Claude = Haiku or Sonnet only
|
|
65
87
|
|
|
66
|
-
- **
|
|
67
|
-
- Uses data from `.task-flow/.internal/tasks.json`
|
|
68
|
-
- Writes to `.task-flow/tasks.flow.md` (created by `rbin-task-flow init`)
|
|
69
|
-
- Does NOT run on `task-flow: sync` — only when user explicitly requests `task-flow: generate flow`
|
|
70
|
-
- Works with [task_execution.mdc](mdc:.cursor/rules/task_execution.mdc) workflow
|
|
88
|
+
- **Natural Language:** "generate flow", "gerar flow", "flow"
|
|
71
89
|
|
|
72
90
|
- **Principle:**
|
|
73
|
-
> **Populate tasks.flow.md with
|
|
91
|
+
> **Populate tasks.flow.md with direct blocks per task. Dependencies, hours, and 3 model options (GPT-5.x, Composer, Claude) with version and effort. Let AI define ranking and effort from task context. Never use Opus.**
|
package/.task-flow/README.md
CHANGED
|
@@ -95,10 +95,10 @@ Refactors code from specific task(s). Removes explanatory comments, improves cod
|
|
|
95
95
|
- `task-flow: refactor all` → Refactors all tasks
|
|
96
96
|
|
|
97
97
|
### `task-flow: estimate X` (simplified syntax)
|
|
98
|
-
Estimates time required to complete task(s) based on
|
|
98
|
+
Estimates time required to complete task(s) based on task level, scope, risk, and developer experience level. Subtask count informs scope, but is not the sole criterion.
|
|
99
99
|
|
|
100
100
|
**Output includes:**
|
|
101
|
-
- Time estimates for Junior (0-2 years),
|
|
101
|
+
- Time estimates for Junior (0-2 years), Intermediate (3-5 years), and Senior (6+ years) developers
|
|
102
102
|
- Estimates in hours and business days
|
|
103
103
|
- Recommendation for management with buffer
|
|
104
104
|
|
|
@@ -108,7 +108,7 @@ Estimates time required to complete task(s) based on the number of subtasks and
|
|
|
108
108
|
- `task-flow: estimate all` → Shows time estimates for all tasks
|
|
109
109
|
|
|
110
110
|
### `task-flow: generate flow`
|
|
111
|
-
Populates `tasks.flow.md` with: (1) task dependencies (for parallelization), (2) estimated hours, and (3) AI model recommendations (
|
|
111
|
+
Populates `tasks.flow.md` with: (1) task dependencies (for parallelization), (2) estimated hours, and (3) AI model recommendations (GPT-5.x, Composer, Claude) with effort levels. Model ranking and effort must be defined by the AI from task context, not from a fixed order or only from subtask count. Run after `task-flow: sync` when you want to know which tasks can run in parallel and which model/effort to use.
|
|
112
112
|
|
|
113
113
|
### `task-flow: report X` (simplified syntax)
|
|
114
114
|
Generates a detailed implementation report for completed task(s) in Markdown format.
|
package/CLAUDE.md
CHANGED
|
@@ -37,11 +37,11 @@ This project uses RBIN Task Flow for task management:
|
|
|
37
37
|
- `task-flow: report X` - Generate implementation report for task X (simplified - no "task" needed)
|
|
38
38
|
- `task-flow: report X,Y` - Generate reports for multiple tasks (comma-separated)
|
|
39
39
|
- `task-flow: report all` - Generate reports for all tasks
|
|
40
|
-
- `task-flow: generate flow` - Populate tasks.flow.md with dependencies, estimated hours
|
|
40
|
+
- `task-flow: generate flow` - Populate tasks.flow.md with dependencies, estimated hours, and AI model recommendations
|
|
41
41
|
- **Files**:
|
|
42
42
|
- `.task-flow/tasks.input.txt` - Define your tasks here
|
|
43
43
|
- `.task-flow/tasks.status.md` - Auto-generated status (DO NOT EDIT manually)
|
|
44
|
-
- `.task-flow/tasks.flow.md` - Dependencies, estimated hours
|
|
44
|
+
- `.task-flow/tasks.flow.md` - Dependencies, estimated hours, and model recommendations (populated by `task-flow: generate flow`)
|
|
45
45
|
- `.task-flow/.internal/` - Internal system files (ignore)
|
|
46
46
|
|
|
47
47
|
Follow all rules defined in `.cursor/rules/` for consistent development practices.
|
package/lib/estimate.js
CHANGED
|
@@ -40,7 +40,8 @@ async function estimateTask(taskIdsInput, targetPath = process.cwd()) {
|
|
|
40
40
|
continue;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
const
|
|
43
|
+
const analysis = analyzeTaskComplexity(task);
|
|
44
|
+
const estimates = calculateEstimates(analysis);
|
|
44
45
|
|
|
45
46
|
if (taskIds.length > 1) {
|
|
46
47
|
console.log('\n' + chalk.cyan('═'.repeat(70)));
|
|
@@ -51,11 +52,13 @@ async function estimateTask(taskIdsInput, targetPath = process.cwd()) {
|
|
|
51
52
|
console.log(chalk.cyan('═'.repeat(70)) + '\n');
|
|
52
53
|
|
|
53
54
|
console.log(chalk.blue.bold('Task:'), chalk.yellow(`#${taskId} - ${task.title}\n`));
|
|
54
|
-
console.log(chalk.blue(`Complexity: ${chalk.yellow(
|
|
55
|
+
console.log(chalk.blue(`Complexity: ${chalk.yellow(analysis.level)} (${subtaskCount} subtasks)\n`));
|
|
55
56
|
|
|
56
57
|
console.log(chalk.cyan('─'.repeat(70)));
|
|
57
58
|
console.log(chalk.magenta.bold('Time Estimates by Experience Level:\n'));
|
|
58
59
|
|
|
60
|
+
console.log(chalk.gray(`Signals: ${analysis.signals.join(', ')}\n`));
|
|
61
|
+
|
|
59
62
|
const juniorDays = Math.ceil(estimates.junior.upper / 8);
|
|
60
63
|
const midDays = Math.ceil(estimates.mid.upper / 8);
|
|
61
64
|
const seniorDays = Math.ceil(estimates.senior.upper / 8);
|
|
@@ -65,7 +68,7 @@ async function estimateTask(taskIdsInput, targetPath = process.cwd()) {
|
|
|
65
68
|
console.log(chalk.white(' Days: '), chalk.yellow(`~${juniorDays} business day(s)`));
|
|
66
69
|
console.log('');
|
|
67
70
|
|
|
68
|
-
console.log(chalk.gray('👨💼
|
|
71
|
+
console.log(chalk.gray('👨💼 Intermediate (3-5 years):'));
|
|
69
72
|
console.log(chalk.white(' Hours:'), chalk.yellow(`${estimates.mid.lower}-${estimates.mid.upper} hours`));
|
|
70
73
|
console.log(chalk.white(' Days: '), chalk.yellow(`~${midDays} business day(s)`));
|
|
71
74
|
console.log('');
|
|
@@ -77,7 +80,7 @@ async function estimateTask(taskIdsInput, targetPath = process.cwd()) {
|
|
|
77
80
|
|
|
78
81
|
console.log(chalk.cyan('─'.repeat(70)));
|
|
79
82
|
console.log(chalk.magenta.bold('Recommendation for Management:\n'));
|
|
80
|
-
console.log(chalk.white(' Recommended estimate:'), chalk.yellow(`${estimates.mid.lower}-${estimates.mid.upper} hours`), chalk.gray('(
|
|
83
|
+
console.log(chalk.white(' Recommended estimate:'), chalk.yellow(`${estimates.mid.lower}-${estimates.mid.upper} hours`), chalk.gray('(3-5 years baseline)'));
|
|
81
84
|
console.log(chalk.white(' Buffer recommended:'), chalk.yellow(`+20%`), chalk.gray('for unexpected issues'));
|
|
82
85
|
console.log(chalk.white(' Total estimate:'), chalk.yellow(`${Math.round(estimates.mid.upper * 1.2)} hours`), chalk.gray(`(~${Math.ceil(estimates.mid.upper * 1.2 / 8)} business days)`));
|
|
83
86
|
console.log('');
|
|
@@ -94,9 +97,16 @@ async function estimateTask(taskIdsInput, targetPath = process.cwd()) {
|
|
|
94
97
|
}
|
|
95
98
|
}
|
|
96
99
|
|
|
97
|
-
function calculateEstimates(
|
|
98
|
-
const
|
|
99
|
-
const
|
|
100
|
+
function calculateEstimates(analysis) {
|
|
101
|
+
const { subtaskCount, level, riskMultiplier } = analysis;
|
|
102
|
+
const levelRanges = {
|
|
103
|
+
low: { lower: 1.5, upper: 2.5 },
|
|
104
|
+
medium: { lower: 2.5, upper: 4 },
|
|
105
|
+
high: { lower: 4, upper: 6.5 }
|
|
106
|
+
};
|
|
107
|
+
const baseRange = levelRanges[level] || levelRanges.medium;
|
|
108
|
+
const baseLower = subtaskCount * baseRange.lower * riskMultiplier;
|
|
109
|
+
const baseUpper = subtaskCount * baseRange.upper * riskMultiplier;
|
|
100
110
|
|
|
101
111
|
return {
|
|
102
112
|
junior: {
|
|
@@ -114,4 +124,91 @@ function calculateEstimates(subtaskCount) {
|
|
|
114
124
|
};
|
|
115
125
|
}
|
|
116
126
|
|
|
127
|
+
function analyzeTaskComplexity(task) {
|
|
128
|
+
const subtasks = Array.isArray(task.subtasks) ? task.subtasks : [];
|
|
129
|
+
const subtaskCount = subtasks.length;
|
|
130
|
+
const content = buildTaskContent(task, subtasks);
|
|
131
|
+
const keywordScores = scoreKeywords(content);
|
|
132
|
+
|
|
133
|
+
let score = 0;
|
|
134
|
+
|
|
135
|
+
if (subtaskCount <= 2) score += 1;
|
|
136
|
+
else if (subtaskCount <= 5) score += 2;
|
|
137
|
+
else score += 3;
|
|
138
|
+
|
|
139
|
+
score += keywordScores.complexity;
|
|
140
|
+
|
|
141
|
+
let level = 'medium';
|
|
142
|
+
if (score <= 2) level = 'low';
|
|
143
|
+
else if (score >= 6) level = 'high';
|
|
144
|
+
|
|
145
|
+
const riskMultiplier = Math.max(1, 1 + keywordScores.risk * 0.08 + keywordScores.scope * 0.06);
|
|
146
|
+
const signals = [
|
|
147
|
+
`${subtaskCount} subtasks`,
|
|
148
|
+
`${level} task level`
|
|
149
|
+
];
|
|
150
|
+
|
|
151
|
+
if (keywordScores.scope > 0) signals.push('multi-file or integration indicators');
|
|
152
|
+
if (keywordScores.risk > 0) signals.push('risk or validation indicators');
|
|
153
|
+
if (keywordScores.architecture > 0) signals.push('architecture/refactor indicators');
|
|
154
|
+
if (signals.length === 2) signals.push('no elevated complexity indicators');
|
|
155
|
+
|
|
156
|
+
return {
|
|
157
|
+
subtaskCount,
|
|
158
|
+
level,
|
|
159
|
+
riskMultiplier,
|
|
160
|
+
signals
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function buildTaskContent(task, subtasks) {
|
|
165
|
+
const parts = [task.title, task.description];
|
|
166
|
+
|
|
167
|
+
for (const subtask of subtasks) {
|
|
168
|
+
if (typeof subtask === 'string') {
|
|
169
|
+
parts.push(subtask);
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (subtask && typeof subtask === 'object') {
|
|
174
|
+
parts.push(subtask.title, subtask.description);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return parts
|
|
179
|
+
.filter(Boolean)
|
|
180
|
+
.join(' ')
|
|
181
|
+
.toLowerCase();
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function scoreKeywords(content) {
|
|
185
|
+
const architectureKeywords = [
|
|
186
|
+
'architecture', 'arquitetura', 'refactor', 'refator', 'migrate', 'migra',
|
|
187
|
+
'core', 'infra', 'foundation', 'sdk', 'abstraction'
|
|
188
|
+
];
|
|
189
|
+
const scopeKeywords = [
|
|
190
|
+
'integration', 'integracao', 'integrar', 'api', 'database', 'banco',
|
|
191
|
+
'auth', 'oauth', 'deploy', 'pipeline', 'webhook', 'service', 'provider'
|
|
192
|
+
];
|
|
193
|
+
const riskKeywords = [
|
|
194
|
+
'critical', 'critico', 'security', 'seguranca', 'performance', 'perf',
|
|
195
|
+
'bug', 'fix', 'regression', 'migration', 'compliance', 'billing', 'payment'
|
|
196
|
+
];
|
|
197
|
+
|
|
198
|
+
const architecture = countMatches(content, architectureKeywords);
|
|
199
|
+
const scope = countMatches(content, scopeKeywords);
|
|
200
|
+
const risk = countMatches(content, riskKeywords);
|
|
201
|
+
|
|
202
|
+
return {
|
|
203
|
+
architecture,
|
|
204
|
+
scope,
|
|
205
|
+
risk,
|
|
206
|
+
complexity: architecture * 2 + scope + risk
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function countMatches(content, keywords) {
|
|
211
|
+
return keywords.reduce((total, keyword) => total + (content.includes(keyword) ? 1 : 0), 0);
|
|
212
|
+
}
|
|
213
|
+
|
|
117
214
|
module.exports = { estimateTask };
|
package/lib/install.js
CHANGED
|
@@ -120,10 +120,10 @@ async function copyConfigs(targetPath, isUpdate = false) {
|
|
|
120
120
|
showSuccess('Codex instructions (AGENTS.md)');
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
await copyTaskFlow(targetPath);
|
|
123
|
+
await copyTaskFlow(targetPath, isUpdate);
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
-
async function copyTaskFlow(targetPath) {
|
|
126
|
+
async function copyTaskFlow(targetPath, isUpdate = false) {
|
|
127
127
|
const taskFlowSrc = path.join(TEMPLATE_DIR, '.task-flow');
|
|
128
128
|
const taskFlowDest = path.join(targetPath, '.task-flow');
|
|
129
129
|
|
|
@@ -132,11 +132,24 @@ async function copyTaskFlow(targetPath) {
|
|
|
132
132
|
const PROTECTED = [
|
|
133
133
|
path.join(taskFlowDest, '.internal'),
|
|
134
134
|
];
|
|
135
|
+
const PRESERVED_ON_INIT = [
|
|
136
|
+
path.join(taskFlowDest, 'tasks.input.txt'),
|
|
137
|
+
path.join(taskFlowDest, 'tasks.status.md'),
|
|
138
|
+
path.join(taskFlowDest, 'tasks.flow.md'),
|
|
139
|
+
];
|
|
135
140
|
|
|
136
141
|
await fs.copy(taskFlowSrc, taskFlowDest, {
|
|
137
142
|
overwrite: true,
|
|
138
|
-
filter: (src) => {
|
|
139
|
-
|
|
143
|
+
filter: (src, dest) => {
|
|
144
|
+
if (PROTECTED.some((p) => src.startsWith(p) || dest.startsWith(p))) {
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (!isUpdate && PRESERVED_ON_INIT.includes(dest) && fs.existsSync(dest)) {
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return true;
|
|
140
153
|
},
|
|
141
154
|
});
|
|
142
155
|
|
|
@@ -148,14 +161,18 @@ async function copyTaskFlow(targetPath) {
|
|
|
148
161
|
'# Task Flow — Dependencies, Hours & Model Recommendations',
|
|
149
162
|
'',
|
|
150
163
|
'<!-- Populated by task-flow: generate flow. Do not edit manually. -->',
|
|
151
|
-
'<!-- Horas:
|
|
164
|
+
'<!-- Horas: uso para cobrança ao cliente -->',
|
|
152
165
|
'',
|
|
153
166
|
].join('\n');
|
|
154
167
|
await fs.writeFile(flowPath, flowStub);
|
|
155
168
|
}
|
|
156
169
|
|
|
157
|
-
showSuccess('Task Flow directory
|
|
158
|
-
|
|
170
|
+
showSuccess('Task Flow directory');
|
|
171
|
+
if (isUpdate) {
|
|
172
|
+
showInfo('Protected: .internal/ (your task data is safe)');
|
|
173
|
+
} else {
|
|
174
|
+
showInfo('Protected on init: .internal/, tasks.input.txt, tasks.status.md, tasks.flow.md');
|
|
175
|
+
}
|
|
159
176
|
}
|
|
160
177
|
|
|
161
178
|
async function updateGitignore(targetPath) {
|