Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 28 additions & 1 deletion .claude/skills/codemap/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
---
name: codemap
description: Analyze codebase structure, dependencies, changes, cross-agent handoffs, and get code-aware intelligence. Use when user asks about project structure, where code is located, how files connect, what changed, how to resume work, before starting any coding task, or when you need risk analysis and skill guidance.
description: Analyze codebase structure, dependencies, changes, cross-agent handoffs, and get code-aware intelligence. Use when user asks about project structure, where code is located, how files connect, what changed, how to resume work, before starting any coding task, when you need risk analysis and skill guidance, or when Codemap should tune project config before analysis.
---

# Codemap

Codemap gives you instant architectural context about any codebase. It classifies your intent, detects risk, matches relevant skills, and tracks your working set — all automatically via hooks.

Codemap should also keep its own per-project config healthy. On first use in a repo, or when output is obviously noisy, tune `.codemap/config.json` before doing deeper analysis so future calls stay code-first instead of asset-first.

## Commands

```bash
Expand All @@ -21,12 +23,30 @@ codemap handoff --json . # Machine-readable handoff payload
codemap skill list # Show available skills with descriptions
codemap skill show <name> # Get full skill instructions
codemap skill init # Create custom skill template
codemap config show # Show current project config
codemap context # Universal JSON context envelope
codemap context --for "prompt" # With pre-classified intent + matched skills
codemap context --compact # Minimal for token-constrained agents
codemap serve --port 9471 # HTTP API for non-MCP integrations
```

## First-Use Setup

Before deeper Codemap analysis in a repo:

1. Check `.codemap/config.json`.
2. If it is missing, clearly boilerplate, or obviously too noisy for the stack, run `codemap skill show config-setup` and follow it.
3. After writing or improving config, rerun `codemap .` and `codemap --deps`.

Treat config as repo memory. Once tuned, future Codemap calls should benefit automatically.

Signals that config needs setup or tuning:
- `.codemap/config.json` is missing
- config only contains generic auto-detected `only` values with no real project shaping
- large non-code directories dominate the tree output
- stack-specific noise is overwhelming source structure (`.xcassets`, screenshots, PDFs, training-data, fixtures, generated files, models, vendor directories)
- the repo stack is obvious, but the config does not reflect it

## When to Use

### ALWAYS run `codemap .` when:
Expand Down Expand Up @@ -56,6 +76,12 @@ codemap serve --port 9471 # HTTP API for non-MCP integrations
- You need guidance for a specific task (hub editing, refactoring, testing)
- Risk level is medium or high

### Run `codemap skill show config-setup` when:
- The repo has no `.codemap/config.json`
- The config looks like a bare bootstrap and not a real project policy
- Codemap output is cluttered by large non-code directories
- You want Codemap to make better future decisions for this specific repo

### Run `codemap context` when:
- Piping codemap intelligence to another tool
- Need a structured JSON summary of the project state
Expand Down Expand Up @@ -84,6 +110,7 @@ Skills matched: hub-safety, refactor — run `codemap skill show <name>` for gui

| Skill | When to Pull |
|-------|-------------|
| `config-setup` | Missing, boilerplate, or noisy `.codemap/config.json` |
| `hub-safety` | Editing files imported by 3+ others |
| `refactor` | Restructuring, renaming, moving code |
| `test-first` | Writing tests, TDD workflows |
Expand Down
52 changes: 52 additions & 0 deletions .claude/skills/config-setup/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
---
name: config-setup
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This skill lives under .claude/skills/codemap-setup/ but its frontmatter name is config-setup. If the Claude skill loader keys off the directory name (as it does for the existing codemap skill), this mismatch can make the skill undiscoverable or confusing to reference. Consider renaming the directory to .claude/skills/config-setup/ (and keeping name: config-setup) so the path and skill name align.

Suggested change
name: config-setup
name: codemap-setup

Copilot uses AI. Check for mistakes.
description: Set up or tune .codemap/config.json so Codemap focuses on code-relevant parts of the repo. Use when config is missing, boilerplate, noisy, or mismatched to the stack.
---

# Codemap Config Setup

## Goal

Write or improve `.codemap/config.json` so future Codemap calls stay focused on the code that matters for this repo.

## Use this when

1. `.codemap/config.json` is missing
2. The existing config looks like a bare bootstrap instead of a real project policy
3. Codemap output is dominated by assets, fixtures, generated files, vendor trees, PDFs, screenshots, models, or training data
4. The project stack is obvious, but Codemap is not prioritizing the right parts of the repo

## Workflow

1. Inspect the repo quickly before writing config
- Run `codemap .`
- If needed, run `codemap --deps .`
- Note the stack markers (`Cargo.toml`, `Package.swift`, `*.xcodeproj`, `go.mod`, `package.json`, `pyproject.toml`, etc.)
- Identify large non-code directories and noisy extensions

2. Decide whether config is missing, boilerplate, or tuned
- Missing: no `.codemap/config.json`
- Boilerplate: only generic `only` values, no real shaping, no excludes despite obvious noise
- Tuned: contains intentional project-specific includes/excludes, depth, or routing hints

3. Write a conservative code-first config
- Keep primary source-language `only` values when they help
- Add `exclude` entries for obvious non-code noise
- Set a moderate `depth` when the repo is broad
- Avoid overfitting or excluding real source directories

4. Prefer stack-aware defaults
- Rust: focus `src`, `tests`, `benches`, `examples`; de-prioritize corpora, sample PDFs, training data, large generated artifacts
- iOS/Swift: focus app/framework source, tests, package/project manifests; de-prioritize `.xcassets`, screenshots, snapshots, vendor/build outputs
- TS/JS: focus `src`, `apps`, `packages`, `tests`; de-prioritize `dist`, `coverage`, Storybook assets, large fixture payloads
- Python: focus package roots, tests, tool config; de-prioritize notebooks, data dumps, models, fixtures when they overwhelm code
- Go: focus packages, cmd, internal, tests; de-prioritize generated assets, sample data, vendor-like noise

5. Preserve user intent
- If config already looks curated, do not replace it wholesale
- Make minimal edits and explain why

6. Verify immediately
- Rerun `codemap .`
- If the repo still looks noisy, refine `exclude` and possibly `depth`
- Only rerun `codemap --deps .` after tree output looks reasonable
52 changes: 27 additions & 25 deletions cmd/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,19 +160,30 @@ func initProjectConfig(root string) (configInitResult, error) {
}

func configShow(root string) {
cfg := config.Load(root)
if isConfigEmpty(cfg) {
cfgPath := config.ConfigPath(root)
if _, err := os.Stat(cfgPath); os.IsNotExist(err) {
fmt.Println("No config file found.")
fmt.Printf("Run 'codemap config init' to create %s\n", cfgPath)
} else {
fmt.Println("Config is empty (no filters active).")
assessment := config.AssessSetup(root)
cfgPath := config.ConfigPath(root)
switch assessment.State {
case config.SetupStateMissing:
fmt.Println("No config file found.")
fmt.Printf("Run 'codemap config init' to create %s\n", cfgPath)
return
case config.SetupStateEmpty:
fmt.Println("Config is empty (no filters active).")
if len(assessment.Reasons) > 0 {
fmt.Println(assessment.Reasons[0])
}
return
case config.SetupStateMalformed:
fmt.Println("Config is malformed or unreadable.")
if len(assessment.Reasons) > 0 {
fmt.Println(assessment.Reasons[0])
}
fmt.Printf("Fix %s or rerun 'codemap config init' to recreate it.\n", cfgPath)
return
}

fmt.Printf("Config: %s\n", config.ConfigPath(root))
cfg := config.Load(root)
fmt.Printf("Config: %s\n", cfgPath)
fmt.Println()
if len(cfg.Only) > 0 {
fmt.Printf(" only: %s\n", strings.Join(cfg.Only, ", "))
Expand Down Expand Up @@ -229,23 +240,14 @@ func configShow(root string) {
fmt.Printf(" require_docs_for: %s\n", strings.Join(cfg.Drift.RequireDocsFor, ", "))
}
}

if assessment.State == config.SetupStateBoilerplate {
fmt.Println()
fmt.Println("Note: this config still looks like a bootstrap.")
fmt.Println("Run `codemap skill show config-setup` to tune it for this repo.")
}
}

func isConfigEmpty(cfg config.ProjectConfig) bool {
if len(cfg.Only) > 0 || len(cfg.Exclude) > 0 || cfg.Depth > 0 {
return false
}
if strings.TrimSpace(cfg.Mode) != "" {
return false
}
if cfg.Budgets.SessionStartBytes > 0 || cfg.Budgets.DiffBytes > 0 || cfg.Budgets.MaxHubs > 0 {
return false
}
if strings.TrimSpace(cfg.Routing.Retrieval.Strategy) != "" || cfg.Routing.Retrieval.TopK > 0 || len(cfg.Routing.Subsystems) > 0 {
return false
}
if cfg.Drift.Enabled || cfg.Drift.RecentCommits > 0 || len(cfg.Drift.RequireDocsFor) > 0 {
return false
}
return true
return cfg.IsZero()
}
38 changes: 38 additions & 0 deletions cmd/config_more_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,44 @@ func TestConfigShowNoConfigFile(t *testing.T) {
}
}

func TestConfigShowMalformedConfig(t *testing.T) {
root := t.TempDir()
cfgPath := config.ConfigPath(root)
if err := os.MkdirAll(filepath.Dir(cfgPath), 0o755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(cfgPath, []byte("{broken"), 0o644); err != nil {
t.Fatal(err)
}

out := captureOutput(func() { configShow(root) })
if !strings.Contains(out, "malformed or unreadable") {
t.Fatalf("expected malformed guidance, got:\n%s", out)
}
if !strings.Contains(out, "rerun 'codemap config init'") {
t.Fatalf("expected recreate guidance, got:\n%s", out)
}
}

func TestConfigShowBoilerplateConfigSuggestsTuning(t *testing.T) {
root := t.TempDir()
cfgPath := config.ConfigPath(root)
if err := os.MkdirAll(filepath.Dir(cfgPath), 0o755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(cfgPath, []byte(`{"only":["rs","toml"],"depth":4}`+"\n"), 0o644); err != nil {
t.Fatal(err)
}

out := captureOutput(func() { configShow(root) })
if !strings.Contains(out, "looks like a bootstrap") {
t.Fatalf("expected bootstrap note, got:\n%s", out)
}
if !strings.Contains(out, "config-setup") {
t.Fatalf("expected config-setup guidance, got:\n%s", out)
}
}

func mustWriteConfigFixture(t *testing.T, path string, content string) {
t.Helper()
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
Expand Down
62 changes: 62 additions & 0 deletions cmd/hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,8 @@ func hookSessionStart(root string) error {
structureBudget := projCfg.SessionStartOutputBytes()
maxHubs := projCfg.HubDisplayLimit()

showConfigSetupHint(root)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Emit config-setup hint on multi-repo session starts

The new setup hint is only called at this point in hookSessionStart, but the function already returns early for meta-repos (len(childRepos) > 1) via hookSessionStartMultiRepo. In that common monorepo path, users with missing/boilerplate config never get the new codemap:config marker or human guidance, so the feature added in this commit is effectively skipped for multi-repo sessions.

Useful? React with 👍 / 👎.


exe, err := os.Executable()
if err == nil {
depth := limits.AdaptiveDepth(fileCount)
Expand Down Expand Up @@ -782,6 +784,7 @@ func showMatchedSkills(root string, intent TaskIntent) {
}

matches := idx.MatchSkills(intent.Category, intent.Files, langs, 3)
matches = injectConfigSetupSkill(root, idx, matches)
if len(matches) == 0 {
return
}
Expand All @@ -808,6 +811,64 @@ func showMatchedSkills(root string, intent TaskIntent) {
fmt.Printf("Skills matched: %s — run `codemap skill show <name>` for guidance\n", strings.Join(names, ", "))
}

func injectConfigSetupSkill(root string, idx *skills.SkillIndex, matches []skills.MatchResult) []skills.MatchResult {
assessment := config.AssessSetup(root)
if !assessment.NeedsAttention() {
return matches
}

skill, ok := idx.ByName["config-setup"]
if !ok || skill == nil {
return matches
}
for _, match := range matches {
if match.Skill != nil && match.Skill.Meta.Name == "config-setup" {
return matches
}
}

reason := "config:" + string(assessment.State)
if len(assessment.Reasons) > 0 {
reason = reason + " " + assessment.Reasons[0]
}

injected := skills.MatchResult{
Skill: skill,
Score: 100,
Reason: reason,
}
matches = append([]skills.MatchResult{injected}, matches...)
if len(matches) > 3 {
matches = matches[:3]
}
return matches
}

func showConfigSetupHint(root string) {
assessment := config.AssessSetup(root)
if !assessment.NeedsAttention() {
return
}

type configHint struct {
State string `json:"state"`
Reasons []string `json:"reasons,omitempty"`
}
if data, err := json.Marshal(configHint{
State: string(assessment.State),
Reasons: assessment.Reasons,
}); err == nil {
fmt.Printf("<!-- codemap:config %s -->\n", string(data))
}

fmt.Println("⚙️ Codemap config setup recommended:")
for _, reason := range assessment.Reasons {
fmt.Printf(" • %s\n", reason)
}
fmt.Println(" • Run `codemap skill show config-setup` and tune `.codemap/config.json` before deeper analysis.")
fmt.Println()
}

// showDriftWarnings checks and displays documentation drift warnings.
func showDriftWarnings(root string, cfg config.DriftConfig, routing config.RoutingConfig) {
warnings := CheckDrift(root, cfg, routing)
Expand Down Expand Up @@ -1441,6 +1502,7 @@ func hookSessionStartMultiRepo(root string, childRepos []string) error {

repoPath := filepath.Join(root, repo)
projCfg := config.Load(repoPath)
showConfigSetupHint(repoPath)

depth := 2
if projCfg.Depth > 0 {
Expand Down
Loading
Loading