pi-ui-extend 0.1.1
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 +307 -0
- package/bin/pix.mjs +219 -0
- package/dist/app/app.d.ts +96 -0
- package/dist/app/app.js +871 -0
- package/dist/app/blink-controller.d.ts +23 -0
- package/dist/app/blink-controller.js +82 -0
- package/dist/app/cli.d.ts +8 -0
- package/dist/app/cli.js +83 -0
- package/dist/app/clipboard.d.ts +1 -0
- package/dist/app/clipboard.js +24 -0
- package/dist/app/command-controller.d.ts +18 -0
- package/dist/app/command-controller.js +58 -0
- package/dist/app/command-host.d.ts +44 -0
- package/dist/app/command-host.js +1 -0
- package/dist/app/command-model-actions.d.ts +12 -0
- package/dist/app/command-model-actions.js +176 -0
- package/dist/app/command-navigation-actions.d.ts +19 -0
- package/dist/app/command-navigation-actions.js +267 -0
- package/dist/app/command-registry.d.ts +32 -0
- package/dist/app/command-registry.js +225 -0
- package/dist/app/command-runtime.d.ts +5 -0
- package/dist/app/command-runtime.js +32 -0
- package/dist/app/command-session-actions.d.ts +20 -0
- package/dist/app/command-session-actions.js +295 -0
- package/dist/app/constants.d.ts +52 -0
- package/dist/app/constants.js +103 -0
- package/dist/app/conversation-entry-renderer.d.ts +21 -0
- package/dist/app/conversation-entry-renderer.js +81 -0
- package/dist/app/conversation-shell-renderer.d.ts +5 -0
- package/dist/app/conversation-shell-renderer.js +43 -0
- package/dist/app/conversation-tool-renderer.d.ts +16 -0
- package/dist/app/conversation-tool-renderer.js +216 -0
- package/dist/app/conversation-viewport.d.ts +55 -0
- package/dist/app/conversation-viewport.js +252 -0
- package/dist/app/dcp-stats.d.ts +2 -0
- package/dist/app/dcp-stats.js +116 -0
- package/dist/app/editor-layout-renderer.d.ts +31 -0
- package/dist/app/editor-layout-renderer.js +211 -0
- package/dist/app/editor-panels.d.ts +4 -0
- package/dist/app/editor-panels.js +130 -0
- package/dist/app/extension-actions-controller.d.ts +22 -0
- package/dist/app/extension-actions-controller.js +60 -0
- package/dist/app/extension-event-bus.d.ts +3 -0
- package/dist/app/extension-event-bus.js +23 -0
- package/dist/app/extension-ui-controller.d.ts +57 -0
- package/dist/app/extension-ui-controller.js +532 -0
- package/dist/app/file-link-opener.d.ts +2 -0
- package/dist/app/file-link-opener.js +66 -0
- package/dist/app/file-links.d.ts +10 -0
- package/dist/app/file-links.js +117 -0
- package/dist/app/guards.d.ts +3 -0
- package/dist/app/guards.js +9 -0
- package/dist/app/icons.d.ts +37 -0
- package/dist/app/icons.js +97 -0
- package/dist/app/id.d.ts +1 -0
- package/dist/app/id.js +4 -0
- package/dist/app/image-click-targets.d.ts +5 -0
- package/dist/app/image-click-targets.js +32 -0
- package/dist/app/image-opener.d.ts +2 -0
- package/dist/app/image-opener.js +64 -0
- package/dist/app/input-action-controller.d.ts +47 -0
- package/dist/app/input-action-controller.js +209 -0
- package/dist/app/input-controller.d.ts +60 -0
- package/dist/app/input-controller.js +425 -0
- package/dist/app/input-paste-handler.d.ts +27 -0
- package/dist/app/input-paste-handler.js +146 -0
- package/dist/app/menu-items-controller.d.ts +32 -0
- package/dist/app/menu-items-controller.js +182 -0
- package/dist/app/message-content.d.ts +8 -0
- package/dist/app/message-content.js +115 -0
- package/dist/app/model-ref.d.ts +13 -0
- package/dist/app/model-ref.js +50 -0
- package/dist/app/model-usage-controller.d.ts +35 -0
- package/dist/app/model-usage-controller.js +99 -0
- package/dist/app/model-usage-status.d.ts +125 -0
- package/dist/app/model-usage-status.js +749 -0
- package/dist/app/mouse-controller.d.ts +182 -0
- package/dist/app/mouse-controller.js +968 -0
- package/dist/app/native-modifiers.d.ts +3 -0
- package/dist/app/native-modifiers.js +60 -0
- package/dist/app/nerd-font-controller.d.ts +11 -0
- package/dist/app/nerd-font-controller.js +90 -0
- package/dist/app/popup-action-controller.d.ts +44 -0
- package/dist/app/popup-action-controller.js +278 -0
- package/dist/app/popup-menu-controller.d.ts +183 -0
- package/dist/app/popup-menu-controller.js +1070 -0
- package/dist/app/prompt-enhancer-controller.d.ts +40 -0
- package/dist/app/prompt-enhancer-controller.js +215 -0
- package/dist/app/queued-message-controller.d.ts +62 -0
- package/dist/app/queued-message-controller.js +377 -0
- package/dist/app/render-controller.d.ts +41 -0
- package/dist/app/render-controller.js +378 -0
- package/dist/app/render-text.d.ts +19 -0
- package/dist/app/render-text.js +67 -0
- package/dist/app/request-history.d.ts +23 -0
- package/dist/app/request-history.js +131 -0
- package/dist/app/runtime.d.ts +39 -0
- package/dist/app/runtime.js +195 -0
- package/dist/app/screen-selection.d.ts +6 -0
- package/dist/app/screen-selection.js +9 -0
- package/dist/app/screen-styler.d.ts +34 -0
- package/dist/app/screen-styler.js +168 -0
- package/dist/app/scroll-controller.d.ts +51 -0
- package/dist/app/scroll-controller.js +207 -0
- package/dist/app/session-event-controller.d.ts +69 -0
- package/dist/app/session-event-controller.js +338 -0
- package/dist/app/session-history.d.ts +23 -0
- package/dist/app/session-history.js +164 -0
- package/dist/app/session-lifecycle-controller.d.ts +55 -0
- package/dist/app/session-lifecycle-controller.js +116 -0
- package/dist/app/session-search.d.ts +24 -0
- package/dist/app/session-search.js +215 -0
- package/dist/app/shell-command.d.ts +27 -0
- package/dist/app/shell-command.js +176 -0
- package/dist/app/shell-controller.d.ts +28 -0
- package/dist/app/shell-controller.js +124 -0
- package/dist/app/slash-commands.d.ts +6 -0
- package/dist/app/slash-commands.js +75 -0
- package/dist/app/startup-checks.d.ts +8 -0
- package/dist/app/startup-checks.js +59 -0
- package/dist/app/startup-info.d.ts +3 -0
- package/dist/app/startup-info.js +176 -0
- package/dist/app/status-controller.d.ts +35 -0
- package/dist/app/status-controller.js +105 -0
- package/dist/app/status-line-renderer.d.ts +68 -0
- package/dist/app/status-line-renderer.js +453 -0
- package/dist/app/subagents-files.d.ts +10 -0
- package/dist/app/subagents-files.js +193 -0
- package/dist/app/subagents-model.d.ts +23 -0
- package/dist/app/subagents-model.js +224 -0
- package/dist/app/subagents-widget-controller.d.ts +43 -0
- package/dist/app/subagents-widget-controller.js +311 -0
- package/dist/app/tab-line-renderer.d.ts +26 -0
- package/dist/app/tab-line-renderer.js +222 -0
- package/dist/app/tabs-controller.d.ts +100 -0
- package/dist/app/tabs-controller.js +885 -0
- package/dist/app/terminal-controller.d.ts +40 -0
- package/dist/app/terminal-controller.js +135 -0
- package/dist/app/terminal-edit-shortcuts.d.ts +23 -0
- package/dist/app/terminal-edit-shortcuts.js +138 -0
- package/dist/app/terminal-output-buffer.d.ts +20 -0
- package/dist/app/terminal-output-buffer.js +52 -0
- package/dist/app/toast-controller.d.ts +13 -0
- package/dist/app/toast-controller.js +40 -0
- package/dist/app/toast-renderer.d.ts +9 -0
- package/dist/app/toast-renderer.js +79 -0
- package/dist/app/todo-model.d.ts +22 -0
- package/dist/app/todo-model.js +179 -0
- package/dist/app/todo-widget-controller.d.ts +21 -0
- package/dist/app/todo-widget-controller.js +59 -0
- package/dist/app/tool-block-renderer.d.ts +26 -0
- package/dist/app/tool-block-renderer.js +439 -0
- package/dist/app/types.d.ts +550 -0
- package/dist/app/types.js +1 -0
- package/dist/app/update.d.ts +36 -0
- package/dist/app/update.js +315 -0
- package/dist/app/voice-controller.d.ts +52 -0
- package/dist/app/voice-controller.js +600 -0
- package/dist/app/workspace-actions-controller.d.ts +40 -0
- package/dist/app/workspace-actions-controller.js +215 -0
- package/dist/app/workspace-undo.d.ts +44 -0
- package/dist/app/workspace-undo.js +215 -0
- package/dist/config.d.ts +62 -0
- package/dist/config.js +527 -0
- package/dist/context-progress-bar.d.ts +17 -0
- package/dist/context-progress-bar.js +48 -0
- package/dist/default-pix-config.d.ts +1 -0
- package/dist/default-pix-config.js +375 -0
- package/dist/fuzzy.d.ts +23 -0
- package/dist/fuzzy.js +165 -0
- package/dist/input-editor-files.d.ts +15 -0
- package/dist/input-editor-files.js +62 -0
- package/dist/input-editor.d.ts +186 -0
- package/dist/input-editor.js +835 -0
- package/dist/main.d.ts +1 -0
- package/dist/main.js +12 -0
- package/dist/markdown-format.d.ts +22 -0
- package/dist/markdown-format.js +542 -0
- package/dist/sdk.d.ts +3 -0
- package/dist/sdk.js +1 -0
- package/dist/syntax-highlight.d.ts +22 -0
- package/dist/syntax-highlight.js +528 -0
- package/dist/terminal-width.d.ts +6 -0
- package/dist/terminal-width.js +245 -0
- package/dist/theme.d.ts +56 -0
- package/dist/theme.js +118 -0
- package/dist/tool-renderers/apply-patch.d.ts +2 -0
- package/dist/tool-renderers/apply-patch.js +36 -0
- package/dist/tool-renderers/ast.d.ts +2 -0
- package/dist/tool-renderers/ast.js +5 -0
- package/dist/tool-renderers/compress.d.ts +2 -0
- package/dist/tool-renderers/compress.js +126 -0
- package/dist/tool-renderers/index.d.ts +3 -0
- package/dist/tool-renderers/index.js +44 -0
- package/dist/tool-renderers/question.d.ts +2 -0
- package/dist/tool-renderers/question.js +88 -0
- package/dist/tool-renderers/read.d.ts +2 -0
- package/dist/tool-renderers/read.js +36 -0
- package/dist/tool-renderers/repo.d.ts +2 -0
- package/dist/tool-renderers/repo.js +13 -0
- package/dist/tool-renderers/shell.d.ts +3 -0
- package/dist/tool-renderers/shell.js +27 -0
- package/dist/tool-renderers/skill.d.ts +2 -0
- package/dist/tool-renderers/skill.js +132 -0
- package/dist/tool-renderers/subagents.d.ts +2 -0
- package/dist/tool-renderers/subagents.js +51 -0
- package/dist/tool-renderers/todo.d.ts +2 -0
- package/dist/tool-renderers/todo.js +9 -0
- package/dist/tool-renderers/types.d.ts +42 -0
- package/dist/tool-renderers/types.js +1 -0
- package/dist/tool-renderers/utils.d.ts +31 -0
- package/dist/tool-renderers/utils.js +230 -0
- package/dist/tool-renderers/web.d.ts +3 -0
- package/dist/tool-renderers/web.js +9 -0
- package/dist/tool-renderers/write.d.ts +2 -0
- package/dist/tool-renderers/write.js +42 -0
- package/dist/ui.d.ts +56 -0
- package/dist/ui.js +94 -0
- package/docs/release.md +81 -0
- package/extensions/question/contract.ts +100 -0
- package/extensions/question/index.ts +34 -0
- package/extensions/question/render.ts +28 -0
- package/extensions/question/result.ts +86 -0
- package/extensions/question/tool-description.ts +11 -0
- package/extensions/question/tui.ts +629 -0
- package/extensions/question/types.ts +123 -0
- package/extensions/session-title/config.ts +169 -0
- package/extensions/session-title/index.ts +459 -0
- package/extensions/terminal-bell/index.ts +315 -0
- package/external/pi-tools-suite/README.md +242 -0
- package/external/pi-tools-suite/index.ts +1 -0
- package/external/pi-tools-suite/licenses/ollama-pi-web-search.MIT +21 -0
- package/external/pi-tools-suite/licenses/opencode-mystatus-MIT.txt +21 -0
- package/external/pi-tools-suite/package.json +53 -0
- package/external/pi-tools-suite/src/antigravity-auth/auth-store.ts +194 -0
- package/external/pi-tools-suite/src/antigravity-auth/commands.ts +80 -0
- package/external/pi-tools-suite/src/antigravity-auth/constants.ts +26 -0
- package/external/pi-tools-suite/src/antigravity-auth/headers.ts +20 -0
- package/external/pi-tools-suite/src/antigravity-auth/index.ts +104 -0
- package/external/pi-tools-suite/src/antigravity-auth/models.ts +86 -0
- package/external/pi-tools-suite/src/antigravity-auth/oauth.ts +305 -0
- package/external/pi-tools-suite/src/antigravity-auth/payload.ts +423 -0
- package/external/pi-tools-suite/src/antigravity-auth/status.ts +78 -0
- package/external/pi-tools-suite/src/antigravity-auth/stream.ts +302 -0
- package/external/pi-tools-suite/src/antigravity-auth/types.ts +113 -0
- package/external/pi-tools-suite/src/ast-grep/index.ts +6 -0
- package/external/pi-tools-suite/src/ast-grep/render.ts +70 -0
- package/external/pi-tools-suite/src/ast-grep/schema.ts +109 -0
- package/external/pi-tools-suite/src/ast-grep/tool.ts +345 -0
- package/external/pi-tools-suite/src/ast-grep/types.ts +55 -0
- package/external/pi-tools-suite/src/ast-grep/utils.ts +65 -0
- package/external/pi-tools-suite/src/async-subagents/async-subagents.sample.jsonc +222 -0
- package/external/pi-tools-suite/src/async-subagents/commands.ts +518 -0
- package/external/pi-tools-suite/src/async-subagents/constants.ts +11 -0
- package/external/pi-tools-suite/src/async-subagents/core/agent-strategy.ts +74 -0
- package/external/pi-tools-suite/src/async-subagents/core/attachment-bridge.ts +133 -0
- package/external/pi-tools-suite/src/async-subagents/core/cleanup.ts +66 -0
- package/external/pi-tools-suite/src/async-subagents/core/concurrency.ts +90 -0
- package/external/pi-tools-suite/src/async-subagents/core/config.ts +819 -0
- package/external/pi-tools-suite/src/async-subagents/core/log-limits.ts +166 -0
- package/external/pi-tools-suite/src/async-subagents/core/model-fallback.ts +133 -0
- package/external/pi-tools-suite/src/async-subagents/core/paths.ts +47 -0
- package/external/pi-tools-suite/src/async-subagents/core/pi-invocation.ts +35 -0
- package/external/pi-tools-suite/src/async-subagents/core/presets.ts +67 -0
- package/external/pi-tools-suite/src/async-subagents/core/process.ts +15 -0
- package/external/pi-tools-suite/src/async-subagents/core/prompt.ts +66 -0
- package/external/pi-tools-suite/src/async-subagents/core/registry.ts +224 -0
- package/external/pi-tools-suite/src/async-subagents/core/retry.ts +191 -0
- package/external/pi-tools-suite/src/async-subagents/core/routing.ts +259 -0
- package/external/pi-tools-suite/src/async-subagents/core/sessions.ts +138 -0
- package/external/pi-tools-suite/src/async-subagents/core/spawn.ts +688 -0
- package/external/pi-tools-suite/src/async-subagents/core/state.ts +281 -0
- package/external/pi-tools-suite/src/async-subagents/core/stop.ts +131 -0
- package/external/pi-tools-suite/src/async-subagents/core/structured-result.ts +237 -0
- package/external/pi-tools-suite/src/async-subagents/core/tool-guard.ts +34 -0
- package/external/pi-tools-suite/src/async-subagents/core/types.ts +150 -0
- package/external/pi-tools-suite/src/async-subagents/core/ultrawork-auto.ts +184 -0
- package/external/pi-tools-suite/src/async-subagents/core/utils.ts +11 -0
- package/external/pi-tools-suite/src/async-subagents/format.ts +41 -0
- package/external/pi-tools-suite/src/async-subagents/index.ts +422 -0
- package/external/pi-tools-suite/src/async-subagents/lib.ts +88 -0
- package/external/pi-tools-suite/src/async-subagents/live.ts +10 -0
- package/external/pi-tools-suite/src/async-subagents/polling.ts +83 -0
- package/external/pi-tools-suite/src/async-subagents/render.ts +230 -0
- package/external/pi-tools-suite/src/async-subagents/subagent-overlay.ts +77 -0
- package/external/pi-tools-suite/src/async-subagents/tasks.ts +120 -0
- package/external/pi-tools-suite/src/async-subagents/tools/cleanup.ts +99 -0
- package/external/pi-tools-suite/src/async-subagents/tools/result.ts +179 -0
- package/external/pi-tools-suite/src/async-subagents/tools/spawn.ts +372 -0
- package/external/pi-tools-suite/src/async-subagents/tools/status.ts +60 -0
- package/external/pi-tools-suite/src/async-subagents/tools/stop.ts +79 -0
- package/external/pi-tools-suite/src/async-subagents/tools/subagents.ts +152 -0
- package/external/pi-tools-suite/src/async-subagents/tools/wait.ts +67 -0
- package/external/pi-tools-suite/src/async-subagents/types.ts +45 -0
- package/external/pi-tools-suite/src/async-subagents/ui.ts +5 -0
- package/external/pi-tools-suite/src/compress/commands.ts +440 -0
- package/external/pi-tools-suite/src/compress/compress-tool.ts +368 -0
- package/external/pi-tools-suite/src/compress/compression-blocks.ts +524 -0
- package/external/pi-tools-suite/src/compress/config.ts +310 -0
- package/external/pi-tools-suite/src/compress/dcp-tui-filter.ts +498 -0
- package/external/pi-tools-suite/src/compress/index.ts +397 -0
- package/external/pi-tools-suite/src/compress/prompts.ts +269 -0
- package/external/pi-tools-suite/src/compress/pruner-candidates.ts +176 -0
- package/external/pi-tools-suite/src/compress/pruner-compression-blocks.ts +260 -0
- package/external/pi-tools-suite/src/compress/pruner-message-ids.ts +147 -0
- package/external/pi-tools-suite/src/compress/pruner-metadata.ts +268 -0
- package/external/pi-tools-suite/src/compress/pruner-nudge.ts +315 -0
- package/external/pi-tools-suite/src/compress/pruner-tools.ts +263 -0
- package/external/pi-tools-suite/src/compress/pruner-types.ts +25 -0
- package/external/pi-tools-suite/src/compress/pruner.ts +92 -0
- package/external/pi-tools-suite/src/compress/state.ts +486 -0
- package/external/pi-tools-suite/src/compress/ui.ts +308 -0
- package/external/pi-tools-suite/src/config.ts +176 -0
- package/external/pi-tools-suite/src/context-usage.ts +31 -0
- package/external/pi-tools-suite/src/default-pi-tools-suite-config.ts +355 -0
- package/external/pi-tools-suite/src/index.ts +46 -0
- package/external/pi-tools-suite/src/lib/lsp.ts +62 -0
- package/external/pi-tools-suite/src/lib/project.ts +42 -0
- package/external/pi-tools-suite/src/lib/tool-args.ts +137 -0
- package/external/pi-tools-suite/src/lsp/_shared/config.ts +156 -0
- package/external/pi-tools-suite/src/lsp/_shared/glob.ts +60 -0
- package/external/pi-tools-suite/src/lsp/_shared/output.ts +102 -0
- package/external/pi-tools-suite/src/lsp/_shared/paths.ts +138 -0
- package/external/pi-tools-suite/src/lsp/_shared/runner.ts +64 -0
- package/external/pi-tools-suite/src/lsp/_shared/template.ts +23 -0
- package/external/pi-tools-suite/src/lsp/_shared/trust.ts +116 -0
- package/external/pi-tools-suite/src/lsp/_shared/types.ts +98 -0
- package/external/pi-tools-suite/src/lsp/async.ts +29 -0
- package/external/pi-tools-suite/src/lsp/child-process.ts +81 -0
- package/external/pi-tools-suite/src/lsp/client.ts +340 -0
- package/external/pi-tools-suite/src/lsp/constants.ts +9 -0
- package/external/pi-tools-suite/src/lsp/diagnostics-store.ts +64 -0
- package/external/pi-tools-suite/src/lsp/documents.ts +24 -0
- package/external/pi-tools-suite/src/lsp/index.ts +31 -0
- package/external/pi-tools-suite/src/lsp/lsp-utils.ts +37 -0
- package/external/pi-tools-suite/src/lsp/manager.ts +190 -0
- package/external/pi-tools-suite/src/lsp/mutation-events.ts +78 -0
- package/external/pi-tools-suite/src/lsp/renderer.ts +1 -0
- package/external/pi-tools-suite/src/lsp/tool-result.ts +6 -0
- package/external/pi-tools-suite/src/lsp/tsserver.ts +107 -0
- package/external/pi-tools-suite/src/lsp/types.ts +15 -0
- package/external/pi-tools-suite/src/model-tools/apply-patch.ts +590 -0
- package/external/pi-tools-suite/src/model-tools/index.ts +430 -0
- package/external/pi-tools-suite/src/model-tools/path-utils.ts +6 -0
- package/external/pi-tools-suite/src/model-tools/tool-args.ts +11 -0
- package/external/pi-tools-suite/src/prompt-commands/index.ts +349 -0
- package/external/pi-tools-suite/src/repo-discovery/index.ts +384 -0
- package/external/pi-tools-suite/src/repo-discovery/project.ts +7 -0
- package/external/pi-tools-suite/src/startup-section.ts +13 -0
- package/external/pi-tools-suite/src/terminal-bell/index.ts +309 -0
- package/external/pi-tools-suite/src/todo/index.ts +201 -0
- package/external/pi-tools-suite/src/todo/state/auto-clear.ts +13 -0
- package/external/pi-tools-suite/src/todo/state/invariants.ts +21 -0
- package/external/pi-tools-suite/src/todo/state/persistence.ts +94 -0
- package/external/pi-tools-suite/src/todo/state/replay.ts +38 -0
- package/external/pi-tools-suite/src/todo/state/selectors.ts +49 -0
- package/external/pi-tools-suite/src/todo/state/state-reducer.ts +362 -0
- package/external/pi-tools-suite/src/todo/state/state.ts +18 -0
- package/external/pi-tools-suite/src/todo/state/store.ts +52 -0
- package/external/pi-tools-suite/src/todo/state/task-graph.ts +57 -0
- package/external/pi-tools-suite/src/todo/todo.ts +487 -0
- package/external/pi-tools-suite/src/todo/tool/response-envelope.ts +143 -0
- package/external/pi-tools-suite/src/todo/tool/types.ts +188 -0
- package/external/pi-tools-suite/src/todo/view/format.ts +18 -0
- package/external/pi-tools-suite/src/todo/view/labels.ts +13 -0
- package/external/pi-tools-suite/src/tool-descriptions.ts +369 -0
- package/external/pi-tools-suite/src/usage/index.ts +152 -0
- package/external/pi-tools-suite/src/usage/lib/copilot.ts +535 -0
- package/external/pi-tools-suite/src/usage/lib/google.ts +478 -0
- package/external/pi-tools-suite/src/usage/lib/openai.ts +268 -0
- package/external/pi-tools-suite/src/usage/lib/types.ts +114 -0
- package/external/pi-tools-suite/src/usage/lib/utils.ts +134 -0
- package/external/pi-tools-suite/src/usage/lib/zhipu.ts +228 -0
- package/external/pi-tools-suite/src/web-search/index.ts +397 -0
- package/package.json +89 -0
- package/skills/context7/SKILL.md +69 -0
- package/skills/context7/scripts/context7.sh +73 -0
- package/skills/handoff/SKILL.md +15 -0
- package/skills/pdf/LICENSE.txt +30 -0
- package/skills/pdf/SKILL.md +314 -0
- package/skills/pdf/forms.md +294 -0
- package/skills/pdf/reference.md +612 -0
- package/skills/pdf/scripts/check_bounding_boxes.py +65 -0
- package/skills/pdf/scripts/check_fillable_fields.py +11 -0
- package/skills/pdf/scripts/convert_pdf_to_images.py +33 -0
- package/skills/pdf/scripts/create_validation_image.py +37 -0
- package/skills/pdf/scripts/extract_form_field_info.py +122 -0
- package/skills/pdf/scripts/extract_form_structure.py +115 -0
- package/skills/pdf/scripts/fill_fillable_fields.py +98 -0
- package/skills/pdf/scripts/fill_pdf_form_with_annotations.py +107 -0
- package/skills/playwright-cli/SKILL.md +388 -0
- package/skills/playwright-cli/references/element-attributes.md +23 -0
- package/skills/playwright-cli/references/playwright-tests.md +39 -0
- package/skills/playwright-cli/references/request-mocking.md +87 -0
- package/skills/playwright-cli/references/running-code.md +241 -0
- package/skills/playwright-cli/references/session-management.md +225 -0
- package/skills/playwright-cli/references/spec-driven-testing.md +305 -0
- package/skills/playwright-cli/references/storage-state.md +275 -0
- package/skills/playwright-cli/references/test-generation.md +134 -0
- package/skills/playwright-cli/references/tracing.md +139 -0
- package/skills/playwright-cli/references/video-recording.md +143 -0
- package/skills/simplify/SKILL.md +51 -0
- package/skills/skill-creator/LICENSE.txt +202 -0
- package/skills/skill-creator/SKILL.md +485 -0
- package/skills/skill-creator/agents/analyzer.md +274 -0
- package/skills/skill-creator/agents/comparator.md +202 -0
- package/skills/skill-creator/agents/grader.md +223 -0
- package/skills/skill-creator/assets/eval_review.html +146 -0
- package/skills/skill-creator/eval-viewer/generate_review.py +471 -0
- package/skills/skill-creator/eval-viewer/viewer.html +1325 -0
- package/skills/skill-creator/references/schemas.md +430 -0
- package/skills/skill-creator/scripts/__init__.py +0 -0
- package/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
- package/skills/skill-creator/scripts/generate_report.py +326 -0
- package/skills/skill-creator/scripts/improve_description.py +247 -0
- package/skills/skill-creator/scripts/package_skill.py +136 -0
- package/skills/skill-creator/scripts/quick_validate.py +103 -0
- package/skills/skill-creator/scripts/run_eval.py +310 -0
- package/skills/skill-creator/scripts/run_loop.py +328 -0
- package/skills/skill-creator/scripts/utils.py +47 -0
|
@@ -0,0 +1,590 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { mkdir, mkdtemp, readFile, realpath, rm, stat, writeFile } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { dirname, join, resolve } from "node:path";
|
|
5
|
+
import { withFileMutationQueue } from "@mariozechner/pi-coding-agent";
|
|
6
|
+
import { isPathInside } from "./path-utils";
|
|
7
|
+
|
|
8
|
+
export interface ApplyPatchResult {
|
|
9
|
+
changedFiles: string[];
|
|
10
|
+
summary: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
type HunkLine = {
|
|
14
|
+
kind: "context" | "remove" | "add";
|
|
15
|
+
text: string;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
type Hunk = {
|
|
19
|
+
header?: string;
|
|
20
|
+
lines: HunkLine[];
|
|
21
|
+
endOfFile: boolean;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
type PatchOperation =
|
|
25
|
+
| { kind: "add"; path: string; lines: string[] }
|
|
26
|
+
| { kind: "delete"; path: string }
|
|
27
|
+
| { kind: "update"; path: string; moveTo?: string; hunks: Hunk[] };
|
|
28
|
+
|
|
29
|
+
interface PlannedWrite {
|
|
30
|
+
kind: "write";
|
|
31
|
+
changeKind: "A" | "M";
|
|
32
|
+
path: string;
|
|
33
|
+
absolutePath: string;
|
|
34
|
+
content: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface PlannedDelete {
|
|
38
|
+
kind: "delete";
|
|
39
|
+
changeKind: "D";
|
|
40
|
+
path: string;
|
|
41
|
+
absolutePath: string;
|
|
42
|
+
summarize: boolean;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
type PlannedChange = PlannedWrite | PlannedDelete;
|
|
46
|
+
type Match = { kind: "found"; index: number } | { kind: "missing" };
|
|
47
|
+
|
|
48
|
+
const BEGIN = "*** Begin Patch";
|
|
49
|
+
const END = "*** End Patch";
|
|
50
|
+
const ADD = "*** Add File: ";
|
|
51
|
+
const DELETE = "*** Delete File: ";
|
|
52
|
+
const UPDATE = "*** Update File: ";
|
|
53
|
+
const MOVE = "*** Move to: ";
|
|
54
|
+
const EOF_MARKER = "*** End of File";
|
|
55
|
+
|
|
56
|
+
type UnifiedChangeKind = "A" | "M" | "D";
|
|
57
|
+
type UnifiedChange = { path: string; changeKind: UnifiedChangeKind };
|
|
58
|
+
|
|
59
|
+
function normalizedPatchLines(input: string): string[] {
|
|
60
|
+
const normalized = input.replace(/\r\n/g, "\n").replace(/\r/g, "\n").trim();
|
|
61
|
+
return normalized ? normalized.split("\n") : [];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function strictPatchBodyLines(lines: string[]): string[] {
|
|
65
|
+
const first = lines[0]?.trim();
|
|
66
|
+
const last = lines.at(-1)?.trim();
|
|
67
|
+
if (first !== BEGIN) throw new Error("Invalid patch: The first line of the patch must be '*** Begin Patch'");
|
|
68
|
+
if (last !== END) throw new Error("Invalid patch: The last line of the patch must be '*** End Patch'");
|
|
69
|
+
return lines.slice(1, -1);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function patchBodyLines(input: string): string[] {
|
|
73
|
+
const originalLines = normalizedPatchLines(input);
|
|
74
|
+
try {
|
|
75
|
+
return strictPatchBodyLines(originalLines);
|
|
76
|
+
} catch (originalError) {
|
|
77
|
+
const first = originalLines[0];
|
|
78
|
+
const last = originalLines.at(-1);
|
|
79
|
+
const isHeredoc = first === "<<EOF" || first === "<<'EOF'" || first === '<<"EOF"';
|
|
80
|
+
if (isHeredoc && last?.endsWith("EOF") && originalLines.length >= 4) {
|
|
81
|
+
return strictPatchBodyLines(originalLines.slice(1, -1));
|
|
82
|
+
}
|
|
83
|
+
throw originalError;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function looksLikeBeginPatch(input: string): boolean {
|
|
88
|
+
const first = normalizedPatchLines(input)[0]?.trim();
|
|
89
|
+
return first === BEGIN || first === "<<EOF" || first === "<<'EOF'" || first === '<<"EOF"';
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function stripDiffPathPrefix(pathText: string): string {
|
|
93
|
+
if (pathText === "/dev/null") return "";
|
|
94
|
+
if ((pathText.startsWith("a/") || pathText.startsWith("b/")) && pathText.length > 2) return pathText.slice(2);
|
|
95
|
+
return pathText;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function unifiedHeaderPath(line: string, marker: "--- " | "+++ "): string {
|
|
99
|
+
let pathText = line.slice(marker.length).trim();
|
|
100
|
+
if (pathText === "/dev/null") return "";
|
|
101
|
+
const tabIndex = pathText.indexOf("\t");
|
|
102
|
+
if (tabIndex !== -1) pathText = pathText.slice(0, tabIndex);
|
|
103
|
+
return stripDiffPathPrefix(pathText);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function extractUnifiedChanges(input: string): UnifiedChange[] {
|
|
107
|
+
const changes = new Map<string, UnifiedChange>();
|
|
108
|
+
let oldPath = "";
|
|
109
|
+
|
|
110
|
+
for (const line of input.split(/\r?\n/)) {
|
|
111
|
+
if (line.startsWith("--- ")) {
|
|
112
|
+
oldPath = unifiedHeaderPath(line, "--- ");
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
if (!line.startsWith("+++ ")) continue;
|
|
116
|
+
|
|
117
|
+
const newPath = unifiedHeaderPath(line, "+++ ");
|
|
118
|
+
const path = newPath || oldPath;
|
|
119
|
+
if (!path) continue;
|
|
120
|
+
const changeKind: UnifiedChangeKind = oldPath ? (newPath ? "M" : "D") : "A";
|
|
121
|
+
changes.set(path, { path, changeKind });
|
|
122
|
+
oldPath = "";
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return [...changes.values()];
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function validateUnifiedPatchPaths(cwd: string, changes: UnifiedChange[]): void {
|
|
129
|
+
const workspace = resolve(cwd);
|
|
130
|
+
for (const change of changes) {
|
|
131
|
+
const absolutePath = resolvePatchPath(cwd, change.path);
|
|
132
|
+
if (!isPathInside(workspace, absolutePath)) throw new Error(`Patch path escapes workspace: ${change.path}`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function readPath(line: string, marker: string, lineNumber: number): string {
|
|
137
|
+
const path = line.slice(marker.length).trim();
|
|
138
|
+
if (!path) throw new Error(`Invalid patch line ${lineNumber}: missing path`);
|
|
139
|
+
return path;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function isFileOp(line: string): boolean {
|
|
143
|
+
const trimmed = line.trim();
|
|
144
|
+
return trimmed.startsWith(ADD) || trimmed.startsWith(DELETE) || trimmed.startsWith(UPDATE) || trimmed === END;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function parseUpdateChunk(
|
|
148
|
+
lines: string[],
|
|
149
|
+
startIndex: number,
|
|
150
|
+
lineNumber: number,
|
|
151
|
+
allowMissingContext: boolean,
|
|
152
|
+
): { hunk: Hunk; consumed: number } {
|
|
153
|
+
if (startIndex >= lines.length) throw new Error(`Invalid update hunk line ${lineNumber}: update hunk does not contain any lines`);
|
|
154
|
+
|
|
155
|
+
let header: string | undefined;
|
|
156
|
+
let index = startIndex;
|
|
157
|
+
if (lines[index] === "@@") {
|
|
158
|
+
index++;
|
|
159
|
+
} else if (lines[index]?.startsWith("@@ ")) {
|
|
160
|
+
header = (lines[index] ?? "").slice(3);
|
|
161
|
+
index++;
|
|
162
|
+
} else if (!allowMissingContext) {
|
|
163
|
+
throw new Error(`Invalid update hunk line ${lineNumber}: expected @@ header`);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (index >= lines.length) {
|
|
167
|
+
throw new Error(`Invalid update hunk line ${lineNumber + 1}: update hunk does not contain any lines`);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const hunkLines: HunkLine[] = [];
|
|
171
|
+
let endOfFile = false;
|
|
172
|
+
let parsedLines = 0;
|
|
173
|
+
|
|
174
|
+
for (; index < lines.length; index++) {
|
|
175
|
+
const current = lines[index] ?? "";
|
|
176
|
+
if (current === EOF_MARKER) {
|
|
177
|
+
if (parsedLines === 0) {
|
|
178
|
+
throw new Error(`Invalid update hunk line ${lineNumber + 1}: update hunk does not contain any lines`);
|
|
179
|
+
}
|
|
180
|
+
endOfFile = true;
|
|
181
|
+
parsedLines++;
|
|
182
|
+
index++;
|
|
183
|
+
break;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const prefix = current[0];
|
|
187
|
+
if (prefix === undefined) {
|
|
188
|
+
hunkLines.push({ kind: "context", text: "" });
|
|
189
|
+
} else if (prefix === " ") {
|
|
190
|
+
hunkLines.push({ kind: "context", text: current.slice(1) });
|
|
191
|
+
} else if (prefix === "-") {
|
|
192
|
+
hunkLines.push({ kind: "remove", text: current.slice(1) });
|
|
193
|
+
} else if (prefix === "+") {
|
|
194
|
+
hunkLines.push({ kind: "add", text: current.slice(1) });
|
|
195
|
+
} else {
|
|
196
|
+
if (parsedLines === 0) {
|
|
197
|
+
throw new Error(`Invalid update hunk line ${lineNumber + 1}: expected space, -, or + prefix`);
|
|
198
|
+
}
|
|
199
|
+
break;
|
|
200
|
+
}
|
|
201
|
+
parsedLines++;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return { hunk: { header, lines: hunkLines, endOfFile }, consumed: index - startIndex };
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export function parseApplyPatch(input: string): PatchOperation[] {
|
|
208
|
+
const lines = patchBodyLines(input);
|
|
209
|
+
const operations: PatchOperation[] = [];
|
|
210
|
+
let index = 0;
|
|
211
|
+
|
|
212
|
+
while (index < lines.length) {
|
|
213
|
+
const line = lines[index]?.trim() ?? "";
|
|
214
|
+
const lineNumber = index + 2;
|
|
215
|
+
|
|
216
|
+
if (line.startsWith(ADD)) {
|
|
217
|
+
const path = readPath(line, ADD, lineNumber);
|
|
218
|
+
index++;
|
|
219
|
+
const content: string[] = [];
|
|
220
|
+
while (index < lines.length) {
|
|
221
|
+
const contentLine = lines[index] ?? "";
|
|
222
|
+
if (!contentLine.startsWith("+")) break;
|
|
223
|
+
content.push(contentLine.slice(1));
|
|
224
|
+
index++;
|
|
225
|
+
}
|
|
226
|
+
operations.push({ kind: "add", path, lines: content });
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (line.startsWith(DELETE)) {
|
|
231
|
+
operations.push({ kind: "delete", path: readPath(line, DELETE, lineNumber) });
|
|
232
|
+
index++;
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (line.startsWith(UPDATE)) {
|
|
237
|
+
const path = readPath(line, UPDATE, lineNumber);
|
|
238
|
+
index++;
|
|
239
|
+
let moveTo: string | undefined;
|
|
240
|
+
if ((lines[index] ?? "").trim().startsWith(MOVE)) {
|
|
241
|
+
moveTo = readPath((lines[index] ?? "").trim(), MOVE, index + 2);
|
|
242
|
+
index++;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const hunks: Hunk[] = [];
|
|
246
|
+
while (index < lines.length) {
|
|
247
|
+
const current = lines[index] ?? "";
|
|
248
|
+
if (current.trim() === "") {
|
|
249
|
+
index++;
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
if (isFileOp(current) || current.startsWith("*")) break;
|
|
253
|
+
|
|
254
|
+
const parsed = parseUpdateChunk(lines, index, index + 2, hunks.length === 0);
|
|
255
|
+
hunks.push(parsed.hunk);
|
|
256
|
+
index += parsed.consumed;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (hunks.length === 0) {
|
|
260
|
+
throw new Error(`Invalid update for ${path}: update must include hunks`);
|
|
261
|
+
}
|
|
262
|
+
operations.push({ kind: "update", path, moveTo, hunks });
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
throw new Error(`Invalid patch line ${lineNumber}: expected file operation header`);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
return operations;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function resolvePatchPath(cwd: string, patchPath: string): string {
|
|
273
|
+
return resolve(cwd, patchPath);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function isEnoent(error: unknown): boolean {
|
|
277
|
+
return !!error && typeof error === "object" && "code" in error && error.code === "ENOENT";
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function assertNotAborted(signal?: AbortSignal): void {
|
|
281
|
+
if (signal?.aborted) throw new Error("Operation aborted");
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function splitContent(content: string): string[] {
|
|
285
|
+
const lines = content.split("\n");
|
|
286
|
+
if (lines.at(-1) === "") lines.pop();
|
|
287
|
+
return lines;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function joinContent(lines: string[]): string {
|
|
291
|
+
const next = [...lines];
|
|
292
|
+
if (next.at(-1) !== "") next.push("");
|
|
293
|
+
return next.join("\n");
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function hunkOldLines(hunk: Hunk): string[] {
|
|
297
|
+
return hunk.lines.filter((line) => line.kind !== "add").map((line) => line.text);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function hunkNewLines(hunk: Hunk): string[] {
|
|
301
|
+
return hunk.lines.filter((line) => line.kind !== "remove").map((line) => line.text);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function matchesAt(lines: string[], pattern: string[], index: number): boolean {
|
|
305
|
+
if (index + pattern.length > lines.length) return false;
|
|
306
|
+
for (let offset = 0; offset < pattern.length; offset++) {
|
|
307
|
+
if (lines[index + offset] !== pattern[offset]) return false;
|
|
308
|
+
}
|
|
309
|
+
return true;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function findHeaderStart(path: string, lines: string[], header: string | undefined, startAt: number): number {
|
|
313
|
+
if (!header) return startAt;
|
|
314
|
+
const match = findMatch(lines, [header], startAt, false);
|
|
315
|
+
if (match.kind === "missing") throw new Error(`Failed to find context '${header}' in ${path}`);
|
|
316
|
+
return match.index + 1;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function normalizeUnicodePunctuation(value: string): string {
|
|
320
|
+
return value
|
|
321
|
+
.trim()
|
|
322
|
+
.replace(/[\u2010\u2011\u2012\u2013\u2014\u2015\u2212]/g, "-")
|
|
323
|
+
.replace(/[\u2018\u2019\u201A\u201B]/g, "'")
|
|
324
|
+
.replace(/[\u201C\u201D\u201E\u201F]/g, '"')
|
|
325
|
+
.replace(/[\u00A0\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000]/g, " ");
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function findMatch(lines: string[], pattern: string[], startAt: number, endOfFile: boolean): Match {
|
|
329
|
+
if (pattern.length === 0) return { kind: "found", index: startAt };
|
|
330
|
+
if (pattern.length > lines.length) return { kind: "missing" };
|
|
331
|
+
|
|
332
|
+
const searchStart = endOfFile && lines.length >= pattern.length ? lines.length - pattern.length : startAt;
|
|
333
|
+
const normalizers = [(value: string) => value, (value: string) => value.trimEnd(), (value: string) => value.trim(), normalizeUnicodePunctuation];
|
|
334
|
+
for (const normalize of normalizers) {
|
|
335
|
+
const normalizedLines = lines.map(normalize);
|
|
336
|
+
const normalizedPattern = pattern.map(normalize);
|
|
337
|
+
for (let index = searchStart; index <= lines.length - pattern.length; index++) {
|
|
338
|
+
if (matchesAt(normalizedLines, normalizedPattern, index)) return { kind: "found", index };
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
return { kind: "missing" };
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function applyUpdateHunks(path: string, content: string, hunks: Hunk[]): string {
|
|
345
|
+
const originalLines = splitContent(content);
|
|
346
|
+
const replacements: Array<{ start: number; oldLength: number; newLines: string[] }> = [];
|
|
347
|
+
let lineIndex = 0;
|
|
348
|
+
|
|
349
|
+
for (const hunk of hunks) {
|
|
350
|
+
lineIndex = findHeaderStart(path, originalLines, hunk.header, lineIndex);
|
|
351
|
+
let oldLines = hunkOldLines(hunk);
|
|
352
|
+
let newLines = hunkNewLines(hunk);
|
|
353
|
+
|
|
354
|
+
if (oldLines.length === 0) {
|
|
355
|
+
replacements.push({ start: originalLines.length, oldLength: 0, newLines });
|
|
356
|
+
continue;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
let match = findMatch(originalLines, oldLines, lineIndex, hunk.endOfFile);
|
|
360
|
+
if (match.kind === "missing" && oldLines.at(-1) === "") {
|
|
361
|
+
oldLines = oldLines.slice(0, -1);
|
|
362
|
+
if (newLines.at(-1) === "") newLines = newLines.slice(0, -1);
|
|
363
|
+
match = findMatch(originalLines, oldLines, lineIndex, hunk.endOfFile);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
if (match.kind === "missing") {
|
|
367
|
+
throw new Error(`Failed to find expected lines in ${path}:\n${hunkOldLines(hunk).join("\n")}`);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
replacements.push({ start: match.index, oldLength: oldLines.length, newLines });
|
|
371
|
+
lineIndex = match.index + oldLines.length;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
const lines = [...originalLines];
|
|
375
|
+
replacements.sort((left, right) => left.start - right.start);
|
|
376
|
+
for (const replacement of replacements.reverse()) {
|
|
377
|
+
lines.splice(replacement.start, replacement.oldLength, ...replacement.newLines);
|
|
378
|
+
}
|
|
379
|
+
return joinContent(lines);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
async function assertDeletableFile(absolutePath: string, patchPath: string): Promise<void> {
|
|
383
|
+
let metadata: Awaited<ReturnType<typeof stat>>;
|
|
384
|
+
try {
|
|
385
|
+
metadata = await stat(absolutePath);
|
|
386
|
+
} catch (error) {
|
|
387
|
+
if (isEnoent(error)) throw new Error(`Cannot delete missing file: ${patchPath}`);
|
|
388
|
+
throw error;
|
|
389
|
+
}
|
|
390
|
+
if (metadata.isDirectory()) throw new Error(`Cannot delete directory: ${patchPath}`);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
async function readUpdateFile(absolutePath: string, patchPath: string): Promise<string> {
|
|
394
|
+
try {
|
|
395
|
+
return await readFile(absolutePath, "utf8");
|
|
396
|
+
} catch (error) {
|
|
397
|
+
if (isEnoent(error)) throw new Error(`Cannot update missing file: ${patchPath}`);
|
|
398
|
+
throw error;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
async function realpathIfExists(path: string): Promise<string | undefined> {
|
|
403
|
+
try {
|
|
404
|
+
return await realpath(path);
|
|
405
|
+
} catch (error) {
|
|
406
|
+
if (isEnoent(error)) return undefined;
|
|
407
|
+
throw error;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
async function planChanges(cwd: string, operations: PatchOperation[], signal?: AbortSignal): Promise<PlannedChange[]> {
|
|
412
|
+
if (operations.length === 0) throw new Error("No files were modified.");
|
|
413
|
+
|
|
414
|
+
const changes: PlannedChange[] = [];
|
|
415
|
+
const pendingContent = new Map<string, string | null>();
|
|
416
|
+
for (const operation of operations) {
|
|
417
|
+
assertNotAborted(signal);
|
|
418
|
+
const absolutePath = resolvePatchPath(cwd, operation.path);
|
|
419
|
+
|
|
420
|
+
if (operation.kind === "add") {
|
|
421
|
+
const content = operation.lines.length === 0 ? "" : `${operation.lines.join("\n")}\n`;
|
|
422
|
+
pendingContent.set(absolutePath, content);
|
|
423
|
+
changes.push({
|
|
424
|
+
kind: "write",
|
|
425
|
+
changeKind: "A",
|
|
426
|
+
path: operation.path,
|
|
427
|
+
absolutePath,
|
|
428
|
+
content,
|
|
429
|
+
});
|
|
430
|
+
continue;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
if (operation.kind === "delete") {
|
|
434
|
+
if (pendingContent.get(absolutePath) === null) throw new Error(`Cannot delete missing file: ${operation.path}`);
|
|
435
|
+
if (!pendingContent.has(absolutePath)) await assertDeletableFile(absolutePath, operation.path);
|
|
436
|
+
pendingContent.set(absolutePath, null);
|
|
437
|
+
changes.push({ kind: "delete", changeKind: "D", path: operation.path, absolutePath, summarize: true });
|
|
438
|
+
continue;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
const pending = pendingContent.get(absolutePath);
|
|
442
|
+
if (pending === null) throw new Error(`Cannot update missing file: ${operation.path}`);
|
|
443
|
+
const current = pending ?? (await readUpdateFile(absolutePath, operation.path));
|
|
444
|
+
assertNotAborted(signal);
|
|
445
|
+
const targetPath = operation.moveTo ?? operation.path;
|
|
446
|
+
const targetAbsolutePath = resolvePatchPath(cwd, targetPath);
|
|
447
|
+
if (operation.moveTo && targetAbsolutePath === absolutePath) {
|
|
448
|
+
throw new Error(`Cannot move file to itself: ${operation.path}`);
|
|
449
|
+
}
|
|
450
|
+
const content = applyUpdateHunks(operation.path, current, operation.hunks);
|
|
451
|
+
pendingContent.set(targetAbsolutePath, content);
|
|
452
|
+
changes.push({
|
|
453
|
+
kind: "write",
|
|
454
|
+
changeKind: "M",
|
|
455
|
+
path: targetPath,
|
|
456
|
+
absolutePath: targetAbsolutePath,
|
|
457
|
+
content,
|
|
458
|
+
});
|
|
459
|
+
if (operation.moveTo) {
|
|
460
|
+
if (!pendingContent.has(absolutePath)) await assertDeletableFile(absolutePath, operation.path);
|
|
461
|
+
pendingContent.set(absolutePath, null);
|
|
462
|
+
changes.push({ kind: "delete", changeKind: "D", path: operation.path, absolutePath, summarize: false });
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
return changes;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
async function writeChange(change: PlannedWrite, signal?: AbortSignal): Promise<void> {
|
|
469
|
+
assertNotAborted(signal);
|
|
470
|
+
await mkdir(dirname(change.absolutePath), { recursive: true });
|
|
471
|
+
await writeFile(change.absolutePath, change.content, { encoding: "utf8" });
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
async function withMutationQueues(paths: string[], fn: () => Promise<void>): Promise<void> {
|
|
475
|
+
const [first, ...rest] = [...new Set(paths)].sort();
|
|
476
|
+
if (!first) return fn();
|
|
477
|
+
return withFileMutationQueue(first, () => withMutationQueues(rest, fn));
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
async function operationPaths(cwd: string, operations: PatchOperation[]): Promise<string[]> {
|
|
481
|
+
const paths: string[] = [];
|
|
482
|
+
for (const operation of operations) {
|
|
483
|
+
const absolutePath = resolvePatchPath(cwd, operation.path);
|
|
484
|
+
const realPath = await realpathIfExists(absolutePath);
|
|
485
|
+
paths.push(realPath ?? absolutePath);
|
|
486
|
+
if (operation.kind === "update" && operation.moveTo) {
|
|
487
|
+
const targetAbsolutePath = resolvePatchPath(cwd, operation.moveTo);
|
|
488
|
+
const targetRealPath = await realpathIfExists(targetAbsolutePath);
|
|
489
|
+
paths.push(targetRealPath ?? targetAbsolutePath);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
return paths;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
function summarizedChanges(changes: PlannedChange[]): PlannedChange[] {
|
|
496
|
+
return changes.filter((change) => change.kind === "write" || change.summarize);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
function summaryForChanges(changes: PlannedChange[]): string {
|
|
500
|
+
const lines = ["Success. Updated the following files:"];
|
|
501
|
+
const visible = summarizedChanges(changes);
|
|
502
|
+
for (const changeKind of ["A", "M", "D"] as const) {
|
|
503
|
+
for (const change of visible) {
|
|
504
|
+
if (change.changeKind === changeKind) lines.push(`${change.changeKind} ${change.path}`);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
return `${lines.join("\n")}\n`;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
function summaryForUnifiedChanges(changes: UnifiedChange[]): string {
|
|
511
|
+
const lines = ["Success. Updated the following files:"];
|
|
512
|
+
for (const changeKind of ["A", "M", "D"] as const) {
|
|
513
|
+
for (const change of changes) {
|
|
514
|
+
if (change.changeKind === changeKind) lines.push(`${change.changeKind} ${change.path}`);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
return `${lines.join("\n")}\n`;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
function runGitApply(cwd: string, patchFile: string, signal?: AbortSignal): Promise<void> {
|
|
521
|
+
return new Promise((resolvePromise, reject) => {
|
|
522
|
+
const child = spawn("git", ["apply", "--no-index", "--whitespace=nowarn", "--recount", patchFile], { cwd, signal });
|
|
523
|
+
let stderr = "";
|
|
524
|
+
let settled = false;
|
|
525
|
+
|
|
526
|
+
const fail = (error: Error) => {
|
|
527
|
+
if (settled) return;
|
|
528
|
+
settled = true;
|
|
529
|
+
reject(error);
|
|
530
|
+
};
|
|
531
|
+
const succeed = () => {
|
|
532
|
+
if (settled) return;
|
|
533
|
+
settled = true;
|
|
534
|
+
resolvePromise();
|
|
535
|
+
};
|
|
536
|
+
|
|
537
|
+
child.stderr?.setEncoding("utf8");
|
|
538
|
+
child.stderr?.on("data", (chunk) => { stderr += String(chunk); });
|
|
539
|
+
child.on("error", (error) => {
|
|
540
|
+
if (signal?.aborted) fail(new Error("Operation aborted"));
|
|
541
|
+
else fail(error);
|
|
542
|
+
});
|
|
543
|
+
child.on("close", (code, signalName) => {
|
|
544
|
+
if (code === 0) {
|
|
545
|
+
succeed();
|
|
546
|
+
return;
|
|
547
|
+
}
|
|
548
|
+
const suffix = signalName ? ` (signal ${signalName})` : code === null ? "" : ` (exit ${code})`;
|
|
549
|
+
fail(new Error(stderr.trim() || `git apply failed${suffix}`));
|
|
550
|
+
});
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
async function applyUnifiedPatch(cwd: string, input: string, signal?: AbortSignal): Promise<ApplyPatchResult> {
|
|
555
|
+
assertNotAborted(signal);
|
|
556
|
+
const changes = extractUnifiedChanges(input);
|
|
557
|
+
if (changes.length === 0) throw new Error("Invalid unified diff: no changed files found");
|
|
558
|
+
validateUnifiedPatchPaths(cwd, changes);
|
|
559
|
+
|
|
560
|
+
const tempDir = await mkdtemp(join(tmpdir(), "pi-apply-patch-"));
|
|
561
|
+
const patchFile = join(tempDir, "patch.diff");
|
|
562
|
+
try {
|
|
563
|
+
await writeFile(patchFile, input, "utf8");
|
|
564
|
+
await runGitApply(cwd, patchFile, signal);
|
|
565
|
+
} finally {
|
|
566
|
+
await rm(tempDir, { recursive: true, force: true }).catch(() => undefined);
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
return { changedFiles: changes.map((change) => change.path), summary: summaryForUnifiedChanges(changes) };
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
export async function applyPatch(cwd: string, input: string, signal?: AbortSignal): Promise<ApplyPatchResult> {
|
|
573
|
+
if (!looksLikeBeginPatch(input)) return applyUnifiedPatch(cwd, input, signal);
|
|
574
|
+
|
|
575
|
+
const operations = parseApplyPatch(input);
|
|
576
|
+
const paths = await operationPaths(cwd, operations);
|
|
577
|
+
let changes: PlannedChange[] = [];
|
|
578
|
+
|
|
579
|
+
await withMutationQueues(paths, async () => {
|
|
580
|
+
changes = await planChanges(cwd, operations, signal);
|
|
581
|
+
for (const change of changes) {
|
|
582
|
+
assertNotAborted(signal);
|
|
583
|
+
if (change.kind === "delete") await rm(change.absolutePath, { force: false });
|
|
584
|
+
else await writeChange(change, signal);
|
|
585
|
+
}
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
const changedFiles = [...new Set(summarizedChanges(changes).map((change) => change.path))];
|
|
589
|
+
return { changedFiles, summary: summaryForChanges(changes) };
|
|
590
|
+
}
|