diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8d9589ec1..6e2ad5097 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -61,7 +61,7 @@ jobs: integrations-lockfiles: ${{ steps.filter.outputs.integrations-lockfiles }} integrations-openai-agents: ${{ steps.filter.outputs.integrations-openai-agents }} integrations-openhands: ${{ steps.filter.outputs.integrations-openhands }} - integrations-windsurf: ${{ steps.filter.outputs.integrations-windsurf }} + integrations-devin-desktop: ${{ steps.filter.outputs.integrations-devin-desktop }} integrations-pipecat: ${{ steps.filter.outputs.integrations-pipecat }} integrations-agentcore: ${{ steps.filter.outputs.integrations-agentcore }} integrations-smolagents: ${{ steps.filter.outputs.integrations-smolagents }} @@ -196,8 +196,8 @@ jobs: - 'hindsight-integrations/openai-agents/**' integrations-openhands: - 'hindsight-integrations/openhands/**' - integrations-windsurf: - - 'hindsight-integrations/windsurf/**' + integrations-devin-desktop: + - 'hindsight-integrations/devin-desktop/**' integrations-pipecat: - 'hindsight-integrations/pipecat/**' integrations-agentcore: @@ -3866,11 +3866,11 @@ jobs: # (requires_real_llm) needs a live Hindsight server and runs separately. run: uv run pytest tests -v -m "not requires_real_llm" - test-windsurf-integration: + test-devin-desktop-integration: needs: [detect-changes] if: >- (github.event_name == 'workflow_dispatch' || - needs.detect-changes.outputs.integrations-windsurf == 'true' || + needs.detect-changes.outputs.integrations-devin-desktop == 'true' || needs.detect-changes.outputs.ci == 'true') runs-on: ubuntu-latest timeout-minutes: 30 @@ -3891,16 +3891,16 @@ jobs: with: python-version-file: ".python-version" - - name: Build windsurf integration - working-directory: ./hindsight-integrations/windsurf + - name: Build devin-desktop integration + working-directory: ./hindsight-integrations/devin-desktop run: uv build - name: Install dependencies - working-directory: ./hindsight-integrations/windsurf + working-directory: ./hindsight-integrations/devin-desktop run: uv sync --frozen - name: Run tests - working-directory: ./hindsight-integrations/windsurf + working-directory: ./hindsight-integrations/devin-desktop # PR CI runs only the deterministic bucket; the real-LLM E2E bucket # (requires_real_llm) needs a live Hindsight server and runs separately. run: uv run pytest tests -v -m "not requires_real_llm" @@ -4957,7 +4957,7 @@ jobs: - test-llamaindex-integration - test-openai-agents-integration - test-openhands-integration - - test-windsurf-integration + - test-devin-desktop-integration - test-agentcore-integration - test-haystack-integration - test-pip-slim diff --git a/hindsight-dev/hindsight_dev/generate_changelog.py b/hindsight-dev/hindsight_dev/generate_changelog.py index 269259baf..7a2c99004 100644 --- a/hindsight-dev/hindsight_dev/generate_changelog.py +++ b/hindsight-dev/hindsight_dev/generate_changelog.py @@ -82,7 +82,7 @@ class IntegrationMeta: "continue": IntegrationMeta("hindsight-continue", "Continue"), "zed": IntegrationMeta("hindsight-zed", "Zed"), "openhands": IntegrationMeta("hindsight-openhands", "OpenHands"), - "windsurf": IntegrationMeta("hindsight-windsurf", "Windsurf"), + "devin-desktop": IntegrationMeta("hindsight-devin-desktop", "Devin Desktop"), } VALID_INTEGRATIONS = list(INTEGRATIONS.keys()) diff --git a/hindsight-docs/docs-integrations/devin-desktop.md b/hindsight-docs/docs-integrations/devin-desktop.md new file mode 100644 index 000000000..483f59474 --- /dev/null +++ b/hindsight-docs/docs-integrations/devin-desktop.md @@ -0,0 +1,46 @@ +--- +sidebar_position: 7 +title: "Devin Desktop Persistent Memory with Hindsight | Integration" +description: "Add long-term memory to Devin Desktop (formerly Windsurf) with Hindsight via MCP. One command wires the Hindsight MCP server into mcp_config.json plus an always-on recall/retain rule, so memory works automatically in Devin." +--- + +# Devin Desktop + +Long-term memory for [Devin Desktop](https://devin.ai) — the editor formerly known as Windsurf (Codeium) — powered by [Hindsight](https://vectorize.io/hindsight). One command connects Devin to the Hindsight MCP server and adds a rule telling the agent to use it — so it recalls relevant memory at the start of a task and retains durable facts as it goes. Recall happens at query time against your actual message, and from your seat it's automatic. + +:::note +Cognition rebranded Windsurf to Devin Desktop in June 2026. The MCP config still lives under `~/.codeium/windsurf/` — that's Devin Desktop's on-disk data directory and is unchanged by the rebrand. The workspace rule now lives under `.devin/rules/` (with `.windsurf/rules/` kept as a legacy fallback). +::: + +## How It Works + +Devin Desktop supports two things this integration uses: + +- **MCP servers:** Devin Desktop runs MCP servers configured under `mcpServers` in `~/.codeium/windsurf/mcp_config.json` and surfaces their tools to the agent. Remote servers connect via a `serverUrl` field with optional headers, so the Hindsight MCP endpoint connects directly — no bridge needed — giving the agent `recall` / `retain` / `reflect` tools. +- **Workspace rules** in `.devin/rules/`. A rule file with `trigger: always_on` frontmatter is included in every Devin request in the workspace. The integration writes a small rule there telling the agent to recall first and retain what it learns. + +## Setup + +```bash +pip install hindsight-devin-desktop +cd your-project +hindsight-devin-desktop init --api-token YOUR_HINDSIGHT_API_KEY --bank-id my-memory +``` + +`init` adds the `hindsight` MCP server to `~/.codeium/windsurf/mcp_config.json` (Devin Desktop's single global MCP config) and writes the recall/retain rule to `./.devin/rules/hindsight.md`. Reload Devin Desktop (or refresh MCP servers), and the `hindsight` server's tools become available. + +Use a [Hindsight Cloud](https://hindsight.vectorize.io) key, or point at a self-hosted server with `--api-url http://localhost:8888` (no token needed for an open local server). If your `mcp_config.json` isn't plain JSON, `init` prints the entry to paste rather than rewriting the file — or run `hindsight-devin-desktop init --print-only` anytime. + +## Commands + +| Command | Description | +| --- | --- | +| `hindsight-devin-desktop init` | Add the MCP server + recall/retain rule | +| `hindsight-devin-desktop status` | Show whether the server + rule are configured | +| `hindsight-devin-desktop uninstall` | Remove the server + rule | + +## Note + +Recall and retain run through MCP tools the agent calls, guided by the always-on rule. This makes recall query-time precise (no lag), with the tradeoff that it relies on the agent following the "recall first" instruction rather than the editor enforcing it. + +See the [package README](https://github.com/vectorize-io/hindsight/tree/main/hindsight-integrations/devin-desktop) for full configuration options. diff --git a/hindsight-docs/docs-integrations/windsurf.md b/hindsight-docs/docs-integrations/windsurf.md deleted file mode 100644 index 7a276910d..000000000 --- a/hindsight-docs/docs-integrations/windsurf.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -sidebar_position: 7 -title: "Windsurf Persistent Memory with Hindsight | Integration" -description: "Add long-term memory to Windsurf (Codeium) with Hindsight via MCP. One command wires the Hindsight MCP server into mcp_config.json plus an always-on recall/retain rule, so memory works automatically in Cascade." ---- - -# Windsurf - -Long-term memory for [Windsurf](https://windsurf.com) (Codeium), powered by [Hindsight](https://vectorize.io/hindsight). One command connects Cascade to the Hindsight MCP server and adds a rule telling the agent to use it — so it recalls relevant memory at the start of a task and retains durable facts as it goes. Recall happens at query time against your actual message, and from your seat it's automatic. - -## How It Works - -Windsurf supports two things this integration uses: - -- **MCP servers:** Windsurf runs MCP servers configured under `mcpServers` in `~/.codeium/windsurf/mcp_config.json` and surfaces their tools in Cascade. Remote servers connect via a `serverUrl` field with optional headers, so the Hindsight MCP endpoint connects directly — no bridge needed — giving the agent `recall` / `retain` / `reflect` tools. -- **Workspace rules** in `.windsurf/rules/`. A rule file with `trigger: always_on` frontmatter is included in every Cascade request in the workspace. The integration writes a small rule there telling the agent to recall first and retain what it learns. - -## Setup - -```bash -pip install hindsight-windsurf -cd your-project -hindsight-windsurf init --api-token YOUR_HINDSIGHT_API_KEY --bank-id my-memory -``` - -`init` adds the `hindsight` MCP server to `~/.codeium/windsurf/mcp_config.json` (Windsurf's single global MCP config) and writes the recall/retain rule to `./.windsurf/rules/hindsight.md`. Reload Windsurf (or refresh MCP servers in Cascade), and the `hindsight` server's tools become available. - -Use a [Hindsight Cloud](https://hindsight.vectorize.io) key, or point at a self-hosted server with `--api-url http://localhost:8888` (no token needed for an open local server). If your `mcp_config.json` isn't plain JSON, `init` prints the entry to paste rather than rewriting the file — or run `hindsight-windsurf init --print-only` anytime. - -## Commands - -| Command | Description | -| --- | --- | -| `hindsight-windsurf init` | Add the MCP server + recall/retain rule | -| `hindsight-windsurf status` | Show whether the server + rule are configured | -| `hindsight-windsurf uninstall` | Remove the server + rule | - -## Note - -Recall and retain run through MCP tools the agent calls, guided by the always-on rule. This makes recall query-time precise (no lag), with the tradeoff that it relies on the agent following the "recall first" instruction rather than the editor enforcing it. - -See the [package README](https://github.com/vectorize-io/hindsight/tree/main/hindsight-integrations/windsurf) for full configuration options. diff --git a/hindsight-docs/src/data/integrations.json b/hindsight-docs/src/data/integrations.json index 621a282e8..0f071aef8 100644 --- a/hindsight-docs/src/data/integrations.json +++ b/hindsight-docs/src/data/integrations.json @@ -571,14 +571,14 @@ "icon": "/img/icons/openhands.png" }, { - "id": "windsurf", - "name": "Windsurf", - "description": "Long-term memory for Windsurf (Codeium) via its native MCP support. One command wires the Hindsight MCP server into mcp_config.json plus an always-on recall/retain rule, so memory works automatically in Cascade.", + "id": "devin-desktop", + "name": "Devin Desktop", + "description": "Long-term memory for Devin Desktop (the editor formerly known as Windsurf) via its native MCP support. One command wires the Hindsight MCP server into mcp_config.json plus an always-on recall/retain rule, so memory works automatically.", "type": "official", "by": "hindsight", "category": "tool", - "link": "/sdks/integrations/windsurf", - "icon": "/img/icons/windsurf.svg" + "link": "/sdks/integrations/devin-desktop", + "icon": "/img/icons/devin-desktop.svg" }, { "id": "epimetheus", diff --git a/hindsight-docs/static/img/icons/devin-desktop.svg b/hindsight-docs/static/img/icons/devin-desktop.svg new file mode 100644 index 000000000..440b59fb5 --- /dev/null +++ b/hindsight-docs/static/img/icons/devin-desktop.svg @@ -0,0 +1 @@ +Devin diff --git a/hindsight-docs/static/img/icons/windsurf.svg b/hindsight-docs/static/img/icons/windsurf.svg deleted file mode 100644 index 7670303f6..000000000 --- a/hindsight-docs/static/img/icons/windsurf.svg +++ /dev/null @@ -1 +0,0 @@ -Windsurf diff --git a/hindsight-integrations/README.md b/hindsight-integrations/README.md index 3c573ddbb..da7c0e8bd 100644 --- a/hindsight-integrations/README.md +++ b/hindsight-integrations/README.md @@ -14,7 +14,7 @@ Each integration lives in its own subdirectory with its own README, configuratio | [**Cursor CLI**](./cursor-cli) | Python hook scripts for Cursor CLI. Auto-recall on `beforeSubmitPrompt`, auto-retain on `stop`, final flush on `sessionEnd`. | `./scripts/install.sh` | | [**Continue.dev**](./continue) | HTTP context provider for precise `@hindsight` recall in chat, plus optional MCP-server + rules for automatic recall/retain in agent mode. | `pip install hindsight-continue` | | [**GitHub Copilot**](./github-copilot) | MCP server config (`.vscode/mcp.json`) + a recall/retain rule for VS Code Copilot's agent mode. | `pip install hindsight-copilot` | -| [**Windsurf**](./windsurf) | Native MCP server config + always-on recall/retain rule for Windsurf (Codeium) Cascade. | `pip install hindsight-windsurf` | +| [**Devin Desktop**](./devin-desktop) | Native MCP server config + always-on recall/retain rule for Devin Desktop (formerly Windsurf). | `pip install hindsight-devin-desktop` | | [**Roo Code**](./roo-code) | Persistent memory for Roo Code VS Code extension. | See README | | [**Hermes (OpenAI Agents SDK)**](./hermes) | Memory layer for OpenAI Agents SDK. | See README | | [**Grok Build**](./grok-build) | Hooks for Grok Build (xAI). | See README | diff --git a/hindsight-integrations/continue/hindsight_continue/server.py b/hindsight-integrations/continue/hindsight_continue/server.py index 01d65077b..5f0c6b460 100644 --- a/hindsight-integrations/continue/hindsight_continue/server.py +++ b/hindsight-integrations/continue/hindsight_continue/server.py @@ -25,8 +25,19 @@ _MAX_BODY_BYTES = 1_000_000 -def make_handler(client: Hindsight, config: HindsightContinueConfig): - """Build a request-handler class bound to a client and config.""" +def make_handler(config: HindsightContinueConfig, client: Optional[Hindsight] = None): + """Build a request-handler class. + + When ``client`` is None (production), a fresh Hindsight client is resolved + **per request**. This is deliberate: the adapter runs on a + :class:`ThreadingHTTPServer` (one worker thread per request), and the + Hindsight client's underlying aiohttp session is bound to the thread / + event loop that first used it. A single shared client therefore succeeds on + the first request and then raises ``Timeout context manager should be used + inside a task`` on every request after it. Resolving per request keeps each + recall on its own thread's client. Tests may inject a client directly (they + drive the handler single-threaded). + """ class _Handler(BaseHTTPRequestHandler): # Quiet the default per-request stderr logging; route through our logger. @@ -66,8 +77,10 @@ def do_POST(self) -> None: # noqa: N802 - stdlib signature self._write_json(400, {"error": "request body must be a JSON object"}) return + request_client = None try: - items = build_context_items(payload, client=client, config=config) + request_client = client if client is not None else resolve_client() + items = build_context_items(payload, client=request_client, config=config) except HindsightError as e: # Surface the failure so it shows up in Continue's warnings rather # than silently returning no memory. @@ -77,6 +90,15 @@ def do_POST(self) -> None: # noqa: N802 - stdlib signature logger.exception("Unexpected error handling context request") self._write_json(500, {"error": f"internal error: {e}"}) return + finally: + # Close only a client we created here (a per-request client), + # not a test-injected one. Otherwise the fresh aiohttp session + # leaks a connector every request on the long-running server. + if client is None and request_client is not None: + try: + request_client.close() + except Exception: # pragma: no cover - best-effort cleanup + logger.debug("client close failed", exc_info=True) self._write_json(200, serialize(items)) @@ -88,8 +110,10 @@ def build_server( ) -> ThreadingHTTPServer: """Create (but do not start) the adapter HTTP server.""" config = config or get_config() - client = client or resolve_client() - handler = make_handler(client, config) + # Do not pre-resolve a shared client: pass it through so the handler resolves + # a fresh client per request (see make_handler). A test-injected client is + # used as-is. + handler = make_handler(config, client) return ThreadingHTTPServer((config.host, config.port), handler) diff --git a/hindsight-integrations/continue/pyproject.toml b/hindsight-integrations/continue/pyproject.toml index 51397ee77..dcf91bfd0 100644 --- a/hindsight-integrations/continue/pyproject.toml +++ b/hindsight-integrations/continue/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "hindsight-continue" -version = "0.1.0" +version = "0.1.1" description = "Continue.dev integration for Hindsight - persistent long-term memory via an HTTP context provider" readme = "README.md" requires-python = ">=3.10" diff --git a/hindsight-integrations/continue/tests/test_server.py b/hindsight-integrations/continue/tests/test_server.py index c5f4ec3bf..0e934ddb9 100644 --- a/hindsight-integrations/continue/tests/test_server.py +++ b/hindsight-integrations/continue/tests/test_server.py @@ -97,3 +97,30 @@ def test_recall_error_surfaces_as_502(self): assert status == 502 assert "error" in json.loads(raw) + + def test_resolves_a_fresh_client_per_request(self, monkeypatch): + """Regression: with no injected client, resolve one per request. + + ThreadingHTTPServer handles each request on its own thread, and the + Hindsight client's aiohttp session is thread/loop-bound — so a single + shared client fails on the 2nd request with "Timeout context manager + should be used inside a task". The adapter must resolve per request. + """ + import hindsight_continue.server as server_mod + + calls = [] + + def fake_resolve(): + calls.append(1) + return make_client(["recalled fact"]) + + monkeypatch.setattr(server_mod, "resolve_client", fake_resolve) + + # client=None -> production path -> per-request resolution + with running_server(None) as (host, port): + for _ in range(3): + status, raw = _post(host, port, continue_request(query="hi")) + assert status == 200 + assert "recalled fact" in json.loads(raw)[0]["content"] + + assert len(calls) == 3, f"expected a fresh client per request, got {len(calls)} resolves" diff --git a/hindsight-integrations/continue/uv.lock b/hindsight-integrations/continue/uv.lock index 372636d97..d4584d04c 100644 --- a/hindsight-integrations/continue/uv.lock +++ b/hindsight-integrations/continue/uv.lock @@ -361,7 +361,7 @@ wheels = [ [[package]] name = "hindsight-continue" -version = "0.1.0" +version = "0.1.1" source = { editable = "." } dependencies = [ { name = "hindsight-client" }, diff --git a/hindsight-integrations/devin-desktop/README.md b/hindsight-integrations/devin-desktop/README.md new file mode 100644 index 000000000..7195a8972 --- /dev/null +++ b/hindsight-integrations/devin-desktop/README.md @@ -0,0 +1,84 @@ +# hindsight-devin-desktop + +Long-term memory for **Devin Desktop** (the editor formerly known as Windsurf / Codeium), powered by [Hindsight](https://github.com/vectorize-io/hindsight). + +`hindsight-devin-desktop init` wires the Hindsight **MCP server** into Devin Desktop's +`~/.codeium/windsurf/mcp_config.json` and adds an always-on recall/retain rule to +`.devin/rules/hindsight.md`. Devin then has `recall` / `retain` / `reflect` +tools and — guided by the rule — recalls relevant memory at the start of a task +and retains durable facts as it works. + +> **Note:** Cognition rebranded Windsurf to Devin Desktop (June 2026). The MCP +> config path still lives under `~/.codeium/windsurf/` — that's Devin Desktop's +> on-disk data directory and is unchanged by the rebrand. The workspace rule now +> lives under `.devin/rules/` (with `.windsurf/rules/` kept as a legacy fallback). + +## How it works + +Devin Desktop supports two things this integration uses: + +- **MCP servers** in `~/.codeium/windsurf/mcp_config.json` under `mcpServers`, + including **remote servers** via `serverUrl` with headers — so the Hindsight + MCP endpoint connects directly: + + ```json + { + "mcpServers": { + "hindsight": { + "serverUrl": "https://api.hindsight.vectorize.io/mcp/my-project/", + "headers": { "Authorization": "Bearer hsk_..." } + } + } + } + ``` + +- **Workspace rules** in `.devin/rules/`. A rule file with `trigger: always_on` + frontmatter is applied to every Devin request in the workspace — that's where + the recall/retain rule lives. + +## Install + +```bash +pip install hindsight-devin-desktop +cd your-project +hindsight-devin-desktop init --api-token YOUR_HINDSIGHT_API_KEY --bank-id my-project +``` + +`init` merges the `mcpServers` entry into `~/.codeium/windsurf/mcp_config.json` +(Devin Desktop's single global MCP config) and writes the rule into +`./.devin/rules/hindsight.md`. Reload Devin Desktop (or refresh MCP servers) and +the `hindsight` tools are available. + +Use a [Hindsight Cloud](https://hindsight.vectorize.io) key, or a self-hosted +server with `--api-url http://localhost:8888` (no token needed for an open local +server). If `mcp_config.json` isn't plain JSON, `init` prints the snippet to +paste instead of touching the file — or run `hindsight-devin-desktop init --print-only` +anytime. + +## Commands + +| Command | Description | +| --- | --- | +| `hindsight-devin-desktop init` | Add the MCP server + recall/retain rule | +| `hindsight-devin-desktop status` | Show whether the server + rule are configured | +| `hindsight-devin-desktop uninstall` | Remove the server + rule | + +## Configuration + +| Setting | Env var | Default | +| --- | --- | --- | +| API URL | `HINDSIGHT_API_URL` | `https://api.hindsight.vectorize.io` | +| API token | `HINDSIGHT_API_TOKEN` | _(none; required for Cloud)_ | +| Bank id | `HINDSIGHT_DEVIN_DESKTOP_BANK_ID` | `devin-desktop` | + +## Development + +```bash +uv sync +uv run pytest tests -v -m 'not requires_real_llm' # deterministic suite +uv run pytest tests -v -m requires_real_llm # gated MCP-endpoint check +``` + +## License + +MIT diff --git a/hindsight-integrations/devin-desktop/hindsight_devin_desktop/__init__.py b/hindsight-integrations/devin-desktop/hindsight_devin_desktop/__init__.py new file mode 100644 index 000000000..521d1ec88 --- /dev/null +++ b/hindsight-integrations/devin-desktop/hindsight_devin_desktop/__init__.py @@ -0,0 +1,12 @@ +"""Hindsight memory integration for Devin Desktop (formerly Windsurf / Codeium). + +Wires the Hindsight MCP server into Devin Desktop's ``~/.codeium/windsurf/mcp_config.json`` +and writes an always-on recall/retain rule into ``.devin/rules/hindsight.md``, +so Devin has ``recall``/``retain``/``reflect`` tools and uses them automatically. + +CLI:: + + hindsight-devin-desktop init --api-token hsk_... --bank-id my-project +""" + +__version__ = "0.1.0" diff --git a/hindsight-integrations/windsurf/hindsight_windsurf/cli.py b/hindsight-integrations/devin-desktop/hindsight_devin_desktop/cli.py similarity index 82% rename from hindsight-integrations/windsurf/hindsight_windsurf/cli.py rename to hindsight-integrations/devin-desktop/hindsight_devin_desktop/cli.py index b145b6dac..b2b67884e 100644 --- a/hindsight-integrations/windsurf/hindsight_windsurf/cli.py +++ b/hindsight-integrations/devin-desktop/hindsight_devin_desktop/cli.py @@ -1,8 +1,8 @@ -"""CLI for the Hindsight Windsurf integration. +"""CLI for the Hindsight Devin Desktop integration. -``hindsight-windsurf init`` wires the Hindsight MCP server into Windsurf's -``~/.codeium/windsurf/mcp_config.json`` and writes an always-on recall/retain -rule into ``.windsurf/rules/hindsight.md``. Cascade then exposes +``hindsight-devin-desktop init`` wires the Hindsight MCP server into Devin +Desktop's ``~/.codeium/windsurf/mcp_config.json`` and writes an always-on +recall/retain rule into ``.devin/rules/hindsight.md``. Devin then exposes ``recall``/``retain``/``reflect`` and (via the rule) uses them automatically. """ @@ -16,7 +16,7 @@ from typing import Optional from . import __version__ -from .config import USER_CONFIG_FILE, WindsurfConfig, load_config +from .config import USER_CONFIG_FILE, DevinDesktopConfig, load_config from .mcp_config import ( McpResult, apply_to_mcp, @@ -36,7 +36,7 @@ class InstallOutcome: rules_path: Path -def build_install(config: WindsurfConfig, mcp_path: Path, rules_path: Path) -> InstallOutcome: +def build_install(config: DevinDesktopConfig, mcp_path: Path, rules_path: Path) -> InstallOutcome: """Apply the MCP server entry and the recall/retain rule (the testable core).""" server = build_http_server(config.hindsight_api_url, config.hindsight_api_token, config.bank_id) mcp = apply_to_mcp(mcp_path, server) @@ -44,7 +44,7 @@ def build_install(config: WindsurfConfig, mcp_path: Path, rules_path: Path) -> I return InstallOutcome(mcp=mcp, rules_path=rules_path) -def _resolve_config(args: argparse.Namespace) -> WindsurfConfig: +def _resolve_config(args: argparse.Namespace) -> DevinDesktopConfig: cfg = load_config(config_file=_user_config_path(args)) if args.api_url: cfg.hindsight_api_url = args.api_url @@ -67,7 +67,7 @@ def _rules_path(args: argparse.Namespace) -> Path: return Path(args.rules_path) if args.rules_path else default_rules_path() -def _scaffold_user_config(cfg: WindsurfConfig, path: Path) -> None: +def _scaffold_user_config(cfg: DevinDesktopConfig, path: Path) -> None: if path.is_file(): return data = {"hindsightApiUrl": cfg.hindsight_api_url, "bankId": cfg.bank_id} @@ -86,11 +86,11 @@ def cmd_init(args: argparse.Namespace) -> None: if args.print_only: print("Add this to your ~/.codeium/windsurf/mcp_config.json:\n") print(render_snippet(server)) - print("\nAnd save this rule as .windsurf/rules/hindsight.md:\n") + print("\nAnd save this rule as .devin/rules/hindsight.md:\n") print(RULE_TEXT) return - print("Setting up Hindsight for Windsurf ...") + print("Setting up Hindsight for Devin Desktop ...") _scaffold_user_config(cfg, _user_config_path(args)) outcome = build_install(cfg, mcp_path, rules_path) @@ -102,7 +102,7 @@ def cmd_init(args: argparse.Namespace) -> None: verb = {"created": "Created", "merged": "Updated", "unchanged": "Already configured in"}[outcome.mcp.action] print(f" {verb} {outcome.mcp.path} (MCP server: hindsight -> bank '{cfg.bank_id}')") print(f" Wrote always-on recall/retain rule to {outcome.rules_path}") - print("\nDone. Reload Windsurf (or refresh MCP servers in Cascade) and the") + print("\nDone. Reload Devin Desktop (or refresh MCP servers) and the") print("hindsight MCP tools (recall/retain/reflect) are available + used automatically.") @@ -131,21 +131,21 @@ def _add_overrides(parser: argparse.ArgumentParser) -> None: parser.add_argument( "--mcp-path", default=None, help="mcp_config.json path (default: ~/.codeium/windsurf/mcp_config.json)" ) - parser.add_argument("--rules-path", default=None, help="rule file path (default: ./.windsurf/rules/hindsight.md)") + parser.add_argument("--rules-path", default=None, help="rule file path (default: ./.devin/rules/hindsight.md)") parser.add_argument("--user-config-path", default=None, help=argparse.SUPPRESS) def main(argv: Optional[list[str]] = None) -> int: parser = argparse.ArgumentParser( - prog="hindsight-windsurf", description="Hindsight memory for Windsurf (Codeium, via MCP)" + prog="hindsight-devin-desktop", description="Hindsight memory for Devin Desktop (formerly Windsurf), via MCP" ) - parser.add_argument("--version", action="version", version=f"hindsight-windsurf {__version__}") + parser.add_argument("--version", action="version", version=f"hindsight-devin-desktop {__version__}") sub = parser.add_subparsers(dest="command") - init_p = sub.add_parser("init", help="Configure Windsurf's MCP server + recall/retain rule") + init_p = sub.add_parser("init", help="Configure Devin Desktop's MCP server + recall/retain rule") init_p.add_argument("--api-url", default=None, help="Hindsight API URL (default: cloud)") init_p.add_argument("--api-token", default=None, help="Hindsight API token (for Cloud)") - init_p.add_argument("--bank-id", default=None, help="Memory bank for the MCP server (default: windsurf)") + init_p.add_argument("--bank-id", default=None, help="Memory bank for the MCP server (default: devin-desktop)") init_p.add_argument("--print-only", action="store_true", help="Print the config to add manually; write nothing") _add_overrides(init_p) init_p.set_defaults(func=cmd_init) diff --git a/hindsight-integrations/windsurf/hindsight_windsurf/config.py b/hindsight-integrations/devin-desktop/hindsight_devin_desktop/config.py similarity index 68% rename from hindsight-integrations/windsurf/hindsight_windsurf/config.py rename to hindsight-integrations/devin-desktop/hindsight_devin_desktop/config.py index 7644a41bd..5f6caab95 100644 --- a/hindsight-integrations/windsurf/hindsight_windsurf/config.py +++ b/hindsight-integrations/devin-desktop/hindsight_devin_desktop/config.py @@ -1,13 +1,15 @@ -"""Configuration for the Hindsight Windsurf integration. +"""Configuration for the Hindsight Devin Desktop integration. -Settings layer (later wins): built-in defaults -> ``~/.hindsight/windsurf.json`` --> environment variables. Resolved into a typed :class:`WindsurfConfig`. +Devin Desktop is the editor formerly known as Windsurf (Codeium). + +Settings layer (later wins): built-in defaults -> ``~/.hindsight/devin-desktop.json`` +-> environment variables. Resolved into a typed :class:`DevinDesktopConfig`. The integration is configuration-only: it wires the Hindsight MCP server into -Windsurf's ``~/.codeium/windsurf/mcp_config.json`` and writes an always-on -recall/retain rule into ``.windsurf/rules/hindsight.md`` (which Cascade applies -to every request in the workspace). Memory operations run through the MCP server -at runtime. +Devin Desktop's ``~/.codeium/windsurf/mcp_config.json`` and writes an always-on +recall/retain rule into ``.devin/rules/hindsight.md`` (which Devin applies to +every request in the workspace). Memory operations run through the MCP server at +runtime. """ from __future__ import annotations @@ -19,14 +21,14 @@ from typing import Optional DEFAULT_HINDSIGHT_API_URL = "https://api.hindsight.vectorize.io" -DEFAULT_BANK_ID = "windsurf" +DEFAULT_BANK_ID = "devin-desktop" -USER_CONFIG_FILE = Path.home() / ".hindsight" / "windsurf.json" +USER_CONFIG_FILE = Path.home() / ".hindsight" / "devin-desktop.json" @dataclass -class WindsurfConfig: - """Resolved configuration for the Windsurf MCP setup.""" +class DevinDesktopConfig: + """Resolved configuration for the Devin Desktop MCP setup.""" hindsight_api_url: str = DEFAULT_HINDSIGHT_API_URL hindsight_api_token: Optional[str] = None @@ -44,13 +46,13 @@ class WindsurfConfig: _ENV_KEYS = { "HINDSIGHT_API_URL": "hindsight_api_url", "HINDSIGHT_API_TOKEN": "hindsight_api_token", - "HINDSIGHT_WINDSURF_BANK_ID": "bank_id", + "HINDSIGHT_DEVIN_DESKTOP_BANK_ID": "bank_id", } -def load_config(config_file: Optional[Path] = None, env: Optional[dict] = None) -> WindsurfConfig: +def load_config(config_file: Optional[Path] = None, env: Optional[dict] = None) -> DevinDesktopConfig: """Load and resolve configuration from file then environment.""" - cfg = WindsurfConfig() + cfg = DevinDesktopConfig() env = os.environ if env is None else env path = config_file if config_file is not None else USER_CONFIG_FILE diff --git a/hindsight-integrations/windsurf/hindsight_windsurf/mcp_config.py b/hindsight-integrations/devin-desktop/hindsight_devin_desktop/mcp_config.py similarity index 82% rename from hindsight-integrations/windsurf/hindsight_windsurf/mcp_config.py rename to hindsight-integrations/devin-desktop/hindsight_devin_desktop/mcp_config.py index 62c51a650..28f718370 100644 --- a/hindsight-integrations/windsurf/hindsight_windsurf/mcp_config.py +++ b/hindsight-integrations/devin-desktop/hindsight_devin_desktop/mcp_config.py @@ -1,9 +1,11 @@ -"""Wire Hindsight into Windsurf's MCP config (``~/.codeium/windsurf/mcp_config.json``). +"""Wire Hindsight into Devin Desktop's MCP config (``~/.codeium/windsurf/mcp_config.json``). -Windsurf's Cascade reads MCP servers from ``~/.codeium/windsurf/mcp_config.json`` -under the ``mcpServers`` key. For a remote server it uses a ``serverUrl`` field -(plus optional ``headers``), so the Hindsight MCP endpoint connects with no -bridge:: +Devin Desktop (formerly Windsurf) reads MCP servers from +``~/.codeium/windsurf/mcp_config.json`` under the ``mcpServers`` key — the path +still carries the legacy ``windsurf`` segment, which is the app's on-disk data +directory and is unchanged by the rebrand. For a remote server it uses a +``serverUrl`` field (plus optional ``headers``), so the Hindsight MCP endpoint +connects with no bridge:: { "mcpServers": { @@ -14,9 +16,9 @@ } } -Unlike VS Code, Windsurf has no project-local MCP file — ``mcp_config.json`` is -a single global file. We only edit it in place when it parses as strict JSON; -otherwise we return the exact snippet to paste, never risking the user's file. +Devin Desktop has no project-local MCP file — ``mcp_config.json`` is a single +global file. We only edit it in place when it parses as strict JSON; otherwise +we return the exact snippet to paste, never risking the user's file. """ from __future__ import annotations @@ -30,7 +32,7 @@ def default_mcp_path() -> Path: - """The global Windsurf MCP config (``~/.codeium/windsurf/mcp_config.json``).""" + """The global Devin Desktop MCP config (``~/.codeium/windsurf/mcp_config.json``).""" return Path.home() / ".codeium" / "windsurf" / "mcp_config.json" diff --git a/hindsight-integrations/windsurf/hindsight_windsurf/py.typed b/hindsight-integrations/devin-desktop/hindsight_devin_desktop/py.typed similarity index 100% rename from hindsight-integrations/windsurf/hindsight_windsurf/py.typed rename to hindsight-integrations/devin-desktop/hindsight_devin_desktop/py.typed diff --git a/hindsight-integrations/windsurf/hindsight_windsurf/rules.py b/hindsight-integrations/devin-desktop/hindsight_devin_desktop/rules.py similarity index 73% rename from hindsight-integrations/windsurf/hindsight_windsurf/rules.py rename to hindsight-integrations/devin-desktop/hindsight_devin_desktop/rules.py index 7c66184ed..28e6e6187 100644 --- a/hindsight-integrations/windsurf/hindsight_windsurf/rules.py +++ b/hindsight-integrations/devin-desktop/hindsight_devin_desktop/rules.py @@ -1,9 +1,10 @@ -"""Write Hindsight's recall/retain rule into ``.windsurf/rules/hindsight.md``. +"""Write Hindsight's recall/retain rule into ``.devin/rules/hindsight.md``. -Windsurf applies workspace rule files under ``.windsurf/rules/``. A file with -``trigger: always_on`` frontmatter is included in every Cascade request in the -workspace, so the rule tells Cascade to use the Hindsight MCP tools — recall -relevant memory at the start of a task, and retain durable facts. +Devin Desktop applies workspace rule files under ``.devin/rules/`` (with +``.windsurf/rules/`` kept as a legacy fallback). A file with ``trigger: always_on`` +frontmatter is included in every Devin request in the workspace, so the rule +tells the agent to use the Hindsight MCP tools — recall relevant memory at the +start of a task, and retain durable facts. The rule lives in its own dedicated file, so we own the whole file: a sentinel comment marks it as ours for idempotent update/removal without touching any @@ -14,7 +15,7 @@ from pathlib import Path -SENTINEL = "" +SENTINEL = "" FRONTMATTER = "---\ntrigger: always_on\n---" @@ -32,8 +33,8 @@ def default_rules_path() -> Path: - """The workspace ``.windsurf/rules/hindsight.md`` (always-on in Cascade).""" - return Path.cwd() / ".windsurf" / "rules" / "hindsight.md" + """The workspace ``.devin/rules/hindsight.md`` (always-on in Devin Desktop).""" + return Path.cwd() / ".devin" / "rules" / "hindsight.md" def render_rule(rule_text: str = RULE_TEXT) -> str: diff --git a/hindsight-integrations/windsurf/pyproject.toml b/hindsight-integrations/devin-desktop/pyproject.toml similarity index 77% rename from hindsight-integrations/windsurf/pyproject.toml rename to hindsight-integrations/devin-desktop/pyproject.toml index 7acb0693c..5df2801a2 100644 --- a/hindsight-integrations/windsurf/pyproject.toml +++ b/hindsight-integrations/devin-desktop/pyproject.toml @@ -1,12 +1,12 @@ [project] -name = "hindsight-windsurf" +name = "hindsight-devin-desktop" version = "0.1.0" -description = "Windsurf (Codeium) integration for Hindsight - persistent long-term memory via MCP" +description = "Devin Desktop (formerly Windsurf) integration for Hindsight - persistent long-term memory via MCP" readme = "README.md" requires-python = ">=3.10" license = { text = "MIT" } authors = [{ name = "Vectorize", email = "support@vectorize.io" }] -keywords = ["ai", "memory", "windsurf", "codeium", "cascade", "agents", "hindsight", "mcp"] +keywords = ["ai", "memory", "devin", "devin-desktop", "windsurf", "agents", "hindsight", "mcp"] classifiers = [ "Development Status :: 4 - Beta", "Intended Audience :: Developers", @@ -20,11 +20,11 @@ classifiers = [ dependencies = [] [project.scripts] -hindsight-windsurf = "hindsight_windsurf.cli:main" +hindsight-devin-desktop = "hindsight_devin_desktop.cli:main" [project.urls] Homepage = "https://github.com/vectorize-io/hindsight" -Documentation = "https://github.com/vectorize-io/hindsight/tree/main/hindsight-integrations/windsurf" +Documentation = "https://github.com/vectorize-io/hindsight/tree/main/hindsight-integrations/devin-desktop" Repository = "https://github.com/vectorize-io/hindsight" [build-system] @@ -32,7 +32,7 @@ requires = ["hatchling"] build-backend = "hatchling.build" [tool.hatch.build.targets.wheel] -packages = ["hindsight_windsurf"] +packages = ["hindsight_devin_desktop"] [tool.ruff] line-length = 120 diff --git a/hindsight-integrations/windsurf/tests/__init__.py b/hindsight-integrations/devin-desktop/tests/__init__.py similarity index 100% rename from hindsight-integrations/windsurf/tests/__init__.py rename to hindsight-integrations/devin-desktop/tests/__init__.py diff --git a/hindsight-integrations/windsurf/tests/test_cli.py b/hindsight-integrations/devin-desktop/tests/test_cli.py similarity index 86% rename from hindsight-integrations/windsurf/tests/test_cli.py rename to hindsight-integrations/devin-desktop/tests/test_cli.py index 615b12d2d..3253c7fe5 100644 --- a/hindsight-integrations/windsurf/tests/test_cli.py +++ b/hindsight-integrations/devin-desktop/tests/test_cli.py @@ -2,18 +2,18 @@ import json -from hindsight_windsurf.cli import build_install, main -from hindsight_windsurf.config import WindsurfConfig -from hindsight_windsurf.mcp_config import SERVER_NAME -from hindsight_windsurf.mcp_config import is_installed as server_installed -from hindsight_windsurf.rules import is_installed as rule_installed +from hindsight_devin_desktop.cli import build_install, main +from hindsight_devin_desktop.config import DevinDesktopConfig +from hindsight_devin_desktop.mcp_config import SERVER_NAME +from hindsight_devin_desktop.mcp_config import is_installed as server_installed +from hindsight_devin_desktop.rules import is_installed as rule_installed class TestBuildInstall: def test_writes_mcp_and_rule(self, tmp_path): mcp = tmp_path / "mcp_config.json" rules = tmp_path / "rules" / "hindsight.md" - cfg = WindsurfConfig( + cfg = DevinDesktopConfig( hindsight_api_url="https://api.hindsight.vectorize.io", hindsight_api_token="k", bank_id="proj" ) outcome = build_install(cfg, mcp, rules) diff --git a/hindsight-integrations/windsurf/tests/test_config.py b/hindsight-integrations/devin-desktop/tests/test_config.py similarity index 71% rename from hindsight-integrations/windsurf/tests/test_config.py rename to hindsight-integrations/devin-desktop/tests/test_config.py index 2233da5ee..8d57a32b0 100644 --- a/hindsight-integrations/windsurf/tests/test_config.py +++ b/hindsight-integrations/devin-desktop/tests/test_config.py @@ -2,7 +2,7 @@ import json -from hindsight_windsurf.config import DEFAULT_BANK_ID, DEFAULT_HINDSIGHT_API_URL, load_config +from hindsight_devin_desktop.config import DEFAULT_BANK_ID, DEFAULT_HINDSIGHT_API_URL, load_config def test_defaults(tmp_path): @@ -13,7 +13,7 @@ def test_defaults(tmp_path): def test_file_values(tmp_path): - p = tmp_path / "windsurf.json" + p = tmp_path / "devin-desktop.json" p.write_text(json.dumps({"hindsightApiToken": "t", "bankId": "proj"})) cfg = load_config(config_file=p, env={}) assert cfg.hindsight_api_token == "t" @@ -21,14 +21,14 @@ def test_file_values(tmp_path): def test_env_overrides_file(tmp_path): - p = tmp_path / "windsurf.json" + p = tmp_path / "devin-desktop.json" p.write_text(json.dumps({"bankId": "from-file"})) - cfg = load_config(config_file=p, env={"HINDSIGHT_WINDSURF_BANK_ID": "from-env", "HINDSIGHT_API_TOKEN": "k"}) + cfg = load_config(config_file=p, env={"HINDSIGHT_DEVIN_DESKTOP_BANK_ID": "from-env", "HINDSIGHT_API_TOKEN": "k"}) assert cfg.bank_id == "from-env" assert cfg.hindsight_api_token == "k" def test_malformed_file_falls_back(tmp_path): - p = tmp_path / "windsurf.json" + p = tmp_path / "devin-desktop.json" p.write_text("{ broken") assert load_config(config_file=p, env={}).bank_id == DEFAULT_BANK_ID diff --git a/hindsight-integrations/windsurf/tests/test_e2e.py b/hindsight-integrations/devin-desktop/tests/test_e2e.py similarity index 89% rename from hindsight-integrations/windsurf/tests/test_e2e.py rename to hindsight-integrations/devin-desktop/tests/test_e2e.py index 3c18311da..25eb1cc95 100644 --- a/hindsight-integrations/windsurf/tests/test_e2e.py +++ b/hindsight-integrations/devin-desktop/tests/test_e2e.py @@ -8,7 +8,7 @@ import pytest -from hindsight_windsurf.mcp_config import mcp_endpoint_url +from hindsight_devin_desktop.mcp_config import mcp_endpoint_url HINDSIGHT_API_URL = os.getenv("HINDSIGHT_API_URL", "http://localhost:8888") HINDSIGHT_API_TOKEN = os.getenv("HINDSIGHT_API_TOKEN") @@ -40,7 +40,7 @@ def _rpc(url, payload, session=None): def test_mcp_endpoint_lists_memory_tools(): - url = mcp_endpoint_url(HINDSIGHT_API_URL, "windsurf-e2e") + url = mcp_endpoint_url(HINDSIGHT_API_URL, "devin-desktop-e2e") init = { "jsonrpc": "2.0", "id": 1, @@ -48,7 +48,7 @@ def test_mcp_endpoint_lists_memory_tools(): "params": { "protocolVersion": "2025-03-26", "capabilities": {}, - "clientInfo": {"name": "windsurf-e2e", "version": "0"}, + "clientInfo": {"name": "devin-desktop-e2e", "version": "0"}, }, } resp = _rpc(url, init) diff --git a/hindsight-integrations/windsurf/tests/test_mcp_config.py b/hindsight-integrations/devin-desktop/tests/test_mcp_config.py similarity index 98% rename from hindsight-integrations/windsurf/tests/test_mcp_config.py rename to hindsight-integrations/devin-desktop/tests/test_mcp_config.py index 2c88d83b2..cc2b73752 100644 --- a/hindsight-integrations/windsurf/tests/test_mcp_config.py +++ b/hindsight-integrations/devin-desktop/tests/test_mcp_config.py @@ -2,7 +2,7 @@ import json -from hindsight_windsurf.mcp_config import ( +from hindsight_devin_desktop.mcp_config import ( SERVER_NAME, apply_to_mcp, build_http_server, diff --git a/hindsight-integrations/windsurf/tests/test_rules.py b/hindsight-integrations/devin-desktop/tests/test_rules.py similarity index 76% rename from hindsight-integrations/windsurf/tests/test_rules.py rename to hindsight-integrations/devin-desktop/tests/test_rules.py index a0810ce3e..bd73a206d 100644 --- a/hindsight-integrations/windsurf/tests/test_rules.py +++ b/hindsight-integrations/devin-desktop/tests/test_rules.py @@ -1,6 +1,21 @@ -"""Tests for the .windsurf/rules/hindsight.md rule writer.""" +"""Tests for the .devin/rules/hindsight.md rule writer.""" -from hindsight_windsurf.rules import RULE_TEXT, SENTINEL, clear_rule, is_installed, render_rule, write_rule +from hindsight_devin_desktop.rules import ( + RULE_TEXT, + SENTINEL, + clear_rule, + default_rules_path, + is_installed, + render_rule, + write_rule, +) + + +def test_default_path_is_devin_rules(): + p = default_rules_path() + assert p.parent.name == "rules" + assert p.parent.parent.name == ".devin" + assert p.name == "hindsight.md" def test_write_creates_dedicated_file(tmp_path): diff --git a/hindsight-integrations/windsurf/uv.lock b/hindsight-integrations/devin-desktop/uv.lock similarity index 78% rename from hindsight-integrations/windsurf/uv.lock rename to hindsight-integrations/devin-desktop/uv.lock index 83b71a1eb..70a2ce5e3 100644 --- a/hindsight-integrations/windsurf/uv.lock +++ b/hindsight-integrations/devin-desktop/uv.lock @@ -24,7 +24,7 @@ wheels = [ ] [[package]] -name = "hindsight-windsurf" +name = "hindsight-devin-desktop" version = "0.1.0" source = { editable = "." } @@ -98,27 +98,27 @@ wheels = [ [[package]] name = "ruff" -version = "0.15.18" +version = "0.15.19" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/74/98/1295ad5a5aa9bc85bdcdfa5d82fe7b49c61af5657df4f227637ff9de0da6/ruff-0.15.18.tar.gz", hash = "sha256:2698a964c70e8bf402dcb99c8810472d270d141e7aa8c4e13599fd52033a2f33", size = 4761437, upload-time = "2026-06-18T18:25:39.224Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d5/e6/15800dfde183a1a106594016c912b4c12d050a301989d1aca6cb63759fe8/ruff-0.15.19.tar.gz", hash = "sha256:edc27f7172a93b32b102687009d6a588508815072141543ae603a8b9b0823063", size = 4772071, upload-time = "2026-06-24T01:10:46.942Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b9/d0/686e984941269621e2be72612d5c1e461f8f7b38415a2a7d7a81c8ae6715/ruff-0.15.18-py3-none-linux_armv6l.whl", hash = "sha256:8b6850172348c8381b8b3084c5915a4393c2373b9b54cd5b5e1ea15812bc10df", size = 10887308, upload-time = "2026-06-18T18:25:03.062Z" }, - { url = "https://files.pythonhosted.org/packages/ed/21/bc4123e3f5515ee99f8ce1eb93a14a0628fe4d1678663cd08f933ac16931/ruff-0.15.18-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:3fccc153a85417dcd976883160cacce486997b0a0058dd18f54b8aaaac7d1ce2", size = 11281305, upload-time = "2026-06-18T18:25:30.026Z" }, - { url = "https://files.pythonhosted.org/packages/51/93/4769464c25cf7ab2acb3c7dda9cad3d867eb41c59565b3e2a9d17249c90c/ruff-0.15.18-py3-none-macosx_11_0_arm64.whl", hash = "sha256:08d4c86a68f2c3ec2c9d56380a71fb4a4f65373055cbb8caabd645e9102f38d4", size = 10641215, upload-time = "2026-06-18T18:25:15.802Z" }, - { url = "https://files.pythonhosted.org/packages/6c/42/56926d17120db2c208d76bf60a1a019644dd9e91dc27f0f95c9caddb1366/ruff-0.15.18-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37e5108745c2c0705da916d7d4de533ddf547051ef45f62888c31bae73f66318", size = 10957224, upload-time = "2026-06-18T18:25:36.955Z" }, - { url = "https://files.pythonhosted.org/packages/22/4f/d43fab8d8189afde803103022d000a8ef9f230616d436d52a8b2b8d63b50/ruff-0.15.18-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:56949a6ce8b3abde54c0bcb22cebfe57e8771cadc84b407ae8b8eaf67ebdcd43", size = 10699024, upload-time = "2026-06-18T18:25:05.707Z" }, - { url = "https://files.pythonhosted.org/packages/63/42/1e3e4c68bd408b9768cf3e439acbe2c78245225faef253f7028a0cdb63e0/ruff-0.15.18-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01a754cd6a1b630d3f97e33eb452cf7a98040482318e870f8bc52a5a30e62657", size = 11491458, upload-time = "2026-06-18T18:25:20.275Z" }, - { url = "https://files.pythonhosted.org/packages/20/77/47a3484bea8521e14a203d98c389c5c97846675e4f02734672da4a69b52a/ruff-0.15.18-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6ba7a07e03a44dbf10bb086ee06705b173625014ec99f73a7e6836a5e5590a0c", size = 12383752, upload-time = "2026-06-18T18:25:22.535Z" }, - { url = "https://files.pythonhosted.org/packages/0a/ca/054159590787023d83b658a1a1819c4c8910114e7015069340b71c0961cb/ruff-0.15.18-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a2c40a41a4cadbcf5897b548ab29dfe248b20c540961c0247d98a3973c70403", size = 11577923, upload-time = "2026-06-18T18:25:10.702Z" }, - { url = "https://files.pythonhosted.org/packages/6d/ff/d353d6b7bbd73cc0ec37f4463d7540e45e894338abdd9964eee0de332708/ruff-0.15.18-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f0480ce690cbb6c4db6e5d08f19fce98e10ba131a8b60c1bcdac42771e3ae2d", size = 11583925, upload-time = "2026-06-18T18:25:32.391Z" }, - { url = "https://files.pythonhosted.org/packages/c1/4a/891f89b9c296ed3e5f3ece1a5629badc989d9a8fdaa30431aaf4774bc1c2/ruff-0.15.18-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:2330215f1f393fa8733f55edce04fcf94c36a2c460fcde31f78cc84e4951e9b1", size = 11582834, upload-time = "2026-06-18T18:25:27.309Z" }, - { url = "https://files.pythonhosted.org/packages/32/a3/ed9e370154bf85de360b93c03026157f02d4943b2d01ff4945f4429f8e8a/ruff-0.15.18-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a6aa6a3d979e48ae617578183674bf264fbe7d0114a796a26bd678d67963c7ff", size = 10927328, upload-time = "2026-06-18T18:25:34.676Z" }, - { url = "https://files.pythonhosted.org/packages/f5/d1/5cf5909329fedb5d39d555ee818ba5cf4638e1a301b89785d34f2905bfcb/ruff-0.15.18-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:a81beadbbff2c9c245561ae3f77b16709d87f35eec650d0501679239d3449b22", size = 10693187, upload-time = "2026-06-18T18:25:08.245Z" }, - { url = "https://files.pythonhosted.org/packages/fd/44/ff6c635cf2c4f4e7b618b6640da057376baa36014695487d88aed4794268/ruff-0.15.18-py3-none-musllinux_1_2_i686.whl", hash = "sha256:2186d9e940ae332ab293623a75b5f4fe49565f449954d50a72a046683aa6b809", size = 11208721, upload-time = "2026-06-18T18:25:41.327Z" }, - { url = "https://files.pythonhosted.org/packages/88/d9/5baa2a30861adfb7022cf33c1e35b2fc18085b08c16f83eff4c7b99a5f48/ruff-0.15.18-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5c2abf140438032bc77b2284a6c9944ecd8a19e5f1c7b52b1b8e4a0a80d19a7a", size = 11678599, upload-time = "2026-06-18T18:25:13.607Z" }, - { url = "https://files.pythonhosted.org/packages/c3/1a/0725a7cfdc32ff769efb96ee782bec882e16448c5d9e3be947ec4c04ce27/ruff-0.15.18-py3-none-win32.whl", hash = "sha256:02299e6e9fa5b297a3f6d5d10d7bcd655c925b028bb8b9d4588214549c6b9ec4", size = 10901903, upload-time = "2026-06-18T18:25:24.755Z" }, - { url = "https://files.pythonhosted.org/packages/f3/51/805d9f6fb7970505c3504794a5ec350f605361b807fef4dcf214ebd35e72/ruff-0.15.18-py3-none-win_amd64.whl", hash = "sha256:dac80dc8d26b2257dbefabed62f5d255c3937b4ccb122da1fc634794fa3578b3", size = 12041189, upload-time = "2026-06-18T18:25:17.915Z" }, - { url = "https://files.pythonhosted.org/packages/29/4c/67bb45e41609eb4726f1bfeb59e083cf91d14c696d4bd14c234a980be93d/ruff-0.15.18-py3-none-win_arm64.whl", hash = "sha256:b2c9257fcbd4a3e5b977a1904e6facca016bafe2edc17df24db67cfaee03b4e4", size = 11329958, upload-time = "2026-06-18T18:25:43.686Z" }, + { url = "https://files.pythonhosted.org/packages/88/4c/9ded7626c39a0440c575bf69e2bf500d443388272c842662c59852ee7fcd/ruff-0.15.19-py3-none-linux_armv6l.whl", hash = "sha256:922d1eb283161564759bd49f507e91dc6112c15da8bd5b84ed714e086243cf86", size = 10950859, upload-time = "2026-06-24T01:10:38.491Z" }, + { url = "https://files.pythonhosted.org/packages/fb/ef/c211505ece1d00ef493d58e54e3b6383c946a21e9874774eb531f2512cf3/ruff-0.15.19-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:4d190d8f62a0b94aba8f721116538a9ee29b1e74d26650846ba9b99f0ae21c40", size = 11294529, upload-time = "2026-06-24T01:10:36.481Z" }, + { url = "https://files.pythonhosted.org/packages/fe/93/78d462e7d39968e58094dc57be7d09ffb14ce37da5b68ed70338a35a1f21/ruff-0.15.19-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5a2c86ba6870dd415a9d9eb8be94d7924ebec6a26ffc7958ec7ca29d4bff967d", size = 10641416, upload-time = "2026-06-24T01:10:48.923Z" }, + { url = "https://files.pythonhosted.org/packages/76/c4/5cb66cfd1f865d5cca908b86c93ac785e7f572193d3c7426079ca6643e24/ruff-0.15.19-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82b432bc087264aea70fd25ac198918b70bd9e2aa0db4297b0bb91bbfbbc63ce", size = 11015582, upload-time = "2026-06-24T01:10:30.089Z" }, + { url = "https://files.pythonhosted.org/packages/51/9f/8ecfaec10cf5eecd28fbc00ff4fb867db90a1be54bf3d39ebf93f893cd52/ruff-0.15.19-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8530a09d03b3a8c994f8b559a7dcdabc690bcd3f78ef276c38c83166798ebf56", size = 10744059, upload-time = "2026-06-24T01:10:32.48Z" }, + { url = "https://files.pythonhosted.org/packages/35/6b/983249d04562bc2d590edd75f32455cdb473affb3ba4bc8d883e939c697d/ruff-0.15.19-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:87bf21fb3875fe69f0eacc825411657e2e85589cce633c35c0adf1113649c62b", size = 11568461, upload-time = "2026-06-24T01:10:17.435Z" }, + { url = "https://files.pythonhosted.org/packages/eb/39/bc7794f127b18f492a3b4ee82bba5a900c985ff13b72b46f46e3c171ba34/ruff-0.15.19-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f9b229cb3ef56ecc2c1c8ebeca64b7a7740ccaef40a9eb097e78dde5a8560b83", size = 12429690, upload-time = "2026-06-24T01:10:40.638Z" }, + { url = "https://files.pythonhosted.org/packages/0a/3b/0de6859e698ed11c8a49e765196c8d333599b6a546c0715df39b6ba1aa2e/ruff-0.15.19-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c6c754515be7b76afe6e7e62df7776709571bcfc1631183828afcf3bafa869e3", size = 11693067, upload-time = "2026-06-24T01:10:25.681Z" }, + { url = "https://files.pythonhosted.org/packages/89/3d/0b1f30f84bee9ae6ae8d349c2ba8b6f4b040966744efdd3acc804ae7c024/ruff-0.15.19-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a498f82e0f4d8904c4e0aea5139cdfac1f39d19a3c51d491292f63a36e83b2e", size = 11616911, upload-time = "2026-06-24T01:10:44.809Z" }, + { url = "https://files.pythonhosted.org/packages/4d/eb/c90bd3dfc12eed9032c2c1bfe05105b93a1b2c8bce555db6308315b853ce/ruff-0.15.19-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:d48caa34488fb521fd0ef4aea2b0e8fe758298df044138f0d67b687a6a0d07ed", size = 11649343, upload-time = "2026-06-24T01:10:23.472Z" }, + { url = "https://files.pythonhosted.org/packages/82/91/01caa13602a2f12fae5edbe8caf78b3c1e6db1293132aee6959eecce095c/ruff-0.15.19-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:4171b6613effa9363cd46dd4f75bd1827b6d1b946b5e278ed0c600d305379445", size = 10977610, upload-time = "2026-06-24T01:10:50.892Z" }, + { url = "https://files.pythonhosted.org/packages/3c/51/acb817922feab9ecbb3201377d4dbe7a25f1395e46545820061973f03468/ruff-0.15.19-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:27c15b2a241dd4d995557949a094fe78b8ad99122a38ccae1595849bcc947b3f", size = 10744900, upload-time = "2026-06-24T01:10:42.726Z" }, + { url = "https://files.pythonhosted.org/packages/84/bc/5c8ca46b8a7a3f2b16cfbec88721d772b1c93912904e8f8c2e49470fea63/ruff-0.15.19-py3-none-musllinux_1_2_i686.whl", hash = "sha256:ed03b7862d68f0a8771d50ee129980cbf1b113f96e250b73954bc292f689e0bb", size = 11293560, upload-time = "2026-06-24T01:10:21.262Z" }, + { url = "https://files.pythonhosted.org/packages/81/e0/4a888cbe4d5523b3f77a2b1fa043f46cfeba1b32eac35dcfadee0578fa8a/ruff-0.15.19-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:08143f0685ae278b30727ea72e90c61e5bd9c31b91aac4f5bb989538f73d24b8", size = 11696533, upload-time = "2026-06-24T01:10:53.046Z" }, + { url = "https://files.pythonhosted.org/packages/98/43/c34b2fcd79262a85161764a97aaca89c3e4f574340ab61430cefa2bdd2c1/ruff-0.15.19-py3-none-win32.whl", hash = "sha256:8f47f0f92952af2557212bb10cf3e695cd4cf28b2c6e42cdb18ec6c9ebfa19da", size = 10986299, upload-time = "2026-06-24T01:10:55.185Z" }, + { url = "https://files.pythonhosted.org/packages/22/e8/15fd23e02b2442b56b2026b455977bc3057aa34b26e6323d1e99e8531a9f/ruff-0.15.19-py3-none-win_amd64.whl", hash = "sha256:efeca47ee3f9d4a7162655a3b8e6ee4a878646044233978d4d2c1ff8cdd914f0", size = 12123473, upload-time = "2026-06-24T01:10:27.74Z" }, + { url = "https://files.pythonhosted.org/packages/30/66/9a73695e31eaee04f35d8475998bf8ab354465f9c638936d76111603dcc5/ruff-0.15.19-py3-none-win_arm64.whl", hash = "sha256:6c6b607466e47349332eb1d9be52fb1467423fc07c217341af41cd0f3f0573be", size = 11376779, upload-time = "2026-06-24T01:10:34.465Z" }, ] [[package]] diff --git a/hindsight-integrations/windsurf/README.md b/hindsight-integrations/windsurf/README.md deleted file mode 100644 index 461a6e1b8..000000000 --- a/hindsight-integrations/windsurf/README.md +++ /dev/null @@ -1,79 +0,0 @@ -# hindsight-windsurf - -Long-term memory for **Windsurf** (Codeium), powered by [Hindsight](https://github.com/vectorize-io/hindsight). - -`hindsight-windsurf init` wires the Hindsight **MCP server** into Windsurf's -`~/.codeium/windsurf/mcp_config.json` and adds an always-on recall/retain rule to -`.windsurf/rules/hindsight.md`. Cascade then has `recall` / `retain` / `reflect` -tools and — guided by the rule — recalls relevant memory at the start of a task -and retains durable facts as it works. - -## How it works - -Windsurf supports two things this integration uses: - -- **MCP servers** in `~/.codeium/windsurf/mcp_config.json` under `mcpServers`, - including **remote servers** via `serverUrl` with headers — so the Hindsight - MCP endpoint connects directly: - - ```json - { - "mcpServers": { - "hindsight": { - "serverUrl": "https://api.hindsight.vectorize.io/mcp/my-project/", - "headers": { "Authorization": "Bearer hsk_..." } - } - } - } - ``` - -- **Workspace rules** in `.windsurf/rules/`. A rule file with `trigger: always_on` - frontmatter is applied to every Cascade request in the workspace — that's where - the recall/retain rule lives. - -## Install - -```bash -pip install hindsight-windsurf -cd your-project -hindsight-windsurf init --api-token YOUR_HINDSIGHT_API_KEY --bank-id my-project -``` - -`init` merges the `mcpServers` entry into `~/.codeium/windsurf/mcp_config.json` -(Windsurf's single global MCP config) and writes the rule into -`./.windsurf/rules/hindsight.md`. Reload Windsurf (or refresh MCP servers in -Cascade) and the `hindsight` tools are available. - -Use a [Hindsight Cloud](https://hindsight.vectorize.io) key, or a self-hosted -server with `--api-url http://localhost:8888` (no token needed for an open local -server). If `mcp_config.json` isn't plain JSON, `init` prints the snippet to -paste instead of touching the file — or run `hindsight-windsurf init --print-only` -anytime. - -## Commands - -| Command | Description | -| --- | --- | -| `hindsight-windsurf init` | Add the MCP server + recall/retain rule | -| `hindsight-windsurf status` | Show whether the server + rule are configured | -| `hindsight-windsurf uninstall` | Remove the server + rule | - -## Configuration - -| Setting | Env var | Default | -| --- | --- | --- | -| API URL | `HINDSIGHT_API_URL` | `https://api.hindsight.vectorize.io` | -| API token | `HINDSIGHT_API_TOKEN` | _(none; required for Cloud)_ | -| Bank id | `HINDSIGHT_WINDSURF_BANK_ID` | `windsurf` | - -## Development - -```bash -uv sync -uv run pytest tests -v -m 'not requires_real_llm' # deterministic suite -uv run pytest tests -v -m requires_real_llm # gated MCP-endpoint check -``` - -## License - -MIT diff --git a/hindsight-integrations/windsurf/hindsight_windsurf/__init__.py b/hindsight-integrations/windsurf/hindsight_windsurf/__init__.py deleted file mode 100644 index 240f9af91..000000000 --- a/hindsight-integrations/windsurf/hindsight_windsurf/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -"""Hindsight memory integration for Windsurf (Codeium). - -Wires the Hindsight MCP server into Windsurf's ``~/.codeium/windsurf/mcp_config.json`` -and writes an always-on recall/retain rule into ``.windsurf/rules/hindsight.md``, -so Cascade has ``recall``/``retain``/``reflect`` tools and uses them automatically. - -CLI:: - - hindsight-windsurf init --api-token hsk_... --bank-id my-project -""" - -__version__ = "0.1.0" diff --git a/scripts/release-integration.sh b/scripts/release-integration.sh index 67ae59c75..370387d17 100755 --- a/scripts/release-integration.sh +++ b/scripts/release-integration.sh @@ -13,7 +13,7 @@ print_info() { echo -e "${GREEN}[INFO]${NC} $1"; } print_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } print_error() { echo -e "${RED}[ERROR]${NC} $1"; } -VALID_INTEGRATIONS=("ag2" "agent-framework" "agentcore" "agno" "aider" "ai-sdk" "autogen" "chat" "claude-agent-sdk" "claude-code" "cline" "cloudflare-oauth-proxy" "codex" "composio" "continue" "crewai" "cursor" "cursor-cli" "dify" "eve" "flowise" "gemini-spark" "github-copilot" "google-adk" "haystack" "langgraph" "litellm" "llamaindex" "n8n" "nemoclaw" "obsidian" "omo" "openai-agents" "openclaw" "opencode" "openhands" "paperclip" "pipecat" "pydantic-ai" "roo-code" "smolagents" "strands" "superagent" "vapi" "windsurf" "zed") +VALID_INTEGRATIONS=("ag2" "agent-framework" "agentcore" "agno" "aider" "ai-sdk" "autogen" "chat" "claude-agent-sdk" "claude-code" "cline" "cloudflare-oauth-proxy" "codex" "composio" "continue" "crewai" "cursor" "cursor-cli" "devin-desktop" "dify" "eve" "flowise" "gemini-spark" "github-copilot" "google-adk" "haystack" "langgraph" "litellm" "llamaindex" "n8n" "nemoclaw" "obsidian" "omo" "openai-agents" "openclaw" "opencode" "openhands" "paperclip" "pipecat" "pydantic-ai" "roo-code" "smolagents" "strands" "superagent" "vapi" "zed") usage() { print_error "Usage: $0 "