mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-03 02:21:44 +00:00
* fix(claude): skip augment hook when server owns db * chore(autofix): apply prettier + eslint fixes via /autofix command * fix(hooks): cross-platform DB lock probe for MCP owner guard Extract hook-db-lock-probe.cjs with a single hasGitNexusDbLockedByGitNexusServer entry point used by both Claude hooks: - Linux: scan /proc/<pid>/fd via dev+inode (no lsof required), optional lsof fallback; GITNEXUS_HOOK_LINUX_PROC_BUDGET_MS caps scan time - macOS and other Unix: trusted lsof + ps (absolute paths / env overrides) - Windows: Restart Manager + Win32_Process via win-rm-list-json.ps1 and GITNEXUS_HOOK_POWERSHELL_PATH Update hooks.test.ts source coverage for the probe module. Co-authored-by: Cursor <cursoragent@cursor.com> * Update gitnexus/hooks/claude/win-rm-list-json.ps1 Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Apply suggestion from @github-actions[bot] Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * fix(gitnexus): repair package.json JSON after malformed engines edit Co-authored-by: Cursor <cursoragent@cursor.com> * Update Node.js engine version requirement to 22.0.0 * Update Node.js engine version to >=22.0.0 * fix(hooks): address ce-code-review findings on PR #1493 P0: - Replace malformed `RM_UNIQUE_PROCESS` block in `gitnexus/hooks/claude/win-rm-list-json.ps1` (duplicate struct decl + duplicate `ProcessStartTime` + unbalanced braces) with a single well-formed `[StructLayout(LayoutKind.Sequential, Pack = 4)]` struct, so PowerShell `Add-Type` actually compiles and the Windows DB-lock probe stops fail-open on every machine. - `gitnexus/src/cli/setup.ts` now copies `hook-db-lock-probe.cjs` and `win-rm-list-json.ps1` into the user's `~/.claude/hooks/gitnexus/` alongside `hook-lock.cjs`, preventing the `MODULE_NOT_FOUND` thrown by `gitnexus-hook.cjs:18`'s top-level require on every fresh install. `gitnexus/test/unit/setup.test.ts` extended to assert both new copy destinations. - Four fail-open hook tests (`ENOENT lsof`, `npx parent line`, `non-GitNexus ps line`, `ps ENOENT`) now seed `createHookToolDir` with a valid `[GitNexus]` stderr line so `expect(parseHookOutput).not.toBeNull()` actually holds on CI. P1: - Plugin copy of `win-rm-list-json.ps1` gains `Pack = 4` so its CLR struct matches the 12-byte native `RM_UNIQUE_PROCESS` layout (multi-blocker `RmGetList` no longer reads mangled `dwProcessId`). - `GITNEXUS_HOOK_CLI_PATH = ''` now falls through to the resolution chain in `gitnexus-hook.cjs`, matching the plugin copy and removing the twin-file divergence on empty-string envs. - Lock-warning suppression test seeds `gitnexusMarkerPath` and asserts the augment subprocess actually ran, plus `GITNEXUS_DEBUG=1` preserves the full discarded prefix. - MCP-owner skip branch in both hook copies now emits `[GitNexus] augment skipped: MCP server owns DB` on stderr, so agents can distinguish intentional skip from silent failure. P2: - `ps` loop in `hook-db-lock-probe.cjs` fails-closed on `ETIMEDOUT` to mirror the `lsof` handling (symmetric subprocess-probe contract). - `RmStartSession` return value captured in both `.ps1` copies; exits early with `[]` on non-zero so subsequent RM API calls don't operate on an invalid handle. - Windows RM-list `.ps1` encoded cache distinguishes uninitialized (`undefined`) from load-failed (`null`) with a one-shot `GITNEXUS_DEBUG` warning instead of silently caching empty string. - `createHookToolDir` helper accepts `lsofOutputLines` and `psOutputByPid`; the multi-PID test uses them instead of duplicating the fake-binary construction inline. - All five skip-path tests now assert `result.status === 0` and the new skip-signal stderr line. - `AGENTS.md` documents the seven hook configuration env vars (`GITNEXUS_HOOK_CLI_PATH`, `_LSOF_PATH`, `_PS_PATH`, `_POWERSHELL_PATH`, `_LINUX_PROC_BUDGET_MS`, `_RM_TARGET`, `GITNEXUS_DEBUG`). - `GITNEXUS_DEBUG` path in `gitnexus-hook.cjs`/`.js` writes the full discarded stderr prefix instead of a 180-char preview. - Inline comment in `hook-db-lock-probe.cjs` explains the intentional Windows ETIMEDOUT fail-closed semantics. - Removed the unnecessary `as WriteFileOptions` cast and orphaned `import type { WriteFileOptions }` in `hooks.test.ts`. P3: - `isGitNexusServerCommand` unexported from `hook-db-lock-probe.cjs` (kept as private helper). - Env-path overrides (`GITNEXUS_HOOK_CLI_PATH`, `_POWERSHELL_PATH`, `_LSOF_PATH`, `_PS_PATH`) require `fs.existsSync` before being returned, so typos / stale config fall through to the standard resolution chain. Misc: - `gitnexus/package.json` engines.node back to `>=22.0.0` (matches origin/main and the original PR reviewer's earlier request). Twin-tree parity / CI sync mechanism tracked separately at abhigyanpatwari/GitNexus#1591. Test plan: vitest run test/unit/hooks.test.ts → 113 passed, 18 Unix-only skipped; setup.test.ts → 14 passed. * chore(autofix): apply prettier + eslint fixes via /autofix command * trigger --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> Co-authored-by: Cursor <cursoragent@cursor.com>
76 lines
3 KiB
PowerShell
76 lines
3 KiB
PowerShell
$ErrorActionPreference = 'Stop'
|
|
$target = $env:GITNEXUS_HOOK_RM_TARGET
|
|
if ([string]::IsNullOrWhiteSpace($target)) { Write-Output '[]'; exit 0 }
|
|
$target = (Resolve-Path -LiteralPath $target).ProviderPath
|
|
|
|
if (-not ([Management.Automation.PSTypeName]'GitNexusHookRm.Native').Type) {
|
|
Add-Type @'
|
|
using System;
|
|
using System.Runtime.InteropServices;
|
|
namespace GitNexusHookRm {
|
|
public static class Native {
|
|
public const int ErrorMoreData = 234;
|
|
[StructLayout(LayoutKind.Sequential, Pack = 4)]
|
|
public struct RM_UNIQUE_PROCESS {
|
|
public int dwProcessId;
|
|
public long ProcessStartTime;
|
|
}
|
|
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
|
public struct RM_PROCESS_INFO {
|
|
public RM_UNIQUE_PROCESS Process;
|
|
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
|
|
public string strAppName;
|
|
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
|
|
public string strServiceShortName;
|
|
public uint ApplicationType;
|
|
public uint AppStatus;
|
|
public uint TSSessionId;
|
|
public uint bRestartable;
|
|
}
|
|
[DllImport("rstrtmgr.dll", CharSet = CharSet.Unicode)]
|
|
public static extern int RmStartSession(out uint pSessionHandle, uint dwSessionFlags, string strSessionKey);
|
|
[DllImport("rstrtmgr.dll", CharSet = CharSet.Unicode)]
|
|
public static extern int RmRegisterResources(uint pSessionHandle, uint nFiles, string[] rgsFileNames, uint nApplications, IntPtr rgApplications, uint nServices, string[] rgsServiceNames);
|
|
[DllImport("rstrtmgr.dll")]
|
|
public static extern int RmGetList(uint dwSessionHandle, out uint pnProcInfoNeeded, ref uint pnProcInfo, [In, Out] RM_PROCESS_INFO[] rgAffectedApps, ref uint lpdwRebootReasons);
|
|
[DllImport("rstrtmgr.dll")]
|
|
public static extern int RmEndSession(uint pSessionHandle);
|
|
}
|
|
}
|
|
'@
|
|
}
|
|
|
|
$h = [uint32]0
|
|
$key = [guid]::NewGuid().ToString('N')
|
|
$rmErr = [GitNexusHookRm.Native]::RmStartSession([ref]$h, 0, $key)
|
|
if ($rmErr -ne 0) { Write-Output '[]'; exit 0 }
|
|
$files = @($target)
|
|
$err = [GitNexusHookRm.Native]::RmRegisterResources($h, 1, $files, 0, [IntPtr]::Zero, 0, $null)
|
|
if ($err -ne 0) {
|
|
[void][GitNexusHookRm.Native]::RmEndSession($h)
|
|
Write-Output '[]'
|
|
exit 0
|
|
}
|
|
$need = [uint32]0
|
|
$n = [uint32]0
|
|
$reboot = [uint32]0
|
|
$err = [GitNexusHookRm.Native]::RmGetList($h, [ref]$need, [ref]$n, $null, [ref]$reboot)
|
|
if ($err -ne [GitNexusHookRm.Native]::ErrorMoreData) {
|
|
[void][GitNexusHookRm.Native]::RmEndSession($h)
|
|
Write-Output '[]'
|
|
exit 0
|
|
}
|
|
$n = $need
|
|
$buf = New-Object GitNexusHookRm.Native+RM_PROCESS_INFO[] ([int]$n)
|
|
$err = [GitNexusHookRm.Native]::RmGetList($h, [ref]$need, [ref]$n, $buf, [ref]$reboot)
|
|
[void][GitNexusHookRm.Native]::RmEndSession($h)
|
|
if ($err -ne 0) { Write-Output '[]'; exit 0 }
|
|
|
|
$out = @()
|
|
for ($i = 0; $i -lt [int]$n; $i++) {
|
|
$procId = $buf[$i].Process.dwProcessId
|
|
$p = Get-CimInstance -ClassName Win32_Process -Filter "ProcessId=$procId" -ErrorAction SilentlyContinue
|
|
$cmd = if ($p) { $p.CommandLine } else { '' }
|
|
$out += [PSCustomObject]@{ pid = [int]$procId; cmd = $cmd }
|
|
}
|
|
ConvertTo-Json -InputObject @($out) -Compress
|