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.
- package/README.md +6 -6
- package/package.json +6 -1
- package/scripts/agileflow-configure.js +174 -2
- package/scripts/agileflow-statusline.sh +171 -78
- package/scripts/agileflow-welcome.js +88 -64
- package/scripts/auto-self-improve.js +23 -45
- package/scripts/check-update.js +35 -42
- package/scripts/damage-control/bash-tool-damage-control.js +257 -0
- package/scripts/damage-control/edit-tool-damage-control.js +279 -0
- package/scripts/damage-control/patterns.yaml +227 -0
- package/scripts/damage-control/write-tool-damage-control.js +274 -0
- package/scripts/damage-control-bash.js +232 -0
- package/scripts/damage-control-edit.js +243 -0
- package/scripts/damage-control-write.js +243 -0
- package/scripts/obtain-context.js +22 -3
- package/scripts/ralph-loop.js +191 -63
- package/scripts/screenshot-verifier.js +213 -0
- package/scripts/session-manager.js +12 -33
- package/src/core/agents/accessibility.md +124 -53
- package/src/core/agents/adr-writer.md +192 -52
- package/src/core/agents/analytics.md +139 -60
- package/src/core/agents/api.md +173 -63
- package/src/core/agents/ci.md +139 -57
- package/src/core/agents/compliance.md +159 -68
- package/src/core/agents/configuration/damage-control.md +356 -0
- package/src/core/agents/configuration-damage-control.md +248 -0
- package/src/core/agents/database.md +162 -61
- package/src/core/agents/datamigration.md +179 -66
- package/src/core/agents/design.md +179 -57
- package/src/core/agents/devops.md +160 -3
- package/src/core/agents/documentation.md +204 -60
- package/src/core/agents/epic-planner.md +147 -55
- package/src/core/agents/integrations.md +197 -69
- package/src/core/agents/mentor.md +158 -57
- package/src/core/agents/mobile.md +159 -67
- package/src/core/agents/monitoring.md +154 -65
- package/src/core/agents/multi-expert.md +115 -43
- package/src/core/agents/orchestrator.md +77 -24
- package/src/core/agents/performance.md +130 -75
- package/src/core/agents/product.md +151 -55
- package/src/core/agents/qa.md +162 -74
- package/src/core/agents/readme-updater.md +178 -76
- package/src/core/agents/refactor.md +148 -95
- package/src/core/agents/research.md +143 -72
- package/src/core/agents/security.md +154 -65
- package/src/core/agents/testing.md +176 -97
- package/src/core/agents/ui.md +170 -79
- package/src/core/commands/adr/list.md +171 -0
- package/src/core/commands/adr/update.md +235 -0
- package/src/core/commands/adr/view.md +252 -0
- package/src/core/commands/adr.md +207 -50
- package/src/core/commands/agent.md +16 -0
- package/src/core/commands/assign.md +148 -44
- package/src/core/commands/auto.md +18 -1
- package/src/core/commands/babysit.md +391 -38
- package/src/core/commands/baseline.md +14 -0
- package/src/core/commands/blockers.md +170 -51
- package/src/core/commands/board.md +144 -66
- package/src/core/commands/changelog.md +15 -0
- package/src/core/commands/ci.md +179 -69
- package/src/core/commands/compress.md +18 -0
- package/src/core/commands/configure.md +16 -0
- package/src/core/commands/context/export.md +193 -4
- package/src/core/commands/context/full.md +191 -18
- package/src/core/commands/context/note.md +248 -4
- package/src/core/commands/debt.md +17 -0
- package/src/core/commands/deploy.md +208 -65
- package/src/core/commands/deps.md +15 -0
- package/src/core/commands/diagnose.md +16 -0
- package/src/core/commands/docs.md +196 -64
- package/src/core/commands/epic/list.md +170 -0
- package/src/core/commands/epic/view.md +242 -0
- package/src/core/commands/epic.md +192 -69
- package/src/core/commands/feedback.md +191 -71
- package/src/core/commands/handoff.md +162 -48
- package/src/core/commands/help.md +9 -0
- package/src/core/commands/ideate.md +446 -0
- package/src/core/commands/impact.md +16 -0
- package/src/core/commands/metrics.md +141 -37
- package/src/core/commands/multi-expert.md +77 -0
- package/src/core/commands/packages.md +16 -0
- package/src/core/commands/pr.md +161 -67
- package/src/core/commands/readme-sync.md +16 -0
- package/src/core/commands/research/analyze.md +568 -0
- package/src/core/commands/research/ask.md +345 -20
- package/src/core/commands/research/import.md +562 -19
- package/src/core/commands/research/list.md +173 -5
- package/src/core/commands/research/view.md +181 -8
- package/src/core/commands/retro.md +135 -48
- package/src/core/commands/review.md +219 -47
- package/src/core/commands/session/end.md +209 -0
- package/src/core/commands/session/history.md +210 -0
- package/src/core/commands/session/init.md +116 -0
- package/src/core/commands/session/new.md +296 -0
- package/src/core/commands/session/resume.md +166 -0
- package/src/core/commands/session/status.md +166 -0
- package/src/core/commands/setup/visual-e2e.md +462 -0
- package/src/core/commands/skill/create.md +115 -17
- package/src/core/commands/skill/delete.md +117 -0
- package/src/core/commands/skill/edit.md +104 -0
- package/src/core/commands/skill/list.md +128 -0
- package/src/core/commands/skill/test.md +135 -0
- package/src/core/commands/skill/upgrade.md +542 -0
- package/src/core/commands/sprint.md +17 -1
- package/src/core/commands/status.md +133 -21
- package/src/core/commands/story/list.md +176 -0
- package/src/core/commands/story/view.md +265 -0
- package/src/core/commands/story-validate.md +101 -1
- package/src/core/commands/story.md +204 -51
- package/src/core/commands/template.md +16 -1
- package/src/core/commands/tests.md +226 -64
- package/src/core/commands/update.md +17 -1
- package/src/core/commands/validate-expertise.md +16 -0
- package/src/core/commands/velocity.md +140 -36
- package/src/core/commands/verify.md +14 -0
- package/src/core/commands/whats-new.md +30 -0
- package/src/core/skills/_learnings/README.md +91 -0
- package/src/core/skills/_learnings/_template.yaml +106 -0
- package/src/core/skills/_learnings/code-review.yaml +118 -0
- package/src/core/skills/_learnings/commit.yaml +69 -0
- package/src/core/skills/_learnings/story-writer.yaml +71 -0
- package/src/core/templates/damage-control-patterns.yaml +234 -0
- package/src/core/templates/skill-template.md +53 -11
- package/tools/cli/commands/start.js +180 -0
- package/tools/cli/installers/ide/claude-code.js +127 -0
- package/tools/cli/tui/Dashboard.js +66 -0
- package/tools/cli/tui/StoryList.js +69 -0
- 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. **
|
|
31
|
-
-
|
|
32
|
-
-
|
|
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. **
|
|
35
|
-
-
|
|
36
|
-
-
|
|
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
|
-
|
|
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
|
-
- [ ]
|
|
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
|
-
-
|
|
74
|
-
-
|
|
75
|
-
-
|
|
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
|
+
};
|