@eventmodelers/cli 2.0.53 → 2.0.55
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/cli.js +41 -8
- package/package.json +1 -1
- package/shared/build-kit/lib/event-prune.js +20 -1
package/cli.js
CHANGED
|
@@ -44,7 +44,7 @@ import { defaultAgentName, withInstanceSuffix } from './shared/build-kit/lib/age
|
|
|
44
44
|
import { acquireAgentId } from './shared/build-kit/lib/agent-lock.js';
|
|
45
45
|
import { readEvents, appendEvent, locationOf } from './shared/build-kit/lib/slice-events.js';
|
|
46
46
|
import { findPromptEvent, replayRequest } from './shared/build-kit/lib/prompt-replay.js';
|
|
47
|
-
import { prunableEvents, pruneLog, rememberPruneAnswer, shouldOfferPrune } from './shared/build-kit/lib/event-prune.js';
|
|
47
|
+
import { clearLog, prunableEvents, pruneLog, rememberPruneAnswer, shouldOfferPrune } from './shared/build-kit/lib/event-prune.js';
|
|
48
48
|
import { fold } from './shared/build-kit/lib/slice-state.js';
|
|
49
49
|
import { filterEvents, baselineIds, formatEvent, formatPayload, sliceTitles, summarize } from './shared/build-kit/lib/events-view.js';
|
|
50
50
|
import { stableGlobalAgentId } from './lib/global-agent-id.js';
|
|
@@ -4525,7 +4525,7 @@ credentialFlags(program
|
|
|
4525
4525
|
// path with no stream-json parsing to gate. RALPH_LOCAL is read by ralph.js's startRalph to force
|
|
4526
4526
|
// the local-only branch even when .eventmodelers/config.json has valid credentials.
|
|
4527
4527
|
// RALPH_AGENT_ID/RALPH_AGENT_NAME (--id/--name) are read there too.
|
|
4528
|
-
execSync(cmd, { cwd: kitDir, stdio: 'inherit', env: { ...process.env, RALPH_VERBOSE: opts.verbose ? '1' : '', RALPH_SYNC: opts.sync ? '1' : '', RALPH_LOCAL: opts.local ? '1' : '', RALPH_HANDOVER: opts.handover ? '1' : '', RALPH_STATUS_TRIGGER: opts.statusTrigger ? String(opts.statusTrigger).trim() : '', RALPH_FEATURE: opts.feature ? String(opts.feature).trim() : '', RALPH_AGENT_ID: identity.agentId ?? '', RALPH_AGENT_NAME: identity.agentName ?? '', ...(typeof localAiTarget === 'string' ? { LOCAL_AI_TARGET: localAiTarget } : {}), ...(typeof opts.exec === 'string' ? { RALPH_EXEC_CMD: opts.exec } : {}), ...(acpAgent ? { RALPH_ACP_AGENT: opts.acpAgent ?? '', RALPH_ACP_CMD: opts.acpCmd ?? '' } : {}) } });
|
|
4528
|
+
execSync(cmd, { cwd: kitDir, stdio: 'inherit', env: { ...process.env, RALPH_VERBOSE: opts.verbose ? '1' : '', RALPH_SYNC: opts.sync ? '1' : '', RALPH_LOCAL: opts.local ? '1' : '', RALPH_HANDOVER: opts.handover ? '1' : '', RALPH_STATUS_TRIGGER: opts.statusTrigger ? String(opts.statusTrigger).trim() : '', RALPH_FEATURE: opts.feature ? String(opts.feature).trim() : '', EVENTMODELERS_KIT_DIR: kitDir, RALPH_AGENT_ID: identity.agentId ?? '', RALPH_AGENT_NAME: identity.agentName ?? '', ...(typeof localAiTarget === 'string' ? { LOCAL_AI_TARGET: localAiTarget } : {}), ...(typeof opts.exec === 'string' ? { RALPH_EXEC_CMD: opts.exec } : {}), ...(acpAgent ? { RALPH_ACP_AGENT: opts.acpAgent ?? '', RALPH_ACP_CMD: opts.acpCmd ?? '' } : {}) } });
|
|
4529
4529
|
} catch (err) {
|
|
4530
4530
|
process.exit(err.status || 1);
|
|
4531
4531
|
}
|
|
@@ -5296,10 +5296,27 @@ program
|
|
|
5296
5296
|
console.log(`✅ Pushed status change to the board (node ${slice.id}).`);
|
|
5297
5297
|
});
|
|
5298
5298
|
|
|
5299
|
+
// The kit directory the run loop pinned for its agent (EVENTMODELERS_KIT_DIR, set by `eventmodelers run`), or null.
|
|
5300
|
+
// An agent's shell cwd drifts; the loop's does not. A stale value (directory gone) is ignored rather than trusted.
|
|
5301
|
+
function pinnedKitDir() {
|
|
5302
|
+
const dir = process.env.EVENTMODELERS_KIT_DIR;
|
|
5303
|
+
return dir && existsSync(dir) ? resolve(dir) : null;
|
|
5304
|
+
}
|
|
5305
|
+
|
|
5306
|
+
// Where an agent-side command resolves the project from: the pinned kit's project, else the shell's cwd.
|
|
5307
|
+
function agentCwd() {
|
|
5308
|
+
const pinned = pinnedKitDir();
|
|
5309
|
+
return pinned ? dirname(pinned) : process.cwd();
|
|
5310
|
+
}
|
|
5311
|
+
|
|
5299
5312
|
// The kit a command that records events works against: a build kit if one is installed, else the modeling
|
|
5300
5313
|
// kit. The build kit wins when both are installed — the modeling agent never runs these commands itself
|
|
5301
5314
|
// (its Bash is an allowlist); it states its learnings in its reply and `runModeling` records them.
|
|
5302
5315
|
function requireBuildKitDir(cwd) {
|
|
5316
|
+
// The run loop exports the kit it runs in. Trust it over the shell's cwd: the build agent may `cd` elsewhere in the
|
|
5317
|
+
// project (an app with a root kit and a web/ kit) and its commands must still reach the kit that holds the claim.
|
|
5318
|
+
const pinned = pinnedKitDir();
|
|
5319
|
+
if (pinned) return pinned;
|
|
5303
5320
|
const installed = findAllInstalledKitDirs(cwd);
|
|
5304
5321
|
const kitDir = installed.find((d) => !d.endsWith(MODELING_KIT.kitDirName)) ?? installed.find((d) => d.endsWith(MODELING_KIT.kitDirName));
|
|
5305
5322
|
if (!kitDir) {
|
|
@@ -5325,7 +5342,7 @@ program
|
|
|
5325
5342
|
.description("For a build agent: record the outcome of the slice the run loop is building (Done, Waiting, Blocked, ...). The loop owns the slice's status on the board and pushes it when the build ends — never edit index.json or call update-slice-status for a loop build.")
|
|
5326
5343
|
.option('--slice <idOrTitle>', 'The slice, when the loop has claimed more than one (default: the one it is building)')
|
|
5327
5344
|
.action((status, opts, command) => {
|
|
5328
|
-
const cwd =
|
|
5345
|
+
const cwd = agentCwd();
|
|
5329
5346
|
// `Handover` is the old name of `Waiting`; older skills and prompts still say it
|
|
5330
5347
|
const wanted = status.toLowerCase() === 'handover' ? 'waiting' : status.toLowerCase();
|
|
5331
5348
|
const canonical = SLICE_STATUSES.find((s) => s.value.toLowerCase() === wanted)?.value;
|
|
@@ -5359,7 +5376,7 @@ program
|
|
|
5359
5376
|
console.error('❌ Action must be `check` or `record`.');
|
|
5360
5377
|
process.exit(1);
|
|
5361
5378
|
}
|
|
5362
|
-
const cwd =
|
|
5379
|
+
const cwd = agentCwd();
|
|
5363
5380
|
const kitDir = requireBuildKitDir(cwd);
|
|
5364
5381
|
let wanted = opts.slice;
|
|
5365
5382
|
if (!wanted) {
|
|
@@ -5367,7 +5384,7 @@ program
|
|
|
5367
5384
|
if (claimed.length !== 1) {
|
|
5368
5385
|
if (!claimed.length) {
|
|
5369
5386
|
// Where it looked: an agent running from another kit's directory finds no claim in that kit's log.
|
|
5370
|
-
try { appendEvent(kitDir, { type: 'claim-not-found', command: 'build-baseline', claimed: 0, ...locationOf(kitDir, cwd) }, agentEventMeta(cwd, kitDir, command)); } catch { /* best effort */ }
|
|
5387
|
+
try { appendEvent(kitDir, { type: 'claim-not-found', command: 'build-baseline', claimed: 0, ...locationOf(kitDir, process.cwd()) }, agentEventMeta(cwd, kitDir, command)); } catch { /* best effort */ }
|
|
5371
5388
|
}
|
|
5372
5389
|
console.error(`❌ ${claimed.length ? `${claimed.length} slices are claimed` : 'No slice is claimed by the run loop'} — name the slice with --slice.`);
|
|
5373
5390
|
process.exit(1);
|
|
@@ -5432,7 +5449,7 @@ program
|
|
|
5432
5449
|
.option('--merge <ids>', 'Replace these learnings (comma-separated ids) by one with the given text — the curator\'s tool')
|
|
5433
5450
|
.option('--reason <reason>', 'Why — recorded with a retraction or a merge')
|
|
5434
5451
|
.action((text, opts, command) => {
|
|
5435
|
-
const cwd =
|
|
5452
|
+
const cwd = agentCwd();
|
|
5436
5453
|
const kitDir = requireBuildKitDir(cwd);
|
|
5437
5454
|
const meta = agentEventMeta(cwd, kitDir, command);
|
|
5438
5455
|
try {
|
|
@@ -5465,7 +5482,7 @@ program
|
|
|
5465
5482
|
.option('--slice <idOrTitle>', 'The slice it is about (default: the one the run loop has claimed)')
|
|
5466
5483
|
.option('--tag <tag>', 'Label the info (repeatable)', (tag, tags) => [...tags, tag], [])
|
|
5467
5484
|
.action(async (text, opts, command) => {
|
|
5468
|
-
const cwd =
|
|
5485
|
+
const cwd = agentCwd();
|
|
5469
5486
|
const kitDir = requireBuildKitDir(cwd);
|
|
5470
5487
|
const meta = agentEventMeta(cwd, kitDir, command);
|
|
5471
5488
|
try {
|
|
@@ -5508,6 +5525,9 @@ program
|
|
|
5508
5525
|
.option('--verbose', 'Print each event\'s payload under its line — for a tool call, the input it was sent (recorded by runs started with --verbose). Also shows the baseline events: the kit\'s first look at each slice (a fresh kit records every slice already on the board), which are hidden by default. `--type board-observed` and `--json` show them too.')
|
|
5509
5526
|
.option('--state', 'Print the state the log folds to (slices, queue, open prompts, learnings) instead of the events')
|
|
5510
5527
|
.option('--prune', 'Delete the events nothing reads any more — the journal of finished prompts — and stop. Slice state, learnings and open prompts are kept; the same prune a run offers at every 1000 events.')
|
|
5528
|
+
.option('--clear', "Delete the whole log, state included, so the kit starts over (it looks at the board again; attempt counts, blocks and failure history are gone). Lists what it would delete and stops unless --yes is given. Refuses while a run holds a claim, unless --force.")
|
|
5529
|
+
.option('--yes', 'With --clear: really delete')
|
|
5530
|
+
.option('--force', 'With --clear: delete even while a run holds a claim')
|
|
5511
5531
|
.option('--follow', 'Keep running and print events as they are appended')
|
|
5512
5532
|
.option('--watch', 'Same as --follow')
|
|
5513
5533
|
.action(async (opts) => {
|
|
@@ -5521,6 +5541,19 @@ program
|
|
|
5521
5541
|
const prototypeLog = join(cwd, '.prototype');
|
|
5522
5542
|
const inPrototype = !opts.global && !opts.kit && !findInstalledKitDir(cwd) && existsSync(join(prototypeLog, 'events.log'));
|
|
5523
5543
|
const kitDir = opts.global ? globalLog : opts.kit ? resolve(cwd, opts.kit) : inPrototype ? prototypeLog : requireBuildKitDir(cwd);
|
|
5544
|
+
if (opts.clear) {
|
|
5545
|
+
try {
|
|
5546
|
+
const where = relative(cwd, kitDir) || kitDir;
|
|
5547
|
+
const { removed } = clearLog(kitDir, { force: opts.force, dryRun: !opts.yes });
|
|
5548
|
+
console.log(opts.yes
|
|
5549
|
+
? `Cleared ${removed} events from ${where}.`
|
|
5550
|
+
: `Would delete all ${removed} events from ${where} — state included. Nothing was deleted; run again with --yes to do it.`);
|
|
5551
|
+
} catch (err) {
|
|
5552
|
+
console.error(`❌ ${err.message}`);
|
|
5553
|
+
process.exit(1);
|
|
5554
|
+
}
|
|
5555
|
+
return;
|
|
5556
|
+
}
|
|
5524
5557
|
if (opts.prune) {
|
|
5525
5558
|
try {
|
|
5526
5559
|
const { removed, kept } = pruneLog(kitDir);
|
|
@@ -5631,7 +5664,7 @@ program
|
|
|
5631
5664
|
.description('Print the recorded learnings — what .build-kit/AGENTS.md says. With --ids, one line each with the id to retract it by.')
|
|
5632
5665
|
.option('--ids', 'One line per learning: id, topic, text')
|
|
5633
5666
|
.action((opts, command) => {
|
|
5634
|
-
const cwd =
|
|
5667
|
+
const cwd = agentCwd();
|
|
5635
5668
|
const kitDir = requireBuildKitDir(cwd);
|
|
5636
5669
|
syncLearnings({ kitDir, meta: agentEventMeta(cwd, kitDir, command) });
|
|
5637
5670
|
const all = listLearnings(kitDir);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eventmodelers/cli",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.55",
|
|
4
4
|
"description": "Eventmodelers CLI — real-time Claude agent + skills for Claude Code, for any stack (Node, Supabase, Axon, OpenCQRS, UmaDB, Kurrent, or modeling-only)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -7,8 +7,9 @@
|
|
|
7
7
|
import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'fs';
|
|
8
8
|
import { isDeepStrictEqual } from 'util';
|
|
9
9
|
import { join } from 'path';
|
|
10
|
-
import { EVENTS_DIR, INDEX_FILE, readEvents } from './slice-events.js';
|
|
10
|
+
import { EVENTS_DIR, INDEX_FILE, SEQ_FILE, readEvents } from './slice-events.js';
|
|
11
11
|
import { fold } from './slice-state.js';
|
|
12
|
+
import { isProcessAlive } from './slice-run.js';
|
|
12
13
|
|
|
13
14
|
export const PRUNE_EVERY = 1000;
|
|
14
15
|
const ASKED_FILE = '.prune-asked'; // inside events/, which readEvents never lists and the kit's git rules already cover
|
|
@@ -40,6 +41,24 @@ export function pruneLog(kitDir) {
|
|
|
40
41
|
return { removed: doomed.size, kept: rest.length };
|
|
41
42
|
}
|
|
42
43
|
|
|
44
|
+
// Deletes the whole log — every event, state included — so the kit starts over as if freshly installed: it looks at
|
|
45
|
+
// the board again and every slice's attempt count, block and failure history is gone. Refuses while a live process
|
|
46
|
+
// holds a claim (its build would end without a trace) unless `force`. Returns { removed, liveClaims }; with
|
|
47
|
+
// `dryRun` nothing is deleted.
|
|
48
|
+
export function clearLog(kitDir, { force = false, dryRun = false } = {}) {
|
|
49
|
+
const events = readEvents(kitDir);
|
|
50
|
+
const liveClaims = Object.entries(fold(events).slices)
|
|
51
|
+
.filter(([, s]) => s.claim && isProcessAlive(s.claim.pid))
|
|
52
|
+
.map(([id, s]) => s.claim.title ?? id);
|
|
53
|
+
if (liveClaims.length && !force) throw new Error(`a run is building ${liveClaims.join(', ')} — stop it first, or pass --force`);
|
|
54
|
+
if (dryRun) return { removed: events.length, liveClaims };
|
|
55
|
+
const dir = join(kitDir, EVENTS_DIR);
|
|
56
|
+
rmSync(dir, { recursive: true, force: true });
|
|
57
|
+
rmSync(join(kitDir, INDEX_FILE), { force: true });
|
|
58
|
+
rmSync(join(kitDir, SEQ_FILE), { force: true });
|
|
59
|
+
return { removed: events.length, liveClaims };
|
|
60
|
+
}
|
|
61
|
+
|
|
43
62
|
// Asked when the log has reached a multiple of PRUNE_EVERY it has not been asked at yet — 1000, then 2000, … — so a
|
|
44
63
|
// "no" is not repeated on every start. `remember` records the size the answer was given at.
|
|
45
64
|
const askedPath = (kitDir) => join(kitDir, EVENTS_DIR, ASKED_FILE);
|