Skip to content
Merged
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
16 changes: 9 additions & 7 deletions Formula/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,14 @@ inside `Formula/` in that tap repo.

1. **Create the tap repo** — `interesting-vibe-coding/homebrew-paws` on GitHub.
Copy `paws.rb` and `paws-games.rb` into its `Formula/` directory.
2. **Tag releases** — create a `v0.3.0` tag in both repos:
- `interesting-vibe-coding/paws`
- `interesting-vibe-coding/paws-games`
3. **Fill in sha256** — download each tarball and run `shasum -a 256 <file>`,
then paste the hash into the corresponding formula's `sha256` field.
4. **Push the tap repo** — once formulae have valid sha256 values, the stable
`brew install paws` path is live.
2. **Tags** — `v0.3.1` has been created in `interesting-vibe-coding/paws`.
The release workflow (`.github/workflows/release.yml`) triggers on `v*` tag
pushes and will produce pre-built binaries attached to the GitHub Release.
`paws-games` still needs its own tag once that repo is ready.
3. **sha256** — both formulae already have sha256 values filled in.
If the release workflow rebuilds and re-uploads tarballs the hashes may need
to be re-verified with `shasum -a 256 <downloaded-tarball>`.
4. **Push the tap repo** — once the tap repo exists and formulae are copied in,
the stable `brew install paws` path is live.

Until steps 1–4 are done, only `--HEAD` installs work.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ English | [中文](README.zh.md)

# 🐾 Paws

[![CI](https://github.com/interesting-vibe-coding/paws/actions/workflows/ci.yml/badge.svg)](https://github.com/interesting-vibe-coding/paws/actions/workflows/ci.yml) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) [![Built for Kaku](https://img.shields.io/badge/Built_for-Kaku-blue)](https://github.com/tw93/kaku) [![Made with Lua & Rust](https://img.shields.io/badge/Made_with-Lua_&_Rust-orange)]() [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/interesting-vibe-coding/paws/pulls) [![GitHub Stars](https://img.shields.io/github/stars/interesting-vibe-coding/paws?style=flat&color=yellow)](https://github.com/interesting-vibe-coding/paws/stargazers)
[![CI](https://github.com/interesting-vibe-coding/paws/actions/workflows/ci.yml/badge.svg)](https://github.com/interesting-vibe-coding/paws/actions/workflows/ci.yml) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) [![Works with Kaku & WezTerm](https://img.shields.io/badge/Works_with-Kaku_%26_WezTerm-blue)](https://wezfurlong.org/wezterm/) [![Made with Lua & Rust](https://img.shields.io/badge/Made_with-Lua_&_Rust-orange)]() [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/interesting-vibe-coding/paws/pulls) [![GitHub Stars](https://img.shields.io/github/stars/interesting-vibe-coding/paws?style=flat&color=yellow)](https://github.com/interesting-vibe-coding/paws/stargazers)

Play games while your AI agent works. A status HUD tells you when to come back.

Expand Down Expand Up @@ -50,7 +50,9 @@ cargo install --git https://github.com/interesting-vibe-coding/paws-games --bin
cargo install --git https://github.com/interesting-vibe-coding/paws-games --bin tetris
```

Then add [`lua/paws.lua`](lua/paws.lua) to your `~/.config/kaku/kaku.lua` (before `return config`) and wire hooks for your agent (see [`hooks/`](hooks/) for reference configs). Reload Kaku (CMD+Shift+R).
Then add [`lua/paws.lua`](lua/paws.lua) to your terminal config (before `return config`) and wire hooks for your agent (see [`hooks/`](hooks/) for reference configs).
- **Kaku:** `~/.config/kaku/kaku.lua` — reload with CMD+Shift+R
- **WezTerm:** `~/.config/wezterm/wezterm.lua` — auto-reloads on save

## Games

Expand Down
6 changes: 4 additions & 2 deletions README.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

# 🐾 Paws

[![CI](https://github.com/interesting-vibe-coding/paws/actions/workflows/ci.yml/badge.svg)](https://github.com/interesting-vibe-coding/paws/actions/workflows/ci.yml) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) [![Built for Kaku](https://img.shields.io/badge/Built_for-Kaku-blue)](https://github.com/tw93/kaku) [![Made with Lua & Rust](https://img.shields.io/badge/Made_with-Lua_&_Rust-orange)]() [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/interesting-vibe-coding/paws/pulls) [![GitHub Stars](https://img.shields.io/github/stars/interesting-vibe-coding/paws?style=flat&color=yellow)](https://github.com/interesting-vibe-coding/paws/stargazers)
[![CI](https://github.com/interesting-vibe-coding/paws/actions/workflows/ci.yml/badge.svg)](https://github.com/interesting-vibe-coding/paws/actions/workflows/ci.yml) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) [![Works with Kaku & WezTerm](https://img.shields.io/badge/Works_with-Kaku_%26_WezTerm-blue)](https://wezfurlong.org/wezterm/) [![Made with Lua & Rust](https://img.shields.io/badge/Made_with-Lua_&_Rust-orange)]() [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/interesting-vibe-coding/paws/pulls) [![GitHub Stars](https://img.shields.io/github/stars/interesting-vibe-coding/paws?style=flat&color=yellow)](https://github.com/interesting-vibe-coding/paws/stargazers)

Agent 工作时尽情玩,需要你时一眼就看到。

Expand Down Expand Up @@ -50,7 +50,9 @@ cargo install --git https://github.com/interesting-vibe-coding/paws-games --bin
cargo install --git https://github.com/interesting-vibe-coding/paws-games --bin tetris
```

然后将 [`lua/paws.lua`](lua/paws.lua) 添加到 `~/.config/kaku/kaku.lua`(`return config` 之前),并为你的 Agent 配置 hooks(参考 [`hooks/`](hooks/) 目录)。重载 Kaku(CMD+Shift+R)。
然后将 [`lua/paws.lua`](lua/paws.lua) 添加到终端配置中(`return config` 之前),并为你的 Agent 配置 hooks(参考 [`hooks/`](hooks/) 目录)。
- **Kaku:** `~/.config/kaku/kaku.lua` — 重载需按 CMD+Shift+R
- **WezTerm:** `~/.config/wezterm/wezterm.lua` — 保存后自动重载

## 游戏

Expand Down
18 changes: 16 additions & 2 deletions docs/kaku-notes.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,22 @@
# Kaku integration notes
# Kaku & WezTerm integration notes

Hard-won facts about Kaku (a WezTerm fork) relevant to Paws. Read before touching
Hard-won facts about Kaku and WezTerm relevant to Paws. Read before touching
the Lua, to avoid re-discovering these.

## WezTerm compatibility

`lua/paws.lua` uses only **standard WezTerm Lua APIs** — every call
(`wezterm.mux`, `wezterm.GLOBAL`, `wezterm.action_callback`, `window:mux_window():spawn_tab`, etc.)
works identically in WezTerm. No code changes are needed.

| Difference | Kaku | WezTerm |
|-----------|------|---------|
| Config path | `~/.config/kaku/kaku.lua` | `~/.config/wezterm/wezterm.lua` |
| Auto-reload on save | ✗ — must press **CMD+Shift+R** | ✓ automatic |
| CMD+SHIFT+G | Taken (lazygit) | Free — but Paws uses CMD+G anyway |

The WezTerm install is identical to Kaku: paste `lua/paws.lua` into your config before `return config`.

## Keybindings
- **No auto-reload.** Kaku does not reload `kaku.lua` on save. Press **CMD+Shift+R**
after every edit, or changes won't take effect.
Expand Down
5 changes: 3 additions & 2 deletions lua/paws.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
-- Paws 🐾 — native Kaku/WezTerm integration.
-- Add this to ~/.config/kaku/kaku.lua (before `return config`).
-- Paws 🐾 — native Kaku / WezTerm integration.
-- Add this to ~/.config/kaku/kaku.lua (Kaku) or ~/.config/wezterm/wezterm.lua (WezTerm),
-- before `return config`. No code changes needed — all APIs are standard WezTerm.
-- Everything runs in-process: no external scripts, no temp files, no `kaku cli`.
--
-- The game lives in its OWN TAB (full-window, never disturbs your panes).
Expand Down
33 changes: 22 additions & 11 deletions skills/paws-install/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: paws-install
description: Install Paws 🐾 (terminal companion for AI coding agents) into the user's Kaku terminal and their agent (Kiro CLI, Claude Code, or Codex CLI). Use this when the user asks to install, set up, or wire up Paws. Performs the Kaku Lua merge, game install, and agent hook wiring — all idempotently.
description: Install Paws 🐾 (terminal companion for AI coding agents) into the user's Kaku or WezTerm terminal and their agent (Kiro CLI, Claude Code, or Codex CLI). Use this when the user asks to install, set up, or wire up Paws. Performs the Lua merge, game install, and agent hook wiring — all idempotently.
---

# Installing Paws 🐾
Expand All @@ -11,8 +11,12 @@ You are installing Paws for the user. Work from a local clone of this repo

## 0. Preconditions

- Confirm the terminal is **Kaku** (`which kaku`). If not, tell the user Paws
currently requires Kaku and stop.
- Confirm the terminal is **Kaku** (`which kaku`) or **WezTerm** (`which wezterm`).
Both are supported — the same `lua/paws.lua` works in both without modification.
If neither is installed, ask the user to install one first and stop.
- Determine the terminal type — it affects the config path in Step 2:
- Kaku: `~/.config/kaku/kaku.lua`
- WezTerm: `~/.config/wezterm/wezterm.lua`
- Note the repo root (absolute path) — you'll need it for hook paths.
**All hook paths in config files must be absolute** — `~` is not expanded
by any of the three agents.
Expand Down Expand Up @@ -44,18 +48,22 @@ Any games not installed now can be installed later directly from the in-app
game picker (uninstalled entries show "⤓ install" and run the install command
on Enter).

## 2. Merge the Lua into the Kaku config
## 2. Merge the Lua into the terminal config

The Kaku config is `~/.config/kaku/kaku.lua` (it returns a `config` table at the
end). The snippet to insert is `lua/paws.lua` from this repo.
The terminal config returns a `config` table at the end. The snippet to insert is
`lua/paws.lua` from this repo — it works identically in Kaku and WezTerm.

- If `kaku.lua` already contains `Paws 🐾` (the marker comment), skip — already installed.
- **Kaku:** config is at `~/.config/kaku/kaku.lua`
- **WezTerm:** config is at `~/.config/wezterm/wezterm.lua`

Steps:
- If the config already contains `Paws 🐾` (the marker comment), skip — already installed.
- Otherwise insert the **body** of `lua/paws.lua` (everything except its
`local wezterm = require 'wezterm'` line, which the config already has)
**immediately before** the final `return config` line.
- Ensure `config.keys` exists before the insert: if the config never sets it,
add `config.keys = config.keys or {}` at the top of the inserted block.
- Syntax-check afterward: `luac -p ~/.config/kaku/kaku.lua` (if `luac` exists).
- Syntax-check afterward: `luac -p <config-path>` (if `luac` exists).

## 3. Wire the agent's state signals (for the status HUD)

Expand Down Expand Up @@ -192,16 +200,19 @@ chmod +x <REPO>/hooks/kiro/paws-pause.sh

## 5. Finish

Tell the user to **reload Kaku (CMD+Shift+R)** — Kaku does NOT auto-reload — then:
- **Kaku users:** Press **CMD+Shift+R** to reload — Kaku does NOT auto-reload on save.
- **WezTerm users:** Config reloads automatically on save — no action needed.

Then:
- **CMD+G** — opens the game tab (a centered menu: games · 🎲 Random · ⚙ Settings); after that it toggles agent ↔ game.
- **CMD+SHIFT+P** — close the game tab and re-open the menu.
- **CMD+H** — open the Paws repo in your browser (to file an issue / say hi).

(Don't use CMD+SHIFT+G — Kaku already binds it to lazygit.)
(Kaku users: don't use CMD+SHIFT+G — Kaku already binds it to lazygit.)

## Verify

- `luac -p ~/.config/kaku/kaku.lua` passes (if luac is available).
- `luac -p <config-path>` passes (if luac is available).
- `paws --list` shows at least one installed game.
- The hook paths in the agent config are absolute and the scripts are executable.
- Quick smoke test (Claude Code / Codex): pipe mock JSON to the hook and check
Expand Down
55 changes: 27 additions & 28 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -340,37 +340,36 @@ fn pick_game_menu(games: &[Game]) -> io::Result<Option<String>> {
install_sel = (install_sel + 1) % games.len();
}
KeyCode::Esc | KeyCode::Char('q') => screen = Screen::Menu,
KeyCode::Enter | KeyCode::Char(' ') => {
if !installed[install_sel] {
let game = &games[install_sel];
disable_raw_mode()?;
io::stdout().execute(LeaveAlternateScreen)?;

println!("\n Installing {}…", game.name);
println!(" $ {}\n", game.install);

let status = std::process::Command::new("sh")
.arg("-c")
.arg(&game.install)
.status();

match status {
Ok(s) if s.success() => println!("\n ✓ {} installed!", game.name),
Ok(s) => {
println!("\n ✗ Install failed (exit {})", s.code().unwrap_or(-1))
}
Err(e) => println!("\n ✗ Install error: {e}"),
KeyCode::Enter | KeyCode::Char(' ') if !installed[install_sel] => {
let game = &games[install_sel];
disable_raw_mode()?;
io::stdout().execute(LeaveAlternateScreen)?;

println!("\n Installing {}…", game.name);
println!(" $ {}\n", game.install);

let status = std::process::Command::new("sh")
.arg("-c")
.arg(&game.install)
.status();

match status {
Ok(s) if s.success() => println!("\n ✓ {} installed!", game.name),
Ok(s) => {
println!("\n ✗ Install failed (exit {})", s.code().unwrap_or(-1))
}
print!(" Press Enter to continue…");
let _ = io::stdout().flush();
let _ = io::stdin().lock().read_line(&mut String::new());

enable_raw_mode()?;
io::stdout().execute(EnterAlternateScreen)?;
terminal = Terminal::new(CrosstermBackend::new(io::stdout()))?;
installed = games.iter().map(|g| is_installed(&g.cmd)).collect();
Err(e) => println!("\n ✗ Install error: {e}"),
}
print!(" Press Enter to continue…");
let _ = io::stdout().flush();
let _ = io::stdin().lock().read_line(&mut String::new());

enable_raw_mode()?;
io::stdout().execute(EnterAlternateScreen)?;
terminal = Terminal::new(CrosstermBackend::new(io::stdout()))?;
installed = games.iter().map(|g| is_installed(&g.cmd)).collect();
}
KeyCode::Enter | KeyCode::Char(' ') => {}
_ => {}
},
Screen::Menu => match key.code {
Expand Down
Loading