Commit Graph

128 Commits

Author SHA1 Message Date
Giancarlo Erra 8921690d72 fix(graph): resolve Python sibling-flat imports in service-style monorepos
Resolves #46. Reported by @mrsuit92.

Python projects where each top-level directory is a runnable application
root (a common service-style monorepo layout) had `import config` from
`service-a/main.py` produce 0 dependency edges, even when
`service-a/config.py` sits next to the importer. At runtime Python
resolves this correctly because the importer's directory is sys.path[0]
when the file is run as `python main.py` from inside its own directory.
The static resolver did not check that path.

The Python case in graph-resolution.ts only tried:

  <projectPath>/<module>.py
  <projectPath>/src/<module>.py
  <projectPath>/lib/<module>.py

It did not try `<sourceDir>/<module>.py`, so non-relative sibling
imports never resolved. Relative imports (`from .config import ...`)
already used sourceDir and worked.

Fix: add `<sourceDir>/<module>.py` as the LAST fallback, after the
existing project-root and src/lib checks. Tried last to preserve
project-root precedence, so any layout that resolved before this PR
continues to resolve to the same file. resolveRelativePath also handles
the `<sourceDir>/<module>/__init__.py` package case via its built-in
Python init fallback, so package-style sibling imports work too.

Tests: 5 new cases in tests/unit/graph-resolution.test.ts covering
sibling-flat resolution, dotted module paths, package via __init__.py,
project-root precedence preservation, and the negative case (no match
anywhere). Existing 730 tests continue to pass; total now 735.

typecheck, biome, and CodeRabbit local review all clean.

Co-authored-by: mrsuit92 <mrsuit92@users.noreply.github.com>
2026-05-05 00:54:22 +01:00
Giancarlo Erra bf36c0c1b7 docs: add note about MCP governance and JanuScope 2026-05-05 00:36:41 +01:00
Giancarlo Erra ccebb8d980 chore: release v1.8.4 v1.8.4 2026-05-04 19:16:25 +01:00
Giancarlo Erra e6ce32710a fix(graph): pre-validate ast-grep grammar libraryPath to survive missing prebuilds (#44)
Resolves #43.

On Linux/Node combinations where one ast-grep grammar package's prebuilt
parser binary is missing for the host architecture, the v1.8.3 loader
silently failed to register every dynamic grammar in the batch, not just
the broken one. registerDynamicLanguage iterates and accesses each
module's lazy libraryPath getter; one throwing getter aborts the call
atomically and zero grammars end up registered.

Fix: pre-validate each grammar's libraryPath getter inside the per-
grammar try/catch so a missing prebuild is contained to that grammar.
Build the batch object with only the survivors and make ONE atomic
registerDynamicLanguage call. Standard environments are unaffected
because all grammars pass pre-validation. Affected environments lose
only the unloadable grammar, the rest register cleanly.

Also captures the actual error reason (the previous empty `catch {}`
discarded it), bumps symbol- and import-extraction failure logs from
debug to warn with one-shot dedupe per language, exposes loaded/failed
grammars via a new getDynamicLanguageStatus() API, and renders an "AST
grammars" block in codebase_graph_status output so users see loader
state without enabling debug logging.

Empirically verified against @ast-grep/napi@0.40.5 in a clean Node
environment. Two probes confirmed the napi semantics: sequential
register({A}); register({B}) calls are REPLACING (so per-language
registration is broken), and batch register with one bad getter is
ATOMIC (so pre-validation before the batch call is the only correct
pattern). All 721 existing unit tests continue to pass unchanged. Adds
9 new tests for the loader status API; total 730 pass. typecheck and
biome clean. CodeRabbit returned no findings on this diff.

Co-authored-by: X-Adam <X-Adam@users.noreply.github.com>
2026-05-04 19:14:27 +01:00
Giancarlo Erra 852ae5d6d5 chore: release v1.8.3 v1.8.3 2026-05-04 12:48:54 +01:00
Tomasz Szuster 332ee800a8 feat(embeddings): add LM Studio as a first-class embedding provider (#42)
LM Studio's Local Server speaks the OpenAI-compatible /v1/embeddings
protocol, so users running it as their model host (chat plus embedding in
one desktop app, GGUF model management) had no clean integration path.

Changes:

- src/services/provider-lmstudio.ts: new LMStudioEmbeddingProvider wrapping
  the OpenAI SDK with a custom baseURL (default http://localhost:1234/v1).
  Sends a placeholder API key to satisfy the OpenAI SDK while LM Studio's
  Local Server runs without auth by default. Skips the dimensions parameter
  because LM Studio models have no Matryoshka projection. Forces
  encoding_format=float to defeat the OpenAI SDK 6.x base64 default, which
  would otherwise mangle LM Studio's plain-array responses into 1024 zeros.
- src/services/embedding-config.ts: extends the EmbeddingProvider union,
  reads LMSTUDIO_URL and LMSTUDIO_API_KEY, fail-fast validation when
  EMBEDDING_PROVIDER=lmstudio without EMBEDDING_MODEL or EMBEDDING_DIMENSIONS.
- src/services/embedding-provider.ts: factory case for lmstudio with a
  dynamic import to avoid loading the OpenAI SDK at startup for ollama users.
- ensureReady distinguishes "LM Studio unreachable" from "reachable but
  embedding model not loaded" so the operator knows whether to start the
  Local Server or load the configured model.
- src/services/qdrant.ts: minor refactor to extract the hybrid-search query
  payload to a local const for readability.
- README.md: dedicated LM Studio section, MCP host config example, env-var
  table entries.
- tests/unit/embedding-config.test.ts: 8 new cases (required-env validation,
  URL default and override, optional API key, context-length override).
- tests/unit/embedding-provider.test.ts: 3 new cases (factory wiring,
  ensureReady error format against a closed port, healthCheck unreachable
  output).

Backward compatible. The lmstudio provider is opt-in via
EMBEDDING_PROVIDER=lmstudio. Existing ollama, openai, and google paths are
untouched.
2026-05-04 12:47:26 +01:00
Giancarlo Erra f991c33c3c chore: release v1.8.2 v1.8.2 2026-05-04 10:47:13 +01:00
Giancarlo Erra baf0fe92ea Merge pull request #40 from a5345534/fix/jvm-symbol-extraction
fix: extract JVM symbol names from declarations
2026-05-04 10:43:50 +01:00
Shawn 1dbc1eb398 test: cover JVM annotations with parameters 2026-05-04 16:32:51 +08:00
Shawn 6a76ad4782 fix: cover JVM annotation and Scala callable edge cases 2026-05-04 16:07:01 +08:00
Shawn 019eba0583 fix: extract JVM symbol names from declarations 2026-05-04 15:51:34 +08:00
Giancarlo Erra aefc8ae1bc chore: release v1.8.1 v1.8.1 2026-05-04 02:09:44 +01:00
Giancarlo Erra 8d6cb86b27 fix(docs): replace broken Marketplace badges and surface listings in main README
shields.io's `visual-studio-marketplace/*` endpoints currently return
"404: badge not found", so the version and installs badges on the
extension's marketplace listing render as broken images. Switch to
`vsmarketplacebadges.dev`, which is the de-facto third-party
replacement most VS Code extensions fall back to.

Also surface the marketplace listings in the root README's top badge
row, alongside the existing MCP-install deep-link badges. The two
serve different purposes (the deep links register the MCP config
without an extension, the marketplace badges link to the extension
itself), so both stay.
2026-05-04 02:06:01 +01:00
Giancarlo Erra fa1755dccc chore: release v1.8.0 v1.8.0 2026-05-04 01:00:16 +01:00
Giancarlo Erra e30e8bfbd0 Merge pull request #39 from giancarloerra/feat/extensions
feat(extension): VS Code and Open VSX extension
2026-05-04 00:57:55 +01:00
Giancarlo Erra c2d012fe4c fix(extension): tighten graphPanel path and line-number bounds
Two follow-up review fixes on the interactive-graph webview surface:

- `loadGraphHtml(projectId)` now resolves the projectId-derived path
  and verifies the result stays inside `GRAPH_DIR`. The
  `socraticode.openInteractiveGraph` command accepts an arbitrary
  argument from any caller (palette, sidebar, other extensions), so a
  value like `../../etc/passwd` would otherwise escape the cache
  directory via `path.join`. Suspicious projectIds are now rejected
  with a log entry and the function returns `undefined`.
- `handleWebviewMessage` now opens the document first, then clamps
  the requested line number against the document's actual `lineCount`
  before constructing a `Range`. The previous `m.line > 0` check
  prevented negatives but allowed absurdly large values (e.g.
  `Number.MAX_SAFE_INTEGER`) that would build a Range past the end of
  the file. Selection is set on the resolved editor.

Lint, typecheck, manifest tests and build all clean.
2026-05-04 00:52:32 +01:00
Giancarlo Erra 562a946053 fix(extension): harden review-flagged paths
A pass over the extension surface to address review feedback:

Safety / hardening:

- `graphPanel.ts`: validate `m.path` from the webview before opening
  files. Reject absolute paths and any path that escapes the workspace
  root (`..`, `/foo`, `C:/...`). Validate the line number is a positive
  integer before constructing a `Range`. Surface failures via the output
  channel rather than letting the rejection bubble up.
- `mcpProvider.ts`: defensively check that
  `vscode.lm.registerMcpServerDefinitionProvider` exists before calling
  it. The `engines.vscode: ^1.99.0` field already enforces this on
  install, but some VS Code-derived editors mis-report their engine
  version. The extension now degrades gracefully (sidebar, commands,
  status bar still work) instead of failing activation.
- `commands.ts` and `graphPanel.ts`: wrap `workbench.action.chat.open`
  in try/catch. Not every VS Code-compatible editor exposes that
  command; falling back to the output channel avoids unhandled
  rejections after the user clicked "Open chat".
- `extension.ts`: persist the first-run walkthrough flag only after the
  walkthrough command resolves successfully, so a transient failure
  doesn't silently skip the onboarding forever.

CI gates:

- `extension-ci.yml` and `extension-release.yml`: run `npm test` between
  typecheck and build, so manifest-level smoke regressions can't slip
  through to either the PR artefact or the marketplace publishes.

Settings copy:

- `socraticode.env` description: explicitly call out that the setting
  is for non-secret config only. Recommend OS environment variables /
  local `.env` files for API keys, since workspace settings can sync
  via Settings Sync and end up in committed `.vscode/settings.json`.

Quality of life:

- `sidebar.ts` `formatRelative`: clamp the computed seconds to zero so
  a file mtime slightly ahead of the local clock doesn't render
  "-5s ago".
- `walkthroughs/first-index.md`: corrected the embedding model name
  (`nomic-embed-text`, not `mxbai-embed-large`) to match the engine
  default in `src/constants.ts`.

Lint / docs:

- `extension/README.md`: hyphenate "Eclipse Theia-based editors".
- `DEVELOPER.md`: add `text` language hint to the directory-tree code
  fence (markdownlint MD040). Updated the inline comment for
  `settings.ts` to reflect its current shape.
- `README.md`: reflow the "extension vs plugin" callout into a single
  blockquote (markdownlint MD028).

Lint, typecheck, manifest tests and build all clean. Engine unit tests
unaffected (706/706 still pass).
2026-05-04 00:41:51 +01:00
Giancarlo Erra a27b8ceb67 chore(extension): set publisher to giancarloerra
Marketplace URL becomes giancarloerra.socraticode (VS Code Marketplace)
and giancarloerra/socraticode (Open VSX), matching the registered
publisher account. The Altaire sponsor link and license contact email
stay as they are; only the publisher slug and the corresponding
shields.io / marketplace URLs in the README change.
2026-05-03 21:45:14 +01:00
Giancarlo Erra 9a197b3421 docs(extension): add Discord badge and hosted-edition pointer
Two small additions to the marketplace README:

- Discord badge in the header row, next to GitHub stars. Static
  shields.io badge linking to the community Discord. The auto-updating
  member-count variant can replace it later if we publish the server
  ID.
- New short "SocratiCode Cloud (private beta)" section between
  Compatibility and Privacy. Mirrors the wording on socraticode.cloud:
  managed infrastructure, webhook-driven indexing, shared team
  indexes, SSO/SAML, audit logs, SOC 2 / ISO 27001-aligned controls,
  private beta. Request-access link only; no in-extension Cloud
  feature is implied.

Marketplace listing now points readers at both the community channel
and the hosted edition without overstating either.
2026-05-03 21:15:59 +01:00
Giancarlo Erra 345c7281d0 docs(extension): editor-neutral marketplace README with hero, badges and benchmarks
The marketplace listing page is the single biggest conversion surface
for the extension. This pass turns the README into something that
reads as a polished product page, not an in-house engineering note.

Headline changes:

- Editor-neutral framing throughout. The same `.vsix` ships to VS
  Code, Cursor, VSCodium, Gitpod, code-server, Theia, Antigravity,
  and Particle Workbench. The previous wording was VS Code-first
  (Copilot agent mode listed first, "VS Code's MCP host", title with
  "for VS Code"). Replaced with editor-agnostic language: "any
  MCP-compatible chat or agent in your editor", followed by named
  surfaces (Copilot agent mode, Cursor's Agent / Composer, the
  Gemini surface in Antigravity, Cline, Continue, Roo Code, others).
- Hero image referenced via raw GitHub URL (absolute, survives both
  marketplace renderers).
- Badge row: VS Code Marketplace version + installs, Open VSX
  version + downloads, GitHub stars, npm engine version, license.
  Marketplace badges 404 until first publish; that's expected.
- Prominent "Full documentation, configuration reference, and
  benchmarks on GitHub" link directly under the badges. The extension
  README is intentionally short; the engine README is the deep doc.
- Headline benchmark callout up top: 61% less context, 84% fewer
  tool calls, 37x faster on the 2.45M-line VS Code codebase, with a
  direct anchor link to the full benchmark.
- New "Why bigger teams pick it" section covering refactor safety
  on monorepos, multi-repo orgs, tool-independence, air-gapped
  deployment, AGPL-3.0 transparency, and the 18+ language list.
- Compatibility section now lists every editor and every AI surface
  explicitly, so a Cursor or Antigravity user can recognise their
  setup without reading between the lines.
- Settings table simplified around the four real settings, with
  `socraticode.env` documented as the single passthrough for every
  engine knob (external Qdrant, embedding providers, project IDs,
  branch-aware indexing, linked projects).

Manifest tweak:

- Add `galleryBanner` (`#3a3d8c` brand colour, dark theme) so the
  Marketplace listing's hero strip matches the icon and the project
  website rather than defaulting to grey.

Build, lint, typecheck and the manifest smoke tests are all clean.
Packaged size 216 KB.
2026-05-03 21:02:03 +01:00
Giancarlo Erra d9459f8591 docs: surface VS Code / Open VSX extension and Cursor Marketplace
README updates:

- Plugins table now includes a row for the VS Code / Open VSX extension,
  with a list of every editor that pulls from one of the two registries
  (Cursor, VSCodium, Gitpod, code-server, Theia, Antigravity, Particle
  Workbench).
- Cursor Marketplace listing mentioned alongside the existing
  `/add-plugin` URL install.
- A short "extension vs plugin" callout explaining when to install
  which: the extension auto-registers MCP and adds native UI, the
  plugin formats add skills + agent rules. Both can coexist.

DEVELOPER.md updates:

- New "VS Code / Open VSX Extension" section covering layout, local
  development (F5 to launch an Extension Development Host), build /
  lint / test / package commands, publishing flow, and versioning
  policy (extension tracks the engine version via the bump script).
- Explicit list of what the extension is NOT (no re-implementation of
  search, no shipped engine, no language-server features). Keeps scope
  honest and prevents future feature creep.
- Added the new section to the table of contents.
2026-05-02 18:32:09 +01:00
Giancarlo Erra 3fe245c2f5 ci(extension): add lint, build, and dual-marketplace release workflows
`extension-ci.yml`: triggers on push and PR to `main` whenever anything
in `extension/` changes. Runs lint, typecheck, build, and `vsce
package`, then uploads the resulting `.vsix` as an artefact for manual
smoke testing (download from the Actions run, install via "Extensions:
Install from VSIX..." in any VS Code-compatible editor).

`extension-release.yml`: triggers on `v*` tags so engine release tags
fire the extension release in lockstep, with manual dispatch as a
fallback. Builds the `.vsix`, then publishes to:

- VS Code Marketplace via `vsce publish` (using the `VSCE_PAT` repo
  secret).
- Open VSX Registry via `ovsx publish` (using the `OVSX_PAT` repo
  secret).

When triggered by a tag, also attaches the `.vsix` to the GitHub
release so users on Open VSX-less editors can grab it directly.

Both workflows are scoped to the `extension/` working directory so
engine-only changes don't trigger them, and they rely on the
extension's own `package-lock.json` for npm caching. Permissions
follow least-privilege: workflow-level `contents: read`, with
`contents: write` granted only to the publish job for the GitHub
release upload step.
2026-05-02 18:31:47 +01:00
Giancarlo Erra bbc68199c3 feat(extension): add VS Code and Open VSX extension
A new top-level `extension/` package containing the SocratiCode VS Code
extension. The same `.vsix` artefact ships to both VS Code Marketplace
and Open VSX, which means it installs cleanly in Cursor, VSCodium,
Gitpod, code-server, Eclipse Theia editors, Google Antigravity, and
Particle Workbench in addition to stock VS Code.

What it does:

- Auto-registers the SocratiCode MCP server in VS Code's MCP host via
  `vscode.lm.registerMcpServerDefinitionProvider` (VS Code 1.99+).
  Copilot agent mode, Cline, Continue, Roo Code and any other
  MCP-aware client see SocratiCode's tools without the user editing
  any `.vscode/mcp.json`.
- Forwards the user's `socraticode.env` setting to the engine
  subprocess unchanged. Power users point at an external Qdrant
  (`QDRANT_MODE=external` + `QDRANT_URL` + `QDRANT_API_KEY`), pick an
  embedding provider, or set any other engine knob exactly as
  documented in the engine README.
- Activity Bar sidebar with "Indexed projects" tree view and welcome
  content. Discovers projects by listing graph artefacts the engine
  writes to `os.tmpdir()/socraticode-graph/`.
- Webview panel for the interactive graph (`graphPanel.ts`). Reads
  the engine's self-contained HTML, wraps it with a tight CSP, injects
  a bridge script so node-clicks can `postMessage` back to the
  extension and open files in the editor.
- Status-bar item showing "SocratiCode" with click-to-open-sidebar.
  Honours the `socraticode.statusBar` setting.
- Two-step getting-started walkthrough (index your project, try
  search and the interactive graph). Shown automatically on first
  install; re-openable via the command palette.
- Command palette commands: index workspace, open graph, refresh
  projects, open walkthrough, show output.
- Output channel for engine logs.

Build: esbuild bundles `src/extension.ts` to `dist/extension.js` (CJS,
node18, sourcemap), `vscode` external. Biome lint and `tsc` strict
mode both clean. Manifest smoke tests via Node's built-in test runner
catch package.json regressions before activation. Packaged size:
~215 KB `.vsix`.

Versioning: extension version tracks the engine version (currently
`1.7.2`). Patch drift is allowed for extension-only hotfixes. The
`scripts/bump-plugin-versions.mjs` hook from the previous commit
already includes `extension/package.json` in its bump list, so future
engine releases keep the extension in lockstep automatically.
2026-05-02 18:31:22 +01:00
Giancarlo Erra 6a91febe45 chore(release): synchronise plugin manifest versions on engine releases
The Cursor and Codex plugin manifests had drifted from the engine
version because the previous `release-it` `after:bump` hook only
updated `.claude-plugin/plugin.json`. Replace the inline-JS hook with
`scripts/bump-plugin-versions.mjs`, which iterates every known plugin
manifest (Claude / Cursor / Codex plus the upcoming
`extension/package.json`) and skips any that don't yet exist. This
keeps every distribution channel in lockstep on subsequent releases.

Bring `.cursor-plugin/plugin.json` and `.codex-plugin/plugin.json` up
to v1.7.2 so they reflect the current engine release. Tidy up the
plugin descriptions in the same pass (replace decorative dashes with
colons, no semantic change).
2026-05-02 18:30:53 +01:00
Giancarlo Erra 2bf07d8798 chore: release v1.7.2 v1.7.2 2026-04-28 14:02:02 +01:00
Giancarlo Erra 71013bf758 Merge pull request #36 from tazawa-masayoshi/fix/qdrant-cloud-readiness-api-key
fix(docker): include api-key header in external Qdrant readiness probe
2026-04-28 14:00:43 +01:00
Giancarlo Erra 7cdf21a961 fix(docker): require HTTPS for QDRANT_API_KEY; deflake no-key test
Address CodeRabbit findings on PR #36:

1. The previous patch attached `QDRANT_API_KEY` as an `api-key` header
   regardless of URL scheme, which would leak the secret on the wire if
   a user configured an authenticated Qdrant over plain HTTP. Add a
   guard that rejects the combination and throws a specific error,
   placed before the readiness probe so its message is not masked by
   the generic "Cannot reach external Qdrant" handler. Loopback URLs
   (`localhost`, `127.0.0.1`, `[::1]`) are accepted on `http://` so
   local-dev workflows where users run authenticated Qdrant on plain
   HTTP keep working. The URL is parsed (rather than checked with
   startsWith) so hostnames like `http://localhost.evil.com` are not
   mistaken for loopback.

2. The "omits api-key header when QDRANT_API_KEY is not set" test
   relied on the spread of the real `constants.js` module, which means
   it would flake to a header-attached state on any developer machine
   with `QDRANT_API_KEY` exported in the shell. Pass
   `QDRANT_API_KEY: undefined` explicitly so the override always wins.

3. Add two tests covering the new guard: one asserting the rejection
   on plain HTTP for non-loopback hosts, and one asserting that the
   localhost exception still attaches the api-key header.

4. Document the HTTPS requirement (and localhost exception) in the
   `QDRANT_API_KEY` row of the README configuration table.
2026-04-28 13:58:00 +01:00
tazawa-masayoshi 812fcd8905 fix(docker): include api-key header in external Qdrant readiness probe
External Qdrant deployments that require authentication (notably Qdrant Cloud,
which returns 403 on every endpoint including /healthz without an api-key
header) cannot pass the existing readiness check, so codebase_index fails with
"Cannot reach external Qdrant" even though codebase_health and codebase_search
still succeed -- their qdrant-js client paths attach the key automatically.

Forward QDRANT_API_KEY as an `api-key` request header from
ensureExternalQdrantReady() by extending waitForService() with an optional
RequestInit. Docker-managed and unauthenticated external Qdrant deployments
remain unaffected (no init forwarded -> no headers).

Covered by two new unit tests in docker.test.ts.
2026-04-28 13:49:55 +09:00
Giancarlo Erra 9e4fcf5192 chore: release v1.7.1 v1.7.1 2026-04-28 00:35:19 +01:00
Giancarlo Erra aa95a60a25 Merge pull request #34 from giancarloerra/fix/csharp-namespace-resolution
fix(graph): resolve C# using directives via namespace scan
2026-04-28 00:34:09 +01:00
Giancarlo Erra ea69a72145 fix(graph): tighten C# namespace regex; capture nested declarations
The previous regex `^namespace\s+([\w.]+)` was anchored at column 0, so
nested namespace declarations (the `namespace Inner` block inside
`namespace Outer { ... }`) were silently missed. The capture group also
accepted invalid identifiers like `1Foo` and matched on stray
occurrences of the word `namespace` not followed by `;` or `{`.

The new regex `^\s*namespace\s+([A-Za-z_]\w*(?:\.[A-Za-z_]\w*)*)\s*(?=[;{])`:

- `^\s*` allows leading whitespace, so indented (nested) declarations
  are captured.
- The dotted-identifier capture requires each segment to start with a
  letter or underscore, matching C# identifier rules.
- `(?=[;{])` requires a real terminator, filtering out spurious matches.

Adds three regression tests: nested-namespace capture, rejection of
digit-leading identifiers, and the terminator-lookahead behaviour.

Found by CodeRabbit on PR #34 (nitpick).
2026-04-28 00:30:36 +01:00
Giancarlo Erra fc249fdfcf fix(graph): make C# namespace resolution deterministic
`buildCsNamespaceMap` previously iterated `fileSet` in fs.readdir()
traversal order, which POSIX does not guarantee. Combined with the
`candidates[0]` selection in `resolveImport`, this meant a `using`
directive could resolve to different files on different machines or
runs.

Sort the `.cs` paths lexically before scanning so candidate lists are
stable. Adds a regression test that builds the map twice from sets
populated in different orders and asserts both produce the same
candidate sequence and the same first candidate.

Found by CodeRabbit on PR #34.
2026-04-28 00:20:45 +01:00
Giancarlo Erra 0aaf3f1d75 fix(graph): resolve C# using directives via namespace scan (closes #33)
C# imports previously always returned null because there is no direct
filename mapping for `using X.Y.Z;`. As a result `codebase_graph_query`
reported zero edges for C# projects, and the symbol-level tools
(impact, flow, symbol) silently lost cross-file resolution because they
walk the file-import graph for Tier 2/3 candidates.

Build a namespace-to-files map once per graph build by regex-scanning
every `.cs` file for `^namespace X.Y.Z` (covers both block-scoped and
file-scoped C# 10+ syntax) and consult it from the csharp branch of
resolveImport. When a namespace spans multiple files the first
candidate is returned; multi-file fan-out is tracked as a follow-up.
External namespaces (System.*, Microsoft.*) are still filtered upstream
by isExternalModule, so the map is only consulted for project-internal
names.

Cost is O(n) reads at graph-build time, gated by a `hasCs` precheck so
non-C# projects pay nothing.

Adds 12 unit tests covering block-scoped, file-scoped, multi-file,
multi-namespace-per-file, commented-out lines, non-.cs files, empty
projects, unknown namespaces, and external (System.*) filtering.
2026-04-28 00:04:01 +01:00
Giancarlo Erra 0e86cd0731 chore: release v1.7.0 v1.7.0 2026-04-27 23:46:19 +01:00
Giancarlo Erra b0356a517f chore: add release-it as dev dependency 2026-04-27 22:32:06 +01:00
Giancarlo Erra 7814ac7d60 Merge pull request #32 from giancarloerra/feat/impact-analysis
feat: Impact Analysis (symbol-level call graph) + Interactive HTML graph explorer
2026-04-27 22:14:36 +01:00
Giancarlo Erra e4da76979e feat(visualize): symbol view as focus graph; UX polish & stats consistency
Symbol view (full rebuild)
──────────────────────────
Replaces the unusable "show all symbols at once" mode with a focus
graph (SourceTrail / IntelliJ pattern):

- Landing state shows the alphabetical list of all symbols in the
  sidebar; canvas shows a "pick a symbol" overlay constrained to the
  canvas area (right: 340px) so the list stays visible.
- Three entry paths to seed: list click, search bar, or symbol click
  from a file's sidebar in Files view.
- Once seeded, canvas shows the symbol + its 2-hop callers/callees
  neighbourhood (auto-falls back to depth 1 if > 60 nodes).
- Clicking any neighbour re-centres on it. Seed has a distinctive
  orange ring + always-visible label so the anchor is obvious.
- "← Back to symbol list" link in the sidebar restores the empty
  state — explicit way out instead of relying on accidental
  empty-canvas clicks.
- Light file-grouping: each symbol's border colour is a stable hash
  of its file path; symbols from the same file share a colour.

Layout dropdown clean-up
────────────────────────
Removed Force-directed (cose). Force-directed is the wrong algorithm
for code dependency graphs — hub clusters collapse, orphans fly off,
labels overlap, no tuning fixes the underlying shape mismatch. Tried
fcose as a substitute; same fundamental problem on dense code graphs.
Default is now Dagre TB; remaining options are Concentric, Breadth-
first, Grid, Circle — all deterministic.

UX polish
─────────
- autoungrabify: true — disables single-click drag, fixes "node jumps
  on click" (trackpad clicks always have some motion which Cytoscape's
  default interprets as a node grab).
- Tap-to-highlight neighbourhood — clicking any node highlights its
  direct neighbours and fades the rest. Less aggressive than the
  transitive blast-radius / call-flow buttons in the sidebar.
- Zoom-bound label visibility — labels hidden below zoom 0.55 so 100+
  node graphs aren't a soup of overlapping text. Selected / highlighted
  nodes always show their label.
- Layout-position persistence — switching Files ↔ Symbols and back
  preserves Files-view positions instead of re-running the layout.

TDZ regression test
───────────────────
New tests/unit/viewer-app.test.ts runs the bundled viewer-app.js in a
sandboxed node:vm context with mocked DOM + Cytoscape, triggering
every function-typed style closure on a fake element. Catches TDZ
("Cannot access X before initialization") and other reference errors
at unit-test time. Would have caught both the prior `cy` and
`LABEL_ZOOM_THRESHOLD` ordering bugs before they reached the browser.

Stats-line consistency
──────────────────────
graph-visualize-html.ts — top bar now displays embedded counts
(sym.symbols.length / sym.symbolEdges.length) instead of meta counts.
Previously the top bar said "630 symbols" while the sidebar's "All
symbols" said "722" because meta excludes synthetic <module>
placeholders. Capped mode keeps meta counts with a "(capped)"
qualifier since the embedded set is empty there.

Quality gates
─────────────
- Biome lint: clean
- TypeScript (tsc): clean
- Unit tests: 687/687 (incl. the new viewer-app.js evaluation test)
2026-04-27 17:46:51 +01:00
Giancarlo Erra 9d113975dc docs(readme): surface impact analysis & portability; fix Claude Code install
- Add "Portable, AI- and host-agnostic" paragraph in the intro that
  bundles two claims: the codebase context lives with the code (works
  with any assistant / IDE / CLI, any combination), and pre-computed
  reasoning (blast radius, call-flow, dependency traversal) lets
  smaller / cheaper models hold their own on architectural questions.
- Add three rows to the "Built-in Code Search vs SocratiCode"
  comparison table: symbol-level impact / blast radius, call-flow
  tracing, interactive visual graph explorer.
- Plugins section: chain Claude Code's two install commands with &&
  so the row is a single copy-paste — the install step was missing.
2026-04-27 17:44:43 +01:00
Giancarlo Erra 081606f9e2 fix(visualize): use function replacers so vendored assets containing $& survive intact
String.prototype.replace with a string replacement argument interprets
`$&`, `$'`, `` $` ``, and `$$` as special tokens (match, post-match,
pre-match, literal-$). The vendored bundles contain the classic
regex-escape idiom `"\\$&"` (`cytoscape.min.js` and `dagre.min.js` each
have one), which the asset-injection pipeline was corrupting into e.g.
`"\\{{CYTOSCAPE}}"` in the generated HTML. The visible symptom was a
broken Cytoscape search-box escape path whenever a user typed a regex
metacharacter into the live-search input.

Fix: convert the six asset/data replacements in buildInteractiveGraphHtml
to function-replacer form (`() => assets.cytoscape`), which uses the
returned string literally with no `$`-token interpretation. Same class
of bug would have affected embedded JSON had a file path or symbol name
contained `$&` — now neutralised for the whole chain.

Regression test added: asserts the `\\$&` idiom survives verbatim in the
rendered HTML and that neither `\\{{CYTOSCAPE}}` nor `\\{{DAGRE}}`
appears. A single `expect(html).toContain('"\\\\$&"')` catches the bug.

Quality gates (all green):
- Biome lint: clean
- TypeScript (tsc --noEmit): clean
- Unit tests: 686/686 (was 685; +1 regression test)
- CodeRabbit: No findings
- Snyk: 0 issues

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 16:48:58 +01:00
Giancarlo Erra 50d8853ea6 feat(visualize): add interactive HTML graph explorer; British-English doc sweep
Interactive Viewer (primary)
────────────────────────────
codebase_graph_visualize now accepts mode="mermaid" (default, existing
behaviour — text Mermaid diagram) or mode="interactive". Interactive
mode generates a self-contained HTML page and opens it in the user's
default browser via the `open` npm package (cross-platform: macOS,
Linux, Windows). Cytoscape.js 3.30.2 + Dagre 0.8.5 + cytoscape-dagre
2.5.0 are vendored under src/assets/ — no CDN, works offline.

Features:
- File view — every source file as a node, imports as edges, language
  colour-coded, circular deps highlighted in red.
- Symbol view toggle — functions/classes/methods as nodes with call
  edges (confidence-styled). Embedded when the symbol graph fits under
  20k symbols / 60k call edges; above that threshold the file view
  remains usable and a banner directs users to codebase_impact /
  codebase_symbols for symbol-level queries.
- Sidebar on node click — imports, dependents, per-file symbol list
  (first 30 + link to codebase_symbols), action buttons for blast
  radius and call flow.
- Right-click any node → blast radius overlay (reverse-transitive
  closure). Call-flow button on the sidebar for forward traversal.
- Live search across files and symbols, six Cytoscape layouts
  (Dagre / force / concentric / breadth-first / grid / circle),
  PNG export (filename sanitised for cross-platform safety).
- `open: false` parameter skips auto-launch and just returns the file
  path — useful in headless environments.

Viewer is XSS-safe by construction: all DOM built with createElement
+ textContent (no innerHTML anywhere); embedded JSON escapes every
"<" as \u003c so a stray </script> in a file path or symbol name
cannot break out of the script-type="application/json" container.

New files:
- src/assets/{cytoscape.min.js,dagre.min.js,cytoscape-dagre.js,
  viewer-template.html,viewer-styles.css,viewer-app.js}
- scripts/copy-assets.mjs — postbuild copier (tsc does not handle
  non-TS files); wired into npm run build and prepublishOnly
- src/services/graph-visualize-html.ts — HTML builder with scale-cap
  logic (MAX_SYMBOLS / MAX_EDGES / MAX_SYMS_PER_FILE) and parallel
  per-file Qdrant payload loading
- src/services/graph-visualize-browser.ts — temp-file write +
  cross-platform open wrapper
- tests/unit/graph-visualize-html.test.ts — 5 tests (self-contained,
  escape-safety, symbolMode omitted/capped, cycle marking)
- tests/unit/graph-visualize-browser.test.ts — 4 tests (deterministic
  path, overwrite, success + failure paths)

New runtime dependency: open@^10.2.0 (Sindre Sorhus, zero transitive
deps, cross-platform).

British-English doc sweep (secondary)
─────────────────────────────────────
Switched all project docs to British English spelling:
  behavior → behaviour                organized → organised
  color-coded → colour-coded          initialization → initialisation
  visualization → visualisation       customization → customisation
  recognized → recognised             optimized → optimised
  acknowledgment → acknowledgement    finalize → finalise
  analyzing → analysing               apologizing → apologising
  sexualized → sexualised

Affected files: README, DEVELOPER, AGENTS, CLAUDE, GEMINI, SECURITY,
CONTRIBUTING, CODE_OF_CONDUCT, agents/codebase-explorer.md,
skills/codebase-exploration/{SKILL.md,references/tool-reference.md},
skills/codebase-management/references/tool-reference.md.

Also surfaced Impact Analysis in the top-level README paragraph.

Docs
────
- README: "Interactive graph explorer" subsection under Impact Analysis,
  tool-table row updated.
- DEVELOPER.md: architecture section under codebase_graph_visualize
  covering asset layout, data flow, cap logic, XSS-safety invariants.
- AGENTS.md / CLAUDE.md / GEMINI.md: new "User asks for a visual /
  interactive / shareable graph" row in the tool-routing table.
- skills/codebase-exploration/: SKILL.md bullet + tool-reference.md
  full mode description.
- CHANGELOG.md: "Interactive Graph Explorer" section under Unreleased.

Quality gates (all green)
─────────────────────────
- Biome lint: clean
- TypeScript (tsc): clean
- Unit tests: 685/685
- Integration tests: 154/154 (real Qdrant + Ollama)
- CodeRabbit: No findings (1 fix applied — filename sanitisation)
- Snyk code test: 0 issues

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 16:04:41 +01:00
Giancarlo Erra 4e41b4604e feat(impact): wire Phase F into watcher; fix prototype-key crash; add real scale test
Closes the four reviewer-flagged gaps from the previous round:

1. **Phase F wired into the watcher / `codebase_update`.**
   `rebuildGraph(path, { skipSymbolGraph: true })` now exposes a
   file-import-only build mode. `services/indexer.ts` calls it +
   `updateChangedFilesSymbolGraph(...)` when meta exists AND ≤ 50 files
   changed (`INCREMENTAL_SYMBOL_THRESHOLD`); falls back to full rebuild
   above that. Measured speedup on a 1000-file synthetic repo: full
   rebuild 6.55 s → Phase F single-file update 197 ms (~33×).

2. **Real end-to-end scale test.**
   New `tests/integration/symbol-graph-scale.test.ts` generates 1000
   synthetic Python files × 20 symbols/file (20k symbols) against a real
   Qdrant, asserts (a) full rebuild within budget, (b) cold listSymbols /
   getImpactRadius queries within budget, (c) Phase F update ≥ 4× faster
   than full rebuild. `SCALE_LARGE=1` pushes to 10k files / 200k symbols.

3. **Smoke benchmark numbers captured.**
   New `scripts/benchmark-graph.ts` runs `rebuildGraph` against any
   target dir and emits JSON + a Markdown row. Numbers for SocratiCode
   itself (82 files / 571 symbols / 9914 call edges / 0.90 s / 167 MB
   RSS) and the synthetic 1000-file repo are now in DEVELOPER.md
   § "Real-world benchmark numbers".

4. **Logger test flake fixed.**
   `services/logger.ts` exposes `setLogLevel` / `getLogLevel`;
   `tests/unit/logger.test.ts` pins the level in beforeEach and restores
   in afterEach. Verified deterministic with `SOCRATICODE_LOG_LEVEL=debug`
   set in the shell environment.

### Bug discovered + fixed by the new benchmark

Running `scripts/benchmark-graph.ts` against SocratiCode itself crashed
the symbol graph build with `TypeError: existing.push is not a function`.
Root cause: shard maps used `shard[name]` bracket access on a plain
`{}`, which returned `Object.prototype.constructor` (a function) for
common method names like `constructor`, `toString`, `hasOwnProperty`.
Fixed by guarding all reads with `Object.hasOwn` in
`services/code-graph.ts` and `services/symbol-graph-incremental.ts`.
Added a regression test in
`tests/integration/symbol-graph-incremental.test.ts`.

### QA

- Biome lint: clean (auto-fixed 1 file).
- VS Code Problems panel: clean.
- Unit tests: 676/676 pass (29 files); reproducible.
- Integration tests touched: 45/45 pass (incremental, scale,
  indexer, code-graph).
- CodeRabbit review: no findings.
- Snyk Code: 0 issues.

### Doc updates

- DEVELOPER.md: removed "watcher still triggers full rebuild" wording,
  added "Real-world benchmark numbers" subsection with measured table.
- CHANGELOG.md: removed "Known Limitations" block; added new
  Bug Fixes entries (prototype keys, logger flake) and a Performance
  entry for the wired Phase F path with measured numbers.
2026-04-21 15:59:55 +01:00
Giancarlo Erra 2d686a2688 feat(impact): close gaps from review — Phase F API, scale + integration tests, language coverage
Addresses six gaps in the prior Impact Analysis work:

1. **Scale benchmarks** (was missing): tests/unit/symbol-graph-scale.test.ts
   exercises sharding/hashing at 10k–100k symbol volumes with loose
   regression thresholds (>10× slowdown to fail).

2. **Per-language symbol-extraction tests** (was ~15% of plan):
   tests/unit/graph-symbols.test.ts now covers Rust, Java/Kotlin/Scala (JVM),
   C#, C/C++, Ruby, PHP, Swift, Bash, and the regex fallback path.

3. **Phase F (per-file incremental updates)** — implemented as
   src/services/symbol-graph-incremental.ts with
   updateChangedFilesSymbolGraph(). Re-extracts changed files, diffs against
   persisted payloads via contentHash, patches only affected name (≤27) and
   reverse-call (≤256) shards, and updates meta counts incrementally.
   Integration tests in tests/integration/symbol-graph-incremental.test.ts.
   Watcher wiring is documented as a follow-up in CHANGELOG (still does
   full rebuild on save).

4. **Integration tests for the four new MCP tools**: codebase_impact,
   codebase_flow, codebase_symbol, codebase_symbols added to
   tests/integration/tools.test.ts.

5. **Symbol-graph store unit tests**: tests/unit/symbol-graph-store.test.ts
   covers nameShardKey, allNameShardKeys, reverseShardKey, reverseShardHex,
   contentHashOf — 14 tests.

6. **Bug fix**: Java/Kotlin/Swift/Scala silently failed because ast-grep
   throws 'Invalid Kind' when a queried node-kind doesn't exist for that
   grammar (e.g. object_declaration is Kotlin-only). The outer try/catch in
   extractSymbolsAndCalls swallowed the error and returned only <module>.
   Fixed via safeFindAll wrapper applied to all 36 call sites in
   src/services/graph-symbols.ts.

Also fixes a CodeRabbit-flagged comment/code mismatch in
src/services/graph-entrypoints.ts (now actually checks for it/describe
test names as the comment claimed).

Tests: 676 unit pass. Lint clean. CodeRabbit clean. Snyk clean.
2026-04-21 15:27:16 +01:00
Giancarlo Erra 59f630e1ca test+docs(impact): add symbol pipeline tests and Phase G docs
- 4 new unit test files (22 tests) for graph-symbols, graph-symbol-resolution,
  graph-entrypoints, symbol-graph-cache. All 799 tests pass.
- README/AGENTS/CLAUDE/GEMINI: document codebase_impact, codebase_flow,
  codebase_symbol, codebase_symbols and updated workflow guidance.
- DEVELOPER.md: new architectural section for symbol-level call graph,
  including sharded storage layout and resolution confidence levels.
- CHANGELOG: Unreleased section for the impact-analysis feature.
- Apply CodeRabbit fixes: dedupe in-flight cache loads, set ImpactResult.truncated
  when frontier extends beyond depth limit, drop redundant String() around Lang enum.
- Switch reverse-shard hash from SHA1 to SHA256 (Snyk hardening; non-cryptographic use).
2026-04-21 13:54:01 +01:00
Giancarlo Erra c356c42f4f feat(impact): add symbol-level call graph and Impact Analysis tools
Implements Phases A-E of the Impact Analysis plan:

Phase A — Foundations:
- New types: SymbolNode, SymbolEdge, SymbolGraphMeta, SymbolGraphFilePayload, EntryPoint
- Constants: MAX_IMPACT_DEPTH, MAX_FLOW_DEPTH, SYMBOL_NAME_SHARDS=27, SYMBOL_REVERSE_SHARDS=256
- Sharded Qdrant store (symbol-graph-store.ts): meta/file/index collections,
  27 name shards, 256 reverse-call shards, all dummy-vector pattern
- LRU cache (symbol-graph-cache.ts): per-project lazy shard loading, 500-file LRU

Phase B — Symbol & call extraction (graph-symbols.ts):
- Per-language extractors: TS/JS/TSX, Python, Go, Rust, JVM (Java/Kotlin/Scala),
  C#, C/C++, Ruby, PHP, Swift, Bash, regex fallback for Dart/Lua/Svelte/Vue
- Synthetic <module> symbol per file as fallback caller
- Scope tracking via ScopeFrame[] for accurate caller attribution

Phase C — Resolution (graph-symbol-resolution.ts):
- Three-tier strategy: local match → walk caller deps → one transitive hop
- Confidence levels: unresolved | unique | multiple-candidates
- computeUnresolvedPct for meta stats

Phase D — Analysis primitives:
- detectEntryPoints: orphans + conventional names + framework patterns + tests
- getImpactRadius: BFS via reverseFileIndex, polymorphic file/symbol target
- getCallFlow: DFS via lazy outgoing edges, cycle-safe, depth-limited
- getSymbolContext: 360° view (definition + callers + callees)
- listSymbols: file-mode or query-mode

Phase E — MCP tools:
- codebase_impact: blast radius for file/symbol
- codebase_flow: entry-point discovery + forward call tree
- codebase_symbol: symbol context (callers + callees)
- codebase_symbols: file/query symbol listing

Integration:
- buildCodeGraph now extracts symbols inline alongside imports
- doRebuildGraph persists both file-import graph and symbol graph
- removeGraph cleans up symbol collections + drops cache
- getGraphStatus surfaces symbol stats (files/symbols/edges/unresolved%)

All 621 existing tests still pass. TypeScript compiles cleanly. Biome lint clean.
2026-04-21 13:26:10 +01:00
Giancarlo Erra 2ad7b3db5d docs: add workflow examples to Context Artifacts section 2026-04-17 12:37:54 +01:00
Giancarlo Erra deaf82981b chore: release v1.6.1 v1.6.1 2026-04-17 11:19:30 +01:00
Giancarlo Erra a21cf01516 Merge pull request #29 from giancarloerra:docs/agent-instructions-zed-graph-triggers
docs: add Zed support, per-IDE instruction paths, and strengthen graph triggers
2026-04-17 11:18:05 +01:00
Giancarlo Erra 270d402f48 docs: add Zed support, per-IDE instruction paths, and strengthen graph triggers 2026-04-17 11:17:08 +01:00
Giancarlo Erra 486bf7622b chore: release v1.6.0 v1.6.0 2026-04-16 19:03:58 +01:00
Giancarlo Erra a8b069a900 docs: add Discord community, Cloud section, and tool portability to README 2026-04-16 19:02:09 +01:00