agileflow 2.94.1 → 2.95.1

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 (74) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +3 -3
  3. package/lib/colors.generated.js +117 -0
  4. package/lib/colors.js +59 -109
  5. package/lib/generator-factory.js +333 -0
  6. package/lib/path-utils.js +49 -0
  7. package/lib/session-registry.js +25 -15
  8. package/lib/smart-json-file.js +40 -32
  9. package/lib/state-machine.js +286 -0
  10. package/package.json +1 -1
  11. package/scripts/agileflow-configure.js +7 -6
  12. package/scripts/archive-completed-stories.sh +86 -11
  13. package/scripts/babysit-context-restore.js +89 -0
  14. package/scripts/claude-tmux.sh +111 -5
  15. package/scripts/damage-control/bash-tool-damage-control.js +11 -247
  16. package/scripts/damage-control/edit-tool-damage-control.js +9 -249
  17. package/scripts/damage-control/write-tool-damage-control.js +9 -244
  18. package/scripts/generate-colors.js +314 -0
  19. package/scripts/lib/colors.generated.sh +82 -0
  20. package/scripts/lib/colors.sh +10 -70
  21. package/scripts/lib/configure-features.js +401 -0
  22. package/scripts/lib/context-loader.js +181 -52
  23. package/scripts/precompact-context.sh +54 -17
  24. package/scripts/session-coordinator.sh +2 -2
  25. package/scripts/session-manager.js +653 -10
  26. package/src/core/commands/audit.md +93 -0
  27. package/src/core/commands/auto.md +73 -0
  28. package/src/core/commands/babysit.md +169 -13
  29. package/src/core/commands/baseline.md +73 -0
  30. package/src/core/commands/batch.md +64 -0
  31. package/src/core/commands/blockers.md +60 -0
  32. package/src/core/commands/board.md +66 -0
  33. package/src/core/commands/choose.md +77 -0
  34. package/src/core/commands/ci.md +77 -0
  35. package/src/core/commands/compress.md +27 -1
  36. package/src/core/commands/configure.md +126 -10
  37. package/src/core/commands/council.md +74 -0
  38. package/src/core/commands/debt.md +72 -0
  39. package/src/core/commands/deploy.md +73 -0
  40. package/src/core/commands/deps.md +68 -0
  41. package/src/core/commands/docs.md +60 -0
  42. package/src/core/commands/feedback.md +68 -0
  43. package/src/core/commands/ideate.md +74 -0
  44. package/src/core/commands/impact.md +74 -0
  45. package/src/core/commands/install.md +529 -0
  46. package/src/core/commands/maintain.md +558 -0
  47. package/src/core/commands/metrics.md +75 -0
  48. package/src/core/commands/multi-expert.md +74 -0
  49. package/src/core/commands/packages.md +69 -0
  50. package/src/core/commands/readme-sync.md +64 -0
  51. package/src/core/commands/research/analyze.md +285 -121
  52. package/src/core/commands/research/import.md +281 -109
  53. package/src/core/commands/retro.md +76 -0
  54. package/src/core/commands/review.md +72 -0
  55. package/src/core/commands/rlm.md +83 -0
  56. package/src/core/commands/rpi.md +90 -0
  57. package/src/core/commands/session/cleanup.md +214 -12
  58. package/src/core/commands/session/end.md +155 -17
  59. package/src/core/commands/sprint.md +72 -0
  60. package/src/core/commands/story-validate.md +68 -0
  61. package/src/core/commands/template.md +69 -0
  62. package/src/core/commands/tests.md +83 -0
  63. package/src/core/commands/update.md +59 -0
  64. package/src/core/commands/validate-expertise.md +76 -0
  65. package/src/core/commands/velocity.md +74 -0
  66. package/src/core/commands/verify.md +91 -0
  67. package/src/core/commands/whats-new.md +69 -0
  68. package/src/core/commands/workflow.md +88 -0
  69. package/src/core/templates/command-documentation.md +187 -0
  70. package/tools/cli/commands/session.js +1171 -0
  71. package/tools/cli/commands/setup.js +2 -81
  72. package/tools/cli/installers/core/installer.js +0 -5
  73. package/tools/cli/installers/ide/claude-code.js +6 -0
  74. package/tools/cli/lib/config-manager.js +42 -5
@@ -1,259 +1,19 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * edit-tool-damage-control.js - Enforce path protection for Edit tool
4
+ * edit-tool-damage-control.js - PreToolUse hook for Edit tool
5
5
  *
6
- * This PreToolUse hook runs before every Edit tool execution.
7
- * It checks the file path against patterns.yaml to block edits
8
- * to protected paths.
9
- *
10
- * Path protection levels:
11
- * zeroAccessPaths: Cannot read, write, edit, or delete
12
- * readOnlyPaths: Can read, cannot modify or delete
13
- * noDeletePaths: Can read and modify, cannot delete (Edit is allowed)
6
+ * Validates file paths against access control patterns in damage-control-patterns.yaml
7
+ * before allowing file edits. Part of AgileFlow's damage control system.
14
8
  *
15
9
  * Exit codes:
16
- * 0 = Allow edit to proceed
17
- * 2 = Block edit
18
- *
19
- * Usage (as PreToolUse hook):
20
- * node .claude/hooks/damage-control/edit-tool-damage-control.js
10
+ * 0 - Allow operation
11
+ * 2 - Block operation
21
12
  *
22
- * Environment:
23
- * CLAUDE_TOOL_INPUT - JSON string with tool input (contains "file_path")
24
- * CLAUDE_PROJECT_DIR - Project root directory
25
- */
26
-
27
- const fs = require('fs');
28
- const path = require('path');
29
-
30
- // ANSI colors for output
31
- const c = {
32
- reset: '\x1b[0m',
33
- bold: '\x1b[1m',
34
- red: '\x1b[31m',
35
- yellow: '\x1b[33m',
36
- cyan: '\x1b[36m',
37
- };
38
-
39
- // Exit codes
40
- const EXIT_ALLOW = 0;
41
- const EXIT_BLOCK = 2;
42
-
43
- /**
44
- * Load path protection rules from patterns.yaml
45
- */
46
- function loadPathRules(projectDir) {
47
- const locations = [
48
- path.join(projectDir, '.claude/hooks/damage-control/patterns.yaml'),
49
- path.join(projectDir, '.agileflow/hooks/damage-control/patterns.yaml'),
50
- path.join(projectDir, 'patterns.yaml'),
51
- ];
52
-
53
- for (const loc of locations) {
54
- if (fs.existsSync(loc)) {
55
- try {
56
- const content = fs.readFileSync(loc, 'utf8');
57
- return parsePathRules(content);
58
- } catch (e) {
59
- console.error(`Warning: Could not parse ${loc}: ${e.message}`);
60
- }
61
- }
62
- }
63
-
64
- return getDefaultPathRules();
65
- }
66
-
67
- /**
68
- * Parse path rules from YAML content
13
+ * Usage: Configured as PreToolUse hook in .claude/settings.json
69
14
  */
70
- function parsePathRules(content) {
71
- const rules = {
72
- zeroAccessPaths: [],
73
- readOnlyPaths: [],
74
- noDeletePaths: [],
75
- };
76
-
77
- let currentSection = null;
78
-
79
- const lines = content.split('\n');
80
-
81
- for (const line of lines) {
82
- if (line.trim().startsWith('#') || line.trim() === '') continue;
83
-
84
- if (line.match(/^zeroAccessPaths:/)) {
85
- currentSection = 'zeroAccessPaths';
86
- continue;
87
- }
88
- if (line.match(/^readOnlyPaths:/)) {
89
- currentSection = 'readOnlyPaths';
90
- continue;
91
- }
92
- if (line.match(/^noDeletePaths:/)) {
93
- currentSection = 'noDeletePaths';
94
- continue;
95
- }
96
- if (line.match(/^(bashToolPatterns|askPatterns|agileflowPatterns|config):/)) {
97
- currentSection = null;
98
- continue;
99
- }
100
-
101
- if (currentSection && rules[currentSection]) {
102
- const pathMatch = line.match(/^\s+-\s*['"]?(.+?)['"]?\s*$/);
103
- if (pathMatch) {
104
- rules[currentSection].push(pathMatch[1]);
105
- }
106
- }
107
- }
108
-
109
- return rules;
110
- }
111
-
112
- /**
113
- * Default path rules if patterns.yaml not found
114
- */
115
- function getDefaultPathRules() {
116
- return {
117
- zeroAccessPaths: ['~/.ssh/', '~/.aws/credentials', '.env', '.env.local', '.env.production'],
118
- readOnlyPaths: ['/etc/', '~/.bashrc', '~/.zshrc', 'package-lock.json', 'yarn.lock', '.git/'],
119
- noDeletePaths: ['.agileflow/', '.claude/', 'docs/09-agents/status.json', 'CLAUDE.md'],
120
- };
121
- }
122
-
123
- /**
124
- * Expand home directory in path
125
- */
126
- function expandHome(filePath) {
127
- if (filePath.startsWith('~/')) {
128
- return path.join(process.env.HOME || '', filePath.slice(2));
129
- }
130
- return filePath;
131
- }
132
-
133
- /**
134
- * Check if a file path matches a pattern
135
- * Supports:
136
- * - Exact paths
137
- * - Directory prefixes (ending with /)
138
- * - Glob wildcards (**)
139
- */
140
- function pathMatches(filePath, pattern) {
141
- const expandedPattern = expandHome(pattern);
142
- const normalizedFile = path.normalize(filePath);
143
- const normalizedPattern = path.normalize(expandedPattern);
144
-
145
- // Exact match
146
- if (normalizedFile === normalizedPattern) return true;
147
-
148
- // Directory prefix match (pattern ends with /)
149
- if (pattern.endsWith('/')) {
150
- if (normalizedFile.startsWith(normalizedPattern)) return true;
151
- }
152
-
153
- // Glob pattern (**/)
154
- if (pattern.includes('**/')) {
155
- const globPart = pattern.split('**/')[1];
156
- if (normalizedFile.includes(globPart)) return true;
157
- }
158
-
159
- // Simple wildcard at end
160
- if (pattern.endsWith('*')) {
161
- const prefix = normalizedPattern.slice(0, -1);
162
- if (normalizedFile.startsWith(prefix)) return true;
163
- }
164
-
165
- // Basename match (for patterns like .env)
166
- if (!pattern.includes('/') && !pattern.includes(path.sep)) {
167
- const basename = path.basename(normalizedFile);
168
- if (basename === pattern) return true;
169
- // Pattern like .env* matching .env.local
170
- if (pattern.endsWith('*')) {
171
- const patternBase = pattern.slice(0, -1);
172
- if (basename.startsWith(patternBase)) return true;
173
- }
174
- }
175
-
176
- return false;
177
- }
178
-
179
- /**
180
- * Check if file path is protected
181
- * Returns: { blocked: boolean, reason: string, level: string }
182
- */
183
- function checkPath(filePath, rules) {
184
- // Check zero access paths (blocked completely)
185
- for (const pattern of rules.zeroAccessPaths) {
186
- if (pathMatches(filePath, pattern)) {
187
- return {
188
- blocked: true,
189
- reason: `Path is in zero-access protected list: ${pattern}`,
190
- level: 'zero-access',
191
- };
192
- }
193
- }
194
-
195
- // Check read-only paths (cannot edit)
196
- for (const pattern of rules.readOnlyPaths) {
197
- if (pathMatches(filePath, pattern)) {
198
- return {
199
- blocked: true,
200
- reason: `Path is read-only: ${pattern}`,
201
- level: 'read-only',
202
- };
203
- }
204
- }
205
-
206
- // noDeletePaths allows editing, so we don't block here
207
- // (deletion is handled by a different mechanism or file watcher)
208
-
209
- return { blocked: false, reason: null, level: null };
210
- }
211
-
212
- /**
213
- * Main entry point
214
- */
215
- function main() {
216
- const toolInput = process.env.CLAUDE_TOOL_INPUT;
217
- const projectDir = process.env.CLAUDE_PROJECT_DIR || process.cwd();
218
-
219
- if (!toolInput) {
220
- process.exit(EXIT_ALLOW);
221
- }
222
-
223
- let input;
224
- try {
225
- input = JSON.parse(toolInput);
226
- } catch (e) {
227
- console.error('Error parsing CLAUDE_TOOL_INPUT:', e.message);
228
- process.exit(EXIT_ALLOW);
229
- }
230
-
231
- const filePath = input.file_path;
232
- if (!filePath) {
233
- process.exit(EXIT_ALLOW);
234
- }
235
-
236
- // Resolve to absolute path
237
- const absolutePath = path.isAbsolute(filePath) ? filePath : path.join(projectDir, filePath);
238
-
239
- // Load rules
240
- const rules = loadPathRules(projectDir);
241
-
242
- // Check path
243
- const result = checkPath(absolutePath, rules);
244
-
245
- if (result.blocked) {
246
- console.error(`${c.red}${c.bold}BLOCKED${c.reset}: ${result.reason}`);
247
- console.error(`${c.yellow}File: ${filePath}${c.reset}`);
248
- console.error(`${c.cyan}This file is protected by damage control (${result.level}).${c.reset}`);
249
- process.exit(EXIT_BLOCK);
250
- }
251
-
252
- process.exit(EXIT_ALLOW);
253
- }
254
15
 
255
- if (require.main === module) {
256
- main();
257
- }
16
+ const { createPathHook } = require('../lib/damage-control-utils');
258
17
 
259
- module.exports = { checkPath, loadPathRules, pathMatches };
18
+ // Run the hook using factory
19
+ createPathHook('edit')();
@@ -1,254 +1,19 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * write-tool-damage-control.js - Enforce path protection for Write tool
4
+ * write-tool-damage-control.js - PreToolUse hook for Write tool
5
5
  *
6
- * This PreToolUse hook runs before every Write tool execution.
7
- * It checks the file path against patterns.yaml to block writes
8
- * to protected paths.
9
- *
10
- * Path protection levels:
11
- * zeroAccessPaths: Cannot read, write, edit, or delete
12
- * readOnlyPaths: Can read, cannot write or delete
13
- * noDeletePaths: Can read and write, cannot delete (Write is allowed)
6
+ * Validates file paths against access control patterns in damage-control-patterns.yaml
7
+ * before allowing file writes. Part of AgileFlow's damage control system.
14
8
  *
15
9
  * Exit codes:
16
- * 0 = Allow write to proceed
17
- * 2 = Block write
18
- *
19
- * Usage (as PreToolUse hook):
20
- * node .claude/hooks/damage-control/write-tool-damage-control.js
10
+ * 0 - Allow operation
11
+ * 2 - Block operation
21
12
  *
22
- * Environment:
23
- * CLAUDE_TOOL_INPUT - JSON string with tool input (contains "file_path")
24
- * CLAUDE_PROJECT_DIR - Project root directory
25
- */
26
-
27
- const fs = require('fs');
28
- const path = require('path');
29
-
30
- // ANSI colors for output
31
- const c = {
32
- reset: '\x1b[0m',
33
- bold: '\x1b[1m',
34
- red: '\x1b[31m',
35
- yellow: '\x1b[33m',
36
- cyan: '\x1b[36m',
37
- };
38
-
39
- // Exit codes
40
- const EXIT_ALLOW = 0;
41
- const EXIT_BLOCK = 2;
42
-
43
- /**
44
- * Load path protection rules from patterns.yaml
45
- */
46
- function loadPathRules(projectDir) {
47
- const locations = [
48
- path.join(projectDir, '.claude/hooks/damage-control/patterns.yaml'),
49
- path.join(projectDir, '.agileflow/hooks/damage-control/patterns.yaml'),
50
- path.join(projectDir, 'patterns.yaml'),
51
- ];
52
-
53
- for (const loc of locations) {
54
- if (fs.existsSync(loc)) {
55
- try {
56
- const content = fs.readFileSync(loc, 'utf8');
57
- return parsePathRules(content);
58
- } catch (e) {
59
- console.error(`Warning: Could not parse ${loc}: ${e.message}`);
60
- }
61
- }
62
- }
63
-
64
- return getDefaultPathRules();
65
- }
66
-
67
- /**
68
- * Parse path rules from YAML content
13
+ * Usage: Configured as PreToolUse hook in .claude/settings.json
69
14
  */
70
- function parsePathRules(content) {
71
- const rules = {
72
- zeroAccessPaths: [],
73
- readOnlyPaths: [],
74
- noDeletePaths: [],
75
- };
76
-
77
- let currentSection = null;
78
-
79
- const lines = content.split('\n');
80
-
81
- for (const line of lines) {
82
- if (line.trim().startsWith('#') || line.trim() === '') continue;
83
-
84
- if (line.match(/^zeroAccessPaths:/)) {
85
- currentSection = 'zeroAccessPaths';
86
- continue;
87
- }
88
- if (line.match(/^readOnlyPaths:/)) {
89
- currentSection = 'readOnlyPaths';
90
- continue;
91
- }
92
- if (line.match(/^noDeletePaths:/)) {
93
- currentSection = 'noDeletePaths';
94
- continue;
95
- }
96
- if (line.match(/^(bashToolPatterns|askPatterns|agileflowPatterns|config):/)) {
97
- currentSection = null;
98
- continue;
99
- }
100
-
101
- if (currentSection && rules[currentSection]) {
102
- const pathMatch = line.match(/^\s+-\s*['"]?(.+?)['"]?\s*$/);
103
- if (pathMatch) {
104
- rules[currentSection].push(pathMatch[1]);
105
- }
106
- }
107
- }
108
-
109
- return rules;
110
- }
111
-
112
- /**
113
- * Default path rules if patterns.yaml not found
114
- */
115
- function getDefaultPathRules() {
116
- return {
117
- zeroAccessPaths: ['~/.ssh/', '~/.aws/credentials', '.env', '.env.local', '.env.production'],
118
- readOnlyPaths: ['/etc/', '~/.bashrc', '~/.zshrc', 'package-lock.json', 'yarn.lock', '.git/'],
119
- noDeletePaths: ['.agileflow/', '.claude/', 'docs/09-agents/status.json', 'CLAUDE.md'],
120
- };
121
- }
122
-
123
- /**
124
- * Expand home directory in path
125
- */
126
- function expandHome(filePath) {
127
- if (filePath.startsWith('~/')) {
128
- return path.join(process.env.HOME || '', filePath.slice(2));
129
- }
130
- return filePath;
131
- }
132
-
133
- /**
134
- * Check if a file path matches a pattern
135
- */
136
- function pathMatches(filePath, pattern) {
137
- const expandedPattern = expandHome(pattern);
138
- const normalizedFile = path.normalize(filePath);
139
- const normalizedPattern = path.normalize(expandedPattern);
140
-
141
- // Exact match
142
- if (normalizedFile === normalizedPattern) return true;
143
-
144
- // Directory prefix match
145
- if (pattern.endsWith('/')) {
146
- if (normalizedFile.startsWith(normalizedPattern)) return true;
147
- }
148
-
149
- // Glob pattern (**)
150
- if (pattern.includes('**/')) {
151
- const globPart = pattern.split('**/')[1];
152
- if (normalizedFile.includes(globPart)) return true;
153
- }
154
-
155
- // Wildcard at end
156
- if (pattern.endsWith('*')) {
157
- const prefix = normalizedPattern.slice(0, -1);
158
- if (normalizedFile.startsWith(prefix)) return true;
159
- }
160
-
161
- // Basename match
162
- if (!pattern.includes('/') && !pattern.includes(path.sep)) {
163
- const basename = path.basename(normalizedFile);
164
- if (basename === pattern) return true;
165
- if (pattern.endsWith('*')) {
166
- const patternBase = pattern.slice(0, -1);
167
- if (basename.startsWith(patternBase)) return true;
168
- }
169
- }
170
-
171
- return false;
172
- }
173
-
174
- /**
175
- * Check if file path is protected for writing
176
- * Returns: { blocked: boolean, reason: string, level: string }
177
- */
178
- function checkPath(filePath, rules) {
179
- // Check zero access paths (blocked completely)
180
- for (const pattern of rules.zeroAccessPaths) {
181
- if (pathMatches(filePath, pattern)) {
182
- return {
183
- blocked: true,
184
- reason: `Path is in zero-access protected list: ${pattern}`,
185
- level: 'zero-access',
186
- };
187
- }
188
- }
189
-
190
- // Check read-only paths (cannot write)
191
- for (const pattern of rules.readOnlyPaths) {
192
- if (pathMatches(filePath, pattern)) {
193
- return {
194
- blocked: true,
195
- reason: `Path is read-only: ${pattern}`,
196
- level: 'read-only',
197
- };
198
- }
199
- }
200
-
201
- // noDeletePaths allows writing, only blocks deletion
202
- // so we don't block writes here
203
-
204
- return { blocked: false, reason: null, level: null };
205
- }
206
-
207
- /**
208
- * Main entry point
209
- */
210
- function main() {
211
- const toolInput = process.env.CLAUDE_TOOL_INPUT;
212
- const projectDir = process.env.CLAUDE_PROJECT_DIR || process.cwd();
213
-
214
- if (!toolInput) {
215
- process.exit(EXIT_ALLOW);
216
- }
217
-
218
- let input;
219
- try {
220
- input = JSON.parse(toolInput);
221
- } catch (e) {
222
- console.error('Error parsing CLAUDE_TOOL_INPUT:', e.message);
223
- process.exit(EXIT_ALLOW);
224
- }
225
-
226
- const filePath = input.file_path;
227
- if (!filePath) {
228
- process.exit(EXIT_ALLOW);
229
- }
230
-
231
- // Resolve to absolute path
232
- const absolutePath = path.isAbsolute(filePath) ? filePath : path.join(projectDir, filePath);
233
-
234
- // Load rules
235
- const rules = loadPathRules(projectDir);
236
-
237
- // Check path
238
- const result = checkPath(absolutePath, rules);
239
-
240
- if (result.blocked) {
241
- console.error(`${c.red}${c.bold}BLOCKED${c.reset}: ${result.reason}`);
242
- console.error(`${c.yellow}File: ${filePath}${c.reset}`);
243
- console.error(`${c.cyan}This file is protected by damage control (${result.level}).${c.reset}`);
244
- process.exit(EXIT_BLOCK);
245
- }
246
-
247
- process.exit(EXIT_ALLOW);
248
- }
249
15
 
250
- if (require.main === module) {
251
- main();
252
- }
16
+ const { createPathHook } = require('../lib/damage-control-utils');
253
17
 
254
- module.exports = { checkPath, loadPathRules, pathMatches };
18
+ // Run the hook using factory
19
+ createPathHook('write')();