Skip to content

Commit 7e41f34

Browse files
committed
fix(enhance): address code review issues for agent analyzer
- Clean up redundant agentPatterns.agentPatterns access pattern in agent-analyzer.js - Fix auto-fix filtering to use patternId matching instead of missing autoFixFn - Replace fs.readdirSync recursive option with compatible withFileTypes approach - Add null safety checks for frontmatter name/description trim() calls - Fix test cases for CoT pattern edge cases (word count and reasoning keyword)
1 parent 69b1f65 commit 7e41f34

4 files changed

Lines changed: 74 additions & 40 deletions

File tree

lib/enhance/agent-analyzer.js

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
const fs = require('fs');
1010
const path = require('path');
11-
const agentPatterns = require('./agent-patterns');
11+
const { agentPatterns } = require('./agent-patterns');
1212

1313
/**
1414
* Parse YAML frontmatter from markdown content
@@ -110,7 +110,7 @@ function analyzeAgent(agentPath, options = {}) {
110110
results.frontmatter = frontmatter;
111111

112112
// Check for missing frontmatter
113-
const missingFmPattern = agentPatterns.agentPatterns.missing_frontmatter;
113+
const missingFmPattern = agentPatterns.missing_frontmatter;
114114
const missingFmResult = missingFmPattern.check(content);
115115
if (missingFmResult) {
116116
results.structureIssues.push({
@@ -124,7 +124,7 @@ function analyzeAgent(agentPath, options = {}) {
124124
// If frontmatter exists, check its fields
125125
if (frontmatter) {
126126
// Check for missing name
127-
const missingNamePattern = agentPatterns.agentPatterns.missing_name;
127+
const missingNamePattern = agentPatterns.missing_name;
128128
const missingNameResult = missingNamePattern.check(frontmatter);
129129
if (missingNameResult) {
130130
results.structureIssues.push({
@@ -136,7 +136,7 @@ function analyzeAgent(agentPath, options = {}) {
136136
}
137137

138138
// Check for missing description
139-
const missingDescPattern = agentPatterns.agentPatterns.missing_description;
139+
const missingDescPattern = agentPatterns.missing_description;
140140
const missingDescResult = missingDescPattern.check(frontmatter);
141141
if (missingDescResult) {
142142
results.structureIssues.push({
@@ -148,7 +148,7 @@ function analyzeAgent(agentPath, options = {}) {
148148
}
149149

150150
// Check for unrestricted tools
151-
const unrestrictedToolsPattern = agentPatterns.agentPatterns.unrestricted_tools;
151+
const unrestrictedToolsPattern = agentPatterns.unrestricted_tools;
152152
const unrestrictedToolsResult = unrestrictedToolsPattern.check(frontmatter);
153153
if (unrestrictedToolsResult) {
154154
results.toolIssues.push({
@@ -160,7 +160,7 @@ function analyzeAgent(agentPath, options = {}) {
160160
}
161161

162162
// Check for unrestricted Bash
163-
const unrestrictedBashPattern = agentPatterns.agentPatterns.unrestricted_bash;
163+
const unrestrictedBashPattern = agentPatterns.unrestricted_bash;
164164
const unrestrictedBashResult = unrestrictedBashPattern.check(frontmatter);
165165
if (unrestrictedBashResult) {
166166
results.toolIssues.push({
@@ -174,7 +174,7 @@ function analyzeAgent(agentPath, options = {}) {
174174
}
175175

176176
// Check for missing role
177-
const missingRolePattern = agentPatterns.agentPatterns.missing_role;
177+
const missingRolePattern = agentPatterns.missing_role;
178178
const missingRoleResult = missingRolePattern.check(content);
179179
if (missingRoleResult) {
180180
results.structureIssues.push({
@@ -187,7 +187,7 @@ function analyzeAgent(agentPath, options = {}) {
187187
}
188188

189189
// Check for missing output format
190-
const missingOutputPattern = agentPatterns.agentPatterns.missing_output_format;
190+
const missingOutputPattern = agentPatterns.missing_output_format;
191191
const missingOutputResult = missingOutputPattern.check(content);
192192
if (missingOutputResult) {
193193
results.structureIssues.push({
@@ -199,7 +199,7 @@ function analyzeAgent(agentPath, options = {}) {
199199
}
200200

201201
// Check for missing constraints
202-
const missingConstraintsPattern = agentPatterns.agentPatterns.missing_constraints;
202+
const missingConstraintsPattern = agentPatterns.missing_constraints;
203203
const missingConstraintsResult = missingConstraintsPattern.check(content);
204204
if (missingConstraintsResult) {
205205
results.structureIssues.push({
@@ -211,7 +211,7 @@ function analyzeAgent(agentPath, options = {}) {
211211
}
212212

213213
// Check for missing XML structure
214-
const missingXmlPattern = agentPatterns.agentPatterns.missing_xml_structure;
214+
const missingXmlPattern = agentPatterns.missing_xml_structure;
215215
const missingXmlResult = missingXmlPattern.check(content);
216216
if (missingXmlResult && (options.verbose || missingXmlPattern.certainty !== 'LOW')) {
217217
results.xmlIssues.push({
@@ -223,7 +223,7 @@ function analyzeAgent(agentPath, options = {}) {
223223
}
224224

225225
// Check for unnecessary CoT
226-
const unnecessaryCotPattern = agentPatterns.agentPatterns.unnecessary_cot;
226+
const unnecessaryCotPattern = agentPatterns.unnecessary_cot;
227227
const unnecessaryCotResult = unnecessaryCotPattern.check(content);
228228
if (unnecessaryCotResult && (options.verbose || unnecessaryCotPattern.certainty !== 'LOW')) {
229229
results.cotIssues.push({
@@ -235,7 +235,7 @@ function analyzeAgent(agentPath, options = {}) {
235235
}
236236

237237
// Check for missing CoT
238-
const missingCotPattern = agentPatterns.agentPatterns.missing_cot;
238+
const missingCotPattern = agentPatterns.missing_cot;
239239
const missingCotResult = missingCotPattern.check(content);
240240
if (missingCotResult && (options.verbose || missingCotPattern.certainty !== 'LOW')) {
241241
results.cotIssues.push({
@@ -247,7 +247,7 @@ function analyzeAgent(agentPath, options = {}) {
247247
}
248248

249249
// Check example count
250-
const exampleCountPattern = agentPatterns.agentPatterns.example_count_suboptimal;
250+
const exampleCountPattern = agentPatterns.example_count_suboptimal;
251251
const exampleCountResult = exampleCountPattern.check(content);
252252
if (exampleCountResult && options.verbose) {
253253
results.exampleIssues.push({
@@ -259,7 +259,7 @@ function analyzeAgent(agentPath, options = {}) {
259259
}
260260

261261
// Check for vague instructions
262-
const vaguePattern = agentPatterns.agentPatterns.vague_instructions;
262+
const vaguePattern = agentPatterns.vague_instructions;
263263
const vagueResult = vaguePattern.check(content);
264264
if (vagueResult && (options.verbose || vaguePattern.certainty !== 'LOW')) {
265265
results.antiPatternIssues.push({
@@ -271,7 +271,7 @@ function analyzeAgent(agentPath, options = {}) {
271271
}
272272

273273
// Check for prompt bloat
274-
const bloatPattern = agentPatterns.agentPatterns.prompt_bloat;
274+
const bloatPattern = agentPatterns.prompt_bloat;
275275
const bloatResult = bloatPattern.check(content);
276276
if (bloatResult && options.verbose) {
277277
results.antiPatternIssues.push({

lib/enhance/agent-patterns.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ const agentPatterns = {
5050
check: (frontmatter) => {
5151
if (!frontmatter || typeof frontmatter !== 'object') return null;
5252

53-
if (!frontmatter.name || frontmatter.name.trim() === '') {
53+
if (!frontmatter.name || (typeof frontmatter.name === 'string' && frontmatter.name.trim() === '')) {
5454
return {
5555
issue: 'Frontmatter missing "name" field',
5656
fix: 'Add "name" field to frontmatter'
@@ -73,7 +73,7 @@ const agentPatterns = {
7373
check: (frontmatter) => {
7474
if (!frontmatter || typeof frontmatter !== 'object') return null;
7575

76-
if (!frontmatter.description || frontmatter.description.trim() === '') {
76+
if (!frontmatter.description || (typeof frontmatter.description === 'string' && frontmatter.description.trim() === '')) {
7777
return {
7878
issue: 'Frontmatter missing "description" field',
7979
fix: 'Add "description" field to frontmatter'

lib/enhance/fixer.js

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,28 @@ function applyFixes(issues, options = {}) {
2626
errors: []
2727
};
2828

29-
// Filter to only HIGH certainty issues with autoFixFn
29+
// Auto-fixable pattern IDs for markdown files
30+
const autoFixablePatternIds = [
31+
'missing_frontmatter',
32+
'unrestricted_bash',
33+
'missing_role'
34+
];
35+
36+
// Filter to only HIGH certainty issues that are auto-fixable
3037
const fixableIssues = issues.filter(i =>
31-
i.certainty === 'HIGH' && i.autoFixFn && i.filePath
38+
i.certainty === 'HIGH' &&
39+
(i.filePath || i.file) &&
40+
autoFixablePatternIds.includes(i.patternId)
3241
);
3342

3443
// Group by file to minimize reads/writes
3544
const byFile = new Map();
3645
for (const issue of fixableIssues) {
37-
if (!byFile.has(issue.filePath)) {
38-
byFile.set(issue.filePath, []);
46+
const fp = issue.filePath || issue.file;
47+
if (!byFile.has(fp)) {
48+
byFile.set(fp, []);
3949
}
40-
byFile.get(issue.filePath).push(issue);
50+
byFile.get(fp).push(issue);
4151
}
4252

4353
// Process each file
@@ -138,11 +148,11 @@ function applyFixes(issues, options = {}) {
138148

139149
// Add non-fixable issues to skipped
140150
const nonFixable = issues.filter(i =>
141-
i.certainty !== 'HIGH' || !i.autoFixFn
151+
i.certainty !== 'HIGH' || !autoFixablePatternIds.includes(i.patternId)
142152
);
143153
results.skipped.push(...nonFixable.map(i => ({
144154
...i,
145-
reason: i.certainty !== 'HIGH' ? 'Not HIGH certainty' : 'No auto-fix available'
155+
reason: i.certainty !== 'HIGH' ? 'Not HIGH certainty' : 'No auto-fix available for this pattern'
146156
})));
147157

148158
return results;
@@ -303,22 +313,35 @@ function restoreFromBackup(filePath) {
303313
function cleanupBackups(directory) {
304314
let count = 0;
305315

306-
const files = fs.readdirSync(directory, { recursive: true });
307-
for (const file of files) {
308-
if (file.endsWith('.backup')) {
309-
const fullPath = path.join(directory, file);
310-
try {
311-
const stat = fs.statSync(fullPath);
312-
if (stat.isFile()) {
316+
/**
317+
* Recursively find backup files
318+
* @param {string} dir - Directory to search
319+
*/
320+
function findBackups(dir) {
321+
let entries;
322+
try {
323+
entries = fs.readdirSync(dir, { withFileTypes: true });
324+
} catch (err) {
325+
// Directory not accessible, skip
326+
return;
327+
}
328+
329+
for (const entry of entries) {
330+
const fullPath = path.join(dir, entry.name);
331+
if (entry.isDirectory()) {
332+
findBackups(fullPath);
333+
} else if (entry.isFile() && entry.name.endsWith('.backup')) {
334+
try {
313335
fs.unlinkSync(fullPath);
314336
count++;
337+
} catch (err) {
338+
// File may have been removed already or not accessible
315339
}
316-
} catch (err) {
317-
// File may have been removed already
318340
}
319341
}
320342
}
321343

344+
findBackups(directory);
322345
return count;
323346
}
324347

tests/enhance/agent-analyzer.test.js

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,16 @@ code
264264
});
265265

266266
it('should not flag CoT on complex tasks', () => {
267-
const content = 'Think step-by-step: ' + 'Complex analysis. '.repeat(200);
267+
// Complex = many words (> 500) AND many sections (>= 4)
268+
const content = `## Section 1
269+
Think step-by-step through this analysis.
270+
## Section 2
271+
More detailed content here.
272+
## Section 3
273+
Even more complex material.
274+
## Section 4
275+
Final section with conclusions.
276+
` + 'Additional detailed analysis content. '.repeat(50);
268277
const pattern = agentPatterns.agentPatterns.unnecessary_cot;
269278
const result = pattern.check(content);
270279

@@ -274,18 +283,20 @@ code
274283

275284
describe('missing_cot', () => {
276285
it('should detect missing CoT on complex tasks', () => {
286+
// Missing CoT requires: wordCount > 1000, sectionCount >= 5, hasAnalysis keywords
287+
// Must NOT contain: step-by-step, <thinking>, reasoning, think through
277288
const longContent = `
278289
## Section 1
279-
Analyze
290+
Analyze this complex topic thoroughly with multiple considerations.
280291
## Section 2
281-
Evaluate
292+
Evaluate the data in great detail and depth here.
282293
## Section 3
283-
Assess
294+
Assess all the parameters carefully and methodically.
284295
## Section 4
285-
Review
296+
Review the findings completely and comprehensively.
286297
## Section 5
287-
Complex
288-
` + 'Word '.repeat(300);
298+
Complex logical work is absolutely necessary in this section.
299+
` + 'Detailed analysis content for this complex evaluation task with careful consideration. '.repeat(100);
289300

290301
const pattern = agentPatterns.agentPatterns.missing_cot;
291302
const result = pattern.check(longContent);

0 commit comments

Comments
 (0)