You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: CLAUDE.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -48,7 +48,7 @@ Read [`docs/architecture.md`](docs/architecture.md) for the system overview, [`d
48
48
-**Routing:** In-house router at `src/admin/lib/routing/`. Replaces `react-router-dom`. Use it for all internal admin navigation, including links rendered from the site editor. `react-router-dom` is banned, raw `<a href="/admin...">` hard navigations are banned in admin UI, and `src/core/` + `src/modules/` must not import the admin router. Gated by `admin-router-usage.test.ts`.
49
49
-**Icons:**`pixel-art-icons/icons/<name>` — deep-imported, tree-shakeable. Vendored at `vendor/pixel-art-icons/`. No `lucide-react`, no inline SVG strings — gated by `no-third-party-icons.test.ts`, `direct-icon-imports.test.ts`. Add a new icon by importing it and running `bun run icons:sync`.
50
50
-**AI providers:** No provider SDKs. Each driver in `server/ai/drivers/` talks directly to its provider's REST API over HTTP/SSE, sharing one multi-turn tool loop (`drivers/http/toolLoop.ts`). `@anthropic-ai/sdk`, `@anthropic-ai/claude-agent-sdk`, `@openai/agents`, and `@openrouter/agent` are banned repo-wide. `@modelcontextprotocol/sdk` is **scoped, not banned**: allowed only under `server/ai/mcp/` (Instatic's MCP *server* implements a real wire protocol), still banned in the drivers and the browser. Gated by `ai-driver-isolation.test.ts`.
51
-
-**MCP server:** Instatic exposes its CMS tools to external MCP clients (Claude Code, Codex, remote agents) at `/_instatic/mcp`, authenticated by per-connector bearer tokens. Thin adapter over the existing tool engine: headless reads (content reads + `read_styles`) run in-process; ALL page editing (the full set of browser-execution editor tools) is relayed to the connector owner's **open editor**via the live editor bridge (`server/ai/mcp/editorBridge.ts` + `useEditorMcpBridge` in `SitePage`), reusing the chat bridge machinery — the live editor store is the single source of truth (no headless DB-mutating page-tree tool, which would desync). Feature doc: [`docs/features/mcp-connectors.md`](docs/features/mcp-connectors.md).
51
+
-**MCP server:** Instatic exposes its CMS tools to external MCP clients (Claude Code, Codex, remote agents) at `/_instatic/mcp`, authenticated by per-connector bearer tokens. Thin adapter over the existing tool engine: headless reads and explicit `site_publish` run in-process; browsertools route by scope to the connector owner's **open Site or Content workspace**through the `(userId, scope)` live bridge (`server/ai/mcp/editorBridge.ts` + `useMcpWorkspaceBridge`). The live workspace is the single source of truth for edits (no headless DB-mutating page-tree tool, which would desync), and writes remain drafts until the explicitly capability-gated publish call. Feature doc: [`docs/features/mcp-connectors.md`](docs/features/mcp-connectors.md).
52
52
-**Tree primitive:** Every tree-of-nodes — pages, Visual Components, slot fills — uses one shape: `NodeTree<TNode>` in `src/core/page-tree/treeSchema.ts`. Mutations are tree-agnostic. Reference: [`docs/reference/page-tree.md`](docs/reference/page-tree.md).
53
53
-**Publishing:** Three-layer pipeline. **Layer A** bakes fully-static pages to `uploads/published/current/<route>.html` at publish time via a two-slot symlink swap (`server/publish/staticArtefact.ts`). **Layer B** is an in-memory LRU keyed by `(urlPath, queryString, publishVersion)` for dynamic routes (`server/publish/renderCache.ts`); `bumpPublishVersion()` evicts wholesale on every publish. **Layer C** emits `<instatic-hole>` placeholders for nodes auto-detected as request-dependent; a ~668 B `IntersectionObserver` runtime lazy-fetches each fragment from `/_instatic/hole/<nodeId>`. Auto-detection lives in `src/core/publisher/dynamicDetection.ts` — one walker, four rules. Single entry: `server/publish/publicRouter.ts:renderPublicResolution`. Full design: [`docs/features/publisher.md`](docs/features/publisher.md).
54
54
-**Tests:**`bun test`. Architectural rules in `src/__tests__/architecture/*` — when *your* change drifts a structural rule, fix the rule's gate test in the same change.
├── AgentPanel.tsx — main panel; resolves active model's contextWindow from the models endpoint
@@ -464,7 +471,7 @@ When a node-targeting write tool (`site_insert_html`, `site_get_node_html`, `sit
464
471
465
472
Content-scope tools are registered under `server/ai/tools/content/`. They use the same `POST /admin/api/ai/chat/content` stream and `POST /admin/api/ai/tool-result` bridge as the Site editor, but the snapshot and browser executor are content-specific:
466
473
467
-
-`ContentAgentMount` builds a `ContentSnapshot` from the live Content workspace: visible `postType`/ `page`collections, active collection id, active document fields/schema, and current user identity.
474
+
-`ContentAgentMount` builds a `ContentSnapshot` from the live Content workspace: visible `postType` collections, active collection id, active document fields/schema, and current user identity.
468
475
-`contentAgentStore.ts` mounts a standalone `AgentSlice` instance per `ContentPage` mount. The Content workspace is hook-based rather than a global Zustand store, so the bridge is exposed through `contentBridgeHandle.ts`.
469
476
- Server read tools hit the data, media, and user repositories through `ctx.db`; write tools are browser-bridged so unsaved draft state in `useContentEntryDraft` and the Tiptap body editor stay authoritative.
470
477
@@ -490,7 +497,7 @@ Content-scope tools are registered under `server/ai/tools/content/`. They use th
490
497
|`content_set_document_field`| Writes one field; body values are markdown and are converted by the browser bridge |
491
498
|`content_set_document_fields`| Batch-writes multiple fields in one save |
492
499
|`content_set_document_author`| Reassigns author; gated by the same author-management capability path as the HTTP UI |
493
-
|`content_set_active_document`|Opens a document in the content editor before editing it visibly|
500
+
|`content_set_active_document`|Loads a document by id across post-type collections and commits the live editor focus before subsequent writes|
494
501
|`content_set_active_collection`| Switches the sidebar focus to a collection |
495
502
496
503
The content system prompt is markdown-native: it tells the model to exchange body content as standard markdown, to read schemas before writing unfamiliar fields, and to prefer `content_set_document_fields` for whole-post generation. The prompt is built with the same `[staticPrefix, SYSTEM_PROMPT_DYNAMIC_BOUNDARY, dynamicSuffix]` shape as the Site prompt so provider prompt caching works the same way.
Copy file name to clipboardExpand all lines: docs/features/content-workspace.md
+6-3Lines changed: 6 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -14,7 +14,7 @@ The Content workspace renders a three-pane shell — explorer sidebar, document
14
14
-**Canvas modes:**`write` (bare editor surface) and `live` (entry rendered inside its template with real site styles).
15
15
-**Settings panel:**`ContentSettingsPanel` — entry-specific; hidden when no entry is selected. Reopened via the top-right notch when collapsed.
16
16
-**Hooks:**`useContentWorkspace` (CRUD + selection), `useContentEntryDraft` (field state + save/publish), `useContentMediaPicker` (media modal + featured media).
17
-
-**AI assistant:**`ContentAgentMount` docks the shared Agent Panel in the content rail. It exposes the live workspace through a content-specific bridge so agent writes mutate the open draft/editor state, not stale database rows.
17
+
-**AI assistant:**`ContentAgentMount` docks the shared Agent Panel in the content rail. `useContentToolBridge`, mounted by `ContentPage`, exposes the live workspace to both the built-in agent and scoped MCP relay even when the panel is closed, so writes mutate the open draft/editor state rather than stale database rows.
18
18
19
19
---
20
20
@@ -149,7 +149,7 @@ The mode switch is client-only. The markdown body is the source of truth in both
149
149
150
150
The Content workspace has its own `content` chat scope, mounted as the `agent` panel in `ContentSidebar` when the current user has `ai.chat`.
151
151
152
-
`ContentAgentMount` creates a fresh per-page `AgentSlice` store (`contentAgentStore.ts`) and registers a `ContentBridgeHandle`for the mounted `ContentPage`. The handle reads the current collections, selected entry, draft fields, schema, and current user via refs so the agent sees the same state the user sees. Tool writes go through that handle and then through `useContentWorkspace` / `useContentEntryDraft`, which keeps unsaved body/title/SEO/media changes and sidebar selection in sync.
152
+
`ContentAgentMount` creates a fresh per-page `AgentSlice` store (`contentAgentStore.ts`) for the visible chat panel. Independently, `ContentPage` always mounts `useContentToolBridge`, which registers a `ContentBridgeHandle`and the `content` MCP stream for as long as the workspace is open. The handle reads the current collections, selected entry, draft fields, schema, and current user via refs so either caller sees the same state the user sees. Tool writes go through that handle and then through `useContentWorkspace` / `useContentEntryDraft`, which keeps unsaved body/title/SEO/media changes and sidebar selection in sync. `content_set_active_document` loads an uncached row by id, switches across post-type collections without waiting for the target sidebar list, and commits the workspace and draft focus before it returns so an immediately following write targets the selected document.
153
153
154
154
The server registers 15 content-scope tools:
155
155
@@ -160,6 +160,8 @@ The server registers 15 content-scope tools:
160
160
161
161
Body content is exchanged with the model as markdown. The browser bridge converts it to/from the Tiptap document when applying field writes, so the persisted `body` cell remains the same markdown source of truth used by the manual editor.
162
162
163
+
`content_create_document` always creates a draft. Publishing or scheduling is deliberately a follow-up `content_set_document_status` call so the dedicated `content.publish.own` / `content.publish.any` capability gate cannot be bypassed through creation.
164
+
163
165
---
164
166
165
167
## Forbidden patterns
@@ -184,7 +186,8 @@ Body content is exchanged with the model as markdown. The browser bridge convert
184
186
-[docs/features/agent.md](agent.md) — shared AI runtime, content-scope tools, and browser bridge
185
187
- Source-of-truth files:
186
188
-`src/admin/pages/content/ContentPage.tsx` — workspace mount point
187
-
-`src/admin/pages/content/agent/ContentAgentMount.tsx` — content Agent Panel mount and bridge registration
189
+
-`src/admin/pages/content/agent/ContentAgentMount.tsx` — content Agent Panel mount
190
+
-`src/admin/pages/content/agent/useContentToolBridge.ts` — always-mounted live handle + scoped MCP bridge registration
0 commit comments