Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Push new updates #93

Merged
merged 21 commits into from
Sep 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
1d7ced7
Merge pull request #2 from warpy-ai/refactor_credentials_encription
jucasoliveira Sep 9, 2024
c6833cc
feat(commands): refactor clear, exit, file, fileTree, web commands to…
jucasoliveira Sep 11, 2024
d594dc9
add unuit test
jucasoliveira Sep 11, 2024
ec9d6b7
add changes
jucasoliveira Sep 11, 2024
7a49012
chore(commands): add eslint-disable comments for specific rules in in…
jucasoliveira Sep 11, 2024
9a81231
refactor(intro.ts): change intro function to be asynchronous to suppo…
jucasoliveira Sep 11, 2024
1ed4eb7
feat(Engine.ts): Add support for 'hasContext' parameter in engine fun…
jucasoliveira Sep 12, 2024
59c0a60
feat(package.json): add execa package to dependencies for executing c…
jucasoliveira Sep 12, 2024
aee5108
Merge pull request #3 from warpy-ai/add_plugin_definer
jucasoliveira Sep 12, 2024
f164e6e
refactor(context.ts): convert VectorStore class to VectorStore interf…
jucasoliveira Sep 12, 2024
764e6d0
fix(creds.ts): add 'model' property to getCredentials function to han…
jucasoliveira Sep 13, 2024
c47ce1c
feat(creds.ts): add model parameter to saveCredentials function for s…
jucasoliveira Sep 13, 2024
e62f9f2
chore(package.json): bump version from 1.7.6 to 2.0.0 to indicate a m…
jucasoliveira Sep 13, 2024
a5c9400
style(README.md): improve formatting and consistency in README.md file
jucasoliveira Sep 13, 2024
ef97f35
chore(README.md): remove duplicate 'Stats' section and associated ima…
jucasoliveira Sep 13, 2024
8f0a85e
docs(README.md): remove broken demo image link to improve README.md r…
jucasoliveira Sep 13, 2024
f23d1bc
Update README.md
jucasoliveira Sep 13, 2024
24ed07b
Merge pull request #4 from warpy-ai/improve_context
jucasoliveira Sep 13, 2024
94ef4a2
feat(encrypt.spec.ts): add support for 'model' field in credentials m…
jucasoliveira Sep 13, 2024
f2f7301
2.0.1
jucasoliveira Sep 13, 2024
3442e00
Merge pull request #5 from warpy-ai/unit_test_renew
jucasoliveira Sep 13, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading