mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-05 02:43:32 +00:00
feat(devcontainer): Codex + Cursor plugin/config host parity with Claude
Codex plugins installed in the container never reached the Windows host because, unlike Claude, the Codex plugin tree wasn't bind-mounted — only memories/ and skills/ were. Verified via live /proc/mounts: Claude binds 6 shareable dirs (incl. plugins/marketplaces + plugins/cache), Codex bound 2. So `codex plugin add` wrote into the ext4 named volume and stayed there. Codex changes: - Bind the WHOLE ~/.codex/plugins dir + ~/.codex/prompts (plus existing memories/skills). Strace of two real `codex plugin add` runs proved the installer stages INSIDE plugins/cache/<marketplace>/ and renames intra-dir, so a single 9p bind of plugins/ keeps the rename intra-fs — no EXDEV (the bug that broke single-file binds). .tmp/ stays on the volume (it's the cross-fs staging source). No path translation needed: Codex enablement lives in config.toml as git URLs, not FS paths. - Verified live: `codex plugin add compound-engineering@...` now writes through to C:\Users\...\.codex\plugins\cache\ on the Windows host, and host-created files appear in the container (bidirectional). Cursor changes (review found cursor-agent has a real plugin surface, not editor-only — Cursor 2.5 Marketplace shared by IDE + CLI): - Bind plugins/marketplaces, plugins/local, rules, commands, agents, skills (dir binds, EXDEV-safe). - Copy-on-create mcp.json (single file → EXDEV-unsafe as bind), alongside the existing cli-config.json. - Translate plugins/installed_plugins.json (carries absolute Windows paths like Claude's) — generalized the existing path-rewrite to run for both Claude and Cursor. - hooks.json deliberately NOT shared (runs shell commands → supply-chain surface); documented as opt-in. ensure-host-config-dirs.cjs pre-creates all new host bind sources. post-create.sh defensive symlink cleanup extended to the new Codex/Cursor paths. README updated with the accurate per-CLI share/sync/translate matrix. Design adversarially verified (straced installs, EXDEV primitive tests, sqlite-under-bind check, path-encoding check) before implementing.
This commit is contained in:
parent
18a78f5b99
commit
0d4a53e84d
4 changed files with 145 additions and 69 deletions
|
|
@ -88,17 +88,21 @@ The three AI CLIs use a **hybrid mount topology**: shareable subdirs/files (plug
|
|||
| Host Claude state, read-only stage | `$HOME/.claude` | `/host/.claude` | **read-only** | `post-create.sh` reads credentials + identity from here on container-create |
|
||||
| Host Codex state, read-only stage | `$HOME/.codex` | `/host/.codex` | **read-only** | Same purpose for Codex |
|
||||
| Host Cursor state, read-only stage | `$HOME/.cursor` | `/host/.cursor` | **read-only** | Same purpose for Cursor |
|
||||
| **Claude shareable subdirs** (overlay on the volume) | `$HOME/.claude/{plugins,skills,agents,memory,commands}` | `/home/node/.claude/{plugins,skills,agents,memory,commands}` | rw | **Bidirectional** — install plugin on host or in container, both sides see it |
|
||||
| **Claude shareable files** (overlay on the volume) | `$HOME/.claude/settings.json`, `$HOME/.claude.json` | `/home/node/.claude/settings.json`, `/home/node/.claude.json` | rw | Theme, enabled plugins, MCP user-scope, `hasCompletedOnboarding`, project trust — all shared |
|
||||
| **Codex shareable** | `$HOME/.codex/{config.toml,memories,skills}` | `/home/node/.codex/{config.toml,memories,skills}` | rw | Symmetric with Claude's shareable surface |
|
||||
| **Claude shareable subdirs** (overlay on the volume) | `$HOME/.claude/{plugins/marketplaces,plugins/cache,skills,agents,memory,commands}` | same under `/home/node/.claude/` | rw | **Bidirectional** dir binds — install plugin on host or in container, both sides see it |
|
||||
| **Codex shareable subdirs** | `$HOME/.codex/{plugins,prompts,memories,skills}` | same under `/home/node/.codex/` | rw | **Bidirectional** — whole `plugins/` dir bound (no path-bearing registry inside it) |
|
||||
| **Cursor shareable subdirs** | `$HOME/.cursor/{plugins/marketplaces,plugins/local,rules,commands,agents,skills}` | same under `/home/node/.cursor/` | rw | **Bidirectional** — cursor-agent shares the Cursor 2.5 plugin/rules/commands surface |
|
||||
|
||||
**What gets shared bidirectionally (RW bind from host):**
|
||||
**What gets shared bidirectionally (RW dir bind from host):**
|
||||
|
||||
- **Claude**: `plugins/`, `skills/`, `agents/`, `memory/`, `commands/` (the user-installed surface), `settings.json` (theme + `enabledPlugins` + `extraKnownMarketplaces`), `$HOME/.claude.json` (`hasCompletedOnboarding` + MCP user-scope + per-project trust + activity counters)
|
||||
- **Codex**: `config.toml` (prefs), `memories/` + `skills/` (user-installed surface)
|
||||
- **Cursor**: nothing structural (Cursor doesn't expose plugin/skill/agent dirs — `cli-config.json` conflates auth+settings and stays per-container)
|
||||
- **Claude**: `plugins/marketplaces`, `plugins/cache`, `skills/`, `agents/`, `memory/`, `commands/`
|
||||
- **Codex**: `plugins/` (whole dir — installed plugins land on host), `prompts/`, `memories/`, `skills/`
|
||||
- **Cursor**: `plugins/marketplaces`, `plugins/local`, `rules/`, `commands/`, `agents/`, `skills/`
|
||||
|
||||
Install a plugin on the host or inside the container — both sides see it immediately. Run `/plugin marketplace add` from inside the container and it lands in your host `~/.claude/plugins/marketplaces/`. Save a memory via the `/remember` skill from either side and it persists to the same host file.
|
||||
Install a plugin on the host or inside the container — both sides see it immediately. `/plugin marketplace add` from inside the container lands in your host `~/.<cli>/plugins/`, and `codex plugin add` / `cursor-agent` plugin installs write straight through to Windows. Save a memory from either side and it persists to the same host file.
|
||||
|
||||
**Single config files are copied on container-create, not bind-mounted** — on Docker Desktop Windows a single-file bind is 9p while the named volume is ext4, and atomic config writes (`tmp` → rename onto target) trip EXDEV (this is what caused Codex's `config/batchWrite failed in TUI`). So these are synced from host on rebuild and the container rewrites its own copy until the next rebuild: `settings.json` + `$HOME/.claude.json` (Claude), `config.toml` (Codex), `cli-config.json` + `mcp.json` (Cursor). `hooks.json` (Cursor) is deliberately **not** synced — Cursor hooks execute shell commands, so sharing them would widen the supply-chain attack surface; add it yourself if you want it.
|
||||
|
||||
**Plugin registry files with absolute paths are translated, not shared** — Claude's `known_marketplaces.json` / `installed_plugins.json` / `plugin-catalog-cache.json` and Cursor's `installed_plugins.json` bake in `C:\Users\…` (Windows) or `/Users/…` (macOS) install paths. `post-create.sh` rewrites those to `/home/node/.<cli>/plugins/…` and writes the result into the named volume, so plugins resolve inside Linux instead of failing with `cache-miss`. Codex needs no translation — its enablement registry is `config.toml` (git URLs + logical keys, no filesystem paths), which is why its whole `plugins/` dir can be bound directly.
|
||||
|
||||
**What stays per-container (in the named volume) and is synced from host on container-create:**
|
||||
|
||||
|
|
|
|||
|
|
@ -111,15 +111,48 @@
|
|||
// DIRECTORY binds — bidirectional, atomic writes inside the dir work
|
||||
// fine (same filesystem). Sub-path writes from container go to host
|
||||
// immediately; host changes are visible to container immediately.
|
||||
//
|
||||
// Claude: plugins SOURCE dirs (marketplaces/ git clones + cache/
|
||||
// extracted files) are path-independent; the path-bearing registry
|
||||
// JSONs at plugins/ root stay in the volume and are translated by
|
||||
// post-create.sh (see SINGLE-FILE note below).
|
||||
"source=${localEnv:HOME}/.claude/plugins/marketplaces,target=/home/node/.claude/plugins/marketplaces,type=bind",
|
||||
"source=${localEnv:HOME}/.claude/plugins/cache,target=/home/node/.claude/plugins/cache,type=bind",
|
||||
"source=${localEnv:HOME}/.claude/skills,target=/home/node/.claude/skills,type=bind",
|
||||
"source=${localEnv:HOME}/.claude/agents,target=/home/node/.claude/agents,type=bind",
|
||||
"source=${localEnv:HOME}/.claude/memory,target=/home/node/.claude/memory,type=bind",
|
||||
"source=${localEnv:HOME}/.claude/commands,target=/home/node/.claude/commands,type=bind",
|
||||
//
|
||||
// Codex: bind the WHOLE plugins/ dir (parent of cache/). `codex plugin
|
||||
// add` stages installs INSIDE plugins/cache/<marketplace>/ and renames
|
||||
// intra-dir (verified by strace), so under a single 9p bind of plugins/
|
||||
// the rename stays intra-9p — no EXDEV. Unlike Claude, Codex has NO
|
||||
// path-bearing registry file under plugins/ (enablement lives in
|
||||
// config.toml at the .codex root as git URLs, not FS paths), so no
|
||||
// translation is needed and the whole dir can be bound. `.tmp/` (the
|
||||
// ext4 marketplace-clone staging area) is deliberately NOT bound — it
|
||||
// must stay on the volume as the cross-fs source side.
|
||||
"source=${localEnv:HOME}/.codex/plugins,target=/home/node/.codex/plugins,type=bind",
|
||||
"source=${localEnv:HOME}/.codex/prompts,target=/home/node/.codex/prompts,type=bind",
|
||||
"source=${localEnv:HOME}/.codex/memories,target=/home/node/.codex/memories,type=bind",
|
||||
"source=${localEnv:HOME}/.codex/skills,target=/home/node/.codex/skills,type=bind",
|
||||
//
|
||||
// Cursor: cursor-agent (CLI, not just the IDE) shares the Cursor 2.5
|
||||
// plugin/rules/commands/agents/skills surface on disk. Bind the SOURCE
|
||||
// dirs (path-independent). plugins/installed_plugins.json carries
|
||||
// absolute Windows paths like Claude's registry, so it stays in the
|
||||
// volume and is translated by post-create.sh — that's why plugins/
|
||||
// sub-dirs are bound individually rather than the whole plugins/ dir.
|
||||
// mcp.json + hooks.json are single files (EXDEV-unsafe as binds) and
|
||||
// are handled as copy-on-create; hooks.json is intentionally NOT
|
||||
// shared (it executes shell commands — supply-chain surface).
|
||||
"source=${localEnv:HOME}/.cursor/plugins/marketplaces,target=/home/node/.cursor/plugins/marketplaces,type=bind",
|
||||
"source=${localEnv:HOME}/.cursor/plugins/local,target=/home/node/.cursor/plugins/local,type=bind",
|
||||
"source=${localEnv:HOME}/.cursor/rules,target=/home/node/.cursor/rules,type=bind",
|
||||
"source=${localEnv:HOME}/.cursor/commands,target=/home/node/.cursor/commands,type=bind",
|
||||
"source=${localEnv:HOME}/.cursor/agents,target=/home/node/.cursor/agents,type=bind",
|
||||
"source=${localEnv:HOME}/.cursor/skills,target=/home/node/.cursor/skills,type=bind",
|
||||
//
|
||||
// SINGLE-FILE binds for settings.json / .claude.json / config.toml
|
||||
// are deliberately ABSENT. On Docker Desktop Windows the named-volume
|
||||
// is ext4 (/dev/sdd) and a single-file bind from host is 9p drvfs —
|
||||
|
|
|
|||
|
|
@ -82,20 +82,36 @@ const home = os.homedir();
|
|||
const dirs = [
|
||||
'.claude',
|
||||
path.join('.claude', 'plugins'),
|
||||
// Plugin registry source dirs — content is path-independent so these
|
||||
// get RW bind-mounted bidirectionally. The path-DEPENDENT registry
|
||||
// JSONs (known_marketplaces.json, installed_plugins.json,
|
||||
// plugin-catalog-cache.json) stay in the container's named volume.
|
||||
// Claude plugin SOURCE dirs — content is path-independent so these get
|
||||
// RW bind-mounted bidirectionally. The path-DEPENDENT registry JSONs
|
||||
// (known_marketplaces.json, installed_plugins.json,
|
||||
// plugin-catalog-cache.json) stay in the container's named volume,
|
||||
// translated by post-create.sh.
|
||||
path.join('.claude', 'plugins', 'marketplaces'),
|
||||
path.join('.claude', 'plugins', 'cache'),
|
||||
path.join('.claude', 'skills'),
|
||||
path.join('.claude', 'agents'),
|
||||
path.join('.claude', 'memory'),
|
||||
path.join('.claude', 'commands'),
|
||||
// Codex shareable surface. The whole plugins/ dir is bound (no
|
||||
// path-bearing registry inside it — enablement is in config.toml at the
|
||||
// root), plus prompts/ (saved prompt library), memories/, skills/.
|
||||
'.codex',
|
||||
path.join('.codex', 'plugins'),
|
||||
path.join('.codex', 'prompts'),
|
||||
path.join('.codex', 'memories'),
|
||||
path.join('.codex', 'skills'),
|
||||
// Cursor shareable surface (cursor-agent CLI shares the Cursor 2.5
|
||||
// plugin/rules/commands/agents/skills dirs). plugins/ sub-dirs are
|
||||
// bound individually because plugins/installed_plugins.json carries
|
||||
// absolute paths and is translated (not bound) by post-create.sh.
|
||||
'.cursor',
|
||||
path.join('.cursor', 'plugins', 'marketplaces'),
|
||||
path.join('.cursor', 'plugins', 'local'),
|
||||
path.join('.cursor', 'rules'),
|
||||
path.join('.cursor', 'commands'),
|
||||
path.join('.cursor', 'agents'),
|
||||
path.join('.cursor', 'skills'),
|
||||
'.ssh',
|
||||
'.docker',
|
||||
'.aws',
|
||||
|
|
@ -110,12 +126,16 @@ for (const dir of dirs) {
|
|||
fs.mkdirSync(path.join(home, dir), { recursive: true });
|
||||
}
|
||||
|
||||
// File bind-mount sources. devcontainer.json bind-mounts each file
|
||||
// individually (so the host file IS the container file — bidirectional
|
||||
// share). Touch-empty if absent so Docker doesn't reject the mount.
|
||||
// `~/.claude.json` carries `hasCompletedOnboarding` + MCP user-scope +
|
||||
// per-project trust; `~/.claude/settings.json` carries theme + enabled
|
||||
// plugins; `~/.codex/config.toml` carries Codex user prefs.
|
||||
// Single-file sources. `~/.claude.json` is bind-mounted READ-ONLY at
|
||||
// /host/.claude.json, so it must exist or Docker rejects the mount —
|
||||
// touch-empty if absent. The others are read by post-create.sh from the
|
||||
// /host/.<cli> read-only dir stages and COPIED into the named volume
|
||||
// (copy-on-create, never single-file-bound — that trips EXDEV on Docker
|
||||
// Desktop Windows). Touching them is harmless and gives sync a source:
|
||||
// `~/.claude/settings.json` (theme + enabled plugins), `~/.codex/config.toml`
|
||||
// (Codex prefs + plugin enablement). `~/.cursor/{mcp.json,cli-config.json}`
|
||||
// are left untouched — sync_from_host no-ops if the host never created
|
||||
// them, which is the correct "not configured yet" state.
|
||||
const files = [
|
||||
'.claude.json',
|
||||
path.join('.claude', 'settings.json'),
|
||||
|
|
|
|||
|
|
@ -33,23 +33,31 @@ echo "[post-create] 2/2: sync AI CLI credentials + identity from host"
|
|||
for p in plugins skills agents memory commands; do
|
||||
[ -L "/home/node/.claude/$p" ] && rm "/home/node/.claude/$p"
|
||||
done
|
||||
for p in config.toml memories skills; do
|
||||
for p in plugins prompts memories skills config.toml; do
|
||||
[ -L "/home/node/.codex/$p" ] && rm "/home/node/.codex/$p"
|
||||
done
|
||||
mkdir -p /home/node/.claude/plugins
|
||||
for p in plugins rules commands agents skills; do
|
||||
[ -L "/home/node/.cursor/$p" ] && rm "/home/node/.cursor/$p"
|
||||
done
|
||||
mkdir -p /home/node/.claude/plugins /home/node/.cursor/plugins
|
||||
|
||||
# Plugins, skills, agents, memory, commands, settings.json, $HOME/.claude.json,
|
||||
# Codex config.toml/memories/skills are all RW bind-mounted directly from
|
||||
# host in devcontainer.json — they live on host and reads/writes go
|
||||
# bidirectionally. Nothing for this script to do for those.
|
||||
# Shareable content is RW bind-mounted directly from host in
|
||||
# devcontainer.json — reads/writes go bidirectionally, nothing for this
|
||||
# script to do for those:
|
||||
# - Claude: plugins/{marketplaces,cache}, skills, agents, memory, commands
|
||||
# - Codex: plugins (whole dir), prompts, memories, skills
|
||||
# - Cursor: plugins/{marketplaces,local}, rules, commands, agents, skills
|
||||
#
|
||||
# What stays per-container (in the named volume) and gets SYNCED from
|
||||
# host on container-create:
|
||||
# - .credentials.json (Claude OAuth tokens)
|
||||
# - .claude/.claude.json (Claude identity: userID, oauthAccount,
|
||||
# migration tracking — different file from $HOME/.claude.json)
|
||||
# - auth.json (Codex)
|
||||
# - cli-config.json (Cursor — conflates auth + settings)
|
||||
# - settings.json (Claude), config.toml (Codex), mcp.json (Cursor) —
|
||||
# single config files that can't be bind-mounted (EXDEV on Windows)
|
||||
# - auth.json (Codex), cli-config.json (Cursor — conflates auth+settings)
|
||||
# - plugins registry JSONs with absolute paths (Claude + Cursor) —
|
||||
# translated below
|
||||
#
|
||||
# Sync semantics: ALWAYS overwrite from host on container-create, so a
|
||||
# fresh container starts logged in as host's user (if host had creds).
|
||||
|
|
@ -83,61 +91,66 @@ sync_from_host /host/.claude/settings.json /home/node/.claude/settings.json 644
|
|||
sync_from_host /host/.claude.json /home/node/.claude.json 644
|
||||
sync_from_host /host/.codex/config.toml /home/node/.codex/config.toml 644
|
||||
|
||||
# Plugin registry path translation. Claude writes absolute OS-native paths
|
||||
# into known_marketplaces.json (`installLocation`), installed_plugins.json
|
||||
# (`installPath`), and plugin-catalog-cache.json — `C:\Users\X\.claude\...`
|
||||
# on Windows hosts, `/Users/X/.claude/...` on macOS — so the host versions
|
||||
# can't be bind-mounted into the Linux container (Claude would fail with
|
||||
# `cache-miss` trying to resolve a Windows path inside Linux). Read host's
|
||||
# registry files, rewrite every absolute path that ends in
|
||||
# `/.claude/plugins/<rest>` to `/home/node/.claude/plugins/<rest>`, and
|
||||
# write the result into the container's named volume.
|
||||
mkdir -p /home/node/.claude/plugins
|
||||
# Plugin registry path translation (Claude + Cursor). Both write absolute
|
||||
# OS-native install paths into their plugin registry JSONs —
|
||||
# `C:\Users\X\.claude\plugins\...` on Windows, `/Users/X/.cursor/plugins/...`
|
||||
# on macOS — so the host versions can't be bind-mounted into the Linux
|
||||
# container (the CLI fails with `cache-miss` resolving a Windows path under
|
||||
# Linux). For each CLI, read host's registry files, rewrite every absolute
|
||||
# path ending in `/.<cli>/plugins/<rest>` to
|
||||
# `/home/node/.<cli>/plugins/<rest>`, and write the result into the named
|
||||
# volume. (Codex needs no translation — its enablement registry is
|
||||
# config.toml with git URLs, not filesystem paths, so its whole plugins/
|
||||
# dir is bind-mounted instead.)
|
||||
node <<'NODE'
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const HOST = "/host/.claude/plugins";
|
||||
const CTR = "/home/node/.claude/plugins";
|
||||
const buildRe = (cliName) =>
|
||||
new RegExp(`^(?:[A-Za-z]:)?[\\\\/].*?[\\\\/]\\.${cliName}[\\\\/]plugins[\\\\/](.*)$`);
|
||||
|
||||
// Match any absolute path (Windows `C:\Users\…\.claude\plugins\<rest>`
|
||||
// or POSIX `/Users/…/.claude/plugins/<rest>` or
|
||||
// `/home/…/.claude/plugins/<rest>`) and translate to the container path.
|
||||
const rewrite = (s) => {
|
||||
if (typeof s !== "string") return s;
|
||||
return s.replace(
|
||||
/^(?:[A-Za-z]:)?[\\/].*?[\\/]\.claude[\\/]plugins[\\/](.*)$/,
|
||||
(_, rest) => `${CTR}/${rest.replace(/\\/g, "/")}`,
|
||||
);
|
||||
};
|
||||
|
||||
const rewriteDeep = (obj) => {
|
||||
if (Array.isArray(obj)) return obj.map(rewriteDeep);
|
||||
const rewriteDeep = (obj, re, ctr) => {
|
||||
if (Array.isArray(obj)) return obj.map((v) => rewriteDeep(v, re, ctr));
|
||||
if (obj && typeof obj === "object") {
|
||||
const out = {};
|
||||
for (const [k, v] of Object.entries(obj)) {
|
||||
out[k] = typeof v === "string" ? rewrite(v) : rewriteDeep(v);
|
||||
}
|
||||
for (const [k, v] of Object.entries(obj)) out[k] = rewriteDeep(v, re, ctr);
|
||||
return out;
|
||||
}
|
||||
if (typeof obj === "string")
|
||||
return obj.replace(re, (_, rest) => `${ctr}/${rest.replace(/\\/g, "/")}`);
|
||||
return obj;
|
||||
};
|
||||
|
||||
for (const name of [
|
||||
"known_marketplaces.json",
|
||||
"installed_plugins.json",
|
||||
"plugin-catalog-cache.json",
|
||||
]) {
|
||||
const src = path.join(HOST, name);
|
||||
const dst = path.join(CTR, name);
|
||||
if (!fs.existsSync(src) || fs.statSync(src).size === 0) continue;
|
||||
let data;
|
||||
try {
|
||||
data = JSON.parse(fs.readFileSync(src, "utf8"));
|
||||
} catch {
|
||||
continue;
|
||||
const REGISTRIES = [
|
||||
{
|
||||
cli: "claude",
|
||||
host: "/host/.claude/plugins",
|
||||
ctr: "/home/node/.claude/plugins",
|
||||
files: ["known_marketplaces.json", "installed_plugins.json", "plugin-catalog-cache.json"],
|
||||
},
|
||||
{
|
||||
cli: "cursor",
|
||||
host: "/host/.cursor/plugins",
|
||||
ctr: "/home/node/.cursor/plugins",
|
||||
files: ["installed_plugins.json"],
|
||||
},
|
||||
];
|
||||
|
||||
for (const reg of REGISTRIES) {
|
||||
const re = buildRe(reg.cli);
|
||||
fs.mkdirSync(reg.ctr, { recursive: true });
|
||||
for (const name of reg.files) {
|
||||
const src = path.join(reg.host, name);
|
||||
const dst = path.join(reg.ctr, name);
|
||||
if (!fs.existsSync(src) || fs.statSync(src).size === 0) continue;
|
||||
let data;
|
||||
try {
|
||||
data = JSON.parse(fs.readFileSync(src, "utf8"));
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
fs.writeFileSync(dst, JSON.stringify(rewriteDeep(data, re, reg.ctr), null, 2));
|
||||
}
|
||||
fs.writeFileSync(dst, JSON.stringify(rewriteDeep(data), null, 2));
|
||||
}
|
||||
NODE
|
||||
|
||||
|
|
@ -151,8 +164,14 @@ sync_from_host \
|
|||
# Cursor CLI — cli-config.json conflates auth + settings. Cursor has known
|
||||
# upstream issues authenticating inside Docker even with correctly-copied
|
||||
# config; if `cursor-agent` reports auth errors after copy, re-run
|
||||
# `cursor-agent login` inside the container.
|
||||
# `cursor-agent login` inside the container. mcp.json (Cursor's MCP server
|
||||
# config) is a single file too — copy-on-create, not bind (EXDEV).
|
||||
# hooks.json is deliberately NOT synced: Cursor hooks run shell commands,
|
||||
# so sharing them would widen the supply-chain attack surface into the
|
||||
# container. Add it yourself if you want host hooks inside the container.
|
||||
sync_from_host \
|
||||
/host/.cursor/cli-config.json /home/node/.cursor/cli-config.json
|
||||
sync_from_host \
|
||||
/host/.cursor/mcp.json /home/node/.cursor/mcp.json 644
|
||||
|
||||
echo "[post-create] done"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue