@seberto/agcp 1.0.0 → 1.0.2

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 ADDED
@@ -0,0 +1,36 @@
1
+ # agcp
2
+
3
+ Turn plain English into shell commands using your local AI.
4
+
5
+ ## Requirements
6
+
7
+ - [Ollama](https://ollama.com) running locally
8
+ - At least one model pulled (e.g. `ollama pull gemma3`)
9
+ - Node.js 18+
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ npm install -g @seberto/agcp
15
+ ```
16
+
17
+ ## Usage
18
+
19
+ ```bash
20
+ agcp "show all running processes"
21
+ agcp "commit with message bug fixed"
22
+ agcp "find all js files modified today"
23
+ ```
24
+
25
+ agcp will generate the command, show it to you, and ask before running it.
26
+
27
+ ## How it works
28
+
29
+ 1. You describe what you want in plain English
30
+ 2. agcp sends it to your local Ollama instance
31
+ 3. The suggested command is shown
32
+ 4. You confirm before it runs — nothing executes without your approval
33
+
34
+ ## License
35
+
36
+ ISC
package/bin/agcp.js CHANGED
@@ -4,7 +4,7 @@ import { Command } from "commander";
4
4
  import chalk from "chalk";
5
5
  import { confirm } from "@inquirer/prompts";
6
6
  import { execSync } from "child_process";
7
- import generateCommand from "../service/ollama.js";
7
+ import generateCommand, { isSafe } from "../service/ollama.js";
8
8
 
9
9
  const program = new Command();
10
10
 
@@ -19,6 +19,16 @@ program
19
19
 
20
20
  const command = await generateCommand(prompt);
21
21
 
22
+ if (command === "UNSAFE") {
23
+ console.log(chalk.red("\n✖ That request is not development related or is unsafe. Only dev commands are supported."));
24
+ process.exit(1);
25
+ }
26
+
27
+ if (!isSafe(command)) {
28
+ console.log(chalk.red(`\n✖ Blocked unsafe command: ${chalk.bold(command)}`));
29
+ process.exit(1);
30
+ }
31
+
22
32
  console.log(chalk.cyan(`\n💡 Suggested command:\n ${chalk.bold(command)}\n`));
23
33
 
24
34
  const shouldRun = await confirm({
@@ -28,7 +38,12 @@ program
28
38
 
29
39
  if (shouldRun) {
30
40
  console.log(chalk.green("\n▶ Running...\n"));
31
- execSync(command, { stdio: "inherit" });
41
+ try {
42
+ execSync(command, { stdio: "inherit", shell: true });
43
+ } catch {
44
+ // command already printed its output, non-zero exit is not our error
45
+ }
46
+
32
47
  } else {
33
48
  console.log(chalk.gray("\nAborted."));
34
49
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seberto/agcp",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "AI CLI that generates and runs shell commands",
5
5
  "type": "module",
6
6
  "bin": {
@@ -8,7 +8,8 @@
8
8
  },
9
9
  "files": [
10
10
  "bin",
11
- "service"
11
+ "service",
12
+ "README.md"
12
13
  ],
13
14
  "scripts": {
14
15
  "start": "node ./bin/agcp.js"
package/service/ollama.js CHANGED
@@ -1,4 +1,32 @@
1
1
  import axios from "axios";
2
+ import { platform } from "os";
3
+
4
+ const OS_MAP = { darwin: "macOS", linux: "Linux", win32: "Windows" };
5
+ const os = OS_MAP[platform()] ?? platform();
6
+ const shell = platform() === "win32" ? "PowerShell" : "bash";
7
+
8
+ // Commands that are never allowed to run
9
+ const BLOCKED = [
10
+ /rm\s+-rf/,
11
+ /rm\s+-fr/,
12
+ /rmdir/,
13
+ /mkfs/,
14
+ /dd\s+if=/,
15
+ /:\(\)\{.*\}/, // fork bomb
16
+ /chmod\s+-R\s+777/,
17
+ /chown\s+-R/,
18
+ /shutdown/,
19
+ /reboot/,
20
+ /halt/,
21
+ /curl.*\|\s*sh/, // curl pipe to shell
22
+ /wget.*\|\s*sh/,
23
+ /sudo\s+rm/,
24
+ />\s*\/dev\//, // writing to devices
25
+ ];
26
+
27
+ export function isSafe(command) {
28
+ return !BLOCKED.some((pattern) => pattern.test(command));
29
+ }
2
30
 
3
31
  async function generateCommand(userPrompt) {
4
32
  const { data } = await axios.get("http://localhost:11434/api/tags");
@@ -6,7 +34,19 @@ async function generateCommand(userPrompt) {
6
34
 
7
35
  if (!model) throw new Error("No Ollama models found. Run 'ollama pull <model>' first.");
8
36
 
9
- const prompt = `Convert the following text into a shell command. Return only the command, no explanation, no markdown.\n\n${userPrompt}`;
37
+ const prompt = `You are a development shell command assistant for ${os} using ${shell}.
38
+
39
+ Allowed topics: git, npm, yarn, node, file navigation, code editors, docker, build tools, package managers, and general software development tasks.
40
+
41
+ Rules:
42
+ - Output a single shell command only
43
+ - No markdown, no backticks, no code blocks, no explanation
44
+ - One line only
45
+ - Only generate commands related to software development
46
+ - Never generate destructive commands like rm -rf, shutdown, reboot, mkfs, dd, fork bombs, or piping curl/wget to shell
47
+ - If the request is not development related, respond with exactly: UNSAFE
48
+
49
+ User request: ${userPrompt}`;
10
50
 
11
51
  const response = await axios.post("http://localhost:11434/api/generate", {
12
52
  model,
@@ -14,7 +54,17 @@ async function generateCommand(userPrompt) {
14
54
  stream: false,
15
55
  });
16
56
 
17
- return response.data.response.trim();
57
+ const raw = response.data.response.trim();
58
+
59
+ // Strip accidental markdown the model may add
60
+ const cleaned = raw
61
+ .replace(/^```[\w]*\n?/m, "")
62
+ .replace(/```$/m, "")
63
+ .replace(/^`|`$/g, "")
64
+ .split("\n")[0]
65
+ .trim();
66
+
67
+ return cleaned;
18
68
  }
19
69
 
20
70
  export default generateCommand;