rbin-task-flow 1.19.2 → 1.19.4
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 +32 -41
- package/.cursor/rules/task_generate_flow.mdc +24 -12
- package/.task-flow/README.md +3 -3
- package/bin/cli.js +1 -1
- package/lib/estimate.js +109 -34
- package/lib/install.js +23 -6
- 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,52 +10,45 @@ 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
|
|
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
|
|
19
|
-
2. **
|
|
20
|
-
|
|
21
|
-
- **
|
|
22
|
-
- **
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
-
|
|
30
|
-
|
|
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
|
|
20
|
+
3. **Task Level Heuristics**:
|
|
21
|
+
- **Low**: localized change, clear requirement, low ambiguity, low regression risk
|
|
22
|
+
- **Medium**: multi-file implementation, moderate ambiguity, moderate validation needs
|
|
23
|
+
- **High**: architectural, cross-cutting, migration, security, performance, or high-risk change
|
|
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**:
|
|
27
|
+
- Low-level task: 1.5-2.5 hours
|
|
28
|
+
- Medium-level task: 2.5-4 hours
|
|
29
|
+
- High-level task: 4-6.5 hours
|
|
30
|
+
7. **Risk and scope adjustment**:
|
|
31
|
+
- Increase estimate when the task mentions integrations, migrations, refactors, security, performance, billing, or architecture work
|
|
32
|
+
8. **Range Calculation**:
|
|
33
|
+
- Lower bound: subtasks × baseline lower × risk factor
|
|
34
|
+
- Upper bound: subtasks × baseline upper × risk factor
|
|
31
35
|
- Round to nearest hour
|
|
32
36
|
|
|
33
37
|
- **Estimation Formula:**
|
|
34
38
|
```
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
Upper: (subtasks × 3) × 1.5
|
|
40
|
-
|
|
41
|
-
Intermediate (3-5 years):
|
|
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
|
|
39
|
+
1. Infer task level: low, medium, or high
|
|
40
|
+
2. Choose average-pace baseline per subtask from task level
|
|
41
|
+
3. Apply risk/scope multiplier when complexity indicators exist
|
|
42
|
+
4. Return one range that represents the majority of developers
|
|
48
43
|
```
|
|
49
44
|
|
|
50
45
|
- **Display Format:**
|
|
51
46
|
```
|
|
52
47
|
📊 Time Estimation for Task X: [Task Title]
|
|
53
48
|
|
|
54
|
-
Based on [N] subtasks:
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
👨💼 Intermediate (3-5 years): [X-Y] hours
|
|
58
|
-
👴 Senior (6+ years): [X-Y] hours
|
|
49
|
+
Based on [N] subtasks and task-level analysis:
|
|
50
|
+
|
|
51
|
+
Average developer, average pace, no AI acceleration: [X-Y] hours
|
|
59
52
|
```
|
|
60
53
|
|
|
61
54
|
- **Natural Language Phrases:**
|
|
@@ -72,15 +65,13 @@ alwaysApply: true
|
|
|
72
65
|
```
|
|
73
66
|
User: "task-flow: estimate 1"
|
|
74
67
|
|
|
75
|
-
Task 1 has 5 subtasks:
|
|
68
|
+
Task 1 has 5 subtasks and medium complexity signals:
|
|
76
69
|
|
|
77
70
|
📊 Time Estimation for Task 1: Create authentication system
|
|
78
71
|
|
|
79
|
-
Based on 5 subtasks:
|
|
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
|
|
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.**
|
|
@@ -8,19 +8,30 @@ alwaysApply: true
|
|
|
8
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
10
|
- **READ**: `.task-flow/.internal/tasks.json` and optionally `status.json`
|
|
11
|
-
- **SEARCH**: Current versions of
|
|
11
|
+
- **SEARCH**: Current versions of GPT-5.x, Composer, Claude (Haiku, Sonnet) via web search
|
|
12
12
|
- **WRITE**: Populate `.task-flow/tasks.flow.md` with the generated flow
|
|
13
13
|
- **DO NOT**: Populate tasks.flow.md when running `task-flow: sync`
|
|
14
14
|
|
|
15
15
|
- **Generation Process:**
|
|
16
|
-
1. **Search for current model versions** (web search):
|
|
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
17
|
2. Read tasks from tasks.json
|
|
18
18
|
3. For each task, determine:
|
|
19
19
|
- **Dependencies**: Which tasks must be completed first (or "—" if none)
|
|
20
|
-
- **Estimated hours** (for billing):
|
|
21
|
-
- **3 model options** (in order of priority): one
|
|
22
|
-
- **
|
|
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.
|
|
23
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
|
|
24
35
|
|
|
25
36
|
- **Output Format (direct blocks per task):**
|
|
26
37
|
```markdown
|
|
@@ -37,7 +48,7 @@ alwaysApply: true
|
|
|
37
48
|
| ⚡ | Pode iniciar agora (deps satisfeitas) |
|
|
38
49
|
| 🔒 | Bloqueada por dependências |
|
|
39
50
|
|
|
40
|
-
**Esforço (Effort):**
|
|
51
|
+
**Esforço (Effort):** definido pelo nível e dificuldade real da task, não pela quantidade de subtasks
|
|
41
52
|
**Claude:** Haiku ou Sonnet — **não use Opus**
|
|
42
53
|
|
|
43
54
|
## Tasks (blocos diretos por task)
|
|
@@ -46,7 +57,7 @@ alwaysApply: true
|
|
|
46
57
|
- **Depende de:** —
|
|
47
58
|
- **Horas cobrança:** 10-15h
|
|
48
59
|
- **Modelos sugeridos** (ordem de prioridade):
|
|
49
|
-
1.
|
|
60
|
+
1. GPT-5.x [versão] — effort medium
|
|
50
61
|
2. Composer [versão] — effort medium
|
|
51
62
|
3. Claude Sonnet [versão] — effort medium
|
|
52
63
|
|
|
@@ -54,8 +65,8 @@ alwaysApply: true
|
|
|
54
65
|
- **Depende de:** Task 1 ✅
|
|
55
66
|
- **Horas cobrança:** 10-15h
|
|
56
67
|
- **Modelos sugeridos** (ordem de prioridade):
|
|
57
|
-
1.
|
|
58
|
-
2.
|
|
68
|
+
1. Composer [versão] — effort medium
|
|
69
|
+
2. GPT-5.x [versão] — effort medium
|
|
59
70
|
3. Claude Sonnet [versão] — effort medium
|
|
60
71
|
|
|
61
72
|
### 🔒 Task 3 — [Título]
|
|
@@ -63,17 +74,18 @@ alwaysApply: true
|
|
|
63
74
|
- **Horas cobrança:** 8-12h
|
|
64
75
|
- **Modelos sugeridos** (ordem de prioridade):
|
|
65
76
|
1. Claude Haiku [versão] — effort low
|
|
66
|
-
2.
|
|
77
|
+
2. GPT-5.x [versão] — effort low
|
|
67
78
|
3. Composer [versão] — effort low
|
|
68
79
|
```
|
|
69
80
|
|
|
70
81
|
- **Model Rules:**
|
|
71
|
-
- **3 models per task**: One
|
|
82
|
+
- **3 models per task**: One GPT-5.x model, one Composer, one Claude — always in order of priority
|
|
72
83
|
- **Version required**: Include current version next to each model name (from web search)
|
|
73
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
|
|
74
86
|
- **No Opus**: Claude = Haiku or Sonnet only
|
|
75
87
|
|
|
76
88
|
- **Natural Language:** "generate flow", "gerar flow", "flow"
|
|
77
89
|
|
|
78
90
|
- **Principle:**
|
|
79
|
-
> **Populate tasks.flow.md with direct blocks per task. Dependencies, hours, and 3 model options (
|
|
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 the
|
|
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
|
|
|
@@ -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/bin/cli.js
CHANGED
|
@@ -42,7 +42,7 @@ program
|
|
|
42
42
|
|
|
43
43
|
program
|
|
44
44
|
.command('estimate')
|
|
45
|
-
.description('Estimate time for task(s) based on
|
|
45
|
+
.description('Estimate time for task(s) based on real task complexity and average development pace')
|
|
46
46
|
.argument('<taskIds>', 'Task ID(s) to estimate (comma-separated or "all")')
|
|
47
47
|
.option('-p, --path <path>', 'Target directory (default: current directory)')
|
|
48
48
|
.action(async (taskIds, options) => {
|
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,35 +52,25 @@ 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
|
-
console.log(chalk.magenta.bold('Time
|
|
58
|
+
console.log(chalk.magenta.bold('Time Estimate for Average Development Pace:\n'));
|
|
58
59
|
|
|
59
|
-
|
|
60
|
-
const midDays = Math.ceil(estimates.mid.upper / 8);
|
|
61
|
-
const seniorDays = Math.ceil(estimates.senior.upper / 8);
|
|
60
|
+
console.log(chalk.gray(`Signals: ${analysis.signals.join(', ')}\n`));
|
|
62
61
|
|
|
63
|
-
|
|
64
|
-
console.log(chalk.white(' Hours:'), chalk.yellow(`${estimates.junior.lower}-${estimates.junior.upper} hours`));
|
|
65
|
-
console.log(chalk.white(' Days: '), chalk.yellow(`~${juniorDays} business day(s)`));
|
|
66
|
-
console.log('');
|
|
67
|
-
|
|
68
|
-
console.log(chalk.gray('👨💼 Intermediate (3-5 years):'));
|
|
69
|
-
console.log(chalk.white(' Hours:'), chalk.yellow(`${estimates.mid.lower}-${estimates.mid.upper} hours`));
|
|
70
|
-
console.log(chalk.white(' Days: '), chalk.yellow(`~${midDays} business day(s)`));
|
|
71
|
-
console.log('');
|
|
62
|
+
const estimateDays = Math.ceil(estimates.upper / 8);
|
|
72
63
|
|
|
73
|
-
console.log(chalk.gray('
|
|
74
|
-
console.log(chalk.white(' Hours:'), chalk.yellow(`${estimates.
|
|
75
|
-
console.log(chalk.white(' Days: '), chalk.yellow(`~${
|
|
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)`));
|
|
76
67
|
console.log('');
|
|
77
68
|
|
|
78
69
|
console.log(chalk.cyan('─'.repeat(70)));
|
|
79
70
|
console.log(chalk.magenta.bold('Recommendation for Management:\n'));
|
|
80
|
-
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)'));
|
|
81
72
|
console.log(chalk.white(' Buffer recommended:'), chalk.yellow(`+20%`), chalk.gray('for unexpected issues'));
|
|
82
|
-
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)`));
|
|
83
74
|
console.log('');
|
|
84
75
|
|
|
85
76
|
successCount++;
|
|
@@ -94,24 +85,108 @@ async function estimateTask(taskIdsInput, targetPath = process.cwd()) {
|
|
|
94
85
|
}
|
|
95
86
|
}
|
|
96
87
|
|
|
97
|
-
function calculateEstimates(
|
|
98
|
-
const
|
|
99
|
-
const
|
|
88
|
+
function calculateEstimates(analysis) {
|
|
89
|
+
const { subtaskCount, level, riskMultiplier } = analysis;
|
|
90
|
+
const levelRanges = {
|
|
91
|
+
low: { lower: 1.5, upper: 2.5 },
|
|
92
|
+
medium: { lower: 2.5, upper: 4 },
|
|
93
|
+
high: { lower: 4, upper: 6.5 }
|
|
94
|
+
};
|
|
95
|
+
const baseRange = levelRanges[level] || levelRanges.medium;
|
|
96
|
+
const baseLower = subtaskCount * baseRange.lower * riskMultiplier;
|
|
97
|
+
const baseUpper = subtaskCount * baseRange.upper * riskMultiplier;
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
lower: Math.round(baseLower),
|
|
101
|
+
upper: Math.round(baseUpper)
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function analyzeTaskComplexity(task) {
|
|
106
|
+
const subtasks = Array.isArray(task.subtasks) ? task.subtasks : [];
|
|
107
|
+
const subtaskCount = subtasks.length;
|
|
108
|
+
const content = buildTaskContent(task, subtasks);
|
|
109
|
+
const keywordScores = scoreKeywords(content);
|
|
110
|
+
|
|
111
|
+
let score = 0;
|
|
112
|
+
|
|
113
|
+
if (subtaskCount <= 2) score += 1;
|
|
114
|
+
else if (subtaskCount <= 5) score += 2;
|
|
115
|
+
else score += 3;
|
|
116
|
+
|
|
117
|
+
score += keywordScores.complexity;
|
|
118
|
+
|
|
119
|
+
let level = 'medium';
|
|
120
|
+
if (score <= 2) level = 'low';
|
|
121
|
+
else if (score >= 6) level = 'high';
|
|
122
|
+
|
|
123
|
+
const riskMultiplier = Math.max(1, 1 + keywordScores.risk * 0.08 + keywordScores.scope * 0.06);
|
|
124
|
+
const signals = [
|
|
125
|
+
`${subtaskCount} subtasks`,
|
|
126
|
+
`${level} task level`
|
|
127
|
+
];
|
|
128
|
+
|
|
129
|
+
if (keywordScores.scope > 0) signals.push('multi-file or integration indicators');
|
|
130
|
+
if (keywordScores.risk > 0) signals.push('risk or validation indicators');
|
|
131
|
+
if (keywordScores.architecture > 0) signals.push('architecture/refactor indicators');
|
|
132
|
+
if (signals.length === 2) signals.push('no elevated complexity indicators');
|
|
100
133
|
|
|
101
134
|
return {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
135
|
+
subtaskCount,
|
|
136
|
+
level,
|
|
137
|
+
riskMultiplier,
|
|
138
|
+
signals
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function buildTaskContent(task, subtasks) {
|
|
143
|
+
const parts = [task.title, task.description];
|
|
144
|
+
|
|
145
|
+
for (const subtask of subtasks) {
|
|
146
|
+
if (typeof subtask === 'string') {
|
|
147
|
+
parts.push(subtask);
|
|
148
|
+
continue;
|
|
113
149
|
}
|
|
150
|
+
|
|
151
|
+
if (subtask && typeof subtask === 'object') {
|
|
152
|
+
parts.push(subtask.title, subtask.description);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return parts
|
|
157
|
+
.filter(Boolean)
|
|
158
|
+
.join(' ')
|
|
159
|
+
.toLowerCase();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function scoreKeywords(content) {
|
|
163
|
+
const architectureKeywords = [
|
|
164
|
+
'architecture', 'arquitetura', 'refactor', 'refator', 'migrate', 'migra',
|
|
165
|
+
'core', 'infra', 'foundation', 'sdk', 'abstraction'
|
|
166
|
+
];
|
|
167
|
+
const scopeKeywords = [
|
|
168
|
+
'integration', 'integracao', 'integrar', 'api', 'database', 'banco',
|
|
169
|
+
'auth', 'oauth', 'deploy', 'pipeline', 'webhook', 'service', 'provider'
|
|
170
|
+
];
|
|
171
|
+
const riskKeywords = [
|
|
172
|
+
'critical', 'critico', 'security', 'seguranca', 'performance', 'perf',
|
|
173
|
+
'bug', 'fix', 'regression', 'migration', 'compliance', 'billing', 'payment'
|
|
174
|
+
];
|
|
175
|
+
|
|
176
|
+
const architecture = countMatches(content, architectureKeywords);
|
|
177
|
+
const scope = countMatches(content, scopeKeywords);
|
|
178
|
+
const risk = countMatches(content, riskKeywords);
|
|
179
|
+
|
|
180
|
+
return {
|
|
181
|
+
architecture,
|
|
182
|
+
scope,
|
|
183
|
+
risk,
|
|
184
|
+
complexity: architecture * 2 + scope + risk
|
|
114
185
|
};
|
|
115
186
|
}
|
|
116
187
|
|
|
188
|
+
function countMatches(content, keywords) {
|
|
189
|
+
return keywords.reduce((total, keyword) => total + (content.includes(keyword) ? 1 : 0), 0);
|
|
190
|
+
}
|
|
191
|
+
|
|
117
192
|
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
|
|
|
@@ -154,8 +167,12 @@ async function copyTaskFlow(targetPath) {
|
|
|
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) {
|