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.
Files changed (109) hide show
  1. package/.claude/settings.local.json +26 -0
  2. package/.github/FUNDING.yml +3 -0
  3. package/.github/ISSUE_TEMPLATE/bug_report.yml +49 -0
  4. package/.github/ISSUE_TEMPLATE/config.yml +5 -0
  5. package/.github/ISSUE_TEMPLATE/feature_request.yml +28 -0
  6. package/.github/PULL_REQUEST_TEMPLATE.md +17 -0
  7. package/.github/SECURITY.md +32 -0
  8. package/.github/assets/logo-horizontal-dark.png +0 -0
  9. package/.github/assets/logo-horizontal-dark.svg +17 -0
  10. package/.github/assets/logo-horizontal.png +0 -0
  11. package/.github/assets/logo-horizontal.svg +17 -0
  12. package/.github/assets/logo.png +0 -0
  13. package/.github/assets/logo.svg +12 -0
  14. package/.github/assets/social-preview.png +0 -0
  15. package/.github/assets/social-preview.svg +50 -0
  16. package/.github/dependabot.yml +21 -0
  17. package/.github/workflows/ci.yml +80 -0
  18. package/.github/workflows/dependabot-auto-merge.yml +32 -0
  19. package/CHANGELOG.md +366 -0
  20. package/CLAUDE.md +340 -0
  21. package/CONTRIBUTING.md +141 -0
  22. package/LICENSE +110 -0
  23. package/README.md +201 -31
  24. package/RELEASING.md +148 -0
  25. package/STYLE.md +202 -0
  26. package/assets/hero.jpeg +0 -0
  27. package/assets/mode-indicators.jpeg +0 -0
  28. package/assets/real-time-indicator.jpeg +0 -0
  29. package/assets/supported-tools.jpeg +0 -0
  30. package/bin/lacy +1028 -0
  31. package/docs/ADDING-BACKENDS.md +124 -0
  32. package/docs/DEVTO-ARTICLE.md +94 -0
  33. package/docs/DOCS.md +68 -0
  34. package/docs/GROWTH-STRATEGY.md +119 -0
  35. package/docs/HN-RESPONSES.md +122 -0
  36. package/docs/LAUNCH-COPY-FINAL.md +105 -0
  37. package/docs/MARKETING.md +411 -0
  38. package/docs/NATURAL_LANGUAGE_DETECTION.md +204 -0
  39. package/docs/UGC_VIDEO_SCRIPT.md +114 -0
  40. package/docs/articles/devto-how-i-made-my-terminal-understand-english.md +117 -0
  41. package/docs/demo-color-transition.gif +0 -0
  42. package/docs/demo-full.gif +0 -0
  43. package/docs/demo-indicator.gif +0 -0
  44. package/docs/launch-thread-may6.sh +158 -0
  45. package/docs/videos/README.md +189 -0
  46. package/docs/videos/generate_frames.py +510 -0
  47. package/docs/videos/generate_frames_v2.py +729 -0
  48. package/docs/videos/generate_short.py +328 -0
  49. package/docs/videos/generate_short_v2.py +526 -0
  50. package/docs/videos/lacy-shell-demo-v2.mp4 +0 -0
  51. package/docs/videos/lacy-shell-demo.mp4 +0 -0
  52. package/docs/videos/lacy-shell-short-v2.mp4 +0 -0
  53. package/docs/videos/lacy-shell-short.mp4 +0 -0
  54. package/install.sh +1009 -0
  55. package/lacy.plugin.bash +75 -0
  56. package/lacy.plugin.fish +43 -0
  57. package/lacy.plugin.zsh +65 -0
  58. package/lib/animations.zsh +3 -0
  59. package/lib/bash/completions.bash +40 -0
  60. package/lib/bash/execute.bash +233 -0
  61. package/lib/bash/init.bash +40 -0
  62. package/lib/bash/keybindings.bash +134 -0
  63. package/lib/bash/prompt.bash +85 -0
  64. package/lib/commands/info.sh +25 -0
  65. package/lib/config.zsh +3 -0
  66. package/lib/constants.zsh +3 -0
  67. package/lib/core/animations.sh +271 -0
  68. package/lib/core/commands.sh +297 -0
  69. package/lib/core/config.sh +340 -0
  70. package/lib/core/constants.sh +366 -0
  71. package/lib/core/context.sh +260 -0
  72. package/lib/core/detection.sh +417 -0
  73. package/lib/core/mcp.sh +741 -0
  74. package/lib/core/modes.sh +123 -0
  75. package/lib/core/preheat.sh +496 -0
  76. package/lib/core/spinner.sh +174 -0
  77. package/lib/core/telemetry.sh +99 -0
  78. package/lib/detection.zsh +3 -0
  79. package/lib/execute.zsh +3 -0
  80. package/lib/fish/config.fish +66 -0
  81. package/lib/fish/detection.fish +90 -0
  82. package/lib/fish/execute.fish +105 -0
  83. package/lib/fish/keybindings.fish +42 -0
  84. package/lib/fish/prompt.fish +30 -0
  85. package/lib/keybindings.zsh +3 -0
  86. package/lib/mcp.zsh +3 -0
  87. package/lib/modes.zsh +3 -0
  88. package/lib/preheat.zsh +3 -0
  89. package/lib/prompt.zsh +3 -0
  90. package/lib/spinner.zsh +3 -0
  91. package/lib/zsh/completions.zsh +60 -0
  92. package/lib/zsh/execute.zsh +294 -0
  93. package/lib/zsh/init.zsh +26 -0
  94. package/lib/zsh/keybindings.zsh +551 -0
  95. package/lib/zsh/prompt.zsh +90 -0
  96. package/package.json +42 -27
  97. package/packages/lacy/README.md +61 -0
  98. package/packages/lacy/commands/info.sh +25 -0
  99. package/{index.mjs → packages/lacy/index.mjs} +247 -20
  100. package/packages/lacy/package-lock.json +71 -0
  101. package/packages/lacy/package.json +42 -0
  102. package/script/release.ts +487 -0
  103. package/squirrel.toml +36 -0
  104. package/tests/test_bash.bash +163 -0
  105. package/tests/test_core.sh +607 -0
  106. package/tests/test_gemini.sh +119 -0
  107. package/tests/test_gemini_mcp.sh +126 -0
  108. package/tests/test_preheat_server.zsh +446 -0
  109. package/uninstall.sh +52 -0
@@ -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
@@ -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
@@ -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,3 @@
1
+ #!/usr/bin/env zsh
2
+ # Backward-compat wrapper — sources from new location
3
+ source "${0:A:h}/core/animations.sh"
@@ -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
+ }