rbin-task-flow 1.19.3 → 1.19.5
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 +19 -28
- package/.task-flow/README.md +2 -2
- package/README.md +8 -0
- package/bin/cli.js +11 -1
- package/lib/estimate.js +9 -31
- package/lib/install.js +25 -7
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Task time estimation based on task complexity
|
|
2
|
+
description: Task time estimation based on real task complexity for an average developer pace
|
|
3
3
|
globs: **/*
|
|
4
4
|
alwaysApply: true
|
|
5
5
|
---
|
|
@@ -10,41 +10,36 @@ 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 task level, complexity signals, and
|
|
14
|
-
- **DISPLAY**: Show
|
|
15
|
-
- **FORMAT**: Show estimates in hours with ranges (e.g
|
|
13
|
+
- **CALCULATE**: Time estimate based on task level, intrinsic complexity signals, and average development pace
|
|
14
|
+
- **DISPLAY**: Show a single estimate range for the majority of developers
|
|
15
|
+
- **FORMAT**: Show estimates in hours with ranges (e.g. "10-14 hours")
|
|
16
16
|
|
|
17
17
|
- **Estimation Rules:**
|
|
18
|
-
1. **Base Analysis**: Evaluate the task title and subtasks to infer real task
|
|
19
|
-
2. **
|
|
20
|
-
- **Junior** (0-2 years): Base time × 1.5 multiplier
|
|
21
|
-
- **Intermediate** (3-5 years): Base time × 1.0 multiplier (baseline)
|
|
22
|
-
- **Senior** (6+ years): Base time × 0.7 multiplier
|
|
18
|
+
1. **Base Analysis**: Evaluate the task title, description, and subtasks to infer the real complexity of the task itself
|
|
19
|
+
2. **Assumption**: Estimate for an average developer working at an average development pace, without AI acceleration
|
|
23
20
|
3. **Task Level Heuristics**:
|
|
24
21
|
- **Low**: localized change, clear requirement, low ambiguity, low regression risk
|
|
25
22
|
- **Medium**: multi-file implementation, moderate ambiguity, moderate validation needs
|
|
26
23
|
- **High**: architectural, cross-cutting, migration, security, performance, or high-risk change
|
|
27
|
-
4. **
|
|
28
|
-
5. **
|
|
24
|
+
4. **Intrinsic complexity matters first**: scope, ambiguity, dependencies, validation, integration points, and regression risk must outweigh raw subtask count
|
|
25
|
+
5. **Subtask count is only one signal**: use it to inform scope, but never as the sole estimate driver
|
|
26
|
+
6. **Baseline per subtask for average pace**:
|
|
29
27
|
- Low-level task: 1.5-2.5 hours
|
|
30
28
|
- Medium-level task: 2.5-4 hours
|
|
31
29
|
- High-level task: 4-6.5 hours
|
|
32
|
-
|
|
30
|
+
7. **Risk and scope adjustment**:
|
|
33
31
|
- Increase estimate when the task mentions integrations, migrations, refactors, security, performance, billing, or architecture work
|
|
34
|
-
|
|
35
|
-
- Lower bound:
|
|
36
|
-
- Upper bound:
|
|
32
|
+
8. **Range Calculation**:
|
|
33
|
+
- Lower bound: subtasks × baseline lower × risk factor
|
|
34
|
+
- Upper bound: subtasks × baseline upper × risk factor
|
|
37
35
|
- Round to nearest hour
|
|
38
36
|
|
|
39
37
|
- **Estimation Formula:**
|
|
40
38
|
```
|
|
41
39
|
1. Infer task level: low, medium, or high
|
|
42
|
-
2. Choose
|
|
40
|
+
2. Choose average-pace baseline per subtask from task level
|
|
43
41
|
3. Apply risk/scope multiplier when complexity indicators exist
|
|
44
|
-
4.
|
|
45
|
-
Junior: × 1.5
|
|
46
|
-
Intermediate: × 1.0
|
|
47
|
-
Senior: × 0.7
|
|
42
|
+
4. Return one range that represents the majority of developers
|
|
48
43
|
```
|
|
49
44
|
|
|
50
45
|
- **Display Format:**
|
|
@@ -52,10 +47,8 @@ alwaysApply: true
|
|
|
52
47
|
📊 Time Estimation for Task X: [Task Title]
|
|
53
48
|
|
|
54
49
|
Based on [N] subtasks and task-level analysis:
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
👨💼 Intermediate (3-5 years): [X-Y] hours
|
|
58
|
-
👴 Senior (6+ years): [X-Y] hours
|
|
50
|
+
|
|
51
|
+
Average developer, average pace, no AI acceleration: [X-Y] hours
|
|
59
52
|
```
|
|
60
53
|
|
|
61
54
|
- **Natural Language Phrases:**
|
|
@@ -78,9 +71,7 @@ alwaysApply: true
|
|
|
78
71
|
|
|
79
72
|
Based on 5 subtasks and task-level analysis:
|
|
80
73
|
|
|
81
|
-
|
|
82
|
-
👨💼 Intermediate (3-5 years): 10-15 hours
|
|
83
|
-
👴 Senior (6+ years): 7-11 hours
|
|
74
|
+
Average developer, average pace, no AI acceleration: 10-15 hours
|
|
84
75
|
```
|
|
85
76
|
|
|
86
77
|
- **Integration:**
|
|
@@ -89,4 +80,4 @@ alwaysApply: true
|
|
|
89
80
|
- Estimates are informational only (not stored)
|
|
90
81
|
|
|
91
82
|
- **Principle:**
|
|
92
|
-
> **Provide realistic time estimates based on real
|
|
83
|
+
> **Provide realistic time estimates for an average developer, based on the real complexity of the task itself. Subtask count informs scope, but must not be the sole estimation criterion, and estimates must not assume AI acceleration.**
|
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 the real complexity of the task, assuming an average developer working at an average pace without AI acceleration. Subtask count informs scope, but is not the sole criterion.
|
|
99
99
|
|
|
100
100
|
**Output includes:**
|
|
101
|
-
-
|
|
101
|
+
- A single estimate range for the majority of developers
|
|
102
102
|
- Estimates in hours and business days
|
|
103
103
|
- Recommendation for management with buffer
|
|
104
104
|
|
package/README.md
CHANGED
|
@@ -71,6 +71,7 @@ rbin-task-flow init
|
|
|
71
71
|
```bash
|
|
72
72
|
rbin-task-flow init # Inicializa no projeto atual
|
|
73
73
|
rbin-task-flow update # Atualiza configurações
|
|
74
|
+
rbin-task-flow reset # Reinstala o Task Flow do zero
|
|
74
75
|
rbin-task-flow version-check # Verifica atualizações de modelos
|
|
75
76
|
rbin-task-flow info # Mostra informações
|
|
76
77
|
rbin-task-flow check # Roda lint/fix e build quando existirem
|
|
@@ -253,6 +254,9 @@ Para atualizar configurações em um projeto existente:
|
|
|
253
254
|
cd /caminho/para/seu/projeto
|
|
254
255
|
rbin-task-flow update
|
|
255
256
|
|
|
257
|
+
# Para reiniciar o Task Flow do zero, incluindo .task-flow/.internal
|
|
258
|
+
rbin-task-flow reset
|
|
259
|
+
|
|
256
260
|
# Ou usando método legacy
|
|
257
261
|
~/.rbin-task-flow/install.sh
|
|
258
262
|
# Digite o caminho do projeto
|
|
@@ -418,6 +422,7 @@ rbin-task-flow init
|
|
|
418
422
|
```bash
|
|
419
423
|
rbin-task-flow init # Initialize in current project
|
|
420
424
|
rbin-task-flow update # Update configurations
|
|
425
|
+
rbin-task-flow reset # Reinstall Task Flow from scratch
|
|
421
426
|
rbin-task-flow version-check # Check for model updates
|
|
422
427
|
rbin-task-flow info # Show information
|
|
423
428
|
rbin-task-flow check # Run lint/fix and build when available
|
|
@@ -600,6 +605,9 @@ To update configs in an existing project:
|
|
|
600
605
|
cd /path/to/your/project
|
|
601
606
|
rbin-task-flow update
|
|
602
607
|
|
|
608
|
+
# To reset Task Flow from scratch, including .task-flow/.internal
|
|
609
|
+
rbin-task-flow reset
|
|
610
|
+
|
|
603
611
|
# Or using legacy method
|
|
604
612
|
~/.rbin-task-flow/install.sh
|
|
605
613
|
# Enter the project path
|
package/bin/cli.js
CHANGED
|
@@ -33,6 +33,15 @@ program
|
|
|
33
33
|
await installInProject(targetPath, { update: true });
|
|
34
34
|
});
|
|
35
35
|
|
|
36
|
+
program
|
|
37
|
+
.command('reset')
|
|
38
|
+
.description('Reset RBIN Task Flow in current directory')
|
|
39
|
+
.option('-p, --path <path>', 'Target directory (default: current directory)')
|
|
40
|
+
.action(async (options) => {
|
|
41
|
+
const targetPath = options.path || process.cwd();
|
|
42
|
+
await installInProject(targetPath, { reset: true });
|
|
43
|
+
});
|
|
44
|
+
|
|
36
45
|
program
|
|
37
46
|
.command('version-check')
|
|
38
47
|
.description('Check for model version updates')
|
|
@@ -42,7 +51,7 @@ program
|
|
|
42
51
|
|
|
43
52
|
program
|
|
44
53
|
.command('estimate')
|
|
45
|
-
.description('Estimate time for task(s) based on
|
|
54
|
+
.description('Estimate time for task(s) based on real task complexity and average development pace')
|
|
46
55
|
.argument('<taskIds>', 'Task ID(s) to estimate (comma-separated or "all")')
|
|
47
56
|
.option('-p, --path <path>', 'Target directory (default: current directory)')
|
|
48
57
|
.action(async (taskIds, options) => {
|
|
@@ -91,6 +100,7 @@ program
|
|
|
91
100
|
console.log(chalk.yellow('\nCommands:'));
|
|
92
101
|
console.log(chalk.cyan(' rbin-task-flow init') + ' - Initialize in current directory');
|
|
93
102
|
console.log(chalk.cyan(' rbin-task-flow update') + ' - Update configurations');
|
|
103
|
+
console.log(chalk.cyan(' rbin-task-flow reset') + ' - Reset task flow files from scratch');
|
|
94
104
|
console.log(chalk.cyan(' rbin-task-flow version-check') + ' - Check for model updates');
|
|
95
105
|
console.log(chalk.cyan(' rbin-task-flow estimate <ids>') + ' - Estimate time (e.g., "1" or "1,2" or "all")');
|
|
96
106
|
console.log(chalk.cyan(' rbin-task-flow report <ids>') + ' - Generate report (e.g., "1" or "1,2" or "all")');
|
package/lib/estimate.js
CHANGED
|
@@ -55,34 +55,22 @@ async function estimateTask(taskIdsInput, targetPath = process.cwd()) {
|
|
|
55
55
|
console.log(chalk.blue(`Complexity: ${chalk.yellow(analysis.level)} (${subtaskCount} subtasks)\n`));
|
|
56
56
|
|
|
57
57
|
console.log(chalk.cyan('─'.repeat(70)));
|
|
58
|
-
console.log(chalk.magenta.bold('Time
|
|
58
|
+
console.log(chalk.magenta.bold('Time Estimate for Average Development Pace:\n'));
|
|
59
59
|
|
|
60
60
|
console.log(chalk.gray(`Signals: ${analysis.signals.join(', ')}\n`));
|
|
61
61
|
|
|
62
|
-
const
|
|
63
|
-
const midDays = Math.ceil(estimates.mid.upper / 8);
|
|
64
|
-
const seniorDays = Math.ceil(estimates.senior.upper / 8);
|
|
62
|
+
const estimateDays = Math.ceil(estimates.upper / 8);
|
|
65
63
|
|
|
66
|
-
console.log(chalk.gray('
|
|
67
|
-
console.log(chalk.white(' Hours:'), chalk.yellow(`${estimates.
|
|
68
|
-
console.log(chalk.white(' Days: '), chalk.yellow(`~${
|
|
69
|
-
console.log('');
|
|
70
|
-
|
|
71
|
-
console.log(chalk.gray('👨💼 Intermediate (3-5 years):'));
|
|
72
|
-
console.log(chalk.white(' Hours:'), chalk.yellow(`${estimates.mid.lower}-${estimates.mid.upper} hours`));
|
|
73
|
-
console.log(chalk.white(' Days: '), chalk.yellow(`~${midDays} business day(s)`));
|
|
74
|
-
console.log('');
|
|
75
|
-
|
|
76
|
-
console.log(chalk.gray('👴 Senior Developer (6+ years):'));
|
|
77
|
-
console.log(chalk.white(' Hours:'), chalk.yellow(`${estimates.senior.lower}-${estimates.senior.upper} hours`));
|
|
78
|
-
console.log(chalk.white(' Days: '), chalk.yellow(`~${seniorDays} business day(s)`));
|
|
64
|
+
console.log(chalk.gray('Assumption: average developer, average delivery pace, without AI acceleration.'));
|
|
65
|
+
console.log(chalk.white(' Hours:'), chalk.yellow(`${estimates.lower}-${estimates.upper} hours`));
|
|
66
|
+
console.log(chalk.white(' Days: '), chalk.yellow(`~${estimateDays} business day(s)`));
|
|
79
67
|
console.log('');
|
|
80
68
|
|
|
81
69
|
console.log(chalk.cyan('─'.repeat(70)));
|
|
82
70
|
console.log(chalk.magenta.bold('Recommendation for Management:\n'));
|
|
83
|
-
console.log(chalk.white(' Recommended estimate:'), chalk.yellow(`${estimates.
|
|
71
|
+
console.log(chalk.white(' Recommended estimate:'), chalk.yellow(`${estimates.lower}-${estimates.upper} hours`), chalk.gray('(average developer baseline)'));
|
|
84
72
|
console.log(chalk.white(' Buffer recommended:'), chalk.yellow(`+20%`), chalk.gray('for unexpected issues'));
|
|
85
|
-
console.log(chalk.white(' Total estimate:'), chalk.yellow(`${Math.round(estimates.
|
|
73
|
+
console.log(chalk.white(' Total estimate:'), chalk.yellow(`${Math.round(estimates.upper * 1.2)} hours`), chalk.gray(`(~${Math.ceil(estimates.upper * 1.2 / 8)} business days)`));
|
|
86
74
|
console.log('');
|
|
87
75
|
|
|
88
76
|
successCount++;
|
|
@@ -109,18 +97,8 @@ function calculateEstimates(analysis) {
|
|
|
109
97
|
const baseUpper = subtaskCount * baseRange.upper * riskMultiplier;
|
|
110
98
|
|
|
111
99
|
return {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
upper: Math.round(baseUpper * 1.5)
|
|
115
|
-
},
|
|
116
|
-
mid: {
|
|
117
|
-
lower: baseLower,
|
|
118
|
-
upper: baseUpper
|
|
119
|
-
},
|
|
120
|
-
senior: {
|
|
121
|
-
lower: Math.round(baseLower * 0.7),
|
|
122
|
-
upper: Math.round(baseUpper * 0.7)
|
|
123
|
-
}
|
|
100
|
+
lower: Math.round(baseLower),
|
|
101
|
+
upper: Math.round(baseUpper)
|
|
124
102
|
};
|
|
125
103
|
}
|
|
126
104
|
|
package/lib/install.js
CHANGED
|
@@ -8,10 +8,13 @@ const TEMPLATE_DIR = path.join(__dirname, '..');
|
|
|
8
8
|
|
|
9
9
|
async function installInProject(targetPath, options = {}) {
|
|
10
10
|
const isUpdate = options.update || false;
|
|
11
|
+
const isReset = options.reset || false;
|
|
11
12
|
|
|
12
13
|
showHeader();
|
|
13
14
|
|
|
14
|
-
if (
|
|
15
|
+
if (isReset) {
|
|
16
|
+
console.log(chalk.blue('♻️ Resetting RBIN Task Flow...'));
|
|
17
|
+
} else if (isUpdate) {
|
|
15
18
|
console.log(chalk.blue('🔄 Updating RBIN Task Flow...'));
|
|
16
19
|
} else {
|
|
17
20
|
console.log(chalk.blue('🚀 Installing RBIN Task Flow...'));
|
|
@@ -36,6 +39,10 @@ async function installInProject(targetPath, options = {}) {
|
|
|
36
39
|
|
|
37
40
|
spinner.text = 'Creating directories...';
|
|
38
41
|
|
|
42
|
+
if (isReset) {
|
|
43
|
+
await fs.remove(path.join(targetPath, '.task-flow'));
|
|
44
|
+
}
|
|
45
|
+
|
|
39
46
|
const dirs = [
|
|
40
47
|
'.cursor/rules',
|
|
41
48
|
'.claude',
|
|
@@ -48,7 +55,7 @@ async function installInProject(targetPath, options = {}) {
|
|
|
48
55
|
|
|
49
56
|
spinner.text = 'Copying configuration files...';
|
|
50
57
|
|
|
51
|
-
await copyConfigs(targetPath, isUpdate);
|
|
58
|
+
await copyConfigs(targetPath, { update: isUpdate, reset: isReset });
|
|
52
59
|
|
|
53
60
|
spinner.text = 'Updating .gitignore...';
|
|
54
61
|
|
|
@@ -69,11 +76,14 @@ async function installInProject(targetPath, options = {}) {
|
|
|
69
76
|
}
|
|
70
77
|
}
|
|
71
78
|
|
|
72
|
-
async function copyConfigs(targetPath,
|
|
79
|
+
async function copyConfigs(targetPath, options = {}) {
|
|
80
|
+
const isUpdate = options.update || false;
|
|
81
|
+
const isReset = options.reset || false;
|
|
82
|
+
|
|
73
83
|
const cursorRulesPath = path.join(TEMPLATE_DIR, '.cursor/rules');
|
|
74
84
|
const cursorRulesDest = path.join(targetPath, '.cursor/rules');
|
|
75
85
|
if (fs.existsSync(cursorRulesPath)) {
|
|
76
|
-
if (isUpdate && fs.existsSync(cursorRulesDest)) {
|
|
86
|
+
if ((isUpdate || isReset) && fs.existsSync(cursorRulesDest)) {
|
|
77
87
|
await fs.emptyDir(cursorRulesDest);
|
|
78
88
|
}
|
|
79
89
|
await fs.copy(cursorRulesPath, cursorRulesDest, { overwrite: true });
|
|
@@ -120,10 +130,12 @@ async function copyConfigs(targetPath, isUpdate = false) {
|
|
|
120
130
|
showSuccess('Codex instructions (AGENTS.md)');
|
|
121
131
|
}
|
|
122
132
|
|
|
123
|
-
await copyTaskFlow(targetPath, isUpdate);
|
|
133
|
+
await copyTaskFlow(targetPath, { update: isUpdate, reset: isReset });
|
|
124
134
|
}
|
|
125
135
|
|
|
126
|
-
async function copyTaskFlow(targetPath,
|
|
136
|
+
async function copyTaskFlow(targetPath, options = {}) {
|
|
137
|
+
const isUpdate = options.update || false;
|
|
138
|
+
const isReset = options.reset || false;
|
|
127
139
|
const taskFlowSrc = path.join(TEMPLATE_DIR, '.task-flow');
|
|
128
140
|
const taskFlowDest = path.join(targetPath, '.task-flow');
|
|
129
141
|
|
|
@@ -141,6 +153,10 @@ async function copyTaskFlow(targetPath, isUpdate = false) {
|
|
|
141
153
|
await fs.copy(taskFlowSrc, taskFlowDest, {
|
|
142
154
|
overwrite: true,
|
|
143
155
|
filter: (src, dest) => {
|
|
156
|
+
if (isReset) {
|
|
157
|
+
return true;
|
|
158
|
+
}
|
|
159
|
+
|
|
144
160
|
if (PROTECTED.some((p) => src.startsWith(p) || dest.startsWith(p))) {
|
|
145
161
|
return false;
|
|
146
162
|
}
|
|
@@ -168,7 +184,9 @@ async function copyTaskFlow(targetPath, isUpdate = false) {
|
|
|
168
184
|
}
|
|
169
185
|
|
|
170
186
|
showSuccess('Task Flow directory');
|
|
171
|
-
if (
|
|
187
|
+
if (isReset) {
|
|
188
|
+
showWarning('Reset completed: .task-flow was recreated from scratch');
|
|
189
|
+
} else if (isUpdate) {
|
|
172
190
|
showInfo('Protected: .internal/ (your task data is safe)');
|
|
173
191
|
} else {
|
|
174
192
|
showInfo('Protected on init: .internal/, tasks.input.txt, tasks.status.md, tasks.flow.md');
|