feat(nushell): add Nushell plugin support#59
Conversation
Adds lacy.plugin.nu and lib/nu/{config,detection,execute,keybindings,prompt}.nu
following the same structure as the Fish plugin. Provides config loading,
input classification (via `which` instead of `command -v`), agent routing,
Alt+Enter keybinding to send buffer to agent, Ctrl+Space mode toggle, and
a right-prompt mode badge. Requires Nushell 0.87+.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
There was a problem hiding this comment.
Code Review
This pull request introduces a Nushell plugin for Lacy Shell, enabling AI agent integration. It includes modules for configuration, input classification, command execution routing, keybindings, and prompt customization. Feedback was provided regarding the manual YAML parsing in lib/nu/config.nu, recommending the use of Nushell's native 'from yaml' command to improve robustness and correctly handle nested structures.
| def _lacy_load_config [] { | ||
| let config_file = ($env.HOME | path join ".lacy" "config.yaml") | ||
| if not ($config_file | path exists) { return } | ||
|
|
||
| let file_lines = (open --raw $config_file | lines) | ||
|
|
||
| # Parse agent_tools.active | ||
| let active_lines = ($file_lines | where { |line| ($line | str trim | str starts-with "active:") }) | ||
| if ($active_lines | length) > 0 { | ||
| let active = ($active_lines | first | ||
| | str replace -r '.*active:\s*' '' | ||
| | str trim | ||
| | str replace --all '"' '' | ||
| | str replace --all "'" '') | ||
| if $active != "" { $env.LACY_ACTIVE_TOOL = $active } | ||
| } | ||
|
|
||
| # Parse agent_tools.custom_command | ||
| let custom_lines = ($file_lines | where { |line| ($line | str trim | str starts-with "custom_command:") }) | ||
| if ($custom_lines | length) > 0 { | ||
| let custom = ($custom_lines | first | ||
| | str replace -r '.*custom_command:\s*' '' | ||
| | str trim | ||
| | str replace --all '"' '' | ||
| | str replace --all "'" '') | ||
| if $custom != "" { $env.LACY_CUSTOM_TOOL_CMD = $custom } | ||
| } | ||
|
|
||
| # Parse modes.default | ||
| let mode_lines = ($file_lines | where { |line| ($line | str trim | str starts-with "default:") }) | ||
| if ($mode_lines | length) > 0 { | ||
| let mode = ($mode_lines | first | ||
| | str replace -r '.*default:\s*' '' | ||
| | str trim | ||
| | str replace --all '"' '' | ||
| | str replace --all "'" '') | ||
| if $mode != "" { $env.LACY_SHELL_MODE = $mode } | ||
| } | ||
| } |
There was a problem hiding this comment.
The current implementation of _lacy_load_config manually parses the config.yaml file using string manipulation. This approach is fragile and can easily break if the YAML structure changes (e.g., with different indentation). It also doesn't correctly handle the nested structure of the configuration file (e.g. agent_tools.active), as it searches for keys like active: anywhere in the file.
Nushell provides built-in support for parsing YAML with the from yaml command. Using it would make the code much more robust, maintainable, and idiomatic.
Consider this refactoring, which uses from yaml to correctly parse the nested configuration and is resilient to formatting changes:
def _lacy_load_config [] {
let config_file = ($env.HOME | path join ".lacy" "config.yaml")
if not ($config_file | path exists) { return }
try {
let config = (open $config_file | from yaml)
if ($config | get agent_tools.active?) != null {
let active = ($config.agent_tools.active | default "" | str trim)
if $active != "" { $env.LACY_ACTIVE_TOOL = $active }
}
if ($config | get agent_tools.custom_command?) != null {
let custom = ($config.agent_tools.custom_command | default "" | str trim)
if $custom != "" { $env.LACY_CUSTOM_TOOL_CMD = $custom }
}
if ($config | get modes.default?) != null {
let mode = ($config.modes.default | default "" | str trim)
if $mode != "" { $env.LACY_SHELL_MODE = $mode }
}
} catch {
print --stderr "Lacy Shell: Error parsing ~/.lacy/config.yaml. Please check its format."
}
}
…1302) - Replace fragile line-by-line YAML string parsing with `from yaml` + `get --optional <path>` so nested keys (agent_tools.active vs modes.default) no longer collide and parsing survives indentation changes - Mark `_lacy_load_config` and `_lacy_toggle_mode` as `def --env` so their $env mutations actually propagate to the caller — without it the config load and mode toggle silently no-op'd in real Nushell sessions
|
CI green on c55f9ca: Syntax (bash) ✓, Syntax (zsh) ✓, shellcheck ✓. PR is MERGEABLE. Holding |
Summary
Closes #38. Adds Nushell support following the same structure as the Fish plugin (
lacy.plugin.fish+lib/fish/).lacy.plugin.nu— entry point; source from~/.config/nushell/config.nulib/nu/config.nu— globals, agent words, config.yaml loaderlib/nu/detection.nu—_lacy_classify_input(useswhichinstead ofcommand -v)lib/nu/execute.nu—_lacy_query_agent,_lacy_send_to_agent, publiclacycommandlib/nu/keybindings.nu— Alt+Enter (send to agent), Ctrl+Space (mode toggle)lib/nu/prompt.nu— right-prompt mode badge (SHELL / AGENT / AUTO)Nushell limitation: Enter cannot be seamlessly overridden without losing interactive shell state (Nushell has no equivalent to ZSH's
zle accept-lineor Fish'scommandline -f execute). Instead, normal Enter executes as shell and Alt+Enter routes the buffer to the agent. Thelacy "query"command is also available for explicit queries.Requires Nushell 0.87+. Zero changes to existing files.
Test plan
source ~/.lacy/lacy.plugin.nuin~/.config/nushell/config.nuAUTObadgelacy "why does this fail?"invokes the active AI toollacywithout args prompts for input~/.lacy/config.yamlis picked up (active tool, default mode)