@webdeb/gogi 1.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/README.md +91 -0
- package/dist/agent.js +150 -0
- package/dist/auth.js +95 -0
- package/dist/config.js +61 -0
- package/dist/index.js +47 -0
- package/package.json +45 -0
package/README.md
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# 🍖 Gogi CLI
|
|
2
|
+
|
|
3
|
+
Gogi is a lightweight, AI-powered terminal assistant built with Node.js. It runs locally on your machine and helps you accomplish tasks directly in your terminal by translating natural language into executable shell commands.
|
|
4
|
+
|
|
5
|
+
Unlike other CLI assistants that require you to manage your own paid API keys, Gogi leverages the open-source `@mariozechner/pi-ai` library to implement **native Device-Code OAuth flows**. This means you can plug Gogi directly into your existing AI subscriptions (like ChatGPT Plus or GitHub Copilot)
|
|
6
|
+
|
|
7
|
+
## ✨ Features
|
|
8
|
+
|
|
9
|
+
- **Natural Language to Shell**: Tell Gogi what you want to do (e.g., `gogi how much space is left on my machine`) and it will propose the correct terminal command.
|
|
10
|
+
- **Safe Execution Loop**: Gogi intercepts the AI's proposed commands and explicitly asks for your permission (`Allow? (y/N)`) before running anything. It will capture the `stdout` and `stderr` and feed it back to the LLM so it can learn from mistakes or chain commands together.
|
|
11
|
+
- **Multi-Provider OAuth**: Sign in securely using your existing AI accounts. Supported providers include:
|
|
12
|
+
- `codex` (ChatGPT Plus/Pro subscription)
|
|
13
|
+
- `gemini` (Google Cloud)
|
|
14
|
+
- `github` (GitHub Copilot)
|
|
15
|
+
- **Auto-Generated System Context**: On first run, Gogi automatically profiles your machine (OS, architecture, shell) and generates a `~/.gogi/system.md` file. It injects this into the LLM's system prompt so the AI always knows what operating system and shell it's working with.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## 🚀 Installation
|
|
20
|
+
|
|
21
|
+
1. Clone or download the repository.
|
|
22
|
+
2. Install dependencies:
|
|
23
|
+
```bash
|
|
24
|
+
npm install
|
|
25
|
+
```
|
|
26
|
+
3. Build the TypeScript source and link it globally:
|
|
27
|
+
```bash
|
|
28
|
+
npm run build
|
|
29
|
+
npm link
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
You can now use the `gogi` command from anywhere in your terminal!
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## 🔑 Authentication
|
|
37
|
+
|
|
38
|
+
Before using Gogi, you must authenticate it with a provider.
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
# Login with ChatGPT (Default)
|
|
42
|
+
gogi login
|
|
43
|
+
|
|
44
|
+
# Or specify a provider:
|
|
45
|
+
gogi login codex
|
|
46
|
+
gogi login gemini
|
|
47
|
+
gogi login github
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
This will trigger a standard OAuth flow. Gogi will open your browser, ask you to log in, and then securely store the resulting `access_token` in `~/.gogi/config.json`.
|
|
51
|
+
|
|
52
|
+
Once you have logged into multiple providers, you can quickly switch the active provider being used for commands:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
gogi provider gemini
|
|
56
|
+
gogi provider codex
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## 💡 Usage
|
|
62
|
+
|
|
63
|
+
Simply type `gogi` followed by your request:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
gogi find all the .ts files in the current directory
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
🤔 Gogi is thinking...
|
|
71
|
+
✔ Gogi wants to run:
|
|
72
|
+
> find . -name "*.ts"
|
|
73
|
+
Allow? Yes
|
|
74
|
+
|
|
75
|
+
Running...
|
|
76
|
+
./src/index.ts
|
|
77
|
+
./src/config.ts
|
|
78
|
+
./src/agent.ts
|
|
79
|
+
./src/auth.ts
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
If a command fails (e.g., a missing dependency), Gogi will read the error output and can automatically propose a follow-up command to fix the issue!
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## ⚙️ Configuration
|
|
87
|
+
|
|
88
|
+
Gogi stores its configuration in your home directory at `~/.gogi/`:
|
|
89
|
+
|
|
90
|
+
- `~/.gogi/config.json`: Stores your active provider and OAuth access tokens securely.
|
|
91
|
+
- `~/.gogi/system.md`: A markdown file containing context about your machine (OS, CPU, Shell). You can edit this file to give Gogi additional context or permanent custom instructions (e.g., "Always use fd instead of find").
|
package/dist/agent.js
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.runAgent = runAgent;
|
|
7
|
+
const pi_ai_1 = require("@mariozechner/pi-ai");
|
|
8
|
+
const config_1 = require("./config");
|
|
9
|
+
const prompts_1 = require("@inquirer/prompts");
|
|
10
|
+
const child_process_1 = require("child_process");
|
|
11
|
+
const util_1 = __importDefault(require("util"));
|
|
12
|
+
const execPromise = util_1.default.promisify(child_process_1.exec);
|
|
13
|
+
async function runAgent(prompt) {
|
|
14
|
+
const config = (0, config_1.getConfig)();
|
|
15
|
+
let apiKey = config.openaiApiKey;
|
|
16
|
+
let providerToUse = 'codex';
|
|
17
|
+
if (config.activeProvider && config.tokens && config.tokens[config.activeProvider]) {
|
|
18
|
+
apiKey = config.tokens[config.activeProvider];
|
|
19
|
+
providerToUse = config.activeProvider;
|
|
20
|
+
}
|
|
21
|
+
if (!apiKey) {
|
|
22
|
+
console.error('❌ No API key or token found. Please run `gogi login` first.');
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
let model;
|
|
26
|
+
if (providerToUse === 'codex') {
|
|
27
|
+
model = (0, pi_ai_1.getModel)('openai-codex', 'gpt-5.1-codex-mini');
|
|
28
|
+
}
|
|
29
|
+
else if (providerToUse === 'gemini') {
|
|
30
|
+
model = (0, pi_ai_1.getModel)('google-gemini-cli', 'gemini-2.5-flash');
|
|
31
|
+
}
|
|
32
|
+
else if (providerToUse === 'github') {
|
|
33
|
+
model = (0, pi_ai_1.getModel)('github-copilot', 'gpt-4o');
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
console.warn(`Unknown provider ${providerToUse}, falling back to openai gpt-4o`);
|
|
37
|
+
model = (0, pi_ai_1.getModel)('openai', 'gpt-4o');
|
|
38
|
+
}
|
|
39
|
+
const tools = [
|
|
40
|
+
{
|
|
41
|
+
name: 'run_terminal_command',
|
|
42
|
+
description: 'Propose a terminal command for the user to execute.',
|
|
43
|
+
parameters: pi_ai_1.Type.Object({
|
|
44
|
+
command: pi_ai_1.Type.String({ description: 'The shell command to propose to the user. Do not wrap in markdown quotes.' })
|
|
45
|
+
})
|
|
46
|
+
}
|
|
47
|
+
];
|
|
48
|
+
const systemDetails = (0, config_1.getSystemContext)();
|
|
49
|
+
const context = {
|
|
50
|
+
systemPrompt: `You are Gogi, a helpful terminal assistant running on a Mac terminal.
|
|
51
|
+
You can propose shell commands to fulfill user requests using the \`run_terminal_command\` tool.
|
|
52
|
+
Wait for the execution result before continuing. If a command fails, try to suggest an alternative or fix the issue.
|
|
53
|
+
Keep your textual responses very concise unless asked to explain.
|
|
54
|
+
|
|
55
|
+
=== SYSTEM CONTEXT ===
|
|
56
|
+
${systemDetails}
|
|
57
|
+
====================`,
|
|
58
|
+
messages: [
|
|
59
|
+
{ role: 'user', content: prompt, timestamp: Date.now() }
|
|
60
|
+
],
|
|
61
|
+
tools
|
|
62
|
+
};
|
|
63
|
+
console.log('🤔 Gogi is thinking...');
|
|
64
|
+
while (true) {
|
|
65
|
+
let response;
|
|
66
|
+
try {
|
|
67
|
+
response = await (0, pi_ai_1.complete)(model, context, { apiKey: config.openaiApiKey });
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
console.error(`❌ API Error: ${err.message}`);
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
context.messages.push(response);
|
|
74
|
+
let hasToolCalls = false;
|
|
75
|
+
if (response.content) {
|
|
76
|
+
for (const block of response.content) {
|
|
77
|
+
if (block.type === 'text') {
|
|
78
|
+
console.log(`\n🤖 ${block.text}`);
|
|
79
|
+
}
|
|
80
|
+
else if (block.type === 'toolCall' && block.name === 'run_terminal_command') {
|
|
81
|
+
hasToolCalls = true;
|
|
82
|
+
// @mariozechner/pi-ai automatically parses arguments
|
|
83
|
+
const cmd = block.arguments.command;
|
|
84
|
+
let answer = false;
|
|
85
|
+
try {
|
|
86
|
+
answer = await (0, prompts_1.confirm)({ message: `Gogi wants to run:\n > \x1b[36m${cmd}\x1b[0m\nAllow?`, default: false });
|
|
87
|
+
}
|
|
88
|
+
catch (e) {
|
|
89
|
+
if (e.name === 'ExitPromptError') {
|
|
90
|
+
console.log('\nGoodbye!');
|
|
91
|
+
process.exit(0);
|
|
92
|
+
}
|
|
93
|
+
throw e;
|
|
94
|
+
}
|
|
95
|
+
if (answer) {
|
|
96
|
+
console.log('\nRunning...');
|
|
97
|
+
try {
|
|
98
|
+
const { stdout, stderr } = await execPromise(cmd);
|
|
99
|
+
let toolOutput = '';
|
|
100
|
+
if (stdout) {
|
|
101
|
+
console.log(stdout); // Log STDOUT to the terminal directly
|
|
102
|
+
toolOutput += `STDOUT:\n${stdout}\n`;
|
|
103
|
+
}
|
|
104
|
+
if (stderr) {
|
|
105
|
+
console.error(`\x1b[31m${stderr}\x1b[0m`); // Log STDERR in red
|
|
106
|
+
toolOutput += `STDERR:\n${stderr}\n`;
|
|
107
|
+
}
|
|
108
|
+
if (!toolOutput) {
|
|
109
|
+
toolOutput = "Command executed successfully with no output.";
|
|
110
|
+
}
|
|
111
|
+
context.messages.push({
|
|
112
|
+
role: 'toolResult',
|
|
113
|
+
toolCallId: block.id,
|
|
114
|
+
toolName: block.name,
|
|
115
|
+
content: [{ type: 'text', text: toolOutput }],
|
|
116
|
+
isError: false,
|
|
117
|
+
timestamp: Date.now()
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
catch (err) {
|
|
121
|
+
console.error(`\x1b[31mExecution Error: ${err.message}\x1b[0m`);
|
|
122
|
+
context.messages.push({
|
|
123
|
+
role: 'toolResult',
|
|
124
|
+
toolCallId: block.id,
|
|
125
|
+
toolName: block.name,
|
|
126
|
+
content: [{ type: 'text', text: `Error: ${err.message}` }],
|
|
127
|
+
isError: true,
|
|
128
|
+
timestamp: Date.now()
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
console.log('❌ Command denied.');
|
|
134
|
+
context.messages.push({
|
|
135
|
+
role: 'toolResult',
|
|
136
|
+
toolCallId: block.id,
|
|
137
|
+
toolName: block.name,
|
|
138
|
+
content: [{ type: 'text', text: 'The user denied the execution of this command.' }],
|
|
139
|
+
isError: true, // Mark as error so the model knows the command was rejected
|
|
140
|
+
timestamp: Date.now()
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (!hasToolCalls) {
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
package/dist/auth.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.loginFlow = loginFlow;
|
|
40
|
+
const pi_ai_1 = require("@mariozechner/pi-ai");
|
|
41
|
+
const config_1 = require("./config");
|
|
42
|
+
const open_1 = __importDefault(require("open"));
|
|
43
|
+
const readline = __importStar(require("readline"));
|
|
44
|
+
async function loginFlow(provider = 'codex') {
|
|
45
|
+
console.log(`Starting login flow for provider: ${provider}...`);
|
|
46
|
+
let credentials;
|
|
47
|
+
if (provider === 'codex') {
|
|
48
|
+
credentials = await (0, pi_ai_1.loginOpenAICodex)({
|
|
49
|
+
onAuth: (info) => {
|
|
50
|
+
console.log(`\n🌐 Opening browser for sign-in... If it doesn't open automatically, visit:\n${info.url}`);
|
|
51
|
+
if (info.instructions)
|
|
52
|
+
console.log(info.instructions);
|
|
53
|
+
(0, open_1.default)(info.url);
|
|
54
|
+
},
|
|
55
|
+
onPrompt: async (prompt) => {
|
|
56
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
57
|
+
return new Promise((resolve) => rl.question(`${prompt.message} `, (answer) => { rl.close(); resolve(answer); }));
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
else if (provider === 'gemini') {
|
|
62
|
+
credentials = await (0, pi_ai_1.loginGeminiCli)((info) => {
|
|
63
|
+
console.log(`\n🌐 Opening browser for sign-in... If it doesn't open automatically, visit:\n${info.url}`);
|
|
64
|
+
if (info.instructions)
|
|
65
|
+
console.log(info.instructions);
|
|
66
|
+
(0, open_1.default)(info.url);
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
else if (provider === 'github') {
|
|
70
|
+
credentials = await (0, pi_ai_1.loginGitHubCopilot)({
|
|
71
|
+
onAuth: (url, instructions) => {
|
|
72
|
+
console.log(`\n🌐 Please authenticate GitHub Copilot.`);
|
|
73
|
+
if (instructions)
|
|
74
|
+
console.log(instructions);
|
|
75
|
+
(0, open_1.default)(url);
|
|
76
|
+
},
|
|
77
|
+
onPrompt: async (prompt) => {
|
|
78
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
79
|
+
return new Promise((resolve) => rl.question(`${prompt.message} `, (answer) => { rl.close(); resolve(answer); }));
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
console.error(`❌ Unknown provider: ${provider}. Supported providers: codex, gemini, github.`);
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
const currentConfig = (0, config_1.getConfig)();
|
|
88
|
+
const tokens = currentConfig.tokens || {};
|
|
89
|
+
tokens[provider] = credentials.access;
|
|
90
|
+
(0, config_1.saveConfig)({
|
|
91
|
+
activeProvider: provider,
|
|
92
|
+
tokens
|
|
93
|
+
});
|
|
94
|
+
console.log(`✅ Successfully authenticated Gogi via ${provider}!`);
|
|
95
|
+
}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getConfig = getConfig;
|
|
7
|
+
exports.saveConfig = saveConfig;
|
|
8
|
+
exports.hasValidConfig = hasValidConfig;
|
|
9
|
+
exports.getSystemContext = getSystemContext;
|
|
10
|
+
const fs_1 = __importDefault(require("fs"));
|
|
11
|
+
const path_1 = __importDefault(require("path"));
|
|
12
|
+
const os_1 = __importDefault(require("os"));
|
|
13
|
+
const GOGI_DIR = path_1.default.join(os_1.default.homedir(), '.gogi');
|
|
14
|
+
const CONFIG_FILE = path_1.default.join(GOGI_DIR, 'config.json');
|
|
15
|
+
const SYSTEM_FILE = path_1.default.join(GOGI_DIR, 'system.md');
|
|
16
|
+
function getConfig() {
|
|
17
|
+
if (!fs_1.default.existsSync(CONFIG_FILE)) {
|
|
18
|
+
return { tokens: {} };
|
|
19
|
+
}
|
|
20
|
+
try {
|
|
21
|
+
const data = fs_1.default.readFileSync(CONFIG_FILE, 'utf-8');
|
|
22
|
+
const parsed = JSON.parse(data);
|
|
23
|
+
if (!parsed.tokens)
|
|
24
|
+
parsed.tokens = {};
|
|
25
|
+
return parsed;
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return { tokens: {} };
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function saveConfig(config) {
|
|
32
|
+
if (!fs_1.default.existsSync(GOGI_DIR)) {
|
|
33
|
+
fs_1.default.mkdirSync(GOGI_DIR, { recursive: true });
|
|
34
|
+
}
|
|
35
|
+
const currentConfig = getConfig();
|
|
36
|
+
const newConfig = { ...currentConfig, ...config };
|
|
37
|
+
fs_1.default.writeFileSync(CONFIG_FILE, JSON.stringify(newConfig, null, 2));
|
|
38
|
+
}
|
|
39
|
+
function hasValidConfig() {
|
|
40
|
+
const config = getConfig();
|
|
41
|
+
if (config.openaiApiKey)
|
|
42
|
+
return true; // legacy
|
|
43
|
+
return !!(config.activeProvider && config.tokens && config.tokens[config.activeProvider]);
|
|
44
|
+
}
|
|
45
|
+
function getSystemContext() {
|
|
46
|
+
if (!fs_1.default.existsSync(SYSTEM_FILE)) {
|
|
47
|
+
const defaultContent = `You are running on a ${os_1.default.type()} (${os_1.default.release()}) machine.
|
|
48
|
+
Architecture: ${os_1.default.arch()}
|
|
49
|
+
CPU: ${os_1.default.cpus()[0]?.model}
|
|
50
|
+
Home directory: ${os_1.default.homedir()}
|
|
51
|
+
Default shell: ${process.env.SHELL || 'unknown'}
|
|
52
|
+
|
|
53
|
+
Please tailor your commands and suggestions to this specific environment.`;
|
|
54
|
+
if (!fs_1.default.existsSync(GOGI_DIR)) {
|
|
55
|
+
fs_1.default.mkdirSync(GOGI_DIR, { recursive: true });
|
|
56
|
+
}
|
|
57
|
+
fs_1.default.writeFileSync(SYSTEM_FILE, defaultContent, 'utf-8');
|
|
58
|
+
return defaultContent;
|
|
59
|
+
}
|
|
60
|
+
return fs_1.default.readFileSync(SYSTEM_FILE, 'utf-8');
|
|
61
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const commander_1 = require("commander");
|
|
5
|
+
const auth_1 = require("./auth");
|
|
6
|
+
const agent_1 = require("./agent");
|
|
7
|
+
const config_1 = require("./config");
|
|
8
|
+
const program = new commander_1.Command();
|
|
9
|
+
program
|
|
10
|
+
.name('gogi')
|
|
11
|
+
.description('Gogi CLI: An AI-powered terminal assistant')
|
|
12
|
+
.version('1.0.0');
|
|
13
|
+
program
|
|
14
|
+
.command('login [provider]')
|
|
15
|
+
.description('Authenticate Gogi with a provider (codex, gemini, github)')
|
|
16
|
+
.action(async (provider) => {
|
|
17
|
+
await (0, auth_1.loginFlow)(provider || 'codex');
|
|
18
|
+
});
|
|
19
|
+
program
|
|
20
|
+
.command('provider <provider>')
|
|
21
|
+
.description('Switch the active provider (codex, gemini, github)')
|
|
22
|
+
.action((provider) => {
|
|
23
|
+
const config = (0, config_1.getConfig)();
|
|
24
|
+
const tokens = config.tokens || {};
|
|
25
|
+
if (!tokens[provider] && !(provider === 'codex' && config.openaiApiKey)) {
|
|
26
|
+
console.error(`❌ No token found for provider '${provider}'. Please run \`gogi login ${provider}\` first.`);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
(0, config_1.saveConfig)({ activeProvider: provider });
|
|
30
|
+
console.log(`✅ Active provider switched to ${provider}`);
|
|
31
|
+
});
|
|
32
|
+
program
|
|
33
|
+
.argument('[prompt...]', 'The task you want gogi to perform')
|
|
34
|
+
.action(async (promptArr) => {
|
|
35
|
+
const prompt = promptArr.join(' ').trim();
|
|
36
|
+
if (!prompt) {
|
|
37
|
+
// If no prompt, just output help
|
|
38
|
+
program.help();
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (!(0, config_1.hasValidConfig)()) {
|
|
42
|
+
console.log('You need to log in first. Running `gogi login`...');
|
|
43
|
+
await (0, auth_1.loginFlow)();
|
|
44
|
+
}
|
|
45
|
+
await (0, agent_1.runAgent)(prompt);
|
|
46
|
+
});
|
|
47
|
+
program.parse(process.argv);
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@webdeb/gogi",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Gogi CLI: An AI-powered terminal assistant with native device-code OAuth.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"gogi": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsc",
|
|
14
|
+
"start": "node ./dist/index.js",
|
|
15
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
16
|
+
"prepublishOnly": "npm run build"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"cli",
|
|
20
|
+
"ai",
|
|
21
|
+
"openai",
|
|
22
|
+
"gemini",
|
|
23
|
+
"github",
|
|
24
|
+
"copilot",
|
|
25
|
+
"terminal",
|
|
26
|
+
"assistant"
|
|
27
|
+
],
|
|
28
|
+
"author": "Boris Kotov <bk@webdeb.de> (http://www.webdeb.de/)",
|
|
29
|
+
"license": "ISC",
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "https://github.com/webdeb/gogi.git"
|
|
33
|
+
},
|
|
34
|
+
"type": "commonjs",
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@inquirer/prompts": "^8.3.0",
|
|
37
|
+
"@mariozechner/pi-ai": "^0.54.2",
|
|
38
|
+
"@types/node": "^25.3.0",
|
|
39
|
+
"commander": "^14.0.3",
|
|
40
|
+
"dotenv": "^17.3.1",
|
|
41
|
+
"open": "^11.0.0",
|
|
42
|
+
"ts-node": "^10.9.2",
|
|
43
|
+
"typescript": "^5.9.3"
|
|
44
|
+
}
|
|
45
|
+
}
|