@grympler/opencode-tmux-handover 1.0.0

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.
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { readFileSync, writeFileSync } from "fs"
4
+ import { dirname, resolve } from "path"
5
+ import { fileURLToPath } from "url"
6
+ import { homedir } from "os"
7
+
8
+ const __filename = fileURLToPath(import.meta.url)
9
+ const __dirname = dirname(__filename)
10
+
11
+ const OPENCODE_CONFIG_PATH = resolve(homedir(), ".config/opencode/opencode.json")
12
+ const PLUGIN_DIR = resolve(__dirname, "..")
13
+
14
+ function unregisterPlugin() {
15
+ try {
16
+ let config
17
+
18
+ try {
19
+ const content = readFileSync(OPENCODE_CONFIG_PATH, "utf-8")
20
+ config = JSON.parse(content)
21
+ } catch (err) {
22
+ console.log("opencode.json not found, nothing to unregister")
23
+ return
24
+ }
25
+
26
+ if (!Array.isArray(config.plugin)) {
27
+ console.log("No plugin array in opencode.json")
28
+ return
29
+ }
30
+
31
+ const index = config.plugin.indexOf(PLUGIN_DIR)
32
+ if (index === -1) {
33
+ console.log("Plugin not found in opencode.json")
34
+ return
35
+ }
36
+
37
+ config.plugin.splice(index, 1)
38
+
39
+ writeFileSync(OPENCODE_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n")
40
+ console.log(`✓ Plugin unregistered: ${PLUGIN_DIR}`)
41
+ console.log(`✓ Updated: ${OPENCODE_CONFIG_PATH}`)
42
+ } catch (err) {
43
+ console.error("Error unregistering plugin:", err)
44
+ process.exit(1)
45
+ }
46
+ }
47
+
48
+ unregisterPlugin()
@@ -0,0 +1,5 @@
1
+ ---
2
+ description: "Lance une session tmux avec claude en split horizontal"
3
+ ---
4
+
5
+ <!-- Intercepted by tt-plugin -->
@@ -0,0 +1,5 @@
1
+ ---
2
+ description: "Lance une session tmux avec copilot en split horizontal"
3
+ ---
4
+
5
+ <!-- Intercepted by tt-plugin -->
@@ -0,0 +1,5 @@
1
+ ---
2
+ description: "Lance une session tmux avec opencode en split horizontal"
3
+ ---
4
+
5
+ <!-- Intercepted by tt-plugin -->
@@ -0,0 +1,5 @@
1
+ ---
2
+ description: "Lance une session tmux simple maximisée"
3
+ ---
4
+
5
+ <!-- Intercepted by tt-plugin -->
@@ -0,0 +1,5 @@
1
+ ---
2
+ description: Configure tt-plugin settings
3
+ ---
4
+
5
+ Open and configure the tt-plugin settings, including which tmux commands to enable and terminal preferences.
package/src/config.ts ADDED
@@ -0,0 +1,46 @@
1
+ import { readFileSync } from "fs"
2
+ import { homedir } from "os"
3
+ import { join } from "path"
4
+
5
+ export interface PluginConfig {
6
+ enabledCommands: string[]
7
+ defaultTerminal?: string
8
+ sessionPrefix?: string
9
+ }
10
+
11
+ const DEFAULT_CONFIG: PluginConfig = {
12
+ enabledCommands: ["tmux", "tmux-oc"],
13
+ defaultTerminal: "auto",
14
+ sessionPrefix: "oc"
15
+ }
16
+
17
+ export function loadConfig(): PluginConfig {
18
+ let config = { ...DEFAULT_CONFIG }
19
+
20
+ // 1. Load from file (overrides defaults)
21
+ const configPath = join(homedir(), ".config", "opencode", "tt-plugin-config.json")
22
+ try {
23
+ const fileContent = readFileSync(configPath, "utf-8")
24
+ const userConfig = JSON.parse(fileContent)
25
+ config = { ...config, ...userConfig }
26
+ } catch {
27
+ // Ignore missing or invalid config file
28
+ }
29
+
30
+ // 2. Load from environment (overrides file and defaults)
31
+ if (process.env.TT_PLUGIN_COMMANDS) {
32
+ config.enabledCommands = process.env.TT_PLUGIN_COMMANDS.split(",")
33
+ .map(s => s.trim())
34
+ .filter(s => s.length > 0)
35
+ }
36
+
37
+ if (process.env.TT_PLUGIN_TERMINAL) {
38
+ config.defaultTerminal = process.env.TT_PLUGIN_TERMINAL
39
+ }
40
+
41
+ if (process.env.TT_PLUGIN_PREFIX) {
42
+ config.sessionPrefix = process.env.TT_PLUGIN_PREFIX
43
+ }
44
+
45
+ return config
46
+ }
package/src/index.ts ADDED
@@ -0,0 +1,66 @@
1
+ import type { Plugin } from "@opencode-ai/plugin"
2
+ import { fileURLToPath } from "url"
3
+ import { dirname, join } from "path"
4
+ import { loadConfig } from "./config.js"
5
+
6
+ const __filename = fileURLToPath(import.meta.url)
7
+ const __dirname = dirname(__filename)
8
+
9
+ export const TmuxLauncher: Plugin = async ({ $, directory }) => {
10
+ const config = loadConfig()
11
+
12
+ return {
13
+ "command.execute.before": async (input, output) => {
14
+ const commandName = input.command || ""
15
+
16
+ // Handle configuration command
17
+ if (commandName === "tt-configure") {
18
+ const scriptPath = join(__dirname, "scripts", "opencode-tt-config.sh")
19
+ try {
20
+ await $`bash ${scriptPath}`.cwd(directory)
21
+ output.parts = []
22
+ } catch (err) {
23
+ console.error("tt-configure error:", err)
24
+ }
25
+ return
26
+ }
27
+
28
+ // Check if command is enabled in configuration
29
+ if (!config.enabledCommands.includes(commandName)) {
30
+ return
31
+ }
32
+
33
+ const scriptMap: Record<string, string> = {
34
+ "tmux": "opencode-tmux.sh",
35
+ "tmux-oc": "opencode-tmux-oc.sh",
36
+ "tmux-claude": "opencode-tmux-claude.sh",
37
+ "tmux-copilot": "opencode-tmux-copilot.sh"
38
+ }
39
+
40
+ const scriptName = scriptMap[commandName]
41
+ if (!scriptName) return
42
+
43
+ const scriptPath = join(__dirname, "scripts", scriptName)
44
+
45
+ // Prepare environment variables from config
46
+ const env: Record<string, string> = {
47
+ ...process.env as Record<string, string>,
48
+ TT_SESSION_PREFIX: config.sessionPrefix || "oc",
49
+ TT_DEFAULT_TERMINAL: config.defaultTerminal || "auto"
50
+ }
51
+
52
+ // Add specific variables for tmux-oc
53
+ if (commandName === "tmux-oc") {
54
+ env.OPENCODE_SESSION_ID = input.sessionID || ""
55
+ env.OPENCODE_SERVER_PASSWORD = process.env.OPENCODE_SERVER_PASSWORD || ""
56
+ }
57
+
58
+ try {
59
+ await $`bash ${scriptPath}`.env(env).cwd(directory)
60
+ output.parts = []
61
+ } catch (err) {
62
+ console.error("tt-plugin execution error:", err)
63
+ }
64
+ },
65
+ }
66
+ }
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ SESSION="claude_$(basename "$PWD" | tr './' '_')"
5
+
6
+ launch_in_terminal() {
7
+ local cmd="$1"
8
+
9
+ if command -v gnome-terminal &>/dev/null; then
10
+ nohup gnome-terminal --maximize -- bash -c "$cmd; exec bash" >/dev/null 2>&1 &
11
+ elif command -v terminator &>/dev/null; then
12
+ nohup terminator --maximize -e "bash -c '$cmd; exec bash'" >/dev/null 2>&1 &
13
+ elif command -v konsole &>/dev/null; then
14
+ nohup konsole -e bash -c "$cmd; exec bash" >/dev/null 2>&1 &
15
+ elif command -v alacritty &>/dev/null; then
16
+ nohup alacritty -e bash -c "$cmd; exec bash" >/dev/null 2>&1 &
17
+ elif command -v kitty &>/dev/null; then
18
+ nohup kitty bash -c "$cmd; exec bash" >/dev/null 2>&1 &
19
+ elif command -v xterm &>/dev/null; then
20
+ nohup xterm -e bash -c "$cmd; exec bash" >/dev/null 2>&1 &
21
+ else
22
+ echo "ERROR: No terminal emulator found" >&2
23
+ exit 1
24
+ fi
25
+ }
26
+
27
+ if [ -n "${TMUX:-}" ]; then
28
+ tmux split-window -h -c "$PWD"
29
+ else
30
+ if tmux has-session -t "$SESSION" 2>/dev/null; then
31
+ launch_in_terminal "tmux attach-session -t '$SESSION'"
32
+ else
33
+ tmux new-session -d -s "$SESSION" -c "$PWD"
34
+ tmux split-window -h -t "$SESSION" -c "$PWD"
35
+ tmux send-keys -t "$SESSION:1.1" "claude ." C-m
36
+ tmux select-pane -t "$SESSION:1.0"
37
+ launch_in_terminal "tmux attach-session -t '$SESSION'"
38
+ fi
39
+ fi
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ SESSION="copilot_$(basename "$PWD" | tr './' '_')"
5
+
6
+ launch_in_terminal() {
7
+ local cmd="$1"
8
+
9
+ if command -v gnome-terminal &>/dev/null; then
10
+ nohup gnome-terminal --maximize -- bash -c "$cmd; exec bash" >/dev/null 2>&1 &
11
+ elif command -v terminator &>/dev/null; then
12
+ nohup terminator --maximize -e "bash -c '$cmd; exec bash'" >/dev/null 2>&1 &
13
+ elif command -v konsole &>/dev/null; then
14
+ nohup konsole -e bash -c "$cmd; exec bash" >/dev/null 2>&1 &
15
+ elif command -v alacritty &>/dev/null; then
16
+ nohup alacritty -e bash -c "$cmd; exec bash" >/dev/null 2>&1 &
17
+ elif command -v kitty &>/dev/null; then
18
+ nohup kitty bash -c "$cmd; exec bash" >/dev/null 2>&1 &
19
+ elif command -v xterm &>/dev/null; then
20
+ nohup xterm -e bash -c "$cmd; exec bash" >/dev/null 2>&1 &
21
+ else
22
+ echo "ERROR: No terminal emulator found" >&2
23
+ exit 1
24
+ fi
25
+ }
26
+
27
+ if [ -n "${TMUX:-}" ]; then
28
+ tmux split-window -h -c "$PWD"
29
+ else
30
+ if tmux has-session -t "$SESSION" 2>/dev/null; then
31
+ launch_in_terminal "tmux attach-session -t '$SESSION'"
32
+ else
33
+ tmux new-session -d -s "$SESSION" -c "$PWD"
34
+ tmux split-window -h -t "$SESSION" -c "$PWD"
35
+ tmux send-keys -t "$SESSION:1.1" "copilot ." C-m
36
+ tmux select-pane -t "$SESSION:1.0"
37
+ launch_in_terminal "tmux attach-session -t '$SESSION'"
38
+ fi
39
+ fi
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ SESSION="oc_$(basename "$PWD" | tr './' '_')"
5
+ SESSION_ID="${OPENCODE_SESSION_ID:-}"
6
+
7
+ launch_in_terminal() {
8
+ local cmd="$1"
9
+
10
+ if command -v gnome-terminal &>/dev/null; then
11
+ nohup gnome-terminal --maximize -- bash -c "$cmd; exec bash" >/dev/null 2>&1 &
12
+ elif command -v terminator &>/dev/null; then
13
+ nohup terminator --maximize -e "bash -c '$cmd; exec bash'" >/dev/null 2>&1 &
14
+ elif command -v konsole &>/dev/null; then
15
+ nohup konsole -e bash -c "$cmd; exec bash" >/dev/null 2>&1 &
16
+ elif command -v alacritty &>/dev/null; then
17
+ nohup alacritty -e bash -c "$cmd; exec bash" >/dev/null 2>&1 &
18
+ elif command -v kitty &>/dev/null; then
19
+ nohup kitty bash -c "$cmd; exec bash" >/dev/null 2>&1 &
20
+ elif command -v xterm &>/dev/null; then
21
+ nohup xterm -e bash -c "$cmd; exec bash" >/dev/null 2>&1 &
22
+ else
23
+ echo "ERROR: No terminal emulator found" >&2
24
+ exit 1
25
+ fi
26
+ }
27
+
28
+ if [ -n "${TMUX:-}" ]; then
29
+ tmux split-window -h -c "$PWD"
30
+ else
31
+ if tmux has-session -t "$SESSION" 2>/dev/null; then
32
+ launch_in_terminal "tmux attach-session -t '$SESSION'"
33
+ else
34
+ opencode_cmd=""
35
+ if [ -n "$SESSION_ID" ]; then
36
+ opencode_cmd="(opencode -s '$SESSION_ID') || (echo 'Failed to open session $SESSION_ID. Starting new...'; sleep 3; opencode .)"
37
+ else
38
+ opencode_cmd="opencode ."
39
+ fi
40
+
41
+ TMUX_CMD="tmux new-session -d -s '$SESSION' -c '$PWD' && tmux split-window -h -t '$SESSION:1' -c '$PWD' && tmux send-keys -t '$SESSION:1.1' \"$opencode_cmd\" C-m && tmux select-pane -t '$SESSION:1.0' && tmux attach-session -t '$SESSION'"
42
+
43
+ launch_in_terminal "$TMUX_CMD"
44
+ fi
45
+ fi
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ SESSION="tmux_$(basename "$PWD" | tr './' '_')"
5
+
6
+ launch_in_terminal() {
7
+ local cmd="$1"
8
+
9
+ if command -v gnome-terminal &>/dev/null; then
10
+ nohup gnome-terminal --maximize -- bash -c "$cmd; exec bash" >/dev/null 2>&1 &
11
+ elif command -v terminator &>/dev/null; then
12
+ nohup terminator --maximize -e "bash -c '$cmd; exec bash'" >/dev/null 2>&1 &
13
+ elif command -v konsole &>/dev/null; then
14
+ nohup konsole -e bash -c "$cmd; exec bash" >/dev/null 2>&1 &
15
+ elif command -v alacritty &>/dev/null; then
16
+ nohup alacritty -e bash -c "$cmd; exec bash" >/dev/null 2>&1 &
17
+ elif command -v kitty &>/dev/null; then
18
+ nohup kitty bash -c "$cmd; exec bash" >/dev/null 2>&1 &
19
+ elif command -v xterm &>/dev/null; then
20
+ nohup xterm -e bash -c "$cmd; exec bash" >/dev/null 2>&1 &
21
+ else
22
+ echo "ERROR: No terminal emulator found" >&2
23
+ exit 1
24
+ fi
25
+ }
26
+
27
+ if [ -n "${TMUX:-}" ]; then
28
+ tmux split-window -h -c "$PWD"
29
+ else
30
+ if tmux has-session -t "$SESSION" 2>/dev/null; then
31
+ launch_in_terminal "tmux attach-session -t '$SESSION'"
32
+ else
33
+ tmux new-session -d -s "$SESSION" -c "$PWD"
34
+ launch_in_terminal "tmux attach-session -t '$SESSION'"
35
+ fi
36
+ fi
@@ -0,0 +1,64 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ launch_in_terminal() {
5
+ local cmd="$1"
6
+
7
+ if command -v gnome-terminal &>/dev/null; then
8
+ nohup gnome-terminal -- bash -c "$cmd; echo 'Press Enter to close...'; read" >/dev/null 2>&1 &
9
+ elif command -v terminator &>/dev/null; then
10
+ nohup terminator -e "bash -c '$cmd; echo \"Press Enter to close...\"; read'" >/dev/null 2>&1 &
11
+ elif command -v konsole &>/dev/null; then
12
+ nohup konsole -e bash -c "$cmd; echo 'Press Enter to close...'; read" >/dev/null 2>&1 &
13
+ elif command -v alacritty &>/dev/null; then
14
+ nohup alacritty -e bash -c "$cmd; echo 'Press Enter to close...'; read" >/dev/null 2>&1 &
15
+ elif command -v kitty &>/dev/null; then
16
+ nohup kitty bash -c "$cmd; echo 'Press Enter to close...'; read" >/dev/null 2>&1 &
17
+ elif command -v xterm &>/dev/null; then
18
+ nohup xterm -e bash -c "$cmd; echo 'Press Enter to close...'; read" >/dev/null 2>&1 &
19
+ else
20
+ echo "ERROR: No terminal emulator found. Creating config quietly." >&2
21
+ # Fallback: run in current shell (might be background)
22
+ export TT_IN_TERMINAL=1
23
+ bash "$0"
24
+ fi
25
+ }
26
+
27
+ if [ -z "${TT_IN_TERMINAL:-}" ]; then
28
+ SCRIPT_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/$(basename "${BASH_SOURCE[0]}")"
29
+ launch_in_terminal "export TT_IN_TERMINAL=1; bash '$SCRIPT_PATH'"
30
+ exit 0
31
+ fi
32
+
33
+ CONFIG_DIR="$HOME/.config/opencode"
34
+ CONFIG_FILE="$CONFIG_DIR/tt-plugin-config.json"
35
+
36
+ mkdir -p "$CONFIG_DIR"
37
+
38
+ if [ -f "$CONFIG_FILE" ]; then
39
+ echo "Current tt-plugin configuration:"
40
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
41
+ cat "$CONFIG_FILE"
42
+ echo ""
43
+ echo "Location: $CONFIG_FILE"
44
+ echo ""
45
+ else
46
+ echo "No tt-plugin configuration found."
47
+ echo "Creating default configuration at: $CONFIG_FILE"
48
+ echo ""
49
+ fi
50
+
51
+ if [ ! -f "$CONFIG_FILE" ]; then
52
+ cat > "$CONFIG_FILE" << 'JSON'
53
+ {
54
+ "enabledCommands": ["tmux", "tmux-oc"],
55
+ "defaultTerminal": "auto",
56
+ "sessionPrefix": "oc"
57
+ }
58
+ JSON
59
+ fi
60
+
61
+ echo "Configuration saved/verified!"
62
+ echo ""
63
+ echo "Opening editor..."
64
+ ${EDITOR:-nano} "$CONFIG_FILE"
package/uninstall.sh ADDED
@@ -0,0 +1,106 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ # Colors for output
5
+ RED='\033[0;31m'
6
+ GREEN='\033[0;32m'
7
+ YELLOW='\033[1;33m'
8
+ BLUE='\033[0;34m'
9
+ NC='\033[0m' # No Color
10
+
11
+ # Configuration
12
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
13
+ OPENCODE_CONFIG="${HOME}/.config/opencode"
14
+
15
+ echo -e "${BLUE}=== OpenCode Tmux Plugin Suite Uninstallation ===${NC}\n"
16
+
17
+ # Function to remove symlink and restore backup
18
+ remove_symlink() {
19
+ local path="$1"
20
+ local name="$2"
21
+
22
+ if [ ! -e "$path" ]; then
23
+ echo -e "${YELLOW} File not found: $path${NC}"
24
+ return 0
25
+ fi
26
+
27
+ if [ -L "$path" ]; then
28
+ rm "$path"
29
+ echo -e "${GREEN}✓ Removed symlink: $name${NC}"
30
+
31
+ # Restore backup if it exists
32
+ if [ -f "${path}.bak" ]; then
33
+ mv "${path}.bak" "$path"
34
+ echo -e "${GREEN}✓ Restored backup: $name${NC}"
35
+ fi
36
+ else
37
+ echo -e "${YELLOW}⚠ File is not a symlink (was it installed manually?): $name${NC}"
38
+ fi
39
+ }
40
+
41
+ echo -e "${YELLOW}Unregistering plugin from opencode.json...${NC}"
42
+ node "${SCRIPT_DIR}/scripts/unregister-plugin.js"
43
+
44
+ echo -e "\n${YELLOW}Removing installed files...${NC}"
45
+
46
+ # Remove commands
47
+ remove_symlink \
48
+ "${OPENCODE_CONFIG}/commands/tmux.md" \
49
+ "tmux command"
50
+
51
+ remove_symlink \
52
+ "${OPENCODE_CONFIG}/commands/tmux-oc.md" \
53
+ "tmux-oc command"
54
+
55
+ remove_symlink \
56
+ "${OPENCODE_CONFIG}/commands/tmux-claude.md" \
57
+ "tmux-claude command"
58
+
59
+ remove_symlink \
60
+ "${OPENCODE_CONFIG}/commands/tmux-copilot.md" \
61
+ "tmux-copilot command"
62
+
63
+ # Cleanup old plugin symlinks if they exist
64
+ echo -e "\n${YELLOW}Cleaning up any old plugin symlinks...${NC}"
65
+ if [ -L "${OPENCODE_CONFIG}/plugins/tmux-launcher.ts" ]; then
66
+ rm "${OPENCODE_CONFIG}/plugins/tmux-launcher.ts"
67
+ echo -e "${GREEN}✓ Removed old tmux-launcher plugin${NC}"
68
+ fi
69
+
70
+ if [ -L "${OPENCODE_CONFIG}/plugins/tmux-oc-launcher.ts" ]; then
71
+ rm "${OPENCODE_CONFIG}/plugins/tmux-oc-launcher.ts"
72
+ echo -e "${GREEN}✓ Removed old tmux-oc-launcher plugin${NC}"
73
+ fi
74
+
75
+ if [ -L "${OPENCODE_CONFIG}/plugins/tmux-claude-launcher.ts" ]; then
76
+ rm "${OPENCODE_CONFIG}/plugins/tmux-claude-launcher.ts"
77
+ echo -e "${GREEN}✓ Removed old tmux-claude-launcher plugin${NC}"
78
+ fi
79
+
80
+ if [ -L "${OPENCODE_CONFIG}/plugins/tmux-copilot-launcher.ts" ]; then
81
+ rm "${OPENCODE_CONFIG}/plugins/tmux-copilot-launcher.ts"
82
+ echo -e "${GREEN}✓ Removed old tmux-copilot-launcher plugin${NC}"
83
+ fi
84
+
85
+ # Verify removal
86
+ echo -e "\n${YELLOW}Verifying removal...${NC}"
87
+
88
+ all_removed=true
89
+
90
+ for cmd in tmux tmux-oc tmux-claude tmux-copilot; do
91
+ if [ ! -L "${OPENCODE_CONFIG}/commands/${cmd}.md" ] && [ ! -e "${OPENCODE_CONFIG}/commands/${cmd}.md" ]; then
92
+ echo -e "${GREEN}✓ Command ${cmd} removed${NC}"
93
+ else
94
+ echo -e "${YELLOW}⚠ Command ${cmd} still exists${NC}"
95
+ all_removed=false
96
+ fi
97
+ done
98
+
99
+ echo -e "\n${BLUE}=== Uninstallation Complete ===${NC}"
100
+ echo -e "\n${YELLOW}Summary:${NC}"
101
+ echo -e "- Plugin unregistered from opencode.json"
102
+ echo -e "- Commands removed from: ${OPENCODE_CONFIG}/commands/"
103
+ echo -e "\n${YELLOW}Source files remain in:${NC}"
104
+ echo -e " - ${SCRIPT_DIR}/"
105
+ echo -e "\n${YELLOW}To completely remove, delete the repository:${NC}"
106
+ echo -e " rm -rf ${SCRIPT_DIR}"