gsd-pi 2.64.0-dev.9c14bd0 → 2.64.0-dev.b3ee078
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/headless.js +3 -1
- package/dist/resources/extensions/bg-shell/bg-shell-lifecycle.js +22 -7
- package/dist/resources/extensions/bg-shell/process-manager.js +6 -1
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +24 -13
- package/dist/resources/extensions/gsd/bootstrap/notify-interceptor.js +28 -0
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +8 -0
- package/dist/resources/extensions/gsd/bootstrap/register-shortcuts.js +16 -0
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +20 -0
- package/dist/resources/extensions/gsd/commands/catalog.js +7 -1
- package/dist/resources/extensions/gsd/commands/handlers/core.js +1 -0
- package/dist/resources/extensions/gsd/commands/handlers/notifications-handler.js +104 -0
- package/dist/resources/extensions/gsd/commands/handlers/ops.js +5 -0
- package/dist/resources/extensions/gsd/notification-overlay.js +223 -0
- package/dist/resources/extensions/gsd/notification-store.js +273 -0
- package/dist/resources/extensions/gsd/notification-widget.js +56 -0
- package/dist/resources/extensions/gsd/workflow-logger.js +8 -0
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +20 -19
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/routes-manifest.json +6 -0
- package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
- 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/notifications/route.js +3 -0
- package/dist/web/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -0
- package/dist/web/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -0
- 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 +20 -19
- package/dist/web/standalone/.next/server/functions-config-manifest.json +1 -0
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +2 -2
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/static/chunks/app/_global-error/page-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/boot/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/input/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/resize/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/stream/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/browse-directories/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/captures/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/cleanup/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/dev-mode/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/doctor/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/experimental/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/export-data/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/files/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/forensics/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/git/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/history/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/hooks/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/inspect/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/knowledge/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/live-state/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/notifications/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/onboarding/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/preferences/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/projects/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/recovery/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/remote-questions/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/session/browser/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/session/command/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/session/events/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/session/manage/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/settings-data/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/shutdown/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/skill-health/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/steer/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/switch-root/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/terminal/input/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/terminal/resize/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/terminal/sessions/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/terminal/stream/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/terminal/upload/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/undo/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/update/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/api/visualizer/route-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/app-error-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/forbidden-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/not-found-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/unauthorized-8805a20e15762c3c.js +1 -0
- package/dist/web/standalone/.next/static/l7tiSF0KtXOwxxYn0ZAyF/_buildManifest.js +1 -0
- package/package.json +1 -1
- package/packages/pi-agent-core/dist/agent-loop.js +26 -9
- package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/packages/pi-agent-core/src/agent-loop.test.ts +100 -4
- package/packages/pi-agent-core/src/agent-loop.ts +43 -12
- package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js +38 -0
- package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +11 -0
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.js +24 -0
- package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/resource-loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/resource-loader.js +4 -1
- package/packages/pi-coding-agent/dist/core/resource-loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +8 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +6 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +36 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/src/core/agent-session-tool-refresh.test.ts +64 -0
- package/packages/pi-coding-agent/src/core/agent-session.ts +10 -0
- package/packages/pi-coding-agent/src/core/resource-loader-cache-reset.test.ts +42 -0
- package/packages/pi-coding-agent/src/core/resource-loader.ts +5 -1
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +9 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +33 -0
- package/packages/pi-tui/dist/__tests__/overlay-layout.test.d.ts +2 -0
- package/packages/pi-tui/dist/__tests__/overlay-layout.test.d.ts.map +1 -0
- package/packages/pi-tui/dist/__tests__/overlay-layout.test.js +66 -0
- package/packages/pi-tui/dist/__tests__/overlay-layout.test.js.map +1 -0
- package/packages/pi-tui/dist/components/loader.d.ts +4 -2
- package/packages/pi-tui/dist/components/loader.d.ts.map +1 -1
- package/packages/pi-tui/dist/components/loader.js +27 -9
- package/packages/pi-tui/dist/components/loader.js.map +1 -1
- package/packages/pi-tui/dist/components/text.d.ts.map +1 -1
- package/packages/pi-tui/dist/components/text.js +2 -0
- package/packages/pi-tui/dist/components/text.js.map +1 -1
- package/packages/pi-tui/dist/overlay-layout.d.ts.map +1 -1
- package/packages/pi-tui/dist/overlay-layout.js +12 -1
- package/packages/pi-tui/dist/overlay-layout.js.map +1 -1
- package/packages/pi-tui/dist/tui.d.ts +4 -0
- package/packages/pi-tui/dist/tui.d.ts.map +1 -1
- package/packages/pi-tui/dist/tui.js +35 -0
- package/packages/pi-tui/dist/tui.js.map +1 -1
- package/packages/pi-tui/src/__tests__/overlay-layout.test.ts +82 -0
- package/packages/pi-tui/src/components/loader.ts +27 -10
- package/packages/pi-tui/src/components/text.ts +1 -0
- package/packages/pi-tui/src/overlay-layout.ts +14 -1
- package/packages/pi-tui/src/tui.ts +34 -0
- package/src/resources/extensions/bg-shell/bg-shell-lifecycle.ts +19 -7
- package/src/resources/extensions/bg-shell/process-manager.ts +8 -2
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +25 -13
- package/src/resources/extensions/gsd/bootstrap/notify-interceptor.ts +34 -0
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +8 -0
- package/src/resources/extensions/gsd/bootstrap/register-shortcuts.ts +20 -0
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +28 -0
- package/src/resources/extensions/gsd/commands/catalog.ts +7 -1
- package/src/resources/extensions/gsd/commands/handlers/core.ts +1 -0
- package/src/resources/extensions/gsd/commands/handlers/notifications-handler.ts +140 -0
- package/src/resources/extensions/gsd/commands/handlers/ops.ts +5 -0
- package/src/resources/extensions/gsd/notification-overlay.ts +266 -0
- package/src/resources/extensions/gsd/notification-store.ts +293 -0
- package/src/resources/extensions/gsd/notification-widget.ts +68 -0
- package/src/resources/extensions/gsd/tests/complete-slice-string-coercion.test.ts +36 -0
- package/src/resources/extensions/gsd/tests/discuss-tool-scope-leak.test.ts +76 -0
- package/src/resources/extensions/gsd/tests/notification-store.test.ts +282 -0
- package/src/resources/extensions/gsd/tests/unstructured-continue-context-injection.test.ts +163 -0
- package/src/resources/extensions/gsd/workflow-logger.ts +13 -0
- package/dist/web/standalone/.next/static/SoxM61WC_ia7R2gk4VMpJ/_buildManifest.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/_global-error/page-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/boot/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/input/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/resize/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/stream/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/browse-directories/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/captures/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/cleanup/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/dev-mode/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/doctor/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/experimental/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/export-data/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/files/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/forensics/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/git/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/history/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/hooks/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/inspect/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/knowledge/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/live-state/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/onboarding/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/preferences/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/projects/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/recovery/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/remote-questions/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/session/browser/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/session/command/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/session/events/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/session/manage/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/settings-data/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/shutdown/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/skill-health/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/steer/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/switch-root/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/terminal/input/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/terminal/resize/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/terminal/sessions/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/terminal/stream/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/terminal/upload/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/undo/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/update/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/api/visualizer/route-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/app-error-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/forbidden-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/not-found-c4cc189e7b117ea2.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/unauthorized-c4cc189e7b117ea2.js +0 -1
- /package/dist/web/standalone/.next/static/{SoxM61WC_ia7R2gk4VMpJ → l7tiSF0KtXOwxxYn0ZAyF}/_ssgManifest.js +0 -0
package/dist/headless.js
CHANGED
|
@@ -184,7 +184,9 @@ async function runHeadlessOnce(options, restartCount) {
|
|
|
184
184
|
// per-unit timeout via auto-supervisor. Disable the overall timeout unless the
|
|
185
185
|
// user explicitly set --timeout.
|
|
186
186
|
const isAutoMode = options.command === 'auto';
|
|
187
|
-
|
|
187
|
+
// discuss and plan are multi-turn: they involve multiple question rounds,
|
|
188
|
+
// codebase scanning, and artifact writing before the workflow completes (#3547).
|
|
189
|
+
const isMultiTurnCommand = options.command === 'auto' || options.command === 'next' || options.command === 'discuss' || options.command === 'plan';
|
|
188
190
|
if (isAutoMode && options.timeout === 300_000) {
|
|
189
191
|
options.timeout = 0;
|
|
190
192
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* context injection, process discovery, footer widget, and periodic maintenance.
|
|
4
4
|
*/
|
|
5
5
|
import { truncateToWidth, visibleWidth, } from "@gsd/pi-tui";
|
|
6
|
-
import { processes, pendingAlerts, cleanupAll, cleanupSessionProcesses, persistManifest, loadManifest, pruneDeadProcesses, } from "./process-manager.js";
|
|
6
|
+
import { processes, pendingAlerts, pushAlert, cleanupAll, cleanupSessionProcesses, persistManifest, loadManifest, pruneDeadProcesses, } from "./process-manager.js";
|
|
7
7
|
import { formatUptime, getBgShellLiveCwd, resolveBgShellPersistenceCwd } from "./utilities.js";
|
|
8
8
|
import { formatTokenCount } from "../shared/format-utils.js";
|
|
9
9
|
export function registerBgShellLifecycle(pi, state) {
|
|
@@ -15,17 +15,32 @@ export function registerBgShellLifecycle(pi, state) {
|
|
|
15
15
|
state.latestCtx = { ...state.latestCtx, cwd: syncedCwd };
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
|
-
// Clean up on session shutdown
|
|
19
|
-
pi.on("session_shutdown", async () => {
|
|
20
|
-
cleanupAll();
|
|
21
|
-
});
|
|
22
18
|
// Register signal handlers to clean up bg processes on unexpected exit (fixes #428)
|
|
23
19
|
const signalCleanup = () => {
|
|
24
20
|
cleanupAll();
|
|
21
|
+
// Also kill bash-tool spawned children that bg-shell doesn't track
|
|
22
|
+
try {
|
|
23
|
+
const { listDescendants } = require("@gsd/native");
|
|
24
|
+
const descendants = listDescendants(process.pid);
|
|
25
|
+
for (const childPid of descendants) {
|
|
26
|
+
try {
|
|
27
|
+
process.kill(childPid, "SIGKILL");
|
|
28
|
+
}
|
|
29
|
+
catch { }
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
catch { }
|
|
25
33
|
};
|
|
26
34
|
process.on("SIGTERM", signalCleanup);
|
|
27
35
|
process.on("SIGINT", signalCleanup);
|
|
28
36
|
process.on("beforeExit", signalCleanup);
|
|
37
|
+
// Clean up on session shutdown — remove signal handlers to prevent accumulation
|
|
38
|
+
pi.on("session_shutdown", async () => {
|
|
39
|
+
process.off("SIGTERM", signalCleanup);
|
|
40
|
+
process.off("SIGINT", signalCleanup);
|
|
41
|
+
process.off("beforeExit", signalCleanup);
|
|
42
|
+
cleanupAll();
|
|
43
|
+
});
|
|
29
44
|
// ── Compaction Awareness: Survive Context Resets ───────────────
|
|
30
45
|
/** Build a compact state summary of all alive processes for context re-injection */
|
|
31
46
|
function buildProcessStateAlert(reason) {
|
|
@@ -39,7 +54,7 @@ export function registerBgShellLifecycle(pi, state) {
|
|
|
39
54
|
const groupInfo = p.group ? ` [${p.group}]` : "";
|
|
40
55
|
return ` - id:${p.id} "${p.label}" [${p.processType}] status:${p.status} uptime:${formatUptime(Date.now() - p.startedAt)}${portInfo}${urlInfo}${errInfo}${groupInfo}`;
|
|
41
56
|
}).join("\n");
|
|
42
|
-
|
|
57
|
+
pushAlert(null, `${reason} ${alive.length} background process(es) are still running:\n${processSummaries}\nUse bg_shell digest/output/kill with these IDs.`);
|
|
43
58
|
}
|
|
44
59
|
// After compaction, the LLM loses all memory of running processes.
|
|
45
60
|
// Queue a detailed alert so the next before_agent_start injects full state.
|
|
@@ -108,7 +123,7 @@ export function registerBgShellLifecycle(pi, state) {
|
|
|
108
123
|
}
|
|
109
124
|
if (surviving.length > 0) {
|
|
110
125
|
const summary = surviving.map(s => ` - ${s.id}: ${s.label} (pid ${s.pid}, type: ${s.processType}${s.group ? `, group: ${s.group}` : ""})`).join("\n");
|
|
111
|
-
|
|
126
|
+
pushAlert(null, `${surviving.length} background process(es) from previous session still running:\n${summary}\n Note: These processes are outside bg_shell's control. Kill them manually if needed.`);
|
|
112
127
|
}
|
|
113
128
|
}
|
|
114
129
|
});
|
|
@@ -16,6 +16,7 @@ import { startPortProbing, transitionToReady } from "./readiness-detector.js";
|
|
|
16
16
|
export const processes = new Map();
|
|
17
17
|
/** Pending alerts to inject into the next agent context */
|
|
18
18
|
export let pendingAlerts = [];
|
|
19
|
+
const MAX_PENDING_ALERTS = 50;
|
|
19
20
|
/** Replace the pendingAlerts array (used by the extension entry point) */
|
|
20
21
|
export function setPendingAlerts(alerts) {
|
|
21
22
|
pendingAlerts = alerts;
|
|
@@ -41,7 +42,11 @@ export function addEvent(bg, event) {
|
|
|
41
42
|
}
|
|
42
43
|
}
|
|
43
44
|
export function pushAlert(bg, message) {
|
|
44
|
-
|
|
45
|
+
const prefix = bg ? `[bg:${bg.id} ${bg.label}] ` : "";
|
|
46
|
+
pendingAlerts.push(`${prefix}${message}`);
|
|
47
|
+
if (pendingAlerts.length > MAX_PENDING_ALERTS) {
|
|
48
|
+
pendingAlerts.splice(0, pendingAlerts.length - MAX_PENDING_ALERTS);
|
|
49
|
+
}
|
|
45
50
|
}
|
|
46
51
|
export function getInfo(p) {
|
|
47
52
|
return {
|
|
@@ -768,31 +768,42 @@ export function registerDbTools(pi) {
|
|
|
768
768
|
return m ? [m[1].trim(), m[2].trim()] : [s.trim(), ""];
|
|
769
769
|
};
|
|
770
770
|
const coerced = { ...params };
|
|
771
|
-
|
|
771
|
+
// Coerce simple string-array fields: LLMs sometimes pass a plain string
|
|
772
|
+
// instead of a single-element array (#3585).
|
|
773
|
+
const wrapArray = (v) => v == null ? [] : Array.isArray(v) ? v : [v];
|
|
774
|
+
coerced.provides = wrapArray(params.provides);
|
|
775
|
+
coerced.keyFiles = wrapArray(params.keyFiles);
|
|
776
|
+
coerced.keyDecisions = wrapArray(params.keyDecisions);
|
|
777
|
+
coerced.patternsEstablished = wrapArray(params.patternsEstablished);
|
|
778
|
+
coerced.observabilitySurfaces = wrapArray(params.observabilitySurfaces);
|
|
779
|
+
coerced.requirementsSurfaced = wrapArray(params.requirementsSurfaced);
|
|
780
|
+
coerced.drillDownPaths = wrapArray(params.drillDownPaths);
|
|
781
|
+
coerced.affects = wrapArray(params.affects);
|
|
782
|
+
coerced.filesModified = wrapArray(params.filesModified).map((f) => {
|
|
772
783
|
if (typeof f !== "string")
|
|
773
784
|
return f;
|
|
774
785
|
const [path, description] = splitPair(f);
|
|
775
786
|
return { path, description };
|
|
776
787
|
});
|
|
777
|
-
coerced.requires = (params.requires
|
|
788
|
+
coerced.requires = wrapArray(params.requires).map((r) => {
|
|
778
789
|
if (typeof r !== "string")
|
|
779
790
|
return r;
|
|
780
791
|
const [slice, provides] = splitPair(r);
|
|
781
792
|
return { slice, provides };
|
|
782
793
|
});
|
|
783
|
-
coerced.requirementsAdvanced = (params.requirementsAdvanced
|
|
794
|
+
coerced.requirementsAdvanced = wrapArray(params.requirementsAdvanced).map((r) => {
|
|
784
795
|
if (typeof r !== "string")
|
|
785
796
|
return r;
|
|
786
797
|
const [id, how] = splitPair(r);
|
|
787
798
|
return { id, how };
|
|
788
799
|
});
|
|
789
|
-
coerced.requirementsValidated = (params.requirementsValidated
|
|
800
|
+
coerced.requirementsValidated = wrapArray(params.requirementsValidated).map((r) => {
|
|
790
801
|
if (typeof r !== "string")
|
|
791
802
|
return r;
|
|
792
803
|
const [id, proof] = splitPair(r);
|
|
793
804
|
return { id, proof };
|
|
794
805
|
});
|
|
795
|
-
coerced.requirementsInvalidated = (params.requirementsInvalidated
|
|
806
|
+
coerced.requirementsInvalidated = wrapArray(params.requirementsInvalidated).map((r) => {
|
|
796
807
|
if (typeof r !== "string")
|
|
797
808
|
return r;
|
|
798
809
|
const [id, what] = splitPair(r);
|
|
@@ -851,14 +862,14 @@ export function registerDbTools(pi) {
|
|
|
851
862
|
deviations: Type.Optional(Type.String({ description: "Deviations from the slice plan, or 'None.'" })),
|
|
852
863
|
knownLimitations: Type.Optional(Type.String({ description: "Known limitations or gaps, or 'None.'" })),
|
|
853
864
|
followUps: Type.Optional(Type.String({ description: "Follow-up work discovered during execution, or 'None.'" })),
|
|
854
|
-
keyFiles: Type.Optional(Type.Array(Type.String(), { description: "Key files created or modified" })),
|
|
855
|
-
keyDecisions: Type.Optional(Type.Array(Type.String(), { description: "Key decisions made during this slice" })),
|
|
856
|
-
patternsEstablished: Type.Optional(Type.Array(Type.String(), { description: "Patterns established by this slice" })),
|
|
857
|
-
observabilitySurfaces: Type.Optional(Type.Array(Type.String(), { description: "Observability surfaces added" })),
|
|
858
|
-
provides: Type.Optional(Type.Array(Type.String(), { description: "What this slice provides to downstream slices" })),
|
|
859
|
-
requirementsSurfaced: Type.Optional(Type.Array(Type.String(), { description: "New requirements surfaced" })),
|
|
860
|
-
drillDownPaths: Type.Optional(Type.Array(Type.String(), { description: "Paths to task summaries for drill-down" })),
|
|
861
|
-
affects: Type.Optional(Type.Array(Type.String(), { description: "Downstream slices affected" })),
|
|
865
|
+
keyFiles: Type.Optional(Type.Union([Type.Array(Type.String()), Type.String()], { description: "Key files created or modified" })),
|
|
866
|
+
keyDecisions: Type.Optional(Type.Union([Type.Array(Type.String()), Type.String()], { description: "Key decisions made during this slice" })),
|
|
867
|
+
patternsEstablished: Type.Optional(Type.Union([Type.Array(Type.String()), Type.String()], { description: "Patterns established by this slice" })),
|
|
868
|
+
observabilitySurfaces: Type.Optional(Type.Union([Type.Array(Type.String()), Type.String()], { description: "Observability surfaces added" })),
|
|
869
|
+
provides: Type.Optional(Type.Union([Type.Array(Type.String()), Type.String()], { description: "What this slice provides to downstream slices" })),
|
|
870
|
+
requirementsSurfaced: Type.Optional(Type.Union([Type.Array(Type.String()), Type.String()], { description: "New requirements surfaced" })),
|
|
871
|
+
drillDownPaths: Type.Optional(Type.Union([Type.Array(Type.String()), Type.String()], { description: "Paths to task summaries for drill-down" })),
|
|
872
|
+
affects: Type.Optional(Type.Union([Type.Array(Type.String()), Type.String()], { description: "Downstream slices affected" })),
|
|
862
873
|
requirementsAdvanced: Type.Optional(Type.Array(Type.Union([
|
|
863
874
|
Type.Object({
|
|
864
875
|
id: Type.String({ description: "Requirement ID" }),
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// GSD Extension — Notify Interceptor
|
|
2
|
+
// Wraps ctx.ui.notify() in-place to persist every notification through the
|
|
3
|
+
// notification store. Uses a WeakSet to prevent double-wrapping and handle
|
|
4
|
+
// UI context replacement on /reload gracefully.
|
|
5
|
+
import { appendNotification } from "../notification-store.js";
|
|
6
|
+
// Track which ui context objects have been wrapped to prevent double-install.
|
|
7
|
+
// WeakSet allows GC to collect replaced uiContext instances after /reload.
|
|
8
|
+
const _wrappedContexts = new WeakSet();
|
|
9
|
+
/**
|
|
10
|
+
* Install the notify interceptor on a context's UI object.
|
|
11
|
+
* Mutates ctx.ui.notify in place — the original is called after persistence.
|
|
12
|
+
* Safe to call multiple times; no-ops if already installed on the same ui object.
|
|
13
|
+
*/
|
|
14
|
+
export function installNotifyInterceptor(ctx) {
|
|
15
|
+
if (_wrappedContexts.has(ctx.ui))
|
|
16
|
+
return;
|
|
17
|
+
const originalNotify = ctx.ui.notify.bind(ctx.ui);
|
|
18
|
+
ctx.ui.notify = (message, type) => {
|
|
19
|
+
try {
|
|
20
|
+
appendNotification(message, (type ?? "info"), "notify");
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
// Non-fatal — never let persistence break the UI
|
|
24
|
+
}
|
|
25
|
+
originalNotify(message, type);
|
|
26
|
+
};
|
|
27
|
+
_wrappedContexts.add(ctx.ui);
|
|
28
|
+
}
|
|
@@ -18,6 +18,9 @@ import { resetAskUserQuestionsCache } from "../../ask-user-questions.js";
|
|
|
18
18
|
import { recordToolCall as safetyRecordToolCall, recordToolResult as safetyRecordToolResult } from "../safety/evidence-collector.js";
|
|
19
19
|
import { classifyCommand } from "../safety/destructive-guard.js";
|
|
20
20
|
import { logWarning as safetyLogWarning } from "../workflow-logger.js";
|
|
21
|
+
import { installNotifyInterceptor } from "./notify-interceptor.js";
|
|
22
|
+
import { initNotificationStore } from "../notification-store.js";
|
|
23
|
+
import { initNotificationWidget } from "../notification-widget.js";
|
|
21
24
|
// Skip the welcome screen on the very first session_start — cli.ts already
|
|
22
25
|
// printed it before the TUI launched. Only re-print on /clear (subsequent sessions).
|
|
23
26
|
let isFirstSession = true;
|
|
@@ -27,6 +30,9 @@ async function syncServiceTierStatus(ctx) {
|
|
|
27
30
|
}
|
|
28
31
|
export function registerHooks(pi) {
|
|
29
32
|
pi.on("session_start", async (_event, ctx) => {
|
|
33
|
+
initNotificationStore(process.cwd());
|
|
34
|
+
installNotifyInterceptor(ctx);
|
|
35
|
+
initNotificationWidget(ctx);
|
|
30
36
|
resetWriteGateState();
|
|
31
37
|
resetToolCallLoopGuard();
|
|
32
38
|
resetAskUserQuestionsCache();
|
|
@@ -63,6 +69,8 @@ export function registerHooks(pi) {
|
|
|
63
69
|
loadToolApiKeys();
|
|
64
70
|
});
|
|
65
71
|
pi.on("session_switch", async (_event, ctx) => {
|
|
72
|
+
initNotificationStore(process.cwd());
|
|
73
|
+
installNotifyInterceptor(ctx);
|
|
66
74
|
resetWriteGateState();
|
|
67
75
|
resetToolCallLoopGuard();
|
|
68
76
|
resetAskUserQuestionsCache();
|
|
@@ -2,6 +2,7 @@ import { existsSync } from "node:fs";
|
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { Key } from "@gsd/pi-tui";
|
|
4
4
|
import { GSDDashboardOverlay } from "../dashboard-overlay.js";
|
|
5
|
+
import { GSDNotificationOverlay } from "../notification-overlay.js";
|
|
5
6
|
import { ParallelMonitorOverlay } from "../parallel-monitor-overlay.js";
|
|
6
7
|
import { shortcutDesc } from "../../shared/mod.js";
|
|
7
8
|
export function registerShortcuts(pi) {
|
|
@@ -23,6 +24,21 @@ export function registerShortcuts(pi) {
|
|
|
23
24
|
});
|
|
24
25
|
},
|
|
25
26
|
});
|
|
27
|
+
pi.registerShortcut(Key.ctrlAlt("n"), {
|
|
28
|
+
description: shortcutDesc("Open notification history", "/gsd notifications"),
|
|
29
|
+
handler: async (ctx) => {
|
|
30
|
+
await ctx.ui.custom((tui, theme, _kb, done) => new GSDNotificationOverlay(tui, theme, () => done()), {
|
|
31
|
+
overlay: true,
|
|
32
|
+
overlayOptions: {
|
|
33
|
+
width: "80%",
|
|
34
|
+
minWidth: 60,
|
|
35
|
+
maxHeight: "88%",
|
|
36
|
+
anchor: "center",
|
|
37
|
+
backdrop: true,
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
},
|
|
41
|
+
});
|
|
26
42
|
pi.registerShortcut(Key.ctrlAlt("p"), {
|
|
27
43
|
description: shortcutDesc("Open parallel worker monitor", "/gsd parallel watch"),
|
|
28
44
|
handler: async (ctx) => {
|
|
@@ -229,6 +229,12 @@ function buildWorktreeContextBlock() {
|
|
|
229
229
|
}
|
|
230
230
|
return "";
|
|
231
231
|
}
|
|
232
|
+
/**
|
|
233
|
+
* Low-entropy resume intent patterns — short phrases a user types to
|
|
234
|
+
* continue work after a pause, rate limit, or context reset (#3615).
|
|
235
|
+
* Tested against the trimmed, lowercased prompt with trailing punctuation stripped.
|
|
236
|
+
*/
|
|
237
|
+
const RESUME_INTENT_PATTERNS = /^(continue|resume|ok|go|go ahead|proceed|keep going|carry on|next|yes|yeah|yep|sure|do it|let's go|pick up where you left off)$/;
|
|
232
238
|
async function buildGuidedExecuteContextInjection(prompt, basePath) {
|
|
233
239
|
const executeMatch = prompt.match(/Execute the next task:\s+(T\d+)\s+\("([^"]+)"\)\s+in slice\s+(S\d+)\s+of milestone\s+(M\d+(?:-[a-z0-9]{6})?)/i);
|
|
234
240
|
if (executeMatch) {
|
|
@@ -243,6 +249,20 @@ async function buildGuidedExecuteContextInjection(prompt, basePath) {
|
|
|
243
249
|
return buildTaskExecutionContextInjection(basePath, milestoneId, sliceId, state.activeTask.id, state.activeTask.title);
|
|
244
250
|
}
|
|
245
251
|
}
|
|
252
|
+
// Fallback: low-entropy resume prompt (e.g., "continue", "ok", "go ahead")
|
|
253
|
+
// during an active executing task — inject task context so the agent
|
|
254
|
+
// doesn't rebuild from scratch (#3615).
|
|
255
|
+
// Intent-gated: only fire for short, resume-like prompts to avoid hijacking
|
|
256
|
+
// control/help/diagnostic prompts with unrelated execution context.
|
|
257
|
+
// Phase-gated: only fire during "executing" to avoid misrouting during
|
|
258
|
+
// replanning, gate evaluation, or other non-execution phases.
|
|
259
|
+
const trimmed = prompt.trim().toLowerCase().replace(/[.!?,]+$/g, "");
|
|
260
|
+
if (RESUME_INTENT_PATTERNS.test(trimmed)) {
|
|
261
|
+
const state = await deriveState(basePath);
|
|
262
|
+
if (state.phase === "executing" && state.activeTask && state.activeMilestone && state.activeSlice) {
|
|
263
|
+
return buildTaskExecutionContextInjection(basePath, state.activeMilestone.id, state.activeSlice.id, state.activeTask.id, state.activeTask.title);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
246
266
|
return null;
|
|
247
267
|
}
|
|
248
268
|
async function buildTaskExecutionContextInjection(basePath, milestoneId, sliceId, taskId, taskTitle) {
|
|
@@ -4,7 +4,7 @@ import { join } from "node:path";
|
|
|
4
4
|
import { loadRegistry } from "../workflow-templates.js";
|
|
5
5
|
import { resolveProjectRoot } from "../worktree.js";
|
|
6
6
|
const gsdHome = process.env.GSD_HOME || join(homedir(), ".gsd");
|
|
7
|
-
export const GSD_COMMAND_DESCRIPTION = "GSD — Get Shit Done: /gsd help|start|templates|next|auto|stop|pause|status|widget|visualize|queue|quick|discuss|capture|triage|dispatch|history|undo|undo-task|reset-slice|rate|skip|export|cleanup|mode|prefs|config|keys|hooks|run-hook|skill-health|doctor|logs|forensics|changelog|migrate|remote|steer|knowledge|new-milestone|parallel|cmux|park|unpark|init|setup|inspect|extensions|update|fast|mcp|rethink|codebase";
|
|
7
|
+
export const GSD_COMMAND_DESCRIPTION = "GSD — Get Shit Done: /gsd help|start|templates|next|auto|stop|pause|status|widget|visualize|queue|quick|discuss|capture|triage|dispatch|history|undo|undo-task|reset-slice|rate|skip|export|cleanup|mode|prefs|config|keys|hooks|run-hook|skill-health|doctor|logs|forensics|changelog|migrate|remote|steer|knowledge|new-milestone|parallel|cmux|park|unpark|init|setup|inspect|extensions|update|fast|mcp|rethink|codebase|notifications";
|
|
8
8
|
export const TOP_LEVEL_SUBCOMMANDS = [
|
|
9
9
|
{ cmd: "help", desc: "Categorized command reference with descriptions" },
|
|
10
10
|
{ cmd: "next", desc: "Explicit step mode (same as /gsd)" },
|
|
@@ -36,6 +36,7 @@ export const TOP_LEVEL_SUBCOMMANDS = [
|
|
|
36
36
|
{ cmd: "hooks", desc: "Show configured post-unit and pre-dispatch hooks" },
|
|
37
37
|
{ cmd: "run-hook", desc: "Manually trigger a specific hook" },
|
|
38
38
|
{ cmd: "skill-health", desc: "Skill lifecycle dashboard" },
|
|
39
|
+
{ cmd: "notifications", desc: "View, filter, and clear persistent notification history" },
|
|
39
40
|
{ cmd: "doctor", desc: "Runtime health checks with auto-fix" },
|
|
40
41
|
{ cmd: "logs", desc: "Browse activity logs, debug logs, and metrics" },
|
|
41
42
|
{ cmd: "forensics", desc: "Examine execution logs" },
|
|
@@ -97,6 +98,11 @@ const NESTED_COMPLETIONS = {
|
|
|
97
98
|
{ cmd: "keys", desc: "Manage API keys" },
|
|
98
99
|
{ cmd: "prefs", desc: "Configure global preferences" },
|
|
99
100
|
],
|
|
101
|
+
notifications: [
|
|
102
|
+
{ cmd: "clear", desc: "Clear all notifications" },
|
|
103
|
+
{ cmd: "tail", desc: "Show last N notifications (default: 20)" },
|
|
104
|
+
{ cmd: "filter", desc: "Filter by severity (error|warning|info|success)" },
|
|
105
|
+
],
|
|
100
106
|
logs: [
|
|
101
107
|
{ cmd: "debug", desc: "List or view debug log files" },
|
|
102
108
|
{ cmd: "tail", desc: "Show last N activity log summaries" },
|
|
@@ -25,6 +25,7 @@ export function showHelp(ctx) {
|
|
|
25
25
|
" /gsd queue Show queued/dispatched units and execution order",
|
|
26
26
|
" /gsd history View execution history [--cost] [--phase] [--model] [N]",
|
|
27
27
|
" /gsd changelog Show categorized release notes [version]",
|
|
28
|
+
" /gsd notifications View persistent notification history [clear|tail|filter] (Ctrl+Alt+N)",
|
|
28
29
|
"",
|
|
29
30
|
"COURSE CORRECTION",
|
|
30
31
|
" /gsd steer <desc> Apply user override to active work",
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
// GSD Extension — /gsd notifications Command Handler
|
|
2
|
+
// View, filter, and clear the persistent notification history.
|
|
3
|
+
import { readNotifications, clearNotifications, getUnreadCount, suppressPersistence, unsuppressPersistence, } from "../../notification-store.js";
|
|
4
|
+
import { GSDNotificationOverlay } from "../../notification-overlay.js";
|
|
5
|
+
function severityIcon(severity) {
|
|
6
|
+
switch (severity) {
|
|
7
|
+
case "error": return "✗";
|
|
8
|
+
case "warning": return "⚠";
|
|
9
|
+
case "success": return "✓";
|
|
10
|
+
case "info":
|
|
11
|
+
default: return "●";
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function formatTimestamp(ts) {
|
|
15
|
+
try {
|
|
16
|
+
const d = new Date(ts);
|
|
17
|
+
return d.toLocaleString("en-US", { hour12: false, month: "short", day: "numeric", hour: "2-digit", minute: "2-digit" });
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return ts.slice(0, 19);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export async function handleNotificationsCommand(args, ctx, pi) {
|
|
24
|
+
// /gsd notifications clear
|
|
25
|
+
if (args === "clear") {
|
|
26
|
+
clearNotifications();
|
|
27
|
+
// Suppress persistence so the confirmation toast doesn't re-populate the store
|
|
28
|
+
suppressPersistence();
|
|
29
|
+
try {
|
|
30
|
+
ctx.ui.notify("All notifications cleared.", "success");
|
|
31
|
+
}
|
|
32
|
+
finally {
|
|
33
|
+
unsuppressPersistence();
|
|
34
|
+
}
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
// /gsd notifications tail [N]
|
|
38
|
+
if (args === "tail" || args.startsWith("tail ")) {
|
|
39
|
+
const countStr = args.replace(/^tail\s*/, "").trim();
|
|
40
|
+
const count = countStr ? parseInt(countStr, 10) : 20;
|
|
41
|
+
const n = isNaN(count) || count < 1 ? 20 : Math.min(count, 100);
|
|
42
|
+
const entries = readNotifications().slice(0, n);
|
|
43
|
+
if (entries.length === 0) {
|
|
44
|
+
ctx.ui.notify("No notifications.", "info");
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
const lines = entries.map((e) => `${severityIcon(e.severity)} [${formatTimestamp(e.ts)}] ${e.message}`);
|
|
48
|
+
ctx.ui.notify(`Last ${entries.length} notification(s):\n${lines.join("\n")}`, "info");
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
// /gsd notifications filter <severity>
|
|
52
|
+
if (args.startsWith("filter ")) {
|
|
53
|
+
const severity = args.replace(/^filter\s+/, "").trim().toLowerCase();
|
|
54
|
+
if (!["error", "warning", "info", "success"].includes(severity)) {
|
|
55
|
+
ctx.ui.notify("Usage: /gsd notifications filter <error|warning|info|success>", "warning");
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
const entries = readNotifications().filter((e) => e.severity === severity);
|
|
59
|
+
if (entries.length === 0) {
|
|
60
|
+
ctx.ui.notify(`No ${severity} notifications.`, "info");
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
const lines = entries.slice(0, 20).map((e) => `${severityIcon(e.severity)} [${formatTimestamp(e.ts)}] ${e.message}`);
|
|
64
|
+
const suffix = entries.length > 20 ? `\n... and ${entries.length - 20} more` : "";
|
|
65
|
+
ctx.ui.notify(`${severity} notifications (${entries.length}):\n${lines.join("\n")}${suffix}`, "info");
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
// /gsd notifications (no args) — open overlay in TUI, or print summary
|
|
69
|
+
if (args === "" || args === "status") {
|
|
70
|
+
// Try overlay first (TUI mode)
|
|
71
|
+
if (ctx.hasUI) {
|
|
72
|
+
try {
|
|
73
|
+
await ctx.ui.custom((tui, theme, _kb, done) => new GSDNotificationOverlay(tui, theme, () => done()), {
|
|
74
|
+
overlay: true,
|
|
75
|
+
overlayOptions: {
|
|
76
|
+
width: "80%",
|
|
77
|
+
minWidth: 60,
|
|
78
|
+
maxHeight: "88%",
|
|
79
|
+
anchor: "center",
|
|
80
|
+
backdrop: true,
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
// Fall through to text output if overlay fails
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
// Text fallback (RPC/headless mode)
|
|
90
|
+
const unread = getUnreadCount();
|
|
91
|
+
const entries = readNotifications().slice(0, 10);
|
|
92
|
+
if (entries.length === 0) {
|
|
93
|
+
ctx.ui.notify("No notifications.", "info");
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
const lines = entries.map((e) => `${severityIcon(e.severity)} [${formatTimestamp(e.ts)}] ${e.message}`);
|
|
97
|
+
const header = unread > 0 ? `${unread} unread — ` : "";
|
|
98
|
+
ctx.ui.notify(`${header}Recent notifications:\n${lines.join("\n")}`, "info");
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
// Unknown subcommand
|
|
102
|
+
ctx.ui.notify("Usage: /gsd notifications [clear|tail [N]|filter <severity>]", "warning");
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
@@ -175,6 +175,11 @@ Examples:
|
|
|
175
175
|
await dispatchDirectPhase(ctx, pi, phase, projectRoot());
|
|
176
176
|
return true;
|
|
177
177
|
}
|
|
178
|
+
if (trimmed === "notifications" || trimmed.startsWith("notifications ")) {
|
|
179
|
+
const { handleNotificationsCommand } = await import("./notifications-handler.js");
|
|
180
|
+
await handleNotificationsCommand(trimmed.replace(/^notifications\s*/, "").trim(), ctx, pi);
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
178
183
|
if (trimmed === "inspect") {
|
|
179
184
|
await handleInspect(ctx);
|
|
180
185
|
return true;
|