agileflow 2.78.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.
@@ -0,0 +1,213 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * screenshot-verifier.js - Verify all screenshots have been reviewed
5
+ *
6
+ * Part of Visual Mode for UI development. This script checks that all
7
+ * screenshots in a directory have been prefixed with "verified-" to
8
+ * confirm Claude has visually reviewed each one.
9
+ *
10
+ * Usage:
11
+ * node scripts/screenshot-verifier.js # Default: ./screenshots
12
+ * node scripts/screenshot-verifier.js --path ./e2e/shots # Custom path
13
+ * node scripts/screenshot-verifier.js --help # Show help
14
+ *
15
+ * Exit codes:
16
+ * 0 - All screenshots verified (or no screenshots found)
17
+ * 1 - Some screenshots missing verified- prefix
18
+ * 2 - Error (directory not found, etc.)
19
+ */
20
+
21
+ const fs = require('fs');
22
+ const path = require('path');
23
+
24
+ // Shared utilities
25
+ const { c } = require('../lib/colors');
26
+
27
+ // Parse command line arguments
28
+ function parseArgs() {
29
+ const args = process.argv.slice(2);
30
+ const options = {
31
+ path: './screenshots',
32
+ help: false,
33
+ quiet: false,
34
+ };
35
+
36
+ for (let i = 0; i < args.length; i++) {
37
+ const arg = args[i];
38
+
39
+ if (arg === '--help' || arg === '-h') {
40
+ options.help = true;
41
+ } else if (arg === '--quiet' || arg === '-q') {
42
+ options.quiet = true;
43
+ } else if (arg === '--path' || arg === '-p') {
44
+ if (args[i + 1]) {
45
+ options.path = args[i + 1];
46
+ i++;
47
+ }
48
+ } else if (!arg.startsWith('-')) {
49
+ // Positional argument - treat as path
50
+ options.path = arg;
51
+ }
52
+ }
53
+
54
+ return options;
55
+ }
56
+
57
+ // Show help message
58
+ function showHelp() {
59
+ console.log(`
60
+ ${c.brand}${c.bold}screenshot-verifier${c.reset} - Verify all screenshots have been reviewed
61
+
62
+ ${c.bold}USAGE${c.reset}
63
+ node screenshot-verifier.js [OPTIONS] [PATH]
64
+
65
+ ${c.bold}OPTIONS${c.reset}
66
+ --path, -p <dir> Directory containing screenshots (default: ./screenshots)
67
+ --quiet, -q Only output on failure
68
+ --help, -h Show this help message
69
+
70
+ ${c.bold}EXAMPLES${c.reset}
71
+ node screenshot-verifier.js # Check ./screenshots
72
+ node screenshot-verifier.js ./tests/e2e/shots # Check custom path
73
+ node screenshot-verifier.js --path ./shots -q # Quiet mode
74
+
75
+ ${c.bold}EXIT CODES${c.reset}
76
+ 0 - All screenshots verified (or no screenshots found)
77
+ 1 - Some screenshots missing 'verified-' prefix
78
+ 2 - Error (directory not found, etc.)
79
+
80
+ ${c.bold}VISUAL MODE${c.reset}
81
+ This script is part of AgileFlow's Visual Mode for UI development.
82
+
83
+ Workflow:
84
+ 1. Playwright tests create screenshots in the target directory
85
+ 2. Claude reviews each screenshot visually
86
+ 3. Claude renames verified screenshots with 'verified-' prefix
87
+ 4. This script confirms all screenshots have been reviewed
88
+
89
+ The 'verified-' prefix ensures Claude actually looked at each image
90
+ rather than declaring completion prematurely.
91
+ `);
92
+ }
93
+
94
+ // Get all image files in directory
95
+ function getImageFiles(dir) {
96
+ const imageExtensions = ['.png', '.jpg', '.jpeg', '.webp', '.gif'];
97
+
98
+ try {
99
+ const files = fs.readdirSync(dir);
100
+ return files.filter((file) => {
101
+ const ext = path.extname(file).toLowerCase();
102
+ return imageExtensions.includes(ext);
103
+ });
104
+ } catch (err) {
105
+ if (err.code === 'ENOENT') {
106
+ return null; // Directory doesn't exist
107
+ }
108
+ throw err;
109
+ }
110
+ }
111
+
112
+ // Main verification logic
113
+ function verifyScreenshots(options) {
114
+ const dir = path.resolve(options.path);
115
+
116
+ // Check if directory exists
117
+ if (!fs.existsSync(dir)) {
118
+ if (!options.quiet) {
119
+ console.log(`${c.yellow}No screenshots directory found at: ${dir}${c.reset}`);
120
+ console.log(`${c.dim}Create the directory or run tests to generate screenshots.${c.reset}`);
121
+ }
122
+ return { success: true, total: 0, verified: 0, unverified: [] };
123
+ }
124
+
125
+ // Get all image files
126
+ const files = getImageFiles(dir);
127
+
128
+ if (files === null) {
129
+ console.error(`${c.red}Error: Could not read directory: ${dir}${c.reset}`);
130
+ process.exit(2);
131
+ }
132
+
133
+ if (files.length === 0) {
134
+ if (!options.quiet) {
135
+ console.log(`${c.yellow}No screenshots found in: ${dir}${c.reset}`);
136
+ }
137
+ return { success: true, total: 0, verified: 0, unverified: [] };
138
+ }
139
+
140
+ // Check each file for verified- prefix
141
+ const verified = [];
142
+ const unverified = [];
143
+
144
+ for (const file of files) {
145
+ if (file.startsWith('verified-')) {
146
+ verified.push(file);
147
+ } else {
148
+ unverified.push(file);
149
+ }
150
+ }
151
+
152
+ return {
153
+ success: unverified.length === 0,
154
+ total: files.length,
155
+ verified: verified.length,
156
+ unverified,
157
+ };
158
+ }
159
+
160
+ // Format result output
161
+ function formatResult(result, options) {
162
+ if (options.quiet && result.success) {
163
+ return;
164
+ }
165
+
166
+ console.log('');
167
+
168
+ if (result.total === 0) {
169
+ console.log(`${c.yellow}No screenshots to verify${c.reset}`);
170
+ return;
171
+ }
172
+
173
+ if (result.success) {
174
+ console.log(`${c.green}${c.bold}All screenshots verified${c.reset}`);
175
+ console.log(`${c.dim}${result.verified}/${result.total} screenshots have 'verified-' prefix${c.reset}`);
176
+ } else {
177
+ console.log(`${c.red}${c.bold}Unverified screenshots found${c.reset}`);
178
+ console.log(`${c.dim}${result.verified}/${result.total} verified${c.reset}`);
179
+ console.log('');
180
+ console.log(`${c.yellow}Missing 'verified-' prefix:${c.reset}`);
181
+ for (const file of result.unverified) {
182
+ console.log(` ${c.red}- ${file}${c.reset}`);
183
+ }
184
+ console.log('');
185
+ console.log(`${c.cyan}To verify: Review each screenshot visually, then rename:${c.reset}`);
186
+ console.log(`${c.dim} mv screenshots/example.png screenshots/verified-example.png${c.reset}`);
187
+ }
188
+
189
+ console.log('');
190
+ }
191
+
192
+ // Main entry point
193
+ function main() {
194
+ const options = parseArgs();
195
+
196
+ if (options.help) {
197
+ showHelp();
198
+ process.exit(0);
199
+ }
200
+
201
+ const result = verifyScreenshots(options);
202
+ formatResult(result, options);
203
+
204
+ process.exit(result.success ? 0 : 1);
205
+ }
206
+
207
+ // Run if called directly
208
+ if (require.main === module) {
209
+ main();
210
+ }
211
+
212
+ // Export for testing
213
+ module.exports = { verifyScreenshots, getImageFiles };
@@ -13,30 +13,10 @@ const fs = require('fs');
13
13
  const path = require('path');
14
14
  const { execSync, spawnSync } = require('child_process');
15
15
 
16
- // ANSI colors
17
- const c = {
18
- reset: '\x1b[0m',
19
- bold: '\x1b[1m',
20
- dim: '\x1b[2m',
21
- red: '\x1b[31m',
22
- green: '\x1b[32m',
23
- yellow: '\x1b[33m',
24
- blue: '\x1b[34m',
25
- cyan: '\x1b[36m',
26
- brand: '\x1b[38;2;232;104;58m',
27
- };
28
-
29
- // Find project root (has .agileflow or .git)
30
- function getProjectRoot() {
31
- let dir = process.cwd();
32
- while (dir !== '/') {
33
- if (fs.existsSync(path.join(dir, '.agileflow')) || fs.existsSync(path.join(dir, '.git'))) {
34
- return dir;
35
- }
36
- dir = path.dirname(dir);
37
- }
38
- return process.cwd();
39
- }
16
+ // Shared utilities
17
+ const { c } = require('../lib/colors');
18
+ const { getProjectRoot } = require('../lib/paths');
19
+ const { safeReadJSON } = require('../lib/errors');
40
20
 
41
21
  const ROOT = getProjectRoot();
42
22
  const SESSIONS_DIR = path.join(ROOT, '.agileflow', 'sessions');
@@ -162,17 +142,16 @@ function getCurrentBranch() {
162
142
 
163
143
  // Get current story from status.json
164
144
  function getCurrentStory() {
165
- try {
166
- const statusPath = path.join(ROOT, 'docs', '09-agents', 'status.json');
167
- if (!fs.existsSync(statusPath)) return null;
145
+ const statusPath = path.join(ROOT, 'docs', '09-agents', 'status.json');
146
+ const result = safeReadJSON(statusPath, { defaultValue: null });
168
147
 
169
- const status = JSON.parse(fs.readFileSync(statusPath, 'utf8'));
170
- for (const [id, story] of Object.entries(status.stories || {})) {
171
- if (story.status === 'in_progress') {
172
- return { id, title: story.title };
173
- }
148
+ if (!result.ok || !result.data) return null;
149
+
150
+ for (const [id, story] of Object.entries(result.data.stories || {})) {
151
+ if (story.status === 'in_progress') {
152
+ return { id, title: story.title };
174
153
  }
175
- } catch (e) {}
154
+ }
176
155
  return null;
177
156
  }
178
157
 
@@ -0,0 +1,248 @@
1
+ ---
2
+ name: configuration-damage-control
3
+ description: Configure AgileFlow damage control to protect against destructive commands
4
+ tools: Read, Write, Edit, Bash, Glob, Grep
5
+ model: haiku
6
+ compact_context:
7
+ priority: high
8
+ preserve_rules:
9
+ - "Use AskUserQuestion for all configuration choices"
10
+ - "Copy hook scripts to .claude/hooks/damage-control/"
11
+ - "Create patterns.yaml if not exists, PRESERVE if exists"
12
+ - "Write PreToolUse hooks to .claude/settings.json"
13
+ - "Never overwrite existing patterns without confirmation"
14
+ state_fields:
15
+ - damage_control_enabled
16
+ - protection_level
17
+ ---
18
+
19
+ # Configuration: Damage Control
20
+
21
+ Set up damage control protection to block destructive commands and protect sensitive paths.
22
+
23
+ ---
24
+
25
+ ## What This Does
26
+
27
+ Damage control protects your codebase from destructive agent commands through PreToolUse hooks:
28
+
29
+ 1. **Bash Command Validation** - Blocks dangerous commands like `rm -rf`, `DROP TABLE`, force pushes
30
+ 2. **Path Protection** - Prevents access to sensitive files (`.env`, `~/.ssh/`, etc.)
31
+ 3. **Ask Confirmation** - Prompts before risky-but-valid operations
32
+
33
+ ---
34
+
35
+ ## Configuration Steps
36
+
37
+ ### Step 1: Ask User to Enable
38
+
39
+ ```xml
40
+ <invoke name="AskUserQuestion">
41
+ <parameter name="questions">[{
42
+ "question": "Enable damage control to protect against destructive commands?",
43
+ "header": "Damage Control",
44
+ "multiSelect": false,
45
+ "options": [
46
+ {"label": "Enable (Recommended)", "description": "Block dangerous commands and protect sensitive paths"},
47
+ {"label": "Skip", "description": "No damage control (not recommended)"}
48
+ ]
49
+ }]</parameter>
50
+ </invoke>
51
+ ```
52
+
53
+ If user selects "Skip", exit with message: "Damage control not enabled. Run /agileflow:configure to enable later."
54
+
55
+ ### Step 2: Ask Protection Level
56
+
57
+ ```xml
58
+ <invoke name="AskUserQuestion">
59
+ <parameter name="questions">[{
60
+ "question": "Choose protection level:",
61
+ "header": "Protection Level",
62
+ "multiSelect": false,
63
+ "options": [
64
+ {"label": "Standard (Recommended)", "description": "Deterministic pattern matching - fast, no AI calls"},
65
+ {"label": "Enhanced", "description": "Standard + AI prompt hook for unknown threats (slower)"}
66
+ ]
67
+ }]</parameter>
68
+ </invoke>
69
+ ```
70
+
71
+ ### Step 3: Create Hooks Directory
72
+
73
+ ```bash
74
+ mkdir -p .claude/hooks/damage-control
75
+ ```
76
+
77
+ ### Step 4: Copy Hook Scripts
78
+
79
+ Copy the following scripts from AgileFlow installation:
80
+
81
+ ```bash
82
+ # Source: .agileflow/scripts/damage-control/
83
+ # Destination: .claude/hooks/damage-control/
84
+
85
+ cp .agileflow/scripts/damage-control/bash-tool-damage-control.js .claude/hooks/damage-control/
86
+ cp .agileflow/scripts/damage-control/edit-tool-damage-control.js .claude/hooks/damage-control/
87
+ cp .agileflow/scripts/damage-control/write-tool-damage-control.js .claude/hooks/damage-control/
88
+ ```
89
+
90
+ ### Step 5: Create or Preserve patterns.yaml
91
+
92
+ **If patterns.yaml does NOT exist**, copy the default:
93
+
94
+ ```bash
95
+ cp .agileflow/scripts/damage-control/patterns.yaml .claude/hooks/damage-control/
96
+ ```
97
+
98
+ **If patterns.yaml ALREADY exists**, preserve it (do not overwrite):
99
+
100
+ ```
101
+ patterns.yaml already exists - preserving existing rules.
102
+ To update patterns, edit .claude/hooks/damage-control/patterns.yaml
103
+ ```
104
+
105
+ ### Step 6: Update settings.json
106
+
107
+ Add PreToolUse hooks to `.claude/settings.json`:
108
+
109
+ **For Standard protection:**
110
+
111
+ ```json
112
+ {
113
+ "hooks": {
114
+ "PreToolUse": [
115
+ {
116
+ "matcher": "Bash",
117
+ "hooks": [{
118
+ "type": "command",
119
+ "command": "node $CLAUDE_PROJECT_DIR/.claude/hooks/damage-control/bash-tool-damage-control.js",
120
+ "timeout": 5000
121
+ }]
122
+ },
123
+ {
124
+ "matcher": "Edit",
125
+ "hooks": [{
126
+ "type": "command",
127
+ "command": "node $CLAUDE_PROJECT_DIR/.claude/hooks/damage-control/edit-tool-damage-control.js",
128
+ "timeout": 5000
129
+ }]
130
+ },
131
+ {
132
+ "matcher": "Write",
133
+ "hooks": [{
134
+ "type": "command",
135
+ "command": "node $CLAUDE_PROJECT_DIR/.claude/hooks/damage-control/write-tool-damage-control.js",
136
+ "timeout": 5000
137
+ }]
138
+ }
139
+ ]
140
+ }
141
+ }
142
+ ```
143
+
144
+ **For Enhanced protection (adds prompt hook):**
145
+
146
+ Add to the Bash matcher:
147
+
148
+ ```json
149
+ {
150
+ "matcher": "Bash",
151
+ "hooks": [
152
+ {
153
+ "type": "command",
154
+ "command": "node $CLAUDE_PROJECT_DIR/.claude/hooks/damage-control/bash-tool-damage-control.js",
155
+ "timeout": 5000
156
+ },
157
+ {
158
+ "type": "prompt",
159
+ "prompt": "Evaluate if this bash command is destructive or could cause irreversible damage. Consider: Does it delete files recursively? Does it modify system files? Could it expose secrets? Block if dangerous."
160
+ }
161
+ ]
162
+ }
163
+ ```
164
+
165
+ ### Step 7: Merge with Existing Hooks
166
+
167
+ **IMPORTANT**: If PreToolUse hooks already exist in settings.json, MERGE the new hooks with existing ones. Do NOT replace existing hooks.
168
+
169
+ Check for existing hooks:
170
+ ```javascript
171
+ // If settings.hooks.PreToolUse exists, append to it
172
+ // If a matcher (Bash, Edit, Write) already exists, merge hooks array
173
+ ```
174
+
175
+ ### Step 8: Show Completion Summary
176
+
177
+ ```
178
+ Damage Control Enabled
179
+
180
+ Protection level: Standard (or Enhanced)
181
+
182
+ Protected against:
183
+ - Destructive bash commands (rm -rf, DROP TABLE, etc.)
184
+ - Access to sensitive paths (~/.ssh, .env, etc.)
185
+ - Force pushes and hard resets
186
+
187
+ Configuration:
188
+ - Hook scripts: .claude/hooks/damage-control/
189
+ - Patterns file: .claude/hooks/damage-control/patterns.yaml
190
+ - Settings: .claude/settings.json
191
+
192
+ To customize blocked patterns, edit:
193
+ .claude/hooks/damage-control/patterns.yaml
194
+
195
+ Restart Claude Code for hooks to take effect.
196
+ ```
197
+
198
+ ---
199
+
200
+ ## Patterns.yaml Reference
201
+
202
+ ```yaml
203
+ # Block dangerous bash commands
204
+ bashToolPatterns:
205
+ - pattern: '\brm\s+-[rRf]'
206
+ reason: "rm with recursive or force flags"
207
+
208
+ # Commands requiring confirmation
209
+ askPatterns:
210
+ - pattern: 'git\s+push\s+.*--force'
211
+ reason: "Force push overwrites history"
212
+
213
+ # Path protection levels
214
+ zeroAccessPaths: # Cannot read, write, edit, delete
215
+ - ~/.ssh/
216
+ - .env
217
+
218
+ readOnlyPaths: # Can read, cannot modify
219
+ - /etc/
220
+ - package-lock.json
221
+
222
+ noDeletePaths: # Can modify, cannot delete
223
+ - .agileflow/
224
+ - .claude/
225
+ ```
226
+
227
+ ---
228
+
229
+ ## Troubleshooting
230
+
231
+ **Hooks not working after enabling:**
232
+ - Restart Claude Code - hooks only load on startup
233
+
234
+ **Command blocked that should be allowed:**
235
+ - Edit patterns.yaml to remove or adjust the pattern
236
+ - Use `ask: true` instead of blocking
237
+
238
+ **Need to disable damage control:**
239
+ - Remove PreToolUse hooks from .claude/settings.json
240
+ - Or delete .claude/hooks/damage-control/ directory
241
+
242
+ ---
243
+
244
+ ## Related
245
+
246
+ - Research: `docs/10-research/20260106-claude-code-damage-control-hooks.md`
247
+ - Patterns file: `.claude/hooks/damage-control/patterns.yaml`
248
+ - Hook scripts: `.claude/hooks/damage-control/*.js`
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  description: Interactive mentor for end-to-end feature implementation
3
- argument-hint: "[EPIC=<id>] [MODE=loop] [MAX=<iterations>]"
3
+ argument-hint: "[EPIC=<id>] [MODE=loop] [MAX=<iterations>] [VISUAL=true]"
4
4
  compact_context:
5
5
  priority: critical
6
6
  preserve_rules:
@@ -61,6 +61,7 @@ When invoked with `MODE=loop`, babysit runs autonomously through an epic's stori
61
61
  | `EPIC` | Yes | Epic ID to process (e.g., EP-0042) |
62
62
  | `MODE` | Yes | Must be `loop` for autonomous mode |
63
63
  | `MAX` | No | Max iterations (default: 20) |
64
+ | `VISUAL` | No | Enable Visual Mode for UI development (screenshot verification) |
64
65
 
65
66
  ### To Start Loop Mode
66
67
 
@@ -69,6 +70,9 @@ After running the context script, if EPIC and MODE=loop are specified:
69
70
  ```bash
70
71
  # Initialize the loop
71
72
  node scripts/ralph-loop.js --init --epic=EP-0042 --max=20
73
+
74
+ # With Visual Mode for UI development
75
+ node scripts/ralph-loop.js --init --epic=EP-0042 --max=20 --visual
72
76
  ```
73
77
 
74
78
  Or manually write to session-state.json:
@@ -80,11 +84,35 @@ Or manually write to session-state.json:
80
84
  "epic": "EP-0042",
81
85
  "current_story": "US-0015",
82
86
  "iteration": 0,
83
- "max_iterations": 20
87
+ "max_iterations": 20,
88
+ "visual_mode": false,
89
+ "screenshots_verified": false
84
90
  }
85
91
  }
86
92
  ```
87
93
 
94
+ ### Visual Mode
95
+
96
+ When `VISUAL=true` is specified, the loop adds screenshot verification:
97
+
98
+ ```
99
+ /agileflow:babysit EPIC=EP-0042 MODE=loop VISUAL=true
100
+ ```
101
+
102
+ **Visual Mode behavior:**
103
+ 1. After tests pass, runs `screenshot-verifier.js`
104
+ 2. Checks all screenshots in `screenshots/` have `verified-` prefix
105
+ 3. Requires minimum 2 iterations before completion
106
+ 4. Prevents premature completion for UI work
107
+
108
+ **When to use Visual Mode:**
109
+ - UI-focused epics (components, styling, layouts)
110
+ - Shadcn/UI development
111
+ - Any work where visual appearance matters
112
+
113
+ **Setup requirement:**
114
+ Run `/agileflow:setup:visual-e2e` first to install Playwright and create e2e tests.
115
+
88
116
  ### Loop Control Commands
89
117
 
90
118
  ```bash