gsd-pi 2.76.0-dev.fe143342a → 2.77.0-dev.1d17f366c
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 +17 -35
- package/dist/claude-cli-check.js +32 -3
- package/dist/cli-web-branch.d.ts +1 -0
- package/dist/cli-web-branch.js +3 -0
- package/dist/cli.js +38 -2
- package/dist/extension-discovery.d.ts +6 -0
- package/dist/extension-discovery.js +37 -0
- package/dist/extension-registry.d.ts +3 -0
- package/dist/extension-sort.d.ts +18 -0
- package/dist/extension-sort.js +114 -0
- package/dist/extension-validator.d.ts +47 -0
- package/dist/extension-validator.js +127 -0
- package/dist/loader.js +35 -7
- package/dist/onboarding.js +45 -0
- package/dist/provider-migrations.d.ts +18 -0
- package/dist/provider-migrations.js +14 -0
- package/dist/resources/extensions/claude-code-cli/readiness.js +4 -3
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +78 -59
- package/dist/resources/extensions/cmux/index.js +20 -0
- package/dist/resources/extensions/github-sync/templates.js +103 -0
- package/dist/resources/extensions/google-search/extension-manifest.json +5 -4
- package/dist/resources/extensions/google-search/index.js +3 -375
- package/dist/resources/extensions/gsd/abandon-detect.js +44 -0
- package/dist/resources/extensions/gsd/auto/loop.js +90 -2
- package/dist/resources/extensions/gsd/auto/phases.js +95 -21
- package/dist/resources/extensions/gsd/auto/resolve.js +24 -0
- package/dist/resources/extensions/gsd/auto/run-unit.js +48 -4
- package/dist/resources/extensions/gsd/auto/session.js +18 -1
- package/dist/resources/extensions/gsd/auto/turn-epoch.js +95 -0
- package/dist/resources/extensions/gsd/auto-dispatch.js +115 -17
- package/dist/resources/extensions/gsd/auto-loop.js +1 -1
- package/dist/resources/extensions/gsd/auto-model-selection.js +1 -1
- package/dist/resources/extensions/gsd/auto-post-unit.js +90 -2
- package/dist/resources/extensions/gsd/auto-prompts.js +14 -0
- package/dist/resources/extensions/gsd/auto-recovery.js +46 -1
- package/dist/resources/extensions/gsd/auto-start.js +45 -39
- package/dist/resources/extensions/gsd/auto-timeout-recovery.js +11 -5
- package/dist/resources/extensions/gsd/auto-unit-closeout.js +11 -2
- package/dist/resources/extensions/gsd/auto-worktree.js +109 -61
- package/dist/resources/extensions/gsd/auto.js +97 -31
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +27 -1
- package/dist/resources/extensions/gsd/bootstrap/provider-error-resume.js +4 -2
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +11 -0
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +17 -6
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +11 -6
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +34 -2
- package/dist/resources/extensions/gsd/clean-root-preflight.js +93 -0
- package/dist/resources/extensions/gsd/commands/handlers/workflow.js +31 -4
- package/dist/resources/extensions/gsd/commands-cmux.js +9 -6
- package/dist/resources/extensions/gsd/commands-extensions.js +634 -43
- package/dist/resources/extensions/gsd/commands-prefs-wizard.js +968 -23
- package/dist/resources/extensions/gsd/dispatch-guard.js +29 -3
- package/dist/resources/extensions/gsd/file-lock.js +49 -9
- package/dist/resources/extensions/gsd/git-service.js +1 -0
- package/dist/resources/extensions/gsd/gitignore.js +2 -0
- package/dist/resources/extensions/gsd/gsd-db.js +90 -30
- package/dist/resources/extensions/gsd/guided-flow-queue.js +4 -1
- package/dist/resources/extensions/gsd/guided-flow.js +212 -9
- package/dist/resources/extensions/gsd/health-widget.js +4 -1
- package/dist/resources/extensions/gsd/journal.js +17 -2
- package/dist/resources/extensions/gsd/key-manager.js +22 -0
- package/dist/resources/extensions/gsd/milestone-actions.js +15 -0
- package/dist/resources/extensions/gsd/milestone-summary-classifier.js +37 -0
- package/dist/resources/extensions/gsd/model-router.js +36 -3
- package/dist/resources/extensions/gsd/notifications.js +30 -16
- package/dist/resources/extensions/gsd/pre-execution-checks.js +31 -6
- package/dist/resources/extensions/gsd/prompts/discuss-headless.md +29 -2
- package/dist/resources/extensions/gsd/prompts/discuss.md +29 -2
- package/dist/resources/extensions/gsd/prompts/doctor-heal.md +5 -4
- package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +5 -2
- package/dist/resources/extensions/gsd/prompts/system.md +1 -0
- package/dist/resources/extensions/gsd/reports.js +5 -4
- package/dist/resources/extensions/gsd/safety/evidence-collector.js +96 -0
- package/dist/resources/extensions/gsd/safety/file-change-validator.js +12 -4
- package/dist/resources/extensions/gsd/safety/safety-harness.js +5 -1
- package/dist/resources/extensions/gsd/state-transition-matrix.js +118 -0
- package/dist/resources/extensions/gsd/state.js +25 -25
- package/dist/resources/extensions/gsd/token-counter.js +22 -5
- package/dist/resources/extensions/gsd/tools/complete-milestone.js +16 -10
- package/dist/resources/extensions/gsd/tools/complete-slice.js +21 -0
- package/dist/resources/extensions/gsd/tools/complete-task.js +31 -0
- package/dist/resources/extensions/gsd/uok/audit.js +18 -2
- package/dist/resources/extensions/gsd/uok/dispatch-envelope.js +33 -0
- package/dist/resources/extensions/gsd/uok/execution-graph.js +10 -0
- package/dist/resources/extensions/gsd/uok/gitops.js +2 -1
- package/dist/resources/extensions/gsd/uok/loop-adapter.js +37 -10
- package/dist/resources/extensions/gsd/uok/parity-report.js +58 -0
- package/dist/resources/extensions/gsd/uok/plan-v2.js +30 -7
- package/dist/resources/extensions/gsd/uok/writer.js +82 -0
- package/dist/resources/extensions/gsd/workflow-logger.js +10 -2
- package/dist/resources/extensions/gsd/worktree-manager.js +1 -0
- package/dist/resources/extensions/gsd/worktree-resolver.js +50 -10
- package/dist/resources/extensions/mcp-client/auth.js +10 -1
- package/dist/resources/extensions/mcp-client/index.js +118 -9
- package/dist/resources/extensions/shared/cmux-events.js +12 -0
- package/dist/resources/extensions/shared/rtk-session-stats.js +1 -2
- package/dist/resources/skills/create-skill/SKILL.md +2 -2
- package/dist/resources/skills/create-skill/references/gsd-skill-ecosystem.md +4 -4
- package/dist/resources/skills/create-skill/workflows/audit-skill.md +4 -4
- package/dist/resources/skills/create-skill/workflows/create-new-skill.md +5 -5
- package/dist/resources/skills/verify-before-complete/SKILL.md +2 -1
- package/dist/resources/skills/write-docs/SKILL.md +2 -1
- 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 +9 -9
- package/dist/web/standalone/.next/build-manifest.json +3 -3
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
- 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/boot/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/notifications/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/session/events/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/events/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route.js.nft.json +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 +9 -9
- package/dist/web/standalone/.next/server/chunks/1926.js +1 -0
- package/dist/web/standalone/.next/server/chunks/6897.js +2 -2
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
- package/dist/web/standalone/.next/server/middleware-react-loadable-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/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/static/chunks/2826.e9f5195e91f9cad2.js +11 -0
- package/dist/web/standalone/.next/static/chunks/{webpack-5fc74f13a25fa1bb.js → webpack-2e68521d7c82f7c2.js} +1 -1
- package/dist/welcome-screen.js +6 -1
- package/dist/wizard.js +2 -0
- package/package.json +16 -14
- package/packages/daemon/package.json +2 -2
- package/packages/mcp-server/README.md +3 -3
- package/packages/mcp-server/dist/env-writer.d.ts +1 -0
- package/packages/mcp-server/dist/env-writer.d.ts.map +1 -1
- package/packages/mcp-server/dist/env-writer.js +74 -6
- package/packages/mcp-server/dist/env-writer.js.map +1 -1
- package/packages/mcp-server/dist/remote-questions.d.ts +45 -0
- package/packages/mcp-server/dist/remote-questions.d.ts.map +1 -0
- package/packages/mcp-server/dist/remote-questions.js +732 -0
- package/packages/mcp-server/dist/remote-questions.js.map +1 -0
- package/packages/mcp-server/dist/server.d.ts +7 -0
- package/packages/mcp-server/dist/server.d.ts.map +1 -1
- package/packages/mcp-server/dist/server.js +95 -10
- package/packages/mcp-server/dist/server.js.map +1 -1
- package/packages/mcp-server/dist/session-manager.d.ts +14 -0
- package/packages/mcp-server/dist/session-manager.d.ts.map +1 -1
- package/packages/mcp-server/dist/session-manager.js +49 -1
- package/packages/mcp-server/dist/session-manager.js.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +15 -6
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/package.json +9 -3
- package/packages/mcp-server/src/env-writer.test.ts +79 -1
- package/packages/mcp-server/src/env-writer.ts +76 -6
- package/packages/mcp-server/src/mcp-server.test.ts +67 -0
- package/packages/mcp-server/src/readers/readers.test.ts +5 -1
- package/packages/mcp-server/src/remote-questions.test.ts +294 -0
- package/packages/mcp-server/src/remote-questions.ts +916 -0
- package/packages/mcp-server/src/server.ts +118 -16
- package/packages/mcp-server/src/session-manager.ts +43 -1
- package/packages/mcp-server/src/workflow-tools.test.ts +44 -0
- package/packages/mcp-server/src/workflow-tools.ts +19 -6
- package/packages/mcp-server/tsconfig.test.json +19 -0
- package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
- package/packages/native/package.json +6 -1
- package/packages/native/src/__tests__/clipboard.test.mjs +69 -23
- package/packages/native/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-agent-core/package.json +6 -1
- package/packages/pi-agent-core/src/agent-loop.test.ts +220 -15
- package/packages/pi-ai/dist/models/custom.d.ts +38 -0
- package/packages/pi-ai/dist/models/custom.d.ts.map +1 -1
- package/packages/pi-ai/dist/models/custom.js +41 -0
- package/packages/pi-ai/dist/models/custom.js.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic-auth.test.js +1 -1
- package/packages/pi-ai/dist/providers/anthropic-auth.test.js.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic-shared.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic-shared.js +27 -4
- package/packages/pi-ai/dist/providers/anthropic-shared.js.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic.js +8 -3
- package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
- package/packages/pi-ai/dist/providers/minimax-tool-name.test.d.ts +2 -0
- package/packages/pi-ai/dist/providers/minimax-tool-name.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/providers/minimax-tool-name.test.js +80 -0
- package/packages/pi-ai/dist/providers/minimax-tool-name.test.js.map +1 -0
- package/packages/pi-ai/dist/providers/simple-options.d.ts +10 -0
- package/packages/pi-ai/dist/providers/simple-options.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/simple-options.js +16 -1
- package/packages/pi-ai/dist/providers/simple-options.js.map +1 -1
- package/packages/pi-ai/package.json +6 -1
- package/packages/pi-ai/src/models/custom.ts +42 -0
- package/packages/pi-ai/src/providers/anthropic-auth.test.ts +1 -1
- package/packages/pi-ai/src/providers/anthropic-shared.ts +26 -5
- package/packages/pi-ai/src/providers/anthropic.ts +9 -3
- package/packages/pi-ai/src/providers/minimax-tool-name.test.ts +98 -0
- package/packages/pi-ai/src/providers/simple-options.ts +17 -1
- package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js +3 -2
- package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts +2 -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 +7 -0
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction/compaction.d.ts +25 -0
- package/packages/pi-coding-agent/dist/core/compaction/compaction.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction/compaction.js +105 -6
- package/packages/pi-coding-agent/dist/core/compaction/compaction.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction/compaction.test.js +230 -28
- package/packages/pi-coding-agent/dist/core/compaction/compaction.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction/utils.d.ts +30 -2
- package/packages/pi-coding-agent/dist/core/compaction/utils.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction/utils.js +113 -12
- package/packages/pi-coding-agent/dist/core/compaction/utils.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction-orchestrator.d.ts +1 -0
- package/packages/pi-coding-agent/dist/core/compaction-orchestrator.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js +29 -18
- package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/compaction-orchestrator.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/compaction-orchestrator.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/compaction-orchestrator.test.js +130 -0
- package/packages/pi-coding-agent/dist/core/compaction-orchestrator.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/compaction-utils.test.js +56 -1
- package/packages/pi-coding-agent/dist/core/compaction-utils.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/discovery-cache.test.js +8 -15
- package/packages/pi-coding-agent/dist/core/discovery-cache.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/extension-discovery.d.ts +25 -0
- package/packages/pi-coding-agent/dist/core/extensions/extension-discovery.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/extensions/extension-discovery.js +109 -0
- package/packages/pi-coding-agent/dist/core/extensions/extension-discovery.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/extensions/extension-registry.d.ts +67 -0
- package/packages/pi-coding-agent/dist/core/extensions/extension-registry.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/extensions/extension-registry.js +167 -0
- package/packages/pi-coding-agent/dist/core/extensions/extension-registry.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts +3 -2
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.js +24 -8
- package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +14 -0
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/lsp-integration.test.js +11 -0
- package/packages/pi-coding-agent/dist/core/lsp/lsp-integration.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js +2 -2
- package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry-custom-caps.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/model-registry-custom-caps.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry-custom-caps.test.js +203 -0
- package/packages/pi-coding-agent/dist/core/model-registry-custom-caps.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.js +14 -0
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/resource-loader.js +1 -1
- package/packages/pi-coding-agent/dist/core/resource-loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.d.ts +1 -0
- package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.js +4 -1
- package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.test.js +19 -1
- package/packages/pi-coding-agent/dist/core/sdk.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/session-manager.js +1 -1
- package/packages/pi-coding-agent/dist/core/session-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/session-manager.test.js +21 -1
- package/packages/pi-coding-agent/dist/core/session-manager.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.js +3 -3
- package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/path-utils.test.js +2 -1
- package/packages/pi-coding-agent/dist/core/tools/path-utils.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/provider-display-name.test.js +15 -6
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/provider-display-name.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js +14 -5
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.d.ts +7 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js +31 -9
- package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +14 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -1
- 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 +13 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/package.json +6 -1
- package/packages/pi-coding-agent/src/core/agent-session-abort-order.test.ts +3 -2
- package/packages/pi-coding-agent/src/core/agent-session.ts +11 -0
- package/packages/pi-coding-agent/src/core/compaction/compaction.test.ts +368 -28
- package/packages/pi-coding-agent/src/core/compaction/compaction.ts +122 -6
- package/packages/pi-coding-agent/src/core/compaction/utils.ts +111 -13
- package/packages/pi-coding-agent/src/core/compaction-orchestrator.test.ts +154 -0
- package/packages/pi-coding-agent/src/core/compaction-orchestrator.ts +32 -18
- package/packages/pi-coding-agent/src/core/compaction-utils.test.ts +68 -1
- package/packages/pi-coding-agent/src/core/discovery-cache.test.ts +9 -18
- package/packages/pi-coding-agent/src/core/extensions/extension-discovery.ts +119 -0
- package/packages/pi-coding-agent/src/core/extensions/extension-registry.ts +222 -0
- package/packages/pi-coding-agent/src/core/extensions/loader.ts +24 -11
- package/packages/pi-coding-agent/src/core/extensions/runner.ts +2 -0
- package/packages/pi-coding-agent/src/core/extensions/types.ts +15 -0
- package/packages/pi-coding-agent/src/core/lsp/lsp-integration.test.ts +13 -0
- package/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts +2 -2
- package/packages/pi-coding-agent/src/core/model-registry-custom-caps.test.ts +245 -0
- package/packages/pi-coding-agent/src/core/model-registry.ts +16 -0
- package/packages/pi-coding-agent/src/core/resource-loader.ts +1 -1
- package/packages/pi-coding-agent/src/core/sdk.test.ts +25 -1
- package/packages/pi-coding-agent/src/core/sdk.ts +10 -3
- package/packages/pi-coding-agent/src/core/session-manager.test.ts +30 -1
- package/packages/pi-coding-agent/src/core/session-manager.ts +1 -1
- package/packages/pi-coding-agent/src/core/system-prompt.ts +3 -3
- package/packages/pi-coding-agent/src/core/tools/path-utils.test.ts +2 -1
- package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/provider-display-name.test.ts +17 -7
- package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +14 -5
- package/packages/pi-coding-agent/src/modes/interactive/components/model-selector.ts +45 -11
- package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +14 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +13 -1
- package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-tui/dist/__tests__/autocomplete.test.js +12 -5
- package/packages/pi-tui/dist/__tests__/autocomplete.test.js.map +1 -1
- package/packages/pi-tui/dist/__tests__/stdin-buffer.test.js +21 -0
- package/packages/pi-tui/dist/__tests__/stdin-buffer.test.js.map +1 -1
- package/packages/pi-tui/dist/stdin-buffer.d.ts +7 -0
- package/packages/pi-tui/dist/stdin-buffer.d.ts.map +1 -1
- package/packages/pi-tui/dist/stdin-buffer.js +20 -0
- package/packages/pi-tui/dist/stdin-buffer.js.map +1 -1
- package/packages/pi-tui/package.json +6 -1
- package/packages/pi-tui/src/__tests__/autocomplete.test.ts +20 -5
- package/packages/pi-tui/src/__tests__/stdin-buffer.test.ts +27 -0
- package/packages/pi-tui/src/stdin-buffer.ts +26 -0
- package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
- package/packages/rpc-client/package.json +6 -1
- package/pkg/package.json +1 -1
- package/scripts/install.js +512 -0
- package/scripts/lib/workspace-manifest.cjs +86 -0
- package/scripts/link-workspace-packages.cjs +5 -17
- package/scripts/postinstall.js +9 -178
- package/src/resources/extensions/claude-code-cli/readiness.ts +4 -3
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +91 -63
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +114 -12
- package/src/resources/extensions/cmux/index.ts +35 -10
- package/src/resources/extensions/github-sync/templates.ts +151 -0
- package/src/resources/extensions/github-sync/tests/templates.test.ts +59 -0
- package/src/resources/extensions/google-search/extension-manifest.json +5 -4
- package/src/resources/extensions/google-search/index.ts +9 -470
- package/src/resources/extensions/gsd/abandon-detect.ts +62 -0
- package/src/resources/extensions/gsd/auto/loop-deps.ts +14 -1
- package/src/resources/extensions/gsd/auto/loop.ts +104 -2
- package/src/resources/extensions/gsd/auto/phases.ts +123 -21
- package/src/resources/extensions/gsd/auto/resolve.ts +29 -0
- package/src/resources/extensions/gsd/auto/run-unit.ts +56 -4
- package/src/resources/extensions/gsd/auto/session.ts +28 -1
- package/src/resources/extensions/gsd/auto/turn-epoch.ts +108 -0
- package/src/resources/extensions/gsd/auto/types.ts +1 -1
- package/src/resources/extensions/gsd/auto-dispatch.ts +117 -16
- package/src/resources/extensions/gsd/auto-loop.ts +1 -1
- package/src/resources/extensions/gsd/auto-model-selection.ts +1 -1
- package/src/resources/extensions/gsd/auto-post-unit.ts +92 -3
- package/src/resources/extensions/gsd/auto-prompts.ts +28 -1
- package/src/resources/extensions/gsd/auto-recovery.ts +40 -1
- package/src/resources/extensions/gsd/auto-start.ts +48 -52
- package/src/resources/extensions/gsd/auto-timeout-recovery.ts +12 -5
- package/src/resources/extensions/gsd/auto-unit-closeout.ts +14 -3
- package/src/resources/extensions/gsd/auto-worktree.ts +122 -68
- package/src/resources/extensions/gsd/auto.ts +105 -35
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +34 -1
- package/src/resources/extensions/gsd/bootstrap/provider-error-resume.ts +6 -2
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +11 -0
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +18 -6
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +13 -9
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +35 -2
- package/src/resources/extensions/gsd/clean-root-preflight.ts +111 -0
- package/src/resources/extensions/gsd/commands/handlers/workflow.ts +27 -8
- package/src/resources/extensions/gsd/commands-cmux.ts +10 -6
- package/src/resources/extensions/gsd/commands-extensions.ts +747 -41
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +898 -32
- package/src/resources/extensions/gsd/dispatch-guard.ts +26 -2
- package/src/resources/extensions/gsd/file-lock.ts +84 -11
- package/src/resources/extensions/gsd/git-service.ts +1 -0
- package/src/resources/extensions/gsd/gitignore.ts +2 -1
- package/src/resources/extensions/gsd/gsd-db.ts +92 -32
- package/src/resources/extensions/gsd/guided-flow-queue.ts +4 -1
- package/src/resources/extensions/gsd/guided-flow.ts +259 -10
- package/src/resources/extensions/gsd/health-widget.ts +3 -1
- package/src/resources/extensions/gsd/journal.ts +29 -3
- package/src/resources/extensions/gsd/key-manager.ts +22 -0
- package/src/resources/extensions/gsd/milestone-actions.ts +18 -0
- package/src/resources/extensions/gsd/milestone-summary-classifier.ts +42 -0
- package/src/resources/extensions/gsd/model-router.ts +42 -1
- package/src/resources/extensions/gsd/notifications.ts +27 -15
- package/src/resources/extensions/gsd/pre-execution-checks.ts +33 -7
- package/src/resources/extensions/gsd/preferences-types.ts +8 -0
- package/src/resources/extensions/gsd/prompts/discuss-headless.md +29 -2
- package/src/resources/extensions/gsd/prompts/discuss.md +29 -2
- package/src/resources/extensions/gsd/prompts/doctor-heal.md +5 -4
- package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +5 -2
- package/src/resources/extensions/gsd/prompts/system.md +1 -0
- package/src/resources/extensions/gsd/reports.ts +5 -4
- package/src/resources/extensions/gsd/safety/evidence-collector.ts +119 -0
- package/src/resources/extensions/gsd/safety/file-change-validator.ts +16 -3
- package/src/resources/extensions/gsd/safety/safety-harness.ts +9 -0
- package/src/resources/extensions/gsd/state-transition-matrix.ts +152 -0
- package/src/resources/extensions/gsd/state.ts +35 -30
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +238 -4
- package/src/resources/extensions/gsd/tests/auto-mode-guards.test.ts +79 -0
- package/src/resources/extensions/gsd/tests/auto-paused-session-validation.test.ts +12 -0
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +122 -0
- package/src/resources/extensions/gsd/tests/auto-start-bootstrap-await-3420.test.ts +141 -0
- package/src/resources/extensions/gsd/tests/auto-start-clean-runtime-db-gated.test.ts +63 -0
- package/src/resources/extensions/gsd/tests/auto-wrapup-inflight-guard.test.ts +23 -0
- package/src/resources/extensions/gsd/tests/clean-root-preflight.test.ts +186 -0
- package/src/resources/extensions/gsd/tests/cmux.test.ts +5 -9
- package/src/resources/extensions/gsd/tests/complete-milestone-false-merge.test.ts +15 -0
- package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +61 -1
- package/src/resources/extensions/gsd/tests/complete-slice.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +161 -0
- package/src/resources/extensions/gsd/tests/db-access-guardrails.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/derive-state.test.ts +1 -2
- package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +14 -9
- package/src/resources/extensions/gsd/tests/dispatch-guard-summary-db-mismatch.test.ts +77 -0
- package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +25 -0
- package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +14 -0
- package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +31 -0
- package/src/resources/extensions/gsd/tests/double-merge-guard.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/escalation.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/exec-history.test.ts +113 -0
- package/src/resources/extensions/gsd/tests/execution-entry-missing-context-4671.test.ts +173 -0
- package/src/resources/extensions/gsd/tests/file-change-validator.test.ts +38 -0
- package/src/resources/extensions/gsd/tests/file-lock.test.ts +86 -12
- package/src/resources/extensions/gsd/tests/google-search-stub.test.ts +131 -0
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +296 -1
- package/src/resources/extensions/gsd/tests/headless-milestone-parity.test.ts +117 -0
- package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +30 -0
- package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +4 -2
- package/src/resources/extensions/gsd/tests/integration/gitignore-tracked-gsd.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/integration/idle-recovery.test.ts +30 -0
- package/src/resources/extensions/gsd/tests/integration/worktree-e2e.test.ts +11 -0
- package/src/resources/extensions/gsd/tests/issue-4540-regressions.test.ts +288 -0
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +66 -0
- package/src/resources/extensions/gsd/tests/key-manager.test.ts +2 -0
- package/src/resources/extensions/gsd/tests/mcp-client-security.test.ts +76 -0
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/memory-pressure-stuck-state.test.ts +12 -0
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/milestone-status-authoritative.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/milestone-summary-classifier.test.ts +30 -0
- package/src/resources/extensions/gsd/tests/milestone-transition-state-rebuild.test.ts +4 -2
- package/src/resources/extensions/gsd/tests/parallel-commit-scope.test.ts +5 -0
- package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +19 -0
- package/src/resources/extensions/gsd/tests/parallel-skill-prompt-integration.test.ts +150 -0
- package/src/resources/extensions/gsd/tests/plan-gate-failed-doctor-heal-hint.test.ts +37 -0
- package/src/resources/extensions/gsd/tests/pre-exec-gate-loop.test.ts +272 -0
- package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +337 -0
- package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +44 -0
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +39 -25
- package/src/resources/extensions/gsd/tests/queue-auto-guard.test.ts +181 -0
- package/src/resources/extensions/gsd/tests/quick-auto-guard.test.ts +13 -7
- package/src/resources/extensions/gsd/tests/ready-phrase-no-files-4573.test.ts +388 -0
- package/src/resources/extensions/gsd/tests/require-slice-discussion-dispatch.test.ts +170 -0
- package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +9 -3
- package/src/resources/extensions/gsd/tests/resume-dispatch-worktree.test.ts +230 -0
- package/src/resources/extensions/gsd/tests/rewrite-docs-abandon-detect.test.ts +195 -0
- package/src/resources/extensions/gsd/tests/safety-harness-false-positives.test.ts +205 -0
- package/src/resources/extensions/gsd/tests/schema-v21-sequence.test.ts +413 -0
- package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +32 -40
- package/src/resources/extensions/gsd/tests/stale-dirlistcache-4648.test.ts +112 -0
- package/src/resources/extensions/gsd/tests/stash-queued-context-files.test.ts +56 -0
- package/src/resources/extensions/gsd/tests/state-machine-full-walkthrough.test.ts +24 -0
- package/src/resources/extensions/gsd/tests/state-transition-matrix.test.ts +44 -0
- package/src/resources/extensions/gsd/tests/stuck-detection-coverage.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/token-counter.test.ts +105 -1
- package/src/resources/extensions/gsd/tests/tool-compatibility.test.ts +107 -0
- package/src/resources/extensions/gsd/tests/triage-resolution.test.ts +50 -2
- package/src/resources/extensions/gsd/tests/turn-epoch.test.ts +162 -0
- package/src/resources/extensions/gsd/tests/uok-contracts.test.ts +51 -0
- package/src/resources/extensions/gsd/tests/uok-execution-graph.test.ts +16 -0
- package/src/resources/extensions/gsd/tests/uok-loop-adapter-writer.test.ts +65 -0
- package/src/resources/extensions/gsd/tests/uok-parity-report.test.ts +42 -0
- package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +42 -2
- package/src/resources/extensions/gsd/tests/uok-writer.test.ts +75 -0
- package/src/resources/extensions/gsd/tests/validate-extension-package.test.ts +168 -0
- package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +138 -5
- package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +25 -2
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +65 -2
- package/src/resources/extensions/gsd/tests/worktree-db.test.ts +35 -0
- package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +6 -1
- package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +78 -5
- package/src/resources/extensions/gsd/tests/write-gate.test.ts +64 -0
- package/src/resources/extensions/gsd/token-counter.ts +22 -5
- package/src/resources/extensions/gsd/tools/complete-milestone.ts +15 -9
- package/src/resources/extensions/gsd/tools/complete-slice.ts +38 -0
- package/src/resources/extensions/gsd/tools/complete-task.ts +49 -0
- package/src/resources/extensions/gsd/uok/audit.ts +20 -2
- package/src/resources/extensions/gsd/uok/contracts.ts +65 -0
- package/src/resources/extensions/gsd/uok/dispatch-envelope.ts +56 -0
- package/src/resources/extensions/gsd/uok/execution-graph.ts +22 -0
- package/src/resources/extensions/gsd/uok/gitops.ts +6 -1
- package/src/resources/extensions/gsd/uok/loop-adapter.ts +45 -10
- package/src/resources/extensions/gsd/uok/parity-report.ts +84 -0
- package/src/resources/extensions/gsd/uok/plan-v2.ts +39 -8
- package/src/resources/extensions/gsd/uok/writer.ts +113 -0
- package/src/resources/extensions/gsd/workflow-logger.ts +23 -3
- package/src/resources/extensions/gsd/worktree-manager.ts +1 -0
- package/src/resources/extensions/gsd/worktree-resolver.ts +54 -9
- package/src/resources/extensions/mcp-client/auth.ts +12 -1
- package/src/resources/extensions/mcp-client/index.ts +129 -10
- package/src/resources/extensions/shared/cmux-events.ts +59 -0
- package/src/resources/extensions/shared/rtk-session-stats.ts +1 -2
- package/src/resources/skills/create-skill/SKILL.md +2 -2
- package/src/resources/skills/create-skill/references/gsd-skill-ecosystem.md +4 -4
- package/src/resources/skills/create-skill/workflows/audit-skill.md +4 -4
- package/src/resources/skills/create-skill/workflows/create-new-skill.md +5 -5
- package/src/resources/skills/verify-before-complete/SKILL.md +2 -1
- package/src/resources/skills/write-docs/SKILL.md +2 -1
- package/dist/web/standalone/.next/server/chunks/7461.js +0 -1
- package/dist/web/standalone/.next/static/chunks/2826.e59e8578e2e28639.js +0 -9
- /package/dist/web/standalone/.next/static/{n21VtX2hZlkpdEUO_nU4z → vidAVJkURvTJ0_V2-64ro}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{n21VtX2hZlkpdEUO_nU4z → vidAVJkURvTJ0_V2-64ro}/_ssgManifest.js +0 -0
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* GSD Extensions Command — /gsd extensions
|
|
3
3
|
*
|
|
4
|
-
* Manage the extension registry: list, enable, disable, info.
|
|
4
|
+
* Manage the extension registry: list, enable, disable, info, install.
|
|
5
5
|
* Self-contained — no imports outside the extensions tree (extensions are loaded
|
|
6
6
|
* via jiti at runtime from ~/.gsd/agent/, not compiled by tsc).
|
|
7
7
|
*/
|
|
8
|
-
import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, writeFileSync } from "node:fs";
|
|
9
|
-
import { dirname, join } from "node:path";
|
|
10
|
-
import { homedir } from "node:os";
|
|
8
|
+
import { cpSync, existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
9
|
+
import { dirname, join, resolve } from "node:path";
|
|
10
|
+
import { homedir, tmpdir } from "node:os";
|
|
11
|
+
import { execFileSync } from "node:child_process";
|
|
12
|
+
import { lockSync, unlockSync } from "proper-lockfile";
|
|
13
|
+
import semver from "semver";
|
|
11
14
|
const gsdHome = process.env.GSD_HOME || join(homedir(), ".gsd");
|
|
12
15
|
// ─── Registry I/O ───────────────────────────────────────────────────────────
|
|
13
16
|
function getRegistryPath() {
|
|
@@ -42,6 +45,35 @@ function saveRegistry(registry) {
|
|
|
42
45
|
}
|
|
43
46
|
catch { /* non-fatal */ }
|
|
44
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* Run a registry load → mutate → save transaction under a cross-process lock.
|
|
50
|
+
* Prevents two concurrent `gsd extensions install/uninstall/update` invocations
|
|
51
|
+
* from trampling each other's registry mutations.
|
|
52
|
+
*
|
|
53
|
+
* Uses proper-lockfile.lockSync against the registry path. Directory is created
|
|
54
|
+
* first so locking works on fresh installs. Lock is always released via finally.
|
|
55
|
+
*/
|
|
56
|
+
function withRegistryLock(mutate) {
|
|
57
|
+
const filePath = getRegistryPath();
|
|
58
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
59
|
+
// lockSync requires the file to exist — ensure it does before acquiring.
|
|
60
|
+
if (!existsSync(filePath)) {
|
|
61
|
+
writeFileSync(filePath, JSON.stringify({ version: 1, entries: {} }, null, 2), "utf-8");
|
|
62
|
+
}
|
|
63
|
+
lockSync(filePath, { retries: { retries: 5, minTimeout: 50, maxTimeout: 500 } });
|
|
64
|
+
try {
|
|
65
|
+
const registry = loadRegistry();
|
|
66
|
+
const result = mutate(registry);
|
|
67
|
+
saveRegistry(registry);
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
70
|
+
finally {
|
|
71
|
+
try {
|
|
72
|
+
unlockSync(filePath);
|
|
73
|
+
}
|
|
74
|
+
catch { /* lock may already be gone */ }
|
|
75
|
+
}
|
|
76
|
+
}
|
|
45
77
|
function isEnabled(registry, id) {
|
|
46
78
|
const entry = registry.entries[id];
|
|
47
79
|
if (!entry)
|
|
@@ -62,20 +94,517 @@ function readManifest(dir) {
|
|
|
62
94
|
return null;
|
|
63
95
|
}
|
|
64
96
|
}
|
|
97
|
+
export function validateExtensionPackage(packageDir) {
|
|
98
|
+
const errors = [];
|
|
99
|
+
const warnings = [];
|
|
100
|
+
// Check package.json exists
|
|
101
|
+
const pkgPath = join(packageDir, "package.json");
|
|
102
|
+
if (!existsSync(pkgPath)) {
|
|
103
|
+
return { valid: false, errors: ["package.json not found"], warnings };
|
|
104
|
+
}
|
|
105
|
+
let pkg;
|
|
106
|
+
try {
|
|
107
|
+
pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
return { valid: false, errors: ["package.json is invalid JSON"], warnings };
|
|
111
|
+
}
|
|
112
|
+
// (a) gsd.extension: true marker (D-12a)
|
|
113
|
+
const gsdField = pkg.gsd;
|
|
114
|
+
if (gsdField?.extension !== true) {
|
|
115
|
+
errors.push('package.json missing "gsd": { "extension": true }');
|
|
116
|
+
}
|
|
117
|
+
// (b) pi.extensions entry paths exist and are resolvable (D-12b)
|
|
118
|
+
const piField = pkg.pi;
|
|
119
|
+
const piExtensions = piField?.extensions;
|
|
120
|
+
if (!Array.isArray(piExtensions) || piExtensions.length === 0) {
|
|
121
|
+
errors.push('package.json missing "pi": { "extensions": [...] }');
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
for (const entry of piExtensions) {
|
|
125
|
+
if (typeof entry === "string") {
|
|
126
|
+
const resolved = join(packageDir, entry);
|
|
127
|
+
if (!existsSync(resolved)) {
|
|
128
|
+
errors.push(`pi.extensions entry not found: ${entry}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
// (c) @gsd/* packages must be in peerDependencies, not dependencies/devDependencies (D-12c)
|
|
134
|
+
// Mirrors validateExtensionManifest below and extension-validator.ts:checkDependencyPlacement.
|
|
135
|
+
for (const field of ["dependencies", "devDependencies"]) {
|
|
136
|
+
const deps = pkg[field] ?? {};
|
|
137
|
+
for (const dep of Object.keys(deps)) {
|
|
138
|
+
if (dep.startsWith("@gsd/")) {
|
|
139
|
+
errors.push(`"${dep}" must be in peerDependencies, not ${field}`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return { valid: errors.length === 0, errors, warnings };
|
|
144
|
+
}
|
|
65
145
|
function discoverManifests() {
|
|
66
|
-
const extDir = getAgentExtensionsDir();
|
|
67
146
|
const manifests = new Map();
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
147
|
+
// Scan both bundled/agent dir and user-installed dir so CLI (list/info/
|
|
148
|
+
// enable/disable) sees the same set the loader will merge at runtime.
|
|
149
|
+
// Bundled entries are scanned first so user-installed IDs override on collision.
|
|
150
|
+
const dirs = [getAgentExtensionsDir(), getInstalledExtDir()];
|
|
151
|
+
for (const extDir of dirs) {
|
|
152
|
+
if (!existsSync(extDir))
|
|
72
153
|
continue;
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
154
|
+
for (const entry of readdirSync(extDir, { withFileTypes: true })) {
|
|
155
|
+
if (!entry.isDirectory() && !entry.isSymbolicLink())
|
|
156
|
+
continue;
|
|
157
|
+
const m = readManifest(join(extDir, entry.name));
|
|
158
|
+
if (m)
|
|
159
|
+
manifests.set(m.id, m);
|
|
160
|
+
}
|
|
76
161
|
}
|
|
77
162
|
return manifests;
|
|
78
163
|
}
|
|
164
|
+
function getInstalledExtDir() {
|
|
165
|
+
return join(gsdHome, "extensions");
|
|
166
|
+
}
|
|
167
|
+
// Source: derived from npm/git URL conventions (from RESEARCH.md)
|
|
168
|
+
function detectInstallType(specifier) {
|
|
169
|
+
if (specifier.startsWith("/") ||
|
|
170
|
+
specifier.startsWith("./") ||
|
|
171
|
+
specifier.startsWith("../") ||
|
|
172
|
+
specifier.startsWith("~/"))
|
|
173
|
+
return "local";
|
|
174
|
+
if (specifier.startsWith("git+") ||
|
|
175
|
+
specifier.startsWith("git://") ||
|
|
176
|
+
specifier.startsWith("github:") ||
|
|
177
|
+
specifier.startsWith("gitlab:") ||
|
|
178
|
+
specifier.startsWith("bitbucket:") ||
|
|
179
|
+
(specifier.startsWith("https://") && specifier.endsWith(".git")) ||
|
|
180
|
+
(specifier.startsWith("http://") && specifier.endsWith(".git")))
|
|
181
|
+
return "git";
|
|
182
|
+
return "npm";
|
|
183
|
+
}
|
|
184
|
+
function validateExtensionManifest(pkg, opts = {}) {
|
|
185
|
+
const errors = [];
|
|
186
|
+
// Check gsd.extension === true (strict)
|
|
187
|
+
if (typeof pkg !== "object" || pkg === null) {
|
|
188
|
+
errors.push({ code: "MISSING_GSD_MARKER", message: 'package.json must declare "gsd": { "extension": true } to be recognized as a GSD extension.', field: "gsd.extension" });
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
const obj = pkg;
|
|
192
|
+
const gsd = obj.gsd;
|
|
193
|
+
if (typeof gsd !== "object" || gsd === null || gsd.extension !== true) {
|
|
194
|
+
errors.push({ code: "MISSING_GSD_MARKER", message: 'package.json must declare "gsd": { "extension": true } to be recognized as a GSD extension.', field: "gsd.extension" });
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
// Check namespace reservation
|
|
198
|
+
if (opts.extensionId && opts.extensionId.startsWith("gsd.") && opts.allowGsdNamespace !== true) {
|
|
199
|
+
errors.push({ code: "RESERVED_NAMESPACE", message: `Extension ID "${opts.extensionId}" is reserved for GSD core extensions. Use a different namespace for community extensions.`, field: "extensionId" });
|
|
200
|
+
}
|
|
201
|
+
// Check dependency placement
|
|
202
|
+
if (typeof pkg === "object" && pkg !== null) {
|
|
203
|
+
const obj = pkg;
|
|
204
|
+
for (const field of ["dependencies", "devDependencies"]) {
|
|
205
|
+
const deps = obj[field];
|
|
206
|
+
if (typeof deps === "object" && deps !== null) {
|
|
207
|
+
for (const pkgName of Object.keys(deps)) {
|
|
208
|
+
if (pkgName.startsWith("@gsd/")) {
|
|
209
|
+
errors.push({ code: "WRONG_DEP_FIELD", message: `"${pkgName}" must not appear in "${field}". Move it to "peerDependencies".`, field });
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return { valid: errors.length === 0, errors };
|
|
216
|
+
}
|
|
217
|
+
// ─── Post-install convergence ────────────────────────────────────────────────
|
|
218
|
+
/**
|
|
219
|
+
* Allowed characters for an extension id when used as a path segment.
|
|
220
|
+
* Rejects anything that could enable traversal or escape (slashes, "..", backslashes).
|
|
221
|
+
*/
|
|
222
|
+
const SAFE_EXTENSION_ID_RE = /^[A-Za-z0-9._-]+$/;
|
|
223
|
+
function isSafeExtensionId(id) {
|
|
224
|
+
if (!id || id === "." || id === "..")
|
|
225
|
+
return false;
|
|
226
|
+
if (id.includes("/") || id.includes("\\") || id.includes(".."))
|
|
227
|
+
return false;
|
|
228
|
+
return SAFE_EXTENSION_ID_RE.test(id);
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Post-install convergence: validate package and read manifest.
|
|
232
|
+
* Returns the (validated) extension ID and manifest on success, or null on failure.
|
|
233
|
+
* Caller is responsible for writing the registry entry *after* the final commit
|
|
234
|
+
* rename succeeds so a failed move doesn't leave a dangling registry entry.
|
|
235
|
+
*/
|
|
236
|
+
function postInstallValidate(destPath, specifier, ctx) {
|
|
237
|
+
// Read package.json
|
|
238
|
+
const pkgJsonPath = join(destPath, "package.json");
|
|
239
|
+
if (!existsSync(pkgJsonPath)) {
|
|
240
|
+
ctx.ui.notify(`Cannot install "${specifier}": no package.json found.`, "error");
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
let pkgJson;
|
|
244
|
+
try {
|
|
245
|
+
pkgJson = JSON.parse(readFileSync(pkgJsonPath, "utf-8"));
|
|
246
|
+
}
|
|
247
|
+
catch {
|
|
248
|
+
ctx.ui.notify(`Cannot install "${specifier}": malformed package.json.`, "error");
|
|
249
|
+
return null;
|
|
250
|
+
}
|
|
251
|
+
// Read extension-manifest.json for the ID
|
|
252
|
+
const manifest = readManifest(destPath);
|
|
253
|
+
const extensionId = manifest?.id;
|
|
254
|
+
// Validate
|
|
255
|
+
const validation = validateExtensionManifest(pkgJson, { extensionId });
|
|
256
|
+
if (!validation.valid) {
|
|
257
|
+
const msgs = validation.errors.map(e => e.message).join("\n");
|
|
258
|
+
ctx.ui.notify(`Cannot install "${specifier}": ${msgs}`, "error");
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
261
|
+
if (!manifest || !extensionId) {
|
|
262
|
+
ctx.ui.notify(`Cannot install "${specifier}": no extension-manifest.json with valid id found.`, "error");
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
265
|
+
// The id from the manifest is used as a path segment under installedExtDir.
|
|
266
|
+
// Reject unsafe ids before the caller performs any path joins.
|
|
267
|
+
if (!isSafeExtensionId(extensionId)) {
|
|
268
|
+
ctx.ui.notify(`Cannot install "${specifier}": extension id "${extensionId}" contains unsafe characters (allowed: alphanumerics, ".", "-", "_").`, "error");
|
|
269
|
+
return null;
|
|
270
|
+
}
|
|
271
|
+
return { id: extensionId, manifest };
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Write the registry entry for a freshly-installed extension. Called after the
|
|
275
|
+
* final destination commit succeeds so a failed rename can't leave a stale entry.
|
|
276
|
+
*/
|
|
277
|
+
function writeInstalledRegistryEntry(id, manifest, specifier, installType) {
|
|
278
|
+
withRegistryLock((registry) => {
|
|
279
|
+
registry.entries[id] = {
|
|
280
|
+
id,
|
|
281
|
+
enabled: true,
|
|
282
|
+
source: "user",
|
|
283
|
+
version: manifest.version,
|
|
284
|
+
installedFrom: specifier,
|
|
285
|
+
installType,
|
|
286
|
+
};
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
// ─── Uninstall helpers ───────────────────────────────────────────────────────
|
|
290
|
+
/**
|
|
291
|
+
* Scan installed extensions to find which ones depend on the target ID.
|
|
292
|
+
* Used for dependency warning on uninstall (D-06).
|
|
293
|
+
*/
|
|
294
|
+
function findDependents(targetId, installedExtDir) {
|
|
295
|
+
const dependents = [];
|
|
296
|
+
if (!existsSync(installedExtDir))
|
|
297
|
+
return dependents;
|
|
298
|
+
for (const entry of readdirSync(installedExtDir, { withFileTypes: true })) {
|
|
299
|
+
if (!entry.isDirectory())
|
|
300
|
+
continue;
|
|
301
|
+
const manifest = readManifest(join(installedExtDir, entry.name));
|
|
302
|
+
if (!manifest)
|
|
303
|
+
continue;
|
|
304
|
+
if (manifest.dependencies?.extensions?.includes(targetId)) {
|
|
305
|
+
dependents.push(manifest.id);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
return dependents;
|
|
309
|
+
}
|
|
310
|
+
function handleUninstall(id, ctx) {
|
|
311
|
+
if (!id) {
|
|
312
|
+
ctx.ui.notify("Usage: /gsd extensions uninstall <id>", "warning");
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
// Hold the registry lock for the entire uninstall transaction so a concurrent
|
|
316
|
+
// install can't add or re-enable `id` while we're in the middle of removing it.
|
|
317
|
+
const result = withRegistryLock((registry) => {
|
|
318
|
+
const entry = registry.entries[id];
|
|
319
|
+
// Check if extension exists and is user-installed
|
|
320
|
+
if (!entry || entry.source !== "user") {
|
|
321
|
+
return { ok: false, reason: "not-found" };
|
|
322
|
+
}
|
|
323
|
+
const installedExtDir = getInstalledExtDir();
|
|
324
|
+
const extDir = join(installedExtDir, id);
|
|
325
|
+
// Check for dependents and warn (D-06: warn-then-proceed)
|
|
326
|
+
const dependents = findDependents(id, installedExtDir);
|
|
327
|
+
// Remove directory first, then registry entry (Pitfall 4 from RESEARCH.md)
|
|
328
|
+
// If rm fails, do NOT remove registry entry — leaves a recoverable state
|
|
329
|
+
try {
|
|
330
|
+
if (existsSync(extDir)) {
|
|
331
|
+
rmSync(extDir, { recursive: true, force: true });
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
catch (err) {
|
|
335
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
336
|
+
return { ok: false, reason: "rm-failed", msg };
|
|
337
|
+
}
|
|
338
|
+
// Remove registry entry (D-07)
|
|
339
|
+
delete registry.entries[id];
|
|
340
|
+
return { ok: true, dependents };
|
|
341
|
+
});
|
|
342
|
+
if (!result.ok) {
|
|
343
|
+
if (result.reason === "not-found") {
|
|
344
|
+
ctx.ui.notify(`Extension "${id}" not found in registry. Run /gsd extensions list to see installed extensions.`, "warning");
|
|
345
|
+
}
|
|
346
|
+
else if (result.reason === "rm-failed") {
|
|
347
|
+
ctx.ui.notify(`Failed to remove extension directory for "${id}": ${result.msg}`, "error");
|
|
348
|
+
}
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
if (result.dependents.length > 0) {
|
|
352
|
+
ctx.ui.notify(`Warning: the following installed extensions depend on "${id}": ${result.dependents.join(", ")}. Removed anyway.`, "warning");
|
|
353
|
+
}
|
|
354
|
+
ctx.ui.notify(`Uninstalled "${id}". Restart GSD to deactivate.`, "info");
|
|
355
|
+
}
|
|
356
|
+
// ─── Update subcommand ───────────────────────────────────────────────────────
|
|
357
|
+
async function getLatestNpmVersion(packageName) {
|
|
358
|
+
try {
|
|
359
|
+
const res = await fetch(`https://registry.npmjs.org/${packageName}/latest`, {
|
|
360
|
+
signal: AbortSignal.timeout(5000),
|
|
361
|
+
});
|
|
362
|
+
if (!res.ok)
|
|
363
|
+
return null;
|
|
364
|
+
const data = await res.json();
|
|
365
|
+
return data.version ?? null;
|
|
366
|
+
}
|
|
367
|
+
catch {
|
|
368
|
+
return null;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
async function handleUpdate(id, ctx) {
|
|
372
|
+
const registry = loadRegistry();
|
|
373
|
+
if (id) {
|
|
374
|
+
// Update single extension (D-12)
|
|
375
|
+
await updateSingleExtension(id, registry, ctx);
|
|
376
|
+
}
|
|
377
|
+
else {
|
|
378
|
+
// Update all installed extensions (D-11)
|
|
379
|
+
await updateAllExtensions(registry, ctx);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
async function updateSingleExtension(id, registry, ctx) {
|
|
383
|
+
const entry = registry.entries[id];
|
|
384
|
+
if (!entry || entry.source !== "user") {
|
|
385
|
+
ctx.ui.notify(`Extension "${id}" not found in registry. Run /gsd extensions list to see installed extensions.`, "warning");
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
// Git and local installs: "reinstall to update" hint (D-10, D-12)
|
|
389
|
+
if (entry.installType !== "npm") {
|
|
390
|
+
const source = entry.installType ?? "unknown";
|
|
391
|
+
const hint = entry.installedFrom ? `gsd extensions install ${entry.installedFrom}` : `gsd extensions install <specifier>`;
|
|
392
|
+
ctx.ui.notify(`"${id}" was installed from ${source}. Reinstall to update: ${hint}`, "warning");
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
// npm extension: check for newer version (D-09)
|
|
396
|
+
const current = entry.version ?? "0.0.0";
|
|
397
|
+
const specifier = entry.installedFrom;
|
|
398
|
+
if (!specifier) {
|
|
399
|
+
ctx.ui.notify(`"${id}" has no recorded install source. Reinstall manually.`, "warning");
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
// Split npm specifier into name + optional pin.
|
|
403
|
+
// Scoped (`@scope/name[@version]`) vs unscoped (`name[@version]`).
|
|
404
|
+
const { name: packageName, pin } = parseNpmSpecifier(specifier);
|
|
405
|
+
// Pinned installs: the user explicitly requested a specific version. Don't
|
|
406
|
+
// silently upgrade past the pin — tell them to re-install with a new pin.
|
|
407
|
+
if (pin) {
|
|
408
|
+
ctx.ui.notify(`"${id}" was installed with a pinned version (${pin}). To update, run: gsd extensions install ${packageName}@<new-version>`, "info");
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
const latest = await getLatestNpmVersion(packageName);
|
|
412
|
+
if (!latest) {
|
|
413
|
+
ctx.ui.notify(`Could not fetch latest version for "${id}".`, "warning");
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
if (semver.gt(latest, current)) {
|
|
417
|
+
ctx.ui.notify(`Updating "${id}": v${current} → v${latest}...`, "info");
|
|
418
|
+
await handleInstall(packageName, ctx);
|
|
419
|
+
}
|
|
420
|
+
else {
|
|
421
|
+
ctx.ui.notify(`"${id}" is already at the latest version (v${current}).`, "info");
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* Parse an npm specifier into its package name and optional version pin.
|
|
426
|
+
* Handles scoped (`@scope/name[@version]`) and unscoped (`name[@version]`).
|
|
427
|
+
*/
|
|
428
|
+
function parseNpmSpecifier(specifier) {
|
|
429
|
+
const isScoped = specifier.startsWith("@");
|
|
430
|
+
const searchFrom = isScoped ? specifier.indexOf("/") + 1 : 0;
|
|
431
|
+
const atIdx = specifier.indexOf("@", searchFrom);
|
|
432
|
+
if (atIdx === -1)
|
|
433
|
+
return { name: specifier, pin: null };
|
|
434
|
+
return { name: specifier.slice(0, atIdx), pin: specifier.slice(atIdx + 1) };
|
|
435
|
+
}
|
|
436
|
+
async function updateAllExtensions(registry, ctx) {
|
|
437
|
+
// Find all user-installed extensions
|
|
438
|
+
const userEntries = Object.values(registry.entries).filter(e => e.source === "user");
|
|
439
|
+
if (userEntries.length === 0) {
|
|
440
|
+
ctx.ui.notify("No user-installed extensions found. Use: gsd extensions install <package> to add one.", "warning");
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
ctx.ui.notify(`Checking ${userEntries.length} installed extension(s) for updates...`, "info");
|
|
444
|
+
let updated = 0;
|
|
445
|
+
let skipped = 0;
|
|
446
|
+
for (const entry of userEntries) {
|
|
447
|
+
// Skip non-npm installs (D-11)
|
|
448
|
+
if (entry.installType !== "npm") {
|
|
449
|
+
const source = entry.installType ?? "unknown";
|
|
450
|
+
ctx.ui.notify(` ${entry.id}: installed from ${source} — reinstall to update`, "info");
|
|
451
|
+
skipped++;
|
|
452
|
+
continue;
|
|
453
|
+
}
|
|
454
|
+
const current = entry.version ?? "0.0.0";
|
|
455
|
+
const packageName = entry.installedFrom;
|
|
456
|
+
if (!packageName) {
|
|
457
|
+
ctx.ui.notify(` ${entry.id}: no recorded install source — skip`, "info");
|
|
458
|
+
skipped++;
|
|
459
|
+
continue;
|
|
460
|
+
}
|
|
461
|
+
const latest = await getLatestNpmVersion(packageName);
|
|
462
|
+
if (!latest) {
|
|
463
|
+
ctx.ui.notify(` ${entry.id}: could not fetch latest version — skip`, "info");
|
|
464
|
+
skipped++;
|
|
465
|
+
continue;
|
|
466
|
+
}
|
|
467
|
+
if (semver.gt(latest, current)) {
|
|
468
|
+
ctx.ui.notify(` ${entry.id}: v${current} → v${latest} (updating)`, "info");
|
|
469
|
+
await handleInstall(packageName, ctx);
|
|
470
|
+
updated++;
|
|
471
|
+
}
|
|
472
|
+
else {
|
|
473
|
+
ctx.ui.notify(` ${entry.id}: v${current} (already up to date)`, "info");
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
ctx.ui.notify(`Updated ${updated} extension(s). ${skipped} skipped (git/local — reinstall to update).`, "info");
|
|
477
|
+
}
|
|
478
|
+
// ─── Install subcommand ──────────────────────────────────────────────────────
|
|
479
|
+
async function handleInstall(specifier, ctx) {
|
|
480
|
+
if (!specifier) {
|
|
481
|
+
ctx.ui.notify("Usage: /gsd extensions install <npm-package|git-url|local-path>", "warning");
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
const installType = detectInstallType(specifier);
|
|
485
|
+
const installedExtDir = getInstalledExtDir();
|
|
486
|
+
mkdirSync(installedExtDir, { recursive: true });
|
|
487
|
+
process.stderr.write(`Installing ${specifier}...\n`);
|
|
488
|
+
if (installType === "npm") {
|
|
489
|
+
installFromNpm(specifier, installedExtDir, ctx);
|
|
490
|
+
}
|
|
491
|
+
else if (installType === "git") {
|
|
492
|
+
installFromGit(specifier, installedExtDir, ctx);
|
|
493
|
+
}
|
|
494
|
+
else {
|
|
495
|
+
installFromLocal(specifier, installedExtDir, ctx);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
function installFromNpm(specifier, installedExtDir, ctx) {
|
|
499
|
+
// packDir holds the tarball in tmpdir(). The *extractDir* is staged inside
|
|
500
|
+
// installedExtDir so the final renameSync to destPath stays on a single
|
|
501
|
+
// filesystem (avoids EXDEV when tmpdir() and ~/.gsd live on different mounts).
|
|
502
|
+
const packDir = mkdtempSync(join(tmpdir(), "gsd-install-"));
|
|
503
|
+
let extractDir = null;
|
|
504
|
+
try {
|
|
505
|
+
// Step 1: npm pack to tmpdir (D-01, D-05)
|
|
506
|
+
execFileSync("npm", ["pack", specifier, "--pack-destination", packDir, "--ignore-scripts"], {
|
|
507
|
+
stdio: "pipe",
|
|
508
|
+
encoding: "utf-8",
|
|
509
|
+
});
|
|
510
|
+
// Step 2: Find the tarball
|
|
511
|
+
const tgzFile = readdirSync(packDir).find(f => f.endsWith(".tgz"));
|
|
512
|
+
if (!tgzFile)
|
|
513
|
+
throw new Error("npm pack produced no tarball");
|
|
514
|
+
// Step 3: Extract via tar into a staging dir *inside* installedExtDir
|
|
515
|
+
extractDir = mkdtempSync(join(installedExtDir, "tmp-npm-"));
|
|
516
|
+
execFileSync("tar", ["xzf", join(packDir, tgzFile), "-C", extractDir, "--strip-components=1"], { stdio: "pipe" });
|
|
517
|
+
// Step 4: Validate and get extension ID
|
|
518
|
+
const validated = postInstallValidate(extractDir, specifier, ctx);
|
|
519
|
+
if (!validated) {
|
|
520
|
+
return; // Error already notified
|
|
521
|
+
}
|
|
522
|
+
// Step 5: Move to final destination — same filesystem as extractDir
|
|
523
|
+
const destPath = join(installedExtDir, validated.id);
|
|
524
|
+
if (existsSync(destPath)) {
|
|
525
|
+
rmSync(destPath, { recursive: true, force: true });
|
|
526
|
+
}
|
|
527
|
+
renameSync(extractDir, destPath);
|
|
528
|
+
extractDir = null; // Successfully moved; skip cleanup
|
|
529
|
+
// Step 6: Commit the registry entry only after the rename succeeds.
|
|
530
|
+
writeInstalledRegistryEntry(validated.id, validated.manifest, specifier, "npm");
|
|
531
|
+
ctx.ui.notify(`Installed "${validated.id}" v${validated.manifest.version ?? "unknown"}. Restart GSD to activate.`, "info");
|
|
532
|
+
}
|
|
533
|
+
catch (err) {
|
|
534
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
535
|
+
ctx.ui.notify(`Failed to install "${specifier}": ${msg}`, "error");
|
|
536
|
+
}
|
|
537
|
+
finally {
|
|
538
|
+
if (extractDir && existsSync(extractDir)) {
|
|
539
|
+
try {
|
|
540
|
+
rmSync(extractDir, { recursive: true, force: true });
|
|
541
|
+
}
|
|
542
|
+
catch { /* best-effort */ }
|
|
543
|
+
}
|
|
544
|
+
rmSync(packDir, { recursive: true, force: true });
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
function installFromGit(gitUrl, installedExtDir, ctx) {
|
|
548
|
+
// Clone into temp dir, validate, then rename to real ID (D-02)
|
|
549
|
+
const tmpDir = join(installedExtDir, `__installing-${Date.now()}`);
|
|
550
|
+
try {
|
|
551
|
+
execFileSync("git", ["clone", "--depth=1", gitUrl, tmpDir], { stdio: "pipe" });
|
|
552
|
+
// Remove .git directory — not needed after clone
|
|
553
|
+
const dotGit = join(tmpDir, ".git");
|
|
554
|
+
if (existsSync(dotGit)) {
|
|
555
|
+
rmSync(dotGit, { recursive: true, force: true });
|
|
556
|
+
}
|
|
557
|
+
const validated = postInstallValidate(tmpDir, gitUrl, ctx);
|
|
558
|
+
if (!validated) {
|
|
559
|
+
rmSync(tmpDir, { recursive: true, force: true });
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
const destPath = join(installedExtDir, validated.id);
|
|
563
|
+
if (existsSync(destPath)) {
|
|
564
|
+
rmSync(destPath, { recursive: true, force: true });
|
|
565
|
+
}
|
|
566
|
+
renameSync(tmpDir, destPath);
|
|
567
|
+
writeInstalledRegistryEntry(validated.id, validated.manifest, gitUrl, "git");
|
|
568
|
+
ctx.ui.notify(`Installed "${validated.id}" v${validated.manifest.version ?? "unknown"}. Restart GSD to activate.`, "info");
|
|
569
|
+
}
|
|
570
|
+
catch (err) {
|
|
571
|
+
if (existsSync(tmpDir))
|
|
572
|
+
rmSync(tmpDir, { recursive: true, force: true });
|
|
573
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
574
|
+
ctx.ui.notify(`Failed to install "${gitUrl}": ${msg}`, "error");
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
function installFromLocal(localPath, installedExtDir, ctx) {
|
|
578
|
+
// Resolve path and copy (not symlink) per D-03
|
|
579
|
+
const sourcePath = resolve(localPath.startsWith("~/") ? join(homedir(), localPath.slice(2)) : localPath);
|
|
580
|
+
if (!existsSync(sourcePath)) {
|
|
581
|
+
ctx.ui.notify(`Cannot install "${localPath}": path does not exist.`, "error");
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
584
|
+
// Copy to temp dir first, validate, then rename
|
|
585
|
+
const tmpDir = join(installedExtDir, `__installing-${Date.now()}`);
|
|
586
|
+
try {
|
|
587
|
+
cpSync(sourcePath, tmpDir, { recursive: true });
|
|
588
|
+
const validated = postInstallValidate(tmpDir, localPath, ctx);
|
|
589
|
+
if (!validated) {
|
|
590
|
+
rmSync(tmpDir, { recursive: true, force: true });
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
const destPath = join(installedExtDir, validated.id);
|
|
594
|
+
if (existsSync(destPath)) {
|
|
595
|
+
rmSync(destPath, { recursive: true, force: true });
|
|
596
|
+
}
|
|
597
|
+
renameSync(tmpDir, destPath);
|
|
598
|
+
writeInstalledRegistryEntry(validated.id, validated.manifest, localPath, "local");
|
|
599
|
+
ctx.ui.notify(`Installed "${validated.id}" v${validated.manifest.version ?? "unknown"}. Restart GSD to activate.`, "info");
|
|
600
|
+
}
|
|
601
|
+
catch (err) {
|
|
602
|
+
if (existsSync(tmpDir))
|
|
603
|
+
rmSync(tmpDir, { recursive: true, force: true });
|
|
604
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
605
|
+
ctx.ui.notify(`Failed to install "${localPath}": ${msg}`, "error");
|
|
606
|
+
}
|
|
607
|
+
}
|
|
79
608
|
// ─── Command Handler ────────────────────────────────────────────────────────
|
|
80
609
|
export async function handleExtensions(args, ctx) {
|
|
81
610
|
const parts = args.split(/\s+/).filter(Boolean);
|
|
@@ -96,7 +625,23 @@ export async function handleExtensions(args, ctx) {
|
|
|
96
625
|
handleInfo(parts[1], ctx);
|
|
97
626
|
return;
|
|
98
627
|
}
|
|
99
|
-
|
|
628
|
+
if (subCmd === "install") {
|
|
629
|
+
await handleInstall(parts[1], ctx);
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
if (subCmd === "uninstall") {
|
|
633
|
+
handleUninstall(parts[1], ctx);
|
|
634
|
+
return;
|
|
635
|
+
}
|
|
636
|
+
if (subCmd === "update") {
|
|
637
|
+
await handleUpdate(parts[1], ctx);
|
|
638
|
+
return;
|
|
639
|
+
}
|
|
640
|
+
if (subCmd === "validate") {
|
|
641
|
+
handleValidate(parts[1], ctx);
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
ctx.ui.notify(`Unknown: /gsd extensions ${subCmd}. Usage: /gsd extensions [list|enable|disable|info|install|uninstall|update|validate]`, "warning");
|
|
100
645
|
}
|
|
101
646
|
function handleList(ctx) {
|
|
102
647
|
const manifests = discoverManifests();
|
|
@@ -128,6 +673,18 @@ function handleList(ctx) {
|
|
|
128
673
|
padRight(m.tier, 10) +
|
|
129
674
|
padRight(String(toolCount), 7) +
|
|
130
675
|
String(cmdCount));
|
|
676
|
+
// Show source indicator and install info for user-installed extensions
|
|
677
|
+
const regEntry = registry.entries[m.id];
|
|
678
|
+
if (regEntry?.source === "user") {
|
|
679
|
+
// Append [user] tag to the last line
|
|
680
|
+
const lastLine = lines[lines.length - 1];
|
|
681
|
+
lines[lines.length - 1] = lastLine + " [user]";
|
|
682
|
+
if (regEntry.installedFrom) {
|
|
683
|
+
const typePrefix = regEntry.installType ? `${regEntry.installType}:` : "";
|
|
684
|
+
const versionSuffix = regEntry.version ? `@${regEntry.version}` : "";
|
|
685
|
+
lines.push(` installed from: ${typePrefix}${regEntry.installedFrom}${versionSuffix}`);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
131
688
|
if (!enabled) {
|
|
132
689
|
lines.push(` ↳ gsd extensions enable ${m.id}`);
|
|
133
690
|
}
|
|
@@ -144,21 +701,24 @@ function handleEnable(id, ctx) {
|
|
|
144
701
|
ctx.ui.notify(`Extension "${id}" not found. Run /gsd extensions list to see available extensions.`, "warning");
|
|
145
702
|
return;
|
|
146
703
|
}
|
|
147
|
-
const
|
|
148
|
-
|
|
704
|
+
const alreadyEnabled = withRegistryLock((registry) => {
|
|
705
|
+
if (isEnabled(registry, id))
|
|
706
|
+
return true;
|
|
707
|
+
const entry = registry.entries[id];
|
|
708
|
+
if (entry) {
|
|
709
|
+
entry.enabled = true;
|
|
710
|
+
delete entry.disabledAt;
|
|
711
|
+
delete entry.disabledReason;
|
|
712
|
+
}
|
|
713
|
+
else {
|
|
714
|
+
registry.entries[id] = { id, enabled: true, source: "bundled" };
|
|
715
|
+
}
|
|
716
|
+
return false;
|
|
717
|
+
});
|
|
718
|
+
if (alreadyEnabled) {
|
|
149
719
|
ctx.ui.notify(`Extension "${id}" is already enabled.`, "info");
|
|
150
720
|
return;
|
|
151
721
|
}
|
|
152
|
-
const entry = registry.entries[id];
|
|
153
|
-
if (entry) {
|
|
154
|
-
entry.enabled = true;
|
|
155
|
-
delete entry.disabledAt;
|
|
156
|
-
delete entry.disabledReason;
|
|
157
|
-
}
|
|
158
|
-
else {
|
|
159
|
-
registry.entries[id] = { id, enabled: true, source: "bundled" };
|
|
160
|
-
}
|
|
161
|
-
saveRegistry(registry);
|
|
162
722
|
ctx.ui.notify(`Enabled "${id}". Restart GSD to activate.`, "info");
|
|
163
723
|
}
|
|
164
724
|
function handleDisable(id, reason, ctx) {
|
|
@@ -176,27 +736,30 @@ function handleDisable(id, reason, ctx) {
|
|
|
176
736
|
ctx.ui.notify(`Cannot disable "${id}" — it is a core extension.`, "warning");
|
|
177
737
|
return;
|
|
178
738
|
}
|
|
179
|
-
const
|
|
180
|
-
|
|
739
|
+
const alreadyDisabled = withRegistryLock((registry) => {
|
|
740
|
+
if (!isEnabled(registry, id))
|
|
741
|
+
return true;
|
|
742
|
+
const entry = registry.entries[id];
|
|
743
|
+
if (entry) {
|
|
744
|
+
entry.enabled = false;
|
|
745
|
+
entry.disabledAt = new Date().toISOString();
|
|
746
|
+
entry.disabledReason = reason || undefined;
|
|
747
|
+
}
|
|
748
|
+
else {
|
|
749
|
+
registry.entries[id] = {
|
|
750
|
+
id,
|
|
751
|
+
enabled: false,
|
|
752
|
+
source: "bundled",
|
|
753
|
+
disabledAt: new Date().toISOString(),
|
|
754
|
+
disabledReason: reason || undefined,
|
|
755
|
+
};
|
|
756
|
+
}
|
|
757
|
+
return false;
|
|
758
|
+
});
|
|
759
|
+
if (alreadyDisabled) {
|
|
181
760
|
ctx.ui.notify(`Extension "${id}" is already disabled.`, "info");
|
|
182
761
|
return;
|
|
183
762
|
}
|
|
184
|
-
const entry = registry.entries[id];
|
|
185
|
-
if (entry) {
|
|
186
|
-
entry.enabled = false;
|
|
187
|
-
entry.disabledAt = new Date().toISOString();
|
|
188
|
-
entry.disabledReason = reason || undefined;
|
|
189
|
-
}
|
|
190
|
-
else {
|
|
191
|
-
registry.entries[id] = {
|
|
192
|
-
id,
|
|
193
|
-
enabled: false,
|
|
194
|
-
source: "bundled",
|
|
195
|
-
disabledAt: new Date().toISOString(),
|
|
196
|
-
disabledReason: reason || undefined,
|
|
197
|
-
};
|
|
198
|
-
}
|
|
199
|
-
saveRegistry(registry);
|
|
200
763
|
ctx.ui.notify(`Disabled "${id}". Restart GSD to deactivate.`, "info");
|
|
201
764
|
}
|
|
202
765
|
function handleInfo(id, ctx) {
|
|
@@ -227,6 +790,15 @@ function handleInfo(id, ctx) {
|
|
|
227
790
|
if (entry?.disabledReason) {
|
|
228
791
|
lines.push(` Reason: ${entry.disabledReason}`);
|
|
229
792
|
}
|
|
793
|
+
// Phase 8 fields for user-installed extensions (per UI-SPEC)
|
|
794
|
+
if (entry?.source === "user") {
|
|
795
|
+
if (entry.installedFrom) {
|
|
796
|
+
lines.push(` Installed from: ${entry.installedFrom}`);
|
|
797
|
+
}
|
|
798
|
+
if (entry.installType) {
|
|
799
|
+
lines.push(` Install type: ${entry.installType}`);
|
|
800
|
+
}
|
|
801
|
+
}
|
|
230
802
|
if (manifest.provides) {
|
|
231
803
|
lines.push("");
|
|
232
804
|
lines.push(" Provides:");
|
|
@@ -255,6 +827,25 @@ function handleInfo(id, ctx) {
|
|
|
255
827
|
}
|
|
256
828
|
ctx.ui.notify(lines.join("\n"), "info");
|
|
257
829
|
}
|
|
830
|
+
function handleValidate(path, ctx) {
|
|
831
|
+
if (!path) {
|
|
832
|
+
ctx.ui.notify("Usage: /gsd extensions validate <path>", "warning");
|
|
833
|
+
return;
|
|
834
|
+
}
|
|
835
|
+
const resolved = resolve(path);
|
|
836
|
+
if (!existsSync(resolved)) {
|
|
837
|
+
ctx.ui.notify(`Path not found: ${resolved}`, "warning");
|
|
838
|
+
return;
|
|
839
|
+
}
|
|
840
|
+
const result = validateExtensionPackage(resolved);
|
|
841
|
+
if (result.valid) {
|
|
842
|
+
ctx.ui.notify(`Valid extension package: ${resolved}`, "info");
|
|
843
|
+
}
|
|
844
|
+
else {
|
|
845
|
+
ctx.ui.notify(`Invalid extension package: ${resolved}\n` +
|
|
846
|
+
result.errors.map(e => ` - ${e}`).join("\n"), "warning");
|
|
847
|
+
}
|
|
848
|
+
}
|
|
258
849
|
function padRight(str, len) {
|
|
259
850
|
return str.length >= len ? str + " " : str + " ".repeat(len - str.length);
|
|
260
851
|
}
|