-
Notifications
You must be signed in to change notification settings - Fork 42
Expand file tree
/
Copy pathauto-capture.mjs
More file actions
executable file
·101 lines (93 loc) · 3.6 KB
/
Copy pathauto-capture.mjs
File metadata and controls
executable file
·101 lines (93 loc) · 3.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
#!/usr/bin/env node
/**
* Auto-Capture: Converts insights into claim-named vault notes.
*
* Usage:
* node auto-capture.mjs --claim "nemotron mamba wont train on windows" --body "details..."
* node auto-capture.mjs --file summary.txt
* echo "insight text" | node auto-capture.mjs
*/
import fs from 'fs';
import path from 'path';
const VAULT = process.env.OPENCLAW_WORKSPACE
? path.join(process.env.OPENCLAW_WORKSPACE, 'vault')
: path.resolve('vault');
const INBOX = path.join(VAULT, '00_inbox');
const THINKING = path.join(VAULT, '01_thinking');
function toClaimName(text) {
let claim = text.split(/[.!?\n]/)[0].trim();
if (claim.length > 80) claim = claim.substring(0, 80);
return claim.toLowerCase()
.replace(/[^a-z0-9\s-]/g, '')
.replace(/\s+/g, '-')
.replace(/-+/g, '-')
.replace(/^-|-$/g, '')
.substring(0, 60) + '.md';
}
function findRelatedMOCs(text) {
if (!fs.existsSync(THINKING)) return [];
const mocs = fs.readdirSync(THINKING).filter(f => f.endsWith('.md') && f !== 'README.md');
const textLower = text.toLowerCase();
return mocs.filter(moc => {
const keywords = moc.replace('.md', '').split('-').filter(w => w.length > 3);
return keywords.filter(kw => textLower.includes(kw)).length >= 2;
}).map(m => m.replace('.md', ''));
}
function buildNote(claim, body, relatedMOCs) {
const date = new Date().toISOString().split('T')[0];
let note = `# ${claim}\n\n## Key Facts\n\n${body}\n\n`;
if (relatedMOCs.length > 0) {
note += `## Connected Topics\n\n`;
for (const moc of relatedMOCs) note += `- [[${moc}]]\n`;
note += `\n`;
}
note += `## Agent Notes\n\n- [ ] Review and verify this capture\n`;
note += `- [ ] Link to additional related notes\n`;
note += `- [ ] Move from inbox to appropriate vault folder\n`;
note += `\n_Captured: ${date}_\n`;
return note;
}
function updateMOCs(filename, relatedMOCs) {
const linkName = filename.replace('.md', '');
for (const moc of relatedMOCs) {
const mocPath = path.join(THINKING, moc + '.md');
if (!fs.existsSync(mocPath)) continue;
let content = fs.readFileSync(mocPath, 'utf8');
if (!content.includes(`[[${linkName}]]`)) {
const idx = content.indexOf('## Agent Notes');
if (idx !== -1) {
const insertPoint = content.indexOf('\n', idx) + 1;
content = content.substring(0, insertPoint)
+ `\n- [ ] New capture linked: [[${linkName}]]\n`
+ content.substring(insertPoint);
fs.writeFileSync(mocPath, content, 'utf8');
console.log(` Updated MOC: ${moc}.md`);
}
}
}
}
async function main() {
const args = process.argv.slice(2);
let claim = '', body = '';
if (args.includes('--claim')) claim = args[args.indexOf('--claim') + 1] || '';
if (args.includes('--body')) body = args[args.indexOf('--body') + 1] || '';
if (args.includes('--file')) {
const f = args[args.indexOf('--file') + 1];
if (f && fs.existsSync(f)) { body = fs.readFileSync(f, 'utf8'); if (!claim) claim = body.split('\n')[0]; }
}
if (!claim && !body) {
console.log('Usage: node auto-capture.mjs --claim "insight" --body "details..."');
process.exit(1);
}
const filename = toClaimName(claim);
const filepath = path.join(INBOX, filename);
const relatedMOCs = findRelatedMOCs(claim + ' ' + body);
if (!fs.existsSync(INBOX)) fs.mkdirSync(INBOX, { recursive: true });
fs.writeFileSync(filepath, buildNote(claim, body, relatedMOCs), 'utf8');
console.log(`✅ Captured: ${filename}`);
if (relatedMOCs.length > 0) {
console.log(` Related MOCs: ${relatedMOCs.join(', ')}`);
updateMOCs(filename, relatedMOCs);
}
}
main().catch(console.error);