aiknowsys 0.6.0 → 0.7.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/lib/commands/audit.js +87 -26
- package/lib/commands/check.js +37 -5
- package/lib/commands/init/templates.js +0 -1
- package/lib/commands/migrate.js +33 -6
- package/lib/commands/scan.js +15 -5
- package/lib/commands/sync.js +4 -9
- package/lib/error-helpers.js +178 -0
- package/lib/sanitize.js +17 -1
- package/package.json +1 -1
- package/templates/AGENTS.template.md +1 -124
- package/templates/agents/architect.agent.template.md +38 -0
- package/templates/skills/validation-troubleshooting/SKILL.md +486 -0
package/lib/commands/audit.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
+
import ora from 'ora';
|
|
3
4
|
import { createLogger } from '../logger.js';
|
|
5
|
+
import { ErrorTemplates } from '../error-helpers.js';
|
|
4
6
|
|
|
5
7
|
/**
|
|
6
8
|
* Audit command - Finds common issues and pattern violations
|
|
@@ -26,42 +28,86 @@ export async function audit(options) {
|
|
|
26
28
|
|
|
27
29
|
// Check if knowledge system exists
|
|
28
30
|
if (!fs.existsSync(essentialsPath) && !fs.existsSync(agentsPath)) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
throw ErrorTemplates.noKnowledgeSystem();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Edge case: Check file sizes before reading
|
|
35
|
+
const filesToCheck = [
|
|
36
|
+
{ path: essentialsPath, name: 'CODEBASE_ESSENTIALS.md' },
|
|
37
|
+
{ path: agentsPath, name: 'AGENTS.md' },
|
|
38
|
+
{ path: changelogPath, name: 'CODEBASE_CHANGELOG.md' }
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
for (const file of filesToCheck) {
|
|
42
|
+
if (fs.existsSync(file.path)) {
|
|
43
|
+
const stats = fs.statSync(file.path);
|
|
44
|
+
const fileSize = stats.size;
|
|
45
|
+
const fiftyMB = 50 * 1024 * 1024;
|
|
46
|
+
|
|
47
|
+
// Edge case: File is empty
|
|
48
|
+
if (fileSize === 0) {
|
|
49
|
+
throw ErrorTemplates.emptyFile(file.name);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Edge case: File too large
|
|
53
|
+
if (fileSize > fiftyMB) {
|
|
54
|
+
throw ErrorTemplates.fileTooLarge(file.name, fileSize / 1024 / 1024);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
33
57
|
}
|
|
34
58
|
|
|
59
|
+
// Create spinner for progress (only if not silent)
|
|
60
|
+
const spinner = silent ? null : ora('Starting audit...').start();
|
|
61
|
+
|
|
35
62
|
// Audit 1: Check for duplicated validation matrix
|
|
36
|
-
|
|
63
|
+
if (spinner) {
|
|
64
|
+
spinner.text = 'Check 1/5: Checking for duplication issues...';
|
|
65
|
+
} else {
|
|
66
|
+
log.white('🔍 Checking for duplication issues...');
|
|
67
|
+
}
|
|
37
68
|
|
|
38
69
|
if (fs.existsSync(essentialsPath) && fs.existsSync(agentsPath)) {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
70
|
+
try {
|
|
71
|
+
const essentialsContent = fs.readFileSync(essentialsPath, 'utf-8');
|
|
72
|
+
const agentsContent = fs.readFileSync(agentsPath, 'utf-8');
|
|
73
|
+
|
|
74
|
+
// Look for validation matrix table in both files
|
|
75
|
+
const matrixTablePattern = /\|\s*Command\s*\|.*\|[\s\S]*?\|.*test.*\|/i;
|
|
76
|
+
const hasEssentialsMatrix = matrixTablePattern.test(essentialsContent);
|
|
77
|
+
const hasAgentsMatrix = matrixTablePattern.test(agentsContent);
|
|
78
|
+
|
|
79
|
+
if (hasEssentialsMatrix && hasAgentsMatrix) {
|
|
80
|
+
log.warn('Validation matrix duplicated in both files');
|
|
81
|
+
issues.push({
|
|
82
|
+
type: 'warning',
|
|
83
|
+
category: 'DRY Violation',
|
|
84
|
+
message: 'Validation matrix appears in both ESSENTIALS and AGENTS',
|
|
85
|
+
fix: 'Run: npx aiknowsys sync'
|
|
86
|
+
});
|
|
87
|
+
warnings++;
|
|
88
|
+
} else if (hasEssentialsMatrix) {
|
|
89
|
+
log.log(' ✓ Validation matrix in ESSENTIALS only (correct)');
|
|
90
|
+
}
|
|
91
|
+
} catch (error) {
|
|
92
|
+
// Handle malformed or corrupted files gracefully
|
|
93
|
+
info++;
|
|
49
94
|
issues.push({
|
|
50
|
-
type: '
|
|
51
|
-
category: '
|
|
52
|
-
message:
|
|
53
|
-
fix: '
|
|
95
|
+
type: 'info',
|
|
96
|
+
category: 'File Processing',
|
|
97
|
+
message: `Could not fully parse files: ${error.message}`,
|
|
98
|
+
fix: 'Check file encoding and markdown structure'
|
|
54
99
|
});
|
|
55
|
-
warnings++;
|
|
56
|
-
} else if (hasEssentialsMatrix) {
|
|
57
|
-
log.log(' ✓ Validation matrix in ESSENTIALS only (correct)');
|
|
58
100
|
}
|
|
59
101
|
}
|
|
60
102
|
|
|
61
103
|
log.blank();
|
|
62
104
|
|
|
63
105
|
// Audit 2: Check for generic placeholder values
|
|
64
|
-
|
|
106
|
+
if (spinner) {
|
|
107
|
+
spinner.text = 'Check 2/5: Checking placeholder quality...';
|
|
108
|
+
} else {
|
|
109
|
+
log.white('📝 Checking for placeholder quality...');
|
|
110
|
+
}
|
|
65
111
|
|
|
66
112
|
if (fs.existsSync(essentialsPath)) {
|
|
67
113
|
const content = fs.readFileSync(essentialsPath, 'utf-8');
|
|
@@ -125,7 +171,11 @@ export async function audit(options) {
|
|
|
125
171
|
log.blank();
|
|
126
172
|
|
|
127
173
|
// Audit 3: Check validation matrix quality
|
|
128
|
-
|
|
174
|
+
if (spinner) {
|
|
175
|
+
spinner.text = 'Check 3/5: Checking validation matrix quality...';
|
|
176
|
+
} else {
|
|
177
|
+
log.white('✅ Checking validation matrix quality...');
|
|
178
|
+
}
|
|
129
179
|
|
|
130
180
|
if (fs.existsSync(essentialsPath)) {
|
|
131
181
|
const content = fs.readFileSync(essentialsPath, 'utf-8');
|
|
@@ -172,7 +222,11 @@ export async function audit(options) {
|
|
|
172
222
|
log.blank();
|
|
173
223
|
|
|
174
224
|
// Audit 4: Check changelog usage
|
|
175
|
-
|
|
225
|
+
if (spinner) {
|
|
226
|
+
spinner.text = 'Check 4/5: Checking changelog...';
|
|
227
|
+
} else {
|
|
228
|
+
log.white('📚 Checking changelog...');
|
|
229
|
+
}
|
|
176
230
|
|
|
177
231
|
if (fs.existsSync(changelogPath)) {
|
|
178
232
|
const content = fs.readFileSync(changelogPath, 'utf-8');
|
|
@@ -192,7 +246,11 @@ export async function audit(options) {
|
|
|
192
246
|
log.blank();
|
|
193
247
|
|
|
194
248
|
// Audit 5: Check .aiknowsys/ gitignore configuration
|
|
195
|
-
|
|
249
|
+
if (spinner) {
|
|
250
|
+
spinner.text = 'Check 5/5: Checking .aiknowsys/ gitignore...';
|
|
251
|
+
} else {
|
|
252
|
+
log.white('🔒 Checking .aiknowsys/ gitignore...');
|
|
253
|
+
}
|
|
196
254
|
|
|
197
255
|
const gitignorePath = path.join(targetDir, '.gitignore');
|
|
198
256
|
const aiknowsysDir = path.join(targetDir, '.aiknowsys');
|
|
@@ -266,6 +324,9 @@ export async function audit(options) {
|
|
|
266
324
|
|
|
267
325
|
log.blank();
|
|
268
326
|
|
|
327
|
+
// Stop spinner before summary
|
|
328
|
+
if (spinner) spinner.succeed('Audit complete');
|
|
329
|
+
|
|
269
330
|
// Summary
|
|
270
331
|
log.section('Audit Summary', '📊');
|
|
271
332
|
|
package/lib/commands/check.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { createLogger } from '../logger.js';
|
|
4
|
+
import { ErrorTemplates } from '../error-helpers.js';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Check command - Validates knowledge system setup
|
|
@@ -28,9 +29,36 @@ export async function check(options) {
|
|
|
28
29
|
];
|
|
29
30
|
|
|
30
31
|
log.white('📁 Checking required files...');
|
|
32
|
+
const warnings = [];
|
|
33
|
+
|
|
31
34
|
for (const file of requiredFiles) {
|
|
32
35
|
const filePath = path.join(targetDir, file.path);
|
|
33
36
|
if (fs.existsSync(filePath)) {
|
|
37
|
+
// Check file size and content
|
|
38
|
+
const stats = fs.statSync(filePath);
|
|
39
|
+
const fileSize = stats.size;
|
|
40
|
+
|
|
41
|
+
// Edge case: Empty file
|
|
42
|
+
if (fileSize === 0) {
|
|
43
|
+
log.log(` ✗ ${file.name} - Empty file`);
|
|
44
|
+
checks.push({ name: file.name, status: 'fail', error: 'File is empty' });
|
|
45
|
+
failed++;
|
|
46
|
+
throw ErrorTemplates.emptyFile(file.path);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Edge case: Huge file (>5MB warning, >50MB error)
|
|
50
|
+
const fiveMB = 5 * 1024 * 1024;
|
|
51
|
+
const fiftyMB = 50 * 1024 * 1024;
|
|
52
|
+
|
|
53
|
+
if (fileSize > fiftyMB) {
|
|
54
|
+
log.log(` ✗ ${file.name} - File too large (${(fileSize / 1024 / 1024).toFixed(1)}MB)`);
|
|
55
|
+
checks.push({ name: file.name, status: 'fail', error: 'File size exceeds 50MB limit' });
|
|
56
|
+
failed++;
|
|
57
|
+
throw ErrorTemplates.fileTooLarge(file.path, fileSize / 1024 / 1024);
|
|
58
|
+
} else if (fileSize > fiveMB) {
|
|
59
|
+
warnings.push(`${file.name} is large (${(fileSize / 1024 / 1024).toFixed(1)}MB). Consider splitting content into multiple files.`);
|
|
60
|
+
}
|
|
61
|
+
|
|
34
62
|
log.log(` ✓ ${file.name}`);
|
|
35
63
|
checks.push({ name: file.name, status: 'pass' });
|
|
36
64
|
passed++;
|
|
@@ -177,9 +205,10 @@ export async function check(options) {
|
|
|
177
205
|
if (failed > 0) {
|
|
178
206
|
log.log(` ✗ Failed: ${failed}`);
|
|
179
207
|
}
|
|
180
|
-
const
|
|
181
|
-
if (warnings > 0) {
|
|
182
|
-
log.log(` ⚠ Warnings: ${warnings}`);
|
|
208
|
+
const warningCount = checks.filter(c => c.status === 'warn').length;
|
|
209
|
+
if (warningCount > 0 || warnings.length > 0) {
|
|
210
|
+
log.log(` ⚠ Warnings: ${warningCount + warnings.length}`);
|
|
211
|
+
warnings.forEach(w => log.warn(` • ${w}`));
|
|
183
212
|
}
|
|
184
213
|
|
|
185
214
|
log.blank();
|
|
@@ -209,10 +238,13 @@ export async function check(options) {
|
|
|
209
238
|
// Exit with appropriate code
|
|
210
239
|
if (failed > 0) {
|
|
211
240
|
log.error('Health check failed');
|
|
212
|
-
|
|
213
|
-
|
|
241
|
+
const failedList = checks.filter(c => c.status === 'fail').map(c => c.name);
|
|
242
|
+
throw ErrorTemplates.validationFailed(failed, failedList);
|
|
243
|
+
} else if (warningCount > 0 || warnings.length > 0) {
|
|
214
244
|
log.warn('Health check passed with warnings');
|
|
245
|
+
return { checks, passed, failed, warnings }; // Return for programmatic use
|
|
215
246
|
} else {
|
|
216
247
|
log.success('Health check passed');
|
|
248
|
+
return { checks, passed, failed, warnings }; // Return for programmatic use
|
|
217
249
|
}
|
|
218
250
|
}
|
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
import fs from 'fs';
|
|
5
5
|
import path from 'path';
|
|
6
6
|
import ora from 'ora';
|
|
7
|
-
import { createLogger } from '../../logger.js';
|
|
8
7
|
import { getPackageDir, copyTemplate } from '../../utils.js';
|
|
9
8
|
import { getProjectTypeName, getLanguageName, getFrameworkName, buildValidationMatrix, TEMPLATE_PATHS } from './index.js';
|
|
10
9
|
|
package/lib/commands/migrate.js
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import inquirer from 'inquirer';
|
|
4
|
+
import ora from 'ora';
|
|
4
5
|
import { createLogger } from '../logger.js';
|
|
5
6
|
import { scan } from './scan.js';
|
|
6
7
|
import { installAgents } from './install-agents.js';
|
|
7
8
|
import { installSkills } from './install-skills.js';
|
|
8
|
-
import { getPackageDir, copyTemplate, displayAIPrompt } from '../utils.js';
|
|
9
|
+
import { getPackageDir, copyTemplate, displayAIPrompt, FileTracker } from '../utils.js';
|
|
9
10
|
|
|
10
11
|
export async function migrate(options) {
|
|
11
12
|
const targetDir = path.resolve(options.dir);
|
|
12
13
|
const essentialsFile = options.essentials || 'CODEBASE_ESSENTIALS.md';
|
|
13
14
|
const log = createLogger(false);
|
|
15
|
+
const tracker = new FileTracker();
|
|
14
16
|
|
|
15
17
|
log.blank();
|
|
16
18
|
log.header('Knowledge System Migration (Existing Project)', '🚀');
|
|
@@ -35,13 +37,23 @@ export async function migrate(options) {
|
|
|
35
37
|
return;
|
|
36
38
|
}
|
|
37
39
|
|
|
40
|
+
try {
|
|
41
|
+
|
|
38
42
|
// Step 1: Scan codebase
|
|
39
43
|
log.blank();
|
|
40
44
|
log.cyan('══════════════════════════════════════════');
|
|
41
45
|
log.header('Step 1/5: Scanning codebase', '');
|
|
42
46
|
log.cyan('══════════════════════════════════════════');
|
|
43
47
|
|
|
48
|
+
// Create single spinner for entire migration workflow (Steps 1-5)
|
|
49
|
+
// Reused across automated phases (skipped for Step 2's interactive prompts)
|
|
50
|
+
const spinner = ora('Scanning codebase...').start();
|
|
44
51
|
const _findings = await scan({ dir: targetDir, output: 'CODEBASE_ESSENTIALS.draft.md' });
|
|
52
|
+
spinner.succeed('Codebase scan complete');
|
|
53
|
+
|
|
54
|
+
// Track draft file for rollback if migration fails
|
|
55
|
+
const draftPath = path.join(targetDir, 'CODEBASE_ESSENTIALS.draft.md');
|
|
56
|
+
tracker.trackFile(draftPath);
|
|
45
57
|
|
|
46
58
|
// Step 2: Review draft
|
|
47
59
|
log.blank();
|
|
@@ -68,18 +80,18 @@ export async function migrate(options) {
|
|
|
68
80
|
}]);
|
|
69
81
|
|
|
70
82
|
const essentialsPath = path.join(targetDir, essentialsFile);
|
|
71
|
-
const draftPath = path.join(targetDir, 'CODEBASE_ESSENTIALS.draft.md');
|
|
72
83
|
|
|
73
84
|
if (!reviewed && !fs.existsSync(essentialsPath)) {
|
|
74
85
|
const { action } = await inquirer.prompt([{
|
|
75
|
-
type: '
|
|
86
|
+
type: 'rawlist',
|
|
76
87
|
name: 'action',
|
|
77
88
|
message: `${essentialsFile} not found. What would you like to do?`,
|
|
78
89
|
choices: [
|
|
79
90
|
{ name: 'Rename draft file now and continue', value: 'rename' },
|
|
80
91
|
{ name: 'Continue anyway (not recommended)', value: 'continue' },
|
|
81
92
|
{ name: 'Exit and complete the file first', value: 'exit' }
|
|
82
|
-
]
|
|
93
|
+
],
|
|
94
|
+
default: 1
|
|
83
95
|
}]);
|
|
84
96
|
|
|
85
97
|
if (action === 'exit') {
|
|
@@ -87,8 +99,9 @@ export async function migrate(options) {
|
|
|
87
99
|
return;
|
|
88
100
|
}
|
|
89
101
|
|
|
90
|
-
|
|
91
|
-
|
|
102
|
+
const draftFilePath = path.join(targetDir, 'CODEBASE_ESSENTIALS.draft.md');
|
|
103
|
+
if (action === 'rename' && fs.existsSync(draftFilePath)) {
|
|
104
|
+
fs.renameSync(draftFilePath, essentialsPath);
|
|
92
105
|
log.success('✅ Renamed to CODEBASE_ESSENTIALS.md');
|
|
93
106
|
}
|
|
94
107
|
}
|
|
@@ -99,6 +112,7 @@ export async function migrate(options) {
|
|
|
99
112
|
log.header('Step 3/5: Creating AGENTS.md', '');
|
|
100
113
|
log.cyan('══════════════════════════════════════════');
|
|
101
114
|
|
|
115
|
+
spinner.start('Creating AGENTS.md...');
|
|
102
116
|
const agentsPath = path.join(targetDir, 'AGENTS.md');
|
|
103
117
|
if (!fs.existsSync(agentsPath)) {
|
|
104
118
|
const packageDir = getPackageDir();
|
|
@@ -106,8 +120,11 @@ export async function migrate(options) {
|
|
|
106
120
|
path.join(packageDir, 'templates', 'AGENTS.template.md'),
|
|
107
121
|
agentsPath
|
|
108
122
|
);
|
|
123
|
+
tracker.trackFile(agentsPath);
|
|
124
|
+
spinner.succeed('AGENTS.md created');
|
|
109
125
|
log.success('✅ AGENTS.md created (customize validation matrix as needed)');
|
|
110
126
|
} else {
|
|
127
|
+
spinner.info('AGENTS.md already exists');
|
|
111
128
|
log.dim('⏭️ AGENTS.md already exists, skipping');
|
|
112
129
|
}
|
|
113
130
|
|
|
@@ -117,7 +134,9 @@ export async function migrate(options) {
|
|
|
117
134
|
log.header('Step 4/5: Installing custom agents', '');
|
|
118
135
|
log.cyan('══════════════════════════════════════════');
|
|
119
136
|
|
|
137
|
+
spinner.start('Installing custom agents...');
|
|
120
138
|
await installAgents({ dir: targetDir, essentials: essentialsFile });
|
|
139
|
+
spinner.succeed('Custom agents installed');
|
|
121
140
|
|
|
122
141
|
// Step 5: Install skills
|
|
123
142
|
log.blank();
|
|
@@ -125,7 +144,9 @@ export async function migrate(options) {
|
|
|
125
144
|
log.header('Step 5/5: Installing universal skills', '');
|
|
126
145
|
log.cyan('══════════════════════════════════════════');
|
|
127
146
|
|
|
147
|
+
spinner.start('Installing universal skills...');
|
|
128
148
|
await installSkills({ dir: targetDir });
|
|
149
|
+
spinner.succeed('Universal skills installed');
|
|
129
150
|
|
|
130
151
|
// Initialize changelog
|
|
131
152
|
const changelogPath = path.join(targetDir, 'CODEBASE_CHANGELOG.md');
|
|
@@ -140,6 +161,7 @@ export async function migrate(options) {
|
|
|
140
161
|
'{{DATE}}': new Date().toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' })
|
|
141
162
|
}
|
|
142
163
|
);
|
|
164
|
+
tracker.trackFile(changelogPath);
|
|
143
165
|
log.success('✅ CODEBASE_CHANGELOG.md initialized');
|
|
144
166
|
}
|
|
145
167
|
|
|
@@ -172,4 +194,9 @@ export async function migrate(options) {
|
|
|
172
194
|
'5. Explaining architecture decisions',
|
|
173
195
|
'Make it project-specific, not a template!"'
|
|
174
196
|
]);
|
|
197
|
+
} catch (error) {
|
|
198
|
+
log.error(`Migration failed: ${error.message}`);
|
|
199
|
+
await tracker.rollback(log);
|
|
200
|
+
throw error;
|
|
201
|
+
}
|
|
175
202
|
}
|
package/lib/commands/scan.js
CHANGED
|
@@ -225,10 +225,11 @@ export async function scan(options) {
|
|
|
225
225
|
|
|
226
226
|
// Scan for common pattern directories
|
|
227
227
|
const commonDirs = ['src', 'lib', 'server', 'backend', 'api', 'app'];
|
|
228
|
+
let filesScanned = 0;
|
|
228
229
|
for (const dir of commonDirs) {
|
|
229
230
|
const dirPath = path.join(targetDir, dir);
|
|
230
231
|
if (fs.existsSync(dirPath)) {
|
|
231
|
-
scanForPatterns(dirPath, findings);
|
|
232
|
+
filesScanned = scanForPatterns(dirPath, findings, spinner, filesScanned);
|
|
232
233
|
}
|
|
233
234
|
}
|
|
234
235
|
|
|
@@ -270,7 +271,7 @@ export async function scan(options) {
|
|
|
270
271
|
log.log('\x1b[33m\x1b[1m📝 Next Steps:\x1b[0m');
|
|
271
272
|
log.white(` 1. Review and complete TODO sections in ${outputFile}`);
|
|
272
273
|
log.white(' 2. Rename to CODEBASE_ESSENTIALS.md when ready');
|
|
273
|
-
log.white(
|
|
274
|
+
log.white(' 3. Run: ');
|
|
274
275
|
log.cyan(' npx aiknowsys install-agents');
|
|
275
276
|
log.blank();
|
|
276
277
|
|
|
@@ -294,9 +295,9 @@ export async function scan(options) {
|
|
|
294
295
|
}
|
|
295
296
|
}
|
|
296
297
|
|
|
297
|
-
function scanForPatterns(dir, findings, depth = 0) {
|
|
298
|
+
function scanForPatterns(dir, findings, spinner = null, filesScanned = 0, depth = 0) {
|
|
298
299
|
// Limit recursion depth to avoid performance issues
|
|
299
|
-
if (depth > 2) return;
|
|
300
|
+
if (depth > 2) return filesScanned;
|
|
300
301
|
|
|
301
302
|
try {
|
|
302
303
|
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
@@ -316,8 +317,15 @@ function scanForPatterns(dir, findings, depth = 0) {
|
|
|
316
317
|
if (entry.name === 'middleware' || entry.name === 'auth') findings.patterns.hasAuthMiddleware = true;
|
|
317
318
|
|
|
318
319
|
// Recurse into subdirectories
|
|
319
|
-
scanForPatterns(fullPath, findings, depth + 1);
|
|
320
|
+
filesScanned = scanForPatterns(fullPath, findings, spinner, filesScanned, depth + 1);
|
|
320
321
|
} else if (entry.isFile() && (entry.name.endsWith('.js') || entry.name.endsWith('.ts'))) {
|
|
322
|
+
filesScanned++;
|
|
323
|
+
|
|
324
|
+
// Update spinner text every 50 files for better performance
|
|
325
|
+
if (spinner && filesScanned % 50 === 0) {
|
|
326
|
+
spinner.text = `Analyzing codebase... (${filesScanned} files scanned)`;
|
|
327
|
+
}
|
|
328
|
+
|
|
321
329
|
// Quick scan of file content for patterns
|
|
322
330
|
try {
|
|
323
331
|
const content = fs.readFileSync(fullPath, 'utf-8');
|
|
@@ -343,6 +351,8 @@ function scanForPatterns(dir, findings, depth = 0) {
|
|
|
343
351
|
} catch (_e) {
|
|
344
352
|
// Skip directories we can't read
|
|
345
353
|
}
|
|
354
|
+
|
|
355
|
+
return filesScanned;
|
|
346
356
|
}
|
|
347
357
|
|
|
348
358
|
function generateEssentialsDraft(findings) {
|
package/lib/commands/sync.js
CHANGED
|
@@ -2,6 +2,7 @@ import fs from 'fs';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import ora from 'ora';
|
|
4
4
|
import { createLogger } from '../logger.js';
|
|
5
|
+
import { ErrorTemplates } from '../error-helpers.js';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Sync command - Syncs AGENTS.md validation reference with CODEBASE_ESSENTIALS.md
|
|
@@ -22,15 +23,11 @@ export async function sync(options) {
|
|
|
22
23
|
|
|
23
24
|
// Check files exist
|
|
24
25
|
if (!fs.existsSync(essentialsPath)) {
|
|
25
|
-
|
|
26
|
-
log.dim(' Run: npx aiknowsys init');
|
|
27
|
-
throw new Error(`${essentialsFile} not found`);
|
|
26
|
+
throw ErrorTemplates.fileNotFound(essentialsFile, ['npx aiknowsys init']);
|
|
28
27
|
}
|
|
29
28
|
|
|
30
29
|
if (!fs.existsSync(agentsPath)) {
|
|
31
|
-
|
|
32
|
-
log.dim(' Run: npx aiknowsys init');
|
|
33
|
-
throw new Error('AGENTS.md not found');
|
|
30
|
+
throw ErrorTemplates.fileNotFound('AGENTS.md', ['npx aiknowsys init']);
|
|
34
31
|
}
|
|
35
32
|
|
|
36
33
|
const spinner = silent ? null : ora('Checking validation matrix...').start();
|
|
@@ -42,9 +39,7 @@ export async function sync(options) {
|
|
|
42
39
|
|
|
43
40
|
if (!hasValidationMatrix) {
|
|
44
41
|
if (spinner) spinner.fail('Validation matrix not found in CODEBASE_ESSENTIALS.md');
|
|
45
|
-
|
|
46
|
-
log.info('Add a Validation Matrix section to CODEBASE_ESSENTIALS.md');
|
|
47
|
-
throw new Error('Validation matrix not found in CODEBASE_ESSENTIALS.md');
|
|
42
|
+
throw ErrorTemplates.missingSection('Validation Matrix', essentialsFile);
|
|
48
43
|
}
|
|
49
44
|
|
|
50
45
|
if (spinner) spinner.text = 'Reading AGENTS.md...';
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error handling utilities for AIKnowSys
|
|
3
|
+
* Provides structured errors with helpful suggestions and documentation links
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* AIKnowSys structured error with helpful suggestions
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* throw new AIKnowSysError(
|
|
11
|
+
* 'CODEBASE_ESSENTIALS.md not found',
|
|
12
|
+
* 'Create it by running:\n aiknowsys scan (from existing code)\n aiknowsys init (from scratch)',
|
|
13
|
+
* 'https://github.com/arpa73/AIKnowSys#getting-started'
|
|
14
|
+
* );
|
|
15
|
+
*/
|
|
16
|
+
export class AIKnowSysError extends Error {
|
|
17
|
+
/**
|
|
18
|
+
* @param {string} message - What went wrong
|
|
19
|
+
* @param {string} suggestion - How to fix it
|
|
20
|
+
* @param {string} learnMore - URL to documentation (optional)
|
|
21
|
+
*/
|
|
22
|
+
constructor(message, suggestion, learnMore = null) {
|
|
23
|
+
super(message);
|
|
24
|
+
this.suggestion = suggestion;
|
|
25
|
+
this.learnMore = learnMore;
|
|
26
|
+
this.name = 'AIKnowSysError';
|
|
27
|
+
|
|
28
|
+
// Maintain proper stack trace (V8 engine)
|
|
29
|
+
if (Error.captureStackTrace) {
|
|
30
|
+
Error.captureStackTrace(this, AIKnowSysError);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Format error with logger for consistent display
|
|
36
|
+
* @param {object} log - Logger instance with error(), info() methods
|
|
37
|
+
*/
|
|
38
|
+
format(log) {
|
|
39
|
+
log.error(this.message);
|
|
40
|
+
log.blank();
|
|
41
|
+
|
|
42
|
+
if (this.suggestion) {
|
|
43
|
+
log.info('💡 How to fix:');
|
|
44
|
+
// Split multi-line suggestions and indent them
|
|
45
|
+
const lines = this.suggestion.split('\n');
|
|
46
|
+
lines.forEach(line => {
|
|
47
|
+
if (line.trim()) {
|
|
48
|
+
log.white(` ${line}`);
|
|
49
|
+
} else {
|
|
50
|
+
log.blank();
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
log.blank();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (this.learnMore) {
|
|
57
|
+
log.cyan(`📚 Learn more: ${this.learnMore}`);
|
|
58
|
+
log.blank();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Get plain text version (for silent mode or testing)
|
|
64
|
+
* @returns {string} Formatted error message
|
|
65
|
+
*/
|
|
66
|
+
toPlainText() {
|
|
67
|
+
let text = `✗ ${this.message}\n`;
|
|
68
|
+
|
|
69
|
+
if (this.suggestion) {
|
|
70
|
+
text += `\n💡 How to fix:\n${this.suggestion}\n`;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (this.learnMore) {
|
|
74
|
+
text += `\n📚 Learn more: ${this.learnMore}\n`;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return text;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Common error templates for consistency
|
|
83
|
+
*/
|
|
84
|
+
export const ErrorTemplates = {
|
|
85
|
+
/**
|
|
86
|
+
* File not found error
|
|
87
|
+
* @param {string} filename - Name of missing file
|
|
88
|
+
* @param {string[]} suggestions - Array of command suggestions
|
|
89
|
+
* @returns {AIKnowSysError}
|
|
90
|
+
*/
|
|
91
|
+
fileNotFound(filename, suggestions = []) {
|
|
92
|
+
const defaultSuggestions = [
|
|
93
|
+
'aiknowsys scan # Generate from existing codebase',
|
|
94
|
+
'aiknowsys init # Start from scratch',
|
|
95
|
+
];
|
|
96
|
+
|
|
97
|
+
const suggestionText = (suggestions.length > 0 ? suggestions : defaultSuggestions)
|
|
98
|
+
.map((s, i) => `${i + 1}. ${s}`)
|
|
99
|
+
.join('\n');
|
|
100
|
+
|
|
101
|
+
return new AIKnowSysError(
|
|
102
|
+
`${filename} not found`,
|
|
103
|
+
`This file is required for AIKnowSys to work. Create it by running:\n\n${suggestionText}`,
|
|
104
|
+
'https://github.com/arpa73/AIKnowSys#getting-started'
|
|
105
|
+
);
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Empty file error
|
|
110
|
+
* @param {string} filename - Name of empty file
|
|
111
|
+
* @returns {AIKnowSysError}
|
|
112
|
+
*/
|
|
113
|
+
emptyFile(filename) {
|
|
114
|
+
return new AIKnowSysError(
|
|
115
|
+
`${filename} is empty or has no content`,
|
|
116
|
+
'Generate content by running:\n\n1. aiknowsys scan # Analyze existing codebase\n2. Fill in manually # Copy template and customize',
|
|
117
|
+
'https://github.com/arpa73/AIKnowSys#scanning-existing-projects'
|
|
118
|
+
);
|
|
119
|
+
},
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* File too large error
|
|
123
|
+
* @param {string} filename - Name of large file
|
|
124
|
+
* @param {number} sizeMB - File size in megabytes
|
|
125
|
+
* @returns {AIKnowSysError}
|
|
126
|
+
*/
|
|
127
|
+
fileTooLarge(filename, sizeMB) {
|
|
128
|
+
return new AIKnowSysError(
|
|
129
|
+
`${filename} is too large (${sizeMB.toFixed(1)}MB)`,
|
|
130
|
+
'Files larger than 50MB can cause performance issues.\n\nConsider:\n1. Split content into multiple files\n2. Move detailed examples to separate docs\n3. Use references/links instead of inline content',
|
|
131
|
+
'https://github.com/arpa73/AIKnowSys/wiki/Best-Practices#keeping-files-lean'
|
|
132
|
+
);
|
|
133
|
+
},
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Missing section error
|
|
137
|
+
* @param {string} section - Name of missing section
|
|
138
|
+
* @param {string} filename - File that should contain the section
|
|
139
|
+
* @returns {AIKnowSysError}
|
|
140
|
+
*/
|
|
141
|
+
missingSection(section, filename) {
|
|
142
|
+
return new AIKnowSysError(
|
|
143
|
+
`Required section "${section}" not found in ${filename}`,
|
|
144
|
+
`Add this section to your ${filename}:\n\n1. Copy from template:\n cp node_modules/aiknowsys/templates/${filename.replace('.md', '.template.md')} ./${filename}\n\n2. Or update to latest:\n aiknowsys update --templates`,
|
|
145
|
+
'https://github.com/arpa73/AIKnowSys#codebase-essentials-structure'
|
|
146
|
+
);
|
|
147
|
+
},
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Validation failed error
|
|
151
|
+
* @param {number} failedCount - Number of failed checks
|
|
152
|
+
* @param {string[]} failures - Array of failure descriptions
|
|
153
|
+
* @returns {AIKnowSysError}
|
|
154
|
+
*/
|
|
155
|
+
validationFailed(failedCount, failures = []) {
|
|
156
|
+
const failureList = failures.length > 0
|
|
157
|
+
? failures.map((f, i) => `${i + 1}. ${f}`).join('\n')
|
|
158
|
+
: 'See details above';
|
|
159
|
+
|
|
160
|
+
return new AIKnowSysError(
|
|
161
|
+
`Health check failed: ${failedCount} check(s) failed`,
|
|
162
|
+
`Fix the following issues:\n\n${failureList}\n\nThen run:\n aiknowsys check # Verify fixes`,
|
|
163
|
+
'https://github.com/arpa73/AIKnowSys#health-checks'
|
|
164
|
+
);
|
|
165
|
+
},
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* No knowledge system found error
|
|
169
|
+
* @returns {AIKnowSysError}
|
|
170
|
+
*/
|
|
171
|
+
noKnowledgeSystem() {
|
|
172
|
+
return new AIKnowSysError(
|
|
173
|
+
'No knowledge system found in this directory',
|
|
174
|
+
'Initialize AIKnowSys by running:\n\n1. aiknowsys init # Interactive setup\n2. aiknowsys init --yes # Quick setup with defaults\n3. aiknowsys migrate # Migrate existing project',
|
|
175
|
+
'https://github.com/arpa73/AIKnowSys#quick-start'
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
};
|
package/lib/sanitize.js
CHANGED
|
@@ -38,6 +38,22 @@ export function sanitizeProjectName(name) {
|
|
|
38
38
|
errors.push('Project name can only contain letters, numbers, hyphens, underscores, and dots');
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
// Check for emoji and special Unicode characters
|
|
42
|
+
// Emoji range: U+1F300 to U+1F9FF (and others)
|
|
43
|
+
if (/[\u{1F300}-\u{1F9FF}\u{2600}-\u{26FF}\u{2700}-\u{27BF}]/u.test(trimmed)) {
|
|
44
|
+
errors.push('Project name cannot contain emoji or special Unicode characters');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Check for npm reserved names
|
|
48
|
+
const reservedNames = [
|
|
49
|
+
'node_modules', 'favicon.ico', 'node', 'npm', 'package', 'readme',
|
|
50
|
+
'license', 'changelog', 'test', 'tests', 'example', 'examples'
|
|
51
|
+
];
|
|
52
|
+
const lowercaseName = trimmed.toLowerCase().replace(/^@[^/]+\//, ''); // Remove scope
|
|
53
|
+
if (reservedNames.includes(lowercaseName)) {
|
|
54
|
+
errors.push(`Project name '${lowercaseName}' is reserved by npm`);
|
|
55
|
+
}
|
|
56
|
+
|
|
41
57
|
// Check for leading/trailing special characters
|
|
42
58
|
if (/^[._-]/.test(trimmed)) {
|
|
43
59
|
errors.push('Project name cannot start with a dot, hyphen, or underscore');
|
|
@@ -198,7 +214,7 @@ export function validatePathTraversal(basePath, userPath) {
|
|
|
198
214
|
}
|
|
199
215
|
|
|
200
216
|
// Normalize paths to prevent traversal
|
|
201
|
-
const
|
|
217
|
+
const _normalizedBase = basePath.replace(/\\/g, '/').replace(/\/+$/, '');
|
|
202
218
|
const normalizedUser = userPath.replace(/\\/g, '/');
|
|
203
219
|
|
|
204
220
|
// Check for absolute paths (should be relative to base)
|
package/package.json
CHANGED
|
@@ -258,36 +258,7 @@ Follow patterns from CODEBASE_ESSENTIALS.md and the skill you read.
|
|
|
258
258
|
2. Use skill format with clear trigger words
|
|
259
259
|
3. Document the pattern for future reuse
|
|
260
260
|
|
|
261
|
-
**
|
|
262
|
-
```markdown
|
|
263
|
-
# Learned Skill: Django Query Optimization Pattern
|
|
264
|
-
|
|
265
|
-
**Pattern Type:** project_specific
|
|
266
|
-
**Created:** {{DATE}}
|
|
267
|
-
**Trigger Words:** "slow query", "n+1 problem", "django performance"
|
|
268
|
-
|
|
269
|
-
## When to Use
|
|
270
|
-
Use when encountering slow Django queries with related objects.
|
|
271
|
-
|
|
272
|
-
## Pattern
|
|
273
|
-
Always use select_related() for foreign keys and prefetch_related() for many-to-many.
|
|
274
|
-
|
|
275
|
-
\```python
|
|
276
|
-
# ❌ N+1 query problem
|
|
277
|
-
users = User.objects.all()
|
|
278
|
-
for user in users:
|
|
279
|
-
print(user.profile.bio) # Query per user!
|
|
280
|
-
|
|
281
|
-
# ✅ Optimized with select_related
|
|
282
|
-
users = User.objects.select_related('profile').all()
|
|
283
|
-
for user in users:
|
|
284
|
-
print(user.profile.bio) # Single query!
|
|
285
|
-
\```
|
|
286
|
-
|
|
287
|
-
## Related
|
|
288
|
-
- Django ORM documentation
|
|
289
|
-
- Performance monitoring with django-debug-toolbar
|
|
290
|
-
```
|
|
261
|
+
**See `.github/skills/skill-creator/SKILL.md` for detailed format and examples.**
|
|
291
262
|
|
|
292
263
|
**Pattern Types:**
|
|
293
264
|
- `error_resolution` - How specific errors were fixed
|
|
@@ -360,100 +331,6 @@ This project uses Developer + Architect agents for automated code review.
|
|
|
360
331
|
|
|
361
332
|
---
|
|
362
333
|
|
|
363
|
-
## ✅ Pre-Commit Validation Checklist
|
|
364
|
-
|
|
365
|
-
**Run this checklist BEFORE every commit. Copy-paste into terminal:**
|
|
366
|
-
|
|
367
|
-
### Quick Check (1 minute)
|
|
368
|
-
```bash
|
|
369
|
-
# 1. Validation Matrix commands
|
|
370
|
-
{{VALIDATION_CMD_1}} # e.g., npm test
|
|
371
|
-
{{VALIDATION_CMD_2}} # e.g., npm run lint
|
|
372
|
-
|
|
373
|
-
# 2. No debug code
|
|
374
|
-
grep -r "console.log\|debugger\|TODO:" src/ || echo "✓ No debug code"
|
|
375
|
-
|
|
376
|
-
# 3. No secrets
|
|
377
|
-
grep -r "password\|api_key\|secret" . --exclude-dir={node_modules,.git} || echo "✓ No secrets"
|
|
378
|
-
```
|
|
379
|
-
|
|
380
|
-
### Full Check (5 minutes)
|
|
381
|
-
```bash
|
|
382
|
-
# Run all validation commands from matrix
|
|
383
|
-
{{ALL_VALIDATION_COMMANDS}}
|
|
384
|
-
|
|
385
|
-
# Additional checks
|
|
386
|
-
git status # No untracked files
|
|
387
|
-
git diff # Review changes
|
|
388
|
-
grep "{{" {{TEMPLATE_FILES}} # No unfilled placeholders
|
|
389
|
-
```
|
|
390
|
-
|
|
391
|
-
### Before Push
|
|
392
|
-
```bash
|
|
393
|
-
# Final validation
|
|
394
|
-
{{VALIDATION_CMD_1}}
|
|
395
|
-
|
|
396
|
-
# Check commits
|
|
397
|
-
git log origin/main..HEAD # Review commits
|
|
398
|
-
|
|
399
|
-
# Push
|
|
400
|
-
git push
|
|
401
|
-
```
|
|
402
|
-
|
|
403
|
-
---
|
|
404
|
-
|
|
405
|
-
## 🔍 Troubleshooting Validation Failures
|
|
406
|
-
|
|
407
|
-
### Tests Failing
|
|
408
|
-
1. Read error message carefully
|
|
409
|
-
2. Check CODEBASE_ESSENTIALS.md for test patterns
|
|
410
|
-
3. Review "Common Gotchas" section
|
|
411
|
-
4. Run single failing test: `{{SINGLE_TEST_CMD}}`
|
|
412
|
-
5. Check if pattern violated (Critical Invariants)
|
|
413
|
-
|
|
414
|
-
### Linting Errors
|
|
415
|
-
1. Auto-fix if possible: `{{AUTO_FIX_CMD}}`
|
|
416
|
-
2. Review Core Patterns for style rules
|
|
417
|
-
3. Don't disable rules - fix the code
|
|
418
|
-
4. If rule is wrong, update ESSENTIALS first
|
|
419
|
-
|
|
420
|
-
### Build Errors
|
|
421
|
-
1. Check dependency versions (Technology Stack section)
|
|
422
|
-
2. Clear cache: `{{CLEAR_CACHE_CMD}}`
|
|
423
|
-
3. Rebuild from scratch: `{{CLEAN_BUILD_CMD}}`
|
|
424
|
-
4. Check environment variables
|
|
425
|
-
|
|
426
|
-
---
|
|
427
|
-
|
|
428
|
-
## 📝 Customization Instructions
|
|
429
|
-
|
|
430
|
-
**This is a template file. To customize:**
|
|
431
|
-
|
|
432
|
-
1. **{{SKILL_MAPPING}}** - Add your project's skill trigger words
|
|
433
|
-
```markdown
|
|
434
|
-
| "refactor", "clean up" | code-refactoring | Test-driven refactoring |
|
|
435
|
-
| "update deps" | dependency-updates | Safe dependency updates |
|
|
436
|
-
| "write tests", "TDD", "test first" | tdd-workflow | Test-driven development |
|
|
437
|
-
```
|
|
438
|
-
|
|
439
|
-
2. **Validation Checklist Placeholders:**
|
|
440
|
-
- `{{VALIDATION_CMD_1}}` → Your primary test command (e.g., `npm test`)
|
|
441
|
-
- `{{VALIDATION_CMD_2}}` → Your lint command (e.g., `npm run lint`)
|
|
442
|
-
- `{{ALL_VALIDATION_COMMANDS}}` → All commands from validation matrix in ESSENTIALS
|
|
443
|
-
- `{{TEMPLATE_FILES}}` → Files to check for placeholders
|
|
444
|
-
- `{{SINGLE_TEST_CMD}}` → How to run one test (e.g., `npm test -- file.test.js`)
|
|
445
|
-
- `{{AUTO_FIX_CMD}}` → Auto-fix linting (e.g., `npm run lint:fix`)
|
|
446
|
-
- `{{CLEAR_CACHE_CMD}}` → Clear build cache
|
|
447
|
-
- `{{CLEAN_BUILD_CMD}}` → Clean rebuild command
|
|
448
|
-
|
|
449
|
-
3. **Add project-specific sections** as needed
|
|
450
|
-
|
|
451
|
-
4. **Remove placeholder text** and instructions
|
|
452
|
-
|
|
453
|
-
**Note:** Validation Matrix is in CODEBASE_ESSENTIALS.md - no need to duplicate it here.
|
|
454
|
-
|
|
455
|
-
---
|
|
456
|
-
|
|
457
334
|
*This file helps AI agents follow a consistent workflow: Read → Plan → Implement → Validate → Document → Confirm*
|
|
458
335
|
|
|
459
336
|
*Part of aiknowsys. See [README](README.md) and [SETUP_GUIDE.md](SETUP_GUIDE.md) for full documentation.*
|
|
@@ -110,6 +110,44 @@ To ensure your review feedback is preserved and actionable:
|
|
|
110
110
|
- Developer deletes PENDING_REVIEW.md after addressing issues
|
|
111
111
|
- Session file gets brief completion status (not full review text)
|
|
112
112
|
|
|
113
|
+
### Documentation Location Guidance (Read Before Reviewing!):
|
|
114
|
+
|
|
115
|
+
When recommending where to document patterns during your review, use this decision framework:
|
|
116
|
+
|
|
117
|
+
**Document in {{ESSENTIALS_FILE}} when:**
|
|
118
|
+
- ✅ **Critical Invariants**: Cannot be violated (ES modules only, no globals, etc.)
|
|
119
|
+
- ✅ **Core Patterns**: Used in EVERY file of that type (Logger, FileTracker, etc.)
|
|
120
|
+
- ✅ **Architecture Decisions**: Technology choices (Node 20+, framework selections, etc.)
|
|
121
|
+
- ✅ **Universal Rules**: Applies project-wide (KISS, DRY, test structure, etc.)
|
|
122
|
+
- ⚠️ **Size check**: ESSENTIALS getting large (>350 lines)? Consider moving to learned/
|
|
123
|
+
|
|
124
|
+
**Document in `.aiknowsys/learned/` when:**
|
|
125
|
+
- ✅ **Project-Specific Patterns**: Emerged from practice (not planned upfront)
|
|
126
|
+
- ✅ **Problem-Solution Pairs**: Recurring error with consistent fix
|
|
127
|
+
- ✅ **Workarounds**: Library/framework-specific solutions
|
|
128
|
+
- ✅ **Advanced Techniques**: Optional patterns that improve quality but aren't mandatory
|
|
129
|
+
- ✅ **Domain Knowledge**: Business logic patterns, API conventions, etc.
|
|
130
|
+
|
|
131
|
+
**Reasoning:**
|
|
132
|
+
- ESSENTIALS = "What AI MUST know before any change" (single source of truth)
|
|
133
|
+
- Learned = "What AI SHOULD know for this specific context" (discoverable via triggers)
|
|
134
|
+
- Keep ESSENTIALS lean (<350 lines ideal) so AI reads it every session
|
|
135
|
+
- Learned skills can be detailed without bloating core docs
|
|
136
|
+
|
|
137
|
+
**How to recommend:**
|
|
138
|
+
```markdown
|
|
139
|
+
**Recommendation:** Document this as a learned skill.
|
|
140
|
+
|
|
141
|
+
**Reasoning:**
|
|
142
|
+
- Pattern emerged from [context] implementation (not core architecture)
|
|
143
|
+
- [X] distinct patterns discovered through practice
|
|
144
|
+
- Optional technique that improves [aspect] but not mandatory
|
|
145
|
+
- {{ESSENTIALS_FILE}} already at [X] lines (over ideal 350)
|
|
146
|
+
- Fits Pattern Extraction Protocol in AGENTS.md
|
|
147
|
+
|
|
148
|
+
**Action:** Create `.aiknowsys/learned/pattern-name.md` using skill format.
|
|
149
|
+
```
|
|
150
|
+
|
|
113
151
|
### Additional Reminders to Developer:
|
|
114
152
|
After completing your review, remind the developer to:
|
|
115
153
|
- **Read PENDING_REVIEW.md:** "Detailed review written to `.aiknowsys/PENDING_REVIEW.md`"
|
|
@@ -0,0 +1,486 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: validation-troubleshooting
|
|
3
|
+
description: Universal troubleshooting guide for validation failures (tests, linting, builds). Use when tests fail, validation commands error, or build breaks. Framework-agnostic debugging strategies for common development workflow issues.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Validation Troubleshooting Skill
|
|
7
|
+
|
|
8
|
+
Step-by-step debugging guide for when validation commands fail.
|
|
9
|
+
|
|
10
|
+
## When to Use This Skill
|
|
11
|
+
|
|
12
|
+
Use when:
|
|
13
|
+
- Tests fail after making changes
|
|
14
|
+
- Validation commands from matrix error
|
|
15
|
+
- Build/compilation fails
|
|
16
|
+
- Linting errors appear
|
|
17
|
+
- Type checking fails
|
|
18
|
+
- User mentions: "tests failing", "validation error", "build broken", "lint error"
|
|
19
|
+
|
|
20
|
+
**Trigger words:** "test fail", "validation fail", "build error", "lint error", "type error", "won't compile"
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## 1. Test Failures
|
|
25
|
+
|
|
26
|
+
### Step 1: Read the Error Message Carefully
|
|
27
|
+
|
|
28
|
+
**DON'T:**
|
|
29
|
+
- ❌ Immediately modify code
|
|
30
|
+
- ❌ Delete failing tests
|
|
31
|
+
- ❌ Disable test runner
|
|
32
|
+
|
|
33
|
+
**DO:**
|
|
34
|
+
- ✅ Read full error output
|
|
35
|
+
- ✅ Note exact line number and file
|
|
36
|
+
- ✅ Identify what was expected vs actual
|
|
37
|
+
|
|
38
|
+
### Step 2: Isolate the Failure
|
|
39
|
+
|
|
40
|
+
Run only the failing test:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# Node.js
|
|
44
|
+
npm test -- path/to/test.test.js
|
|
45
|
+
|
|
46
|
+
# Python
|
|
47
|
+
pytest path/to/test.py::test_name -v
|
|
48
|
+
|
|
49
|
+
# Jest
|
|
50
|
+
npm test -- --testNamePattern="test name"
|
|
51
|
+
|
|
52
|
+
# Vitest
|
|
53
|
+
npx vitest path/to/test.test.ts
|
|
54
|
+
|
|
55
|
+
# Go
|
|
56
|
+
go test -run TestName ./...
|
|
57
|
+
|
|
58
|
+
# Rust
|
|
59
|
+
cargo test test_name
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Step 3: Check Test Expectations
|
|
63
|
+
|
|
64
|
+
**Common issues:**
|
|
65
|
+
- Test expects old behavior (need to update test)
|
|
66
|
+
- Implementation is wrong (fix code)
|
|
67
|
+
- Test setup/mocking is incorrect
|
|
68
|
+
- Environment/config mismatch
|
|
69
|
+
|
|
70
|
+
**Questions to ask:**
|
|
71
|
+
1. Is the test expectation still valid?
|
|
72
|
+
2. Did I change behavior the test depends on?
|
|
73
|
+
3. Are mocks/fixtures up to date?
|
|
74
|
+
4. Does the test match CODEBASE_ESSENTIALS.md patterns?
|
|
75
|
+
|
|
76
|
+
### Step 4: Debug the Test
|
|
77
|
+
|
|
78
|
+
**Add logging:**
|
|
79
|
+
```javascript
|
|
80
|
+
// JavaScript/TypeScript
|
|
81
|
+
console.log('Actual value:', result);
|
|
82
|
+
console.log('Expected:', expected);
|
|
83
|
+
|
|
84
|
+
// Python
|
|
85
|
+
print(f"Actual: {result}, Expected: {expected}")
|
|
86
|
+
|
|
87
|
+
// Rust
|
|
88
|
+
println!("Actual: {:?}, Expected: {:?}", result, expected);
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**Run in debug mode:**
|
|
92
|
+
```bash
|
|
93
|
+
# Node.js
|
|
94
|
+
node --inspect-brk node_modules/.bin/jest --runInBand
|
|
95
|
+
|
|
96
|
+
# Python
|
|
97
|
+
python -m pdb -m pytest path/to/test.py
|
|
98
|
+
|
|
99
|
+
# Rust
|
|
100
|
+
rust-gdb target/debug/test_binary
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Step 5: Fix and Validate
|
|
104
|
+
|
|
105
|
+
1. Fix the issue (code or test)
|
|
106
|
+
2. Run the single test again - should pass
|
|
107
|
+
3. Run full test suite - all should pass
|
|
108
|
+
4. Commit with clear message explaining the fix
|
|
109
|
+
|
|
110
|
+
**Never claim done without running full test suite!**
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## 2. Linting Errors
|
|
115
|
+
|
|
116
|
+
### Step 1: Identify Error Type
|
|
117
|
+
|
|
118
|
+
**Syntax errors:**
|
|
119
|
+
```
|
|
120
|
+
Parsing error: Unexpected token
|
|
121
|
+
Missing semicolon
|
|
122
|
+
Unexpected identifier
|
|
123
|
+
```
|
|
124
|
+
→ **Fix:** Correct syntax immediately
|
|
125
|
+
|
|
126
|
+
**Style violations:**
|
|
127
|
+
```
|
|
128
|
+
'variable' is assigned but never used
|
|
129
|
+
Missing trailing comma
|
|
130
|
+
Prefer const over let
|
|
131
|
+
```
|
|
132
|
+
→ **Fix:** Address or justify
|
|
133
|
+
|
|
134
|
+
**Security issues:**
|
|
135
|
+
```
|
|
136
|
+
Detected eval usage
|
|
137
|
+
Unsafe regex
|
|
138
|
+
XSS vulnerability
|
|
139
|
+
```
|
|
140
|
+
→ **Fix:** MUST fix, don't disable
|
|
141
|
+
|
|
142
|
+
### Step 2: Auto-Fix When Possible
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
# ESLint
|
|
146
|
+
npm run lint:fix
|
|
147
|
+
|
|
148
|
+
# Prettier
|
|
149
|
+
npm run format
|
|
150
|
+
|
|
151
|
+
# Black (Python)
|
|
152
|
+
black .
|
|
153
|
+
|
|
154
|
+
# Rustfmt
|
|
155
|
+
cargo fmt
|
|
156
|
+
|
|
157
|
+
# Go
|
|
158
|
+
go fmt ./...
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Step 3: Manual Fixes
|
|
162
|
+
|
|
163
|
+
**Read CODEBASE_ESSENTIALS.md for style rules:**
|
|
164
|
+
- Check "Code Patterns" section
|
|
165
|
+
- Verify naming conventions
|
|
166
|
+
- Review import ordering
|
|
167
|
+
|
|
168
|
+
**DON'T disable rules without justification:**
|
|
169
|
+
```javascript
|
|
170
|
+
// ❌ BAD - Disabling without reason
|
|
171
|
+
// eslint-disable-next-line no-console
|
|
172
|
+
console.log(data);
|
|
173
|
+
|
|
174
|
+
// ✅ GOOD - Justified exception
|
|
175
|
+
// eslint-disable-next-line no-console -- Debugging production issue #123
|
|
176
|
+
console.log(data);
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Step 4: Update Rules If Wrong
|
|
180
|
+
|
|
181
|
+
If rule conflicts with project patterns:
|
|
182
|
+
1. Discuss with team/review ESSENTIALS
|
|
183
|
+
2. Update linting config
|
|
184
|
+
3. Document change in CODEBASE_CHANGELOG.md
|
|
185
|
+
4. Update CODEBASE_ESSENTIALS.md if pattern changed
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## 3. Build/Compilation Errors
|
|
190
|
+
|
|
191
|
+
### Step 1: Check Error Category
|
|
192
|
+
|
|
193
|
+
**Dependency errors:**
|
|
194
|
+
```
|
|
195
|
+
Cannot find module 'package-name'
|
|
196
|
+
Module not found
|
|
197
|
+
Package not installed
|
|
198
|
+
```
|
|
199
|
+
→ **Fix:** Install dependencies
|
|
200
|
+
|
|
201
|
+
**Type errors:**
|
|
202
|
+
```
|
|
203
|
+
Type 'string' is not assignable to type 'number'
|
|
204
|
+
Property 'x' does not exist on type 'Y'
|
|
205
|
+
```
|
|
206
|
+
→ **Fix:** Correct types or update type definitions
|
|
207
|
+
|
|
208
|
+
**Configuration errors:**
|
|
209
|
+
```
|
|
210
|
+
Invalid configuration object
|
|
211
|
+
Missing environment variable
|
|
212
|
+
Unknown compiler option
|
|
213
|
+
```
|
|
214
|
+
→ **Fix:** Check config files
|
|
215
|
+
|
|
216
|
+
### Step 2: Clean Build
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
# JavaScript/TypeScript
|
|
220
|
+
rm -rf node_modules dist .cache
|
|
221
|
+
npm install
|
|
222
|
+
npm run build
|
|
223
|
+
|
|
224
|
+
# Python
|
|
225
|
+
rm -rf __pycache__ .pytest_cache dist
|
|
226
|
+
pip install -r requirements.txt
|
|
227
|
+
|
|
228
|
+
# Rust
|
|
229
|
+
cargo clean
|
|
230
|
+
cargo build
|
|
231
|
+
|
|
232
|
+
# Go
|
|
233
|
+
go clean -cache
|
|
234
|
+
go build
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### Step 3: Check Environment
|
|
238
|
+
|
|
239
|
+
**Verify versions match CODEBASE_ESSENTIALS.md:**
|
|
240
|
+
```bash
|
|
241
|
+
# Check Node.js version
|
|
242
|
+
node --version
|
|
243
|
+
|
|
244
|
+
# Check Python version
|
|
245
|
+
python --version
|
|
246
|
+
|
|
247
|
+
# Check Rust version
|
|
248
|
+
rustc --version
|
|
249
|
+
|
|
250
|
+
# Check Go version
|
|
251
|
+
go version
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
**Check environment variables:**
|
|
255
|
+
```bash
|
|
256
|
+
# List all env vars
|
|
257
|
+
printenv
|
|
258
|
+
|
|
259
|
+
# Check specific required vars
|
|
260
|
+
echo $NODE_ENV
|
|
261
|
+
echo $DATABASE_URL
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### Step 4: Incremental Debugging
|
|
265
|
+
|
|
266
|
+
1. Comment out recent changes
|
|
267
|
+
2. Build incrementally
|
|
268
|
+
3. Identify breaking change
|
|
269
|
+
4. Fix root cause
|
|
270
|
+
5. Uncomment and rebuild
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
## 4. Type Checking Errors
|
|
275
|
+
|
|
276
|
+
### Common TypeScript Issues
|
|
277
|
+
|
|
278
|
+
**Missing type definitions:**
|
|
279
|
+
```bash
|
|
280
|
+
npm install --save-dev @types/package-name
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
**Type mismatch:**
|
|
284
|
+
```typescript
|
|
285
|
+
// ❌ Wrong
|
|
286
|
+
const id: number = "123";
|
|
287
|
+
|
|
288
|
+
// ✅ Fix - convert type
|
|
289
|
+
const id: number = parseInt("123", 10);
|
|
290
|
+
|
|
291
|
+
// ✅ Or - fix type annotation
|
|
292
|
+
const id: string = "123";
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
**Null/undefined issues:**
|
|
296
|
+
```typescript
|
|
297
|
+
// ❌ Unsafe
|
|
298
|
+
const name = user.name;
|
|
299
|
+
|
|
300
|
+
// ✅ Safe - optional chaining
|
|
301
|
+
const name = user?.name;
|
|
302
|
+
|
|
303
|
+
// ✅ Safe - nullish coalescing
|
|
304
|
+
const name = user?.name ?? 'Unknown';
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### Common Python Type Issues
|
|
308
|
+
|
|
309
|
+
**mypy errors:**
|
|
310
|
+
```bash
|
|
311
|
+
# Run mypy
|
|
312
|
+
mypy src/
|
|
313
|
+
|
|
314
|
+
# Ignore specific line (rarely)
|
|
315
|
+
result = something() # type: ignore[attr-defined]
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
## 5. Validation Matrix Command Failures
|
|
321
|
+
|
|
322
|
+
### Strategy: Work Through Matrix Systematically
|
|
323
|
+
|
|
324
|
+
**From CODEBASE_ESSENTIALS.md validation matrix:**
|
|
325
|
+
|
|
326
|
+
```markdown
|
|
327
|
+
| Command | Purpose | Expected |
|
|
328
|
+
|---------|---------|----------|
|
|
329
|
+
| npm test | Run tests | All pass |
|
|
330
|
+
| npm run lint | Check style | No errors |
|
|
331
|
+
| npm run build | Compile | No errors |
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
**Run each command:**
|
|
335
|
+
1. If passes → ✅ Move to next
|
|
336
|
+
2. If fails → 🔴 Debug using sections above
|
|
337
|
+
3. Don't move forward until current command passes
|
|
338
|
+
|
|
339
|
+
### Example Debugging Session
|
|
340
|
+
|
|
341
|
+
```bash
|
|
342
|
+
# 1. Run tests
|
|
343
|
+
npm test
|
|
344
|
+
# ❌ FAIL - 2 tests failing
|
|
345
|
+
|
|
346
|
+
# 2. Isolate failure
|
|
347
|
+
npm test -- auth.test.js
|
|
348
|
+
# Read error, fix issue
|
|
349
|
+
|
|
350
|
+
# 3. Verify fix
|
|
351
|
+
npm test
|
|
352
|
+
# ✅ PASS - All tests green
|
|
353
|
+
|
|
354
|
+
# 4. Continue to next validation
|
|
355
|
+
npm run lint
|
|
356
|
+
# ✅ PASS - No issues
|
|
357
|
+
|
|
358
|
+
# 5. Final check
|
|
359
|
+
npm run build
|
|
360
|
+
# ✅ PASS - Build successful
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
**Rule: Fix failures in order, don't skip ahead!**
|
|
364
|
+
|
|
365
|
+
---
|
|
366
|
+
|
|
367
|
+
## 6. Common Patterns and Solutions
|
|
368
|
+
|
|
369
|
+
### Pattern: "Works on my machine"
|
|
370
|
+
|
|
371
|
+
**Cause:** Environment differences
|
|
372
|
+
|
|
373
|
+
**Solutions:**
|
|
374
|
+
1. Check Node.js/Python/Rust version matches team
|
|
375
|
+
2. Verify environment variables set correctly
|
|
376
|
+
3. Clear caches and reinstall dependencies
|
|
377
|
+
4. Check for OS-specific issues (Windows vs Unix paths)
|
|
378
|
+
5. Use Docker/containers for consistency
|
|
379
|
+
|
|
380
|
+
### Pattern: "Test passes locally, fails in CI"
|
|
381
|
+
|
|
382
|
+
**Cause:** CI environment differences
|
|
383
|
+
|
|
384
|
+
**Solutions:**
|
|
385
|
+
1. Check CI logs carefully
|
|
386
|
+
2. Verify CI environment variables
|
|
387
|
+
3. Check for timing issues (add proper waits)
|
|
388
|
+
4. Ensure deterministic test data
|
|
389
|
+
5. Check for missing CI dependencies
|
|
390
|
+
|
|
391
|
+
### Pattern: "Intermittent test failures"
|
|
392
|
+
|
|
393
|
+
**Cause:** Non-deterministic tests
|
|
394
|
+
|
|
395
|
+
**Solutions:**
|
|
396
|
+
1. Remove time-dependent logic
|
|
397
|
+
2. Fix race conditions
|
|
398
|
+
3. Mock random/date functions
|
|
399
|
+
4. Ensure proper cleanup between tests
|
|
400
|
+
5. Avoid shared state
|
|
401
|
+
|
|
402
|
+
### Pattern: "Everything broke after dependency update"
|
|
403
|
+
|
|
404
|
+
**Cause:** Breaking changes in dependency
|
|
405
|
+
|
|
406
|
+
**Solutions:**
|
|
407
|
+
1. Check dependency changelog
|
|
408
|
+
2. Revert to previous version temporarily
|
|
409
|
+
3. Read migration guide
|
|
410
|
+
4. Update code to new API
|
|
411
|
+
5. Consider alternative package
|
|
412
|
+
|
|
413
|
+
---
|
|
414
|
+
|
|
415
|
+
## Workflow Checklist
|
|
416
|
+
|
|
417
|
+
When validation fails:
|
|
418
|
+
|
|
419
|
+
- [ ] **Read error message** - Don't guess
|
|
420
|
+
- [ ] **Check CODEBASE_ESSENTIALS.md** - Verify patterns
|
|
421
|
+
- [ ] **Isolate the issue** - Run single test/command
|
|
422
|
+
- [ ] **Debug systematically** - Use tools, not trial-and-error
|
|
423
|
+
- [ ] **Fix root cause** - Not just symptoms
|
|
424
|
+
- [ ] **Verify fix** - Run full validation matrix
|
|
425
|
+
- [ ] **Document if needed** - Update docs if pattern unclear
|
|
426
|
+
- [ ] **Commit with context** - Explain what broke and why
|
|
427
|
+
|
|
428
|
+
---
|
|
429
|
+
|
|
430
|
+
## Never Do These
|
|
431
|
+
|
|
432
|
+
❌ **Delete tests to make them pass**
|
|
433
|
+
- Tests found a real issue
|
|
434
|
+
- Fix the code, not the test
|
|
435
|
+
|
|
436
|
+
❌ **Disable linting rules without justification**
|
|
437
|
+
- Rules exist for a reason
|
|
438
|
+
- Discuss with team first
|
|
439
|
+
|
|
440
|
+
❌ **Skip validation steps**
|
|
441
|
+
- "It probably works" is not good enough
|
|
442
|
+
- Run full matrix before claiming done
|
|
443
|
+
|
|
444
|
+
❌ **Commit broken code "to fix later"**
|
|
445
|
+
- Breaks team workflow
|
|
446
|
+
- Creates technical debt
|
|
447
|
+
|
|
448
|
+
❌ **Change multiple things at once**
|
|
449
|
+
- Can't identify root cause
|
|
450
|
+
- Fix one thing, validate, then next
|
|
451
|
+
|
|
452
|
+
---
|
|
453
|
+
|
|
454
|
+
## Quick Reference
|
|
455
|
+
|
|
456
|
+
### Emergency Triage
|
|
457
|
+
|
|
458
|
+
```bash
|
|
459
|
+
# 1. What broke?
|
|
460
|
+
git diff # Review recent changes
|
|
461
|
+
|
|
462
|
+
# 2. When did it break?
|
|
463
|
+
git log --oneline -10 # Recent commits
|
|
464
|
+
|
|
465
|
+
# 3. Revert if needed
|
|
466
|
+
git revert HEAD # Undo last commit safely
|
|
467
|
+
|
|
468
|
+
# 4. Isolate and fix
|
|
469
|
+
# Use sections above based on error type
|
|
470
|
+
|
|
471
|
+
# 5. Validate before moving on
|
|
472
|
+
npm test && npm run lint && npm run build
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
### Getting Unstuck
|
|
476
|
+
|
|
477
|
+
If stuck for >15 minutes:
|
|
478
|
+
1. Read error message again (slowly)
|
|
479
|
+
2. Check CODEBASE_ESSENTIALS.md for relevant pattern
|
|
480
|
+
3. Search project for similar code that works
|
|
481
|
+
4. Check dependency documentation
|
|
482
|
+
5. Ask for help (provide full error message)
|
|
483
|
+
|
|
484
|
+
---
|
|
485
|
+
|
|
486
|
+
*Framework-agnostic troubleshooting - adapt commands to your project's tech stack.*
|