patchwarden 0.6.1 → 1.1.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.
Files changed (261) hide show
  1. package/PatchWarden-Control-Tray.cmd +11 -0
  2. package/PatchWarden-Control.cmd +6 -0
  3. package/PatchWarden-Desktop.cmd +5 -0
  4. package/PatchWarden.cmd +1 -1
  5. package/README.en.md +112 -24
  6. package/README.md +36 -25
  7. package/Restart-PatchWarden-Control.cmd +6 -0
  8. package/Stop-PatchWarden.cmd +11 -0
  9. package/dist/agents/agentRouter.d.ts +40 -0
  10. package/dist/agents/agentRouter.js +95 -0
  11. package/dist/config.d.ts +1 -1
  12. package/dist/config.js +6 -2
  13. package/dist/controlCenter.d.ts +14 -0
  14. package/dist/controlCenter.js +2015 -0
  15. package/dist/doctor.js +35 -4
  16. package/dist/goal/acceptanceEngine.d.ts +62 -0
  17. package/dist/goal/acceptanceEngine.js +103 -0
  18. package/dist/goal/acceptanceTemplate.d.ts +10 -0
  19. package/dist/goal/acceptanceTemplate.js +104 -0
  20. package/dist/goal/goalGraph.d.ts +58 -0
  21. package/dist/goal/goalGraph.js +189 -0
  22. package/dist/goal/goalProgress.d.ts +81 -0
  23. package/dist/goal/goalProgress.js +216 -0
  24. package/dist/goal/goalStatus.d.ts +60 -0
  25. package/dist/goal/goalStatus.js +137 -0
  26. package/dist/goal/goalStore.d.ts +79 -0
  27. package/dist/goal/goalStore.js +211 -0
  28. package/dist/goal/handoffExport.d.ts +34 -0
  29. package/dist/goal/handoffExport.js +183 -0
  30. package/dist/goal/subgoalSync.d.ts +40 -0
  31. package/dist/goal/subgoalSync.js +72 -0
  32. package/dist/goal/worktreeManager.d.ts +96 -0
  33. package/dist/goal/worktreeManager.js +292 -0
  34. package/dist/logging.d.ts +44 -0
  35. package/dist/logging.js +68 -0
  36. package/dist/release/releaseGate.d.ts +99 -0
  37. package/dist/release/releaseGate.js +475 -0
  38. package/dist/runner/postTaskCleanup.d.ts +13 -0
  39. package/dist/runner/postTaskCleanup.js +147 -0
  40. package/dist/runner/runTask.js +50 -10
  41. package/dist/security/discoveryTokenStore.d.ts +62 -0
  42. package/dist/security/discoveryTokenStore.js +100 -0
  43. package/dist/security/toolInvocationGuard.d.ts +35 -0
  44. package/dist/security/toolInvocationGuard.js +153 -0
  45. package/dist/smoke-test.js +18 -10
  46. package/dist/taskRuntime.d.ts +17 -0
  47. package/dist/test/unit/acceptance-engine.test.d.ts +1 -0
  48. package/dist/test/unit/acceptance-engine.test.js +228 -0
  49. package/dist/test/unit/agent-router.test.d.ts +1 -0
  50. package/dist/test/unit/agent-router.test.js +287 -0
  51. package/dist/test/unit/audit-checks.test.d.ts +1 -0
  52. package/dist/test/unit/audit-checks.test.js +350 -0
  53. package/dist/test/unit/diagnose-task.test.d.ts +1 -0
  54. package/dist/test/unit/diagnose-task.test.js +457 -0
  55. package/dist/test/unit/discovery-token-store.test.d.ts +1 -0
  56. package/dist/test/unit/discovery-token-store.test.js +139 -0
  57. package/dist/test/unit/goal-graph.test.d.ts +1 -0
  58. package/dist/test/unit/goal-graph.test.js +298 -0
  59. package/dist/test/unit/goal-progress.test.d.ts +1 -0
  60. package/dist/test/unit/goal-progress.test.js +381 -0
  61. package/dist/test/unit/goal-status.test.d.ts +1 -0
  62. package/dist/test/unit/goal-status.test.js +215 -0
  63. package/dist/test/unit/goal-store.test.d.ts +1 -0
  64. package/dist/test/unit/goal-store.test.js +253 -0
  65. package/dist/test/unit/goal-subgoal-task.test.d.ts +1 -0
  66. package/dist/test/unit/goal-subgoal-task.test.js +55 -0
  67. package/dist/test/unit/goal-tools-registry.test.d.ts +1 -0
  68. package/dist/test/unit/goal-tools-registry.test.js +190 -0
  69. package/dist/test/unit/handoff-export.test.d.ts +1 -0
  70. package/dist/test/unit/handoff-export.test.js +263 -0
  71. package/dist/test/unit/invoke-discovered-tool.test.d.ts +1 -0
  72. package/dist/test/unit/invoke-discovered-tool.test.js +167 -0
  73. package/dist/test/unit/logging.test.js +127 -5
  74. package/dist/test/unit/post-task-cleanup.test.d.ts +1 -0
  75. package/dist/test/unit/post-task-cleanup.test.js +48 -0
  76. package/dist/test/unit/reconcile-tasks.test.d.ts +1 -0
  77. package/dist/test/unit/reconcile-tasks.test.js +456 -0
  78. package/dist/test/unit/release-gate.test.d.ts +1 -0
  79. package/dist/test/unit/release-gate.test.js +242 -0
  80. package/dist/test/unit/safe-views.test.d.ts +1 -0
  81. package/dist/test/unit/safe-views.test.js +171 -0
  82. package/dist/test/unit/schema-drift-check.test.d.ts +1 -0
  83. package/dist/test/unit/schema-drift-check.test.js +175 -0
  84. package/dist/test/unit/subgoal-sync.test.d.ts +1 -0
  85. package/dist/test/unit/subgoal-sync.test.js +183 -0
  86. package/dist/test/unit/tool-invocation-guard.test.d.ts +1 -0
  87. package/dist/test/unit/tool-invocation-guard.test.js +432 -0
  88. package/dist/test/unit/tool-usage-stats.test.d.ts +1 -0
  89. package/dist/test/unit/tool-usage-stats.test.js +300 -0
  90. package/dist/test/unit/toolSearch.test.d.ts +1 -0
  91. package/dist/test/unit/toolSearch.test.js +571 -0
  92. package/dist/test/unit/worktree-manager.test.d.ts +1 -0
  93. package/dist/test/unit/worktree-manager.test.js +176 -0
  94. package/dist/tools/auditTask.d.ts +103 -1
  95. package/dist/tools/auditTask.js +461 -8
  96. package/dist/tools/cancelTask.js +1 -1
  97. package/dist/tools/checkReleaseGate.d.ts +21 -0
  98. package/dist/tools/checkReleaseGate.js +22 -0
  99. package/dist/tools/createTask.d.ts +18 -2
  100. package/dist/tools/createTask.js +29 -1
  101. package/dist/tools/diagnoseTask.d.ts +45 -0
  102. package/dist/tools/diagnoseTask.js +347 -0
  103. package/dist/tools/discardWorktree.d.ts +24 -0
  104. package/dist/tools/discardWorktree.js +27 -0
  105. package/dist/tools/discoverTools.d.ts +11 -0
  106. package/dist/tools/discoverTools.js +39 -0
  107. package/dist/tools/explainTool.d.ts +11 -0
  108. package/dist/tools/explainTool.js +21 -0
  109. package/dist/tools/getTaskSummary.js +2 -1
  110. package/dist/tools/goalSubgoalTask.d.ts +51 -0
  111. package/dist/tools/goalSubgoalTask.js +110 -0
  112. package/dist/tools/healthCheck.d.ts +1 -0
  113. package/dist/tools/healthCheck.js +5 -1
  114. package/dist/tools/invokeDiscoveredTool.d.ts +37 -0
  115. package/dist/tools/invokeDiscoveredTool.js +112 -0
  116. package/dist/tools/listTasks.d.ts +3 -1
  117. package/dist/tools/listTasks.js +14 -1
  118. package/dist/tools/mergeWorktree.d.ts +24 -0
  119. package/dist/tools/mergeWorktree.js +27 -0
  120. package/dist/tools/reconcileTasks.d.ts +41 -0
  121. package/dist/tools/reconcileTasks.js +352 -0
  122. package/dist/tools/registry.js +550 -0
  123. package/dist/tools/safeStatus.d.ts +31 -1
  124. package/dist/tools/safeStatus.js +43 -1
  125. package/dist/tools/safeViews.d.ts +256 -0
  126. package/dist/tools/safeViews.js +250 -0
  127. package/dist/tools/schemaDriftCheck.d.ts +46 -0
  128. package/dist/tools/schemaDriftCheck.js +80 -0
  129. package/dist/tools/toolCatalog.d.ts +4 -3
  130. package/dist/tools/toolCatalog.js +33 -11
  131. package/dist/tools/toolRegistry.d.ts +61 -0
  132. package/dist/tools/toolRegistry.js +724 -0
  133. package/dist/tools/toolSearch.d.ts +110 -0
  134. package/dist/tools/toolSearch.js +331 -0
  135. package/dist/tools/toolUsageStats.d.ts +41 -0
  136. package/dist/tools/toolUsageStats.js +116 -0
  137. package/dist/tools/waitForTask.js +1 -0
  138. package/dist/version.d.ts +2 -2
  139. package/dist/version.js +2 -2
  140. package/dist/watcherStatus.d.ts +29 -0
  141. package/dist/watcherStatus.js +92 -1
  142. package/docs/control-center/README.md +33 -0
  143. package/docs/control-center/control-center-daily-driver.md +211 -0
  144. package/docs/control-center/control-center-mvp.md +205 -0
  145. package/docs/control-center/control-center-phase2.md +159 -0
  146. package/docs/demo.md +3 -0
  147. package/docs/release-v0.6.4.md +45 -0
  148. package/examples/openai-tunnel/README.md +5 -5
  149. package/examples/openai-tunnel/tunnel-client.example.yaml +3 -3
  150. package/package.json +25 -16
  151. package/scripts/README.md +47 -0
  152. package/scripts/{brand-check.js → checks/brand-check.js} +2 -2
  153. package/scripts/checks/control-center-smoke.js +1098 -0
  154. package/scripts/{control-smoke.js → checks/control-smoke.js} +19 -5
  155. package/scripts/{doctor-smoke.js → checks/doctor-smoke.js} +2 -1
  156. package/scripts/{http-mcp-smoke.js → checks/http-mcp-smoke.js} +2 -2
  157. package/scripts/{lifecycle-smoke.js → checks/lifecycle-smoke.js} +25 -21
  158. package/scripts/{mcp-manifest-check.js → checks/mcp-manifest-check.js} +54 -7
  159. package/scripts/{mcp-smoke.js → checks/mcp-smoke.js} +40 -11
  160. package/scripts/{package-manifest-check.js → checks/package-manifest-check.js} +2 -1
  161. package/scripts/{tunnel-supervisor-smoke.js → checks/tunnel-supervisor-smoke.js} +2 -2
  162. package/scripts/{unit-tests.js → checks/unit-tests.js} +1 -1
  163. package/scripts/{watcher-supervisor-smoke.js → checks/watcher-supervisor-smoke.js} +13 -7
  164. package/scripts/control/control-center-tray.ps1 +281 -0
  165. package/scripts/{get-patchwarden-health.ps1 → control/get-patchwarden-health.ps1} +3 -3
  166. package/scripts/{manage-patchwarden.ps1 → control/manage-patchwarden.ps1} +30 -4
  167. package/scripts/control/restart-control-center.ps1 +173 -0
  168. package/scripts/{restart-patchwarden.ps1 → control/restart-patchwarden.ps1} +1 -1
  169. package/scripts/control/start-control-center.ps1 +263 -0
  170. package/scripts/{start-patchwarden-tunnel.ps1 → control/start-patchwarden-tunnel.ps1} +48 -6
  171. package/scripts/control/stop-patchwarden.ps1 +114 -0
  172. package/scripts/launchers/Check-PatchWarden-Health.cmd +1 -1
  173. package/scripts/launchers/Reset-PatchWarden-Tunnel-Key.cmd +1 -1
  174. package/scripts/launchers/Restart-PatchWarden.cmd +1 -1
  175. package/scripts/launchers/Start-PatchWarden-Direct-Tunnel.cmd +1 -1
  176. package/scripts/launchers/Start-PatchWarden-Tunnel.cmd +1 -1
  177. package/scripts/{patchwarden-mcp-direct.cmd → mcp/patchwarden-mcp-direct.cmd} +1 -1
  178. package/scripts/{patchwarden-mcp-stdio.cmd → mcp/patchwarden-mcp-stdio.cmd} +1 -1
  179. package/scripts/{pack-clean.js → release/pack-clean.js} +9 -1
  180. package/src/agents/agentRouter.ts +149 -0
  181. package/src/config.ts +9 -3
  182. package/src/controlCenter.ts +2166 -0
  183. package/src/doctor.ts +40 -5
  184. package/src/goal/acceptanceEngine.ts +160 -0
  185. package/src/goal/acceptanceTemplate.ts +121 -0
  186. package/src/goal/goalGraph.ts +225 -0
  187. package/src/goal/goalProgress.ts +301 -0
  188. package/src/goal/goalStatus.ts +234 -0
  189. package/src/goal/goalStore.ts +306 -0
  190. package/src/goal/handoffExport.ts +211 -0
  191. package/src/goal/subgoalSync.ts +82 -0
  192. package/src/goal/worktreeManager.ts +404 -0
  193. package/src/logging.ts +91 -0
  194. package/src/release/releaseGate.ts +567 -0
  195. package/src/runner/postTaskCleanup.ts +154 -0
  196. package/src/runner/runTask.ts +49 -10
  197. package/src/security/discoveryTokenStore.ts +157 -0
  198. package/src/security/toolInvocationGuard.ts +251 -0
  199. package/src/smoke-test.ts +15 -7
  200. package/src/taskRuntime.ts +17 -0
  201. package/src/test/unit/acceptance-engine.test.ts +261 -0
  202. package/src/test/unit/agent-router.test.ts +342 -0
  203. package/src/test/unit/audit-checks.test.ts +567 -0
  204. package/src/test/unit/diagnose-task.test.ts +544 -0
  205. package/src/test/unit/discovery-token-store.test.ts +181 -0
  206. package/src/test/unit/goal-graph.test.ts +347 -0
  207. package/src/test/unit/goal-progress.test.ts +538 -0
  208. package/src/test/unit/goal-status.test.ts +270 -0
  209. package/src/test/unit/goal-store.test.ts +318 -0
  210. package/src/test/unit/goal-subgoal-task.test.ts +72 -0
  211. package/src/test/unit/goal-tools-registry.test.ts +243 -0
  212. package/src/test/unit/handoff-export.test.ts +295 -0
  213. package/src/test/unit/invoke-discovered-tool.test.ts +216 -0
  214. package/src/test/unit/logging.test.ts +177 -5
  215. package/src/test/unit/post-task-cleanup.test.ts +53 -0
  216. package/src/test/unit/reconcile-tasks.test.ts +551 -0
  217. package/src/test/unit/release-gate.test.ts +314 -0
  218. package/src/test/unit/safe-views.test.ts +184 -0
  219. package/src/test/unit/schema-drift-check.test.ts +258 -0
  220. package/src/test/unit/subgoal-sync.test.ts +236 -0
  221. package/src/test/unit/tool-invocation-guard.test.ts +542 -0
  222. package/src/test/unit/tool-usage-stats.test.ts +384 -0
  223. package/src/test/unit/toolSearch.test.ts +756 -0
  224. package/src/test/unit/worktree-manager.test.ts +247 -0
  225. package/src/tools/auditTask.ts +831 -402
  226. package/src/tools/cancelTask.ts +1 -1
  227. package/src/tools/checkReleaseGate.ts +45 -0
  228. package/src/tools/createTask.ts +64 -6
  229. package/src/tools/diagnoseTask.ts +460 -0
  230. package/src/tools/discardWorktree.ts +42 -0
  231. package/src/tools/discoverTools.ts +51 -0
  232. package/src/tools/explainTool.ts +26 -0
  233. package/src/tools/getTaskSummary.ts +2 -1
  234. package/src/tools/goalSubgoalTask.ts +170 -0
  235. package/src/tools/healthCheck.ts +4 -1
  236. package/src/tools/invokeDiscoveredTool.ts +163 -0
  237. package/src/tools/listTasks.ts +16 -2
  238. package/src/tools/mergeWorktree.ts +42 -0
  239. package/src/tools/reconcileTasks.ts +464 -0
  240. package/src/tools/registry.ts +618 -1
  241. package/src/tools/safeStatus.ts +73 -2
  242. package/src/tools/safeViews.ts +271 -0
  243. package/src/tools/schemaDriftCheck.ts +120 -0
  244. package/src/tools/toolCatalog.ts +34 -11
  245. package/src/tools/toolRegistry.ts +786 -0
  246. package/src/tools/toolSearch.ts +464 -0
  247. package/src/tools/toolUsageStats.ts +151 -0
  248. package/src/tools/waitForTask.ts +1 -0
  249. package/src/version.ts +2 -2
  250. package/src/watcherStatus.ts +97 -1
  251. package/ui/colors_and_type.css +141 -0
  252. package/ui/pages/audit.html +743 -0
  253. package/ui/pages/dashboard.html +1154 -0
  254. package/ui/pages/direct-sessions.html +652 -0
  255. package/ui/pages/logs.html +502 -0
  256. package/ui/pages/task-detail.html +1229 -0
  257. package/ui/pages/tasks.html +702 -0
  258. package/ui/pages/workspace.html +947 -0
  259. package/ui/partials/project-shell.html +362 -0
  260. package/ui/vendor/lucide.js +12 -0
  261. package/ui/vendor/tailwindcss-browser.js +947 -0
@@ -0,0 +1,211 @@
1
+ /**
2
+ * v0.8.0: Goal Session 目录 CRUD — 在 workspaceRoot 下管理 .patchwarden/goals/ 目录。
3
+ *
4
+ * 目录结构:
5
+ * {workspaceRoot}/.patchwarden/goals/{goal_id}/
6
+ * ├── GOAL.md 人类可读的 goal 描述
7
+ * ├── GOALS.md 子目标列表(人类可读)
8
+ * ├── goal_status.json 机器可读的状态文件(原子写)
9
+ * ├── tasks/ 任务产物
10
+ * └── artifacts/ 其他产物
11
+ *
12
+ * 所有文件系统函数都接受可选的 workspaceRoot 参数用于测试;
13
+ * 默认从 getConfig().workspaceRoot 读取。
14
+ */
15
+ import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, writeFileSync, statSync, } from "node:fs";
16
+ import { join } from "node:path";
17
+ import { getConfig } from "../config.js";
18
+ import { guardWorkspacePath } from "../security/pathGuard.js";
19
+ import { PatchWardenError } from "../errors.js";
20
+ import { createInitialGoalStatus, } from "./goalStatus.js";
21
+ // ── 辅助:解析 workspaceRoot ──────────────────────────────────────
22
+ function resolveWorkspaceRoot(workspaceRoot) {
23
+ return workspaceRoot ?? getConfig().workspaceRoot;
24
+ }
25
+ // ── Goal ID 生成 ──────────────────────────────────────────────────
26
+ /**
27
+ * 从 title 生成 slug:小写、非字母数字字符替换为 `_`、合并连续 `_`、去除首尾 `_`、截断到 30 字符。
28
+ * 如果 slug 为空(title 全是符号),用 `untitled` 代替。
29
+ */
30
+ function titleToSlug(title) {
31
+ const slug = title
32
+ .toLowerCase()
33
+ .replace(/[^a-z0-9]+/g, "_")
34
+ .replace(/^_+|_+$/g, "")
35
+ .slice(0, 30);
36
+ return slug === "" ? "untitled" : slug;
37
+ }
38
+ /**
39
+ * 生成 `goal_{YYYYMMDD}_{slug}` 格式的 goal id。
40
+ * 冲突时追加 `_2`、`_3`... 直到唯一。
41
+ * 日期用本地时区(new Date())。
42
+ */
43
+ export function generateGoalId(title, existingIds) {
44
+ const now = new Date();
45
+ const y = now.getFullYear();
46
+ const m = String(now.getMonth() + 1).padStart(2, "0");
47
+ const d = String(now.getDate()).padStart(2, "0");
48
+ const datePart = `${y}${m}${d}`;
49
+ const slug = titleToSlug(title);
50
+ const existing = new Set(existingIds);
51
+ const base = `goal_${datePart}_${slug}`;
52
+ if (!existing.has(base)) {
53
+ return base;
54
+ }
55
+ let counter = 2;
56
+ while (existing.has(`${base}_${counter}`)) {
57
+ counter++;
58
+ }
59
+ return `${base}_${counter}`;
60
+ }
61
+ // ── 目录路径解析 ──────────────────────────────────────────────────
62
+ /**
63
+ * 返回 `.patchwarden/goals/` 目录路径(相对于 workspaceRoot)。
64
+ * 不自动创建目录。
65
+ */
66
+ export function getGoalsDir(workspaceRoot) {
67
+ return join(resolveWorkspaceRoot(workspaceRoot), ".patchwarden", "goals");
68
+ }
69
+ /**
70
+ * 返回 `{getGoalsDir()}/{goalId}` 路径。
71
+ */
72
+ export function getGoalDir(goalId, workspaceRoot) {
73
+ return join(getGoalsDir(workspaceRoot), goalId);
74
+ }
75
+ // ── CRUD ──────────────────────────────────────────────────────────
76
+ /**
77
+ * 创建一个新的 Goal Session。
78
+ * - 用 guardWorkspacePath 校验 repoPath 在 workspaceRoot 内
79
+ * - 扫描现有 goal 目录获取 existingIds,调用 generateGoalId
80
+ * - 创建目录结构:goal_dir/、tasks/、artifacts/
81
+ * - 写入 GOAL.md、GOALS.md、goal_status.json
82
+ * 返回 { goal_id, goal_dir }。
83
+ */
84
+ export function createGoal(repoPath, title, description, workspaceRoot) {
85
+ const wsRoot = resolveWorkspaceRoot(workspaceRoot);
86
+ const guardedRepo = guardWorkspacePath(repoPath, wsRoot);
87
+ const goalsDir = getGoalsDir(workspaceRoot);
88
+ const existingIds = [];
89
+ if (existsSync(goalsDir)) {
90
+ for (const entry of readdirSync(goalsDir)) {
91
+ const entryPath = join(goalsDir, entry);
92
+ try {
93
+ if (statSync(entryPath).isDirectory()) {
94
+ existingIds.push(entry);
95
+ }
96
+ }
97
+ catch {
98
+ // 跳过无法 stat 的条目
99
+ }
100
+ }
101
+ }
102
+ const goalId = generateGoalId(title, existingIds);
103
+ const goalDir = join(goalsDir, goalId);
104
+ // 创建目录结构
105
+ mkdirSync(join(goalDir, "tasks"), { recursive: true });
106
+ mkdirSync(join(goalDir, "artifacts"), { recursive: true });
107
+ // 写入 GOAL.md
108
+ const now = new Date().toISOString();
109
+ const goalMd = [
110
+ `# ${title}`,
111
+ "",
112
+ description,
113
+ "",
114
+ `- Created: ${now}`,
115
+ `- Repo: ${guardedRepo}`,
116
+ "- Status: active",
117
+ "",
118
+ ].join("\n");
119
+ writeFileSync(join(goalDir, "GOAL.md"), goalMd, "utf-8");
120
+ // 写入 GOALS.md
121
+ const goalsMd = `# Subgoals: ${title}\n\n_No subgoals yet._\n`;
122
+ writeFileSync(join(goalDir, "GOALS.md"), goalsMd, "utf-8");
123
+ // 写入 goal_status.json
124
+ const status = createInitialGoalStatus(goalId, title, guardedRepo);
125
+ writeFileSync(join(goalDir, "goal_status.json"), JSON.stringify(status, null, 2) + "\n", "utf-8");
126
+ return { goal_id: goalId, goal_dir: goalDir };
127
+ }
128
+ /**
129
+ * 列出所有 goal 的摘要信息,按 updated_at 降序排列。
130
+ * 无法解析的目录会被跳过。
131
+ */
132
+ export function listGoals(workspaceRoot) {
133
+ const goalsDir = getGoalsDir(workspaceRoot);
134
+ if (!existsSync(goalsDir)) {
135
+ return [];
136
+ }
137
+ const results = [];
138
+ for (const entry of readdirSync(goalsDir)) {
139
+ const entryPath = join(goalsDir, entry);
140
+ try {
141
+ if (!statSync(entryPath).isDirectory()) {
142
+ continue;
143
+ }
144
+ const status = readGoalStatus(entry, workspaceRoot);
145
+ results.push({
146
+ goal_id: status.goal_id,
147
+ title: status.title,
148
+ status: status.status,
149
+ subgoal_total: status.subgoals.length,
150
+ subgoal_accepted: status.subgoals.filter((s) => s.status === "accepted").length,
151
+ subgoal_running: status.subgoals.filter((s) => s.status === "running").length,
152
+ updated_at: status.updated_at,
153
+ });
154
+ }
155
+ catch {
156
+ // 跳过无法解析的目录
157
+ }
158
+ }
159
+ results.sort((a, b) => (a.updated_at < b.updated_at ? 1 : a.updated_at > b.updated_at ? -1 : 0));
160
+ return results;
161
+ }
162
+ /**
163
+ * 读取 goal 的完整详情:goal_status.json + GOAL.md 内容。
164
+ * 如果 goal 目录不存在或 goal_status.json 不存在,抛出 PatchWardenError("goal_not_found")。
165
+ */
166
+ export function readGoal(goalId, workspaceRoot) {
167
+ const goalDir = getGoalDir(goalId, workspaceRoot);
168
+ if (!existsSync(goalDir)) {
169
+ throw new PatchWardenError("goal_not_found", `Goal directory not found: "${goalDir}"`, "Ensure the goal id exists before reading.", true, { goal_id: goalId, goal_dir: goalDir });
170
+ }
171
+ const status = readGoalStatus(goalId, workspaceRoot);
172
+ let goalDescription = "";
173
+ const goalMdPath = join(goalDir, "GOAL.md");
174
+ if (existsSync(goalMdPath)) {
175
+ goalDescription = readFileSync(goalMdPath, "utf-8");
176
+ }
177
+ return {
178
+ goal_id: status.goal_id,
179
+ title: status.title,
180
+ status: status.status,
181
+ repo_path: status.repo_path,
182
+ created_at: status.created_at,
183
+ updated_at: status.updated_at,
184
+ goal_description: goalDescription,
185
+ subgoals: status.subgoals,
186
+ };
187
+ }
188
+ /**
189
+ * 原子写入 goal_status.json:先写到 .tmp 文件,再 renameSync。
190
+ * JSON 序列化用 JSON.stringify(status, null, 2) + 末尾换行。
191
+ */
192
+ export function writeGoalStatus(goalId, status, workspaceRoot) {
193
+ const goalDir = getGoalDir(goalId, workspaceRoot);
194
+ const finalPath = join(goalDir, "goal_status.json");
195
+ const tmpPath = join(goalDir, "goal_status.json.tmp");
196
+ writeFileSync(tmpPath, JSON.stringify(status, null, 2) + "\n", "utf-8");
197
+ renameSync(tmpPath, finalPath);
198
+ }
199
+ /**
200
+ * 读取 goal_status.json 并 JSON.parse。
201
+ * 如果文件不存在,抛出 PatchWardenError("goal_not_found")。
202
+ */
203
+ export function readGoalStatus(goalId, workspaceRoot) {
204
+ const goalDir = getGoalDir(goalId, workspaceRoot);
205
+ const statusPath = join(goalDir, "goal_status.json");
206
+ if (!existsSync(statusPath)) {
207
+ throw new PatchWardenError("goal_not_found", `goal_status.json not found for goal "${goalId}" at "${statusPath}"`, "Ensure the goal has been created via createGoal before reading its status.", true, { goal_id: goalId, status_path: statusPath });
208
+ }
209
+ const raw = readFileSync(statusPath, "utf-8");
210
+ return JSON.parse(raw);
211
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * v0.8.0: Goal Session 交接文档生成 — 将当前 goal 状态渲染为 Markdown 交接报告。
3
+ *
4
+ * generateHandoff 生成 Markdown 字符串;exportHandoff 将其落盘到
5
+ * {workspaceRoot}/.patchwarden/goals/{goalId}/handoff.md。
6
+ * exportHandoff 接受调用方传入的 goalStatus,不自行读取 goal_status.json,
7
+ * 以避免与 goalStore 形成循环依赖。
8
+ */
9
+ import type { GoalStatus } from "./goalStatus.js";
10
+ /**
11
+ * 生成 Goal Session 交接文档(Markdown 字符串)。
12
+ *
13
+ * 包含章节:当前 Goal、版本目标、已完成/未完成/已拒绝子目标、最近 diff 摘要、
14
+ * 最近测试结果、当前阻塞点、下一步建议、风险提醒、接手说明。
15
+ *
16
+ * @param goalId Goal 标识(用于接手说明中的引用)
17
+ * @param goalStatus 当前 GoalStatus 快照
18
+ * @param recentDiff 最近一次 git diff 摘要(可选)
19
+ * @param recentTestResult 最近一次测试结果(可选)
20
+ * @returns Markdown 格式的交接文档
21
+ */
22
+ export declare function generateHandoff(goalId: string, goalStatus: GoalStatus, recentDiff?: string, recentTestResult?: string): string;
23
+ /**
24
+ * 生成交接文档并写入 {workspaceRoot}/.patchwarden/goals/{goalId}/handoff.md。
25
+ *
26
+ * @param goalId Goal 标识
27
+ * @param goalStatus 当前 GoalStatus 快照(由调用方读取后传入)
28
+ * @param workspaceRoot 工作区根目录(可选,默认从 getConfig().workspaceRoot 获取)
29
+ * @returns handoff_path(绝对路径)和 content_preview(前 500 字符,超出则追加 "...")
30
+ */
31
+ export declare function exportHandoff(goalId: string, goalStatus: GoalStatus, workspaceRoot?: string): {
32
+ handoff_path: string;
33
+ content_preview: string;
34
+ };
@@ -0,0 +1,183 @@
1
+ /**
2
+ * v0.8.0: Goal Session 交接文档生成 — 将当前 goal 状态渲染为 Markdown 交接报告。
3
+ *
4
+ * generateHandoff 生成 Markdown 字符串;exportHandoff 将其落盘到
5
+ * {workspaceRoot}/.patchwarden/goals/{goalId}/handoff.md。
6
+ * exportHandoff 接受调用方传入的 goalStatus,不自行读取 goal_status.json,
7
+ * 以避免与 goalStore 形成循环依赖。
8
+ */
9
+ import { suggestNextSubgoal } from "./goalGraph.js";
10
+ import { getConfig } from "../config.js";
11
+ import { mkdirSync, writeFileSync } from "node:fs";
12
+ import { join, resolve } from "node:path";
13
+ // ── Markdown 生成 ────────────────────────────────────────────────
14
+ /**
15
+ * 生成 Goal Session 交接文档(Markdown 字符串)。
16
+ *
17
+ * 包含章节:当前 Goal、版本目标、已完成/未完成/已拒绝子目标、最近 diff 摘要、
18
+ * 最近测试结果、当前阻塞点、下一步建议、风险提醒、接手说明。
19
+ *
20
+ * @param goalId Goal 标识(用于接手说明中的引用)
21
+ * @param goalStatus 当前 GoalStatus 快照
22
+ * @param recentDiff 最近一次 git diff 摘要(可选)
23
+ * @param recentTestResult 最近一次测试结果(可选)
24
+ * @returns Markdown 格式的交接文档
25
+ */
26
+ export function generateHandoff(goalId, goalStatus, recentDiff, recentTestResult) {
27
+ const lines = [];
28
+ const now = new Date().toISOString();
29
+ lines.push("# Goal Session Handoff");
30
+ lines.push("");
31
+ lines.push("Generated: " + now);
32
+ lines.push("");
33
+ // ── 当前 Goal ───────────────────────────────────────────
34
+ lines.push("## 当前 Goal");
35
+ lines.push("");
36
+ lines.push("- **goal_id**: " + goalStatus.goal_id);
37
+ lines.push("- **title**: " + goalStatus.title);
38
+ lines.push("- **status**: " + goalStatus.status);
39
+ lines.push("- **repo_path**: " + goalStatus.repo_path);
40
+ lines.push("");
41
+ // ── 版本目标 ────────────────────────────────────────────
42
+ lines.push("## 版本目标");
43
+ lines.push("");
44
+ lines.push(goalStatus.title);
45
+ lines.push("");
46
+ // ── 已完成子目标 ────────────────────────────────────────
47
+ lines.push("## 已完成子目标");
48
+ lines.push("");
49
+ const accepted = goalStatus.subgoals.filter((s) => s.status === "accepted");
50
+ if (accepted.length === 0) {
51
+ lines.push("无");
52
+ }
53
+ else {
54
+ for (const s of accepted) {
55
+ lines.push("- **" + s.id + "**: " + s.title + " (accepted_at: " + (s.accepted_at ?? "N/A") + ")");
56
+ }
57
+ }
58
+ lines.push("");
59
+ // ── 未完成子目标 ────────────────────────────────────────
60
+ lines.push("## 未完成子目标");
61
+ lines.push("");
62
+ const incomplete = goalStatus.subgoals.filter((s) => s.status !== "accepted" && s.status !== "rejected");
63
+ if (incomplete.length === 0) {
64
+ lines.push("无");
65
+ }
66
+ else {
67
+ for (const s of incomplete) {
68
+ const deps = s.depends_on.length > 0 ? s.depends_on.join(", ") : "无";
69
+ lines.push("- **" + s.id + "**: " + s.title + " (status: " + s.status + ", depends_on: " + deps + ")");
70
+ }
71
+ }
72
+ lines.push("");
73
+ // ── 已拒绝子目标 ────────────────────────────────────────
74
+ lines.push("## 已拒绝子目标");
75
+ lines.push("");
76
+ const rejected = goalStatus.subgoals.filter((s) => s.status === "rejected");
77
+ if (rejected.length === 0) {
78
+ lines.push("无");
79
+ }
80
+ else {
81
+ for (const s of rejected) {
82
+ lines.push("- **" + s.id + "**: " + s.title + " (rejected_reason: " + (s.rejected_reason ?? "N/A") + ")");
83
+ }
84
+ }
85
+ lines.push("");
86
+ // ── 最近一次 diff 摘要 ──────────────────────────────────
87
+ lines.push("## 最近一次 diff 摘要");
88
+ lines.push("");
89
+ if (recentDiff) {
90
+ lines.push(recentDiff);
91
+ }
92
+ else {
93
+ lines.push("暂无");
94
+ }
95
+ lines.push("");
96
+ // ── 最近一次测试结果 ────────────────────────────────────
97
+ lines.push("## 最近一次测试结果");
98
+ lines.push("");
99
+ if (recentTestResult) {
100
+ lines.push(recentTestResult);
101
+ }
102
+ else {
103
+ lines.push("暂无");
104
+ }
105
+ lines.push("");
106
+ // ── 当前阻塞点 ─────────────────────────────────────────
107
+ lines.push("## 当前阻塞点");
108
+ lines.push("");
109
+ const needsFix = goalStatus.subgoals.filter((s) => s.status === "needs_fix");
110
+ if (needsFix.length === 0) {
111
+ lines.push("无");
112
+ }
113
+ else {
114
+ for (const s of needsFix) {
115
+ lines.push("- **" + s.id + "**: " + s.title);
116
+ }
117
+ }
118
+ lines.push("");
119
+ // ── 下一步建议 ─────────────────────────────────────────
120
+ lines.push("## 下一步建议");
121
+ lines.push("");
122
+ const suggestion = suggestNextSubgoal(goalStatus);
123
+ if (suggestion.subgoal_id) {
124
+ lines.push("建议执行子目标 **" + suggestion.subgoal_id + "**:" + (suggestion.title ?? ""));
125
+ }
126
+ else if (suggestion.reason === "dependencies_not_met") {
127
+ lines.push("当前存在被阻塞的子目标,建议先处理以下依赖:");
128
+ const blockedList = suggestion.blocked_by ?? [];
129
+ for (const id of blockedList) {
130
+ lines.push("- " + id);
131
+ }
132
+ }
133
+ else {
134
+ lines.push("当前无可执行的 ready 子目标,建议检查所有子目标状态或等待任务完成。");
135
+ }
136
+ lines.push("");
137
+ // ── 风险提醒 ───────────────────────────────────────────
138
+ lines.push("## 风险提醒");
139
+ lines.push("");
140
+ const risks = goalStatus.subgoals.filter((s) => s.status === "needs_fix" || s.status === "running");
141
+ if (risks.length === 0) {
142
+ lines.push("无");
143
+ }
144
+ else {
145
+ for (const s of risks) {
146
+ if (s.status === "needs_fix") {
147
+ lines.push("- **" + s.id + "**: " + s.title + " (needs_fix — 需要修复)");
148
+ }
149
+ else {
150
+ lines.push("- **" + s.id + "**: " + s.title + " (running — 长期运行中)");
151
+ }
152
+ }
153
+ }
154
+ lines.push("");
155
+ // ── 接手说明 ───────────────────────────────────────────
156
+ lines.push("## 接手说明");
157
+ lines.push("");
158
+ lines.push("新会话接手时,请先调用 read_goal 查看 " + goalId + " 的完整状态,然后调用 suggest_next_subgoal 获取下一个可执行子目标。");
159
+ lines.push("");
160
+ return lines.join("\n");
161
+ }
162
+ // ── 落盘导出 ─────────────────────────────────────────────────────
163
+ /**
164
+ * 生成交接文档并写入 {workspaceRoot}/.patchwarden/goals/{goalId}/handoff.md。
165
+ *
166
+ * @param goalId Goal 标识
167
+ * @param goalStatus 当前 GoalStatus 快照(由调用方读取后传入)
168
+ * @param workspaceRoot 工作区根目录(可选,默认从 getConfig().workspaceRoot 获取)
169
+ * @returns handoff_path(绝对路径)和 content_preview(前 500 字符,超出则追加 "...")
170
+ */
171
+ export function exportHandoff(goalId, goalStatus, workspaceRoot) {
172
+ const content = generateHandoff(goalId, goalStatus);
173
+ const root = workspaceRoot ?? getConfig().workspaceRoot;
174
+ const goalDir = resolve(root, ".patchwarden", "goals", goalId);
175
+ mkdirSync(goalDir, { recursive: true });
176
+ const handoffPath = join(goalDir, "handoff.md");
177
+ writeFileSync(handoffPath, content, "utf-8");
178
+ const preview = content.length > 500 ? content.slice(0, 500) + "..." : content;
179
+ return {
180
+ handoff_path: handoffPath,
181
+ content_preview: preview,
182
+ };
183
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * v0.8.0: Subgoal 状态同步 — 当任务状态变为 done_by_agent 时回写关联 subgoal 状态。
3
+ *
4
+ * 设计原则:
5
+ * - 向后兼容:无 goal_id/subgoal_id 关联的任务直接返回,不影响现有流程。
6
+ * - 错误隔离:subgoal 同步失败只记录到 stderr,不阻断任务完成主流程。
7
+ * - 仅同步 running → done_by_agent 转换;其他状态由 audit_task / 显式 API 处理。
8
+ */
9
+ /**
10
+ * v0.8.0: 当任务状态变为 done_by_agent 时,同步更新关联的 subgoal 状态。
11
+ *
12
+ * 行为:
13
+ * - 如果 taskMeta.subgoal_id 或 goal_id 为 null/undefined,直接返回(向后兼容)。
14
+ * - 读取 goal_status.json,找到对应 subgoal。
15
+ * - 仅当 subgoal 当前状态为 "running" 时,更新为 "done_by_agent"。
16
+ * - 其他状态(ready/accepted/rejected/needs_fix/done_by_agent)保持不变。
17
+ * - 所有操作用 try/catch 包裹,失败时只记录到 stderr 不阻断主流程。
18
+ *
19
+ * @param taskId 任务 id(仅用于日志)
20
+ * @param taskMeta 任务的 goal 关联元信息(goal_id / subgoal_id)
21
+ * @param workspaceRoot 可选的工作区根目录(测试用),默认从 getConfig() 读取
22
+ */
23
+ export declare function syncSubgoalOnTaskDone(taskId: string, taskMeta: {
24
+ goal_id?: string | null;
25
+ subgoal_id?: string | null;
26
+ }, workspaceRoot?: string): void;
27
+ /**
28
+ * v0.8.0: 从 task 的 status.json 读取 goal_id/subgoal_id 关联信息。
29
+ *
30
+ * 行为:
31
+ * - 读取 {taskDir}/status.json,解析其中的 goal_id / subgoal_id 字段。
32
+ * - 任何错误(文件不存在、JSON 解析失败等)都返回 { goal_id: null, subgoal_id: null },
33
+ * 不抛错,保证调用方流程不受影响。
34
+ *
35
+ * @param taskDir 任务目录路径(包含 status.json)
36
+ */
37
+ export declare function readTaskGoalMeta(taskDir: string): {
38
+ goal_id: string | null;
39
+ subgoal_id: string | null;
40
+ };
@@ -0,0 +1,72 @@
1
+ /**
2
+ * v0.8.0: Subgoal 状态同步 — 当任务状态变为 done_by_agent 时回写关联 subgoal 状态。
3
+ *
4
+ * 设计原则:
5
+ * - 向后兼容:无 goal_id/subgoal_id 关联的任务直接返回,不影响现有流程。
6
+ * - 错误隔离:subgoal 同步失败只记录到 stderr,不阻断任务完成主流程。
7
+ * - 仅同步 running → done_by_agent 转换;其他状态由 audit_task / 显式 API 处理。
8
+ */
9
+ import { readFileSync } from "node:fs";
10
+ import { join } from "node:path";
11
+ import { readGoalStatus, writeGoalStatus } from "./goalStore.js";
12
+ import { updateSubgoalStatus } from "./goalStatus.js";
13
+ // ── 同步函数 ──────────────────────────────────────────────────────
14
+ /**
15
+ * v0.8.0: 当任务状态变为 done_by_agent 时,同步更新关联的 subgoal 状态。
16
+ *
17
+ * 行为:
18
+ * - 如果 taskMeta.subgoal_id 或 goal_id 为 null/undefined,直接返回(向后兼容)。
19
+ * - 读取 goal_status.json,找到对应 subgoal。
20
+ * - 仅当 subgoal 当前状态为 "running" 时,更新为 "done_by_agent"。
21
+ * - 其他状态(ready/accepted/rejected/needs_fix/done_by_agent)保持不变。
22
+ * - 所有操作用 try/catch 包裹,失败时只记录到 stderr 不阻断主流程。
23
+ *
24
+ * @param taskId 任务 id(仅用于日志)
25
+ * @param taskMeta 任务的 goal 关联元信息(goal_id / subgoal_id)
26
+ * @param workspaceRoot 可选的工作区根目录(测试用),默认从 getConfig() 读取
27
+ */
28
+ export function syncSubgoalOnTaskDone(taskId, taskMeta, workspaceRoot) {
29
+ const goalId = taskMeta.goal_id;
30
+ const subgoalId = taskMeta.subgoal_id;
31
+ if (!goalId || !subgoalId)
32
+ return;
33
+ try {
34
+ const goalStatus = readGoalStatus(goalId, workspaceRoot);
35
+ const subgoal = goalStatus.subgoals.find((s) => s.id === subgoalId);
36
+ if (!subgoal)
37
+ return;
38
+ if (subgoal.status !== "running")
39
+ return;
40
+ const updated = updateSubgoalStatus(goalStatus, subgoalId, "done_by_agent");
41
+ writeGoalStatus(goalId, updated, workspaceRoot);
42
+ }
43
+ catch (err) {
44
+ // subgoal 同步失败不应影响任务完成流程,只记录到 stderr
45
+ console.error(`[goal] syncSubgoalOnTaskDone failed for task ${taskId}: ${err instanceof Error ? err.message : String(err)}`);
46
+ }
47
+ }
48
+ // ── Task 元信息读取 ───────────────────────────────────────────────
49
+ /**
50
+ * v0.8.0: 从 task 的 status.json 读取 goal_id/subgoal_id 关联信息。
51
+ *
52
+ * 行为:
53
+ * - 读取 {taskDir}/status.json,解析其中的 goal_id / subgoal_id 字段。
54
+ * - 任何错误(文件不存在、JSON 解析失败等)都返回 { goal_id: null, subgoal_id: null },
55
+ * 不抛错,保证调用方流程不受影响。
56
+ *
57
+ * @param taskDir 任务目录路径(包含 status.json)
58
+ */
59
+ export function readTaskGoalMeta(taskDir) {
60
+ try {
61
+ const statusFile = join(taskDir, "status.json");
62
+ const raw = readFileSync(statusFile, "utf-8");
63
+ const data = JSON.parse(raw);
64
+ return {
65
+ goal_id: typeof data.goal_id === "string" ? data.goal_id : null,
66
+ subgoal_id: typeof data.subgoal_id === "string" ? data.subgoal_id : null,
67
+ };
68
+ }
69
+ catch {
70
+ return { goal_id: null, subgoal_id: null };
71
+ }
72
+ }
@@ -0,0 +1,96 @@
1
+ /**
2
+ * v1.0.0 Part B: Worktree 隔离管理器 — 在 workspaceRoot 下管理 git worktree,
3
+ * 为每个 subgoal task 提供独立的代码副本,避免并发任务互相污染工作区。
4
+ *
5
+ * 目录结构:
6
+ * {workspaceRoot}/_workspacetrees/{worktree_id}/
7
+ * └── worktree_status.json 机器可读的状态文件(原子写)
8
+ * {workspaceRoot}/.patchwarden/worktree-archive/{worktree_id}.json
9
+ * 归档的已 discard 状态(worktree 目录被删除后保留审计记录)
10
+ *
11
+ * 安全约束:
12
+ * - 所有路径经 guardWorkspacePath + guardSensitivePath 校验
13
+ * - git 命令只用 child_process.execFileSync(不使用 shell),白名单仅
14
+ * git worktree add/remove/prune + git merge + git branch
15
+ * - createWorktree 失败时清理半成品 worktree 目录与临时 branch
16
+ * - 不暴露通用 shell,不 blanket-kill watcher(worktree 与 watcher 无关)
17
+ *
18
+ * 所有文件系统函数都接受可选的 workspaceRoot 参数用于测试;
19
+ * 默认从 getConfig().workspaceRoot 读取。
20
+ */
21
+ export interface WorktreeStatus {
22
+ worktree_id: string;
23
+ goal_id: string;
24
+ subgoal_id: string;
25
+ path: string;
26
+ created_at: string;
27
+ status: "active" | "merged" | "discarded";
28
+ branch: string;
29
+ merged_at?: string;
30
+ discarded_at?: string;
31
+ }
32
+ /** worktree 根目录名,放在 workspaceRoot 下。 */
33
+ export declare const WorktreeDir = "_workspacetrees";
34
+ /**
35
+ * 返回 `<workspaceRoot>/_workspacetrees` 目录路径。不自动创建目录。
36
+ */
37
+ export declare function getWorktreesDir(workspaceRoot?: string): string;
38
+ /**
39
+ * 返回 `<getWorktreesDir()>/<worktreeId>` 路径。
40
+ */
41
+ export declare function getWorktreeDir(worktreeId: string, workspaceRoot?: string): string;
42
+ /**
43
+ * 为指定 goal/subgoal 创建一个隔离的 git worktree。
44
+ *
45
+ * 流程:
46
+ * 1. 生成 worktreeId(内部随机),拼出 worktreePath 与 branch
47
+ * 2. guardWorkspacePath + guardSensitivePath 校验路径
48
+ * 3. `git worktree add -b <branch> <worktreePath>` 创建 worktree
49
+ * 4. 原子写入 worktree_status.json(status="active")
50
+ *
51
+ * 失败时清理:若 git worktree add 或写 status 失败,移除已创建的 worktree
52
+ * 目录与临时 branch,抛出 PatchWardenError("worktree_create_failed")。
53
+ *
54
+ * @returns { worktreeId, worktreePath, branch }
55
+ */
56
+ export declare function createWorktree(goalId: string, subgoalId: string, workspaceRoot: string): {
57
+ worktreeId: string;
58
+ worktreePath: string;
59
+ branch: string;
60
+ };
61
+ /**
62
+ * 读取 worktree_status.json。不存在返回 null。
63
+ * 路径逃逸或敏感路径会抛 PatchWardenError(不静默吞掉安全违规)。
64
+ */
65
+ export declare function readWorktreeStatus(worktreeId: string, workspaceRoot?: string): WorktreeStatus | null;
66
+ /**
67
+ * 将 worktree 的 branch 合并回主工作区(workspaceRoot)。
68
+ *
69
+ * 流程:
70
+ * 1. 读取 worktree_status.json,校验 status === "active"
71
+ * 2. `git merge <branch>`(在 workspaceRoot 执行)
72
+ * 3. 原子更新 worktree_status.json:status="merged",merged_at=ISO timestamp
73
+ *
74
+ * 合并失败时抛 PatchWardenError("worktree_merge_failed"),不删除 worktree
75
+ * (保留供人工排查冲突)。
76
+ */
77
+ export declare function mergeWorktree(worktreeId: string, workspaceRoot: string): {
78
+ status: "merged";
79
+ };
80
+ /**
81
+ * 丢弃 worktree:移除 worktree 目录与临时 branch,归档最终状态。
82
+ *
83
+ * 流程:
84
+ * 1. 读取 worktree_status.json(在 remove 之前读取,因为 status 文件位于
85
+ * worktree 目录内),校验 status === "active"
86
+ * 2. `git worktree remove --force <worktreePath>`
87
+ * 3. `git branch -D <branch>` 删除临时 branch
88
+ * 4. 把更新后的 status(discarded)写到归档目录
89
+ * `<workspaceRoot>/.patchwarden/worktree-archive/<worktreeId>.json`
90
+ * (.patchwarden 始终为安全路径,用 guardWorkspacePath 校验)
91
+ *
92
+ * 移除失败抛 PatchWardenError("worktree_discard_failed")。
93
+ */
94
+ export declare function discardWorktree(worktreeId: string, workspaceRoot: string): {
95
+ status: "discarded";
96
+ };