@nerviq/cli 1.17.2 → 1.17.3

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
@@ -223,7 +223,7 @@ All successful operational responses are wrapped in a JSON envelope:
223
223
  {
224
224
  "data": {},
225
225
  "meta": {
226
- "version": "1.17.2",
226
+ "version": "1.17.3",
227
227
  "timestamp": "2026-04-12T12:00:00.000Z"
228
228
  }
229
229
  }
package/bin/cli.js CHANGED
@@ -1,4 +1,13 @@
1
- #!/usr/bin/env node
1
+ // macOS pipe-flush guard: when stdout is a pipe, Node defaults to
2
+ // non-blocking writes. `console.log(...) + process.exit(N)` then drops
3
+ // the trailing write (empty stdout on macOS Node 18, truncation at the
4
+ // 8192-byte pipe buffer on macOS Node 20). Flipping stdout+stderr to
5
+ // blocking mode at the *very start* — before any writes queue up —
6
+ // makes every subsequent console.log a synchronous syscall, so the
7
+ // data is in the kernel before we ever reach process.exit. Runs before
8
+ // require() so it covers warnings from Node's module loader too.
9
+ try { if (process.stdout?._handle?.setBlocking) process.stdout._handle.setBlocking(true); } catch {}
10
+ try { if (process.stderr?._handle?.setBlocking) process.stderr._handle.setBlocking(true); } catch {}
2
11
 
3
12
  const { audit, detectPlatforms, getCatalog } = require('../src/public-api');
4
13
  const { setup } = require('../src/setup');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nerviq/cli",
3
- "version": "1.17.2",
3
+ "version": "1.17.3",
4
4
  "description": "The intelligent nervous system for AI coding agents — 2,441 checks (8 platforms × ~300 governance rules), 10 languages, 62 domain packs. Audit, align, and amplify.",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -173,7 +173,10 @@ function findFillerLine(content) {
173
173
 
174
174
  function hasContradictions(content) {
175
175
  const lines = content.split(/\r?\n/);
176
+ const lineEndingPattern = /\b(CRLF|LF|line[- ]ending|trailing newline|EOF|end.of.file|newline at end)\b/i;
176
177
  for (const line of lines) {
178
+ // Skip line-ending/EOF style guidance — not actual contradictions
179
+ if (lineEndingPattern.test(line)) continue;
177
180
  if (/\balways\b.*\bnever\b|\bnever\b.*\balways\b/i.test(line)) {
178
181
  return true;
179
182
  }
@@ -280,10 +283,20 @@ function unsupportedHookEvent(ctx) {
280
283
  }
281
284
 
282
285
  function hooksClaimed(ctx) {
286
+ // Strong signals: Codex-specific files/directories
283
287
  if (ctx.hasDir('.codex/hooks')) return true;
284
288
  if (ctx.hooksJsonContent && ctx.hooksJsonContent()) return true;
289
+ const config = ctx.fileContent('.codex/config.toml') || '';
290
+ if (/\[hooks\]|codex_hooks\s*=|\[features\][\s\S]*codex_hooks/i.test(config)) return true;
291
+ // Text signals: require Codex-specific hook terminology (not generic "hooks")
285
292
  const content = agentsContent(ctx);
286
- return /\bhooks?\b|\bSessionStart\b|\bPreToolUse\b|\bPostToolUse\b|\bUserPromptSubmit\b|\bStop\b/i.test(content);
293
+ if (!content) return false;
294
+ // Specific Codex hook events — these are unambiguous
295
+ if (/\b(SessionStart|PreToolUse|PostToolUse|UserPromptSubmit)\b/.test(content)) return true;
296
+ // "Codex hooks" explicit mention
297
+ if (/\bcodex\s+hooks?\b/i.test(content)) return true;
298
+ if (/\bhooks?\.json\b/i.test(content) && /\bcodex\b/i.test(content)) return true;
299
+ return false;
287
300
  }
288
301
 
289
302
  function findSecretLine(content) {
@@ -1422,15 +1435,15 @@ function desktopProjectMcpCaveatIssue(ctx) {
1422
1435
  const CODEX_TECHNIQUES = {
1423
1436
  codexAgentsMd: {
1424
1437
  id: 'CX-A01',
1425
- name: 'AGENTS.md exists at project root',
1426
- check: (ctx) => Boolean(ctx.fileContent('AGENTS.md')),
1438
+ name: 'AGENTS.md exists at project root or .codex/',
1439
+ check: (ctx) => Boolean(ctx.fileContent('AGENTS.md') || ctx.fileContent('.codex/AGENTS.md') || ctx.fileContent('.codex/agents.md')),
1427
1440
  impact: 'critical',
1428
1441
  rating: 5,
1429
1442
  category: 'instructions',
1430
- fix: 'Create AGENTS.md at the project root with repo-specific commands, trust guidance, and workflow expectations.',
1443
+ fix: 'Create AGENTS.md at the project root (or .codex/AGENTS.md) with repo-specific commands, trust guidance, and workflow expectations.',
1431
1444
  template: 'codex-agents-md',
1432
- file: () => 'AGENTS.md',
1433
- line: (ctx) => (ctx.fileContent('AGENTS.md') ? 1 : null),
1445
+ file: (ctx) => ctx.fileContent('AGENTS.md') ? 'AGENTS.md' : (ctx.fileContent('.codex/AGENTS.md') ? '.codex/AGENTS.md' : 'AGENTS.md'),
1446
+ line: (ctx) => (ctx.fileContent('AGENTS.md') || ctx.fileContent('.codex/AGENTS.md') ? 1 : null),
1434
1447
  },
1435
1448
  codexAgentsMdSubstantive: {
1436
1449
  id: 'CX-A02',
@@ -3111,8 +3124,14 @@ const CODEX_TECHNIQUES = {
3111
3124
  'Gemfile', 'composer.json', 'pom.xml', 'build.gradle',
3112
3125
  'flake.nix', 'shard.yml', 'mix.exs', 'rebar.config',
3113
3126
  'Makefile', 'CMakeLists.txt', 'Package.swift', 'pubspec.yaml',
3127
+ // .NET ecosystem
3128
+ 'Directory.Packages.props', 'Directory.Build.props', 'global.json',
3129
+ // Gradle wrapper
3130
+ 'gradlew', 'gradlew.bat',
3114
3131
  ];
3115
- const hasManifest = manifestFiles.some(f => ctx.files.includes(f));
3132
+ const hasManifest = manifestFiles.some(f => ctx.files.includes(f)) ||
3133
+ // .NET solution/project files use extensions — match by suffix
3134
+ ctx.files.some(f => /\.(sln|slnx|csproj|fsproj|vbproj)$/i.test(f));
3116
3135
  // Dotfiles/config-only repos: they don't ship code, so pack recommendations
3117
3136
  // aren't meaningful — N/A is the correct answer.
3118
3137
  const dotfilesSignals = ['.zshrc', '.bashrc', '.vimrc', '.tmux.conf', '.gitconfig', 'install.sh', 'bootstrap.sh'];