agileflow 2.90.7 → 2.91.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 (73) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/README.md +6 -6
  3. package/lib/codebase-indexer.js +810 -0
  4. package/lib/validate-names.js +3 -3
  5. package/package.json +4 -1
  6. package/scripts/obtain-context.js +238 -0
  7. package/scripts/precompact-context.sh +13 -1
  8. package/scripts/query-codebase.js +430 -0
  9. package/scripts/tui/blessed/data/watcher.js +175 -0
  10. package/scripts/tui/blessed/index.js +244 -0
  11. package/scripts/tui/blessed/panels/output.js +95 -0
  12. package/scripts/tui/blessed/panels/sessions.js +143 -0
  13. package/scripts/tui/blessed/panels/trace.js +91 -0
  14. package/scripts/tui/blessed/ui/help.js +77 -0
  15. package/scripts/tui/blessed/ui/screen.js +52 -0
  16. package/scripts/tui/blessed/ui/statusbar.js +51 -0
  17. package/scripts/tui/blessed/ui/tabbar.js +99 -0
  18. package/scripts/tui/index.js +38 -30
  19. package/scripts/validators/README.md +143 -0
  20. package/scripts/validators/component-validator.js +212 -0
  21. package/scripts/validators/json-schema-validator.js +179 -0
  22. package/scripts/validators/markdown-validator.js +153 -0
  23. package/scripts/validators/migration-validator.js +117 -0
  24. package/scripts/validators/security-validator.js +276 -0
  25. package/scripts/validators/story-format-validator.js +176 -0
  26. package/scripts/validators/test-result-validator.js +99 -0
  27. package/scripts/validators/workflow-validator.js +240 -0
  28. package/src/core/agents/accessibility.md +6 -0
  29. package/src/core/agents/adr-writer.md +6 -0
  30. package/src/core/agents/analytics.md +6 -0
  31. package/src/core/agents/api.md +6 -0
  32. package/src/core/agents/ci.md +6 -0
  33. package/src/core/agents/codebase-query.md +237 -0
  34. package/src/core/agents/compliance.md +6 -0
  35. package/src/core/agents/configuration-damage-control.md +6 -0
  36. package/src/core/agents/configuration-visual-e2e.md +6 -0
  37. package/src/core/agents/database.md +10 -0
  38. package/src/core/agents/datamigration.md +6 -0
  39. package/src/core/agents/design.md +6 -0
  40. package/src/core/agents/devops.md +6 -0
  41. package/src/core/agents/documentation.md +6 -0
  42. package/src/core/agents/epic-planner.md +6 -0
  43. package/src/core/agents/integrations.md +6 -0
  44. package/src/core/agents/mentor.md +6 -0
  45. package/src/core/agents/mobile.md +6 -0
  46. package/src/core/agents/monitoring.md +6 -0
  47. package/src/core/agents/multi-expert.md +6 -0
  48. package/src/core/agents/performance.md +6 -0
  49. package/src/core/agents/product.md +6 -0
  50. package/src/core/agents/qa.md +6 -0
  51. package/src/core/agents/readme-updater.md +6 -0
  52. package/src/core/agents/refactor.md +6 -0
  53. package/src/core/agents/research.md +6 -0
  54. package/src/core/agents/security.md +6 -0
  55. package/src/core/agents/testing.md +10 -0
  56. package/src/core/agents/ui.md +6 -0
  57. package/src/core/commands/audit.md +401 -0
  58. package/src/core/commands/board.md +1 -0
  59. package/src/core/commands/epic.md +92 -1
  60. package/src/core/commands/help.md +1 -0
  61. package/src/core/commands/metrics.md +1 -0
  62. package/src/core/commands/research/analyze.md +1 -0
  63. package/src/core/commands/research/ask.md +2 -0
  64. package/src/core/commands/research/import.md +1 -0
  65. package/src/core/commands/research/list.md +2 -0
  66. package/src/core/commands/research/synthesize.md +584 -0
  67. package/src/core/commands/research/view.md +2 -0
  68. package/src/core/commands/status.md +126 -1
  69. package/src/core/commands/story/list.md +9 -9
  70. package/src/core/commands/story/view.md +1 -0
  71. package/src/core/experts/codebase-query/expertise.yaml +190 -0
  72. package/src/core/experts/codebase-query/question.md +73 -0
  73. package/src/core/experts/codebase-query/self-improve.md +105 -0
@@ -0,0 +1,179 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * JSON Schema Validator
4
+ *
5
+ * Validates JSON files for proper structure after Write operations.
6
+ *
7
+ * Exit codes:
8
+ * 0 = Success
9
+ * 2 = Error (Claude will attempt to fix)
10
+ * 1 = Warning (logged but not blocking)
11
+ *
12
+ * Usage in agent hooks:
13
+ * hooks:
14
+ * PostToolUse:
15
+ * - matcher: "Write"
16
+ * hooks:
17
+ * - type: command
18
+ * command: "node .agileflow/hooks/validators/json-schema-validator.js"
19
+ */
20
+
21
+ const fs = require('fs');
22
+ const path = require('path');
23
+
24
+ let input = '';
25
+ process.stdin.on('data', chunk => input += chunk);
26
+ process.stdin.on('end', () => {
27
+ try {
28
+ const context = JSON.parse(input);
29
+ const filePath = context.tool_input?.file_path;
30
+
31
+ // Only validate JSON files
32
+ if (!filePath || !filePath.endsWith('.json')) {
33
+ process.exit(0);
34
+ }
35
+
36
+ // Skip if file doesn't exist (might be a create failure)
37
+ if (!fs.existsSync(filePath)) {
38
+ console.log(`File not found: ${filePath} (skipping validation)`);
39
+ process.exit(0);
40
+ }
41
+
42
+ const issues = validateJson(filePath);
43
+
44
+ if (issues.length > 0) {
45
+ console.error(`Resolve these JSON issues in ${filePath}:`);
46
+ issues.forEach(i => console.error(` - ${i}`));
47
+ process.exit(2); // Claude will fix
48
+ }
49
+
50
+ console.log(`JSON validation passed: ${filePath}`);
51
+ process.exit(0);
52
+ } catch (e) {
53
+ console.error(`Validator error: ${e.message}`);
54
+ process.exit(1);
55
+ }
56
+ });
57
+
58
+ function validateJson(filePath) {
59
+ const issues = [];
60
+
61
+ try {
62
+ const content = fs.readFileSync(filePath, 'utf8');
63
+
64
+ // Check for empty file
65
+ if (!content.trim()) {
66
+ issues.push('File is empty');
67
+ return issues;
68
+ }
69
+
70
+ // Try to parse JSON
71
+ const data = JSON.parse(content);
72
+
73
+ // Check for common JSON issues
74
+ if (typeof data !== 'object' || data === null) {
75
+ issues.push('Root must be an object or array');
76
+ }
77
+
78
+ // Specific checks for known files
79
+ const fileName = path.basename(filePath);
80
+
81
+ if (fileName === 'status.json') {
82
+ issues.push(...validateStatusJson(data));
83
+ } else if (fileName === 'package.json') {
84
+ issues.push(...validatePackageJson(data));
85
+ } else if (fileName === 'tsconfig.json') {
86
+ issues.push(...validateTsConfig(data));
87
+ }
88
+
89
+ } catch (e) {
90
+ if (e instanceof SyntaxError) {
91
+ issues.push(`Invalid JSON syntax: ${e.message}`);
92
+ } else {
93
+ issues.push(`Read error: ${e.message}`);
94
+ }
95
+ }
96
+
97
+ return issues;
98
+ }
99
+
100
+ function validateStatusJson(data) {
101
+ const issues = [];
102
+
103
+ // Check for required structure - status.json has epics object with embedded stories
104
+ if (!data.epics && !data.current_story && !data.updated) {
105
+ issues.push('status.json should have epics, current_story, or updated field');
106
+ }
107
+
108
+ // Validate epics object if present
109
+ if (data.epics) {
110
+ if (typeof data.epics !== 'object' || Array.isArray(data.epics)) {
111
+ issues.push('epics must be an object (not array)');
112
+ } else {
113
+ Object.entries(data.epics).forEach(([epicId, epic]) => {
114
+ if (!epic.title) {
115
+ issues.push(`Epic ${epicId} missing 'title' field`);
116
+ }
117
+ if (epic.stories && !Array.isArray(epic.stories)) {
118
+ issues.push(`Epic ${epicId} stories must be an array`);
119
+ }
120
+ });
121
+ }
122
+ }
123
+
124
+ // Validate stories - can be object (map by ID) or array
125
+ if (data.stories) {
126
+ if (Array.isArray(data.stories)) {
127
+ // Array format
128
+ data.stories.forEach((story, index) => {
129
+ if (!story.id) {
130
+ issues.push(`Story at index ${index} missing required 'id' field`);
131
+ }
132
+ if (!story.title && !story.name) {
133
+ issues.push(`Story ${story.id || index} missing 'title' or 'name' field`);
134
+ }
135
+ if (story.status && !['pending', 'in_progress', 'completed', 'done', 'blocked', 'ready', 'in-progress', 'in_review', 'in-review', 'archived'].includes(story.status)) {
136
+ issues.push(`Story ${story.id || index} has invalid status: ${story.status}`);
137
+ }
138
+ });
139
+ } else if (typeof data.stories === 'object') {
140
+ // Object/map format (keyed by story ID)
141
+ Object.entries(data.stories).forEach(([storyId, story]) => {
142
+ if (!story.title && !story.name) {
143
+ issues.push(`Story ${storyId} missing 'title' or 'name' field`);
144
+ }
145
+ if (story.status && !['pending', 'in_progress', 'completed', 'done', 'blocked', 'ready', 'in-progress', 'in_review', 'in-review', 'archived'].includes(story.status)) {
146
+ issues.push(`Story ${storyId} has invalid status: ${story.status}`);
147
+ }
148
+ });
149
+ }
150
+ }
151
+
152
+ return issues;
153
+ }
154
+
155
+ function validatePackageJson(data) {
156
+ const issues = [];
157
+
158
+ if (!data.name) {
159
+ issues.push('package.json requires "name" field');
160
+ }
161
+ if (!data.version) {
162
+ issues.push('package.json requires "version" field');
163
+ }
164
+ if (data.version && !/^\d+\.\d+\.\d+/.test(data.version)) {
165
+ issues.push(`Invalid version format: ${data.version} (expected semver)`);
166
+ }
167
+
168
+ return issues;
169
+ }
170
+
171
+ function validateTsConfig(data) {
172
+ const issues = [];
173
+
174
+ if (!data.compilerOptions) {
175
+ issues.push('tsconfig.json should have compilerOptions');
176
+ }
177
+
178
+ return issues;
179
+ }
@@ -0,0 +1,153 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Markdown Validator
4
+ *
5
+ * Validates markdown files for proper structure after Write operations.
6
+ *
7
+ * Exit codes:
8
+ * 0 = Success
9
+ * 2 = Error (Claude will attempt to fix)
10
+ * 1 = Warning (logged but not blocking)
11
+ *
12
+ * Usage in agent hooks:
13
+ * hooks:
14
+ * PostToolUse:
15
+ * - matcher: "Write"
16
+ * hooks:
17
+ * - type: command
18
+ * command: "node .agileflow/hooks/validators/markdown-validator.js"
19
+ */
20
+
21
+ const fs = require('fs');
22
+ const path = require('path');
23
+
24
+ let input = '';
25
+ process.stdin.on('data', chunk => input += chunk);
26
+ process.stdin.on('end', () => {
27
+ try {
28
+ const context = JSON.parse(input);
29
+ const filePath = context.tool_input?.file_path;
30
+
31
+ // Only validate markdown files
32
+ if (!filePath || !filePath.endsWith('.md')) {
33
+ process.exit(0);
34
+ }
35
+
36
+ // Skip if file doesn't exist
37
+ if (!fs.existsSync(filePath)) {
38
+ console.log(`File not found: ${filePath} (skipping validation)`);
39
+ process.exit(0);
40
+ }
41
+
42
+ const issues = validateMarkdown(filePath);
43
+
44
+ if (issues.length > 0) {
45
+ console.error(`Resolve these markdown issues in ${filePath}:`);
46
+ issues.forEach(i => console.error(` - ${i}`));
47
+ process.exit(2); // Claude will fix
48
+ }
49
+
50
+ console.log(`Markdown validation passed: ${filePath}`);
51
+ process.exit(0);
52
+ } catch (e) {
53
+ console.error(`Validator error: ${e.message}`);
54
+ process.exit(1);
55
+ }
56
+ });
57
+
58
+ function validateMarkdown(filePath) {
59
+ const issues = [];
60
+
61
+ try {
62
+ const content = fs.readFileSync(filePath, 'utf8');
63
+ const lines = content.split('\n');
64
+ const fileName = path.basename(filePath);
65
+
66
+ // Check for empty file
67
+ if (!content.trim()) {
68
+ issues.push('File is empty');
69
+ return issues;
70
+ }
71
+
72
+ // Check for title (first line should be # heading)
73
+ const firstContentLine = lines.find(l => l.trim());
74
+ if (!firstContentLine?.startsWith('#')) {
75
+ issues.push('Document should start with a heading (#)');
76
+ }
77
+
78
+ // Check for broken links (basic check)
79
+ const brokenLinkPattern = /\[([^\]]*)\]\(\s*\)/g;
80
+ let match;
81
+ while ((match = brokenLinkPattern.exec(content)) !== null) {
82
+ issues.push(`Empty link found: [${match[1]}]()`);
83
+ }
84
+
85
+ // Check for unclosed code blocks
86
+ const codeBlockCount = (content.match(/```/g) || []).length;
87
+ if (codeBlockCount % 2 !== 0) {
88
+ issues.push('Unclosed code block (odd number of ``` markers)');
89
+ }
90
+
91
+ // Check for heading hierarchy issues
92
+ let lastHeadingLevel = 0;
93
+ lines.forEach((line, index) => {
94
+ const headingMatch = line.match(/^(#{1,6})\s/);
95
+ if (headingMatch) {
96
+ const level = headingMatch[1].length;
97
+ // Skip from level 0 to level 1 is OK
98
+ if (lastHeadingLevel > 0 && level > lastHeadingLevel + 1) {
99
+ issues.push(`Line ${index + 1}: Heading level jump (h${lastHeadingLevel} to h${level})`);
100
+ }
101
+ lastHeadingLevel = level;
102
+ }
103
+ });
104
+
105
+ // Check for specific file types
106
+ if (fileName.startsWith('adr-') || fileName.startsWith('ADR-')) {
107
+ issues.push(...validateADR(content));
108
+ } else if (filePath.includes('/10-research/')) {
109
+ issues.push(...validateResearchNote(content));
110
+ }
111
+
112
+ } catch (e) {
113
+ issues.push(`Read error: ${e.message}`);
114
+ }
115
+
116
+ return issues;
117
+ }
118
+
119
+ function validateADR(content) {
120
+ const issues = [];
121
+
122
+ const requiredSections = ['## Context', '## Decision', '## Consequences'];
123
+ requiredSections.forEach(section => {
124
+ if (!content.includes(section)) {
125
+ issues.push(`ADR missing required section: ${section}`);
126
+ }
127
+ });
128
+
129
+ if (!content.includes('**Status**:')) {
130
+ issues.push('ADR missing Status field');
131
+ }
132
+ if (!content.includes('**Date**:')) {
133
+ issues.push('ADR missing Date field');
134
+ }
135
+
136
+ return issues;
137
+ }
138
+
139
+ function validateResearchNote(content) {
140
+ const issues = [];
141
+
142
+ if (!content.includes('**Import Date**:')) {
143
+ issues.push('Research note missing Import Date');
144
+ }
145
+ if (!content.includes('## Summary')) {
146
+ issues.push('Research note missing Summary section');
147
+ }
148
+ if (!content.includes('## Key Findings')) {
149
+ issues.push('Research note missing Key Findings section');
150
+ }
151
+
152
+ return issues;
153
+ }
@@ -0,0 +1,117 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Migration Validator
4
+ *
5
+ * Validates database migration commands for proper patterns.
6
+ *
7
+ * Exit codes:
8
+ * 0 = Success
9
+ * 2 = Error (Claude will attempt to fix)
10
+ * 1 = Warning (logged but not blocking)
11
+ *
12
+ * Usage in agent hooks:
13
+ * hooks:
14
+ * PostToolUse:
15
+ * - matcher: "Bash"
16
+ * hooks:
17
+ * - type: command
18
+ * command: "node .agileflow/hooks/validators/migration-validator.js"
19
+ */
20
+
21
+ let input = '';
22
+ process.stdin.on('data', chunk => input += chunk);
23
+ process.stdin.on('end', () => {
24
+ try {
25
+ const context = JSON.parse(input);
26
+ const command = context.tool_input?.command;
27
+ const result = context.result || '';
28
+
29
+ // Only validate migration-related commands
30
+ if (!command || !isMigrationCommand(command)) {
31
+ process.exit(0);
32
+ }
33
+
34
+ const issues = validateMigration(command, result);
35
+
36
+ if (issues.length > 0) {
37
+ console.error(`Migration validation issues:`);
38
+ issues.forEach(i => console.error(` - ${i}`));
39
+ process.exit(2); // Claude will fix
40
+ }
41
+
42
+ console.log(`Migration validation passed: ${command.substring(0, 50)}...`);
43
+ process.exit(0);
44
+ } catch (e) {
45
+ console.error(`Validator error: ${e.message}`);
46
+ process.exit(1);
47
+ }
48
+ });
49
+
50
+ function isMigrationCommand(command) {
51
+ const migrationPatterns = [
52
+ /prisma\s+migrate/i,
53
+ /drizzle-kit/i,
54
+ /typeorm\s+migration/i,
55
+ /knex\s+migrate/i,
56
+ /sequelize\s+db:migrate/i,
57
+ /alembic/i,
58
+ /flyway/i,
59
+ /liquibase/i,
60
+ /rails\s+db:migrate/i,
61
+ /django.*migrate/i,
62
+ /psql.*-f.*\.sql/i,
63
+ /mysql.*<.*\.sql/i,
64
+ ];
65
+
66
+ return migrationPatterns.some(pattern => pattern.test(command));
67
+ }
68
+
69
+ function validateMigration(command, result) {
70
+ const issues = [];
71
+
72
+ // Check for destructive operations without safeguards
73
+ const destructivePatterns = [
74
+ { pattern: /DROP\s+TABLE/i, message: 'DROP TABLE detected - ensure backup exists and this is intentional' },
75
+ { pattern: /DROP\s+DATABASE/i, message: 'DROP DATABASE detected - this is extremely destructive!' },
76
+ { pattern: /TRUNCATE/i, message: 'TRUNCATE detected - this removes all data permanently' },
77
+ { pattern: /DELETE\s+FROM.*WHERE\s*$/i, message: 'DELETE without WHERE clause detected - will delete all rows' },
78
+ { pattern: /--force|--skip-safe/i, message: 'Force flag used - bypassing safety checks' },
79
+ ];
80
+
81
+ for (const { pattern, message } of destructivePatterns) {
82
+ if (pattern.test(command) || pattern.test(result)) {
83
+ issues.push(message);
84
+ }
85
+ }
86
+
87
+ // Check for production environment safeguards
88
+ if (/production|prod/i.test(command) && !/--dry-run/i.test(command)) {
89
+ issues.push('Production migration without --dry-run flag - consider dry run first');
90
+ }
91
+
92
+ // Check for failed migrations in output
93
+ const failurePatterns = [
94
+ { pattern: /error.*migration/i, message: 'Migration error detected in output' },
95
+ { pattern: /rollback.*failed/i, message: 'Rollback failure detected' },
96
+ { pattern: /constraint.*violation/i, message: 'Database constraint violation' },
97
+ { pattern: /duplicate.*key/i, message: 'Duplicate key error - migration may have partially applied' },
98
+ { pattern: /already exists/i, message: 'Object already exists - migration may need cleanup' },
99
+ ];
100
+
101
+ for (const { pattern, message } of failurePatterns) {
102
+ if (pattern.test(result)) {
103
+ issues.push(message);
104
+ }
105
+ }
106
+
107
+ // Check for missing rollback strategy
108
+ if (/migrate.*dev|migrate.*run/i.test(command)) {
109
+ // Prisma and similar ORMs
110
+ if (!/down|rollback|reset/i.test(result) && result.includes('migration')) {
111
+ // Just a warning, not blocking
112
+ console.log('Note: Ensure rollback migration exists for this change');
113
+ }
114
+ }
115
+
116
+ return issues;
117
+ }