majlis 0.1.0 → 0.2.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/README.md +1 -1
- package/dist/cli.js +113 -129
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Multi-agent workflow CLI for structured doubt, independent verification, and compressed knowledge.
|
|
4
4
|
|
|
5
|
-
This is the CLI package. For full documentation, see the [root README](
|
|
5
|
+
This is the CLI package. For full documentation, see the [root README](https://github.com/raihaan123/majlis#readme).
|
|
6
6
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
package/dist/cli.js
CHANGED
|
@@ -501,9 +501,9 @@ var init_init = __esm({
|
|
|
501
501
|
},
|
|
502
502
|
models: {
|
|
503
503
|
builder: "opus",
|
|
504
|
-
critic: "
|
|
505
|
-
adversary: "
|
|
506
|
-
verifier: "
|
|
504
|
+
critic: "opus",
|
|
505
|
+
adversary: "opus",
|
|
506
|
+
verifier: "opus",
|
|
507
507
|
reframer: "opus",
|
|
508
508
|
compressor: "opus"
|
|
509
509
|
}
|
|
@@ -546,7 +546,7 @@ At the end of your work, include a <!-- majlis-json --> block with your decision
|
|
|
546
546
|
\`\`\``,
|
|
547
547
|
critic: `---
|
|
548
548
|
name: critic
|
|
549
|
-
model:
|
|
549
|
+
model: opus
|
|
550
550
|
tools: [Read, Glob, Grep]
|
|
551
551
|
---
|
|
552
552
|
You are the Critic. You practise constructive doubt.
|
|
@@ -578,7 +578,7 @@ Write to docs/doubts/NNN-against-experiment-NNN.md
|
|
|
578
578
|
-->`,
|
|
579
579
|
adversary: `---
|
|
580
580
|
name: adversary
|
|
581
|
-
model:
|
|
581
|
+
model: opus
|
|
582
582
|
tools: [Read, Glob, Grep]
|
|
583
583
|
---
|
|
584
584
|
You are the Adversary. You do NOT review code for bugs.
|
|
@@ -606,7 +606,7 @@ Write to docs/challenges/NNN-against-experiment-NNN.md
|
|
|
606
606
|
-->`,
|
|
607
607
|
verifier: `---
|
|
608
608
|
name: verifier
|
|
609
|
-
model:
|
|
609
|
+
model: opus
|
|
610
610
|
tools: [Read, Glob, Grep, Bash]
|
|
611
611
|
---
|
|
612
612
|
You are the Verifier. Perform dual verification:
|
|
@@ -687,7 +687,7 @@ You may NOT write code, make decisions, or run experiments.
|
|
|
687
687
|
-->`,
|
|
688
688
|
scout: `---
|
|
689
689
|
name: scout
|
|
690
|
-
model:
|
|
690
|
+
model: opus
|
|
691
691
|
tools: [Read, Glob, Grep, WebSearch]
|
|
692
692
|
---
|
|
693
693
|
You are the Scout. You practise rihla \u2014 travel in search of knowledge.
|
|
@@ -2094,7 +2094,7 @@ var init_types2 = __esm({
|
|
|
2094
2094
|
});
|
|
2095
2095
|
|
|
2096
2096
|
// src/agents/parse.ts
|
|
2097
|
-
function extractStructuredData(role, markdown) {
|
|
2097
|
+
async function extractStructuredData(role, markdown) {
|
|
2098
2098
|
const tier1 = extractMajlisJsonBlock(markdown);
|
|
2099
2099
|
if (tier1) {
|
|
2100
2100
|
const parsed = tryParseJson(tier1);
|
|
@@ -2109,7 +2109,7 @@ function extractStructuredData(role, markdown) {
|
|
|
2109
2109
|
return tier2;
|
|
2110
2110
|
}
|
|
2111
2111
|
console.warn(`[majlis] Regex fallback insufficient for ${role}. Using Haiku extraction.`);
|
|
2112
|
-
const tier3 = extractViaHaiku(role, markdown);
|
|
2112
|
+
const tier3 = await extractViaHaiku(role, markdown);
|
|
2113
2113
|
if (tier3) return tier3;
|
|
2114
2114
|
console.error(
|
|
2115
2115
|
`[majlis] FAILED to extract structured data from ${role} output. State machine will continue but data is missing. Manual review required.`
|
|
@@ -2185,22 +2185,36 @@ function extractViaPatterns(role, markdown) {
|
|
|
2185
2185
|
if (doubts.length > 0) result.doubts = doubts;
|
|
2186
2186
|
return result;
|
|
2187
2187
|
}
|
|
2188
|
-
function extractViaHaiku(role, markdown) {
|
|
2188
|
+
async function extractViaHaiku(role, markdown) {
|
|
2189
2189
|
try {
|
|
2190
2190
|
const truncated = markdown.length > 8e3 ? markdown.slice(0, 8e3) + "\n[truncated]" : markdown;
|
|
2191
2191
|
const prompt = `Extract all decisions, evidence levels, grades, doubts, and guidance from this ${role} document as JSON. Follow this schema exactly: ${EXTRACTION_SCHEMA}
|
|
2192
2192
|
|
|
2193
2193
|
Document:
|
|
2194
2194
|
${truncated}`;
|
|
2195
|
-
const
|
|
2196
|
-
|
|
2197
|
-
{
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2195
|
+
const conversation = (0, import_claude_agent_sdk.query)({
|
|
2196
|
+
prompt,
|
|
2197
|
+
options: {
|
|
2198
|
+
model: "haiku",
|
|
2199
|
+
tools: [],
|
|
2200
|
+
systemPrompt: "You are a JSON extraction assistant. Output only valid JSON matching the requested schema. No markdown, no explanation, just JSON.",
|
|
2201
|
+
permissionMode: "bypassPermissions",
|
|
2202
|
+
allowDangerouslySkipPermissions: true,
|
|
2203
|
+
maxTurns: 1,
|
|
2204
|
+
persistSession: false
|
|
2201
2205
|
}
|
|
2202
|
-
);
|
|
2203
|
-
|
|
2206
|
+
});
|
|
2207
|
+
let resultText = "";
|
|
2208
|
+
for await (const message of conversation) {
|
|
2209
|
+
if (message.type === "assistant") {
|
|
2210
|
+
for (const block of message.message.content) {
|
|
2211
|
+
if (block.type === "text") {
|
|
2212
|
+
resultText += block.text;
|
|
2213
|
+
}
|
|
2214
|
+
}
|
|
2215
|
+
}
|
|
2216
|
+
}
|
|
2217
|
+
return tryParseJson(resultText.trim());
|
|
2204
2218
|
} catch (err) {
|
|
2205
2219
|
console.warn(`[majlis] Haiku extraction failed for ${role}: ${err instanceof Error ? err.message : String(err)}`);
|
|
2206
2220
|
return null;
|
|
@@ -2209,12 +2223,12 @@ ${truncated}`;
|
|
|
2209
2223
|
function hasData(output) {
|
|
2210
2224
|
return !!(output.decisions && output.decisions.length > 0 || output.grades && output.grades.length > 0 || output.doubts && output.doubts.length > 0 || output.guidance);
|
|
2211
2225
|
}
|
|
2212
|
-
var
|
|
2226
|
+
var import_claude_agent_sdk;
|
|
2213
2227
|
var init_parse = __esm({
|
|
2214
2228
|
"src/agents/parse.ts"() {
|
|
2215
2229
|
"use strict";
|
|
2216
2230
|
init_types2();
|
|
2217
|
-
|
|
2231
|
+
import_claude_agent_sdk = require("@anthropic-ai/claude-agent-sdk");
|
|
2218
2232
|
}
|
|
2219
2233
|
});
|
|
2220
2234
|
|
|
@@ -2233,7 +2247,7 @@ function loadAgentDefinition(role, projectRoot) {
|
|
|
2233
2247
|
const frontmatter = frontmatterMatch[1];
|
|
2234
2248
|
const body = frontmatterMatch[2].trim();
|
|
2235
2249
|
const name = extractYamlField(frontmatter, "name") ?? role;
|
|
2236
|
-
const model = extractYamlField(frontmatter, "model") ?? "
|
|
2250
|
+
const model = extractYamlField(frontmatter, "model") ?? "opus";
|
|
2237
2251
|
const toolsStr = extractYamlField(frontmatter, "tools") ?? "[]";
|
|
2238
2252
|
const tools = toolsStr.replace(/[\[\]]/g, "").split(",").map((t) => t.trim()).filter(Boolean);
|
|
2239
2253
|
return { name, model, tools, systemPrompt: body };
|
|
@@ -2242,125 +2256,96 @@ function extractYamlField(yaml, field) {
|
|
|
2242
2256
|
const match = yaml.match(new RegExp(`^${field}:\\s*(.+)$`, "m"));
|
|
2243
2257
|
return match ? match[1].trim() : null;
|
|
2244
2258
|
}
|
|
2245
|
-
function writeTempContext(context) {
|
|
2246
|
-
const tmpDir = os.tmpdir();
|
|
2247
|
-
const tmpFile = path7.join(tmpDir, `majlis-context-${Date.now()}.json`);
|
|
2248
|
-
fs7.writeFileSync(tmpFile, JSON.stringify(context, null, 2));
|
|
2249
|
-
return tmpFile;
|
|
2250
|
-
}
|
|
2251
2259
|
async function spawnAgent(role, context, projectRoot) {
|
|
2252
2260
|
const agentDef = loadAgentDefinition(role, projectRoot);
|
|
2253
|
-
const contextFile = writeTempContext(context);
|
|
2254
2261
|
const root = projectRoot ?? findProjectRoot() ?? process.cwd();
|
|
2255
2262
|
const taskPrompt = context.taskPrompt ?? `Perform your role as ${agentDef.name}.`;
|
|
2256
|
-
const
|
|
2257
|
-
const
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
...agentDef.tools,
|
|
2265
|
-
"--append-system-prompt",
|
|
2266
|
-
agentDef.systemPrompt,
|
|
2267
|
-
"-p",
|
|
2268
|
-
prompt
|
|
2269
|
-
];
|
|
2263
|
+
const contextJson = JSON.stringify(context, null, 2);
|
|
2264
|
+
const prompt = `Here is your context:
|
|
2265
|
+
|
|
2266
|
+
\`\`\`json
|
|
2267
|
+
${contextJson}
|
|
2268
|
+
\`\`\`
|
|
2269
|
+
|
|
2270
|
+
${taskPrompt}`;
|
|
2270
2271
|
console.log(`[majlis] Spawning ${role} agent (model: ${agentDef.model})...`);
|
|
2271
|
-
const
|
|
2272
|
-
|
|
2272
|
+
const { text: markdown, costUsd } = await runQuery({
|
|
2273
|
+
prompt,
|
|
2274
|
+
model: agentDef.model,
|
|
2275
|
+
tools: agentDef.tools,
|
|
2276
|
+
systemPrompt: agentDef.systemPrompt,
|
|
2277
|
+
cwd: root
|
|
2278
|
+
});
|
|
2279
|
+
console.log(`[majlis] ${role} agent complete (cost: $${costUsd.toFixed(4)})`);
|
|
2273
2280
|
const artifactPath = writeArtifact(role, context, markdown, root);
|
|
2274
2281
|
if (artifactPath) {
|
|
2275
2282
|
console.log(`[majlis] ${role} artifact written to ${artifactPath}`);
|
|
2276
2283
|
}
|
|
2277
|
-
const structured = extractStructuredData(role, markdown);
|
|
2278
|
-
try {
|
|
2279
|
-
fs7.unlinkSync(contextFile);
|
|
2280
|
-
} catch {
|
|
2281
|
-
}
|
|
2284
|
+
const structured = await extractStructuredData(role, markdown);
|
|
2282
2285
|
return { output: markdown, structured };
|
|
2283
2286
|
}
|
|
2284
2287
|
async function spawnSynthesiser(context, projectRoot) {
|
|
2285
|
-
const contextFile = writeTempContext(context);
|
|
2286
2288
|
const root = projectRoot ?? findProjectRoot() ?? process.cwd();
|
|
2287
|
-
const
|
|
2288
|
-
const
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
"Grep",
|
|
2298
|
-
"--append-system-prompt",
|
|
2299
|
-
"You are a Synthesis Agent. Your job is to take a verification report, confirmed doubts, and adversarial test results, and compress them into specific, actionable guidance for the builder's next attempt. Be concrete: which decisions failed, which assumptions broke, what constraints must the next approach satisfy. Output a 'guidance' field in JSON wrapped in a <!-- majlis-json --> block.",
|
|
2300
|
-
"-p",
|
|
2301
|
-
prompt
|
|
2302
|
-
];
|
|
2289
|
+
const contextJson = JSON.stringify(context, null, 2);
|
|
2290
|
+
const taskPrompt = context.taskPrompt ?? "Synthesise the findings into actionable builder guidance.";
|
|
2291
|
+
const prompt = `Here is your context:
|
|
2292
|
+
|
|
2293
|
+
\`\`\`json
|
|
2294
|
+
${contextJson}
|
|
2295
|
+
\`\`\`
|
|
2296
|
+
|
|
2297
|
+
${taskPrompt}`;
|
|
2298
|
+
const systemPrompt = "You are a Synthesis Agent. Your job is to take a verification report, confirmed doubts, and adversarial test results, and compress them into specific, actionable guidance for the builder's next attempt. Be concrete: which decisions failed, which assumptions broke, what constraints must the next approach satisfy. Output a 'guidance' field in JSON wrapped in a <!-- majlis-json --> block.";
|
|
2303
2299
|
console.log(`[majlis] Spawning synthesiser micro-agent...`);
|
|
2304
|
-
const
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
}
|
|
2300
|
+
const { text: markdown, costUsd } = await runQuery({
|
|
2301
|
+
prompt,
|
|
2302
|
+
model: "opus",
|
|
2303
|
+
tools: ["Read", "Glob", "Grep"],
|
|
2304
|
+
systemPrompt,
|
|
2305
|
+
cwd: root
|
|
2306
|
+
});
|
|
2307
|
+
console.log(`[majlis] Synthesiser complete (cost: $${costUsd.toFixed(4)})`);
|
|
2308
|
+
const structured = await extractStructuredData("synthesiser", markdown);
|
|
2311
2309
|
return { output: markdown, structured };
|
|
2312
2310
|
}
|
|
2313
|
-
function
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
} else {
|
|
2332
|
-
resolve2(stdout);
|
|
2333
|
-
}
|
|
2334
|
-
});
|
|
2335
|
-
proc.on("error", (err) => {
|
|
2336
|
-
reject(new Error(`Failed to spawn claude: ${err.message}`));
|
|
2337
|
-
});
|
|
2311
|
+
async function runQuery(opts) {
|
|
2312
|
+
const conversation = (0, import_claude_agent_sdk2.query)({
|
|
2313
|
+
prompt: opts.prompt,
|
|
2314
|
+
options: {
|
|
2315
|
+
model: opts.model,
|
|
2316
|
+
tools: opts.tools,
|
|
2317
|
+
systemPrompt: {
|
|
2318
|
+
type: "preset",
|
|
2319
|
+
preset: "claude_code",
|
|
2320
|
+
append: opts.systemPrompt
|
|
2321
|
+
},
|
|
2322
|
+
cwd: opts.cwd,
|
|
2323
|
+
permissionMode: "bypassPermissions",
|
|
2324
|
+
allowDangerouslySkipPermissions: true,
|
|
2325
|
+
maxTurns: 50,
|
|
2326
|
+
persistSession: false,
|
|
2327
|
+
settingSources: ["project"]
|
|
2328
|
+
}
|
|
2338
2329
|
});
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
const
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
if (event.type === "assistant" && event.message?.content) {
|
|
2347
|
-
for (const block of event.message.content) {
|
|
2348
|
-
if (block.type === "text") {
|
|
2349
|
-
parts.push(block.text);
|
|
2350
|
-
}
|
|
2351
|
-
}
|
|
2352
|
-
} else if (event.type === "content_block_delta" && event.delta?.text) {
|
|
2353
|
-
parts.push(event.delta.text);
|
|
2354
|
-
} else if (event.type === "result" && event.result) {
|
|
2355
|
-
if (typeof event.result === "string") {
|
|
2356
|
-
parts.push(event.result);
|
|
2330
|
+
const textParts = [];
|
|
2331
|
+
let costUsd = 0;
|
|
2332
|
+
for await (const message of conversation) {
|
|
2333
|
+
if (message.type === "assistant") {
|
|
2334
|
+
for (const block of message.message.content) {
|
|
2335
|
+
if (block.type === "text") {
|
|
2336
|
+
textParts.push(block.text);
|
|
2357
2337
|
}
|
|
2358
2338
|
}
|
|
2359
|
-
}
|
|
2360
|
-
if (
|
|
2339
|
+
} else if (message.type === "result") {
|
|
2340
|
+
if (message.subtype === "success") {
|
|
2341
|
+
costUsd = message.total_cost_usd;
|
|
2342
|
+
} else {
|
|
2343
|
+
const errors = "errors" in message ? message.errors?.join("; ") ?? "Unknown error" : "Unknown error";
|
|
2344
|
+
throw new Error(`Agent query failed (${message.subtype}): ${errors}`);
|
|
2345
|
+
}
|
|
2361
2346
|
}
|
|
2362
2347
|
}
|
|
2363
|
-
return
|
|
2348
|
+
return { text: textParts.join("\n\n"), costUsd };
|
|
2364
2349
|
}
|
|
2365
2350
|
function writeArtifact(role, context, markdown, projectRoot) {
|
|
2366
2351
|
const dirMap = {
|
|
@@ -2391,14 +2376,13 @@ function writeArtifact(role, context, markdown, projectRoot) {
|
|
|
2391
2376
|
fs7.writeFileSync(target, markdown);
|
|
2392
2377
|
return target;
|
|
2393
2378
|
}
|
|
2394
|
-
var fs7, path7,
|
|
2379
|
+
var fs7, path7, import_claude_agent_sdk2;
|
|
2395
2380
|
var init_spawn = __esm({
|
|
2396
2381
|
"src/agents/spawn.ts"() {
|
|
2397
2382
|
"use strict";
|
|
2398
2383
|
fs7 = __toESM(require("fs"));
|
|
2399
2384
|
path7 = __toESM(require("path"));
|
|
2400
|
-
|
|
2401
|
-
import_node_child_process4 = require("child_process");
|
|
2385
|
+
import_claude_agent_sdk2 = require("@anthropic-ai/claude-agent-sdk");
|
|
2402
2386
|
init_parse();
|
|
2403
2387
|
init_connection();
|
|
2404
2388
|
}
|
|
@@ -2479,7 +2463,7 @@ async function resolve(db, exp, projectRoot) {
|
|
|
2479
2463
|
}
|
|
2480
2464
|
function gitMerge(branch, cwd) {
|
|
2481
2465
|
try {
|
|
2482
|
-
(0,
|
|
2466
|
+
(0, import_node_child_process3.execSync)(`git merge ${branch} --no-ff -m "Merge experiment branch ${branch}"`, {
|
|
2483
2467
|
cwd,
|
|
2484
2468
|
encoding: "utf-8",
|
|
2485
2469
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -2490,12 +2474,12 @@ function gitMerge(branch, cwd) {
|
|
|
2490
2474
|
}
|
|
2491
2475
|
function gitRevert(branch, cwd) {
|
|
2492
2476
|
try {
|
|
2493
|
-
const currentBranch = (0,
|
|
2477
|
+
const currentBranch = (0, import_node_child_process3.execSync)("git rev-parse --abbrev-ref HEAD", {
|
|
2494
2478
|
cwd,
|
|
2495
2479
|
encoding: "utf-8"
|
|
2496
2480
|
}).trim();
|
|
2497
2481
|
if (currentBranch === branch) {
|
|
2498
|
-
(0,
|
|
2482
|
+
(0, import_node_child_process3.execSync)("git checkout main 2>/dev/null || git checkout master", {
|
|
2499
2483
|
cwd,
|
|
2500
2484
|
encoding: "utf-8",
|
|
2501
2485
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -2517,7 +2501,7 @@ ${gaps}
|
|
|
2517
2501
|
`;
|
|
2518
2502
|
fs8.writeFileSync(fragPath, content + entry);
|
|
2519
2503
|
}
|
|
2520
|
-
var fs8, path8,
|
|
2504
|
+
var fs8, path8, import_node_child_process3;
|
|
2521
2505
|
var init_resolve = __esm({
|
|
2522
2506
|
"src/resolve.ts"() {
|
|
2523
2507
|
"use strict";
|
|
@@ -2526,7 +2510,7 @@ var init_resolve = __esm({
|
|
|
2526
2510
|
init_types();
|
|
2527
2511
|
init_queries();
|
|
2528
2512
|
init_spawn();
|
|
2529
|
-
|
|
2513
|
+
import_node_child_process3 = require("child_process");
|
|
2530
2514
|
init_format();
|
|
2531
2515
|
}
|
|
2532
2516
|
});
|
|
@@ -3233,8 +3217,8 @@ async function main() {
|
|
|
3233
3217
|
case "history":
|
|
3234
3218
|
case "circuit-breakers":
|
|
3235
3219
|
case "check-commit": {
|
|
3236
|
-
const { query:
|
|
3237
|
-
await
|
|
3220
|
+
const { query: query4 } = await Promise.resolve().then(() => (init_query(), query_exports));
|
|
3221
|
+
await query4(command, rest, isJson);
|
|
3238
3222
|
break;
|
|
3239
3223
|
}
|
|
3240
3224
|
case "build":
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "majlis",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Multi-agent workflow CLI for structured doubt, independent verification, and compressed knowledge",
|
|
5
5
|
"bin": {
|
|
6
6
|
"majlis": "./dist/cli.js"
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"test": "tsx --test src/test/*.test.ts"
|
|
11
11
|
},
|
|
12
12
|
"dependencies": {
|
|
13
|
+
"@anthropic-ai/claude-agent-sdk": "^0.2.42",
|
|
13
14
|
"better-sqlite3": "^11.0.0"
|
|
14
15
|
},
|
|
15
16
|
"devDependencies": {
|