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