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,622 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-Learning Suppression System for /enhance
|
|
3
|
+
*
|
|
4
|
+
* Automatically detects obvious false positives and stores them
|
|
5
|
+
* for future runs, making the enhance tool smarter over time.
|
|
6
|
+
*
|
|
7
|
+
* Key features:
|
|
8
|
+
* - Pattern-specific heuristics for common false positives
|
|
9
|
+
* - Cross-platform storage using getSuppressionPath()
|
|
10
|
+
* - 0.90+ confidence threshold for auto-suppression
|
|
11
|
+
* - Backward compatible with existing suppression.js
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
const path = require('path');
|
|
16
|
+
const { execFileSync } = require('child_process');
|
|
17
|
+
|
|
18
|
+
// Try to import cross-platform helpers
|
|
19
|
+
let getSuppressionPath;
|
|
20
|
+
try {
|
|
21
|
+
const crossPlatform = require('../cross-platform');
|
|
22
|
+
getSuppressionPath = crossPlatform.getSuppressionPath;
|
|
23
|
+
} catch {
|
|
24
|
+
// Fallback for when running from plugin directory
|
|
25
|
+
const os = require('os');
|
|
26
|
+
getSuppressionPath = () => path.join(os.homedir(), '.claude', 'enhance', 'suppressions.json');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Minimum confidence threshold for auto-suppression
|
|
31
|
+
*/
|
|
32
|
+
const CONFIDENCE_THRESHOLD = 0.90;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Maximum suppressions per project (prevents file bloat)
|
|
36
|
+
*/
|
|
37
|
+
const MAX_SUPPRESSIONS_PER_PROJECT = 100;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Suppression expiry in milliseconds (6 months)
|
|
41
|
+
*/
|
|
42
|
+
const SUPPRESSION_EXPIRY_MS = 6 * 30 * 24 * 60 * 60 * 1000;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Pattern-specific heuristics for detecting false positives
|
|
46
|
+
* Each heuristic returns { reason, confidence } or null
|
|
47
|
+
*/
|
|
48
|
+
const PATTERN_HEURISTICS = {
|
|
49
|
+
/**
|
|
50
|
+
* vague_instructions: Detects when vague terms appear in pattern documentation
|
|
51
|
+
* False positive when content describes the pattern itself
|
|
52
|
+
*/
|
|
53
|
+
vague_instructions: (finding, content, context) => {
|
|
54
|
+
const contentLower = content.toLowerCase();
|
|
55
|
+
|
|
56
|
+
// Check if file is pattern documentation describing vague language detection
|
|
57
|
+
const isPatternDoc =
|
|
58
|
+
/pattern.*detect.*usually|example.*vague|fuzzy.*language.*like/i.test(content) ||
|
|
59
|
+
/vague.*terms.*like|"usually".*"sometimes"/i.test(content);
|
|
60
|
+
|
|
61
|
+
if (isPatternDoc) {
|
|
62
|
+
return {
|
|
63
|
+
reason: 'Pattern documentation self-reference (describes vague language detection)',
|
|
64
|
+
confidence: 0.98
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Check if in table describing patterns
|
|
69
|
+
const line = finding.line || 0;
|
|
70
|
+
const lines = content.split('\n');
|
|
71
|
+
const surroundingLines = lines.slice(Math.max(0, line - 5), line + 5).join('\n');
|
|
72
|
+
if (/\|.*vague.*\||\|.*usually.*sometimes.*\|/i.test(surroundingLines)) {
|
|
73
|
+
return {
|
|
74
|
+
reason: 'Pattern table documentation',
|
|
75
|
+
confidence: 0.95
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return null;
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* aggressive_emphasis: Detects legitimate workflow enforcement usage
|
|
84
|
+
* False positive in workflow gates and critical agent constraints
|
|
85
|
+
*/
|
|
86
|
+
aggressive_emphasis: (finding, content, context) => {
|
|
87
|
+
const line = finding.line || 0;
|
|
88
|
+
const lines = content.split('\n');
|
|
89
|
+
const surroundingLines = lines.slice(Math.max(0, line - 20), line + 20).join('\n');
|
|
90
|
+
|
|
91
|
+
// Check for workflow gate context
|
|
92
|
+
const isWorkflowGate =
|
|
93
|
+
/WORKFLOW\s+GATES?/i.test(surroundingLines) ||
|
|
94
|
+
/\[CRITICAL\]\s*NO\s+AGENT\s+may/i.test(surroundingLines) ||
|
|
95
|
+
/MUST\s+NOT\s+DO|NEVER\s+skip|DO\s+NOT\s+proceed/i.test(surroundingLines) ||
|
|
96
|
+
/SubagentStop\s+hook|phase\s+9\s+review/i.test(surroundingLines);
|
|
97
|
+
|
|
98
|
+
if (isWorkflowGate) {
|
|
99
|
+
return {
|
|
100
|
+
reason: 'Workflow enforcement requires emphasis for gates',
|
|
101
|
+
confidence: 0.95
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Check for critical rules section
|
|
106
|
+
const isCriticalRules =
|
|
107
|
+
/critical-rules|Critical\s+Rules.*Priority/i.test(surroundingLines) ||
|
|
108
|
+
/<critical-rules>/i.test(surroundingLines);
|
|
109
|
+
|
|
110
|
+
if (isCriticalRules) {
|
|
111
|
+
return {
|
|
112
|
+
reason: 'Critical rules section requires emphasis',
|
|
113
|
+
confidence: 0.93
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return null;
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* missing_examples: Detects orchestrator/workflow files that delegate to subagents
|
|
122
|
+
* False positive when file is orchestrator that spawns other agents
|
|
123
|
+
*/
|
|
124
|
+
missing_examples: (finding, content, context) => {
|
|
125
|
+
const filePath = finding.file || context?.file || '';
|
|
126
|
+
const fileNameLower = path.basename(filePath).toLowerCase();
|
|
127
|
+
|
|
128
|
+
// Check if file is an orchestrator
|
|
129
|
+
const isOrchestrator =
|
|
130
|
+
fileNameLower.includes('orchestrator') ||
|
|
131
|
+
fileNameLower.includes('coordinator') ||
|
|
132
|
+
/Task\s*\(\s*\{[\s\S]*subagent_type/i.test(content);
|
|
133
|
+
|
|
134
|
+
if (isOrchestrator) {
|
|
135
|
+
return {
|
|
136
|
+
reason: 'Orchestrator file delegates to subagents (examples in subagents)',
|
|
137
|
+
confidence: 0.92
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Check if workflow command that invokes agents
|
|
142
|
+
const isWorkflowCommand =
|
|
143
|
+
/spawn.*agent|invoke.*agent|Task\s*\(\s*\{/i.test(content) &&
|
|
144
|
+
fileNameLower.endsWith('.md');
|
|
145
|
+
|
|
146
|
+
if (isWorkflowCommand) {
|
|
147
|
+
return {
|
|
148
|
+
reason: 'Workflow command invokes agents with examples',
|
|
149
|
+
confidence: 0.90
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return null;
|
|
154
|
+
},
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* missing_output_format: Detects files that spawn subagents with output specs
|
|
158
|
+
* False positive when subagent is responsible for output format
|
|
159
|
+
*/
|
|
160
|
+
missing_output_format: (finding, content, context) => {
|
|
161
|
+
// Check if content spawns subagents with their own output specs
|
|
162
|
+
const spawnsSubagent =
|
|
163
|
+
/subagent_type|spawn.*agent|Task\s*\(\s*\{/i.test(content) ||
|
|
164
|
+
/enhance:.*-enhancer|enhance:.*-reporter/i.test(content);
|
|
165
|
+
|
|
166
|
+
if (spawnsSubagent) {
|
|
167
|
+
return {
|
|
168
|
+
reason: 'Delegates output to subagent (subagent defines format)',
|
|
169
|
+
confidence: 0.91
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return null;
|
|
174
|
+
},
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* missing_constraints: Detects files that already have constraint sections
|
|
178
|
+
* False positive when "## What Agent MUST NOT Do" or similar exists
|
|
179
|
+
*/
|
|
180
|
+
missing_constraints: (finding, content, context) => {
|
|
181
|
+
// Check for constraint section presence
|
|
182
|
+
const hasConstraintSection =
|
|
183
|
+
/##\s*What\s+.*MUST\s+NOT\s+Do/i.test(content) ||
|
|
184
|
+
/##\s*Constraints/i.test(content) ||
|
|
185
|
+
/<constraints>/i.test(content) ||
|
|
186
|
+
/##\s*Critical\s+Constraints/i.test(content) ||
|
|
187
|
+
/WORKFLOW\s+GATES/i.test(content);
|
|
188
|
+
|
|
189
|
+
if (hasConstraintSection) {
|
|
190
|
+
return {
|
|
191
|
+
reason: 'File has constraint section (different heading format)',
|
|
192
|
+
confidence: 0.94
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return null;
|
|
197
|
+
},
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* redundant_cot: Detects legitimate step-by-step for complex workflows
|
|
201
|
+
* False positive in multi-phase workflow prompts
|
|
202
|
+
*/
|
|
203
|
+
redundant_cot: (finding, content, context) => {
|
|
204
|
+
// Check if multi-phase workflow
|
|
205
|
+
const isMultiPhase =
|
|
206
|
+
/Phase\s+\d+:|Step\s+\d+:|###\s+Phase/i.test(content) &&
|
|
207
|
+
/Phase\s+[2-9]:|Step\s+[2-9]:/i.test(content);
|
|
208
|
+
|
|
209
|
+
if (isMultiPhase) {
|
|
210
|
+
return {
|
|
211
|
+
reason: 'Multi-phase workflow requires step guidance',
|
|
212
|
+
confidence: 0.91
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Check if a finding is likely a false positive
|
|
222
|
+
*
|
|
223
|
+
* @param {Object} finding - The finding to check
|
|
224
|
+
* @param {string} content - File content
|
|
225
|
+
* @param {Object} context - Additional context { file, analyzer, suppressions }
|
|
226
|
+
* @returns {{ reason: string, confidence: number } | null}
|
|
227
|
+
*/
|
|
228
|
+
function isLikelyFalsePositive(finding, content, context = {}) {
|
|
229
|
+
const patternId = (finding.patternId || finding.id || '').toLowerCase();
|
|
230
|
+
|
|
231
|
+
// Skip if no content
|
|
232
|
+
if (!content || typeof content !== 'string') {
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Check pattern-specific heuristic
|
|
237
|
+
const heuristic = PATTERN_HEURISTICS[patternId];
|
|
238
|
+
if (heuristic) {
|
|
239
|
+
const result = heuristic(finding, content, context);
|
|
240
|
+
if (result && result.confidence >= CONFIDENCE_THRESHOLD) {
|
|
241
|
+
return result;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Generic self-reference detection: pattern names in pattern docs
|
|
246
|
+
if (finding.file && isPatternDocumentation(finding.file, content, patternId)) {
|
|
247
|
+
return {
|
|
248
|
+
reason: 'Pattern self-reference in documentation',
|
|
249
|
+
confidence: 0.96
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return null;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Check if file is pattern documentation that mentions the pattern
|
|
258
|
+
*
|
|
259
|
+
* @param {string} filePath - File path
|
|
260
|
+
* @param {string} content - File content
|
|
261
|
+
* @param {string} patternId - Pattern being checked
|
|
262
|
+
* @returns {boolean}
|
|
263
|
+
*/
|
|
264
|
+
function isPatternDocumentation(filePath, content, patternId) {
|
|
265
|
+
const fileName = path.basename(filePath).toLowerCase();
|
|
266
|
+
|
|
267
|
+
// Pattern documentation file names
|
|
268
|
+
const isPatternFile =
|
|
269
|
+
fileName.includes('pattern') ||
|
|
270
|
+
fileName.includes('enhance.md') ||
|
|
271
|
+
fileName.includes('enhancer');
|
|
272
|
+
|
|
273
|
+
if (!isPatternFile) return false;
|
|
274
|
+
|
|
275
|
+
// Check if content documents patterns in table format
|
|
276
|
+
const patternIdReadable = patternId.replace(/_/g, ' ');
|
|
277
|
+
const describesPattern =
|
|
278
|
+
new RegExp(`\\|[^|]*${patternId}[^|]*\\|`, 'i').test(content) ||
|
|
279
|
+
new RegExp(`\\|[^|]*${patternIdReadable}[^|]*\\|`, 'i').test(content);
|
|
280
|
+
|
|
281
|
+
return describesPattern;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Get project identifier from git remote or directory hash
|
|
286
|
+
*
|
|
287
|
+
* @param {string} projectRoot - Project root directory
|
|
288
|
+
* @returns {string} Project identifier
|
|
289
|
+
*/
|
|
290
|
+
function getProjectId(projectRoot = process.cwd()) {
|
|
291
|
+
try {
|
|
292
|
+
// Try to get git remote
|
|
293
|
+
const remote = execFileSync('git', ['remote', 'get-url', 'origin'], {
|
|
294
|
+
cwd: projectRoot,
|
|
295
|
+
encoding: 'utf8',
|
|
296
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
|
297
|
+
}).trim();
|
|
298
|
+
|
|
299
|
+
if (remote) {
|
|
300
|
+
// Normalize remote URL to identifier
|
|
301
|
+
// https://github.com/user/repo.git -> github.com/user/repo
|
|
302
|
+
// git@github.com:user/repo.git -> github.com/user/repo
|
|
303
|
+
return remote
|
|
304
|
+
.replace(/^https?:\/\//, '')
|
|
305
|
+
.replace(/^git@/, '')
|
|
306
|
+
.replace(/\.git$/, '')
|
|
307
|
+
.replace(':', '/');
|
|
308
|
+
}
|
|
309
|
+
} catch {
|
|
310
|
+
// Git not available or not a git repo
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Fallback to directory name hash
|
|
314
|
+
const absPath = path.resolve(projectRoot);
|
|
315
|
+
return `local:${path.basename(absPath)}`;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Load auto-learned suppressions for a project
|
|
320
|
+
*
|
|
321
|
+
* @param {string} suppressionPath - Path to suppressions.json
|
|
322
|
+
* @param {string} projectId - Project identifier
|
|
323
|
+
* @returns {Object} Auto-learned suppressions { patterns: {}, stats: {} }
|
|
324
|
+
*/
|
|
325
|
+
function loadAutoSuppressions(suppressionPath, projectId) {
|
|
326
|
+
const defaultResult = { patterns: {}, stats: { totalSuppressed: 0 } };
|
|
327
|
+
|
|
328
|
+
try {
|
|
329
|
+
if (!fs.existsSync(suppressionPath)) {
|
|
330
|
+
return defaultResult;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const data = JSON.parse(fs.readFileSync(suppressionPath, 'utf8'));
|
|
334
|
+
const projectData = data.projects?.[projectId];
|
|
335
|
+
|
|
336
|
+
if (!projectData?.auto_learned) {
|
|
337
|
+
return defaultResult;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Prune expired suppressions
|
|
341
|
+
const autoLearned = projectData.auto_learned;
|
|
342
|
+
const now = Date.now();
|
|
343
|
+
const prunedPatterns = {};
|
|
344
|
+
|
|
345
|
+
for (const [patternId, suppression] of Object.entries(autoLearned.patterns || {})) {
|
|
346
|
+
const learnedAt = new Date(suppression.learnedAt).getTime();
|
|
347
|
+
if (now - learnedAt < SUPPRESSION_EXPIRY_MS) {
|
|
348
|
+
prunedPatterns[patternId] = suppression;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
return {
|
|
353
|
+
patterns: prunedPatterns,
|
|
354
|
+
stats: autoLearned.stats || { totalSuppressed: 0 }
|
|
355
|
+
};
|
|
356
|
+
} catch {
|
|
357
|
+
return defaultResult;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Save auto-learned suppressions
|
|
363
|
+
*
|
|
364
|
+
* @param {string} suppressionPath - Path to suppressions.json
|
|
365
|
+
* @param {string} projectId - Project identifier
|
|
366
|
+
* @param {Array} findings - Findings to save as suppressions
|
|
367
|
+
*/
|
|
368
|
+
function saveAutoSuppressions(suppressionPath, projectId, findings) {
|
|
369
|
+
if (!findings || findings.length === 0) return;
|
|
370
|
+
|
|
371
|
+
// Ensure directory exists
|
|
372
|
+
const dir = path.dirname(suppressionPath);
|
|
373
|
+
if (!fs.existsSync(dir)) {
|
|
374
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// Load existing data
|
|
378
|
+
let data = { version: '2.0', projects: {} };
|
|
379
|
+
try {
|
|
380
|
+
if (fs.existsSync(suppressionPath)) {
|
|
381
|
+
data = JSON.parse(fs.readFileSync(suppressionPath, 'utf8'));
|
|
382
|
+
}
|
|
383
|
+
} catch {
|
|
384
|
+
// Start fresh on error
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// Ensure structure
|
|
388
|
+
if (!data.projects) data.projects = {};
|
|
389
|
+
if (!data.projects[projectId]) data.projects[projectId] = {};
|
|
390
|
+
if (!data.projects[projectId].auto_learned) {
|
|
391
|
+
data.projects[projectId].auto_learned = {
|
|
392
|
+
patterns: {},
|
|
393
|
+
stats: { totalSuppressed: 0, lastAnalysis: null }
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
const autoLearned = data.projects[projectId].auto_learned;
|
|
398
|
+
const now = new Date().toISOString();
|
|
399
|
+
|
|
400
|
+
// Group findings by pattern
|
|
401
|
+
const byPattern = {};
|
|
402
|
+
for (const finding of findings) {
|
|
403
|
+
const patternId = (finding.patternId || finding.id || '').toLowerCase();
|
|
404
|
+
if (!patternId) continue;
|
|
405
|
+
|
|
406
|
+
if (!byPattern[patternId]) {
|
|
407
|
+
byPattern[patternId] = {
|
|
408
|
+
files: [],
|
|
409
|
+
reason: finding.suppressionReason || 'Auto-detected false positive',
|
|
410
|
+
confidence: finding.confidence || CONFIDENCE_THRESHOLD,
|
|
411
|
+
learnedAt: now,
|
|
412
|
+
occurrences: 0
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
if (finding.file && !byPattern[patternId].files.includes(finding.file)) {
|
|
417
|
+
byPattern[patternId].files.push(finding.file);
|
|
418
|
+
}
|
|
419
|
+
byPattern[patternId].occurrences++;
|
|
420
|
+
|
|
421
|
+
// Keep highest confidence
|
|
422
|
+
if (finding.confidence > byPattern[patternId].confidence) {
|
|
423
|
+
byPattern[patternId].confidence = finding.confidence;
|
|
424
|
+
byPattern[patternId].reason = finding.suppressionReason;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// Merge with existing patterns
|
|
429
|
+
for (const [patternId, newSuppression] of Object.entries(byPattern)) {
|
|
430
|
+
const existing = autoLearned.patterns[patternId];
|
|
431
|
+
if (existing) {
|
|
432
|
+
// Merge files
|
|
433
|
+
const allFiles = [...new Set([...existing.files, ...newSuppression.files])];
|
|
434
|
+
existing.files = allFiles.slice(0, 50); // Cap files per pattern
|
|
435
|
+
existing.occurrences = (existing.occurrences || 0) + newSuppression.occurrences;
|
|
436
|
+
existing.lastSeen = now;
|
|
437
|
+
// Update confidence if higher
|
|
438
|
+
if (newSuppression.confidence > existing.confidence) {
|
|
439
|
+
existing.confidence = newSuppression.confidence;
|
|
440
|
+
existing.reason = newSuppression.reason;
|
|
441
|
+
}
|
|
442
|
+
} else {
|
|
443
|
+
autoLearned.patterns[patternId] = newSuppression;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// Enforce max suppressions per project
|
|
448
|
+
const patternIds = Object.keys(autoLearned.patterns);
|
|
449
|
+
if (patternIds.length > MAX_SUPPRESSIONS_PER_PROJECT) {
|
|
450
|
+
// Remove oldest patterns
|
|
451
|
+
const sorted = patternIds.sort((a, b) => {
|
|
452
|
+
const aDate = new Date(autoLearned.patterns[a].learnedAt);
|
|
453
|
+
const bDate = new Date(autoLearned.patterns[b].learnedAt);
|
|
454
|
+
return aDate - bDate;
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
const toRemove = sorted.slice(0, patternIds.length - MAX_SUPPRESSIONS_PER_PROJECT);
|
|
458
|
+
for (const id of toRemove) {
|
|
459
|
+
delete autoLearned.patterns[id];
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// Update stats
|
|
464
|
+
autoLearned.stats.totalSuppressed = Object.keys(autoLearned.patterns).length;
|
|
465
|
+
autoLearned.stats.lastAnalysis = now;
|
|
466
|
+
|
|
467
|
+
// Write file
|
|
468
|
+
fs.writeFileSync(suppressionPath, JSON.stringify(data, null, 2));
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* Clear auto-learned suppressions for a project
|
|
473
|
+
*
|
|
474
|
+
* @param {string} suppressionPath - Path to suppressions.json
|
|
475
|
+
* @param {string} projectId - Project identifier
|
|
476
|
+
*/
|
|
477
|
+
function clearAutoSuppressions(suppressionPath, projectId) {
|
|
478
|
+
try {
|
|
479
|
+
if (!fs.existsSync(suppressionPath)) return;
|
|
480
|
+
|
|
481
|
+
const data = JSON.parse(fs.readFileSync(suppressionPath, 'utf8'));
|
|
482
|
+
|
|
483
|
+
if (data.projects?.[projectId]?.auto_learned) {
|
|
484
|
+
data.projects[projectId].auto_learned = {
|
|
485
|
+
patterns: {},
|
|
486
|
+
stats: { totalSuppressed: 0, lastAnalysis: new Date().toISOString() }
|
|
487
|
+
};
|
|
488
|
+
fs.writeFileSync(suppressionPath, JSON.stringify(data, null, 2));
|
|
489
|
+
}
|
|
490
|
+
} catch {
|
|
491
|
+
// Ignore errors
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* Merge auto-learned suppressions with manual suppressions
|
|
497
|
+
*
|
|
498
|
+
* @param {Object} autoLearned - Auto-learned suppressions
|
|
499
|
+
* @param {Object} manual - Manual suppressions (from config)
|
|
500
|
+
* @returns {Object} Merged suppressions
|
|
501
|
+
*/
|
|
502
|
+
function mergeSuppressions(autoLearned, manual) {
|
|
503
|
+
return {
|
|
504
|
+
ignore: {
|
|
505
|
+
patterns: [...(manual.ignore?.patterns || [])],
|
|
506
|
+
files: [...(manual.ignore?.files || [])],
|
|
507
|
+
rules: { ...(manual.ignore?.rules || {}) }
|
|
508
|
+
},
|
|
509
|
+
severity: { ...(manual.severity || {}) },
|
|
510
|
+
auto_learned: autoLearned
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* Export learned suppressions for team sharing
|
|
516
|
+
*
|
|
517
|
+
* @param {string} suppressionPath - Path to suppressions.json
|
|
518
|
+
* @param {string} projectId - Project identifier
|
|
519
|
+
* @returns {Object} Exportable suppression data
|
|
520
|
+
*/
|
|
521
|
+
function exportAutoSuppressions(suppressionPath, projectId) {
|
|
522
|
+
const autoLearned = loadAutoSuppressions(suppressionPath, projectId);
|
|
523
|
+
|
|
524
|
+
return {
|
|
525
|
+
exportedAt: new Date().toISOString(),
|
|
526
|
+
projectId,
|
|
527
|
+
suppressions: autoLearned.patterns,
|
|
528
|
+
stats: autoLearned.stats
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
/**
|
|
533
|
+
* Import shared suppressions
|
|
534
|
+
*
|
|
535
|
+
* @param {string} suppressionPath - Path to suppressions.json
|
|
536
|
+
* @param {string} projectId - Project identifier
|
|
537
|
+
* @param {Object} importData - Data from exportAutoSuppressions
|
|
538
|
+
*/
|
|
539
|
+
function importAutoSuppressions(suppressionPath, projectId, importData) {
|
|
540
|
+
if (!importData?.suppressions) return;
|
|
541
|
+
|
|
542
|
+
// Convert imported suppressions to findings format
|
|
543
|
+
const findings = [];
|
|
544
|
+
for (const [patternId, suppression] of Object.entries(importData.suppressions)) {
|
|
545
|
+
for (const file of suppression.files || []) {
|
|
546
|
+
findings.push({
|
|
547
|
+
patternId,
|
|
548
|
+
file,
|
|
549
|
+
suppressionReason: suppression.reason,
|
|
550
|
+
confidence: suppression.confidence
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
saveAutoSuppressions(suppressionPath, projectId, findings);
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
/**
|
|
559
|
+
* Analyze findings for potential auto-suppression
|
|
560
|
+
* Returns findings that should be auto-suppressed
|
|
561
|
+
*
|
|
562
|
+
* @param {Array} findings - All findings
|
|
563
|
+
* @param {Map<string, string>} fileContents - Map of file path to content
|
|
564
|
+
* @param {Object} options - Options { noLearn, projectRoot }
|
|
565
|
+
* @returns {Array} Findings to be auto-suppressed
|
|
566
|
+
*/
|
|
567
|
+
function analyzeForAutoSuppression(findings, fileContents, options = {}) {
|
|
568
|
+
if (options.noLearn) return [];
|
|
569
|
+
|
|
570
|
+
const toSuppress = [];
|
|
571
|
+
|
|
572
|
+
for (const finding of findings) {
|
|
573
|
+
const filePath = finding.file || finding.filePath;
|
|
574
|
+
const content = fileContents.get(filePath);
|
|
575
|
+
|
|
576
|
+
if (!content) continue;
|
|
577
|
+
|
|
578
|
+
const fpCheck = isLikelyFalsePositive(finding, content, {
|
|
579
|
+
file: filePath,
|
|
580
|
+
projectRoot: options.projectRoot
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
if (fpCheck) {
|
|
584
|
+
toSuppress.push({
|
|
585
|
+
...finding,
|
|
586
|
+
suppressed: true,
|
|
587
|
+
suppressionReason: fpCheck.reason,
|
|
588
|
+
confidence: fpCheck.confidence
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
return toSuppress;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
module.exports = {
|
|
597
|
+
// Constants
|
|
598
|
+
CONFIDENCE_THRESHOLD,
|
|
599
|
+
MAX_SUPPRESSIONS_PER_PROJECT,
|
|
600
|
+
SUPPRESSION_EXPIRY_MS,
|
|
601
|
+
|
|
602
|
+
// Core functions
|
|
603
|
+
isLikelyFalsePositive,
|
|
604
|
+
getProjectId,
|
|
605
|
+
|
|
606
|
+
// Storage functions
|
|
607
|
+
loadAutoSuppressions,
|
|
608
|
+
saveAutoSuppressions,
|
|
609
|
+
clearAutoSuppressions,
|
|
610
|
+
mergeSuppressions,
|
|
611
|
+
|
|
612
|
+
// Import/export
|
|
613
|
+
exportAutoSuppressions,
|
|
614
|
+
importAutoSuppressions,
|
|
615
|
+
|
|
616
|
+
// Analysis helper
|
|
617
|
+
analyzeForAutoSuppression,
|
|
618
|
+
|
|
619
|
+
// For testing
|
|
620
|
+
PATTERN_HEURISTICS,
|
|
621
|
+
isPatternDocumentation
|
|
622
|
+
};
|