@opengsd/gsd-pi 1.4.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -11
- package/dist/onboarding.js +11 -0
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/GSD-WORKFLOW.md +20 -25
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +118 -53
- package/dist/resources/extensions/cursor-cli/index.js +16 -0
- package/dist/resources/extensions/cursor-cli/models.js +63 -0
- package/dist/resources/extensions/cursor-cli/package.json +11 -0
- package/dist/resources/extensions/cursor-cli/readiness.js +128 -0
- package/dist/resources/extensions/cursor-cli/stream-adapter.js +353 -0
- package/dist/resources/extensions/gsd/activity-log.js +4 -1
- package/dist/resources/extensions/gsd/artifact-verification.js +25 -9
- package/dist/resources/extensions/gsd/auto/closeout.js +45 -42
- package/dist/resources/extensions/gsd/auto/finalize.js +9 -0
- package/dist/resources/extensions/gsd/auto/orchestrator.js +1 -1
- package/dist/resources/extensions/gsd/auto/run-unit.js +5 -2
- package/dist/resources/extensions/gsd/auto/schedule-wakeup.js +3 -0
- package/dist/resources/extensions/gsd/auto-dispatch.js +15 -5
- package/dist/resources/extensions/gsd/auto-model-selection.js +36 -18
- package/dist/resources/extensions/gsd/auto-prompts.js +76 -2
- package/dist/resources/extensions/gsd/auto-start.js +4 -3
- package/dist/resources/extensions/gsd/auto-timeout-recovery.js +4 -0
- package/dist/resources/extensions/gsd/auto-worktree.js +38 -11
- package/dist/resources/extensions/gsd/auto.js +23 -3
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +3 -0
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +114 -5
- package/dist/resources/extensions/gsd/bootstrap/schedule-wakeup-tool.js +58 -9
- package/dist/resources/extensions/gsd/bootstrap/tool-call-loop-guard.js +10 -2
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +14 -1
- package/dist/resources/extensions/gsd/codebase-generator.js +157 -19
- package/dist/resources/extensions/gsd/commands/handlers/core.js +40 -4
- package/dist/resources/extensions/gsd/commands-prefs-wizard.js +153 -1
- package/dist/resources/extensions/gsd/commands-worktree.js +11 -2
- package/dist/resources/extensions/gsd/db/queries.js +39 -0
- package/dist/resources/extensions/gsd/discussion-handoff.js +8 -3
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +8 -2
- package/dist/resources/extensions/gsd/doctor-engine-checks.js +206 -14
- package/dist/resources/extensions/gsd/doctor-providers.js +38 -10
- package/dist/resources/extensions/gsd/doctor.js +93 -3
- package/dist/resources/extensions/gsd/exec-sandbox.js +6 -2
- package/dist/resources/extensions/gsd/flat-phase-migration.js +74 -23
- package/dist/resources/extensions/gsd/git-service.js +14 -18
- package/dist/resources/extensions/gsd/gsd-db.js +52 -0
- package/dist/resources/extensions/gsd/guidance.js +1 -0
- package/dist/resources/extensions/gsd/guided-flow.js +40 -2
- package/dist/resources/extensions/gsd/key-manager.js +1 -0
- package/dist/resources/extensions/gsd/markdown-renderer.js +25 -8
- package/dist/resources/extensions/gsd/migrate-external.js +24 -7
- package/dist/resources/extensions/gsd/milestone-merge-transaction.js +3 -3
- package/dist/resources/extensions/gsd/preferences-diagnostics.js +24 -1
- package/dist/resources/extensions/gsd/preferences-models.js +6 -4
- package/dist/resources/extensions/gsd/preferences-validation.js +25 -2
- package/dist/resources/extensions/gsd/preferences.js +29 -2
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +4 -2
- package/dist/resources/extensions/gsd/redact-secrets.js +37 -0
- package/dist/resources/extensions/gsd/repo-identity.js +16 -0
- package/dist/resources/extensions/gsd/repository-registry.js +6 -1
- package/dist/resources/extensions/gsd/root-write-leak-guard.js +4 -2
- package/dist/resources/extensions/gsd/safe-fs.js +23 -2
- package/dist/resources/extensions/gsd/skills/gsd-headless/SKILL.md +7 -9
- package/dist/resources/extensions/gsd/skills/gsd-headless/references/commands.md +1 -0
- package/dist/resources/extensions/gsd/tools/plan-task.js +76 -8
- package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +40 -0
- package/dist/resources/extensions/gsd/unit-context-manifest.js +5 -4
- package/dist/resources/extensions/gsd/unit-registry.js +27 -2
- package/dist/resources/extensions/gsd/unit-runtime.js +21 -0
- package/dist/resources/extensions/gsd/workspace.js +26 -8
- package/dist/resources/extensions/gsd/worktree-lifecycle.js +67 -5
- package/dist/resources/extensions/gsd/worktree-manager.js +97 -4
- package/dist/tsconfig.extensions.tsbuildinfo +1 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +7 -7
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +7 -7
- package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/node_modules/@gsd/native/package.json +1 -1
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
- package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
- package/dist/wizard.d.ts +8 -0
- package/dist/wizard.js +8 -1
- package/integrations/hermes/CONTEXT.md +47 -0
- package/integrations/hermes/README.md +21 -0
- package/integrations/hermes/__init__.py +31 -0
- package/integrations/hermes/docs/issue-1162-grilling.md +197 -0
- package/integrations/hermes/docs/setup.md +4 -0
- package/integrations/hermes/open_gsd_hermes/commands.py +141 -0
- package/integrations/hermes/open_gsd_hermes/config.py +2 -0
- package/integrations/hermes/open_gsd_hermes/gsd_client.py +594 -31
- package/integrations/hermes/open_gsd_hermes/notifications.py +10 -1
- package/integrations/hermes/plugin.yaml +4 -0
- package/integrations/hermes/tests/test_config.py +6 -0
- package/integrations/hermes/tests/test_directory_loader.py +37 -0
- package/integrations/hermes/tests/test_gsd_client_cache.py +3 -0
- package/integrations/hermes/tests/test_gsd_client_execute.py +35 -0
- package/integrations/hermes/tests/test_gsd_client_process_cwd.py +22 -0
- package/integrations/hermes/tests/test_gsd_client_timeout.py +98 -0
- package/integrations/hermes/tests/test_milestone_client.py +447 -0
- package/integrations/hermes/tests/test_new_milestone_command.py +313 -0
- package/integrations/hermes/tests/test_supervisor_fsm.py +40 -0
- package/package.json +8 -8
- package/packages/cloud-mcp-gateway/dist/auth-store.d.ts +1 -0
- package/packages/cloud-mcp-gateway/dist/auth-store.d.ts.map +1 -1
- package/packages/cloud-mcp-gateway/dist/auth-store.js +21 -1
- package/packages/cloud-mcp-gateway/dist/auth-store.js.map +1 -1
- package/packages/cloud-mcp-gateway/dist/auth-store.test.js +34 -0
- package/packages/cloud-mcp-gateway/dist/auth-store.test.js.map +1 -1
- package/packages/cloud-mcp-gateway/package.json +2 -2
- package/packages/contracts/package.json +1 -1
- package/packages/daemon/dist/cloud-runtime.d.ts +2 -0
- package/packages/daemon/dist/cloud-runtime.d.ts.map +1 -1
- package/packages/daemon/dist/cloud-runtime.js +25 -1
- package/packages/daemon/dist/cloud-runtime.js.map +1 -1
- package/packages/daemon/dist/cloud-runtime.test.js +51 -0
- package/packages/daemon/dist/cloud-runtime.test.js.map +1 -1
- package/packages/daemon/dist/message-batcher.d.ts +7 -0
- package/packages/daemon/dist/message-batcher.d.ts.map +1 -1
- package/packages/daemon/dist/message-batcher.js +13 -0
- package/packages/daemon/dist/message-batcher.js.map +1 -1
- package/packages/daemon/dist/message-batcher.test.js +48 -0
- package/packages/daemon/dist/message-batcher.test.js.map +1 -1
- package/packages/daemon/dist/session-manager.js +1 -1
- package/packages/daemon/dist/session-manager.js.map +1 -1
- package/packages/daemon/package.json +5 -5
- package/packages/gsd-agent-core/dist/compaction/compaction.d.ts +13 -1
- package/packages/gsd-agent-core/dist/compaction/compaction.d.ts.map +1 -1
- package/packages/gsd-agent-core/dist/compaction/compaction.js +15 -1
- package/packages/gsd-agent-core/dist/compaction/compaction.js.map +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-navigation.d.ts.map +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-navigation.js +4 -1
- package/packages/gsd-agent-core/dist/session/agent-session-navigation.js.map +1 -1
- package/packages/gsd-agent-core/package.json +5 -5
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.js +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/rpc/rpc-client.js +1 -1
- package/packages/gsd-agent-modes/dist/modes/rpc/rpc-client.js.map +1 -1
- package/packages/gsd-agent-modes/package.json +7 -7
- package/packages/mcp-server/dist/server.d.ts +18 -5
- package/packages/mcp-server/dist/server.d.ts.map +1 -1
- package/packages/mcp-server/dist/server.js +46 -5
- package/packages/mcp-server/dist/server.js.map +1 -1
- package/packages/mcp-server/dist/session-manager.js +1 -1
- package/packages/mcp-server/dist/session-manager.js.map +1 -1
- package/packages/mcp-server/package.json +4 -4
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/dist/env-api-keys.d.ts.map +1 -1
- package/packages/pi-ai/dist/env-api-keys.js +3 -0
- package/packages/pi-ai/dist/env-api-keys.js.map +1 -1
- package/packages/pi-ai/dist/types.d.ts +2 -2
- package/packages/pi-ai/dist/types.d.ts.map +1 -1
- package/packages/pi-ai/dist/types.js.map +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/model-resolver.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-resolver.js +1 -0
- package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/session-manager-types.d.ts +1 -0
- package/packages/pi-coding-agent/dist/core/session-manager-types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/session-manager-types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/session-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/session-manager.js +3 -0
- package/packages/pi-coding-agent/dist/core/session-manager.js.map +1 -1
- package/packages/pi-coding-agent/package.json +7 -7
- package/packages/pi-tui/package.json +2 -2
- package/packages/rpc-client/dist/rpc-client.js +1 -1
- package/packages/rpc-client/dist/rpc-client.js.map +1 -1
- package/packages/rpc-client/package.json +2 -2
- package/pkg/package.json +1 -1
- package/scripts/link-workspace-packages.cjs +14 -4
- package/src/resources/GSD-WORKFLOW.md +20 -25
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +130 -51
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +371 -39
- package/src/resources/extensions/cursor-cli/index.ts +18 -0
- package/src/resources/extensions/cursor-cli/models.ts +65 -0
- package/src/resources/extensions/cursor-cli/package.json +11 -0
- package/src/resources/extensions/cursor-cli/readiness.ts +143 -0
- package/src/resources/extensions/cursor-cli/stream-adapter.ts +440 -0
- package/src/resources/extensions/cursor-cli/tests/index.test.ts +54 -0
- package/src/resources/extensions/cursor-cli/tests/readiness.test.ts +29 -0
- package/src/resources/extensions/cursor-cli/tests/stream-adapter.test.ts +166 -0
- package/src/resources/extensions/gsd/activity-log.ts +4 -1
- package/src/resources/extensions/gsd/artifact-verification.ts +25 -9
- package/src/resources/extensions/gsd/auto/closeout.ts +54 -56
- package/src/resources/extensions/gsd/auto/finalize.ts +8 -0
- package/src/resources/extensions/gsd/auto/loop-deps.ts +1 -0
- package/src/resources/extensions/gsd/auto/orchestrator.ts +1 -1
- package/src/resources/extensions/gsd/auto/run-unit.ts +5 -2
- package/src/resources/extensions/gsd/auto/schedule-wakeup.ts +8 -0
- package/src/resources/extensions/gsd/auto-dispatch.ts +17 -5
- package/src/resources/extensions/gsd/auto-model-selection.ts +49 -18
- package/src/resources/extensions/gsd/auto-prompts.ts +73 -2
- package/src/resources/extensions/gsd/auto-start.ts +4 -3
- package/src/resources/extensions/gsd/auto-timeout-recovery.ts +4 -0
- package/src/resources/extensions/gsd/auto-worktree.ts +50 -11
- package/src/resources/extensions/gsd/auto.ts +22 -3
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +3 -0
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +128 -5
- package/src/resources/extensions/gsd/bootstrap/schedule-wakeup-tool.ts +80 -12
- package/src/resources/extensions/gsd/bootstrap/tool-call-loop-guard.ts +12 -2
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +23 -1
- package/src/resources/extensions/gsd/codebase-generator.ts +179 -18
- package/src/resources/extensions/gsd/commands/handlers/core.ts +39 -3
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +135 -1
- package/src/resources/extensions/gsd/commands-worktree.ts +13 -2
- package/src/resources/extensions/gsd/db/queries.ts +40 -0
- package/src/resources/extensions/gsd/discussion-handoff.ts +10 -2
- package/src/resources/extensions/gsd/docs/preferences-reference.md +8 -2
- package/src/resources/extensions/gsd/doctor-engine-checks.ts +269 -18
- package/src/resources/extensions/gsd/doctor-providers.ts +39 -10
- package/src/resources/extensions/gsd/doctor-types.ts +5 -1
- package/src/resources/extensions/gsd/doctor.ts +95 -3
- package/src/resources/extensions/gsd/exec-sandbox.ts +6 -2
- package/src/resources/extensions/gsd/flat-phase-migration.ts +98 -22
- package/src/resources/extensions/gsd/git-service.ts +14 -17
- package/src/resources/extensions/gsd/gsd-db.ts +72 -0
- package/src/resources/extensions/gsd/guidance.ts +2 -0
- package/src/resources/extensions/gsd/guided-flow.ts +45 -2
- package/src/resources/extensions/gsd/key-manager.ts +1 -0
- package/src/resources/extensions/gsd/markdown-renderer.ts +25 -7
- package/src/resources/extensions/gsd/migrate-external.ts +23 -6
- package/src/resources/extensions/gsd/milestone-merge-transaction.ts +4 -4
- package/src/resources/extensions/gsd/model-router.ts +4 -5
- package/src/resources/extensions/gsd/preferences-diagnostics.ts +24 -0
- package/src/resources/extensions/gsd/preferences-models.ts +6 -3
- package/src/resources/extensions/gsd/preferences-validation.ts +28 -2
- package/src/resources/extensions/gsd/preferences.ts +38 -4
- package/src/resources/extensions/gsd/prompts/plan-slice.md +4 -2
- package/src/resources/extensions/gsd/redact-secrets.ts +40 -0
- package/src/resources/extensions/gsd/repo-identity.ts +17 -0
- package/src/resources/extensions/gsd/repository-registry.ts +7 -1
- package/src/resources/extensions/gsd/root-write-leak-guard.ts +3 -2
- package/src/resources/extensions/gsd/safe-fs.ts +22 -2
- package/src/resources/extensions/gsd/skills/gsd-headless/SKILL.md +7 -9
- package/src/resources/extensions/gsd/skills/gsd-headless/references/commands.md +1 -0
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +131 -5
- package/src/resources/extensions/gsd/tests/auto-migrating-recovery.test.ts +18 -0
- package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +178 -0
- package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +43 -1
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +54 -0
- package/src/resources/extensions/gsd/tests/auto-remote-session-lock-cleanup.test.ts +41 -0
- package/src/resources/extensions/gsd/tests/auto-runtime-state.test.ts +41 -0
- package/src/resources/extensions/gsd/tests/auto-session-scope.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/auto-start-bootstrap-await-3420.test.ts +3 -0
- package/src/resources/extensions/gsd/tests/auto-timeout-recovery-harness-abort.test.ts +55 -0
- package/src/resources/extensions/gsd/tests/auto-warning-noise-regression.test.ts +8 -0
- package/src/resources/extensions/gsd/tests/batch-task-query.test.ts +85 -0
- package/src/resources/extensions/gsd/tests/check-auto-start-ready-guard.test.ts +29 -0
- package/src/resources/extensions/gsd/tests/codebase-generator.test.ts +365 -0
- package/src/resources/extensions/gsd/tests/complete-milestone-excerpt.test.ts +62 -1
- package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +138 -1
- package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +397 -5
- package/src/resources/extensions/gsd/tests/doctor-workspace.test.ts +207 -0
- package/src/resources/extensions/gsd/tests/flat-phase-migration.test.ts +38 -1
- package/src/resources/extensions/gsd/tests/flat-rate-routing-guard.test.ts +87 -19
- package/src/resources/extensions/gsd/tests/gate-1b-orphan-discrimination.test.ts +89 -0
- package/src/resources/extensions/gsd/tests/guided-flow-session-isolation.test.ts +9 -0
- package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +4 -2
- package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +122 -0
- package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/migrate-external-worktree.test.ts +116 -0
- package/src/resources/extensions/gsd/tests/milestone-merge-stash-restore.test.ts +152 -22
- package/src/resources/extensions/gsd/tests/milestone-merge-transaction.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/pending-autostart-scope.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/phases-merge-error-stops-auto.test.ts +50 -6
- package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +17 -0
- package/src/resources/extensions/gsd/tests/plan-task.test.ts +264 -1
- package/src/resources/extensions/gsd/tests/preferences.test.ts +150 -0
- package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +142 -0
- package/src/resources/extensions/gsd/tests/recovery-verify-logs.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/redact-secrets.test.ts +56 -0
- package/src/resources/extensions/gsd/tests/register-hooks-flat-phase-migration.test.ts +73 -0
- package/src/resources/extensions/gsd/tests/register-hooks-loop-guard-auto.test.ts +275 -0
- package/src/resources/extensions/gsd/tests/repository-registry.test.ts +41 -2
- package/src/resources/extensions/gsd/tests/require-slice-discussion-dispatch.test.ts +55 -0
- package/src/resources/extensions/gsd/tests/root-write-leak-guard.test.ts +31 -9
- package/src/resources/extensions/gsd/tests/schedule-wakeup-tool.test.ts +167 -0
- package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +49 -0
- package/src/resources/extensions/gsd/tests/state-corruption-2945.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/status-per-repo.test.ts +94 -0
- package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +35 -0
- package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +63 -0
- package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +66 -18
- package/src/resources/extensions/gsd/tests/unit-registry.test.ts +56 -0
- package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +30 -0
- package/src/resources/extensions/gsd/tests/uok-gitops-turn-action.test.ts +52 -0
- package/src/resources/extensions/gsd/tests/validator-scope-parity.test.ts +53 -1
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +208 -1
- package/src/resources/extensions/gsd/tests/workspace.test.ts +18 -4
- package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/worktree-lifecycle.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +31 -1
- package/src/resources/extensions/gsd/tests/worktree-teardown-safety.test.ts +32 -0
- package/src/resources/extensions/gsd/tools/plan-task.ts +107 -9
- package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +58 -0
- package/src/resources/extensions/gsd/unit-context-manifest.ts +11 -5
- package/src/resources/extensions/gsd/unit-registry.ts +34 -2
- package/src/resources/extensions/gsd/unit-runtime.ts +42 -0
- package/src/resources/extensions/gsd/workspace.ts +42 -8
- package/src/resources/extensions/gsd/worktree-lifecycle.ts +135 -11
- package/src/resources/extensions/gsd/worktree-manager.ts +143 -5
- /package/dist/web/standalone/.next/static/{zQLcXm0Za8lexoXtgDpMG → bCg3aYrFN1DKZr2dqgE7L}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{zQLcXm0Za8lexoXtgDpMG → bCg3aYrFN1DKZr2dqgE7L}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""open-gsd-hermes — Hermes Agent plugin directory-loader bridge."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import importlib.util
|
|
6
|
+
import sys
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from types import ModuleType
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _load_package() -> ModuleType:
|
|
12
|
+
package_dir = Path(__file__).resolve().parent / "open_gsd_hermes"
|
|
13
|
+
init_file = package_dir / "__init__.py"
|
|
14
|
+
spec = importlib.util.spec_from_file_location(
|
|
15
|
+
"open_gsd_hermes",
|
|
16
|
+
init_file,
|
|
17
|
+
submodule_search_locations=[str(package_dir)],
|
|
18
|
+
)
|
|
19
|
+
if spec is None or spec.loader is None:
|
|
20
|
+
raise ImportError(f"Cannot load open_gsd_hermes from {init_file}")
|
|
21
|
+
|
|
22
|
+
module = importlib.util.module_from_spec(spec)
|
|
23
|
+
sys.modules["open_gsd_hermes"] = module
|
|
24
|
+
spec.loader.exec_module(module)
|
|
25
|
+
return module
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
_package = _load_package()
|
|
29
|
+
register = _package.register
|
|
30
|
+
|
|
31
|
+
__all__ = ["register"]
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# Issue #1162 — Grilling summary: `/gsd new-milestone` via Hermes
|
|
2
|
+
|
|
3
|
+
Source: `grill-with-docs` + `grilling` + `domain-modeling` skills on issue #1162.
|
|
4
|
+
This is the shared understanding the interview reached — the design tree walked
|
|
5
|
+
branch-by-branch with a decision at each node. Implementation should conform to
|
|
6
|
+
this; deviations should re-open the corresponding question, not silently diverge.
|
|
7
|
+
|
|
8
|
+
Glossary for every term below lives in [`../CONTEXT.md`](../CONTEXT.md).
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## The one-line design
|
|
13
|
+
|
|
14
|
+
Add a `/gsd new-milestone <spec>` slash command that spawns
|
|
15
|
+
`gsd headless new-milestone` as a **plugin-owned subprocess**, fires-and-forgets
|
|
16
|
+
an ack, drives lifecycle (blocker/terminal) notifications from the subprocess's
|
|
17
|
+
stream-json stdout, and hands execution off to the **existing, untouched**
|
|
18
|
+
`/gsd auto` path. No `--auto` chaining, no new MCP tool, no TS-side changes.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Decision tree (Q1–Q9)
|
|
23
|
+
|
|
24
|
+
### Q1 — Mechanism & transport → **(A) Slash command → CLI subprocess**
|
|
25
|
+
- New `_cmd_new_milestone` in `GsdCommandRouter`. Shells out to
|
|
26
|
+
`gsd headless new-milestone --context-text <spec>`.
|
|
27
|
+
- **Not** a Hermes skill (B), **not** a new MCP `gsd_create_milestone` tool (C).
|
|
28
|
+
- Rationale: the plugin already has a proven direct-CLI pattern (`progress()`
|
|
29
|
+
calls `gsd read progress --json`); `new-milestone` is even more naturally a
|
|
30
|
+
CLI call (it bootstraps `.gsd/`, has a 10-min timeout, manages its own RPC
|
|
31
|
+
child). An MCP tool would re-implement what the CLI already does, and the
|
|
32
|
+
upstream lease-layer co-run issue (CHANGELOG:509, open) makes advertising
|
|
33
|
+
`gsd_create_milestone` over MCP actively unsafe to every MCP client.
|
|
34
|
+
- A later Hermes skill (B) can wrap the same client method with zero rework.
|
|
35
|
+
|
|
36
|
+
### Q2 — Co-run safety gate → **(A) Local supervisor state only**
|
|
37
|
+
- Reject if `SupervisorContext.state in (RUNNING, BLOCKED)` before spawning.
|
|
38
|
+
- **Not** a server-side cross-binding check (B). Documented limitation: a
|
|
39
|
+
session started by *another* plugin instance / a headless run outside this
|
|
40
|
+
plugin is invisible to the local guard; the CLI's own lease check is the
|
|
41
|
+
backstop.
|
|
42
|
+
- Rationale: the plugin can only authoritatively own its own state; a
|
|
43
|
+
server-side "is anything running?" check is itself racy and adds a new query
|
|
44
|
+
path to design and test.
|
|
45
|
+
|
|
46
|
+
### Q3 — Run model → **(A) Fire-and-track via the existing async-push model**
|
|
47
|
+
- Return "🚀 Milestone creation started (session X)" immediately; push
|
|
48
|
+
blocker/terminal notifications from a background stream-reader.
|
|
49
|
+
- **Not** a synchronous blocking call (rejected after discovering the plugin is
|
|
50
|
+
fundamentally async-push — `_cmd_auto` already works this way; the target
|
|
51
|
+
chat platforms don't hold request/response open for minutes).
|
|
52
|
+
- *(Note: "reuse `SupervisorFsm` unchanged" was initially proposed under this
|
|
53
|
+
label but does NOT work — see A1 below.)*
|
|
54
|
+
|
|
55
|
+
### Q3 follow-up — How to drive the fire-and-track → **(A1) Client-owned subprocess, direct stream→notification**
|
|
56
|
+
- `GsdMcpClient` spawns `gsd headless new-milestone --output-format stream-json`
|
|
57
|
+
on a background thread, reads stdout line-by-line, captures
|
|
58
|
+
`init_result.sessionId`, and on terminal/blocker events calls
|
|
59
|
+
`NotificationService` directly.
|
|
60
|
+
- `SupervisorContext` is **shared state** (written by the stream-reader, read by
|
|
61
|
+
`/gsd status`, `/gsd cancel`, the co-run gate); the `SupervisorFsm` poll loop
|
|
62
|
+
is **not** reused for the milestone path (its `_tick` calls `gsd_status`,
|
|
63
|
+
which can't see our subprocess).
|
|
64
|
+
- Rationale: the stream *is* the lifecycle signal; polling would be a lossy
|
|
65
|
+
proxy for events we already get deterministically. Reuses
|
|
66
|
+
`NotificationService` and `SupervisorContext` unchanged.
|
|
67
|
+
|
|
68
|
+
### Q4 — Cancellation → **(A) Local PID ownership + direct SIGTERM**
|
|
69
|
+
- `GsdMcpClient` holds the `subprocess.Popen` handle; `cancel_milestone()` calls
|
|
70
|
+
`proc.terminate()` directly. `_cmd_cancel` routes to it when a milestone proc
|
|
71
|
+
is active.
|
|
72
|
+
- **Not** a new `.gsd/milestone.lock` pid file for the MCP backstop (B) — that
|
|
73
|
+
crosses the Python/TS boundary and inverts ownership. **Not** "no cancel during
|
|
74
|
+
planning" (C) — a stuck planning run needs an escape hatch.
|
|
75
|
+
- Discovered: MCP `cancelSessionByDir`'s `auto.lock` pid backstop does NOT find
|
|
76
|
+
our planning subprocess (no `auto.lock` exists during planning — it's an
|
|
77
|
+
auto-loop concept). So local ownership is required, not optional.
|
|
78
|
+
- Plus explicit supervisor stop on cancel (immediate confirmation, don't wait
|
|
79
|
+
for next poll tick).
|
|
80
|
+
|
|
81
|
+
### Q5 — Scope: `--auto` (chained execution)? → **No. Planning-only.**
|
|
82
|
+
- The plugin creates the milestone and hands off; execution is `/gsd auto`'s job.
|
|
83
|
+
- `/gsd auto` already has a robust path (MCP `gsd_execute` → self-healing poll
|
|
84
|
+
supervisor). Chaining `--auto` would duplicate that with a stateful
|
|
85
|
+
stream-reader over hours (fragile) instead of the proven poll loop.
|
|
86
|
+
- **Boundary clarification:** this plugin (`gsd-pi/integrations/hermes/`) is the
|
|
87
|
+
chat gateway. One-shot `new-milestone --auto` is gsd-core's terminal surface,
|
|
88
|
+
not the gateway's.
|
|
89
|
+
- `/gsd new-milestone --auto` is **rejected at the command level** with a message
|
|
90
|
+
pointing to the two-step flow.
|
|
91
|
+
|
|
92
|
+
### Q6 — Input shape → **Bare positional, with `--file` escape**
|
|
93
|
+
- `/gsd new-milestone <spec text>` → `--context-text` (matches `/gsd reply`).
|
|
94
|
+
- `/gsd new-milestone --file <path>` → `--context <file>` (explicit opt-in).
|
|
95
|
+
- **Not** auto-detect path-vs-text (heuristic misroutes on collision — rule #5).
|
|
96
|
+
- **Not** `--description "..."` flag form (issue's example used it, but the bare
|
|
97
|
+
positional matches the sibling command users already know).
|
|
98
|
+
|
|
99
|
+
### Q7 — Output → **(A) Bounded summary via `gsd_query` at completion, with (B) as graceful fallback**
|
|
100
|
+
- Immediate ack: "🚀 Milestone creation started for `<project>`."
|
|
101
|
+
- Completion push: bounded summary — milestone id, slice count, task count,
|
|
102
|
+
one-line next step ("Run `/gsd auto` to start."). Built from
|
|
103
|
+
`gsd_query {projectDir, query: "milestones"}` after exit-0.
|
|
104
|
+
- Fallback: if the query comes back empty/missing after exit-0, degrade to
|
|
105
|
+
"✅ Milestone created. Run `/gsd status` for details."
|
|
106
|
+
- **Trust exit-0; no defensive re-querying.** If exit-0 lies about flush state,
|
|
107
|
+
that's a gsd-core bug to fix at the source, not a leak for the plugin to patch
|
|
108
|
+
with a racy retry loop.
|
|
109
|
+
- Error/blocked cases route through existing `notify_terminal`/`notify_blocker`
|
|
110
|
+
unchanged.
|
|
111
|
+
|
|
112
|
+
### Q8 — Failure modes
|
|
113
|
+
| # | Mode | Disposition |
|
|
114
|
+
|---|---|---|
|
|
115
|
+
| 1 | Active auto session (co-run) | Reject before spawn; fail closed |
|
|
116
|
+
| 2 | No project bound | Reuse `BindingError` path (zero new code) |
|
|
117
|
+
| 3 | Empty spec | Usage message (matches `_cmd_reply`/`_cmd_bind`) |
|
|
118
|
+
| 4 | Spec too long for `--context-text` | Surface `E2BIG`/`OSError`; point to `--file` |
|
|
119
|
+
| 5 | Planning errors (non-zero exit) | `notify_terminal(status, error)` via existing path |
|
|
120
|
+
| 6 | Planning hits a blocker | `notify_blocker`; reply via **client-owned stdin** (see below) |
|
|
121
|
+
| 7 | `--file` missing/unreadable | Validate before spawn |
|
|
122
|
+
| 8 | Stream-reader thread/pipe dies | Best-effort: set FAILED, `notify_terminal("failed", "stream lost — run /gsd cancel")`. **No watchdog.** Cancel is the escape hatch. |
|
|
123
|
+
| 9 | Plugin restart mid-planning | **Documented limitation.** Orphaned subprocess survives; invisible after restart (same as existing supervisor's restart-blindness). |
|
|
124
|
+
| 10 | User passes `--auto` | Reject at command level (see Q5) |
|
|
125
|
+
|
|
126
|
+
**Blocker replies (#6) — client-owned, not MCP-routed.** `gsd_resolve_blocker`
|
|
127
|
+
calls `sessionManager.resolveBlocker` which looks up the MCP server's *own*
|
|
128
|
+
sessions map — our subprocess isn't there, so it throws "Session not found".
|
|
129
|
+
Because we own the subprocess's stdin (A1), `/gsd reply` for a planning blocker
|
|
130
|
+
writes the response to *our* subprocess's stdin via a new
|
|
131
|
+
`respond_to_milestone_blocker(response)` method. `_cmd_reply` gets one branch:
|
|
132
|
+
if a milestone subprocess with a pending blocker is active, route locally; else
|
|
133
|
+
fall through to the existing MCP `resolve_blocker` path.
|
|
134
|
+
|
|
135
|
+
### Q9 — Cross-effects on existing commands
|
|
136
|
+
- **`_cmd_cancel`** — MUST change: add milestone-subprocess branch routing to
|
|
137
|
+
local `cancel_milestone()` (SIGTERM) when one is active. *Required.*
|
|
138
|
+
- **`_cmd_auto`** — MUST change: add symmetric co-run guard (reject if
|
|
139
|
+
`SupervisorContext` shows an active milestone run, point to `/gsd cancel`).
|
|
140
|
+
*Required* — without it, `/gsd auto` during milestone planning silently
|
|
141
|
+
re-exposes the unsafe co-run (the MCP server's "Session already active"
|
|
142
|
+
rejection does NOT fire for our planning subprocess, since it's not registered
|
|
143
|
+
and writes no `auto.lock`). Rule #12 (fail loud) over rule #3 (surgical) here.
|
|
144
|
+
- **`/gsd status`** — leave alone. Reads disk honestly; the Q3-accepted planning
|
|
145
|
+
progress gap means it may show stale/partial state early on. The completion
|
|
146
|
+
notification is the real "done" signal.
|
|
147
|
+
- **`/gsd reply`** — additive branch only (Q8 #6); core MCP path untouched.
|
|
148
|
+
- **`SupervisorFsm`** — untouched (A1 uses a separate stream-reader driver).
|
|
149
|
+
- **TS MCP server** — untouched (no new tools).
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## Implementation surface (additive unless noted)
|
|
154
|
+
|
|
155
|
+
**`gsd_client.py`** — new methods + state:
|
|
156
|
+
- `self._milestone_proc: subprocess.Popen | None`
|
|
157
|
+
- `create_milestone(project_dir, *, context_text=None, context_file=None) -> str` —
|
|
158
|
+
spawns `gsd headless new-milestone --output-format stream-json`, starts the
|
|
159
|
+
stream-reader thread, returns `sessionId` (captured from `init_result`). Throws
|
|
160
|
+
on co-run guard / missing spec.
|
|
161
|
+
- `_milestone_stream_loop(proc)` — reads stdout JSONL; on `init_result` captures
|
|
162
|
+
`sessionId`; on blocker event → `notify_blocker` + sets
|
|
163
|
+
`SupervisorContext.pending_blocker_id`; on terminal/exit → `notify_terminal` +
|
|
164
|
+
sets `SupervisorContext.state`.
|
|
165
|
+
- `cancel_milestone()` — `proc.terminate()` + cleanup.
|
|
166
|
+
- `respond_to_milestone_blocker(response)` — writes UI-response to proc stdin.
|
|
167
|
+
- `milestone_active()` predicate for the co-run gate.
|
|
168
|
+
|
|
169
|
+
**`commands.py`** — new handler + two modifications:
|
|
170
|
+
- `_cmd_new_milestone(rest)` — the new command (guard, parse, spawn, ack).
|
|
171
|
+
- `_cmd_cancel` (modify) — milestone-subprocess branch.
|
|
172
|
+
- `_cmd_auto` (modify) — symmetric co-run guard.
|
|
173
|
+
- `_cmd_reply` (modify) — milestone-blocker branch (additive).
|
|
174
|
+
- `_cmd_help` (modify) — list `new-milestone`.
|
|
175
|
+
|
|
176
|
+
**`__init__.py`** — wire the new client methods / supervisor notifications if
|
|
177
|
+
the constructor surface changes (likely no change — `GsdMcpClient` already has
|
|
178
|
+
`config`, and `NotificationService` is already injected).
|
|
179
|
+
|
|
180
|
+
**Tests** (new, matching existing `tests/test_*.py` patterns):
|
|
181
|
+
- `test_new_milestone_command.py` — guard (co-run both ways), input parsing
|
|
182
|
+
(bare text, `--file`, empty, `--auto` rejected), spawn + ack.
|
|
183
|
+
- extend `test_gsd_client_cache.py` or new `test_milestone_client.py` —
|
|
184
|
+
stream-reader captures sessionId from `init_result`; blocker event →
|
|
185
|
+
`notify_blocker`; terminal → `notify_terminal`; cancel SIGTERMs the proc;
|
|
186
|
+
blocker reply writes stdin.
|
|
187
|
+
- extend cancel/reply tests for the new branches.
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## Explicitly OUT of scope (v1)
|
|
192
|
+
|
|
193
|
+
- `--auto` chaining (execution stays with `/gsd auto`).
|
|
194
|
+
- Cross-instance / server-side co-run coordination (Q2 limitation).
|
|
195
|
+
- Plugin-restart recovery of an orphaned planning subprocess (Q8 #9).
|
|
196
|
+
- A Hermes skill (issue's Option B) — additive follow-on, wraps the same client.
|
|
197
|
+
- Any TS-side / MCP-server change.
|
|
@@ -79,6 +79,7 @@ gsd:
|
|
|
79
79
|
default_project: ~/code/myapp # optional fallback
|
|
80
80
|
poll_interval_seconds: 12
|
|
81
81
|
cache_ttl_seconds: 45
|
|
82
|
+
mcp_read_timeout_seconds: 60 # per-response MCP read timeout
|
|
82
83
|
notification_level: normal # quiet | normal | verbose
|
|
83
84
|
bindings:
|
|
84
85
|
slack:
|
|
@@ -89,6 +90,8 @@ gsd:
|
|
|
89
90
|
|
|
90
91
|
**Binding tiers (first match wins):** cron explicit → slash argument → `/gsd bind` session binding → channel map → `default_project` → cwd heuristic.
|
|
91
92
|
|
|
93
|
+
**MCP read timeout:** `gsd.mcp_read_timeout_seconds` defaults to `60` seconds and bounds each response read from the `gsd-mcp-server` sidecar. Increase it for very slow projects, machines, or network-backed filesystems where Hermes reports MCP response timeouts but the same GSD command eventually completes locally; lower it only when you prefer faster failure detection. When the timeout fires, the plugin terminates the stuck sidecar and the next project-scoped call starts a fresh server in the bound project directory.
|
|
94
|
+
|
|
92
95
|
**Credentials:** With `credential_source: gsd`, ensure GSD’s normal provider keys are configured (`~/.gsd/` or env). Hermes passthrough (`credential_source: hermes`) is 6b+.
|
|
93
96
|
|
|
94
97
|
### Verify plugin loaded
|
|
@@ -224,6 +227,7 @@ Verify on **one** platform (Slack or Telegram):
|
|
|
224
227
|
| `GsdVersionError` on start | `gsd` outside semver range | Set `gsd_version_min` in `gsd.yaml` or upgrade gsd |
|
|
225
228
|
| MCP spawn fails | Wrong `mcp_server_path` | `which gsd-mcp-server`; set absolute path |
|
|
226
229
|
| `/gsd status` hangs | MCP sidecar stuck | Check `GSD_CLI_PATH`; restart gateway; kill orphaned `gsd-mcp-server` |
|
|
230
|
+
| MCP response times out after 60s | Slow project or filesystem, or wedged sidecar | Confirm the command locally; tune `mcp_read_timeout_seconds` if it completes after 60s |
|
|
227
231
|
| No push notifications | Invalid session key / target | Key must match `agent:main:{platform}:{chat_type}:{chat_id}`; check Hermes gateway logs |
|
|
228
232
|
| Notifications in CLI but not Slack | `DeliveryTarget.from_session_key` returned None | Run checklist in **gateway** mode, not CLI-only |
|
|
229
233
|
| Cancel ignored | Stale session id | `/gsd cancel` uses `gsd_cancel_by_project` fallback when no session id |
|
|
@@ -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")),
|