@geminilight/mindos 0.6.22 → 0.6.25

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 (62) hide show
  1. package/README.md +58 -46
  2. package/README_zh.md +58 -46
  3. package/app/app/.well-known/agent-card.json/route.ts +34 -0
  4. package/app/app/api/a2a/route.ts +100 -0
  5. package/app/app/api/file/import/route.ts +0 -2
  6. package/app/app/api/setup/route.ts +2 -0
  7. package/app/components/Backlinks.tsx +2 -2
  8. package/app/components/Breadcrumb.tsx +1 -1
  9. package/app/components/CsvView.tsx +41 -19
  10. package/app/components/DirView.tsx +2 -2
  11. package/app/components/FileTree.tsx +14 -1
  12. package/app/components/GuideCard.tsx +6 -2
  13. package/app/components/HomeContent.tsx +2 -2
  14. package/app/components/RightAskPanel.tsx +17 -10
  15. package/app/components/SearchModal.tsx +3 -3
  16. package/app/components/SidebarLayout.tsx +4 -2
  17. package/app/components/SyncStatusBar.tsx +2 -2
  18. package/app/components/ask/AskContent.tsx +6 -6
  19. package/app/components/ask/FileChip.tsx +1 -1
  20. package/app/components/ask/MentionPopover.tsx +2 -2
  21. package/app/components/ask/MessageList.tsx +1 -1
  22. package/app/components/ask/SlashCommandPopover.tsx +1 -1
  23. package/app/components/explore/UseCaseCard.tsx +2 -2
  24. package/app/components/help/HelpContent.tsx +6 -1
  25. package/app/components/panels/AgentsPanelAgentDetail.tsx +2 -2
  26. package/app/components/panels/DiscoverPanel.tsx +3 -3
  27. package/app/components/panels/PanelNavRow.tsx +2 -2
  28. package/app/components/panels/PluginsPanel.tsx +1 -1
  29. package/app/components/panels/SearchPanel.tsx +3 -3
  30. package/app/components/renderers/summary/SummaryRenderer.tsx +1 -1
  31. package/app/components/settings/AiTab.tsx +4 -4
  32. package/app/components/settings/KnowledgeTab.tsx +1 -1
  33. package/app/components/settings/McpTab.tsx +22 -4
  34. package/app/components/settings/UpdateTab.tsx +1 -1
  35. package/app/components/setup/index.tsx +9 -3
  36. package/app/components/walkthrough/WalkthroughProvider.tsx +2 -2
  37. package/app/hooks/useAskPanel.ts +7 -3
  38. package/app/hooks/useFileImport.ts +1 -1
  39. package/app/lib/a2a/agent-card.ts +107 -0
  40. package/app/lib/a2a/index.ts +23 -0
  41. package/app/lib/a2a/task-handler.ts +228 -0
  42. package/app/lib/a2a/types.ts +158 -0
  43. package/app/lib/agent/tools.ts +1 -1
  44. package/app/lib/core/fs-ops.ts +3 -2
  45. package/app/lib/fs.ts +28 -11
  46. package/app/lib/i18n-en.ts +2 -0
  47. package/app/lib/i18n-zh.ts +2 -0
  48. package/app/lib/settings.ts +1 -1
  49. package/bin/cli.js +48 -20
  50. package/bin/commands/agent.js +18 -0
  51. package/bin/commands/api.js +58 -0
  52. package/bin/commands/ask.js +101 -0
  53. package/bin/commands/file.js +286 -0
  54. package/bin/commands/search.js +51 -0
  55. package/bin/commands/space.js +167 -0
  56. package/bin/commands/status.js +69 -0
  57. package/bin/lib/command.js +156 -0
  58. package/mcp/dist/index.cjs +1 -1
  59. package/mcp/src/index.ts +1 -1
  60. package/package.json +1 -1
  61. package/skills/mindos/SKILL.md +2 -2
  62. package/skills/mindos-zh/SKILL.md +2 -2
package/README.md CHANGED
@@ -15,7 +15,6 @@
15
15
  <p align="center">
16
16
  <a href="https://tianfuwang.tech/MindOS"><img src="https://img.shields.io/badge/Website-MindOS-0ea5e9.svg?style=for-the-badge" alt="Website"></a>
17
17
  <a href="https://www.npmjs.com/package/@geminilight/mindos"><img src="https://img.shields.io/npm/v/@geminilight/mindos.svg?style=for-the-badge&color=f59e0b" alt="npm version"></a>
18
- <a href="https://www.npmjs.com/package/@geminilight/mindos"><img src="https://img.shields.io/npm/dw/@geminilight/mindos.svg?style=for-the-badge&color=10b981" alt="npm downloads"></a>
19
18
  <a href="#wechat"><img src="https://img.shields.io/badge/WeChat-Group-07C160.svg?style=for-the-badge&logo=wechat&logoColor=white" alt="WeChat"></a>
20
19
  <a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-6366f1.svg?style=for-the-badge" alt="MIT License"></a>
21
20
  </p>
@@ -32,6 +31,25 @@ MindOS is where you think, and where your AI agents act — a local-first knowle
32
31
  </picture>
33
32
  </p>
34
33
 
34
+ <table>
35
+ <tr>
36
+ <td width="50%"><img src="assets/images/mindos-home.png" alt="MindOS Home" /></td>
37
+ <td width="50%"><img src="assets/images/mindos-chat.png" alt="MindOS AI Chat" /></td>
38
+ </tr>
39
+ <tr>
40
+ <td align="center"><em>Home — Knowledge base overview</em></td>
41
+ <td align="center"><em>AI Chat — Converse with your knowledge in context</em></td>
42
+ </tr>
43
+ <tr>
44
+ <td width="50%"><img src="assets/images/mindos-dashboard.png" alt="MindOS Agents Dashboard" /></td>
45
+ <td width="50%"><img src="assets/images/mindos-echo.png" alt="MindOS Echo" /></td>
46
+ </tr>
47
+ <tr>
48
+ <td align="center"><em>Agents — Manage all connected AI agents</em></td>
49
+ <td align="center"><em>Echo — Reflect and distill cognitive growth</em></td>
50
+ </tr>
51
+ </table>
52
+
35
53
  > [!IMPORTANT]
36
54
  > **⭐ One-click install:** Send this to your Agent (Claude Code, Cursor, etc.) to set up everything automatically:
37
55
  > ```
@@ -67,37 +85,6 @@ You express preferences but the next chat starts from zero, leaving your thinkin
67
85
 
68
86
  > **Foundation:** Local-first by default — all data stays in local plain text for privacy, ownership, and speed.
69
87
 
70
- ## ✨ Features
71
-
72
- **For Humans**
73
-
74
- - **GUI Workbench**: browse, edit, search notes with unified search + AI entry (`⌘K` / `⌘/`), designed for human-AI co-creation.
75
- - **Built-in Agent Assistant**: converse with the knowledge base in context; edits seamlessly capture human-curated knowledge.
76
- - **Plugin Extensions**: multiple built-in renderer plugins — TODO Board, CSV Views, Wiki Graph, Timeline, Agent Inspector, and more.
77
-
78
- **For Agents**
79
-
80
- - **MCP Server + Skills**: stdio + HTTP dual transport, full-lineup Agent compatible (OpenClaw, Claude Code, Cursor, etc.). Zero-config access.
81
- - **Structured Templates**: pre-set directory structures for Profiles, Workflows, Configurations, etc., to jumpstart personal context.
82
- - **Agent-Ready Docs**: everyday notes naturally double as high-quality executable Agent commands — no format conversion needed, write and dispatch.
83
-
84
- **Infrastructure**
85
-
86
- - **Security**: Bearer Token auth, path sandboxing, INSTRUCTION.md write-protection, atomic writes.
87
- - **Knowledge Graph**: dynamically parses and visualizes inter-file references and dependencies.
88
- - **Backlinks View**: displays all files that reference the current file, helping you understand how a note fits into the knowledge network.
89
- - **Git Time Machine**: Git auto-sync (commit/push/pull), records every edit by both humans and Agents. One-click rollback, cross-device sync.
90
- - **Desktop App**: native macOS/Windows/Linux app with system tray, auto-start, and local process management.
91
-
92
- <details>
93
- <summary><strong>Coming Soon</strong></summary>
94
-
95
- - [ ] ACP (Agent Communication Protocol): connect external Agents (e.g., Claude Code, Cursor) and turn the knowledge base into a multi-Agent collaboration hub
96
- - [ ] Deep RAG integration: retrieval-augmented generation grounded in your knowledge base for more accurate, context-aware AI responses
97
- - [ ] Agent Inspector: render Agent operation logs as a filterable timeline to audit every tool call in detail
98
-
99
- </details>
100
-
101
88
  ---
102
89
 
103
90
  ## 🚀 Getting Started
@@ -150,9 +137,6 @@ mindos open
150
137
  2. Upload your resume or any personal/project material.
151
138
  3. Send this prompt: `Help me sync this information into my MindOS knowledge base.`
152
139
 
153
- <p align="center">
154
- <img src="assets/images/gui-sync-cv.png" alt="Sync CV Example" width="800" />
155
- </p>
156
140
 
157
141
  ### 4. Make Any Agent Ready (MCP + Skills)
158
142
 
@@ -172,6 +156,37 @@ npx skills add https://github.com/GeminiLight/MindOS --skill mindos-zh -g -y #
172
156
 
173
157
  > For remote access, manual JSON config, and common pitfalls, see **[docs/en/supported-agents.md](docs/en/supported-agents.md)**.
174
158
 
159
+ ## ✨ Features
160
+
161
+ **For Humans**
162
+
163
+ - **GUI Workbench**: browse, edit, search notes with unified search + AI entry (`⌘K` / `⌘/`), designed for human-AI co-creation.
164
+ - **Built-in Agent Assistant**: converse with the knowledge base in context; edits seamlessly capture human-curated knowledge.
165
+ - **Plugin Extensions**: multiple built-in renderer plugins — TODO Board, CSV Views, Wiki Graph, Timeline, Agent Inspector, and more.
166
+
167
+ **For Agents**
168
+
169
+ - **MCP Server + Skills**: stdio + HTTP dual transport, full-lineup Agent compatible (OpenClaw, Claude Code, Cursor, etc.). Zero-config access.
170
+ - **Structured Templates**: pre-set directory structures for Profiles, Workflows, Configurations, etc., to jumpstart personal context.
171
+ - **Agent-Ready Docs**: everyday notes naturally double as high-quality executable Agent commands — no format conversion needed, write and dispatch.
172
+
173
+ **Infrastructure**
174
+
175
+ - **Security**: Bearer Token auth, path sandboxing, INSTRUCTION.md write-protection, atomic writes.
176
+ - **Knowledge Graph**: dynamically parses and visualizes inter-file references and dependencies.
177
+ - **Backlinks View**: displays all files that reference the current file, helping you understand how a note fits into the knowledge network.
178
+ - **Git Time Machine**: Git auto-sync (commit/push/pull), records every edit by both humans and Agents. One-click rollback, cross-device sync.
179
+ - **Desktop App**: native macOS/Windows/Linux app with system tray, auto-start, and local process management.
180
+
181
+ <details>
182
+ <summary><strong>Coming Soon</strong></summary>
183
+
184
+ - [ ] ACP (Agent Communication Protocol): connect external Agents (e.g., Claude Code, Cursor) and turn the knowledge base into a multi-Agent collaboration hub
185
+ - [ ] Deep RAG integration: retrieval-augmented generation grounded in your knowledge base for more accurate, context-aware AI responses
186
+ - [ ] Agent Inspector: render Agent operation logs as a filterable timeline to audit every tool call in detail
187
+
188
+ </details>
189
+
175
190
  ## ⚙️ How It Works
176
191
 
177
192
  ```mermaid
@@ -203,22 +218,21 @@ graph LR
203
218
 
204
219
  | Agent | MCP | Skills |
205
220
  |:------|:---:|:------:|
206
- | MindOS Agent | ✅ | ✅ |
207
221
  | OpenClaw | ✅ | ✅ |
208
- | Claude Desktop / Code | ✅ | ✅ |
209
- | CodeBuddy | ✅ | ✅ |
222
+ | Claude Code | ✅ | ✅ |
210
223
  | Cursor | ✅ | ✅ |
211
- | Windsurf | ✅ | ✅ |
212
- | Cline | ✅ | ✅ |
213
- | Trae | ✅ | ✅ |
224
+ | Codex | ✅ | ✅ |
214
225
  | Gemini CLI | ✅ | ✅ |
215
226
  | GitHub Copilot | ✅ | ✅ |
216
- | iFlow | ✅ | ✅ |
227
+ | Trae | ✅ | ✅ |
228
+ | CodeBuddy | ✅ | ✅ |
229
+ | Qoder | ✅ | ✅ |
230
+ | Cline | ✅ | ✅ |
231
+ | Windsurf | ✅ | ✅ |
217
232
 
218
233
  ---
219
234
 
220
- <details>
221
- <summary><strong>📁 Project Structure</strong></summary>
235
+ ## 📁 Project Structure
222
236
 
223
237
  ```bash
224
238
  MindOS/
@@ -236,8 +250,6 @@ MindOS/
236
250
  └── mind/ # Your private knowledge base (default: ~/MindOS/mind, customizable on onboard)
237
251
  ```
238
252
 
239
- </details>
240
-
241
253
  ## ⌨️ CLI Commands
242
254
 
243
255
  > Full command reference: **[docs/en/cli-commands.md](docs/en/cli-commands.md)**
package/README_zh.md CHANGED
@@ -15,7 +15,6 @@
15
15
  <p align="center">
16
16
  <a href="https://tianfuwang.tech/MindOS"><img src="https://img.shields.io/badge/Website-MindOS-0ea5e9.svg?style=for-the-badge" alt="Website"></a>
17
17
  <a href="https://www.npmjs.com/package/@geminilight/mindos"><img src="https://img.shields.io/npm/v/@geminilight/mindos.svg?style=for-the-badge&color=f59e0b" alt="npm version"></a>
18
- <a href="https://www.npmjs.com/package/@geminilight/mindos"><img src="https://img.shields.io/npm/dw/@geminilight/mindos.svg?style=for-the-badge&color=10b981" alt="npm downloads"></a>
19
18
  <a href="#wechat"><img src="https://img.shields.io/badge/WeChat-群聊-07C160.svg?style=for-the-badge&logo=wechat&logoColor=white" alt="WeChat"></a>
20
19
  <a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-6366f1.svg?style=for-the-badge" alt="MIT License"></a>
21
20
  </p>
@@ -32,6 +31,25 @@ MindOS 是你思考的地方,也是 AI Agent 行动的起点——一个你和
32
31
  </picture>
33
32
  </p>
34
33
 
34
+ <table>
35
+ <tr>
36
+ <td width="50%"><img src="assets/images/mindos-home.png" alt="MindOS 首页" /></td>
37
+ <td width="50%"><img src="assets/images/mindos-chat.png" alt="MindOS AI 对话" /></td>
38
+ </tr>
39
+ <tr>
40
+ <td align="center"><em>首页 — 知识库概览</em></td>
41
+ <td align="center"><em>AI 对话 — 在上下文中与知识库对话</em></td>
42
+ </tr>
43
+ <tr>
44
+ <td width="50%"><img src="assets/images/mindos-dashboard.png" alt="MindOS Agent 工作台" /></td>
45
+ <td width="50%"><img src="assets/images/mindos-echo.png" alt="MindOS Echo" /></td>
46
+ </tr>
47
+ <tr>
48
+ <td align="center"><em>Agent 工作台 — 管理所有已连接的 AI Agent</em></td>
49
+ <td align="center"><em>Echo — 复盘与认知沉淀</em></td>
50
+ </tr>
51
+ </table>
52
+
35
53
  > [!IMPORTANT]
36
54
  > **⭐ 一键安装:** 把这句话发给你的 Agent(Claude Code、Cursor 等),自动完成全部安装:
37
55
  > ```
@@ -67,37 +85,6 @@ Agent 记忆锁在黑箱中,推理无法审查,错误极难纠正且幻觉
67
85
 
68
86
  > **底层原则:** 默认本地优先,全部数据以本地纯文本保存,兼顾隐私、主权与性能。
69
87
 
70
- ## ✨ 功能特性
71
-
72
- **人类侧**
73
-
74
- - **GUI 工作台**:浏览、编辑、搜索笔记,统一搜索 + AI 入口(`⌘K` / `⌘/`),专为人机共创设计。
75
- - **内置 Agent 助手**:在上下文中与知识库对话,编辑无缝沉淀为可管理知识。
76
- - **插件扩展**:多种内置渲染器插件——TODO Board、CSV Views、Wiki Graph、Timeline、Agent Inspector 等。
77
-
78
- **Agent 侧**
79
-
80
- - **MCP Server + Skills**:stdio + HTTP 双传输,全阵容 Agent 兼容(OpenClaw, Claude Code, Cursor 等),零配置接入。
81
- - **结构化模板**:预置 Profile、Workflows、Configurations 等目录骨架,快速冷启动个人 Context。
82
- - **笔记即指令**:日常笔记天然就是 Agent 可直接执行的高质量指令——无需额外格式转换,写下即可调度。
83
-
84
- **基础设施**
85
-
86
- - **安全防线**:Bearer Token 认证、路径沙箱、INSTRUCTION.md 写保护、原子写入。
87
- - **知识图谱**:动态解析并可视化文件间的引用与依赖关系。
88
- - **反向链接视图**:展示所有引用当前文件的反向链接,理解笔记在知识网络中的位置。
89
- - **Git 时光机**:Git 自动同步(commit/push/pull),记录人类与 Agent 的每次编辑历史,一键回滚,跨设备同步。
90
- - **桌面客户端**:原生 macOS/Windows/Linux 应用,系统托盘、开机自启、本地进程管理。
91
-
92
- <details>
93
- <summary><strong>即将到来</strong></summary>
94
-
95
- - [ ] ACP(Agent Communication Protocol):连接外部 Agent(如 Claude Code、Cursor),让知识库成为多 Agent 协作的中枢
96
- - [ ] RAG 深度集成:基于知识库内容的检索增强生成,让 AI 回答更精准、更有上下文
97
- - [ ] Agent 审计面板(Agent Inspector):将 Agent 操作日志渲染为可筛选的时间线,审查每次工具调用的详情
98
-
99
- </details>
100
-
101
88
  ---
102
89
 
103
90
  ## 🚀 快速开始
@@ -150,9 +137,6 @@ mindos open
150
137
  2. 上传你的简历或任意个人/项目资料。
151
138
  3. 发送指令:`帮我把这些信息同步到我的 MindOS 知识库。`
152
139
 
153
- <p align="center">
154
- <img src="assets/images/gui-sync-cv.png" alt="同步简历示例" width="800" />
155
- </p>
156
140
 
157
141
  ### 4. 让任意 Agent 可用(MCP + Skills)
158
142
 
@@ -172,6 +156,37 @@ npx skills add https://github.com/GeminiLight/MindOS --skill mindos-zh -g -y #
172
156
 
173
157
  > 远程配置、手动 JSON 片段、常见误区等详见 **[docs/zh/supported-agents.md](docs/zh/supported-agents.md)**。
174
158
 
159
+ ## ✨ 功能特性
160
+
161
+ **人类侧**
162
+
163
+ - **GUI 工作台**:浏览、编辑、搜索笔记,统一搜索 + AI 入口(`⌘K` / `⌘/`),专为人机共创设计。
164
+ - **内置 Agent 助手**:在上下文中与知识库对话,编辑无缝沉淀为可管理知识。
165
+ - **插件扩展**:多种内置渲染器插件——TODO Board、CSV Views、Wiki Graph、Timeline、Agent Inspector 等。
166
+
167
+ **Agent 侧**
168
+
169
+ - **MCP Server + Skills**:stdio + HTTP 双传输,全阵容 Agent 兼容(OpenClaw, Claude Code, Cursor 等),零配置接入。
170
+ - **结构化模板**:预置 Profile、Workflows、Configurations 等目录骨架,快速冷启动个人 Context。
171
+ - **笔记即指令**:日常笔记天然就是 Agent 可直接执行的高质量指令——无需额外格式转换,写下即可调度。
172
+
173
+ **基础设施**
174
+
175
+ - **安全防线**:Bearer Token 认证、路径沙箱、INSTRUCTION.md 写保护、原子写入。
176
+ - **知识图谱**:动态解析并可视化文件间的引用与依赖关系。
177
+ - **反向链接视图**:展示所有引用当前文件的反向链接,理解笔记在知识网络中的位置。
178
+ - **Git 时光机**:Git 自动同步(commit/push/pull),记录人类与 Agent 的每次编辑历史,一键回滚,跨设备同步。
179
+ - **桌面客户端**:原生 macOS/Windows/Linux 应用,系统托盘、开机自启、本地进程管理。
180
+
181
+ <details>
182
+ <summary><strong>即将到来</strong></summary>
183
+
184
+ - [ ] ACP(Agent Communication Protocol):连接外部 Agent(如 Claude Code、Cursor),让知识库成为多 Agent 协作的中枢
185
+ - [ ] RAG 深度集成:基于知识库内容的检索增强生成,让 AI 回答更精准、更有上下文
186
+ - [ ] Agent 审计面板(Agent Inspector):将 Agent 操作日志渲染为可筛选的时间线,审查每次工具调用的详情
187
+
188
+ </details>
189
+
175
190
  ## ⚙️ 运作机制
176
191
 
177
192
  ```mermaid
@@ -203,22 +218,21 @@ graph LR
203
218
 
204
219
  | Agent | MCP | Skills |
205
220
  |:------|:---:|:------:|
206
- | MindOS Agent | ✅ | ✅ |
207
221
  | OpenClaw | ✅ | ✅ |
208
- | Claude Desktop / Code | ✅ | ✅ |
209
- | CodeBuddy | ✅ | ✅ |
222
+ | Claude Code | ✅ | ✅ |
210
223
  | Cursor | ✅ | ✅ |
211
- | Windsurf | ✅ | ✅ |
212
- | Cline | ✅ | ✅ |
213
- | Trae | ✅ | ✅ |
224
+ | Codex | ✅ | ✅ |
214
225
  | Gemini CLI | ✅ | ✅ |
215
226
  | GitHub Copilot | ✅ | ✅ |
216
- | iFlow | ✅ | ✅ |
227
+ | Trae | ✅ | ✅ |
228
+ | CodeBuddy | ✅ | ✅ |
229
+ | Qoder | ✅ | ✅ |
230
+ | Cline | ✅ | ✅ |
231
+ | Windsurf | ✅ | ✅ |
217
232
 
218
233
  ---
219
234
 
220
- <details>
221
- <summary><strong>📁 项目架构</strong></summary>
235
+ ## 📁 项目架构
222
236
 
223
237
  ```bash
224
238
  MindOS/
@@ -236,8 +250,6 @@ MindOS/
236
250
  └── my-mind/ # 你的私有知识库(默认路径,onboard 时可自定义)
237
251
  ```
238
252
 
239
- </details>
240
-
241
253
  ## ⌨️ CLI 命令
242
254
 
243
255
  > 完整命令参考:**[docs/zh/cli-commands.md](docs/zh/cli-commands.md)**
@@ -0,0 +1,34 @@
1
+ export const dynamic = 'force-dynamic';
2
+
3
+ import { NextRequest, NextResponse } from 'next/server';
4
+ import { buildAgentCard } from '@/lib/a2a/agent-card';
5
+
6
+ const CORS_HEADERS: Record<string, string> = {
7
+ 'Access-Control-Allow-Origin': '*',
8
+ 'Access-Control-Allow-Methods': 'GET, OPTIONS',
9
+ 'Access-Control-Allow-Headers': 'Content-Type, Authorization',
10
+ 'Cache-Control': 'public, max-age=300',
11
+ };
12
+
13
+ export async function GET(req: NextRequest) {
14
+ // Prefer explicit config; fall back to request headers
15
+ const configuredUrl = process.env.MINDOS_BASE_URL;
16
+ let baseUrl: string;
17
+ if (configuredUrl) {
18
+ baseUrl = configuredUrl.replace(/\/+$/, '');
19
+ } else {
20
+ const proto = req.headers.get('x-forwarded-proto') ?? 'http';
21
+ const host = req.headers.get('host') ?? `localhost:${process.env.PORT || 3456}`;
22
+ baseUrl = `${proto}://${host}`;
23
+ }
24
+
25
+ const card = buildAgentCard(baseUrl);
26
+
27
+ const res = NextResponse.json(card);
28
+ for (const [k, v] of Object.entries(CORS_HEADERS)) res.headers.set(k, v);
29
+ return res;
30
+ }
31
+
32
+ export async function OPTIONS() {
33
+ return new Response(null, { status: 204, headers: CORS_HEADERS });
34
+ }
@@ -0,0 +1,100 @@
1
+ export const dynamic = 'force-dynamic';
2
+
3
+ import { NextRequest, NextResponse } from 'next/server';
4
+ import type { JsonRpcRequest, JsonRpcResponse, SendMessageParams, GetTaskParams, CancelTaskParams } from '@/lib/a2a/types';
5
+ import { A2A_ERRORS } from '@/lib/a2a/types';
6
+ import { handleSendMessage, handleGetTask, handleCancelTask } from '@/lib/a2a/task-handler';
7
+
8
+ const CORS_HEADERS: Record<string, string> = {
9
+ 'Access-Control-Allow-Origin': '*',
10
+ 'Access-Control-Allow-Methods': 'POST, OPTIONS',
11
+ 'Access-Control-Allow-Headers': 'Content-Type, Authorization, A2A-Version',
12
+ };
13
+
14
+ function jsonRpcOk(id: string | number | null, result: unknown): JsonRpcResponse {
15
+ return { jsonrpc: '2.0', id, result };
16
+ }
17
+
18
+ function jsonRpcError(id: string | number | null, error: { code: number; message: string; data?: unknown }): JsonRpcResponse {
19
+ return { jsonrpc: '2.0', id, error };
20
+ }
21
+
22
+ function respond(body: JsonRpcResponse, status = 200) {
23
+ const res = NextResponse.json(body, { status });
24
+ for (const [k, v] of Object.entries(CORS_HEADERS)) res.headers.set(k, v);
25
+ return res;
26
+ }
27
+
28
+ const MAX_REQUEST_BYTES = 100_000; // 100KB max request body
29
+
30
+ export async function POST(req: NextRequest) {
31
+ // Check content length to prevent OOM from oversized payloads
32
+ const contentLength = Number(req.headers.get('content-length') || 0);
33
+ if (contentLength > MAX_REQUEST_BYTES) {
34
+ return respond(jsonRpcError(null, { code: -32600, message: `Request too large (max ${MAX_REQUEST_BYTES} bytes)` }), 413);
35
+ }
36
+
37
+ // Parse JSON-RPC request
38
+ let rpc: JsonRpcRequest;
39
+ try {
40
+ rpc = await req.json();
41
+ } catch {
42
+ return respond(jsonRpcError(null, A2A_ERRORS.PARSE_ERROR), 400);
43
+ }
44
+
45
+ if (rpc.jsonrpc !== '2.0' || typeof rpc.method !== 'string') {
46
+ return respond(jsonRpcError(rpc.id ?? null, A2A_ERRORS.INVALID_REQUEST), 400);
47
+ }
48
+
49
+ try {
50
+ switch (rpc.method) {
51
+ case 'SendMessage': {
52
+ const params = rpc.params as unknown as SendMessageParams;
53
+ if (!params?.message?.parts?.length) {
54
+ return respond(jsonRpcError(rpc.id, A2A_ERRORS.INVALID_PARAMS));
55
+ }
56
+ const task = await handleSendMessage(params);
57
+ return respond(jsonRpcOk(rpc.id, task));
58
+ }
59
+
60
+ case 'GetTask': {
61
+ const params = rpc.params as unknown as GetTaskParams;
62
+ if (!params?.id) {
63
+ return respond(jsonRpcError(rpc.id, A2A_ERRORS.INVALID_PARAMS));
64
+ }
65
+ const task = handleGetTask(params);
66
+ if (!task) {
67
+ return respond(jsonRpcError(rpc.id, A2A_ERRORS.TASK_NOT_FOUND));
68
+ }
69
+ return respond(jsonRpcOk(rpc.id, task));
70
+ }
71
+
72
+ case 'CancelTask': {
73
+ const params = rpc.params as unknown as CancelTaskParams;
74
+ if (!params?.id) {
75
+ return respond(jsonRpcError(rpc.id, A2A_ERRORS.INVALID_PARAMS));
76
+ }
77
+ const task = handleCancelTask(params);
78
+ if (!task) {
79
+ return respond(jsonRpcError(rpc.id, {
80
+ ...A2A_ERRORS.TASK_NOT_FOUND,
81
+ message: 'Task not found or not cancelable',
82
+ }));
83
+ }
84
+ return respond(jsonRpcOk(rpc.id, task));
85
+ }
86
+
87
+ default:
88
+ return respond(jsonRpcError(rpc.id, A2A_ERRORS.METHOD_NOT_FOUND));
89
+ }
90
+ } catch (err) {
91
+ return respond(jsonRpcError(rpc.id, {
92
+ ...A2A_ERRORS.INTERNAL_ERROR,
93
+ data: (err as Error).message,
94
+ }), 500);
95
+ }
96
+ }
97
+
98
+ export async function OPTIONS() {
99
+ return new Response(null, { status: 204, headers: CORS_HEADERS });
100
+ }
@@ -7,7 +7,6 @@ import { NextRequest, NextResponse } from 'next/server';
7
7
  import { revalidatePath } from 'next/cache';
8
8
  import { sanitizeFileName, convertToMarkdown } from '@/lib/core/file-convert';
9
9
  import { resolveSafe } from '@/lib/core/security';
10
- import { scaffoldIfNewSpace } from '@/lib/core/space-scaffold';
11
10
  import { organizeAfterImport } from '@/lib/core/organize';
12
11
  import { invalidateSearchIndex } from '@/lib/core/search';
13
12
  import { effectiveSopRoot } from '@/lib/settings';
@@ -157,7 +156,6 @@ export async function POST(req: NextRequest) {
157
156
 
158
157
  fs.mkdirSync(path.dirname(resolved), { recursive: true });
159
158
  fs.writeFileSync(resolved, convertResult.content, 'utf-8');
160
- scaffoldIfNewSpace(mindRoot, finalRel);
161
159
 
162
160
  created.push({ original: entry.name, path: finalRel });
163
161
  createdPaths.push(finalRel);
@@ -172,6 +172,8 @@ export async function PATCH(req: NextRequest) {
172
172
  if (typeof patch.askedAI === 'boolean') updated.askedAI = patch.askedAI;
173
173
  if (typeof patch.nextStepIndex === 'number' && patch.nextStepIndex >= 0) updated.nextStepIndex = patch.nextStepIndex;
174
174
  if (typeof patch.active === 'boolean') updated.active = patch.active;
175
+ if (typeof patch.walkthroughStep === 'number' && patch.walkthroughStep >= 0) updated.walkthroughStep = patch.walkthroughStep;
176
+ if (typeof patch.walkthroughDismissed === 'boolean') updated.walkthroughDismissed = patch.walkthroughDismissed;
175
177
 
176
178
  writeSettings({ ...current, guideState: updated });
177
179
  return NextResponse.json({ ok: true, guideState: updated });
@@ -58,10 +58,10 @@ export default function Backlinks({ filePath }: { filePath: string }) {
58
58
  <FileText size={14} className="text-muted-foreground group-hover:text-[var(--amber)]" />
59
59
  </div>
60
60
  <div className="min-w-0 flex-1">
61
- <div className="font-medium text-sm text-foreground group-hover:text-[var(--amber)] transition-colors truncate mb-1">
61
+ <div className="font-medium text-sm text-foreground group-hover:text-[var(--amber)] transition-colors truncate mb-1" title={link.filePath}>
62
62
  {link.filePath}
63
63
  </div>
64
- <div className="text-xs text-muted-foreground line-clamp-2 leading-relaxed italic opacity-80 group-hover:opacity-100 transition-opacity">
64
+ <div className="text-xs text-muted-foreground line-clamp-2 leading-relaxed italic opacity-80 group-hover:opacity-100 transition-opacity" title={link.snippets[0] || ''}>
65
65
  {link.snippets[0] || ''}
66
66
  </div>
67
67
  </div>
@@ -29,7 +29,7 @@ export default function Breadcrumb({ filePath }: { filePath: string }) {
29
29
  <span suppressHydrationWarning>{part}</span>
30
30
  </span>
31
31
  ) : (
32
- <Link href={href} className="hover:text-foreground transition-colors truncate max-w-[200px]">
32
+ <Link href={href} className="px-2 py-0.5 rounded-md hover:bg-muted/50 transition-colors truncate max-w-[200px]" title={part}>
33
33
  <span suppressHydrationWarning>{part}</span>
34
34
  </Link>
35
35
  )}
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { useMemo, useState, useRef, useCallback, useEffect } from 'react';
4
4
  import Papa from 'papaparse';
5
- import { ChevronUp, ChevronDown, ChevronsUpDown, Plus, Trash2 } from 'lucide-react';
5
+ import { ChevronUp, ChevronDown, ChevronsUpDown, Plus, Trash2, Loader2 } from 'lucide-react';
6
6
 
7
7
  interface CsvViewProps {
8
8
  content: string;
@@ -135,6 +135,7 @@ export default function CsvView({ content: initialContent, appendAction, saveAct
135
135
  const [sortCol, setSortCol] = useState<number | null>(null);
136
136
  const [sortDir, setSortDir] = useState<SortDir>(null);
137
137
  const [showAdd, setShowAdd] = useState(false);
138
+ const [saving, setSaving] = useState(false);
138
139
 
139
140
  const parsed = useMemo(() => {
140
141
  const result = Papa.parse<string[]>(content, { skipEmptyLines: true });
@@ -163,38 +164,57 @@ export default function CsvView({ content: initialContent, appendAction, saveAct
163
164
  // Update a single cell and persist
164
165
  const handleCellCommit = useCallback(async (rowIdx: number, colIdx: number, newVal: string) => {
165
166
  if (!saveAction) return;
166
- // rowIdx here is index into sortedRows — need to map back to original rows
167
- const updatedRows = rows.map((r, i) => {
168
- // find which original row matches this sorted row
167
+ const updatedRows = rows.map((r) => {
169
168
  const sorted = sortedRows[rowIdx];
170
169
  if (r === sorted) return r.map((cell, ci) => ci === colIdx ? newVal : cell);
171
170
  return r;
172
171
  });
173
172
  const newContent = serializeRows(headers, updatedRows);
174
173
  setContent(newContent);
175
- await saveAction(newContent);
174
+ setSaving(true);
175
+ try {
176
+ await saveAction(newContent);
177
+ } catch (err) {
178
+ console.error('[CsvView] Cell save failed:', err);
179
+ } finally {
180
+ setSaving(false);
181
+ }
176
182
  }, [saveAction, rows, sortedRows, headers]);
177
183
 
178
184
  // Delete a row and persist
179
185
  const handleDeleteRow = useCallback(async (rowIdx: number) => {
180
- if (!saveAction) return;
186
+ if (!saveAction || saving) return;
181
187
  const sorted = sortedRows[rowIdx];
182
188
  const updatedRows = rows.filter(r => r !== sorted);
183
189
  const newContent = serializeRows(headers, updatedRows);
184
190
  setContent(newContent);
185
- await saveAction(newContent);
186
- }, [saveAction, rows, sortedRows, headers]);
191
+ setSaving(true);
192
+ try {
193
+ await saveAction(newContent);
194
+ } catch (err) {
195
+ console.error('[CsvView] Row delete failed:', err);
196
+ } finally {
197
+ setSaving(false);
198
+ }
199
+ }, [saveAction, rows, sortedRows, headers, saving]);
187
200
 
188
201
  // Append a new row
189
202
  const handleAddRow = useCallback(async (newRow: string[]) => {
190
- setShowAdd(false);
191
- if (appendAction) {
192
- const result = await appendAction(newRow);
193
- setContent(result.newContent);
194
- } else if (saveAction) {
195
- const newContent = serializeRows(headers, [...rows, newRow]);
196
- setContent(newContent);
197
- await saveAction(newContent);
203
+ setSaving(true);
204
+ try {
205
+ if (appendAction) {
206
+ const result = await appendAction(newRow);
207
+ setContent(result.newContent);
208
+ } else if (saveAction) {
209
+ const newContent = serializeRows(headers, [...rows, newRow]);
210
+ setContent(newContent);
211
+ await saveAction(newContent);
212
+ }
213
+ setShowAdd(false);
214
+ } catch (err) {
215
+ console.error('[CsvView] Add row failed:', err);
216
+ } finally {
217
+ setSaving(false);
198
218
  }
199
219
  }, [appendAction, saveAction, headers, rows]);
200
220
 
@@ -266,9 +286,10 @@ export default function CsvView({ content: initialContent, appendAction, saveAct
266
286
  {saveAction && (
267
287
  <button
268
288
  onClick={() => handleDeleteRow(rowIdx)}
269
- className="opacity-0 group-hover:opacity-100 transition-opacity p-1 rounded hover:bg-destructive/10"
289
+ disabled={saving}
290
+ className="opacity-0 group-hover:opacity-100 transition-opacity p-1 rounded hover:bg-destructive/10 disabled:opacity-30"
270
291
  style={{ color: 'var(--muted-foreground)' }}
271
- title="Delete row"
292
+ title={saving ? 'Saving...' : 'Delete row'}
272
293
  >
273
294
  <Trash2 size={12} />
274
295
  </button>
@@ -295,8 +316,9 @@ export default function CsvView({ content: initialContent, appendAction, saveAct
295
316
  className="px-4 py-2 flex items-center justify-between"
296
317
  style={{ background: 'var(--muted)', borderTop: '1px solid var(--border)' }}
297
318
  >
298
- <span className="text-xs font-display" style={{ color: 'var(--muted-foreground)' }}>
319
+ <span className="text-xs font-display flex items-center gap-1.5" style={{ color: 'var(--muted-foreground)' }}>
299
320
  {rows.length} rows · {headers.length} cols
321
+ {saving && <Loader2 size={10} className="animate-spin" style={{ color: 'var(--amber)' }} />}
300
322
  </span>
301
323
 
302
324
  {canEdit && !showAdd && (
@@ -207,7 +207,7 @@ export default function DirView({ dirPath, entries, spacePreview }: DirViewProps
207
207
  {entry.type === 'directory'
208
208
  ? <FolderOpen size={22} className="text-yellow-400" />
209
209
  : <FileIconLarge node={entry} />}
210
- <span className="text-xs text-foreground leading-snug line-clamp-2 w-full" suppressHydrationWarning>
210
+ <span className="text-xs text-foreground leading-snug line-clamp-2 w-full" title={entry.name} suppressHydrationWarning>
211
211
  {entry.name}
212
212
  </span>
213
213
  {entry.type === 'directory' && (
@@ -230,7 +230,7 @@ export default function DirView({ dirPath, entries, spacePreview }: DirViewProps
230
230
  className="flex items-center gap-3 px-4 py-3 bg-card hover:bg-accent transition-colors duration-100"
231
231
  >
232
232
  <FileIcon node={entry} />
233
- <span className="flex-1 text-sm text-foreground truncate" suppressHydrationWarning>
233
+ <span className="flex-1 text-sm text-foreground truncate" title={entry.name} suppressHydrationWarning>
234
234
  {entry.name}
235
235
  </span>
236
236
  {entry.type === 'directory' ? (