majlis 0.6.0 → 0.6.1

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.
Files changed (2) hide show
  1. package/dist/cli.js +125 -51
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -1903,6 +1903,16 @@ function truncateContext(content, limit) {
1903
1903
  if (content.length <= limit) return content;
1904
1904
  return content.slice(0, limit) + "\n[TRUNCATED]";
1905
1905
  }
1906
+ function readLatestDiagnosis(projectRoot) {
1907
+ const dir = path3.join(projectRoot, "docs", "diagnosis");
1908
+ try {
1909
+ const files = fs3.readdirSync(dir).filter((f) => f.startsWith("diagnosis-") && f.endsWith(".md")).sort().reverse();
1910
+ if (files.length === 0) return "";
1911
+ return fs3.readFileSync(path3.join(dir, files[0]), "utf-8");
1912
+ } catch {
1913
+ return "";
1914
+ }
1915
+ }
1906
1916
  var fs3, path3, DEFAULT_CONFIG2, _cachedConfig, _cachedRoot, CONTEXT_LIMITS;
1907
1917
  var init_config = __esm({
1908
1918
  "src/config.ts"() {
@@ -2417,8 +2427,10 @@ ${taskPrompt}`;
2417
2427
  }
2418
2428
  return { output: markdown, structured, truncated };
2419
2429
  }
2420
- async function spawnSynthesiser(context, projectRoot) {
2430
+ async function spawnSynthesiser(context, projectRoot, opts) {
2421
2431
  const root = projectRoot ?? findProjectRoot() ?? process.cwd();
2432
+ const maxTurns = opts?.maxTurns ?? 5;
2433
+ const tools = opts?.tools ?? ["Read", "Glob", "Grep"];
2422
2434
  const contextJson = JSON.stringify(context);
2423
2435
  const taskPrompt = context.taskPrompt ?? "Synthesise the findings into actionable builder guidance.";
2424
2436
  const prompt = `Here is your context:
@@ -2429,14 +2441,14 @@ ${contextJson}
2429
2441
 
2430
2442
  ${taskPrompt}`;
2431
2443
  const systemPrompt = 'You are a Synthesis Agent. Be concrete: which decisions failed, which assumptions broke, what constraints must the next approach satisfy. CRITICAL: Your LAST line of output MUST be a <!-- majlis-json --> block. The framework parses this programmatically \u2014 if you omit it, the pipeline breaks. Format: <!-- majlis-json {"guidance": "your guidance here"} -->';
2432
- console.log(`[synthesiser] Spawning (maxTurns: 5)...`);
2444
+ console.log(`[synthesiser] Spawning (maxTurns: ${maxTurns})...`);
2433
2445
  const { text: markdown, costUsd, truncated } = await runQuery({
2434
2446
  prompt,
2435
2447
  model: "sonnet",
2436
- tools: ["Read", "Glob", "Grep"],
2448
+ tools,
2437
2449
  systemPrompt,
2438
2450
  cwd: root,
2439
- maxTurns: 5,
2451
+ maxTurns,
2440
2452
  label: "synthesiser",
2441
2453
  role: "synthesiser"
2442
2454
  });
@@ -2663,6 +2675,38 @@ var init_spawn = __esm({
2663
2675
  }
2664
2676
  });
2665
2677
 
2678
+ // src/git.ts
2679
+ function autoCommit(root, message) {
2680
+ try {
2681
+ (0, import_node_child_process.execSync)("git add docs/ .majlis/scripts/ 2>/dev/null; true", {
2682
+ cwd: root,
2683
+ encoding: "utf-8",
2684
+ stdio: ["pipe", "pipe", "pipe"]
2685
+ });
2686
+ const diff = (0, import_node_child_process.execSync)("git diff --cached --stat", {
2687
+ cwd: root,
2688
+ encoding: "utf-8",
2689
+ stdio: ["pipe", "pipe", "pipe"]
2690
+ }).trim();
2691
+ if (!diff) return;
2692
+ (0, import_node_child_process.execSync)(`git commit -m ${JSON.stringify(`[majlis] ${message}`)}`, {
2693
+ cwd: root,
2694
+ encoding: "utf-8",
2695
+ stdio: ["pipe", "pipe", "pipe"]
2696
+ });
2697
+ info(`Auto-committed: ${message}`);
2698
+ } catch {
2699
+ }
2700
+ }
2701
+ var import_node_child_process;
2702
+ var init_git = __esm({
2703
+ "src/git.ts"() {
2704
+ "use strict";
2705
+ import_node_child_process = require("child_process");
2706
+ init_format();
2707
+ }
2708
+ });
2709
+
2666
2710
  // src/metrics.ts
2667
2711
  function compareMetrics(db, experimentId, config) {
2668
2712
  const before = getMetricsByExperimentAndPhase(db, experimentId, "before");
@@ -2752,7 +2796,7 @@ async function captureMetrics(phase, args) {
2752
2796
  if (config.build.pre_measure) {
2753
2797
  info(`Running pre-measure: ${config.build.pre_measure}`);
2754
2798
  try {
2755
- (0, import_node_child_process.execSync)(config.build.pre_measure, { cwd: root, encoding: "utf-8", stdio: "inherit" });
2799
+ (0, import_node_child_process2.execSync)(config.build.pre_measure, { cwd: root, encoding: "utf-8", stdio: "inherit" });
2756
2800
  } catch {
2757
2801
  warn("Pre-measure command failed \u2014 continuing anyway.");
2758
2802
  }
@@ -2763,7 +2807,7 @@ async function captureMetrics(phase, args) {
2763
2807
  info(`Running metrics: ${config.metrics.command}`);
2764
2808
  let metricsOutput;
2765
2809
  try {
2766
- metricsOutput = (0, import_node_child_process.execSync)(config.metrics.command, {
2810
+ metricsOutput = (0, import_node_child_process2.execSync)(config.metrics.command, {
2767
2811
  cwd: root,
2768
2812
  encoding: "utf-8",
2769
2813
  stdio: ["pipe", "pipe", "pipe"]
@@ -2782,7 +2826,7 @@ async function captureMetrics(phase, args) {
2782
2826
  success(`Captured ${parsed.length} metric(s) for ${exp.slug} (phase: ${phase})`);
2783
2827
  if (config.build.post_measure) {
2784
2828
  try {
2785
- (0, import_node_child_process.execSync)(config.build.post_measure, { cwd: root, encoding: "utf-8", stdio: "inherit" });
2829
+ (0, import_node_child_process2.execSync)(config.build.post_measure, { cwd: root, encoding: "utf-8", stdio: "inherit" });
2786
2830
  } catch {
2787
2831
  warn("Post-measure command failed.");
2788
2832
  }
@@ -2833,11 +2877,11 @@ function formatDelta(delta) {
2833
2877
  const prefix = delta > 0 ? "+" : "";
2834
2878
  return `${prefix}${delta.toFixed(4)}`;
2835
2879
  }
2836
- var import_node_child_process;
2880
+ var import_node_child_process2;
2837
2881
  var init_measure = __esm({
2838
2882
  "src/commands/measure.ts"() {
2839
2883
  "use strict";
2840
- import_node_child_process = require("child_process");
2884
+ import_node_child_process2 = require("child_process");
2841
2885
  init_connection();
2842
2886
  init_queries();
2843
2887
  init_metrics();
@@ -2870,7 +2914,7 @@ async function newExperiment(args) {
2870
2914
  const paddedNum = String(num).padStart(3, "0");
2871
2915
  const branch = `exp/${paddedNum}-${slug}`;
2872
2916
  try {
2873
- (0, import_node_child_process2.execSync)(`git checkout -b ${branch}`, {
2917
+ (0, import_node_child_process3.execSync)(`git checkout -b ${branch}`, {
2874
2918
  cwd: root,
2875
2919
  encoding: "utf-8",
2876
2920
  stdio: ["pipe", "pipe", "pipe"]
@@ -2891,6 +2935,7 @@ async function newExperiment(args) {
2891
2935
  fs5.writeFileSync(logPath, logContent);
2892
2936
  info(`Created experiment log: docs/experiments/${paddedNum}-${slug}.md`);
2893
2937
  }
2938
+ autoCommit(root, `new: ${slug}`);
2894
2939
  if (config.cycle.auto_baseline_on_new_experiment && config.metrics.command) {
2895
2940
  info("Auto-baselining... (run `majlis baseline` to do this manually)");
2896
2941
  try {
@@ -2927,12 +2972,12 @@ async function revert(args) {
2927
2972
  );
2928
2973
  updateExperimentStatus(db, exp.id, "dead_end");
2929
2974
  try {
2930
- const currentBranch = (0, import_node_child_process2.execSync)("git rev-parse --abbrev-ref HEAD", {
2975
+ const currentBranch = (0, import_node_child_process3.execSync)("git rev-parse --abbrev-ref HEAD", {
2931
2976
  cwd: root,
2932
2977
  encoding: "utf-8"
2933
2978
  }).trim();
2934
2979
  if (currentBranch === exp.branch) {
2935
- (0, import_node_child_process2.execSync)("git checkout main 2>/dev/null || git checkout master", {
2980
+ (0, import_node_child_process3.execSync)("git checkout main 2>/dev/null || git checkout master", {
2936
2981
  cwd: root,
2937
2982
  encoding: "utf-8",
2938
2983
  stdio: ["pipe", "pipe", "pipe"]
@@ -2943,17 +2988,18 @@ async function revert(args) {
2943
2988
  }
2944
2989
  info(`Experiment ${exp.slug} reverted to dead-end. Reason: ${reason}`);
2945
2990
  }
2946
- var fs5, path5, import_node_child_process2;
2991
+ var fs5, path5, import_node_child_process3;
2947
2992
  var init_experiment = __esm({
2948
2993
  "src/commands/experiment.ts"() {
2949
2994
  "use strict";
2950
2995
  fs5 = __toESM(require("fs"));
2951
2996
  path5 = __toESM(require("path"));
2952
- import_node_child_process2 = require("child_process");
2997
+ import_node_child_process3 = require("child_process");
2953
2998
  init_connection();
2954
2999
  init_queries();
2955
3000
  init_config();
2956
3001
  init_spawn();
3002
+ init_git();
2957
3003
  init_format();
2958
3004
  }
2959
3005
  });
@@ -3301,6 +3347,7 @@ async function resolve(db, exp, projectRoot) {
3301
3347
  gitMerge(exp.branch, projectRoot);
3302
3348
  const gaps = grades.filter((g) => g.grade === "good").map((g) => `- **${g.component}**: ${g.notes ?? "minor gaps"}`).join("\n");
3303
3349
  appendToFragilityMap(projectRoot, exp.slug, gaps);
3350
+ autoCommit(projectRoot, `resolve: fragility gaps from ${exp.slug}`);
3304
3351
  updateExperimentStatus(db, exp.id, "merged");
3305
3352
  success(`Experiment ${exp.slug} MERGED (good, ${grades.filter((g) => g.grade === "good").length} gaps added to fragility map).`);
3306
3353
  break;
@@ -3435,7 +3482,12 @@ async function resolveDbOnly(db, exp, projectRoot) {
3435
3482
  }
3436
3483
  function gitMerge(branch, cwd) {
3437
3484
  try {
3438
- (0, import_node_child_process3.execSync)(`git merge ${branch} --no-ff -m "Merge experiment branch ${branch}"`, {
3485
+ (0, import_node_child_process4.execSync)("git checkout main 2>/dev/null || git checkout master", {
3486
+ cwd,
3487
+ encoding: "utf-8",
3488
+ stdio: ["pipe", "pipe", "pipe"]
3489
+ });
3490
+ (0, import_node_child_process4.execSync)(`git merge ${branch} --no-ff -m "Merge experiment branch ${branch}"`, {
3439
3491
  cwd,
3440
3492
  encoding: "utf-8",
3441
3493
  stdio: ["pipe", "pipe", "pipe"]
@@ -3446,16 +3498,16 @@ function gitMerge(branch, cwd) {
3446
3498
  }
3447
3499
  function gitRevert(branch, cwd) {
3448
3500
  try {
3449
- const currentBranch = (0, import_node_child_process3.execSync)("git rev-parse --abbrev-ref HEAD", {
3501
+ const currentBranch = (0, import_node_child_process4.execSync)("git rev-parse --abbrev-ref HEAD", {
3450
3502
  cwd,
3451
3503
  encoding: "utf-8"
3452
3504
  }).trim();
3453
3505
  if (currentBranch === branch) {
3454
3506
  try {
3455
- (0, import_node_child_process3.execSync)("git checkout -- .", { cwd, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
3507
+ (0, import_node_child_process4.execSync)("git checkout -- .", { cwd, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
3456
3508
  } catch {
3457
3509
  }
3458
- (0, import_node_child_process3.execSync)("git checkout main 2>/dev/null || git checkout master", {
3510
+ (0, import_node_child_process4.execSync)("git checkout main 2>/dev/null || git checkout master", {
3459
3511
  cwd,
3460
3512
  encoding: "utf-8",
3461
3513
  stdio: ["pipe", "pipe", "pipe"]
@@ -3477,7 +3529,7 @@ ${gaps}
3477
3529
  `;
3478
3530
  fs7.writeFileSync(fragPath, content + entry);
3479
3531
  }
3480
- var fs7, path7, import_node_child_process3;
3532
+ var fs7, path7, import_node_child_process4;
3481
3533
  var init_resolve = __esm({
3482
3534
  "src/resolve.ts"() {
3483
3535
  "use strict";
@@ -3486,7 +3538,8 @@ var init_resolve = __esm({
3486
3538
  init_types2();
3487
3539
  init_queries();
3488
3540
  init_spawn();
3489
- import_node_child_process3 = require("child_process");
3541
+ import_node_child_process4 = require("child_process");
3542
+ init_git();
3490
3543
  init_format();
3491
3544
  }
3492
3545
  });
@@ -3605,7 +3658,7 @@ async function doBuild(db, exp, root) {
3605
3658
  const existingBaseline = getMetricsByExperimentAndPhase(db, exp.id, "before");
3606
3659
  if (config.metrics?.command && existingBaseline.length === 0) {
3607
3660
  try {
3608
- const output = (0, import_node_child_process4.execSync)(config.metrics.command, {
3661
+ const output = (0, import_node_child_process5.execSync)(config.metrics.command, {
3609
3662
  cwd: root,
3610
3663
  encoding: "utf-8",
3611
3664
  timeout: 6e4,
@@ -3662,7 +3715,7 @@ Build the experiment: ${exp.hypothesis}` : `Build the experiment: ${exp.hypothes
3662
3715
  } else {
3663
3716
  if (config.metrics?.command) {
3664
3717
  try {
3665
- const output = (0, import_node_child_process4.execSync)(config.metrics.command, {
3718
+ const output = (0, import_node_child_process5.execSync)(config.metrics.command, {
3666
3719
  cwd: root,
3667
3720
  encoding: "utf-8",
3668
3721
  timeout: 6e4,
@@ -3686,7 +3739,7 @@ async function doChallenge(db, exp, root) {
3686
3739
  transition(exp.status, "challenged" /* CHALLENGED */);
3687
3740
  let gitDiff = "";
3688
3741
  try {
3689
- gitDiff = (0, import_node_child_process4.execSync)('git diff main -- . ":!.majlis/"', {
3742
+ gitDiff = (0, import_node_child_process5.execSync)('git diff main -- . ":!.majlis/"', {
3690
3743
  cwd: root,
3691
3744
  encoding: "utf-8",
3692
3745
  stdio: ["pipe", "pipe", "pipe"]
@@ -3898,12 +3951,13 @@ async function doCompress(db, root) {
3898
3951
  }, root);
3899
3952
  const sizeAfter = fs8.existsSync(synthesisPath) ? fs8.statSync(synthesisPath).size : 0;
3900
3953
  recordCompression(db, sessionCount, sizeBefore, sizeAfter);
3954
+ autoCommit(root, "compress: update synthesis");
3901
3955
  success(`Compression complete. Synthesis: ${sizeBefore}B \u2192 ${sizeAfter}B`);
3902
3956
  }
3903
3957
  function gitCommitBuild(exp, cwd) {
3904
3958
  try {
3905
- (0, import_node_child_process4.execSync)('git add -A -- ":!.majlis/"', { cwd, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
3906
- const diff = (0, import_node_child_process4.execSync)("git diff --cached --stat", { cwd, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
3959
+ (0, import_node_child_process5.execSync)('git add -A -- ":!.majlis/"', { cwd, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
3960
+ const diff = (0, import_node_child_process5.execSync)("git diff --cached --stat", { cwd, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
3907
3961
  if (!diff) {
3908
3962
  info("No code changes to commit.");
3909
3963
  return;
@@ -3911,7 +3965,7 @@ function gitCommitBuild(exp, cwd) {
3911
3965
  const msg = `EXP-${String(exp.id).padStart(3, "0")}: ${exp.slug}
3912
3966
 
3913
3967
  ${exp.hypothesis ?? ""}`;
3914
- (0, import_node_child_process4.execSync)(`git commit -m ${JSON.stringify(msg)}`, { cwd, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
3968
+ (0, import_node_child_process5.execSync)(`git commit -m ${JSON.stringify(msg)}`, { cwd, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
3915
3969
  info(`Committed builder changes on ${exp.branch}.`);
3916
3970
  } catch {
3917
3971
  warn("Could not auto-commit builder changes \u2014 commit manually before resolving.");
@@ -3987,13 +4041,13 @@ function ingestStructuredOutput(db, experimentId, structured) {
3987
4041
  info(`Ingested ${structured.findings.length} finding(s)`);
3988
4042
  }
3989
4043
  }
3990
- var fs8, path8, import_node_child_process4;
4044
+ var fs8, path8, import_node_child_process5;
3991
4045
  var init_cycle = __esm({
3992
4046
  "src/commands/cycle.ts"() {
3993
4047
  "use strict";
3994
4048
  fs8 = __toESM(require("fs"));
3995
4049
  path8 = __toESM(require("path"));
3996
- import_node_child_process4 = require("child_process");
4050
+ import_node_child_process5 = require("child_process");
3997
4051
  init_connection();
3998
4052
  init_queries();
3999
4053
  init_machine();
@@ -4002,6 +4056,7 @@ var init_cycle = __esm({
4002
4056
  init_resolve();
4003
4057
  init_config();
4004
4058
  init_metrics();
4059
+ init_git();
4005
4060
  init_format();
4006
4061
  }
4007
4062
  });
@@ -4035,6 +4090,7 @@ ${deadEnds}
4035
4090
 
4036
4091
  Write the classification to docs/classification/ following the template.`
4037
4092
  }, root);
4093
+ autoCommit(root, `classify: ${domain.slice(0, 60)}`);
4038
4094
  success("Classification complete. Check docs/classification/ for the output.");
4039
4095
  }
4040
4096
  async function reframe(args) {
@@ -4077,6 +4133,7 @@ ${deadEnds}
4077
4133
  Independently propose a decomposition. Compare with the existing classification. Flag structural divergences \u2014 these are the most valuable signals.
4078
4134
  Write to docs/reframes/.`
4079
4135
  }, root);
4136
+ autoCommit(root, `reframe: ${target.slice(0, 60)}`);
4080
4137
  success("Reframe complete. Check docs/reframes/ for the output.");
4081
4138
  }
4082
4139
  var fs9, path9;
@@ -4087,6 +4144,7 @@ var init_classify = __esm({
4087
4144
  path9 = __toESM(require("path"));
4088
4145
  init_connection();
4089
4146
  init_spawn();
4147
+ init_git();
4090
4148
  init_format();
4091
4149
  }
4092
4150
  });
@@ -4398,6 +4456,7 @@ async function run(args) {
4398
4456
  usedHypotheses.add(hypothesis);
4399
4457
  info(`Next hypothesis: ${hypothesis}`);
4400
4458
  exp = await createNewExperiment(db, root, hypothesis);
4459
+ autoCommit(root, `new: ${exp.slug}`);
4401
4460
  success(`Created experiment #${exp.id}: ${exp.slug}`);
4402
4461
  }
4403
4462
  if (isTerminal(exp.status)) {
@@ -4449,12 +4508,13 @@ async function deriveNextHypothesis(goal, root, db) {
4449
4508
  const synthesis = truncateContext(readFileOrEmpty(path11.join(root, "docs", "synthesis", "current.md")), CONTEXT_LIMITS.synthesis);
4450
4509
  const fragility = truncateContext(readFileOrEmpty(path11.join(root, "docs", "synthesis", "fragility.md")), CONTEXT_LIMITS.fragility);
4451
4510
  const deadEndsDoc = truncateContext(readFileOrEmpty(path11.join(root, "docs", "synthesis", "dead-ends.md")), CONTEXT_LIMITS.deadEnds);
4511
+ const diagnosis = truncateContext(readLatestDiagnosis(root), CONTEXT_LIMITS.synthesis);
4452
4512
  const deadEnds = listAllDeadEnds(db);
4453
4513
  const config = loadConfig(root);
4454
4514
  let metricsOutput = "";
4455
4515
  if (config.metrics?.command) {
4456
4516
  try {
4457
- metricsOutput = (0, import_node_child_process5.execSync)(config.metrics.command, {
4517
+ metricsOutput = (0, import_node_child_process6.execSync)(config.metrics.command, {
4458
4518
  cwd: root,
4459
4519
  encoding: "utf-8",
4460
4520
  timeout: 6e4,
@@ -4469,7 +4529,10 @@ async function deriveNextHypothesis(goal, root, db) {
4469
4529
 
4470
4530
  ## Goal
4471
4531
  ${goal}
4472
-
4532
+ ${diagnosis ? `
4533
+ ## Latest Diagnosis Report (PRIORITISE \u2014 deep analysis from diagnostician agent)
4534
+ ${diagnosis}
4535
+ ` : ""}
4473
4536
  ## Current Metrics
4474
4537
  ${metricsOutput || "(no metrics configured)"}
4475
4538
 
@@ -4489,6 +4552,8 @@ Note: [structural] dead ends are HARD CONSTRAINTS \u2014 your hypothesis MUST NO
4489
4552
  [procedural] dead ends are process failures \u2014 the approach may still be valid if executed differently.
4490
4553
 
4491
4554
  ## Your Task
4555
+ DO NOT read source code or use tools. All context you need is above. Plan from the synthesis and dead-end registry.
4556
+
4492
4557
  1. Assess: based on the metrics and synthesis, has the goal been met? Be specific.
4493
4558
  2. If YES \u2014 output the JSON block below with goal_met: true.
4494
4559
  3. If NO \u2014 propose the SINGLE most promising next experiment hypothesis.
@@ -4504,7 +4569,7 @@ CRITICAL: Your LAST line of output MUST be EXACTLY this format (on its own line,
4504
4569
 
4505
4570
  If the goal is met:
4506
4571
  <!-- majlis-json {"goal_met": true, "hypothesis": null} -->`
4507
- }, root);
4572
+ }, root, { maxTurns: 2, tools: [] });
4508
4573
  const structured = result.structured;
4509
4574
  if (structured?.goal_met === true) {
4510
4575
  return null;
@@ -4530,7 +4595,7 @@ If the goal is met:
4530
4595
  ${result.output.slice(-2e3)}
4531
4596
 
4532
4597
  <!-- majlis-json {"goal_met": false, "hypothesis": "your hypothesis"} -->`
4533
- }, root);
4598
+ }, root, { maxTurns: 1, tools: [] });
4534
4599
  if (retry.structured?.hypothesis) return retry.structured.hypothesis;
4535
4600
  warn("Could not extract hypothesis. Using goal as fallback.");
4536
4601
  return goal;
@@ -4548,7 +4613,7 @@ async function createNewExperiment(db, root, hypothesis) {
4548
4613
  const paddedNum = String(num).padStart(3, "0");
4549
4614
  const branch = `exp/${paddedNum}-${finalSlug}`;
4550
4615
  try {
4551
- (0, import_node_child_process5.execSync)(`git checkout -b ${branch}`, {
4616
+ (0, import_node_child_process6.execSync)(`git checkout -b ${branch}`, {
4552
4617
  cwd: root,
4553
4618
  encoding: "utf-8",
4554
4619
  stdio: ["pipe", "pipe", "pipe"]
@@ -4571,13 +4636,13 @@ async function createNewExperiment(db, root, hypothesis) {
4571
4636
  }
4572
4637
  return exp;
4573
4638
  }
4574
- var fs11, path11, import_node_child_process5;
4639
+ var fs11, path11, import_node_child_process6;
4575
4640
  var init_run = __esm({
4576
4641
  "src/commands/run.ts"() {
4577
4642
  "use strict";
4578
4643
  fs11 = __toESM(require("fs"));
4579
4644
  path11 = __toESM(require("path"));
4580
- import_node_child_process5 = require("child_process");
4645
+ import_node_child_process6 = require("child_process");
4581
4646
  init_connection();
4582
4647
  init_queries();
4583
4648
  init_machine();
@@ -4586,6 +4651,7 @@ var init_run = __esm({
4586
4651
  init_spawn();
4587
4652
  init_config();
4588
4653
  init_shutdown();
4654
+ init_git();
4589
4655
  init_format();
4590
4656
  }
4591
4657
  });
@@ -4596,7 +4662,7 @@ function createWorktree(mainRoot, slug, paddedNum) {
4596
4662
  const worktreeName = `${projectName}-swarm-${paddedNum}-${slug}`;
4597
4663
  const worktreePath = path12.join(path12.dirname(mainRoot), worktreeName);
4598
4664
  const branch = `swarm/${paddedNum}-${slug}`;
4599
- (0, import_node_child_process6.execSync)(`git worktree add ${JSON.stringify(worktreePath)} -b ${branch}`, {
4665
+ (0, import_node_child_process7.execSync)(`git worktree add ${JSON.stringify(worktreePath)} -b ${branch}`, {
4600
4666
  cwd: mainRoot,
4601
4667
  encoding: "utf-8",
4602
4668
  stdio: ["pipe", "pipe", "pipe"]
@@ -4647,7 +4713,7 @@ function initializeWorktree(mainRoot, worktreePath) {
4647
4713
  }
4648
4714
  function cleanupWorktree(mainRoot, wt) {
4649
4715
  try {
4650
- (0, import_node_child_process6.execSync)(`git worktree remove ${JSON.stringify(wt.path)} --force`, {
4716
+ (0, import_node_child_process7.execSync)(`git worktree remove ${JSON.stringify(wt.path)} --force`, {
4651
4717
  cwd: mainRoot,
4652
4718
  encoding: "utf-8",
4653
4719
  stdio: ["pipe", "pipe", "pipe"]
@@ -4656,7 +4722,7 @@ function cleanupWorktree(mainRoot, wt) {
4656
4722
  warn(`Could not remove worktree ${wt.path} \u2014 remove manually.`);
4657
4723
  }
4658
4724
  try {
4659
- (0, import_node_child_process6.execSync)(`git branch -D ${wt.branch}`, {
4725
+ (0, import_node_child_process7.execSync)(`git branch -D ${wt.branch}`, {
4660
4726
  cwd: mainRoot,
4661
4727
  encoding: "utf-8",
4662
4728
  stdio: ["pipe", "pipe", "pipe"]
@@ -4664,7 +4730,7 @@ function cleanupWorktree(mainRoot, wt) {
4664
4730
  } catch {
4665
4731
  }
4666
4732
  try {
4667
- (0, import_node_child_process6.execSync)("git worktree prune", {
4733
+ (0, import_node_child_process7.execSync)("git worktree prune", {
4668
4734
  cwd: mainRoot,
4669
4735
  encoding: "utf-8",
4670
4736
  stdio: ["pipe", "pipe", "pipe"]
@@ -4672,13 +4738,13 @@ function cleanupWorktree(mainRoot, wt) {
4672
4738
  } catch {
4673
4739
  }
4674
4740
  }
4675
- var fs12, path12, import_node_child_process6;
4741
+ var fs12, path12, import_node_child_process7;
4676
4742
  var init_worktree = __esm({
4677
4743
  "src/swarm/worktree.ts"() {
4678
4744
  "use strict";
4679
4745
  fs12 = __toESM(require("fs"));
4680
4746
  path12 = __toESM(require("path"));
4681
- import_node_child_process6 = require("child_process");
4747
+ import_node_child_process7 = require("child_process");
4682
4748
  init_connection();
4683
4749
  init_format();
4684
4750
  }
@@ -4983,7 +5049,7 @@ async function swarm(args) {
4983
5049
  MAX_PARALLEL
4984
5050
  );
4985
5051
  try {
4986
- const status2 = (0, import_node_child_process7.execSync)("git status --porcelain", {
5052
+ const status2 = (0, import_node_child_process8.execSync)("git status --porcelain", {
4987
5053
  cwd: root,
4988
5054
  encoding: "utf-8",
4989
5055
  stdio: ["pipe", "pipe", "pipe"]
@@ -5067,7 +5133,7 @@ async function swarm(args) {
5067
5133
  const best = summary.bestExperiment;
5068
5134
  info(`Best experiment: ${best.worktree.slug} (${best.overallGrade})`);
5069
5135
  try {
5070
- (0, import_node_child_process7.execSync)(
5136
+ (0, import_node_child_process8.execSync)(
5071
5137
  `git merge ${best.worktree.branch} --no-ff -m "Merge swarm winner: ${best.worktree.slug}"`,
5072
5138
  { cwd: root, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
5073
5139
  );
@@ -5126,13 +5192,14 @@ async function deriveMultipleHypotheses(goal, root, count) {
5126
5192
  readFileOrEmpty(path14.join(root, "docs", "synthesis", "dead-ends.md")),
5127
5193
  CONTEXT_LIMITS.deadEnds
5128
5194
  );
5195
+ const diagnosis = truncateContext(readLatestDiagnosis(root), CONTEXT_LIMITS.synthesis);
5129
5196
  const db = getDb(root);
5130
5197
  const deadEnds = listAllDeadEnds(db);
5131
5198
  const config = loadConfig(root);
5132
5199
  let metricsOutput = "";
5133
5200
  if (config.metrics?.command) {
5134
5201
  try {
5135
- metricsOutput = (0, import_node_child_process7.execSync)(config.metrics.command, {
5202
+ metricsOutput = (0, import_node_child_process8.execSync)(config.metrics.command, {
5136
5203
  cwd: root,
5137
5204
  encoding: "utf-8",
5138
5205
  timeout: 6e4,
@@ -5147,7 +5214,10 @@ async function deriveMultipleHypotheses(goal, root, count) {
5147
5214
 
5148
5215
  ## Goal
5149
5216
  ${goal}
5150
-
5217
+ ${diagnosis ? `
5218
+ ## Latest Diagnosis Report (PRIORITISE \u2014 deep analysis from diagnostician agent)
5219
+ ${diagnosis}
5220
+ ` : ""}
5151
5221
  ## Current Metrics
5152
5222
  ${metricsOutput || "(no metrics configured)"}
5153
5223
 
@@ -5167,6 +5237,8 @@ Note: [structural] dead ends are HARD CONSTRAINTS \u2014 hypotheses MUST NOT rep
5167
5237
  [procedural] dead ends are process failures \u2014 the approach may still be valid if executed differently.
5168
5238
 
5169
5239
  ## Your Task
5240
+ DO NOT read source code or use tools. All context you need is above. Plan from the synthesis and dead-end registry.
5241
+
5170
5242
  1. Assess: based on the metrics and synthesis, has the goal been met? Be specific.
5171
5243
  2. If YES \u2014 output the JSON block below with goal_met: true.
5172
5244
  3. If NO \u2014 generate exactly ${count} DIVERSE hypotheses for parallel testing.
@@ -5184,7 +5256,7 @@ CRITICAL: Your LAST line of output MUST be EXACTLY this format (on its own line,
5184
5256
 
5185
5257
  If the goal is met:
5186
5258
  <!-- majlis-json {"goal_met": true, "hypotheses": []} -->`
5187
- }, root);
5259
+ }, root, { maxTurns: 2, tools: [] });
5188
5260
  if (result.structured?.goal_met === true) return [];
5189
5261
  if (result.structured?.hypotheses && Array.isArray(result.structured.hypotheses)) {
5190
5262
  return result.structured.hypotheses.filter(
@@ -5207,12 +5279,12 @@ If the goal is met:
5207
5279
  warn("Planner did not return structured hypotheses. Using goal as single hypothesis.");
5208
5280
  return [goal];
5209
5281
  }
5210
- var path14, import_node_child_process7, MAX_PARALLEL, DEFAULT_PARALLEL;
5282
+ var path14, import_node_child_process8, MAX_PARALLEL, DEFAULT_PARALLEL;
5211
5283
  var init_swarm = __esm({
5212
5284
  "src/commands/swarm.ts"() {
5213
5285
  "use strict";
5214
5286
  path14 = __toESM(require("path"));
5215
- import_node_child_process7 = require("child_process");
5287
+ import_node_child_process8 = require("child_process");
5216
5288
  init_connection();
5217
5289
  init_queries();
5218
5290
  init_spawn();
@@ -5251,7 +5323,7 @@ async function diagnose(args) {
5251
5323
  let metricsOutput = "";
5252
5324
  if (config.metrics?.command) {
5253
5325
  try {
5254
- metricsOutput = (0, import_node_child_process8.execSync)(config.metrics.command, {
5326
+ metricsOutput = (0, import_node_child_process9.execSync)(config.metrics.command, {
5255
5327
  cwd: root,
5256
5328
  encoding: "utf-8",
5257
5329
  timeout: 6e4,
@@ -5330,19 +5402,21 @@ Remember: you may write files ONLY to .majlis/scripts/. You cannot modify projec
5330
5402
  if (result.truncated) {
5331
5403
  warn("Diagnostician was truncated (hit 60 turn limit).");
5332
5404
  }
5405
+ autoCommit(root, `diagnosis: ${focus || "general"}`);
5333
5406
  success("Diagnosis complete.");
5334
5407
  }
5335
- var fs14, path15, import_node_child_process8;
5408
+ var fs14, path15, import_node_child_process9;
5336
5409
  var init_diagnose = __esm({
5337
5410
  "src/commands/diagnose.ts"() {
5338
5411
  "use strict";
5339
5412
  fs14 = __toESM(require("fs"));
5340
5413
  path15 = __toESM(require("path"));
5341
- import_node_child_process8 = require("child_process");
5414
+ import_node_child_process9 = require("child_process");
5342
5415
  init_connection();
5343
5416
  init_queries();
5344
5417
  init_spawn();
5345
5418
  init_config();
5419
+ init_git();
5346
5420
  init_format();
5347
5421
  }
5348
5422
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "majlis",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "description": "Multi-agent workflow CLI for structured doubt, independent verification, and compressed knowledge",
5
5
  "bin": {
6
6
  "majlis": "./dist/cli.js"