agentsys 5.0.3 → 5.1.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/.claude-plugin/marketplace.json +21 -14
- package/.claude-plugin/plugin.json +1 -1
- package/AGENTS.md +2 -1
- package/CHANGELOG.md +18 -0
- package/README.md +7 -6
- package/adapters/codex/skills/agnix/SKILL.md +0 -1
- package/adapters/codex/skills/audit-project/SKILL.md +0 -1
- package/adapters/codex/skills/audit-project-agents/SKILL.md +0 -1
- package/adapters/codex/skills/audit-project-github/SKILL.md +0 -1
- package/adapters/codex/skills/consult/SKILL.md +132 -57
- package/adapters/codex/skills/debate/SKILL.md +214 -0
- package/adapters/codex/skills/delivery-approval/SKILL.md +0 -1
- package/adapters/codex/skills/deslop/SKILL.md +0 -1
- package/adapters/codex/skills/drift-detect/SKILL.md +0 -1
- package/adapters/codex/skills/enhance/SKILL.md +0 -1
- package/adapters/codex/skills/learn/SKILL.md +0 -1
- package/adapters/codex/skills/next-task/SKILL.md +0 -1
- package/adapters/codex/skills/perf/SKILL.md +0 -1
- package/adapters/codex/skills/repo-map/SKILL.md +0 -1
- package/adapters/codex/skills/ship/SKILL.md +0 -1
- package/adapters/codex/skills/ship-ci-review-loop/SKILL.md +0 -1
- package/adapters/codex/skills/ship-deployment/SKILL.md +0 -1
- package/adapters/codex/skills/ship-error-handling/SKILL.md +0 -1
- package/adapters/codex/skills/sync-docs/SKILL.md +0 -1
- package/adapters/opencode/agents/agent-enhancer.md +0 -1
- package/adapters/opencode/agents/agnix-agent.md +0 -1
- package/adapters/opencode/agents/ci-fixer.md +0 -1
- package/adapters/opencode/agents/ci-monitor.md +0 -1
- package/adapters/opencode/agents/claudemd-enhancer.md +0 -1
- package/adapters/opencode/agents/consult-agent.md +122 -30
- package/adapters/opencode/agents/cross-file-enhancer.md +0 -1
- package/adapters/opencode/agents/debate-orchestrator.md +169 -0
- package/adapters/opencode/agents/delivery-validator.md +0 -1
- package/adapters/opencode/agents/deslop-agent.md +0 -1
- package/adapters/opencode/agents/docs-enhancer.md +0 -1
- package/adapters/opencode/agents/exploration-agent.md +0 -1
- package/adapters/opencode/agents/hooks-enhancer.md +0 -1
- package/adapters/opencode/agents/implementation-agent.md +0 -1
- package/adapters/opencode/agents/learn-agent.md +0 -1
- package/adapters/opencode/agents/map-validator.md +0 -1
- package/adapters/opencode/agents/perf-analyzer.md +0 -1
- package/adapters/opencode/agents/perf-code-paths.md +0 -1
- package/adapters/opencode/agents/perf-investigation-logger.md +0 -1
- package/adapters/opencode/agents/perf-orchestrator.md +0 -1
- package/adapters/opencode/agents/perf-theory-gatherer.md +0 -1
- package/adapters/opencode/agents/perf-theory-tester.md +0 -1
- package/adapters/opencode/agents/plan-synthesizer.md +0 -1
- package/adapters/opencode/agents/planning-agent.md +0 -1
- package/adapters/opencode/agents/plugin-enhancer.md +0 -1
- package/adapters/opencode/agents/prompt-enhancer.md +0 -1
- package/adapters/opencode/agents/simple-fixer.md +0 -1
- package/adapters/opencode/agents/skills-enhancer.md +0 -1
- package/adapters/opencode/agents/sync-docs-agent.md +0 -1
- package/adapters/opencode/agents/task-discoverer.md +0 -1
- package/adapters/opencode/agents/test-coverage-checker.md +0 -1
- package/adapters/opencode/agents/worktree-manager.md +0 -1
- package/adapters/opencode/commands/agnix.md +0 -1
- package/adapters/opencode/commands/audit-project-agents.md +0 -1
- package/adapters/opencode/commands/audit-project-github.md +0 -1
- package/adapters/opencode/commands/audit-project.md +0 -1
- package/adapters/opencode/commands/consult.md +133 -57
- package/adapters/opencode/commands/debate.md +224 -0
- package/adapters/opencode/commands/delivery-approval.md +0 -1
- package/adapters/opencode/commands/deslop.md +0 -1
- package/adapters/opencode/commands/drift-detect.md +0 -1
- package/adapters/opencode/commands/enhance.md +0 -1
- package/adapters/opencode/commands/learn.md +0 -1
- package/adapters/opencode/commands/next-task.md +0 -1
- package/adapters/opencode/commands/perf.md +0 -1
- package/adapters/opencode/commands/repo-map.md +0 -1
- package/adapters/opencode/commands/ship-ci-review-loop.md +0 -1
- package/adapters/opencode/commands/ship-deployment.md +0 -1
- package/adapters/opencode/commands/ship-error-handling.md +0 -1
- package/adapters/opencode/commands/ship.md +0 -1
- package/adapters/opencode/commands/sync-docs.md +0 -1
- package/adapters/opencode/skills/agnix/SKILL.md +1 -2
- package/adapters/opencode/skills/consult/SKILL.md +33 -23
- package/adapters/opencode/skills/debate/SKILL.md +245 -0
- package/adapters/opencode/skills/deslop/SKILL.md +1 -2
- package/adapters/opencode/skills/discover-tasks/SKILL.md +1 -2
- package/adapters/opencode/skills/drift-analysis/SKILL.md +1 -2
- package/adapters/opencode/skills/enhance-agent-prompts/SKILL.md +1 -2
- package/adapters/opencode/skills/enhance-claude-memory/SKILL.md +1 -2
- package/adapters/opencode/skills/enhance-cross-file/SKILL.md +1 -2
- package/adapters/opencode/skills/enhance-docs/SKILL.md +1 -2
- package/adapters/opencode/skills/enhance-hooks/SKILL.md +1 -2
- package/adapters/opencode/skills/enhance-orchestrator/SKILL.md +1 -2
- package/adapters/opencode/skills/enhance-plugins/SKILL.md +1 -2
- package/adapters/opencode/skills/enhance-prompts/SKILL.md +1 -2
- package/adapters/opencode/skills/enhance-skills/SKILL.md +1 -2
- package/adapters/opencode/skills/learn/SKILL.md +1 -2
- package/adapters/opencode/skills/orchestrate-review/SKILL.md +0 -1
- package/adapters/opencode/skills/perf-analyzer/SKILL.md +1 -2
- package/adapters/opencode/skills/perf-baseline-manager/SKILL.md +1 -2
- package/adapters/opencode/skills/perf-benchmarker/SKILL.md +1 -2
- package/adapters/opencode/skills/perf-code-paths/SKILL.md +1 -2
- package/adapters/opencode/skills/perf-investigation-logger/SKILL.md +1 -2
- package/adapters/opencode/skills/perf-profiler/SKILL.md +1 -2
- package/adapters/opencode/skills/perf-theory-gatherer/SKILL.md +1 -2
- package/adapters/opencode/skills/perf-theory-tester/SKILL.md +1 -2
- package/adapters/opencode/skills/repo-mapping/SKILL.md +1 -2
- package/adapters/opencode/skills/sync-docs/SKILL.md +1 -2
- package/adapters/opencode/skills/validate-delivery/SKILL.md +1 -2
- package/lib/adapter-transforms.js +24 -4
- package/package.json +1 -1
- package/plugins/agnix/.claude-plugin/plugin.json +1 -1
- package/plugins/agnix/skills/agnix/SKILL.md +1 -1
- package/plugins/audit-project/.claude-plugin/plugin.json +1 -1
- package/plugins/audit-project/lib/adapter-transforms.js +24 -4
- package/plugins/consult/.claude-plugin/plugin.json +1 -1
- package/plugins/consult/agents/consult-agent.md +122 -29
- package/plugins/consult/commands/consult.md +135 -58
- package/plugins/consult/skills/consult/SKILL.md +31 -20
- package/plugins/debate/.claude-plugin/plugin.json +21 -0
- package/plugins/debate/agents/debate-orchestrator.md +175 -0
- package/plugins/debate/commands/debate.md +221 -0
- package/plugins/debate/lib/adapter-transforms.js +298 -0
- package/plugins/debate/lib/collectors/codebase.js +392 -0
- package/plugins/debate/lib/collectors/docs-patterns.js +713 -0
- package/plugins/debate/lib/collectors/documentation.js +219 -0
- package/plugins/debate/lib/collectors/github.js +330 -0
- package/plugins/debate/lib/collectors/index.js +126 -0
- package/plugins/debate/lib/config/index.js +14 -0
- package/plugins/debate/lib/cross-platform/index.js +539 -0
- package/plugins/debate/lib/discovery/index.js +352 -0
- package/plugins/debate/lib/drift-detect/collectors.js +37 -0
- package/plugins/debate/lib/enhance/agent-analyzer.js +421 -0
- package/plugins/debate/lib/enhance/agent-patterns.js +571 -0
- package/plugins/debate/lib/enhance/auto-suppression.js +622 -0
- package/plugins/debate/lib/enhance/benchmark.js +417 -0
- package/plugins/debate/lib/enhance/cross-file-analyzer.js +930 -0
- package/plugins/debate/lib/enhance/cross-file-patterns.js +370 -0
- package/plugins/debate/lib/enhance/docs-analyzer.js +325 -0
- package/plugins/debate/lib/enhance/docs-patterns.js +671 -0
- package/plugins/debate/lib/enhance/fixer.js +721 -0
- package/plugins/debate/lib/enhance/hook-analyzer.js +135 -0
- package/plugins/debate/lib/enhance/hook-patterns.js +40 -0
- package/plugins/debate/lib/enhance/index.js +127 -0
- package/plugins/debate/lib/enhance/plugin-analyzer.js +402 -0
- package/plugins/debate/lib/enhance/plugin-patterns.js +326 -0
- package/plugins/debate/lib/enhance/projectmemory-analyzer.js +551 -0
- package/plugins/debate/lib/enhance/projectmemory-patterns.js +617 -0
- package/plugins/debate/lib/enhance/prompt-analyzer.js +457 -0
- package/plugins/debate/lib/enhance/prompt-patterns.js +1484 -0
- package/plugins/debate/lib/enhance/reporter.js +1348 -0
- package/plugins/debate/lib/enhance/security-patterns.js +284 -0
- package/plugins/debate/lib/enhance/skill-analyzer.js +182 -0
- package/plugins/debate/lib/enhance/skill-patterns.js +147 -0
- package/plugins/debate/lib/enhance/suppression.js +352 -0
- package/plugins/debate/lib/enhance/tool-patterns.js +373 -0
- package/plugins/debate/lib/index.js +270 -0
- package/plugins/debate/lib/patterns/cli-enhancers.js +611 -0
- package/plugins/debate/lib/patterns/pipeline.js +948 -0
- package/plugins/debate/lib/patterns/review-patterns.js +558 -0
- package/plugins/debate/lib/patterns/slop-analyzers.js +2305 -0
- package/plugins/debate/lib/patterns/slop-patterns.js +1187 -0
- package/plugins/debate/lib/perf/analyzer/index.js +22 -0
- package/plugins/debate/lib/perf/argument-parser.js +105 -0
- package/plugins/debate/lib/perf/baseline-comparator.js +50 -0
- package/plugins/debate/lib/perf/baseline-store.js +127 -0
- package/plugins/debate/lib/perf/benchmark-runner.js +404 -0
- package/plugins/debate/lib/perf/breaking-point-finder.js +52 -0
- package/plugins/debate/lib/perf/breaking-point-runner.js +60 -0
- package/plugins/debate/lib/perf/checkpoint.js +123 -0
- package/plugins/debate/lib/perf/code-paths.js +86 -0
- package/plugins/debate/lib/perf/consolidation.js +37 -0
- package/plugins/debate/lib/perf/constraint-runner.js +71 -0
- package/plugins/debate/lib/perf/experiment-runner.js +32 -0
- package/plugins/debate/lib/perf/index.js +41 -0
- package/plugins/debate/lib/perf/investigation-state.js +874 -0
- package/plugins/debate/lib/perf/optimization-runner.js +79 -0
- package/plugins/debate/lib/perf/profilers/go.js +22 -0
- package/plugins/debate/lib/perf/profilers/index.js +46 -0
- package/plugins/debate/lib/perf/profilers/java.js +23 -0
- package/plugins/debate/lib/perf/profilers/node.js +27 -0
- package/plugins/debate/lib/perf/profilers/python.js +23 -0
- package/plugins/debate/lib/perf/profilers/rust.js +23 -0
- package/plugins/debate/lib/perf/profiling-runner.js +75 -0
- package/plugins/debate/lib/perf/schemas.js +140 -0
- package/plugins/debate/lib/platform/detect-platform.js +413 -0
- package/plugins/debate/lib/platform/detection-configs.js +93 -0
- package/plugins/debate/lib/platform/state-dir.js +132 -0
- package/plugins/debate/lib/platform/verify-tools.js +182 -0
- package/plugins/debate/lib/repo-map/cache.js +152 -0
- package/plugins/debate/lib/repo-map/concurrency.js +29 -0
- package/plugins/debate/lib/repo-map/index.js +222 -0
- package/plugins/debate/lib/repo-map/installer.js +212 -0
- package/plugins/debate/lib/repo-map/queries/go.js +27 -0
- package/plugins/debate/lib/repo-map/queries/index.js +100 -0
- package/plugins/debate/lib/repo-map/queries/java.js +38 -0
- package/plugins/debate/lib/repo-map/queries/javascript.js +55 -0
- package/plugins/debate/lib/repo-map/queries/python.js +24 -0
- package/plugins/debate/lib/repo-map/queries/rust.js +73 -0
- package/plugins/debate/lib/repo-map/queries/typescript.js +38 -0
- package/plugins/debate/lib/repo-map/runner.js +1364 -0
- package/plugins/debate/lib/repo-map/updater.js +562 -0
- package/plugins/debate/lib/repo-map/usage-analyzer.js +407 -0
- package/plugins/debate/lib/schemas/plugin-manifest.schema.json +57 -0
- package/plugins/debate/lib/schemas/validator.js +247 -0
- package/plugins/debate/lib/sources/custom-handler.js +199 -0
- package/plugins/debate/lib/sources/policy-questions.js +246 -0
- package/plugins/debate/lib/sources/source-cache.js +165 -0
- package/plugins/debate/lib/state/workflow-state.js +576 -0
- package/plugins/debate/lib/types/agent-frontmatter.d.ts +134 -0
- package/plugins/debate/lib/types/command-frontmatter.d.ts +107 -0
- package/plugins/debate/lib/types/hook-frontmatter.d.ts +115 -0
- package/plugins/debate/lib/types/index.d.ts +84 -0
- package/plugins/debate/lib/types/plugin-manifest.d.ts +102 -0
- package/plugins/debate/lib/types/skill-frontmatter.d.ts +89 -0
- package/plugins/debate/lib/utils/atomic-write.js +94 -0
- package/plugins/debate/lib/utils/cache-manager.js +159 -0
- package/plugins/debate/lib/utils/command-parser.js +0 -0
- package/plugins/debate/lib/utils/context-optimizer.js +300 -0
- package/plugins/debate/lib/utils/deprecation.js +37 -0
- package/plugins/debate/lib/utils/shell-escape.js +88 -0
- package/plugins/debate/lib/utils/state-helpers.js +61 -0
- package/plugins/debate/skills/debate/SKILL.md +264 -0
- package/plugins/deslop/.claude-plugin/plugin.json +1 -1
- package/plugins/deslop/lib/adapter-transforms.js +24 -4
- package/plugins/deslop/skills/deslop/SKILL.md +1 -1
- package/plugins/drift-detect/.claude-plugin/plugin.json +1 -1
- package/plugins/drift-detect/lib/adapter-transforms.js +24 -4
- package/plugins/drift-detect/skills/drift-analysis/SKILL.md +1 -1
- package/plugins/enhance/.claude-plugin/plugin.json +1 -1
- package/plugins/enhance/lib/adapter-transforms.js +24 -4
- package/plugins/enhance/skills/enhance-agent-prompts/SKILL.md +1 -1
- package/plugins/enhance/skills/enhance-claude-memory/SKILL.md +1 -1
- package/plugins/enhance/skills/enhance-cross-file/SKILL.md +1 -1
- package/plugins/enhance/skills/enhance-docs/SKILL.md +1 -1
- package/plugins/enhance/skills/enhance-hooks/SKILL.md +1 -1
- package/plugins/enhance/skills/enhance-orchestrator/SKILL.md +1 -1
- package/plugins/enhance/skills/enhance-plugins/SKILL.md +1 -1
- package/plugins/enhance/skills/enhance-prompts/SKILL.md +1 -1
- package/plugins/enhance/skills/enhance-skills/SKILL.md +1 -1
- package/plugins/learn/.claude-plugin/plugin.json +1 -1
- package/plugins/learn/agents/learn-agent.md +1 -1
- package/plugins/learn/lib/adapter-transforms.js +24 -4
- package/plugins/learn/skills/learn/SKILL.md +1 -1
- package/plugins/next-task/.claude-plugin/plugin.json +1 -1
- package/plugins/next-task/agents/exploration-agent.md +1 -1
- package/plugins/next-task/lib/adapter-transforms.js +24 -4
- package/plugins/next-task/skills/discover-tasks/SKILL.md +1 -1
- package/plugins/next-task/skills/validate-delivery/SKILL.md +1 -1
- package/plugins/perf/.claude-plugin/plugin.json +1 -1
- package/plugins/perf/lib/adapter-transforms.js +24 -4
- package/plugins/perf/skills/perf-analyzer/SKILL.md +1 -1
- package/plugins/perf/skills/perf-baseline-manager/SKILL.md +1 -1
- package/plugins/perf/skills/perf-benchmarker/SKILL.md +1 -1
- package/plugins/perf/skills/perf-code-paths/SKILL.md +1 -1
- package/plugins/perf/skills/perf-investigation-logger/SKILL.md +1 -1
- package/plugins/perf/skills/perf-profiler/SKILL.md +1 -1
- package/plugins/perf/skills/perf-theory-gatherer/SKILL.md +1 -1
- package/plugins/perf/skills/perf-theory-tester/SKILL.md +1 -1
- package/plugins/repo-map/.claude-plugin/plugin.json +1 -1
- package/plugins/repo-map/lib/adapter-transforms.js +24 -4
- package/plugins/ship/.claude-plugin/plugin.json +1 -1
- package/plugins/ship/lib/adapter-transforms.js +24 -4
- package/plugins/sync-docs/.claude-plugin/plugin.json +1 -1
- package/plugins/sync-docs/lib/adapter-transforms.js +24 -4
- package/plugins/sync-docs/skills/sync-docs/SKILL.md +1 -1
- package/scripts/gen-adapters.js +6 -7
- package/scripts/generate-docs.js +4 -2
- package/scripts/plugins.txt +1 -0
- package/site/content.json +6 -6
|
@@ -0,0 +1,671 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Documentation Patterns
|
|
3
|
+
* @author Avi Fenesh
|
|
4
|
+
* @license MIT
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
function estimateTokens(text) {
|
|
8
|
+
if (!text || typeof text !== 'string') return 0;
|
|
9
|
+
return Math.ceil(text.length / 4);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Supports modes: 'ai' (RAG optimized), 'both' (balanced), 'shared' (both)
|
|
14
|
+
*/
|
|
15
|
+
const docsPatterns = {
|
|
16
|
+
broken_internal_link: {
|
|
17
|
+
id: 'broken_internal_link',
|
|
18
|
+
category: 'link',
|
|
19
|
+
certainty: 'HIGH',
|
|
20
|
+
autoFix: false,
|
|
21
|
+
mode: 'shared',
|
|
22
|
+
description: 'Internal link references non-existent file or anchor',
|
|
23
|
+
check: (content, context = {}) => {
|
|
24
|
+
if (!content || typeof content !== 'string') return null;
|
|
25
|
+
|
|
26
|
+
// Find markdown links
|
|
27
|
+
const linkRegex = /\[([^\]]+)\]\(([^)]+)\)/g;
|
|
28
|
+
const brokenLinks = [];
|
|
29
|
+
let match;
|
|
30
|
+
|
|
31
|
+
while ((match = linkRegex.exec(content)) !== null) {
|
|
32
|
+
const linkTarget = match[2];
|
|
33
|
+
|
|
34
|
+
// Skip external links
|
|
35
|
+
if (linkTarget.startsWith('http://') || linkTarget.startsWith('https://')) {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Check internal anchor links
|
|
40
|
+
if (linkTarget.startsWith('#')) {
|
|
41
|
+
const anchorId = linkTarget.slice(1).toLowerCase();
|
|
42
|
+
// Generate expected heading anchors from content
|
|
43
|
+
const headings = content.match(/^#{1,6}\s+(.+)$/gm) || [];
|
|
44
|
+
const anchors = headings.map(h => {
|
|
45
|
+
return h.replace(/^#{1,6}\s+/, '')
|
|
46
|
+
.toLowerCase()
|
|
47
|
+
.replace(/[^a-z0-9\s-]/g, '')
|
|
48
|
+
.replace(/\s+/g, '-');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
if (!anchors.includes(anchorId)) {
|
|
52
|
+
brokenLinks.push(linkTarget);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Note: File existence checks require context.existingFiles
|
|
57
|
+
// which is passed by the analyzer
|
|
58
|
+
if (context.existingFiles && !linkTarget.startsWith('#')) {
|
|
59
|
+
const targetPath = linkTarget.split('#')[0];
|
|
60
|
+
if (!context.existingFiles.includes(targetPath)) {
|
|
61
|
+
brokenLinks.push(linkTarget);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (brokenLinks.length > 0) {
|
|
67
|
+
return {
|
|
68
|
+
issue: `Broken internal links: ${brokenLinks.slice(0, 3).join(', ')}${brokenLinks.length > 3 ? '...' : ''}`,
|
|
69
|
+
fix: 'Fix or remove broken links',
|
|
70
|
+
details: brokenLinks
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
inconsistent_heading_levels: {
|
|
78
|
+
id: 'inconsistent_heading_levels',
|
|
79
|
+
category: 'structure',
|
|
80
|
+
certainty: 'HIGH',
|
|
81
|
+
autoFix: true,
|
|
82
|
+
mode: 'shared',
|
|
83
|
+
description: 'Heading levels skip (e.g., H1 to H3 without H2)',
|
|
84
|
+
check: (content) => {
|
|
85
|
+
if (!content || typeof content !== 'string') return null;
|
|
86
|
+
|
|
87
|
+
const headingRegex = /^(#{1,6})\s+/gm;
|
|
88
|
+
const levels = [];
|
|
89
|
+
let match;
|
|
90
|
+
|
|
91
|
+
while ((match = headingRegex.exec(content)) !== null) {
|
|
92
|
+
levels.push(match[1].length);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Check for skipped levels
|
|
96
|
+
for (let i = 1; i < levels.length; i++) {
|
|
97
|
+
const jump = levels[i] - levels[i - 1];
|
|
98
|
+
if (jump > 1) {
|
|
99
|
+
return {
|
|
100
|
+
issue: `Heading level jumps from H${levels[i - 1]} to H${levels[i]}`,
|
|
101
|
+
fix: 'Fix heading hierarchy to not skip levels'
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
|
|
109
|
+
missing_code_language: {
|
|
110
|
+
id: 'missing_code_language',
|
|
111
|
+
category: 'code',
|
|
112
|
+
certainty: 'HIGH',
|
|
113
|
+
autoFix: false,
|
|
114
|
+
mode: 'shared',
|
|
115
|
+
description: 'Code block without language specification',
|
|
116
|
+
check: (content) => {
|
|
117
|
+
if (!content || typeof content !== 'string') return null;
|
|
118
|
+
|
|
119
|
+
// Find all code block starts
|
|
120
|
+
const allCodeBlocks = content.match(/```/g) || [];
|
|
121
|
+
// Count pairs (opening blocks)
|
|
122
|
+
const totalBlocks = Math.floor(allCodeBlocks.length / 2);
|
|
123
|
+
|
|
124
|
+
if (totalBlocks === 0) return null;
|
|
125
|
+
|
|
126
|
+
// Find code blocks with language (``` followed by non-whitespace)
|
|
127
|
+
const withLangRegex = /```[a-zA-Z][a-zA-Z0-9_-]*/g;
|
|
128
|
+
const withLang = content.match(withLangRegex) || [];
|
|
129
|
+
|
|
130
|
+
const withoutLang = totalBlocks - withLang.length;
|
|
131
|
+
|
|
132
|
+
if (withoutLang > 0) {
|
|
133
|
+
return {
|
|
134
|
+
issue: `${withoutLang} code block(s) without language specification`,
|
|
135
|
+
fix: 'Add language hint after ``` (e.g., ```javascript)'
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
|
|
142
|
+
section_too_long: {
|
|
143
|
+
id: 'section_too_long',
|
|
144
|
+
category: 'structure',
|
|
145
|
+
certainty: 'MEDIUM',
|
|
146
|
+
autoFix: false,
|
|
147
|
+
mode: 'shared',
|
|
148
|
+
description: 'Section exceeds 1000 tokens (poor for RAG chunking)',
|
|
149
|
+
maxTokens: 1000,
|
|
150
|
+
check: (content) => {
|
|
151
|
+
if (!content || typeof content !== 'string') return null;
|
|
152
|
+
|
|
153
|
+
// Split by headings
|
|
154
|
+
const sections = content.split(/^#{1,6}\s+/m);
|
|
155
|
+
const longSections = [];
|
|
156
|
+
|
|
157
|
+
for (let i = 1; i < sections.length; i++) {
|
|
158
|
+
const section = sections[i];
|
|
159
|
+
const tokens = estimateTokens(section);
|
|
160
|
+
if (tokens > 1000) {
|
|
161
|
+
// Get section title (first line)
|
|
162
|
+
const title = section.split('\n')[0].trim().slice(0, 50);
|
|
163
|
+
longSections.push({ title, tokens });
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (longSections.length > 0) {
|
|
168
|
+
return {
|
|
169
|
+
issue: `${longSections.length} section(s) exceed 1000 tokens`,
|
|
170
|
+
fix: 'Break long sections into smaller, focused subsections',
|
|
171
|
+
details: longSections
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
|
|
178
|
+
unnecessary_prose: {
|
|
179
|
+
id: 'unnecessary_prose',
|
|
180
|
+
category: 'efficiency',
|
|
181
|
+
certainty: 'HIGH',
|
|
182
|
+
autoFix: false,
|
|
183
|
+
mode: 'ai',
|
|
184
|
+
description: 'Filler prose that adds no information value',
|
|
185
|
+
check: (content) => {
|
|
186
|
+
if (!content || typeof content !== 'string') return null;
|
|
187
|
+
|
|
188
|
+
// Patterns that indicate unnecessary prose
|
|
189
|
+
const prosePatterns = [
|
|
190
|
+
/in this (?:document|section|guide)/gi,
|
|
191
|
+
/as you (?:can see|may know|probably know)/gi,
|
|
192
|
+
/it(?:'s| is) (?:important|worth noting) (?:to note |that )?/gi,
|
|
193
|
+
/please note that/gi,
|
|
194
|
+
/the following (?:section|document|guide) (?:will |provides )/gi,
|
|
195
|
+
/let(?:'s| us) (?:take a look|explore|dive into)/gi,
|
|
196
|
+
/we(?:'ll| will) (?:cover|discuss|explore)/gi,
|
|
197
|
+
/this allows you to/gi,
|
|
198
|
+
/you(?:'ll| will) (?:learn|discover|find)/gi,
|
|
199
|
+
/as mentioned (?:earlier|above|before)/gi
|
|
200
|
+
];
|
|
201
|
+
|
|
202
|
+
const found = [];
|
|
203
|
+
for (const pattern of prosePatterns) {
|
|
204
|
+
const matches = content.match(pattern);
|
|
205
|
+
if (matches) {
|
|
206
|
+
found.push(...matches.slice(0, 2));
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (found.length >= 3) {
|
|
211
|
+
return {
|
|
212
|
+
issue: `Found ${found.length} instances of unnecessary prose`,
|
|
213
|
+
fix: 'Remove filler text - state facts directly',
|
|
214
|
+
details: found.slice(0, 5)
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
|
|
221
|
+
verbose_explanations: {
|
|
222
|
+
id: 'verbose_explanations',
|
|
223
|
+
category: 'efficiency',
|
|
224
|
+
certainty: 'HIGH',
|
|
225
|
+
autoFix: true,
|
|
226
|
+
mode: 'ai',
|
|
227
|
+
description: 'Verbose explanations that could be condensed',
|
|
228
|
+
check: (content) => {
|
|
229
|
+
if (!content || typeof content !== 'string') return null;
|
|
230
|
+
|
|
231
|
+
// Detect verbose patterns
|
|
232
|
+
const verbosePatterns = [
|
|
233
|
+
{ pattern: /\bin order to\b/gi, replacement: 'to' },
|
|
234
|
+
{ pattern: /\bfor the purpose of\b/gi, replacement: 'for' },
|
|
235
|
+
{ pattern: /\bin the event that\b/gi, replacement: 'if' },
|
|
236
|
+
{ pattern: /\bat this point in time\b/gi, replacement: 'now' },
|
|
237
|
+
{ pattern: /\bdue to the fact that\b/gi, replacement: 'because' },
|
|
238
|
+
{ pattern: /\bhas the ability to\b/gi, replacement: 'can' },
|
|
239
|
+
{ pattern: /\bis able to\b/gi, replacement: 'can' },
|
|
240
|
+
{ pattern: /\bmake use of\b/gi, replacement: 'use' },
|
|
241
|
+
{ pattern: /\ba large number of\b/gi, replacement: 'many' },
|
|
242
|
+
{ pattern: /\ba small number of\b/gi, replacement: 'few' },
|
|
243
|
+
{ pattern: /\bthe majority of\b/gi, replacement: 'most' },
|
|
244
|
+
{ pattern: /\bprior to\b/gi, replacement: 'before' },
|
|
245
|
+
{ pattern: /\bsubsequent to\b/gi, replacement: 'after' }
|
|
246
|
+
];
|
|
247
|
+
|
|
248
|
+
const found = [];
|
|
249
|
+
for (const { pattern } of verbosePatterns) {
|
|
250
|
+
const matches = content.match(pattern);
|
|
251
|
+
if (matches) {
|
|
252
|
+
found.push(...matches);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if (found.length >= 3) {
|
|
257
|
+
return {
|
|
258
|
+
issue: `Found ${found.length} verbose phrases that could be simplified`,
|
|
259
|
+
fix: 'Replace verbose phrases with concise alternatives',
|
|
260
|
+
details: found.slice(0, 5)
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
265
|
+
},
|
|
266
|
+
|
|
267
|
+
suboptimal_chunking: {
|
|
268
|
+
id: 'suboptimal_chunking',
|
|
269
|
+
category: 'rag',
|
|
270
|
+
certainty: 'MEDIUM',
|
|
271
|
+
autoFix: false,
|
|
272
|
+
mode: 'ai',
|
|
273
|
+
description: 'Content structure suboptimal for RAG chunking',
|
|
274
|
+
check: (content) => {
|
|
275
|
+
if (!content || typeof content !== 'string') return null;
|
|
276
|
+
|
|
277
|
+
const issues = [];
|
|
278
|
+
|
|
279
|
+
// Check for very few headings in long content
|
|
280
|
+
const tokens = estimateTokens(content);
|
|
281
|
+
const headingCount = (content.match(/^#{1,6}\s+/gm) || []).length;
|
|
282
|
+
|
|
283
|
+
if (tokens > 500 && headingCount < Math.floor(tokens / 500)) {
|
|
284
|
+
issues.push('Too few section headings for content length');
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Check for headings without content
|
|
288
|
+
const sections = content.split(/^#{1,6}\s+/m);
|
|
289
|
+
for (let i = 1; i < sections.length; i++) {
|
|
290
|
+
const sectionContent = sections[i].split(/^#{1,6}\s+/m)[0];
|
|
291
|
+
if (estimateTokens(sectionContent) < 20) {
|
|
292
|
+
issues.push('Some sections have very little content');
|
|
293
|
+
break;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
if (issues.length > 0) {
|
|
298
|
+
return {
|
|
299
|
+
issue: issues.join('; '),
|
|
300
|
+
fix: 'Restructure content with consistent section sizes (200-500 tokens)'
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
return null;
|
|
304
|
+
}
|
|
305
|
+
},
|
|
306
|
+
|
|
307
|
+
poor_semantic_boundaries: {
|
|
308
|
+
id: 'poor_semantic_boundaries',
|
|
309
|
+
category: 'rag',
|
|
310
|
+
certainty: 'MEDIUM',
|
|
311
|
+
autoFix: false,
|
|
312
|
+
mode: 'ai',
|
|
313
|
+
description: 'Section mixes multiple distinct topics',
|
|
314
|
+
check: (content) => {
|
|
315
|
+
if (!content || typeof content !== 'string') return null;
|
|
316
|
+
|
|
317
|
+
// Look for transition words that suggest topic changes within sections
|
|
318
|
+
const transitionPatterns = [
|
|
319
|
+
/\n\n(?:additionally|also|furthermore|moreover|on another note|separately)/gi,
|
|
320
|
+
/\n\n(?:however|on the other hand|alternatively|in contrast)/gi,
|
|
321
|
+
/\n\n(?:next|then|finally|lastly|another (?:thing|point|topic))/gi
|
|
322
|
+
];
|
|
323
|
+
|
|
324
|
+
// Split into sections and check each
|
|
325
|
+
const sections = content.split(/^#{1,6}\s+/m);
|
|
326
|
+
let problemSections = 0;
|
|
327
|
+
|
|
328
|
+
for (const section of sections) {
|
|
329
|
+
let transitionsInSection = 0;
|
|
330
|
+
for (const pattern of transitionPatterns) {
|
|
331
|
+
const matches = section.match(pattern);
|
|
332
|
+
if (matches) transitionsInSection += matches.length;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
if (transitionsInSection >= 3) {
|
|
336
|
+
problemSections++;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
if (problemSections > 0) {
|
|
341
|
+
return {
|
|
342
|
+
issue: `${problemSections} section(s) may mix multiple topics`,
|
|
343
|
+
fix: 'Split sections so each covers a single, focused topic'
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
return null;
|
|
347
|
+
}
|
|
348
|
+
},
|
|
349
|
+
|
|
350
|
+
missing_context_anchors: {
|
|
351
|
+
id: 'missing_context_anchors',
|
|
352
|
+
category: 'rag',
|
|
353
|
+
certainty: 'MEDIUM',
|
|
354
|
+
autoFix: false,
|
|
355
|
+
mode: 'ai',
|
|
356
|
+
description: 'Sections lack self-contained context for RAG retrieval',
|
|
357
|
+
check: (content) => {
|
|
358
|
+
if (!content || typeof content !== 'string') return null;
|
|
359
|
+
|
|
360
|
+
// Check for dangling pronouns at section starts
|
|
361
|
+
const sections = content.split(/^#{1,6}\s+/m);
|
|
362
|
+
const issues = [];
|
|
363
|
+
|
|
364
|
+
for (let i = 1; i < sections.length; i++) {
|
|
365
|
+
const section = sections[i];
|
|
366
|
+
const lines = section.split('\n').filter(l => l.trim());
|
|
367
|
+
|
|
368
|
+
if (lines.length > 1) {
|
|
369
|
+
// First content line after heading
|
|
370
|
+
const firstLine = lines[1] || '';
|
|
371
|
+
|
|
372
|
+
// Check if starts with dangling reference
|
|
373
|
+
if (/^(?:It|This|These|Those|They|The above|As mentioned)\s/i.test(firstLine)) {
|
|
374
|
+
const title = lines[0].slice(0, 30);
|
|
375
|
+
issues.push(title);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
if (issues.length >= 2) {
|
|
381
|
+
return {
|
|
382
|
+
issue: `${issues.length} sections start with context-dependent references`,
|
|
383
|
+
fix: 'Make each section self-contained (avoid "It", "This" without context)',
|
|
384
|
+
details: issues.slice(0, 3)
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
return null;
|
|
388
|
+
}
|
|
389
|
+
},
|
|
390
|
+
|
|
391
|
+
token_inefficiency_suggestions: {
|
|
392
|
+
id: 'token_inefficiency_suggestions',
|
|
393
|
+
category: 'efficiency',
|
|
394
|
+
certainty: 'LOW',
|
|
395
|
+
autoFix: false,
|
|
396
|
+
mode: 'ai',
|
|
397
|
+
description: 'Suggestions for reducing token usage',
|
|
398
|
+
check: (content) => {
|
|
399
|
+
if (!content || typeof content !== 'string') return null;
|
|
400
|
+
|
|
401
|
+
const suggestions = [];
|
|
402
|
+
const tokens = estimateTokens(content);
|
|
403
|
+
|
|
404
|
+
// Check for repeated phrases
|
|
405
|
+
const words = content.toLowerCase().split(/\s+/);
|
|
406
|
+
const phrases = {};
|
|
407
|
+
for (let i = 0; i < words.length - 2; i++) {
|
|
408
|
+
const phrase = words.slice(i, i + 3).join(' ');
|
|
409
|
+
phrases[phrase] = (phrases[phrase] || 0) + 1;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
const repeatedPhrases = Object.entries(phrases)
|
|
413
|
+
.filter(([_, count]) => count >= 4)
|
|
414
|
+
.map(([phrase]) => phrase);
|
|
415
|
+
|
|
416
|
+
if (repeatedPhrases.length > 0) {
|
|
417
|
+
suggestions.push(`Repeated phrases could be consolidated: ${repeatedPhrases.slice(0, 2).join(', ')}`);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// Check for very long lists that could be tables
|
|
421
|
+
const longLists = content.match(/(?:^[-*][ \t]+\S[^\n]*\n){10,}/gm);
|
|
422
|
+
if (longLists) {
|
|
423
|
+
suggestions.push('Long lists (10+ items) might be more efficient as tables');
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// Check token density
|
|
427
|
+
const lineCount = content.split('\n').length;
|
|
428
|
+
if (lineCount > 0 && tokens / lineCount < 3) {
|
|
429
|
+
suggestions.push('Many short lines - consider consolidating');
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
if (suggestions.length > 0) {
|
|
433
|
+
return {
|
|
434
|
+
issue: `Token efficiency suggestions (current: ~${tokens} tokens)`,
|
|
435
|
+
fix: suggestions.join('; ')
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
return null;
|
|
439
|
+
}
|
|
440
|
+
},
|
|
441
|
+
|
|
442
|
+
missing_section_headers: {
|
|
443
|
+
id: 'missing_section_headers',
|
|
444
|
+
category: 'structure',
|
|
445
|
+
certainty: 'MEDIUM',
|
|
446
|
+
autoFix: false,
|
|
447
|
+
mode: 'both',
|
|
448
|
+
description: 'Long content blocks without section headers',
|
|
449
|
+
check: (content) => {
|
|
450
|
+
if (!content || typeof content !== 'string') return null;
|
|
451
|
+
|
|
452
|
+
// Find paragraphs (content between headings or start/end)
|
|
453
|
+
const parts = content.split(/^#{1,6}\s+/m);
|
|
454
|
+
const longBlocks = [];
|
|
455
|
+
|
|
456
|
+
for (let i = 0; i < parts.length; i++) {
|
|
457
|
+
const part = parts[i];
|
|
458
|
+
// First part (before any heading) should have heading line removed
|
|
459
|
+
// Subsequent parts have the heading text as first line, which we keep for token count
|
|
460
|
+
const cleanPart = i === 0 ? part : part.split('\n').slice(1).join('\n');
|
|
461
|
+
const tokens = estimateTokens(cleanPart);
|
|
462
|
+
|
|
463
|
+
if (tokens > 500) {
|
|
464
|
+
const preview = cleanPart.trim().split('\n')[0].slice(0, 50);
|
|
465
|
+
longBlocks.push({ tokens, preview });
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
if (longBlocks.length > 0) {
|
|
470
|
+
return {
|
|
471
|
+
issue: `${longBlocks.length} content block(s) over 500 tokens without sub-headers`,
|
|
472
|
+
fix: 'Add section headers to break up long content',
|
|
473
|
+
details: longBlocks
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
return null;
|
|
477
|
+
}
|
|
478
|
+
},
|
|
479
|
+
|
|
480
|
+
poor_context_ordering: {
|
|
481
|
+
id: 'poor_context_ordering',
|
|
482
|
+
category: 'structure',
|
|
483
|
+
certainty: 'MEDIUM',
|
|
484
|
+
autoFix: false,
|
|
485
|
+
mode: 'both',
|
|
486
|
+
description: 'Important information may be buried too deep',
|
|
487
|
+
check: (content) => {
|
|
488
|
+
if (!content || typeof content !== 'string') return null;
|
|
489
|
+
|
|
490
|
+
// Check if critical keywords appear late in document
|
|
491
|
+
const criticalKeywords = [
|
|
492
|
+
/\b(?:important|critical|must|required|warning|caution|danger)\b/i,
|
|
493
|
+
/\b(?:error|fail|break|crash|security|vulnerability)\b/i
|
|
494
|
+
];
|
|
495
|
+
|
|
496
|
+
const lines = content.split('\n');
|
|
497
|
+
const totalLines = lines.length;
|
|
498
|
+
const lateThreshold = Math.floor(totalLines * 0.7);
|
|
499
|
+
|
|
500
|
+
const lateImportantLines = [];
|
|
501
|
+
|
|
502
|
+
for (let i = lateThreshold; i < totalLines; i++) {
|
|
503
|
+
for (const pattern of criticalKeywords) {
|
|
504
|
+
if (pattern.test(lines[i])) {
|
|
505
|
+
lateImportantLines.push(lines[i].trim().slice(0, 50));
|
|
506
|
+
break;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
if (lateImportantLines.length >= 3) {
|
|
512
|
+
return {
|
|
513
|
+
issue: 'Critical information appears in the last 30% of document',
|
|
514
|
+
fix: 'Move important warnings/requirements earlier in the document'
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
return null;
|
|
518
|
+
}
|
|
519
|
+
},
|
|
520
|
+
|
|
521
|
+
readability_with_rag_suggestions: {
|
|
522
|
+
id: 'readability_with_rag_suggestions',
|
|
523
|
+
category: 'balance',
|
|
524
|
+
certainty: 'LOW',
|
|
525
|
+
autoFix: false,
|
|
526
|
+
mode: 'both',
|
|
527
|
+
description: 'Suggestions for balancing readability and RAG optimization',
|
|
528
|
+
check: (content) => {
|
|
529
|
+
if (!content || typeof content !== 'string') return null;
|
|
530
|
+
|
|
531
|
+
const suggestions = [];
|
|
532
|
+
|
|
533
|
+
// Check for very short paragraphs (good for RAG, but might hurt readability)
|
|
534
|
+
const paragraphs = content.split(/\n\n+/).filter(p => p.trim());
|
|
535
|
+
const veryShort = paragraphs.filter(p => estimateTokens(p) < 20).length;
|
|
536
|
+
|
|
537
|
+
if (veryShort > paragraphs.length * 0.5) {
|
|
538
|
+
suggestions.push('Many very short paragraphs - consider grouping related points');
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
// Check for lack of explanatory text (good for AI, bad for humans)
|
|
542
|
+
const hasExamples = /```|<example>|for example|e\.g\./i.test(content);
|
|
543
|
+
const hasExplanation = /because|since|therefore|this means/i.test(content);
|
|
544
|
+
|
|
545
|
+
if (hasExamples && !hasExplanation) {
|
|
546
|
+
suggestions.push('Examples present but limited explanation - add context for human readers');
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
// Check for dense technical content without summaries
|
|
550
|
+
const codeBlocks = (content.match(/```[\s\S]*?```/g) || []).length;
|
|
551
|
+
const hasSummary = /^##?\s+(?:summary|overview|tldr|key points)/im.test(content);
|
|
552
|
+
|
|
553
|
+
if (codeBlocks >= 5 && !hasSummary) {
|
|
554
|
+
suggestions.push('Dense code content - consider adding a summary section');
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
if (suggestions.length > 0) {
|
|
558
|
+
return {
|
|
559
|
+
issue: 'Balance suggestions for readability vs RAG optimization',
|
|
560
|
+
fix: suggestions.join('; ')
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
return null;
|
|
564
|
+
}
|
|
565
|
+
},
|
|
566
|
+
|
|
567
|
+
structure_recommendations: {
|
|
568
|
+
id: 'structure_recommendations',
|
|
569
|
+
category: 'structure',
|
|
570
|
+
certainty: 'LOW',
|
|
571
|
+
autoFix: false,
|
|
572
|
+
mode: 'both',
|
|
573
|
+
description: 'General structure recommendations',
|
|
574
|
+
check: (content) => {
|
|
575
|
+
if (!content || typeof content !== 'string') return null;
|
|
576
|
+
|
|
577
|
+
const recommendations = [];
|
|
578
|
+
|
|
579
|
+
// Check for table of contents in long documents
|
|
580
|
+
const tokens = estimateTokens(content);
|
|
581
|
+
const headingCount = (content.match(/^#{1,6}\s+/gm) || []).length;
|
|
582
|
+
const hasToc = /^##?\s+(?:table of contents|contents|toc)/im.test(content) ||
|
|
583
|
+
/^\s*-\s+\[.+\]\(#/m.test(content);
|
|
584
|
+
|
|
585
|
+
if (tokens > 2000 && headingCount >= 5 && !hasToc) {
|
|
586
|
+
recommendations.push('Consider adding a table of contents for navigation');
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
// Check for missing introduction
|
|
590
|
+
const firstHeading = content.match(/^#{1,6}\s+(.+)/m);
|
|
591
|
+
const hasIntro = /^##?\s+(?:introduction|overview|about|getting started)/im.test(content);
|
|
592
|
+
|
|
593
|
+
if (tokens > 1000 && firstHeading && !hasIntro) {
|
|
594
|
+
recommendations.push('Consider adding an introduction or overview section');
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// Check for consistent formatting
|
|
598
|
+
const bulletStyles = {
|
|
599
|
+
dash: (content.match(/^-\s+/gm) || []).length,
|
|
600
|
+
asterisk: (content.match(/^\*\s+/gm) || []).length
|
|
601
|
+
};
|
|
602
|
+
|
|
603
|
+
if (bulletStyles.dash > 0 && bulletStyles.asterisk > 0) {
|
|
604
|
+
recommendations.push('Mixed bullet styles (- and *) - consider using one consistently');
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
if (recommendations.length > 0) {
|
|
608
|
+
return {
|
|
609
|
+
issue: 'Structure recommendations',
|
|
610
|
+
fix: recommendations.join('; ')
|
|
611
|
+
};
|
|
612
|
+
}
|
|
613
|
+
return null;
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
};
|
|
617
|
+
|
|
618
|
+
function getAllPatterns() {
|
|
619
|
+
return docsPatterns;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
function getPatternsByMode(mode) {
|
|
623
|
+
const result = {};
|
|
624
|
+
for (const [name, pattern] of Object.entries(docsPatterns)) {
|
|
625
|
+
if (pattern.mode === mode || pattern.mode === 'shared') {
|
|
626
|
+
result[name] = pattern;
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
return result;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
function getPatternsByCertainty(certainty) {
|
|
633
|
+
const result = {};
|
|
634
|
+
for (const [name, pattern] of Object.entries(docsPatterns)) {
|
|
635
|
+
if (pattern.certainty === certainty) {
|
|
636
|
+
result[name] = pattern;
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
return result;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
function getPatternsByCategory(category) {
|
|
643
|
+
const result = {};
|
|
644
|
+
for (const [name, pattern] of Object.entries(docsPatterns)) {
|
|
645
|
+
if (pattern.category === category) {
|
|
646
|
+
result[name] = pattern;
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
return result;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
function getAutoFixablePatterns() {
|
|
653
|
+
const result = {};
|
|
654
|
+
for (const [name, pattern] of Object.entries(docsPatterns)) {
|
|
655
|
+
if (pattern.autoFix) {
|
|
656
|
+
result[name] = pattern;
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
return result;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
module.exports = {
|
|
663
|
+
docsPatterns,
|
|
664
|
+
estimateTokens,
|
|
665
|
+
getAllPatterns,
|
|
666
|
+
getPatternsByMode,
|
|
667
|
+
getPatternsByCertainty,
|
|
668
|
+
getPatternsByCategory,
|
|
669
|
+
getAutoFixablePatterns,
|
|
670
|
+
getPatternsForMode: getPatternsByMode
|
|
671
|
+
};
|