@vibedrift/cli 0.3.0 → 0.3.2

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/LICENSE CHANGED
@@ -110,4 +110,4 @@ AGREE, DO NOT INSTALL OR USE THE SOFTWARE.
110
110
  10. CONTACT
111
111
 
112
112
  For commercial licensing, partnerships, or other inquiries:
113
- sami@vibedrift.ai
113
+ sami.ahmadkhan12@gmail.com
package/README.md CHANGED
@@ -149,11 +149,11 @@ In short:
149
149
  - No use to build competing products
150
150
  - All rights reserved
151
151
 
152
- For commercial licensing, partnerships, or enterprise terms: **sami@vibedrift.ai**
152
+ For commercial licensing, partnerships, or enterprise terms: **sami.ahmadkhan12@gmail.com**
153
153
 
154
154
  ---
155
155
 
156
156
  ## Links
157
157
 
158
158
  - **Website:** https://vibedrift.ai
159
- - **Issues / Support:** sami@vibedrift.ai
159
+ - **Issues / Support:** sami.ahmadkhan12@gmail.com
package/dist/index.js CHANGED
@@ -7889,6 +7889,119 @@ function previewToken(token) {
7889
7889
  return token.slice(0, 12) + "\u2026";
7890
7890
  }
7891
7891
 
7892
+ // src/auth/api.ts
7893
+ init_esm_shims();
7894
+ var REQUEST_TIMEOUT_MS = 3e4;
7895
+ var VibeDriftApiError = class extends Error {
7896
+ constructor(status, message) {
7897
+ super(message);
7898
+ this.status = status;
7899
+ this.name = "VibeDriftApiError";
7900
+ }
7901
+ status;
7902
+ };
7903
+ async function jsonFetch(url, init = {}) {
7904
+ const controller = new AbortController();
7905
+ const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
7906
+ try {
7907
+ const res = await fetch(url, {
7908
+ ...init,
7909
+ signal: controller.signal,
7910
+ headers: {
7911
+ Accept: "application/json",
7912
+ ...init.body ? { "Content-Type": "application/json" } : {},
7913
+ ...init.headers ?? {}
7914
+ }
7915
+ });
7916
+ if (!res.ok) {
7917
+ let detail = "";
7918
+ try {
7919
+ const body = await res.json();
7920
+ detail = body.detail ?? body.message ?? "";
7921
+ } catch {
7922
+ try {
7923
+ detail = await res.text();
7924
+ } catch {
7925
+ }
7926
+ }
7927
+ throw new VibeDriftApiError(res.status, detail || `HTTP ${res.status}`);
7928
+ }
7929
+ return await res.json();
7930
+ } catch (err) {
7931
+ if (err?.name === "AbortError") {
7932
+ throw new VibeDriftApiError(0, `Request timed out after ${REQUEST_TIMEOUT_MS / 1e3}s`);
7933
+ }
7934
+ if (err instanceof VibeDriftApiError) throw err;
7935
+ throw new VibeDriftApiError(0, err?.message ?? String(err));
7936
+ } finally {
7937
+ clearTimeout(timer);
7938
+ }
7939
+ }
7940
+ async function startDeviceAuth(opts) {
7941
+ const base = await resolveApiUrl(opts?.apiUrl);
7942
+ return jsonFetch(`${base}/auth/device`, {
7943
+ method: "POST",
7944
+ body: JSON.stringify({ client_id: "vibedrift-cli" })
7945
+ });
7946
+ }
7947
+ async function pollDeviceAuth(deviceCode, opts) {
7948
+ const base = await resolveApiUrl(opts?.apiUrl);
7949
+ return jsonFetch(`${base}/auth/poll`, {
7950
+ method: "POST",
7951
+ body: JSON.stringify({ device_code: deviceCode })
7952
+ });
7953
+ }
7954
+ async function validateToken(token, opts) {
7955
+ const base = await resolveApiUrl(opts?.apiUrl);
7956
+ return jsonFetch(`${base}/auth/validate`, {
7957
+ method: "GET",
7958
+ headers: { Authorization: `Bearer ${token}` }
7959
+ });
7960
+ }
7961
+ async function revokeToken(token, opts) {
7962
+ const base = await resolveApiUrl(opts?.apiUrl);
7963
+ await jsonFetch(`${base}/auth/revoke`, {
7964
+ method: "POST",
7965
+ headers: { Authorization: `Bearer ${token}` }
7966
+ });
7967
+ }
7968
+ async function fetchUsage(token, opts) {
7969
+ const base = await resolveApiUrl(opts?.apiUrl);
7970
+ return jsonFetch(`${base}/account/usage`, {
7971
+ method: "GET",
7972
+ headers: { Authorization: `Bearer ${token}` }
7973
+ });
7974
+ }
7975
+ async function sendFeedback(args) {
7976
+ const base = await resolveApiUrl(args.apiUrl);
7977
+ const headers = {};
7978
+ if (args.token) headers.Authorization = `Bearer ${args.token}`;
7979
+ return jsonFetch(`${base}/v1/feedback/general`, {
7980
+ method: "POST",
7981
+ headers,
7982
+ body: JSON.stringify({
7983
+ source: args.source,
7984
+ message: args.message,
7985
+ email: args.email,
7986
+ metadata: args.metadata
7987
+ })
7988
+ });
7989
+ }
7990
+ async function fetchCredits(token, opts) {
7991
+ const base = await resolveApiUrl(opts?.apiUrl);
7992
+ return jsonFetch(`${base}/account/credits`, {
7993
+ method: "GET",
7994
+ headers: { Authorization: `Bearer ${token}` }
7995
+ });
7996
+ }
7997
+ async function createPortalSession(token, opts) {
7998
+ const base = await resolveApiUrl(opts?.apiUrl);
7999
+ return jsonFetch(`${base}/account/portal`, {
8000
+ method: "POST",
8001
+ headers: { Authorization: `Bearer ${token}` }
8002
+ });
8003
+ }
8004
+
7892
8005
  // src/cli/commands/scan.ts
7893
8006
  async function runScan(targetPath, options) {
7894
8007
  const rootDir = resolve(targetPath);
@@ -7910,7 +8023,9 @@ async function runScan(targetPath, options) {
7910
8023
  console.error("");
7911
8024
  console.error(chalk2.red(" \u2717 Deep scans require a VibeDrift account."));
7912
8025
  console.error("");
7913
- console.error(" Run " + chalk2.bold("vibedrift login") + " to sign in.");
8026
+ console.error(chalk2.bgYellow.black.bold(" \u{1F381} But your first deep scan is FREE. "));
8027
+ console.error("");
8028
+ console.error(" Run " + chalk2.bold("vibedrift login") + " to sign in and claim it.");
7914
8029
  console.error(" Or set " + chalk2.bold("VIBEDRIFT_TOKEN") + " in your environment for CI.");
7915
8030
  console.error("");
7916
8031
  process.exit(1);
@@ -7927,6 +8042,25 @@ async function runScan(targetPath, options) {
7927
8042
  } catch {
7928
8043
  }
7929
8044
  }
8045
+ if (!options.json && options.format !== "json" && !options.deep) {
8046
+ if (bearerToken) {
8047
+ try {
8048
+ const credits = await fetchCredits(bearerToken, { apiUrl });
8049
+ if (credits.has_free_deep_scan && !credits.unlimited) {
8050
+ console.log("");
8051
+ console.log(chalk2.bgYellow.black.bold(" \u{1F381} 1 FREE DEEP SCAN AVAILABLE "));
8052
+ console.log(chalk2.yellow(" Run with ") + chalk2.bold.cyan("--deep") + chalk2.yellow(" to use Claude-powered analysis (no card required)."));
8053
+ console.log("");
8054
+ }
8055
+ } catch {
8056
+ }
8057
+ } else {
8058
+ console.log("");
8059
+ console.log(chalk2.dim(" Tip: ") + chalk2.yellow("sign up free to get 1 deep scan included"));
8060
+ console.log(chalk2.dim(" Run ") + chalk2.bold("vibedrift login") + chalk2.dim(" to claim it (no card required)."));
8061
+ console.log("");
8062
+ }
8063
+ }
7930
8064
  const startTime = Date.now();
7931
8065
  const timings = {};
7932
8066
  const isTerminal = options.format === "terminal" && !options.json;
@@ -8281,97 +8415,6 @@ Update failed: ${message}`));
8281
8415
  init_esm_shims();
8282
8416
  import chalk4 from "chalk";
8283
8417
 
8284
- // src/auth/api.ts
8285
- init_esm_shims();
8286
- var REQUEST_TIMEOUT_MS = 3e4;
8287
- var VibeDriftApiError = class extends Error {
8288
- constructor(status, message) {
8289
- super(message);
8290
- this.status = status;
8291
- this.name = "VibeDriftApiError";
8292
- }
8293
- status;
8294
- };
8295
- async function jsonFetch(url, init = {}) {
8296
- const controller = new AbortController();
8297
- const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
8298
- try {
8299
- const res = await fetch(url, {
8300
- ...init,
8301
- signal: controller.signal,
8302
- headers: {
8303
- Accept: "application/json",
8304
- ...init.body ? { "Content-Type": "application/json" } : {},
8305
- ...init.headers ?? {}
8306
- }
8307
- });
8308
- if (!res.ok) {
8309
- let detail = "";
8310
- try {
8311
- const body = await res.json();
8312
- detail = body.detail ?? body.message ?? "";
8313
- } catch {
8314
- try {
8315
- detail = await res.text();
8316
- } catch {
8317
- }
8318
- }
8319
- throw new VibeDriftApiError(res.status, detail || `HTTP ${res.status}`);
8320
- }
8321
- return await res.json();
8322
- } catch (err) {
8323
- if (err?.name === "AbortError") {
8324
- throw new VibeDriftApiError(0, `Request timed out after ${REQUEST_TIMEOUT_MS / 1e3}s`);
8325
- }
8326
- if (err instanceof VibeDriftApiError) throw err;
8327
- throw new VibeDriftApiError(0, err?.message ?? String(err));
8328
- } finally {
8329
- clearTimeout(timer);
8330
- }
8331
- }
8332
- async function startDeviceAuth(opts) {
8333
- const base = await resolveApiUrl(opts?.apiUrl);
8334
- return jsonFetch(`${base}/auth/device`, {
8335
- method: "POST",
8336
- body: JSON.stringify({ client_id: "vibedrift-cli" })
8337
- });
8338
- }
8339
- async function pollDeviceAuth(deviceCode, opts) {
8340
- const base = await resolveApiUrl(opts?.apiUrl);
8341
- return jsonFetch(`${base}/auth/poll`, {
8342
- method: "POST",
8343
- body: JSON.stringify({ device_code: deviceCode })
8344
- });
8345
- }
8346
- async function validateToken(token, opts) {
8347
- const base = await resolveApiUrl(opts?.apiUrl);
8348
- return jsonFetch(`${base}/auth/validate`, {
8349
- method: "GET",
8350
- headers: { Authorization: `Bearer ${token}` }
8351
- });
8352
- }
8353
- async function revokeToken(token, opts) {
8354
- const base = await resolveApiUrl(opts?.apiUrl);
8355
- await jsonFetch(`${base}/auth/revoke`, {
8356
- method: "POST",
8357
- headers: { Authorization: `Bearer ${token}` }
8358
- });
8359
- }
8360
- async function fetchUsage(token, opts) {
8361
- const base = await resolveApiUrl(opts?.apiUrl);
8362
- return jsonFetch(`${base}/account/usage`, {
8363
- method: "GET",
8364
- headers: { Authorization: `Bearer ${token}` }
8365
- });
8366
- }
8367
- async function createPortalSession(token, opts) {
8368
- const base = await resolveApiUrl(opts?.apiUrl);
8369
- return jsonFetch(`${base}/account/portal`, {
8370
- method: "POST",
8371
- headers: { Authorization: `Bearer ${token}` }
8372
- });
8373
- }
8374
-
8375
8418
  // src/auth/browser.ts
8376
8419
  init_esm_shims();
8377
8420
  import { spawn as spawn2 } from "child_process";
@@ -8478,11 +8521,43 @@ async function runLogin(options = {}) {
8478
8521
  console.log(` Account: ${chalk4.bold(result.email)}`);
8479
8522
  console.log(` Plan: ${chalk4.bold(result.plan)}`);
8480
8523
  console.log("");
8481
- if (result.plan === "free") {
8482
- console.log(chalk4.dim(" Run `vibedrift upgrade` to enable deep AI scans."));
8483
- console.log("");
8484
- } else {
8485
- console.log(chalk4.dim(" Run `vibedrift . --deep` to use AI-powered analysis."));
8524
+ try {
8525
+ const credits = await fetchCredits(result.access_token, {
8526
+ apiUrl: options.apiUrl
8527
+ });
8528
+ if (credits.has_free_deep_scan && !credits.unlimited) {
8529
+ console.log(
8530
+ chalk4.bgYellow.black.bold(" \u{1F381} 1 FREE deep scan included with your account ")
8531
+ );
8532
+ console.log("");
8533
+ console.log(
8534
+ chalk4.yellow(" Try the full pipeline (Claude analysis, security review,")
8535
+ );
8536
+ console.log(
8537
+ chalk4.yellow(" AI-powered drift detection) on any project \u2014 no card needed.")
8538
+ );
8539
+ console.log("");
8540
+ console.log(` ${chalk4.cyan("vibedrift . --deep")}`);
8541
+ console.log("");
8542
+ } else if (credits.unlimited) {
8543
+ console.log(chalk4.dim(" Run `vibedrift . --deep` to use AI-powered analysis."));
8544
+ console.log("");
8545
+ } else if (credits.available_total > 0) {
8546
+ console.log(
8547
+ chalk4.dim(` You have ${credits.available_total} deep scan credit${credits.available_total === 1 ? "" : "s"} available.`)
8548
+ );
8549
+ console.log(chalk4.dim(" Run `vibedrift . --deep` to use one."));
8550
+ console.log("");
8551
+ } else {
8552
+ console.log(chalk4.dim(" Run `vibedrift upgrade` to enable deep AI scans."));
8553
+ console.log("");
8554
+ }
8555
+ } catch {
8556
+ if (result.plan === "free") {
8557
+ console.log(chalk4.dim(" Run `vibedrift upgrade` to enable deep AI scans."));
8558
+ } else {
8559
+ console.log(chalk4.dim(" Run `vibedrift . --deep` to use AI-powered analysis."));
8560
+ }
8486
8561
  console.log("");
8487
8562
  }
8488
8563
  return;
@@ -8594,6 +8669,21 @@ async function runStatus() {
8594
8669
  console.log(chalk6.dim(` ${err.message}`));
8595
8670
  }
8596
8671
  }
8672
+ try {
8673
+ const credits = await fetchCredits(resolved.token, { apiUrl: config.apiUrl });
8674
+ console.log("");
8675
+ if (credits.unlimited) {
8676
+ console.log(` Deep scans: ${chalk6.bold.green("unlimited")} (${credits.plan})`);
8677
+ } else if (credits.has_free_deep_scan) {
8678
+ console.log(` Deep scans: ${chalk6.bold.yellow("1 free")} + ${credits.available_purchased} purchased`);
8679
+ console.log(chalk6.dim(" Run `vibedrift . --deep` to use your free credit."));
8680
+ } else if (credits.available_total > 0) {
8681
+ console.log(` Deep scans: ${chalk6.bold(credits.available_total)} credit${credits.available_total === 1 ? "" : "s"} available`);
8682
+ } else {
8683
+ console.log(` Deep scans: ${chalk6.dim("0 credits")} \u2014 run \`vibedrift upgrade\` for more`);
8684
+ }
8685
+ } catch {
8686
+ }
8597
8687
  console.log("");
8598
8688
  }
8599
8689
  function describeSource(source) {
@@ -8885,6 +8975,145 @@ function describeSource2(source) {
8885
8975
  }
8886
8976
  }
8887
8977
 
8978
+ // src/cli/commands/feedback.ts
8979
+ init_esm_shims();
8980
+ import os from "os";
8981
+ import readline from "readline";
8982
+ import chalk11 from "chalk";
8983
+ init_version();
8984
+ var MAX_MESSAGE_BYTES = 4096;
8985
+ async function runFeedback(opts = {}) {
8986
+ console.log("");
8987
+ console.log(chalk11.bold(" VibeDrift feedback"));
8988
+ console.log(
8989
+ chalk11.dim(" Tell us what's broken, what's confusing, or what you wish existed.")
8990
+ );
8991
+ console.log(
8992
+ chalk11.dim(" Goes straight to the maintainer \u2014 anonymous unless you're logged in.")
8993
+ );
8994
+ console.log("");
8995
+ let message = (opts.message ?? "").trim();
8996
+ if (!message) {
8997
+ message = await promptForMultilineMessage();
8998
+ }
8999
+ if (!message) {
9000
+ console.error(chalk11.red(" \u2717 No feedback provided. Aborting."));
9001
+ process.exit(1);
9002
+ }
9003
+ if (Buffer.byteLength(message, "utf8") > MAX_MESSAGE_BYTES) {
9004
+ console.error(
9005
+ chalk11.red(
9006
+ ` \u2717 Message too long (max ${MAX_MESSAGE_BYTES} bytes). Please trim it or open an issue on GitHub for longer reports.`
9007
+ )
9008
+ );
9009
+ process.exit(1);
9010
+ }
9011
+ let token = null;
9012
+ try {
9013
+ const resolved = await resolveToken();
9014
+ if (resolved) token = resolved.token;
9015
+ } catch {
9016
+ }
9017
+ const metadata = {
9018
+ cli_version: getVersion(),
9019
+ node_version: process.version,
9020
+ platform: process.platform,
9021
+ arch: process.arch,
9022
+ os_release: os.release(),
9023
+ locale: process.env.LANG ?? null,
9024
+ tty: process.stdin.isTTY === true
9025
+ };
9026
+ process.stdout.write(chalk11.dim(" Sending... "));
9027
+ try {
9028
+ const result = await sendFeedback({
9029
+ source: "cli",
9030
+ message,
9031
+ token: token ?? void 0,
9032
+ metadata,
9033
+ apiUrl: await resolveApiUrl(opts.apiUrl)
9034
+ });
9035
+ console.log(chalk11.green("ok"));
9036
+ console.log("");
9037
+ console.log(` ${chalk11.dim("Reference:")} ${chalk11.dim(result.id)}`);
9038
+ if (token) {
9039
+ console.log(
9040
+ chalk11.dim(
9041
+ " Submitted under your account \u2014 we'll reply to your email if needed."
9042
+ )
9043
+ );
9044
+ } else {
9045
+ console.log(
9046
+ chalk11.dim(
9047
+ " Submitted anonymously. If you'd like a reply, run `vibedrift login` first."
9048
+ )
9049
+ );
9050
+ }
9051
+ console.log("");
9052
+ console.log(chalk11.green(" \u2713 Thanks for helping us improve VibeDrift."));
9053
+ console.log("");
9054
+ } catch (err) {
9055
+ console.log(chalk11.red("failed"));
9056
+ console.log("");
9057
+ if (err instanceof VibeDriftApiError) {
9058
+ console.error(chalk11.red(` \u2717 ${err.message}`));
9059
+ } else {
9060
+ console.error(
9061
+ chalk11.red(` \u2717 ${err instanceof Error ? err.message : String(err)}`)
9062
+ );
9063
+ }
9064
+ console.error(
9065
+ chalk11.dim(
9066
+ " You can also email sami.ahmadkhan12@gmail.com directly."
9067
+ )
9068
+ );
9069
+ console.log("");
9070
+ process.exit(1);
9071
+ }
9072
+ }
9073
+ function promptForMultilineMessage() {
9074
+ return new Promise((resolve2) => {
9075
+ if (!process.stdin.isTTY) {
9076
+ let buf = "";
9077
+ process.stdin.setEncoding("utf8");
9078
+ process.stdin.on("data", (chunk) => {
9079
+ buf += chunk;
9080
+ });
9081
+ process.stdin.on("end", () => resolve2(buf.trim()));
9082
+ return;
9083
+ }
9084
+ console.log(
9085
+ chalk11.dim(
9086
+ " Type your feedback below. Submit with a single line containing 'EOF',"
9087
+ )
9088
+ );
9089
+ console.log(chalk11.dim(" or press Ctrl-D when done. Press Ctrl-C to abort."));
9090
+ console.log("");
9091
+ const rl = readline.createInterface({
9092
+ input: process.stdin,
9093
+ output: process.stdout,
9094
+ prompt: chalk11.yellow(" > ")
9095
+ });
9096
+ const lines = [];
9097
+ rl.prompt();
9098
+ rl.on("line", (line) => {
9099
+ if (line.trim() === "EOF") {
9100
+ rl.close();
9101
+ return;
9102
+ }
9103
+ lines.push(line);
9104
+ rl.prompt();
9105
+ });
9106
+ rl.on("close", () => {
9107
+ console.log("");
9108
+ resolve2(lines.join("\n").trim());
9109
+ });
9110
+ rl.on("SIGINT", () => {
9111
+ console.log(chalk11.red("\n \u2717 Aborted."));
9112
+ process.exit(1);
9113
+ });
9114
+ });
9115
+ }
9116
+
8888
9117
  // src/cli/index.ts
8889
9118
  init_version();
8890
9119
  var VERSION = getVersion();
@@ -8932,6 +9161,9 @@ program.command("scan", { isDefault: true }).description("Scan a project for vib
8932
9161
  ).option(
8933
9162
  "--update",
8934
9163
  "update the VibeDrift CLI to the latest version (alias for `vibedrift update`)"
9164
+ ).option(
9165
+ "--feedback [message...]",
9166
+ "send feedback to the maintainer (alias for `vibedrift feedback`)"
8935
9167
  ).option("--verbose", "show timing breakdown and analyzer details").addOption(
8936
9168
  new Option("--api-url <url>", "override the VibeDrift API base URL").hideHelp()
8937
9169
  ).action(async (path2, options) => {
@@ -8939,6 +9171,14 @@ program.command("scan", { isDefault: true }).description("Scan a project for vib
8939
9171
  await runUpdate(VERSION);
8940
9172
  return;
8941
9173
  }
9174
+ if (options.feedback) {
9175
+ const inline = Array.isArray(options.feedback) ? options.feedback.join(" ").trim() : "";
9176
+ await runFeedback({
9177
+ message: inline || void 0,
9178
+ apiUrl: options.apiUrl
9179
+ });
9180
+ return;
9181
+ }
8942
9182
  await runScan(path2, {
8943
9183
  json: options.json,
8944
9184
  format: options.json ? "json" : options.format,
@@ -8982,6 +9222,15 @@ program.command("doctor").description("Diagnose CLI installation, auth, and API
8982
9222
  program.command("update").description("Update the VibeDrift CLI to the latest version").action(async () => {
8983
9223
  await runUpdate(VERSION);
8984
9224
  });
9225
+ program.command("feedback").description("Send feedback, bug reports, or feature requests directly to the maintainer").argument(
9226
+ "[message...]",
9227
+ "feedback text (omit to be prompted interactively)"
9228
+ ).addOption(
9229
+ new Option("--api-url <url>", "override the API base URL").hideHelp()
9230
+ ).action(async (messageWords, options) => {
9231
+ const message = (messageWords ?? []).join(" ").trim() || void 0;
9232
+ await runFeedback({ message, apiUrl: options.apiUrl });
9233
+ });
8985
9234
  program.addHelpText(
8986
9235
  "after",
8987
9236
  `
@@ -9000,6 +9249,10 @@ Examples:
9000
9249
  $ vibedrift upgrade open the pricing page
9001
9250
  $ vibedrift billing manage your Stripe subscription
9002
9251
  $ vibedrift update update to the latest CLI version
9252
+ $ vibedrift feedback open an interactive feedback prompt
9253
+ $ vibedrift feedback "the install is broken on Windows"
9254
+ send inline feedback in one shot
9255
+ $ vibedrift --feedback "..." same as above, top-level shortcut
9003
9256
 
9004
9257
  Environment:
9005
9258
  VIBEDRIFT_TOKEN bearer token (overrides ~/.vibedrift/config.json)