agileflow 2.78.0 → 2.80.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.
@@ -10,25 +10,13 @@
10
10
 
11
11
  const path = require('path');
12
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
- };
13
+ const { c: colors } = require('../../../lib/colors');
28
14
 
29
15
  function showBetaWarning() {
30
16
  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}`);
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
+ );
32
20
  console.log(`${colors.dim} Expect bugs and incomplete features${colors.reset}`);
33
21
  console.log('');
34
22
  }
@@ -91,13 +79,17 @@ async function showDashboard() {
91
79
  const status = await loadStatus();
92
80
 
93
81
  if (!status) {
94
- console.log(`${colors.dim} No status.json found. Run /agileflow:story to create stories.${colors.reset}`);
82
+ console.log(
83
+ `${colors.dim} No status.json found. Run /agileflow:story to create stories.${colors.reset}`
84
+ );
95
85
  console.log('');
96
86
  return;
97
87
  }
98
88
 
99
89
  // Count stories by status
100
- const stories = Object.values(status).filter(s => s && typeof s === 'object' && (s.id || s.story_id));
90
+ const stories = Object.values(status).filter(
91
+ s => s && typeof s === 'object' && (s.id || s.story_id)
92
+ );
101
93
  const counts = {
102
94
  in_progress: stories.filter(s => ['in_progress', 'in-progress'].includes(s.status)).length,
103
95
  blocked: stories.filter(s => s.status === 'blocked').length,
@@ -111,7 +103,9 @@ async function showDashboard() {
111
103
  // Summary
112
104
  console.log(`${colors.bold} Summary${colors.reset}`);
113
105
  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}`);
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
+ );
115
109
  console.log(` ${colors.dim}Completion: ${completionPct}%${colors.reset}`);
116
110
  console.log('');
117
111
 
@@ -140,7 +134,9 @@ async function showDashboard() {
140
134
  // Ready Stories (up to 5)
141
135
  const readyStories = stories.filter(s => s.status === 'ready').slice(0, 5);
142
136
  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}`);
137
+ console.log(
138
+ `${colors.bold} ${colors.cyan}Ready for Work${colors.reset} ${colors.dim}(showing ${readyStories.length} of ${counts.ready})${colors.reset}`
139
+ );
144
140
  console.log(` ────────────────────────────────────────────`);
145
141
  readyStories.forEach(story => {
146
142
  console.log(formatStory(story));
@@ -165,7 +161,9 @@ async function main() {
165
161
  console.log(' npx agileflow start Show dashboard');
166
162
  console.log(' npx agileflow start --help Show this help');
167
163
  console.log('');
168
- console.log(`${colors.dim}This is a beta feature. For stable commands, use Claude Code slash commands.${colors.reset}`);
164
+ console.log(
165
+ `${colors.dim}This is a beta feature. For stable commands, use Claude Code slash commands.${colors.reset}`
166
+ );
169
167
  return;
170
168
  }
171
169
 
@@ -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,143 @@ 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 = [
115
+ 'bash-tool-damage-control.js',
116
+ 'edit-tool-damage-control.js',
117
+ 'write-tool-damage-control.js',
118
+ ];
119
+ for (const script of scripts) {
120
+ const src = path.join(damageControlSource, script);
121
+ const dest = path.join(damageControlTarget, script);
122
+ if (fs.existsSync(src)) {
123
+ await fs.copy(src, dest);
124
+ }
125
+ }
126
+
127
+ // Copy patterns.yaml (preserve existing)
128
+ const patternsSource = path.join(damageControlSource, 'patterns.yaml');
129
+ const patternsTarget = path.join(damageControlTarget, 'patterns.yaml');
130
+ if (fs.existsSync(patternsSource) && !fs.existsSync(patternsTarget)) {
131
+ await fs.copy(patternsSource, patternsTarget);
132
+ console.log(chalk.dim(` - Damage control: patterns.yaml created`));
133
+ } else if (fs.existsSync(patternsTarget)) {
134
+ console.log(chalk.dim(` - Damage control: patterns.yaml preserved`));
135
+ }
136
+
137
+ // Setup hooks in settings.json (unless disabled)
138
+ if (!options.skipDamageControl) {
139
+ await this.setupDamageControlHooks(claudeDir);
140
+ console.log(chalk.dim(` - Damage control: hooks enabled`));
141
+ }
142
+ }
143
+
144
+ /**
145
+ * Add PreToolUse hooks to settings.json
146
+ * @param {string} claudeDir - .claude directory path
147
+ */
148
+ async setupDamageControlHooks(claudeDir) {
149
+ const settingsPath = path.join(claudeDir, 'settings.json');
150
+ let settings = {};
151
+
152
+ // Load existing settings
153
+ if (fs.existsSync(settingsPath)) {
154
+ try {
155
+ settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
156
+ } catch (e) {
157
+ settings = {};
158
+ }
159
+ }
160
+
161
+ // Initialize hooks structure
162
+ if (!settings.hooks) settings.hooks = {};
163
+ if (!settings.hooks.PreToolUse) settings.hooks.PreToolUse = [];
164
+
165
+ // Define damage control hooks
166
+ const damageControlHooks = [
167
+ {
168
+ matcher: 'Bash',
169
+ hooks: [
170
+ {
171
+ type: 'command',
172
+ command:
173
+ 'node $CLAUDE_PROJECT_DIR/.claude/hooks/damage-control/bash-tool-damage-control.js',
174
+ timeout: 5000,
175
+ },
176
+ ],
177
+ },
178
+ {
179
+ matcher: 'Edit',
180
+ hooks: [
181
+ {
182
+ type: 'command',
183
+ command:
184
+ 'node $CLAUDE_PROJECT_DIR/.claude/hooks/damage-control/edit-tool-damage-control.js',
185
+ timeout: 5000,
186
+ },
187
+ ],
188
+ },
189
+ {
190
+ matcher: 'Write',
191
+ hooks: [
192
+ {
193
+ type: 'command',
194
+ command:
195
+ 'node $CLAUDE_PROJECT_DIR/.claude/hooks/damage-control/write-tool-damage-control.js',
196
+ timeout: 5000,
197
+ },
198
+ ],
199
+ },
200
+ ];
201
+
202
+ // Merge with existing hooks (don't duplicate)
203
+ for (const newHook of damageControlHooks) {
204
+ const existingIdx = settings.hooks.PreToolUse.findIndex(h => h.matcher === newHook.matcher);
205
+ if (existingIdx === -1) {
206
+ // No existing matcher, add new
207
+ settings.hooks.PreToolUse.push(newHook);
208
+ } else {
209
+ // Existing matcher, merge hooks array
210
+ const existing = settings.hooks.PreToolUse[existingIdx];
211
+ if (!existing.hooks) existing.hooks = [];
212
+
213
+ // Check if damage control hook already exists
214
+ const dcHook = newHook.hooks[0];
215
+ const hasDcHook = existing.hooks.some(
216
+ h => h.type === 'command' && h.command && h.command.includes('damage-control')
217
+ );
218
+
219
+ if (!hasDcHook) {
220
+ // Add at beginning for priority
221
+ existing.hooks.unshift(dcHook);
222
+ }
223
+ }
224
+ }
225
+
226
+ // Write settings
227
+ await fs.writeFile(settingsPath, JSON.stringify(settings, null, 2));
228
+ }
89
229
  }
90
230
 
91
231
  module.exports = { ClaudeCodeSetup };
@@ -9,7 +9,8 @@ const fs = require('fs');
9
9
 
10
10
  class Dashboard {
11
11
  constructor(options = {}) {
12
- this.statusPath = options.statusPath || path.join(process.cwd(), 'docs', '09-agents', 'status.json');
12
+ this.statusPath =
13
+ options.statusPath || path.join(process.cwd(), 'docs', '09-agents', 'status.json');
13
14
  this.data = null;
14
15
  }
15
16
 
@@ -29,9 +30,7 @@ class Dashboard {
29
30
 
30
31
  getStories() {
31
32
  if (!this.data) return [];
32
- return Object.values(this.data).filter(
33
- s => s && typeof s === 'object' && (s.id || s.story_id)
34
- );
33
+ return Object.values(this.data).filter(s => s && typeof s === 'object' && (s.id || s.story_id));
35
34
  }
36
35
 
37
36
  getStats() {
@@ -10,15 +10,7 @@
10
10
  const { execSync } = require('node:child_process');
11
11
  const path = require('node:path');
12
12
  const fs = require('node:fs');
13
-
14
- // ANSI color codes for terminal output
15
- const colors = {
16
- green: '\x1b[32m',
17
- yellow: '\x1b[33m',
18
- blue: '\x1b[36m',
19
- dim: '\x1b[2m',
20
- reset: '\x1b[0m',
21
- };
13
+ const { c: colors } = require('../lib/colors');
22
14
 
23
15
  function log(message, color = 'reset') {
24
16
  console.log(`${colors[color]}${message}${colors.reset}`);