dsh-taskboard 0.6.6 → 0.7.0
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.
- package/README.md +305 -288
- package/lib/client.js +985 -931
- package/lib/host/archive-sessions.js +30 -0
- package/lib/host/archive-sessions.js.map +1 -0
- package/lib/host/assets.js +139 -0
- package/lib/host/assets.js.map +1 -0
- package/lib/host/execution.js +3 -0
- package/lib/host/execution.js.map +1 -1
- package/lib/host/locale.js +17 -0
- package/lib/host/locale.js.map +1 -0
- package/lib/host/routes.js +132 -6
- package/lib/host/routes.js.map +1 -1
- package/lib/host/scheduler.js +2 -0
- package/lib/host/scheduler.js.map +1 -1
- package/lib/host/session-sync.js +4 -1
- package/lib/host/session-sync.js.map +1 -1
- package/lib/host/storage-queue.js +14 -0
- package/lib/host/storage-queue.js.map +1 -0
- package/lib/host/storage.js +249 -0
- package/lib/host/storage.js.map +1 -0
- package/lib/host/store.js +34 -7
- package/lib/host/store.js.map +1 -1
- package/lib/host/templates.js +73 -113
- package/lib/host/templates.js.map +1 -1
- package/lib/host/tools.js +23 -8
- package/lib/host/tools.js.map +1 -1
- package/lib/index.js +83 -27
- package/lib/index.js.map +1 -1
- package/lib/shared/api.js.map +1 -1
- package/lib/shared/builtin-templates.js +155 -0
- package/lib/shared/builtin-templates.js.map +1 -0
- package/lib/shared/protocol.js +39 -2
- package/lib/shared/protocol.js.map +1 -1
- package/package.json +90 -89
- package/src/client/api.ts +35 -1
- package/src/client/board/SettingsModal.tsx +67 -4
- package/src/client/board/SlashPromptInput.tsx +80 -1
- package/src/client/board/TaskBoard.tsx +16 -11
- package/src/client/board/TaskDetail.tsx +186 -17
- package/src/client/board/TaskFormModal.tsx +8 -5
- package/src/client/board/TemplateManager.tsx +22 -10
- package/src/client/controller.ts +67 -5
- package/src/client/i18n/en.ts +35 -3
- package/src/client/i18n/templates.ts +25 -0
- package/src/client/i18n/zh.ts +35 -3
- package/src/client/image-insert.ts +29 -0
- package/src/client/styles.ts +35 -2
- package/src/host/archive-sessions.ts +18 -0
- package/src/host/assets.ts +120 -0
- package/src/host/execution.ts +4 -1
- package/src/host/locale.ts +44 -0
- package/src/host/routes.ts +132 -7
- package/src/host/scheduler.ts +2 -0
- package/src/host/session-sync.ts +4 -1
- package/src/host/storage-queue.ts +10 -0
- package/src/host/storage.ts +212 -0
- package/src/host/store.ts +40 -12
- package/src/host/templates.ts +38 -66
- package/src/host/tools.ts +28 -11
- package/src/index.ts +88 -33
- package/src/shared/api.ts +32 -3
- package/src/shared/builtin-templates.ts +153 -0
- package/src/shared/protocol.ts +64 -0
- package/src/shared/version.ts +9 -9
package/lib/shared/api.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.js","names":[],"sources":["../../src/shared/api.ts"],"sourcesContent":["/**\n * Wire contract for the /taskboard host routes: the JSON envelope,\n * request/response shapes, and SSE event payloads shared by the host routes\n * and the browser client.\n *\n * @module dsh-taskboard/shared/api\n */\nimport type { BoardSettings, TaskLedger, TaskModel, TaskRecord, TaskSummary } from './protocol.ts'\n\nexport type { TaskModel, TaskRecord }\n\n/** Route prefix on the shared DSH webserver (same origin as the GUI). */\nexport const ROUTE_PREFIX = '/dsh-taskboard'\n\n/** SSE stream path (exact route; longest-prefix wins keep it disjoint). */\nexport const SSE_PATH = '/dsh-taskboard/events'\n\n/** Stable error codes (mirror the tool-level codes plus HTTP mapping). */\nexport type ApiErrorCode =\n | 'invalid_input'\n | 'not_found'\n | 'version_conflict'\n | 'invalid_transition'\n | 'forbidden'\n | 'internal'\n\n/** Success envelope. */\nexport type ApiOk<T> = { ok: true; value: T }\n\n/** Failure envelope. */\nexport type ApiFail = { ok: false; error: { code: ApiErrorCode; message: string } }\n\n/** The envelope either way. */\nexport type ApiResult<T> = ApiOk<T> | ApiFail\n\n// ---------------------------------------------------------------------------\n// payloads\n// ---------------------------------------------------------------------------\n\n/** Full-state response (the reconnect baseline after an SSE gap). */\nexport type StateResponse = TaskLedger\n\n/**\n * Workspace listing for the UI pickers. `repoCount` (0.6.3): how many repos a\n * task mirror of this workspace would cover (root repo + nested) — the form's\n * worktree option shows the mirror badge when it exceeds 1.\n */\nexport type WorkspaceView = { id: string; path: string; title: string; sessionCount: number; gitAvailable?: boolean; repoCount?: number }\n\n/** Create-task request body (actor is always the GUI user). */\nexport type CreateTaskBody = {\n title: string\n workspaceId: string\n urgency: string\n description?: string\n prompt?: string\n execution?: { mode?: string; cron?: string }\n model?: TaskModel\n /** Code isolation for executions ('worktree' | 'none'); omitted = default. */\n isolation?: string\n /** Agent preset for execution sessions; omitted = deployment default. */\n presetId?: string\n /** Execution permission preset ('workspace-write' | 'read-only' | 'danger-full-access'); omitted = default. */\n permission?: string\n /** Acceptance checklist item texts (host mints ids, all unchecked). */\n checklist?: string[]\n}\n\n/** Update-task request body (ifVersion mandatory). */\nexport type UpdateTaskBody = {\n ifVersion: number\n title?: string\n description?: string\n prompt?: string\n urgency?: string\n blocked?: boolean\n /** Rebind the task to another project (GUI owner surface only). */\n workspaceId?: string\n execution?: { mode?: string; cron?: string }\n model?: TaskModel | null\n /** Change isolation; locked once the task has execution history. */\n isolation?: string\n /** Change the execution preset (takes effect on the next run). */\n presetId?: string | null\n /** Change the execution permission (0.5.5; 'workspace-write' | 'read-only' | 'danger-full-access'). */\n permission?: string | null\n /** Replace the whole checklist (GUI owner surface); null clears it. */\n checklist?: unknown\n}\n\n/** Move-task request body (ifVersion mandatory; the user MAY move to done). */\nexport type MoveTaskBody = { ifVersion: number; status: string }\n\n/**\n * Quick-reject request body (card ✗ button): move back to todo plus an\n * optional user comment, committed as ONE ledger mutation so a failed move\n * can never strand an orphan comment.\n */\nexport type RejectTaskBody = { ifVersion: number; body?: string }\n\n/** Comment request body. */\nexport type CommentBody = { body: string }\n\n/** Delete request body (purge=true physically removes a trashed task). */\nexport type DeleteTaskBody = { ifVersion?: number; purge?: boolean }\n\n/** Run request body; `reuse: true` = 续跑 (keep a live worktree as-is). */\nexport type RunTaskBody = { reuse?: boolean }\n\n/** One repo's merge outcome in a multi-repo merge (0.6.3; `repo: ''` = the workspace root repo). */\nexport type MergeRepoResult = {\n repo: string\n branch: string\n outcome: 'merged' | 'noop' | 'failed'\n /** Failure reason (verbatim git message) when outcome = 'failed'. */\n error?: string\n}\n\n/**\n * Merge outcome. Legacy single-repo tasks keep the flat shape; multi-repo\n * mirror tasks (0.6.3) additionally return per-repo results — merges run\n * sequentially and a failed repo does not block the others (plan §4.5).\n */\nexport type MergeBranchResponse = {\n merged: boolean\n noop?: boolean\n /** The merged task branch (legacy single-repo shape; multi-repo responses omit it). */\n branch?: string\n /** Present only on multi-repo mirror merges (0.6.3). */\n results?: MergeRepoResult[]\n}\n\n/** Remove a task's worktree; optionally delete its branch too. */\nexport type WorktreeRemoveBody = { deleteBranch?: boolean }\n\n/** One orphan worktree directory (exists on disk, owned by no live task). */\nexport type OrphanWorktree = { workspaceId: string; workspacePath: string; taskId: string; path: string }\n\n/** A git-enabled workspace whose .gitignore does not cover the worktree dir. */\nexport type GitignoreSuggestion = { workspaceId: string; workspacePath: string }\n\n/** Health-diagnostics response (⚙ panel). */\nexport type DiagnosticsResponse = {\n revision: number\n tasks: number\n /** Executions currently marked `running`. */\n staleRunning: number\n /** Worktree directories whose task no longer exists in the ledger. */\n orphanWorktrees: OrphanWorktree[]\n /** Git workspaces whose .gitignore does not ignore the worktree dir. */\n gitIgnoreSuggestions: GitignoreSuggestion[]\n}\n\n/** Fields a task template may prefill (0.4.0). */\nexport type TaskTemplateSpec = {\n title?: string\n description?: string\n prompt?: string\n urgency?: string\n execution?: { mode?: string; cron?: string }\n model?: TaskModel\n isolation?: string\n presetId?: string\n /** Execution permission preset (0.5.5). */\n permission?: string\n /** Checklist item texts (host mints ids at create time). */\n checklist?: string[]\n}\n\n/** One reusable task template (0.4.0). */\nexport type TaskTemplate = {\n id: string\n name: string\n task: TaskTemplateSpec\n /** Seeded built-in templates (kept on load, deletable like any other). */\n builtin?: boolean\n createdAt: number\n updatedAt: number\n}\n\n/** Templates listing response. */\nexport type TemplatesResponse = { templates: TaskTemplate[] }\n\n/** Board-settings response (0.5.0; absent fields follow factory defaults). */\nexport type SettingsResponse = BoardSettings\n\n/** Update-board-settings request body (0.5.0; whole-object replace semantics). */\nexport type UpdateSettingsBody = {\n /** Default code isolation for NEW tasks ('worktree' | 'none'). */\n defaultIsolation?: string\n /** Automatically capture external workspace sessions into the taskboard. */\n syncExternalSessions?: boolean\n /** Default permission preset for NEW tasks ('workspace-write' | 'read-only' | 'danger-full-access'). */\n defaultPermission?: string\n}\n\n/** Prompt completion item for skills and slash commands (0.5.5). */\nexport type PromptCompletionItem = {\n name: string\n kind: 'skill' | 'command'\n description?: string\n hint?: string\n}\n\n/** Prompt completions response (0.5.5). */\nexport type PromptCompletionsResponse = {\n commands: PromptCompletionItem[]\n skills: PromptCompletionItem[]\n}\n\n/** Model item in catalog (0.5.5). */\nexport type CatalogModelItem = {\n provider: string\n model: string\n name?: string\n description?: string\n reasoning?: {\n efforts: Array<{ id: string; name: string; description?: string }>\n defaultEffort?: string\n }\n}\n\n/** Preset item in catalog (0.5.5). */\nexport type CatalogPresetItem = {\n id: string\n name?: string\n}\n\n/** Model and preset catalog response (0.5.5). */\nexport type ModelCatalogResponse = {\n models: CatalogModelItem[]\n presets: CatalogPresetItem[]\n defaultPresetId?: string\n}\n\n/** Import dry-run response (0.4.0): every task classified, nothing written. */\nexport type ImportPreviewResponse = {\n plan: {\n create: Array<{ id: string; title: string; status: string }>\n overwrite: Array<{ id: string; title: string; status: string }>\n invalid: Array<{ id?: string; reason: string }>\n }\n}\n\n/** Import commit response. */\nexport type ImportCommitResponse = {\n mode: 'merge' | 'replace'\n created: number\n overwritten: number\n replacedTotal?: number\n /** The backup file written BEFORE a replace wiped the live ledger. */\n backupFile?: string\n}\n\n/** Diff-viewer response (0.4.0). */\nexport type DiffResponse = { diff: string; truncated: boolean }\n\n/** One task (full record) response. */\nexport type TaskResponse = TaskRecord\n\n/** Summary response used by list-ish endpoints. */\nexport type SummaryResponse = { tasks: TaskSummary[] }\n\n// ---------------------------------------------------------------------------\n// SSE\n// ---------------------------------------------------------------------------\n\n/** Change frame pushed on every committed ledger mutation. */\nexport type ChangeEvent = {\n revision: number\n kind: 'task-created' | 'task-updated' | 'task-moved' | 'task-deleted' | 'comment-added' | 'execution-recorded' | 'settings-updated' | 'ledger-replaced'\n tasks: TaskSummary[]\n}"],"mappings":";;AAYA,MAAa,eAAe;;AAG5B,MAAa,WAAW"}
|
|
1
|
+
{"version":3,"file":"api.js","names":[],"sources":["../../src/shared/api.ts"],"sourcesContent":["/**\n * Wire contract for the /taskboard host routes: the JSON envelope,\n * request/response shapes, and SSE event payloads shared by the host routes\n * and the browser client.\n *\n * @module dsh-taskboard/shared/api\n */\nimport type { BoardSettings, TaskLedger, TaskModel, TaskRecord, TaskSummary } from './protocol.ts'\n\nexport type { TaskModel, TaskRecord }\n\n/** Route prefix on the shared DSH webserver (same origin as the GUI). */\nexport const ROUTE_PREFIX = '/dsh-taskboard'\n\n/** SSE stream path (exact route; longest-prefix wins keep it disjoint). */\nexport const SSE_PATH = '/dsh-taskboard/events'\n\n/** Stable error codes (mirror the tool-level codes plus HTTP mapping). */\nexport type ApiErrorCode =\n | 'invalid_input'\n | 'not_found'\n | 'version_conflict'\n | 'invalid_transition'\n | 'forbidden'\n | 'internal'\n\n/** Success envelope. */\nexport type ApiOk<T> = { ok: true; value: T }\n\n/** Failure envelope. */\nexport type ApiFail = { ok: false; error: { code: ApiErrorCode; message: string } }\n\n/** The envelope either way. */\nexport type ApiResult<T> = ApiOk<T> | ApiFail\n\n// ---------------------------------------------------------------------------\n// payloads\n// ---------------------------------------------------------------------------\n\n/** Full-state response (the reconnect baseline after an SSE gap). */\nexport type StateResponse = TaskLedger & { capabilities?: { archiveSessions: boolean } }\n\n/**\n * Workspace listing for the UI pickers. `repoCount` (0.6.3): how many repos a\n * task mirror of this workspace would cover (root repo + nested) — the form's\n * worktree option shows the mirror badge when it exceeds 1.\n */\nexport type WorkspaceView = { id: string; path: string; title: string; sessionCount: number; gitAvailable?: boolean; repoCount?: number }\n\n/** Create-task request body (actor is always the GUI user). */\nexport type CreateTaskBody = {\n title: string\n workspaceId: string\n urgency: string\n description?: string\n prompt?: string\n execution?: { mode?: string; cron?: string }\n model?: TaskModel\n /** Code isolation for executions ('worktree' | 'none'); omitted = default. */\n isolation?: string\n /** Agent preset for execution sessions; omitted = deployment default. */\n presetId?: string\n /** Execution permission preset ('workspace-write' | 'read-only' | 'danger-full-access'); omitted = default. */\n permission?: string\n /** Acceptance checklist item texts (host mints ids, all unchecked). */\n checklist?: string[]\n}\n\n/** Update-task request body (ifVersion mandatory). */\nexport type UpdateTaskBody = {\n ifVersion: number\n title?: string\n description?: string\n prompt?: string\n urgency?: string\n blocked?: boolean\n /** Rebind the task to another project (GUI owner surface only). */\n workspaceId?: string\n execution?: { mode?: string; cron?: string }\n model?: TaskModel | null\n /** Change isolation; locked once the task has execution history. */\n isolation?: string\n /** Change the execution preset (takes effect on the next run). */\n presetId?: string | null\n /** Change the execution permission (0.5.5; 'workspace-write' | 'read-only' | 'danger-full-access'). */\n permission?: string | null\n /** Replace the whole checklist (GUI owner surface); null clears it. */\n checklist?: unknown\n}\n\n/** Move-task request body (ifVersion mandatory; the user MAY move to done). */\nexport type MoveTaskBody = { ifVersion: number; status: string; archiveSessions?: boolean }\n\nexport type SessionArchiveResult = { archived: string[]; failed: Array<{ sessionId: string; error: string }>; unsupported: string[] }\nexport type MoveTaskResponse = TaskSummary & { sessionArchive?: SessionArchiveResult }\n\n/**\n * Quick-reject request body (card ✗ button): move back to todo plus an\n * optional user comment, committed as ONE ledger mutation so a failed move\n * can never strand an orphan comment.\n */\nexport type RejectTaskBody = { ifVersion: number; body?: string }\n\n/** Comment request body. */\nexport type CommentBody = { body: string }\n\n/** One content-addressed image uploaded outside the ledger. */\nexport type AttachmentUpload = {\n id: string\n name: string\n size: number\n url: string\n extension: 'png' | 'jpg' | 'gif' | 'webp'\n mime: 'image/png' | 'image/jpeg' | 'image/gif' | 'image/webp'\n}\n\n/** Delete request body (purge=true physically removes a trashed task). */\nexport type DeleteTaskBody = { ifVersion?: number; purge?: boolean }\n\n/** Run request body; `reuse: true` = 续跑 (keep a live worktree as-is). */\nexport type RunTaskBody = { reuse?: boolean }\n\n/** One repo's merge outcome in a multi-repo merge (0.6.3; `repo: ''` = the workspace root repo). */\nexport type MergeRepoResult = {\n repo: string\n branch: string\n outcome: 'merged' | 'noop' | 'failed'\n /** Failure reason (verbatim git message) when outcome = 'failed'. */\n error?: string\n}\n\n/**\n * Merge outcome. Legacy single-repo tasks keep the flat shape; multi-repo\n * mirror tasks (0.6.3) additionally return per-repo results — merges run\n * sequentially and a failed repo does not block the others (plan §4.5).\n */\nexport type MergeBranchResponse = {\n merged: boolean\n noop?: boolean\n /** The merged task branch (legacy single-repo shape; multi-repo responses omit it). */\n branch?: string\n /** Present only on multi-repo mirror merges (0.6.3). */\n results?: MergeRepoResult[]\n}\n\n/** Remove a task's worktree; optionally delete its branch too. */\nexport type WorktreeRemoveBody = { deleteBranch?: boolean }\n\n/** One orphan worktree directory (exists on disk, owned by no live task). */\nexport type OrphanWorktree = { workspaceId: string; workspacePath: string; taskId: string; path: string }\n\n/** A git-enabled workspace whose .gitignore does not cover the worktree dir. */\nexport type GitignoreSuggestion = { workspaceId: string; workspacePath: string }\n\n/** Health-diagnostics response (⚙ panel). */\nexport type DiagnosticsResponse = {\n revision: number\n tasks: number\n /** Executions currently marked `running`. */\n staleRunning: number\n /** Worktree directories whose task no longer exists in the ledger. */\n orphanWorktrees: OrphanWorktree[]\n /** Git workspaces whose .gitignore does not ignore the worktree dir. */\n gitIgnoreSuggestions: GitignoreSuggestion[]\n}\n\n/** Fields a task template may prefill (0.4.0). */\nexport type TaskTemplateSpec = {\n title?: string\n description?: string\n prompt?: string\n urgency?: string\n execution?: { mode?: string; cron?: string }\n model?: TaskModel\n isolation?: string\n presetId?: string\n /** Execution permission preset (0.5.5). */\n permission?: string\n /** Checklist item texts (host mints ids at create time). */\n checklist?: string[]\n}\n\n/** One reusable task template (0.4.0). */\nexport type TaskTemplate = {\n id: string\n name: string\n task: TaskTemplateSpec\n /** Seeded built-in templates (kept on load, deletable like any other). */\n builtin?: boolean\n createdAt: number\n updatedAt: number\n}\n\n/** Templates listing response. */\nexport type TemplatesResponse = { templates: TaskTemplate[] }\n\n/** Board-settings response (0.5.0; absent fields follow factory defaults). */\nexport type SettingsResponse = BoardSettings\n\n/** Current host-side location of all durable taskboard data. */\nexport type StorageStatus = {\n currentDirectory: string\n defaultDirectory: string\n isDefault: boolean\n configured: boolean\n writable: boolean\n assetCount: number\n assetBytes: number\n checkedDirectory?: string\n error?: string\n}\n\n/** Completed storage relocation, including non-fatal old-file cleanup failures. */\nexport type StorageMigrationResult = StorageStatus & { migrated: boolean; warnings: string[] }\n\n/** Update-board-settings request body (0.5.0; whole-object replace semantics). */\nexport type UpdateSettingsBody = {\n /** Default code isolation for NEW tasks ('worktree' | 'none'). */\n defaultIsolation?: string\n /** Automatically capture external workspace sessions into the taskboard. */\n syncExternalSessions?: boolean\n /** Default permission preset for NEW tasks ('workspace-write' | 'read-only' | 'danger-full-access'). */\n defaultPermission?: string\n}\n\n/** Prompt completion item for skills and slash commands (0.5.5). */\nexport type PromptCompletionItem = {\n name: string\n kind: 'skill' | 'command'\n description?: string\n hint?: string\n}\n\n/** Prompt completions response (0.5.5). */\nexport type PromptCompletionsResponse = {\n commands: PromptCompletionItem[]\n skills: PromptCompletionItem[]\n}\n\n/** Model item in catalog (0.5.5). */\nexport type CatalogModelItem = {\n provider: string\n model: string\n name?: string\n description?: string\n reasoning?: {\n efforts: Array<{ id: string; name: string; description?: string }>\n defaultEffort?: string\n }\n}\n\n/** Preset item in catalog (0.5.5). */\nexport type CatalogPresetItem = {\n id: string\n name?: string\n}\n\n/** Model and preset catalog response (0.5.5). */\nexport type ModelCatalogResponse = {\n models: CatalogModelItem[]\n presets: CatalogPresetItem[]\n defaultPresetId?: string\n}\n\n/** Import dry-run response (0.4.0): every task classified, nothing written. */\nexport type ImportPreviewResponse = {\n plan: {\n create: Array<{ id: string; title: string; status: string }>\n overwrite: Array<{ id: string; title: string; status: string }>\n invalid: Array<{ id?: string; reason: string }>\n }\n}\n\n/** Import commit response. */\nexport type ImportCommitResponse = {\n mode: 'merge' | 'replace'\n created: number\n overwritten: number\n replacedTotal?: number\n /** The backup file written BEFORE a replace wiped the live ledger. */\n backupFile?: string\n}\n\n/** Diff-viewer response (0.4.0). */\nexport type DiffResponse = { diff: string; truncated: boolean }\n\n/** One task (full record) response. */\nexport type TaskResponse = TaskRecord\n\n/** Summary response used by list-ish endpoints. */\nexport type SummaryResponse = { tasks: TaskSummary[] }\n\n// ---------------------------------------------------------------------------\n// SSE\n// ---------------------------------------------------------------------------\n\n/** Change frame pushed on every committed ledger mutation. */\nexport type ChangeEvent = {\n revision: number\n kind: 'task-created' | 'task-updated' | 'task-moved' | 'task-deleted' | 'comment-added' | 'execution-recorded' | 'settings-updated' | 'ledger-replaced'\n tasks: TaskSummary[]\n}\n"],"mappings":";;AAYA,MAAa,eAAe;;AAG5B,MAAa,WAAW"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
//#region src/shared/builtin-templates.ts
|
|
2
|
+
/** Stable ids of the factory templates (persisted in the side file). */
|
|
3
|
+
const BUILTIN_TEMPLATE_IDS = [
|
|
4
|
+
"tpl-feature",
|
|
5
|
+
"tpl-bugfix",
|
|
6
|
+
"tpl-release",
|
|
7
|
+
"tpl-patrol"
|
|
8
|
+
];
|
|
9
|
+
/** Built-in template content per locale (name + prefilled task spec). */
|
|
10
|
+
const BUILTIN_TEMPLATE_CONTENT = {
|
|
11
|
+
zh: {
|
|
12
|
+
"tpl-feature": {
|
|
13
|
+
name: "新增功能",
|
|
14
|
+
task: {
|
|
15
|
+
title: "新增:",
|
|
16
|
+
prompt: [
|
|
17
|
+
"实现以上新功能并按序交接:",
|
|
18
|
+
"1. 明确需求边界与验收标准,列出实现要点",
|
|
19
|
+
"2. 实现功能(含类型定义与错误处理)",
|
|
20
|
+
"3. 补充测试(单测/回归)",
|
|
21
|
+
"4. 运行相关测试套件确认通过"
|
|
22
|
+
].join("\n"),
|
|
23
|
+
urgency: "normal",
|
|
24
|
+
checklist: [
|
|
25
|
+
"实现要点已明确(需求边界与验收标准)",
|
|
26
|
+
"功能已实现并补充测试",
|
|
27
|
+
"相关测试套件通过"
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"tpl-bugfix": {
|
|
32
|
+
name: "Bug 修复",
|
|
33
|
+
task: {
|
|
34
|
+
title: "修复:",
|
|
35
|
+
prompt: [
|
|
36
|
+
"修复以上问题并按序交接:",
|
|
37
|
+
"1. 复现问题(写最小复现步骤或测试)",
|
|
38
|
+
"2. 定位根因,说明为什么会发生",
|
|
39
|
+
"3. 修复并补回归测试",
|
|
40
|
+
"4. 运行相关测试套件确认无回归"
|
|
41
|
+
].join("\n"),
|
|
42
|
+
urgency: "urgent",
|
|
43
|
+
checklist: [
|
|
44
|
+
"已复现并定位根因",
|
|
45
|
+
"修复已提交到任务分支",
|
|
46
|
+
"回归测试通过"
|
|
47
|
+
]
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
"tpl-release": {
|
|
51
|
+
name: "发布检查",
|
|
52
|
+
task: {
|
|
53
|
+
title: "发布:",
|
|
54
|
+
prompt: "执行发布流程:版本号更新、构建、测试、变更记录,完成后按序交接(不要实际推送/发布,等用户确认)。",
|
|
55
|
+
urgency: "normal",
|
|
56
|
+
checklist: [
|
|
57
|
+
"版本号已更新(package.json 与版本常量同步)",
|
|
58
|
+
"构建通过",
|
|
59
|
+
"全部测试通过",
|
|
60
|
+
"变更记录已写"
|
|
61
|
+
]
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
"tpl-patrol": {
|
|
65
|
+
name: "例行巡检",
|
|
66
|
+
task: {
|
|
67
|
+
title: "巡检:",
|
|
68
|
+
prompt: [
|
|
69
|
+
"例行巡检:检查依赖更新、失败测试、明显代码问题与未处理的告警。",
|
|
70
|
+
"发现的问题逐条列出(严重度/位置/建议),小问题直接修复,大问题只报告不动手。",
|
|
71
|
+
"输出巡检摘要(用 {{lastComments}} 可回看上次巡检结论)。"
|
|
72
|
+
].join("\n"),
|
|
73
|
+
urgency: "relaxed",
|
|
74
|
+
execution: {
|
|
75
|
+
mode: "scheduled",
|
|
76
|
+
cron: "0 9 * * 1"
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
en: {
|
|
82
|
+
"tpl-feature": {
|
|
83
|
+
name: "New feature",
|
|
84
|
+
task: {
|
|
85
|
+
title: "New feature:",
|
|
86
|
+
prompt: [
|
|
87
|
+
"Implement the new feature above and hand off in order:",
|
|
88
|
+
"1. Clarify the requirement boundaries and acceptance criteria; list the implementation points",
|
|
89
|
+
"2. Implement the feature (including type definitions and error handling)",
|
|
90
|
+
"3. Add tests (unit / regression)",
|
|
91
|
+
"4. Run the relevant test suites and confirm they pass"
|
|
92
|
+
].join("\n"),
|
|
93
|
+
urgency: "normal",
|
|
94
|
+
checklist: [
|
|
95
|
+
"Implementation points clarified (requirement boundaries and acceptance criteria)",
|
|
96
|
+
"Feature implemented with tests added",
|
|
97
|
+
"Relevant test suites pass"
|
|
98
|
+
]
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
"tpl-bugfix": {
|
|
102
|
+
name: "Bug fix",
|
|
103
|
+
task: {
|
|
104
|
+
title: "Fix:",
|
|
105
|
+
prompt: [
|
|
106
|
+
"Fix the issue above and hand off in order:",
|
|
107
|
+
"1. Reproduce the issue (write minimal reproduction steps or a test)",
|
|
108
|
+
"2. Locate the root cause and explain why it happens",
|
|
109
|
+
"3. Fix it and add regression tests",
|
|
110
|
+
"4. Run the relevant test suites and confirm there are no regressions"
|
|
111
|
+
].join("\n"),
|
|
112
|
+
urgency: "urgent",
|
|
113
|
+
checklist: [
|
|
114
|
+
"Issue reproduced and root cause located",
|
|
115
|
+
"Fix committed to the task branch",
|
|
116
|
+
"Regression tests pass"
|
|
117
|
+
]
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
"tpl-release": {
|
|
121
|
+
name: "Release check",
|
|
122
|
+
task: {
|
|
123
|
+
title: "Release:",
|
|
124
|
+
prompt: "Run the release process: bump the version, build, test, and update the changelog, then hand off in order (do not actually push / publish — wait for user confirmation).",
|
|
125
|
+
urgency: "normal",
|
|
126
|
+
checklist: [
|
|
127
|
+
"Version bumped (package.json and version constants in sync)",
|
|
128
|
+
"Build passes",
|
|
129
|
+
"All tests pass",
|
|
130
|
+
"Changelog written"
|
|
131
|
+
]
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
"tpl-patrol": {
|
|
135
|
+
name: "Routine patrol",
|
|
136
|
+
task: {
|
|
137
|
+
title: "Patrol:",
|
|
138
|
+
prompt: [
|
|
139
|
+
"Routine patrol: check dependency updates, failing tests, obvious code issues, and unhandled alerts.",
|
|
140
|
+
"List each finding (severity / location / suggestion); fix small issues directly, and only report (do not touch) large ones.",
|
|
141
|
+
"Output a patrol summary (use {{lastComments}} to review the last patrol’s conclusions)."
|
|
142
|
+
].join("\n"),
|
|
143
|
+
urgency: "relaxed",
|
|
144
|
+
execution: {
|
|
145
|
+
mode: "scheduled",
|
|
146
|
+
cron: "0 9 * * 1"
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
//#endregion
|
|
153
|
+
export { BUILTIN_TEMPLATE_CONTENT, BUILTIN_TEMPLATE_IDS };
|
|
154
|
+
|
|
155
|
+
//# sourceMappingURL=builtin-templates.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"builtin-templates.js","names":[],"sources":["../../src/shared/builtin-templates.ts"],"sourcesContent":["/**\n * Built-in task-template content (0.6.4): the four factory templates seeded\n * into the template side file, in both shipped locales. The host seeds the zh\n * copy as the persisted fallback (the side file is plain data); the client\n * resolves the ACTIVE locale's copy at render / prefill time via\n * {@link builtinTemplateContent}, so an English GUI shows English template\n * text without ever re-seeding the side file.\n *\n * Kept in shared/ (not the client i18n dictionaries) because the content is\n * structured task DATA — a checklist is an array and the prompt is multi-line\n * — rather than UI-chrome strings, and because the host needs the same seed\n * content to write the side file.\n *\n * @module dsh-taskboard/shared/builtin-templates\n */\nimport type { TaskTemplateSpec } from './api.ts'\n\n/** Shipped built-in template locales (mirrors the client i18n LocaleId). */\nexport type BuiltinTemplateLocale = 'zh' | 'en'\n\n/** One built-in template's localized name + task spec. */\nexport interface BuiltinTemplateContent {\n name: string\n task: TaskTemplateSpec\n}\n\n/** Stable ids of the factory templates (persisted in the side file). */\nexport const BUILTIN_TEMPLATE_IDS = ['tpl-feature', 'tpl-bugfix', 'tpl-release', 'tpl-patrol'] as const\n\nexport type BuiltinTemplateId = (typeof BUILTIN_TEMPLATE_IDS)[number]\n\n/** Built-in template content per locale (name + prefilled task spec). */\nexport const BUILTIN_TEMPLATE_CONTENT: Readonly<Record<BuiltinTemplateLocale, Readonly<Record<BuiltinTemplateId, BuiltinTemplateContent>>>> = {\n zh: {\n 'tpl-feature': {\n name: '新增功能',\n task: {\n title: '新增:',\n prompt: [\n '实现以上新功能并按序交接:',\n '1. 明确需求边界与验收标准,列出实现要点',\n '2. 实现功能(含类型定义与错误处理)',\n '3. 补充测试(单测/回归)',\n '4. 运行相关测试套件确认通过',\n ].join('\\n'),\n urgency: 'normal',\n checklist: ['实现要点已明确(需求边界与验收标准)', '功能已实现并补充测试', '相关测试套件通过'],\n },\n },\n 'tpl-bugfix': {\n name: 'Bug 修复',\n task: {\n title: '修复:',\n prompt: [\n '修复以上问题并按序交接:',\n '1. 复现问题(写最小复现步骤或测试)',\n '2. 定位根因,说明为什么会发生',\n '3. 修复并补回归测试',\n '4. 运行相关测试套件确认无回归',\n ].join('\\n'),\n urgency: 'urgent',\n checklist: ['已复现并定位根因', '修复已提交到任务分支', '回归测试通过'],\n },\n },\n 'tpl-release': {\n name: '发布检查',\n task: {\n title: '发布:',\n prompt: '执行发布流程:版本号更新、构建、测试、变更记录,完成后按序交接(不要实际推送/发布,等用户确认)。',\n urgency: 'normal',\n checklist: ['版本号已更新(package.json 与版本常量同步)', '构建通过', '全部测试通过', '变更记录已写'],\n },\n },\n 'tpl-patrol': {\n name: '例行巡检',\n task: {\n title: '巡检:',\n prompt: [\n '例行巡检:检查依赖更新、失败测试、明显代码问题与未处理的告警。',\n '发现的问题逐条列出(严重度/位置/建议),小问题直接修复,大问题只报告不动手。',\n '输出巡检摘要(用 {{lastComments}} 可回看上次巡检结论)。',\n ].join('\\n'),\n urgency: 'relaxed',\n execution: { mode: 'scheduled', cron: '0 9 * * 1' },\n },\n },\n },\n en: {\n 'tpl-feature': {\n name: 'New feature',\n task: {\n title: 'New feature:',\n prompt: [\n 'Implement the new feature above and hand off in order:',\n '1. Clarify the requirement boundaries and acceptance criteria; list the implementation points',\n '2. Implement the feature (including type definitions and error handling)',\n '3. Add tests (unit / regression)',\n '4. Run the relevant test suites and confirm they pass',\n ].join('\\n'),\n urgency: 'normal',\n checklist: ['Implementation points clarified (requirement boundaries and acceptance criteria)', 'Feature implemented with tests added', 'Relevant test suites pass'],\n },\n },\n 'tpl-bugfix': {\n name: 'Bug fix',\n task: {\n title: 'Fix:',\n prompt: [\n 'Fix the issue above and hand off in order:',\n '1. Reproduce the issue (write minimal reproduction steps or a test)',\n '2. Locate the root cause and explain why it happens',\n '3. Fix it and add regression tests',\n '4. Run the relevant test suites and confirm there are no regressions',\n ].join('\\n'),\n urgency: 'urgent',\n checklist: ['Issue reproduced and root cause located', 'Fix committed to the task branch', 'Regression tests pass'],\n },\n },\n 'tpl-release': {\n name: 'Release check',\n task: {\n title: 'Release:',\n prompt: 'Run the release process: bump the version, build, test, and update the changelog, then hand off in order (do not actually push / publish — wait for user confirmation).',\n urgency: 'normal',\n checklist: ['Version bumped (package.json and version constants in sync)', 'Build passes', 'All tests pass', 'Changelog written'],\n },\n },\n 'tpl-patrol': {\n name: 'Routine patrol',\n task: {\n title: 'Patrol:',\n prompt: [\n 'Routine patrol: check dependency updates, failing tests, obvious code issues, and unhandled alerts.',\n 'List each finding (severity / location / suggestion); fix small issues directly, and only report (do not touch) large ones.',\n 'Output a patrol summary (use {{lastComments}} to review the last patrol\\u2019s conclusions).',\n ].join('\\n'),\n urgency: 'relaxed',\n execution: { mode: 'scheduled', cron: '0 9 * * 1' },\n },\n },\n },\n}\n\n/**\n * Resolve one built-in template's localized content.\n * @param id - the template id.\n * @param locale - the requested locale.\n * @returns the content, or undefined when `id` is not a built-in template id.\n */\nexport function builtinTemplateContent(id: string, locale: BuiltinTemplateLocale): BuiltinTemplateContent | undefined {\n if (!(BUILTIN_TEMPLATE_IDS as readonly string[]).includes(id)) return undefined\n return BUILTIN_TEMPLATE_CONTENT[locale][id as BuiltinTemplateId]\n}\n"],"mappings":";;AA2BA,MAAa,uBAAuB;CAAC;CAAe;CAAc;CAAe;AAAY;;AAK7F,MAAa,2BAAiI;CAC5I,IAAI;EACF,eAAe;GACb,MAAM;GACN,MAAM;IACJ,OAAO;IACP,QAAQ;KACN;KACA;KACA;KACA;KACA;IACF,CAAC,CAAC,KAAK,IAAI;IACX,SAAS;IACT,WAAW;KAAC;KAAsB;KAAc;IAAU;GAC5D;EACF;EACA,cAAc;GACZ,MAAM;GACN,MAAM;IACJ,OAAO;IACP,QAAQ;KACN;KACA;KACA;KACA;KACA;IACF,CAAC,CAAC,KAAK,IAAI;IACX,SAAS;IACT,WAAW;KAAC;KAAY;KAAc;IAAQ;GAChD;EACF;EACA,eAAe;GACb,MAAM;GACN,MAAM;IACJ,OAAO;IACP,QAAQ;IACR,SAAS;IACT,WAAW;KAAC;KAAgC;KAAQ;KAAU;IAAQ;GACxE;EACF;EACA,cAAc;GACZ,MAAM;GACN,MAAM;IACJ,OAAO;IACP,QAAQ;KACN;KACA;KACA;IACF,CAAC,CAAC,KAAK,IAAI;IACX,SAAS;IACT,WAAW;KAAE,MAAM;KAAa,MAAM;IAAY;GACpD;EACF;CACF;CACA,IAAI;EACF,eAAe;GACb,MAAM;GACN,MAAM;IACJ,OAAO;IACP,QAAQ;KACN;KACA;KACA;KACA;KACA;IACF,CAAC,CAAC,KAAK,IAAI;IACX,SAAS;IACT,WAAW;KAAC;KAAoF;KAAwC;IAA2B;GACrK;EACF;EACA,cAAc;GACZ,MAAM;GACN,MAAM;IACJ,OAAO;IACP,QAAQ;KACN;KACA;KACA;KACA;KACA;IACF,CAAC,CAAC,KAAK,IAAI;IACX,SAAS;IACT,WAAW;KAAC;KAA2C;KAAoC;IAAuB;GACpH;EACF;EACA,eAAe;GACb,MAAM;GACN,MAAM;IACJ,OAAO;IACP,QAAQ;IACR,SAAS;IACT,WAAW;KAAC;KAA+D;KAAgB;KAAkB;IAAmB;GAClI;EACF;EACA,cAAc;GACZ,MAAM;GACN,MAAM;IACJ,OAAO;IACP,QAAQ;KACN;KACA;KACA;IACF,CAAC,CAAC,KAAK,IAAI;IACX,SAAS;IACT,WAAW;KAAE,MAAM;KAAa,MAAM;IAAY;GACpD;EACF;CACF;AACF"}
|
package/lib/shared/protocol.js
CHANGED
|
@@ -363,6 +363,30 @@ function syncClaim(task, to, now, holder) {
|
|
|
363
363
|
}
|
|
364
364
|
}
|
|
365
365
|
/**
|
|
366
|
+
* Collect unique execution session IDs associated with a task:
|
|
367
|
+
* - executions with a non-empty `sessionId`
|
|
368
|
+
* Creator and claim sessions may serve other tasks and are never included.
|
|
369
|
+
* @param task - the task record to inspect.
|
|
370
|
+
* @returns an array of distinct session IDs in stable discovery order.
|
|
371
|
+
*/
|
|
372
|
+
function taskAssociatedSessionIds(task) {
|
|
373
|
+
const seen = /* @__PURE__ */ new Set();
|
|
374
|
+
const result = [];
|
|
375
|
+
const push = (raw) => {
|
|
376
|
+
if (typeof raw === "string") {
|
|
377
|
+
const trimmed = raw.trim();
|
|
378
|
+
if (trimmed.length > 0 && !seen.has(trimmed)) {
|
|
379
|
+
seen.add(trimmed);
|
|
380
|
+
result.push(trimmed);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
};
|
|
384
|
+
if (Array.isArray(task.executions)) {
|
|
385
|
+
for (const ex of task.executions) if (ex !== null && typeof ex === "object") push(ex.sessionId);
|
|
386
|
+
}
|
|
387
|
+
return result;
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
366
390
|
* Validate and normalize a pinned model: `{ provider, model, reasoningEffort? }`,
|
|
367
391
|
* provider and model must be non-empty trimmed strings.
|
|
368
392
|
* @param raw - the raw input.
|
|
@@ -579,7 +603,20 @@ function validateImportedTask(raw, now) {
|
|
|
579
603
|
body,
|
|
580
604
|
version: numOr(ce, "version", 1),
|
|
581
605
|
createdAt: numOr(ce, "createdAt", now),
|
|
582
|
-
...typeof ce.threadId === "string" ? { threadId: ce.threadId } : {}
|
|
606
|
+
...typeof ce.threadId === "string" ? { threadId: ce.threadId } : {},
|
|
607
|
+
...typeof ce.systemKey === "string" && /^sys\.[A-Za-z0-9]+$/.test(ce.systemKey) && ce.systemKey.length <= 100 ? {
|
|
608
|
+
systemKey: ce.systemKey,
|
|
609
|
+
...typeof ce.systemParams === "object" && ce.systemParams !== null && !Array.isArray(ce.systemParams) ? { systemParams: Object.fromEntries(Object.entries(ce.systemParams).filter(([key, value]) => key.length <= 100 && typeof value === "string" && value.length <= 4e3).slice(0, 20)) } : {},
|
|
610
|
+
...Array.isArray(ce.systemRows) ? { systemRows: ce.systemRows.filter((row) => typeof row === "object" && row !== null && typeof row.repo === "string" && (row.repo === "" || isValidRelRepoPath(row.repo)) && [
|
|
611
|
+
"merged",
|
|
612
|
+
"noop",
|
|
613
|
+
"failed"
|
|
614
|
+
].includes(row.outcome) && (row.error === void 0 || typeof row.error === "string")).slice(0, 8).map((row) => ({
|
|
615
|
+
repo: row.repo,
|
|
616
|
+
outcome: row.outcome,
|
|
617
|
+
...row.error !== void 0 ? { error: row.error.slice(0, 4e3) } : {}
|
|
618
|
+
})) } : {}
|
|
619
|
+
} : {}
|
|
583
620
|
});
|
|
584
621
|
}
|
|
585
622
|
else return fail("comments must be an array");
|
|
@@ -743,6 +780,6 @@ function summarize(task) {
|
|
|
743
780
|
};
|
|
744
781
|
}
|
|
745
782
|
//#endregion
|
|
746
|
-
export { ALL_STATUSES, DEFAULT_ISOLATION, DEFAULT_PERMISSION, MAIN_STATUSES, SECONDARY_STATUSES, URGENCIES, asBoardSettings, asIsolation, asPermission, asStatus, asUrgency, canTransition, checklistFromTexts, checklistProgress, defaultIsolationOf, defaultPermissionOf, defaultSyncExternalSessionsOf, effectiveIsolation, effectivePrompt, emptyLedger, isClaim, isClaimedBy, isPlausibleTaskRecord, isValidRelRepoPath, isValidTaskId, newChecklistItemId, newCommentId, newExecutionId, newTaskId, nextCronTime, normalizeBody, normalizeBranchesMap, normalizeChecklist, normalizeChecklistText, normalizeExecution, normalizeExecutionReport, normalizeModel, normalizePrompt, normalizeRepoEvidence, normalizeTitle, parseCron, pruneExecutions, summarize, syncClaim, validateImportedTask, validateLedgerImport };
|
|
783
|
+
export { ALL_STATUSES, DEFAULT_ISOLATION, DEFAULT_PERMISSION, MAIN_STATUSES, SECONDARY_STATUSES, URGENCIES, asBoardSettings, asIsolation, asPermission, asStatus, asUrgency, canTransition, checklistFromTexts, checklistProgress, defaultIsolationOf, defaultPermissionOf, defaultSyncExternalSessionsOf, effectiveIsolation, effectivePrompt, emptyLedger, isClaim, isClaimedBy, isPlausibleTaskRecord, isValidRelRepoPath, isValidTaskId, newChecklistItemId, newCommentId, newExecutionId, newTaskId, nextCronTime, normalizeBody, normalizeBranchesMap, normalizeChecklist, normalizeChecklistText, normalizeExecution, normalizeExecutionReport, normalizeModel, normalizePrompt, normalizeRepoEvidence, normalizeTitle, parseCron, pruneExecutions, summarize, syncClaim, taskAssociatedSessionIds, validateImportedTask, validateLedgerImport };
|
|
747
784
|
|
|
748
785
|
//# sourceMappingURL=protocol.js.map
|