modelstat 0.0.26 → 0.0.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.mjs CHANGED
@@ -21132,7 +21132,7 @@ var require_snapshot_recorder = __commonJS({
21132
21132
  "../../node_modules/.pnpm/undici@7.25.0/node_modules/undici/lib/mock/snapshot-recorder.js"(exports, module) {
21133
21133
  "use strict";
21134
21134
  var { writeFile, readFile, mkdir: mkdir2 } = __require("fs/promises");
21135
- var { dirname: dirname8, resolve: resolve6 } = __require("path");
21135
+ var { dirname: dirname9, resolve: resolve6 } = __require("path");
21136
21136
  var { setTimeout: setTimeout2, clearTimeout: clearTimeout2 } = __require("timers");
21137
21137
  var { InvalidArgumentError, UndiciError } = require_errors();
21138
21138
  var { hashId, isUrlExcludedFactory, normalizeHeaders, createHeaderFilters } = require_snapshot_utils();
@@ -21363,7 +21363,7 @@ var require_snapshot_recorder = __commonJS({
21363
21363
  throw new InvalidArgumentError("Snapshot path is required");
21364
21364
  }
21365
21365
  const resolvedPath = resolve6(path5);
21366
- await mkdir2(dirname8(resolvedPath), { recursive: true });
21366
+ await mkdir2(dirname9(resolvedPath), { recursive: true });
21367
21367
  const data = Array.from(this.#snapshots.entries()).map(([hash, snapshot]) => ({
21368
21368
  hash,
21369
21369
  snapshot
@@ -43981,7 +43981,8 @@ var init_config2 = __esm({
43981
43981
  claimUrl: null,
43982
43982
  userEmail: null,
43983
43983
  defaultOrgId: null,
43984
- cursor: {}
43984
+ cursor: {},
43985
+ processingVersion: null
43985
43986
  }
43986
43987
  });
43987
43988
  cachedIdentity = (() => {
@@ -44073,6 +44074,19 @@ var init_config2 = __esm({
44073
44074
  map[path5] = v;
44074
44075
  store.set("cursor", map);
44075
44076
  },
44077
+ /** Drop every per-file cursor so the next scan re-reads every
44078
+ * JSONL from byte 0. Called when the local processing-pipeline
44079
+ * version bumps (see processing-version.ts) or via the
44080
+ * `modelstat rescan` CLI command. */
44081
+ wipeCursors() {
44082
+ store.set("cursor", {});
44083
+ },
44084
+ get processingVersion() {
44085
+ return store.get("processingVersion");
44086
+ },
44087
+ setProcessingVersion(v) {
44088
+ store.set("processingVersion", v);
44089
+ },
44076
44090
  get storePath() {
44077
44091
  return store.path;
44078
44092
  }
@@ -45220,6 +45234,29 @@ var init_lock = __esm({
45220
45234
  }
45221
45235
  });
45222
45236
 
45237
+ // src/processing-version.ts
45238
+ var processing_version_exports = {};
45239
+ __export(processing_version_exports, {
45240
+ PROCESSING_VERSION: () => PROCESSING_VERSION,
45241
+ reconcileProcessingVersion: () => reconcileProcessingVersion
45242
+ });
45243
+ function reconcileProcessingVersion(state2) {
45244
+ const stored = state2.processingVersion ?? 1;
45245
+ if (stored >= PROCESSING_VERSION) {
45246
+ return { changed: false, from: stored, to: PROCESSING_VERSION };
45247
+ }
45248
+ state2.wipeCursors();
45249
+ state2.setProcessingVersion(PROCESSING_VERSION);
45250
+ return { changed: true, from: stored, to: PROCESSING_VERSION };
45251
+ }
45252
+ var PROCESSING_VERSION;
45253
+ var init_processing_version = __esm({
45254
+ "src/processing-version.ts"() {
45255
+ "use strict";
45256
+ PROCESSING_VERSION = 2;
45257
+ }
45258
+ });
45259
+
45223
45260
  // ../../node_modules/.pnpm/readdirp@4.1.2/node_modules/readdirp/esm/index.js
45224
45261
  import { stat as stat3, lstat, readdir as readdir2, realpath } from "fs/promises";
45225
45262
  import { Readable as Readable2 } from "stream";
@@ -45953,9 +45990,9 @@ var init_handler = __esm({
45953
45990
  if (this.fsw.closed) {
45954
45991
  return;
45955
45992
  }
45956
- const dirname8 = sysPath.dirname(file);
45993
+ const dirname9 = sysPath.dirname(file);
45957
45994
  const basename4 = sysPath.basename(file);
45958
- const parent = this.fsw._getWatchedDir(dirname8);
45995
+ const parent = this.fsw._getWatchedDir(dirname9);
45959
45996
  let prevStats = stats;
45960
45997
  if (parent.has(basename4))
45961
45998
  return;
@@ -45982,7 +46019,7 @@ var init_handler = __esm({
45982
46019
  prevStats = newStats2;
45983
46020
  }
45984
46021
  } catch (error) {
45985
- this.fsw._remove(dirname8, basename4);
46022
+ this.fsw._remove(dirname9, basename4);
45986
46023
  }
45987
46024
  } else if (parent.has(basename4)) {
45988
46025
  const at = newStats.atimeMs;
@@ -46947,6 +46984,9 @@ __export(daemon_exports, {
46947
46984
  setQueue: () => setQueue
46948
46985
  });
46949
46986
  import { existsSync as existsSync8, statSync as statSync2 } from "fs";
46987
+ import { readFileSync as readFileSync4 } from "fs";
46988
+ import { dirname as dirname8, join as join9 } from "path";
46989
+ import { fileURLToPath as __agentFileURLToPath } from "url";
46950
46990
  function setPhase(phase, message) {
46951
46991
  status.phase = phase;
46952
46992
  status.message = message ?? null;
@@ -47074,22 +47114,29 @@ async function runDaemon(opts = {}) {
47074
47114
  setPhase("error", `summariser preflight failed: ${err.message}`);
47075
47115
  throw err;
47076
47116
  }
47117
+ const { reconcileProcessingVersion: reconcileProcessingVersion2 } = await Promise.resolve().then(() => (init_processing_version(), processing_version_exports));
47118
+ const pv = reconcileProcessingVersion2(state);
47119
+ if (pv.changed) {
47120
+ console.log(
47121
+ `[modelstat] processing pipeline v${pv.from} \u2192 v${pv.to} \u2014 wiped file cursors so every session is re-processed by the new pipeline`
47122
+ );
47123
+ }
47077
47124
  await runDiscovery();
47078
47125
  await runScanCycle("startup");
47079
47126
  const chokidar = (await Promise.resolve().then(() => (init_esm2(), esm_exports))).default;
47080
47127
  const { homedir: homedir9, platform: platform5 } = await import("os");
47081
- const { join: join10 } = await import("path");
47128
+ const { join: join11 } = await import("path");
47082
47129
  const home2 = homedir9();
47083
47130
  const dirs = [
47084
- join10(home2, ".claude/projects"),
47085
- join10(home2, ".codex/sessions"),
47086
- join10(home2, ".cursor/ai-tracking"),
47087
- join10(home2, ".gemini"),
47131
+ join11(home2, ".claude/projects"),
47132
+ join11(home2, ".codex/sessions"),
47133
+ join11(home2, ".cursor/ai-tracking"),
47134
+ join11(home2, ".gemini"),
47088
47135
  ...platform5() === "darwin" ? [
47089
- join10(home2, "Library/Application Support/Cursor/User/workspaceStorage"),
47090
- join10(home2, "Library/Application Support/Claude")
47136
+ join11(home2, "Library/Application Support/Cursor/User/workspaceStorage"),
47137
+ join11(home2, "Library/Application Support/Claude")
47091
47138
  ] : [
47092
- join10(home2, ".config/Cursor/User/workspaceStorage")
47139
+ join11(home2, ".config/Cursor/User/workspaceStorage")
47093
47140
  ]
47094
47141
  ].filter((p) => existsSync8(p) && statSync2(p).isDirectory());
47095
47142
  setPhase("watching", `Watching ${dirs.length} directories`);
@@ -47138,7 +47185,24 @@ var init_daemon = __esm({
47138
47185
  init_config2();
47139
47186
  init_lock();
47140
47187
  init_scan();
47141
- AGENT_VERSION2 = "agent-dev-0.0.23";
47188
+ AGENT_VERSION2 = (() => {
47189
+ try {
47190
+ const here2 = dirname8(__agentFileURLToPath(import.meta.url));
47191
+ const candidates = [
47192
+ join9(here2, "..", "package.json"),
47193
+ join9(here2, "..", "..", "package.json")
47194
+ ];
47195
+ for (const c of candidates) {
47196
+ try {
47197
+ const v = JSON.parse(readFileSync4(c, "utf8")).version;
47198
+ if (typeof v === "string") return `agent-${v}`;
47199
+ } catch {
47200
+ }
47201
+ }
47202
+ } catch {
47203
+ }
47204
+ return "agent-unknown";
47205
+ })();
47142
47206
  HEARTBEAT_INTERVAL_MS = 1e4;
47143
47207
  SCAN_INTERVAL_MS = 5 * 60 * 1e3;
47144
47208
  status = {
@@ -47160,32 +47224,32 @@ __export(watch_exports, {
47160
47224
  });
47161
47225
  import { existsSync as existsSync9 } from "fs";
47162
47226
  import { homedir as homedir8, platform as platform3 } from "os";
47163
- import { join as join9 } from "path";
47227
+ import { join as join10 } from "path";
47164
47228
  function resolveWatchDirs() {
47165
47229
  const home2 = homedir8();
47166
- const xdgConfig = process.env.XDG_CONFIG_HOME ?? join9(home2, ".config");
47167
- const xdgData = process.env.XDG_DATA_HOME ?? join9(home2, ".local/share");
47230
+ const xdgConfig = process.env.XDG_CONFIG_HOME ?? join10(home2, ".config");
47231
+ const xdgData = process.env.XDG_DATA_HOME ?? join10(home2, ".local/share");
47168
47232
  const candidates = [
47169
47233
  // universal (default HOME-rooted CLI data dirs)
47170
- join9(home2, ".claude/projects"),
47171
- join9(home2, ".codex/sessions"),
47172
- join9(home2, ".cursor/ai-tracking"),
47173
- join9(home2, ".gemini"),
47174
- join9(home2, ".aider"),
47234
+ join10(home2, ".claude/projects"),
47235
+ join10(home2, ".codex/sessions"),
47236
+ join10(home2, ".cursor/ai-tracking"),
47237
+ join10(home2, ".gemini"),
47238
+ join10(home2, ".aider"),
47175
47239
  // XDG / Linux
47176
- join9(xdgConfig, "claude/projects"),
47177
- join9(xdgConfig, "codex/sessions"),
47178
- join9(xdgConfig, "Cursor/User/workspaceStorage"),
47179
- join9(xdgConfig, "Code/User/workspaceStorage"),
47180
- join9(xdgConfig, "Code - Insiders/User/workspaceStorage"),
47181
- join9(xdgData, "claude/projects"),
47240
+ join10(xdgConfig, "claude/projects"),
47241
+ join10(xdgConfig, "codex/sessions"),
47242
+ join10(xdgConfig, "Cursor/User/workspaceStorage"),
47243
+ join10(xdgConfig, "Code/User/workspaceStorage"),
47244
+ join10(xdgConfig, "Code - Insiders/User/workspaceStorage"),
47245
+ join10(xdgData, "claude/projects"),
47182
47246
  // macOS
47183
47247
  ...platform3() === "darwin" ? [
47184
- join9(home2, "Library/Application Support/Cursor/User/workspaceStorage"),
47185
- join9(home2, "Library/Application Support/Claude"),
47186
- join9(home2, "Library/Application Support/Code/User/workspaceStorage"),
47187
- join9(home2, "Library/Application Support/Windsurf/User/workspaceStorage"),
47188
- join9(home2, "Library/Application Support/Zed")
47248
+ join10(home2, "Library/Application Support/Cursor/User/workspaceStorage"),
47249
+ join10(home2, "Library/Application Support/Claude"),
47250
+ join10(home2, "Library/Application Support/Code/User/workspaceStorage"),
47251
+ join10(home2, "Library/Application Support/Windsurf/User/workspaceStorage"),
47252
+ join10(home2, "Library/Application Support/Zed")
47189
47253
  ] : []
47190
47254
  ];
47191
47255
  return Array.from(new Set(candidates)).filter((p) => existsSync9(p));
@@ -47847,12 +47911,30 @@ async function cmdScan() {
47847
47911
  const { preflightSummariser: preflightSummariser2 } = await Promise.resolve().then(() => (init_pipeline2(), pipeline_exports));
47848
47912
  const sample = await preflightSummariser2();
47849
47913
  console.log(`[modelstat] summariser preflight ok: "${sample}"`);
47914
+ const { reconcileProcessingVersion: reconcileProcessingVersion2 } = await Promise.resolve().then(() => (init_processing_version(), processing_version_exports));
47915
+ const pv = reconcileProcessingVersion2(state);
47916
+ if (pv.changed) {
47917
+ console.log(
47918
+ `[modelstat] processing pipeline v${pv.from} \u2192 v${pv.to} \u2014 wiped file cursors so every session is re-processed`
47919
+ );
47920
+ }
47850
47921
  const r = await scanAll();
47851
47922
  console.log();
47852
47923
  console.log(
47853
47924
  `Done: ${r.filesScanned} files scanned, ${r.filesUnchanged} unchanged, ${r.batchesUploaded} batches, ${r.eventsUploaded} events uploaded`
47854
47925
  );
47855
47926
  }
47927
+ async function cmdRescan() {
47928
+ const { PROCESSING_VERSION: PROCESSING_VERSION2 } = await Promise.resolve().then(() => (init_processing_version(), processing_version_exports));
47929
+ state.wipeCursors();
47930
+ state.setProcessingVersion(PROCESSING_VERSION2);
47931
+ console.log(
47932
+ `[modelstat] cursors wiped \u2014 next \`modelstat scan\` (or daemon scan cycle) will re-read every JSONL from the start and re-summarise every session at processing version v${PROCESSING_VERSION2}.`
47933
+ );
47934
+ console.log(
47935
+ " If the daemon is running, kick it with: modelstat stop && modelstat start"
47936
+ );
47937
+ }
47856
47938
  async function cmdWatch() {
47857
47939
  const { watchForever: watchForever2 } = await Promise.resolve().then(() => (init_watch(), watch_exports));
47858
47940
  await watchForever2();
@@ -48041,6 +48123,8 @@ async function main() {
48041
48123
  return cmdDiscover();
48042
48124
  case "scan":
48043
48125
  return cmdScan();
48126
+ case "rescan":
48127
+ return cmdRescan();
48044
48128
  case "watch":
48045
48129
  return cmdWatch();
48046
48130
  case "stop":
@@ -48073,6 +48157,7 @@ async function main() {
48073
48157
  console.log(" start \u2014 run the daemon (used by the installed service)");
48074
48158
  console.log(" discover \u2014 one-shot report of installs/identities");
48075
48159
  console.log(" scan \u2014 one-shot parse + upload of local JSONL");
48160
+ console.log(" rescan \u2014 wipe file cursors so next scan re-reads & re-summarises everything");
48076
48161
  console.log(" watch \u2014 continuous (chokidar) with periodic backstop");
48077
48162
  process.exit(1);
48078
48163
  }