Skip to content

Commit

Permalink
Merge pull request #93 from warpy-ai/main
Browse files Browse the repository at this point in the history
Push new updates
  • Loading branch information
jucasoliveira authored Sep 13, 2024
2 parents 19d049a + 3442e00 commit ff0dd6b
Show file tree
Hide file tree
Showing 32 changed files with 4,519 additions and 4,097 deletions.
93 changes: 32 additions & 61 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,52 +2,52 @@
<img width="200" alt="TerminalGPT logo" src="https://github.com/jucasoliveira/terminalGPT/assets/11979969/f371e361-6c74-4a5b-9634-c537aa6db21d"/>
</p>

<h1 align="center">TerminalGPT</h1>

<p align="center">
<img width="80" alt="TerminalGPT logo" src="https://img.shields.io/github/actions/workflow/status/jucasoliveira/terminalGPT/pr.yml"/>
<img width="100" alt="TerminalGPT logo" src="https://img.shields.io/npm/dt/terminalgpt"/>
<img width="100" alt="TerminalGPT logo" src="https://img.shields.io/github/contributors/jucasoliveira/terminalGPT"/>
<img width="100" alt="TerminalGPT logo" src="https://img.shields.io/github/package-json/v/jucasoliveira/terminalGPT"/>

<p align="center" class={{ marginTop: "10px"}}>
<img width="500" alt="TerminalGPT logo" src="https://github.com/user-attachments/assets/c5ea1861-5994-4fe2-af44-ac92c1fa3013"/>
</p>

<p align="center">
Get GPT-like chatGPT on your terminal
</p>

<p align="center">
<img alt="TerminalGPT logo" src="https://github.com/jucasoliveira/terminalGPT/assets/11979969/3de20615-87ad-4157-99ad-33ba2687214b"/>
<strong>Get GPT-like chatGPT on your terminal</strong>
</p>


<p align="center">
<a href="https://www.producthunt.com/posts/terminalgpt?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-terminalgpt" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=373888&theme=light" alt="terminalGPT - Use&#0032;OpenAi&#0032;like&#0032;chatGPT&#0044;&#0032;on&#0032;your&#0032;terminal | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
<img src="https://img.shields.io/github/actions/workflow/status/jucasoliveira/terminalGPT/pr.yml" alt="Build Status"/>
<img src="https://img.shields.io/npm/dt/terminalgpt" alt="Downloads"/>
<img src="https://img.shields.io/github/contributors/jucasoliveira/terminalGPT" alt="Contributors"/>
<img src="https://img.shields.io/github/package-json/v/jucasoliveira/terminalGPT" alt="Version"/>
</p>

## Stats

<p align="center">
<img alt="TerminalGPT logo" src="https://repobeats.axiom.co/api/embed/92b8c74cac77f3fbb0e843cc3f6a36b01e7bd152.svg"/>
<img alt="TerminalGPT stats" src="https://repobeats.axiom.co/api/embed/92b8c74cac77f3fbb0e843cc3f6a36b01e7bd152.svg"/>
</p>



<p align="center">
<a href="https://www.producthunt.com/posts/terminalgpt?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-terminalgpt" target="_blank">
<img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=373888&theme=light" alt="terminalGPT - Use OpenAI like chatGPT, on your terminal | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" />
</a>
</p>

## Prerequisites

You'll need to have your own `OpenAi` apikey to operate this package.
You need an LLM API key to use this package:

1. Go to <https://platform.openai.com>
2. Select your profile menu and go to `View API Keys`
3. Select `+ Create new secret key`
4. Copy generated key
- [OpenAI](https://platform.openai.com/docs/api-reference/introduction)
- [Anthropic](https://www.anthropic.com/)
- [Groq](https://www.groq.com/)
- [Gemini](https://gemini.google.com/)

# Installation
## Installation

Install terminalGPT globally:
Install TerminalGPT globally:

```bash
npm -g install terminalgpt
npm install -g terminalgpt
```

or
Expand All @@ -56,62 +56,33 @@ or
yarn global add terminalgpt
```

## Start chat

```bash
tgpt chat
```

PS: If it is your first time running it, it will ask for open AI key, **paste generated key from pre-requisite steps**.

## Options
## Usage

### Change engine and temperature
Start a chat:

```bash
tgpt chat --engine "gpt-4" --temperature 0.7
tgpt chat
```

Note this library uses [Chat Completions API](https://platform.openai.com/docs/api-reference/chat).
The `engine` parameter is the same as the `model` parameter in the API. The default value is `gpt-3.5-turbo`.

### Use markdown
On first run, you'll be prompted to enter your OpenAI API key.

```bash
tgpt chat --markdown
```
### Options

## Change or delete api key
### Commands

It you are not satisfied or added a wrong api key, run
Delete all conversations:

```bash
tgpt delete
```

## Using with npx

```bash
npx terminalgpt
```

```bash
npx terminalgpt <command>
```

Note `npx terminalgpt` doesn't install the terminalgpt package, instead it downloads the package to your computer and directly executes it from the cache.

You can find the package using

`ls ~/.npm/_npx/*/node_modules`

To delete the package, you can use
### Using with npx

`rm -r ~/.npm/_npx/*/node_modules/terminalgpt`
Note: `npx terminalgpt` runs the package without installation.

## Contributing

Refer to CONTRIBUTING.md 😎
Please refer to [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to contribute to this project.

## ✨ Contributors

Expand Down
14 changes: 9 additions & 5 deletions __tests__/encrypt.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,20 +51,24 @@ describe("Credentials Management", () => {
expect(result).to.deep.equal({
apiKey: null,
engine: null,
model: null,
tavilyApiKey: null,
});
});

it("should save and retrieve credentials correctly", () => {
saveCredentials("testApiKey", "testEngine", "testTavilyApiKey");
saveCredentials("testApiKey", "testEngine", "model", "testTavilyApiKey");
const result = getCredentials();
expect(result.apiKey).to.equal("testApiKey");
expect(result.engine).to.equal("testEngine");
expect(result.tavilyApiKey).to.equal("testTavilyApiKey");
expect(result).to.deep.equal({
apiKey: "testApiKey",
engine: "testEngine",
model: "model",
tavilyApiKey: "testTavilyApiKey",
});
});

it("should encrypt the API key when saving", () => {
saveCredentials("testApiKey", "testEngine", "testTavilyApiKey");
saveCredentials("testApiKey", "testEngine", "model", "testTavilyApiKey");
const rawData = fs.readFileSync(credentialsPath, "utf-8");
const savedCredentials = JSON.parse(rawData);
expect(savedCredentials.apiKey).to.not.equal("testApiKey");
Expand Down
68 changes: 68 additions & 0 deletions __tests__/plugin.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */
// @ts-ignore
import clearPlugin from "../src/commands/clear";
import exitPlugin from "../src/commands/exit";
import chalk from "chalk";

// Force chalk to enable color output in test environment
chalk.level = 1;

describe("Clear Plugin", () => {
let originalStdoutWrite: typeof process.stdout.write;

beforeEach(() => {
originalStdoutWrite = process.stdout.write;
process.stdout.write = jest.fn();
});

afterEach(() => {
process.stdout.write = originalStdoutWrite;
});

it("should have the correct name, keyword, and description", () => {
expect(clearPlugin.name).toBe("clear");
expect(clearPlugin.keyword).toBe("@clear");
expect(clearPlugin.description).toBe("Clears the terminal screen");
});

it("should clear the terminal screen when executed", async () => {
const result = await clearPlugin.execute({});
expect(process.stdout.write).toHaveBeenCalledWith("\x1Bc");
expect(result).toBe("Terminal screen cleared.");
});
});

describe("Exit Plugin", () => {
let consoleLogSpy: jest.SpyInstance;
let processExitSpy: jest.SpyInstance;

beforeEach(() => {
consoleLogSpy = jest.spyOn(console, "log").mockImplementation(() => {});
processExitSpy = jest
.spyOn(process, "exit")
.mockImplementation((code?: number) => {
throw new Error(`Process.exit called with code: ${code}`);
});
});

afterEach(() => {
consoleLogSpy.mockRestore();
processExitSpy.mockRestore();
});

it("should have the correct name, keyword, and description", () => {
expect(exitPlugin.name).toBe("exit");
expect(exitPlugin.keyword).toBe("@exit");
expect(exitPlugin.description).toBe("Exits the application");
});

it("should log goodbye message and exit the process when executed", async () => {
await expect(exitPlugin.execute({})).rejects.toThrow(
"Process.exit called with code: 0"
);
expect(consoleLogSpy).toHaveBeenCalledWith(
expect.stringContaining("Goodbye!")
);
expect(processExitSpy).toHaveBeenCalledWith(0);
});
});
2 changes: 2 additions & 0 deletions __tests__/utils.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ describe("apiKeyPrompt()", () => {
JSON.stringify({
apiKey: encrypt("test"),
engine: "test",
model: "test",
tavilyApiKey: encrypt("test"),
})
);
Expand All @@ -29,6 +30,7 @@ describe("apiKeyPrompt()", () => {
expect(result).to.be.an("object");
expect(result).to.have.property("apiKey");
expect(result).to.have.property("engine");
expect(result).to.have.property("model");
});
});

Expand Down
Binary file modified bun.lockb
Binary file not shown.
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "terminalgpt",
"version": "1.7.6",
"version": "2.0.1",
"main": "lib/index.js",
"description": "Get GPT like chatGPT on your terminal",
"scripts": {
Expand Down Expand Up @@ -43,6 +43,7 @@
"clipboardy": "2.3.0",
"commander": "^9.5.0",
"compromise": "^14.8.1",
"execa": "^9.3.1",
"gradient-string": "^2.0.2",
"hnswlib-node": "^3.0.0",
"lowdb": "^5.1.0",
Expand All @@ -59,6 +60,7 @@
"@types/chalk": "^2.2.0",
"@types/clipboardy": "2.0.1",
"@types/eslint": "^8.44.2",
"@types/execa": "^2.0.0",
"@types/jest": "^29.5.4",
"@types/ora": "^3.2.0",
"@typescript-eslint/eslint-plugin": "^6.4.1",
Expand Down
Empty file removed src/commands/autossugestion.ts
Empty file.
14 changes: 11 additions & 3 deletions src/commands/clear.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
const clearFunc = () => {
process.stdout.write("\x1Bc");
import { Plugin } from "./index";

const clearPlugin: Plugin = {
name: "clear",
keyword: "@clear",
description: "Clears the terminal screen",
execute: async () => {
process.stdout.write("\x1Bc");
return "Terminal screen cleared.";
},
};

export default clearFunc;
export default clearPlugin;
14 changes: 10 additions & 4 deletions src/commands/exit.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import { Plugin } from "./index";
import chalk from "chalk";

const exitFunc = () => {
console.log(chalk.yellow("Goodbye!"));
process.exit(0);
const exitPlugin: Plugin = {
name: "exit",
keyword: "@exit",
description: "Exits the application",
execute: async () => {
console.log(chalk.yellow("Goodbye!"));
process.exit(0);
},
};

export default exitFunc;
export default exitPlugin;
49 changes: 33 additions & 16 deletions src/commands/file.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,39 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import chalk from "chalk";
import { Plugin } from "./index";
import { handleFileReference } from "../handlers/fileHandler"; // Assuming this function exists
import { apiKeyPrompt, promptResponse } from "../utils"; // Assuming this function exists
import { promptResponse } from "../utils"; // Assuming this function exists

const fileFunc = async (userInput: string) => {
const creds = await apiKeyPrompt();
// we need to call file handler here
const [, filePath, ...promptParts] = userInput.split(" ");
const promptText = promptParts.join(" ");
if (filePath) {
await handleFileReference(filePath, promptText);
if (creds.apiKey != null) {
await promptResponse(creds.engine, creds.apiKey, userInput, {});
const filePlugin: Plugin = {
name: "file",
keyword: "@file",
description: "Handles file operations and references",
execute: async (context: {
userInput: string;
engine: string;
apiKey: string;
opts: any;
}) => {
const { userInput, engine, apiKey, opts } = context;
const [, filePath, ...promptParts] = userInput.split(" ");
const promptText = promptParts.join(" ");

if (filePath) {
try {
await handleFileReference(filePath, promptText);
const response = await promptResponse(engine, apiKey, userInput, opts);
return response;
} catch (error) {
console.error(chalk.red(`Error handling file: ${error}`));
return `Error: ${error}`;
}
} else {
console.log(
chalk.yellow("Please provide a file path. Usage: @file <path> [prompt]")
);
return "Error: No file path provided";
}
} else {
console.log(
chalk.yellow("Please provide a file path. Usage: @file <path> [prompt]")
);
}
},
};

export default fileFunc;
export default filePlugin;
Loading

0 comments on commit ff0dd6b

Please sign in to comment.