majlis 0.8.4 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +597 -8
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -75,6 +75,20 @@ function statusColor(status2) {
|
|
|
75
75
|
return yellow(status2);
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
|
+
function gradeColor(grade) {
|
|
79
|
+
switch (grade) {
|
|
80
|
+
case "sound":
|
|
81
|
+
return green(grade);
|
|
82
|
+
case "good":
|
|
83
|
+
return cyan(grade);
|
|
84
|
+
case "weak":
|
|
85
|
+
return yellow(grade);
|
|
86
|
+
case "rejected":
|
|
87
|
+
return red(grade);
|
|
88
|
+
default:
|
|
89
|
+
return grade;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
78
92
|
function evidenceColor(level) {
|
|
79
93
|
switch (level) {
|
|
80
94
|
case "proof":
|
|
@@ -375,6 +389,33 @@ var init_migrations = __esm({
|
|
|
375
389
|
(db) => {
|
|
376
390
|
db.exec(`
|
|
377
391
|
ALTER TABLE experiments ADD COLUMN gate_rejection_reason TEXT;
|
|
392
|
+
`);
|
|
393
|
+
},
|
|
394
|
+
// Migration 008: v7 → v8 — Pilot integration: notes, journal, hypothesis file, provenance
|
|
395
|
+
(db) => {
|
|
396
|
+
db.exec(`
|
|
397
|
+
CREATE TABLE notes (
|
|
398
|
+
id INTEGER PRIMARY KEY,
|
|
399
|
+
session_id INTEGER REFERENCES sessions(id),
|
|
400
|
+
experiment_id INTEGER REFERENCES experiments(id),
|
|
401
|
+
tag TEXT,
|
|
402
|
+
content TEXT NOT NULL,
|
|
403
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
404
|
+
);
|
|
405
|
+
CREATE INDEX idx_notes_session ON notes(session_id);
|
|
406
|
+
CREATE INDEX idx_notes_experiment ON notes(experiment_id);
|
|
407
|
+
|
|
408
|
+
CREATE TABLE journal_entries (
|
|
409
|
+
id INTEGER PRIMARY KEY,
|
|
410
|
+
session_id INTEGER REFERENCES sessions(id),
|
|
411
|
+
content TEXT NOT NULL,
|
|
412
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
413
|
+
);
|
|
414
|
+
CREATE INDEX idx_journal_session ON journal_entries(session_id);
|
|
415
|
+
|
|
416
|
+
ALTER TABLE experiments ADD COLUMN hypothesis_file TEXT;
|
|
417
|
+
ALTER TABLE experiments ADD COLUMN provenance TEXT DEFAULT 'cycle'
|
|
418
|
+
CHECK(provenance IN ('cycle', 'catch-up', 'absorb'));
|
|
378
419
|
`);
|
|
379
420
|
}
|
|
380
421
|
];
|
|
@@ -1781,10 +1822,10 @@ Run \`majlis status\` for live experiment state and cycle position.
|
|
|
1781
1822
|
}
|
|
1782
1823
|
}, null, 2);
|
|
1783
1824
|
}
|
|
1784
|
-
var
|
|
1825
|
+
var fs24 = __toESM2(require("fs"));
|
|
1785
1826
|
function mkdirSafe3(dir) {
|
|
1786
|
-
if (!
|
|
1787
|
-
|
|
1827
|
+
if (!fs24.existsSync(dir)) {
|
|
1828
|
+
fs24.mkdirSync(dir, { recursive: true });
|
|
1788
1829
|
}
|
|
1789
1830
|
}
|
|
1790
1831
|
function validateProject2(checks) {
|
|
@@ -3985,6 +4026,37 @@ function updateSwarmMember(db, swarmRunId, slug, finalStatus, overallGrade, cost
|
|
|
3985
4026
|
WHERE swarm_run_id = ? AND experiment_slug = ?
|
|
3986
4027
|
`).run(finalStatus, overallGrade, costUsd, error2, swarmRunId, slug);
|
|
3987
4028
|
}
|
|
4029
|
+
function insertNote(db, sessionId, experimentId, tag, content) {
|
|
4030
|
+
const stmt = db.prepare(`
|
|
4031
|
+
INSERT INTO notes (session_id, experiment_id, tag, content) VALUES (?, ?, ?, ?)
|
|
4032
|
+
`);
|
|
4033
|
+
const result = stmt.run(sessionId, experimentId, tag, content);
|
|
4034
|
+
return db.prepare("SELECT * FROM notes WHERE id = ?").get(result.lastInsertRowid);
|
|
4035
|
+
}
|
|
4036
|
+
function getNotesBySession(db, sessionId) {
|
|
4037
|
+
return db.prepare("SELECT * FROM notes WHERE session_id = ? ORDER BY created_at").all(sessionId);
|
|
4038
|
+
}
|
|
4039
|
+
function getNotesByExperiment(db, experimentId) {
|
|
4040
|
+
return db.prepare("SELECT * FROM notes WHERE experiment_id = ? ORDER BY created_at").all(experimentId);
|
|
4041
|
+
}
|
|
4042
|
+
function getRecentNotes(db, limit = 20) {
|
|
4043
|
+
return db.prepare("SELECT * FROM notes ORDER BY created_at DESC LIMIT ?").all(limit);
|
|
4044
|
+
}
|
|
4045
|
+
function insertJournalEntry(db, sessionId, content) {
|
|
4046
|
+
const stmt = db.prepare(`
|
|
4047
|
+
INSERT INTO journal_entries (session_id, content) VALUES (?, ?)
|
|
4048
|
+
`);
|
|
4049
|
+
const result = stmt.run(sessionId, content);
|
|
4050
|
+
return db.prepare("SELECT * FROM journal_entries WHERE id = ?").get(result.lastInsertRowid);
|
|
4051
|
+
}
|
|
4052
|
+
function getJournalBySession(db, sessionId) {
|
|
4053
|
+
return db.prepare("SELECT * FROM journal_entries WHERE session_id = ? ORDER BY created_at").all(sessionId);
|
|
4054
|
+
}
|
|
4055
|
+
function storeHypothesisFile(db, experimentId, filePath) {
|
|
4056
|
+
db.prepare(`
|
|
4057
|
+
UPDATE experiments SET hypothesis_file = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?
|
|
4058
|
+
`).run(filePath, experimentId);
|
|
4059
|
+
}
|
|
3988
4060
|
function exportForCompressor(db, maxLength = 5e4) {
|
|
3989
4061
|
const experiments = listAllExperiments(db);
|
|
3990
4062
|
const sections = ["# Structured Data Export (from SQLite)\n"];
|
|
@@ -4043,6 +4115,28 @@ function exportForCompressor(db, maxLength = 5e4) {
|
|
|
4043
4115
|
sections.push(`- [${d.severity}] ${d.claim_doubted} (exp: ${d.experiment_slug})`);
|
|
4044
4116
|
}
|
|
4045
4117
|
}
|
|
4118
|
+
const notes = db.prepare(`
|
|
4119
|
+
SELECT n.*, s.intent as session_intent FROM notes n
|
|
4120
|
+
LEFT JOIN sessions s ON n.session_id = s.id
|
|
4121
|
+
ORDER BY n.created_at
|
|
4122
|
+
`).all();
|
|
4123
|
+
if (notes.length > 0) {
|
|
4124
|
+
sections.push("\n## Pilot Notes (fold into narrative \u2014 these are human/pilot observations)");
|
|
4125
|
+
for (const n of notes) {
|
|
4126
|
+
sections.push(`- ${n.tag ? `[${n.tag}] ` : ""}${n.content}`);
|
|
4127
|
+
}
|
|
4128
|
+
}
|
|
4129
|
+
const journal2 = db.prepare(`
|
|
4130
|
+
SELECT j.*, s.intent as session_intent FROM journal_entries j
|
|
4131
|
+
LEFT JOIN sessions s ON j.session_id = s.id
|
|
4132
|
+
ORDER BY j.created_at
|
|
4133
|
+
`).all();
|
|
4134
|
+
if (journal2.length > 0) {
|
|
4135
|
+
sections.push("\n## Journal (fold into timeline \u2014 breadcrumbs from manual hacking sessions)");
|
|
4136
|
+
for (const j of journal2) {
|
|
4137
|
+
sections.push(`- [${j.created_at}] ${j.content}`);
|
|
4138
|
+
}
|
|
4139
|
+
}
|
|
4046
4140
|
const full = sections.join("\n");
|
|
4047
4141
|
if (full.length > maxLength) {
|
|
4048
4142
|
return full.slice(0, maxLength) + `
|
|
@@ -4180,6 +4274,33 @@ function exportForDiagnostician(db, maxLength = 6e4) {
|
|
|
4180
4274
|
sections.push(`- ${f.slug}: ${f.approach} (${f.source}) ${f.contradicts_current ? "[CONTRADICTS CURRENT]" : ""}`);
|
|
4181
4275
|
}
|
|
4182
4276
|
}
|
|
4277
|
+
const notes = db.prepare(`
|
|
4278
|
+
SELECT n.*, e.slug as exp_slug, s.intent as session_intent
|
|
4279
|
+
FROM notes n
|
|
4280
|
+
LEFT JOIN experiments e ON n.experiment_id = e.id
|
|
4281
|
+
LEFT JOIN sessions s ON n.session_id = s.id
|
|
4282
|
+
ORDER BY n.created_at
|
|
4283
|
+
`).all();
|
|
4284
|
+
if (notes.length > 0) {
|
|
4285
|
+
sections.push("\n## Pilot Notes");
|
|
4286
|
+
for (const n of notes) {
|
|
4287
|
+
const ctx = n.exp_slug ? ` (exp: ${n.exp_slug})` : n.session_intent ? ` (session: ${n.session_intent})` : "";
|
|
4288
|
+
sections.push(`- ${n.tag ? `[${n.tag}] ` : ""}${n.content}${ctx}`);
|
|
4289
|
+
}
|
|
4290
|
+
}
|
|
4291
|
+
const journal2 = db.prepare(`
|
|
4292
|
+
SELECT j.*, s.intent as session_intent
|
|
4293
|
+
FROM journal_entries j
|
|
4294
|
+
LEFT JOIN sessions s ON j.session_id = s.id
|
|
4295
|
+
ORDER BY j.created_at
|
|
4296
|
+
`).all();
|
|
4297
|
+
if (journal2.length > 0) {
|
|
4298
|
+
sections.push("\n## Journal Entries");
|
|
4299
|
+
for (const j of journal2) {
|
|
4300
|
+
const ctx = j.session_intent ? ` (session: ${j.session_intent})` : "";
|
|
4301
|
+
sections.push(`- [${j.created_at}] ${j.content}${ctx}`);
|
|
4302
|
+
}
|
|
4303
|
+
}
|
|
4183
4304
|
const full = sections.join("\n");
|
|
4184
4305
|
if (full.length > maxLength) {
|
|
4185
4306
|
return full.slice(0, maxLength) + `
|
|
@@ -4358,7 +4479,7 @@ var init_types2 = __esm({
|
|
|
4358
4479
|
revert: (current, target) => target === "dead_end" /* DEAD_END */ && !isTerminalStatus(current),
|
|
4359
4480
|
circuit_breaker: (current, target) => target === "dead_end" /* DEAD_END */ && !isTerminalStatus(current),
|
|
4360
4481
|
error_recovery: (current, target) => target === "dead_end" /* DEAD_END */ && !isTerminalStatus(current),
|
|
4361
|
-
bootstrap: (current, target) => current === "classified" /* CLASSIFIED */ && target === "reframed" /* REFRAMED */
|
|
4482
|
+
bootstrap: (current, target) => current === "classified" /* CLASSIFIED */ && target === "reframed" /* REFRAMED */ || current === "classified" /* CLASSIFIED */ && target === "built" /* BUILT */
|
|
4362
4483
|
};
|
|
4363
4484
|
}
|
|
4364
4485
|
});
|
|
@@ -5014,6 +5135,7 @@ Check: (a) stale references \u2014 does the hypothesis reference specific lines,
|
|
|
5014
5135
|
Output your gate_decision as "approve", "reject", or "flag" with reasoning.`
|
|
5015
5136
|
}, root);
|
|
5016
5137
|
ingestStructuredOutput(db, exp.id, result.structured);
|
|
5138
|
+
printStepSummary("gate", result.structured);
|
|
5017
5139
|
const decision = result.structured?.gate_decision ?? "approve";
|
|
5018
5140
|
const reason = result.structured?.reason ?? "";
|
|
5019
5141
|
if (decision === "reject") {
|
|
@@ -5085,6 +5207,17 @@ Your experiment doc: ${expDocRelPath(exp)}`;
|
|
|
5085
5207
|
if (lineage) {
|
|
5086
5208
|
taskPrompt += "\n\n" + lineage;
|
|
5087
5209
|
}
|
|
5210
|
+
const pilotNotes = loadPilotNotes(db, exp.id);
|
|
5211
|
+
if (pilotNotes) taskPrompt += pilotNotes;
|
|
5212
|
+
if (exp.hypothesis_file) {
|
|
5213
|
+
const hypoContent = readFileOrEmpty(path11.join(root, exp.hypothesis_file));
|
|
5214
|
+
if (hypoContent) {
|
|
5215
|
+
taskPrompt += `
|
|
5216
|
+
|
|
5217
|
+
## Structured Hypothesis (from pilot)
|
|
5218
|
+
${hypoContent.slice(0, 8e3)}`;
|
|
5219
|
+
}
|
|
5220
|
+
}
|
|
5088
5221
|
const result = await spawnAgent("builder", {
|
|
5089
5222
|
experiment: {
|
|
5090
5223
|
id: exp.id,
|
|
@@ -5288,6 +5421,7 @@ ${gitDiff}
|
|
|
5288
5421
|
taskPrompt
|
|
5289
5422
|
}, root);
|
|
5290
5423
|
ingestStructuredOutput(db, exp.id, result.structured);
|
|
5424
|
+
printStepSummary("challenge", result.structured);
|
|
5291
5425
|
if (result.truncated && !result.structured) {
|
|
5292
5426
|
warn(`Adversary was truncated without structured output. Experiment stays at current status.`);
|
|
5293
5427
|
} else {
|
|
@@ -5329,6 +5463,7 @@ ${experimentDoc}
|
|
|
5329
5463
|
taskPrompt
|
|
5330
5464
|
}, root);
|
|
5331
5465
|
ingestStructuredOutput(db, exp.id, result.structured);
|
|
5466
|
+
printStepSummary("doubt", result.structured);
|
|
5332
5467
|
if (result.truncated && !result.structured) {
|
|
5333
5468
|
warn(`Critic was truncated without structured output. Experiment stays at current status.`);
|
|
5334
5469
|
} else {
|
|
@@ -5452,6 +5587,8 @@ async function doVerify(db, exp, root) {
|
|
|
5452
5587
|
if (builderGuidanceForVerifier?.includes("[PROVENANCE WARNING]")) {
|
|
5453
5588
|
verifierTaskPrompt += "\n\nNote: The builder's structured output was reconstructed by a secondary model (tier 3). Treat reported decisions with additional scrutiny.";
|
|
5454
5589
|
}
|
|
5590
|
+
const verifierPilotNotes = loadPilotNotes(db, exp.id);
|
|
5591
|
+
if (verifierPilotNotes) verifierTaskPrompt += verifierPilotNotes;
|
|
5455
5592
|
const result = await spawnAgent("verifier", {
|
|
5456
5593
|
experiment: {
|
|
5457
5594
|
id: exp.id,
|
|
@@ -5469,6 +5606,7 @@ async function doVerify(db, exp, root) {
|
|
|
5469
5606
|
taskPrompt: verifierTaskPrompt
|
|
5470
5607
|
}, root);
|
|
5471
5608
|
ingestStructuredOutput(db, exp.id, result.structured);
|
|
5609
|
+
printStepSummary("verify", result.structured);
|
|
5472
5610
|
if (result.truncated && !result.structured) {
|
|
5473
5611
|
warn(`Verifier was truncated without structured output. Experiment stays at 'verifying'.`);
|
|
5474
5612
|
return;
|
|
@@ -5562,6 +5700,20 @@ ${content.slice(0, 8e3)}
|
|
|
5562
5700
|
}
|
|
5563
5701
|
return sections.join("\n\n");
|
|
5564
5702
|
}
|
|
5703
|
+
function loadPilotNotes(db, experimentId) {
|
|
5704
|
+
const expNotes = getNotesByExperiment(db, experimentId);
|
|
5705
|
+
const session2 = getActiveSession(db);
|
|
5706
|
+
const sessionNotes = session2 ? getNotesBySession(db, session2.id) : [];
|
|
5707
|
+
const seenIds = new Set(expNotes.map((n) => n.id));
|
|
5708
|
+
const allNotes = [...expNotes, ...sessionNotes.filter((n) => !seenIds.has(n.id))].slice(-10);
|
|
5709
|
+
if (allNotes.length === 0) return "";
|
|
5710
|
+
let section = "\n\n## Pilot Notes\n";
|
|
5711
|
+
for (const n of allNotes) {
|
|
5712
|
+
section += `- ${n.tag ? `[${n.tag}] ` : ""}${n.content}
|
|
5713
|
+
`;
|
|
5714
|
+
}
|
|
5715
|
+
return section;
|
|
5716
|
+
}
|
|
5565
5717
|
function expDocRelPath(exp) {
|
|
5566
5718
|
return `docs/experiments/${String(exp.id).padStart(3, "0")}-${exp.slug}.md`;
|
|
5567
5719
|
}
|
|
@@ -5577,6 +5729,38 @@ function resolveExperimentArg(db, args) {
|
|
|
5577
5729
|
}
|
|
5578
5730
|
return exp;
|
|
5579
5731
|
}
|
|
5732
|
+
function printStepSummary(step, structured) {
|
|
5733
|
+
if (!structured) return;
|
|
5734
|
+
if (step === "gate") {
|
|
5735
|
+
const decision = structured.gate_decision ?? "approve";
|
|
5736
|
+
const reason = structured.reason ?? "";
|
|
5737
|
+
header(`Gate: ${decision}`);
|
|
5738
|
+
if (reason) console.log(` ${reason.slice(0, 150)}`);
|
|
5739
|
+
}
|
|
5740
|
+
if (step === "doubt" && structured.doubts) {
|
|
5741
|
+
const critical = structured.doubts.filter((d) => d.severity === "critical").length;
|
|
5742
|
+
const moderate = structured.doubts.filter((d) => d.severity === "moderate").length;
|
|
5743
|
+
const minor = structured.doubts.filter((d) => d.severity === "minor").length;
|
|
5744
|
+
header(`Doubt Summary: ${structured.doubts.length} doubt(s) \u2014 ${critical} critical, ${moderate} moderate, ${minor} minor`);
|
|
5745
|
+
for (const d of structured.doubts.slice(0, 3)) {
|
|
5746
|
+
console.log(` [${d.severity}] ${d.claim_doubted.slice(0, 100)}`);
|
|
5747
|
+
}
|
|
5748
|
+
}
|
|
5749
|
+
if (step === "challenge" && structured.challenges) {
|
|
5750
|
+
header(`Challenge Summary: ${structured.challenges.length} challenge(s)`);
|
|
5751
|
+
for (const c of structured.challenges.slice(0, 3)) {
|
|
5752
|
+
console.log(` ${c.description.slice(0, 100)}`);
|
|
5753
|
+
}
|
|
5754
|
+
}
|
|
5755
|
+
if (step === "verify" && structured.grades) {
|
|
5756
|
+
const grades = structured.grades.map((g) => g.grade);
|
|
5757
|
+
const worst = ["rejected", "weak", "good", "sound"].find((g) => grades.includes(g)) ?? grades[0] ?? "unknown";
|
|
5758
|
+
header(`Verification Summary: overall=${worst}`);
|
|
5759
|
+
for (const g of structured.grades) {
|
|
5760
|
+
console.log(` ${g.component}: ${gradeColor(g.grade)}`);
|
|
5761
|
+
}
|
|
5762
|
+
}
|
|
5763
|
+
}
|
|
5580
5764
|
function ingestStructuredOutput(db, experimentId, structured) {
|
|
5581
5765
|
if (!structured) return;
|
|
5582
5766
|
db.transaction(() => {
|
|
@@ -5824,6 +6008,8 @@ async function newExperiment(args) {
|
|
|
5824
6008
|
const dependsOn = getFlagValue(args, "--depends-on") ?? null;
|
|
5825
6009
|
const contextArg = getFlagValue(args, "--context") ?? null;
|
|
5826
6010
|
const contextFiles = contextArg ? contextArg.split(",").map((f) => f.trim()) : null;
|
|
6011
|
+
const skipGate = args.includes("--skip-gate");
|
|
6012
|
+
const fromFile = getFlagValue(args, "--from-file") ?? null;
|
|
5827
6013
|
if (dependsOn) {
|
|
5828
6014
|
const depExp = getExperimentBySlug(db, dependsOn);
|
|
5829
6015
|
if (!depExp) {
|
|
@@ -5841,7 +6027,18 @@ async function newExperiment(args) {
|
|
|
5841
6027
|
const templatePath = path12.join(docsDir, "_TEMPLATE.md");
|
|
5842
6028
|
if (fs12.existsSync(templatePath)) {
|
|
5843
6029
|
const template = fs12.readFileSync(templatePath, "utf-8");
|
|
5844
|
-
|
|
6030
|
+
let logContent = template.replace(/\{\{title\}\}/g, hypothesis).replace(/\{\{hypothesis\}\}/g, hypothesis).replace(/\{\{branch\}\}/g, branch).replace(/\{\{status\}\}/g, "classified").replace(/\{\{sub_type\}\}/g, subType ?? "unclassified").replace(/\{\{date\}\}/g, (/* @__PURE__ */ new Date()).toISOString().split("T")[0]);
|
|
6031
|
+
if (fromFile) {
|
|
6032
|
+
const hypoPath = path12.join(root, fromFile);
|
|
6033
|
+
if (fs12.existsSync(hypoPath)) {
|
|
6034
|
+
const hypoContent = fs12.readFileSync(hypoPath, "utf-8");
|
|
6035
|
+
logContent += "\n\n## Structured Hypothesis (from pilot)\n\n" + hypoContent;
|
|
6036
|
+
storeHypothesisFile(db, exp.id, fromFile);
|
|
6037
|
+
info(`Hypothesis file: ${fromFile}`);
|
|
6038
|
+
} else {
|
|
6039
|
+
warn(`Hypothesis file not found: ${fromFile}`);
|
|
6040
|
+
}
|
|
6041
|
+
}
|
|
5845
6042
|
const logPath = path12.join(root, docRelPath);
|
|
5846
6043
|
fs12.writeFileSync(logPath, logContent);
|
|
5847
6044
|
info(`Created experiment log: ${docRelPath}`);
|
|
@@ -5856,6 +6053,10 @@ async function newExperiment(args) {
|
|
|
5856
6053
|
warn("Auto-baseline failed \u2014 run `majlis baseline` manually.");
|
|
5857
6054
|
}
|
|
5858
6055
|
}
|
|
6056
|
+
if (skipGate) {
|
|
6057
|
+
updateExperimentStatus(db, exp.id, "gated");
|
|
6058
|
+
success(`Gate skipped (pilot-verified). Run \`majlis build\` next.`);
|
|
6059
|
+
}
|
|
5859
6060
|
}
|
|
5860
6061
|
async function revert(args) {
|
|
5861
6062
|
const root = findProjectRoot();
|
|
@@ -8193,12 +8394,368 @@ var init_resync = __esm({
|
|
|
8193
8394
|
}
|
|
8194
8395
|
});
|
|
8195
8396
|
|
|
8397
|
+
// src/commands/note.ts
|
|
8398
|
+
var note_exports = {};
|
|
8399
|
+
__export(note_exports, {
|
|
8400
|
+
note: () => note
|
|
8401
|
+
});
|
|
8402
|
+
async function note(args) {
|
|
8403
|
+
const root = findProjectRoot();
|
|
8404
|
+
if (!root) throw new Error("Not in a Majlis project. Run `majlis init` first.");
|
|
8405
|
+
const db = getDb(root);
|
|
8406
|
+
const content = args.filter((a) => !a.startsWith("--")).join(" ");
|
|
8407
|
+
if (!content) {
|
|
8408
|
+
throw new Error(
|
|
8409
|
+
'Usage: majlis note "text" [--tag <tag>] [--experiment <slug>]'
|
|
8410
|
+
);
|
|
8411
|
+
}
|
|
8412
|
+
const tag = getFlagValue(args, "--tag");
|
|
8413
|
+
const expSlug = getFlagValue(args, "--experiment");
|
|
8414
|
+
const session2 = getActiveSession(db);
|
|
8415
|
+
let experimentId = null;
|
|
8416
|
+
if (expSlug) {
|
|
8417
|
+
const exp = getExperimentBySlug(db, expSlug);
|
|
8418
|
+
if (!exp) throw new Error(`Experiment not found: ${expSlug}`);
|
|
8419
|
+
experimentId = exp.id;
|
|
8420
|
+
} else {
|
|
8421
|
+
const latest = getLatestExperiment(db);
|
|
8422
|
+
experimentId = latest?.id ?? null;
|
|
8423
|
+
}
|
|
8424
|
+
insertNote(db, session2?.id ?? null, experimentId, tag ?? null, content);
|
|
8425
|
+
success(`Note saved${tag ? ` [${tag}]` : ""}${expSlug ? ` \u2192 ${expSlug}` : ""}`);
|
|
8426
|
+
}
|
|
8427
|
+
var init_note = __esm({
|
|
8428
|
+
"src/commands/note.ts"() {
|
|
8429
|
+
"use strict";
|
|
8430
|
+
init_connection();
|
|
8431
|
+
init_queries();
|
|
8432
|
+
init_config();
|
|
8433
|
+
init_format();
|
|
8434
|
+
}
|
|
8435
|
+
});
|
|
8436
|
+
|
|
8437
|
+
// src/commands/journal.ts
|
|
8438
|
+
var journal_exports = {};
|
|
8439
|
+
__export(journal_exports, {
|
|
8440
|
+
journal: () => journal
|
|
8441
|
+
});
|
|
8442
|
+
async function journal(args) {
|
|
8443
|
+
const root = findProjectRoot();
|
|
8444
|
+
if (!root) throw new Error("Not in a Majlis project. Run `majlis init` first.");
|
|
8445
|
+
const db = getDb(root);
|
|
8446
|
+
const content = args.filter((a) => !a.startsWith("--")).join(" ");
|
|
8447
|
+
if (!content) {
|
|
8448
|
+
throw new Error('Usage: majlis journal "text"');
|
|
8449
|
+
}
|
|
8450
|
+
const session2 = getActiveSession(db);
|
|
8451
|
+
insertJournalEntry(db, session2?.id ?? null, content);
|
|
8452
|
+
success(`Journal entry saved (${(/* @__PURE__ */ new Date()).toLocaleTimeString()})`);
|
|
8453
|
+
}
|
|
8454
|
+
var init_journal = __esm({
|
|
8455
|
+
"src/commands/journal.ts"() {
|
|
8456
|
+
"use strict";
|
|
8457
|
+
init_connection();
|
|
8458
|
+
init_queries();
|
|
8459
|
+
init_format();
|
|
8460
|
+
}
|
|
8461
|
+
});
|
|
8462
|
+
|
|
8463
|
+
// src/commands/brief.ts
|
|
8464
|
+
var brief_exports = {};
|
|
8465
|
+
__export(brief_exports, {
|
|
8466
|
+
brief: () => brief
|
|
8467
|
+
});
|
|
8468
|
+
async function brief(args, isJson) {
|
|
8469
|
+
const root = findProjectRoot();
|
|
8470
|
+
if (!root) throw new Error("Not in a Majlis project. Run `majlis init` first.");
|
|
8471
|
+
const db = getDb(root);
|
|
8472
|
+
const plain = args.includes("--plain");
|
|
8473
|
+
const short = args.includes("--short");
|
|
8474
|
+
const exp = getLatestExperiment(db);
|
|
8475
|
+
const session2 = getActiveSession(db);
|
|
8476
|
+
const deadEnds = exp?.sub_type ? listStructuralDeadEndsBySubType(db, exp.sub_type) : listStructuralDeadEnds(db);
|
|
8477
|
+
const doubts = exp ? getDoubtsByExperiment(db, exp.id) : [];
|
|
8478
|
+
const challenges = exp ? getChallengesByExperiment(db, exp.id) : [];
|
|
8479
|
+
const verifications = exp ? getVerificationsByExperiment(db, exp.id) : [];
|
|
8480
|
+
const beforeMetrics = exp ? getMetricsByExperimentAndPhase(db, exp.id, "before") : [];
|
|
8481
|
+
const afterMetrics = exp ? getMetricsByExperimentAndPhase(db, exp.id, "after") : [];
|
|
8482
|
+
const notes = getRecentNotes(db, 5);
|
|
8483
|
+
if (isJson) {
|
|
8484
|
+
const data = {
|
|
8485
|
+
experiment: exp ? {
|
|
8486
|
+
slug: exp.slug,
|
|
8487
|
+
status: exp.status,
|
|
8488
|
+
hypothesis: exp.hypothesis,
|
|
8489
|
+
sub_type: exp.sub_type
|
|
8490
|
+
} : null,
|
|
8491
|
+
dead_ends: deadEnds.slice(0, 5).map((d) => ({
|
|
8492
|
+
id: d.id,
|
|
8493
|
+
structural_constraint: d.structural_constraint
|
|
8494
|
+
})),
|
|
8495
|
+
doubts: doubts.map((d) => ({
|
|
8496
|
+
claim: d.claim_doubted,
|
|
8497
|
+
severity: d.severity,
|
|
8498
|
+
resolution: d.resolution
|
|
8499
|
+
})),
|
|
8500
|
+
challenges: challenges.map((c) => ({
|
|
8501
|
+
description: c.description
|
|
8502
|
+
})),
|
|
8503
|
+
verifications: verifications.map((v) => ({
|
|
8504
|
+
component: v.component,
|
|
8505
|
+
grade: v.grade,
|
|
8506
|
+
notes: v.notes
|
|
8507
|
+
})),
|
|
8508
|
+
metrics: beforeMetrics.map((bm) => {
|
|
8509
|
+
const am = afterMetrics.find(
|
|
8510
|
+
(a) => a.fixture === bm.fixture && a.metric_name === bm.metric_name
|
|
8511
|
+
);
|
|
8512
|
+
return {
|
|
8513
|
+
fixture: bm.fixture,
|
|
8514
|
+
metric: bm.metric_name,
|
|
8515
|
+
before: bm.metric_value,
|
|
8516
|
+
after: am?.metric_value ?? null
|
|
8517
|
+
};
|
|
8518
|
+
}),
|
|
8519
|
+
notes: notes.map((n) => ({
|
|
8520
|
+
tag: n.tag,
|
|
8521
|
+
content: n.content
|
|
8522
|
+
})),
|
|
8523
|
+
session: session2 ? { intent: session2.intent } : null
|
|
8524
|
+
};
|
|
8525
|
+
console.log(JSON.stringify(data, null, 2));
|
|
8526
|
+
return;
|
|
8527
|
+
}
|
|
8528
|
+
const lines = [];
|
|
8529
|
+
lines.push(bold("[majlis] Context Brief"));
|
|
8530
|
+
lines.push("");
|
|
8531
|
+
if (exp) {
|
|
8532
|
+
lines.push(bold("Experiment"));
|
|
8533
|
+
lines.push(` Slug: ${cyan(exp.slug)}`);
|
|
8534
|
+
lines.push(` Status: ${statusColor(exp.status)}`);
|
|
8535
|
+
if (exp.hypothesis) lines.push(` Hypothesis: ${exp.hypothesis}`);
|
|
8536
|
+
if (exp.sub_type) lines.push(` Sub-type: ${dim(exp.sub_type)}`);
|
|
8537
|
+
} else {
|
|
8538
|
+
lines.push(dim("No active experiment."));
|
|
8539
|
+
}
|
|
8540
|
+
if (deadEnds.length > 0) {
|
|
8541
|
+
lines.push("");
|
|
8542
|
+
lines.push(bold("Dead Ends"));
|
|
8543
|
+
for (const d of deadEnds.slice(0, 5)) {
|
|
8544
|
+
lines.push(` - ${dim(`[DE-${d.id}]`)} ${d.structural_constraint}`);
|
|
8545
|
+
}
|
|
8546
|
+
if (deadEnds.length > 5) {
|
|
8547
|
+
lines.push(dim(` ... and ${deadEnds.length - 5} more`));
|
|
8548
|
+
}
|
|
8549
|
+
}
|
|
8550
|
+
if (exp && POST_DOUBT_STATES.has(exp.status)) {
|
|
8551
|
+
if (doubts.length > 0) {
|
|
8552
|
+
lines.push("");
|
|
8553
|
+
lines.push(bold("Doubts"));
|
|
8554
|
+
for (const d of doubts) {
|
|
8555
|
+
const res = d.resolution ? dim(` (${d.resolution})`) : yellow(" (pending)");
|
|
8556
|
+
lines.push(` - [${d.severity}] ${d.claim_doubted}${res}`);
|
|
8557
|
+
}
|
|
8558
|
+
}
|
|
8559
|
+
if (challenges.length > 0) {
|
|
8560
|
+
lines.push("");
|
|
8561
|
+
lines.push(bold("Challenges"));
|
|
8562
|
+
for (const c of challenges) {
|
|
8563
|
+
lines.push(` - ${c.description}`);
|
|
8564
|
+
}
|
|
8565
|
+
}
|
|
8566
|
+
if (verifications.length > 0) {
|
|
8567
|
+
lines.push("");
|
|
8568
|
+
lines.push(bold("Verifications"));
|
|
8569
|
+
for (const v of verifications) {
|
|
8570
|
+
const note2 = v.notes ? dim(` \u2014 ${v.notes}`) : "";
|
|
8571
|
+
lines.push(` - ${v.component}: ${gradeColor(v.grade)}${note2}`);
|
|
8572
|
+
}
|
|
8573
|
+
}
|
|
8574
|
+
}
|
|
8575
|
+
if (notes.length > 0) {
|
|
8576
|
+
lines.push("");
|
|
8577
|
+
lines.push(bold("Recent Notes"));
|
|
8578
|
+
for (const n of notes) {
|
|
8579
|
+
const tag = n.tag ? dim(`[${n.tag}] `) : "";
|
|
8580
|
+
lines.push(` - ${tag}${n.content}`);
|
|
8581
|
+
}
|
|
8582
|
+
}
|
|
8583
|
+
if (beforeMetrics.length > 0) {
|
|
8584
|
+
lines.push("");
|
|
8585
|
+
lines.push(bold("Metrics"));
|
|
8586
|
+
for (const bm of beforeMetrics) {
|
|
8587
|
+
const am = afterMetrics.find(
|
|
8588
|
+
(a) => a.fixture === bm.fixture && a.metric_name === bm.metric_name
|
|
8589
|
+
);
|
|
8590
|
+
if (am) {
|
|
8591
|
+
const delta = am.metric_value - bm.metric_value;
|
|
8592
|
+
const sign = delta >= 0 ? "+" : "";
|
|
8593
|
+
const color = delta >= 0 ? green : red;
|
|
8594
|
+
lines.push(` - ${bm.fixture}/${bm.metric_name}: ${bm.metric_value} -> ${am.metric_value} ${color(`(${sign}${delta.toFixed(4)})`)}`);
|
|
8595
|
+
} else {
|
|
8596
|
+
lines.push(` - ${bm.fixture}/${bm.metric_name}: ${bm.metric_value} ${dim("(no after)")}`);
|
|
8597
|
+
}
|
|
8598
|
+
}
|
|
8599
|
+
}
|
|
8600
|
+
if (session2) {
|
|
8601
|
+
lines.push("");
|
|
8602
|
+
lines.push(bold("Session"));
|
|
8603
|
+
lines.push(` Intent: ${session2.intent}`);
|
|
8604
|
+
}
|
|
8605
|
+
lines.push("");
|
|
8606
|
+
let output = lines.join("\n");
|
|
8607
|
+
if (plain) {
|
|
8608
|
+
output = stripAnsi(output);
|
|
8609
|
+
}
|
|
8610
|
+
if (short && output.length > 3e3) {
|
|
8611
|
+
output = output.slice(0, 2986) + "\n[TRUNCATED]";
|
|
8612
|
+
}
|
|
8613
|
+
process.stdout.write(output);
|
|
8614
|
+
}
|
|
8615
|
+
var POST_DOUBT_STATES;
|
|
8616
|
+
var init_brief = __esm({
|
|
8617
|
+
"src/commands/brief.ts"() {
|
|
8618
|
+
"use strict";
|
|
8619
|
+
init_connection();
|
|
8620
|
+
init_queries();
|
|
8621
|
+
init_format();
|
|
8622
|
+
POST_DOUBT_STATES = /* @__PURE__ */ new Set([
|
|
8623
|
+
"doubted",
|
|
8624
|
+
"challenged",
|
|
8625
|
+
"scouted",
|
|
8626
|
+
"verifying",
|
|
8627
|
+
"verified",
|
|
8628
|
+
"resolved",
|
|
8629
|
+
"compressed",
|
|
8630
|
+
"merged",
|
|
8631
|
+
"dead_end"
|
|
8632
|
+
]);
|
|
8633
|
+
}
|
|
8634
|
+
});
|
|
8635
|
+
|
|
8636
|
+
// src/commands/catchup.ts
|
|
8637
|
+
var catchup_exports = {};
|
|
8638
|
+
__export(catchup_exports, {
|
|
8639
|
+
catchUp: () => catchUp
|
|
8640
|
+
});
|
|
8641
|
+
async function catchUp(args) {
|
|
8642
|
+
const root = findProjectRoot();
|
|
8643
|
+
if (!root) throw new Error("Not in a Majlis project. Run `majlis init` first.");
|
|
8644
|
+
const db = getDb(root);
|
|
8645
|
+
const description = args.filter((a) => !a.startsWith("--")).join(" ");
|
|
8646
|
+
if (!description) {
|
|
8647
|
+
throw new Error('Usage: majlis catch-up "description of what was done"');
|
|
8648
|
+
}
|
|
8649
|
+
const subType = getFlagValue(args, "--sub-type") ?? null;
|
|
8650
|
+
const diffRange = getFlagValue(args, "--diff");
|
|
8651
|
+
if (!diffRange) {
|
|
8652
|
+
throw new Error("--diff is required for catch-up. Example: --diff HEAD~3..HEAD or --diff main..my-branch");
|
|
8653
|
+
}
|
|
8654
|
+
let diffStat = "";
|
|
8655
|
+
try {
|
|
8656
|
+
diffStat = (0, import_node_child_process13.execFileSync)("git", ["diff", "--stat", diffRange], {
|
|
8657
|
+
cwd: root,
|
|
8658
|
+
encoding: "utf-8",
|
|
8659
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
8660
|
+
}).trim();
|
|
8661
|
+
} catch {
|
|
8662
|
+
warn(`Could not get diff stat for range ${diffRange}.`);
|
|
8663
|
+
}
|
|
8664
|
+
let slug = await generateSlug(description, root);
|
|
8665
|
+
let attempt = 0;
|
|
8666
|
+
while (getExperimentBySlug(db, slug + (attempt ? `-${attempt}` : ""))) {
|
|
8667
|
+
attempt++;
|
|
8668
|
+
}
|
|
8669
|
+
if (attempt > 0) slug = `${slug}-${attempt}`;
|
|
8670
|
+
const allExps = db.prepare("SELECT COUNT(*) as count FROM experiments").get();
|
|
8671
|
+
const num = allExps.count + 1;
|
|
8672
|
+
const exp = createExperiment(db, slug, "catch-up", description, subType, null, null, null);
|
|
8673
|
+
db.prepare("UPDATE experiments SET provenance = ? WHERE id = ?").run("catch-up", exp.id);
|
|
8674
|
+
let journalSection = "";
|
|
8675
|
+
const session2 = getActiveSession(db);
|
|
8676
|
+
if (session2) {
|
|
8677
|
+
const entries = getJournalBySession(db, session2.id);
|
|
8678
|
+
if (entries.length > 0) {
|
|
8679
|
+
journalSection = entries.map((e) => `- [${e.created_at}] ${e.content}`).join("\n");
|
|
8680
|
+
}
|
|
8681
|
+
}
|
|
8682
|
+
const docContent = [
|
|
8683
|
+
`# ${description}`,
|
|
8684
|
+
"",
|
|
8685
|
+
`- Branch: catch-up (retroactive)`,
|
|
8686
|
+
`- Status: built (catch-up)`,
|
|
8687
|
+
`- Sub-type: ${subType ?? "unclassified"}`,
|
|
8688
|
+
`- Date: ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}`,
|
|
8689
|
+
`- Provenance: catch-up`,
|
|
8690
|
+
"",
|
|
8691
|
+
"## Hypothesis",
|
|
8692
|
+
description,
|
|
8693
|
+
"",
|
|
8694
|
+
"## Approach",
|
|
8695
|
+
`Implemented manually (catch-up). Changes captured from \`${diffRange}\`.`,
|
|
8696
|
+
"",
|
|
8697
|
+
"## Files Changed",
|
|
8698
|
+
diffStat || "(no diff stat available)",
|
|
8699
|
+
"",
|
|
8700
|
+
"## Journal",
|
|
8701
|
+
journalSection || "(no journal entries)",
|
|
8702
|
+
""
|
|
8703
|
+
].join("\n");
|
|
8704
|
+
const docRelPath = `docs/experiments/${String(exp.id).padStart(3, "0")}-${slug}.md`;
|
|
8705
|
+
const docFullPath = path23.join(root, docRelPath);
|
|
8706
|
+
const docsDir = path23.dirname(docFullPath);
|
|
8707
|
+
if (!fs22.existsSync(docsDir)) {
|
|
8708
|
+
fs22.mkdirSync(docsDir, { recursive: true });
|
|
8709
|
+
}
|
|
8710
|
+
fs22.writeFileSync(docFullPath, docContent);
|
|
8711
|
+
autoCommit(root, `catch-up: ${slug}`);
|
|
8712
|
+
const config = loadConfig(root);
|
|
8713
|
+
if (config.metrics?.command) {
|
|
8714
|
+
try {
|
|
8715
|
+
const output = (0, import_node_child_process13.execSync)(config.metrics.command, {
|
|
8716
|
+
cwd: root,
|
|
8717
|
+
encoding: "utf-8",
|
|
8718
|
+
timeout: 6e4,
|
|
8719
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
8720
|
+
}).trim();
|
|
8721
|
+
const parsed = parseMetricsOutput(output);
|
|
8722
|
+
for (const m of parsed) {
|
|
8723
|
+
insertMetric(db, exp.id, "after", m.fixture, m.metric_name, m.metric_value);
|
|
8724
|
+
}
|
|
8725
|
+
if (parsed.length > 0) info(`Captured ${parsed.length} metric(s).`);
|
|
8726
|
+
} catch {
|
|
8727
|
+
warn("Could not capture metrics.");
|
|
8728
|
+
}
|
|
8729
|
+
}
|
|
8730
|
+
adminTransitionAndPersist(db, exp.id, "classified" /* CLASSIFIED */, "built" /* BUILT */, "bootstrap");
|
|
8731
|
+
success(`Catch-up experiment created: ${slug} (now at 'built')`);
|
|
8732
|
+
info(`Run \`majlis doubt\` when ready to evaluate.`);
|
|
8733
|
+
}
|
|
8734
|
+
var fs22, path23, import_node_child_process13;
|
|
8735
|
+
var init_catchup = __esm({
|
|
8736
|
+
"src/commands/catchup.ts"() {
|
|
8737
|
+
"use strict";
|
|
8738
|
+
fs22 = __toESM(require("fs"));
|
|
8739
|
+
path23 = __toESM(require("path"));
|
|
8740
|
+
import_node_child_process13 = require("child_process");
|
|
8741
|
+
init_connection();
|
|
8742
|
+
init_queries();
|
|
8743
|
+
init_machine();
|
|
8744
|
+
init_types2();
|
|
8745
|
+
init_config();
|
|
8746
|
+
init_spawn();
|
|
8747
|
+
init_metrics();
|
|
8748
|
+
init_git();
|
|
8749
|
+
init_format();
|
|
8750
|
+
}
|
|
8751
|
+
});
|
|
8752
|
+
|
|
8196
8753
|
// src/cli.ts
|
|
8197
|
-
var
|
|
8198
|
-
var
|
|
8754
|
+
var fs23 = __toESM(require("fs"));
|
|
8755
|
+
var path24 = __toESM(require("path"));
|
|
8199
8756
|
init_format();
|
|
8200
8757
|
var VERSION2 = JSON.parse(
|
|
8201
|
-
|
|
8758
|
+
fs23.readFileSync(path24.join(__dirname, "..", "package.json"), "utf-8")
|
|
8202
8759
|
).version;
|
|
8203
8760
|
async function main() {
|
|
8204
8761
|
let sigintCount = 0;
|
|
@@ -8339,6 +8896,26 @@ async function main() {
|
|
|
8339
8896
|
await resync2(rest);
|
|
8340
8897
|
break;
|
|
8341
8898
|
}
|
|
8899
|
+
case "note": {
|
|
8900
|
+
const { note: note2 } = await Promise.resolve().then(() => (init_note(), note_exports));
|
|
8901
|
+
await note2(rest);
|
|
8902
|
+
break;
|
|
8903
|
+
}
|
|
8904
|
+
case "journal": {
|
|
8905
|
+
const { journal: journal2 } = await Promise.resolve().then(() => (init_journal(), journal_exports));
|
|
8906
|
+
await journal2(rest);
|
|
8907
|
+
break;
|
|
8908
|
+
}
|
|
8909
|
+
case "brief": {
|
|
8910
|
+
const { brief: brief2 } = await Promise.resolve().then(() => (init_brief(), brief_exports));
|
|
8911
|
+
await brief2(rest, isJson);
|
|
8912
|
+
break;
|
|
8913
|
+
}
|
|
8914
|
+
case "catch-up": {
|
|
8915
|
+
const { catchUp: catchUp2 } = await Promise.resolve().then(() => (init_catchup(), catchup_exports));
|
|
8916
|
+
await catchUp2(rest);
|
|
8917
|
+
break;
|
|
8918
|
+
}
|
|
8342
8919
|
default:
|
|
8343
8920
|
console.error(`Unknown command: ${command}`);
|
|
8344
8921
|
printHelp();
|
|
@@ -8368,10 +8945,15 @@ Experiments:
|
|
|
8368
8945
|
--sub-type TYPE Classify by problem sub-type
|
|
8369
8946
|
--depends-on SLUG Block building until dependency is merged
|
|
8370
8947
|
--context FILE,FILE Inject domain-specific docs into agent context
|
|
8948
|
+
--skip-gate Skip gatekeeper (pilot-verified hypothesis)
|
|
8949
|
+
--from-file FILE Inject structured hypothesis from markdown file
|
|
8371
8950
|
baseline Capture metrics snapshot (before)
|
|
8372
8951
|
measure Capture metrics snapshot (after)
|
|
8373
8952
|
compare [--json] Compare before/after, detect regressions
|
|
8374
8953
|
revert Revert experiment, log to dead-end
|
|
8954
|
+
catch-up "description" Create experiment retroactively from manual work
|
|
8955
|
+
--diff RANGE Git diff range (required, e.g. HEAD~3..HEAD)
|
|
8956
|
+
--sub-type TYPE Classify by problem sub-type
|
|
8375
8957
|
|
|
8376
8958
|
Cycle:
|
|
8377
8959
|
next [experiment] [--auto] Determine and execute next cycle step
|
|
@@ -8404,6 +8986,13 @@ Sessions:
|
|
|
8404
8986
|
session start "intent" Declare session intent
|
|
8405
8987
|
session end Log accomplished/unfinished/fragility
|
|
8406
8988
|
|
|
8989
|
+
Pilot:
|
|
8990
|
+
note "text" Save an observation to the DB
|
|
8991
|
+
--tag TAG Tag the note (hypothesis, code-pointer, etc.)
|
|
8992
|
+
--experiment SLUG Attach to a specific experiment
|
|
8993
|
+
journal "text" Timestamped breadcrumb during manual hacking
|
|
8994
|
+
brief [--plain] [--short] Pilot-friendly context dump for Claude Code
|
|
8995
|
+
|
|
8407
8996
|
Orchestration:
|
|
8408
8997
|
run "goal" Autonomous orchestration until goal met
|
|
8409
8998
|
swarm "goal" [--parallel N] Run N experiments in parallel worktrees
|