Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
- Preserve normal CLI behavior expectations: stdin/stdout composition, scriptability, and minimal surprise for shell users.

## Current Repo State
- Current entrypoint and installer are `bin/psh.sh`.
- Current entrypoint is `bin/psh.sh`. CLI install is `install.sh` (XDG only). The Omarchy plugin is installed with `omarchy plugin add` / `omarchy plugin install`.
- Tests live in `tests/` and use the npm-installed Bats runner at `node_modules/bats/bin/bats`.
- `Makefile` provides `make test` and `make install-smoke` shortcuts.
- Do not invent build/lint commands until the relevant executable config exists.
Expand All @@ -22,7 +22,7 @@
- Bats test dependency check: `test -x node_modules/bats/bin/bats && command -v setsid && command -v script`.
- Bats test suite: `node_modules/bats/bin/bats tests`.
- Make test shortcut: `make test`.
- Local installer smoke check: `PSH_INSTALL_DIR=$(mktemp -d) sh bin/psh.sh install` installs `psh` into the temp directory.
- Local installer smoke check: `XDG_DATA_HOME=$(mktemp -d) PSH_INSTALL_DIR=$(mktemp -d) bash install.sh` writes the payload under `XDG_DATA_HOME/psh` and the launcher into the temp bin directory.
- Dependency check without API key or config: `XDG_CONFIG_HOME=$(mktemp -d) bin/psh.sh run clean up docker` exits 2 with `psh: API key is required; run \`psh setup\` or set provider API key env var`.
- Smoke checks with OpenAI access: `OPENAI_API_KEY=... bin/psh.sh run clean up docker`, `OPENAI_API_KEY=... bin/psh.sh clean up docker`, and `printf %s "clean up docker" | OPENAI_API_KEY=... bin/psh.sh run` verify prompt ingestion and command generation.
- Smoke check with Fireworks access: `PSH_PROVIDER=fireworks FIREWORKS_API_KEY=... bin/psh.sh run clean up docker` verifies Fireworks command generation.
Expand Down
62 changes: 62 additions & 0 deletions BarWidget.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import QtQuick
import Quickshell
import qs.Ui

BarWidget {
id: root
moduleName: "com.mdtrr.promptshell"

readonly property bool opened: panelLoader.item ? panelLoader.item.opened === true : false
readonly property bool popoutSwitchClosing: panelLoader.item ? panelLoader.item.popoutSwitchClosing === true : false

function open() {
if (panelLoader.item) panelLoader.item.open()
}

function close() {
if (panelLoader.item) panelLoader.item.close()
}

function toggle() {
if (panelLoader.item) panelLoader.item.toggle()
}

function closeForPopoutSwitch() {
if (panelLoader.item) panelLoader.item.closeForPopoutSwitch()
}

function injectPanel() {
var target = panelLoader.item
if (!target) return
if ("bar" in target) target.bar = root.bar
if ("anchorItem" in target) target.anchorItem = button
if ("hostWidget" in target) target.hostWidget = root
}

implicitWidth: button.implicitWidth
implicitHeight: button.implicitHeight

onBarChanged: injectPanel()

Loader {
id: panelLoader
active: true
source: Qt.resolvedUrl("Panel.qml")
visible: false
onLoaded: {
root.injectPanel()
Qt.callLater(root.injectPanel)
}
}

WidgetButton {
id: button
anchors.fill: parent
bar: root.bar
text: "psh"
tooltipText: "Prompt Shell"
onPressed: function(buttonCode) {
if (buttonCode === Qt.LeftButton) root.toggle()
}
}
}
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ make install-smoke

## Code Style

- POSIX shell for the CLI, including integrated install/uninstall commands.
- POSIX shell for the CLI. CLI install is `install.sh` (bash). Omarchy plugin install is `omarchy plugin add` / `omarchy plugin install`.
- Keep changes small and focused.
- Preserve stdin/stdout composition and scriptability.
- Use `/dev/tty` for interactive-only UI.
Expand Down
7 changes: 6 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ deps:

syntax:
sh -n bin/psh.sh
bash -n install.sh
bash -n tests/helpers/psh.bash

test: syntax
Expand All @@ -16,4 +17,8 @@ test: syntax
$(BATS) tests

install-smoke:
install_dir=$$(mktemp -d) && PSH_INSTALL_DIR=$$install_dir sh bin/psh.sh install && test -x "$$install_dir/psh" && PSH_INSTALL_DIR=$$install_dir "$$install_dir/psh" uninstall && test ! -e "$$install_dir/psh"
data_home=$$(mktemp -d) && install_dir=$$(mktemp -d) && \
XDG_DATA_HOME=$$data_home PSH_INSTALL_DIR=$$install_dir bash install.sh && \
test -x "$$install_dir/psh" && test -x "$$data_home/psh/psh.sh" && \
XDG_DATA_HOME=$$data_home PSH_INSTALL_DIR=$$install_dir "$$install_dir/psh" uninstall && \
test ! -e "$$install_dir/psh" && test ! -e "$$data_home/psh/psh.sh"
82 changes: 82 additions & 0 deletions Panel.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import QtQuick
import Quickshell
import qs.Commons
import qs.Ui

Panel {
id: root
moduleName: "com.mdtrr.promptshell"
manageIpc: false

property var anchorItem: null
property var hostWidget: null

function open() {
root.controller.show()
Qt.callLater(function() {
if (promptInput) promptInput.forceActiveFocus()
})
}

function close() {
root.controller.hide()
}

function switchPanel(direction) {
if (root.bar && typeof root.bar.switchPanelFrom === "function")
return root.bar.switchPanelFrom(root.hostWidget || root, direction)
return false
}

function runPrompt() {
var prompt = promptInput.text.trim()
if (prompt === "") return
Quickshell.execDetached(["omarchy-launch-tui", "psh", "run", prompt])
promptInput.text = ""
root.close()
}

KeyboardPanel {
id: panel
anchorItem: root.anchorItem
owner: root.hostWidget || root
bar: root.bar
open: root.opened
focusTarget: keyCatcher
contentWidth: panel.fittedContentWidth(Style.space(320))
contentHeight: panel.fittedContentHeight(content.implicitHeight)

PanelKeyCatcher {
id: keyCatcher
anchors.fill: parent
onCloseRequested: root.close()
onTabRequested: function(direction) { root.switchPanel(direction) }

Column {
id: content
width: parent.width
spacing: Style.space(8)

Text {
width: parent.width
text: "Prompt Shell"
color: root.bar ? root.bar.foreground : Color.foreground
font.family: root.bar ? root.bar.fontFamily : Style.font.family
font.pixelSize: Style.font.subtitle
font.bold: true
}

TextInput {
id: promptInput
width: parent.width
color: root.bar ? root.bar.foreground : Color.foreground
font.family: root.bar ? root.bar.fontFamily : Style.font.family
font.pixelSize: Style.font.body
wrapMode: TextInput.Wrap
Keys.onReturnPressed: root.runPrompt()
Keys.onEnterPressed: root.runPrompt()
}
}
}
}
}
33 changes: 24 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,32 +10,47 @@ printf %s "clean up docker" | psh run

## Install

Install with curl:
Two install targets:

**CLI** — `install.sh` writes `psh` into XDG directories. It does not install the Omarchy plugin.

```sh
curl -fsSL https://raw.githubusercontent.com/modoterra/promptshell/main/bin/psh.sh | sh -s -- install
curl -fsSL https://raw.githubusercontent.com/modoterra/promptshell/main/install.sh | bash
```

The `-s` flag tells `sh` to read the downloaded script from stdin and pass `install` to `psh`.
From a checkout:

```sh
bash install.sh
```

Or with wget:
Layout:

- Payload: `${XDG_DATA_HOME:-$HOME/.local/share}/psh/psh.sh`
- Launcher: `${XDG_BIN_HOME:-$HOME/.local/bin}/psh` (override with `PSH_INSTALL_DIR`)
- Bash completion: `${XDG_DATA_HOME:-$HOME/.local/share}/bash-completion/completions/psh`
- Config: `${XDG_CONFIG_HOME:-$HOME/.config}/psh/config.json`

```sh
wget -qO- https://raw.githubusercontent.com/modoterra/promptshell/main/bin/psh.sh | sh -s -- install
curl -fsSL https://raw.githubusercontent.com/modoterra/promptshell/main/install.sh | PSH_INSTALL_DIR=/usr/local/bin bash
```

By default, the installer writes `psh` to `$HOME/.local/bin`. Override the destination with `PSH_INSTALL_DIR`:
Refresh an existing CLI install:

```sh
curl -fsSL https://raw.githubusercontent.com/modoterra/promptshell/main/bin/psh.sh | PSH_INSTALL_DIR=/usr/local/bin sh -s -- install
psh update
```

Uninstall the installed `psh` binary:
**Omarchy plugin** — native plugin install. This is the bar widget only; run `install.sh` as well if `psh` is not already on `PATH`.

```sh
psh uninstall
omarchy plugin add https://github.com/modoterra/promptshell.git --enable
```

`omarchy plugin install` is an alias of `omarchy plugin add`. Enable later with `omarchy plugin enable com.mdtrr.promptshell` if you omit `--enable`.

Remove the CLI with `psh uninstall` (add `--purge` to drop config). Remove the plugin with `omarchy plugin remove com.mdtrr.promptshell`.

## Requirements

- `jq` is required.
Expand Down
Loading