@eventmodelers/cli 2.0.53 → 2.0.54

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 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';
@@ -5508,6 +5508,9 @@ program
5508
5508
  .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
5509
  .option('--state', 'Print the state the log folds to (slices, queue, open prompts, learnings) instead of the events')
5510
5510
  .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.')
5511
+ .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.")
5512
+ .option('--yes', 'With --clear: really delete')
5513
+ .option('--force', 'With --clear: delete even while a run holds a claim')
5511
5514
  .option('--follow', 'Keep running and print events as they are appended')
5512
5515
  .option('--watch', 'Same as --follow')
5513
5516
  .action(async (opts) => {
@@ -5521,6 +5524,19 @@ program
5521
5524
  const prototypeLog = join(cwd, '.prototype');
5522
5525
  const inPrototype = !opts.global && !opts.kit && !findInstalledKitDir(cwd) && existsSync(join(prototypeLog, 'events.log'));
5523
5526
  const kitDir = opts.global ? globalLog : opts.kit ? resolve(cwd, opts.kit) : inPrototype ? prototypeLog : requireBuildKitDir(cwd);
5527
+ if (opts.clear) {
5528
+ try {
5529
+ const where = relative(cwd, kitDir) || kitDir;
5530
+ const { removed } = clearLog(kitDir, { force: opts.force, dryRun: !opts.yes });
5531
+ console.log(opts.yes
5532
+ ? `Cleared ${removed} events from ${where}.`
5533
+ : `Would delete all ${removed} events from ${where} — state included. Nothing was deleted; run again with --yes to do it.`);
5534
+ } catch (err) {
5535
+ console.error(`❌ ${err.message}`);
5536
+ process.exit(1);
5537
+ }
5538
+ return;
5539
+ }
5524
5540
  if (opts.prune) {
5525
5541
  try {
5526
5542
  const { removed, kept } = pruneLog(kitDir);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eventmodelers/cli",
3
- "version": "2.0.53",
3
+ "version": "2.0.54",
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);