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
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,20 @@ struct validate my-config.yaml
struct mcp --server
```

### Shell Completion

Enable tab completion for struct commands and options:

```sh
# Print exact setup commands for your shell (auto-detects if omitted)
struct completion install

# Or specify explicitly
struct completion install zsh
struct completion install bash
struct completion install fish
```

### 🤖 MCP Integration Quick Start

Struct supports MCP (Model Context Protocol) for seamless AI tool integration:
Expand Down
15 changes: 14 additions & 1 deletion docs/cli-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ The `struct` CLI allows you to generate project structures from YAML configurati
**Basic Usage:**

```sh
struct {info,validate,generate,list,generate-schema} ...
struct {info,validate,generate,list,generate-schema,mcp,completion} ...
```

## Global Options
Expand Down Expand Up @@ -107,6 +107,19 @@ struct generate-schema [-h] [-l LOG] [-c CONFIG_FILE] [-i LOG_FILE] [-s STRUCTUR
- `-s STRUCTURES_PATH, --structures-path STRUCTURES_PATH`: Path to structure definitions.
- `-o OUTPUT, --output OUTPUT`: Output file path for the schema (default: stdout).

### `completion`

Manage shell completions for struct.

Usage:

```sh
struct completion install [bash|zsh|fish]
```

- If no shell is provided, the command attempts to auto-detect your current shell and prints the exact commands to enable argcomplete-based completion for struct.
- This does not modify your shell configuration; it only prints the commands you can copy-paste.

## Examples

### Basic Structure Generation
Expand Down
14 changes: 14 additions & 0 deletions docs/completion.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,20 @@ STRUCT provides intelligent auto-completion for commands, options, and **structu

## Quick Setup

The easiest way is to ask struct to print the exact commands for your shell:

```sh
# Auto-detect current shell and print install steps
struct completion install

# Or specify explicitly
struct completion install zsh
struct completion install bash
struct completion install fish
```

You can still follow the manual steps below if you prefer.

For most users, this simple setup will enable full completion:

```sh
Expand Down
60 changes: 60 additions & 0 deletions struct_module/commands/completion.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
from struct_module.commands import Command
import os

SUPPORTED_SHELLS = ["bash", "zsh", "fish"]

class CompletionCommand(Command):
def __init__(self, parser):
super().__init__(parser)
parser.description = "Manage CLI shell completions for struct (argcomplete)"
sub = parser.add_subparsers(dest="action")

install = sub.add_parser("install", help="Print the commands to enable completion for your shell")
install.add_argument("shell", nargs="?", choices=SUPPORTED_SHELLS, help="Shell type (auto-detected if omitted)")
install.set_defaults(func=self._install)

def _detect_shell(self):
shell = os.environ.get("SHELL", "")
if shell:
basename = os.path.basename(shell)
if basename in SUPPORTED_SHELLS:
return basename
# Fallback to zsh if running zsh, else bash
if os.environ.get("ZSH_NAME") or os.environ.get("ZDOTDIR"):
return "zsh"
return "bash"

def _install(self, args):
shell = args.shell or self._detect_shell()
print(f"Detected shell: {shell}")

if shell == "bash":
print("\n# One-time dependency (if not installed):")
print("python -m pip install argcomplete")
print("\n# Enable completion for 'struct' in bash (append to ~/.bashrc):")
print('echo "eval \"$(register-python-argcomplete struct)\"" >> ~/.bashrc')
print("\n# Apply now:")
print("source ~/.bashrc")

elif shell == "zsh":
print("\n# One-time dependency (if not installed):")
print("python -m pip install argcomplete")
print("\n# Enable completion for 'struct' in zsh (append to ~/.zshrc):")
print('echo "eval \"$(register-python-argcomplete --shell zsh struct)\"" >> ~/.zshrc')
print("\n# Apply now:")
print("source ~/.zshrc")

elif shell == "fish":
print("\n# One-time dependency (if not installed):")
print("python -m pip install argcomplete")
print("\n# Install fish completion file for 'struct':")
print('mkdir -p ~/.config/fish/completions')
print('register-python-argcomplete --shell fish struct > ~/.config/fish/completions/struct.fish')
print("\n# Apply now:")
print("fish -c 'source ~/.config/fish/completions/struct.fish'")

else:
self.logger.error(f"Unsupported shell: {shell}. Supported: {', '.join(SUPPORTED_SHELLS)}")
return

print("\nTip: If 'register-python-argcomplete' is not found, try:\n python -m argcomplete.shellintegration <shell>")
4 changes: 4 additions & 0 deletions struct_module/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ def main():
GenerateSchemaCommand(subparsers.add_parser('generate-schema', help='Generate JSON schema for available structures'))
MCPCommand(subparsers.add_parser('mcp', help='MCP (Model Context Protocol) support'))

# completion installer
from struct_module.commands.completion import CompletionCommand
CompletionCommand(subparsers.add_parser('completion', help='Manage shell completions'))

argcomplete.autocomplete(parser)

args = parser.parse_args()
Expand Down
61 changes: 61 additions & 0 deletions tests/test_completion_command.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import argparse
import os
from unittest.mock import patch

from struct_module.commands.completion import CompletionCommand


def make_parser():
return argparse.ArgumentParser()


def _gather_print_output(mock_print):
return "\n".join(str(call.args[0]) for call in mock_print.call_args_list)


def test_completion_install_bash_explicit():
parser = make_parser()
cmd = CompletionCommand(parser)
with patch('builtins.print') as mock_print:
args = parser.parse_args(['install', 'bash'])
cmd._install(args)
out = _gather_print_output(mock_print)
assert "Detected shell: bash" in out
assert "register-python-argcomplete struct" in out
assert "~/.bashrc" in out


def test_completion_install_zsh_explicit():
parser = make_parser()
cmd = CompletionCommand(parser)
with patch('builtins.print') as mock_print:
args = parser.parse_args(['install', 'zsh'])
cmd._install(args)
out = _gather_print_output(mock_print)
assert "Detected shell: zsh" in out
assert "register-python-argcomplete --shell zsh struct" in out
assert "~/.zshrc" in out


def test_completion_install_fish_explicit():
parser = make_parser()
cmd = CompletionCommand(parser)
with patch('builtins.print') as mock_print:
args = parser.parse_args(['install', 'fish'])
cmd._install(args)
out = _gather_print_output(mock_print)
assert "Detected shell: fish" in out
assert "register-python-argcomplete --shell fish struct" in out
assert "~/.config/fish/completions/struct.fish" in out


def test_completion_install_auto_detect_zsh():
parser = make_parser()
cmd = CompletionCommand(parser)
with patch.dict(os.environ, {"SHELL": "/bin/zsh"}, clear=False):
with patch('builtins.print') as mock_print:
args = parser.parse_args(['install'])
cmd._install(args)
out = _gather_print_output(mock_print)
assert "Detected shell: zsh" in out
assert "register-python-argcomplete --shell zsh struct" in out
Loading