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
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -443,11 +443,15 @@ Useful commands:
npm run sync-tmux
npm run sync-tmux -- --reload
npm run sync-tmux -- --bootstrap --reload
npm run restore-tmux
```

- `sync-tmux` copies this checkout into `~/.tmux/plugins/coding-agents-tmux`
- `--reload` runs `tmux source-file ~/.tmux.conf` after syncing
- `--bootstrap` reinstalls production dependencies in the synced plugin copy when `package.json` changed
- `restore-tmux` removes the synced development files, preserves `node_modules`, and returns the TPM checkout to a clean state so `prefix + U` can update it normally

Run `npm run restore-tmux` when you finish local development or before asking TPM to update all plugins.

## CLI

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"check": "./bin/coding-agents-tmux --help",
"typecheck": "tsc --project tsconfig.json",
"sync-tmux": "./scripts/sync-tmux-plugin.sh",
"restore-tmux": "./scripts/sync-tmux-plugin.sh --restore",
"fmt": "oxfmt .",
"fmt:check": "oxfmt --check ."
},
Expand Down
23 changes: 22 additions & 1 deletion scripts/sync-tmux-plugin.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,18 @@ TARGET_DIR="${CODING_AGENTS_TMUX_PLUGIN_DIR:-$HOME/.tmux/plugins/coding-agents-t
TMUX_CONF="${CODING_AGENTS_TMUX_TMUX_CONF:-$HOME/.tmux.conf}"
RELOAD=0
BOOTSTRAP=0
RESTORE=0

usage() {
printf 'Usage: %s [--target <dir>] [--reload] [--bootstrap]\n' "${0##*/}"
printf 'Usage: %s [--target <dir>] [--reload] [--bootstrap] [--restore]\n' "${0##*/}"
printf '\n'
printf 'Sync this checkout into the tmux plugin install used by TPM.\n'
printf '\n'
printf 'Options:\n'
printf ' --target <dir> Override target plugin directory\n'
printf ' --reload Reload tmux after syncing\n'
printf ' --bootstrap Reinstall production npm deps in the target dir\n'
printf ' --restore Discard synced files and restore the TPM checkout\n'
printf ' -h, --help Show this help\n'
}

Expand Down Expand Up @@ -76,6 +78,9 @@ while [ "$#" -gt 0 ]; do
--bootstrap)
BOOTSTRAP=1
;;
--restore)
RESTORE=1
;;
-h | --help)
usage
exit 0
Expand All @@ -89,6 +94,22 @@ while [ "$#" -gt 0 ]; do
shift
done

if [ "$RESTORE" -eq 1 ]; then
if [ ! -d "$TARGET_DIR/.git" ]; then
printf 'coding-agents-tmux: cannot restore non-git target: %s\n' "$TARGET_DIR" >&2
exit 1
fi

git -C "$TARGET_DIR" reset --hard HEAD
git -C "$TARGET_DIR" clean -fd -e node_modules/
printf 'Restored clean TPM checkout at %s\n' "$TARGET_DIR"

if [ "$RELOAD" -eq 1 ]; then
reload_tmux
fi
exit 0
fi

if ! command -v rsync >/dev/null 2>&1; then
printf 'coding-agents-tmux: rsync is required to sync the plugin checkout\n' >&2
exit 1
Expand Down
44 changes: 44 additions & 0 deletions test/sync-tmux-plugin.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import assert from "node:assert/strict";
import { execFileSync } from "node:child_process";
import { mkdtempSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import test from "node:test";

const repoRoot = join(import.meta.dirname, "..");
const syncScript = join(repoRoot, "scripts", "sync-tmux-plugin.sh");

function git(cwd: string, ...args: string[]) {
execFileSync("git", args, { cwd, stdio: "pipe" });
}

test("--restore returns a synced TPM checkout to a clean install", () => {
const tempRoot = mkdtempSync(join(tmpdir(), "coding-agents-tmux-sync-"));
const target = join(tempRoot, "plugin");

mkdirSync(join(target, "node_modules"), { recursive: true });
writeFileSync(join(target, "README.md"), "installed version\n");
writeFileSync(join(target, ".gitignore"), "node_modules/\n");
writeFileSync(join(target, "node_modules", "keep.txt"), "keep\n");

git(target, "init", "-q");
git(target, "config", "user.name", "Test User");
git(target, "config", "user.email", "test@example.com");
git(target, "add", "README.md", ".gitignore");
git(target, "commit", "-qm", "installed plugin");

execFileSync(syncScript, ["--target", target], { cwd: repoRoot, stdio: "pipe" });
assert.notEqual(readFileSync(join(target, "README.md"), "utf8"), "installed version\n");

execFileSync(syncScript, ["--target", target, "--restore"], {
cwd: repoRoot,
stdio: "pipe",
});

assert.equal(readFileSync(join(target, "README.md"), "utf8"), "installed version\n");
assert.equal(readFileSync(join(target, "node_modules", "keep.txt"), "utf8"), "keep\n");
assert.equal(
execFileSync("git", ["status", "--porcelain"], { cwd: target, encoding: "utf8" }),
"",
);
});
Loading