agileflow 2.82.5 → 2.84.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.
- package/CHANGELOG.md +10 -0
- package/package.json +1 -1
- package/scripts/agileflow-configure.js +2 -4
- package/scripts/agileflow-statusline.sh +50 -3
- package/scripts/agileflow-welcome.js +84 -11
- package/scripts/check-update.js +12 -63
- package/scripts/lib/file-tracking.js +733 -0
- package/scripts/lib/story-claiming.js +558 -0
- package/scripts/obtain-context.js +117 -1
- package/scripts/session-manager.js +519 -1
- package/src/core/agents/configuration-visual-e2e.md +29 -1
- package/src/core/agents/ui.md +50 -0
- package/src/core/commands/babysit.md +118 -0
- package/src/core/commands/session/end.md +44 -2
- package/tools/cli/commands/start.js +0 -178
- package/tools/cli/tui/Dashboard.js +0 -65
- package/tools/cli/tui/StoryList.js +0 -69
- package/tools/cli/tui/index.js +0 -16
|
@@ -184,6 +184,47 @@ When `VISUAL=true` is specified, the loop adds screenshot verification:
|
|
|
184
184
|
**Setup requirement:**
|
|
185
185
|
Run `/agileflow:configure` and select "Set up Visual E2E testing" to install Playwright and create e2e tests.
|
|
186
186
|
|
|
187
|
+
### Visual Mode Auto-Detection (IMPORTANT)
|
|
188
|
+
|
|
189
|
+
**Check the context output** from `obtain-context.js` for Visual E2E status.
|
|
190
|
+
|
|
191
|
+
**If "📸 VISUAL E2E TESTING: ENABLED" appears:**
|
|
192
|
+
|
|
193
|
+
When presenting task options for UI-focused work, **proactively suggest VISUAL mode**:
|
|
194
|
+
|
|
195
|
+
```
|
|
196
|
+
This epic involves UI work. Visual E2E is configured.
|
|
197
|
+
|
|
198
|
+
Suggested command:
|
|
199
|
+
/agileflow:babysit EPIC=EP-0042 MODE=loop VISUAL=true
|
|
200
|
+
|
|
201
|
+
Visual Mode ensures:
|
|
202
|
+
- Screenshots are captured and verified
|
|
203
|
+
- Minimum 2 iterations (prevents premature completion)
|
|
204
|
+
- Both tests AND visual verification must pass
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
**Detection criteria for suggesting VISUAL=true:**
|
|
208
|
+
| Indicator | Suggest VISUAL? |
|
|
209
|
+
|-----------|-----------------|
|
|
210
|
+
| Epic mentions "UI", "component", "styling" | Yes |
|
|
211
|
+
| Stories have owner: AG-UI | Yes |
|
|
212
|
+
| Files involve src/components/, *.css, *.tsx | Yes |
|
|
213
|
+
| Work is API/backend only | No |
|
|
214
|
+
| Work is CLI/scripts only | No |
|
|
215
|
+
|
|
216
|
+
**If "VISUAL E2E TESTING: NOT CONFIGURED" appears:**
|
|
217
|
+
|
|
218
|
+
For UI work, suggest setup:
|
|
219
|
+
```
|
|
220
|
+
This is UI work. Visual E2E not configured.
|
|
221
|
+
|
|
222
|
+
To enable screenshot verification:
|
|
223
|
+
/agileflow:configure → Visual E2E testing
|
|
224
|
+
|
|
225
|
+
This helps catch visual issues that tests miss (wrong colors, broken layouts).
|
|
226
|
+
```
|
|
227
|
+
|
|
187
228
|
### Loop Control Commands
|
|
188
229
|
|
|
189
230
|
```bash
|
|
@@ -662,6 +703,7 @@ Based on your project state:
|
|
|
662
703
|
- Delegate complex work to experts
|
|
663
704
|
- If stuck 2+ times → research prompt
|
|
664
705
|
- Use state narration markers (📍🔀🔄⚠️✅) for visibility
|
|
706
|
+
- **STORY CLAIMING**: Claim stories before working, skip 🔒 claimed stories in suggestions
|
|
665
707
|
|
|
666
708
|
<!-- COMPACT_SUMMARY_END -->
|
|
667
709
|
|
|
@@ -1096,6 +1138,82 @@ After loading context, analyze and present ranked options:
|
|
|
1096
1138
|
|
|
1097
1139
|
---
|
|
1098
1140
|
|
|
1141
|
+
## STORY CLAIMING (Multi-Session Coordination)
|
|
1142
|
+
|
|
1143
|
+
When multiple Claude Code sessions work in the same repo, story claiming prevents conflicts.
|
|
1144
|
+
|
|
1145
|
+
### How It Works
|
|
1146
|
+
|
|
1147
|
+
1. **Claim on Selection**: When user selects a story to work on, claim it:
|
|
1148
|
+
```bash
|
|
1149
|
+
node .agileflow/scripts/lib/story-claiming.js claim US-0042
|
|
1150
|
+
```
|
|
1151
|
+
|
|
1152
|
+
2. **Check Before Suggesting**: Filter out claimed stories from suggestions:
|
|
1153
|
+
- Stories with 🔒 badge are claimed by OTHER sessions
|
|
1154
|
+
- Stories with ✓ badge are claimed by THIS session (can continue)
|
|
1155
|
+
- Stories without badge are available
|
|
1156
|
+
|
|
1157
|
+
3. **Release on Completion**: When story is marked "done", release claim:
|
|
1158
|
+
```bash
|
|
1159
|
+
node .agileflow/scripts/lib/story-claiming.js release US-0042
|
|
1160
|
+
```
|
|
1161
|
+
|
|
1162
|
+
### Story Badges in AskUserQuestion
|
|
1163
|
+
|
|
1164
|
+
| Badge | Meaning | Action |
|
|
1165
|
+
|-------|---------|--------|
|
|
1166
|
+
| ⭐ | Ready, available | Can select |
|
|
1167
|
+
| 🔒 | Claimed by other session | **DO NOT suggest** (or show as disabled) |
|
|
1168
|
+
| ✓ | Claimed by this session | Continue working |
|
|
1169
|
+
|
|
1170
|
+
### Claiming Flow
|
|
1171
|
+
|
|
1172
|
+
```
|
|
1173
|
+
User: "Work on US-0042"
|
|
1174
|
+
↓
|
|
1175
|
+
Check: Is US-0042 claimed?
|
|
1176
|
+
↓
|
|
1177
|
+
┌──────────────┐ ┌──────────────────┐
|
|
1178
|
+
│ Not claimed │ │ Claimed by other │
|
|
1179
|
+
└──────────────┘ └──────────────────┘
|
|
1180
|
+
↓ ↓
|
|
1181
|
+
Claim it, proceed Show warning:
|
|
1182
|
+
"US-0042 is being worked on
|
|
1183
|
+
by Session 2 (../project-auth).
|
|
1184
|
+
|
|
1185
|
+
Pick a different story to
|
|
1186
|
+
avoid merge conflicts."
|
|
1187
|
+
```
|
|
1188
|
+
|
|
1189
|
+
### Commands
|
|
1190
|
+
|
|
1191
|
+
```bash
|
|
1192
|
+
# Claim a story
|
|
1193
|
+
node .agileflow/scripts/lib/story-claiming.js claim US-0042
|
|
1194
|
+
|
|
1195
|
+
# Release a story
|
|
1196
|
+
node .agileflow/scripts/lib/story-claiming.js release US-0042
|
|
1197
|
+
|
|
1198
|
+
# Check if claimed
|
|
1199
|
+
node .agileflow/scripts/lib/story-claiming.js check US-0042
|
|
1200
|
+
|
|
1201
|
+
# List stories claimed by others
|
|
1202
|
+
node .agileflow/scripts/lib/story-claiming.js others
|
|
1203
|
+
|
|
1204
|
+
# Clean stale claims (dead PIDs)
|
|
1205
|
+
node .agileflow/scripts/lib/story-claiming.js cleanup
|
|
1206
|
+
```
|
|
1207
|
+
|
|
1208
|
+
### Important Rules
|
|
1209
|
+
|
|
1210
|
+
- **Always claim before working**: Prevents conflicts
|
|
1211
|
+
- **Stale claims auto-expire**: If session PID dies or 4 hours pass
|
|
1212
|
+
- **Force claim available**: `--force` flag overrides (use sparingly)
|
|
1213
|
+
- **Release on completion**: Or let auto-expiry handle it
|
|
1214
|
+
|
|
1215
|
+
---
|
|
1216
|
+
|
|
1099
1217
|
## KNOWLEDGE INDEX
|
|
1100
1218
|
|
|
1101
1219
|
**Context script provides:**
|
|
@@ -190,7 +190,7 @@ If `hasConflicts: true`:
|
|
|
190
190
|
```
|
|
191
191
|
⚠️ Merge conflicts detected!
|
|
192
192
|
|
|
193
|
-
This branch has conflicts with {mainBranch}
|
|
193
|
+
This branch has conflicts with {mainBranch}. Smart merge can attempt automatic resolution.
|
|
194
194
|
```
|
|
195
195
|
|
|
196
196
|
Then show conflict options:
|
|
@@ -201,6 +201,8 @@ AskUserQuestion:
|
|
|
201
201
|
header: "Merge conflicts"
|
|
202
202
|
multiSelect: false
|
|
203
203
|
options:
|
|
204
|
+
- label: "Auto-resolve conflicts (Recommended)"
|
|
205
|
+
description: "Smart merge will resolve based on file types automatically"
|
|
204
206
|
- label: "Resolve manually"
|
|
205
207
|
description: "Keep session active and resolve conflicts yourself"
|
|
206
208
|
- label: "End session without merging"
|
|
@@ -209,6 +211,40 @@ AskUserQuestion:
|
|
|
209
211
|
description: "Keep session as-is"
|
|
210
212
|
```
|
|
211
213
|
|
|
214
|
+
If "Auto-resolve conflicts" selected:
|
|
215
|
+
```bash
|
|
216
|
+
node .agileflow/scripts/session-manager.js smart-merge {session_id} --strategy={squash|merge}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
The smart merge will:
|
|
220
|
+
1. Categorize conflicting files by type (docs, tests, schema, config, source)
|
|
221
|
+
2. Apply appropriate resolution strategy per file type
|
|
222
|
+
3. Log all auto-resolutions for audit
|
|
223
|
+
|
|
224
|
+
Display result:
|
|
225
|
+
```
|
|
226
|
+
✓ Conflicts auto-resolved!
|
|
227
|
+
|
|
228
|
+
Files resolved:
|
|
229
|
+
📄 docs/README.md → accept_both (Documentation kept from both)
|
|
230
|
+
🧪 tests/api.test.ts → accept_both (Tests kept from both)
|
|
231
|
+
⚙️ package.json → merge_keys (Config merged)
|
|
232
|
+
📝 src/api.ts → intelligent_merge (Source merged)
|
|
233
|
+
|
|
234
|
+
Merge log saved to: .agileflow/sessions/merge-log.json
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
If auto-resolution fails:
|
|
238
|
+
```
|
|
239
|
+
⚠️ Some conflicts could not be auto-resolved:
|
|
240
|
+
|
|
241
|
+
❌ src/complex.ts → Changes overlap in same code block
|
|
242
|
+
|
|
243
|
+
Options:
|
|
244
|
+
• Resolve manually (see instructions below)
|
|
245
|
+
• End session without merging
|
|
246
|
+
```
|
|
247
|
+
|
|
212
248
|
If "Resolve manually" selected, show instructions:
|
|
213
249
|
```
|
|
214
250
|
To resolve conflicts manually:
|
|
@@ -409,7 +445,13 @@ node .agileflow/scripts/session-manager.js merge-preview {session_id}
|
|
|
409
445
|
Display commits and files to be merged.
|
|
410
446
|
|
|
411
447
|
**Step 3: Check conflicts**
|
|
412
|
-
If `hasConflicts: true` → Show conflict options
|
|
448
|
+
If `hasConflicts: true` → Show conflict options (auto-resolve/manual/end/cancel)
|
|
449
|
+
|
|
450
|
+
**Step 3a: If auto-resolve selected**
|
|
451
|
+
```bash
|
|
452
|
+
node .agileflow/scripts/session-manager.js smart-merge {session_id} --strategy={squash|merge}
|
|
453
|
+
```
|
|
454
|
+
Smart merge auto-resolves by file type: docs→accept_both, tests→accept_both, config→merge_keys, source→theirs
|
|
413
455
|
|
|
414
456
|
**Step 4: Choose strategy**
|
|
415
457
|
```xml
|
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* AgileFlow TUI Dashboard
|
|
5
|
-
*
|
|
6
|
-
* BETA - Internal use only, not publicly documented
|
|
7
|
-
*
|
|
8
|
-
* Usage: npx agileflow start
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
const path = require('path');
|
|
12
|
-
const fs = require('fs');
|
|
13
|
-
const { c: colors } = require('../../../lib/colors');
|
|
14
|
-
|
|
15
|
-
function showBetaWarning() {
|
|
16
|
-
console.log('');
|
|
17
|
-
console.log(
|
|
18
|
-
`${colors.bgYellow}${colors.bold} BETA ${colors.reset} ${colors.yellow}This feature is in beta and not yet stable${colors.reset}`
|
|
19
|
-
);
|
|
20
|
-
console.log(`${colors.dim} Expect bugs and incomplete features${colors.reset}`);
|
|
21
|
-
console.log('');
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function showHeader() {
|
|
25
|
-
console.log(`${colors.orange}${colors.bold}`);
|
|
26
|
-
console.log(' ╔═══════════════════════════════════════════╗');
|
|
27
|
-
console.log(' ║ AgileFlow TUI Dashboard ║');
|
|
28
|
-
console.log(' ╚═══════════════════════════════════════════╝');
|
|
29
|
-
console.log(`${colors.reset}`);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
async function loadStatus() {
|
|
33
|
-
const statusPath = path.join(process.cwd(), 'docs', '09-agents', 'status.json');
|
|
34
|
-
|
|
35
|
-
if (!fs.existsSync(statusPath)) {
|
|
36
|
-
return null;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
try {
|
|
40
|
-
const content = fs.readFileSync(statusPath, 'utf8');
|
|
41
|
-
return JSON.parse(content);
|
|
42
|
-
} catch (err) {
|
|
43
|
-
return null;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function getStatusColor(status) {
|
|
48
|
-
switch (status) {
|
|
49
|
-
case 'completed':
|
|
50
|
-
case 'done':
|
|
51
|
-
return colors.green;
|
|
52
|
-
case 'in_progress':
|
|
53
|
-
case 'in-progress':
|
|
54
|
-
return colors.yellow;
|
|
55
|
-
case 'blocked':
|
|
56
|
-
return colors.red;
|
|
57
|
-
case 'ready':
|
|
58
|
-
return colors.cyan;
|
|
59
|
-
default:
|
|
60
|
-
return colors.dim;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function formatStory(story) {
|
|
65
|
-
const statusColor = getStatusColor(story.status);
|
|
66
|
-
const id = story.id || story.story_id || 'Unknown';
|
|
67
|
-
const title = story.title || story.summary || 'Untitled';
|
|
68
|
-
const status = (story.status || 'unknown').toUpperCase();
|
|
69
|
-
const owner = story.owner || '-';
|
|
70
|
-
|
|
71
|
-
return ` ${colors.bold}${id}${colors.reset} ${title.substring(0, 40)}${title.length > 40 ? '...' : ''}
|
|
72
|
-
${statusColor}[${status}]${colors.reset} ${colors.dim}Owner: ${owner}${colors.reset}`;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
async function showDashboard() {
|
|
76
|
-
showBetaWarning();
|
|
77
|
-
showHeader();
|
|
78
|
-
|
|
79
|
-
const status = await loadStatus();
|
|
80
|
-
|
|
81
|
-
if (!status) {
|
|
82
|
-
console.log(
|
|
83
|
-
`${colors.dim} No status.json found. Run /agileflow:story to create stories.${colors.reset}`
|
|
84
|
-
);
|
|
85
|
-
console.log('');
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// Count stories by status
|
|
90
|
-
const stories = Object.values(status).filter(
|
|
91
|
-
s => s && typeof s === 'object' && (s.id || s.story_id)
|
|
92
|
-
);
|
|
93
|
-
const counts = {
|
|
94
|
-
in_progress: stories.filter(s => ['in_progress', 'in-progress'].includes(s.status)).length,
|
|
95
|
-
blocked: stories.filter(s => s.status === 'blocked').length,
|
|
96
|
-
ready: stories.filter(s => s.status === 'ready').length,
|
|
97
|
-
completed: stories.filter(s => ['completed', 'done'].includes(s.status)).length,
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
const total = stories.length;
|
|
101
|
-
const completionPct = total > 0 ? Math.round((counts.completed / total) * 100) : 0;
|
|
102
|
-
|
|
103
|
-
// Summary
|
|
104
|
-
console.log(`${colors.bold} Summary${colors.reset}`);
|
|
105
|
-
console.log(` ────────────────────────────────────────────`);
|
|
106
|
-
console.log(
|
|
107
|
-
` ${colors.yellow}In Progress:${colors.reset} ${counts.in_progress} ${colors.red}Blocked:${colors.reset} ${counts.blocked} ${colors.cyan}Ready:${colors.reset} ${counts.ready} ${colors.green}Done:${colors.reset} ${counts.completed}`
|
|
108
|
-
);
|
|
109
|
-
console.log(` ${colors.dim}Completion: ${completionPct}%${colors.reset}`);
|
|
110
|
-
console.log('');
|
|
111
|
-
|
|
112
|
-
// In Progress Stories
|
|
113
|
-
const inProgressStories = stories.filter(s => ['in_progress', 'in-progress'].includes(s.status));
|
|
114
|
-
if (inProgressStories.length > 0) {
|
|
115
|
-
console.log(`${colors.bold} ${colors.yellow}In Progress${colors.reset}`);
|
|
116
|
-
console.log(` ────────────────────────────────────────────`);
|
|
117
|
-
inProgressStories.forEach(story => {
|
|
118
|
-
console.log(formatStory(story));
|
|
119
|
-
});
|
|
120
|
-
console.log('');
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// Blocked Stories
|
|
124
|
-
const blockedStories = stories.filter(s => s.status === 'blocked');
|
|
125
|
-
if (blockedStories.length > 0) {
|
|
126
|
-
console.log(`${colors.bold} ${colors.red}Blocked${colors.reset}`);
|
|
127
|
-
console.log(` ────────────────────────────────────────────`);
|
|
128
|
-
blockedStories.forEach(story => {
|
|
129
|
-
console.log(formatStory(story));
|
|
130
|
-
});
|
|
131
|
-
console.log('');
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// Ready Stories (up to 5)
|
|
135
|
-
const readyStories = stories.filter(s => s.status === 'ready').slice(0, 5);
|
|
136
|
-
if (readyStories.length > 0) {
|
|
137
|
-
console.log(
|
|
138
|
-
`${colors.bold} ${colors.cyan}Ready for Work${colors.reset} ${colors.dim}(showing ${readyStories.length} of ${counts.ready})${colors.reset}`
|
|
139
|
-
);
|
|
140
|
-
console.log(` ────────────────────────────────────────────`);
|
|
141
|
-
readyStories.forEach(story => {
|
|
142
|
-
console.log(formatStory(story));
|
|
143
|
-
});
|
|
144
|
-
console.log('');
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
console.log(`${colors.dim} Use /agileflow:board for interactive kanban view${colors.reset}`);
|
|
148
|
-
console.log(`${colors.dim} Use /agileflow:story:list for full story list${colors.reset}`);
|
|
149
|
-
console.log('');
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
async function main() {
|
|
153
|
-
const args = process.argv.slice(2);
|
|
154
|
-
|
|
155
|
-
// Check for help flag
|
|
156
|
-
if (args.includes('--help') || args.includes('-h')) {
|
|
157
|
-
showBetaWarning();
|
|
158
|
-
console.log(`${colors.bold}AgileFlow TUI Dashboard${colors.reset}`);
|
|
159
|
-
console.log('');
|
|
160
|
-
console.log(`${colors.bold}Usage:${colors.reset}`);
|
|
161
|
-
console.log(' npx agileflow start Show dashboard');
|
|
162
|
-
console.log(' npx agileflow start --help Show this help');
|
|
163
|
-
console.log('');
|
|
164
|
-
console.log(
|
|
165
|
-
`${colors.dim}This is a beta feature. For stable commands, use Claude Code slash commands.${colors.reset}`
|
|
166
|
-
);
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
await showDashboard();
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
main().catch(err => {
|
|
174
|
-
console.error(`${colors.red}Error:${colors.reset}`, err.message);
|
|
175
|
-
process.exit(1);
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
module.exports = { main };
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Dashboard Component
|
|
3
|
-
*
|
|
4
|
-
* BETA - Main TUI dashboard view
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
const path = require('path');
|
|
8
|
-
const fs = require('fs');
|
|
9
|
-
|
|
10
|
-
class Dashboard {
|
|
11
|
-
constructor(options = {}) {
|
|
12
|
-
this.statusPath =
|
|
13
|
-
options.statusPath || path.join(process.cwd(), 'docs', '09-agents', 'status.json');
|
|
14
|
-
this.data = null;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
async load() {
|
|
18
|
-
if (!fs.existsSync(this.statusPath)) {
|
|
19
|
-
return false;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
try {
|
|
23
|
-
const content = fs.readFileSync(this.statusPath, 'utf8');
|
|
24
|
-
this.data = JSON.parse(content);
|
|
25
|
-
return true;
|
|
26
|
-
} catch (err) {
|
|
27
|
-
return false;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
getStories() {
|
|
32
|
-
if (!this.data) return [];
|
|
33
|
-
return Object.values(this.data).filter(s => s && typeof s === 'object' && (s.id || s.story_id));
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
getStats() {
|
|
37
|
-
const stories = this.getStories();
|
|
38
|
-
return {
|
|
39
|
-
total: stories.length,
|
|
40
|
-
in_progress: stories.filter(s => ['in_progress', 'in-progress'].includes(s.status)).length,
|
|
41
|
-
blocked: stories.filter(s => s.status === 'blocked').length,
|
|
42
|
-
ready: stories.filter(s => s.status === 'ready').length,
|
|
43
|
-
completed: stories.filter(s => ['completed', 'done'].includes(s.status)).length,
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
getCompletionPercentage() {
|
|
48
|
-
const stats = this.getStats();
|
|
49
|
-
if (stats.total === 0) return 0;
|
|
50
|
-
return Math.round((stats.completed / stats.total) * 100);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
getStoriesByStatus(status) {
|
|
54
|
-
const stories = this.getStories();
|
|
55
|
-
if (status === 'in_progress') {
|
|
56
|
-
return stories.filter(s => ['in_progress', 'in-progress'].includes(s.status));
|
|
57
|
-
}
|
|
58
|
-
if (status === 'completed') {
|
|
59
|
-
return stories.filter(s => ['completed', 'done'].includes(s.status));
|
|
60
|
-
}
|
|
61
|
-
return stories.filter(s => s.status === status);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
module.exports = Dashboard;
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* StoryList Component
|
|
3
|
-
*
|
|
4
|
-
* BETA - Story list rendering for TUI
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
class StoryList {
|
|
8
|
-
constructor(stories = []) {
|
|
9
|
-
this.stories = stories;
|
|
10
|
-
this.selectedIndex = 0;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
setStories(stories) {
|
|
14
|
-
this.stories = stories;
|
|
15
|
-
this.selectedIndex = 0;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
selectNext() {
|
|
19
|
-
if (this.selectedIndex < this.stories.length - 1) {
|
|
20
|
-
this.selectedIndex++;
|
|
21
|
-
}
|
|
22
|
-
return this.getSelected();
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
selectPrev() {
|
|
26
|
-
if (this.selectedIndex > 0) {
|
|
27
|
-
this.selectedIndex--;
|
|
28
|
-
}
|
|
29
|
-
return this.getSelected();
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
getSelected() {
|
|
33
|
-
return this.stories[this.selectedIndex] || null;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
filter(predicate) {
|
|
37
|
-
return new StoryList(this.stories.filter(predicate));
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
sortByPriority() {
|
|
41
|
-
const priorityOrder = {
|
|
42
|
-
blocked: 0,
|
|
43
|
-
in_progress: 1,
|
|
44
|
-
'in-progress': 1,
|
|
45
|
-
ready: 2,
|
|
46
|
-
draft: 3,
|
|
47
|
-
completed: 4,
|
|
48
|
-
done: 4,
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
return new StoryList(
|
|
52
|
-
[...this.stories].sort((a, b) => {
|
|
53
|
-
const aPriority = priorityOrder[a.status] ?? 99;
|
|
54
|
-
const bPriority = priorityOrder[b.status] ?? 99;
|
|
55
|
-
return aPriority - bPriority;
|
|
56
|
-
})
|
|
57
|
-
);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
toArray() {
|
|
61
|
-
return [...this.stories];
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
get length() {
|
|
65
|
-
return this.stories.length;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
module.exports = StoryList;
|
package/tools/cli/tui/index.js
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AgileFlow TUI Components
|
|
3
|
-
*
|
|
4
|
-
* BETA - Internal use only, not publicly documented
|
|
5
|
-
*
|
|
6
|
-
* This module contains TUI components for the AgileFlow dashboard.
|
|
7
|
-
* Currently in early development.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
const Dashboard = require('./Dashboard');
|
|
11
|
-
const StoryList = require('./StoryList');
|
|
12
|
-
|
|
13
|
-
module.exports = {
|
|
14
|
-
Dashboard,
|
|
15
|
-
StoryList,
|
|
16
|
-
};
|