Files
notebook-ai-extension/TODO.md
T
tao.chen c919c95842 Initial commit: opencode_bridge JupyterLab extension (Slices 1-3.5)
A JupyterLab extension that bridges the cell UI to a local OpenCode Serve
process. The extension is a dual package: a Python server extension
exposed under /opencode-bridge/*, plus a TypeScript frontend that
registers per-cell toolbars.

Backend (Python, tornado)
- Slice 1: config + auth + OpenCode HTTP client (tornado.httpclient,
  no aiohttp). 4 settings in schema/plugin.json (url, user, password,
  request timeout).
- Slice 2: handlers for /hello, /health, /providers, /edit.
- Slice 2.1 (correction): SessionManager with 1 notebook = 1 session
  mapping, async-safe via per-path locks, 404 recovery via invalidate().
  Two new endpoints: GET /sessions, DELETE /session?notebook=<path>.
- 32 pytest tests pass.

Frontend (TypeScript, JupyterLab 4.6)
- src/types.ts: CellContext, OpenCodeRequest/Response, OpenCodeSettings.
- src/context/cell_context.ts: extract CellContext from a CodeCell +
  its parent NotebookPanel, structured error collection.
- src/api/opencode_client.ts: callOpenCodeEdit, callOpenCodeProviders.
- src/components/opencode_cell_footer.ts: OpenCodeCellFooter Widget
  implementing ICellFooter with 3 buttons (optimize / fix / edit),
  resolved via this.parent instanceof CodeCell. NOT cellToolbar
  (does not exist in JL 4.6) and NOT Widget.findParent (removed in
  @lumino/widgets 2.x).
- src/components/opencode_cell_factory.ts: Cell.ContentFactory
  subclass returning the OpenCodeCellFooter.
- src/components/opencode_installer.ts: installOpenCodeEverywhere
  patches every notebook (existing + new) to use the custom factory.
- src/index.ts: registers the factory, loads settings, fetches
  /providers on activation and logs the list to the console.
- 23 jest tests pass (mocked JupyterLab boundary, pnpm path safe).

Settings
- 6 fields: 3 auth (url/user/password) + 1 timeout + 2 model selection
  (provider/model). Provider list is fetched at startup from
  /opencode-bridge/providers and printed to the browser console so
  users can copy values into Settings Editor.

Docs
- design.md: 6 sections covering architecture, UI flow, API contract,
  TS skeletons, session management (v0.2.1 correction), and
  provider/model selection (v0.2.2 addition).
- CLAUDE.md: agent guidance for working in this repo.
- TODO.md: remaining work for Slices 4-7 + v0.4+ backlog.

CI
- Gitea release workflow at .github/workflows/build.yml.
- Bark notification helper (non-fatal on failure).

Generated artefacts ignored: opencode_bridge/labextension/, _version.py,
*.tsbuildinfo, junit.xml, test.ipynb scratch notebook.
2026-07-22 19:07:58 +08:00

5.1 KiB
Raw Permalink Blame History

TODO

剩余开发任务。按依赖顺序切片,每步可独立验证。


Slice 4 — Diff 面板 + Yjs 写入

目标:把 OpenCodeCellFooter._handleResponse 里的 Notification.info(...) 替换成真正的 Diff 面板,支持 Accept / Reject / 微调,写入走 Yjs 事务保护。

未决问题先回答再写代码

  1. Accept 后是否自动执行 cell?(建议:否,让用户自己决定;slice 5 再说)
  2. 多个 cell 同时打开 Diff 面板时如何管理?(每个 footer 实例一个面板 vs 全局单例——建议每 cell 一个)
  3. Diff 视图用什么库?JL 自带 IDiffModel?还是简单 diff-match-patch零新依赖优先

任务

  • src/components/diff_panel.ts 新建 DiffPanel widget(继承 ReactWidget 或纯 Lumino Widget
  • 接受 → cell.model.sharedModel.transact(() => cell.model.sharedModel.setSource(finalSource), 'opencode-bridge-accept')
  • 拒绝 → 关闭面板
  • 微调 → 进入 textarea 编辑模式
  • _handleResponse 里替换 Notification.info 调用,改为打开 DiffPanel
  • 新增 src/__tests__/diff_panel.spec.ts 测试 accept/reject 路径

验收

  • pytest opencode_bridge/tests/ 仍 32/32 pass
  • jest src/__tests__/ 仍 23/23+ pass+ diff_panel 新测试)
  • tsc --noEmit 干净

Slice 5 — Inline prompt + Edit flow

目标:替换 opencode_cell_footer.ts:127 里的 window.prompt(...),做内嵌 prompt 框,跟 design.md §2 一致。

任务

  • opencode_cell_footer.ts 加内嵌 prompt 状态机:closed → open → streaming → done / error
  • 🪄 编辑 按钮点击 → 状态变 open → footer 下方滑出 textarea + 发送/取消按钮
  • 提交 → 状态变 streaming → 显示进度(用现有 loading 状态) → 完成后切到 done → Diff 面板出现
  • 取消 → 状态回到 closed
  • 多个 cell 同时 open 的 prompt 互相独立(每个 footer 自己的状态)

验收

  • 所有现有测试 pass
  • 新增 1 个状态机集成测试

Slice 6 — Fix flow 完整闭环

目标🐛 排错按钮从"灰着"变成真能用——点击后调用 /editmode: 'fix',自动注入 traceback 到 context(后端 _build_request_body 已支持,前端只发就行)。

任务

  • 验证 cell 报错后 → fix 按钮可点(已实现)
  • 点击 → 后端在 _build_request_body 里用 error 字段拼 traceback(已实现)
  • 流式回包后 → Diff 面板显示修复版代码(Slice 4 完成后顺带支持)
  • 验证 acceptance 后 cell.model.sharedModel.setSource(...) 后用户能直接重跑

验收

  • pytest opencode_bridge/tests/ 加一个 fix-mode 的 testmock OpenCode client 验证 traceback 被拼进 parts

Slice 7 — 边界场景

目标:把所有 v0.2/v0.2.1/v0.2.2 设计里"未解决"和"边界"集中处理。

任务

  • OpenCode 不可达:前端 HealthHandler 返回 503 → 3 个按钮全 disabled + Notification 显示
  • 401/403OpenCode 拒绝 Basic Auth → Notification 提示检查 opencodeServerPassword setting
  • session 失效OpenCode 返回 404 → 后端 EditHandler 调用 sm.invalidate(notebook_path) → 下次 get_or_create 重建(后端逻辑已在 v0.2.1 实现,补前端 Notification 提示)
  • 空 notebook pathEditHandler 400 + {error: "missing context.notebookPath"}(已实现,前端检查)
  • 长 prompt 截断:超 4000 chars 的 prompt 提示用户
  • provider/model 拼错/config/providers 返回 200 但 provider 不存在 → OpenCode 端报错 → 后端 EditHandler 502 + 错误信息透传
  • 取消进行中请求:用户点 之后能取消 → AbortController + client.abort(session_id) 调 OpenCode /session/:id/abort

验收

  • 每个边界场景有一个对应的 test(pytest 或 jest,看发生在哪层)
  • Notification 文案一致

v0.4+(不在本轮迭代范围内)

来自 design.md §5.6

  • TTL reapernotebook 关闭后 30 分钟 idle 自动 release session
  • 多 server 横向扩展:in-memory state → Redis / sticky session
  • 启动清理:Jupyter 重启后调 GET /session 列出 OpenCode 端孤儿删除
  • 并发 LLM 调用:asyncio.Semaphore(per_session=1) 显式控制
  • 流式 UX:v0.3 砍掉流式(同步版),v0.4 加 prompt_async + /event SSE 多路复用
  • Provider/model 选值 UX:升级为 JupyterLab command + QuickPick,替代 console.log

来自 design.md §6.2

  • Settings UI dynamic enum:等 JupyterLab 支持后把 console.log 替换成真正的 settings 渲染器

当前统计

  • 后端32 pytest testsconfig + client + routes + session_manager
  • 前端23 jest teststypes + cell_context + opencode_client + opencode_cell_footer
  • Schema 字段63 auth + 1 timeout + 2 provider/model
  • 设计文档design.md v0.2.26 节
  • CLAUDE.md 已建

总进度:Slices 1–3.5 完成,4–7 待开发。