imprnt 0.1.3-edge.53 → 0.1.3-edge.54
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 +46 -12
- package/dist/imp.js +46 -12
- 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
|
@@ -2036,6 +2036,28 @@ function targetFolders(target) {
|
|
|
2036
2036
|
function linksEntity(target) {
|
|
2037
2037
|
return targetFolders(target).some((f) => ENTITY_FOLDERS.has(f));
|
|
2038
2038
|
}
|
|
2039
|
+
function hostMemoryStores() {
|
|
2040
|
+
const override = process.env.IMPRNT_HOST_MEMORY_DIR;
|
|
2041
|
+
if (override)
|
|
2042
|
+
return [override];
|
|
2043
|
+
const configDir = process.env.CLAUDE_CONFIG_DIR || join14(homedir3(), ".claude");
|
|
2044
|
+
const projectsDir = join14(configDir, "projects");
|
|
2045
|
+
if (!existsSync12(projectsDir))
|
|
2046
|
+
return [];
|
|
2047
|
+
let entries;
|
|
2048
|
+
try {
|
|
2049
|
+
entries = readdirSync8(projectsDir);
|
|
2050
|
+
} catch {
|
|
2051
|
+
return [];
|
|
2052
|
+
}
|
|
2053
|
+
return entries.map((p) => join14(projectsDir, p, "memory")).filter((d) => {
|
|
2054
|
+
try {
|
|
2055
|
+
return statSync8(d).isDirectory();
|
|
2056
|
+
} catch {
|
|
2057
|
+
return false;
|
|
2058
|
+
}
|
|
2059
|
+
}).sort();
|
|
2060
|
+
}
|
|
2039
2061
|
function syncNeedsReview(lines2) {
|
|
2040
2062
|
const p = join14(vault4, "_needs-review.md");
|
|
2041
2063
|
const exists = existsSync12(p);
|
|
@@ -2076,7 +2098,7 @@ ${REVIEW_END}
|
|
|
2076
2098
|
writeFileSync9(p, next);
|
|
2077
2099
|
return lines2.length ? "written" : "cleared";
|
|
2078
2100
|
}
|
|
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,
|
|
2101
|
+
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
2102
|
var init_check = __esm(() => {
|
|
2081
2103
|
init_roots();
|
|
2082
2104
|
init_moc();
|
|
@@ -2214,10 +2236,14 @@ var init_check = __esm(() => {
|
|
|
2214
2236
|
uncovered = [...new Set(rawEntries.map(norm))].filter((r) => !refNorm.has(r)).sort();
|
|
2215
2237
|
for (const r of uncovered)
|
|
2216
2238
|
review.push(`- [ ] unclassified snapshot \`${r}\` — no vault note points back`);
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2239
|
+
memoryStores = hostMemoryStores();
|
|
2240
|
+
strayStores = memoryStores.map((dir2) => ({
|
|
2241
|
+
dir: dir2,
|
|
2242
|
+
files: existsSync12(dir2) ? readdirSync8(dir2).filter((f) => f.endsWith(".md") && f !== "MEMORY.md").sort() : []
|
|
2243
|
+
})).filter((s) => s.files.length > 0);
|
|
2244
|
+
strayCount = strayStores.reduce((n, s) => n + s.files.length, 0);
|
|
2245
|
+
for (const s of strayStores)
|
|
2246
|
+
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
2247
|
console.log(`imprnt check — ${notes.length} notes in ${vault4}
|
|
2222
2248
|
`);
|
|
2223
2249
|
if (orphans.length) {
|
|
@@ -2269,14 +2295,17 @@ var init_check = __esm(() => {
|
|
|
2269
2295
|
} else
|
|
2270
2296
|
console.log("✓ every raw snapshot has a derived note");
|
|
2271
2297
|
}
|
|
2272
|
-
if (
|
|
2273
|
-
console.log(`⚠ host auto-memory not empty (${
|
|
2274
|
-
|
|
2298
|
+
if (strayCount) {
|
|
2299
|
+
console.log(`⚠ host auto-memory not empty (${strayCount} in ${strayStores.length} project store(s)) — a second store recall can't see:`);
|
|
2300
|
+
for (const s of strayStores) {
|
|
2301
|
+
console.log(` ${s.dir}`);
|
|
2302
|
+
console.log(cap(s.files.map((f) => " " + f)).join(`
|
|
2275
2303
|
`));
|
|
2276
|
-
|
|
2304
|
+
}
|
|
2305
|
+
console.log(` migrate knowledge → vault notes, behavior → a CLAUDE.local.md fragment, then empty each.
|
|
2277
2306
|
`);
|
|
2278
2307
|
} else
|
|
2279
|
-
console.log(`✓ host auto-memory empty (vault is the only store) —
|
|
2308
|
+
console.log(`✓ host auto-memory empty (vault is the only store) — swept ${memoryStores.length} project store(s)`);
|
|
2280
2309
|
({ count, folders } = generateIndex(vault4));
|
|
2281
2310
|
console.log(`↻ regenerated index.md — ${count} notes across ${folders} folders`);
|
|
2282
2311
|
synced = syncNeedsReview(review);
|
|
@@ -2284,7 +2313,7 @@ var init_check = __esm(() => {
|
|
|
2284
2313
|
console.log(`↻ ${review.length} finding(s) → _needs-review.md (run \`imprnt hot\` to see them)`);
|
|
2285
2314
|
else if (synced === "cleared")
|
|
2286
2315
|
console.log("↻ cleared resolved findings from _needs-review.md");
|
|
2287
|
-
issues = orphans.length + disconnected.length + domainIssues.length + untagged.length + uncovered.length + dupPairs.length +
|
|
2316
|
+
issues = orphans.length + disconnected.length + domainIssues.length + untagged.length + uncovered.length + dupPairs.length + strayCount;
|
|
2288
2317
|
console.log(issues ? `
|
|
2289
2318
|
${issues} thing(s) to look at above.` : `
|
|
2290
2319
|
clean.`);
|
|
@@ -2334,7 +2363,7 @@ all plugin checks passed.`);
|
|
|
2334
2363
|
|
|
2335
2364
|
// scripts/cli.ts
|
|
2336
2365
|
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";
|
|
2366
|
+
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
2367
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
2339
2368
|
import { join as join15, dirname as dirname4, resolve as resolve6, basename as basename5 } from "node:path";
|
|
2340
2369
|
import { homedir as homedir4 } from "node:os";
|
|
@@ -2743,6 +2772,11 @@ enable: imprnt plugin add <name> disable: imprnt plugin rm <name> [--purge]`);
|
|
|
2743
2772
|
cpSync3(join15(pkgRoot, "CLAUDE.md"), claudeMd);
|
|
2744
2773
|
added.push("CLAUDE.md");
|
|
2745
2774
|
}
|
|
2775
|
+
try {
|
|
2776
|
+
chmodSync(target, 448);
|
|
2777
|
+
} catch (e) {
|
|
2778
|
+
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`);
|
|
2779
|
+
}
|
|
2746
2780
|
const reg = registerVault(target, { force: rest.includes("--register") });
|
|
2747
2781
|
if (reg.status === "registered")
|
|
2748
2782
|
console.log(`registered as imp's default vault project (${configPath()})`);
|
package/dist/imp.js
CHANGED
|
@@ -2036,6 +2036,28 @@ function targetFolders(target) {
|
|
|
2036
2036
|
function linksEntity(target) {
|
|
2037
2037
|
return targetFolders(target).some((f) => ENTITY_FOLDERS.has(f));
|
|
2038
2038
|
}
|
|
2039
|
+
function hostMemoryStores() {
|
|
2040
|
+
const override = process.env.IMPRNT_HOST_MEMORY_DIR;
|
|
2041
|
+
if (override)
|
|
2042
|
+
return [override];
|
|
2043
|
+
const configDir = process.env.CLAUDE_CONFIG_DIR || join14(homedir3(), ".claude");
|
|
2044
|
+
const projectsDir = join14(configDir, "projects");
|
|
2045
|
+
if (!existsSync12(projectsDir))
|
|
2046
|
+
return [];
|
|
2047
|
+
let entries;
|
|
2048
|
+
try {
|
|
2049
|
+
entries = readdirSync8(projectsDir);
|
|
2050
|
+
} catch {
|
|
2051
|
+
return [];
|
|
2052
|
+
}
|
|
2053
|
+
return entries.map((p) => join14(projectsDir, p, "memory")).filter((d) => {
|
|
2054
|
+
try {
|
|
2055
|
+
return statSync8(d).isDirectory();
|
|
2056
|
+
} catch {
|
|
2057
|
+
return false;
|
|
2058
|
+
}
|
|
2059
|
+
}).sort();
|
|
2060
|
+
}
|
|
2039
2061
|
function syncNeedsReview(lines2) {
|
|
2040
2062
|
const p = join14(vault4, "_needs-review.md");
|
|
2041
2063
|
const exists = existsSync12(p);
|
|
@@ -2076,7 +2098,7 @@ ${REVIEW_END}
|
|
|
2076
2098
|
writeFileSync9(p, next);
|
|
2077
2099
|
return lines2.length ? "written" : "cleared";
|
|
2078
2100
|
}
|
|
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,
|
|
2101
|
+
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
2102
|
var init_check = __esm(() => {
|
|
2081
2103
|
init_roots();
|
|
2082
2104
|
init_moc();
|
|
@@ -2214,10 +2236,14 @@ var init_check = __esm(() => {
|
|
|
2214
2236
|
uncovered = [...new Set(rawEntries.map(norm))].filter((r) => !refNorm.has(r)).sort();
|
|
2215
2237
|
for (const r of uncovered)
|
|
2216
2238
|
review.push(`- [ ] unclassified snapshot \`${r}\` — no vault note points back`);
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2239
|
+
memoryStores = hostMemoryStores();
|
|
2240
|
+
strayStores = memoryStores.map((dir2) => ({
|
|
2241
|
+
dir: dir2,
|
|
2242
|
+
files: existsSync12(dir2) ? readdirSync8(dir2).filter((f) => f.endsWith(".md") && f !== "MEMORY.md").sort() : []
|
|
2243
|
+
})).filter((s) => s.files.length > 0);
|
|
2244
|
+
strayCount = strayStores.reduce((n, s) => n + s.files.length, 0);
|
|
2245
|
+
for (const s of strayStores)
|
|
2246
|
+
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
2247
|
console.log(`imprnt check — ${notes.length} notes in ${vault4}
|
|
2222
2248
|
`);
|
|
2223
2249
|
if (orphans.length) {
|
|
@@ -2269,14 +2295,17 @@ var init_check = __esm(() => {
|
|
|
2269
2295
|
} else
|
|
2270
2296
|
console.log("✓ every raw snapshot has a derived note");
|
|
2271
2297
|
}
|
|
2272
|
-
if (
|
|
2273
|
-
console.log(`⚠ host auto-memory not empty (${
|
|
2274
|
-
|
|
2298
|
+
if (strayCount) {
|
|
2299
|
+
console.log(`⚠ host auto-memory not empty (${strayCount} in ${strayStores.length} project store(s)) — a second store recall can't see:`);
|
|
2300
|
+
for (const s of strayStores) {
|
|
2301
|
+
console.log(` ${s.dir}`);
|
|
2302
|
+
console.log(cap(s.files.map((f) => " " + f)).join(`
|
|
2275
2303
|
`));
|
|
2276
|
-
|
|
2304
|
+
}
|
|
2305
|
+
console.log(` migrate knowledge → vault notes, behavior → a CLAUDE.local.md fragment, then empty each.
|
|
2277
2306
|
`);
|
|
2278
2307
|
} else
|
|
2279
|
-
console.log(`✓ host auto-memory empty (vault is the only store) —
|
|
2308
|
+
console.log(`✓ host auto-memory empty (vault is the only store) — swept ${memoryStores.length} project store(s)`);
|
|
2280
2309
|
({ count, folders } = generateIndex(vault4));
|
|
2281
2310
|
console.log(`↻ regenerated index.md — ${count} notes across ${folders} folders`);
|
|
2282
2311
|
synced = syncNeedsReview(review);
|
|
@@ -2284,7 +2313,7 @@ var init_check = __esm(() => {
|
|
|
2284
2313
|
console.log(`↻ ${review.length} finding(s) → _needs-review.md (run \`imprnt hot\` to see them)`);
|
|
2285
2314
|
else if (synced === "cleared")
|
|
2286
2315
|
console.log("↻ cleared resolved findings from _needs-review.md");
|
|
2287
|
-
issues = orphans.length + disconnected.length + domainIssues.length + untagged.length + uncovered.length + dupPairs.length +
|
|
2316
|
+
issues = orphans.length + disconnected.length + domainIssues.length + untagged.length + uncovered.length + dupPairs.length + strayCount;
|
|
2288
2317
|
console.log(issues ? `
|
|
2289
2318
|
${issues} thing(s) to look at above.` : `
|
|
2290
2319
|
clean.`);
|
|
@@ -2334,7 +2363,7 @@ all plugin checks passed.`);
|
|
|
2334
2363
|
|
|
2335
2364
|
// scripts/cli.ts
|
|
2336
2365
|
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";
|
|
2366
|
+
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
2367
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
2339
2368
|
import { join as join15, dirname as dirname4, resolve as resolve6, basename as basename5 } from "node:path";
|
|
2340
2369
|
import { homedir as homedir4 } from "node:os";
|
|
@@ -2743,6 +2772,11 @@ enable: imprnt plugin add <name> disable: imprnt plugin rm <name> [--purge]`);
|
|
|
2743
2772
|
cpSync3(join15(pkgRoot, "CLAUDE.md"), claudeMd);
|
|
2744
2773
|
added.push("CLAUDE.md");
|
|
2745
2774
|
}
|
|
2775
|
+
try {
|
|
2776
|
+
chmodSync(target, 448);
|
|
2777
|
+
} catch (e) {
|
|
2778
|
+
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`);
|
|
2779
|
+
}
|
|
2746
2780
|
const reg = registerVault(target, { force: rest.includes("--register") });
|
|
2747
2781
|
if (reg.status === "registered")
|
|
2748
2782
|
console.log(`registered as imp's default vault project (${configPath()})`);
|
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.54",
|
|
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.
|