delimit-cli 3.14.15 → 3.14.17

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.
@@ -322,14 +322,14 @@ function installClaudeHooks(tool, hookConfig) {
322
322
  if (!existingSecurity) {
323
323
  config.hooks.PreToolUse.push({
324
324
  matcher: 'Bash',
325
- if: "command matches 'npm publish' or command matches 'npx deploy' or command matches 'deploy' or command matches 'release'",
325
+ if: "command matches 'npm publish' or command matches 'npx deploy' or command matches 'deploy' or command matches 'release' or command matches 'docker compose up' or command matches 'docker-compose up' or command matches 'docker build'",
326
326
  hooks: [{
327
327
  type: 'command',
328
- command: `${npxCmd} security-audit`,
328
+ command: `${npxCmd} hook deploy-gate`,
329
329
  timeout: 30,
330
330
  }],
331
331
  });
332
- changes.push('PreToolUse:deploy-audit');
332
+ changes.push('PreToolUse:deploy-gate');
333
333
  }
334
334
  }
335
335
  }
@@ -937,6 +937,97 @@ async function hookPreCommit() {
937
937
 
938
938
  // ---------------------------------------------------------------------------
939
939
  // Exports
940
+ // ---------------------------------------------------------------------------
941
+ // Deploy gate hook — runs smoke test before any deploy (LED-024 feedback)
942
+ // ---------------------------------------------------------------------------
943
+
944
+ async function hookDeployGate() {
945
+ const lines = [];
946
+ lines.push('[Delimit] Deploy gate check');
947
+ lines.push('');
948
+
949
+ let blocked = false;
950
+
951
+ // 1. Check for common import/syntax errors
952
+ const cwd = process.cwd();
953
+ const hasDockerCompose = fs.existsSync(path.join(cwd, 'docker-compose.yml'))
954
+ || fs.existsSync(path.join(cwd, 'docker-compose.yaml'))
955
+ || fs.existsSync(path.join(cwd, 'compose.yml'));
956
+
957
+ // 2. Check for Python import errors if it's a Python project
958
+ const hasPython = fs.existsSync(path.join(cwd, 'requirements.txt'))
959
+ || fs.existsSync(path.join(cwd, 'pyproject.toml'))
960
+ || fs.existsSync(path.join(cwd, 'setup.py'));
961
+
962
+ if (hasPython) {
963
+ try {
964
+ // Find the main app module
965
+ const appDirs = ['app', 'src', 'api'];
966
+ for (const dir of appDirs) {
967
+ const initFile = path.join(cwd, dir, '__init__.py');
968
+ const mainFile = path.join(cwd, dir, 'main.py');
969
+ if (fs.existsSync(initFile) || fs.existsSync(mainFile)) {
970
+ try {
971
+ execSync(`python3 -c "import ${dir}" 2>&1`, {
972
+ encoding: 'utf-8',
973
+ timeout: 10000,
974
+ cwd,
975
+ });
976
+ lines.push(`[Delimit] ✓ ${dir}/ imports clean`);
977
+ } catch (e) {
978
+ lines.push(`[Delimit] ✗ ${dir}/ import error: ${e.stdout || e.stderr || e.message}`);
979
+ blocked = true;
980
+ }
981
+ }
982
+ }
983
+ } catch { /* ignore */ }
984
+ }
985
+
986
+ // 3. Check for Node.js syntax errors
987
+ const hasNode = fs.existsSync(path.join(cwd, 'package.json'));
988
+ if (hasNode) {
989
+ try {
990
+ execSync('node -e "require(\'./\')" 2>&1', {
991
+ encoding: 'utf-8',
992
+ timeout: 5000,
993
+ cwd,
994
+ });
995
+ lines.push('[Delimit] ✓ Node.js entry point loads');
996
+ } catch {
997
+ // Not all projects have a main entry — skip silently
998
+ }
999
+ }
1000
+
1001
+ // 4. Check for uncommitted changes
1002
+ try {
1003
+ const status = execSync('git status --porcelain 2>/dev/null', {
1004
+ encoding: 'utf-8',
1005
+ timeout: 3000,
1006
+ cwd,
1007
+ }).trim();
1008
+ if (status) {
1009
+ const fileCount = status.split('\n').length;
1010
+ lines.push(`[Delimit] ⚠ ${fileCount} uncommitted file(s) — consider committing before deploy`);
1011
+ }
1012
+ } catch { /* not a git repo */ }
1013
+
1014
+ // 5. Result
1015
+ lines.push('');
1016
+ if (blocked) {
1017
+ lines.push('[Delimit] ✗ DEPLOY BLOCKED — fix import errors above');
1018
+ lines.push('[Delimit] Run: delimit_test_smoke for full diagnostics');
1019
+ } else {
1020
+ lines.push('[Delimit] ✓ Deploy gate passed');
1021
+ }
1022
+ lines.push('');
1023
+
1024
+ process.stdout.write(lines.join('\n') + '\n');
1025
+
1026
+ if (blocked) {
1027
+ process.exit(1);
1028
+ }
1029
+ }
1030
+
940
1031
  // ---------------------------------------------------------------------------
941
1032
 
942
1033
  module.exports = {
@@ -954,6 +1045,7 @@ module.exports = {
954
1045
  hookSessionStart,
955
1046
  hookPreTool,
956
1047
  hookPreCommit,
1048
+ hookDeployGate,
957
1049
  countPendingStrategyItems,
958
1050
  getTopStrategyItem,
959
1051
  findClaudeHookGroup,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "delimit-cli",
3
3
  "mcpName": "io.github.delimit-ai/delimit-mcp-server",
4
- "version": "3.14.15",
4
+ "version": "3.14.17",
5
5
  "description": "Unify Claude Code, Codex, Cursor, and Gemini CLI with persistent context, governance, and multi-model debate.",
6
6
  "main": "index.js",
7
7
  "files": [