Files
maestro/src/store/mappers.ts
T
wangjia fa472a06a7 feat: 同步引擎+Agent配置+依赖自动落位+看板大改(预览/md渲染/筛选/徽章组)
后端:
- src/sync/todo-sync.ts: todo.json 单向同步引擎(导入=首次同步,幂等,source_ref 映射,
  subs 复杂度修正为 easy,旧侧 done 历史事实优先 forceDone)
- 依赖自动落位:ready 意图按 deps 落位 blocked,依赖全 done 自动放行,
  daemon 启动 reconcileDeps 对账,手动绕过会弹回
- 新 API: PATCH projects/:id(autonomy/concurrency)、POST :id/sync、GET /api/agents、
  PATCH tasks/:id(title/priority/complexity 重置)
- daemon 定时同步(MAESTRO_SYNC_INTERVAL 默认 300s) + project.synced 事件
- priority 语义翻转: P0 最高/P1 默认/P2 最低,取值限 0..2,排序/映射/MCP/CLI 全跟进
- 静态服务发 no-cache 头(修浏览器吃旧 CSS/JS)
- schema 迁移: tasks.source_ref / projects.last_sync_at(ensureColumn 平滑升级旧库)

看板:
- 全屏预览模式(94vh 读完整方案+就地裁决,Esc/遮罩/裁决自动关闭)
- 产出 markdown 渲染为 HTML(零依赖渲染器,转义优先)
- 任务树筛选(复杂度/状态分组/关键字)+ 顶栏徽章组(待审批/可执行/执行中,hover 展开)
- 依赖可视化:详情 DEPS 区块 + 行内⛓等依赖 + 锚点跳转定位
- 按钮收敛:提交评审/编辑产出移除(CC 经 MCP 操作),界面只留用户动作
- Agent 执行面板 + 项目配置(并发/工作模式)+ 同步按钮

测试:21 个全过(新增 sync 幂等/迁移/patch/依赖落位/对账幂等)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 23:58:28 +08:00

75 lines
3.2 KiB
TypeScript

import type { Project, Task, ApprovalRecord, Run, Event, TaskResult, Autonomy } from '../model/types.js';
import type { Complexity } from '../model/complexity.js';
import type { TaskStatus, GateKind } from '../model/status.js';
import type { RunKind, RunStatus, EventType } from '../model/types.js';
/** SQLite 行类型(snake_case,文本/数字原样) */
export interface ProjectRow {
id: string; name: string; repo_path: string; default_branch: string;
verify_cmd: string | null; autonomy: string; model: string | null;
concurrency: number; status: string; created_at: string;
last_sync_at: string | null;
}
export interface TaskRow {
id: string; project_id: string; parent_id: string | null; depth: number;
title: string; complexity: string; status: string; priority: number;
deps: string; plan: string | null; spec: string | null; operations: string | null;
result: string | null; assignee: string | null; created_at: string; updated_at: string;
source_ref: string | null;
}
export interface ApprovalRow {
id: string; task_id: string; gate: string; action: string;
actor: string; reason: string | null; at: string;
}
export interface RunRow {
id: string; task_id: string; kind: string; worktree: string | null; branch: string | null;
status: string; started_at: string; ended_at: string | null;
transcript_ref: string | null; claude_session_id: string | null; error: string | null;
}
export interface EventRow {
id: string; project_id: string; task_id: string | null; type: string; payload: string; at: string;
}
export function rowToProject(r: ProjectRow): Project {
return {
id: r.id, name: r.name, repoPath: r.repo_path, defaultBranch: r.default_branch,
verifyCmd: r.verify_cmd, autonomy: r.autonomy as Autonomy, model: r.model,
concurrency: r.concurrency, status: r.status as Project['status'], createdAt: r.created_at,
lastSyncAt: r.last_sync_at,
};
}
export function rowToTask(r: TaskRow, approvals: ApprovalRecord[] = []): Task {
return {
id: r.id, projectId: r.project_id, parentId: r.parent_id, depth: r.depth,
title: r.title, complexity: r.complexity as Complexity, status: r.status as TaskStatus,
priority: r.priority, deps: JSON.parse(r.deps) as string[],
plan: r.plan, spec: r.spec, operations: r.operations,
approvals, result: r.result ? (JSON.parse(r.result) as TaskResult) : null,
assignee: r.assignee as Task['assignee'], createdAt: r.created_at, updatedAt: r.updated_at,
};
}
export function rowToApproval(r: ApprovalRow): ApprovalRecord {
return {
gate: r.gate as GateKind, action: r.action as ApprovalRecord['action'],
actor: r.actor, reason: r.reason, at: r.at,
};
}
export function rowToRun(r: RunRow): Run {
return {
id: r.id, taskId: r.task_id, kind: r.kind as RunKind,
worktree: r.worktree, branch: r.branch, status: r.status as RunStatus,
startedAt: r.started_at, endedAt: r.ended_at,
transcriptRef: r.transcript_ref, claudeSessionId: r.claude_session_id, error: r.error,
};
}
export function rowToEvent(r: EventRow): Event {
return {
id: r.id, projectId: r.project_id, taskId: r.task_id,
type: r.type as EventType, payload: JSON.parse(r.payload) as Record<string, unknown>, at: r.at,
};
}