Compare commits

...
Author SHA1 Message Date
Ben 82c6db52ad feat(agent-plugin): add portable Hindsight plugin for the Agent Plugins standard
Add a vendor-neutral Hindsight plugin conforming to Vercel's Agent Plugins
1.0.0 standard (plugin.json + mcp.json + skills/SKILL.md), so one artifact
gives long-term memory to any compatible client (Codex, Cursor, GitHub
Copilot, Kiro, VS Code) instead of a per-IDE integration. The plugin is a
thin transport wrapper over Hindsight's existing MCP server (retain / recall
/ reflect); a bundled skill teaches the agent when to use it.

Wiring:
- CI: test-agent-plugin-integration job runs the manifest validator, gated on
  hindsight-integrations/agent-plugin/** changes.
- Docs: integrations.json gallery entry + docs-integrations/agent-plugin.md.
- Release: agent-plugin added to release-integration.sh and the changelog
  generator; both learn to read a root-level plugin.json and link the
  changelog to the source tree (git-distributed bundle, no registry package).
2026-08-11 11:21:54 -04:00
10 changed files with 425 additions and 3 deletions
+27
View File
@@ -43,6 +43,7 @@ jobs:
integrations-continue: ${{ steps.filter.outputs.integrations-continue }}
integrations-cursor-cli: ${{ steps.filter.outputs.integrations-cursor-cli }}
integrations-zcode: ${{ steps.filter.outputs.integrations-zcode }}
integrations-agent-plugin: ${{ steps.filter.outputs.integrations-agent-plugin }}
integrations-copilot-cli: ${{ steps.filter.outputs.integrations-copilot-cli }}
integrations-crewai: ${{ steps.filter.outputs.integrations-crewai }}
integrations-litellm: ${{ steps.filter.outputs.integrations-litellm }}
@@ -194,6 +195,8 @@ jobs:
- 'hindsight-integrations/zed/**'
integrations-zcode:
- 'hindsight-integrations/zcode/**'
integrations-agent-plugin:
- 'hindsight-integrations/agent-plugin/**'
integrations-n8n:
- 'hindsight-integrations/n8n/**'
integrations-zapier:
@@ -796,6 +799,29 @@ jobs:
working-directory: ./hindsight-integrations/zcode
run: uv run pytest tests -v
test-agent-plugin-integration:
needs: [detect-changes]
if: >-
(github.event_name == 'workflow_dispatch' ||
needs.detect-changes.outputs.integrations-agent-plugin == 'true' ||
needs.detect-changes.outputs.ci == 'true')
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v6
with:
ref: ${{ github.event.pull_request.head.sha || '' }}
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version-file: ".python-version"
- name: Validate Agent Plugin manifests
working-directory: ./hindsight-integrations/agent-plugin
run: python3 validate.py
build-ai-sdk-integration:
needs: [detect-changes]
if: >-
@@ -5217,6 +5243,7 @@ jobs:
- test-codex-integration
- test-cursor-cli-integration
- test-zcode-integration
- test-agent-plugin-integration
- build-ai-sdk-integration
- test-ai-sdk-integration-deno
- test-opencode-integration
@@ -50,6 +50,9 @@ INTEGRATIONS: dict[str, IntegrationMeta] = {
"nemoclaw": IntegrationMeta("@vectorize-io/hindsight-nemoclaw", "NemoClaw"),
"strands": IntegrationMeta("hindsight-strands", "Strands"),
"claude-code": IntegrationMeta("hindsight-memory", "Claude Code"),
# Git-distributed plugin bundle (Agent Plugins standard), not a registry
# package — its changelog links to the source tree (see _package_url).
"agent-plugin": IntegrationMeta("hindsight-agent-plugin", "Agent Plugins"),
"zcode": IntegrationMeta("hindsight-zcode", "ZCode"),
"claude-agent-sdk": IntegrationMeta("hindsight-claude-agent-sdk", "Claude Agent SDK"),
"llamaindex": IntegrationMeta("hindsight-llamaindex", "LlamaIndex"),
@@ -657,8 +660,10 @@ def _get_package_name(integration: str) -> str:
def _package_url(integration: str, package_name: str) -> str:
if integration == "claude-code":
return "https://github.com/vectorize-io/hindsight/tree/main/hindsight-integrations/claude-code"
# Git-distributed plugin bundles have no npm/pypi package — link to the
# source tree instead of a registry page.
if integration in ("claude-code", "agent-plugin"):
return f"https://github.com/vectorize-io/hindsight/tree/main/hindsight-integrations/{integration}"
if package_name.startswith("@"):
return f"https://www.npmjs.com/package/{package_name}"
return f"https://pypi.org/project/{package_name}/"
@@ -0,0 +1,92 @@
---
sidebar_position: 2
title: "Agent Plugins Persistent Memory with Hindsight | Integration Guide"
description: "Give any Agent Plugins client — Codex, Cursor, GitHub Copilot, Kiro, VS Code — long-term memory with Hindsight. One portable, standards-based plugin bundles the Hindsight MCP server and a memory skill for recall, retain, and reflect."
---
# Agent Plugins
Portable long-term memory for any [Agent Plugins](https://agent-plugins.org) client, powered by [Hindsight](https://vectorize.io/hindsight).
[Agent Plugins](https://agent-plugins.org) is the vendor-neutral open standard (developed with Amazon, Cursor, Microsoft, OpenAI, and Vercel) for packaging **Agent Skills + MCP servers** into a single distributable plugin. Instead of a separate integration per tool, Hindsight ships **one** plugin that every compatible client can load — at launch: **ChatGPT / Codex, Cursor, GitHub Copilot, Kiro, and VS Code**.
## Quick Start
:::tip Recommended: Hindsight Cloud
[Sign up free](https://ui.hindsight.vectorize.io/signup) for a Hindsight Cloud API key — no self-hosting, no local daemon to manage.
:::
1. Get your `hsk_...` API key from [ui.hindsight.vectorize.io/connect](https://ui.hindsight.vectorize.io/connect).
2. Set the environment variables the plugin reads:
```bash
export HINDSIGHT_API_KEY="hsk_your_token"
export HINDSIGHT_BANK_ID="my-project" # optional; defaults to "default"
```
3. Install the plugin in your client (through its plugin/MCP UI, or by pointing it at the plugin directory — installation is client-specific per the standard).
Once installed, ask the agent something that depends on past context, or tell it a durable preference — it calls `recall` and `retain` automatically, guided by the bundled skill.
## What's in the plugin
The plugin is a thin, transport-only wrapper — all memory logic stays server-side in Hindsight. It follows the Agent Plugins `1.0.0` layout:
```
agent-plugin/
├── plugin.json # manifest ($schema + name + metadata)
├── mcp.json # Hindsight MCP server (Streamable HTTP)
└── skills/
└── hindsight-memory/
└── SKILL.md # teaches the agent when to recall / retain / reflect
```
- **`mcp.json`** connects the client to Hindsight's built-in [MCP server](/developer/mcp-server) over Streamable HTTP.
- **`skills/hindsight-memory/SKILL.md`** is loaded into the agent's context so it knows *when* to reach for memory, not just that the tools exist.
## Memory tools
Via the MCP server, the agent gets Hindsight's full memory surface. The three it reaches for most:
| Tool | When | What it does |
|------|------|--------------|
| `recall` | Before answering, when past context could help | Semantic + keyword + graph + temporal retrieval over the bank |
| `retain` | After learning a durable, reusable fact | Stores the fact for future sessions |
| `reflect` | When a lookup is too shallow and you need synthesized reasoning | Disposition-aware reasoning over everything remembered |
Additional tools (knowledge pages, mental models, documents, tags) are exposed too — see the [MCP Server reference](/developer/mcp-server).
## Configuration
The plugin reads two environment variables, interpolated into `mcp.json`:
| Setting | Env Var | Default | Description |
|---------|---------|---------|-------------|
| API key | `HINDSIGHT_API_KEY` | — | Your `hsk_...` key. Sent as `Authorization: Bearer`. Required for Hindsight Cloud. |
| Memory bank | `HINDSIGHT_BANK_ID` | `default` | Bank to read from and write to (sent as `X-Bank-Id`). Use one bank per user, project, or team for isolation. |
:::note Env-var syntax varies by client
Most clients substitute `${VAR}`; some (VS Code, Cursor) use `${env:VAR}`. If your client doesn't interpolate, paste the literal key and bank id into `mcp.json`.
:::
**Self-hosting:** replace the host in `mcp.json` (`https://api.hindsight.vectorize.io`) with your deployment's URL. A local server with the MCP endpoint open needs no API key.
## Explicit tools vs. automatic capture
Agent Plugins `1.0.0` standardizes **Skills + MCP**, not session lifecycle hooks. This plugin therefore delivers **explicit, tool-driven** memory that works identically across every supported client.
For the fully automatic experience — recall injected before every prompt and transcripts retained on session end — use the native, hook-based integration built for your specific tool, such as [Claude Code](/sdks/integrations/claude-code) or [Codex](/sdks/integrations/codex). Both share the same Hindsight banks, so memory captured by the hook-based integration is recalled through the Agent Plugin, and vice versa.
## Troubleshooting
**No memories recalled**: `recall` returns results only after something has been retained. Retain a fact first, or seed the bank via the [API](/developer/api/quickstart).
**401 Unauthorized**: Check `HINDSIGHT_API_KEY` is set and your client is interpolating it into the `Authorization` header (see the env-var syntax note above).
**Wrong or empty memory**: Confirm `HINDSIGHT_BANK_ID` points at the bank you expect. Different tools writing to different banks won't share memory.
## Learn more
- [Agent Plugins standard](https://agent-plugins.org)
- [Hindsight MCP Server reference](/developer/mcp-server)
- [Hindsight Cloud sign-up](https://ui.hindsight.vectorize.io/signup)
+10
View File
@@ -1,5 +1,15 @@
{
"integrations": [
{
"id": "agent-plugin",
"name": "Agent Plugins",
"description": "Portable long-term memory for any Agent Plugins client (Codex, Cursor, GitHub Copilot, Kiro, VS Code). One standards-based plugin bundles Hindsight's MCP server and a memory skill — recall, retain, and reflect everywhere.",
"type": "official",
"by": "hindsight",
"category": "tool",
"link": "/sdks/integrations/agent-plugin",
"icon": "/img/icons/mcp.png"
},
{
"id": "coding-agents",
"name": "Coding Agents",
@@ -0,0 +1,77 @@
# Hindsight — Agent Plugin
A portable [Agent Plugin](https://agent-plugins.org) (spec `1.0.0`) that gives any
compatible agent client long-term memory via [Hindsight](https://hindsight.vectorize.io).
Agent Plugins is the vendor-neutral standard (AWS, Cursor, GitHub/Microsoft, OpenAI,
Vercel) for packaging **Agent Skills + MCP servers** into one distributable plugin. At
launch it is supported by **ChatGPT/Codex, Cursor, GitHub Copilot, Kiro, and VS Code**.
This is the *portable* front door to Hindsight: one artifact, every supported client. It
carries the same `retain` / `recall` / `reflect` memory as our per-IDE integrations, but
as a single standards-based bundle instead of N hand-rolled configs.
## What's in the bundle
```
agent-plugin/
├── plugin.json # manifest ($schema + name + metadata)
├── mcp.json # Hindsight MCP server (Streamable HTTP)
└── skills/
└── hindsight-memory/
└── SKILL.md # teaches the agent when to recall/retain/reflect
```
- **`mcp.json`** points the client at Hindsight's built-in MCP server (retain, recall,
reflect, knowledge pages, and more — see [MCP Server docs](https://hindsight.vectorize.io/developer/mcp-server)).
The plugin is transport-only; all memory logic stays server-side.
- **`skills/hindsight-memory/SKILL.md`** is loaded into the agent's context so it knows
*when* to reach for memory, not just that the tools exist.
## Configuration
The plugin reads two environment variables (values are interpolated into `mcp.json`):
| Variable | Required | Purpose |
|----------|----------|---------|
| `HINDSIGHT_API_KEY` | yes (Cloud) | Your `hsk_...` key from [ui.hindsight.vectorize.io/connect](https://ui.hindsight.vectorize.io/connect). Sent as `Authorization: Bearer`. |
| `HINDSIGHT_BANK_ID` | no | Memory bank to scope to (sent as `X-Bank-Id`). Defaults to `default`. Use one bank per user/project/team. |
**Self-hosting:** replace the host in `mcp.json` (`https://api.hindsight.vectorize.io`)
with your deployment's URL. A local server with the MCP endpoint open needs no API key.
> Env-var interpolation syntax varies by client. Most use `${VAR}`; some (VS Code,
> Cursor) prefer `${env:VAR}`. If your client doesn't substitute, paste the literal key
> and bank id into `mcp.json` instead.
## Install
Installation and distribution are intentionally left to each client by the spec. Common
paths:
- **VS Code / GitHub Copilot / Cursor / Kiro** — add this plugin directory through the
client's plugin/MCP UI, or drop it where the client discovers plugins, then set the
two environment variables above.
- **Codex / ChatGPT** — register the plugin per the client's Agent Plugins support.
Once installed, ask the agent something that depends on past context (or tell it a
durable preference) and it will call `recall` / `retain` automatically, guided by the
skill.
## Want automatic capture (hooks)?
Agent Plugins `1.0.0` standardizes **Skills + MCP**, not session lifecycle hooks. This
plugin therefore delivers **explicit, tool-driven** memory that works identically
everywhere. For the fully automatic experience — recall injected before every prompt and
transcripts retained on session end — use the native, hook-based integration for your
tool (e.g. [`hindsight-integrations/claude-code`](../claude-code)). The two share the
same banks, so memory captured by one is recalled by the other.
## Validate the manifests
```bash
python3 hindsight-integrations/agent-plugin/validate.py
```
Checks that `plugin.json` and `mcp.json` parse and satisfy the Agent Plugins `1.0.0`
required-field contract.
@@ -0,0 +1,13 @@
{
"$schema": "https://agent-plugins.org/schemas/1.0.0/mcp.schema.json",
"mcpServers": {
"hindsight": {
"type": "streamable-http",
"url": "https://api.hindsight.vectorize.io/mcp",
"headers": {
"Authorization": "Bearer ${HINDSIGHT_API_KEY}",
"X-Bank-Id": "${HINDSIGHT_BANK_ID}"
}
}
}
}
@@ -0,0 +1,29 @@
{
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
"name": "hindsight",
"version": "1.0.0",
"description": "Long-term memory for AI agents. Recalls relevant past context before you answer and retains durable facts as you work, backed by Hindsight's retain/recall/reflect memory engine.",
"author": {
"name": "Hindsight (Vectorize)",
"url": "https://hindsight.vectorize.io"
},
"homepage": "https://hindsight.vectorize.io",
"repository": "https://github.com/vectorize-io/hindsight",
"license": "MIT",
"keywords": [
"memory",
"long-term-memory",
"recall",
"retain",
"reflect",
"mcp",
"agent-memory"
],
"extensions": {
"io.vectorize.hindsight": {
"bankSelection": "Set HINDSIGHT_BANK_ID to scope memory to a user, project, or team. The mcp.json endpoint is per-bank, so tools never need a bank_id argument.",
"selfHosted": "Point HINDSIGHT_MCP_URL at your own deployment instead of Hindsight Cloud; the plugin is transport-only and stays otherwise identical.",
"deepIntegration": "For automatic capture/injection (session hooks that recall before each prompt and retain transcripts on exit), see the native Claude Code plugin at hindsight-integrations/claude-code. This portable plugin exposes the same memory via explicit MCP tools."
}
}
}
@@ -0,0 +1,64 @@
---
name: hindsight-memory
description: Long-term memory for the agent via Hindsight. Use to recall relevant past context before answering, retain durable facts as you learn them, and reflect over accumulated memory for the "why" behind a decision. Load whenever continuity across sessions matters — the user refers to earlier work, states a lasting preference, or asks a question that prior context could answer.
---
# Hindsight long-term memory
This plugin connects the agent to **Hindsight**, a long-term memory engine. Memory
persists across sessions in a **bank** (scoped by `HINDSIGHT_BANK_ID`), so what you
retain now is available to recall in future conversations.
The `hindsight` MCP server exposes the tools below. Prefer these over guessing from
scratch when the answer might live in past context.
## When to recall (read memory)
Call **`recall`** at the start of a task, or whenever the user:
- refers to earlier work, a past decision, or "the thing we set up",
- states a preference or constraint that may already be recorded,
- asks a question that accumulated project/user context could answer.
```
recall(query: "how do we deploy the API and which region")
```
`recall` runs semantic + keyword + graph + temporal retrieval and returns the most
relevant memories. Ground your answer in what comes back, and say when nothing
relevant was found rather than inventing continuity.
## When to retain (write memory)
Call **`retain`** when you learn something **durable and reusable** — worth having in
a future session, not just this one:
- stable user preferences ("prefers pnpm; deploys from `main` only"),
- project facts and decisions ("staging DB is Postgres 16 on Neon"),
- outcomes and gotchas ("the flaky test was a timezone bug, fixed in #482").
```
retain(content: "The user deploys the API to us-east-1 via GitHub Actions on push to main.")
```
Do **not** retain transient chatter, secrets, or anything the user asked you to keep
out of memory. Retain the fact, not the whole transcript.
## When to reflect (reason over memory)
Call **`reflect`** when a single recall is too shallow and you need synthesized
reasoning over everything remembered — the *why* behind a behavior, or a judgment that
weighs many facts together:
```
reflect(query: "What has repeatedly caused our CI to flake, and what should we standardize?")
```
`reflect` is slower and disposition-aware; use it deliberately, not for lookups.
## Bank scope
All tools operate on the bank selected by the connection (`HINDSIGHT_BANK_ID`, default
`default`). One bank = one memory store — keep a project's or a user's memory in its
own bank so context stays isolated and relevant. You never pass a bank id to a tool;
it is implicit from the endpoint.
@@ -0,0 +1,99 @@
#!/usr/bin/env python3
"""Validate this Agent Plugin against the Agent Plugins 1.0.0 required-field contract.
Config-only plugin (no runtime code to unit-test), so the meaningful check is that the
manifests parse and satisfy the spec's structural requirements. Runs as a script
(`python3 validate.py`) and is imported by CI as a pytest.
"""
from __future__ import annotations
import json
import re
from pathlib import Path
PLUGIN_DIR = Path(__file__).resolve().parent
SPEC = "1.0.0"
PLUGIN_SCHEMA = f"https://agent-plugins.org/schemas/{SPEC}/plugin.schema.json"
MCP_SCHEMA = f"https://agent-plugins.org/schemas/{SPEC}/mcp.schema.json"
# https://agent-plugins.org/schemas/1.0.0/plugin.schema.json — name pattern
NAME_RE = re.compile(r"^(?!.*(?:--|\.\.))[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?$")
AUTHOR_KEYS = {"name", "email", "url"}
SERVER_TYPES = {"stdio", "streamable-http", "sse"}
def _load(name: str) -> dict:
return json.loads((PLUGIN_DIR / name).read_text())
def validate_plugin_manifest() -> None:
m = _load("plugin.json")
assert m.get("$schema") == PLUGIN_SCHEMA, f"plugin.json $schema must be {PLUGIN_SCHEMA}"
name = m.get("name")
assert isinstance(name, str) and 1 <= len(name) <= 64, "name must be a 1-64 char string"
assert NAME_RE.match(name), f"name {name!r} violates the Agent Plugins name pattern"
author = m.get("author")
if author is not None:
assert isinstance(author, dict), "author must be an object"
assert set(author).issubset(AUTHOR_KEYS), f"author allows only {AUTHOR_KEYS}"
extensions = m.get("extensions")
if extensions is not None:
assert isinstance(extensions, dict), "extensions must be an object"
for key in extensions:
assert "." in key, f"extension namespace {key!r} must be reverse-domain"
def validate_mcp_manifest() -> None:
m = _load("mcp.json")
assert m.get("$schema") == MCP_SCHEMA, f"mcp.json $schema must be {MCP_SCHEMA}"
servers = m.get("mcpServers")
assert isinstance(servers, dict) and servers, "mcp.json needs a non-empty mcpServers object"
for server_name, server in servers.items():
stype = server.get("type")
assert stype in SERVER_TYPES, f"{server_name}: type must be one of {SERVER_TYPES}"
if stype == "stdio":
assert server.get("command"), f"{server_name}: stdio server requires command"
else: # streamable-http | sse
assert server.get("url"), f"{server_name}: {stype} server requires url"
headers = server.get("headers", {})
assert all(isinstance(v, str) for v in headers.values()), (
f"{server_name}: header values must be strings"
)
def validate_skills() -> None:
skills_dir = PLUGIN_DIR / "skills"
assert skills_dir.is_dir(), "skills/ directory is missing"
skill_files = list(skills_dir.glob("*/SKILL.md"))
assert skill_files, "expected at least one skills/<name>/SKILL.md"
for skill in skill_files:
text = skill.read_text()
assert text.startswith("---"), f"{skill} must open with YAML frontmatter"
assert "name:" in text and "description:" in text, (
f"{skill} frontmatter needs name and description"
)
def test_plugin_manifest() -> None:
validate_plugin_manifest()
def test_mcp_manifest() -> None:
validate_mcp_manifest()
def test_skills() -> None:
validate_skills()
def main() -> int:
for check in (validate_plugin_manifest, validate_mcp_manifest, validate_skills):
check()
print(f"ok: {check.__name__}")
print("Agent Plugin is valid against Agent Plugins 1.0.0 required fields.")
return 0
if __name__ == "__main__":
raise SystemExit(main())
+7 -1
View File
@@ -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" "coding-agents" "codex" "composio" "continue" "copilot-cli" "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" "zcode" "zed")
VALID_INTEGRATIONS=("ag2" "agent-framework" "agent-plugin" "agentcore" "agno" "aider" "ai-sdk" "autogen" "chat" "claude-agent-sdk" "claude-code" "cline" "cloudflare-oauth-proxy" "coding-agents" "codex" "composio" "continue" "copilot-cli" "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" "zcode" "zed")
usage() {
print_error "Usage: $0 <integration> <version>"
@@ -58,6 +58,8 @@ get_current_version() {
grep '"version"' "$dir/package.json" | head -1 | sed 's/.*"version": "\(.*\)".*/\1/'
elif [ -f "$dir/.claude-plugin/plugin.json" ]; then
grep '"version"' "$dir/.claude-plugin/plugin.json" | head -1 | sed 's/.*"version": "\(.*\)".*/\1/'
elif [ -f "$dir/plugin.json" ]; then
grep '"version"' "$dir/plugin.json" | head -1 | sed 's/.*"version": "\(.*\)".*/\1/'
elif [ -f "$dir/settings.json" ] && grep -q '"version"' "$dir/settings.json"; then
grep '"version"' "$dir/settings.json" | head -1 | sed 's/.*"version": "\(.*\)".*/\1/'
else
@@ -155,6 +157,10 @@ elif [ -f "$INTEGRATION_DIR/.claude-plugin/plugin.json" ]; then
print_info "Updating version in $INTEGRATION_DIR/.claude-plugin/plugin.json"
sed -i.bak "s/\"version\": \".*\"/\"version\": \"$VERSION\"/" "$INTEGRATION_DIR/.claude-plugin/plugin.json"
rm "$INTEGRATION_DIR/.claude-plugin/plugin.json.bak"
elif [ -f "$INTEGRATION_DIR/plugin.json" ]; then
print_info "Updating version in $INTEGRATION_DIR/plugin.json"
sed -i.bak "s/\"version\": \".*\"/\"version\": \"$VERSION\"/" "$INTEGRATION_DIR/plugin.json"
rm "$INTEGRATION_DIR/plugin.json.bak"
elif [ -f "$INTEGRATION_DIR/settings.json" ] && grep -q '"version"' "$INTEGRATION_DIR/settings.json"; then
print_info "Updating version in $INTEGRATION_DIR/settings.json"
sed -i.bak "s/\"version\": \".*\"/\"version\": \"$VERSION\"/" "$INTEGRATION_DIR/settings.json"