@dungle-scrubs/tallow 0.8.23 → 0.8.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config.d.ts +1 -1
- package/dist/config.js +1 -1
- package/dist/workspace-transition-interactive.d.ts +1 -0
- package/dist/workspace-transition-interactive.d.ts.map +1 -1
- package/dist/workspace-transition-interactive.js +3 -0
- package/dist/workspace-transition-interactive.js.map +1 -1
- package/extensions/_shared/__tests__/shell-policy.test.ts +16 -13
- package/extensions/_shared/shell-policy.ts +8 -3
- package/extensions/hooks/__tests__/stale-cwd.test.ts +108 -0
- package/extensions/hooks/index.ts +52 -2
- package/extensions/plan-mode-tool/__tests__/index.test.ts +0 -1
- package/extensions/plan-mode-tool/__tests__/utils.test.ts +0 -57
- package/extensions/plan-mode-tool/extension.json +1 -10
- package/extensions/plan-mode-tool/index.ts +11 -284
- package/extensions/plan-mode-tool/utils.ts +0 -29
- package/extensions/skill-commands/__tests__/shared-skills-dirs.test.ts +113 -0
- package/extensions/skill-commands/index.ts +62 -5
- package/node_modules/.bun/mime-types@3.0.2/node_modules/mime-db/HISTORY.md +541 -0
- package/node_modules/.bun/mime-types@3.0.2/node_modules/mime-db/LICENSE +23 -0
- package/node_modules/.bun/mime-types@3.0.2/node_modules/mime-db/README.md +109 -0
- package/node_modules/.bun/mime-types@3.0.2/node_modules/mime-db/db.json +9342 -0
- package/node_modules/.bun/mime-types@3.0.2/node_modules/mime-db/index.js +12 -0
- package/node_modules/.bun/mime-types@3.0.2/node_modules/mime-db/package.json +56 -0
- package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/keys.d.ts.map +1 -1
- package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/keys.js +59 -7
- package/node_modules/@mariozechner/pi-tui/dist/keys.js.map +1 -0
- package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/tui.d.ts +13 -0
- package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/tui.d.ts.map +1 -1
- package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/tui.js +22 -2
- package/node_modules/@mariozechner/pi-tui/dist/tui.js.map +1 -0
- package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/package.json +1 -1
- package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/src/__tests__/tui-diff-regression.test.ts +52 -0
- package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/src/keys.ts +71 -7
- package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/src/tui.ts +23 -2
- package/package.json +11 -9
- package/packages/tallow-tui/node_modules/@types/mime-types/LICENSE +21 -0
- package/packages/tallow-tui/node_modules/@types/mime-types/README.md +28 -0
- package/packages/tallow-tui/node_modules/@types/mime-types/index.d.ts +9 -0
- package/packages/tallow-tui/node_modules/@types/mime-types/package.json +25 -0
- package/packages/tallow-tui/node_modules/chalk/license +9 -0
- package/packages/tallow-tui/node_modules/chalk/package.json +83 -0
- package/packages/tallow-tui/node_modules/chalk/readme.md +297 -0
- package/packages/tallow-tui/node_modules/chalk/source/index.d.ts +325 -0
- package/packages/tallow-tui/node_modules/chalk/source/index.js +225 -0
- package/packages/tallow-tui/node_modules/chalk/source/utilities.js +33 -0
- package/packages/tallow-tui/node_modules/chalk/source/vendor/ansi-styles/index.d.ts +236 -0
- package/packages/tallow-tui/node_modules/chalk/source/vendor/ansi-styles/index.js +223 -0
- package/packages/tallow-tui/node_modules/chalk/source/vendor/supports-color/browser.d.ts +1 -0
- package/packages/tallow-tui/node_modules/chalk/source/vendor/supports-color/browser.js +34 -0
- package/packages/tallow-tui/node_modules/chalk/source/vendor/supports-color/index.d.ts +55 -0
- package/packages/tallow-tui/node_modules/chalk/source/vendor/supports-color/index.js +190 -0
- package/packages/tallow-tui/node_modules/get-east-asian-width/index.d.ts +60 -0
- package/packages/tallow-tui/node_modules/get-east-asian-width/index.js +30 -0
- package/packages/tallow-tui/node_modules/get-east-asian-width/license +9 -0
- package/packages/tallow-tui/node_modules/get-east-asian-width/lookup.js +403 -0
- package/packages/tallow-tui/node_modules/get-east-asian-width/package.json +70 -0
- package/packages/tallow-tui/node_modules/get-east-asian-width/readme.md +65 -0
- package/packages/tallow-tui/node_modules/marked/LICENSE.md +44 -0
- package/packages/tallow-tui/node_modules/marked/README.md +106 -0
- package/packages/tallow-tui/node_modules/marked/bin/main.js +282 -0
- package/packages/tallow-tui/node_modules/marked/bin/marked.js +15 -0
- package/packages/tallow-tui/node_modules/marked/lib/marked.cjs +2211 -0
- package/packages/tallow-tui/node_modules/marked/lib/marked.cjs.map +7 -0
- package/packages/tallow-tui/node_modules/marked/lib/marked.d.cts +728 -0
- package/packages/tallow-tui/node_modules/marked/lib/marked.d.ts +728 -0
- package/packages/tallow-tui/node_modules/marked/lib/marked.esm.js +2189 -0
- package/packages/tallow-tui/node_modules/marked/lib/marked.esm.js.map +7 -0
- package/packages/tallow-tui/node_modules/marked/lib/marked.umd.js +2213 -0
- package/packages/tallow-tui/node_modules/marked/lib/marked.umd.js.map +7 -0
- package/packages/tallow-tui/node_modules/marked/man/marked.1 +111 -0
- package/packages/tallow-tui/node_modules/marked/man/marked.1.md +92 -0
- package/packages/tallow-tui/node_modules/marked/marked.min.js +69 -0
- package/packages/tallow-tui/node_modules/marked/package.json +111 -0
- package/packages/tallow-tui/node_modules/mime-types/HISTORY.md +428 -0
- package/packages/tallow-tui/node_modules/mime-types/LICENSE +23 -0
- package/packages/tallow-tui/node_modules/mime-types/README.md +126 -0
- package/packages/tallow-tui/node_modules/mime-types/index.js +211 -0
- package/packages/tallow-tui/node_modules/mime-types/mimeScore.js +57 -0
- package/packages/tallow-tui/node_modules/mime-types/package.json +49 -0
- package/extensions/__integration__/plan-rejection-feedback.test.ts +0 -272
- package/extensions/plan-mode-tool/__tests__/agent-end-execution.test.ts +0 -373
- package/packages/tallow-tui/dist/keys.js.map +0 -1
- package/packages/tallow-tui/dist/tui.js.map +0 -1
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/README.md +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/autocomplete.d.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/autocomplete.d.ts.map +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/autocomplete.js +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/autocomplete.js.map +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/border-styles.d.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/border-styles.d.ts.map +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/border-styles.js +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/border-styles.js.map +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/components/bordered-box.d.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/components/bordered-box.d.ts.map +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/components/bordered-box.js +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/components/bordered-box.js.map +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/components/box.d.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/components/box.d.ts.map +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/components/box.js +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/components/box.js.map +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/components/cancellable-loader.d.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/components/cancellable-loader.d.ts.map +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/components/cancellable-loader.js +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/components/cancellable-loader.js.map +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/components/editor.d.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/components/editor.d.ts.map +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/components/editor.js +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/components/editor.js.map +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/components/image.d.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/components/image.d.ts.map +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/components/image.js +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/components/image.js.map +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/components/input.d.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/components/input.d.ts.map +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/components/input.js +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/components/input.js.map +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/components/loader.d.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/components/loader.d.ts.map +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/components/loader.js +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/components/loader.js.map +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/components/markdown.d.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/components/markdown.d.ts.map +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/components/markdown.js +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/components/markdown.js.map +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/components/select-list.d.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/components/select-list.d.ts.map +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/components/select-list.js +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/components/select-list.js.map +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/components/settings-list.d.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/components/settings-list.d.ts.map +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/components/settings-list.js +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/components/settings-list.js.map +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/components/spacer.d.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/components/spacer.d.ts.map +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/components/spacer.js +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/components/spacer.js.map +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/components/text.d.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/components/text.d.ts.map +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/components/text.js +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/components/text.js.map +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/components/truncated-text.d.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/components/truncated-text.d.ts.map +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/components/truncated-text.js +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/components/truncated-text.js.map +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/editor-component.d.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/editor-component.d.ts.map +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/editor-component.js +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/editor-component.js.map +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/fuzzy.d.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/fuzzy.d.ts.map +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/fuzzy.js +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/fuzzy.js.map +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/index.d.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/index.d.ts.map +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/index.js +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/index.js.map +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/keybindings.d.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/keybindings.d.ts.map +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/keybindings.js +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/keybindings.js.map +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/keys.d.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/kill-ring.d.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/kill-ring.d.ts.map +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/kill-ring.js +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/kill-ring.js.map +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/stdin-buffer.d.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/stdin-buffer.d.ts.map +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/stdin-buffer.js +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/stdin-buffer.js.map +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/terminal-image.d.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/terminal-image.d.ts.map +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/terminal-image.js +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/terminal-image.js.map +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/terminal.d.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/terminal.d.ts.map +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/terminal.js +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/terminal.js.map +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/test-utils/capability-env.d.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/test-utils/capability-env.d.ts.map +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/test-utils/capability-env.js +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/test-utils/capability-env.js.map +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/undo-stack.d.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/undo-stack.d.ts.map +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/undo-stack.js +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/undo-stack.js.map +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/utils.d.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/utils.d.ts.map +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/utils.js +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/dist/utils.js.map +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/src/__tests__/__snapshots__/render.test.ts.snap +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/src/__tests__/editor-border.test.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/src/__tests__/editor-change-listener.test.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/src/__tests__/editor-ghost-text.test.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/src/__tests__/fuzzy.test.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/src/__tests__/image-component.test.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/src/__tests__/keys.test.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/src/__tests__/render.test.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/src/__tests__/stdin-buffer.test.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/src/__tests__/terminal-image.test.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/src/__tests__/tui-render-scheduling.test.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/src/__tests__/utils.test.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/src/autocomplete.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/src/border-styles.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/src/components/bordered-box.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/src/components/box.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/src/components/cancellable-loader.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/src/components/editor.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/src/components/image.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/src/components/input.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/src/components/loader.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/src/components/markdown.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/src/components/select-list.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/src/components/settings-list.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/src/components/spacer.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/src/components/text.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/src/components/truncated-text.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/src/editor-component.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/src/fuzzy.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/src/index.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/src/keybindings.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/src/kill-ring.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/src/stdin-buffer.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/src/terminal-image.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/src/terminal.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/src/test-utils/capability-env.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/src/undo-stack.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/src/utils.ts +0 -0
- /package/{packages/tallow-tui → node_modules/@mariozechner/pi-tui}/tsconfig.build.json +0 -0
|
@@ -9,8 +9,7 @@
|
|
|
9
9
|
* - Strict fail-closed tool allowlist while plan mode is active
|
|
10
10
|
* - Bash restricted to allowlisted read-only commands
|
|
11
11
|
* - Extracts numbered plan steps from "Plan:" sections
|
|
12
|
-
* -
|
|
13
|
-
* - Progress tracking widget during execution
|
|
12
|
+
* - Delegates execution tracking to the tasks extension
|
|
14
13
|
*/
|
|
15
14
|
|
|
16
15
|
import type { AgentMessage } from "@mariozechner/pi-agent-core";
|
|
@@ -37,7 +36,6 @@ import {
|
|
|
37
36
|
extractTodoItems,
|
|
38
37
|
isPlanModeToolAllowed,
|
|
39
38
|
isSafeCommand,
|
|
40
|
-
markCompletedSteps,
|
|
41
39
|
PLAN_MODE_ALLOWED_TOOLS,
|
|
42
40
|
stripPlanIntent,
|
|
43
41
|
type TodoItem,
|
|
@@ -103,9 +101,7 @@ class PlanModeEditor extends CustomEditor {
|
|
|
103
101
|
*/
|
|
104
102
|
export default function planModeExtension(pi: ExtensionAPI): void {
|
|
105
103
|
let planModeEnabled = false;
|
|
106
|
-
let executionMode = false;
|
|
107
104
|
let todoItems: TodoItem[] = [];
|
|
108
|
-
let currentStepIndex: number | null = null;
|
|
109
105
|
let normalModeTools: string[] = [];
|
|
110
106
|
|
|
111
107
|
/**
|
|
@@ -154,45 +150,6 @@ export default function planModeExtension(pi: ExtensionAPI): void {
|
|
|
154
150
|
return normalModeTools;
|
|
155
151
|
}
|
|
156
152
|
|
|
157
|
-
/**
|
|
158
|
-
* Computes the current step index from todo completion state.
|
|
159
|
-
*
|
|
160
|
-
* @param items - Ordered todo items
|
|
161
|
-
* @returns Zero-based index of first incomplete step, or null when complete
|
|
162
|
-
*/
|
|
163
|
-
function computeCurrentStepIndex(items: readonly TodoItem[]): number | null {
|
|
164
|
-
const nextIndex = items.findIndex((item) => !item.completed);
|
|
165
|
-
return nextIndex >= 0 ? nextIndex : null;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* Synchronizes currentStepIndex with todo completion state.
|
|
170
|
-
*
|
|
171
|
-
* @returns Updated current step index
|
|
172
|
-
*/
|
|
173
|
-
function syncCurrentStepIndex(): number | null {
|
|
174
|
-
currentStepIndex = computeCurrentStepIndex(todoItems);
|
|
175
|
-
return currentStepIndex;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* Gets the current step item for execution guidance.
|
|
180
|
-
*
|
|
181
|
-
* @returns Current step todo item, or null if all steps are complete
|
|
182
|
-
*/
|
|
183
|
-
function getCurrentStep(): TodoItem | null {
|
|
184
|
-
if (todoItems.length === 0) return null;
|
|
185
|
-
if (
|
|
186
|
-
currentStepIndex === null ||
|
|
187
|
-
currentStepIndex < 0 ||
|
|
188
|
-
currentStepIndex >= todoItems.length ||
|
|
189
|
-
todoItems[currentStepIndex]?.completed
|
|
190
|
-
) {
|
|
191
|
-
syncCurrentStepIndex();
|
|
192
|
-
}
|
|
193
|
-
return currentStepIndex === null ? null : (todoItems[currentStepIndex] ?? null);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
153
|
pi.registerFlag("plan", {
|
|
197
154
|
description: "Start in plan mode (read-only exploration)",
|
|
198
155
|
type: "boolean",
|
|
@@ -206,7 +163,7 @@ export default function planModeExtension(pi: ExtensionAPI): void {
|
|
|
206
163
|
* @param ctx - The extension context
|
|
207
164
|
*/
|
|
208
165
|
function updateStatus(ctx: ExtensionContext): void {
|
|
209
|
-
// Footer status — plan mode only
|
|
166
|
+
// Footer status — plan mode only
|
|
210
167
|
if (planModeEnabled) {
|
|
211
168
|
ctx.ui.setStatus(
|
|
212
169
|
"plan-mode",
|
|
@@ -226,7 +183,6 @@ export default function planModeExtension(pi: ExtensionAPI): void {
|
|
|
226
183
|
}
|
|
227
184
|
|
|
228
185
|
// Full-width banner above editor — plan mode only.
|
|
229
|
-
// Execution mode does not show a banner; the tasks extension owns progress tracking.
|
|
230
186
|
if (planModeEnabled) {
|
|
231
187
|
ctx.ui.setWidget("plan-banner", (_tui, theme) => {
|
|
232
188
|
const label = " PLAN MODE — READ ONLY ";
|
|
@@ -241,7 +197,6 @@ export default function planModeExtension(pi: ExtensionAPI): void {
|
|
|
241
197
|
ctx.ui.setWidget("plan-banner", undefined);
|
|
242
198
|
}
|
|
243
199
|
|
|
244
|
-
// Todo list widget — plan mode only (execution delegates to tasks extension)
|
|
245
200
|
ctx.ui.setWidget("plan-todos", undefined);
|
|
246
201
|
}
|
|
247
202
|
|
|
@@ -251,9 +206,7 @@ export default function planModeExtension(pi: ExtensionAPI): void {
|
|
|
251
206
|
*/
|
|
252
207
|
function togglePlanMode(ctx: ExtensionContext): void {
|
|
253
208
|
planModeEnabled = !planModeEnabled;
|
|
254
|
-
executionMode = false;
|
|
255
209
|
todoItems = [];
|
|
256
|
-
currentStepIndex = null;
|
|
257
210
|
|
|
258
211
|
if (planModeEnabled) {
|
|
259
212
|
captureNormalModeTools();
|
|
@@ -273,10 +226,8 @@ export default function planModeExtension(pi: ExtensionAPI): void {
|
|
|
273
226
|
function persistState(): void {
|
|
274
227
|
pi.appendEntry("plan-mode", {
|
|
275
228
|
enabled: planModeEnabled,
|
|
276
|
-
executing: executionMode,
|
|
277
229
|
normalTools: normalModeTools,
|
|
278
230
|
todos: todoItems,
|
|
279
|
-
currentStepIndex,
|
|
280
231
|
});
|
|
281
232
|
}
|
|
282
233
|
|
|
@@ -341,7 +292,7 @@ Use action "enable" to enter plan mode, "disable" to exit, or "status" to check
|
|
|
341
292
|
const { action } = params;
|
|
342
293
|
|
|
343
294
|
if (action === "status") {
|
|
344
|
-
const mode =
|
|
295
|
+
const mode = planModeEnabled ? "planning" : "normal";
|
|
345
296
|
const tools = planModeEnabled ? getPlanModeTools() : pi.getActiveTools();
|
|
346
297
|
return {
|
|
347
298
|
content: [
|
|
@@ -373,9 +324,7 @@ Use action "enable" to enter plan mode, "disable" to exit, or "status" to check
|
|
|
373
324
|
}
|
|
374
325
|
|
|
375
326
|
planModeEnabled = shouldEnable;
|
|
376
|
-
executionMode = false;
|
|
377
327
|
todoItems = [];
|
|
378
|
-
currentStepIndex = null;
|
|
379
328
|
|
|
380
329
|
let activeTools: string[];
|
|
381
330
|
if (planModeEnabled) {
|
|
@@ -431,8 +380,8 @@ Use action "enable" to enter plan mode, "disable" to exit, or "status" to check
|
|
|
431
380
|
|
|
432
381
|
// Auto-enable plan mode when user expresses planning intent in natural language
|
|
433
382
|
pi.on("input", async (event, ctx) => {
|
|
434
|
-
// No-op if already in plan mode
|
|
435
|
-
if (planModeEnabled
|
|
383
|
+
// No-op if already in plan mode
|
|
384
|
+
if (planModeEnabled) {
|
|
436
385
|
return { action: "continue" as const };
|
|
437
386
|
}
|
|
438
387
|
|
|
@@ -513,188 +462,10 @@ Do NOT attempt to make changes - just describe what you would do.`,
|
|
|
513
462
|
},
|
|
514
463
|
};
|
|
515
464
|
}
|
|
516
|
-
|
|
517
|
-
if (executionMode && todoItems.length > 0) {
|
|
518
|
-
syncCurrentStepIndex();
|
|
519
|
-
const currentStep = getCurrentStep();
|
|
520
|
-
const remaining = todoItems.filter((t) => !t.completed);
|
|
521
|
-
const todoList = remaining.map((t) => `${t.step}. ${t.text}`).join("\n");
|
|
522
|
-
const currentStepText = currentStep
|
|
523
|
-
? `${currentStep.step}. ${currentStep.text}`
|
|
524
|
-
: "(all steps completed)";
|
|
525
|
-
return {
|
|
526
|
-
message: {
|
|
527
|
-
customType: "plan-execution-context",
|
|
528
|
-
content: `[EXECUTING PLAN - Full tool access enabled]
|
|
529
|
-
|
|
530
|
-
Current step:
|
|
531
|
-
${currentStepText}
|
|
532
|
-
|
|
533
|
-
Remaining steps:
|
|
534
|
-
${todoList}
|
|
535
|
-
|
|
536
|
-
Execute each step in order.
|
|
537
|
-
After completing a step, include a [DONE:n] tag in your response.
|
|
538
|
-
If you receive [PLAN GUIDANCE — Step n: ...], treat it as user steering for that step and apply it before continuing.`,
|
|
539
|
-
display: false,
|
|
540
|
-
},
|
|
541
|
-
};
|
|
542
|
-
}
|
|
543
|
-
});
|
|
544
|
-
|
|
545
|
-
// Track progress after each turn
|
|
546
|
-
pi.on("turn_end", async (event, ctx) => {
|
|
547
|
-
if (!executionMode || todoItems.length === 0) return;
|
|
548
|
-
if (!isAssistantMessage(event.message)) return;
|
|
549
|
-
|
|
550
|
-
const text = getTextContent(event.message);
|
|
551
|
-
const completedCount = markCompletedSteps(text, todoItems);
|
|
552
|
-
if (completedCount > 0) {
|
|
553
|
-
syncCurrentStepIndex();
|
|
554
|
-
updateStatus(ctx);
|
|
555
|
-
}
|
|
556
|
-
persistState();
|
|
557
|
-
});
|
|
558
|
-
|
|
559
|
-
// Offer user steering when a tool is blocked during tracked execution.
|
|
560
|
-
pi.on("tool_result", async (event, ctx) => {
|
|
561
|
-
if (!executionMode || !event.isError || !ctx.hasUI || todoItems.length === 0) return;
|
|
562
|
-
|
|
563
|
-
const currentStep = getCurrentStep();
|
|
564
|
-
if (!currentStep) return;
|
|
565
|
-
|
|
566
|
-
const errorText = Array.isArray(event.content)
|
|
567
|
-
? event.content
|
|
568
|
-
.filter((block): block is TextContent => block.type === "text")
|
|
569
|
-
.map((block) => block.text)
|
|
570
|
-
.join("\n")
|
|
571
|
-
.trim()
|
|
572
|
-
: "";
|
|
573
|
-
|
|
574
|
-
const shouldProvideGuidance = await ctx.ui.confirm(
|
|
575
|
-
"Plan step blocked",
|
|
576
|
-
[
|
|
577
|
-
`Tool "${event.toolName}" was blocked while executing step ${currentStep.step}.`,
|
|
578
|
-
"",
|
|
579
|
-
currentStep.text,
|
|
580
|
-
"",
|
|
581
|
-
`Error: ${errorText || "No error details provided."}`,
|
|
582
|
-
"",
|
|
583
|
-
"Provide guidance before execution continues?",
|
|
584
|
-
].join("\n")
|
|
585
|
-
);
|
|
586
|
-
if (!shouldProvideGuidance) return;
|
|
587
|
-
|
|
588
|
-
const guidance = await ctx.ui.editor(`Guidance for step ${currentStep.step}:`, "");
|
|
589
|
-
const trimmedGuidance = guidance?.trim();
|
|
590
|
-
if (!trimmedGuidance) return;
|
|
591
|
-
|
|
592
|
-
pi.sendUserMessage(
|
|
593
|
-
[
|
|
594
|
-
`[PLAN GUIDANCE — Step ${currentStep.step}: ${currentStep.text}]`,
|
|
595
|
-
`Tool "${event.toolName}" was blocked.`,
|
|
596
|
-
"",
|
|
597
|
-
"User guidance:",
|
|
598
|
-
trimmedGuidance,
|
|
599
|
-
].join("\n"),
|
|
600
|
-
{ deliverAs: "steer" }
|
|
601
|
-
);
|
|
602
465
|
});
|
|
603
466
|
|
|
604
467
|
// Handle plan completion and plan mode UI
|
|
605
468
|
pi.on("agent_end", async (event, ctx) => {
|
|
606
|
-
// Check if execution is complete
|
|
607
|
-
if (executionMode && todoItems.length > 0) {
|
|
608
|
-
if (todoItems.every((t) => t.completed)) {
|
|
609
|
-
const completedList = todoItems.map((t) => `~~${t.text}~~`).join("\n");
|
|
610
|
-
pi.sendMessage(
|
|
611
|
-
{
|
|
612
|
-
customType: "plan-complete",
|
|
613
|
-
content: `**Plan Complete!** ${getIcon("success")}\n\n${completedList}`,
|
|
614
|
-
display: true,
|
|
615
|
-
},
|
|
616
|
-
{ triggerTurn: false }
|
|
617
|
-
);
|
|
618
|
-
executionMode = false;
|
|
619
|
-
todoItems = [];
|
|
620
|
-
currentStepIndex = null;
|
|
621
|
-
restoreNormalModeTools();
|
|
622
|
-
updateStatus(ctx);
|
|
623
|
-
persistState();
|
|
624
|
-
} else if (ctx.hasUI) {
|
|
625
|
-
// Agent finished its turn but not all steps are marked complete.
|
|
626
|
-
// Show progress summary and prompt for next action.
|
|
627
|
-
const completed = todoItems.filter((t) => t.completed);
|
|
628
|
-
|
|
629
|
-
updateStatus(ctx);
|
|
630
|
-
ctx.ui.setWorkingMessage(Loader.HIDE);
|
|
631
|
-
|
|
632
|
-
const choice = await ctx.ui.select(
|
|
633
|
-
`Plan execution paused (${completed.length}/${todoItems.length} done)`,
|
|
634
|
-
["Continue execution", "Provide guidance", "Mark plan as done", "Abort plan"]
|
|
635
|
-
);
|
|
636
|
-
|
|
637
|
-
if (choice === "Continue execution") {
|
|
638
|
-
const nextStep = getCurrentStep();
|
|
639
|
-
const continueMessage =
|
|
640
|
-
nextStep !== null
|
|
641
|
-
? `Continue executing the plan. Next: step ${nextStep.step}: ${nextStep.text}`
|
|
642
|
-
: "Continue executing the remaining plan steps.";
|
|
643
|
-
pi.sendMessage(
|
|
644
|
-
{ customType: "plan-mode-execute", content: continueMessage, display: true },
|
|
645
|
-
{ triggerTurn: true }
|
|
646
|
-
);
|
|
647
|
-
} else if (choice === "Provide guidance") {
|
|
648
|
-
const nextStep = getCurrentStep();
|
|
649
|
-
const stepLabel = nextStep ? `step ${nextStep.step} (${nextStep.text})` : "next step";
|
|
650
|
-
const guidance = await ctx.ui.editor(`Guidance for ${stepLabel}:`, "");
|
|
651
|
-
const trimmedGuidance = guidance?.trim();
|
|
652
|
-
if (trimmedGuidance && nextStep) {
|
|
653
|
-
pi.sendUserMessage(
|
|
654
|
-
[
|
|
655
|
-
`[PLAN GUIDANCE — Step ${nextStep.step}: ${nextStep.text}]`,
|
|
656
|
-
"",
|
|
657
|
-
"User guidance:",
|
|
658
|
-
trimmedGuidance,
|
|
659
|
-
].join("\n")
|
|
660
|
-
);
|
|
661
|
-
} else if (trimmedGuidance) {
|
|
662
|
-
pi.sendUserMessage(trimmedGuidance);
|
|
663
|
-
} else {
|
|
664
|
-
ctx.ui.notify("No guidance provided. Plan unchanged.", "info");
|
|
665
|
-
}
|
|
666
|
-
} else if (choice === "Mark plan as done") {
|
|
667
|
-
const completedList = todoItems
|
|
668
|
-
.map((t) => (t.completed ? `~~${t.text}~~` : t.text))
|
|
669
|
-
.join("\n");
|
|
670
|
-
pi.sendMessage(
|
|
671
|
-
{
|
|
672
|
-
customType: "plan-complete",
|
|
673
|
-
content: `**Plan Complete!** ${getIcon("success")}\n\n${completedList}`,
|
|
674
|
-
display: true,
|
|
675
|
-
},
|
|
676
|
-
{ triggerTurn: false }
|
|
677
|
-
);
|
|
678
|
-
executionMode = false;
|
|
679
|
-
todoItems = [];
|
|
680
|
-
currentStepIndex = null;
|
|
681
|
-
restoreNormalModeTools();
|
|
682
|
-
updateStatus(ctx);
|
|
683
|
-
persistState();
|
|
684
|
-
} else {
|
|
685
|
-
// Abort plan (or dismissed/escaped the select)
|
|
686
|
-
executionMode = false;
|
|
687
|
-
todoItems = [];
|
|
688
|
-
currentStepIndex = null;
|
|
689
|
-
restoreNormalModeTools();
|
|
690
|
-
updateStatus(ctx);
|
|
691
|
-
persistState();
|
|
692
|
-
ctx.ui.notify("Plan aborted.", "info");
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
|
-
return;
|
|
696
|
-
}
|
|
697
|
-
|
|
698
469
|
if (!(planModeEnabled && ctx.hasUI)) return;
|
|
699
470
|
|
|
700
471
|
// Extract todos from last assistant message
|
|
@@ -727,7 +498,7 @@ If you receive [PLAN GUIDANCE — Step n: ...], treat it as user steering for th
|
|
|
727
498
|
ctx.ui.setWorkingMessage(Loader.HIDE);
|
|
728
499
|
|
|
729
500
|
const choice = await ctx.ui.select("Plan mode - what next?", [
|
|
730
|
-
|
|
501
|
+
"Execute the plan",
|
|
731
502
|
"Stay in plan mode",
|
|
732
503
|
"Refine the plan",
|
|
733
504
|
]);
|
|
@@ -736,21 +507,17 @@ If you receive [PLAN GUIDANCE — Step n: ...], treat it as user steering for th
|
|
|
736
507
|
ctx.ui.setWidget("plan-steps", undefined);
|
|
737
508
|
|
|
738
509
|
if (choice?.startsWith("Execute")) {
|
|
510
|
+
const steps = [...todoItems];
|
|
739
511
|
planModeEnabled = false;
|
|
740
|
-
|
|
741
|
-
if (executionMode) {
|
|
742
|
-
syncCurrentStepIndex();
|
|
743
|
-
} else {
|
|
744
|
-
currentStepIndex = null;
|
|
745
|
-
}
|
|
512
|
+
todoItems = [];
|
|
746
513
|
restoreNormalModeTools();
|
|
747
514
|
updateStatus(ctx);
|
|
748
515
|
persistState();
|
|
749
516
|
|
|
750
|
-
const
|
|
517
|
+
const stepList = steps.map((t) => `${t.step}. ${t.text}`).join("\n");
|
|
751
518
|
const execMessage =
|
|
752
|
-
|
|
753
|
-
? `Execute
|
|
519
|
+
steps.length > 0
|
|
520
|
+
? `Execute this plan. Create tasks to track each step, then work through them:\n\n${stepList}`
|
|
754
521
|
: "Execute the plan you just created.";
|
|
755
522
|
pi.sendMessage(
|
|
756
523
|
{ customType: "plan-mode-execute", content: execMessage, display: true },
|
|
@@ -786,56 +553,16 @@ If you receive [PLAN GUIDANCE — Step n: ...], treat it as user steering for th
|
|
|
786
553
|
| {
|
|
787
554
|
data?: {
|
|
788
555
|
enabled?: boolean;
|
|
789
|
-
executing?: boolean;
|
|
790
556
|
normalTools?: string[];
|
|
791
557
|
todos?: TodoItem[];
|
|
792
|
-
currentStepIndex?: number | null;
|
|
793
558
|
};
|
|
794
559
|
}
|
|
795
560
|
| undefined;
|
|
796
561
|
|
|
797
562
|
if (planModeEntry?.data) {
|
|
798
563
|
planModeEnabled = planModeEntry.data.enabled ?? planModeEnabled;
|
|
799
|
-
executionMode = planModeEntry.data.executing ?? executionMode;
|
|
800
564
|
normalModeTools = planModeEntry.data.normalTools ?? normalModeTools;
|
|
801
565
|
todoItems = planModeEntry.data.todos ?? todoItems;
|
|
802
|
-
currentStepIndex = planModeEntry.data.currentStepIndex ?? currentStepIndex;
|
|
803
|
-
}
|
|
804
|
-
|
|
805
|
-
// On resume: re-scan messages to rebuild completion state
|
|
806
|
-
// Only scan messages AFTER the last "plan-mode-execute" to avoid picking up [DONE:n] from previous plans
|
|
807
|
-
const isResume = planModeEntry !== undefined;
|
|
808
|
-
if (isResume && executionMode && todoItems.length > 0) {
|
|
809
|
-
// Find the index of the last plan-mode-execute entry (marks when current execution started)
|
|
810
|
-
let executeIndex = -1;
|
|
811
|
-
for (let i = entries.length - 1; i >= 0; i--) {
|
|
812
|
-
const entry = entries[i] as { type: string; customType?: string };
|
|
813
|
-
if (entry.customType === "plan-mode-execute") {
|
|
814
|
-
executeIndex = i;
|
|
815
|
-
break;
|
|
816
|
-
}
|
|
817
|
-
}
|
|
818
|
-
|
|
819
|
-
// Only scan messages after the execute marker
|
|
820
|
-
const messages: AssistantMessage[] = [];
|
|
821
|
-
for (let i = executeIndex + 1; i < entries.length; i++) {
|
|
822
|
-
const entry = entries[i];
|
|
823
|
-
if (
|
|
824
|
-
entry.type === "message" &&
|
|
825
|
-
"message" in entry &&
|
|
826
|
-
isAssistantMessage(entry.message as AgentMessage)
|
|
827
|
-
) {
|
|
828
|
-
messages.push(entry.message as AssistantMessage);
|
|
829
|
-
}
|
|
830
|
-
}
|
|
831
|
-
const allText = messages.map(getTextContent).join("\n");
|
|
832
|
-
markCompletedSteps(allText, todoItems);
|
|
833
|
-
}
|
|
834
|
-
|
|
835
|
-
if (executionMode && todoItems.length > 0) {
|
|
836
|
-
syncCurrentStepIndex();
|
|
837
|
-
} else {
|
|
838
|
-
currentStepIndex = null;
|
|
839
566
|
}
|
|
840
567
|
|
|
841
568
|
if (normalModeTools.length === 0) {
|
|
@@ -255,32 +255,3 @@ export function extractTodoItems(message: string): TodoItem[] {
|
|
|
255
255
|
}
|
|
256
256
|
return items;
|
|
257
257
|
}
|
|
258
|
-
|
|
259
|
-
/**
|
|
260
|
-
* Extracts [DONE:n] step markers from a message.
|
|
261
|
-
* @param message - The message text to search
|
|
262
|
-
* @returns Array of step numbers that were marked as done
|
|
263
|
-
*/
|
|
264
|
-
export function extractDoneSteps(message: string): number[] {
|
|
265
|
-
const steps: number[] = [];
|
|
266
|
-
for (const match of message.matchAll(/\[DONE:(\d+)\]/gi)) {
|
|
267
|
-
const step = Number(match[1]);
|
|
268
|
-
if (Number.isFinite(step)) steps.push(step);
|
|
269
|
-
}
|
|
270
|
-
return steps;
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
/**
|
|
274
|
-
* Marks todo items as completed based on [DONE:n] markers in text.
|
|
275
|
-
* @param text - The text containing [DONE:n] markers
|
|
276
|
-
* @param items - The todo items to update
|
|
277
|
-
* @returns The number of items that were marked as completed
|
|
278
|
-
*/
|
|
279
|
-
export function markCompletedSteps(text: string, items: TodoItem[]): number {
|
|
280
|
-
const doneSteps = extractDoneSteps(text);
|
|
281
|
-
for (const step of doneSteps) {
|
|
282
|
-
const item = items.find((t) => t.step === step);
|
|
283
|
-
if (item) item.completed = true;
|
|
284
|
-
}
|
|
285
|
-
return doneSteps.length;
|
|
286
|
-
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it } from "bun:test";
|
|
2
|
+
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { resolveSharedSkillsDirsFromSettings } from "../index.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Create a temporary directory for test fixtures.
|
|
9
|
+
*
|
|
10
|
+
* @returns Path to the newly created temp directory
|
|
11
|
+
*/
|
|
12
|
+
function createTmpDir(): string {
|
|
13
|
+
return mkdtempSync(join(tmpdir(), "skill-cmds-shared-"));
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Write a settings.json file with the given content.
|
|
18
|
+
*
|
|
19
|
+
* @param dir - Directory to write settings.json into
|
|
20
|
+
* @param settings - Settings object to serialize
|
|
21
|
+
* @returns Path to the written settings.json
|
|
22
|
+
*/
|
|
23
|
+
function writeSettings(dir: string, settings: Record<string, unknown>): string {
|
|
24
|
+
const path = join(dir, "settings.json");
|
|
25
|
+
writeFileSync(path, JSON.stringify(settings, null, 2));
|
|
26
|
+
return path;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
describe("resolveSharedSkillsDirsFromSettings", () => {
|
|
30
|
+
let tmp: string;
|
|
31
|
+
|
|
32
|
+
beforeEach(() => {
|
|
33
|
+
tmp = createTmpDir();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
afterEach(() => {
|
|
37
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it("returns empty array when settings file does not exist", () => {
|
|
41
|
+
expect(resolveSharedSkillsDirsFromSettings(join(tmp, "nope.json"))).toEqual([]);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it("returns empty array when settings file is invalid JSON", () => {
|
|
45
|
+
const path = join(tmp, "settings.json");
|
|
46
|
+
writeFileSync(path, "not json {{");
|
|
47
|
+
expect(resolveSharedSkillsDirsFromSettings(path)).toEqual([]);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("returns empty array when sharedSkillsDirs is missing", () => {
|
|
51
|
+
const path = writeSettings(tmp, { theme: "nord" });
|
|
52
|
+
expect(resolveSharedSkillsDirsFromSettings(path)).toEqual([]);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("returns empty array when sharedSkillsDirs is not an array", () => {
|
|
56
|
+
const path = writeSettings(tmp, { sharedSkillsDirs: "/some/path" });
|
|
57
|
+
expect(resolveSharedSkillsDirsFromSettings(path)).toEqual([]);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("resolves absolute paths that exist as directories", () => {
|
|
61
|
+
const skillsDir = join(tmp, "my-skills");
|
|
62
|
+
mkdirSync(skillsDir);
|
|
63
|
+
const path = writeSettings(tmp, { sharedSkillsDirs: [skillsDir] });
|
|
64
|
+
|
|
65
|
+
expect(resolveSharedSkillsDirsFromSettings(path)).toEqual([skillsDir]);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("skips non-existent directories silently", () => {
|
|
69
|
+
const path = writeSettings(tmp, {
|
|
70
|
+
sharedSkillsDirs: [join(tmp, "does-not-exist")],
|
|
71
|
+
});
|
|
72
|
+
expect(resolveSharedSkillsDirsFromSettings(path)).toEqual([]);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it("skips paths that are files, not directories", () => {
|
|
76
|
+
const filePath = join(tmp, "not-a-dir");
|
|
77
|
+
writeFileSync(filePath, "hello");
|
|
78
|
+
const path = writeSettings(tmp, { sharedSkillsDirs: [filePath] });
|
|
79
|
+
|
|
80
|
+
expect(resolveSharedSkillsDirsFromSettings(path)).toEqual([]);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("rejects relative paths", () => {
|
|
84
|
+
const path = writeSettings(tmp, {
|
|
85
|
+
sharedSkillsDirs: ["relative/path", "./also-relative", "no-slash"],
|
|
86
|
+
});
|
|
87
|
+
expect(resolveSharedSkillsDirsFromSettings(path)).toEqual([]);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it("rejects non-string and empty entries", () => {
|
|
91
|
+
const path = writeSettings(tmp, {
|
|
92
|
+
sharedSkillsDirs: [42, null, "", " ", true],
|
|
93
|
+
});
|
|
94
|
+
expect(resolveSharedSkillsDirsFromSettings(path)).toEqual([]);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it("handles mixed valid and invalid entries", () => {
|
|
98
|
+
const validDir = join(tmp, "valid");
|
|
99
|
+
mkdirSync(validDir);
|
|
100
|
+
const path = writeSettings(tmp, {
|
|
101
|
+
sharedSkillsDirs: [validDir, "relative", join(tmp, "nonexistent"), 42],
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
expect(resolveSharedSkillsDirsFromSettings(path)).toEqual([validDir]);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it("expands tilde paths (skips when dir does not exist)", () => {
|
|
108
|
+
const path = writeSettings(tmp, {
|
|
109
|
+
sharedSkillsDirs: ["~/.nonexistent-skills-test-dir-99999"],
|
|
110
|
+
});
|
|
111
|
+
expect(resolveSharedSkillsDirsFromSettings(path)).toEqual([]);
|
|
112
|
+
});
|
|
113
|
+
});
|
|
@@ -125,24 +125,81 @@ function disableBuiltinSkillCommands(): void {
|
|
|
125
125
|
}
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
+
/**
|
|
129
|
+
* Resolve shared skill directories from global settings.
|
|
130
|
+
*
|
|
131
|
+
* Reads `sharedSkillsDirs`, tilde-expands each entry, and validates
|
|
132
|
+
* that it exists and is a directory. Mirrors the logic in `sdk.ts`
|
|
133
|
+
* so slash-command registration sees the same skills as the system prompt.
|
|
134
|
+
*
|
|
135
|
+
* @param settingsPath - Path to global settings.json
|
|
136
|
+
* @returns Array of validated, resolved directory paths
|
|
137
|
+
*/
|
|
138
|
+
export function resolveSharedSkillsDirsFromSettings(settingsPath: string): string[] {
|
|
139
|
+
if (!fs.existsSync(settingsPath)) return [];
|
|
140
|
+
let settings: Record<string, unknown>;
|
|
141
|
+
try {
|
|
142
|
+
settings = JSON.parse(fs.readFileSync(settingsPath, "utf-8"));
|
|
143
|
+
} catch {
|
|
144
|
+
return [];
|
|
145
|
+
}
|
|
146
|
+
const raw = settings.sharedSkillsDirs;
|
|
147
|
+
if (!Array.isArray(raw)) return [];
|
|
148
|
+
|
|
149
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? "~";
|
|
150
|
+
const resolved: string[] = [];
|
|
151
|
+
|
|
152
|
+
for (const entry of raw) {
|
|
153
|
+
if (typeof entry !== "string" || !entry.trim()) continue;
|
|
154
|
+
const trimmed = entry.trim();
|
|
155
|
+
let expanded: string;
|
|
156
|
+
if (trimmed === "~") {
|
|
157
|
+
expanded = home;
|
|
158
|
+
} else if (trimmed.startsWith("~/")) {
|
|
159
|
+
expanded = join(home, trimmed.slice(2));
|
|
160
|
+
} else if (trimmed.startsWith("/")) {
|
|
161
|
+
expanded = trimmed;
|
|
162
|
+
} else {
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
try {
|
|
166
|
+
const stats = fs.statSync(expanded);
|
|
167
|
+
if (stats.isDirectory()) resolved.push(expanded);
|
|
168
|
+
} catch {
|
|
169
|
+
// statSync failed — skip this entry
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return resolved;
|
|
173
|
+
}
|
|
174
|
+
|
|
128
175
|
export default function (pi: ExtensionAPI) {
|
|
129
176
|
disableBuiltinSkillCommands();
|
|
130
177
|
|
|
178
|
+
const agentDir =
|
|
179
|
+
process.env.PI_CODING_AGENT_DIR ??
|
|
180
|
+
join(process.env.HOME ?? process.env.USERPROFILE ?? "~", ".tallow");
|
|
181
|
+
const settingsPath = join(agentDir, "settings.json");
|
|
182
|
+
|
|
183
|
+
// Shared skill directories from global settings (e.g. ~/dev/skills)
|
|
184
|
+
const sharedSkillsDirs = resolveSharedSkillsDirsFromSettings(settingsPath);
|
|
185
|
+
|
|
131
186
|
// Include .claude/skills/ directories for Claude Code compatibility
|
|
132
|
-
const
|
|
187
|
+
const extraSkillPaths: string[] = [...sharedSkillsDirs];
|
|
133
188
|
const userClaudeSkills = join(
|
|
134
189
|
process.env.HOME ?? process.env.USERPROFILE ?? "~",
|
|
135
190
|
".claude",
|
|
136
191
|
"skills"
|
|
137
192
|
);
|
|
138
193
|
const projectClaudeSkills = join(process.cwd(), ".claude", "skills");
|
|
139
|
-
if (fs.existsSync(userClaudeSkills))
|
|
194
|
+
if (fs.existsSync(userClaudeSkills)) extraSkillPaths.push(userClaudeSkills);
|
|
140
195
|
if (isProjectTrusted(process.cwd()) && fs.existsSync(projectClaudeSkills)) {
|
|
141
|
-
|
|
196
|
+
extraSkillPaths.push(projectClaudeSkills);
|
|
142
197
|
}
|
|
143
198
|
|
|
144
|
-
// Load skills synchronously during extension init for autocomplete to work
|
|
145
|
-
|
|
199
|
+
// Load skills synchronously during extension init for autocomplete to work.
|
|
200
|
+
// includeDefaults: true picks up ~/.tallow/skills/ and ./skills/ (project).
|
|
201
|
+
// extraSkillPaths adds shared dirs + Claude bridge paths.
|
|
202
|
+
const { skills } = loadSkills({ agentDir, skillPaths: extraSkillPaths });
|
|
146
203
|
|
|
147
204
|
for (const skill of skills) {
|
|
148
205
|
// Validate name before registration — invalid names produce broken commands
|