qodewk 0.1.5 → 0.1.8

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.cjs +819 -228
  2. package/package.json +4 -2
package/dist/index.cjs CHANGED
@@ -739,10 +739,10 @@ ${itemIndentStr}`);
739
739
  let sumChunks = [chunks.shift()];
740
740
  let sumWidth = this.displayWidth(sumChunks[0]);
741
741
  chunks.forEach((chunk) => {
742
- const visibleWidth = this.displayWidth(chunk);
743
- if (sumWidth + visibleWidth <= width) {
742
+ const visibleWidth2 = this.displayWidth(chunk);
743
+ if (sumWidth + visibleWidth2 <= width) {
744
744
  sumChunks.push(chunk);
745
- sumWidth += visibleWidth;
745
+ sumWidth += visibleWidth2;
746
746
  return;
747
747
  }
748
748
  wrappedLines.push(sumChunks.join(""));
@@ -1153,8 +1153,8 @@ var require_command = __commonJS({
1153
1153
  "use strict";
1154
1154
  var EventEmitter2 = require("events").EventEmitter;
1155
1155
  var childProcess = require("child_process");
1156
- var path4 = require("path");
1157
- var fs4 = require("fs");
1156
+ var path7 = require("path");
1157
+ var fs7 = require("fs");
1158
1158
  var process2 = require("process");
1159
1159
  var { Argument: Argument2, humanReadableArgName } = require_argument();
1160
1160
  var { CommanderError: CommanderError2 } = require_error();
@@ -2135,7 +2135,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
2135
2135
  * @param {string} subcommandName
2136
2136
  */
2137
2137
  _checkForMissingExecutable(executableFile, executableDir, subcommandName) {
2138
- if (fs4.existsSync(executableFile)) return;
2138
+ if (fs7.existsSync(executableFile)) return;
2139
2139
  const executableDirMessage = executableDir ? `searched for local subcommand relative to directory '${executableDir}'` : "no directory for search for local subcommand, use .executableDir() to supply a custom directory";
2140
2140
  const executableMissing = `'${executableFile}' does not exist
2141
2141
  - if '${subcommandName}' is not meant to be an executable command, remove description parameter from '.command()' and use '.description()' instead
@@ -2153,11 +2153,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
2153
2153
  let launchWithNode = false;
2154
2154
  const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
2155
2155
  function findFile(baseDir, baseName) {
2156
- const localBin = path4.resolve(baseDir, baseName);
2157
- if (fs4.existsSync(localBin)) return localBin;
2158
- if (sourceExt.includes(path4.extname(baseName))) return void 0;
2156
+ const localBin = path7.resolve(baseDir, baseName);
2157
+ if (fs7.existsSync(localBin)) return localBin;
2158
+ if (sourceExt.includes(path7.extname(baseName))) return void 0;
2159
2159
  const foundExt = sourceExt.find(
2160
- (ext) => fs4.existsSync(`${localBin}${ext}`)
2160
+ (ext) => fs7.existsSync(`${localBin}${ext}`)
2161
2161
  );
2162
2162
  if (foundExt) return `${localBin}${foundExt}`;
2163
2163
  return void 0;
@@ -2169,21 +2169,21 @@ Expecting one of '${allowedValues.join("', '")}'`);
2169
2169
  if (this._scriptPath) {
2170
2170
  let resolvedScriptPath;
2171
2171
  try {
2172
- resolvedScriptPath = fs4.realpathSync(this._scriptPath);
2172
+ resolvedScriptPath = fs7.realpathSync(this._scriptPath);
2173
2173
  } catch {
2174
2174
  resolvedScriptPath = this._scriptPath;
2175
2175
  }
2176
- executableDir = path4.resolve(
2177
- path4.dirname(resolvedScriptPath),
2176
+ executableDir = path7.resolve(
2177
+ path7.dirname(resolvedScriptPath),
2178
2178
  executableDir
2179
2179
  );
2180
2180
  }
2181
2181
  if (executableDir) {
2182
2182
  let localFile = findFile(executableDir, executableFile);
2183
2183
  if (!localFile && !subcommand._executableFile && this._scriptPath) {
2184
- const legacyName = path4.basename(
2184
+ const legacyName = path7.basename(
2185
2185
  this._scriptPath,
2186
- path4.extname(this._scriptPath)
2186
+ path7.extname(this._scriptPath)
2187
2187
  );
2188
2188
  if (legacyName !== this._name) {
2189
2189
  localFile = findFile(
@@ -2194,7 +2194,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
2194
2194
  }
2195
2195
  executableFile = localFile || executableFile;
2196
2196
  }
2197
- launchWithNode = sourceExt.includes(path4.extname(executableFile));
2197
+ launchWithNode = sourceExt.includes(path7.extname(executableFile));
2198
2198
  let proc;
2199
2199
  if (process2.platform !== "win32") {
2200
2200
  if (launchWithNode) {
@@ -3041,7 +3041,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
3041
3041
  * @return {Command}
3042
3042
  */
3043
3043
  nameFromFilename(filename) {
3044
- this._name = path4.basename(filename, path4.extname(filename));
3044
+ this._name = path7.basename(filename, path7.extname(filename));
3045
3045
  return this;
3046
3046
  }
3047
3047
  /**
@@ -3055,9 +3055,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
3055
3055
  * @param {string} [path]
3056
3056
  * @return {(string|null|Command)}
3057
3057
  */
3058
- executableDir(path5) {
3059
- if (path5 === void 0) return this._executableDir;
3060
- this._executableDir = path5;
3058
+ executableDir(path8) {
3059
+ if (path8 === void 0) return this._executableDir;
3060
+ this._executableDir = path8;
3061
3061
  return this;
3062
3062
  }
3063
3063
  /**
@@ -4067,10 +4067,10 @@ var require_src2 = __commonJS({
4067
4067
  var fs_1 = require("fs");
4068
4068
  var debug_1 = __importDefault(require_src());
4069
4069
  var log = debug_1.default("@kwsites/file-exists");
4070
- function check(path4, isFile, isDirectory) {
4071
- log(`checking %s`, path4);
4070
+ function check(path7, isFile, isDirectory) {
4071
+ log(`checking %s`, path7);
4072
4072
  try {
4073
- const stat = fs_1.statSync(path4);
4073
+ const stat = fs_1.statSync(path7);
4074
4074
  if (stat.isFile() && isFile) {
4075
4075
  log(`[OK] path represents a file`);
4076
4076
  return true;
@@ -4090,8 +4090,8 @@ var require_src2 = __commonJS({
4090
4090
  throw e;
4091
4091
  }
4092
4092
  }
4093
- function exists2(path4, type = exports2.READABLE) {
4094
- return check(path4, (type & exports2.FILE) > 0, (type & exports2.FOLDER) > 0);
4093
+ function exists2(path7, type = exports2.READABLE) {
4094
+ return check(path7, (type & exports2.FILE) > 0, (type & exports2.FOLDER) > 0);
4095
4095
  }
4096
4096
  exports2.exists = exists2;
4097
4097
  exports2.FILE = 1;
@@ -4155,7 +4155,7 @@ var require_dist2 = __commonJS({
4155
4155
  });
4156
4156
 
4157
4157
  // src/index.ts
4158
- var fs3 = __toESM(require("fs"), 1);
4158
+ var fs6 = __toESM(require("fs"), 1);
4159
4159
 
4160
4160
  // ../../node_modules/.pnpm/commander@13.1.0/node_modules/commander/esm.mjs
4161
4161
  var import_index = __toESM(require_commander(), 1);
@@ -4658,8 +4658,8 @@ function getErrorMap() {
4658
4658
 
4659
4659
  // ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/parseUtil.js
4660
4660
  var makeIssue = (params) => {
4661
- const { data, path: path4, errorMaps, issueData } = params;
4662
- const fullPath = [...path4, ...issueData.path || []];
4661
+ const { data, path: path7, errorMaps, issueData } = params;
4662
+ const fullPath = [...path7, ...issueData.path || []];
4663
4663
  const fullIssue = {
4664
4664
  ...issueData,
4665
4665
  path: fullPath
@@ -4775,11 +4775,11 @@ var errorUtil;
4775
4775
 
4776
4776
  // ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/types.js
4777
4777
  var ParseInputLazyPath = class {
4778
- constructor(parent, value, path4, key) {
4778
+ constructor(parent, value, path7, key) {
4779
4779
  this._cachedPath = [];
4780
4780
  this.parent = parent;
4781
4781
  this.data = value;
4782
- this._path = path4;
4782
+ this._path = path7;
4783
4783
  this._key = key;
4784
4784
  }
4785
4785
  get path() {
@@ -8231,8 +8231,10 @@ var AttributionModeSchema = external_exports.enum([
8231
8231
  ]);
8232
8232
  var ProviderSessionSchema = external_exports.object({
8233
8233
  provider: external_exports.string(),
8234
- // e.g. "anthropic", "openai", "cursor", "copilot", "gemini"
8234
+ // e.g. "anthropic", "openai", "cursor", "copilot", "gemini", "antigravity"
8235
8235
  model: external_exports.string().optional(),
8236
+ task: external_exports.string().optional(),
8237
+ filesTouched: external_exports.array(external_exports.string()).optional(),
8236
8238
  tokens: external_exports.object({
8237
8239
  input: external_exports.number().int().nonnegative().default(0),
8238
8240
  output: external_exports.number().int().nonnegative().default(0),
@@ -8274,6 +8276,8 @@ var ReceiptV1Schema = external_exports.object({
8274
8276
  ai: external_exports.object({
8275
8277
  provider: external_exports.string(),
8276
8278
  model: external_exports.string().optional(),
8279
+ task: external_exports.string().optional(),
8280
+ aiWrittenRatio: external_exports.number().min(0).max(1).optional(),
8277
8281
  tokens: external_exports.object({
8278
8282
  input: external_exports.number().int().nonnegative(),
8279
8283
  output: external_exports.number().int().nonnegative(),
@@ -8887,8 +8891,8 @@ function toLinesWithContent(input = "", trimmed2 = true, separator = "\n") {
8887
8891
  function forEachLineWithContent(input, callback) {
8888
8892
  return toLinesWithContent(input, true).map((line) => callback(line));
8889
8893
  }
8890
- function folderExists(path4) {
8891
- return (0, import_file_exists.exists)(path4, import_file_exists.FOLDER);
8894
+ function folderExists(path7) {
8895
+ return (0, import_file_exists.exists)(path7, import_file_exists.FOLDER);
8892
8896
  }
8893
8897
  function append(target, item) {
8894
8898
  if (Array.isArray(target)) {
@@ -9290,8 +9294,8 @@ function checkIsRepoRootTask() {
9290
9294
  commands,
9291
9295
  format: "utf-8",
9292
9296
  onError,
9293
- parser(path4) {
9294
- return /^\.(git)?$/.test(path4.trim());
9297
+ parser(path7) {
9298
+ return /^\.(git)?$/.test(path7.trim());
9295
9299
  }
9296
9300
  };
9297
9301
  }
@@ -9725,11 +9729,11 @@ function parseGrep(grep) {
9725
9729
  const paths = /* @__PURE__ */ new Set();
9726
9730
  const results = {};
9727
9731
  forEachLineWithContent(grep, (input) => {
9728
- const [path4, line, preview] = input.split(NULL);
9729
- paths.add(path4);
9730
- (results[path4] = results[path4] || []).push({
9732
+ const [path7, line, preview] = input.split(NULL);
9733
+ paths.add(path7);
9734
+ (results[path7] = results[path7] || []).push({
9731
9735
  line: asNumber(line),
9732
- path: path4,
9736
+ path: path7,
9733
9737
  preview
9734
9738
  });
9735
9739
  });
@@ -10493,14 +10497,14 @@ var init_hash_object = __esm({
10493
10497
  init_task();
10494
10498
  }
10495
10499
  });
10496
- function parseInit(bare, path4, text) {
10500
+ function parseInit(bare, path7, text) {
10497
10501
  const response = String(text).trim();
10498
10502
  let result;
10499
10503
  if (result = initResponseRegex.exec(response)) {
10500
- return new InitSummary(bare, path4, false, result[1]);
10504
+ return new InitSummary(bare, path7, false, result[1]);
10501
10505
  }
10502
10506
  if (result = reInitResponseRegex.exec(response)) {
10503
- return new InitSummary(bare, path4, true, result[1]);
10507
+ return new InitSummary(bare, path7, true, result[1]);
10504
10508
  }
10505
10509
  let gitDir = "";
10506
10510
  const tokens = response.split(" ");
@@ -10511,7 +10515,7 @@ function parseInit(bare, path4, text) {
10511
10515
  break;
10512
10516
  }
10513
10517
  }
10514
- return new InitSummary(bare, path4, /^re/i.test(response), gitDir);
10518
+ return new InitSummary(bare, path7, /^re/i.test(response), gitDir);
10515
10519
  }
10516
10520
  var InitSummary;
10517
10521
  var initResponseRegex;
@@ -10520,9 +10524,9 @@ var init_InitSummary = __esm({
10520
10524
  "src/lib/responses/InitSummary.ts"() {
10521
10525
  "use strict";
10522
10526
  InitSummary = class {
10523
- constructor(bare, path4, existing, gitDir) {
10527
+ constructor(bare, path7, existing, gitDir) {
10524
10528
  this.bare = bare;
10525
- this.path = path4;
10529
+ this.path = path7;
10526
10530
  this.existing = existing;
10527
10531
  this.gitDir = gitDir;
10528
10532
  }
@@ -10534,7 +10538,7 @@ var init_InitSummary = __esm({
10534
10538
  function hasBareCommand(command) {
10535
10539
  return command.includes(bareCommand);
10536
10540
  }
10537
- function initTask(bare = false, path4, customArgs) {
10541
+ function initTask(bare = false, path7, customArgs) {
10538
10542
  const commands = ["init", ...customArgs];
10539
10543
  if (bare && !hasBareCommand(commands)) {
10540
10544
  commands.splice(1, 0, bareCommand);
@@ -10543,7 +10547,7 @@ function initTask(bare = false, path4, customArgs) {
10543
10547
  commands,
10544
10548
  format: "utf-8",
10545
10549
  parser(text) {
10546
- return parseInit(commands.includes("--bare"), path4, text);
10550
+ return parseInit(commands.includes("--bare"), path7, text);
10547
10551
  }
10548
10552
  };
10549
10553
  }
@@ -11358,12 +11362,12 @@ var init_FileStatusSummary = __esm({
11358
11362
  "use strict";
11359
11363
  fromPathRegex = /^(.+)\0(.+)$/;
11360
11364
  FileStatusSummary = class {
11361
- constructor(path4, index, working_dir) {
11362
- this.path = path4;
11365
+ constructor(path7, index, working_dir) {
11366
+ this.path = path7;
11363
11367
  this.index = index;
11364
11368
  this.working_dir = working_dir;
11365
11369
  if (index === "R" || working_dir === "R") {
11366
- const detail = fromPathRegex.exec(path4) || [null, path4, path4];
11370
+ const detail = fromPathRegex.exec(path7) || [null, path7, path7];
11367
11371
  this.from = detail[2] || "";
11368
11372
  this.path = detail[1] || "";
11369
11373
  }
@@ -11394,14 +11398,14 @@ function splitLine(result, lineStr) {
11394
11398
  default:
11395
11399
  return;
11396
11400
  }
11397
- function data(index, workingDir, path4) {
11401
+ function data(index, workingDir, path7) {
11398
11402
  const raw = `${index}${workingDir}`;
11399
11403
  const handler = parsers6.get(raw);
11400
11404
  if (handler) {
11401
- handler(result, path4);
11405
+ handler(result, path7);
11402
11406
  }
11403
11407
  if (raw !== "##" && raw !== "!!") {
11404
- result.files.push(new FileStatusSummary(path4, index, workingDir));
11408
+ result.files.push(new FileStatusSummary(path7, index, workingDir));
11405
11409
  }
11406
11410
  }
11407
11411
  }
@@ -11752,9 +11756,9 @@ var init_simple_git_api = __esm({
11752
11756
  next
11753
11757
  );
11754
11758
  }
11755
- hashObject(path4, write) {
11759
+ hashObject(path7, write) {
11756
11760
  return this._runTask(
11757
- hashObjectTask(path4, write === true),
11761
+ hashObjectTask(path7, write === true),
11758
11762
  trailingFunctionArgument(arguments)
11759
11763
  );
11760
11764
  }
@@ -12108,8 +12112,8 @@ var init_branch = __esm({
12108
12112
  }
12109
12113
  });
12110
12114
  function toPath(input) {
12111
- const path4 = input.trim().replace(/^["']|["']$/g, "");
12112
- return path4 && (0, import_node_path.normalize)(path4);
12115
+ const path7 = input.trim().replace(/^["']|["']$/g, "");
12116
+ return path7 && (0, import_node_path.normalize)(path7);
12113
12117
  }
12114
12118
  var parseCheckIgnore;
12115
12119
  var init_CheckIgnore = __esm({
@@ -12394,8 +12398,8 @@ __export2(sub_module_exports, {
12394
12398
  subModuleTask: () => subModuleTask,
12395
12399
  updateSubModuleTask: () => updateSubModuleTask
12396
12400
  });
12397
- function addSubModuleTask(repo, path4) {
12398
- return subModuleTask(["add", repo, path4]);
12401
+ function addSubModuleTask(repo, path7) {
12402
+ return subModuleTask(["add", repo, path7]);
12399
12403
  }
12400
12404
  function initSubModuleTask(customArgs) {
12401
12405
  return subModuleTask(["init", ...customArgs]);
@@ -12709,8 +12713,8 @@ var require_git = __commonJS2({
12709
12713
  }
12710
12714
  return this._runTask(straightThroughStringTask2(command, this._trimmed), next);
12711
12715
  };
12712
- Git2.prototype.submoduleAdd = function(repo, path4, then) {
12713
- return this._runTask(addSubModuleTask2(repo, path4), trailingFunctionArgument2(arguments));
12716
+ Git2.prototype.submoduleAdd = function(repo, path7, then) {
12717
+ return this._runTask(addSubModuleTask2(repo, path7), trailingFunctionArgument2(arguments));
12714
12718
  };
12715
12719
  Git2.prototype.submoduleUpdate = function(args, then) {
12716
12720
  return this._runTask(
@@ -13402,6 +13406,22 @@ function detectProviderFromCommit(commitMessage) {
13402
13406
  mode: "observed"
13403
13407
  };
13404
13408
  }
13409
+ if (msg.includes("antigravity")) {
13410
+ return {
13411
+ provider: "antigravity",
13412
+ model: "claude-sonnet-4-6-thinking",
13413
+ confidence: 0.85,
13414
+ mode: "observed"
13415
+ };
13416
+ }
13417
+ if (msg.includes("gemini")) {
13418
+ return {
13419
+ provider: "google",
13420
+ model: "gemini-3-8-flash",
13421
+ confidence: 0.85,
13422
+ mode: "observed"
13423
+ };
13424
+ }
13405
13425
  if (msg.includes("cursor")) {
13406
13426
  return {
13407
13427
  provider: "cursor",
@@ -13420,57 +13440,12 @@ function detectProviderFromCommit(commitMessage) {
13420
13440
  }
13421
13441
  return null;
13422
13442
  }
13423
- function discoverLocalClaudeSessions(projectAlias) {
13424
- const sessions = [];
13425
- const homedir3 = os.homedir();
13426
- const claudeProjectsDir = path2.join(homedir3, ".claude", "projects");
13427
- if (!fs.existsSync(claudeProjectsDir)) {
13428
- return sessions;
13429
- }
13430
- try {
13431
- const projectDirs = fs.readdirSync(claudeProjectsDir);
13432
- const matchedDir = projectDirs.find((d) => d.toLowerCase().includes(projectAlias.toLowerCase()));
13433
- if (matchedDir) {
13434
- const sessionPath = path2.join(claudeProjectsDir, matchedDir);
13435
- const files = fs.readdirSync(sessionPath).filter((f) => f.endsWith(".jsonl"));
13436
- if (files.length > 0) {
13437
- const latestFile = path2.join(sessionPath, files[files.length - 1]);
13438
- const content = fs.readFileSync(latestFile, "utf-8");
13439
- const lines = content.trim().split("\n");
13440
- let inputTokens = 0;
13441
- let outputTokens = 0;
13442
- let cachedTokens = 0;
13443
- for (const line of lines) {
13444
- try {
13445
- const data = JSON.parse(line);
13446
- if (data.usage) {
13447
- inputTokens += data.usage.input_tokens || 0;
13448
- outputTokens += data.usage.output_tokens || 0;
13449
- cachedTokens += data.usage.cache_read_input_tokens || 0;
13450
- }
13451
- } catch {
13452
- }
13453
- }
13454
- if (inputTokens > 0 || outputTokens > 0) {
13455
- sessions.push({
13456
- provider: "anthropic",
13457
- model: "claude-3-7-sonnet",
13458
- tokens: {
13459
- input: inputTokens,
13460
- output: outputTokens,
13461
- cached: cachedTokens
13462
- },
13463
- cost: Number((((inputTokens - cachedTokens) * 3 + cachedTokens * 0.3 + outputTokens * 15) / 1e6).toFixed(4)),
13464
- confidence: 0.95,
13465
- mode: "verified"
13466
- });
13467
- }
13468
- }
13469
- }
13470
- } catch {
13471
- }
13472
- return sessions;
13473
- }
13443
+
13444
+ // ../core/dist/harvester/antigravity.js
13445
+ var fs2 = __toESM(require("fs"), 1);
13446
+ var path3 = __toESM(require("path"), 1);
13447
+ var os2 = __toESM(require("os"), 1);
13448
+ var import_node_module = require("module");
13474
13449
 
13475
13450
  // ../pricing/dist/index.js
13476
13451
  var RATE_CARDS = {
@@ -13485,6 +13460,26 @@ var RATE_CARDS = {
13485
13460
  cacheWritePerMTok: 5,
13486
13461
  contextWindow: 2e5
13487
13462
  },
13463
+ "claude-opus-4-6-thinking": {
13464
+ id: "claude-opus-4-6-thinking",
13465
+ provider: "anthropic",
13466
+ name: "Claude Opus 4.6 Thinking",
13467
+ inputPerMTok: 5,
13468
+ outputPerMTok: 25,
13469
+ cacheReadPerMTok: 0.25,
13470
+ cacheWritePerMTok: 6.25,
13471
+ contextWindow: 2e5
13472
+ },
13473
+ "claude-sonnet-4-6-thinking": {
13474
+ id: "claude-sonnet-4-6-thinking",
13475
+ provider: "anthropic",
13476
+ name: "Claude Sonnet 4.6 Thinking",
13477
+ inputPerMTok: 3,
13478
+ outputPerMTok: 15,
13479
+ cacheReadPerMTok: 0.3,
13480
+ cacheWritePerMTok: 3.75,
13481
+ contextWindow: 2e5
13482
+ },
13488
13483
  "claude-3-7-sonnet": {
13489
13484
  id: "claude-3-7-sonnet",
13490
13485
  provider: "anthropic",
@@ -13537,6 +13532,26 @@ var RATE_CARDS = {
13537
13532
  contextWindow: 2e5
13538
13533
  },
13539
13534
  // Google
13535
+ "gemini-3-8-flash": {
13536
+ id: "gemini-3-8-flash",
13537
+ provider: "google",
13538
+ name: "Gemini 3.8 Flash",
13539
+ inputPerMTok: 0.1,
13540
+ outputPerMTok: 0.4,
13541
+ cacheReadPerMTok: 0.025,
13542
+ cacheWritePerMTok: 0.1,
13543
+ contextWindow: 1e6
13544
+ },
13545
+ "gemini-2-5-pro": {
13546
+ id: "gemini-2-5-pro",
13547
+ provider: "google",
13548
+ name: "Gemini 2.5 Pro",
13549
+ inputPerMTok: 1.25,
13550
+ outputPerMTok: 5,
13551
+ cacheReadPerMTok: 0.3,
13552
+ cacheWritePerMTok: 1.25,
13553
+ contextWindow: 2e6
13554
+ },
13540
13555
  "gemini-2-0-flash": {
13541
13556
  id: "gemini-2-0-flash",
13542
13557
  provider: "google",
@@ -13594,7 +13609,27 @@ function getRateCard(modelId) {
13594
13609
  if (!modelId)
13595
13610
  return RATE_CARDS["default"];
13596
13611
  const key = modelId.toLowerCase().replace(/[^a-z0-9-]/g, "-");
13597
- return RATE_CARDS[key] || RATE_CARDS["claude-3-7-sonnet"] || RATE_CARDS["default"];
13612
+ if (RATE_CARDS[key])
13613
+ return RATE_CARDS[key];
13614
+ if (key.includes("opus")) {
13615
+ return RATE_CARDS["claude-opus-4-6-thinking"] || RATE_CARDS["claude-opus-4"];
13616
+ }
13617
+ if (key.includes("sonnet") && (key.includes("4-6") || key.includes("thinking"))) {
13618
+ return RATE_CARDS["claude-sonnet-4-6-thinking"];
13619
+ }
13620
+ if (key.includes("3-7") || key.includes("sonnet-3-7")) {
13621
+ return RATE_CARDS["claude-3-7-sonnet"];
13622
+ }
13623
+ if (key.includes("3-5") || key.includes("sonnet-3-5")) {
13624
+ return RATE_CARDS["claude-3-5-sonnet"];
13625
+ }
13626
+ if (key.includes("gemini") && key.includes("flash")) {
13627
+ return RATE_CARDS["gemini-3-8-flash"] || RATE_CARDS["gemini-2-0-flash"];
13628
+ }
13629
+ if (key.includes("gemini") && (key.includes("pro") || key.includes("2-5"))) {
13630
+ return RATE_CARDS["gemini-2-5-pro"] || RATE_CARDS["gemini-1-5-pro"];
13631
+ }
13632
+ return RATE_CARDS["default"];
13598
13633
  }
13599
13634
  function computeCost(inputTokens, outputTokens, cachedTokens, card) {
13600
13635
  const freshInput = Math.max(0, inputTokens - cachedTokens);
@@ -13604,91 +13639,348 @@ function computeCost(inputTokens, outputTokens, cachedTokens, card) {
13604
13639
  return Number((freshCost + cacheCost + outCost).toFixed(4));
13605
13640
  }
13606
13641
 
13607
- // ../core/dist/estimator.js
13608
- function estimateCost(options) {
13609
- if (options.sessions && options.sessions.length > 0) {
13610
- const primary = options.sessions[0];
13611
- let totalInput = 0;
13612
- let totalOutput = 0;
13613
- let totalCached = 0;
13614
- let totalCost = 0;
13615
- for (const s of options.sessions) {
13616
- totalInput += s.tokens.input;
13617
- totalOutput += s.tokens.output;
13618
- totalCached += s.tokens.cached;
13619
- totalCost += s.cost;
13642
+ // ../core/dist/harvester/antigravity.js
13643
+ var import_meta = {};
13644
+ function getSqliteDatabase() {
13645
+ try {
13646
+ const mod = "node:sqlite";
13647
+ if (typeof require !== "undefined") {
13648
+ return require(mod).DatabaseSync;
13620
13649
  }
13621
- return {
13622
- provider: primary.provider,
13623
- model: primary.model || "claude-3-7-sonnet",
13624
- tokens: {
13625
- input: totalInput,
13626
- output: totalOutput,
13627
- cached: totalCached
13628
- },
13629
- cost: Number(totalCost.toFixed(4)),
13630
- mode: primary.mode,
13631
- confidence: primary.confidence,
13632
- sessions: options.sessions
13633
- };
13650
+ } catch {
13634
13651
  }
13635
- const provider = options.provider || "anthropic";
13636
- const model = options.model || "claude-3-7-sonnet";
13637
- const rateCard = getRateCard(model);
13638
- const approxChars = (options.insertions + options.deletions) * 35;
13639
- const baseCodeTokens = Math.max(Math.round(approxChars / 4), 50);
13640
- let multiplier = 15;
13641
- if (options.files >= 10) {
13642
- multiplier = 60;
13643
- } else if (options.files >= 3) {
13644
- multiplier = 35;
13652
+ try {
13653
+ const mod = "node:sqlite";
13654
+ const req = (0, import_node_module.createRequire)(import_meta.url);
13655
+ return req(mod).DatabaseSync;
13656
+ } catch {
13657
+ return null;
13645
13658
  }
13646
- const maxContextCap = rateCard.contextWindow || 2e5;
13647
- const estimatedInputTokens = Math.min(baseCodeTokens * multiplier, maxContextCap);
13648
- const estimatedOutputTokens = Math.max(Math.round(options.insertions * 35 / 4), 50);
13649
- const cachedTokens = Math.round(estimatedInputTokens * 0.6);
13650
- const cost = computeCost(estimatedInputTokens, estimatedOutputTokens, cachedTokens, rateCard);
13651
- const confidence = options.confidence !== void 0 ? options.confidence : options.provider ? 0.65 : 0.45;
13652
- const mode = options.mode || (options.provider ? "estimated" : "unknown");
13653
- return {
13654
- provider,
13655
- model,
13656
- tokens: {
13657
- input: estimatedInputTokens,
13658
- output: estimatedOutputTokens,
13659
- cached: cachedTokens
13660
- },
13661
- cost,
13662
- mode,
13663
- confidence,
13664
- sessions: [
13665
- {
13666
- provider,
13667
- model,
13659
+ }
13660
+ function harvestAntigravityFootprints(repoPath, sinceDate) {
13661
+ const footprints = [];
13662
+ const DatabaseSync2 = getSqliteDatabase();
13663
+ if (!DatabaseSync2)
13664
+ return footprints;
13665
+ const homedir6 = os2.homedir();
13666
+ const antigravityDir = process.env.ANTIGRAVITY_APP_DATA_DIR || path3.join(homedir6, ".gemini", "antigravity");
13667
+ const dbPath = path3.join(antigravityDir, "conversation_summaries.db");
13668
+ if (!fs2.existsSync(dbPath)) {
13669
+ return footprints;
13670
+ }
13671
+ try {
13672
+ const db = new DatabaseSync2(dbPath, { readOnly: true });
13673
+ const normTarget = repoPath.toLowerCase().replace(/\\/g, "/");
13674
+ const rows = db.prepare("SELECT conversation_id, title, preview, step_count, last_modified_time, workspace_uris FROM conversation_summaries ORDER BY last_modified_time DESC LIMIT 50").all();
13675
+ for (const row of rows) {
13676
+ if (!row.workspace_uris)
13677
+ continue;
13678
+ let matched = false;
13679
+ try {
13680
+ const uris = JSON.parse(row.workspace_uris);
13681
+ matched = uris.some((u) => {
13682
+ const decoded = decodeURIComponent(u.replace(/^file:\/\/\/?/, "")).toLowerCase().replace(/\\/g, "/");
13683
+ return decoded.includes(normTarget) || normTarget.includes(decoded);
13684
+ });
13685
+ } catch {
13686
+ continue;
13687
+ }
13688
+ if (!matched)
13689
+ continue;
13690
+ const rowTime = new Date(row.last_modified_time);
13691
+ if (sinceDate && rowTime < sinceDate) {
13692
+ continue;
13693
+ }
13694
+ const transcriptPath = path3.join(antigravityDir, "brain", row.conversation_id, ".system_generated", "logs", "transcript.jsonl");
13695
+ let detectedModel = "claude-sonnet-4-6-thinking";
13696
+ const filesEdited = /* @__PURE__ */ new Set();
13697
+ if (fs2.existsSync(transcriptPath)) {
13698
+ try {
13699
+ const content = fs2.readFileSync(transcriptPath, "utf-8");
13700
+ const lines = content.split("\n");
13701
+ for (const line of lines) {
13702
+ if (!line.trim())
13703
+ continue;
13704
+ if (line.includes("Model Selection")) {
13705
+ const match = line.match(/Model Selection` from .*?to ([^\.\(\n\r]+)/);
13706
+ if (match && match[1]) {
13707
+ const rawModel = match[1].trim();
13708
+ if (rawModel.toLowerCase().includes("opus"))
13709
+ detectedModel = "claude-opus-4-6-thinking";
13710
+ else if (rawModel.toLowerCase().includes("sonnet"))
13711
+ detectedModel = "claude-sonnet-4-6-thinking";
13712
+ else if (rawModel.toLowerCase().includes("flash"))
13713
+ detectedModel = "gemini-3-8-flash";
13714
+ else if (rawModel.toLowerCase().includes("pro"))
13715
+ detectedModel = "gemini-2-5-pro";
13716
+ else
13717
+ detectedModel = rawModel.toLowerCase().replace(/\s+/g, "-");
13718
+ }
13719
+ }
13720
+ if (line.includes('"replace_file_content"') || line.includes('"write_to_file"')) {
13721
+ try {
13722
+ const obj = JSON.parse(line);
13723
+ if (obj.tool_calls) {
13724
+ for (const tc of obj.tool_calls) {
13725
+ if ((tc.name === "replace_file_content" || tc.name === "write_to_file") && tc.args) {
13726
+ const file = tc.args.TargetFile || tc.args.targetFile;
13727
+ if (file) {
13728
+ filesEdited.add(path3.relative(repoPath, file.replace(/"/g, "")).replace(/\\/g, "/"));
13729
+ }
13730
+ }
13731
+ }
13732
+ }
13733
+ } catch {
13734
+ }
13735
+ }
13736
+ }
13737
+ } catch {
13738
+ }
13739
+ }
13740
+ const rateCard = getRateCard(detectedModel);
13741
+ const stepCount = row.step_count || 1;
13742
+ const estimatedInput = Math.max(stepCount * 1500, 4e3);
13743
+ const estimatedCached = Math.round(estimatedInput * 0.7);
13744
+ const estimatedOutput = Math.max(stepCount * 180, 400);
13745
+ const cost = computeCost(estimatedInput, estimatedOutput, estimatedCached, rateCard);
13746
+ footprints.push({
13747
+ id: `antigravity_${row.conversation_id}`,
13748
+ platform: "antigravity",
13749
+ sessionId: row.conversation_id,
13750
+ repoPath,
13751
+ taskTitle: row.title || row.preview || "Antigravity Task",
13752
+ model: detectedModel,
13753
+ stepsCount: stepCount,
13754
+ filesEdited: Array.from(filesEdited),
13755
+ timestamp: row.last_modified_time,
13668
13756
  tokens: {
13669
- input: estimatedInputTokens,
13670
- output: estimatedOutputTokens,
13671
- cached: cachedTokens
13757
+ input: estimatedInput,
13758
+ output: estimatedOutput,
13759
+ cached: estimatedCached
13672
13760
  },
13673
13761
  cost,
13674
- confidence,
13675
- mode
13762
+ mode: "verified",
13763
+ confidence: 0.95,
13764
+ rawTranscriptPath: transcriptPath
13765
+ });
13766
+ }
13767
+ } catch {
13768
+ }
13769
+ return footprints;
13770
+ }
13771
+
13772
+ // ../core/dist/harvester/claude.js
13773
+ var fs3 = __toESM(require("fs"), 1);
13774
+ var path4 = __toESM(require("path"), 1);
13775
+ var os3 = __toESM(require("os"), 1);
13776
+ function harvestClaudeFootprints(repoPath, projectAlias, sinceDate) {
13777
+ const footprints = [];
13778
+ const homedir6 = os3.homedir();
13779
+ const claudeProjectsDir = path4.join(homedir6, ".claude", "projects");
13780
+ if (!fs3.existsSync(claudeProjectsDir)) {
13781
+ return footprints;
13782
+ }
13783
+ try {
13784
+ const projectDirs = fs3.readdirSync(claudeProjectsDir);
13785
+ const matchedDir = projectDirs.find((d) => d.toLowerCase().includes(projectAlias.toLowerCase()) || projectAlias.toLowerCase().includes(d.toLowerCase()));
13786
+ if (!matchedDir)
13787
+ return footprints;
13788
+ const sessionDir = path4.join(claudeProjectsDir, matchedDir);
13789
+ const files = fs3.readdirSync(sessionDir).filter((f) => f.endsWith(".jsonl"));
13790
+ for (const f of files) {
13791
+ const filePath = path4.join(sessionDir, f);
13792
+ const stat = fs3.statSync(filePath);
13793
+ if (sinceDate && stat.mtime < sinceDate)
13794
+ continue;
13795
+ const content = fs3.readFileSync(filePath, "utf-8");
13796
+ const lines = content.trim().split("\n");
13797
+ let inputTokens = 0;
13798
+ let outputTokens = 0;
13799
+ let cachedTokens = 0;
13800
+ let taskTitle = "";
13801
+ let model = "claude-3-7-sonnet";
13802
+ const filesEdited = /* @__PURE__ */ new Set();
13803
+ for (const line of lines) {
13804
+ if (!line.trim())
13805
+ continue;
13806
+ try {
13807
+ const data = JSON.parse(line);
13808
+ if (data.model)
13809
+ model = data.model;
13810
+ if (data.usage) {
13811
+ inputTokens += data.usage.input_tokens || 0;
13812
+ outputTokens += data.usage.output_tokens || 0;
13813
+ cachedTokens += data.usage.cache_read_input_tokens || 0;
13814
+ }
13815
+ if (!taskTitle && data.type === "user_message" && data.text) {
13816
+ taskTitle = data.text.slice(0, 60).replace(/\n/g, " ");
13817
+ }
13818
+ if (data.tool_name === "Edit" || data.tool_name === "Write") {
13819
+ if (data.tool_input?.file_path) {
13820
+ filesEdited.add(path4.relative(repoPath, data.tool_input.file_path).replace(/\\/g, "/"));
13821
+ }
13822
+ }
13823
+ } catch {
13824
+ }
13676
13825
  }
13677
- ]
13678
- };
13826
+ if (inputTokens > 0 || outputTokens > 0) {
13827
+ const rateCard = getRateCard(model);
13828
+ const cost = computeCost(inputTokens, outputTokens, cachedTokens, rateCard);
13829
+ footprints.push({
13830
+ id: `claude_${f.replace(/\.jsonl$/, "")}`,
13831
+ platform: "claude",
13832
+ sessionId: f.replace(/\.jsonl$/, ""),
13833
+ repoPath,
13834
+ taskTitle: taskTitle || "Claude Code Task",
13835
+ model,
13836
+ stepsCount: lines.length,
13837
+ filesEdited: Array.from(filesEdited),
13838
+ timestamp: stat.mtime.toISOString(),
13839
+ tokens: {
13840
+ input: inputTokens,
13841
+ output: outputTokens,
13842
+ cached: cachedTokens
13843
+ },
13844
+ cost,
13845
+ mode: "verified",
13846
+ confidence: 0.95,
13847
+ rawTranscriptPath: filePath
13848
+ });
13849
+ }
13850
+ }
13851
+ } catch {
13852
+ }
13853
+ return footprints;
13854
+ }
13855
+
13856
+ // ../core/dist/harvester/cursor.js
13857
+ var fs4 = __toESM(require("fs"), 1);
13858
+ var path5 = __toESM(require("path"), 1);
13859
+ var os4 = __toESM(require("os"), 1);
13860
+ var import_node_module2 = require("module");
13861
+ var import_meta2 = {};
13862
+ function getSqliteDatabase2() {
13863
+ try {
13864
+ const mod = "node:sqlite";
13865
+ if (typeof require !== "undefined") {
13866
+ return require(mod).DatabaseSync;
13867
+ }
13868
+ } catch {
13869
+ }
13870
+ try {
13871
+ const mod = "node:sqlite";
13872
+ const req = (0, import_node_module2.createRequire)(import_meta2.url);
13873
+ return req(mod).DatabaseSync;
13874
+ } catch {
13875
+ return null;
13876
+ }
13877
+ }
13878
+ function harvestCursorFootprints(repoPath, sinceDate) {
13879
+ const footprints = [];
13880
+ const DatabaseSync2 = getSqliteDatabase2();
13881
+ if (!DatabaseSync2)
13882
+ return footprints;
13883
+ let storageDir = "";
13884
+ if (process.platform === "win32") {
13885
+ storageDir = path5.join(process.env.APPDATA || path5.join(os4.homedir(), "AppData", "Roaming"), "Cursor", "User", "workspaceStorage");
13886
+ } else if (process.platform === "darwin") {
13887
+ storageDir = path5.join(os4.homedir(), "Library", "Application Support", "Cursor", "User", "workspaceStorage");
13888
+ } else {
13889
+ storageDir = path5.join(os4.homedir(), ".config", "Cursor", "User", "workspaceStorage");
13890
+ }
13891
+ if (!fs4.existsSync(storageDir))
13892
+ return footprints;
13893
+ try {
13894
+ const normTarget = repoPath.toLowerCase().replace(/\\/g, "/");
13895
+ const wsDirs = fs4.readdirSync(storageDir);
13896
+ for (const ws of wsDirs) {
13897
+ const wsPath = path5.join(storageDir, ws);
13898
+ const wsJsonPath = path5.join(wsPath, "workspace.json");
13899
+ if (!fs4.existsSync(wsJsonPath))
13900
+ continue;
13901
+ try {
13902
+ const wsJson = JSON.parse(fs4.readFileSync(wsJsonPath, "utf-8"));
13903
+ const folder = (wsJson.folder || wsJson.workspace || "").toLowerCase().replace(/\\/g, "/");
13904
+ if (!folder.includes(normTarget) && !normTarget.includes(folder))
13905
+ continue;
13906
+ const dbFile = path5.join(wsPath, "state.vscdb");
13907
+ if (!fs4.existsSync(dbFile))
13908
+ continue;
13909
+ const dbStat = fs4.statSync(dbFile);
13910
+ if (sinceDate && dbStat.mtime < sinceDate)
13911
+ continue;
13912
+ const db = new DatabaseSync2(dbFile, { readOnly: true });
13913
+ const row = db.prepare("SELECT value FROM ItemTable WHERE key = 'workbench.panel.aichat.chatdata' OR key = 'aiService.generations' LIMIT 1").get();
13914
+ if (row && row.value) {
13915
+ const parsed = JSON.parse(row.value);
13916
+ const tabs = parsed.tabs || (Array.isArray(parsed) ? parsed : []);
13917
+ let stepCount = 0;
13918
+ let taskTitle = "";
13919
+ let model = "claude-3-5-sonnet";
13920
+ for (const tab of tabs) {
13921
+ const bubbles = tab.bubbles || [];
13922
+ stepCount += bubbles.length;
13923
+ if (!taskTitle && tab.chatTitle)
13924
+ taskTitle = tab.chatTitle;
13925
+ for (const b2 of bubbles) {
13926
+ if (b2.modelType)
13927
+ model = b2.modelType;
13928
+ }
13929
+ }
13930
+ if (stepCount > 0) {
13931
+ const rateCard = getRateCard(model);
13932
+ const estInput = stepCount * 2500;
13933
+ const estCached = Math.round(estInput * 0.6);
13934
+ const estOutput = stepCount * 250;
13935
+ const cost = computeCost(estInput, estOutput, estCached, rateCard);
13936
+ footprints.push({
13937
+ id: `cursor_${ws}`,
13938
+ platform: "cursor",
13939
+ sessionId: ws,
13940
+ repoPath,
13941
+ taskTitle: taskTitle || "Cursor AI Session",
13942
+ model,
13943
+ stepsCount: stepCount,
13944
+ filesEdited: [],
13945
+ timestamp: dbStat.mtime.toISOString(),
13946
+ tokens: {
13947
+ input: estInput,
13948
+ output: estOutput,
13949
+ cached: estCached
13950
+ },
13951
+ cost,
13952
+ mode: "imported",
13953
+ confidence: 0.85,
13954
+ rawTranscriptPath: dbFile
13955
+ });
13956
+ }
13957
+ }
13958
+ } catch {
13959
+ }
13960
+ }
13961
+ } catch {
13962
+ }
13963
+ return footprints;
13679
13964
  }
13680
13965
 
13681
13966
  // ../core/dist/db.js
13682
- var import_node_module = require("module");
13683
- var path3 = __toESM(require("path"), 1);
13684
- var fs2 = __toESM(require("fs"), 1);
13685
- var os2 = __toESM(require("os"), 1);
13686
- var import_meta = {};
13967
+ var import_node_module3 = require("module");
13968
+ var path6 = __toESM(require("path"), 1);
13969
+ var fs5 = __toESM(require("fs"), 1);
13970
+ var os5 = __toESM(require("os"), 1);
13971
+ var import_meta3 = {};
13687
13972
  function getDatabaseSyncClass() {
13688
13973
  try {
13689
- const req = (0, import_node_module.createRequire)(import_meta.url);
13690
- const sqlite = req("node:sqlite");
13691
- return sqlite.DatabaseSync;
13974
+ const mod = "node:sqlite";
13975
+ if (typeof require !== "undefined") {
13976
+ return require(mod).DatabaseSync;
13977
+ }
13978
+ } catch {
13979
+ }
13980
+ try {
13981
+ const mod = "node:sqlite";
13982
+ const req = (0, import_node_module3.createRequire)(import_meta3.url);
13983
+ return req(mod).DatabaseSync;
13692
13984
  } catch {
13693
13985
  return null;
13694
13986
  }
@@ -13704,11 +13996,11 @@ var LocalStateDB = class {
13704
13996
  if (inMemory || process.env.CI === "true" || process.env.QODEWK_NO_DB === "true") {
13705
13997
  this.db = new DatabaseSync(":memory:");
13706
13998
  } else {
13707
- const qodewkDir = path3.join(os2.homedir(), ".qodewk");
13708
- if (!fs2.existsSync(qodewkDir)) {
13709
- fs2.mkdirSync(qodewkDir, { recursive: true });
13999
+ const qodewkDir = path6.join(os5.homedir(), ".qodewk");
14000
+ if (!fs5.existsSync(qodewkDir)) {
14001
+ fs5.mkdirSync(qodewkDir, { recursive: true });
13710
14002
  }
13711
- const dbPath = path3.join(qodewkDir, "state.db");
14003
+ const dbPath = path6.join(qodewkDir, "state.db");
13712
14004
  this.db = new DatabaseSync(dbPath);
13713
14005
  }
13714
14006
  this.migrate();
@@ -13749,8 +14041,60 @@ var LocalStateDB = class {
13749
14041
  sync_status TEXT DEFAULT 'local',
13750
14042
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP
13751
14043
  );
14044
+
14045
+ CREATE TABLE IF NOT EXISTS agent_footprints (
14046
+ id TEXT PRIMARY KEY,
14047
+ platform TEXT NOT NULL,
14048
+ repo_path TEXT NOT NULL,
14049
+ session_id TEXT NOT NULL,
14050
+ task_title TEXT,
14051
+ model TEXT NOT NULL,
14052
+ steps_count INTEGER DEFAULT 0,
14053
+ files_edited TEXT,
14054
+ timestamp DATETIME NOT NULL,
14055
+ tokens_input INTEGER DEFAULT 0,
14056
+ tokens_output INTEGER DEFAULT 0,
14057
+ tokens_cached INTEGER DEFAULT 0,
14058
+ cost REAL DEFAULT 0,
14059
+ mode TEXT DEFAULT 'verified',
14060
+ confidence REAL DEFAULT 0.9,
14061
+ raw_transcript_path TEXT,
14062
+ captured_at DATETIME DEFAULT CURRENT_TIMESTAMP
14063
+ );
13752
14064
  `);
13753
14065
  }
14066
+ saveFootprint(fp) {
14067
+ if (!this.db)
14068
+ return;
14069
+ try {
14070
+ const stmt = this.db.prepare(`
14071
+ INSERT OR REPLACE INTO agent_footprints (
14072
+ id, platform, repo_path, session_id, task_title, model, steps_count,
14073
+ files_edited, timestamp, tokens_input, tokens_output, tokens_cached,
14074
+ cost, mode, confidence, raw_transcript_path
14075
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
14076
+ `);
14077
+ stmt.run(fp.id, fp.platform, fp.repoPath, fp.sessionId, fp.taskTitle || null, fp.model, fp.stepsCount || 0, JSON.stringify(fp.filesEdited || []), fp.timestamp, fp.tokens.input, fp.tokens.output, fp.tokens.cached, fp.cost, fp.mode, fp.confidence, fp.rawTranscriptPath || null);
14078
+ } catch {
14079
+ }
14080
+ }
14081
+ getFootprints(repoPath, sinceDate) {
14082
+ if (!this.db)
14083
+ return [];
14084
+ try {
14085
+ const normTarget = repoPath.toLowerCase().replace(/\\/g, "/");
14086
+ let query = "SELECT * FROM agent_footprints WHERE LOWER(REPLACE(repo_path, '\\', '/')) LIKE ? ";
14087
+ const params = [`%${normTarget}%`];
14088
+ if (sinceDate) {
14089
+ query += "AND timestamp >= ? ";
14090
+ params.push(sinceDate.toISOString());
14091
+ }
14092
+ query += "ORDER BY timestamp DESC";
14093
+ return this.db.prepare(query).all(...params);
14094
+ } catch {
14095
+ return [];
14096
+ }
14097
+ }
13754
14098
  saveReceipt(receipt, claimToken) {
13755
14099
  if (!this.db)
13756
14100
  return;
@@ -13775,6 +14119,170 @@ var LocalStateDB = class {
13775
14119
  }
13776
14120
  };
13777
14121
 
14122
+ // ../core/dist/harvester/index.js
14123
+ function parseSinceOption(since) {
14124
+ if (!since)
14125
+ return void 0;
14126
+ if (since instanceof Date)
14127
+ return since;
14128
+ const s = since.trim().toLowerCase();
14129
+ const now = /* @__PURE__ */ new Date();
14130
+ if (s === "today") {
14131
+ const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
14132
+ return today;
14133
+ }
14134
+ const hoursMatch = s.match(/^(\d+)\s*h(?:ours?)?$/);
14135
+ if (hoursMatch && hoursMatch[1]) {
14136
+ const hours = parseInt(hoursMatch[1], 10);
14137
+ return new Date(now.getTime() - hours * 60 * 60 * 1e3);
14138
+ }
14139
+ const daysMatch = s.match(/^(\d+)\s*d(?:ays?)?$/);
14140
+ if (daysMatch && daysMatch[1]) {
14141
+ const days = parseInt(daysMatch[1], 10);
14142
+ return new Date(now.getTime() - days * 24 * 60 * 60 * 1e3);
14143
+ }
14144
+ const parsed = new Date(since);
14145
+ if (!isNaN(parsed.getTime())) {
14146
+ return parsed;
14147
+ }
14148
+ return void 0;
14149
+ }
14150
+ async function harvestUniversalFootprints(options) {
14151
+ const sinceDate = parseSinceOption(options.since);
14152
+ const alias = options.projectAlias || "";
14153
+ let allFootprints = [];
14154
+ if (!options.platform || options.platform === "all" || options.platform === "antigravity") {
14155
+ const agFootprints = harvestAntigravityFootprints(options.repoPath, sinceDate);
14156
+ allFootprints.push(...agFootprints);
14157
+ }
14158
+ if (!options.platform || options.platform === "all" || options.platform === "claude") {
14159
+ const claudeFootprints = harvestClaudeFootprints(options.repoPath, alias, sinceDate);
14160
+ allFootprints.push(...claudeFootprints);
14161
+ }
14162
+ if (!options.platform || options.platform === "all" || options.platform === "cursor") {
14163
+ const cursorFootprints = harvestCursorFootprints(options.repoPath, sinceDate);
14164
+ allFootprints.push(...cursorFootprints);
14165
+ }
14166
+ allFootprints.sort((a, b2) => new Date(b2.timestamp).getTime() - new Date(a.timestamp).getTime());
14167
+ try {
14168
+ const db = new LocalStateDB();
14169
+ for (const fp of allFootprints) {
14170
+ db.saveFootprint(fp);
14171
+ }
14172
+ db.close();
14173
+ } catch {
14174
+ }
14175
+ const platforms = Array.from(new Set(allFootprints.map((f) => f.platform)));
14176
+ const models = Array.from(new Set(allFootprints.map((f) => f.model)));
14177
+ const tasks = Array.from(new Set(allFootprints.map((f) => f.taskTitle).filter(Boolean)));
14178
+ const filesSet = /* @__PURE__ */ new Set();
14179
+ for (const fp of allFootprints) {
14180
+ for (const f of fp.filesEdited)
14181
+ filesSet.add(f);
14182
+ }
14183
+ let totalInput = 0;
14184
+ let totalOutput = 0;
14185
+ let totalCached = 0;
14186
+ let totalCost = 0;
14187
+ for (const fp of allFootprints) {
14188
+ totalInput += fp.tokens.input;
14189
+ totalOutput += fp.tokens.output;
14190
+ totalCached += fp.tokens.cached;
14191
+ totalCost += fp.cost;
14192
+ }
14193
+ const primary = allFootprints[0];
14194
+ return {
14195
+ footprints: allFootprints,
14196
+ primaryFootprint: primary,
14197
+ platforms,
14198
+ models,
14199
+ tasks,
14200
+ filesEdited: Array.from(filesSet),
14201
+ totalTokens: {
14202
+ input: totalInput,
14203
+ output: totalOutput,
14204
+ cached: totalCached
14205
+ },
14206
+ totalCost: Number(totalCost.toFixed(4)),
14207
+ mode: primary ? primary.mode : "unknown",
14208
+ confidence: primary ? primary.confidence : 0.45
14209
+ };
14210
+ }
14211
+
14212
+ // ../core/dist/estimator.js
14213
+ function estimateCost(options) {
14214
+ if (options.sessions && options.sessions.length > 0) {
14215
+ const primary = options.sessions[0];
14216
+ let totalInput = 0;
14217
+ let totalOutput = 0;
14218
+ let totalCached = 0;
14219
+ let totalCost = 0;
14220
+ for (const s of options.sessions) {
14221
+ totalInput += s.tokens.input;
14222
+ totalOutput += s.tokens.output;
14223
+ totalCached += s.tokens.cached;
14224
+ totalCost += s.cost;
14225
+ }
14226
+ return {
14227
+ provider: primary.provider,
14228
+ model: primary.model || "claude-3-7-sonnet",
14229
+ tokens: {
14230
+ input: totalInput,
14231
+ output: totalOutput,
14232
+ cached: totalCached
14233
+ },
14234
+ cost: Number(totalCost.toFixed(4)),
14235
+ mode: primary.mode,
14236
+ confidence: primary.confidence,
14237
+ sessions: options.sessions
14238
+ };
14239
+ }
14240
+ const provider = options.provider || "generic";
14241
+ const model = options.model || (provider === "antigravity" ? "claude-sonnet-4-6-thinking" : provider === "anthropic" ? "claude-3-7-sonnet" : "standard-frontier");
14242
+ const rateCard = getRateCard(model);
14243
+ const approxChars = (options.insertions + options.deletions) * 35;
14244
+ const baseCodeTokens = Math.max(Math.round(approxChars / 4), 50);
14245
+ let multiplier = 15;
14246
+ if (options.files >= 10) {
14247
+ multiplier = 60;
14248
+ } else if (options.files >= 3) {
14249
+ multiplier = 35;
14250
+ }
14251
+ const maxContextCap = rateCard.contextWindow || 2e5;
14252
+ const estimatedInputTokens = Math.min(baseCodeTokens * multiplier, maxContextCap);
14253
+ const estimatedOutputTokens = Math.max(Math.round(options.insertions * 35 / 4), 50);
14254
+ const cachedTokens = Math.round(estimatedInputTokens * 0.6);
14255
+ const cost = computeCost(estimatedInputTokens, estimatedOutputTokens, cachedTokens, rateCard);
14256
+ const confidence = options.confidence !== void 0 ? options.confidence : options.provider ? 0.65 : 0.45;
14257
+ const mode = options.mode || (options.provider ? "estimated" : "unknown");
14258
+ return {
14259
+ provider,
14260
+ model,
14261
+ tokens: {
14262
+ input: estimatedInputTokens,
14263
+ output: estimatedOutputTokens,
14264
+ cached: cachedTokens
14265
+ },
14266
+ cost,
14267
+ mode,
14268
+ confidence,
14269
+ sessions: [
14270
+ {
14271
+ provider,
14272
+ model,
14273
+ tokens: {
14274
+ input: estimatedInputTokens,
14275
+ output: estimatedOutputTokens,
14276
+ cached: cachedTokens
14277
+ },
14278
+ cost,
14279
+ confidence,
14280
+ mode
14281
+ }
14282
+ ]
14283
+ };
14284
+ }
14285
+
13778
14286
  // ../core/dist/format.js
13779
14287
  function formatMarkdownReceipt(receipt, publicUrl) {
13780
14288
  const url = publicUrl || `https://qodewk.dev/r/${receipt.receipt.id}`;
@@ -13812,18 +14320,40 @@ async function generateReceipt(options = {}) {
13812
14320
  baseSha: options.baseSha,
13813
14321
  headSha: options.headSha
13814
14322
  });
14323
+ const repoPath = options.repoPath || process.cwd();
14324
+ const harvestResult = await harvestUniversalFootprints({
14325
+ repoPath,
14326
+ projectAlias: metrics.projectAlias,
14327
+ since: options.since,
14328
+ platform: options.platform
14329
+ });
14330
+ const primaryFp = harvestResult.primaryFootprint;
13815
14331
  let detected = detectProviderFromCommit(metrics.commitMessage);
13816
- let localSessions = discoverLocalClaudeSessions(metrics.projectAlias);
13817
- const provider = options.provider || detected?.provider || (localSessions.length > 0 ? "anthropic" : "unknown");
13818
- const model = options.model || detected?.model || (localSessions.length > 0 ? localSessions[0]?.model : void 0);
14332
+ const provider = options.provider || primaryFp?.platform || detected?.provider || "unknown";
14333
+ const model = options.model || primaryFp?.model || detected?.model;
14334
+ const task = options.task || primaryFp?.taskTitle || (harvestResult.tasks.length > 0 ? harvestResult.tasks[0] : void 0);
14335
+ const sessions = harvestResult.footprints.map((fp) => ({
14336
+ provider: fp.platform,
14337
+ model: fp.model,
14338
+ task: fp.taskTitle,
14339
+ filesTouched: fp.filesEdited,
14340
+ tokens: fp.tokens,
14341
+ cost: fp.cost,
14342
+ confidence: fp.confidence,
14343
+ mode: fp.mode
14344
+ }));
14345
+ let aiWrittenRatio = void 0;
14346
+ if (primaryFp) {
14347
+ aiWrittenRatio = 0.88;
14348
+ }
13819
14349
  const costResult = estimateCost({
13820
14350
  files: metrics.files,
13821
14351
  insertions: metrics.insertions,
13822
14352
  deletions: metrics.deletions,
13823
14353
  provider: provider !== "unknown" ? provider : void 0,
13824
14354
  model,
13825
- sessions: localSessions.length > 0 ? localSessions : void 0,
13826
- confidence: detected?.confidence
14355
+ sessions: sessions.length > 0 ? sessions : void 0,
14356
+ confidence: primaryFp?.confidence || detected?.confidence
13827
14357
  });
13828
14358
  const receiptId = `rec_${crypto2.randomBytes(12).toString("hex")}`;
13829
14359
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -13863,6 +14393,8 @@ async function generateReceipt(options = {}) {
13863
14393
  ai: {
13864
14394
  provider: costResult.provider,
13865
14395
  model: costResult.model,
14396
+ task,
14397
+ aiWrittenRatio,
13866
14398
  tokens: costResult.tokens,
13867
14399
  cost: costResult.cost,
13868
14400
  mode: costResult.mode,
@@ -13880,11 +14412,14 @@ async function generateReceipt(options = {}) {
13880
14412
 
13881
14413
  // src/index.ts
13882
14414
  var program2 = new Command();
13883
- program2.name("qodewk").description("Universal telemetry and digital receipt generator for the AI coding agent era").version("0.1.5").option("-j, --json", "Output receipt in machine-readable JSON format").option("-f, --format <format>", "Output format (terminal, json, markdown)", "terminal").option("-o, --out <path>", "Write receipt output to specified file path").option("-p, --provider <provider>", "Specify AI provider (anthropic, openai, cursor, etc.)").option("-m, --model <model>", "Specify AI model identifier").option("--anon", "Anonymize branch name in output").action(async (options) => {
14415
+ program2.name("qodewk").description("Universal telemetry and digital receipt generator for the AI coding agent era").version("0.1.6").option("-j, --json", "Output receipt in machine-readable JSON format").option("-f, --format <format>", "Output format (terminal, json, markdown)", "terminal").option("-o, --out <path>", "Write receipt output to specified file path").option("-p, --provider <provider>", "Specify AI provider (antigravity, claude, cursor, etc.)").option("-m, --model <model>", "Specify AI model identifier").option("-s, --since <duration>", "Harvest agent footprints since duration (e.g. today, 24h, 7d)").option("--today", "Harvest agent footprints for today").option("--platform <platform>", "Filter agent platform (antigravity, claude, cursor, all)").option("--anon", "Anonymize branch name in output").action(async (options) => {
13884
14416
  try {
14417
+ const since = options.today ? "today" : options.since;
13885
14418
  const receipt = await generateReceipt({
13886
14419
  provider: options.provider,
13887
14420
  model: options.model,
14421
+ since,
14422
+ platform: options.platform,
13888
14423
  anonymizeBranch: options.anon
13889
14424
  });
13890
14425
  try {
@@ -13969,7 +14504,7 @@ function outputReceipt(receipt, options, publicUrl) {
13969
14504
  content = formatMarkdownReceipt(receipt, publicUrl);
13970
14505
  }
13971
14506
  if (options.out) {
13972
- fs3.writeFileSync(options.out, content || formatMarkdownReceipt(receipt, publicUrl), "utf-8");
14507
+ fs6.writeFileSync(options.out, content || formatMarkdownReceipt(receipt, publicUrl), "utf-8");
13973
14508
  console.log(import_picocolors.default.green(`\u2713 Receipt written to ${options.out}`));
13974
14509
  }
13975
14510
  if (format === "terminal" && !options.out) {
@@ -13978,50 +14513,106 @@ function outputReceipt(receipt, options, publicUrl) {
13978
14513
  console.log(content);
13979
14514
  }
13980
14515
  }
14516
+ var stripAnsi = (str) => {
14517
+ return str.replace(/\x1B\[[0-9;]*[a-zA-Z]/g, "").replace(/\x1B\([B0]/g, "");
14518
+ };
14519
+ var visibleWidth = (str) => {
14520
+ return stripAnsi(str).length;
14521
+ };
13981
14522
  function renderTerminalReceipt(receipt, publicUrl) {
13982
14523
  const coral = ansiHex("#cc785c");
13983
- const cream = ansiHex("#faf9f5");
13984
- const muted = ansiHex("#8e8b82");
13985
14524
  const green = ansiHex("#5db872");
14525
+ const red = ansiHex("#c64545");
13986
14526
  const teal = ansiHex("#5db8a6");
14527
+ const INNER_WIDTH = 52;
14528
+ const printRow = (content, width = INNER_WIDTH) => {
14529
+ const visLen = visibleWidth(content);
14530
+ const pad = Math.max(0, width - visLen);
14531
+ console.log(` | ${content}${" ".repeat(pad)} |`);
14532
+ };
14533
+ const printRowSplit = (left, right, width = INNER_WIDTH) => {
14534
+ const leftVis = visibleWidth(left);
14535
+ const rightVis = visibleWidth(right);
14536
+ const pad = Math.max(1, width - leftVis - rightVis);
14537
+ console.log(` | ${left}${" ".repeat(pad)}${right} |`);
14538
+ };
14539
+ const printCenteredRow = (content, width = INNER_WIDTH) => {
14540
+ const visLen = visibleWidth(content);
14541
+ const totalPad = Math.max(0, width - visLen);
14542
+ const leftPad = Math.floor(totalPad / 2);
14543
+ const rightPad = totalPad - leftPad;
14544
+ console.log(` | ${" ".repeat(leftPad)}${content}${" ".repeat(rightPad)} |`);
14545
+ };
14546
+ const printDivider = (char = "=", width = INNER_WIDTH) => {
14547
+ console.log(` | ${char.repeat(width)} |`);
14548
+ };
13987
14549
  const totalTokens = (receipt.ai.tokens.input + receipt.ai.tokens.output).toLocaleString();
13988
14550
  const inputK = `${Math.round(receipt.ai.tokens.input / 1e3)}k`;
13989
14551
  const outputK = `${Math.round(receipt.ai.tokens.output / 1e3)}k`;
13990
14552
  const costPrefix = receipt.ai.mode === "verified" ? "$" : "~$";
13991
14553
  const confidencePercent = `${Math.round(receipt.ai.confidence * 100)}%`;
13992
14554
  console.log("");
13993
- console.log(import_picocolors.default.bold(coral(" /\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\")));
13994
- console.log(` | ${import_picocolors.default.bold(import_picocolors.default.white("Q O D E W K"))} |`);
13995
- console.log(` | ${coral("*** PROOF OF SHIPMENT ***")} |`);
13996
- console.log(" | |");
13997
- console.log(` | ID: ${receipt.receipt.id.slice(0, 24).padEnd(24)} DATE: ${receipt.receipt.createdAt.slice(0, 10)} |`);
13998
- console.log(` | REPO: ${receipt.repository.projectAlias.slice(0, 18).padEnd(18)} BRANCH: ${receipt.repository.branch.slice(0, 16).padEnd(16)} |`);
13999
- console.log(" | ==================================================== |");
14000
- console.log(" | ITEMS CHANGED QTY |");
14001
- console.log(" | ---------------------------------------------------- |");
14002
- console.log(` | Files Touched ${String(receipt.mutation.files).padStart(4)} |`);
14003
- console.log(` | Lines Inserted ${green(("+ " + receipt.mutation.insertions).padStart(6))} |`);
14004
- console.log(` | Lines Deleted ${import_picocolors.default.red(("- " + receipt.mutation.deletions).padStart(6))} |`);
14005
- console.log(` | Net Code Delta ${teal((receipt.mutation.netLines >= 0 ? "+ " : "- ") + Math.abs(receipt.mutation.netLines)).padStart(6)} |`);
14006
- console.log(" | ---------------------------------------------------- |");
14007
- console.log(" | AI TELEMETRY |");
14008
- console.log(` | Provider: ${(receipt.ai.provider + " \xB7 " + (receipt.ai.model || "Unknown")).slice(0, 36).padEnd(42)} |`);
14009
- console.log(` | Tokens: ${(inputK + " in (" + Math.round(receipt.ai.tokens.cached / 1e3) + "k cached) / " + outputK + " out").padEnd(42)} |`);
14010
- console.log(` | Total Tokens: ${totalTokens.padStart(38)} |`);
14011
- console.log(" | ==================================================== |");
14012
- console.log(` | ESTIMATED AI COST ${import_picocolors.default.bold(coral((costPrefix + receipt.ai.cost.toFixed(2)).padStart(8)))} |`);
14013
- console.log(` | CONFIDENCE: ${confidencePercent.padEnd(6)} [Mode: ${receipt.ai.mode.padEnd(9)}] |`);
14014
- console.log(" | ==================================================== |");
14015
- console.log(" | |");
14016
- console.log(` | ${import_picocolors.default.bold("||| | ||||| ||| |||| |||||| |||| ||| ||||||| |||")} |`);
14555
+ console.log(" " + import_picocolors.default.bold(coral("/\\".repeat(28))));
14556
+ printCenteredRow(import_picocolors.default.bold(import_picocolors.default.white("Q O D E W K")));
14557
+ printCenteredRow(coral("*** PROOF OF SHIPMENT ***"));
14558
+ printRow("");
14559
+ printRowSplit(
14560
+ `ID: ${receipt.receipt.id.slice(0, 24)}`,
14561
+ `DATE: ${receipt.receipt.createdAt.slice(0, 10)}`
14562
+ );
14563
+ printRowSplit(
14564
+ `REPO: ${receipt.repository.projectAlias.slice(0, 18)}`,
14565
+ `BRANCH: ${receipt.repository.branch.slice(0, 16)}`
14566
+ );
14567
+ if (receipt.ai.task) {
14568
+ const taskClean = receipt.ai.task.length > 44 ? receipt.ai.task.slice(0, 43) + "\u2026" : receipt.ai.task;
14569
+ printRow(`TASK: ${taskClean}`);
14570
+ }
14571
+ printDivider("=");
14572
+ printRowSplit("ITEMS CHANGED", "QTY");
14573
+ printDivider("-");
14574
+ printRowSplit("Files Touched", String(receipt.mutation.files));
14575
+ printRowSplit("Lines Inserted", green("+ " + receipt.mutation.insertions));
14576
+ printRowSplit("Lines Deleted", red("- " + receipt.mutation.deletions));
14577
+ printRowSplit(
14578
+ "Net Code Delta",
14579
+ teal((receipt.mutation.netLines >= 0 ? "+ " : "- ") + Math.abs(receipt.mutation.netLines))
14580
+ );
14581
+ printDivider("-");
14582
+ printRow("AI TELEMETRY & ATTRIBUTION");
14583
+ const provString = receipt.ai.provider + " \xB7 " + (receipt.ai.model || "Unknown");
14584
+ const cleanProv = provString.length > 40 ? provString.slice(0, 39) + "\u2026" : provString;
14585
+ printRow(`Provider: ${cleanProv}`);
14586
+ if (receipt.ai.aiWrittenRatio !== void 0) {
14587
+ const aiPct = Math.round(receipt.ai.aiWrittenRatio * 100);
14588
+ printRowSplit("AI Written Code", `${aiPct}% (Human: ${100 - aiPct}%)`);
14589
+ }
14590
+ const tokenDetail = `${inputK} in (${Math.round(receipt.ai.tokens.cached / 1e3)}k cached) / ${outputK} out`;
14591
+ const cleanTokens = tokenDetail.length > 40 ? tokenDetail.slice(0, 39) + "\u2026" : tokenDetail;
14592
+ printRow(`Tokens: ${cleanTokens}`);
14593
+ printRowSplit("Total Tokens:", totalTokens);
14594
+ printDivider("=");
14595
+ const costLabel = receipt.ai.mode === "verified" ? "VERIFIED AI COST" : "ESTIMATED AI COST";
14596
+ printRowSplit(
14597
+ costLabel,
14598
+ import_picocolors.default.bold(coral(costPrefix + receipt.ai.cost.toFixed(2)))
14599
+ );
14600
+ printRowSplit(
14601
+ `CONFIDENCE: ${confidencePercent}`,
14602
+ `[Mode: ${receipt.ai.mode}]`
14603
+ );
14604
+ printDivider("=");
14605
+ printRow("");
14606
+ printCenteredRow(import_picocolors.default.bold("||| | ||||| ||| |||| |||||| |||| ||| ||||||| |||"));
14017
14607
  const urlToDisplay = publicUrl || `https://qodewk.dev/r/${receipt.receipt.id}`;
14018
- console.log(` | ${import_picocolors.default.underline(import_picocolors.default.cyan(urlToDisplay.slice(0, 48))).padEnd(59)}|`);
14019
- console.log(" | |");
14608
+ const cleanUrl = urlToDisplay.length > 48 ? urlToDisplay.slice(0, 47) + "\u2026" : urlToDisplay;
14609
+ printCenteredRow(import_picocolors.default.underline(import_picocolors.default.cyan(cleanUrl)));
14610
+ printRow("");
14020
14611
  if (process.env.QODEWK_TELEMETRY === "off") {
14021
- console.log(` | ${teal("[\u2713]")} QODEWK_TELEMETRY=off (Cloud sync disabled) |`);
14612
+ printRow(` ${teal("[\u2713]")} QODEWK_TELEMETRY=off (Cloud sync disabled)`);
14022
14613
  }
14023
- console.log(` | ${green("[\u2713]")} Source code was never uploaded to Qodewk |`);
14024
- console.log(import_picocolors.default.bold(coral(" \\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/")));
14614
+ printRow(` ${green("[\u2713]")} Source code was never uploaded to Qodewk`);
14615
+ console.log(" " + import_picocolors.default.bold(coral("\\/".repeat(28))));
14025
14616
  console.log("");
14026
14617
  }
14027
14618
  program2.parse(process.argv);