lacy 1.8.11 → 1.8.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +26 -0
- package/.github/FUNDING.yml +3 -0
- package/.github/ISSUE_TEMPLATE/bug_report.yml +49 -0
- package/.github/ISSUE_TEMPLATE/config.yml +5 -0
- package/.github/ISSUE_TEMPLATE/feature_request.yml +28 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +17 -0
- package/.github/SECURITY.md +32 -0
- package/.github/assets/logo-horizontal-dark.png +0 -0
- package/.github/assets/logo-horizontal-dark.svg +17 -0
- package/.github/assets/logo-horizontal.png +0 -0
- package/.github/assets/logo-horizontal.svg +17 -0
- package/.github/assets/logo.png +0 -0
- package/.github/assets/logo.svg +12 -0
- package/.github/assets/social-preview.png +0 -0
- package/.github/assets/social-preview.svg +50 -0
- package/.github/dependabot.yml +21 -0
- package/.github/workflows/ci.yml +80 -0
- package/.github/workflows/dependabot-auto-merge.yml +32 -0
- package/CHANGELOG.md +366 -0
- package/CLAUDE.md +340 -0
- package/CONTRIBUTING.md +141 -0
- package/LICENSE +110 -0
- package/README.md +201 -31
- package/RELEASING.md +148 -0
- package/STYLE.md +202 -0
- package/assets/hero.jpeg +0 -0
- package/assets/mode-indicators.jpeg +0 -0
- package/assets/real-time-indicator.jpeg +0 -0
- package/assets/supported-tools.jpeg +0 -0
- package/bin/lacy +1028 -0
- package/docs/ADDING-BACKENDS.md +124 -0
- package/docs/DEVTO-ARTICLE.md +94 -0
- package/docs/DOCS.md +68 -0
- package/docs/GROWTH-STRATEGY.md +119 -0
- package/docs/HN-RESPONSES.md +122 -0
- package/docs/LAUNCH-COPY-FINAL.md +105 -0
- package/docs/MARKETING.md +411 -0
- package/docs/NATURAL_LANGUAGE_DETECTION.md +204 -0
- package/docs/UGC_VIDEO_SCRIPT.md +114 -0
- package/docs/articles/devto-how-i-made-my-terminal-understand-english.md +117 -0
- package/docs/demo-color-transition.gif +0 -0
- package/docs/demo-full.gif +0 -0
- package/docs/demo-indicator.gif +0 -0
- package/docs/launch-thread-may6.sh +158 -0
- package/docs/videos/README.md +189 -0
- package/docs/videos/generate_frames.py +510 -0
- package/docs/videos/generate_frames_v2.py +729 -0
- package/docs/videos/generate_short.py +328 -0
- package/docs/videos/generate_short_v2.py +526 -0
- package/docs/videos/lacy-shell-demo-v2.mp4 +0 -0
- package/docs/videos/lacy-shell-demo.mp4 +0 -0
- package/docs/videos/lacy-shell-short-v2.mp4 +0 -0
- package/docs/videos/lacy-shell-short.mp4 +0 -0
- package/install.sh +1009 -0
- package/lacy.plugin.bash +75 -0
- package/lacy.plugin.fish +43 -0
- package/lacy.plugin.zsh +65 -0
- package/lib/animations.zsh +3 -0
- package/lib/bash/completions.bash +40 -0
- package/lib/bash/execute.bash +233 -0
- package/lib/bash/init.bash +40 -0
- package/lib/bash/keybindings.bash +134 -0
- package/lib/bash/prompt.bash +85 -0
- package/lib/commands/info.sh +25 -0
- package/lib/config.zsh +3 -0
- package/lib/constants.zsh +3 -0
- package/lib/core/animations.sh +271 -0
- package/lib/core/commands.sh +297 -0
- package/lib/core/config.sh +340 -0
- package/lib/core/constants.sh +366 -0
- package/lib/core/context.sh +260 -0
- package/lib/core/detection.sh +417 -0
- package/lib/core/mcp.sh +741 -0
- package/lib/core/modes.sh +123 -0
- package/lib/core/preheat.sh +496 -0
- package/lib/core/spinner.sh +174 -0
- package/lib/core/telemetry.sh +99 -0
- package/lib/detection.zsh +3 -0
- package/lib/execute.zsh +3 -0
- package/lib/fish/config.fish +66 -0
- package/lib/fish/detection.fish +90 -0
- package/lib/fish/execute.fish +105 -0
- package/lib/fish/keybindings.fish +42 -0
- package/lib/fish/prompt.fish +30 -0
- package/lib/keybindings.zsh +3 -0
- package/lib/mcp.zsh +3 -0
- package/lib/modes.zsh +3 -0
- package/lib/preheat.zsh +3 -0
- package/lib/prompt.zsh +3 -0
- package/lib/spinner.zsh +3 -0
- package/lib/zsh/completions.zsh +60 -0
- package/lib/zsh/execute.zsh +294 -0
- package/lib/zsh/init.zsh +26 -0
- package/lib/zsh/keybindings.zsh +551 -0
- package/lib/zsh/prompt.zsh +90 -0
- package/package.json +42 -27
- package/packages/lacy/README.md +61 -0
- package/packages/lacy/commands/info.sh +25 -0
- package/{index.mjs → packages/lacy/index.mjs} +247 -20
- package/packages/lacy/package-lock.json +71 -0
- package/packages/lacy/package.json +42 -0
- package/script/release.ts +487 -0
- package/squirrel.toml +36 -0
- package/tests/test_bash.bash +163 -0
- package/tests/test_core.sh +607 -0
- package/tests/test_gemini.sh +119 -0
- package/tests/test_gemini_mcp.sh +126 -0
- package/tests/test_preheat_server.zsh +446 -0
- package/uninstall.sh +52 -0
package/lacy.plugin.bash
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
# Lacy Shell - Smart shell plugin with MCP support (Bash adapter)
|
|
4
|
+
|
|
5
|
+
# Prevent multiple sourcing
|
|
6
|
+
if [[ "${LACY_SHELL_LOADED:-}" == "true" ]]; then
|
|
7
|
+
return 0
|
|
8
|
+
fi
|
|
9
|
+
LACY_SHELL_LOADED=true
|
|
10
|
+
export LACY_SHELL_ACTIVE=1
|
|
11
|
+
|
|
12
|
+
# Plugin directory
|
|
13
|
+
LACY_SHELL_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
14
|
+
|
|
15
|
+
# Shell type identification (used by shared core)
|
|
16
|
+
LACY_SHELL_TYPE="bash"
|
|
17
|
+
_LACY_ARR_OFFSET=0
|
|
18
|
+
|
|
19
|
+
# Load shared core + Bash adapter modules
|
|
20
|
+
source "$LACY_SHELL_DIR/lib/bash/init.bash" || {
|
|
21
|
+
LACY_SHELL_LOADED=false
|
|
22
|
+
return 1
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
# Initialize
|
|
26
|
+
lacy_shell_init() {
|
|
27
|
+
# Performance optimization: Initialize caches early
|
|
28
|
+
lacy_shell_init_detection_cache
|
|
29
|
+
|
|
30
|
+
lacy_shell_load_config
|
|
31
|
+
lacy_shell_setup_keybindings
|
|
32
|
+
lacy_shell_init_mcp
|
|
33
|
+
lacy_preheat_init
|
|
34
|
+
lacy_shell_setup_prompt
|
|
35
|
+
lacy_shell_init_mode
|
|
36
|
+
lacy_shell_setup_interrupt_handler
|
|
37
|
+
lacy_shell_setup_eof_handler
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
# Cleanup
|
|
41
|
+
lacy_shell_cleanup() {
|
|
42
|
+
lacy_stop_spinner 2>/dev/null
|
|
43
|
+
lacy_preheat_cleanup
|
|
44
|
+
lacy_shell_cleanup_mcp
|
|
45
|
+
lacy_shell_cleanup_keybindings_bash
|
|
46
|
+
trap - INT
|
|
47
|
+
unset IGNOREEOF
|
|
48
|
+
# Restore original PROMPT_COMMAND
|
|
49
|
+
if [[ -n "$_LACY_ORIGINAL_PROMPT_COMMAND" ]]; then
|
|
50
|
+
PROMPT_COMMAND="$_LACY_ORIGINAL_PROMPT_COMMAND"
|
|
51
|
+
else
|
|
52
|
+
PROMPT_COMMAND=""
|
|
53
|
+
fi
|
|
54
|
+
LACY_SHELL_QUITTING=false
|
|
55
|
+
LACY_SHELL_ENABLED=false
|
|
56
|
+
LACY_SHELL_LOADED=false
|
|
57
|
+
unset LACY_SHELL_ACTIVE
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
# Set up PROMPT_COMMAND for post-execution hooks
|
|
61
|
+
_LACY_ORIGINAL_PROMPT_COMMAND="${PROMPT_COMMAND:-}"
|
|
62
|
+
if [[ -n "$PROMPT_COMMAND" ]]; then
|
|
63
|
+
PROMPT_COMMAND="lacy_shell_precmd_bash; ${PROMPT_COMMAND}"
|
|
64
|
+
else
|
|
65
|
+
PROMPT_COMMAND="lacy_shell_precmd_bash"
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
# Initialize
|
|
69
|
+
lacy_shell_init
|
|
70
|
+
|
|
71
|
+
# One-time install tracking (background, fail-silent)
|
|
72
|
+
_lacy_track_first_load
|
|
73
|
+
|
|
74
|
+
# Cleanup on exit
|
|
75
|
+
trap lacy_shell_cleanup EXIT
|
package/lacy.plugin.fish
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Lacy Shell — Fish plugin entry point
|
|
2
|
+
# Source this file in ~/.config/fish/conf.d/lacy.fish:
|
|
3
|
+
# source ~/.lacy/lacy.plugin.fish
|
|
4
|
+
#
|
|
5
|
+
# Or let the installer add it automatically.
|
|
6
|
+
|
|
7
|
+
# ============================================================================
|
|
8
|
+
# Guards
|
|
9
|
+
# ============================================================================
|
|
10
|
+
|
|
11
|
+
# Require Fish 3.1+ for bind --sets-mode and commandline -f
|
|
12
|
+
set -l _lacy_fish_major (string match -r 'fish, version (\d+)' -- (fish --version 2>&1) | tail -1)
|
|
13
|
+
if test -z "$_lacy_fish_major"; or test "$_lacy_fish_major" -lt 3
|
|
14
|
+
echo "Lacy Shell: Fish 3.1+ is required. You have: "(fish --version 2>&1)"." >&2
|
|
15
|
+
set --erase _lacy_fish_major
|
|
16
|
+
return
|
|
17
|
+
end
|
|
18
|
+
set --erase _lacy_fish_major
|
|
19
|
+
|
|
20
|
+
# ============================================================================
|
|
21
|
+
# Paths
|
|
22
|
+
# ============================================================================
|
|
23
|
+
|
|
24
|
+
set -gx LACY_SHELL_HOME "$HOME/.lacy"
|
|
25
|
+
set -gx LACY_SHELL_TYPE "fish"
|
|
26
|
+
set -gx LACY_SHELL_ACTIVE 1
|
|
27
|
+
set -gx LACY_SHELL_DIR (dirname (realpath (status filename) 2>/dev/null; or echo "$HOME/.lacy"))
|
|
28
|
+
|
|
29
|
+
# ============================================================================
|
|
30
|
+
# Source Fish modules
|
|
31
|
+
# ============================================================================
|
|
32
|
+
|
|
33
|
+
set -l _lacy_fish_dir "$LACY_SHELL_DIR/lib/fish"
|
|
34
|
+
|
|
35
|
+
if test -d "$_lacy_fish_dir"
|
|
36
|
+
source "$_lacy_fish_dir/config.fish"
|
|
37
|
+
source "$_lacy_fish_dir/detection.fish"
|
|
38
|
+
source "$_lacy_fish_dir/execute.fish"
|
|
39
|
+
source "$_lacy_fish_dir/keybindings.fish"
|
|
40
|
+
source "$_lacy_fish_dir/prompt.fish"
|
|
41
|
+
else
|
|
42
|
+
echo "Lacy Shell: lib/fish not found at $_lacy_fish_dir" >&2
|
|
43
|
+
end
|
package/lacy.plugin.zsh
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
#!/usr/bin/env zsh
|
|
2
|
+
|
|
3
|
+
# Lacy Shell - Smart shell plugin with MCP support
|
|
4
|
+
|
|
5
|
+
# Prevent multiple sourcing
|
|
6
|
+
if [[ "${LACY_SHELL_LOADED:-}" == "true" ]]; then
|
|
7
|
+
return 0
|
|
8
|
+
fi
|
|
9
|
+
LACY_SHELL_LOADED=true
|
|
10
|
+
export LACY_SHELL_ACTIVE=1
|
|
11
|
+
|
|
12
|
+
# Plugin directory
|
|
13
|
+
LACY_SHELL_DIR="${0:A:h}"
|
|
14
|
+
|
|
15
|
+
# Shell type identification (used by shared core)
|
|
16
|
+
LACY_SHELL_TYPE="zsh"
|
|
17
|
+
_LACY_ARR_OFFSET=1
|
|
18
|
+
|
|
19
|
+
# Load shared core + ZSH adapter modules
|
|
20
|
+
source "$LACY_SHELL_DIR/lib/zsh/init.zsh"
|
|
21
|
+
|
|
22
|
+
# Initialize
|
|
23
|
+
lacy_shell_init() {
|
|
24
|
+
# Performance optimization: Initialize caches early
|
|
25
|
+
lacy_shell_init_detection_cache
|
|
26
|
+
|
|
27
|
+
lacy_shell_load_config
|
|
28
|
+
lacy_shell_setup_keybindings
|
|
29
|
+
lacy_shell_init_mcp
|
|
30
|
+
lacy_preheat_init
|
|
31
|
+
lacy_shell_setup_prompt
|
|
32
|
+
lacy_shell_init_mode
|
|
33
|
+
lacy_shell_setup_interrupt_handler
|
|
34
|
+
lacy_shell_setup_eof_handler
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
# Cleanup
|
|
38
|
+
lacy_shell_cleanup() {
|
|
39
|
+
lacy_stop_spinner 2>/dev/null
|
|
40
|
+
lacy_shell_remove_top_bar
|
|
41
|
+
lacy_preheat_cleanup
|
|
42
|
+
lacy_shell_cleanup_mcp
|
|
43
|
+
lacy_shell_cleanup_keybindings
|
|
44
|
+
unfunction TRAPINT 2>/dev/null
|
|
45
|
+
trap - INT
|
|
46
|
+
unsetopt IGNORE_EOF
|
|
47
|
+
unset IGNOREEOF
|
|
48
|
+
LACY_SHELL_QUITTING=false
|
|
49
|
+
LACY_SHELL_ENABLED=false
|
|
50
|
+
LACY_SHELL_LOADED=false
|
|
51
|
+
unset LACY_SHELL_ACTIVE
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
# Set up hooks
|
|
55
|
+
zle -N accept-line lacy_shell_smart_accept_line
|
|
56
|
+
precmd_functions+=(lacy_shell_precmd)
|
|
57
|
+
|
|
58
|
+
# Initialize
|
|
59
|
+
lacy_shell_init
|
|
60
|
+
|
|
61
|
+
# One-time install tracking (background, fail-silent)
|
|
62
|
+
_lacy_track_first_load
|
|
63
|
+
|
|
64
|
+
# Cleanup on exit
|
|
65
|
+
trap lacy_shell_cleanup EXIT
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
# Bash tab completion for the `lacy` CLI
|
|
4
|
+
# Sourced by lib/bash/init.bash after all core modules are loaded
|
|
5
|
+
|
|
6
|
+
_lacy_completions() {
|
|
7
|
+
local cur prev
|
|
8
|
+
cur="${COMP_WORDS[COMP_CWORD]}"
|
|
9
|
+
prev="${COMP_WORDS[COMP_CWORD-1]}"
|
|
10
|
+
|
|
11
|
+
local commands="setup install uninstall update reinstall status info doctor config new resume logs changelog completions version help"
|
|
12
|
+
local tool_names="lash claude opencode gemini codex custom auto"
|
|
13
|
+
|
|
14
|
+
case "$prev" in
|
|
15
|
+
config)
|
|
16
|
+
COMPREPLY=( $(compgen -W "show edit path" -- "$cur") )
|
|
17
|
+
return
|
|
18
|
+
;;
|
|
19
|
+
logs)
|
|
20
|
+
COMPREPLY=( $(compgen -W "--clear --count" -- "$cur") )
|
|
21
|
+
return
|
|
22
|
+
;;
|
|
23
|
+
set)
|
|
24
|
+
COMPREPLY=( $(compgen -W "$tool_names" -- "$cur") )
|
|
25
|
+
return
|
|
26
|
+
;;
|
|
27
|
+
tool)
|
|
28
|
+
COMPREPLY=( $(compgen -W "set list" -- "$cur") )
|
|
29
|
+
return
|
|
30
|
+
;;
|
|
31
|
+
completions)
|
|
32
|
+
COMPREPLY=( $(compgen -W "zsh bash" -- "$cur") )
|
|
33
|
+
return
|
|
34
|
+
;;
|
|
35
|
+
esac
|
|
36
|
+
|
|
37
|
+
COMPREPLY=( $(compgen -W "$commands" -- "$cur") )
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
complete -F _lacy_completions lacy
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
# Command execution logic for Lacy Shell — Bash adapter
|
|
4
|
+
# Uses bind -x for Enter override + PROMPT_COMMAND for post-exec
|
|
5
|
+
|
|
6
|
+
# Pending query (set by Enter handler, dispatched by PROMPT_COMMAND)
|
|
7
|
+
LACY_SHELL_PENDING_QUERY=""
|
|
8
|
+
LACY_SHELL_REROUTE_CANDIDATE=""
|
|
9
|
+
LACY_SHELL_PENDING_CMD=""
|
|
10
|
+
_lacy_last_exit=0
|
|
11
|
+
|
|
12
|
+
# Smart accept-line for Bash — called by bind -x on Enter
|
|
13
|
+
lacy_shell_smart_accept_line_bash() {
|
|
14
|
+
# If disabled, let normal readline handle it
|
|
15
|
+
if [[ "$LACY_SHELL_ENABLED" != true ]]; then
|
|
16
|
+
return
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
local input="$READLINE_LINE"
|
|
20
|
+
|
|
21
|
+
# Skip empty commands
|
|
22
|
+
if [[ -z "$input" ]]; then
|
|
23
|
+
return
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
# Intercept slash-prefixed session commands (/new, /reset, /clear, /resume)
|
|
27
|
+
local _slashcmd="$input"
|
|
28
|
+
_slashcmd="${_slashcmd#"${_slashcmd%%[^[:space:]]*}"}"
|
|
29
|
+
case "$_slashcmd" in
|
|
30
|
+
/new|/reset|/clear|/resume)
|
|
31
|
+
history -s -- "$input"
|
|
32
|
+
history -a 2>/dev/null
|
|
33
|
+
if [[ "$_slashcmd" == "/resume" ]]; then
|
|
34
|
+
LACY_SHELL_PENDING_CMD="session_resume"
|
|
35
|
+
else
|
|
36
|
+
LACY_SHELL_PENDING_CMD="session_new"
|
|
37
|
+
fi
|
|
38
|
+
READLINE_LINE=""
|
|
39
|
+
READLINE_POINT=0
|
|
40
|
+
return
|
|
41
|
+
;;
|
|
42
|
+
esac
|
|
43
|
+
|
|
44
|
+
# Classify using centralized detection
|
|
45
|
+
local classification
|
|
46
|
+
classification=$(lacy_shell_classify_input "$input")
|
|
47
|
+
|
|
48
|
+
case "$classification" in
|
|
49
|
+
"neutral")
|
|
50
|
+
# Let readline process normally
|
|
51
|
+
return
|
|
52
|
+
;;
|
|
53
|
+
"shell")
|
|
54
|
+
# Trim to check for ! bypass
|
|
55
|
+
local trimmed="$input"
|
|
56
|
+
trimmed="${trimmed#"${trimmed%%[^[:space:]]*}"}"
|
|
57
|
+
|
|
58
|
+
if [[ "$trimmed" == !* ]]; then
|
|
59
|
+
trimmed="${trimmed#!}"
|
|
60
|
+
READLINE_LINE="$trimmed"
|
|
61
|
+
READLINE_POINT=${#trimmed}
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
# Handle "exit" — in auto/agent mode quit lacy shell
|
|
65
|
+
local first_word="${trimmed%% *}"
|
|
66
|
+
if [[ "$first_word" == "exit" && "$LACY_SHELL_CURRENT_MODE" != "shell" ]]; then
|
|
67
|
+
READLINE_LINE=""
|
|
68
|
+
READLINE_POINT=0
|
|
69
|
+
lacy_shell_quit
|
|
70
|
+
return
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
# In auto mode, flag commands with NL markers as reroute candidates
|
|
74
|
+
if [[ "$LACY_SHELL_CURRENT_MODE" == "auto" ]] && lacy_shell_has_nl_markers "$trimmed"; then
|
|
75
|
+
LACY_SHELL_REROUTE_CANDIDATE="$trimmed"
|
|
76
|
+
else
|
|
77
|
+
LACY_SHELL_REROUTE_CANDIDATE=""
|
|
78
|
+
fi
|
|
79
|
+
|
|
80
|
+
# Record command for terminal context (agent will see it on next query)
|
|
81
|
+
_lacy_ctx_mark_command "$READLINE_LINE"
|
|
82
|
+
|
|
83
|
+
# Let readline execute the command normally
|
|
84
|
+
return
|
|
85
|
+
;;
|
|
86
|
+
"agent")
|
|
87
|
+
# Strip @ agent bypass prefix if present
|
|
88
|
+
local agent_input="$input"
|
|
89
|
+
local _at_trimmed="${agent_input#"${agent_input%%[^[:space:]]*}"}"
|
|
90
|
+
if [[ "$_at_trimmed" == @* ]]; then
|
|
91
|
+
agent_input="${_at_trimmed#@}"
|
|
92
|
+
agent_input="${agent_input#"${agent_input%%[^[:space:]]*}"}"
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
# Add to Bash history
|
|
96
|
+
history -s -- "$input"
|
|
97
|
+
# Flush to HISTFILE immediately — needed when HISTFILE/PROMPT_COMMAND
|
|
98
|
+
# write-on-exit isn't sufficient (e.g. with HISTAPPEND set).
|
|
99
|
+
history -a 2>/dev/null
|
|
100
|
+
|
|
101
|
+
# Defer agent execution to PROMPT_COMMAND
|
|
102
|
+
LACY_SHELL_PENDING_QUERY="$agent_input"
|
|
103
|
+
READLINE_LINE=""
|
|
104
|
+
READLINE_POINT=0
|
|
105
|
+
return
|
|
106
|
+
;;
|
|
107
|
+
esac
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
# lacy_shell_execute_agent is in lib/core/commands.sh
|
|
111
|
+
|
|
112
|
+
# Precmd equivalent for Bash — called via PROMPT_COMMAND
|
|
113
|
+
lacy_shell_precmd_bash() {
|
|
114
|
+
# Capture exit code immediately
|
|
115
|
+
_lacy_last_exit=$?
|
|
116
|
+
|
|
117
|
+
# Track exit code for terminal context (only for real shell commands)
|
|
118
|
+
_lacy_ctx_on_precmd $_lacy_last_exit
|
|
119
|
+
|
|
120
|
+
# Ensure terminal state is clean
|
|
121
|
+
printf '\e[?25h' # Cursor visible
|
|
122
|
+
printf '\e[?7h' # Line wrapping enabled
|
|
123
|
+
|
|
124
|
+
# Don't run if disabled or quitting
|
|
125
|
+
if [[ "$LACY_SHELL_ENABLED" != true || "$LACY_SHELL_QUITTING" == true ]]; then
|
|
126
|
+
LACY_SHELL_REROUTE_CANDIDATE=""
|
|
127
|
+
return
|
|
128
|
+
fi
|
|
129
|
+
|
|
130
|
+
# Check reroute candidate: show hint to re-try via agent with @ prefix
|
|
131
|
+
if [[ -n "$LACY_SHELL_REROUTE_CANDIDATE" ]]; then
|
|
132
|
+
local candidate="$LACY_SHELL_REROUTE_CANDIDATE"
|
|
133
|
+
LACY_SHELL_REROUTE_CANDIDATE=""
|
|
134
|
+
if (( _lacy_last_exit != 0 && _lacy_last_exit < LACY_SIGNAL_EXIT_THRESHOLD )); then
|
|
135
|
+
printf ' \e[38;5;%dm%s\e[0m \e[38;5;%dm@ %s\e[0m\n' \
|
|
136
|
+
"$LACY_COLOR_AGENT" "$LACY_INDICATOR_CHAR" \
|
|
137
|
+
"$LACY_COLOR_NEUTRAL" "$candidate"
|
|
138
|
+
fi
|
|
139
|
+
fi
|
|
140
|
+
|
|
141
|
+
# Handle deferred quit triggered by Ctrl-D
|
|
142
|
+
if [[ "$LACY_SHELL_DEFER_QUIT" == true ]]; then
|
|
143
|
+
LACY_SHELL_DEFER_QUIT=false
|
|
144
|
+
LACY_SHELL_REROUTE_CANDIDATE=""
|
|
145
|
+
lacy_shell_quit
|
|
146
|
+
return
|
|
147
|
+
fi
|
|
148
|
+
|
|
149
|
+
# Handle pending internal commands (from slash-prefixed session commands)
|
|
150
|
+
if [[ -n "$LACY_SHELL_PENDING_CMD" ]]; then
|
|
151
|
+
local _cmd="$LACY_SHELL_PENDING_CMD"
|
|
152
|
+
LACY_SHELL_PENDING_CMD=""
|
|
153
|
+
case "$_cmd" in
|
|
154
|
+
session_new) lacy_session_new ;;
|
|
155
|
+
session_resume) lacy_session_resume ;;
|
|
156
|
+
esac
|
|
157
|
+
fi
|
|
158
|
+
|
|
159
|
+
# Handle pending agent query
|
|
160
|
+
if [[ -n "$LACY_SHELL_PENDING_QUERY" ]]; then
|
|
161
|
+
local pending="$LACY_SHELL_PENDING_QUERY"
|
|
162
|
+
LACY_SHELL_PENDING_QUERY=""
|
|
163
|
+
# Show the query text that was cleared from readline
|
|
164
|
+
printf '\e[38;5;%dm%s\e[0m %s\n' "$LACY_COLOR_AGENT" "$LACY_INDICATOR_CHAR" "$pending"
|
|
165
|
+
lacy_shell_execute_agent "$pending"
|
|
166
|
+
fi
|
|
167
|
+
|
|
168
|
+
# Update prompt
|
|
169
|
+
lacy_shell_update_prompt
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
# lacy_shell_mode, lacy_shell_tool, lacy_shell_spinner, lacy_shell_clear_conversation,
|
|
173
|
+
# lacy_shell_show_conversation, and lacy() are in lib/core/commands.sh
|
|
174
|
+
|
|
175
|
+
# Quit lacy shell
|
|
176
|
+
lacy_shell_quit() {
|
|
177
|
+
LACY_SHELL_ENABLED=false
|
|
178
|
+
LACY_SHELL_QUITTING=true
|
|
179
|
+
unset LACY_SHELL_ACTIVE
|
|
180
|
+
|
|
181
|
+
echo ""
|
|
182
|
+
lacy_print_color "$LACY_COLOR_NEUTRAL" "$LACY_MSG_QUIT"
|
|
183
|
+
echo ""
|
|
184
|
+
|
|
185
|
+
# Remove our PROMPT_COMMAND entry
|
|
186
|
+
if [[ -n "$_LACY_ORIGINAL_PROMPT_COMMAND" ]]; then
|
|
187
|
+
PROMPT_COMMAND="$_LACY_ORIGINAL_PROMPT_COMMAND"
|
|
188
|
+
else
|
|
189
|
+
PROMPT_COMMAND=""
|
|
190
|
+
fi
|
|
191
|
+
|
|
192
|
+
# Cleanup keybindings
|
|
193
|
+
lacy_shell_cleanup_keybindings_bash
|
|
194
|
+
|
|
195
|
+
# Terminal reset
|
|
196
|
+
printf '\e[0m' # Reset attributes
|
|
197
|
+
printf '\e[?7h' # Line wrapping
|
|
198
|
+
printf '\e[?25h' # Cursor visible
|
|
199
|
+
|
|
200
|
+
# Stop preheated servers
|
|
201
|
+
lacy_preheat_cleanup
|
|
202
|
+
|
|
203
|
+
# Unset functions used as commands
|
|
204
|
+
unset -f ask mode tool spinner quit stop lacy 2>/dev/null
|
|
205
|
+
|
|
206
|
+
# Define a `lacy` function so user can re-enter by typing `lacy`
|
|
207
|
+
local _ldir="$LACY_SHELL_DIR"
|
|
208
|
+
eval "lacy() {
|
|
209
|
+
if [[ \$# -eq 0 ]]; then
|
|
210
|
+
unset -f lacy 2>/dev/null
|
|
211
|
+
LACY_SHELL_LOADED=false
|
|
212
|
+
source \"${_ldir}/lacy.plugin.bash\"
|
|
213
|
+
else
|
|
214
|
+
command lacy \"\$@\"
|
|
215
|
+
fi
|
|
216
|
+
}"
|
|
217
|
+
|
|
218
|
+
# Restore prompt
|
|
219
|
+
lacy_shell_restore_prompt
|
|
220
|
+
|
|
221
|
+
echo ""
|
|
222
|
+
|
|
223
|
+
LACY_SHELL_QUITTING=false
|
|
224
|
+
LACY_SHELL_LOADED=false
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
# Define command functions (Bash uses functions, not aliases, for reliability)
|
|
228
|
+
ask() { lacy_shell_query_agent "$*"; }
|
|
229
|
+
mode() { lacy_shell_mode "$@"; }
|
|
230
|
+
tool() { lacy_shell_tool "$@"; }
|
|
231
|
+
spinner() { lacy_shell_spinner "$@"; }
|
|
232
|
+
quit() { lacy_shell_quit; }
|
|
233
|
+
stop() { lacy_shell_quit; }
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
# Bash adapter init — sources shared core + Bash-specific modules
|
|
4
|
+
|
|
5
|
+
# Require Bash 4+ (for declare -A, ${var,,}, READLINE_LINE)
|
|
6
|
+
if [[ ${BASH_VERSINFO[0]} -lt 4 ]]; then
|
|
7
|
+
echo "Lacy Shell requires Bash 4 or later. You have Bash ${BASH_VERSION}."
|
|
8
|
+
echo ""
|
|
9
|
+
echo "Upgrade options:"
|
|
10
|
+
echo " macOS: brew install bash"
|
|
11
|
+
echo " Linux: sudo apt install bash (or your package manager)"
|
|
12
|
+
echo ""
|
|
13
|
+
echo "After installing, add the new bash to /etc/shells and set it as default:"
|
|
14
|
+
echo " echo /opt/homebrew/bin/bash | sudo tee -a /etc/shells"
|
|
15
|
+
echo " chsh -s /opt/homebrew/bin/bash"
|
|
16
|
+
return 1 2>/dev/null || exit 1
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
# Shell type is already set by lacy.plugin.bash before sourcing this file.
|
|
20
|
+
# LACY_SHELL_TYPE="bash"
|
|
21
|
+
# _LACY_ARR_OFFSET=0
|
|
22
|
+
|
|
23
|
+
# Source shared core modules
|
|
24
|
+
source "$LACY_SHELL_DIR/lib/core/constants.sh"
|
|
25
|
+
source "$LACY_SHELL_DIR/lib/core/config.sh"
|
|
26
|
+
source "$LACY_SHELL_DIR/lib/core/modes.sh"
|
|
27
|
+
source "$LACY_SHELL_DIR/lib/core/animations.sh"
|
|
28
|
+
source "$LACY_SHELL_DIR/lib/core/spinner.sh"
|
|
29
|
+
source "$LACY_SHELL_DIR/lib/core/mcp.sh"
|
|
30
|
+
source "$LACY_SHELL_DIR/lib/core/preheat.sh"
|
|
31
|
+
source "$LACY_SHELL_DIR/lib/core/context.sh"
|
|
32
|
+
source "$LACY_SHELL_DIR/lib/core/detection.sh"
|
|
33
|
+
source "$LACY_SHELL_DIR/lib/core/commands.sh"
|
|
34
|
+
source "$LACY_SHELL_DIR/lib/core/telemetry.sh"
|
|
35
|
+
|
|
36
|
+
# Source Bash-specific adapter modules
|
|
37
|
+
source "$LACY_SHELL_DIR/lib/bash/keybindings.bash"
|
|
38
|
+
source "$LACY_SHELL_DIR/lib/bash/prompt.bash"
|
|
39
|
+
source "$LACY_SHELL_DIR/lib/bash/execute.bash"
|
|
40
|
+
source "$LACY_SHELL_DIR/lib/bash/completions.bash"
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
# Keybinding setup for Lacy Shell — Bash adapter
|
|
4
|
+
# Uses bind -x for Enter override and Ctrl+Space toggle
|
|
5
|
+
|
|
6
|
+
# Interrupt state and input type are initialized in constants.sh
|
|
7
|
+
|
|
8
|
+
# Track agent execution state for interrupt handling
|
|
9
|
+
LACY_SHELL_AGENT_RUNNING=false
|
|
10
|
+
|
|
11
|
+
# Save original IGNOREEOF
|
|
12
|
+
_LACY_ORIGINAL_IGNOREEOF="${IGNOREEOF:-}"
|
|
13
|
+
|
|
14
|
+
# Set up all keybindings
|
|
15
|
+
lacy_shell_setup_keybindings() {
|
|
16
|
+
# Bind our classification function to a hidden key sequence.
|
|
17
|
+
# We can't bind -x directly to \C-m because bind -x replaces the
|
|
18
|
+
# key action entirely — accept-line never fires, so shell commands
|
|
19
|
+
# never submit. Instead, \C-m is a macro: call classification, then
|
|
20
|
+
# \C-j (accept-line). Classification can clear READLINE_LINE for
|
|
21
|
+
# agent queries; accept-line then submits the (possibly empty) line.
|
|
22
|
+
bind -x '"\C-x\C-l": lacy_shell_smart_accept_line_bash'
|
|
23
|
+
bind '"\C-m": "\C-x\C-l\C-j"'
|
|
24
|
+
|
|
25
|
+
# Ctrl+Space: Toggle mode via bind -x
|
|
26
|
+
# We save/restore READLINE_LINE so the user's in-progress input is preserved.
|
|
27
|
+
# After toggling, we update PS1 and force readline to redraw the prompt.
|
|
28
|
+
bind -x '"\C-@": _lacy_ctrl_space_toggle'
|
|
29
|
+
|
|
30
|
+
# Ensure \C-j is bound to accept-line (used by macros above)
|
|
31
|
+
bind '"\C-j": accept-line'
|
|
32
|
+
|
|
33
|
+
# Prevent Ctrl-D from exiting the shell
|
|
34
|
+
IGNOREEOF=1000
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
# Ctrl+Space handler via bind -x.
|
|
38
|
+
# Toggles mode, prints feedback, updates PS1, and redraws the prompt
|
|
39
|
+
# in-place without exposing any internal command text to the user.
|
|
40
|
+
_lacy_ctrl_space_toggle() {
|
|
41
|
+
local saved_line="$READLINE_LINE"
|
|
42
|
+
local saved_point="$READLINE_POINT"
|
|
43
|
+
|
|
44
|
+
lacy_shell_toggle_mode
|
|
45
|
+
lacy_shell_update_prompt
|
|
46
|
+
|
|
47
|
+
local new_mode="$LACY_SHELL_CURRENT_MODE"
|
|
48
|
+
# Print mode feedback above the prompt
|
|
49
|
+
case "$new_mode" in
|
|
50
|
+
"shell") printf '\n'; _lacy_print_indicator_msg "$LACY_COLOR_SHELL" "$LACY_MSG_MODE_SHELL_SHORT" ;;
|
|
51
|
+
"agent") printf '\n'; _lacy_print_indicator_msg "$LACY_COLOR_AGENT" "$LACY_MSG_MODE_AGENT_SHORT" ;;
|
|
52
|
+
"auto") printf '\n'; _lacy_print_indicator_msg "$LACY_COLOR_AUTO" "$LACY_MSG_MODE_AUTO_SHORT" ;;
|
|
53
|
+
esac
|
|
54
|
+
|
|
55
|
+
# Restore the user's in-progress input
|
|
56
|
+
READLINE_LINE="$saved_line"
|
|
57
|
+
READLINE_POINT="$saved_point"
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
# Interrupt handler for double Ctrl-C quit
|
|
61
|
+
lacy_shell_interrupt_handler_bash() {
|
|
62
|
+
# Don't handle if disabled
|
|
63
|
+
if [[ "$LACY_SHELL_ENABLED" != true ]]; then
|
|
64
|
+
return
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
# During agent execution, just stop the spinner and return cleanly.
|
|
68
|
+
# The agent child process already received SIGINT from the kernel.
|
|
69
|
+
# This mirrors ZSH's TRAPINT behavior which checks $ZLE_STATE and
|
|
70
|
+
# lets SIGINT propagate to child processes when ZLE is inactive.
|
|
71
|
+
if [[ "$LACY_SHELL_AGENT_RUNNING" == true ]]; then
|
|
72
|
+
lacy_stop_spinner
|
|
73
|
+
LACY_SHELL_AGENT_RUNNING=false
|
|
74
|
+
echo ""
|
|
75
|
+
return
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
# Get current time in milliseconds (portable, no python3 overhead)
|
|
79
|
+
local current_time
|
|
80
|
+
if command -v gdate >/dev/null 2>&1; then
|
|
81
|
+
current_time=$(gdate +%s%3N)
|
|
82
|
+
elif date +%s%3N 2>/dev/null | grep -q '^[0-9]*$'; then
|
|
83
|
+
current_time=$(date +%s%3N)
|
|
84
|
+
else
|
|
85
|
+
# macOS: second-precision fallback (adequate for double-tap detection)
|
|
86
|
+
current_time=$(( $(date +%s) * 1000 ))
|
|
87
|
+
fi
|
|
88
|
+
|
|
89
|
+
local time_diff=$(( current_time - LACY_SHELL_LAST_INTERRUPT_TIME ))
|
|
90
|
+
|
|
91
|
+
if [[ $time_diff -lt $LACY_SHELL_EXIT_TIMEOUT_MS ]]; then
|
|
92
|
+
# Double Ctrl+C — quit
|
|
93
|
+
echo ""
|
|
94
|
+
lacy_shell_quit
|
|
95
|
+
else
|
|
96
|
+
LACY_SHELL_LAST_INTERRUPT_TIME=$current_time
|
|
97
|
+
echo ""
|
|
98
|
+
lacy_print_color "$LACY_COLOR_NEUTRAL" "$LACY_MSG_CTRL_C_HINT"
|
|
99
|
+
fi
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
# Set up the interrupt handler
|
|
103
|
+
lacy_shell_setup_interrupt_handler() {
|
|
104
|
+
trap 'lacy_shell_interrupt_handler_bash' INT
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
# EOF handler (Ctrl-D) — IGNOREEOF handles it, but we add deferred quit
|
|
108
|
+
lacy_shell_setup_eof_handler() {
|
|
109
|
+
IGNOREEOF=1000
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
# Cleanup keybindings
|
|
113
|
+
lacy_shell_cleanup_keybindings_bash() {
|
|
114
|
+
# Restore Enter to default readline behavior
|
|
115
|
+
bind '"\C-m": accept-line' 2>/dev/null
|
|
116
|
+
|
|
117
|
+
# Remove hidden classification key
|
|
118
|
+
bind -r '"\C-x\C-l"' 2>/dev/null
|
|
119
|
+
|
|
120
|
+
# Restore Ctrl+Space and \C-j to defaults
|
|
121
|
+
bind -r '"\C-@"' 2>/dev/null
|
|
122
|
+
bind '"\C-@": set-mark' 2>/dev/null
|
|
123
|
+
bind '"\C-j": accept-line' 2>/dev/null
|
|
124
|
+
|
|
125
|
+
# Restore IGNOREEOF
|
|
126
|
+
if [[ -n "$_LACY_ORIGINAL_IGNOREEOF" ]]; then
|
|
127
|
+
IGNOREEOF="$_LACY_ORIGINAL_IGNOREEOF"
|
|
128
|
+
else
|
|
129
|
+
unset IGNOREEOF
|
|
130
|
+
fi
|
|
131
|
+
|
|
132
|
+
# Remove interrupt trap
|
|
133
|
+
trap - INT
|
|
134
|
+
}
|