# Task Manager: kho công việc trung tâm

> Create and update tasks from the widget, from other plugins via invoke, or from AI agents via MCP.

# Task Manager plugin (plugins-library/task-manager)

## Mục đích

Kho **task trung tâm** cho toàn app. Một task gồm: `taskId`, `conversationId`, `providerName`,
`agentName`, `title`, `desc`, `status`, `comment` (+ `createdAt`/`updatedAt`). Task được tạo/cập nhật
từ 3 nguồn:

1. **Widget** của chính plugin (form tạo thủ công, đổi status, sửa comment, xóa).
2. **Plugin khác** gọi vào qua `invoke('plugin:task-manager:*', ...)` — giống cách plugin `notification`
   cho gọi `plugin:notification:show`. Đây là kênh Agent Review dùng để đẩy session thành task.
3. **AI agent của ai-chat** qua **MCP tools** `task_create` / `task_update` / `task_list` (chỉ khi
   Dev Mode bật) — khi Claude quản lý task trong lúc chat thì ghi thẳng vào kho này.

Loại plugin: `backend` (có `main/` chạy trong Electron main process để đăng ký IPC channel dùng chung
+ giữ store bền vững).

Status hợp lệ: `pending` / `in_progress` / `completed` / `cancelled` (giá trị khác bị ép về `pending`).

## File liên quan

- `plugins-library/task-manager/manifest.json` — `mainEntry = "main/index.js"`,
  `defaultSettings = { tasks: [], hideCompleted: false }`, `defaultWidgetConfig = 780x580 @ (80,80)`.
- `plugins-library/task-manager/main/index.ts` (+ `main/index.js` đã compile) — backend: store + IPC +
  MCP tools. **Sửa `.ts` xong phải chạy lại `npx tsc -p tsconfig.plugins.json`.**
- `plugins-library/task-manager/package.json` — chỉ khai báo `zod` (dùng cho MCP tools; host chạy
  `npm install --omit=dev` khi cài plugin). Thiếu zod thì `getMcpTools` trả `[]`, phần còn lại vẫn chạy.
- `plugins-library/task-manager/widget.html` + `js/widget.js` + `css/widget.css` — UI: toolbar (lọc theo
  status + nút "+ New"), form thêm task, danh sách task (đổi status, sửa comment, xóa), overlay Settings.
- `plugins-library/task-manager/settings.html` + `js/settings.js` + `css/settings.css` — 1 checkbox
  `hideCompleted`, dùng bởi Plugin Manager và cửa sổ pop-out (trùng lặp có chủ đích với overlay trong widget).
- `plugins-library/task-manager/locales/en.json5` + `vi.json5` — chuỗi UI, namespace `widget.*` /
  `filter.*` / `status.*` / `settings.*`.

## Cách hoạt động

### Lưu trữ

Task được lưu trong **settings slice của chính plugin** (`ctx.getSetting('tasks')` /
`ctx.setSettings({ tasks })`), KHÔNG dùng file riêng. Lý do:

- Bền vững qua restart app và qua nâng cấp plugin (settings nằm trong ConfigStore của app, không bị
  ghi đè khi copy lại thư mục plugin lúc cài).
- Mỗi `setSettings` tự bắn `settingsChanged` → widget nghe event này để tự refresh, không cần
  `broadcast` custom.

### IPC channels (điểm cốt lõi — mở cho mọi caller)

`list` / `get` / `upsert` / `update` / `remove` **cố tình KHÔNG dùng `ensureIsSelf`** để plugin khác
gọi được (giống `plugin:notification:show`). Chỉ `clear` là self-gated (phải truyền `'task-manager'`).

```js
// Tạo mới (không có taskId) hoặc cập nhật (taskId đã tồn tại). Trả về task đã lưu.
await window['kazuo-ai'].invoke('plugin:task-manager:upsert', {
  taskId: 'session:<id>',        // tùy chọn — có thì upsert theo id (idempotent)
  conversationId: '<id>', providerName: 'claude-code', agentName: '',
  title: '...', desc: '...', status: 'pending', comment: '',
});
await window['kazuo-ai'].invoke('plugin:task-manager:update', { taskId, patch: { status: 'completed' } });
await window['kazuo-ai'].invoke('plugin:task-manager:remove', taskId);
const tasks = await window['kazuo-ai'].invoke('plugin:task-manager:list', { status: 'pending' }); // status optional
```

- `upsert`: có `taskId` khớp → merge (chỉ ghi đè field được truyền); không có/không khớp → tạo mới với
  `taskId` sinh tự động (`t_<base36 time>_<rand>`), `unshift` lên đầu.
- `update`: patch theo `taskId`, trả `null` nếu không tìm thấy.

### MCP tools (`getMcpTools`, Dev Mode only)

Export `task_create` / `task_update` / `task_list` bằng `ctx.modules.claudeAgent.tool(...)`. Host tự
thêm tiền tố → agent thấy `task-manager_task_create`... Chúng gọi CÙNG hàm `upsertTask`/`updateTask`
với IPC nên UI và AI chia sẻ đúng một store. `zod` require lazily, thiếu → trả `[]` (không vỡ `activate`).

### Widget

- Đọc task qua `plugin:task-manager:list`; nghe `settingsChanged` để cập nhật khi task đổi (kể cả do
  plugin khác/AI ghi vào). Mọi thao tác dựng bằng DOM API (`textContent`) nên nội dung task không bị
  chèn thẳng vào HTML.
- Lọc theo status; `hideCompleted` ẩn task `completed`/`cancelled` khi đang xem "All".

## Lưu ý / giới hạn

- Không chống đua ghi đồng thời phức tạp: mỗi handler đọc → sửa → `setSettings` trong 1 lượt sync
  (main process 1 luồng) nên đủ an toàn cho quy mô vài chục–vài trăm task (KISS). Không dành cho hàng
  chục nghìn task.
- MCP tools chỉ có tác dụng khi Dev Mode bật và agent kết nối lại (gate tính lại mỗi lần connect —
  giống các plugin MCP khác). Người dùng thường không cho AI đụng store qua đường này.
- `conversationId`/`providerName`/`agentName` chỉ là field lưu trữ — plugin không tự suy ra, do caller
  (Agent Review, AI, hoặc người dùng) cung cấp.
