bitfab-cli 0.2.179 → 0.2.181

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.
Files changed (2) hide show
  1. package/dist/index.js +622 -92
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -5155,8 +5155,8 @@ var require_utils = __commonJS({
5155
5155
  }
5156
5156
  return ind;
5157
5157
  }
5158
- function removeDotSegments(path20) {
5159
- let input = path20;
5158
+ function removeDotSegments(path21) {
5159
+ let input = path21;
5160
5160
  const output = [];
5161
5161
  let nextSlash = -1;
5162
5162
  let len = 0;
@@ -5408,8 +5408,8 @@ var require_schemes = __commonJS({
5408
5408
  wsComponent.secure = void 0;
5409
5409
  }
5410
5410
  if (wsComponent.resourceName) {
5411
- const [path20, query] = wsComponent.resourceName.split("?");
5412
- wsComponent.path = path20 && path20 !== "/" ? path20 : void 0;
5411
+ const [path21, query] = wsComponent.resourceName.split("?");
5412
+ wsComponent.path = path21 && path21 !== "/" ? path21 : void 0;
5413
5413
  wsComponent.query = query;
5414
5414
  wsComponent.resourceName = void 0;
5415
5415
  }
@@ -8802,12 +8802,12 @@ var require_dist = __commonJS({
8802
8802
  throw new Error(`Unknown format "${name}"`);
8803
8803
  return f;
8804
8804
  };
8805
- function addFormats(ajv, list, fs23, exportName) {
8805
+ function addFormats(ajv, list, fs24, exportName) {
8806
8806
  var _a2;
8807
8807
  var _b;
8808
8808
  (_a2 = (_b = ajv.opts.code).formats) !== null && _a2 !== void 0 ? _a2 : _b.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
8809
8809
  for (const f of list)
8810
- ajv.addFormat(f, fs23[f]);
8810
+ ajv.addFormat(f, fs24[f]);
8811
8811
  }
8812
8812
  module.exports = exports = formatsPlugin;
8813
8813
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -9013,18 +9013,18 @@ async function closeStudio(client, message) {
9013
9013
  data: message ? { message } : {}
9014
9014
  });
9015
9015
  }
9016
- async function navigateStudio(client, path20) {
9017
- if (!isValidStudioRoute(path20)) {
9018
- throw new Error(`Studio route not allowed: ${path20}`);
9016
+ async function navigateStudio(client, path21) {
9017
+ if (!isValidStudioRoute(path21)) {
9018
+ throw new Error(`Studio route not allowed: ${path21}`);
9019
9019
  }
9020
9020
  return pushAgentSessionEvent(client, {
9021
9021
  type: "agent:navigate",
9022
- data: { path: path20 }
9022
+ data: { path: path21 }
9023
9023
  });
9024
9024
  }
9025
9025
  var STUDIO_NAVIGATE_ACK_TIMEOUT_MS = 12e3;
9026
9026
  var PING_PONG_TIMEOUT_MS = 5e3;
9027
- function awaitNavigateAck(client, path20, ackTimeoutMs, startCursor) {
9027
+ function awaitNavigateAck(client, path21, ackTimeoutMs, startCursor) {
9028
9028
  const abortController = new AbortController();
9029
9029
  let timer = null;
9030
9030
  const result = new Promise((resolve2) => {
@@ -9032,7 +9032,7 @@ function awaitNavigateAck(client, path20, ackTimeoutMs, startCursor) {
9032
9032
  resolve2({ acked: false, reason: "timeout" });
9033
9033
  abortController.abort();
9034
9034
  }, ackTimeoutMs);
9035
- const pathOnly = path20.split("?")[0];
9035
+ const pathOnly = path21.split("?")[0];
9036
9036
  pollAgentSessionEvents({
9037
9037
  serviceUrl: client.serviceUrl,
9038
9038
  apiKey: client.apiKey,
@@ -9118,18 +9118,18 @@ function awaitPong(client, pingTs) {
9118
9118
  });
9119
9119
  });
9120
9120
  }
9121
- async function navigateStudioAndAwaitAck(client, path20, opts = {}) {
9121
+ async function navigateStudioAndAwaitAck(client, path21, opts = {}) {
9122
9122
  const ackTimeoutMs = opts.ackTimeoutMs ?? STUDIO_NAVIGATE_ACK_TIMEOUT_MS;
9123
- if (!isValidStudioRoute(path20)) {
9123
+ if (!isValidStudioRoute(path21)) {
9124
9124
  return { acked: false, reason: "invalid-route" };
9125
9125
  }
9126
9126
  const startCursor = await fetchStreamTip(client);
9127
9127
  try {
9128
- await navigateStudio(client, path20);
9128
+ await navigateStudio(client, path21);
9129
9129
  } catch {
9130
9130
  return { acked: false, reason: "push-failed" };
9131
9131
  }
9132
- const result = await awaitNavigateAck(client, path20, ackTimeoutMs, startCursor);
9132
+ const result = await awaitNavigateAck(client, path21, ackTimeoutMs, startCursor);
9133
9133
  if (result.acked || result.reason !== "timeout") {
9134
9134
  return result;
9135
9135
  }
@@ -10077,20 +10077,20 @@ function resolveStudioInitialPath(input) {
10077
10077
  return DEFAULT_STUDIO_PATH;
10078
10078
  }
10079
10079
  function buildStudioSessionUrl(args) {
10080
- const { serviceUrl, path: path20, sessionId } = args;
10081
- if (path20.includes("session=")) {
10082
- return `${serviceUrl}${path20}`;
10080
+ const { serviceUrl, path: path21, sessionId } = args;
10081
+ if (path21.includes("session=")) {
10082
+ return `${serviceUrl}${path21}`;
10083
10083
  }
10084
- const separator = path20.includes("?") ? "&" : "?";
10085
- return `${serviceUrl}${path20}${separator}session=${encodeURIComponent(sessionId)}`;
10084
+ const separator = path21.includes("?") ? "&" : "?";
10085
+ return `${serviceUrl}${path21}${separator}session=${encodeURIComponent(sessionId)}`;
10086
10086
  }
10087
10087
  function resolveStudioSessionTarget(args) {
10088
- const path20 = resolveStudioInitialPath(args.path);
10088
+ const path21 = resolveStudioInitialPath(args.path);
10089
10089
  return {
10090
- path: path20,
10090
+ path: path21,
10091
10091
  url: buildStudioSessionUrl({
10092
10092
  serviceUrl: args.serviceUrl,
10093
- path: path20,
10093
+ path: path21,
10094
10094
  sessionId: args.sessionId
10095
10095
  })
10096
10096
  };
@@ -10751,8 +10751,8 @@ function tailStudioEvents(key, onLine, opts) {
10751
10751
  }
10752
10752
 
10753
10753
  // ../bitfab-plugin-lib/dist/studioChannel.js
10754
- function stripSessionId(path20, sessionId) {
10755
- return sessionId ? path20.split(sessionId).join("__SID__") : path20;
10754
+ function stripSessionId(path21, sessionId) {
10755
+ return sessionId ? path21.split(sessionId).join("__SID__") : path21;
10756
10756
  }
10757
10757
  var DirectChannel = class {
10758
10758
  apiKey;
@@ -10767,10 +10767,10 @@ var DirectChannel = class {
10767
10767
  isPersistent() {
10768
10768
  return false;
10769
10769
  }
10770
- async openOrNavigate(path20, opts) {
10770
+ async openOrNavigate(path21, opts) {
10771
10771
  if (!this.apiKey) {
10772
10772
  const sessionId2 = opts?.sessionId ?? crypto8.randomUUID();
10773
- const initialPath = resolveStudioInitialPath(path20);
10773
+ const initialPath = resolveStudioInitialPath(path21);
10774
10774
  const live = readActiveStudioSession();
10775
10775
  if (live && live.source === "direct" && !live.authenticated && live.windowState !== "closed" && live.serviceUrl === this.serviceUrl && live.currentPath != null && stripSessionId(live.currentPath, live.sessionId) === stripSessionId(initialPath, sessionId2)) {
10776
10776
  return { sessionId: live.sessionId, opened: false };
@@ -10801,11 +10801,11 @@ var DirectChannel = class {
10801
10801
  serviceUrl: existing.serviceUrl,
10802
10802
  apiKey: this.apiKey,
10803
10803
  sessionId: existing.sessionId
10804
- }, path20);
10804
+ }, path21);
10805
10805
  if (!ack.acked) {
10806
10806
  throw new StudioNavigationError(ack.reason ?? "unknown", ack.blockedReason, existing.sessionId);
10807
10807
  }
10808
- updateActiveStudioSession({ currentPath: path20 });
10808
+ updateActiveStudioSession({ currentPath: path21 });
10809
10809
  return { sessionId: existing.sessionId, opened: false };
10810
10810
  }
10811
10811
  truncateStudioEventLog(resolveAgentSessionKey());
@@ -10814,7 +10814,7 @@ var DirectChannel = class {
10814
10814
  serviceUrl: this.serviceUrl,
10815
10815
  apiKey: this.apiKey,
10816
10816
  sessionId,
10817
- initialPath: path20,
10817
+ initialPath: path21,
10818
10818
  clientHeaders: opts?.clientHeaders
10819
10819
  });
10820
10820
  return { sessionId: session.sessionId, opened: true };
@@ -10881,18 +10881,18 @@ var DaemonChannel = class _DaemonChannel {
10881
10881
  isPersistent() {
10882
10882
  return true;
10883
10883
  }
10884
- async openOrNavigate(path20, opts) {
10884
+ async openOrNavigate(path21, opts) {
10885
10885
  const openRes = await this.client.open({
10886
10886
  key: this.key,
10887
10887
  serviceUrl: this.serviceUrl,
10888
10888
  apiKey: this.apiKey,
10889
10889
  sessionId: opts?.sessionId,
10890
- initialPath: path20,
10890
+ initialPath: path21,
10891
10891
  clientHeaders: opts?.clientHeaders,
10892
10892
  focusTarget: opts?.focusTarget
10893
10893
  });
10894
10894
  if (openRes.reconnected) {
10895
- const navRes = await this.client.navigate(this.key, path20);
10895
+ const navRes = await this.client.navigate(this.key, path21);
10896
10896
  if (!navRes.acked) {
10897
10897
  this.client.destroy();
10898
10898
  throw new StudioNavigationError(navRes.reason ?? "unknown", void 0, openRes.sessionId);
@@ -10950,9 +10950,9 @@ function isDaemonEnabled() {
10950
10950
  function withDirectFallback(primary, apiKey, serviceUrl) {
10951
10951
  let active = primary;
10952
10952
  return {
10953
- async openOrNavigate(path20, opts) {
10953
+ async openOrNavigate(path21, opts) {
10954
10954
  try {
10955
- return await active.openOrNavigate(path20, opts);
10955
+ return await active.openOrNavigate(path21, opts);
10956
10956
  } catch (err) {
10957
10957
  if (err instanceof StudioNavigationError || active !== primary) {
10958
10958
  throw err;
@@ -10960,7 +10960,7 @@ function withDirectFallback(primary, apiKey, serviceUrl) {
10960
10960
  console.error(`daemon failed, falling back to direct: ${err instanceof Error ? err.message : String(err)}`);
10961
10961
  active.destroy();
10962
10962
  active = new DirectChannel(apiKey, serviceUrl);
10963
- return active.openOrNavigate(path20, opts);
10963
+ return active.openOrNavigate(path21, opts);
10964
10964
  }
10965
10965
  },
10966
10966
  async close(sessionId, message) {
@@ -11285,7 +11285,7 @@ function createEventSubscription(channel, sessionId, onEvent, restoreFocus) {
11285
11285
  done: sub.done
11286
11286
  };
11287
11287
  }
11288
- async function openStudioTo(path20, opts = {}) {
11288
+ async function openStudioTo(path21, opts = {}) {
11289
11289
  const config4 = getConfig();
11290
11290
  const serviceUrl = config4.serviceUrl;
11291
11291
  const apiKey = !opts.forceLogin && hasCredentials() ? config4.apiKey : null;
@@ -11363,7 +11363,7 @@ async function openStudioTo(path20, opts = {}) {
11363
11363
  };
11364
11364
  };
11365
11365
  if (!apiKey) {
11366
- const destination = opts.freshWindowPath ?? path20;
11366
+ const destination = opts.freshWindowPath ?? path21;
11367
11367
  const destSeparator = destination.includes("?") ? "&" : "?";
11368
11368
  const buildSignInPath = (sid) => {
11369
11369
  const redirectPath = `${destination}${destSeparator}session=${encodeURIComponent(sid)}&pluginLogin=true`;
@@ -11408,11 +11408,11 @@ async function openStudioTo(path20, opts = {}) {
11408
11408
  channel.setApiKey(loginApiKey);
11409
11409
  return finish(sessionId2, openResult.opened, loginApiKey, openResult.opened ? signInUrl : void 0);
11410
11410
  }
11411
- const { sessionId, opened } = await channel.openOrNavigate(path20, {
11411
+ const { sessionId, opened } = await channel.openOrNavigate(path21, {
11412
11412
  clientHeaders: opts.clientHeaders,
11413
11413
  focusTarget: focusTarget ?? void 0
11414
11414
  });
11415
- const url2 = opened ? resolveStudioSessionTarget({ serviceUrl, path: path20, sessionId }).url : void 0;
11415
+ const url2 = opened ? resolveStudioSessionTarget({ serviceUrl, path: path21, sessionId }).url : void 0;
11416
11416
  if (url2) {
11417
11417
  opts.onWindowOpened?.(url2);
11418
11418
  }
@@ -12293,10 +12293,10 @@ function mergeDefs(...defs) {
12293
12293
  function cloneDef(schema) {
12294
12294
  return mergeDefs(schema._zod.def);
12295
12295
  }
12296
- function getElementAtPath(obj, path20) {
12297
- if (!path20)
12296
+ function getElementAtPath(obj, path21) {
12297
+ if (!path21)
12298
12298
  return obj;
12299
- return path20.reduce((acc, key) => acc?.[key], obj);
12299
+ return path21.reduce((acc, key) => acc?.[key], obj);
12300
12300
  }
12301
12301
  function promiseAllObject(promisesObj) {
12302
12302
  const keys = Object.keys(promisesObj);
@@ -12679,11 +12679,11 @@ function aborted(x, startIndex = 0) {
12679
12679
  }
12680
12680
  return false;
12681
12681
  }
12682
- function prefixIssues(path20, issues) {
12682
+ function prefixIssues(path21, issues) {
12683
12683
  return issues.map((iss) => {
12684
12684
  var _a2;
12685
12685
  (_a2 = iss).path ?? (_a2.path = []);
12686
- iss.path.unshift(path20);
12686
+ iss.path.unshift(path21);
12687
12687
  return iss;
12688
12688
  });
12689
12689
  }
@@ -12866,7 +12866,7 @@ function formatError(error48, mapper = (issue2) => issue2.message) {
12866
12866
  }
12867
12867
  function treeifyError(error48, mapper = (issue2) => issue2.message) {
12868
12868
  const result = { errors: [] };
12869
- const processError = (error49, path20 = []) => {
12869
+ const processError = (error49, path21 = []) => {
12870
12870
  var _a2, _b;
12871
12871
  for (const issue2 of error49.issues) {
12872
12872
  if (issue2.code === "invalid_union" && issue2.errors.length) {
@@ -12876,7 +12876,7 @@ function treeifyError(error48, mapper = (issue2) => issue2.message) {
12876
12876
  } else if (issue2.code === "invalid_element") {
12877
12877
  processError({ issues: issue2.issues }, issue2.path);
12878
12878
  } else {
12879
- const fullpath = [...path20, ...issue2.path];
12879
+ const fullpath = [...path21, ...issue2.path];
12880
12880
  if (fullpath.length === 0) {
12881
12881
  result.errors.push(mapper(issue2));
12882
12882
  continue;
@@ -12908,8 +12908,8 @@ function treeifyError(error48, mapper = (issue2) => issue2.message) {
12908
12908
  }
12909
12909
  function toDotPath(_path) {
12910
12910
  const segs = [];
12911
- const path20 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
12912
- for (const seg of path20) {
12911
+ const path21 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
12912
+ for (const seg of path21) {
12913
12913
  if (typeof seg === "number")
12914
12914
  segs.push(`[${seg}]`);
12915
12915
  else if (typeof seg === "symbol")
@@ -24886,13 +24886,13 @@ function resolveRef(ref, ctx) {
24886
24886
  if (!ref.startsWith("#")) {
24887
24887
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
24888
24888
  }
24889
- const path20 = ref.slice(1).split("/").filter(Boolean);
24890
- if (path20.length === 0) {
24889
+ const path21 = ref.slice(1).split("/").filter(Boolean);
24890
+ if (path21.length === 0) {
24891
24891
  return ctx.rootSchema;
24892
24892
  }
24893
24893
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
24894
- if (path20[0] === defsKey) {
24895
- const key = path20[1];
24894
+ if (path21[0] === defsKey) {
24895
+ const key = path21[1];
24896
24896
  if (!key || !ctx.defs[key]) {
24897
24897
  throw new Error(`Reference not found: ${ref}`);
24898
24898
  }
@@ -25730,8 +25730,8 @@ function getErrorMap2() {
25730
25730
 
25731
25731
  // ../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v3/helpers/parseUtil.js
25732
25732
  var makeIssue = (params) => {
25733
- const { data, path: path20, errorMaps, issueData } = params;
25734
- const fullPath = [...path20, ...issueData.path || []];
25733
+ const { data, path: path21, errorMaps, issueData } = params;
25734
+ const fullPath = [...path21, ...issueData.path || []];
25735
25735
  const fullIssue = {
25736
25736
  ...issueData,
25737
25737
  path: fullPath
@@ -25846,11 +25846,11 @@ var errorUtil;
25846
25846
 
25847
25847
  // ../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v3/types.js
25848
25848
  var ParseInputLazyPath = class {
25849
- constructor(parent, value, path20, key) {
25849
+ constructor(parent, value, path21, key) {
25850
25850
  this._cachedPath = [];
25851
25851
  this.parent = parent;
25852
25852
  this.data = value;
25853
- this._path = path20;
25853
+ this._path = path21;
25854
25854
  this._key = key;
25855
25855
  }
25856
25856
  get path() {
@@ -30957,6 +30957,27 @@ function cliUpdate(cfg, args, skipPermissions) {
30957
30957
  }
30958
30958
  }
30959
30959
 
30960
+ // src/redaction.ts
30961
+ var SECRET_PATTERN = /bf_[A-Za-z0-9]+/g;
30962
+ var STREAMING_SECRET_TAIL_LENGTH = 512;
30963
+ function redactSecrets(text) {
30964
+ return text.replace(SECRET_PATTERN, "bf_[redacted]");
30965
+ }
30966
+ var StreamingSecretRedactor = class {
30967
+ pending = "";
30968
+ redact(chunk2) {
30969
+ const text = this.pending + chunk2;
30970
+ const flushLength = Math.max(0, text.length - STREAMING_SECRET_TAIL_LENGTH);
30971
+ this.pending = text.slice(flushLength);
30972
+ return redactSecrets(text.slice(0, flushLength));
30973
+ }
30974
+ flush() {
30975
+ const redacted = redactSecrets(this.pending);
30976
+ this.pending = "";
30977
+ return redacted;
30978
+ }
30979
+ };
30980
+
30960
30981
  // src/claude.ts
30961
30982
  var CLI = "claude";
30962
30983
  var SKIP_FLAG = "--dangerously-skip-permissions";
@@ -31035,9 +31056,27 @@ async function runClaudeAnalyzeRepo(captureOverride, limit) {
31035
31056
  }
31036
31057
  const command = limit === void 0 ? ANALYZE_REPO_COMMAND : `${ANALYZE_REPO_COMMAND} limit=${limit}`;
31037
31058
  const out = fs20.createWriteStream(logPath);
31038
- const spinner5 = p3.spinner();
31039
- spinner5.start("Analyzing repo and drafting trace plans");
31059
+ const spinner6 = p3.spinner();
31060
+ spinner6.start("Analyzing repo and drafting trace plans");
31040
31061
  const stdoutChunks = [];
31062
+ const stdoutRedactor = new StreamingSecretRedactor();
31063
+ const stderrRedactor = new StreamingSecretRedactor();
31064
+ let redactorsFlushed = false;
31065
+ const flushRedactors = () => {
31066
+ if (redactorsFlushed) {
31067
+ return;
31068
+ }
31069
+ redactorsFlushed = true;
31070
+ const stdoutTail = stdoutRedactor.flush();
31071
+ if (stdoutTail !== "") {
31072
+ out.write(stdoutTail);
31073
+ stdoutChunks.push(stdoutTail);
31074
+ }
31075
+ const stderrTail = stderrRedactor.flush();
31076
+ if (stderrTail !== "") {
31077
+ out.write(stderrTail);
31078
+ }
31079
+ };
31041
31080
  try {
31042
31081
  const exitCode = await new Promise((resolve2, reject) => {
31043
31082
  const child = spawn5(
@@ -31053,31 +31092,49 @@ async function runClaudeAnalyzeRepo(captureOverride, limit) {
31053
31092
  { stdio: ["ignore", "pipe", "pipe"], env, ...SHELL_OPTS }
31054
31093
  );
31055
31094
  child.stdout.on("data", (chunk2) => {
31056
- out.write(chunk2);
31057
31095
  const text = chunk2.toString();
31058
- stdoutChunks.push(text);
31096
+ const redacted = stdoutRedactor.redact(text);
31097
+ if (redacted !== "") {
31098
+ out.write(redacted);
31099
+ stdoutChunks.push(redacted);
31100
+ }
31059
31101
  const label = latestActivityLabel(text);
31060
31102
  if (label) {
31061
- spinner5.message(label);
31103
+ spinner6.message(label);
31062
31104
  }
31063
31105
  });
31064
- child.stderr.on("data", (chunk2) => out.write(chunk2));
31106
+ child.stderr.on(
31107
+ "data",
31108
+ (chunk2) => out.write(stderrRedactor.redact(chunk2.toString()))
31109
+ );
31065
31110
  child.on("error", reject);
31066
- child.on("close", (code) => resolve2(code ?? 0));
31111
+ child.on("close", (code, signal) => {
31112
+ if (code === null) {
31113
+ reject(
31114
+ new Error(
31115
+ `analyze-repo terminated by signal ${signal ?? "unknown"}. See ${logPath}`
31116
+ )
31117
+ );
31118
+ return;
31119
+ }
31120
+ resolve2(code);
31121
+ });
31067
31122
  });
31123
+ flushRedactors();
31068
31124
  if (exitCode !== 0) {
31069
31125
  throw new Error(
31070
31126
  `analyze-repo exited with status ${exitCode}. See ${logPath}`
31071
31127
  );
31072
31128
  }
31073
31129
  const uploaded = countUploadedTracePlans(stdoutChunks.join(""));
31074
- spinner5.stop(
31130
+ spinner6.stop(
31075
31131
  uploaded === 0 ? "No AI workflows found - nothing to upload" : `Uploaded ${uploaded} draft trace plan${uploaded === 1 ? "" : "s"} to Bitfab`
31076
31132
  );
31077
31133
  } catch (err) {
31078
- spinner5.stop("analyze-repo failed");
31134
+ spinner6.stop("analyze-repo failed");
31079
31135
  throw err;
31080
31136
  } finally {
31137
+ flushRedactors();
31081
31138
  out.end();
31082
31139
  }
31083
31140
  p3.log.info(`Full run log: ${logPath}`);
@@ -31242,6 +31299,8 @@ var claudeAdapter = {
31242
31299
  };
31243
31300
 
31244
31301
  // src/codex.ts
31302
+ import { spawn as spawn6 } from "child_process";
31303
+ import crypto11 from "crypto";
31245
31304
  import fs21 from "fs";
31246
31305
  import os16 from "os";
31247
31306
  import path18 from "path";
@@ -31251,7 +31310,9 @@ var SKIP_FLAG2 = "--full-auto";
31251
31310
  var REPO2 = "Project-White-Rabbit/bitfab-codex-plugin";
31252
31311
  var MARKETPLACE2 = "bitfab";
31253
31312
  var PLUGIN_KEY2 = "bitfab@bitfab";
31313
+ var INTERNAL_PLUGIN_KEY = "bitfab@bitfab-internal";
31254
31314
  var SETUP_COMMAND2 = "$bitfab:setup";
31315
+ var ANALYZE_REPO_COMMAND2 = "$bitfab:setup analyze-repo";
31255
31316
  var ASSISTANT_COMMAND2 = "$bitfab:assistant";
31256
31317
  var UPDATE_COMMAND2 = "$bitfab:update";
31257
31318
  function isCodexLoginStatusLoggedIn(output) {
@@ -31271,6 +31332,12 @@ function ensureLatestCodexPlugin() {
31271
31332
  ensureLatestPlugin(pullLatestCodexPlugin);
31272
31333
  }
31273
31334
  function runCodexInstall() {
31335
+ const configPath = path18.join(os16.homedir(), ".codex", "config.toml");
31336
+ if (isCodexPluginEnabled(configPath, INTERNAL_PLUGIN_KEY)) {
31337
+ p4.log.step("Codex dev plugin already active");
31338
+ p4.log.success("Bitfab plugin ready in Codex");
31339
+ return;
31340
+ }
31274
31341
  const s = p4.spinner();
31275
31342
  s.start("Adding bitfab marketplace");
31276
31343
  const out = runCli(CLI2, ["plugin", "marketplace", "add", REPO2]);
@@ -31281,9 +31348,7 @@ function runCodexInstall() {
31281
31348
  s.stop("Marketplace added");
31282
31349
  }
31283
31350
  s.start("Enabling bitfab plugin in config");
31284
- const changed = enableCodexPlugin(
31285
- path18.join(os16.homedir(), ".codex", "config.toml")
31286
- );
31351
+ const changed = enableCodexPlugin(configPath);
31287
31352
  if (changed) {
31288
31353
  s.stop("Plugin enabled");
31289
31354
  } else {
@@ -31294,13 +31359,229 @@ function runCodexInstall() {
31294
31359
  }
31295
31360
  p4.log.success("Bitfab plugin ready in Codex");
31296
31361
  }
31297
- function runCodexAnalyzeRepo() {
31298
- p4.log.warn(
31299
- "Headless analyze-repo is currently supported for Claude Code only."
31300
- );
31301
- p4.log.info(
31302
- `To run it in Codex, launch ${SETUP_COMMAND2} analyze-repo interactively.`
31362
+ async function runCodexAnalyzeRepo(captureOverride, limit) {
31363
+ const auth = checkCodexAuth();
31364
+ if (!auth.ok) {
31365
+ throw new Error(auth.message);
31366
+ }
31367
+ const logPath = analyzeRepoLogPath2();
31368
+ const agentRunId = crypto11.randomUUID();
31369
+ const env = { ...process.env };
31370
+ env.BITFAB_AGENT_RUN_ID = agentRunId;
31371
+ if (captureOverride !== void 0) {
31372
+ env.BITFAB_CAPTURE_SESSIONS = captureOverride ? "1" : "0";
31373
+ }
31374
+ const command = limit === void 0 ? ANALYZE_REPO_COMMAND2 : `${ANALYZE_REPO_COMMAND2} limit=${limit}`;
31375
+ const out = fs21.createWriteStream(logPath);
31376
+ const spinner6 = p4.spinner();
31377
+ spinner6.start("Analyzing repo and drafting trace plans");
31378
+ const stdoutChunks = [];
31379
+ const stdoutRedactor = new StreamingSecretRedactor();
31380
+ const stderrRedactor = new StreamingSecretRedactor();
31381
+ let redactorsFlushed = false;
31382
+ const flushRedactors = () => {
31383
+ if (redactorsFlushed) {
31384
+ return;
31385
+ }
31386
+ redactorsFlushed = true;
31387
+ const stdoutTail = stdoutRedactor.flush();
31388
+ if (stdoutTail !== "") {
31389
+ out.write(stdoutTail);
31390
+ stdoutChunks.push(stdoutTail);
31391
+ }
31392
+ const stderrTail = stderrRedactor.flush();
31393
+ if (stderrTail !== "") {
31394
+ out.write(stderrTail);
31395
+ }
31396
+ };
31397
+ try {
31398
+ const exitCode = await new Promise((resolve2, reject) => {
31399
+ const child = spawn6(
31400
+ CLI2,
31401
+ [
31402
+ "--ask-for-approval",
31403
+ "on-request",
31404
+ "--config",
31405
+ 'approvals_reviewer="auto_review"',
31406
+ "exec",
31407
+ "--json",
31408
+ "--sandbox",
31409
+ "danger-full-access",
31410
+ command
31411
+ ],
31412
+ { stdio: ["ignore", "pipe", "pipe"], env, ...SHELL_OPTS }
31413
+ );
31414
+ child.stdout.on("data", (chunk2) => {
31415
+ const text = chunk2.toString();
31416
+ const redacted = stdoutRedactor.redact(text);
31417
+ if (redacted !== "") {
31418
+ out.write(redacted);
31419
+ stdoutChunks.push(redacted);
31420
+ }
31421
+ const label = latestCodexActivityLabel(text);
31422
+ if (label) {
31423
+ spinner6.message(label);
31424
+ }
31425
+ });
31426
+ child.stderr.on(
31427
+ "data",
31428
+ (chunk2) => out.write(stderrRedactor.redact(chunk2.toString()))
31429
+ );
31430
+ child.on("error", reject);
31431
+ child.on("close", (code, signal) => {
31432
+ if (code === null) {
31433
+ reject(
31434
+ new Error(
31435
+ `analyze-repo terminated by signal ${signal ?? "unknown"}. See ${logPath}`
31436
+ )
31437
+ );
31438
+ return;
31439
+ }
31440
+ resolve2(code);
31441
+ });
31442
+ });
31443
+ flushRedactors();
31444
+ if (exitCode !== 0) {
31445
+ throw new Error(
31446
+ `analyze-repo exited with status ${exitCode}. See ${logPath}`
31447
+ );
31448
+ }
31449
+ const uploaded = countUploadedTracePlans2(stdoutChunks.join(""));
31450
+ spinner6.stop(
31451
+ uploaded === 0 ? "No AI workflows found - nothing to upload" : `Uploaded ${uploaded} draft trace plan${uploaded === 1 ? "" : "s"} to Bitfab`
31452
+ );
31453
+ } catch (err) {
31454
+ spinner6.stop("analyze-repo failed");
31455
+ throw err;
31456
+ } finally {
31457
+ flushRedactors();
31458
+ out.end();
31459
+ }
31460
+ p4.log.info(`Full run log: ${logPath}`);
31461
+ }
31462
+ function latestCodexActivityLabel(chunk2) {
31463
+ const lines = chunk2.split("\n").filter((l) => l.trim() !== "");
31464
+ for (let i = lines.length - 1; i >= 0; i--) {
31465
+ let event;
31466
+ try {
31467
+ event = JSON.parse(lines[i]);
31468
+ } catch {
31469
+ continue;
31470
+ }
31471
+ const label = codexActivityLabelFromEvent(event);
31472
+ if (label) {
31473
+ return label;
31474
+ }
31475
+ }
31476
+ return null;
31477
+ }
31478
+ function codexActivityLabelFromEvent(event) {
31479
+ const item = eventItem(event);
31480
+ if (!item) {
31481
+ return null;
31482
+ }
31483
+ if (isMcpToolItem(item, "create_trace_plan")) {
31484
+ return "Uploading a draft trace plan";
31485
+ }
31486
+ if (isMcpToolItem(item, "get_bitfab_api_key")) {
31487
+ return "Checking authentication";
31488
+ }
31489
+ const command = typeof item.command === "string" ? item.command : "";
31490
+ if (command.includes("rg ") || command.includes("rg --files")) {
31491
+ return "Searching the codebase";
31492
+ }
31493
+ if (command.includes("sed ") || command.includes("cat ") || command.includes("nl ")) {
31494
+ return "Reading files";
31495
+ }
31496
+ if (typeof item.type === "string" && item.type.includes("web")) {
31497
+ return "Reading SDK docs";
31498
+ }
31499
+ return null;
31500
+ }
31501
+ function countUploadedTracePlans2(logText) {
31502
+ const planToolUseIds = /* @__PURE__ */ new Set();
31503
+ const nonErrorResultIds = /* @__PURE__ */ new Set();
31504
+ let completedPlanCalls = 0;
31505
+ for (const line of logText.split("\n")) {
31506
+ if (line.trim() === "") {
31507
+ continue;
31508
+ }
31509
+ let event;
31510
+ try {
31511
+ event = JSON.parse(line);
31512
+ } catch {
31513
+ continue;
31514
+ }
31515
+ const item = eventItem(event);
31516
+ if (item && isMcpToolItem(item, "create_trace_plan")) {
31517
+ const status = typeof item.status === "string" ? item.status : "";
31518
+ if (status === "completed" || status === "" && eventType(event) === "item.completed") {
31519
+ completedPlanCalls++;
31520
+ }
31521
+ }
31522
+ for (const block of eventMessageContent2(event) ?? []) {
31523
+ if (typeof block !== "object" || block === null) {
31524
+ continue;
31525
+ }
31526
+ const b = block;
31527
+ if (b.type === "tool_use" && typeof b.id === "string" && typeof b.name === "string" && b.name.includes("create_trace_plan")) {
31528
+ planToolUseIds.add(b.id);
31529
+ }
31530
+ if (b.type === "tool_result" && typeof b.tool_use_id === "string" && b.is_error !== true) {
31531
+ nonErrorResultIds.add(b.tool_use_id);
31532
+ }
31533
+ }
31534
+ }
31535
+ let pairedCount = 0;
31536
+ for (const id of planToolUseIds) {
31537
+ if (nonErrorResultIds.has(id)) {
31538
+ pairedCount++;
31539
+ }
31540
+ }
31541
+ return pairedCount + completedPlanCalls;
31542
+ }
31543
+ function isMcpToolItem(item, toolName) {
31544
+ if (typeof item.type === "string" && !item.type.includes("mcp")) {
31545
+ return false;
31546
+ }
31547
+ const directNames = [item.tool, item.name].filter(
31548
+ (value) => typeof value === "string"
31303
31549
  );
31550
+ if (directNames.some((name) => name.includes(toolName))) {
31551
+ return true;
31552
+ }
31553
+ const toolCall = item.tool_call;
31554
+ if (typeof toolCall === "object" && toolCall !== null) {
31555
+ const nested = toolCall;
31556
+ return [nested.tool, nested.name].some(
31557
+ (value) => typeof value === "string" && value.includes(toolName)
31558
+ );
31559
+ }
31560
+ return false;
31561
+ }
31562
+ function eventType(event) {
31563
+ return typeof event === "object" && event !== null && typeof event.type === "string" ? event.type : "";
31564
+ }
31565
+ function eventItem(event) {
31566
+ if (typeof event !== "object" || event === null) {
31567
+ return null;
31568
+ }
31569
+ const item = event.item;
31570
+ return typeof item === "object" && item !== null ? item : null;
31571
+ }
31572
+ function eventMessageContent2(event) {
31573
+ if (typeof event !== "object" || event === null) {
31574
+ return null;
31575
+ }
31576
+ const message = event.message;
31577
+ const content = typeof message === "object" && message !== null ? message.content : void 0;
31578
+ return Array.isArray(content) ? content : null;
31579
+ }
31580
+ function analyzeRepoLogPath2() {
31581
+ const dir = path18.join(process.cwd(), ".bitfab", "logs");
31582
+ fs21.mkdirSync(dir, { recursive: true });
31583
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
31584
+ return path18.join(dir, `analyze-repo-${stamp}.jsonl`);
31304
31585
  }
31305
31586
  function enableCodexPlugin(configPath) {
31306
31587
  let content = "";
@@ -31316,7 +31597,7 @@ enabled = true
31316
31597
  "m"
31317
31598
  );
31318
31599
  const match = sectionRegex.exec(content);
31319
- if (match?.[0].includes("enabled = true")) {
31600
+ if (match && sectionHasEnabledTrue(match[0])) {
31320
31601
  return false;
31321
31602
  }
31322
31603
  let next;
@@ -31332,9 +31613,24 @@ ${desiredSection}`;
31332
31613
  fs21.writeFileSync(configPath, next);
31333
31614
  return true;
31334
31615
  }
31616
+ function isCodexPluginEnabled(configPath, pluginKey) {
31617
+ if (!fs21.existsSync(configPath)) {
31618
+ return false;
31619
+ }
31620
+ const content = fs21.readFileSync(configPath, "utf-8");
31621
+ const sectionRegex = new RegExp(
31622
+ `\\[plugins\\."${escapeRegex2(pluginKey)}"\\][^\\[]*`,
31623
+ "m"
31624
+ );
31625
+ const match = sectionRegex.exec(content);
31626
+ return match ? sectionHasEnabledTrue(match[0]) : false;
31627
+ }
31335
31628
  function escapeRegex2(s) {
31336
31629
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
31337
31630
  }
31631
+ function sectionHasEnabledTrue(section) {
31632
+ return section.split(/\r?\n/).some((line) => /^\s*enabled\s*=\s*true\s*(?:#.*)?$/.test(line));
31633
+ }
31338
31634
  var config3 = {
31339
31635
  cli: CLI2,
31340
31636
  skipFlag: SKIP_FLAG2,
@@ -31355,12 +31651,16 @@ var codexAdapter = {
31355
31651
  };
31356
31652
 
31357
31653
  // src/cursor.ts
31358
- import { spawn as spawn6, spawnSync as spawnSync2 } from "child_process";
31654
+ import { spawn as spawn7, spawnSync as spawnSync2 } from "child_process";
31655
+ import crypto12 from "crypto";
31656
+ import fs22 from "fs";
31359
31657
  import os17 from "os";
31658
+ import path19 from "path";
31360
31659
  import * as p5 from "@clack/prompts";
31361
31660
  var REPO3 = "Project-White-Rabbit/bitfab-cursor-plugin";
31362
31661
  var ADD_PLUGIN_CMD = `/add-plugin ${REPO3}`;
31363
31662
  var SETUP_COMMAND3 = "/bitfab-setup";
31663
+ var ANALYZE_REPO_COMMAND3 = "/bitfab-setup analyze-repo";
31364
31664
  var ASSISTANT_COMMAND3 = "/bitfab-assistant";
31365
31665
  var UPDATE_COMMAND3 = "/bitfab-update";
31366
31666
  function isCursorAgentStatusAuthenticated(output) {
@@ -31399,7 +31699,7 @@ function runCursorInstall() {
31399
31699
  "Finish setup in Cursor"
31400
31700
  );
31401
31701
  p5.log.info("Opening Cursor...");
31402
- const child = spawn6("cursor", ["."], {
31702
+ const child = spawn7("cursor", ["."], {
31403
31703
  stdio: "ignore",
31404
31704
  detached: true,
31405
31705
  windowsHide: true,
@@ -31418,11 +31718,241 @@ function runCursorSetup(skipPermissions) {
31418
31718
  }
31419
31719
  p5.log.info(`To complete setup, run ${SETUP_COMMAND3} in Cursor.`);
31420
31720
  }
31421
- function runCursorAnalyzeRepo() {
31422
- p5.log.warn(
31423
- "Headless analyze-repo is currently supported for Claude Code only."
31424
- );
31425
- p5.log.info(`To run it in Cursor, run ${SETUP_COMMAND3} analyze-repo manually.`);
31721
+ async function runCursorAnalyzeRepo(captureOverride, limit) {
31722
+ ensureCursorAuthenticated();
31723
+ const pluginDir = resolveCursorPluginDir();
31724
+ if (!pluginDir) {
31725
+ throw new Error(
31726
+ [
31727
+ "Could not find a Cursor Bitfab plugin directory to load headlessly.",
31728
+ `Install the plugin in Cursor with ${ADD_PLUGIN_CMD}, or run from the monorepo checkout so bitfab-cursor-plugin is available.`
31729
+ ].join("\n")
31730
+ );
31731
+ }
31732
+ const logPath = analyzeRepoLogPath3();
31733
+ const agentRunId = crypto12.randomUUID();
31734
+ const env = { ...process.env };
31735
+ env.BITFAB_AGENT_RUN_ID = agentRunId;
31736
+ if (captureOverride !== void 0) {
31737
+ env.BITFAB_CAPTURE_SESSIONS = captureOverride ? "1" : "0";
31738
+ }
31739
+ const command = limit === void 0 ? ANALYZE_REPO_COMMAND3 : `${ANALYZE_REPO_COMMAND3} limit=${limit}`;
31740
+ const out = fs22.createWriteStream(logPath);
31741
+ const spinner6 = p5.spinner();
31742
+ spinner6.start("Analyzing repo and drafting trace plans");
31743
+ const stdoutChunks = [];
31744
+ const stdoutRedactor = new StreamingSecretRedactor();
31745
+ const stderrRedactor = new StreamingSecretRedactor();
31746
+ let redactorsFlushed = false;
31747
+ const flushRedactors = () => {
31748
+ if (redactorsFlushed) {
31749
+ return;
31750
+ }
31751
+ redactorsFlushed = true;
31752
+ const stdoutTail = stdoutRedactor.flush();
31753
+ if (stdoutTail !== "") {
31754
+ out.write(stdoutTail);
31755
+ stdoutChunks.push(stdoutTail);
31756
+ }
31757
+ const stderrTail = stderrRedactor.flush();
31758
+ if (stderrTail !== "") {
31759
+ out.write(stderrTail);
31760
+ }
31761
+ };
31762
+ try {
31763
+ const exitCode = await new Promise((resolve2, reject) => {
31764
+ const child = spawn7(
31765
+ "cursor",
31766
+ [
31767
+ "agent",
31768
+ "--print",
31769
+ "--output-format",
31770
+ "stream-json",
31771
+ "--trust",
31772
+ "--approve-mcps",
31773
+ "--force",
31774
+ "--plugin-dir",
31775
+ pluginDir,
31776
+ "--workspace",
31777
+ process.cwd(),
31778
+ command
31779
+ ],
31780
+ { stdio: ["ignore", "pipe", "pipe"], env, ...SHELL_OPTS }
31781
+ );
31782
+ child.stdout.on("data", (chunk2) => {
31783
+ const text = chunk2.toString();
31784
+ const redacted = stdoutRedactor.redact(text);
31785
+ if (redacted !== "") {
31786
+ out.write(redacted);
31787
+ stdoutChunks.push(redacted);
31788
+ }
31789
+ const label = latestCursorActivityLabel(text);
31790
+ if (label) {
31791
+ spinner6.message(label);
31792
+ }
31793
+ });
31794
+ child.stderr.on(
31795
+ "data",
31796
+ (chunk2) => out.write(stderrRedactor.redact(chunk2.toString()))
31797
+ );
31798
+ child.on("error", reject);
31799
+ child.on("close", (code, signal) => {
31800
+ if (code === null) {
31801
+ reject(
31802
+ new Error(
31803
+ `analyze-repo terminated by signal ${signal ?? "unknown"}. See ${logPath}`
31804
+ )
31805
+ );
31806
+ return;
31807
+ }
31808
+ resolve2(code);
31809
+ });
31810
+ });
31811
+ flushRedactors();
31812
+ if (exitCode !== 0) {
31813
+ throw new Error(
31814
+ `analyze-repo exited with status ${exitCode}. See ${logPath}`
31815
+ );
31816
+ }
31817
+ const uploaded = countUploadedTracePlans3(stdoutChunks.join(""));
31818
+ spinner6.stop(
31819
+ uploaded === 0 ? "No AI workflows found - nothing to upload" : `Uploaded ${uploaded} draft trace plan${uploaded === 1 ? "" : "s"} to Bitfab`
31820
+ );
31821
+ } catch (err) {
31822
+ spinner6.stop("analyze-repo failed");
31823
+ throw err;
31824
+ } finally {
31825
+ flushRedactors();
31826
+ out.end();
31827
+ }
31828
+ p5.log.info(`Full run log: ${logPath}`);
31829
+ }
31830
+ function latestCursorActivityLabel(chunk2) {
31831
+ const lines = chunk2.split("\n").filter((l) => l.trim() !== "");
31832
+ for (let i = lines.length - 1; i >= 0; i--) {
31833
+ let event;
31834
+ try {
31835
+ event = JSON.parse(lines[i]);
31836
+ } catch {
31837
+ continue;
31838
+ }
31839
+ const label = cursorActivityLabelFromEvent(event);
31840
+ if (label) {
31841
+ return label;
31842
+ }
31843
+ }
31844
+ return null;
31845
+ }
31846
+ function cursorActivityLabelFromEvent(event) {
31847
+ const toolCall = eventToolCall(event);
31848
+ if (!toolCall) {
31849
+ return null;
31850
+ }
31851
+ const mcpToolName = mcpToolCallName(toolCall);
31852
+ if (mcpToolName?.includes("create_trace_plan")) {
31853
+ return "Uploading a draft trace plan";
31854
+ }
31855
+ if (mcpToolName?.includes("get_bitfab_api_key")) {
31856
+ return "Checking authentication";
31857
+ }
31858
+ if ("grepToolCall" in toolCall) {
31859
+ return "Searching the codebase";
31860
+ }
31861
+ if ("readToolCall" in toolCall) {
31862
+ return "Reading files";
31863
+ }
31864
+ return null;
31865
+ }
31866
+ function eventToolCall(event) {
31867
+ if (typeof event !== "object" || event === null) {
31868
+ return null;
31869
+ }
31870
+ const toolCall = event.tool_call;
31871
+ return typeof toolCall === "object" && toolCall !== null ? toolCall : null;
31872
+ }
31873
+ function mcpToolCallName(toolCall) {
31874
+ const mcpToolCall = toolCall.mcpToolCall;
31875
+ if (typeof mcpToolCall !== "object" || mcpToolCall === null) {
31876
+ return null;
31877
+ }
31878
+ const call = mcpToolCall;
31879
+ const args = call.args;
31880
+ const names = [
31881
+ call.toolName,
31882
+ call.name,
31883
+ typeof args === "object" && args !== null ? args.name : void 0
31884
+ ];
31885
+ return names.find((name) => typeof name === "string") ?? null;
31886
+ }
31887
+ function countUploadedTracePlans3(logText) {
31888
+ let completedPlanCalls = 0;
31889
+ for (const line of logText.split("\n")) {
31890
+ if (line.trim() === "") {
31891
+ continue;
31892
+ }
31893
+ let event;
31894
+ try {
31895
+ event = JSON.parse(line);
31896
+ } catch {
31897
+ continue;
31898
+ }
31899
+ if (isSuccessfulCreateTracePlanEvent(event)) {
31900
+ completedPlanCalls++;
31901
+ }
31902
+ }
31903
+ if (completedPlanCalls > 0) {
31904
+ return completedPlanCalls;
31905
+ }
31906
+ return 0;
31907
+ }
31908
+ function isSuccessfulCreateTracePlanEvent(event) {
31909
+ if (typeof event !== "object" || event === null) {
31910
+ return false;
31911
+ }
31912
+ const e = event;
31913
+ if (e.type !== "tool_call" || e.subtype !== "completed") {
31914
+ return false;
31915
+ }
31916
+ const toolCall = e.tool_call;
31917
+ if (typeof toolCall !== "object" || toolCall === null) {
31918
+ return false;
31919
+ }
31920
+ const mcpToolCall = toolCall.mcpToolCall;
31921
+ if (typeof mcpToolCall !== "object" || mcpToolCall === null) {
31922
+ return false;
31923
+ }
31924
+ const call = mcpToolCall;
31925
+ const args = call.args;
31926
+ const names = [
31927
+ call.toolName,
31928
+ call.name,
31929
+ typeof args === "object" && args !== null ? args.name : void 0
31930
+ ];
31931
+ return names.some(
31932
+ (name) => typeof name === "string" && name.includes("create_trace_plan")
31933
+ ) && hasSuccessfulToolResult(call.result);
31934
+ }
31935
+ function hasSuccessfulToolResult(result) {
31936
+ return typeof result === "object" && result !== null && "success" in result && !("error" in result);
31937
+ }
31938
+ function resolveCursorPluginDir() {
31939
+ const candidates = [
31940
+ path19.join(process.cwd(), "bitfab-cursor-plugin"),
31941
+ path19.join(os17.homedir(), ".cursor", "plugins", "local", "bitfab-dev"),
31942
+ path19.join(os17.homedir(), ".cursor", "plugins", "local", "bitfab")
31943
+ ];
31944
+ for (const candidate of candidates) {
31945
+ if (fs22.existsSync(path19.join(candidate, ".cursor-plugin", "plugin.json")) && fs22.existsSync(path19.join(candidate, "mcp.json"))) {
31946
+ return candidate;
31947
+ }
31948
+ }
31949
+ return null;
31950
+ }
31951
+ function analyzeRepoLogPath3() {
31952
+ const dir = path19.join(process.cwd(), ".bitfab", "logs");
31953
+ fs22.mkdirSync(dir, { recursive: true });
31954
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
31955
+ return path19.join(dir, `analyze-repo-${stamp}.jsonl`);
31426
31956
  }
31427
31957
  function runCursorAssistant(args, skipPermissions) {
31428
31958
  ensureCursorAuthenticated();
@@ -31498,12 +32028,12 @@ var platform = {
31498
32028
  };
31499
32029
 
31500
32030
  // src/version.ts
31501
- import fs22 from "fs";
31502
- import path19 from "path";
32031
+ import fs23 from "fs";
32032
+ import path20 from "path";
31503
32033
  import { fileURLToPath as fileURLToPath4 } from "url";
31504
32034
  var CLI_VERSION = JSON.parse(
31505
- fs22.readFileSync(
31506
- path19.join(path19.dirname(fileURLToPath4(import.meta.url)), "../package.json"),
32035
+ fs23.readFileSync(
32036
+ path20.join(path20.dirname(fileURLToPath4(import.meta.url)), "../package.json"),
31507
32037
  "utf-8"
31508
32038
  )
31509
32039
  ).version;
@@ -31669,10 +32199,10 @@ async function runAnalyzeRepo(opts) {
31669
32199
  p6.intro("Bitfab");
31670
32200
  const chosen = await resolveEditor(opts);
31671
32201
  p6.log.step(`Preparing ${ADAPTERS[chosen].label} plugin`);
31672
- ADAPTERS[chosen].install();
31673
- if (chosen !== "claude") {
31674
- ADAPTERS[chosen].analyzeRepo();
31675
- process.exit(1);
32202
+ if (chosen === "cursor") {
32203
+ ADAPTERS[chosen].ensureLatest();
32204
+ } else {
32205
+ ADAPTERS[chosen].install();
31676
32206
  }
31677
32207
  await ensureAnalyzeRepoAuth();
31678
32208
  const captureOverride = await resolveCaptureOverride(opts.uploadLogs);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bitfab-cli",
3
- "version": "0.2.179",
3
+ "version": "0.2.181",
4
4
  "description": "Install and configure the Bitfab plugin in Claude Code, Codex, or Cursor.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",