btca 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +127 -5
- package/dist/btca-darwin-arm64 +0 -0
- package/dist/btca-darwin-x64 +0 -0
- package/dist/btca-linux-arm64 +0 -0
- package/dist/btca-linux-x64 +0 -0
- package/dist/index.js +132 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,15 +1,137 @@
|
|
|
1
|
-
#
|
|
1
|
+
# btca
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A CLI tool for asking questions about technologies using their source code repositories.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
4
6
|
|
|
5
7
|
```bash
|
|
6
8
|
bun install
|
|
7
9
|
```
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
bun run src/index.ts
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Or after building:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
btca <command>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Commands
|
|
24
|
+
|
|
25
|
+
### `btca`
|
|
26
|
+
|
|
27
|
+
Show version information.
|
|
28
|
+
|
|
29
|
+
### `btca ask`
|
|
30
|
+
|
|
31
|
+
Ask a question about a technology.
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
btca ask -t <tech> -q <question>
|
|
35
|
+
btca ask --tech svelte --question "How do I create a reactive store?"
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Options:
|
|
39
|
+
- `-t, --tech` - The technology/repo to query
|
|
40
|
+
- `-q, --question` - The question to ask
|
|
41
|
+
|
|
42
|
+
### `btca chat`
|
|
43
|
+
|
|
44
|
+
Start an interactive TUI chat session.
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
btca chat -t <tech>
|
|
48
|
+
btca chat --tech nextjs
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Options:
|
|
52
|
+
- `-t, --tech` - The technology/repo to chat about
|
|
53
|
+
|
|
54
|
+
### `btca serve`
|
|
55
|
+
|
|
56
|
+
Start an HTTP server to answer questions via API.
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
btca serve
|
|
60
|
+
btca serve -p 3000
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Options:
|
|
64
|
+
- `-p, --port` - Port to listen on (default: 8080)
|
|
65
|
+
|
|
66
|
+
Endpoint:
|
|
67
|
+
- `POST /question` - Send `{ "tech": "svelte", "question": "..." }` to get answers
|
|
68
|
+
|
|
69
|
+
### `btca open`
|
|
70
|
+
|
|
71
|
+
Hold an OpenCode instance in the background for faster subsequent queries.
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
btca open
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### `btca config`
|
|
78
|
+
|
|
79
|
+
Manage CLI configuration. Shows the config file path when run without subcommands.
|
|
10
80
|
|
|
11
81
|
```bash
|
|
12
|
-
|
|
82
|
+
btca config
|
|
13
83
|
```
|
|
14
84
|
|
|
15
|
-
|
|
85
|
+
#### `btca config model`
|
|
86
|
+
|
|
87
|
+
View or set the model and provider.
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
# View current model/provider
|
|
91
|
+
btca config model
|
|
92
|
+
|
|
93
|
+
# Set model and provider
|
|
94
|
+
btca config model -p <provider> -m <model>
|
|
95
|
+
btca config model --provider anthropic --model claude-3-opus
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Options:
|
|
99
|
+
- `-p, --provider` - The provider to use
|
|
100
|
+
- `-m, --model` - The model to use
|
|
101
|
+
|
|
102
|
+
Both options must be specified together when updating.
|
|
103
|
+
|
|
104
|
+
#### `btca config repos list`
|
|
105
|
+
|
|
106
|
+
List all configured repositories.
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
btca config repos list
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
#### `btca config repos add`
|
|
113
|
+
|
|
114
|
+
Add a new repository to the configuration.
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
btca config repos add -n <name> -u <url> [-b <branch>] [--notes <notes>]
|
|
118
|
+
btca config repos add --name react --url https://github.com/facebook/react --branch main
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Options:
|
|
122
|
+
- `-n, --name` - Unique name for the repo (required)
|
|
123
|
+
- `-u, --url` - Git repository URL (required)
|
|
124
|
+
- `-b, --branch` - Branch to use (default: "main")
|
|
125
|
+
- `--notes` - Special instructions for the AI when using this repo
|
|
126
|
+
|
|
127
|
+
## Configuration
|
|
128
|
+
|
|
129
|
+
Configuration is stored at `~/.config/btca/btca.json`. The config file includes:
|
|
130
|
+
|
|
131
|
+
- `promptsDirectory` - Directory for system prompts
|
|
132
|
+
- `reposDirectory` - Directory where repos are cloned
|
|
133
|
+
- `port` - Default server port
|
|
134
|
+
- `maxInstances` - Maximum concurrent OpenCode instances
|
|
135
|
+
- `repos` - Array of configured repositories
|
|
136
|
+
- `model` - AI model to use
|
|
137
|
+
- `provider` - AI provider to use
|
package/dist/btca-darwin-arm64
CHANGED
|
Binary file
|
package/dist/btca-darwin-x64
CHANGED
|
Binary file
|
package/dist/btca-linux-arm64
CHANGED
|
Binary file
|
package/dist/btca-linux-x64
CHANGED
|
Binary file
|
package/dist/index.js
CHANGED
|
@@ -60472,9 +60472,10 @@ var DEFAULT_CONFIG = {
|
|
|
60472
60472
|
specialNotes: "This is the svelte docs website repo, not the actual svelte repo. Use the docs to answer questions about svelte."
|
|
60473
60473
|
},
|
|
60474
60474
|
{
|
|
60475
|
-
name: "
|
|
60476
|
-
url: "https://github.com/
|
|
60477
|
-
branch: "main"
|
|
60475
|
+
name: "tailwindcss",
|
|
60476
|
+
url: "https://github.com/tailwindlabs/tailwindcss.com",
|
|
60477
|
+
branch: "main",
|
|
60478
|
+
specialNotes: "This is the tailwindcss docs website repo, not the actual tailwindcss repo. Use the docs to answer questions about tailwindcss."
|
|
60478
60479
|
},
|
|
60479
60480
|
{
|
|
60480
60481
|
name: "nextjs",
|
|
@@ -60485,6 +60486,29 @@ var DEFAULT_CONFIG = {
|
|
|
60485
60486
|
model: "big-pickle",
|
|
60486
60487
|
provider: "opencode"
|
|
60487
60488
|
};
|
|
60489
|
+
var collapseHome = (path2) => {
|
|
60490
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
60491
|
+
if (home && path2.startsWith(home)) {
|
|
60492
|
+
return "~" + path2.slice(home.length);
|
|
60493
|
+
}
|
|
60494
|
+
return path2;
|
|
60495
|
+
};
|
|
60496
|
+
var writeConfig = (config2) => exports_Effect.gen(function* () {
|
|
60497
|
+
const path2 = yield* exports_Path.Path;
|
|
60498
|
+
const fs = yield* exports_FileSystem.FileSystem;
|
|
60499
|
+
const configDir = yield* expandHome(CONFIG_DIRECTORY);
|
|
60500
|
+
const configPath = path2.join(configDir, CONFIG_FILENAME);
|
|
60501
|
+
const configToWrite = {
|
|
60502
|
+
...config2,
|
|
60503
|
+
promptsDirectory: collapseHome(config2.promptsDirectory),
|
|
60504
|
+
reposDirectory: collapseHome(config2.reposDirectory)
|
|
60505
|
+
};
|
|
60506
|
+
yield* fs.writeFileString(configPath, JSON.stringify(configToWrite, null, 2)).pipe(exports_Effect.catchAll((error4) => exports_Effect.fail(new ConfigError({
|
|
60507
|
+
message: "Failed to write config",
|
|
60508
|
+
cause: error4
|
|
60509
|
+
}))));
|
|
60510
|
+
return configToWrite;
|
|
60511
|
+
});
|
|
60488
60512
|
var OPENCODE_CONFIG = (args2) => exports_Effect.gen(function* () {
|
|
60489
60513
|
const path2 = yield* exports_Path.Path;
|
|
60490
60514
|
return {
|
|
@@ -60583,7 +60607,7 @@ var onStartLoadConfig = exports_Effect.gen(function* () {
|
|
|
60583
60607
|
var configService = exports_Effect.gen(function* () {
|
|
60584
60608
|
const path2 = yield* exports_Path.Path;
|
|
60585
60609
|
const loadedConfig = yield* onStartLoadConfig;
|
|
60586
|
-
|
|
60610
|
+
let { config: config2, configPath } = loadedConfig;
|
|
60587
60611
|
const getRepo = ({
|
|
60588
60612
|
repoName,
|
|
60589
60613
|
config: config3
|
|
@@ -60619,7 +60643,23 @@ var configService = exports_Effect.gen(function* () {
|
|
|
60619
60643
|
specialNotes: repo?.specialNotes
|
|
60620
60644
|
});
|
|
60621
60645
|
}),
|
|
60622
|
-
rawConfig: () => exports_Effect.succeed(config2)
|
|
60646
|
+
rawConfig: () => exports_Effect.succeed(config2),
|
|
60647
|
+
getRepos: () => exports_Effect.succeed(config2.repos),
|
|
60648
|
+
getModel: () => exports_Effect.succeed({ provider: config2.provider, model: config2.model }),
|
|
60649
|
+
updateModel: (args2) => exports_Effect.gen(function* () {
|
|
60650
|
+
config2 = { ...config2, provider: args2.provider, model: args2.model };
|
|
60651
|
+
yield* writeConfig(config2);
|
|
60652
|
+
return { provider: config2.provider, model: config2.model };
|
|
60653
|
+
}),
|
|
60654
|
+
addRepo: (repo) => exports_Effect.gen(function* () {
|
|
60655
|
+
const existing = config2.repos.find((r) => r.name === repo.name);
|
|
60656
|
+
if (existing) {
|
|
60657
|
+
return yield* exports_Effect.fail(new ConfigError({ message: `Repo "${repo.name}" already exists` }));
|
|
60658
|
+
}
|
|
60659
|
+
config2 = { ...config2, repos: [...config2.repos, repo] };
|
|
60660
|
+
yield* writeConfig(config2);
|
|
60661
|
+
return repo;
|
|
60662
|
+
})
|
|
60623
60663
|
};
|
|
60624
60664
|
});
|
|
60625
60665
|
|
|
@@ -60800,7 +60840,7 @@ class OcService extends exports_Effect.Service()("OcService", {
|
|
|
60800
60840
|
}
|
|
60801
60841
|
|
|
60802
60842
|
// src/services/cli.ts
|
|
60803
|
-
var VERSION = "0.2.
|
|
60843
|
+
var VERSION = "0.2.1";
|
|
60804
60844
|
var programLayer = exports_Layer.mergeAll(OcService.Default, ConfigService.Default);
|
|
60805
60845
|
var questionOption = exports_Options.text("question").pipe(exports_Options.withAlias("q"));
|
|
60806
60846
|
var techOption = exports_Options.text("tech").pipe(exports_Options.withAlias("t"));
|
|
@@ -60892,11 +60932,93 @@ var serveCommand = exports_Command2.make("serve", { port: portOption }, ({ port:
|
|
|
60892
60932
|
const HttpLive = router.pipe(exports_HttpServer.serve(), exports_HttpServer.withLogAddress, exports_Layer.provide(ServerLive));
|
|
60893
60933
|
return yield* exports_Layer.launch(HttpLive);
|
|
60894
60934
|
}).pipe(exports_Effect.scoped, exports_Effect.provide(programLayer)));
|
|
60895
|
-
var
|
|
60935
|
+
var providerOption = exports_Options.text("provider").pipe(exports_Options.withAlias("p"), exports_Options.optional);
|
|
60936
|
+
var modelOption = exports_Options.text("model").pipe(exports_Options.withAlias("m"), exports_Options.optional);
|
|
60937
|
+
var configModelCommand = exports_Command2.make("model", { provider: providerOption, model: modelOption }, ({ provider, model }) => exports_Effect.gen(function* () {
|
|
60896
60938
|
const config2 = yield* ConfigService;
|
|
60897
|
-
|
|
60898
|
-
|
|
60939
|
+
if (provider._tag === "Some" && model._tag === "Some") {
|
|
60940
|
+
const result = yield* config2.updateModel({
|
|
60941
|
+
provider: provider.value,
|
|
60942
|
+
model: model.value
|
|
60943
|
+
});
|
|
60944
|
+
console.log(`Updated model configuration:`);
|
|
60945
|
+
console.log(` Provider: ${result.provider}`);
|
|
60946
|
+
console.log(` Model: ${result.model}`);
|
|
60947
|
+
} else if (provider._tag === "Some" || model._tag === "Some") {
|
|
60948
|
+
console.error("Error: Both --provider and --model must be specified together");
|
|
60949
|
+
process.exit(1);
|
|
60950
|
+
} else {
|
|
60951
|
+
const current2 = yield* config2.getModel();
|
|
60952
|
+
console.log(`Current model configuration:`);
|
|
60953
|
+
console.log(` Provider: ${current2.provider}`);
|
|
60954
|
+
console.log(` Model: ${current2.model}`);
|
|
60955
|
+
}
|
|
60956
|
+
}).pipe(exports_Effect.provide(programLayer)));
|
|
60957
|
+
var configReposListCommand = exports_Command2.make("list", {}, () => exports_Effect.gen(function* () {
|
|
60958
|
+
const config2 = yield* ConfigService;
|
|
60959
|
+
const repos = yield* config2.getRepos();
|
|
60960
|
+
if (repos.length === 0) {
|
|
60961
|
+
console.log("No repos configured.");
|
|
60962
|
+
return;
|
|
60963
|
+
}
|
|
60964
|
+
console.log(`Configured repos:
|
|
60965
|
+
`);
|
|
60966
|
+
for (const repo of repos) {
|
|
60967
|
+
console.log(` ${repo.name}`);
|
|
60968
|
+
console.log(` URL: ${repo.url}`);
|
|
60969
|
+
console.log(` Branch: ${repo.branch}`);
|
|
60970
|
+
if (repo.specialNotes) {
|
|
60971
|
+
console.log(` Notes: ${repo.specialNotes}`);
|
|
60972
|
+
}
|
|
60973
|
+
console.log();
|
|
60974
|
+
}
|
|
60899
60975
|
}).pipe(exports_Effect.provide(programLayer)));
|
|
60976
|
+
var repoNameOption = exports_Options.text("name").pipe(exports_Options.withAlias("n"));
|
|
60977
|
+
var repoUrlOption = exports_Options.text("url").pipe(exports_Options.withAlias("u"));
|
|
60978
|
+
var repoBranchOption = exports_Options.text("branch").pipe(exports_Options.withAlias("b"), exports_Options.withDefault("main"));
|
|
60979
|
+
var repoNotesOption = exports_Options.text("notes").pipe(exports_Options.optional);
|
|
60980
|
+
var configReposAddCommand = exports_Command2.make("add", {
|
|
60981
|
+
name: repoNameOption,
|
|
60982
|
+
url: repoUrlOption,
|
|
60983
|
+
branch: repoBranchOption,
|
|
60984
|
+
notes: repoNotesOption
|
|
60985
|
+
}, ({ name, url: url2, branch, notes }) => exports_Effect.gen(function* () {
|
|
60986
|
+
const config2 = yield* ConfigService;
|
|
60987
|
+
const repo = {
|
|
60988
|
+
name,
|
|
60989
|
+
url: url2,
|
|
60990
|
+
branch,
|
|
60991
|
+
...notes._tag === "Some" ? { specialNotes: notes.value } : {}
|
|
60992
|
+
};
|
|
60993
|
+
yield* config2.addRepo(repo);
|
|
60994
|
+
console.log(`Added repo "${name}":`);
|
|
60995
|
+
console.log(` URL: ${url2}`);
|
|
60996
|
+
console.log(` Branch: ${branch}`);
|
|
60997
|
+
if (notes._tag === "Some") {
|
|
60998
|
+
console.log(` Notes: ${notes.value}`);
|
|
60999
|
+
}
|
|
61000
|
+
}).pipe(exports_Effect.catchTag("ConfigError", (e) => exports_Effect.sync(() => {
|
|
61001
|
+
console.error(`Error: ${e.message}`);
|
|
61002
|
+
process.exit(1);
|
|
61003
|
+
})), exports_Effect.provide(programLayer)));
|
|
61004
|
+
var configReposCommand = exports_Command2.make("repos", {}, () => exports_Effect.sync(() => {
|
|
61005
|
+
console.log("Usage: btca config repos <command>");
|
|
61006
|
+
console.log("");
|
|
61007
|
+
console.log("Commands:");
|
|
61008
|
+
console.log(" list List all configured repos");
|
|
61009
|
+
console.log(" add Add a new repo");
|
|
61010
|
+
})).pipe(exports_Command2.withSubcommands([configReposListCommand, configReposAddCommand]));
|
|
61011
|
+
var configCommand = exports_Command2.make("config", {}, () => exports_Effect.gen(function* () {
|
|
61012
|
+
const config2 = yield* ConfigService;
|
|
61013
|
+
const configPath = yield* config2.getConfigPath();
|
|
61014
|
+
console.log(`Config file: ${configPath}`);
|
|
61015
|
+
console.log("");
|
|
61016
|
+
console.log("Usage: btca config <command>");
|
|
61017
|
+
console.log("");
|
|
61018
|
+
console.log("Commands:");
|
|
61019
|
+
console.log(" model View or set the model and provider");
|
|
61020
|
+
console.log(" repos Manage configured repos");
|
|
61021
|
+
}).pipe(exports_Effect.provide(programLayer))).pipe(exports_Command2.withSubcommands([configModelCommand, configReposCommand]));
|
|
60900
61022
|
var mainCommand = exports_Command2.make("btca", {}, () => exports_Effect.sync(() => {
|
|
60901
61023
|
console.log(`btca v${VERSION}. run btca --help for more information.`);
|
|
60902
61024
|
})).pipe(exports_Command2.withSubcommands([
|
|
@@ -60904,7 +61026,7 @@ var mainCommand = exports_Command2.make("btca", {}, () => exports_Effect.sync(()
|
|
|
60904
61026
|
serveCommand,
|
|
60905
61027
|
openCommand,
|
|
60906
61028
|
chatCommand,
|
|
60907
|
-
|
|
61029
|
+
configCommand
|
|
60908
61030
|
]));
|
|
60909
61031
|
var cliService = exports_Effect.gen(function* () {
|
|
60910
61032
|
return {
|