├── .nvmrc ├── .prettierrc.json ├── .vscode └── settings.json ├── .gitignore ├── src ├── helpers │ ├── constants.ts │ ├── stream-to-string.ts │ ├── strip-regex-patterns.ts │ ├── replace-all-polyfill.ts │ ├── os-detect.ts │ ├── stream-to-iterable.ts │ ├── error.ts │ ├── shell-history.ts │ ├── i18n.ts │ ├── config.ts │ └── completion.ts ├── commands │ ├── update.ts │ ├── config.ts │ └── chat.ts ├── cli.ts ├── locales │ ├── zh-Hans.json │ ├── zh-Hant.json │ ├── ko.json │ ├── jp.json │ ├── ar.json │ ├── en.json │ ├── tr.json │ ├── id.json │ ├── vi.json │ ├── uk.json │ ├── pt.json │ ├── ru.json │ ├── es.json │ ├── fr.json │ └── de.json └── prompt.ts ├── tsconfig.json ├── .github └── workflows │ └── buildOnPR.yml ├── .eslintrc.cjs ├── LICENSE ├── package.json ├── CONTRIBUTING.md ├── CHANGELOG.md ├── README-zh-Hans.md └── README.md /.nvmrc: -------------------------------------------------------------------------------- 1 | v18.14.0 2 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true 3 | } 4 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": ["execa", "kolorist"] 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /nbproject/ 2 | /.idea/* 3 | *.tmlanguage.cache 4 | *.tmPreferences.cache 5 | *.stTheme.cache 6 | *.sublime-workspace 7 | *.sublime-project 8 | -------------------------------------------------------------------------------- /src/helpers/constants.ts: -------------------------------------------------------------------------------- 1 | import pkg from '../../package.json'; 2 | 3 | export const commandName = 'ai'; 4 | export const projectName = 'AI Shell'; 5 | export const repoUrl = pkg.repository.url; 6 | -------------------------------------------------------------------------------- /src/helpers/stream-to-string.ts: -------------------------------------------------------------------------------- 1 | import { IncomingMessage } from 'http'; 2 | 3 | export async function streamToString(stream: IncomingMessage): Promise { 4 | let str = ''; 5 | for await (const chunk of stream) { 6 | str += chunk; 7 | } 8 | return str; 9 | } 10 | -------------------------------------------------------------------------------- /src/helpers/strip-regex-patterns.ts: -------------------------------------------------------------------------------- 1 | export const stripRegexPatterns = ( 2 | inputString: string, 3 | patternList: (RegExp | string | undefined)[] 4 | ) => 5 | patternList.reduce( 6 | (currentString: string, pattern) => 7 | pattern ? currentString.replaceAll(pattern, '') : currentString, 8 | inputString 9 | ); 10 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "noEmit": true, 4 | "module": "ESNext", 5 | "target": "ESNext", 6 | "moduleResolution": "node", 7 | "strict": true, 8 | "resolveJsonModule": true, 9 | "esModuleInterop": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "skipLibCheck": true 12 | }, 13 | "include": ["src"] 14 | } 15 | -------------------------------------------------------------------------------- /.github/workflows/buildOnPR.yml: -------------------------------------------------------------------------------- 1 | name: Build on PR 2 | 3 | on: [pull_request] 4 | 5 | jobs: 6 | run-build: 7 | name: Run Build 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - name: Check out Git repository 12 | uses: actions/checkout@v2 13 | 14 | - name: Set up Node.js 15 | uses: actions/setup-node@v1 16 | with: 17 | node-version: 18 18 | 19 | - name: Install Node.js dependencies 20 | run: npm install 21 | - name: Lint 22 | run: npm run lint 23 | - name: Build 24 | run: npm run build 25 | -------------------------------------------------------------------------------- /src/helpers/replace-all-polyfill.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * String.prototype.replaceAll() polyfill 3 | * https://gomakethings.com/how-to-replace-a-section-of-a-string-with-another-one-with-vanilla-js/ 4 | * @author Chris Ferdinandi 5 | * @license MIT 6 | */ 7 | 8 | if (!String.prototype.replaceAll) { 9 | String.prototype.replaceAll = function (str, newStr) { 10 | if ( 11 | Object.prototype.toString.call(str).toLowerCase() === '[object regexp]' 12 | ) { 13 | return this.replace(str, newStr as string); 14 | } 15 | 16 | return this.replace(new RegExp(str, 'g'), newStr as string); 17 | }; 18 | } 19 | -------------------------------------------------------------------------------- /src/helpers/os-detect.ts: -------------------------------------------------------------------------------- 1 | import os from 'os'; 2 | import path from 'path'; 3 | import i18n from './i18n'; 4 | 5 | export function detectShell() { 6 | try { 7 | // Detect if we're running on win32 and assume powershell 8 | if (os.platform() === 'win32') { 9 | return 'powershell'; 10 | } 11 | // otherwise return current shell; default to bash 12 | return path.basename(os.userInfo().shell ?? 'bash'); 13 | } catch (err: unknown) { 14 | if (err instanceof Error) { 15 | throw new Error( 16 | `${i18n.t('Shell detection failed unexpectedly')}: ${err.message}` 17 | ); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/helpers/stream-to-iterable.ts: -------------------------------------------------------------------------------- 1 | import { IncomingMessage } from 'http'; 2 | 3 | export async function* streamToIterable(stream: IncomingMessage) { 4 | let previous = ''; 5 | for await (const chunk of stream) { 6 | const bufferChunk = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk); 7 | previous += bufferChunk; 8 | let eolIndex; 9 | while ((eolIndex = previous.indexOf('\n')) >= 0) { 10 | // line includes the EOL 11 | const line = previous.slice(0, eolIndex + 1).trimEnd(); 12 | if (line.startsWith('data: ')) yield line; 13 | previous = previous.slice(eolIndex + 1); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/commands/update.ts: -------------------------------------------------------------------------------- 1 | import { command } from 'cleye'; 2 | import { execaCommand } from 'execa'; 3 | import { dim } from 'kolorist'; 4 | import i18n from '../helpers/i18n'; 5 | 6 | export default command( 7 | { 8 | name: 'update', 9 | help: { 10 | description: 'Update AI Shell to the latest version', 11 | }, 12 | }, 13 | async () => { 14 | console.log(''); 15 | const command = `npm update -g @builder.io/ai-shell`; 16 | console.log(dim(`${i18n.t('Running')}: ${command}`)); 17 | console.log(''); 18 | await execaCommand(command, { 19 | stdio: 'inherit', 20 | shell: process.env.SHELL || true, 21 | }).catch(() => { 22 | // No need to handle, will go to stderr 23 | }); 24 | console.log(''); 25 | } 26 | ); 27 | -------------------------------------------------------------------------------- /src/helpers/error.ts: -------------------------------------------------------------------------------- 1 | import { dim } from 'kolorist'; 2 | import { version } from '../../package.json'; 3 | import i18n from './i18n'; 4 | 5 | export class KnownError extends Error {} 6 | 7 | const indent = ' '.repeat(4); 8 | 9 | export const handleCliError = (error: any) => { 10 | if (error instanceof Error && !(error instanceof KnownError)) { 11 | if (error.stack) { 12 | console.error(dim(error.stack.split('\n').slice(1).join('\n'))); 13 | } 14 | console.error(`\n${indent}${dim(`ai-shell v${version}`)}`); 15 | console.error( 16 | `\n${indent}${i18n.t( 17 | 'Please open a Bug report with the information above' 18 | )}:` 19 | ); 20 | console.error(`${indent}https://github.com/BuilderIO/ai-shell/issues/new`); 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | es2021: true, 4 | node: true, 5 | }, 6 | extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'], 7 | parser: '@typescript-eslint/parser', 8 | parserOptions: { 9 | ecmaVersion: 'latest', 10 | sourceType: 'module', 11 | }, 12 | overrides: [{ files: ['src/**/*.ts'] }], 13 | plugins: ['@typescript-eslint', 'unused-imports'], 14 | rules: { 15 | 'no-async-promise-executor': 'off', 16 | '@typescript-eslint/no-explicit-any': 'off', 17 | 'no-unused-vars': 'off', 18 | '@typescript-eslint/no-unused-vars': 'off', 19 | 'unused-imports/no-unused-imports': 'error', 20 | 'unused-imports/no-unused-vars': [ 21 | 'warn', 22 | { 23 | vars: 'all', 24 | varsIgnorePattern: '^_', 25 | args: 'after-used', 26 | argsIgnorePattern: '^_', 27 | }, 28 | ], 29 | }, 30 | }; 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Builder.io 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/cli.ts: -------------------------------------------------------------------------------- 1 | import { cli } from 'cleye'; 2 | import { red } from 'kolorist'; 3 | import { version } from '../package.json'; 4 | import config from './commands/config'; 5 | import update from './commands/update'; 6 | import chat from './commands/chat'; 7 | import { commandName } from './helpers/constants'; 8 | import { handleCliError } from './helpers/error'; 9 | import { prompt } from './prompt'; 10 | 11 | cli( 12 | { 13 | name: commandName, 14 | version: version, 15 | flags: { 16 | prompt: { 17 | type: String, 18 | description: 'Prompt to run', 19 | alias: 'p', 20 | }, 21 | silent: { 22 | type: Boolean, 23 | description: 'Less verbose, skip printing the command explanation ', 24 | alias: 's', 25 | }, 26 | }, 27 | commands: [config, chat, update], 28 | }, 29 | (argv) => { 30 | const silentMode = argv.flags.silent; 31 | const promptText = argv._.join(' '); 32 | 33 | if (promptText.trim() === 'update') { 34 | update.callback?.(argv); 35 | } else { 36 | prompt({ usePrompt: promptText, silentMode }).catch((error) => { 37 | console.error(`\n${red('✖')} ${error.message}`); 38 | handleCliError(error); 39 | process.exit(1); 40 | }); 41 | } 42 | } 43 | ); 44 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@builder.io/ai-shell", 3 | "description": "A CLI that converts natural language to shell commands.", 4 | "version": "1.0.12", 5 | "type": "module", 6 | "dependencies": { 7 | "@clack/core": "latest", 8 | "@clack/prompts": "latest", 9 | "@dqbd/tiktoken": "^1.0.2", 10 | "@nexssp/os": "^2.0.35", 11 | "axios": "^1.3.5", 12 | "cleye": "^1.3.2", 13 | "clipboardy": "^2.3.0", 14 | "dedent": "^0.7.0", 15 | "execa": "^7.1.1", 16 | "i18next": "^22.4.15", 17 | "ini": "^4.0.0", 18 | "kolorist": "^1.7.0", 19 | "openai": "^3.2.1" 20 | }, 21 | "repository": { 22 | "type": "git", 23 | "url": "https://github.com/BuilderIO/ai-shell" 24 | }, 25 | "files": [ 26 | "dist" 27 | ], 28 | "bin": { 29 | "ai-shell": "./dist/cli.mjs", 30 | "ai": "./dist/cli.mjs" 31 | }, 32 | "scripts": { 33 | "start": "jiti ./src/cli.ts", 34 | "lint:fix": "prettier --write . && eslint --fix", 35 | "lint": "prettier --check . && eslint", 36 | "typecheck": "tsc", 37 | "build": "pkgroll", 38 | "release:patch": "npm run build && npm version patch && npm run build && npm publish && git push --follow-tags" 39 | }, 40 | "devDependencies": { 41 | "@types/dedent": "^0.7.0", 42 | "@types/ini": "^1.3.31", 43 | "@types/node": "^18.15.11", 44 | "@typescript-eslint/eslint-plugin": "^5.57.1", 45 | "@typescript-eslint/parser": "^5.57.1", 46 | "eslint": "^8.38.0", 47 | "eslint-plugin-unused-imports": "^2.0.0", 48 | "jiti": "^1.17.0", 49 | "pkgroll": "^1.9.0", 50 | "prettier": "^2.8.8", 51 | "typescript": "^4.9.5" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/helpers/shell-history.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import os from 'os'; 3 | import path from 'path'; 4 | 5 | // Function to get the history file based on the shell 6 | function getHistoryFile(): string | null { 7 | const shell = process.env.SHELL || ''; 8 | const homeDir = os.homedir(); 9 | 10 | switch (path.basename(shell)) { 11 | case 'bash': 12 | case 'sh': 13 | return path.join(homeDir, '.bash_history'); 14 | case 'zsh': 15 | return path.join(homeDir, '.zsh_history'); 16 | case 'fish': 17 | return path.join(homeDir, '.local', 'share', 'fish', 'fish_history'); 18 | case 'ksh': 19 | return path.join(homeDir, '.ksh_history'); 20 | case 'tcsh': 21 | return path.join(homeDir, '.history'); 22 | default: 23 | return null; 24 | } 25 | } 26 | 27 | // Function to get the last command from the history file 28 | function getLastCommand(historyFile: string): string | null { 29 | try { 30 | const data = fs.readFileSync(historyFile, 'utf8'); 31 | const commands = data.trim().split('\n'); 32 | return commands[commands.length - 1]; 33 | } catch (err) { 34 | // Ignore any errors 35 | return null; 36 | } 37 | } 38 | 39 | // Function to append the command to the history file if it's not the same as the last command 40 | export function appendToShellHistory(command: string): void { 41 | const historyFile = getHistoryFile(); 42 | if (historyFile) { 43 | const lastCommand = getLastCommand(historyFile); 44 | if (lastCommand !== command) { 45 | fs.appendFile(historyFile, `${command}\n`, (err) => { 46 | if (err) { 47 | // Ignore any errors 48 | } 49 | }); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribution Guide 2 | 3 | ## Setting up the project 4 | 5 | Use [nvm](https://nvm.sh) to use the appropriate Node.js version from `.nvmrc`: 6 | 7 | ```sh 8 | nvm i 9 | ``` 10 | 11 | Install the dependencies using npm: 12 | 13 | ```sh 14 | npm i 15 | ``` 16 | 17 | ## Building the project 18 | 19 | Run the `build` script: 20 | 21 | ```sh 22 | npm build 23 | ``` 24 | 25 | The package is bundled using [pkgroll](https://github.com/privatenumber/pkgroll) (Rollup). It infers the entry-points from `package.json` so there are no build configurations. 26 | 27 | ### Development (watch) mode 28 | 29 | During development, you can use the watch flag (`--watch, -w`) to automatically rebuild the package on file changes: 30 | 31 | ```sh 32 | npm build -w 33 | ``` 34 | 35 | ## Running the package locally 36 | 37 | Since pkgroll knows the entry-point is a binary (being in `package.json#bin`), it automatically adds the Node.js hashbang to the top of the file, and chmods it so it's executable. 38 | 39 | You can run the distribution file in any directory: 40 | 41 | ```sh 42 | ./dist/cli.mjs 43 | ``` 44 | 45 | Or in non-UNIX environments, you can use Node.js to run the file: 46 | 47 | ```sh 48 | node ./dist/cli.mjs 49 | ``` 50 | 51 | ## Check the lint in order to pass 52 | 53 | First, install prettier. 54 | 55 | ```sh 56 | npm install -g prettier 57 | ``` 58 | 59 | Once Prettier is installed, you can run it on a single file or multiple files using the following commands: 60 | 61 | 1. For a single file: 62 | 63 | ```sh 64 | prettier --write path/to/your/file.js 65 | ``` 66 | 67 | 2. For a multiple file: 68 | 69 | ```sh 70 | prettier --write "src/**/*.js" 71 | ``` 72 | 73 | If you use Vscode, It is recommended to use [prettier-vscode](https://github.com/prettier/prettier-vscode) 74 | -------------------------------------------------------------------------------- /src/commands/config.ts: -------------------------------------------------------------------------------- 1 | import { command } from 'cleye'; 2 | import { red } from 'kolorist'; 3 | import { 4 | hasOwn, 5 | getConfig, 6 | setConfigs, 7 | showConfigUI, 8 | } from '../helpers/config.js'; 9 | import { KnownError, handleCliError } from '../helpers/error.js'; 10 | import i18n from '../helpers/i18n.js'; 11 | 12 | export default command( 13 | { 14 | name: 'config', 15 | parameters: ['[mode]', '[key=value...]'], 16 | help: { 17 | description: 'Configure the CLI', 18 | }, 19 | }, 20 | (argv) => { 21 | (async () => { 22 | const { mode, keyValue: keyValues } = argv._; 23 | 24 | if (mode === 'ui' || !mode) { 25 | await showConfigUI(); 26 | return; 27 | } 28 | 29 | if (!keyValues.length) { 30 | console.error( 31 | `${i18n.t('Error')}: ${i18n.t( 32 | 'Missing required parameter' 33 | )} "key=value"\n` 34 | ); 35 | argv.showHelp(); 36 | return process.exit(1); 37 | } 38 | 39 | if (mode === 'get') { 40 | const config = await getConfig(); 41 | for (const key of keyValues) { 42 | if (hasOwn(config, key)) { 43 | console.log(`${key}=${config[key as keyof typeof config]}`); 44 | } else { 45 | throw new KnownError( 46 | `${i18n.t('Invalid config property')}: ${key}` 47 | ); 48 | } 49 | } 50 | return; 51 | } 52 | 53 | if (mode === 'set') { 54 | await setConfigs( 55 | keyValues.map((keyValue) => keyValue.split('=') as [string, string]) 56 | ); 57 | return; 58 | } 59 | 60 | throw new KnownError(`${i18n.t('Invalid mode')}: ${mode}`); 61 | })().catch((error) => { 62 | console.error(`\n${red('✖')} ${error.message}`); 63 | handleCliError(error); 64 | process.exit(1); 65 | }); 66 | } 67 | ); 68 | -------------------------------------------------------------------------------- /src/locales/zh-Hans.json: -------------------------------------------------------------------------------- 1 | { 2 | "Starting new conversation": "开始新的对话", 3 | "Goodbye!": "再见!", 4 | "send a message ('exit' to quit)": "发送消息('exit' 退出)", 5 | "Please enter a prompt.": "请输入提示语", 6 | "THINKING...": "思考中...", 7 | "Please set your OpenAI API key via `ai config set OPENAI_KEY=`": "请通过 `ai config set OPENAI_KEY=` 设置您的 OpenAI API 密钥", 8 | "Set config": "设置配置", 9 | "Enter your OpenAI API key": "输入您的 OpenAI API 密钥", 10 | "(not set)": "(未设置)", 11 | "OpenAI Key": "OpenAI 密钥", 12 | "Please enter a key": "请输入密钥", 13 | "OpenAI API Endpoint": "OpenAI API 端点", 14 | "Enter your OpenAI API Endpoint": "输入您的 OpenAI API 端点", 15 | "Silent Mode": "静默模式", 16 | "Enable silent mode?": "启用静默模式?", 17 | "Model": "模型", 18 | "Enter the model you want to use": "输入您想要使用的模型", 19 | "Language": "语言", 20 | "Enter the language you want to use": "输入您想要使用的语言", 21 | "What would you like me to do?": "您想让我做什么?", 22 | "delete all log files": "删除所有日志文件", 23 | "list js files": "列出 js 文件", 24 | "fetch me a random joke": "随便给我讲个笑话", 25 | "list all commits": "列出所有提交", 26 | "Say hello": "打个招呼", 27 | "you can edit script here": "您可以在这里编辑脚本", 28 | "What would you like me to change in this script?": "您想让我在这个脚本中做什么修改?", 29 | "e.g.": "例如:", 30 | "e.g. change the folder name": "例如:更改文件夹名称", 31 | "Your script": "您的脚本", 32 | "Loading...": "加载中...", 33 | "Getting explanation...": "获取解释中...", 34 | "Explanation": "解释", 35 | "Run this script?": "运行这个脚本?", 36 | "Revise this script?": "修改这个脚本?", 37 | "Yes": "是", 38 | "Lets go!": "开始运行吧!", 39 | "Edit": "编辑", 40 | "Make some adjustments before running": "在运行之前进行一些调整", 41 | "Revise": "修改", 42 | "Give feedback via prompt and get a new result": "通过提示提供反馈并获得新结果", 43 | "Copy": "复制", 44 | "Copy the generated script to your clipboard": "将生成的脚本复制到剪贴板", 45 | "Cancel": "取消", 46 | "Exit the program": "退出程序", 47 | "Running": "正在运行", 48 | "Copied to clipboard!": "已复制到剪贴板!", 49 | "Your new script": "您的新脚本", 50 | "Invalid config property": "无效的配置属性", 51 | "Shell detection failed unexpectedly": "Shell 检测到意外失败", 52 | "Invalid model": "无效的模型", 53 | "Error": "错误", 54 | "Missing required parameter": "缺少必需的参数", 55 | "Please open a Bug report with the information above": "请使用上面的信息打开 Bug 报告", 56 | "Prompt to run": "要运行的提示语", 57 | "You": "您" 58 | } 59 | -------------------------------------------------------------------------------- /src/locales/zh-Hant.json: -------------------------------------------------------------------------------- 1 | { 2 | "Starting new conversation": "開始新的對話", 3 | "Goodbye!": "再見!", 4 | "send a message ('exit' to quit)": "發送消息('exit' 退出)", 5 | "Please enter a prompt.": "請輸入提示語", 6 | "THINKING...": "思考中...", 7 | "Please set your OpenAI API key via `ai config set OPENAI_KEY=`": "請藉由 `ai config set OPENAI_KEY=` 設定您的 OpenAI API 密鑰", 8 | "Set config": "設置配置", 9 | "Enter your OpenAI API key": "輸入您的 OpenAI API 密鑰", 10 | "(not set)": "(未設置)", 11 | "OpenAI Key": "OpenAI 密鑰", 12 | "Please enter a key": "請輸入密鑰", 13 | "OpenAI API Endpoint": "OpenAI API 端點", 14 | "Enter your OpenAI API Endpoint": "輸入您的 OpenAI API 端點", 15 | "Silent Mode": "靜默模式", 16 | "Enable silent mode?": "啟用靜默模式?", 17 | "Model": "模型", 18 | "Enter the model you want to use": "輸入您想要使用的模型", 19 | "Language": "語言", 20 | "Enter the language you want to use": "輸入您想要使用的語言", 21 | "What would you like me to do?": "您想讓我做什麼?", 22 | "delete all log files": "刪除所有日誌文件", 23 | "list js files": "列出 js 文件", 24 | "fetch me a random joke": "隨便給我講個笑話", 25 | "list all commits": "列出所有提交", 26 | "Say hello": "打個招呼", 27 | "you can edit script here": "您可以在這裡編輯腳本", 28 | "What would you like me to change in this script?": "您想讓我在這個腳本中做什麼修改?", 29 | "e.g.": "例如:", 30 | "e.g. change the folder name": "例如:更改文件夾名稱", 31 | "Your script": "您的腳本", 32 | "Loading...": "加載中...", 33 | "Getting explanation...": "獲取解釋中...", 34 | "Explanation": "解釋", 35 | "Run this script?": "運行這個腳本?", 36 | "Revise this script?": "修改這個腳本?", 37 | "Yes": "是", 38 | "Lets go!": "開始運行吧!", 39 | "Edit": "編輯", 40 | "Make some adjustments before running": "在運行之前進行一些調整", 41 | "Revise": "修改", 42 | "Give feedback via prompt and get a new result": "通過提示提供回饋並獲得新結果", 43 | "Copy": "複製", 44 | "Copy the generated script to your clipboard": "將生成的腳本複製到剪貼簿", 45 | "Cancel": "取消", 46 | "Exit the program": "退出程序", 47 | "Running": "正在運行", 48 | "Copied to clipboard!": "已複製到剪貼簿!", 49 | "Your new script": "您的新腳本", 50 | "Invalid config property": "無效的配置屬性", 51 | "Shell detection failed unexpectedly": "Shell 檢測到意外失敗", 52 | "Invalid model": "無效的模型", 53 | "Error": "錯誤", 54 | "Missing required parameter": "缺少必需的參數", 55 | "Please open a Bug report with the information above": "請使用上面的訊息打開 Bug 報告", 56 | "Prompt to run": "要運行的提示語", 57 | "You": "您" 58 | } 59 | -------------------------------------------------------------------------------- /src/locales/ko.json: -------------------------------------------------------------------------------- 1 | { 2 | "Starting new conversation": "새 대화 시작", 3 | "Goodbye!": "안녕!", 4 | "send a message ('exit' to quit)": "메시지 보내기 ('exit' 입력 시 종료)", 5 | "Please enter a prompt.": "프롬프트를 입력하세요.", 6 | "THINKING...": "생각 중...", 7 | "Please set your OpenAI API key via `ai config set OPENAI_KEY=`": "OpenAI API 키를 `ai config set OPENAI_KEY=`으로 설정하세요.", 8 | "Set config": "설정", 9 | "Enter your OpenAI API key": "OpenAI API 키를 입력하세요.", 10 | "(not set)": "(설정되지 않음)", 11 | "OpenAI Key": "OpenAI 키", 12 | "Please enter a key": "키를 입력하세요.", 13 | "OpenAI API Endpoint": "OpenAI API 엔드포인트", 14 | "Enter your OpenAI API Endpoint": "OpenAI API 엔드포인트를 입력하세요.", 15 | "Silent Mode": "조용한 모드", 16 | "Enable silent mode?": "조용한 모드를 활성화하시겠습니까?", 17 | "Model": "모델", 18 | "Enter the model you want to use": "사용하려는 모델을 입력하세요.", 19 | "Language": "언어", 20 | "Enter the language you want to use": "사용하려는 언어를 입력하세요.", 21 | "What would you like me to do?": "무엇을 도와드릴까요?", 22 | "delete all log files": "모든 로그 파일 삭제", 23 | "list js files": "js 파일 목록", 24 | "fetch me a random joke": "랜덤한 농담 가져오기", 25 | "list all commits": "모든 커밋 목록", 26 | "Say hello": "인사하기", 27 | "you can edit script here": "여기서 스크립트를 편집할 수 있습니다.", 28 | "What would you like me to change in this script?": "이 스크립트에서 무엇을 변경하시겠습니까?", 29 | "e.g.": "예시:", 30 | "e.g. change the folder name": "예시: 폴더 이름 변경", 31 | "Your script": "스크립트", 32 | "Loading...": "로딩 중...", 33 | "Getting explanation...": "설명 가져오는 중...", 34 | "Explanation": "설명", 35 | "Run this script?": "이 스크립트를 실행하시겠습니까?", 36 | "Revise this script?": "이 스크립트를 수정하시겠습니까?", 37 | "Yes": "예", 38 | "Lets go!": "시작!", 39 | "Edit": "편집", 40 | "Make some adjustments before running": "실행 전에 일부 조정을 하세요.", 41 | "Revise": "수정", 42 | "Give feedback via prompt and get a new result": "프롬프트를 통해 피드백을 제공하고 새 결과를 얻으세요.", 43 | "Copy": "복사", 44 | "Copy the generated script to your clipboard": "생성된 스크립트를 클립보드에 복사하세요.", 45 | "Cancel": "취소", 46 | "Exit the program": "프로그램 종료", 47 | "Running": "실행 중", 48 | "Copied to clipboard!": "클립보드에 복사됨!", 49 | "Your new script": "새 스크립트", 50 | "Invalid config property": "잘못된 구성 속성", 51 | "Shell detection failed unexpectedly": "쉘 감지가 예기치 않게 실패했습니다.", 52 | "Invalid model": "잘못된 모델", 53 | "Error": "오류", 54 | "Missing required parameter": "필수 매개변수가 누락되었습니다.", 55 | "Please open a Bug report with the information above": "위 정보를 포함하여 버그 보고서를 열어주세요.", 56 | "Prompt to run": "실행할 프롬프트", 57 | "You": "당신" 58 | } 59 | -------------------------------------------------------------------------------- /src/locales/jp.json: -------------------------------------------------------------------------------- 1 | { 2 | "Starting new conversation": "新しい会話を開始する", 3 | "Goodbye!": "さようなら!", 4 | "send a message ('exit' to quit)": "メッセージを送信する(終了するには 'exit')", 5 | "Please enter a prompt.": "プロンプトを入力してください。", 6 | "THINKING...": "考え中...", 7 | "Please set your OpenAI API key via `ai config set OPENAI_KEY=`": "OpenAI APIキーを `ai config set OPENAI_KEY=` で設定してください", 8 | "Set config": "設定", 9 | "Enter your OpenAI API key": "OpenAI APIキーを入力してください", 10 | "(not set)": "(未設定)", 11 | "OpenAI Key": "OpenAIキー", 12 | "Please enter a key": "キーを入力してください", 13 | "OpenAI API Endpoint": "OpenAI APIエンドポイント", 14 | "Enter your OpenAI API Endpoint": "OpenAI APIエンドポイントを入力してください", 15 | "Silent Mode": "サイレントモード", 16 | "Enable silent mode?": "サイレントモードを有効にしますか?", 17 | "Model": "モデル", 18 | "Enter the model you want to use": "使用するモデルを入力してください", 19 | "Language": "言語", 20 | "Enter the language you want to use": "使用する言語を入力してください", 21 | "What would you like me to do?": "何をしてほしいですか?", 22 | "delete all log files": "すべてのログファイルを削除する", 23 | "list js files": "jsファイルをリストアップする", 24 | "fetch me a random joke": "ランダムなジョークを取得する", 25 | "list all commits": "すべてのコミットをリストアップする", 26 | "Say hello": "こんにちは", 27 | "you can edit script here": "ここでスクリプトを編集できます", 28 | "What would you like me to change in this script?": "このスクリプトで何を変更したいですか?", 29 | "e.g.": "例:", 30 | "e.g. change the folder name": "例:フォルダ名を変更する", 31 | "Your script": "あなたのスクリプト", 32 | "Loading...": "読み込み中...", 33 | "Getting explanation...": "説明を取得中...", 34 | "Explanation": "説明", 35 | "Run this script?": "このスクリプトを実行しますか?", 36 | "Revise this script?": "このスクリプトを修正しますか?", 37 | "Yes": "はい", 38 | "Lets go!": "始めましょう!", 39 | "Edit": "編集", 40 | "Make some adjustments before running": "実行前に調整を行う", 41 | "Revise": "修正", 42 | "Give feedback via prompt and get a new result": "プロンプトを介してフィードバックを提供し、新しい結果を取得する", 43 | "Copy": "コピー", 44 | "Copy the generated script to your clipboard": "生成されたスクリプトをクリップボードにコピーする", 45 | "Cancel": "キャンセル", 46 | "Exit the program": "プログラムを終了する", 47 | "Running": "実行中", 48 | "Copied to clipboard!": "クリップボードにコピーしました!", 49 | "Your new script": "あなたの新しいスクリプト", 50 | "Invalid config property": "無効な設定プロパティ", 51 | "Shell detection failed unexpectedly": "シェルの検出が予期せず失敗しました", 52 | "Invalid model": "無効なモデル", 53 | "Error": "エラー", 54 | "Missing required parameter": "必要なパラメーターがありません", 55 | "Please open a Bug report with the information above": "上記の情報を含めてバグレポートを開いてください", 56 | "Prompt to run": "実行するプロンプト", 57 | "You": "あなた" 58 | } 59 | -------------------------------------------------------------------------------- /src/commands/chat.ts: -------------------------------------------------------------------------------- 1 | import { command } from 'cleye'; 2 | import { spinner, intro, outro, text, isCancel } from '@clack/prompts'; 3 | import { cyan, green } from 'kolorist'; 4 | import { generateCompletion, readData } from '../helpers/completion'; 5 | import { getConfig } from '../helpers/config'; 6 | import { streamToIterable } from '../helpers/stream-to-iterable'; 7 | import { ChatCompletionRequestMessage } from 'openai'; 8 | import i18n from '../helpers/i18n'; 9 | 10 | export default command( 11 | { 12 | name: 'chat', 13 | help: { 14 | description: 15 | 'Start a new chat session to send and receive messages, continue replying until the user chooses to exit.', 16 | }, 17 | }, 18 | async () => { 19 | const { 20 | OPENAI_KEY: key, 21 | OPENAI_API_ENDPOINT: apiEndpoint, 22 | MODEL: model, 23 | } = await getConfig(); 24 | const chatHistory: ChatCompletionRequestMessage[] = []; 25 | 26 | console.log(''); 27 | intro(i18n.t('Starting new conversation')); 28 | const prompt = async () => { 29 | const msgYou = `${i18n.t('You')}:`; 30 | const userPrompt = (await text({ 31 | message: `${cyan(msgYou)}`, 32 | placeholder: i18n.t(`send a message ('exit' to quit)`), 33 | validate: (value) => { 34 | if (!value) return i18n.t('Please enter a prompt.'); 35 | }, 36 | })) as string; 37 | 38 | if (isCancel(userPrompt) || userPrompt === 'exit') { 39 | outro(i18n.t('Goodbye!')); 40 | process.exit(0); 41 | } 42 | 43 | const infoSpin = spinner(); 44 | infoSpin.start(i18n.t(`THINKING...`)); 45 | chatHistory.push({ 46 | role: 'user', 47 | content: userPrompt, 48 | }); 49 | const { readResponse } = await getResponse({ 50 | prompt: chatHistory, 51 | key, 52 | model, 53 | apiEndpoint, 54 | }); 55 | 56 | infoSpin.stop(`${green('AI Shell:')}`); 57 | console.log(''); 58 | const fullResponse = await readResponse( 59 | process.stdout.write.bind(process.stdout) 60 | ); 61 | chatHistory.push({ 62 | role: 'assistant', 63 | content: fullResponse, 64 | }); 65 | console.log(''); 66 | console.log(''); 67 | prompt(); 68 | }; 69 | 70 | prompt(); 71 | } 72 | ); 73 | 74 | async function getResponse({ 75 | prompt, 76 | number = 1, 77 | key, 78 | model, 79 | apiEndpoint, 80 | }: { 81 | prompt: string | ChatCompletionRequestMessage[]; 82 | number?: number; 83 | model?: string; 84 | key: string; 85 | apiEndpoint: string; 86 | }) { 87 | const stream = await generateCompletion({ 88 | prompt, 89 | key, 90 | model, 91 | number, 92 | apiEndpoint, 93 | }); 94 | 95 | const iterableStream = streamToIterable(stream); 96 | 97 | return { readResponse: readData(iterableStream) }; 98 | } 99 | -------------------------------------------------------------------------------- /src/locales/ar.json: -------------------------------------------------------------------------------- 1 | { 2 | "Starting new conversation": "بدء محادثة جديدة", 3 | "Goodbye!": "وداعا!", 4 | "send a message ('exit' to quit)": "أرسل رسالة ('exit' للخروج)", 5 | "Please enter a prompt.": "الرجاء إدخال تعليمات.", 6 | "THINKING...": "فكر...", 7 | "Please set your OpenAI API key via `ai config set OPENAI_KEY=`": "يرجى تعيين مفتاح OpenAI API الخاص بك عبر `ai config set OPENAI_KEY=`", 8 | "Set config": "تعيين التكوين", 9 | "Enter your OpenAI API key": "أدخل مفتاح OpenAI API الخاص بك", 10 | "(not set)": "(غير مضبوط)", 11 | "OpenAI Key": "مفتاح OpenAI", 12 | "Please enter a key": "الرجاء إدخال مفتاح", 13 | "OpenAI API Endpoint": "نقطة نهاية OpenAI API", 14 | "Enter your OpenAI API Endpoint": "أدخل نقطة نهاية OpenAI API الخاصة بك", 15 | "Silent Mode": "وضع صامت", 16 | "Enable silent mode?": "تمكين الوضع الصامت؟", 17 | "Model": "نموذج", 18 | "Enter the model you want to use": "أدخل النموذج الذي تريد استخدامه", 19 | "Language": "لغة", 20 | "Enter the language you want to use": "أدخل اللغة التي تريد استخدامها", 21 | "What would you like me to do?": "ماذا تريد مني أن أفعل؟", 22 | "delete all log files": "حذف جميع ملفات السجلات", 23 | "list js files": "قائمة ملفات js", 24 | "fetch me a random joke": "جلب لي نكتة عشوائية", 25 | "list all commits": "قائمة جميع الالتزامات", 26 | "Say hello": "قل مرحبا", 27 | "you can edit script here": "يمكنك تحرير النص هنا", 28 | "What would you like me to change in this script?": "ما الذي تريد تغييره في هذا النص؟", 29 | "e.g.": "على سبيل المثال", 30 | "e.g. change the folder name": "على سبيل المثال تغيير اسم المجلد", 31 | "Your script": "نصك", 32 | "Loading...": "جار التحميل...", 33 | "Getting explanation...": "الحصول على تفسير...", 34 | "Explanation": "تفسير", 35 | "Run this script?": "تشغيل هذا النص؟", 36 | "Revise this script?": "مراجعة هذا النص؟", 37 | "Yes": "نعم", 38 | "Lets go!": "لنذهب!", 39 | "Edit": "تحرير", 40 | "Make some adjustments before running": "قم بإجراء بعض التعديلات قبل التشغيل", 41 | "Revise": "مراجعة", 42 | "Give feedback via prompt and get a new result": "تقديم ملاحظات عبر البريد الإلكتروني والحصول على نتيجة جديدة", 43 | "Copy": "نسخ", 44 | "Copy the generated script to your clipboard": "انسخ النص المولد إلى الحافظة الخاصة بك", 45 | "Cancel": "إلغاء", 46 | "Exit the program": "خروج من البرنامج", 47 | "Running": "جار التشغيل", 48 | "Copied to clipboard!": "تم النسخ إلى الحافظة!", 49 | "Your new script": "نصك الجديد", 50 | "Invalid config property": "خاصية التكوين غير صالحة", 51 | "Shell detection failed unexpectedly": "فشل كشف الخطأ بشكل غير متوقع", 52 | "Invalid model": "نموذج غير صالح", 53 | "Error": "خطأ", 54 | "Missing required parameter": "معلمة مطلوبة مفقودة", 55 | "Please open a Bug report with the information above": "يرجى فتح تقرير خلل مع المعلومات أعلاه", 56 | "Prompt to run": "تشغيل التعليمات", 57 | "You": "أنت" 58 | } 59 | -------------------------------------------------------------------------------- /src/locales/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "Starting new conversation": "Starting new conversation", 3 | "Goodbye!": "Goodbye!", 4 | "send a message ('exit' to quit)": "send a message ('exit' to quit)", 5 | "Please enter a prompt.": "Please enter a prompt.", 6 | "THINKING...": "THINKING...", 7 | "Please set your OpenAI API key via `ai config set OPENAI_KEY=`": "Please set your OpenAI API key via `ai config set OPENAI_KEY=`", 8 | "Set config": "Set config", 9 | "Enter your OpenAI API key": "Enter your OpenAI API key", 10 | "(not set)": "(not set)", 11 | "OpenAI Key": "OpenAI Key", 12 | "Please enter a key": "Please enter a key", 13 | "OpenAI API Endpoint": "OpenAI API Endpoint", 14 | "Enter your OpenAI API Endpoint": "Enter your OpenAI API Endpoint", 15 | "Silent Mode": "Silent Mode", 16 | "Enable silent mode?": "Enable silent mode?", 17 | "Model": "Model", 18 | "Enter the model you want to use": "Enter the model you want to use", 19 | "Language": "Language", 20 | "Enter the language you want to use": "Enter the language you want to use", 21 | "What would you like me to do?": "What would you like me to do?", 22 | "delete all log files": "delete all log files", 23 | "list js files": "list js files", 24 | "fetch me a random joke": "fetch me a random joke", 25 | "list all commits": "list all commits", 26 | "Say hello": "Say hello", 27 | "you can edit script here": "you can edit script here", 28 | "What would you like me to change in this script?": "What would you like me to change in this script?", 29 | "e.g.": "e.g.", 30 | "e.g. change the folder name": "e.g. change the folder name", 31 | "Your script": "Your script", 32 | "Loading...": "Loading...", 33 | "Getting explanation...": "Getting explanation...", 34 | "Explanation": "Explanation", 35 | "Run this script?": "Run this script?", 36 | "Revise this script?": "Revise this script?", 37 | "Yes": "Yes", 38 | "Lets go!": "Lets go!", 39 | "Edit": "Edit", 40 | "Make some adjustments before running": "Make some adjustments before running", 41 | "Revise": "Revise", 42 | "Give feedback via prompt and get a new result": "Give feedback via prompt and get a new result", 43 | "Copy": "Copy", 44 | "Copy the generated script to your clipboard": "Copy the generated script to your clipboard", 45 | "Cancel": "Cancel", 46 | "Exit the program": "Exit the program", 47 | "Running": "Running", 48 | "Copied to clipboard!": "Copied to clipboard!", 49 | "Your new script": "Your new script", 50 | "Invalid config property": "Invalid config property", 51 | "Shell detection failed unexpectedly": "Shell detection failed unexpectedly", 52 | "Invalid model": "Invalid model", 53 | "Error": "Error", 54 | "Missing required parameter": "Missing required parameter", 55 | "Please open a Bug report with the information above": "Please open a Bug report with the information above", 56 | "Prompt to run": "Prompt to run", 57 | "You": "You" 58 | } 59 | -------------------------------------------------------------------------------- /src/helpers/i18n.ts: -------------------------------------------------------------------------------- 1 | import i18next from 'i18next'; 2 | import zhHansTranslation from '../locales/zh-Hans.json'; 3 | import zhHantTranslation from '../locales/zh-Hant.json'; 4 | import esTranslation from '../locales/es.json'; 5 | import jpTranslation from '../locales/jp.json'; 6 | import koTranslation from '../locales/ko.json'; 7 | import frTranslation from '../locales/fr.json'; 8 | import deTranslation from '../locales/de.json'; 9 | import ruTranslation from '../locales/ru.json'; 10 | import ukTranslation from '../locales/uk.json'; 11 | import viTranslation from '../locales/vi.json'; 12 | import arTranslation from '../locales/ar.json'; 13 | import ptTranslation from '../locales/pt.json'; 14 | import idTranslation from '../locales/id.json'; 15 | 16 | let currentlang: string = 'en'; 17 | 18 | const languages: Record = { 19 | en: 'English', 20 | 'zh-Hans': '简体中文', // simplified Chinese 21 | 'zh-Hant': '繁體中文', // traditional Chinese 22 | es: 'Español', // Spanish 23 | jp: '日本語', // Japanese 24 | ko: '한국어', // Korean 25 | fr: 'Français', // French 26 | de: 'Deutsch', // German 27 | ru: 'Русский', // Russian 28 | uk: 'Українська', // Ukrainian 29 | vi: 'Tiếng Việt', // Vietnamese 30 | ar: 'العربية', // Arabic 31 | pt: 'Português', // Portuguese 32 | id: 'Indonesia', // Indonesia 33 | }; 34 | 35 | i18next.init({ 36 | lng: currentlang, 37 | fallbackLng: 'en', 38 | resources: { 39 | 'zh-Hans': { 40 | translation: zhHansTranslation, 41 | }, 42 | 'zh-Hant': { 43 | translation: zhHantTranslation, 44 | }, 45 | es: { 46 | translation: esTranslation, 47 | }, 48 | jp: { 49 | translation: jpTranslation, 50 | }, 51 | ko: { 52 | translation: koTranslation, 53 | }, 54 | fr: { 55 | translation: frTranslation, 56 | }, 57 | de: { 58 | translation: deTranslation, 59 | }, 60 | ru: { 61 | translation: ruTranslation, 62 | }, 63 | uk: { 64 | translation: ukTranslation, 65 | }, 66 | vi: { 67 | translation: viTranslation, 68 | }, 69 | ar: { 70 | translation: arTranslation, 71 | }, 72 | pt: { 73 | translation: ptTranslation, 74 | }, 75 | id: { 76 | translation: idTranslation, 77 | }, 78 | }, 79 | }); 80 | 81 | /** 82 | * Adds a public method called "t" that takes a string parameter and returns a string. 83 | * @param key - The translation key to look up. 84 | * @returns The translated string. 85 | */ 86 | const t = (key: string): string => { 87 | if (!currentlang || currentlang === 'en') return key; 88 | return i18next.t(key) as string; 89 | }; 90 | 91 | const setLanguage = (lang: string) => { 92 | currentlang = lang || 'en'; 93 | i18next.changeLanguage(currentlang); 94 | }; 95 | 96 | const getCurrentLanguagenName = () => { 97 | return languages[currentlang]; 98 | }; 99 | 100 | export default { setLanguage, t, getCurrentLanguagenName, languages }; 101 | -------------------------------------------------------------------------------- /src/locales/tr.json: -------------------------------------------------------------------------------- 1 | { 2 | "Starting new conversation": "Yeni konuşma başlıyor", 3 | "Goodbye!": "Güle güle!", 4 | "send a message ('exit' to quit)": "bir mesaj gönder (çıkmak için 'exit' yaz)", 5 | "Please enter a prompt.": "Lütfen bir bilgi istemi girin.", 6 | "THINKING...": "DÜŞÜNÜYOR...", 7 | "Please set your OpenAI API key via `ai config set OPENAI_KEY=`": "Lütfen OpenAI anahtarınızı bunla ayarlayın `ai config set OPENAI_KEY=`", 8 | "Set config": "Yapılandırmayı ayarla", 9 | "Enter your OpenAI API key": "OpenAI API anahtarınızı girin", 10 | "(not set)": "(ayarlanmadı)", 11 | "OpenAI Key": "OpenAI Anahtarınız", 12 | "Please enter a key": "Lütfen bir anahtar girin", 13 | "OpenAI API Endpoint": "OpenAI API Uçnoktası", 14 | "Enter your OpenAI API Endpoint": "OpenAI API Uçnoktanızı girin", 15 | "Silent Mode": "Sessiz Mod", 16 | "Enable silent mode?": "Sessiz mod etkinleştirilsin mi?", 17 | "Model": "Model", 18 | "Enter the model you want to use": "Kullanmak istediğiniz modeli girin", 19 | "Language": "Dil", 20 | "Enter the language you want to use": "Kullanmak istediğiniz dili girin", 21 | "What would you like me to do?": "Ne yapmamı istersiniz?", 22 | "delete all log files": "tüm günlük kayıt dosyalarını sil", 23 | "list js files": "js dosyalarını listele", 24 | "fetch me a random joke": "bana rastgele bir şaka yap", 25 | "list all commits": "tüm işlemeleri listele", 26 | "Say hello": "Merhaba de", 27 | "you can edit script here": "burada kodunuzu düzenleyebilirsiniz", 28 | "What would you like me to change in this script?": "Bu kodda neyi değiştirmemi istersiniz?", 29 | "e.g.": "örneğin", 30 | "e.g. change the folder name": "örneğin klasör adını değiştir", 31 | "Your script": "Kodunuz", 32 | "Loading...": "Yükleniyor...", 33 | "Getting explanation...": "Açıklama alınıyor...", 34 | "Explanation": "Açıklama", 35 | "Run this script?": "Bu kodu çalıştırayım mı?", 36 | "Revise this script?": "Bu kodu düzenleyeyim mi?", 37 | "Yes": "Evet", 38 | "Lets go!": "Haydi gidelim!", 39 | "Edit": "Düzenle", 40 | "Make some adjustments before running": "Çalıştırmadan önce bazı ayarlamalar yapın", 41 | "Revise": "Düzenle", 42 | "Give feedback via prompt and get a new result": "Geri bildirimde bulunun ve yeni bir sonuç alın", 43 | "Copy": "Kopyala", 44 | "Copy the generated script to your clipboard": "Oluşturulan kodu panonuza kopyalayın", 45 | "Cancel": "İptal", 46 | "Exit the program": "Programdan çık", 47 | "Running": "Çalışıyor", 48 | "Copied to clipboard!": "Panoya kopyalandı!", 49 | "Your new script": "Yeni kodunuz", 50 | "Invalid config property": "Geçersiz yapılandırma girdisi", 51 | "Shell detection failed unexpectedly": "Kabuk tespiti beklenmedik bir şekilde hata verdi", 52 | "Invalid model": "Geçersiz model", 53 | "Error": "Hata", 54 | "Missing required parameter": "Gerekli parametre eksik", 55 | "Please open a Bug report with the information above": "Lütfen yukarıdaki bilgilerle bir Hata raporu açın", 56 | "Prompt to run": "Çalıştırma istemi", 57 | "You": "Siz" 58 | } 59 | -------------------------------------------------------------------------------- /src/locales/id.json: -------------------------------------------------------------------------------- 1 | { 2 | "Starting new conversation": "Memulai percakapan baru", 3 | "Goodbye!": "Selamat tinggal!", 4 | "send a message ('exit' to quit)": "kirim pesan ('exit' untuk keluar)", 5 | "Please enter a prompt.": "Silakan masukkan permintaan.", 6 | "THINKING...": "BERPIKIR...", 7 | "Please set your OpenAI API key via ai config set OPENAI_KEY=": "Harap atur kunci API OpenAI Anda melalui ai config set OPENAI_KEY=", 8 | "Set config": "Atur konfigurasi", 9 | "Enter your OpenAI API key": "Masukkan kunci API OpenAI Anda", 10 | "(not set)": "(belum diatur)", 11 | "OpenAI Key": "Kunci OpenAI", 12 | "Please enter a key": "Silakan masukkan kunci", 13 | "OpenAI API Endpoint": "Titik Akhir API OpenAI", 14 | "Enter your OpenAI API Endpoint": "Masukkan Titik Akhir API OpenAI Anda", 15 | "Silent Mode": "Mode Senyap", 16 | "Enable silent mode?": "Aktifkan mode senyap?", 17 | "Model": "Model", 18 | "Enter the model you want to use": "Masukkan model yang ingin Anda gunakan", 19 | "Language": "Bahasa", 20 | "Enter the language you want to use": "Masukkan bahasa yang ingin Anda gunakan", 21 | "What would you like me to do?": "Apa yang ingin Anda saya lakukan?", 22 | "delete all log files": "hapus semua file log", 23 | "list js files": "daftar file js", 24 | "fetch me a random joke": "berikan saya lelucon acak", 25 | "list all commits": "daftar semua komit", 26 | "Say hello": "Katakan halo", 27 | "you can edit script here": "Anda dapat mengedit skrip di sini", 28 | "What would you like me to change in this script?": "Apa yang ingin Anda saya ubah dalam skrip ini?", 29 | "e.g.": "contoh", 30 | "e.g. change the folder name": "contoh: ubah nama folder", 31 | "Your script": "Skrip Anda", 32 | "Loading...": "Memuat...", 33 | "Getting explanation...": "Mendapatkan penjelasan...", 34 | "Explanation": "Penjelasan", 35 | "Run this script?": "Jalankan skrip ini?", 36 | "Revise this script?": "Periksa ulang skrip ini?", 37 | "Yes": "Ya", 38 | "Lets go!": "Ayo!", 39 | "Edit": "Edit", 40 | "Make some adjustments before running": "Lakukan beberapa penyesuaian sebelum menjalankan", 41 | "Revise": "Periksa ulang", 42 | "Give feedback via prompt and get a new result": "Beri umpan balik melalui permintaan dan dapatkan hasil baru", 43 | "Copy": "Salin", 44 | "Copy the generated script to your clipboard": "Salin skrip yang dihasilkan ke papan klip Anda", 45 | "Cancel": "Batal", 46 | "Exit the program": "Keluar dari program", 47 | "Running": "Menjalankan", 48 | "Copied to clipboard!": "Disalin ke papan klip!", 49 | "Your new script": "Skrip baru Anda", 50 | "Invalid config property": "Properti konfigurasi tidak valid", 51 | "Shell detection failed unexpectedly": "Deteksi shell gagal secara tak terduga", 52 | "Invalid model": "Model tidak valid", 53 | "Error": "Kesalahan", 54 | "Missing required parameter": "Parameter yang diperlukan hilang", 55 | "Please open a Bug report with the information above": "Harap buka laporan Bug dengan informasi di atas", 56 | "Prompt to run": "Permintaan untuk dijalankan", 57 | "You": "Anda" 58 | } 59 | -------------------------------------------------------------------------------- /src/locales/vi.json: -------------------------------------------------------------------------------- 1 | { 2 | "Starting new conversation": "Bắt đầu cuộc trò chuyện mới", 3 | "Goodbye!": "Tạm biệt!", 4 | "send a message ('exit' to quit)": "gửi tin nhắn ('exit' để thoát)", 5 | "Please enter a prompt.": "Vui lòng nhập một lời nhắn.", 6 | "THINKING...": "ĐANG SUY NGHĨ...", 7 | "Please set your OpenAI API key via `ai config set OPENAI_KEY=`": "Vui lòng thiết lập khóa API OpenAI của bạn thông qua `ai config set OPENAI_KEY=`", 8 | "Set config": "Cài đặt cấu hình", 9 | "Enter your OpenAI API key": "Nhập khóa API OpenAI của bạn", 10 | "(not set)": "(không được đặt)", 11 | "OpenAI Key": "Khóa OpenAI", 12 | "Please enter a key": "Vui lòng nhập một khóa", 13 | "OpenAI API Endpoint": "Điểm cuối API OpenAI", 14 | "Enter your OpenAI API Endpoint": "Nhập Điểm cuối API OpenAI của bạn", 15 | "Silent Mode": "Chế độ im lặng", 16 | "Enable silent mode?": "Bật chế độ im lặng?", 17 | "Model": "Mô hình", 18 | "Enter the model you want to use": "Nhập mô hình bạn muốn sử dụng", 19 | "Language": "Ngôn ngữ", 20 | "Enter the language you want to use": "Nhập ngôn ngữ bạn muốn sử dụng", 21 | "What would you like me to do?": "Bạn muốn tôi làm gì?", 22 | "delete all log files": "xóa tất cả các tệp nhật ký", 23 | "list js files": "liệt kê các tệp js", 24 | "fetch me a random joke": "lấy cho tôi một câu chuyện cười ngẫu nhiên", 25 | "list all commits": "liệt kê tất cả các commit", 26 | "Say hello": "Nói xin chào", 27 | "you can edit script here": "bạn có thể chỉnh sửa kịch bản ở đây", 28 | "What would you like me to change in this script?": "Bạn muốn tôi thay đổi gì trong kịch bản này?", 29 | "e.g.": "ví dụ", 30 | "e.g. change the folder name": "ví dụ thay đổi tên thư mục", 31 | "Your script": "Kịch bản của bạn", 32 | "Loading...": "Đang tải...", 33 | "Getting explanation...": "Đang lấy giải thích...", 34 | "Explanation": "Giải thích", 35 | "Run this script?": "Chạy kịch bản này?", 36 | "Revise this script?": "Điều chỉnh kịch bản này?", 37 | "Yes": "Có", 38 | "Lets go!": "Hãy đi!", 39 | "Edit": "Chỉnh sửa", 40 | "Make some adjustments before running": "Thực hiện một số điều chỉnh trước khi chạy", 41 | "Revise": "Điều chỉnh", 42 | "Give feedback via prompt and get a new result": "Đưa ra phản hồi qua lời nhắc và nhận kết quả mới", 43 | "Copy": "Sao chép", 44 | "Copy the generated script to your clipboard": "Sao chép kịch bản được tạo ra vào clipboard của bạn", 45 | "Cancel": "Hủy", 46 | "Exit the program": "Thoát chương trình", 47 | "Running": "Đang chạy", 48 | "Copied to clipboard!": "Đã sao chép vào clipboard!", 49 | "Your new script": "Kịch bản mới của bạn", 50 | "Invalid config property": "Thuộc tính cấu hình không hợp lệ", 51 | "Shell detection failed unexpectedly": "Phát hiện Shell thất bại một cách không mong đợi", 52 | "Invalid model": "Mô hình không hợp lệ", 53 | "Error": "Lỗi", 54 | "Missing required parameter": "Thiếu tham số bắt buộc", 55 | "Please open a Bug report with the information above": "Vui lòng mở một báo cáo lỗi với thông tin ở trên", 56 | "Prompt to run": "Lời nhắc để chạy", 57 | "You": "Bạn" 58 | } 59 | -------------------------------------------------------------------------------- /src/locales/uk.json: -------------------------------------------------------------------------------- 1 | { 2 | "Starting new conversation": "Початок нової розмови", 3 | "Goodbye!": "До побачення!", 4 | "send a message ('exit' to quit)": "надіслати повідомлення ('вихід', щоб вийти)", 5 | "Please enter a prompt.": "Будь ласка, введіть запитання.", 6 | "THINKING...": "ДУМАЮ...", 7 | "Please set your OpenAI API key via `ai config set OPENAI_KEY=`": "Будь ласка, встановіть свій ключ OpenAI API через `ai config set OPENAI_KEY=`", 8 | "Set config": "Встановити конфігурацію", 9 | "Enter your OpenAI API key": "Введіть свій ключ OpenAI API", 10 | "(not set)": "(не встановлено)", 11 | "OpenAI Key": "Ключ OpenAI", 12 | "Please enter a key": "Будь ласка, введіть ключ", 13 | "OpenAI API Endpoint": "Кінцева точка OpenAI API", 14 | "Enter your OpenAI API Endpoint": "Введіть кінцеву точку OpenAI API", 15 | "Silent Mode": "Тихий режим", 16 | "Enable silent mode?": "Увімкнути тихий режим?", 17 | "Model": "Модель", 18 | "Enter the model you want to use": "Введіть модель, яку ви хочете використовувати", 19 | "Language": "Мова", 20 | "Enter the language you want to use": "Введіть мову, яку ви хочете використовувати", 21 | "What would you like me to do?": "Що ви хочете, щоб я зробив?", 22 | "delete all log files": "видалити всі файли журналу", 23 | "list js files": "список js файлів", 24 | "fetch me a random joke": "знайти випадковий жарт", 25 | "list all commits": "список всіх комітів", 26 | "Say hello": "Скажіть привіт", 27 | "you can edit script here": "ви можете редагувати скрипт тут", 28 | "What would you like me to change in this script?": "Що ви хочете змінити в цьому скрипті?", 29 | "e.g.": "наприклад", 30 | "e.g. change the folder name": "наприклад, змінити назву папки", 31 | "Your script": "Ваш скрипт", 32 | "Loading...": "Завантаження...", 33 | "Getting explanation...": "Отримання пояснення...", 34 | "Explanation": "Пояснення", 35 | "Run this script?": "Запустити цей скрипт?", 36 | "Revise this script?": "Переглянути цей скрипт?", 37 | "Yes": "Так", 38 | "Lets go!": "Пішли!", 39 | "Edit": "Редагувати", 40 | "Make some adjustments before running": "Внесіть деякі зміни перед запуском", 41 | "Revise": "Переглянути", 42 | "Give feedback via prompt and get a new result": "Дайте відгук через промпт і отримайте новий результат", 43 | "Copy": "Копіювати", 44 | "Copy the generated script to your clipboard": "Скопіюйте згенерований скрипт в буфер обміну", 45 | "Cancel": "Скасувати", 46 | "Exit the program": "Вийти з програми", 47 | "Running": "Виконується", 48 | "Copied to clipboard!": "Скопійовано в буфер обміну!", 49 | "Your new script": "Ваш новий скрипт", 50 | "Invalid config property": "Недійсна властивість конфігурації", 51 | "Shell detection failed unexpectedly": "Неочікувано не вдалося виявити оболонку", 52 | "Invalid model": "Недійсна модель", 53 | "Error": "Помилка", 54 | "Missing required parameter": "Відсутній обов'язковий параметр", 55 | "Please open a Bug report with the information above": "Будь ласка, відкрийте звіт про помилку з інформацією вище", 56 | "Prompt to run": "Запит на запуск", 57 | "You": "Ви" 58 | } 59 | -------------------------------------------------------------------------------- /src/locales/pt.json: -------------------------------------------------------------------------------- 1 | { 2 | "Starting new conversation": "Começando nova conversa", 3 | "Goodbye!": "Tchau!", 4 | "send a message ('exit' to quit)": "enviar uma mensagem ('exit' para sair)", 5 | "Please enter a prompt.": "Por favor, insira um prompt.", 6 | "THINKING...": "PENSANDO...", 7 | "Please set your OpenAI API key via `ai config set OPENAI_KEY=`": "Por favor, defina sua chave de API OpenAI via `ai config set OPENAI_KEY=`", 8 | "Set config": "Definir configuração", 9 | "Enter your OpenAI API key": "Insira sua chave de API OpenAI", 10 | "(not set)": "(não definido)", 11 | "OpenAI Key": "Chave OpenAI", 12 | "Please enter a key": "Por favor, insira uma chave", 13 | "OpenAI API Endpoint": "Endpoint da API OpenAI", 14 | "Enter your OpenAI API Endpoint": "Insira seu Endpoint da API OpenAI", 15 | "Silent Mode": "Modo silencioso", 16 | "Enable silent mode?": "Ativar modo silencioso?", 17 | "Model": "Modelo", 18 | "Enter the model you want to use": "Insira o modelo que você deseja usar", 19 | "Language": "Língua", 20 | "Enter the language you want to use": "Insira a língua que você deseja usar", 21 | "What would you like me to do?": "O que você gostaria que eu fizesse?", 22 | "delete all log files": "excluir todos os arquivos de log", 23 | "list js files": "listar arquivos js", 24 | "fetch me a random joke": "me traga uma piada aleatória", 25 | "list all commits": "listar todos os commits", 26 | "Say hello": "Diga olá", 27 | "you can edit script here": "você pode editar o script aqui", 28 | "What would you like me to change in this script?": "O que você gostaria que eu mudasse neste script?", 29 | "e.g.": "por exemplo", 30 | "e.g. change the folder name": "por exemplo, mude o nome da pasta", 31 | "Your script": "Seu script", 32 | "Loading...": "Carregando...", 33 | "Getting explanation...": "Obtendo explicação...", 34 | "Explanation": "Explicação", 35 | "Run this script?": "Executar este script?", 36 | "Revise this script?": "Revisar este script?", 37 | "Yes": "Sim", 38 | "Lets go!": "Vamos lá!", 39 | "Edit": "Editar", 40 | "Make some adjustments before running": "Faça alguns ajustes antes de executar", 41 | "Revise": "Revisar", 42 | "Give feedback via prompt and get a new result": "Dê feedback via prompt e obtenha um novo resultado", 43 | "Copy": "Copiar", 44 | "Copy the generated script to your clipboard": "Copie o script gerado para a área de transferência", 45 | "Cancel": "Cancelar", 46 | "Exit the program": "Sair do programa", 47 | "Running": "Executando", 48 | "Copied to clipboard!": "Copiado para a área de transferência!", 49 | "Your new script": "Seu novo script", 50 | "Invalid config property": "Propriedade de configuração inválida", 51 | "Shell detection failed unexpectedly": "A detecção do shell falhou inesperadamente", 52 | "Invalid model": "Modelo inválido", 53 | "Error": "Erro", 54 | "Missing required parameter": "Parâmetro obrigatório faltando", 55 | "Please open a Bug report with the information above": "Por favor, abra um relatório de bug com as informações acima", 56 | "Prompt to run": "Prompt para executar", 57 | "You": "Você" 58 | } 59 | -------------------------------------------------------------------------------- /src/locales/ru.json: -------------------------------------------------------------------------------- 1 | { 2 | "Starting new conversation": "Начало нового разговора", 3 | "Goodbye!": "До свидания!", 4 | "send a message ('exit' to quit)": "отправить сообщение ('выход' для выхода)", 5 | "Please enter a prompt.": "Пожалуйста, введите запрос.", 6 | "THINKING...": "ДУМАЮ...", 7 | "Please set your OpenAI API key via `ai config set OPENAI_KEY=`": "Пожалуйста, установите свой ключ OpenAI API через `ai config set OPENAI_KEY=`", 8 | "Set config": "Установить конфигурацию", 9 | "Enter your OpenAI API key": "Введите ваш ключ OpenAI API", 10 | "(not set)": "(не установлено)", 11 | "OpenAI Key": "Ключ OpenAI", 12 | "Please enter a key": "Пожалуйста, введите ключ", 13 | "OpenAI API Endpoint": "Конечная точка OpenAI API", 14 | "Enter your OpenAI API Endpoint": "Введите конечную точку OpenAI API OpenAI", 15 | "Silent Mode": "Тихий режим", 16 | "Enable silent mode?": "Включить тихий режим?", 17 | "Model": "Модель", 18 | "Enter the model you want to use": "Введите модель, которую вы хотите использовать", 19 | "Language": "Язык", 20 | "Enter the language you want to use": "Введите язык, который вы хотите использовать", 21 | "What would you like me to do?": "Что бы вы хотели, чтобы я сделал?", 22 | "delete all log files": "удалить все файлы журналов", 23 | "list js files": "список js файлов", 24 | "fetch me a random joke": "получить случайную шутку", 25 | "list all commits": "список всех коммитов", 26 | "Say hello": "Скажи привет", 27 | "you can edit script here": "вы можете редактировать скрипт здесь", 28 | "What would you like me to change in this script?": "Что бы вы хотели изменить в этом скрипте?", 29 | "e.g.": "например", 30 | "e.g. change the folder name": "например, изменить имя папки", 31 | "Your script": "Ваш скрипт", 32 | "Loading...": "Загрузка...", 33 | "Getting explanation...": "Получение объяснения...", 34 | "Explanation": "Объяснение", 35 | "Run this script?": "Запустить этот скрипт?", 36 | "Revise this script?": "Пересмотреть этот скрипт?", 37 | "Yes": "Да", 38 | "Lets go!": "Поехали!", 39 | "Edit": "Редактировать", 40 | "Make some adjustments before running": "Внесите некоторые изменения перед запуском", 41 | "Revise": "Пересмотреть", 42 | "Give feedback via prompt and get a new result": "Оставьте отзыв через приглашение и получите новый результат", 43 | "Copy": "Копировать", 44 | "Copy the generated script to your clipboard": "Скопировать сгенерированный скрипт в буфер обмена", 45 | "Cancel": "Отмена", 46 | "Exit the program": "Выход из программы", 47 | "Running": "Запуск", 48 | "Copied to clipboard!": "Скопировано в буфер обмена!", 49 | "Your new script": "Ваш новый скрипт", 50 | "Invalid config property": "Недопустимое свойство конфигурации", 51 | "Shell detection failed unexpectedly": "Обнаружение оболочки неожиданно не удалось", 52 | "Invalid model": "Недопустимая модель", 53 | "Error": "Ошибка", 54 | "Missing required parameter": "Отсутствует обязательный параметр", 55 | "Please open a Bug report with the information above": "Пожалуйста, откройте отчет об ошибке с указанной выше информацией", 56 | "Prompt to run": "Приглашение на запуск", 57 | "You": "Вы" 58 | } 59 | -------------------------------------------------------------------------------- /src/locales/es.json: -------------------------------------------------------------------------------- 1 | { 2 | "Starting new conversation": "Comenzando nueva conversación", 3 | "Goodbye!": "¡Adiós!", 4 | "send a message ('exit' to quit)": "enviar un mensaje ('exit' para salir)", 5 | "Please enter a prompt.": "Por favor ingrese un indicio.", 6 | "THINKING...": "PENSANDO...", 7 | "Please set your OpenAI API key via `ai config set OPENAI_KEY=`": "Por favor configure su clave de API de OpenAI a través de `ai config set OPENAI_KEY=`", 8 | "Set config": "Configuración", 9 | "Enter your OpenAI API key": "Ingrese su clave de API de OpenAI", 10 | "(not set)": "(no establecido)", 11 | "OpenAI Key": "Clave de OpenAI", 12 | "Please enter a key": "Por favor ingrese una clave", 13 | "OpenAI API Endpoint": "Punto final de la API de OpenAI", 14 | "Enter your OpenAI API Endpoint": "Ingrese su punto final de API de OpenAI", 15 | "Silent Mode": "Modo silencioso", 16 | "Enable silent mode?": "¿Habilitar el modo silencioso?", 17 | "Model": "Modelo", 18 | "Enter the model you want to use": "Ingrese el modelo que desea utilizar", 19 | "Language": "Idioma", 20 | "Enter the language you want to use": "Ingrese el idioma que desea utilizar", 21 | "What would you like me to do?": "¿Qué te gustaría que hiciera?", 22 | "delete all log files": "eliminar todos los archivos de registro", 23 | "list js files": "listar archivos js", 24 | "fetch me a random joke": "traerme una broma aleatoria", 25 | "list all commits": "listar todos los commits", 26 | "Say hello": "Decir hola", 27 | "you can edit script here": "puede editar el script aquí", 28 | "What would you like me to change in this script?": "¿Qué te gustaría cambiar en este script?", 29 | "e.g.": "por ejemplo", 30 | "e.g. change the folder name": "por ejemplo, cambie el nombre de la carpeta", 31 | "Your script": "Tu script", 32 | "Loading...": "Cargando...", 33 | "Getting explanation...": "Obteniendo explicación...", 34 | "Explanation": "Explicación", 35 | "Run this script?": "¿Ejecutar este script?", 36 | "Revise this script?": "¿Revisar este script?", 37 | "Yes": "Sí", 38 | "Lets go!": "¡Vamos!", 39 | "Edit": "Editar", 40 | "Make some adjustments before running": "Haga algunos ajustes antes de ejecutar", 41 | "Revise": "Revisar", 42 | "Give feedback via prompt and get a new result": "Dé su opinión a través del indicio y obtenga un nuevo resultado", 43 | "Copy": "Copiar", 44 | "Copy the generated script to your clipboard": "Copie el script generado en su portapapeles", 45 | "Cancel": "Cancelar", 46 | "Exit the program": "Salir del programa", 47 | "Running": "Ejecutando", 48 | "Copied to clipboard!": "¡Copiado al portapapeles!", 49 | "Your new script": "Tu nuevo script", 50 | "Invalid config property": "Propiedad de configuración no válida", 51 | "Shell detection failed unexpectedly": "La detección de shell falló inesperadamente", 52 | "Invalid model": "Modelo no válido", 53 | "Error": "Error", 54 | "Missing required parameter": "Falta el parámetro requerido", 55 | "Please open a Bug report with the information above": "Por favor abra un informe de error con la información anterior", 56 | "Prompt to run": "Indicio para ejecutar", 57 | "You": "Tú" 58 | } 59 | -------------------------------------------------------------------------------- /src/locales/fr.json: -------------------------------------------------------------------------------- 1 | { 2 | "Starting new conversation": "Démarrer une nouvelle conversation", 3 | "Goodbye!": "Au revoir!", 4 | "send a message ('exit' to quit)": "envoyer un message ('exit' pour quitter)", 5 | "Please enter a prompt.": "Veuillez entrer une invite.", 6 | "THINKING...": "EN RÉFLEXION...", 7 | "Please set your OpenAI API key via `ai config set OPENAI_KEY=`": "Veuillez définir votre clé API OpenAI via `ai config set OPENAI_KEY=`", 8 | "Set config": "Configurer", 9 | "Enter your OpenAI API key": "Entrez votre clé API OpenAI", 10 | "(not set)": "(non défini)", 11 | "OpenAI Key": "Clé OpenAI", 12 | "Please enter a key": "Veuillez entrer une clé", 13 | "OpenAI API Endpoint": "Point de terminaison de l'API OpenAI", 14 | "Enter your OpenAI API Endpoint": "Entrez votre point de terminaison d'API OpenAI", 15 | "Silent Mode": "Mode silencieux", 16 | "Enable silent mode?": "Activer le mode silencieux?", 17 | "Model": "Modèle", 18 | "Enter the model you want to use": "Entrez le modèle que vous souhaitez utiliser", 19 | "Language": "Langue", 20 | "Enter the language you want to use": "Entrez la langue que vous souhaitez utiliser", 21 | "What would you like me to do?": "Que voulez-vous que je fasse?", 22 | "delete all log files": "supprimer tous les fichiers journaux", 23 | "list js files": "liste des fichiers js", 24 | "fetch me a random joke": "apporte-moi une blague aléatoire", 25 | "list all commits": "liste de tous les engagements", 26 | "Say hello": "Dire bonjour", 27 | "you can edit script here": "vous pouvez éditer le script ici", 28 | "What would you like me to change in this script?": "Que voulez-vous que je change dans ce script?", 29 | "e.g.": "par exemple", 30 | "e.g. change the folder name": "par exemple, changer le nom du dossier", 31 | "Your script": "Votre script", 32 | "Loading...": "Chargement...", 33 | "Getting explanation...": "Obtenir une explication...", 34 | "Explanation": "Explication", 35 | "Run this script?": "Exécuter ce script?", 36 | "Revise this script?": "Réviser ce script?", 37 | "Yes": "Oui", 38 | "Lets go!": "Allons-y!", 39 | "Edit": "Modifier", 40 | "Make some adjustments before running": "Faites quelques ajustements avant de lancer", 41 | "Revise": "Réviser", 42 | "Give feedback via prompt and get a new result": "Donnez votre avis via une invite et obtenez un nouveau résultat", 43 | "Copy": "Copier", 44 | "Copy the generated script to your clipboard": "Copiez le script généré dans votre presse-papiers", 45 | "Cancel": "Annuler", 46 | "Exit the program": "Quitter le programme", 47 | "Running": "En cours d'exécution", 48 | "Copied to clipboard!": "Copié dans le presse-papiers!", 49 | "Your new script": "Votre nouveau script", 50 | "Invalid config property": "Propriété de configuration non valide", 51 | "Shell detection failed unexpectedly": "La détection de la coquille a échoué de manière inattendue", 52 | "Invalid model": "Modèle invalide", 53 | "Error": "Erreur", 54 | "Missing required parameter": "Paramètre requis manquant", 55 | "Please open a Bug report with the information above": "Veuillez ouvrir un rapport de bogue avec les informations ci-dessus", 56 | "Prompt to run": "Invite pour exécuter", 57 | "You": "Vous" 58 | } 59 | -------------------------------------------------------------------------------- /src/locales/de.json: -------------------------------------------------------------------------------- 1 | { 2 | "Starting new conversation": "Neues Gespräch beginnen", 3 | "Goodbye!": "Auf Wiedersehen!", 4 | "send a message ('exit' to quit)": "Nachricht senden ('exit' zum Beenden)", 5 | "Please enter a prompt.": "Bitte geben Sie eine Aufforderung ein.", 6 | "THINKING...": "DENKEN...", 7 | "Please set your OpenAI API key via `ai config set OPENAI_KEY=`": "Bitte setzen Sie Ihren OpenAI-API-Schlüssel über `ai config set OPENAI_KEY=`", 8 | "Set config": "Konfiguration setzen", 9 | "Enter your OpenAI API key": "Geben Sie Ihren OpenAI-API-Schlüssel ein", 10 | "(not set)": "(nicht gesetzt)", 11 | "OpenAI Key": "OpenAI-Schlüssel", 12 | "Please enter a key": "Bitte geben Sie einen Schlüssel ein", 13 | "OpenAI API Endpoint": "OpenAI-API-Endpunkt", 14 | "Enter your OpenAI API Endpoint": "Geben Sie Ihren OpenAI-API-Endpunkt ein", 15 | "Silent Mode": "Leiser Modus", 16 | "Enable silent mode?": "Leisen Modus aktivieren?", 17 | "Model": "Modell", 18 | "Enter the model you want to use": "Geben Sie das Modell ein, das Sie verwenden möchten", 19 | "Language": "Sprache", 20 | "Enter the language you want to use": "Geben Sie die Sprache ein, die Sie verwenden möchten", 21 | "What would you like me to do?": "Was möchten Sie, dass ich tue?", 22 | "delete all log files": "Löschen Sie alle Protokolldateien", 23 | "list js files": "Liste js-Dateien auf", 24 | "fetch me a random joke": "Holen Sie mir einen zufälligen Witz", 25 | "list all commits": "Liste alle Commits auf", 26 | "Say hello": "Sag Hallo", 27 | "you can edit script here": "Sie können das Skript hier bearbeiten", 28 | "What would you like me to change in this script?": "Was möchten Sie in diesem Skript ändern?", 29 | "e.g.": "z.B.", 30 | "e.g. change the folder name": "z.B. Ändern Sie den Ordnernamen", 31 | "Your script": "Ihr Skript", 32 | "Loading...": "Wird geladen...", 33 | "Getting explanation...": "Erklärung bekommen...", 34 | "Explanation": "Erklärung", 35 | "Run this script?": "Dieses Skript ausführen?", 36 | "Revise this script?": "Dieses Skript überarbeiten?", 37 | "Yes": "Ja", 38 | "Lets go!": "Los geht's!", 39 | "Edit": "Bearbeiten", 40 | "Make some adjustments before running": "Machen Sie einige Anpassungen, bevor Sie es ausführen", 41 | "Revise": "Überarbeiten", 42 | "Give feedback via prompt and get a new result": "Geben Sie Feedback über die Eingabeaufforderung und erhalten Sie ein neues Ergebnis", 43 | "Copy": "Kopieren", 44 | "Copy the generated script to your clipboard": "Kopieren Sie das generierte Skript in die Zwischenablage", 45 | "Cancel": "Abbrechen", 46 | "Exit the program": "Programm beenden", 47 | "Running": "Läuft", 48 | "Copied to clipboard!": "In die Zwischenablage kopiert!", 49 | "Your new script": "Ihr neues Skript", 50 | "Invalid config property": "Ungültige Konfigurationseigenschaft", 51 | "Shell detection failed unexpectedly": "Shell-Erkennung fehlgeschlagen", 52 | "Invalid model": "Ungültiges Modell", 53 | "Error": "Fehler", 54 | "Missing required parameter": "Fehlender erforderlicher Parameter", 55 | "Please open a Bug report with the information above": "Bitte öffnen Sie einen Fehlerbericht mit den oben genannten Informationen", 56 | "Prompt to run": "Aufforderung zum Ausführen", 57 | "You": "Sie" 58 | } 59 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.0.12 2 | 3 | - Bug fixes 4 | - Default to gpt-4o-mini instead of gpt-3.5-turbo 5 | 6 | ## 1.0.8 7 | 8 | - Fix a bug causing completions to not display in some cases 9 | - Allow hitting "q" or "esc" to stop the streaming output 10 | 11 | ## 1.0.7 12 | 13 | - Fix a bug causing completions to not display in some cases 14 | 15 | ## 1.0.6 16 | 17 | - Fix for streaming data including the shell command name erroneously, causing the command to fail 18 | 19 | ## 1.0.5 20 | 21 | - Fix using the configured model in all cases 22 | 23 | ## 1.0.4 24 | 25 | - Improve `--help` menu descriptions 26 | 27 | ## 1.0.3 28 | 29 | - Fix choosing custom models 30 | 31 | ## 1.0.2 32 | 33 | - Add Turkish language support 34 | - Improve config UX (correctly `await` on setting configs) 35 | 36 | ## 1.0.1 37 | 38 | - Fix a typo ("to to" -> "to") [#78](https://github.com/BuilderIO/ai-shell/pull/78) 39 | 40 | ## 1.0.0 41 | 42 | - Overdue major version bump to signify the stability of this CLI 43 | - Add Indonesian language support 44 | 45 | ## 0.1.23 46 | 47 | - Add support for Android [#75](https://github.com/BuilderIO/ai-shell/pull/75) 48 | 49 | ## 0.1.22 50 | 51 | - Fix a crash on linux [#64](https://github.com/BuilderIO/ai-shell/issues/64) 52 | 53 | ## 0.1.21 54 | 55 | - Fix a bug preventing initial configuration [#69](https://github.com/BuilderIO/ai-shell/issues/69) 56 | 57 | ## 0.1.20 58 | 59 | - Multi language support [#62](https://github.com/BuilderIO/ai-shell/pull/62) 60 | 61 | ## 0.1.19 62 | 63 | - Support Azure keys 64 | 65 | ## 0.1.18 66 | 67 | - Option to copy the generated script to the clipboard 68 | 69 | ## 0.1.15 70 | 71 | - Fix chat mode to support chat history 72 | 73 | ## 0.1.14 74 | 75 | - Adds chat mode 76 | - Fixes "ai update" to not eagerly capture 77 | - Improve operating system identification 78 | 79 | ## 0.1.13 80 | 81 | - Fix for using cloudflare proxy (fix streaming logic to handle partial chunks) 82 | 83 | ## 0.1.12 84 | 85 | - Support custom API endpoints 86 | 87 | ## 0.1.11 88 | 89 | - `ai update` command 90 | - Minor UX improvements and fixes 91 | 92 | ## 0.1.10 93 | 94 | - Obfuscate API key from config UI 95 | 96 | ## 0.1.9 97 | 98 | - Visual config UI (from `ai config ui`) 99 | 100 | ## 0.1.8 101 | 102 | - Better error handling (handle streams and non streams, JSON and strings) 103 | 104 | ## 0.1.7 105 | 106 | - Option to edit script before running 107 | - Fix error with String.prototype.replaceAll not existing 108 | 109 | ## 0.1.6 110 | 111 | - Silent mode (skip explanation) 112 | 113 | ## 0.1.5 114 | 115 | - Improve error messaging UX, again x3 116 | 117 | ## 0.1.4 118 | 119 | - Improve error messaging, again again 120 | 121 | ## 0.1.3 122 | 123 | - Improve error messaging, again 124 | 125 | ## 0.1.2 126 | 127 | - Improve error messaging 128 | 129 | ## 0.1.1 130 | 131 | - Minor UX improvement ("AI Shell" vs "ai-shell") 132 | 133 | ## 0.1.0 134 | 135 | - Minor error message improvements 136 | 137 | ## 0.0.18 138 | 139 | - Better error handling 140 | 141 | ## 0.0.17 142 | 143 | - Minor improvements to the UX of the revision flow 144 | 145 | ## 0.0.16 146 | 147 | - Take the current operating system into account 148 | 149 | ## 0.0.15 150 | 151 | - Stream responses 152 | -------------------------------------------------------------------------------- /README-zh-Hans.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 | AI Shell logo 5 | 6 |

7 | 8 |

9 | 一个将自然语言转换为 Shell 命令的 CLI。 10 |

11 |

12 | Current version 13 |

14 | 15 |

16 | Gif Demo 17 |

18 | 19 |

20 | 受 GitHub Copilot X CLI 启发, 但是面向所有人开源. 21 |

22 | 23 |
24 | 25 | # AI Shell 26 | 27 | ## 安装 28 | 29 | > 最低支持的 Node.js 版本是 v14 30 | 31 | 1. 安装 _ai shell_: 32 | 33 | ```sh 34 | npm install -g @builder.io/ai-shell 35 | ``` 36 | 37 | 2. 从 [OpenAI](https://platform.openai.com/account/api-keys) 获取您的 API 密钥 38 | 39 | > 注意:如果您还没有创建帐户并设置计费方式,您需要先进行这些步骤。 40 | 41 | 3. 设置密钥以便 ai-shell 可以使用它: 42 | 43 | ```sh 44 | ai config set OPENAI_KEY= 45 | ``` 46 | 47 | 这将在您的主目录中创建一个名为 `.ai-shell` 的文件。 48 | 49 | ## 使用 50 | 51 | ```bash 52 | ai 53 | ``` 54 | 55 | 例如: 56 | 57 | ```bash 58 | ai 查找所有.log文件 59 | ``` 60 | 61 | 然后您将会得到如下输出,您可以选择运行建议的命令、通过提示修改命令或者取消: 62 | 63 | ```bash 64 | ◇ 您的脚本: 65 | │ 66 | │ find . -name "*.log" 67 | │ 68 | ◇ 解释: 69 | │ 70 | │ 这个脚本的步骤如下: 71 | │ 1. 在当前目录下搜索所有后缀为".log"的文件。 72 | │ 2. 输出所有找到的文件的路径。 73 | │ 74 | ◆ 运行这个脚本? 75 | │ ● ✅ 是 (开始运行吧!) 76 | │ ○ 📝 修改 77 | │ ○ ❌ 取消 78 | └ 79 | ``` 80 | 81 | ### 特殊字符 82 | 83 | 请注意,某些 shell 会处理某些特殊字符,如 `?` 或 `*` 或看起来像是文件路径的字符。如果您遇到奇怪的行为,可以将提示符用引号括起来以避免问题,例如下面的示例: 84 | 85 | ```bash 86 | ai '我的 IP 地址是什么?' 87 | ``` 88 | 89 | ### 聊天模式 90 | 91 | ![Chat demo](https://user-images.githubusercontent.com/844291/232889699-e13fb3fe-1659-4583-80ee-6c58d1bcbd06.gif) 92 | 93 | ```bash 94 | ai chat 95 | ``` 96 | 97 | 通过此模式,您可以直接通过 CLI 与 AI 进行对话,并以自然、对话式的方式获得有用的响应: 98 | 99 | ```sh 100 | ┌ 开始新的对话 101 | │ 102 | ◇ 您: 103 | │ 在 Express 中如何进行重定向服务? 104 | │ 105 | ◇ AI Shell: 106 | 107 | 在 Express 中,您可以使用 `redirect()` 方法来进行重定向服务。`redirect()` 方法接受一个参数,即您要重定向到的 URL。 108 | 109 | 以下是一个示例: 110 | 111 | \`\`\`js 112 | app.get('/oldurl', (req, res) => { 113 | res.redirect('/newurl'); 114 | }); 115 | \`\`\` 116 | ``` 117 | 118 | ### 静默模式(跳过解释) 119 | 120 | 您可以使用 `-s` 或 `--silent` 标志来禁用和跳过解释部分。 121 | 122 | ```bash 123 | ai -s 列出所有日志文件 124 | ``` 125 | 126 | 或者使用以下命令将选项保存为首选项: 127 | 128 | ```bash 129 | ai config set SILENT_MODE=true 130 | ``` 131 | 132 | ### 自定义 API 端点 133 | 134 | 您可以自定义 OpenAI API 端点以设置 OPENAI_API_ENDPOINT(默认值为 `https://api.openai.com/v1`)。 135 | 136 | ```sh 137 | ai config set OPENAI_API_ENDPOINT= 138 | ``` 139 | 140 | ### 设置语言 141 | 142 | ![Language UI](https://user-images.githubusercontent.com/1784873/235330029-0a3b394c-d797-41d6-8717-9a6b487f1ae8.gif) 143 | 144 | AI Shell 的默认语言是英文,但您也可以参考下列语言对应的键,来设置您需要的语言界面: 145 | 146 | | 语言 | 键值 | 147 | | ---------- | ------- | 148 | | English | en | 149 | | 简体中文 | zh-Hans | 150 | | 繁体中文 | zh-Hant | 151 | | Español | es | 152 | | 日本語 | jp | 153 | | 한국어 | ko | 154 | | Français | fr | 155 | | Deutsch | de | 156 | | Русский | ru | 157 | | Українська | uk | 158 | | Tiếng Việt | vi | 159 | | العربية | ar | 160 | | Português | pt | 161 | 162 | 例如,您要将语言设置为简体中文,可以在设置 LANGUAGE 的值为 zh-Hans: 163 | 164 | ```sh 165 | ai config set LANGUAGE=zh-Hans 166 | ``` 167 | 168 | 这样您的语言就设置为了简体中文 169 | 170 | ### 配置界面 171 | 172 | 如果要使用更可视化的界面查看和设置配置选项,可以键入: 173 | 174 | ```bash 175 | ai config 176 | ``` 177 | 178 | 要获取如下所示的交互式 UI: 179 | 180 | ```bash 181 | ◆ Set config: 182 | │ ○ OpenAI Key 183 | │ ○ OpenAI API Endpoint 184 | │ ○ Silent Mode 185 | │ ● Model (gpt-4o-mini) 186 | │ ○ Language 187 | │ ○ Cancel 188 | └ 189 | ``` 190 | 191 | ### 升级 192 | 193 | 使用以下命令检查已安装的版本: 194 | 195 | ```bash 196 | ai --version 197 | ``` 198 | 199 | 如果它不是[最新版本](https://github.com/BuilderIO/ai-shell/tags),请运行: 200 | 201 | ```bash 202 | npm update -g @builder.io/ai-shell 203 | ``` 204 | 205 | 或者只需使用 AI shell: 206 | 207 | ```bash 208 | ai update 209 | ``` 210 | 211 | ## 常见问题 212 | 213 | ### 429 错误 214 | 215 | 一些用户报告了来自 OpenAI 的 429 错误。这是由于错误的计费设置或过度使用配额所致。请按照[此指南](https://help.openai.com/en/articles/6891831-error-code-429-you-exceeded-your-current-quota-please-check-your-plan-and-billing-details)进行修复。 216 | 217 | 您可以在[此链接](https://platform.openai.com/account/billing/overview)上激活计费。如果没有获得 OpenAI 的活动赠款,请确保添加支付方式。 218 | 219 | ## 动机 220 | 221 | 我不是一个 bash 巫师,迫切需要访问 copilot CLI,但我已经等不急了。 222 | 223 | ## 贡献 224 | 225 | 如果您想在 [Issues](https://github.com/BuilderIO/ai-shell/issues) 中修复错误或实现功能(提示:注意 `help wanted` 标签),请查看 [Contribution Guide](CONTRIBUTING.md) 以了解如何设置项目。 226 | 227 | ## 致谢 228 | 229 | - 感谢 GitHub Copilot 提供的惊人工具和这个想法 230 | - 感谢 Hassan 和他在 [aicommits](https://github.com/Nutlope/aicommits) 上的工作,这启发了工作流程和部分代码和流程。 231 | 232 | ## 社区 233 | 234 | 欢迎加入 [Builder.io Discord](https://discord.gg/EMx6e58xnw) 并在 #ai-shell 房间与我们聊天。 235 | 236 |

237 | 238 |

239 | 240 | 241 | 242 | Made with love by Builder.io 243 | 244 | 245 |

246 | -------------------------------------------------------------------------------- /src/helpers/config.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs/promises'; 2 | import path from 'path'; 3 | import os from 'os'; 4 | import ini from 'ini'; 5 | import type { TiktokenModel } from '@dqbd/tiktoken'; 6 | import { commandName } from './constants'; 7 | import { KnownError, handleCliError } from './error'; 8 | import * as p from '@clack/prompts'; 9 | import { red } from 'kolorist'; 10 | import i18n from './i18n'; 11 | import { getModels } from './completion'; 12 | import { Model } from 'openai'; 13 | 14 | const { hasOwnProperty } = Object.prototype; 15 | export const hasOwn = (object: unknown, key: PropertyKey) => 16 | hasOwnProperty.call(object, key); 17 | 18 | const languagesOptions = Object.entries(i18n.languages).map(([key, value]) => ({ 19 | value: key, 20 | label: value, 21 | })); 22 | 23 | const parseAssert = (name: string, condition: any, message: string) => { 24 | if (!condition) { 25 | throw new KnownError( 26 | `${i18n.t('Invalid config property')} ${name}: ${message}` 27 | ); 28 | } 29 | }; 30 | 31 | const configParsers = { 32 | OPENAI_KEY(key?: string) { 33 | if (!key) { 34 | throw new KnownError( 35 | `Please set your OpenAI API key via \`${commandName} config set OPENAI_KEY=\`` // TODO: i18n 36 | ); 37 | } 38 | 39 | return key; 40 | }, 41 | MODEL(model?: string) { 42 | if (!model || model.length === 0) { 43 | return 'gpt-4o-mini'; 44 | } 45 | 46 | return model as TiktokenModel; 47 | }, 48 | SILENT_MODE(mode?: string) { 49 | return String(mode).toLowerCase() === 'true'; 50 | }, 51 | OPENAI_API_ENDPOINT(apiEndpoint?: string) { 52 | return apiEndpoint || 'https://api.openai.com/v1'; 53 | }, 54 | LANGUAGE(language?: string) { 55 | return language || 'en'; 56 | }, 57 | } as const; 58 | 59 | type ConfigKeys = keyof typeof configParsers; 60 | 61 | type RawConfig = { 62 | [key in ConfigKeys]?: string; 63 | }; 64 | 65 | type ValidConfig = { 66 | [Key in ConfigKeys]: ReturnType<(typeof configParsers)[Key]>; 67 | }; 68 | 69 | const configPath = path.join(os.homedir(), '.ai-shell'); 70 | 71 | const fileExists = (filePath: string) => 72 | fs.lstat(filePath).then( 73 | () => true, 74 | () => false 75 | ); 76 | 77 | const readConfigFile = async (): Promise => { 78 | const configExists = await fileExists(configPath); 79 | if (!configExists) { 80 | return Object.create(null); 81 | } 82 | 83 | const configString = await fs.readFile(configPath, 'utf8'); 84 | return ini.parse(configString); 85 | }; 86 | 87 | export const getConfig = async ( 88 | cliConfig?: RawConfig 89 | ): Promise => { 90 | const config = await readConfigFile(); 91 | const parsedConfig: Record = {}; 92 | 93 | for (const key of Object.keys(configParsers) as ConfigKeys[]) { 94 | const parser = configParsers[key]; 95 | const value = cliConfig?.[key] ?? config[key]; 96 | parsedConfig[key] = parser(value); 97 | } 98 | 99 | return parsedConfig as ValidConfig; 100 | }; 101 | 102 | export const setConfigs = async (keyValues: [key: string, value: string][]) => { 103 | const config = await readConfigFile(); 104 | 105 | for (const [key, value] of keyValues) { 106 | if (!hasOwn(configParsers, key)) { 107 | throw new KnownError(`${i18n.t('Invalid config property')}: ${key}`); 108 | } 109 | 110 | const parsed = configParsers[key as ConfigKeys](value); 111 | config[key as ConfigKeys] = parsed as any; 112 | } 113 | 114 | await fs.writeFile(configPath, ini.stringify(config), 'utf8'); 115 | }; 116 | 117 | export const showConfigUI = async () => { 118 | try { 119 | const config = await getConfig(); 120 | const choice = (await p.select({ 121 | message: i18n.t('Set config') + ':', 122 | options: [ 123 | { 124 | label: i18n.t('OpenAI Key'), 125 | value: 'OPENAI_KEY', 126 | hint: hasOwn(config, 'OPENAI_KEY') 127 | ? // Obfuscate the key 128 | 'sk-...' + config.OPENAI_KEY.slice(-3) 129 | : i18n.t('(not set)'), 130 | }, 131 | { 132 | label: i18n.t('OpenAI API Endpoint'), 133 | value: 'OPENAI_API_ENDPOINT', 134 | hint: hasOwn(config, 'OPENAI_API_ENDPOINT') 135 | ? config.OPENAI_API_ENDPOINT 136 | : i18n.t('(not set)'), 137 | }, 138 | { 139 | label: i18n.t('Silent Mode'), 140 | value: 'SILENT_MODE', 141 | hint: hasOwn(config, 'SILENT_MODE') 142 | ? config.SILENT_MODE.toString() 143 | : i18n.t('(not set)'), 144 | }, 145 | { 146 | label: i18n.t('Model'), 147 | value: 'MODEL', 148 | hint: hasOwn(config, 'MODEL') ? config.MODEL : i18n.t('(not set)'), 149 | }, 150 | { 151 | label: i18n.t('Language'), 152 | value: 'LANGUAGE', 153 | hint: hasOwn(config, 'LANGUAGE') 154 | ? config.LANGUAGE 155 | : i18n.t('(not set)'), 156 | }, 157 | { 158 | label: i18n.t('Cancel'), 159 | value: 'cancel', 160 | hint: i18n.t('Exit the program'), 161 | }, 162 | ], 163 | })) as ConfigKeys | 'cancel' | symbol; 164 | 165 | if (p.isCancel(choice)) return; 166 | 167 | if (choice === 'OPENAI_KEY') { 168 | const key = await p.text({ 169 | message: i18n.t('Enter your OpenAI API key'), 170 | validate: (value) => { 171 | if (!value.length) { 172 | return i18n.t('Please enter a key'); 173 | } 174 | }, 175 | }); 176 | if (p.isCancel(key)) return; 177 | await setConfigs([['OPENAI_KEY', key]]); 178 | } else if (choice === 'OPENAI_API_ENDPOINT') { 179 | const apiEndpoint = await p.text({ 180 | message: i18n.t('Enter your OpenAI API Endpoint'), 181 | }); 182 | if (p.isCancel(apiEndpoint)) return; 183 | await setConfigs([['OPENAI_API_ENDPOINT', apiEndpoint]]); 184 | } else if (choice === 'SILENT_MODE') { 185 | const silentMode = await p.confirm({ 186 | message: i18n.t('Enable silent mode?'), 187 | }); 188 | if (p.isCancel(silentMode)) return; 189 | await setConfigs([['SILENT_MODE', silentMode ? 'true' : 'false']]); 190 | } else if (choice === 'MODEL') { 191 | const { OPENAI_KEY: key, OPENAI_API_ENDPOINT: apiEndpoint } = 192 | await getConfig(); 193 | const models = await getModels(key, apiEndpoint); 194 | const model = (await p.select({ 195 | message: 'Pick a model.', 196 | options: models.map((m: Model) => { 197 | return { value: m.id, label: m.id }; 198 | }), 199 | })) as string; 200 | 201 | if (p.isCancel(model)) return; 202 | await setConfigs([['MODEL', model]]); 203 | } else if (choice === 'LANGUAGE') { 204 | const language = (await p.select({ 205 | message: i18n.t('Enter the language you want to use'), 206 | options: languagesOptions, 207 | })) as string; 208 | if (p.isCancel(language)) return; 209 | await setConfigs([['LANGUAGE', language]]); 210 | i18n.setLanguage(language); 211 | } 212 | if (choice === 'cancel') return; 213 | showConfigUI(); 214 | } catch (error: any) { 215 | console.error(`\n${red('✖')} ${error.message}`); 216 | handleCliError(error); 217 | process.exit(1); 218 | } 219 | }; 220 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 | AI Shell logo 5 | 6 |

7 | 8 |

9 | A CLI that converts natural language to shell commands. 10 |

11 |

12 | Current version 13 |

14 | 15 |

16 | Gif Demo 17 |

18 | 19 |

20 | Inspired by the GitHub Copilot X CLI, but open source for everyone. 21 |

22 | 23 |
24 | 25 | # AI Shell 26 | 27 | ## Setup 28 | 29 | > The minimum supported version of Node.js is v14 30 | 31 | 1. Install _ai shell_: 32 | 33 | ```sh 34 | npm install -g @builder.io/ai-shell 35 | ``` 36 | 37 | 2. Retrieve your API key from [OpenAI](https://platform.openai.com/account/api-keys) 38 | 39 | > Note: If you haven't already, you'll have to create an account and set up billing. 40 | 41 | 3. Set the key so ai-shell can use it: 42 | 43 | ```sh 44 | ai config set OPENAI_KEY= 45 | ``` 46 | 47 | This will create a `.ai-shell` file in your home directory. 48 | 49 | ## Usage 50 | 51 | ```bash 52 | ai 53 | ``` 54 | 55 | For example: 56 | 57 | ```bash 58 | ai list all log files 59 | ``` 60 | 61 | Then you will get an output like this, where you can choose to run the suggested command, revise the command via a prompt, or cancel: 62 | 63 | ```bash 64 | ◇ Your script: 65 | │ 66 | │ find . -name "*.log" 67 | │ 68 | ◇ Explanation: 69 | │ 70 | │ 1. Searches for all files with the extension ".log" in the current directory and any subdirectories. 71 | │ 72 | ◆ Run this script? 73 | │ ● ✅ Yes (Lets go!) 74 | │ ○ 📝 Revise 75 | │ ○ ❌ Cancel 76 | └ 77 | ``` 78 | 79 | ### Special characters 80 | 81 | Note that some shells handle certain characters like the `?` or `*` or things that look like file paths specially. If you are getting strange behaviors, you can wrap the prompt in quotes to avoid issues, like below: 82 | 83 | ```bash 84 | ai 'what is my ip address' 85 | ``` 86 | 87 | ### Chat mode 88 | 89 | ![Chat demo](https://user-images.githubusercontent.com/844291/232889699-e13fb3fe-1659-4583-80ee-6c58d1bcbd06.gif) 90 | 91 | ```bash 92 | ai chat 93 | ``` 94 | 95 | With this mode, you can engage in a conversation with the AI and receive helpful responses in a natural, conversational manner directly through the CLI: 96 | 97 | ```sh 98 | ┌ Starting new conversation 99 | │ 100 | ◇ You: 101 | │ how do I serve a redirect in express 102 | │ 103 | ◇ AI Shell: 104 | 105 | In Express, you can use the `redirect()` method to serve a redirect. The `redirect()` method takes one argument, which is the URL that you want to redirect to. 106 | 107 | Here's an example: 108 | 109 | \`\`\`js 110 | app.get('/oldurl', (req, res) => { 111 | res.redirect('/newurl'); 112 | }); 113 | \`\`\` 114 | ``` 115 | 116 | ### Silent mode (skip explanations) 117 | 118 | You can disable and skip the explanation section by using the flag `-s` or `--silent` 119 | 120 | ```bash 121 | ai -s list all log files 122 | ``` 123 | 124 | or save the option as a preference using this command: 125 | 126 | ```bash 127 | ai config set SILENT_MODE=true 128 | ``` 129 | 130 | ### Custom API endpoint 131 | 132 | You can custom OpenAI API endpoint to set OPENAI_API_ENDPOINT(default: `https://api.openai.com/v1`) 133 | 134 | ```sh 135 | ai config set OPENAI_API_ENDPOINT= 136 | ``` 137 | 138 | ### Set Language 139 | 140 | ![Language UI](https://user-images.githubusercontent.com/1784873/235330029-0a3b394c-d797-41d6-8717-9a6b487f1ae8.gif) 141 | 142 | The AI Shell's default language is English, but you can easily switch to your preferred language by using the corresponding language keys, as shown below: 143 | 144 | | Language | Key | 145 | | ------------------- | ------- | 146 | | English | en | 147 | | Simplified Chinese | zh-Hans | 148 | | Traditional Chinese | zh-Hant | 149 | | Spanish | es | 150 | | Japanese | jp | 151 | | Korean | ko | 152 | | French | fr | 153 | | German | de | 154 | | Russian | ru | 155 | | Ukrainian | uk | 156 | | Vietnamese | vi | 157 | | Arabic | ar | 158 | | Portuguese | pt | 159 | | Turkish | tr | 160 | 161 | For instance, if you want to switch to Simplified Chinese, you can do so by setting the LANGUAGE value to zh-Hans: 162 | 163 | ```sh 164 | ai config set LANGUAGE=zh-Hans 165 | ``` 166 | 167 | This will set your language to Simplified Chinese. 168 | 169 | ### Config UI 170 | 171 | To use a more visual interface to view and set config options you can type: 172 | 173 | ```bash 174 | ai config 175 | ``` 176 | 177 | To get an interactive UI like below: 178 | 179 | ```bash 180 | ◆ Set config: 181 | │ ○ OpenAI Key 182 | │ ○ OpenAI API Endpoint 183 | │ ○ Silent Mode 184 | │ ● Model (gpt-4o-mini) 185 | │ ○ Language 186 | │ ○ Cancel 187 | └ 188 | ``` 189 | 190 | ### Upgrading 191 | 192 | Check the installed version with: 193 | 194 | ```bash 195 | ai --version 196 | ``` 197 | 198 | If it's not the [latest version](https://github.com/BuilderIO/ai-shell/tags), run: 199 | 200 | ```bash 201 | npm update -g @builder.io/ai-shell 202 | ``` 203 | 204 | Or just use AI shell: 205 | 206 | ```bash 207 | ai update 208 | ``` 209 | 210 | ## Common Issues 211 | 212 | ### 429 error 213 | 214 | Some users are reporting a 429 from OpenAI. This is due to incorrect billing setup or excessive quota usage. Please follow [this guide](https://help.openai.com/en/articles/6891831-error-code-429-you-exceeded-your-current-quota-please-check-your-plan-and-billing-details) to fix it. 215 | 216 | You can activate billing at [this link](https://platform.openai.com/account/billing/overview). Make sure to add a payment method if not under an active grant from OpenAI. 217 | 218 | ## Motivation 219 | 220 | I am not a bash wizard, and am dying for access to the copilot CLI, and got impatient. 221 | 222 | ## Contributing 223 | 224 | If you want to help fix a bug or implement a feature in [Issues](https://github.com/BuilderIO/ai-shell/issues) (tip: look out for the `help wanted` label), checkout the [Contribution Guide](CONTRIBUTING.md) to learn how to setup the project. 225 | 226 | ## Credit 227 | 228 | - Thanks to GitHub Copilot for their amazing tools and the idea for this. 229 | - Thanks to Hassan and his work on [aicommits](https://github.com/Nutlope/aicommits), which inspired the workflow and some parts of the code and flows 230 | 231 |

232 | 233 |

234 | 235 | 236 | 237 | Made with love by Builder.io 238 | 239 | 240 |

241 | -------------------------------------------------------------------------------- /src/prompt.ts: -------------------------------------------------------------------------------- 1 | import * as p from '@clack/prompts'; 2 | import { execaCommand } from 'execa'; 3 | import { cyan, dim } from 'kolorist'; 4 | import { 5 | getExplanation, 6 | getRevision, 7 | getScriptAndInfo, 8 | } from './helpers/completion'; 9 | import { getConfig } from './helpers/config'; 10 | import { projectName } from './helpers/constants'; 11 | import { KnownError } from './helpers/error'; 12 | import clipboardy from 'clipboardy'; 13 | import i18n from './helpers/i18n'; 14 | import { appendToShellHistory } from './helpers/shell-history'; 15 | 16 | const init = async () => { 17 | try { 18 | const { LANGUAGE: language } = await getConfig(); 19 | i18n.setLanguage(language); 20 | } catch { 21 | i18n.setLanguage('en'); 22 | } 23 | }; 24 | 25 | const examples: string[] = []; 26 | const initPromise = init(); 27 | initPromise.then(() => { 28 | examples.push(i18n.t('delete all log files')); 29 | examples.push(i18n.t('list js files')); 30 | examples.push(i18n.t('fetch me a random joke')); 31 | examples.push(i18n.t('list all commits')); 32 | }); 33 | 34 | const sample = (arr: T[]): T | undefined => { 35 | const len = arr == null ? 0 : arr.length; 36 | return len ? arr[Math.floor(Math.random() * len)] : undefined; 37 | }; 38 | 39 | async function runScript(script: string) { 40 | p.outro(`${i18n.t('Running')}: ${script}`); 41 | console.log(''); 42 | try { 43 | await execaCommand(script, { 44 | stdio: 'inherit', 45 | shell: process.env.SHELL || true, 46 | }); 47 | appendToShellHistory(script); 48 | } catch (error) { 49 | // Nothing needed, it'll output to stderr 50 | } 51 | } 52 | 53 | async function getPrompt(prompt?: string) { 54 | await initPromise; 55 | const group = p.group( 56 | { 57 | prompt: () => 58 | p.text({ 59 | message: i18n.t('What would you like me to do?'), 60 | placeholder: `${i18n.t('e.g.')} ${sample(examples)}`, 61 | initialValue: prompt, 62 | defaultValue: i18n.t('Say hello'), 63 | validate: (value) => { 64 | if (!value) return i18n.t('Please enter a prompt.'); 65 | }, 66 | }), 67 | }, 68 | { 69 | onCancel: () => { 70 | p.cancel(i18n.t('Goodbye!')); 71 | process.exit(0); 72 | }, 73 | } 74 | ); 75 | return (await group).prompt; 76 | } 77 | 78 | async function promptForRevision() { 79 | const group = p.group( 80 | { 81 | prompt: () => 82 | p.text({ 83 | message: i18n.t('What would you like me to change in this script?'), 84 | placeholder: i18n.t('e.g. change the folder name'), 85 | validate: (value) => { 86 | if (!value) return i18n.t('Please enter a prompt.'); 87 | }, 88 | }), 89 | }, 90 | { 91 | onCancel: () => { 92 | p.cancel(i18n.t('Goodbye!')); 93 | process.exit(0); 94 | }, 95 | } 96 | ); 97 | return (await group).prompt; 98 | } 99 | 100 | export async function prompt({ 101 | usePrompt, 102 | silentMode, 103 | }: { usePrompt?: string; silentMode?: boolean } = {}) { 104 | const { 105 | OPENAI_KEY: key, 106 | SILENT_MODE, 107 | OPENAI_API_ENDPOINT: apiEndpoint, 108 | MODEL: model, 109 | } = await getConfig(); 110 | const skipCommandExplanation = silentMode || SILENT_MODE; 111 | 112 | console.log(''); 113 | p.intro(`${cyan(`${projectName}`)}`); 114 | 115 | const thePrompt = usePrompt || (await getPrompt()); 116 | const spin = p.spinner(); 117 | spin.start(i18n.t(`Loading...`)); 118 | const { readInfo, readScript } = await getScriptAndInfo({ 119 | prompt: thePrompt, 120 | key, 121 | model, 122 | apiEndpoint, 123 | }); 124 | spin.stop(`${i18n.t('Your script')}:`); 125 | console.log(''); 126 | const script = await readScript(process.stdout.write.bind(process.stdout)); 127 | console.log(''); 128 | console.log(''); 129 | console.log(dim('•')); 130 | if (!skipCommandExplanation) { 131 | spin.start(i18n.t(`Getting explanation...`)); 132 | const info = await readInfo(process.stdout.write.bind(process.stdout)); 133 | if (!info) { 134 | const { readExplanation } = await getExplanation({ 135 | script, 136 | key, 137 | model, 138 | apiEndpoint, 139 | }); 140 | spin.stop(`${i18n.t('Explanation')}:`); 141 | console.log(''); 142 | await readExplanation(process.stdout.write.bind(process.stdout)); 143 | console.log(''); 144 | console.log(''); 145 | console.log(dim('•')); 146 | } 147 | } 148 | 149 | await runOrReviseFlow(script, key, model, apiEndpoint, silentMode); 150 | } 151 | 152 | async function runOrReviseFlow( 153 | script: string, 154 | key: string, 155 | model: string, 156 | apiEndpoint: string, 157 | silentMode?: boolean 158 | ) { 159 | const emptyScript = script.trim() === ''; 160 | 161 | const answer: symbol | (() => any) = await p.select({ 162 | message: emptyScript 163 | ? i18n.t('Revise this script?') 164 | : i18n.t('Run this script?'), 165 | options: [ 166 | ...(emptyScript 167 | ? [] 168 | : [ 169 | { 170 | label: '✅ ' + i18n.t('Yes'), 171 | hint: i18n.t('Lets go!'), 172 | value: async () => { 173 | await runScript(script); 174 | }, 175 | }, 176 | { 177 | label: '📝 ' + i18n.t('Edit'), 178 | hint: i18n.t('Make some adjustments before running'), 179 | value: async () => { 180 | const newScript = await p.text({ 181 | message: i18n.t('you can edit script here:'), 182 | initialValue: script, 183 | }); 184 | if (!p.isCancel(newScript)) { 185 | await runScript(newScript); 186 | } 187 | }, 188 | }, 189 | ]), 190 | { 191 | label: '🔁 ' + i18n.t('Revise'), 192 | hint: i18n.t('Give feedback via prompt and get a new result'), 193 | value: async () => { 194 | await revisionFlow(script, key, model, apiEndpoint, silentMode); 195 | }, 196 | }, 197 | { 198 | label: '📋 ' + i18n.t('Copy'), 199 | hint: i18n.t('Copy the generated script to your clipboard'), 200 | value: async () => { 201 | await clipboardy.write(script); 202 | p.outro(i18n.t('Copied to clipboard!')); 203 | }, 204 | }, 205 | { 206 | label: '❌ ' + i18n.t('Cancel'), 207 | hint: i18n.t('Exit the program'), 208 | value: () => { 209 | p.cancel(i18n.t('Goodbye!')); 210 | process.exit(0); 211 | }, 212 | }, 213 | ], 214 | }); 215 | 216 | if (typeof answer === 'function') { 217 | await answer(); 218 | } 219 | } 220 | 221 | async function revisionFlow( 222 | currentScript: string, 223 | key: string, 224 | model: string, 225 | apiEndpoint: string, 226 | silentMode?: boolean 227 | ) { 228 | const revision = await promptForRevision(); 229 | const spin = p.spinner(); 230 | spin.start(i18n.t(`Loading...`)); 231 | const { readScript } = await getRevision({ 232 | prompt: revision, 233 | code: currentScript, 234 | key, 235 | model, 236 | apiEndpoint, 237 | }); 238 | spin.stop(`${i18n.t(`Your new script`)}:`); 239 | 240 | console.log(''); 241 | const script = await readScript(process.stdout.write.bind(process.stdout)); 242 | console.log(''); 243 | console.log(''); 244 | console.log(dim('•')); 245 | 246 | if (!silentMode) { 247 | const infoSpin = p.spinner(); 248 | infoSpin.start(i18n.t(`Getting explanation...`)); 249 | const { readExplanation } = await getExplanation({ 250 | script, 251 | key, 252 | model, 253 | apiEndpoint, 254 | }); 255 | 256 | infoSpin.stop(`${i18n.t('Explanation')}:`); 257 | console.log(''); 258 | await readExplanation(process.stdout.write.bind(process.stdout)); 259 | console.log(''); 260 | console.log(''); 261 | console.log(dim('•')); 262 | } 263 | 264 | await runOrReviseFlow(script, key, model, apiEndpoint, silentMode); 265 | } 266 | 267 | export const parseAssert = (name: string, condition: any, message: string) => { 268 | if (!condition) { 269 | throw new KnownError( 270 | `${i18n.t('Invalid config property')} ${name}: ${message}` 271 | ); 272 | } 273 | }; 274 | -------------------------------------------------------------------------------- /src/helpers/completion.ts: -------------------------------------------------------------------------------- 1 | import { 2 | OpenAIApi, 3 | Configuration, 4 | ChatCompletionRequestMessage, 5 | Model, 6 | } from 'openai'; 7 | import dedent from 'dedent'; 8 | import { IncomingMessage } from 'http'; 9 | import { KnownError } from './error'; 10 | import { streamToIterable } from './stream-to-iterable'; 11 | import { detectShell } from './os-detect'; 12 | import type { AxiosError } from 'axios'; 13 | import { streamToString } from './stream-to-string'; 14 | import './replace-all-polyfill'; 15 | import i18n from './i18n'; 16 | import { stripRegexPatterns } from './strip-regex-patterns'; 17 | import readline from 'readline'; 18 | 19 | const explainInSecondRequest = true; 20 | 21 | function getOpenAi(key: string, apiEndpoint: string) { 22 | const openAi = new OpenAIApi( 23 | new Configuration({ apiKey: key, basePath: apiEndpoint }) 24 | ); 25 | return openAi; 26 | } 27 | 28 | // Openai outputs markdown format for code blocks. It oftne uses 29 | // a github style like: "```bash" 30 | const shellCodeExclusions = [/```[a-zA-Z]*\n/gi, /```[a-zA-Z]*/gi, '\n']; 31 | 32 | export async function getScriptAndInfo({ 33 | prompt, 34 | key, 35 | model, 36 | apiEndpoint, 37 | }: { 38 | prompt: string; 39 | key: string; 40 | model?: string; 41 | apiEndpoint: string; 42 | }) { 43 | const fullPrompt = getFullPrompt(prompt); 44 | const stream = await generateCompletion({ 45 | prompt: fullPrompt, 46 | number: 1, 47 | key, 48 | model, 49 | apiEndpoint, 50 | }); 51 | const iterableStream = streamToIterable(stream); 52 | return { 53 | readScript: readData(iterableStream, ...shellCodeExclusions), 54 | readInfo: readData(iterableStream, ...shellCodeExclusions), 55 | }; 56 | } 57 | 58 | export async function generateCompletion({ 59 | prompt, 60 | number = 1, 61 | key, 62 | model, 63 | apiEndpoint, 64 | }: { 65 | prompt: string | ChatCompletionRequestMessage[]; 66 | number?: number; 67 | model?: string; 68 | key: string; 69 | apiEndpoint: string; 70 | }) { 71 | const openAi = getOpenAi(key, apiEndpoint); 72 | try { 73 | const completion = await openAi.createChatCompletion( 74 | { 75 | model: model || 'gpt-4o-mini', 76 | messages: Array.isArray(prompt) 77 | ? prompt 78 | : [{ role: 'user', content: prompt }], 79 | n: Math.min(number, 10), 80 | stream: true, 81 | }, 82 | { responseType: 'stream' } 83 | ); 84 | 85 | return completion.data as unknown as IncomingMessage; 86 | } catch (err) { 87 | const error = err as AxiosError; 88 | 89 | if (error.code === 'ENOTFOUND') { 90 | throw new KnownError( 91 | `Error connecting to ${error.request.hostname} (${error.request.syscall}). Are you connected to the internet?` 92 | ); 93 | } 94 | 95 | const response = error.response; 96 | let message = response?.data as string | object | IncomingMessage; 97 | if (response && message instanceof IncomingMessage) { 98 | message = await streamToString( 99 | response.data as unknown as IncomingMessage 100 | ); 101 | try { 102 | // Handle if the message is JSON. It should be but occasionally will 103 | // be HTML, so lets handle both 104 | message = JSON.parse(message); 105 | } catch (e) { 106 | // Ignore 107 | } 108 | } 109 | 110 | const messageString = message && JSON.stringify(message, null, 2); 111 | if (response?.status === 429) { 112 | throw new KnownError( 113 | dedent` 114 | Request to OpenAI failed with status 429. This is due to incorrect billing setup or excessive quota usage. Please follow this guide to fix it: https://help.openai.com/en/articles/6891831-error-code-429-you-exceeded-your-current-quota-please-check-your-plan-and-billing-details 115 | 116 | You can activate billing here: https://platform.openai.com/account/billing/overview . Make sure to add a payment method if not under an active grant from OpenAI. 117 | 118 | Full message from OpenAI: 119 | ` + 120 | '\n\n' + 121 | messageString + 122 | '\n' 123 | ); 124 | } else if (response && message) { 125 | throw new KnownError( 126 | dedent` 127 | Request to OpenAI failed with status ${response?.status}: 128 | ` + 129 | '\n\n' + 130 | messageString + 131 | '\n' 132 | ); 133 | } 134 | 135 | throw error; 136 | } 137 | } 138 | 139 | export async function getExplanation({ 140 | script, 141 | key, 142 | model, 143 | apiEndpoint, 144 | }: { 145 | script: string; 146 | key: string; 147 | model?: string; 148 | apiEndpoint: string; 149 | }) { 150 | const prompt = getExplanationPrompt(script); 151 | const stream = await generateCompletion({ 152 | prompt, 153 | key, 154 | number: 1, 155 | model, 156 | apiEndpoint, 157 | }); 158 | const iterableStream = streamToIterable(stream); 159 | return { readExplanation: readData(iterableStream) }; 160 | } 161 | 162 | export async function getRevision({ 163 | prompt, 164 | code, 165 | key, 166 | model, 167 | apiEndpoint, 168 | }: { 169 | prompt: string; 170 | code: string; 171 | key: string; 172 | model?: string; 173 | apiEndpoint: string; 174 | }) { 175 | const fullPrompt = getRevisionPrompt(prompt, code); 176 | const stream = await generateCompletion({ 177 | prompt: fullPrompt, 178 | key, 179 | number: 1, 180 | model, 181 | apiEndpoint, 182 | }); 183 | const iterableStream = streamToIterable(stream); 184 | return { 185 | readScript: readData(iterableStream, ...shellCodeExclusions), 186 | }; 187 | } 188 | 189 | export const readData = 190 | ( 191 | iterableStream: AsyncGenerator, 192 | ...excluded: (RegExp | string | undefined)[] 193 | ) => 194 | (writer: (data: string) => void): Promise => 195 | new Promise(async (resolve) => { 196 | let stopTextStream = false; 197 | let data = ''; 198 | let content = ''; 199 | let dataStart = false; 200 | let buffer = ''; // This buffer will temporarily hold incoming data only for detecting the start 201 | 202 | const [excludedPrefix] = excluded; 203 | const stopTextStreamKeys = ['q', 'escape']; //Group of keys that stop the text stream 204 | 205 | const rl = readline.createInterface({ 206 | input: process.stdin, 207 | }); 208 | 209 | process.stdin.setRawMode(true); 210 | 211 | process.stdin.on('keypress', (key, data) => { 212 | if (stopTextStreamKeys.includes(data.name)) { 213 | stopTextStream = true; 214 | } 215 | }); 216 | for await (const chunk of iterableStream) { 217 | const payloads = chunk.toString().split('\n\n'); 218 | for (const payload of payloads) { 219 | if (payload.includes('[DONE]') || stopTextStream) { 220 | dataStart = false; 221 | resolve(data); 222 | return; 223 | } 224 | 225 | if (payload.startsWith('data:')) { 226 | content = parseContent(payload); 227 | // Use buffer only for start detection 228 | if (!dataStart) { 229 | // Append content to the buffer 230 | buffer += content; 231 | if (buffer.match(excludedPrefix ?? '')) { 232 | dataStart = true; 233 | // Clear the buffer once it has served its purpose 234 | buffer = ''; 235 | if (excludedPrefix) break; 236 | } 237 | } 238 | 239 | if (dataStart && content) { 240 | const contentWithoutExcluded = stripRegexPatterns( 241 | content, 242 | excluded 243 | ); 244 | 245 | data += contentWithoutExcluded; 246 | writer(contentWithoutExcluded); 247 | } 248 | } 249 | } 250 | } 251 | 252 | function parseContent(payload: string): string { 253 | const data = payload.replaceAll(/(\n)?^data:\s*/g, ''); 254 | try { 255 | const delta = JSON.parse(data.trim()); 256 | return delta.choices?.[0]?.delta?.content ?? ''; 257 | } catch (error) { 258 | return `Error with JSON.parse and ${payload}.\n${error}`; 259 | } 260 | } 261 | 262 | resolve(data); 263 | }); 264 | 265 | function getExplanationPrompt(script: string) { 266 | return dedent` 267 | ${explainScript} Please reply in ${i18n.getCurrentLanguagenName()} 268 | 269 | The script: ${script} 270 | `; 271 | } 272 | 273 | function getShellDetails() { 274 | const shellDetails = detectShell(); 275 | 276 | return dedent` 277 | The target shell is ${shellDetails} 278 | `; 279 | } 280 | const shellDetails = getShellDetails(); 281 | 282 | const explainScript = dedent` 283 | Please provide a clear, concise description of the script, using minimal words. Outline the steps in a list format. 284 | `; 285 | 286 | function getOperationSystemDetails() { 287 | const os = require('@nexssp/os/legacy'); 288 | return os.name(); 289 | } 290 | const generationDetails = dedent` 291 | Only reply with the single line command surrounded by three backticks. It must be able to be directly run in the target shell. Do not include any other text. 292 | 293 | Make sure the command runs on ${getOperationSystemDetails()} operating system. 294 | `; 295 | 296 | function getFullPrompt(prompt: string) { 297 | return dedent` 298 | Create a single line command that one can enter in a terminal and run, based on what is specified in the prompt. 299 | 300 | ${shellDetails} 301 | 302 | ${generationDetails} 303 | 304 | ${explainInSecondRequest ? '' : explainScript} 305 | 306 | The prompt is: ${prompt} 307 | `; 308 | } 309 | 310 | function getRevisionPrompt(prompt: string, code: string) { 311 | return dedent` 312 | Update the following script based on what is asked in the following prompt. 313 | 314 | The script: ${code} 315 | 316 | The prompt: ${prompt} 317 | 318 | ${generationDetails} 319 | `; 320 | } 321 | 322 | export async function getModels( 323 | key: string, 324 | apiEndpoint: string 325 | ): Promise { 326 | const openAi = getOpenAi(key, apiEndpoint); 327 | const response = await openAi.listModels(); 328 | 329 | return response.data.data.filter((model) => model.object === 'model'); 330 | } 331 | --------------------------------------------------------------------------------