Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
163 changes: 163 additions & 0 deletions .github/workflows/publish-extension.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
name: Publish extension

# Build a platform-specific .vsix on every push to the extension branch
# (artifact-only, no publish). Publish to Open VSX only when a human pushes
# a tag matching `extension-v*` — agents must never push tags per D-024.

on:
push:
branches:
- feat/vscode-extension-**
tags:
- "extension-v*"
pull_request:
paths:
- "extension/**"
- ".github/workflows/publish-extension.yml"
workflow_dispatch:

permissions:
contents: read

jobs:
build:
name: Build .vsix (${{ matrix.target }})
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
include:
- target: linux-x64
runner: ubuntu-latest
binary_name: axme-code-linux-x64
extension_bin: axme-code
- target: linux-arm64
runner: ubuntu-latest
binary_name: axme-code-linux-arm64
extension_bin: axme-code
- target: darwin-x64
runner: macos-13
binary_name: axme-code-darwin-x64
extension_bin: axme-code
- target: darwin-arm64
runner: macos-latest
binary_name: axme-code-darwin-arm64
extension_bin: axme-code
- target: win32-x64
runner: windows-latest
binary_name: axme-code-windows-x64.exe
extension_bin: axme-code.exe
steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm

- name: Install core deps
run: npm install

- name: Build core
run: npm run build

- name: Bundle core CLI to a single platform-specific file
shell: bash
run: |
mkdir -p extension/bin
# Bundle dist/cli.mjs into a single CJS file with all deps inlined
# EXCEPT @cursor/sdk. claude-agent-sdk is always required (used by
# LLM scanners during setup and by the session auditor); it must
# be inside the binary so Node doesn't try to resolve it from a
# node_modules/ dir that doesn't ship with the .vsix.
#
# @cursor/sdk stays external because (a) it carries ~15 MB of
# platform-specific native binaries that bloat the .vsix, and
# (b) the AgentSdk factory's fallback gracefully degrades to the
# Claude path on MODULE_NOT_FOUND. v0.0.1 users use Claude for
# the auditor; Cursor SDK as a first-class in-extension option
# is a v0.0.2 follow-up.
#
# WHY CJS, not ESM: the output is a shebang script with no file
# extension (Windows uses .exe — see matrix.extension_bin).
# Without ".mjs" extension AND without a sibling package.json
# declaring "type":"module", Node loads the file as CJS and
# ESM import statements throw at runtime.
npx esbuild dist/cli.mjs \
--bundle \
--platform=node \
--target=node20 \
--format=cjs \
--external:@cursor/sdk \
--outfile=extension/bin/axme-code.cjs
# Wrap in a shebang shim so it's executable as a binary.
{
printf '#!/usr/bin/env node\n'
cat extension/bin/axme-code.cjs
} > extension/bin/${{ matrix.extension_bin }}
rm extension/bin/axme-code.cjs
chmod +x extension/bin/${{ matrix.extension_bin }} || true

- name: Install extension deps
working-directory: extension
run: npm install

- name: Build extension bundle
working-directory: extension
run: npm run build

- name: Package .vsix
working-directory: extension
run: npx vsce package --target ${{ matrix.target }} --no-dependencies -o ../axme-code-${{ matrix.target }}.vsix

- uses: actions/upload-artifact@v4
with:
name: axme-code-${{ matrix.target }}
path: axme-code-${{ matrix.target }}.vsix
retention-days: 14

publish:
name: Publish to Open VSX
needs: build
if: startsWith(github.ref, 'refs/tags/extension-v')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20

- name: Download all .vsix artifacts
uses: actions/download-artifact@v4
with:
path: vsix
merge-multiple: true

- name: Publish per-target to Open VSX
env:
OVSX_TOKEN: ${{ secrets.OVSX_TOKEN }}
run: |
set -euo pipefail
for target in linux-x64 linux-arm64 darwin-x64 darwin-arm64 win32-x64; do
file="vsix/axme-code-${target}.vsix"
if [ ! -f "$file" ]; then
echo "Warning: $file missing; skipping $target."
continue
fi
echo "Publishing $file (target=$target)"
npx ovsx publish "$file" --target "$target" --pat "$OVSX_TOKEN"
done

- name: Attach .vsix files to GitHub Release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
# Create release if missing, then attach all 5 .vsix files for
# sideload distribution alongside Open VSX.
tag="${GITHUB_REF#refs/tags/}"
gh release view "$tag" >/dev/null 2>&1 || \
gh release create "$tag" --title "$tag" --notes "Extension $tag — six platform-specific .vsix files attached. See README for install instructions."
for f in vsix/axme-code-*.vsix; do
gh release upload "$tag" "$f" --clobber
done
16 changes: 14 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,21 @@ Decisions enforce verification requirements: agent must run tests and show proof

## Quick Start

**Requires [Claude Code](https://docs.anthropic.com/en/docs/claude-code) (CLI or VS Code extension).**
AXME Code supports three IDE paths today, ranked by lowest install friction:

### Option 1: Claude Code plugin (recommended)
### Option 0: Cursor extension (1-click install — recommended for Cursor users)

For **Cursor 0.42+** users — install the `AXME Code` extension from the Extensions panel (Open VSX). The extension bundles the binary, registers the MCP server programmatically (no manual Enable click), installs user-level safety hooks at `~/.cursor/hooks.json` (apply to every project on your machine), and offers a one-click "Run setup" notification the first time you open a project without `.axme-code/`.

```
Cursor → Extensions → search "AXME Code" → Install
```

Or sideload the .vsix attached to the [latest release](https://github.com/AxmeAI/axme-code/releases) (`Extensions → ... menu → "Install from VSIX..."`).

On first activation a modal asks for an LLM credential for the session auditor: paste an Anthropic API key, a Cursor SDK key (cursor.com → Integrations), or skip the auditor. If `claude` CLI is logged in (`claude login`), the extension auto-uses your Claude subscription — no paste needed.

### Option 1: Claude Code plugin (recommended for Claude Code users)

In Claude Code, run:

Expand Down
4 changes: 4 additions & 0 deletions extension/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
node_modules/
out/
*.vsix
package-lock.json
13 changes: 13 additions & 0 deletions extension/.vscodeignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
.vscode/**
.vscode-test/**
src/**
**/*.map
**/*.ts
**/tsconfig.json
**/.eslintrc*
**/.gitignore
**/build.mjs
**/node_modules/**
.github/**
.git/**
**/*.vsix
37 changes: 37 additions & 0 deletions extension/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# AXME Code

Persistent memory, decisions, and safety guardrails for AI coding agents — Cursor, GitHub Copilot, Cline, Continue, Roo Code, Windsurf, and any VS Code chat agent that respects the Language Model API.

## What this extension does

- Registers `axme-code` as an MCP server so the agent has access to the project knowledge base — `axme_context`, `axme_save_memory`, `axme_save_decision`, `axme_safety`, and ~15 more tools.
- Installs safety hooks at the user level (`~/.cursor/hooks.json`) so dangerous operations (force-push to main, `rm -rf` on protected paths, secret-file edits) are blocked **before** the agent runs them.
- Auto-spawns the session auditor at chat end — extracts non-obvious patterns / decisions / safety rules from your conversation and saves them to `.axme-code/` for the next session to load.

## Requirements

- Cursor 0.42+ **or** VS Code 1.96+ (Copilot Agent Mode, Cline, Continue, Roo Code, Windsurf — anything that consumes MCP servers via the standard discovery API).
- The `axme-code` CLI installed on your `$PATH`. Get it: `curl -fsSL https://raw.githubusercontent.com/AxmeAI/axme-code/main/install.sh | bash`. The extension auto-detects it on activation; if your install is non-standard, set `axme.binaryPath` in settings.

## Settings

| Setting | Default | Description |
| --- | --- | --- |
| `axme.binaryPath` | `""` | Absolute path to the `axme-code` binary. Leave empty for auto-detect. |
| `axme.contextMode` | `"full"` | `full` loads every memory into agent context. `search` uses semantic search at scale. |
| `axme.enableHooks` | `true` | Register safety hooks. Turn off if you don't want machine-wide guardrails. |

## Commands

- **AXME: Set up workspace** — runs `axme-code setup` against the current workspace folder.
- **AXME: Open dashboard** — opens the worklog / decisions / memories view.
- **AXME: Reindex semantic search** — rebuilds the embeddings index.
- **AXME: Show status** — shows session count, audit health, recent worklog entries.

## How this differs from the CLI install

The `axme-code` CLI alone writes `.cursor/mcp.json` and `.cursor/hooks.json` per project. Cursor 0.42+ requires a manual **Enable** click in Settings → MCP for any new project-level server (security feature). This extension registers MCP via Cursor's extension API directly, bypassing the per-project Enable gate — install the extension once, every project just works.

## License

MIT — see [LICENSE](https://github.com/AxmeAI/axme-code/blob/main/LICENSE).
38 changes: 38 additions & 0 deletions extension/build.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* Bundle the AXME Code VS Code extension entry point.
*
* VS Code extensions are loaded as CommonJS, with `vscode` provided by the
* host (must stay external). Everything else is inlined into a single file
* so the .vsix has no node_modules at runtime — that keeps the artifact
* small (well under 1 MB) and avoids platform-specific binary surprises.
*/

import { build, context } from "esbuild";
import { readFileSync } from "fs";

const watch = process.argv.includes("--watch");

const pkg = JSON.parse(readFileSync("package.json", "utf-8"));

const buildOptions = {
entryPoints: ["src/extension.ts"],
bundle: true,
platform: "node",
target: "node20",
format: "cjs",
external: ["vscode"],
outfile: "out/extension.js",
sourcemap: true,
define: {
__EXTENSION_VERSION__: JSON.stringify(pkg.version),
},
};

if (watch) {
const ctx = await context(buildOptions);
await ctx.watch();
console.log("Watching extension/...");
} else {
await build(buildOptions);
console.log(`Built extension v${pkg.version} → out/extension.js`);
}
100 changes: 100 additions & 0 deletions extension/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
{
"name": "axme-code",
"displayName": "AXME Code",
"description": "Persistent memory, decisions, and safety guardrails for Cursor, GitHub Copilot, Cline, Continue, Roo Code, Windsurf, and VS Code chat agents",
"version": "0.0.1",
"publisher": "AxmeAI",
"repository": {
"type": "git",
"url": "https://github.com/AxmeAI/axme-code.git",
"directory": "extension"
},
"homepage": "https://github.com/AxmeAI/axme-code",
"bugs": {
"url": "https://github.com/AxmeAI/axme-code/issues"
},
"license": "MIT",
"engines": {
"vscode": "^1.96.0"
},
"categories": [
"AI",
"Other"
],
"keywords": [
"ai",
"agent",
"cursor",
"copilot",
"cline",
"mcp",
"memory",
"safety"
],
"main": "./out/extension.js",
"activationEvents": [
"onStartupFinished"
],
"contributes": {
"configuration": {
"title": "AXME Code",
"properties": {
"axme.binaryPath": {
"type": "string",
"default": "",
"markdownDescription": "Absolute path to the `axme-code` binary. Leave empty to auto-detect via `$PATH`, `~/.local/bin/axme-code`, or platform standards. Override only if you have a non-standard install."
},
"axme.contextMode": {
"type": "string",
"enum": [
"full",
"search"
],
"default": "full",
"markdownDescription": "Knowledge-base loading mode. `full` loads every memory + decision into agent context (default, simple). `search` loads only catalog + uses semantic search (saves tokens at scale; requires the search-mode runtime — install via `axme-code config set context.mode search`)."
},
"axme.enableHooks": {
"type": "boolean",
"default": true,
"markdownDescription": "Register safety hooks (`preToolUse` / `postToolUse` / `sessionEnd`) at activation. When ON, force-pushes / dangerous bash / file-write violations are blocked across every project on this machine."
}
}
},
"commands": [
{
"command": "axme.setup",
"title": "AXME: Set up workspace (writes .axme-code/ + hooks)",
"category": "AXME"
},
{
"command": "axme.openDashboard",
"title": "AXME: Open dashboard (worklog + decisions + memories)",
"category": "AXME"
},
{
"command": "axme.reindex",
"title": "AXME: Reindex semantic search",
"category": "AXME"
},
{
"command": "axme.showStatus",
"title": "AXME: Show status",
"category": "AXME"
}
]
},
"scripts": {
"build": "node build.mjs",
"watch": "node build.mjs --watch",
"package": "vsce package --no-dependencies",
"publish:ovsx": "ovsx publish --pat $OVSX_TOKEN"
},
"devDependencies": {
"@types/node": "^22.0.0",
"@types/vscode": "^1.96.0",
"@vscode/vsce": "^3.2.0",
"esbuild": "^0.25.0",
"ovsx": "^0.10.0",
"typescript": "^5.9.0"
}
}
Loading
Loading