create-majlis 0.1.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.
Files changed (3) hide show
  1. package/README.md +39 -0
  2. package/dist/index.js +958 -0
  3. package/package.json +39 -0
package/README.md ADDED
@@ -0,0 +1,39 @@
1
+ # create-majlis
2
+
3
+ Scaffold the [Majlis Framework](../../README.md) into a project.
4
+
5
+ ## Usage
6
+
7
+ ### New project
8
+
9
+ ```bash
10
+ npx create-majlis my-project
11
+ ```
12
+
13
+ ### Existing project
14
+
15
+ ```bash
16
+ npx create-majlis --init
17
+ ```
18
+
19
+ ### Options
20
+
21
+ ```
22
+ npx create-majlis [directory] [options]
23
+
24
+ Options:
25
+ --init Add Majlis to an existing project (don't create directory)
26
+ --yes, -y Accept defaults (skip prompts)
27
+ --no-hooks Skip hooks configuration
28
+ --minimal Core roles only (builder, critic, verifier, compressor)
29
+ ```
30
+
31
+ ## What it creates
32
+
33
+ - `.majlis/` — Config, agent definitions, SQLite database
34
+ - `.claude/` — Agents, slash commands, hooks for Claude Code
35
+ - `docs/` — Templates for experiments, doubts, challenges, verification, synthesis
36
+
37
+ ## License
38
+
39
+ MIT
package/dist/index.js ADDED
@@ -0,0 +1,958 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
18
+ // If the importer is in node compatibility mode or this is not an ESM
19
+ // file that has been converted to a CommonJS file using a Babel-
20
+ // compatible transform (i.e. "__esModule" has not been set), then set
21
+ // "default" to the CommonJS "module.exports" for node compatibility.
22
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
23
+ mod
24
+ ));
25
+
26
+ // src/prompts.ts
27
+ var readline = __toESM(require("readline"));
28
+ var DEFAULTS = {
29
+ name: "",
30
+ description: "",
31
+ objective: "",
32
+ metricsCommand: `echo '{"fixtures":{}}'`,
33
+ buildPre: "",
34
+ buildPost: ""
35
+ };
36
+ function ask(rl, question, defaultVal) {
37
+ const suffix = defaultVal ? ` (${defaultVal})` : "";
38
+ return new Promise((resolve2) => {
39
+ rl.question(`${question}${suffix}: `, (answer) => {
40
+ resolve2(answer.trim() || defaultVal);
41
+ });
42
+ });
43
+ }
44
+ async function runPrompts(projectName) {
45
+ const rl = readline.createInterface({
46
+ input: process.stdin,
47
+ output: process.stdout
48
+ });
49
+ try {
50
+ console.log("\n\x1B[1mMajlis Framework \u2014 Project Setup\x1B[0m\n");
51
+ const name = projectName || await ask(rl, "Project name", DEFAULTS.name);
52
+ const description = await ask(rl, "Description", DEFAULTS.description);
53
+ const objective = await ask(rl, "Primary objective (what are you trying to solve?)", DEFAULTS.objective);
54
+ const metricsCommand = await ask(rl, "Metrics command (JSON output)", DEFAULTS.metricsCommand);
55
+ const buildPre = await ask(rl, "Pre-measure command (e.g., npm run build)", DEFAULTS.buildPre);
56
+ const buildPost = await ask(rl, "Post-measure command (optional)", DEFAULTS.buildPost);
57
+ return { name, description, objective, metricsCommand, buildPre, buildPost };
58
+ } finally {
59
+ rl.close();
60
+ }
61
+ }
62
+ function defaultAnswers(projectName) {
63
+ return { ...DEFAULTS, name: projectName };
64
+ }
65
+
66
+ // src/scaffold.ts
67
+ var fs = __toESM(require("fs"));
68
+ var path = __toESM(require("path"));
69
+ var import_node_child_process = require("child_process");
70
+ function configTemplate(answers) {
71
+ return JSON.stringify({
72
+ project: {
73
+ name: answers.name,
74
+ description: answers.description,
75
+ objective: answers.objective
76
+ },
77
+ metrics: {
78
+ command: answers.metricsCommand,
79
+ fixtures: [],
80
+ tracked: {}
81
+ },
82
+ build: {
83
+ pre_measure: answers.buildPre || null,
84
+ post_measure: answers.buildPost || null
85
+ },
86
+ cycle: {
87
+ compression_interval: 5,
88
+ circuit_breaker_threshold: 3,
89
+ require_doubt_before_verify: true,
90
+ require_challenge_before_verify: false,
91
+ auto_baseline_on_new_experiment: true
92
+ },
93
+ models: {
94
+ builder: "opus",
95
+ critic: "sonnet",
96
+ adversary: "sonnet",
97
+ verifier: "sonnet",
98
+ reframer: "opus",
99
+ compressor: "opus"
100
+ }
101
+ }, null, 2);
102
+ }
103
+ var AGENTS = {
104
+ builder: `---
105
+ name: builder
106
+ model: opus
107
+ tools: [Read, Write, Edit, Bash, Glob, Grep]
108
+ ---
109
+ You are the Builder. You write code, run experiments, and make technical decisions.
110
+
111
+ Before building:
112
+ 1. Read docs/synthesis/current.md for project state
113
+ 2. Read the dead-ends provided in your context \u2014 these are structural constraints
114
+ 3. Check docs/classification/ for problem taxonomy
115
+ 4. Check docs/experiments/ for prior work
116
+
117
+ During building:
118
+ - Create branch: exp/NNN-description
119
+ - Create experiment log from template
120
+ - Tag EVERY decision: proof / test / strong-consensus / consensus / analogy / judgment
121
+ - When making judgment-level decisions, state: "This is judgment \u2014 reasoning without precedent"
122
+ - Run baseline metrics BEFORE making changes
123
+ - Run comparison metrics AFTER making changes
124
+
125
+ You may NOT verify your own work or mark your own decisions as proven.
126
+ Output your decisions in structured format so they can be recorded in the database.
127
+
128
+ ## Structured Output Format
129
+ At the end of your work, include a <!-- majlis-json --> block with your decisions:
130
+ \`\`\`
131
+ <!-- majlis-json
132
+ {
133
+ "decisions": [
134
+ { "description": "...", "evidence_level": "judgment|test|proof|analogy|consensus|strong_consensus", "justification": "..." }
135
+ ]
136
+ }
137
+ -->
138
+ \`\`\``,
139
+ critic: `---
140
+ name: critic
141
+ model: sonnet
142
+ tools: [Read, Glob, Grep]
143
+ ---
144
+ You are the Critic. You practise constructive doubt.
145
+
146
+ You receive the builder's OUTPUT only \u2014 never its reasoning chain.
147
+ Read the experiment log, related prior experiments, classification, and synthesis.
148
+
149
+ For each doubt:
150
+ - What specific claim, decision, or assumption you doubt
151
+ - WHY: reference a prior experiment, inconsistency, untested case, or false analogy
152
+ - Evidence level of the doubted decision
153
+ - Severity: minor / moderate / critical
154
+
155
+ Rules:
156
+ - Every doubt MUST reference evidence. "This feels wrong" is not a doubt.
157
+ - You may NOT suggest fixes. Identify problems only.
158
+ - Focus on judgment and analogy-level decisions first.
159
+ - You may NOT modify any files. Produce a doubt document only.
160
+
161
+ Write to docs/doubts/NNN-against-experiment-NNN.md
162
+
163
+ ## Structured Output Format
164
+ <!-- majlis-json
165
+ {
166
+ "doubts": [
167
+ { "claim_doubted": "...", "evidence_level_of_claim": "judgment", "evidence_for_doubt": "...", "severity": "critical|moderate|minor" }
168
+ ]
169
+ }
170
+ -->`,
171
+ adversary: `---
172
+ name: adversary
173
+ model: sonnet
174
+ tools: [Read, Glob, Grep]
175
+ ---
176
+ You are the Adversary. You do NOT review code for bugs.
177
+ You reason about problem structure to CONSTRUCT pathological cases.
178
+
179
+ For each approach the builder takes, ask:
180
+ - What input would make this fail?
181
+ - What boundary condition was not tested?
182
+ - What degenerate case collapses a distinction the algorithm relies on?
183
+ - What distribution shift invalidates the assumptions?
184
+ - Under what conditions do two things the builder treats as distinct become identical?
185
+
186
+ Produce constructed counterexamples with reasoning.
187
+ Do NOT suggest fixes. Do NOT modify files.
188
+
189
+ Write to docs/challenges/NNN-against-experiment-NNN.md
190
+
191
+ ## Structured Output Format
192
+ <!-- majlis-json
193
+ {
194
+ "challenges": [
195
+ { "description": "...", "reasoning": "..." }
196
+ ]
197
+ }
198
+ -->`,
199
+ verifier: `---
200
+ name: verifier
201
+ model: sonnet
202
+ tools: [Read, Glob, Grep, Bash]
203
+ ---
204
+ You are the Verifier. Perform dual verification:
205
+
206
+ PROVENANCE CHECK:
207
+ - Can every piece of code trace to an experiment or decision?
208
+ - Is the chain unbroken from requirement -> classification -> experiment -> code?
209
+ - Flag any broken chains.
210
+
211
+ CONTENT CHECK:
212
+ - Does the code do what the experiment log says?
213
+ - Do tests demonstrate the hypothesis?
214
+ - Write and run targeted tests against the critic's doubts AND the adversary's cases.
215
+
216
+ Grade each component: sound / good / weak / rejected
217
+ Grade each doubt/challenge: confirmed / dismissed (with evidence) / inconclusive
218
+
219
+ Write to docs/verification/NNN-for-experiment-NNN.md
220
+
221
+ ## Structured Output Format
222
+ <!-- majlis-json
223
+ {
224
+ "grades": [
225
+ { "component": "...", "grade": "sound|good|weak|rejected", "provenance_intact": true, "content_correct": true, "notes": "..." }
226
+ ],
227
+ "doubt_resolutions": [
228
+ { "doubt_id": 0, "resolution": "confirmed|dismissed|inconclusive" }
229
+ ]
230
+ }
231
+ -->`,
232
+ reframer: `---
233
+ name: reframer
234
+ model: opus
235
+ tools: [Read, Glob, Grep]
236
+ ---
237
+ You are the Reframer. You receive ONLY:
238
+ - The original problem statement
239
+ - The current classification document
240
+ - The synthesis and dead-end registry
241
+
242
+ You do NOT read builder code, experiments, or solutions.
243
+
244
+ Independently propose:
245
+ - How should this problem be decomposed?
246
+ - What are the natural joints?
247
+ - What analogies from other domains apply?
248
+ - What framework would a different field use?
249
+
250
+ Compare your decomposition with the existing classification.
251
+ Flag structural divergences \u2014 these are the most valuable signals.
252
+
253
+ Write to docs/reframes/NNN.md`,
254
+ compressor: `---
255
+ name: compressor
256
+ model: opus
257
+ tools: [Read, Write, Edit, Glob, Grep]
258
+ ---
259
+ You are the Compressor. Hold the entire project in view and compress it.
260
+
261
+ 1. Read ALL experiments, decisions, doubts, challenges, verification reports,
262
+ reframes, and recent diffs.
263
+ 2. Cross-reference: same question in different language? contradicting decisions?
264
+ workaround masking root cause?
265
+ 3. Update fragility map: thin coverage, weak components, untested judgment
266
+ decisions, broken provenance.
267
+ 4. Update dead-end registry: compress rejected experiments into structural constraints.
268
+ 5. REWRITE synthesis \u2014 shorter and denser. If it's growing, you're accumulating,
269
+ not compressing.
270
+ 6. Review classification: new sub-types? resolved sub-types?
271
+
272
+ You may NOT write code, make decisions, or run experiments.
273
+
274
+ ## Structured Output Format
275
+ <!-- majlis-json
276
+ {
277
+ "guidance": "Summary of compression findings and updated state"
278
+ }
279
+ -->`,
280
+ scout: `---
281
+ name: scout
282
+ model: sonnet
283
+ tools: [Read, Glob, Grep, WebSearch]
284
+ ---
285
+ You are the Scout. You practise rihla \u2014 travel in search of knowledge.
286
+
287
+ Your job is to search externally for alternative approaches, contradictory evidence,
288
+ and perspectives from other fields that could inform the current experiment.
289
+
290
+ For the given experiment:
291
+ 1. Describe the problem in domain-neutral terms
292
+ 2. Search for alternative approaches in other fields or frameworks
293
+ 3. Identify known limitations of the current approach from external sources
294
+ 4. Find structurally similar problems in unrelated domains
295
+ 5. Report what you find on its own terms \u2014 do not judge or filter
296
+
297
+ Rules:
298
+ - Present findings neutrally. Report each approach on its own terms.
299
+ - Note where external approaches contradict the current one \u2014 these are the most valuable signals.
300
+ - You may NOT modify code or make decisions. Produce a rihla document only.
301
+
302
+ Write to docs/rihla/NNN-scout-for-experiment-NNN.md
303
+
304
+ ## Structured Output Format
305
+ <!-- majlis-json
306
+ {
307
+ "decisions": []
308
+ }
309
+ -->`
310
+ };
311
+ var COMMANDS = {
312
+ classify: {
313
+ description: "Classify a problem domain into canonical sub-types before building",
314
+ body: `Run \`majlis classify "$ARGUMENTS"\` and follow its output.
315
+ If the CLI is not installed, act as the Builder in classification mode.
316
+ Read docs/synthesis/current.md and docs/synthesis/dead-ends.md for context.
317
+ Enumerate and classify all canonical sub-types of: $ARGUMENTS
318
+ Produce a classification document following docs/classification/_TEMPLATE.md.`
319
+ },
320
+ doubt: {
321
+ description: "Run a constructive doubt pass on an experiment",
322
+ body: `Run \`majlis doubt $ARGUMENTS\` to spawn the critic agent.
323
+ If the CLI is not installed, act as the Critic directly.
324
+ Doubt the experiment at $ARGUMENTS. Produce a doubt document
325
+ following docs/doubts/_TEMPLATE.md.`
326
+ },
327
+ challenge: {
328
+ description: "Construct adversarial test cases for an experiment",
329
+ body: `Run \`majlis challenge $ARGUMENTS\` to spawn the adversary agent.
330
+ If the CLI is not installed, act as the Adversary directly.
331
+ Construct pathological inputs designed to break the approach in $ARGUMENTS.
332
+ Produce a challenge document following docs/challenges/_TEMPLATE.md.`
333
+ },
334
+ verify: {
335
+ description: "Verify correctness and provenance of an experiment",
336
+ body: `Run \`majlis verify $ARGUMENTS\` to spawn the verifier agent.
337
+ If the CLI is not installed, act as the Verifier directly.
338
+ Perform dual verification (provenance + content) on $ARGUMENTS.
339
+ Produce a verification report following docs/verification/_TEMPLATE.md.`
340
+ },
341
+ reframe: {
342
+ description: "Independently reframe a problem from scratch",
343
+ body: `Run \`majlis reframe $ARGUMENTS\` to spawn the reframer agent.
344
+ If the CLI is not installed, act as the Reframer directly.
345
+ You receive ONLY the problem statement and classification \u2014 NOT builder code.
346
+ Independently decompose $ARGUMENTS and compare with existing classification.`
347
+ },
348
+ compress: {
349
+ description: "Compress project state into dense synthesis",
350
+ body: `Run \`majlis compress\` to spawn the compressor agent.
351
+ If the CLI is not installed, act as the Compressor directly.
352
+ Read everything. Rewrite docs/synthesis/current.md shorter and denser.
353
+ Update fragility map and dead-end registry.`
354
+ },
355
+ scout: {
356
+ description: "Search externally for alternative approaches",
357
+ body: `Run \`majlis scout $ARGUMENTS\` to spawn the scout agent.
358
+ If the CLI is not installed, search for alternative approaches to $ARGUMENTS.
359
+ Look for: limitations of current approach, alternative formulations from other fields,
360
+ structurally similar problems in unrelated domains.
361
+ Produce a rihla document at docs/rihla/.`
362
+ },
363
+ audit: {
364
+ description: "Maqasid check \u2014 is the frame right?",
365
+ body: `Run \`majlis audit "$ARGUMENTS"\` for a purpose audit.
366
+ If the CLI is not installed, review: original objective, current classification,
367
+ recent failures, dead-ends. Ask: is the classification serving the objective?
368
+ Would we decompose differently with what we now know?`
369
+ }
370
+ };
371
+ var HOOKS_CONFIG = {
372
+ hooks: {
373
+ SessionStart: [
374
+ {
375
+ hooks: [
376
+ {
377
+ type: "command",
378
+ command: "majlis status --json 2>/dev/null || true"
379
+ }
380
+ ]
381
+ }
382
+ ],
383
+ PreToolUse: [
384
+ {
385
+ matcher: "Bash",
386
+ hooks: [
387
+ {
388
+ type: "command",
389
+ command: "majlis check-commit 2>/dev/null || true",
390
+ timeout: 10
391
+ }
392
+ ]
393
+ }
394
+ ],
395
+ SubagentStop: [
396
+ {
397
+ hooks: [
398
+ {
399
+ type: "command",
400
+ command: "echo 'Subagent completed. Run majlis next to continue the cycle.'",
401
+ timeout: 5
402
+ }
403
+ ]
404
+ }
405
+ ]
406
+ }
407
+ };
408
+ function claudeMdContent(name, objective) {
409
+ return `# ${name}
410
+
411
+ ${objective ? `**Objective:** ${objective}
412
+ ` : ""}
413
+ ## Majlis Protocol
414
+
415
+ This project uses the Majlis Framework for structured multi-agent problem solving.
416
+ See \`docs/workflow.md\` for the full cycle. See \`.claude/agents/\` for role definitions (source of truth in \`.majlis/agents/\`).
417
+
418
+ ### Evidence Hierarchy (tag every decision)
419
+ 1. **Proof** \u2014 mathematical proof. Overturn requires error in proof.
420
+ 2. **Test** \u2014 empirical test. Overturn requires showing test insufficiency.
421
+ 3a. **Strong Consensus** \u2014 convergence across independent approaches.
422
+ 3b. **Consensus** \u2014 agreement from same-model experiments.
423
+ 4. **Analogy** \u2014 justified by similarity to prior work.
424
+ 5. **Judgment** \u2014 independent reasoning without precedent.
425
+
426
+ ### Session Discipline
427
+ - One intent per session. Declare it with \`majlis session start "intent"\`.
428
+ - Stray thoughts \u2192 Telegram (Scribe) or docs/inbox/.
429
+ - Every session ends with \`majlis session end\`.
430
+
431
+ ### Before Building
432
+ - Read \`docs/synthesis/current.md\` for compressed project state.
433
+ - Run \`majlis dead-ends --sub-type <relevant>\` for structural constraints.
434
+ - Run \`majlis decisions --level judgment\` for provisional decisions to challenge.
435
+
436
+ ### Compression Trigger
437
+ - Run \`majlis status\` \u2014 it will warn when compression is due.
438
+
439
+ ### Current State
440
+ Run \`majlis status\` for live experiment state and cycle position.
441
+ `;
442
+ }
443
+ var WORKFLOW_MD = `# Majlis Workflow \u2014 Quick Reference
444
+
445
+ ## The Cycle
446
+
447
+ \`\`\`
448
+ 1. CLASSIFY \u2192 Taxonomy before solution (Al-Khwarizmi)
449
+ 2. REFRAME \u2192 Independent decomposition (Al-Biruni)
450
+ 3. BUILD \u2192 Write code with tagged decisions (Ijtihad)
451
+ 4. CHALLENGE \u2192 Construct breaking inputs (Ibn al-Haytham)
452
+ 5. DOUBT \u2192 Systematic challenge with evidence (Shukuk)
453
+ 6. SCOUT \u2192 External search for alternatives (Rihla)
454
+ 7. VERIFY \u2192 Provenance + content checks (Isnad + Matn)
455
+ 8. RESOLVE \u2192 Route based on grades
456
+ 9. COMPRESS \u2192 Shorter and denser (Hifz)
457
+ \`\`\`
458
+
459
+ ## Resolution
460
+ - **Sound** \u2192 Merge
461
+ - **Good** \u2192 Merge + add gaps to fragility map
462
+ - **Weak** \u2192 Cycle back with synthesised guidance
463
+ - **Rejected** \u2192 Dead-end with structural constraint
464
+
465
+ ## Circuit Breaker
466
+ 3+ weak/rejected on same sub-type \u2192 Maqasid Check (purpose audit)
467
+
468
+ ## Evidence Hierarchy
469
+ 1. Proof \u2192 2. Test \u2192 3a. Strong Consensus \u2192 3b. Consensus \u2192 4. Analogy \u2192 5. Judgment
470
+
471
+ ## Commands
472
+ | Action | Command |
473
+ |--------|---------|
474
+ | Initialize | \`majlis init\` |
475
+ | Status | \`majlis status\` |
476
+ | New experiment | \`majlis new "hypothesis"\` |
477
+ | Baseline metrics | \`majlis baseline\` |
478
+ | Measure metrics | \`majlis measure\` |
479
+ | Compare metrics | \`majlis compare\` |
480
+ | Next step | \`majlis next\` |
481
+ | Auto cycle | \`majlis next --auto\` |
482
+ | Autonomous | \`majlis run "goal"\` |
483
+ | Session start | \`majlis session start "intent"\` |
484
+ | Session end | \`majlis session end\` |
485
+ | Compress | \`majlis compress\` |
486
+ | Audit | \`majlis audit "objective"\` |
487
+ `;
488
+ var DOC_TEMPLATES = {
489
+ "experiments/_TEMPLATE.md": `# Experiment: {{title}}
490
+
491
+ **Hypothesis:** {{hypothesis}}
492
+ **Branch:** {{branch}}
493
+ **Status:** {{status}}
494
+ **Sub-type:** {{sub_type}}
495
+ **Created:** {{date}}
496
+
497
+ ## Approach
498
+
499
+ [Describe the approach]
500
+
501
+ ## Decisions
502
+
503
+ - [evidence_level] Decision description \u2014 justification
504
+
505
+ ## Results
506
+
507
+ [Describe the results]
508
+
509
+ ## Metrics
510
+
511
+ | Fixture | Metric | Before | After | Delta |
512
+ |---------|--------|--------|-------|-------|
513
+ | | | | | |
514
+
515
+ <!-- majlis-json
516
+ {
517
+ "decisions": [],
518
+ "grades": []
519
+ }
520
+ -->
521
+ `,
522
+ "decisions/_TEMPLATE.md": `# Decision: {{title}}
523
+
524
+ **Evidence Level:** {{evidence_level}}
525
+ **Experiment:** {{experiment}}
526
+ **Date:** {{date}}
527
+
528
+ ## Description
529
+
530
+ [What was decided]
531
+
532
+ ## Justification
533
+
534
+ [Why this decision was made, referencing evidence]
535
+
536
+ ## Alternatives Considered
537
+
538
+ [What else was considered and why it was rejected]
539
+
540
+ <!-- majlis-json
541
+ {
542
+ "decisions": [
543
+ { "description": "", "evidence_level": "", "justification": "" }
544
+ ]
545
+ }
546
+ -->
547
+ `,
548
+ "classification/_TEMPLATE.md": `# Classification: {{domain}}
549
+
550
+ **Date:** {{date}}
551
+
552
+ ## Problem Domain
553
+
554
+ [Describe the problem domain]
555
+
556
+ ## Sub-Types
557
+
558
+ ### 1. {{sub_type_1}}
559
+ - **Description:**
560
+ - **Canonical form:**
561
+ - **Known constraints:**
562
+
563
+ ### 2. {{sub_type_2}}
564
+ - **Description:**
565
+ - **Canonical form:**
566
+ - **Known constraints:**
567
+
568
+ ## Relationships
569
+
570
+ [How sub-types relate to each other]
571
+ `,
572
+ "doubts/_TEMPLATE.md": `# Doubt Document \u2014 Against Experiment {{experiment}}
573
+
574
+ **Critic:** {{agent}}
575
+ **Date:** {{date}}
576
+
577
+ ## Doubt 1: {{title}}
578
+
579
+ **Claim doubted:** {{claim}}
580
+ **Evidence level of claim:** {{evidence_level}}
581
+ **Severity:** {{severity}}
582
+
583
+ **Evidence for doubt:**
584
+ [Specific evidence \u2014 a prior experiment, inconsistency, untested case, or false analogy]
585
+
586
+ <!-- majlis-json
587
+ {
588
+ "doubts": [
589
+ { "claim_doubted": "", "evidence_level_of_claim": "", "evidence_for_doubt": "", "severity": "critical" }
590
+ ]
591
+ }
592
+ -->
593
+ `,
594
+ "challenges/_TEMPLATE.md": `# Challenge Document \u2014 Against Experiment {{experiment}}
595
+
596
+ **Adversary:** {{agent}}
597
+ **Date:** {{date}}
598
+
599
+ ## Challenge 1: {{title}}
600
+
601
+ **Constructed case:**
602
+ [Specific input or condition designed to break the approach]
603
+
604
+ **Reasoning:**
605
+ [Why this case should break the approach \u2014 what assumption does it violate?]
606
+
607
+ ## Challenge 2: {{title}}
608
+
609
+ **Constructed case:**
610
+ [Specific input or condition]
611
+
612
+ **Reasoning:**
613
+ [Why this should break]
614
+
615
+ <!-- majlis-json
616
+ {
617
+ "challenges": [
618
+ { "description": "", "reasoning": "" }
619
+ ]
620
+ }
621
+ -->
622
+ `,
623
+ "verification/_TEMPLATE.md": `# Verification Report \u2014 Experiment {{experiment}}
624
+
625
+ **Verifier:** {{agent}}
626
+ **Date:** {{date}}
627
+
628
+ ## Provenance Check (Isnad)
629
+
630
+ | Component | Traceable | Chain intact | Notes |
631
+ |-----------|-----------|--------------|-------|
632
+ | | yes/no | yes/no | |
633
+
634
+ ## Content Check (Matn)
635
+
636
+ | Component | Tests pass | Consistent | Grade | Notes |
637
+ |-----------|-----------|------------|-------|-------|
638
+ | | yes/no | yes/no | sound/good/weak/rejected | |
639
+
640
+ ## Doubt Resolution
641
+
642
+ | Doubt | Resolution | Evidence |
643
+ |-------|------------|----------|
644
+ | | confirmed/dismissed/inconclusive | |
645
+
646
+ <!-- majlis-json
647
+ {
648
+ "grades": [
649
+ { "component": "", "grade": "sound", "provenance_intact": true, "content_correct": true, "notes": "" }
650
+ ],
651
+ "doubt_resolutions": [
652
+ { "doubt_id": 0, "resolution": "confirmed" }
653
+ ]
654
+ }
655
+ -->
656
+ `,
657
+ "reframes/_TEMPLATE.md": `# Reframe: {{domain}}
658
+
659
+ **Reframer:** {{agent}}
660
+ **Date:** {{date}}
661
+
662
+ ## Independent Decomposition
663
+
664
+ [How this problem should be decomposed \u2014 without seeing the builder's approach]
665
+
666
+ ## Natural Joints
667
+
668
+ [Where does this problem naturally divide?]
669
+
670
+ ## Cross-Domain Analogies
671
+
672
+ [What analogies from other domains apply?]
673
+
674
+ ## Comparison with Existing Classification
675
+
676
+ [Structural divergences from the current classification]
677
+
678
+ ## Divergences (Most Valuable Signals)
679
+
680
+ [Where the independent decomposition differs from the builder's classification]
681
+ `,
682
+ "rihla/_TEMPLATE.md": `# Rihla (Scout Report): {{topic}}
683
+
684
+ **Date:** {{date}}
685
+
686
+ ## Problem (Domain-Neutral)
687
+
688
+ [Describe the problem in domain-neutral terms]
689
+
690
+ ## Alternative Approaches Found
691
+
692
+ ### 1. {{approach}}
693
+ - **Source:**
694
+ - **Description:**
695
+ - **Applicability:**
696
+
697
+ ## Known Limitations of Current Approach
698
+
699
+ [What external sources say about where this approach fails]
700
+
701
+ ## Cross-Domain Analogues
702
+
703
+ [Structurally similar problems in unrelated domains]
704
+ `
705
+ };
706
+ function scaffold(opts) {
707
+ const { targetDir, answers, fresh, noHooks, minimal } = opts;
708
+ if (fresh) {
709
+ scaffoldFresh(targetDir, answers, noHooks, minimal);
710
+ } else {
711
+ scaffoldInit(targetDir, answers, noHooks, minimal);
712
+ }
713
+ }
714
+ function scaffoldFresh(targetDir, answers, noHooks, minimal) {
715
+ const p = path.resolve(targetDir);
716
+ if (fs.existsSync(p)) {
717
+ throw new Error(`Directory already exists: ${p}`);
718
+ }
719
+ fs.mkdirSync(p, { recursive: true });
720
+ console.log(` Created ${p}`);
721
+ (0, import_node_child_process.execSync)("git init", { cwd: p, stdio: "pipe" });
722
+ (0, import_node_child_process.execSync)('git commit --allow-empty -m "Initial commit"', { cwd: p, stdio: "pipe" });
723
+ console.log(" Initialized git repository");
724
+ const pkg = {
725
+ name: answers.name || path.basename(p),
726
+ version: "0.0.1",
727
+ description: answers.description,
728
+ private: true,
729
+ scripts: {
730
+ test: 'echo "Error: no test specified" && exit 1'
731
+ },
732
+ devDependencies: {}
733
+ };
734
+ fs.writeFileSync(path.join(p, "package.json"), JSON.stringify(pkg, null, 2));
735
+ console.log(" Created package.json");
736
+ fs.writeFileSync(path.join(p, ".gitignore"), [
737
+ "node_modules/",
738
+ "dist/",
739
+ "*.db",
740
+ ".majlis/majlis.db",
741
+ ".DS_Store",
742
+ ""
743
+ ].join("\n"));
744
+ console.log(" Created .gitignore");
745
+ scaffoldMajlisFiles(p, answers, noHooks, minimal);
746
+ try {
747
+ (0, import_node_child_process.execSync)("npm install --save-dev majlis", { cwd: p, stdio: "pipe", timeout: 6e4 });
748
+ console.log(" Installed majlis as dev dependency");
749
+ } catch {
750
+ console.log(" \x1B[33mNote: Could not install majlis package. Install manually: npm install --save-dev majlis\x1B[0m");
751
+ }
752
+ try {
753
+ (0, import_node_child_process.execSync)("npx majlis init", { cwd: p, stdio: "pipe", timeout: 3e4 });
754
+ console.log(" Ran majlis init (database created)");
755
+ } catch {
756
+ console.log(" \x1B[33mNote: Could not run majlis init. Run it manually after installing.\x1B[0m");
757
+ }
758
+ console.log(`
759
+ \x1B[32m\x1B[1mDone!\x1B[0m Project created at ${p}`);
760
+ console.log(`
761
+ cd ${targetDir}`);
762
+ console.log(" majlis status");
763
+ console.log(' majlis session start "First session"');
764
+ console.log(' majlis new "First hypothesis"\n');
765
+ }
766
+ function scaffoldInit(targetDir, answers, noHooks, minimal) {
767
+ const p = path.resolve(targetDir);
768
+ if (!fs.existsSync(p)) {
769
+ throw new Error(`Directory does not exist: ${p}`);
770
+ }
771
+ const gitDir = path.join(p, ".git");
772
+ if (!fs.existsSync(gitDir)) {
773
+ console.log(" \x1B[33mWarning: No git repository found. Initializing...\x1B[0m");
774
+ (0, import_node_child_process.execSync)("git init", { cwd: p, stdio: "pipe" });
775
+ (0, import_node_child_process.execSync)('git commit --allow-empty -m "Initial commit"', { cwd: p, stdio: "pipe" });
776
+ }
777
+ scaffoldMajlisFiles(p, answers, noHooks, minimal);
778
+ try {
779
+ (0, import_node_child_process.execSync)("npx majlis init", { cwd: p, stdio: "pipe", timeout: 3e4 });
780
+ console.log(" Ran majlis init (database created)");
781
+ } catch {
782
+ console.log(" \x1B[33mNote: Could not run majlis init. Install majlis and run it manually.\x1B[0m");
783
+ }
784
+ console.log(`
785
+ \x1B[32m\x1B[1mDone!\x1B[0m Majlis added to ${p}`);
786
+ console.log("\n majlis status");
787
+ console.log(' majlis session start "First session"\n');
788
+ }
789
+ function scaffoldMajlisFiles(projectRoot, answers, noHooks, minimal) {
790
+ const agentNames = minimal ? ["builder", "critic", "verifier", "compressor"] : ["builder", "critic", "adversary", "verifier", "reframer", "compressor", "scout"];
791
+ const majlisDir = path.join(projectRoot, ".majlis");
792
+ mkdirSafe(majlisDir);
793
+ const configPath = path.join(majlisDir, "config.json");
794
+ writeIfMissing(configPath, configTemplate(answers));
795
+ console.log(" Created .majlis/config.json");
796
+ const agentsDir = path.join(majlisDir, "agents");
797
+ mkdirSafe(agentsDir);
798
+ for (const name of agentNames) {
799
+ fs.writeFileSync(path.join(agentsDir, `${name}.md`), AGENTS[name]);
800
+ }
801
+ console.log(` Created ${agentNames.length} agent definitions in .majlis/agents/`);
802
+ const claudeAgentsDir = path.join(projectRoot, ".claude", "agents");
803
+ mkdirSafe(claudeAgentsDir);
804
+ for (const name of agentNames) {
805
+ fs.writeFileSync(path.join(claudeAgentsDir, `${name}.md`), AGENTS[name]);
806
+ }
807
+ console.log(" Copied agents to .claude/agents/");
808
+ const commandsDir = path.join(projectRoot, ".claude", "commands");
809
+ mkdirSafe(commandsDir);
810
+ for (const [name, cmd] of Object.entries(COMMANDS)) {
811
+ if (minimal && (name === "challenge" || name === "reframe")) continue;
812
+ const content = `---
813
+ description: ${cmd.description}
814
+ ---
815
+ ${cmd.body}
816
+ `;
817
+ fs.writeFileSync(path.join(commandsDir, `${name}.md`), content);
818
+ }
819
+ console.log(" Created slash commands in .claude/commands/");
820
+ if (!noHooks) {
821
+ const settingsPath = path.join(projectRoot, ".claude", "settings.json");
822
+ if (fs.existsSync(settingsPath)) {
823
+ try {
824
+ const existing = JSON.parse(fs.readFileSync(settingsPath, "utf-8"));
825
+ existing.hooks = { ...existing.hooks, ...HOOKS_CONFIG.hooks };
826
+ fs.writeFileSync(settingsPath, JSON.stringify(existing, null, 2));
827
+ } catch {
828
+ fs.writeFileSync(settingsPath, JSON.stringify(HOOKS_CONFIG, null, 2));
829
+ }
830
+ } else {
831
+ mkdirSafe(path.join(projectRoot, ".claude"));
832
+ fs.writeFileSync(settingsPath, JSON.stringify(HOOKS_CONFIG, null, 2));
833
+ }
834
+ console.log(" Created hooks in .claude/settings.json");
835
+ }
836
+ const docsDir = path.join(projectRoot, "docs");
837
+ const docDirs = [
838
+ "inbox",
839
+ "experiments",
840
+ "decisions",
841
+ "classification",
842
+ "doubts",
843
+ "challenges",
844
+ "verification",
845
+ "reframes",
846
+ "rihla",
847
+ "synthesis"
848
+ ];
849
+ for (const dir of docDirs) {
850
+ mkdirSafe(path.join(docsDir, dir));
851
+ }
852
+ for (const [relativePath, content] of Object.entries(DOC_TEMPLATES)) {
853
+ const fullPath = path.join(docsDir, relativePath);
854
+ writeIfMissing(fullPath, content);
855
+ }
856
+ console.log(" Created docs/ tree with templates");
857
+ const synthesisDir = path.join(docsDir, "synthesis");
858
+ writeIfMissing(
859
+ path.join(synthesisDir, "current.md"),
860
+ '# Project Synthesis\n\n*No experiments yet. Run `majlis new "hypothesis"` to begin.*\n'
861
+ );
862
+ writeIfMissing(
863
+ path.join(synthesisDir, "fragility.md"),
864
+ "# Fragility Map\n\n*No fragility recorded yet.*\n"
865
+ );
866
+ writeIfMissing(
867
+ path.join(synthesisDir, "dead-ends.md"),
868
+ "# Dead-End Registry\n\n*No dead-ends recorded yet.*\n"
869
+ );
870
+ writeIfMissing(path.join(docsDir, "workflow.md"), WORKFLOW_MD);
871
+ console.log(" Created docs/workflow.md");
872
+ const claudeMdPath = path.join(projectRoot, "CLAUDE.md");
873
+ if (fs.existsSync(claudeMdPath)) {
874
+ const existing = fs.readFileSync(claudeMdPath, "utf-8");
875
+ if (!existing.includes("## Majlis Protocol")) {
876
+ fs.writeFileSync(claudeMdPath, existing + "\n" + claudeMdContent(answers.name, answers.objective));
877
+ console.log(" Appended Majlis Protocol to existing CLAUDE.md");
878
+ }
879
+ } else {
880
+ fs.writeFileSync(claudeMdPath, claudeMdContent(answers.name || path.basename(projectRoot), answers.objective));
881
+ console.log(" Created CLAUDE.md");
882
+ }
883
+ }
884
+ function mkdirSafe(dir) {
885
+ if (!fs.existsSync(dir)) {
886
+ fs.mkdirSync(dir, { recursive: true });
887
+ }
888
+ }
889
+ function writeIfMissing(filePath, content) {
890
+ if (!fs.existsSync(filePath)) {
891
+ fs.writeFileSync(filePath, content);
892
+ }
893
+ }
894
+
895
+ // src/index.ts
896
+ var VERSION = "0.1.0";
897
+ async function main() {
898
+ const args = process.argv.slice(2);
899
+ const hasFlag = (flag) => args.includes(flag);
900
+ const isInit = hasFlag("--init");
901
+ const noHooks = hasFlag("--no-hooks");
902
+ const minimal = hasFlag("--minimal");
903
+ const yes = hasFlag("--yes") || hasFlag("-y");
904
+ if (hasFlag("--version") || hasFlag("-v")) {
905
+ console.log(VERSION);
906
+ process.exit(0);
907
+ }
908
+ if (hasFlag("--help") || hasFlag("-h")) {
909
+ printHelp();
910
+ process.exit(0);
911
+ }
912
+ const positional = args.filter((a) => !a.startsWith("-"));
913
+ const projectArg = positional[0];
914
+ if (isInit) {
915
+ const targetDir = projectArg || ".";
916
+ const answers = yes ? defaultAnswers(targetDir === "." ? currentDirName() : targetDir) : await runPrompts(targetDir === "." ? currentDirName() : targetDir);
917
+ console.log("\n\x1B[1mAdding Majlis to existing project...\x1B[0m\n");
918
+ scaffold({ targetDir, answers, fresh: false, noHooks, minimal });
919
+ } else if (projectArg) {
920
+ const answers = yes ? defaultAnswers(projectArg) : await runPrompts(projectArg);
921
+ console.log("\n\x1B[1mCreating new Majlis project...\x1B[0m\n");
922
+ scaffold({ targetDir: projectArg, answers, fresh: true, noHooks, minimal });
923
+ } else {
924
+ const answers = yes ? defaultAnswers(currentDirName()) : await runPrompts(currentDirName());
925
+ console.log("\n\x1B[1mAdding Majlis to current directory...\x1B[0m\n");
926
+ scaffold({ targetDir: ".", answers, fresh: false, noHooks, minimal });
927
+ }
928
+ }
929
+ function currentDirName() {
930
+ return process.cwd().split("/").pop() || "project";
931
+ }
932
+ function printHelp() {
933
+ console.log(`
934
+ \x1B[1mcreate-majlis\x1B[0m v${VERSION} \u2014 Scaffold the Majlis Framework
935
+
936
+ \x1B[1mUsage:\x1B[0m
937
+ npx create-majlis <project-name> Create a new project with Majlis
938
+ npx create-majlis --init Add Majlis to existing project
939
+ npx create-majlis Add Majlis to current directory
940
+
941
+ \x1B[1mFlags:\x1B[0m
942
+ --init Add to existing project (don't create new dir)
943
+ --yes, -y Accept defaults (non-interactive)
944
+ --no-hooks Skip Claude Code hooks setup
945
+ --minimal Only include builder, critic, verifier, compressor
946
+ --version, -v Print version
947
+ --help, -h Print help
948
+
949
+ \x1B[1mExamples:\x1B[0m
950
+ npx create-majlis my-research
951
+ npx create-majlis --init --minimal
952
+ npx create-majlis my-project --yes --no-hooks
953
+ `);
954
+ }
955
+ main().catch((err) => {
956
+ console.error(`\x1B[31mError: ${err instanceof Error ? err.message : String(err)}\x1B[0m`);
957
+ process.exit(1);
958
+ });
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "create-majlis",
3
+ "version": "0.1.0",
4
+ "description": "Scaffold the Majlis Framework into a project",
5
+ "bin": {
6
+ "create-majlis": "./dist/index.js"
7
+ },
8
+ "scripts": {
9
+ "build": "tsup src/index.ts --format cjs --clean",
10
+ "test": "echo 'No tests yet'"
11
+ },
12
+ "devDependencies": {
13
+ "@types/node": "^22.0.0",
14
+ "tsup": "^8.0.0",
15
+ "typescript": "^5.5.0"
16
+ },
17
+ "files": [
18
+ "dist"
19
+ ],
20
+ "license": "MIT",
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "https://github.com/raihaan123/majlis"
24
+ },
25
+ "keywords": [
26
+ "claude-code",
27
+ "majlis",
28
+ "scaffolder",
29
+ "multi-agent",
30
+ "create"
31
+ ],
32
+ "engines": {
33
+ "node": ">=18.0.0"
34
+ },
35
+ "homepage": "https://github.com/raihaan123/majlis#readme",
36
+ "bugs": {
37
+ "url": "https://github.com/raihaan123/majlis/issues"
38
+ }
39
+ }