@ijfw/install 1.4.4 → 1.5.0
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/ijfw.js +57 -2
- package/docs/GUIDE.md +1 -1
- package/package.json +1 -1
package/dist/ijfw.js
CHANGED
|
@@ -1583,7 +1583,7 @@ var init_project_type_detector = __esm({
|
|
|
1583
1583
|
});
|
|
1584
1584
|
|
|
1585
1585
|
// ../mcp-server/src/gate-result.js
|
|
1586
|
-
import { mkdir, writeFile } from "node:fs/promises";
|
|
1586
|
+
import { mkdir, writeFile, readdir, stat, unlink, appendFile } from "node:fs/promises";
|
|
1587
1587
|
import { basename, dirname as dirname2, join as join6 } from "node:path";
|
|
1588
1588
|
async function emitGateResult(gateOpts, context = {}) {
|
|
1589
1589
|
if (gateOpts === null || typeof gateOpts !== "object") {
|
|
@@ -1643,6 +1643,7 @@ async function makeReceipt(gateResult, opts = {}) {
|
|
|
1643
1643
|
await mkdir(dirname2(receiptPath), { recursive: true });
|
|
1644
1644
|
const body = JSON.stringify(gateResult, null, 2) + "\n";
|
|
1645
1645
|
await writeFile(receiptPath, body, "utf8");
|
|
1646
|
+
await evictOldReceipts(dirname2(receiptPath));
|
|
1646
1647
|
} catch (err) {
|
|
1647
1648
|
const msg = err && err.message ? err.message : String(err);
|
|
1648
1649
|
try {
|
|
@@ -1652,6 +1653,58 @@ async function makeReceipt(gateResult, opts = {}) {
|
|
|
1652
1653
|
}
|
|
1653
1654
|
}
|
|
1654
1655
|
}
|
|
1656
|
+
async function evictOldReceipts(dir, opts = {}) {
|
|
1657
|
+
const keep = Number.isFinite(opts.keep) && opts.keep > 0 ? opts.keep | 0 : RECEIPTS_KEEP;
|
|
1658
|
+
try {
|
|
1659
|
+
let entries;
|
|
1660
|
+
try {
|
|
1661
|
+
entries = await readdir(dir);
|
|
1662
|
+
} catch {
|
|
1663
|
+
return { evicted: 0 };
|
|
1664
|
+
}
|
|
1665
|
+
const jsonFiles = entries.filter((f) => f.endsWith(".json"));
|
|
1666
|
+
if (jsonFiles.length <= keep) return { evicted: 0 };
|
|
1667
|
+
const stamped = [];
|
|
1668
|
+
for (const f of jsonFiles) {
|
|
1669
|
+
const full = join6(dir, f);
|
|
1670
|
+
try {
|
|
1671
|
+
const s = await stat(full);
|
|
1672
|
+
stamped.push({ full, name: f, mtimeMs: s.mtimeMs });
|
|
1673
|
+
} catch {
|
|
1674
|
+
stamped.push({ full, name: f, mtimeMs: 0 });
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
stamped.sort((a, b2) => b2.mtimeMs - a.mtimeMs);
|
|
1678
|
+
const toEvict = stamped.slice(keep);
|
|
1679
|
+
if (toEvict.length === 0) return { evicted: 0 };
|
|
1680
|
+
const archivePath = join6(dir, RECEIPTS_ARCHIVE);
|
|
1681
|
+
let evicted = 0;
|
|
1682
|
+
for (const victim of toEvict) {
|
|
1683
|
+
try {
|
|
1684
|
+
let body;
|
|
1685
|
+
try {
|
|
1686
|
+
const { readFile } = await import("node:fs/promises");
|
|
1687
|
+
body = await readFile(victim.full, "utf8");
|
|
1688
|
+
} catch {
|
|
1689
|
+
continue;
|
|
1690
|
+
}
|
|
1691
|
+
let archiveLine;
|
|
1692
|
+
try {
|
|
1693
|
+
archiveLine = JSON.stringify(JSON.parse(body)) + "\n";
|
|
1694
|
+
} catch {
|
|
1695
|
+
archiveLine = JSON.stringify({ raw: body, evicted_from: victim.name }) + "\n";
|
|
1696
|
+
}
|
|
1697
|
+
await appendFile(archivePath, archiveLine, "utf8");
|
|
1698
|
+
await unlink(victim.full);
|
|
1699
|
+
evicted++;
|
|
1700
|
+
} catch {
|
|
1701
|
+
}
|
|
1702
|
+
}
|
|
1703
|
+
return { evicted };
|
|
1704
|
+
} catch {
|
|
1705
|
+
return { evicted: 0 };
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
1655
1708
|
async function resolveProjectType(projectRoot) {
|
|
1656
1709
|
try {
|
|
1657
1710
|
const root = typeof projectRoot === "string" && projectRoot.length > 0 ? projectRoot : process.cwd();
|
|
@@ -1667,12 +1720,14 @@ async function resolveProjectType(projectRoot) {
|
|
|
1667
1720
|
return "unknown";
|
|
1668
1721
|
}
|
|
1669
1722
|
}
|
|
1670
|
-
var RECEIPT_GATE_ID_PATTERN;
|
|
1723
|
+
var RECEIPT_GATE_ID_PATTERN, RECEIPTS_KEEP, RECEIPTS_ARCHIVE;
|
|
1671
1724
|
var init_gate_result = __esm({
|
|
1672
1725
|
"../mcp-server/src/gate-result.js"() {
|
|
1673
1726
|
init_gate_result_schema();
|
|
1674
1727
|
init_project_type_detector();
|
|
1675
1728
|
RECEIPT_GATE_ID_PATTERN = /^[a-z][a-z0-9-]+$/;
|
|
1729
|
+
RECEIPTS_KEEP = 1e3;
|
|
1730
|
+
RECEIPTS_ARCHIVE = ".archive.jsonl";
|
|
1676
1731
|
}
|
|
1677
1732
|
});
|
|
1678
1733
|
|
package/docs/GUIDE.md
CHANGED
|
@@ -172,7 +172,7 @@ Run `/team setup` in Claude Code, or `ijfw team` from the shell, to see your cur
|
|
|
172
172
|
|------|-------|--------------|
|
|
173
173
|
| Hot | Plain markdown in `.ijfw/memory/` | Always on. Instant reads. Git friendly. |
|
|
174
174
|
| Warm | BM25 ranked search | Always on. Scales to around 10,000 entries per project. |
|
|
175
|
-
| Cold | Optional semantic vectors | Off by default.
|
|
175
|
+
| Cold | Optional semantic vectors (hybrid BM25 + cosine rerank) | Off by default. Enable with `IJFW_VECTORS=on` and `npm i @xenova/transformers` (one-time ~23MB MiniLM model cached locally). Top-K BM25 candidates are reranked via cosine similarity with weights 0.6 BM25 / 0.4 vector. Pure no-op fallback to BM25 if disabled, the package isn't installed, or the model fails to load. |
|
|
176
176
|
|
|
177
177
|
Every session also ends with an optional "dream cycle". Run `/consolidate` or "run a dream cycle" to have IJFW sweep the day's memory: promote observed patterns into your knowledge base, prune stale entries, reconcile contradictions, optionally lift winners into global memory so every future project benefits. Memory that grows sharper over time instead of heavier.
|
|
178
178
|
|