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,411 @@
1
+ # Lacy Shell — Marketing Launch Kit
2
+
3
+ All launch copy is ready to submit. Demo GIFs are recorded and hosted.
4
+
5
+ **GIF URLs (raw GitHub):**
6
+ - Full demo: `https://raw.githubusercontent.com/lacymorrow/lacy/main/docs/demo-full.gif`
7
+ - Indicator: `https://raw.githubusercontent.com/lacymorrow/lacy/main/docs/demo-indicator.gif`
8
+ - Color transition: `https://raw.githubusercontent.com/lacymorrow/lacy/main/docs/demo-color-transition.gif`
9
+
10
+ ---
11
+
12
+ ## 1. Show HN Post
13
+
14
+ **Best time:** Tuesday or Wednesday, 9-10am ET
15
+
16
+ **Title:**
17
+ ```
18
+ Show HN: Lacy Shell – Talk to your terminal. Commands run, questions go to AI
19
+ ```
20
+
21
+ **Body:**
22
+ ```
23
+ Hi HN,
24
+
25
+ I built Lacy, a ZSH/Bash plugin that figures out whether you're typing a command or asking a question, then sends it to the right place. Commands run in your shell. Questions go to your AI agent. You don't type a prefix or hit a hotkey. You just type.
26
+
27
+ There's a color indicator next to your prompt that changes as you type — green means it'll run in the shell, magenta means it's headed to AI. The first word gets syntax-highlighted too. Updates every keystroke.
28
+
29
+ How it decides:
30
+
31
+ - `ls -la` → Shell (valid command, green)
32
+ - `what files are here` → AI (natural language, magenta)
33
+ - `do we have auth?` → AI (shell reserved words like "do", "in", "then" are never standalone commands)
34
+ - `kill the process on 3000` → Shell first, then AI (valid command fails with NL patterns, silent reroute)
35
+
36
+ No AI call to classify your input. It's pure lexical analysis — checks command validity, word counts, article/pronoun markers, and known error patterns. Sub-millisecond.
37
+
38
+ Works with whatever AI CLI you already have: Claude Code, Gemini CLI, OpenCode, Codex, or Lash (my OpenCode fork). Lacy doesn't replace any of them. It just makes them easier to reach.
39
+
40
+ Install:
41
+
42
+ curl -fsSL https://lacy.sh/install | bash
43
+
44
+ Or: `brew install lacymorrow/tap/lacy` | `npx lacy`
45
+
46
+ macOS, Linux, WSL. ZSH and Bash 4+.
47
+
48
+ Site: https://lacy.sh
49
+ Source: https://github.com/lacymorrow/lacy
50
+ ```
51
+
52
+ ---
53
+
54
+ ## 2. Twitter/X Launch Thread
55
+
56
+ **Best time:** Same day as HN, 1-2 hours after
57
+
58
+ **Tweet 1 (Hook):**
59
+ ```
60
+ I made my terminal understand English.
61
+
62
+ Type a command → it runs in your shell.
63
+ Type a question → it goes to your AI agent.
64
+
65
+ No prefix. No hotkey. Just type.
66
+
67
+ It's called Lacy Shell. Free and open source.
68
+
69
+ https://raw.githubusercontent.com/lacymorrow/lacy/main/docs/demo-full.gif
70
+ ```
71
+
72
+ **Tweet 2 (Problem):**
73
+ ```
74
+ The problem: every time you need AI help, you leave your terminal.
75
+
76
+ Copy output. Switch to Claude/ChatGPT. Paste. Wait. Copy answer. Switch back. Paste.
77
+
78
+ I was doing that 20+ times a day. So I fixed it.
79
+ ```
80
+
81
+ **Tweet 3 (How it works):**
82
+ ```
83
+ How it works:
84
+
85
+ A color indicator next to your prompt updates as you type:
86
+
87
+ 🟢 Green = shell command (runs normally)
88
+ 🟣 Magenta = natural language (goes to AI)
89
+
90
+ No AI call to classify. Pure lexical analysis. Sub-millisecond.
91
+
92
+ If a command fails with NL patterns, it silently reroutes to AI.
93
+ ```
94
+
95
+ **Tweet 4 (Tool agnostic):**
96
+ ```
97
+ Lacy works with whatever AI tool you already have.
98
+
99
+ - Claude Code
100
+ - Gemini CLI
101
+ - OpenCode
102
+ - Codex CLI
103
+ - Lash
104
+ - Any custom command
105
+
106
+ It auto-detects what's installed. You don't configure anything.
107
+ ```
108
+
109
+ **Tweet 5 (CTA):**
110
+ ```
111
+ One line to install:
112
+
113
+ curl -fsSL https://lacy.sh/install | bash
114
+
115
+ Also: brew install lacymorrow/tap/lacy
116
+
117
+ ZSH + Bash 4+. macOS, Linux, WSL.
118
+
119
+ github.com/lacymorrow/lacy
120
+ lacy.sh
121
+ ```
122
+
123
+ **Tags:** @AnthropicAI @GoogleDeepMind
124
+
125
+ ---
126
+
127
+ ## 3. Reddit Posts
128
+
129
+ ### r/commandline
130
+
131
+ **Title:**
132
+ ```
133
+ I built a ZSH/Bash plugin that figures out if you're typing a command or talking to AI
134
+ ```
135
+
136
+ **Body:**
137
+ ```
138
+ I kept doing the same thing over and over: run a command, realize I need AI help, alt-tab to Claude, paste context, get answer, alt-tab back. So I built a plugin that just... does this for me.
139
+
140
+ Lacy is a shell plugin that watches what you type and routes it. There's a color indicator next to your prompt that updates as you type. Green means the shell will run it, magenta means it's headed to your AI agent. You see what's going to happen before you hit enter.
141
+
142
+ Examples:
143
+ - `git status` → runs in your shell (green)
144
+ - `what changed in the last commit` → goes to AI (magenta)
145
+ - `do we have a way to deploy?` → AI (reserved words like `do` and `in` never appear as standalone commands)
146
+ - `make sure tests pass` → shell tries it first, it fails, then silently reroutes to AI
147
+
148
+ If something fails and looks like it was natural language, Lacy shows ghost text on the next prompt suggesting you retry through the agent. Right arrow or tab accepts it. Coexists fine with zsh-autosuggestions.
149
+
150
+ No AI call to classify your input. It checks `command -v`, word patterns, and known error messages. All local, sub-millisecond.
151
+
152
+ Works with Claude Code, Gemini CLI, OpenCode, Codex, Lash, or a custom command you set. It checks what's installed on first run.
153
+
154
+ curl -fsSL https://lacy.sh/install | bash
155
+
156
+ Also `brew install lacymorrow/tap/lacy` or `npx lacy`.
157
+
158
+ ZSH and Bash 4+ on macOS, Linux, WSL. MIT licensed. I've been using it daily for a while, currently on v1.8.11.
159
+
160
+ https://github.com/lacymorrow/lacy
161
+ ```
162
+
163
+ ### r/zsh
164
+
165
+ **Title:**
166
+ ```
167
+ ZSH plugin that routes input to shell or AI agent, with real-time indicator and ghost text
168
+ ```
169
+
170
+ **Body:**
171
+ ```
172
+ I wrote a ZSH plugin (works in Bash 4+ too) that classifies your input and routes it to either the shell or an AI agent. The ZSH-specific bits ended up being the most interesting part, so figured I'd share here.
173
+
174
+ What it does:
175
+
176
+ - Color indicator left of your prompt updates as you type: green for shell commands, magenta for AI queries
177
+ - First word gets highlighted via `region_highlight` with `memo=lacy` tags, updates on `zle-line-pre-redraw`
178
+ - Mode badge in `RPS1` (SHELL / AGENT / AUTO)
179
+ - Ctrl+Space toggles modes via a custom `zle` widget
180
+ - Custom accept-line widget for routing
181
+ - Ghost text via `POSTDISPLAY`. When a reroute candidate fails, the next empty prompt shows a suggestion to retry through the agent. Right arrow or tab accepts.
182
+
183
+ Classification is all local, no network calls. Checks `command -v`, word counts, article/pronoun markers, reserved words. Sub-millisecond.
184
+
185
+ How auto mode works:
186
+ 1. ~150 conversational words ("explain", "why", "thanks") → always AI
187
+ 2. Shell reserved words (`do`, `then`, `in`, `fi`) → AI. They pass `command -v` but nobody types `do` as a standalone command.
188
+ 3. First word is a valid command → shell
189
+ 4. Single word, not a command → shell (probably a typo, let it error)
190
+ 5. Multiple words, first word isn't a command → AI
191
+ 6. Valid command + natural language args, command fails → shell first, reroute to AI on failure
192
+
193
+ The `zsh-autosuggestions` coexistence was the trickiest part. Lacy uses `memo=lacy` tags on `region_highlight` entries so it only removes its own highlights on redraw. For `POSTDISPLAY`, Lacy calls `_zsh_autosuggest_clear` before writing ghost text, and autosuggestions picks back up once the user starts typing. Right arrow and tab fall back to `zle forward-char` (not `.forward-char`) so autosuggestions' widget wrappers still fire.
194
+
195
+ Works with Claude Code, Gemini CLI, OpenCode, Codex, or Lash.
196
+
197
+ `curl -fsSL https://lacy.sh/install | bash`
198
+
199
+ https://github.com/lacymorrow/lacy
200
+ ```
201
+
202
+ ---
203
+
204
+ ## 4. Dev.to Article
205
+
206
+ **Title:**
207
+ ```
208
+ How I Made My Terminal Understand English
209
+ ```
210
+
211
+ **Tags:** `#terminal #ai #devtools #opensource`
212
+
213
+ **Body:**
214
+
215
+ ```markdown
216
+ Every time I need AI help while coding, I do the same thing:
217
+
218
+ 1. Copy terminal output
219
+ 2. Switch to Claude/ChatGPT
220
+ 3. Paste and ask my question
221
+ 4. Wait for the response
222
+ 5. Copy the answer
223
+ 6. Switch back to terminal
224
+ 7. Paste and run
225
+
226
+ That loop happens 20+ times a day. It's death by a thousand context switches.
227
+
228
+ So I built [Lacy Shell](https://lacy.sh) — a ZSH/Bash plugin that detects whether you're typing a command or natural language and routes accordingly. Commands execute in your shell. Questions go to your AI agent. No prefix, no hotkey, no new terminal.
229
+
230
+ ## The Real-Time Indicator
231
+
232
+ As you type, a colored indicator shows what will happen when you press enter:
233
+
234
+ - **Green** → shell command (will execute normally)
235
+ - **Magenta** → natural language (will go to AI agent)
236
+
237
+ The first word also gets syntax-highlighted in real-time. It updates on every keystroke.
238
+
239
+ ## How Detection Works
240
+
241
+ The interesting part: Lacy doesn't use AI to classify your input. It's pure lexical analysis:
242
+
243
+ 1. **Agent words** — ~150 common conversational words like "explain", "why", "thanks", "perfect" always route to AI
244
+ 2. **Shell reserved words** — `do`, `then`, `in`, `fi` pass `command -v` but are never standalone commands. "Do we have auth?" is natural language, not a `do` loop.
245
+ 3. **Command validity** — if the first word is a valid command, it goes to shell
246
+ 4. **Word count heuristic** — single non-command words go to shell (typos). Multiple words starting with a non-command go to AI.
247
+ 5. **Post-execution reroute** — if a valid command fails with natural language patterns (3+ bare words, articles/pronouns), it silently reroutes to AI
248
+
249
+ This makes it sub-millisecond. No network call, no API key needed for classification.
250
+
251
+ ## Tool Agnostic
252
+
253
+ Lacy doesn't replace your AI tool — it makes it accessible:
254
+
255
+ | Tool | How Lacy calls it |
256
+ |------|------------------|
257
+ | Claude Code | `claude -p "query"` |
258
+ | Gemini CLI | `gemini --resume -p "query"` |
259
+ | OpenCode | `opencode run -c "query"` |
260
+ | Codex | `codex exec resume --last "query"` |
261
+ | Lash | `lash run -c "query"` |
262
+
263
+ It auto-detects whatever you have installed. Or set a custom command.
264
+
265
+ ## Examples
266
+
267
+ | You type | Routes to | Why |
268
+ |----------|-----------|-----|
269
+ | `ls -la` | Shell | Valid command |
270
+ | `what files are here` | AI | Natural language |
271
+ | `git status` | Shell | Valid command |
272
+ | `do we have auth?` | AI | Reserved word "do" |
273
+ | `fix the bug` | AI | Multi-word, not a command |
274
+ | `kill the process on 3000` | Shell → AI | Valid command fails with NL patterns |
275
+
276
+ ## Install
277
+
278
+ One line:
279
+
280
+ ```bash
281
+ curl -fsSL https://lacy.sh/install | bash
282
+ ```
283
+
284
+ Also: `brew install lacymorrow/tap/lacy` or `npx lacy`
285
+
286
+ Works on macOS, Linux, WSL. ZSH and Bash 4+. MIT licensed.
287
+
288
+ [GitHub](https://github.com/lacymorrow/lacy) | [Website](https://lacy.sh)
289
+ ```
290
+
291
+ ---
292
+
293
+ ## 5. Awesome List Submissions
294
+
295
+ Submit PRs to these repos (after launch posts get traction):
296
+
297
+ | List | Repo | Category |
298
+ |------|------|----------|
299
+ | awesome-zsh-plugins | unixorn/awesome-zsh-plugins | Plugins / AI |
300
+ | awesome-cli-apps | agarrharr/awesome-cli-apps | Productivity / AI |
301
+ | awesome-shell | alebcay/awesome-shell | AI / Productivity |
302
+ | awesome-ai-tools | mahseema/awesome-ai-tools | Developer Tools |
303
+ | terminals-are-sexy | k4m4/terminals-are-sexy | Shell Plugins |
304
+
305
+ **PR description template:**
306
+ ```
307
+ Add Lacy Shell — a ZSH/Bash plugin that auto-routes natural language to AI agents (Claude Code, Gemini, OpenCode, Codex). Real-time color indicator, sub-millisecond lexical detection.
308
+ ```
309
+
310
+ ---
311
+
312
+ ## 6. One-Liner Descriptions (by context)
313
+
314
+ **GitHub description (140 chars):**
315
+ ```
316
+ Talk to your shell. Commands run, questions go to AI. Real-time indicator. Works with Claude, Gemini, OpenCode, Codex.
317
+ ```
318
+
319
+ **npm description:**
320
+ ```
321
+ Talk to your terminal — AI agent routing for your shell
322
+ ```
323
+
324
+ **Tweet-length (280 chars):**
325
+ ```
326
+ Lacy Shell: type commands or natural language in your terminal. Commands run in your shell. Questions go to AI. Real-time color indicator shows the routing before you press enter. Works with Claude Code, Gemini, OpenCode, Codex. One line install: curl -fsSL https://lacy.sh/install | bash
327
+ ```
328
+
329
+ **Elevator pitch (30 seconds):**
330
+ ```
331
+ Lacy is a shell plugin that lets you talk to your terminal. Type a command — it runs. Type a question — it goes to your AI agent. A real-time color indicator shows which will happen before you press enter. No prefix, no hotkey, no new terminal. It works with whatever AI CLI you already use. One line to install, MIT licensed.
332
+ ```
333
+
334
+ ---
335
+
336
+ ## 7. Demo GIF Recording Script
337
+
338
+ Record with [vhs](https://github.com/charmbracelet/vhs), [asciinema](https://asciinema.org/), or Screen Studio.
339
+
340
+ ### GIF A: "The Indicator" (5 seconds)
341
+
342
+ ```
343
+ 1. Terminal open, clean prompt
344
+ 2. Type `ls -la` — green indicator appears
345
+ 3. Clear line
346
+ 4. Type `what files are here` — magenta indicator appears
347
+ 5. Hold for 1 second
348
+ ```
349
+
350
+ ### GIF B: "Full Demo" (10 seconds)
351
+
352
+ ```
353
+ 1. Type `ls -la` — green indicator → press enter → output
354
+ 2. Type `what files are here` — magenta indicator → press enter → AI responds
355
+ 3. Hold for 2 seconds showing AI output
356
+ ```
357
+
358
+ ### GIF C: "Color Transition" (5 seconds)
359
+
360
+ ```
361
+ 1. Type `g` — green (git?)
362
+ 2. Type `gi` — green
363
+ 3. Type `git` — green
364
+ 4. Backspace all
365
+ 5. Type `w` — neutral
366
+ 6. Type `wh` — neutral
367
+ 7. Type `what` — magenta (agent word)
368
+ 8. Hold 1 second
369
+ ```
370
+
371
+ ### GIF Specs
372
+ - **Size:** 800x500px (optimize < 5MB for GitHub)
373
+ - **Terminal:** Dark theme, font 16pt+
374
+ - **Colors:** Ensure green (34) and magenta (200) are visible
375
+ - **FPS:** 10-15 (keeps file size down)
376
+
377
+ ---
378
+
379
+ ## 8. Launch Day Checklist
380
+
381
+ ### Pre-launch (day before)
382
+ - [x] Demo GIF recorded and hosted (3 variants in docs/)
383
+ - [x] README updated with GIF at top + badges + "Why Lacy?" section
384
+ - [x] Analytics on lacy.sh (Plausible/Umami) — LAC-43 done
385
+ - [x] All copy reviewed and links tested
386
+ - [x] GitHub topics set (14 topics)
387
+ - [x] Demo videos recorded (docs/videos/)
388
+
389
+ ### Launch Day (Tuesday/Wednesday)
390
+ - [ ] 9:00am ET — Post Show HN
391
+ - [ ] 10:30am ET — Post Twitter thread (after HN settles)
392
+ - [ ] Monitor HN comments — respond to every question within 30 min
393
+ - [ ] Monitor Twitter replies
394
+
395
+ ### Day 2
396
+ - [ ] Post r/commandline
397
+ - [ ] Post r/zsh
398
+ - [ ] Respond to all HN/Twitter engagement from day 1
399
+
400
+ ### Day 3-4
401
+ - [ ] Publish Dev.to article
402
+ - [ ] Cross-post to Hashnode
403
+ - [ ] Submit first awesome-list PR
404
+
405
+ ### Week 2
406
+ - [x] Build SEO comparison pages on lacy.sh (5 pages: vs Warp, ShellGPT, GitHub Copilot CLI, AI Shell, Amazon Q)
407
+ - [ ] Submit remaining awesome-list PRs
408
+ - [ ] Begin Product Hunt prep
409
+
410
+ ### Ongoing
411
+ - See `docs/GROWTH-STRATEGY.md` for the full sustained growth plan
@@ -0,0 +1,204 @@
1
+ ## Shell natural language detection
2
+
3
+ Route natural language input away from the shell and toward the agent
4
+
5
+ ---
6
+
7
+ ### Summary
8
+
9
+ Users sometimes type natural language in shell mode where the first word happens to be a valid command or shell keyword. Two complementary layers catch this: a pre-execution filter for shell reserved words that are never valid standalone commands, and a post-execution heuristic that detects natural language after a real command fails. Additionally, common shell syntax patterns (like inline environment variable assignments `VAR=value command`) are recognized before the `command -v` check to avoid false agent routing.
10
+
11
+ This spec is a shared reference for both lash (opencode) and lacyshell. The algorithm is intentionally simple — pure string matching with no dependencies.
12
+
13
+ ---
14
+
15
+ ### Layer 1: Filter reserved words before execution
16
+
17
+ Shell reserved words like `do`, `done`, `then`, `else` are recognized by `command -v` (exit 0) but are never valid as standalone invocations. They only make sense inside compound constructs (`if/then/fi`, `for/do/done`, etc.).
18
+
19
+ When the first token of user input is a reserved word, skip the `command -v` check and route directly to the agent.
20
+
21
+ Complete reserved word list:
22
+
23
+ ```
24
+ do done then else elif fi esac in select function coproc { } ! [[
25
+ ```
26
+
27
+ `if`, `for`, `while`, `until`, `case`, and `time` are excluded because they are commonly used as real command prefixes or have standalone uses. Those are handled by Layer 2.
28
+
29
+ Examples caught by this layer:
30
+
31
+ - `do We already have an easy way to uninstall lacy?`
32
+ - `in the codebase where is the auth module?`
33
+ - `then what should I do next?`
34
+
35
+ ---
36
+
37
+ ### Inline environment variable assignments
38
+
39
+ Shell syntax like `VAR=value command args` prepends environment variables to a command. The first token (`VAR=value`) is not a command and fails `command -v`, but the input is clearly shell syntax, not natural language.
40
+
41
+ When the first token contains `=`, skip past all `VAR=value` tokens to find the actual command. If that command passes `command -v`, route to shell. If no valid command follows (or no tokens remain), fall through to the normal multi-word heuristic.
42
+
43
+ Examples:
44
+
45
+ - `RUST_LOG=debug cargo run` → `cargo` is valid → shell
46
+ - `FOO=bar BAZ=qux node index.js` → skip both assignments, `node` is valid → shell
47
+ - `CC=gcc make -j4` → `make` is valid → shell
48
+ - `FOO=bar unknown_thing here` → `unknown_thing` not valid → falls through to agent (multi-word, non-command first word)
49
+
50
+ ---
51
+
52
+ ### Layer 2: Detect natural language after a failed command
53
+
54
+ For real commands that pass `command -v` (like `find`, `make`, `git`, `while`, `test`), the user may still be typing natural language that starts with a valid command name. After shell execution, if the command exits non-zero, analyze the output against two criteria.
55
+
56
+ Both criteria must be satisfied.
57
+
58
+ ---
59
+
60
+ ### Match error patterns (criterion A)
61
+
62
+ The shell output must contain at least one of these error strings:
63
+
64
+ ```
65
+ parse error
66
+ syntax error
67
+ unexpected token
68
+ unexpected end of file
69
+ command not found
70
+ no such file or directory
71
+ invalid option
72
+ unrecognized option
73
+ illegal option
74
+ unknown option
75
+ no rule to make target
76
+ unknown primary or operator
77
+ missing argument to
78
+ invalid regular expression
79
+ is not a git command
80
+ unknown command
81
+ no such command
82
+ ```
83
+
84
+ ---
85
+
86
+ ### Check for natural language signal (criterion B)
87
+
88
+ One of:
89
+
90
+ 1. The **second word** of the input (lowercased) is in the natural language word list, OR
91
+ 2. The input has **5+ words** AND the error matches `parse error`, `syntax error`, or `unexpected token`
92
+
93
+ ---
94
+
95
+ ### Natural language word list
96
+
97
+ ~300 common English words that are unusual as shell arguments:
98
+
99
+ | Category | Words |
100
+ | ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
101
+ | Articles/determiners | a, an, the, this, that, these, those |
102
+ | Possessive determiners | my, our, your, his, her, its, their |
103
+ | Pronouns | i, we, you, it, they, me, us, him, her, them |
104
+ | Reflexive pronouns | myself, yourself, ourselves, themselves, itself |
105
+ | Prepositions | to, of, about, with, from, for, into, through, between, after, before, above, below, under, over, within, without, against, toward, towards, onto, upon, across, along, behind, beside, beyond, during, except, inside, outside, underneath, throughout, among, beneath |
106
+ | Conjunctions | and, but, or, so, because, since, although, though, unless, however, therefore, moreover, furthermore, nevertheless, meanwhile, otherwise |
107
+ | Verbs | is, are, was, were, be, been, have, has, had, can, could, would, should, will, shall, may, might, must, need, want, know, think, believe, understand, remember, forget, try, keep, let, seem, feel, look, mean, take, give, tell, ask, say, said, work, works, working, use, using, used, make, making, run, running, show, showing, create, creating, add, adding, change, changing, move, update, delete, remove, build, write, read, open, close, start, stop, find, check, set, get, put, call, come, goes, going, went, done, doing, being, having, getting, looking, trying, thinking, coming, taking, making, saying, seeing, knowing, wanting, needing |
108
+ | Adverbs | not, already, also, just, still, even, really, actually, probably, maybe, always, never, sometimes, often, usually, quickly, slowly, currently, recently, finally, completely, definitely, apparently, obviously, certainly, basically, essentially, primarily, particularly, especially, extremely, absolutely, entirely, simply, merely, nearly, virtually, totally, practically, likely, possibly, perhaps, hardly, barely, suddenly, immediately, eventually, originally, previously, honestly, frankly |
109
+ | Question words | how, what, when, where, why, who, which |
110
+ | Reactions/conversational | sure, please, sorry, okay, ok, right, wrong, correct, incorrect, true, false, good, bad, better, worse, best, worst, new, old, big, small, many, much, more, less, most, least, few, several, different, same, other, another, next, last, first, second, only, own, certain, possible, impossible, important, necessary, available, specific, general, common, whole, entire, both, either, neither, whether, whatever, whichever, wherever, whenever, whoever, however |
111
+ | Other | if, there, here, all, any, some, every, no, each, does, do, did, out, up, down, ahead, back, away, around, anyone, someone, everyone, anything, something, everything, nothing, nobody, nowhere, everywhere, somehow, anyway, anywhere, otherwise, instead, rather, quite, enough, such, too, very, well |
112
+ | Common nouns | bug, error, fix, file, files, code, issue, problem, question, answer, way, thing, part, place, point, end, side, area, line, word, number, name, type, kind, sort, case, fact, reason, result, example, idea, state, system, function, method, class, test, tests, command, option, message, output, input, value, data, list, string, version, module, package, project, server, client, database, config, repo, branch, commit, change, feature, release, request, response, page, section, table, field, key, entry |
113
+
114
+ ---
115
+
116
+ ### Trailing punctuation
117
+
118
+ Trailing punctuation (`?`, `!`, `.`, `,`, `;`, `:`) is stripped from the first word before matching against agent words and reserved words. This ensures that `why?` routes to the agent just like `why`, and `do?` is recognized as the reserved word `do`.
119
+
120
+ Punctuation is only stripped for word-list lookups — the original token (with punctuation) is still used for `command -v` checks, so real commands are unaffected.
121
+
122
+ ---
123
+
124
+ ### Minimum word count
125
+
126
+ Inputs with fewer than 2 words are always treated as real commands, even if they fail. Single-word inputs are handled by the agent words list (Layer 1) or treated as typos.
127
+
128
+ ---
129
+
130
+ ### Detection logic
131
+
132
+ ```
133
+ function detectNaturalLanguage(input, output, exitCode):
134
+ if exitCode == 0 or exitCode == null: return null
135
+ if wordCount(input) < 2: return null
136
+ if not matchesAnyErrorPattern(output): return null
137
+
138
+ secondWord = lowercase(words(input)[1])
139
+ if secondWord in NATURAL_LANGUAGE_WORDS:
140
+ return true
141
+
142
+ if wordCount(input) >= 4 and output matches /parse error|syntax error|unexpected token/:
143
+ return true
144
+
145
+ return null
146
+ ```
147
+
148
+ ---
149
+
150
+ ### Reroute behavior
151
+
152
+ When natural language is detected, silently reroute the input to the agent. No user-facing hint messages — the detection is transparent. The shell error output stays in conversation as context.
153
+
154
+ ---
155
+
156
+ ### Reference examples
157
+
158
+ | Input | First token | Layer | What happens |
159
+ | ---------------------------------------- | ----------- | ----- | --------------------------------------------------------------- |
160
+ | `do We already have a way to uninstall?` | `do` | 1 | Reserved word — route to agent |
161
+ | `in the codebase where is auth?` | `in` | 1 | Reserved word — route to agent |
162
+ | `find out how the auth system works` | `find` | 2 | Runs — "unknown primary or operator" + "out" is NL — reroute |
163
+ | `make sure the tests pass` | `make` | 2 | Runs — "No rule to make target 'sure'" + "sure" is NL — reroute |
164
+ | `git me the latest changes` | `git` | 2 | Runs — "'me' is not a git command" + "me" is NL — reroute |
165
+ | `while you are at it fix the tests` | `while` | 2 | Runs — "syntax error" + "you" is NL — reroute |
166
+ | `test the login flow works` | `test` | 2 | Runs — exits non-zero + "the" is NL — reroute |
167
+ | `go ahead and fix the tests` | `go` | 2 | Runs — "unknown command" + "ahead" is NL — reroute |
168
+ | `go for it and deploy` | `go` | 2 | Runs — "unknown command" + "for" is NL — reroute |
169
+ | `cargo ahead with the release` | `cargo` | 2 | Runs — "no such command" + "ahead" is NL — reroute |
170
+ | `RUST_LOG=debug cargo run` | `RUST_LOG=…`| — | Env var prefix — `cargo` is valid command — shell |
171
+ | `FOO=bar BAZ=qux node index.js` | `FOO=…` | — | Multiple env var prefixes — `node` is valid — shell |
172
+ | `why?` | `why?`→`why`| 1 | Trailing `?` stripped — matches agent word — route to agent |
173
+ | `ls -la` | `ls` | — | Succeeds — no detection |
174
+ | `grep -r foo` | `grep` | — | May fail but error doesn't match NL heuristic |
175
+
176
+ ---
177
+
178
+ ### Implementation in lash (opencode)
179
+
180
+ | File | Role |
181
+ | --------------------------------------- | ------------------------------------------------------------------------- |
182
+ | `plugin/shell-mode/command-check.ts` | `SHELL_RESERVED_WORDS` set, checked before `command -v` |
183
+ | `plugin/shell-mode/natural-language.ts` | `detectNaturalLanguage()` function |
184
+ | `src/session/prompt.ts` | Captures exit code, calls `detectNaturalLanguage`, reroutes to agent loop |
185
+ | `test/plugin/shell-mode.test.ts` | Tests for both layers |
186
+
187
+ ---
188
+
189
+ ### Implementation in lacyshell
190
+
191
+ | File | Role |
192
+ | ----------------------- | ----------------------------------------------------------------------------------------------- |
193
+ | `lib/core/constants.sh` | `LACY_SHELL_RESERVED_WORDS`, `LACY_NL_MARKERS`, `LACY_SHELL_ERROR_PATTERNS` arrays |
194
+ | `lib/core/detection.sh` | `lacy_shell_classify_input()` (Layer 1 check), `lacy_shell_detect_natural_language()` (Layer 2) |
195
+ | `lib/zsh/execute.zsh` | Reroute candidate logic in `lacy_shell_precmd()`, silent reroute |
196
+ | `lib/bash/execute.bash` | Reroute candidate logic in `lacy_shell_precmd_bash()`, silent reroute |
197
+ | `tests/test_core.sh` | Tests for both layers (Bash and ZSH) |
198
+
199
+ Both lists (reserved words and natural language words) must be kept in sync across implementations. The algorithm has no dependencies — it is pure string matching.
200
+
201
+ **Shared across both implementations:**
202
+
203
+ - `LACY_AGENT_WORDS` / `AGENT_WORDS` (~150 conversational words) — single-word inputs that always route to agent
204
+ - `lacy_shell_has_nl_markers()` — pre-execution reroute candidate flagging (counts bare words after first word, checks for NL markers)