capture-inbox.mjs
74 lines 2.4 KB
Raw
sha256:8915fe406161f95c1681f9469375e7bae5b28c884f00bedbdef65e4b0cd0738d docs(flow): commit FLOW-V0-SPEC.md hygiene for 7A-INT merge Human 16 hours ago
1 /**
2 * Fast inbox capture (Issue #1 Phase C3). No AIR — inbox exempt.
3 */
4
5 import { loadConfig } from './config.mjs';
6 import { writeNote } from './write.mjs';
7 import { normalizeSlug, normalizeTags } from './vault.mjs';
8
9 function slugFromText(text, maxLen = 48) {
10 const base = String(text || '')
11 .toLowerCase()
12 .replace(/[^a-z0-9]+/g, '-')
13 .replace(/^-|-$/g, '')
14 .slice(0, maxLen);
15 return base || 'capture';
16 }
17
18 /**
19 * Build vault-relative path, body, and inbox frontmatter for a fast capture (local disk or hosted canister write).
20 *
21 * @param {string} text
22 * @param {{ source?: string, project?: string, tags?: string[] }} options
23 * @param {Date} [now] - defaults to `new Date()`; inject in tests for stable paths
24 * @returns {{ path: string, body: string, frontmatter: Record<string, unknown> }}
25 */
26 export function buildCaptureInboxWritePayload(text, options = {}, now = new Date()) {
27 const dateStr = now.toISOString().slice(0, 10);
28 const hh = String(now.getHours()).padStart(2, '0');
29 const mm = String(now.getMinutes()).padStart(2, '0');
30 const ss = String(now.getSeconds()).padStart(2, '0');
31 const timeStr = `${hh}${mm}${ss}`;
32 const slug = slugFromText(text);
33
34 let relDir = 'inbox';
35 if (options.project) {
36 const proj = normalizeSlug(String(options.project));
37 if (proj) relDir = `projects/${proj}/inbox`;
38 }
39
40 const filename = `${dateStr}-${timeStr}-${slug}.md`.replace(/-+/g, '-');
41 const relativePath = `${relDir}/${filename}`;
42
43 const tags = options.tags?.length ? normalizeTags(options.tags) : [];
44 const tagStr = tags.length ? tags.join(', ') : undefined;
45
46 /** @type {Record<string, unknown>} */
47 const frontmatter = {
48 source: options.source || 'mcp-capture',
49 date: dateStr,
50 inbox: true,
51 };
52 if (tagStr) frontmatter.tags = tagStr;
53 if (options.project) frontmatter.project = normalizeSlug(String(options.project));
54
55 return {
56 path: relativePath,
57 body: String(text || '').trim(),
58 frontmatter,
59 };
60 }
61
62 /**
63 * @param {string} text
64 * @param {{ source?: string, project?: string, tags?: string[] }} options
65 * @returns {Promise<{ path: string, written: boolean }>}
66 */
67 export async function runCaptureInbox(text, options = {}) {
68 const config = loadConfig();
69 const { path, body, frontmatter } = buildCaptureInboxWritePayload(text, options);
70 return writeNote(config.vault_path, path, {
71 body,
72 frontmatter,
73 });
74 }
File History 1 commit
sha256:8915fe406161f95c1681f9469375e7bae5b28c884f00bedbdef65e4b0cd0738d docs(flow): commit FLOW-V0-SPEC.md hygiene for 7A-INT merge Human 16 hours ago