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,611 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI Enhancers for Slop Detection Pipeline
|
|
3
|
+
*
|
|
4
|
+
* Optional CLI tool integration for Phase 2 detection.
|
|
5
|
+
* All tools are user-installed globally - zero npm dependencies for this module.
|
|
6
|
+
* Functions gracefully degrade when tools are not available.
|
|
7
|
+
*
|
|
8
|
+
* Supported languages: javascript, typescript, python, rust, go
|
|
9
|
+
*
|
|
10
|
+
* @module patterns/cli-enhancers
|
|
11
|
+
* @author Avi Fenesh
|
|
12
|
+
* @license MIT
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const { execSync, execFileSync } = require('child_process');
|
|
16
|
+
const path = require('path');
|
|
17
|
+
const fs = require('fs');
|
|
18
|
+
// Note: escapeDoubleQuotes no longer needed - using execFileSync with arg arrays
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Cache for tool availability (per-repo)
|
|
22
|
+
* Key: repoPath, Value: { tools: {...}, languages: [...], timestamp: Date }
|
|
23
|
+
*/
|
|
24
|
+
const toolCache = new Map();
|
|
25
|
+
const CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Supported languages (must match slop-patterns.js)
|
|
29
|
+
*/
|
|
30
|
+
const SUPPORTED_LANGUAGES = ['javascript', 'typescript', 'python', 'rust', 'go'];
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* CLI tool definitions organized by language
|
|
34
|
+
* Only includes tools for supported languages
|
|
35
|
+
*/
|
|
36
|
+
const CLI_TOOLS = {
|
|
37
|
+
// Cross-language tools
|
|
38
|
+
jscpd: {
|
|
39
|
+
name: 'jscpd',
|
|
40
|
+
description: 'Copy/paste detector for code duplication',
|
|
41
|
+
checkCommand: 'jscpd --version',
|
|
42
|
+
installHint: 'npm install -g jscpd',
|
|
43
|
+
languages: ['javascript', 'typescript', 'python', 'go', 'rust']
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
// JavaScript/TypeScript tools
|
|
47
|
+
madge: {
|
|
48
|
+
name: 'madge',
|
|
49
|
+
description: 'Circular dependency detector',
|
|
50
|
+
checkCommand: 'madge --version',
|
|
51
|
+
installHint: 'npm install -g madge',
|
|
52
|
+
languages: ['javascript', 'typescript']
|
|
53
|
+
},
|
|
54
|
+
escomplex: {
|
|
55
|
+
name: 'escomplex',
|
|
56
|
+
description: 'Cyclomatic complexity analyzer',
|
|
57
|
+
checkCommand: 'escomplex --version',
|
|
58
|
+
installHint: 'npm install -g escomplex',
|
|
59
|
+
languages: ['javascript']
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
// Python tools
|
|
63
|
+
pylint: {
|
|
64
|
+
name: 'pylint',
|
|
65
|
+
description: 'Python linter with complexity analysis',
|
|
66
|
+
checkCommand: 'pylint --version',
|
|
67
|
+
installHint: 'pip install pylint',
|
|
68
|
+
languages: ['python']
|
|
69
|
+
},
|
|
70
|
+
radon: {
|
|
71
|
+
name: 'radon',
|
|
72
|
+
description: 'Python complexity and maintainability metrics',
|
|
73
|
+
checkCommand: 'radon --version',
|
|
74
|
+
installHint: 'pip install radon',
|
|
75
|
+
languages: ['python']
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
// Go tools
|
|
79
|
+
golangci_lint: {
|
|
80
|
+
name: 'golangci-lint',
|
|
81
|
+
description: 'Go linters aggregator with complexity checks',
|
|
82
|
+
checkCommand: 'golangci-lint --version',
|
|
83
|
+
installHint: 'go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest',
|
|
84
|
+
languages: ['go']
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
// Rust tools
|
|
88
|
+
clippy: {
|
|
89
|
+
name: 'cargo-clippy',
|
|
90
|
+
description: 'Rust linter with code smell detection',
|
|
91
|
+
checkCommand: 'cargo clippy --version',
|
|
92
|
+
installHint: 'rustup component add clippy',
|
|
93
|
+
languages: ['rust']
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Check if a CLI tool is available in PATH
|
|
99
|
+
* Uses execFileSync to avoid shell injection risks
|
|
100
|
+
*
|
|
101
|
+
* @param {string} command - Command to check (e.g., 'jscpd --version')
|
|
102
|
+
* @returns {boolean} True if tool is available
|
|
103
|
+
*/
|
|
104
|
+
function isToolAvailable(command) {
|
|
105
|
+
try {
|
|
106
|
+
// Parse command into executable and args for safer execution
|
|
107
|
+
const parts = command.split(/\s+/);
|
|
108
|
+
const executable = parts[0];
|
|
109
|
+
const args = parts.slice(1);
|
|
110
|
+
execFileSync(executable, args, {
|
|
111
|
+
stdio: 'pipe',
|
|
112
|
+
timeout: 5000,
|
|
113
|
+
windowsHide: true
|
|
114
|
+
});
|
|
115
|
+
return true;
|
|
116
|
+
} catch {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Get cache key for a repo
|
|
123
|
+
* @param {string} repoPath - Repository root path
|
|
124
|
+
* @returns {string} Cache key
|
|
125
|
+
*/
|
|
126
|
+
function getCacheKey(repoPath) {
|
|
127
|
+
return path.resolve(repoPath);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Check if cache is valid
|
|
132
|
+
* @param {Object} cacheEntry - Cache entry
|
|
133
|
+
* @returns {boolean} True if cache is still valid
|
|
134
|
+
*/
|
|
135
|
+
function isCacheValid(cacheEntry) {
|
|
136
|
+
if (!cacheEntry) return false;
|
|
137
|
+
return Date.now() - cacheEntry.timestamp < CACHE_TTL_MS;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Clear the tool cache (useful for testing)
|
|
142
|
+
*/
|
|
143
|
+
function clearCache() {
|
|
144
|
+
toolCache.clear();
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Detect primary language(s) of a repository based on file extensions and config files
|
|
149
|
+
*
|
|
150
|
+
* @param {string} repoPath - Repository root path
|
|
151
|
+
* @returns {string[]} Array of detected languages (only supported ones)
|
|
152
|
+
*/
|
|
153
|
+
function detectProjectLanguages(repoPath) {
|
|
154
|
+
const languages = new Set();
|
|
155
|
+
|
|
156
|
+
// Check for language-specific config files
|
|
157
|
+
const configIndicators = {
|
|
158
|
+
'package.json': ['javascript', 'typescript'],
|
|
159
|
+
'tsconfig.json': ['typescript'],
|
|
160
|
+
'requirements.txt': ['python'],
|
|
161
|
+
'setup.py': ['python'],
|
|
162
|
+
'pyproject.toml': ['python'],
|
|
163
|
+
'Pipfile': ['python'],
|
|
164
|
+
'go.mod': ['go'],
|
|
165
|
+
'go.sum': ['go'],
|
|
166
|
+
'Cargo.toml': ['rust']
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
for (const [file, langs] of Object.entries(configIndicators)) {
|
|
170
|
+
if (fs.existsSync(path.join(repoPath, file))) {
|
|
171
|
+
langs.forEach(l => languages.add(l));
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// If no config files found, scan for source files
|
|
176
|
+
if (languages.size === 0) {
|
|
177
|
+
const extensionMap = {
|
|
178
|
+
'.js': 'javascript',
|
|
179
|
+
'.jsx': 'javascript',
|
|
180
|
+
'.mjs': 'javascript',
|
|
181
|
+
'.cjs': 'javascript',
|
|
182
|
+
'.ts': 'typescript',
|
|
183
|
+
'.tsx': 'typescript',
|
|
184
|
+
'.py': 'python',
|
|
185
|
+
'.go': 'go',
|
|
186
|
+
'.rs': 'rust'
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
// Quick scan of top-level and src/ directories
|
|
190
|
+
const dirsToScan = [repoPath, path.join(repoPath, 'src'), path.join(repoPath, 'lib')];
|
|
191
|
+
|
|
192
|
+
for (const dir of dirsToScan) {
|
|
193
|
+
if (!fs.existsSync(dir)) continue;
|
|
194
|
+
try {
|
|
195
|
+
const files = fs.readdirSync(dir);
|
|
196
|
+
for (const file of files) {
|
|
197
|
+
const ext = path.extname(file).toLowerCase();
|
|
198
|
+
if (extensionMap[ext]) {
|
|
199
|
+
languages.add(extensionMap[ext]);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
} catch {
|
|
203
|
+
// Directory not readable
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Filter to only supported languages
|
|
209
|
+
const result = Array.from(languages).filter(l => SUPPORTED_LANGUAGES.includes(l));
|
|
210
|
+
|
|
211
|
+
// Default to javascript if nothing detected
|
|
212
|
+
if (result.length === 0) {
|
|
213
|
+
result.push('javascript');
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return result;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Get tools relevant for specific languages
|
|
221
|
+
*
|
|
222
|
+
* @param {string[]} languages - Array of language names
|
|
223
|
+
* @returns {Object} Filtered CLI_TOOLS for the specified languages
|
|
224
|
+
*/
|
|
225
|
+
function getToolsForLanguages(languages) {
|
|
226
|
+
const relevant = {};
|
|
227
|
+
|
|
228
|
+
for (const [toolName, tool] of Object.entries(CLI_TOOLS)) {
|
|
229
|
+
if (tool.languages.some(lang => languages.includes(lang))) {
|
|
230
|
+
relevant[toolName] = tool;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return relevant;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Detect which CLI tools are available on the system
|
|
239
|
+
* Uses cache when available
|
|
240
|
+
*
|
|
241
|
+
* @param {string[]} [languages] - Optional languages to filter tools for
|
|
242
|
+
* @param {string} [repoPath] - Optional repo path for caching
|
|
243
|
+
* @returns {Object} Object with tool names as keys and availability as boolean values
|
|
244
|
+
*/
|
|
245
|
+
function detectAvailableTools(languages = null, repoPath = null) {
|
|
246
|
+
// Check cache if repoPath provided
|
|
247
|
+
if (repoPath) {
|
|
248
|
+
const cacheKey = getCacheKey(repoPath);
|
|
249
|
+
const cached = toolCache.get(cacheKey);
|
|
250
|
+
if (isCacheValid(cached)) {
|
|
251
|
+
// Return cached tools filtered by languages if specified
|
|
252
|
+
if (languages) {
|
|
253
|
+
const relevantTools = getToolsForLanguages(languages);
|
|
254
|
+
const filtered = {};
|
|
255
|
+
for (const name of Object.keys(relevantTools)) {
|
|
256
|
+
filtered[name] = cached.tools[name] || false;
|
|
257
|
+
}
|
|
258
|
+
return filtered;
|
|
259
|
+
}
|
|
260
|
+
return { ...cached.tools };
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Get tools to check
|
|
265
|
+
const toolsToCheck = languages ? getToolsForLanguages(languages) : CLI_TOOLS;
|
|
266
|
+
const result = {};
|
|
267
|
+
|
|
268
|
+
for (const [toolName, tool] of Object.entries(toolsToCheck)) {
|
|
269
|
+
result[toolName] = isToolAvailable(tool.checkCommand);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Update cache if repoPath provided
|
|
273
|
+
if (repoPath) {
|
|
274
|
+
const cacheKey = getCacheKey(repoPath);
|
|
275
|
+
const existing = toolCache.get(cacheKey) || {};
|
|
276
|
+
toolCache.set(cacheKey, {
|
|
277
|
+
tools: { ...existing.tools, ...result },
|
|
278
|
+
languages: languages || existing.languages || [],
|
|
279
|
+
timestamp: Date.now()
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return result;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Get tool availability for a specific repo (with caching)
|
|
288
|
+
*
|
|
289
|
+
* @param {string} repoPath - Repository root path
|
|
290
|
+
* @param {Object} [options] - Options
|
|
291
|
+
* @param {boolean} [options.forceRefresh=false] - Force cache refresh
|
|
292
|
+
* @returns {{ available: Object, missing: string[], languages: string[] }} Tool availability info
|
|
293
|
+
*/
|
|
294
|
+
function getToolAvailabilityForRepo(repoPath, options = {}) {
|
|
295
|
+
const cacheKey = getCacheKey(repoPath);
|
|
296
|
+
|
|
297
|
+
// Check cache unless force refresh
|
|
298
|
+
if (!options.forceRefresh) {
|
|
299
|
+
const cached = toolCache.get(cacheKey);
|
|
300
|
+
if (isCacheValid(cached) && cached.languages && cached.languages.length > 0) {
|
|
301
|
+
const relevantTools = getToolsForLanguages(cached.languages);
|
|
302
|
+
const missing = Object.keys(relevantTools).filter(t => !cached.tools[t]);
|
|
303
|
+
return {
|
|
304
|
+
available: { ...cached.tools },
|
|
305
|
+
missing,
|
|
306
|
+
languages: [...cached.languages]
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Detect languages
|
|
312
|
+
const languages = detectProjectLanguages(repoPath);
|
|
313
|
+
|
|
314
|
+
// Detect tools for those languages
|
|
315
|
+
const available = detectAvailableTools(languages, repoPath);
|
|
316
|
+
|
|
317
|
+
// Find missing tools
|
|
318
|
+
const relevantTools = getToolsForLanguages(languages);
|
|
319
|
+
const missing = Object.keys(relevantTools).filter(t => !available[t]);
|
|
320
|
+
|
|
321
|
+
// Update cache
|
|
322
|
+
toolCache.set(cacheKey, {
|
|
323
|
+
tools: available,
|
|
324
|
+
languages,
|
|
325
|
+
timestamp: Date.now()
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
return { available, missing, languages };
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Run duplicate code detection using jscpd
|
|
333
|
+
*
|
|
334
|
+
* @param {string} repoPath - Repository root path
|
|
335
|
+
* @param {Object} options - Options
|
|
336
|
+
* @param {number} [options.minLines=5] - Minimum lines for duplicate detection
|
|
337
|
+
* @param {number} [options.minTokens=50] - Minimum tokens for duplicate detection
|
|
338
|
+
* @returns {Array|null} Duplicates found, or null if tool not available
|
|
339
|
+
*/
|
|
340
|
+
function runDuplicateDetection(repoPath, options = {}) {
|
|
341
|
+
if (!isToolAvailable(CLI_TOOLS.jscpd.checkCommand)) {
|
|
342
|
+
return null;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
const minLines = options.minLines || 5;
|
|
346
|
+
const minTokens = options.minTokens || 50;
|
|
347
|
+
|
|
348
|
+
try {
|
|
349
|
+
// Run jscpd with JSON output
|
|
350
|
+
// Use execFileSync with arg array to prevent command injection (no shell interpretation)
|
|
351
|
+
const outputPath = process.platform === 'win32' ? 'NUL' : '/dev/null';
|
|
352
|
+
const args = [
|
|
353
|
+
repoPath,
|
|
354
|
+
'--min-lines', String(minLines),
|
|
355
|
+
'--min-tokens', String(minTokens),
|
|
356
|
+
'--reporters', 'json',
|
|
357
|
+
'--output', outputPath,
|
|
358
|
+
'--silent'
|
|
359
|
+
];
|
|
360
|
+
|
|
361
|
+
const result = execFileSync('jscpd', args, {
|
|
362
|
+
stdio: ['pipe', 'pipe', 'pipe'], // stdin, stdout, stderr all piped
|
|
363
|
+
timeout: 60000,
|
|
364
|
+
windowsHide: true,
|
|
365
|
+
cwd: repoPath,
|
|
366
|
+
encoding: 'utf8'
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
// Parse JSON output
|
|
370
|
+
try {
|
|
371
|
+
const report = JSON.parse(result);
|
|
372
|
+
const duplicates = [];
|
|
373
|
+
|
|
374
|
+
if (report.duplicates) {
|
|
375
|
+
for (const dup of report.duplicates) {
|
|
376
|
+
duplicates.push({
|
|
377
|
+
firstFile: dup.firstFile?.name || 'unknown',
|
|
378
|
+
firstLine: dup.firstFile?.start || 0,
|
|
379
|
+
secondFile: dup.secondFile?.name || 'unknown',
|
|
380
|
+
secondLine: dup.secondFile?.start || 0,
|
|
381
|
+
lines: dup.lines || 0,
|
|
382
|
+
tokens: dup.tokens || 0,
|
|
383
|
+
fragment: dup.fragment?.substring(0, 100) || ''
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
return duplicates;
|
|
389
|
+
} catch {
|
|
390
|
+
// JSON parsing failed, return empty array
|
|
391
|
+
return [];
|
|
392
|
+
}
|
|
393
|
+
} catch {
|
|
394
|
+
// Tool execution failed
|
|
395
|
+
return null;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Run circular dependency detection using madge
|
|
401
|
+
*
|
|
402
|
+
* @param {string} repoPath - Repository root path
|
|
403
|
+
* @param {Object} options - Options
|
|
404
|
+
* @param {string} [options.entry] - Entry file (defaults to src/index.js or index.js)
|
|
405
|
+
* @returns {Array|null} Circular dependency cycles, or null if tool not available
|
|
406
|
+
*/
|
|
407
|
+
function runDependencyAnalysis(repoPath, options = {}) {
|
|
408
|
+
if (!isToolAvailable(CLI_TOOLS.madge.checkCommand)) {
|
|
409
|
+
return null;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// Determine entry point
|
|
413
|
+
let entry = options.entry;
|
|
414
|
+
if (!entry) {
|
|
415
|
+
const possibleEntries = [
|
|
416
|
+
'src/index.js',
|
|
417
|
+
'src/index.ts',
|
|
418
|
+
'index.js',
|
|
419
|
+
'index.ts',
|
|
420
|
+
'lib/index.js',
|
|
421
|
+
'main.js'
|
|
422
|
+
];
|
|
423
|
+
|
|
424
|
+
for (const e of possibleEntries) {
|
|
425
|
+
if (fs.existsSync(path.join(repoPath, e))) {
|
|
426
|
+
entry = e;
|
|
427
|
+
break;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
if (!entry) {
|
|
433
|
+
// No entry point found, scan entire directory
|
|
434
|
+
entry = '.';
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
try {
|
|
438
|
+
// Run madge with circular flag and JSON output
|
|
439
|
+
// Use execFileSync with arg array to prevent command injection (no shell interpretation)
|
|
440
|
+
const args = ['--circular', '--json', entry];
|
|
441
|
+
|
|
442
|
+
const result = execFileSync('madge', args, {
|
|
443
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
444
|
+
timeout: 60000,
|
|
445
|
+
windowsHide: true,
|
|
446
|
+
cwd: repoPath,
|
|
447
|
+
encoding: 'utf8'
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
// Parse JSON output
|
|
451
|
+
try {
|
|
452
|
+
const cycles = JSON.parse(result);
|
|
453
|
+
// madge returns array of arrays (each cycle is an array of file paths)
|
|
454
|
+
return Array.isArray(cycles) ? cycles : [];
|
|
455
|
+
} catch {
|
|
456
|
+
return [];
|
|
457
|
+
}
|
|
458
|
+
} catch {
|
|
459
|
+
// Tool execution failed
|
|
460
|
+
return null;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* Run complexity analysis using escomplex
|
|
466
|
+
*
|
|
467
|
+
* @param {string} repoPath - Repository root path
|
|
468
|
+
* @param {string[]} targetFiles - Files to analyze
|
|
469
|
+
* @param {Object} options - Options
|
|
470
|
+
* @returns {Array|null} Complexity results, or null if tool not available
|
|
471
|
+
*/
|
|
472
|
+
function runComplexityAnalysis(repoPath, targetFiles, options = {}) {
|
|
473
|
+
if (!isToolAvailable(CLI_TOOLS.escomplex.checkCommand)) {
|
|
474
|
+
return null;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
const results = [];
|
|
478
|
+
|
|
479
|
+
// escomplex works on individual files
|
|
480
|
+
for (const file of targetFiles) {
|
|
481
|
+
// Only analyze JS/TS files
|
|
482
|
+
if (!file.match(/\.[jt]sx?$/)) continue;
|
|
483
|
+
|
|
484
|
+
const filePath = path.isAbsolute(file) ? file : path.join(repoPath, file);
|
|
485
|
+
|
|
486
|
+
try {
|
|
487
|
+
// Use execFileSync with arg array to prevent command injection (no shell interpretation)
|
|
488
|
+
const args = [filePath, '--format', 'json'];
|
|
489
|
+
|
|
490
|
+
const result = execFileSync('escomplex', args, {
|
|
491
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
492
|
+
timeout: 30000,
|
|
493
|
+
windowsHide: true,
|
|
494
|
+
cwd: repoPath,
|
|
495
|
+
encoding: 'utf8'
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
try {
|
|
499
|
+
const report = JSON.parse(result);
|
|
500
|
+
|
|
501
|
+
// Extract function-level complexity
|
|
502
|
+
if (report.functions) {
|
|
503
|
+
for (const fn of report.functions) {
|
|
504
|
+
results.push({
|
|
505
|
+
file,
|
|
506
|
+
name: fn.name || 'anonymous',
|
|
507
|
+
line: fn.line || 0,
|
|
508
|
+
complexity: fn.cyclomatic || 0,
|
|
509
|
+
halstead: fn.halstead?.difficulty || 0,
|
|
510
|
+
sloc: fn.sloc?.logical || 0
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// Also include module-level metrics
|
|
516
|
+
if (report.aggregate) {
|
|
517
|
+
results.push({
|
|
518
|
+
file,
|
|
519
|
+
name: 'module',
|
|
520
|
+
line: 0,
|
|
521
|
+
complexity: report.aggregate.cyclomatic || 0,
|
|
522
|
+
halstead: report.aggregate.halstead?.difficulty || 0,
|
|
523
|
+
sloc: report.aggregate.sloc?.logical || 0,
|
|
524
|
+
maintainability: report.maintainability || 0
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
} catch {
|
|
528
|
+
// JSON parsing failed for this file
|
|
529
|
+
}
|
|
530
|
+
} catch {
|
|
531
|
+
// Tool execution failed for this file
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
return results.length > 0 ? results : null;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/**
|
|
539
|
+
* Get user-friendly message about missing tools (language-aware)
|
|
540
|
+
*
|
|
541
|
+
* @param {string[]} missingTools - Array of missing tool names
|
|
542
|
+
* @param {string[]} [languages] - Detected languages (for context in message)
|
|
543
|
+
* @returns {string} Formatted message
|
|
544
|
+
*/
|
|
545
|
+
function getMissingToolsMessage(missingTools, languages = null) {
|
|
546
|
+
if (!missingTools || missingTools.length === 0) {
|
|
547
|
+
return '';
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
// Filter to only known tools
|
|
551
|
+
const validTools = missingTools.filter(t => CLI_TOOLS[t]);
|
|
552
|
+
if (validTools.length === 0) {
|
|
553
|
+
return '';
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
let message = '\n## Enhanced Analysis Available\n\n';
|
|
557
|
+
|
|
558
|
+
if (languages && languages.length > 0) {
|
|
559
|
+
message += `Detected project languages: ${languages.join(', ')}\n\n`;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
message += 'For deeper analysis, consider installing:\n\n';
|
|
563
|
+
|
|
564
|
+
for (const toolName of validTools) {
|
|
565
|
+
const tool = CLI_TOOLS[toolName];
|
|
566
|
+
if (tool) {
|
|
567
|
+
message += `- **${tool.name}**: ${tool.description}\n`;
|
|
568
|
+
message += ` Install: \`${tool.installHint}\`\n`;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
message += '\nThese tools are optional and enhance detection capabilities.\n';
|
|
573
|
+
|
|
574
|
+
return message;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
/**
|
|
578
|
+
* Get all CLI tool definitions
|
|
579
|
+
*
|
|
580
|
+
* @returns {Object} CLI tool definitions
|
|
581
|
+
*/
|
|
582
|
+
function getToolDefinitions() {
|
|
583
|
+
return { ...CLI_TOOLS };
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
/**
|
|
587
|
+
* Get supported languages list
|
|
588
|
+
*
|
|
589
|
+
* @returns {string[]} Array of supported language names
|
|
590
|
+
*/
|
|
591
|
+
function getSupportedLanguages() {
|
|
592
|
+
return [...SUPPORTED_LANGUAGES];
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
module.exports = {
|
|
596
|
+
detectAvailableTools,
|
|
597
|
+
detectProjectLanguages,
|
|
598
|
+
getToolsForLanguages,
|
|
599
|
+
getToolAvailabilityForRepo,
|
|
600
|
+
runDuplicateDetection,
|
|
601
|
+
runDependencyAnalysis,
|
|
602
|
+
runComplexityAnalysis,
|
|
603
|
+
getMissingToolsMessage,
|
|
604
|
+
getToolDefinitions,
|
|
605
|
+
getSupportedLanguages,
|
|
606
|
+
clearCache,
|
|
607
|
+
// Exported for testing
|
|
608
|
+
isToolAvailable,
|
|
609
|
+
CLI_TOOLS,
|
|
610
|
+
SUPPORTED_LANGUAGES
|
|
611
|
+
};
|