@fmdzc/cli-ai 3.0.0

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/LICENSE ADDED
@@ -0,0 +1,15 @@
1
+ ISC License
2
+
3
+ Copyright (c) 2024 fmdz387
4
+
5
+ Permission to use, copy, modify, and/or distribute this software for any
6
+ purpose with or without fee is hereby granted, provided that the above
7
+ copyright notice and this permission notice appear in all copies.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
10
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
12
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
14
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15
+ PERFORMANCE OF THIS SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,169 @@
1
+ # CLI AI
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@fmdz387/cli-ai)](https://www.npmjs.com/package/@fmdz387/cli-ai)
4
+ [![License: ISC](https://img.shields.io/badge/License-ISC-blue.svg)](https://opensource.org/licenses/ISC)
5
+ [![Node.js](https://img.shields.io/badge/Node.js-20+-green.svg)](https://nodejs.org/)
6
+
7
+ **Natural language to shell commands. Powered by Claude AI.**
8
+
9
+ Describe what you want in plain English. Get the right command. Review, execute, or copy.
10
+
11
+ ![CLI AI Demo](assets/cli-ai.png)
12
+
13
+ ## Features
14
+
15
+ - **Natural language** - Just describe what you want to do
16
+ - **Cross-platform** - Windows (PowerShell, CMD, Git Bash), macOS, Linux
17
+ - **Shell-aware** - Commands tailored to your detected shell
18
+ - **Interactive** - Execute, copy, edit, or request alternatives
19
+ - **Context-aware** - Remembers your conversation history for smarter suggestions
20
+ - **Secure** - API keys stored in system keyring, never in plain text
21
+ - **Risk assessment** - Color-coded safety levels for every command
22
+
23
+ ## Quick Start
24
+
25
+ ```bash
26
+ # Install globally
27
+ npm install -g @fmdz387/cli-ai
28
+
29
+ # Run
30
+ s
31
+ ```
32
+
33
+ On first run, you'll be prompted for your [Anthropic API key](https://console.anthropic.com/settings/keys).
34
+
35
+ ## Usage
36
+
37
+ Type what you want in natural language:
38
+
39
+ ```
40
+ > find files larger than 100MB
41
+
42
+ $ find . -size +100M -type f
43
+ Risk: low
44
+
45
+ [1] Execute [2] Copy [3] Edit [4] Alternatives [5] Cancel
46
+ ```
47
+
48
+ ### Slash Commands
49
+
50
+ Type `/` to access commands:
51
+
52
+ | Command | Description |
53
+ |---------|-------------|
54
+ | `/config` | Open settings panel |
55
+ | `/help` | Show help and shortcuts |
56
+ | `/clear` | Clear command history |
57
+ | `/exit` | Exit application |
58
+
59
+ ### Keyboard Shortcuts
60
+
61
+ **Input Mode**
62
+ | Key | Action |
63
+ |-----|--------|
64
+ | `/` | Open command palette |
65
+ | `Enter` | Submit query |
66
+ | `O` | Toggle output expansion |
67
+ | `Ctrl+D` | Exit (when empty) |
68
+
69
+ **Command Proposal**
70
+ | Key | Action |
71
+ |-----|--------|
72
+ | `1` / `Enter` | Execute command |
73
+ | `2` | Copy to clipboard |
74
+ | `3` | Edit command |
75
+ | `4` | Show alternatives |
76
+ | `5` / `Esc` | Cancel |
77
+ | `?` | Explain command |
78
+
79
+ **Settings Panel**
80
+ | Key | Action |
81
+ |-----|--------|
82
+ | `Tab` | Next section |
83
+ | `Up/Down` | Navigate items |
84
+ | `Enter` | Toggle/Select |
85
+ | `Esc` | Close |
86
+
87
+ ## Settings
88
+
89
+ Access settings with `/config`:
90
+
91
+ ### API Key
92
+ - View masked key and storage method
93
+ - Change or remove API key
94
+
95
+ ### Model Selection
96
+ | Model | Description |
97
+ |-------|-------------|
98
+ | Claude Sonnet 4.5 | Fast and capable (default) |
99
+ | Claude Opus 4.5 | Most capable |
100
+ | Claude Haiku 4.5 | Fastest |
101
+
102
+ ### Options
103
+ | Setting | Description |
104
+ |---------|-------------|
105
+ | Context | Pass conversation history to AI for smarter suggestions |
106
+ | Show explanations | Display command explanations |
107
+ | Syntax highlighting | Colorize command output |
108
+ | Simple mode | Minimal UI mode |
109
+
110
+ ## Risk Assessment
111
+
112
+ | Level | Color | Meaning |
113
+ |-------|-------|---------|
114
+ | Low | Green | Safe, read-only commands |
115
+ | Medium | Yellow | Modifies files or system state |
116
+ | High | Red | Potentially destructive |
117
+
118
+ ## Security
119
+
120
+ ### API Key Storage
121
+
122
+ Your Anthropic API key is stored securely using industry-standard methods:
123
+
124
+ **Primary: System Keyring**
125
+
126
+ | Platform | Storage Backend |
127
+ |----------|-----------------|
128
+ | macOS | Keychain |
129
+ | Windows | Credential Manager |
130
+ | Linux | Secret Service API (GNOME Keyring, KWallet) |
131
+
132
+ The system keyring provides OS-level encryption and access control. Your API key is never stored in plain text or environment variables.
133
+
134
+ **Fallback: Encrypted File**
135
+
136
+ If the system keyring is unavailable, the key is stored in an encrypted file at `~/.cli_ai_assistant/`. The encryption key is derived from your machine's unique identifiers (hostname + username), making the encrypted file non-portable and machine-specific.
137
+
138
+ ### Key Management
139
+
140
+ - **View**: See masked key (`sk-ant-***...****`) and storage method in `/config`
141
+ - **Change**: Update your API key anytime through settings
142
+ - **Remove**: Delete your stored key completely
143
+
144
+ ## Requirements
145
+
146
+ - **Node.js 20+**
147
+ - **Build tools** for native modules:
148
+
149
+ | Platform | Command |
150
+ |----------|---------|
151
+ | Windows | `npm install -g windows-build-tools` (Admin) |
152
+ | macOS | `xcode-select --install` |
153
+ | Ubuntu/Debian | `sudo apt install build-essential libsecret-1-dev` |
154
+ | Fedora | `sudo dnf install gcc-c++ libsecret-devel` |
155
+ | Arch | `sudo pacman -S base-devel libsecret` |
156
+
157
+ ## Development
158
+
159
+ ```bash
160
+ git clone https://github.com/fmdz387/cli-ai.git
161
+ cd cli-ai
162
+ pnpm install
163
+ pnpm dev # Watch mode
164
+ pnpm build # Production build
165
+ ```
166
+
167
+ ## License
168
+
169
+ ISC
package/dist/cli.js ADDED
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+
3
+ // Suppress experimental warnings for JSON imports from dependencies
4
+ const originalEmit = process.emit;
5
+ process.emit = function (name, data) {
6
+ if (name === 'warning' && data && data.name === 'ExperimentalWarning') {
7
+ return false;
8
+ }
9
+ return originalEmit.apply(process, arguments);
10
+ };
11
+
12
+ // Import and run the main module
13
+ import('./index.js');
@@ -0,0 +1,2 @@
1
+
2
+ export { }
package/dist/index.js ADDED
@@ -0,0 +1,65 @@
1
+ import{render as jr}from"ink";var gt=[{id:"claude-sonnet-4-5",name:"Claude Sonnet 4.5",description:"Fast and capable"},{id:"claude-opus-4-5",name:"Claude Opus 4.5",description:"Most capable"},{id:"claude-haiku-4-5",name:"Claude Haiku 4.5",description:"Fastest"}];function xt(e,t){let o=e.toLowerCase(),n=t.toLowerCase();if(n.startsWith(o))return 100+(100-n.length);if(n.includes(o))return 50+(50-n.indexOf(o));let i=0,s=0;for(let a=0;a<n.length&&s<o.length;a++)n[a]===o[s]&&(i+=10,s++);return s===o.length?i:0}function Kn(e,t){let o=xt(e,t.name),n=xt(e,t.description),i=(t.aliases??[]).map(s=>xt(e,s));return Math.max(o,n,...i)}function qt(){let e=[],t=new Map;function o(){t.clear();for(let n of e){t.set(n.name.toLowerCase(),n);for(let i of n.aliases??[])t.set(i.toLowerCase(),n)}}return{get commands(){return e},get(n){return t.get(n.toLowerCase())},filter(n){return n?e.map(s=>({cmd:s,score:Kn(n,s)})).filter(({score:s})=>s>0).sort((s,a)=>a.score-s.score).map(({cmd:s})=>s):[...e]},register(n){t.get(n.name.toLowerCase())||(e.push(n),o())}}}var z=qt();var yt={name:"clear",description:"Clear conversation history",category:"session",aliases:["cls"],execute:()=>({type:"navigate",to:"clear"}),isAvailable:e=>e==="input"};var Ct={name:"config",description:"Open settings and configuration panel",category:"settings",aliases:["settings","preferences","prefs"],shortcut:"Ctrl+,",execute:()=>({type:"panel",panel:"config"}),isAvailable:e=>e==="input"};var ht={name:"exit",description:"Exit the application",category:"session",aliases:["quit","q"],execute:()=>({type:"exit"}),isAvailable:()=>!0};var Tt={name:"help",description:"Show help and keyboard shortcuts",category:"help",aliases:["h","?"],execute:()=>({type:"panel",panel:"help"}),isAvailable:()=>!0};z.register(Ct);z.register(Tt);z.register(yt);z.register(ht);var ee="3.0.0",Ce="CLI AI",Xt="cli-ai",Wt="anthropic",Qt=".cli_ai_assistant",_e="claude-sonnet-4-5",he={model:_e,maxHistoryEntries:5,maxOutputLines:10,maxAlternatives:3,contextEnabled:!0},Vt=500,zt=10,Jt=["rm -rf","sudo rm","chmod 777","mkfs","dd if=","> /dev/","format","del /f","rmdir /s","DROP TABLE","DELETE FROM","--no-preserve-root",":(){:|:&};:","| sh","| bash","curl | bash","wget | bash","eval","sudo su","passwd","chown -R","> /etc/","fdisk","wipefs","shred"],Zt=["rm ","mv ","cp ","sudo ","npm install","pnpm install","yarn add","pip install","brew install","apt install","apt-get install","pacman -S","chmod ","chown ","git push","git reset","git rebase","docker ","kubectl ","systemctl ","service ","kill ","pkill "],eo=["sudo","rm","git","npm","pnpm","yarn","docker","kubectl","pip","python","node","npx","cd","ls","cat","grep","find","mkdir","touch","mv","cp","echo","curl","wget","ssh","scp"],Je=500,Me={maxAttempts:3,baseDelayMs:1e3,maxDelayMs:5e3};import{Box as $,Text as J,useInput as Un}from"ink";import{useState as St}from"react";import{TextInput as Hn}from"@inkjs/ui";import{jsx as N,jsxs as le}from"react/jsx-runtime";function to({onComplete:e,onError:t,error:o}){let[n,i]=St("welcome"),[s,a]=St(""),[c,g]=St(null);Un((d,u)=>{n==="welcome"&&(u.return||d===" ")&&i("input")},{isActive:n==="welcome"});let m=d=>{let u=d.trim();if(!u){g("API key cannot be empty");return}if(!u.startsWith("sk-ant-")){g('Invalid key format. Anthropic API keys start with "sk-ant-"');return}if(u.length<20){g("API key seems too short");return}g(null),i("saving"),e(u)};if(n==="welcome")return le($,{flexDirection:"column",paddingY:1,children:[N($,{marginBottom:1,children:le(J,{bold:!0,color:"cyan",children:[Ce," v",ee]})}),N($,{marginBottom:1,children:N(J,{children:"Welcome! This tool translates natural language into shell commands."})}),N($,{marginBottom:1,children:le(J,{dimColor:!0,children:["To get started, you'll need an Anthropic API key.",`
2
+ `,"Get one at: ",N(J,{color:"blue",children:"https://console.anthropic.com/settings/keys"})]})}),N($,{children:N(J,{color:"green",children:"Press Enter to continue..."})})]});let y=o??c;return n==="input"?le($,{flexDirection:"column",paddingY:1,children:[N($,{marginBottom:1,children:N(J,{bold:!0,children:"Enter your Anthropic API key:"})}),y&&N($,{marginBottom:1,children:le(J,{color:"red",children:["\u26A0 ",y]})}),le($,{children:[N(J,{dimColor:!0,children:"> "}),N(Hn,{placeholder:"sk-ant-...",onChange:a,onSubmit:m})]}),N($,{marginTop:1,children:N(J,{dimColor:!0,children:"Your key will be stored securely in your system keyring."})})]}):n==="saving"?N($,{flexDirection:"column",paddingY:1,children:N($,{children:N(J,{color:"yellow",children:"\u23F3 Saving API key..."})})}):le($,{flexDirection:"column",paddingY:1,children:[N($,{children:N(J,{color:"green",children:"\u2713 API key saved successfully!"})}),N($,{marginTop:1,children:le(J,{children:["Starting ",Ce,"..."]})})]})}import{Box as Ze,Text as Et}from"ink";import{jsx as Ke,jsxs as oo}from"react/jsx-runtime";function no({lines:e,command:t}){return oo(Ze,{flexDirection:"column",marginY:1,children:[Ke(Ze,{marginBottom:1,children:oo(Et,{dimColor:!0,children:["$ ",t]})}),Ke(Ze,{flexDirection:"column",children:e.map((o,n)=>{let i=o.startsWith("[ERR]");return Ke(Et,{color:i?"red":void 0,children:i?o.slice(6):o},n)})}),Ke(Ze,{marginTop:1,children:Ke(Et,{color:"yellow",children:"\u23F3 Running..."})})]})}import{Box as Ae,Text as we}from"ink";import{Box as Fn,Text as ce}from"ink";import{Fragment as $n,jsx as Ie,jsxs as et}from"react/jsx-runtime";function vt({command:e,index:t,isSelected:o}){return et(Fn,{children:[Ie(ce,{color:o?"cyan":"gray",bold:o,children:o?"> ":" "}),et(ce,{color:o?"cyan":"blue",bold:o,children:["[",t+1,"]"]}),Ie(ce,{children:" "}),et(ce,{color:o?"white":"gray",bold:o,children:["/",e.name]}),Ie(ce,{children:" "}),Ie(ce,{dimColor:!o,children:e.description}),e.shortcut?et($n,{children:[Ie(ce,{children:" "}),Ie(ce,{dimColor:!0,color:"yellow",children:e.shortcut})]}):null]})}import{jsx as te,jsxs as ro}from"react/jsx-runtime";function bt({query:e,filteredCommands:t,selectedIndex:o,visible:n}){return n?ro(Ae,{flexDirection:"column",borderStyle:"round",borderColor:"blue",paddingX:1,marginTop:1,children:[ro(Ae,{children:[te(we,{color:"blue",bold:!0,children:"/"}),te(we,{color:"white",children:e}),te(we,{color:"cyan",children:"_"})]}),te(Ae,{marginY:0,children:te(we,{dimColor:!0,children:"\u2500".repeat(50)})}),t.length>0?te(Ae,{flexDirection:"column",children:t.slice(0,9).map((i,s)=>te(vt,{command:i,index:s,isSelected:s===o},i.name))}):te(Ae,{children:te(we,{dimColor:!0,children:"No matching commands"})}),te(Ae,{marginTop:1,children:te(we,{dimColor:!0,children:"[Enter] Select [1-9] Quick select [Esc] Close"})})]}):null}import{Box as ue,Text as Te}from"ink";import Us from"chalk";function Yn(e){let t=[],o=e.split(/(\s+)/);for(let n of o)if(n){if(/^\s+$/.test(n)){t.push({type:"default",value:n});continue}if(/^[|><&;]+$/.test(n)||n==="&&"||n==="||"){t.push({type:"pipe",value:n});continue}if(/^-{1,2}[\w-]+=?/.test(n)){t.push({type:"flag",value:n});continue}if(/^\$[\w{}]+/.test(n)){t.push({type:"variable",value:n});continue}if(/^["'].*["']$/.test(n)){t.push({type:"string",value:n});continue}if(n.includes("/")||n.includes("\\")){t.push({type:"path",value:n});continue}if(eo.includes(n.toLowerCase())){t.push({type:"keyword",value:n});continue}t.push({type:"default",value:n})}return t}function so(e){return Yn(e).map(o=>{switch(o.type){case"keyword":return{text:o.value,color:"cyan",bold:!0};case"flag":return{text:o.value,color:"yellow"};case"string":return{text:o.value,color:"green"};case"pipe":return{text:o.value,color:"magenta"};case"path":return{text:o.value,color:"blue"};case"variable":return{text:o.value,color:"cyan"};default:return{text:o.value}}})}import{jsx as X,jsxs as Ue}from"react/jsx-runtime";function io({command:e}){let t=so(e);return X(Te,{children:t.map((o,n)=>X(Te,{color:o.color,bold:o.bold,children:o.text},n))})}function ao({proposal:e,showExplanation:t=!1}){return Ue(ue,{flexDirection:"column",children:[X(ue,{borderStyle:"round",borderColor:"gray",paddingX:2,paddingY:1,children:X(io,{command:e.command})}),t&&e.explanation&&X(ue,{marginTop:1,paddingX:1,children:X(Te,{dimColor:!0,children:e.explanation})})]})}function Gn({command:e,index:t,selected:o=!1}){return Ue(ue,{children:[Ue(Te,{color:o?"cyan":"blue",bold:o,children:["[",t+1,"]"]}),X(Te,{children:" "}),X(io,{command:e})]})}function lo({proposals:e,selectedIndex:t=-1}){return Ue(ue,{flexDirection:"column",marginY:1,children:[X(ue,{marginBottom:1,children:X(Te,{bold:!0,children:"Alternative commands:"})}),e.map((o,n)=>X(ue,{marginBottom:1,children:X(Gn,{command:o.command,index:n,selected:n===t})},n)),X(ue,{marginTop:1,children:Ue(Te,{dimColor:!0,children:["Press 1-",e.length," to select, or [5] Cancel"]})})]})}import{Box as He,Text as oe}from"ink";import{Fragment as jn,jsx as se,jsxs as Se}from"react/jsx-runtime";function Pt({hasApiKey:e,storageMethod:t,storageDescription:o,isSecure:n,maskedKey:i,isSectionActive:s,focusedIndex:a}){return Se(He,{flexDirection:"column",children:[Se(He,{children:[se(oe,{dimColor:!0,children:"Status: "}),se(oe,{color:e?"green":"red",children:e?"Configured":"Not configured"}),e?Se(jn,{children:[se(oe,{dimColor:!0,children:" ("}),se(oe,{color:n?"green":"yellow",children:o}),se(oe,{dimColor:!0,children:")"})]}):null]}),i?Se(He,{children:[se(oe,{dimColor:!0,children:"Key: "}),se(oe,{color:"gray",children:i})]}):null,Se(He,{marginTop:1,children:[se(oe,{color:s&&a===0?"cyan":"gray",bold:s&&a===0,children:s&&a===0?"> ":" "}),Se(oe,{color:s&&a===0?"cyan":"blue",children:["[",e?"Change API Key":"Set API Key","]"]})]}),e?Se(He,{children:[se(oe,{color:s&&a===1?"cyan":"gray",bold:s&&a===1,children:s&&a===1?"> ":" "}),se(oe,{color:s&&a===1?"red":"gray",children:"[Remove API Key]"})]}):null]})}import{Box as ie,Text as Ee}from"ink";import{Box as tt,Text as It}from"ink";import{jsx as Fe,jsxs as co}from"react/jsx-runtime";function Re({title:e,isActive:t,children:o}){return co(tt,{flexDirection:"column",marginBottom:1,children:[co(tt,{children:[Fe(It,{color:t?"cyan":"white",bold:!0,children:e}),t?Fe(It,{color:"cyan",children:" *"}):null]}),Fe(tt,{children:Fe(It,{dimColor:!0,children:"\u2500".repeat(50)})}),Fe(tt,{flexDirection:"column",paddingLeft:0,children:o})]})}import{Box as uo,Text as $e}from"ink";import{Fragment as qn,jsx as Oe,jsxs as po}from"react/jsx-runtime";function At({options:e,selectedValue:t,focusedIndex:o,isSectionActive:n}){return Oe(uo,{flexDirection:"column",children:e.map((i,s)=>{let a=i.id===t,c=n&&s===o;return po(uo,{children:[Oe($e,{color:c?"cyan":"gray",bold:c,children:c?"> ":" "}),Oe($e,{color:a?"green":c?"white":"gray",children:a?"(*) ":"( ) "}),Oe($e,{color:c?"white":"gray",bold:a,children:i.name}),i.description?po(qn,{children:[Oe($e,{dimColor:!0,children:" - "}),Oe($e,{dimColor:!0,children:i.description})]}):null]},i.id)})})}import{Box as Xn,Text as ot}from"ink";import{jsx as nt,jsxs as Wn}from"react/jsx-runtime";function Be({label:e,value:t,isSelected:o}){return Wn(Xn,{children:[nt(ot,{color:o?"cyan":"gray",bold:o,children:o?"> ":" "}),nt(ot,{color:t?"green":"gray",children:t?"[x]":"[ ]"}),nt(ot,{children:" "}),nt(ot,{color:o?"white":"gray",children:e})]})}import{jsx as K,jsxs as pe}from"react/jsx-runtime";var mo=["api-key","model","toggles","about"];function wt({visible:e,activeSection:t,sectionItemIndex:o,config:n,hasApiKey:i,storageInfo:s,maskedKey:a,toggles:c}){if(!e)return null;let g=mo.indexOf(t);return pe(ie,{flexDirection:"column",borderStyle:"round",borderColor:"blue",paddingX:2,paddingY:1,children:[pe(ie,{justifyContent:"space-between",marginBottom:1,children:[K(ie,{children:K(Ee,{color:"cyan",bold:!0,children:"Settings"})}),K(ie,{children:K(Ee,{dimColor:!0,children:"[Esc] Close [Tab] Section [Enter] Select"})})]}),K(Re,{title:"API Key",isActive:t==="api-key",children:K(Pt,{hasApiKey:i,storageMethod:s.method,storageDescription:s.description,isSecure:s.secure,maskedKey:a,isSectionActive:t==="api-key",focusedIndex:o})}),K(Re,{title:"Model",isActive:t==="model",children:K(At,{options:gt,selectedValue:n.model,focusedIndex:o,isSectionActive:t==="model"})}),pe(Re,{title:"Options",isActive:t==="toggles",children:[K(Be,{label:"Context (pass history to AI)",value:c.contextEnabled,isSelected:t==="toggles"&&o===0}),K(Be,{label:"Show explanations",value:c.showExplanations,isSelected:t==="toggles"&&o===1}),K(Be,{label:"Syntax highlighting",value:c.syntaxHighlighting,isSelected:t==="toggles"&&o===2}),K(Be,{label:"Simple mode",value:c.simpleMode,isSelected:t==="toggles"&&o===3})]}),K(Re,{title:"About",isActive:t==="about",children:pe(ie,{flexDirection:"column",children:[pe(ie,{children:[K(Ee,{dimColor:!0,children:"Version: "}),pe(Ee,{children:["CLI AI v",ee]})]}),pe(ie,{children:[K(Ee,{dimColor:!0,children:"Storage: "}),K(Ee,{color:s.secure?"green":"yellow",children:s.description})]})]})}),K(ie,{marginTop:1,justifyContent:"center",children:mo.map((m,y)=>K(ie,{marginX:1,children:pe(Ee,{color:y===g?"cyan":"gray",children:[y===g?"[":" ",y+1,y===g?"]":" "]})},m))})]})}import{Box as D,Text as Y}from"ink";import{jsx as P,jsxs as W}from"react/jsx-runtime";var Qn=[{key:"/",description:"Open command palette"},{key:"Enter",description:"Submit query"},{key:"Ctrl+D",description:"Exit (when input is empty)"},{key:"O",description:"Toggle output expansion (when input is empty)"}],Vn=[{key:"1 / Enter",description:"Execute command"},{key:"2",description:"Copy to clipboard"},{key:"3",description:"Edit command"},{key:"4",description:"Show alternatives"},{key:"5 / Esc",description:"Cancel"},{key:"?",description:"Explain command"},{key:"O",description:"Toggle output"}],zn=[{key:"Up/Down",description:"Navigate commands"},{key:"Enter",description:"Select command"},{key:"Esc",description:"Close palette"},{key:"1-9",description:"Quick select"}],Jn=[{key:"Tab",description:"Next section"},{key:"Shift+Tab",description:"Previous section"},{key:"Up/Down",description:"Navigate items"},{key:"Enter/Space",description:"Toggle/Select"},{key:"Esc",description:"Close panel"}];function rt({title:e,shortcuts:t}){return W(D,{flexDirection:"column",marginBottom:1,children:[P(Y,{color:"yellow",bold:!0,children:e}),t.map(o=>W(D,{children:[P(D,{width:16,children:P(Y,{color:"cyan",children:o.key})}),P(Y,{dimColor:!0,children:o.description})]},o.key))]})}function Rt({visible:e}){return e?W(D,{flexDirection:"column",borderStyle:"round",borderColor:"blue",paddingX:2,paddingY:1,children:[W(D,{justifyContent:"space-between",marginBottom:1,children:[P(D,{children:W(Y,{color:"cyan",bold:!0,children:["Help - ",Ce," v",ee]})}),P(D,{children:P(Y,{dimColor:!0,children:"[Esc] Close"})})]}),P(D,{marginBottom:1,children:P(Y,{children:"Natural language to shell command translator. Describe what you want in plain English and get executable commands."})}),W(D,{children:[W(D,{flexDirection:"column",marginRight:4,children:[P(rt,{title:"Input Mode",shortcuts:Qn}),P(rt,{title:"Command Palette",shortcuts:zn})]}),W(D,{flexDirection:"column",children:[P(rt,{title:"Proposal Mode",shortcuts:Vn}),P(rt,{title:"Config Panel",shortcuts:Jn})]})]}),W(D,{flexDirection:"column",marginTop:1,children:[P(Y,{color:"yellow",bold:!0,children:"Available Commands"}),W(D,{children:[P(D,{width:16,children:P(Y,{color:"cyan",children:"/config"})}),P(Y,{dimColor:!0,children:"Open settings panel"})]}),W(D,{children:[P(D,{width:16,children:P(Y,{color:"cyan",children:"/help"})}),P(Y,{dimColor:!0,children:"Show this help"})]}),W(D,{children:[P(D,{width:16,children:P(Y,{color:"cyan",children:"/clear"})}),P(Y,{dimColor:!0,children:"Clear command history"})]}),W(D,{children:[P(D,{width:16,children:P(Y,{color:"cyan",children:"/exit"})}),P(Y,{dimColor:!0,children:"Exit application"})]})]})]}):null}import{Box as st,Text as Ge}from"ink";import{Text as Zn}from"ink";import Ye from"chalk";import{useMemo as fo}from"react";import{jsx as tr}from"react/jsx-runtime";var Ot=Ye.inverse(" ");function go({value:e,cursorOffset:t,placeholder:o="",isDisabled:n=!1}){let i=fo(()=>n?o?Ye.dim(o):"":o&&o.length>0?Ye.inverse(o[0])+Ye.dim(o.slice(1)):Ot,[n,o]),s=fo(()=>{if(n)return e;let a=0,c=e.length>0?"":Ot;for(let g of e)c+=a===t?Ye.inverse(g):g,a++;return e.length>0&&t===e.length&&(c+=Ot),c},[n,e,t]);return tr(Zn,{children:e.length>0?s:i})}var er={value:"",cursorOffset:0};function xo(e=""){return{value:e,cursorOffset:e.length}}function yo(e,t){switch(t.type){case"insert":return{value:e.value.slice(0,e.cursorOffset)+t.text+e.value.slice(e.cursorOffset),cursorOffset:e.cursorOffset+t.text.length};case"delete":{if(e.cursorOffset===0)return e;let o=e.cursorOffset-1;return{value:e.value.slice(0,o)+e.value.slice(o+1),cursorOffset:o}}case"move-left":return{...e,cursorOffset:Math.max(0,e.cursorOffset-1)};case"move-right":return{...e,cursorOffset:Math.min(e.value.length,e.cursorOffset+1)};case"clear":return er;case"set":return{value:t.value,cursorOffset:t.value.length};default:return e}}import{jsx as Ne,jsxs as it}from"react/jsx-runtime";function Co({textState:e,placeholder:t="Describe what you want to do...",disabled:o=!1,hasHistory:n=!1,visible:i=!0}){return i?o?it(st,{children:[Ne(Ge,{dimColor:!0,children:"> "}),Ne(Ge,{dimColor:!0,children:e.value||"..."})]}):it(st,{flexDirection:"column",children:[it(st,{children:[Ne(Ge,{color:"green",bold:!0,children:"> "}),Ne(go,{value:e.value,cursorOffset:e.cursorOffset,placeholder:t,isDisabled:o})]}),n&&Ne(st,{marginTop:1,children:it(Ge,{dimColor:!0,children:[Ne(Ge,{color:"blue",children:"[O]"})," Toggle output"]})})]}):null}import{Box as je,Text as ne}from"ink";import{jsx as de,jsxs as me}from"react/jsx-runtime";var or=[{key:"1",label:"Execute"},{key:"2",label:"Copy"},{key:"3",label:"Edit"},{key:"4",label:"Alternatives"},{key:"5",label:"Cancel"}];function ho({focusedIndex:e,showExplain:t=!0,visible:o=!0}){return o?me(je,{flexDirection:"column",marginTop:1,children:[de(je,{flexDirection:"row",gap:1,children:or.map((n,i)=>{let s=i===e;return me(je,{children:[me(ne,{color:s?"cyan":"blue",bold:s,inverse:s,children:["[",n.key,"]"]}),me(ne,{color:s?"cyan":void 0,bold:s,children:[" ",n.label]})]},n.key)})}),t&&de(je,{marginTop:1,children:me(ne,{dimColor:!0,children:[de(ne,{color:"blue",children:"[?]"})," Explain"," ",de(ne,{color:"blue",children:"[O]"})," Toggle output"," ",de(ne,{dimColor:!0,children:"| \u2190\u2192 Navigate, Enter Select"})]})})]}):null}function To({count:e,focusedIndex:t,visible:o=!0}){return o?de(je,{children:me(ne,{dimColor:!0,children:["Press ",me(ne,{color:"blue",children:["1-",e]})," or ",de(ne,{color:"blue",children:"\u2191\u2193"})," to select,"," ",de(ne,{color:"blue",children:"[5]"})," Cancel",t>=0&&me(ne,{color:"cyan",children:[" (focused: ",t+1,")"]})]})}):null}import{Box as Bt,Text as nr}from"ink";import{Spinner as rr}from"@inkjs/ui";import{jsx as Nt,jsxs as So}from"react/jsx-runtime";function Dt({query:e,label:t="Thinking..."}){return So(Bt,{flexDirection:"column",marginY:1,children:[Nt(Bt,{children:Nt(rr,{label:t})}),e&&Nt(Bt,{marginTop:1,children:So(nr,{dimColor:!0,children:["Query: ",e]})})]})}import{Box as Q,Text as H}from"ink";import{homedir as sr,userInfo as ir}from"os";import{jsx as U,jsxs as Z}from"react/jsx-runtime";var ar=[" \u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E "," \u2502 \u25B6 _ \u2502 "," \u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F "];function Eo({shell:e,cwd:t,model:o}){let n=ir().username||"user",i=t.replace(sr(),"~"),s=o||"claude-sonnet-4-5";return Z(Q,{flexDirection:"column",borderStyle:"round",borderColor:"cyan",paddingX:2,paddingY:1,marginBottom:1,children:[Z(Q,{marginBottom:1,children:[U(H,{color:"cyan",bold:!0,children:Ce}),Z(H,{dimColor:!0,children:[" v",ee]})]}),Z(Q,{children:[Z(Q,{flexDirection:"column",marginRight:4,children:[Z(Q,{marginBottom:1,children:[U(H,{children:"Welcome, "}),U(H,{color:"yellow",bold:!0,children:n}),U(H,{children:"!"})]}),U(Q,{flexDirection:"column",children:ar.map((a,c)=>U(H,{color:"cyan",children:a},c))}),Z(Q,{marginTop:1,flexDirection:"column",children:[Z(Q,{children:[U(H,{dimColor:!0,children:"Shell: "}),U(H,{color:"green",children:e})]}),Z(Q,{children:[U(H,{dimColor:!0,children:"Path: "}),U(H,{children:i})]})]})]}),Z(Q,{flexDirection:"column",borderStyle:"single",borderColor:"gray",borderLeft:!0,borderRight:!1,borderTop:!1,borderBottom:!1,paddingLeft:2,children:[U(Q,{marginBottom:1,children:U(H,{color:"yellow",bold:!0,children:"Quick Start"})}),Z(Q,{flexDirection:"column",children:[U(H,{dimColor:!0,children:"Describe what you want in natural language:"}),U(H,{color:"gray",italic:!0,children:' "list all files modified today"'}),U(H,{color:"gray",italic:!0,children:' "find large files over 100MB"'}),U(H,{color:"gray",italic:!0,children:' "show git status"'})]}),Z(Q,{marginTop:1,flexDirection:"column",children:[U(H,{dimColor:!0,children:"Model: "}),U(H,{color:"magenta",children:s.split("-").slice(0,2).join(" ")})]})]})]})]})}import{useCallback as pt,useRef as Mo,useState as Rr}from"react";import{readdirSync as lr,statSync as cr}from"fs";import{join as ur,relative as pr}from"path";var mr=new Set(["node_modules",".git",".next","dist","build","__pycache__",".venv","venv",".idea",".vscode","coverage",".cache",".npm",".yarn",".pnpm",".turbo",".nuxt",".output","target","vendor",".angular",".svelte-kit"]),dr=new Set([".DS_Store","Thumbs.db",".gitignore",".npmrc",".yarnrc","pnpm-lock.yaml","package-lock.json","yarn.lock"]),fr=3,gr=20;function vo(e,t=0){if(t>=fr)return[];try{let o=lr(e),n=[],i=0;for(let s of o){if(i>=gr){n.push({name:`... (${o.length-i} more)`,isDirectory:!1});break}if(s.startsWith(".")&&!s.startsWith(".env")||dr.has(s))continue;let a=ur(e,s);try{if(cr(a).isDirectory()){if(mr.has(s))continue;let g=vo(a,t+1);n.push({name:s,isDirectory:!0,children:g.length>0?g:void 0})}else n.push({name:s,isDirectory:!1});i++}catch{continue}}return n}catch{return[]}}function bo(e,t=""){let o=[];return e.forEach((n,i)=>{let s=i===e.length-1,a=s?"\u2514\u2500\u2500 ":"\u251C\u2500\u2500 ",c=s?" ":"\u2502 ";n.isDirectory?(o.push(`${t}${a}${n.name}/`),n.children&&n.children.length>0&&o.push(bo(n.children,`${t}${c}`))):o.push(`${t}${a}${n.name}`)}),o.join(`
3
+ `)}function Po(e){let t=vo(e);return t.length===0?"(empty or inaccessible directory)":`${pr(process.cwd(),e)||"."}/
4
+ ${bo(t)}`}function xr(e){let t=e.toLowerCase();for(let o of Jt)if(t.includes(o.toLowerCase()))return"high";for(let o of Zt)if(t.includes(o.toLowerCase()))return"medium";return"low"}function Lt(e,t){let o=xr(t),n={low:0,medium:1,high:2};return n[e]>=n[o]?e:o}import{Entry as yr}from"@napi-rs/keyring";import at from"conf";import{createHash as Cr}from"crypto";import{existsSync as Ao,unlinkSync as Io}from"fs";import{hostname as hr,userInfo as Tr}from"os";import{homedir as Sr}from"os";import{join as wo}from"path";var Er="cli-ai-v3-encryption-key";function vr(){let e=`${hr()}-${Tr().username}-cli-ai-v3-salt`;return Cr("sha256").update(e).digest("hex").slice(0,32)}var Xe=wo(Sr(),Qt),lt=wo(Xe,"config.json");function br(){if(!Ao(lt))return null;try{let e=new at({projectName:"cli-ai",cwd:Xe,encryptionKey:Er}),t=e.get("apiKey"),o=e.get("config");if(t||o)return{apiKey:t,config:o}}catch{}return null}function Pr(){let e=vr(),t=br();if(t){try{Io(lt)}catch{}let o=new at({projectName:"cli-ai",cwd:Xe,encryptionKey:e});return t.apiKey&&o.set("apiKey",t.apiKey),t.config&&o.set("config",t.config),o}try{return new at({projectName:"cli-ai",cwd:Xe,encryptionKey:e})}catch{try{Ao(lt)&&Io(lt)}catch{}return new at({projectName:"cli-ai",cwd:Xe,encryptionKey:e})}}var fe=Pr(),qe=null,kt=null;function ct(){if(kt===!1)return null;if(qe===null)try{qe=new yr(Xt,Wt),qe.getPassword(),kt=!0}catch{return kt=!1,qe=null,null}return qe}function ge(){let e=process.env.ANTHROPIC_API_KEY;if(e)return e;let t=ct();if(t)try{let o=t.getPassword();if(o)return o}catch{}return fe.get("apiKey")??null}function ut(e){try{let t=ct();if(t)try{return t.setPassword(e),fe.delete("apiKey"),{success:!0,data:void 0}}catch{}return fe.set("apiKey",e),{success:!0,data:void 0}}catch(t){return{success:!1,error:t instanceof Error?t:new Error(String(t))}}}function Ro(){try{let e=ct();if(e)try{e.deleteCredential()}catch{}return fe.delete("apiKey"),{success:!0,data:void 0}}catch(e){return{success:!1,error:e instanceof Error?e:new Error(String(e))}}}function _t(){let e=ge();return e!==null&&e.length>0}function De(){let e=fe.get("config")??{};return{...he,...e}}function We(e){let t=fe.get("config")??{};fe.set("config",{...t,...e})}function Oo(e){return e.startsWith("sk-ant-")&&e.length>20}function Bo(){if(process.env.ANTHROPIC_API_KEY)return{method:"env",secure:!1,description:"Environment variable (visible to other processes)"};let e=ct();if(e)try{if(e.getPassword())return{method:"keyring",secure:!0,description:"System keyring (OS-protected)"}}catch{}return fe.get("apiKey")?{method:"encrypted-file",secure:!1,description:"Encrypted file (machine-specific key)"}:{method:"none",secure:!1,description:"Not configured"}}import Mt from"@anthropic-ai/sdk";function No(e){return`You are a CLI command generator for ${e}.
5
+ You translate natural language requests into shell commands.
6
+
7
+ IMPORTANT: Output ONLY valid JSON, no markdown, no explanation text.
8
+ Format: { "command": "...", "risk": "low|medium|high" }
9
+
10
+ Risk levels:
11
+ - low: Safe reads, common commands (ls, cat, pwd, git status, etc.)
12
+ - medium: Writes files, installs packages, modifies state
13
+ - high: Destructive operations, sudo, system changes, recursive deletes
14
+
15
+ Rules:
16
+ 1. Generate ONLY the command, no explanations in the command itself
17
+ 2. Use appropriate flags for the target shell
18
+ 3. Prefer safe alternatives when possible
19
+ 4. For destructive operations, include safety flags (-i for interactive, etc.)
20
+ 5. Never include placeholder values - ask for specifics if needed`}function Ir(e,t){if(!e||e.length<=t)return e;let o=e.slice(0,t),n=o.lastIndexOf(`
21
+ `);return n>t*.5?o.slice(0,n)+`
22
+ ... (truncated)`:o+"... (truncated)"}function Do(e,t){let o=[];if(o.push(`Current directory: ${t.cwd}`),o.push(`
23
+ Directory structure:
24
+ ${t.directoryTree}`),t.history.length>0){o.push(`
25
+ Conversation context (recent queries and results):`);let n=t.history.slice(-zt);for(let i of n)if(o.push(`
26
+ Query: "${i.query}"`),o.push(`Command: ${i.command}`),i.exitCode!==void 0&&o.push(`Exit code: ${i.exitCode}`),i.output){let s=Ir(i.output,Vt);o.push(`Output:
27
+ ${s}`)}}return o.push(`
28
+ User request: ${e}`),o.join(`
29
+ `)}function Ar(e){let t=e.match(/\{[\s\S]*\}/);if(!t)throw new Error("No JSON found in AI response");let o=JSON.parse(t[0]);if(!o.command||typeof o.command!="string")throw new Error("Invalid response: missing command");let n=o.risk,i=n&&["low","medium","high"].includes(n)?n:"medium";return{command:o.command.trim(),risk:Lt(i,o.command)}}function wr(e){return new Promise(t=>setTimeout(t,e))}async function Lo(e,t){let o=ge();if(!o)return{success:!1,error:new Error("No API key configured")};let n=new Mt({apiKey:o}),i=process.env.AI_MODEL??_e;for(let s=0;s<Me.maxAttempts;s++)try{let c=(await n.messages.create({model:i,max_tokens:Je,system:No(t.shell),messages:[{role:"user",content:Do(e,t)}]})).content.find(m=>m.type==="text");if(!c||c.type!=="text")throw new Error("No text content in AI response");return{success:!0,data:Ar(c.text)}}catch(a){if(s===Me.maxAttempts-1)return{success:!1,error:a instanceof Error?a:new Error(String(a))};let g=Math.min(Me.baseDelayMs*Math.pow(2,s),Me.maxDelayMs);await wr(g)}return{success:!1,error:new Error("Unexpected error in AI request")}}async function ko(e,t,o,n=3){let i=ge();if(!i)return{success:!1,error:new Error("No API key configured")};let s=new Mt({apiKey:i}),a=process.env.AI_MODEL??_e,c=`${Do(e,t)}
30
+
31
+ Generate ${n} ALTERNATIVE commands (different approaches).
32
+ Exclude: ${o}
33
+
34
+ Output JSON array: [{ "command": "...", "risk": "low|medium|high" }, ...]`;try{let m=(await s.messages.create({model:a,max_tokens:Je*2,system:No(t.shell),messages:[{role:"user",content:c}]})).content.find(p=>p.type==="text");if(!m||m.type!=="text")throw new Error("No text content in AI response");let y=m.text.match(/\[[\s\S]*\]/);if(!y)throw new Error("No JSON array found in AI response");return{success:!0,data:JSON.parse(y[0]).filter(p=>p.command&&typeof p.command=="string").map(p=>{let r=p.risk,v=r&&["low","medium","high"].includes(r)?r:"medium";return{command:p.command.trim(),risk:Lt(v,p.command)}})}}catch(g){return{success:!1,error:g instanceof Error?g:new Error(String(g))}}}async function _o(e){let t=ge();if(!t)return{success:!1,error:new Error("No API key configured")};let o=new Mt({apiKey:t}),n=process.env.AI_MODEL??_e;try{let s=(await o.messages.create({model:n,max_tokens:Je,messages:[{role:"user",content:`Explain this command briefly (2-3 sentences max):
35
+ ${e}`}]})).content.find(a=>a.type==="text");if(!s||s.type!=="text")throw new Error("No text content in AI response");return{success:!0,data:s.text.trim()}}catch(i){return{success:!1,error:i instanceof Error?i:new Error(String(i))}}}function Kt(e,t=[]){let o=process.cwd();return{shell:e,cwd:o,platform:process.platform,directoryTree:Po(o),history:t}}function Ko({shell:e,history:t=[],contextEnabled:o=!0}){let[n,i]=Rr({isLoading:!1,error:null,lastProposal:null}),s=Mo(t);s.current=t;let a=Mo(o);a.current=o;let c=pt(async d=>{i(u=>({...u,isLoading:!0,error:null}));try{let u=a.current?s.current:[],p=Kt(e,u),r=await Lo(d,p);return r.success?i(v=>({...v,isLoading:!1,lastProposal:r.data})):i(v=>({...v,isLoading:!1,error:r.error})),r}catch(u){let p=u instanceof Error?u:new Error(String(u));return i(r=>({...r,isLoading:!1,error:p})),{success:!1,error:p}}},[e]),g=pt(async(d,u)=>{i(p=>({...p,isLoading:!0,error:null}));try{let p=a.current?s.current:[],r=Kt(e,p),v=await ko(d,r,u);return i(S=>({...S,isLoading:!1})),v.success||i(S=>({...S,error:v.error})),v}catch(p){let r=p instanceof Error?p:new Error(String(p));return i(v=>({...v,isLoading:!1,error:r})),{success:!1,error:r}}},[e]),m=pt(async d=>{i(u=>({...u,isLoading:!0,error:null}));try{let u=await _o(d);return i(p=>({...p,isLoading:!1})),u.success||i(p=>({...p,error:u.error})),u}catch(u){let p=u instanceof Error?u:new Error(String(u));return i(r=>({...r,isLoading:!1,error:p})),{success:!1,error:p}}},[]),y=pt(()=>{i(d=>({...d,error:null}))},[]);return{...n,generate:c,getAlternatives:g,explain:m,clearError:y}}import{useCallback as xe,useMemo as Or,useState as Uo}from"react";function Ho({sessionStatus:e,config:t,updateConfig:o,onOpenConfig:n,onOpenHelp:i,onClearHistory:s,onExit:a}){let[c,g]=Uo(""),[m,y]=Uo(0),d=Or(()=>z.filter(c).filter(F=>!F.isAvailable||F.isAvailable(e)),[c,e]),u=xe(T=>{g(T),y(0)},[]),p=xe(()=>{y(T=>d.length===0?0:(T-1+d.length)%d.length)},[d.length]),r=xe(()=>{y(T=>d.length===0?0:(T+1)%d.length)},[d.length]),v=xe(()=>({sessionStatus:e,config:t,updateConfig:o,exit:a}),[e,t,o,a]),S=xe(T=>{switch(T.type){case"panel":T.panel==="config"?n():T.panel==="help"&&i();break;case"navigate":T.to==="clear"&&s();break;case"exit":a();break}return T},[n,i,s,a]),V=xe(()=>{if(d.length===0)return null;let T=d[m];if(!T)return null;let F=v(),j=T.execute(F);return j instanceof Promise?(j.then(S),null):S(j)},[d,m,v,S]),L=xe(T=>{let F=z.get(T);if(!F||F.isAvailable&&!F.isAvailable(e))return null;let j=v(),re=F.execute(j);return re instanceof Promise?(re.then(S),null):S(re)},[e,v,S]),G=xe(()=>{g(""),y(0)},[]);return{query:c,setQuery:u,filteredCommands:d,focusedIndex:m,focusUp:p,focusDown:r,executeSelected:V,executeCommand:L,reset:G}}import{useCallback as Ut,useEffect as Br,useState as Nr}from"react";function Fo(){let[e,t]=Nr({isLoading:!0,hasKey:!1,config:De(),error:null});Br(()=>{try{let s=_t();t(a=>({...a,isLoading:!1,hasKey:s,error:null}))}catch(s){t(a=>({...a,isLoading:!1,error:s instanceof Error?s.message:"Failed to check API key"}))}},[]);let o=Ut(s=>{if(!Oo(s))return t(a=>({...a,error:'Invalid API key format. Key should start with "sk-ant-"'})),!1;try{let a=ut(s);return a.success?(t(c=>({...c,hasKey:!0,error:null})),!0):(t(c=>({...c,error:a.error.message})),!1)}catch(a){return t(c=>({...c,error:a instanceof Error?a.message:"Failed to save API key"})),!1}},[]),n=Ut(s=>{We(s),t(a=>({...a,config:{...a.config,...s}}))},[]),i=Ut(()=>{try{let s=_t();t(a=>({...a,hasKey:s,error:null}))}catch(s){t(a=>({...a,error:s instanceof Error?s.message:"Failed to check API key"}))}},[]);return{...e,saveKey:o,updateConfig:n,refreshKeyStatus:i}}import{execa as Dr}from"execa";import{useCallback as Go,useRef as Lr,useState as kr}from"react";function $o(){if(process.platform==="win32")return process.env.MSYSTEM||process.env.MINGW_PREFIX?"bash":process.env.PSModulePath?.includes("PowerShell\\7")?"pwsh":process.env.PSModulePath?"powershell":"cmd";let e=process.env.SHELL??"/bin/bash";return e.includes("zsh")?"zsh":e.includes("fish")?"fish":"bash"}function Yo(e,t){switch(e){case"powershell":return{cmd:"powershell",args:["-Command",t]};case"pwsh":return{cmd:"pwsh",args:["-Command",t]};case"cmd":return{cmd:"cmd",args:["/c",t]};default:return{cmd:e,args:["-c",t]}}}function jo({shell:e,maxOutputLines:t=he.maxOutputLines,onOutput:o,onComplete:n}){let[i,s]=kr({isExecuting:!1,liveOutput:[],error:null}),a=Lr(null),c=Go(async m=>{s({isExecuting:!0,liveOutput:[],error:null});let{cmd:y,args:d}=Yo(e,m);try{let u=Dr(y,d,{reject:!1,all:!0,buffer:!0});a.current=u;let p=[];u.stdout&&u.stdout.on("data",S=>{let V=S.toString().split(`
36
+ `).filter(Boolean);for(let L of V)p.push(L),o?.(L,!1),s(G=>({...G,liveOutput:[...G.liveOutput,L].slice(-t)}))}),u.stderr&&u.stderr.on("data",S=>{let V=S.toString().split(`
37
+ `).filter(Boolean);for(let L of V){let G=`[ERR] ${L}`;p.push(G),o?.(L,!0),s(T=>({...T,liveOutput:[...T.liveOutput,G].slice(-t)}))}});let r=await u;a.current=null;let v={command:m,stdout:r.stdout??"",stderr:r.stderr??"",exitCode:r.exitCode??0};return s(S=>({...S,isExecuting:!1})),n?.(v),{success:!0,data:v}}catch(u){a.current=null;let p=u instanceof Error?u:new Error(String(u));s(v=>({...v,isExecuting:!1,error:p}));let r={command:m,stdout:"",stderr:p.message,exitCode:1};return n?.(r),{success:!1,error:p}}},[e,t,o,n]),g=Go(()=>{a.current&&(a.current.kill("SIGINT"),a.current=null,s(m=>({...m,isExecuting:!1,liveOutput:[...m.liveOutput,"[Interrupted]"]})))},[]);return{...i,execute:c,kill:g}}import{useInput as _r}from"ink";import{useCallback as qo,useReducer as Mr,useRef as Xo,useState as Qe}from"react";function Wo({mode:e,menuCallbacks:t,selectionCallbacks:o,textCallbacks:n,paletteCallbacks:i,configCallbacks:s,helpCallbacks:a,initialTextValue:c="",paletteQuery:g=""}){let[m,y]=Mr(yo,xo(c)),[d,u]=Qe(0),[p,r]=Qe(0),[v,S]=Qe(0),[V,L]=Qe(0),[G,T]=Qe(0),F=Xo(c);F.current!==c&&(F.current=c,y({type:"set",value:c}));let j=Xo(e);j.current!==e&&(e==="menu"?u(0):e==="selection"?r(0):e==="palette"?S(0):e==="config"&&(L(0),T(0)),j.current=e);let re=qo(()=>{y({type:"clear"})},[]),R=qo(b=>{y({type:"set",value:b})},[]);return _r((b,f)=>{if(e!=="disabled"){if(e==="text"&&n){if(f.ctrl&&!f.meta&&b==="d"){m.value.trim()===""&&process.exit(130);return}if(f.return){let h=m.value.trim();(h.toLowerCase()==="exit"||h.toLowerCase()==="quit")&&process.exit(0),h&&(n.onSubmit(h),y({type:"clear"}));return}if(b.toLowerCase()==="o"&&m.value.trim()===""&&n.onToggleOutput&&n.hasHistory){n.onToggleOutput();return}if(n.hasInlinePalette&&n.onNavigateInlinePalette){if(f.upArrow){n.onNavigateInlinePalette("up");return}if(f.downArrow){n.onNavigateInlinePalette("down");return}}if(f.leftArrow){y({type:"move-left"});return}if(f.rightArrow){y({type:"move-right"});return}if(f.backspace||f.delete){y({type:"delete"});let h=m.value.slice(0,m.cursorOffset-1)+m.value.slice(m.cursorOffset);n.onTextChange?.(h);return}if(f.escape){n.hasInlinePalette&&n.onCloseInlinePalette&&(n.onCloseInlinePalette(),y({type:"clear"}));return}if(f.ctrl||f.meta||f.tab)return;if(b&&b.length>0){y({type:"insert",text:b});let h=m.value.slice(0,m.cursorOffset)+b+m.value.slice(m.cursorOffset);n.onTextChange?.(h)}return}if(e==="menu"&&t){if(f.leftArrow){u(h=>(h-1+5)%5);return}if(f.rightArrow){u(h=>(h+1)%5);return}if(f.return){switch(["execute","copy","edit","alternatives","cancel"][d]){case"execute":t.onExecute();break;case"copy":t.onCopy();break;case"edit":t.onEdit();break;case"alternatives":t.onAlternatives();break;case"cancel":t.onCancel();break}return}if(b>="1"&&b<="5"){let h=parseInt(b,10)-1;switch(["execute","copy","edit","alternatives","cancel"][h]){case"execute":t.onExecute();break;case"copy":t.onCopy();break;case"edit":t.onEdit();break;case"alternatives":t.onAlternatives();break;case"cancel":t.onCancel();break}return}if(b==="?"){t.onExplain();return}if(b.toLowerCase()==="o"){t.onToggle();return}if(f.escape){t.onCancel();return}return}if(e==="selection"&&o){let h=o.count;if(f.upArrow){r(A=>(A-1+h)%h);return}if(f.downArrow){r(A=>(A+1)%h);return}if(f.return){o.onSelect(p);return}let k=parseInt(b,10);if(k>=1&&k<=h){o.onSelect(k-1);return}if(b==="5"||b.toLowerCase()==="c"||f.escape){o.onCancel();return}return}if(e==="palette"&&i){let h=i.filteredCount;if(f.upArrow){S(A=>h===0?0:(A-1+h)%h),i.onNavigate("up");return}if(f.downArrow){S(A=>h===0?0:(A+1)%h),i.onNavigate("down");return}if(f.return){i.onSelect();return}if(f.escape){i.onClose();return}if((f.backspace||f.delete)&&g===""){i.onClose();return}if(f.backspace||f.delete){let A=g.slice(0,-1);i.onQueryChange(A),S(0);return}if(f.ctrl||f.meta||f.tab)return;let k=parseInt(b,10);if(k>=1&&k<=9&&k<=h){S(k-1),i.onSelect();return}if(b&&b.length>0){let A=g+b;i.onQueryChange(A),S(0)}return}if(e==="config"&&s){let h=s.sectionCount,k=s.itemCount;if(f.tab&&!f.shift){L(A=>(A+1)%h),T(0),s.onNavigateSection("next");return}if(f.tab&&f.shift){L(A=>(A-1+h)%h),T(0),s.onNavigateSection("prev");return}if(f.upArrow){T(A=>k===0?0:(A-1+k)%k),s.onNavigateItem("up");return}if(f.downArrow){T(A=>k===0?0:(A+1)%k),s.onNavigateItem("down");return}if(f.return||b===" "){s.onToggle();return}if(f.escape){s.onClose();return}return}if(e==="help"&&a){if(f.escape){a.onClose();return}return}}},{isActive:e!=="disabled"}),{textState:m,dispatchText:y,clearText:re,setText:R,menuFocusIndex:d,selectionFocusIndex:p,paletteFocusIndex:v,configSectionIndex:V,configItemIndex:G}}import{useCallback as O,useReducer as Kr}from"react";var Ur={state:{status:"setup"},history:[],currentQuery:"",editingCommand:null,outputExpanded:!1,error:null};function Hr(e,t){switch(t.type){case"SETUP_COMPLETE":return{...e,state:{status:"input"},error:null};case"SUBMIT":return{...e,state:{status:"loading",query:t.query},currentQuery:t.query,editingCommand:null,error:null};case"AI_RESPONSE":return{...e,state:{status:"proposal",proposal:t.proposal},error:null};case"AI_ALTERNATIVES":return{...e,state:{status:"alternatives",proposals:t.proposals,originalQuery:e.currentQuery},error:null};case"AI_ERROR":return{...e,state:{status:"input"},error:t.error.message};case"EXECUTE":{let o=e.state;return o.status!=="proposal"?e:{...e,state:{status:"executing",command:o.proposal.command},error:null}}case"EXECUTE_EDITED":return{...e,state:{status:"executing",command:t.command},editingCommand:null,error:null};case"EXEC_DONE":{let o=[...e.history,{query:e.currentQuery,command:t.result.command,output:t.result.stdout||t.result.stderr,exitCode:t.result.exitCode}].slice(-he.maxHistoryEntries);return{...e,state:{status:"input"},history:o,outputExpanded:!1,error:null}}case"COPY":return{...e,state:{status:"input"},error:null};case"EDIT":return{...e,state:{status:"input"},editingCommand:t.command,error:null};case"CANCEL":return{...e,state:{status:"input"},editingCommand:null,error:null};case"TOGGLE_OUTPUT":return e.history.length===0?e:{...e,outputExpanded:!e.outputExpanded};case"OPEN_PALETTE":return{...e,state:{status:"palette",query:"",filteredCommands:[]},error:null};case"UPDATE_PALETTE":return{...e,state:{status:"palette",query:t.query,filteredCommands:t.filteredCommands}};case"CLOSE_PALETTE":return{...e,state:{status:"input"},error:null};case"OPEN_CONFIG":return{...e,state:{status:"config",section:"api-key"},error:null};case"UPDATE_CONFIG_SECTION":return{...e,state:{status:"config",section:t.section}};case"CLOSE_CONFIG":return{...e,state:{status:"input"},error:null};case"OPEN_HELP":return{...e,state:{status:"help"},error:null};case"CLOSE_HELP":return{...e,state:{status:"input"},error:null};case"CLEAR_HISTORY":return{...e,state:{status:"input"},history:[],outputExpanded:!1,error:null};default:return e}}function Qo(){let[e,t]=Kr(Hr,Ur),o=O(R=>{t({type:"SUBMIT",query:R})},[]),n=O(R=>{t({type:"AI_RESPONSE",proposal:R})},[]),i=O(R=>{t({type:"AI_ALTERNATIVES",proposals:R})},[]),s=O(R=>{t({type:"AI_ERROR",error:R})},[]),a=O(()=>{t({type:"EXECUTE"})},[]),c=O(R=>{t({type:"EXECUTE_EDITED",command:R})},[]),g=O(R=>{t({type:"EXEC_DONE",result:R})},[]),m=O(()=>{t({type:"COPY"})},[]),y=O(R=>{t({type:"EDIT",command:R})},[]),d=O(()=>{t({type:"CANCEL"})},[]),u=O(()=>{t({type:"TOGGLE_OUTPUT"})},[]),p=O(()=>{t({type:"SETUP_COMPLETE"})},[]),r=O(()=>{t({type:"EDIT",command:""})},[]),v=O(()=>{t({type:"OPEN_PALETTE"})},[]),S=O((R,b)=>{t({type:"UPDATE_PALETTE",query:R,filteredCommands:b})},[]),V=O(()=>{t({type:"CLOSE_PALETTE"})},[]),L=O(()=>{t({type:"OPEN_CONFIG"})},[]),G=O(R=>{t({type:"UPDATE_CONFIG_SECTION",section:R})},[]),T=O(()=>{t({type:"CLOSE_CONFIG"})},[]),F=O(()=>{t({type:"OPEN_HELP"})},[]),j=O(()=>{t({type:"CLOSE_HELP"})},[]),re=O(()=>{t({type:"CLEAR_HISTORY"})},[]);return{store:e,dispatch:t,submitQuery:o,handleAIResponse:n,handleAIAlternatives:i,handleAIError:s,execute:a,executeEdited:c,markExecutionDone:g,copy:m,edit:y,cancel:d,toggleOutput:u,completeSetup:p,clearEditingCommand:r,openPalette:v,updatePalette:S,closePalette:V,openConfig:L,updateConfigSection:G,closeConfig:T,openHelp:F,closeHelp:j,clearHistory:re}}import Fr from"clipboardy";async function Vo(e){try{return await Fr.write(e),{success:!0,data:{success:!0,message:"Copied to clipboard"}}}catch(t){return{success:!1,error:t instanceof Error?t:new Error("Failed to copy to clipboard")}}}import{Box as w,Static as $r,Text as I,useApp as Yr}from"ink";import{useCallback as _,useEffect as zo,useMemo as Le,useState as ye}from"react";import{Fragment as Gr,jsx as x,jsxs as B}from"react/jsx-runtime";function Jo(){let{exit:e}=Yr(),t=$o(),[o,n]=ye(null),[i,s]=ye(null),[a,c]=ye([]),[g,m]=ye(0),{isLoading:y,hasKey:d,error:u,refreshKeyStatus:p}=Fo(),{store:r,submitQuery:v,handleAIResponse:S,handleAIAlternatives:V,handleAIError:L,execute:G,executeEdited:T,markExecutionDone:F,copy:j,edit:re,cancel:R,toggleOutput:b,completeSetup:f,openPalette:h,updatePalette:k,closePalette:A,openConfig:Zo,updateConfigSection:Ht,closeConfig:ke,openHelp:en,closeHelp:tn,clearHistory:on}=Qo(),[ve,mt]=ye(0),[be,nn]=ye(()=>De().model),[dt,rn]=ye(()=>({contextEnabled:De().contextEnabled,showExplanations:!0,syntaxHighlighting:!0,simpleMode:!1})),[sn,Ft]=ye(!1),an=Le(()=>Bo(),[d]),ln=Le(()=>{let l=ge();return l?l.length<=12?"***":`${l.slice(0,7)}...${l.slice(-4)}`:null},[d]),$t=Le(()=>({model:be,contextEnabled:dt.contextEnabled,maxHistoryEntries:5,maxOutputLines:10,maxAlternatives:3}),[be]),q=Ho({sessionStatus:r.state.status,config:$t,updateConfig:()=>{},onOpenConfig:Zo,onOpenHelp:en,onClearHistory:on,onExit:()=>e()}),cn=Le(()=>{let l=[{id:"header",type:"header"}];return r.history.slice(0,-1).forEach((E,M)=>{l.push({id:`history-${M}`,type:"history",entry:E})}),l},[r.history]),ae=r.history.length>0?r.history[r.history.length-1]:null,{generate:Yt,getAlternatives:Gt,explain:jt,isLoading:Pe}=Ko({shell:t,history:r.history,contextEnabled:dt.contextEnabled}),{execute:Ve,kill:zr,isExecuting:ft,liveOutput:un}=jo({shell:t,onComplete:F}),pn=Le(()=>y||!d||r.state.status==="setup"||r.state.status==="loading"||r.state.status==="executing"||ft?"disabled":r.state.status==="palette"?"palette":r.state.status==="config"?"config":r.state.status==="help"?"help":r.state.status==="proposal"?Pe?"disabled":"menu":r.state.status==="alternatives"?Pe?"disabled":"selection":r.state.status==="input"?"text":"disabled",[y,d,r.state.status,ft,Pe]),mn=_(l=>{ut(l).success&&(p(),Ft(!1),f())},[p,f]);zo(()=>{!y&&d&&r.state.status==="setup"&&f()},[y,d,r.state.status,f]),zo(()=>{let l=De().model;be!==l&&We({model:be})},[be]);let dn=_(l=>{if(l.startsWith("/")){let C=l.slice(1),E=z.filter(C);c(E),m(0)}else c([]),m(0)},[]),fn=_(async l=>{if(l.startsWith("/")){let E=l.slice(1).trim();if(a.length>0){let M=a[g];if(M&&q.executeCommand(M.name)){c([]),m(0);return}}if(E&&q.executeCommand(E)){c([]),m(0);return}c([]),m(0);return}if(c([]),m(0),r.editingCommand){T(l),await Ve(l);return}v(l),s(null);let C=await Yt(l);C.success?S(C.data):L(C.error)},[r.editingCommand,v,Yt,S,L,T,Ve,q,a,g]),gn=_(async()=>{r.state.status==="proposal"&&(G(),await Ve(r.state.proposal.command))},[r.state,G,Ve]),xn=_(async()=>{if(r.state.status!=="proposal")return;(await Vo(r.state.proposal.command)).success?(n("\u2713 Copied to clipboard"),setTimeout(()=>n(null),2e3)):(n("\u2717 Failed to copy"),setTimeout(()=>n(null),2e3)),j()},[r.state,j]),yn=_(()=>{r.state.status==="proposal"&&re(r.state.proposal.command)},[r.state,re]),Cn=_(async()=>{if(r.state.status!=="proposal")return;let l=await Gt(r.currentQuery,r.state.proposal.command);l.success?V(l.data):L(l.error)},[r.state,r.currentQuery,Gt,V,L]),hn=_(()=>{R(),s(null)},[R]),Tn=_(async()=>{if(r.state.status!=="proposal")return;let l=await jt(r.state.proposal.command);l.success&&s(l.data)},[r.state,jt]),Sn=_(()=>{b()},[b]),En=_(l=>{if(r.state.status!=="alternatives")return;let C=r.state.proposals[l];C&&S(C)},[r.state,S]),vn=_(l=>{let C=z.filter(l);q.setQuery(l),k(l,C)},[q,k]),bn=_(()=>{q.executeSelected()&&(A(),q.reset())},[q,A]),Pn=_(l=>{l==="up"?q.focusUp():q.focusDown()},[q]),In=_(()=>{A(),q.reset()},[A,q]),An=_(l=>{let C=["api-key","model","toggles","about"];if(r.state.status!=="config")return;let E=C.indexOf(r.state.section),M;l==="next"?M=(E+1)%C.length:M=(E-1+C.length)%C.length,Ht(C[M]),mt(0)},[r.state,Ht]),wn=_(l=>{let C={"api-key":d?2:1,model:3,toggles:4,about:0};if(r.state.status!=="config")return;let E=C[r.state.section];E!==0&&mt(M=>l==="up"?(M-1+E)%E:(M+1)%E)},[r.state,d]),Rn=_(()=>{if(r.state.status==="config"){if(r.state.section==="toggles"){rn(l=>{let E=["contextEnabled","showExplanations","syntaxHighlighting","simpleMode"][ve];if(E){let M=!l[E];return E==="contextEnabled"&&We({contextEnabled:M}),{...l,[E]:M}}return l});return}if(r.state.section==="model"){let C=["claude-sonnet-4-5","claude-opus-4-5","claude-haiku-4-5"][ve];C&&nn(C);return}if(r.state.section==="api-key"){ve===0?(ke(),Ft(!0)):ve===1&&d&&Ro().success&&(p(),ke());return}}},[r.state,ve,ke,d,p]),On=_(()=>{ke(),mt(0)},[ke]),Bn=r.state.status==="palette"?r.state.query:"",Nn=Le(()=>r.state.status!=="config"?0:{"api-key":d?2:1,model:3,toggles:4,about:0}[r.state.section],[r.state,d]),{textState:Dn,clearText:Jr,setText:Zr,menuFocusIndex:Ln,selectionFocusIndex:kn,paletteFocusIndex:_n,configSectionIndex:es}=Wo({mode:pn,initialTextValue:r.editingCommand??"",paletteQuery:Bn,menuCallbacks:{onExecute:gn,onCopy:xn,onEdit:yn,onAlternatives:Cn,onCancel:hn,onExplain:Tn,onToggle:Sn},selectionCallbacks:{onSelect:En,onCancel:R,count:r.state.status==="alternatives"?r.state.proposals.length:1},textCallbacks:{onSubmit:fn,onToggleOutput:b,onTextChange:dn,onNavigateInlinePalette:l=>{m(C=>{let E=a.length;return E===0?0:l==="up"?(C-1+E)%E:(C+1)%E})},onCloseInlinePalette:()=>{c([]),m(0)},hasInlinePalette:a.length>0,hasHistory:r.history.length>0},paletteCallbacks:{onQueryChange:vn,onSelect:bn,onNavigate:Pn,onClose:In,filteredCount:r.state.status==="palette"?r.state.filteredCommands.length:0},configCallbacks:{onNavigateSection:An,onNavigateItem:wn,onToggle:Rn,onClose:On,sectionCount:4,itemCount:Nn},helpCallbacks:{onClose:tn}});return y?x(w,{flexDirection:"column",paddingY:1,children:x(I,{dimColor:!0,children:"Loading configuration..."})}):sn||!d&&r.state.status==="setup"?x(to,{onComplete:mn,error:u}):B(w,{flexDirection:"column",children:[x($r,{items:cn,children:l=>l.type==="header"?x(Eo,{shell:t,cwd:process.cwd(),model:be},l.id):l.type==="history"?B(w,{flexDirection:"column",marginBottom:1,children:[B(w,{children:[x(I,{color:"cyan",bold:!0,children:"\u276F "}),x(I,{color:"cyan",children:l.entry.query})]}),B(w,{marginLeft:2,flexDirection:"column",children:[B(w,{children:[B(I,{dimColor:!0,children:["$ ",l.entry.command," "]}),x(I,{color:l.entry.exitCode===0?"green":"red",children:l.entry.exitCode===0?"\u2713":`\u2717 ${l.entry.exitCode}`})]}),l.entry.output&&B(w,{flexDirection:"column",children:[l.entry.output.split(`
38
+ `).slice(0,10).map((C,E)=>x(I,{children:C},E)),l.entry.output.split(`
39
+ `).length>10&&B(I,{dimColor:!0,children:["... (",l.entry.output.split(`
40
+ `).length-10," more lines)"]})]})]}),x(w,{marginTop:1,children:x(I,{dimColor:!0,children:"\u2500".repeat(50)})})]},l.id):null}),ae&&B(w,{flexDirection:"column",marginBottom:1,children:[B(w,{children:[x(I,{color:"cyan",bold:!0,children:"\u276F "}),x(I,{color:"cyan",children:ae.query})]}),B(w,{marginLeft:2,flexDirection:"column",children:[B(w,{children:[B(I,{dimColor:!0,children:["$ ",ae.command," "]}),x(I,{color:ae.exitCode===0?"green":"red",children:ae.exitCode===0?"\u2713":`\u2717 ${ae.exitCode}`})]}),ae.output&&x(w,{flexDirection:"column",children:(()=>{let l=ae.output.split(`
41
+ `).filter(ze=>ze.length>0),C=r.outputExpanded?500:10,E=l.slice(0,C),M=l.length-E.length;return B(Gr,{children:[E.map((ze,Mn)=>x(I,{children:ze},Mn)),M>0&&B(w,{children:[B(I,{dimColor:!0,children:["... (",M," more lines, press "]}),x(I,{color:"blue",children:"[O]"}),x(I,{dimColor:!0,children:" to expand)"})]})]})})()})]}),x(w,{marginTop:1,children:x(I,{dimColor:!0,children:"\u2500".repeat(50)})})]}),r.error&&x(w,{marginY:1,children:B(I,{color:"red",children:["Error: ",r.error]})}),o&&x(w,{marginY:1,children:x(I,{color:"green",children:o})}),r.state.status==="loading"&&x(Dt,{query:r.state.query}),r.state.status==="proposal"&&B(w,{flexDirection:"column",marginY:1,children:[x(ao,{proposal:r.state.proposal,showExplanation:!!i}),i&&x(w,{marginTop:1,paddingX:1,children:x(I,{dimColor:!0,children:i})})]}),r.state.status==="proposal"&&Pe&&x(Dt,{label:"Generating alternatives..."}),x(ho,{focusedIndex:Ln,visible:r.state.status==="proposal"&&!Pe}),r.state.status==="alternatives"&&x(w,{flexDirection:"column",marginY:1,children:x(lo,{proposals:r.state.proposals})}),x(To,{count:r.state.status==="alternatives"?r.state.proposals.length:0,focusedIndex:kn,visible:r.state.status==="alternatives"&&!Pe}),(r.state.status==="executing"||ft)&&x(no,{lines:un,command:r.state.status==="executing"?r.state.command:""}),x(bt,{query:r.state.status==="palette"?r.state.query:"",filteredCommands:r.state.status==="palette"?r.state.filteredCommands:[],selectedIndex:_n,visible:r.state.status==="palette"}),x(wt,{visible:r.state.status==="config",activeSection:r.state.status==="config"?r.state.section:"api-key",sectionItemIndex:ve,config:$t,hasApiKey:d,storageInfo:an,maskedKey:ln,toggles:dt}),x(Rt,{visible:r.state.status==="help"}),x(Co,{textState:Dn,placeholder:"Describe what you want to do... (type / for commands)",hasHistory:r.history.length>0,visible:r.state.status==="input"}),r.state.status==="input"&&a.length>0&&B(w,{flexDirection:"column",marginLeft:2,marginTop:0,children:[x(w,{marginBottom:0,children:x(I,{dimColor:!0,children:"\u2500".repeat(50)})}),a.slice(0,5).map((l,C)=>B(w,{children:[x(I,{color:C===g?"cyan":"gray",bold:C===g,children:C===g?"> ":" "}),B(I,{color:C===g?"cyan":"blue",bold:C===g,children:["/",l.name]}),x(I,{children:" "}),x(I,{dimColor:C!==g,children:l.description})]},l.name)),x(w,{marginTop:0,children:x(I,{dimColor:!0,children:"[Enter] Select [Up/Down] Navigate [Esc] Cancel"})})]})]})}import{jsx as Vr}from"react/jsx-runtime";var qr="\x1Bc",Xr="\x1B]0;CLI AI\x07";function Wr(){console.log(`
42
+ CLI AI v${ee} - Natural language to shell commands
43
+
44
+ Usage:
45
+ s Start interactive session
46
+ cli-ai Start interactive session
47
+
48
+ Options:
49
+ --help, -h Show this help message
50
+ --version, -v Show version number
51
+
52
+ Session Controls:
53
+ [1] Execute Run the generated command
54
+ [2] Copy Copy command to clipboard
55
+ [3] Edit Edit the command
56
+ [4] Alternatives Show alternative commands
57
+ [5] Cancel Cancel and start new query
58
+ [?] Explain Get explanation of the command
59
+ [O] Toggle Toggle output expansion
60
+ Arrow keys Navigate menu options
61
+ Enter Select focused option
62
+ Escape Cancel current action
63
+ exit, quit Exit the session
64
+ Ctrl+D Exit (empty input)
65
+ `)}async function Qr(){let e=process.argv.slice(2);(e.includes("--help")||e.includes("-h"))&&(Wr(),process.exit(0)),(e.includes("--version")||e.includes("-v"))&&(console.log(`CLI AI v${ee}`),process.exit(0)),process.stdout.write(qr+Xr);let{waitUntilExit:t}=jr(Vr(Jo,{}));process.on("SIGINT",()=>{}),process.on("SIGTERM",()=>{process.exit(0)}),await t()}Qr().catch(e=>{console.error("Fatal error:",e),process.exit(1)});
package/package.json ADDED
@@ -0,0 +1,111 @@
1
+ {
2
+ "name": "@fmdzc/cli-ai",
3
+ "version": "3.0.0",
4
+ "description": "CLI AI Assistant v3 - Natural language to shell commands with persistent REPL session",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "bin": {
15
+ "s": "./dist/cli.js",
16
+ "cli-ai": "./dist/cli.js"
17
+ },
18
+ "files": [
19
+ "dist",
20
+ "scripts"
21
+ ],
22
+ "engines": {
23
+ "node": ">=20"
24
+ },
25
+ "scripts": {
26
+ "build": "tsup",
27
+ "dev": "tsup --watch",
28
+ "typecheck": "tsc --noEmit",
29
+ "lint": "eslint src --ext .ts,.tsx",
30
+ "lint:security": "eslint src --ext .ts,.tsx --rule 'security/*: error'",
31
+ "format": "prettier --write src/**/*.{ts,tsx}",
32
+ "test": "vitest run",
33
+ "test:watch": "vitest",
34
+ "audit": "pnpm audit",
35
+ "audit:fix": "pnpm audit --fix",
36
+ "security": "pnpm audit && pnpm lint:security",
37
+ "prepare": "tsup",
38
+ "prepublishOnly": "pnpm typecheck && pnpm audit && pnpm build",
39
+ "link:global": "pnpm build && pnpm link --global",
40
+ "unlink:global": "pnpm unlink --global",
41
+ "uninstall": "node scripts/uninstall.js",
42
+ "uninstall:force": "node scripts/uninstall.js --force"
43
+ },
44
+ "keywords": [
45
+ "cli",
46
+ "ai",
47
+ "assistant",
48
+ "terminal",
49
+ "shell",
50
+ "anthropic",
51
+ "claude",
52
+ "natural-language",
53
+ "command-line"
54
+ ],
55
+ "author": "fmdz387",
56
+ "license": "ISC",
57
+ "repository": {
58
+ "type": "git",
59
+ "url": "git+https://github.com/fmdz387/cli-ai.git"
60
+ },
61
+ "homepage": "https://github.com/fmdz387/cli-ai#readme",
62
+ "bugs": {
63
+ "url": "https://github.com/fmdz387/cli-ai/issues"
64
+ },
65
+ "publishConfig": {
66
+ "access": "public"
67
+ },
68
+ "contributors": [
69
+ {
70
+ "name": "fmdz387",
71
+ "url": "https://github.com/fmdz387"
72
+ },
73
+ {
74
+ "name": "fmdz387",
75
+ "url": "https://x.com/fmdz387"
76
+ }
77
+ ],
78
+ "packageManager": "pnpm@10.10.0",
79
+ "dependencies": {
80
+ "@anthropic-ai/sdk": "^0.71.2",
81
+ "@inkjs/ui": "^2.0.0",
82
+ "@napi-rs/keyring": "^1.2.0",
83
+ "chalk": "^5.3.0",
84
+ "clipboardy": "^4.0.0",
85
+ "conf": "^13.0.0",
86
+ "execa": "^9.0.0",
87
+ "ink": "^6.0.0",
88
+ "react": "^19.0.0",
89
+ "zod": "^3.23.0"
90
+ },
91
+ "devDependencies": {
92
+ "@eslint/js": "8.57.0",
93
+ "@trivago/prettier-plugin-sort-imports": "5.2.2",
94
+ "@types/node": "^22.0.0",
95
+ "@types/react": "^19.0.0",
96
+ "@typescript-eslint/eslint-plugin": "6.21.0",
97
+ "@typescript-eslint/parser": "6.21.0",
98
+ "eslint": "8.57.0",
99
+ "eslint-config-prettier": "10.1.8",
100
+ "eslint-plugin-jsx-a11y": "^6.10.2",
101
+ "eslint-plugin-react": "7.33.2",
102
+ "eslint-plugin-react-hooks": "5.2.0",
103
+ "eslint-plugin-security": "^3.0.0",
104
+ "eslint-plugin-unused-imports": "4.2.0",
105
+ "prettier": "3.2.5",
106
+ "prettier-eslint": "16.4.2",
107
+ "tsup": "^8.0.0",
108
+ "typescript": "^5.6.0",
109
+ "vitest": "^4.0.17"
110
+ }
111
+ }
@@ -0,0 +1,332 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * CLI AI Uninstall Script
5
+ * Safely removes all cli-ai components from the system
6
+ *
7
+ * Usage: node scripts/uninstall.js [--force]
8
+ *
9
+ * Components removed:
10
+ * - API key from system keyring
11
+ * - Config directory (~/.cli_ai_assistant)
12
+ * - Global npm/pnpm link (optional)
13
+ */
14
+
15
+ import { execSync, spawnSync } from 'node:child_process';
16
+ import { existsSync, rmSync, readFileSync, writeFileSync, copyFileSync } from 'node:fs';
17
+ import { homedir, platform } from 'node:os';
18
+ import { join } from 'node:path';
19
+ import { createInterface } from 'node:readline';
20
+
21
+ const CONFIG_DIR_NAME = '.cli_ai_assistant';
22
+ const KEYRING_SERVICE = 'cli-ai';
23
+ const KEYRING_ACCOUNT = 'anthropic';
24
+
25
+ const isWindows = platform() === 'win32';
26
+ const isMac = platform() === 'darwin';
27
+ const isLinux = platform() === 'linux';
28
+
29
+ const forceMode = process.argv.includes('--force') || process.argv.includes('-f');
30
+
31
+ /**
32
+ * Print colored output
33
+ */
34
+ function log(message, type = 'info') {
35
+ const colors = {
36
+ info: '\x1b[36m', // cyan
37
+ success: '\x1b[32m', // green
38
+ warn: '\x1b[33m', // yellow
39
+ error: '\x1b[31m', // red
40
+ reset: '\x1b[0m',
41
+ };
42
+
43
+ const prefix = {
44
+ info: 'ℹ',
45
+ success: '✓',
46
+ warn: '⚠',
47
+ error: '✗',
48
+ };
49
+
50
+ console.log(`${colors[type]}${prefix[type]} ${message}${colors.reset}`);
51
+ }
52
+
53
+ /**
54
+ * Prompt user for confirmation
55
+ */
56
+ async function confirm(question) {
57
+ if (forceMode) return true;
58
+
59
+ const rl = createInterface({
60
+ input: process.stdin,
61
+ output: process.stdout,
62
+ });
63
+
64
+ return new Promise((resolve) => {
65
+ rl.question(`${question} (y/N): `, (answer) => {
66
+ rl.close();
67
+ resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
68
+ });
69
+ });
70
+ }
71
+
72
+ /**
73
+ * Remove API key from system keyring
74
+ */
75
+ async function removeKeyringEntry() {
76
+ log('Removing API key from system keyring...');
77
+
78
+ try {
79
+ // Try using @napi-rs/keyring first (if available)
80
+ try {
81
+ const { Entry } = await import('@napi-rs/keyring');
82
+ const entry = new Entry(KEYRING_SERVICE, KEYRING_ACCOUNT);
83
+ entry.deleteCredential();
84
+ log('API key removed from keyring (via @napi-rs/keyring)', 'success');
85
+ return true;
86
+ } catch {
87
+ // Fall through to platform-specific methods
88
+ }
89
+
90
+ // Platform-specific fallbacks
91
+ if (isWindows) {
92
+ // Windows Credential Manager
93
+ const result = spawnSync('cmdkey', ['/delete:cli-ai'], {
94
+ encoding: 'utf-8',
95
+ shell: true,
96
+ });
97
+ if (result.status === 0) {
98
+ log('API key removed from Windows Credential Manager', 'success');
99
+ return true;
100
+ }
101
+ } else if (isMac) {
102
+ // macOS Keychain
103
+ const result = spawnSync('security', [
104
+ 'delete-generic-password',
105
+ '-s', KEYRING_SERVICE,
106
+ '-a', KEYRING_ACCOUNT,
107
+ ], { encoding: 'utf-8' });
108
+ if (result.status === 0) {
109
+ log('API key removed from macOS Keychain', 'success');
110
+ return true;
111
+ }
112
+ } else if (isLinux) {
113
+ // Linux secret-tool (GNOME Keyring)
114
+ const result = spawnSync('secret-tool', [
115
+ 'clear',
116
+ 'service', KEYRING_SERVICE,
117
+ 'account', KEYRING_ACCOUNT,
118
+ ], { encoding: 'utf-8' });
119
+ if (result.status === 0) {
120
+ log('API key removed from Linux keyring', 'success');
121
+ return true;
122
+ }
123
+ }
124
+
125
+ log('No API key found in keyring (may not have been stored there)', 'warn');
126
+ return true;
127
+ } catch (error) {
128
+ log(`Could not remove keyring entry: ${error.message}`, 'warn');
129
+ return false;
130
+ }
131
+ }
132
+
133
+ /**
134
+ * Remove config directory
135
+ */
136
+ function removeConfigDirectory() {
137
+ const configDir = join(homedir(), CONFIG_DIR_NAME);
138
+
139
+ if (!existsSync(configDir)) {
140
+ log(`Config directory not found: ${configDir}`, 'warn');
141
+ return true;
142
+ }
143
+
144
+ log(`Removing config directory: ${configDir}`);
145
+
146
+ try {
147
+ rmSync(configDir, { recursive: true, force: true });
148
+ log('Config directory removed', 'success');
149
+ return true;
150
+ } catch (error) {
151
+ log(`Failed to remove config directory: ${error.message}`, 'error');
152
+ return false;
153
+ }
154
+ }
155
+
156
+ /**
157
+ * Remove global package link
158
+ */
159
+ function removeGlobalLink() {
160
+ log('Checking for global package installation...');
161
+
162
+ // Try pnpm first
163
+ try {
164
+ const pnpmResult = spawnSync('pnpm', ['list', '-g', '--depth=0'], {
165
+ encoding: 'utf-8',
166
+ shell: true,
167
+ });
168
+
169
+ if (pnpmResult.stdout && pnpmResult.stdout.includes('cli-ai')) {
170
+ log('Found pnpm global link, removing...');
171
+ const unlinkResult = spawnSync('pnpm', ['unlink', '--global', '@fmdz387/cli-ai'], {
172
+ encoding: 'utf-8',
173
+ shell: true,
174
+ });
175
+ if (unlinkResult.status === 0) {
176
+ log('Removed pnpm global link', 'success');
177
+ return true;
178
+ }
179
+ }
180
+ } catch {
181
+ // pnpm not available
182
+ }
183
+
184
+ // Try npm
185
+ try {
186
+ const npmResult = spawnSync('npm', ['list', '-g', '--depth=0'], {
187
+ encoding: 'utf-8',
188
+ shell: true,
189
+ });
190
+
191
+ if (npmResult.stdout && npmResult.stdout.includes('cli-ai')) {
192
+ log('Found npm global link, removing...');
193
+ const unlinkResult = spawnSync('npm', ['unlink', '-g', '@fmdz387/cli-ai'], {
194
+ encoding: 'utf-8',
195
+ shell: true,
196
+ });
197
+ if (unlinkResult.status === 0) {
198
+ log('Removed npm global link', 'success');
199
+ return true;
200
+ }
201
+ }
202
+ } catch {
203
+ // npm not available
204
+ }
205
+
206
+ log('No global package link found', 'info');
207
+ return true;
208
+ }
209
+
210
+ /**
211
+ * Remove shell alias from config files
212
+ */
213
+ function removeShellAliases() {
214
+ log('Checking for shell aliases...');
215
+
216
+ const shellConfigs = [];
217
+
218
+ if (isWindows) {
219
+ // PowerShell profile locations
220
+ const psProfile = join(homedir(), 'Documents', 'WindowsPowerShell', 'Microsoft.PowerShell_profile.ps1');
221
+ const pwshProfile = join(homedir(), 'Documents', 'PowerShell', 'Microsoft.PowerShell_profile.ps1');
222
+ if (existsSync(psProfile)) shellConfigs.push(psProfile);
223
+ if (existsSync(pwshProfile)) shellConfigs.push(pwshProfile);
224
+ } else {
225
+ // Unix shell configs
226
+ const bashrc = join(homedir(), '.bashrc');
227
+ const zshrc = join(homedir(), '.zshrc');
228
+ const fishConfig = join(homedir(), '.config', 'fish', 'config.fish');
229
+
230
+ if (existsSync(bashrc)) shellConfigs.push(bashrc);
231
+ if (existsSync(zshrc)) shellConfigs.push(zshrc);
232
+ if (existsSync(fishConfig)) shellConfigs.push(fishConfig);
233
+ }
234
+
235
+ let aliasesRemoved = 0;
236
+
237
+ for (const configPath of shellConfigs) {
238
+ try {
239
+ const content = readFileSync(configPath, 'utf-8');
240
+
241
+ // Look for cli-ai related aliases
242
+ const aliasPatterns = [
243
+ /^.*alias\s+s\s*=.*cli[_-]?ai.*$/gm,
244
+ /^.*Set-Alias.*cli[_-]?ai.*$/gm,
245
+ /^.*function\s+s\s*\(\).*cli[_-]?ai.*$/gm,
246
+ /^# CLI AI.*$/gm,
247
+ ];
248
+
249
+ let newContent = content;
250
+ let hasChanges = false;
251
+
252
+ for (const pattern of aliasPatterns) {
253
+ if (pattern.test(newContent)) {
254
+ hasChanges = true;
255
+ newContent = newContent.replace(pattern, '');
256
+ }
257
+ }
258
+
259
+ if (hasChanges) {
260
+ // Create backup
261
+ const backupPath = `${configPath}.cli-ai-backup`;
262
+ copyFileSync(configPath, backupPath);
263
+ log(`Created backup: ${backupPath}`, 'info');
264
+
265
+ // Clean up empty lines
266
+ newContent = newContent.replace(/\n{3,}/g, '\n\n').trim() + '\n';
267
+ writeFileSync(configPath, newContent);
268
+ log(`Removed alias from: ${configPath}`, 'success');
269
+ aliasesRemoved++;
270
+ }
271
+ } catch (error) {
272
+ log(`Could not process ${configPath}: ${error.message}`, 'warn');
273
+ }
274
+ }
275
+
276
+ if (aliasesRemoved === 0) {
277
+ log('No shell aliases found', 'info');
278
+ }
279
+
280
+ return true;
281
+ }
282
+
283
+ /**
284
+ * Main uninstall flow
285
+ */
286
+ async function main() {
287
+ console.log('\n\x1b[1m\x1b[35m🗑️ CLI AI Uninstaller\x1b[0m\n');
288
+
289
+ if (!forceMode) {
290
+ console.log('This will remove:');
291
+ console.log(' • API key from system keyring');
292
+ console.log(` • Config directory (~/${CONFIG_DIR_NAME})`);
293
+ console.log(' • Global package link (if exists)');
294
+ console.log(' • Shell aliases (if configured)\n');
295
+
296
+ const proceed = await confirm('Do you want to proceed?');
297
+ if (!proceed) {
298
+ log('Uninstall cancelled', 'info');
299
+ process.exit(0);
300
+ }
301
+ console.log();
302
+ }
303
+
304
+ const results = {
305
+ keyring: await removeKeyringEntry(),
306
+ config: removeConfigDirectory(),
307
+ globalLink: removeGlobalLink(),
308
+ aliases: removeShellAliases(),
309
+ };
310
+
311
+ console.log('\n\x1b[1m--- Summary ---\x1b[0m\n');
312
+
313
+ const allSuccess = Object.values(results).every(Boolean);
314
+
315
+ if (allSuccess) {
316
+ log('CLI AI has been completely removed from your system!', 'success');
317
+ console.log('\nNote: If you installed via npm/pnpm globally, you may also want to run:');
318
+ console.log(' npm uninstall -g @fmdz387/cli-ai');
319
+ console.log(' # or');
320
+ console.log(' pnpm uninstall -g @fmdz387/cli-ai\n');
321
+ } else {
322
+ log('Uninstall completed with some warnings (see above)', 'warn');
323
+ }
324
+
325
+ // Remind about shell restart
326
+ console.log('\x1b[33mRestart your terminal for changes to take effect.\x1b[0m\n');
327
+ }
328
+
329
+ main().catch((error) => {
330
+ log(`Uninstall failed: ${error.message}`, 'error');
331
+ process.exit(1);
332
+ });