feat: add familiar second-brain loop
This commit is contained in:
parent
ed208d56e4
commit
275de5d0e8
|
|
@ -158,6 +158,9 @@ fable-agent plinius godmode "improve explanation quality"
|
||||||
- `familiar graph`
|
- `familiar graph`
|
||||||
- `familiar health`
|
- `familiar health`
|
||||||
- `familiar briefing`
|
- `familiar briefing`
|
||||||
|
- `familiar loop`
|
||||||
|
- `-p, --priority <n>`
|
||||||
|
- `-d, --dir <path>`
|
||||||
|
|
||||||
### `pai-pi`
|
### `pai-pi`
|
||||||
|
|
||||||
|
|
|
||||||
78
src/index.ts
78
src/index.ts
|
|
@ -1185,6 +1185,52 @@ familiar
|
||||||
console.log(`\n${briefing}\n`);
|
console.log(`\n${briefing}\n`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
familiar
|
||||||
|
.command("loop")
|
||||||
|
.description("Process inbox notes, detect simple contradictions, and queue follow-up goals")
|
||||||
|
.option("-p, --priority <n>", "Queued goal priority (1=high, 2=normal, 3=low)", parseInt, 1)
|
||||||
|
.option("-d, --dir <path>", "Base directory containing inbox/wiki/resources", process.cwd())
|
||||||
|
.action(async (opts: { priority?: number; dir?: string }) => {
|
||||||
|
const { FamiliarKnowledge } = await import("./upgrades/familiar-knowledge.js");
|
||||||
|
const { GoalQueue } = await import("./upgrades/goal-queue.js");
|
||||||
|
const { StateStore } = await import("./core/state-store.js");
|
||||||
|
const f = new FamiliarKnowledge(opts.dir);
|
||||||
|
const queue = new GoalQueue(new StateStore());
|
||||||
|
const priorClaims = loadWikiClaims(opts.dir ?? process.cwd());
|
||||||
|
const priority = opts.priority === 2 || opts.priority === 3 ? opts.priority : 1;
|
||||||
|
|
||||||
|
const processed = f.processAllInbox((content) => {
|
||||||
|
const firstLine = content.split("\n")[0]?.replace(/^#\s*/, "").trim() || "Untitled note";
|
||||||
|
const claims = splitClaims(content);
|
||||||
|
const contradictions = claims.flatMap((claim) => detectSimpleContradictions(claim, priorClaims));
|
||||||
|
return {
|
||||||
|
title: firstLine.slice(0, 80),
|
||||||
|
summary: claims.join("; ").slice(0, 300),
|
||||||
|
confidence: contradictions.length > 0 ? 0.9 : 0.6,
|
||||||
|
tags: contradictions.length > 0 ? ["second-brain", "contradiction"] : ["second-brain"],
|
||||||
|
mentions: [],
|
||||||
|
backlinks: [],
|
||||||
|
contradictions,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const queued: string[] = [];
|
||||||
|
for (const note of processed) {
|
||||||
|
for (const contradiction of note.contradictions) {
|
||||||
|
queued.push(queue.push(`Resolve contradiction in ${path.basename(note.wikiPath)}: ${contradiction}`, {
|
||||||
|
priority,
|
||||||
|
tags: ["second-brain", "contradiction"],
|
||||||
|
}).id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`\n Familiar loop`);
|
||||||
|
console.log(` Processed: ${processed.length} note(s)`);
|
||||||
|
console.log(` Queued: ${queued.length} contradiction goal(s)`);
|
||||||
|
if (queued.length > 0) console.log(` Goal IDs: ${queued.slice(0, 3).join(", ")}`);
|
||||||
|
console.log(``);
|
||||||
|
});
|
||||||
|
|
||||||
// ── PAI Pi ───────────────────────────────────────────────────
|
// ── PAI Pi ───────────────────────────────────────────────────
|
||||||
|
|
||||||
const paiPi = program
|
const paiPi = program
|
||||||
|
|
@ -2626,6 +2672,38 @@ security
|
||||||
✓ No prompt-injection markers found in ${files.length} file(s)`);
|
✓ No prompt-injection markers found in ${files.length} file(s)`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function splitClaims(content: string): string[] {
|
||||||
|
return content
|
||||||
|
.split(/[.!?]\s+/)
|
||||||
|
.map((s) => s.trim())
|
||||||
|
.filter((s) => s.length > 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadWikiClaims(baseDir: string): string[] {
|
||||||
|
const wikiDir = path.join(baseDir, "wiki");
|
||||||
|
if (!fs.existsSync(wikiDir)) return [];
|
||||||
|
return fs.readdirSync(wikiDir)
|
||||||
|
.filter((file) => file.endsWith(".md"))
|
||||||
|
.map((file) => fs.readFileSync(path.join(wikiDir, file), "utf-8").toLowerCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
function detectSimpleContradictions(claim: string, prior: string[]): string[] {
|
||||||
|
const current = claim.toLowerCase();
|
||||||
|
const opposites: Array<[string, string]> = [
|
||||||
|
["always", "never"],
|
||||||
|
["enabled", "disabled"],
|
||||||
|
["online", "offline"],
|
||||||
|
["allow", "block"],
|
||||||
|
["increase", "decrease"],
|
||||||
|
];
|
||||||
|
return prior
|
||||||
|
.filter((entry) => opposites.some(([a, b]) =>
|
||||||
|
(entry.includes(a) && current.includes(b)) || (entry.includes(b) && current.includes(a))
|
||||||
|
))
|
||||||
|
.slice(0, 2)
|
||||||
|
.map((entry) => `Conflicts with prior: ${entry.slice(0, 140)}`);
|
||||||
|
}
|
||||||
|
|
||||||
program.parse(process.argv);
|
program.parse(process.argv);
|
||||||
|
|
||||||
// Show help if no args
|
// Show help if no args
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue