From cf260da6a8888e005c0ef31768b655119af962c7 Mon Sep 17 00:00:00 2001 From: Gergo Magyar Date: Sat, 28 Mar 2026 14:30:17 +0000 Subject: [PATCH 1/6] feat: configure prettier with pre-commit hook integration Add prettier, lint-staged, and prettier-plugin-tailwindcss at the repo root with husky pre-commit hook integration. Moves husky from gitnexus/ to root package.json for reliable hook installation. - Root package.json with prepare/format/format:check scripts - .prettierrc with endOfLine:lf and tailwindStylesheet for TW v4 - .prettierignore excluding fixtures, vendor, generated, *.d.ts, *.md - .gitattributes enforcing LF line endings for Windows consistency - Pre-commit hook uses direct node_modules/.bin/ paths (no npx) --- .gitattributes | 2 + .husky/pre-commit | 15 +- .prettierignore | 16 + .prettierrc | 10 + gitnexus/package.json | 1 - package-lock.json | 879 +++++++++++++++++++++++++++++++++++++++++- package.json | 18 + 7 files changed, 933 insertions(+), 8 deletions(-) create mode 100644 .gitattributes create mode 100644 .prettierignore create mode 100644 .prettierrc create mode 100644 package.json diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..5e9d18bf44 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +* text=auto eol=lf +.husky/* text eol=lf diff --git a/.husky/pre-commit b/.husky/pre-commit index 36047caa52..14a145c0d3 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Pre-commit hook (husky): typecheck + unit tests for both packages. +# Pre-commit hook: format staged files, then typecheck + unit tests. # Mirrors CI checks from ci-quality.yml and ci-tests.yml. # Skip with: git commit --no-verify # @@ -11,23 +11,28 @@ ROOT="$(git rev-parse --show-toplevel)" +# 1. Format staged files with prettier via lint-staged +echo "pre-commit: formatting staged files..." +"$ROOT/node_modules/.bin/lint-staged" || exit 1 + +# 2. Typecheck + test changed packages WEB_CHANGED=$(git diff --cached --name-only -- 'gitnexus-web/' | head -1) CLI_CHANGED=$(git diff --cached --name-only -- 'gitnexus/' | head -1) if [ -n "$WEB_CHANGED" ]; then echo "pre-commit: typechecking gitnexus-web (tsc -b)..." - cd "$ROOT/gitnexus-web" && npx tsc -b --noEmit + cd "$ROOT/gitnexus-web" && ./node_modules/.bin/tsc -b --noEmit || exit 1 echo "pre-commit: running gitnexus-web unit tests..." - npx vitest run --reporter=dot + ./node_modules/.bin/vitest run --reporter=dot || exit 1 fi if [ -n "$CLI_CHANGED" ]; then echo "pre-commit: typechecking gitnexus..." - cd "$ROOT/gitnexus" && npx tsc --noEmit + cd "$ROOT/gitnexus" && ./node_modules/.bin/tsc --noEmit || exit 1 echo "pre-commit: running gitnexus unit tests (default project)..." - npx vitest run --project default --reporter=dot + ./node_modules/.bin/vitest run --project default --reporter=dot || exit 1 fi echo "pre-commit: all checks passed" diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000000..8c43748f44 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,16 @@ +dist/ +coverage/ +gitnexus/vendor/ +gitnexus/test/fixtures/ +gitnexus-web/playwright-report/ +gitnexus-web/test-results/ +*.d.ts +*.snap +*.wasm +*.md +.gitnexus/ +.vercel/ +.claude-flow/ +.swarm/ +assets/ +repomix-output* diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000000..376b0b3c4e --- /dev/null +++ b/.prettierrc @@ -0,0 +1,10 @@ +{ + "semi": true, + "singleQuote": true, + "trailingComma": "all", + "printWidth": 100, + "tabWidth": 2, + "endOfLine": "lf", + "plugins": ["prettier-plugin-tailwindcss"], + "tailwindStylesheet": "./gitnexus-web/src/index.css" +} diff --git a/gitnexus/package.json b/gitnexus/package.json index 2705527826..c778650261 100644 --- a/gitnexus/package.json +++ b/gitnexus/package.json @@ -93,7 +93,6 @@ "@types/node": "^20.0.0", "@types/uuid": "^10.0.0", "@vitest/coverage-v8": "^4.0.18", - "husky": "^9.1.7", "tsx": "^4.0.0", "typescript": "^5.4.5", "vitest": "^4.0.18" diff --git a/package-lock.json b/package-lock.json index 9c0b3071ff..96c003e5ef 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,881 @@ { - "name": "GitNexus", + "name": "gitnexus-monorepo", "lockfileVersion": 3, "requires": true, - "packages": {} + "packages": { + "": { + "name": "gitnexus-monorepo", + "devDependencies": { + "husky": "^9.1.7", + "lint-staged": "^15.5.0", + "prettier": "^3.8.0", + "prettier-plugin-tailwindcss": "^0.7.0" + } + }, + "node_modules/ansi-escapes": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.3.0.tgz", + "integrity": "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", + "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", + "dev": true, + "license": "MIT", + "dependencies": { + "slice-ansi": "^5.0.0", + "string-width": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.5.0.tgz", + "integrity": "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/husky": { + "version": "9.1.7", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz", + "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==", + "dev": true, + "license": "MIT", + "bin": { + "husky": "bin.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lint-staged": { + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.5.2.tgz", + "integrity": "sha512-YUSOLq9VeRNAo/CTaVmhGDKG+LBtA8KF1X4K5+ykMSwWST1vDxJRB2kv2COgLb1fvpCo+A/y9A0G0znNVmdx4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.4.1", + "commander": "^13.1.0", + "debug": "^4.4.0", + "execa": "^8.0.1", + "lilconfig": "^3.1.3", + "listr2": "^8.2.5", + "micromatch": "^4.0.8", + "pidtree": "^0.6.0", + "string-argv": "^0.3.2", + "yaml": "^2.7.0" + }, + "bin": { + "lint-staged": "bin/lint-staged.js" + }, + "engines": { + "node": ">=18.12.0" + }, + "funding": { + "url": "https://opencollective.com/lint-staged" + } + }, + "node_modules/listr2": { + "version": "8.3.3", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.3.3.tgz", + "integrity": "sha512-LWzX2KsqcB1wqQ4AHgYb4RsDXauQiqhjLk+6hjbaeHG4zpjjVAB6wC/gz6X0l+Du1cN3pUB5ZlrvTbhGSNnUQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "cli-truncate": "^4.0.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^6.1.0", + "rfdc": "^1.4.1", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/log-update": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", + "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^7.0.0", + "cli-cursor": "^5.0.0", + "slice-ansi": "^7.1.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/is-fullwidth-code-point": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz", + "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.3.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/slice-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz", + "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pidtree": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", + "dev": true, + "license": "MIT", + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/prettier": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", + "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-plugin-tailwindcss": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.7.2.tgz", + "integrity": "sha512-LkphyK3Fw+q2HdMOoiEHWf93fNtYJwfamoKPl7UwtjFQdei/iIBoX11G6j706FzN3ymX9mPVi97qIY8328vdnA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.19" + }, + "peerDependencies": { + "@ianvs/prettier-plugin-sort-imports": "*", + "@prettier/plugin-hermes": "*", + "@prettier/plugin-oxc": "*", + "@prettier/plugin-pug": "*", + "@shopify/prettier-plugin-liquid": "*", + "@trivago/prettier-plugin-sort-imports": "*", + "@zackad/prettier-plugin-twig": "*", + "prettier": "^3.0", + "prettier-plugin-astro": "*", + "prettier-plugin-css-order": "*", + "prettier-plugin-jsdoc": "*", + "prettier-plugin-marko": "*", + "prettier-plugin-multiline-arrays": "*", + "prettier-plugin-organize-attributes": "*", + "prettier-plugin-organize-imports": "*", + "prettier-plugin-sort-imports": "*", + "prettier-plugin-svelte": "*" + }, + "peerDependenciesMeta": { + "@ianvs/prettier-plugin-sort-imports": { + "optional": true + }, + "@prettier/plugin-hermes": { + "optional": true + }, + "@prettier/plugin-oxc": { + "optional": true + }, + "@prettier/plugin-pug": { + "optional": true + }, + "@shopify/prettier-plugin-liquid": { + "optional": true + }, + "@trivago/prettier-plugin-sort-imports": { + "optional": true + }, + "@zackad/prettier-plugin-twig": { + "optional": true + }, + "prettier-plugin-astro": { + "optional": true + }, + "prettier-plugin-css-order": { + "optional": true + }, + "prettier-plugin-jsdoc": { + "optional": true + }, + "prettier-plugin-marko": { + "optional": true + }, + "prettier-plugin-multiline-arrays": { + "optional": true + }, + "prettier-plugin-organize-attributes": { + "optional": true + }, + "prettier-plugin-organize-imports": { + "optional": true + }, + "prettier-plugin-sort-imports": { + "optional": true + }, + "prettier-plugin-svelte": { + "optional": true + } + } + }, + "node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/restore-cursor/node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "dev": true, + "license": "MIT" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/yaml": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz", + "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + } + } } diff --git a/package.json b/package.json new file mode 100644 index 0000000000..e70c2ec18b --- /dev/null +++ b/package.json @@ -0,0 +1,18 @@ +{ + "name": "gitnexus-monorepo", + "private": true, + "scripts": { + "prepare": "husky", + "format": "prettier --write .", + "format:check": "prettier --check ." + }, + "devDependencies": { + "husky": "^9.1.7", + "lint-staged": "^15.5.0", + "prettier": "^3.8.0", + "prettier-plugin-tailwindcss": "^0.7.0" + }, + "lint-staged": { + "*.{ts,tsx,js,jsx,mjs,json,css,yml,yaml}": "prettier --write" + } +} From afcc3d1523f99c77ff67c4fd1af12334660113f6 Mon Sep 17 00:00:00 2001 From: Gergo Magyar Date: Sat, 28 Mar 2026 14:30:37 +0000 Subject: [PATCH 2/6] style: apply prettier formatting to entire codebase One-time bulk format. No logic changes. Use .git-blame-ignore-revs to skip this commit in git blame. --- .github/release.yml | 2 +- .github/workflows/ci-report.yml | 6 +- .github/workflows/claude.yml | 2 +- eval/configs/models/claude-haiku.yaml | 4 +- eval/configs/models/claude-opus.yaml | 4 +- eval/configs/models/claude-sonnet.yaml | 4 +- eval/configs/models/deepseek-chat.yaml | 6 +- eval/configs/models/deepseek-v3.yaml | 6 +- eval/configs/models/glm-4.7.yaml | 4 +- eval/configs/models/glm-5.yaml | 4 +- eval/configs/models/minimax-2.5.yaml | 4 +- eval/configs/models/minimax-m2.1.yaml | 4 +- eval/configs/modes/baseline.yaml | 6 +- eval/configs/modes/native.yaml | 6 +- eval/configs/modes/native_augment.yaml | 6 +- gitnexus-claude-plugin/hooks/gitnexus-hook.js | 85 +- gitnexus-shared/src/index.ts | 10 +- gitnexus-shared/src/language-detection.ts | 100 +- gitnexus-shared/src/lbug/schema-constants.ts | 58 +- gitnexus-web/e2e/debug-issues.spec.ts | 12 +- gitnexus-web/e2e/manual-record.spec.ts | 6 +- gitnexus-web/e2e/onboarding.spec.ts | 40 +- gitnexus-web/e2e/server-connect.spec.ts | 31 +- gitnexus-web/index.html | 9 +- gitnexus-web/playwright.config.ts | 5 +- gitnexus-web/src/App.tsx | 154 +- .../src/components/AnalyzeOnboarding.tsx | 33 +- .../src/components/AnalyzeProgress.tsx | 21 +- .../src/components/CodeReferencesPanel.tsx | 462 +++-- gitnexus-web/src/components/DropZone.tsx | 78 +- .../src/components/EmbeddingStatus.tsx | 65 +- gitnexus-web/src/components/FileTreePanel.tsx | 267 +-- gitnexus-web/src/components/GraphCanvas.tsx | 187 +- gitnexus-web/src/components/Header.tsx | 204 +- gitnexus-web/src/components/HelpPanel.tsx | 979 ++++++--- .../src/components/LoadingOverlay.tsx | 39 +- .../src/components/MarkdownRenderer.tsx | 397 ++-- .../src/components/MermaidDiagram.tsx | 71 +- .../src/components/OnboardingGuide.tsx | 154 +- .../src/components/ProcessFlowModal.tsx | 551 ++--- .../src/components/ProcessesPanel.tsx | 955 ++++----- gitnexus-web/src/components/QueryFAB.tsx | 175 +- gitnexus-web/src/components/RepoAnalyzer.tsx | 214 +- gitnexus-web/src/components/RightPanel.tsx | 335 ++-- gitnexus-web/src/components/SettingsPanel.tsx | 511 +++-- gitnexus-web/src/components/StatusBar.tsx | 33 +- gitnexus-web/src/components/ToolCallCard.tsx | 67 +- .../src/components/WebGPUFallbackDialog.tsx | 72 +- .../settings/ProviderConfigCard.tsx | 24 +- gitnexus-web/src/config/ignore-service.ts | 451 +++-- gitnexus-web/src/core/graph/graph.ts | 17 +- .../src/core/ingestion/cluster-enricher.ts | 80 +- gitnexus-web/src/core/llm/agent.ts | 117 +- gitnexus-web/src/core/llm/context-builder.ts | 130 +- gitnexus-web/src/core/llm/index.ts | 2 +- gitnexus-web/src/core/llm/settings-service.ts | 38 +- gitnexus-web/src/core/llm/tools.ts | 653 +++--- gitnexus-web/src/core/llm/types.ts | 47 +- gitnexus-web/src/hooks/app-state/graph.tsx | 45 +- gitnexus-web/src/hooks/useAppState.tsx | 1132 ++++++----- gitnexus-web/src/hooks/useBackend.ts | 7 +- gitnexus-web/src/hooks/useSettings.ts | 4 +- gitnexus-web/src/hooks/useSigma.ts | 196 +- gitnexus-web/src/index.css | 23 +- gitnexus-web/src/lib/constants.ts | 138 +- gitnexus-web/src/lib/graph-adapter.ts | 134 +- gitnexus-web/src/lib/grounding-patterns.ts | 6 +- gitnexus-web/src/lib/mermaid-generator.ts | 46 +- gitnexus-web/src/lib/path-resolution.ts | 10 +- gitnexus-web/src/lib/utils.ts | 4 +- gitnexus-web/src/main.tsx | 2 +- gitnexus-web/src/services/backend-client.ts | 125 +- gitnexus-web/src/vendor/leiden/index.js | 557 +++-- gitnexus-web/src/vendor/leiden/utils.js | 538 +++-- gitnexus-web/test/fixtures/graph.ts | 6 +- gitnexus-web/test/unit/bug-fixes.test.ts | 19 +- gitnexus-web/test/unit/constants.test.ts | 2 +- gitnexus-web/test/unit/filter-panel.test.ts | 28 +- gitnexus-web/test/unit/graph.test.ts | 7 +- .../test/unit/path-resolution.test.ts | 4 +- .../test/unit/perf-optimizations.test.ts | 4 +- .../test/unit/security-guards.test.ts | 69 +- .../test/unit/settings-service.test.ts | 28 +- gitnexus-web/tsconfig.json | 5 +- gitnexus-web/vite.config.ts | 12 +- gitnexus-web/vitest.config.ts | 20 +- gitnexus/.claude/settings.local.json | 4 +- gitnexus/hooks/claude/gitnexus-hook.cjs | 74 +- gitnexus/src/cli/ai-context.ts | 47 +- gitnexus/src/cli/analyze.ts | 97 +- gitnexus/src/cli/augment.ts | 10 +- gitnexus/src/cli/clean.ts | 2 +- gitnexus/src/cli/eval-server.ts | 65 +- gitnexus/src/cli/index-repo.ts | 49 +- gitnexus/src/cli/index.ts | 19 +- gitnexus/src/cli/lazy-action.ts | 5 +- gitnexus/src/cli/list.ts | 6 +- gitnexus/src/cli/mcp.ts | 10 +- gitnexus/src/cli/setup.ts | 46 +- gitnexus/src/cli/skill-gen.ts | 85 +- gitnexus/src/cli/status.ts | 2 +- gitnexus/src/cli/tool.ts | 65 +- gitnexus/src/cli/wiki.ts | 112 +- gitnexus/src/config/ignore-service.ts | 457 +++-- gitnexus/src/core/augmentation/engine.ts | 115 +- gitnexus/src/core/embeddings/embedder.ts | 102 +- .../src/core/embeddings/embedding-pipeline.ts | 64 +- gitnexus/src/core/embeddings/http-client.ts | 24 +- gitnexus/src/core/embeddings/index.ts | 3 +- .../src/core/embeddings/text-generator.ts | 91 +- gitnexus/src/core/embeddings/types.ts | 16 +- gitnexus/src/core/graph/graph.ts | 25 +- gitnexus/src/core/ingestion/ast-cache.ts | 11 +- gitnexus/src/core/ingestion/call-processor.ts | 640 ++++-- gitnexus/src/core/ingestion/call-routing.ts | 26 +- .../src/core/ingestion/cluster-enricher.ts | 80 +- .../src/core/ingestion/cobol-processor.ts | 283 ++- .../ingestion/cobol/cobol-copy-expander.ts | 40 +- .../ingestion/cobol/cobol-preprocessor.ts | 590 ++++-- .../src/core/ingestion/cobol/jcl-parser.ts | 10 +- .../src/core/ingestion/cobol/jcl-processor.ts | 2 +- .../src/core/ingestion/community-processor.ts | 94 +- .../src/core/ingestion/entry-point-scoring.ts | 334 +-- .../src/core/ingestion/export-detection.ts | 57 +- .../src/core/ingestion/field-extractor.ts | 24 +- .../field-extractors/configs/c-cpp.ts | 20 +- .../field-extractors/configs/dart.ts | 8 +- .../ingestion/field-extractors/configs/go.ts | 8 +- .../field-extractors/configs/helpers.ts | 5 +- .../ingestion/field-extractors/configs/jvm.ts | 14 +- .../ingestion/field-extractors/configs/php.ts | 20 +- .../field-extractors/configs/ruby.ts | 7 +- .../field-extractors/configs/rust.ts | 5 +- .../field-extractors/configs/swift.ts | 14 +- .../configs/typescript-javascript.ts | 6 +- .../ingestion/field-extractors/generic.ts | 12 +- .../ingestion/field-extractors/typescript.ts | 22 +- .../src/core/ingestion/filesystem-walker.ts | 25 +- .../src/core/ingestion/framework-detection.ts | 584 ++++-- .../src/core/ingestion/heritage-processor.ts | 91 +- .../src/core/ingestion/import-processor.ts | 132 +- .../core/ingestion/import-resolvers/csharp.ts | 16 +- .../src/core/ingestion/import-resolvers/go.ts | 18 +- .../core/ingestion/import-resolvers/jvm.ts | 80 +- .../core/ingestion/import-resolvers/php.ts | 19 +- .../core/ingestion/import-resolvers/python.ts | 3 +- .../core/ingestion/import-resolvers/ruby.ts | 7 +- .../core/ingestion/import-resolvers/rust.ts | 10 +- .../ingestion/import-resolvers/standard.ts | 16 +- .../core/ingestion/import-resolvers/swift.ts | 5 +- .../core/ingestion/import-resolvers/types.ts | 7 +- .../core/ingestion/import-resolvers/utils.ts | 42 +- .../src/core/ingestion/language-config.ts | 16 +- .../src/core/ingestion/language-provider.ts | 25 +- .../src/core/ingestion/languages/c-cpp.ts | 133 +- .../src/core/ingestion/languages/csharp.ts | 110 +- gitnexus/src/core/ingestion/languages/dart.ts | 32 +- .../src/core/ingestion/languages/index.ts | 12 +- .../src/core/ingestion/languages/kotlin.ts | 68 +- gitnexus/src/core/ingestion/languages/php.ts | 132 +- .../src/core/ingestion/languages/python.ts | 31 +- gitnexus/src/core/ingestion/languages/ruby.ts | 68 +- gitnexus/src/core/ingestion/languages/rust.ts | 64 +- .../src/core/ingestion/languages/swift.ts | 144 +- .../core/ingestion/languages/typescript.ts | 112 +- .../src/core/ingestion/markdown-processor.ts | 13 +- gitnexus/src/core/ingestion/mro-processor.ts | 35 +- .../core/ingestion/named-binding-processor.ts | 7 +- .../src/core/ingestion/named-bindings/rust.ts | 6 +- .../core/ingestion/named-bindings/types.ts | 6 +- .../src/core/ingestion/parsing-processor.ts | 158 +- gitnexus/src/core/ingestion/pipeline.ts | 620 ++++-- .../src/core/ingestion/process-processor.ts | 184 +- .../src/core/ingestion/resolution-context.ts | 13 +- .../ingestion/route-extractors/middleware.ts | 61 +- .../core/ingestion/route-extractors/php.ts | 5 +- .../route-extractors/response-shapes.ts | 100 +- .../src/core/ingestion/structure-processor.ts | 90 +- gitnexus/src/core/ingestion/symbol-table.ts | 55 +- .../src/core/ingestion/tree-sitter-queries.ts | 7 +- gitnexus/src/core/ingestion/type-env.ts | 270 ++- .../core/ingestion/type-extractors/c-cpp.ts | 163 +- .../core/ingestion/type-extractors/csharp.ts | 125 +- .../core/ingestion/type-extractors/dart.ts | 71 +- .../src/core/ingestion/type-extractors/go.ts | 67 +- .../src/core/ingestion/type-extractors/jvm.ts | 167 +- .../src/core/ingestion/type-extractors/php.ts | 116 +- .../core/ingestion/type-extractors/python.ts | 91 +- .../core/ingestion/type-extractors/ruby.ts | 61 +- .../core/ingestion/type-extractors/rust.ts | 82 +- .../core/ingestion/type-extractors/shared.ts | 318 ++- .../core/ingestion/type-extractors/swift.ts | 99 +- .../core/ingestion/type-extractors/types.ts | 20 +- .../ingestion/type-extractors/typescript.ts | 133 +- .../src/core/ingestion/utils/ast-helpers.ts | 246 ++- .../src/core/ingestion/utils/call-analysis.ts | 241 ++- .../src/core/ingestion/utils/event-loop.ts | 3 +- .../core/ingestion/workers/parse-worker.ts | 420 +++- .../src/core/ingestion/workers/worker-pool.ts | 30 +- gitnexus/src/core/lbug/csv-generator.ts | 346 ++-- gitnexus/src/core/lbug/lbug-adapter.ts | 230 ++- gitnexus/src/core/lbug/schema.ts | 14 +- gitnexus/src/core/run-analyze.ts | 295 +-- gitnexus/src/core/search/bm25-index.ts | 38 +- gitnexus/src/core/search/hybrid-search.ts | 50 +- .../src/core/tree-sitter/parser-loader.ts | 24 +- gitnexus/src/core/wiki/cursor-client.ts | 30 +- gitnexus/src/core/wiki/generator.ts | 167 +- gitnexus/src/core/wiki/graph-queries.ts | 92 +- gitnexus/src/core/wiki/html-viewer.ts | 23 +- gitnexus/src/core/wiki/llm-client.ts | 49 +- gitnexus/src/core/wiki/prompts.ts | 27 +- gitnexus/src/lib/utils.ts | 4 +- .../src/mcp/compatible-stdio-transport.ts | 16 +- gitnexus/src/mcp/core/embedder.ts | 28 +- gitnexus/src/mcp/core/lbug-adapter.ts | 41 +- gitnexus/src/mcp/local/local-backend.ts | 1306 ++++++++---- gitnexus/src/mcp/resources.ts | 63 +- gitnexus/src/mcp/server.ts | 35 +- gitnexus/src/mcp/staleness.ts | 17 +- gitnexus/src/mcp/tools.ts | 144 +- gitnexus/src/server/analyze-job.ts | 14 +- gitnexus/src/server/analyze-worker.ts | 4 +- gitnexus/src/server/api.ts | 321 +-- gitnexus/src/server/git-clone.ts | 16 +- gitnexus/src/server/mcp-http.ts | 6 +- gitnexus/src/storage/git.ts | 4 +- gitnexus/src/storage/repo-manager.ts | 40 +- gitnexus/test/helpers/test-graph.ts | 55 +- gitnexus/test/helpers/test-indexed-db.ts | 18 +- .../test/integration/api-impact-e2e.test.ts | 540 ++--- .../test/integration/augmentation.test.ts | 138 +- .../class-impact-all-languages.test.ts | 394 +++- gitnexus/test/integration/cli-e2e.test.ts | 89 +- .../integration/cross-file-binding.test.ts | 68 +- .../test/integration/csv-pipeline.test.ts | 40 +- gitnexus/test/integration/enrichment.test.ts | 35 +- gitnexus/test/integration/expo-routes.test.ts | 12 +- .../integration/filesystem-walker.test.ts | 100 +- gitnexus/test/integration/has-method.test.ts | 152 +- gitnexus/test/integration/hooks-e2e.test.ts | 42 +- .../integration/ignore-and-skip-e2e.test.ts | 39 +- .../integration/java-class-impact.test.ts | 383 ++-- .../integration/lbug-core-adapter.test.ts | 193 +- .../integration/lbug-pool-stability.test.ts | 240 ++- gitnexus/test/integration/lbug-pool.test.ts | 342 ++-- .../local-backend-calltool.test.ts | 444 ++-- .../test/integration/local-backend.test.ts | 403 ++-- .../test/integration/orm-dataflow.test.ts | 22 +- gitnexus/test/integration/parsing.test.ts | 292 ++- gitnexus/test/integration/pipeline.test.ts | 24 +- .../integration/query-compilation.test.ts | 6 +- .../resolvers/api-deep-flow.test.ts | 55 +- .../test/integration/resolvers/cobol.test.ts | 320 ++- .../test/integration/resolvers/cpp.test.ts | 419 ++-- .../test/integration/resolvers/csharp.test.ts | 569 +++--- .../test/integration/resolvers/dart.test.ts | 36 +- .../resolvers/express-routes.test.ts | 20 +- .../test/integration/resolvers/go.test.ts | 503 +++-- .../test/integration/resolvers/helpers.ts | 19 +- .../test/integration/resolvers/java.test.ts | 572 +++--- .../integration/resolvers/javascript.test.ts | 135 +- .../test/integration/resolvers/kotlin.test.ts | 664 +++--- .../resolvers/php-response-shapes.test.ts | 41 +- .../test/integration/resolvers/php.test.ts | 521 +++-- .../resolvers/python-mcp-tools.test.ts | 16 +- .../test/integration/resolvers/python.test.ts | 672 +++---- .../resolvers/route-mapping.test.ts | 30 +- .../test/integration/resolvers/ruby.test.ts | 385 ++-- .../test/integration/resolvers/rust.test.ts | 643 +++--- .../integration/resolvers/shape-check.test.ts | 38 +- .../test/integration/resolvers/swift.test.ts | 217 +- .../integration/resolvers/typescript.test.ts | 769 ++++--- gitnexus/test/integration/search-core.test.ts | 174 +- gitnexus/test/integration/search-pool.test.ts | 106 +- .../test/integration/server-analyze.test.ts | 5 +- .../test/integration/setup-skills.test.ts | 14 +- .../shape-check-regression.test.ts | 225 ++- gitnexus/test/integration/skills-e2e.test.ts | 61 +- .../staleness-and-stability.test.ts | 356 ++-- .../integration/tree-sitter-languages.test.ts | 109 +- gitnexus/test/integration/worker-pool.test.ts | 72 +- gitnexus/test/unit/ai-context.test.ts | 4 +- gitnexus/test/unit/analyze-api.test.ts | 5 +- gitnexus/test/unit/analyze-job.test.ts | 5 +- gitnexus/test/unit/call-form.test.ts | 85 +- gitnexus/test/unit/call-processor.test.ts | 850 ++++---- gitnexus/test/unit/call-routing/ruby.test.ts | 42 +- gitnexus/test/unit/calltool-dispatch.test.ts | 96 +- .../test/unit/cobol-copy-expander.test.ts | 8 +- gitnexus/test/unit/cobol-preprocessor.test.ts | 224 +-- .../test/unit/cohesion-consistency.test.ts | 22 +- .../test/unit/community-processor.test.ts | 5 +- .../unit/compatible-stdio-transport.test.ts | 52 +- gitnexus/test/unit/cors.test.ts | 2 +- .../test/unit/dart-import-resolver.test.ts | 2 +- .../test/unit/dart-type-extractor.test.ts | 215 +- .../test/unit/entry-point-scoring.test.ts | 123 +- gitnexus/test/unit/eval-formatters.test.ts | 72 +- .../unit/extract-generic-type-args.test.ts | 4 +- .../test/unit/fetch-reason-parsing.test.ts | 7 +- gitnexus/test/unit/field-extraction.test.ts | 93 +- .../test/unit/framework-detection.test.ts | 43 +- gitnexus/test/unit/git.test.ts | 24 +- gitnexus/test/unit/graph.test.ts | 12 +- gitnexus/test/unit/has-method.test.ts | 246 ++- gitnexus/test/unit/heritage-processor.test.ts | 280 ++- gitnexus/test/unit/hooks.test.ts | 78 +- gitnexus/test/unit/http-embedder.test.ts | 138 +- gitnexus/test/unit/hybrid-search.test.ts | 12 +- gitnexus/test/unit/ignore-service.test.ts | 115 +- .../unit/impact-batching-grouping.test.ts | 131 +- gitnexus/test/unit/impact-confidence.test.ts | 9 +- .../import-resolution/preprocessing.test.ts | 56 +- gitnexus/test/unit/index-repo-command.test.ts | 124 +- gitnexus/test/unit/ingestion-utils.test.ts | 44 +- gitnexus/test/unit/isWriteQuery.test.ts | 1 - gitnexus/test/unit/jcl-parser.test.ts | 52 +- gitnexus/test/unit/method-signature.test.ts | 8 +- gitnexus/test/unit/mro-processor.test.ts | 125 +- gitnexus/test/unit/parser-loader.test.ts | 8 +- gitnexus/test/unit/process-processor.test.ts | 322 ++- gitnexus/test/unit/resources.test.ts | 64 +- .../test/unit/route-tool-detection.test.ts | 92 +- gitnexus/test/unit/schema.test.ts | 49 +- gitnexus/test/unit/security.test.ts | 38 +- .../sequential-language-availability.test.ts | 55 +- gitnexus/test/unit/server.test.ts | 4 +- gitnexus/test/unit/setup-codex.test.ts | 8 +- gitnexus/test/unit/setup.test.ts | 4 +- .../test/unit/shared-type-extractors.test.ts | 4 +- gitnexus/test/unit/skill-gen.test.ts | 478 +++-- gitnexus/test/unit/staleness.test.ts | 16 +- .../test/unit/structure-processor.test.ts | 30 +- .../test/unit/suffix-index-ambiguity.test.ts | 68 +- gitnexus/test/unit/symbol-resolver.test.ts | 187 +- gitnexus/test/unit/symbol-table.test.ts | 10 +- gitnexus/test/unit/tools.test.ts | 38 +- gitnexus/test/unit/topological-sort.test.ts | 12 +- gitnexus/test/unit/type-env.test.ts | 1786 ++++++++++++----- gitnexus/test/unit/utils.test.ts | 4 +- gitnexus/test/unit/wiki-flags.test.ts | 68 +- gitnexus/tsconfig.json | 14 +- gitnexus/vitest.config.ts | 7 +- 344 files changed, 26153 insertions(+), 17098 deletions(-) diff --git a/.github/release.yml b/.github/release.yml index 1cffb36676..ff84fb8ef0 100644 --- a/.github/release.yml +++ b/.github/release.yml @@ -35,7 +35,7 @@ changelog: - dependencies - title: "\U0001F4DD Other Changes" labels: - - "*" + - '*' exclude: labels: - dependencies diff --git a/.github/workflows/ci-report.yml b/.github/workflows/ci-report.yml index 2e117f859c..2a6e5cea82 100644 --- a/.github/workflows/ci-report.yml +++ b/.github/workflows/ci-report.yml @@ -6,12 +6,12 @@ name: CI Report on: workflow_run: - workflows: ["CI"] + workflows: ['CI'] types: [completed] permissions: - actions: read # needed to list/download workflow run artifacts - contents: read # needed for sparse checkout of vitest.config.ts + actions: read # needed to list/download workflow run artifacts + contents: read # needed for sparse checkout of vitest.config.ts pull-requests: write # needed to post sticky PR comment jobs: diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml index 7925c0ba40..407b2fcf8c 100644 --- a/.github/workflows/claude.yml +++ b/.github/workflows/claude.yml @@ -53,7 +53,7 @@ jobs: pull-requests: write issues: write id-token: write - actions: read # required for Claude to read CI results on PRs + actions: read # required for Claude to read CI results on PRs steps: # For PR-related triggers, resolve the fork repo so we can checkout correctly. - name: Resolve PR context diff --git a/eval/configs/models/claude-haiku.yaml b/eval/configs/models/claude-haiku.yaml index 548cc7f849..e1e395464a 100644 --- a/eval/configs/models/claude-haiku.yaml +++ b/eval/configs/models/claude-haiku.yaml @@ -1,8 +1,8 @@ # Claude Haiku 4.5 — fast, cheap, good baseline # Via OpenRouter (set OPENROUTER_API_KEY in .env) model: - model_name: "openrouter/anthropic/claude-haiku-4.5" - cost_tracking: "ignore_errors" + model_name: 'openrouter/anthropic/claude-haiku-4.5' + cost_tracking: 'ignore_errors' model_kwargs: max_tokens: 8192 temperature: 0 diff --git a/eval/configs/models/claude-opus.yaml b/eval/configs/models/claude-opus.yaml index 3c3e114c41..571d4f490d 100644 --- a/eval/configs/models/claude-opus.yaml +++ b/eval/configs/models/claude-opus.yaml @@ -2,8 +2,8 @@ # Via OpenRouter (set OPENROUTER_API_KEY in .env) # To use Anthropic directly, change to: anthropic/claude-opus-4-20250514 model: - model_name: "openrouter/anthropic/claude-opus-4" - cost_tracking: "ignore_errors" + model_name: 'openrouter/anthropic/claude-opus-4' + cost_tracking: 'ignore_errors' model_kwargs: max_tokens: 16384 temperature: 0 diff --git a/eval/configs/models/claude-sonnet.yaml b/eval/configs/models/claude-sonnet.yaml index 2f8ab14930..b34da890ff 100644 --- a/eval/configs/models/claude-sonnet.yaml +++ b/eval/configs/models/claude-sonnet.yaml @@ -2,8 +2,8 @@ # Via OpenRouter (set OPENROUTER_API_KEY in .env) # To use Anthropic directly, change to: anthropic/claude-sonnet-4-20250514 model: - model_name: "openrouter/anthropic/claude-sonnet-4" - cost_tracking: "ignore_errors" + model_name: 'openrouter/anthropic/claude-sonnet-4' + cost_tracking: 'ignore_errors' model_kwargs: max_tokens: 16384 temperature: 0 diff --git a/eval/configs/models/deepseek-chat.yaml b/eval/configs/models/deepseek-chat.yaml index e649a021f1..949e3acb21 100644 --- a/eval/configs/models/deepseek-chat.yaml +++ b/eval/configs/models/deepseek-chat.yaml @@ -1,9 +1,9 @@ model: deepseek-ai/deepseek-chat provider: openrouter cost: - input: 0.14 # per 1M tokens - output: 0.28 # per 1M tokens - + input: 0.14 # per 1M tokens + output: 0.28 # per 1M tokens + # Native DeepSeek API (direct) api_key: null base_url: null diff --git a/eval/configs/models/deepseek-v3.yaml b/eval/configs/models/deepseek-v3.yaml index f15c5b7a62..7476ac051a 100644 --- a/eval/configs/models/deepseek-v3.yaml +++ b/eval/configs/models/deepseek-v3.yaml @@ -1,9 +1,9 @@ model: deepseek-ai/DeepSeek-V3 provider: openrouter cost: - input: 0.27 # per 1M tokens - output: 1.10 # per 1M tokens - + input: 0.27 # per 1M tokens + output: 1.10 # per 1M tokens + # Native DeepSeek API (direct) # Get your API key at: https://platform.deepseek.com/ # Or use OpenRouter with: OPENROUTER_API_KEY diff --git a/eval/configs/models/glm-4.7.yaml b/eval/configs/models/glm-4.7.yaml index 8dc3111a17..5ba934eaf0 100644 --- a/eval/configs/models/glm-4.7.yaml +++ b/eval/configs/models/glm-4.7.yaml @@ -1,7 +1,7 @@ # GLM 4.7 — via OpenRouter (set OPENROUTER_API_KEY in .env) model: - model_name: "openrouter/zhipuai/glm-4.7" - cost_tracking: "ignore_errors" + model_name: 'openrouter/zhipuai/glm-4.7' + cost_tracking: 'ignore_errors' model_kwargs: max_tokens: 8192 temperature: 0 diff --git a/eval/configs/models/glm-5.yaml b/eval/configs/models/glm-5.yaml index f37a162e6b..88ec8e0a80 100644 --- a/eval/configs/models/glm-5.yaml +++ b/eval/configs/models/glm-5.yaml @@ -1,7 +1,7 @@ # GLM 5 — via OpenRouter (set OPENROUTER_API_KEY in .env) model: - model_name: "openrouter/zhipuai/glm-5" - cost_tracking: "ignore_errors" + model_name: 'openrouter/zhipuai/glm-5' + cost_tracking: 'ignore_errors' model_kwargs: max_tokens: 8192 temperature: 0 diff --git a/eval/configs/models/minimax-2.5.yaml b/eval/configs/models/minimax-2.5.yaml index f3b43d5548..6e7800a6a5 100644 --- a/eval/configs/models/minimax-2.5.yaml +++ b/eval/configs/models/minimax-2.5.yaml @@ -1,7 +1,7 @@ # MiniMax M1 2.5 — via OpenRouter (set OPENROUTER_API_KEY in .env) model: - model_name: "openrouter/minimax/minimax-m1-2.5" - cost_tracking: "ignore_errors" + model_name: 'openrouter/minimax/minimax-m1-2.5' + cost_tracking: 'ignore_errors' model_kwargs: max_tokens: 8192 temperature: 0 diff --git a/eval/configs/models/minimax-m2.1.yaml b/eval/configs/models/minimax-m2.1.yaml index 766d75a007..5ba088dd62 100644 --- a/eval/configs/models/minimax-m2.1.yaml +++ b/eval/configs/models/minimax-m2.1.yaml @@ -3,9 +3,9 @@ # The action_regex tells mini-swe-agent to parse ```bash blocks from responses. model: model_class: litellm_textbased - model_name: "openrouter/minimax/minimax-m2.5" + model_name: 'openrouter/minimax/minimax-m2.5' action_regex: "```(?:bash|mswea_bash_command)\\s*\\n(.*?)\\n```" - cost_tracking: "ignore_errors" + cost_tracking: 'ignore_errors' model_kwargs: max_tokens: 8192 temperature: 0 diff --git a/eval/configs/modes/baseline.yaml b/eval/configs/modes/baseline.yaml index c9c73b6379..ec60a4abaa 100644 --- a/eval/configs/modes/baseline.yaml +++ b/eval/configs/modes/baseline.yaml @@ -1,9 +1,9 @@ # Baseline mode — no GitNexus, pure mini-swe-agent (control group) agent: - agent_class: "eval.agents.gitnexus_agent.GitNexusAgent" - gitnexus_mode: "baseline" + agent_class: 'eval.agents.gitnexus_agent.GitNexusAgent' + gitnexus_mode: 'baseline' step_limit: 30 cost_limit: 3.0 environment: - environment_class: "docker" + environment_class: 'docker' diff --git a/eval/configs/modes/native.yaml b/eval/configs/modes/native.yaml index 573d125e7c..1e6086016a 100644 --- a/eval/configs/modes/native.yaml +++ b/eval/configs/modes/native.yaml @@ -5,14 +5,14 @@ # # Use this mode to isolate the value of explicit tools without grep augmentation. agent: - agent_class: "eval.agents.gitnexus_agent.GitNexusAgent" - gitnexus_mode: "native" + agent_class: 'eval.agents.gitnexus_agent.GitNexusAgent' + gitnexus_mode: 'native' step_limit: 30 cost_limit: 3.0 track_gitnexus_usage: true environment: - environment_class: "eval.environments.gitnexus_docker.GitNexusDockerEnvironment" + environment_class: 'eval.environments.gitnexus_docker.GitNexusDockerEnvironment' enable_gitnexus: true skip_embeddings: true gitnexus_timeout: 120 diff --git a/eval/configs/modes/native_augment.yaml b/eval/configs/modes/native_augment.yaml index fb9b79729e..a26c5bfb39 100644 --- a/eval/configs/modes/native_augment.yaml +++ b/eval/configs/modes/native_augment.yaml @@ -8,8 +8,8 @@ # # The agent decides when to use explicit tools vs rely on enriched grep results. agent: - agent_class: "eval.agents.gitnexus_agent.GitNexusAgent" - gitnexus_mode: "native_augment" + agent_class: 'eval.agents.gitnexus_agent.GitNexusAgent' + gitnexus_mode: 'native_augment' step_limit: 30 cost_limit: 3.0 augment_timeout: 5.0 @@ -17,7 +17,7 @@ agent: track_gitnexus_usage: true environment: - environment_class: "eval.environments.gitnexus_docker.GitNexusDockerEnvironment" + environment_class: 'eval.environments.gitnexus_docker.GitNexusDockerEnvironment' enable_gitnexus: true skip_embeddings: true gitnexus_timeout: 120 diff --git a/gitnexus-claude-plugin/hooks/gitnexus-hook.js b/gitnexus-claude-plugin/hooks/gitnexus-hook.js index 4cc79c100b..e29726d854 100644 --- a/gitnexus-claude-plugin/hooks/gitnexus-hook.js +++ b/gitnexus-claude-plugin/hooks/gitnexus-hook.js @@ -64,10 +64,26 @@ function extractPattern(toolName, toolInput) { const tokens = cmd.split(/\s+/); let foundCmd = false; let skipNext = false; - const flagsWithValues = new Set(['-e', '-f', '-m', '-A', '-B', '-C', '-g', '--glob', '-t', '--type', '--include', '--exclude']); + const flagsWithValues = new Set([ + '-e', + '-f', + '-m', + '-A', + '-B', + '-C', + '-g', + '--glob', + '-t', + '--type', + '--include', + '--exclude', + ]); for (const token of tokens) { - if (skipNext) { skipNext = false; continue; } + if (skipNext) { + skipNext = false; + continue; + } if (!foundCmd) { if (/\brg$|\bgrep$/.test(token)) foundCmd = true; continue; @@ -98,33 +114,42 @@ function runGitNexusCli(args, cwd, timeout) { // Detect whether 'gitnexus' is on PATH (cheap check, no execution) let useDirectBinary = false; try { - const which = spawnSync( - isWin ? 'where' : 'which', ['gitnexus'], - { encoding: 'utf-8', timeout: 3000, stdio: ['pipe', 'pipe', 'pipe'] } - ); + const which = spawnSync(isWin ? 'where' : 'which', ['gitnexus'], { + encoding: 'utf-8', + timeout: 3000, + stdio: ['pipe', 'pipe', 'pipe'], + }); useDirectBinary = which.status === 0; - } catch { /* not on PATH */ } + } catch { + /* not on PATH */ + } if (useDirectBinary) { - return spawnSync( - isWin ? 'gitnexus.cmd' : 'gitnexus', args, - { encoding: 'utf-8', timeout, cwd, stdio: ['pipe', 'pipe', 'pipe'] } - ); + return spawnSync(isWin ? 'gitnexus.cmd' : 'gitnexus', args, { + encoding: 'utf-8', + timeout, + cwd, + stdio: ['pipe', 'pipe', 'pipe'], + }); } // npx fallback needs shell on Windows since npx is a .cmd script - return spawnSync( - isWin ? 'npx.cmd' : 'npx', ['-y', 'gitnexus', ...args], - { encoding: 'utf-8', timeout: timeout + 5000, cwd, stdio: ['pipe', 'pipe', 'pipe'] } - ); + return spawnSync(isWin ? 'npx.cmd' : 'npx', ['-y', 'gitnexus', ...args], { + encoding: 'utf-8', + timeout: timeout + 5000, + cwd, + stdio: ['pipe', 'pipe', 'pipe'], + }); } /** * Emit a hook response with additional context for the agent. */ function sendHookResponse(hookEventName, message) { - console.log(JSON.stringify({ - hookSpecificOutput: { hookEventName, additionalContext: message } - })); + console.log( + JSON.stringify({ + hookSpecificOutput: { hookEventName, additionalContext: message }, + }), + ); } /** @@ -149,7 +174,9 @@ function handlePreToolUse(input) { if (!child.error && child.status === 0) { result = child.stderr || ''; } - } catch { /* graceful failure */ } + } catch { + /* graceful failure */ + } if (result && result.trim()) { sendHookResponse('PreToolUse', result.trim()); @@ -185,10 +212,15 @@ function handlePostToolUse(input) { let currentHead = ''; try { const headResult = spawnSync('git', ['rev-parse', 'HEAD'], { - encoding: 'utf-8', timeout: 3000, cwd, stdio: ['pipe', 'pipe', 'pipe'], + encoding: 'utf-8', + timeout: 3000, + cwd, + stdio: ['pipe', 'pipe', 'pipe'], }); currentHead = (headResult.stdout || '').trim(); - } catch { return; } + } catch { + return; + } if (!currentHead) return; @@ -197,16 +229,19 @@ function handlePostToolUse(input) { try { const meta = JSON.parse(fs.readFileSync(path.join(gitNexusDir, 'meta.json'), 'utf-8')); lastCommit = meta.lastCommit || ''; - hadEmbeddings = (meta.stats && meta.stats.embeddings > 0); - } catch { /* no meta — treat as stale */ } + hadEmbeddings = meta.stats && meta.stats.embeddings > 0; + } catch { + /* no meta — treat as stale */ + } // If HEAD matches last indexed commit, no reindex needed if (currentHead && currentHead === lastCommit) return; const analyzeCmd = `npx gitnexus analyze${hadEmbeddings ? ' --embeddings' : ''}`; - sendHookResponse('PostToolUse', + sendHookResponse( + 'PostToolUse', `GitNexus index is stale (last indexed: ${lastCommit ? lastCommit.slice(0, 7) : 'never'}). ` + - `Run \`${analyzeCmd}\` to update the knowledge graph.` + `Run \`${analyzeCmd}\` to update the knowledge graph.`, ); } diff --git a/gitnexus-shared/src/index.ts b/gitnexus-shared/src/index.ts index dad639895b..bd89dfc622 100644 --- a/gitnexus-shared/src/index.ts +++ b/gitnexus-shared/src/index.ts @@ -14,17 +14,11 @@ export { REL_TYPES, EMBEDDING_TABLE_NAME, } from './lbug/schema-constants.js'; -export type { - NodeTableName, - RelType, -} from './lbug/schema-constants.js'; +export type { NodeTableName, RelType } from './lbug/schema-constants.js'; // Language support export { SupportedLanguages } from './languages.js'; export { getLanguageFromFilename, getSyntaxLanguageFromFilename } from './language-detection.js'; // Pipeline progress -export type { - PipelinePhase, - PipelineProgress, -} from './pipeline.js'; +export type { PipelinePhase, PipelineProgress } from './pipeline.js'; diff --git a/gitnexus-shared/src/language-detection.ts b/gitnexus-shared/src/language-detection.ts index 757e18b195..0b3b778f87 100644 --- a/gitnexus-shared/src/language-detection.ts +++ b/gitnexus-shared/src/language-detection.ts @@ -12,7 +12,13 @@ import { SupportedLanguages } from './languages.js'; /** Ruby extensionless filenames recognised as Ruby source */ -const RUBY_EXTENSIONLESS_FILES = new Set(['Rakefile', 'Gemfile', 'Guardfile', 'Vagrantfile', 'Brewfile']); +const RUBY_EXTENSIONLESS_FILES = new Set([ + 'Rakefile', + 'Gemfile', + 'Guardfile', + 'Vagrantfile', + 'Brewfile', +]); /** * Exhaustive map: every SupportedLanguages member → its file extensions. @@ -21,26 +27,29 @@ const RUBY_EXTENSIONLESS_FILES = new Set(['Rakefile', 'Gemfile', 'Guardfile', 'V * TypeScript emits a compile error: "Property 'NewLang' is missing in type..." */ const EXTENSION_MAP: Record = { - [SupportedLanguages.JavaScript]: ['.js', '.jsx', '.mjs', '.cjs'], - [SupportedLanguages.TypeScript]: ['.ts', '.tsx', '.mts', '.cts'], - [SupportedLanguages.Python]: ['.py'], - [SupportedLanguages.Java]: ['.java'], - [SupportedLanguages.C]: ['.c'], - [SupportedLanguages.CPlusPlus]: ['.cpp', '.cc', '.cxx', '.h', '.hpp', '.hxx', '.hh'], - [SupportedLanguages.CSharp]: ['.cs'], - [SupportedLanguages.Go]: ['.go'], - [SupportedLanguages.Ruby]: ['.rb', '.rake', '.gemspec'], - [SupportedLanguages.Rust]: ['.rs'], - [SupportedLanguages.PHP]: ['.php', '.phtml', '.php3', '.php4', '.php5', '.php8'], - [SupportedLanguages.Kotlin]: ['.kt', '.kts'], - [SupportedLanguages.Swift]: ['.swift'], - [SupportedLanguages.Dart]: ['.dart'], - [SupportedLanguages.Cobol]: ['.cbl', '.cob', '.cpy', '.cobol'], + [SupportedLanguages.JavaScript]: ['.js', '.jsx', '.mjs', '.cjs'], + [SupportedLanguages.TypeScript]: ['.ts', '.tsx', '.mts', '.cts'], + [SupportedLanguages.Python]: ['.py'], + [SupportedLanguages.Java]: ['.java'], + [SupportedLanguages.C]: ['.c'], + [SupportedLanguages.CPlusPlus]: ['.cpp', '.cc', '.cxx', '.h', '.hpp', '.hxx', '.hh'], + [SupportedLanguages.CSharp]: ['.cs'], + [SupportedLanguages.Go]: ['.go'], + [SupportedLanguages.Ruby]: ['.rb', '.rake', '.gemspec'], + [SupportedLanguages.Rust]: ['.rs'], + [SupportedLanguages.PHP]: ['.php', '.phtml', '.php3', '.php4', '.php5', '.php8'], + [SupportedLanguages.Kotlin]: ['.kt', '.kts'], + [SupportedLanguages.Swift]: ['.swift'], + [SupportedLanguages.Dart]: ['.dart'], + [SupportedLanguages.Cobol]: ['.cbl', '.cob', '.cpy', '.cobol'], } satisfies Record; // Ensure exhaustiveness /** Pre-built reverse lookup: extension → language (built once at module load). */ const extToLang = new Map(); -for (const [lang, exts] of Object.entries(EXTENSION_MAP) as [SupportedLanguages, readonly string[]][]) { +for (const [lang, exts] of Object.entries(EXTENSION_MAP) as [ + SupportedLanguages, + readonly string[], +][]) { for (const ext of exts) { extToLang.set(ext, lang); } @@ -75,37 +84,50 @@ export const getLanguageFromFilename = (filename: string): SupportedLanguages | * TypeScript emits a compile error. */ const SYNTAX_MAP: Record = { - [SupportedLanguages.JavaScript]: 'javascript', - [SupportedLanguages.TypeScript]: 'typescript', - [SupportedLanguages.Python]: 'python', - [SupportedLanguages.Java]: 'java', - [SupportedLanguages.C]: 'c', - [SupportedLanguages.CPlusPlus]: 'cpp', - [SupportedLanguages.CSharp]: 'csharp', - [SupportedLanguages.Go]: 'go', - [SupportedLanguages.Ruby]: 'ruby', - [SupportedLanguages.Rust]: 'rust', - [SupportedLanguages.PHP]: 'php', - [SupportedLanguages.Kotlin]: 'kotlin', - [SupportedLanguages.Swift]: 'swift', - [SupportedLanguages.Dart]: 'dart', - [SupportedLanguages.Cobol]: 'cobol', + [SupportedLanguages.JavaScript]: 'javascript', + [SupportedLanguages.TypeScript]: 'typescript', + [SupportedLanguages.Python]: 'python', + [SupportedLanguages.Java]: 'java', + [SupportedLanguages.C]: 'c', + [SupportedLanguages.CPlusPlus]: 'cpp', + [SupportedLanguages.CSharp]: 'csharp', + [SupportedLanguages.Go]: 'go', + [SupportedLanguages.Ruby]: 'ruby', + [SupportedLanguages.Rust]: 'rust', + [SupportedLanguages.PHP]: 'php', + [SupportedLanguages.Kotlin]: 'kotlin', + [SupportedLanguages.Swift]: 'swift', + [SupportedLanguages.Dart]: 'dart', + [SupportedLanguages.Cobol]: 'cobol', } satisfies Record; // Ensure exhaustiveness /** Non-code file extensions → Prism-compatible syntax identifiers */ const AUXILIARY_SYNTAX_MAP: Record = { - json: 'json', yaml: 'yaml', yml: 'yaml', - md: 'markdown', mdx: 'markdown', - html: 'markup', htm: 'markup', erb: 'markup', xml: 'markup', - css: 'css', scss: 'css', sass: 'css', - sh: 'bash', bash: 'bash', zsh: 'bash', - sql: 'sql', toml: 'toml', ini: 'ini', + json: 'json', + yaml: 'yaml', + yml: 'yaml', + md: 'markdown', + mdx: 'markdown', + html: 'markup', + htm: 'markup', + erb: 'markup', + xml: 'markup', + css: 'css', + scss: 'css', + sass: 'css', + sh: 'bash', + bash: 'bash', + zsh: 'bash', + sql: 'sql', + toml: 'toml', + ini: 'ini', dockerfile: 'docker', }; /** Extensionless filenames → Prism-compatible syntax identifiers */ const AUXILIARY_BASENAME_MAP: Record = { - Makefile: 'makefile', Dockerfile: 'docker', + Makefile: 'makefile', + Dockerfile: 'docker', }; /** diff --git a/gitnexus-shared/src/lbug/schema-constants.ts b/gitnexus-shared/src/lbug/schema-constants.ts index d13b3b8de1..e30948492f 100644 --- a/gitnexus-shared/src/lbug/schema-constants.ts +++ b/gitnexus-shared/src/lbug/schema-constants.ts @@ -9,25 +9,63 @@ */ export const NODE_TABLES = [ - 'File', 'Folder', 'Function', 'Class', 'Interface', 'Method', 'CodeElement', 'Community', 'Process', 'Section', - 'Struct', 'Enum', 'Macro', 'Typedef', 'Union', 'Namespace', 'Trait', 'Impl', - 'TypeAlias', 'Const', 'Static', 'Property', 'Record', 'Delegate', 'Annotation', 'Constructor', 'Template', 'Module', + 'File', + 'Folder', + 'Function', + 'Class', + 'Interface', + 'Method', + 'CodeElement', + 'Community', + 'Process', + 'Section', + 'Struct', + 'Enum', + 'Macro', + 'Typedef', + 'Union', + 'Namespace', + 'Trait', + 'Impl', + 'TypeAlias', + 'Const', + 'Static', + 'Property', + 'Record', + 'Delegate', + 'Annotation', + 'Constructor', + 'Template', + 'Module', 'Route', 'Tool', ] as const; -export type NodeTableName = typeof NODE_TABLES[number]; +export type NodeTableName = (typeof NODE_TABLES)[number]; export const REL_TABLE_NAME = 'CodeRelation'; export const REL_TYPES = [ - 'CONTAINS', 'DEFINES', 'IMPORTS', 'CALLS', 'EXTENDS', 'IMPLEMENTS', - 'HAS_METHOD', 'HAS_PROPERTY', 'ACCESSES', 'OVERRIDES', - 'MEMBER_OF', 'STEP_IN_PROCESS', - 'HANDLES_ROUTE', 'FETCHES', 'HANDLES_TOOL', 'ENTRY_POINT_OF', - 'WRAPS', 'QUERIES', + 'CONTAINS', + 'DEFINES', + 'IMPORTS', + 'CALLS', + 'EXTENDS', + 'IMPLEMENTS', + 'HAS_METHOD', + 'HAS_PROPERTY', + 'ACCESSES', + 'OVERRIDES', + 'MEMBER_OF', + 'STEP_IN_PROCESS', + 'HANDLES_ROUTE', + 'FETCHES', + 'HANDLES_TOOL', + 'ENTRY_POINT_OF', + 'WRAPS', + 'QUERIES', ] as const; -export type RelType = typeof REL_TYPES[number]; +export type RelType = (typeof REL_TYPES)[number]; export const EMBEDDING_TABLE_NAME = 'CodeEmbedding'; diff --git a/gitnexus-web/e2e/debug-issues.spec.ts b/gitnexus-web/e2e/debug-issues.spec.ts index c744964e1b..175e09ba6c 100644 --- a/gitnexus-web/e2e/debug-issues.spec.ts +++ b/gitnexus-web/e2e/debug-issues.spec.ts @@ -9,7 +9,7 @@ const BACKEND_URL = process.env.BACKEND_URL ?? 'http://localhost:4747'; const debugTest = process.env.DEBUG_E2E ? test : test.skip; async function connectToServer(page: import('@playwright/test').Page) { - page.on('console', msg => { + page.on('console', (msg) => { if (msg.type() === 'error') console.log(`[error] ${msg.text()}`); }); @@ -86,7 +86,10 @@ debugTest('debug: process view Reset View button', async ({ page }, testInfo) => const transformAfterReset = await diagramDiv.getAttribute('style'); console.log('Transform AFTER reset:', transformAfterReset); - await page.screenshot({ path: testInfo.outputPath('debug-modal-after-reset.png'), fullPage: true }); + await page.screenshot({ + path: testInfo.outputPath('debug-modal-after-reset.png'), + fullPage: true, + }); // Verify transform actually changed back expect(transformAfterZoom).not.toBe(transformBefore); @@ -123,5 +126,8 @@ debugTest('debug: lightbulb clears node selection dimming', async ({ page }, tes // Click it again to toggle back on await lightbulbBtn.click(); await page.waitForTimeout(500); - await page.screenshot({ path: testInfo.outputPath('debug-after-lightbulb-toggle-back.png'), fullPage: true }); + await page.screenshot({ + path: testInfo.outputPath('debug-after-lightbulb-toggle-back.png'), + fullPage: true, + }); }); diff --git a/gitnexus-web/e2e/manual-record.spec.ts b/gitnexus-web/e2e/manual-record.spec.ts index 794ad56946..b670efc312 100644 --- a/gitnexus-web/e2e/manual-record.spec.ts +++ b/gitnexus-web/e2e/manual-record.spec.ts @@ -12,16 +12,16 @@ import { test } from '@playwright/test'; */ test.skip( !!process.env.CI || process.env.PWDEBUG !== '1', - 'Manual recording requires --headed and PWDEBUG=1. Run: PWDEBUG=1 npx playwright test e2e/manual-record.spec.ts --headed --timeout=0' + 'Manual recording requires --headed and PWDEBUG=1. Run: PWDEBUG=1 npx playwright test e2e/manual-record.spec.ts --headed --timeout=0', ); test('manual recording session', async ({ page }) => { - page.on('console', msg => { + page.on('console', (msg) => { if (msg.type() === 'error' || msg.type() === 'warning') { console.log(`[${msg.type()}] ${msg.text()}`); } }); - page.on('pageerror', err => console.log(`[crash] ${err.message}`)); + page.on('pageerror', (err) => console.log(`[crash] ${err.message}`)); await page.goto('http://localhost:5173'); await page.pause(); diff --git a/gitnexus-web/e2e/onboarding.spec.ts b/gitnexus-web/e2e/onboarding.spec.ts index 03f2058c68..309dc7ef90 100644 --- a/gitnexus-web/e2e/onboarding.spec.ts +++ b/gitnexus-web/e2e/onboarding.spec.ts @@ -38,7 +38,9 @@ test.describe('Flow 1: Onboarding — no server', () => { // Step 2 title changes to "Waiting for server to start" once polling begins await expect(page.getByText('Waiting for server to start')).toBeAttached({ timeout: 10_000 }); // Step 3 is always rendered - await expect(page.getByText('Auto-connects and opens the graph')).toBeAttached({ timeout: 5_000 }); + await expect(page.getByText('Auto-connects and opens the graph')).toBeAttached({ + timeout: 5_000, + }); }); test('shows terminal window with command', async ({ page }) => { @@ -98,7 +100,9 @@ test.describe('Flow 2: Server detected — auto-connect', () => { }); await page.route(`${BACKEND_URL}/api/repo`, async (route) => { if (blockBackend) return route.abort('connectionrefused'); - await route.fulfill({ json: { name: 'test-repo', path: '/tmp/test', repoPath: '/tmp/test' } }); + await route.fulfill({ + json: { name: 'test-repo', path: '/tmp/test', repoPath: '/tmp/test' }, + }); }); await page.route(`${BACKEND_URL}/api/graph**`, async (route) => { if (blockBackend) return route.abort('connectionrefused'); @@ -135,7 +139,11 @@ test.describe('Flow 2: Server detected — auto-connect', () => { route.fulfill({ json: { version: '1.0.0', launchContext: 'npx', nodeVersion: 'v22.0.0' } }), ); await page.route(`${BACKEND_URL}/api/heartbeat`, (route) => - route.fulfill({ status: 200, headers: { 'Content-Type': 'text/event-stream' }, body: ':ok\n\n' }), + route.fulfill({ + status: 200, + headers: { 'Content-Type': 'text/event-stream' }, + body: ':ok\n\n', + }), ); await page.goto('/'); @@ -158,7 +166,11 @@ test.describe('Flow 3: Analyze form', () => { route.fulfill({ json: { version: '1.0.0', launchContext: 'npx', nodeVersion: 'v22.0.0' } }), ); await page.route(`${BACKEND_URL}/api/heartbeat`, (route) => - route.fulfill({ status: 200, headers: { 'Content-Type': 'text/event-stream' }, body: ':ok\n\n' }), + route.fulfill({ + status: 200, + headers: { 'Content-Type': 'text/event-stream' }, + body: ':ok\n\n', + }), ); }); @@ -222,9 +234,15 @@ test.describe('Flow 4: Repo dropdown in exploring view', () => { if (process.env.E2E) return; try { const res = await fetch(`${BACKEND_URL}/api/repos`); - if (!res.ok) { test.skip(true, SKIP_MSG); return; } + if (!res.ok) { + test.skip(true, SKIP_MSG); + return; + } const repos = await res.json(); - if (!repos.length) { test.skip(true, 'Server has no indexed repos'); return; } + if (!repos.length) { + test.skip(true, 'Server has no indexed repos'); + return; + } } catch { test.skip(true, SKIP_MSG); } @@ -238,7 +256,10 @@ test.describe('Flow 4: Repo dropdown in exploring view', () => { await page.screenshot({ path: testInfo.outputPath('exploring-loaded.png') }); // Click the project badge (has a chevron) - const badge = page.locator('header button').filter({ has: page.locator('svg') }).first(); + const badge = page + .locator('header button') + .filter({ has: page.locator('svg') }) + .first(); await badge.click(); // Repo dropdown should be visible @@ -252,7 +273,10 @@ test.describe('Flow 4: Repo dropdown in exploring view', () => { await expect(page.locator('[data-testid="status-ready"]')).toBeVisible({ timeout: 30_000 }); // Open repo dropdown - const badge = page.locator('header button').filter({ has: page.locator('svg') }).first(); + const badge = page + .locator('header button') + .filter({ has: page.locator('svg') }) + .first(); await badge.click(); // Click "Analyze a new repository..." diff --git a/gitnexus-web/e2e/server-connect.spec.ts b/gitnexus-web/e2e/server-connect.spec.ts index 61860aa2bc..5d1c307e3c 100644 --- a/gitnexus-web/e2e/server-connect.spec.ts +++ b/gitnexus-web/e2e/server-connect.spec.ts @@ -21,11 +21,17 @@ test.beforeAll(async () => { fetch(`${BACKEND_URL}/api/repos`), fetch(FRONTEND_URL), ]); - if (backendRes.status === 'rejected' || (backendRes.status === 'fulfilled' && !backendRes.value.ok)) { + if ( + backendRes.status === 'rejected' || + (backendRes.status === 'fulfilled' && !backendRes.value.ok) + ) { test.skip(true, 'gitnexus serve not available on :4747'); return; } - if (frontendRes.status === 'rejected' || (frontendRes.status === 'fulfilled' && !frontendRes.value.ok)) { + if ( + frontendRes.status === 'rejected' || + (frontendRes.status === 'fulfilled' && !frontendRes.value.ok) + ) { test.skip(true, 'Vite dev server not available on :5173'); return; } @@ -85,7 +91,9 @@ test.describe('Processes Panel', () => { await page.getByRole('button', { name: 'Nexus AI' }).click(); await page.getByText('Processes').click(); - await expect(page.locator('[data-testid="process-list-loaded"]')).toBeVisible({ timeout: 15_000 }); + await expect(page.locator('[data-testid="process-list-loaded"]')).toBeVisible({ + timeout: 15_000, + }); await page.screenshot({ path: testInfo.outputPath('processes-panel.png'), fullPage: true }); const processRow = page.locator('[data-testid="process-row"]').first(); @@ -96,7 +104,10 @@ test.describe('Processes Panel', () => { await viewBtn.waitFor({ state: 'visible', timeout: 5_000 }); await viewBtn.click(); await expect(page.locator('[data-testid="process-modal"]')).toBeVisible({ timeout: 5_000 }); - await page.screenshot({ path: testInfo.outputPath('process-view-clicked.png'), fullPage: true }); + await page.screenshot({ + path: testInfo.outputPath('process-view-clicked.png'), + fullPage: true, + }); }); test('lightbulb highlights nodes in graph', async ({ page }, testInfo) => { @@ -104,7 +115,9 @@ test.describe('Processes Panel', () => { await page.getByRole('button', { name: 'Nexus AI' }).click(); await page.getByText('Processes').click(); - await expect(page.locator('[data-testid="process-list-loaded"]')).toBeVisible({ timeout: 15_000 }); + await expect(page.locator('[data-testid="process-list-loaded"]')).toBeVisible({ + timeout: 15_000, + }); const processRow = page.locator('[data-testid="process-row"]').first(); await expect(processRow).toBeVisible({ timeout: 10_000 }); @@ -129,10 +142,14 @@ test.describe('Turn Off All Highlights', () => { await fileItem.click(); const highlightToggle = page.locator('[data-testid="ai-highlights-toggle"]'); - await expect(highlightToggle).toHaveAttribute('title', 'Turn off all highlights', { timeout: 5_000 }); + await expect(highlightToggle).toHaveAttribute('title', 'Turn off all highlights', { + timeout: 5_000, + }); await highlightToggle.click(); - await expect(highlightToggle).toHaveAttribute('title', 'Turn on AI highlights', { timeout: 5_000 }); + await expect(highlightToggle).toHaveAttribute('title', 'Turn on AI highlights', { + timeout: 5_000, + }); await page.screenshot({ path: testInfo.outputPath('highlights-cleared.png'), fullPage: true }); }); }); diff --git a/gitnexus-web/index.html b/gitnexus-web/index.html index 13f6013793..95f25b846e 100644 --- a/gitnexus-web/index.html +++ b/gitnexus-web/index.html @@ -4,9 +4,12 @@ GitNexus - - - + + +
diff --git a/gitnexus-web/playwright.config.ts b/gitnexus-web/playwright.config.ts index 11e5eeeee2..291b1805f3 100644 --- a/gitnexus-web/playwright.config.ts +++ b/gitnexus-web/playwright.config.ts @@ -40,9 +40,6 @@ export default defineConfig({ use: { browserName: 'chromium' }, }, ], - reporter: [ - ['list'], - ['html', { open: 'never', outputFolder: 'playwright-report' }], - ], + reporter: [['list'], ['html', { open: 'never', outputFolder: 'playwright-report' }]], outputDir: 'test-results', }); diff --git a/gitnexus-web/src/App.tsx b/gitnexus-web/src/App.tsx index 2ad50ad36e..7916d3268c 100644 --- a/gitnexus-web/src/App.tsx +++ b/gitnexus-web/src/App.tsx @@ -11,7 +11,15 @@ import { FileTreePanel } from './components/FileTreePanel'; import { CodeReferencesPanel } from './components/CodeReferencesPanel'; import { getActiveProviderConfig } from './core/llm/settings-service'; import { createKnowledgeGraph } from './core/graph/graph'; -import { connectToServer, fetchRepos, normalizeServerUrl, connectHeartbeat, BackendError, type ConnectResult, type BackendRepo } from './services/backend-client'; +import { + connectToServer, + fetchRepos, + normalizeServerUrl, + connectHeartbeat, + BackendError, + type ConnectResult, + type BackendRepo, +} from './services/backend-client'; import { ERROR_RESET_DELAY_MS } from './config/ui-constants'; const AppContent = () => { @@ -41,36 +49,39 @@ const AppContent = () => { const graphCanvasRef = useRef(null); - const handleServerConnect = useCallback(async (result: ConnectResult): Promise => { - // Extract project name from repoPath - const repoPath = result.repoInfo.repoPath ?? result.repoInfo.path; - const parts = (repoPath || '').split('/').filter(p => p && !p.startsWith('.')); - const projectName = parts[parts.length - 1] || parts[0] || 'server-project'; - setProjectName(projectName); - - // Build KnowledgeGraph from server data for visualization - const graph = createKnowledgeGraph(); - for (const node of result.nodes) { - graph.addNode(node); - } - for (const rel of result.relationships) { - graph.addRelationship(rel); - } - setGraph(graph); - - // Transition directly to exploring view - setViewMode('exploring'); - - // Initialize agent with backend queries, then start embeddings - try { - if (getActiveProviderConfig()) { - await initializeAgent(projectName); + const handleServerConnect = useCallback( + async (result: ConnectResult): Promise => { + // Extract project name from repoPath + const repoPath = result.repoInfo.repoPath ?? result.repoInfo.path; + const parts = (repoPath || '').split('/').filter((p) => p && !p.startsWith('.')); + const projectName = parts[parts.length - 1] || parts[0] || 'server-project'; + setProjectName(projectName); + + // Build KnowledgeGraph from server data for visualization + const graph = createKnowledgeGraph(); + for (const node of result.nodes) { + graph.addNode(node); + } + for (const rel of result.relationships) { + graph.addRelationship(rel); } - startEmbeddingsWithFallback(); - } catch (err) { - console.warn('Failed to initialize agent:', err); - } - }, [setViewMode, setGraph, setProjectName, initializeAgent, startEmbeddingsWithFallback]); + setGraph(graph); + + // Transition directly to exploring view + setViewMode('exploring'); + + // Initialize agent with backend queries, then start embeddings + try { + if (getActiveProviderConfig()) { + await initializeAgent(projectName); + } + startEmbeddingsWithFallback(); + } catch (err) { + console.warn('Failed to initialize agent:', err); + } + }, + [setViewMode, setGraph, setProjectName, initializeAgent, startEmbeddingsWithFallback], + ); // Auto-connect when ?server query param is present (bookmarkable shortcut) const autoConnectRan = useRef(false); @@ -84,7 +95,12 @@ const AppContent = () => { const cleanUrl = window.location.pathname + window.location.hash; window.history.replaceState(null, '', cleanUrl); - setProgress({ phase: 'extracting', percent: 0, message: 'Connecting to server...', detail: 'Validating server' }); + setProgress({ + phase: 'extracting', + percent: 0, + message: 'Connecting to server...', + detail: 'Validating server', + }); setViewMode('loading'); const serverUrl = params.get('server') || window.location.origin; @@ -93,34 +109,51 @@ const AppContent = () => { connectToServer(serverUrl, (phase, downloaded, total) => { if (phase === 'validating') { - setProgress({ phase: 'extracting', percent: 5, message: 'Connecting to server...', detail: 'Validating server' }); + setProgress({ + phase: 'extracting', + percent: 5, + message: 'Connecting to server...', + detail: 'Validating server', + }); } else if (phase === 'downloading') { const pct = total ? Math.round((downloaded / total) * 90) + 5 : 50; const mb = (downloaded / (1024 * 1024)).toFixed(1); - setProgress({ phase: 'extracting', percent: pct, message: 'Downloading graph...', detail: `${mb} MB downloaded` }); + setProgress({ + phase: 'extracting', + percent: pct, + message: 'Downloading graph...', + detail: `${mb} MB downloaded`, + }); } else if (phase === 'extracting') { - setProgress({ phase: 'extracting', percent: 97, message: 'Processing...', detail: 'Extracting file contents' }); + setProgress({ + phase: 'extracting', + percent: 97, + message: 'Processing...', + detail: 'Extracting file contents', + }); } - }).then(async (result) => { - await handleServerConnect(result); - setProgress(null); - setServerBaseUrl(baseUrl); - fetchRepos() - .then((repos) => setAvailableRepos(repos)) - .catch((e) => console.warn('Failed to fetch repo list:', e)); - }).catch((err) => { - console.error('Auto-connect failed:', err); - setProgress({ - phase: 'error', - percent: 0, - message: 'Failed to connect to server', - detail: err instanceof Error ? err.message : 'Unknown error', - }); - setTimeout(() => { - setViewMode('onboarding'); + }) + .then(async (result) => { + await handleServerConnect(result); setProgress(null); - }, ERROR_RESET_DELAY_MS); - }); + setServerBaseUrl(baseUrl); + fetchRepos() + .then((repos) => setAvailableRepos(repos)) + .catch((e) => console.warn('Failed to fetch repo list:', e)); + }) + .catch((err) => { + console.error('Auto-connect failed:', err); + setProgress({ + phase: 'error', + percent: 0, + message: 'Failed to connect to server', + detail: err instanceof Error ? err.message : 'Unknown error', + }); + setTimeout(() => { + setViewMode('onboarding'); + setProgress(null); + }, ERROR_RESET_DELAY_MS); + }); }, [handleServerConnect, setProgress, setViewMode, setServerBaseUrl, setAvailableRepos]); const handleFocusNode = useCallback((nodeId: string) => { @@ -176,7 +209,7 @@ const AppContent = () => { // Exploring view return ( -
+
{ } catch (err: unknown) { if (attempt === 0 && err instanceof BackendError && err.status === 404) { // Server may still be reinitializing — wait and retry - await new Promise(r => setTimeout(r, 1500)); + await new Promise((r) => setTimeout(r, 1500)); continue; } console.error('Failed to connect after analyze:', err); - fetchRepos().then(repos => setAvailableRepos(repos)).catch(() => {}); + fetchRepos() + .then((repos) => setAvailableRepos(repos)) + .catch(() => {}); return; } } }} /> -
+
{/* Left Panel - File Tree */} {/* Graph area - takes remaining space */} -
+
{/* Code References Panel (overlay) - does NOT resize the graph, it overlaps on top */} {isCodePanelOpen && (codeReferences.length > 0 || !!selectedNode) && ( -
+
)} @@ -239,7 +274,6 @@ const AppContent = () => { onClose={() => setSettingsPanelOpen(false)} onSettingsSaved={handleSettingsSaved} /> -
); }; diff --git a/gitnexus-web/src/components/AnalyzeOnboarding.tsx b/gitnexus-web/src/components/AnalyzeOnboarding.tsx index 1884b66f5a..f4147f57cb 100644 --- a/gitnexus-web/src/components/AnalyzeOnboarding.tsx +++ b/gitnexus-web/src/components/AnalyzeOnboarding.tsx @@ -25,49 +25,44 @@ interface AnalyzeOnboardingProps { export const AnalyzeOnboarding = ({ onComplete }: AnalyzeOnboardingProps) => { return ( -
- +
{/* Ambient glows — mirrors OnboardingGuide aesthetic */} -
-
+
+
{/* Header */}
- {/* Eyebrow */} -
- - +
+ + GitNexus
{/* Icon */} -
- +
+
-

+

Analyze your first repository

-

- Paste a GitHub URL and GitNexus will clone it, parse the code, and - build a live knowledge graph — right in your browser. +

+ Paste a GitHub URL and GitNexus will clone it, parse the code, and build a live + knowledge graph — right in your browser.

{/* Analyzer form */}
- +
{/* Footer hint */} -

+

Public repos only · Cloned locally by the server · No data leaves your machine

diff --git a/gitnexus-web/src/components/AnalyzeProgress.tsx b/gitnexus-web/src/components/AnalyzeProgress.tsx index c16d4cd6c3..213c7b3e68 100644 --- a/gitnexus-web/src/components/AnalyzeProgress.tsx +++ b/gitnexus-web/src/components/AnalyzeProgress.tsx @@ -49,33 +49,26 @@ export const AnalyzeProgress = ({ progress, onCancel }: AnalyzeProgressProps) =>
{/* Phase label + elapsed */}
- {label} - {formatElapsed(elapsed)} + {label} + {formatElapsed(elapsed)}
{/* Progress bar */} -
+
{/* Percent + cancel */}
- {pct}% + {pct}%
diff --git a/gitnexus-web/src/components/CodeReferencesPanel.tsx b/gitnexus-web/src/components/CodeReferencesPanel.tsx index e5508568db..6357d15ad3 100644 --- a/gitnexus-web/src/components/CodeReferencesPanel.tsx +++ b/gitnexus-web/src/components/CodeReferencesPanel.tsx @@ -1,5 +1,16 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import { Code, PanelLeftClose, PanelLeft, Trash2, X, Target, FileCode, Sparkles, MousePointerClick, Loader2 } from '@/lib/lucide-icons'; +import { + Code, + PanelLeftClose, + PanelLeft, + Trash2, + X, + Target, + FileCode, + Sparkles, + MousePointerClick, + Loader2, +} from '@/lib/lucide-icons'; import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; import { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism'; import { useAppState } from '../hooks/useAppState'; @@ -47,7 +58,7 @@ export const CodeReferencesPanel = ({ onFocusNode }: CodeReferencesPanelProps) = const nodeById = useMemo(() => { if (!graph) return new Map(); - return new Map(graph.nodes.map(n => [n.id, n])); + return new Map(graph.nodes.map((n) => [n.id, n])); }, [graph]); const [isCollapsed, setIsCollapsed] = useState(false); @@ -85,34 +96,40 @@ export const CodeReferencesPanel = ({ onFocusNode }: CodeReferencesPanelProps) = } }, [panelWidth]); - const startResize = useCallback((e: React.MouseEvent) => { - e.preventDefault(); - e.stopPropagation(); - resizeRef.current = { startX: e.clientX, startWidth: panelWidth }; - document.body.style.cursor = 'col-resize'; - document.body.style.userSelect = 'none'; - - const onMove = (ev: MouseEvent) => { - const state = resizeRef.current; - if (!state) return; - const delta = ev.clientX - state.startX; - const next = Math.max(420, Math.min(state.startWidth + delta, 900)); - setPanelWidth(next); - }; - - const onUp = () => { - resizeRef.current = null; - document.body.style.cursor = ''; - document.body.style.userSelect = ''; - window.removeEventListener('mousemove', onMove); - window.removeEventListener('mouseup', onUp); - }; - - window.addEventListener('mousemove', onMove); - window.addEventListener('mouseup', onUp); - }, [panelWidth]); + const startResize = useCallback( + (e: React.MouseEvent) => { + e.preventDefault(); + e.stopPropagation(); + resizeRef.current = { startX: e.clientX, startWidth: panelWidth }; + document.body.style.cursor = 'col-resize'; + document.body.style.userSelect = 'none'; + + const onMove = (ev: MouseEvent) => { + const state = resizeRef.current; + if (!state) return; + const delta = ev.clientX - state.startX; + const next = Math.max(420, Math.min(state.startWidth + delta, 900)); + setPanelWidth(next); + }; + + const onUp = () => { + resizeRef.current = null; + document.body.style.cursor = ''; + document.body.style.userSelect = ''; + window.removeEventListener('mousemove', onMove); + window.removeEventListener('mouseup', onUp); + }; + + window.addEventListener('mousemove', onMove); + window.addEventListener('mouseup', onUp); + }, + [panelWidth], + ); - const aiReferences = useMemo(() => codeReferences.filter(r => r.source === 'ai'), [codeReferences]); + const aiReferences = useMemo( + () => codeReferences.filter((r) => r.source === 'ai'), + [codeReferences], + ); // When the user clicks a citation badge in chat, focus the corresponding snippet card: // - expand the panel if collapsed @@ -126,12 +143,9 @@ export const CodeReferencesPanel = ({ onFocusNode }: CodeReferencesPanelProps) = const { filePath, startLine, endLine } = codeReferenceFocus; const target = - aiReferences.find(r => - r.filePath === filePath && - r.startLine === startLine && - r.endLine === endLine - ) ?? - aiReferences.find(r => r.filePath === filePath); + aiReferences.find( + (r) => r.filePath === filePath && r.startLine === startLine && r.endLine === endLine, + ) ?? aiReferences.find((r) => r.filePath === filePath); if (!target) return; @@ -158,13 +172,21 @@ export const CodeReferencesPanel = ({ onFocusNode }: CodeReferencesPanelProps) = rafIds.push(outerRafId); return () => { - rafIds.forEach(id => cancelAnimationFrame(id)); + rafIds.forEach((id) => cancelAnimationFrame(id)); }; }, [codeReferenceFocus?.ts, aiReferences]); const refsWithSnippets = useMemo(() => { return aiReferences.map((ref) => { - return { ref, content: null as string | null, start: 0, end: 0, highlightStart: 0, highlightEnd: 0, totalLines: 0 }; + return { + ref, + content: null as string | null, + start: 0, + end: 0, + highlightStart: 0, + highlightEnd: 0, + totalLines: 0, + }; }); }, [aiReferences]); @@ -207,20 +229,29 @@ export const CodeReferencesPanel = ({ onFocusNode }: CodeReferencesPanelProps) = endLine: (endLine ?? startLine) + CONTEXT_LINES, }; - readFile(selectedFilePath, options).then(result => { - if (!cancelled) { - setFileResult(result); - setIsLoadingFile(false); - } - }).catch(() => { - if (!cancelled) { - setFileResult(null); - setIsLoadingFile(false); - } - }); + readFile(selectedFilePath, options) + .then((result) => { + if (!cancelled) { + setFileResult(result); + setIsLoadingFile(false); + } + }) + .catch(() => { + if (!cancelled) { + setFileResult(null); + setIsLoadingFile(false); + } + }); - return () => { cancelled = true; }; - }, [selectedFilePath, selectedNode?.properties?.startLine, selectedNode?.properties?.endLine, selectedIsFile]); + return () => { + cancelled = true; + }; + }, [ + selectedFilePath, + selectedNode?.properties?.startLine, + selectedNode?.properties?.endLine, + selectedIsFile, + ]); // Scroll to the selected node's startLine after content loads useEffect(() => { @@ -234,8 +265,9 @@ export const CodeReferencesPanel = ({ onFocusNode }: CodeReferencesPanelProps) = if (cancelled) return; const container = selectedViewerRef.current; if (!container) return; - const lineEl = container.querySelector(`[data-line-number="${startLine + 1}"]`) as HTMLElement - ?? container.querySelectorAll('.linenumber')[startLine] as HTMLElement; + const lineEl = + (container.querySelector(`[data-line-number="${startLine + 1}"]`) as HTMLElement) ?? + (container.querySelectorAll('.linenumber')[startLine] as HTMLElement); if (lineEl) { lineEl.scrollIntoView({ behavior: 'smooth', block: 'center' }); } else { @@ -247,27 +279,30 @@ export const CodeReferencesPanel = ({ onFocusNode }: CodeReferencesPanelProps) = rafIds.push(innerRaf); }); const rafIds = [outerRaf]; - return () => { cancelled = true; rafIds.forEach(id => cancelAnimationFrame(id)); }; + return () => { + cancelled = true; + rafIds.forEach((id) => cancelAnimationFrame(id)); + }; }, [selectedFileContent, selectedNode?.properties?.startLine]); if (isCollapsed) { return ( -