@atharvaspatil5/aish 1.0.1
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/README.md +70 -0
- package/bin/ai.js +49 -0
- package/package.json +22 -0
- package/src/executor.js +28 -0
- package/src/index.js +1 -0
- package/src/llm.js +63 -0
package/README.md
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# AI.sh
|
|
2
|
+
|
|
3
|
+
AI.sh is a CLI tool that converts natural language into safe shell commands.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
ai "list files"
|
|
9
|
+
ai "delete node_modules"
|
|
10
|
+
ai "find files larger than 100MB"
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Options
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
-y, --yes skip confirmation prompt
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## How it works
|
|
20
|
+
|
|
21
|
+
1. Takes user input from CLI
|
|
22
|
+
2. Sends it to Groq (LLM)
|
|
23
|
+
3. Converts it into a shell command
|
|
24
|
+
4. Validates the command for safety
|
|
25
|
+
5. Asks for confirmation (optional)
|
|
26
|
+
6. Executes the command
|
|
27
|
+
|
|
28
|
+
## Example
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
ai "list all files"
|
|
32
|
+
# → ls
|
|
33
|
+
|
|
34
|
+
ai "delete everything"
|
|
35
|
+
# → rm -i *
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Safety
|
|
39
|
+
|
|
40
|
+
* Blocks critical system-level commands
|
|
41
|
+
* Warns on risky operations (like file deletion)
|
|
42
|
+
* Requires confirmation before execution
|
|
43
|
+
|
|
44
|
+
## Setup
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npm install
|
|
48
|
+
npm link
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Create a `.env` file:
|
|
52
|
+
|
|
53
|
+
```env
|
|
54
|
+
GROQ_API_KEY=your_api_key
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Structure
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
bin/ CLI entry
|
|
61
|
+
src/ core logic
|
|
62
|
+
llm.js
|
|
63
|
+
validator.js
|
|
64
|
+
executor.js
|
|
65
|
+
index.js
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Notes
|
|
69
|
+
|
|
70
|
+
This is a local CLI tool. Use carefully when executing destructive commands.
|
package/bin/ai.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Command } from "commander";
|
|
4
|
+
import chalk from "chalk";
|
|
5
|
+
import { getCommand } from "../src/llm.js";
|
|
6
|
+
import { askConfirmation, runCommand } from "../src/executor.js";
|
|
7
|
+
|
|
8
|
+
const program = new Command();
|
|
9
|
+
|
|
10
|
+
program
|
|
11
|
+
.name("ai")
|
|
12
|
+
.description("AI-powered shell assistant")
|
|
13
|
+
.argument("<query>", "What you want to do")
|
|
14
|
+
.option("-y, --yes", "Skip confirmation prompt")
|
|
15
|
+
.action(async (query, options) => {
|
|
16
|
+
const result = await getCommand(query);
|
|
17
|
+
|
|
18
|
+
let coloredCommand;
|
|
19
|
+
if (result.risk_level === "danger") {
|
|
20
|
+
coloredCommand = chalk.red(result.command);
|
|
21
|
+
} else if (result.risk_level === "warning") {
|
|
22
|
+
coloredCommand = chalk.yellow(result.command);
|
|
23
|
+
} else {
|
|
24
|
+
coloredCommand = chalk.green(result.command);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
console.log(coloredCommand);
|
|
28
|
+
|
|
29
|
+
let confirm = true;
|
|
30
|
+
|
|
31
|
+
if (!options.yes || result.risk_level === "danger") {
|
|
32
|
+
confirm = await askConfirmation("Run this? (y/n) ");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (!confirm) {
|
|
36
|
+
console.log(chalk.blue("Command execution cancelled."));
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
const output = await runCommand(result.command);
|
|
42
|
+
console.log(output);
|
|
43
|
+
} catch (error) {
|
|
44
|
+
console.error(chalk.red("Error: "));
|
|
45
|
+
console.error(chalk.red(error.message));
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
program.parse();
|
package/package.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@atharvaspatil5/aish",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "AI-powered CLI that converts natural language to shell commands",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
8
|
+
},
|
|
9
|
+
"keywords": [],
|
|
10
|
+
"author": "athPATIL247",
|
|
11
|
+
"license": "ISC",
|
|
12
|
+
"bin": {
|
|
13
|
+
"ai": "./bin/ai.js"
|
|
14
|
+
},
|
|
15
|
+
"type": "module",
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"chalk": "^5.6.2",
|
|
18
|
+
"commander": "^14.0.3",
|
|
19
|
+
"dotenv": "^17.4.0",
|
|
20
|
+
"groq-sdk": "^1.1.2"
|
|
21
|
+
}
|
|
22
|
+
}
|
package/src/executor.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { exec } from "child_process";
|
|
2
|
+
import readline from "readline";
|
|
3
|
+
|
|
4
|
+
export function askConfirmation(question) {
|
|
5
|
+
const rl = readline.createInterface({
|
|
6
|
+
input: process.stdin,
|
|
7
|
+
output: process.stdout,
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
return new Promise((resolve) => {
|
|
11
|
+
rl.question(question, (answer) => {
|
|
12
|
+
rl.close();
|
|
13
|
+
resolve(answer.toLowerCase() === "y");
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function runCommand(command) {
|
|
19
|
+
return new Promise((resolve, reject) => {
|
|
20
|
+
exec(command, (error, stdout, stderr) => {
|
|
21
|
+
if (error) {
|
|
22
|
+
reject(stderr || error.message);
|
|
23
|
+
} else {
|
|
24
|
+
resolve(stdout);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
// Entry point is bin/ai.js
|
package/src/llm.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import Groq from "groq-sdk";
|
|
2
|
+
import { readFileSync } from 'fs';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import { dirname, join } from 'path';
|
|
5
|
+
|
|
6
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = dirname(__filename);
|
|
8
|
+
|
|
9
|
+
const envPath = join(__dirname, '..', '.env');
|
|
10
|
+
const envContent = readFileSync(envPath, 'utf8');
|
|
11
|
+
const envLines = envContent.split('\n');
|
|
12
|
+
for (const line of envLines) {
|
|
13
|
+
const [key, ...valueParts] = line.split('=');
|
|
14
|
+
if (key && valueParts.length) {
|
|
15
|
+
process.env[key.trim()] = valueParts.join('=').trim();
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const groq = new Groq({
|
|
20
|
+
apiKey: process.env.GROQ_API_KEY,
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
export async function getCommand(query){
|
|
24
|
+
const response = await groq.chat.completions.create({
|
|
25
|
+
model: "llama-3.1-8b-instant",
|
|
26
|
+
messages: [
|
|
27
|
+
{
|
|
28
|
+
role: "system",
|
|
29
|
+
content: `You are a Linux shell expert. Convert user instructions into a single shell command and assess its risk level.
|
|
30
|
+
|
|
31
|
+
Output ONLY a JSON object with exactly these fields:
|
|
32
|
+
- command: the shell command (string)
|
|
33
|
+
- risk_level: "safe", "warning", or "danger" (string)
|
|
34
|
+
|
|
35
|
+
Do not output any other text, explanations, code blocks, or notes. Just the raw JSON.`,
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
role: "user",
|
|
39
|
+
content: query,
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const content = response.choices[0].message.content.trim();
|
|
45
|
+
// Remove markdown code blocks if present
|
|
46
|
+
let jsonString = content;
|
|
47
|
+
if (jsonString.startsWith('```json')) {
|
|
48
|
+
jsonString = jsonString.replace(/^```json\s*/, '').replace(/\s*```$/, '');
|
|
49
|
+
} else if (jsonString.startsWith('```')) {
|
|
50
|
+
jsonString = jsonString.replace(/^```\s*/, '').replace(/\s*```$/, '');
|
|
51
|
+
}
|
|
52
|
+
try {
|
|
53
|
+
const result = JSON.parse(jsonString);
|
|
54
|
+
return result;
|
|
55
|
+
} catch (e) {
|
|
56
|
+
// Fallback: assume safe if parsing fails
|
|
57
|
+
return {
|
|
58
|
+
command: content,
|
|
59
|
+
risk_level: "safe",
|
|
60
|
+
reason: "Unable to assess risk"
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
}
|