v0 scaffold: teacher runtime (hermetic pi SDK), data model, ledgers, normalizer

- teacher/system-prompt.md: teach methodology + quiz/ask guidelines folded in
- extensions/: quiz.ts + ask-user-question.ts (hermetic copies)
- src/teacher/create-session.ts: isolated agent (noTools builtin, own agentDir/sessions/prompt)
- src/schema.ts + ledger.ts: append-only ledgers, known-vs-understood data model
- src/extract/normalize.ts: session JSONL -> canonical doc (verified on real session)
- extractor/merger/derive/what-next: specified stubs
This commit is contained in:
Amos
2026-08-21 21:37:28 +03:00
commit 9a6af9697f
19 changed files with 5271 additions and 0 deletions
+78
View File
@@ -0,0 +1,78 @@
#!/usr/bin/env node
/**
* learning-harness CLI
*
* teach [message] launch an interactive teaching session (full pi TUI)
* doctor non-interactive smoke test of the teacher wiring
* ingest <file> normalize a session JSONL and print the canonical document
* (Stage 1 only — extractor/merger not yet implemented)
* graph print graph/ledger stats
* what-next (not yet implemented)
*/
import * as fs from "node:fs";
import { doctor, runTeachingSession } from "./teacher/create-session.ts";
import { normalizeSession } from "./extract/normalize.ts";
import { ledgers } from "./ledger.ts";
import { deriveAll } from "./derive.ts";
import { PATHS } from "../config.ts";
async function main() {
const [command, ...rest] = process.argv.slice(2);
switch (command) {
case "teach": {
await runTeachingSession(rest.join(" ") || undefined);
break;
}
case "doctor": {
await doctor();
break;
}
case "ingest": {
const file = rest[0];
if (!file) {
console.error("Usage: ingest <session.jsonl>");
process.exit(1);
}
const doc = normalizeSession(file);
ledgers.appendSession(doc.session);
console.log(`Session: ${doc.session.id}`);
console.log(`Topic: ${doc.session.topic}`);
console.log(`Entries: ${doc.entries.length}`);
const quizzes = doc.entries.filter((e) => e.kind === "quiz");
console.log(`Quizzes: ${quizzes.length}`);
// Full canonical document for inspection / later pipeline stages.
fs.mkdirSync(PATHS.graph, { recursive: true });
const out = `${PATHS.graph}/canonical-${doc.session.id.slice(0, 8)}.json`;
fs.writeFileSync(out, JSON.stringify(doc, null, "\t"), "utf-8");
console.log(`Canonical document written: ${out}`);
break;
}
case "graph": {
const nodes = ledgers.nodes();
const edges = ledgers.edges();
const events = ledgers.events();
const sessions = ledgers.sessions();
const derived = deriveAll();
console.log(`Sessions: ${sessions.length}`);
console.log(`Nodes: ${nodes.length} (known: ${derived.filter((d) => d.known).length})`);
console.log(`Edges: ${edges.length}`);
console.log(`Events: ${events.length}`);
break;
}
case "what-next": {
console.error("Not implemented yet — needs extractor + merger first.");
process.exit(1);
}
default: {
console.log("Commands: teach [message] | doctor | ingest <file> | graph | what-next");
process.exit(command ? 1 : 0);
}
}
}
main().catch((err) => {
console.error(err);
process.exit(1);
});