@eventmodelers/cli 0.0.27 → 0.0.28

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,9 +225,9 @@ 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/ (project root if no kit, or a modeling-kit, is installed)
229
- npx @eventmodelers/cli fetch --slice-id <id> # same, then print just that slice
230
- npx @eventmodelers/cli fetch --slice-title <title> # same, then print just the slice matching this title
228
+ npx @eventmodelers/cli fetch --context <name> # pull full slice detail for one context on the board into <kit-dir>/.slices/ (project root if no kit, or a modeling-kit, is installed)
229
+ npx @eventmodelers/cli fetch --context <name> --slice-id <id> # same, then print just that slice
230
+ npx @eventmodelers/cli fetch --context <name> --slice-title <title> # same, then print just the slice matching this title
231
231
  npx @eventmodelers/cli stacks # list available stacks
232
232
  npx @eventmodelers/cli status # check what's installed
233
233
  npx @eventmodelers/cli config # print the fully resolved config (file + env), token masked
@@ -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). 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.
241
+ `fetch` is the pull-based counterpart to `listen`: instead of waiting for the board UI to push data to a running listener, it calls `slicedata?contextName=<name>` for the required `--context` (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 the whole context, then just print the one slice you asked about.
242
242
 
243
243
  ### Uninstall
244
244
 
package/cli.js CHANGED
@@ -1463,7 +1463,8 @@ program
1463
1463
 
1464
1464
  program
1465
1465
  .command('fetch')
1466
- .description('Pull full slice detail from every context on the board via the slicedata API and write it into .slices/ — the pull-based counterpart to `listen`, without screen images')
1466
+ .description('Pull full slice detail for one context on the board via the slicedata API and write it into .slices/ — the pull-based counterpart to `listen`, without screen images')
1467
+ .requiredOption('--context <name>', 'Name of the MODEL_CONTEXT to fetch')
1467
1468
  .option('--slice-id <id>', 'After fetching, print just the slice with this id')
1468
1469
  .option('--slice-title <title>', 'After fetching, print just the slice with this title (case-insensitive)')
1469
1470
  .action(async (opts, command) => {
package/lib/fetch.js CHANGED
@@ -45,13 +45,13 @@ function sliceFolderName(title) {
45
45
  return (title ?? '').replaceAll(' ', '').replaceAll('slice:', '').toLowerCase();
46
46
  }
47
47
 
48
- // Pulls full slice detail from every context on a board and writes it into
48
+ // Pulls full slice detail from a single context on a board and writes it into
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
52
  // kitDir here is null for modeling-kit (see cli.js's fetch action) — nothing
53
53
  // nests .slices/ under .agent-modeling-kit/, so it lands at cwd instead.
54
- // { cwd, kitDir, cfg: { token, organizationId, boardId, baseUrl }, opts: { sliceId?, sliceTitle? } }
54
+ // { cwd, kitDir, cfg: { token, organizationId, boardId, baseUrl }, opts: { context, sliceId?, sliceTitle? } }
55
55
  export async function runFetch({ cwd, kitDir, cfg, opts = {} }) {
56
56
  const baseUrl = cfg.baseUrl || DEFAULT_BASE_URL;
57
57
  const headers = { 'x-token': cfg.token, 'x-board-id': cfg.boardId, 'x-user-id': 'cli-fetch' };
@@ -74,63 +74,42 @@ export async function runFetch({ cwd, kitDir, cfg, opts = {} }) {
74
74
  return res.json();
75
75
  }
76
76
 
77
- console.log(`▶ Fetching slices from ${baseUrl} (board ${cfg.boardId})...`);
78
-
79
- // No dedicated "list contexts" endpoint — MODEL_CONTEXT nodes are the contexts,
80
- // same as how the connect skill lists CHAPTER nodes for its health check.
81
- const contextNodes = await fetchJson(
82
- `${baseUrl}/api/org/${cfg.organizationId}/boards/${cfg.boardId}/nodes?type=MODEL_CONTEXT`,
83
- 'nodes?type=MODEL_CONTEXT',
84
- );
85
- if (!contextNodes?.length) {
86
- console.log('ℹ️ No contexts found on this board.');
87
- return;
88
- }
77
+ console.log(`▶ Fetching context "${opts.context}" from ${baseUrl} (board ${cfg.boardId})...`);
89
78
 
90
79
  // Falls back to cwd when no kit is installed — fetch doesn't need kit-specific
91
80
  // files, just somewhere to write .slices/.
92
81
  const SLICES_DIR = join(kitDir || cwd, '.slices');
93
82
 
94
83
  // /slicedata (buildSliceData) is per-context and returns full slice detail —
95
- // commands/events/readmodels/screens/processors/specifications/comments — unlike
96
- // the lightweight /slicedata/slices summary. There's no "all contexts in one
97
- // call" variant, so fetch each context's full data and merge client-side.
98
- const allSlices = [];
99
- for (const node of contextNodes) {
100
- const contextName = node.meta?.title ?? node.node?.data?.title ?? '';
101
- // contextId is the normal path (we already have the node id); contextName is
102
- // the fallback the endpoint itself supports when an id can't be resolved.
103
- const contextQuery = node.id ? `contextId=${encodeURIComponent(node.id)}` : `contextName=${encodeURIComponent(contextName)}`;
104
- const payload = await fetchJson(
105
- `${baseUrl}/api/org/${cfg.organizationId}/boards/${cfg.boardId}/slicedata?${contextQuery}`,
106
- `slicedata?${contextQuery}`,
107
- );
108
- const { slices } = payload;
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
- }
84
+ // commands/events/readmodels/screens/processors/specifications/comments. It
85
+ // resolves contextName by exact match, so no separate "list contexts" lookup
86
+ // is needed first.
87
+ const contextQuery = `contextName=${encodeURIComponent(opts.context)}`;
88
+ const payload = await fetchJson(
89
+ `${baseUrl}/api/org/${cfg.organizationId}/boards/${cfg.boardId}/slicedata?${contextQuery}`,
90
+ `slicedata?${contextQuery}`,
91
+ );
92
+ const { slices: allSlices } = payload;
93
+
94
+ if (allSlices.length) {
95
+ // Mirrors code-export.mjs's /api/generate: a raw per-context payload dump at
96
+ // .slices/<context>/config.json, alongside the per-slice output below — kept
97
+ // for parity with `listen` even though nothing in this repo reads it back.
98
+ const contextSlug = slugify(allSlices[0]?.context || opts.context || 'default') || 'default';
99
+ const baseFolder = join(SLICES_DIR, contextSlug);
100
+ mkdirSync(baseFolder, { recursive: true });
101
+ writeFileSync(join(baseFolder, 'config.json'), JSON.stringify(payload, null, 2));
120
102
  }
121
103
 
122
104
  if (!allSlices.length) {
123
- console.log('ℹ️ No slices found on this board.');
105
+ console.log(`ℹ️ No slices found in context "${opts.context}".`);
124
106
  return;
125
107
  }
126
108
 
127
- const contextNames = new Set();
128
-
129
109
  for (const slice of allSlices) {
130
110
  // buildSliceData names this field `context`, not `contextName` (that's the
131
111
  // /slicedata/slices summary endpoint's field) — read the one this endpoint sends.
132
- const contextName = slice.context || 'default';
133
- contextNames.add(contextName);
112
+ const contextName = slice.context || opts.context || 'default';
134
113
  const contextSlug = slugify(contextName) || 'default';
135
114
  const baseFolder = join(SLICES_DIR, contextSlug);
136
115
  const sliceFolder = sliceFolderName(slice.title);
@@ -165,14 +144,9 @@ export async function runFetch({ cwd, kitDir, cfg, opts = {} }) {
165
144
  writeFileSync(indexFile, JSON.stringify(sliceIndices, null, 2));
166
145
  }
167
146
 
168
- // A single shared current_context.json only makes sense when everything fetched
169
- // belongs to one context — with several, any one choice would be arbitrary, so
170
- // leave whatever `listen`/a prior fetch already wrote there untouched.
171
- if (contextNames.size === 1) {
172
- writeFileSync(join(SLICES_DIR, 'current_context.json'), JSON.stringify({ name: [...contextNames][0] }, null, 2));
173
- }
147
+ writeFileSync(join(SLICES_DIR, 'current_context.json'), JSON.stringify({ name: opts.context }, null, 2));
174
148
 
175
- console.log(`✅ Fetched ${allSlices.length} slice${allSlices.length === 1 ? '' : 's'} across ${contextNames.size} context${contextNames.size === 1 ? '' : 's'} → ${relative(cwd, SLICES_DIR)}/`);
149
+ console.log(`✅ Fetched ${allSlices.length} slice${allSlices.length === 1 ? '' : 's'} from context "${opts.context}" → ${relative(cwd, SLICES_DIR)}/`);
176
150
 
177
151
  // --slice-id/--slice-title mirror load-slice's Step 4/5 — fetch+persist everything
178
152
  // regardless, then just report the one the caller asked about.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eventmodelers/cli",
3
- "version": "0.0.27",
3
+ "version": "0.0.28",
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": {