use std::fs;
use std::io::{self, Write};
use std::path::PathBuf;
const SKILL_CONTENT: &str = r#"---
name: km-analyze
description: Analyze a code repository using km (code metrics) tool
---
# km — Code Metrics Analysis Skill
You have access to the `km` CLI tool for comprehensive code analysis. Use it to analyze repositories across multiple dimensions.
## Available Commands
Run these via the Bash tool. Always use `--json` for machine-readable output.
### Lines of Code
```bash
km loc [PATH] --json
```
Language breakdown: files, blank lines, comment lines, code lines.
### Code Health Score
```bash
km score [PATH] --json
km score [PATH] --json --model legacy
```
Overall grade (A++ to F--). Default model (cogcom): 5 dimensions — cognitive complexity, duplication, indentation, Halstead effort, file size. Legacy model (--model legacy): 6 dimensions — MI, cyclomatic complexity, duplication, indentation, Halstead effort, file size.
### Score Diff (requires git)
```bash
km score diff [PATH] --json --git-ref HEAD~1
```
Compare current code health score against a git ref. Shows per-dimension deltas.
### Cognitive Complexity
```bash
km cogcom [PATH] --json --top 20
```
SonarSource method (2017). Measures how difficult code is to understand, penalizing deep nesting.
### Cyclomatic Complexity
```bash
km cycom [PATH] --json --top 20
```
Per-file and per-function complexity. High values indicate hard-to-test code.
### Maintainability Index
```bash
km miv [PATH] --json --top 20
```
Verifysoft variant (with comment weight). Values below 65 are hard to maintain.
### Halstead Complexity
```bash
km hal [PATH] --json --top 20 --sort-by effort
```
Effort, volume, and estimated bugs per file.
### Indentation Complexity
```bash
km indent [PATH] --json
```
Indentation depth stddev — high values suggest deeply nested code.
### Duplicate Code
```bash
km dups [PATH] --json --report
```
Duplicate blocks across the project.
### Hotspots (requires git)
```bash
km hotspots [PATH] --json --top 20
```
Files that change frequently AND have high complexity — top refactoring targets.
### Code Ownership (requires git)
```bash
km knowledge [PATH] --json --top 20
```
Bus factor risk per file via git blame analysis.
### Temporal Coupling (requires git)
```bash
km tc [PATH] --json --top 20
```
Files that change together in commits — hidden dependencies.
## Analysis Workflow
1. Start with `km score` for the overall health grade
2. Run `km loc` for project size and language breakdown
3. Use `km cogcom`, `km cycom` and `km miv` to find the most complex/unmaintainable files
4. Run `km hotspots` to find high-risk change-prone files
5. Check `km dups` for code duplication opportunities
6. Optionally run `km knowledge` and `km tc` for team/architecture insights
7. Use `km score diff --git-ref HEAD~N` to track score changes over time
## Output Format
Produce a structured report with:
- **Overview**: Project size, languages, overall grade
- **Code Health**: Score breakdown by dimension
- **Complexity Hotspots**: Worst files by complexity
- **Maintainability**: Files hardest to maintain
- **Key Findings**: Notable patterns and risks
- **Recommendations**: Prioritized, actionable suggestions
Reference specific file names and metric values. Be concise but thorough.
"#;
pub fn install(provider: &str, with_permissions: bool) -> Result<(), Box<dyn std::error::Error>> {
if provider != "claude" {
return Err(format!("Unsupported provider: {provider}. Supported: claude").into());
}
println!("Where do you want to install the km skill?");
println!(" 1) Project-level (.claude/skills/km-analyze/)");
println!(" 2) User-level (~/.claude/skills/km-analyze/)");
print!("Choose [1/2]: ");
io::stdout().flush()?;
let mut choice = String::new();
io::stdin().read_line(&mut choice)?;
let choice = choice.trim();
let (skill_dir, project_root) = match choice {
"1" => {
let repo = git2::Repository::discover(".")
.map_err(|_| "Could not find a git repository. Run from a project directory or use option 2 (user-level).")?;
let workdir = repo
.workdir()
.ok_or("Could not determine repository working directory")?;
(
workdir.join(".claude/skills/km-analyze"),
Some(workdir.to_path_buf()),
)
}
"2" => {
let home = std::env::var("HOME").map_err(|_| "Could not determine home directory")?;
(PathBuf::from(home).join(".claude/skills/km-analyze"), None)
}
_ => return Err("Invalid choice. Please enter 1 or 2.".into()),
};
fs::create_dir_all(&skill_dir)?;
let skill_path = skill_dir.join("SKILL.md");
fs::write(&skill_path, SKILL_CONTENT)?;
println!("Skill installed at: {}", skill_path.display());
println!("Claude Code will now be able to use km for code analysis.");
if with_permissions {
if let Some(root) = project_root {
super::permissions::install(&root)?;
} else {
eprintln!(
"warning: --with-permissions only applies to project-level installs, skipping"
);
}
}
Ok(())
}