qodewk 0.9.2 → 0.10.1

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 +894 -273
  2. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -1157,8 +1157,8 @@ var require_command = __commonJS({
1157
1157
  "use strict";
1158
1158
  var EventEmitter2 = require("events").EventEmitter;
1159
1159
  var childProcess = require("child_process");
1160
- var path16 = require("path");
1161
- var fs15 = require("fs");
1160
+ var path17 = require("path");
1161
+ var fs16 = require("fs");
1162
1162
  var process3 = require("process");
1163
1163
  var { Argument: Argument2, humanReadableArgName } = require_argument();
1164
1164
  var { CommanderError: CommanderError2 } = require_error();
@@ -2139,7 +2139,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
2139
2139
  * @param {string} subcommandName
2140
2140
  */
2141
2141
  _checkForMissingExecutable(executableFile, executableDir, subcommandName) {
2142
- if (fs15.existsSync(executableFile)) return;
2142
+ if (fs16.existsSync(executableFile)) return;
2143
2143
  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";
2144
2144
  const executableMissing = `'${executableFile}' does not exist
2145
2145
  - if '${subcommandName}' is not meant to be an executable command, remove description parameter from '.command()' and use '.description()' instead
@@ -2157,11 +2157,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
2157
2157
  let launchWithNode = false;
2158
2158
  const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
2159
2159
  function findFile(baseDir, baseName) {
2160
- const localBin = path16.resolve(baseDir, baseName);
2161
- if (fs15.existsSync(localBin)) return localBin;
2162
- if (sourceExt.includes(path16.extname(baseName))) return void 0;
2160
+ const localBin = path17.resolve(baseDir, baseName);
2161
+ if (fs16.existsSync(localBin)) return localBin;
2162
+ if (sourceExt.includes(path17.extname(baseName))) return void 0;
2163
2163
  const foundExt = sourceExt.find(
2164
- (ext) => fs15.existsSync(`${localBin}${ext}`)
2164
+ (ext) => fs16.existsSync(`${localBin}${ext}`)
2165
2165
  );
2166
2166
  if (foundExt) return `${localBin}${foundExt}`;
2167
2167
  return void 0;
@@ -2173,21 +2173,21 @@ Expecting one of '${allowedValues.join("', '")}'`);
2173
2173
  if (this._scriptPath) {
2174
2174
  let resolvedScriptPath;
2175
2175
  try {
2176
- resolvedScriptPath = fs15.realpathSync(this._scriptPath);
2176
+ resolvedScriptPath = fs16.realpathSync(this._scriptPath);
2177
2177
  } catch {
2178
2178
  resolvedScriptPath = this._scriptPath;
2179
2179
  }
2180
- executableDir = path16.resolve(
2181
- path16.dirname(resolvedScriptPath),
2180
+ executableDir = path17.resolve(
2181
+ path17.dirname(resolvedScriptPath),
2182
2182
  executableDir
2183
2183
  );
2184
2184
  }
2185
2185
  if (executableDir) {
2186
2186
  let localFile = findFile(executableDir, executableFile);
2187
2187
  if (!localFile && !subcommand._executableFile && this._scriptPath) {
2188
- const legacyName = path16.basename(
2188
+ const legacyName = path17.basename(
2189
2189
  this._scriptPath,
2190
- path16.extname(this._scriptPath)
2190
+ path17.extname(this._scriptPath)
2191
2191
  );
2192
2192
  if (legacyName !== this._name) {
2193
2193
  localFile = findFile(
@@ -2198,7 +2198,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
2198
2198
  }
2199
2199
  executableFile = localFile || executableFile;
2200
2200
  }
2201
- launchWithNode = sourceExt.includes(path16.extname(executableFile));
2201
+ launchWithNode = sourceExt.includes(path17.extname(executableFile));
2202
2202
  let proc;
2203
2203
  if (process3.platform !== "win32") {
2204
2204
  if (launchWithNode) {
@@ -3045,7 +3045,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
3045
3045
  * @return {Command}
3046
3046
  */
3047
3047
  nameFromFilename(filename) {
3048
- this._name = path16.basename(filename, path16.extname(filename));
3048
+ this._name = path17.basename(filename, path17.extname(filename));
3049
3049
  return this;
3050
3050
  }
3051
3051
  /**
@@ -3059,9 +3059,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
3059
3059
  * @param {string} [path]
3060
3060
  * @return {(string|null|Command)}
3061
3061
  */
3062
- executableDir(path17) {
3063
- if (path17 === void 0) return this._executableDir;
3064
- this._executableDir = path17;
3062
+ executableDir(path18) {
3063
+ if (path18 === void 0) return this._executableDir;
3064
+ this._executableDir = path18;
3065
3065
  return this;
3066
3066
  }
3067
3067
  /**
@@ -4207,10 +4207,10 @@ var require_src2 = __commonJS({
4207
4207
  var fs_1 = require("fs");
4208
4208
  var debug_1 = __importDefault(require_src());
4209
4209
  var log = debug_1.default("@kwsites/file-exists");
4210
- function check(path16, isFile, isDirectory) {
4211
- log(`checking %s`, path16);
4210
+ function check(path17, isFile, isDirectory) {
4211
+ log(`checking %s`, path17);
4212
4212
  try {
4213
- const stat = fs_1.statSync(path16);
4213
+ const stat = fs_1.statSync(path17);
4214
4214
  if (stat.isFile() && isFile) {
4215
4215
  log(`[OK] path represents a file`);
4216
4216
  return true;
@@ -4230,8 +4230,8 @@ var require_src2 = __commonJS({
4230
4230
  throw e;
4231
4231
  }
4232
4232
  }
4233
- function exists2(path16, type = exports2.READABLE) {
4234
- return check(path16, (type & exports2.FILE) > 0, (type & exports2.FOLDER) > 0);
4233
+ function exists2(path17, type = exports2.READABLE) {
4234
+ return check(path17, (type & exports2.FILE) > 0, (type & exports2.FOLDER) > 0);
4235
4235
  }
4236
4236
  exports2.exists = exists2;
4237
4237
  exports2.FILE = 1;
@@ -4842,8 +4842,8 @@ function toLinesWithContent(input = "", trimmed2 = true, separator = "\n") {
4842
4842
  function forEachLineWithContent(input, callback) {
4843
4843
  return toLinesWithContent(input, true).map((line) => callback(line));
4844
4844
  }
4845
- function folderExists(path16) {
4846
- return (0, import_file_exists.exists)(path16, import_file_exists.FOLDER);
4845
+ function folderExists(path17) {
4846
+ return (0, import_file_exists.exists)(path17, import_file_exists.FOLDER);
4847
4847
  }
4848
4848
  function append(target, item) {
4849
4849
  if (Array.isArray(target)) {
@@ -5034,8 +5034,8 @@ function checkIsRepoRootTask() {
5034
5034
  commands,
5035
5035
  format: "utf-8",
5036
5036
  onError,
5037
- parser(path16) {
5038
- return /^\.(git)?$/.test(path16.trim());
5037
+ parser(path17) {
5038
+ return /^\.(git)?$/.test(path17.trim());
5039
5039
  }
5040
5040
  };
5041
5041
  }
@@ -5291,11 +5291,11 @@ function parseGrep(grep) {
5291
5291
  const paths = /* @__PURE__ */ new Set();
5292
5292
  const results = {};
5293
5293
  forEachLineWithContent(grep, (input) => {
5294
- const [path16, line, preview] = input.split(NULL);
5295
- paths.add(path16);
5296
- (results[path16] = results[path16] || []).push({
5294
+ const [path17, line, preview] = input.split(NULL);
5295
+ paths.add(path17);
5296
+ (results[path17] = results[path17] || []).push({
5297
5297
  line: asNumber(line),
5298
- path: path16,
5298
+ path: path17,
5299
5299
  preview
5300
5300
  });
5301
5301
  });
@@ -5592,14 +5592,14 @@ function hashObjectTask(filePath, write) {
5592
5592
  }
5593
5593
  return straightThroughStringTask(commands, true);
5594
5594
  }
5595
- function parseInit(bare, path16, text) {
5595
+ function parseInit(bare, path17, text) {
5596
5596
  const response = String(text).trim();
5597
5597
  let result;
5598
5598
  if (result = initResponseRegex.exec(response)) {
5599
- return new InitSummary(bare, path16, false, result[1]);
5599
+ return new InitSummary(bare, path17, false, result[1]);
5600
5600
  }
5601
5601
  if (result = reInitResponseRegex.exec(response)) {
5602
- return new InitSummary(bare, path16, true, result[1]);
5602
+ return new InitSummary(bare, path17, true, result[1]);
5603
5603
  }
5604
5604
  let gitDir = "";
5605
5605
  const tokens = response.split(" ");
@@ -5610,12 +5610,12 @@ function parseInit(bare, path16, text) {
5610
5610
  break;
5611
5611
  }
5612
5612
  }
5613
- return new InitSummary(bare, path16, /^re/i.test(response), gitDir);
5613
+ return new InitSummary(bare, path17, /^re/i.test(response), gitDir);
5614
5614
  }
5615
5615
  function hasBareCommand(command) {
5616
5616
  return command.includes(bareCommand);
5617
5617
  }
5618
- function initTask(bare = false, path16, customArgs) {
5618
+ function initTask(bare = false, path17, customArgs) {
5619
5619
  const commands = ["init", ...customArgs];
5620
5620
  if (bare && !hasBareCommand(commands)) {
5621
5621
  commands.splice(1, 0, bareCommand);
@@ -5624,7 +5624,7 @@ function initTask(bare = false, path16, customArgs) {
5624
5624
  commands,
5625
5625
  format: "utf-8",
5626
5626
  parser(text) {
5627
- return parseInit(commands.includes("--bare"), path16, text);
5627
+ return parseInit(commands.includes("--bare"), path17, text);
5628
5628
  }
5629
5629
  };
5630
5630
  }
@@ -5903,14 +5903,14 @@ function splitLine(result, lineStr) {
5903
5903
  default:
5904
5904
  return;
5905
5905
  }
5906
- function data(index, workingDir, path16) {
5906
+ function data(index, workingDir, path17) {
5907
5907
  const raw = `${index}${workingDir}`;
5908
5908
  const handler = parsers6.get(raw);
5909
5909
  if (handler) {
5910
- handler(result, path16);
5910
+ handler(result, path17);
5911
5911
  }
5912
5912
  if (raw !== "##" && raw !== "!!") {
5913
- result.files.push(new FileStatusSummary(path16, index, workingDir));
5913
+ result.files.push(new FileStatusSummary(path17, index, workingDir));
5914
5914
  }
5915
5915
  }
5916
5916
  }
@@ -6097,8 +6097,8 @@ function deleteBranchTask(branch, forceDelete = false) {
6097
6097
  return task;
6098
6098
  }
6099
6099
  function toPath(input) {
6100
- const path16 = input.trim().replace(/^["']|["']$/g, "");
6101
- return path16 && (0, import_node_path.normalize)(path16);
6100
+ const path17 = input.trim().replace(/^["']|["']$/g, "");
6101
+ return path17 && (0, import_node_path.normalize)(path17);
6102
6102
  }
6103
6103
  function checkIgnoreTask(paths) {
6104
6104
  return {
@@ -6237,8 +6237,8 @@ function stashListTask(opt = {}, customArgs) {
6237
6237
  parser: parser4
6238
6238
  };
6239
6239
  }
6240
- function addSubModuleTask(repo, path16) {
6241
- return subModuleTask(["add", repo, path16]);
6240
+ function addSubModuleTask(repo, path17) {
6241
+ return subModuleTask(["add", repo, path17]);
6242
6242
  }
6243
6243
  function initSubModuleTask(customArgs) {
6244
6244
  return subModuleTask(["init", ...customArgs]);
@@ -6646,12 +6646,12 @@ function gitP(...args) {
6646
6646
  );
6647
6647
  }
6648
6648
  return chain.then(function() {
6649
- return new Promise(function(resolve5, reject) {
6649
+ return new Promise(function(resolve6, reject) {
6650
6650
  const callback = (err, result) => {
6651
6651
  if (err) {
6652
6652
  return reject(toError(err));
6653
6653
  }
6654
- resolve5(result);
6654
+ resolve6(result);
6655
6655
  };
6656
6656
  args2.push(callback);
6657
6657
  git2[fn].apply(git2, args2);
@@ -7570,9 +7570,9 @@ var init_esm = __esm({
7570
7570
  "src/lib/responses/InitSummary.ts"() {
7571
7571
  "use strict";
7572
7572
  InitSummary = class {
7573
- constructor(bare, path16, existing, gitDir) {
7573
+ constructor(bare, path17, existing, gitDir) {
7574
7574
  this.bare = bare;
7575
- this.path = path16;
7575
+ this.path = path17;
7576
7576
  this.existing = existing;
7577
7577
  this.gitDir = gitDir;
7578
7578
  }
@@ -8105,12 +8105,12 @@ var init_esm = __esm({
8105
8105
  "use strict";
8106
8106
  fromPathRegex = /^(.+)\0(.+)$/;
8107
8107
  FileStatusSummary = class {
8108
- constructor(path16, index, working_dir) {
8109
- this.path = path16;
8108
+ constructor(path17, index, working_dir) {
8109
+ this.path = path17;
8110
8110
  this.index = index;
8111
8111
  this.working_dir = working_dir;
8112
8112
  if (index === "R" || working_dir === "R") {
8113
- const detail = fromPathRegex.exec(path16) || [null, path16, path16];
8113
+ const detail = fromPathRegex.exec(path17) || [null, path17, path17];
8114
8114
  this.from = detail[2] || "";
8115
8115
  this.path = detail[1] || "";
8116
8116
  }
@@ -8372,9 +8372,9 @@ var init_esm = __esm({
8372
8372
  next
8373
8373
  );
8374
8374
  }
8375
- hashObject(path16, write) {
8375
+ hashObject(path17, write) {
8376
8376
  return this._runTask(
8377
- hashObjectTask(path16, write === true),
8377
+ hashObjectTask(path17, write === true),
8378
8378
  trailingFunctionArgument(arguments)
8379
8379
  );
8380
8380
  }
@@ -9011,8 +9011,8 @@ var init_esm = __esm({
9011
9011
  }
9012
9012
  return this._runTask(straightThroughStringTask2(command, this._trimmed), next);
9013
9013
  };
9014
- Git2.prototype.submoduleAdd = function(repo, path16, then) {
9015
- return this._runTask(addSubModuleTask2(repo, path16), trailingFunctionArgument2(arguments));
9014
+ Git2.prototype.submoduleAdd = function(repo, path17, then) {
9015
+ return this._runTask(addSubModuleTask2(repo, path17), trailingFunctionArgument2(arguments));
9016
9016
  };
9017
9017
  Git2.prototype.submoduleUpdate = function(args, then) {
9018
9018
  return this._runTask(
@@ -9318,8 +9318,8 @@ var init_esm = __esm({
9318
9318
  });
9319
9319
 
9320
9320
  // src/index.ts
9321
- var fs14 = __toESM(require("fs"), 1);
9322
- var path15 = __toESM(require("path"), 1);
9321
+ var fs15 = __toESM(require("fs"), 1);
9322
+ var path16 = __toESM(require("path"), 1);
9323
9323
 
9324
9324
  // ../../node_modules/.pnpm/commander@13.1.0/node_modules/commander/esm.mjs
9325
9325
  var import_index = __toESM(require_commander(), 1);
@@ -9339,7 +9339,7 @@ var {
9339
9339
  } = import_index.default;
9340
9340
 
9341
9341
  // src/index.ts
9342
- var import_picocolors3 = __toESM(require_picocolors(), 1);
9342
+ var import_picocolors4 = __toESM(require_picocolors(), 1);
9343
9343
 
9344
9344
  // ../core/dist/index.js
9345
9345
  var crypto2 = __toESM(require("crypto"), 1);
@@ -9822,8 +9822,8 @@ function getErrorMap() {
9822
9822
 
9823
9823
  // ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/parseUtil.js
9824
9824
  var makeIssue = (params) => {
9825
- const { data, path: path16, errorMaps, issueData } = params;
9826
- const fullPath = [...path16, ...issueData.path || []];
9825
+ const { data, path: path17, errorMaps, issueData } = params;
9826
+ const fullPath = [...path17, ...issueData.path || []];
9827
9827
  const fullIssue = {
9828
9828
  ...issueData,
9829
9829
  path: fullPath
@@ -9939,11 +9939,11 @@ var errorUtil;
9939
9939
 
9940
9940
  // ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/types.js
9941
9941
  var ParseInputLazyPath = class {
9942
- constructor(parent, value, path16, key) {
9942
+ constructor(parent, value, path17, key) {
9943
9943
  this._cachedPath = [];
9944
9944
  this.parent = parent;
9945
9945
  this.data = value;
9946
- this._path = path16;
9946
+ this._path = path17;
9947
9947
  this._key = key;
9948
9948
  }
9949
9949
  get path() {
@@ -14352,6 +14352,28 @@ function harvestTrailerFootprints(repoPath, commitMessage, headSha, commitDate)
14352
14352
  }
14353
14353
  return footprints;
14354
14354
  }
14355
+ function discoverAntigravityEnvironment() {
14356
+ const isAgent = process.env.ANTIGRAVITY_AGENT === "1" || Boolean(process.env.ANTIGRAVITY_CONVERSATION_ID);
14357
+ const homedir12 = os2.homedir();
14358
+ const antigravityDir = process.env.ANTIGRAVITY_APP_DATA_DIR || path2.join(homedir12, ".gemini", "antigravity");
14359
+ if (isAgent) {
14360
+ return {
14361
+ provider: "antigravity",
14362
+ model: "claude-sonnet-4-6-thinking",
14363
+ confidence: 0.85,
14364
+ mode: "observed"
14365
+ };
14366
+ }
14367
+ if (fs.existsSync(antigravityDir)) {
14368
+ return {
14369
+ provider: "antigravity",
14370
+ model: "claude-sonnet-4-6-thinking",
14371
+ confidence: 0.65,
14372
+ mode: "estimated"
14373
+ };
14374
+ }
14375
+ return null;
14376
+ }
14355
14377
 
14356
14378
  // ../core/dist/harvester/antigravity.js
14357
14379
  var fs2 = __toESM(require("fs"), 1);
@@ -14396,7 +14418,8 @@ function scoreFootprint(fp, gitCtx) {
14396
14418
  if (found)
14397
14419
  matches++;
14398
14420
  }
14399
- const ratio = matches / normChanged.length;
14421
+ const maxPossibleMatches = Math.min(normChanged.length, normEdited.length);
14422
+ const ratio = maxPossibleMatches > 0 ? matches / maxPossibleMatches : 0;
14400
14423
  if (matches > 0) {
14401
14424
  score += ratio * 0.4;
14402
14425
  } else {
@@ -14496,8 +14519,8 @@ function harvestAntigravityFootprints(repoPath, sinceDate) {
14496
14519
  const DatabaseSync2 = getSqliteDatabase();
14497
14520
  if (!DatabaseSync2)
14498
14521
  return footprints;
14499
- const homedir11 = os3.homedir();
14500
- const antigravityDir = process.env.ANTIGRAVITY_APP_DATA_DIR || path3.join(homedir11, ".gemini", "antigravity");
14522
+ const homedir12 = os3.homedir();
14523
+ const antigravityDir = process.env.ANTIGRAVITY_APP_DATA_DIR || path3.join(homedir12, ".gemini", "antigravity");
14501
14524
  const dbPath = path3.join(antigravityDir, "conversation_summaries.db");
14502
14525
  if (!fs2.existsSync(dbPath)) {
14503
14526
  return footprints;
@@ -14599,8 +14622,8 @@ var path4 = __toESM(require("path"), 1);
14599
14622
  var os4 = __toESM(require("os"), 1);
14600
14623
  function harvestClaudeFootprints(repoPath, projectAlias, sinceDate) {
14601
14624
  const footprints = [];
14602
- const homedir11 = os4.homedir();
14603
- const claudeProjectsDir = path4.join(homedir11, ".claude", "projects");
14625
+ const homedir12 = os4.homedir();
14626
+ const claudeProjectsDir = path4.join(homedir12, ".claude", "projects");
14604
14627
  if (!fs3.existsSync(claudeProjectsDir)) {
14605
14628
  return footprints;
14606
14629
  }
@@ -15175,9 +15198,9 @@ function resolveClineStorageDirs(customDir) {
15175
15198
  if (process.env.ROO_STORAGE_DIR && fs7.existsSync(process.env.ROO_STORAGE_DIR)) {
15176
15199
  dirs.push(process.env.ROO_STORAGE_DIR);
15177
15200
  }
15178
- const homedir11 = os7.homedir();
15201
+ const homedir12 = os7.homedir();
15179
15202
  if (process.platform === "win32") {
15180
- const appData = process.env.APPDATA || path8.join(homedir11, "AppData", "Roaming");
15203
+ const appData = process.env.APPDATA || path8.join(homedir12, "AppData", "Roaming");
15181
15204
  const ideRoots = ["Code", "Code - Insiders", "Cursor", "Windsurf"];
15182
15205
  const extensionIds = [
15183
15206
  "saoudrizwan.claude-dev",
@@ -15191,7 +15214,7 @@ function resolveClineStorageDirs(customDir) {
15191
15214
  }
15192
15215
  }
15193
15216
  } else if (process.platform === "darwin") {
15194
- const appSupport = path8.join(homedir11, "Library", "Application Support");
15217
+ const appSupport = path8.join(homedir12, "Library", "Application Support");
15195
15218
  const ideRoots = ["Code", "Code - Insiders", "Cursor", "Windsurf"];
15196
15219
  const extensionIds = [
15197
15220
  "saoudrizwan.claude-dev",
@@ -15205,7 +15228,7 @@ function resolveClineStorageDirs(customDir) {
15205
15228
  }
15206
15229
  }
15207
15230
  } else {
15208
- const configRoot = process.env.XDG_CONFIG_HOME || path8.join(homedir11, ".config");
15231
+ const configRoot = process.env.XDG_CONFIG_HOME || path8.join(homedir12, ".config");
15209
15232
  const ideRoots = ["Code", "Code - Insiders", "Cursor", "Windsurf"];
15210
15233
  const extensionIds = [
15211
15234
  "saoudrizwan.claude-dev",
@@ -15777,7 +15800,8 @@ function parseSinceOption2(since) {
15777
15800
  return void 0;
15778
15801
  }
15779
15802
  async function harvestUniversalFootprints(options) {
15780
- const sinceDate = parseSinceOption2(options.since);
15803
+ const parsedSince = parseSinceOption2(options.since);
15804
+ const sinceDate = parsedSince ? new Date(parsedSince.getTime() - 24 * 60 * 60 * 1e3) : void 0;
15781
15805
  const alias = options.projectAlias || "";
15782
15806
  let allFootprints = [];
15783
15807
  if (!options.platform || options.platform === "all" || options.platform === "antigravity") {
@@ -16063,6 +16087,289 @@ function formatMarkdownReceipt(receipt, publicUrl) {
16063
16087
  return lines.join("\n");
16064
16088
  }
16065
16089
 
16090
+ // ../core/dist/doctor.js
16091
+ var fs12 = __toESM(require("fs"), 1);
16092
+ var path13 = __toESM(require("path"), 1);
16093
+ var os12 = __toESM(require("os"), 1);
16094
+ var import_node_child_process = require("child_process");
16095
+ var HOOK_MARKER_BEGIN = "# --- BEGIN QODEWK HOOK ---";
16096
+ function auditAgentEnvironments(repoPath = process.cwd()) {
16097
+ const homedir12 = os12.homedir();
16098
+ const results = [];
16099
+ const agEnv = process.env.ANTIGRAVITY_APP_DATA_DIR;
16100
+ const agHome = path13.join(homedir12, ".gemini", "antigravity");
16101
+ const agPath = agEnv && fs12.existsSync(agEnv) ? agEnv : fs12.existsSync(agHome) ? agHome : void 0;
16102
+ results.push({
16103
+ id: "antigravity",
16104
+ name: "Google Antigravity",
16105
+ status: agPath ? "active" : "not_detected",
16106
+ path: agPath,
16107
+ details: agPath ? "Antigravity brain telemetry detected" : "No active session brain found"
16108
+ });
16109
+ const claudeProjects = path13.join(homedir12, ".claude", "projects");
16110
+ const claudeExists = fs12.existsSync(claudeProjects);
16111
+ results.push({
16112
+ id: "claude",
16113
+ name: "Claude Code CLI",
16114
+ status: claudeExists ? "active" : "not_detected",
16115
+ path: claudeExists ? claudeProjects : void 0,
16116
+ details: claudeExists ? "Claude project session directory found" : "No ~/.claude/projects directory found"
16117
+ });
16118
+ let cursorPath;
16119
+ if (process.platform === "win32") {
16120
+ const c3 = path13.join(process.env.APPDATA || path13.join(homedir12, "AppData", "Roaming"), "Cursor", "User", "workspaceStorage");
16121
+ if (fs12.existsSync(c3))
16122
+ cursorPath = c3;
16123
+ } else if (process.platform === "darwin") {
16124
+ const c3 = path13.join(homedir12, "Library", "Application Support", "Cursor", "User", "workspaceStorage");
16125
+ if (fs12.existsSync(c3))
16126
+ cursorPath = c3;
16127
+ } else {
16128
+ const c3 = path13.join(homedir12, ".config", "Cursor", "User", "workspaceStorage");
16129
+ if (fs12.existsSync(c3))
16130
+ cursorPath = c3;
16131
+ }
16132
+ results.push({
16133
+ id: "cursor",
16134
+ name: "Cursor IDE",
16135
+ status: cursorPath ? "active" : "not_detected",
16136
+ path: cursorPath,
16137
+ details: cursorPath ? "Cursor workspace storage detected" : "Cursor IDE storage not detected"
16138
+ });
16139
+ let windsurfPath;
16140
+ if (process.platform === "win32") {
16141
+ const w = path13.join(process.env.APPDATA || path13.join(homedir12, "AppData", "Roaming"), "Windsurf", "User", "workspaceStorage");
16142
+ if (fs12.existsSync(w))
16143
+ windsurfPath = w;
16144
+ } else if (process.platform === "darwin") {
16145
+ const w = path13.join(homedir12, "Library", "Application Support", "Windsurf", "User", "workspaceStorage");
16146
+ if (fs12.existsSync(w))
16147
+ windsurfPath = w;
16148
+ } else {
16149
+ const w = path13.join(homedir12, ".config", "Windsurf", "User", "workspaceStorage");
16150
+ if (fs12.existsSync(w))
16151
+ windsurfPath = w;
16152
+ }
16153
+ results.push({
16154
+ id: "windsurf",
16155
+ name: "Windsurf IDE",
16156
+ status: windsurfPath ? "active" : "not_detected",
16157
+ path: windsurfPath,
16158
+ details: windsurfPath ? "Cascade session workspace storage detected" : "Windsurf IDE storage not detected"
16159
+ });
16160
+ let clinePath;
16161
+ const localCline = path13.join(repoPath, ".cline");
16162
+ const localRoo = path13.join(repoPath, ".roo");
16163
+ if (fs12.existsSync(localCline)) {
16164
+ clinePath = localCline;
16165
+ } else if (fs12.existsSync(localRoo)) {
16166
+ clinePath = localRoo;
16167
+ } else {
16168
+ const appData = process.platform === "win32" ? process.env.APPDATA || path13.join(homedir12, "AppData", "Roaming") : process.platform === "darwin" ? path13.join(homedir12, "Library", "Application Support") : path13.join(homedir12, ".config");
16169
+ const clineExt = path13.join(appData, "Code", "User", "globalStorage", "saoudrizwan.claude-dev", "tasks");
16170
+ const rooExt = path13.join(appData, "Code", "User", "globalStorage", "rooveterinaryinc.roo-cline", "tasks");
16171
+ if (fs12.existsSync(clineExt))
16172
+ clinePath = clineExt;
16173
+ else if (fs12.existsSync(rooExt))
16174
+ clinePath = rooExt;
16175
+ }
16176
+ results.push({
16177
+ id: "cline",
16178
+ name: "Cline / Roo Code",
16179
+ status: clinePath ? "active" : "not_detected",
16180
+ path: clinePath,
16181
+ details: clinePath ? "VS Code autonomous agent task storage detected" : "No active Cline or Roo Code tasks found"
16182
+ });
16183
+ const aiderPath = path13.join(repoPath, ".aider.chat.history.md");
16184
+ const aiderExists = fs12.existsSync(aiderPath);
16185
+ results.push({
16186
+ id: "aider",
16187
+ name: "Aider CLI",
16188
+ status: aiderExists ? "active" : "not_detected",
16189
+ path: aiderExists ? aiderPath : void 0,
16190
+ details: aiderExists ? "Repository pairing transcript found" : "No repo-local .aider.chat.history.md found"
16191
+ });
16192
+ const localOpenCode = path13.join(repoPath, ".opencode");
16193
+ const homeOpenCode = path13.join(homedir12, ".opencode", "sessions");
16194
+ const opencodePath = fs12.existsSync(localOpenCode) ? localOpenCode : fs12.existsSync(homeOpenCode) ? homeOpenCode : void 0;
16195
+ results.push({
16196
+ id: "opencode",
16197
+ name: "OpenCode CLI",
16198
+ status: opencodePath ? "active" : "not_detected",
16199
+ path: opencodePath,
16200
+ details: opencodePath ? "OpenCode session history detected" : "No OpenCode session files found"
16201
+ });
16202
+ return results;
16203
+ }
16204
+ function resolveHooksDir(cwd = process.cwd()) {
16205
+ const gitPath = path13.join(cwd, ".git");
16206
+ if (!fs12.existsSync(gitPath))
16207
+ return null;
16208
+ try {
16209
+ const stat = fs12.statSync(gitPath);
16210
+ if (stat.isDirectory()) {
16211
+ return path13.join(gitPath, "hooks");
16212
+ }
16213
+ const content = fs12.readFileSync(gitPath, "utf-8");
16214
+ const match = content.match(/gitdir:\s*(.+)/i);
16215
+ if (!match?.[1])
16216
+ return null;
16217
+ let gitDir = match[1].trim();
16218
+ if (!path13.isAbsolute(gitDir)) {
16219
+ gitDir = path13.resolve(cwd, gitDir);
16220
+ }
16221
+ const commonFile = path13.join(gitDir, "commondir");
16222
+ if (fs12.existsSync(commonFile)) {
16223
+ let common = fs12.readFileSync(commonFile, "utf-8").trim();
16224
+ if (!path13.isAbsolute(common))
16225
+ common = path13.resolve(gitDir, common);
16226
+ return path13.join(common, "hooks");
16227
+ } else if (path13.basename(path13.dirname(gitDir)) === "worktrees") {
16228
+ return path13.join(path13.dirname(path13.dirname(gitDir)), "hooks");
16229
+ }
16230
+ return path13.join(gitDir, "hooks");
16231
+ } catch {
16232
+ return null;
16233
+ }
16234
+ }
16235
+ function auditGitHooks(repoPath = process.cwd()) {
16236
+ const hooksDir = resolveHooksDir(repoPath);
16237
+ if (!hooksDir || !fs12.existsSync(hooksDir)) {
16238
+ return {
16239
+ isGitRepo: Boolean(hooksDir),
16240
+ hooksDir,
16241
+ postCommitInstalled: false,
16242
+ postRewriteInstalled: false,
16243
+ markerFound: false
16244
+ };
16245
+ }
16246
+ const postCommit = path13.join(hooksDir, "post-commit");
16247
+ const postRewrite = path13.join(hooksDir, "post-rewrite");
16248
+ const hasMarker = (filePath) => {
16249
+ if (!fs12.existsSync(filePath))
16250
+ return false;
16251
+ try {
16252
+ return fs12.readFileSync(filePath, "utf-8").includes(HOOK_MARKER_BEGIN);
16253
+ } catch {
16254
+ return false;
16255
+ }
16256
+ };
16257
+ const commitOk = hasMarker(postCommit);
16258
+ const rewriteOk = hasMarker(postRewrite);
16259
+ return {
16260
+ isGitRepo: true,
16261
+ hooksDir,
16262
+ postCommitInstalled: commitOk,
16263
+ postRewriteInstalled: rewriteOk,
16264
+ markerFound: commitOk || rewriteOk
16265
+ };
16266
+ }
16267
+ async function benchmarkHookLatency(trials = 3) {
16268
+ const samples = [];
16269
+ for (let i2 = 0; i2 < trials; i2++) {
16270
+ const start = process.hrtime.bigint();
16271
+ try {
16272
+ const child = (0, import_node_child_process.spawn)(process.execPath, ["-e", "process.exit(0)"], {
16273
+ detached: true,
16274
+ stdio: "ignore",
16275
+ windowsHide: true
16276
+ });
16277
+ child.unref();
16278
+ const end = process.hrtime.bigint();
16279
+ const diffMs = Number(end - start) / 1e6;
16280
+ samples.push(diffMs);
16281
+ } catch {
16282
+ samples.push(99);
16283
+ }
16284
+ }
16285
+ const minMs = samples.length > 0 ? Math.min(...samples) : 99;
16286
+ const roundedMs = Number(minMs.toFixed(2));
16287
+ if (roundedMs < 5) {
16288
+ return {
16289
+ executionMs: roundedMs,
16290
+ passedInvariant: true,
16291
+ status: "pass",
16292
+ message: `Detached in ${roundedMs} ms (passed < 5ms invariant)`
16293
+ };
16294
+ } else if (roundedMs < 45) {
16295
+ return {
16296
+ executionMs: roundedMs,
16297
+ passedInvariant: true,
16298
+ status: "warn",
16299
+ message: `Detached in ${roundedMs} ms (acceptable cold spawn, target is < 5ms)`
16300
+ };
16301
+ } else {
16302
+ return {
16303
+ executionMs: roundedMs,
16304
+ passedInvariant: false,
16305
+ status: "fail",
16306
+ message: `Detached in ${roundedMs} ms (exceeded non-blocking threshold)`
16307
+ };
16308
+ }
16309
+ }
16310
+ function auditStorageHealth() {
16311
+ const dbPath = path13.join(os12.homedir(), ".qodewk", "state.db");
16312
+ try {
16313
+ const db = new LocalStateDB();
16314
+ const stats = db.getStats();
16315
+ db.close();
16316
+ let sizeBytes = 0;
16317
+ if (fs12.existsSync(dbPath)) {
16318
+ sizeBytes = fs12.statSync(dbPath).size;
16319
+ }
16320
+ return {
16321
+ healthy: true,
16322
+ dbPath,
16323
+ receiptsCount: stats.totalReceipts,
16324
+ footprintsCount: stats.totalFootprints,
16325
+ sizeBytes
16326
+ };
16327
+ } catch {
16328
+ return {
16329
+ healthy: false,
16330
+ dbPath,
16331
+ receiptsCount: 0,
16332
+ footprintsCount: 0
16333
+ };
16334
+ }
16335
+ }
16336
+ function auditPricingSync() {
16337
+ const modelsCount = Object.keys(RATE_CARDS).length;
16338
+ const cachedJson = path13.join(os12.homedir(), ".qodewk", "pricing.json");
16339
+ const hasCached = fs12.existsSync(cachedJson);
16340
+ return {
16341
+ modelsLoaded: modelsCount,
16342
+ cachedRegistryPresent: hasCached,
16343
+ status: modelsCount > 0 ? "active" : "offline"
16344
+ };
16345
+ }
16346
+ async function runDiagnostics(options = {}) {
16347
+ const repoPath = options.repoPath || process.cwd();
16348
+ const agents = auditAgentEnvironments(repoPath);
16349
+ const hooks = auditGitHooks(repoPath);
16350
+ const benchmark = await benchmarkHookLatency();
16351
+ const storage = auditStorageHealth();
16352
+ const pricing = auditPricingSync();
16353
+ const activeAgents = agents.filter((a) => a.status === "active").length;
16354
+ let overallStatus = "healthy";
16355
+ if (!storage.healthy || !hooks.isGitRepo) {
16356
+ overallStatus = "error";
16357
+ } else if (!hooks.markerFound || activeAgents === 0) {
16358
+ overallStatus = "warning";
16359
+ }
16360
+ return {
16361
+ overallStatus,
16362
+ version: "0.10.1",
16363
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
16364
+ repoPath,
16365
+ agents,
16366
+ hooks,
16367
+ benchmark,
16368
+ storage,
16369
+ pricing
16370
+ };
16371
+ }
16372
+
16066
16373
  // ../core/dist/index.js
16067
16374
  function sanitizeReceiptForShare(receipt) {
16068
16375
  const sessions = receipt.ai.sessions?.map((s) => ({
@@ -16194,7 +16501,7 @@ async function generateReceipt(options = {}) {
16194
16501
  }
16195
16502
 
16196
16503
  // src/receipt-view.ts
16197
- var fs12 = __toESM(require("fs"), 1);
16504
+ var fs13 = __toESM(require("fs"), 1);
16198
16505
  var import_picocolors = __toESM(require_picocolors(), 1);
16199
16506
 
16200
16507
  // src/barcode.ts
@@ -16608,7 +16915,7 @@ async function outputReceipt(receipt, options, publicUrl) {
16608
16915
  }
16609
16916
  if (options.out) {
16610
16917
  const fileContent = format === "terminal" ? stripAnsi(content) : content;
16611
- fs12.writeFileSync(options.out, fileContent.endsWith("\n") ? fileContent : fileContent + "\n", "utf-8");
16918
+ fs13.writeFileSync(options.out, fileContent.endsWith("\n") ? fileContent : fileContent + "\n", "utf-8");
16612
16919
  console.log(import_picocolors.default.green(`\u2713 Receipt written to ${options.out}`));
16613
16920
  }
16614
16921
  if (!options.out) {
@@ -16621,12 +16928,12 @@ async function outputReceipt(receipt, options, publicUrl) {
16621
16928
  }
16622
16929
 
16623
16930
  // src/hooks.ts
16624
- var fs13 = __toESM(require("fs"), 1);
16625
- var path13 = __toESM(require("path"), 1);
16626
- var HOOK_MARKER_BEGIN = "# --- BEGIN QODEWK HOOK ---";
16931
+ var fs14 = __toESM(require("fs"), 1);
16932
+ var path14 = __toESM(require("path"), 1);
16933
+ var HOOK_MARKER_BEGIN2 = "# --- BEGIN QODEWK HOOK ---";
16627
16934
  var HOOK_MARKER_END = "# --- END QODEWK HOOK ---";
16628
16935
  var HOOK_SNIPPET = `
16629
- ${HOOK_MARKER_BEGIN}
16936
+ ${HOOK_MARKER_BEGIN2}
16630
16937
  # Non-blocking Qodewk background recorder (< 5ms)
16631
16938
  if command -v qodewk >/dev/null 2>&1 || command -v pnpm >/dev/null 2>&1 || [ -f "./node_modules/.bin/qodewk" ]; then
16632
16939
  ( ( qodewk record || pnpm qodewk record || npx qodewk record ) >/dev/null 2>&1 & )
@@ -16634,37 +16941,37 @@ fi
16634
16941
  ${HOOK_MARKER_END}
16635
16942
  `;
16636
16943
  function resolveGitHooksDir(cwd = process.cwd()) {
16637
- const gitPath = path13.join(cwd, ".git");
16638
- if (!fs13.existsSync(gitPath)) return null;
16944
+ const gitPath = path14.join(cwd, ".git");
16945
+ if (!fs14.existsSync(gitPath)) return null;
16639
16946
  let gitCommonDir;
16640
- const stat = fs13.statSync(gitPath);
16947
+ const stat = fs14.statSync(gitPath);
16641
16948
  if (stat.isDirectory()) {
16642
16949
  gitCommonDir = gitPath;
16643
16950
  } else {
16644
- const content = fs13.readFileSync(gitPath, "utf-8");
16951
+ const content = fs14.readFileSync(gitPath, "utf-8");
16645
16952
  const match = content.match(/gitdir:\s*(.+)/i);
16646
16953
  if (!match?.[1]) return null;
16647
16954
  let gitDir = match[1].trim();
16648
- if (!path13.isAbsolute(gitDir)) {
16649
- gitDir = path13.resolve(cwd, gitDir);
16955
+ if (!path14.isAbsolute(gitDir)) {
16956
+ gitDir = path14.resolve(cwd, gitDir);
16650
16957
  }
16651
- const commonFile = path13.join(gitDir, "commondir");
16652
- if (fs13.existsSync(commonFile)) {
16653
- let common = fs13.readFileSync(commonFile, "utf-8").trim();
16654
- if (!path13.isAbsolute(common)) {
16655
- common = path13.resolve(gitDir, common);
16958
+ const commonFile = path14.join(gitDir, "commondir");
16959
+ if (fs14.existsSync(commonFile)) {
16960
+ let common = fs14.readFileSync(commonFile, "utf-8").trim();
16961
+ if (!path14.isAbsolute(common)) {
16962
+ common = path14.resolve(gitDir, common);
16656
16963
  }
16657
16964
  gitCommonDir = common;
16658
- } else if (path13.basename(path13.dirname(gitDir)) === "worktrees") {
16659
- gitCommonDir = path13.dirname(path13.dirname(gitDir));
16965
+ } else if (path14.basename(path14.dirname(gitDir)) === "worktrees") {
16966
+ gitCommonDir = path14.dirname(path14.dirname(gitDir));
16660
16967
  } else {
16661
16968
  gitCommonDir = gitDir;
16662
16969
  }
16663
16970
  }
16664
- return path13.join(gitCommonDir, "hooks");
16971
+ return path14.join(gitCommonDir, "hooks");
16665
16972
  }
16666
16973
  function checkHookStatus(hooksDir) {
16667
- if (!hooksDir || !fs13.existsSync(hooksDir)) {
16974
+ if (!hooksDir || !fs14.existsSync(hooksDir)) {
16668
16975
  return {
16669
16976
  isGit: Boolean(hooksDir),
16670
16977
  hooksDir,
@@ -16672,13 +16979,13 @@ function checkHookStatus(hooksDir) {
16672
16979
  postRewriteInstalled: false
16673
16980
  };
16674
16981
  }
16675
- const postCommit = path13.join(hooksDir, "post-commit");
16676
- const postRewrite = path13.join(hooksDir, "post-rewrite");
16982
+ const postCommit = path14.join(hooksDir, "post-commit");
16983
+ const postRewrite = path14.join(hooksDir, "post-rewrite");
16677
16984
  const hasHook = (p2) => {
16678
- if (!fs13.existsSync(p2)) return false;
16985
+ if (!fs14.existsSync(p2)) return false;
16679
16986
  try {
16680
- const content = fs13.readFileSync(p2, "utf-8");
16681
- return content.includes(HOOK_MARKER_BEGIN);
16987
+ const content = fs14.readFileSync(p2, "utf-8");
16988
+ return content.includes(HOOK_MARKER_BEGIN2);
16682
16989
  } catch {
16683
16990
  return false;
16684
16991
  }
@@ -16692,31 +16999,118 @@ function checkHookStatus(hooksDir) {
16692
16999
  }
16693
17000
  function installHookFile(hookFile) {
16694
17001
  let content = "";
16695
- if (fs13.existsSync(hookFile)) {
16696
- content = fs13.readFileSync(hookFile, "utf-8");
17002
+ if (fs14.existsSync(hookFile)) {
17003
+ content = fs14.readFileSync(hookFile, "utf-8");
16697
17004
  } else {
16698
17005
  content = "#!/bin/sh\n";
16699
17006
  }
16700
- if (content.includes(HOOK_MARKER_BEGIN)) {
17007
+ if (content.includes(HOOK_MARKER_BEGIN2)) {
16701
17008
  return "exists";
16702
17009
  }
16703
- fs13.writeFileSync(hookFile, content + HOOK_SNIPPET, { mode: 493 });
17010
+ fs14.writeFileSync(hookFile, content + HOOK_SNIPPET, { mode: 493 });
16704
17011
  return "installed";
16705
17012
  }
16706
17013
  function uninstallHookFile(hookFile) {
16707
- if (!fs13.existsSync(hookFile)) return "absent";
16708
- const content = fs13.readFileSync(hookFile, "utf-8");
16709
- if (!content.includes(HOOK_MARKER_BEGIN)) return "missing";
16710
- const regex = new RegExp(`\\n?${HOOK_MARKER_BEGIN}[\\s\\S]*?${HOOK_MARKER_END}\\n?`, "g");
17014
+ if (!fs14.existsSync(hookFile)) return "absent";
17015
+ const content = fs14.readFileSync(hookFile, "utf-8");
17016
+ if (!content.includes(HOOK_MARKER_BEGIN2)) return "missing";
17017
+ const regex = new RegExp(`\\n?${HOOK_MARKER_BEGIN2}[\\s\\S]*?${HOOK_MARKER_END}\\n?`, "g");
16711
17018
  const updated = content.replace(regex, "");
16712
- fs13.writeFileSync(hookFile, updated, { mode: 493 });
17019
+ fs14.writeFileSync(hookFile, updated, { mode: 493 });
16713
17020
  return "removed";
16714
17021
  }
16715
17022
 
16716
17023
  // src/menu.ts
16717
17024
  var readline = __toESM(require("readline"), 1);
16718
- var path14 = __toESM(require("path"), 1);
17025
+ var path15 = __toESM(require("path"), 1);
17026
+ var import_picocolors3 = __toESM(require_picocolors(), 1);
17027
+
17028
+ // src/doctor-view.ts
16719
17029
  var import_picocolors2 = __toESM(require_picocolors(), 1);
17030
+ function formatDoctorReport(report) {
17031
+ const lines = [];
17032
+ const push = (l) => lines.push(l);
17033
+ push("");
17034
+ const badge = bg.teal(import_picocolors2.default.bold(colors.ink(" qodewk doctor ")));
17035
+ push(` ${colors.mutedSoft("\u250C")} ${badge} ${import_picocolors2.default.dim(`v${report.version}`)}`);
17036
+ push(` ${colors.mutedSoft("\u2502")}`);
17037
+ push(` ${colors.teal("\u25C7")} ${import_picocolors2.default.bold(import_picocolors2.default.white("AI Agent Environment"))}`);
17038
+ push(` ${colors.mutedSoft("\u2502")}`);
17039
+ for (const agent of report.agents) {
17040
+ const isAct = agent.status === "active";
17041
+ const icon = isAct ? colors.green("\u2713") : import_picocolors2.default.dim("-");
17042
+ const namePad = agent.name.padEnd(20, ".");
17043
+ const statusText = isAct ? colors.green("Active") : import_picocolors2.default.dim("Not detected");
17044
+ const detail = agent.path ? import_picocolors2.default.dim(` (${agent.path})`) : "";
17045
+ push(` ${colors.mutedSoft("\u2502")} ${icon} ${import_picocolors2.default.white(namePad)} ${statusText}${detail}`);
17046
+ }
17047
+ push(` ${colors.mutedSoft("\u2502")}`);
17048
+ push(` ${colors.teal("\u25C7")} ${import_picocolors2.default.bold(import_picocolors2.default.white("Git Hook Telemetry"))}`);
17049
+ push(` ${colors.mutedSoft("\u2502")}`);
17050
+ const hookDirText = report.hooks.hooksDir ? import_picocolors2.default.dim(` (${report.hooks.hooksDir})`) : "";
17051
+ const pcIcon = report.hooks.postCommitInstalled ? colors.green("\u2713") : colors.red("\xD7");
17052
+ const pcStatus = report.hooks.postCommitInstalled ? colors.green("Installed") : colors.red("Missing");
17053
+ push(` ${colors.mutedSoft("\u2502")} ${pcIcon} ${import_picocolors2.default.white("post-commit hook ...")} ${pcStatus}${hookDirText}`);
17054
+ const prIcon = report.hooks.postRewriteInstalled ? colors.green("\u2713") : colors.red("\xD7");
17055
+ const prStatus = report.hooks.postRewriteInstalled ? colors.green("Installed") : colors.red("Missing");
17056
+ push(` ${colors.mutedSoft("\u2502")} ${prIcon} ${import_picocolors2.default.white("post-rewrite hook ..")} ${prStatus}`);
17057
+ const bench = report.benchmark;
17058
+ const benchIcon = bench.status === "pass" ? colors.green("\u2713") : bench.status === "warn" ? colors.amber("!") : colors.red("\xD7");
17059
+ const benchColor = bench.status === "pass" ? colors.green : bench.status === "warn" ? colors.amber : colors.red;
17060
+ push(` ${colors.mutedSoft("\u2502")} ${benchIcon} ${import_picocolors2.default.white("Hook spawn latency .")} ${benchColor(`${bench.executionMs} ms`)} ${import_picocolors2.default.dim(`(${bench.message})`)}`);
17061
+ push(` ${colors.mutedSoft("\u2502")}`);
17062
+ push(` ${colors.teal("\u25C7")} ${import_picocolors2.default.bold(import_picocolors2.default.white("Local Storage & Pricing"))}`);
17063
+ push(` ${colors.mutedSoft("\u2502")}`);
17064
+ const dbIcon = report.storage.healthy ? colors.green("\u2713") : colors.red("\xD7");
17065
+ const dbStatus = report.storage.healthy ? colors.green("Healthy") : colors.red("Failed");
17066
+ const dbDetail = report.storage.healthy ? import_picocolors2.default.dim(` (${report.storage.receiptsCount} receipts, ${report.storage.footprintsCount} footprints)`) : "";
17067
+ push(` ${colors.mutedSoft("\u2502")} ${dbIcon} ${import_picocolors2.default.white("SQLite Database ....")} ${dbStatus}${dbDetail}`);
17068
+ const prcIcon = report.pricing.status === "active" ? colors.green("\u2713") : colors.red("\xD7");
17069
+ const prcStatus = report.pricing.status === "active" ? colors.green("Active") : colors.red("Offline");
17070
+ push(` ${colors.mutedSoft("\u2502")} ${prcIcon} ${import_picocolors2.default.white("Rate Card Registry .")} ${prcStatus} ${import_picocolors2.default.dim(`(${report.pricing.modelsLoaded} model rate cards loaded)`)}`);
17071
+ push(` ${colors.mutedSoft("\u2502")}`);
17072
+ if (report.overallStatus === "healthy") {
17073
+ push(` ${colors.coral("\u25C6")} ${import_picocolors2.default.bold(colors.green("All Systems Operational \u2014 Telemetry Ready"))}`);
17074
+ } else if (report.overallStatus === "warning") {
17075
+ push(` ${colors.amber("\u25C6")} ${import_picocolors2.default.bold(colors.amber("Operational with Warnings \u2014 Run `qodewk hook install` to enable automatic recording"))}`);
17076
+ } else {
17077
+ push(` ${colors.red("\u25C6")} ${import_picocolors2.default.bold(colors.red("Diagnostic Issues Detected \u2014 Check Git repository or database status"))}`);
17078
+ }
17079
+ push(` ${colors.mutedSoft("\u2514")}`);
17080
+ push("");
17081
+ return lines.join("\n");
17082
+ }
17083
+ function formatHookTestReport(hooks, benchmark) {
17084
+ const lines = [];
17085
+ const push = (l) => lines.push(l);
17086
+ push("");
17087
+ const badge = bg.teal(import_picocolors2.default.bold(colors.ink(" qodewk hook test ")));
17088
+ push(` ${colors.mutedSoft("\u250C")} ${badge}`);
17089
+ push(` ${colors.mutedSoft("\u2502")}`);
17090
+ const pcIcon = hooks.postCommitInstalled ? colors.green("\u2713") : colors.red("\xD7");
17091
+ const pcStatus = hooks.postCommitInstalled ? colors.green("Installed") : colors.red("Missing");
17092
+ push(` ${colors.mutedSoft("\u2502")} ${pcIcon} post-commit hook: ${pcStatus}`);
17093
+ const prIcon = hooks.postRewriteInstalled ? colors.green("\u2713") : colors.red("\xD7");
17094
+ const prStatus = hooks.postRewriteInstalled ? colors.green("Installed") : colors.red("Missing");
17095
+ push(` ${colors.mutedSoft("\u2502")} ${prIcon} post-rewrite hook: ${prStatus}`);
17096
+ const benchIcon = benchmark.status === "pass" ? colors.green("\u2713") : benchmark.status === "warn" ? colors.amber("!") : colors.red("\xD7");
17097
+ const benchColor = benchmark.status === "pass" ? colors.green : benchmark.status === "warn" ? colors.amber : colors.red;
17098
+ push(` ${colors.mutedSoft("\u2502")} ${benchIcon} Detachment Latency: ${benchColor(`${benchmark.executionMs} ms`)}`);
17099
+ push(` ${colors.mutedSoft("\u2502")} ${import_picocolors2.default.dim(benchmark.message)}`);
17100
+ push(` ${colors.mutedSoft("\u2502")}`);
17101
+ if (benchmark.passedInvariant && hooks.markerFound) {
17102
+ push(` ${colors.coral("\u25C6")} ${import_picocolors2.default.bold(colors.green("Non-blocking Hook Verified (< 5ms Invariant Passed)"))}`);
17103
+ } else if (!hooks.markerFound) {
17104
+ push(` ${colors.amber("\u25C6")} ${import_picocolors2.default.bold(colors.amber("Hooks Not Installed \u2014 Run `qodewk hook install`"))}`);
17105
+ } else {
17106
+ push(` ${colors.red("\u25C6")} ${import_picocolors2.default.bold(colors.red("Latency Threshold Exceeded \u2014 Reinstall recommended"))}`);
17107
+ }
17108
+ push(` ${colors.mutedSoft("\u2514")}`);
17109
+ push("");
17110
+ return lines.join("\n");
17111
+ }
17112
+
17113
+ // src/menu.ts
16720
17114
  var SHARE_PAYLOAD_MAX_BYTES = 5e4;
16721
17115
  var BANNER_LINES = [
16722
17116
  " \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557",
@@ -16763,6 +17157,12 @@ var MENU_ITEMS = [
16763
17157
  label: "Git Notes Management",
16764
17158
  description: "Inspect & attach receipts to refs/notes/qodewk"
16765
17159
  },
17160
+ {
17161
+ id: "doctor",
17162
+ key: "7",
17163
+ label: "System Health & Diagnostics",
17164
+ description: "Audit AI agents, Git hooks, SQLite & pricing registry (doctor)"
17165
+ },
16766
17166
  {
16767
17167
  id: "exit",
16768
17168
  key: "0",
@@ -16808,7 +17208,45 @@ var RECEIPT_HORIZON_ITEMS = [
16808
17208
  description: "Return to previous screen"
16809
17209
  }
16810
17210
  ];
16811
- function buildMenuFrame(selectedIndex, items = MENU_ITEMS, title = "Telemetry Control Panel", subtitle = "\u2191\u2193 move \xB7 enter select \xB7 0-6 quick jump \xB7 q quit", options = {}) {
17211
+ var SHARE_HORIZON_ITEMS = [
17212
+ {
17213
+ id: "latest",
17214
+ key: "1",
17215
+ label: "Latest Changes (Default)",
17216
+ description: "Publish git working tree or latest commit to cloud"
17217
+ },
17218
+ {
17219
+ id: "today",
17220
+ key: "2",
17221
+ label: "Today's Work Session",
17222
+ description: "Publish commits & agent activity since midnight (--today)"
17223
+ },
17224
+ {
17225
+ id: "yesterday",
17226
+ key: "3",
17227
+ label: "Yesterday's Work",
17228
+ description: "Publish activity from yesterday to now (--since yesterday)"
17229
+ },
17230
+ {
17231
+ id: "week",
17232
+ key: "4",
17233
+ label: "Past 7 Days (Sprint)",
17234
+ description: "Publish weekly sprint telemetry across all agents (--since 7d)"
17235
+ },
17236
+ {
17237
+ id: "custom",
17238
+ key: "5",
17239
+ label: "Custom Duration",
17240
+ description: "Specify hours (e.g. 12h) or days (e.g. 3d, 14d) to publish"
17241
+ },
17242
+ {
17243
+ id: "back",
17244
+ key: "0",
17245
+ label: "Back to Main Menu",
17246
+ description: "Return to previous screen"
17247
+ }
17248
+ ];
17249
+ function buildMenuFrame(selectedIndex, items = MENU_ITEMS, title = "Telemetry Control Panel", subtitle = "\u2191\u2193 move \xB7 enter select \xB7 0-7 quick jump \xB7 q quit", options = {}) {
16812
17250
  const lines = [];
16813
17251
  const push = (line) => lines.push(line);
16814
17252
  if (options.showBanner ?? true) {
@@ -16819,21 +17257,31 @@ function buildMenuFrame(selectedIndex, items = MENU_ITEMS, title = "Telemetry Co
16819
17257
  }
16820
17258
  }
16821
17259
  push("");
16822
- const badge = bg.teal(import_picocolors2.default.bold(colors.ink(" qodewk ")));
16823
- push(` ${colors.mutedSoft("\u250C")} ${badge} ${import_picocolors2.default.dim("v0.9.2")}`);
17260
+ const badge = bg.teal(import_picocolors3.default.bold(colors.ink(" qodewk ")));
17261
+ push(` ${colors.mutedSoft("\u250C")} ${badge} ${import_picocolors3.default.dim("v0.10.1")}`);
16824
17262
  push(` ${colors.mutedSoft("\u2502")}`);
16825
17263
  if (options.actionContext) {
16826
- push(` ${colors.teal("\u25C7")} ${import_picocolors2.default.dim("Action:")} ${import_picocolors2.default.white(options.actionContext)}`);
17264
+ push(` ${colors.teal("\u25C7")} ${import_picocolors3.default.dim("Action:")} ${import_picocolors3.default.white(options.actionContext)}`);
16827
17265
  push(` ${colors.mutedSoft("\u2502")}`);
16828
17266
  } else {
16829
17267
  const repo = options.repoContext?.alias || "Qodewk";
16830
17268
  const branch = options.repoContext?.branch ? `(${options.repoContext.branch})` : "";
16831
- push(` ${colors.teal("\u25C7")} ${import_picocolors2.default.dim("Repository:")} ${import_picocolors2.default.white(repo)} ${import_picocolors2.default.dim(branch)}`);
17269
+ push(` ${colors.teal("\u25C7")} ${import_picocolors3.default.dim("Repository:")} ${import_picocolors3.default.white(repo)} ${import_picocolors3.default.dim(branch)}`);
16832
17270
  push(` ${colors.mutedSoft("\u2502")}`);
16833
- push(` ${colors.teal("\u25C7")} ${import_picocolors2.default.dim("Telemetry:")} ${import_picocolors2.default.white("Claude Warm Editorial Rate Cards")}`);
17271
+ let telemetryLabel = "Multi-Model Rate Cards (Active)";
17272
+ try {
17273
+ const agEnv = discoverAntigravityEnvironment();
17274
+ if (agEnv && agEnv.mode === "observed") {
17275
+ telemetryLabel = "Google Antigravity (Active)";
17276
+ } else if (agEnv) {
17277
+ telemetryLabel = "Google Antigravity Detected";
17278
+ }
17279
+ } catch {
17280
+ }
17281
+ push(` ${colors.teal("\u25C7")} ${import_picocolors3.default.dim("Telemetry:")} ${import_picocolors3.default.white(telemetryLabel)}`);
16834
17282
  push(` ${colors.mutedSoft("\u2502")}`);
16835
17283
  }
16836
- push(` ${colors.coral("\u25C6")} ${import_picocolors2.default.bold(import_picocolors2.default.white(title))}`);
17284
+ push(` ${colors.coral("\u25C6")} ${import_picocolors3.default.bold(import_picocolors3.default.white(title))}`);
16837
17285
  push(` ${colors.mutedSoft("\u2502")}`);
16838
17286
  const selectedItem = items[selectedIndex] || items[0];
16839
17287
  for (let i2 = 0; i2 < items.length; i2++) {
@@ -16841,44 +17289,44 @@ function buildMenuFrame(selectedIndex, items = MENU_ITEMS, title = "Telemetry Co
16841
17289
  const isSelected = i2 === selectedIndex;
16842
17290
  if (isSelected) {
16843
17291
  push(
16844
- ` ${colors.mutedSoft("\u2502")} ${import_picocolors2.default.bold(colors.coral("\u203A"))} ${import_picocolors2.default.bold(colors.coral(item.key))} ${import_picocolors2.default.bold(import_picocolors2.default.white(item.label))}`
17292
+ ` ${colors.mutedSoft("\u2502")} ${import_picocolors3.default.bold(colors.coral("\u203A"))} ${import_picocolors3.default.bold(colors.coral(item.key))} ${import_picocolors3.default.bold(import_picocolors3.default.white(item.label))}`
16845
17293
  );
16846
17294
  } else {
16847
17295
  push(
16848
- ` ${colors.mutedSoft("\u2502")} ${import_picocolors2.default.dim(item.key)} ${import_picocolors2.default.dim(import_picocolors2.default.white(item.label))}`
17296
+ ` ${colors.mutedSoft("\u2502")} ${import_picocolors3.default.dim(item.key)} ${import_picocolors3.default.dim(import_picocolors3.default.white(item.label))}`
16849
17297
  );
16850
17298
  }
16851
17299
  }
16852
17300
  const divider = colors.mutedSoft("\u2500".repeat(58));
16853
17301
  push(` ${colors.mutedSoft("\u2502")}`);
16854
17302
  push(` ${colors.mutedSoft("\u2502")} ${divider}`);
16855
- push(` ${colors.mutedSoft("\u2502")} ${import_picocolors2.default.bold(import_picocolors2.default.dim("Description"))}`);
17303
+ push(` ${colors.mutedSoft("\u2502")} ${import_picocolors3.default.bold(import_picocolors3.default.dim("Description"))}`);
16856
17304
  push(` ${colors.mutedSoft("\u2502")} ${colors.amber(selectedItem.description)}`);
16857
17305
  push(` ${colors.mutedSoft("\u2502")} ${divider}`);
16858
17306
  push(` ${colors.mutedSoft("\u2502")}`);
16859
- push(` ${colors.mutedSoft("\u2502")} ${import_picocolors2.default.dim(subtitle)}`);
16860
- push(` ${colors.mutedSoft("\u2502")} ${colors.green("[\u2713]")} ${import_picocolors2.default.dim("Source code was never uploaded to Qodewk")}`);
17307
+ push(` ${colors.mutedSoft("\u2502")} ${import_picocolors3.default.dim(subtitle)}`);
17308
+ push(` ${colors.mutedSoft("\u2502")} ${colors.green("[\u2713]")} ${import_picocolors3.default.dim("Source code was never uploaded to Qodewk")}`);
16861
17309
  push(` ${colors.mutedSoft("\u2514")}`);
16862
17310
  push("");
16863
17311
  return lines.join("\n");
16864
17312
  }
16865
17313
  function askLine(promptText) {
16866
- return new Promise((resolve5) => {
17314
+ return new Promise((resolve6) => {
16867
17315
  const rl = readline.createInterface({
16868
17316
  input: process.stdin,
16869
17317
  output: process.stdout
16870
17318
  });
16871
17319
  rl.question(promptText, (answer) => {
16872
17320
  rl.close();
16873
- resolve5(answer.trim());
17321
+ resolve6(answer.trim());
16874
17322
  });
16875
17323
  });
16876
17324
  }
16877
17325
  function waitForKeyToReturn() {
16878
- return new Promise((resolve5) => {
16879
- console.log(import_picocolors2.default.dim("\n Press any key or Enter to return to menu..."));
17326
+ return new Promise((resolve6) => {
17327
+ console.log(import_picocolors3.default.dim("\n Press any key or Enter to return to menu..."));
16880
17328
  if (!process.stdin.isTTY) {
16881
- resolve5();
17329
+ resolve6();
16882
17330
  return;
16883
17331
  }
16884
17332
  const wasRaw = process.stdin.isRaw;
@@ -16887,11 +17335,90 @@ function waitForKeyToReturn() {
16887
17335
  const onKey = () => {
16888
17336
  process.stdin.removeListener("data", onKey);
16889
17337
  if (wasRaw !== void 0) process.stdin.setRawMode(wasRaw);
16890
- resolve5();
17338
+ resolve6();
16891
17339
  };
16892
17340
  process.stdin.once("data", onKey);
16893
17341
  });
16894
17342
  }
17343
+ function waitForKeyOrAction(promptText) {
17344
+ return new Promise((resolve6) => {
17345
+ console.log(promptText);
17346
+ if (!process.stdin.isTTY) {
17347
+ resolve6("");
17348
+ return;
17349
+ }
17350
+ const wasRaw = process.stdin.isRaw;
17351
+ process.stdin.setRawMode(true);
17352
+ process.stdin.resume();
17353
+ const onKey = (data) => {
17354
+ process.stdin.removeListener("data", onKey);
17355
+ if (wasRaw !== void 0) process.stdin.setRawMode(wasRaw);
17356
+ const str = data.toString();
17357
+ resolve6(str);
17358
+ };
17359
+ process.stdin.once("data", onKey);
17360
+ });
17361
+ }
17362
+ async function publishReceiptToCloud(receipt) {
17363
+ if (process.env.QODEWK_TELEMETRY === "off") {
17364
+ console.log(import_picocolors3.default.yellow("\n Cloud publishing is disabled by QODEWK_TELEMETRY=off."));
17365
+ return null;
17366
+ }
17367
+ const sanitized = sanitizeReceiptForShare(receipt);
17368
+ let body = JSON.stringify(sanitized);
17369
+ if (Buffer.byteLength(body, "utf-8") > SHARE_PAYLOAD_MAX_BYTES && sanitized.ai.sessions) {
17370
+ const trimmed2 = {
17371
+ ...sanitized,
17372
+ ai: { ...sanitized.ai, sessions: void 0 }
17373
+ };
17374
+ body = JSON.stringify(trimmed2);
17375
+ }
17376
+ if (Buffer.byteLength(body, "utf-8") > SHARE_PAYLOAD_MAX_BYTES) {
17377
+ console.error(import_picocolors3.default.red(" Sanitized receipt still exceeds the 50 KB cloud payload limit."));
17378
+ return null;
17379
+ }
17380
+ const endpoint = resolveApiUrl();
17381
+ console.log(import_picocolors3.default.dim(`
17382
+ [\xB7] Publishing receipt to ${endpoint}...`));
17383
+ try {
17384
+ const response = await fetch(endpoint, {
17385
+ method: "POST",
17386
+ headers: {
17387
+ "Content-Type": "application/json",
17388
+ "Content-Length": String(Buffer.byteLength(body, "utf-8"))
17389
+ },
17390
+ body
17391
+ });
17392
+ if (!response.ok) {
17393
+ const errText = await response.text().catch(() => "");
17394
+ if (response.status === 503) {
17395
+ console.log(import_picocolors3.default.yellow("\n Notice: Remote server requires durable storage (Supabase or PostgreSQL) in production"));
17396
+ console.log(import_picocolors3.default.dim(" to protect receipts from serverless cold-start data loss."));
17397
+ console.log(import_picocolors3.default.dim(` Endpoint: ${endpoint}`));
17398
+ console.log(colors.green("\n [\u2713] Your receipt is safely saved locally in: ~/.qodewk/state.db"));
17399
+ console.log(import_picocolors3.default.dim(" You can also save it as a local Git note: qodewk --notes\n"));
17400
+ return null;
17401
+ }
17402
+ throw new Error(`API responded with ${response.status}: ${errText.slice(0, 100)}`);
17403
+ }
17404
+ const data = await response.json();
17405
+ const db = new LocalStateDB();
17406
+ db.saveReceipt(sanitized, data.claimToken);
17407
+ db.close();
17408
+ console.log("");
17409
+ console.log(colors.green(" [\u2713] Published successfully."));
17410
+ console.log(` ${import_picocolors3.default.dim("Public URL:")} ${import_picocolors3.default.underline(import_picocolors3.default.cyan(data.url))}`);
17411
+ console.log(` ${import_picocolors3.default.bold(colors.amber("Claim token:"))} ${colors.amber(data.claimToken)}`);
17412
+ console.log(import_picocolors3.default.dim(" (Saved to ~/.qodewk/state.db for future authorship proofs)\n"));
17413
+ await renderTerminalReceipt(sanitized, data.url);
17414
+ return data;
17415
+ } catch (err) {
17416
+ const message = err instanceof Error ? err.message : String(err);
17417
+ console.error(import_picocolors3.default.red(`
17418
+ Share failed: ${message}`));
17419
+ return null;
17420
+ }
17421
+ }
16895
17422
  var cachedRepoContext = null;
16896
17423
  async function getRepoContext() {
16897
17424
  if (cachedRepoContext) return cachedRepoContext;
@@ -16905,7 +17432,7 @@ async function getRepoContext() {
16905
17432
  }
16906
17433
  const branchSummary = await git.branch();
16907
17434
  const branch = branchSummary.current || "HEAD";
16908
- const alias = path14.basename(process.cwd());
17435
+ const alias = path15.basename(process.cwd());
16909
17436
  cachedRepoContext = { alias, branch };
16910
17437
  return cachedRepoContext;
16911
17438
  } catch {
@@ -16916,7 +17443,7 @@ async function getRepoContext() {
16916
17443
  async function runInteractiveMenu() {
16917
17444
  if (!process.stdin.isTTY) {
16918
17445
  console.log(buildMenuFrame(0));
16919
- console.log(import_picocolors2.default.yellow("Interactive menu requires a TTY terminal. Use `qodewk --help` for commands."));
17446
+ console.log(import_picocolors3.default.yellow("Interactive menu requires a TTY terminal. Use `qodewk --help` for commands."));
16920
17447
  return;
16921
17448
  }
16922
17449
  const repoContext = await getRepoContext();
@@ -16924,12 +17451,16 @@ async function runInteractiveMenu() {
16924
17451
  let selectedIndex = 0;
16925
17452
  let active = true;
16926
17453
  let lastRenderedLinesCount = 0;
16927
- const getActiveItems = () => currentScreen === "main" ? MENU_ITEMS : RECEIPT_HORIZON_ITEMS;
17454
+ const getActiveItems = () => {
17455
+ if (currentScreen === "receipt_horizon") return RECEIPT_HORIZON_ITEMS;
17456
+ if (currentScreen === "share_horizon") return SHARE_HORIZON_ITEMS;
17457
+ return MENU_ITEMS;
17458
+ };
16928
17459
  const render = () => {
16929
17460
  const items = getActiveItems();
16930
- const title = currentScreen === "main" ? "Telemetry Control Panel" : "Generate Local Receipt";
16931
- const subtitle = currentScreen === "main" ? "\u2191\u2193 move \xB7 enter select \xB7 0-6 quick jump \xB7 q quit" : "\u2191\u2193 move \xB7 enter select \xB7 0-5 quick jump \xB7 esc / 0 back";
16932
- const actionContext = currentScreen === "receipt_horizon" ? "Generate Local Receipt" : void 0;
17461
+ const title = currentScreen === "main" ? "Telemetry Control Panel" : currentScreen === "receipt_horizon" ? "Generate Local Receipt" : "Publish Receipt to Cloud";
17462
+ const subtitle = currentScreen === "main" ? "\u2191\u2193 move \xB7 enter select \xB7 0-7 quick jump \xB7 q quit" : "\u2191\u2193 move \xB7 enter select \xB7 0-5 quick jump \xB7 esc / 0 back";
17463
+ const actionContext = currentScreen === "receipt_horizon" ? "Generate Local Receipt" : currentScreen === "share_horizon" ? "Publish Receipt to Cloud" : void 0;
16933
17464
  const showBanner = currentScreen === "main";
16934
17465
  const frame = buildMenuFrame(selectedIndex, items, title, subtitle, {
16935
17466
  repoContext,
@@ -16969,25 +17500,70 @@ async function runInteractiveMenu() {
16969
17500
  since = "7d";
16970
17501
  label = "past 7 days weekly sprint (--since 7d)";
16971
17502
  } else if (item.id === "custom") {
16972
- console.log(import_picocolors2.default.bold(import_picocolors2.default.white(" Custom Telemetry Horizon")));
16973
- console.log(import_picocolors2.default.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
17503
+ console.log(import_picocolors3.default.bold(import_picocolors3.default.white(" Custom Telemetry Horizon")));
17504
+ console.log(import_picocolors3.default.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
16974
17505
  const input = await askLine(" \u203A Enter duration or date (e.g. 12h, 3d, 2026-10-01): ");
16975
17506
  if (input) {
16976
17507
  since = input;
16977
17508
  label = `custom range: ${input}`;
16978
17509
  }
16979
17510
  }
16980
- console.log(import_picocolors2.default.dim(` [\xB7] Generating local receipt for ${label}...`));
17511
+ console.log(import_picocolors3.default.dim(` [\xB7] Generating local receipt for ${label}...`));
16981
17512
  try {
16982
17513
  const receipt = await generateReceipt({ since, isPublic: false });
16983
17514
  const db = new LocalStateDB();
16984
17515
  db.saveReceipt(receipt);
16985
17516
  db.close();
16986
17517
  await renderTerminalReceipt(receipt);
17518
+ console.log("");
17519
+ const actionKey = await waitForKeyOrAction(
17520
+ ` ${colors.coral("[s]")} ${import_picocolors3.default.white("Publish this receipt to cloud")} \xB7 ${import_picocolors3.default.dim("[any other key] Return to menu")}`
17521
+ );
17522
+ if (actionKey.trim().toLowerCase() === "s") {
17523
+ await publishReceiptToCloud(receipt);
17524
+ await waitForKeyToReturn();
17525
+ }
16987
17526
  } catch (err) {
16988
17527
  const message = err instanceof Error ? err.message : String(err);
16989
- console.error(import_picocolors2.default.red(`
17528
+ console.error(import_picocolors3.default.red(`
16990
17529
  Failed to generate receipt: ${message}`));
17530
+ await waitForKeyToReturn();
17531
+ }
17532
+ currentScreen = "main";
17533
+ selectedIndex = 0;
17534
+ };
17535
+ const executeShareHorizon = async (item) => {
17536
+ cleanup();
17537
+ console.log("");
17538
+ lastRenderedLinesCount = 0;
17539
+ let since = void 0;
17540
+ let label = "latest git diff";
17541
+ if (item.id === "today") {
17542
+ since = "today";
17543
+ label = "today's work session (--today)";
17544
+ } else if (item.id === "yesterday") {
17545
+ since = "yesterday";
17546
+ label = "yesterday's work (--since yesterday)";
17547
+ } else if (item.id === "week") {
17548
+ since = "7d";
17549
+ label = "past 7 days weekly sprint (--since 7d)";
17550
+ } else if (item.id === "custom") {
17551
+ console.log(import_picocolors3.default.bold(import_picocolors3.default.white(" Custom Telemetry Horizon for Cloud Publish")));
17552
+ console.log(import_picocolors3.default.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
17553
+ const input = await askLine(" \u203A Enter duration or date (e.g. 12h, 3d, 2026-10-01): ");
17554
+ if (input) {
17555
+ since = input;
17556
+ label = `custom range: ${input}`;
17557
+ }
17558
+ }
17559
+ console.log(import_picocolors3.default.dim(` [\xB7] Generating and publishing receipt for ${label}...`));
17560
+ try {
17561
+ const receipt = await generateReceipt({ since, isPublic: false });
17562
+ await publishReceiptToCloud(receipt);
17563
+ } catch (err) {
17564
+ const message = err instanceof Error ? err.message : String(err);
17565
+ console.error(import_picocolors3.default.red(`
17566
+ Share failed: ${message}`));
16991
17567
  }
16992
17568
  await waitForKeyToReturn();
16993
17569
  currentScreen = "main";
@@ -17000,8 +17576,8 @@ async function runInteractiveMenu() {
17000
17576
  const selectedItem = MENU_ITEMS[selectedIndex];
17001
17577
  switch (selectedItem.id) {
17002
17578
  case "audit": {
17003
- console.log(import_picocolors2.default.bold(import_picocolors2.default.white("\n Audit Branch or Revision Diff")));
17004
- console.log(import_picocolors2.default.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
17579
+ console.log(import_picocolors3.default.bold(import_picocolors3.default.white("\n Audit Branch or Revision Diff")));
17580
+ console.log(import_picocolors3.default.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
17005
17581
  let defaultBase = "origin/main";
17006
17582
  try {
17007
17583
  defaultBase = await detectDefaultBaseBranch(process.cwd());
@@ -17013,7 +17589,7 @@ async function runInteractiveMenu() {
17013
17589
  const head = await askLine(
17014
17590
  ` \u203A Head git ref or commit SHA [default: HEAD]: `
17015
17591
  );
17016
- console.log(import_picocolors2.default.dim("\n [\xB7] Auditing git revision range..."));
17592
+ console.log(import_picocolors3.default.dim("\n [\xB7] Auditing git revision range..."));
17017
17593
  try {
17018
17594
  const receipt = await generateReceipt({
17019
17595
  baseSha: base || defaultBase,
@@ -17026,71 +17602,15 @@ async function runInteractiveMenu() {
17026
17602
  await renderTerminalReceipt(receipt);
17027
17603
  } catch (err) {
17028
17604
  const message = err instanceof Error ? err.message : String(err);
17029
- console.error(import_picocolors2.default.red(`
17605
+ console.error(import_picocolors3.default.red(`
17030
17606
  Audit failed: ${message}`));
17031
17607
  }
17032
17608
  await waitForKeyToReturn();
17033
17609
  break;
17034
17610
  }
17035
17611
  case "share": {
17036
- console.log(import_picocolors2.default.dim(" [\xB7] Generating and sanitizing receipt for cloud publishing..."));
17037
- try {
17038
- const receipt = await generateReceipt({ isPublic: false });
17039
- if (process.env.QODEWK_TELEMETRY === "off") {
17040
- console.log(import_picocolors2.default.yellow("\n Cloud publishing is disabled by QODEWK_TELEMETRY=off."));
17041
- await renderTerminalReceipt(receipt);
17042
- await waitForKeyToReturn();
17043
- break;
17044
- }
17045
- const sanitized = sanitizeReceiptForShare(receipt);
17046
- let body = JSON.stringify(sanitized);
17047
- if (Buffer.byteLength(body, "utf-8") > SHARE_PAYLOAD_MAX_BYTES && sanitized.ai.sessions) {
17048
- const trimmed2 = {
17049
- ...sanitized,
17050
- ai: { ...sanitized.ai, sessions: void 0 }
17051
- };
17052
- body = JSON.stringify(trimmed2);
17053
- }
17054
- const endpoint = resolveApiUrl();
17055
- console.log(import_picocolors2.default.dim(` [\xB7] Uploading metadata to ${endpoint}...`));
17056
- const response = await fetch(endpoint, {
17057
- method: "POST",
17058
- headers: {
17059
- "Content-Type": "application/json",
17060
- "Content-Length": String(Buffer.byteLength(body, "utf-8"))
17061
- },
17062
- body
17063
- });
17064
- if (!response.ok) {
17065
- const errText = await response.text().catch(() => "");
17066
- if (response.status === 503) {
17067
- console.log(import_picocolors2.default.yellow("\n Notice: Remote server requires durable storage (Supabase or PostgreSQL) in production"));
17068
- console.log(import_picocolors2.default.dim(" to protect receipts from serverless cold-start data loss."));
17069
- console.log(import_picocolors2.default.dim(` Endpoint: ${endpoint}`));
17070
- console.log(colors.green("\n [\u2713] Your receipt is safely saved locally in: ~/.qodewk/state.db"));
17071
- console.log(import_picocolors2.default.dim(" You can also save it as a local Git note: qodewk --notes\n"));
17072
- await renderTerminalReceipt(sanitized);
17073
- await waitForKeyToReturn();
17074
- break;
17075
- }
17076
- throw new Error(`API responded with ${response.status}${errText ? `: ${errText.slice(0, 100)}` : ""}`);
17077
- }
17078
- const data = await response.json();
17079
- const db = new LocalStateDB();
17080
- db.saveReceipt(sanitized, data.claimToken);
17081
- db.close();
17082
- console.log("");
17083
- console.log(colors.green(" [\u2713] Published successfully."));
17084
- console.log(` ${import_picocolors2.default.dim("Public URL:")} ${import_picocolors2.default.underline(import_picocolors2.default.cyan(data.url))}`);
17085
- console.log(` ${import_picocolors2.default.bold(colors.amber("Claim token:"))} ${colors.amber(data.claimToken)}`);
17086
- console.log(import_picocolors2.default.dim(" (Saved to ~/.qodewk/state.db for future authorship proofs)\n"));
17087
- await renderTerminalReceipt(sanitized, data.url);
17088
- } catch (err) {
17089
- const message = err instanceof Error ? err.message : String(err);
17090
- console.error(import_picocolors2.default.red(`
17091
- Share failed: ${message}`));
17092
- }
17093
- await waitForKeyToReturn();
17612
+ currentScreen = "share_horizon";
17613
+ selectedIndex = 0;
17094
17614
  break;
17095
17615
  }
17096
17616
  case "hooks": {
@@ -17105,10 +17625,14 @@ async function runInteractiveMenu() {
17105
17625
  await manageGitNotesSubmenu();
17106
17626
  break;
17107
17627
  }
17628
+ case "doctor": {
17629
+ await runDoctorDiagnosticsAction();
17630
+ break;
17631
+ }
17108
17632
  case "exit": {
17109
17633
  active = false;
17110
17634
  cleanup();
17111
- console.log(import_picocolors2.default.dim(" Exited Qodewk.\n"));
17635
+ console.log(import_picocolors3.default.dim(" Exited Qodewk.\n"));
17112
17636
  process.exit(0);
17113
17637
  return;
17114
17638
  }
@@ -17117,31 +17641,32 @@ async function runInteractiveMenu() {
17117
17641
  const manageHooksSubmenu = async () => {
17118
17642
  const hooksDir = resolveGitHooksDir();
17119
17643
  const status = checkHookStatus(hooksDir);
17120
- console.log(import_picocolors2.default.bold(import_picocolors2.default.white("\n Qodewk Git Hooks Management")));
17121
- console.log(import_picocolors2.default.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
17644
+ console.log(import_picocolors3.default.bold(import_picocolors3.default.white("\n Qodewk Git Hooks Management")));
17645
+ console.log(import_picocolors3.default.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
17122
17646
  if (!status.isGit) {
17123
- console.log(import_picocolors2.default.yellow(" Not inside a Git repository. Hooks cannot be configured."));
17647
+ console.log(import_picocolors3.default.yellow(" Not inside a Git repository. Hooks cannot be configured."));
17124
17648
  await waitForKeyToReturn();
17125
17649
  return;
17126
17650
  }
17127
- console.log(` Hooks directory: ${import_picocolors2.default.dim(status.hooksDir || "None")}`);
17651
+ console.log(` Hooks directory: ${import_picocolors3.default.dim(status.hooksDir || "None")}`);
17128
17652
  console.log(
17129
- ` post-commit: ${status.postCommitInstalled ? colors.green("[\u2713 Installed]") : import_picocolors2.default.dim("[\xB7 Not installed]")}`
17653
+ ` post-commit: ${status.postCommitInstalled ? colors.green("[\u2713 Installed]") : import_picocolors3.default.dim("[\xB7 Not installed]")}`
17130
17654
  );
17131
17655
  console.log(
17132
- ` post-rewrite: ${status.postRewriteInstalled ? colors.green("[\u2713 Installed]") : import_picocolors2.default.dim("[\xB7 Not installed]")}`
17656
+ ` post-rewrite: ${status.postRewriteInstalled ? colors.green("[\u2713 Installed]") : import_picocolors3.default.dim("[\xB7 Not installed]")}`
17133
17657
  );
17134
17658
  console.log("");
17135
17659
  console.log(" [1] Install non-blocking hooks (< 5ms background recorder)");
17136
17660
  console.log(" [2] Uninstall Qodewk hooks");
17661
+ console.log(" [3] Test & benchmark hook latency (< 5ms invariant)");
17137
17662
  console.log(" [0] Back to main menu");
17138
17663
  console.log("");
17139
- const choice = await askLine(" \u203A Select option [0-2]: ");
17664
+ const choice = await askLine(" \u203A Select option [0-3]: ");
17140
17665
  if (choice === "1") {
17141
17666
  if (status.hooksDir) {
17142
17667
  const targets = ["post-commit", "post-rewrite"];
17143
17668
  for (const t2 of targets) {
17144
- installHookFile(path14.join(status.hooksDir, t2));
17669
+ installHookFile(path15.join(status.hooksDir, t2));
17145
17670
  }
17146
17671
  console.log(colors.green("\n [\u2713] Non-blocking hooks installed successfully."));
17147
17672
  }
@@ -17149,16 +17674,20 @@ async function runInteractiveMenu() {
17149
17674
  if (status.hooksDir) {
17150
17675
  const targets = ["post-commit", "post-rewrite"];
17151
17676
  for (const t2 of targets) {
17152
- uninstallHookFile(path14.join(status.hooksDir, t2));
17677
+ uninstallHookFile(path15.join(status.hooksDir, t2));
17153
17678
  }
17154
17679
  console.log(colors.green("\n [\u2713] Qodewk hooks removed."));
17155
17680
  }
17681
+ } else if (choice === "3") {
17682
+ const hooks = auditGitHooks();
17683
+ const bench = await benchmarkHookLatency();
17684
+ console.log(formatHookTestReport(hooks, bench));
17156
17685
  }
17157
17686
  await waitForKeyToReturn();
17158
17687
  };
17159
17688
  const manageGitNotesSubmenu = async () => {
17160
- console.log(import_picocolors2.default.bold(import_picocolors2.default.white("\n Qodewk Git Notes Management (refs/notes/qodewk)")));
17161
- console.log(import_picocolors2.default.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
17689
+ console.log(import_picocolors3.default.bold(import_picocolors3.default.white("\n Qodewk Git Notes Management (refs/notes/qodewk)")));
17690
+ console.log(import_picocolors3.default.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
17162
17691
  console.log(" [1] List commits with attached Qodewk notes");
17163
17692
  console.log(" [2] Show receipt note on a commit (default: HEAD)");
17164
17693
  console.log(" [3] Attach receipt note to a Git commit (default: HEAD)");
@@ -17169,17 +17698,17 @@ async function runInteractiveMenu() {
17169
17698
  try {
17170
17699
  const shas = await listGitReceiptNotes();
17171
17700
  if (shas.length === 0) {
17172
- console.log(import_picocolors2.default.yellow("\n No Qodewk Git notes found in repository."));
17701
+ console.log(import_picocolors3.default.yellow("\n No Qodewk Git notes found in repository."));
17173
17702
  } else {
17174
- console.log(import_picocolors2.default.bold(`
17703
+ console.log(import_picocolors3.default.bold(`
17175
17704
  Found ${shas.length} commit note(s) in refs/notes/qodewk:`));
17176
17705
  for (const sha of shas) {
17177
- console.log(` ${import_picocolors2.default.cyan(sha.slice(0, 10))} ${import_picocolors2.default.dim(sha)}`);
17706
+ console.log(` ${import_picocolors3.default.cyan(sha.slice(0, 10))} ${import_picocolors3.default.dim(sha)}`);
17178
17707
  }
17179
17708
  }
17180
17709
  } catch (err) {
17181
17710
  const message = err instanceof Error ? err.message : String(err);
17182
- console.error(import_picocolors2.default.red(`
17711
+ console.error(import_picocolors3.default.red(`
17183
17712
  Error listing Git notes: ${message}`));
17184
17713
  }
17185
17714
  } else if (choice === "2") {
@@ -17187,20 +17716,20 @@ async function runInteractiveMenu() {
17187
17716
  try {
17188
17717
  const receipt = await readGitReceiptNote(commit);
17189
17718
  if (!receipt) {
17190
- console.log(import_picocolors2.default.yellow(`
17719
+ console.log(import_picocolors3.default.yellow(`
17191
17720
  No Qodewk receipt note found on commit '${commit}'.`));
17192
17721
  } else {
17193
17722
  await renderTerminalReceipt(receipt);
17194
17723
  }
17195
17724
  } catch (err) {
17196
17725
  const message = err instanceof Error ? err.message : String(err);
17197
- console.error(import_picocolors2.default.red(`
17726
+ console.error(import_picocolors3.default.red(`
17198
17727
  Error reading Git note: ${message}`));
17199
17728
  }
17200
17729
  } else if (choice === "3") {
17201
17730
  const commit = await askLine(" \u203A Commit SHA to attach note [default: HEAD]: ") || "HEAD";
17202
17731
  try {
17203
- console.log(import_picocolors2.default.dim(`
17732
+ console.log(import_picocolors3.default.dim(`
17204
17733
  [\xB7] Generating receipt for ${commit}...`));
17205
17734
  const receipt = await generateReceipt({ headSha: commit });
17206
17735
  await writeGitReceiptNote(receipt.repository.headSha, receipt);
@@ -17210,30 +17739,30 @@ async function runInteractiveMenu() {
17210
17739
  );
17211
17740
  } catch (err) {
17212
17741
  const message = err instanceof Error ? err.message : String(err);
17213
- console.error(import_picocolors2.default.red(`
17742
+ console.error(import_picocolors3.default.red(`
17214
17743
  Error writing Git note: ${message}`));
17215
17744
  }
17216
17745
  }
17217
17746
  await waitForKeyToReturn();
17218
17747
  };
17219
17748
  const showStorageStatus = async () => {
17220
- console.log(import_picocolors2.default.bold(import_picocolors2.default.white("\n Qodewk Local Database & Storage Status")));
17221
- console.log(import_picocolors2.default.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
17749
+ console.log(import_picocolors3.default.bold(import_picocolors3.default.white("\n Qodewk Local Database & Storage Status")));
17750
+ console.log(import_picocolors3.default.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
17222
17751
  try {
17223
17752
  const db = new LocalStateDB();
17224
17753
  const stats = db.getStats();
17225
17754
  const recent = db.getRecentReceipts(5);
17226
17755
  db.close();
17227
- console.log(` Database path: ${import_picocolors2.default.dim("~/.qodewk/state.db")}`);
17228
- console.log(` Total saved receipts: ${import_picocolors2.default.bold(import_picocolors2.default.white(String(stats.totalReceipts)))}`);
17229
- console.log(` Total agent footprints: ${import_picocolors2.default.bold(import_picocolors2.default.white(String(stats.totalFootprints)))}`);
17756
+ console.log(` Database path: ${import_picocolors3.default.dim("~/.qodewk/state.db")}`);
17757
+ console.log(` Total saved receipts: ${import_picocolors3.default.bold(import_picocolors3.default.white(String(stats.totalReceipts)))}`);
17758
+ console.log(` Total agent footprints: ${import_picocolors3.default.bold(import_picocolors3.default.white(String(stats.totalFootprints)))}`);
17230
17759
  console.log("");
17231
17760
  if (recent.length === 0) {
17232
- console.log(import_picocolors2.default.dim(" No stored receipts yet. Run 'Generate Local Receipt' to record your first."));
17761
+ console.log(import_picocolors3.default.dim(" No stored receipts yet. Run 'Generate Local Receipt' to record your first."));
17233
17762
  } else {
17234
- console.log(import_picocolors2.default.dim(" Recent Receipts:"));
17235
- console.log(import_picocolors2.default.dim(" " + "ID".padEnd(26) + "DATE".padEnd(12) + "BRANCH".padEnd(18) + "COST"));
17236
- console.log(import_picocolors2.default.dim(" " + "\u2500".repeat(60)));
17763
+ console.log(import_picocolors3.default.dim(" Recent Receipts:"));
17764
+ console.log(import_picocolors3.default.dim(" " + "ID".padEnd(26) + "DATE".padEnd(12) + "BRANCH".padEnd(18) + "COST"));
17765
+ console.log(import_picocolors3.default.dim(" " + "\u2500".repeat(60)));
17237
17766
  for (const item of recent) {
17238
17767
  const id = item.receipt.receipt.id.slice(0, 24).padEnd(26);
17239
17768
  const date = item.receipt.receipt.createdAt.slice(0, 10).padEnd(12);
@@ -17243,10 +17772,37 @@ async function runInteractiveMenu() {
17243
17772
  }
17244
17773
  }
17245
17774
  console.log("");
17246
- console.log(` ${colors.green("[\u2713]")} ${import_picocolors2.default.dim("Privacy guarantee: raw source code is never stored in SQLite")}`);
17775
+ console.log(` ${colors.green("[\u2713]")} ${import_picocolors3.default.dim("Privacy guarantee: raw source code is never stored in SQLite")}`);
17776
+ if (recent.length > 0) {
17777
+ console.log("");
17778
+ const ans = await askLine(" \u203A Enter receipt ID prefix to publish to cloud [Enter to return]: ");
17779
+ if (ans) {
17780
+ const matched = recent.find((r2) => r2.receipt.receipt.id.toLowerCase().startsWith(ans.toLowerCase()));
17781
+ if (matched) {
17782
+ await publishReceiptToCloud(matched.receipt);
17783
+ await waitForKeyToReturn();
17784
+ return;
17785
+ } else {
17786
+ console.log(import_picocolors3.default.yellow(` No stored receipt matching '${ans}' found.`));
17787
+ }
17788
+ }
17789
+ }
17790
+ } catch (err) {
17791
+ const message = err instanceof Error ? err.message : String(err);
17792
+ console.error(import_picocolors3.default.red(` Failed to inspect storage: ${message}`));
17793
+ }
17794
+ await waitForKeyToReturn();
17795
+ };
17796
+ const runDoctorDiagnosticsAction = async () => {
17797
+ console.log(import_picocolors3.default.bold(import_picocolors3.default.white("\n Qodewk System Health & Diagnostics (Doctor)")));
17798
+ console.log(import_picocolors3.default.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
17799
+ console.log(import_picocolors3.default.dim(" [\xB7] Running diagnostic audit across agents, hooks, storage, and pricing...\n"));
17800
+ try {
17801
+ const report = await runDiagnostics();
17802
+ console.log(formatDoctorReport(report));
17247
17803
  } catch (err) {
17248
17804
  const message = err instanceof Error ? err.message : String(err);
17249
- console.error(import_picocolors2.default.red(` Failed to inspect storage: ${message}`));
17805
+ console.error(import_picocolors3.default.red(` Diagnostic check failed: ${message}`));
17250
17806
  }
17251
17807
  await waitForKeyToReturn();
17252
17808
  };
@@ -17271,7 +17827,7 @@ async function runInteractiveMenu() {
17271
17827
  return;
17272
17828
  }
17273
17829
  cleanup();
17274
- console.log(import_picocolors2.default.dim("\n Exited Qodewk.\n"));
17830
+ console.log(import_picocolors3.default.dim("\n Exited Qodewk.\n"));
17275
17831
  process.exit(0);
17276
17832
  }
17277
17833
  if (key.name === "up" || key.name === "k") {
@@ -17297,8 +17853,14 @@ async function runInteractiveMenu() {
17297
17853
  if (active) startLoop();
17298
17854
  return;
17299
17855
  }
17856
+ if (selected.id === "share") {
17857
+ currentScreen = "share_horizon";
17858
+ selectedIndex = 0;
17859
+ if (active) startLoop();
17860
+ return;
17861
+ }
17300
17862
  await executeSelectedAction();
17301
- } else {
17863
+ } else if (currentScreen === "receipt_horizon") {
17302
17864
  if (selected.id === "back") {
17303
17865
  currentScreen = "main";
17304
17866
  selectedIndex = 0;
@@ -17306,6 +17868,14 @@ async function runInteractiveMenu() {
17306
17868
  return;
17307
17869
  }
17308
17870
  await executeReceiptHorizon(selected);
17871
+ } else if (currentScreen === "share_horizon") {
17872
+ if (selected.id === "back") {
17873
+ currentScreen = "main";
17874
+ selectedIndex = 0;
17875
+ if (active) startLoop();
17876
+ return;
17877
+ }
17878
+ await executeShareHorizon(selected);
17309
17879
  }
17310
17880
  if (active) {
17311
17881
  startLoop();
@@ -17322,8 +17892,14 @@ async function runInteractiveMenu() {
17322
17892
  if (active) startLoop();
17323
17893
  return;
17324
17894
  }
17895
+ if (selected.id === "share") {
17896
+ currentScreen = "share_horizon";
17897
+ selectedIndex = 0;
17898
+ if (active) startLoop();
17899
+ return;
17900
+ }
17325
17901
  await executeSelectedAction();
17326
- } else {
17902
+ } else if (currentScreen === "receipt_horizon") {
17327
17903
  if (selected.id === "back") {
17328
17904
  currentScreen = "main";
17329
17905
  selectedIndex = 0;
@@ -17331,6 +17907,14 @@ async function runInteractiveMenu() {
17331
17907
  return;
17332
17908
  }
17333
17909
  await executeReceiptHorizon(selected);
17910
+ } else if (currentScreen === "share_horizon") {
17911
+ if (selected.id === "back") {
17912
+ currentScreen = "main";
17913
+ selectedIndex = 0;
17914
+ if (active) startLoop();
17915
+ return;
17916
+ }
17917
+ await executeShareHorizon(selected);
17334
17918
  }
17335
17919
  if (active) {
17336
17920
  startLoop();
@@ -17348,19 +17932,19 @@ loadQodewkEnv();
17348
17932
  var SHARE_PAYLOAD_MAX_BYTES2 = 5e4;
17349
17933
  function resolveCliVersion() {
17350
17934
  try {
17351
- const argv1 = process.argv[1] ? path15.dirname(path15.resolve(process.argv[1])) : process.cwd();
17935
+ const argv1 = process.argv[1] ? path16.dirname(path16.resolve(process.argv[1])) : process.cwd();
17352
17936
  const candidates = [
17353
- path15.join(argv1, "..", "package.json"),
17354
- path15.join(argv1, "package.json")
17937
+ path16.join(argv1, "..", "package.json"),
17938
+ path16.join(argv1, "package.json")
17355
17939
  ];
17356
17940
  for (const candidate of candidates) {
17357
- if (!fs14.existsSync(candidate)) continue;
17358
- const pkg = JSON.parse(fs14.readFileSync(candidate, "utf-8"));
17941
+ if (!fs15.existsSync(candidate)) continue;
17942
+ const pkg = JSON.parse(fs15.readFileSync(candidate, "utf-8"));
17359
17943
  if (pkg.name === "qodewk" && pkg.version) return pkg.version;
17360
17944
  }
17361
17945
  } catch {
17362
17946
  }
17363
- return "0.9.2";
17947
+ return "0.10.1";
17364
17948
  }
17365
17949
  function resolveSince(options) {
17366
17950
  return options.today ? "today" : options.since;
@@ -17398,21 +17982,39 @@ program2.name("qodewk").description("Universal telemetry and digital receipt gen
17398
17982
  if (options.notes) {
17399
17983
  try {
17400
17984
  await writeGitReceiptNote(receipt.repository.headSha, receipt);
17401
- console.log(import_picocolors3.default.green(`\u2713 Attached receipt note to ${receipt.repository.headSha.slice(0, 7)} (refs/notes/qodewk)`));
17985
+ console.log(import_picocolors4.default.green(`\u2713 Attached receipt note to ${receipt.repository.headSha.slice(0, 7)} (refs/notes/qodewk)`));
17402
17986
  } catch (noteErr) {
17403
- console.warn(import_picocolors3.default.yellow(`Warning: Could not write Git note: ${noteErr.message}`));
17987
+ console.warn(import_picocolors4.default.yellow(`Warning: Could not write Git note: ${noteErr.message}`));
17404
17988
  }
17405
17989
  }
17406
17990
  await outputReceipt(receipt, options);
17407
17991
  } catch (err) {
17408
17992
  const message = err instanceof Error ? err.message : String(err);
17409
- console.error(import_picocolors3.default.red(`Error generating receipt: ${message}`));
17993
+ console.error(import_picocolors4.default.red(`Error generating receipt: ${message}`));
17410
17994
  process.exit(1);
17411
17995
  }
17412
17996
  });
17413
17997
  program2.command("menu").description("Launch interactive terminal control panel").action(async () => {
17414
17998
  await runInteractiveMenu();
17415
17999
  });
18000
+ program2.command("doctor").description("Run complete diagnostic health check of AI agents, git hooks, and local telemetry").option("-j, --json", "Output diagnostic report in machine-readable JSON format").action(async (options) => {
18001
+ try {
18002
+ const isJson = Boolean(options.json || program2.opts().json);
18003
+ const report = await runDiagnostics();
18004
+ if (isJson) {
18005
+ console.log(JSON.stringify(report, null, 2));
18006
+ } else {
18007
+ console.log(formatDoctorReport(report));
18008
+ }
18009
+ if (report.overallStatus === "error") {
18010
+ process.exit(1);
18011
+ }
18012
+ } catch (err) {
18013
+ const message = err instanceof Error ? err.message : String(err);
18014
+ console.error(import_picocolors4.default.red(`Error running diagnostics: ${message}`));
18015
+ process.exit(1);
18016
+ }
18017
+ });
17416
18018
  program2.command("audit").description("Audit PR or branch diff against a target base commit").option("-b, --base <base>", "Base git ref or commit SHA (e.g. origin/main)").option("-H, --head <head>", "Head git ref or commit SHA").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").option("-m, --model <model>", "Specify AI model").option("-s, --since <duration>", "Harvest agent footprints since duration").option("--today", "Harvest agent footprints for today").option("--platform <platform>", "Filter agent platform").option("--anon", "Anonymize branch name in output").option("--barcode", "Render classic 1D thermal barcode (always on; kept for compatibility)").action(async (options) => {
17417
18019
  try {
17418
18020
  const receipt = await generateReceipt({
@@ -17429,7 +18031,7 @@ program2.command("audit").description("Audit PR or branch diff against a target
17429
18031
  await outputReceipt(receipt, options);
17430
18032
  } catch (err) {
17431
18033
  const message = err instanceof Error ? err.message : String(err);
17432
- console.error(import_picocolors3.default.red(`Error auditing git range: ${message}`));
18034
+ console.error(import_picocolors4.default.red(`Error auditing git range: ${message}`));
17433
18035
  process.exit(1);
17434
18036
  }
17435
18037
  });
@@ -17446,12 +18048,12 @@ program2.command("share").description("Publish privacy-safe receipt to Qodewk cl
17446
18048
  const localFlag = program2.opts().local === true;
17447
18049
  if (process.env.QODEWK_TELEMETRY === "off" || localFlag) {
17448
18050
  console.log(
17449
- import_picocolors3.default.yellow(
18051
+ import_picocolors4.default.yellow(
17450
18052
  `
17451
18053
  Cloud publishing is disabled (${localFlag ? "--local" : "QODEWK_TELEMETRY=off"}).`
17452
18054
  )
17453
18055
  );
17454
- console.log(import_picocolors3.default.dim("Telemetry remains strictly stored in local SQLite (~/.qodewk/state.db).\n"));
18056
+ console.log(import_picocolors4.default.dim("Telemetry remains strictly stored in local SQLite (~/.qodewk/state.db).\n"));
17455
18057
  persistReceipt(receipt);
17456
18058
  await renderTerminalReceipt(receipt);
17457
18059
  return;
@@ -17466,11 +18068,11 @@ Cloud publishing is disabled (${localFlag ? "--local" : "QODEWK_TELEMETRY=off"})
17466
18068
  body = JSON.stringify(trimmed2);
17467
18069
  }
17468
18070
  if (Buffer.byteLength(body, "utf-8") > SHARE_PAYLOAD_MAX_BYTES2) {
17469
- console.error(import_picocolors3.default.red("Sanitized receipt still exceeds the 50 KB cloud payload limit."));
18071
+ console.error(import_picocolors4.default.red("Sanitized receipt still exceeds the 50 KB cloud payload limit."));
17470
18072
  process.exit(1);
17471
18073
  }
17472
18074
  const endpoint = resolveApiUrl();
17473
- console.log(import_picocolors3.default.dim(`Publishing receipt ${sanitized.receipt.id} to ${endpoint}...`));
18075
+ console.log(import_picocolors4.default.dim(`Publishing receipt ${sanitized.receipt.id} to ${endpoint}...`));
17474
18076
  try {
17475
18077
  const response = await fetch(endpoint, {
17476
18078
  method: "POST",
@@ -17487,11 +18089,11 @@ Cloud publishing is disabled (${localFlag ? "--local" : "QODEWK_TELEMETRY=off"})
17487
18089
  const data = await response.json();
17488
18090
  persistReceipt(sanitized, data.claimToken);
17489
18091
  console.log("");
17490
- console.log(import_picocolors3.default.green(" Published successfully."));
17491
- console.log(` ${import_picocolors3.default.dim("Public URL:")} ${import_picocolors3.default.underline(import_picocolors3.default.cyan(data.url))}`);
17492
- console.log(` ${import_picocolors3.default.bold(import_picocolors3.default.yellow(" Claim token (store securely \u2014 shown once):"))}`);
17493
- console.log(` ${import_picocolors3.default.yellow(data.claimToken)}`);
17494
- console.log(import_picocolors3.default.dim(" Saved to ~/.qodewk/state.db \u2014 required to prove authorship later."));
18092
+ console.log(import_picocolors4.default.green(" Published successfully."));
18093
+ console.log(` ${import_picocolors4.default.dim("Public URL:")} ${import_picocolors4.default.underline(import_picocolors4.default.cyan(data.url))}`);
18094
+ console.log(` ${import_picocolors4.default.bold(import_picocolors4.default.yellow(" Claim token (store securely \u2014 shown once):"))}`);
18095
+ console.log(` ${import_picocolors4.default.yellow(data.claimToken)}`);
18096
+ console.log(import_picocolors4.default.dim(" Saved to ~/.qodewk/state.db \u2014 required to prove authorship later."));
17495
18097
  console.log("");
17496
18098
  await renderTerminalReceipt(sanitized, data.url);
17497
18099
  if (options.json) {
@@ -17499,14 +18101,14 @@ Cloud publishing is disabled (${localFlag ? "--local" : "QODEWK_TELEMETRY=off"})
17499
18101
  }
17500
18102
  } catch (networkErr) {
17501
18103
  const message = networkErr instanceof Error ? networkErr.message : String(networkErr);
17502
- console.log(import_picocolors3.default.yellow(`
18104
+ console.log(import_picocolors4.default.yellow(`
17503
18105
  Could not reach cloud API (${message}). Rendered locally:`));
17504
18106
  persistReceipt(receipt);
17505
18107
  await renderTerminalReceipt(receipt);
17506
18108
  }
17507
18109
  } catch (err) {
17508
18110
  const message = err instanceof Error ? err.message : String(err);
17509
- console.error(import_picocolors3.default.red(`Error sharing receipt: ${message}`));
18111
+ console.error(import_picocolors4.default.red(`Error sharing receipt: ${message}`));
17510
18112
  process.exit(1);
17511
18113
  }
17512
18114
  });
@@ -17524,35 +18126,35 @@ hookCommand.command("install").description("Install non-blocking post-commit and
17524
18126
  try {
17525
18127
  const hooksDir = resolveGitHooksDir();
17526
18128
  if (!hooksDir) {
17527
- console.error(import_picocolors3.default.red("Error: Current directory is not a Git repository (.git not found)."));
18129
+ console.error(import_picocolors4.default.red("Error: Current directory is not a Git repository (.git not found)."));
17528
18130
  process.exit(1);
17529
18131
  }
17530
- if (!fs14.existsSync(hooksDir)) {
17531
- fs14.mkdirSync(hooksDir, { recursive: true });
18132
+ if (!fs15.existsSync(hooksDir)) {
18133
+ fs15.mkdirSync(hooksDir, { recursive: true });
17532
18134
  }
17533
18135
  const targets = ["post-commit", "post-rewrite"];
17534
18136
  let installed = 0;
17535
18137
  let existing = 0;
17536
18138
  for (const name of targets) {
17537
- const result = installHookFile(path15.join(hooksDir, name));
18139
+ const result = installHookFile(path16.join(hooksDir, name));
17538
18140
  if (result === "installed") installed++;
17539
18141
  else existing++;
17540
18142
  }
17541
18143
  if (installed === 0 && existing > 0) {
17542
- console.log(import_picocolors3.default.yellow("Qodewk Git hooks are already installed (post-commit, post-rewrite)."));
18144
+ console.log(import_picocolors4.default.yellow("Qodewk Git hooks are already installed (post-commit, post-rewrite)."));
17543
18145
  return;
17544
18146
  }
17545
18147
  console.log(
17546
- import_picocolors3.default.green(
18148
+ import_picocolors4.default.green(
17547
18149
  `\u2713 Non-blocking Qodewk hooks installed in ${hooksDir} (post-commit${installed > 1 || existing > 0 ? ", post-rewrite" : installed === 1 && targets.length === 2 ? " + post-rewrite" : ""})`
17548
18150
  )
17549
18151
  );
17550
18152
  if (existing > 0) {
17551
- console.log(import_picocolors3.default.dim(` (${existing} hook file(s) already contained the Qodewk marker)`));
18153
+ console.log(import_picocolors4.default.dim(` (${existing} hook file(s) already contained the Qodewk marker)`));
17552
18154
  }
17553
18155
  } catch (err) {
17554
18156
  const message = err instanceof Error ? err.message : String(err);
17555
- console.error(import_picocolors3.default.red(`Failed to install Git hook: ${message}`));
18157
+ console.error(import_picocolors4.default.red(`Failed to install Git hook: ${message}`));
17556
18158
  process.exit(1);
17557
18159
  }
17558
18160
  });
@@ -17560,23 +18162,42 @@ hookCommand.command("uninstall").description("Remove Qodewk post-commit and post
17560
18162
  try {
17561
18163
  const hooksDir = resolveGitHooksDir();
17562
18164
  if (!hooksDir) {
17563
- console.log(import_picocolors3.default.yellow("No Git repository found."));
18165
+ console.log(import_picocolors4.default.yellow("No Git repository found."));
17564
18166
  return;
17565
18167
  }
17566
18168
  const targets = ["post-commit", "post-rewrite"];
17567
18169
  let removed = 0;
17568
18170
  for (const name of targets) {
17569
- const result = uninstallHookFile(path15.join(hooksDir, name));
18171
+ const result = uninstallHookFile(path16.join(hooksDir, name));
17570
18172
  if (result === "removed") removed++;
17571
18173
  }
17572
18174
  if (removed === 0) {
17573
- console.log(import_picocolors3.default.yellow("Qodewk hooks are not installed."));
18175
+ console.log(import_picocolors4.default.yellow("Qodewk hooks are not installed."));
17574
18176
  return;
17575
18177
  }
17576
- console.log(import_picocolors3.default.green(`\u2713 Qodewk hook markers removed from ${removed} file(s).`));
18178
+ console.log(import_picocolors4.default.green(`\u2713 Qodewk hook markers removed from ${removed} file(s).`));
18179
+ } catch (err) {
18180
+ const message = err instanceof Error ? err.message : String(err);
18181
+ console.error(import_picocolors4.default.red(`Failed to uninstall hook: ${message}`));
18182
+ process.exit(1);
18183
+ }
18184
+ });
18185
+ hookCommand.command("test").aliases(["verify", "check"]).description("Verify non-blocking Git hook installation and benchmark latency (< 5ms invariant)").option("-j, --json", "Output benchmark in JSON format").action(async (options) => {
18186
+ try {
18187
+ const hooks = auditGitHooks();
18188
+ const benchmark = await benchmarkHookLatency();
18189
+ const isJson = Boolean(options.json || program2.opts().json);
18190
+ if (isJson) {
18191
+ console.log(JSON.stringify({ hooks, benchmark }, null, 2));
18192
+ } else {
18193
+ console.log(formatHookTestReport(hooks, benchmark));
18194
+ }
18195
+ if (!benchmark.passedInvariant) {
18196
+ process.exit(1);
18197
+ }
17577
18198
  } catch (err) {
17578
18199
  const message = err instanceof Error ? err.message : String(err);
17579
- console.error(import_picocolors3.default.red(`Failed to uninstall hook: ${message}`));
18200
+ console.error(import_picocolors4.default.red(`Error testing hooks: ${message}`));
17580
18201
  process.exit(1);
17581
18202
  }
17582
18203
  });
@@ -17585,7 +18206,7 @@ notesCmd.command("show [commit]").description("Display Qodewk receipt attached t
17585
18206
  try {
17586
18207
  const receipt = await readGitReceiptNote(commit);
17587
18208
  if (!receipt) {
17588
- console.log(import_picocolors3.default.yellow(`No Qodewk receipt note found on commit '${commit}'.`));
18209
+ console.log(import_picocolors4.default.yellow(`No Qodewk receipt note found on commit '${commit}'.`));
17589
18210
  return;
17590
18211
  }
17591
18212
  if (opts.json) {
@@ -17595,7 +18216,7 @@ notesCmd.command("show [commit]").description("Display Qodewk receipt attached t
17595
18216
  }
17596
18217
  } catch (err) {
17597
18218
  const message = err instanceof Error ? err.message : String(err);
17598
- console.error(import_picocolors3.default.red(`Error reading Git note: ${message}`));
18219
+ console.error(import_picocolors4.default.red(`Error reading Git note: ${message}`));
17599
18220
  process.exit(1);
17600
18221
  }
17601
18222
  });
@@ -17604,11 +18225,11 @@ notesCmd.command("write [commit]").description("Generate and attach receipt to a
17604
18225
  const receipt = await generateReceipt({ headSha: commit });
17605
18226
  await writeGitReceiptNote(receipt.repository.headSha, receipt);
17606
18227
  console.log(
17607
- import_picocolors3.default.green(`\u2713 Attached receipt note to ${receipt.repository.headSha.slice(0, 7)} (refs/notes/qodewk)`)
18228
+ import_picocolors4.default.green(`\u2713 Attached receipt note to ${receipt.repository.headSha.slice(0, 7)} (refs/notes/qodewk)`)
17608
18229
  );
17609
18230
  } catch (err) {
17610
18231
  const message = err instanceof Error ? err.message : String(err);
17611
- console.error(import_picocolors3.default.red(`Error writing Git note: ${message}`));
18232
+ console.error(import_picocolors4.default.red(`Error writing Git note: ${message}`));
17612
18233
  process.exit(1);
17613
18234
  }
17614
18235
  });
@@ -17616,16 +18237,16 @@ notesCmd.command("list").description("List all commits with attached Qodewk rece
17616
18237
  try {
17617
18238
  const shas = await listGitReceiptNotes();
17618
18239
  if (shas.length === 0) {
17619
- console.log(import_picocolors3.default.yellow("No Qodewk Git notes found in repository."));
18240
+ console.log(import_picocolors4.default.yellow("No Qodewk Git notes found in repository."));
17620
18241
  return;
17621
18242
  }
17622
- console.log(import_picocolors3.default.bold(`Found ${shas.length} commit note(s) in refs/notes/qodewk:`));
18243
+ console.log(import_picocolors4.default.bold(`Found ${shas.length} commit note(s) in refs/notes/qodewk:`));
17623
18244
  for (const sha of shas) {
17624
- console.log(` ${import_picocolors3.default.cyan(sha.slice(0, 10))} ${import_picocolors3.default.dim(sha)}`);
18245
+ console.log(` ${import_picocolors4.default.cyan(sha.slice(0, 10))} ${import_picocolors4.default.dim(sha)}`);
17625
18246
  }
17626
18247
  } catch (err) {
17627
18248
  const message = err instanceof Error ? err.message : String(err);
17628
- console.error(import_picocolors3.default.red(`Error listing Git notes: ${message}`));
18249
+ console.error(import_picocolors4.default.red(`Error listing Git notes: ${message}`));
17629
18250
  process.exit(1);
17630
18251
  }
17631
18252
  });