@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 +15 -0
- package/README.md +169 -0
- package/dist/cli.js +13 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +65 -0
- package/package.json +111 -0
- package/scripts/uninstall.js +332 -0
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
|
+
[](https://www.npmjs.com/package/@fmdz387/cli-ai)
|
|
4
|
+
[](https://opensource.org/licenses/ISC)
|
|
5
|
+
[](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
|
+

|
|
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');
|
package/dist/index.d.ts
ADDED
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
|
+
});
|