The discovery engine auto-detects all commands from the `commands/` directory, parses their metadata, and caches results for performance.
Scans commands directory and caches metadata for fast command discovery.
/plugin marketplace add Data-Wise/craft/plugin install data-wise-craft@Data-Wise/craftThe discovery engine auto-detects all commands from the commands/ directory, parses their metadata, and caches results for performance.
from commands._discovery import load_cached_commands, get_command_stats
# Load all commands (uses cache if fresh)
commands = load_cached_commands()
# Get statistics
stats = get_command_stats()
print(f"Total commands: {stats['total']}")
print(f"Categories: {stats['categories']}")
load_cached_commands() -> list[dict]Load commands from cache if fresh, else regenerate.
Returns: List of command metadata dictionaries
Performance:
Example:
commands = load_cached_commands()
for cmd in commands:
print(f"{cmd['name']}: {cmd['description']}")
discover_commands() -> list[dict]Force regeneration of command metadata (ignores cache).
Returns: List of command metadata dictionaries
Example:
from commands._discovery import discover_commands
# Force fresh scan
commands = discover_commands()
get_command_stats() -> dictGet summary statistics about commands.
Returns: Dictionary with:
total: Total command countcategories: Dict of category → countwith_modes: Count of commands with mode supportwith_dry_run: Count of commands with dry-run argumentgenerated: ISO timestamp of cache generationExample:
stats = get_command_stats()
print(f"Found {stats['total']} commands across {len(stats['categories'])} categories")
cache_commands(commands: list[dict]) -> NoneSave commands to cache file.
Args:
commands: List of command metadataSide effects: Writes commands/_cache.json
Each command has the following fields:
name (str): Command identifier (e.g., "code:lint", "hub")category (str): Primary category (e.g., "code", "test", "docs")description (str): One-line summaryfile (str): Relative path from commands/ directorysubcategory (str): Subcategory for groupingmodes (list[str]): Supported execution modes (auto-detected from arguments)arguments (list[dict]): Command-line arguments from frontmattertutorial (bool): Whether tutorial is availabletutorial_level (str): Difficulty level ("beginner", "intermediate", "advanced")tutorial_file (str): Path to tutorial markdownrelated_commands (list[str]): Related command namestags (list[str]): Searchable tagsproject_types (list[str]): Applicable project typescommands = load_cached_commands()
# Get all commands in a category
code_commands = [c for c in commands if c['category'] == 'code']
# Get commands with mode support
mode_commands = [c for c in commands if 'modes' in c]
# Get commands with dry-run
dry_run_commands = [
c for c in commands
if 'arguments' in c
and any(
isinstance(arg, dict) and 'dry-run' in arg.get('name', '').lower()
for arg in c['arguments']
)
]
# Search by name
lint_cmd = next((c for c in commands if c['name'] == 'code:lint'), None)
The cache auto-invalidates when:
_cache.json doesn't exist.md file in commands/ is newer than cache fileManual regeneration:
from commands._discovery import discover_commands, cache_commands
# Force regenerate
commands = discover_commands()
cache_commands(commands)
# Run discovery and print statistics
python3 commands/_discovery.py
Output:
Discovering commands...
Found 97 commands
Categories:
arch: 4
check: 1
ci: 3
code: 12
...
Statistics:
Total: 97
With modes: 11
With dry-run: 29
Generated: 2026-01-17T11:24:00.383464
✓ Done!
The discovery engine handles errors gracefully:
try:
commands = load_cached_commands()
except Exception as e:
print(f"Error loading commands: {e}")
load_cached_commands() instead of discover_commands()| Operation | Time |
|---|---|
| Load from cache | ~2ms |
| First discovery | ~12ms |
| Parse single file | ~0.1ms |
from commands._discovery import load_cached_commands
def show_hub():
"""Display command hub."""
commands = load_cached_commands()
# Group by category
categories = {}
for cmd in commands:
cat = cmd['category']
if cat not in categories:
categories[cat] = []
categories[cat].append(cmd)
# Display
for cat, cmds in sorted(categories.items()):
print(f"\n{cat.upper()} ({len(cmds)} commands)")
for cmd in cmds:
print(f" {cmd['name']:30s} {cmd['description']}")
# Delete cache manually
rm commands/_cache.json
# Regenerate
python3 commands/_discovery.py
Check that files:
commands/ directory.md extension_ (reserved for internal files)Categories are inferred from file paths:
commands/code/lint.md → category: "code"commands/hub.md → category: "hub"commands/git/docs/refcard.md → category: "git"If frontmatter is missing:
name: Auto-generated from filepathcategory: Auto-inferred from directorydescription: Extracted from heading or first paragraph| File | Purpose |
|---|---|
commands/_discovery.py | Discovery engine implementation |
commands/_schema.json | Metadata schema documentation |
commands/_cache.json | Generated cache (gitignored) |
commands/_discovery_usage.md | This usage guide |