| name | repo-scan |
| description | Discover git repos and manage repo notes in the vault. Scans ~/src and ~/dev for git repos, runs an intake interview for new repos, and refreshes metadata for existing ones. Use when the user says "repo scan", "repo intake", "resume repo intake", "scan my repos", "update repo notes", "what repos don't have notes", or mentions managing 3r/repos/ notes.
|
Repo Scanner
You manage the 3r/repos/ resource notes — the vault's canonical records for git repositories. Each repo gets one note, linked into the knowledge graph via wikilinks.
Model routing
The intake interview stays in the main session on Opus — it's conversational and the note is written from the user's blurb. The refresh phase (metadata updates, status/language/last-commit for existing notes) can be delegated to the vault-operator agent (Sonnet). Use the Agent tool with subagent_type="vault-operator" for refresh-only runs.
Infrastructure
- Scan script:
.claude/skills/repo-scan/scan-repos.sh — discovers repos, outputs JSON
- Detail script:
.claude/skills/repo-scan/repo-detail.sh <repo-path> — comprehensive single-repo briefing (commits, activity, branches, README)
- Diff script:
.claude/skills/repo-scan/repo-diff.sh [repos.json] — compares scan against vault notes, shows unprocessed and stale repos
- Cached scan:
.claude/skills/repo-scan/repos.json — latest scan results
- FileClass:
zz_config/fileClass/repo.md — fields: repo_path, repo_url, repo_status, repo_languages, repo_last_commit, repo_journal
- MOC:
3r/repos/repos.md — Dataview tables grouped by status
Script Invocation
Always invoke scripts exactly as shown below — relative paths prefixed with bash. Do not resolve to absolute paths. Never run ad-hoc git, ls, or cd commands against repo directories — always use repo-detail.sh instead, which has all the git/ls logic built in and avoids bare-repo-attack permission blocks.
Smart Default Behavior
On invocation, determine what needs doing:
- Diff scan against vault — run
repo-diff.sh (it reports cache freshness automatically):
bash .claude/skills/repo-scan/repo-diff.sh .claude/skills/repo-scan/repos.json
If the SCAN FRESHNESS section warns the scan is stale, re-run the scan first:
bash .claude/skills/repo-scan/scan-repos.sh .claude/skills/repo-scan/repos.json
- Report both buckets:
- New repos (no vault note) → intake interview
- Existing repos (vault note exists) → check for stale metadata (last_commit drift, status drift)
- Let the user choose what to process. Don't force a mode.
Intake Interview
This is a conversational process, not a form. One repo at a time.
Step 1: Present the summary
Gather the full briefing with repo-detail.sh:
bash .claude/skills/repo-scan/repo-detail.sh <repo-path>
Then format the key fields as a compact briefing for the user:
**{name}** | {status suggestion} | {yours/fork of upstream} | last commit {date}
`{path}` | branch: {branch} | worktrees: {list if any, omit .journal}
Remote: {remote} | Languages: {detected}
> {README excerpt — first meaningful paragraph from default branch}
The script also provides recent commits, activity counts, directory listing, and branches — use these to add context if the user asks "what is this?" or seems unfamiliar with the repo.
Step 2: Ask for status
The only structured input. Use AskUserQuestion with three options:
- Active — getting attention right now
- Dormant — paused, may come back
- Archived — done or abandoned
Step 3: Listen
Ask: "What's the story?" (or similar open prompt). Shut up and let the user talk. They'll tell you:
- What the repo is and why it exists (→ Intent section)
- What it connects to in their world (→ Links frontmatter, wikilinks in body)
- Context the README doesn't capture (→ Notes section)
Do NOT use multiple-choice for this. No cluster dropdowns, no category pickers. The freeform blurb is the whole point — it captures nuance, motivation, and connections that no dropdown can.
Step 4: Draft the note
Generate the vault note from metadata + the user's words:
Frontmatter:
---
Created: {ISO timestamp}
fileClass: repo
tags:
- type/repo
Area:
Links:
repo_path: {path}
repo_url: {https URL}
repo_status: {Active/Dormant/Archived}
repo_languages:
- {lang}
repo_last_commit: {YYYY-MM-DD}
repo_journal: "[[journal-{repo-name}]]"
---
Journal link: Always include repo_journal: [[journal-{repo-name}]] — even if the journal note doesn't exist yet. The ghost link becomes real when the first journal entry is created. This is standard Obsidian practice (unresolved links as breadcrumbs for future connections).
Verify after writing: After creating the repo note, confirm repo_journal was set correctly with obsidian property:read name="repo_journal" path="3r/repos/{repo-name}.md". Report the result when presenting the draft to the user.
For forks, add:
is_fork: true
upstream: {org/repo}
Body:
# {Name}
## Intent
{Distilled from the user's blurb. Their words, tightened. Capture the why,
the motivation, the context. Not a README summary — a human perspective.}
## Roadmap
{If mentioned. Otherwise omit the section entirely.}
## Notes
{Technical details, related repos, build commands, anything useful.
Link to related repo notes with wikilinks.
Omit .journal from worktree mentions — it's infrastructure for the /journal skill, not a project branch.}
Inline wikilinks: On first mention of a major entity in the body text, link it as a [[wikilink]] — even if the target note doesn't exist yet. Unresolved links are breadcrumbs for future connections (Kepano's "emergent structure" approach). Focus on things that are or should become vault nodes: tools, projects, organizations, named concepts. Skip generic terms like "JavaScript" or "browser."
Frontmatter Links vs. inline: The Links: frontmatter field is for explicit structural relationships (related areas, key people) — not a mirror of every entity mentioned in the body. Most repo notes won't need it; inline wikilinks carry the entity graph.
Step 5: Confirm and continue
Show the drafted note. Ask if it looks good. Fix anything, then write the file and move to the next repo.
Filename: use the repo's directory name (e.g., goneelsewhere.md). No prefix — the 3r/repos/ path and fileClass handle identification.
Update Process (for existing notes)
For repos that already have vault notes, use the STALE REPOS output from repo-diff.sh to identify drift:
- Compare
repo_last_commit in the note vs. the scan JSON (the diff script does this automatically).
- Flag drift:
- New commits on a repo marked Dormant or Archived → ask if status should change
- No commits in 6+ months on an Active repo → suggest status change
- Refresh
repo_last_commit date if changed.
- Surface any new worktrees or branch changes.
- Don't touch the Intent or Notes sections — those are the user's words.
Things to Watch For
- README from default branch — the scan script reads from
refs/remotes/origin/HEAD, not the current checkout. Worktree checkouts may not have the README.
- Duplicate names — the scan deduplicates by remote URL, but two genuinely different repos can share a name. Use the full path to disambiguate during interview.
- Fork vs. clone —
is_fork from GitHub API. Repos cloned from others but not GitHub-forked show as is_fork: false. Listen for the user saying "this is someone else's project" and note it.
- Bitbucket repos — fork detection doesn't work (gh CLI only). Classify from user context.
- No remote / no commits — some repos are local-only experiments. Still worth a note if the user wants one.
- Don't invent clusters — there is no repo_cluster field. Grouping emerges from wikilinks and the knowledge graph. Never suggest adding one.