@eventmodelers/cli 1.0.49 → 1.0.51

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
@@ -918,24 +918,7 @@ async function installStack(stackKey, stackCfg, options = {}) {
918
918
  if (options.hooks) {
919
919
  const hooksSrc = join(rootSrc, '.githooks');
920
920
  if (existsSync(hooksSrc)) {
921
- copyDirContents(hooksSrc, join(targetDir, '.githooks'));
922
- const preCommitHook = join(targetDir, '.githooks', 'pre-commit');
923
- if (existsSync(preCommitHook)) {
924
- // cpSync doesn't reliably carry over the executable bit across platforms,
925
- // and git silently skips a non-executable hook.
926
- try { execSync(`chmod +x "${preCommitHook}"`); } catch {}
927
- }
928
- try {
929
- execSync('git rev-parse --git-dir', { cwd: targetDir, stdio: 'ignore' });
930
- // core.hooksPath is resolved against the repo's actual top level, not `cwd` —
931
- // a relative `.githooks` breaks silently (no error, hooks just don't run) when
932
- // targetDir is a subfolder of a larger repo rather than the repo root itself.
933
- // Use an absolute path so it's correct regardless of where the git root is.
934
- execSync(`git config core.hooksPath "${join(targetDir, '.githooks')}"`, { cwd: targetDir });
935
- console.log(' ✓ Installed .githooks/ and set core.hooksPath — commits touching src/slices/ are now scope-guarded');
936
- } catch {
937
- console.log(` ✓ Installed .githooks/ — run \`git config core.hooksPath ${join(targetDir, '.githooks')}\` once this directory is a git repo to activate it`);
938
- }
921
+ configureHooks({ hooksSrc, targetDir });
939
922
  } else {
940
923
  console.log(' ℹ️ --hooks was given but this stack ships no .githooks/ template — nothing to install');
941
924
  }
@@ -1192,6 +1175,33 @@ async function configureMcp(options = {}) {
1192
1175
  }
1193
1176
  }
1194
1177
 
1178
+ // Installs/refreshes the slice commit-scope guard (.githooks/pre-commit, running
1179
+ // .build-kit/lib/check-commit-scope.cjs) and wires it up via `git config
1180
+ // core.hooksPath .githooks`. Shared by `init --hooks`, `re-init --hooks`, and the
1181
+ // standalone `init-hooks` command so all three copy/chmod/git-config identically
1182
+ // instead of drifting apart — callers are responsible for checking `hooksSrc`
1183
+ // exists first, since what "no template for this stack" means differs per caller.
1184
+ function configureHooks({ hooksSrc, targetDir }) {
1185
+ copyDirContents(hooksSrc, join(targetDir, '.githooks'));
1186
+ const preCommitHook = join(targetDir, '.githooks', 'pre-commit');
1187
+ if (existsSync(preCommitHook)) {
1188
+ // cpSync doesn't reliably carry over the executable bit across platforms,
1189
+ // and git silently skips a non-executable hook.
1190
+ try { execSync(`chmod +x "${preCommitHook}"`); } catch {}
1191
+ }
1192
+ try {
1193
+ execSync('git rev-parse --git-dir', { cwd: targetDir, stdio: 'ignore' });
1194
+ // core.hooksPath is resolved against the repo's actual top level, not `cwd` —
1195
+ // a relative `.githooks` breaks silently (no error, hooks just don't run) when
1196
+ // targetDir is a subfolder of a larger repo rather than the repo root itself.
1197
+ // Use an absolute path so it's correct regardless of where the git root is.
1198
+ execSync(`git config core.hooksPath "${join(targetDir, '.githooks')}"`, { cwd: targetDir });
1199
+ console.log(' ✓ Installed .githooks/ and set core.hooksPath — commits touching src/slices/ are now scope-guarded');
1200
+ } catch {
1201
+ console.log(` ✓ Installed .githooks/ — run \`git config core.hooksPath ${join(targetDir, '.githooks')}\` once this directory is a git repo to activate it`);
1202
+ }
1203
+ }
1204
+
1195
1205
  // Registers the eventmodelers MCP server in `.mcp.json` at the project root, the
1196
1206
  // same file/shape the `connect` skill's Step 3.5 produces — kept here as a
1197
1207
  // belt-and-suspenders guarantee, since an agent executing that skill can skip a
@@ -1777,6 +1787,99 @@ program
1777
1787
  await configureAgentHosts({ hosts, global: opts.global });
1778
1788
  });
1779
1789
 
1790
+ program
1791
+ .command('init-hooks')
1792
+ .description('Install/refresh the slice commit-scope guard (.githooks/pre-commit) and set `git config core.hooksPath .githooks` — same as `init --hooks`/`re-init --hooks`, for installing the latest hooks or re-pointing git config at them without a full re-scaffold')
1793
+ .option('--stack <name>', `Which stack's .githooks/ template to install (${Object.keys(STACKS).join(', ')}) — defaults to whichever stack is recorded in install-manifest.json`)
1794
+ .action(async (opts) => {
1795
+ const targetDir = process.cwd();
1796
+
1797
+ let stackKey = opts.stack;
1798
+ if (stackKey && !STACKS[stackKey]) {
1799
+ console.error(`❌ Unknown stack "${stackKey}". Available: ${Object.keys(STACKS).join(', ')}`);
1800
+ process.exit(1);
1801
+ }
1802
+ if (!stackKey) {
1803
+ for (const name of KIT_DIR_NAMES) {
1804
+ const manifest = readJsonSafe(join(targetDir, name, '.eventmodelers', 'install-manifest.json'));
1805
+ if (manifest.stack && STACKS[manifest.stack]) {
1806
+ stackKey = manifest.stack;
1807
+ break;
1808
+ }
1809
+ }
1810
+ }
1811
+ if (!stackKey) {
1812
+ console.error(`❌ Can't tell which stack's .githooks/ template to install — pass --stack <name> (${Object.keys(STACKS).join(', ')}), or run \`init\`/\`re-init\` for one of those stacks first.`);
1813
+ process.exit(1);
1814
+ }
1815
+
1816
+ const hooksSrc = join(__dirname, 'stacks', stackKey, 'templates', 'root', '.githooks');
1817
+ if (!existsSync(hooksSrc)) {
1818
+ console.error(`❌ "${stackKey}" ships no .githooks/ template — nothing to install.`);
1819
+ process.exit(1);
1820
+ }
1821
+
1822
+ console.log('🪝 Configuring git hooks...');
1823
+ configureHooks({ hooksSrc, targetDir });
1824
+ });
1825
+
1826
+ program
1827
+ .command('disable-hooks')
1828
+ .description('Turn off the slice commit-scope guard by unsetting `git config core.hooksPath` — leaves .githooks/ on disk untouched; run `init-hooks` again any time to re-enable')
1829
+ .action(() => {
1830
+ const targetDir = process.cwd();
1831
+
1832
+ try {
1833
+ execSync('git rev-parse --git-dir', { cwd: targetDir, stdio: 'ignore' });
1834
+ } catch {
1835
+ console.error(`❌ ${targetDir} is not a git repository — nothing to unset.`);
1836
+ process.exit(1);
1837
+ }
1838
+
1839
+ let currentHooksPath = null;
1840
+ try {
1841
+ currentHooksPath = execSync('git config --get core.hooksPath', { cwd: targetDir, stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim();
1842
+ } catch {
1843
+ // core.hooksPath isn't set — nothing to do
1844
+ }
1845
+
1846
+ if (!currentHooksPath) {
1847
+ console.log(' ℹ️ core.hooksPath is not set — the guard is already off, nothing to do.');
1848
+ return;
1849
+ }
1850
+
1851
+ execSync('git config --unset core.hooksPath', { cwd: targetDir });
1852
+ console.log(` ✓ Unset core.hooksPath (was "${currentHooksPath}") — the commit-scope guard is now off. Run \`init-hooks\` again to turn it back on.`);
1853
+ });
1854
+
1855
+ program
1856
+ .command('run-checks')
1857
+ .alias('run:checks')
1858
+ .description("Run this project's commit-scope checks (.build-kit/lib/check-commit-scope.cjs, the same runner .githooks/pre-commit calls) against the currently staged changeset. Works for any build-kit stack — stacks/installs that ship no checks yet report that and exit 0, so this is safe to call unconditionally (e.g. from an agent loop) without checking the stack first.")
1859
+ .action(() => {
1860
+ const cwd = process.cwd();
1861
+ // Mirrors `run`'s buildKitDir resolution: modeling-kit/bridge-kit installs share
1862
+ // KIT_DIR_NAMES but never ship check-commit-scope.cjs, so prefer whichever
1863
+ // installed dir isn't one of those over blindly taking the first match.
1864
+ const installedKitDirs = findAllInstalledKitDirs(cwd);
1865
+ const modelingKitDir = installedKitDirs.find((d) => d.endsWith(MODELING_KIT.kitDirName)) ?? null;
1866
+ const bridgeKitDir = installedKitDirs.find((d) => d.endsWith(BRIDGE_KIT.kitDirName)) ?? null;
1867
+ const kitDir = installedKitDirs.find((d) => d !== modelingKitDir && d !== bridgeKitDir) ?? installedKitDirs[0];
1868
+
1869
+ const checkScript = join(kitDir, 'lib', 'check-commit-scope.cjs');
1870
+ if (!existsSync(checkScript)) {
1871
+ console.log(`ℹ️ ${relative(cwd, kitDir)} ships no checks yet — nothing to run.`);
1872
+ return;
1873
+ }
1874
+
1875
+ console.log(`🔎 Running checks from ${relative(cwd, checkScript)}...`);
1876
+ try {
1877
+ execSync(`node "${checkScript}"`, { cwd: kitDir, stdio: 'inherit' });
1878
+ } catch (err) {
1879
+ process.exit(err.status || 1);
1880
+ }
1881
+ });
1882
+
1780
1883
  credentialFlags(program
1781
1884
  .command('init-config')
1782
1885
  .description('Configure credentials only — writes .eventmodelers/config.json in the current directory, or ~/.eventmodelers/config.json with --global')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eventmodelers/cli",
3
- "version": "1.0.49",
3
+ "version": "1.0.51",
4
4
  "description": "Eventmodelers CLI — real-time Claude agent + skills for Claude Code, for any stack (Node, Supabase, Axon, Cratis, OpenCQRS, UmaDB, Kurrent, or modeling-only)",
5
5
  "type": "module",
6
6
  "bin": {
@@ -75,7 +75,7 @@ It loads every check under `.build-kit/lib/checks/` and rejects the commit if an
75
75
  - **tsc-build** — `npx tsc --noEmit` must still pass
76
76
 
77
77
  If a commit is rejected, split it — commit the out-of-scope file separately from the slice work, or add
78
- the missing test/fix the field — rather than passing `--no-verify`. Run `npm run check:scope` any time
78
+ the missing test/fix the field — rather than passing `--no-verify`. Run `npm run run:checks` any time
79
79
  you want to check staged files before committing. To add a new check, read
80
80
  `.build-kit/lib/checks/README.md` and drop in a file following its interface — no other wiring needed.
81
81
 
@@ -23,7 +23,7 @@
23
23
  // SLICE_PATTERN RegExp matching a path inside a slice's own folder
24
24
  //
25
25
  // Zero dependencies — plain Node, so it works from git's pre-commit hook
26
- // (see ../../.githooks/pre-commit), from `npm run check:scope`, or from CI.
26
+ // (see ../../.githooks/pre-commit), from `npm run run:checks`, or from CI.
27
27
  // Invoked as: node .build-kit/lib/check-commit-scope.cjs
28
28
 
29
29
  const { execSync } = require('child_process');
@@ -8,7 +8,7 @@
8
8
  "build": "tsc",
9
9
  "start": "NODE_ENV=production node --env-file=.env --require ts-node/register server.ts",
10
10
  "test": "tsx --test 'src/**/*.test.ts'",
11
- "check:scope": "node .build-kit/lib/check-commit-scope.cjs"
11
+ "run:checks": "node .build-kit/lib/check-commit-scope.cjs"
12
12
  },
13
13
  "dependencies": {
14
14
  "@event-driven-io/emmett": "^0.42.1-alpha.1",
@@ -79,7 +79,7 @@ and rejects the commit if any of them find a problem:
79
79
  - **tsc-build** — `npx tsc --noEmit` must still pass
80
80
 
81
81
  If a commit is rejected, split it — commit the out-of-scope file separately from the slice work, or add
82
- the missing test/fix the field — rather than passing `--no-verify`. Run `npm run check:scope` any time
82
+ the missing test/fix the field — rather than passing `--no-verify`. Run `npm run run:checks` any time
83
83
  you want to check staged files before committing. To add a new check, read
84
84
  `.build-kit/lib/checks/README.md` and drop in a file following its interface — no other wiring needed.
85
85
 
@@ -23,7 +23,7 @@
23
23
  // SLICE_PATTERN RegExp matching a path inside a slice's own folder
24
24
  //
25
25
  // Zero dependencies — plain Node, so it works from git's pre-commit hook
26
- // (see ../../.githooks/pre-commit), from `npm run check:scope`, or from CI.
26
+ // (see ../../.githooks/pre-commit), from `npm run run:checks`, or from CI.
27
27
  // Invoked as: node .build-kit/lib/check-commit-scope.cjs
28
28
 
29
29
  const { execSync } = require('child_process');
@@ -8,7 +8,7 @@
8
8
  "build": "tsc",
9
9
  "start": "NODE_ENV=production node --env-file=.env --require ts-node/register server.ts",
10
10
  "test": "tsx --test 'src/**/*.test.ts'",
11
- "check:scope": "node .build-kit/lib/check-commit-scope.cjs"
11
+ "run:checks": "node .build-kit/lib/check-commit-scope.cjs"
12
12
  },
13
13
  "dependencies": {
14
14
  "@event-driven-io/emmett": "^0.42.1-alpha.1",