pub struct RedundancyDetector { /* private fields */ }Expand description
Analyzes dependency patterns to detect and categorize redundancies.
The RedundancyDetector is the main analysis engine for identifying
optimization opportunities in dependency manifests. It builds a comprehensive
view of how resources use source files and identifies patterns that might
indicate redundancy.
§Analysis Process
- Collection: Gather all resource usages via
add_usage()oranalyze_manifest() - Detection: Run
detect_redundancies()to find redundant patterns - Reporting: Generate warnings or suggestions using helper methods
§Thread Safety
The detector is not thread-safe due to mutable state during analysis. Create separate instances for concurrent analysis operations.
§Memory Usage
The detector maintains an in-memory map of all resource usages. For large manifests with hundreds of dependencies, memory usage scales linearly:
- Each resource usage: ~100 bytes (strings + metadata)
HashMapoverhead: ~25% of total usage data
Implementations§
Source§impl RedundancyDetector
impl RedundancyDetector
Sourcepub fn new() -> Self
pub fn new() -> Self
Creates a new redundancy detector with empty state.
The detector starts with no resource usage data. Use add_usage()
for individual dependencies or analyze_manifest() for complete
manifest analysis.
§Example
use agpm_cli::resolver::redundancy::RedundancyDetector;
let mut detector = RedundancyDetector::new();
// Add usages or analyze manifest...
let redundancies = detector.detect_redundancies();Sourcepub fn add_usage(&mut self, resource_name: String, dep: &ResourceDependency)
pub fn add_usage(&mut self, resource_name: String, dep: &ResourceDependency)
Records a resource usage for redundancy analysis.
This method adds a single resource dependency to the analysis dataset. Local dependencies are automatically filtered out since they don’t have redundancy concerns (each local path is unique).
§Filtering Logic
- Remote Dependencies: Added to analysis (have source + path)
- Local Dependencies: Skipped (path-only, no redundancy issues)
- Invalid Dependencies: Skipped (missing source information)
§Source File Identification
Remote dependencies are identified by their composite key:
source_file = "{source_name}:{resource_path}"This ensures that the same file from different sources is treated as separate resources (no cross-source redundancy detection yet).
§Parameters
resource_name: Name assigned to this resource in the manifestdep: Resource dependency specification from manifest
§Example
use agpm_cli::resolver::redundancy::RedundancyDetector;
use agpm_cli::manifest::{ResourceDependency, DetailedDependency};
let mut detector = RedundancyDetector::new();
// This will be recorded
let remote_dep = ResourceDependency::Detailed(Box::new(DetailedDependency {
source: Some("community".to_string()),
path: "agents/helper.md".to_string(),
version: Some("v1.0.0".to_string()),
branch: None,
rev: None,
command: None,
args: None,
target: None,
filename: None,
dependencies: None,
tool: "claude-code".to_string(),
}));
detector.add_usage("my-helper".to_string(), &remote_dep);
// This will be ignored (local dependency)
let local_dep = ResourceDependency::Simple("../local/helper.md".to_string());
detector.add_usage("local-helper".to_string(), &local_dep);Sourcepub fn analyze_manifest(&mut self, manifest: &Manifest)
pub fn analyze_manifest(&mut self, manifest: &Manifest)
Analyzes all dependencies from a manifest for redundancy patterns.
This is a convenience method that processes all dependencies from
a manifest file in a single operation. It’s equivalent to calling
add_usage() for each dependency individually.
§Processing Scope
The method analyzes:
- Agent Dependencies: From
[agents]section - Snippet Dependencies: From
[snippets]section - Remote Dependencies: Only those with source specifications
Local dependencies are automatically filtered out during analysis.
§Usage Pattern
This method is typically used in the main resolution workflow:
use agpm_cli::resolver::redundancy::RedundancyDetector;
use agpm_cli::manifest::Manifest;
use std::path::Path;
let manifest = Manifest::load(Path::new("agpm.toml"))?;
let mut detector = RedundancyDetector::new();
detector.analyze_manifest(&manifest);
let redundancies = detector.detect_redundancies();
let warning = detector.generate_redundancy_warning(&redundancies);
if !warning.is_empty() {
eprintln!("{}", warning);
}§Performance
- Time Complexity: O(n) where n = total dependencies
- Space Complexity: O(r) where r = remote dependencies
- Memory Usage: Linear with number of remote dependencies
Sourcepub fn detect_redundancies(&self) -> Vec<Redundancy>
pub fn detect_redundancies(&self) -> Vec<Redundancy>
Detects redundancy patterns in the collected resource usages.
This method analyzes all collected resource usages and identifies patterns where multiple resources use the same source file with different version constraints.
§Detection Algorithm
For each source file in the usage map:
- Skip Single Usage: Files used by only one resource are not redundant
- Version Analysis: Collect all unique version constraints for the file
- Redundancy Check: If multiple different versions exist, mark as redundant
§Redundancy Criteria
A source file is considered redundant when:
- Multiple Resources: More than one resource uses the file
- Different Versions: Resources specify different version constraints
§Non-Redundant Cases
These cases are NOT considered redundant:
- Single resource using a source file
- Multiple resources using identical version constraints
- Multiple resources all using “latest” (no version specified)
§Algorithm Complexity
- Time: O(n + k·m) where:
- n = total resource usages
- k = unique source files
- m = average usages per file
- Space: O(r) where r = detected redundancies
§Returns
A vector of Redundancy objects, each representing a source file
with redundant usage patterns. The vector is empty if no redundancies
are detected.
§Example Output
For a manifest with redundant dependencies, this method might return:
[
Redundancy {
source_file: "community:agents/helper.md",
usages: [
ResourceUsage { resource_name: "app-helper", version: Some("v1.0.0") },
ResourceUsage { resource_name: "tool-helper", version: Some("v2.0.0") },
]
}
]Sourcepub fn can_consolidate(&self, redundancy: &Redundancy) -> bool
pub fn can_consolidate(&self, redundancy: &Redundancy) -> bool
Determines if a redundancy could be consolidated to use a single version.
This method analyzes a detected redundancy to determine if all resources using the source file could reasonably be updated to use the same version. This is a heuristic for suggesting consolidation opportunities.
§Consolidation Logic
A redundancy can be consolidated if:
- All resources use the same version constraint (already consolidated)
- All resources use “latest” (no specific versions)
A redundancy cannot be easily consolidated if:
- Resources use different specific versions (may have compatibility reasons)
- Mixed latest and specific versions (may indicate intentional pinning)
§Use Cases
This method helps identify:
- Easy Wins: Redundancies that could be quickly resolved
- Complex Cases: Redundancies that may require careful consideration
- Intentional Patterns: Cases where redundancy might be deliberate
§Parameters
redundancy: The redundancy pattern to analyze
§Returns
true: All usages could likely be consolidated to a single versionfalse: Consolidation would require careful analysis of compatibility
§Example
use agpm_cli::resolver::redundancy::{RedundancyDetector, Redundancy, ResourceUsage};
let detector = RedundancyDetector::new();
// Easy to consolidate (all use latest)
let easy_redundancy = Redundancy {
source_file: "community:agents/helper.md".to_string(),
usages: vec![
ResourceUsage { resource_name: "helper1".to_string(), source_file: "community:agents/helper.md".to_string(), version: None },
ResourceUsage { resource_name: "helper2".to_string(), source_file: "community:agents/helper.md".to_string(), version: None },
]
};
assert!(detector.can_consolidate(&easy_redundancy));
// Hard to consolidate (different versions)
let hard_redundancy = Redundancy {
source_file: "community:agents/helper.md".to_string(),
usages: vec![
ResourceUsage { resource_name: "helper1".to_string(), source_file: "community:agents/helper.md".to_string(), version: Some("v1.0.0".to_string()) },
ResourceUsage { resource_name: "helper2".to_string(), source_file: "community:agents/helper.md".to_string(), version: Some("v2.0.0".to_string()) },
]
};
assert!(!detector.can_consolidate(&hard_redundancy));Sourcepub fn generate_redundancy_warning(&self, redundancies: &[Redundancy]) -> String
pub fn generate_redundancy_warning(&self, redundancies: &[Redundancy]) -> String
Generates a comprehensive warning message for detected redundancies.
This method creates a user-friendly warning message that explains detected redundancies and provides actionable suggestions for optimization. The message is designed to be informative rather than alarming, emphasizing that redundancy is not an error.
§Message Structure
The generated warning includes:
- Header: Clear indication this is a warning, not an error
- Redundancy List: Each detected redundancy with details
- General Guidance: Explanation of implications and options
- Specific Suggestions: Targeted advice based on detected patterns
§Message Tone
The warning message maintains a helpful, non-blocking tone:
- Emphasizes that installation will proceed normally
- Explains that redundancy may be intentional
- Provides optimization suggestions without mandating changes
- Uses clear, jargon-free language
§Color Coding
The message uses terminal colors for better readability:
- Yellow: Warning indicators and attention markers
- Blue: Informational notes and suggestions
- Default: Main content and resource names
§Parameters
redundancies: List of detected redundancy patterns
§Returns
- Non-empty: Formatted warning message if redundancies exist
- Empty string: If no redundancies provided
§Example Output
Warning: Redundant dependencies detected
⚠ Multiple versions of 'community:agents/helper.md' will be installed:
- 'app-helper' uses version v1.0.0
- 'tool-helper' uses version latest
Note: This is not an error, but you may want to consider:
• Using the same version for consistency
• These resources will be installed to different files
• Each will work independently
• Consider aligning versions for 'community:agents/helper.md' across all resourcesSourcepub const fn check_transitive_redundancies(&self) -> Vec<Redundancy>
pub const fn check_transitive_redundancies(&self) -> Vec<Redundancy>
Placeholder for future transitive redundancy detection.
This method is reserved for future implementation when AGPM supports dependencies-of-dependencies (transitive dependencies). Currently returns an empty vector as transitive analysis is not yet implemented.
§Planned Functionality
When implemented, this method will:
- Build Dependency Tree: Map entire transitive dependency graph
- Detect Deep Redundancy: Find redundant patterns across dependency levels
- Analyze Impact: Calculate storage and maintenance implications
- Suggest Optimizations: Recommend dependency tree restructuring
§Example Future Analysis
Direct: app-agent → community:agents/helper.md v1.0.0
Transitive: app-agent → tool-lib → community:agents/helper.md v2.0.0
Result: Transitive redundancy detected - app-agent indirectly depends
on two versions of the same resource.§Implementation Challenges
- Circular Dependencies: Detection and handling of cycles
- Version Compatibility: Analyzing semantic version compatibility
- Performance: Efficient analysis of large dependency trees
- Cache Management: Handling cached vs. fresh transitive data
§Returns
Currently returns an empty vector. Future implementation will return detected transitive redundancies.
Sourcepub fn suggest_consolidation(&self, redundancy: &Redundancy) -> Vec<String>
pub fn suggest_consolidation(&self, redundancy: &Redundancy) -> Vec<String>
Generates actionable consolidation strategies for a specific redundancy.
This method analyzes a detected redundancy pattern and provides specific, actionable suggestions for resolving or managing the redundancy. The suggestions are tailored to the specific pattern of version usage.
§Strategy Categories
§Version Alignment
For redundancies with multiple specific versions:
- Suggest adopting a single version across all resources
- Recommend the most recent or most commonly used version
§Constraint Standardization
For mixed latest/specific version patterns:
- Suggest using specific versions for reproducibility
- Explain benefits of version pinning
§Impact Assessment
For all redundancies:
- Clarify that resources will be installed independently
- Explain that each resource will function correctly
- List all affected resource names
§Suggestion Algorithm
- Analyze Version Pattern: Identify specific vs. latest usage
- Generate Alignment Suggestions: Recommend version standardization
- Provide Context: Explain implications and benefits
- List Affected Resources: Show impact scope
§Parameters
redundancy: The redundancy pattern to analyze
§Returns
A vector of suggestion strings, ordered by priority:
- Primary suggestions (version alignment)
- Best practice recommendations (reproducibility)
- Impact clarification (what will actually happen)
§Example Output
For a redundancy with mixed version constraints:
"Consider using version v2.0.0 for all resources using 'community:agents/helper.md'"
"Consider using specific versions for all resources for reproducibility"
"Note: Each resource (app-helper, tool-helper) will be installed independently"§Use Cases
- CLI Tools: Generate help text for redundancy warnings
- IDE Extensions: Provide quick-fix suggestions
- Automated Tools: Implement dependency optimization utilities
- Documentation: Generate project-specific optimization guides