Skip to content
dsh.fish
Bundle

godot-asset-planner

Unified Godot asset and project-goal management for DeepSeek Harness: godot_* model tools, a /gap REST surface, and a better-sidebar UI with asset manager, scene-tree viewer, and Git panel.

Source
LINinLIN-0079
stars
3 stars
License
MIT
Updated
Updated 6 hours ago

Readme

# godot-asset-planner — Godot 资产与项目目标统一管理插件

一个 DSH(DeepSeek Harness)插件,把 Godot 游戏项目的**资产管理**与**项目目标**整合到同一套工具里:注册资产、创建目标、按关键词/目标查询、生成关联状态报告。数据通过 DSH 的 `storage` 服务持久化到本地 JSON 文件,重启后自动加载。

**作者**:[LINinLIN-0079](https://github.com/LINinLIN-0079) · **许可证**:MIT(见 [LICENSE](LICENSE))

## 截图

**资产管理器**(资产列表 / 拖拽上传 / 搜索筛选 / 目标看板 / 预览区) | **场景树查看器**(Godot 节点树实时读取与编辑) | **Git 版本控制**(状态 / 提交 / 分支 / 差异)

| | | |
| --- | --- | --- |
| ![资产管理器](assets/screenshot-1.png) | ![场景树查看器](assets/screenshot-2.png) | ![Git 版本控制](assets/screenshot-3.png) |

## 文件结构

| 文件 | 说明 |
| --- | --- |
| `godot-asset-planner.ts` | 插件源码(TypeScript,运行时零依赖):4 个工具 + `/gap` REST 路由 + 拖拽文件上传。包主入口(`main` / `exports["."]`) |
| `lib/client.js` | 浏览器 half(`exports["./client"]`):better-sidebar 三个 tab(资产管理器 / 场景树查看器 / Git 版本控制),`window.__ModuleLoader__.load` 工厂格式,只 `require('react')` |
| `cordis.patch.yml` | bundle 补丁层(`dsh.bundle.patch`):插入插件行。安装后由 profile 启动自动合并 |
| `cordis.yml` | `--patch` 一次性加载用的补丁清单(同内容覆盖文件) |
| `game-dev.yml` | 「游戏开发模式」覆盖文件(同 `--patch` 或 profile 补丁) |
| `package.json` | 包清单:`dsh.bundle` + `dsh.client` 双声明(单包 = host + client),`"type": "module"` |
| `tsconfig.json` | 编辑器类型检查(`erasableSyntaxOnly` + `verbatimModuleSyntax`) |
| `test-plugin.mjs` | 功能测试:真实 json 后端 + 9 个工具 + REST 路由 + 上传 + 重启持久化 + **Godot 场景桥实时测试**(headless 读树/增删改回写) |
| `test-render-client.mjs` | 浏览器 bundle SSR 冒烟:模块加载/工厂物化/双 tab 注册/组件渲染(含 `SceneTreeView`) |
| `debug-scene-bridge.mjs` | 场景桥调试脚本:临时 Godot 项目 + 真实 console 二进制,打印每次 dump/编辑结果 |

## 快速开始

### 方式一:`dsh plugin` 安装(推荐,发布形态)

本插件是**单包双 half**:`dsh.bundle` 提供 host 插件(工具 + REST 路由),同一个包的 `dsh.client` 声明让 client-modules 自动把 `lib/client.js` 纳入浏览器启动清单——一条命令装好 host + client:

```powershell
# 从 npm / GitHub 发布后:
dsh plugin --profile web add godot-asset-planner

# 本地开发(本仓库直接链接):
dsh plugin --profile web add link:D:\deepseek\my-plugins
```

安装命令会把 `godot-asset-planner` 追加到 profile 的 `dsh.profile.bundles` 层栈,profile 启动时合并包的 `cordis.patch.yml` 完成挂载。**重启 `dsh web` / 桌面应用后生效**。

### 方式二:写入 profile 补丁(`--patch` / 热重载)

桌面版 DeepSeek Harness 每次启动读取 `C:\Users\ASUS\.dsh\profiles\web\cordis.patch.yml`,且该文件**支持热重载**——编辑保存后,正在运行的 GUI 会重新组合。

把下面这段追加到该文件里(`name` 为包名,从 profile 的 node_modules 解析):

```yaml
- insert:
    - id: godot-asset-planner
      name: godot-asset-planner
```

或在 harness 目录用命令行一次性加载:

```powershell
cd D:\deepseek\DeepSeek Harness\resources\harness
D:\deepseek\DeepSeek Harness\resources\node\node.exe --import tsx/esm apps/cli/src/bin.ts web --patch D:\deepseek\my-plugins\cordis.yml
```

> ⚠️ `--patch` 必须放在 `--host`/`--port` 等应用参数**之前**:一旦出现第一个未识别的参数,其后的选项会原样透传给 web 应用。

验证补丁组合(免启动,打印组合后的完整树):

```powershell
node "D:\deepseek\DeepSeek Harness\resources\harness\apps\cli\lib\bin.js" --profile web --dump-config --patch ./my-plugins/cordis.yml
```

> 注意:`--dump-config` 会重写 profile 根文件 `cordis.yml`(内容固定为空列表,幂等),需要对该目录有写权限。

## 工具清单

> 除 `godot_list_projects` 外,所有工具都支持可选参数 `project_id`(缺省为 `default` 项目);项目列表可用 `godot_list_projects` 查询。资产、目标、标签、关联、报告均按项目隔离。

| 工具 | 输入参数 | 功能 |
| --- | --- | --- |
| `godot_list_projects` | 无 | 列出全部规划项目(`id` + 名称,可选 Godot 目录路径),供其它工具指定 `project_id` |
| `godot_register_asset` | `asset_name`(必填), `asset_type`(`model`/`sound`/`scene`/`script`/`image`/`other`, 必填), `file_path`(必填), `linked_goal`(可选), `tags`(可选数组), `project_id`(可选) | 注册游戏资产;可关联到已存在的目标;**可带初始标签**;名称重复或关联不存在的目标会失败 |
| `godot_create_goal` | `goal_name`(必填), `goal_description`(必填), `due_date`(可选) | 创建项目里程碑/目标;名称重复会失败 |
| `godot_query_assets` | `keyword`(可选), `linked_goal`(可选) | 按大小写不敏感关键词(匹配名称/路径/类型/关联目标)和/或目标名查询资产;都不给则返回全部;两者为 AND 关系 |
| `godot_asset_report` | `format`(`text`/`markdown`, 必填) | 生成全部目标及其关联资产、未关联资产清单和统计的状态报告,按格式渲染 |
| `godot_add_asset_tag` / `godot_remove_asset_tag` | `asset_name`(必填), `tag`(必填, ≤40 字符) | 给资产添加/移除用户标签;重复添加/移除不存在标签为无操作 |
| `godot_link_asset_to_goal` / `godot_unlink_asset_from_goal` | `asset_name`(必填), `goal_name`(必填/可选) | 建立/解除资产与目标的关联;解除时传错目标名会失败 |
| `godot_set_asset_done` | `asset_name`(必填), `done`(布尔, 必填) | 标记资产完成状态——目标的**进度条**由关联资产中 `done` 的比例驱动 |
| `godot_delete_asset` | `asset_name`(必填) | 永久删除资产记录(仅移除规划器记录,磁盘文件不受影响);资产不存在会失败 |
| `godot_update_asset` | `asset_name`(必填), `file_path`/`asset_type`/`linked_goal`/`tags`/`done`(均可选) | 更新资产字段(**只改提供的字段**);`linked_goal` 传空串清除关联;`tags` 传数组整体替换(去重、每项 ≤40 字符);资产不存在/非法类型/未知目标会失败 |

## 数据持久化

- 存储服务:DSH `storage` 服务(`inject: ['tools', 'storage']`)。
- 后端:`@deepseek-ai/dsh-storage-json`(web profile 已挂载,根目录 `$DSH_HOME/storages/`,即 `C:\Users\ASUS\.dsh\storages\`)。
- 单元:KV 单元 `godot_asset_planner`(文件 `godot_asset_planner.json`),内含 `assets` 与 `goals` 两张表,每条资产/目标记录独立原子落盘。
- 可通过 `cordis.yml` 的 `config.unitName` 指定其它单元名(须匹配 `^[a-z][a-z0-9_]*$`)。
- 插件卸载/重载时会关闭单元;进程退出由后端统一收尾。

## 多项目(planner projects)

- **概念**:资产/目标按「规划项目」隔离(记录带 `projectId`,存储 key 为 `` `${projectId}::${name}` ``);**旧数据(无 `projectId`)自动归入常驻的 `default`(默认项目)**,无需迁移。
- **项目管理**:客户端工具栏左侧**项目切换器**(下拉列出全部项目 + 当前项目名),新建/重命名/删除/从 Godot 目录添加在「管理项目」弹窗完成;当前选中项目持久化(prefs 单元 `currentProject`),跨会话保留。
- **切换视图**:切换项目后重新拉取 `/gap/state`,资产列表、看板、预览、标签、关联、报告、场景树、操作日志全部显示该项目数据。
- **删除项目**:确认后**连同该项目的资产与目标一并删除**(不可恢复);`default` 项目不可删除。
- **Godot 目录发现**:「管理项目 → 扫描 Godot 项目目录」把 `godotScanRoot`(默认 `D:/Godot`)下发现的含 `project.godot` 的目录一键加入项目列表;**添加时会主动读取项目文件**——宿主验证 `project.godot` 存在(否则拒绝)并解析 `config/name` 作为项目名(未提供名称时),记录其 `path`;**创建后自动批量导入项目资源为资产**(`res://` 相对路径、按扩展名推断类型;跳过 `project.godot` 本身、`.godot`/`.git`/`bin`/`obj`/`build` 等缓存构建目录及无法识别的文件;重名跳过并计数,上限 3000 个防阻塞)。
- **REST**:`GET /gap/state?project=<id>` 指定视图(缺省用当前项目);`POST /gap/projects`(创建)/`/gap/projects/rename`/`/gap/projects/delete`/`/gap/projects/select`(持久化当前项目);`GET /gap/projects/candidates`(Godot 目录候选);各写路由支持 body `projectId`(缺省当前项目)。
- **工具**:11 个 `godot_*` 工具可选 `project_id`(缺省 `default`),配合 `godot_list_projects` 查询项目 id。

## 实现要点

- **运行时零依赖**:插件只用 `import type`(编译期类型),没有任何运行时 import——Node 的类型剥离(Node ^22.19 || >=24)直接加载 `.ts` 源文件,无需打包或 tsx。
- **原始 JSON Schema 注册**:工具定义直接传给 `ctx.tools.register`(DSH 支持 raw-schema 工具自校验参数),校验逻辑在 `execute` 内完成。
- **JSON Schema 子集约束**:`required` 只能用**对象级数组**(如 `required: ['name']`),不能写在属性内部(`{ type: 'string', required: true }` 会被真实注册器拒绝——本插件的 schema 已按此修正)。
- **写串行化**:KV 单元不保证并发写顺序,插件内部用 promise 链把变更串行化。
- **生命周期**:单元懒打开(每个后端同一单元只能有一个活句柄),通过 `ctx.effect` 在插件 fiber 销毁时关闭。
- **工具与 UI 共用同一套核心操作**:4 个工具与 `/gap` REST 路由都调用 `registerAssetCore`/`createGoalCore`/`queryAssetsCore`/`buildReportCore`,模型与界面永远看到同一份数据。
- **REST 表面按需挂载**:路由通过 `ctx.inject(['webServer'], ...)` 注册(组合条件式),等 webserver 就绪后生效;headless 等无 web 的 profile 自动退化为纯工具插件。
- 工具名带 `godot_` 前缀,避免与其它插件的全局工具名冲突。

## 游戏开发模式(game-dev profile)与可视化界面

### 自定义模式(Agent 预设 + profile)

游戏开发模式 = **Agent 预设**(新建会话下拉可选)+ **host 平面插件**(工具/REST/UI 已在 web profile 挂载):

**① Agent 预设(出现在新建会话的预设下拉里,与“标准模式”并列)**

```
C:\Users\ASUS\.dsh\.agent-presets\game-dev\
├── preset.yml          # name: 游戏开发模式 / description / order: 2
└── agent.cordis.yml    # 游戏开发 persona + 标准模式全部工具行
```

新建会话时在预设下拉选择「游戏开发模式」→ 会话即被激活:Agent 以 Godot 开发助手身份工作。客户端**右侧**的「Godot 资产管理器」以 **dsh-better-sidebar 的一个 tab** 呈现(`+` 菜单 → Godot 资产管理器,或侧边栏 tab 栏点击图标),所有会话可见(管理的是全局项目数据,不绑定会话)。

> 为什么插件行不在预设里?`godot-asset-planner` 打开 storage KV 单元(同一单元仅允许一个活句柄)并注册共享 webserver 路由——按会话挂载会在第二个会话冲突。因此插件挂在 host 平面(profile 补丁),预设只贡献 persona 与工具指引,两者组合即为完整模式。

**② game-dev profile(命令行独立模式)**

```
C:\Users\ASUS\.dsh\profiles\game-dev\
├── package.json        # bundles: dsh-base + dsh-web-app(即“标准模式”)+ godot-asset-planner
└── cordis.patch.yml    # 插件行(bundle 层已插入,这里可只留 config 覆盖)
```

启动(与桌面启动器相同的源码启动方式):

```powershell
cd D:\deepseek\DeepSeek Harness\resources\harness
D:\deepseek\DeepSeek Harness\resources\node\node.exe --import tsx/esm apps/cli/src/bin.ts --profile game-dev
```

> 端口默认 3080;如需其它端口把 `--host 127.0.0.1 --port 3081` 追加在末尾。`dsh` 在 PATH 时可简写 `dsh --profile game-dev`。

`my-plugins/game-dev.yml` 是同一内容的独立覆盖文件,两种用法:
- 覆盖到任意 web 能力 profile:`dsh web --patch ./my-plugins/game-dev.yml`;
- 或把其中的 `- insert:` 块放进某个 profile 的 `cordis.patch.yml`。

> ⚠️ 桌面版启动器(Electron)固定启动 `web` profile。若日常用桌面版,把 `game-dev.yml` 的 insert 块放进 `C:\Users\ASUS\.dsh\profiles\web\cordis.patch.yml`(本机已配置好),重启桌面应用即可。

### 客户端界面(Godot 资产管理器,better-sidebar tab)

- **形态**:注册为 **dsh-better-sidebar 的三个 tab**(`ctx.betterSidebar.registerTab`):「Godot 资产管理器」(id `godot-assets`,order 60)、「场景树查看器」(id `godot-scene`,order 61)、「Git 版本控制」(id `godot-git`,order 62),均 `single: true`——侧边栏本身的布局(吸附左右、调宽、折叠、会话持久化)全部由 better-sidebar 提供,无需自绘。
- **依赖**:需要已安装 `dsh-better-sidebar`(web profile 已装 v0.12.2);插件 `inject: ['betterSidebar']`,better-sidebar 缺席时本插件等待该服务。
- **主题**:全部使用 DSH 主题 token(`--dsw-alias-*`)加中性回退色,适配亮/暗主题;图标为内联 SVG,**无 emoji**。
- **功能**(**多面板工作区**:自由拖动标题栏、拖拽边框调大小、双击最大化、关闭后从工具栏「面板」菜单重开;详见下方「多面板布局」章节。默认「默认」预设 = 资产列表(左 40%)+ 目标看板(右 60%)+ AI 助手(底部)):
  - **资产列表面板**:列表(名称/类型/路径/关联目标/标签)+ 行内**发送到对话**按钮(插入 `@名称 <res://路径>(磁盘:绝对路径)` 到当前会话输入框——**光标处**插入,未聚焦输入框时追加到末尾,经 `conversation.input.setDraft` 公开服务,与 better-sidebar 的草稿插入同一路径;`/gap/state` 为每个资产附加 **`diskPath`**(`res://` 解析到所属项目根的绝对路径,绝对路径原样),引用同时带上磁盘位置,模型可直接读取文件)+ 行内**删除按钮**(确认后移除记录)+ **拖拽上传**(拖入后补类型与关联目标)+ **高级搜索与筛选**:
    - 工具栏**导入**为下拉菜单(导出=↑箭头、导入=↓箭头,语义对调):**导入配置备份(JSON)**(合并/覆盖恢复)或 **导入文件夹**(**两步弹窗**:①弹出窗口输入文件夹完整路径 → 宿主 `/gap/dir/scan` 递归读取该路径下所有文件;②再弹出标签窗口,选择**路径下文件夹名**(候选 chips 多选)或输入自定义标签,确认后 `/gap/dir/import` **一次**为全部文件批量注册(按扩展名推断类型、绝对路径引用、重复名计为失败);`/gap/assets` 与 `/gap/upload` 路由新增可选 `tags` 初始标签、重传合并标签);
    - 搜索栏(**Ctrl+F** 聚焦)实时过滤名称/路径/类型/标签/目标;
    - **筛选系统**(筛选面板,默认收起):**名称**、**路径**(`res://` 或磁盘路径均可)、**标签**多选、类型、关联目标、创建日期范围;带「清除筛选」;
    - **视图切换**(三种):**文件名**(紧凑单行,仅文件名+大小提示)、**双排**(双列卡片:类型图标/路径/标签/目标)、**表格**(名称/类型/大小/标签/目标/操作列——第三种为补充);`/gap/state` 为资产附加 **`size`**(磁盘文件字节数,文件不存在时省略)驱动大小列与排序;
    - **排列方式切换**:名称、文件大小、标签名称、创建时间、类型、目标、完成状态(补充后四项),支持**升序/降序**切换;
    - **点击资产行自动打开预览区面板**显示详情(预览是独立面板,无需手动打开)。
  - **预览区面板**:选中资产的预览(可**折叠**、可**拖动上边缘调高** 120–480px);头部显示注册路径与**磁盘位置**(`diskPath`,`res://` 已解析为绝对路径,便于复制/让模型读取),并有**编辑**按钮(弹窗修改路径/类型/关联目标/标签/完成状态,`POST /gap/assets/update`,只改变更字段):
    - 图片:内联大图(`GET /gap/file` 服务注册资产文件),**点击放大**为全屏覆盖层(右上角关闭按钮 / Esc / 点击背景关闭);
    - 音效:Web Audio 播放/暂停 + **可拖动进度条**(拖动 seek,显示当前/总时长)+ **音量滑块**(0–100%);
    - 模型:OBJ 统计顶点/面数,GLTF 统计顶点/三角面(3D 预览为未来功能);
    - 脚本:轻量**语法高亮**(注释/字符串/关键字/数字);
    - 场景:节点总数 + 节点类型分布;
    - 预览区内可**添加/移除标签**(回车或按钮)。
  - **目标看板面板**:目标以**卡片**展示——名称(悬停显示完整描述)、**进度条**(关联资产中已完成的百分比)、**截止日期**(过期标红)、已关联资产数;支持**按截止日期/进度/创建时间排序**;卡片**点击展开**关联资产明细(含完成勾选);顶部**资产 dock 点击多选 + 拖拽**到目标卡片建立关联、拖到「未关联」区解除关联(HTML5 DnD,拖拽高亮 + 自定义拖拽缩略图)。
  - **标签面板**(4.2,默认关闭):全部标签列表——颜色圆点 + 标签名 + 关联资产数 + 资产名,点击即按该标签筛选资产列表。
  - **关联面板**(默认关闭):按目标分组的资产清单 + 未关联资产区。
  - **报告面板**(默认关闭):一键生成(text/markdown)+ 面板内预览 + Blob 下载。
  - **场景树面板**:复用场景树查看器组件(headless 读树/编辑)。
  - **Git 版本控制面板**(默认关闭,也可从第三个 tab「Git 版本控制」打开):见下方「Git 版本控制」章节。
  - **AI 助手面板**:工具清单 + 当前数据摘要 + 会话提示(对话在主聊天窗口进行)。
  - **Toast**:所有操作成功/失败即时反馈(内容区底部,自动消失)。
- **后端新增**:资产类型扩展 **image/other**(上传按扩展名归类:png/jpg/svg→图片);标签工具 **`godot_add_asset_tag` / `godot_remove_asset_tag`**;关联工具 **`godot_link_asset_to_goal` / `godot_unlink_asset_from_goal`** 与完成状态 **`godot_set_asset_done`**(资产 `done` 标志驱动看板进度条,均持久化到 storage);路由新增 `POST /gap/assets/tags`、`/gap/assets/link`、`/gap/assets/unlink`、`/gap/assets/done`、`GET /gap/file`(仅服务**已注册资产**的文件,内容类型按扩展名、8MB 上限——预览数据通道)。
- **通信**:同源 JSON fetch 调用 host 插件的 `/gap/*` 路由(state/assets/goals/upload/tags/link/unlink/done/**export/import/layout**/file/report/**git**),与 9 个工具共用核心操作,持久化在 storage。
- **打包形态**:单包双 half——`godot-asset-planner.ts` 是 host 插件(`exports["."]`),`lib/client.js` 是浏览器 bundle(`exports["./client"]`)。包同时声明 `dsh.bundle`(插入 host 行)与 `dsh.client`(`platform: "web"`),client-modules 扫描 loader 行解析到本包后自动把 `lib/client.js` 纳入浏览器启动清单并服务 `/plugins/godot-asset-planner/client.js`。浏览器 bundle 手工写成 `window.__ModuleLoader__.load` 工厂格式(`id: 'godot-asset-planner'`,与包名一致),只 `require('react')`(seed 词),无需构建步骤。

### 场景树查看器(第二个 tab)

- **形态**:注册为 better-sidebar 的第二个 tab(id `godot-scene`,文档+节点 SVG 图标,标题「场景树查看器」,order 61)。
- **数据来源**:**Godot `--headless` 实时读取**(不依赖 GodotMCP / godot-bridge / 打开中的编辑器)。宿主把两段 GDScript(`dump.gd` 读树、`edit.gd` 写回)写入临时目录,用 `Godot*_console.exe --headless --path <项目> --script <runner>.gd -- <场景路径> <ops>` 运行,从 stdout 解析脚本打印的 JSON。场景必须**先注册为资产**(路由白名单),项目根通过配置 `godotProject` 或在 `godotScanRoot`(默认 `D:/Godot`)下发现解析。
- **功能**:
  - 场景选择器(列出**已注册的 scene 类型资产**)+ 刷新;选择记住在 localStorage,重开自动恢复;
  - 节点树:Godot 风格**彩色图标**(2D/3D/UI/动画/通用按类着色)+ 节点名 + 类型 + **可见性(眼睛)/可编辑(铅笔)**状态,缩进层级,展开/折叠(默认全展开,含「全部展开/全部折叠」);
  - **Ctrl+Shift+F** 节点搜索:匹配子串高亮 + 命中节点的祖先自动展开;「清除搜索」恢复;
  - **右键上下文菜单**:添加子节点(内联名称 + 常用节点类型下拉)、重命名(内联输入,Enter 确认/Esc 取消)、复制子树、删除(弹窗确认,无法撤销)、**定位**(树内高亮 + 滚动,展开祖先);
  - 底部状态栏:节点总数 · 所属项目 · 场景根节点名。
  - 每次编辑都是**真实的 headless Godot 运行**(`POST /gap/scene/edit`),成功即**写回 .tscn 文件**并以 Toast 反馈。
- **已知限制**:「跳转到 Godot 编辑器中该节点」需要编辑器桥接(GodotMCP / godot-bridge),未安装——UI 用树内「定位」替代并在菜单里注明。
- **新后端路由**:`GET /gap/scene/projects`(扫描发现的项目根)、`GET /gap/scene/tree?path=`(dump 场景节点树,`{root, nodes:[{name,path,type,visible,editable,children}]}`)、`POST /gap/scene/edit`(`{path, ops:[{op:'add'|'rename'|'copy'|'delete', ...}]}`,逐 op 执行并保存)。参数/操作类错误返回 400(`ClientRouteError`),Godot 不可用返回 500。
- **桥接实现要点**:`--path <项目>` 必须带,否则 `res://` 无法解析;Windows 需 **console 版** exe(GUI 版不写 stdout);脚本只用**显式类型**(Godot 4 把从 Variant 推断类型当作错误);操作路径与 dump 一致——根节点路径即根名(如 `Root`),子节点 `Root/Child`,`_find` 先剥离等于根名的首段;根节点禁止删除;`copy` 用 `duplicate(15)`(深度复制)+ 重设 owner。

### 面板体验增强(4.x)

- **4.1 数据导入/导出**(工具栏「导出」「导入」按钮):
  - 导出:`GET /gap/export` 返回 `{format:'godot-asset-planner', version:1, exportedAt, goals, assets}` 完整配置 → `JSON.stringify` + **Blob + `<a download>`** 下载为 `godot-planner-backup-<日期>.json`。
  - 导入:选择 JSON 文件后弹窗选择**合并**(追加新数据,重名跳过)或**覆盖**(清空两张表后重建);`POST /gap/import {mode, data}`。非法记录(缺 name / 非法类型等)**跳过并计数**,不会让整个导入失败;返回 `{goalsImported, assetsImported, goalsSkipped, assetsSkipped}`。
  - 宿主实现:`KvUnit` 增加 `deleteRecord`(覆盖模式先按 `loadAll().tables` 逐键删除);所有写入走既有写串行化链。
- **4.2 标签系统(增强版)**:
  - 标签颜色:**10 色预定义调色板**(`#FF6B6B #4ECDC4 #45B7D1 #96CEB4 #FFEAA7 #DDA0DD #F4A261 #F7DC6F #BB8FCE #85C1E9`),按标签名**确定性哈希**取色——同名同色,无需持久化颜色字段。
  - 资产列表的标签显示为**圆角色块 + 文字**(`renderTagChip`:色点 + 半透明底色描边),**点击即筛选**该标签下的资产(与筛选面板联动)。
  - 「标签」视图:全部标签 + 关联资产数 + 资产名,点击跳回资产视图并筛选。
- **4.3 最近操作记录**(面板底部「操作日志」区):
  - **内存存储**(模块级 store,会话期间有效,刷新即清空,不持久化);上限 100 条。
  - 记录:上传/创建目标/关联与解除/完成状态/标签增删(资产、目标 tab)+ 场景编辑(场景树 tab)——**跨 tab 共享**同一份日志。
  - 每条一行(溢出省略),**悬停 `title` 显示完整内容**;点击**跳转**:资产→打开/选中资产列表面板,目标→打开/展开目标看板面板,场景→激活场景树 tab;导出/导入类条目不可跳转。
  - 「清空」按钮清空日志。
- **4.4 面板设置**(右上角**齿轮**弹出):
  - **默认展开/折叠**(立即开合面板)、**默认宽度**(滑块 280–600px,350ms 防抖保存)。
  - 自动保存到 storage(复用 `/gap/ui-prefs`,KV 单元 `godot_asset_planner_ui`);**仅当用户在设置面板修改某项时**才通过 `props.store.reduce`(better-sidebar store,`panelOpen`/`width` 字段)**精确应用对应字段**——store 引用存放在 ref 中保持 reducer 稳定,挂载时**不**覆盖 better-sidebar 自身持久化的面板几何,因此拖拽调宽、toggle 开合等 better-sidebar 原生操作永远优先,不会与之冲突。

### 多面板布局(自由工作区)

把资产管理器 tab 拆成**可自由拖动、调整大小**的子面板工作区(需求 1.x):

- **面板清单**(9 个,工具栏「面板」下拉开关):
  - 核心 5 个:**资产列表**(搜索/筛选/列表)、**目标看板**(排序/进度/拖拽关联)、**场景树**(复用 SceneTab 组件,headless 读树/编辑)、**AI 助手**(工具清单 + 当前数据摘要 + 会话提示——对话本身在主聊天窗口进行,客户端无法直接驱动 LLM)、**预览区**(选中资产的预览与标签编辑)。
  - 附加 4 个(默认关闭,可从面板菜单打开):Git 版本控制、关联、标签、报告。
- **交互**:
  - **拖动标题栏**移动位置;**拖拽 8 个边框/角手柄**调整大小(最小 12%×8%);拖动中的面板显示**高亮描边**(拖动指示器)并置顶;
  - **关闭按钮**隐藏面板(工具栏「面板」菜单勾选重新打开);
  - **双击标题栏**最大化/还原(最大化时其余面板隐藏,标题栏出现「还原」按钮)。
  - 面板间 1px 边框分隔线;每个标题栏 = 图标 + 名称 + 还原 + 关闭。
- **预设布局**(工具栏下拉,3 个):**默认**(资产列表左 40% + 目标看板右 60%,AI 助手底部全宽)、**开发**(场景树左 30% + 资产列表中 40% + 预览区右 30%)、**管理**(目标看板全屏)。
- **持久化**:每次移动/缩放/开关/最大化/预设切换后 400ms 防抖保存到 storage(新增 `GET/POST /gap/layout`,存 ui-prefs 单元的 `global.layout`——`savePrefs` 改为合并写,互相不覆盖);下次打开自动恢复。
- **技术实现**:browser bundle 受「仅 `require('react')`、零构建」硬约束,无法引入 react-grid-layout / react-mosaic-component——改为**自研轻量布局**(约 250 行 `PanelWorkspace`:归一化 0–1 坐标 + pointer events 拖动/缩放 + ResizeObserver 测量),功能与交互完整覆盖需求;布局状态通过宿主 `/gap/layout` 路由持久化,面板内容以 `contentOf(id)` 配置注入。
- 宿主侧:`WorkspaceLayout` 归一化校验(坐标 clamp 0–1、未知面板 id 丢弃、`maximized` 白名单);`KvUnit` 无新增能力(复用 `setGlobal`)。

### Git 版本控制(第三个 tab + 工作区面板)

- **形态**:独立组件 `GitPanel` 被两处复用——better-sidebar 第三个 tab(id `godot-git`,「Git 版本控制」,Git 分支图标)+ 工作区面板(工具栏「面板」菜单打开)。
- **目录选择**:顶部下拉列出 `godotScanRoot`/`godotProject` 发现的项目根;选择记忆在 localStorage。**安全白名单**:所有 `/gap/git/*` 路由先校验目录属于已发现项目(否则 400);diff 的文件路径做**穿越防护**(resolve 后必须仍在项目目录内)。
- **功能**(1.1):
  - **状态查看**:`git status --porcelain -b`(`core.quotepath=false` 避免中文路径转义)解析为 已修改 / 已暂存 / 未跟踪 三组 + 当前分支;非仓库返回 `isRepo:false` 并显示「**初始化 Git 仓库**」按钮(`git init`)。
  - **提交**:多行 textarea + `git add -A && git commit -F -`(消息经 stdin 传入,支持多行);成功/失败即时反馈。
  - **推拉**:`git push` / `git pull`(120s 超时);执行期间顶部显示**进度提示**("正在执行 git push…"),按钮禁用防重入。
  - **分支管理**:当前分支 chip + 统计(修改/暂存/未跟踪数);切换下拉(`git checkout`);新建输入框 + Enter(`git checkout -b`,创建并切换)。
  - **文件差异**:点击文件行 → `git diff`(已修改)/ `git diff --cached`(已暂存)/ 直接读文件逐行加 `+`(未跟踪);Diff 视图**行级语法着色**(`+` 绿 / `-` 红 / `@@` 蓝 / 文件头灰),点击行加载,底部面板显示,可关闭。
- **操作反馈**(1.2):每次操作写入面板底部「执行日志」(成功 ✓ / 失败 ✗,悬停看详情),失败附带**详细错误 + 解决建议**(宿主 `gitHint`:未配置身份 → 提示 `git config`;认证失败 → 检查凭据/SSH key;无远程 → `git remote add origin`;无上游 → `git push -u`;合并冲突 → 手动解决等);操作也记入共享操作日志(不可跳转)。
- **宿主路由**:`GET /gap/git/projects`(自动发现 + 用户导入目录的并集)、`POST /gap/git/import-dir`(导入任意存在的本地目录到白名单,持久化到 ui-prefs 单元的 `global.gitDirs`)、`GET /gap/git/status?dir=`、`GET /gap/git/branches?dir=`、`GET /gap/git/diff?dir=&file=&status=`、`POST /gap/git/init|commit|push|pull|branch|checkout`(`gitOp` 统一封装 `node:child_process` spawn + 120s 超时;失败返回 `{ok:false, error, hint}`,HTTP 恒为 200 由 ok 判定)。
- **导入目录**(1.x 交互补充):目录下拉旁「导入目录」按钮弹窗输入路径(可在文件资源管理器复制路径粘贴),或直接把本地目录**拖放**到 Git 面板(Electron 环境 `File.path` 可用时);宿主校验目录存在后加入白名单并持久化,重启后仍在。

### 整合与启动步骤(一键到 tab)

> 以下示例以本机(Windows + `$DSH_HOME` = `C:\Users\ASUS\.dsh`)为例;路径请按你的环境替换。

1. 安装插件(发布形态,一条命令装好 host + client):`dsh plugin --profile web add godot-asset-planner`(本地开发:`dsh plugin --profile web add link:<本仓库路径>`,会作为符号链接进入 profile)。
2. 确认 profile 补丁含 config 覆盖(可选:`godotProject` / `godotBin` / `uploadRoot` / `unitName`);确认预设目录 `$DSH_HOME\.agent-presets\game-dev\` 存在;确认 `dsh-better-sidebar` 已装(bundle 层含 `better-sidebar` 行)。
3. **重启 harness 进程**(bundle 层变更与新的 tab 注册需重启生效):桌面版托盘 → 退出 → 重新打开。
4. 打开右侧边栏 → `+` 菜单或 tab 栏点「Godot 资产管理器」(Godot 三角图标)→ 使用多面板工作区(工具栏:项目切换器 / 布局预设下拉 / 面板开关 / 导出 / 导入 / 设置);或点「场景树查看器」(文档+节点图标)→ 选择已注册的 .tscn 场景查看/编辑节点树(需重启后宿主含场景桥路由)。
5. 上传文件默认落在 `$DSH_HOME\godot-uploads\`;想直接进 Godot 工程目录,在 profile 补丁的 config 里加 `uploadRoot`。

## 测试

```powershell
node my-plugins/test-plugin.mjs      # 宿主:工具 + REST + 持久化 + 多项目 + Godot 场景桥(真实 headless)
node my-plugins/test-render-client.mjs  # 客户端:SSR 渲染三 tab + 工作区 + 场景树 + Git + 项目切换器
```

`test-plugin.mjs` 覆盖:模块导出面(name/inject/apply)、**12 个工具**注册、目标/资产创建、全部参数校验失败路径(重复、未知目标、非法枚举、空串)、关键词/目标查询、text/markdown 报告渲染、**REST 路由全流程**(state/goals/assets/upload/report、400/404、base64 解码落盘、重传刷新)、"重启后数据仍在"的持久化验证(真实 `@deepseek-ai/dsh-storage-json` 后端 + 临时文件)、**导出/导入**(`GET /gap/export` 版本化文档、合并模式跳过重名、追加新数据、覆盖模式清表重建、非法记录跳过计数、非法 mode/data 400)、**diskPath 解析**(`/gap/state` 将 `res://` 资产解析为项目根下的绝对磁盘路径、绝对路径原样)**与 size 附加**(磁盘文件字节数、缺失省略)、**初始标签**(register/upload 路由与工具支持可选 `tags`、重传合并标签)、**文件夹批量导入**(`/gap/dir/scan` 递归扫描 + `/gap/dir/import` 一次注册全部文件并按文件夹名打标签、重复名计失败不崩溃、缺失目录失败、`/gap/dialog/open-directory` 非 Electron 环境优雅降级)、**删除资产**(工具 + 路由:删除记录、状态消失、未知资产 400/拒绝)、**更新资产**(工具 + 路由:改路径/类型、清关联、整体替换标签去重、设 done、只改提供字段、未知资产/非法类型/超长标签/非数组标签拒绝)、**工作区布局**(`GET/POST /gap/layout` 默认值、存取、坐标 clamp、非法面板 id 丢弃、prefs 保存不抹掉 layout)、**多项目**(项目创建/切换/隔离/重命名/删除连带数据、`?project=` 视图覆盖、**从 Godot 目录添加**:读取 `project.godot` 校验与取名、**自动批量导入项目资源**(`res://` 路径、类型推断、排除 `.godot` 缓存与无法识别文件))、**Git 版本控制**(真实 git 临时仓库:projects 发现、非仓库 isRepo:false、init、status 解析未跟踪/已修改、本地身份 commit、干净工作区、modified/untracked diff、路径穿越拒绝、分支创建/切换/列表、无远程 push 失败带建议、未知目录 400、空提交信息 400、**import-dir 导入任意目录并入白名单、缺失目录失败**),以及 **Godot 场景桥实时测试**(找到 `Godot*_console.exe` 时自动跑;临时项目建树 → 注册场景 → dump 树 → add/rename/copy/delete 逐 op 回写并复核新 dump 与 .tscn 内容 → 未注册场景/删根/父节点缺失/ops 非数组等 400 路径;找不到二进制则跳过并提示设置 `GODOT_BIN`)。

`test-render-client.mjs` 覆盖:bundle 执行与 `window.__ModuleLoader__.load` 工厂交接、`require('react')` 物化、`apply()` 注册**三个** better-sidebar tab(id/标题/order/single/组件/图标)、`GodotAssetTab` 工具栏(布局预设下拉/面板下拉/导出/导入/设置齿轮)+ 工作区加载态 + 操作日志空态、`tagColor` 调色板 + `renderTagChip` 色块、**`buildAssetRef`**(引用含磁盘路径、路径相同时省略、缺省容忍)、`KanbanView` 排序/进度/逾期/**focusGoal 展开**、`SceneTab` 空态提示与搜索栏、`SceneTreeView` 正常/折叠/搜索自动展开/内联重命名/内联添加五种渲染路径、**`PanelWorkspace`** 面板标题/`data-panel-id`/内容注入/关闭按钮/**最大化隐藏其余面板**、**`GitPanel`** 无项目提示/刷新/导入目录/执行日志空态、无 emoji 断言。

## 参考

- [Smalldy/godot-bridge](https://github.com/Smalldy/godot-bridge):DSH 插件 + `cordis.patch.yml` 加载模式的参考实现(通过游戏内 TCP 服务驱动 Godot 4.x)。
- 官方工具插件示例:`packages/fs/tool-fs`(`ctx.tools.register` / `defineTool` / `output.render` 的规范写法,及 `docs/cookbook/adding-a-tool.md`)。

## 依赖与鸣谢

本插件运行在 **DeepSeek Harness**([deepseek-ai/DeepSeek-Harness](https://github.com/deepseek-ai/DeepSeek-Harness))之上,并直接依赖以下组件:

| 依赖 | 版本 | 用途 | 来源 / 许可证 |
| --- | --- | --- | --- |
| [dsh-better-sidebar](https://github.com/omdsh-dev/DSH-better-sidebar) | 0.12.2 | 客户端 UI 的侧边栏容器:本插件的「Godot 资产管理器 / 场景树查看器 / Git 版本控制」三个 tab 通过其 `ctx.betterSidebar.registerTab` 服务注册;面板几何(拖拽、调宽、折叠)由它提供 | [github.com/omdsh-dev/DSH-better-sidebar](https://github.com/omdsh-dev/DSH-better-sidebar) · **MIT** |
| `@deepseek-ai/dsh-storage-json` | web profile 内置 | 数据持久化后端(KV 单元 `godot_asset_planner` / `godot_asset_planner_ui` → `$DSH_HOME/storages/*.json`) | DeepSeek Harness 工作区包 |
| `@deepseek-ai/dsh-tools` / `dsh-tool-*` | harness 内置 | 宿主工具注册、shell/fs/jobs 等运行时能力 | DeepSeek Harness 工作区包 |
| [Godot Engine](https://godotengine.org/) | 4.x(headless console 版) | 场景树查看器/编辑器:通过 `Godot*_console.exe --headless` 真实读写 `.tscn` | MIT(Godot 引擎本体) |

### 特别鸣谢

- **[omdsh-dev / DSH-better-sidebar](https://github.com/omdsh-dev/DSH-better-sidebar)(MIT)**:本插件的所有侧边栏 UI 都依赖它提供的 tab 注册服务与面板交互能力,特此鸣谢。
- **DeepSeek Harness 团队**:`tools` / `storage` / `webServer` / 预设组合等基础设施。
- **[Smalldy / godot-bridge](https://github.com/Smalldy/godot-bridge)**:插件加载模式与 Godot 桥接思路的参考。

如果你在使用本插件,请一并感谢以上项目的作者。

## 故障排查

- **Node 版本**:加载 `.ts` 需要 Node ^22.19 || >=24(默认类型剥离)。桌面版自带 `resources\node\node.exe`(v24)。
- **移动项目后**:`test-plugin.mjs` / `debug-scene-bridge.mjs` 里 `file:///D:/deepseek/my-plugins/godot-asset-planner.ts` 的绝对路径需同步更新(测试脚本直接加载源码,不走包解析)。
- **headless 等 profile 没有 storage**:`storage`/`storage-json`/`storage-domain` 是 web bundle 的行;在其它 profile 使用本插件前需先补上这些行(在已有 storage 的 profile 上重复添加会报 `duplicate-backend`)。
- **插件没生效**:用 `--dump-config --patch` 确认行已进入组合树;查看启动日志中是否有 `failed to apply loader entry godot-asset-planner` 的报错。

Install

dsh plugin --profile web add github:LINinLIN-0079/godot-asset-planner-public#a0e70b004a51c216bd98358ccb5021f1a696f470

Profile: web

Source