imprnt 0.1.3-edge.54 → 0.1.3-edge.59
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +277 -46
- package/dist/imp.js +277 -46
- 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 {
|
|
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
|
-
|
|
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,160 @@ function mergeFragment(pass, fragment, extraInject) {
|
|
|
930
977
|
args.push(...inject);
|
|
931
978
|
return args;
|
|
932
979
|
}
|
|
933
|
-
function
|
|
934
|
-
const
|
|
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
|
-
|
|
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
|
|
944
|
-
return {
|
|
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
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
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
|
|
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 escapeGeminiImports(text) {
|
|
1024
|
+
return text.replaceAll("@", "\\@");
|
|
1025
|
+
}
|
|
1026
|
+
function geminiRenderArgs(spec) {
|
|
1027
|
+
const includes = [];
|
|
1028
|
+
const systemPrompt = joinFragments([spec.cast, spec.pointer, spec.globals]);
|
|
1029
|
+
if (systemPrompt) {
|
|
1030
|
+
const dir = mkdtempSync2(join8(tmpdir2(), "imprnt-gemini-"));
|
|
1031
|
+
writeFileSync6(join8(dir, "GEMINI.md"), escapeGeminiImports(systemPrompt) + `
|
|
1032
|
+
`);
|
|
1033
|
+
process.on("exit", () => {
|
|
1034
|
+
try {
|
|
1035
|
+
rmSync3(dir, { recursive: true, force: true });
|
|
1036
|
+
} catch {}
|
|
1037
|
+
});
|
|
1038
|
+
includes.push(dir);
|
|
1039
|
+
}
|
|
1040
|
+
includes.push(...spec.addDirs);
|
|
1041
|
+
if (spec.pluginsRoot && harnessFlags(spec.pluginsRoot).length) {
|
|
1042
|
+
console.error("imp: gemini does not host claude harness plugins (statusline, timemachine) - skipping them");
|
|
1043
|
+
}
|
|
1044
|
+
const lead = [];
|
|
1045
|
+
if (spec.skipPermissions) {
|
|
1046
|
+
if (!spec.passthrough.includes("--yolo") && !spec.passthrough.includes("-y"))
|
|
1047
|
+
lead.push("--yolo");
|
|
1048
|
+
if (!spec.passthrough.includes("--skip-trust"))
|
|
1049
|
+
lead.push("--skip-trust");
|
|
1050
|
+
}
|
|
1051
|
+
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) {
|
|
1056
|
+
pass.unshift("-m", expandGeminiModel(spec.model));
|
|
1057
|
+
}
|
|
1058
|
+
const rIdx = pass.findIndex((a) => a === "-r" || a === "--resume");
|
|
1059
|
+
if (rIdx >= 0 && (pass[rIdx + 1] === undefined || pass[rIdx + 1].startsWith("-"))) {
|
|
1060
|
+
pass.splice(rIdx + 1, 0, "latest");
|
|
1061
|
+
}
|
|
1062
|
+
const flags = includes.length ? ["--include-directories", includes.join(",")] : [];
|
|
1063
|
+
return [...lead, ...flags, ...pass];
|
|
1064
|
+
}
|
|
1065
|
+
function resolveLaunch(passthrough, config = {}) {
|
|
1066
|
+
let agentFlag;
|
|
1067
|
+
let yoloFlag;
|
|
1068
|
+
const rest = [];
|
|
1069
|
+
for (const a of passthrough) {
|
|
1070
|
+
if (a === "--gemini")
|
|
1071
|
+
agentFlag = agentFlag ?? "gemini";
|
|
1072
|
+
else if (a === "--claude")
|
|
1073
|
+
agentFlag = agentFlag ?? "claude";
|
|
1074
|
+
else if (a === "--yolo")
|
|
1075
|
+
yoloFlag = yoloFlag ?? true;
|
|
1076
|
+
else if (a === "--safe")
|
|
1077
|
+
yoloFlag = yoloFlag ?? false;
|
|
1078
|
+
else
|
|
1079
|
+
rest.push(a);
|
|
1080
|
+
}
|
|
1081
|
+
const name = agentFlag ?? process.env.IMPRNT_AGENT ?? config.agent ?? "claude";
|
|
1082
|
+
let backend = backends[name];
|
|
1083
|
+
if (!backend) {
|
|
1084
|
+
console.error(`imp: unknown agent "${name}" - falling back to claude (valid: ${Object.keys(backends).join(", ")})`);
|
|
1085
|
+
backend = claudeBackend;
|
|
1086
|
+
}
|
|
1087
|
+
const envYolo = "IMPRNT_YOLO" in process.env ? !["0", "false", ""].includes(process.env.IMPRNT_YOLO ?? "") : undefined;
|
|
1088
|
+
const skipPermissions = yoloFlag ?? envYolo ?? config.yolo ?? false;
|
|
1089
|
+
return { backend, skipPermissions, passthrough: rest };
|
|
1090
|
+
}
|
|
1091
|
+
function launchBackend(backend, cwd, args, env = process.env) {
|
|
961
1092
|
if (!isDir(cwd)) {
|
|
962
1093
|
console.error(`imp: vault project not found at ${cwd} — re-run \`imprnt init\` in its new location (add --register to switch the default)`);
|
|
963
1094
|
return 1;
|
|
964
1095
|
}
|
|
965
|
-
const r = spawnSync2(
|
|
1096
|
+
const r = spawnSync2(backend.name, args, { cwd, stdio: "inherit", env });
|
|
966
1097
|
if (r.error) {
|
|
967
1098
|
const code = r.error.code;
|
|
968
|
-
console.error(code === "ENOENT" ?
|
|
1099
|
+
console.error(code === "ENOENT" ? `imp: \`${backend.name}\` not found on PATH. ${backend.missingHint}` : `imp: failed to launch ${backend.name}: ${r.error.message}`);
|
|
969
1100
|
return 1;
|
|
970
1101
|
}
|
|
971
1102
|
return r.status ?? 1;
|
|
972
1103
|
}
|
|
1104
|
+
var claudeBackend, GEMINI_MODEL_ALIASES, geminiBackend, backends;
|
|
973
1105
|
var init_launch = __esm(() => {
|
|
974
1106
|
init_plugins();
|
|
975
1107
|
init_global();
|
|
1108
|
+
claudeBackend = {
|
|
1109
|
+
name: "claude",
|
|
1110
|
+
missingHint: "Install Claude Code first: npm i -g @anthropic-ai/claude-code",
|
|
1111
|
+
renderArgs: claudeRenderArgs
|
|
1112
|
+
};
|
|
1113
|
+
GEMINI_MODEL_ALIASES = {
|
|
1114
|
+
pro: "gemini-3.1-pro-preview",
|
|
1115
|
+
flash: "gemini-3.5-flash",
|
|
1116
|
+
pro25: "gemini-2.5-pro",
|
|
1117
|
+
lite: "gemini-3.1-flash-lite",
|
|
1118
|
+
gemma31: "gemma-4-31b-it",
|
|
1119
|
+
gemma26: "gemma-4-26b-a4b-it"
|
|
1120
|
+
};
|
|
1121
|
+
geminiBackend = {
|
|
1122
|
+
name: "gemini",
|
|
1123
|
+
missingHint: "Install the Gemini CLI first: npm i -g @google/gemini-cli",
|
|
1124
|
+
renderArgs: geminiRenderArgs
|
|
1125
|
+
};
|
|
1126
|
+
backends = {
|
|
1127
|
+
claude: claudeBackend,
|
|
1128
|
+
gemini: geminiBackend
|
|
1129
|
+
};
|
|
976
1130
|
});
|
|
977
1131
|
|
|
978
1132
|
// scripts/lib/manifest.ts
|
|
979
|
-
import { readFileSync as readFileSync8, writeFileSync as
|
|
1133
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync7, renameSync, existsSync as existsSync8 } from "node:fs";
|
|
980
1134
|
import { join as join9 } from "node:path";
|
|
981
1135
|
function manifestPath(vault) {
|
|
982
1136
|
return join9(vault, ".manifest.json");
|
|
@@ -1000,7 +1154,7 @@ function loadManifest(vault) {
|
|
|
1000
1154
|
function saveManifest(vault, m) {
|
|
1001
1155
|
const p = manifestPath(vault);
|
|
1002
1156
|
const tmp = `${p}.tmp.${process.pid}.${Date.now()}`;
|
|
1003
|
-
|
|
1157
|
+
writeFileSync7(tmp, JSON.stringify(m, null, 2) + `
|
|
1004
1158
|
`);
|
|
1005
1159
|
renameSync(tmp, p);
|
|
1006
1160
|
}
|
|
@@ -1008,7 +1162,7 @@ var init_manifest = () => {};
|
|
|
1008
1162
|
|
|
1009
1163
|
// scripts/ingest.ts
|
|
1010
1164
|
var exports_ingest = {};
|
|
1011
|
-
import { readFileSync as readFileSync9, writeFileSync as
|
|
1165
|
+
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
1166
|
import { createHash } from "node:crypto";
|
|
1013
1167
|
import { basename as basename2, extname, join as join10, relative as relative3 } from "node:path";
|
|
1014
1168
|
function slugify(s) {
|
|
@@ -1124,7 +1278,7 @@ function applyStaged(staged, vault) {
|
|
|
1124
1278
|
const existingHash = createHash("sha256").update(readFileSync9(notePath, "utf8")).digest("hex").slice(0, 16);
|
|
1125
1279
|
if (existingHash === fileHash) {
|
|
1126
1280
|
console.log(` = ${noteRel} already filed, identical content (hash ${fileHash}) — no-op`);
|
|
1127
|
-
|
|
1281
|
+
rmSync4(staged);
|
|
1128
1282
|
return "noop";
|
|
1129
1283
|
}
|
|
1130
1284
|
console.error(` ! ${noteRel} exists with DIFFERENT content — not overwriting (contradiction discipline)`);
|
|
@@ -1135,12 +1289,12 @@ function applyStaged(staged, vault) {
|
|
|
1135
1289
|
if (!stagedSource && !priorSnapshot) {
|
|
1136
1290
|
mkdirSync3(rawDir, { recursive: true });
|
|
1137
1291
|
if (!existsSync9(rawPath))
|
|
1138
|
-
|
|
1292
|
+
writeFileSync8(rawPath, text);
|
|
1139
1293
|
}
|
|
1140
1294
|
manifest[manifestKey] = { hash, note: notePath, ingested: new Date().toISOString(), raw: rawEntry };
|
|
1141
1295
|
saveManifest(vault, manifest);
|
|
1142
1296
|
mkdirSync3(join10(vault, folder), { recursive: true });
|
|
1143
|
-
|
|
1297
|
+
writeFileSync8(notePath, finalText);
|
|
1144
1298
|
const participantsList = fmList2(fm, "participants");
|
|
1145
1299
|
const participantsScalar = participantsList.length ? "" : fmScalar2(fm, "participants");
|
|
1146
1300
|
const participants = (participantsScalar ? [participantsScalar] : participantsList).map(linkSlug);
|
|
@@ -1153,7 +1307,7 @@ function applyStaged(staged, vault) {
|
|
|
1153
1307
|
flagNeedsReview(vault, `- [ ] unresolved person \`people/${personSlug}\` — from [[${noteRel}]] (applied from \`${staged}\`)`);
|
|
1154
1308
|
}
|
|
1155
1309
|
}
|
|
1156
|
-
|
|
1310
|
+
rmSync4(staged);
|
|
1157
1311
|
console.log(` ✓ filed ${noteRel} (type: ${type}${domain ? `, domain: ${domain}` : ""})`);
|
|
1158
1312
|
if (stagedSource)
|
|
1159
1313
|
console.log(` source -> ${rawEntry} (note's own source: kept - no redundant snapshot)`);
|
|
@@ -1369,7 +1523,7 @@ ${filed} filed, ${noop} no-op, ${conflict} conflict, ${error} error.`);
|
|
|
1369
1523
|
if (isFile)
|
|
1370
1524
|
copyFileSync(src, rawPath);
|
|
1371
1525
|
else
|
|
1372
|
-
|
|
1526
|
+
writeFileSync8(rawPath, text);
|
|
1373
1527
|
}
|
|
1374
1528
|
} catch (e) {
|
|
1375
1529
|
console.error(`cannot write snapshot ${rawPath}: ${e instanceof Error ? e.message : e}`);
|
|
@@ -1466,7 +1620,7 @@ ${body}`;
|
|
|
1466
1620
|
console.log(` hash8 collision with a distinct source -> trying \`events/${effectiveSlug}\` instead`);
|
|
1467
1621
|
}
|
|
1468
1622
|
}
|
|
1469
|
-
|
|
1623
|
+
writeFileSync8(notePath, note);
|
|
1470
1624
|
manifest[manifestKey] = { hash, note: notePath, ingested: new Date().toISOString(), raw: rawPath };
|
|
1471
1625
|
saveManifest(vault, manifest);
|
|
1472
1626
|
unresolved = [...speakers].filter((name) => !personResolved(vault, slugify(name), name));
|
|
@@ -1484,7 +1638,7 @@ ${body}`;
|
|
|
1484
1638
|
});
|
|
1485
1639
|
|
|
1486
1640
|
// scripts/lib/tags.ts
|
|
1487
|
-
import { readFileSync as readFileSync10, writeFileSync as
|
|
1641
|
+
import { readFileSync as readFileSync10, writeFileSync as writeFileSync9, existsSync as existsSync10 } from "node:fs";
|
|
1488
1642
|
import { join as join11 } from "node:path";
|
|
1489
1643
|
function section(text2, name) {
|
|
1490
1644
|
return text2.match(new RegExp(`##\\s*${name}\\s*\\n([\\s\\S]*?)(?:\\n##\\s|\\s*$)`, "i"))?.[1] ?? "";
|
|
@@ -1565,7 +1719,7 @@ function appendTags(vault2, newTags) {
|
|
|
1565
1719
|
const sec = `## Tags
|
|
1566
1720
|
${tags.join(", ")}
|
|
1567
1721
|
`;
|
|
1568
|
-
|
|
1722
|
+
writeFileSync9(p, base === "" ? sec : `${base}
|
|
1569
1723
|
|
|
1570
1724
|
${sec}`);
|
|
1571
1725
|
return tags;
|
|
@@ -1581,7 +1735,7 @@ ${sec}`);
|
|
|
1581
1735
|
lines2.splice(h + 1, 0, tags.join(", "));
|
|
1582
1736
|
else
|
|
1583
1737
|
lines2[last] = `${lines2[last].replace(/\s+$/, "")}, ${tags.join(", ")}`;
|
|
1584
|
-
|
|
1738
|
+
writeFileSync9(p, lines2.join(`
|
|
1585
1739
|
`));
|
|
1586
1740
|
return tags;
|
|
1587
1741
|
}
|
|
@@ -2011,7 +2165,7 @@ var init_snapshot = __esm(() => {
|
|
|
2011
2165
|
|
|
2012
2166
|
// scripts/check.ts
|
|
2013
2167
|
var exports_check = {};
|
|
2014
|
-
import { readdirSync as readdirSync8, readFileSync as readFileSync13, statSync as statSync8, existsSync as existsSync12, writeFileSync as
|
|
2168
|
+
import { readdirSync as readdirSync8, readFileSync as readFileSync13, statSync as statSync8, existsSync as existsSync12, writeFileSync as writeFileSync10 } from "node:fs";
|
|
2015
2169
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
2016
2170
|
import { join as join14, relative as relative6, dirname as dirname3 } from "node:path";
|
|
2017
2171
|
import { homedir as homedir3 } from "node:os";
|
|
@@ -2095,7 +2249,7 @@ ${REVIEW_END}
|
|
|
2095
2249
|
`) + section2;
|
|
2096
2250
|
}
|
|
2097
2251
|
if (next !== prev || !exists)
|
|
2098
|
-
|
|
2252
|
+
writeFileSync10(p, next);
|
|
2099
2253
|
return lines2.length ? "written" : "cleared";
|
|
2100
2254
|
}
|
|
2101
2255
|
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 +2646,9 @@ var init_cli = __esm(async () => {
|
|
|
2492
2646
|
}
|
|
2493
2647
|
case "lair": {
|
|
2494
2648
|
const home = requireVaultHome();
|
|
2495
|
-
const
|
|
2496
|
-
|
|
2649
|
+
const { backend, skipPermissions, passthrough } = resolveLaunch(rest, { agent: readDefaultAgent(), yolo: readSkipPermissions() });
|
|
2650
|
+
const spec = assembleSession({ cwd: home, vaultProject: home, pkgRoot, passthrough, skipPermissions, model: readDefaultModel() });
|
|
2651
|
+
process.exit(launchBackend(backend, home, backend.renderArgs(spec), spec.env));
|
|
2497
2652
|
}
|
|
2498
2653
|
case "context": {
|
|
2499
2654
|
const home = requireVaultHome();
|
|
@@ -2505,6 +2660,75 @@ var init_cli = __esm(async () => {
|
|
|
2505
2660
|
process.stdout.write(readFileSync14(contract, "utf8"));
|
|
2506
2661
|
break;
|
|
2507
2662
|
}
|
|
2663
|
+
case "agent": {
|
|
2664
|
+
const [name] = rest;
|
|
2665
|
+
if (!name) {
|
|
2666
|
+
const stored = readDefaultAgent();
|
|
2667
|
+
console.log(`default agent: ${stored ?? "claude (built-in)"}`);
|
|
2668
|
+
if (process.env.IMPRNT_AGENT)
|
|
2669
|
+
console.log(` IMPRNT_AGENT=${process.env.IMPRNT_AGENT} overrides it in this shell`);
|
|
2670
|
+
console.log("set: imprnt agent <claude|gemini> override one session: imp --gemini | imp --claude");
|
|
2671
|
+
break;
|
|
2672
|
+
}
|
|
2673
|
+
if (name !== "claude" && name !== "gemini") {
|
|
2674
|
+
console.error(`unknown agent "${name}" — valid: claude, gemini`);
|
|
2675
|
+
process.exit(1);
|
|
2676
|
+
}
|
|
2677
|
+
const r = setDefaultAgent(name);
|
|
2678
|
+
if (!r.ok) {
|
|
2679
|
+
console.error(`could not set default agent (${configPath()}): ${r.error}`);
|
|
2680
|
+
process.exit(1);
|
|
2681
|
+
}
|
|
2682
|
+
console.log(`default agent set to ${name} (${configPath()})`);
|
|
2683
|
+
break;
|
|
2684
|
+
}
|
|
2685
|
+
case "yolo": {
|
|
2686
|
+
const [arg] = rest;
|
|
2687
|
+
if (!arg) {
|
|
2688
|
+
console.log(`skip-permissions default: ${readSkipPermissions() ? "on" : "off"}`);
|
|
2689
|
+
if ("IMPRNT_YOLO" in process.env)
|
|
2690
|
+
console.log(` IMPRNT_YOLO=${process.env.IMPRNT_YOLO} overrides it in this shell`);
|
|
2691
|
+
console.log("set: imprnt yolo on|off override one session: imp --yolo | imp --safe");
|
|
2692
|
+
break;
|
|
2693
|
+
}
|
|
2694
|
+
if (arg !== "on" && arg !== "off") {
|
|
2695
|
+
console.error("usage: imprnt yolo [on|off]");
|
|
2696
|
+
process.exit(1);
|
|
2697
|
+
}
|
|
2698
|
+
const r = setSkipPermissions(arg === "on");
|
|
2699
|
+
if (!r.ok) {
|
|
2700
|
+
console.error(`could not set yolo default (${configPath()}): ${r.error}`);
|
|
2701
|
+
process.exit(1);
|
|
2702
|
+
}
|
|
2703
|
+
console.log(arg === "on" ? `skip-permissions ENABLED (${configPath()}) — every imp session now runs without permission prompts` : `skip-permissions disabled (${configPath()})`);
|
|
2704
|
+
break;
|
|
2705
|
+
}
|
|
2706
|
+
case "model": {
|
|
2707
|
+
const [arg] = rest;
|
|
2708
|
+
if (!arg) {
|
|
2709
|
+
const m = readDefaultModel();
|
|
2710
|
+
console.log(`default model: ${m ?? "(agent's own default)"}`);
|
|
2711
|
+
console.log("set: imprnt model <alias|id> clear: imprnt model off");
|
|
2712
|
+
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");
|
|
2713
|
+
break;
|
|
2714
|
+
}
|
|
2715
|
+
if (arg === "off" || arg === "default" || arg === "none") {
|
|
2716
|
+
const r2 = setDefaultModel("");
|
|
2717
|
+
if (!r2.ok) {
|
|
2718
|
+
console.error(`could not clear default model (${configPath()}): ${r2.error}`);
|
|
2719
|
+
process.exit(1);
|
|
2720
|
+
}
|
|
2721
|
+
console.log(`default model cleared (${configPath()}) — the agent uses its own default`);
|
|
2722
|
+
break;
|
|
2723
|
+
}
|
|
2724
|
+
const r = setDefaultModel(arg);
|
|
2725
|
+
if (!r.ok) {
|
|
2726
|
+
console.error(`could not set default model (${configPath()}): ${r.error}`);
|
|
2727
|
+
process.exit(1);
|
|
2728
|
+
}
|
|
2729
|
+
console.log(`default model set to ${arg} (${configPath()})`);
|
|
2730
|
+
break;
|
|
2731
|
+
}
|
|
2508
2732
|
case "plugin": {
|
|
2509
2733
|
const proj = pluginRoot();
|
|
2510
2734
|
if (proj !== process.cwd())
|
|
@@ -2818,17 +3042,21 @@ enable: imprnt plugin add <name> disable: imprnt plugin rm <name> [--purge]`);
|
|
|
2818
3042
|
if (wantsLaunch && (!bare || process.stdin.isTTY && process.stdout.isTTY)) {
|
|
2819
3043
|
const home = vaultProjectRoot();
|
|
2820
3044
|
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
|
|
3045
|
+
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
3046
|
}
|
|
2823
|
-
const
|
|
2824
|
-
|
|
3047
|
+
const raw = bare ? [] : [cmd, ...rest];
|
|
3048
|
+
const { backend, skipPermissions, passthrough } = resolveLaunch(raw, { agent: readDefaultAgent(), yolo: readSkipPermissions() });
|
|
3049
|
+
const spec = assembleSession({ cwd: process.cwd(), vaultProject: home, pkgRoot, passthrough, skipPermissions, model: readDefaultModel() });
|
|
3050
|
+
process.exit(launchBackend(backend, process.cwd(), backend.renderArgs(spec), spec.env));
|
|
2825
3051
|
}
|
|
2826
3052
|
console.log(`imprnt — deterministic-first markdown knowledge vault
|
|
2827
3053
|
|
|
2828
3054
|
the front door (the \`imp\` bin):
|
|
2829
|
-
imp open your assistant HERE:
|
|
3055
|
+
imp open your assistant HERE: your agent + your cast + the vault pointer
|
|
2830
3056
|
imp lair open it in your vault project — full contract, resumable history
|
|
2831
|
-
imp
|
|
3057
|
+
imp --gemini | --claude pick the agent backend for this session (default: imprnt agent)
|
|
3058
|
+
imp --yolo | --safe skip / keep permission prompts for this session (default: imprnt yolo)
|
|
3059
|
+
imp -c | --resume | <agent flags> flags pass through to the chosen agent
|
|
2832
3060
|
|
|
2833
3061
|
engine (same subcommands under \`imp\` or \`imprnt\`):
|
|
2834
3062
|
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 +3064,9 @@ engine (same subcommands under \`imp\` or \`imprnt\`):
|
|
|
2836
3064
|
imprnt ingest <file|text> [--vault D] snapshot a source -> raw/; a transcript file also gets an event skeleton (no LLM)
|
|
2837
3065
|
imprnt recall "<query>" [--vault D] synonym-aware BM25 ranking over the vault
|
|
2838
3066
|
imprnt context print the vault contract — agents run this before writing any note
|
|
3067
|
+
imprnt agent [claude|gemini] show or set the per-machine default agent backend for imp
|
|
3068
|
+
imprnt yolo [on|off] show or set the per-machine skip-permissions default for imp
|
|
3069
|
+
imprnt model [alias|id|off] show or set the per-machine default model (gemini aliases: pro, flash, pro25, lite, ...)
|
|
2839
3070
|
imprnt check [--all] [--vault D] integrity (orphan links, disconnected notes, uncovered snapshots) + regenerate index.md; --all also runs each plugins/*/check.js
|
|
2840
3071
|
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
3072
|
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 {
|
|
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
|
-
|
|
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,160 @@ function mergeFragment(pass, fragment, extraInject) {
|
|
|
930
977
|
args.push(...inject);
|
|
931
978
|
return args;
|
|
932
979
|
}
|
|
933
|
-
function
|
|
934
|
-
const
|
|
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
|
-
|
|
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
|
|
944
|
-
return {
|
|
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
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
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
|
|
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 escapeGeminiImports(text) {
|
|
1024
|
+
return text.replaceAll("@", "\\@");
|
|
1025
|
+
}
|
|
1026
|
+
function geminiRenderArgs(spec) {
|
|
1027
|
+
const includes = [];
|
|
1028
|
+
const systemPrompt = joinFragments([spec.cast, spec.pointer, spec.globals]);
|
|
1029
|
+
if (systemPrompt) {
|
|
1030
|
+
const dir = mkdtempSync2(join8(tmpdir2(), "imprnt-gemini-"));
|
|
1031
|
+
writeFileSync6(join8(dir, "GEMINI.md"), escapeGeminiImports(systemPrompt) + `
|
|
1032
|
+
`);
|
|
1033
|
+
process.on("exit", () => {
|
|
1034
|
+
try {
|
|
1035
|
+
rmSync3(dir, { recursive: true, force: true });
|
|
1036
|
+
} catch {}
|
|
1037
|
+
});
|
|
1038
|
+
includes.push(dir);
|
|
1039
|
+
}
|
|
1040
|
+
includes.push(...spec.addDirs);
|
|
1041
|
+
if (spec.pluginsRoot && harnessFlags(spec.pluginsRoot).length) {
|
|
1042
|
+
console.error("imp: gemini does not host claude harness plugins (statusline, timemachine) - skipping them");
|
|
1043
|
+
}
|
|
1044
|
+
const lead = [];
|
|
1045
|
+
if (spec.skipPermissions) {
|
|
1046
|
+
if (!spec.passthrough.includes("--yolo") && !spec.passthrough.includes("-y"))
|
|
1047
|
+
lead.push("--yolo");
|
|
1048
|
+
if (!spec.passthrough.includes("--skip-trust"))
|
|
1049
|
+
lead.push("--skip-trust");
|
|
1050
|
+
}
|
|
1051
|
+
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) {
|
|
1056
|
+
pass.unshift("-m", expandGeminiModel(spec.model));
|
|
1057
|
+
}
|
|
1058
|
+
const rIdx = pass.findIndex((a) => a === "-r" || a === "--resume");
|
|
1059
|
+
if (rIdx >= 0 && (pass[rIdx + 1] === undefined || pass[rIdx + 1].startsWith("-"))) {
|
|
1060
|
+
pass.splice(rIdx + 1, 0, "latest");
|
|
1061
|
+
}
|
|
1062
|
+
const flags = includes.length ? ["--include-directories", includes.join(",")] : [];
|
|
1063
|
+
return [...lead, ...flags, ...pass];
|
|
1064
|
+
}
|
|
1065
|
+
function resolveLaunch(passthrough, config = {}) {
|
|
1066
|
+
let agentFlag;
|
|
1067
|
+
let yoloFlag;
|
|
1068
|
+
const rest = [];
|
|
1069
|
+
for (const a of passthrough) {
|
|
1070
|
+
if (a === "--gemini")
|
|
1071
|
+
agentFlag = agentFlag ?? "gemini";
|
|
1072
|
+
else if (a === "--claude")
|
|
1073
|
+
agentFlag = agentFlag ?? "claude";
|
|
1074
|
+
else if (a === "--yolo")
|
|
1075
|
+
yoloFlag = yoloFlag ?? true;
|
|
1076
|
+
else if (a === "--safe")
|
|
1077
|
+
yoloFlag = yoloFlag ?? false;
|
|
1078
|
+
else
|
|
1079
|
+
rest.push(a);
|
|
1080
|
+
}
|
|
1081
|
+
const name = agentFlag ?? process.env.IMPRNT_AGENT ?? config.agent ?? "claude";
|
|
1082
|
+
let backend = backends[name];
|
|
1083
|
+
if (!backend) {
|
|
1084
|
+
console.error(`imp: unknown agent "${name}" - falling back to claude (valid: ${Object.keys(backends).join(", ")})`);
|
|
1085
|
+
backend = claudeBackend;
|
|
1086
|
+
}
|
|
1087
|
+
const envYolo = "IMPRNT_YOLO" in process.env ? !["0", "false", ""].includes(process.env.IMPRNT_YOLO ?? "") : undefined;
|
|
1088
|
+
const skipPermissions = yoloFlag ?? envYolo ?? config.yolo ?? false;
|
|
1089
|
+
return { backend, skipPermissions, passthrough: rest };
|
|
1090
|
+
}
|
|
1091
|
+
function launchBackend(backend, cwd, args, env = process.env) {
|
|
961
1092
|
if (!isDir(cwd)) {
|
|
962
1093
|
console.error(`imp: vault project not found at ${cwd} — re-run \`imprnt init\` in its new location (add --register to switch the default)`);
|
|
963
1094
|
return 1;
|
|
964
1095
|
}
|
|
965
|
-
const r = spawnSync2(
|
|
1096
|
+
const r = spawnSync2(backend.name, args, { cwd, stdio: "inherit", env });
|
|
966
1097
|
if (r.error) {
|
|
967
1098
|
const code = r.error.code;
|
|
968
|
-
console.error(code === "ENOENT" ?
|
|
1099
|
+
console.error(code === "ENOENT" ? `imp: \`${backend.name}\` not found on PATH. ${backend.missingHint}` : `imp: failed to launch ${backend.name}: ${r.error.message}`);
|
|
969
1100
|
return 1;
|
|
970
1101
|
}
|
|
971
1102
|
return r.status ?? 1;
|
|
972
1103
|
}
|
|
1104
|
+
var claudeBackend, GEMINI_MODEL_ALIASES, geminiBackend, backends;
|
|
973
1105
|
var init_launch = __esm(() => {
|
|
974
1106
|
init_plugins();
|
|
975
1107
|
init_global();
|
|
1108
|
+
claudeBackend = {
|
|
1109
|
+
name: "claude",
|
|
1110
|
+
missingHint: "Install Claude Code first: npm i -g @anthropic-ai/claude-code",
|
|
1111
|
+
renderArgs: claudeRenderArgs
|
|
1112
|
+
};
|
|
1113
|
+
GEMINI_MODEL_ALIASES = {
|
|
1114
|
+
pro: "gemini-3.1-pro-preview",
|
|
1115
|
+
flash: "gemini-3.5-flash",
|
|
1116
|
+
pro25: "gemini-2.5-pro",
|
|
1117
|
+
lite: "gemini-3.1-flash-lite",
|
|
1118
|
+
gemma31: "gemma-4-31b-it",
|
|
1119
|
+
gemma26: "gemma-4-26b-a4b-it"
|
|
1120
|
+
};
|
|
1121
|
+
geminiBackend = {
|
|
1122
|
+
name: "gemini",
|
|
1123
|
+
missingHint: "Install the Gemini CLI first: npm i -g @google/gemini-cli",
|
|
1124
|
+
renderArgs: geminiRenderArgs
|
|
1125
|
+
};
|
|
1126
|
+
backends = {
|
|
1127
|
+
claude: claudeBackend,
|
|
1128
|
+
gemini: geminiBackend
|
|
1129
|
+
};
|
|
976
1130
|
});
|
|
977
1131
|
|
|
978
1132
|
// scripts/lib/manifest.ts
|
|
979
|
-
import { readFileSync as readFileSync8, writeFileSync as
|
|
1133
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync7, renameSync, existsSync as existsSync8 } from "node:fs";
|
|
980
1134
|
import { join as join9 } from "node:path";
|
|
981
1135
|
function manifestPath(vault) {
|
|
982
1136
|
return join9(vault, ".manifest.json");
|
|
@@ -1000,7 +1154,7 @@ function loadManifest(vault) {
|
|
|
1000
1154
|
function saveManifest(vault, m) {
|
|
1001
1155
|
const p = manifestPath(vault);
|
|
1002
1156
|
const tmp = `${p}.tmp.${process.pid}.${Date.now()}`;
|
|
1003
|
-
|
|
1157
|
+
writeFileSync7(tmp, JSON.stringify(m, null, 2) + `
|
|
1004
1158
|
`);
|
|
1005
1159
|
renameSync(tmp, p);
|
|
1006
1160
|
}
|
|
@@ -1008,7 +1162,7 @@ var init_manifest = () => {};
|
|
|
1008
1162
|
|
|
1009
1163
|
// scripts/ingest.ts
|
|
1010
1164
|
var exports_ingest = {};
|
|
1011
|
-
import { readFileSync as readFileSync9, writeFileSync as
|
|
1165
|
+
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
1166
|
import { createHash } from "node:crypto";
|
|
1013
1167
|
import { basename as basename2, extname, join as join10, relative as relative3 } from "node:path";
|
|
1014
1168
|
function slugify(s) {
|
|
@@ -1124,7 +1278,7 @@ function applyStaged(staged, vault) {
|
|
|
1124
1278
|
const existingHash = createHash("sha256").update(readFileSync9(notePath, "utf8")).digest("hex").slice(0, 16);
|
|
1125
1279
|
if (existingHash === fileHash) {
|
|
1126
1280
|
console.log(` = ${noteRel} already filed, identical content (hash ${fileHash}) — no-op`);
|
|
1127
|
-
|
|
1281
|
+
rmSync4(staged);
|
|
1128
1282
|
return "noop";
|
|
1129
1283
|
}
|
|
1130
1284
|
console.error(` ! ${noteRel} exists with DIFFERENT content — not overwriting (contradiction discipline)`);
|
|
@@ -1135,12 +1289,12 @@ function applyStaged(staged, vault) {
|
|
|
1135
1289
|
if (!stagedSource && !priorSnapshot) {
|
|
1136
1290
|
mkdirSync3(rawDir, { recursive: true });
|
|
1137
1291
|
if (!existsSync9(rawPath))
|
|
1138
|
-
|
|
1292
|
+
writeFileSync8(rawPath, text);
|
|
1139
1293
|
}
|
|
1140
1294
|
manifest[manifestKey] = { hash, note: notePath, ingested: new Date().toISOString(), raw: rawEntry };
|
|
1141
1295
|
saveManifest(vault, manifest);
|
|
1142
1296
|
mkdirSync3(join10(vault, folder), { recursive: true });
|
|
1143
|
-
|
|
1297
|
+
writeFileSync8(notePath, finalText);
|
|
1144
1298
|
const participantsList = fmList2(fm, "participants");
|
|
1145
1299
|
const participantsScalar = participantsList.length ? "" : fmScalar2(fm, "participants");
|
|
1146
1300
|
const participants = (participantsScalar ? [participantsScalar] : participantsList).map(linkSlug);
|
|
@@ -1153,7 +1307,7 @@ function applyStaged(staged, vault) {
|
|
|
1153
1307
|
flagNeedsReview(vault, `- [ ] unresolved person \`people/${personSlug}\` — from [[${noteRel}]] (applied from \`${staged}\`)`);
|
|
1154
1308
|
}
|
|
1155
1309
|
}
|
|
1156
|
-
|
|
1310
|
+
rmSync4(staged);
|
|
1157
1311
|
console.log(` ✓ filed ${noteRel} (type: ${type}${domain ? `, domain: ${domain}` : ""})`);
|
|
1158
1312
|
if (stagedSource)
|
|
1159
1313
|
console.log(` source -> ${rawEntry} (note's own source: kept - no redundant snapshot)`);
|
|
@@ -1369,7 +1523,7 @@ ${filed} filed, ${noop} no-op, ${conflict} conflict, ${error} error.`);
|
|
|
1369
1523
|
if (isFile)
|
|
1370
1524
|
copyFileSync(src, rawPath);
|
|
1371
1525
|
else
|
|
1372
|
-
|
|
1526
|
+
writeFileSync8(rawPath, text);
|
|
1373
1527
|
}
|
|
1374
1528
|
} catch (e) {
|
|
1375
1529
|
console.error(`cannot write snapshot ${rawPath}: ${e instanceof Error ? e.message : e}`);
|
|
@@ -1466,7 +1620,7 @@ ${body}`;
|
|
|
1466
1620
|
console.log(` hash8 collision with a distinct source -> trying \`events/${effectiveSlug}\` instead`);
|
|
1467
1621
|
}
|
|
1468
1622
|
}
|
|
1469
|
-
|
|
1623
|
+
writeFileSync8(notePath, note);
|
|
1470
1624
|
manifest[manifestKey] = { hash, note: notePath, ingested: new Date().toISOString(), raw: rawPath };
|
|
1471
1625
|
saveManifest(vault, manifest);
|
|
1472
1626
|
unresolved = [...speakers].filter((name) => !personResolved(vault, slugify(name), name));
|
|
@@ -1484,7 +1638,7 @@ ${body}`;
|
|
|
1484
1638
|
});
|
|
1485
1639
|
|
|
1486
1640
|
// scripts/lib/tags.ts
|
|
1487
|
-
import { readFileSync as readFileSync10, writeFileSync as
|
|
1641
|
+
import { readFileSync as readFileSync10, writeFileSync as writeFileSync9, existsSync as existsSync10 } from "node:fs";
|
|
1488
1642
|
import { join as join11 } from "node:path";
|
|
1489
1643
|
function section(text2, name) {
|
|
1490
1644
|
return text2.match(new RegExp(`##\\s*${name}\\s*\\n([\\s\\S]*?)(?:\\n##\\s|\\s*$)`, "i"))?.[1] ?? "";
|
|
@@ -1565,7 +1719,7 @@ function appendTags(vault2, newTags) {
|
|
|
1565
1719
|
const sec = `## Tags
|
|
1566
1720
|
${tags.join(", ")}
|
|
1567
1721
|
`;
|
|
1568
|
-
|
|
1722
|
+
writeFileSync9(p, base === "" ? sec : `${base}
|
|
1569
1723
|
|
|
1570
1724
|
${sec}`);
|
|
1571
1725
|
return tags;
|
|
@@ -1581,7 +1735,7 @@ ${sec}`);
|
|
|
1581
1735
|
lines2.splice(h + 1, 0, tags.join(", "));
|
|
1582
1736
|
else
|
|
1583
1737
|
lines2[last] = `${lines2[last].replace(/\s+$/, "")}, ${tags.join(", ")}`;
|
|
1584
|
-
|
|
1738
|
+
writeFileSync9(p, lines2.join(`
|
|
1585
1739
|
`));
|
|
1586
1740
|
return tags;
|
|
1587
1741
|
}
|
|
@@ -2011,7 +2165,7 @@ var init_snapshot = __esm(() => {
|
|
|
2011
2165
|
|
|
2012
2166
|
// scripts/check.ts
|
|
2013
2167
|
var exports_check = {};
|
|
2014
|
-
import { readdirSync as readdirSync8, readFileSync as readFileSync13, statSync as statSync8, existsSync as existsSync12, writeFileSync as
|
|
2168
|
+
import { readdirSync as readdirSync8, readFileSync as readFileSync13, statSync as statSync8, existsSync as existsSync12, writeFileSync as writeFileSync10 } from "node:fs";
|
|
2015
2169
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
2016
2170
|
import { join as join14, relative as relative6, dirname as dirname3 } from "node:path";
|
|
2017
2171
|
import { homedir as homedir3 } from "node:os";
|
|
@@ -2095,7 +2249,7 @@ ${REVIEW_END}
|
|
|
2095
2249
|
`) + section2;
|
|
2096
2250
|
}
|
|
2097
2251
|
if (next !== prev || !exists)
|
|
2098
|
-
|
|
2252
|
+
writeFileSync10(p, next);
|
|
2099
2253
|
return lines2.length ? "written" : "cleared";
|
|
2100
2254
|
}
|
|
2101
2255
|
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 +2646,9 @@ var init_cli = __esm(async () => {
|
|
|
2492
2646
|
}
|
|
2493
2647
|
case "lair": {
|
|
2494
2648
|
const home = requireVaultHome();
|
|
2495
|
-
const
|
|
2496
|
-
|
|
2649
|
+
const { backend, skipPermissions, passthrough } = resolveLaunch(rest, { agent: readDefaultAgent(), yolo: readSkipPermissions() });
|
|
2650
|
+
const spec = assembleSession({ cwd: home, vaultProject: home, pkgRoot, passthrough, skipPermissions, model: readDefaultModel() });
|
|
2651
|
+
process.exit(launchBackend(backend, home, backend.renderArgs(spec), spec.env));
|
|
2497
2652
|
}
|
|
2498
2653
|
case "context": {
|
|
2499
2654
|
const home = requireVaultHome();
|
|
@@ -2505,6 +2660,75 @@ var init_cli = __esm(async () => {
|
|
|
2505
2660
|
process.stdout.write(readFileSync14(contract, "utf8"));
|
|
2506
2661
|
break;
|
|
2507
2662
|
}
|
|
2663
|
+
case "agent": {
|
|
2664
|
+
const [name] = rest;
|
|
2665
|
+
if (!name) {
|
|
2666
|
+
const stored = readDefaultAgent();
|
|
2667
|
+
console.log(`default agent: ${stored ?? "claude (built-in)"}`);
|
|
2668
|
+
if (process.env.IMPRNT_AGENT)
|
|
2669
|
+
console.log(` IMPRNT_AGENT=${process.env.IMPRNT_AGENT} overrides it in this shell`);
|
|
2670
|
+
console.log("set: imprnt agent <claude|gemini> override one session: imp --gemini | imp --claude");
|
|
2671
|
+
break;
|
|
2672
|
+
}
|
|
2673
|
+
if (name !== "claude" && name !== "gemini") {
|
|
2674
|
+
console.error(`unknown agent "${name}" — valid: claude, gemini`);
|
|
2675
|
+
process.exit(1);
|
|
2676
|
+
}
|
|
2677
|
+
const r = setDefaultAgent(name);
|
|
2678
|
+
if (!r.ok) {
|
|
2679
|
+
console.error(`could not set default agent (${configPath()}): ${r.error}`);
|
|
2680
|
+
process.exit(1);
|
|
2681
|
+
}
|
|
2682
|
+
console.log(`default agent set to ${name} (${configPath()})`);
|
|
2683
|
+
break;
|
|
2684
|
+
}
|
|
2685
|
+
case "yolo": {
|
|
2686
|
+
const [arg] = rest;
|
|
2687
|
+
if (!arg) {
|
|
2688
|
+
console.log(`skip-permissions default: ${readSkipPermissions() ? "on" : "off"}`);
|
|
2689
|
+
if ("IMPRNT_YOLO" in process.env)
|
|
2690
|
+
console.log(` IMPRNT_YOLO=${process.env.IMPRNT_YOLO} overrides it in this shell`);
|
|
2691
|
+
console.log("set: imprnt yolo on|off override one session: imp --yolo | imp --safe");
|
|
2692
|
+
break;
|
|
2693
|
+
}
|
|
2694
|
+
if (arg !== "on" && arg !== "off") {
|
|
2695
|
+
console.error("usage: imprnt yolo [on|off]");
|
|
2696
|
+
process.exit(1);
|
|
2697
|
+
}
|
|
2698
|
+
const r = setSkipPermissions(arg === "on");
|
|
2699
|
+
if (!r.ok) {
|
|
2700
|
+
console.error(`could not set yolo default (${configPath()}): ${r.error}`);
|
|
2701
|
+
process.exit(1);
|
|
2702
|
+
}
|
|
2703
|
+
console.log(arg === "on" ? `skip-permissions ENABLED (${configPath()}) — every imp session now runs without permission prompts` : `skip-permissions disabled (${configPath()})`);
|
|
2704
|
+
break;
|
|
2705
|
+
}
|
|
2706
|
+
case "model": {
|
|
2707
|
+
const [arg] = rest;
|
|
2708
|
+
if (!arg) {
|
|
2709
|
+
const m = readDefaultModel();
|
|
2710
|
+
console.log(`default model: ${m ?? "(agent's own default)"}`);
|
|
2711
|
+
console.log("set: imprnt model <alias|id> clear: imprnt model off");
|
|
2712
|
+
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");
|
|
2713
|
+
break;
|
|
2714
|
+
}
|
|
2715
|
+
if (arg === "off" || arg === "default" || arg === "none") {
|
|
2716
|
+
const r2 = setDefaultModel("");
|
|
2717
|
+
if (!r2.ok) {
|
|
2718
|
+
console.error(`could not clear default model (${configPath()}): ${r2.error}`);
|
|
2719
|
+
process.exit(1);
|
|
2720
|
+
}
|
|
2721
|
+
console.log(`default model cleared (${configPath()}) — the agent uses its own default`);
|
|
2722
|
+
break;
|
|
2723
|
+
}
|
|
2724
|
+
const r = setDefaultModel(arg);
|
|
2725
|
+
if (!r.ok) {
|
|
2726
|
+
console.error(`could not set default model (${configPath()}): ${r.error}`);
|
|
2727
|
+
process.exit(1);
|
|
2728
|
+
}
|
|
2729
|
+
console.log(`default model set to ${arg} (${configPath()})`);
|
|
2730
|
+
break;
|
|
2731
|
+
}
|
|
2508
2732
|
case "plugin": {
|
|
2509
2733
|
const proj = pluginRoot();
|
|
2510
2734
|
if (proj !== process.cwd())
|
|
@@ -2818,17 +3042,21 @@ enable: imprnt plugin add <name> disable: imprnt plugin rm <name> [--purge]`);
|
|
|
2818
3042
|
if (wantsLaunch && (!bare || process.stdin.isTTY && process.stdout.isTTY)) {
|
|
2819
3043
|
const home = vaultProjectRoot();
|
|
2820
3044
|
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
|
|
3045
|
+
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
3046
|
}
|
|
2823
|
-
const
|
|
2824
|
-
|
|
3047
|
+
const raw = bare ? [] : [cmd, ...rest];
|
|
3048
|
+
const { backend, skipPermissions, passthrough } = resolveLaunch(raw, { agent: readDefaultAgent(), yolo: readSkipPermissions() });
|
|
3049
|
+
const spec = assembleSession({ cwd: process.cwd(), vaultProject: home, pkgRoot, passthrough, skipPermissions, model: readDefaultModel() });
|
|
3050
|
+
process.exit(launchBackend(backend, process.cwd(), backend.renderArgs(spec), spec.env));
|
|
2825
3051
|
}
|
|
2826
3052
|
console.log(`imprnt — deterministic-first markdown knowledge vault
|
|
2827
3053
|
|
|
2828
3054
|
the front door (the \`imp\` bin):
|
|
2829
|
-
imp open your assistant HERE:
|
|
3055
|
+
imp open your assistant HERE: your agent + your cast + the vault pointer
|
|
2830
3056
|
imp lair open it in your vault project — full contract, resumable history
|
|
2831
|
-
imp
|
|
3057
|
+
imp --gemini | --claude pick the agent backend for this session (default: imprnt agent)
|
|
3058
|
+
imp --yolo | --safe skip / keep permission prompts for this session (default: imprnt yolo)
|
|
3059
|
+
imp -c | --resume | <agent flags> flags pass through to the chosen agent
|
|
2832
3060
|
|
|
2833
3061
|
engine (same subcommands under \`imp\` or \`imprnt\`):
|
|
2834
3062
|
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 +3064,9 @@ engine (same subcommands under \`imp\` or \`imprnt\`):
|
|
|
2836
3064
|
imprnt ingest <file|text> [--vault D] snapshot a source -> raw/; a transcript file also gets an event skeleton (no LLM)
|
|
2837
3065
|
imprnt recall "<query>" [--vault D] synonym-aware BM25 ranking over the vault
|
|
2838
3066
|
imprnt context print the vault contract — agents run this before writing any note
|
|
3067
|
+
imprnt agent [claude|gemini] show or set the per-machine default agent backend for imp
|
|
3068
|
+
imprnt yolo [on|off] show or set the per-machine skip-permissions default for imp
|
|
3069
|
+
imprnt model [alias|id|off] show or set the per-machine default model (gemini aliases: pro, flash, pro25, lite, ...)
|
|
2839
3070
|
imprnt check [--all] [--vault D] integrity (orphan links, disconnected notes, uncovered snapshots) + regenerate index.md; --all also runs each plugins/*/check.js
|
|
2840
3071
|
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
3072
|
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.
|
|
3
|
+
"version": "0.1.3-edge.59",
|
|
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": {
|