@opengsd/gsd-pi 1.4.0-dev.5fc22c6f → 1.4.0-dev.c18009cd

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 (135) hide show
  1. package/dist/resources/.managed-resources-content-hash +1 -1
  2. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +58 -4
  3. package/dist/resources/extensions/gsd/artifact-verification.js +17 -5
  4. package/dist/resources/extensions/gsd/auto/closeout.js +45 -42
  5. package/dist/resources/extensions/gsd/auto/finalize.js +9 -0
  6. package/dist/resources/extensions/gsd/auto/orchestrator.js +1 -1
  7. package/dist/resources/extensions/gsd/auto/run-unit.js +5 -2
  8. package/dist/resources/extensions/gsd/auto/schedule-wakeup.js +3 -0
  9. package/dist/resources/extensions/gsd/auto.js +10 -3
  10. package/dist/resources/extensions/gsd/bootstrap/schedule-wakeup-tool.js +58 -9
  11. package/dist/resources/extensions/gsd/doctor-engine-checks.js +174 -16
  12. package/dist/resources/extensions/gsd/gsd-db.js +52 -0
  13. package/dist/resources/extensions/gsd/migrate-external.js +13 -4
  14. package/dist/resources/extensions/gsd/milestone-merge-transaction.js +3 -3
  15. package/dist/resources/extensions/gsd/repo-identity.js +16 -0
  16. package/dist/resources/extensions/gsd/root-write-leak-guard.js +4 -2
  17. package/dist/resources/extensions/gsd/unit-registry.js +27 -2
  18. package/dist/resources/extensions/gsd/worktree-lifecycle.js +67 -5
  19. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  20. package/dist/web/standalone/.next/BUILD_ID +1 -1
  21. package/dist/web/standalone/.next/app-path-routes-manifest.json +6 -6
  22. package/dist/web/standalone/.next/build-manifest.json +2 -2
  23. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  24. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  25. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  26. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  27. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  28. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  29. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  30. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  31. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  32. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  33. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  34. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  35. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  36. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  37. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  38. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  39. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  40. package/dist/web/standalone/.next/server/app/index.html +1 -1
  41. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  42. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  43. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  44. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  45. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  46. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  47. package/dist/web/standalone/.next/server/app-paths-manifest.json +6 -6
  48. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  49. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  50. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  51. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  52. package/integrations/hermes/CONTEXT.md +47 -0
  53. package/integrations/hermes/README.md +21 -0
  54. package/integrations/hermes/docs/issue-1162-grilling.md +197 -0
  55. package/integrations/hermes/docs/setup.md +4 -0
  56. package/integrations/hermes/open_gsd_hermes/commands.py +141 -0
  57. package/integrations/hermes/open_gsd_hermes/config.py +2 -0
  58. package/integrations/hermes/open_gsd_hermes/gsd_client.py +594 -31
  59. package/integrations/hermes/open_gsd_hermes/notifications.py +10 -1
  60. package/integrations/hermes/plugin.yaml +4 -0
  61. package/integrations/hermes/tests/test_config.py +6 -0
  62. package/integrations/hermes/tests/test_gsd_client_cache.py +3 -0
  63. package/integrations/hermes/tests/test_gsd_client_execute.py +35 -0
  64. package/integrations/hermes/tests/test_gsd_client_process_cwd.py +22 -0
  65. package/integrations/hermes/tests/test_gsd_client_timeout.py +98 -0
  66. package/integrations/hermes/tests/test_milestone_client.py +447 -0
  67. package/integrations/hermes/tests/test_new_milestone_command.py +313 -0
  68. package/integrations/hermes/tests/test_supervisor_fsm.py +40 -0
  69. package/package.json +1 -1
  70. package/packages/cloud-mcp-gateway/package.json +2 -2
  71. package/packages/contracts/package.json +1 -1
  72. package/packages/daemon/dist/session-manager.js +1 -1
  73. package/packages/daemon/dist/session-manager.js.map +1 -1
  74. package/packages/daemon/package.json +4 -4
  75. package/packages/gsd-agent-core/dist/session/agent-session-navigation.d.ts.map +1 -1
  76. package/packages/gsd-agent-core/dist/session/agent-session-navigation.js +4 -1
  77. package/packages/gsd-agent-core/dist/session/agent-session-navigation.js.map +1 -1
  78. package/packages/gsd-agent-core/package.json +5 -5
  79. package/packages/gsd-agent-modes/package.json +7 -7
  80. package/packages/mcp-server/dist/server.d.ts +18 -5
  81. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  82. package/packages/mcp-server/dist/server.js +46 -5
  83. package/packages/mcp-server/dist/server.js.map +1 -1
  84. package/packages/mcp-server/dist/session-manager.js +1 -1
  85. package/packages/mcp-server/dist/session-manager.js.map +1 -1
  86. package/packages/mcp-server/package.json +4 -4
  87. package/packages/native/package.json +1 -1
  88. package/packages/pi-agent-core/package.json +1 -1
  89. package/packages/pi-ai/package.json +1 -1
  90. package/packages/pi-coding-agent/dist/core/session-manager-types.d.ts +1 -0
  91. package/packages/pi-coding-agent/dist/core/session-manager-types.d.ts.map +1 -1
  92. package/packages/pi-coding-agent/dist/core/session-manager-types.js.map +1 -1
  93. package/packages/pi-coding-agent/dist/core/session-manager.d.ts.map +1 -1
  94. package/packages/pi-coding-agent/dist/core/session-manager.js +3 -0
  95. package/packages/pi-coding-agent/dist/core/session-manager.js.map +1 -1
  96. package/packages/pi-coding-agent/package.json +7 -7
  97. package/packages/pi-tui/package.json +2 -2
  98. package/packages/rpc-client/package.json +2 -2
  99. package/pkg/package.json +1 -1
  100. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +64 -3
  101. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +212 -39
  102. package/src/resources/extensions/gsd/artifact-verification.ts +16 -4
  103. package/src/resources/extensions/gsd/auto/closeout.ts +54 -56
  104. package/src/resources/extensions/gsd/auto/finalize.ts +8 -0
  105. package/src/resources/extensions/gsd/auto/loop-deps.ts +1 -0
  106. package/src/resources/extensions/gsd/auto/orchestrator.ts +1 -1
  107. package/src/resources/extensions/gsd/auto/run-unit.ts +5 -2
  108. package/src/resources/extensions/gsd/auto/schedule-wakeup.ts +8 -0
  109. package/src/resources/extensions/gsd/auto.ts +9 -3
  110. package/src/resources/extensions/gsd/bootstrap/schedule-wakeup-tool.ts +80 -12
  111. package/src/resources/extensions/gsd/doctor-engine-checks.ts +230 -21
  112. package/src/resources/extensions/gsd/gsd-db.ts +72 -0
  113. package/src/resources/extensions/gsd/migrate-external.ts +14 -4
  114. package/src/resources/extensions/gsd/milestone-merge-transaction.ts +4 -4
  115. package/src/resources/extensions/gsd/repo-identity.ts +17 -0
  116. package/src/resources/extensions/gsd/root-write-leak-guard.ts +3 -2
  117. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +131 -5
  118. package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +43 -1
  119. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +54 -0
  120. package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +335 -0
  121. package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +4 -4
  122. package/src/resources/extensions/gsd/tests/migrate-external-worktree.test.ts +116 -0
  123. package/src/resources/extensions/gsd/tests/milestone-merge-stash-restore.test.ts +152 -22
  124. package/src/resources/extensions/gsd/tests/milestone-merge-transaction.test.ts +1 -1
  125. package/src/resources/extensions/gsd/tests/phases-merge-error-stops-auto.test.ts +50 -6
  126. package/src/resources/extensions/gsd/tests/root-write-leak-guard.test.ts +31 -9
  127. package/src/resources/extensions/gsd/tests/schedule-wakeup-tool.test.ts +167 -0
  128. package/src/resources/extensions/gsd/tests/state-corruption-2945.test.ts +1 -1
  129. package/src/resources/extensions/gsd/tests/unit-registry.test.ts +56 -0
  130. package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +4 -4
  131. package/src/resources/extensions/gsd/tests/worktree-lifecycle.test.ts +2 -2
  132. package/src/resources/extensions/gsd/unit-registry.ts +34 -2
  133. package/src/resources/extensions/gsd/worktree-lifecycle.ts +135 -11
  134. /package/dist/web/standalone/.next/static/{kIEFsCryyGy6RzifULbJl → L2UMqXELiDH3oz-7yguXp}/_buildManifest.js +0 -0
  135. /package/dist/web/standalone/.next/static/{kIEFsCryyGy6RzifULbJl → L2UMqXELiDH3oz-7yguXp}/_ssgManifest.js +0 -0
@@ -2,6 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import os
5
6
  import shlex
6
7
  from typing import Any, Callable
7
8
 
@@ -58,6 +59,7 @@ class GsdCommandRouter:
58
59
  "auto": self._cmd_auto,
59
60
  "cancel": self._cmd_cancel,
60
61
  "reply": self._cmd_reply,
62
+ "new-milestone": self._cmd_new_milestone,
61
63
  "help": self._cmd_help,
62
64
  }
63
65
  handler = handlers.get(sub, self._cmd_help)
@@ -69,6 +71,7 @@ class GsdCommandRouter:
69
71
  "- `/gsd bind <path>` — bind this chat to a project\n"
70
72
  "- `/gsd status` — show progress snapshot\n"
71
73
  "- `/gsd auto` — start auto mode (MCP)\n"
74
+ "- `/gsd new-milestone <spec>` or `--file <path>` — create a milestone (then `/gsd auto`)\n"
72
75
  "- `/gsd cancel` — cancel running session\n"
73
76
  "- `/gsd reply <text>` — resolve blocker"
74
77
  )
@@ -112,6 +115,15 @@ class GsdCommandRouter:
112
115
  return f"GSD status unavailable: {e}"
113
116
 
114
117
  async def _cmd_auto(self, _rest: list[str]) -> str:
118
+ # Symmetric co-run guard (issue #1162): refuse if a milestone run is
119
+ # in progress. The MCP server's "Session already active" rejection
120
+ # does NOT fire for a milestone subprocess (it's not registered and
121
+ # writes no auto.lock during planning), so this local gate is the
122
+ # only thing preventing an unsafe co-run.
123
+ if self._client.milestone_active():
124
+ return (
125
+ "Milestone creation is in progress. Run `/gsd cancel` first."
126
+ )
115
127
  try:
116
128
  project_dir = resolve_project_dir(
117
129
  self._config, self._binding_ctx()
@@ -140,6 +152,18 @@ class GsdCommandRouter:
140
152
 
141
153
  async def _cmd_cancel(self, _rest: list[str]) -> str:
142
154
  ctx = self._get_supervisor_ctx()
155
+ # Milestone subprocess is locally owned (issue #1162); route to the
156
+ # local SIGTERM path instead of MCP cancel, which cannot see it.
157
+ if self._client.milestone_active():
158
+ self._client.cancel_milestone()
159
+ ctx.session_id = None
160
+ ctx.state = SupervisorState.CANCELLED
161
+ ctx.notified_terminal = True
162
+ self._set_supervisor_ctx(ctx)
163
+ self._supervisor.stop()
164
+ if self._notifications is not None:
165
+ self._notifications.notify_terminal("cancelled")
166
+ return "Cancel requested."
143
167
  try:
144
168
  try:
145
169
  if ctx.session_id and ctx.project_dir:
@@ -175,6 +199,17 @@ class GsdCommandRouter:
175
199
  if not rest:
176
200
  return "Usage: `/gsd reply <your answer>`"
177
201
  text = " ".join(rest)
202
+ # Milestone planning blocker (issue #1162): the subprocess is locally
203
+ # owned, so its blocker replies must go to its stdin, not MCP
204
+ # gsd_resolve_blocker (which can't see our session).
205
+ if self._client.milestone_active():
206
+ if not self._client.milestone_pending_blocker_id():
207
+ return "No pending blocker for the active milestone session."
208
+ try:
209
+ self._client.respond_to_milestone_blocker(text)
210
+ except Exception as e:
211
+ return f"Could not send blocker response: {e}"
212
+ return "Blocker response sent."
178
213
  ctx = self._get_supervisor_ctx()
179
214
  if not ctx.session_id:
180
215
  return "No active GSD session. Run `/gsd auto` first."
@@ -184,3 +219,109 @@ class GsdCommandRouter:
184
219
  except Exception as e:
185
220
  return f"Could not send blocker response: {e}"
186
221
  return "Blocker response sent."
222
+
223
+ async def _cmd_new_milestone(self, rest: list[str]) -> str:
224
+ """Create a milestone via `gsd headless --supervised new-milestone`.
225
+
226
+ Planning-only: the plugin spawns the subprocess, owns its lifecycle,
227
+ and notifies on blocker/terminal events. Execution stays with
228
+ `/gsd auto`. See integrations/hermes/docs/issue-1162-grilling.md.
229
+ """
230
+ if not rest:
231
+ return (
232
+ "Usage: `/gsd new-milestone <spec>` "
233
+ "(or `--file <path>`)"
234
+ )
235
+ # --auto is intentionally rejected (Q5): execution is /gsd auto's job.
236
+ if rest[0] == "--auto":
237
+ return (
238
+ "Execution isn't chained from `/gsd new-milestone`. "
239
+ "Create the milestone, then run `/gsd auto`."
240
+ )
241
+ # Co-run guard (Q2): refuse if a milestone or auto session is active.
242
+ if self._client.milestone_active():
243
+ return (
244
+ "Milestone creation is in progress. Run `/gsd cancel` first."
245
+ )
246
+ ctx = self._get_supervisor_ctx()
247
+ if ctx.state in (SupervisorState.RUNNING, SupervisorState.BLOCKED):
248
+ return (
249
+ "GSD auto is running. Run `/gsd cancel` first."
250
+ )
251
+ # Resolve the bound project.
252
+ try:
253
+ project_dir = resolve_project_dir(
254
+ self._config, self._binding_ctx()
255
+ )
256
+ except BindingError as e:
257
+ return str(e)
258
+
259
+ # Input shape (Q6): bare positional → context_text, `--file <path>`
260
+ # → context_file.
261
+ context_text: str | None = None
262
+ context_file: str | None = None
263
+ if rest[0] == "--file":
264
+ if len(rest) < 2:
265
+ return "Usage: `/gsd new-milestone --file <path>`"
266
+ context_file = rest[1]
267
+ resolved_file = (
268
+ context_file
269
+ if os.path.isabs(context_file)
270
+ else os.path.join(project_dir, context_file)
271
+ )
272
+ if not os.path.isfile(resolved_file):
273
+ return f"Context file not found: `{context_file}`"
274
+ if not os.access(resolved_file, os.R_OK):
275
+ return f"Context file is not readable: `{context_file}`"
276
+ context_file = resolved_file
277
+ else:
278
+ context_text = " ".join(rest)
279
+
280
+ def _on_milestone_terminal(status: str) -> None:
281
+ milestone_ctx = self._get_supervisor_ctx()
282
+ if milestone_ctx.session_id != session_id:
283
+ return
284
+ normalized = status.lower()
285
+ if normalized in ("complete", "completed", "done"):
286
+ milestone_ctx.state = SupervisorState.COMPLETE
287
+ elif normalized == "cancelled":
288
+ milestone_ctx.state = SupervisorState.CANCELLED
289
+ else:
290
+ milestone_ctx.state = SupervisorState.FAILED
291
+ milestone_ctx.session_id = None
292
+ milestone_ctx.pending_blocker_id = None
293
+ milestone_ctx.notified_terminal = True
294
+ self._set_supervisor_ctx(milestone_ctx)
295
+
296
+ # Spawn the supervised subprocess; create_milestone returns a local
297
+ # session id immediately and continues the stream reader in the
298
+ # background.
299
+ try:
300
+ session_id = self._client.create_milestone(
301
+ project_dir,
302
+ context_text=context_text,
303
+ context_file=context_file,
304
+ notifications=self._notifications,
305
+ on_terminal=_on_milestone_terminal,
306
+ )
307
+ except ValueError:
308
+ return "Usage: `/gsd new-milestone <spec>` (or `--file <path>`)"
309
+ except Exception as e:
310
+ return f"Could not start milestone creation: {e}"
311
+
312
+ # Record the active milestone run so /gsd status, /gsd cancel, and the
313
+ # co-run gate (in _cmd_auto) see it. The supervisor poll loop is NOT
314
+ # used (the stream reader drives notifications); we only carry state.
315
+ self._supervisor.stop()
316
+ ctx.session_id = session_id
317
+ ctx.project_dir = project_dir
318
+ ctx.state = SupervisorState.RUNNING
319
+ ctx.last_progress = None
320
+ ctx.last_status = None
321
+ ctx.pending_blocker_id = None
322
+ ctx.notified_terminal = False
323
+ self._set_supervisor_ctx(ctx)
324
+ return (
325
+ f"🚀 Milestone creation started (session `{session_id}`). "
326
+ "I'll notify you when it's ready; run `/gsd auto` to execute."
327
+ )
@@ -19,6 +19,7 @@ class GsdConfig:
19
19
  bindings: dict[str, dict[str, str]] = field(default_factory=dict)
20
20
  poll_interval_seconds: int = 12
21
21
  cache_ttl_seconds: int = 45
22
+ mcp_read_timeout_seconds: float = 60.0
22
23
  notification_level: str = "normal"
23
24
  gsd_version_min: str = "2.53"
24
25
  gsd_version_max: str = "3.0"
@@ -35,6 +36,7 @@ class GsdConfig:
35
36
  bindings=dict(gsd.get("bindings") or {}),
36
37
  poll_interval_seconds=int(gsd.get("poll_interval_seconds", 12)),
37
38
  cache_ttl_seconds=int(gsd.get("cache_ttl_seconds", 45)),
39
+ mcp_read_timeout_seconds=float(gsd.get("mcp_read_timeout_seconds", 60.0)),
38
40
  notification_level=str(gsd.get("notification_level", "normal")),
39
41
  gsd_version_min=str(gsd.get("gsd_version_min", "2.53")),
40
42
  gsd_version_max=str(gsd.get("gsd_version_max", "3.0")),