imprnt 0.1.3-edge.53 → 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.
- package/CLAUDE.md +10 -0
- package/dist/cli.js +316 -58
- package/dist/imp.js +316 -58
- package/package.json +1 -1
- package/templates/pointer.md +2 -0
package/CLAUDE.md
CHANGED
|
@@ -13,6 +13,16 @@ You never run the CLI by hand. You speak in plain language ("ingest this", "load
|
|
|
13
13
|
|
|
14
14
|
The whole vault is yours, local, owner-only (`chmod 700`), never shared. There is **no** sensitivity field, no redaction pass, no secret-fencing. It holds everything, including medical, financial, and personal data, because that's the point. The only rule: it never goes near a public repo (`.gitignore` guards it if the dir is ever git-init'd). If you ever publish a subset (an article), that's an **export-time** filter you run consciously then, not a tax paid on every note at ingest.
|
|
15
15
|
|
|
16
|
+
## Memory: the vault is the only store
|
|
17
|
+
|
|
18
|
+
One knowledge store, reached on demand. Do **not** use the host's auto-memory (Claude Code's `MEMORY.md` + `memory/*.md`), and do not treat it as a place facts may live. It is a second always-on store `recall` cannot search, so anything parked there is invisible knowledge that silently misleads the next session.
|
|
19
|
+
|
|
20
|
+
The routing test, applied before saving anything durable:
|
|
21
|
+
- **Knowledge** (a fact about a person, a record, a number, project state, a reference) is a **vault note**, found by `recall`.
|
|
22
|
+
- **Behavior** (a standing instruction, a taste rule, how to act) is a markdown fragment loaded every session: this contract for the universal rules, `CLAUDE.local.md` + `plugins/_personal/*` (gitignored) for the personal ones. Never the auto-memory.
|
|
23
|
+
|
|
24
|
+
The same fact never lives in both. Two copies drift, and one is invisible to search. `imprnt check` sweeps every project's host auto-memory (read-only) and flags any that is non-empty, so a leak is caught, not trusted. This is also what keeps imprnt vendor-neutral: the knowledge is plain files you own, with nothing locked inside one vendor's memory, so the agent underneath is swappable.
|
|
25
|
+
|
|
16
26
|
## The one rule: deterministic-first = ration the LLM by WHERE it runs
|
|
17
27
|
|
|
18
28
|
Deterministic-first does **not** mean "avoid the LLM." It means **invest it where it pays and keep it out of the hot path**. The line is drawn by *how often a step runs*:
|
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,153 @@ 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 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(
|
|
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" ?
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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";
|
|
@@ -2036,6 +2183,28 @@ function targetFolders(target) {
|
|
|
2036
2183
|
function linksEntity(target) {
|
|
2037
2184
|
return targetFolders(target).some((f) => ENTITY_FOLDERS.has(f));
|
|
2038
2185
|
}
|
|
2186
|
+
function hostMemoryStores() {
|
|
2187
|
+
const override = process.env.IMPRNT_HOST_MEMORY_DIR;
|
|
2188
|
+
if (override)
|
|
2189
|
+
return [override];
|
|
2190
|
+
const configDir = process.env.CLAUDE_CONFIG_DIR || join14(homedir3(), ".claude");
|
|
2191
|
+
const projectsDir = join14(configDir, "projects");
|
|
2192
|
+
if (!existsSync12(projectsDir))
|
|
2193
|
+
return [];
|
|
2194
|
+
let entries;
|
|
2195
|
+
try {
|
|
2196
|
+
entries = readdirSync8(projectsDir);
|
|
2197
|
+
} catch {
|
|
2198
|
+
return [];
|
|
2199
|
+
}
|
|
2200
|
+
return entries.map((p) => join14(projectsDir, p, "memory")).filter((d) => {
|
|
2201
|
+
try {
|
|
2202
|
+
return statSync8(d).isDirectory();
|
|
2203
|
+
} catch {
|
|
2204
|
+
return false;
|
|
2205
|
+
}
|
|
2206
|
+
}).sort();
|
|
2207
|
+
}
|
|
2039
2208
|
function syncNeedsReview(lines2) {
|
|
2040
2209
|
const p = join14(vault4, "_needs-review.md");
|
|
2041
2210
|
const exists = existsSync12(p);
|
|
@@ -2073,10 +2242,10 @@ ${REVIEW_END}
|
|
|
2073
2242
|
`) + section2;
|
|
2074
2243
|
}
|
|
2075
2244
|
if (next !== prev || !exists)
|
|
2076
|
-
|
|
2245
|
+
writeFileSync10(p, next);
|
|
2077
2246
|
return lines2.length ? "written" : "cleared";
|
|
2078
2247
|
}
|
|
2079
|
-
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,
|
|
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;
|
|
2080
2249
|
var init_check = __esm(() => {
|
|
2081
2250
|
init_roots();
|
|
2082
2251
|
init_moc();
|
|
@@ -2214,10 +2383,14 @@ var init_check = __esm(() => {
|
|
|
2214
2383
|
uncovered = [...new Set(rawEntries.map(norm))].filter((r) => !refNorm.has(r)).sort();
|
|
2215
2384
|
for (const r of uncovered)
|
|
2216
2385
|
review.push(`- [ ] unclassified snapshot \`${r}\` — no vault note points back`);
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2386
|
+
memoryStores = hostMemoryStores();
|
|
2387
|
+
strayStores = memoryStores.map((dir2) => ({
|
|
2388
|
+
dir: dir2,
|
|
2389
|
+
files: existsSync12(dir2) ? readdirSync8(dir2).filter((f) => f.endsWith(".md") && f !== "MEMORY.md").sort() : []
|
|
2390
|
+
})).filter((s) => s.files.length > 0);
|
|
2391
|
+
strayCount = strayStores.reduce((n, s) => n + s.files.length, 0);
|
|
2392
|
+
for (const s of strayStores)
|
|
2393
|
+
review.push(`- [ ] host auto-memory not empty — ${s.files.length} note(s) in ${s.dir}; migrate knowledge → vault notes, behavior → a CLAUDE.local.md fragment, then empty it`);
|
|
2221
2394
|
console.log(`imprnt check — ${notes.length} notes in ${vault4}
|
|
2222
2395
|
`);
|
|
2223
2396
|
if (orphans.length) {
|
|
@@ -2269,14 +2442,17 @@ var init_check = __esm(() => {
|
|
|
2269
2442
|
} else
|
|
2270
2443
|
console.log("✓ every raw snapshot has a derived note");
|
|
2271
2444
|
}
|
|
2272
|
-
if (
|
|
2273
|
-
console.log(`⚠ host auto-memory not empty (${
|
|
2274
|
-
|
|
2445
|
+
if (strayCount) {
|
|
2446
|
+
console.log(`⚠ host auto-memory not empty (${strayCount} in ${strayStores.length} project store(s)) — a second store recall can't see:`);
|
|
2447
|
+
for (const s of strayStores) {
|
|
2448
|
+
console.log(` ${s.dir}`);
|
|
2449
|
+
console.log(cap(s.files.map((f) => " " + f)).join(`
|
|
2275
2450
|
`));
|
|
2276
|
-
|
|
2451
|
+
}
|
|
2452
|
+
console.log(` migrate knowledge → vault notes, behavior → a CLAUDE.local.md fragment, then empty each.
|
|
2277
2453
|
`);
|
|
2278
2454
|
} else
|
|
2279
|
-
console.log(`✓ host auto-memory empty (vault is the only store) —
|
|
2455
|
+
console.log(`✓ host auto-memory empty (vault is the only store) — swept ${memoryStores.length} project store(s)`);
|
|
2280
2456
|
({ count, folders } = generateIndex(vault4));
|
|
2281
2457
|
console.log(`↻ regenerated index.md — ${count} notes across ${folders} folders`);
|
|
2282
2458
|
synced = syncNeedsReview(review);
|
|
@@ -2284,7 +2460,7 @@ var init_check = __esm(() => {
|
|
|
2284
2460
|
console.log(`↻ ${review.length} finding(s) → _needs-review.md (run \`imprnt hot\` to see them)`);
|
|
2285
2461
|
else if (synced === "cleared")
|
|
2286
2462
|
console.log("↻ cleared resolved findings from _needs-review.md");
|
|
2287
|
-
issues = orphans.length + disconnected.length + domainIssues.length + untagged.length + uncovered.length + dupPairs.length +
|
|
2463
|
+
issues = orphans.length + disconnected.length + domainIssues.length + untagged.length + uncovered.length + dupPairs.length + strayCount;
|
|
2288
2464
|
console.log(issues ? `
|
|
2289
2465
|
${issues} thing(s) to look at above.` : `
|
|
2290
2466
|
clean.`);
|
|
@@ -2334,7 +2510,7 @@ all plugin checks passed.`);
|
|
|
2334
2510
|
|
|
2335
2511
|
// scripts/cli.ts
|
|
2336
2512
|
var exports_cli = {};
|
|
2337
|
-
import { cpSync as cpSync3, mkdirSync as mkdirSync5, existsSync as existsSync13, readFileSync as readFileSync14, lstatSync as lstatSync4, statSync as statSync9, realpathSync as realpathSync2 } from "node:fs";
|
|
2513
|
+
import { cpSync as cpSync3, mkdirSync as mkdirSync5, chmodSync, existsSync as existsSync13, readFileSync as readFileSync14, lstatSync as lstatSync4, statSync as statSync9, realpathSync as realpathSync2 } from "node:fs";
|
|
2338
2514
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
2339
2515
|
import { join as join15, dirname as dirname4, resolve as resolve6, basename as basename5 } from "node:path";
|
|
2340
2516
|
import { homedir as homedir4 } from "node:os";
|
|
@@ -2463,8 +2639,9 @@ var init_cli = __esm(async () => {
|
|
|
2463
2639
|
}
|
|
2464
2640
|
case "lair": {
|
|
2465
2641
|
const home = requireVaultHome();
|
|
2466
|
-
const
|
|
2467
|
-
|
|
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));
|
|
2468
2645
|
}
|
|
2469
2646
|
case "context": {
|
|
2470
2647
|
const home = requireVaultHome();
|
|
@@ -2476,6 +2653,75 @@ var init_cli = __esm(async () => {
|
|
|
2476
2653
|
process.stdout.write(readFileSync14(contract, "utf8"));
|
|
2477
2654
|
break;
|
|
2478
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
|
+
}
|
|
2479
2725
|
case "plugin": {
|
|
2480
2726
|
const proj = pluginRoot();
|
|
2481
2727
|
if (proj !== process.cwd())
|
|
@@ -2743,6 +2989,11 @@ enable: imprnt plugin add <name> disable: imprnt plugin rm <name> [--purge]`);
|
|
|
2743
2989
|
cpSync3(join15(pkgRoot, "CLAUDE.md"), claudeMd);
|
|
2744
2990
|
added.push("CLAUDE.md");
|
|
2745
2991
|
}
|
|
2992
|
+
try {
|
|
2993
|
+
chmodSync(target, 448);
|
|
2994
|
+
} catch (e) {
|
|
2995
|
+
console.error(`could not set owner-only (chmod 700) on ${target}: ${e instanceof Error ? e.message : String(e)} — tighten it by hand to honor the private-vault promise`);
|
|
2996
|
+
}
|
|
2746
2997
|
const reg = registerVault(target, { force: rest.includes("--register") });
|
|
2747
2998
|
if (reg.status === "registered")
|
|
2748
2999
|
console.log(`registered as imp's default vault project (${configPath()})`);
|
|
@@ -2784,17 +3035,21 @@ enable: imprnt plugin add <name> disable: imprnt plugin rm <name> [--purge]`);
|
|
|
2784
3035
|
if (wantsLaunch && (!bare || process.stdin.isTTY && process.stdout.isTTY)) {
|
|
2785
3036
|
const home = vaultProjectRoot();
|
|
2786
3037
|
if (!home) {
|
|
2787
|
-
console.error("imp: no vault registered yet — run `imprnt init` in your vault project to give your assistant a memory. Launching plain
|
|
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.");
|
|
2788
3039
|
}
|
|
2789
|
-
const
|
|
2790
|
-
|
|
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));
|
|
2791
3044
|
}
|
|
2792
3045
|
console.log(`imprnt — deterministic-first markdown knowledge vault
|
|
2793
3046
|
|
|
2794
3047
|
the front door (the \`imp\` bin):
|
|
2795
|
-
imp open your assistant HERE:
|
|
3048
|
+
imp open your assistant HERE: your agent + your cast + the vault pointer
|
|
2796
3049
|
imp lair open it in your vault project — full contract, resumable history
|
|
2797
|
-
imp
|
|
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
|
|
2798
3053
|
|
|
2799
3054
|
engine (same subcommands under \`imp\` or \`imprnt\`):
|
|
2800
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)
|
|
@@ -2802,6 +3057,9 @@ engine (same subcommands under \`imp\` or \`imprnt\`):
|
|
|
2802
3057
|
imprnt ingest <file|text> [--vault D] snapshot a source -> raw/; a transcript file also gets an event skeleton (no LLM)
|
|
2803
3058
|
imprnt recall "<query>" [--vault D] synonym-aware BM25 ranking over the vault
|
|
2804
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, ...)
|
|
2805
3063
|
imprnt check [--all] [--vault D] integrity (orphan links, disconnected notes, uncovered snapshots) + regenerate index.md; --all also runs each plugins/*/check.js
|
|
2806
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/
|
|
2807
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 {
|
|
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,153 @@ 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 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(
|
|
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" ?
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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";
|
|
@@ -2036,6 +2183,28 @@ function targetFolders(target) {
|
|
|
2036
2183
|
function linksEntity(target) {
|
|
2037
2184
|
return targetFolders(target).some((f) => ENTITY_FOLDERS.has(f));
|
|
2038
2185
|
}
|
|
2186
|
+
function hostMemoryStores() {
|
|
2187
|
+
const override = process.env.IMPRNT_HOST_MEMORY_DIR;
|
|
2188
|
+
if (override)
|
|
2189
|
+
return [override];
|
|
2190
|
+
const configDir = process.env.CLAUDE_CONFIG_DIR || join14(homedir3(), ".claude");
|
|
2191
|
+
const projectsDir = join14(configDir, "projects");
|
|
2192
|
+
if (!existsSync12(projectsDir))
|
|
2193
|
+
return [];
|
|
2194
|
+
let entries;
|
|
2195
|
+
try {
|
|
2196
|
+
entries = readdirSync8(projectsDir);
|
|
2197
|
+
} catch {
|
|
2198
|
+
return [];
|
|
2199
|
+
}
|
|
2200
|
+
return entries.map((p) => join14(projectsDir, p, "memory")).filter((d) => {
|
|
2201
|
+
try {
|
|
2202
|
+
return statSync8(d).isDirectory();
|
|
2203
|
+
} catch {
|
|
2204
|
+
return false;
|
|
2205
|
+
}
|
|
2206
|
+
}).sort();
|
|
2207
|
+
}
|
|
2039
2208
|
function syncNeedsReview(lines2) {
|
|
2040
2209
|
const p = join14(vault4, "_needs-review.md");
|
|
2041
2210
|
const exists = existsSync12(p);
|
|
@@ -2073,10 +2242,10 @@ ${REVIEW_END}
|
|
|
2073
2242
|
`) + section2;
|
|
2074
2243
|
}
|
|
2075
2244
|
if (next !== prev || !exists)
|
|
2076
|
-
|
|
2245
|
+
writeFileSync10(p, next);
|
|
2077
2246
|
return lines2.length ? "written" : "cleared";
|
|
2078
2247
|
}
|
|
2079
|
-
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,
|
|
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;
|
|
2080
2249
|
var init_check = __esm(() => {
|
|
2081
2250
|
init_roots();
|
|
2082
2251
|
init_moc();
|
|
@@ -2214,10 +2383,14 @@ var init_check = __esm(() => {
|
|
|
2214
2383
|
uncovered = [...new Set(rawEntries.map(norm))].filter((r) => !refNorm.has(r)).sort();
|
|
2215
2384
|
for (const r of uncovered)
|
|
2216
2385
|
review.push(`- [ ] unclassified snapshot \`${r}\` — no vault note points back`);
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2386
|
+
memoryStores = hostMemoryStores();
|
|
2387
|
+
strayStores = memoryStores.map((dir2) => ({
|
|
2388
|
+
dir: dir2,
|
|
2389
|
+
files: existsSync12(dir2) ? readdirSync8(dir2).filter((f) => f.endsWith(".md") && f !== "MEMORY.md").sort() : []
|
|
2390
|
+
})).filter((s) => s.files.length > 0);
|
|
2391
|
+
strayCount = strayStores.reduce((n, s) => n + s.files.length, 0);
|
|
2392
|
+
for (const s of strayStores)
|
|
2393
|
+
review.push(`- [ ] host auto-memory not empty — ${s.files.length} note(s) in ${s.dir}; migrate knowledge → vault notes, behavior → a CLAUDE.local.md fragment, then empty it`);
|
|
2221
2394
|
console.log(`imprnt check — ${notes.length} notes in ${vault4}
|
|
2222
2395
|
`);
|
|
2223
2396
|
if (orphans.length) {
|
|
@@ -2269,14 +2442,17 @@ var init_check = __esm(() => {
|
|
|
2269
2442
|
} else
|
|
2270
2443
|
console.log("✓ every raw snapshot has a derived note");
|
|
2271
2444
|
}
|
|
2272
|
-
if (
|
|
2273
|
-
console.log(`⚠ host auto-memory not empty (${
|
|
2274
|
-
|
|
2445
|
+
if (strayCount) {
|
|
2446
|
+
console.log(`⚠ host auto-memory not empty (${strayCount} in ${strayStores.length} project store(s)) — a second store recall can't see:`);
|
|
2447
|
+
for (const s of strayStores) {
|
|
2448
|
+
console.log(` ${s.dir}`);
|
|
2449
|
+
console.log(cap(s.files.map((f) => " " + f)).join(`
|
|
2275
2450
|
`));
|
|
2276
|
-
|
|
2451
|
+
}
|
|
2452
|
+
console.log(` migrate knowledge → vault notes, behavior → a CLAUDE.local.md fragment, then empty each.
|
|
2277
2453
|
`);
|
|
2278
2454
|
} else
|
|
2279
|
-
console.log(`✓ host auto-memory empty (vault is the only store) —
|
|
2455
|
+
console.log(`✓ host auto-memory empty (vault is the only store) — swept ${memoryStores.length} project store(s)`);
|
|
2280
2456
|
({ count, folders } = generateIndex(vault4));
|
|
2281
2457
|
console.log(`↻ regenerated index.md — ${count} notes across ${folders} folders`);
|
|
2282
2458
|
synced = syncNeedsReview(review);
|
|
@@ -2284,7 +2460,7 @@ var init_check = __esm(() => {
|
|
|
2284
2460
|
console.log(`↻ ${review.length} finding(s) → _needs-review.md (run \`imprnt hot\` to see them)`);
|
|
2285
2461
|
else if (synced === "cleared")
|
|
2286
2462
|
console.log("↻ cleared resolved findings from _needs-review.md");
|
|
2287
|
-
issues = orphans.length + disconnected.length + domainIssues.length + untagged.length + uncovered.length + dupPairs.length +
|
|
2463
|
+
issues = orphans.length + disconnected.length + domainIssues.length + untagged.length + uncovered.length + dupPairs.length + strayCount;
|
|
2288
2464
|
console.log(issues ? `
|
|
2289
2465
|
${issues} thing(s) to look at above.` : `
|
|
2290
2466
|
clean.`);
|
|
@@ -2334,7 +2510,7 @@ all plugin checks passed.`);
|
|
|
2334
2510
|
|
|
2335
2511
|
// scripts/cli.ts
|
|
2336
2512
|
var exports_cli = {};
|
|
2337
|
-
import { cpSync as cpSync3, mkdirSync as mkdirSync5, existsSync as existsSync13, readFileSync as readFileSync14, lstatSync as lstatSync4, statSync as statSync9, realpathSync as realpathSync2 } from "node:fs";
|
|
2513
|
+
import { cpSync as cpSync3, mkdirSync as mkdirSync5, chmodSync, existsSync as existsSync13, readFileSync as readFileSync14, lstatSync as lstatSync4, statSync as statSync9, realpathSync as realpathSync2 } from "node:fs";
|
|
2338
2514
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
2339
2515
|
import { join as join15, dirname as dirname4, resolve as resolve6, basename as basename5 } from "node:path";
|
|
2340
2516
|
import { homedir as homedir4 } from "node:os";
|
|
@@ -2463,8 +2639,9 @@ var init_cli = __esm(async () => {
|
|
|
2463
2639
|
}
|
|
2464
2640
|
case "lair": {
|
|
2465
2641
|
const home = requireVaultHome();
|
|
2466
|
-
const
|
|
2467
|
-
|
|
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));
|
|
2468
2645
|
}
|
|
2469
2646
|
case "context": {
|
|
2470
2647
|
const home = requireVaultHome();
|
|
@@ -2476,6 +2653,75 @@ var init_cli = __esm(async () => {
|
|
|
2476
2653
|
process.stdout.write(readFileSync14(contract, "utf8"));
|
|
2477
2654
|
break;
|
|
2478
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
|
+
}
|
|
2479
2725
|
case "plugin": {
|
|
2480
2726
|
const proj = pluginRoot();
|
|
2481
2727
|
if (proj !== process.cwd())
|
|
@@ -2743,6 +2989,11 @@ enable: imprnt plugin add <name> disable: imprnt plugin rm <name> [--purge]`);
|
|
|
2743
2989
|
cpSync3(join15(pkgRoot, "CLAUDE.md"), claudeMd);
|
|
2744
2990
|
added.push("CLAUDE.md");
|
|
2745
2991
|
}
|
|
2992
|
+
try {
|
|
2993
|
+
chmodSync(target, 448);
|
|
2994
|
+
} catch (e) {
|
|
2995
|
+
console.error(`could not set owner-only (chmod 700) on ${target}: ${e instanceof Error ? e.message : String(e)} — tighten it by hand to honor the private-vault promise`);
|
|
2996
|
+
}
|
|
2746
2997
|
const reg = registerVault(target, { force: rest.includes("--register") });
|
|
2747
2998
|
if (reg.status === "registered")
|
|
2748
2999
|
console.log(`registered as imp's default vault project (${configPath()})`);
|
|
@@ -2784,17 +3035,21 @@ enable: imprnt plugin add <name> disable: imprnt plugin rm <name> [--purge]`);
|
|
|
2784
3035
|
if (wantsLaunch && (!bare || process.stdin.isTTY && process.stdout.isTTY)) {
|
|
2785
3036
|
const home = vaultProjectRoot();
|
|
2786
3037
|
if (!home) {
|
|
2787
|
-
console.error("imp: no vault registered yet — run `imprnt init` in your vault project to give your assistant a memory. Launching plain
|
|
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.");
|
|
2788
3039
|
}
|
|
2789
|
-
const
|
|
2790
|
-
|
|
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));
|
|
2791
3044
|
}
|
|
2792
3045
|
console.log(`imprnt — deterministic-first markdown knowledge vault
|
|
2793
3046
|
|
|
2794
3047
|
the front door (the \`imp\` bin):
|
|
2795
|
-
imp open your assistant HERE:
|
|
3048
|
+
imp open your assistant HERE: your agent + your cast + the vault pointer
|
|
2796
3049
|
imp lair open it in your vault project — full contract, resumable history
|
|
2797
|
-
imp
|
|
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
|
|
2798
3053
|
|
|
2799
3054
|
engine (same subcommands under \`imp\` or \`imprnt\`):
|
|
2800
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)
|
|
@@ -2802,6 +3057,9 @@ engine (same subcommands under \`imp\` or \`imprnt\`):
|
|
|
2802
3057
|
imprnt ingest <file|text> [--vault D] snapshot a source -> raw/; a transcript file also gets an event skeleton (no LLM)
|
|
2803
3058
|
imprnt recall "<query>" [--vault D] synonym-aware BM25 ranking over the vault
|
|
2804
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, ...)
|
|
2805
3063
|
imprnt check [--all] [--vault D] integrity (orphan links, disconnected notes, uncovered snapshots) + regenerate index.md; --all also runs each plugins/*/check.js
|
|
2806
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/
|
|
2807
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.
|
|
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": {
|
package/templates/pointer.md
CHANGED
|
@@ -8,5 +8,7 @@ decisions, finances, health, and history.
|
|
|
8
8
|
`imprnt recall "<keywords>"`, then read the top hits.
|
|
9
9
|
- Before filing or editing ANYTHING in the vault, run `imprnt context` and follow what it prints.
|
|
10
10
|
Never write a vault note without it.
|
|
11
|
+
- The vault is the only knowledge store. Durable facts go in a vault note, never the host's
|
|
12
|
+
auto-memory (Claude Code's `MEMORY.md` / `memory/`), which `recall` cannot search.
|
|
11
13
|
- The vault is private data. Never copy its contents into committed files, code, or external
|
|
12
14
|
services.
|