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,370 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-File Semantic Analysis Patterns
|
|
3
|
+
* Detection patterns for multi-file consistency and alignment
|
|
4
|
+
*
|
|
5
|
+
* Cross-platform compatible: Works with Claude Code, OpenCode, and Codex
|
|
6
|
+
*
|
|
7
|
+
* @author Avi Fenesh
|
|
8
|
+
* @license MIT
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const path = require('path');
|
|
13
|
+
const { getStateDir, getPlatformName } = require('../platform/state-dir');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Platform-specific default tools
|
|
17
|
+
* These are used when no tools.json config is found
|
|
18
|
+
*/
|
|
19
|
+
const PLATFORM_TOOLS = {
|
|
20
|
+
claude: [
|
|
21
|
+
'Task', 'Read', 'Write', 'Edit', 'Glob', 'Grep', 'Bash',
|
|
22
|
+
'WebFetch', 'WebSearch', 'AskUserQuestion', 'NotebookEdit',
|
|
23
|
+
'LSP', 'Skill', 'EnterPlanMode', 'ExitPlanMode',
|
|
24
|
+
'TaskCreate', 'TaskUpdate', 'TaskList', 'TaskGet', 'TaskOutput', 'TaskStop'
|
|
25
|
+
],
|
|
26
|
+
opencode: [
|
|
27
|
+
'Task', 'Read', 'Write', 'Edit', 'Glob', 'Grep', 'Bash',
|
|
28
|
+
'WebFetch', 'WebSearch', 'AskUser', 'Notebook',
|
|
29
|
+
'LSP', 'Skill', 'Plan'
|
|
30
|
+
],
|
|
31
|
+
codex: [
|
|
32
|
+
'Task', 'Read', 'Write', 'Edit', 'Glob', 'Grep', 'Bash',
|
|
33
|
+
'WebFetch', 'Ask', 'Shell'
|
|
34
|
+
],
|
|
35
|
+
// Superset for unknown platforms
|
|
36
|
+
unknown: [
|
|
37
|
+
'Task', 'Read', 'Write', 'Edit', 'Glob', 'Grep', 'Bash',
|
|
38
|
+
'WebFetch', 'WebSearch', 'AskUserQuestion', 'AskUser', 'Ask',
|
|
39
|
+
'NotebookEdit', 'Notebook', 'LSP', 'Skill', 'Plan',
|
|
40
|
+
'EnterPlanMode', 'ExitPlanMode', 'Shell',
|
|
41
|
+
'TaskCreate', 'TaskUpdate', 'TaskList', 'TaskGet', 'TaskOutput', 'TaskStop'
|
|
42
|
+
]
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/** Maximum size for tools.json config file (prevents DoS via large files) */
|
|
46
|
+
const MAX_CONFIG_SIZE = 64 * 1024; // 64KB
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Load tools configuration from state directory or use platform defaults
|
|
50
|
+
* @param {string} [basePath=process.cwd()] - Base path to check
|
|
51
|
+
* @returns {string[]} Array of known tool names
|
|
52
|
+
*/
|
|
53
|
+
function loadKnownTools(basePath = process.cwd()) {
|
|
54
|
+
const stateDir = getStateDir(basePath);
|
|
55
|
+
const toolsConfigPath = path.join(basePath, stateDir, 'tools.json');
|
|
56
|
+
|
|
57
|
+
// Try to load from config file (without race condition)
|
|
58
|
+
try {
|
|
59
|
+
const content = fs.readFileSync(toolsConfigPath, 'utf8');
|
|
60
|
+
|
|
61
|
+
// Size limit check
|
|
62
|
+
if (content.length > MAX_CONFIG_SIZE) {
|
|
63
|
+
if (process.env.DEBUG) {
|
|
64
|
+
console.error('[cross-file-patterns] tools.json exceeds size limit, using defaults');
|
|
65
|
+
}
|
|
66
|
+
throw new Error('Config file too large');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const config = JSON.parse(content);
|
|
70
|
+
|
|
71
|
+
// Validate config structure
|
|
72
|
+
if (Array.isArray(config.tools)) {
|
|
73
|
+
return config.tools;
|
|
74
|
+
}
|
|
75
|
+
if (config.knownTools && Array.isArray(config.knownTools)) {
|
|
76
|
+
return config.knownTools;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (process.env.DEBUG) {
|
|
80
|
+
console.error('[cross-file-patterns] tools.json missing tools array, using defaults');
|
|
81
|
+
}
|
|
82
|
+
} catch (err) {
|
|
83
|
+
// ENOENT (file not found) is expected, don't log
|
|
84
|
+
// Other errors should be logged for debugging
|
|
85
|
+
if (err.code !== 'ENOENT' && process.env.DEBUG) {
|
|
86
|
+
console.error('[cross-file-patterns] Failed to load tools.json:', err.message);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Fall back to platform-specific defaults
|
|
91
|
+
const platform = getPlatformName(basePath);
|
|
92
|
+
return PLATFORM_TOOLS[platform] || PLATFORM_TOOLS.unknown;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Cross-file semantic analysis patterns
|
|
97
|
+
* All patterns use MEDIUM certainty since cross-file analysis requires human review
|
|
98
|
+
*/
|
|
99
|
+
const crossFilePatterns = {
|
|
100
|
+
/**
|
|
101
|
+
* Tool mentioned in prompt body not in frontmatter tools list
|
|
102
|
+
*/
|
|
103
|
+
tool_not_in_allowed_list: {
|
|
104
|
+
id: 'tool_not_in_allowed_list',
|
|
105
|
+
category: 'tool-consistency',
|
|
106
|
+
certainty: 'MEDIUM',
|
|
107
|
+
autoFix: false,
|
|
108
|
+
description: 'Prompt mentions tools not declared in frontmatter',
|
|
109
|
+
check: (data) => {
|
|
110
|
+
const { declaredTools, usedTools, agentName } = data;
|
|
111
|
+
if (!declaredTools || declaredTools.length === 0) return null;
|
|
112
|
+
if (!usedTools || usedTools.length === 0) return null;
|
|
113
|
+
|
|
114
|
+
// Normalize declared tools (handle Bash(git:*) -> Bash)
|
|
115
|
+
const normalizedDeclared = declaredTools.map(t => t.split('(')[0].trim());
|
|
116
|
+
|
|
117
|
+
const undeclared = usedTools.filter(tool =>
|
|
118
|
+
!normalizedDeclared.includes(tool) &&
|
|
119
|
+
!normalizedDeclared.includes('*')
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
if (undeclared.length > 0) {
|
|
123
|
+
return {
|
|
124
|
+
issue: `Agent "${agentName}" uses ${undeclared.join(', ')} but not declared in tools frontmatter`,
|
|
125
|
+
fix: `Add ${undeclared.join(', ')} to tools field or remove usage from prompt`
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Workflow references an agent that does not exist
|
|
134
|
+
*/
|
|
135
|
+
missing_workflow_agent: {
|
|
136
|
+
id: 'missing_workflow_agent',
|
|
137
|
+
category: 'workflow',
|
|
138
|
+
certainty: 'MEDIUM',
|
|
139
|
+
autoFix: false,
|
|
140
|
+
description: 'Workflow references non-existent agent',
|
|
141
|
+
check: (data) => {
|
|
142
|
+
const { referencedAgent, existingAgents, sourceFile } = data;
|
|
143
|
+
if (!referencedAgent || !existingAgents) return null;
|
|
144
|
+
|
|
145
|
+
// Extract plugin:agent format
|
|
146
|
+
const [plugin, agentName] = referencedAgent.includes(':')
|
|
147
|
+
? referencedAgent.split(':')
|
|
148
|
+
: [null, referencedAgent];
|
|
149
|
+
|
|
150
|
+
// Check if agent exists
|
|
151
|
+
const exists = existingAgents.some(a => {
|
|
152
|
+
if (plugin) {
|
|
153
|
+
return a.plugin === plugin && a.name === agentName;
|
|
154
|
+
}
|
|
155
|
+
return a.name === agentName;
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
if (!exists) {
|
|
159
|
+
return {
|
|
160
|
+
issue: `Referenced agent "${referencedAgent}" does not exist`,
|
|
161
|
+
fix: `Create agent "${referencedAgent}" or fix the reference in ${sourceFile}`
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Workflow phases not fully connected
|
|
170
|
+
*/
|
|
171
|
+
incomplete_phase_transition: {
|
|
172
|
+
id: 'incomplete_phase_transition',
|
|
173
|
+
category: 'workflow',
|
|
174
|
+
certainty: 'MEDIUM',
|
|
175
|
+
autoFix: false,
|
|
176
|
+
description: 'Workflow phase transitions are incomplete',
|
|
177
|
+
check: (data) => {
|
|
178
|
+
const { phases, transitions, workflowFile } = data;
|
|
179
|
+
if (!phases || !transitions || phases.length === 0) return null;
|
|
180
|
+
|
|
181
|
+
// Check each phase has a transition (except last)
|
|
182
|
+
const missingTransitions = [];
|
|
183
|
+
for (let i = 0; i < phases.length - 1; i++) {
|
|
184
|
+
const currentPhase = phases[i];
|
|
185
|
+
const hasTransition = transitions.some(t =>
|
|
186
|
+
t.from === currentPhase || t.to === phases[i + 1]
|
|
187
|
+
);
|
|
188
|
+
if (!hasTransition) {
|
|
189
|
+
missingTransitions.push(`${currentPhase} -> ${phases[i + 1]}`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (missingTransitions.length > 0) {
|
|
194
|
+
return {
|
|
195
|
+
issue: `Missing phase transitions: ${missingTransitions.join(', ')}`,
|
|
196
|
+
fix: `Add transition logic for ${missingTransitions[0]} in ${workflowFile}`
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
},
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Duplicate instructions across multiple agents
|
|
205
|
+
*/
|
|
206
|
+
duplicate_instructions: {
|
|
207
|
+
id: 'duplicate_instructions',
|
|
208
|
+
category: 'consistency',
|
|
209
|
+
certainty: 'MEDIUM',
|
|
210
|
+
autoFix: false,
|
|
211
|
+
description: 'Identical critical instructions across agents',
|
|
212
|
+
check: (data) => {
|
|
213
|
+
const { instruction, files } = data;
|
|
214
|
+
if (!instruction || !files || files.length < 2) return null;
|
|
215
|
+
|
|
216
|
+
return {
|
|
217
|
+
issue: `Duplicate instruction found in ${files.length} files: "${instruction.substring(0, 50)}..."`,
|
|
218
|
+
fix: `Extract shared instruction to a common include or ensure intentional duplication`
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
},
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Contradictory rules across agents
|
|
225
|
+
*/
|
|
226
|
+
contradictory_rules: {
|
|
227
|
+
id: 'contradictory_rules',
|
|
228
|
+
category: 'consistency',
|
|
229
|
+
certainty: 'MEDIUM',
|
|
230
|
+
autoFix: false,
|
|
231
|
+
description: 'Conflicting rules across agents (always vs never)',
|
|
232
|
+
check: (data) => {
|
|
233
|
+
const { rule1, rule2, file1, file2 } = data;
|
|
234
|
+
if (!rule1 || !rule2) return null;
|
|
235
|
+
|
|
236
|
+
return {
|
|
237
|
+
issue: `Contradictory rules: "${rule1.substring(0, 40)}..." vs "${rule2.substring(0, 40)}..."`,
|
|
238
|
+
fix: `Resolve conflict between ${file1} and ${file2}`
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
},
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Prompt not referenced by any workflow or skill
|
|
245
|
+
*/
|
|
246
|
+
orphaned_prompt: {
|
|
247
|
+
id: 'orphaned_prompt',
|
|
248
|
+
category: 'consistency',
|
|
249
|
+
certainty: 'MEDIUM',
|
|
250
|
+
autoFix: false,
|
|
251
|
+
description: 'Agent/prompt not referenced anywhere',
|
|
252
|
+
check: (data) => {
|
|
253
|
+
const { promptFile, referencedBy } = data;
|
|
254
|
+
if (!promptFile) return null;
|
|
255
|
+
|
|
256
|
+
if (!referencedBy || referencedBy.length === 0) {
|
|
257
|
+
return {
|
|
258
|
+
issue: `Orphaned prompt: ${promptFile} is not referenced by any workflow or skill`,
|
|
259
|
+
fix: `Add reference to ${promptFile} or remove if unused`
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
return null;
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Skill allowed-tools differs from what prompt uses
|
|
268
|
+
*/
|
|
269
|
+
skill_tool_mismatch: {
|
|
270
|
+
id: 'skill_tool_mismatch',
|
|
271
|
+
category: 'skill-alignment',
|
|
272
|
+
certainty: 'MEDIUM',
|
|
273
|
+
autoFix: false,
|
|
274
|
+
description: 'Skill allowed-tools differs from prompt tool usage',
|
|
275
|
+
check: (data) => {
|
|
276
|
+
const { skillName, skillAllowedTools, promptUsedTools } = data;
|
|
277
|
+
if (!skillAllowedTools || !promptUsedTools) return null;
|
|
278
|
+
|
|
279
|
+
// Normalize skill tools
|
|
280
|
+
const normalizedSkill = skillAllowedTools.map(t => t.split('(')[0].trim());
|
|
281
|
+
|
|
282
|
+
const mismatches = promptUsedTools.filter(tool =>
|
|
283
|
+
!normalizedSkill.includes(tool) &&
|
|
284
|
+
!normalizedSkill.includes('*')
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
if (mismatches.length > 0) {
|
|
288
|
+
return {
|
|
289
|
+
issue: `Skill "${skillName}" prompt uses ${mismatches.join(', ')} not in allowed-tools`,
|
|
290
|
+
fix: `Add ${mismatches.join(', ')} to skill allowed-tools or update prompt`
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
return null;
|
|
294
|
+
}
|
|
295
|
+
},
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Skill description does not match actual behavior
|
|
299
|
+
*/
|
|
300
|
+
skill_description_mismatch: {
|
|
301
|
+
id: 'skill_description_mismatch',
|
|
302
|
+
category: 'skill-alignment',
|
|
303
|
+
certainty: 'MEDIUM',
|
|
304
|
+
autoFix: false,
|
|
305
|
+
description: 'Skill description mentions capabilities not found in body',
|
|
306
|
+
check: (data) => {
|
|
307
|
+
const { skillName, description, bodyCapabilities, descriptionCapabilities } = data;
|
|
308
|
+
if (!description || !bodyCapabilities) return null;
|
|
309
|
+
|
|
310
|
+
const missingInBody = descriptionCapabilities.filter(cap =>
|
|
311
|
+
!bodyCapabilities.some(bc => bc.toLowerCase().includes(cap.toLowerCase()))
|
|
312
|
+
);
|
|
313
|
+
|
|
314
|
+
if (missingInBody.length > 0) {
|
|
315
|
+
return {
|
|
316
|
+
issue: `Skill "${skillName}" description mentions "${missingInBody[0]}" not found in body`,
|
|
317
|
+
fix: `Update description to match actual capabilities or add missing functionality`
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
return null;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Get all cross-file patterns
|
|
327
|
+
* @returns {Object} All patterns
|
|
328
|
+
*/
|
|
329
|
+
function getAllPatterns() {
|
|
330
|
+
return crossFilePatterns;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Get patterns by category
|
|
335
|
+
* @param {string} category - tool-consistency, workflow, consistency, skill-alignment
|
|
336
|
+
* @returns {Object} Filtered patterns
|
|
337
|
+
*/
|
|
338
|
+
function getPatternsByCategory(category) {
|
|
339
|
+
const result = {};
|
|
340
|
+
for (const [name, pattern] of Object.entries(crossFilePatterns)) {
|
|
341
|
+
if (pattern.category === category) {
|
|
342
|
+
result[name] = pattern;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
return result;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Get patterns by certainty
|
|
350
|
+
* @param {string} certainty - HIGH, MEDIUM, LOW
|
|
351
|
+
* @returns {Object} Filtered patterns
|
|
352
|
+
*/
|
|
353
|
+
function getPatternsByCertainty(certainty) {
|
|
354
|
+
const result = {};
|
|
355
|
+
for (const [name, pattern] of Object.entries(crossFilePatterns)) {
|
|
356
|
+
if (pattern.certainty === certainty) {
|
|
357
|
+
result[name] = pattern;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
return result;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
module.exports = {
|
|
364
|
+
PLATFORM_TOOLS,
|
|
365
|
+
loadKnownTools,
|
|
366
|
+
crossFilePatterns,
|
|
367
|
+
getAllPatterns,
|
|
368
|
+
getPatternsByCategory,
|
|
369
|
+
getPatternsByCertainty
|
|
370
|
+
};
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Documentation Analyzer
|
|
3
|
+
* @author Avi Fenesh
|
|
4
|
+
* @license MIT
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const { getPatternsForMode, estimateTokens } = require('./docs-patterns');
|
|
10
|
+
|
|
11
|
+
function analyzeDoc(docPath, options = {}) {
|
|
12
|
+
const { mode = 'both', verbose = false, existingFiles = [] } = options;
|
|
13
|
+
|
|
14
|
+
const results = {
|
|
15
|
+
docName: path.basename(docPath, '.md'),
|
|
16
|
+
docPath,
|
|
17
|
+
mode,
|
|
18
|
+
tokenCount: 0,
|
|
19
|
+
linkIssues: [],
|
|
20
|
+
structureIssues: [],
|
|
21
|
+
codeIssues: [],
|
|
22
|
+
efficiencyIssues: [],
|
|
23
|
+
ragIssues: [],
|
|
24
|
+
balanceIssues: []
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// Read file
|
|
28
|
+
if (!fs.existsSync(docPath)) {
|
|
29
|
+
results.structureIssues.push({
|
|
30
|
+
issue: 'File not found',
|
|
31
|
+
file: docPath,
|
|
32
|
+
certainty: 'HIGH',
|
|
33
|
+
patternId: 'file_not_found'
|
|
34
|
+
});
|
|
35
|
+
return results;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
let content;
|
|
39
|
+
try {
|
|
40
|
+
content = fs.readFileSync(docPath, 'utf8');
|
|
41
|
+
} catch (err) {
|
|
42
|
+
results.structureIssues.push({
|
|
43
|
+
issue: `Failed to read file: ${err.message}`,
|
|
44
|
+
file: docPath,
|
|
45
|
+
certainty: 'HIGH',
|
|
46
|
+
patternId: 'read_error'
|
|
47
|
+
});
|
|
48
|
+
return results;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Calculate token count
|
|
52
|
+
results.tokenCount = estimateTokens(content);
|
|
53
|
+
|
|
54
|
+
// Get patterns applicable to this mode
|
|
55
|
+
const patterns = getPatternsForMode(mode);
|
|
56
|
+
|
|
57
|
+
// Context for pattern checks
|
|
58
|
+
const context = { existingFiles };
|
|
59
|
+
|
|
60
|
+
// Run each pattern check
|
|
61
|
+
for (const [patternName, pattern] of Object.entries(patterns)) {
|
|
62
|
+
// Skip LOW certainty unless verbose
|
|
63
|
+
if (pattern.certainty === 'LOW' && !verbose) {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Run the check
|
|
68
|
+
const result = pattern.check(content, context);
|
|
69
|
+
|
|
70
|
+
if (result) {
|
|
71
|
+
const issue = {
|
|
72
|
+
...result,
|
|
73
|
+
file: docPath,
|
|
74
|
+
certainty: pattern.certainty,
|
|
75
|
+
patternId: pattern.id,
|
|
76
|
+
autoFix: pattern.autoFix
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// Route to appropriate issue category
|
|
80
|
+
switch (pattern.category) {
|
|
81
|
+
case 'link':
|
|
82
|
+
results.linkIssues.push(issue);
|
|
83
|
+
break;
|
|
84
|
+
case 'structure':
|
|
85
|
+
results.structureIssues.push(issue);
|
|
86
|
+
break;
|
|
87
|
+
case 'code':
|
|
88
|
+
results.codeIssues.push(issue);
|
|
89
|
+
break;
|
|
90
|
+
case 'efficiency':
|
|
91
|
+
results.efficiencyIssues.push(issue);
|
|
92
|
+
break;
|
|
93
|
+
case 'rag':
|
|
94
|
+
results.ragIssues.push(issue);
|
|
95
|
+
break;
|
|
96
|
+
case 'balance':
|
|
97
|
+
results.balanceIssues.push(issue);
|
|
98
|
+
break;
|
|
99
|
+
default:
|
|
100
|
+
results.structureIssues.push(issue);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return results;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function analyzeAllDocs(docsDir, options = {}) {
|
|
109
|
+
const { recursive = true, ...analyzeOptions } = options;
|
|
110
|
+
const results = [];
|
|
111
|
+
|
|
112
|
+
if (!fs.existsSync(docsDir)) {
|
|
113
|
+
return results;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Collect all markdown files
|
|
117
|
+
const mdFiles = [];
|
|
118
|
+
|
|
119
|
+
function findMdFiles(dir) {
|
|
120
|
+
let entries;
|
|
121
|
+
try {
|
|
122
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
123
|
+
} catch (err) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
for (const entry of entries) {
|
|
128
|
+
const fullPath = path.join(dir, entry.name);
|
|
129
|
+
|
|
130
|
+
if (entry.isDirectory() && recursive) {
|
|
131
|
+
// Skip common non-doc directories
|
|
132
|
+
if (!['node_modules', '.git', 'dist', 'build'].includes(entry.name)) {
|
|
133
|
+
findMdFiles(fullPath);
|
|
134
|
+
}
|
|
135
|
+
} else if (entry.isFile() && entry.name.endsWith('.md')) {
|
|
136
|
+
// Skip README files in nested directories for agent-docs mode
|
|
137
|
+
if (options.mode === 'ai' && entry.name === 'README.md') {
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
mdFiles.push(fullPath);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
findMdFiles(docsDir);
|
|
146
|
+
|
|
147
|
+
// Get relative paths for link validation
|
|
148
|
+
const existingFiles = mdFiles.map(f => path.relative(docsDir, f).replace(/\\/g, '/'));
|
|
149
|
+
|
|
150
|
+
// Analyze each file
|
|
151
|
+
for (const mdFile of mdFiles) {
|
|
152
|
+
const result = analyzeDoc(mdFile, { ...analyzeOptions, existingFiles });
|
|
153
|
+
results.push(result);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return results;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function analyze(options = {}) {
|
|
160
|
+
const {
|
|
161
|
+
doc,
|
|
162
|
+
docsDir = 'docs',
|
|
163
|
+
mode = 'both',
|
|
164
|
+
verbose = false
|
|
165
|
+
} = options;
|
|
166
|
+
|
|
167
|
+
if (doc) {
|
|
168
|
+
// Check if doc is a directory or file
|
|
169
|
+
try {
|
|
170
|
+
const stats = fs.statSync(doc);
|
|
171
|
+
if (stats.isDirectory()) {
|
|
172
|
+
// Analyze all docs in directory
|
|
173
|
+
return analyzeAllDocs(doc, { mode, verbose });
|
|
174
|
+
} else {
|
|
175
|
+
// Analyze single doc
|
|
176
|
+
return analyzeDoc(doc, { mode, verbose });
|
|
177
|
+
}
|
|
178
|
+
} catch (err) {
|
|
179
|
+
// If file doesn't exist, let analyzeDoc handle the error
|
|
180
|
+
return analyzeDoc(doc, { mode, verbose });
|
|
181
|
+
}
|
|
182
|
+
} else {
|
|
183
|
+
// Analyze all docs in directory
|
|
184
|
+
return analyzeAllDocs(docsDir, { mode, verbose });
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function applyFixes(results, options = {}) {
|
|
189
|
+
// Collect all issues
|
|
190
|
+
let allIssues = [];
|
|
191
|
+
|
|
192
|
+
if (Array.isArray(results)) {
|
|
193
|
+
for (const r of results) {
|
|
194
|
+
allIssues.push(...(r.linkIssues || []));
|
|
195
|
+
allIssues.push(...(r.structureIssues || []));
|
|
196
|
+
allIssues.push(...(r.codeIssues || []));
|
|
197
|
+
allIssues.push(...(r.efficiencyIssues || []));
|
|
198
|
+
allIssues.push(...(r.ragIssues || []));
|
|
199
|
+
allIssues.push(...(r.balanceIssues || []));
|
|
200
|
+
}
|
|
201
|
+
} else {
|
|
202
|
+
allIssues.push(...(results.linkIssues || []));
|
|
203
|
+
allIssues.push(...(results.structureIssues || []));
|
|
204
|
+
allIssues.push(...(results.codeIssues || []));
|
|
205
|
+
allIssues.push(...(results.efficiencyIssues || []));
|
|
206
|
+
allIssues.push(...(results.ragIssues || []));
|
|
207
|
+
allIssues.push(...(results.balanceIssues || []));
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Filter to auto-fixable docs issues
|
|
211
|
+
const docsFixablePatternIds = [
|
|
212
|
+
'inconsistent_heading_levels',
|
|
213
|
+
'verbose_explanations'
|
|
214
|
+
];
|
|
215
|
+
|
|
216
|
+
const fixableIssues = allIssues.filter(i =>
|
|
217
|
+
i.certainty === 'HIGH' &&
|
|
218
|
+
i.autoFix &&
|
|
219
|
+
docsFixablePatternIds.includes(i.patternId)
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
// Apply fixes using the fixer module's pattern
|
|
223
|
+
return applyDocsFixes(fixableIssues, options);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function applyDocsFixes(issues, options = {}) {
|
|
227
|
+
const { dryRun = false, backup = true } = options;
|
|
228
|
+
const fixer = require('./fixer');
|
|
229
|
+
|
|
230
|
+
const results = {
|
|
231
|
+
applied: [],
|
|
232
|
+
skipped: [],
|
|
233
|
+
errors: []
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
// Group by file
|
|
237
|
+
const byFile = new Map();
|
|
238
|
+
for (const issue of issues) {
|
|
239
|
+
const fp = issue.file;
|
|
240
|
+
if (!byFile.has(fp)) {
|
|
241
|
+
byFile.set(fp, []);
|
|
242
|
+
}
|
|
243
|
+
byFile.get(fp).push(issue);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Process each file
|
|
247
|
+
for (const [filePath, fileIssues] of byFile) {
|
|
248
|
+
try {
|
|
249
|
+
if (!fs.existsSync(filePath)) {
|
|
250
|
+
results.errors.push({ filePath, error: 'File not found' });
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
let content = fs.readFileSync(filePath, 'utf8');
|
|
255
|
+
const appliedToFile = [];
|
|
256
|
+
|
|
257
|
+
for (const issue of fileIssues) {
|
|
258
|
+
try {
|
|
259
|
+
if (issue.patternId === 'inconsistent_heading_levels') {
|
|
260
|
+
content = fixer.fixInconsistentHeadings(content);
|
|
261
|
+
appliedToFile.push({
|
|
262
|
+
issue: issue.issue,
|
|
263
|
+
fix: 'Fixed heading levels',
|
|
264
|
+
filePath
|
|
265
|
+
});
|
|
266
|
+
} else if (issue.patternId === 'verbose_explanations') {
|
|
267
|
+
content = fixer.fixVerboseExplanations(content);
|
|
268
|
+
appliedToFile.push({
|
|
269
|
+
issue: issue.issue,
|
|
270
|
+
fix: 'Simplified verbose phrases',
|
|
271
|
+
filePath
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
} catch (err) {
|
|
275
|
+
results.errors.push({
|
|
276
|
+
issue: issue.issue,
|
|
277
|
+
filePath,
|
|
278
|
+
error: err.message
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Write changes
|
|
284
|
+
if (!dryRun && appliedToFile.length > 0) {
|
|
285
|
+
if (backup) {
|
|
286
|
+
fs.writeFileSync(`${filePath}.backup`, fs.readFileSync(filePath, 'utf8'), 'utf8');
|
|
287
|
+
}
|
|
288
|
+
fs.writeFileSync(filePath, content, 'utf8');
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
results.applied.push(...appliedToFile);
|
|
292
|
+
|
|
293
|
+
} catch (err) {
|
|
294
|
+
results.errors.push({
|
|
295
|
+
filePath,
|
|
296
|
+
error: err.message
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
return results;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function generateReport(results, options = {}) {
|
|
305
|
+
const reporter = require('./reporter');
|
|
306
|
+
|
|
307
|
+
if (Array.isArray(results)) {
|
|
308
|
+
return reporter.generateDocsSummaryReport(results, options);
|
|
309
|
+
} else {
|
|
310
|
+
return reporter.generateDocsReport(results, options);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const fixer = require('./fixer');
|
|
315
|
+
|
|
316
|
+
module.exports = {
|
|
317
|
+
analyzeDoc,
|
|
318
|
+
analyzeAllDocs,
|
|
319
|
+
analyze,
|
|
320
|
+
applyFixes,
|
|
321
|
+
applyDocsFixes,
|
|
322
|
+
fixInconsistentHeadings: fixer.fixInconsistentHeadings,
|
|
323
|
+
fixVerboseExplanations: fixer.fixVerboseExplanations,
|
|
324
|
+
generateReport
|
|
325
|
+
};
|