* fix: add platform-aware semantic fallback
Make VECTOR an optional capability so Windows analysis remains stable while semantic embeddings can fall back to exact scan when native vector indexing is unavailable.
Made-with: Cursor
* fix: remove stale vector pool import
Keep the merge with main lint-clean after VECTOR loading moved out of the read pool.
* feat(ingestion): GITNEXUS_INDEX_TEST_DIRS opt-in for __tests__ / __mocks__ (#771)
The DEFAULT_IGNORE_LIST hardcodes __tests__ and __mocks__ as
auto-filtered directory names. The comment at ignore-service.ts:273
explicitly documents this as intentional — .gitnexusignore negation
cannot override hardcoded entries. That default is right for the
majority of users, but for Quality Engineering workflows where test
files are the primary index target (tracing coverage via CALLS
edges), there was no escape hatch short of patching the installed
package.
Add an opt-in env var mirroring the GITNEXUS_NO_GITIGNORE /
GITNEXUS_MAX_FILE_SIZE precedent:
`GITNEXUS_INDEX_TEST_DIRS=1` removes __tests__ / __mocks__ from the
effective ignore set. Scope is deliberately limited to these two
names — the issue asked for these specifically, and other
test-adjacent entries (__snapshots__, snapshots, fixtures, .jest)
remain auto-filtered unchanged. .gitnexusignore negation semantics
are not touched; the env var is the orthogonal escape hatch.
Implementation: new `isEffectivelyIgnoredDirectory` helper in
ignore-service.ts wraps the `DEFAULT_IGNORE_LIST.has(name)` check
with the env-var opt-out. Two call-sites swap: shouldIgnorePath
(affects filesystem walker and wiki generator) and
createIgnoreFilter.childrenIgnored (affects directory pruning
during traversal). `isHardcodedIgnoredDirectory` export unchanged —
its contract is "is in the raw list", which remains true for
__tests__ / __mocks__ regardless of env var state (locked in by a
test).
Default behaviour is byte-identical for users who don't set the env
var. 9 new unit tests cover default-unset, opt-in-set, scoped scope
(other hardcoded entries unaffected), and the scope-discipline
guard (future expansion beyond the two named dirs fails loudly).
Env state restored by afterEach to prevent leakage.
Closes#771.
* feat(ingestion): .gitnexusignore negation overrides hardcoded DEFAULT_IGNORE_LIST (#771)
Per @magyargergo's review feedback: rather than add a special-case
GITNEXUS_INDEX_TEST_DIRS env var to unlock __tests__ / __mocks__,
let .gitnexusignore use !pattern negation to override the hardcoded
DEFAULT_IGNORE_LIST — mirroring the .gitignore mental model users
already know.
Implementation:
- New private hasExplicitUnignore(ig, rel) helper that walks ancestor
segments and uses ignore.test(path)'s `unignored` flag to detect
explicit negation. Ancestor-walk is required because .gitignore
negation propagates — !__tests__/ implicitly unignores every
descendant, but ignore.test() only reports unignored: true on the
directly-matched path.
- createIgnoreFilter.ignored() and .childrenIgnored() now check
hasExplicitUnignore BEFORE applying the hardcoded DEFAULT_IGNORE_LIST.
If any ancestor (or the path itself) was explicitly unignored in
.gitnexusignore, the hardcoded block is bypassed.
- shouldIgnorePath stays pure hardcoded-list — the wiki generator and
other callers without per-repo config context keep deterministic
behavior. The #771 override lives only inside createIgnoreFilter,
which IS called with config.
Dropped:
- GITNEXUS_INDEX_TEST_DIRS env var (superseded by the more general
negation mechanism)
- isEffectivelyIgnoredDirectory helper
- Associated env-var help text and unit tests
Added:
- Tip in analyze --help pointing users at .gitnexusignore with
!__tests__/ as the example
- 8 new unit tests covering default behaviour, directory-level
negation, selective overrides, generalisation (!node_modules/),
non-leakage across hardcoded entries, standard non-negation rules
still layering on top, and preservation of shouldIgnorePath /
isHardcodedIgnoredDirectory contracts
Default behaviour (no .gitnexusignore or no negation pattern) is
byte-identical to pre-#771. Users who want to index an auto-filtered
directory add a single !pattern line — no env var, no flag, no
re-install.
Closes#771.
* fix(ingestion): honour re-ignore rules after .gitnexusignore negation (#771)
When .gitnexusignore contains both `!__tests__/` and
`__tests__/generated/`, the parent negation previously short-circuited
and allowed the re-ignored child through. Consult `ig.ignores(rel)`
after `hasExplicitUnignore` so a more-specific rule in the same file
correctly re-ignores a subset — matching .gitignore's last-match-wins
semantics. Adds a compound-pattern test locking this in.
* feat(ingestion): make large-file skip threshold configurable
The walker previously hardcoded a 512KB skip threshold, which silently dropped legitimate large source files (e.g. ~900KB hand-written Java service classes) during analysis with no way to override short of editing source.
Allow overrides via the GITNEXUS_MAX_FILE_SIZE env var (KB) — consistent with the existing GITNEXUS_NO_GITIGNORE / GITNEXUS_VERBOSE patterns — and a matching --max-file-size <kb> flag on gitnexus analyze.
- New utility getMaxFileSizeBytes() in core/ingestion/utils/max-file-size.ts parses the env var, falls back to the 512KB default for missing/invalid values, and clamps against TREE_SITTER_MAX_BUFFER (32MB) to keep the downstream parser safe.
- filesystem-walker.ts now resolves the threshold per call and drops the 'likely generated/vendored' editorial when the user has explicitly raised the limit.
- analyze CLI wires --max-file-size to the env var and echoes a one-line notice when the threshold is overridden, mirroring how --no-gitignore is handled.
- index.ts documents the new flag and env var under the analyze help text.
- Warnings for invalid or out-of-range values are emitted exactly once per distinct value to avoid log spam.
Tests:
- New test/unit/max-file-size.test.ts covers defaults, KB parsing, clamp-at-ceiling, invalid-input fallback + warn-once, and distinct-value warnings.
- test/integration/filesystem-walker.test.ts gains a 'large file skip threshold (#991)' block: 600KB fixture skipped by default, included under GITNEXUS_MAX_FILE_SIZE=1024, invalid values fall back and warn once, and the 'generated/vendored' suffix is only emitted under the default threshold.
Closes#991
* fix(cli): show effective clamped max-file-size in banner
Addresses the PR #1044 review finding: the startup banner printed the raw GITNEXUS_MAX_FILE_SIZE value rather than the clamped effective threshold, producing misleading telemetry when the value exceeded the 32 MB tree-sitter ceiling.
The banner is also suppressed when the effective threshold equals the default, removing log noise when operators explicitly set the value to the current default.
Extracted the logic into a new getMaxFileSizeBannerMessage() helper and pinned the behavior with unit tests covering default, raised override, invalid fallback, and above-ceiling clamp cases.
* feat(cli): gitnexus remove <target> to unindex a registered repo by name or path (#664)
Add a `remove` CLI command that deletes the `.gitnexus/` index AND
unregisters a repo from the global registry (~/.gitnexus/registry.json),
addressing the lifecycle gap flagged in #664: previously users had to
cd into the repo to run `clean`, and there was no path-based or
alias-based remove for an already-deleted working tree.
- New command `gitnexus remove <target> [-f|--force]`. `<target>` is
alias / basename-derived name / remote-inferred name / absolute path.
- New helper `resolveRegistryEntry(entries, target)` in repo-manager.ts
with path > name precedence; throws RegistryNotFoundError or
RegistryAmbiguousTargetError (typed, `kind`-discriminated).
- Atomicity mirrors `clean`: fs.rm first, then unregisterRepo; partial
failures self-heal on next `listRegisteredRepos({ validate: true })`.
- Idempotent on unknown targets (exit 0 with warning) per the #664
spec: "behave atomically and idempotently so retries are safe".
- `--force` uses `clean`-style confirmation-skip semantics — distinct
from `analyze --force` (pipeline re-index); here there is no pipeline
so no conflation.
- 7 new unit tests cover resolver precedence, case sensitivity,
ambiguity, and not-found hints; 2 integration tests cover the real
CLI -> registry -> filesystem chain including the --allow-duplicate-name
(#829) ambiguity case.
* fix(cli): canonicalize repo paths so remove/register match across platforms (#1003 review)
Address review feedback from @evander-wang and @magyargergo on PR #1003
plus the Windows + macOS CI failure (same root cause).
Problem:
- macOS: /var is a symlink to /private/var. `path.resolve` does NOT
follow symlinks, so a child running analyze in /var/folders/X stores
/private/var/folders/X (realpath from OS cwd) but an outer caller
passing the symlink form misses.
- Windows: GitHub runners surface tmpdirs in 8.3 short-name form
(RUNNERA~1) while process.cwd() returns the long form (runneradmin).
Same divergence.
Fix: new `canonicalizePath(p)` helper wraps `path.resolve` plus
`fs.realpathSync.native`, falling back to `path.resolve` when the path
doesn't exist (preserves idempotent-on-missing semantics needed by
`remove <unknown>`). Applied at 3 call-sites — registerRepo,
unregisterRepo, resolveRegistryEntry — canonicalising BOTH the input
and each stored `entry.path` at compare time. That last bit is the
backward-compat story: registries written by older versions
(pre-canonicalisation) still match correctly, so we don't need a
migration script.
Test side: the ambiguous-target integration test now reads the path
from the registry snapshot rather than passing the outer `repoA`
variable directly, so it exercises the registry contract regardless of
which path form the platform stores. 4 new unit tests cover the helper
(idempotent, fallback-on-missing, absolute-for-relative) plus the
backward-compat resolver path.
* fix(cli): store resolved (non-canonical) path, compare via canonicalizePath (#1003 CI)
Follow-up to c5eceba0. The previous commit canonicalised the repo path
at BOTH write-time AND compare-time in registerRepo — that expanded
Windows 8.3 short names (RUNNER~1) to long names (runneradmin) when
storing `entry.path`. Pre-existing #829 unit tests that assert
`path.resolve(err.existingPath) === path.resolve(tmpPath)` then broke
because `tmpPath` is still short-form (path.resolve doesn't expand
8.3) while `entry.path` was long-form (canonicalizePath does).
Fix: split storage from comparison.
- entry.path stores `path.resolve(repoPath)` — whatever form the
caller passed. `list` output and error messages show the path the
user typed.
- All compare points (existing-entry lookup in registerRepo, the
collision guard, unregisterRepo, resolveRegistryEntry path tier)
canonicalise BOTH sides via `canonicalizePath`. That is where the
/var ↔ /private/var and RUNNER~1 ↔ runneradmin divergence actually
matters.
Net effect: storage is tolerant (preserves user input), matching is
strict (canonical-vs-canonical). Pre-existing #829 tests stay green
because `err.existingPath` is unchanged from what `path.resolve` gives
back; the cross-platform CI failure from #1003 stays fixed because
every comparison path goes through `canonicalizePath`.
* fix(cli): refuse destructive fs.rm when registry storagePath isn't <repo>/.gitnexus (#1003 review)
Address @magyargergo's inline review finding on remove.ts:89 and the
sibling vulnerability in clean.ts --all (caught during a pre-commit
safety audit). ~/.gitnexus/registry.json is a user-writable plain-text
file, so a corrupted or hand-edited entry could point storagePath at
the repo root (catastrophic: rm the working tree), an empty string
(→ cwd), a parent dir, or anywhere else. fs.rm(recursive: true,
force: true) on any of those is a runtime disaster.
- New UnsafeStoragePathError + exported assertSafeStoragePath() in
repo-manager.ts. Pure lexical string check (Windows-case-
insensitive) asserting entry.storagePath === path.join(entry.path,
'.gitnexus').
- Guard wired into BOTH destructive registry-trusting sites:
- remove.ts: exit 1 with actionable hint
- clean.ts --all: skip the poisoned entry with a warning and
continue (preserves existing per-repo error tolerance — one bad
entry doesn't halt the batch)
- clean.ts default path and server/api.ts are safe-by-construction
(they recompute storagePath from findRepo / getStoragePath rather
than trusting the registry field).
- 8 unit tests cover the guard (valid, repo-root, parent, empty,
unrelated, sibling, error payload, Windows case).
- 2 integration tests prove the full CLI path: remove-poisoned exits
1 without touching the working tree; clean --all with a poisoned
sibling entry cleans the good entry, skips the bad one, and leaves
the poisoned repo intact.
* test(cli): assert full remove dry-run + success output shape (#1003 NIT)
Address the one NIT from the senior-reviewer pass on PR #1003: the
integration test was only checking for the "Run with --force" hint in
dry-run output, not verifying that the three actual console.log lines
(alias, repo path, storage path) appear. Same weak check on the
success-branch "Removed" output.
Tighten both assertions to toContain(alias), toContain(entry.path),
toContain(storagePath). Catches silent format regressions — e.g. a
future refactor that drops a console.log line or swaps
entry.name/entry.path in the output.
No code change; +20 test lines. All assertions in the happy-path
integration test now fire for a meaningful reason.
Wire extractors into the sync pipeline with service boundary detection.
GroupService provides high-level API for all group operations.
- Sync pipeline: orchestrates extraction (HTTP, gRPC, topics) with
service boundary assignment and exact matching
- GroupService: groupList, groupSync, groupContracts, groupQuery,
groupStatus (groupImpact deferred to cross-repo follow-up PR)
- CLI: group create/add/remove/list/sync/contracts/query/status
- MCP tools: group_list, group_sync, group_contracts, group_query,
group_status
- Monorepo fixture: 3 services (auth/orders/gateway) connected via
gRPC + Kafka + HTTP — all intra-repo cross-links discovered
- Documentation: CLI commands and MCP tools added to both READMEs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(wiki): extend LLMConfig/CLIConfig with Azure and reasoning model fields
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(wiki): restore cursor model resolution, fix LLMProvider type, clean up regex
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(wiki): remove stale LLMProvider type alias from repo-manager
* fix(wiki): fix Azure auth header, api-version param, reasoning model params, content_filter error
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(wiki): tighten Azure detection, reasoning model regex, content_filter gating
- isReasoningModel: new regex matches only o1/o3 bare + any oN-mini/oN-preview; bare o4/o5/etc now return false
- isAzureProvider: use URL hostname matching to block spoofed subdomain URLs
- callLLM: warn on Azure legacy /deployments/ URL without api-version
- callLLM: gate content_filter error to azure===true; also catch ResponsibleAIPolicyViolation
- tests: add afterEach stub cleanup, spoofed-URL, bare-o4, non-Azure content_filter, and URL-only Azure auto-detect tests
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(wiki): detect content_filter finish_reason in SSE stream and throw clear error
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(wiki): skip delta accumulation after content_filter, use provider-neutral error message
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(wiki): add Azure OpenAI option to interactive setup wizard
Inserts Azure as option [3] in the provider menu (shifting Custom to [4]
and Cursor to [5]), adds guided Azure setup flow with resource/deployment
prompts, v1/legacy URL format selection, reasoning-model flag, and
content_filter error handling in the catch block.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(wiki): store explicit false for non-reasoning Azure deployments, trim resource name inputs
- isReasoningModelDeployment now stores false (not undefined) when user says no
- Always include isReasoningModel in saved azureConfig (no conditional guard needed)
- Trim resourceName and deploymentName prompt inputs to avoid whitespace issues
- Improve reasoning model note to mention Azure requirement
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(wiki): add --api-version and --reasoning-model CLI flags for Azure
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(wiki): include apiVersion and reasoningModel in hasCLIOverrides guard
* fix(wiki): default provider to 'openai' in resolveLLMConfig when not configured
* style: apply prettier formatting
* fix(wiki): address PR review — remove unrelated files, harden inputs
- Remove evidence/, fix-adapter.js, and planning doc accidentally included
- URL-encode apiVersion in buildRequestUrl to prevent query string injection
- Add --no-reasoning-model flag to allow CLI override of saved config
- Simplify verbose ternary in Azure wizard prompt
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(wiki): use execFileSync for EDITOR to prevent shell injection
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* 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)
* 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.
* chore: add .git-blame-ignore-revs for prettier format commit
* perf: pre-commit hook runs only tests related to staged files
Use vitest --related to scope test execution to tests that import
the changed files, instead of running the full suite on every commit.
* perf: remove vitest from pre-commit hook, keep in CI only
Pre-commit now runs lint-staged + tsc only. Tests run in CI
(ci-tests.yml) where they belong — keeps commits fast.
* ci: add prettier format check to quality workflow
PRs will now fail if code isn't formatted with prettier.
Add Cursor headless CLI as a 4th provider option for wiki generation,
allowing users to leverage their Cursor subscription for wiki pages.
- Add --provider cursor flag and cursor-client.ts
- Add --review flag for interactive module tree editing
- Add --verbose flag for debugging
- Improve module tree generation (flatten single-child, unique slugs)
- Prevent DB timeout during long LLM calls
Usage: gitnexus wiki --provider cursor --model claude-4.5-opus-high
Made-with: Cursor
Addresses all review items from @magyargergo and Copilot:
1. **Rename --no-git to --skip-git**: Commander.js treats --no-X flags
as negation of --X (stores as options.git = false, not options.noGit).
--skip-git maps correctly to options.skipGit.
2. **Fix false " Already up to date\ on non-git folders**: When
currentCommit is empty string, skip the cache check — we cannot
detect changes without git, so always rebuild.
3. **Replace isGitRepo() with hasGitDir()**: Use filesystem check
(statSync on .git) instead of shelling out to git CLI. Consistent,
faster, and works when git is not installed.
4. **Fix misleading warning**: Message now only fires when .git
directory is actually absent (not when git CLI fails).
5. **Add CLI integration tests**: Verify Commander maps --skip-git
correctly and that non-git folders are rejected without the flag.
- Replace require(" fs\) with ESM-compatible top-level import (statSync)
- Register --no-git option in Commander CLI definition
- Use hasGitDir() instead of isGitRepo() for .gitignore update guard
to match the PR intent (filesystem check vs git CLI invocation)
* feat(ingestion): respect .gitignore and .gitnexusignore during file discovery
Add support for excluding files from indexing based on .gitignore and
.gitnexusignore patterns. Previously, GitNexus used only a hardcoded
ignore list, causing significant index pollution in repositories with
git-ignored directories containing code (e.g., Docker-mounted volumes).
Changes:
- Add `ignore` package for gitignore-spec pattern matching
- Add `loadIgnoreRules()` to parse .gitignore + .gitnexusignore
- Add `createIgnoreFilter()` returning glob-compatible IgnoreLike object
- Integrate filter into glob's `ignore` option for directory-level pruning
- Remove post-glob `.filter()` call (now handled during traversal)
The hardcoded DEFAULT_IGNORE_LIST remains as fallback for non-git repos.
Closes#228
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(ingestion): address review feedback on ignore filtering
- Distinguish ENOENT vs EACCES in loadIgnoreRules (warn on permission errors)
- Add GITNEXUS_NO_GITIGNORE env var to bypass .gitignore parsing
- Fix bare-name pattern matching in childrenIgnored (check both with/without trailing slash)
- Rename isIgnoredDirectory to isHardcodedIgnoredDirectory for clarity
- Add clarifying comments for design decisions (D2 negation, D3 dot:false redundancy)
- Add tests for bare-name patterns, file-glob patterns, EACCES handling, env var
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(ingestion): address second round of review feedback
- G1: Document GITNEXUS_NO_GITIGNORE in `analyze --help` and log when active
- G2: Add comment clarifying path-scurry POSIX normalization contract
- G3: Add IgnoreOptions interface — env var now falls back, callers can
pass `noGitignore` explicitly for testability and future CLI flag
- G4: Add integration test verifying walkRepositoryPaths respects the env var
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat(ingestion): gracefully skip files with unavailable tree-sitter grammars
Port unsupported language resilience from PR #301 by @jecanore.
- Make Kotlin import optional (like Swift) in parser-loader and parse-worker
- Add worker-local isLanguageAvailable() with filePath param for tsx distinction
- Track and log skipped files per language in both sequential and worker paths
- Add skippedLanguages to ParseWorkerResult for worker→main aggregation
- Add isLanguageAvailable unit tests
Refs: #301, #155, #228
Co-Authored-By: jecanore <juan@housingbase.io>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* test(e2e): add ignore + language-skip end-to-end test with fixture repo
Add a fixture repo (test/fixtures/ignore-and-skip-repo/) with .gitignore,
.gitnexusignore, TypeScript source files, and a Swift file to exercise
all three features end-to-end:
- File discovery: verifies .gitignore excludes data/ and *.log,
.gitnexusignore excludes vendor/, source files are discovered
- Parsing: verifies TypeScript files produce Function nodes and DEFINES
relationships, Swift files are skipped gracefully when grammar is
unavailable
Add the test to the standalone group in ci-integration.yml and coverage job.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(ci): move ignore-and-skip-e2e test to e2e group per review feedback
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(test): use temp directory instead of fixture for e2e ignore test
The fixture's .gitignore prevented data/seed.json and debug.log from
being committed — these files would be missing after checkout in CI.
Switch to creating the entire test structure in a temp directory via
beforeAll (matching filesystem-walker.test.ts pattern). This ensures
all files exist regardless of git ignore rules.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(test): correct graph API usage in e2e ignore test
Use graph.nodes property getter instead of graph.getNodes(), and check
Function node filePath instead of non-existent File nodes (File nodes
are created by processStructure, not processParsing).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* ci: add workflows permission to ci-integration.yml
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* ci: change workflows permission to write per review
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* ci: move workflows permission from ci-integration.yml to ci.yml caller
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(ci): fix Claude workflows for fork PRs, remove misplaced workflows perm
Three issues prevented Claude from running on fork PRs:
1. claude-code-review.yml lacked workflows:write — push failed when
fork PRs modify .github/workflows/ files
2. claude.yml had no fork PR support — checked out main and couldn't
fetch the fork's branch from origin
3. Cleanup step unconditionally deleted branches even when push failed,
breaking the concurrent claude.yml workflow
Also removes workflows:write from ci.yml's integration job — CI tests
don't need that permission. The permission belongs on the claude
workflows that push fork branches.
Changes:
- Add workflows:write to both claude workflow permissions blocks
- Add fork PR detection + branch push/cleanup to claude.yml
- Add step id to push-fork; cleanup only runs if push succeeded
- Pass branch names via env vars to prevent shell injection (security)
- Add concurrency groups to prevent race conditions between workflows
- Remove misplaced workflows:write from ci.yml integration job
* fix(ci): use GitHub API for fork branch refs instead of git push
GITHUB_TOKEN cannot have 'workflows' permission — it's only valid for
PATs and GitHub Apps. This means git push fails whenever a fork PR
modifies .github/workflows/ files.
Replace git push with the GitHub REST API (POST/PATCH /git/refs) to
create temporary branch refs. The API creates a pointer to the
already-existing PR head commit without triggering the workflow file
push protection. Similarly, cleanup uses DELETE /git/refs instead of
git push --delete.
Also removes the invalid 'workflows: write' from permissions blocks.
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: jecanore <juan@housingbase.io>
Co-authored-by: Gergo Magyar <gergomagyar@icloud.com>
* calm fix 4 adding skills to repo [ISSUE #140]
* inspect
* unit and integration tests
* fixed hardcoded cohesion miss
* e2e tests for --skills flag for langauge/repo support
* Cohesion test e2e tests
* Fix MCP startup transport compatibility
* Preserve CLI flags in MCP startup fix
* Harden MCP transport error handling
* Harden transport security and improve type safety
Transport hardening:
- Add MAX_BUFFER_SIZE (10 MB) cap to prevent OOM from oversized
Content-Length or unbounded newline-delimited input
- Replace recursive readNewlineMessage with iterative loop to prevent
stack overflow from consecutive empty lines
- Tighten looksLikeContentLength to require 14+ bytes before matching
- Add closed-state guard and error handling to send()
- Simplify processReadBuffer loop to break on error
- Fix loose equality (==) to strict (===)
- Widen constructor param types to ReadableStream/WritableStream
Type safety:
- Constrain createLazyAction generics so export name is validated
against the module's actual exports at compile time
- Use proper type guard instead of lint suppression
- Fix test tsconfig type errors
Regression tests for all hardening fixes (13 tests passing).
---------
Co-authored-by: Gergo Magyar <gergomagyar@icloud.com>
Keep main's barLog implementation, preserve both currentDbPath and
ftsLoaded reset in closeKuzu, take PR's new resolveRepo pattern
for /api/query. Path traversal guard confirmed intact.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Address code review feedback on the server-mode PR:
Critical fixes:
- Restore CORS whitelist (localhost + gitnexus.vercel.app only)
- Bind to 127.0.0.1 by default; add --host CLI flag for opt-in remote access
- Restore path traversal guard on /api/file (resolve + startsWith check)
- Restore try/catch on all route handlers + global error middleware
- Restore SIGINT/SIGTERM graceful shutdown handlers
Bug fixes:
- Add mutex to core initKuzu to prevent race conditions on concurrent
DB switches (two requests for different repos no longer corrupt state)
- Track ftsLoaded flag and reset on DB switch / close so FTS extension
is reloaded for each new database connection
- Restore input validation on /api/query (cypher required) and
/api/search (query required)
Improvements:
- Add TTL-based cleanup for orphaned MCP sessions (30min idle eviction)
to prevent memory leaks from network drops that skip onclose
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Embeddings (ONNX Runtime) caused segfaults on macOS (#38) and CUDA
install failures on Linux (#40). Since embeddings only power semantic
vector search in query() and BM25 + graph traversal already provide
strong retrieval, disable embeddings by default with minimal quality
impact.
Changes:
- Replace --skip-embeddings with --embeddings opt-in flag
- Remove disposeEmbedder() call (ONNX native cleanup segfaults)
- Add process.exit(0) when embeddings are used to bypass ONNX atexit hooks
- Suppress ONNX session warnings via logSeverityLevel
- Fix progress bar duplication by routing console output through bar.log()
- Add elapsed time indicator for long-running phases
Fixes#38Fixes#40