@balpal4495/quorum 1.0.0 → 3.0.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/.github/copilot-instructions.md +29 -6
- package/README.md +304 -193
- package/SETUP.md +60 -96
- package/bin/commands/compass.js +422 -0
- package/bin/commands/init.js +65 -60
- package/bin/commands/migrate-v2.js +136 -0
- package/bin/commands/sentinel.js +1 -1
- package/bin/commands/sync.js +97 -0
- package/bin/quorum.js +35 -0
- package/bin/templates/CLAUDE.md +101 -0
- package/modules/README.md +57 -10
- package/modules/compass/behavior.ts +161 -0
- package/modules/compass/create.ts +365 -0
- package/modules/compass/evidence/collect.ts +109 -0
- package/modules/compass/index.ts +7 -0
- package/modules/compass/prompts/index.ts +230 -0
- package/modules/compass/prompts/system.ts +24 -0
- package/modules/compass/propose.ts +152 -0
- package/modules/compass/schemas.ts +121 -0
- package/modules/compass/score.ts +77 -0
- package/modules/compass/sources/index.ts +413 -0
- package/modules/compass/types.ts +431 -0
- package/modules/setup.ts +33 -0
- package/package.json +21 -11
- package/bin/init.js +0 -378
package/bin/init.js
DELETED
|
@@ -1,378 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* quorum init
|
|
4
|
-
*
|
|
5
|
-
* Drops Quorum into an existing Node.js project.
|
|
6
|
-
* Run from the target project root:
|
|
7
|
-
*
|
|
8
|
-
* npx @balpal4495/quorum@latest init
|
|
9
|
-
*
|
|
10
|
-
* Zero external dependencies — uses only Node.js built-ins.
|
|
11
|
-
* Requires Node.js 18+.
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import { promises as fs } from "fs"
|
|
15
|
-
import path from "path"
|
|
16
|
-
import { fileURLToPath } from "url"
|
|
17
|
-
import { execSync } from "child_process"
|
|
18
|
-
import { createRequire } from "module"
|
|
19
|
-
|
|
20
|
-
const _require = createRequire(import.meta.url)
|
|
21
|
-
const PKG_VERSION = _require("../package.json").version
|
|
22
|
-
|
|
23
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
24
|
-
const QUORUM_ROOT = path.resolve(__dirname, "..")
|
|
25
|
-
const TARGET = process.cwd()
|
|
26
|
-
|
|
27
|
-
// ── Deps Quorum requires in the host project ───────────────────────────────
|
|
28
|
-
|
|
29
|
-
const DEPS = {
|
|
30
|
-
zod: "^3.23.0",
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const OPTIONAL_DEPS = {
|
|
34
|
-
vectordb: "^0.4.0",
|
|
35
|
-
"@xenova/transformers": "^2.17.0",
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// ── Logging ────────────────────────────────────────────────────────────────
|
|
39
|
-
|
|
40
|
-
const c = {
|
|
41
|
-
bold: (s) => `\x1b[1m${s}\x1b[0m`,
|
|
42
|
-
green: (s) => `\x1b[32m${s}\x1b[0m`,
|
|
43
|
-
blue: (s) => `\x1b[34m${s}\x1b[0m`,
|
|
44
|
-
dim: (s) => `\x1b[90m${s}\x1b[0m`,
|
|
45
|
-
yellow:(s) => `\x1b[33m${s}\x1b[0m`,
|
|
46
|
-
red: (s) => `\x1b[31m${s}\x1b[0m`,
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const log = {
|
|
50
|
-
section: (title) => console.log(`\n${c.bold(title)}`),
|
|
51
|
-
created: (file) => console.log(` ${c.green("+ created ")} ${file}`),
|
|
52
|
-
appended:(file) => console.log(` ${c.blue("~ appended")} ${file}`),
|
|
53
|
-
skipped: (file) => console.log(` ${c.dim("· skipped ")} ${file}`),
|
|
54
|
-
warn: (msg) => console.log(` ${c.yellow("⚠ " + msg)}`),
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// ── Helpers ────────────────────────────────────────────────────────────────
|
|
58
|
-
|
|
59
|
-
async function exists(p) {
|
|
60
|
-
return fs.access(p).then(() => true).catch(() => false)
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
async function readJson(p) {
|
|
64
|
-
return JSON.parse(await fs.readFile(p, "utf8"))
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function geminiAvailable() {
|
|
68
|
-
try {
|
|
69
|
-
execSync("which gemini", { stdio: "ignore" })
|
|
70
|
-
return true
|
|
71
|
-
} catch {
|
|
72
|
-
return false
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// ── Steps ──────────────────────────────────────────────────────────────────
|
|
77
|
-
|
|
78
|
-
async function guardAlreadyInitialized() {
|
|
79
|
-
if (await exists(path.join(TARGET, "quorum", "modules"))) {
|
|
80
|
-
console.log(c.yellow("\nQuorum is already initialized in this project."))
|
|
81
|
-
console.log("Remove quorum/ first if you want to reinitialize.\n")
|
|
82
|
-
process.exit(0)
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
async function copyModules() {
|
|
87
|
-
log.section("Copying modules")
|
|
88
|
-
|
|
89
|
-
const src = path.join(QUORUM_ROOT, "modules")
|
|
90
|
-
const dest = path.join(TARGET, "quorum", "modules")
|
|
91
|
-
await fs.cp(src, dest, {
|
|
92
|
-
recursive: true,
|
|
93
|
-
filter: (src) =>
|
|
94
|
-
!src.includes("__tests__") &&
|
|
95
|
-
!src.includes(".test.ts") &&
|
|
96
|
-
!src.includes(".spec.ts"),
|
|
97
|
-
})
|
|
98
|
-
log.created("quorum/modules/")
|
|
99
|
-
|
|
100
|
-
await fs.copyFile(
|
|
101
|
-
path.join(QUORUM_ROOT, "SETUP.md"),
|
|
102
|
-
path.join(TARGET, "quorum", "SETUP.md"),
|
|
103
|
-
)
|
|
104
|
-
log.created("quorum/SETUP.md")
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
async function copyEvals() {
|
|
108
|
-
const src = path.join(QUORUM_ROOT, "evals")
|
|
109
|
-
const dest = path.join(TARGET, "quorum", "evals")
|
|
110
|
-
await fs.cp(src, dest, { recursive: true })
|
|
111
|
-
log.created("quorum/evals/")
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
async function mergeCopilotInstructions() {
|
|
115
|
-
log.section("Merging AI instruction files")
|
|
116
|
-
|
|
117
|
-
const src = path.join(QUORUM_ROOT, ".github", "copilot-instructions.md")
|
|
118
|
-
const dest = path.join(TARGET, ".github", "copilot-instructions.md")
|
|
119
|
-
const content = await fs.readFile(src, "utf8")
|
|
120
|
-
|
|
121
|
-
await fs.mkdir(path.join(TARGET, ".github"), { recursive: true })
|
|
122
|
-
|
|
123
|
-
if (await exists(dest)) {
|
|
124
|
-
const existing = await fs.readFile(dest, "utf8")
|
|
125
|
-
if (existing.includes("<!-- quorum -->")) {
|
|
126
|
-
log.skipped(".github/copilot-instructions.md (already present)")
|
|
127
|
-
return
|
|
128
|
-
}
|
|
129
|
-
await fs.appendFile(dest, `\n\n---\n\n<!-- quorum -->\n${content}`, "utf8")
|
|
130
|
-
log.appended(".github/copilot-instructions.md")
|
|
131
|
-
} else {
|
|
132
|
-
await fs.writeFile(dest, content, "utf8")
|
|
133
|
-
log.created(".github/copilot-instructions.md")
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
async function mergeAgentsMd() {
|
|
138
|
-
const dest = path.join(TARGET, "AGENTS.md")
|
|
139
|
-
const section = [
|
|
140
|
-
"",
|
|
141
|
-
"## Quorum modules",
|
|
142
|
-
"",
|
|
143
|
-
"See [quorum/modules/AGENTS.md](quorum/modules/AGENTS.md) for Oracle, Jury, and Council internals.",
|
|
144
|
-
"See [.github/copilot-instructions.md](.github/copilot-instructions.md) for workflow rules.",
|
|
145
|
-
"",
|
|
146
|
-
].join("\n")
|
|
147
|
-
|
|
148
|
-
if (await exists(dest)) {
|
|
149
|
-
const existing = await fs.readFile(dest, "utf8")
|
|
150
|
-
if (existing.includes("quorum/modules/AGENTS.md")) {
|
|
151
|
-
log.skipped("AGENTS.md (already present)")
|
|
152
|
-
return
|
|
153
|
-
}
|
|
154
|
-
await fs.appendFile(dest, section, "utf8")
|
|
155
|
-
log.appended("AGENTS.md")
|
|
156
|
-
} else {
|
|
157
|
-
await fs.writeFile(dest, `# Agent Instructions\n${section}`, "utf8")
|
|
158
|
-
log.created("AGENTS.md")
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
async function mergeClaudeMd() {
|
|
163
|
-
const dest = path.join(TARGET, "CLAUDE.md")
|
|
164
|
-
const section = `
|
|
165
|
-
## Quorum modules
|
|
166
|
-
|
|
167
|
-
See [quorum/modules/CLAUDE.md](quorum/modules/CLAUDE.md) for Oracle, Jury, and Council internals.
|
|
168
|
-
See [.github/copilot-instructions.md](.github/copilot-instructions.md) for workflow rules.
|
|
169
|
-
|
|
170
|
-
## Gemini CLI (optional assistant)
|
|
171
|
-
|
|
172
|
-
Before attempting any Gemini call, check availability:
|
|
173
|
-
|
|
174
|
-
\`\`\`bash
|
|
175
|
-
which gemini 2>/dev/null
|
|
176
|
-
\`\`\`
|
|
177
|
-
|
|
178
|
-
If the command returns empty, skip this section entirely. The project is fully functional
|
|
179
|
-
without Gemini. Never try to install it or ask the user to install it mid-task.
|
|
180
|
-
|
|
181
|
-
If Gemini is available, use it as a large-context assistant for tasks that require
|
|
182
|
-
surveying many files at once — it can hold the entire codebase in a single context window.
|
|
183
|
-
|
|
184
|
-
\`\`\`bash
|
|
185
|
-
# The Bash tool does not auto-source shell profiles — always prefix with source:
|
|
186
|
-
source ~/.zshrc && gemini -p "Summarise the public API across all modules"
|
|
187
|
-
source ~/.zshrc && gemini -p "I'm about to change X. What should I watch out for?"
|
|
188
|
-
\`\`\`
|
|
189
|
-
|
|
190
|
-
You reason about Gemini's output — it assists, you decide. Never pass its response to the
|
|
191
|
-
user unfiltered. If Gemini contradicts what you know from reading the code, trust your reading.
|
|
192
|
-
`
|
|
193
|
-
|
|
194
|
-
if (await exists(dest)) {
|
|
195
|
-
const existing = await fs.readFile(dest, "utf8")
|
|
196
|
-
if (existing.includes("quorum/modules/CLAUDE.md")) {
|
|
197
|
-
log.skipped("CLAUDE.md (already present)")
|
|
198
|
-
return
|
|
199
|
-
}
|
|
200
|
-
await fs.appendFile(dest, section, "utf8")
|
|
201
|
-
log.appended("CLAUDE.md")
|
|
202
|
-
} else {
|
|
203
|
-
await fs.writeFile(dest, `# Claude Instructions\n${section}`, "utf8")
|
|
204
|
-
log.created("CLAUDE.md")
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
async function mergeGeminiMd() {
|
|
209
|
-
const dest = path.join(TARGET, "GEMINI.md")
|
|
210
|
-
|
|
211
|
-
if (await exists(dest)) {
|
|
212
|
-
log.skipped("GEMINI.md (already present)")
|
|
213
|
-
return
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
if (!geminiAvailable()) {
|
|
217
|
-
log.skipped("GEMINI.md (Gemini CLI not detected — install it later to enable)")
|
|
218
|
-
return
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
const src = path.join(QUORUM_ROOT, "GEMINI.md")
|
|
222
|
-
if (await exists(src)) {
|
|
223
|
-
await fs.copyFile(src, dest)
|
|
224
|
-
log.created("GEMINI.md")
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
async function updatePackageJson() {
|
|
229
|
-
log.section("Updating package.json")
|
|
230
|
-
|
|
231
|
-
const pkgPath = path.join(TARGET, "package.json")
|
|
232
|
-
let pkg
|
|
233
|
-
|
|
234
|
-
if (await exists(pkgPath)) {
|
|
235
|
-
pkg = await readJson(pkgPath)
|
|
236
|
-
} else {
|
|
237
|
-
pkg = { name: path.basename(TARGET), version: "0.1.0", private: true }
|
|
238
|
-
log.warn("No package.json found — creating a minimal one")
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
pkg.dependencies = pkg.dependencies ?? {}
|
|
242
|
-
pkg.optionalDependencies = pkg.optionalDependencies ?? {}
|
|
243
|
-
|
|
244
|
-
const added = []
|
|
245
|
-
|
|
246
|
-
for (const [name, version] of Object.entries(DEPS)) {
|
|
247
|
-
if (!pkg.dependencies[name]) {
|
|
248
|
-
pkg.dependencies[name] = version
|
|
249
|
-
added.push(name)
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
for (const [name, version] of Object.entries(OPTIONAL_DEPS)) {
|
|
254
|
-
if (!pkg.optionalDependencies[name]) {
|
|
255
|
-
pkg.optionalDependencies[name] = version
|
|
256
|
-
added.push(`${name} (optional)`)
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
await fs.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8")
|
|
261
|
-
|
|
262
|
-
if (added.length > 0) {
|
|
263
|
-
log.appended(`package.json — added: ${added.join(", ")}`)
|
|
264
|
-
} else {
|
|
265
|
-
log.skipped("package.json (all deps already present)")
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
async function updateGitignore() {
|
|
270
|
-
log.section("Updating .gitignore")
|
|
271
|
-
|
|
272
|
-
const dest = path.join(TARGET, ".gitignore")
|
|
273
|
-
const block = [
|
|
274
|
-
"",
|
|
275
|
-
"# Quorum — Chronicle",
|
|
276
|
-
"# entries/ is a binary vector store — do not commit",
|
|
277
|
-
".chronicle/entries/",
|
|
278
|
-
".chronicle/query-log.jsonl",
|
|
279
|
-
"",
|
|
280
|
-
].join("\n")
|
|
281
|
-
|
|
282
|
-
if (await exists(dest)) {
|
|
283
|
-
const existing = await fs.readFile(dest, "utf8")
|
|
284
|
-
if (existing.includes(".chronicle/entries/")) {
|
|
285
|
-
log.skipped(".gitignore (already present)")
|
|
286
|
-
return
|
|
287
|
-
}
|
|
288
|
-
await fs.appendFile(dest, block, "utf8")
|
|
289
|
-
log.appended(".gitignore")
|
|
290
|
-
} else {
|
|
291
|
-
await fs.writeFile(dest, block.trimStart(), "utf8")
|
|
292
|
-
log.created(".gitignore")
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
async function createChronicle() {
|
|
297
|
-
log.section("Creating Chronicle")
|
|
298
|
-
|
|
299
|
-
await fs.mkdir(path.join(TARGET, ".chronicle", "proposals"), { recursive: true })
|
|
300
|
-
log.created(".chronicle/proposals/")
|
|
301
|
-
|
|
302
|
-
await fs.mkdir(path.join(TARGET, ".chronicle", "committed"), { recursive: true })
|
|
303
|
-
log.created(".chronicle/committed/")
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
// ── Main ───────────────────────────────────────────────────────────────────
|
|
307
|
-
|
|
308
|
-
async function main() {
|
|
309
|
-
console.log(c.bold("\nQuorum init"))
|
|
310
|
-
console.log(`Target: ${c.dim(TARGET)}\n`)
|
|
311
|
-
|
|
312
|
-
if (TARGET === QUORUM_ROOT) {
|
|
313
|
-
console.log(c.yellow("Run this from your project directory, not the Quorum repo itself."))
|
|
314
|
-
process.exit(1)
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
await guardAlreadyInitialized()
|
|
318
|
-
await copyModules()
|
|
319
|
-
await copyEvals()
|
|
320
|
-
await mergeCopilotInstructions()
|
|
321
|
-
await mergeAgentsMd()
|
|
322
|
-
await mergeClaudeMd()
|
|
323
|
-
await mergeGeminiMd()
|
|
324
|
-
await updatePackageJson()
|
|
325
|
-
await updateGitignore()
|
|
326
|
-
await createChronicle()
|
|
327
|
-
|
|
328
|
-
const hasGemini = geminiAvailable()
|
|
329
|
-
|
|
330
|
-
console.log(`\n${c.green("✓ Quorum initialized.")}`)
|
|
331
|
-
console.log("\nNext steps:")
|
|
332
|
-
console.log(" 1. npm install")
|
|
333
|
-
console.log(" 2. Wire setup() into your entry point:\n")
|
|
334
|
-
console.log(c.dim(' import { setup } from "./quorum/modules/setup"'))
|
|
335
|
-
console.log(c.dim(' const { oracle, evaluate, deliberate } = await setup({ llm: yourProvider })'))
|
|
336
|
-
console.log("\n Or tell your AI: \"follow quorum/SETUP.md\"")
|
|
337
|
-
|
|
338
|
-
if (!hasGemini) {
|
|
339
|
-
console.log(`\n ${c.dim("Optional: install Gemini CLI for large-context assistance")}`)
|
|
340
|
-
console.log(c.dim(" npm install -g @google/gemini-cli + set GEMINI_API_KEY"))
|
|
341
|
-
console.log(c.dim(" See quorum/SETUP.md Step 10 for details."))
|
|
342
|
-
} else {
|
|
343
|
-
console.log(`\n ${c.green("✓ Gemini CLI detected")} — GEMINI.md written. Set GEMINI_API_KEY if not already set.`)
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
console.log("")
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
async function cli() {
|
|
350
|
-
const command = process.argv[2] ?? ""
|
|
351
|
-
|
|
352
|
-
if (!command || command === "help" || command === "--help" || command === "-h") {
|
|
353
|
-
console.log(`\n${c.bold("quorum")} — portable reasoning layer for agentic codebases\n`)
|
|
354
|
-
console.log("Usage:")
|
|
355
|
-
console.log(` ${c.blue("npx @balpal4495/quorum init")} Scaffold Quorum into a project (or meld into an existing one)`)
|
|
356
|
-
console.log(` ${c.blue("npx quorum --version")} Print version\n`)
|
|
357
|
-
return
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
if (command === "--version" || command === "-v" || command === "version") {
|
|
361
|
-
console.log(PKG_VERSION)
|
|
362
|
-
return
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
if (command === "init") {
|
|
366
|
-
await main()
|
|
367
|
-
return
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
console.error(c.red(`\nUnknown command: ${command}`))
|
|
371
|
-
console.error("Run 'npx quorum help' for usage.")
|
|
372
|
-
process.exit(1)
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
cli().catch((err) => {
|
|
376
|
-
console.error(c.red("\nQuorum failed:"), err.message)
|
|
377
|
-
process.exit(1)
|
|
378
|
-
})
|