@eventmodelers/cli 0.0.1 → 0.0.3
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 +41 -9
- package/cli.js +96 -42
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -41,6 +41,16 @@ your-project/
|
|
|
41
41
|
|
|
42
42
|
The four backend stacks (`node`, `supabase`, `axon`, `cratis-csharp`) also scaffold a real project skeleton into your project root (`templates/root/`) — source layout, build files, migrations, etc. `modeling-kit` only installs skills + the agent loop, with no backend opinion.
|
|
43
43
|
|
|
44
|
+
### Installing skills globally
|
|
45
|
+
|
|
46
|
+
By default, skills are copied into the project's own `.claude/skills/`. Pass `--global` to `init` or `init-modeling` to install them into `~/.claude/skills/` instead — available in every project without re-running the installer each time:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npx @eventmodelers/cli init-modeling --global
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Everything else (the kit dir, project scaffold, credentials, MCP registration) still targets the current directory as usual — `--global` only changes where skills land.
|
|
53
|
+
|
|
44
54
|
## Claude execution & config resolution
|
|
45
55
|
|
|
46
56
|
During install you can optionally point the agent at a local LLM server (vLLM, Ollama) instead of the default Claude Code endpoint, and/or pin a specific model:
|
|
@@ -144,19 +154,41 @@ Which skills install depends on the chosen stack — see `stacks/<name>/template
|
|
|
144
154
|
## Commands
|
|
145
155
|
|
|
146
156
|
```bash
|
|
147
|
-
npx @eventmodelers/cli init --stack <name>
|
|
148
|
-
npx @eventmodelers/cli init
|
|
149
|
-
npx @eventmodelers/cli
|
|
150
|
-
npx @eventmodelers/cli
|
|
151
|
-
npx @eventmodelers/cli run
|
|
152
|
-
npx @eventmodelers/cli
|
|
153
|
-
npx @eventmodelers/cli
|
|
154
|
-
npx @eventmodelers/cli
|
|
155
|
-
npx @eventmodelers/cli
|
|
157
|
+
npx @eventmodelers/cli init --stack <name> # scaffold a stack + install + configure (alias: install)
|
|
158
|
+
npx @eventmodelers/cli init --stack <name> --global # same, but skills go to ~/.claude/skills/ (every project)
|
|
159
|
+
npx @eventmodelers/cli init-modeling # skills + agent loop only, no backend scaffold (alias: modeling)
|
|
160
|
+
npx @eventmodelers/cli init-modeling --global # same, but skills go to ~/.claude/skills/ (every project)
|
|
161
|
+
npx @eventmodelers/cli run # start the agent loop (ralph-claude.js) from the installed kit dir
|
|
162
|
+
npx @eventmodelers/cli run --ollama # same, via local Ollama (ralph-ollama.js)
|
|
163
|
+
npx @eventmodelers/cli run --bash # bash-only loop, no realtime (ralph.sh)
|
|
164
|
+
npx @eventmodelers/cli stacks # list available stacks
|
|
165
|
+
npx @eventmodelers/cli status # check what's installed
|
|
166
|
+
npx @eventmodelers/cli config # print the fully resolved config (file + env), token masked
|
|
167
|
+
npx @eventmodelers/cli uninstall # remove everything init/init-modeling installed
|
|
156
168
|
```
|
|
157
169
|
|
|
158
170
|
`run` is a thin dispatcher — it just finds the installed kit dir (whatever it's named for the stack) and execs the runner file already sitting in it. The agent loop's actual logic stays in the scaffolded `<kit-dir>/`, not in this package, since you (and the agent itself, via `AGENT.md`) may customize those files per project.
|
|
159
171
|
|
|
172
|
+
### Uninstall
|
|
173
|
+
|
|
174
|
+
Every `init`/`init-modeling` run writes an install manifest into `<kit-dir>/.eventmodelers/install-manifest.json` recording exactly what it put down. `uninstall` reads that manifest back and removes only:
|
|
175
|
+
|
|
176
|
+
- the kit dir (`.build-kit/` or `.agent-modeling-kit/`)
|
|
177
|
+
- the skills it copied — from `.claude/skills/` normally, or `~/.claude/skills/` if it was installed with `--global`
|
|
178
|
+
- the `eventmodelers` entry it added to `.claude/settings.json`'s `mcpServers` (the rest of that file, and the file itself, is left in place)
|
|
179
|
+
|
|
180
|
+
It deliberately **never** touches the root project scaffold (`package.json`, `src/`, `server.ts`, migrations, `docker-compose.yml`, etc.) — that's your actual application code, not tooling, so `uninstall` won't delete it even though `init` wrote it.
|
|
181
|
+
|
|
182
|
+
If a kit dir predates this tracking (no manifest present), `uninstall` falls back to only removing the kit dir itself and tells you so — any skills or MCP registration from that older install need to be cleaned up by hand.
|
|
183
|
+
|
|
184
|
+
Registering the MCP server with another harness (`claude mcp add`, `code --add-mcp`, or the manual Cursor/Windsurf steps) happens outside this project's files, so `uninstall` doesn't attempt to undo it — remove it yourself in that harness if you connected one.
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
npx @eventmodelers/cli uninstall # remove the one installed kit dir (errors if more than one is present)
|
|
188
|
+
npx @eventmodelers/cli uninstall --build-kit # remove .build-kit/ specifically
|
|
189
|
+
npx @eventmodelers/cli uninstall --modeling-kit # remove .agent-modeling-kit/ specifically
|
|
190
|
+
```
|
|
191
|
+
|
|
160
192
|
`--config <path>` and `--print` are global flags accepted by every command. `--print` skips the "connect MCP globally?" prompt during install and just prints the `claude mcp add` command instead of running it — combined with the env vars above, `--print` makes `init` fully non-interactive:
|
|
161
193
|
|
|
162
194
|
```bash
|
package/cli.js
CHANGED
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
} from 'fs';
|
|
16
16
|
import { execSync } from 'child_process';
|
|
17
17
|
import { createInterface, emitKeypressEvents, moveCursor, clearScreenDown } from 'readline';
|
|
18
|
+
import { homedir } from 'os';
|
|
18
19
|
|
|
19
20
|
const __filename = fileURLToPath(import.meta.url);
|
|
20
21
|
const __dirname = dirname(__filename);
|
|
@@ -285,10 +286,26 @@ async function installStack(stackKey, stackCfg, options = {}) {
|
|
|
285
286
|
process.exit(1);
|
|
286
287
|
}
|
|
287
288
|
|
|
288
|
-
// --- 1. Install skills
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
289
|
+
// --- 1. Install skills (project-local by default, or ~/.claude/skills/ with --global) ---
|
|
290
|
+
// Recorded into the install manifest (step 8) so `uninstall` can remove exactly
|
|
291
|
+
// these files later and nothing the user added independently.
|
|
292
|
+
const claudeSkillsSrc = join(templatesSource, '.claude', 'skills');
|
|
293
|
+
const installedSkills = existsSync(claudeSkillsSrc) ? readdirSync(claudeSkillsSrc) : [];
|
|
294
|
+
let claudeExtras = [];
|
|
295
|
+
|
|
296
|
+
if (options.global) {
|
|
297
|
+
const globalSkillsDir = join(homedir(), '.claude', 'skills');
|
|
298
|
+
console.log('📦 Installing Claude skills globally...');
|
|
299
|
+
console.log(` Copies skills into ${globalSkillsDir} so they're available in every project, not just this one.\n`);
|
|
300
|
+
copyDirContents(claudeSkillsSrc, globalSkillsDir);
|
|
301
|
+
} else {
|
|
302
|
+
console.log('📦 Installing Claude skills...');
|
|
303
|
+
console.log(' Copies skills and settings into .claude/ so Claude Code picks them up automatically.\n');
|
|
304
|
+
copyDirContents(join(templatesSource, '.claude'), join(targetDir, '.claude'));
|
|
305
|
+
claudeExtras = existsSync(join(templatesSource, '.claude'))
|
|
306
|
+
? readdirSync(join(templatesSource, '.claude')).filter((f) => f !== 'skills')
|
|
307
|
+
: [];
|
|
308
|
+
}
|
|
292
309
|
|
|
293
310
|
// --- 2. Spread stack scaffold files into the project root ---
|
|
294
311
|
const rootSrc = join(templatesSource, 'root');
|
|
@@ -400,41 +417,15 @@ async function installStack(stackKey, stackCfg, options = {}) {
|
|
|
400
417
|
console.log('\n ✓ Config already present — skipping credential prompt');
|
|
401
418
|
}
|
|
402
419
|
|
|
403
|
-
//
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
console.log(' ✓ Using EVENTMODELERS_ANTHROPIC_BASE_URL / EVENTMODELERS_MODEL from environment — skipping prompt');
|
|
409
|
-
} else if (options.print) {
|
|
410
|
-
console.log(' ✓ --print — skipping prompt, keeping existing Claude execution settings');
|
|
411
|
-
} else {
|
|
412
|
-
const presetUrls = ['', 'http://localhost:8000', 'http://localhost:11434'];
|
|
413
|
-
let defaultUrlIndex = presetUrls.indexOf(config.anthropicBaseUrl || '');
|
|
414
|
-
if (defaultUrlIndex === -1) defaultUrlIndex = 0;
|
|
415
|
-
|
|
416
|
-
let anthropicBaseUrl = await selectPrompt('Anthropic Base URL:', [
|
|
417
|
-
{ label: 'None — use the default Claude Code endpoint', value: '' },
|
|
418
|
-
{ label: 'Local vLLM (http://localhost:8000)', value: 'http://localhost:8000' },
|
|
419
|
-
{ label: 'Local Ollama (http://localhost:11434)', value: 'http://localhost:11434' },
|
|
420
|
-
{ label: 'Custom…', value: '__custom__' },
|
|
421
|
-
], defaultUrlIndex);
|
|
422
|
-
|
|
423
|
-
if (anthropicBaseUrl === '__custom__') {
|
|
424
|
-
anthropicBaseUrl = await prompt(' Custom Anthropic Base URL: ');
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
const claudeModel = await prompt(` Model ${config.model ? `[${config.model}]` : '(optional, press Enter to skip)'}: `);
|
|
428
|
-
|
|
429
|
-
if (anthropicBaseUrl) config.anthropicBaseUrl = anthropicBaseUrl;
|
|
430
|
-
else delete config.anthropicBaseUrl;
|
|
431
|
-
if (claudeModel) config.model = claudeModel;
|
|
432
|
-
}
|
|
433
|
-
|
|
420
|
+
// Claude vs. Ollama, and any custom Anthropic-compatible base URL, is a `run`-time
|
|
421
|
+
// choice (`eventmodelers run` vs `--ollama`) — not an install-time one. Power users
|
|
422
|
+
// can still pin config.anthropicBaseUrl/model via EVENTMODELERS_ANTHROPIC_BASE_URL /
|
|
423
|
+
// EVENTMODELERS_MODEL env vars or by editing config.json directly; loadEffectiveConfig
|
|
424
|
+
// already picks those up above, so nothing further to do here.
|
|
434
425
|
writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
435
426
|
console.log(`\n ✓ Saved to ${relative(targetDir, configPath)}`);
|
|
436
427
|
|
|
437
|
-
// ---
|
|
428
|
+
// --- 6. MCP server in .claude/settings.json ---
|
|
438
429
|
console.log('\n🔌 Configuring MCP server...');
|
|
439
430
|
console.log(' Registers the Eventmodelers MCP server in .claude/settings.json so Claude Code can call modeling tools directly.\n');
|
|
440
431
|
const claudeSettingsDir = join(targetDir, '.claude');
|
|
@@ -493,6 +484,17 @@ async function installStack(stackKey, stackCfg, options = {}) {
|
|
|
493
484
|
}
|
|
494
485
|
}
|
|
495
486
|
|
|
487
|
+
// --- 7. Install manifest (drives precise `uninstall` later) ---
|
|
488
|
+
// Only the footprint listed here is ever removed by `uninstall` — the root
|
|
489
|
+
// scaffold (step 2) is real project source the user builds on, so it's
|
|
490
|
+
// deliberately left out and never touched by uninstall.
|
|
491
|
+
const manifestDir = join(kitDir, '.eventmodelers');
|
|
492
|
+
mkdirSync(manifestDir, { recursive: true });
|
|
493
|
+
writeFileSync(
|
|
494
|
+
join(manifestDir, 'install-manifest.json'),
|
|
495
|
+
JSON.stringify({ stack: stackKey, global: !!options.global, skills: installedSkills, claudeExtras, mcpRegistered: true }, null, 2),
|
|
496
|
+
);
|
|
497
|
+
|
|
496
498
|
console.log('\n✅ Done!\n');
|
|
497
499
|
console.log('Start the agent (realtime + task loop in one process):');
|
|
498
500
|
console.log(' npx @eventmodelers/cli run\n');
|
|
@@ -500,7 +502,7 @@ async function installStack(stackKey, stackCfg, options = {}) {
|
|
|
500
502
|
console.log(' npx @eventmodelers/cli run --ollama\n');
|
|
501
503
|
console.log('Or using the bash loop only (no realtime):');
|
|
502
504
|
console.log(' npx @eventmodelers/cli run --bash\n');
|
|
503
|
-
console.log(`Skills are ready in .claude/skills/ — use /connect to set a board ID.\n`);
|
|
505
|
+
console.log(`Skills are ready in ${options.global ? join(homedir(), '.claude', 'skills') : '.claude/skills/'} — use /connect to set a board ID.\n`);
|
|
504
506
|
console.log('💡 Recommended: add Chrome DevTools MCP for browser inspection:');
|
|
505
507
|
console.log(' claude mcp add chrome-devtools --scope user -- npx chrome-devtools-mcp@latest\n');
|
|
506
508
|
}
|
|
@@ -519,19 +521,21 @@ program
|
|
|
519
521
|
.alias('install')
|
|
520
522
|
.description('Scaffold a stack + install the agent modeling kit into the current directory')
|
|
521
523
|
.option('--stack <name>', `Stack to install (${Object.keys(STACKS).join(', ')})`)
|
|
524
|
+
.option('--global', 'Install skills into ~/.claude/skills/ instead of the project — available in every project')
|
|
522
525
|
.action(async (opts, command) => {
|
|
523
526
|
const stackKey = await resolveStack(opts.stack);
|
|
524
527
|
const globalOpts = command.optsWithGlobals();
|
|
525
|
-
await installStack(stackKey, STACKS[stackKey], { configPath: globalOpts.config, print: globalOpts.print });
|
|
528
|
+
await installStack(stackKey, STACKS[stackKey], { configPath: globalOpts.config, print: globalOpts.print, global: opts.global });
|
|
526
529
|
});
|
|
527
530
|
|
|
528
531
|
program
|
|
529
532
|
.command('init-modeling')
|
|
530
533
|
.alias('modeling')
|
|
531
534
|
.description('Install skills + the agent loop only — no backend scaffold')
|
|
535
|
+
.option('--global', 'Install skills into ~/.claude/skills/ instead of the project — available in every project')
|
|
532
536
|
.action(async (opts, command) => {
|
|
533
537
|
const globalOpts = command.optsWithGlobals();
|
|
534
|
-
await installStack(MODELING_KIT.key, MODELING_KIT, { configPath: globalOpts.config, print: globalOpts.print });
|
|
538
|
+
await installStack(MODELING_KIT.key, MODELING_KIT, { configPath: globalOpts.config, print: globalOpts.print, global: opts.global });
|
|
535
539
|
});
|
|
536
540
|
|
|
537
541
|
program
|
|
@@ -579,9 +583,60 @@ program
|
|
|
579
583
|
console.log(`\nNot a stack — skills + agent loop only, no backend: npx @eventmodelers/cli init-modeling`);
|
|
580
584
|
});
|
|
581
585
|
|
|
586
|
+
// Removes exactly what a given `init`/`init-modeling` run put down — read back from
|
|
587
|
+
// the install manifest written at the end of installStack() — and nothing else: not
|
|
588
|
+
// unrelated skills the user added by hand, not the root project scaffold.
|
|
589
|
+
function uninstallKitDir(kitDir, cwd) {
|
|
590
|
+
const manifestPath = join(kitDir, '.eventmodelers', 'install-manifest.json');
|
|
591
|
+
const manifest = readJsonSafe(manifestPath);
|
|
592
|
+
|
|
593
|
+
if (manifest.skills?.length) {
|
|
594
|
+
const skillsDir = manifest.global ? join(homedir(), '.claude', 'skills') : join(cwd, '.claude', 'skills');
|
|
595
|
+
for (const name of manifest.skills) {
|
|
596
|
+
const p = join(skillsDir, name);
|
|
597
|
+
if (existsSync(p)) {
|
|
598
|
+
rmSync(p, { recursive: true, force: true });
|
|
599
|
+
console.log(` ✓ Removed ${relative(cwd, p) || p}`);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
if (manifest.claudeExtras?.length && !manifest.global) {
|
|
605
|
+
for (const name of manifest.claudeExtras) {
|
|
606
|
+
const p = join(cwd, '.claude', name);
|
|
607
|
+
if (existsSync(p)) {
|
|
608
|
+
rmSync(p, { recursive: true, force: true });
|
|
609
|
+
console.log(` ✓ Removed ${relative(cwd, p)}`);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
if (manifest.mcpRegistered) {
|
|
615
|
+
const settingsPath = join(cwd, '.claude', 'settings.json');
|
|
616
|
+
if (existsSync(settingsPath)) {
|
|
617
|
+
try {
|
|
618
|
+
const settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));
|
|
619
|
+
if (settings.mcpServers?.[MCP_SERVER_NAME]) {
|
|
620
|
+
delete settings.mcpServers[MCP_SERVER_NAME];
|
|
621
|
+
if (Object.keys(settings.mcpServers).length === 0) delete settings.mcpServers;
|
|
622
|
+
writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
|
|
623
|
+
console.log(` ✓ Removed ${MCP_SERVER_NAME} MCP entry from ${relative(cwd, settingsPath)}`);
|
|
624
|
+
}
|
|
625
|
+
} catch {}
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
if (!existsSync(manifestPath)) {
|
|
630
|
+
console.log(` ℹ️ ${relative(cwd, kitDir)} predates install tracking — only the kit dir itself was removed; any installed skills or MCP registration must be cleaned up by hand.`);
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
rmSync(kitDir, { recursive: true, force: true });
|
|
634
|
+
console.log(` ✓ Removed ${relative(cwd, kitDir) || kitDir}`);
|
|
635
|
+
}
|
|
636
|
+
|
|
582
637
|
program
|
|
583
638
|
.command('uninstall')
|
|
584
|
-
.description('Remove
|
|
639
|
+
.description('Remove everything init/init-modeling installed: the kit dir, the skills it copied (project-local or ~/.claude/skills with --global), and its MCP entry in .claude/settings.json. Leaves the root project scaffold untouched.')
|
|
585
640
|
.option('--build-kit', `Remove ${STACKS.node.kitDirName}/ (the backend-stack kit dir)`)
|
|
586
641
|
.option('--modeling-kit', `Remove ${MODELING_KIT.kitDirName}/ (the modeling-only kit dir)`)
|
|
587
642
|
.action((opts) => {
|
|
@@ -611,8 +666,7 @@ program
|
|
|
611
666
|
}
|
|
612
667
|
|
|
613
668
|
for (const t of targets) {
|
|
614
|
-
|
|
615
|
-
console.log(` ✓ Removed ${t}`);
|
|
669
|
+
uninstallKitDir(t, cwd);
|
|
616
670
|
}
|
|
617
671
|
console.log('✅ Uninstalled');
|
|
618
672
|
});
|
package/package.json
CHANGED