@agent-native/core 0.102.2 → 0.103.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (205) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +12 -0
  3. package/corpus/core/docs/content/local-file-mode.mdx +99 -489
  4. package/corpus/core/docs/content/locales/ar-SA/local-file-mode.mdx +35 -490
  5. package/corpus/core/docs/content/locales/ar-SA/template-content.mdx +10 -24
  6. package/corpus/core/docs/content/locales/de-DE/local-file-mode.mdx +37 -491
  7. package/corpus/core/docs/content/locales/de-DE/template-content.mdx +10 -24
  8. package/corpus/core/docs/content/locales/es-ES/local-file-mode.mdx +35 -493
  9. package/corpus/core/docs/content/locales/es-ES/template-content.mdx +10 -24
  10. package/corpus/core/docs/content/locales/fr-FR/local-file-mode.mdx +38 -491
  11. package/corpus/core/docs/content/locales/fr-FR/template-content.mdx +10 -24
  12. package/corpus/core/docs/content/locales/hi-IN/local-file-mode.mdx +33 -492
  13. package/corpus/core/docs/content/locales/hi-IN/template-content.mdx +10 -24
  14. package/corpus/core/docs/content/locales/ja-JP/local-file-mode.mdx +36 -492
  15. package/corpus/core/docs/content/locales/ja-JP/template-content.mdx +10 -24
  16. package/corpus/core/docs/content/locales/ko-KR/local-file-mode.mdx +35 -490
  17. package/corpus/core/docs/content/locales/ko-KR/template-content.mdx +10 -24
  18. package/corpus/core/docs/content/locales/pt-BR/local-file-mode.mdx +35 -493
  19. package/corpus/core/docs/content/locales/pt-BR/template-content.mdx +10 -24
  20. package/corpus/core/docs/content/locales/zh-CN/local-file-mode.mdx +31 -489
  21. package/corpus/core/docs/content/locales/zh-CN/template-content.mdx +9 -24
  22. package/corpus/core/docs/content/locales/zh-TW/local-file-mode.mdx +31 -489
  23. package/corpus/core/docs/content/locales/zh-TW/template-content.mdx +9 -24
  24. package/corpus/core/docs/content/template-content.mdx +26 -42
  25. package/corpus/core/package.json +1 -1
  26. package/corpus/core/src/cli/content-local.ts +86 -18
  27. package/corpus/core/src/cli/index.ts +2 -2
  28. package/corpus/core/src/cli/skills-content/content-skill.ts +36 -25
  29. package/corpus/core/src/cli/skills.ts +47 -13
  30. package/corpus/core/src/local-artifacts/index.ts +65 -0
  31. package/corpus/core/src/vite/client.ts +2 -0
  32. package/corpus/templates/assets/app/components/library/GenerationPresetsPanel.tsx +325 -0
  33. package/corpus/templates/assets/app/hooks/use-navigation-state.ts +12 -0
  34. package/corpus/templates/assets/app/i18n/zh-TW.ts +21 -0
  35. package/corpus/templates/assets/app/i18n-data.ts +215 -0
  36. package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +19 -479
  37. package/corpus/templates/assets/app/routes/brand-kits.$id_.presets.$presetId.tsx +1 -1
  38. package/corpus/templates/assets/app/routes/brand-kits.$id_.settings.tsx +446 -0
  39. package/corpus/templates/clips/app/components/editor/editor-layout.tsx +12 -2
  40. package/corpus/templates/clips/app/components/player/settings-panel.tsx +30 -12
  41. package/corpus/templates/clips/app/components/player/video-player.tsx +5 -1
  42. package/corpus/templates/clips/app/components/recorder/microphone-visualizer.tsx +22 -3
  43. package/corpus/templates/clips/app/i18n/ar-SA.ts +3 -2
  44. package/corpus/templates/clips/app/i18n/de-DE.ts +4 -2
  45. package/corpus/templates/clips/app/i18n/en-US.ts +3 -2
  46. package/corpus/templates/clips/app/i18n/es-ES.ts +4 -2
  47. package/corpus/templates/clips/app/i18n/fr-FR.ts +3 -2
  48. package/corpus/templates/clips/app/i18n/hi-IN.ts +3 -2
  49. package/corpus/templates/clips/app/i18n/ja-JP.ts +4 -1
  50. package/corpus/templates/clips/app/i18n/ko-KR.ts +3 -2
  51. package/corpus/templates/clips/app/i18n/pt-BR.ts +4 -2
  52. package/corpus/templates/clips/app/i18n/zh-CN.ts +3 -2
  53. package/corpus/templates/clips/app/i18n/zh-TW.ts +3 -2
  54. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +27 -1
  55. package/corpus/templates/clips/server/lib/share-password.ts +6 -3
  56. package/corpus/templates/content/.agents/skills/content/SKILL.md +26 -39
  57. package/corpus/templates/content/AGENTS.md +25 -30
  58. package/corpus/templates/content/actions/_builder-docs-client.ts +23 -0
  59. package/corpus/templates/content/actions/_content-database-source-adapters.ts +21 -3
  60. package/corpus/templates/content/actions/_content-files.ts +338 -0
  61. package/corpus/templates/content/actions/_content-space-access.ts +135 -0
  62. package/corpus/templates/content/actions/_content-spaces.ts +640 -0
  63. package/corpus/templates/content/actions/_database-source-utils.ts +90 -7
  64. package/corpus/templates/content/actions/_database-utils.ts +7 -1
  65. package/corpus/templates/content/actions/_local-file-documents.ts +0 -9
  66. package/corpus/templates/content/actions/_local-folder-source.ts +53 -0
  67. package/corpus/templates/content/actions/_property-utils.ts +9 -4
  68. package/corpus/templates/content/actions/add-database-item.ts +108 -65
  69. package/corpus/templates/content/actions/backfill-content-files.ts +22 -0
  70. package/corpus/templates/content/actions/connect-local-folder-source.ts +221 -0
  71. package/corpus/templates/content/actions/create-content-database.ts +129 -3
  72. package/corpus/templates/content/actions/create-document.ts +65 -55
  73. package/corpus/templates/content/actions/create-inline-content-database.ts +6 -1
  74. package/corpus/templates/content/actions/delete-content-database.ts +3 -0
  75. package/corpus/templates/content/actions/delete-document.ts +7 -11
  76. package/corpus/templates/content/actions/disconnect-local-folder-source.ts +175 -0
  77. package/corpus/templates/content/actions/duplicate-database-item.ts +10 -0
  78. package/corpus/templates/content/actions/duplicate-database-items.ts +13 -0
  79. package/corpus/templates/content/actions/edit-document.ts +2 -28
  80. package/corpus/templates/content/actions/ensure-content-spaces.ts +26 -0
  81. package/corpus/templates/content/actions/export-content-source.ts +47 -9
  82. package/corpus/templates/content/actions/get-document.ts +0 -14
  83. package/corpus/templates/content/actions/import-content-source.ts +43 -3
  84. package/corpus/templates/content/actions/list-content-spaces.ts +103 -0
  85. package/corpus/templates/content/actions/list-documents.ts +12 -9
  86. package/corpus/templates/content/actions/move-document.ts +3 -14
  87. package/corpus/templates/content/actions/pull-document.ts +0 -23
  88. package/corpus/templates/content/actions/remove-local-file-source.ts +50 -29
  89. package/corpus/templates/content/actions/resolve-local-folder-conflict.ts +442 -0
  90. package/corpus/templates/content/actions/reveal-local-source-file.ts +6 -17
  91. package/corpus/templates/content/actions/search-documents.ts +0 -34
  92. package/corpus/templates/content/actions/share-local-file-document.ts +25 -1
  93. package/corpus/templates/content/actions/submit-content-database-form.ts +12 -0
  94. package/corpus/templates/content/actions/sync-local-folder-source.ts +731 -0
  95. package/corpus/templates/content/actions/sync-manifest-local-folder-source.ts +123 -0
  96. package/corpus/templates/content/actions/update-content-database-view.ts +10 -1
  97. package/corpus/templates/content/actions/update-document.ts +0 -15
  98. package/corpus/templates/content/actions/view-screen.ts +2 -50
  99. package/corpus/templates/content/app/components/editor/SlashCommandMenu.tsx +76 -62
  100. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +70 -22
  101. package/corpus/templates/content/app/components/editor/database/grouping.ts +2 -1
  102. package/corpus/templates/content/app/components/editor/database/settings.tsx +23 -20
  103. package/corpus/templates/content/app/components/editor/database/shared.tsx +2 -0
  104. package/corpus/templates/content/app/components/editor/database/sidebar.tsx +330 -0
  105. package/corpus/templates/content/app/components/editor/database/types.ts +1 -0
  106. package/corpus/templates/content/app/components/editor/database/view-config.ts +3 -1
  107. package/corpus/templates/content/app/components/layout/Layout.tsx +25 -1
  108. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +209 -113
  109. package/corpus/templates/content/app/components/sidebar/select-content-space.ts +31 -0
  110. package/corpus/templates/content/app/hooks/use-content-database.ts +16 -0
  111. package/corpus/templates/content/app/hooks/use-content-spaces.ts +39 -0
  112. package/corpus/templates/content/app/i18n/zh-TW.ts +11 -0
  113. package/corpus/templates/content/app/i18n-data.ts +75 -0
  114. package/corpus/templates/content/app/routes/_app.local-files.tsx +174 -65
  115. package/corpus/templates/content/changelog/2026-07-16-content-callouts-can-now-be-reviewed-and-published-to-builder.md +6 -0
  116. package/corpus/templates/content/changelog/2026-07-16-heading-5-and-heading-6-are-available-from-the-editor-slash-.md +6 -0
  117. package/corpus/templates/content/changelog/2026-07-16-organize-every-workspace-through-a-customizable-files-databa.md +6 -0
  118. package/corpus/templates/content/parity/matrix.md +2 -1
  119. package/corpus/templates/content/parity/matrix.ts +37 -1
  120. package/corpus/templates/content/scripts/dev-database.mjs +7 -5
  121. package/corpus/templates/content/server/db/schema.ts +113 -40
  122. package/corpus/templates/content/server/lib/document-context.ts +15 -1
  123. package/corpus/templates/content/server/lib/notion-sync.ts +6 -0
  124. package/corpus/templates/content/server/plugins/db.ts +153 -0
  125. package/corpus/templates/content/shared/api.ts +17 -3
  126. package/corpus/templates/content/shared/builder-mdx.ts +163 -16
  127. package/corpus/templates/design/.agents/skills/design-templates/SKILL.md +59 -0
  128. package/corpus/templates/design/AGENTS.md +6 -1
  129. package/corpus/templates/design/actions/create-design-from-template.ts +19 -7
  130. package/corpus/templates/design/actions/list-design-templates.ts +69 -21
  131. package/corpus/templates/design/actions/navigate.ts +2 -2
  132. package/corpus/templates/design/actions/view-screen.ts +15 -2
  133. package/corpus/templates/design/agent-native.app-skill.json +5 -0
  134. package/corpus/templates/design/app/components/editor/PromptDialog.tsx +281 -56
  135. package/corpus/templates/design/app/components/templates/TemplatePreview.tsx +14 -2
  136. package/corpus/templates/design/app/hooks/use-navigation-state.ts +3 -2
  137. package/corpus/templates/design/app/i18n/zh-TW.ts +3 -3
  138. package/corpus/templates/design/app/i18n-data.ts +21 -2
  139. package/corpus/templates/design/app/i18n-template-feature.ts +218 -10
  140. package/corpus/templates/design/app/pages/Index.tsx +195 -24
  141. package/corpus/templates/design/app/pages/Templates.tsx +144 -55
  142. package/corpus/templates/design/app/pages/design-editor/generation-prompt-directives.ts +12 -1
  143. package/corpus/templates/design/changelog/2026-07-14-templates-now-appear-in-the-new-design-picker-with-previews-.md +6 -0
  144. package/corpus/templates/design/changelog/2026-07-15-templates-now-open-immediately-after-copying-even-while-the-.md +6 -0
  145. package/corpus/templates/design/server/plugins/agent-chat.ts +1 -1
  146. package/corpus/templates/mail/AGENTS.md +23 -18
  147. package/corpus/templates/mail/actions/bulk-archive.ts +32 -28
  148. package/corpus/templates/mail/actions/mark-read.ts +131 -20
  149. package/corpus/templates/mail/actions/mark-thread-read.ts +1 -1
  150. package/corpus/templates/mail/actions/move-email.ts +30 -24
  151. package/corpus/templates/mail/actions/send-email.ts +73 -68
  152. package/corpus/templates/mail/changelog/2026-07-15-unread-inbox-cleanup-now-completes-reliably.md +6 -0
  153. package/corpus/templates/mail/server/handlers/emails.ts +218 -209
  154. package/corpus/templates/mail/server/lib/bulk-mark-read.ts +30 -0
  155. package/corpus/templates/mail/server/lib/email-state.ts +198 -113
  156. package/corpus/templates/mail/server/lib/google-auth.ts +218 -25
  157. package/corpus/templates/mail/server/lib/jobs.ts +69 -74
  158. package/corpus/templates/mail/server/lib/local-email-store.ts +265 -0
  159. package/corpus/templates/mail/server/plugins/agent-chat.ts +6 -0
  160. package/dist/cli/content-local.d.ts +1 -0
  161. package/dist/cli/content-local.d.ts.map +1 -1
  162. package/dist/cli/content-local.js +75 -17
  163. package/dist/cli/content-local.js.map +1 -1
  164. package/dist/cli/index.js +2 -2
  165. package/dist/cli/index.js.map +1 -1
  166. package/dist/cli/skills-content/content-skill.d.ts +1 -1
  167. package/dist/cli/skills-content/content-skill.d.ts.map +1 -1
  168. package/dist/cli/skills-content/content-skill.js +36 -25
  169. package/dist/cli/skills-content/content-skill.js.map +1 -1
  170. package/dist/cli/skills.d.ts.map +1 -1
  171. package/dist/cli/skills.js +47 -14
  172. package/dist/cli/skills.js.map +1 -1
  173. package/dist/collab/struct-routes.d.ts +1 -1
  174. package/dist/local-artifacts/index.d.ts +10 -0
  175. package/dist/local-artifacts/index.d.ts.map +1 -1
  176. package/dist/local-artifacts/index.js +39 -0
  177. package/dist/local-artifacts/index.js.map +1 -1
  178. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  179. package/dist/vite/client.d.ts.map +1 -1
  180. package/dist/vite/client.js +2 -0
  181. package/dist/vite/client.js.map +1 -1
  182. package/docs/content/local-file-mode.mdx +99 -489
  183. package/docs/content/locales/ar-SA/local-file-mode.mdx +35 -490
  184. package/docs/content/locales/ar-SA/template-content.mdx +10 -24
  185. package/docs/content/locales/de-DE/local-file-mode.mdx +37 -491
  186. package/docs/content/locales/de-DE/template-content.mdx +10 -24
  187. package/docs/content/locales/es-ES/local-file-mode.mdx +35 -493
  188. package/docs/content/locales/es-ES/template-content.mdx +10 -24
  189. package/docs/content/locales/fr-FR/local-file-mode.mdx +38 -491
  190. package/docs/content/locales/fr-FR/template-content.mdx +10 -24
  191. package/docs/content/locales/hi-IN/local-file-mode.mdx +33 -492
  192. package/docs/content/locales/hi-IN/template-content.mdx +10 -24
  193. package/docs/content/locales/ja-JP/local-file-mode.mdx +36 -492
  194. package/docs/content/locales/ja-JP/template-content.mdx +10 -24
  195. package/docs/content/locales/ko-KR/local-file-mode.mdx +35 -490
  196. package/docs/content/locales/ko-KR/template-content.mdx +10 -24
  197. package/docs/content/locales/pt-BR/local-file-mode.mdx +35 -493
  198. package/docs/content/locales/pt-BR/template-content.mdx +10 -24
  199. package/docs/content/locales/zh-CN/local-file-mode.mdx +31 -489
  200. package/docs/content/locales/zh-CN/template-content.mdx +9 -24
  201. package/docs/content/locales/zh-TW/local-file-mode.mdx +31 -489
  202. package/docs/content/locales/zh-TW/template-content.mdx +9 -24
  203. package/docs/content/template-content.mdx +26 -42
  204. package/package.json +1 -1
  205. package/corpus/templates/assets/app/components/library/EditLibraryDialog.tsx +0 -126
@@ -91,9 +91,8 @@ When you open the app, you'll see a page tree next to the editor. The agent alwa
91
91
  - **Organize pages in a tree** — nest infinitely, drag to reorder, favorite pages you use often.
92
92
  - **Search across everything** with full-text search across titles and content.
93
93
  - **Edit local Markdown/MDX files like Obsidian.** Use the `/local-files` view
94
- to export your workspace to files, edit them in your own tools, preview
95
- changes, and import them back. In Local File Mode, Content writes straight to
96
- the selected `.md` or `.mdx` file.
94
+ to connect a folder to a space's Files database, edit with your own tools,
95
+ sync changes, and review conflicts without leaving the database-backed app.
97
96
  - **Generate rich interactive custom blocks.** Register local React components,
98
97
  insert them as MDX, and let the agent create or update component files for
99
98
  your docs.
@@ -136,31 +135,23 @@ as the review surface. The hosted app remains the source of truth for sharing,
136
135
  comments, permissions, and live collaboration; the local folder is an explicit
137
136
  sync surface.
138
137
 
139
- Content can also run in **Local File Mode**, where files are the source of
140
- truth instead of SQL documents. Add `agent-native.json` to a repo, set
141
- `mode: "local-files"`, and configure roots such as `docs/`, `blog/`,
142
- `content/`, and `resources/`. The standard Content editor then populates its
143
- left sidebar from those local `.md`/`.mdx` files and writes edits back to the
144
- selected file through the normal document actions. Use this for repo-first docs,
145
- blogs, resource libraries, or an Obsidian-style personal MDX-powered notes vault; switch back to
146
- database mode when you want hosted collaboration and SQL-backed sharing. See [Local File Mode](/docs/local-file-mode) for the
147
- standalone repo layout, configuration, custom MDX components, local
148
- `extensions/` widgets, and production safety guide.
138
+ Local folders now use the same database model as every other page. Each page
139
+ belongs to a space and its system Files database; a folder is a source adapter
140
+ on that database. You can attach a folder to an existing space or create a
141
+ private folder-backed space, then use database properties and saved sidebar
142
+ views on the imported pages. See [Local folder sources](/docs/local-file-mode)
143
+ for configuration, truth policies, conflicts, custom MDX components, and local
144
+ `extensions/` widgets.
149
145
 
150
- To install the Content local-files skill into an existing repo:
146
+ To open Content for an existing repo:
151
147
 
152
148
  ```bash
153
- npx @agent-native/core@latest skills add content --mode local-files --scope project
149
+ npx @agent-native/core@latest content .
154
150
  ```
155
151
 
156
- The installer copies the `content` skill for your coding agent and writes or
157
- updates `agent-native.json` with Content roots for `docs/`, `blog/`, `content/`,
158
- and `resources/`. When a local Content app, Agent Native Desktop, or trusted
159
- local bridge is running, agents should use Content actions such as
160
- `list-documents`, `get-document`, `edit-document`, `update-document`, and
161
- `share-local-file-document` instead of raw filesystem writes. Without that local
162
- bridge, the installed skill still gives the agent the repo-editing contract for
163
- safe Markdown/MDX edits.
152
+ The CLI starts normal database-backed Content, registers an opaque local-folder
153
+ connection in `agent-native.json`, and opens the connection screen. It does not
154
+ set a local-mode environment variable.
164
155
 
165
156
  ## For developers
166
157
 
@@ -189,28 +180,21 @@ Open `http://localhost:8083` and create your first page. Then ask the agent to "
189
180
 
190
181
  **Search and comments.** Full-text search, anchored comments, version history, and restore flows are built into the document surface.
191
182
 
192
- **Sync surfaces.** Documents can sync with Notion or local Markdown/MDX folders, with SQL acting as the collaborative cache/history layer.
183
+ **Sync surfaces.** Documents can sync with Notion or local Markdown/MDX folders, while SQL remains the canonical collaborative data model.
193
184
 
194
185
  ### Local file sync
195
186
 
196
187
  The protected `/local-files` route uses the browser File System Access API, or a
197
- guarded native folder bridge inside Agent Native Desktop, to read and write
198
- Markdown/MDX files from a user-chosen folder. After the folder is linked and
199
- imported, the selected file is treated as the authority: opening the page reads
200
- the file, and normal editor saves write the file first. SQL is then updated as a
201
- cache/history layer for the existing document UI, search, and version panel, not
202
- as the source of truth. The top-right page menu exposes the local source path:
203
- relative path is always available, absolute path is available in true local-file
204
- mode and Agent Native Desktop, and Reveal in Finder is available through the
205
- desktop bridge or server-backed local-file mode.
206
-
207
- The bulk sync route calls:
208
-
209
- - `export-content-source` — reads the accessible document tree and returns a
210
- deterministic `content/` file bundle.
211
- - `import-content-source` — validates files, creates new private documents,
212
- updates documents where the caller has editor access, preserves version
213
- history, and rejects invalid parent cycles.
188
+ guarded native folder bridge inside Agent Native Desktop, to connect a folder to
189
+ a Files database. SQL stores only an opaque connection ID, relative paths,
190
+ hashes, and source metadata. Sync materializes ordinary database pages and
191
+ records concurrent edits as reviewable change sets instead of silently
192
+ overwriting either side. Disconnecting preserves both database pages and files.
193
+
194
+ The folder workflow calls `connect-local-folder-source`,
195
+ `sync-local-folder-source`, `resolve-local-folder-conflict`, and
196
+ `disconnect-local-folder-source`. `export-content-source` can export one source
197
+ using its original relative paths.
214
198
 
215
199
  The source format lives in `shared/content-source.ts`. Keep that file as the
216
200
  single contract for filenames, frontmatter, parsing, and serialization.
@@ -674,6 +658,6 @@ The agent can make all of these changes itself — ask it to "add a tags column
674
658
 
675
659
  - [**Templates**](/docs/cloneable-saas) — the clone-and-own model, and every other domain template to start from
676
660
  - [**Getting Started**](/docs/getting-started) — the framework fundamentals: actions, application state, and live sync
677
- - [**Local File Mode**](/docs/local-file-mode) — the standalone repo layout, config, and production safety guide
661
+ - [**Local folder sources**](/docs/local-file-mode) — spaces, Files databases, folder adapters, migration, and safety
678
662
  - [**Real-Time Collaboration**](/docs/real-time-collaboration) — the Yjs pipeline documents share with other collaborative surfaces
679
663
  - [**Sharing**](/docs/sharing) — the share-grant model documents build on
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/core",
3
- "version": "0.102.2",
3
+ "version": "0.103.1",
4
4
  "description": "Framework for agent-native application development — where AI agents and UI share SQL state, actions, and context",
5
5
  "homepage": "https://github.com/BuilderIO/agent-native#readme",
6
6
  "bugs": {
@@ -1,4 +1,5 @@
1
1
  import { spawn } from "node:child_process";
2
+ import { createHash } from "node:crypto";
2
3
  import fsSync from "node:fs";
3
4
  import fs from "node:fs/promises";
4
5
  import path from "node:path";
@@ -8,7 +9,7 @@ const CONTENT_APP_ID = "content";
8
9
  const MANIFEST_FILE = "agent-native.json";
9
10
  const DEFAULT_PORT = 8083;
10
11
  const DEFAULT_PROFILE = "docs/no-bookkeeping";
11
- const LOCAL_FILE_ID_PREFIX = "local-file:";
12
+ const LOCAL_FOLDER_CONNECTION_PREFIX = "local-folder:";
12
13
 
13
14
  export interface ContentLocalArgs {
14
15
  target?: string;
@@ -25,6 +26,7 @@ export interface ContentLocalLaunchPlan {
25
26
  manifestPath: string;
26
27
  rootPath: string;
27
28
  filePath?: string;
29
+ connectionId: string;
28
30
  url: string;
29
31
  command: string;
30
32
  args: string[];
@@ -48,10 +50,18 @@ function titleFromPath(value: string) {
48
50
  );
49
51
  }
50
52
 
51
- function encodeLocalFileId(filePath: string) {
52
- return `${LOCAL_FILE_ID_PREFIX}${Buffer.from(filePath, "utf8").toString(
53
- "base64url",
54
- )}`;
53
+ function localFolderConnectionId(absoluteRootPath: string) {
54
+ let canonicalRootPath = path.resolve(absoluteRootPath);
55
+ try {
56
+ canonicalRootPath = fsSync.realpathSync(canonicalRootPath);
57
+ } catch (error) {
58
+ if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
59
+ }
60
+ const digest = createHash("sha256")
61
+ .update(canonicalRootPath)
62
+ .digest("base64url")
63
+ .slice(0, 24);
64
+ return `${LOCAL_FOLDER_CONNECTION_PREFIX}${digest}`;
55
65
  }
56
66
 
57
67
  function isRecord(value: unknown): value is Record<string, unknown> {
@@ -134,6 +144,7 @@ function resolveTarget(target: string, cwd: string) {
134
144
 
135
145
  return {
136
146
  absoluteTarget,
147
+ absoluteRootPath: targetDirectory,
137
148
  isFile: stat.isFile(),
138
149
  workspaceRoot,
139
150
  rootPath: normalizeSlash(rootPath),
@@ -161,8 +172,13 @@ async function readManifest(
161
172
 
162
173
  function upsertContentManifest(
163
174
  manifest: Record<string, unknown>,
175
+ workspaceRoot: string,
164
176
  rootPath: string,
165
- options: { filePath?: string; profile?: string },
177
+ options: {
178
+ connectionId: string;
179
+ filePath?: string;
180
+ profile?: string;
181
+ },
166
182
  ) {
167
183
  const next: Record<string, unknown> = {
168
184
  ...manifest,
@@ -192,18 +208,62 @@ function upsertContentManifest(
192
208
  ? existingRoot.extensions
193
209
  : [".md", ".mdx"],
194
210
  ...(profile ? { profile } : {}),
195
- ...(include && rootIndex === -1 ? { include } : {}),
211
+ ...(include && (rootIndex === -1 || Array.isArray(existingRoot?.include))
212
+ ? {
213
+ include: Array.from(
214
+ new Set([
215
+ ...(Array.isArray(existingRoot?.include)
216
+ ? existingRoot.include.filter(
217
+ (value): value is string => typeof value === "string",
218
+ )
219
+ : []),
220
+ ...include,
221
+ ]),
222
+ ),
223
+ }
224
+ : {}),
225
+ source: {
226
+ ...(isRecord(existingRoot?.source) ? existingRoot.source : {}),
227
+ type: "local-folder",
228
+ connectionId: options.connectionId,
229
+ truthPolicy: "source_primary",
230
+ },
196
231
  };
197
- const roots =
232
+ let roots =
198
233
  rootIndex === -1
199
234
  ? [...existingRoots, root]
200
235
  : existingRoots.map((entry, index) =>
201
236
  index === rootIndex ? { ...entry, ...root } : entry,
202
237
  );
203
238
 
239
+ if (existingContent.mode === "local-files") {
240
+ roots = roots.map((entry) => {
241
+ if (entry === root || typeof entry.path !== "string") return entry;
242
+ const existingSource = isRecord(entry.source) ? entry.source : {};
243
+ return {
244
+ ...entry,
245
+ source: {
246
+ ...existingSource,
247
+ type: "local-folder",
248
+ connectionId:
249
+ typeof existingSource.connectionId === "string" &&
250
+ existingSource.connectionId
251
+ ? existingSource.connectionId
252
+ : localFolderConnectionId(
253
+ path.resolve(workspaceRoot, entry.path),
254
+ ),
255
+ truthPolicy:
256
+ typeof existingSource.truthPolicy === "string"
257
+ ? existingSource.truthPolicy
258
+ : "source_primary",
259
+ },
260
+ };
261
+ });
262
+ }
263
+
264
+ delete existingContent.mode;
204
265
  apps[CONTENT_APP_ID] = {
205
266
  ...existingContent,
206
- mode: "local-files",
207
267
  roots,
208
268
  hide: Array.isArray(existingContent.hide)
209
269
  ? existingContent.hide
@@ -234,9 +294,10 @@ export function findContentTemplateDir(cwd = process.cwd()) {
234
294
  );
235
295
  }
236
296
 
237
- function launchUrl(port: number, filePath?: string) {
238
- const base = `http://127.0.0.1:${port}`;
239
- return filePath ? `${base}/page/${encodeLocalFileId(filePath)}` : base;
297
+ function launchUrl(port: number, connectionId: string, filePath?: string) {
298
+ const params = new URLSearchParams({ connectionId });
299
+ if (filePath) params.set("file", filePath);
300
+ return `http://127.0.0.1:${port}/local-files?${params.toString()}`;
240
301
  }
241
302
 
242
303
  export async function prepareContentLocalLaunch(options: {
@@ -249,10 +310,13 @@ export async function prepareContentLocalLaunch(options: {
249
310
  const cwd = path.resolve(options.cwd ?? process.cwd());
250
311
  const target = resolveTarget(options.target, cwd);
251
312
  const manifestPath = path.join(target.workspaceRoot, MANIFEST_FILE);
313
+ const connectionId = localFolderConnectionId(target.absoluteRootPath);
252
314
  const manifest = upsertContentManifest(
253
315
  await readManifest(manifestPath),
316
+ target.workspaceRoot,
254
317
  target.rootPath,
255
318
  {
319
+ connectionId,
256
320
  filePath: target.filePath,
257
321
  profile: options.profile,
258
322
  },
@@ -273,7 +337,8 @@ export async function prepareContentLocalLaunch(options: {
273
337
  manifestPath,
274
338
  rootPath: target.rootPath,
275
339
  filePath: target.filePath,
276
- url: launchUrl(options.port ?? DEFAULT_PORT, target.filePath),
340
+ connectionId,
341
+ url: launchUrl(options.port ?? DEFAULT_PORT, connectionId, target.filePath),
277
342
  command: "corepack",
278
343
  args: [
279
344
  "pnpm",
@@ -287,7 +352,6 @@ export async function prepareContentLocalLaunch(options: {
287
352
  "--strictPort",
288
353
  ],
289
354
  env: {
290
- AGENT_NATIVE_MODE: "local-files",
291
355
  AGENT_NATIVE_MANIFEST_PATH: manifestPath,
292
356
  },
293
357
  manifest,
@@ -316,7 +380,7 @@ function printHelp() {
316
380
  agent-native content <file-or-folder> [options]
317
381
 
318
382
  Options:
319
- --profile <name> Local file profile, for example docs/no-bookkeeping
383
+ --profile <name> Local folder profile, for example docs/no-bookkeeping
320
384
  --port <number> Dev server port (default ${DEFAULT_PORT})
321
385
  --open Open the target in the browser (default)
322
386
  --no-open Start the server without opening the browser
@@ -326,9 +390,13 @@ Options:
326
390
 
327
391
  export async function runContentLocal(argv: string[]) {
328
392
  const parsed = parseContentLocalArgs(argv);
329
- if (parsed.help || !parsed.target) {
393
+ if (parsed.help) {
394
+ printHelp();
395
+ return 0;
396
+ }
397
+ if (!parsed.target) {
330
398
  printHelp();
331
- return parsed.target ? 0 : 1;
399
+ return 1;
332
400
  }
333
401
 
334
402
  const plan = await prepareContentLocalLaunch({
@@ -343,7 +411,7 @@ export async function runContentLocal(argv: string[]) {
343
411
  return 0;
344
412
  }
345
413
 
346
- console.log(`Content local files: ${plan.rootPath}`);
414
+ console.log(`Content folder source: ${plan.rootPath}`);
347
415
  console.log(`Manifest: ${plan.manifestPath}`);
348
416
  console.log(`Opening: ${plan.url}`);
349
417
 
@@ -1098,8 +1098,8 @@ Usage:
1098
1098
  --with-github-action also writes the PR Visual
1099
1099
  Recap workflow into .github/workflows/.
1100
1100
  agent-native content local-files <file-or-folder>
1101
- Launch Content in local-file mode for a local
1102
- docs/content folder. Use --no-open, --port N,
1101
+ Connect a local docs/content folder to normal
1102
+ database-backed Content. Use --no-open, --port N,
1103
1103
  or --profile docs/no-bookkeeping as needed.
1104
1104
  agent-native design connect Start a localhost Design bridge for a running
1105
1105
  dev server. Use --url, --port, --root, or
@@ -2,8 +2,9 @@ export const CONTENT_SKILL_MD = `---
2
2
  name: content
3
3
  description: >-
4
4
  Use Content for repo-backed Markdown/MDX docs, blogs, resources, rich
5
- document editing, local components, shareable copies, and Content local-file
6
- workspaces. Prefer Content actions over raw filesystem writes when available.
5
+ document editing, local components, shareable copies, and database-backed
6
+ local-folder sources. Prefer Content actions over raw filesystem writes when
7
+ available.
7
8
  metadata:
8
9
  visibility: exported
9
10
  ---
@@ -14,21 +15,22 @@ Use the Content app when a workflow is about authoring, editing, reviewing, or
14
15
  publishing Markdown/MDX documents: docs sites, blogs, resource libraries,
15
16
  marketing pages, internal notes, and local MDX components. Content gives the
16
17
  agent a document tree, a rich editor, normal document actions, and optional
17
- local-file source of truth.
18
+ local-folder synchronization.
18
19
 
19
20
  ## Choose The Path
20
21
 
21
22
  - Use Content actions when the Content MCP/action tools are available:
22
23
  \`list-documents\`, \`search-documents\`, \`get-document\`,
23
24
  \`pull-document\`, \`create-document\`, \`edit-document\`,
24
- \`update-document\`, \`delete-document\`, \`share-local-file-document\`,
25
+ \`update-document\`, \`delete-document\`,
26
+ \`sync-manifest-local-folder-source\`, \`sync-local-folder-source\`,
25
27
  \`list-local-component-files\`, and \`write-local-component-file\`.
26
28
  - Use \`pull-document\` or \`get-document\` before editing a page. Use
27
29
  \`edit-document\` for precise find/replace changes and \`update-document\`
28
30
  for full rewrites or new content.
29
- - In Local File Mode, Content actions read and write the repo files declared in
30
- \`agent-native.json\`; SQL remains cache/history/search glue, not the source of
31
- truth for those pages.
31
+ - Local folders declared in \`agent-native.json\` are sources for ordinary
32
+ SQL-backed Content pages. Use the trusted bridge's pull/check/push workflows to
33
+ synchronize them; normal document actions always operate on the database.
32
34
  - If Content tools are not visible and no local Content app or Desktop bridge is
33
35
  running, treat this skill as repo-editing guidance. Edit configured
34
36
  \`.md\`/\`.mdx\` files directly, preserve frontmatter and MDX imports, and tell
@@ -40,16 +42,15 @@ Prefer JSON input for action calls:
40
42
 
41
43
  \`\`\`bash
42
44
  pnpm action list-documents
43
- pnpm action get-document '{"id":"local-file:..."}'
44
- pnpm action edit-document '{"id":"local-file:...","find":"old copy","replace":"new copy"}'
45
- pnpm action update-document '{"id":"local-file:...","content":"# Updated\\n\\nBody"}'
46
- pnpm action share-local-file-document '{"id":"local-file:..."}'
45
+ pnpm action get-document '{"id":"document-id"}'
46
+ pnpm action edit-document '{"id":"document-id","find":"old copy","replace":"new copy"}'
47
+ pnpm action update-document '{"id":"document-id","content":"# Updated\\n\\nBody"}'
47
48
  \`\`\`
48
49
 
49
50
  Run \`refresh-list\` after create/update/delete operations when you need the
50
51
  open Content UI sidebar to repaint immediately.
51
52
 
52
- ## Local File Mode
53
+ ## Local Folder Sources
53
54
 
54
55
  Install into an existing repo with:
55
56
 
@@ -58,20 +59,30 @@ npx @agent-native/core@latest skills add content --mode local-files --scope proj
58
59
  \`\`\`
59
60
 
60
61
  The installer copies this skill and writes or updates \`agent-native.json\` with
61
- Content roots for \`docs/\`, \`blog/\`, \`content/\`, and \`resources/\`, plus a
62
- \`components/\` folder for local MDX components. A typical manifest looks like:
62
+ database-backed local-folder sources for \`docs/\`, \`blog/\`, \`content/\`, and
63
+ \`resources/\`, plus a \`components/\` folder for local MDX components. A trusted
64
+ local bridge imports these files into the workspace's canonical Files database.
65
+ Run \`sync-manifest-local-folder-source\` with a root's generated
66
+ \`source.connectionId\`, or launch \`agent-native content local-files <target>\`,
67
+ to connect and pull it. A typical root looks like:
63
68
 
64
69
  \`\`\`json
65
70
  {
66
71
  "version": 1,
67
72
  "apps": {
68
73
  "content": {
69
- "mode": "local-files",
70
74
  "roots": [
71
- { "name": "Docs", "path": "docs", "kind": "docs", "extensions": [".md", ".mdx"] },
72
- { "name": "Blog", "path": "blog", "kind": "blog", "extensions": [".md", ".mdx"] },
73
- { "name": "Content", "path": "content", "kind": "content", "extensions": [".md", ".mdx"] },
74
- { "name": "Resources", "path": "resources", "kind": "resources", "extensions": [".md", ".mdx"] }
75
+ {
76
+ "name": "Docs",
77
+ "path": "docs",
78
+ "kind": "docs",
79
+ "extensions": [".md", ".mdx"],
80
+ "source": {
81
+ "type": "local-folder",
82
+ "connectionId": "local-folder:<opaque-id>",
83
+ "truthPolicy": "source_primary"
84
+ }
85
+ }
75
86
  ],
76
87
  "components": "components",
77
88
  "extensions": "extensions",
@@ -81,9 +92,9 @@ Content roots for \`docs/\`, \`blog/\`, \`content/\`, and \`resources/\`, plus a
81
92
  }
82
93
  \`\`\`
83
94
 
84
- Local File Mode does not make the host language model local, and the hosted
85
- Content app cannot read private repo files by itself. File access requires a
86
- local Content app, Agent Native Desktop, or another trusted local bridge.
95
+ Local-folder synchronization does not make the host language model local, and
96
+ the hosted Content app cannot read private repo files by itself. File access
97
+ requires a local Content app, Agent Native Desktop, or another trusted bridge.
87
98
 
88
99
  ## MDX And Components
89
100
 
@@ -99,9 +110,9 @@ local Content app, Agent Native Desktop, or another trusted local bridge.
99
110
 
100
111
  ## Boundaries
101
112
 
102
- - Moving, renaming, and reordering local-file pages are not first-class Content
103
- UI operations yet. Use normal file operations when the user asks for those,
104
- then let Content rediscover the file tree.
113
+ - A database page and its source file can change independently. Use folder
114
+ check/pull/push and resolve reported conflicts rather than silently overwriting
115
+ either revision.
105
116
  - Do not push/pull Notion, Builder.io, or other provider-backed content unless
106
117
  the user explicitly asks for provider sync.
107
118
  - Do not paste secrets, private provider data, or credential-looking values into
@@ -119,7 +119,7 @@ export const BUILT_IN_APP_SKILLS = {
119
119
  id: "content",
120
120
  displayName: "Content",
121
121
  description:
122
- "Edit docs, blogs, resources, and MDX content through the Content app, including repo-backed Local File Mode.",
122
+ "Edit docs, blogs, resources, and MDX content through the Content app, including database-backed local-folder sources.",
123
123
  hosted: {
124
124
  url: "https://content.agent-native.com",
125
125
  mcpUrl: "https://content.agent-native.com/mcp",
@@ -128,7 +128,7 @@ export const BUILT_IN_APP_SKILLS = {
128
128
  auth: {
129
129
  mode: "oauth",
130
130
  setup:
131
- "Authenticate with the Content MCP connector in the host app. Local File Mode requires a local Content app, Agent Native Desktop, or trusted local bridge for filesystem access.",
131
+ "Authenticate with the Content MCP connector in the host app. Local-folder synchronization requires a local Content app, Agent Native Desktop, or trusted local bridge for filesystem access.",
132
132
  },
133
133
  surfaces: [
134
134
  {
@@ -941,15 +941,15 @@ function contentModeInstructionBlock(input: {
941
941
  if (input.mode === "local-files") {
942
942
  return `## Installed Mode
943
943
 
944
- Default storage for this installation: Content Local File Mode. This repo should
945
- have an \`agent-native.json\` file with \`apps.content.mode: "local-files"\`;
946
- the installer writes one if missing and fills in default roots for \`docs/\`,
947
- \`blog/\`, \`content/\`, and \`resources/\`. Prefer Content document actions
948
- when a local Content app,
949
- Agent Native Desktop, or another trusted local bridge exposes them. If those
950
- tools are not currently available, edit the configured Markdown/MDX files and
951
- local components directly, preserving frontmatter, imports, JSX, and unknown MDX
952
- syntax. The hosted Content app cannot read private repo files by itself.`;
944
+ Default storage for this installation is Content's SQL database. This repo's
945
+ \`agent-native.json\` declares \`docs/\`, \`blog/\`, \`content/\`, and
946
+ \`resources/\` as local-folder sources with opaque connection ids; it does not
947
+ select a separate application mode. A trusted local Content app or Agent Native
948
+ Desktop bridge imports those files into their workspace's canonical Files
949
+ database, after which normal Content document actions read and edit the SQL-backed
950
+ pages. Use \`sync-manifest-local-folder-source\` with each root's generated
951
+ connection id, or launch \`agent-native content local-files <target>\`, to connect
952
+ and pull it. The hosted Content app cannot read private repo files by itself.`;
953
953
  }
954
954
  if (input.mode === "self-hosted") {
955
955
  return `## Installed Mode
@@ -1139,8 +1139,23 @@ function shouldWriteContentLocalFilesManifest(
1139
1139
  return targetId === "content" && mode === "local-files";
1140
1140
  }
1141
1141
 
1142
+ function contentLocalFolderConnectionId(baseDir: string, rootPath: string) {
1143
+ const absoluteRootPath = path.resolve(baseDir, rootPath);
1144
+ let canonicalRootPath = absoluteRootPath;
1145
+ try {
1146
+ canonicalRootPath = fs.realpathSync(absoluteRootPath);
1147
+ } catch (error: any) {
1148
+ if (error?.code !== "ENOENT") throw error;
1149
+ }
1150
+ return `local-folder:${createHash("sha256")
1151
+ .update(canonicalRootPath)
1152
+ .digest("base64url")
1153
+ .slice(0, 24)}`;
1154
+ }
1155
+
1142
1156
  function mergeContentLocalFilesManifest(
1143
1157
  existing: unknown,
1158
+ baseDir: string,
1144
1159
  ): Record<string, unknown> {
1145
1160
  const manifest = isJsonRecord(existing) ? { ...existing } : {};
1146
1161
  if (manifest.version === undefined) manifest.version = 1;
@@ -1148,10 +1163,29 @@ function mergeContentLocalFilesManifest(
1148
1163
  const apps = isJsonRecord(manifest.apps) ? { ...manifest.apps } : {};
1149
1164
  const contentApp = isJsonRecord(apps.content) ? { ...apps.content } : {};
1150
1165
  const defaults = defaultContentLocalFilesAppConfig();
1151
- contentApp.mode = "local-files";
1152
1166
  if (!Array.isArray(contentApp.roots) || contentApp.roots.length === 0) {
1153
1167
  contentApp.roots = defaults.roots;
1154
1168
  }
1169
+ contentApp.roots = contentApp.roots.map((root: unknown) => {
1170
+ if (!isJsonRecord(root) || typeof root.path !== "string") return root;
1171
+ const source = isJsonRecord(root.source) ? root.source : {};
1172
+ return {
1173
+ ...root,
1174
+ source: {
1175
+ ...source,
1176
+ type: "local-folder",
1177
+ connectionId:
1178
+ typeof source.connectionId === "string" && source.connectionId
1179
+ ? source.connectionId
1180
+ : contentLocalFolderConnectionId(baseDir, root.path),
1181
+ truthPolicy:
1182
+ typeof source.truthPolicy === "string"
1183
+ ? source.truthPolicy
1184
+ : "source_primary",
1185
+ },
1186
+ };
1187
+ });
1188
+ delete contentApp.mode;
1155
1189
  if (contentApp.components === undefined) {
1156
1190
  contentApp.components = defaults.components;
1157
1191
  }
@@ -1181,7 +1215,7 @@ function writeContentLocalFilesManifest(
1181
1215
  );
1182
1216
  }
1183
1217
  }
1184
- const manifest = mergeContentLocalFilesManifest(existing);
1218
+ const manifest = mergeContentLocalFilesManifest(existing, baseDir);
1185
1219
  if (!options.dryRun) {
1186
1220
  fs.writeFileSync(
1187
1221
  manifestPath,