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.
- package/README.md +39 -0
- package/dist/index.js +958 -0
- 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
|
+
}
|