@co0ontty/wand 4.57.0 → 4.58.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/dist/build-info.json +3 -3
- package/dist/ensure-node-pty-helper.d.ts +0 -1
- package/dist/ensure-node-pty-helper.js +0 -5
- package/dist/git-quick-commit.js +18 -19
- package/dist/github-connector.d.ts +11 -5
- package/dist/github-connector.js +105 -33
- package/dist/missions.d.ts +1 -1
- package/dist/missions.js +17 -4
- package/dist/server-file-routes.js +43 -2
- package/dist/server-github-routes.js +50 -53
- package/dist/server-mission-routes.js +11 -10
- package/dist/server-task-routes.d.ts +14 -2
- package/dist/server-task-routes.js +248 -84
- package/dist/server-workspace-routes.js +90 -16
- package/dist/server.js +2 -20
- package/dist/storage.d.ts +23 -6
- package/dist/storage.js +130 -53
- package/dist/structured-exec-host.d.ts +13 -2
- package/dist/structured-exec-host.js +71 -36
- package/dist/structured-exec-pump.d.ts +1 -1
- package/dist/structured-exec-pump.js +14 -8
- package/dist/task-types.d.ts +15 -0
- package/dist/terminal-daemon-client.d.ts +2 -0
- package/dist/terminal-daemon-client.js +7 -1
- package/dist/terminal-daemon-server.js +17 -18
- package/dist/tui/ipc-server.js +3 -1
- package/dist/types.d.ts +1 -0
- package/dist/wand-task-sync.d.ts +9 -0
- package/dist/wand-task-sync.js +50 -0
- package/dist/web-ui/content/scripts.js +85 -85
- package/dist/web-ui/content/styles.css +1 -1
- package/dist/web-ui/embedded-assets.js +2 -2
- package/dist/ws-broadcast.js +5 -1
- package/package.json +2 -3
- package/dist/taskboard-bridge.d.ts +0 -11
- package/dist/taskboard-bridge.js +0 -371
- package/vendor/codex-taskboard/LICENSE +0 -201
- package/vendor/codex-taskboard/dist/web/assets/AiChat-BNn4VJ5a.js +0 -12
- package/vendor/codex-taskboard/dist/web/assets/GanttView-D8wnfGrG.css +0 -1
- package/vendor/codex-taskboard/dist/web/assets/GanttView-rGWhuEX9.js +0 -39
- package/vendor/codex-taskboard/dist/web/assets/abnfDiagram-N423BO3Z-DbFG6lbA.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/arc-BjSQqbzd.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/architecture-TIHT7OUA-C9-t8p4o.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/architectureDiagram-T3A2C74G-Db4kJZwO.js +0 -36
- package/vendor/codex-taskboard/dist/web/assets/array-BifhSqXX.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/blockDiagram-VBNYF7ZC-C-BywuWV.js +0 -132
- package/vendor/codex-taskboard/dist/web/assets/c4Diagram-5PPSVZJV-Dq-80rjP.js +0 -10
- package/vendor/codex-taskboard/dist/web/assets/channel-C7mnz8Xc.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/chunk-2GRJ4B5K-B2G57us9.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/chunk-2Q5K7J3B-C1jixKkw.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/chunk-3NCLNEKW-DVGIRib_.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/chunk-4I5QYGJK-kNoGFqr0.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/chunk-5RXB4S5H-4iv6-xM6.js +0 -231
- package/vendor/codex-taskboard/dist/web/assets/chunk-5VM5RSS4-ZNzvKenW.js +0 -15
- package/vendor/codex-taskboard/dist/web/assets/chunk-6Q2QTUOP-1J34vxHr.js +0 -88
- package/vendor/codex-taskboard/dist/web/assets/chunk-7BUUIJ7U-Bb538aSH.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/chunk-7Z6QIM7H-CDQpQlxX.js +0 -10
- package/vendor/codex-taskboard/dist/web/assets/chunk-GF5L2VYU-Dg92oHHl.js +0 -206
- package/vendor/codex-taskboard/dist/web/assets/chunk-I66GZJ75-Bk8_gFs2.js +0 -125
- package/vendor/codex-taskboard/dist/web/assets/chunk-J7OUQ5F2-CJRcenlr.js +0 -2
- package/vendor/codex-taskboard/dist/web/assets/chunk-JQJVKLGR-BLBV_Sus.js +0 -156
- package/vendor/codex-taskboard/dist/web/assets/chunk-JWPE2WC7-DVXcaiue.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/chunk-KBJHAD2P-Da9Yjz0p.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/chunk-KEIR6QF5-Dj-OpFgW.js +0 -161
- package/vendor/codex-taskboard/dist/web/assets/chunk-NSK5VX7P-Cb56tnGK.js +0 -2
- package/vendor/codex-taskboard/dist/web/assets/chunk-QR6OTTB3-BwoszTxn.js +0 -62
- package/vendor/codex-taskboard/dist/web/assets/chunk-RYQCIY6F-D_L2RdcQ.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/chunk-UBXNYLIW-YX5mN8cl.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/chunk-W5SLKNZC-DlOLhhDO.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/chunk-WRU74C26-B7SBmP0K.js +0 -70
- package/vendor/codex-taskboard/dist/web/assets/chunk-XXDRQBXY-BuE3VzE_.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/chunk-Y2CYZVJY-DsF7k-Jl.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/chunk-ZIRB5QZD-C6fEPe3t.js +0 -32
- package/vendor/codex-taskboard/dist/web/assets/classDiagram-JCYQIIEL-KIdCVvbA.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/classDiagram-v2-OCEON4UE-KIdCVvbA.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/cose-bilkent-JH36ORCC-BzQvy5TC.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/cynefin-VYW2F7L2-D1_5O1XR.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/cynefinDiagram-MW4NZA55-DOW_U5QN.js +0 -62
- package/vendor/codex-taskboard/dist/web/assets/cytoscape.esm-DnGd5Fuo.js +0 -321
- package/vendor/codex-taskboard/dist/web/assets/dagre-VZM6K2ZE-0Dntc3NT.js +0 -4
- package/vendor/codex-taskboard/dist/web/assets/dagre-dpRSp0QF.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/defaultLocale-C8Fc0cco.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/diagram-7IWD3JNH-D7GleYAM.js +0 -30
- package/vendor/codex-taskboard/dist/web/assets/diagram-B4RE2ZJO-DCXTe6q5.js +0 -3
- package/vendor/codex-taskboard/dist/web/assets/diagram-LBJQPF4R-ZFJyXEXl.js +0 -24
- package/vendor/codex-taskboard/dist/web/assets/diagram-Q27KOJAE-DUGCuGd9.js +0 -24
- package/vendor/codex-taskboard/dist/web/assets/diagram-UB23O5K3-DRmp4Nng.js +0 -41
- package/vendor/codex-taskboard/dist/web/assets/dist-D9sYb5Oa.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/ebnfDiagram-BXEA7PRR-CkKoaf7T.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/erDiagram-JOGREHBK-nBX5z4R4.js +0 -85
- package/vendor/codex-taskboard/dist/web/assets/eventmodeling-45OFAUF4-pexmK65N.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/flowDiagram-UKHOOZJN-aroohRbL.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/ganttDiagram-PKOTCBZU-BOJWUTXa.js +0 -292
- package/vendor/codex-taskboard/dist/web/assets/gitGraph-TEB2WS4Q-DxnuhSZU.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/gitGraphDiagram-DS77QQ5N-Cte975TC.js +0 -106
- package/vendor/codex-taskboard/dist/web/assets/graphlib-DS17s2tU.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/index-Docmx4uQ.css +0 -1
- package/vendor/codex-taskboard/dist/web/assets/index-Du07lEGz.js +0 -121
- package/vendor/codex-taskboard/dist/web/assets/info-DKCQHKI2-DOTjgYxk.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/infoDiagram-6WML65LV-SpyV9UJd.js +0 -2
- package/vendor/codex-taskboard/dist/web/assets/init-D6jRqBbL.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/ishikawaDiagram-WSZJBQD7-T0PZwoUZ.js +0 -70
- package/vendor/codex-taskboard/dist/web/assets/journeyDiagram-NVQOT4AX-B88b8UgX.js +0 -139
- package/vendor/codex-taskboard/dist/web/assets/kanban-definition-27J2QSJJ-CIlfNnl_.js +0 -89
- package/vendor/codex-taskboard/dist/web/assets/katex-B7rAX3Vi.js +0 -257
- package/vendor/codex-taskboard/dist/web/assets/line-fWrwPZhB.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/linear-B7l8qgEw.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/map-BaFkSB1l.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/mermaid-parser.core-C5gls2nC.js +0 -7
- package/vendor/codex-taskboard/dist/web/assets/mermaid.core-POz2Xcvr.js +0 -11
- package/vendor/codex-taskboard/dist/web/assets/mindmap-definition-FAOFIHXS-BM0-egZ5.js +0 -96
- package/vendor/codex-taskboard/dist/web/assets/ordinal-hYBb2elL.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/packet-7NZHBO7P-DQZzyVos.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/path-BWPyau1x.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/pegDiagram-VL7TDLO6-B8aEwl-7.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/pie-RZYD4A2V-DRUflilR.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/pieDiagram-7S7Q4E2Y-0cDDxlua.js +0 -39
- package/vendor/codex-taskboard/dist/web/assets/purify.es-Bwt4ulIK.js +0 -3
- package/vendor/codex-taskboard/dist/web/assets/quadrantDiagram-CIZ2JOQS-CBgb0YZ0.js +0 -7
- package/vendor/codex-taskboard/dist/web/assets/radar-I7S5WNFK-tRocoKxq.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/railroad-3IZDKUUU-DaS4ZlX_.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/railroad-abnf-AHOZXSZD-BN1T5dBT.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/railroad-ebnf-EBAXGLYW-Cuf8qyKs.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/railroad-peg-LSFZ7HO6-B511_EYq.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/railroadDiagram-AXF67PYL-QtPo-JZb.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/requirementDiagram-LRYGKXZP-CuBIF85r.js +0 -84
- package/vendor/codex-taskboard/dist/web/assets/rolldown-runtime-QTnfLwEv.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/rough.esm-CSKSodPl.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/sankeyDiagram-W5VNT64P-CRA-b_za.js +0 -40
- package/vendor/codex-taskboard/dist/web/assets/sequenceDiagram-SI44F4Z6-CoddVWwY.js +0 -162
- package/vendor/codex-taskboard/dist/web/assets/sizeCapture-X5ZJPWSS-B0uUizjq.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/src-_wZywoZs.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/stateDiagram-OKZ733FA-D8DMM2ln.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/stateDiagram-v2-UEYNNEHI-CVp0eYAS.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/swimlanes-SLNWSIFB-C1fC7EFu.js +0 -2
- package/vendor/codex-taskboard/dist/web/assets/swimlanesDiagram-ULZ7WXOC-CPd58Xso.js +0 -8
- package/vendor/codex-taskboard/dist/web/assets/timeline-definition-Z64GVDOM-BUBv8aKP.js +0 -120
- package/vendor/codex-taskboard/dist/web/assets/treeView-QDETBFTQ-C-Bve2kY.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/treemap-6X3UGDF4-BcphHDdV.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/vennDiagram-T6HMQDX7-0wQnwAyA.js +0 -34
- package/vendor/codex-taskboard/dist/web/assets/wardley-OPB4EBWU-CJZ4BevB.js +0 -1
- package/vendor/codex-taskboard/dist/web/assets/wardleyDiagram-T6FBY63Y-U9nwPrkN.js +0 -78
- package/vendor/codex-taskboard/dist/web/assets/xychartDiagram-ELKLHX3M-CyCqnzgE.js +0 -7
- package/vendor/codex-taskboard/dist/web/codex-agent-logo.png +0 -0
- package/vendor/codex-taskboard/dist/web/codex-app-icon.png +0 -0
- package/vendor/codex-taskboard/dist/web/favicon.svg +0 -7
- package/vendor/codex-taskboard/dist/web/index.html +0 -20
- package/vendor/codex-taskboard/server/ai-chat-catalog.mjs +0 -1031
- package/vendor/codex-taskboard/server/ai-chat-process.mjs +0 -462
- package/vendor/codex-taskboard/server/ai-chat.mjs +0 -1237
- package/vendor/codex-taskboard/server/ai-turn-owner.mjs +0 -39
- package/vendor/codex-taskboard/server/app.mjs +0 -3211
- package/vendor/codex-taskboard/server/cloud-config.mjs +0 -188
- package/vendor/codex-taskboard/server/cloud-proxy.mjs +0 -310
- package/vendor/codex-taskboard/server/codex-app-server.mjs +0 -376
- package/vendor/codex-taskboard/server/codex-slash-commands-0.139.0.json +0 -62
- package/vendor/codex-taskboard/server/database.mjs +0 -2845
- package/vendor/codex-taskboard/server/index.mjs +0 -41
- package/vendor/codex-taskboard/server/jira-config.mjs +0 -181
- package/vendor/codex-taskboard/server/jira-integration.mjs +0 -487
- package/vendor/codex-taskboard/server/project-summary.mjs +0 -159
- package/vendor/codex-taskboard/shared/api-fields.mjs +0 -163
- package/vendor/codex-taskboard/shared/codex-environment.mjs +0 -5
- package/vendor/codex-taskboard/shared/codex-executable.mjs +0 -77
- package/vendor/codex-taskboard/shared/codex-permissions.mjs +0 -9
- package/vendor/codex-taskboard/shared/composer-reference.d.mts +0 -19
- package/vendor/codex-taskboard/shared/composer-reference.mjs +0 -90
- package/vendor/codex-taskboard/shared/domain.mjs +0 -34
- package/vendor/codex-taskboard/shared/executable-command.mjs +0 -13
- package/vendor/codex-taskboard/shared/process-tree.mjs +0 -27
- package/vendor/codex-taskboard/shared/task-input.mjs +0 -308
- package/vendor/codex-taskboard/shared/task-records.mjs +0 -199
- package/vendor/codex-taskboard/shared/task-relations.mjs +0 -55
- package/vendor/codex-taskboard/shared/taskboard-automation.mjs +0 -277
- package/vendor/codex-taskboard/skills/manage-taskboard/SKILL.md +0 -45
- package/vendor/codex-taskboard/skills/manage-taskboard/agents/openai.yaml +0 -4
- package/vendor/codex-taskboard/skills/manage-taskboard/references/cli.md +0 -211
|
@@ -1,277 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
import { fileURLToPath } from "node:url";
|
|
3
|
-
|
|
4
|
-
const taskctlCliPath = fileURLToPath(new URL("../cli/taskctl.mjs", import.meta.url));
|
|
5
|
-
|
|
6
|
-
const AUTOMATION_OPERATIONS = new Set(["ensure-active", "pause", "list", "apply-policy"]);
|
|
7
|
-
const INTERVAL_MINUTES = new Set([5, 10, 15, 30, 60]);
|
|
8
|
-
const HOST_REQUEST_FIELDS = new Set([
|
|
9
|
-
"id",
|
|
10
|
-
"action",
|
|
11
|
-
"requestId",
|
|
12
|
-
"operation",
|
|
13
|
-
"taskboardProjectId",
|
|
14
|
-
"codexProjectId",
|
|
15
|
-
"codexProjectKind",
|
|
16
|
-
"codexHostId",
|
|
17
|
-
"projectName",
|
|
18
|
-
"workspacePath",
|
|
19
|
-
"remoteProjects",
|
|
20
|
-
"skillPath",
|
|
21
|
-
"automationId",
|
|
22
|
-
"enabledByUser",
|
|
23
|
-
"quotaAware",
|
|
24
|
-
"intervalMinutes",
|
|
25
|
-
"model",
|
|
26
|
-
"reasoningEffort",
|
|
27
|
-
]);
|
|
28
|
-
|
|
29
|
-
export function parseTaskboardAutomationHostRequest(value) {
|
|
30
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
31
|
-
if (Object.keys(value).some((field) => !HOST_REQUEST_FIELDS.has(field))) return null;
|
|
32
|
-
if (value.action !== "automation") return null;
|
|
33
|
-
if (!validIdentifier(value.id, 80) || !validIdentifier(value.requestId, 100)) return null;
|
|
34
|
-
if (!AUTOMATION_OPERATIONS.has(value.operation)) return null;
|
|
35
|
-
if (!validProjectId(value.taskboardProjectId)) return null;
|
|
36
|
-
if (!validText(value.codexProjectId, 256) || !validText(value.projectName, 200)) return null;
|
|
37
|
-
const codexProjectKind = value.codexProjectKind ?? "local";
|
|
38
|
-
const codexHostId = value.codexHostId ?? "local";
|
|
39
|
-
if (codexProjectKind !== "local" && codexProjectKind !== "remote") return null;
|
|
40
|
-
if (!validText(codexHostId, 256)) return null;
|
|
41
|
-
if (codexProjectKind === "local" && codexHostId !== "local") return null;
|
|
42
|
-
if (codexProjectKind === "remote" && codexHostId === "local") return null;
|
|
43
|
-
if (!validAbsolutePath(value.workspacePath) || !validAbsolutePath(value.skillPath)) return null;
|
|
44
|
-
const remoteProjects = value.remoteProjects === undefined ? [] : value.remoteProjects;
|
|
45
|
-
if (
|
|
46
|
-
!Array.isArray(remoteProjects)
|
|
47
|
-
|| remoteProjects.some((project) => (
|
|
48
|
-
!project
|
|
49
|
-
|| typeof project !== "object"
|
|
50
|
-
|| Array.isArray(project)
|
|
51
|
-
|| Object.keys(project).some((field) => ![
|
|
52
|
-
"codexProjectId",
|
|
53
|
-
"codexProjectKind",
|
|
54
|
-
"codexHostId",
|
|
55
|
-
"workspacePath",
|
|
56
|
-
].includes(field))
|
|
57
|
-
|| !validText(project.codexProjectId, 256)
|
|
58
|
-
|| project.codexProjectKind !== "remote"
|
|
59
|
-
|| project.codexHostId !== codexHostId
|
|
60
|
-
|| !validAbsolutePath(project.workspacePath)
|
|
61
|
-
))
|
|
62
|
-
|| (codexProjectKind === "local" && remoteProjects.length > 0)
|
|
63
|
-
) return null;
|
|
64
|
-
if (!INTERVAL_MINUTES.has(value.intervalMinutes)) return null;
|
|
65
|
-
if (!validText(value.model, 256) || !validText(value.reasoningEffort, 100)) return null;
|
|
66
|
-
if (value.automationId !== undefined && !validText(value.automationId, 256)) return null;
|
|
67
|
-
if (typeof value.enabledByUser !== "boolean" || typeof value.quotaAware !== "boolean") return null;
|
|
68
|
-
|
|
69
|
-
return {
|
|
70
|
-
id: value.id,
|
|
71
|
-
action: "automation",
|
|
72
|
-
requestId: value.requestId,
|
|
73
|
-
operation: value.operation,
|
|
74
|
-
taskboardProjectId: value.taskboardProjectId,
|
|
75
|
-
codexProjectId: value.codexProjectId,
|
|
76
|
-
codexProjectKind,
|
|
77
|
-
codexHostId,
|
|
78
|
-
projectName: value.projectName,
|
|
79
|
-
workspacePath: value.workspacePath,
|
|
80
|
-
...(value.remoteProjects === undefined ? {} : { remoteProjects }),
|
|
81
|
-
skillPath: value.skillPath,
|
|
82
|
-
...(value.automationId === undefined ? {} : { automationId: value.automationId }),
|
|
83
|
-
enabledByUser: value.enabledByUser,
|
|
84
|
-
quotaAware: value.quotaAware,
|
|
85
|
-
intervalMinutes: value.intervalMinutes,
|
|
86
|
-
model: value.model,
|
|
87
|
-
reasoningEffort: value.reasoningEffort,
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
export function buildTaskboardAutomationName(request) {
|
|
92
|
-
return `Taskboard 自动认领 · ${request.taskboardProjectId}`;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export function buildTaskboardAutomationPrompt(request) {
|
|
96
|
-
const taskctlCommand = buildTaskctlCommand(request);
|
|
97
|
-
const remoteProject = request.codexProjectKind === "remote";
|
|
98
|
-
const remoteProjects = request.remoteProjects ?? [];
|
|
99
|
-
const candidateInstructions = [
|
|
100
|
-
"从返回的 todo 中只选择依赖已完成的议题:relations.blockedBy 为空,或其中每个依赖的 status 都严格等于 done。无依赖的 todo 仍可并行处理。若有 todo 但全部被未完成依赖阻塞,本轮直接结束,不暂停自动化,也不创建或打开新的任务会话。",
|
|
101
|
-
"每次仅处理一个符合依赖条件的 todo:选定后先用 issue get 读取最新议题内容,并用 comment list 读取全部评论。根据描述和最新评论判断是否允许开始;若其中写明等待、暂不执行或当前不应开始,立即跳过并报告,不改状态。评论也包含已完成后被打回的返工要求。",
|
|
102
|
-
"完成 issue get 和 comment list 后、移动状态前,必须再次运行 issue get,并复核 relations.blockedBy 仍为空或其中每个依赖的 status 都严格等于 done。若依赖条件不再满足,立即跳过并结束本轮,不改状态,也不暂停自动化。",
|
|
103
|
-
];
|
|
104
|
-
const executionInstructions = remoteProject
|
|
105
|
-
? [
|
|
106
|
-
`本自动化仅在本机作为任务面板控制器运行;实际开发必须派发到 Codex SSH 远程项目。导入项目的基础 identity 是 projectId=${JSON.stringify(request.codexProjectId)}、hostId=${JSON.stringify(request.codexHostId)}、workspacePath=${JSON.stringify(request.workspacePath)};同一保存主机当前可用的精确远程项目映射是 ${JSON.stringify(remoteProjects)}。不要在当前本地自动化会话修改项目文件。`,
|
|
107
|
-
...candidateInstructions,
|
|
108
|
-
"先检查 issue get 的 projectId、version、status、archivedAt、threadId 和 threadBinding。完整 threadBinding 包含 threadId、codexProjectId、codexProjectKind、codexHostId、workspacePath,且它是该议题后续 send、wait 和状态写回的唯一目标;当前自动化的项目和主机只能作为未绑定议题的首次目标,不能替换已有绑定。若存在 threadId 但没有完整 threadBinding,这是只能由 UI 打开的 legacy local 绑定:使用 comment add 说明自动化无法确认项目和主机,再使用首次读取的 version 作为 --if-version、用 --binding-thread-id 保留原 threadId 将议题移动到 blocked;若冲突立即停止。不得 send、create 或覆盖该绑定。",
|
|
109
|
-
`未绑定议题必须先从上述精确远程项目映射解析 actualTarget。若 developmentContext.type 是 worktree,只保留 codexProjectKind="remote"、codexHostId=${JSON.stringify(request.codexHostId)} 且 workspacePath 与 developmentContext.path 完全相同的项;必须恰好命中一项,并使用该项自己的 codexProjectId、codexHostId 和 workspacePath。零项或多项时使用 comment add 明确记录“目标 SSH worktree 未映射”,随后结束本轮,不认领、不 create、不写基础项目 binding。若没有 worktree,actualTarget 才是上述基础 identity,并且它必须存在于精确映射中。不得回退到基础 root、local、项目名、其他主机或同路径的其他主机。`,
|
|
110
|
-
"确认允许开始后,只有未绑定且仍为未归档 todo 的议题才可在读取代码、下载附件、分析或实施前,由当前本地控制器使用刚读取的 version 移到 in_progress。已有完整 threadBinding 时,issue move 必须同时传 --binding-thread-id、--binding-codex-project-id、--binding-codex-project-kind、--binding-codex-host-id、--binding-workspace-path 的保存值,但在旧会话 send/stale 判断完成前不得把这个 todo 移到 in_progress;stale 清除步骤按后文显式使用 --clear-binding-thread。未绑定时必须传 --clear-binding-thread,避免把本地控制器 CODEX_THREAD_ID 写成任务绑定。写入成功后记录响应 task 的 version 为 ownedVersion、projectId 为 ownedProjectId,并记录本轮 binding;以后本轮每次 issue move 都必须显式传 --if-version ownedVersion,成功后再用响应 version 更新 ownedVersion。不得省略 --if-version 后让 taskctl 自动读取最新 version。写入成功前不得继续。所有认领、评论和状态写入只由当前本地控制器完成,不得要求远程会话运行 taskctl。",
|
|
111
|
-
"若因 version 陈旧发生版本冲突,重新运行 issue get 和 comment list;仅当仍为可认领 todo、绑定身份未变化、未归档且描述和最新评论未变化时,用最新 version 重试一次。若已被认领、绑定、状态或要求已变、已归档、服务或永久 API 错误,或重试仍失败,立即跳过该议题、退出并报告;不得抢占或循环重试。",
|
|
112
|
-
"认领成功后,已有完整 threadBinding 时,只能使用其保存的 threadId 和 codexHostId 调用 Codex send_message_to_thread。send 成功后必须重新 issue get 一次,确认 projectId 未变、未归档、status 仍为 todo 且完整 threadBinding 与保存值完全相同;然后由当前本地控制器使用这次复核返回的最新 version、完整旧 binding 和 --if-version 执行 issue move --status in_progress,传入 --binding-thread-id、--binding-codex-project-id、--binding-codex-project-kind、--binding-codex-host-id、--binding-workspace-path,并记录响应 task.version 为 ownedVersion。认领成功后继续执行后文现有 Codex wait_threads、结果评论和 in_review 写回路径,不得结束本轮;若认领发生 409,立即停止,不得重读新 version 覆盖。只有旧会话工具明确返回终态 NOT_FOUND 或 CLOSED 等会话不存在或已关闭结果时,才确认 stale。timeout、network failure、Codex host 暂时不可达或 Taskboard service unavailable 都不是 stale:保留 binding 并结束本轮,不得猜测、clear、create 或抢占。若任务已是 in_progress、活跃、已归档、状态或 binding 已变化,立即停止,不得在当前自动化目标创建替代会话。只有未绑定议题才使用 Codex create_thread 创建远程任务,target 必须是 {type:\"project\",projectId:actualTarget.codexProjectId,environment:{type:\"local\"}},首次 identity 必须使用 actualTarget 的 projectId、kind=\"remote\"、hostId 和 workspacePath。发送给远程会话的指令必须包含议题编号、标题、完整描述、全部评论和开发上下文,并说明远程会话不运行 taskctl,只需完成实现、验证并返回改动、结果和剩余风险。",
|
|
113
|
-
"确认旧会话 stale 后,必须先用 comment add 保存一条历史记录,并同时传 --thread-id、--binding-thread-id、--binding-codex-project-id、--binding-codex-project-kind、--binding-codex-host-id 和 --binding-workspace-path 的完整旧 binding;评论写入成功后,再使用同一次 issue get 的 version 执行 issue move --status todo --clear-binding-thread --if-version。评论或清除失败立即停止,不得认领。然后只重新 issue get 一次;仅当 projectId 未变、未归档、status 仍为 todo、threadId 为空且 threadBinding 为空时,才进入未绑定议题的现有认领和 create_thread 路径。",
|
|
114
|
-
"仅当 send_message_to_thread 成功,或 create_thread 成功返回远程 threadId,才视为远程 worker 已确认。未绑定议题在 create_thread 失败时,使用 comment add 记录失败工具和错误;随后用 ownedVersion、显式 --if-version 和 --clear-binding-thread 将当前议题移回 todo 并结束。若发生 409,说明其他控制端已修改任务,立即停止且不得重读最新 version 后覆盖。此补偿只处理本轮当前已认领议题;不得扫描或接管其他 in_progress。",
|
|
115
|
-
"新建远程任务成功后,使用 ownedVersion 和显式 --if-version 再次移动到 in_progress;必须用完整 binding 参数保存 create_thread 返回的 threadId,以及 actualTarget 的 projectId、kind=\"remote\"、hostId 和 workspacePath。成功后用响应 version 更新 ownedVersion 和本轮 binding。若请求响应丢失或结果不确定,只允许重新 issue get 一次;仅当 projectId 等于 ownedProjectId、未归档、状态仍为本轮 in_progress,且 threadBinding 为空或与本轮五字段 binding 完全相同时才可继续。读到相同 binding 视为前次保存成功;读到空 binding 时才可用本次核对后的 version 重试一次;读到不同 binding 或任一其他核对项变化时立即退出,不得写回。若确定绑定写入失败,使用 comment add 记录失败和远程 threadId,再用 ownedVersion、显式 --if-version 和同一完整 binding 将议题移动到 blocked;409 时停止且不得重复派发。",
|
|
116
|
-
"使用 Codex wait_threads 等待远程会话时,目标必须使用任务保存的 threadBinding.threadId 和 threadBinding.codexHostId。wait_threads 失败、远程会话明确需要用户输入或无法继续时,使用 comment add 记录原因,再用 ownedVersion、显式 --if-version 和完整保存 binding 将议题移动到 blocked;409 时立即停止。远程会话完成后,使用 comment add 写入改动、验证结果、执行结果和剩余风险,再用 ownedVersion、显式 --if-version 和完整保存 binding 将议题移动到 in_review。worker 确认后的每一次 issue move 都必须显式传完整远程 binding;不要把未完成工作标记为 in_review。",
|
|
117
|
-
]
|
|
118
|
-
: [
|
|
119
|
-
...candidateInstructions,
|
|
120
|
-
`确认允许开始后,只有 threadId 和 threadBinding 都为空且仍为未归档 todo 的议题才可在读取代码、下载附件、分析或实施前认领。认领必须使用刚读取的 version 移到 in_progress,并显式传 --binding-thread-id "$CODEX_THREAD_ID"、--binding-codex-project-id ${JSON.stringify(request.codexProjectId)}、--binding-codex-project-kind "local"、--binding-codex-host-id ${JSON.stringify(request.codexHostId)}、--binding-workspace-path ${JSON.stringify(request.workspacePath)},把当前自动化会话一次写成完整 binding;记录响应 task.version 为 ownedVersion。写入成功前不得继续。已有完整 binding 或 legacy local binding 的议题必须先按旧会话规则处理,不得先认领;不得认领已被其他会话绑定或其他 Agent 领取的议题。认领后的每一次 issue move 都必须显式传 ownedVersion 和这五个完整 binding 字段,成功后更新 ownedVersion。`,
|
|
121
|
-
"若因 version 陈旧发生版本冲突,重新运行 issue get 和 comment list;仅当仍为可认领 todo、未绑定其他会话、未归档且描述和最新评论未变化时,用最新 version 重试一次。若已被认领、状态或要求已变、已归档、服务或永久 API 错误,或重试仍失败,立即跳过该议题、退出并报告;不得抢占或循环重试。",
|
|
122
|
-
`若首次 issue get 返回完整 threadBinding,议题已绑定原会话:不要在当前自动化会话认领;只能使用保存的 threadId 和 codexHostId 调用 Codex send_message_to_thread。send 成功时保留 binding 并结束本轮;只有工具明确返回终态 NOT_FOUND 或 CLOSED 等会话不存在或已关闭结果时才确认 stale。timeout、network failure、Codex host 暂时不可达或 Taskboard service unavailable 都保留 binding 并结束本轮,不得猜测 stale。确认 stale 后,先用 comment add 同时传 --thread-id 和完整旧 binding 保存历史,再用同一次 issue get 的 version 执行 issue move --status todo --clear-binding-thread --if-version;然后只重新 issue get 一次,仍为未归档 todo 且 threadId、threadBinding 都为空时,才在当前自动化会话处理。若任务已是 in_progress、活跃、已归档、状态或 binding 已变化,或发生 409,立即停止,不得抢占。若返回 threadId 但没有完整 threadBinding,这是 legacy local 绑定:先调用 Codex list_threads(limit=50),合并 pinnedThreads 与 threads,并按完整 threadId 精确查找。只有恰好一项 kind="codex"、projectId=${JSON.stringify(request.codexProjectId)}、hostId=${JSON.stringify(request.codexHostId)}、cwd=${JSON.stringify(request.workspacePath)} 全部一致时,才把该项视为可核验旧会话;使用最新 issue version 执行 issue move --status todo --if-version,并显式传旧 threadId 及上述 projectId、kind="local"、hostId、workspacePath 五字段,将 legacy local 原位升级为完整 binding。升级成功后只向该旧 threadId 和 hostId 调用 send_message_to_thread,随后结束本轮,由旧会话按议题最新要求继续。若 list_threads 未找到、出现多项或任一字段不一致,不得迁移或发送;使用 comment add 记录实际不一致项,再用首次读取的 version 和 --if-version、--binding-thread-id 保留原 threadId 将议题移到 blocked。若升级发生 409,立即停止,不得用新 version 覆盖。若没有 threadId,则按未绑定议题处理。`,
|
|
123
|
-
"若议题已绑定 branch 或 worktree,必须在该议题绑定的开发上下文执行,避免并行 Agent 修改同一工作目录。",
|
|
124
|
-
"执行完成并验证后,先用 comment add 记录关键改动、验证结果、执行结果和剩余风险,再使用 ownedVersion、显式 --if-version 和认领时保存的完整 binding 将议题移动到 in_review;成功后更新 ownedVersion。不要省略 binding,避免把完整绑定降级为 legacy local;不要直接标记为 done。",
|
|
125
|
-
];
|
|
126
|
-
return [
|
|
127
|
-
`[$manage-taskboard](${request.skillPath}) e-taskboard 每 ${request.intervalMinutes} 分钟检查任务面板中的「${request.projectName}」项目(项目 ID:${request.taskboardProjectId},项目目录:${request.workspacePath})。`,
|
|
128
|
-
`本轮所有 taskctl 操作都使用完整命令前缀 ${taskctlCommand},不要使用 PATH 中的 taskctl。`,
|
|
129
|
-
`开始时先运行 ${taskctlCommand} issue list --project ${request.taskboardProjectId} --status todo --json。若没有 todo,直接结束;Taskboard 主机侧会暂停当前自动化,不要创建或打开新的任务会话。`,
|
|
130
|
-
...executionInstructions,
|
|
131
|
-
`本次处理或交接后,再次运行 ${taskctlCommand} issue list --project ${request.taskboardProjectId} --status todo --json。若没有 todo,直接结束;Taskboard 主机侧会暂停当前自动化,避免后续创建空会话。`,
|
|
132
|
-
].join("\n");
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
function buildTaskctlCommand(request) {
|
|
136
|
-
const command = `${shellQuote(process.execPath)} ${shellQuote(taskctlCliPath)}`;
|
|
137
|
-
const runtimeFilePath = process.env.CODEX_TASKBOARD_RUNTIME_FILE;
|
|
138
|
-
return runtimeFilePath
|
|
139
|
-
? `${command} --runtime-file ${shellQuote(runtimeFilePath)}`
|
|
140
|
-
: command;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
function shellQuote(value) {
|
|
144
|
-
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
export function buildTaskboardAutomationSpec(request) {
|
|
148
|
-
return {
|
|
149
|
-
kind: "cron",
|
|
150
|
-
name: buildTaskboardAutomationName(request),
|
|
151
|
-
prompt: buildTaskboardAutomationPrompt(request),
|
|
152
|
-
projectId: request.codexProjectKind === "remote" ? null : request.codexProjectId,
|
|
153
|
-
executionEnvironment: "local",
|
|
154
|
-
localEnvironmentConfigPath: null,
|
|
155
|
-
model: request.model,
|
|
156
|
-
reasoningEffort: request.reasoningEffort,
|
|
157
|
-
rrule: `RRULE:FREQ=MINUTELY;INTERVAL=${request.intervalMinutes}`,
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
export function taskboardAutomationPolicyOperation(request, {
|
|
162
|
-
explicit,
|
|
163
|
-
hasTodo,
|
|
164
|
-
previousQuotaState,
|
|
165
|
-
quotaState,
|
|
166
|
-
currentStatus,
|
|
167
|
-
}) {
|
|
168
|
-
if (!request.enabledByUser) return "pause";
|
|
169
|
-
if (hasTodo === false) return "pause";
|
|
170
|
-
if (
|
|
171
|
-
!explicit
|
|
172
|
-
&& currentStatus === "PAUSED"
|
|
173
|
-
&& (!request.quotaAware || previousQuotaState === "available")
|
|
174
|
-
) return "list";
|
|
175
|
-
if (request.quotaAware && quotaState !== "available") return "pause";
|
|
176
|
-
return "ensure-active";
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
export async function reconcileTaskboardAutomation(request, rpc) {
|
|
180
|
-
const listed = await rpc("list-automations", {});
|
|
181
|
-
const items = Array.isArray(listed?.items) ? listed.items : [];
|
|
182
|
-
const name = buildTaskboardAutomationName(request);
|
|
183
|
-
const matchingItems = items.filter((item) => item?.name === name);
|
|
184
|
-
|
|
185
|
-
if (request.operation === "list") {
|
|
186
|
-
return { items: matchingItems.map(sanitizeAutomation).filter(Boolean) };
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
const existing = (
|
|
190
|
-
request.automationId
|
|
191
|
-
? matchingItems.find((item) => item?.id === request.automationId)
|
|
192
|
-
: null
|
|
193
|
-
) ?? matchingItems[0];
|
|
194
|
-
const spec = buildTaskboardAutomationSpec(request);
|
|
195
|
-
|
|
196
|
-
if (request.operation === "pause") {
|
|
197
|
-
if (!existing) return { error: "not-found" };
|
|
198
|
-
if (automationMatchesSpec(existing, spec, "PAUSED")) return { item: existing };
|
|
199
|
-
return rpc("automation-update", { ...spec, id: existing.id, status: "PAUSED" });
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
if (request.operation !== "ensure-active") {
|
|
203
|
-
throw new Error(`Unsupported automation operation: ${request.operation}`);
|
|
204
|
-
}
|
|
205
|
-
if (existing) {
|
|
206
|
-
if (automationMatchesSpec(existing, spec, "ACTIVE")) return { item: existing };
|
|
207
|
-
return rpc("automation-update", {
|
|
208
|
-
...spec,
|
|
209
|
-
id: existing.id,
|
|
210
|
-
status: "ACTIVE",
|
|
211
|
-
});
|
|
212
|
-
}
|
|
213
|
-
return rpc("automation-create", spec);
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
function sanitizeAutomation(item) {
|
|
217
|
-
if (
|
|
218
|
-
!validText(item?.id, 256)
|
|
219
|
-
|| (item.status !== "ACTIVE" && item.status !== "PAUSED")
|
|
220
|
-
|| !validText(item.model, 256)
|
|
221
|
-
|| !validText(item.reasoningEffort, 100)
|
|
222
|
-
|| !validRrule(item.rrule)
|
|
223
|
-
) return null;
|
|
224
|
-
return {
|
|
225
|
-
id: item.id,
|
|
226
|
-
status: item.status,
|
|
227
|
-
model: item.model,
|
|
228
|
-
reasoningEffort: item.reasoningEffort,
|
|
229
|
-
rrule: item.rrule,
|
|
230
|
-
...(
|
|
231
|
-
item.nextRunAt === null || Number.isFinite(item.nextRunAt)
|
|
232
|
-
? { nextRunAt: item.nextRunAt }
|
|
233
|
-
: {}
|
|
234
|
-
),
|
|
235
|
-
};
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
function validRrule(value) {
|
|
239
|
-
return typeof value === "string"
|
|
240
|
-
&& /^RRULE:FREQ=MINUTELY;INTERVAL=(5|10|15|30|60)$/.test(value);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
function automationMatchesSpec(item, spec, status) {
|
|
244
|
-
return item?.status === status
|
|
245
|
-
&& Object.entries(spec).every(([field, value]) => (
|
|
246
|
-
field === "projectId"
|
|
247
|
-
? (item.projectId ?? item.target?.projectId ?? null) === value
|
|
248
|
-
: item[field] === value
|
|
249
|
-
));
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
function validIdentifier(value, maxLength) {
|
|
253
|
-
return typeof value === "string"
|
|
254
|
-
&& value.length > 0
|
|
255
|
-
&& value.length <= maxLength
|
|
256
|
-
&& /^[a-z0-9-]+$/i.test(value);
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
function validProjectId(value) {
|
|
260
|
-
return typeof value === "string"
|
|
261
|
-
&& value.length > 0
|
|
262
|
-
&& value.length <= 128
|
|
263
|
-
&& /^[a-z0-9._-]+$/i.test(value);
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
function validText(value, maxLength) {
|
|
267
|
-
return typeof value === "string"
|
|
268
|
-
&& value.trim() === value
|
|
269
|
-
&& value.length > 0
|
|
270
|
-
&& value.length <= maxLength
|
|
271
|
-
&& !/[\u0000-\u001f\u007f]/.test(value);
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
function validAbsolutePath(value) {
|
|
275
|
-
return validText(value, 2_048)
|
|
276
|
-
&& (path.posix.isAbsolute(value) || path.win32.isAbsolute(value));
|
|
277
|
-
}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: manage-taskboard
|
|
3
|
-
description: Manage Codex Taskboard / e-taskboard work with taskctl. Use for taskboard issue IDs, status sync, comments, or taskctl cloud setup—not for unrelated product docs.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Manage Taskboard
|
|
7
|
-
|
|
8
|
-
Use `taskctl` for every project, issue, relation, and comment operation. Consume its JSON output. Use the exact issue identifier returned by the taskboard or supplied in the prompt. Never assume, derive, or rewrite an identifier prefix.
|
|
9
|
-
|
|
10
|
-
Open only the relevant section of [references/cli.md](references/cli.md) when command syntax is needed.
|
|
11
|
-
|
|
12
|
-
## Select the CLI and active service
|
|
13
|
-
|
|
14
|
-
- Use the exact `taskctl` binary and Taskboard URL supplied by the task or injected runtime. Do not replace them with a global CLI, the default port, or another board.
|
|
15
|
-
- On macOS, when no binary is injected and the desktop app is installed, use `'/Applications/Codex Taskboard.app/Contents/Resources/bin/taskctl' issue get ID --json`. Keep the single quotes because the path contains a space. The packaged wrapper reads the active launcher runtime; do not search the filesystem for another CLI or reconstruct the tokenized URL.
|
|
16
|
-
- On Linux, when no binary is injected and Codex was started by the desktop app, use `taskctl issue get ID --json`. The desktop app adds its packaged wrapper to the managed Codex `PATH`; do not search the filesystem for another CLI or reconstruct the tokenized URL.
|
|
17
|
-
- If that exact command reaches a sandbox restriction on the loopback service, retry the same command with the required permission. Do not switch binaries or endpoints.
|
|
18
|
-
|
|
19
|
-
## Terminology: local companion
|
|
20
|
-
|
|
21
|
-
In this product, **companion** means the **device-local loopback service** used for cloud mode (Codex/Git/Skill/MCP, path mapping, Basic Auth proxy). Related names: `local companion`, `loopback companion`, `CODEX_TASKBOARD_COMPANION_URL`, `cloud-companion.json`, `LOCAL_COMPANION_REQUIRED`.
|
|
22
|
-
|
|
23
|
-
When writing Chinese, keep the English word or use **本地 companion** / **本地配套服务** / **环回代理**. Never translate as **伴侣** or invent **伴侣 API**. Ordinary task/comment/attachment HTTP routes (`/api/tasks`, `/api/comments`, `/api/attachments`, …) are the **Taskboard HTTP API** (or local server API)—not “companion API”.
|
|
24
|
-
|
|
25
|
-
## Core workflow
|
|
26
|
-
|
|
27
|
-
1. For an existing issue, first run `issue get` and `comment list`. Also run `attachment list --task`. On the first handoff, omit `--after` and read the full results. Keep the separate `nextCursor` from each list. When the same task resumes, run `issue get` again, then pass each saved cursor to its matching list with `--after` so only new or modified entries are returned. Comment lists include the attachments on returned comments; use `attachment list --comment` with its own cursor when a known comment attachment list can grow. Read the description and latest comments before deciding whether to start. Treat comments as current requirements, including returned work. If they say to wait, not execute, or not start now, stop and report without changing the status.
|
|
28
|
-
2. Treat `backlog` as not approved for execution. Unless the user explicitly authorizes that issue, do not claim it, move it to another status, or perform task work; its assignee alone is not authorization. If work may start, claim it before reading code, downloading attachments, analyzing the implementation, or doing any other task work. Move a claimable `todo` to `in_progress` with its current `version`; do not continue until the move succeeds. If it is already `in_progress`, continue only when it is bound to the current conversation. Never move an issue claimed by another conversation.
|
|
29
|
-
3. If the move conflicts because the `version` is stale, run `issue get` and `comment list` again. Retry once with the latest `version` only when the issue is still a claimable `todo`, is not bound to another conversation, is not archived, and its description and latest comments are unchanged. If it was claimed, its status or requirements changed, it is archived, the service is unavailable, a permanent API error occurs, or the retry fails, stop and report. Never loop or take over another agent's claim.
|
|
30
|
-
4. For a new durable requirement, run `context current`. Treat its project as a workspace match only when `project.workspacePath` is the current directory or one of its ancestors. An unmatched `local` project is the documented fallback, not proof that the requirement belongs in the global project. If the user named a target project or the working directory identifies one, run `project list`, select that exact project by id or name, and stop to ask if the result is ambiguous. Search existing project issues before creating one in that confirmed project, then pass its explicit id to `issue create`. Update a matching issue instead of creating a duplicate. Use the fallback only when the user explicitly wants the global project. Do not track trivial requests.
|
|
31
|
-
5. Execute only the requested work in the issue's branch or worktree when one is bound.
|
|
32
|
-
6. Verify the requested operation path. Add a comment with the changes, verification result, outcome, and remaining risks. Read the issue again, then move it to `in_review` with its current `version`.
|
|
33
|
-
7. Move an issue to `done` only after the user explicitly accepts it or asks to complete it. Use `blocked` when work cannot continue and `canceled` when it will not continue.
|
|
34
|
-
|
|
35
|
-
## Other operations
|
|
36
|
-
|
|
37
|
-
- Run `taskctl project readme get [PROJECT_ID]` to inspect project architecture, constraints, and conventions before planning or executing complex tasks.
|
|
38
|
-
- Keep the project README focused on root overview and conventions; store detailed multi-page documentation in the local repository's `docs/` folder.
|
|
39
|
-
- Preserve existing issue scope when adding requirements or acceptance details.
|
|
40
|
-
- Add only relations that the work requires. Use parent for contained work, blocks or blocked_by for dependencies, and related for close association.
|
|
41
|
-
- Let `taskctl` read `CODEX_THREAD_ID` for controller attribution. Outside Codex, pass the exact conversation ID with `--thread-id`. This value alone is not a complete task binding.
|
|
42
|
-
- Any issue that the current conversation claims or continues must store a complete `threadBinding`: `threadId`, `codexProjectId`, `codexProjectKind`, `codexHostId`, and `workspacePath`. For an unbound local issue launched with injected Taskboard context, use the current `CODEX_THREAD_ID`, the injected project id and workspace path, `local` project kind, and `local` host id. Pass all five explicit `--binding-*` options on the claim and every later `issue move` that retains ownership. If any identity field is unavailable, stop before moving the issue to `in_progress`; never create a legacy binding containing only `threadId`.
|
|
43
|
-
- When an issue already has a complete `threadBinding`, preserve its exact five saved values on every status write. Do not rebuild or replace it from the current context, and never take over a binding owned by another conversation.
|
|
44
|
-
- Use the latest returned `version` with `--if-version` for concurrent updates. On conflict, read the issue again and reconcile before retrying.
|
|
45
|
-
- Download and inspect an inline `` image only when it is needed to understand the requirement.
|
|
@@ -1,211 +0,0 @@
|
|
|
1
|
-
# taskctl CLI
|
|
2
|
-
|
|
3
|
-
`taskctl` emits JSON for normal commands. Add `--json` when making the output contract explicit. Built-in help is the only successful stdout exception: it writes plain text, exits with code `0`, and does not request the Taskboard service.
|
|
4
|
-
|
|
5
|
-
Use built-in help for the current command tree or a specific supported level:
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
taskctl --help
|
|
9
|
-
taskctl issue --help
|
|
10
|
-
taskctl comment list --help
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
## Terminology: local companion
|
|
14
|
-
|
|
15
|
-
**Companion** here is a product term for the **device-local loopback HTTP service** that `taskctl` talks to in cloud mode. It applies Basic Authentication, stores device-only project path mappings, and keeps Codex/Git/Skill/MCP capabilities on the machine. It is not a chat persona and not a separate public “companion product API”.
|
|
16
|
-
|
|
17
|
-
| English | Prefer in Chinese | Do not use |
|
|
18
|
-
| --- | --- | --- |
|
|
19
|
-
| local companion / loopback companion | 本地 companion、本地配套服务、环回代理 | 伴侣、伴侣 API |
|
|
20
|
-
| Taskboard HTTP API (`/api/tasks`, `/api/comments`, `/api/attachments`, …) | Taskboard HTTP API、本地服务 API、附件上传接口 | companion API、伴侣 API |
|
|
21
|
-
|
|
22
|
-
Env and files that refer to this service: `CODEX_TASKBOARD_COMPANION_URL`, `CODEX_TASKBOARD_URL` (loopback origin), `.data/cloud-companion.json`. Error code `LOCAL_COMPANION_REQUIRED` means a capability needs that **local loopback service**, not a different API surface.
|
|
23
|
-
|
|
24
|
-
## Context and projects
|
|
25
|
-
|
|
26
|
-
```bash
|
|
27
|
-
taskctl context current [--cwd PATH] [--json]
|
|
28
|
-
taskctl project list [--json]
|
|
29
|
-
taskctl project create --name NAME [--id ID] [--workspace-path PATH] [--json]
|
|
30
|
-
taskctl project map PROJECT_ID --workspace-path PATH [--json]
|
|
31
|
-
taskctl project readme get [PROJECT_ID] [--json]
|
|
32
|
-
taskctl project readme set [PROJECT_ID] (--content TEXT | --file PATH) [--if-version N] [--json]
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
Use `--workspace-path` to associate a project with a local repository. `context current` chooses the most specific project whose workspace contains the current directory, then falls back to the `local` project.
|
|
36
|
-
|
|
37
|
-
Use `project readme get` and `project readme set` to read and update the project's single root README document. Detailed multi-page documentation belongs in the project's local `docs/` folder.
|
|
38
|
-
|
|
39
|
-
Set `CODEX_TASKBOARD_URL` to override the default local API origin, `http://127.0.0.1:47823`.
|
|
40
|
-
|
|
41
|
-
For a shared cloud board, keep `taskctl` pointed at the **loopback companion** (local loopback service; see Terminology above) and configure the upstream HTTPS origin through it:
|
|
42
|
-
|
|
43
|
-
```bash
|
|
44
|
-
taskctl cloud login --url HTTPS_ORIGIN --actor-name NAME [--json]
|
|
45
|
-
taskctl cloud status [--json]
|
|
46
|
-
taskctl project list [--json]
|
|
47
|
-
taskctl project map PROJECT_ID --workspace-path /absolute/local/path [--json]
|
|
48
|
-
taskctl cloud logout [--json]
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
`cloud login` reads the shared password from a private `Shared key:` prompt. The actor name is the display attribution sent through Basic Authentication. The local companion stores its configuration with mode `0600`; project mappings stay on the current device and can differ between collaborators. In cloud mode, failed upstream writes fail rather than falling back to or double-writing the local SQLite database.
|
|
52
|
-
|
|
53
|
-
Every issue or comment write must be attributed to a Codex conversation. In Codex, `taskctl` reads the current conversation from `CODEX_THREAD_ID`. Outside Codex, pass `--thread-id ID` explicitly. An explicit option takes precedence over the environment. Read commands do not require a conversation id.
|
|
54
|
-
|
|
55
|
-
Except for built-in help, every successful command writes one JSON object with `schemaVersion` to stdout. The current schema version is `2`. Errors write one JSON object to stderr. Exit codes are `0` for success, `2` for invalid input, `3` when the service is unavailable, `4` for API or response errors, and `5` for conflicts.
|
|
56
|
-
|
|
57
|
-
## Read issues
|
|
58
|
-
|
|
59
|
-
```bash
|
|
60
|
-
taskctl issue list [--project PROJECT_ID] [--status STATUS] [--archived true|false|all] [--json]
|
|
61
|
-
taskctl issue get ID [--json]
|
|
62
|
-
taskctl issue tree ID --direction descendants|ancestors --depth N [--json]
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
`issue tree` is a bounded structural read. `--depth 1` returns only direct children or the direct parent; larger values include that many levels, up to 25. The response is flat and deterministic: every node carries `id`, traversal `parentId`, `depth`, and `path` (usable as a breadcrumb), plus a small task summary. It never calculates status rollups or changes issues.
|
|
66
|
-
|
|
67
|
-
## Create issues
|
|
68
|
-
|
|
69
|
-
```bash
|
|
70
|
-
taskctl issue create \
|
|
71
|
-
--project PROJECT_ID \
|
|
72
|
-
--title TITLE \
|
|
73
|
-
[--description TEXT | --description-file FILE] \
|
|
74
|
-
[--status STATUS] \
|
|
75
|
-
[--priority PRIORITY] \
|
|
76
|
-
[--labels a,b] \
|
|
77
|
-
[--thread-id ID] \
|
|
78
|
-
[--git-branch BRANCH] \
|
|
79
|
-
[--worktree-path PATH] \
|
|
80
|
-
[--worktree-branch BRANCH] \
|
|
81
|
-
[--start-date YYYY-MM-DD] \
|
|
82
|
-
[--due-date YYYY-MM-DD] \
|
|
83
|
-
[--recurrence-interval N --recurrence-unit day|week|month|year] \
|
|
84
|
-
[--json]
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
Statuses are `backlog`, `todo`, `in_progress`, `in_review`, `blocked`, `done`, and `canceled`. Priorities are `none`, `urgent`, `high`, `medium`, and `low`.
|
|
88
|
-
|
|
89
|
-
Issues created through `taskctl` are assigned to Codex Agent by default. Other CLI writes preserve the existing assignee.
|
|
90
|
-
|
|
91
|
-
## Update issues
|
|
92
|
-
|
|
93
|
-
Read the issue immediately before a write and pass its `version` with `--if-version`.
|
|
94
|
-
|
|
95
|
-
```bash
|
|
96
|
-
taskctl issue update ID \
|
|
97
|
-
[--project PROJECT_ID] \
|
|
98
|
-
[--title TITLE] \
|
|
99
|
-
[--description TEXT | --description-file FILE] \
|
|
100
|
-
[--status STATUS] \
|
|
101
|
-
[--priority PRIORITY] \
|
|
102
|
-
[--labels a,b] \
|
|
103
|
-
[--thread-id ID] \
|
|
104
|
-
[--git-branch BRANCH] \
|
|
105
|
-
[--worktree-path PATH] \
|
|
106
|
-
[--worktree-branch BRANCH] \
|
|
107
|
-
[--start-date YYYY-MM-DD] \
|
|
108
|
-
[--due-date YYYY-MM-DD] \
|
|
109
|
-
[--recurrence-interval N --recurrence-unit day|week|month|year] \
|
|
110
|
-
[--if-version N] \
|
|
111
|
-
[--json]
|
|
112
|
-
|
|
113
|
-
taskctl issue move ID --status STATUS \
|
|
114
|
-
[--thread-id ID] \
|
|
115
|
-
[--binding-thread-id ID \
|
|
116
|
-
[--binding-codex-project-id PROJECT_ID \
|
|
117
|
-
--binding-codex-project-kind local|remote \
|
|
118
|
-
--binding-codex-host-id HOST_ID \
|
|
119
|
-
--binding-workspace-path PATH] \
|
|
120
|
-
| --clear-binding-thread] \
|
|
121
|
-
[--if-version N] [--json]
|
|
122
|
-
taskctl issue archive ID [--thread-id ID] [--if-version N] [--json]
|
|
123
|
-
taskctl issue restore ID [--thread-id ID] [--if-version N] [--json]
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
Use `issue move` to set `in_progress` before implementation and `in_review` after implementation and self-verification. Codex must not move work directly from `in_progress` to `done`; use `done` only after the user explicitly confirms acceptance or explicitly asks to mark the issue complete. Use `blocked` when work cannot continue and `canceled` when it will not continue. On a version conflict, fetch the issue again and reconcile before retrying.
|
|
127
|
-
|
|
128
|
-
`--thread-id` records the conversation performing the mutation; it does not create a complete task binding. `--binding-thread-id` can stand alone to preserve a legacy local binding. If any binding identity option is present, all four identity options are required. `--clear-binding-thread` conflicts with every `--binding-*` option. A conversation that claims or continues an issue must pass all five `--binding-*` options together. Reuse an existing complete binding exactly. For an unbound local issue launched with injected Taskboard context, use the current conversation id, injected project id and workspace path, `local` project kind, and `local` host id. Never leave an active issue with only a legacy local `threadId`. Use `--clear-binding-thread` only when the workflow explicitly requires an unbound issue.
|
|
129
|
-
|
|
130
|
-
Use either `--git-branch` or `--worktree-path`/`--worktree-branch`; an issue has only one development context. Issue JSON stores it as `developmentContext`, either `{ "type": "branch", "branch": "..." }` or `{ "type": "worktree", "path": "...", "branch": "..." }`. Its singular `threadId` is the Codex conversation that most recently created or changed the issue itself. Recurrence requires a due date.
|
|
131
|
-
|
|
132
|
-
Changing only `--project` preserves the issue's existing linked conversation.
|
|
133
|
-
|
|
134
|
-
## Issue relations
|
|
135
|
-
|
|
136
|
-
Read the anchor issue immediately before adding or removing a relation and use its current version. Relation writes require Codex conversation attribution like every other issue write.
|
|
137
|
-
|
|
138
|
-
```bash
|
|
139
|
-
taskctl issue relation add ISSUE_ID \
|
|
140
|
-
--type parent \
|
|
141
|
-
--issue PARENT_ISSUE_ID \
|
|
142
|
-
[--thread-id ID] \
|
|
143
|
-
[--if-version N] \
|
|
144
|
-
[--json]
|
|
145
|
-
|
|
146
|
-
taskctl issue relation add ISSUE_ID \
|
|
147
|
-
--type blocks|blocked_by|related \
|
|
148
|
-
--issue RELATED_ISSUE_ID \
|
|
149
|
-
[--thread-id ID] \
|
|
150
|
-
[--if-version N] \
|
|
151
|
-
[--json]
|
|
152
|
-
|
|
153
|
-
taskctl issue relation remove ISSUE_ID \
|
|
154
|
-
--type parent|blocks|blocked_by|related \
|
|
155
|
-
--issue RELATED_ISSUE_ID \
|
|
156
|
-
[--thread-id ID] \
|
|
157
|
-
[--if-version N] \
|
|
158
|
-
[--json]
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
For `--type parent`, `ISSUE_ID` is the child and `PARENT_ISSUE_ID` is its parent. Adding another parent replaces the child's current parent atomically. To add an existing issue as a sub-issue, anchor the command on the child and pass the exact parent identifier with `--issue PARENT_ISSUE_ID`.
|
|
162
|
-
|
|
163
|
-
For `blocks`, the anchor issue blocks the related issue. For `blocked_by`, the related issue blocks the anchor. `related` is symmetric. Self-relations, duplicates, and parent cycles are rejected. For compatibility, relation writes between different projects remain rejected for now; this is a temporary boundary, not the final hierarchy contract.
|
|
164
|
-
|
|
165
|
-
## Issue comments
|
|
166
|
-
|
|
167
|
-
Use the issue id to read or append comments. Comment updates and deletes require the latest comment `version` returned by `comment list`.
|
|
168
|
-
|
|
169
|
-
```bash
|
|
170
|
-
taskctl comment list ISSUE_ID [--after CURSOR] [--json]
|
|
171
|
-
taskctl comment add ISSUE_ID (--body TEXT | --body-file FILE) [--thread-id ID] [--json]
|
|
172
|
-
taskctl comment update COMMENT_ID --body TEXT --if-version N [--thread-id ID] [--json]
|
|
173
|
-
taskctl comment delete COMMENT_ID --if-version N [--thread-id ID] [--json]
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
Without `--after`, `comment list` returns the full list. Its response includes `nextCursor`; keep that value and pass it to the next read of the same issue to return only comments created or modified after that cursor. `--body-file` reads the UTF-8 file and passes its contents directly to the existing comment write path.
|
|
177
|
-
|
|
178
|
-
Each comment JSON object independently records the most recent conversation that created or changed that comment as `threadId`. Comment operations never change the parent issue's `threadId`.
|
|
179
|
-
|
|
180
|
-
## Attachments
|
|
181
|
-
|
|
182
|
-
Issue descriptions and comments may contain inline images at exact positions in their Markdown:
|
|
183
|
-
|
|
184
|
-
```markdown
|
|
185
|
-

|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
Upload a local file to a task or a comment. Provide exactly one of `--task` or `--comment`:
|
|
189
|
-
|
|
190
|
-
```bash
|
|
191
|
-
taskctl attachment list (--task TASK_ID | --comment COMMENT_ID) [--after CURSOR] [--json]
|
|
192
|
-
taskctl attachment upload --task TASK_ID --file PATH [--content-type TYPE] [--kind inline|attachment] [--json]
|
|
193
|
-
taskctl attachment upload --comment COMMENT_ID --file PATH [--content-type TYPE] [--kind inline|attachment] [--json]
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
Without `--after`, `attachment list` returns the full list for that task or comment. Its response includes `nextCursor`; keep a separate cursor for every attachment list target and pass it to the next read to return only later attachments.
|
|
197
|
-
|
|
198
|
-
The command sends the file bytes to:
|
|
199
|
-
|
|
200
|
-
- `POST /api/tasks/:id/attachments`, or
|
|
201
|
-
- `POST /api/comments/:id/attachments`
|
|
202
|
-
|
|
203
|
-
with the same headers as the web UI (`Content-Type`, `X-Taskboard-Filename`, `X-Taskboard-Attachment-Kind`). If `--content-type` is omitted, the CLI guesses from the file extension and falls back to `application/octet-stream`. If `--kind` is omitted, images use `inline` and other files use `attachment`. Use `--kind attachment` for an image that must appear in the attachment list. An inline upload returns the attachment id; use that id in the task description or comment Markdown at the required position.
|
|
204
|
-
|
|
205
|
-
Download an attachment to an explicit local path:
|
|
206
|
-
|
|
207
|
-
```bash
|
|
208
|
-
taskctl attachment download ATTACHMENT_ID --output PATH [--json]
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
The command writes the response body as binary data and returns the absolute output path, content type, and size in its JSON result. Choose the output filename yourself; `taskctl` does not infer or append an extension.
|