unforgit 0.8.4 → 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.
@@ -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-VESJIIT2.js.map
4001
+ //# sourceMappingURL=chunk-JXSAAZ3I.js.map