imprnt 0.1.3-edge.60 → 0.1.3-edge.61

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 (3) hide show
  1. package/dist/cli.js +141 -99
  2. package/dist/imp.js +141 -99
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -766,14 +766,9 @@ function registerVault(path, opts = {}) {
766
766
  const name = opts.name ?? "personal";
767
767
  reg.vaults[name] = path;
768
768
  reg.default = name;
769
- const p = configPath();
770
- try {
771
- mkdirSync2(dirname2(p), { recursive: true });
772
- writeFileSync5(p, JSON.stringify(reg, null, 2) + `
773
- `);
774
- } catch (e) {
775
- return { status: "error", current: path, error: e instanceof Error ? e.message : String(e) };
776
- }
769
+ const w = writeRegistry(reg);
770
+ if (!w.ok)
771
+ return { status: "error", current: path, error: w.error };
777
772
  return { status: "registered", current: path };
778
773
  }
779
774
  function readDefaultAgent() {
@@ -842,8 +837,9 @@ var init_registry = () => {};
842
837
  // scripts/lib/launch.ts
843
838
  import { existsSync as existsSync7, mkdtempSync as mkdtempSync2, readFileSync as readFileSync7, realpathSync, rmSync as rmSync3, statSync as statSync5, writeFileSync as writeFileSync6 } from "node:fs";
844
839
  import { homedir as homedir2, tmpdir as tmpdir2 } from "node:os";
845
- import { isAbsolute, join as join8, resolve as resolve4, sep as sep2 } from "node:path";
840
+ import { basename as basename2, isAbsolute, join as join8, resolve as resolve4, sep as sep2 } from "node:path";
846
841
  import { spawnSync as spawnSync2 } from "node:child_process";
842
+ import { createInterface } from "node:readline/promises";
847
843
  function importPath(root, target) {
848
844
  if (target.startsWith("~/"))
849
845
  return join8(homedir2(), target.slice(2));
@@ -1013,7 +1009,12 @@ function claudeRenderArgs(spec) {
1013
1009
  const systemPrompt = spec.insideProject ? spec.globals : joinFragments([spec.cast, spec.pointer, spec.globals]);
1014
1010
  const harness = spec.pluginsRoot ? harnessFlags(spec.pluginsRoot) : [];
1015
1011
  const extra = spec.addDirs.flatMap((d) => ["--add-dir", d]);
1016
- const pass = spec.skipPermissions && !spec.passthrough.includes("--dangerously-skip-permissions") ? ["--dangerously-skip-permissions", ...spec.passthrough] : spec.passthrough;
1012
+ const lead = [];
1013
+ if (spec.skipPermissions && !spec.passthrough.includes("--dangerously-skip-permissions"))
1014
+ lead.push("--dangerously-skip-permissions");
1015
+ if (spec.model && !spec.passthrough.some((a) => a === "--model" || a.startsWith("--model=")))
1016
+ lead.push("--model", spec.model);
1017
+ const pass = lead.length ? [...lead, ...spec.passthrough] : spec.passthrough;
1017
1018
  const body = systemPrompt ? mergeFragment(pass, systemPrompt, extra) : [...pass, ...extra];
1018
1019
  return [...harness, ...body];
1019
1020
  }
@@ -1023,6 +1024,19 @@ function expandGeminiModel(m) {
1023
1024
  function escapeGeminiImports(text) {
1024
1025
  return text.replaceAll("@", "\\@");
1025
1026
  }
1027
+ function trackGeminiTempDir(dir) {
1028
+ geminiTempDirs.add(dir);
1029
+ if (geminiCleanupArmed)
1030
+ return;
1031
+ geminiCleanupArmed = true;
1032
+ process.on("exit", () => {
1033
+ for (const d of geminiTempDirs) {
1034
+ try {
1035
+ rmSync3(d, { recursive: true, force: true });
1036
+ } catch {}
1037
+ }
1038
+ });
1039
+ }
1026
1040
  function geminiRenderArgs(spec) {
1027
1041
  const includes = [];
1028
1042
  const systemPrompt = joinFragments([spec.cast, spec.pointer, spec.globals]);
@@ -1030,11 +1044,7 @@ function geminiRenderArgs(spec) {
1030
1044
  const dir = mkdtempSync2(join8(tmpdir2(), "imprnt-gemini-"));
1031
1045
  writeFileSync6(join8(dir, "GEMINI.md"), escapeGeminiImports(systemPrompt) + `
1032
1046
  `);
1033
- process.on("exit", () => {
1034
- try {
1035
- rmSync3(dir, { recursive: true, force: true });
1036
- } catch {}
1037
- });
1047
+ trackGeminiTempDir(dir);
1038
1048
  includes.push(dir);
1039
1049
  }
1040
1050
  includes.push(...spec.addDirs);
@@ -1049,16 +1059,23 @@ function geminiRenderArgs(spec) {
1049
1059
  lead.push("--skip-trust");
1050
1060
  }
1051
1061
  const pass = [...spec.passthrough];
1052
- const mIdx = pass.findIndex((a) => a === "-m" || a === "--model");
1053
- if (mIdx >= 0 && pass[mIdx + 1] !== undefined) {
1054
- pass[mIdx + 1] = expandGeminiModel(pass[mIdx + 1]);
1055
- } else if (mIdx < 0 && spec.model) {
1062
+ const mTerm = pass.indexOf("--");
1063
+ const mEnd = mTerm < 0 ? pass.length : mTerm;
1064
+ const isModelFlag = (a) => a === "-m" || a === "--model" || a.startsWith("-m=") || a.startsWith("--model=");
1065
+ const mIdx = pass.slice(0, mEnd).findIndex(isModelFlag);
1066
+ if (mIdx >= 0) {
1067
+ const tok = pass[mIdx];
1068
+ const eq = tok.indexOf("=");
1069
+ if (eq >= 0)
1070
+ pass[mIdx] = tok.slice(0, eq + 1) + expandGeminiModel(tok.slice(eq + 1));
1071
+ else if (mIdx + 1 < mEnd && pass[mIdx + 1] !== undefined)
1072
+ pass[mIdx + 1] = expandGeminiModel(pass[mIdx + 1]);
1073
+ } else if (spec.model) {
1056
1074
  pass.unshift("-m", expandGeminiModel(spec.model));
1057
1075
  }
1058
- const rIdx = pass.findIndex((a) => a === "-r" || a === "--resume");
1059
- if (rIdx >= 0 && (pass[rIdx + 1] === undefined || pass[rIdx + 1].startsWith("-"))) {
1076
+ const rIdx = valuelessResumeIndex(pass);
1077
+ if (rIdx >= 0)
1060
1078
  pass.splice(rIdx + 1, 0, "latest");
1061
- }
1062
1079
  const flags = includes.length ? ["--include-directories", includes.join(",")] : [];
1063
1080
  return [...lead, ...flags, ...pass];
1064
1081
  }
@@ -1071,11 +1088,88 @@ function parseGeminiSessions(stdout) {
1071
1088
  }
1072
1089
  return out;
1073
1090
  }
1091
+ function valuelessResumeIndex(args) {
1092
+ const term = args.indexOf("--");
1093
+ const end = term < 0 ? args.length : term;
1094
+ let i = -1;
1095
+ for (let k = 0;k < end; k++) {
1096
+ if (args[k] === "-r" || args[k] === "--resume") {
1097
+ i = k;
1098
+ break;
1099
+ }
1100
+ }
1101
+ if (i < 0)
1102
+ return -1;
1103
+ const next = i + 1 < end ? args[i + 1] : undefined;
1104
+ return next === undefined || next.startsWith("-") ? i : -1;
1105
+ }
1106
+ function listGeminiSessions(cwd) {
1107
+ const r = spawnSync2("gemini", ["--list-sessions", "--skip-trust"], { cwd, encoding: "utf8" });
1108
+ if (r.error || r.status !== 0)
1109
+ return { sessions: [], ran: false };
1110
+ const stdout = r.stdout || "";
1111
+ const sessions = parseGeminiSessions(stdout);
1112
+ if (!sessions.length && stdout.trim() && !/\(\s*0\s*\)|no sessions/i.test(stdout)) {
1113
+ console.error("imp: couldn't parse gemini's session list (its format may have changed) - starting fresh");
1114
+ }
1115
+ return { sessions, ran: true };
1116
+ }
1117
+ async function pickGeminiSession(sessions, label) {
1118
+ const cols = process.stdout.columns || 80;
1119
+ const nameW = Math.max(20, Math.min(64, cols - 16));
1120
+ const trunc = (s) => s.length > nameW ? s.slice(0, nameW - 1) + "…" : s;
1121
+ process.stderr.write(`
1122
+ resume a chat - gemini · ${label}
1123
+
1124
+ `);
1125
+ sessions.forEach((s, i) => {
1126
+ process.stderr.write(` ${String(i + 1).padStart(2)} ${trunc(s.name).padEnd(nameW)} ${s.age.replace(/\s*ago$/, "")}
1127
+ `);
1128
+ });
1129
+ process.stderr.write(`
1130
+ `);
1131
+ const rl = createInterface({ input: process.stdin, output: process.stderr });
1132
+ const answer = (await rl.question(" number to resume · enter for newest · q to cancel › ")).trim();
1133
+ rl.close();
1134
+ if (answer === "")
1135
+ return "latest";
1136
+ if (answer.toLowerCase() === "q")
1137
+ return null;
1138
+ const n = Number(answer);
1139
+ if (Number.isInteger(n) && n >= 1 && n <= sessions.length)
1140
+ return sessions[n - 1].id;
1141
+ process.stderr.write(` not a valid choice - starting a fresh session
1142
+ `);
1143
+ return null;
1144
+ }
1145
+ async function geminiResolveResume(passthrough, cwd) {
1146
+ const rIdx = valuelessResumeIndex(passthrough);
1147
+ if (rIdx < 0)
1148
+ return passthrough;
1149
+ if (!(process.stdin.isTTY && process.stdout.isTTY))
1150
+ return passthrough;
1151
+ const { sessions, ran } = listGeminiSessions(cwd);
1152
+ if (!ran)
1153
+ return passthrough;
1154
+ if (!sessions.length) {
1155
+ console.error("imp: no saved gemini chats for this project yet - starting fresh");
1156
+ return passthrough.filter((_, i) => i !== rIdx);
1157
+ }
1158
+ const chosen = await pickGeminiSession(sessions, basename2(cwd));
1159
+ if (chosen === null)
1160
+ return passthrough.filter((_, i) => i !== rIdx);
1161
+ const out = [...passthrough];
1162
+ out.splice(rIdx + 1, 0, chosen);
1163
+ return out;
1164
+ }
1074
1165
  function resolveLaunch(passthrough, config = {}) {
1166
+ const term = passthrough.indexOf("--");
1167
+ const head = term < 0 ? passthrough : passthrough.slice(0, term);
1168
+ const tail = term < 0 ? [] : passthrough.slice(term);
1075
1169
  let agentFlag;
1076
1170
  let yoloFlag;
1077
1171
  const rest = [];
1078
- for (const a of passthrough) {
1172
+ for (const a of head) {
1079
1173
  if (a === "--gemini")
1080
1174
  agentFlag = agentFlag ?? "gemini";
1081
1175
  else if (a === "--claude")
@@ -1093,9 +1187,10 @@ function resolveLaunch(passthrough, config = {}) {
1093
1187
  console.error(`imp: unknown agent "${name}" - falling back to claude (valid: ${Object.keys(backends).join(", ")})`);
1094
1188
  backend = claudeBackend;
1095
1189
  }
1096
- const envYolo = "IMPRNT_YOLO" in process.env ? !["0", "false", ""].includes(process.env.IMPRNT_YOLO ?? "") : undefined;
1190
+ const yoloEnv = process.env.IMPRNT_YOLO;
1191
+ const envYolo = yoloEnv === undefined ? undefined : !["0", "false", "", "off", "no"].includes(yoloEnv.toLowerCase());
1097
1192
  const skipPermissions = yoloFlag ?? envYolo ?? config.yolo ?? false;
1098
- return { backend, skipPermissions, passthrough: rest };
1193
+ return { backend, skipPermissions, passthrough: [...rest, ...tail] };
1099
1194
  }
1100
1195
  function launchBackend(backend, cwd, args, env = process.env) {
1101
1196
  if (!isDir(cwd)) {
@@ -1110,7 +1205,7 @@ function launchBackend(backend, cwd, args, env = process.env) {
1110
1205
  }
1111
1206
  return r.status ?? 1;
1112
1207
  }
1113
- var claudeBackend, GEMINI_MODEL_ALIASES, geminiBackend, backends;
1208
+ var claudeBackend, GEMINI_MODEL_ALIASES, geminiTempDirs, geminiCleanupArmed = false, geminiBackend, backends;
1114
1209
  var init_launch = __esm(() => {
1115
1210
  init_plugins();
1116
1211
  init_global();
@@ -1127,10 +1222,12 @@ var init_launch = __esm(() => {
1127
1222
  gemma31: "gemma-4-31b-it",
1128
1223
  gemma26: "gemma-4-26b-a4b-it"
1129
1224
  };
1225
+ geminiTempDirs = new Set;
1130
1226
  geminiBackend = {
1131
1227
  name: "gemini",
1132
1228
  missingHint: "Install the Gemini CLI first: npm i -g @google/gemini-cli",
1133
- renderArgs: geminiRenderArgs
1229
+ renderArgs: geminiRenderArgs,
1230
+ resolveResume: geminiResolveResume
1134
1231
  };
1135
1232
  backends = {
1136
1233
  claude: claudeBackend,
@@ -1173,7 +1270,7 @@ var init_manifest = () => {};
1173
1270
  var exports_ingest = {};
1174
1271
  import { readFileSync as readFileSync9, writeFileSync as writeFileSync8, mkdirSync as mkdirSync3, existsSync as existsSync9, copyFileSync, readdirSync as readdirSync5, rmSync as rmSync4, statSync as statSync6 } from "node:fs";
1175
1272
  import { createHash } from "node:crypto";
1176
- import { basename as basename2, extname, join as join10, relative as relative3 } from "node:path";
1273
+ import { basename as basename3, extname, join as join10, relative as relative3 } from "node:path";
1177
1274
  function slugify(s) {
1178
1275
  return s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 60);
1179
1276
  }
@@ -1254,7 +1351,7 @@ function applyStaged(staged, vault) {
1254
1351
  return "error";
1255
1352
  }
1256
1353
  const title = text.match(/^#\s+(.+)$/m)?.[1]?.trim() ?? "";
1257
- const slug = deriveSlug([fmScalar2(fm, "slug"), title, basename2(staged, ".md")]);
1354
+ const slug = deriveSlug([fmScalar2(fm, "slug"), title, basename3(staged, ".md")]);
1258
1355
  if (!slug) {
1259
1356
  console.error(` ✗ ${staged}: can't derive a slug - slug:, the H1 title, and the filename all slugify to nothing`);
1260
1357
  return "error";
@@ -1481,7 +1578,7 @@ ${filed} filed, ${noop} no-op, ${conflict} conflict, ${error} error.`);
1481
1578
  console.log(`unchanged (hash ${hash}) — skipping ${src}. note: ${prior.note}`);
1482
1579
  process.exit(0);
1483
1580
  }
1484
- fname = basename2(src);
1581
+ fname = basename3(src);
1485
1582
  dateMatch = fname.match(/(\d{4}-\d{2}-\d{2})/) || text.match(/^\s*date:\s*(\d{4}-\d{2}-\d{2})/im);
1486
1583
  date = dateMatch ? dateMatch[1] : new Date().toISOString().slice(0, 10);
1487
1584
  lines = text.split(/\r?\n/);
@@ -1515,7 +1612,7 @@ ${filed} filed, ${noop} no-op, ${conflict} conflict, ${error} error.`);
1515
1612
  }
1516
1613
  recurringSpeaker = [...turnsBySpeaker.values()].some((n) => n >= 2);
1517
1614
  isTranscript = isFile && looksLikeTranscript(speakers, turnCount, contentLines, recurringSpeaker);
1518
- slugBasis = slugHint || subject || [...speakers].join("-") || (isFile ? basename2(src, extname(src)) : text.slice(0, 60));
1615
+ slugBasis = slugHint || subject || [...speakers].join("-") || (isFile ? basename3(src, extname(src)) : text.slice(0, 60));
1519
1616
  subjectSlug = slugify(slugBasis) || "source";
1520
1617
  noteSlug = `${date}-${subjectSlug}`;
1521
1618
  rawDir = join10(vault, "..", "raw", isTranscript ? "transcripts" : "adhoc");
@@ -1756,7 +1853,7 @@ var init_tags = __esm(() => {
1756
1853
  // scripts/recall.ts
1757
1854
  var exports_recall = {};
1758
1855
  import { lstatSync as lstatSync2, readdirSync as readdirSync6, readFileSync as readFileSync11 } from "node:fs";
1759
- import { basename as basename3, join as join12, relative as relative4 } from "node:path";
1856
+ import { basename as basename4, join as join12, relative as relative4 } from "node:path";
1760
1857
  function walk2(dir2) {
1761
1858
  const out = [];
1762
1859
  for (const entry of readdirSync6(dir2)) {
@@ -1973,7 +2070,7 @@ var init_recall = __esm(() => {
1973
2070
  for (const t of tokens)
1974
2071
  tf.set(t, (tf.get(t) ?? 0) + weight);
1975
2072
  };
1976
- add([...tokenize(titleText), ...tokenize(basename3(path, ".md"))], TITLE_BOOST);
2073
+ add([...tokenize(titleText), ...tokenize(basename4(path, ".md"))], TITLE_BOOST);
1977
2074
  add(tokenize(aliases), TITLE_BOOST);
1978
2075
  add(tags.flatMap(tokenize), TAG_BOOST);
1979
2076
  add(tokenize(summary), SUMMARY_BOOST);
@@ -2038,7 +2135,7 @@ var init_recall = __esm(() => {
2038
2135
  var exports_snapshot = {};
2039
2136
  import { readdirSync as readdirSync7, readFileSync as readFileSync12, copyFileSync as copyFileSync2, mkdirSync as mkdirSync4, existsSync as existsSync11, statSync as statSync7, lstatSync as lstatSync3 } from "node:fs";
2040
2137
  import { createHash as createHash2 } from "node:crypto";
2041
- import { join as join13, relative as relative5, basename as basename4, extname as extname2, resolve as resolve5, sep as sep3 } from "node:path";
2138
+ import { join as join13, relative as relative5, basename as basename5, extname as extname2, resolve as resolve5, sep as sep3 } from "node:path";
2042
2139
  function destWithinRaw(rawRoot, dest) {
2043
2140
  const rootResolved = resolve5(rawRoot);
2044
2141
  const destResolved = resolve5(rootResolved, dest);
@@ -2050,7 +2147,7 @@ function collect(p, base) {
2050
2147
  let skippedLinks = 0;
2051
2148
  const st = statSync7(p);
2052
2149
  if (st.isFile())
2053
- return { files: [{ abs: p, rel: basename4(p) }], skippedLinks };
2150
+ return { files: [{ abs: p, rel: basename5(p) }], skippedLinks };
2054
2151
  const out = [];
2055
2152
  const walk3 = (dir2) => {
2056
2153
  for (const entry of readdirSync7(dir2)) {
@@ -2528,10 +2625,10 @@ all plugin checks passed.`);
2528
2625
  var exports_cli = {};
2529
2626
  import { cpSync as cpSync3, mkdirSync as mkdirSync5, chmodSync, existsSync as existsSync13, readFileSync as readFileSync14, lstatSync as lstatSync4, statSync as statSync9, realpathSync as realpathSync2 } from "node:fs";
2530
2627
  import { spawnSync as spawnSync4 } from "node:child_process";
2531
- import { join as join15, dirname as dirname4, resolve as resolve6, basename as basename5 } from "node:path";
2628
+ import { join as join15, dirname as dirname4, resolve as resolve6, basename as basename6 } from "node:path";
2532
2629
  import { homedir as homedir4 } from "node:os";
2533
2630
  import { fileURLToPath } from "node:url";
2534
- import { createInterface } from "node:readline/promises";
2631
+ import { createInterface as createInterface2 } from "node:readline/promises";
2535
2632
  function pluginRoot() {
2536
2633
  const local = projectRoot();
2537
2634
  if (process.env.IMPRNT_ROOT || process.env.IMPRINT_ROOT)
@@ -2578,7 +2675,7 @@ function physicalTarget(target) {
2578
2675
  const parent = dirname4(existing);
2579
2676
  if (parent === existing)
2580
2677
  return target;
2581
- tail.unshift(basename5(existing));
2678
+ tail.unshift(basename6(existing));
2582
2679
  existing = parent;
2583
2680
  }
2584
2681
  try {
@@ -2607,61 +2704,6 @@ function controlSlot(dst) {
2607
2704
  return "keep";
2608
2705
  return "blocked-non-regular-file";
2609
2706
  }
2610
- function listGeminiSessions(cwd) {
2611
- const r = spawnSync4("gemini", ["--list-sessions", "--skip-trust"], { cwd, encoding: "utf8" });
2612
- return parseGeminiSessions(r.stdout || "");
2613
- }
2614
- async function pickGeminiSession(sessions, label) {
2615
- const cols = process.stdout.columns || 80;
2616
- const nameW = Math.max(20, Math.min(64, cols - 16));
2617
- const trunc = (s) => s.length > nameW ? s.slice(0, nameW - 1) + "…" : s;
2618
- process.stderr.write(`
2619
- resume a chat — gemini · ${label}
2620
-
2621
- `);
2622
- sessions.forEach((s, i) => {
2623
- process.stderr.write(` ${String(i + 1).padStart(2)} ${trunc(s.name).padEnd(nameW)} ${s.age.replace(/\s*ago$/, "")}
2624
- `);
2625
- });
2626
- process.stderr.write(`
2627
- `);
2628
- const rl = createInterface({ input: process.stdin, output: process.stderr });
2629
- const answer = (await rl.question(" number to resume · enter for newest · q to cancel › ")).trim();
2630
- rl.close();
2631
- if (answer === "")
2632
- return "latest";
2633
- if (answer.toLowerCase() === "q")
2634
- return null;
2635
- const n = Number(answer);
2636
- if (Number.isInteger(n) && n >= 1 && n <= sessions.length)
2637
- return sessions[n - 1].id;
2638
- process.stderr.write(` not a valid choice — starting a fresh session
2639
- `);
2640
- return null;
2641
- }
2642
- async function resolveGeminiResume(backendName, passthrough, cwd) {
2643
- if (backendName !== "gemini")
2644
- return passthrough;
2645
- const rIdx = passthrough.findIndex((a) => a === "-r" || a === "--resume");
2646
- if (rIdx < 0)
2647
- return passthrough;
2648
- const next = passthrough[rIdx + 1];
2649
- if (next !== undefined && !next.startsWith("-"))
2650
- return passthrough;
2651
- if (!(process.stdin.isTTY && process.stdout.isTTY))
2652
- return passthrough;
2653
- const sessions = listGeminiSessions(cwd);
2654
- if (!sessions.length) {
2655
- console.error("imp: no saved gemini chats for this project yet — starting fresh");
2656
- return passthrough.filter((_, i) => i !== rIdx);
2657
- }
2658
- const chosen = await pickGeminiSession(sessions, basename5(cwd));
2659
- if (chosen === null)
2660
- return passthrough.filter((_, i) => i !== rIdx);
2661
- const out = [...passthrough];
2662
- out.splice(rIdx + 1, 0, chosen);
2663
- return out;
2664
- }
2665
2707
  var here, pkgRoot, cmd, rest, asImp;
2666
2708
  var init_cli = __esm(async () => {
2667
2709
  init_resolve();
@@ -2711,7 +2753,7 @@ var init_cli = __esm(async () => {
2711
2753
  case "lair": {
2712
2754
  const home = requireVaultHome();
2713
2755
  const { backend, skipPermissions, passthrough } = resolveLaunch(rest, { agent: readDefaultAgent(), yolo: readSkipPermissions() });
2714
- const resumed = await resolveGeminiResume(backend.name, passthrough, home);
2756
+ const resumed = backend.resolveResume ? await backend.resolveResume(passthrough, home) : passthrough;
2715
2757
  const spec = assembleSession({ cwd: home, vaultProject: home, pkgRoot, passthrough: resumed, skipPermissions, model: readDefaultModel() });
2716
2758
  process.exit(launchBackend(backend, home, backend.renderArgs(spec), spec.env));
2717
2759
  }
@@ -2735,8 +2777,8 @@ var init_cli = __esm(async () => {
2735
2777
  console.log("set: imprnt agent <claude|gemini> override one session: imp --gemini | imp --claude");
2736
2778
  break;
2737
2779
  }
2738
- if (name !== "claude" && name !== "gemini") {
2739
- console.error(`unknown agent "${name}" — valid: claude, gemini`);
2780
+ if (!(name in backends)) {
2781
+ console.error(`unknown agent "${name}" — valid: ${Object.keys(backends).join(", ")}`);
2740
2782
  process.exit(1);
2741
2783
  }
2742
2784
  const r = setDefaultAgent(name);
@@ -2774,7 +2816,7 @@ var init_cli = __esm(async () => {
2774
2816
  const m = readDefaultModel();
2775
2817
  console.log(`default model: ${m ?? "(agent's own default)"}`);
2776
2818
  console.log("set: imprnt model <alias|id> clear: imprnt model off");
2777
- console.log("gemini aliases: pro=gemini-3.1-pro-preview, flash=gemini-3.5-flash, pro25=gemini-2.5-pro, lite=gemini-3.1-flash-lite, gemma31, gemma26");
2819
+ console.log(`gemini aliases: ${Object.entries(GEMINI_MODEL_ALIASES).map(([k, v]) => `${k}=${v}`).join(", ")}`);
2778
2820
  break;
2779
2821
  }
2780
2822
  if (arg === "off" || arg === "default" || arg === "none") {
@@ -2999,7 +3041,7 @@ enable: imprnt plugin add <name> disable: imprnt plugin rm <name> [--purge]`);
2999
3041
  const inPlace = isVaultProject(process.cwd());
3000
3042
  const display = inPlace ? process.cwd() : "~/imprnt";
3001
3043
  const fallback = inPlace ? process.cwd() : join15(homedir4(), "imprnt");
3002
- const rl = createInterface({ input: process.stdin, output: process.stdout });
3044
+ const rl = createInterface2({ input: process.stdin, output: process.stdout });
3003
3045
  const answer = (await rl.question(`vault location [${display}]: `)).trim();
3004
3046
  rl.close();
3005
3047
  target = answer ? resolvePath(answer) : fallback;
@@ -3111,7 +3153,7 @@ enable: imprnt plugin add <name> disable: imprnt plugin rm <name> [--purge]`);
3111
3153
  }
3112
3154
  const raw = bare ? [] : [cmd, ...rest];
3113
3155
  const { backend, skipPermissions, passthrough } = resolveLaunch(raw, { agent: readDefaultAgent(), yolo: readSkipPermissions() });
3114
- const resumed = await resolveGeminiResume(backend.name, passthrough, process.cwd());
3156
+ const resumed = backend.resolveResume ? await backend.resolveResume(passthrough, process.cwd()) : passthrough;
3115
3157
  const spec = assembleSession({ cwd: process.cwd(), vaultProject: home, pkgRoot, passthrough: resumed, skipPermissions, model: readDefaultModel() });
3116
3158
  process.exit(launchBackend(backend, process.cwd(), backend.renderArgs(spec), spec.env));
3117
3159
  }
package/dist/imp.js CHANGED
@@ -766,14 +766,9 @@ function registerVault(path, opts = {}) {
766
766
  const name = opts.name ?? "personal";
767
767
  reg.vaults[name] = path;
768
768
  reg.default = name;
769
- const p = configPath();
770
- try {
771
- mkdirSync2(dirname2(p), { recursive: true });
772
- writeFileSync5(p, JSON.stringify(reg, null, 2) + `
773
- `);
774
- } catch (e) {
775
- return { status: "error", current: path, error: e instanceof Error ? e.message : String(e) };
776
- }
769
+ const w = writeRegistry(reg);
770
+ if (!w.ok)
771
+ return { status: "error", current: path, error: w.error };
777
772
  return { status: "registered", current: path };
778
773
  }
779
774
  function readDefaultAgent() {
@@ -842,8 +837,9 @@ var init_registry = () => {};
842
837
  // scripts/lib/launch.ts
843
838
  import { existsSync as existsSync7, mkdtempSync as mkdtempSync2, readFileSync as readFileSync7, realpathSync, rmSync as rmSync3, statSync as statSync5, writeFileSync as writeFileSync6 } from "node:fs";
844
839
  import { homedir as homedir2, tmpdir as tmpdir2 } from "node:os";
845
- import { isAbsolute, join as join8, resolve as resolve4, sep as sep2 } from "node:path";
840
+ import { basename as basename2, isAbsolute, join as join8, resolve as resolve4, sep as sep2 } from "node:path";
846
841
  import { spawnSync as spawnSync2 } from "node:child_process";
842
+ import { createInterface } from "node:readline/promises";
847
843
  function importPath(root, target) {
848
844
  if (target.startsWith("~/"))
849
845
  return join8(homedir2(), target.slice(2));
@@ -1013,7 +1009,12 @@ function claudeRenderArgs(spec) {
1013
1009
  const systemPrompt = spec.insideProject ? spec.globals : joinFragments([spec.cast, spec.pointer, spec.globals]);
1014
1010
  const harness = spec.pluginsRoot ? harnessFlags(spec.pluginsRoot) : [];
1015
1011
  const extra = spec.addDirs.flatMap((d) => ["--add-dir", d]);
1016
- const pass = spec.skipPermissions && !spec.passthrough.includes("--dangerously-skip-permissions") ? ["--dangerously-skip-permissions", ...spec.passthrough] : spec.passthrough;
1012
+ const lead = [];
1013
+ if (spec.skipPermissions && !spec.passthrough.includes("--dangerously-skip-permissions"))
1014
+ lead.push("--dangerously-skip-permissions");
1015
+ if (spec.model && !spec.passthrough.some((a) => a === "--model" || a.startsWith("--model=")))
1016
+ lead.push("--model", spec.model);
1017
+ const pass = lead.length ? [...lead, ...spec.passthrough] : spec.passthrough;
1017
1018
  const body = systemPrompt ? mergeFragment(pass, systemPrompt, extra) : [...pass, ...extra];
1018
1019
  return [...harness, ...body];
1019
1020
  }
@@ -1023,6 +1024,19 @@ function expandGeminiModel(m) {
1023
1024
  function escapeGeminiImports(text) {
1024
1025
  return text.replaceAll("@", "\\@");
1025
1026
  }
1027
+ function trackGeminiTempDir(dir) {
1028
+ geminiTempDirs.add(dir);
1029
+ if (geminiCleanupArmed)
1030
+ return;
1031
+ geminiCleanupArmed = true;
1032
+ process.on("exit", () => {
1033
+ for (const d of geminiTempDirs) {
1034
+ try {
1035
+ rmSync3(d, { recursive: true, force: true });
1036
+ } catch {}
1037
+ }
1038
+ });
1039
+ }
1026
1040
  function geminiRenderArgs(spec) {
1027
1041
  const includes = [];
1028
1042
  const systemPrompt = joinFragments([spec.cast, spec.pointer, spec.globals]);
@@ -1030,11 +1044,7 @@ function geminiRenderArgs(spec) {
1030
1044
  const dir = mkdtempSync2(join8(tmpdir2(), "imprnt-gemini-"));
1031
1045
  writeFileSync6(join8(dir, "GEMINI.md"), escapeGeminiImports(systemPrompt) + `
1032
1046
  `);
1033
- process.on("exit", () => {
1034
- try {
1035
- rmSync3(dir, { recursive: true, force: true });
1036
- } catch {}
1037
- });
1047
+ trackGeminiTempDir(dir);
1038
1048
  includes.push(dir);
1039
1049
  }
1040
1050
  includes.push(...spec.addDirs);
@@ -1049,16 +1059,23 @@ function geminiRenderArgs(spec) {
1049
1059
  lead.push("--skip-trust");
1050
1060
  }
1051
1061
  const pass = [...spec.passthrough];
1052
- const mIdx = pass.findIndex((a) => a === "-m" || a === "--model");
1053
- if (mIdx >= 0 && pass[mIdx + 1] !== undefined) {
1054
- pass[mIdx + 1] = expandGeminiModel(pass[mIdx + 1]);
1055
- } else if (mIdx < 0 && spec.model) {
1062
+ const mTerm = pass.indexOf("--");
1063
+ const mEnd = mTerm < 0 ? pass.length : mTerm;
1064
+ const isModelFlag = (a) => a === "-m" || a === "--model" || a.startsWith("-m=") || a.startsWith("--model=");
1065
+ const mIdx = pass.slice(0, mEnd).findIndex(isModelFlag);
1066
+ if (mIdx >= 0) {
1067
+ const tok = pass[mIdx];
1068
+ const eq = tok.indexOf("=");
1069
+ if (eq >= 0)
1070
+ pass[mIdx] = tok.slice(0, eq + 1) + expandGeminiModel(tok.slice(eq + 1));
1071
+ else if (mIdx + 1 < mEnd && pass[mIdx + 1] !== undefined)
1072
+ pass[mIdx + 1] = expandGeminiModel(pass[mIdx + 1]);
1073
+ } else if (spec.model) {
1056
1074
  pass.unshift("-m", expandGeminiModel(spec.model));
1057
1075
  }
1058
- const rIdx = pass.findIndex((a) => a === "-r" || a === "--resume");
1059
- if (rIdx >= 0 && (pass[rIdx + 1] === undefined || pass[rIdx + 1].startsWith("-"))) {
1076
+ const rIdx = valuelessResumeIndex(pass);
1077
+ if (rIdx >= 0)
1060
1078
  pass.splice(rIdx + 1, 0, "latest");
1061
- }
1062
1079
  const flags = includes.length ? ["--include-directories", includes.join(",")] : [];
1063
1080
  return [...lead, ...flags, ...pass];
1064
1081
  }
@@ -1071,11 +1088,88 @@ function parseGeminiSessions(stdout) {
1071
1088
  }
1072
1089
  return out;
1073
1090
  }
1091
+ function valuelessResumeIndex(args) {
1092
+ const term = args.indexOf("--");
1093
+ const end = term < 0 ? args.length : term;
1094
+ let i = -1;
1095
+ for (let k = 0;k < end; k++) {
1096
+ if (args[k] === "-r" || args[k] === "--resume") {
1097
+ i = k;
1098
+ break;
1099
+ }
1100
+ }
1101
+ if (i < 0)
1102
+ return -1;
1103
+ const next = i + 1 < end ? args[i + 1] : undefined;
1104
+ return next === undefined || next.startsWith("-") ? i : -1;
1105
+ }
1106
+ function listGeminiSessions(cwd) {
1107
+ const r = spawnSync2("gemini", ["--list-sessions", "--skip-trust"], { cwd, encoding: "utf8" });
1108
+ if (r.error || r.status !== 0)
1109
+ return { sessions: [], ran: false };
1110
+ const stdout = r.stdout || "";
1111
+ const sessions = parseGeminiSessions(stdout);
1112
+ if (!sessions.length && stdout.trim() && !/\(\s*0\s*\)|no sessions/i.test(stdout)) {
1113
+ console.error("imp: couldn't parse gemini's session list (its format may have changed) - starting fresh");
1114
+ }
1115
+ return { sessions, ran: true };
1116
+ }
1117
+ async function pickGeminiSession(sessions, label) {
1118
+ const cols = process.stdout.columns || 80;
1119
+ const nameW = Math.max(20, Math.min(64, cols - 16));
1120
+ const trunc = (s) => s.length > nameW ? s.slice(0, nameW - 1) + "…" : s;
1121
+ process.stderr.write(`
1122
+ resume a chat - gemini · ${label}
1123
+
1124
+ `);
1125
+ sessions.forEach((s, i) => {
1126
+ process.stderr.write(` ${String(i + 1).padStart(2)} ${trunc(s.name).padEnd(nameW)} ${s.age.replace(/\s*ago$/, "")}
1127
+ `);
1128
+ });
1129
+ process.stderr.write(`
1130
+ `);
1131
+ const rl = createInterface({ input: process.stdin, output: process.stderr });
1132
+ const answer = (await rl.question(" number to resume · enter for newest · q to cancel › ")).trim();
1133
+ rl.close();
1134
+ if (answer === "")
1135
+ return "latest";
1136
+ if (answer.toLowerCase() === "q")
1137
+ return null;
1138
+ const n = Number(answer);
1139
+ if (Number.isInteger(n) && n >= 1 && n <= sessions.length)
1140
+ return sessions[n - 1].id;
1141
+ process.stderr.write(` not a valid choice - starting a fresh session
1142
+ `);
1143
+ return null;
1144
+ }
1145
+ async function geminiResolveResume(passthrough, cwd) {
1146
+ const rIdx = valuelessResumeIndex(passthrough);
1147
+ if (rIdx < 0)
1148
+ return passthrough;
1149
+ if (!(process.stdin.isTTY && process.stdout.isTTY))
1150
+ return passthrough;
1151
+ const { sessions, ran } = listGeminiSessions(cwd);
1152
+ if (!ran)
1153
+ return passthrough;
1154
+ if (!sessions.length) {
1155
+ console.error("imp: no saved gemini chats for this project yet - starting fresh");
1156
+ return passthrough.filter((_, i) => i !== rIdx);
1157
+ }
1158
+ const chosen = await pickGeminiSession(sessions, basename2(cwd));
1159
+ if (chosen === null)
1160
+ return passthrough.filter((_, i) => i !== rIdx);
1161
+ const out = [...passthrough];
1162
+ out.splice(rIdx + 1, 0, chosen);
1163
+ return out;
1164
+ }
1074
1165
  function resolveLaunch(passthrough, config = {}) {
1166
+ const term = passthrough.indexOf("--");
1167
+ const head = term < 0 ? passthrough : passthrough.slice(0, term);
1168
+ const tail = term < 0 ? [] : passthrough.slice(term);
1075
1169
  let agentFlag;
1076
1170
  let yoloFlag;
1077
1171
  const rest = [];
1078
- for (const a of passthrough) {
1172
+ for (const a of head) {
1079
1173
  if (a === "--gemini")
1080
1174
  agentFlag = agentFlag ?? "gemini";
1081
1175
  else if (a === "--claude")
@@ -1093,9 +1187,10 @@ function resolveLaunch(passthrough, config = {}) {
1093
1187
  console.error(`imp: unknown agent "${name}" - falling back to claude (valid: ${Object.keys(backends).join(", ")})`);
1094
1188
  backend = claudeBackend;
1095
1189
  }
1096
- const envYolo = "IMPRNT_YOLO" in process.env ? !["0", "false", ""].includes(process.env.IMPRNT_YOLO ?? "") : undefined;
1190
+ const yoloEnv = process.env.IMPRNT_YOLO;
1191
+ const envYolo = yoloEnv === undefined ? undefined : !["0", "false", "", "off", "no"].includes(yoloEnv.toLowerCase());
1097
1192
  const skipPermissions = yoloFlag ?? envYolo ?? config.yolo ?? false;
1098
- return { backend, skipPermissions, passthrough: rest };
1193
+ return { backend, skipPermissions, passthrough: [...rest, ...tail] };
1099
1194
  }
1100
1195
  function launchBackend(backend, cwd, args, env = process.env) {
1101
1196
  if (!isDir(cwd)) {
@@ -1110,7 +1205,7 @@ function launchBackend(backend, cwd, args, env = process.env) {
1110
1205
  }
1111
1206
  return r.status ?? 1;
1112
1207
  }
1113
- var claudeBackend, GEMINI_MODEL_ALIASES, geminiBackend, backends;
1208
+ var claudeBackend, GEMINI_MODEL_ALIASES, geminiTempDirs, geminiCleanupArmed = false, geminiBackend, backends;
1114
1209
  var init_launch = __esm(() => {
1115
1210
  init_plugins();
1116
1211
  init_global();
@@ -1127,10 +1222,12 @@ var init_launch = __esm(() => {
1127
1222
  gemma31: "gemma-4-31b-it",
1128
1223
  gemma26: "gemma-4-26b-a4b-it"
1129
1224
  };
1225
+ geminiTempDirs = new Set;
1130
1226
  geminiBackend = {
1131
1227
  name: "gemini",
1132
1228
  missingHint: "Install the Gemini CLI first: npm i -g @google/gemini-cli",
1133
- renderArgs: geminiRenderArgs
1229
+ renderArgs: geminiRenderArgs,
1230
+ resolveResume: geminiResolveResume
1134
1231
  };
1135
1232
  backends = {
1136
1233
  claude: claudeBackend,
@@ -1173,7 +1270,7 @@ var init_manifest = () => {};
1173
1270
  var exports_ingest = {};
1174
1271
  import { readFileSync as readFileSync9, writeFileSync as writeFileSync8, mkdirSync as mkdirSync3, existsSync as existsSync9, copyFileSync, readdirSync as readdirSync5, rmSync as rmSync4, statSync as statSync6 } from "node:fs";
1175
1272
  import { createHash } from "node:crypto";
1176
- import { basename as basename2, extname, join as join10, relative as relative3 } from "node:path";
1273
+ import { basename as basename3, extname, join as join10, relative as relative3 } from "node:path";
1177
1274
  function slugify(s) {
1178
1275
  return s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 60);
1179
1276
  }
@@ -1254,7 +1351,7 @@ function applyStaged(staged, vault) {
1254
1351
  return "error";
1255
1352
  }
1256
1353
  const title = text.match(/^#\s+(.+)$/m)?.[1]?.trim() ?? "";
1257
- const slug = deriveSlug([fmScalar2(fm, "slug"), title, basename2(staged, ".md")]);
1354
+ const slug = deriveSlug([fmScalar2(fm, "slug"), title, basename3(staged, ".md")]);
1258
1355
  if (!slug) {
1259
1356
  console.error(` ✗ ${staged}: can't derive a slug - slug:, the H1 title, and the filename all slugify to nothing`);
1260
1357
  return "error";
@@ -1481,7 +1578,7 @@ ${filed} filed, ${noop} no-op, ${conflict} conflict, ${error} error.`);
1481
1578
  console.log(`unchanged (hash ${hash}) — skipping ${src}. note: ${prior.note}`);
1482
1579
  process.exit(0);
1483
1580
  }
1484
- fname = basename2(src);
1581
+ fname = basename3(src);
1485
1582
  dateMatch = fname.match(/(\d{4}-\d{2}-\d{2})/) || text.match(/^\s*date:\s*(\d{4}-\d{2}-\d{2})/im);
1486
1583
  date = dateMatch ? dateMatch[1] : new Date().toISOString().slice(0, 10);
1487
1584
  lines = text.split(/\r?\n/);
@@ -1515,7 +1612,7 @@ ${filed} filed, ${noop} no-op, ${conflict} conflict, ${error} error.`);
1515
1612
  }
1516
1613
  recurringSpeaker = [...turnsBySpeaker.values()].some((n) => n >= 2);
1517
1614
  isTranscript = isFile && looksLikeTranscript(speakers, turnCount, contentLines, recurringSpeaker);
1518
- slugBasis = slugHint || subject || [...speakers].join("-") || (isFile ? basename2(src, extname(src)) : text.slice(0, 60));
1615
+ slugBasis = slugHint || subject || [...speakers].join("-") || (isFile ? basename3(src, extname(src)) : text.slice(0, 60));
1519
1616
  subjectSlug = slugify(slugBasis) || "source";
1520
1617
  noteSlug = `${date}-${subjectSlug}`;
1521
1618
  rawDir = join10(vault, "..", "raw", isTranscript ? "transcripts" : "adhoc");
@@ -1756,7 +1853,7 @@ var init_tags = __esm(() => {
1756
1853
  // scripts/recall.ts
1757
1854
  var exports_recall = {};
1758
1855
  import { lstatSync as lstatSync2, readdirSync as readdirSync6, readFileSync as readFileSync11 } from "node:fs";
1759
- import { basename as basename3, join as join12, relative as relative4 } from "node:path";
1856
+ import { basename as basename4, join as join12, relative as relative4 } from "node:path";
1760
1857
  function walk2(dir2) {
1761
1858
  const out = [];
1762
1859
  for (const entry of readdirSync6(dir2)) {
@@ -1973,7 +2070,7 @@ var init_recall = __esm(() => {
1973
2070
  for (const t of tokens)
1974
2071
  tf.set(t, (tf.get(t) ?? 0) + weight);
1975
2072
  };
1976
- add([...tokenize(titleText), ...tokenize(basename3(path, ".md"))], TITLE_BOOST);
2073
+ add([...tokenize(titleText), ...tokenize(basename4(path, ".md"))], TITLE_BOOST);
1977
2074
  add(tokenize(aliases), TITLE_BOOST);
1978
2075
  add(tags.flatMap(tokenize), TAG_BOOST);
1979
2076
  add(tokenize(summary), SUMMARY_BOOST);
@@ -2038,7 +2135,7 @@ var init_recall = __esm(() => {
2038
2135
  var exports_snapshot = {};
2039
2136
  import { readdirSync as readdirSync7, readFileSync as readFileSync12, copyFileSync as copyFileSync2, mkdirSync as mkdirSync4, existsSync as existsSync11, statSync as statSync7, lstatSync as lstatSync3 } from "node:fs";
2040
2137
  import { createHash as createHash2 } from "node:crypto";
2041
- import { join as join13, relative as relative5, basename as basename4, extname as extname2, resolve as resolve5, sep as sep3 } from "node:path";
2138
+ import { join as join13, relative as relative5, basename as basename5, extname as extname2, resolve as resolve5, sep as sep3 } from "node:path";
2042
2139
  function destWithinRaw(rawRoot, dest) {
2043
2140
  const rootResolved = resolve5(rawRoot);
2044
2141
  const destResolved = resolve5(rootResolved, dest);
@@ -2050,7 +2147,7 @@ function collect(p, base) {
2050
2147
  let skippedLinks = 0;
2051
2148
  const st = statSync7(p);
2052
2149
  if (st.isFile())
2053
- return { files: [{ abs: p, rel: basename4(p) }], skippedLinks };
2150
+ return { files: [{ abs: p, rel: basename5(p) }], skippedLinks };
2054
2151
  const out = [];
2055
2152
  const walk3 = (dir2) => {
2056
2153
  for (const entry of readdirSync7(dir2)) {
@@ -2528,10 +2625,10 @@ all plugin checks passed.`);
2528
2625
  var exports_cli = {};
2529
2626
  import { cpSync as cpSync3, mkdirSync as mkdirSync5, chmodSync, existsSync as existsSync13, readFileSync as readFileSync14, lstatSync as lstatSync4, statSync as statSync9, realpathSync as realpathSync2 } from "node:fs";
2530
2627
  import { spawnSync as spawnSync4 } from "node:child_process";
2531
- import { join as join15, dirname as dirname4, resolve as resolve6, basename as basename5 } from "node:path";
2628
+ import { join as join15, dirname as dirname4, resolve as resolve6, basename as basename6 } from "node:path";
2532
2629
  import { homedir as homedir4 } from "node:os";
2533
2630
  import { fileURLToPath } from "node:url";
2534
- import { createInterface } from "node:readline/promises";
2631
+ import { createInterface as createInterface2 } from "node:readline/promises";
2535
2632
  function pluginRoot() {
2536
2633
  const local = projectRoot();
2537
2634
  if (process.env.IMPRNT_ROOT || process.env.IMPRINT_ROOT)
@@ -2578,7 +2675,7 @@ function physicalTarget(target) {
2578
2675
  const parent = dirname4(existing);
2579
2676
  if (parent === existing)
2580
2677
  return target;
2581
- tail.unshift(basename5(existing));
2678
+ tail.unshift(basename6(existing));
2582
2679
  existing = parent;
2583
2680
  }
2584
2681
  try {
@@ -2607,61 +2704,6 @@ function controlSlot(dst) {
2607
2704
  return "keep";
2608
2705
  return "blocked-non-regular-file";
2609
2706
  }
2610
- function listGeminiSessions(cwd) {
2611
- const r = spawnSync4("gemini", ["--list-sessions", "--skip-trust"], { cwd, encoding: "utf8" });
2612
- return parseGeminiSessions(r.stdout || "");
2613
- }
2614
- async function pickGeminiSession(sessions, label) {
2615
- const cols = process.stdout.columns || 80;
2616
- const nameW = Math.max(20, Math.min(64, cols - 16));
2617
- const trunc = (s) => s.length > nameW ? s.slice(0, nameW - 1) + "…" : s;
2618
- process.stderr.write(`
2619
- resume a chat — gemini · ${label}
2620
-
2621
- `);
2622
- sessions.forEach((s, i) => {
2623
- process.stderr.write(` ${String(i + 1).padStart(2)} ${trunc(s.name).padEnd(nameW)} ${s.age.replace(/\s*ago$/, "")}
2624
- `);
2625
- });
2626
- process.stderr.write(`
2627
- `);
2628
- const rl = createInterface({ input: process.stdin, output: process.stderr });
2629
- const answer = (await rl.question(" number to resume · enter for newest · q to cancel › ")).trim();
2630
- rl.close();
2631
- if (answer === "")
2632
- return "latest";
2633
- if (answer.toLowerCase() === "q")
2634
- return null;
2635
- const n = Number(answer);
2636
- if (Number.isInteger(n) && n >= 1 && n <= sessions.length)
2637
- return sessions[n - 1].id;
2638
- process.stderr.write(` not a valid choice — starting a fresh session
2639
- `);
2640
- return null;
2641
- }
2642
- async function resolveGeminiResume(backendName, passthrough, cwd) {
2643
- if (backendName !== "gemini")
2644
- return passthrough;
2645
- const rIdx = passthrough.findIndex((a) => a === "-r" || a === "--resume");
2646
- if (rIdx < 0)
2647
- return passthrough;
2648
- const next = passthrough[rIdx + 1];
2649
- if (next !== undefined && !next.startsWith("-"))
2650
- return passthrough;
2651
- if (!(process.stdin.isTTY && process.stdout.isTTY))
2652
- return passthrough;
2653
- const sessions = listGeminiSessions(cwd);
2654
- if (!sessions.length) {
2655
- console.error("imp: no saved gemini chats for this project yet — starting fresh");
2656
- return passthrough.filter((_, i) => i !== rIdx);
2657
- }
2658
- const chosen = await pickGeminiSession(sessions, basename5(cwd));
2659
- if (chosen === null)
2660
- return passthrough.filter((_, i) => i !== rIdx);
2661
- const out = [...passthrough];
2662
- out.splice(rIdx + 1, 0, chosen);
2663
- return out;
2664
- }
2665
2707
  var here, pkgRoot, cmd, rest, asImp;
2666
2708
  var init_cli = __esm(async () => {
2667
2709
  init_resolve();
@@ -2711,7 +2753,7 @@ var init_cli = __esm(async () => {
2711
2753
  case "lair": {
2712
2754
  const home = requireVaultHome();
2713
2755
  const { backend, skipPermissions, passthrough } = resolveLaunch(rest, { agent: readDefaultAgent(), yolo: readSkipPermissions() });
2714
- const resumed = await resolveGeminiResume(backend.name, passthrough, home);
2756
+ const resumed = backend.resolveResume ? await backend.resolveResume(passthrough, home) : passthrough;
2715
2757
  const spec = assembleSession({ cwd: home, vaultProject: home, pkgRoot, passthrough: resumed, skipPermissions, model: readDefaultModel() });
2716
2758
  process.exit(launchBackend(backend, home, backend.renderArgs(spec), spec.env));
2717
2759
  }
@@ -2735,8 +2777,8 @@ var init_cli = __esm(async () => {
2735
2777
  console.log("set: imprnt agent <claude|gemini> override one session: imp --gemini | imp --claude");
2736
2778
  break;
2737
2779
  }
2738
- if (name !== "claude" && name !== "gemini") {
2739
- console.error(`unknown agent "${name}" — valid: claude, gemini`);
2780
+ if (!(name in backends)) {
2781
+ console.error(`unknown agent "${name}" — valid: ${Object.keys(backends).join(", ")}`);
2740
2782
  process.exit(1);
2741
2783
  }
2742
2784
  const r = setDefaultAgent(name);
@@ -2774,7 +2816,7 @@ var init_cli = __esm(async () => {
2774
2816
  const m = readDefaultModel();
2775
2817
  console.log(`default model: ${m ?? "(agent's own default)"}`);
2776
2818
  console.log("set: imprnt model <alias|id> clear: imprnt model off");
2777
- console.log("gemini aliases: pro=gemini-3.1-pro-preview, flash=gemini-3.5-flash, pro25=gemini-2.5-pro, lite=gemini-3.1-flash-lite, gemma31, gemma26");
2819
+ console.log(`gemini aliases: ${Object.entries(GEMINI_MODEL_ALIASES).map(([k, v]) => `${k}=${v}`).join(", ")}`);
2778
2820
  break;
2779
2821
  }
2780
2822
  if (arg === "off" || arg === "default" || arg === "none") {
@@ -2999,7 +3041,7 @@ enable: imprnt plugin add <name> disable: imprnt plugin rm <name> [--purge]`);
2999
3041
  const inPlace = isVaultProject(process.cwd());
3000
3042
  const display = inPlace ? process.cwd() : "~/imprnt";
3001
3043
  const fallback = inPlace ? process.cwd() : join15(homedir4(), "imprnt");
3002
- const rl = createInterface({ input: process.stdin, output: process.stdout });
3044
+ const rl = createInterface2({ input: process.stdin, output: process.stdout });
3003
3045
  const answer = (await rl.question(`vault location [${display}]: `)).trim();
3004
3046
  rl.close();
3005
3047
  target = answer ? resolvePath(answer) : fallback;
@@ -3111,7 +3153,7 @@ enable: imprnt plugin add <name> disable: imprnt plugin rm <name> [--purge]`);
3111
3153
  }
3112
3154
  const raw = bare ? [] : [cmd, ...rest];
3113
3155
  const { backend, skipPermissions, passthrough } = resolveLaunch(raw, { agent: readDefaultAgent(), yolo: readSkipPermissions() });
3114
- const resumed = await resolveGeminiResume(backend.name, passthrough, process.cwd());
3156
+ const resumed = backend.resolveResume ? await backend.resolveResume(passthrough, process.cwd()) : passthrough;
3115
3157
  const spec = assembleSession({ cwd: process.cwd(), vaultProject: home, pkgRoot, passthrough: resumed, skipPermissions, model: readDefaultModel() });
3116
3158
  process.exit(launchBackend(backend, process.cwd(), backend.renderArgs(spec), spec.env));
3117
3159
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "imprnt",
3
- "version": "0.1.3-edge.60",
3
+ "version": "0.1.3-edge.61",
4
4
  "description": "Deterministic-first, plain-markdown knowledge vault. Code does the bulk transform. The LLM only touches the irreducibly-semantic 20%. Native grep plus BM25 retrieval, zero MCP on the vault.",
5
5
  "type": "module",
6
6
  "bin": {