@opengsd/gsd-pi 1.1.1-dev.616a1a1 → 1.1.1-dev.74e8dd1

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 (232) hide show
  1. package/dist/resources/.managed-resources-content-hash +1 -1
  2. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +167 -16
  3. package/dist/resources/extensions/gsd/auto/phases.js +4 -3
  4. package/dist/resources/extensions/gsd/auto-dashboard.js +15 -4
  5. package/dist/resources/extensions/gsd/auto-dispatch.js +39 -0
  6. package/dist/resources/extensions/gsd/auto-post-unit.js +113 -7
  7. package/dist/resources/extensions/gsd/auto-prompts.js +9 -0
  8. package/dist/resources/extensions/gsd/auto-recovery.js +4 -4
  9. package/dist/resources/extensions/gsd/auto-start.js +94 -15
  10. package/dist/resources/extensions/gsd/auto-unit-tool-scope.js +2 -1
  11. package/dist/resources/extensions/gsd/auto.js +22 -4
  12. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +79 -0
  13. package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +43 -0
  14. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +30 -9
  15. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +16 -10
  16. package/dist/resources/extensions/gsd/commands/catalog.js +6 -1
  17. package/dist/resources/extensions/gsd/commands/handlers/core.js +6 -2
  18. package/dist/resources/extensions/gsd/commands/handlers/ops.js +7 -3
  19. package/dist/resources/extensions/gsd/commands-maintenance.js +172 -2
  20. package/dist/resources/extensions/gsd/commands-mcp-status.js +107 -59
  21. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +3 -1
  22. package/dist/resources/extensions/gsd/commands-verdict.js +1 -1
  23. package/dist/resources/extensions/gsd/config-overlay.js +2 -1
  24. package/dist/resources/extensions/gsd/error-classifier.js +2 -1
  25. package/dist/resources/extensions/gsd/exec-sandbox.js +2 -0
  26. package/dist/resources/extensions/gsd/gsd-db.js +37 -4
  27. package/dist/resources/extensions/gsd/guided-flow.js +1 -1
  28. package/dist/resources/extensions/gsd/mcp-filter.js +3 -0
  29. package/dist/resources/extensions/gsd/mcp-project-config.js +67 -8
  30. package/dist/resources/extensions/gsd/migration-auto-check.js +2 -2
  31. package/dist/resources/extensions/gsd/prompts/run-uat.md +10 -4
  32. package/dist/resources/extensions/gsd/prompts/system.md +3 -1
  33. package/dist/resources/extensions/gsd/safety/destructive-guard.js +3 -0
  34. package/dist/resources/extensions/gsd/skill-activation.js +20 -3
  35. package/dist/resources/extensions/gsd/state-reconciliation/drift/artifact-db.js +4 -2
  36. package/dist/resources/extensions/gsd/state-reconciliation/drift/project-md.js +1 -1
  37. package/dist/resources/extensions/gsd/state-reconciliation/drift/roadmap.js +18 -1
  38. package/dist/resources/extensions/gsd/state-reconciliation/index.js +6 -0
  39. package/dist/resources/extensions/gsd/state.js +15 -12
  40. package/dist/resources/extensions/gsd/tool-presentation-plan.js +120 -0
  41. package/dist/resources/extensions/gsd/tools/exec-tool.js +109 -0
  42. package/dist/resources/extensions/gsd/tools/plan-slice.js +14 -9
  43. package/dist/resources/extensions/gsd/tools/reopen-milestone.js +2 -2
  44. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +366 -3
  45. package/dist/resources/extensions/gsd/unit-context-manifest.js +8 -3
  46. package/dist/resources/extensions/gsd/validation-block-guard.js +2 -0
  47. package/dist/resources/extensions/gsd/workflow-mcp-auto-prep.js +3 -1
  48. package/dist/resources/extensions/gsd/workflow-mcp.js +5 -1
  49. package/dist/resources/extensions/gsd/worktree-lifecycle.js +24 -0
  50. package/dist/resources/extensions/mcp-client/manager.js +31 -1
  51. package/dist/web/standalone/.next/BUILD_ID +1 -1
  52. package/dist/web/standalone/.next/app-path-routes-manifest.json +4 -4
  53. package/dist/web/standalone/.next/build-manifest.json +2 -2
  54. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  55. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  56. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  57. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  58. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  59. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  60. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  61. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  62. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  63. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  64. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  65. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  66. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  67. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  68. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  69. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  70. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  71. package/dist/web/standalone/.next/server/app/index.html +1 -1
  72. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  76. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  77. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  78. package/dist/web/standalone/.next/server/app-paths-manifest.json +4 -4
  79. package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
  80. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  81. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  82. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  83. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  84. package/package.json +2 -2
  85. package/packages/cloud-mcp-gateway/package.json +2 -2
  86. package/packages/contracts/dist/workflow.d.ts +14 -0
  87. package/packages/contracts/dist/workflow.d.ts.map +1 -1
  88. package/packages/contracts/dist/workflow.js +16 -0
  89. package/packages/contracts/dist/workflow.js.map +1 -1
  90. package/packages/contracts/package.json +1 -1
  91. package/packages/daemon/package.json +4 -4
  92. package/packages/gsd-agent-core/package.json +5 -5
  93. package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  94. package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  95. package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.js +10 -0
  96. package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.js.map +1 -1
  97. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts +1 -0
  98. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  99. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +72 -31
  100. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  101. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-extension-dialogs.d.ts.map +1 -1
  102. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-extension-dialogs.js +2 -0
  103. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-extension-dialogs.js.map +1 -1
  104. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.d.ts +1 -1
  105. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.d.ts.map +1 -1
  106. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.js +1 -1
  107. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.js.map +1 -1
  108. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  109. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js +1 -0
  110. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js.map +1 -1
  111. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-settings.d.ts.map +1 -1
  112. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-settings.js +5 -0
  113. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-settings.js.map +1 -1
  114. package/packages/gsd-agent-modes/package.json +7 -7
  115. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  116. package/packages/mcp-server/dist/workflow-tools.js +82 -0
  117. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  118. package/packages/mcp-server/package.json +3 -3
  119. package/packages/native/package.json +1 -1
  120. package/packages/pi-agent-core/package.json +1 -1
  121. package/packages/pi-ai/dist/image-models.generated.d.ts +15 -0
  122. package/packages/pi-ai/dist/image-models.generated.d.ts.map +1 -1
  123. package/packages/pi-ai/dist/image-models.generated.js +15 -0
  124. package/packages/pi-ai/dist/image-models.generated.js.map +1 -1
  125. package/packages/pi-ai/dist/models.generated.d.ts +338 -17
  126. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  127. package/packages/pi-ai/dist/models.generated.js +412 -112
  128. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  129. package/packages/pi-ai/package.json +1 -1
  130. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +3 -0
  131. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  132. package/packages/pi-coding-agent/dist/core/settings-manager.js +11 -0
  133. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  134. package/packages/pi-coding-agent/package.json +7 -7
  135. package/packages/pi-tui/dist/terminal.d.ts +1 -0
  136. package/packages/pi-tui/dist/terminal.d.ts.map +1 -1
  137. package/packages/pi-tui/dist/terminal.js +8 -4
  138. package/packages/pi-tui/dist/terminal.js.map +1 -1
  139. package/packages/pi-tui/package.json +1 -1
  140. package/packages/rpc-client/package.json +2 -2
  141. package/pkg/package.json +1 -1
  142. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +196 -16
  143. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +239 -63
  144. package/src/resources/extensions/gsd/auto/phases.ts +5 -3
  145. package/src/resources/extensions/gsd/auto-dashboard.ts +16 -4
  146. package/src/resources/extensions/gsd/auto-dispatch.ts +48 -0
  147. package/src/resources/extensions/gsd/auto-post-unit.ts +138 -7
  148. package/src/resources/extensions/gsd/auto-prompts.ts +9 -0
  149. package/src/resources/extensions/gsd/auto-recovery.ts +4 -4
  150. package/src/resources/extensions/gsd/auto-start.ts +112 -17
  151. package/src/resources/extensions/gsd/auto-unit-tool-scope.ts +2 -1
  152. package/src/resources/extensions/gsd/auto.ts +35 -3
  153. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +86 -0
  154. package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +51 -0
  155. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +51 -14
  156. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +21 -10
  157. package/src/resources/extensions/gsd/commands/catalog.ts +6 -1
  158. package/src/resources/extensions/gsd/commands/handlers/core.ts +6 -2
  159. package/src/resources/extensions/gsd/commands/handlers/ops.ts +7 -3
  160. package/src/resources/extensions/gsd/commands-maintenance.ts +197 -2
  161. package/src/resources/extensions/gsd/commands-mcp-status.ts +134 -57
  162. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +4 -1
  163. package/src/resources/extensions/gsd/commands-verdict.ts +1 -1
  164. package/src/resources/extensions/gsd/config-overlay.ts +3 -1
  165. package/src/resources/extensions/gsd/error-classifier.ts +2 -1
  166. package/src/resources/extensions/gsd/exec-sandbox.ts +4 -0
  167. package/src/resources/extensions/gsd/gsd-db.ts +41 -6
  168. package/src/resources/extensions/gsd/guided-flow.ts +1 -1
  169. package/src/resources/extensions/gsd/mcp-filter.ts +3 -0
  170. package/src/resources/extensions/gsd/mcp-project-config.ts +92 -10
  171. package/src/resources/extensions/gsd/migration-auto-check.ts +2 -2
  172. package/src/resources/extensions/gsd/preferences-types.ts +1 -1
  173. package/src/resources/extensions/gsd/prompts/run-uat.md +10 -4
  174. package/src/resources/extensions/gsd/prompts/system.md +3 -1
  175. package/src/resources/extensions/gsd/safety/destructive-guard.ts +3 -0
  176. package/src/resources/extensions/gsd/skill-activation.ts +20 -2
  177. package/src/resources/extensions/gsd/state-reconciliation/drift/artifact-db.ts +4 -2
  178. package/src/resources/extensions/gsd/state-reconciliation/drift/project-md.ts +1 -1
  179. package/src/resources/extensions/gsd/state-reconciliation/drift/roadmap.ts +20 -0
  180. package/src/resources/extensions/gsd/state-reconciliation/index.ts +6 -0
  181. package/src/resources/extensions/gsd/state-reconciliation/types.ts +1 -0
  182. package/src/resources/extensions/gsd/state.ts +16 -12
  183. package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +51 -0
  184. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +86 -0
  185. package/src/resources/extensions/gsd/tests/auto-start-orphan-bootstrap.test.ts +143 -2
  186. package/src/resources/extensions/gsd/tests/auto-start-project-milestone-reconcile.test.ts +24 -2
  187. package/src/resources/extensions/gsd/tests/commands-dispatcher-validation-block.test.ts +38 -3
  188. package/src/resources/extensions/gsd/tests/commands-verdict.test.ts +6 -2
  189. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +8 -0
  190. package/src/resources/extensions/gsd/tests/derive-state-helpers.test.ts +50 -13
  191. package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +60 -0
  192. package/src/resources/extensions/gsd/tests/exec-sandbox.test.ts +18 -0
  193. package/src/resources/extensions/gsd/tests/exec-tool.test.ts +69 -0
  194. package/src/resources/extensions/gsd/tests/gsd-rebuild.test.ts +199 -0
  195. package/src/resources/extensions/gsd/tests/gsd-recover.test.ts +75 -0
  196. package/src/resources/extensions/gsd/tests/integration/state-machine-live-validation.test.ts +13 -6
  197. package/src/resources/extensions/gsd/tests/mcp-filter.test.ts +15 -0
  198. package/src/resources/extensions/gsd/tests/mcp-project-config.test.ts +68 -0
  199. package/src/resources/extensions/gsd/tests/mcp-status.test.ts +177 -0
  200. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +3 -3
  201. package/src/resources/extensions/gsd/tests/parallel-skill-prompt-integration.test.ts +54 -7
  202. package/src/resources/extensions/gsd/tests/plan-slice.test.ts +39 -1
  203. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +10 -0
  204. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +18 -1
  205. package/src/resources/extensions/gsd/tests/reactive-executor.test.ts +36 -0
  206. package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +35 -0
  207. package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +1 -1
  208. package/src/resources/extensions/gsd/tests/skill-activation.test.ts +55 -0
  209. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +6 -2
  210. package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +52 -0
  211. package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +84 -10
  212. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +12 -2
  213. package/src/resources/extensions/gsd/tests/tui-header-lifecycle.test.ts +29 -6
  214. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +29 -6
  215. package/src/resources/extensions/gsd/tests/validation-block-guard.test.ts +21 -0
  216. package/src/resources/extensions/gsd/tests/workflow-mcp-auto-prep.test.ts +17 -2
  217. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +83 -0
  218. package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +25 -0
  219. package/src/resources/extensions/gsd/tool-presentation-plan.ts +167 -0
  220. package/src/resources/extensions/gsd/tools/exec-tool.ts +130 -0
  221. package/src/resources/extensions/gsd/tools/plan-slice.ts +14 -9
  222. package/src/resources/extensions/gsd/tools/reopen-milestone.ts +2 -2
  223. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +440 -2
  224. package/src/resources/extensions/gsd/unit-context-manifest.ts +14 -5
  225. package/src/resources/extensions/gsd/validation-block-guard.ts +2 -0
  226. package/src/resources/extensions/gsd/workflow-mcp-auto-prep.ts +2 -1
  227. package/src/resources/extensions/gsd/workflow-mcp.ts +5 -1
  228. package/src/resources/extensions/gsd/worktree-lifecycle.ts +26 -0
  229. package/src/resources/extensions/mcp-client/manager.ts +33 -1
  230. package/src/resources/extensions/mcp-client/tests/manager.test.ts +35 -0
  231. /package/dist/web/standalone/.next/static/{L9N5SPFi7f-Ne4u2uXzCe → eRWf-RI9bzbrwEurm_3uI}/_buildManifest.js +0 -0
  232. /package/dist/web/standalone/.next/static/{L9N5SPFi7f-Ne4u2uXzCe → eRWf-RI9bzbrwEurm_3uI}/_ssgManifest.js +0 -0
@@ -0,0 +1,167 @@
1
+ // Project/App: gsd-pi
2
+ // File Purpose: Resolve phase-aware tool surfaces for GSD model presentations.
3
+
4
+ export type ToolPresentationSurface = "provider-tools" | "claude-code-sdk" | "mcp" | "hybrid";
5
+
6
+ export interface ToolPresentationModel {
7
+ provider?: string;
8
+ api?: string;
9
+ id?: string;
10
+ }
11
+
12
+ export interface ToolPresentationPlan {
13
+ phase: string;
14
+ surface: ToolPresentationSurface;
15
+ model?: ToolPresentationModel;
16
+ allowedToolNames: string[];
17
+ presentedToolNames: string[];
18
+ blockedToolNames: Array<{ name: string; reason: string }>;
19
+ aliases: Array<{ requested: string; canonical: string }>;
20
+ diagnostics: string[];
21
+ }
22
+
23
+ export const RUN_UAT_WORKFLOW_TOOL_NAMES = [
24
+ "gsd_uat_exec",
25
+ "gsd_uat_result_save",
26
+ "gsd_resume",
27
+ "gsd_milestone_status",
28
+ "gsd_journal_query",
29
+ ] as const;
30
+
31
+ export const RUN_UAT_FORBIDDEN_TOOL_NAMES = [
32
+ "edit",
33
+ "write",
34
+ "gsd_exec",
35
+ "gsd_summary_save",
36
+ "gsd_save_gate_result",
37
+ "search-the-web",
38
+ "WebSearch",
39
+ "Bash",
40
+ "Write",
41
+ "Edit",
42
+ "mcp__gsd-workflow__*",
43
+ ] as const;
44
+
45
+ export const RUN_UAT_CLAUDE_NATIVE_TOOL_NAMES = [
46
+ "Read",
47
+ "Glob",
48
+ "Grep",
49
+ ] as const;
50
+
51
+ const WORKFLOW_ALIAS_TO_CANONICAL: Record<string, string> = {
52
+ gsd_save_decision: "gsd_decision_save",
53
+ gsd_update_requirement: "gsd_requirement_update",
54
+ gsd_save_requirement: "gsd_requirement_save",
55
+ gsd_save_summary: "gsd_summary_save",
56
+ gsd_generate_milestone_id: "gsd_milestone_generate_id",
57
+ gsd_milestone_plan: "gsd_plan_milestone",
58
+ gsd_slice_plan: "gsd_plan_slice",
59
+ gsd_task_plan: "gsd_plan_task",
60
+ gsd_slice_replan: "gsd_replan_slice",
61
+ gsd_complete_slice: "gsd_slice_complete",
62
+ gsd_milestone_complete: "gsd_complete_milestone",
63
+ gsd_milestone_validate: "gsd_validate_milestone",
64
+ gsd_roadmap_reassess: "gsd_reassess_roadmap",
65
+ gsd_complete_task: "gsd_task_complete",
66
+ gsd_reopen_task: "gsd_task_reopen",
67
+ gsd_reopen_slice: "gsd_slice_reopen",
68
+ gsd_reopen_milestone: "gsd_milestone_reopen",
69
+ };
70
+
71
+ export function canonicalWorkflowToolName(toolName: string): string {
72
+ const mcp = parseMcpToolName(toolName);
73
+ const baseName = mcp?.tool ?? toolName;
74
+ return WORKFLOW_ALIAS_TO_CANONICAL[baseName] ?? baseName;
75
+ }
76
+
77
+ export function parseMcpToolName(toolName: string): { server: string; tool: string } | null {
78
+ if (!toolName.startsWith("mcp__")) return null;
79
+ const toolSeparator = toolName.indexOf("__", "mcp__".length);
80
+ if (toolSeparator < 0) return null;
81
+ return {
82
+ server: toolName.slice("mcp__".length, toolSeparator),
83
+ tool: toolName.slice(toolSeparator + 2),
84
+ };
85
+ }
86
+
87
+ export function toWorkflowMcpToolName(serverName: string, toolName: string): string {
88
+ return `mcp__${serverName}__${canonicalWorkflowToolName(toolName)}`;
89
+ }
90
+
91
+ function dedupe(values: readonly string[]): string[] {
92
+ return [...new Set(values)];
93
+ }
94
+
95
+ function addBlockedTool(
96
+ blocked: ToolPresentationPlan["blockedToolNames"],
97
+ name: string,
98
+ reason: string,
99
+ ): void {
100
+ if (!blocked.some((entry) => entry.name === name)) {
101
+ blocked.push({ name, reason });
102
+ }
103
+ }
104
+
105
+ export function buildRunUatCanonicalToolNames(options: { includeBrowserTools?: readonly string[] } = {}): string[] {
106
+ return dedupe([
107
+ ...RUN_UAT_WORKFLOW_TOOL_NAMES,
108
+ ...(options.includeBrowserTools ?? []),
109
+ ]);
110
+ }
111
+
112
+ export function resolveToolPresentationPlan(options: {
113
+ phase: string;
114
+ surface: ToolPresentationSurface;
115
+ model?: ToolPresentationModel;
116
+ workflowMcpServerName?: string | null;
117
+ requestedToolNames?: readonly string[];
118
+ availableToolNames?: readonly string[];
119
+ includeBrowserTools?: readonly string[];
120
+ }): ToolPresentationPlan {
121
+ const requested = options.requestedToolNames ?? (
122
+ options.phase === "run-uat"
123
+ ? buildRunUatCanonicalToolNames({ includeBrowserTools: options.includeBrowserTools })
124
+ : []
125
+ );
126
+ const available = new Set(options.availableToolNames ?? requested);
127
+ const aliases: ToolPresentationPlan["aliases"] = [];
128
+ const blockedToolNames: ToolPresentationPlan["blockedToolNames"] = [];
129
+ const allowed: string[] = [];
130
+
131
+ for (const name of requested) {
132
+ const canonical = canonicalWorkflowToolName(name);
133
+ if (canonical !== name) aliases.push({ requested: name, canonical });
134
+ if (!available.has(name) && !available.has(canonical)) {
135
+ addBlockedTool(blockedToolNames, canonical, "not registered or provider-incompatible");
136
+ continue;
137
+ }
138
+ allowed.push(canonical);
139
+ }
140
+
141
+ const allowedToolNames = dedupe(allowed);
142
+ const workflowServerName = options.workflowMcpServerName || "gsd-workflow";
143
+ const presentedToolNames = options.surface === "claude-code-sdk" || options.surface === "mcp"
144
+ ? allowedToolNames.map((name) =>
145
+ name.startsWith("gsd_") || name === "ask_user_questions"
146
+ ? toWorkflowMcpToolName(workflowServerName, name)
147
+ : name
148
+ )
149
+ : allowedToolNames;
150
+
151
+ if (options.phase === "run-uat") {
152
+ for (const forbidden of RUN_UAT_FORBIDDEN_TOOL_NAMES) {
153
+ addBlockedTool(blockedToolNames, forbidden, "forbidden during run-uat");
154
+ }
155
+ }
156
+
157
+ return {
158
+ phase: options.phase,
159
+ surface: options.surface,
160
+ model: options.model,
161
+ allowedToolNames,
162
+ presentedToolNames,
163
+ blockedToolNames,
164
+ aliases,
165
+ diagnostics: [],
166
+ };
167
+ }
@@ -20,6 +20,7 @@ export interface ExecToolParams {
20
20
  cmd?: unknown;
21
21
  code?: unknown;
22
22
  purpose?: string;
23
+ metadata?: Record<string, unknown>;
23
24
  timeout_ms?: number;
24
25
  }
25
26
 
@@ -33,6 +34,44 @@ export interface ExecToolDeps {
33
34
  generateId?: () => string;
34
35
  }
35
36
 
37
+ export type UatExecIntent =
38
+ | "uat-artifact-check"
39
+ | "uat-runtime-check"
40
+ | "uat-browser-check"
41
+ | "uat-service-start"
42
+ | "uat-log-inspection";
43
+
44
+ export interface UatExecToolParams extends ExecToolParams {
45
+ milestoneId?: unknown;
46
+ sliceId?: unknown;
47
+ checkId?: unknown;
48
+ intent?: unknown;
49
+ expected?: unknown;
50
+ }
51
+
52
+ const UAT_EXEC_INTENTS: readonly UatExecIntent[] = [
53
+ "uat-artifact-check",
54
+ "uat-runtime-check",
55
+ "uat-browser-check",
56
+ "uat-service-start",
57
+ "uat-log-inspection",
58
+ ] as const;
59
+
60
+ const UAT_EXEC_INTENT_ALIASES: Record<string, UatExecIntent> = {
61
+ artifact: "uat-artifact-check",
62
+ "artifact-driven": "uat-artifact-check",
63
+ runtime: "uat-runtime-check",
64
+ "runtime-executable": "uat-runtime-check",
65
+ "live-runtime": "uat-runtime-check",
66
+ browser: "uat-browser-check",
67
+ "browser-executable": "uat-browser-check",
68
+ service: "uat-service-start",
69
+ "service-start": "uat-service-start",
70
+ log: "uat-log-inspection",
71
+ logs: "uat-log-inspection",
72
+ "log-inspection": "uat-log-inspection",
73
+ };
74
+
36
75
  export function buildExecOptions(
37
76
  baseDir: string,
38
77
  cfg: ContextModeConfig | undefined,
@@ -112,6 +151,39 @@ function normalizeScript(params: ExecToolParams): string | ToolExecutionResult {
112
151
  return paramError("script is required and must be a non-empty string");
113
152
  }
114
153
 
154
+ function normalizeRequiredString(value: unknown, field: string): string | ToolExecutionResult {
155
+ if (typeof value !== "string" || value.trim().length === 0) {
156
+ return paramError(`${field} is required and must be a non-empty string`);
157
+ }
158
+ return value.trim();
159
+ }
160
+
161
+ function normalizeUatIntent(value: unknown): UatExecIntent | ToolExecutionResult {
162
+ if (typeof value !== "string") {
163
+ return paramError(`intent is required and must be one of: ${UAT_EXEC_INTENTS.join(", ")}`);
164
+ }
165
+ const normalized = value.trim().toLowerCase();
166
+ if ((UAT_EXEC_INTENTS as readonly string[]).includes(normalized)) return normalized as UatExecIntent;
167
+ const alias = UAT_EXEC_INTENT_ALIASES[normalized];
168
+ if (alias) return alias;
169
+ return paramError(`invalid intent "${value}" — must be one of: ${UAT_EXEC_INTENTS.join(", ")}`);
170
+ }
171
+
172
+ function rejectUatScript(script: string): string | null {
173
+ const patterns: Array<{ re: RegExp; reason: string }> = [
174
+ { re: /\b(?:npm|pnpm|yarn|bun)\s+(?:i|install|add|remove|update|upgrade)\b/i, reason: "package dependency mutation is not allowed during UAT" },
175
+ { re: /\b(?:pip|pip3|python\s+-m\s+pip)\s+install\b/i, reason: "package dependency mutation is not allowed during UAT" },
176
+ { re: /\bgit\s+(?:add|commit|push|reset|checkout|switch|merge|rebase|clean|rm|mv|tag|branch)\b/i, reason: "git mutations are not allowed during UAT" },
177
+ { re: /\brm\s+-[^\n\r;|&]*r[^\n\r;|&]*f\b/i, reason: "destructive filesystem cleanup is not allowed during UAT" },
178
+ { re: /\b(?:env|printenv)\b(?:\s|$)/i, reason: "dumping environment variables is not allowed during UAT" },
179
+ { re: /\bcat\s+\.env(?:\b|\.|$)/i, reason: "reading credential files is not allowed during UAT" },
180
+ ];
181
+ for (const pattern of patterns) {
182
+ if (pattern.re.test(script)) return pattern.reason;
183
+ }
184
+ return null;
185
+ }
186
+
115
187
  function isToolExecutionResult(value: unknown): value is ToolExecutionResult {
116
188
  return typeof value === "object" && value !== null && Array.isArray((value as { content?: unknown }).content);
117
189
  }
@@ -266,6 +338,7 @@ export async function executeGsdExec(
266
338
  runtime,
267
339
  script,
268
340
  ...(typeof params.purpose === "string" ? { purpose: params.purpose } : {}),
341
+ ...(params.metadata && typeof params.metadata === "object" ? { metadata: params.metadata } : {}),
269
342
  ...(typeof params.timeout_ms === "number" ? { timeout_ms: params.timeout_ms } : {}),
270
343
  },
271
344
  opts,
@@ -281,6 +354,63 @@ export async function executeGsdExec(
281
354
  }
282
355
  }
283
356
 
357
+ export async function executeUatExec(
358
+ params: UatExecToolParams,
359
+ deps: ExecToolDeps,
360
+ ): Promise<ToolExecutionResult> {
361
+ const milestoneId = normalizeRequiredString(params.milestoneId, "milestoneId");
362
+ if (isToolExecutionResult(milestoneId)) return milestoneId;
363
+ const sliceId = normalizeRequiredString(params.sliceId, "sliceId");
364
+ if (isToolExecutionResult(sliceId)) return sliceId;
365
+ const checkId = normalizeRequiredString(params.checkId, "checkId");
366
+ if (isToolExecutionResult(checkId)) return checkId;
367
+ const intent = normalizeUatIntent(params.intent);
368
+ if (isToolExecutionResult(intent)) return intent;
369
+ const script = normalizeScript(params);
370
+ if (isToolExecutionResult(script)) return script;
371
+ const rejected = rejectUatScript(script);
372
+ if (rejected) {
373
+ return {
374
+ content: [{ type: "text", text: `Error: gsd_uat_exec blocked command — ${rejected}` }],
375
+ details: { operation: "gsd_uat_exec", error: "uat_exec_policy_block", reason: rejected },
376
+ isError: true,
377
+ };
378
+ }
379
+
380
+ const result = await executeGsdExec(
381
+ {
382
+ ...params,
383
+ script,
384
+ purpose: typeof params.purpose === "string" && params.purpose.trim().length > 0
385
+ ? params.purpose
386
+ : `UAT ${milestoneId}/${sliceId}/${checkId} (${intent})`,
387
+ metadata: {
388
+ kind: "uat_exec",
389
+ milestoneId,
390
+ sliceId,
391
+ checkId,
392
+ intent,
393
+ ...(typeof params.expected === "string" && params.expected.trim().length > 0
394
+ ? { expected: params.expected.trim() }
395
+ : {}),
396
+ },
397
+ },
398
+ deps,
399
+ );
400
+ const details = result.details ?? {};
401
+ return {
402
+ ...result,
403
+ details: {
404
+ ...details,
405
+ operation: "gsd_uat_exec",
406
+ milestoneId,
407
+ sliceId,
408
+ checkId,
409
+ intent,
410
+ },
411
+ };
412
+ }
413
+
284
414
  function formatResult(result: ExecSandboxResult): ToolExecutionResult {
285
415
  const headerLines = [
286
416
  `gsd_exec[${result.id}] runtime=${result.runtime} exit=${formatExit(result)} duration=${result.duration_ms}ms`,
@@ -355,15 +355,9 @@ export async function handlePlanSlice(
355
355
  deleteTask(params.milestoneId, params.sliceId, taskId);
356
356
  }
357
357
 
358
+ const existingTaskById = new Map(existingTasks.map((task) => [task.id, task]));
358
359
  for (const task of params.tasks) {
359
- insertTask({
360
- id: task.taskId,
361
- sliceId: params.sliceId,
362
- milestoneId: params.milestoneId,
363
- title: task.title,
364
- status: "pending",
365
- });
366
- upsertTaskPlanning(params.milestoneId, params.sliceId, task.taskId, {
360
+ const planning = {
367
361
  title: task.title,
368
362
  description: task.description,
369
363
  estimate: task.estimate,
@@ -374,7 +368,18 @@ export async function handlePlanSlice(
374
368
  observabilityImpact: task.observabilityImpact ?? "",
375
369
  fullPlanMd: task.fullPlanMd,
376
370
  targetRepositories: task.targetRepositories ?? params.targetRepositories ?? defaultTargets,
377
- });
371
+ };
372
+ const existingTask = existingTaskById.get(task.taskId);
373
+ if (!existingTask || !isClosedStatus(existingTask.status)) {
374
+ insertTask({
375
+ id: task.taskId,
376
+ sliceId: params.sliceId,
377
+ milestoneId: params.milestoneId,
378
+ title: task.title,
379
+ status: "pending",
380
+ });
381
+ }
382
+ upsertTaskPlanning(params.milestoneId, params.sliceId, task.taskId, planning);
378
383
  }
379
384
 
380
385
  // Seed quality gate rows inside the transaction — all-or-nothing with
@@ -13,7 +13,7 @@ import {
13
13
  getMilestone,
14
14
  getMilestoneSlices,
15
15
  getSliceTasks,
16
- updateMilestoneStatus,
16
+ reopenMilestoneStatus,
17
17
  updateSliceStatus,
18
18
  updateTaskStatus,
19
19
  transaction,
@@ -69,7 +69,7 @@ export async function handleReopenMilestone(
69
69
  return;
70
70
  }
71
71
 
72
- updateMilestoneStatus(params.milestoneId, "active", null);
72
+ reopenMilestoneStatus(params.milestoneId);
73
73
 
74
74
  const slices = getMilestoneSlices(params.milestoneId);
75
75
  slicesResetCount = slices.length;