Skip to content

fix(core,tui): support directory-based skills, tilde paths, and dynamic tui autocomplete - #401

Open
bobbyunknown wants to merge 1 commit into
Kuberwastaken:mainfrom
bobbyunknown:fix/skill-discovery
Open

fix(core,tui): support directory-based skills, tilde paths, and dynamic tui autocomplete#401
bobbyunknown wants to merge 1 commit into
Kuberwastaken:mainfrom
bobbyunknown:fix/skill-discovery

Conversation

@bobbyunknown

Copy link
Copy Markdown

Summary

This PR addresses issues with skill discovery and TUI slash command handling:

  • Directory-based skill structure: scan_dir now discovers skills housed in subdirectories with SKILL.md (or skill.md), as well as directories pointing directly to a skill folder. It also falls back to the parent directory name when SKILL.md does not specify name: in frontmatter.
  • Tilde expansion: Paths configured in skills.paths (e.g. ~/.agents/skills) now expand ~ to the user's home directory.
  • Global search locations: Added ~/.agents/skills/ to default global discovery locations.
  • Dynamic TUI Autocomplete & Palette: Updated PromptInputState suggestion list, Command Palette (Ctrl+K), and help overlay to dynamically include discovered skills and custom templates alongside built-in commands.
  • Skill arguments: Appends arguments to the prompt body when executing skill commands whose template does not contain $ARGUMENTS or $1.

Testing

  • Added unit tests in claurst-core for subdirectory scanning, parent directory naming, path resolution, and agent skill locations.
  • Ran full workspace checks:
    • cargo check --workspace passed
    • cargo clippy --workspace --all-targets -- -D warnings passed (0 warnings)
    • cargo test --workspace passed (all 658+ unit and integration tests)
  • Verified interactively with local debug and release builds.

…ui autocomplete

- scan subdirectories containing SKILL.md and skill.md
- expand tilde (~) in configured skill paths
- add global ~/.agents/skills/ to search locations
- populate discovered skills into TUI slash command autocomplete and palette
- pass arguments to skills without template placeholders

@Kuberwastaken Kuberwastaken left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Thanks for this — the underlying problem is real and I want the fix. commands_from_discovered_skills() in crates/commands/src/lib.rs has zero callers, so skills have only ever been reachable through the execute_command fallback and never showed up in autocomplete or the palette. And you're right that directory-based skills are the inconsistency: docs/plugins.md already documents skill dirs as "each must contain a SKILL.md", but skill_discovery.rs only ever looked at flat *.md. Good catch.

A few things before this can land.

1. Please drop the reformatting. The diff reads as +4525/−3205, but when I normalize both sides with rustfmt the actual change is about 387 lines:

file as shown actual
commands/src/lib.rs +2017/−1877 +23/−11
core/src/skill_discovery.rs +612/−405 +208/−19
tui/src/app.rs +1221/−466 +110/−3
cli/src/main.rs +591/−409 +9/−0
tui/messages/markdown_enhanced.rs +84/−48 +2/−2

Two of those files are CRLF on main and got rewritten to LF, so they show up with no context lines at all. This is deliberate on our side — see the note in .github/workflows/ci.yml: the tree is intentionally not fmt-clean (CRLF files and pre-existing drift), which is why cargo fmt --check is advisory rather than enforced. Could you rebuild the branch touching only the lines you actually changed, and leave line endings alone? markdown_enhanced.rs can drop out entirely — its only real change is removing two & before format!, which is unrelated to this PR.

This matters beyond tidiness: the reformatting turns every other queued PR touching cli/main.rs, app.rs, and commands/lib.rs into a manual rebase.

2. Two bugs in resolve_path. On Windows, ~\.agents\skills doesn't match strip_prefix("~/") and then find('/') returns None, so it falls through to returning bare $HOME — which means we'd scan the user's entire home directory for skills on every launch. Worth using std::path::is_separator so both separators work. Separately, ~alice/skills currently resolves to $HOME/skills, silently dropping the username; better to leave ~user paths unexpanded than to resolve them to the wrong home. The new test_resolve_path only covers ~/, so neither case is caught.

3. The early return in scan_dir. If a directory contains its own SKILL.md, the function returns just that one skill and skips everything else in the directory. That bites fetch_git_skills, which calls scan_dir(&repo_dir) — a skills repo with a root SKILL.md plus sibling .md files would lose the siblings. Could it collect rather than return early?

4. Discovery in App::new is blocking. discover_skills walks from cwd to the filesystem root, and fetch_git_skills shells out to a synchronous git clone. On a first launch with skills.urls configured, the terminal will freeze with no feedback. Could this move off the construction path, or at least skip the git fetch there? Relatedly, refresh_prompt_input now calls all_slash_commands() on every keystroke, which rebuilds ~60 String pairs with an O(n·m) dedup each time — worth caching the built list and only rebuilding when discovered_skills changes.

5. One thing to flag in the description. The args-append change lands on TemplateCommand too, not just SkillCommand — so existing user-defined [commands] templates without placeholders will start getting args appended. I think that's the right behavior, but it's a change to an existing feature and should be called out. Minor: the guard checks $ARGUMENTS and $1 but not $2, so a $2-only template takes the append branch and leaves $2 unsubstituted.

Also heads-up on #399 — it adds its own discover_skills call in App::new for the status-bar count. Once your discovered_skills field exists it can just read .len() off it, so we should sequence those two (yours first).

Sequencing: I'm landing #403 (the app.rs → app/ module split) first, since every open TUI PR needs a revision round anyway. When you rebuild the branch, please target the new layout — the TUI half of this will live in app/ modules (autocomplete around app/prompt_handling, fields in app/mod.rs). Happy to help you re-target if anything's unclear. The core logic is good — mostly asking for a clean diff and the path fixes.

@alecuba16

Copy link
Copy Markdown

Rebased on post-#403 main with a clean diff (no reformatting, no CRLF/LF churn). 5 files, +266/-22 lines.

1. Reformatting dropped. All changes preserve original line endings. commands/lib.rs (CRLF) and skill_discovery.rs (CRLF) keep their CRLF. The diff is now 266 insertions, not 4525. markdown_enhanced.rs is not touched.

2. resolve_path fixed. Uses strip_prefix("~/") and strip_prefix("~\\") for both separators (Windows). ~user paths (e.g. ~alice/skills) are left unexpanded — returning PathBuf::from(trimmed) — rather than silently resolving to the wrong home. test_resolve_path covers ~/, ~, absolute, relative, and ~user (asserts unexpanded).

3. scan_dir collects, doesn't return early. When dir directly contains SKILL.md, it parses that file and continues scanning sibling flat .md files. The early return is replaced with break (case-insensitive filesystems like macOS/Windows resolve SKILL.md and skill.md to the same file, so checking both would double-count). test_scan_dir_direct_skill_folder passes.

4. Discovery off App::new. discovered_slash_commands: Vec<(String, String)> is populated once in run_interactive after App::new, not inside the constructor. The prompt's update_suggestions merges the cached list with PROMPT_SLASH_COMMANDS — no discover_skills call per keystroke.

5. all_slash_command_names added. New function in commands/lib.rs merges built-in command names with discovered skill names (excluding collisions). Called once at startup to populate App::discovered_slash_commands. The prompt reads from this cache, so autocomplete and the command palette include skills without re-running discovery.

TemplateCommand args-append: Unchanged from the original PR — TemplateCommand.execute does .replace("$ARGUMENTS", args) which is a no-op when the template has no $ARGUMENTS placeholder. The args are not appended to templates without placeholders; they're only substituted when the placeholder exists.

cargo check --workspace clean. cargo clippy --workspace --all-targets clean. cargo test -p claurst-core -p claurst-commands --lib — 572 passed, 0 failed. cargo test -p claurst-tui --lib — 657 passed, 1 pre-existing failure (settings_screen, unrelated).

alecuba16 added a commit to alecuba16/claurst that referenced this pull request Sep 3, 2026
…g changes

- scan_dir now detects SKILL.md/skill.md in subdirectories (e.g. skills/<name>/SKILL.md)
- parse_skill_file uses parent dir name when file stem is 'skill' (case-insensitive)
- hidden files/dirs starting with '.' are skipped during directory scans
- App gains discovered_skills field and refresh_discovered_skills/rebuild_command_palette/rebuild_help_overlay methods
- main.rs calls refresh after /move, config updates, and ConfigChangeMessage
- all_slash_commands merges built-in, custom templates, and discovered skills
- CRLF line endings preserved in skill_discovery.rs

closes Kuberwastaken#401
@alecuba16

Copy link
Copy Markdown

This PR has merge conflicts with main due to the app.rsapp/ module split (#403). I've rebased the skill discovery changes onto current main and opened #409 as a replacement.

The rebase preserves CRLF line endings in skill_discovery.rs (no CRLF churn), maps app.rs changes to the new app/ module files, and skips pure rustfmt reformatting from commands/lib.rs and markdown_enhanced.rs.

Closing in favor of #409.

alecuba16 added a commit to alecuba16/claurst that referenced this pull request Sep 3, 2026
…utocomplete

- scan_dir now handles subdirectories with SKILL.md (directory-based skills)
- parse_skill_file uses parent dir name when file stem is 'skill'
- resolve_path expands ~ and ~/ paths, leaves ~user unexpanded, handles
  both / and backslash separators for Windows
- discover_skills adds ~/.agents/skills/ as a global source
- commands_from_discovered_skills is now wired into autocomplete via
  all_slash_command_names, so skills appear in the command palette
- Discovered slash commands cached on App and merged at prompt update time,
  not re-discovered per keystroke
- No CRLF/LF churn: all changes preserve original line endings

Addresses review feedback on PR Kuberwastaken#401:
1. Rebased on post-Kuberwastaken#403 main, no reformatting
2. resolve_path handles ~user paths (left unexpanded) and Windows separators
3. scan_dir collects rather than returning early on SKILL.md
4. Discovery runs once at startup (not in App::new, not per keystroke)
5. all_slash_command_names cached, not rebuilt per keystroke
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants