agileflow 2.77.0 → 2.79.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 (128) hide show
  1. package/README.md +6 -6
  2. package/package.json +6 -1
  3. package/scripts/agileflow-configure.js +174 -2
  4. package/scripts/agileflow-statusline.sh +171 -78
  5. package/scripts/agileflow-welcome.js +88 -64
  6. package/scripts/auto-self-improve.js +23 -45
  7. package/scripts/check-update.js +35 -42
  8. package/scripts/damage-control/bash-tool-damage-control.js +257 -0
  9. package/scripts/damage-control/edit-tool-damage-control.js +279 -0
  10. package/scripts/damage-control/patterns.yaml +227 -0
  11. package/scripts/damage-control/write-tool-damage-control.js +274 -0
  12. package/scripts/damage-control-bash.js +232 -0
  13. package/scripts/damage-control-edit.js +243 -0
  14. package/scripts/damage-control-write.js +243 -0
  15. package/scripts/obtain-context.js +22 -3
  16. package/scripts/ralph-loop.js +191 -63
  17. package/scripts/screenshot-verifier.js +213 -0
  18. package/scripts/session-manager.js +12 -33
  19. package/src/core/agents/accessibility.md +124 -53
  20. package/src/core/agents/adr-writer.md +192 -52
  21. package/src/core/agents/analytics.md +139 -60
  22. package/src/core/agents/api.md +173 -63
  23. package/src/core/agents/ci.md +139 -57
  24. package/src/core/agents/compliance.md +159 -68
  25. package/src/core/agents/configuration/damage-control.md +356 -0
  26. package/src/core/agents/configuration-damage-control.md +248 -0
  27. package/src/core/agents/database.md +162 -61
  28. package/src/core/agents/datamigration.md +179 -66
  29. package/src/core/agents/design.md +179 -57
  30. package/src/core/agents/devops.md +160 -3
  31. package/src/core/agents/documentation.md +204 -60
  32. package/src/core/agents/epic-planner.md +147 -55
  33. package/src/core/agents/integrations.md +197 -69
  34. package/src/core/agents/mentor.md +158 -57
  35. package/src/core/agents/mobile.md +159 -67
  36. package/src/core/agents/monitoring.md +154 -65
  37. package/src/core/agents/multi-expert.md +115 -43
  38. package/src/core/agents/orchestrator.md +77 -24
  39. package/src/core/agents/performance.md +130 -75
  40. package/src/core/agents/product.md +151 -55
  41. package/src/core/agents/qa.md +162 -74
  42. package/src/core/agents/readme-updater.md +178 -76
  43. package/src/core/agents/refactor.md +148 -95
  44. package/src/core/agents/research.md +143 -72
  45. package/src/core/agents/security.md +154 -65
  46. package/src/core/agents/testing.md +176 -97
  47. package/src/core/agents/ui.md +170 -79
  48. package/src/core/commands/adr/list.md +171 -0
  49. package/src/core/commands/adr/update.md +235 -0
  50. package/src/core/commands/adr/view.md +252 -0
  51. package/src/core/commands/adr.md +207 -50
  52. package/src/core/commands/agent.md +16 -0
  53. package/src/core/commands/assign.md +148 -44
  54. package/src/core/commands/auto.md +18 -1
  55. package/src/core/commands/babysit.md +391 -38
  56. package/src/core/commands/baseline.md +14 -0
  57. package/src/core/commands/blockers.md +170 -51
  58. package/src/core/commands/board.md +144 -66
  59. package/src/core/commands/changelog.md +15 -0
  60. package/src/core/commands/ci.md +179 -69
  61. package/src/core/commands/compress.md +18 -0
  62. package/src/core/commands/configure.md +16 -0
  63. package/src/core/commands/context/export.md +193 -4
  64. package/src/core/commands/context/full.md +191 -18
  65. package/src/core/commands/context/note.md +248 -4
  66. package/src/core/commands/debt.md +17 -0
  67. package/src/core/commands/deploy.md +208 -65
  68. package/src/core/commands/deps.md +15 -0
  69. package/src/core/commands/diagnose.md +16 -0
  70. package/src/core/commands/docs.md +196 -64
  71. package/src/core/commands/epic/list.md +170 -0
  72. package/src/core/commands/epic/view.md +242 -0
  73. package/src/core/commands/epic.md +192 -69
  74. package/src/core/commands/feedback.md +191 -71
  75. package/src/core/commands/handoff.md +162 -48
  76. package/src/core/commands/help.md +9 -0
  77. package/src/core/commands/ideate.md +446 -0
  78. package/src/core/commands/impact.md +16 -0
  79. package/src/core/commands/metrics.md +141 -37
  80. package/src/core/commands/multi-expert.md +77 -0
  81. package/src/core/commands/packages.md +16 -0
  82. package/src/core/commands/pr.md +161 -67
  83. package/src/core/commands/readme-sync.md +16 -0
  84. package/src/core/commands/research/analyze.md +568 -0
  85. package/src/core/commands/research/ask.md +345 -20
  86. package/src/core/commands/research/import.md +562 -19
  87. package/src/core/commands/research/list.md +173 -5
  88. package/src/core/commands/research/view.md +181 -8
  89. package/src/core/commands/retro.md +135 -48
  90. package/src/core/commands/review.md +219 -47
  91. package/src/core/commands/session/end.md +209 -0
  92. package/src/core/commands/session/history.md +210 -0
  93. package/src/core/commands/session/init.md +116 -0
  94. package/src/core/commands/session/new.md +296 -0
  95. package/src/core/commands/session/resume.md +166 -0
  96. package/src/core/commands/session/status.md +166 -0
  97. package/src/core/commands/setup/visual-e2e.md +462 -0
  98. package/src/core/commands/skill/create.md +115 -17
  99. package/src/core/commands/skill/delete.md +117 -0
  100. package/src/core/commands/skill/edit.md +104 -0
  101. package/src/core/commands/skill/list.md +128 -0
  102. package/src/core/commands/skill/test.md +135 -0
  103. package/src/core/commands/skill/upgrade.md +542 -0
  104. package/src/core/commands/sprint.md +17 -1
  105. package/src/core/commands/status.md +133 -21
  106. package/src/core/commands/story/list.md +176 -0
  107. package/src/core/commands/story/view.md +265 -0
  108. package/src/core/commands/story-validate.md +101 -1
  109. package/src/core/commands/story.md +204 -51
  110. package/src/core/commands/template.md +16 -1
  111. package/src/core/commands/tests.md +226 -64
  112. package/src/core/commands/update.md +17 -1
  113. package/src/core/commands/validate-expertise.md +16 -0
  114. package/src/core/commands/velocity.md +140 -36
  115. package/src/core/commands/verify.md +14 -0
  116. package/src/core/commands/whats-new.md +30 -0
  117. package/src/core/skills/_learnings/README.md +91 -0
  118. package/src/core/skills/_learnings/_template.yaml +106 -0
  119. package/src/core/skills/_learnings/code-review.yaml +118 -0
  120. package/src/core/skills/_learnings/commit.yaml +69 -0
  121. package/src/core/skills/_learnings/story-writer.yaml +71 -0
  122. package/src/core/templates/damage-control-patterns.yaml +234 -0
  123. package/src/core/templates/skill-template.md +53 -11
  124. package/tools/cli/commands/start.js +180 -0
  125. package/tools/cli/installers/ide/claude-code.js +127 -0
  126. package/tools/cli/tui/Dashboard.js +66 -0
  127. package/tools/cli/tui/StoryList.js +69 -0
  128. package/tools/cli/tui/index.js +16 -0
@@ -23,19 +23,45 @@ Include:
23
23
  - Key capabilities
24
24
  - Expected outcomes
25
25
 
26
+ ## Self-Improving Learnings
27
+
28
+ This skill learns from your corrections and preferences.
29
+
30
+ **On invocation**:
31
+ 1. Check if `.agileflow/skills/_learnings/{skill-name}.yaml` exists
32
+ 2. If exists, read and apply learned preferences
33
+ 3. Follow conventions and avoid anti-patterns from learnings
34
+
35
+ **On correction**:
36
+ 1. When user corrects output, extract the signal
37
+ 2. Determine confidence level:
38
+ - **high**: Explicit correction ("never do X", "always do Y")
39
+ - **medium**: User approved or pattern worked well
40
+ - **low**: Observation to review later
41
+ 3. Update the learnings file with new preference
42
+
43
+ **Learnings file location**: `.agileflow/skills/_learnings/{skill-name}.yaml`
44
+
26
45
  ## Instructions
27
46
 
28
47
  Step-by-step guidance for Claude:
29
48
 
30
- 1. **First step**: What to do first
31
- - Detail A
32
- - Detail B
49
+ 1. **Load learnings** (if exists):
50
+ - Read `.agileflow/skills/_learnings/{skill-name}.yaml`
51
+ - Apply preferences, conventions, and anti-patterns
52
+ - Skip if file doesn't exist (first run)
33
53
 
34
- 2. **Second step**: What to do next
35
- - Detail A
36
- - Detail B
54
+ 2. **Execute skill**:
55
+ - Follow the instructions below with learned preferences applied
56
+ - [Your skill-specific step A]
57
+ - [Your skill-specific step B]
58
+
59
+ 3. **If user corrects**:
60
+ - Extract signal from correction
61
+ - Update learnings file
62
+ - Continue with corrected approach
37
63
 
38
- 3. **Final step**: How to complete the task
64
+ 4. **Final step**: Complete the task
39
65
  - Detail A
40
66
  - Detail B
41
67
 
@@ -50,9 +76,11 @@ Describe the expected output or deliverable:
50
76
  ## Quality Checklist
51
77
 
52
78
  Before completing, verify:
79
+ - [ ] Loaded learnings file (if exists)
80
+ - [ ] Applied learned preferences
53
81
  - [ ] Requirement 1 met
54
82
  - [ ] Requirement 2 met
55
- - [ ] Requirement 3 met
83
+ - [ ] If corrected, updated learnings file
56
84
 
57
85
  ## Examples
58
86
 
@@ -68,8 +96,22 @@ Before completing, verify:
68
96
  [Example of what Claude should produce]
69
97
  ```
70
98
 
99
+ ### Example 2: Learning from Correction
100
+
101
+ **User Correction:**
102
+ ```
103
+ "Don't include emojis in the output"
104
+ ```
105
+
106
+ **Self-Improve Action:**
107
+ 1. Extract signal: "User said 'Don't include emojis'"
108
+ 2. Learning: "Never include emojis in output"
109
+ 3. Confidence: high (explicit correction)
110
+ 4. Update `.agileflow/skills/_learnings/{skill-name}.yaml`
111
+
71
112
  ## Notes
72
113
 
73
- - Additional guidance or edge cases
74
- - Integration with other skills/commands
75
- - Important considerations
114
+ - Learnings persist across sessions
115
+ - First run has no learnings file - that's OK
116
+ - High-confidence learnings are treated as rules
117
+ - Git tracks learnings evolution over time
@@ -0,0 +1,180 @@
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
+
14
+ // ANSI color codes
15
+ const colors = {
16
+ reset: '\x1b[0m',
17
+ bold: '\x1b[1m',
18
+ dim: '\x1b[2m',
19
+ yellow: '\x1b[33m',
20
+ red: '\x1b[31m',
21
+ green: '\x1b[32m',
22
+ cyan: '\x1b[36m',
23
+ magenta: '\x1b[35m',
24
+ orange: '\x1b[38;2;232;104;58m',
25
+ bgYellow: '\x1b[43m',
26
+ bgRed: '\x1b[41m',
27
+ };
28
+
29
+ function showBetaWarning() {
30
+ console.log('');
31
+ console.log(`${colors.bgYellow}${colors.bold} BETA ${colors.reset} ${colors.yellow}This feature is in beta and not yet stable${colors.reset}`);
32
+ console.log(`${colors.dim} Expect bugs and incomplete features${colors.reset}`);
33
+ console.log('');
34
+ }
35
+
36
+ function showHeader() {
37
+ console.log(`${colors.orange}${colors.bold}`);
38
+ console.log(' ╔═══════════════════════════════════════════╗');
39
+ console.log(' ║ AgileFlow TUI Dashboard ║');
40
+ console.log(' ╚═══════════════════════════════════════════╝');
41
+ console.log(`${colors.reset}`);
42
+ }
43
+
44
+ async function loadStatus() {
45
+ const statusPath = path.join(process.cwd(), 'docs', '09-agents', 'status.json');
46
+
47
+ if (!fs.existsSync(statusPath)) {
48
+ return null;
49
+ }
50
+
51
+ try {
52
+ const content = fs.readFileSync(statusPath, 'utf8');
53
+ return JSON.parse(content);
54
+ } catch (err) {
55
+ return null;
56
+ }
57
+ }
58
+
59
+ function getStatusColor(status) {
60
+ switch (status) {
61
+ case 'completed':
62
+ case 'done':
63
+ return colors.green;
64
+ case 'in_progress':
65
+ case 'in-progress':
66
+ return colors.yellow;
67
+ case 'blocked':
68
+ return colors.red;
69
+ case 'ready':
70
+ return colors.cyan;
71
+ default:
72
+ return colors.dim;
73
+ }
74
+ }
75
+
76
+ function formatStory(story) {
77
+ const statusColor = getStatusColor(story.status);
78
+ const id = story.id || story.story_id || 'Unknown';
79
+ const title = story.title || story.summary || 'Untitled';
80
+ const status = (story.status || 'unknown').toUpperCase();
81
+ const owner = story.owner || '-';
82
+
83
+ return ` ${colors.bold}${id}${colors.reset} ${title.substring(0, 40)}${title.length > 40 ? '...' : ''}
84
+ ${statusColor}[${status}]${colors.reset} ${colors.dim}Owner: ${owner}${colors.reset}`;
85
+ }
86
+
87
+ async function showDashboard() {
88
+ showBetaWarning();
89
+ showHeader();
90
+
91
+ const status = await loadStatus();
92
+
93
+ if (!status) {
94
+ console.log(`${colors.dim} No status.json found. Run /agileflow:story to create stories.${colors.reset}`);
95
+ console.log('');
96
+ return;
97
+ }
98
+
99
+ // Count stories by status
100
+ const stories = Object.values(status).filter(s => s && typeof s === 'object' && (s.id || s.story_id));
101
+ const counts = {
102
+ in_progress: stories.filter(s => ['in_progress', 'in-progress'].includes(s.status)).length,
103
+ blocked: stories.filter(s => s.status === 'blocked').length,
104
+ ready: stories.filter(s => s.status === 'ready').length,
105
+ completed: stories.filter(s => ['completed', 'done'].includes(s.status)).length,
106
+ };
107
+
108
+ const total = stories.length;
109
+ const completionPct = total > 0 ? Math.round((counts.completed / total) * 100) : 0;
110
+
111
+ // Summary
112
+ console.log(`${colors.bold} Summary${colors.reset}`);
113
+ console.log(` ────────────────────────────────────────────`);
114
+ console.log(` ${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}`);
115
+ console.log(` ${colors.dim}Completion: ${completionPct}%${colors.reset}`);
116
+ console.log('');
117
+
118
+ // In Progress Stories
119
+ const inProgressStories = stories.filter(s => ['in_progress', 'in-progress'].includes(s.status));
120
+ if (inProgressStories.length > 0) {
121
+ console.log(`${colors.bold} ${colors.yellow}In Progress${colors.reset}`);
122
+ console.log(` ────────────────────────────────────────────`);
123
+ inProgressStories.forEach(story => {
124
+ console.log(formatStory(story));
125
+ });
126
+ console.log('');
127
+ }
128
+
129
+ // Blocked Stories
130
+ const blockedStories = stories.filter(s => s.status === 'blocked');
131
+ if (blockedStories.length > 0) {
132
+ console.log(`${colors.bold} ${colors.red}Blocked${colors.reset}`);
133
+ console.log(` ────────────────────────────────────────────`);
134
+ blockedStories.forEach(story => {
135
+ console.log(formatStory(story));
136
+ });
137
+ console.log('');
138
+ }
139
+
140
+ // Ready Stories (up to 5)
141
+ const readyStories = stories.filter(s => s.status === 'ready').slice(0, 5);
142
+ if (readyStories.length > 0) {
143
+ console.log(`${colors.bold} ${colors.cyan}Ready for Work${colors.reset} ${colors.dim}(showing ${readyStories.length} of ${counts.ready})${colors.reset}`);
144
+ console.log(` ────────────────────────────────────────────`);
145
+ readyStories.forEach(story => {
146
+ console.log(formatStory(story));
147
+ });
148
+ console.log('');
149
+ }
150
+
151
+ console.log(`${colors.dim} Use /agileflow:board for interactive kanban view${colors.reset}`);
152
+ console.log(`${colors.dim} Use /agileflow:story:list for full story list${colors.reset}`);
153
+ console.log('');
154
+ }
155
+
156
+ async function main() {
157
+ const args = process.argv.slice(2);
158
+
159
+ // Check for help flag
160
+ if (args.includes('--help') || args.includes('-h')) {
161
+ showBetaWarning();
162
+ console.log(`${colors.bold}AgileFlow TUI Dashboard${colors.reset}`);
163
+ console.log('');
164
+ console.log(`${colors.bold}Usage:${colors.reset}`);
165
+ console.log(' npx agileflow start Show dashboard');
166
+ console.log(' npx agileflow start --help Show this help');
167
+ console.log('');
168
+ console.log(`${colors.dim}This is a beta feature. For stable commands, use Claude Code slash commands.${colors.reset}`);
169
+ return;
170
+ }
171
+
172
+ await showDashboard();
173
+ }
174
+
175
+ main().catch(err => {
176
+ console.error(`${colors.red}Error:${colors.reset}`, err.message);
177
+ process.exit(1);
178
+ });
179
+
180
+ module.exports = { main };
@@ -69,6 +69,9 @@ class ClaudeCodeSetup extends BaseIdeSetup {
69
69
  await this.ensureDir(skillsTargetDir);
70
70
  console.log(chalk.dim(` - Skills directory: .claude/skills/ (for user-generated skills)`));
71
71
 
72
+ // Setup damage control hooks
73
+ await this.setupDamageControl(projectDir, agileflowDir, claudeDir, options);
74
+
72
75
  const totalCommands = commandResult.commands + agentResult.commands;
73
76
  const totalSubdirs =
74
77
  commandResult.subdirs + (agentResult.commands > 0 ? 1 : 0) + agentResult.subdirs;
@@ -86,6 +89,130 @@ class ClaudeCodeSetup extends BaseIdeSetup {
86
89
  subdirs: totalSubdirs,
87
90
  };
88
91
  }
92
+
93
+ /**
94
+ * Setup damage control hooks
95
+ * @param {string} projectDir - Project directory
96
+ * @param {string} agileflowDir - AgileFlow installation directory
97
+ * @param {string} claudeDir - .claude directory path
98
+ * @param {Object} options - Setup options
99
+ */
100
+ async setupDamageControl(projectDir, agileflowDir, claudeDir, options = {}) {
101
+ const damageControlSource = path.join(agileflowDir, 'scripts', 'damage-control');
102
+ const damageControlTarget = path.join(claudeDir, 'hooks', 'damage-control');
103
+
104
+ // Check if source exists
105
+ if (!fs.existsSync(damageControlSource)) {
106
+ console.log(chalk.dim(` - Damage control: source not found, skipping`));
107
+ return;
108
+ }
109
+
110
+ // Create hooks directory
111
+ await this.ensureDir(damageControlTarget);
112
+
113
+ // Copy hook scripts
114
+ const scripts = ['bash-tool-damage-control.js', 'edit-tool-damage-control.js', 'write-tool-damage-control.js'];
115
+ for (const script of scripts) {
116
+ const src = path.join(damageControlSource, script);
117
+ const dest = path.join(damageControlTarget, script);
118
+ if (fs.existsSync(src)) {
119
+ await fs.copy(src, dest);
120
+ }
121
+ }
122
+
123
+ // Copy patterns.yaml (preserve existing)
124
+ const patternsSource = path.join(damageControlSource, 'patterns.yaml');
125
+ const patternsTarget = path.join(damageControlTarget, 'patterns.yaml');
126
+ if (fs.existsSync(patternsSource) && !fs.existsSync(patternsTarget)) {
127
+ await fs.copy(patternsSource, patternsTarget);
128
+ console.log(chalk.dim(` - Damage control: patterns.yaml created`));
129
+ } else if (fs.existsSync(patternsTarget)) {
130
+ console.log(chalk.dim(` - Damage control: patterns.yaml preserved`));
131
+ }
132
+
133
+ // Setup hooks in settings.json (unless disabled)
134
+ if (!options.skipDamageControl) {
135
+ await this.setupDamageControlHooks(claudeDir);
136
+ console.log(chalk.dim(` - Damage control: hooks enabled`));
137
+ }
138
+ }
139
+
140
+ /**
141
+ * Add PreToolUse hooks to settings.json
142
+ * @param {string} claudeDir - .claude directory path
143
+ */
144
+ async setupDamageControlHooks(claudeDir) {
145
+ const settingsPath = path.join(claudeDir, 'settings.json');
146
+ let settings = {};
147
+
148
+ // Load existing settings
149
+ if (fs.existsSync(settingsPath)) {
150
+ try {
151
+ settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
152
+ } catch (e) {
153
+ settings = {};
154
+ }
155
+ }
156
+
157
+ // Initialize hooks structure
158
+ if (!settings.hooks) settings.hooks = {};
159
+ if (!settings.hooks.PreToolUse) settings.hooks.PreToolUse = [];
160
+
161
+ // Define damage control hooks
162
+ const damageControlHooks = [
163
+ {
164
+ matcher: 'Bash',
165
+ hooks: [{
166
+ type: 'command',
167
+ command: 'node $CLAUDE_PROJECT_DIR/.claude/hooks/damage-control/bash-tool-damage-control.js',
168
+ timeout: 5000
169
+ }]
170
+ },
171
+ {
172
+ matcher: 'Edit',
173
+ hooks: [{
174
+ type: 'command',
175
+ command: 'node $CLAUDE_PROJECT_DIR/.claude/hooks/damage-control/edit-tool-damage-control.js',
176
+ timeout: 5000
177
+ }]
178
+ },
179
+ {
180
+ matcher: 'Write',
181
+ hooks: [{
182
+ type: 'command',
183
+ command: 'node $CLAUDE_PROJECT_DIR/.claude/hooks/damage-control/write-tool-damage-control.js',
184
+ timeout: 5000
185
+ }]
186
+ }
187
+ ];
188
+
189
+ // Merge with existing hooks (don't duplicate)
190
+ for (const newHook of damageControlHooks) {
191
+ const existingIdx = settings.hooks.PreToolUse.findIndex(h => h.matcher === newHook.matcher);
192
+ if (existingIdx === -1) {
193
+ // No existing matcher, add new
194
+ settings.hooks.PreToolUse.push(newHook);
195
+ } else {
196
+ // Existing matcher, merge hooks array
197
+ const existing = settings.hooks.PreToolUse[existingIdx];
198
+ if (!existing.hooks) existing.hooks = [];
199
+
200
+ // Check if damage control hook already exists
201
+ const dcHook = newHook.hooks[0];
202
+ const hasDcHook = existing.hooks.some(h =>
203
+ h.type === 'command' && h.command && h.command.includes('damage-control')
204
+ );
205
+
206
+ if (!hasDcHook) {
207
+ // Add at beginning for priority
208
+ existing.hooks.unshift(dcHook);
209
+ }
210
+ }
211
+ }
212
+
213
+ // Write settings
214
+ await fs.writeFile(settingsPath, JSON.stringify(settings, null, 2));
215
+ }
89
216
  }
90
217
 
91
218
  module.exports = { ClaudeCodeSetup };
@@ -0,0 +1,66 @@
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 = options.statusPath || path.join(process.cwd(), 'docs', '09-agents', 'status.json');
13
+ this.data = null;
14
+ }
15
+
16
+ async load() {
17
+ if (!fs.existsSync(this.statusPath)) {
18
+ return false;
19
+ }
20
+
21
+ try {
22
+ const content = fs.readFileSync(this.statusPath, 'utf8');
23
+ this.data = JSON.parse(content);
24
+ return true;
25
+ } catch (err) {
26
+ return false;
27
+ }
28
+ }
29
+
30
+ getStories() {
31
+ if (!this.data) return [];
32
+ return Object.values(this.data).filter(
33
+ s => s && typeof s === 'object' && (s.id || s.story_id)
34
+ );
35
+ }
36
+
37
+ getStats() {
38
+ const stories = this.getStories();
39
+ return {
40
+ total: stories.length,
41
+ in_progress: stories.filter(s => ['in_progress', 'in-progress'].includes(s.status)).length,
42
+ blocked: stories.filter(s => s.status === 'blocked').length,
43
+ ready: stories.filter(s => s.status === 'ready').length,
44
+ completed: stories.filter(s => ['completed', 'done'].includes(s.status)).length,
45
+ };
46
+ }
47
+
48
+ getCompletionPercentage() {
49
+ const stats = this.getStats();
50
+ if (stats.total === 0) return 0;
51
+ return Math.round((stats.completed / stats.total) * 100);
52
+ }
53
+
54
+ getStoriesByStatus(status) {
55
+ const stories = this.getStories();
56
+ if (status === 'in_progress') {
57
+ return stories.filter(s => ['in_progress', 'in-progress'].includes(s.status));
58
+ }
59
+ if (status === 'completed') {
60
+ return stories.filter(s => ['completed', 'done'].includes(s.status));
61
+ }
62
+ return stories.filter(s => s.status === status);
63
+ }
64
+ }
65
+
66
+ module.exports = Dashboard;
@@ -0,0 +1,69 @@
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;
@@ -0,0 +1,16 @@
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
+ };