@eventmodelers/cli 0.0.26 → 0.0.27

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 CHANGED
@@ -225,7 +225,7 @@ npx @eventmodelers/cli run --ollama # same, via local Ollama (ra
225
225
  npx @eventmodelers/cli run --bash # bash-only loop, no realtime (ralph.sh)
226
226
  npx @eventmodelers/cli listen # start the code-export listener (code-export.mjs) from the installed kit dir
227
227
  npx @eventmodelers/cli listen --port 4000 # same, on a different port
228
- npx @eventmodelers/cli fetch # pull full slice detail from every context on the board into <kit-dir>/.slices/
228
+ npx @eventmodelers/cli fetch # pull full slice detail from every context on the board into <kit-dir>/.slices/ (project root if no kit, or a modeling-kit, is installed)
229
229
  npx @eventmodelers/cli fetch --slice-id <id> # same, then print just that slice
230
230
  npx @eventmodelers/cli fetch --slice-title <title> # same, then print just the slice matching this title
231
231
  npx @eventmodelers/cli stacks # list available stacks
@@ -238,7 +238,7 @@ npx @eventmodelers/cli uninstall # remove everything init/ini
238
238
 
239
239
  `listen` is the same kind of dispatcher, but for `<kit-dir>/code-export.mjs` — a local HTTP server (port 3001 by default) that the eventmodelers board UI posts slice/screen data to, which then gets written under `<kit-dir>/.slices/`.
240
240
 
241
- `fetch` is the pull-based counterpart to `listen`: instead of waiting for the board UI to push data to a running listener, it lists every `MODEL_CONTEXT` node on the board, calls `slicedata?contextId=<id>` for each (full slice detail — commands/events/readmodels/screens/processors/specifications/comments), and writes the same `.slices/<context>/<slice>/slice.json`, `index.json`, and `context.json` layout — useful in CI or any context where nothing is listening on a port. It does not fetch screen images (those only arrive via `listen`'s push). If credentials are missing, it prompts the same way `init-config` does. `--slice-id`/`--slice-title` still fetch and persist everything, then just print the one you asked about.
241
+ `fetch` is the pull-based counterpart to `listen`: instead of waiting for the board UI to push data to a running listener, it lists every `MODEL_CONTEXT` node on the board, calls `slicedata?contextId=<id>` for each (full slice detail — commands/events/readmodels/screens/processors/specifications/comments), and writes the same `.slices/<context>/<slice>/slice.json`, `index.json`, and `context.json` layout — useful in CI or any context where nothing is listening on a port. It does not fetch screen images (those only arrive via `listen`'s push). Unlike every other command, `fetch` also works with no kit installed at all — it only needs credentials, not kit-specific files — and, unique to modeling-kit, writes `.slices/` to the project root instead of nesting it under `.agent-modeling-kit/`, since nothing reads it from there (modeling-kit has no `code-export.mjs`/`listen`). If credentials are missing, it prompts the same way `init-config` does. `--slice-id`/`--slice-title` still fetch and persist everything, then just print the one you asked about.
242
242
 
243
243
  ### Uninstall
244
244
 
package/cli.js CHANGED
@@ -734,8 +734,15 @@ async function configureCredentials({ config, configPath, targetDir, requiredFie
734
734
  }
735
735
 
736
736
  const stillMissing = force || requiredFields.some((f) => !config[f]);
737
+ // Sole gate on the persist step below — 'instructions'/'skip' and a paste that
738
+ // couldn't be parsed all explicitly tell the user nothing was saved, so the
739
+ // final write must not run for them (it used to run unconditionally, silently
740
+ // writing out whatever `config` happened to be — `{}` on a first run — which
741
+ // contradicted those messages and left behind a bogus config.json).
742
+ let skipWrite = false;
737
743
  if (stillMissing && print) {
738
744
  console.log('\n ℹ️ --print — skipping credential prompt, missing fields must be set via flags, EVENTMODELERS_* env vars, or config.json');
745
+ skipWrite = true;
739
746
  } else if (stillMissing) {
740
747
  const choice = await selectPrompt('How do you want to configure credentials?', [
741
748
  { label: 'Paste values copied from app.eventmodelers.ai/account', value: 'paste' },
@@ -751,12 +758,10 @@ async function configureCredentials({ config, configPath, targetDir, requiredFie
751
758
  const parsed = parseCredentialsPaste(pasted, requiredFields);
752
759
  if (parsed) {
753
760
  config = { ...config, ...parsed };
754
- if (!config.baseUrl) config.baseUrl = DEFAULT_BASE_URL;
755
- writeFileSync(configPath, JSON.stringify(config, null, 2));
756
- console.log(`\n ✓ Saved to ${relative(targetDir, configPath)}`);
757
761
  } else {
758
762
  console.log(`\n ⚠️ Couldn't make sense of that paste — nothing was saved.`);
759
763
  console.log(` Paste it into ${relative(targetDir, configPath)} yourself, or use /connect later.`);
764
+ skipWrite = true;
760
765
  }
761
766
  } else if (choice === 'manual') {
762
767
  console.log('\n🔑 Enter your Eventmodelers credentials:\n');
@@ -766,9 +771,6 @@ async function configureCredentials({ config, configPath, targetDir, requiredFie
766
771
  const boardId = await prompt(` Board ID${boardIdOptional ? ' (optional)' : ''}: `);
767
772
  if (boardId) config.boardId = boardId;
768
773
  config.token = await prompt(' Token: ');
769
- if (!config.baseUrl) config.baseUrl = DEFAULT_BASE_URL;
770
- writeFileSync(configPath, JSON.stringify(config, null, 2));
771
- console.log(`\n ✓ Credentials saved to ${relative(targetDir, configPath)}`);
772
774
  } else if (choice === 'instructions') {
773
775
  console.log(`\n Paste your credentials into:\n`);
774
776
  console.log(` ${configPath}`);
@@ -778,8 +780,10 @@ async function configureCredentials({ config, configPath, targetDir, requiredFie
778
780
  const sample = ` {\n "token": "...",\n "boardId": "...",\n "organizationId": "...",\n "baseUrl": "https://api.eventmodelers.ai"\n }\n`;
779
781
  console.log(sample);
780
782
  console.log(' Then re-run this installer, or just run the agent afterwards.\n');
783
+ skipWrite = true;
781
784
  } else {
782
785
  console.log('\n ℹ️ Skipped — use /connect in Claude Code to add credentials later');
786
+ skipWrite = true;
783
787
  }
784
788
  } else {
785
789
  console.log('\n ✓ Config already present — skipping credential prompt');
@@ -791,8 +795,10 @@ async function configureCredentials({ config, configPath, targetDir, requiredFie
791
795
  config.baseUrl = DEFAULT_BASE_URL;
792
796
  }
793
797
 
794
- writeFileSync(configPath, JSON.stringify(config, null, 2));
795
- console.log(`\n ✓ Saved to ${relative(targetDir, configPath)}`);
798
+ if (!skipWrite) {
799
+ writeFileSync(configPath, JSON.stringify(config, null, 2));
800
+ console.log(`\n ✓ Saved to ${relative(targetDir, configPath)}`);
801
+ }
796
802
  return config;
797
803
  }
798
804
 
@@ -1463,6 +1469,12 @@ program
1463
1469
  .action(async (opts, command) => {
1464
1470
  const cwd = process.cwd();
1465
1471
  const kitDir = findInstalledKitDir(cwd);
1472
+ // modeling-kit has no code-export.mjs (useShared:false, no `listen`) and no
1473
+ // skill reads a nested .agent-modeling-kit/.slices/ — so nothing expects fetch's
1474
+ // output there either. Every other kit dir does have a code-export.mjs that
1475
+ // hardcodes its .slices/ next to itself, so those still nest to stay
1476
+ // interchangeable with what `listen` produces.
1477
+ const slicesKitDir = kitDir?.endsWith(MODELING_KIT.kitDirName) ? null : kitDir;
1466
1478
  const globalOpts = command.optsWithGlobals();
1467
1479
  const explicitConfig = globalOpts.config;
1468
1480
  const effective = loadEffectiveConfig(cwd, kitDir, explicitConfig);
@@ -1496,7 +1508,7 @@ program
1496
1508
  if (requiredFields.some((f) => !cfg[f])) await promptForCredentials();
1497
1509
 
1498
1510
  try {
1499
- await runFetch({ cwd, kitDir, cfg, opts });
1511
+ await runFetch({ cwd, kitDir: slicesKitDir, cfg, opts });
1500
1512
  } catch (err) {
1501
1513
  if (!(err instanceof FetchAuthError)) throw err;
1502
1514
  // Present but wrong, not missing — the connect skill's Step 4 (Verify) treats
@@ -1506,7 +1518,7 @@ program
1506
1518
  if (err.status === 404) delete cfg.boardId;
1507
1519
  else delete cfg.token;
1508
1520
  await promptForCredentials();
1509
- await runFetch({ cwd, kitDir, cfg, opts });
1521
+ await runFetch({ cwd, kitDir: slicesKitDir, cfg, opts });
1510
1522
  }
1511
1523
  });
1512
1524
 
package/lib/fetch.js CHANGED
@@ -49,6 +49,8 @@ function sliceFolderName(title) {
49
49
  // .slices/, mirroring the layout code-export.mjs's /api/generate handler produces
50
50
  // (minus screen images, which only ever arrive via that push-based listener).
51
51
  //
52
+ // kitDir here is null for modeling-kit (see cli.js's fetch action) — nothing
53
+ // nests .slices/ under .agent-modeling-kit/, so it lands at cwd instead.
52
54
  // { cwd, kitDir, cfg: { token, organizationId, boardId, baseUrl }, opts: { sliceId?, sliceTitle? } }
53
55
  export async function runFetch({ cwd, kitDir, cfg, opts = {} }) {
54
56
  const baseUrl = cfg.baseUrl || DEFAULT_BASE_URL;
@@ -85,6 +87,10 @@ export async function runFetch({ cwd, kitDir, cfg, opts = {} }) {
85
87
  return;
86
88
  }
87
89
 
90
+ // Falls back to cwd when no kit is installed — fetch doesn't need kit-specific
91
+ // files, just somewhere to write .slices/.
92
+ const SLICES_DIR = join(kitDir || cwd, '.slices');
93
+
88
94
  // /slicedata (buildSliceData) is per-context and returns full slice detail —
89
95
  // commands/events/readmodels/screens/processors/specifications/comments — unlike
90
96
  // the lightweight /slicedata/slices summary. There's no "all contexts in one
@@ -95,11 +101,22 @@ export async function runFetch({ cwd, kitDir, cfg, opts = {} }) {
95
101
  // contextId is the normal path (we already have the node id); contextName is
96
102
  // the fallback the endpoint itself supports when an id can't be resolved.
97
103
  const contextQuery = node.id ? `contextId=${encodeURIComponent(node.id)}` : `contextName=${encodeURIComponent(contextName)}`;
98
- const { slices } = await fetchJson(
104
+ const payload = await fetchJson(
99
105
  `${baseUrl}/api/org/${cfg.organizationId}/boards/${cfg.boardId}/slicedata?${contextQuery}`,
100
106
  `slicedata?${contextQuery}`,
101
107
  );
108
+ const { slices } = payload;
102
109
  allSlices.push(...slices);
110
+
111
+ if (slices.length) {
112
+ // Mirrors code-export.mjs's /api/generate: a raw per-context payload dump at
113
+ // .slices/<context>/config.json, alongside the per-slice output below — kept
114
+ // for parity with `listen` even though nothing in this repo reads it back.
115
+ const contextSlug = slugify(slices[0]?.context || contextName || 'default') || 'default';
116
+ const baseFolder = join(SLICES_DIR, contextSlug);
117
+ mkdirSync(baseFolder, { recursive: true });
118
+ writeFileSync(join(baseFolder, 'config.json'), JSON.stringify(payload, null, 2));
119
+ }
103
120
  }
104
121
 
105
122
  if (!allSlices.length) {
@@ -107,9 +124,6 @@ export async function runFetch({ cwd, kitDir, cfg, opts = {} }) {
107
124
  return;
108
125
  }
109
126
 
110
- // Falls back to cwd when no kit is installed — fetch doesn't need kit-specific
111
- // files, just somewhere to write .slices/.
112
- const SLICES_DIR = join(kitDir || cwd, '.slices');
113
127
  const contextNames = new Set();
114
128
 
115
129
  for (const slice of allSlices) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eventmodelers/cli",
3
- "version": "0.0.26",
3
+ "version": "0.0.27",
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": {