This is the second article of a series in which I am building an open source RAG CLI tool. In the for this project.
But you said you need to compile your code into a single binary, can Typescritpt do that?
Yes, Bun can produce a binary of your code: https://bun.sh/docs/bundler/executables
I thought Python was the preferred language for AI stuff, and it seems to fit your constraints.
Yes, Python would be a good option. I just chose Typescript because I am more familiar with it.
Creating the project
Let's start by creating an empty bun project:
bun init
After writing the project name rag and the entry file src/main.ts, I have the following directory structure:
node_modules/
src/
main.ts
.gitignore
bun.lockb
package.json
tsconfig.json
I like to use Prettier to format my code, so
bun add -D prettier
and create a .prettierrc file:
{
"printWidth": 155,
"semi": false,
"tabWidth": 2,
"singleQuote": true,
"trailingComma": "all",
"bracketSpacing": true
}
Implementing the CLI interface
I chose to use the commander package to implement the CLI interface. After installing it, I wrote the following code in src/main.ts:
import { program } from 'commander'
program.name('rag').version('0.0.1').description('Simple RAG system for developers')
const docs = program.command('docs').description('Manage documentations')
docs
.command('add')
.description('Add new documentation')
.argument('<name>', 'Name of the documentation')
.argument('<repo_url>', 'URL of the Git repository')
.option('--subdir <subdir>', 'Subdirectory within the repository', '.')
.option('--branch <branch>', 'Branch to use', 'main')
.action((name, repo_url, options) => {
console.log(`Adding documentation ${name} from ${repo_url} with options ${JSON.stringify(options)}`)
})
docs
.command('update')
.description('Update existing documentation')
.argument('<name>', 'Name of the documentation')
.option('--repo_url <repo_url>', 'New repository URL')
.option('--subdir <subdir>', 'New subdirectory within the repository')
.option('--branch <branch>', 'New branch to use')
.action((name, options) => {
console.log(`Updating documentation ${name} with options ${JSON.stringify(options)}`)
})
docs
.command('remove')
.description('Remove existing documentation')
.argument('<name>', 'Name of the documentation')
.action((name) => {
console.log(`Removing documentation ${name}`)
})
program
.command('get')
.description('Get relevant parts from documentations')
.argument('<prompt>', 'Search prompt')
.option('--count <count>', 'Number of relevant parts to retrieve', '5')
.option('--json', 'Output results in JSON format')
.action((prompt, options) => {
console.log(`Getting relevant parts from documentations for prompt ${prompt} with options ${JSON.stringify(options)}`)
})
program.parseAsync().catch((err) => {
console.error(err)
process.exit(1)
})
Now running bun src/main.ts shows the following output:
Usage: rag [options] [command]
Simple RAG system for developers
Options:
-V, --version output the version number
-h, --help display help for command
Commands:
docs Manage documentations
get [options] <prompt> Get relevant parts from documentations
help [command] display help for command
And doing bun src/main.ts docs add tailwindcss https://github.com/tailwindlabs/tailwindcss shows the following output:
Adding documentation tailwindcss from https://github.com/tailwindlabs/tailwindcss with options {"branch":"main", "subdir":"."}
So I guess the CLI interface is working as expected.
No, you said that the
promptargument should be read from stdin if it doesn't exist. But it seems to be required right now.
You are right, if I do
bun src/main.ts get
I get the following error:
Missing required argument 'prompt'
To make the argument optional, we should use [prompt] instead of <prompt> in the argument definition. And now we can check when the argument is missing, and read it from stdin.
program
.command('get')
.argument('[prompt]', 'Search prompt')
// ...
.action(async (prompt, options) => {
prompt = prompt || await Bun.stdin.text()
console.log(`Getting relevant parts from documentations for prompt "${prompt}" with options ${JSON.stringify(options)}`)
})
Which gives the following:
❯ bun src/main.ts get "arg prompt"
Getting relevant parts from documentations for prompt "arg prompt" with options {"count":"5"}
❯ echo "stdin prompt" | bun src/main.ts get
Getting relevant parts from documentations for prompt "stdin prompt
" with options {"count":"5"}
Summary
In this article,
- we designed the CLI interface and chose to have the commands
add,update,removeandget. - we chose to implement the tool using Typescript and Bun.
- we implemented the CLI interface using the
commanderpackage.
What's next
The next steps are:
- Add the database
- Implement the commands logic
- Add tests
- Create CI/CD pipeline
Feel free to comment if you have any suggestions or feedback. See you in the next article!
SOCIAL SHARE CARD GENERATOR