Implement mt which <name> command to resolve and display the real path of binaries, following symlinks to show actual file locations. This complements mt cd and mt edit by providing path resolution without navigation or editing.
Reuse the search logic from _mt_cd but output the resolved path instead of changing directory. The command will:
- Search in the same order as
mt cd: modules → packages → functions → executables - Use
realpathto resolve all symlinks to the actual file location - Output only the path (script-friendly, no decoration)
- Return appropriate exit codes (0=success, 1=not found)
Why follow _mt_cd pattern?
- Consistency: Users expect similar search behavior across mt commands
- Completeness: Covers modules, packages, functions, and executables
- Proven: The pattern is well-tested and handles edge cases
Why use realpath?
- Resolves entire symlink chain (not just one level)
- Already a required dependency of metool
- Provides canonical absolute paths
Output format:
- Simple: Just the path, nothing else (enables piping and scripting)
- Example:
/Users/admin/Code/github.com/mbailey/metool-packages-dev/tmux/bin/tmux-dev-window
Location: worktree/lib/path.sh
Action: Add new function _mt_which()
Function signature:
_mt_which() {
if (($# != 1)); then
echo "Usage: mt which <module|package|function|executable>" >&2
return 1
fi
# ... implementation
}Logic flow:
1. Validate arguments (exactly 1 required)
2. Check for realpath command availability
3. Try module (check MT_MODULES_DIR)
→ If found: realpath the module directory, output, return 0
4. Try package (check MT_PACKAGES_DIR)
→ If found: realpath the package directory, output, return 0
5. Try function (use declare -F)
→ If found: realpath the source file, output, return 0
6. Try executable (use which)
→ If found: realpath the executable, output, return 0
7. Not found: error to stderr, return 1
Key differences from _mt_cd:
- Output path via
echoinstead ofcd - For modules/packages: output the directory path itself
- For functions: output the source file path
- For executables: output the resolved executable path
Error handling:
- Check realpath availability (same as
_mt_cd) - Single argument required
- Clear error message if target not found
- All errors to stderr, return code 1
Location: worktree/shell/mt
Line: After line 198 (after cd command, before git)
Code to add:
which)
shift
_mt_which "$@"
;;Lines: 158-189 (Core Commands section)
Add after cd command (line 159):
echo " which TARGET Show real path to module, package, function, or executable"Sort order: Keep alphabetical within sections where reasonable, but which naturally fits after cd and before other commands.
Location: worktree/shell/completions/mt.bash
Line: 54 Change:
local mt_commands="cd deps doctor edit git module package reload update"To:
local mt_commands="cd deps doctor edit git module package reload update which"Line: After line 70 (after edit completion rule)
Code to add:
elif [[ ${prev} == "which" ]]; then
_mt_complete_functions_and_executablesRationale: Use same completion as edit - both work with functions and executables.
| File | Lines | Action | Details |
|---|---|---|---|
lib/path.sh |
End of file (~217) | Add | New _mt_which() function (~50 lines) |
shell/mt |
~199 | Add | Command handler (4 lines) |
shell/mt |
~159 | Add | Help text (1 line) |
shell/completions/mt.bash |
~54 | Modify | Add 'which' to command list |
shell/completions/mt.bash |
~71 | Add | Completion rule (2 lines) |
Total changes: ~60 lines across 3 files
- Open
worktree/lib/path.sh - Add
_mt_which()function at end of file (after_mt_path_rm) - Follow the logic flow outlined above
- Match coding style: use
realpathchecks, error messages to stderr - Test the function in isolation if possible
- Open
worktree/shell/mt - Add
whichcase aftercdcase (around line 199) - Add help text entry (around line 159)
- Verify case statement syntax
- Open
worktree/shell/completions/mt.bash - Add 'which' to
mt_commandslist (line 54) - Add completion rule after
editrule (line 71) - Verify completion logic syntax
- Source the updated shell/mt:
source worktree/shell/mt - Test each search path:
- Module:
mt which <module-name> - Package:
mt which <package-name> - Function:
mt which <function-name> - Executable:
mt which <executable-name>
- Module:
- Test error cases:
- No arguments:
mt which - Too many arguments:
mt which foo bar - Non-existent target:
mt which nonexistent
- No arguments:
- Test shell completion:
mt which <TAB>
No formal unit test framework exists for metool bash functions. Testing will be manual.
| Test Case | Command | Expected Output | Notes |
|---|---|---|---|
| Module | mt which <module> |
Resolved module directory path | If module exists in working set |
| Package | mt which <package> |
Resolved package directory path | If package exists in working set |
| Function | mt which _mt_cd |
Path to lib/path.sh | Built-in function |
| Executable (symlink) | mt which tmux-dev-window |
Real path after resolving symlinks | Example from README |
| Executable (direct) | mt which bash |
Real path to bash | System executable |
| Not found | mt which nonexistent |
Error to stderr, exit 1 | |
| No args | mt which |
Usage message to stderr, exit 1 | |
| Multiple args | mt which foo bar |
Usage message to stderr, exit 1 | |
| Completion | mt which <TAB> |
Shows functions and executables | Tab completion works |
| Help | mt --help |
Includes 'which' in command list | Help text updated |
- Verify
mt whichworks from different shells (bash, zsh if applicable) - Verify output is clean (no extra formatting) for script use
- Test pipe usage:
cd $(dirname $(mt which tmux-dev-window)) - Compare with
mt cdbehavior for consistency
- Symlink chains: Multiple levels of symlinks (e.g., metool bin → packages → repo)
- Functions with spaces in path: Ensure path parsing handles spaces
- Case sensitivity: Function names are case-sensitive
- Ambiguity: If name matches both module and executable, module takes precedence (follows
mt cdorder)
Follow existing metool conventions:
- Use
[[ ]]for conditionals (not[ ]) - Use
$()for command substitution (not backticks) - Use
localfor function-scoped variables - Error messages to stderr:
echo "Error: ..." >&2 - Use
_mt_log DEBUGfor debug output (already available) - Return 1 on error, 0 on success
- Match indentation style (2 spaces)
- realpath: Already a required dependency (checked in shell/mt and _mt_cd)
- which: Standard command, assumed available
- declare: Bash built-in for function introspection
- compgen: Bash built-in for completion
No new dependencies required.
This is a new command, no backwards compatibility concerns.
- Minimal overhead: Function uses same search pattern as
mt cd - No recursion or loops over large datasets
realpathis fast (single syscall)- Completion uses existing helper functions
Expected performance: Instant (<50ms) for typical use cases.
- No user input is passed to eval or similar dangerous constructs
- Paths are properly quoted in all operations
- Uses standard commands (which, realpath, declare)
- No temporary files or network access
Already included in implementation (shell/mt modification).
Consider adding example to task README.md after implementation:
$ mt which tmux-dev-window
/Users/admin/Code/github.com/mbailey/metool-packages-dev/tmux/bin/tmux-dev-windowAfter merge, add entry:
### Added
- `mt which` command to resolve real paths of modules, packages, functions, and executables
If issues are discovered:
- Remove
whichcase from shell/mt - Remove
_mt_whichfunction from lib/path.sh - Remove completion rule from shell/completions/mt.bash
- Revert help text change
Clean rollback: The function is self-contained and doesn't modify existing code.
Implementation is complete when:
- ✅
mt which <executable>shows resolved real path - ✅ Works for modules, packages, functions, and executables
- ✅ Shell completion functions correctly
- ✅ Help text includes new command
- ✅ Error messages are clear and helpful
- ✅ All manual test cases pass
- ✅ Behavior is consistent with
mt cdsearch order
- Proceed to impl-001: Implement the design as specified
- Follow implementation steps 1-4 sequentially
- Test each component as it's implemented
- Commit changes with task ID reference
- Update harness tracking