@use-aistack/cli 0.15.0 → 0.16.0

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/index.js CHANGED
@@ -377,12 +377,12 @@ function apiEquivalentCost(modelKey, t, atMs) {
377
377
  }
378
378
 
379
379
  // src/version.ts
380
- var CLI_VERSION = true ? "0.15.0" : "0.0.0-dev";
380
+ var CLI_VERSION = true ? "0.16.0" : "0.0.0-dev";
381
381
 
382
382
  // src/api.ts
383
383
  var BASE_URL = process.env.AISTACK_URL || "https://aistack.to";
384
- async function request(path13, options = {}) {
385
- return fetch(`${BASE_URL}${path13}`, {
384
+ async function request(path14, options = {}) {
385
+ return fetch(`${BASE_URL}${path14}`, {
386
386
  ...options,
387
387
  headers: {
388
388
  "Content-Type": "application/json",
@@ -800,9 +800,9 @@ function canonicalizeRepoUrl(input) {
800
800
  function repoNameFromCanonical(canonical) {
801
801
  return parseRepo(canonical)?.repo ?? "";
802
802
  }
803
- function normalizeUpstreamPath(path13) {
804
- if (!path13) return "";
805
- return path13.split("/").filter(Boolean).join("/");
803
+ function normalizeUpstreamPath(path14) {
804
+ if (!path14) return "";
805
+ return path14.split("/").filter(Boolean).join("/");
806
806
  }
807
807
 
808
808
  // src/git.ts
@@ -848,16 +848,16 @@ function buildRepoLinkResource(canonical) {
848
848
  import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
849
849
  import { homedir as homedir2 } from "node:os";
850
850
  import { join as join2 } from "node:path";
851
- function readJson(path13) {
851
+ function readJson(path14) {
852
852
  try {
853
- if (!existsSync2(path13)) return null;
854
- return JSON.parse(readFileSync2(path13, "utf-8"));
853
+ if (!existsSync2(path14)) return null;
854
+ return JSON.parse(readFileSync2(path14, "utf-8"));
855
855
  } catch {
856
856
  return null;
857
857
  }
858
858
  }
859
- function hooksFrom(path13, source, out, seen) {
860
- const hooks = readJson(path13)?.hooks;
859
+ function hooksFrom(path14, source, out, seen) {
860
+ const hooks = readJson(path14)?.hooks;
861
861
  if (!hooks || typeof hooks !== "object") return;
862
862
  for (const [event, config] of Object.entries(hooks)) {
863
863
  const stableKey = `hooks:${source}:${event}`;
@@ -1006,16 +1006,16 @@ function buildMcpResource(name, group, pkg) {
1006
1006
  pkg
1007
1007
  };
1008
1008
  }
1009
- function readText(path13) {
1009
+ function readText(path14) {
1010
1010
  try {
1011
- if (!existsSync3(path13)) return null;
1012
- return readFileSync3(path13, "utf-8");
1011
+ if (!existsSync3(path14)) return null;
1012
+ return readFileSync3(path14, "utf-8");
1013
1013
  } catch {
1014
1014
  return null;
1015
1015
  }
1016
1016
  }
1017
- function readParsed(path13, parse2) {
1018
- const raw = readText(path13);
1017
+ function readParsed(path14, parse2) {
1018
+ const raw = readText(path14);
1019
1019
  if (raw === null) return null;
1020
1020
  try {
1021
1021
  return parse2(raw);
@@ -1023,14 +1023,14 @@ function readParsed(path13, parse2) {
1023
1023
  return null;
1024
1024
  }
1025
1025
  }
1026
- function readJson2(path13) {
1027
- return readParsed(path13, JSON.parse);
1026
+ function readJson2(path14) {
1027
+ return readParsed(path14, JSON.parse);
1028
1028
  }
1029
- function readYaml(path13) {
1030
- return readParsed(path13, parseYaml);
1029
+ function readYaml(path14) {
1030
+ return readParsed(path14, parseYaml);
1031
1031
  }
1032
- function readToml(path13) {
1033
- return readParsed(path13, parseToml);
1032
+ function readToml(path14) {
1033
+ return readParsed(path14, parseToml);
1034
1034
  }
1035
1035
  function continueListToMap(file) {
1036
1036
  if (!file?.mcpServers?.length) return void 0;
@@ -1169,8 +1169,8 @@ function resolveSource(entry, mpRepoUrl) {
1169
1169
  const src = entry.source;
1170
1170
  if (typeof src === "string") {
1171
1171
  if (!mpRepoUrl) return null;
1172
- const path13 = src.replace(/^\.\//, "").replace(/\/+$/, "");
1173
- return { url: mpRepoUrl, path: path13 || void 0 };
1172
+ const path14 = src.replace(/^\.\//, "").replace(/\/+$/, "");
1173
+ return { url: mpRepoUrl, path: path14 || void 0 };
1174
1174
  }
1175
1175
  if (src && typeof src === "object" && src.url) {
1176
1176
  return { url: src.url, path: src.path, sha: src.sha };
@@ -1207,10 +1207,10 @@ function resolvePluginLinks(installed, marketplaces, manifests) {
1207
1207
  }
1208
1208
  return out;
1209
1209
  }
1210
- function readJson3(path13) {
1210
+ function readJson3(path14) {
1211
1211
  try {
1212
- if (!existsSync4(path13)) return null;
1213
- return JSON.parse(readFileSync4(path13, "utf-8"));
1212
+ if (!existsSync4(path14)) return null;
1213
+ return JSON.parse(readFileSync4(path14, "utf-8"));
1214
1214
  } catch {
1215
1215
  return null;
1216
1216
  }
@@ -1844,7 +1844,7 @@ function diffResources(current, existing) {
1844
1844
  // src/commands/connect.ts
1845
1845
  import { spawnSync } from "node:child_process";
1846
1846
  import { cpSync, existsSync as existsSync6 } from "node:fs";
1847
- import { homedir as homedir13 } from "node:os";
1847
+ import { homedir as homedir14 } from "node:os";
1848
1848
  import { dirname as dirname3, join as join6 } from "node:path";
1849
1849
  import { fileURLToPath } from "node:url";
1850
1850
  import * as p3 from "@clack/prompts";
@@ -3634,13 +3634,13 @@ function canonicalJson(value) {
3634
3634
  function fnv1a64(text) {
3635
3635
  const prime = 0x100000001b3n;
3636
3636
  const mask = 0xffffffffffffffffn;
3637
- let hash = 0xcbf29ce484222325n;
3637
+ let hash2 = 0xcbf29ce484222325n;
3638
3638
  const bytes = new TextEncoder().encode(text);
3639
3639
  for (const byte of bytes) {
3640
- hash ^= BigInt(byte);
3641
- hash = hash * prime & mask;
3640
+ hash2 ^= BigInt(byte);
3641
+ hash2 = hash2 * prime & mask;
3642
3642
  }
3643
- return hash.toString(16).padStart(16, "0");
3643
+ return hash2.toString(16).padStart(16, "0");
3644
3644
  }
3645
3645
  function dayFingerprint(day) {
3646
3646
  return fnv1a64(
@@ -5441,11 +5441,11 @@ function ingestWithRetry(agg, file, opts) {
5441
5441
  }
5442
5442
  }
5443
5443
  function ingestFile2(agg, file, opts) {
5444
- const readFile3 = opts.readFileImpl ?? readFileSync6;
5444
+ const readFile4 = opts.readFileImpl ?? readFileSync6;
5445
5445
  let text;
5446
5446
  if (file.endsWith(".zst")) {
5447
5447
  if (zstdDecompress === null) throw readError("zstd-unsupported");
5448
- const raw = readFile3(file);
5448
+ const raw = readFile4(file);
5449
5449
  try {
5450
5450
  text = zstdDecompress(
5451
5451
  Buffer.isBuffer(raw) ? raw : Buffer.from(raw)
@@ -5454,7 +5454,7 @@ function ingestFile2(agg, file, opts) {
5454
5454
  throw readError("zstd-corrupt");
5455
5455
  }
5456
5456
  } else {
5457
- text = readFile3(file).toString("utf8");
5457
+ text = readFile4(file).toString("utf8");
5458
5458
  }
5459
5459
  const records = [];
5460
5460
  let nonEmptyLines = 0;
@@ -6610,15 +6610,129 @@ function ingestContribution(agg, row) {
6610
6610
 
6611
6611
  // src/harness/grok/scan.ts
6612
6612
  import { createReadStream as createReadStream2 } from "node:fs";
6613
- import { readdir as readdir4, readFile as readFile2, stat as stat5 } from "node:fs/promises";
6614
- import { homedir as homedir10 } from "node:os";
6615
- import path8 from "node:path";
6613
+ import { readdir as readdir5, readFile as readFile3, stat as stat5 } from "node:fs/promises";
6614
+ import { homedir as homedir11 } from "node:os";
6615
+ import path9 from "node:path";
6616
6616
  import readline2 from "node:readline";
6617
6617
  import { parse as parseToml3 } from "smol-toml";
6618
+
6619
+ // src/harness/grok/context.ts
6620
+ import { createHash as createHash2 } from "node:crypto";
6621
+ import { appendFile, mkdir as mkdir2, readdir as readdir4, readFile as readFile2, unlink } from "node:fs/promises";
6622
+ import { homedir as homedir10 } from "node:os";
6623
+ import path8 from "node:path";
6624
+ var DAY_MS = 864e5;
6625
+ var hash = (value) => createHash2("sha256").update(value).digest("hex");
6626
+ function contextCall(value) {
6627
+ const row = asObj(value);
6628
+ if (row?.src !== "shell" || row.msg !== "shell.turn.inference_done")
6629
+ return null;
6630
+ const session = asStr(row.sid);
6631
+ const ts = asStr(row.ts);
6632
+ const ctx = asObj(row.ctx);
6633
+ const tokens = ctx?.prompt_tokens;
6634
+ const loop = ctx?.loop_index;
6635
+ const tsMs = ts ? Date.parse(ts) : NaN;
6636
+ if (!session || !Number.isFinite(tsMs) || typeof tokens !== "number" || !Number.isSafeInteger(tokens) || tokens < 0 || typeof loop !== "number" || !Number.isSafeInteger(loop) || loop < 0)
6637
+ return null;
6638
+ return {
6639
+ id: hash(JSON.stringify([session, tsMs, row.pid ?? null, loop, tokens])),
6640
+ session,
6641
+ tsMs,
6642
+ tokens
6643
+ };
6644
+ }
6645
+ function contextCacheDirectory(root) {
6646
+ return path8.join(
6647
+ homedir10(),
6648
+ ".config",
6649
+ "aistack",
6650
+ "grok-context",
6651
+ hash(path8.resolve(root))
6652
+ );
6653
+ }
6654
+ async function optionalRead(file) {
6655
+ try {
6656
+ return await readFile2(file, "utf8");
6657
+ } catch (error) {
6658
+ if (error.code === "ENOENT") return "";
6659
+ throw error;
6660
+ }
6661
+ }
6662
+ async function retainedContextCalls(root, sessions, cacheDir = contextCacheDirectory(root), now = Date.now()) {
6663
+ const cutoff = now - 400 * DAY_MS;
6664
+ const calls = /* @__PURE__ */ new Map();
6665
+ let files = [];
6666
+ try {
6667
+ files = await readdir4(cacheDir);
6668
+ } catch (error) {
6669
+ if (error.code !== "ENOENT") throw error;
6670
+ }
6671
+ for (const file of files) {
6672
+ if (!/^\d{4}-\d{2}-\d{2}\.jsonl$/.test(file)) continue;
6673
+ if (Date.parse(file.slice(0, 10)) + DAY_MS < cutoff) {
6674
+ await unlink(path8.join(cacheDir, file)).catch(
6675
+ (error) => {
6676
+ if (error.code !== "ENOENT") throw error;
6677
+ }
6678
+ );
6679
+ continue;
6680
+ }
6681
+ const raw2 = await optionalRead(path8.join(cacheDir, file));
6682
+ if (raw2 && !raw2.endsWith("\n"))
6683
+ throw new Error("Incomplete Grok context cache");
6684
+ for (const line of raw2.split("\n").filter(Boolean)) {
6685
+ const call = asObj(JSON.parse(line));
6686
+ if (!call || typeof call.id !== "string" || !/^[a-f0-9]{64}$/.test(call.id) || typeof call.session !== "string" || typeof call.tsMs !== "number" || !Number.isFinite(call.tsMs) || typeof call.tokens !== "number" || !Number.isSafeInteger(call.tokens) || call.tokens < 0)
6687
+ throw new Error("Invalid Grok context cache");
6688
+ if (call.tsMs >= cutoff && call.tsMs <= now)
6689
+ calls.set(call.id, {
6690
+ id: call.id,
6691
+ session: call.session,
6692
+ tsMs: call.tsMs,
6693
+ tokens: call.tokens
6694
+ });
6695
+ }
6696
+ }
6697
+ const raw = await optionalRead(
6698
+ path8.join(root, "..", "logs", "unified.jsonl")
6699
+ );
6700
+ const additions = /* @__PURE__ */ new Map();
6701
+ for (const line of raw.slice(0, raw.lastIndexOf("\n") + 1).split("\n")) {
6702
+ let value;
6703
+ try {
6704
+ value = JSON.parse(line);
6705
+ } catch {
6706
+ continue;
6707
+ }
6708
+ const call = contextCall(value);
6709
+ if (!call || !sessions.has(call.session) || call.tsMs < cutoff || call.tsMs > now || calls.has(call.id))
6710
+ continue;
6711
+ calls.set(call.id, call);
6712
+ const date = new Date(call.tsMs).toISOString().slice(0, 10);
6713
+ const lines2 = additions.get(date) ?? [];
6714
+ lines2.push(JSON.stringify(call));
6715
+ additions.set(date, lines2);
6716
+ }
6717
+ if (additions.size) {
6718
+ await mkdir2(cacheDir, { recursive: true, mode: 448 });
6719
+ for (const [date, lines2] of additions) {
6720
+ await appendFile(
6721
+ path8.join(cacheDir, `${date}.jsonl`),
6722
+ `${lines2.join("\n")}
6723
+ `,
6724
+ { mode: 384 }
6725
+ );
6726
+ }
6727
+ }
6728
+ return [...calls.values()].filter((call) => sessions.has(call.session)).sort((a, b) => a.tsMs - b.tsMs || a.id.localeCompare(b.id));
6729
+ }
6730
+
6731
+ // src/harness/grok/scan.ts
6618
6732
  function sessionRoots() {
6619
6733
  return [
6620
- path8.join(
6621
- process.env.GROK_HOME || path8.join(homedir10(), ".grok"),
6734
+ path9.join(
6735
+ process.env.GROK_HOME || path9.join(homedir11(), ".grok"),
6622
6736
  "sessions"
6623
6737
  )
6624
6738
  ];
@@ -6650,7 +6764,7 @@ async function stableRead(file, jsonl) {
6650
6764
  if (before.size === after.size && before.mtimeMs === after.mtimeMs)
6651
6765
  return { lines: lines2, complete: true };
6652
6766
  } else {
6653
- const raw = await readFile2(file, "utf8");
6767
+ const raw = await readFile3(file, "utf8");
6654
6768
  const after = await stat5(file);
6655
6769
  if (before.size === after.size && before.mtimeMs === after.mtimeMs)
6656
6770
  return { json: JSON.parse(raw), complete: true };
@@ -6662,16 +6776,16 @@ async function stableRead(file, jsonl) {
6662
6776
  }
6663
6777
  async function directories(root) {
6664
6778
  try {
6665
- const workspaces = await readdir4(root, { withFileTypes: true });
6779
+ const workspaces = await readdir5(root, { withFileTypes: true });
6666
6780
  const out = [];
6667
6781
  for (const workspace of workspaces) {
6668
6782
  if (!workspace.isDirectory() || workspace.isSymbolicLink()) continue;
6669
- const workspacePath = path8.join(root, workspace.name);
6670
- for (const session of await readdir4(workspacePath, {
6783
+ const workspacePath = path9.join(root, workspace.name);
6784
+ for (const session of await readdir5(workspacePath, {
6671
6785
  withFileTypes: true
6672
6786
  })) {
6673
6787
  if (session.isDirectory() && !session.isSymbolicLink())
6674
- out.push(path8.join(workspacePath, session.name));
6788
+ out.push(path9.join(workspacePath, session.name));
6675
6789
  }
6676
6790
  }
6677
6791
  return out.sort();
@@ -6683,7 +6797,7 @@ async function modelAliasesForRoot(root) {
6683
6797
  if (process.env.GROK_MODELS_BASE_URL) return /* @__PURE__ */ new Map();
6684
6798
  try {
6685
6799
  const parsed = asObj(
6686
- parseToml3(await readFile2(path8.join(root, "..", "config.toml"), "utf8"))
6800
+ parseToml3(await readFile3(path9.join(root, "..", "config.toml"), "utf8"))
6687
6801
  );
6688
6802
  if (!parsed || asObj(parsed.endpoints)?.models_base_url) return /* @__PURE__ */ new Map();
6689
6803
  const models = asObj(parsed.model);
@@ -6706,6 +6820,7 @@ async function scan4(agg, opts = {}) {
6706
6820
  let complete = true;
6707
6821
  for (const root of opts.roots ?? sessionRoots()) {
6708
6822
  const aliases = await modelAliasesForRoot(root);
6823
+ const contextSessions = /* @__PURE__ */ new Map();
6709
6824
  const dirs = await directories(root);
6710
6825
  if (dirs === null) {
6711
6826
  complete = false;
@@ -6714,7 +6829,7 @@ async function scan4(agg, opts = {}) {
6714
6829
  for (const dir of dirs) {
6715
6830
  let entries;
6716
6831
  try {
6717
- entries = await readdir4(dir, { withFileTypes: true });
6832
+ entries = await readdir5(dir, { withFileTypes: true });
6718
6833
  } catch {
6719
6834
  complete = false;
6720
6835
  continue;
@@ -6722,10 +6837,10 @@ async function scan4(agg, opts = {}) {
6722
6837
  const files = new Map(
6723
6838
  entries.filter(
6724
6839
  (e) => e.isFile() && (isGrokEvidenceFile(e.name) || e.name === "summary.json")
6725
- ).map((e) => [e.name, path8.join(dir, e.name)])
6840
+ ).map((e) => [e.name, path9.join(dir, e.name)])
6726
6841
  );
6727
6842
  let projectDir = dir;
6728
- let sessionFallback = path8.basename(dir);
6843
+ let sessionFallback = path9.basename(dir);
6729
6844
  let child = false;
6730
6845
  let parentSession;
6731
6846
  const summary = files.get("summary.json");
@@ -6743,6 +6858,7 @@ async function scan4(agg, opts = {}) {
6743
6858
  parentSession = asStr(value?.parentSessionId) ?? asStr(value?.parent_session_id) ?? asStr(info?.parentSessionId) ?? asStr(info?.parent_session_id) ?? void 0;
6744
6859
  child = parentSession !== void 0;
6745
6860
  }
6861
+ contextSessions.set(sessionFallback, { projectDir, parentSession });
6746
6862
  let rows = [];
6747
6863
  let precedence = 0;
6748
6864
  const usage = files.get("usage.json");
@@ -6756,7 +6872,11 @@ async function scan4(agg, opts = {}) {
6756
6872
  }
6757
6873
  stats.filesRead++;
6758
6874
  rows = sidecarContributions(read3.json, projectDir);
6759
- if (rows.length > 0) precedence = 2;
6875
+ if (rows.length > 0) {
6876
+ precedence = 2;
6877
+ for (const row of rows)
6878
+ contextSessions.set(row.sessionId, { projectDir, parentSession });
6879
+ }
6760
6880
  }
6761
6881
  if (child) {
6762
6882
  rows = [];
@@ -6829,6 +6949,32 @@ async function scan4(agg, opts = {}) {
6829
6949
  }
6830
6950
  opts.onProgress?.(stats.filesFound);
6831
6951
  }
6952
+ try {
6953
+ const calls = await retainedContextCalls(
6954
+ root,
6955
+ new Set(contextSessions.keys()),
6956
+ opts.contextCacheDir
6957
+ );
6958
+ for (const call of calls) {
6959
+ if (opts.sinceMs !== void 0 && call.tsMs < opts.sinceMs) continue;
6960
+ const source = contextSessions.get(call.session);
6961
+ agg.workflow.ingest({
6962
+ type: "response",
6963
+ session: call.session,
6964
+ projectWorkspace: source?.projectDir,
6965
+ parentSession: source?.parentSession,
6966
+ tsMs: call.tsMs,
6967
+ responseId: `context:${call.id}`,
6968
+ contextTokens: call.tokens
6969
+ });
6970
+ const dates = sessionDates.get(call.session) ?? /* @__PURE__ */ new Set();
6971
+ dates.add(new Date(call.tsMs).toISOString().slice(0, 10));
6972
+ sessionDates.set(call.session, dates);
6973
+ }
6974
+ } catch {
6975
+ complete = false;
6976
+ stats.filesUnreadable++;
6977
+ }
6832
6978
  }
6833
6979
  for (const { rows } of candidates.values()) {
6834
6980
  for (const row of rows) {
@@ -7056,14 +7202,14 @@ function noteConfiguredMcpServers2(agg, state, serverNames) {
7056
7202
 
7057
7203
  // src/harness/opencode/scan.ts
7058
7204
  import { readFileSync as readFileSync7 } from "node:fs";
7059
- import { readdir as readdir5, stat as stat6 } from "node:fs/promises";
7060
- import { homedir as homedir11 } from "node:os";
7061
- import path9 from "node:path";
7205
+ import { readdir as readdir6, stat as stat6 } from "node:fs/promises";
7206
+ import { homedir as homedir12 } from "node:os";
7207
+ import path10 from "node:path";
7062
7208
  var OPENCODE_MIGRATION_CEILING = 20260622202450;
7063
7209
  function opencodeDataDirs() {
7064
7210
  const xdg = process.env.XDG_DATA_HOME;
7065
- const base = xdg || path9.join(homedir11(), ".local", "share");
7066
- return [path9.join(base, "opencode")];
7211
+ const base = xdg || path10.join(homedir12(), ".local", "share");
7212
+ return [path10.join(base, "opencode")];
7067
7213
  }
7068
7214
  function isStoreFile(basename2) {
7069
7215
  return basename2 === "opencode.db" || /^opencode-[^/]+\.db$/.test(basename2);
@@ -7072,8 +7218,8 @@ async function dbFilesIn(root) {
7072
7218
  const override = process.env.OPENCODE_DB;
7073
7219
  if (override) return [override];
7074
7220
  try {
7075
- const entries = await readdir5(root, { withFileTypes: true });
7076
- return entries.filter((e) => e.isFile() && isStoreFile(e.name)).map((e) => path9.join(root, e.name)).sort();
7221
+ const entries = await readdir6(root, { withFileTypes: true });
7222
+ return entries.filter((e) => e.isFile() && isStoreFile(e.name)).map((e) => path10.join(root, e.name)).sort();
7077
7223
  } catch {
7078
7224
  return [];
7079
7225
  }
@@ -7110,7 +7256,7 @@ async function scan5(agg, opts = {}) {
7110
7256
  } catch (e) {
7111
7257
  stats.filesUnreadable++;
7112
7258
  stats.unreadableFiles.push({
7113
- path: path9.basename(file),
7259
+ path: path10.basename(file),
7114
7260
  reason: errorClass2(e)
7115
7261
  });
7116
7262
  }
@@ -7277,8 +7423,8 @@ function pickTs(jsonTs, columnTs) {
7277
7423
  }
7278
7424
  function opencodeConfigFile() {
7279
7425
  const xdg = process.env.XDG_CONFIG_HOME;
7280
- const base = xdg || path9.join(homedir11(), ".config");
7281
- return path9.join(base, "opencode", "opencode.json");
7426
+ const base = xdg || path10.join(homedir12(), ".config");
7427
+ return path10.join(base, "opencode", "opencode.json");
7282
7428
  }
7283
7429
  function readConfiguredMcpServers2(agg, state, configFile) {
7284
7430
  const file = configFile ?? opencodeConfigFile();
@@ -7580,17 +7726,17 @@ function readCounts2(usageRaw) {
7580
7726
 
7581
7727
  // src/harness/pi/scan.ts
7582
7728
  import { createReadStream as createReadStream3 } from "node:fs";
7583
- import { readdir as readdir6, realpath as realpath3, stat as stat7 } from "node:fs/promises";
7584
- import { homedir as homedir12 } from "node:os";
7585
- import path10 from "node:path";
7729
+ import { readdir as readdir7, realpath as realpath3, stat as stat7 } from "node:fs/promises";
7730
+ import { homedir as homedir13 } from "node:os";
7731
+ import path11 from "node:path";
7586
7732
  import readline3 from "node:readline";
7587
7733
  var MAX_SESSION_VERSION = 3;
7588
7734
  function piAgentDir() {
7589
- return process.env.PI_CODING_AGENT_DIR || path10.join(homedir12(), ".pi", "agent");
7735
+ return process.env.PI_CODING_AGENT_DIR || path11.join(homedir13(), ".pi", "agent");
7590
7736
  }
7591
7737
  function sessionRoots2() {
7592
7738
  const override = process.env.PI_CODING_AGENT_SESSION_DIR;
7593
- return [override || path10.join(piAgentDir(), "sessions")];
7739
+ return [override || path11.join(piAgentDir(), "sessions")];
7594
7740
  }
7595
7741
  var SESSION_FILE_RE = /^\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}-\d{3}Z_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.jsonl$/;
7596
7742
  function isSessionFile(basename2) {
@@ -7599,12 +7745,12 @@ function isSessionFile(basename2) {
7599
7745
  async function* walkSessions(dir) {
7600
7746
  let entries;
7601
7747
  try {
7602
- entries = await readdir6(dir, { withFileTypes: true });
7748
+ entries = await readdir7(dir, { withFileTypes: true });
7603
7749
  } catch {
7604
7750
  return;
7605
7751
  }
7606
7752
  for (const e of entries) {
7607
- const full = path10.join(dir, e.name);
7753
+ const full = path11.join(dir, e.name);
7608
7754
  if (e.isDirectory()) yield* walkSessions(full);
7609
7755
  else if (e.isFile() && isSessionFile(e.name)) yield full;
7610
7756
  }
@@ -7652,7 +7798,7 @@ async function scan6(agg, opts = {}) {
7652
7798
  stats.filesUnreadable++;
7653
7799
  stats.filesRead--;
7654
7800
  stats.unreadableFiles.push({
7655
- path: path10.relative(root, file),
7801
+ path: path11.relative(root, file),
7656
7802
  reason: "version-too-new"
7657
7803
  });
7658
7804
  }
@@ -7660,7 +7806,7 @@ async function scan6(agg, opts = {}) {
7660
7806
  stats.filesUnreadable++;
7661
7807
  stats.filesRead--;
7662
7808
  stats.unreadableFiles.push({
7663
- path: path10.relative(root, file),
7809
+ path: path11.relative(root, file),
7664
7810
  reason: "read-error"
7665
7811
  });
7666
7812
  }
@@ -7798,7 +7944,7 @@ var MCP_ADD_ARGS = [
7798
7944
  "mcp"
7799
7945
  ];
7800
7946
  var MCP_REMOVE_ARGS = ["mcp", "remove", "--scope", "user", "aistack"];
7801
- var SKILL_DEST = join6(homedir13(), ".claude", "skills", "aistack-sync");
7947
+ var SKILL_DEST = join6(homedir14(), ".claude", "skills", "aistack-sync");
7802
7948
  function runClaude(args) {
7803
7949
  const r = spawnSync("claude", args, { encoding: "utf-8" });
7804
7950
  const notFound = r.error !== void 0 && r.error.code === "ENOENT";
@@ -8120,13 +8266,13 @@ async function loginCommand(options = {}) {
8120
8266
  import * as p7 from "@clack/prompts";
8121
8267
 
8122
8268
  // src/autosync/codexHook.ts
8123
- import { createHash as createHash2 } from "node:crypto";
8269
+ import { createHash as createHash3 } from "node:crypto";
8124
8270
  import { existsSync as existsSync8, mkdirSync as mkdirSync3, readFileSync as readFileSync9, writeFileSync as writeFileSync3 } from "node:fs";
8125
- import { homedir as homedir14 } from "node:os";
8271
+ import { homedir as homedir15 } from "node:os";
8126
8272
  import { dirname as dirname5, join as join8 } from "node:path";
8127
8273
  import { parse } from "smol-toml";
8128
8274
  function codexHome2() {
8129
- return process.env.CODEX_HOME || join8(homedir14(), ".codex");
8275
+ return process.env.CODEX_HOME || join8(homedir15(), ".codex");
8130
8276
  }
8131
8277
  function codexHooksFile() {
8132
8278
  return join8(codexHome2(), "hooks.json");
@@ -8235,7 +8381,7 @@ function codexHookTrusted(configFile = codexConfigFile(), hooksFile = codexHooks
8235
8381
  hooks: [normalizedHandler]
8236
8382
  };
8237
8383
  if (typeof group.matcher === "string") identity.matcher = group.matcher;
8238
- const currentHash = `sha256:${createHash2("sha256").update(JSON.stringify(canonicalJson2(identity))).digest("hex")}`;
8384
+ const currentHash = `sha256:${createHash3("sha256").update(JSON.stringify(canonicalJson2(identity))).digest("hex")}`;
8239
8385
  const key2 = `${hooksFile}:session_start:${groupIndex}:${handlerIndex}`;
8240
8386
  matches.push(config.hooks?.state?.[key2]?.trusted_hash === currentHash);
8241
8387
  }
@@ -8267,14 +8413,14 @@ import * as p6 from "@clack/prompts";
8267
8413
 
8268
8414
  // src/autosync/cursorHook.ts
8269
8415
  import { existsSync as existsSync10, mkdirSync as mkdirSync5, readFileSync as readFileSync11, writeFileSync as writeFileSync5 } from "node:fs";
8270
- import { homedir as homedir16, platform as platform2 } from "node:os";
8416
+ import { homedir as homedir17, platform as platform2 } from "node:os";
8271
8417
  import { dirname as dirname7, join as join10 } from "node:path";
8272
8418
 
8273
8419
  // src/autosync/hook.ts
8274
8420
  import { existsSync as existsSync9, mkdirSync as mkdirSync4, readFileSync as readFileSync10, writeFileSync as writeFileSync4 } from "node:fs";
8275
- import { homedir as homedir15 } from "node:os";
8421
+ import { homedir as homedir16 } from "node:os";
8276
8422
  import { dirname as dirname6, join as join9 } from "node:path";
8277
- var CLAUDE_SETTINGS_FILE = join9(homedir15(), ".claude", "settings.json");
8423
+ var CLAUDE_SETTINGS_FILE = join9(homedir16(), ".claude", "settings.json");
8278
8424
  var AUTO_SYNC_HOOK_COMMAND = "npx -y @use-aistack/cli@latest sync --auto || npx -y --prefer-offline @use-aistack/cli sync --auto";
8279
8425
  function isOurs2(entry) {
8280
8426
  return typeof entry.command === "string" && entry.command.includes("@use-aistack/cli") && entry.command.includes("sync --auto");
@@ -8347,7 +8493,7 @@ function autoSyncHookInstalled(file = CLAUDE_SETTINGS_FILE) {
8347
8493
  }
8348
8494
 
8349
8495
  // src/autosync/cursorHook.ts
8350
- var CURSOR_HOOK_FILE = join10(homedir16(), ".cursor", "hooks.json");
8496
+ var CURSOR_HOOK_FILE = join10(homedir17(), ".cursor", "hooks.json");
8351
8497
  function cursorHookCommand(os = platform2()) {
8352
8498
  if (os === "win32") {
8353
8499
  const script = `Start-Process -WindowStyle Hidden -FilePath 'cmd.exe' -ArgumentList '/d /s /c "set AISTACK_HOOK_SOURCE=cursor&& (${AUTO_SYNC_HOOK_COMMAND}) <NUL >NUL 2>&1"'`;
@@ -8427,10 +8573,10 @@ import {
8427
8573
  unlinkSync,
8428
8574
  writeFileSync as writeFileSync6
8429
8575
  } from "node:fs";
8430
- import { homedir as homedir17, platform as platform3 } from "node:os";
8576
+ import { homedir as homedir18, platform as platform3 } from "node:os";
8431
8577
  import { dirname as dirname8, join as join11 } from "node:path";
8432
8578
  var GROK_HOOK_FILE = join11(
8433
- process.env.GROK_HOME || join11(homedir17(), ".grok"),
8579
+ process.env.GROK_HOME || join11(homedir18(), ".grok"),
8434
8580
  "hooks",
8435
8581
  "aistack.json"
8436
8582
  );
@@ -8788,11 +8934,11 @@ import {
8788
8934
  unlinkSync as unlinkSync2,
8789
8935
  writeFileSync as writeFileSync8
8790
8936
  } from "node:fs";
8791
- import { homedir as homedir19 } from "node:os";
8937
+ import { homedir as homedir20 } from "node:os";
8792
8938
  import { dirname as dirname9, join as join12 } from "node:path";
8793
8939
 
8794
8940
  // src/sync/stage.ts
8795
- import { createHash as createHash4 } from "node:crypto";
8941
+ import { createHash as createHash5 } from "node:crypto";
8796
8942
 
8797
8943
  // src/usage/days.ts
8798
8944
  var round6 = (n) => Math.round(n * 1e6) / 1e6;
@@ -8907,11 +9053,11 @@ function buildMeasuredDays(input) {
8907
9053
 
8908
9054
  // src/usage/diff.ts
8909
9055
  var MAX_DAY_WINDOW = 400;
8910
- var DAY_MS = 864e5;
9056
+ var DAY_MS2 = 864e5;
8911
9057
  function retentionFloor(todayUtc, days) {
8912
9058
  const span = Math.max(1, Math.min(days, MAX_DAY_WINDOW));
8913
9059
  const todayMs = Date.parse(`${todayUtc}T00:00:00.000Z`);
8914
- return new Date(todayMs - (span - 1) * DAY_MS).toISOString().slice(0, 10);
9060
+ return new Date(todayMs - (span - 1) * DAY_MS2).toISOString().slice(0, 10);
8915
9061
  }
8916
9062
  function selectDaysToPublish(input) {
8917
9063
  const { local, manifest, todayUtc } = input;
@@ -8948,7 +9094,7 @@ function selectDaysToPublish(input) {
8948
9094
 
8949
9095
  // src/workflow/git.ts
8950
9096
  import { execFile, execFileSync as execFileSync2 } from "node:child_process";
8951
- import path11 from "node:path";
9097
+ import path12 from "node:path";
8952
9098
  var TEST_FILE_RULE_VERSION = "test-files/v2";
8953
9099
  var FILE_TYPE_RULE_VERSION = "file-types/v2";
8954
9100
  var COMMIT_SET_RULE_VERSION = "commit-set/v1";
@@ -9212,10 +9358,10 @@ function reduceGitHistories(histories, options) {
9212
9358
  const field = fields[fieldIndex] ?? "";
9213
9359
  if (field.replace(/^\n+/, "") === COMMIT_MARKER) {
9214
9360
  finishCommit();
9215
- const hash = fields[++fieldIndex] ?? "";
9361
+ const hash2 = fields[++fieldIndex] ?? "";
9216
9362
  const authoredAt = fields[++fieldIndex] ?? "";
9217
9363
  const authoredMs = Date.parse(authoredAt);
9218
- const included = Number.isFinite(authoredMs) && authoredMs >= options.fromMs && authoredMs <= options.toMs && !seenCommits.has(hash);
9364
+ const included = Number.isFinite(authoredMs) && authoredMs >= options.fromMs && authoredMs <= options.toMs && !seenCommits.has(hash2);
9219
9365
  current = {
9220
9366
  included,
9221
9367
  date: included ? new Date(authoredMs).toISOString().slice(0, 10) : "",
@@ -9228,7 +9374,7 @@ function reduceGitHistories(histories, options) {
9228
9374
  withheldLines: 0,
9229
9375
  extensionLines: /* @__PURE__ */ new Map()
9230
9376
  };
9231
- if (included) seenCommits.add(hash);
9377
+ if (included) seenCommits.add(hash2);
9232
9378
  continue;
9233
9379
  }
9234
9380
  const stat8 = parseNumstat(field);
@@ -9247,7 +9393,7 @@ function reduceGitHistories(histories, options) {
9247
9393
  current.changedLines += fileChangedLines;
9248
9394
  if (isTestFile(file)) current.touchesTest = true;
9249
9395
  if (fileChangedLines <= 0) continue;
9250
- const extension = path11.extname(file).toLowerCase();
9396
+ const extension = path12.extname(file).toLowerCase();
9251
9397
  if (APPROVED_EXTENSIONS.has(extension)) {
9252
9398
  current.extensionLines.set(
9253
9399
  extension,
@@ -9351,18 +9497,18 @@ function buildWorkflowExtraction(harnessWorkflows, git, utcOffsetMinutes = machi
9351
9497
  }
9352
9498
 
9353
9499
  // src/sync/grokDateCache.ts
9354
- import { createHash as createHash3 } from "node:crypto";
9500
+ import { createHash as createHash4 } from "node:crypto";
9355
9501
  import { existsSync as existsSync12, mkdirSync as mkdirSync7, readFileSync as readFileSync13, writeFileSync as writeFileSync7 } from "node:fs";
9356
- import { homedir as homedir18 } from "node:os";
9357
- import path12 from "node:path";
9358
- var defaultFile = path12.join(
9359
- homedir18(),
9502
+ import { homedir as homedir19 } from "node:os";
9503
+ import path13 from "node:path";
9504
+ var defaultFile = path13.join(
9505
+ homedir19(),
9360
9506
  ".config",
9361
9507
  "aistack",
9362
9508
  "grok-session-dates.json"
9363
9509
  );
9364
9510
  function grokCacheScope(baseUrl, stack, token) {
9365
- return createHash3("sha256").update(`${baseUrl}\0${stack}\0${token}`).digest("hex");
9511
+ return createHash4("sha256").update(`${baseUrl}\0${stack}\0${token}`).digest("hex");
9366
9512
  }
9367
9513
  function read2(file) {
9368
9514
  if (!existsSync12(file)) return {};
@@ -9379,7 +9525,7 @@ function loadGrokDateHints(scope, file = defaultFile) {
9379
9525
  function saveGrokDateHints(scope, hints, file = defaultFile) {
9380
9526
  const cache = read2(file);
9381
9527
  cache[scope] = hints;
9382
- mkdirSync7(path12.dirname(file), { recursive: true });
9528
+ mkdirSync7(path13.dirname(file), { recursive: true });
9383
9529
  writeFileSync7(file, JSON.stringify(cache, null, 2));
9384
9530
  }
9385
9531
  function mapToHints(value, floor) {
@@ -9791,7 +9937,7 @@ function buildGateSummary(ctx) {
9791
9937
  // src/sync/stage.ts
9792
9938
  var utcDate2 = (ms) => new Date(ms).toISOString().slice(0, 10);
9793
9939
  function stageId(bodyJson) {
9794
- return createHash4("sha256").update(bodyJson).digest("hex").slice(0, 12);
9940
+ return createHash5("sha256").update(bodyJson).digest("hex").slice(0, 12);
9795
9941
  }
9796
9942
  async function stageSync(deps) {
9797
9943
  const now = (deps.now ?? Date.now)();
@@ -9996,7 +10142,7 @@ async function stageSync(deps) {
9996
10142
  }
9997
10143
 
9998
10144
  // src/autosync/run.ts
9999
- var SYNC_LOG_FILE = join12(homedir19(), ".config", "aistack", "sync.log");
10145
+ var SYNC_LOG_FILE = join12(homedir20(), ".config", "aistack", "sync.log");
10000
10146
  var SYNC_LOG_MAX_LINES = 200;
10001
10147
  var FIX_COMMAND = "npx @use-aistack/cli sync";
10002
10148
  var REVOKED_RESULT = "off - auto-sync is switched off for this stack";
@@ -10049,7 +10195,7 @@ async function runAutoSync(deps) {
10049
10195
  const state = settings.autoSyncState ?? {};
10050
10196
  const lastRunAt = state.lastRunAt ?? 0;
10051
10197
  if (now - lastRunAt < windowMs) return;
10052
- const reservationFile = deps.reservationFile ?? `${settingsFile ?? join12(homedir19(), ".config", "aistack", "settings.json")}.auto-sync-attempt`;
10198
+ const reservationFile = deps.reservationFile ?? `${settingsFile ?? join12(homedir20(), ".config", "aistack", "settings.json")}.auto-sync-attempt`;
10053
10199
  if (!reserveAttempt(reservationFile, now, windowMs)) return;
10054
10200
  saveSettings({ autoSyncState: { ...state, lastRunAt: now } }, settingsFile);
10055
10201
  const stage = deps.stageImpl ?? stageSync;