mif.mjs
88 lines 3.4 KB
Raw
sha256:65ccb454656ea5acdea0a10e559b78bcde1eb6ff753ecc2911bc99d1c3d7cadd feat(calendar): enforce agent context tiers in retrieval AP… Human minor ⚠ breaking 1 day ago
1 /**
2 * MIF (Memory Interchange Format) importer. Copy .memory.md or folder into vault.
3 * Add source: mif; optionally normalize frontmatter (mif:id -> source_id).
4 */
5
6 import fs from 'fs';
7 import path from 'path';
8 import { writeNote } from '../write.mjs';
9 import { parseFrontmatterAndBody, normalizeSlug } from '../vault.mjs';
10
11 /**
12 * @param {string} input - Path to .memory.md, .memory.json, or folder of MIF files
13 * @param {{ vaultPath: string, outputBase: string, project?: string, tags: string[], dryRun: boolean }} ctx
14 * @returns {Promise<{ imported: { path: string, source_id?: string }[], count: number }>}
15 */
16 export async function importMIF(input, ctx) {
17 const { vaultPath, outputBase, project, tags, dryRun } = ctx;
18 const absInput = path.isAbsolute(input) ? input : path.resolve(process.cwd(), input);
19 if (!fs.existsSync(absInput)) {
20 throw new Error(`Input not found: ${input}`);
21 }
22
23 const files = [];
24 if (fs.statSync(absInput).isFile()) {
25 if (absInput.endsWith('.memory.md') || absInput.endsWith('.memory.json') || absInput.endsWith('.md')) {
26 files.push({ fullPath: absInput, relPath: path.basename(absInput).replace(/\.memory\.(md|json)$/, '.md') });
27 }
28 } else {
29 walkMIF(absInput, absInput, '', files);
30 }
31
32 const imported = [];
33 const now = new Date().toISOString().slice(0, 10);
34
35 for (const { fullPath, relPath } of files) {
36 const ext = path.extname(fullPath);
37 let content, frontmatter = {}, body = '';
38
39 if (ext === '.json') {
40 const raw = fs.readFileSync(fullPath, 'utf8');
41 let mif;
42 try {
43 mif = JSON.parse(raw);
44 } catch (e) {
45 continue; // skip invalid JSON
46 }
47 body = typeof mif.content === 'string' ? mif.content : (mif.text || JSON.stringify(mif));
48 frontmatter = mif.metadata || mif.frontmatter || {};
49 if (mif.id) frontmatter.source_id = mif.id;
50 } else {
51 content = fs.readFileSync(fullPath, 'utf8');
52 const parsed = parseFrontmatterAndBody(content);
53 frontmatter = { ...parsed.frontmatter };
54 body = parsed.body;
55 }
56
57 frontmatter.source = 'mif';
58 frontmatter.date = frontmatter.date || frontmatter.created || now;
59 if (frontmatter['mif:id'] && !frontmatter.source_id) frontmatter.source_id = frontmatter['mif:id'];
60 if (project) frontmatter.project = normalizeSlug(project);
61 if (tags.length) frontmatter.tags = [...new Set([...(Array.isArray(frontmatter.tags) ? frontmatter.tags : []), ...tags])];
62
63 const outputRel = path.join(outputBase, relPath).replace(/\\/g, '/');
64 const cleanFrontmatter = Object.fromEntries(
65 Object.entries(frontmatter).filter(([, v]) => v !== undefined && v !== null && v !== '')
66 );
67
68 if (!dryRun) {
69 writeNote(vaultPath, outputRel, { body, frontmatter: cleanFrontmatter });
70 }
71 imported.push({ path: outputRel, source_id: frontmatter.source_id });
72 }
73
74 return { imported, count: imported.length };
75 }
76
77 function walkMIF(rootDir, dir, relDir, out) {
78 const entries = fs.readdirSync(dir, { withFileTypes: true });
79 for (const e of entries) {
80 const rel = relDir ? `${relDir}/${e.name}` : e.name;
81 if (e.isDirectory()) {
82 walkMIF(rootDir, path.join(dir, e.name), rel, out);
83 } else if (e.name.endsWith('.memory.md') || e.name.endsWith('.memory.json')) {
84 const outName = e.name.replace(/\.memory\.(md|json)$/, '.md');
85 out.push({ fullPath: path.join(dir, e.name), relPath: (relDir ? `${relDir}/` : '') + outName });
86 }
87 }
88 }
File History 2 commits
sha256:65ccb454656ea5acdea0a10e559b78bcde1eb6ff753ecc2911bc99d1c3d7cadd feat(calendar): enforce agent context tiers in retrieval AP… Human minor 1 day ago
sha256:9103f98c89257ed2b01c237cea895dabb3e85ea337dccb1161c175e4422355b6 docs: accept Calendar Events v0 spec with Phase 0 security … Human 1 day ago