@baique/pi-web-sky 0.1.35 → 0.1.37
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/.next/BUILD_ID +1 -1
- package/.next/app-path-routes-manifest.json +11 -11
- package/.next/build-manifest.json +4 -4
- package/.next/next-server.js.nft.json +1 -1
- package/.next/prerender-manifest.json +3 -3
- package/.next/react-loadable-manifest.json +2 -2
- package/.next/required-server-files.js +6 -6
- package/.next/required-server-files.json +6 -6
- package/.next/server/app/_global-error/page.js +2 -2
- package/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_global-error.html +1 -1
- package/.next/server/app/_global-error.rsc +1 -1
- package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/_not-found/page.js +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_not-found.html +1 -1
- package/.next/server/app/_not-found.rsc +3 -3
- package/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +3 -3
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/api/agent/[id]/bash-output/route.js +2 -2
- package/.next/server/app/api/agent/[id]/bash-output/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/agent/[id]/events/route.js +3 -3
- package/.next/server/app/api/agent/[id]/events/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/agent/[id]/route.js +2 -2
- package/.next/server/app/api/agent/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/agent/new/route.js +1 -1
- package/.next/server/app/api/agent/new/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/agent/running/route.js +1 -1
- package/.next/server/app/api/agent/running/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/app-update/route.js +1 -1
- package/.next/server/app/api/app-update/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/all-providers/route.js +1 -1
- package/.next/server/app/api/auth/all-providers/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/api-key/[provider]/route.js +1 -1
- package/.next/server/app/api/auth/api-key/[provider]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/login/[provider]/route.js +3 -3
- package/.next/server/app/api/auth/login/[provider]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/logout/[provider]/route.js +1 -1
- package/.next/server/app/api/auth/logout/[provider]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/providers/route.js +1 -1
- package/.next/server/app/api/auth/providers/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/boards/[id]/canvas/route.js +1 -1
- package/.next/server/app/api/boards/[id]/canvas/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/boards/[id]/edges/[eid]/route.js +1 -1
- package/.next/server/app/api/boards/[id]/edges/[eid]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/boards/[id]/edges/route.js +1 -1
- package/.next/server/app/api/boards/[id]/edges/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/boards/[id]/nodes/[nid]/route.js +1 -1
- package/.next/server/app/api/boards/[id]/nodes/[nid]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/boards/[id]/nodes/route.js +1 -1
- package/.next/server/app/api/boards/[id]/nodes/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/boards/[id]/reconcile-task-edges/route.js +1 -1
- package/.next/server/app/api/boards/[id]/reconcile-task-edges/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/boards/[id]/route.js +1 -1
- package/.next/server/app/api/boards/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/boards/purge-orphans/route.js +1 -1
- package/.next/server/app/api/boards/purge-orphans/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/boards/reorder/route.js +1 -1
- package/.next/server/app/api/boards/reorder/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/boards/route.js +1 -1
- package/.next/server/app/api/boards/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/cwd/browse/route.js +1 -1
- package/.next/server/app/api/cwd/browse/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/cwd/validate/route.js +2 -2
- package/.next/server/app/api/cwd/validate/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/default-cwd/route.js +2 -2
- package/.next/server/app/api/default-cwd/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/file-index/route.js +2 -2
- package/.next/server/app/api/file-index/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/files/[...path]/route.js +2 -2
- package/.next/server/app/api/files/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/git/diff/route.js +1 -1
- package/.next/server/app/api/git/diff/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/git/status/route.js +1 -1
- package/.next/server/app/api/git/status/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/home/route.js +1 -1
- package/.next/server/app/api/home/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/mcp/route.js +2 -2
- package/.next/server/app/api/mcp/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/models/route.js +2 -2
- package/.next/server/app/api/models/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/models-config/catalog/route.js +1 -1
- package/.next/server/app/api/models-config/catalog/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/models-config/discover/route.js +1 -1
- package/.next/server/app/api/models-config/discover/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/models-config/route.js +1 -1
- package/.next/server/app/api/models-config/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/models-config/test/route.js +1 -1
- package/.next/server/app/api/models-config/test/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/plugins/route.js +2 -2
- package/.next/server/app/api/plugins/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/project-trust/route.js +2 -2
- package/.next/server/app/api/project-trust/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/quota/route.js +1 -1
- package/.next/server/app/api/quota/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/search/route.js +2 -2
- package/.next/server/app/api/search/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/sessions/[id]/auto-name/route.js +3 -3
- package/.next/server/app/api/sessions/[id]/auto-name/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/sessions/[id]/context/route.js +2 -2
- package/.next/server/app/api/sessions/[id]/context/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/sessions/[id]/entries/[entryId]/thinking/route.js +2 -2
- package/.next/server/app/api/sessions/[id]/entries/[entryId]/thinking/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/sessions/[id]/export/route.js +2 -2
- package/.next/server/app/api/sessions/[id]/export/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/sessions/[id]/route.js +1 -1
- package/.next/server/app/api/sessions/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/sessions/[id]/state/route.js +2 -2
- package/.next/server/app/api/sessions/[id]/state/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/sessions/[id]/todos/route.js +2 -2
- package/.next/server/app/api/sessions/[id]/todos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/sessions/route.js +2 -2
- package/.next/server/app/api/sessions/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/check/route.js +1 -1
- package/.next/server/app/api/skills/check/route.js.nft.json +1 -1
- package/.next/server/app/api/skills/check/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/install/route.js +2 -2
- package/.next/server/app/api/skills/install/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/route.js +2 -31
- package/.next/server/app/api/skills/route.js.nft.json +1 -1
- package/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/search/route.js +1 -1
- package/.next/server/app/api/skills/search/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/update/route.js +1 -1
- package/.next/server/app/api/skills/update/route.js.nft.json +1 -1
- package/.next/server/app/api/skills/update/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/task-card-questions/[id]/answer/route.js +1 -1
- package/.next/server/app/api/task-card-questions/[id]/answer/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/task-card-questions/route.js +1 -1
- package/.next/server/app/api/task-card-questions/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/task-cards/[id]/route.js +1 -1
- package/.next/server/app/api/task-cards/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/task-cards/route.js +1 -1
- package/.next/server/app/api/task-cards/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/task-scheduler/status/route.js +1 -1
- package/.next/server/app/api/task-scheduler/status/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/tasks/[id]/board/route.js +1 -1
- package/.next/server/app/api/tasks/[id]/board/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/tasks/[id]/route.js +1 -1
- package/.next/server/app/api/tasks/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/tasks/reorder/route.js +1 -1
- package/.next/server/app/api/tasks/reorder/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/tasks/route.js +1 -1
- package/.next/server/app/api/tasks/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/terminal/[id]/events/route.js +3 -3
- package/.next/server/app/api/terminal/[id]/events/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/terminal/[id]/route.js +2 -2
- package/.next/server/app/api/terminal/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/terminal/route.js +2 -2
- package/.next/server/app/api/terminal/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/route.js +2 -2
- package/.next/server/app/api/worktrees/route_client-reference-manifest.js +1 -1
- package/.next/server/app/favicon.ico/route.js +1 -1
- package/.next/server/app/index.html +1 -1
- package/.next/server/app/index.rsc +5 -5
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +5 -5
- package/.next/server/app/index.segments/_full.segment.rsc +5 -5
- package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/manifest.webmanifest/route.js +2 -2
- package/.next/server/app/manifest.webmanifest/route_client-reference-manifest.js +1 -1
- package/.next/server/app/page.js +3 -3
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app-paths-manifest.json +11 -11
- package/.next/server/chunks/2404.js +2 -2
- package/.next/server/chunks/2430.js +4 -4
- package/.next/server/chunks/3500.js +30 -0
- package/.next/server/chunks/4226.js +2 -2
- package/.next/server/chunks/542.js +11 -6
- package/.next/server/chunks/5794.js +2 -2
- package/.next/server/chunks/6429.js +1 -1
- package/.next/server/chunks/898.js +2 -2
- package/.next/server/instrumentation.js +1 -1
- package/.next/server/middleware-build-manifest.js +1 -1
- package/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/.next/server/middleware.js +3 -3
- package/.next/server/next-font-manifest.js +1 -1
- package/.next/server/next-font-manifest.json +1 -1
- package/.next/server/pages/404.html +1 -1
- package/.next/server/pages/500.html +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/chunks/5315.dba7515ae6e139ae.js +4 -0
- package/.next/static/chunks/{7999.f4ab221861378788.js → 7999.879d5a20e8641752.js} +1 -1
- package/.next/static/chunks/app/_not-found/{page-af4fe23994cb3d66.js → page-8cea8d8de35f4da4.js} +1 -1
- package/.next/static/chunks/app/{layout-1d9a7a44fbc92d2e.js → layout-c4ca28ccc08bc975.js} +1 -1
- package/.next/static/chunks/app/{page-6d6779a81a0a7639.js → page-c9ead38525a85686.js} +3 -3
- package/.next/static/chunks/{main-app-b3e8a8d15d83e1f0.js → main-app-dd2385a1a5a588df.js} +1 -1
- package/.next/static/chunks/next/dist/client/components/builtin/global-error-be943dcb89867ab9.js +1 -0
- package/.next/static/chunks/{webpack-fa4f40085b1ec1ac.js → webpack-4bbf04891e910713.js} +1 -1
- package/.next/static/css/422c7dc0faa04d89.css +1 -0
- package/.next/trace +8 -9
- package/.next/trace-build +1 -1
- package/.next/types/app/api/agent/[id]/bash-output/route.ts +1 -1
- package/.next/types/app/api/agent/[id]/events/route.ts +1 -1
- package/.next/types/app/api/agent/[id]/route.ts +1 -1
- package/.next/types/app/api/agent/new/route.ts +1 -1
- package/.next/types/app/api/agent/running/route.ts +1 -1
- package/.next/types/app/api/app-update/route.ts +1 -1
- package/.next/types/app/api/auth/all-providers/route.ts +1 -1
- package/.next/types/app/api/auth/api-key/[provider]/route.ts +1 -1
- package/.next/types/app/api/auth/login/[provider]/route.ts +1 -1
- package/.next/types/app/api/auth/logout/[provider]/route.ts +1 -1
- package/.next/types/app/api/auth/providers/route.ts +1 -1
- package/.next/types/app/api/boards/[id]/canvas/route.ts +1 -1
- package/.next/types/app/api/boards/[id]/edges/[eid]/route.ts +1 -1
- package/.next/types/app/api/boards/[id]/edges/route.ts +1 -1
- package/.next/types/app/api/boards/[id]/nodes/[nid]/route.ts +1 -1
- package/.next/types/app/api/boards/[id]/nodes/route.ts +1 -1
- package/.next/types/app/api/boards/[id]/reconcile-task-edges/route.ts +1 -1
- package/.next/types/app/api/boards/[id]/route.ts +1 -1
- package/.next/types/app/api/boards/purge-orphans/route.ts +1 -1
- package/.next/types/app/api/boards/reorder/route.ts +1 -1
- package/.next/types/app/api/boards/route.ts +1 -1
- package/.next/types/app/api/cwd/browse/route.ts +1 -1
- package/.next/types/app/api/cwd/validate/route.ts +1 -1
- package/.next/types/app/api/default-cwd/route.ts +1 -1
- package/.next/types/app/api/file-index/route.ts +1 -1
- package/.next/types/app/api/files/[...path]/route.ts +1 -1
- package/.next/types/app/api/git/diff/route.ts +1 -1
- package/.next/types/app/api/git/status/route.ts +1 -1
- package/.next/types/app/api/home/route.ts +1 -1
- package/.next/types/app/api/mcp/route.ts +1 -1
- package/.next/types/app/api/models/route.ts +1 -1
- package/.next/types/app/api/models-config/catalog/route.ts +1 -1
- package/.next/types/app/api/models-config/discover/route.ts +1 -1
- package/.next/types/app/api/models-config/route.ts +1 -1
- package/.next/types/app/api/models-config/test/route.ts +1 -1
- package/.next/types/app/api/plugins/route.ts +1 -1
- package/.next/types/app/api/project-trust/route.ts +1 -1
- package/.next/types/app/api/quota/route.ts +1 -1
- package/.next/types/app/api/search/route.ts +1 -1
- package/.next/types/app/api/sessions/[id]/auto-name/route.ts +1 -1
- package/.next/types/app/api/sessions/[id]/context/route.ts +1 -1
- package/.next/types/app/api/sessions/[id]/entries/[entryId]/thinking/route.ts +1 -1
- package/.next/types/app/api/sessions/[id]/export/route.ts +1 -1
- package/.next/types/app/api/sessions/[id]/route.ts +1 -1
- package/.next/types/app/api/sessions/[id]/state/route.ts +1 -1
- package/.next/types/app/api/sessions/[id]/todos/route.ts +1 -1
- package/.next/types/app/api/sessions/route.ts +1 -1
- package/.next/types/app/api/skills/check/route.ts +1 -1
- package/.next/types/app/api/skills/install/route.ts +1 -1
- package/.next/types/app/api/skills/route.ts +1 -1
- package/.next/types/app/api/skills/search/route.ts +1 -1
- package/.next/types/app/api/skills/update/route.ts +1 -1
- package/.next/types/app/api/task-card-questions/[id]/answer/route.ts +1 -1
- package/.next/types/app/api/task-card-questions/route.ts +1 -1
- package/.next/types/app/api/task-cards/[id]/route.ts +1 -1
- package/.next/types/app/api/task-cards/route.ts +1 -1
- package/.next/types/app/api/task-scheduler/status/route.ts +1 -1
- package/.next/types/app/api/tasks/[id]/board/route.ts +1 -1
- package/.next/types/app/api/tasks/[id]/route.ts +1 -1
- package/.next/types/app/api/tasks/reorder/route.ts +1 -1
- package/.next/types/app/api/tasks/route.ts +1 -1
- package/.next/types/app/api/terminal/[id]/events/route.ts +1 -1
- package/.next/types/app/api/terminal/[id]/route.ts +1 -1
- package/.next/types/app/api/terminal/route.ts +1 -1
- package/.next/types/app/api/worktrees/route.ts +1 -1
- package/.next/types/app/layout.ts +1 -1
- package/.next/types/app/page.ts +1 -1
- package/bin/pi-web-sky.js +15 -20
- package/lib/agent-client.test.mjs +64 -0
- package/lib/agent-client.ts +53 -0
- package/lib/agent-event-connection.test.mjs +193 -0
- package/lib/agent-event-connection.ts +202 -0
- package/lib/agent-event-stream.test.mjs +155 -0
- package/lib/agent-event-stream.ts +132 -0
- package/lib/agent-event-wire.test.mjs +278 -0
- package/lib/agent-event-wire.ts +107 -0
- package/lib/agent-phase.ts +24 -0
- package/lib/allowed-roots.ts +31 -0
- package/lib/ansi.test.mjs +52 -0
- package/lib/ansi.ts +211 -0
- package/lib/api-types.ts +139 -0
- package/lib/app-update.test.mjs +27 -0
- package/lib/app-update.ts +28 -0
- package/lib/atomic-file.test.mjs +37 -0
- package/lib/atomic-file.ts +34 -0
- package/lib/audit-session.ts +235 -0
- package/lib/bash-output.test.mjs +63 -0
- package/lib/bash-output.ts +53 -0
- package/lib/bg-image.ts +129 -0
- package/lib/board-events.ts +83 -0
- package/lib/board-purge.test.mjs +66 -0
- package/lib/board-purge.ts +65 -0
- package/lib/board-search.test.mjs +110 -0
- package/lib/board-search.ts +63 -0
- package/lib/board-store.test.mjs +542 -0
- package/lib/board-store.ts +794 -0
- package/lib/board-types.ts +96 -0
- package/lib/board-utils.ts +17 -0
- package/lib/bounded-form-data.test.mjs +54 -0
- package/lib/bounded-form-data.ts +49 -0
- package/lib/browser-notifications.test.mjs +167 -0
- package/lib/browser-notifications.ts +104 -0
- package/lib/chat-lazy-load.test.mjs +140 -0
- package/lib/chat-lazy-load.ts +64 -0
- package/lib/clipboard-paths.test.mjs +140 -0
- package/lib/clipboard-paths.ts +192 -0
- package/lib/clipboard.ts +18 -0
- package/lib/compaction-summary.test.mjs +44 -0
- package/lib/compaction-summary.ts +34 -0
- package/lib/custom-ui-terminal.test.mjs +32 -0
- package/lib/custom-ui-terminal.ts +27 -0
- package/lib/deepseek-pricing.test.mjs +120 -0
- package/lib/deepseek-pricing.ts +71 -0
- package/lib/directory-browser.test.mjs +60 -0
- package/lib/directory-browser.ts +83 -0
- package/lib/draft-stash.test.mjs +91 -0
- package/lib/draft-stash.ts +102 -0
- package/lib/draft-store.ts +105 -0
- package/lib/dropdown-direction.ts +27 -0
- package/lib/extension-command.test.mjs +63 -0
- package/lib/extension-command.ts +81 -0
- package/lib/file-access.test.mjs +31 -0
- package/lib/file-access.ts +59 -0
- package/lib/file-dirent.test.mjs +56 -0
- package/lib/file-dirent.ts +17 -0
- package/lib/file-explorer-state.test.mjs +63 -0
- package/lib/file-explorer-state.ts +36 -0
- package/lib/file-fuzzy.test.mjs +29 -0
- package/lib/file-fuzzy.ts +189 -0
- package/lib/file-links.test.mjs +92 -0
- package/lib/file-links.ts +148 -0
- package/lib/file-paths.ts +43 -0
- package/lib/file-types.test.mjs +34 -0
- package/lib/file-types.ts +73 -0
- package/lib/file-upload.test.mjs +70 -0
- package/lib/file-upload.ts +59 -0
- package/lib/file-viewer-state.test.mjs +20 -0
- package/lib/file-viewer-state.ts +15 -0
- package/lib/frontmatter.test.mjs +55 -0
- package/lib/frontmatter.ts +87 -0
- package/lib/git-changes.test.mjs +52 -0
- package/lib/git-changes.ts +232 -0
- package/lib/git-status.ts +47 -0
- package/lib/git-types.ts +29 -0
- package/lib/http-dispatcher.test.mjs +83 -0
- package/lib/http-dispatcher.ts +86 -0
- package/lib/i18n/format.test.mjs +19 -0
- package/lib/i18n/format.ts +60 -0
- package/lib/i18n/messages/en.ts +662 -0
- package/lib/i18n/messages/zh-CN.ts +661 -0
- package/lib/i18n/registry.test.mjs +32 -0
- package/lib/i18n/registry.ts +43 -0
- package/lib/i18n/types.ts +15 -0
- package/lib/image-attachments.test.mjs +27 -0
- package/lib/image-attachments.ts +56 -0
- package/lib/initial-navigation.test.mjs +65 -0
- package/lib/initial-navigation.ts +15 -0
- package/lib/markdown.test.mjs +104 -0
- package/lib/markdown.ts +347 -0
- package/lib/message-display.test.mjs +130 -0
- package/lib/message-display.ts +47 -0
- package/lib/model-catalog.test.mjs +327 -0
- package/lib/model-catalog.ts +414 -0
- package/lib/model-discovery-auth.ts +58 -0
- package/lib/model-discovery.test.mjs +50 -0
- package/lib/model-discovery.ts +75 -0
- package/lib/model-id-preference.ts +46 -0
- package/lib/model-scope.test.mjs +193 -0
- package/lib/model-scope.ts +198 -0
- package/lib/models-cache.test.mjs +122 -0
- package/lib/models-cache.ts +90 -0
- package/lib/models-config-store.test.mjs +200 -0
- package/lib/models-config-store.ts +84 -0
- package/lib/next-config-esm.test.mjs +14 -0
- package/lib/next-config.test.mjs +13 -0
- package/lib/node-version.test.mjs +33 -0
- package/lib/normalize.test.mjs +54 -0
- package/lib/normalize.ts +63 -0
- package/lib/npx.ts +62 -0
- package/lib/panel-layout.test.mjs +56 -0
- package/lib/panel-layout.ts +52 -0
- package/lib/patch.test.mjs +110 -0
- package/lib/patch.ts +134 -0
- package/lib/path-security.ts +42 -0
- package/lib/paths.test.mjs +58 -0
- package/lib/paths.ts +71 -0
- package/lib/pi-types.ts +183 -0
- package/lib/pi-web-options.test.mjs +63 -0
- package/lib/process-lifecycle.test.mjs +83 -0
- package/lib/project-command-env.test.mjs +298 -0
- package/lib/project-command-env.ts +129 -0
- package/lib/project-groups.test.mjs +60 -0
- package/lib/project-groups.ts +53 -0
- package/lib/project-identity.test.mjs +37 -0
- package/lib/project-identity.ts +26 -0
- package/lib/project-tree.test.mjs +115 -0
- package/lib/project-tree.ts +176 -0
- package/lib/project-trust.test.mjs +117 -0
- package/lib/project-trust.ts +48 -0
- package/lib/prompt-recovery.test.mjs +33 -0
- package/lib/prompt-recovery.ts +47 -0
- package/lib/provider-api-key-route.test.mjs +12 -0
- package/lib/provider-credential-store.test.mjs +126 -0
- package/lib/provider-credential-store.ts +114 -0
- package/lib/provider-listing-runtime.ts +37 -0
- package/lib/provider-listing.test.mjs +114 -0
- package/lib/provider-listing.ts +118 -0
- package/lib/provider-services.ts +64 -0
- package/lib/request-security.test.mjs +256 -0
- package/lib/request-security.ts +136 -0
- package/lib/rpc-manager-shutdown.test.mjs +372 -0
- package/lib/rpc-manager-widgets.test.mjs +342 -0
- package/lib/rpc-manager.test.mjs +139 -0
- package/lib/rpc-manager.ts +1703 -0
- package/lib/rpc-session-info.test.mjs +93 -0
- package/lib/session-delete.test.mjs +53 -0
- package/lib/session-delete.ts +86 -0
- package/lib/session-file-references-core.ts +82 -0
- package/lib/session-file-references.test.mjs +91 -0
- package/lib/session-file-references.ts +29 -0
- package/lib/session-path.test.mjs +24 -0
- package/lib/session-path.ts +11 -0
- package/lib/session-reader.pagination.test.mjs +115 -0
- package/lib/session-reader.test.mjs +446 -0
- package/lib/session-reader.ts +548 -0
- package/lib/session-resolve.test.mjs +65 -0
- package/lib/session-row-context-menu.test.mjs +46 -0
- package/lib/session-row-context-menu.ts +29 -0
- package/lib/session-scanner.test.mjs +151 -0
- package/lib/session-scanner.ts +238 -0
- package/lib/session-search.test.mjs +173 -0
- package/lib/session-search.ts +284 -0
- package/lib/session-stats.ts +26 -0
- package/lib/session-timing.test.mjs +107 -0
- package/lib/session-timing.ts +45 -0
- package/lib/session-title.test.mjs +284 -0
- package/lib/session-title.ts +255 -0
- package/lib/skill-frontmatter.test.mjs +75 -0
- package/lib/skill-frontmatter.ts +40 -0
- package/lib/skill-lock.test.mjs +182 -0
- package/lib/skill-lock.ts +146 -0
- package/lib/skill-updates.test.mjs +182 -0
- package/lib/skill-updates.ts +263 -0
- package/lib/skills-service.ts +16 -0
- package/lib/slash-display.test.mjs +122 -0
- package/lib/slash-display.ts +30 -0
- package/lib/sqlite-db.test.mjs +140 -0
- package/lib/sqlite-db.ts +205 -0
- package/lib/startup-preferences.test.mjs +130 -0
- package/lib/startup-preferences.ts +65 -0
- package/lib/streaming-message.test.mjs +212 -0
- package/lib/streaming-message.ts +151 -0
- package/lib/subagent-widget.test.mjs +87 -0
- package/lib/subagent-widget.ts +109 -0
- package/lib/sync-room-server.mjs +137 -0
- package/lib/task-card-store.test.mjs +309 -0
- package/lib/task-card-store.ts +559 -0
- package/lib/task-scheduler.ts +498 -0
- package/lib/task-store.test.mjs +216 -0
- package/lib/task-store.ts +271 -0
- package/lib/terminal-input.test.mjs +46 -0
- package/lib/terminal-input.ts +63 -0
- package/lib/terminal-manager.test.mjs +71 -0
- package/lib/terminal-manager.ts +245 -0
- package/lib/tool-execution-progress.test.mjs +32 -0
- package/lib/tool-execution-progress.ts +29 -0
- package/lib/tool-names.ts +25 -0
- package/lib/tool-preset-preference.test.mjs +72 -0
- package/lib/tool-preset-preference.ts +41 -0
- package/lib/tool-presets.test.mjs +56 -0
- package/lib/tool-presets.ts +42 -0
- package/lib/turn-written-files.test.mjs +137 -0
- package/lib/turn-written-files.ts +60 -0
- package/lib/types.ts +349 -0
- package/lib/wallpaper-settings.ts +191 -0
- package/lib/web-auth.test.mjs +50 -0
- package/lib/web-auth.ts +45 -0
- package/lib/workspace-memory.test.mjs +129 -0
- package/lib/workspace-memory.ts +98 -0
- package/lib/worktree.test.mjs +50 -0
- package/lib/worktree.ts +250 -0
- package/package.json +3 -1
- package/server.mjs +34 -0
- package/.next/server/chunks/7513.js +0 -30
- package/.next/static/chunks/5315.03de3c6abd8b283e.js +0 -4
- package/.next/static/chunks/next/dist/client/components/builtin/global-error-00a4badaccb388e6.js +0 -1
- package/.next/static/css/cf25164cb7c04d95.css +0 -1
- /package/.next/static/{Ud6Y2ONkGuPVFbo1xZQvk → 7sDSz-wW-RWYnxh1l6-fD}/_buildManifest.js +0 -0
- /package/.next/static/{Ud6Y2ONkGuPVFbo1xZQvk → 7sDSz-wW-RWYnxh1l6-fD}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import { execFile } from "child_process";
|
|
2
|
+
import { mkdtemp, rm } from "fs/promises";
|
|
3
|
+
import { tmpdir } from "os";
|
|
4
|
+
import { join } from "path";
|
|
5
|
+
import { promisify } from "util";
|
|
6
|
+
import type {
|
|
7
|
+
SkillInstallInfo,
|
|
8
|
+
SkillUpdateResult,
|
|
9
|
+
} from "@/lib/api-types";
|
|
10
|
+
|
|
11
|
+
const CHECK_TIMEOUT_MS = 15_000;
|
|
12
|
+
const GIT_CHECK_TIMEOUT_MS = 30_000;
|
|
13
|
+
const DEFAULT_SKILLS_API_BASE = process.env.SKILLS_API_URL || "https://skills.sh";
|
|
14
|
+
const execFileAsync = promisify(execFile);
|
|
15
|
+
|
|
16
|
+
type Fetcher = (input: string, init?: RequestInit) => Promise<Response>;
|
|
17
|
+
type GitTreeResolver = (install: SkillInstallInfo) => Promise<string>;
|
|
18
|
+
|
|
19
|
+
interface CheckOptions {
|
|
20
|
+
fetcher?: Fetcher;
|
|
21
|
+
skillsApiBase?: string;
|
|
22
|
+
githubToken?: string;
|
|
23
|
+
resolveGitTreeHash?: GitTreeResolver;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface GitHubTreeEntry {
|
|
27
|
+
path?: unknown;
|
|
28
|
+
type?: unknown;
|
|
29
|
+
sha?: unknown;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface GitHubTreeResponse {
|
|
33
|
+
sha?: unknown;
|
|
34
|
+
tree?: unknown;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface SnapshotResponse {
|
|
38
|
+
hash?: unknown;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
class HttpError extends Error {
|
|
42
|
+
constructor(readonly status: number) {
|
|
43
|
+
super(`HTTP ${status}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function skillUpdateKey(install: Pick<SkillInstallInfo, "scope" | "package">): string {
|
|
48
|
+
return `${install.scope}\0${install.package}`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function buildSkillUpdateArgs(install: SkillInstallInfo): string[] {
|
|
52
|
+
const folder = skillFolder(install.skillPath ?? "");
|
|
53
|
+
const source = folder ? `${install.source}/${folder}` : install.source;
|
|
54
|
+
const ref = install.ref ? `#${encodeURIComponent(install.ref)}` : "";
|
|
55
|
+
const args = [
|
|
56
|
+
"skills",
|
|
57
|
+
"add",
|
|
58
|
+
`${source}${ref}`,
|
|
59
|
+
"--skill",
|
|
60
|
+
skillNameFromPackage(install.package),
|
|
61
|
+
"-y",
|
|
62
|
+
"--agent",
|
|
63
|
+
"pi",
|
|
64
|
+
];
|
|
65
|
+
if (install.scope === "global") args.push("-g");
|
|
66
|
+
return args;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function skillSlug(name: string): string {
|
|
70
|
+
return name
|
|
71
|
+
.toLowerCase()
|
|
72
|
+
.replace(/[\s_]+/g, "-")
|
|
73
|
+
.replace(/[^a-z0-9-]/g, "")
|
|
74
|
+
.replace(/-+/g, "-")
|
|
75
|
+
.replace(/^-|-$/g, "");
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function skillNameFromPackage(pkg: string): string {
|
|
79
|
+
const at = pkg.lastIndexOf("@");
|
|
80
|
+
return at >= 0 ? pkg.slice(at + 1) : pkg;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function skillFolder(skillPath: string): string {
|
|
84
|
+
let folder = skillPath.replace(/\\/g, "/");
|
|
85
|
+
if (folder.toLowerCase().endsWith("/skill.md")) folder = folder.slice(0, -9);
|
|
86
|
+
else if (folder.toLowerCase().endsWith("skill.md")) folder = folder.slice(0, -8);
|
|
87
|
+
return folder.replace(/\/$/, "");
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function result(
|
|
91
|
+
install: SkillInstallInfo,
|
|
92
|
+
state: SkillUpdateResult["state"],
|
|
93
|
+
latestVersion?: string,
|
|
94
|
+
message?: string,
|
|
95
|
+
): SkillUpdateResult {
|
|
96
|
+
return {
|
|
97
|
+
package: install.package,
|
|
98
|
+
scope: install.scope,
|
|
99
|
+
state,
|
|
100
|
+
currentVersion: install.versionHash,
|
|
101
|
+
latestVersion,
|
|
102
|
+
message,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async function fetchJson(
|
|
107
|
+
url: string,
|
|
108
|
+
fetcher: Fetcher,
|
|
109
|
+
headers?: HeadersInit,
|
|
110
|
+
): Promise<unknown> {
|
|
111
|
+
const response = await fetcher(url, {
|
|
112
|
+
cache: "no-store",
|
|
113
|
+
headers,
|
|
114
|
+
signal: AbortSignal.timeout(CHECK_TIMEOUT_MS),
|
|
115
|
+
});
|
|
116
|
+
if (!response.ok) throw new HttpError(response.status);
|
|
117
|
+
return response.json();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async function resolveGitTreeHash(install: SkillInstallInfo): Promise<string> {
|
|
121
|
+
const repository = `https://github.com/${install.source}.git`;
|
|
122
|
+
const ref = install.ref || "HEAD";
|
|
123
|
+
const folder = skillFolder(install.skillPath!);
|
|
124
|
+
const gitDir = await mkdtemp(join(tmpdir(), "pi-web-skill-check-"));
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
await execFileAsync("git", ["init", "--bare", gitDir], {
|
|
128
|
+
timeout: GIT_CHECK_TIMEOUT_MS,
|
|
129
|
+
});
|
|
130
|
+
await execFileAsync("git", [
|
|
131
|
+
`--git-dir=${gitDir}`,
|
|
132
|
+
"fetch",
|
|
133
|
+
"--depth=1",
|
|
134
|
+
"--filter=blob:none",
|
|
135
|
+
"--no-tags",
|
|
136
|
+
repository,
|
|
137
|
+
ref,
|
|
138
|
+
], { timeout: GIT_CHECK_TIMEOUT_MS });
|
|
139
|
+
const revision = folder ? `FETCH_HEAD:${folder}` : "FETCH_HEAD^{tree}";
|
|
140
|
+
const { stdout } = await execFileAsync(
|
|
141
|
+
"git",
|
|
142
|
+
[`--git-dir=${gitDir}`, "rev-parse", revision],
|
|
143
|
+
{ timeout: GIT_CHECK_TIMEOUT_MS },
|
|
144
|
+
);
|
|
145
|
+
const hash = stdout.trim();
|
|
146
|
+
if (!/^[0-9a-f]{40}$/i.test(hash)) throw new Error("Invalid Git tree hash");
|
|
147
|
+
return hash;
|
|
148
|
+
} finally {
|
|
149
|
+
await rm(gitDir, { recursive: true, force: true });
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async function checkGlobalSkill(
|
|
154
|
+
install: SkillInstallInfo,
|
|
155
|
+
options: Required<Pick<CheckOptions, "fetcher" | "resolveGitTreeHash">> & CheckOptions,
|
|
156
|
+
): Promise<SkillUpdateResult> {
|
|
157
|
+
const ref = install.ref || "HEAD";
|
|
158
|
+
const url = `https://api.github.com/repos/${install.source}/git/trees/${encodeURIComponent(ref)}?recursive=1`;
|
|
159
|
+
const headers: Record<string, string> = {
|
|
160
|
+
Accept: "application/vnd.github.v3+json",
|
|
161
|
+
"User-Agent": "pi-web",
|
|
162
|
+
};
|
|
163
|
+
if (options.githubToken) headers.Authorization = `Bearer ${options.githubToken}`;
|
|
164
|
+
const folder = skillFolder(install.skillPath!);
|
|
165
|
+
let latestVersion: string | undefined;
|
|
166
|
+
|
|
167
|
+
try {
|
|
168
|
+
const raw = (await fetchJson(url, options.fetcher, headers)) as GitHubTreeResponse;
|
|
169
|
+
latestVersion = typeof raw.sha === "string" && !folder ? raw.sha : undefined;
|
|
170
|
+
|
|
171
|
+
if (folder && Array.isArray(raw.tree)) {
|
|
172
|
+
const entry = (raw.tree as GitHubTreeEntry[]).find(
|
|
173
|
+
(item) => item.type === "tree" && item.path === folder,
|
|
174
|
+
);
|
|
175
|
+
if (entry && typeof entry.sha === "string") latestVersion = entry.sha;
|
|
176
|
+
}
|
|
177
|
+
} catch (error) {
|
|
178
|
+
if (!(error instanceof HttpError) || ![401, 403, 429].includes(error.status)) {
|
|
179
|
+
throw error;
|
|
180
|
+
}
|
|
181
|
+
latestVersion = await options.resolveGitTreeHash(install);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (!latestVersion) {
|
|
185
|
+
return result(install, "error", undefined, "Remote skill path was not found.");
|
|
186
|
+
}
|
|
187
|
+
return result(
|
|
188
|
+
install,
|
|
189
|
+
latestVersion === install.versionHash ? "up-to-date" : "update-available",
|
|
190
|
+
latestVersion,
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
async function checkProjectSkill(
|
|
195
|
+
install: SkillInstallInfo,
|
|
196
|
+
options: Required<Pick<CheckOptions, "fetcher" | "skillsApiBase">> & CheckOptions,
|
|
197
|
+
): Promise<SkillUpdateResult> {
|
|
198
|
+
const [owner, repo] = install.source.split("/");
|
|
199
|
+
const name = skillSlug(skillNameFromPackage(install.package));
|
|
200
|
+
const url = `${options.skillsApiBase}/api/download/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/${encodeURIComponent(name)}`;
|
|
201
|
+
const raw = (await fetchJson(url, options.fetcher)) as SnapshotResponse;
|
|
202
|
+
const latestVersion = typeof raw.hash === "string" ? raw.hash : undefined;
|
|
203
|
+
if (!latestVersion) {
|
|
204
|
+
return result(install, "error", undefined, "skills.sh did not return a version hash.");
|
|
205
|
+
}
|
|
206
|
+
return result(
|
|
207
|
+
install,
|
|
208
|
+
latestVersion === install.versionHash ? "up-to-date" : "update-available",
|
|
209
|
+
latestVersion,
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export async function checkSkillUpdate(
|
|
214
|
+
install: SkillInstallInfo,
|
|
215
|
+
options: CheckOptions = {},
|
|
216
|
+
): Promise<SkillUpdateResult> {
|
|
217
|
+
if (!install.canCheckForUpdates || !install.versionHash || !install.skillPath) {
|
|
218
|
+
return result(install, "unsupported", undefined, "This lock entry cannot be checked automatically.");
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const resolvedOptions = {
|
|
222
|
+
...options,
|
|
223
|
+
fetcher: options.fetcher ?? fetch,
|
|
224
|
+
skillsApiBase: options.skillsApiBase ?? DEFAULT_SKILLS_API_BASE,
|
|
225
|
+
resolveGitTreeHash: options.resolveGitTreeHash ?? resolveGitTreeHash,
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
try {
|
|
229
|
+
return install.scope === "global"
|
|
230
|
+
? await checkGlobalSkill(install, resolvedOptions)
|
|
231
|
+
: await checkProjectSkill(install, resolvedOptions);
|
|
232
|
+
} catch (error) {
|
|
233
|
+
return result(
|
|
234
|
+
install,
|
|
235
|
+
"error",
|
|
236
|
+
undefined,
|
|
237
|
+
error instanceof Error ? error.message : String(error),
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export async function checkSkillUpdates(
|
|
243
|
+
installs: SkillInstallInfo[],
|
|
244
|
+
options: CheckOptions = {},
|
|
245
|
+
): Promise<SkillUpdateResult[]> {
|
|
246
|
+
const fetcher = options.fetcher ?? fetch;
|
|
247
|
+
const requests = new Map<string, Promise<Response>>();
|
|
248
|
+
const cachedFetcher: Fetcher = async (input, init) => {
|
|
249
|
+
let request = requests.get(input);
|
|
250
|
+
if (!request) {
|
|
251
|
+
request = fetcher(input, init);
|
|
252
|
+
requests.set(input, request);
|
|
253
|
+
}
|
|
254
|
+
return (await request).clone();
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
return Promise.all(
|
|
258
|
+
installs.map((install) => checkSkillUpdate(install, {
|
|
259
|
+
...options,
|
|
260
|
+
fetcher: cachedFetcher,
|
|
261
|
+
})),
|
|
262
|
+
);
|
|
263
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { DefaultResourceLoader, getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import type { SkillInfo, SkillsResponse } from "@/lib/api-types";
|
|
3
|
+
import { annotateSkillsWithInstallInfo } from "@/lib/skill-lock";
|
|
4
|
+
import { getProjectTrustStatus, projectTrustReloadOptions } from "@/lib/project-trust";
|
|
5
|
+
|
|
6
|
+
export async function loadSkillsWithInstallInfo(cwd: string): Promise<SkillsResponse> {
|
|
7
|
+
const agentDir = getAgentDir();
|
|
8
|
+
const loader = new DefaultResourceLoader({ cwd, agentDir });
|
|
9
|
+
await loader.reload(projectTrustReloadOptions(cwd, agentDir));
|
|
10
|
+
const { skills, diagnostics } = loader.getSkills();
|
|
11
|
+
return {
|
|
12
|
+
skills: annotateSkillsWithInstallInfo(skills as SkillInfo[], { cwd, agentDir }),
|
|
13
|
+
diagnostics,
|
|
14
|
+
projectResourcesLoaded: getProjectTrustStatus(cwd, agentDir).trusted,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
|
|
4
|
+
const { skillExpansionToCommand } = await import("./slash-display.ts");
|
|
5
|
+
|
|
6
|
+
function skillExpansion({
|
|
7
|
+
name = "review",
|
|
8
|
+
location = "/path/to/review/SKILL.md",
|
|
9
|
+
baseDir = "/path/to/review",
|
|
10
|
+
body = "Review the supplied files.",
|
|
11
|
+
args,
|
|
12
|
+
} = {}) {
|
|
13
|
+
return `<skill name="${name}" location="${location}">\nReferences are relative to ${baseDir}.\n\n${body}\n</skill>${args === undefined ? "" : `\n\n${args}`}`;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
test("restores a complete SDK skill expansion with arguments", () => {
|
|
17
|
+
const result = skillExpansionToCommand(skillExpansion({ args: "src/main.ts" }));
|
|
18
|
+
assert.equal(result?.command, "/skill:review src/main.ts");
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test("restores a complete SDK skill expansion without arguments", () => {
|
|
22
|
+
const result = skillExpansionToCommand(skillExpansion());
|
|
23
|
+
assert.equal(result?.command, "/skill:review");
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test("restores multiline arguments", () => {
|
|
27
|
+
const result = skillExpansionToCommand(
|
|
28
|
+
skillExpansion({ args: "first line\nsecond line" }),
|
|
29
|
+
);
|
|
30
|
+
assert.equal(
|
|
31
|
+
result?.command,
|
|
32
|
+
"/skill:review first line\nsecond line",
|
|
33
|
+
);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test("uses the final closing tag when the skill body contains an example", () => {
|
|
37
|
+
const result = skillExpansionToCommand(
|
|
38
|
+
skillExpansion({
|
|
39
|
+
body: "Example:\n</skill>\nContinue.",
|
|
40
|
+
args: "src",
|
|
41
|
+
}),
|
|
42
|
+
);
|
|
43
|
+
assert.equal(result?.command, "/skill:review src");
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("does not collapse incomplete or lookalike user text", () => {
|
|
47
|
+
assert.equal(
|
|
48
|
+
skillExpansionToCommand(
|
|
49
|
+
'<skill name="review" location="/path/to/review/SKILL.md">\nordinary user text',
|
|
50
|
+
),
|
|
51
|
+
null,
|
|
52
|
+
);
|
|
53
|
+
assert.equal(
|
|
54
|
+
skillExpansionToCommand(
|
|
55
|
+
'<skill name="review" location="/path/to/review/SKILL.md">\nReferences are elsewhere.\n\nbody\n</skill>',
|
|
56
|
+
),
|
|
57
|
+
null,
|
|
58
|
+
);
|
|
59
|
+
assert.equal(skillExpansionToCommand("ordinary user text"), null);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("collapse keeps session auto-naming free of skill XML", () => {
|
|
63
|
+
const firstMessage = skillExpansion({ name: "agent-md", args: "写计划" });
|
|
64
|
+
const result = skillExpansionToCommand(firstMessage);
|
|
65
|
+
const collapsed = result?.command ?? firstMessage;
|
|
66
|
+
assert.equal(collapsed, "/skill:agent-md 写计划");
|
|
67
|
+
// The sidebar slices the display form to 50 chars for the fallback title.
|
|
68
|
+
assert.ok(collapsed.length <= 50);
|
|
69
|
+
assert.ok(!collapsed.includes("<skill"));
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("plain first messages pass through unchanged for naming", () => {
|
|
73
|
+
assert.equal(
|
|
74
|
+
skillExpansionToCommand("hello world")?.command ?? "hello world",
|
|
75
|
+
"hello world",
|
|
76
|
+
);
|
|
77
|
+
assert.equal(
|
|
78
|
+
skillExpansionToCommand("")?.command ?? "",
|
|
79
|
+
"",
|
|
80
|
+
);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test("extracts the skill body (document) for expanded view", () => {
|
|
84
|
+
const result = skillExpansionToCommand(
|
|
85
|
+
skillExpansion({ body: "## 使用说明\n\n这是技能文档内容。" }),
|
|
86
|
+
);
|
|
87
|
+
assert.equal(result?.command, "/skill:review");
|
|
88
|
+
assert.equal(result?.skillBody, "## 使用说明\n\n这是技能文档内容。");
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test("extracts a multiline skill body without leaking the envelope", () => {
|
|
92
|
+
const input = `<skill name="analyze" location="/path/SKILL.md">\nReferences are relative to /path.\n\n第一步:调研\n第二步:建模\n</skill>\n\n请分析一下项目结构`;
|
|
93
|
+
const result = skillExpansionToCommand(input);
|
|
94
|
+
assert.equal(result?.command, "/skill:analyze 请分析一下项目结构");
|
|
95
|
+
// skillBody must be exactly the skill document, not the whole envelope.
|
|
96
|
+
assert.equal(result?.skillBody, "第一步:调研\n第二步:建模");
|
|
97
|
+
assert.ok(!result?.skillBody.includes("<skill"));
|
|
98
|
+
assert.ok(!result?.skillBody.includes("</skill>"));
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test("extracts user message after closing skill tag", () => {
|
|
102
|
+
const input = `<skill name="review" location="/path/SKILL.md">\nReferences are relative to /path.\n\nbody text\n</skill>\n\nThis is the user's actual message.`;
|
|
103
|
+
const result = skillExpansionToCommand(input);
|
|
104
|
+
// Group 4 captures the text after </skill>\n\n which doubles as args
|
|
105
|
+
// AND the user's prompt.
|
|
106
|
+
assert.equal(result?.command, "/skill:review This is the user's actual message.");
|
|
107
|
+
assert.equal(result?.userMessage, "This is the user's actual message.");
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test("userMessage is undefined when skill has no trailing message", () => {
|
|
111
|
+
const input = `<skill name="review" location="/path/SKILL.md">\nReferences are relative to /path.\n\nbody text\n</skill>`;
|
|
112
|
+
const result = skillExpansionToCommand(input);
|
|
113
|
+
assert.equal(result?.command, "/skill:review");
|
|
114
|
+
assert.equal(result?.userMessage, undefined);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
test("userMessage includes args when args are after skill tag", () => {
|
|
118
|
+
const input = `<skill name="review" location="/path/SKILL.md">\nReferences are relative to /path.\n\nbody\n</skill>\n\n--arg1 value1`;
|
|
119
|
+
const result = skillExpansionToCommand(input);
|
|
120
|
+
assert.equal(result?.command, "/skill:review --arg1 value1");
|
|
121
|
+
assert.equal(result?.userMessage, "--arg1 value1");
|
|
122
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Display-only restoration for the exact envelope emitted by pi's
|
|
3
|
+
* `_expandSkillCommand`. The expanded text remains the stored session input.
|
|
4
|
+
*/
|
|
5
|
+
const SKILL_EXPANSION_RE = /^<skill name="([^"\n]+)" location="([^"\n]+)">\nReferences are relative to [^\n]+\.\n\n([\s\S]*)\n<\/skill>(?:\n\n([\s\S]+))?$/;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Restore a complete SDK skill expansion to its compact command form.
|
|
9
|
+
*
|
|
10
|
+
* The expression intentionally requires the opening envelope, matching
|
|
11
|
+
* base-directory reference, final closing tag, and an optional two-newline args
|
|
12
|
+
* suffix. This avoids collapsing ordinary text that merely starts with a
|
|
13
|
+
* skill-looking tag. The greedy body capture makes the final closing tag win
|
|
14
|
+
* when a skill body contains an example `</skill>` tag.
|
|
15
|
+
*
|
|
16
|
+
* Returns `{ command, skillBody, userMessage }` — skillBody is the skill
|
|
17
|
+
* document between the opening tag and `</skill>` (shown when expanded),
|
|
18
|
+
* userMessage is the text after the closing tag (the user's actual prompt,
|
|
19
|
+
* kept as-is), or undefined if there is no separate user message.
|
|
20
|
+
*/
|
|
21
|
+
export function skillExpansionToCommand(
|
|
22
|
+
text: string,
|
|
23
|
+
): { command: string; skillBody: string; userMessage?: string } | null {
|
|
24
|
+
const match = text.match(SKILL_EXPANSION_RE);
|
|
25
|
+
if (!match) return null;
|
|
26
|
+
|
|
27
|
+
const [, name, , body, args] = match;
|
|
28
|
+
const command = args ? `/skill:${name} ${args}` : `/skill:${name}`;
|
|
29
|
+
return { command, skillBody: body, userMessage: match[4] };
|
|
30
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
import { DatabaseSync } from "node:sqlite";
|
|
4
|
+
import { createJiti } from "jiti";
|
|
5
|
+
|
|
6
|
+
const jiti = createJiti(import.meta.url, { interopDefault: true, moduleCache: false });
|
|
7
|
+
const { initSchema, SCHEMA_VERSION } = await jiti.import("./sqlite-db.ts");
|
|
8
|
+
|
|
9
|
+
function memoryDb() {
|
|
10
|
+
const db = new DatabaseSync(":memory:");
|
|
11
|
+
initSchema(db);
|
|
12
|
+
return db;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
test("initSchema creates all four tables (idempotent)", () => {
|
|
16
|
+
const db = memoryDb();
|
|
17
|
+
db.exec("PRAGMA journal_mode = WAL;"); // ensure no-op doesn't throw
|
|
18
|
+
const names = db
|
|
19
|
+
.prepare("SELECT name FROM sqlite_master WHERE name NOT LIKE 'sqlite_%'")
|
|
20
|
+
.all()
|
|
21
|
+
.map((r) => r.name);
|
|
22
|
+
for (const t of ["tasks", "session_meta", "search_state", "session_search"]) {
|
|
23
|
+
assert.ok(names.includes(t), `missing table ${t}`);
|
|
24
|
+
}
|
|
25
|
+
// Second init must not throw (idempotent).
|
|
26
|
+
initSchema(db);
|
|
27
|
+
assert.ok(true);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("migrations upgrade an old v0 database (adds pinned + sort_order)", () => {
|
|
31
|
+
const db = new DatabaseSync(":memory:");
|
|
32
|
+
// 模拟无版本管理时代的老库:基础表,无 pinned/sort_order 列,user_version=0。
|
|
33
|
+
db.exec(`
|
|
34
|
+
CREATE TABLE tasks (
|
|
35
|
+
id TEXT PRIMARY KEY,
|
|
36
|
+
project_key TEXT NOT NULL,
|
|
37
|
+
name TEXT NOT NULL,
|
|
38
|
+
created INTEGER NOT NULL,
|
|
39
|
+
updated INTEGER NOT NULL
|
|
40
|
+
);
|
|
41
|
+
CREATE TABLE session_meta (
|
|
42
|
+
session_id TEXT PRIMARY KEY,
|
|
43
|
+
task_id TEXT,
|
|
44
|
+
updated INTEGER NOT NULL
|
|
45
|
+
);
|
|
46
|
+
INSERT INTO tasks (id, project_key, name, created, updated) VALUES ('t1', 'p', 'A', 1, 1);
|
|
47
|
+
`);
|
|
48
|
+
db.exec("PRAGMA user_version = 0");
|
|
49
|
+
|
|
50
|
+
initSchema(db);
|
|
51
|
+
|
|
52
|
+
const v = db.prepare("PRAGMA user_version").get().user_version;
|
|
53
|
+
assert.equal(v, SCHEMA_VERSION);
|
|
54
|
+
const taskCols = db.prepare("SELECT name FROM pragma_table_info('tasks')").all().map((r) => r.name);
|
|
55
|
+
assert.ok(taskCols.includes("pinned"), "tasks.pinned added by v1");
|
|
56
|
+
assert.ok(taskCols.includes("sort_order"), "tasks.sort_order added by v2");
|
|
57
|
+
const metaCols = db.prepare("SELECT name FROM pragma_table_info('session_meta')").all().map((r) => r.name);
|
|
58
|
+
assert.ok(metaCols.includes("pinned"), "session_meta.pinned added by v1");
|
|
59
|
+
// 老数据保留,新列默认值可用。
|
|
60
|
+
const row = db.prepare("SELECT name, pinned, sort_order FROM tasks WHERE id = 't1'").get();
|
|
61
|
+
assert.equal(row.name, "A");
|
|
62
|
+
assert.equal(row.pinned, 0);
|
|
63
|
+
assert.equal(row.sort_order, 0);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test("migrations are idempotent and skip already-applied versions", () => {
|
|
67
|
+
const db = new DatabaseSync(":memory:");
|
|
68
|
+
initSchema(db); // fresh -> SCHEMA_VERSION
|
|
69
|
+
const v1 = db.prepare("PRAGMA user_version").get().user_version;
|
|
70
|
+
initSchema(db); // second run must be a no-op
|
|
71
|
+
const v2 = db.prepare("PRAGMA user_version").get().user_version;
|
|
72
|
+
assert.equal(v1, v2);
|
|
73
|
+
assert.equal(v1, SCHEMA_VERSION);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test("v1 database (pinned present, no sort_order) upgrades to v2 only", () => {
|
|
77
|
+
const db = new DatabaseSync(":memory:");
|
|
78
|
+
db.exec(`
|
|
79
|
+
CREATE TABLE tasks (
|
|
80
|
+
id TEXT PRIMARY KEY,
|
|
81
|
+
project_key TEXT NOT NULL,
|
|
82
|
+
name TEXT NOT NULL,
|
|
83
|
+
created INTEGER NOT NULL,
|
|
84
|
+
updated INTEGER NOT NULL,
|
|
85
|
+
pinned INTEGER NOT NULL DEFAULT 0
|
|
86
|
+
);
|
|
87
|
+
CREATE TABLE session_meta (
|
|
88
|
+
session_id TEXT PRIMARY KEY,
|
|
89
|
+
task_id TEXT,
|
|
90
|
+
updated INTEGER NOT NULL,
|
|
91
|
+
pinned INTEGER NOT NULL DEFAULT 0
|
|
92
|
+
);
|
|
93
|
+
`);
|
|
94
|
+
db.exec("PRAGMA user_version = 1");
|
|
95
|
+
|
|
96
|
+
initSchema(db);
|
|
97
|
+
|
|
98
|
+
assert.equal(db.prepare("PRAGMA user_version").get().user_version, SCHEMA_VERSION);
|
|
99
|
+
const cols = db.prepare("SELECT name FROM pragma_table_info('tasks')").all().map((r) => r.name);
|
|
100
|
+
assert.ok(cols.includes("sort_order"));
|
|
101
|
+
assert.equal(cols.filter((c) => c === "pinned").length, 1, "pinned not duplicated");
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test("FTS5 trigram indexes Chinese substrings", () => {
|
|
105
|
+
const db = memoryDb();
|
|
106
|
+
db.prepare("INSERT INTO session_search(session_id, title, body) VALUES (?, ?, ?)")
|
|
107
|
+
.run("s1", "修复登录接口", "hello world 你好");
|
|
108
|
+
// 4-char query matches mid-token (unicode61 would fail this).
|
|
109
|
+
const byMatch = db.prepare("SELECT session_id FROM session_search WHERE session_search MATCH ?")
|
|
110
|
+
.all('"登录接口"');
|
|
111
|
+
assert.deepEqual(byMatch.map((r) => r.session_id), ["s1"]);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test("FTS5 trigram short-query falls back to LIKE", () => {
|
|
115
|
+
const db = memoryDb();
|
|
116
|
+
db.prepare("INSERT INTO session_search(session_id, title, body) VALUES (?, ?, ?)")
|
|
117
|
+
.run("s1", "修复登录接口", "做个登录页 hello world");
|
|
118
|
+
// 2-char trigram MATCH misses entirely (needs >= 3 chars).
|
|
119
|
+
const byMatch = db.prepare("SELECT session_id FROM session_search WHERE session_search MATCH ?")
|
|
120
|
+
.all('"登录"');
|
|
121
|
+
assert.equal(byMatch.length, 0, "2-char MATCH must not hit (documents the LIKE fallback need)");
|
|
122
|
+
// LIKE on the FTS table is the short-query path — hits both columns.
|
|
123
|
+
const byTitleLike = db.prepare("SELECT session_id FROM session_search WHERE title LIKE ?").all("%登录%");
|
|
124
|
+
assert.equal(byTitleLike.length, 1);
|
|
125
|
+
const byBodyLike = db.prepare("SELECT session_id FROM session_search WHERE body LIKE ?").all("%登录%");
|
|
126
|
+
assert.equal(byBodyLike.length, 1);
|
|
127
|
+
// LIKE stays case-insensitive-free for Latin (trigram) — plain contains check:
|
|
128
|
+
const byEnglishLike = db.prepare("SELECT session_id FROM session_search WHERE body LIKE ?").all("%hello%");
|
|
129
|
+
assert.equal(byEnglishLike.length, 1);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
test("session_search keeps UNINDEXED session_id column distinct from content", () => {
|
|
133
|
+
const db = memoryDb();
|
|
134
|
+
db.prepare("INSERT INTO session_search(session_id, title, body) VALUES (?, ?, ?)")
|
|
135
|
+
.run("s-a-b", "t1", "b1");
|
|
136
|
+
db.prepare("INSERT INTO session_search(session_id, title, body) VALUES (?, ?, ?)")
|
|
137
|
+
.run("s-x-y", "t2", "b2");
|
|
138
|
+
const rows = db.prepare("SELECT session_id FROM session_search").all();
|
|
139
|
+
assert.deepEqual(rows.map((r) => r.session_id), ["s-a-b", "s-x-y"]);
|
|
140
|
+
});
|