unforgit 0.8.5 → 0.9.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/{chunk-VESJIIT2.js → chunk-JXSAAZ3I.js} +198 -1
- package/dist/chunk-JXSAAZ3I.js.map +1 -0
- package/dist/index.js +128 -3
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +1 -1
- package/package.json +3 -3
- package/dist/chunk-VESJIIT2.js.map +0 -1
|
@@ -10,6 +10,7 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
10
10
|
import OpenAI from "openai";
|
|
11
11
|
import { createHash } from "crypto";
|
|
12
12
|
import OpenAI2 from "openai";
|
|
13
|
+
import { createHash as createHash2 } from "crypto";
|
|
13
14
|
var DEFAULT_TTL_SECONDS_BY_TYPE = {
|
|
14
15
|
episodic: 30 * 24 * 60 * 60,
|
|
15
16
|
semantic: void 0,
|
|
@@ -1461,6 +1462,198 @@ function formatTemplateList() {
|
|
|
1461
1462
|
}
|
|
1462
1463
|
return lines.join("\n");
|
|
1463
1464
|
}
|
|
1465
|
+
var HEADING_TAGS = [
|
|
1466
|
+
[/conventions?/i, "convention"],
|
|
1467
|
+
[/gotchas?|warnings?|pitfalls?/i, "gotcha"],
|
|
1468
|
+
[/playbooks?|procedures?|workflows?|commands?/i, "playbook"],
|
|
1469
|
+
[/decisions?/i, "decision"],
|
|
1470
|
+
[/architecture/i, "architecture"]
|
|
1471
|
+
];
|
|
1472
|
+
var SECRET_PATTERNS = [
|
|
1473
|
+
/\bgh[pousr]_[A-Za-z0-9_]{20,}\b/,
|
|
1474
|
+
/\b(?:sk|pk)_[A-Za-z0-9]{20,}\b/,
|
|
1475
|
+
/\b(?:token|secret|password)\s+(?:is|:)\s+\S+/i,
|
|
1476
|
+
/\b[A-Za-z0-9_]*SECRET[A-Za-z0-9_]*\s*=\s*\S+/i,
|
|
1477
|
+
/-----BEGIN (?:RSA |OPENSSH |EC |DSA )?PRIVATE KEY-----/
|
|
1478
|
+
];
|
|
1479
|
+
var PROMPT_INJECTION_PATTERNS = [
|
|
1480
|
+
/ignore (?:all )?(?:previous|prior) instructions/i,
|
|
1481
|
+
/reveal (?:all )?(?:secrets|tokens|keys)/i,
|
|
1482
|
+
/system prompt/i
|
|
1483
|
+
];
|
|
1484
|
+
function checksumFor(text) {
|
|
1485
|
+
return createHash2("sha256").update(text.trim(), "utf8").digest("hex");
|
|
1486
|
+
}
|
|
1487
|
+
function isTagChar(char) {
|
|
1488
|
+
const code = char.charCodeAt(0);
|
|
1489
|
+
return code >= 97 && code <= 122 || code >= 48 && code <= 57 || char === "_" || char === "-";
|
|
1490
|
+
}
|
|
1491
|
+
function normalizeTag(tag) {
|
|
1492
|
+
const slug = tag.trim().toLowerCase().split("").map((char) => isTagChar(char) ? char : "-").join("");
|
|
1493
|
+
let start = 0;
|
|
1494
|
+
let end = slug.length;
|
|
1495
|
+
while (start < end && slug[start] === "-") start += 1;
|
|
1496
|
+
while (end > start && slug[end - 1] === "-") end -= 1;
|
|
1497
|
+
return slug.slice(start, end);
|
|
1498
|
+
}
|
|
1499
|
+
function uniqueTags(tags) {
|
|
1500
|
+
return Array.from(new Set(tags.map(normalizeTag).filter(Boolean)));
|
|
1501
|
+
}
|
|
1502
|
+
function inferType(headingPath, text, explicit) {
|
|
1503
|
+
if (explicit) return explicit;
|
|
1504
|
+
const haystack = `${headingPath.join(" ")} ${text}`;
|
|
1505
|
+
if (/playbooks?|procedures?|workflows?|commands?|\bto\s+\w+[,,:]?\s+run\b/i.test(haystack)) {
|
|
1506
|
+
return "procedural";
|
|
1507
|
+
}
|
|
1508
|
+
if (/incidents?|bugs?|found|fixed|session|today|yesterday/i.test(haystack)) {
|
|
1509
|
+
return "episodic";
|
|
1510
|
+
}
|
|
1511
|
+
return "semantic";
|
|
1512
|
+
}
|
|
1513
|
+
function inferTags(headingPath, explicitTags = []) {
|
|
1514
|
+
const tags = [...explicitTags];
|
|
1515
|
+
for (const heading of headingPath) {
|
|
1516
|
+
for (const [pattern, tag] of HEADING_TAGS) {
|
|
1517
|
+
if (pattern.test(heading)) tags.push(tag);
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
return uniqueTags(tags);
|
|
1521
|
+
}
|
|
1522
|
+
function parseMetadata(line) {
|
|
1523
|
+
const match = line.match(/<!--\s*unforgit:([^>]*)-->/i);
|
|
1524
|
+
if (!match) return void 0;
|
|
1525
|
+
const metadata = {};
|
|
1526
|
+
const body = match[1];
|
|
1527
|
+
for (const part of body.split(/\s+/).filter(Boolean)) {
|
|
1528
|
+
const [key, rawValue] = part.split("=");
|
|
1529
|
+
const value = rawValue?.trim();
|
|
1530
|
+
if (!value) continue;
|
|
1531
|
+
if (key === "id") metadata.id = value;
|
|
1532
|
+
if (key === "type" && ["episodic", "semantic", "procedural"].includes(value)) {
|
|
1533
|
+
metadata.memoryType = value;
|
|
1534
|
+
}
|
|
1535
|
+
if (key === "tags") metadata.tags = value.split(",").map((tag) => tag.trim());
|
|
1536
|
+
}
|
|
1537
|
+
return metadata;
|
|
1538
|
+
}
|
|
1539
|
+
function stripBullet(line) {
|
|
1540
|
+
const match = line.match(/^\s*(?:[-*+]\s+|\d+[.)]\s+)(.+?)\s*$/);
|
|
1541
|
+
return match?.[1]?.trim();
|
|
1542
|
+
}
|
|
1543
|
+
function headingLevel(line) {
|
|
1544
|
+
const match = line.match(/^(#{1,6})\s+(.+?)\s*#*\s*$/);
|
|
1545
|
+
if (!match) return void 0;
|
|
1546
|
+
return { level: match[1].length, text: match[2].trim() };
|
|
1547
|
+
}
|
|
1548
|
+
function parseMarkdownMemories(markdown, options) {
|
|
1549
|
+
const lines = markdown.replace(/\r\n/g, "\n").split("\n");
|
|
1550
|
+
const headings = [];
|
|
1551
|
+
const memories = [];
|
|
1552
|
+
let pendingMetadata;
|
|
1553
|
+
let inFence = false;
|
|
1554
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
1555
|
+
const line = lines[i];
|
|
1556
|
+
if (/^\s*```/.test(line)) {
|
|
1557
|
+
inFence = !inFence;
|
|
1558
|
+
continue;
|
|
1559
|
+
}
|
|
1560
|
+
if (inFence) continue;
|
|
1561
|
+
const heading = headingLevel(line);
|
|
1562
|
+
if (heading) {
|
|
1563
|
+
while (headings.length > 0 && headings[headings.length - 1].level >= heading.level) {
|
|
1564
|
+
headings.pop();
|
|
1565
|
+
}
|
|
1566
|
+
headings.push(heading);
|
|
1567
|
+
pendingMetadata = void 0;
|
|
1568
|
+
continue;
|
|
1569
|
+
}
|
|
1570
|
+
const metadata = parseMetadata(line);
|
|
1571
|
+
if (metadata) {
|
|
1572
|
+
pendingMetadata = metadata;
|
|
1573
|
+
continue;
|
|
1574
|
+
}
|
|
1575
|
+
const text = stripBullet(line);
|
|
1576
|
+
if (!text) continue;
|
|
1577
|
+
const headingPath = headings.map((h) => h.text);
|
|
1578
|
+
const memoryType = inferType(headingPath, text, pendingMetadata?.memoryType);
|
|
1579
|
+
const tags = inferTags(headingPath, pendingMetadata?.tags);
|
|
1580
|
+
memories.push({
|
|
1581
|
+
id: pendingMetadata?.id,
|
|
1582
|
+
text,
|
|
1583
|
+
memoryType,
|
|
1584
|
+
tags,
|
|
1585
|
+
headingPath,
|
|
1586
|
+
sourceFile: options.sourceFile,
|
|
1587
|
+
lineStart: i + 1,
|
|
1588
|
+
lineEnd: i + 1,
|
|
1589
|
+
checksum: checksumFor(text)
|
|
1590
|
+
});
|
|
1591
|
+
pendingMetadata = void 0;
|
|
1592
|
+
}
|
|
1593
|
+
return memories;
|
|
1594
|
+
}
|
|
1595
|
+
function findUnsafeMarkdownMemoryFindings(memories) {
|
|
1596
|
+
const findings = [];
|
|
1597
|
+
for (const memory of memories) {
|
|
1598
|
+
if (SECRET_PATTERNS.some((pattern) => pattern.test(memory.text))) {
|
|
1599
|
+
findings.push({
|
|
1600
|
+
checksum: memory.checksum,
|
|
1601
|
+
reason: "possible-secret",
|
|
1602
|
+
severity: "error",
|
|
1603
|
+
message: `Possible secret in ${memory.sourceFile}:${memory.lineStart}`
|
|
1604
|
+
});
|
|
1605
|
+
}
|
|
1606
|
+
if (PROMPT_INJECTION_PATTERNS.some((pattern) => pattern.test(memory.text))) {
|
|
1607
|
+
findings.push({
|
|
1608
|
+
checksum: memory.checksum,
|
|
1609
|
+
reason: "prompt-injection",
|
|
1610
|
+
severity: "warn",
|
|
1611
|
+
message: `Prompt-injection-like instruction in ${memory.sourceFile}:${memory.lineStart}`
|
|
1612
|
+
});
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
return findings;
|
|
1616
|
+
}
|
|
1617
|
+
function shouldImportMarkdownMemory(memory, findings) {
|
|
1618
|
+
return !findings.some((finding) => finding.checksum === memory.checksum);
|
|
1619
|
+
}
|
|
1620
|
+
function sectionFor(memory) {
|
|
1621
|
+
if (memory.memoryType === "procedural" || memory.tags.includes("playbook")) return "Playbooks";
|
|
1622
|
+
if (memory.tags.includes("gotcha")) return "Gotchas";
|
|
1623
|
+
if (memory.tags.includes("decision")) return "Decisions";
|
|
1624
|
+
return "Conventions";
|
|
1625
|
+
}
|
|
1626
|
+
function sortExportMemories(a, b) {
|
|
1627
|
+
const section = sectionFor(a).localeCompare(sectionFor(b));
|
|
1628
|
+
if (section !== 0) return section;
|
|
1629
|
+
return a.id.localeCompare(b.id);
|
|
1630
|
+
}
|
|
1631
|
+
function exportMarkdownMemories(memories, options = {}) {
|
|
1632
|
+
const title = options.title ?? (options.format === "claude" ? "CLAUDE.md" : "Memory");
|
|
1633
|
+
const sorted = [...memories].sort(sortExportMemories);
|
|
1634
|
+
const lines = [
|
|
1635
|
+
`# ${title}`,
|
|
1636
|
+
"",
|
|
1637
|
+
"Generated from Unforgit. Edit carefully; run `unforgit md sync` to import reviewed changes.",
|
|
1638
|
+
""
|
|
1639
|
+
];
|
|
1640
|
+
let currentSection = "";
|
|
1641
|
+
for (const memory of sorted) {
|
|
1642
|
+
const section = sectionFor(memory);
|
|
1643
|
+
if (section !== currentSection) {
|
|
1644
|
+
if (currentSection) lines.push("");
|
|
1645
|
+
lines.push(`## ${section}`, "");
|
|
1646
|
+
currentSection = section;
|
|
1647
|
+
}
|
|
1648
|
+
const tags = uniqueTags(memory.tags);
|
|
1649
|
+
lines.push(
|
|
1650
|
+
`<!-- unforgit:id=${memory.id} type=${memory.memoryType} tags=${tags.join(",")} -->`,
|
|
1651
|
+
`- ${memory.text}`
|
|
1652
|
+
);
|
|
1653
|
+
}
|
|
1654
|
+
return `${lines.join("\n")}
|
|
1655
|
+
`;
|
|
1656
|
+
}
|
|
1464
1657
|
|
|
1465
1658
|
// ../../packages/config/dist/index.js
|
|
1466
1659
|
import fs from "fs";
|
|
@@ -3784,6 +3977,10 @@ export {
|
|
|
3784
3977
|
getTemplate,
|
|
3785
3978
|
applyTemplate,
|
|
3786
3979
|
formatTemplateList,
|
|
3980
|
+
parseMarkdownMemories,
|
|
3981
|
+
findUnsafeMarkdownMemoryFindings,
|
|
3982
|
+
shouldImportMarkdownMemory,
|
|
3983
|
+
exportMarkdownMemories,
|
|
3787
3984
|
validateMemoryType,
|
|
3788
3985
|
parseConfidence,
|
|
3789
3986
|
parseThreshold,
|
|
@@ -3801,4 +3998,4 @@ export {
|
|
|
3801
3998
|
RemoteClient,
|
|
3802
3999
|
LocalStore
|
|
3803
4000
|
};
|
|
3804
|
-
//# sourceMappingURL=chunk-
|
|
4001
|
+
//# sourceMappingURL=chunk-JXSAAZ3I.js.map
|