@codevector/cli 0.3.4 → 0.4.0

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/dist/index.js CHANGED
@@ -37,14 +37,14 @@ var require_src = __commonJS({
37
37
  var CSI2 = `${ESC2}[`;
38
38
  var beep = "\x07";
39
39
  var cursor = {
40
- to(x, y) {
41
- if (!y) return `${CSI2}${x + 1}G`;
42
- return `${CSI2}${y + 1};${x + 1}H`;
40
+ to(x3, y) {
41
+ if (!y) return `${CSI2}${x3 + 1}G`;
42
+ return `${CSI2}${y + 1};${x3 + 1}H`;
43
43
  },
44
- move(x, y) {
44
+ move(x3, y) {
45
45
  let ret = "";
46
- if (x < 0) ret += `${CSI2}${-x}D`;
47
- else if (x > 0) ret += `${CSI2}${x}C`;
46
+ if (x3 < 0) ret += `${CSI2}${-x3}D`;
47
+ else if (x3 > 0) ret += `${CSI2}${x3}C`;
48
48
  if (y < 0) ret += `${CSI2}${-y}A`;
49
49
  else if (y > 0) ret += `${CSI2}${y}B`;
50
50
  return ret;
@@ -163,7 +163,7 @@ function toArray(val) {
163
163
  function formatLineColumns(lines, linePrefix = "") {
164
164
  const maxLength = [];
165
165
  for (const line of lines) for (const [i, element] of line.entries()) maxLength[i] = Math.max(maxLength[i] || 0, element.length);
166
- return lines.map((l) => l.map((c2, i) => linePrefix + c2[i === 0 ? "padStart" : "padEnd"](maxLength[i])).join(" ")).join("\n");
166
+ return lines.map((l) => l.map((c, i) => linePrefix + c[i === 0 ? "padStart" : "padEnd"](maxLength[i])).join(" ")).join("\n");
167
167
  }
168
168
  function resolveValue(input) {
169
169
  return typeof input === "function" ? input() : input;
@@ -274,7 +274,7 @@ var noColor = /* @__PURE__ */ (() => {
274
274
  const env = globalThis.process?.env ?? {};
275
275
  return env.NO_COLOR === "1" || env.TERM === "dumb" || env.TEST || env.CI;
276
276
  })();
277
- var _c = (c2, r = 39) => (t) => noColor ? t : `\x1B[${c2}m${t}\x1B[${r}m`;
277
+ var _c = (c, r = 39) => (t) => noColor ? t : `\x1B[${c}m${t}\x1B[${r}m`;
278
278
  var bold = /* @__PURE__ */ _c(1, 22);
279
279
  var cyan = /* @__PURE__ */ _c(36);
280
280
  var gray = /* @__PURE__ */ _c(90);
@@ -552,9 +552,9 @@ function _getBuiltinFlags(long, short, userNames, userAliases) {
552
552
  return [`--${long}`, `-${short}`];
553
553
  }
554
554
 
555
- // ../../node_modules/.pnpm/@clack+core@1.3.0/node_modules/@clack/core/dist/index.mjs
555
+ // ../../node_modules/.pnpm/@clack+core@1.3.1/node_modules/@clack/core/dist/index.mjs
556
556
  import { styleText as v } from "util";
557
- import { stdout as S, stdin as D } from "process";
557
+ import { stdout as x, stdin as D } from "process";
558
558
  import * as b from "readline";
559
559
  import E from "readline";
560
560
 
@@ -570,11 +570,11 @@ var getCodePointsLength = /* @__PURE__ */ (() => {
570
570
  return input.length - surrogatePairsNr;
571
571
  };
572
572
  })();
573
- var isFullWidth = (x) => {
574
- return x === 12288 || x >= 65281 && x <= 65376 || x >= 65504 && x <= 65510;
573
+ var isFullWidth = (x3) => {
574
+ return x3 === 12288 || x3 >= 65281 && x3 <= 65376 || x3 >= 65504 && x3 <= 65510;
575
575
  };
576
- var isWideNotCJKTNotEmoji = (x) => {
577
- return x === 8987 || x === 9001 || x >= 12272 && x <= 12287 || x >= 12289 && x <= 12350 || x >= 12441 && x <= 12543 || x >= 12549 && x <= 12591 || x >= 12593 && x <= 12686 || x >= 12688 && x <= 12771 || x >= 12783 && x <= 12830 || x >= 12832 && x <= 12871 || x >= 12880 && x <= 19903 || x >= 65040 && x <= 65049 || x >= 65072 && x <= 65106 || x >= 65108 && x <= 65126 || x >= 65128 && x <= 65131 || x >= 127488 && x <= 127490 || x >= 127504 && x <= 127547 || x >= 127552 && x <= 127560 || x >= 131072 && x <= 196605 || x >= 196608 && x <= 262141;
576
+ var isWideNotCJKTNotEmoji = (x3) => {
577
+ return x3 === 8987 || x3 === 9001 || x3 >= 12272 && x3 <= 12287 || x3 >= 12289 && x3 <= 12350 || x3 >= 12441 && x3 <= 12543 || x3 >= 12549 && x3 <= 12591 || x3 >= 12593 && x3 <= 12686 || x3 >= 12688 && x3 <= 12771 || x3 >= 12783 && x3 <= 12830 || x3 >= 12832 && x3 <= 12871 || x3 >= 12880 && x3 <= 19903 || x3 >= 65040 && x3 <= 65049 || x3 >= 65072 && x3 <= 65106 || x3 >= 65108 && x3 <= 65126 || x3 >= 65128 && x3 <= 65131 || x3 >= 127488 && x3 <= 127490 || x3 >= 127504 && x3 <= 127547 || x3 >= 127552 && x3 <= 127560 || x3 >= 131072 && x3 <= 196605 || x3 >= 196608 && x3 <= 262141;
578
578
  };
579
579
 
580
580
  // ../../node_modules/.pnpm/fast-string-truncated-width@3.0.3/node_modules/fast-string-truncated-width/dist/index.js
@@ -894,13 +894,13 @@ function wrapAnsi(string4, columns, options) {
894
894
  return String(string4).normalize().split(CRLF_OR_LF).map((line) => exec(line, columns, options)).join("\n");
895
895
  }
896
896
 
897
- // ../../node_modules/.pnpm/@clack+core@1.3.0/node_modules/@clack/core/dist/index.mjs
897
+ // ../../node_modules/.pnpm/@clack+core@1.3.1/node_modules/@clack/core/dist/index.mjs
898
898
  var import_sisteransi = __toESM(require_src(), 1);
899
899
  import { ReadStream as O } from "tty";
900
- function d(r, t, s) {
900
+ function f(r, t, s) {
901
901
  if (!s.some((o) => !o.disabled)) return r;
902
902
  const e2 = r + t, i = Math.max(s.length - 1, 0), n = e2 < 0 ? i : e2 > i ? 0 : e2;
903
- return s[n].disabled ? d(n, t < 0 ? -1 : 1, s) : n;
903
+ return s[n].disabled ? f(n, t < 0 ? -1 : 1, s) : n;
904
904
  }
905
905
  var G = ["up", "down", "left", "right", "space", "enter", "cancel"];
906
906
  var K = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
@@ -927,7 +927,7 @@ function w(r, t) {
927
927
  const s = r;
928
928
  s.isTTY && s.setRawMode(t);
929
929
  }
930
- function R({ input: r = D, output: t = S, overwrite: s = true, hideCursor: e2 = true } = {}) {
930
+ function R({ input: r = D, output: t = x, overwrite: s = true, hideCursor: e2 = true } = {}) {
931
931
  const i = b.createInterface({ input: r, output: t, prompt: "", tabSize: 1 });
932
932
  b.emitKeypressEvents(r, i), r instanceof O && r.isTTY && r.setRawMode(true);
933
933
  const n = (o, { name: u, sequence: a }) => {
@@ -937,8 +937,8 @@ function R({ input: r = D, output: t = S, overwrite: s = true, hideCursor: e2 =
937
937
  return;
938
938
  }
939
939
  if (!s) return;
940
- const f = u === "return" ? 0 : -1, y = u === "return" ? -1 : 0;
941
- b.moveCursor(t, f, y, () => {
940
+ const c = u === "return" ? 0 : -1, y = u === "return" ? -1 : 0;
941
+ b.moveCursor(t, c, y, () => {
942
942
  b.clearLine(t, 1, () => {
943
943
  r.once("keypress", n);
944
944
  });
@@ -950,16 +950,16 @@ function R({ input: r = D, output: t = S, overwrite: s = true, hideCursor: e2 =
950
950
  }
951
951
  var A = (r) => "columns" in r && typeof r.columns == "number" ? r.columns : 80;
952
952
  var L = (r) => "rows" in r && typeof r.rows == "number" ? r.rows : 20;
953
- function W(r, t, s, e2 = s, i) {
954
- const n = A(r ?? S);
955
- return wrapAnsi(t, n - s.length, { hard: true, trim: false }).split(`
956
- `).map((o, u) => {
957
- const a = i ? i(o, u) : o;
958
- return `${u === 0 ? e2 : s}${a}`;
953
+ function W(r, t, s, e2 = s, i = s, n) {
954
+ const o = A(r ?? x);
955
+ return wrapAnsi(t, o - s.length, { hard: true, trim: false }).split(`
956
+ `).map((u, a, l) => {
957
+ const c = n ? n(u, a) : u;
958
+ return a === 0 ? `${e2}${c}` : a === l.length - 1 ? `${i}${c}` : `${s}${c}`;
959
959
  }).join(`
960
960
  `);
961
961
  }
962
- var p = class {
962
+ var m = class {
963
963
  input;
964
964
  output;
965
965
  _abortSignal;
@@ -975,7 +975,7 @@ var p = class {
975
975
  value;
976
976
  userInput = "";
977
977
  constructor(t, s = true) {
978
- const { input: e2 = D, output: i = S, render: n, signal: o, ...u } = t;
978
+ const { input: e2 = D, output: i = x, render: n, signal: o, ...u } = t;
979
979
  this.opts = u, this.onKeypress = this.onKeypress.bind(this), this.close = this.close.bind(this), this.render = this.render.bind(this), this._render = n.bind(this), this._track = s, this._abortSignal = o, this.input = e2, this.output = i;
980
980
  }
981
981
  unsubscribe() {
@@ -1084,7 +1084,24 @@ var p = class {
1084
1084
  }
1085
1085
  }
1086
1086
  };
1087
- var nt = class extends p {
1087
+ var X = class extends m {
1088
+ get cursor() {
1089
+ return this.value ? 0 : 1;
1090
+ }
1091
+ get _value() {
1092
+ return this.cursor === 0;
1093
+ }
1094
+ constructor(t) {
1095
+ super(t, false), this.value = !!t.initialValue, this.on("userInput", () => {
1096
+ this.value = this._value;
1097
+ }), this.on("confirm", (s) => {
1098
+ this.output.write(import_sisteransi.cursor.move(0, -1)), this.value = s, this.state = "submit", this.close();
1099
+ }), this.on("cursor", () => {
1100
+ this.value = !this.value;
1101
+ });
1102
+ }
1103
+ };
1104
+ var nt = class extends m {
1088
1105
  options;
1089
1106
  cursor = 0;
1090
1107
  get _value() {
@@ -1111,17 +1128,17 @@ var nt = class extends p {
1111
1128
  constructor(t) {
1112
1129
  super(t, false), this.options = t.options, this.value = [...t.initialValues ?? []];
1113
1130
  const s = Math.max(this.options.findIndex(({ value: e2 }) => e2 === t.cursorAt), 0);
1114
- this.cursor = this.options[s].disabled ? d(s, 1, this.options) : s, this.on("key", (e2) => {
1131
+ this.cursor = this.options[s].disabled ? f(s, 1, this.options) : s, this.on("key", (e2) => {
1115
1132
  e2 === "a" && this.toggleAll(), e2 === "i" && this.toggleInvert();
1116
1133
  }), this.on("cursor", (e2) => {
1117
1134
  switch (e2) {
1118
1135
  case "left":
1119
1136
  case "up":
1120
- this.cursor = d(this.cursor, -1, this.options);
1137
+ this.cursor = f(this.cursor, -1, this.options);
1121
1138
  break;
1122
1139
  case "down":
1123
1140
  case "right":
1124
- this.cursor = d(this.cursor, 1, this.options);
1141
+ this.cursor = f(this.cursor, 1, this.options);
1125
1142
  break;
1126
1143
  case "space":
1127
1144
  this.toggleValue();
@@ -1130,7 +1147,7 @@ var nt = class extends p {
1130
1147
  });
1131
1148
  }
1132
1149
  };
1133
- var ot = class extends p {
1150
+ var ot = class extends m {
1134
1151
  _mask = "\u2022";
1135
1152
  get cursor() {
1136
1153
  return this._cursor;
@@ -1154,7 +1171,7 @@ var ot = class extends p {
1154
1171
  });
1155
1172
  }
1156
1173
  };
1157
- var ut = class extends p {
1174
+ var ut = class extends m {
1158
1175
  options;
1159
1176
  cursor = 0;
1160
1177
  get _selectedValue() {
@@ -1166,22 +1183,22 @@ var ut = class extends p {
1166
1183
  constructor(t) {
1167
1184
  super(t, false), this.options = t.options;
1168
1185
  const s = this.options.findIndex(({ value: i }) => i === t.initialValue), e2 = s === -1 ? 0 : s;
1169
- this.cursor = this.options[e2].disabled ? d(e2, 1, this.options) : e2, this.changeValue(), this.on("cursor", (i) => {
1186
+ this.cursor = this.options[e2].disabled ? f(e2, 1, this.options) : e2, this.changeValue(), this.on("cursor", (i) => {
1170
1187
  switch (i) {
1171
1188
  case "left":
1172
1189
  case "up":
1173
- this.cursor = d(this.cursor, -1, this.options);
1190
+ this.cursor = f(this.cursor, -1, this.options);
1174
1191
  break;
1175
1192
  case "down":
1176
1193
  case "right":
1177
- this.cursor = d(this.cursor, 1, this.options);
1194
+ this.cursor = f(this.cursor, 1, this.options);
1178
1195
  break;
1179
1196
  }
1180
1197
  this.changeValue();
1181
1198
  });
1182
1199
  }
1183
1200
  };
1184
- var ht = class extends p {
1201
+ var ht = class extends m {
1185
1202
  get userInputWithCursor() {
1186
1203
  if (this.state === "submit") return this.userInput;
1187
1204
  const t = this.userInput;
@@ -1201,54 +1218,54 @@ var ht = class extends p {
1201
1218
  }
1202
1219
  };
1203
1220
 
1204
- // ../../node_modules/.pnpm/@clack+prompts@1.3.0/node_modules/@clack/prompts/dist/index.mjs
1221
+ // ../../node_modules/.pnpm/@clack+prompts@1.4.0/node_modules/@clack/prompts/dist/index.mjs
1205
1222
  import { styleText as e, stripVTControlCharacters as nt2 } from "util";
1206
- import j2 from "process";
1223
+ import V2 from "process";
1207
1224
  var import_sisteransi2 = __toESM(require_src(), 1);
1208
- import { existsSync as zt, lstatSync as wt, readdirSync as Qt } from "fs";
1209
- import { dirname as bt, join as Zt } from "path";
1210
- function te() {
1211
- return j2.platform !== "win32" ? j2.env.TERM !== "linux" : !!j2.env.CI || !!j2.env.WT_SESSION || !!j2.env.TERMINUS_SUBLIME || j2.env.ConEmuTask === "{cmd::Cmder}" || j2.env.TERM_PROGRAM === "Terminus-Sublime" || j2.env.TERM_PROGRAM === "vscode" || j2.env.TERM === "xterm-256color" || j2.env.TERM === "alacritty" || j2.env.TERMINAL_EMULATOR === "JetBrains-JediTerm";
1212
- }
1213
- var tt = te();
1214
- var at2 = () => process.env.CI === "true";
1215
- var w2 = (t, r) => tt ? t : r;
1216
- var _t = w2("\u25C6", "*");
1217
- var ot2 = w2("\u25A0", "x");
1225
+ import { existsSync as Qt, lstatSync as wt, readdirSync as Zt } from "fs";
1226
+ import { dirname as bt, join as te } from "path";
1227
+ function ee() {
1228
+ return V2.platform !== "win32" ? V2.env.TERM !== "linux" : !!V2.env.CI || !!V2.env.WT_SESSION || !!V2.env.TERMINUS_SUBLIME || V2.env.ConEmuTask === "{cmd::Cmder}" || V2.env.TERM_PROGRAM === "Terminus-Sublime" || V2.env.TERM_PROGRAM === "vscode" || V2.env.TERM === "xterm-256color" || V2.env.TERM === "alacritty" || V2.env.TERMINAL_EMULATOR === "JetBrains-JediTerm";
1229
+ }
1230
+ var tt = ee();
1231
+ var ot2 = () => process.env.CI === "true";
1232
+ var w2 = (t, i) => tt ? t : i;
1233
+ var Tt = w2("\u25C6", "*");
1234
+ var at2 = w2("\u25A0", "x");
1218
1235
  var ut2 = w2("\u25B2", "x");
1219
- var F = w2("\u25C7", "o");
1236
+ var H = w2("\u25C7", "o");
1220
1237
  var lt = w2("\u250C", "T");
1221
1238
  var $ = w2("\u2502", "|");
1222
- var E2 = w2("\u2514", "\u2014");
1223
- var It = w2("\u2510", "T");
1224
- var Et = w2("\u2518", "\u2014");
1239
+ var x2 = w2("\u2514", "\u2014");
1240
+ var _t = w2("\u2510", "T");
1241
+ var xt = w2("\u2518", "\u2014");
1225
1242
  var z2 = w2("\u25CF", ">");
1226
- var H = w2("\u25CB", " ");
1243
+ var U = w2("\u25CB", " ");
1227
1244
  var et2 = w2("\u25FB", "[\u2022]");
1228
- var U = w2("\u25FC", "[+]");
1229
- var J = w2("\u25FB", "[ ]");
1230
- var Gt = w2("\u25AA", "\u2022");
1245
+ var K2 = w2("\u25FC", "[+]");
1246
+ var Y2 = w2("\u25FB", "[ ]");
1247
+ var Et = w2("\u25AA", "\u2022");
1231
1248
  var st = w2("\u2500", "-");
1232
1249
  var ct = w2("\u256E", "+");
1233
- var xt = w2("\u251C", "+");
1250
+ var Gt = w2("\u251C", "+");
1234
1251
  var $t = w2("\u256F", "+");
1235
1252
  var dt = w2("\u2570", "+");
1236
- var Ot = w2("\u256D", "+");
1253
+ var Mt = w2("\u256D", "+");
1237
1254
  var ht2 = w2("\u25CF", "\u2022");
1238
1255
  var pt = w2("\u25C6", "*");
1239
1256
  var mt = w2("\u25B2", "!");
1240
1257
  var gt = w2("\u25A0", "x");
1241
- var M = (t) => {
1258
+ var P = (t) => {
1242
1259
  switch (t) {
1243
1260
  case "initial":
1244
1261
  case "active":
1245
- return e("cyan", _t);
1262
+ return e("cyan", Tt);
1246
1263
  case "cancel":
1247
- return e("red", ot2);
1264
+ return e("red", at2);
1248
1265
  case "error":
1249
1266
  return e("yellow", ut2);
1250
1267
  case "submit":
1251
- return e("green", F);
1268
+ return e("green", H);
1252
1269
  }
1253
1270
  };
1254
1271
  var yt = (t) => {
@@ -1264,247 +1281,274 @@ var yt = (t) => {
1264
1281
  return e("green", $);
1265
1282
  }
1266
1283
  };
1267
- var ee = (t, r, s, i, u) => {
1268
- let n = r, o = 0;
1269
- for (let c2 = s; c2 < i; c2++) {
1270
- const a = t[c2];
1271
- if (n = n - a.length, o++, n <= u) break;
1272
- }
1273
- return { lineCount: n, removals: o };
1284
+ var Ot = (t, i, s, r, u, n = false) => {
1285
+ let a = i, c = 0;
1286
+ if (n) for (let o = r - 1; o >= s && (a -= t[o].length, c++, !(a <= u)); o--) ;
1287
+ else for (let o = s; o < r && (a -= t[o].length, c++, !(a <= u)); o++) ;
1288
+ return { lineCount: a, removals: c };
1274
1289
  };
1275
- var Y2 = ({ cursor: t, options: r, style: s, output: i = process.stdout, maxItems: u = Number.POSITIVE_INFINITY, columnPadding: n = 0, rowPadding: o = 4 }) => {
1276
- const c2 = A(i) - n, a = L(i), l = e("dim", "..."), d2 = Math.max(a - o, 0), y = Math.max(Math.min(u, d2), 5);
1290
+ var F = ({ cursor: t, options: i, style: s, output: r = process.stdout, maxItems: u = Number.POSITIVE_INFINITY, columnPadding: n = 0, rowPadding: a = 4 }) => {
1291
+ const c = A(r) - n, o = L(r), l = e("dim", "..."), d = Math.max(o - a, 0), g = Math.max(Math.min(u, d), 5);
1277
1292
  let p2 = 0;
1278
- t >= y - 3 && (p2 = Math.max(Math.min(t - y + 3, r.length - y), 0));
1279
- let m = y < r.length && p2 > 0, g = y < r.length && p2 + y < r.length;
1280
- const S2 = Math.min(p2 + y, r.length), h2 = [];
1281
- let f = 0;
1282
- m && f++, g && f++;
1283
- const v2 = p2 + (m ? 1 : 0), T = S2 - (g ? 1 : 0);
1284
- for (let b2 = v2; b2 < T; b2++) {
1285
- const G2 = wrapAnsi(s(r[b2], b2 === t), c2, { hard: true, trim: false }).split(`
1293
+ t >= g - 3 && (p2 = Math.max(Math.min(t - g + 3, i.length - g), 0));
1294
+ let f2 = g < i.length && p2 > 0, h2 = g < i.length && p2 + g < i.length;
1295
+ const I = Math.min(p2 + g, i.length), m2 = [];
1296
+ let y = 0;
1297
+ f2 && y++, h2 && y++;
1298
+ const v2 = p2 + (f2 ? 1 : 0), C2 = I - (h2 ? 1 : 0);
1299
+ for (let b2 = v2; b2 < C2; b2++) {
1300
+ const G2 = wrapAnsi(s(i[b2], b2 === t), c, { hard: true, trim: false }).split(`
1286
1301
  `);
1287
- h2.push(G2), f += G2.length;
1288
- }
1289
- if (f > d2) {
1290
- let b2 = 0, G2 = 0, x = f;
1291
- const A2 = t - v2, P = (N, D2) => ee(h2, x, N, D2, d2);
1292
- m ? ({ lineCount: x, removals: b2 } = P(0, A2), x > d2 && ({ lineCount: x, removals: G2 } = P(A2 + 1, h2.length))) : ({ lineCount: x, removals: G2 } = P(A2 + 1, h2.length), x > d2 && ({ lineCount: x, removals: b2 } = P(0, A2))), b2 > 0 && (m = true, h2.splice(0, b2)), G2 > 0 && (g = true, h2.splice(h2.length - G2, G2));
1293
- }
1294
- const C2 = [];
1295
- m && C2.push(l);
1296
- for (const b2 of h2) for (const G2 of b2) C2.push(G2);
1297
- return g && C2.push(l), C2;
1302
+ m2.push(G2), y += G2.length;
1303
+ }
1304
+ if (y > d) {
1305
+ let b2 = 0, G2 = 0, M = y;
1306
+ const N = t - v2;
1307
+ let O2 = d;
1308
+ const j2 = () => Ot(m2, M, 0, N, O2), k2 = () => Ot(m2, M, N + 1, m2.length, O2, true);
1309
+ f2 ? ({ lineCount: M, removals: b2 } = j2(), M > O2 && (h2 || (O2 -= 1), { lineCount: M, removals: G2 } = k2())) : (h2 || (O2 -= 1), { lineCount: M, removals: G2 } = k2(), M > O2 && (O2 -= 1, { lineCount: M, removals: b2 } = j2())), b2 > 0 && (f2 = true, m2.splice(0, b2)), G2 > 0 && (h2 = true, m2.splice(m2.length - G2, G2));
1310
+ }
1311
+ const S = [];
1312
+ f2 && S.push(l);
1313
+ for (const b2 of m2) for (const G2 of b2) S.push(G2);
1314
+ return h2 && S.push(l), S;
1315
+ };
1316
+ var ue = (t) => {
1317
+ const i = t.active ?? "Yes", s = t.inactive ?? "No";
1318
+ return new X({ active: i, inactive: s, signal: t.signal, input: t.input, output: t.output, initialValue: t.initialValue ?? true, render() {
1319
+ const r = t.withGuide ?? h.withGuide, u = `${P(this.state)} `, n = r ? `${e("gray", $)} ` : "", a = W(t.output, t.message, n, u), c = `${r ? `${e("gray", $)}
1320
+ ` : ""}${a}
1321
+ `, o = this.value ? i : s;
1322
+ switch (this.state) {
1323
+ case "submit": {
1324
+ const l = r ? `${e("gray", $)} ` : "";
1325
+ return `${c}${l}${e("dim", o)}`;
1326
+ }
1327
+ case "cancel": {
1328
+ const l = r ? `${e("gray", $)} ` : "";
1329
+ return `${c}${l}${e(["strikethrough", "dim"], o)}${r ? `
1330
+ ${e("gray", $)}` : ""}`;
1331
+ }
1332
+ default: {
1333
+ const l = r ? `${e("cyan", $)} ` : "", d = r ? e("cyan", x2) : "";
1334
+ return `${c}${l}${this.value ? `${e("green", z2)} ${i}` : `${e("dim", U)} ${e("dim", i)}`}${t.vertical ? r ? `
1335
+ ${e("cyan", $)} ` : `
1336
+ ` : ` ${e("dim", "/")} `}${this.value ? `${e("dim", U)} ${e("dim", s)}` : `${e("green", z2)} ${s}`}
1337
+ ${d}
1338
+ `;
1339
+ }
1340
+ }
1341
+ } }).prompt();
1298
1342
  };
1299
- var R2 = { message: (t = [], { symbol: r = e("gray", $), secondarySymbol: s = e("gray", $), output: i = process.stdout, spacing: u = 1, withGuide: n } = {}) => {
1300
- const o = [], c2 = n ?? h.withGuide, a = c2 ? s : "", l = c2 ? `${r} ` : "", d2 = c2 ? `${s} ` : "";
1301
- for (let p2 = 0; p2 < u; p2++) o.push(a);
1302
- const y = Array.isArray(t) ? t : t.split(`
1343
+ var R2 = { message: (t = [], { symbol: i = e("gray", $), secondarySymbol: s = e("gray", $), output: r = process.stdout, spacing: u = 1, withGuide: n } = {}) => {
1344
+ const a = [], c = n ?? h.withGuide, o = c ? s : "", l = c ? `${i} ` : "", d = c ? `${s} ` : "";
1345
+ for (let p2 = 0; p2 < u; p2++) a.push(o);
1346
+ const g = Array.isArray(t) ? t : t.split(`
1303
1347
  `);
1304
- if (y.length > 0) {
1305
- const [p2, ...m] = y;
1306
- p2.length > 0 ? o.push(`${l}${p2}`) : o.push(c2 ? r : "");
1307
- for (const g of m) g.length > 0 ? o.push(`${d2}${g}`) : o.push(c2 ? s : "");
1348
+ if (g.length > 0) {
1349
+ const [p2, ...f2] = g;
1350
+ p2.length > 0 ? a.push(`${l}${p2}`) : a.push(c ? i : "");
1351
+ for (const h2 of f2) h2.length > 0 ? a.push(`${d}${h2}`) : a.push(c ? s : "");
1308
1352
  }
1309
- i.write(`${o.join(`
1353
+ r.write(`${a.join(`
1310
1354
  `)}
1311
1355
  `);
1312
- }, info: (t, r) => {
1313
- R2.message(t, { ...r, symbol: e("blue", ht2) });
1314
- }, success: (t, r) => {
1315
- R2.message(t, { ...r, symbol: e("green", pt) });
1316
- }, step: (t, r) => {
1317
- R2.message(t, { ...r, symbol: e("green", F) });
1318
- }, warn: (t, r) => {
1319
- R2.message(t, { ...r, symbol: e("yellow", mt) });
1320
- }, warning: (t, r) => {
1321
- R2.warn(t, r);
1322
- }, error: (t, r) => {
1323
- R2.message(t, { ...r, symbol: e("red", gt) });
1356
+ }, info: (t, i) => {
1357
+ R2.message(t, { ...i, symbol: e("blue", ht2) });
1358
+ }, success: (t, i) => {
1359
+ R2.message(t, { ...i, symbol: e("green", pt) });
1360
+ }, step: (t, i) => {
1361
+ R2.message(t, { ...i, symbol: e("green", H) });
1362
+ }, warn: (t, i) => {
1363
+ R2.message(t, { ...i, symbol: e("yellow", mt) });
1364
+ }, warning: (t, i) => {
1365
+ R2.warn(t, i);
1366
+ }, error: (t, i) => {
1367
+ R2.message(t, { ...i, symbol: e("red", gt) });
1324
1368
  } };
1325
- var me = (t = "", r) => {
1326
- const s = r?.output ?? process.stdout, i = r?.withGuide ?? h.withGuide ? `${e("gray", E2)} ` : "";
1327
- s.write(`${i}${e("red", t)}
1369
+ var me = (t = "", i) => {
1370
+ const s = i?.output ?? process.stdout, r = i?.withGuide ?? h.withGuide ? `${e("gray", x2)} ` : "";
1371
+ s.write(`${r}${e("red", t)}
1328
1372
 
1329
1373
  `);
1330
1374
  };
1331
- var ge = (t = "", r) => {
1332
- const s = r?.output ?? process.stdout, i = r?.withGuide ?? h.withGuide ? `${e("gray", lt)} ` : "";
1333
- s.write(`${i}${t}
1375
+ var ge = (t = "", i) => {
1376
+ const s = i?.output ?? process.stdout, r = i?.withGuide ?? h.withGuide ? `${e("gray", lt)} ` : "";
1377
+ s.write(`${r}${t}
1334
1378
  `);
1335
1379
  };
1336
- var ye = (t = "", r) => {
1337
- const s = r?.output ?? process.stdout, i = r?.withGuide ?? h.withGuide ? `${e("gray", $)}
1338
- ${e("gray", E2)} ` : "";
1339
- s.write(`${i}${t}
1380
+ var ye = (t = "", i) => {
1381
+ const s = i?.output ?? process.stdout, r = i?.withGuide ?? h.withGuide ? `${e("gray", $)}
1382
+ ${e("gray", x2)} ` : "";
1383
+ s.write(`${r}${t}
1340
1384
 
1341
1385
  `);
1342
1386
  };
1343
- var Q2 = (t, r) => t.split(`
1344
- `).map((s) => r(s)).join(`
1387
+ var Q2 = (t, i) => t.split(`
1388
+ `).map((s) => i(s)).join(`
1345
1389
  `);
1346
1390
  var ve = (t) => {
1347
- const r = (i, u) => {
1348
- const n = i.label ?? String(i.value);
1349
- return u === "disabled" ? `${e("gray", J)} ${Q2(n, (o) => e(["strikethrough", "gray"], o))}${i.hint ? ` ${e("dim", `(${i.hint ?? "disabled"})`)}` : ""}` : u === "active" ? `${e("cyan", et2)} ${n}${i.hint ? ` ${e("dim", `(${i.hint})`)}` : ""}` : u === "selected" ? `${e("green", U)} ${Q2(n, (o) => e("dim", o))}${i.hint ? ` ${e("dim", `(${i.hint})`)}` : ""}` : u === "cancelled" ? `${Q2(n, (o) => e(["strikethrough", "dim"], o))}` : u === "active-selected" ? `${e("green", U)} ${n}${i.hint ? ` ${e("dim", `(${i.hint})`)}` : ""}` : u === "submitted" ? `${Q2(n, (o) => e("dim", o))}` : `${e("dim", J)} ${Q2(n, (o) => e("dim", o))}`;
1391
+ const i = (r, u) => {
1392
+ const n = r.label ?? String(r.value);
1393
+ return u === "disabled" ? `${e("gray", Y2)} ${Q2(n, (a) => e(["strikethrough", "gray"], a))}${r.hint ? ` ${e("dim", `(${r.hint ?? "disabled"})`)}` : ""}` : u === "active" ? `${e("cyan", et2)} ${n}${r.hint ? ` ${e("dim", `(${r.hint})`)}` : ""}` : u === "selected" ? `${e("green", K2)} ${Q2(n, (a) => e("dim", a))}${r.hint ? ` ${e("dim", `(${r.hint})`)}` : ""}` : u === "cancelled" ? `${Q2(n, (a) => e(["strikethrough", "dim"], a))}` : u === "active-selected" ? `${e("green", K2)} ${n}${r.hint ? ` ${e("dim", `(${r.hint})`)}` : ""}` : u === "submitted" ? `${Q2(n, (a) => e("dim", a))}` : `${e("dim", Y2)} ${Q2(n, (a) => e("dim", a))}`;
1350
1394
  }, s = t.required ?? true;
1351
- return new nt({ options: t.options, signal: t.signal, input: t.input, output: t.output, initialValues: t.initialValues, required: s, cursorAt: t.cursorAt, validate(i) {
1352
- if (s && (i === void 0 || i.length === 0)) return `Please select at least one option.
1395
+ return new nt({ options: t.options, signal: t.signal, input: t.input, output: t.output, initialValues: t.initialValues, required: s, cursorAt: t.cursorAt, validate(r) {
1396
+ if (s && (r === void 0 || r.length === 0)) return `Please select at least one option.
1353
1397
  ${e("reset", e("dim", `Press ${e(["gray", "bgWhite", "inverse"], " space ")} to select, ${e("gray", e("bgWhite", e("inverse", " enter ")))} to submit`))}`;
1354
1398
  }, render() {
1355
- const i = t.withGuide ?? h.withGuide, u = W(t.output, t.message, i ? `${yt(this.state)} ` : "", `${M(this.state)} `), n = `${i ? `${e("gray", $)}
1399
+ const r = t.withGuide ?? h.withGuide, u = W(t.output, t.message, r ? `${yt(this.state)} ` : "", `${P(this.state)} `), n = `${r ? `${e("gray", $)}
1356
1400
  ` : ""}${u}
1357
- `, o = this.value ?? [], c2 = (a, l) => {
1358
- if (a.disabled) return r(a, "disabled");
1359
- const d2 = o.includes(a.value);
1360
- return l && d2 ? r(a, "active-selected") : d2 ? r(a, "selected") : r(a, l ? "active" : "inactive");
1401
+ `, a = this.value ?? [], c = (o, l) => {
1402
+ if (o.disabled) return i(o, "disabled");
1403
+ const d = a.includes(o.value);
1404
+ return l && d ? i(o, "active-selected") : d ? i(o, "selected") : i(o, l ? "active" : "inactive");
1361
1405
  };
1362
1406
  switch (this.state) {
1363
1407
  case "submit": {
1364
- const a = this.options.filter(({ value: d2 }) => o.includes(d2)).map((d2) => r(d2, "submitted")).join(e("dim", ", ")) || e("dim", "none"), l = W(t.output, a, i ? `${e("gray", $)} ` : "");
1408
+ const o = this.options.filter(({ value: d }) => a.includes(d)).map((d) => i(d, "submitted")).join(e("dim", ", ")) || e("dim", "none"), l = W(t.output, o, r ? `${e("gray", $)} ` : "");
1365
1409
  return `${n}${l}`;
1366
1410
  }
1367
1411
  case "cancel": {
1368
- const a = this.options.filter(({ value: d2 }) => o.includes(d2)).map((d2) => r(d2, "cancelled")).join(e("dim", ", "));
1369
- if (a.trim() === "") return `${n}${e("gray", $)}`;
1370
- const l = W(t.output, a, i ? `${e("gray", $)} ` : "");
1371
- return `${n}${l}${i ? `
1412
+ const o = this.options.filter(({ value: d }) => a.includes(d)).map((d) => i(d, "cancelled")).join(e("dim", ", "));
1413
+ if (o.trim() === "") return `${n}${e("gray", $)}`;
1414
+ const l = W(t.output, o, r ? `${e("gray", $)} ` : "");
1415
+ return `${n}${l}${r ? `
1372
1416
  ${e("gray", $)}` : ""}`;
1373
1417
  }
1374
1418
  case "error": {
1375
- const a = i ? `${e("yellow", $)} ` : "", l = this.error.split(`
1376
- `).map((p2, m) => m === 0 ? `${i ? `${e("yellow", E2)} ` : ""}${e("yellow", p2)}` : ` ${p2}`).join(`
1377
- `), d2 = n.split(`
1378
- `).length, y = l.split(`
1419
+ const o = r ? `${e("yellow", $)} ` : "", l = this.error.split(`
1420
+ `).map((p2, f2) => f2 === 0 ? `${r ? `${e("yellow", x2)} ` : ""}${e("yellow", p2)}` : ` ${p2}`).join(`
1421
+ `), d = n.split(`
1422
+ `).length, g = l.split(`
1379
1423
  `).length + 1;
1380
- return `${n}${a}${Y2({ output: t.output, options: this.options, cursor: this.cursor, maxItems: t.maxItems, columnPadding: a.length, rowPadding: d2 + y, style: c2 }).join(`
1381
- ${a}`)}
1424
+ return `${n}${o}${F({ output: t.output, options: this.options, cursor: this.cursor, maxItems: t.maxItems, columnPadding: o.length, rowPadding: d + g, style: c }).join(`
1425
+ ${o}`)}
1382
1426
  ${l}
1383
1427
  `;
1384
1428
  }
1385
1429
  default: {
1386
- const a = i ? `${e("cyan", $)} ` : "", l = n.split(`
1387
- `).length, d2 = i ? 2 : 1;
1388
- return `${n}${a}${Y2({ output: t.output, options: this.options, cursor: this.cursor, maxItems: t.maxItems, columnPadding: a.length, rowPadding: l + d2, style: c2 }).join(`
1389
- ${a}`)}
1390
- ${i ? e("cyan", E2) : ""}
1430
+ const o = r ? `${e("cyan", $)} ` : "", l = n.split(`
1431
+ `).length, d = r ? 2 : 1;
1432
+ return `${n}${o}${F({ output: t.output, options: this.options, cursor: this.cursor, maxItems: t.maxItems, columnPadding: o.length, rowPadding: l + d, style: c }).join(`
1433
+ ${o}`)}
1434
+ ${r ? e("cyan", x2) : ""}
1391
1435
  `;
1392
1436
  }
1393
1437
  }
1394
1438
  } }).prompt();
1395
1439
  };
1396
1440
  var we = (t) => e("dim", t);
1397
- var be = (t, r, s) => {
1398
- const i = { hard: true, trim: false }, u = wrapAnsi(t, r, i).split(`
1399
- `), n = u.reduce((a, l) => Math.max(dist_default2(l), a), 0), o = u.map(s).reduce((a, l) => Math.max(dist_default2(l), a), 0), c2 = r - (o - n);
1400
- return wrapAnsi(t, c2, i);
1441
+ var be = (t, i, s) => {
1442
+ const r = { hard: true, trim: false }, u = wrapAnsi(t, i, r).split(`
1443
+ `), n = u.reduce((o, l) => Math.max(dist_default2(l), o), 0), a = u.map(s).reduce((o, l) => Math.max(dist_default2(l), o), 0), c = i - (a - n);
1444
+ return wrapAnsi(t, c, r);
1401
1445
  };
1402
- var Se = (t = "", r = "", s) => {
1403
- const i = s?.output ?? j2.stdout, u = s?.withGuide ?? h.withGuide, n = s?.format ?? we, o = ["", ...be(t, A(i) - 6, n).split(`
1404
- `).map(n), ""], c2 = dist_default2(r), a = Math.max(o.reduce((p2, m) => {
1405
- const g = dist_default2(m);
1406
- return g > p2 ? g : p2;
1407
- }, 0), c2) + 2, l = o.map((p2) => `${e("gray", $)} ${p2}${" ".repeat(a - dist_default2(p2))}${e("gray", $)}`).join(`
1408
- `), d2 = u ? `${e("gray", $)}
1409
- ` : "", y = u ? xt : dt;
1410
- i.write(`${d2}${e("green", F)} ${e("reset", r)} ${e("gray", st.repeat(Math.max(a - c2 - 1, 1)) + ct)}
1446
+ var Se = (t = "", i = "", s) => {
1447
+ const r = s?.output ?? V2.stdout, u = s?.withGuide ?? h.withGuide, n = s?.format ?? we, a = ["", ...be(t, A(r) - 6, n).split(`
1448
+ `).map(n), ""], c = dist_default2(i), o = Math.max(a.reduce((p2, f2) => {
1449
+ const h2 = dist_default2(f2);
1450
+ return h2 > p2 ? h2 : p2;
1451
+ }, 0), c) + 2, l = a.map((p2) => `${e("gray", $)} ${p2}${" ".repeat(o - dist_default2(p2))}${e("gray", $)}`).join(`
1452
+ `), d = u ? `${e("gray", $)}
1453
+ ` : "", g = u ? Gt : dt;
1454
+ r.write(`${d}${e("green", H)} ${e("reset", i)} ${e("gray", st.repeat(Math.max(o - c - 1, 1)) + ct)}
1411
1455
  ${l}
1412
- ${e("gray", y + st.repeat(a + 2) + $t)}
1456
+ ${e("gray", g + st.repeat(o + 2) + $t)}
1413
1457
  `);
1414
1458
  };
1415
- var Ce = (t) => new ot({ validate: t.validate, mask: t.mask ?? Gt, signal: t.signal, input: t.input, output: t.output, render() {
1416
- const r = t.withGuide ?? h.withGuide, s = `${r ? `${e("gray", $)}
1417
- ` : ""}${M(this.state)} ${t.message}
1418
- `, i = this.userInputWithCursor, u = this.masked;
1459
+ var Ce = (t) => new ot({ validate: t.validate, mask: t.mask ?? Et, signal: t.signal, input: t.input, output: t.output, render() {
1460
+ const i = t.withGuide ?? h.withGuide, s = `${i ? `${e("gray", $)}
1461
+ ` : ""}${P(this.state)} ${t.message}
1462
+ `, r = this.userInputWithCursor, u = this.masked;
1419
1463
  switch (this.state) {
1420
1464
  case "error": {
1421
- const n = r ? `${e("yellow", $)} ` : "", o = r ? `${e("yellow", E2)} ` : "", c2 = u ?? "";
1465
+ const n = i ? `${e("yellow", $)} ` : "", a = i ? `${e("yellow", x2)} ` : "", c = u ?? "";
1422
1466
  return t.clearOnError && this.clear(), `${s.trim()}
1423
- ${n}${c2}
1424
- ${o}${e("yellow", this.error)}
1467
+ ${n}${c}
1468
+ ${a}${e("yellow", this.error)}
1425
1469
  `;
1426
1470
  }
1427
1471
  case "submit": {
1428
- const n = r ? `${e("gray", $)} ` : "", o = u ? e("dim", u) : "";
1429
- return `${s}${n}${o}`;
1472
+ const n = i ? `${e("gray", $)} ` : "", a = u ? e("dim", u) : "";
1473
+ return `${s}${n}${a}`;
1430
1474
  }
1431
1475
  case "cancel": {
1432
- const n = r ? `${e("gray", $)} ` : "", o = u ? e(["strikethrough", "dim"], u) : "";
1433
- return `${s}${n}${o}${u && r ? `
1476
+ const n = i ? `${e("gray", $)} ` : "", a = u ? e(["strikethrough", "dim"], u) : "";
1477
+ return `${s}${n}${a}${u && i ? `
1434
1478
  ${e("gray", $)}` : ""}`;
1435
1479
  }
1436
1480
  default: {
1437
- const n = r ? `${e("cyan", $)} ` : "", o = r ? e("cyan", E2) : "";
1438
- return `${s}${n}${i}
1439
- ${o}
1481
+ const n = i ? `${e("cyan", $)} ` : "", a = i ? e("cyan", x2) : "";
1482
+ return `${s}${n}${r}
1483
+ ${a}
1440
1484
  `;
1441
1485
  }
1442
1486
  }
1443
1487
  } }).prompt();
1444
- var _e = (t) => e("magenta", t);
1445
- var ft = ({ indicator: t = "dots", onCancel: r, output: s = process.stdout, cancelMessage: i, errorMessage: u, frames: n = tt ? ["\u25D2", "\u25D0", "\u25D3", "\u25D1"] : ["\u2022", "o", "O", "0"], delay: o = tt ? 80 : 120, signal: c2, ...a } = {}) => {
1446
- const l = at2();
1447
- let d2, y, p2 = false, m = false, g = "", S2, h2 = performance.now();
1448
- const f = A(s), v2 = a?.styleFrame ?? _e, T = (I) => {
1449
- const V2 = I > 1 ? u ?? h.messages.error : i ?? h.messages.cancel;
1450
- m = I === 1, p2 && (W2(V2, I), m && typeof r == "function" && r());
1451
- }, C2 = () => T(2), b2 = () => T(1), G2 = () => {
1452
- process.on("uncaughtExceptionMonitor", C2), process.on("unhandledRejection", C2), process.on("SIGINT", b2), process.on("SIGTERM", b2), process.on("exit", T), c2 && c2.addEventListener("abort", b2);
1453
- }, x = () => {
1454
- process.removeListener("uncaughtExceptionMonitor", C2), process.removeListener("unhandledRejection", C2), process.removeListener("SIGINT", b2), process.removeListener("SIGTERM", b2), process.removeListener("exit", T), c2 && c2.removeEventListener("abort", b2);
1455
- }, A2 = () => {
1456
- if (S2 === void 0) return;
1488
+ var Te = (t) => e("magenta", t);
1489
+ var ft = ({ indicator: t = "dots", onCancel: i, output: s = process.stdout, cancelMessage: r, errorMessage: u, frames: n = tt ? ["\u25D2", "\u25D0", "\u25D3", "\u25D1"] : ["\u2022", "o", "O", "0"], delay: a = tt ? 80 : 120, signal: c, ...o } = {}) => {
1490
+ const l = ot2();
1491
+ let d, g, p2 = false, f2 = false, h2 = "", I, m2 = performance.now();
1492
+ const y = A(s), v2 = o?.styleFrame ?? Te, C2 = (_) => {
1493
+ const A2 = _ > 1 ? u ?? h.messages.error : r ?? h.messages.cancel;
1494
+ f2 = _ === 1, p2 && (W2(A2, _), f2 && typeof i == "function" && i());
1495
+ }, S = () => C2(2), b2 = () => C2(1), G2 = () => {
1496
+ process.on("uncaughtExceptionMonitor", S), process.on("unhandledRejection", S), process.on("SIGINT", b2), process.on("SIGTERM", b2), process.on("exit", C2), c && c.addEventListener("abort", b2);
1497
+ }, M = () => {
1498
+ process.removeListener("uncaughtExceptionMonitor", S), process.removeListener("unhandledRejection", S), process.removeListener("SIGINT", b2), process.removeListener("SIGTERM", b2), process.removeListener("exit", C2), c && c.removeEventListener("abort", b2);
1499
+ }, N = () => {
1500
+ if (I === void 0) return;
1457
1501
  l && s.write(`
1458
1502
  `);
1459
- const I = wrapAnsi(S2, f, { hard: true, trim: false }).split(`
1503
+ const _ = wrapAnsi(I, y, { hard: true, trim: false }).split(`
1460
1504
  `);
1461
- I.length > 1 && s.write(import_sisteransi2.cursor.up(I.length - 1)), s.write(import_sisteransi2.cursor.to(0)), s.write(import_sisteransi2.erase.down());
1462
- }, P = (I) => I.replace(/\.+$/, ""), N = (I) => {
1463
- const V2 = (performance.now() - I) / 1e3, B = Math.floor(V2 / 60), L2 = Math.floor(V2 % 60);
1464
- return B > 0 ? `[${B}m ${L2}s]` : `[${L2}s]`;
1465
- }, D2 = a.withGuide ?? h.withGuide, rt2 = (I = "") => {
1466
- p2 = true, d2 = R({ output: s }), g = P(I), h2 = performance.now(), D2 && s.write(`${e("gray", $)}
1505
+ _.length > 1 && s.write(import_sisteransi2.cursor.up(_.length - 1)), s.write(import_sisteransi2.cursor.to(0)), s.write(import_sisteransi2.erase.down());
1506
+ }, O2 = (_) => _.replace(/\.+$/, ""), j2 = (_) => {
1507
+ const A2 = (performance.now() - _) / 1e3, L2 = Math.floor(A2 / 60), D2 = Math.floor(A2 % 60);
1508
+ return L2 > 0 ? `[${L2}m ${D2}s]` : `[${D2}s]`;
1509
+ }, k2 = o.withGuide ?? h.withGuide, rt2 = (_ = "") => {
1510
+ p2 = true, d = R({ output: s }), h2 = O2(_), m2 = performance.now(), k2 && s.write(`${e("gray", $)}
1467
1511
  `);
1468
- let V2 = 0, B = 0;
1469
- G2(), y = setInterval(() => {
1470
- if (l && g === S2) return;
1471
- A2(), S2 = g;
1472
- const L2 = v2(n[V2]);
1512
+ let A2 = 0, L2 = 0;
1513
+ G2(), g = setInterval(() => {
1514
+ if (l && h2 === I) return;
1515
+ N(), I = h2;
1516
+ const D2 = v2(n[A2]);
1473
1517
  let Z;
1474
- if (l) Z = `${L2} ${g}...`;
1475
- else if (t === "timer") Z = `${L2} ${g} ${N(h2)}`;
1518
+ if (l) Z = `${D2} ${h2}...`;
1519
+ else if (t === "timer") Z = `${D2} ${h2} ${j2(m2)}`;
1476
1520
  else {
1477
- const kt = ".".repeat(Math.floor(B)).slice(0, 3);
1478
- Z = `${L2} ${g}${kt}`;
1521
+ const kt = ".".repeat(Math.floor(L2)).slice(0, 3);
1522
+ Z = `${D2} ${h2}${kt}`;
1479
1523
  }
1480
- const Nt = wrapAnsi(Z, f, { hard: true, trim: false });
1481
- s.write(Nt), V2 = V2 + 1 < n.length ? V2 + 1 : 0, B = B < 4 ? B + 0.125 : 0;
1482
- }, o);
1483
- }, W2 = (I = "", V2 = 0, B = false) => {
1524
+ const Bt = wrapAnsi(Z, y, { hard: true, trim: false });
1525
+ s.write(Bt), A2 = A2 + 1 < n.length ? A2 + 1 : 0, L2 = L2 < 4 ? L2 + 0.125 : 0;
1526
+ }, a);
1527
+ }, W2 = (_ = "", A2 = 0, L2 = false) => {
1484
1528
  if (!p2) return;
1485
- p2 = false, clearInterval(y), A2();
1486
- const L2 = V2 === 0 ? e("green", F) : V2 === 1 ? e("red", ot2) : e("red", ut2);
1487
- g = I ?? g, B || (t === "timer" ? s.write(`${L2} ${g} ${N(h2)}
1488
- `) : s.write(`${L2} ${g}
1489
- `)), x(), d2();
1490
- };
1491
- return { start: rt2, stop: (I = "") => W2(I, 0), message: (I = "") => {
1492
- g = P(I ?? g);
1493
- }, cancel: (I = "") => W2(I, 1), error: (I = "") => W2(I, 2), clear: () => W2("", 0, true), get isCancelled() {
1494
- return m;
1529
+ p2 = false, clearInterval(g), N();
1530
+ const D2 = A2 === 0 ? e("green", H) : A2 === 1 ? e("red", at2) : e("red", ut2);
1531
+ h2 = _ ?? h2, L2 || (t === "timer" ? s.write(`${D2} ${h2} ${j2(m2)}
1532
+ `) : s.write(`${D2} ${h2}
1533
+ `)), M(), d();
1534
+ };
1535
+ return { start: rt2, stop: (_ = "") => W2(_, 0), message: (_ = "") => {
1536
+ h2 = O2(_ ?? h2);
1537
+ }, cancel: (_ = "") => W2(_, 1), error: (_ = "") => W2(_, 2), clear: () => W2("", 0, true), get isCancelled() {
1538
+ return f2;
1495
1539
  } };
1496
1540
  };
1497
- var Vt = { light: w2("\u2500", "-"), heavy: w2("\u2501", "="), block: w2("\u2588", "#") };
1498
- var it2 = (t, r) => t.includes(`
1541
+ var jt = { light: w2("\u2500", "-"), heavy: w2("\u2501", "="), block: w2("\u2588", "#") };
1542
+ var it2 = (t, i) => t.includes(`
1499
1543
  `) ? t.split(`
1500
- `).map((s) => r(s)).join(`
1501
- `) : r(t);
1502
- var Ee = (t) => {
1503
- const r = (s, i) => {
1544
+ `).map((s) => i(s)).join(`
1545
+ `) : i(t);
1546
+ var xe = (t) => {
1547
+ const i = (s, r) => {
1504
1548
  const u = s.label ?? String(s.value);
1505
- switch (i) {
1549
+ switch (r) {
1506
1550
  case "disabled":
1507
- return `${e("gray", H)} ${it2(u, (n) => e("gray", n))}${s.hint ? ` ${e("dim", `(${s.hint ?? "disabled"})`)}` : ""}`;
1551
+ return `${e("gray", U)} ${it2(u, (n) => e("gray", n))}${s.hint ? ` ${e("dim", `(${s.hint ?? "disabled"})`)}` : ""}`;
1508
1552
  case "selected":
1509
1553
  return `${it2(u, (n) => e("dim", n))}`;
1510
1554
  case "active":
@@ -1512,60 +1556,60 @@ var Ee = (t) => {
1512
1556
  case "cancelled":
1513
1557
  return `${it2(u, (n) => e(["strikethrough", "dim"], n))}`;
1514
1558
  default:
1515
- return `${e("dim", H)} ${it2(u, (n) => e("dim", n))}`;
1559
+ return `${e("dim", U)} ${it2(u, (n) => e("dim", n))}`;
1516
1560
  }
1517
1561
  };
1518
1562
  return new ut({ options: t.options, signal: t.signal, input: t.input, output: t.output, initialValue: t.initialValue, render() {
1519
- const s = t.withGuide ?? h.withGuide, i = `${M(this.state)} `, u = `${yt(this.state)} `, n = W(t.output, t.message, u, i), o = `${s ? `${e("gray", $)}
1563
+ const s = t.withGuide ?? h.withGuide, r = `${P(this.state)} `, u = `${yt(this.state)} `, n = W(t.output, t.message, u, r), a = `${s ? `${e("gray", $)}
1520
1564
  ` : ""}${n}
1521
1565
  `;
1522
1566
  switch (this.state) {
1523
1567
  case "submit": {
1524
- const c2 = s ? `${e("gray", $)} ` : "", a = W(t.output, r(this.options[this.cursor], "selected"), c2);
1525
- return `${o}${a}`;
1568
+ const c = s ? `${e("gray", $)} ` : "", o = W(t.output, i(this.options[this.cursor], "selected"), c);
1569
+ return `${a}${o}`;
1526
1570
  }
1527
1571
  case "cancel": {
1528
- const c2 = s ? `${e("gray", $)} ` : "", a = W(t.output, r(this.options[this.cursor], "cancelled"), c2);
1529
- return `${o}${a}${s ? `
1572
+ const c = s ? `${e("gray", $)} ` : "", o = W(t.output, i(this.options[this.cursor], "cancelled"), c);
1573
+ return `${a}${o}${s ? `
1530
1574
  ${e("gray", $)}` : ""}`;
1531
1575
  }
1532
1576
  default: {
1533
- const c2 = s ? `${e("cyan", $)} ` : "", a = s ? e("cyan", E2) : "", l = o.split(`
1534
- `).length, d2 = s ? 2 : 1;
1535
- return `${o}${c2}${Y2({ output: t.output, cursor: this.cursor, options: this.options, maxItems: t.maxItems, columnPadding: c2.length, rowPadding: l + d2, style: (y, p2) => r(y, y.disabled ? "disabled" : p2 ? "active" : "inactive") }).join(`
1536
- ${c2}`)}
1537
- ${a}
1577
+ const c = s ? `${e("cyan", $)} ` : "", o = s ? e("cyan", x2) : "", l = a.split(`
1578
+ `).length, d = s ? 2 : 1;
1579
+ return `${a}${c}${F({ output: t.output, cursor: this.cursor, options: this.options, maxItems: t.maxItems, columnPadding: c.length, rowPadding: l + d, style: (g, p2) => i(g, g.disabled ? "disabled" : p2 ? "active" : "inactive") }).join(`
1580
+ ${c}`)}
1581
+ ${o}
1538
1582
  `;
1539
1583
  }
1540
1584
  }
1541
1585
  } }).prompt();
1542
1586
  };
1543
- var jt = `${e("gray", $)} `;
1544
- var Re = (t) => new ht({ validate: t.validate, placeholder: t.placeholder, defaultValue: t.defaultValue, initialValue: t.initialValue, output: t.output, signal: t.signal, input: t.input, render() {
1545
- const r = t?.withGuide ?? h.withGuide, s = `${`${r ? `${e("gray", $)}
1546
- ` : ""}${M(this.state)} `}${t.message}
1547
- `, i = t.placeholder ? e("inverse", t.placeholder[0]) + e("dim", t.placeholder.slice(1)) : e(["inverse", "hidden"], "_"), u = this.userInput ? this.userInputWithCursor : i, n = this.value ?? "";
1587
+ var Nt = `${e("gray", $)} `;
1588
+ var Pe = (t) => new ht({ validate: t.validate, placeholder: t.placeholder, defaultValue: t.defaultValue, initialValue: t.initialValue, output: t.output, signal: t.signal, input: t.input, render() {
1589
+ const i = t?.withGuide ?? h.withGuide, s = `${`${i ? `${e("gray", $)}
1590
+ ` : ""}${P(this.state)} `}${t.message}
1591
+ `, r = t.placeholder ? e("inverse", t.placeholder[0]) + e("dim", t.placeholder.slice(1)) : e(["inverse", "hidden"], "_"), u = this.userInput ? this.userInputWithCursor : r, n = this.value ?? "";
1548
1592
  switch (this.state) {
1549
1593
  case "error": {
1550
- const o = this.error ? ` ${e("yellow", this.error)}` : "", c2 = r ? `${e("yellow", $)} ` : "", a = r ? e("yellow", E2) : "";
1594
+ const a = this.error ? ` ${e("yellow", this.error)}` : "", c = i ? `${e("yellow", $)} ` : "", o = i ? e("yellow", x2) : "";
1551
1595
  return `${s.trim()}
1552
- ${c2}${u}
1553
- ${a}${o}
1596
+ ${c}${u}
1597
+ ${o}${a}
1554
1598
  `;
1555
1599
  }
1556
1600
  case "submit": {
1557
- const o = n ? ` ${e("dim", n)}` : "", c2 = r ? e("gray", $) : "";
1558
- return `${s}${c2}${o}`;
1601
+ const a = n ? ` ${e("dim", n)}` : "", c = i ? e("gray", $) : "";
1602
+ return `${s}${c}${a}`;
1559
1603
  }
1560
1604
  case "cancel": {
1561
- const o = n ? ` ${e(["strikethrough", "dim"], n)}` : "", c2 = r ? e("gray", $) : "";
1562
- return `${s}${c2}${o}${n.trim() ? `
1563
- ${c2}` : ""}`;
1605
+ const a = n ? ` ${e(["strikethrough", "dim"], n)}` : "", c = i ? e("gray", $) : "";
1606
+ return `${s}${c}${a}${n.trim() ? `
1607
+ ${c}` : ""}`;
1564
1608
  }
1565
1609
  default: {
1566
- const o = r ? `${e("cyan", $)} ` : "", c2 = r ? e("cyan", E2) : "";
1567
- return `${s}${o}${u}
1568
- ${c2}
1610
+ const a = i ? `${e("cyan", $)} ` : "", c = i ? e("cyan", x2) : "";
1611
+ return `${s}${a}${u}
1612
+ ${c}
1569
1613
  `;
1570
1614
  }
1571
1615
  }
@@ -1994,9 +2038,16 @@ function trimRightSlash(s) {
1994
2038
  }
1995
2039
 
1996
2040
  // src/lib/credentials.ts
1997
- import { chmodSync, mkdirSync, renameSync, rmSync, statSync, writeFileSync } from "fs";
1998
- import { readFile } from "fs/promises";
1999
- import { dirname } from "path";
2041
+ import {
2042
+ chmodSync as chmodSync2,
2043
+ mkdirSync as mkdirSync3,
2044
+ readFileSync as readFileSync5,
2045
+ renameSync as renameSync2,
2046
+ rmSync,
2047
+ statSync as statSync3,
2048
+ writeFileSync as writeFileSync4
2049
+ } from "fs";
2050
+ import { dirname as dirname3 } from "path";
2000
2051
 
2001
2052
  // ../../node_modules/.pnpm/zod@4.4.2/node_modules/zod/v4/classic/external.js
2002
2053
  var external_exports = {};
@@ -3156,21 +3207,21 @@ function required(Class2, schema, mask) {
3156
3207
  });
3157
3208
  return clone(schema, def);
3158
3209
  }
3159
- function aborted(x, startIndex = 0) {
3160
- if (x.aborted === true)
3210
+ function aborted(x3, startIndex = 0) {
3211
+ if (x3.aborted === true)
3161
3212
  return true;
3162
- for (let i = startIndex; i < x.issues.length; i++) {
3163
- if (x.issues[i]?.continue !== true) {
3213
+ for (let i = startIndex; i < x3.issues.length; i++) {
3214
+ if (x3.issues[i]?.continue !== true) {
3164
3215
  return true;
3165
3216
  }
3166
3217
  }
3167
3218
  return false;
3168
3219
  }
3169
- function explicitlyAborted(x, startIndex = 0) {
3170
- if (x.aborted === true)
3220
+ function explicitlyAborted(x3, startIndex = 0) {
3221
+ if (x3.aborted === true)
3171
3222
  return true;
3172
- for (let i = startIndex; i < x.issues.length; i++) {
3173
- if (x.issues[i]?.continue === false) {
3223
+ for (let i = startIndex; i < x3.issues.length; i++) {
3224
+ if (x3.issues[i]?.continue === false) {
3174
3225
  return true;
3175
3226
  }
3176
3227
  }
@@ -4247,9 +4298,9 @@ var Doc = class {
4247
4298
  return;
4248
4299
  }
4249
4300
  const content = arg;
4250
- const lines = content.split("\n").filter((x) => x);
4251
- const minIndent = Math.min(...lines.map((x) => x.length - x.trimStart().length));
4252
- const dedented = lines.map((x) => x.slice(minIndent)).map((x) => " ".repeat(this.indent * 2) + x);
4301
+ const lines = content.split("\n").filter((x3) => x3);
4302
+ const minIndent = Math.min(...lines.map((x3) => x3.length - x3.trimStart().length));
4303
+ const dedented = lines.map((x3) => x3.slice(minIndent)).map((x3) => " ".repeat(this.indent * 2) + x3);
4253
4304
  for (const line of dedented) {
4254
4305
  this.content.push(line);
4255
4306
  }
@@ -4258,7 +4309,7 @@ var Doc = class {
4258
4309
  const F2 = Function;
4259
4310
  const args = this?.args;
4260
4311
  const content = this?.content ?? [``];
4261
- const lines = [...content.map((x) => ` ${x}`)];
4312
+ const lines = [...content.map((x3) => ` ${x3}`)];
4262
4313
  return new F2(...args, lines.join("\n"));
4263
4314
  }
4264
4315
  };
@@ -4635,7 +4686,7 @@ var $ZodBase64 = /* @__PURE__ */ $constructor("$ZodBase64", (inst, def) => {
4635
4686
  function isValidBase64URL(data) {
4636
4687
  if (!base64url.test(data))
4637
4688
  return false;
4638
- const base643 = data.replace(/[-_]/g, (c2) => c2 === "-" ? "+" : "/");
4689
+ const base643 = data.replace(/[-_]/g, (c) => c === "-" ? "+" : "/");
4639
4690
  const padded = base643.padEnd(Math.ceil(base643.length / 4) * 4, "=");
4640
4691
  return isValidBase64(padded);
4641
4692
  }
@@ -5036,13 +5087,13 @@ var $ZodObject = /* @__PURE__ */ $constructor("$ZodObject", (inst, def) => {
5036
5087
  }
5037
5088
  return propValues;
5038
5089
  });
5039
- const isObject5 = isObject2;
5090
+ const isObject6 = isObject2;
5040
5091
  const catchall = def.catchall;
5041
5092
  let value;
5042
5093
  inst._zod.parse = (payload, ctx) => {
5043
5094
  value ?? (value = _normalized.value);
5044
5095
  const input = payload.value;
5045
- if (!isObject5(input)) {
5096
+ if (!isObject6(input)) {
5046
5097
  payload.issues.push({
5047
5098
  expected: "object",
5048
5099
  code: "invalid_type",
@@ -5169,7 +5220,7 @@ var $ZodObjectJIT = /* @__PURE__ */ $constructor("$ZodObjectJIT", (inst, def) =>
5169
5220
  return (payload, ctx) => fn(shape, payload, ctx);
5170
5221
  };
5171
5222
  let fastpass;
5172
- const isObject5 = isObject2;
5223
+ const isObject6 = isObject2;
5173
5224
  const jit = !globalConfig.jitless;
5174
5225
  const allowsEval2 = allowsEval;
5175
5226
  const fastEnabled = jit && allowsEval2.value;
@@ -5178,7 +5229,7 @@ var $ZodObjectJIT = /* @__PURE__ */ $constructor("$ZodObjectJIT", (inst, def) =>
5178
5229
  inst._zod.parse = (payload, ctx) => {
5179
5230
  value ?? (value = _normalized.value);
5180
5231
  const input = payload.value;
5181
- if (!isObject5(input)) {
5232
+ if (!isObject6(input)) {
5182
5233
  payload.issues.push({
5183
5234
  expected: "object",
5184
5235
  code: "invalid_type",
@@ -5468,7 +5519,7 @@ function handleIntersectionResults(result, left, right) {
5468
5519
  result.issues.push(iss);
5469
5520
  }
5470
5521
  }
5471
- const bothKeys = [...unrecKeys].filter(([, f]) => f.l && f.r).map(([k2]) => k2);
5522
+ const bothKeys = [...unrecKeys].filter(([, f2]) => f2.l && f2.r).map(([k2]) => k2);
5472
5523
  if (bothKeys.length && unrecIssue) {
5473
5524
  result.issues.push({ ...unrecIssue, keys: bothKeys });
5474
5525
  }
@@ -5983,7 +6034,7 @@ var $ZodNonOptional = /* @__PURE__ */ $constructor("$ZodNonOptional", (inst, def
5983
6034
  $ZodType.init(inst, def);
5984
6035
  defineLazy(inst._zod, "values", () => {
5985
6036
  const v2 = def.innerType._zod.values;
5986
- return v2 ? new Set([...v2].filter((x) => x !== void 0)) : void 0;
6037
+ return v2 ? new Set([...v2].filter((x3) => x3 !== void 0)) : void 0;
5987
6038
  });
5988
6039
  inst._zod.parse = (payload, ctx) => {
5989
6040
  const result = def.innerType._zod.run(payload, ctx);
@@ -6311,10 +6362,10 @@ var $ZodPromise = /* @__PURE__ */ $constructor("$ZodPromise", (inst, def) => {
6311
6362
  var $ZodLazy = /* @__PURE__ */ $constructor("$ZodLazy", (inst, def) => {
6312
6363
  $ZodType.init(inst, def);
6313
6364
  defineLazy(inst._zod, "innerType", () => {
6314
- const d2 = def;
6315
- if (!d2._cachedInner)
6316
- d2._cachedInner = def.getter();
6317
- return d2._cachedInner;
6365
+ const d = def;
6366
+ if (!d._cachedInner)
6367
+ d._cachedInner = def.getter();
6368
+ return d._cachedInner;
6318
6369
  });
6319
6370
  defineLazy(inst._zod, "pattern", () => inst._zod.innerType?._zod?.pattern);
6320
6371
  defineLazy(inst._zod, "propValues", () => inst._zod.innerType?._zod?.propValues);
@@ -12349,8 +12400,8 @@ var $ZodRegistry = class {
12349
12400
  if (p2) {
12350
12401
  const pm = { ...this.get(p2) ?? {} };
12351
12402
  delete pm.id;
12352
- const f = { ...pm, ...this._map.get(schema) };
12353
- return Object.keys(f).length ? f : void 0;
12403
+ const f2 = { ...pm, ...this._map.get(schema) };
12404
+ return Object.keys(f2).length ? f2 : void 0;
12354
12405
  }
12355
12406
  return this._map.get(schema);
12356
12407
  }
@@ -13960,7 +14011,7 @@ var fileProcessor = (schema, _ctx, json2, _params) => {
13960
14011
  Object.assign(_json, file2);
13961
14012
  } else {
13962
14013
  Object.assign(_json, file2);
13963
- _json.anyOf = mime.map((m) => ({ contentMediaType: m }));
14014
+ _json.anyOf = mime.map((m2) => ({ contentMediaType: m2 }));
13964
14015
  }
13965
14016
  } else {
13966
14017
  Object.assign(_json, file2);
@@ -14047,7 +14098,7 @@ var objectProcessor = (schema, ctx, _json, params) => {
14047
14098
  var unionProcessor = (schema, ctx, json2, params) => {
14048
14099
  const def = schema._zod.def;
14049
14100
  const isExclusive = def.inclusive === false;
14050
- const options = def.options.map((x, i) => process2(x, ctx, {
14101
+ const options = def.options.map((x3, i) => process2(x3, ctx, {
14051
14102
  ...params,
14052
14103
  path: [...params.path, isExclusive ? "oneOf" : "anyOf", i]
14053
14104
  }));
@@ -14080,7 +14131,7 @@ var tupleProcessor = (schema, ctx, _json, params) => {
14080
14131
  json2.type = "array";
14081
14132
  const prefixPath = ctx.target === "draft-2020-12" ? "prefixItems" : "items";
14082
14133
  const restPath = ctx.target === "draft-2020-12" ? "items" : ctx.target === "openapi-3.0" ? "items" : "additionalItems";
14083
- const prefixItems = def.items.map((x, i) => process2(x, ctx, {
14134
+ const prefixItems = def.items.map((x3, i) => process2(x3, ctx, {
14084
14135
  ...params,
14085
14136
  path: [...params.path, prefixPath, i]
14086
14137
  }));
@@ -14806,11 +14857,11 @@ var ZodType = /* @__PURE__ */ $constructor("ZodType", (inst, def) => {
14806
14857
  transform(tx) {
14807
14858
  return pipe(this, transform(tx));
14808
14859
  },
14809
- default(d2) {
14810
- return _default2(this, d2);
14860
+ default(d) {
14861
+ return _default2(this, d);
14811
14862
  },
14812
- prefault(d2) {
14813
- return prefault(this, d2);
14863
+ prefault(d) {
14864
+ return prefault(this, d);
14814
14865
  },
14815
14866
  catch(params) {
14816
14867
  return _catch2(this, params);
@@ -15307,9 +15358,9 @@ var ZodDate = /* @__PURE__ */ $constructor("ZodDate", (inst, def) => {
15307
15358
  inst._zod.processJSONSchema = (ctx, json2, params) => dateProcessor(inst, ctx, json2, params);
15308
15359
  inst.min = (value, params) => inst.check(_gte(value, params));
15309
15360
  inst.max = (value, params) => inst.check(_lte(value, params));
15310
- const c2 = inst._zod.bag;
15311
- inst.minDate = c2.minimum ? new Date(c2.minimum) : null;
15312
- inst.maxDate = c2.maximum ? new Date(c2.maximum) : null;
15361
+ const c = inst._zod.bag;
15362
+ inst.minDate = c.minimum ? new Date(c.minimum) : null;
15363
+ inst.maxDate = c.maximum ? new Date(c.maximum) : null;
15313
15364
  });
15314
15365
  function date3(params) {
15315
15366
  return _date(ZodDate, params);
@@ -16506,6 +16557,92 @@ function date4(params) {
16506
16557
  // ../../node_modules/.pnpm/zod@4.4.2/node_modules/zod/v4/classic/external.js
16507
16558
  config(en_default());
16508
16559
 
16560
+ // src/config-writers/claude-code.ts
16561
+ import { existsSync, readFileSync as readFileSync2 } from "fs";
16562
+ import { homedir as homedir2 } from "os";
16563
+ import { join as join2 } from "path";
16564
+
16565
+ // src/lib/merge-json.ts
16566
+ import { chmodSync, mkdirSync, readFileSync, renameSync, statSync, writeFileSync } from "fs";
16567
+ import { dirname } from "path";
16568
+ function isPlainObject2(v2) {
16569
+ return typeof v2 === "object" && v2 !== null && !Array.isArray(v2);
16570
+ }
16571
+ function deepMerge2(base, patch, opts = {}, currentPath = []) {
16572
+ const hookPaths = new Set(opts.hookArrayPaths ?? []);
16573
+ const dedupeKey = opts.hookDedupeKey ?? "command";
16574
+ const out = { ...base };
16575
+ for (const [k2, v2] of Object.entries(patch)) {
16576
+ const nextPath = [...currentPath, k2];
16577
+ const pathStr = nextPath.join(".");
16578
+ const existing = out[k2];
16579
+ if (isPlainObject2(v2) && isPlainObject2(existing)) {
16580
+ out[k2] = deepMerge2(existing, v2, opts, nextPath);
16581
+ continue;
16582
+ }
16583
+ if (Array.isArray(v2) && Array.isArray(existing) && hookPaths.has(pathStr)) {
16584
+ out[k2] = mergeHookArray(existing, v2, dedupeKey);
16585
+ continue;
16586
+ }
16587
+ out[k2] = v2;
16588
+ }
16589
+ return out;
16590
+ }
16591
+ function extractHookCommands(entry, dedupeKey) {
16592
+ if (!isPlainObject2(entry)) return [];
16593
+ if (typeof entry[dedupeKey] === "string") return [entry[dedupeKey]];
16594
+ const inner = entry.hooks;
16595
+ if (!Array.isArray(inner)) return [];
16596
+ return inner.filter(isPlainObject2).map((h2) => h2[dedupeKey]).filter((x3) => typeof x3 === "string");
16597
+ }
16598
+ function mergeHookArray(existing, patch, dedupeKey) {
16599
+ const patchCommands = new Set(patch.flatMap((e2) => extractHookCommands(e2, dedupeKey)));
16600
+ const kept = existing.filter((entry) => {
16601
+ if (!isPlainObject2(entry) || typeof entry[dedupeKey] !== "string") return true;
16602
+ return !patchCommands.has(entry[dedupeKey]);
16603
+ });
16604
+ const keptCommands = new Set(kept.flatMap((e2) => extractHookCommands(e2, dedupeKey)));
16605
+ const additions = patch.filter((entry) => {
16606
+ const cmds = extractHookCommands(entry, dedupeKey);
16607
+ if (cmds.length === 0) return true;
16608
+ return !cmds.some((c) => keptCommands.has(c));
16609
+ });
16610
+ return [...kept, ...additions];
16611
+ }
16612
+ function mergeJsonFile(filePath, patch, opts = {}) {
16613
+ const dir = dirname(filePath);
16614
+ mkdirSync(dir, { recursive: true, mode: 448 });
16615
+ let existing = {};
16616
+ let mode;
16617
+ try {
16618
+ const raw = readFileSync(filePath, "utf8");
16619
+ if (raw.trim().length > 0) {
16620
+ const parsed = JSON.parse(raw);
16621
+ if (isPlainObject2(parsed)) existing = parsed;
16622
+ else {
16623
+ throw new Error(
16624
+ `${filePath} contains non-object JSON (${Array.isArray(parsed) ? "array" : typeof parsed}). Refusing to overwrite.`
16625
+ );
16626
+ }
16627
+ }
16628
+ mode = statSync(filePath).mode & 511;
16629
+ } catch (err) {
16630
+ if (err.code !== "ENOENT") throw err;
16631
+ }
16632
+ const merged = deepMerge2(existing, patch, opts);
16633
+ const tmp = `${filePath}.${process.pid}.tmp`;
16634
+ writeFileSync(tmp, `${JSON.stringify(merged, null, 2)}
16635
+ `);
16636
+ if (mode !== void 0) {
16637
+ try {
16638
+ chmodSync(tmp, mode);
16639
+ } catch {
16640
+ }
16641
+ }
16642
+ renameSync(tmp, filePath);
16643
+ return merged;
16644
+ }
16645
+
16509
16646
  // src/lib/paths.ts
16510
16647
  import { homedir } from "os";
16511
16648
  import { join } from "path";
@@ -16517,87 +16654,649 @@ function userCwd() {
16517
16654
  return process.env.INIT_CWD ?? process.cwd();
16518
16655
  }
16519
16656
 
16520
- // src/lib/credentials.ts
16521
- var CredentialsFileSchema = external_exports.object({
16522
- apiKey: external_exports.string().min(1),
16523
- gatewayUrl: external_exports.url(),
16524
- userId: external_exports.uuid(),
16525
- email: external_exports.email(),
16526
- savedAt: external_exports.iso.datetime()
16527
- });
16528
- function writeCredentials(creds) {
16529
- const payload = {
16530
- ...creds,
16531
- savedAt: (/* @__PURE__ */ new Date()).toISOString()
16657
+ // src/config-writers/claude-code.ts
16658
+ var writeClaudeCodeConfig = ({
16659
+ gatewayUrl,
16660
+ apiKey,
16661
+ hookScriptPath,
16662
+ scope,
16663
+ project,
16664
+ model,
16665
+ availableModels = []
16666
+ }) => {
16667
+ const path = claudeSettingsPath(scope);
16668
+ const baseUrl = `${trimRightSlash2(gatewayUrl)}/gateway/anthropic`;
16669
+ const env = {
16670
+ ANTHROPIC_BASE_URL: baseUrl,
16671
+ ANTHROPIC_AUTH_TOKEN: apiKey,
16672
+ ANTHROPIC_CUSTOM_HEADERS: buildCustomHeaders(scope, project)
16532
16673
  };
16533
- const parsed = CredentialsFileSchema.parse(payload);
16534
- const dir = dirname(CREDENTIALS_FILE);
16535
- mkdirSync(dir, { recursive: true, mode: 448 });
16536
- try {
16537
- chmodSync(dir, 448);
16538
- } catch {
16674
+ for (const [key, value] of Object.entries(resolveTierDefaults(availableModels))) {
16675
+ env[key] = value;
16539
16676
  }
16540
- const tmp = `${CREDENTIALS_FILE}.${process.pid}.tmp`;
16541
- writeFileSync(tmp, JSON.stringify(parsed, null, 2), { mode: 384 });
16542
- try {
16543
- chmodSync(tmp, 384);
16544
- } catch {
16677
+ const patch = {
16678
+ env,
16679
+ hooks: {
16680
+ Stop: [{ matcher: "*", hooks: [{ type: "command", command: hookScriptPath }] }],
16681
+ SessionEnd: [{ matcher: "*", hooks: [{ type: "command", command: hookScriptPath }] }]
16682
+ }
16683
+ };
16684
+ patch.model = model?.slug;
16685
+ if (availableModels.length > 0) {
16686
+ patch.availableModels = availableModels.map((m2) => m2.slug).sort();
16687
+ }
16688
+ mergeJsonFile(path, patch, {
16689
+ hookArrayPaths: ["hooks.Stop", "hooks.SessionEnd"],
16690
+ hookDedupeKey: "command"
16691
+ });
16692
+ const notes = [];
16693
+ if (scope === "local") notes.push("Claude Code auto-ignores settings.local.json in git.");
16694
+ if (scope !== "user" && !project) {
16695
+ notes.push(
16696
+ "Project slug could not be derived (no git remote and no .codevector.json). Per-project usage attribution will be blank until you set one."
16697
+ );
16698
+ }
16699
+ return {
16700
+ tool: "claude-code",
16701
+ status: "configured",
16702
+ path,
16703
+ scope,
16704
+ notes: notes.length > 0 ? notes.join(" ") : void 0
16705
+ };
16706
+ };
16707
+ function claudeSettingsPath(scope) {
16708
+ switch (scope) {
16709
+ case "user":
16710
+ return join2(homedir2(), ".claude", "settings.json");
16711
+ case "project":
16712
+ return join2(userCwd(), ".claude", "settings.json");
16713
+ case "local":
16714
+ return join2(userCwd(), ".claude", "settings.local.json");
16545
16715
  }
16546
- renameSync(tmp, CREDENTIALS_FILE);
16547
- return parsed;
16548
16716
  }
16549
- async function readCredentials() {
16550
- let raw;
16551
- try {
16552
- raw = await readFile(CREDENTIALS_FILE, "utf8");
16553
- } catch (err) {
16554
- if (err.code === "ENOENT") return null;
16555
- throw err;
16717
+ function detectClaudeCodeConfig() {
16718
+ const scopes = ["local", "project", "user"];
16719
+ for (const scope of scopes) {
16720
+ const path = claudeSettingsPath(scope);
16721
+ if (!existsSync(path)) continue;
16722
+ try {
16723
+ const raw = JSON.parse(readFileSync2(path, "utf8"));
16724
+ if (raw.env?.ANTHROPIC_BASE_URL?.includes("/gateway/anthropic")) {
16725
+ return { scope, modelSlug: raw.model };
16726
+ }
16727
+ } catch {
16728
+ }
16556
16729
  }
16557
- let parsed;
16558
- try {
16559
- parsed = JSON.parse(raw);
16560
- } catch {
16561
- throw new Error(
16562
- `${CREDENTIALS_FILE} is not valid JSON. Run \`codevector logout\` and re-login.`
16730
+ return void 0;
16731
+ }
16732
+ function resolveTierDefaults(available) {
16733
+ const out = {};
16734
+ const tierToEnv = {
16735
+ haiku: "ANTHROPIC_DEFAULT_HAIKU_MODEL",
16736
+ sonnet: "ANTHROPIC_DEFAULT_SONNET_MODEL",
16737
+ opus: "ANTHROPIC_DEFAULT_OPUS_MODEL"
16738
+ };
16739
+ for (const [tier, envKey] of Object.entries(tierToEnv)) {
16740
+ const matches = available.filter(
16741
+ (m2) => typeof m2.family === "string" && m2.family.toLowerCase().includes(tier)
16563
16742
  );
16743
+ if (matches.length === 1) {
16744
+ out[envKey] = matches[0].slug;
16745
+ }
16564
16746
  }
16565
- return CredentialsFileSchema.parse(parsed);
16747
+ return out;
16566
16748
  }
16567
- function deleteCredentials() {
16568
- try {
16569
- rmSync(CREDENTIALS_FILE);
16570
- return true;
16571
- } catch (err) {
16572
- if (err.code === "ENOENT") return false;
16573
- throw err;
16749
+ function buildCustomHeaders(scope, project) {
16750
+ const safe = (v2) => v2.replace(/[\r\n]/g, "").trim();
16751
+ const headers = [`x-client-app: claude-code`];
16752
+ if (scope !== "user" && project) {
16753
+ const slug = safe(project);
16754
+ if (slug) headers.push(`x-project: ${slug}`);
16574
16755
  }
16756
+ return headers.join("\n");
16575
16757
  }
16576
- function credentialsFileModeOk() {
16758
+ function trimRightSlash2(url2) {
16759
+ return url2.endsWith("/") ? url2.slice(0, -1) : url2;
16760
+ }
16761
+
16762
+ // src/config-writers/opencode.ts
16763
+ import { existsSync as existsSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
16764
+ import { homedir as homedir3 } from "os";
16765
+ import { join as join4 } from "path";
16766
+
16767
+ // src/lib/gitignore.ts
16768
+ import { existsSync as existsSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
16769
+ import { join as join3 } from "path";
16770
+ function ensureGitignored(pattern, cwd = userCwd()) {
16771
+ const path = join3(cwd, ".gitignore");
16772
+ const trimmed = pattern.trim();
16773
+ if (!trimmed) return false;
16774
+ let existing = "";
16775
+ if (existsSync2(path)) {
16776
+ existing = readFileSync3(path, "utf8");
16777
+ if (hasMatchingEntry(existing, trimmed)) return false;
16778
+ }
16779
+ const needsLeadingNewline = existing.length > 0 && !existing.endsWith("\n");
16780
+ const prefix = needsLeadingNewline ? "\n" : "";
16577
16781
  try {
16578
- const stats = statSync(CREDENTIALS_FILE);
16579
- const mode = stats.mode & 511;
16580
- return mode === 384;
16782
+ writeFileSync2(path, `${existing}${prefix}${trimmed}
16783
+ `);
16784
+ return true;
16581
16785
  } catch {
16582
16786
  return false;
16583
16787
  }
16584
16788
  }
16585
- function maskApiKey(key) {
16586
- if (key.length <= 12) return "********";
16587
- return `${key.slice(0, 5)}\u2026${key.slice(-4)}`;
16588
- }
16589
-
16590
- // src/lib/prompt.ts
16591
- function unwrap(value) {
16592
- if (q(value)) {
16593
- me("Cancelled.");
16594
- process.exit(130);
16789
+ function hasMatchingEntry(contents, pattern) {
16790
+ const lines = contents.split("\n").map((l) => l.replace(/#.*$/, "").trim()).filter(Boolean);
16791
+ if (lines.includes(pattern)) return true;
16792
+ if (pattern.includes("/")) {
16793
+ const topDir = `${pattern.split("/")[0]}/`;
16794
+ if (lines.includes(topDir)) return true;
16595
16795
  }
16596
- return value;
16796
+ return false;
16597
16797
  }
16598
- async function promptText(message, placeholder) {
16599
- return unwrap(
16600
- await Re({
16798
+
16799
+ // src/config-writers/opencode.ts
16800
+ var ENV_VAR_NAME = "CODEVECTOR_GATEWAY_KEY";
16801
+ var PROVIDER_PREFIX = "codevector";
16802
+ var writeOpencodeConfig = ({
16803
+ gatewayUrl,
16804
+ apiKey,
16805
+ scope,
16806
+ project,
16807
+ model,
16808
+ availableModels = []
16809
+ }) => {
16810
+ const path = opencodeSettingsPath(scope);
16811
+ const gateway = trimRightSlash3(gatewayUrl);
16812
+ const keyLiteral = scope === "project" ? `{env:${ENV_VAR_NAME}}` : apiKey;
16813
+ removeStaleGatewayProviders(path, gateway);
16814
+ const models = buildModelEntries(availableModels);
16815
+ const options = {
16816
+ apiKey: keyLiteral,
16817
+ baseURL: `${gateway}/gateway/openai/v1`,
16818
+ headers: buildCustomHeaders2(scope, project)
16819
+ };
16820
+ const patch = {
16821
+ $schema: "https://opencode.ai/config.json",
16822
+ provider: {
16823
+ [PROVIDER_PREFIX]: {
16824
+ options,
16825
+ models
16826
+ }
16827
+ }
16828
+ };
16829
+ patch.model = model ? `${PROVIDER_PREFIX}/${model.slug}` : void 0;
16830
+ mergeJsonFile(path, patch);
16831
+ forceReplaceCgwModels(path, models);
16832
+ const notes = [];
16833
+ if (scope === "local") {
16834
+ if (ensureGitignored("opencode.json")) {
16835
+ notes.push("Added `opencode.json` to .gitignore.");
16836
+ }
16837
+ }
16838
+ if (scope === "project") {
16839
+ notes.push(
16840
+ `Project scope avoids embedding secrets. Export ${ENV_VAR_NAME}=<your key> before running opencode, or switch to local scope to embed the key.`
16841
+ );
16842
+ }
16843
+ if (scope !== "user" && !project) {
16844
+ notes.push(
16845
+ "Project slug could not be derived (no git remote and no .codevector.json). Per-project usage attribution will be blank until you set one."
16846
+ );
16847
+ }
16848
+ return {
16849
+ tool: "opencode",
16850
+ status: "configured",
16851
+ path,
16852
+ scope,
16853
+ notes: notes.length > 0 ? notes.join(" ") : void 0
16854
+ };
16855
+ };
16856
+ function syncOpencodeModels(path, availableModels) {
16857
+ if (!existsSync3(path)) {
16858
+ throw new Error(
16859
+ `No opencode config found at ${path}. Run \`codevector configure opencode\` first.`
16860
+ );
16861
+ }
16862
+ const raw = readFileSync4(path, "utf8");
16863
+ let parsed;
16864
+ try {
16865
+ parsed = raw.trim().length === 0 ? {} : JSON.parse(raw);
16866
+ } catch (err) {
16867
+ throw new Error(`${path} is not valid JSON: ${err instanceof Error ? err.message : err}`);
16868
+ }
16869
+ if (!isObject3(parsed)) {
16870
+ throw new Error(`${path} is not a JSON object; refusing to overwrite.`);
16871
+ }
16872
+ const provider = isObject3(parsed.provider) ? parsed.provider : void 0;
16873
+ const codevector = provider && isObject3(provider[PROVIDER_PREFIX]) ? provider[PROVIDER_PREFIX] : void 0;
16874
+ if (!provider || !codevector) {
16875
+ throw new Error(
16876
+ `${path} has no \`provider.${PROVIDER_PREFIX}\` entry. Run \`codevector configure opencode\` first.`
16877
+ );
16878
+ }
16879
+ const models = buildModelEntries(availableModels);
16880
+ codevector.models = models;
16881
+ writeFileSync3(path, `${JSON.stringify(parsed, null, 2)}
16882
+ `);
16883
+ return Object.keys(models).length;
16884
+ }
16885
+ function buildModelEntries(availableModels) {
16886
+ const out = {};
16887
+ for (const m2 of availableModels) {
16888
+ const entry = {};
16889
+ if (m2.displayName) entry.name = m2.displayName;
16890
+ if (m2.family) entry.family = m2.family;
16891
+ if (m2.releaseDate) entry.release_date = m2.releaseDate;
16892
+ const inP = parsePrice(m2.inputPricePerMtok);
16893
+ const outP = parsePrice(m2.outputPricePerMtok);
16894
+ if (inP !== void 0 && outP !== void 0) {
16895
+ const cost = { input: inP, output: outP };
16896
+ const cacheReadP = parsePrice(m2.cachedInputPricePerMtok);
16897
+ const cacheWriteP = parsePrice(m2.cacheWritePricePerMtok);
16898
+ if (cacheReadP !== void 0) cost.cache_read = cacheReadP;
16899
+ if (cacheWriteP !== void 0) cost.cache_write = cacheWriteP;
16900
+ entry.cost = cost;
16901
+ }
16902
+ const ctx = m2.contextWindow;
16903
+ const outTok = m2.maxOutputTokens;
16904
+ if (typeof ctx === "number" && ctx > 0 && typeof outTok === "number" && outTok > 0) {
16905
+ const limit = { context: ctx, output: outTok };
16906
+ if (typeof m2.maxInputTokens === "number" && m2.maxInputTokens > 0) {
16907
+ limit.input = m2.maxInputTokens;
16908
+ }
16909
+ entry.limit = limit;
16910
+ }
16911
+ if (typeof m2.supportsReasoning === "boolean") entry.reasoning = m2.supportsReasoning;
16912
+ if (typeof m2.supportsAttachment === "boolean") entry.attachment = m2.supportsAttachment;
16913
+ if (typeof m2.supportsToolCall === "boolean") entry.tool_call = m2.supportsToolCall;
16914
+ if (typeof m2.supportsTemperature === "boolean") entry.temperature = m2.supportsTemperature;
16915
+ const inMods = m2.inputModalities;
16916
+ const outMods = m2.outputModalities;
16917
+ if (inMods && inMods.length > 0 && outMods && outMods.length > 0) {
16918
+ entry.modalities = { input: [...inMods], output: [...outMods] };
16919
+ }
16920
+ out[m2.slug] = entry;
16921
+ }
16922
+ return out;
16923
+ }
16924
+ function parsePrice(raw) {
16925
+ if (raw == null) return void 0;
16926
+ const n = Number(raw);
16927
+ return Number.isFinite(n) ? n : void 0;
16928
+ }
16929
+ function forceReplaceCgwModels(path, models) {
16930
+ if (!existsSync3(path)) return;
16931
+ let parsed;
16932
+ try {
16933
+ parsed = JSON.parse(readFileSync4(path, "utf8"));
16934
+ } catch {
16935
+ return;
16936
+ }
16937
+ if (!isObject3(parsed)) return;
16938
+ const provider = parsed.provider;
16939
+ if (!isObject3(provider)) return;
16940
+ const codevector = provider[PROVIDER_PREFIX];
16941
+ if (!isObject3(codevector)) return;
16942
+ codevector.models = models;
16943
+ writeFileSync3(path, `${JSON.stringify(parsed, null, 2)}
16944
+ `);
16945
+ }
16946
+ function opencodeSettingsPath(scope) {
16947
+ switch (scope) {
16948
+ case "user":
16949
+ return join4(homedir3(), ".config", "opencode", "opencode.json");
16950
+ case "project":
16951
+ case "local":
16952
+ return join4(userCwd(), "opencode.json");
16953
+ }
16954
+ }
16955
+ function detectOpencodeConfig() {
16956
+ const scopes = ["local", "project", "user"];
16957
+ for (const scope of scopes) {
16958
+ const path = opencodeSettingsPath(scope);
16959
+ if (!existsSync3(path)) continue;
16960
+ try {
16961
+ const raw = JSON.parse(readFileSync4(path, "utf8"));
16962
+ if (raw.provider?.codevector?.options?.baseURL) {
16963
+ const modelSlug = raw.model?.startsWith("codevector/") ? raw.model.slice("codevector/".length) : raw.model;
16964
+ return { scope, modelSlug };
16965
+ }
16966
+ } catch {
16967
+ }
16968
+ }
16969
+ return void 0;
16970
+ }
16971
+ function removeStaleGatewayProviders(path, gateway) {
16972
+ if (!existsSync3(path)) return;
16973
+ let parsed;
16974
+ try {
16975
+ const raw = readFileSync4(path, "utf8");
16976
+ if (raw.trim().length === 0) return;
16977
+ parsed = JSON.parse(raw);
16978
+ } catch {
16979
+ return;
16980
+ }
16981
+ if (!isObject3(parsed)) return;
16982
+ const provider = parsed.provider;
16983
+ if (!isObject3(provider)) return;
16984
+ const ourBaseUrls = /* @__PURE__ */ new Set([`${gateway}/gateway/anthropic`, `${gateway}/gateway/openai/v1`]);
16985
+ let mutated = false;
16986
+ for (const key of ["openai", "anthropic"]) {
16987
+ const entry = provider[key];
16988
+ if (!isObject3(entry)) continue;
16989
+ const options = entry.options;
16990
+ if (!isObject3(options)) continue;
16991
+ const baseURL = options.baseURL;
16992
+ if (typeof baseURL === "string" && ourBaseUrls.has(baseURL)) {
16993
+ delete provider[key];
16994
+ mutated = true;
16995
+ }
16996
+ }
16997
+ if (mutated) {
16998
+ writeFileSync3(path, `${JSON.stringify(parsed, null, 2)}
16999
+ `);
17000
+ }
17001
+ }
17002
+ function isObject3(v2) {
17003
+ return typeof v2 === "object" && v2 !== null && !Array.isArray(v2);
17004
+ }
17005
+ function buildCustomHeaders2(scope, project) {
17006
+ const safe = (v2) => v2.replace(/[\r\n]/g, "").trim();
17007
+ const headers = {
17008
+ "x-client-app": "opencode"
17009
+ };
17010
+ if (scope !== "user" && project) {
17011
+ const slug = safe(project);
17012
+ if (slug) {
17013
+ headers["x-project"] = slug;
17014
+ }
17015
+ }
17016
+ return headers;
17017
+ }
17018
+ function trimRightSlash3(url2) {
17019
+ return url2.endsWith("/") ? url2.slice(0, -1) : url2;
17020
+ }
17021
+
17022
+ // src/lib/credentials-lock.ts
17023
+ import { closeSync, constants, mkdirSync as mkdirSync2, openSync, statSync as statSync2, unlinkSync } from "fs";
17024
+ import { dirname as dirname2 } from "path";
17025
+ var LOCK_FILE = `${CREDENTIALS_FILE}.lock`;
17026
+ var MAX_RETRIES = 50;
17027
+ var RETRY_INTERVAL_MS = 100;
17028
+ var STALE_LOCK_MS = 3e4;
17029
+ var sleepView = new Int32Array(new SharedArrayBuffer(4));
17030
+ function syncSleep(ms) {
17031
+ Atomics.wait(sleepView, 0, 0, ms);
17032
+ }
17033
+ var lockDepth = 0;
17034
+ function withCredentialsLockSync(fn) {
17035
+ if (lockDepth > 0) {
17036
+ lockDepth++;
17037
+ try {
17038
+ return fn();
17039
+ } finally {
17040
+ lockDepth--;
17041
+ }
17042
+ }
17043
+ acquireLock();
17044
+ lockDepth++;
17045
+ try {
17046
+ return fn();
17047
+ } finally {
17048
+ lockDepth--;
17049
+ releaseLock();
17050
+ }
17051
+ }
17052
+ function acquireLock() {
17053
+ mkdirSync2(dirname2(LOCK_FILE), { recursive: true, mode: 448 });
17054
+ for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
17055
+ try {
17056
+ const fd = openSync(
17057
+ LOCK_FILE,
17058
+ constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL,
17059
+ 384
17060
+ );
17061
+ closeSync(fd);
17062
+ return;
17063
+ } catch (err) {
17064
+ if (err.code !== "EEXIST") throw err;
17065
+ try {
17066
+ const stats = statSync2(LOCK_FILE);
17067
+ if (Date.now() - stats.mtimeMs > STALE_LOCK_MS) {
17068
+ try {
17069
+ unlinkSync(LOCK_FILE);
17070
+ } catch {
17071
+ }
17072
+ continue;
17073
+ }
17074
+ } catch {
17075
+ continue;
17076
+ }
17077
+ syncSleep(RETRY_INTERVAL_MS);
17078
+ }
17079
+ }
17080
+ throw new Error(
17081
+ `Could not acquire credentials lock at ${LOCK_FILE} after ${MAX_RETRIES * RETRY_INTERVAL_MS / 1e3}s. Is another codevector command running? If you're sure none are, delete the lockfile manually.`
17082
+ );
17083
+ }
17084
+ function releaseLock() {
17085
+ try {
17086
+ unlinkSync(LOCK_FILE);
17087
+ } catch {
17088
+ }
17089
+ }
17090
+
17091
+ // src/lib/credentials.ts
17092
+ var ToolConfigSchema = external_exports.object({
17093
+ tool: external_exports.string().min(1),
17094
+ scope: external_exports.enum(["user", "project", "local"]),
17095
+ modelSlug: external_exports.string().optional()
17096
+ });
17097
+ var ProfileSchema = external_exports.object({
17098
+ apiKey: external_exports.string().min(1),
17099
+ gatewayUrl: external_exports.url(),
17100
+ userId: external_exports.uuid(),
17101
+ email: external_exports.email(),
17102
+ savedAt: external_exports.iso.datetime(),
17103
+ toolConfigs: external_exports.array(ToolConfigSchema).optional()
17104
+ });
17105
+ var ProfilesFileSchema = external_exports.object({
17106
+ activeProfile: external_exports.string().min(1),
17107
+ profiles: external_exports.record(external_exports.string().min(1), ProfileSchema)
17108
+ });
17109
+ function writeProfilesFile(payload) {
17110
+ const parsed = ProfilesFileSchema.parse(payload);
17111
+ const dir = dirname3(CREDENTIALS_FILE);
17112
+ mkdirSync3(dir, { recursive: true, mode: 448 });
17113
+ try {
17114
+ chmodSync2(dir, 448);
17115
+ } catch {
17116
+ }
17117
+ const tmp = `${CREDENTIALS_FILE}.${process.pid}.tmp`;
17118
+ writeFileSync4(tmp, JSON.stringify(parsed, null, 2), { mode: 384 });
17119
+ try {
17120
+ chmodSync2(tmp, 384);
17121
+ } catch {
17122
+ }
17123
+ renameSync2(tmp, CREDENTIALS_FILE);
17124
+ }
17125
+ function readProfilesSync() {
17126
+ let raw;
17127
+ try {
17128
+ raw = readFileSync5(CREDENTIALS_FILE, "utf8");
17129
+ } catch (err) {
17130
+ if (err.code === "ENOENT") return null;
17131
+ throw err;
17132
+ }
17133
+ let parsed;
17134
+ try {
17135
+ parsed = JSON.parse(raw);
17136
+ } catch {
17137
+ throw new Error(
17138
+ `${CREDENTIALS_FILE} is not valid JSON. Run \`codevector auth logout\` and re-login.`
17139
+ );
17140
+ }
17141
+ if (parsed && typeof parsed === "object" && "profiles" in parsed) {
17142
+ return ProfilesFileSchema.parse(parsed);
17143
+ }
17144
+ return withCredentialsLockSync(() => {
17145
+ let lockedRaw;
17146
+ try {
17147
+ lockedRaw = readFileSync5(CREDENTIALS_FILE, "utf8");
17148
+ } catch (err) {
17149
+ if (err.code === "ENOENT") {
17150
+ return null;
17151
+ }
17152
+ throw err;
17153
+ }
17154
+ const lockedParsed = JSON.parse(lockedRaw);
17155
+ if (lockedParsed && typeof lockedParsed === "object" && "profiles" in lockedParsed) {
17156
+ return ProfilesFileSchema.parse(lockedParsed);
17157
+ }
17158
+ const oldProfile = ProfileSchema.parse(lockedParsed);
17159
+ const seeded = seedToolConfigsFromDisk();
17160
+ const defaultProfile = {
17161
+ ...oldProfile,
17162
+ ...seeded.length > 0 ? { toolConfigs: seeded } : {}
17163
+ };
17164
+ const migrated = {
17165
+ activeProfile: "default",
17166
+ profiles: { default: defaultProfile }
17167
+ };
17168
+ try {
17169
+ writeProfilesFile(migrated);
17170
+ } catch {
17171
+ }
17172
+ return migrated;
17173
+ });
17174
+ }
17175
+ function seedToolConfigsFromDisk() {
17176
+ const out = [];
17177
+ const claude = detectClaudeCodeConfig();
17178
+ if (claude) {
17179
+ out.push({
17180
+ tool: "claude-code",
17181
+ scope: claude.scope,
17182
+ ...claude.modelSlug ? { modelSlug: claude.modelSlug } : {}
17183
+ });
17184
+ }
17185
+ const opencode = detectOpencodeConfig();
17186
+ if (opencode) {
17187
+ out.push({
17188
+ tool: "opencode",
17189
+ scope: opencode.scope,
17190
+ ...opencode.modelSlug ? { modelSlug: opencode.modelSlug } : {}
17191
+ });
17192
+ }
17193
+ return out;
17194
+ }
17195
+ async function readProfiles() {
17196
+ return readProfilesSync();
17197
+ }
17198
+ async function getActiveProfile() {
17199
+ const profiles = readProfilesSync();
17200
+ if (!profiles) return null;
17201
+ return profiles.profiles[profiles.activeProfile] ?? null;
17202
+ }
17203
+ async function setActiveProfile(name) {
17204
+ withCredentialsLockSync(() => {
17205
+ const profiles = readProfilesSync();
17206
+ if (!profiles) {
17207
+ throw new Error("No profiles configured. Run `codevector auth login` first.");
17208
+ }
17209
+ if (!profiles.profiles[name]) {
17210
+ throw new Error(`Profile "${name}" does not exist.`);
17211
+ }
17212
+ profiles.activeProfile = name;
17213
+ writeProfilesFile(profiles);
17214
+ });
17215
+ }
17216
+ function writeProfile(name, creds, toolConfigs) {
17217
+ return withCredentialsLockSync(() => {
17218
+ const existing = readProfilesSync();
17219
+ const resolvedToolConfigs = toolConfigs ?? existing?.profiles[name]?.toolConfigs;
17220
+ const payload = {
17221
+ ...creds,
17222
+ savedAt: (/* @__PURE__ */ new Date()).toISOString(),
17223
+ ...resolvedToolConfigs ? { toolConfigs: resolvedToolConfigs } : {}
17224
+ };
17225
+ const parsed = ProfileSchema.parse(payload);
17226
+ const profiles = existing ?? { activeProfile: name, profiles: {} };
17227
+ profiles.profiles[name] = parsed;
17228
+ profiles.activeProfile = name;
17229
+ writeProfilesFile(profiles);
17230
+ return parsed;
17231
+ });
17232
+ }
17233
+ async function readCredentials() {
17234
+ return getActiveProfile();
17235
+ }
17236
+ function deleteProfile(name) {
17237
+ return withCredentialsLockSync(() => {
17238
+ const profiles = readProfilesSync();
17239
+ if (!profiles) return false;
17240
+ if (!profiles.profiles[name]) return false;
17241
+ delete profiles.profiles[name];
17242
+ const remaining = Object.keys(profiles.profiles);
17243
+ if (remaining.length === 0) {
17244
+ rmSync(CREDENTIALS_FILE, { force: true });
17245
+ return true;
17246
+ }
17247
+ if (profiles.activeProfile === name) {
17248
+ const next = remaining.sort((a, b2) => {
17249
+ if (a === "default") return -1;
17250
+ if (b2 === "default") return 1;
17251
+ return a.localeCompare(b2);
17252
+ })[0];
17253
+ profiles.activeProfile = next;
17254
+ }
17255
+ writeProfilesFile(profiles);
17256
+ return true;
17257
+ });
17258
+ }
17259
+ function updateProfileToolConfigs(name, configs) {
17260
+ withCredentialsLockSync(() => {
17261
+ const profiles = readProfilesSync();
17262
+ if (!profiles || !profiles.profiles[name]) {
17263
+ throw new Error(`Profile "${name}" does not exist.`);
17264
+ }
17265
+ const profile = profiles.profiles[name];
17266
+ const existing = profile.toolConfigs ?? [];
17267
+ const byTool = new Map(existing.map((c) => [c.tool, c]));
17268
+ for (const cfg of configs) {
17269
+ byTool.set(cfg.tool, cfg);
17270
+ }
17271
+ profile.toolConfigs = Array.from(byTool.values());
17272
+ writeProfilesFile(profiles);
17273
+ });
17274
+ }
17275
+ function credentialsFileModeOk() {
17276
+ try {
17277
+ const stats = statSync3(CREDENTIALS_FILE);
17278
+ const mode = stats.mode & 511;
17279
+ return mode === 384;
17280
+ } catch {
17281
+ return false;
17282
+ }
17283
+ }
17284
+ function maskApiKey(key) {
17285
+ if (key.length <= 12) return "********";
17286
+ return `${key.slice(0, 5)}\u2026${key.slice(-4)}`;
17287
+ }
17288
+
17289
+ // src/lib/prompt.ts
17290
+ function unwrap(value) {
17291
+ if (q(value)) {
17292
+ me("Cancelled.");
17293
+ process.exit(130);
17294
+ }
17295
+ return value;
17296
+ }
17297
+ async function promptText(message, placeholder) {
17298
+ return unwrap(
17299
+ await Pe({
16601
17300
  message,
16602
17301
  placeholder,
16603
17302
  validate: (v2) => !v2 || v2.trim().length === 0 ? "Required." : void 0
@@ -16627,10 +17326,63 @@ var authLoginCommand = defineCommand({
16627
17326
  "api-key": {
16628
17327
  type: "string",
16629
17328
  description: "API key. Prompted (masked) if omitted."
17329
+ },
17330
+ profile: {
17331
+ type: "string",
17332
+ description: "Profile name to create or update. Prompted if omitted."
16630
17333
  }
16631
17334
  },
16632
17335
  async run({ args }) {
16633
17336
  ge("codevector auth login");
17337
+ const profiles = await readProfiles();
17338
+ const existingNames = profiles ? Object.keys(profiles.profiles) : [];
17339
+ const hasDefault = existingNames.includes("default");
17340
+ let profileName;
17341
+ if (args.profile) {
17342
+ const trimmed = args.profile.trim();
17343
+ if (!trimmed || trimmed === "__create__") {
17344
+ R2.error(
17345
+ trimmed ? `"__create__" is a reserved name. Choose a different profile name.` : "Profile name cannot be empty."
17346
+ );
17347
+ process.exitCode = 1;
17348
+ return;
17349
+ }
17350
+ profileName = trimmed;
17351
+ } else {
17352
+ const options = [
17353
+ { value: "default", label: "default", hint: hasDefault ? "existing" : "new" },
17354
+ { value: "__create__", label: "Create new profile" },
17355
+ ...existingNames.filter((n) => n !== "default").sort().map((name) => ({ value: name, label: name, hint: "existing" }))
17356
+ ];
17357
+ const selected = unwrap(
17358
+ await xe({
17359
+ message: "Select Profile",
17360
+ options,
17361
+ initialValue: profiles?.activeProfile ?? "default"
17362
+ })
17363
+ );
17364
+ if (selected === "__create__") {
17365
+ const name = unwrap(
17366
+ await Pe({
17367
+ message: "Profile Name",
17368
+ validate: (v2) => {
17369
+ const trimmed = v2?.trim();
17370
+ if (!trimmed) return "Required.";
17371
+ if (trimmed === "default")
17372
+ return `"default" is reserved. Select "default" from the list instead.`;
17373
+ if (trimmed === "__create__")
17374
+ return `"__create__" is a reserved name. Choose a different name.`;
17375
+ if (existingNames.includes(trimmed))
17376
+ return `Profile "${trimmed}" already exists. Choose a different name or select it from the list to update it.`;
17377
+ return void 0;
17378
+ }
17379
+ })
17380
+ );
17381
+ profileName = name.trim();
17382
+ } else {
17383
+ profileName = selected;
17384
+ }
17385
+ }
16634
17386
  const gatewayUrl = (args["gateway-url"] ?? await promptText("Gateway URL", "https://...")).trim();
16635
17387
  if (!gatewayUrl) throw new Error("Gateway URL is required.");
16636
17388
  assertHttpsUrl(gatewayUrl);
@@ -16642,15 +17394,16 @@ var authLoginCommand = defineCommand({
16642
17394
  try {
16643
17395
  const me2 = await translateErrors(() => call(parseResponse(client.me.$get())));
16644
17396
  const config2 = await translateErrors(() => call(parseResponse(client.config.$get())));
16645
- const stored = writeCredentials({
17397
+ const stored = writeProfile(profileName, {
16646
17398
  apiKey,
16647
17399
  gatewayUrl: config2.gatewayPublicUrl,
16648
17400
  userId: me2.user.id,
16649
17401
  email: me2.user.email
16650
17402
  });
16651
- s.stop(`Signed in as ${stored.email}`);
17403
+ s.stop(`Signed in as ${stored.email} (${profileName})`);
16652
17404
  Se(
16653
17405
  `Gateway: ${stored.gatewayUrl}
17406
+ Profile: ${profileName}
16654
17407
  Next: run \`codevector configure claude-code\`.`,
16655
17408
  "Credentials saved"
16656
17409
  );
@@ -16665,16 +17418,50 @@ Next: run \`codevector configure claude-code\`.`,
16665
17418
  var authLogoutCommand = defineCommand({
16666
17419
  meta: {
16667
17420
  name: "logout",
16668
- description: "Delete local credentials."
17421
+ description: "Delete local credentials for a profile."
16669
17422
  },
16670
- run() {
16671
- const removed = deleteCredentials();
17423
+ args: {
17424
+ profile: {
17425
+ type: "string",
17426
+ description: "Profile to log out of. Defaults to the active profile."
17427
+ }
17428
+ },
17429
+ async run({ args }) {
17430
+ const profiles = await readProfiles();
17431
+ if (!profiles || Object.keys(profiles.profiles).length === 0) {
17432
+ R2.info("No credentials were found. Nothing to do.");
17433
+ return;
17434
+ }
17435
+ let target = args.profile;
17436
+ if (!target) {
17437
+ const names = Object.keys(profiles.profiles);
17438
+ if (names.length === 1) {
17439
+ target = names[0];
17440
+ } else {
17441
+ const sortedNames = [...names].sort((a, b2) => {
17442
+ if (a === "default") return -1;
17443
+ if (b2 === "default") return 1;
17444
+ return a.localeCompare(b2);
17445
+ });
17446
+ const options = sortedNames.map((n) => ({
17447
+ value: n,
17448
+ label: n,
17449
+ hint: profiles.activeProfile === n ? "active" : void 0
17450
+ }));
17451
+ target = unwrap(
17452
+ await xe({
17453
+ message: "Select profile to log out of",
17454
+ options,
17455
+ initialValue: profiles.activeProfile
17456
+ })
17457
+ );
17458
+ }
17459
+ }
17460
+ const removed = deleteProfile(target);
16672
17461
  if (removed) {
16673
- R2.success(
16674
- "Logged out. Hooks are left in place \u2014 run `codevector auth login` to sign back in."
16675
- );
17462
+ R2.success(`Logged out of profile "${target}".`);
16676
17463
  } else {
16677
- R2.info("No credentials were found. Nothing to do.");
17464
+ R2.info(`Profile "${target}" not found. Nothing to do.`);
16678
17465
  }
16679
17466
  }
16680
17467
  });
@@ -16756,29 +17543,29 @@ function assertHttpsUrl(urlString) {
16756
17543
  import { homedir as homedir5 } from "os";
16757
17544
 
16758
17545
  // src/lib/hooks.ts
16759
- import { chmodSync as chmodSync2, copyFileSync, existsSync, mkdirSync as mkdirSync2 } from "fs";
16760
- import { dirname as dirname2, join as join2 } from "path";
17546
+ import { chmodSync as chmodSync3, copyFileSync, existsSync as existsSync4, mkdirSync as mkdirSync4 } from "fs";
17547
+ import { dirname as dirname4, join as join5 } from "path";
16761
17548
  import { fileURLToPath } from "url";
16762
17549
  function bundledHookSource() {
16763
- const here = dirname2(fileURLToPath(import.meta.url));
17550
+ const here = dirname4(fileURLToPath(import.meta.url));
16764
17551
  const candidates = [
16765
- join2(here, "hooks", "acceptance.sh"),
17552
+ join5(here, "hooks", "acceptance.sh"),
16766
17553
  // production — dist/hooks/…
16767
- join2(here, "..", "hooks", "acceptance.sh")
17554
+ join5(here, "..", "hooks", "acceptance.sh")
16768
17555
  // dev — src/hooks/…
16769
17556
  ];
16770
17557
  for (const candidate of candidates) {
16771
- if (existsSync(candidate)) return candidate;
17558
+ if (existsSync4(candidate)) return candidate;
16772
17559
  }
16773
17560
  throw new Error(
16774
17561
  "Could not locate the bundled acceptance hook script (acceptance.sh). Reinstall @codevector/cli to repair the installation."
16775
17562
  );
16776
17563
  }
16777
17564
  function installAcceptanceHook() {
16778
- mkdirSync2(HOOKS_DIR, { recursive: true, mode: 448 });
17565
+ mkdirSync4(HOOKS_DIR, { recursive: true, mode: 448 });
16779
17566
  copyFileSync(bundledHookSource(), ACCEPTANCE_HOOK_FILE);
16780
17567
  try {
16781
- chmodSync2(ACCEPTANCE_HOOK_FILE, 493);
17568
+ chmodSync3(ACCEPTANCE_HOOK_FILE, 493);
16782
17569
  } catch {
16783
17570
  }
16784
17571
  return ACCEPTANCE_HOOK_FILE;
@@ -16786,8 +17573,8 @@ function installAcceptanceHook() {
16786
17573
 
16787
17574
  // src/lib/project-context.ts
16788
17575
  import { execFileSync } from "child_process";
16789
- import { existsSync as existsSync2, readFileSync } from "fs";
16790
- import { join as join3 } from "path";
17576
+ import { existsSync as existsSync5, readFileSync as readFileSync6 } from "fs";
17577
+ import { join as join6 } from "path";
16791
17578
  var DEFAULT_TICKET_PATTERN = /[A-Z]+-\d+/;
16792
17579
  function safeGit(args, cwd) {
16793
17580
  try {
@@ -16818,227 +17605,52 @@ function resolveProjectContext(cwd = process.cwd(), ticketPattern = DEFAULT_TICK
16818
17605
  const remote = safeGit(["remote", "get-url", "origin"], cwd);
16819
17606
  if (remote) project = repoSlugFromRemote(remote);
16820
17607
  }
16821
- const branch = safeGit(["branch", "--show-current"], cwd);
16822
- if (branch) {
16823
- const match = effectivePattern.exec(branch);
16824
- if (match) ticket = match[0];
16825
- }
16826
- return { project, ticket };
16827
- }
16828
- function readLocalConfig(cwd) {
16829
- const path = join3(cwd, ".codevector.json");
16830
- if (!existsSync2(path)) return null;
16831
- try {
16832
- const parsed = JSON.parse(readFileSync(path, "utf8"));
16833
- if (typeof parsed !== "object" || parsed === null) return null;
16834
- const p2 = parsed;
16835
- const result = {};
16836
- if (typeof p2.projectName === "string" && p2.projectName.length > 0) {
16837
- result.projectName = p2.projectName;
16838
- }
16839
- if (typeof p2.ticketPattern === "string" && p2.ticketPattern.length > 0) {
16840
- result.ticketPattern = p2.ticketPattern;
16841
- }
16842
- return result;
16843
- } catch {
16844
- return null;
16845
- }
16846
- }
16847
- function safeCompileTicketPattern(pattern) {
16848
- try {
16849
- return new RegExp(pattern);
16850
- } catch {
16851
- return null;
16852
- }
16853
- }
16854
-
16855
- // src/config-writers/claude-code.ts
16856
- import { homedir as homedir2 } from "os";
16857
- import { join as join4 } from "path";
16858
-
16859
- // src/lib/merge-json.ts
16860
- import { chmodSync as chmodSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync2, renameSync as renameSync2, statSync as statSync2, writeFileSync as writeFileSync2 } from "fs";
16861
- import { dirname as dirname3 } from "path";
16862
- function isPlainObject2(v2) {
16863
- return typeof v2 === "object" && v2 !== null && !Array.isArray(v2);
16864
- }
16865
- function deepMerge2(base, patch, opts = {}, currentPath = []) {
16866
- const hookPaths = new Set(opts.hookArrayPaths ?? []);
16867
- const dedupeKey = opts.hookDedupeKey ?? "command";
16868
- const out = { ...base };
16869
- for (const [k2, v2] of Object.entries(patch)) {
16870
- const nextPath = [...currentPath, k2];
16871
- const pathStr = nextPath.join(".");
16872
- const existing = out[k2];
16873
- if (isPlainObject2(v2) && isPlainObject2(existing)) {
16874
- out[k2] = deepMerge2(existing, v2, opts, nextPath);
16875
- continue;
16876
- }
16877
- if (Array.isArray(v2) && Array.isArray(existing) && hookPaths.has(pathStr)) {
16878
- out[k2] = mergeHookArray(existing, v2, dedupeKey);
16879
- continue;
16880
- }
16881
- out[k2] = v2;
16882
- }
16883
- return out;
16884
- }
16885
- function extractHookCommands(entry, dedupeKey) {
16886
- if (!isPlainObject2(entry)) return [];
16887
- if (typeof entry[dedupeKey] === "string") return [entry[dedupeKey]];
16888
- const inner = entry.hooks;
16889
- if (!Array.isArray(inner)) return [];
16890
- return inner.filter(isPlainObject2).map((h2) => h2[dedupeKey]).filter((x) => typeof x === "string");
16891
- }
16892
- function mergeHookArray(existing, patch, dedupeKey) {
16893
- const patchCommands = new Set(patch.flatMap((e2) => extractHookCommands(e2, dedupeKey)));
16894
- const kept = existing.filter((entry) => {
16895
- if (!isPlainObject2(entry) || typeof entry[dedupeKey] !== "string") return true;
16896
- return !patchCommands.has(entry[dedupeKey]);
16897
- });
16898
- const keptCommands = new Set(kept.flatMap((e2) => extractHookCommands(e2, dedupeKey)));
16899
- const additions = patch.filter((entry) => {
16900
- const cmds = extractHookCommands(entry, dedupeKey);
16901
- if (cmds.length === 0) return true;
16902
- return !cmds.some((c2) => keptCommands.has(c2));
16903
- });
16904
- return [...kept, ...additions];
16905
- }
16906
- function mergeJsonFile(filePath, patch, opts = {}) {
16907
- const dir = dirname3(filePath);
16908
- mkdirSync3(dir, { recursive: true, mode: 448 });
16909
- let existing = {};
16910
- let mode;
16911
- try {
16912
- const raw = readFileSync2(filePath, "utf8");
16913
- if (raw.trim().length > 0) {
16914
- const parsed = JSON.parse(raw);
16915
- if (isPlainObject2(parsed)) existing = parsed;
16916
- else {
16917
- throw new Error(
16918
- `${filePath} contains non-object JSON (${Array.isArray(parsed) ? "array" : typeof parsed}). Refusing to overwrite.`
16919
- );
16920
- }
16921
- }
16922
- mode = statSync2(filePath).mode & 511;
16923
- } catch (err) {
16924
- if (err.code !== "ENOENT") throw err;
16925
- }
16926
- const merged = deepMerge2(existing, patch, opts);
16927
- const tmp = `${filePath}.${process.pid}.tmp`;
16928
- writeFileSync2(tmp, `${JSON.stringify(merged, null, 2)}
16929
- `);
16930
- if (mode !== void 0) {
16931
- try {
16932
- chmodSync3(tmp, mode);
16933
- } catch {
16934
- }
16935
- }
16936
- renameSync2(tmp, filePath);
16937
- return merged;
16938
- }
16939
-
16940
- // src/config-writers/claude-code.ts
16941
- var writeClaudeCodeConfig = ({
16942
- gatewayUrl,
16943
- apiKey,
16944
- hookScriptPath,
16945
- scope,
16946
- project,
16947
- model,
16948
- availableModels = []
16949
- }) => {
16950
- const path = claudeSettingsPath(scope);
16951
- const baseUrl = `${trimRightSlash2(gatewayUrl)}/gateway/anthropic`;
16952
- const env = {
16953
- ANTHROPIC_BASE_URL: baseUrl,
16954
- ANTHROPIC_AUTH_TOKEN: apiKey,
16955
- ANTHROPIC_CUSTOM_HEADERS: buildCustomHeaders(scope, project)
16956
- };
16957
- for (const [key, value] of Object.entries(resolveTierDefaults(availableModels))) {
16958
- env[key] = value;
16959
- }
16960
- const patch = {
16961
- env,
16962
- hooks: {
16963
- Stop: [{ matcher: "*", hooks: [{ type: "command", command: hookScriptPath }] }],
16964
- SessionEnd: [{ matcher: "*", hooks: [{ type: "command", command: hookScriptPath }] }]
16965
- }
16966
- };
16967
- if (model) patch.model = model.slug;
16968
- if (availableModels.length > 0) {
16969
- patch.availableModels = availableModels.map((m) => m.slug).sort();
16970
- }
16971
- mergeJsonFile(path, patch, {
16972
- hookArrayPaths: ["hooks.Stop", "hooks.SessionEnd"],
16973
- hookDedupeKey: "command"
16974
- });
16975
- const notes = [];
16976
- if (scope === "local") notes.push("Claude Code auto-ignores settings.local.json in git.");
16977
- if (scope !== "user" && !project) {
16978
- notes.push(
16979
- "Project slug could not be derived (no git remote and no .codevector.json). Per-project usage attribution will be blank until you set one."
16980
- );
16981
- }
16982
- return {
16983
- tool: "claude-code",
16984
- status: "configured",
16985
- path,
16986
- scope,
16987
- notes: notes.length > 0 ? notes.join(" ") : void 0
16988
- };
16989
- };
16990
- function claudeSettingsPath(scope) {
16991
- switch (scope) {
16992
- case "user":
16993
- return join4(homedir2(), ".claude", "settings.json");
16994
- case "project":
16995
- return join4(userCwd(), ".claude", "settings.json");
16996
- case "local":
16997
- return join4(userCwd(), ".claude", "settings.local.json");
16998
- }
17608
+ const branch = safeGit(["branch", "--show-current"], cwd);
17609
+ if (branch) {
17610
+ const match = effectivePattern.exec(branch);
17611
+ if (match) ticket = match[0];
17612
+ }
17613
+ return { project, ticket };
16999
17614
  }
17000
- function resolveTierDefaults(available) {
17001
- const out = {};
17002
- const tierToEnv = {
17003
- haiku: "ANTHROPIC_DEFAULT_HAIKU_MODEL",
17004
- sonnet: "ANTHROPIC_DEFAULT_SONNET_MODEL",
17005
- opus: "ANTHROPIC_DEFAULT_OPUS_MODEL"
17006
- };
17007
- for (const [tier, envKey] of Object.entries(tierToEnv)) {
17008
- const matches = available.filter(
17009
- (m) => typeof m.family === "string" && m.family.toLowerCase().includes(tier)
17010
- );
17011
- if (matches.length === 1) {
17012
- out[envKey] = matches[0].slug;
17615
+ function readLocalConfig(cwd) {
17616
+ const path = join6(cwd, ".codevector.json");
17617
+ if (!existsSync5(path)) return null;
17618
+ try {
17619
+ const parsed = JSON.parse(readFileSync6(path, "utf8"));
17620
+ if (typeof parsed !== "object" || parsed === null) return null;
17621
+ const p2 = parsed;
17622
+ const result = {};
17623
+ if (typeof p2.projectName === "string" && p2.projectName.length > 0) {
17624
+ result.projectName = p2.projectName;
17625
+ }
17626
+ if (typeof p2.ticketPattern === "string" && p2.ticketPattern.length > 0) {
17627
+ result.ticketPattern = p2.ticketPattern;
17013
17628
  }
17629
+ return result;
17630
+ } catch {
17631
+ return null;
17014
17632
  }
17015
- return out;
17016
17633
  }
17017
- function buildCustomHeaders(scope, project) {
17018
- const safe = (v2) => v2.replace(/[\r\n]/g, "").trim();
17019
- const headers = [`x-client-app: claude-code`];
17020
- if (scope !== "user" && project) {
17021
- const slug = safe(project);
17022
- if (slug) headers.push(`x-project: ${slug}`);
17634
+ function safeCompileTicketPattern(pattern) {
17635
+ try {
17636
+ return new RegExp(pattern);
17637
+ } catch {
17638
+ return null;
17023
17639
  }
17024
- return headers.join("\n");
17025
- }
17026
- function trimRightSlash2(url2) {
17027
- return url2.endsWith("/") ? url2.slice(0, -1) : url2;
17028
17640
  }
17029
17641
 
17030
17642
  // src/config-writers/codex.ts
17031
17643
  import {
17032
17644
  chmodSync as chmodSync4,
17033
- existsSync as existsSync3,
17034
- mkdirSync as mkdirSync4,
17035
- readFileSync as readFileSync3,
17645
+ existsSync as existsSync6,
17646
+ mkdirSync as mkdirSync5,
17647
+ readFileSync as readFileSync7,
17036
17648
  renameSync as renameSync3,
17037
- statSync as statSync3,
17038
- writeFileSync as writeFileSync3
17649
+ statSync as statSync4,
17650
+ writeFileSync as writeFileSync5
17039
17651
  } from "fs";
17040
- import { dirname as dirname4, join as join5 } from "path";
17041
- import { homedir as homedir3 } from "os";
17652
+ import { dirname as dirname5, join as join7 } from "path";
17653
+ import { homedir as homedir4 } from "os";
17042
17654
 
17043
17655
  // ../../node_modules/.pnpm/smol-toml@1.6.1/node_modules/smol-toml/dist/error.js
17044
17656
  function getLineColFromPtr(string4, ptr) {
@@ -17095,12 +17707,12 @@ function indexOfNewline(str, start = 0, end = str.length) {
17095
17707
  }
17096
17708
  function skipComment(str, ptr) {
17097
17709
  for (let i = ptr; i < str.length; i++) {
17098
- let c2 = str[i];
17099
- if (c2 === "\n")
17710
+ let c = str[i];
17711
+ if (c === "\n")
17100
17712
  return i;
17101
- if (c2 === "\r" && str[i + 1] === "\n")
17713
+ if (c === "\r" && str[i + 1] === "\n")
17102
17714
  return i + 1;
17103
- if (c2 < " " && c2 !== " " || c2 === "\x7F") {
17715
+ if (c < " " && c !== " " || c === "\x7F") {
17104
17716
  throw new TomlError("control characters are not allowed in comments", {
17105
17717
  toml: str,
17106
17718
  ptr
@@ -17110,11 +17722,11 @@ function skipComment(str, ptr) {
17110
17722
  return str.length;
17111
17723
  }
17112
17724
  function skipVoid(str, ptr, banNewLines, banComments) {
17113
- let c2;
17725
+ let c;
17114
17726
  while (1) {
17115
- while ((c2 = str[ptr]) === " " || c2 === " " || !banNewLines && (c2 === "\n" || c2 === "\r" && str[ptr + 1] === "\n"))
17727
+ while ((c = str[ptr]) === " " || c === " " || !banNewLines && (c === "\n" || c === "\r" && str[ptr + 1] === "\n"))
17116
17728
  ptr++;
17117
- if (banComments || c2 !== "#")
17729
+ if (banComments || c !== "#")
17118
17730
  break;
17119
17731
  ptr = skipComment(str, ptr);
17120
17732
  }
@@ -17126,12 +17738,12 @@ function skipUntil(str, ptr, sep, end, banNewLines = false) {
17126
17738
  return ptr < 0 ? str.length : ptr;
17127
17739
  }
17128
17740
  for (let i = ptr; i < str.length; i++) {
17129
- let c2 = str[i];
17130
- if (c2 === "#") {
17741
+ let c = str[i];
17742
+ if (c === "#") {
17131
17743
  i = indexOfNewline(str, i);
17132
- } else if (c2 === sep) {
17744
+ } else if (c === sep) {
17133
17745
  return i + 1;
17134
- } else if (c2 === end || banNewLines && (c2 === "\n" || c2 === "\r" && str[i + 1] === "\n")) {
17746
+ } else if (c === end || banNewLines && (c === "\n" || c === "\r" && str[i + 1] === "\n")) {
17135
17747
  return i;
17136
17748
  }
17137
17749
  }
@@ -17281,15 +17893,15 @@ function parseString(str, ptr = 0, endPtr = str.length) {
17281
17893
  let parsed = "";
17282
17894
  let sliceStart = ptr;
17283
17895
  while (ptr < endPtr - 1) {
17284
- let c2 = str[ptr++];
17285
- if (c2 === "\n" || c2 === "\r" && str[ptr] === "\n") {
17896
+ let c = str[ptr++];
17897
+ if (c === "\n" || c === "\r" && str[ptr] === "\n") {
17286
17898
  if (!isMultiline) {
17287
17899
  throw new TomlError("newlines are not allowed in strings", {
17288
17900
  toml: str,
17289
17901
  ptr: ptr - 1
17290
17902
  });
17291
17903
  }
17292
- } else if (c2 < " " && c2 !== " " || c2 === "\x7F") {
17904
+ } else if (c < " " && c !== " " || c === "\x7F") {
17293
17905
  throw new TomlError("control characters are not allowed in strings", {
17294
17906
  toml: str,
17295
17907
  ptr: ptr - 1
@@ -17297,8 +17909,8 @@ function parseString(str, ptr = 0, endPtr = str.length) {
17297
17909
  }
17298
17910
  if (isEscape) {
17299
17911
  isEscape = false;
17300
- if (c2 === "x" || c2 === "u" || c2 === "U") {
17301
- let code = str.slice(ptr, ptr += c2 === "x" ? 2 : c2 === "u" ? 4 : 8);
17912
+ if (c === "x" || c === "u" || c === "U") {
17913
+ let code = str.slice(ptr, ptr += c === "x" ? 2 : c === "u" ? 4 : 8);
17302
17914
  if (!ESCAPE_REGEX.test(code)) {
17303
17915
  throw new TomlError("invalid unicode escape", {
17304
17916
  toml: str,
@@ -17313,7 +17925,7 @@ function parseString(str, ptr = 0, endPtr = str.length) {
17313
17925
  ptr: tmp
17314
17926
  });
17315
17927
  }
17316
- } else if (isMultiline && (c2 === "\n" || c2 === " " || c2 === " " || c2 === "\r")) {
17928
+ } else if (isMultiline && (c === "\n" || c === " " || c === " " || c === "\r")) {
17317
17929
  ptr = skipVoid(str, ptr - 1, true);
17318
17930
  if (str[ptr] !== "\n" && str[ptr] !== "\r") {
17319
17931
  throw new TomlError("invalid escape: only line-ending whitespace may be escaped", {
@@ -17322,8 +17934,8 @@ function parseString(str, ptr = 0, endPtr = str.length) {
17322
17934
  });
17323
17935
  }
17324
17936
  ptr = skipVoid(str, ptr);
17325
- } else if (c2 in ESC_MAP) {
17326
- parsed += ESC_MAP[c2];
17937
+ } else if (c in ESC_MAP) {
17938
+ parsed += ESC_MAP[c];
17327
17939
  } else {
17328
17940
  throw new TomlError("unrecognized escape sequence", {
17329
17941
  toml: str,
@@ -17331,7 +17943,7 @@ function parseString(str, ptr = 0, endPtr = str.length) {
17331
17943
  });
17332
17944
  }
17333
17945
  sliceStart = ptr;
17334
- } else if (!isLiteral && c2 === "\\") {
17946
+ } else if (!isLiteral && c === "\\") {
17335
17947
  tmp = ptr - 1;
17336
17948
  isEscape = true;
17337
17949
  parsed += str.slice(sliceStart, tmp);
@@ -17407,9 +18019,9 @@ function extractValue(str, ptr, end, depth, integersAsBigInt) {
17407
18019
  ptr
17408
18020
  });
17409
18021
  }
17410
- let c2 = str[ptr];
17411
- if (c2 === "[" || c2 === "{") {
17412
- let [value, endPtr2] = c2 === "[" ? parseArray(str, ptr, depth, integersAsBigInt) : parseInlineTable(str, ptr, depth, integersAsBigInt);
18022
+ let c = str[ptr];
18023
+ if (c === "[" || c === "{") {
18024
+ let [value, endPtr2] = c === "[" ? parseArray(str, ptr, depth, integersAsBigInt) : parseInlineTable(str, ptr, depth, integersAsBigInt);
17413
18025
  if (end) {
17414
18026
  endPtr2 = skipVoid(str, endPtr2);
17415
18027
  if (str[endPtr2] === ",")
@@ -17424,7 +18036,7 @@ function extractValue(str, ptr, end, depth, integersAsBigInt) {
17424
18036
  return [value, endPtr2];
17425
18037
  }
17426
18038
  let endPtr;
17427
- if (c2 === '"' || c2 === "'") {
18039
+ if (c === '"' || c === "'") {
17428
18040
  endPtr = getStringEnd(str, ptr);
17429
18041
  let parsed = parseString(str, ptr, endPtr);
17430
18042
  if (end) {
@@ -17470,10 +18082,10 @@ function parseKey(str, ptr, end = "=") {
17470
18082
  });
17471
18083
  }
17472
18084
  do {
17473
- let c2 = str[ptr = ++dot];
17474
- if (c2 !== " " && c2 !== " ") {
17475
- if (c2 === '"' || c2 === "'") {
17476
- if (c2 === str[ptr + 1] && c2 === str[ptr + 2]) {
18085
+ let c = str[ptr = ++dot];
18086
+ if (c !== " " && c !== " ") {
18087
+ if (c === '"' || c === "'") {
18088
+ if (c === str[ptr + 1] && c === str[ptr + 2]) {
17477
18089
  throw new TomlError("multiline strings are not allowed in keys", {
17478
18090
  toml: str,
17479
18091
  ptr
@@ -17529,17 +18141,17 @@ function parseKey(str, ptr, end = "=") {
17529
18141
  function parseInlineTable(str, ptr, depth, integersAsBigInt) {
17530
18142
  let res = {};
17531
18143
  let seen = /* @__PURE__ */ new Set();
17532
- let c2;
18144
+ let c;
17533
18145
  ptr++;
17534
- while ((c2 = str[ptr++]) !== "}" && c2) {
17535
- if (c2 === ",") {
18146
+ while ((c = str[ptr++]) !== "}" && c) {
18147
+ if (c === ",") {
17536
18148
  throw new TomlError("expected value, found comma", {
17537
18149
  toml: str,
17538
18150
  ptr: ptr - 1
17539
18151
  });
17540
- } else if (c2 === "#")
18152
+ } else if (c === "#")
17541
18153
  ptr = skipComment(str, ptr);
17542
- else if (c2 !== " " && c2 !== " " && c2 !== "\n" && c2 !== "\r") {
18154
+ else if (c !== " " && c !== " " && c !== "\n" && c !== "\r") {
17543
18155
  let k2;
17544
18156
  let t = res;
17545
18157
  let hasOwn = false;
@@ -17570,7 +18182,7 @@ function parseInlineTable(str, ptr, depth, integersAsBigInt) {
17570
18182
  ptr = valueEndPtr;
17571
18183
  }
17572
18184
  }
17573
- if (!c2) {
18185
+ if (!c) {
17574
18186
  throw new TomlError("unfinished table encountered", {
17575
18187
  toml: str,
17576
18188
  ptr
@@ -17580,23 +18192,23 @@ function parseInlineTable(str, ptr, depth, integersAsBigInt) {
17580
18192
  }
17581
18193
  function parseArray(str, ptr, depth, integersAsBigInt) {
17582
18194
  let res = [];
17583
- let c2;
18195
+ let c;
17584
18196
  ptr++;
17585
- while ((c2 = str[ptr++]) !== "]" && c2) {
17586
- if (c2 === ",") {
18197
+ while ((c = str[ptr++]) !== "]" && c) {
18198
+ if (c === ",") {
17587
18199
  throw new TomlError("expected value, found comma", {
17588
18200
  toml: str,
17589
18201
  ptr: ptr - 1
17590
18202
  });
17591
- } else if (c2 === "#")
18203
+ } else if (c === "#")
17592
18204
  ptr = skipComment(str, ptr);
17593
- else if (c2 !== " " && c2 !== " " && c2 !== "\n" && c2 !== "\r") {
18205
+ else if (c !== " " && c !== " " && c !== "\n" && c !== "\r") {
17594
18206
  let e2 = extractValue(str, ptr - 1, "]", depth - 1, integersAsBigInt);
17595
18207
  res.push(e2[0]);
17596
18208
  ptr = e2[1];
17597
18209
  }
17598
18210
  }
17599
- if (!c2) {
18211
+ if (!c) {
17600
18212
  throw new TomlError("unfinished array encountered", {
17601
18213
  toml: str,
17602
18214
  ptr
@@ -17608,33 +18220,33 @@ function parseArray(str, ptr, depth, integersAsBigInt) {
17608
18220
  // ../../node_modules/.pnpm/smol-toml@1.6.1/node_modules/smol-toml/dist/parse.js
17609
18221
  function peekTable(key, table, meta3, type) {
17610
18222
  let t = table;
17611
- let m = meta3;
18223
+ let m2 = meta3;
17612
18224
  let k2;
17613
18225
  let hasOwn = false;
17614
18226
  let state;
17615
18227
  for (let i = 0; i < key.length; i++) {
17616
18228
  if (i) {
17617
18229
  t = hasOwn ? t[k2] : t[k2] = {};
17618
- m = (state = m[k2]).c;
18230
+ m2 = (state = m2[k2]).c;
17619
18231
  if (type === 0 && (state.t === 1 || state.t === 2)) {
17620
18232
  return null;
17621
18233
  }
17622
18234
  if (state.t === 2) {
17623
18235
  let l = t.length - 1;
17624
18236
  t = t[l];
17625
- m = m[l].c;
18237
+ m2 = m2[l].c;
17626
18238
  }
17627
18239
  }
17628
18240
  k2 = key[i];
17629
- if ((hasOwn = Object.hasOwn(t, k2)) && m[k2]?.t === 0 && m[k2]?.d) {
18241
+ if ((hasOwn = Object.hasOwn(t, k2)) && m2[k2]?.t === 0 && m2[k2]?.d) {
17630
18242
  return null;
17631
18243
  }
17632
18244
  if (!hasOwn) {
17633
18245
  if (k2 === "__proto__") {
17634
18246
  Object.defineProperty(t, k2, { enumerable: true, configurable: true, writable: true });
17635
- Object.defineProperty(m, k2, { enumerable: true, configurable: true, writable: true });
18247
+ Object.defineProperty(m2, k2, { enumerable: true, configurable: true, writable: true });
17636
18248
  }
17637
- m[k2] = {
18249
+ m2[k2] = {
17638
18250
  t: i < key.length - 1 && type === 2 ? 3 : type,
17639
18251
  d: false,
17640
18252
  i: 0,
@@ -17642,7 +18254,7 @@ function peekTable(key, table, meta3, type) {
17642
18254
  };
17643
18255
  }
17644
18256
  }
17645
- state = m[k2];
18257
+ state = m2[k2];
17646
18258
  if (state.t !== type && !(type === 1 && state.t === 3)) {
17647
18259
  return null;
17648
18260
  }
@@ -17669,7 +18281,7 @@ function parse3(toml, { maxDepth = 1e3, integersAsBigInt } = {}) {
17669
18281
  let res = {};
17670
18282
  let meta3 = {};
17671
18283
  let tbl = res;
17672
- let m = meta3;
18284
+ let m2 = meta3;
17673
18285
  for (let ptr = skipVoid(toml, 0); ptr < toml.length; ) {
17674
18286
  if (toml[ptr] === "[") {
17675
18287
  let isTableArray = toml[++ptr] === "[";
@@ -17696,7 +18308,7 @@ function parse3(toml, { maxDepth = 1e3, integersAsBigInt } = {}) {
17696
18308
  ptr
17697
18309
  });
17698
18310
  }
17699
- m = p2[2];
18311
+ m2 = p2[2];
17700
18312
  tbl = p2[1];
17701
18313
  ptr = k2[1];
17702
18314
  } else {
@@ -17704,7 +18316,7 @@ function parse3(toml, { maxDepth = 1e3, integersAsBigInt } = {}) {
17704
18316
  let p2 = peekTable(
17705
18317
  k2[0],
17706
18318
  tbl,
17707
- m,
18319
+ m2,
17708
18320
  0
17709
18321
  /* Type.DOTTED */
17710
18322
  );
@@ -17872,7 +18484,7 @@ function stringify(obj, { maxDepth = 1e3, numbersAsFloat = false } = {}) {
17872
18484
  }
17873
18485
 
17874
18486
  // src/config-writers/codex.ts
17875
- var ENV_VAR_NAME = "CODEVECTOR_GATEWAY_KEY";
18487
+ var ENV_VAR_NAME2 = "CODEVECTOR_GATEWAY_KEY";
17876
18488
  var PROVIDER_ID = "codevector";
17877
18489
  var writeCodexConfig = ({
17878
18490
  gatewayUrl,
@@ -17880,325 +18492,91 @@ var writeCodexConfig = ({
17880
18492
  model,
17881
18493
  availableModels = []
17882
18494
  }) => {
17883
- if (scope !== "user") {
17884
- return {
17885
- tool: "codex",
17886
- status: "skipped",
17887
- path: "",
17888
- scope,
17889
- notes: `codex only supports user-scope config. Re-run with \`--scope user\` or pick user in the prompt.`
17890
- };
17891
- }
17892
- const path = codexConfigPath();
17893
- const gateway = trimRightSlash3(gatewayUrl);
18495
+ const path = codexConfigPath(scope);
18496
+ const gateway = trimRightSlash4(gatewayUrl);
17894
18497
  const existing = readTomlOrEmpty(path);
17895
18498
  const merged = { ...existing };
17896
18499
  merged.model_provider = PROVIDER_ID;
17897
- const providers = isObject3(merged.model_providers) ? { ...merged.model_providers } : {};
18500
+ const providers = isObject4(merged.model_providers) ? { ...merged.model_providers } : {};
17898
18501
  providers[PROVIDER_ID] = {
17899
18502
  name: "CodeVector Gateway",
17900
18503
  base_url: `${gateway}/gateway/openai/v1`,
17901
- env_key: ENV_VAR_NAME,
18504
+ env_key: ENV_VAR_NAME2,
17902
18505
  wire_api: "responses"
17903
18506
  };
17904
18507
  merged.model_providers = providers;
17905
18508
  if (model) {
17906
18509
  merged.model = model.slug;
17907
- const meta3 = availableModels.find((m) => m.slug === model.slug);
18510
+ const meta3 = availableModels.find((m2) => m2.slug === model.slug);
17908
18511
  if (meta3?.supportsReasoning === true) {
17909
18512
  merged.model_reasoning_summary = "auto";
17910
18513
  merged.model_supports_reasoning_summaries = true;
18514
+ } else {
18515
+ delete merged.model_reasoning_summary;
18516
+ delete merged.model_supports_reasoning_summaries;
17911
18517
  }
18518
+ } else {
18519
+ delete merged.model;
18520
+ delete merged.model_reasoning_summary;
18521
+ delete merged.model_supports_reasoning_summaries;
17912
18522
  }
17913
18523
  writeTomlAtomic(path, merged);
17914
- return {
17915
- tool: "codex",
17916
- status: "configured",
17917
- path,
17918
- scope,
17919
- notes: `codex does not auto-load .env files. Add to your shell rc: export ${ENV_VAR_NAME}=<your key>`
17920
- };
17921
- };
17922
- function codexConfigPath() {
17923
- return join5(homedir3(), ".codex", "config.toml");
17924
- }
17925
- function readTomlOrEmpty(path) {
17926
- if (!existsSync3(path)) return {};
17927
- const raw = readFileSync3(path, "utf8");
17928
- if (raw.trim().length === 0) return {};
17929
- const parsed = parse3(raw);
17930
- if (!isObject3(parsed)) {
17931
- throw new Error(`${path} is not a TOML table. Refusing to overwrite.`);
17932
- }
17933
- return parsed;
17934
- }
17935
- function writeTomlAtomic(path, value) {
17936
- const dir = dirname4(path);
17937
- mkdirSync4(dir, { recursive: true, mode: 448 });
17938
- let mode;
17939
- if (existsSync3(path)) {
17940
- mode = statSync3(path).mode & 511;
17941
- }
17942
- const tmp = `${path}.${process.pid}.tmp`;
17943
- writeFileSync3(tmp, `${stringify(value)}
17944
- `);
17945
- if (mode !== void 0) {
17946
- try {
17947
- chmodSync4(tmp, mode);
17948
- } catch {
17949
- }
17950
- }
17951
- renameSync3(tmp, path);
17952
- }
17953
- function isObject3(v2) {
17954
- return typeof v2 === "object" && v2 !== null && !Array.isArray(v2);
17955
- }
17956
- function trimRightSlash3(url2) {
17957
- return url2.endsWith("/") ? url2.slice(0, -1) : url2;
17958
- }
17959
-
17960
- // src/config-writers/opencode.ts
17961
- import { existsSync as existsSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "fs";
17962
- import { homedir as homedir4 } from "os";
17963
- import { join as join7 } from "path";
17964
-
17965
- // src/lib/gitignore.ts
17966
- import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
17967
- import { join as join6 } from "path";
17968
- function ensureGitignored(pattern, cwd = userCwd()) {
17969
- const path = join6(cwd, ".gitignore");
17970
- const trimmed = pattern.trim();
17971
- if (!trimmed) return false;
17972
- let existing = "";
17973
- if (existsSync4(path)) {
17974
- existing = readFileSync4(path, "utf8");
17975
- if (hasMatchingEntry(existing, trimmed)) return false;
17976
- }
17977
- const needsLeadingNewline = existing.length > 0 && !existing.endsWith("\n");
17978
- const prefix = needsLeadingNewline ? "\n" : "";
17979
- try {
17980
- writeFileSync4(path, `${existing}${prefix}${trimmed}
17981
- `);
17982
- return true;
17983
- } catch {
17984
- return false;
17985
- }
17986
- }
17987
- function hasMatchingEntry(contents, pattern) {
17988
- const lines = contents.split("\n").map((l) => l.replace(/#.*$/, "").trim()).filter(Boolean);
17989
- if (lines.includes(pattern)) return true;
17990
- if (pattern.includes("/")) {
17991
- const topDir = `${pattern.split("/")[0]}/`;
17992
- if (lines.includes(topDir)) return true;
17993
- }
17994
- return false;
17995
- }
17996
-
17997
- // src/config-writers/opencode.ts
17998
- var ENV_VAR_NAME2 = "CODEVECTOR_GATEWAY_KEY";
17999
- var PROVIDER_PREFIX = "codevector";
18000
- var writeOpencodeConfig = ({
18001
- gatewayUrl,
18002
- apiKey,
18003
- scope,
18004
- project,
18005
- model,
18006
- availableModels = []
18007
- }) => {
18008
- const path = opencodeSettingsPath(scope);
18009
- const gateway = trimRightSlash4(gatewayUrl);
18010
- const keyLiteral = scope === "project" ? `{env:${ENV_VAR_NAME2}}` : apiKey;
18011
- removeStaleGatewayProviders(path, gateway);
18012
- const models = buildModelEntries(availableModels);
18013
- const options = {
18014
- apiKey: keyLiteral,
18015
- baseURL: `${gateway}/gateway/openai/v1`,
18016
- headers: buildCustomHeaders2(scope, project)
18017
- };
18018
- const patch = {
18019
- $schema: "https://opencode.ai/config.json",
18020
- provider: {
18021
- [PROVIDER_PREFIX]: {
18022
- options,
18023
- models
18024
- }
18025
- }
18026
- };
18027
- if (model) {
18028
- patch.model = `${PROVIDER_PREFIX}/${model.slug}`;
18029
- }
18030
- mergeJsonFile(path, patch);
18031
- forceReplaceCgwModels(path, models);
18032
- const notes = [];
18524
+ const notes = [
18525
+ `codex does not auto-load .env files. Add to your shell rc: export ${ENV_VAR_NAME2}=<your key>`
18526
+ ];
18033
18527
  if (scope === "local") {
18034
- if (ensureGitignored("opencode.json")) {
18035
- notes.push("Added `opencode.json` to .gitignore.");
18528
+ if (ensureGitignored(".codex/config.toml")) {
18529
+ notes.push("Added `.codex/config.toml` to .gitignore.");
18036
18530
  }
18037
18531
  }
18038
- if (scope === "project") {
18039
- notes.push(
18040
- `Project scope avoids embedding secrets. Export ${ENV_VAR_NAME2}=<your key> before running opencode, or switch to local scope to embed the key.`
18041
- );
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
- }
18048
18532
  return {
18049
- tool: "opencode",
18533
+ tool: "codex",
18050
18534
  status: "configured",
18051
18535
  path,
18052
18536
  scope,
18053
- notes: notes.length > 0 ? notes.join(" ") : void 0
18537
+ notes: notes.join(" ")
18054
18538
  };
18055
18539
  };
18056
- function syncOpencodeModels(path, availableModels) {
18057
- if (!existsSync5(path)) {
18058
- throw new Error(
18059
- `No opencode config found at ${path}. Run \`codevector configure opencode\` first.`
18060
- );
18061
- }
18062
- const raw = readFileSync5(path, "utf8");
18063
- let parsed;
18064
- try {
18065
- parsed = raw.trim().length === 0 ? {} : JSON.parse(raw);
18066
- } catch (err) {
18067
- throw new Error(`${path} is not valid JSON: ${err instanceof Error ? err.message : err}`);
18068
- }
18069
- if (!isObject4(parsed)) {
18070
- throw new Error(`${path} is not a JSON object; refusing to overwrite.`);
18071
- }
18072
- const provider = isObject4(parsed.provider) ? parsed.provider : void 0;
18073
- const codevector = provider && isObject4(provider[PROVIDER_PREFIX]) ? provider[PROVIDER_PREFIX] : void 0;
18074
- if (!provider || !codevector) {
18075
- throw new Error(
18076
- `${path} has no \`provider.${PROVIDER_PREFIX}\` entry. Run \`codevector configure opencode\` first.`
18077
- );
18078
- }
18079
- const models = buildModelEntries(availableModels);
18080
- codevector.models = models;
18081
- writeFileSync5(path, `${JSON.stringify(parsed, null, 2)}
18082
- `);
18083
- return Object.keys(models).length;
18084
- }
18085
- function buildModelEntries(availableModels) {
18086
- const out = {};
18087
- for (const m of availableModels) {
18088
- const entry = {};
18089
- if (m.displayName) entry.name = m.displayName;
18090
- if (m.family) entry.family = m.family;
18091
- if (m.releaseDate) entry.release_date = m.releaseDate;
18092
- const inP = parsePrice(m.inputPricePerMtok);
18093
- const outP = parsePrice(m.outputPricePerMtok);
18094
- if (inP !== void 0 && outP !== void 0) {
18095
- const cost = { input: inP, output: outP };
18096
- const cacheReadP = parsePrice(m.cachedInputPricePerMtok);
18097
- const cacheWriteP = parsePrice(m.cacheWritePricePerMtok);
18098
- if (cacheReadP !== void 0) cost.cache_read = cacheReadP;
18099
- if (cacheWriteP !== void 0) cost.cache_write = cacheWriteP;
18100
- entry.cost = cost;
18101
- }
18102
- const ctx = m.contextWindow;
18103
- const outTok = m.maxOutputTokens;
18104
- if (typeof ctx === "number" && ctx > 0 && typeof outTok === "number" && outTok > 0) {
18105
- const limit = { context: ctx, output: outTok };
18106
- if (typeof m.maxInputTokens === "number" && m.maxInputTokens > 0) {
18107
- limit.input = m.maxInputTokens;
18108
- }
18109
- entry.limit = limit;
18110
- }
18111
- if (m.supportsReasoning === true) entry.reasoning = true;
18112
- if (m.supportsAttachment === true) entry.attachment = true;
18113
- if (m.supportsToolCall === true) entry.tool_call = true;
18114
- if (m.supportsTemperature === true) entry.temperature = true;
18115
- const inMods = m.inputModalities;
18116
- const outMods = m.outputModalities;
18117
- if (inMods && inMods.length > 0 && outMods && outMods.length > 0) {
18118
- entry.modalities = { input: [...inMods], output: [...outMods] };
18119
- }
18120
- out[m.slug] = entry;
18121
- }
18122
- return out;
18123
- }
18124
- function parsePrice(raw) {
18125
- if (raw == null) return void 0;
18126
- const n = Number(raw);
18127
- return Number.isFinite(n) ? n : void 0;
18128
- }
18129
- function forceReplaceCgwModels(path, models) {
18130
- if (!existsSync5(path)) return;
18131
- let parsed;
18132
- try {
18133
- parsed = JSON.parse(readFileSync5(path, "utf8"));
18134
- } catch {
18135
- return;
18136
- }
18137
- if (!isObject4(parsed)) return;
18138
- const provider = parsed.provider;
18139
- if (!isObject4(provider)) return;
18140
- const codevector = provider[PROVIDER_PREFIX];
18141
- if (!isObject4(codevector)) return;
18142
- codevector.models = models;
18143
- writeFileSync5(path, `${JSON.stringify(parsed, null, 2)}
18144
- `);
18145
- }
18146
- function opencodeSettingsPath(scope) {
18540
+ function codexConfigPath(scope = "user") {
18147
18541
  switch (scope) {
18148
18542
  case "user":
18149
- return join7(homedir4(), ".config", "opencode", "opencode.json");
18543
+ return join7(homedir4(), ".codex", "config.toml");
18150
18544
  case "project":
18151
18545
  case "local":
18152
- return join7(userCwd(), "opencode.json");
18546
+ return join7(userCwd(), ".codex", "config.toml");
18153
18547
  }
18154
18548
  }
18155
- function removeStaleGatewayProviders(path, gateway) {
18156
- if (!existsSync5(path)) return;
18157
- let parsed;
18158
- try {
18159
- const raw = readFileSync5(path, "utf8");
18160
- if (raw.trim().length === 0) return;
18161
- parsed = JSON.parse(raw);
18162
- } catch {
18163
- return;
18164
- }
18165
- if (!isObject4(parsed)) return;
18166
- const provider = parsed.provider;
18167
- if (!isObject4(provider)) return;
18168
- const ourBaseUrls = /* @__PURE__ */ new Set([`${gateway}/gateway/anthropic`, `${gateway}/gateway/openai/v1`]);
18169
- let mutated = false;
18170
- for (const key of ["openai", "anthropic"]) {
18171
- const entry = provider[key];
18172
- if (!isObject4(entry)) continue;
18173
- const options = entry.options;
18174
- if (!isObject4(options)) continue;
18175
- const baseURL = options.baseURL;
18176
- if (typeof baseURL === "string" && ourBaseUrls.has(baseURL)) {
18177
- delete provider[key];
18178
- mutated = true;
18179
- }
18549
+ function readTomlOrEmpty(path) {
18550
+ if (!existsSync6(path)) return {};
18551
+ const raw = readFileSync7(path, "utf8");
18552
+ if (raw.trim().length === 0) return {};
18553
+ const parsed = parse3(raw);
18554
+ if (!isObject4(parsed)) {
18555
+ throw new Error(`${path} is not a TOML table. Refusing to overwrite.`);
18180
18556
  }
18181
- if (mutated) {
18182
- writeFileSync5(path, `${JSON.stringify(parsed, null, 2)}
18557
+ return parsed;
18558
+ }
18559
+ function writeTomlAtomic(path, value) {
18560
+ const dir = dirname5(path);
18561
+ mkdirSync5(dir, { recursive: true, mode: 448 });
18562
+ let mode;
18563
+ if (existsSync6(path)) {
18564
+ mode = statSync4(path).mode & 511;
18565
+ }
18566
+ const tmp = `${path}.${process.pid}.tmp`;
18567
+ writeFileSync5(tmp, `${stringify(value)}
18183
18568
  `);
18569
+ if (mode !== void 0) {
18570
+ try {
18571
+ chmodSync4(tmp, mode);
18572
+ } catch {
18573
+ }
18184
18574
  }
18575
+ renameSync3(tmp, path);
18185
18576
  }
18186
18577
  function isObject4(v2) {
18187
18578
  return typeof v2 === "object" && v2 !== null && !Array.isArray(v2);
18188
18579
  }
18189
- function buildCustomHeaders2(scope, project) {
18190
- const safe = (v2) => v2.replace(/[\r\n]/g, "").trim();
18191
- const headers = {
18192
- "x-client-app": "opencode"
18193
- };
18194
- if (scope !== "user" && project) {
18195
- const slug = safe(project);
18196
- if (slug) {
18197
- headers["x-project"] = slug;
18198
- }
18199
- }
18200
- return headers;
18201
- }
18202
18580
  function trimRightSlash4(url2) {
18203
18581
  return url2.endsWith("/") ? url2.slice(0, -1) : url2;
18204
18582
  }
@@ -18210,7 +18588,7 @@ var WRITERS = {
18210
18588
  codex: writeCodexConfig
18211
18589
  };
18212
18590
  var ALL_TOOLS = ["claude-code", "opencode", "codex"];
18213
- var SCOPES = ["local", "project", "user"];
18591
+ var SCOPES = ["local", "project"];
18214
18592
  var configureCommand = defineCommand({
18215
18593
  meta: {
18216
18594
  name: "configure",
@@ -18224,8 +18602,8 @@ var configureCommand = defineCommand({
18224
18602
  },
18225
18603
  scope: {
18226
18604
  type: "string",
18227
- description: "Settings scope: local (default, per-repo, gitignored), project, user.",
18228
- valueHint: "local|project|user"
18605
+ description: "Settings scope: local (default, per-repo, gitignored) or project (committed). For user-scope (global) setup, use `codevector system configure`.",
18606
+ valueHint: "local|project"
18229
18607
  },
18230
18608
  all: {
18231
18609
  type: "boolean",
@@ -18233,10 +18611,17 @@ var configureCommand = defineCommand({
18233
18611
  }
18234
18612
  },
18235
18613
  async run({ args }) {
18236
- const creds = await readCredentials();
18237
- if (!creds) {
18614
+ const initialProfiles = await readProfiles();
18615
+ if (!initialProfiles) {
18238
18616
  throw new Error("Not signed in. Run `codevector auth login` first.");
18239
18617
  }
18618
+ const activeProfileName = initialProfiles.activeProfile;
18619
+ const creds = initialProfiles.profiles[activeProfileName];
18620
+ if (!creds) {
18621
+ throw new Error(
18622
+ `Active profile "${activeProfileName}" is missing from credentials. Run \`codevector auth login\` to recover.`
18623
+ );
18624
+ }
18240
18625
  ge("codevector configure");
18241
18626
  const tools = await resolveTools(args);
18242
18627
  const scope = await resolveScope(args.scope, tools);
@@ -18271,25 +18656,25 @@ var configureCommand = defineCommand({
18271
18656
  scope,
18272
18657
  ...project ? { project } : {},
18273
18658
  ...model ? { model } : {},
18274
- availableModels: reachable.map((m) => ({
18275
- slug: m.slug,
18276
- displayName: m.displayName,
18277
- contextWindow: m.contextWindow,
18278
- maxOutputTokens: m.maxOutputTokens,
18279
- maxInputTokens: m.maxInputTokens,
18280
- inputPricePerMtok: m.inputPricePerMtok,
18281
- cachedInputPricePerMtok: m.cachedInputPricePerMtok,
18282
- cacheWritePricePerMtok: m.cacheWritePricePerMtok,
18283
- reasoningPricePerMtok: m.reasoningPricePerMtok,
18284
- outputPricePerMtok: m.outputPricePerMtok,
18285
- supportsReasoning: m.supportsReasoning,
18286
- supportsAttachment: m.supportsAttachment,
18287
- supportsToolCall: m.supportsToolCall,
18288
- supportsTemperature: m.supportsTemperature,
18289
- inputModalities: m.inputModalities,
18290
- outputModalities: m.outputModalities,
18291
- releaseDate: m.releaseDate,
18292
- family: m.family
18659
+ availableModels: reachable.map((m2) => ({
18660
+ slug: m2.slug,
18661
+ displayName: m2.displayName,
18662
+ contextWindow: m2.contextWindow,
18663
+ maxOutputTokens: m2.maxOutputTokens,
18664
+ maxInputTokens: m2.maxInputTokens,
18665
+ inputPricePerMtok: m2.inputPricePerMtok,
18666
+ cachedInputPricePerMtok: m2.cachedInputPricePerMtok,
18667
+ cacheWritePricePerMtok: m2.cacheWritePricePerMtok,
18668
+ reasoningPricePerMtok: m2.reasoningPricePerMtok,
18669
+ outputPricePerMtok: m2.outputPricePerMtok,
18670
+ supportsReasoning: m2.supportsReasoning,
18671
+ supportsAttachment: m2.supportsAttachment,
18672
+ supportsToolCall: m2.supportsToolCall,
18673
+ supportsTemperature: m2.supportsTemperature,
18674
+ inputModalities: m2.inputModalities,
18675
+ outputModalities: m2.outputModalities,
18676
+ releaseDate: m2.releaseDate,
18677
+ family: m2.family
18293
18678
  }))
18294
18679
  })
18295
18680
  );
@@ -18303,6 +18688,19 @@ var configureCommand = defineCommand({
18303
18688
  });
18304
18689
  }
18305
18690
  }
18691
+ const persisted = [];
18692
+ for (const r of results) {
18693
+ if (r.status === "configured") {
18694
+ persisted.push({
18695
+ tool: r.tool,
18696
+ scope: r.scope,
18697
+ ...model ? { modelSlug: model.slug } : {}
18698
+ });
18699
+ }
18700
+ }
18701
+ if (persisted.length > 0) {
18702
+ updateProfileToolConfigs(activeProfileName, persisted);
18703
+ }
18306
18704
  for (const r of results) {
18307
18705
  if (r.status === "configured") {
18308
18706
  R2.success(`${r.tool} \u2192 ${r.path} [${r.scope}]`);
@@ -18344,6 +18742,11 @@ async function resolveTools(args) {
18344
18742
  return picked;
18345
18743
  }
18346
18744
  async function resolveScope(raw, tools) {
18745
+ if (raw === "user") {
18746
+ throw new Error(
18747
+ "`--scope user` is no longer supported by `configure`. Run `codevector system configure` to set the global (user-scope) config for all editors at once, with backups."
18748
+ );
18749
+ }
18347
18750
  if (raw) {
18348
18751
  if (!isScope(raw)) {
18349
18752
  throw new Error(`Invalid --scope "${raw}". Use one of: ${SCOPES.join(", ")}.`);
@@ -18351,7 +18754,7 @@ async function resolveScope(raw, tools) {
18351
18754
  return raw;
18352
18755
  }
18353
18756
  return unwrap(
18354
- await Ee({
18757
+ await xe({
18355
18758
  message: "Where should these settings be written? (arrow keys to move, enter to select)",
18356
18759
  options: [
18357
18760
  {
@@ -18363,11 +18766,6 @@ async function resolveScope(raw, tools) {
18363
18766
  value: "project",
18364
18767
  label: "project",
18365
18768
  hint: `${pathHint(tools, "project")} \u2014 committed, shared with team`
18366
- },
18367
- {
18368
- value: "user",
18369
- label: "user",
18370
- hint: `${pathHint(tools, "user")} \u2014 global default for all projects`
18371
18769
  }
18372
18770
  ],
18373
18771
  initialValue: "local"
@@ -18383,7 +18781,7 @@ function pathHint(tools, scope) {
18383
18781
  case "opencode":
18384
18782
  return relativizeHomeAndCwd(opencodeSettingsPath(scope));
18385
18783
  case "codex":
18386
- return scope === "user" ? relativizeHomeAndCwd(codexConfigPath()) : `${relativizeHomeAndCwd(codexConfigPath())} (codex is user-scope only)`;
18784
+ return relativizeHomeAndCwd(codexConfigPath(scope));
18387
18785
  }
18388
18786
  }
18389
18787
  function relativizeHomeAndCwd(absolutePath) {
@@ -18409,26 +18807,26 @@ async function fetchReachableChatModels(creds) {
18409
18807
  s.start("Loading reachable models\u2026");
18410
18808
  try {
18411
18809
  const res = await call(parseResponse(client.models.$get()));
18412
- const models = res.data.filter((m) => m.kind === "chat").map((m) => ({
18413
- slug: m.slug,
18414
- providerKind: m.providerKind,
18415
- displayName: m.displayName,
18416
- contextWindow: m.contextWindow,
18417
- maxOutputTokens: m.maxOutputTokens,
18418
- maxInputTokens: m.maxInputTokens,
18419
- inputPricePerMtok: m.inputPricePerMtok,
18420
- cachedInputPricePerMtok: m.cachedInputPricePerMtok,
18421
- cacheWritePricePerMtok: m.cacheWritePricePerMtok,
18422
- reasoningPricePerMtok: m.reasoningPricePerMtok,
18423
- outputPricePerMtok: m.outputPricePerMtok,
18424
- supportsReasoning: m.supportsReasoning,
18425
- supportsAttachment: m.supportsAttachment,
18426
- supportsToolCall: m.supportsToolCall,
18427
- supportsTemperature: m.supportsTemperature,
18428
- inputModalities: m.inputModalities,
18429
- outputModalities: m.outputModalities,
18430
- releaseDate: m.releaseDate,
18431
- family: m.family
18810
+ const models = res.data.filter((m2) => m2.kind === "chat").map((m2) => ({
18811
+ slug: m2.slug,
18812
+ providerKind: m2.providerKind,
18813
+ displayName: m2.displayName,
18814
+ contextWindow: m2.contextWindow,
18815
+ maxOutputTokens: m2.maxOutputTokens,
18816
+ maxInputTokens: m2.maxInputTokens,
18817
+ inputPricePerMtok: m2.inputPricePerMtok,
18818
+ cachedInputPricePerMtok: m2.cachedInputPricePerMtok,
18819
+ cacheWritePricePerMtok: m2.cacheWritePricePerMtok,
18820
+ reasoningPricePerMtok: m2.reasoningPricePerMtok,
18821
+ outputPricePerMtok: m2.outputPricePerMtok,
18822
+ supportsReasoning: m2.supportsReasoning,
18823
+ supportsAttachment: m2.supportsAttachment,
18824
+ supportsToolCall: m2.supportsToolCall,
18825
+ supportsTemperature: m2.supportsTemperature,
18826
+ inputModalities: m2.inputModalities,
18827
+ outputModalities: m2.outputModalities,
18828
+ releaseDate: m2.releaseDate,
18829
+ family: m2.family
18432
18830
  }));
18433
18831
  s.stop(`${models.length} model${models.length === 1 ? "" : "s"} reachable`);
18434
18832
  return models;
@@ -18447,7 +18845,7 @@ async function pickPinnedModel(models) {
18447
18845
  }
18448
18846
  const SKIP = "__skip__";
18449
18847
  const picked = unwrap(
18450
- await Ee({
18848
+ await xe({
18451
18849
  message: "Pin a default model for these tools? (arrow keys to move, enter to select)",
18452
18850
  options: [
18453
18851
  {
@@ -18455,23 +18853,23 @@ async function pickPinnedModel(models) {
18455
18853
  label: "Skip (recommended)",
18456
18854
  hint: "let each tool pick per request \u2014 keeps model-switching UIs working"
18457
18855
  },
18458
- ...models.map((m) => ({
18459
- value: m.slug,
18460
- label: m.slug,
18461
- hint: `${m.displayName}${m.providerKind ? ` \xB7 ${m.providerKind}` : ""}`
18856
+ ...models.map((m2) => ({
18857
+ value: m2.slug,
18858
+ label: m2.slug,
18859
+ hint: `${m2.displayName}${m2.providerKind ? ` \xB7 ${m2.providerKind}` : ""}`
18462
18860
  }))
18463
18861
  ],
18464
18862
  initialValue: SKIP
18465
18863
  })
18466
18864
  );
18467
18865
  if (picked === SKIP) return void 0;
18468
- const chosen = models.find((m) => m.slug === picked);
18866
+ const chosen = models.find((m2) => m2.slug === picked);
18469
18867
  if (!chosen) return void 0;
18470
18868
  return { slug: chosen.slug, providerKind: chosen.providerKind };
18471
18869
  }
18472
18870
 
18473
18871
  // src/commands/doctor.ts
18474
- import { existsSync as existsSync6, readFileSync as readFileSync6, statSync as statSync4 } from "fs";
18872
+ import { existsSync as existsSync7, readFileSync as readFileSync8, statSync as statSync5 } from "fs";
18475
18873
  var CLAUDE_SCOPE_ORDER = ["local", "project", "user"];
18476
18874
  var doctorCommand = defineCommand({
18477
18875
  meta: {
@@ -18520,14 +18918,14 @@ var doctorCommand = defineCommand({
18520
18918
  checks.push({ level: "fail", label: "gateway /me", detail: message });
18521
18919
  }
18522
18920
  checks.push(inspectClaudeSettings());
18523
- if (!existsSync6(ACCEPTANCE_HOOK_FILE)) {
18921
+ if (!existsSync7(ACCEPTANCE_HOOK_FILE)) {
18524
18922
  checks.push({
18525
18923
  level: "warn",
18526
18924
  label: "acceptance hook",
18527
18925
  detail: `${ACCEPTANCE_HOOK_FILE} missing \u2014 run \`codevector configure claude-code\``
18528
18926
  });
18529
18927
  } else {
18530
- const mode = statSync4(ACCEPTANCE_HOOK_FILE).mode & 511;
18928
+ const mode = statSync5(ACCEPTANCE_HOOK_FILE).mode & 511;
18531
18929
  checks.push(
18532
18930
  mode & 64 ? { level: "ok", label: "acceptance hook", detail: "installed + executable" } : {
18533
18931
  level: "warn",
@@ -18537,7 +18935,7 @@ var doctorCommand = defineCommand({
18537
18935
  );
18538
18936
  }
18539
18937
  emit(checks);
18540
- if (checks.some((c2) => c2.level === "fail")) {
18938
+ if (checks.some((c) => c.level === "fail")) {
18541
18939
  process.exitCode = 1;
18542
18940
  }
18543
18941
  }
@@ -18545,9 +18943,9 @@ var doctorCommand = defineCommand({
18545
18943
  function inspectClaudeSettings() {
18546
18944
  for (const scope of CLAUDE_SCOPE_ORDER) {
18547
18945
  const path = claudeSettingsPath(scope);
18548
- if (!existsSync6(path)) continue;
18946
+ if (!existsSync7(path)) continue;
18549
18947
  try {
18550
- const raw = JSON.parse(readFileSync6(path, "utf8"));
18948
+ const raw = JSON.parse(readFileSync8(path, "utf8"));
18551
18949
  if (typeof raw.env?.ANTHROPIC_BASE_URL === "string") {
18552
18950
  return {
18553
18951
  level: "ok",
@@ -18571,21 +18969,21 @@ function inspectClaudeSettings() {
18571
18969
  }
18572
18970
  function emit(checks) {
18573
18971
  Se(
18574
- checks.map((c2) => {
18575
- const icon = c2.level === "ok" ? "\u2713" : c2.level === "warn" ? "!" : "\u2717";
18576
- const detail = c2.detail ? ` \u2014 ${c2.detail}` : "";
18577
- return `${icon} ${c2.label}${detail}`;
18972
+ checks.map((c) => {
18973
+ const icon = c.level === "ok" ? "\u2713" : c.level === "warn" ? "!" : "\u2717";
18974
+ const detail = c.detail ? ` \u2014 ${c.detail}` : "";
18975
+ return `${icon} ${c.label}${detail}`;
18578
18976
  }).join("\n"),
18579
18977
  "Doctor"
18580
18978
  );
18581
- for (const c2 of checks) {
18582
- if (c2.level === "fail") R2.error(c2.label);
18583
- else if (c2.level === "warn") R2.warn(c2.label);
18979
+ for (const c of checks) {
18980
+ if (c.level === "fail") R2.error(c.label);
18981
+ else if (c.level === "warn") R2.warn(c.label);
18584
18982
  }
18585
18983
  }
18586
18984
 
18587
18985
  // src/commands/init.ts
18588
- import { existsSync as existsSync7, writeFileSync as writeFileSync6 } from "fs";
18986
+ import { existsSync as existsSync8, writeFileSync as writeFileSync6 } from "fs";
18589
18987
  import { execFileSync as execFileSync2 } from "child_process";
18590
18988
  import { join as join8 } from "path";
18591
18989
  var DEFAULT_TICKET_PATTERN2 = "[A-Z]+-\\d+";
@@ -18608,269 +19006,1003 @@ var initCommand = defineCommand({
18608
19006
  description: "Overwrite an existing .codevector.json."
18609
19007
  }
18610
19008
  },
18611
- run({ args }) {
18612
- const cwd = userCwd();
18613
- const target = join8(cwd, ".codevector.json");
18614
- if (existsSync7(target) && !args.force) {
18615
- throw new Error(`.codevector.json already exists in ${cwd}. Pass --force to overwrite.`);
19009
+ run({ args }) {
19010
+ const cwd = userCwd();
19011
+ const target = join8(cwd, ".codevector.json");
19012
+ if (existsSync8(target) && !args.force) {
19013
+ throw new Error(`.codevector.json already exists in ${cwd}. Pass --force to overwrite.`);
19014
+ }
19015
+ const projectName = args.project ?? deriveProjectName(cwd);
19016
+ if (!projectName) {
19017
+ throw new Error(
19018
+ "Could not derive projectName from git remote. Pass --project <name> explicitly."
19019
+ );
19020
+ }
19021
+ const ticketPattern = args["ticket-pattern"] ?? DEFAULT_TICKET_PATTERN2;
19022
+ writeFileSync6(target, `${JSON.stringify({ projectName, ticketPattern }, null, 2)}
19023
+ `, {
19024
+ mode: 420
19025
+ });
19026
+ R2.success(`Wrote ${target}`);
19027
+ R2.info(`projectName: ${projectName}`);
19028
+ R2.info(`ticketPattern: ${ticketPattern}`);
19029
+ }
19030
+ });
19031
+ function deriveProjectName(cwd) {
19032
+ try {
19033
+ const out = execFileSync2("git", ["remote", "get-url", "origin"], {
19034
+ encoding: "utf8",
19035
+ cwd,
19036
+ stdio: ["ignore", "pipe", "ignore"],
19037
+ timeout: 2e3
19038
+ }).trim();
19039
+ return out ? repoSlugFromRemote(out) : null;
19040
+ } catch {
19041
+ return null;
19042
+ }
19043
+ }
19044
+
19045
+ // src/lib/table.ts
19046
+ function renderTable(headers, rows) {
19047
+ const widths = headers.map(
19048
+ (h2, i) => Math.max(h2.length, ...rows.map((row) => (row[i] ?? "").length))
19049
+ );
19050
+ const pad = (s, i) => s.padEnd(widths[i] ?? 0);
19051
+ const divider = widths.map((w3) => "\u2500".repeat(w3)).join(" ");
19052
+ return [headers.map(pad).join(" "), divider, ...rows.map((row) => row.map(pad).join(" "))].join(
19053
+ "\n"
19054
+ );
19055
+ }
19056
+
19057
+ // src/commands/sync-models.ts
19058
+ var SCOPES2 = ["local", "project", "user"];
19059
+ var modelsSyncCommand = defineCommand({
19060
+ meta: {
19061
+ name: "sync",
19062
+ description: "Refresh the model list (with names, costs, context limits) in an existing opencode.json."
19063
+ },
19064
+ args: {
19065
+ scope: {
19066
+ type: "string",
19067
+ description: "Settings scope: local (default, per-repo), project, user.",
19068
+ valueHint: "local|project|user"
19069
+ },
19070
+ path: {
19071
+ type: "string",
19072
+ description: "Explicit opencode.json path. Overrides --scope.",
19073
+ valueHint: "/path/to/opencode.json"
19074
+ }
19075
+ },
19076
+ async run({ args }) {
19077
+ const creds = await readCredentials();
19078
+ if (!creds) {
19079
+ throw new Error("Not signed in. Run `codevector auth login` first.");
19080
+ }
19081
+ ge("Sync models");
19082
+ const target = await resolveTarget(args);
19083
+ const client = gatewayClient(creds.gatewayUrl, creds.apiKey, 1e4);
19084
+ const s = ft();
19085
+ s.start("Loading reachable models\u2026");
19086
+ let reachable;
19087
+ try {
19088
+ const res = await call(parseResponse(client.models.$get()));
19089
+ reachable = res.data.filter((m2) => m2.kind === "chat").map((m2) => ({
19090
+ slug: m2.slug,
19091
+ displayName: m2.displayName,
19092
+ contextWindow: m2.contextWindow,
19093
+ maxOutputTokens: m2.maxOutputTokens,
19094
+ maxInputTokens: m2.maxInputTokens,
19095
+ inputPricePerMtok: m2.inputPricePerMtok,
19096
+ cachedInputPricePerMtok: m2.cachedInputPricePerMtok,
19097
+ cacheWritePricePerMtok: m2.cacheWritePricePerMtok,
19098
+ reasoningPricePerMtok: m2.reasoningPricePerMtok,
19099
+ outputPricePerMtok: m2.outputPricePerMtok,
19100
+ supportsReasoning: m2.supportsReasoning,
19101
+ supportsAttachment: m2.supportsAttachment,
19102
+ supportsToolCall: m2.supportsToolCall,
19103
+ supportsTemperature: m2.supportsTemperature,
19104
+ inputModalities: m2.inputModalities,
19105
+ outputModalities: m2.outputModalities,
19106
+ releaseDate: m2.releaseDate,
19107
+ family: m2.family
19108
+ }));
19109
+ s.stop(`${reachable.length} model${reachable.length === 1 ? "" : "s"} reachable`);
19110
+ } catch (err) {
19111
+ s.stop("Could not load models");
19112
+ R2.error(err instanceof ApiClientError ? err.message : String(err));
19113
+ process.exitCode = 1;
19114
+ return;
19115
+ }
19116
+ try {
19117
+ const written = syncOpencodeModels(target, reachable);
19118
+ R2.success(`Wrote ${written} model${written === 1 ? "" : "s"} to ${target}`);
19119
+ } catch (err) {
19120
+ R2.error(err instanceof Error ? err.message : String(err));
19121
+ process.exitCode = 1;
19122
+ return;
19123
+ }
19124
+ ye("Done.");
19125
+ }
19126
+ });
19127
+ async function resolveTarget(args) {
19128
+ if (args.path) return args.path;
19129
+ if (args.scope) {
19130
+ if (!isScope2(args.scope)) {
19131
+ throw new Error(`Invalid --scope "${args.scope}". Use one of: ${SCOPES2.join(", ")}.`);
19132
+ }
19133
+ return opencodeSettingsPath(args.scope);
19134
+ }
19135
+ const picked = unwrap(
19136
+ await xe({
19137
+ message: "Which opencode.json should be refreshed? (arrow keys to move, enter to select)",
19138
+ options: [
19139
+ { value: "local", label: "local", hint: "./opencode.json (this repo)" },
19140
+ { value: "project", label: "project", hint: "./opencode.json (this repo, committed)" },
19141
+ { value: "user", label: "user", hint: "~/.config/opencode/opencode.json (global)" }
19142
+ ],
19143
+ initialValue: "local"
19144
+ })
19145
+ );
19146
+ return opencodeSettingsPath(picked);
19147
+ }
19148
+ function isScope2(value) {
19149
+ return SCOPES2.includes(value);
19150
+ }
19151
+
19152
+ // src/commands/models.ts
19153
+ var modelsListCommand = defineCommand({
19154
+ meta: {
19155
+ name: "list",
19156
+ description: "List models you can reach through the gateway."
19157
+ },
19158
+ args: {
19159
+ kind: {
19160
+ type: "string",
19161
+ description: "Filter by kind: chat or embedding.",
19162
+ valueHint: "chat|embedding"
19163
+ },
19164
+ json: {
19165
+ type: "boolean",
19166
+ description: "Emit raw JSON (for scripting)."
19167
+ }
19168
+ },
19169
+ async run({ args }) {
19170
+ const creds = await readCredentials();
19171
+ if (!creds) {
19172
+ R2.warn("Not signed in. Run `codevector auth login` to get started.");
19173
+ process.exitCode = 1;
19174
+ return;
19175
+ }
19176
+ const kindFilter = args.kind;
19177
+ if (kindFilter && kindFilter !== "chat" && kindFilter !== "embedding") {
19178
+ throw new Error(`Invalid --kind "${kindFilter}". Use "chat" or "embedding".`);
19179
+ }
19180
+ const client = gatewayClient(creds.gatewayUrl, creds.apiKey, 1e4);
19181
+ const s = args.json ? null : ft();
19182
+ s?.start("Loading models\u2026");
19183
+ try {
19184
+ const res = await call(parseResponse(client.models.$get()));
19185
+ const filtered = kindFilter ? res.data.filter((m2) => m2.kind === kindFilter) : res.data;
19186
+ s?.stop(`${filtered.length} model${filtered.length === 1 ? "" : "s"} reachable`);
19187
+ if (args.json) {
19188
+ process.stdout.write(`${JSON.stringify(filtered, null, 2)}
19189
+ `);
19190
+ return;
19191
+ }
19192
+ if (filtered.length === 0) {
19193
+ R2.info(
19194
+ kindFilter ? `No ${kindFilter} models available. Ask your gateway admin to grant access.` : "No models available. Ask your gateway admin to grant access."
19195
+ );
19196
+ return;
19197
+ }
19198
+ const headers = [
19199
+ "SLUG",
19200
+ "KIND",
19201
+ "STATUS",
19202
+ "PROVIDER",
19203
+ "CONTEXT",
19204
+ "IN $/MTOK",
19205
+ "OUT $/MTOK",
19206
+ "NAME"
19207
+ ];
19208
+ const cells = filtered.map((m2) => [
19209
+ m2.slug,
19210
+ m2.kind,
19211
+ m2.status,
19212
+ m2.providerKind ?? "\u2014",
19213
+ m2.contextWindow ? m2.contextWindow.toLocaleString() : "\u2014",
19214
+ m2.inputPricePerMtok ?? "\u2014",
19215
+ m2.outputPricePerMtok ?? "\u2014",
19216
+ m2.displayName
19217
+ ]);
19218
+ Se(renderTable(headers, cells), "Reachable models");
19219
+ } catch (err) {
19220
+ s?.stop("Could not load models");
19221
+ R2.error(err instanceof ApiClientError ? err.message : String(err));
19222
+ process.exitCode = 1;
19223
+ }
19224
+ }
19225
+ });
19226
+ var modelsCommand = defineCommand({
19227
+ meta: {
19228
+ name: "models",
19229
+ description: "List and manage models."
19230
+ },
19231
+ subCommands: {
19232
+ list: modelsListCommand,
19233
+ sync: modelsSyncCommand
19234
+ }
19235
+ });
19236
+
19237
+ // src/commands/profile.ts
19238
+ import { existsSync as existsSync9, readFileSync as readFileSync9 } from "fs";
19239
+ var WRITERS2 = {
19240
+ "claude-code": writeClaudeCodeConfig,
19241
+ opencode: writeOpencodeConfig,
19242
+ codex: writeCodexConfig
19243
+ };
19244
+ var profileListCommand = defineCommand({
19245
+ meta: {
19246
+ name: "list",
19247
+ description: "List all currently configured profiles."
19248
+ },
19249
+ async run() {
19250
+ const profiles = await readProfiles();
19251
+ if (!profiles || Object.keys(profiles.profiles).length === 0) {
19252
+ R2.info("No profiles configured. Run `codevector auth login` to get started.");
19253
+ return;
19254
+ }
19255
+ const names = Object.keys(profiles.profiles).sort((a, b2) => {
19256
+ if (a === "default") return -1;
19257
+ if (b2 === "default") return 1;
19258
+ return a.localeCompare(b2);
19259
+ });
19260
+ const lines = names.map((name) => {
19261
+ const p2 = profiles.profiles[name];
19262
+ const marker = name === profiles.activeProfile ? " *" : " ";
19263
+ return `${marker} ${name.padEnd(12)} ${p2.email.padEnd(24)} ${p2.gatewayUrl} (${maskApiKey(p2.apiKey)})`;
19264
+ });
19265
+ Se(lines.join("\n"), "Profiles");
19266
+ }
19267
+ });
19268
+ var profileSwitchCommand = defineCommand({
19269
+ meta: {
19270
+ name: "switch",
19271
+ description: "Switch the active profile used by subsequent commands."
19272
+ },
19273
+ async run() {
19274
+ const profiles = await readProfiles();
19275
+ if (!profiles || Object.keys(profiles.profiles).length === 0) {
19276
+ R2.warn("No profiles configured. Run `codevector auth login` first.");
19277
+ process.exitCode = 1;
19278
+ return;
18616
19279
  }
18617
- const projectName = args.project ?? deriveProjectName(cwd);
18618
- if (!projectName) {
18619
- throw new Error(
18620
- "Could not derive projectName from git remote. Pass --project <name> explicitly."
19280
+ const names = Object.keys(profiles.profiles).sort((a, b2) => {
19281
+ if (a === "default") return -1;
19282
+ if (b2 === "default") return 1;
19283
+ return a.localeCompare(b2);
19284
+ });
19285
+ const selected = unwrap(
19286
+ await xe({
19287
+ message: "Select active profile",
19288
+ options: names.map((n) => ({
19289
+ value: n,
19290
+ label: n,
19291
+ hint: n === profiles.activeProfile ? "active" : void 0
19292
+ })),
19293
+ initialValue: profiles.activeProfile
19294
+ })
19295
+ );
19296
+ await setActiveProfile(selected);
19297
+ const active = await getActiveProfile();
19298
+ if (!active) {
19299
+ R2.error(`Profile "${selected}" disappeared mid-switch. Re-run \`codevector profile switch\`.`);
19300
+ process.exitCode = 1;
19301
+ return;
19302
+ }
19303
+ R2.success(`Active profile set to "${selected}".`);
19304
+ Se(
19305
+ `User: ${active.email}
19306
+ Gateway: ${active.gatewayUrl}
19307
+ Key: ${maskApiKey(active.apiKey)}`,
19308
+ `Profile: ${selected}`
19309
+ );
19310
+ const storedToolConfigs = active.toolConfigs ?? [];
19311
+ const defaultCodexConfig = { tool: "codex", scope: "user" };
19312
+ const hasCodexUserConfig = storedToolConfigs.some((tc) => tc.tool === "codex");
19313
+ const toolConfigs = hasCodexUserConfig ? storedToolConfigs : [...storedToolConfigs, defaultCodexConfig];
19314
+ if (!hasCodexUserConfig) {
19315
+ R2.info(
19316
+ storedToolConfigs.length === 0 ? "No stored tool configurations for this profile; applying codex user-scope config." : "No stored codex configuration for this profile; applying codex user-scope config."
18621
19317
  );
18622
19318
  }
18623
- const ticketPattern = args["ticket-pattern"] ?? DEFAULT_TICKET_PATTERN2;
18624
- writeFileSync6(target, `${JSON.stringify({ projectName, ticketPattern }, null, 2)}
18625
- `, {
18626
- mode: 420
19319
+ const fetched = await fetchReachableModels(active.gatewayUrl, active.apiKey);
19320
+ if (!fetched.ok) {
19321
+ R2.warn(
19322
+ `Could not reach the gateway to verify reachable models: ${fetched.reason}.`
19323
+ );
19324
+ R2.info(
19325
+ "Stored tool configurations were left unchanged. Re-run `codevector profile switch` once the gateway is reachable to reapply them."
19326
+ );
19327
+ return;
19328
+ }
19329
+ const reachable = fetched.models;
19330
+ const hookPath = installAcceptanceHook();
19331
+ const project = resolveProjectContext(userCwd()).project ?? void 0;
19332
+ const results = [];
19333
+ const persisted = [];
19334
+ for (const tc of toolConfigs) {
19335
+ const tool = tc.tool;
19336
+ const writer = WRITERS2[tool];
19337
+ if (!writer) {
19338
+ results.push({
19339
+ tool: tc.tool,
19340
+ status: "skipped",
19341
+ path: "",
19342
+ scope: tc.scope,
19343
+ notes: `Unsupported tool "${tc.tool}".`
19344
+ });
19345
+ continue;
19346
+ }
19347
+ const storedModelSlug = tool === "codex" && tc.modelSlug === "codex" ? void 0 : tc.modelSlug;
19348
+ const reachableModel = storedModelSlug ? reachable.find((m2) => m2.slug === storedModelSlug) : void 0;
19349
+ const pinAllowed = !reachableModel ? false : tool !== "codex" || isResponsesCompatibleProviderKind(reachableModel.providerKind);
19350
+ let modelArg = storedModelSlug && pinAllowed ? { slug: reachableModel.slug, providerKind: reachableModel.providerKind } : void 0;
19351
+ let extraNote;
19352
+ if (tc.modelSlug === "codex" && tool === "codex") {
19353
+ extraNote = `Pinned model "codex" is an alias; selecting a concrete reachable model for this profile.`;
19354
+ } else if (storedModelSlug && !reachableModel) {
19355
+ extraNote = `Pinned model "${tc.modelSlug}" is not reachable with this profile; model pin removed.`;
19356
+ } else if (storedModelSlug && reachableModel && !pinAllowed) {
19357
+ extraNote = `Pinned model "${tc.modelSlug}" is incompatible with Codex Responses; model pin removed.`;
19358
+ }
19359
+ if (tool === "codex" && !modelArg) {
19360
+ const fallback = pickCodexFallbackModel(reachable);
19361
+ if (fallback) {
19362
+ modelArg = { slug: fallback.slug, providerKind: fallback.providerKind };
19363
+ if (tc.modelSlug) {
19364
+ extraNote = `Pinned model "${tc.modelSlug}" unavailable for this profile; defaulting Codex to "${fallback.slug}".`;
19365
+ } else {
19366
+ extraNote = `No stored Codex model pin for this profile; defaulting to "${fallback.slug}".`;
19367
+ }
19368
+ }
19369
+ }
19370
+ try {
19371
+ const result = writer({
19372
+ gatewayUrl: active.gatewayUrl,
19373
+ apiKey: active.apiKey,
19374
+ hookScriptPath: hookPath,
19375
+ scope: tc.scope,
19376
+ ...project ? { project } : {},
19377
+ ...modelArg ? { model: modelArg } : {},
19378
+ availableModels: reachable
19379
+ });
19380
+ if (extraNote) {
19381
+ result.notes = result.notes ? `${result.notes} ${extraNote}` : extraNote;
19382
+ }
19383
+ results.push(result);
19384
+ if (result.status === "configured") {
19385
+ persisted.push({
19386
+ tool: result.tool,
19387
+ scope: result.scope,
19388
+ ...modelArg ? { modelSlug: modelArg.slug } : {}
19389
+ });
19390
+ }
19391
+ } catch (err) {
19392
+ results.push({
19393
+ tool: tc.tool,
19394
+ status: "skipped",
19395
+ path: "",
19396
+ scope: tc.scope,
19397
+ notes: err instanceof Error ? err.message : String(err)
19398
+ });
19399
+ }
19400
+ }
19401
+ if (persisted.length > 0) {
19402
+ updateProfileToolConfigs(selected, persisted);
19403
+ }
19404
+ const lines = results.map((r) => {
19405
+ if (r.status === "configured") {
19406
+ const noteStr = r.notes ? `
19407
+ ${r.notes}` : "";
19408
+ return ` \u2713 ${r.tool} \u2192 ${r.path} [${r.scope}]${noteStr}`;
19409
+ }
19410
+ return ` ! ${r.tool}: skipped \u2014 ${r.notes ?? "unknown reason"}`;
18627
19411
  });
18628
- R2.success(`Wrote ${target}`);
18629
- R2.info(`projectName: ${projectName}`);
18630
- R2.info(`ticketPattern: ${ticketPattern}`);
19412
+ Se(lines.join("\n"), "Tool configurations updated");
19413
+ const codexConfigured = results.some(
19414
+ (r) => r.tool === "codex" && r.status === "configured"
19415
+ );
19416
+ if (codexConfigured) {
19417
+ Se(
19418
+ `codex reads its API key from $CODEVECTOR_GATEWAY_KEY in your shell, not from codevector's credentials file.
19419
+ Update your shell to use this profile's key:
19420
+ export CODEVECTOR_GATEWAY_KEY=${active.apiKey}
19421
+ Or add it to your shell rc to persist across sessions.`,
19422
+ "codex requires env var update"
19423
+ );
19424
+ }
18631
19425
  }
18632
19426
  });
18633
- function deriveProjectName(cwd) {
19427
+ var profileCommand = defineCommand({
19428
+ meta: {
19429
+ name: "profile",
19430
+ description: "Manage CLI profiles."
19431
+ },
19432
+ subCommands: {
19433
+ list: profileListCommand,
19434
+ switch: profileSwitchCommand
19435
+ }
19436
+ });
19437
+ async function fetchReachableModels(gatewayUrl, apiKey) {
19438
+ const client = gatewayClient(gatewayUrl, apiKey, 1e4);
18634
19439
  try {
18635
- const out = execFileSync2("git", ["remote", "get-url", "origin"], {
18636
- encoding: "utf8",
18637
- cwd,
18638
- stdio: ["ignore", "pipe", "ignore"],
18639
- timeout: 2e3
18640
- }).trim();
18641
- return out ? repoSlugFromRemote(out) : null;
19440
+ const res = await call(parseResponse(client.models.$get()));
19441
+ const models = res.data.filter((m2) => m2.kind === "chat").map((m2) => ({
19442
+ slug: m2.slug,
19443
+ providerKind: m2.providerKind,
19444
+ displayName: m2.displayName,
19445
+ contextWindow: m2.contextWindow,
19446
+ maxOutputTokens: m2.maxOutputTokens,
19447
+ maxInputTokens: m2.maxInputTokens,
19448
+ inputPricePerMtok: m2.inputPricePerMtok,
19449
+ cachedInputPricePerMtok: m2.cachedInputPricePerMtok,
19450
+ cacheWritePricePerMtok: m2.cacheWritePricePerMtok,
19451
+ reasoningPricePerMtok: m2.reasoningPricePerMtok,
19452
+ outputPricePerMtok: m2.outputPricePerMtok,
19453
+ supportsReasoning: m2.supportsReasoning,
19454
+ supportsAttachment: m2.supportsAttachment,
19455
+ supportsToolCall: m2.supportsToolCall,
19456
+ supportsTemperature: m2.supportsTemperature,
19457
+ inputModalities: m2.inputModalities,
19458
+ outputModalities: m2.outputModalities,
19459
+ releaseDate: m2.releaseDate,
19460
+ family: m2.family
19461
+ }));
19462
+ return { ok: true, models };
19463
+ } catch (err) {
19464
+ return { ok: false, reason: err instanceof ApiClientError ? err.message : String(err) };
19465
+ }
19466
+ }
19467
+ function isResponsesCompatibleProviderKind(providerKind) {
19468
+ return providerKind === "openai" || providerKind === "openai_compatible";
19469
+ }
19470
+ function pickCodexFallbackModel(reachable) {
19471
+ const candidates = reachable.filter((m2) => isResponsesCompatibleProviderKind(m2.providerKind));
19472
+ if (candidates.length === 0) {
19473
+ return void 0;
19474
+ }
19475
+ const ranked = readCodexNuxRankedModels();
19476
+ for (const slug of ranked) {
19477
+ const match = candidates.find((m2) => m2.slug === slug);
19478
+ if (match) return match;
19479
+ }
19480
+ return candidates[0];
19481
+ }
19482
+ function readCodexNuxRankedModels() {
19483
+ const path = codexConfigPath();
19484
+ if (!existsSync9(path)) return [];
19485
+ let parsed;
19486
+ try {
19487
+ parsed = parse3(readFileSync9(path, "utf8"));
18642
19488
  } catch {
18643
- return null;
19489
+ return [];
18644
19490
  }
19491
+ if (!isObject5(parsed)) return [];
19492
+ const tui = asObject(parsed.tui);
19493
+ const availability = asObject(tui?.model_availability_nux);
19494
+ if (!availability) return [];
19495
+ return Object.entries(availability).filter((entry) => typeof entry[1] === "number" && Number.isFinite(entry[1])).sort((a, b2) => b2[1] - a[1] || a[0].localeCompare(b2[0])).map(([slug]) => slug);
18645
19496
  }
18646
-
18647
- // src/lib/table.ts
18648
- function renderTable(headers, rows) {
18649
- const widths = headers.map(
18650
- (h2, i) => Math.max(h2.length, ...rows.map((row) => (row[i] ?? "").length))
18651
- );
18652
- const pad = (s, i) => s.padEnd(widths[i] ?? 0);
18653
- const divider = widths.map((w3) => "\u2500".repeat(w3)).join(" ");
18654
- return [headers.map(pad).join(" "), divider, ...rows.map((row) => row.map(pad).join(" "))].join(
18655
- "\n"
18656
- );
19497
+ function isObject5(value) {
19498
+ return typeof value === "object" && value !== null && !Array.isArray(value);
19499
+ }
19500
+ function asObject(value) {
19501
+ return isObject5(value) ? value : void 0;
18657
19502
  }
18658
19503
 
18659
- // src/commands/sync-models.ts
18660
- var SCOPES2 = ["local", "project", "user"];
18661
- var modelsSyncCommand = defineCommand({
19504
+ // src/commands/status.ts
19505
+ var statusCommand = defineCommand({
18662
19506
  meta: {
18663
- name: "sync",
18664
- description: "Refresh the model list (with names, costs, context limits) in an existing opencode.json."
18665
- },
18666
- args: {
18667
- scope: {
18668
- type: "string",
18669
- description: "Settings scope: local (default, per-repo), project, user.",
18670
- valueHint: "local|project|user"
18671
- },
18672
- path: {
18673
- type: "string",
18674
- description: "Explicit opencode.json path. Overrides --scope.",
18675
- valueHint: "/path/to/opencode.json"
18676
- }
19507
+ name: "status",
19508
+ description: "Show current login + gateway reachability."
18677
19509
  },
18678
- async run({ args }) {
19510
+ async run() {
18679
19511
  const creds = await readCredentials();
18680
19512
  if (!creds) {
18681
- throw new Error("Not signed in. Run `codevector auth login` first.");
19513
+ R2.warn("Not signed in. Run `codevector auth login` to get started.");
19514
+ process.exitCode = 1;
19515
+ return;
18682
19516
  }
18683
- ge("Sync models");
18684
- const target = await resolveTarget(args);
19517
+ Se(
19518
+ `Signed in as ${creds.email}
19519
+ Gateway: ${creds.gatewayUrl}
19520
+ API key: ${maskApiKey(creds.apiKey)}
19521
+ Last saved: ${creds.savedAt}`,
19522
+ "Credentials"
19523
+ );
18685
19524
  const client = gatewayClient(creds.gatewayUrl, creds.apiKey, 1e4);
18686
19525
  const s = ft();
18687
- s.start("Loading reachable models\u2026");
18688
- let reachable;
19526
+ s.start("Reaching gateway\u2026");
18689
19527
  try {
18690
19528
  const res = await call(parseResponse(client.models.$get()));
18691
- reachable = res.data.filter((m) => m.kind === "chat").map((m) => ({
18692
- slug: m.slug,
18693
- displayName: m.displayName,
18694
- contextWindow: m.contextWindow,
18695
- maxOutputTokens: m.maxOutputTokens,
18696
- maxInputTokens: m.maxInputTokens,
18697
- inputPricePerMtok: m.inputPricePerMtok,
18698
- cachedInputPricePerMtok: m.cachedInputPricePerMtok,
18699
- cacheWritePricePerMtok: m.cacheWritePricePerMtok,
18700
- reasoningPricePerMtok: m.reasoningPricePerMtok,
18701
- outputPricePerMtok: m.outputPricePerMtok,
18702
- supportsReasoning: m.supportsReasoning,
18703
- supportsAttachment: m.supportsAttachment,
18704
- supportsToolCall: m.supportsToolCall,
18705
- supportsTemperature: m.supportsTemperature,
18706
- inputModalities: m.inputModalities,
18707
- outputModalities: m.outputModalities,
18708
- releaseDate: m.releaseDate,
18709
- family: m.family
18710
- }));
18711
- s.stop(`${reachable.length} model${reachable.length === 1 ? "" : "s"} reachable`);
19529
+ s.stop(
19530
+ res.data.length === 0 ? "Gateway reachable \u2014 no models granted. Ask your admin for access." : `Gateway reachable \u2014 ${res.data.length} model${res.data.length === 1 ? "" : "s"} available. Run \`codevector models\` to list them.`
19531
+ );
18712
19532
  } catch (err) {
18713
- s.stop("Could not load models");
19533
+ s.stop("Gateway unreachable");
18714
19534
  R2.error(err instanceof ApiClientError ? err.message : String(err));
18715
19535
  process.exitCode = 1;
18716
- return;
18717
- }
18718
- try {
18719
- const written = syncOpencodeModels(target, reachable);
18720
- R2.success(`Wrote ${written} model${written === 1 ? "" : "s"} to ${target}`);
18721
- } catch (err) {
18722
- R2.error(err instanceof Error ? err.message : String(err));
18723
- process.exitCode = 1;
18724
- return;
18725
19536
  }
18726
- ye("Done.");
18727
19537
  }
18728
19538
  });
18729
- async function resolveTarget(args) {
18730
- if (args.path) return args.path;
18731
- if (args.scope) {
18732
- if (!isScope2(args.scope)) {
18733
- throw new Error(`Invalid --scope "${args.scope}". Use one of: ${SCOPES2.join(", ")}.`);
19539
+
19540
+ // src/commands/system.ts
19541
+ import { existsSync as existsSync11 } from "fs";
19542
+
19543
+ // src/lib/backup.ts
19544
+ import {
19545
+ copyFileSync as copyFileSync2,
19546
+ existsSync as existsSync10,
19547
+ mkdirSync as mkdirSync6,
19548
+ readdirSync,
19549
+ statSync as statSync6
19550
+ } from "fs";
19551
+ import { basename, dirname as dirname6, join as join9 } from "path";
19552
+ import { homedir as homedir6 } from "os";
19553
+ var BACKUP_ROOT = process.env.CODEVECTOR_BACKUP_ROOT ?? join9(homedir6(), ".codevector", "backups");
19554
+ function backupTimestamp(d = /* @__PURE__ */ new Date()) {
19555
+ return d.toISOString().replace(/:/g, "-");
19556
+ }
19557
+ function backupFile(sourcePath, tool, timestamp) {
19558
+ if (!existsSync10(sourcePath)) return void 0;
19559
+ const destDir = join9(BACKUP_ROOT, timestamp, tool);
19560
+ mkdirSync6(destDir, { recursive: true, mode: 448 });
19561
+ const dest = join9(destDir, basename(sourcePath));
19562
+ copyFileSync2(sourcePath, dest);
19563
+ return dest;
19564
+ }
19565
+ function listBackupRuns() {
19566
+ if (!existsSync10(BACKUP_ROOT)) return [];
19567
+ const entries = readdirSync(BACKUP_ROOT, { withFileTypes: true }).filter((e2) => e2.isDirectory()).map((e2) => e2.name).sort().reverse();
19568
+ const runs = [];
19569
+ for (const ts of entries) {
19570
+ const dir = join9(BACKUP_ROOT, ts);
19571
+ const tools = readdirSync(dir, { withFileTypes: true }).filter((e2) => e2.isDirectory());
19572
+ const collected = [];
19573
+ for (const toolDir of tools) {
19574
+ const toolPath = join9(dir, toolDir.name);
19575
+ for (const file2 of readdirSync(toolPath, { withFileTypes: true })) {
19576
+ if (!file2.isFile()) continue;
19577
+ collected.push({
19578
+ tool: toolDir.name,
19579
+ original: "",
19580
+ // resolved by caller per tool — see restoreBackup()
19581
+ backup: join9(toolPath, file2.name)
19582
+ });
19583
+ }
18734
19584
  }
18735
- return opencodeSettingsPath(args.scope);
19585
+ if (collected.length === 0) continue;
19586
+ runs.push({ timestamp: ts, dir, entries: collected });
18736
19587
  }
18737
- const picked = unwrap(
18738
- await Ee({
18739
- message: "Which opencode.json should be refreshed? (arrow keys to move, enter to select)",
18740
- options: [
18741
- { value: "local", label: "local", hint: "./opencode.json (this repo)" },
18742
- { value: "project", label: "project", hint: "./opencode.json (this repo, committed)" },
18743
- { value: "user", label: "user", hint: "~/.config/opencode/opencode.json (global)" }
18744
- ],
18745
- initialValue: "local"
18746
- })
18747
- );
18748
- return opencodeSettingsPath(picked);
19588
+ return runs;
18749
19589
  }
18750
- function isScope2(value) {
18751
- return SCOPES2.includes(value);
19590
+ function restoreBackup(backupPath, originalPath) {
19591
+ if (!existsSync10(backupPath)) {
19592
+ throw new Error(`Backup file missing: ${backupPath}`);
19593
+ }
19594
+ mkdirSync6(dirname6(originalPath), { recursive: true });
19595
+ copyFileSync2(backupPath, originalPath);
19596
+ }
19597
+ function backupRunMtime(dir) {
19598
+ return statSync6(dir).mtime;
18752
19599
  }
18753
19600
 
18754
- // src/commands/models.ts
18755
- var modelsListCommand = defineCommand({
19601
+ // src/commands/system.ts
19602
+ var TOOLS = ["claude-code", "opencode", "codex"];
19603
+ var WRITERS3 = {
19604
+ "claude-code": writeClaudeCodeConfig,
19605
+ opencode: writeOpencodeConfig,
19606
+ codex: writeCodexConfig
19607
+ };
19608
+ function userPathFor(tool) {
19609
+ switch (tool) {
19610
+ case "claude-code":
19611
+ return claudeSettingsPath("user");
19612
+ case "opencode":
19613
+ return opencodeSettingsPath("user");
19614
+ case "codex":
19615
+ return codexConfigPath("user");
19616
+ }
19617
+ }
19618
+ var systemConfigureCommand = defineCommand({
18756
19619
  meta: {
18757
- name: "list",
18758
- description: "List models you can reach through the gateway."
19620
+ name: "configure",
19621
+ description: "Write user-scope (global, machine-wide) config for all supported editors at once. Existing files are backed up so `system restore` can roll back."
19622
+ },
19623
+ args: {},
19624
+ async run() {
19625
+ const profiles = await readProfiles();
19626
+ if (!profiles) {
19627
+ throw new Error("Not signed in. Run `codevector auth login` first.");
19628
+ }
19629
+ const creds = profiles.profiles[profiles.activeProfile];
19630
+ if (!creds) {
19631
+ throw new Error(
19632
+ `Active profile "${profiles.activeProfile}" is missing from credentials. Run \`codevector auth login\` to recover.`
19633
+ );
19634
+ }
19635
+ ge("codevector system configure");
19636
+ const timestamp = backupTimestamp();
19637
+ const backups = [];
19638
+ for (const tool of TOOLS) {
19639
+ const target = userPathFor(tool);
19640
+ const backup = backupFile(target, tool, timestamp);
19641
+ if (backup) backups.push({ tool, backup });
19642
+ }
19643
+ if (backups.length > 0) {
19644
+ R2.info(
19645
+ `Backed up ${backups.length} existing config file${backups.length === 1 ? "" : "s"} to ${BACKUP_ROOT}/${timestamp}/`
19646
+ );
19647
+ } else {
19648
+ R2.info("No pre-existing user-scope config files to back up.");
19649
+ }
19650
+ const reachable = await fetchReachableChatModels2(creds);
19651
+ const hookPath = installAcceptanceHook();
19652
+ const results = [];
19653
+ for (const tool of TOOLS) {
19654
+ const writer = WRITERS3[tool];
19655
+ try {
19656
+ results.push(
19657
+ writer({
19658
+ gatewayUrl: creds.gatewayUrl,
19659
+ apiKey: creds.apiKey,
19660
+ hookScriptPath: hookPath,
19661
+ scope: "user",
19662
+ availableModels: reachable
19663
+ })
19664
+ );
19665
+ } catch (err) {
19666
+ results.push({
19667
+ tool,
19668
+ status: "skipped",
19669
+ path: "",
19670
+ scope: "user",
19671
+ notes: err instanceof Error ? err.message : String(err)
19672
+ });
19673
+ }
19674
+ }
19675
+ for (const r of results) {
19676
+ if (r.status === "configured") {
19677
+ R2.success(`${r.tool} \u2192 ${r.path} [user]`);
19678
+ if (r.notes) R2.info(r.notes);
19679
+ } else {
19680
+ R2.warn(`${r.tool}: skipped \u2014 ${r.notes ?? "unknown reason"}`);
19681
+ }
19682
+ }
19683
+ if (backups.length > 0) {
19684
+ Se(
19685
+ `To roll back: codevector system restore --timestamp ${timestamp}`,
19686
+ "Rollback available"
19687
+ );
19688
+ }
19689
+ ye("Done.");
19690
+ }
19691
+ });
19692
+ var systemRestoreCommand = defineCommand({
19693
+ meta: {
19694
+ name: "restore",
19695
+ description: "Restore previously backed-up user-scope config files for all editors. Pick a backup run (--timestamp) or use --latest."
18759
19696
  },
18760
19697
  args: {
18761
- kind: {
19698
+ timestamp: {
18762
19699
  type: "string",
18763
- description: "Filter by kind: chat or embedding.",
18764
- valueHint: "chat|embedding"
19700
+ description: "Specific backup timestamp (directory name under ~/.codevector/backups/)."
18765
19701
  },
18766
- json: {
19702
+ latest: {
18767
19703
  type: "boolean",
18768
- description: "Emit raw JSON (for scripting)."
19704
+ description: "Restore the most recent backup without prompting."
19705
+ },
19706
+ yes: {
19707
+ type: "boolean",
19708
+ description: "Skip the confirmation prompt before overwriting current files."
18769
19709
  }
18770
19710
  },
18771
19711
  async run({ args }) {
18772
- const creds = await readCredentials();
18773
- if (!creds) {
18774
- R2.warn("Not signed in. Run `codevector auth login` to get started.");
18775
- process.exitCode = 1;
19712
+ ge("codevector system restore");
19713
+ const runs = listBackupRuns();
19714
+ if (runs.length === 0) {
19715
+ R2.warn(`No backups found under ${BACKUP_ROOT}.`);
19716
+ ye("Nothing to restore.");
18776
19717
  return;
18777
19718
  }
18778
- const kindFilter = args.kind;
18779
- if (kindFilter && kindFilter !== "chat" && kindFilter !== "embedding") {
18780
- throw new Error(`Invalid --kind "${kindFilter}". Use "chat" or "embedding".`);
19719
+ let chosen = runs[0];
19720
+ if (args.timestamp) {
19721
+ const match = runs.find((r) => r.timestamp === args.timestamp);
19722
+ if (!match) {
19723
+ throw new Error(
19724
+ `No backup found with timestamp "${args.timestamp}". Available: ${runs.map((r) => r.timestamp).join(", ")}.`
19725
+ );
19726
+ }
19727
+ chosen = match;
19728
+ } else if (!args.latest) {
19729
+ const picked = unwrap(
19730
+ await xe({
19731
+ message: "Which backup do you want to restore? (arrow keys to move, enter to select)",
19732
+ options: runs.map((r) => ({
19733
+ value: r.timestamp,
19734
+ label: r.timestamp,
19735
+ hint: `${backupRunMtime(r.dir).toLocaleString()} \xB7 ${r.entries.length} file${r.entries.length === 1 ? "" : "s"}`
19736
+ })),
19737
+ initialValue: runs[0].timestamp
19738
+ })
19739
+ );
19740
+ const match = runs.find((r) => r.timestamp === picked);
19741
+ if (!match) throw new Error(`Internal error: picked timestamp not found (${picked}).`);
19742
+ chosen = match;
19743
+ }
19744
+ R2.info(`Backup ${chosen.timestamp} \u2014 ${chosen.entries.length} file(s):`);
19745
+ const plan = [];
19746
+ for (const e2 of chosen.entries) {
19747
+ if (!isTool2(e2.tool)) {
19748
+ R2.warn(` skip ${e2.tool} (unknown tool in backup)`);
19749
+ continue;
19750
+ }
19751
+ const target = userPathFor(e2.tool);
19752
+ plan.push({ tool: e2.tool, backup: e2.backup, target });
19753
+ R2.info(` ${e2.tool} \u2192 ${target}${existsSync11(target) ? " (will overwrite)" : " (new)"}`);
18781
19754
  }
18782
- const client = gatewayClient(creds.gatewayUrl, creds.apiKey, 1e4);
18783
- const s = args.json ? null : ft();
18784
- s?.start("Loading models\u2026");
18785
- try {
18786
- const res = await call(parseResponse(client.models.$get()));
18787
- const filtered = kindFilter ? res.data.filter((m) => m.kind === kindFilter) : res.data;
18788
- s?.stop(`${filtered.length} model${filtered.length === 1 ? "" : "s"} reachable`);
18789
- if (args.json) {
18790
- process.stdout.write(`${JSON.stringify(filtered, null, 2)}
18791
- `);
19755
+ if (plan.length === 0) {
19756
+ ye("Nothing to restore.");
19757
+ return;
19758
+ }
19759
+ if (!args.yes) {
19760
+ const ok = await confirmRestore();
19761
+ if (!ok) {
19762
+ ye("Aborted.");
18792
19763
  return;
18793
19764
  }
18794
- if (filtered.length === 0) {
18795
- R2.info(
18796
- kindFilter ? `No ${kindFilter} models available. Ask your gateway admin to grant access.` : "No models available. Ask your gateway admin to grant access."
18797
- );
18798
- return;
19765
+ }
19766
+ for (const step of plan) {
19767
+ try {
19768
+ restoreBackup(step.backup, step.target);
19769
+ R2.success(`Restored ${step.tool} \u2192 ${step.target}`);
19770
+ } catch (err) {
19771
+ R2.error(`${step.tool}: ${err instanceof Error ? err.message : String(err)}`);
19772
+ process.exitCode = 1;
18799
19773
  }
18800
- const headers = [
18801
- "SLUG",
18802
- "KIND",
18803
- "STATUS",
18804
- "PROVIDER",
18805
- "CONTEXT",
18806
- "IN $/MTOK",
18807
- "OUT $/MTOK",
18808
- "NAME"
18809
- ];
18810
- const cells = filtered.map((m) => [
18811
- m.slug,
18812
- m.kind,
18813
- m.status,
18814
- m.providerKind ?? "\u2014",
18815
- m.contextWindow ? m.contextWindow.toLocaleString() : "\u2014",
18816
- m.inputPricePerMtok ?? "\u2014",
18817
- m.outputPricePerMtok ?? "\u2014",
18818
- m.displayName
18819
- ]);
18820
- Se(renderTable(headers, cells), "Reachable models");
18821
- } catch (err) {
18822
- s?.stop("Could not load models");
18823
- R2.error(err instanceof ApiClientError ? err.message : String(err));
18824
- process.exitCode = 1;
18825
19774
  }
19775
+ ye("Done.");
18826
19776
  }
18827
19777
  });
18828
- var modelsCommand = defineCommand({
19778
+ async function confirmRestore() {
19779
+ const v2 = await ue({
19780
+ message: "Overwrite current user-scope config files with this backup?",
19781
+ initialValue: false
19782
+ });
19783
+ if (q(v2)) return false;
19784
+ return v2 === true;
19785
+ }
19786
+ function isTool2(value) {
19787
+ return TOOLS.includes(value);
19788
+ }
19789
+ async function fetchReachableChatModels2(creds) {
19790
+ const client = gatewayClient(creds.gatewayUrl, creds.apiKey, 1e4);
19791
+ const s = ft();
19792
+ s.start("Loading reachable models\u2026");
19793
+ try {
19794
+ const res = await call(parseResponse(client.models.$get()));
19795
+ const models = res.data.filter((m2) => m2.kind === "chat").map((m2) => ({
19796
+ slug: m2.slug,
19797
+ displayName: m2.displayName,
19798
+ contextWindow: m2.contextWindow,
19799
+ maxOutputTokens: m2.maxOutputTokens,
19800
+ maxInputTokens: m2.maxInputTokens,
19801
+ inputPricePerMtok: m2.inputPricePerMtok,
19802
+ cachedInputPricePerMtok: m2.cachedInputPricePerMtok,
19803
+ cacheWritePricePerMtok: m2.cacheWritePricePerMtok,
19804
+ reasoningPricePerMtok: m2.reasoningPricePerMtok,
19805
+ outputPricePerMtok: m2.outputPricePerMtok,
19806
+ supportsReasoning: m2.supportsReasoning,
19807
+ supportsAttachment: m2.supportsAttachment,
19808
+ supportsToolCall: m2.supportsToolCall,
19809
+ supportsTemperature: m2.supportsTemperature,
19810
+ inputModalities: m2.inputModalities,
19811
+ outputModalities: m2.outputModalities,
19812
+ releaseDate: m2.releaseDate,
19813
+ family: m2.family
19814
+ }));
19815
+ s.stop(`${models.length} model${models.length === 1 ? "" : "s"} reachable`);
19816
+ return models;
19817
+ } catch (err) {
19818
+ s.stop("Could not load models");
19819
+ R2.warn(
19820
+ `Continuing without model list \u2014 ${err instanceof ApiClientError ? err.message : String(err)}.`
19821
+ );
19822
+ return [];
19823
+ }
19824
+ }
19825
+ var systemCommand = defineCommand({
18829
19826
  meta: {
18830
- name: "models",
18831
- description: "List and manage models."
19827
+ name: "system",
19828
+ description: "Machine-wide (user-scope) operations. Use `system configure` for global setup of all editors and `system restore` to roll back."
18832
19829
  },
18833
19830
  subCommands: {
18834
- list: modelsListCommand,
18835
- sync: modelsSyncCommand
19831
+ configure: systemConfigureCommand,
19832
+ restore: systemRestoreCommand
18836
19833
  }
18837
19834
  });
18838
19835
 
18839
- // src/commands/status.ts
18840
- var statusCommand = defineCommand({
19836
+ // src/commands/update.ts
19837
+ import { spawnSync } from "child_process";
19838
+
19839
+ // package.json
19840
+ var package_default = {
19841
+ name: "@codevector/cli",
19842
+ version: "0.4.0",
19843
+ description: "CodeVector CLI \u2014 installs and configures first-party coding-tool integrations.",
19844
+ license: "UNLICENSED",
19845
+ bin: {
19846
+ codevector: "./bin/codevector.mjs"
19847
+ },
19848
+ files: [
19849
+ "bin",
19850
+ "dist",
19851
+ "scripts",
19852
+ "src/hooks"
19853
+ ],
19854
+ type: "module",
19855
+ publishConfig: {
19856
+ access: "public"
19857
+ },
19858
+ scripts: {
19859
+ dev: "tsx src/index.ts",
19860
+ build: "tsup",
19861
+ "type-check": "tsc --noEmit",
19862
+ test: "vitest run",
19863
+ "test:watch": "vitest",
19864
+ postinstall: "node scripts/postinstall.mjs"
19865
+ },
19866
+ dependencies: {
19867
+ "@clack/prompts": "1.4.0",
19868
+ citty: "^0.2.2",
19869
+ hono: "4.12.16",
19870
+ "smol-toml": "^1.6.1"
19871
+ },
19872
+ devDependencies: {
19873
+ "@codevector/api": "workspace:*",
19874
+ "@codevector/common": "workspace:*",
19875
+ "@types/node": "25.6.0",
19876
+ tsup: "^8.5.1",
19877
+ tsx: "4.21.0",
19878
+ typescript: "6.0.3",
19879
+ vitest: "4.1.5",
19880
+ zod: "4.4.2"
19881
+ },
19882
+ engines: {
19883
+ node: ">=20"
19884
+ }
19885
+ };
19886
+
19887
+ // src/lib/install-pref.ts
19888
+ import { existsSync as existsSync12, mkdirSync as mkdirSync7, readFileSync as readFileSync10, renameSync as renameSync4, writeFileSync as writeFileSync7 } from "fs";
19889
+ import { join as join10 } from "path";
19890
+ var INSTALL_PREF_FILE = join10(CODEVECTOR_CONFIG_DIR, "install.json");
19891
+ var PACKAGE_MANAGERS = ["npm", "pnpm", "yarn"];
19892
+ function isPackageManager(v2) {
19893
+ return typeof v2 === "string" && PACKAGE_MANAGERS.includes(v2);
19894
+ }
19895
+ function readInstallPref() {
19896
+ if (!existsSync12(INSTALL_PREF_FILE)) return void 0;
19897
+ try {
19898
+ const raw = readFileSync10(INSTALL_PREF_FILE, "utf8");
19899
+ const parsed = JSON.parse(raw);
19900
+ if (!isPackageManager(parsed.packageManager)) return void 0;
19901
+ const source = parsed.source === "user" ? "user" : "auto";
19902
+ return {
19903
+ packageManager: parsed.packageManager,
19904
+ source,
19905
+ detectedAt: typeof parsed.detectedAt === "string" ? parsed.detectedAt : (/* @__PURE__ */ new Date()).toISOString()
19906
+ };
19907
+ } catch {
19908
+ return void 0;
19909
+ }
19910
+ }
19911
+ function writeInstallPref(pref) {
19912
+ mkdirSync7(CODEVECTOR_CONFIG_DIR, { recursive: true, mode: 448 });
19913
+ const tmp = `${INSTALL_PREF_FILE}.${process.pid}.tmp`;
19914
+ writeFileSync7(tmp, JSON.stringify(pref, null, 2));
19915
+ renameSync4(tmp, INSTALL_PREF_FILE);
19916
+ }
19917
+
19918
+ // src/commands/update.ts
19919
+ var PKG_NAME = "@codevector/cli";
19920
+ var updateCommand = defineCommand({
18841
19921
  meta: {
18842
- name: "status",
18843
- description: "Show current login + gateway reachability."
19922
+ name: "update",
19923
+ description: `Update ${PKG_NAME} to the latest version on npm. Uses the package manager that installed it (auto-detected on install) unless --with overrides.`
18844
19924
  },
18845
- async run() {
18846
- const creds = await readCredentials();
18847
- if (!creds) {
18848
- R2.warn("Not signed in. Run `codevector auth login` to get started.");
18849
- process.exitCode = 1;
18850
- return;
19925
+ args: {
19926
+ with: {
19927
+ type: "string",
19928
+ description: "Package manager to invoke and remember for next time.",
19929
+ valueHint: "npm|pnpm|yarn"
18851
19930
  }
18852
- Se(
18853
- `Signed in as ${creds.email}
18854
- Gateway: ${creds.gatewayUrl}
18855
- API key: ${maskApiKey(creds.apiKey)}
18856
- Last saved: ${creds.savedAt}`,
18857
- "Credentials"
18858
- );
18859
- const client = gatewayClient(creds.gatewayUrl, creds.apiKey, 1e4);
18860
- const s = ft();
18861
- s.start("Reaching gateway\u2026");
18862
- try {
18863
- const res = await call(parseResponse(client.models.$get()));
18864
- s.stop(
18865
- res.data.length === 0 ? "Gateway reachable \u2014 no models granted. Ask your admin for access." : `Gateway reachable \u2014 ${res.data.length} model${res.data.length === 1 ? "" : "s"} available. Run \`codevector models\` to list them.`
19931
+ },
19932
+ async run({ args }) {
19933
+ ge("codevector update");
19934
+ R2.info(`Current version: ${package_default.version}`);
19935
+ const manager = await resolvePackageManager(args.with);
19936
+ const command = installCommand(manager);
19937
+ R2.info(`Using ${manager}: ${command.cmd} ${command.args.join(" ")}`);
19938
+ const result = spawnSync(command.cmd, command.args, { stdio: "inherit" });
19939
+ if (result.error) {
19940
+ throw new Error(
19941
+ `Failed to invoke ${command.cmd}: ${result.error.message}. Is ${command.cmd} on your PATH?`
19942
+ );
19943
+ }
19944
+ if (typeof result.status === "number" && result.status !== 0) {
19945
+ throw new Error(
19946
+ `${command.cmd} exited with status ${result.status}. The new version was not installed.`
18866
19947
  );
18867
- } catch (err) {
18868
- s.stop("Gateway unreachable");
18869
- R2.error(err instanceof ApiClientError ? err.message : String(err));
18870
- process.exitCode = 1;
18871
19948
  }
19949
+ R2.success("Update complete. Run `codevector --version` to confirm.");
19950
+ ye("Done.");
18872
19951
  }
18873
19952
  });
19953
+ async function resolvePackageManager(raw) {
19954
+ if (raw) {
19955
+ const pm = parsePackageManager(raw);
19956
+ if (!pm) {
19957
+ throw new Error(`Unsupported --with "${raw}". Use npm, pnpm, or yarn.`);
19958
+ }
19959
+ writeInstallPref({ packageManager: pm, source: "user", detectedAt: (/* @__PURE__ */ new Date()).toISOString() });
19960
+ return pm;
19961
+ }
19962
+ const pref = readInstallPref();
19963
+ if (pref) return pref.packageManager;
19964
+ R2.info(
19965
+ `Couldn't detect which package manager installed ${PKG_NAME} (no record at ${INSTALL_PREF_FILE}).`
19966
+ );
19967
+ const picked = unwrap(
19968
+ await xe({
19969
+ message: "Which package manager should `update` use? (remembered for next time)",
19970
+ options: [
19971
+ { value: "npm", label: "npm", hint: "npm install -g @codevector/cli@latest" },
19972
+ { value: "pnpm", label: "pnpm", hint: "pnpm add -g @codevector/cli@latest" },
19973
+ { value: "yarn", label: "yarn", hint: "yarn global add @codevector/cli@latest" }
19974
+ ],
19975
+ initialValue: "npm"
19976
+ })
19977
+ );
19978
+ writeInstallPref({
19979
+ packageManager: picked,
19980
+ source: "user",
19981
+ detectedAt: (/* @__PURE__ */ new Date()).toISOString()
19982
+ });
19983
+ return picked;
19984
+ }
19985
+ function parsePackageManager(value) {
19986
+ switch (value.trim()) {
19987
+ case "npm":
19988
+ case "pnpm":
19989
+ case "yarn":
19990
+ return value.trim();
19991
+ default:
19992
+ return void 0;
19993
+ }
19994
+ }
19995
+ function installCommand(manager) {
19996
+ const target = `${PKG_NAME}@latest`;
19997
+ switch (manager) {
19998
+ case "npm":
19999
+ return { cmd: "npm", args: ["install", "-g", target] };
20000
+ case "pnpm":
20001
+ return { cmd: "pnpm", args: ["add", "-g", target] };
20002
+ case "yarn":
20003
+ return { cmd: "yarn", args: ["global", "add", target] };
20004
+ }
20005
+ }
18874
20006
 
18875
20007
  // src/commands/usage.ts
18876
20008
  var usageCommand = defineCommand({
@@ -18959,52 +20091,6 @@ function buildQuery(args) {
18959
20091
  return args.end ? { end: args.end } : {};
18960
20092
  }
18961
20093
 
18962
- // package.json
18963
- var package_default = {
18964
- name: "@codevector/cli",
18965
- version: "0.3.4",
18966
- description: "CodeVector CLI \u2014 installs and configures first-party coding-tool integrations.",
18967
- license: "UNLICENSED",
18968
- bin: {
18969
- codevector: "./bin/codevector.mjs"
18970
- },
18971
- files: [
18972
- "bin",
18973
- "dist",
18974
- "src/hooks"
18975
- ],
18976
- type: "module",
18977
- publishConfig: {
18978
- access: "public"
18979
- },
18980
- scripts: {
18981
- dev: "tsx src/index.ts",
18982
- build: "tsup",
18983
- "type-check": "tsc --noEmit",
18984
- test: "vitest run",
18985
- "test:watch": "vitest"
18986
- },
18987
- dependencies: {
18988
- "@clack/prompts": "1.3.0",
18989
- citty: "^0.2.2",
18990
- hono: "4.12.16",
18991
- "smol-toml": "^1.6.1"
18992
- },
18993
- devDependencies: {
18994
- "@codevector/api": "workspace:*",
18995
- "@codevector/common": "workspace:*",
18996
- "@types/node": "25.6.0",
18997
- tsup: "^8.5.1",
18998
- tsx: "4.21.0",
18999
- typescript: "6.0.3",
19000
- vitest: "4.1.5",
19001
- zod: "4.4.2"
19002
- },
19003
- engines: {
19004
- node: ">=20"
19005
- }
19006
- };
19007
-
19008
20094
  // src/commands/version.ts
19009
20095
  var versionCommand = defineCommand({
19010
20096
  meta: {
@@ -19016,6 +20102,89 @@ var versionCommand = defineCommand({
19016
20102
  }
19017
20103
  });
19018
20104
 
20105
+ // src/lib/update-notifier.ts
20106
+ import { existsSync as existsSync13, mkdirSync as mkdirSync8, readFileSync as readFileSync11, writeFileSync as writeFileSync8 } from "fs";
20107
+ import { join as join11 } from "path";
20108
+ var PKG_NAME2 = "@codevector/cli";
20109
+ var REGISTRY_URL = `https://registry.npmjs.org/${PKG_NAME2}/latest`;
20110
+ var CHECK_CACHE_FILE = join11(CODEVECTOR_CONFIG_DIR, "update-check.json");
20111
+ var CHECK_TTL_MS = 24 * 60 * 60 * 1e3;
20112
+ var FETCH_TIMEOUT_MS = 2e3;
20113
+ function readCache() {
20114
+ if (!existsSync13(CHECK_CACHE_FILE)) return void 0;
20115
+ try {
20116
+ const raw = readFileSync11(CHECK_CACHE_FILE, "utf8");
20117
+ const parsed = JSON.parse(raw);
20118
+ if (typeof parsed.checkedAt !== "number") return void 0;
20119
+ return {
20120
+ latest: typeof parsed.latest === "string" ? parsed.latest : null,
20121
+ checkedAt: parsed.checkedAt
20122
+ };
20123
+ } catch {
20124
+ return void 0;
20125
+ }
20126
+ }
20127
+ function writeCache(cache) {
20128
+ try {
20129
+ mkdirSync8(CODEVECTOR_CONFIG_DIR, { recursive: true, mode: 448 });
20130
+ writeFileSync8(CHECK_CACHE_FILE, JSON.stringify(cache));
20131
+ } catch {
20132
+ }
20133
+ }
20134
+ function isNewer(current, latest) {
20135
+ const a = current.split(".").map((p2) => Number.parseInt(p2, 10));
20136
+ const b2 = latest.split(".").map((p2) => Number.parseInt(p2, 10));
20137
+ for (let i = 0; i < Math.max(a.length, b2.length); i++) {
20138
+ const ai = a[i] ?? 0;
20139
+ const bi = b2[i] ?? 0;
20140
+ if (Number.isNaN(ai) || Number.isNaN(bi)) return latest > current;
20141
+ if (bi > ai) return true;
20142
+ if (bi < ai) return false;
20143
+ }
20144
+ return false;
20145
+ }
20146
+ function printUpdateNoticeIfCached(currentVersion) {
20147
+ if (process.env.CODEVECTOR_NO_UPDATE_CHECK === "1") return;
20148
+ try {
20149
+ const cache = readCache();
20150
+ if (!cache || !cache.latest) return;
20151
+ if (isNewer(currentVersion, cache.latest)) {
20152
+ process.stderr.write(
20153
+ `\x1B[33m\u203A\x1B[0m A new version of @codevector/cli is available: ${currentVersion} \u2192 ${cache.latest}. Run \`codevector update\` to install.
20154
+ `
20155
+ );
20156
+ }
20157
+ } catch {
20158
+ }
20159
+ }
20160
+ function scheduleUpdateCheck() {
20161
+ if (process.env.CODEVECTOR_NO_UPDATE_CHECK === "1") return;
20162
+ const cache = readCache();
20163
+ const now = Date.now();
20164
+ if (cache && now - cache.checkedAt < CHECK_TTL_MS) return;
20165
+ void fetchLatestVersion().then((latest) => {
20166
+ writeCache({ latest, checkedAt: Date.now() });
20167
+ }).catch(() => {
20168
+ writeCache({ latest: cache?.latest ?? null, checkedAt: Date.now() });
20169
+ });
20170
+ }
20171
+ async function fetchLatestVersion() {
20172
+ const controller = new AbortController();
20173
+ const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
20174
+ timer.unref?.();
20175
+ try {
20176
+ const res = await fetch(REGISTRY_URL, {
20177
+ signal: controller.signal,
20178
+ headers: { accept: "application/json" }
20179
+ });
20180
+ if (!res.ok) return null;
20181
+ const body = await res.json();
20182
+ return typeof body.version === "string" ? body.version : null;
20183
+ } finally {
20184
+ clearTimeout(timer);
20185
+ }
20186
+ }
20187
+
19019
20188
  // src/index.ts
19020
20189
  var main = defineCommand({
19021
20190
  meta: {
@@ -19029,11 +20198,16 @@ var main = defineCommand({
19029
20198
  init: initCommand,
19030
20199
  doctor: doctorCommand,
19031
20200
  status: statusCommand,
20201
+ system: systemCommand,
19032
20202
  models: modelsCommand,
20203
+ profile: profileCommand,
20204
+ update: updateCommand,
19033
20205
  usage: usageCommand,
19034
20206
  version: versionCommand
19035
20207
  }
19036
20208
  });
20209
+ printUpdateNoticeIfCached(package_default.version);
20210
+ scheduleUpdateCheck();
19037
20211
  runMain(main);
19038
20212
  /*! Bundled license information:
19039
20213