@codevector/cli 0.3.3 → 0.3.4
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 +1 -4
- package/dist/index.js +44 -141
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -34,7 +34,6 @@ That's the whole onboarding. Pick a tool, pick a scope, done.
|
|
|
34
34
|
| -------------------------------- | ---------------------------------------- | ---------------------- | ---------------------------------------------------------- | --------------------- |
|
|
35
35
|
| **Claude Code** | `~/.claude/settings.json` | user / project / local | literal in JSON (`ANTHROPIC_AUTH_TOKEN`) | `Stop` + `SessionEnd` |
|
|
36
36
|
| **opencode** (sst/opencode) | `~/.config/opencode/opencode.json` | user / project / local | literal (user + local); env-var reference in project scope | — |
|
|
37
|
-
| **qwen-code** (Alibaba Qwen CLI) | `~/.qwen/settings.json` + `~/.qwen/.env` | user / project / local | `.env` file alongside settings (`envKey` reference) | `Stop` + `SessionEnd` |
|
|
38
37
|
| **codex** (OpenAI codex CLI) | `~/.codex/config.toml` | **user only** | shell env var (codex does not auto-load `.env`) | not wired |
|
|
39
38
|
|
|
40
39
|
Each writer emits both Anthropic and OpenAI-compatible provider entries where the tool supports both wire formats. codex is OpenAI-only.
|
|
@@ -49,7 +48,7 @@ Three scopes, same names across every supported tool that allows more than one:
|
|
|
49
48
|
| --------- | ------------------------------------------------------------------------------------------------ | ------------------------------- | --------------------------------------------------------- |
|
|
50
49
|
| `user` | your home directory (`~/.claude/…`, `~/.codex/…`, etc.) | no (outside the repo) | your personal default across every project |
|
|
51
50
|
| `project` | this repo's config dir (`./.claude/settings.json`, etc.) | **yes** — shared with teammates | base URL only; we never embed the API key here |
|
|
52
|
-
| `local` | this repo's local-override file (`./.claude/settings.local.json`, `./opencode.json
|
|
51
|
+
| `local` | this repo's local-override file (`./.claude/settings.local.json`, `./opencode.json`) | no — auto-gitignored | the usual default: per-seat API key stays on your machine |
|
|
53
52
|
|
|
54
53
|
`local` is the default when you don't pass `--scope`. The CLI adds gitignore entries automatically for tools that don't natively ignore their local file.
|
|
55
54
|
|
|
@@ -175,8 +174,6 @@ Override the config root with `CODEVECTOR_CONFIG_DIR`.
|
|
|
175
174
|
| ---------------------- | --------------------------------------------------------- | ---------------------- |
|
|
176
175
|
| Claude Code | `~/.claude/settings.json` / `.claude/settings.local.json` | JSON |
|
|
177
176
|
| opencode | `~/.config/opencode/opencode.json` / `./opencode.json` | JSON (JSONC tolerated) |
|
|
178
|
-
| qwen-code | `~/.qwen/settings.json` / `.qwen/settings.json` | JSON |
|
|
179
|
-
| qwen-code secrets | `~/.qwen/.env` / `.qwen/.env` | dotenv |
|
|
180
177
|
| codex | `~/.codex/config.toml` | TOML |
|
|
181
178
|
| codevector credentials | `~/.config/codevector/credentials.json` | JSON (chmod 0600) |
|
|
182
179
|
| codevector hook script | `~/.config/codevector/hooks/acceptance.sh` | shell |
|
package/dist/index.js
CHANGED
|
@@ -16753,7 +16753,7 @@ function assertHttpsUrl(urlString) {
|
|
|
16753
16753
|
}
|
|
16754
16754
|
|
|
16755
16755
|
// src/commands/configure.ts
|
|
16756
|
-
import { homedir as
|
|
16756
|
+
import { homedir as homedir5 } from "os";
|
|
16757
16757
|
|
|
16758
16758
|
// src/lib/hooks.ts
|
|
16759
16759
|
import { chmodSync as chmodSync2, copyFileSync, existsSync, mkdirSync as mkdirSync2 } from "fs";
|
|
@@ -18001,6 +18001,7 @@ var writeOpencodeConfig = ({
|
|
|
18001
18001
|
gatewayUrl,
|
|
18002
18002
|
apiKey,
|
|
18003
18003
|
scope,
|
|
18004
|
+
project,
|
|
18004
18005
|
model,
|
|
18005
18006
|
availableModels = []
|
|
18006
18007
|
}) => {
|
|
@@ -18009,14 +18010,16 @@ var writeOpencodeConfig = ({
|
|
|
18009
18010
|
const keyLiteral = scope === "project" ? `{env:${ENV_VAR_NAME2}}` : apiKey;
|
|
18010
18011
|
removeStaleGatewayProviders(path, gateway);
|
|
18011
18012
|
const models = buildModelEntries(availableModels);
|
|
18013
|
+
const options = {
|
|
18014
|
+
apiKey: keyLiteral,
|
|
18015
|
+
baseURL: `${gateway}/gateway/openai/v1`,
|
|
18016
|
+
headers: buildCustomHeaders2(scope, project)
|
|
18017
|
+
};
|
|
18012
18018
|
const patch = {
|
|
18013
18019
|
$schema: "https://opencode.ai/config.json",
|
|
18014
18020
|
provider: {
|
|
18015
18021
|
[PROVIDER_PREFIX]: {
|
|
18016
|
-
options
|
|
18017
|
-
apiKey: keyLiteral,
|
|
18018
|
-
baseURL: `${gateway}/gateway/openai/v1`
|
|
18019
|
-
},
|
|
18022
|
+
options,
|
|
18020
18023
|
models
|
|
18021
18024
|
}
|
|
18022
18025
|
}
|
|
@@ -18037,6 +18040,11 @@ var writeOpencodeConfig = ({
|
|
|
18037
18040
|
`Project scope avoids embedding secrets. Export ${ENV_VAR_NAME2}=<your key> before running opencode, or switch to local scope to embed the key.`
|
|
18038
18041
|
);
|
|
18039
18042
|
}
|
|
18043
|
+
if (scope !== "user" && !project) {
|
|
18044
|
+
notes.push(
|
|
18045
|
+
"Project slug could not be derived (no git remote and no .codevector.json). Per-project usage attribution will be blank until you set one."
|
|
18046
|
+
);
|
|
18047
|
+
}
|
|
18040
18048
|
return {
|
|
18041
18049
|
tool: "opencode",
|
|
18042
18050
|
status: "configured",
|
|
@@ -18178,126 +18186,20 @@ function removeStaleGatewayProviders(path, gateway) {
|
|
|
18178
18186
|
function isObject4(v2) {
|
|
18179
18187
|
return typeof v2 === "object" && v2 !== null && !Array.isArray(v2);
|
|
18180
18188
|
}
|
|
18181
|
-
function
|
|
18182
|
-
|
|
18183
|
-
|
|
18184
|
-
|
|
18185
|
-
// src/config-writers/qwen-code.ts
|
|
18186
|
-
import { homedir as homedir5 } from "os";
|
|
18187
|
-
import { join as join8 } from "path";
|
|
18188
|
-
|
|
18189
|
-
// src/lib/env-file.ts
|
|
18190
|
-
import { chmodSync as chmodSync5, existsSync as existsSync6, mkdirSync as mkdirSync5, readFileSync as readFileSync6, writeFileSync as writeFileSync6 } from "fs";
|
|
18191
|
-
import { dirname as dirname5 } from "path";
|
|
18192
|
-
function mergeEnvFile(path, entries) {
|
|
18193
|
-
mkdirSync5(dirname5(path), { recursive: true, mode: 448 });
|
|
18194
|
-
const existing = existsSync6(path) ? readFileSync6(path, "utf8") : "";
|
|
18195
|
-
const lines = existing.split("\n");
|
|
18196
|
-
const seen = /* @__PURE__ */ new Set();
|
|
18197
|
-
const updated = lines.map((line) => {
|
|
18198
|
-
const match = line.match(/^\s*([A-Za-z_][A-Za-z0-9_]*)\s*=/);
|
|
18199
|
-
if (!match) return line;
|
|
18200
|
-
const key = match[1];
|
|
18201
|
-
if (key === void 0 || !(key in entries)) return line;
|
|
18202
|
-
seen.add(key);
|
|
18203
|
-
return `${key}=${quote(entries[key] ?? "")}`;
|
|
18204
|
-
});
|
|
18205
|
-
for (const [key, value] of Object.entries(entries)) {
|
|
18206
|
-
if (!seen.has(key)) updated.push(`${key}=${quote(value)}`);
|
|
18207
|
-
}
|
|
18208
|
-
while (updated.length > 0 && updated[updated.length - 1] === "") updated.pop();
|
|
18209
|
-
writeFileSync6(path, `${updated.join("\n")}
|
|
18210
|
-
`);
|
|
18211
|
-
try {
|
|
18212
|
-
chmodSync5(path, 384);
|
|
18213
|
-
} catch {
|
|
18214
|
-
}
|
|
18215
|
-
}
|
|
18216
|
-
function quote(v2) {
|
|
18217
|
-
const escaped = v2.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n");
|
|
18218
|
-
return `"${escaped}"`;
|
|
18219
|
-
}
|
|
18220
|
-
|
|
18221
|
-
// src/config-writers/qwen-code.ts
|
|
18222
|
-
var ENV_VAR_NAME3 = "CODEVECTOR_GATEWAY_KEY";
|
|
18223
|
-
var writeQwenCodeConfig = ({
|
|
18224
|
-
gatewayUrl,
|
|
18225
|
-
apiKey,
|
|
18226
|
-
hookScriptPath,
|
|
18227
|
-
scope,
|
|
18228
|
-
model
|
|
18229
|
-
}) => {
|
|
18230
|
-
const path = qwenSettingsPath(scope);
|
|
18231
|
-
const gateway = trimRightSlash5(gatewayUrl);
|
|
18232
|
-
const patch = {
|
|
18233
|
-
modelProviders: {
|
|
18234
|
-
anthropic: [
|
|
18235
|
-
{
|
|
18236
|
-
id: "codevector-anthropic",
|
|
18237
|
-
name: "CodeVector Gateway (Anthropic)",
|
|
18238
|
-
envKey: ENV_VAR_NAME3,
|
|
18239
|
-
baseUrl: `${gateway}/gateway/anthropic`
|
|
18240
|
-
}
|
|
18241
|
-
],
|
|
18242
|
-
openai: [
|
|
18243
|
-
{
|
|
18244
|
-
id: "codevector-openai",
|
|
18245
|
-
name: "CodeVector Gateway (OpenAI-compat)",
|
|
18246
|
-
envKey: ENV_VAR_NAME3,
|
|
18247
|
-
baseUrl: `${gateway}/gateway/openai/v1`
|
|
18248
|
-
}
|
|
18249
|
-
]
|
|
18250
|
-
},
|
|
18251
|
-
hooks: {
|
|
18252
|
-
Stop: [{ matcher: "*", hooks: [{ type: "command", command: hookScriptPath, async: true }] }],
|
|
18253
|
-
SessionEnd: [
|
|
18254
|
-
{ matcher: "*", hooks: [{ type: "command", command: hookScriptPath, async: true }] }
|
|
18255
|
-
]
|
|
18256
|
-
}
|
|
18189
|
+
function buildCustomHeaders2(scope, project) {
|
|
18190
|
+
const safe = (v2) => v2.replace(/[\r\n]/g, "").trim();
|
|
18191
|
+
const headers = {
|
|
18192
|
+
"x-client-app": "opencode"
|
|
18257
18193
|
};
|
|
18258
|
-
if (
|
|
18259
|
-
|
|
18260
|
-
|
|
18261
|
-
|
|
18262
|
-
const notes = [];
|
|
18263
|
-
if (scope === "project") {
|
|
18264
|
-
notes.push(
|
|
18265
|
-
`Project scope avoids writing the API key. Export ${ENV_VAR_NAME3}=<your key> in your shell, or switch to local scope to drop it into .qwen/.env.`
|
|
18266
|
-
);
|
|
18267
|
-
} else {
|
|
18268
|
-
const envPath = qwenEnvPath(scope);
|
|
18269
|
-
mergeEnvFile(envPath, { [ENV_VAR_NAME3]: apiKey });
|
|
18270
|
-
notes.push(`API key written to ${envPath} (chmod 600).`);
|
|
18271
|
-
}
|
|
18272
|
-
if (scope === "local") {
|
|
18273
|
-
if (ensureGitignored(".qwen/")) {
|
|
18274
|
-
notes.push("Added `.qwen/` to .gitignore.");
|
|
18194
|
+
if (scope !== "user" && project) {
|
|
18195
|
+
const slug = safe(project);
|
|
18196
|
+
if (slug) {
|
|
18197
|
+
headers["x-project"] = slug;
|
|
18275
18198
|
}
|
|
18276
18199
|
}
|
|
18277
|
-
return
|
|
18278
|
-
tool: "qwen-code",
|
|
18279
|
-
status: "configured",
|
|
18280
|
-
path,
|
|
18281
|
-
scope,
|
|
18282
|
-
notes: notes.length > 0 ? notes.join(" ") : void 0
|
|
18283
|
-
};
|
|
18284
|
-
};
|
|
18285
|
-
function qwenSettingsPath(scope) {
|
|
18286
|
-
return join8(qwenRootForScope(scope), "settings.json");
|
|
18287
|
-
}
|
|
18288
|
-
function qwenEnvPath(scope) {
|
|
18289
|
-
return join8(qwenRootForScope(scope), ".env");
|
|
18290
|
-
}
|
|
18291
|
-
function qwenRootForScope(scope) {
|
|
18292
|
-
switch (scope) {
|
|
18293
|
-
case "user":
|
|
18294
|
-
return join8(homedir5(), ".qwen");
|
|
18295
|
-
case "project":
|
|
18296
|
-
case "local":
|
|
18297
|
-
return join8(userCwd(), ".qwen");
|
|
18298
|
-
}
|
|
18200
|
+
return headers;
|
|
18299
18201
|
}
|
|
18300
|
-
function
|
|
18202
|
+
function trimRightSlash4(url2) {
|
|
18301
18203
|
return url2.endsWith("/") ? url2.slice(0, -1) : url2;
|
|
18302
18204
|
}
|
|
18303
18205
|
|
|
@@ -18305,10 +18207,9 @@ function trimRightSlash5(url2) {
|
|
|
18305
18207
|
var WRITERS = {
|
|
18306
18208
|
"claude-code": writeClaudeCodeConfig,
|
|
18307
18209
|
opencode: writeOpencodeConfig,
|
|
18308
|
-
"qwen-code": writeQwenCodeConfig,
|
|
18309
18210
|
codex: writeCodexConfig
|
|
18310
18211
|
};
|
|
18311
|
-
var ALL_TOOLS = ["claude-code", "opencode", "
|
|
18212
|
+
var ALL_TOOLS = ["claude-code", "opencode", "codex"];
|
|
18312
18213
|
var SCOPES = ["local", "project", "user"];
|
|
18313
18214
|
var configureCommand = defineCommand({
|
|
18314
18215
|
meta: {
|
|
@@ -18319,7 +18220,7 @@ var configureCommand = defineCommand({
|
|
|
18319
18220
|
tool: {
|
|
18320
18221
|
type: "positional",
|
|
18321
18222
|
required: false,
|
|
18322
|
-
description: "Tool to configure. Prompted if omitted. One of: claude-code, opencode,
|
|
18223
|
+
description: "Tool to configure. Prompted if omitted. One of: claude-code, opencode, codex, all."
|
|
18323
18224
|
},
|
|
18324
18225
|
scope: {
|
|
18325
18226
|
type: "string",
|
|
@@ -18428,9 +18329,13 @@ async function resolveTools(args) {
|
|
|
18428
18329
|
`Unsupported tool "${args.tool}". Known: ${ALL_TOOLS.join(", ")}, or pass --all.`
|
|
18429
18330
|
);
|
|
18430
18331
|
}
|
|
18332
|
+
Se(
|
|
18333
|
+
"Use arrow keys to move, space to toggle, a to toggle all, enter to confirm.",
|
|
18334
|
+
"Controls"
|
|
18335
|
+
);
|
|
18431
18336
|
const picked = unwrap(
|
|
18432
18337
|
await ve({
|
|
18433
|
-
message: "Which tools do you want to configure?",
|
|
18338
|
+
message: "Which tools do you want to configure? (space to select/deselect, enter to confirm)",
|
|
18434
18339
|
options: ALL_TOOLS.map((t) => ({ value: t, label: t })),
|
|
18435
18340
|
initialValues: ["claude-code"],
|
|
18436
18341
|
required: true
|
|
@@ -18447,7 +18352,7 @@ async function resolveScope(raw, tools) {
|
|
|
18447
18352
|
}
|
|
18448
18353
|
return unwrap(
|
|
18449
18354
|
await Ee({
|
|
18450
|
-
message: "Where should these settings be written?",
|
|
18355
|
+
message: "Where should these settings be written? (arrow keys to move, enter to select)",
|
|
18451
18356
|
options: [
|
|
18452
18357
|
{
|
|
18453
18358
|
value: "local",
|
|
@@ -18477,14 +18382,12 @@ function pathHint(tools, scope) {
|
|
|
18477
18382
|
return relativizeHomeAndCwd(claudeSettingsPath(scope));
|
|
18478
18383
|
case "opencode":
|
|
18479
18384
|
return relativizeHomeAndCwd(opencodeSettingsPath(scope));
|
|
18480
|
-
case "qwen-code":
|
|
18481
|
-
return relativizeHomeAndCwd(qwenSettingsPath(scope));
|
|
18482
18385
|
case "codex":
|
|
18483
18386
|
return scope === "user" ? relativizeHomeAndCwd(codexConfigPath()) : `${relativizeHomeAndCwd(codexConfigPath())} (codex is user-scope only)`;
|
|
18484
18387
|
}
|
|
18485
18388
|
}
|
|
18486
18389
|
function relativizeHomeAndCwd(absolutePath) {
|
|
18487
|
-
const home =
|
|
18390
|
+
const home = homedir5();
|
|
18488
18391
|
const cwd = userCwd();
|
|
18489
18392
|
if (home && absolutePath.startsWith(`${home}/`)) {
|
|
18490
18393
|
return `~${absolutePath.slice(home.length)}`;
|
|
@@ -18545,7 +18448,7 @@ async function pickPinnedModel(models) {
|
|
|
18545
18448
|
const SKIP = "__skip__";
|
|
18546
18449
|
const picked = unwrap(
|
|
18547
18450
|
await Ee({
|
|
18548
|
-
message: "Pin a default model for these tools?",
|
|
18451
|
+
message: "Pin a default model for these tools? (arrow keys to move, enter to select)",
|
|
18549
18452
|
options: [
|
|
18550
18453
|
{
|
|
18551
18454
|
value: SKIP,
|
|
@@ -18568,7 +18471,7 @@ async function pickPinnedModel(models) {
|
|
|
18568
18471
|
}
|
|
18569
18472
|
|
|
18570
18473
|
// src/commands/doctor.ts
|
|
18571
|
-
import { existsSync as
|
|
18474
|
+
import { existsSync as existsSync6, readFileSync as readFileSync6, statSync as statSync4 } from "fs";
|
|
18572
18475
|
var CLAUDE_SCOPE_ORDER = ["local", "project", "user"];
|
|
18573
18476
|
var doctorCommand = defineCommand({
|
|
18574
18477
|
meta: {
|
|
@@ -18617,7 +18520,7 @@ var doctorCommand = defineCommand({
|
|
|
18617
18520
|
checks.push({ level: "fail", label: "gateway /me", detail: message });
|
|
18618
18521
|
}
|
|
18619
18522
|
checks.push(inspectClaudeSettings());
|
|
18620
|
-
if (!
|
|
18523
|
+
if (!existsSync6(ACCEPTANCE_HOOK_FILE)) {
|
|
18621
18524
|
checks.push({
|
|
18622
18525
|
level: "warn",
|
|
18623
18526
|
label: "acceptance hook",
|
|
@@ -18642,9 +18545,9 @@ var doctorCommand = defineCommand({
|
|
|
18642
18545
|
function inspectClaudeSettings() {
|
|
18643
18546
|
for (const scope of CLAUDE_SCOPE_ORDER) {
|
|
18644
18547
|
const path = claudeSettingsPath(scope);
|
|
18645
|
-
if (!
|
|
18548
|
+
if (!existsSync6(path)) continue;
|
|
18646
18549
|
try {
|
|
18647
|
-
const raw = JSON.parse(
|
|
18550
|
+
const raw = JSON.parse(readFileSync6(path, "utf8"));
|
|
18648
18551
|
if (typeof raw.env?.ANTHROPIC_BASE_URL === "string") {
|
|
18649
18552
|
return {
|
|
18650
18553
|
level: "ok",
|
|
@@ -18682,9 +18585,9 @@ function emit(checks) {
|
|
|
18682
18585
|
}
|
|
18683
18586
|
|
|
18684
18587
|
// src/commands/init.ts
|
|
18685
|
-
import { existsSync as
|
|
18588
|
+
import { existsSync as existsSync7, writeFileSync as writeFileSync6 } from "fs";
|
|
18686
18589
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
18687
|
-
import { join as
|
|
18590
|
+
import { join as join8 } from "path";
|
|
18688
18591
|
var DEFAULT_TICKET_PATTERN2 = "[A-Z]+-\\d+";
|
|
18689
18592
|
var initCommand = defineCommand({
|
|
18690
18593
|
meta: {
|
|
@@ -18707,8 +18610,8 @@ var initCommand = defineCommand({
|
|
|
18707
18610
|
},
|
|
18708
18611
|
run({ args }) {
|
|
18709
18612
|
const cwd = userCwd();
|
|
18710
|
-
const target =
|
|
18711
|
-
if (
|
|
18613
|
+
const target = join8(cwd, ".codevector.json");
|
|
18614
|
+
if (existsSync7(target) && !args.force) {
|
|
18712
18615
|
throw new Error(`.codevector.json already exists in ${cwd}. Pass --force to overwrite.`);
|
|
18713
18616
|
}
|
|
18714
18617
|
const projectName = args.project ?? deriveProjectName(cwd);
|
|
@@ -18718,7 +18621,7 @@ var initCommand = defineCommand({
|
|
|
18718
18621
|
);
|
|
18719
18622
|
}
|
|
18720
18623
|
const ticketPattern = args["ticket-pattern"] ?? DEFAULT_TICKET_PATTERN2;
|
|
18721
|
-
|
|
18624
|
+
writeFileSync6(target, `${JSON.stringify({ projectName, ticketPattern }, null, 2)}
|
|
18722
18625
|
`, {
|
|
18723
18626
|
mode: 420
|
|
18724
18627
|
});
|
|
@@ -18833,7 +18736,7 @@ async function resolveTarget(args) {
|
|
|
18833
18736
|
}
|
|
18834
18737
|
const picked = unwrap(
|
|
18835
18738
|
await Ee({
|
|
18836
|
-
message: "Which opencode.json should be refreshed?",
|
|
18739
|
+
message: "Which opencode.json should be refreshed? (arrow keys to move, enter to select)",
|
|
18837
18740
|
options: [
|
|
18838
18741
|
{ value: "local", label: "local", hint: "./opencode.json (this repo)" },
|
|
18839
18742
|
{ value: "project", label: "project", hint: "./opencode.json (this repo, committed)" },
|
|
@@ -19059,7 +18962,7 @@ function buildQuery(args) {
|
|
|
19059
18962
|
// package.json
|
|
19060
18963
|
var package_default = {
|
|
19061
18964
|
name: "@codevector/cli",
|
|
19062
|
-
version: "0.3.
|
|
18965
|
+
version: "0.3.4",
|
|
19063
18966
|
description: "CodeVector CLI \u2014 installs and configures first-party coding-tool integrations.",
|
|
19064
18967
|
license: "UNLICENSED",
|
|
19065
18968
|
bin: {
|