@eventmodelers/cli 0.0.1 → 0.0.2

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 +41 -9
  2. package/cli.js +90 -10
  3. 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> # scaffold a stack + install + configure (alias: install)
148
- npx @eventmodelers/cli init-modeling # skills + agent loop only, no backend scaffold (alias: modeling)
149
- npx @eventmodelers/cli run # start the agent loop (ralph-claude.js) from the installed kit dir
150
- npx @eventmodelers/cli run --ollama # same, via local Ollama (ralph-ollama.js)
151
- npx @eventmodelers/cli run --bash # bash-only loop, no realtime (ralph.sh)
152
- npx @eventmodelers/cli stacks # list available stacks
153
- npx @eventmodelers/cli status # check what's installed
154
- npx @eventmodelers/cli config # print the fully resolved config (file + env), token masked
155
- npx @eventmodelers/cli uninstall # remove the installed kit dir
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 and Claude settings into project root ---
289
- console.log('📦 Installing Claude skills...');
290
- console.log(' Copies skills and settings into .claude/ so Claude Code picks them up automatically.\n');
291
- copyDirContents(join(templatesSource, '.claude'), join(targetDir, '.claude'));
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');
@@ -493,6 +510,17 @@ async function installStack(stackKey, stackCfg, options = {}) {
493
510
  }
494
511
  }
495
512
 
513
+ // --- 8. Install manifest (drives precise `uninstall` later) ---
514
+ // Only the footprint listed here is ever removed by `uninstall` — the root
515
+ // scaffold (step 2) is real project source the user builds on, so it's
516
+ // deliberately left out and never touched by uninstall.
517
+ const manifestDir = join(kitDir, '.eventmodelers');
518
+ mkdirSync(manifestDir, { recursive: true });
519
+ writeFileSync(
520
+ join(manifestDir, 'install-manifest.json'),
521
+ JSON.stringify({ stack: stackKey, global: !!options.global, skills: installedSkills, claudeExtras, mcpRegistered: true }, null, 2),
522
+ );
523
+
496
524
  console.log('\n✅ Done!\n');
497
525
  console.log('Start the agent (realtime + task loop in one process):');
498
526
  console.log(' npx @eventmodelers/cli run\n');
@@ -500,7 +528,7 @@ async function installStack(stackKey, stackCfg, options = {}) {
500
528
  console.log(' npx @eventmodelers/cli run --ollama\n');
501
529
  console.log('Or using the bash loop only (no realtime):');
502
530
  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`);
531
+ console.log(`Skills are ready in ${options.global ? join(homedir(), '.claude', 'skills') : '.claude/skills/'} — use /connect to set a board ID.\n`);
504
532
  console.log('💡 Recommended: add Chrome DevTools MCP for browser inspection:');
505
533
  console.log(' claude mcp add chrome-devtools --scope user -- npx chrome-devtools-mcp@latest\n');
506
534
  }
@@ -519,19 +547,21 @@ program
519
547
  .alias('install')
520
548
  .description('Scaffold a stack + install the agent modeling kit into the current directory')
521
549
  .option('--stack <name>', `Stack to install (${Object.keys(STACKS).join(', ')})`)
550
+ .option('--global', 'Install skills into ~/.claude/skills/ instead of the project — available in every project')
522
551
  .action(async (opts, command) => {
523
552
  const stackKey = await resolveStack(opts.stack);
524
553
  const globalOpts = command.optsWithGlobals();
525
- await installStack(stackKey, STACKS[stackKey], { configPath: globalOpts.config, print: globalOpts.print });
554
+ await installStack(stackKey, STACKS[stackKey], { configPath: globalOpts.config, print: globalOpts.print, global: opts.global });
526
555
  });
527
556
 
528
557
  program
529
558
  .command('init-modeling')
530
559
  .alias('modeling')
531
560
  .description('Install skills + the agent loop only — no backend scaffold')
561
+ .option('--global', 'Install skills into ~/.claude/skills/ instead of the project — available in every project')
532
562
  .action(async (opts, command) => {
533
563
  const globalOpts = command.optsWithGlobals();
534
- await installStack(MODELING_KIT.key, MODELING_KIT, { configPath: globalOpts.config, print: globalOpts.print });
564
+ await installStack(MODELING_KIT.key, MODELING_KIT, { configPath: globalOpts.config, print: globalOpts.print, global: opts.global });
535
565
  });
536
566
 
537
567
  program
@@ -579,9 +609,60 @@ program
579
609
  console.log(`\nNot a stack — skills + agent loop only, no backend: npx @eventmodelers/cli init-modeling`);
580
610
  });
581
611
 
612
+ // Removes exactly what a given `init`/`init-modeling` run put down — read back from
613
+ // the install manifest written at the end of installStack() — and nothing else: not
614
+ // unrelated skills the user added by hand, not the root project scaffold.
615
+ function uninstallKitDir(kitDir, cwd) {
616
+ const manifestPath = join(kitDir, '.eventmodelers', 'install-manifest.json');
617
+ const manifest = readJsonSafe(manifestPath);
618
+
619
+ if (manifest.skills?.length) {
620
+ const skillsDir = manifest.global ? join(homedir(), '.claude', 'skills') : join(cwd, '.claude', 'skills');
621
+ for (const name of manifest.skills) {
622
+ const p = join(skillsDir, name);
623
+ if (existsSync(p)) {
624
+ rmSync(p, { recursive: true, force: true });
625
+ console.log(` ✓ Removed ${relative(cwd, p) || p}`);
626
+ }
627
+ }
628
+ }
629
+
630
+ if (manifest.claudeExtras?.length && !manifest.global) {
631
+ for (const name of manifest.claudeExtras) {
632
+ const p = join(cwd, '.claude', name);
633
+ if (existsSync(p)) {
634
+ rmSync(p, { recursive: true, force: true });
635
+ console.log(` ✓ Removed ${relative(cwd, p)}`);
636
+ }
637
+ }
638
+ }
639
+
640
+ if (manifest.mcpRegistered) {
641
+ const settingsPath = join(cwd, '.claude', 'settings.json');
642
+ if (existsSync(settingsPath)) {
643
+ try {
644
+ const settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));
645
+ if (settings.mcpServers?.[MCP_SERVER_NAME]) {
646
+ delete settings.mcpServers[MCP_SERVER_NAME];
647
+ if (Object.keys(settings.mcpServers).length === 0) delete settings.mcpServers;
648
+ writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
649
+ console.log(` ✓ Removed ${MCP_SERVER_NAME} MCP entry from ${relative(cwd, settingsPath)}`);
650
+ }
651
+ } catch {}
652
+ }
653
+ }
654
+
655
+ if (!existsSync(manifestPath)) {
656
+ 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.`);
657
+ }
658
+
659
+ rmSync(kitDir, { recursive: true, force: true });
660
+ console.log(` ✓ Removed ${relative(cwd, kitDir) || kitDir}`);
661
+ }
662
+
582
663
  program
583
664
  .command('uninstall')
584
- .description('Remove the installed kit dir from the current directory')
665
+ .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
666
  .option('--build-kit', `Remove ${STACKS.node.kitDirName}/ (the backend-stack kit dir)`)
586
667
  .option('--modeling-kit', `Remove ${MODELING_KIT.kitDirName}/ (the modeling-only kit dir)`)
587
668
  .action((opts) => {
@@ -611,8 +692,7 @@ program
611
692
  }
612
693
 
613
694
  for (const t of targets) {
614
- rmSync(t, { recursive: true, force: true });
615
- console.log(` ✓ Removed ${t}`);
695
+ uninstallKitDir(t, cwd);
616
696
  }
617
697
  console.log('✅ Uninstalled');
618
698
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eventmodelers/cli",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "Eventmodelers CLI — real-time Claude agent + skills for Claude Code, for any stack (Node, Supabase, Axon, Cratis, or modeling-only)",
5
5
  "type": "module",
6
6
  "bin": {