imprnt 0.1.3-edge.54 → 0.1.3-edge.58

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 +270 -46
  2. package/dist/imp.js +270 -46
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -737,7 +737,13 @@ function readRegistry() {
737
737
  for (const [k, v] of Object.entries(raw?.vaults ?? {}))
738
738
  if (typeof v === "string")
739
739
  vaults[k] = v;
740
- return { default: typeof raw?.default === "string" ? raw.default : undefined, vaults };
740
+ return {
741
+ default: typeof raw?.default === "string" ? raw.default : undefined,
742
+ vaults,
743
+ defaultAgent: typeof raw?.defaultAgent === "string" ? raw.defaultAgent : undefined,
744
+ skipPermissions: typeof raw?.skipPermissions === "boolean" ? raw.skipPermissions : undefined,
745
+ defaultModel: typeof raw?.defaultModel === "string" ? raw.defaultModel : undefined
746
+ };
741
747
  } catch {
742
748
  return { vaults: {} };
743
749
  }
@@ -770,6 +776,41 @@ function registerVault(path, opts = {}) {
770
776
  }
771
777
  return { status: "registered", current: path };
772
778
  }
779
+ function readDefaultAgent() {
780
+ return readRegistry().defaultAgent;
781
+ }
782
+ function setDefaultAgent(name) {
783
+ const reg = readRegistry();
784
+ reg.defaultAgent = name;
785
+ return writeRegistry(reg);
786
+ }
787
+ function readSkipPermissions() {
788
+ return readRegistry().skipPermissions ?? false;
789
+ }
790
+ function setSkipPermissions(on) {
791
+ const reg = readRegistry();
792
+ reg.skipPermissions = on;
793
+ return writeRegistry(reg);
794
+ }
795
+ function readDefaultModel() {
796
+ return readRegistry().defaultModel;
797
+ }
798
+ function setDefaultModel(model) {
799
+ const reg = readRegistry();
800
+ reg.defaultModel = model || undefined;
801
+ return writeRegistry(reg);
802
+ }
803
+ function writeRegistry(reg) {
804
+ const p = configPath();
805
+ try {
806
+ mkdirSync2(dirname2(p), { recursive: true });
807
+ writeFileSync5(p, JSON.stringify(reg, null, 2) + `
808
+ `);
809
+ } catch (e) {
810
+ return { ok: false, error: e instanceof Error ? e.message : String(e) };
811
+ }
812
+ return { ok: true };
813
+ }
773
814
  function isVaultProject(dir) {
774
815
  const vault = join7(dir, "vault");
775
816
  try {
@@ -799,8 +840,8 @@ function vaultProjectRoot(start = process.cwd()) {
799
840
  var init_registry = () => {};
800
841
 
801
842
  // scripts/lib/launch.ts
802
- import { existsSync as existsSync7, readFileSync as readFileSync7, realpathSync, statSync as statSync5 } from "node:fs";
803
- import { homedir as homedir2 } from "node:os";
843
+ import { existsSync as existsSync7, mkdtempSync as mkdtempSync2, readFileSync as readFileSync7, realpathSync, rmSync as rmSync3, statSync as statSync5, writeFileSync as writeFileSync6 } from "node:fs";
844
+ import { homedir as homedir2, tmpdir as tmpdir2 } from "node:os";
804
845
  import { isAbsolute, join as join8, resolve as resolve4, sep as sep2 } from "node:path";
805
846
  import { spawnSync as spawnSync2 } from "node:child_process";
806
847
  function importPath(root, target) {
@@ -829,7 +870,13 @@ function castFragment(root) {
829
870
  `);
830
871
  }
831
872
  function pointerFragment(pkgRoot, vaultProject) {
832
- const tpl = readFileSync7(join8(pkgRoot, "templates", "pointer.md"), "utf8");
873
+ let tpl;
874
+ try {
875
+ tpl = readFileSync7(join8(pkgRoot, "templates", "pointer.md"), "utf8");
876
+ } catch {
877
+ console.error(`imp: pointer template missing at ${join8(pkgRoot, "templates", "pointer.md")} - launching without the vault pointer`);
878
+ return "";
879
+ }
833
880
  return tpl.replaceAll("{{VAULT_PROJECT}}", () => vaultProject);
834
881
  }
835
882
  function substitutePluginDir(value, dir) {
@@ -930,53 +977,153 @@ function mergeFragment(pass, fragment, extraInject) {
930
977
  args.push(...inject);
931
978
  return args;
932
979
  }
933
- function buildLaunch(opts) {
934
- const pass = [...opts.passthrough ?? []];
980
+ function assembleSession(opts) {
981
+ const passthrough = [...opts.passthrough ?? []];
935
982
  const globalDir = opts.globalDir ?? defaultGlobalDir();
983
+ const skipPermissions = opts.skipPermissions ?? false;
984
+ const base = { cast: "", pointer: "", insideProject: false, addDirs: [], skipPermissions, model: opts.model, passthrough };
936
985
  if (!opts.vaultProject) {
937
- const globals2 = globalFragment(globalDir);
938
- if (!globals2)
939
- return { args: pass, env: process.env };
940
- return { args: mergeFragment(pass, globals2, []), env: process.env };
986
+ return { ...base, globals: globalFragment(globalDir) || "", env: process.env };
941
987
  }
942
988
  if (!isDir(opts.vaultProject)) {
943
- console.error(`imp: vault project not found at ${opts.vaultProject} - launching plain claude (re-run \`imprnt init\` there, or fix IMPRNT_ROOT)`);
944
- return { args: pass, env: process.env };
989
+ console.error(`imp: vault project not found at ${opts.vaultProject} - launching a plain session (re-run \`imprnt init\` there, or fix IMPRNT_ROOT)`);
990
+ return { ...base, globals: "", env: process.env };
945
991
  }
946
- const harness = harnessFlags(opts.vaultProject);
947
992
  const projectPlugins = new Set(enabledPluginDirs(opts.vaultProject));
948
- if (isInside(opts.cwd, opts.vaultProject)) {
949
- const globals2 = globalFragment(globalDir, projectPlugins);
950
- const args2 = globals2 ? mergeFragment(pass, globals2, []) : pass;
951
- return { args: [...harness, ...args2], env: childEnv(opts.vaultProject) };
952
- }
953
- const globals = globalFragment(globalDir, projectPlugins);
954
- const fragment = [castFragment(opts.vaultProject), pointerFragment(opts.pkgRoot, opts.vaultProject), globals].filter(Boolean).join(`
993
+ const inside = isInside(opts.cwd, opts.vaultProject);
994
+ return {
995
+ cast: castFragment(opts.vaultProject),
996
+ pointer: pointerFragment(opts.pkgRoot, opts.vaultProject),
997
+ globals: globalFragment(globalDir, projectPlugins) || "",
998
+ insideProject: inside,
999
+ addDirs: inside ? [] : [opts.vaultProject],
1000
+ pluginsRoot: opts.vaultProject,
1001
+ skipPermissions,
1002
+ model: opts.model,
1003
+ env: childEnv(opts.vaultProject),
1004
+ passthrough
1005
+ };
1006
+ }
1007
+ function joinFragments(parts) {
1008
+ return parts.filter(Boolean).join(`
955
1009
 
956
1010
  `);
957
- const args = mergeFragment(pass, fragment, ["--add-dir", opts.vaultProject]);
958
- return { args: [...harness, ...args], env: childEnv(opts.vaultProject) };
959
1011
  }
960
- function launchClaude(cwd, args, env = process.env) {
1012
+ function claudeRenderArgs(spec) {
1013
+ const systemPrompt = spec.insideProject ? spec.globals : joinFragments([spec.cast, spec.pointer, spec.globals]);
1014
+ const harness = spec.pluginsRoot ? harnessFlags(spec.pluginsRoot) : [];
1015
+ 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;
1017
+ const body = systemPrompt ? mergeFragment(pass, systemPrompt, extra) : [...pass, ...extra];
1018
+ return [...harness, ...body];
1019
+ }
1020
+ function expandGeminiModel(m) {
1021
+ return GEMINI_MODEL_ALIASES[m] ?? m;
1022
+ }
1023
+ function geminiRenderArgs(spec) {
1024
+ const includes = [];
1025
+ const systemPrompt = joinFragments([spec.cast, spec.pointer, spec.globals]);
1026
+ if (systemPrompt) {
1027
+ const dir = mkdtempSync2(join8(tmpdir2(), "imprnt-gemini-"));
1028
+ writeFileSync6(join8(dir, "GEMINI.md"), systemPrompt + `
1029
+ `);
1030
+ process.on("exit", () => {
1031
+ try {
1032
+ rmSync3(dir, { recursive: true, force: true });
1033
+ } catch {}
1034
+ });
1035
+ includes.push(dir);
1036
+ }
1037
+ includes.push(...spec.addDirs);
1038
+ if (spec.pluginsRoot && harnessFlags(spec.pluginsRoot).length) {
1039
+ console.error("imp: gemini does not host claude harness plugins (statusline, timemachine) - skipping them");
1040
+ }
1041
+ const lead = [];
1042
+ if (spec.skipPermissions) {
1043
+ if (!spec.passthrough.includes("--yolo") && !spec.passthrough.includes("-y"))
1044
+ lead.push("--yolo");
1045
+ if (!spec.passthrough.includes("--skip-trust"))
1046
+ lead.push("--skip-trust");
1047
+ }
1048
+ const pass = [...spec.passthrough];
1049
+ const mIdx = pass.findIndex((a) => a === "-m" || a === "--model");
1050
+ if (mIdx >= 0 && pass[mIdx + 1] !== undefined) {
1051
+ pass[mIdx + 1] = expandGeminiModel(pass[mIdx + 1]);
1052
+ } else if (mIdx < 0 && spec.model) {
1053
+ pass.unshift("-m", expandGeminiModel(spec.model));
1054
+ }
1055
+ const flags = includes.length ? ["--include-directories", includes.join(",")] : [];
1056
+ return [...lead, ...flags, ...pass];
1057
+ }
1058
+ function resolveLaunch(passthrough, config = {}) {
1059
+ let agentFlag;
1060
+ let yoloFlag;
1061
+ const rest = [];
1062
+ for (const a of passthrough) {
1063
+ if (a === "--gemini")
1064
+ agentFlag = agentFlag ?? "gemini";
1065
+ else if (a === "--claude")
1066
+ agentFlag = agentFlag ?? "claude";
1067
+ else if (a === "--yolo")
1068
+ yoloFlag = yoloFlag ?? true;
1069
+ else if (a === "--safe")
1070
+ yoloFlag = yoloFlag ?? false;
1071
+ else
1072
+ rest.push(a);
1073
+ }
1074
+ const name = agentFlag ?? process.env.IMPRNT_AGENT ?? config.agent ?? "claude";
1075
+ let backend = backends[name];
1076
+ if (!backend) {
1077
+ console.error(`imp: unknown agent "${name}" - falling back to claude (valid: ${Object.keys(backends).join(", ")})`);
1078
+ backend = claudeBackend;
1079
+ }
1080
+ const envYolo = "IMPRNT_YOLO" in process.env ? !["0", "false", ""].includes(process.env.IMPRNT_YOLO ?? "") : undefined;
1081
+ const skipPermissions = yoloFlag ?? envYolo ?? config.yolo ?? false;
1082
+ return { backend, skipPermissions, passthrough: rest };
1083
+ }
1084
+ function launchBackend(backend, cwd, args, env = process.env) {
961
1085
  if (!isDir(cwd)) {
962
1086
  console.error(`imp: vault project not found at ${cwd} — re-run \`imprnt init\` in its new location (add --register to switch the default)`);
963
1087
  return 1;
964
1088
  }
965
- const r = spawnSync2("claude", args, { cwd, stdio: "inherit", env });
1089
+ const r = spawnSync2(backend.name, args, { cwd, stdio: "inherit", env });
966
1090
  if (r.error) {
967
1091
  const code = r.error.code;
968
- console.error(code === "ENOENT" ? "imp: `claude` not found on PATH. Install Claude Code first: npm i -g @anthropic-ai/claude-code" : `imp: failed to launch claude: ${r.error.message}`);
1092
+ console.error(code === "ENOENT" ? `imp: \`${backend.name}\` not found on PATH. ${backend.missingHint}` : `imp: failed to launch ${backend.name}: ${r.error.message}`);
969
1093
  return 1;
970
1094
  }
971
1095
  return r.status ?? 1;
972
1096
  }
1097
+ var claudeBackend, GEMINI_MODEL_ALIASES, geminiBackend, backends;
973
1098
  var init_launch = __esm(() => {
974
1099
  init_plugins();
975
1100
  init_global();
1101
+ claudeBackend = {
1102
+ name: "claude",
1103
+ missingHint: "Install Claude Code first: npm i -g @anthropic-ai/claude-code",
1104
+ renderArgs: claudeRenderArgs
1105
+ };
1106
+ GEMINI_MODEL_ALIASES = {
1107
+ pro: "gemini-3.1-pro-preview",
1108
+ flash: "gemini-3.5-flash",
1109
+ pro25: "gemini-2.5-pro",
1110
+ lite: "gemini-3.1-flash-lite",
1111
+ gemma31: "gemma-4-31b-it",
1112
+ gemma26: "gemma-4-26b-a4b-it"
1113
+ };
1114
+ geminiBackend = {
1115
+ name: "gemini",
1116
+ missingHint: "Install the Gemini CLI first: npm i -g @google/gemini-cli",
1117
+ renderArgs: geminiRenderArgs
1118
+ };
1119
+ backends = {
1120
+ claude: claudeBackend,
1121
+ gemini: geminiBackend
1122
+ };
976
1123
  });
977
1124
 
978
1125
  // scripts/lib/manifest.ts
979
- import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, renameSync, existsSync as existsSync8 } from "node:fs";
1126
+ import { readFileSync as readFileSync8, writeFileSync as writeFileSync7, renameSync, existsSync as existsSync8 } from "node:fs";
980
1127
  import { join as join9 } from "node:path";
981
1128
  function manifestPath(vault) {
982
1129
  return join9(vault, ".manifest.json");
@@ -1000,7 +1147,7 @@ function loadManifest(vault) {
1000
1147
  function saveManifest(vault, m) {
1001
1148
  const p = manifestPath(vault);
1002
1149
  const tmp = `${p}.tmp.${process.pid}.${Date.now()}`;
1003
- writeFileSync6(tmp, JSON.stringify(m, null, 2) + `
1150
+ writeFileSync7(tmp, JSON.stringify(m, null, 2) + `
1004
1151
  `);
1005
1152
  renameSync(tmp, p);
1006
1153
  }
@@ -1008,7 +1155,7 @@ var init_manifest = () => {};
1008
1155
 
1009
1156
  // scripts/ingest.ts
1010
1157
  var exports_ingest = {};
1011
- import { readFileSync as readFileSync9, writeFileSync as writeFileSync7, mkdirSync as mkdirSync3, existsSync as existsSync9, copyFileSync, readdirSync as readdirSync5, rmSync as rmSync3, statSync as statSync6 } from "node:fs";
1158
+ 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";
1012
1159
  import { createHash } from "node:crypto";
1013
1160
  import { basename as basename2, extname, join as join10, relative as relative3 } from "node:path";
1014
1161
  function slugify(s) {
@@ -1124,7 +1271,7 @@ function applyStaged(staged, vault) {
1124
1271
  const existingHash = createHash("sha256").update(readFileSync9(notePath, "utf8")).digest("hex").slice(0, 16);
1125
1272
  if (existingHash === fileHash) {
1126
1273
  console.log(` = ${noteRel} already filed, identical content (hash ${fileHash}) — no-op`);
1127
- rmSync3(staged);
1274
+ rmSync4(staged);
1128
1275
  return "noop";
1129
1276
  }
1130
1277
  console.error(` ! ${noteRel} exists with DIFFERENT content — not overwriting (contradiction discipline)`);
@@ -1135,12 +1282,12 @@ function applyStaged(staged, vault) {
1135
1282
  if (!stagedSource && !priorSnapshot) {
1136
1283
  mkdirSync3(rawDir, { recursive: true });
1137
1284
  if (!existsSync9(rawPath))
1138
- writeFileSync7(rawPath, text);
1285
+ writeFileSync8(rawPath, text);
1139
1286
  }
1140
1287
  manifest[manifestKey] = { hash, note: notePath, ingested: new Date().toISOString(), raw: rawEntry };
1141
1288
  saveManifest(vault, manifest);
1142
1289
  mkdirSync3(join10(vault, folder), { recursive: true });
1143
- writeFileSync7(notePath, finalText);
1290
+ writeFileSync8(notePath, finalText);
1144
1291
  const participantsList = fmList2(fm, "participants");
1145
1292
  const participantsScalar = participantsList.length ? "" : fmScalar2(fm, "participants");
1146
1293
  const participants = (participantsScalar ? [participantsScalar] : participantsList).map(linkSlug);
@@ -1153,7 +1300,7 @@ function applyStaged(staged, vault) {
1153
1300
  flagNeedsReview(vault, `- [ ] unresolved person \`people/${personSlug}\` — from [[${noteRel}]] (applied from \`${staged}\`)`);
1154
1301
  }
1155
1302
  }
1156
- rmSync3(staged);
1303
+ rmSync4(staged);
1157
1304
  console.log(` ✓ filed ${noteRel} (type: ${type}${domain ? `, domain: ${domain}` : ""})`);
1158
1305
  if (stagedSource)
1159
1306
  console.log(` source -> ${rawEntry} (note's own source: kept - no redundant snapshot)`);
@@ -1369,7 +1516,7 @@ ${filed} filed, ${noop} no-op, ${conflict} conflict, ${error} error.`);
1369
1516
  if (isFile)
1370
1517
  copyFileSync(src, rawPath);
1371
1518
  else
1372
- writeFileSync7(rawPath, text);
1519
+ writeFileSync8(rawPath, text);
1373
1520
  }
1374
1521
  } catch (e) {
1375
1522
  console.error(`cannot write snapshot ${rawPath}: ${e instanceof Error ? e.message : e}`);
@@ -1466,7 +1613,7 @@ ${body}`;
1466
1613
  console.log(` hash8 collision with a distinct source -> trying \`events/${effectiveSlug}\` instead`);
1467
1614
  }
1468
1615
  }
1469
- writeFileSync7(notePath, note);
1616
+ writeFileSync8(notePath, note);
1470
1617
  manifest[manifestKey] = { hash, note: notePath, ingested: new Date().toISOString(), raw: rawPath };
1471
1618
  saveManifest(vault, manifest);
1472
1619
  unresolved = [...speakers].filter((name) => !personResolved(vault, slugify(name), name));
@@ -1484,7 +1631,7 @@ ${body}`;
1484
1631
  });
1485
1632
 
1486
1633
  // scripts/lib/tags.ts
1487
- import { readFileSync as readFileSync10, writeFileSync as writeFileSync8, existsSync as existsSync10 } from "node:fs";
1634
+ import { readFileSync as readFileSync10, writeFileSync as writeFileSync9, existsSync as existsSync10 } from "node:fs";
1488
1635
  import { join as join11 } from "node:path";
1489
1636
  function section(text2, name) {
1490
1637
  return text2.match(new RegExp(`##\\s*${name}\\s*\\n([\\s\\S]*?)(?:\\n##\\s|\\s*$)`, "i"))?.[1] ?? "";
@@ -1565,7 +1712,7 @@ function appendTags(vault2, newTags) {
1565
1712
  const sec = `## Tags
1566
1713
  ${tags.join(", ")}
1567
1714
  `;
1568
- writeFileSync8(p, base === "" ? sec : `${base}
1715
+ writeFileSync9(p, base === "" ? sec : `${base}
1569
1716
 
1570
1717
  ${sec}`);
1571
1718
  return tags;
@@ -1581,7 +1728,7 @@ ${sec}`);
1581
1728
  lines2.splice(h + 1, 0, tags.join(", "));
1582
1729
  else
1583
1730
  lines2[last] = `${lines2[last].replace(/\s+$/, "")}, ${tags.join(", ")}`;
1584
- writeFileSync8(p, lines2.join(`
1731
+ writeFileSync9(p, lines2.join(`
1585
1732
  `));
1586
1733
  return tags;
1587
1734
  }
@@ -2011,7 +2158,7 @@ var init_snapshot = __esm(() => {
2011
2158
 
2012
2159
  // scripts/check.ts
2013
2160
  var exports_check = {};
2014
- import { readdirSync as readdirSync8, readFileSync as readFileSync13, statSync as statSync8, existsSync as existsSync12, writeFileSync as writeFileSync9 } from "node:fs";
2161
+ import { readdirSync as readdirSync8, readFileSync as readFileSync13, statSync as statSync8, existsSync as existsSync12, writeFileSync as writeFileSync10 } from "node:fs";
2015
2162
  import { spawnSync as spawnSync3 } from "node:child_process";
2016
2163
  import { join as join14, relative as relative6, dirname as dirname3 } from "node:path";
2017
2164
  import { homedir as homedir3 } from "node:os";
@@ -2095,7 +2242,7 @@ ${REVIEW_END}
2095
2242
  `) + section2;
2096
2243
  }
2097
2244
  if (next !== prev || !exists)
2098
- writeFileSync9(p, next);
2245
+ writeFileSync10(p, next);
2099
2246
  return lines2.length ? "written" : "cleared";
2100
2247
  }
2101
2248
  var args4, vault4, all = false, ENTITY_FOLDERS, DOMAIN_FOLDERS2, LINK, notes, allSlugs, folderOf, byBasename, orphans, disconnected, domainIssues, untagged, review, referencedRaw, tagVocab, hasRealTag = (tags) => tags.some((t) => /[\p{L}\p{N}]/u.test(normalize(tagVocab, t))), hasTagsFile, addedTags, dupPairs, manifest3, rawEntries, norm = (p) => p.replace(/^\.\//, "").replace(/^.*\/raw\//, "raw/").replace(/\.md$/, ""), refNorm, uncovered, memoryStores, strayStores, strayCount, REVIEW_BEGIN = "<!-- imprnt-check:begin (regenerated by `imprnt check` - do not edit between the markers) -->", REVIEW_END = "<!-- imprnt-check:end -->", cap = (xs, n = 25) => xs.slice(0, n).concat(xs.length > n ? [` … +${xs.length - n} more`] : []), count, folders, synced, issues;
@@ -2492,8 +2639,9 @@ var init_cli = __esm(async () => {
2492
2639
  }
2493
2640
  case "lair": {
2494
2641
  const home = requireVaultHome();
2495
- const lair = buildLaunch({ cwd: home, vaultProject: home, pkgRoot, passthrough: rest });
2496
- process.exit(launchClaude(home, lair.args, lair.env));
2642
+ const { backend, skipPermissions, passthrough } = resolveLaunch(rest, { agent: readDefaultAgent(), yolo: readSkipPermissions() });
2643
+ const spec = assembleSession({ cwd: home, vaultProject: home, pkgRoot, passthrough, skipPermissions, model: readDefaultModel() });
2644
+ process.exit(launchBackend(backend, home, backend.renderArgs(spec), spec.env));
2497
2645
  }
2498
2646
  case "context": {
2499
2647
  const home = requireVaultHome();
@@ -2505,6 +2653,75 @@ var init_cli = __esm(async () => {
2505
2653
  process.stdout.write(readFileSync14(contract, "utf8"));
2506
2654
  break;
2507
2655
  }
2656
+ case "agent": {
2657
+ const [name] = rest;
2658
+ if (!name) {
2659
+ const stored = readDefaultAgent();
2660
+ console.log(`default agent: ${stored ?? "claude (built-in)"}`);
2661
+ if (process.env.IMPRNT_AGENT)
2662
+ console.log(` IMPRNT_AGENT=${process.env.IMPRNT_AGENT} overrides it in this shell`);
2663
+ console.log("set: imprnt agent <claude|gemini> override one session: imp --gemini | imp --claude");
2664
+ break;
2665
+ }
2666
+ if (name !== "claude" && name !== "gemini") {
2667
+ console.error(`unknown agent "${name}" — valid: claude, gemini`);
2668
+ process.exit(1);
2669
+ }
2670
+ const r = setDefaultAgent(name);
2671
+ if (!r.ok) {
2672
+ console.error(`could not set default agent (${configPath()}): ${r.error}`);
2673
+ process.exit(1);
2674
+ }
2675
+ console.log(`default agent set to ${name} (${configPath()})`);
2676
+ break;
2677
+ }
2678
+ case "yolo": {
2679
+ const [arg] = rest;
2680
+ if (!arg) {
2681
+ console.log(`skip-permissions default: ${readSkipPermissions() ? "on" : "off"}`);
2682
+ if ("IMPRNT_YOLO" in process.env)
2683
+ console.log(` IMPRNT_YOLO=${process.env.IMPRNT_YOLO} overrides it in this shell`);
2684
+ console.log("set: imprnt yolo on|off override one session: imp --yolo | imp --safe");
2685
+ break;
2686
+ }
2687
+ if (arg !== "on" && arg !== "off") {
2688
+ console.error("usage: imprnt yolo [on|off]");
2689
+ process.exit(1);
2690
+ }
2691
+ const r = setSkipPermissions(arg === "on");
2692
+ if (!r.ok) {
2693
+ console.error(`could not set yolo default (${configPath()}): ${r.error}`);
2694
+ process.exit(1);
2695
+ }
2696
+ console.log(arg === "on" ? `skip-permissions ENABLED (${configPath()}) — every imp session now runs without permission prompts` : `skip-permissions disabled (${configPath()})`);
2697
+ break;
2698
+ }
2699
+ case "model": {
2700
+ const [arg] = rest;
2701
+ if (!arg) {
2702
+ const m = readDefaultModel();
2703
+ console.log(`default model: ${m ?? "(agent's own default)"}`);
2704
+ console.log("set: imprnt model <alias|id> clear: imprnt model off");
2705
+ 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");
2706
+ break;
2707
+ }
2708
+ if (arg === "off" || arg === "default" || arg === "none") {
2709
+ const r2 = setDefaultModel("");
2710
+ if (!r2.ok) {
2711
+ console.error(`could not clear default model (${configPath()}): ${r2.error}`);
2712
+ process.exit(1);
2713
+ }
2714
+ console.log(`default model cleared (${configPath()}) — the agent uses its own default`);
2715
+ break;
2716
+ }
2717
+ const r = setDefaultModel(arg);
2718
+ if (!r.ok) {
2719
+ console.error(`could not set default model (${configPath()}): ${r.error}`);
2720
+ process.exit(1);
2721
+ }
2722
+ console.log(`default model set to ${arg} (${configPath()})`);
2723
+ break;
2724
+ }
2508
2725
  case "plugin": {
2509
2726
  const proj = pluginRoot();
2510
2727
  if (proj !== process.cwd())
@@ -2818,17 +3035,21 @@ enable: imprnt plugin add <name> disable: imprnt plugin rm <name> [--purge]`);
2818
3035
  if (wantsLaunch && (!bare || process.stdin.isTTY && process.stdout.isTTY)) {
2819
3036
  const home = vaultProjectRoot();
2820
3037
  if (!home) {
2821
- console.error("imp: no vault registered yet — run `imprnt init` in your vault project to give your assistant a memory. Launching plain claude.");
3038
+ console.error("imp: no vault registered yet — run `imprnt init` in your vault project to give your assistant a memory. Launching a plain session.");
2822
3039
  }
2823
- const { args: args5, env } = buildLaunch({ cwd: process.cwd(), vaultProject: home, pkgRoot, passthrough: bare ? [] : [cmd, ...rest] });
2824
- process.exit(launchClaude(process.cwd(), args5, env));
3040
+ const raw = bare ? [] : [cmd, ...rest];
3041
+ const { backend, skipPermissions, passthrough } = resolveLaunch(raw, { agent: readDefaultAgent(), yolo: readSkipPermissions() });
3042
+ const spec = assembleSession({ cwd: process.cwd(), vaultProject: home, pkgRoot, passthrough, skipPermissions, model: readDefaultModel() });
3043
+ process.exit(launchBackend(backend, process.cwd(), backend.renderArgs(spec), spec.env));
2825
3044
  }
2826
3045
  console.log(`imprnt — deterministic-first markdown knowledge vault
2827
3046
 
2828
3047
  the front door (the \`imp\` bin):
2829
- imp open your assistant HERE: claude + your cast + the vault pointer
3048
+ imp open your assistant HERE: your agent + your cast + the vault pointer
2830
3049
  imp lair open it in your vault project — full contract, resumable history
2831
- imp -c | --resume | <claude flags> flags pass through to claude
3050
+ imp --gemini | --claude pick the agent backend for this session (default: imprnt agent)
3051
+ imp --yolo | --safe skip / keep permission prompts for this session (default: imprnt yolo)
3052
+ imp -c | --resume | <agent flags> flags pass through to the chosen agent
2832
3053
 
2833
3054
  engine (same subcommands under \`imp\` or \`imprnt\`):
2834
3055
  imprnt init [path] [--register] scaffold vault (entities/domains/forms) + raw, register as imp's default; prompts for a location when run interactively with no path (default ~/imprnt)
@@ -2836,6 +3057,9 @@ engine (same subcommands under \`imp\` or \`imprnt\`):
2836
3057
  imprnt ingest <file|text> [--vault D] snapshot a source -> raw/; a transcript file also gets an event skeleton (no LLM)
2837
3058
  imprnt recall "<query>" [--vault D] synonym-aware BM25 ranking over the vault
2838
3059
  imprnt context print the vault contract — agents run this before writing any note
3060
+ imprnt agent [claude|gemini] show or set the per-machine default agent backend for imp
3061
+ imprnt yolo [on|off] show or set the per-machine skip-permissions default for imp
3062
+ imprnt model [alias|id|off] show or set the per-machine default model (gemini aliases: pro, flash, pro25, lite, ...)
2839
3063
  imprnt check [--all] [--vault D] integrity (orphan links, disconnected notes, uncovered snapshots) + regenerate index.md; --all also runs each plugins/*/check.js
2840
3064
  imprnt ingest --apply <file> [--vault D] file a pre-enriched staged note from a plugin into the vault (snapshot + resolve); --apply-all globs plugins/*/proposed/
2841
3065
  imprnt hot [--vault D] needs-review + the session primer
package/dist/imp.js CHANGED
@@ -737,7 +737,13 @@ function readRegistry() {
737
737
  for (const [k, v] of Object.entries(raw?.vaults ?? {}))
738
738
  if (typeof v === "string")
739
739
  vaults[k] = v;
740
- return { default: typeof raw?.default === "string" ? raw.default : undefined, vaults };
740
+ return {
741
+ default: typeof raw?.default === "string" ? raw.default : undefined,
742
+ vaults,
743
+ defaultAgent: typeof raw?.defaultAgent === "string" ? raw.defaultAgent : undefined,
744
+ skipPermissions: typeof raw?.skipPermissions === "boolean" ? raw.skipPermissions : undefined,
745
+ defaultModel: typeof raw?.defaultModel === "string" ? raw.defaultModel : undefined
746
+ };
741
747
  } catch {
742
748
  return { vaults: {} };
743
749
  }
@@ -770,6 +776,41 @@ function registerVault(path, opts = {}) {
770
776
  }
771
777
  return { status: "registered", current: path };
772
778
  }
779
+ function readDefaultAgent() {
780
+ return readRegistry().defaultAgent;
781
+ }
782
+ function setDefaultAgent(name) {
783
+ const reg = readRegistry();
784
+ reg.defaultAgent = name;
785
+ return writeRegistry(reg);
786
+ }
787
+ function readSkipPermissions() {
788
+ return readRegistry().skipPermissions ?? false;
789
+ }
790
+ function setSkipPermissions(on) {
791
+ const reg = readRegistry();
792
+ reg.skipPermissions = on;
793
+ return writeRegistry(reg);
794
+ }
795
+ function readDefaultModel() {
796
+ return readRegistry().defaultModel;
797
+ }
798
+ function setDefaultModel(model) {
799
+ const reg = readRegistry();
800
+ reg.defaultModel = model || undefined;
801
+ return writeRegistry(reg);
802
+ }
803
+ function writeRegistry(reg) {
804
+ const p = configPath();
805
+ try {
806
+ mkdirSync2(dirname2(p), { recursive: true });
807
+ writeFileSync5(p, JSON.stringify(reg, null, 2) + `
808
+ `);
809
+ } catch (e) {
810
+ return { ok: false, error: e instanceof Error ? e.message : String(e) };
811
+ }
812
+ return { ok: true };
813
+ }
773
814
  function isVaultProject(dir) {
774
815
  const vault = join7(dir, "vault");
775
816
  try {
@@ -799,8 +840,8 @@ function vaultProjectRoot(start = process.cwd()) {
799
840
  var init_registry = () => {};
800
841
 
801
842
  // scripts/lib/launch.ts
802
- import { existsSync as existsSync7, readFileSync as readFileSync7, realpathSync, statSync as statSync5 } from "node:fs";
803
- import { homedir as homedir2 } from "node:os";
843
+ import { existsSync as existsSync7, mkdtempSync as mkdtempSync2, readFileSync as readFileSync7, realpathSync, rmSync as rmSync3, statSync as statSync5, writeFileSync as writeFileSync6 } from "node:fs";
844
+ import { homedir as homedir2, tmpdir as tmpdir2 } from "node:os";
804
845
  import { isAbsolute, join as join8, resolve as resolve4, sep as sep2 } from "node:path";
805
846
  import { spawnSync as spawnSync2 } from "node:child_process";
806
847
  function importPath(root, target) {
@@ -829,7 +870,13 @@ function castFragment(root) {
829
870
  `);
830
871
  }
831
872
  function pointerFragment(pkgRoot, vaultProject) {
832
- const tpl = readFileSync7(join8(pkgRoot, "templates", "pointer.md"), "utf8");
873
+ let tpl;
874
+ try {
875
+ tpl = readFileSync7(join8(pkgRoot, "templates", "pointer.md"), "utf8");
876
+ } catch {
877
+ console.error(`imp: pointer template missing at ${join8(pkgRoot, "templates", "pointer.md")} - launching without the vault pointer`);
878
+ return "";
879
+ }
833
880
  return tpl.replaceAll("{{VAULT_PROJECT}}", () => vaultProject);
834
881
  }
835
882
  function substitutePluginDir(value, dir) {
@@ -930,53 +977,153 @@ function mergeFragment(pass, fragment, extraInject) {
930
977
  args.push(...inject);
931
978
  return args;
932
979
  }
933
- function buildLaunch(opts) {
934
- const pass = [...opts.passthrough ?? []];
980
+ function assembleSession(opts) {
981
+ const passthrough = [...opts.passthrough ?? []];
935
982
  const globalDir = opts.globalDir ?? defaultGlobalDir();
983
+ const skipPermissions = opts.skipPermissions ?? false;
984
+ const base = { cast: "", pointer: "", insideProject: false, addDirs: [], skipPermissions, model: opts.model, passthrough };
936
985
  if (!opts.vaultProject) {
937
- const globals2 = globalFragment(globalDir);
938
- if (!globals2)
939
- return { args: pass, env: process.env };
940
- return { args: mergeFragment(pass, globals2, []), env: process.env };
986
+ return { ...base, globals: globalFragment(globalDir) || "", env: process.env };
941
987
  }
942
988
  if (!isDir(opts.vaultProject)) {
943
- console.error(`imp: vault project not found at ${opts.vaultProject} - launching plain claude (re-run \`imprnt init\` there, or fix IMPRNT_ROOT)`);
944
- return { args: pass, env: process.env };
989
+ console.error(`imp: vault project not found at ${opts.vaultProject} - launching a plain session (re-run \`imprnt init\` there, or fix IMPRNT_ROOT)`);
990
+ return { ...base, globals: "", env: process.env };
945
991
  }
946
- const harness = harnessFlags(opts.vaultProject);
947
992
  const projectPlugins = new Set(enabledPluginDirs(opts.vaultProject));
948
- if (isInside(opts.cwd, opts.vaultProject)) {
949
- const globals2 = globalFragment(globalDir, projectPlugins);
950
- const args2 = globals2 ? mergeFragment(pass, globals2, []) : pass;
951
- return { args: [...harness, ...args2], env: childEnv(opts.vaultProject) };
952
- }
953
- const globals = globalFragment(globalDir, projectPlugins);
954
- const fragment = [castFragment(opts.vaultProject), pointerFragment(opts.pkgRoot, opts.vaultProject), globals].filter(Boolean).join(`
993
+ const inside = isInside(opts.cwd, opts.vaultProject);
994
+ return {
995
+ cast: castFragment(opts.vaultProject),
996
+ pointer: pointerFragment(opts.pkgRoot, opts.vaultProject),
997
+ globals: globalFragment(globalDir, projectPlugins) || "",
998
+ insideProject: inside,
999
+ addDirs: inside ? [] : [opts.vaultProject],
1000
+ pluginsRoot: opts.vaultProject,
1001
+ skipPermissions,
1002
+ model: opts.model,
1003
+ env: childEnv(opts.vaultProject),
1004
+ passthrough
1005
+ };
1006
+ }
1007
+ function joinFragments(parts) {
1008
+ return parts.filter(Boolean).join(`
955
1009
 
956
1010
  `);
957
- const args = mergeFragment(pass, fragment, ["--add-dir", opts.vaultProject]);
958
- return { args: [...harness, ...args], env: childEnv(opts.vaultProject) };
959
1011
  }
960
- function launchClaude(cwd, args, env = process.env) {
1012
+ function claudeRenderArgs(spec) {
1013
+ const systemPrompt = spec.insideProject ? spec.globals : joinFragments([spec.cast, spec.pointer, spec.globals]);
1014
+ const harness = spec.pluginsRoot ? harnessFlags(spec.pluginsRoot) : [];
1015
+ 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;
1017
+ const body = systemPrompt ? mergeFragment(pass, systemPrompt, extra) : [...pass, ...extra];
1018
+ return [...harness, ...body];
1019
+ }
1020
+ function expandGeminiModel(m) {
1021
+ return GEMINI_MODEL_ALIASES[m] ?? m;
1022
+ }
1023
+ function geminiRenderArgs(spec) {
1024
+ const includes = [];
1025
+ const systemPrompt = joinFragments([spec.cast, spec.pointer, spec.globals]);
1026
+ if (systemPrompt) {
1027
+ const dir = mkdtempSync2(join8(tmpdir2(), "imprnt-gemini-"));
1028
+ writeFileSync6(join8(dir, "GEMINI.md"), systemPrompt + `
1029
+ `);
1030
+ process.on("exit", () => {
1031
+ try {
1032
+ rmSync3(dir, { recursive: true, force: true });
1033
+ } catch {}
1034
+ });
1035
+ includes.push(dir);
1036
+ }
1037
+ includes.push(...spec.addDirs);
1038
+ if (spec.pluginsRoot && harnessFlags(spec.pluginsRoot).length) {
1039
+ console.error("imp: gemini does not host claude harness plugins (statusline, timemachine) - skipping them");
1040
+ }
1041
+ const lead = [];
1042
+ if (spec.skipPermissions) {
1043
+ if (!spec.passthrough.includes("--yolo") && !spec.passthrough.includes("-y"))
1044
+ lead.push("--yolo");
1045
+ if (!spec.passthrough.includes("--skip-trust"))
1046
+ lead.push("--skip-trust");
1047
+ }
1048
+ const pass = [...spec.passthrough];
1049
+ const mIdx = pass.findIndex((a) => a === "-m" || a === "--model");
1050
+ if (mIdx >= 0 && pass[mIdx + 1] !== undefined) {
1051
+ pass[mIdx + 1] = expandGeminiModel(pass[mIdx + 1]);
1052
+ } else if (mIdx < 0 && spec.model) {
1053
+ pass.unshift("-m", expandGeminiModel(spec.model));
1054
+ }
1055
+ const flags = includes.length ? ["--include-directories", includes.join(",")] : [];
1056
+ return [...lead, ...flags, ...pass];
1057
+ }
1058
+ function resolveLaunch(passthrough, config = {}) {
1059
+ let agentFlag;
1060
+ let yoloFlag;
1061
+ const rest = [];
1062
+ for (const a of passthrough) {
1063
+ if (a === "--gemini")
1064
+ agentFlag = agentFlag ?? "gemini";
1065
+ else if (a === "--claude")
1066
+ agentFlag = agentFlag ?? "claude";
1067
+ else if (a === "--yolo")
1068
+ yoloFlag = yoloFlag ?? true;
1069
+ else if (a === "--safe")
1070
+ yoloFlag = yoloFlag ?? false;
1071
+ else
1072
+ rest.push(a);
1073
+ }
1074
+ const name = agentFlag ?? process.env.IMPRNT_AGENT ?? config.agent ?? "claude";
1075
+ let backend = backends[name];
1076
+ if (!backend) {
1077
+ console.error(`imp: unknown agent "${name}" - falling back to claude (valid: ${Object.keys(backends).join(", ")})`);
1078
+ backend = claudeBackend;
1079
+ }
1080
+ const envYolo = "IMPRNT_YOLO" in process.env ? !["0", "false", ""].includes(process.env.IMPRNT_YOLO ?? "") : undefined;
1081
+ const skipPermissions = yoloFlag ?? envYolo ?? config.yolo ?? false;
1082
+ return { backend, skipPermissions, passthrough: rest };
1083
+ }
1084
+ function launchBackend(backend, cwd, args, env = process.env) {
961
1085
  if (!isDir(cwd)) {
962
1086
  console.error(`imp: vault project not found at ${cwd} — re-run \`imprnt init\` in its new location (add --register to switch the default)`);
963
1087
  return 1;
964
1088
  }
965
- const r = spawnSync2("claude", args, { cwd, stdio: "inherit", env });
1089
+ const r = spawnSync2(backend.name, args, { cwd, stdio: "inherit", env });
966
1090
  if (r.error) {
967
1091
  const code = r.error.code;
968
- console.error(code === "ENOENT" ? "imp: `claude` not found on PATH. Install Claude Code first: npm i -g @anthropic-ai/claude-code" : `imp: failed to launch claude: ${r.error.message}`);
1092
+ console.error(code === "ENOENT" ? `imp: \`${backend.name}\` not found on PATH. ${backend.missingHint}` : `imp: failed to launch ${backend.name}: ${r.error.message}`);
969
1093
  return 1;
970
1094
  }
971
1095
  return r.status ?? 1;
972
1096
  }
1097
+ var claudeBackend, GEMINI_MODEL_ALIASES, geminiBackend, backends;
973
1098
  var init_launch = __esm(() => {
974
1099
  init_plugins();
975
1100
  init_global();
1101
+ claudeBackend = {
1102
+ name: "claude",
1103
+ missingHint: "Install Claude Code first: npm i -g @anthropic-ai/claude-code",
1104
+ renderArgs: claudeRenderArgs
1105
+ };
1106
+ GEMINI_MODEL_ALIASES = {
1107
+ pro: "gemini-3.1-pro-preview",
1108
+ flash: "gemini-3.5-flash",
1109
+ pro25: "gemini-2.5-pro",
1110
+ lite: "gemini-3.1-flash-lite",
1111
+ gemma31: "gemma-4-31b-it",
1112
+ gemma26: "gemma-4-26b-a4b-it"
1113
+ };
1114
+ geminiBackend = {
1115
+ name: "gemini",
1116
+ missingHint: "Install the Gemini CLI first: npm i -g @google/gemini-cli",
1117
+ renderArgs: geminiRenderArgs
1118
+ };
1119
+ backends = {
1120
+ claude: claudeBackend,
1121
+ gemini: geminiBackend
1122
+ };
976
1123
  });
977
1124
 
978
1125
  // scripts/lib/manifest.ts
979
- import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, renameSync, existsSync as existsSync8 } from "node:fs";
1126
+ import { readFileSync as readFileSync8, writeFileSync as writeFileSync7, renameSync, existsSync as existsSync8 } from "node:fs";
980
1127
  import { join as join9 } from "node:path";
981
1128
  function manifestPath(vault) {
982
1129
  return join9(vault, ".manifest.json");
@@ -1000,7 +1147,7 @@ function loadManifest(vault) {
1000
1147
  function saveManifest(vault, m) {
1001
1148
  const p = manifestPath(vault);
1002
1149
  const tmp = `${p}.tmp.${process.pid}.${Date.now()}`;
1003
- writeFileSync6(tmp, JSON.stringify(m, null, 2) + `
1150
+ writeFileSync7(tmp, JSON.stringify(m, null, 2) + `
1004
1151
  `);
1005
1152
  renameSync(tmp, p);
1006
1153
  }
@@ -1008,7 +1155,7 @@ var init_manifest = () => {};
1008
1155
 
1009
1156
  // scripts/ingest.ts
1010
1157
  var exports_ingest = {};
1011
- import { readFileSync as readFileSync9, writeFileSync as writeFileSync7, mkdirSync as mkdirSync3, existsSync as existsSync9, copyFileSync, readdirSync as readdirSync5, rmSync as rmSync3, statSync as statSync6 } from "node:fs";
1158
+ 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";
1012
1159
  import { createHash } from "node:crypto";
1013
1160
  import { basename as basename2, extname, join as join10, relative as relative3 } from "node:path";
1014
1161
  function slugify(s) {
@@ -1124,7 +1271,7 @@ function applyStaged(staged, vault) {
1124
1271
  const existingHash = createHash("sha256").update(readFileSync9(notePath, "utf8")).digest("hex").slice(0, 16);
1125
1272
  if (existingHash === fileHash) {
1126
1273
  console.log(` = ${noteRel} already filed, identical content (hash ${fileHash}) — no-op`);
1127
- rmSync3(staged);
1274
+ rmSync4(staged);
1128
1275
  return "noop";
1129
1276
  }
1130
1277
  console.error(` ! ${noteRel} exists with DIFFERENT content — not overwriting (contradiction discipline)`);
@@ -1135,12 +1282,12 @@ function applyStaged(staged, vault) {
1135
1282
  if (!stagedSource && !priorSnapshot) {
1136
1283
  mkdirSync3(rawDir, { recursive: true });
1137
1284
  if (!existsSync9(rawPath))
1138
- writeFileSync7(rawPath, text);
1285
+ writeFileSync8(rawPath, text);
1139
1286
  }
1140
1287
  manifest[manifestKey] = { hash, note: notePath, ingested: new Date().toISOString(), raw: rawEntry };
1141
1288
  saveManifest(vault, manifest);
1142
1289
  mkdirSync3(join10(vault, folder), { recursive: true });
1143
- writeFileSync7(notePath, finalText);
1290
+ writeFileSync8(notePath, finalText);
1144
1291
  const participantsList = fmList2(fm, "participants");
1145
1292
  const participantsScalar = participantsList.length ? "" : fmScalar2(fm, "participants");
1146
1293
  const participants = (participantsScalar ? [participantsScalar] : participantsList).map(linkSlug);
@@ -1153,7 +1300,7 @@ function applyStaged(staged, vault) {
1153
1300
  flagNeedsReview(vault, `- [ ] unresolved person \`people/${personSlug}\` — from [[${noteRel}]] (applied from \`${staged}\`)`);
1154
1301
  }
1155
1302
  }
1156
- rmSync3(staged);
1303
+ rmSync4(staged);
1157
1304
  console.log(` ✓ filed ${noteRel} (type: ${type}${domain ? `, domain: ${domain}` : ""})`);
1158
1305
  if (stagedSource)
1159
1306
  console.log(` source -> ${rawEntry} (note's own source: kept - no redundant snapshot)`);
@@ -1369,7 +1516,7 @@ ${filed} filed, ${noop} no-op, ${conflict} conflict, ${error} error.`);
1369
1516
  if (isFile)
1370
1517
  copyFileSync(src, rawPath);
1371
1518
  else
1372
- writeFileSync7(rawPath, text);
1519
+ writeFileSync8(rawPath, text);
1373
1520
  }
1374
1521
  } catch (e) {
1375
1522
  console.error(`cannot write snapshot ${rawPath}: ${e instanceof Error ? e.message : e}`);
@@ -1466,7 +1613,7 @@ ${body}`;
1466
1613
  console.log(` hash8 collision with a distinct source -> trying \`events/${effectiveSlug}\` instead`);
1467
1614
  }
1468
1615
  }
1469
- writeFileSync7(notePath, note);
1616
+ writeFileSync8(notePath, note);
1470
1617
  manifest[manifestKey] = { hash, note: notePath, ingested: new Date().toISOString(), raw: rawPath };
1471
1618
  saveManifest(vault, manifest);
1472
1619
  unresolved = [...speakers].filter((name) => !personResolved(vault, slugify(name), name));
@@ -1484,7 +1631,7 @@ ${body}`;
1484
1631
  });
1485
1632
 
1486
1633
  // scripts/lib/tags.ts
1487
- import { readFileSync as readFileSync10, writeFileSync as writeFileSync8, existsSync as existsSync10 } from "node:fs";
1634
+ import { readFileSync as readFileSync10, writeFileSync as writeFileSync9, existsSync as existsSync10 } from "node:fs";
1488
1635
  import { join as join11 } from "node:path";
1489
1636
  function section(text2, name) {
1490
1637
  return text2.match(new RegExp(`##\\s*${name}\\s*\\n([\\s\\S]*?)(?:\\n##\\s|\\s*$)`, "i"))?.[1] ?? "";
@@ -1565,7 +1712,7 @@ function appendTags(vault2, newTags) {
1565
1712
  const sec = `## Tags
1566
1713
  ${tags.join(", ")}
1567
1714
  `;
1568
- writeFileSync8(p, base === "" ? sec : `${base}
1715
+ writeFileSync9(p, base === "" ? sec : `${base}
1569
1716
 
1570
1717
  ${sec}`);
1571
1718
  return tags;
@@ -1581,7 +1728,7 @@ ${sec}`);
1581
1728
  lines2.splice(h + 1, 0, tags.join(", "));
1582
1729
  else
1583
1730
  lines2[last] = `${lines2[last].replace(/\s+$/, "")}, ${tags.join(", ")}`;
1584
- writeFileSync8(p, lines2.join(`
1731
+ writeFileSync9(p, lines2.join(`
1585
1732
  `));
1586
1733
  return tags;
1587
1734
  }
@@ -2011,7 +2158,7 @@ var init_snapshot = __esm(() => {
2011
2158
 
2012
2159
  // scripts/check.ts
2013
2160
  var exports_check = {};
2014
- import { readdirSync as readdirSync8, readFileSync as readFileSync13, statSync as statSync8, existsSync as existsSync12, writeFileSync as writeFileSync9 } from "node:fs";
2161
+ import { readdirSync as readdirSync8, readFileSync as readFileSync13, statSync as statSync8, existsSync as existsSync12, writeFileSync as writeFileSync10 } from "node:fs";
2015
2162
  import { spawnSync as spawnSync3 } from "node:child_process";
2016
2163
  import { join as join14, relative as relative6, dirname as dirname3 } from "node:path";
2017
2164
  import { homedir as homedir3 } from "node:os";
@@ -2095,7 +2242,7 @@ ${REVIEW_END}
2095
2242
  `) + section2;
2096
2243
  }
2097
2244
  if (next !== prev || !exists)
2098
- writeFileSync9(p, next);
2245
+ writeFileSync10(p, next);
2099
2246
  return lines2.length ? "written" : "cleared";
2100
2247
  }
2101
2248
  var args4, vault4, all = false, ENTITY_FOLDERS, DOMAIN_FOLDERS2, LINK, notes, allSlugs, folderOf, byBasename, orphans, disconnected, domainIssues, untagged, review, referencedRaw, tagVocab, hasRealTag = (tags) => tags.some((t) => /[\p{L}\p{N}]/u.test(normalize(tagVocab, t))), hasTagsFile, addedTags, dupPairs, manifest3, rawEntries, norm = (p) => p.replace(/^\.\//, "").replace(/^.*\/raw\//, "raw/").replace(/\.md$/, ""), refNorm, uncovered, memoryStores, strayStores, strayCount, REVIEW_BEGIN = "<!-- imprnt-check:begin (regenerated by `imprnt check` - do not edit between the markers) -->", REVIEW_END = "<!-- imprnt-check:end -->", cap = (xs, n = 25) => xs.slice(0, n).concat(xs.length > n ? [` … +${xs.length - n} more`] : []), count, folders, synced, issues;
@@ -2492,8 +2639,9 @@ var init_cli = __esm(async () => {
2492
2639
  }
2493
2640
  case "lair": {
2494
2641
  const home = requireVaultHome();
2495
- const lair = buildLaunch({ cwd: home, vaultProject: home, pkgRoot, passthrough: rest });
2496
- process.exit(launchClaude(home, lair.args, lair.env));
2642
+ const { backend, skipPermissions, passthrough } = resolveLaunch(rest, { agent: readDefaultAgent(), yolo: readSkipPermissions() });
2643
+ const spec = assembleSession({ cwd: home, vaultProject: home, pkgRoot, passthrough, skipPermissions, model: readDefaultModel() });
2644
+ process.exit(launchBackend(backend, home, backend.renderArgs(spec), spec.env));
2497
2645
  }
2498
2646
  case "context": {
2499
2647
  const home = requireVaultHome();
@@ -2505,6 +2653,75 @@ var init_cli = __esm(async () => {
2505
2653
  process.stdout.write(readFileSync14(contract, "utf8"));
2506
2654
  break;
2507
2655
  }
2656
+ case "agent": {
2657
+ const [name] = rest;
2658
+ if (!name) {
2659
+ const stored = readDefaultAgent();
2660
+ console.log(`default agent: ${stored ?? "claude (built-in)"}`);
2661
+ if (process.env.IMPRNT_AGENT)
2662
+ console.log(` IMPRNT_AGENT=${process.env.IMPRNT_AGENT} overrides it in this shell`);
2663
+ console.log("set: imprnt agent <claude|gemini> override one session: imp --gemini | imp --claude");
2664
+ break;
2665
+ }
2666
+ if (name !== "claude" && name !== "gemini") {
2667
+ console.error(`unknown agent "${name}" — valid: claude, gemini`);
2668
+ process.exit(1);
2669
+ }
2670
+ const r = setDefaultAgent(name);
2671
+ if (!r.ok) {
2672
+ console.error(`could not set default agent (${configPath()}): ${r.error}`);
2673
+ process.exit(1);
2674
+ }
2675
+ console.log(`default agent set to ${name} (${configPath()})`);
2676
+ break;
2677
+ }
2678
+ case "yolo": {
2679
+ const [arg] = rest;
2680
+ if (!arg) {
2681
+ console.log(`skip-permissions default: ${readSkipPermissions() ? "on" : "off"}`);
2682
+ if ("IMPRNT_YOLO" in process.env)
2683
+ console.log(` IMPRNT_YOLO=${process.env.IMPRNT_YOLO} overrides it in this shell`);
2684
+ console.log("set: imprnt yolo on|off override one session: imp --yolo | imp --safe");
2685
+ break;
2686
+ }
2687
+ if (arg !== "on" && arg !== "off") {
2688
+ console.error("usage: imprnt yolo [on|off]");
2689
+ process.exit(1);
2690
+ }
2691
+ const r = setSkipPermissions(arg === "on");
2692
+ if (!r.ok) {
2693
+ console.error(`could not set yolo default (${configPath()}): ${r.error}`);
2694
+ process.exit(1);
2695
+ }
2696
+ console.log(arg === "on" ? `skip-permissions ENABLED (${configPath()}) — every imp session now runs without permission prompts` : `skip-permissions disabled (${configPath()})`);
2697
+ break;
2698
+ }
2699
+ case "model": {
2700
+ const [arg] = rest;
2701
+ if (!arg) {
2702
+ const m = readDefaultModel();
2703
+ console.log(`default model: ${m ?? "(agent's own default)"}`);
2704
+ console.log("set: imprnt model <alias|id> clear: imprnt model off");
2705
+ 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");
2706
+ break;
2707
+ }
2708
+ if (arg === "off" || arg === "default" || arg === "none") {
2709
+ const r2 = setDefaultModel("");
2710
+ if (!r2.ok) {
2711
+ console.error(`could not clear default model (${configPath()}): ${r2.error}`);
2712
+ process.exit(1);
2713
+ }
2714
+ console.log(`default model cleared (${configPath()}) — the agent uses its own default`);
2715
+ break;
2716
+ }
2717
+ const r = setDefaultModel(arg);
2718
+ if (!r.ok) {
2719
+ console.error(`could not set default model (${configPath()}): ${r.error}`);
2720
+ process.exit(1);
2721
+ }
2722
+ console.log(`default model set to ${arg} (${configPath()})`);
2723
+ break;
2724
+ }
2508
2725
  case "plugin": {
2509
2726
  const proj = pluginRoot();
2510
2727
  if (proj !== process.cwd())
@@ -2818,17 +3035,21 @@ enable: imprnt plugin add <name> disable: imprnt plugin rm <name> [--purge]`);
2818
3035
  if (wantsLaunch && (!bare || process.stdin.isTTY && process.stdout.isTTY)) {
2819
3036
  const home = vaultProjectRoot();
2820
3037
  if (!home) {
2821
- console.error("imp: no vault registered yet — run `imprnt init` in your vault project to give your assistant a memory. Launching plain claude.");
3038
+ console.error("imp: no vault registered yet — run `imprnt init` in your vault project to give your assistant a memory. Launching a plain session.");
2822
3039
  }
2823
- const { args: args5, env } = buildLaunch({ cwd: process.cwd(), vaultProject: home, pkgRoot, passthrough: bare ? [] : [cmd, ...rest] });
2824
- process.exit(launchClaude(process.cwd(), args5, env));
3040
+ const raw = bare ? [] : [cmd, ...rest];
3041
+ const { backend, skipPermissions, passthrough } = resolveLaunch(raw, { agent: readDefaultAgent(), yolo: readSkipPermissions() });
3042
+ const spec = assembleSession({ cwd: process.cwd(), vaultProject: home, pkgRoot, passthrough, skipPermissions, model: readDefaultModel() });
3043
+ process.exit(launchBackend(backend, process.cwd(), backend.renderArgs(spec), spec.env));
2825
3044
  }
2826
3045
  console.log(`imprnt — deterministic-first markdown knowledge vault
2827
3046
 
2828
3047
  the front door (the \`imp\` bin):
2829
- imp open your assistant HERE: claude + your cast + the vault pointer
3048
+ imp open your assistant HERE: your agent + your cast + the vault pointer
2830
3049
  imp lair open it in your vault project — full contract, resumable history
2831
- imp -c | --resume | <claude flags> flags pass through to claude
3050
+ imp --gemini | --claude pick the agent backend for this session (default: imprnt agent)
3051
+ imp --yolo | --safe skip / keep permission prompts for this session (default: imprnt yolo)
3052
+ imp -c | --resume | <agent flags> flags pass through to the chosen agent
2832
3053
 
2833
3054
  engine (same subcommands under \`imp\` or \`imprnt\`):
2834
3055
  imprnt init [path] [--register] scaffold vault (entities/domains/forms) + raw, register as imp's default; prompts for a location when run interactively with no path (default ~/imprnt)
@@ -2836,6 +3057,9 @@ engine (same subcommands under \`imp\` or \`imprnt\`):
2836
3057
  imprnt ingest <file|text> [--vault D] snapshot a source -> raw/; a transcript file also gets an event skeleton (no LLM)
2837
3058
  imprnt recall "<query>" [--vault D] synonym-aware BM25 ranking over the vault
2838
3059
  imprnt context print the vault contract — agents run this before writing any note
3060
+ imprnt agent [claude|gemini] show or set the per-machine default agent backend for imp
3061
+ imprnt yolo [on|off] show or set the per-machine skip-permissions default for imp
3062
+ imprnt model [alias|id|off] show or set the per-machine default model (gemini aliases: pro, flash, pro25, lite, ...)
2839
3063
  imprnt check [--all] [--vault D] integrity (orphan links, disconnected notes, uncovered snapshots) + regenerate index.md; --all also runs each plugins/*/check.js
2840
3064
  imprnt ingest --apply <file> [--vault D] file a pre-enriched staged note from a plugin into the vault (snapshot + resolve); --apply-all globs plugins/*/proposed/
2841
3065
  imprnt hot [--vault D] needs-review + the session primer
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "imprnt",
3
- "version": "0.1.3-edge.54",
3
+ "version": "0.1.3-edge.58",
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": {