@trolleroof/tui 0.2.3

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,4 @@
1
+ {
2
+ "tag": "overlay-v0.2.3",
3
+ "version": "0.2.3"
4
+ }
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@trolleroof/tui",
3
+ "version": "0.2.3",
4
+ "description": "Play familiar single-player games in a lightweight HTML overlay while your agents work.",
5
+ "type": "module",
6
+ "bin": {
7
+ "gamepigeon": "./dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "scripts",
12
+ "overlay-release.json"
13
+ ],
14
+ "packageManager": "bun@1.3.11",
15
+ "scripts": {
16
+ "start": "bun src/index.ts",
17
+ "build": "bun build src/index.ts --outdir dist --target bun",
18
+ "build:overlay:web": "bun scripts/build-overlay-web.ts",
19
+ "build:overlay": "bun run build:overlay:web && cargo build --release --manifest-path tauri/Cargo.toml",
20
+ "claude:dev": "bash scripts/dev-harness.sh claude",
21
+ "codex:dev": "bash scripts/dev-harness.sh codex",
22
+ "antigravity:dev": "bash scripts/dev-harness.sh antigravity",
23
+ "dev:overlay": "bun run build:overlay:web && cargo build --manifest-path tauri/Cargo.toml && GAMEPIGEON_OVERLAY_BIN=\"$PWD/tauri/target/debug/gamepigeon-overlay\" bun scripts/overlay.ts --show",
24
+ "preview:overlay": "bun scripts/preview-overlay.ts",
25
+ "install:adapters": "bash scripts/install-adapters.sh",
26
+ "test": "bun test ./src/",
27
+ "test:adapters": "bun run build && bash scripts/install-adapters.test.sh && bash scripts/dev-harness.test.sh",
28
+ "test:overlay": "bun test scripts/overlay.test.ts scripts/plugin-update.test.ts scripts/version-bump.test.ts overlay/session.test.ts overlay/persistence.test.ts overlay/recording.test.ts overlay/renderers.test.ts overlay/motion.test.ts overlay/connect4-layout.test.ts overlay/analytics.test.ts overlay/tauri-bridge.test.ts",
29
+ "test:plugin-update": "bun test scripts/plugin-update.test.ts scripts/run-plugin-update.test.ts",
30
+ "test:launcher": "bun run test:overlay",
31
+ "typecheck": "tsc --noEmit && tsc -p overlay/tsconfig.json",
32
+ "export:bc": "bun scripts/export-bc.ts",
33
+ "version:bump": "bun scripts/version-bump.ts",
34
+ "prepack": "bun run build"
35
+ },
36
+ "engines": {
37
+ "bun": ">=1.3"
38
+ },
39
+ "devDependencies": {
40
+ "@types/bun": "^1.3.5",
41
+ "typescript": "^5.6.0",
42
+ "vitest": "^4.1.10"
43
+ },
44
+ "dependencies": {
45
+ "@tauri-apps/api": "^2.11.1",
46
+ "posthog-node": "^5.44.0"
47
+ }
48
+ }
@@ -0,0 +1,34 @@
1
+ import { existsSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { spawnSync } from "node:child_process";
4
+
5
+ const root = join(import.meta.dir, "..");
6
+
7
+ /** Worktrees and fresh clones often have no node_modules; overlay bundle needs @tauri-apps/api. */
8
+ function ensureNodeModules(): void {
9
+ const tauriApi = join(root, "node_modules/@tauri-apps/api/package.json");
10
+ if (existsSync(tauriApi)) return;
11
+ console.error("node_modules missing — running bun install…");
12
+ const install = spawnSync("bun", ["install"], { cwd: root, stdio: "inherit" });
13
+ if (install.status !== 0) process.exit(install.status ?? 1);
14
+ }
15
+
16
+ ensureNodeModules();
17
+
18
+ const pkg = JSON.parse(await Bun.file("package.json").text()) as { version?: string };
19
+
20
+ const result = await Bun.build({
21
+ entrypoints: ["overlay/app.ts"],
22
+ outdir: "overlay/dist",
23
+ target: "browser",
24
+ minify: true,
25
+ define: {
26
+ __POSTHOG_CAPTURE_KEY__: JSON.stringify(process.env.POSTHOG_CAPTURE_KEY ?? ""),
27
+ __APP_VERSION__: JSON.stringify(pkg.version ?? "0.0.0"),
28
+ },
29
+ });
30
+
31
+ if (!result.success) {
32
+ for (const log of result.logs) console.error(log);
33
+ process.exit(1);
34
+ }
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5
+ HARNESS="${1:-}"
6
+
7
+ usage() {
8
+ cat <<'EOF' >&2
9
+ Usage: scripts/dev-harness.sh {claude|codex|antigravity|prepare}
10
+
11
+ Shared local overlay development launcher:
12
+ prepare Build overlay web bundle + debug Tauri shell and export dev env
13
+ claude prepare + claude --plugin-dir .
14
+ codex prepare + workspace plugin + Codex adapter + codex
15
+ antigravity prepare + workspace plugin + agy
16
+ EOF
17
+ exit 2
18
+ }
19
+
20
+ require_command() {
21
+ if ! command -v "$1" >/dev/null 2>&1; then
22
+ echo "Missing required command: $1" >&2
23
+ exit 1
24
+ fi
25
+ }
26
+
27
+ prepare_overlay() {
28
+ require_command bun
29
+ require_command cargo
30
+ if [[ ! -f "$ROOT_DIR/node_modules/@tauri-apps/api/package.json" ]]; then
31
+ (cd "$ROOT_DIR" && bun install)
32
+ fi
33
+ (cd "$ROOT_DIR" && bun run build:overlay:web)
34
+ cargo build --manifest-path "$ROOT_DIR/tauri/Cargo.toml"
35
+ export GAMEPIGEON_OVERLAY_BIN="$ROOT_DIR/tauri/target/debug/gamepigeon-overlay"
36
+ export GAMEPIGEON_SKIP_UPDATE_CHECK=1
37
+ bun "$ROOT_DIR/scripts/overlay.ts" --quit
38
+ }
39
+
40
+ ensure_codex_plugin() {
41
+ bash "$ROOT_DIR/scripts/materialize-agents-plugin.sh"
42
+ bash "$ROOT_DIR/scripts/install-adapters.sh" --codex
43
+ require_command codex
44
+
45
+ if ! codex plugin marketplace list 2>/dev/null | grep -Fq "$ROOT_DIR"; then
46
+ codex plugin marketplace add "$ROOT_DIR" >/dev/null
47
+ fi
48
+
49
+ if ! codex plugin list 2>/dev/null | grep -Fq 'tui-gamepigeon@tui-gamepigeon-dev'; then
50
+ codex plugin add tui-gamepigeon@tui-gamepigeon-dev >/dev/null
51
+ fi
52
+ }
53
+
54
+ ensure_antigravity_plugin() {
55
+ bash "$ROOT_DIR/scripts/materialize-agents-plugin.sh"
56
+ }
57
+
58
+ case "$HARNESS" in
59
+ prepare)
60
+ prepare_overlay
61
+ ;;
62
+ claude)
63
+ prepare_overlay
64
+ require_command claude
65
+ exec claude --plugin-dir "$ROOT_DIR"
66
+ ;;
67
+ codex)
68
+ prepare_overlay
69
+ ensure_codex_plugin
70
+ exec codex
71
+ ;;
72
+ antigravity)
73
+ prepare_overlay
74
+ ensure_antigravity_plugin
75
+ require_command agy
76
+ exec agy
77
+ ;;
78
+ *)
79
+ usage
80
+ ;;
81
+ esac
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5
+ TEMP_DIR="$(mktemp -d)"
6
+ trap 'rm -rf "$TEMP_DIR"' EXIT
7
+
8
+ WORKTREE="$TEMP_DIR/worktree"
9
+ mkdir -p "$WORKTREE/.codex-plugin" "$WORKTREE/scripts"
10
+ cp "$ROOT_DIR/.codex-plugin/plugin.json" "$WORKTREE/.codex-plugin/plugin.json"
11
+ cp "$ROOT_DIR/scripts/materialize-agents-plugin.sh" "$WORKTREE/scripts/materialize-agents-plugin.sh"
12
+ chmod +x "$WORKTREE/scripts/materialize-agents-plugin.sh"
13
+
14
+ (
15
+ cd "$WORKTREE"
16
+ bash scripts/materialize-agents-plugin.sh
17
+ )
18
+
19
+ test -f "$WORKTREE/.agents/plugins/marketplace.json"
20
+ test -f "$WORKTREE/.agents/plugins/tui-gamepigeon/plugin.json"
21
+ test -f "$WORKTREE/.agents/plugins/tui-gamepigeon/hooks.json"
22
+ test -f "$WORKTREE/.agents/plugins/tui-gamepigeon/skills/arcade/SKILL.md"
23
+
24
+ grep -Fq "bun $WORKTREE/scripts/overlay.ts --enable" \
25
+ "$WORKTREE/.agents/plugins/tui-gamepigeon/skills/arcade/SKILL.md"
26
+ grep -Fq "bun $WORKTREE/scripts/overlay.ts --reset" \
27
+ "$WORKTREE/.agents/plugins/tui-gamepigeon/hooks.json"
28
+ grep -Fq '"name": "tui-gamepigeon-dev"' \
29
+ "$WORKTREE/.agents/plugins/marketplace.json"
30
+
31
+ bash "$ROOT_DIR/scripts/dev-harness.sh" 2>/dev/null && exit 1 || true
32
+
33
+ echo "dev-harness tests passed"
@@ -0,0 +1,124 @@
1
+ import {
2
+ chmodSync,
3
+ mkdirSync,
4
+ readFileSync,
5
+ readdirSync,
6
+ statSync,
7
+ writeFileSync,
8
+ } from "node:fs";
9
+ import { homedir } from "node:os";
10
+ import { resolve } from "node:path";
11
+ import {
12
+ extractBehaviorCloningSamples,
13
+ splitForGame,
14
+ type BehaviorCloningSample,
15
+ type DatasetSplit,
16
+ } from "../src/data/bc-export.js";
17
+ import type { TraceEventChunk } from "../src/recording/types.js";
18
+
19
+ interface CliOptions {
20
+ inputs: string[];
21
+ output: string;
22
+ includeNoops: boolean;
23
+ }
24
+
25
+ function usage(): never {
26
+ console.error(
27
+ "Usage: bun run export:bc -- [--input <file-or-directory>]... [--output <directory>] [--include-noops]",
28
+ );
29
+ process.exit(2);
30
+ }
31
+
32
+ function parseArgs(args: string[]): CliOptions {
33
+ const inputs: string[] = [];
34
+ let output = resolve("bc-dataset");
35
+ let includeNoops = false;
36
+ for (let index = 0; index < args.length; index++) {
37
+ const argument = args[index]!;
38
+ if (argument === "--input") {
39
+ const value = args[++index];
40
+ if (!value) usage();
41
+ inputs.push(resolve(value));
42
+ } else if (argument === "--output") {
43
+ const value = args[++index];
44
+ if (!value) usage();
45
+ output = resolve(value);
46
+ } else if (argument === "--include-noops") {
47
+ includeNoops = true;
48
+ } else {
49
+ usage();
50
+ }
51
+ }
52
+ if (inputs.length === 0) {
53
+ const recordings = resolve(homedir(), ".tui-gamepigeon", "recordings");
54
+ inputs.push(resolve(recordings, "completed", "chunks"));
55
+ inputs.push(resolve(recordings, "overlay-completed"));
56
+ }
57
+ return { inputs, output, includeNoops };
58
+ }
59
+
60
+ function jsonFiles(input: string): string[] {
61
+ let stats;
62
+ try {
63
+ stats = statSync(input);
64
+ } catch {
65
+ return [];
66
+ }
67
+ if (stats.isFile()) return input.endsWith(".json") ? [input] : [];
68
+ if (!stats.isDirectory()) return [];
69
+ return readdirSync(input, { withFileTypes: true })
70
+ .flatMap((entry) => jsonFiles(resolve(input, entry.name)))
71
+ .sort();
72
+ }
73
+
74
+ function loadChunks(inputs: readonly string[]): TraceEventChunk[] {
75
+ return inputs.flatMap(jsonFiles).flatMap((path) => {
76
+ try {
77
+ const value = JSON.parse(readFileSync(path, "utf8")) as TraceEventChunk;
78
+ return value.schema_version === 2 && Array.isArray(value.events) ? [value] : [];
79
+ } catch (error) {
80
+ throw new Error(`Could not parse ${path}: ${String(error)}`);
81
+ }
82
+ });
83
+ }
84
+
85
+ function writeDataset(
86
+ output: string,
87
+ samples: readonly BehaviorCloningSample[],
88
+ includeNoops: boolean,
89
+ ): void {
90
+ mkdirSync(output, { recursive: true, mode: 0o700 });
91
+ chmodSync(output, 0o700);
92
+ const splits: Record<DatasetSplit, BehaviorCloningSample[]> = {
93
+ train: [],
94
+ validation: [],
95
+ test: [],
96
+ };
97
+ for (const sample of samples) splits[splitForGame(sample.game_id)].push(sample);
98
+ for (const [name, rows] of Object.entries(splits)) {
99
+ const body = rows.map((row) => JSON.stringify(row)).join("\n");
100
+ writeFileSync(resolve(output, `${name}.jsonl`), body ? `${body}\n` : "", { mode: 0o600 });
101
+ }
102
+ const manifest = {
103
+ schema_version: 1,
104
+ format: "behavior_cloning_jsonl",
105
+ split_unit: "game_id",
106
+ noops_included: includeNoops,
107
+ samples: {
108
+ total: samples.length,
109
+ train: splits.train.length,
110
+ validation: splits.validation.length,
111
+ test: splits.test.length,
112
+ },
113
+ games: [...new Set(samples.map((sample) => sample.game_type))].sort(),
114
+ };
115
+ writeFileSync(resolve(output, "manifest.json"), `${JSON.stringify(manifest, null, 2)}\n`, {
116
+ mode: 0o600,
117
+ });
118
+ }
119
+
120
+ const options = parseArgs(process.argv.slice(2));
121
+ const chunks = loadChunks(options.inputs);
122
+ const samples = extractBehaviorCloningSamples(chunks, { includeNoops: options.includeNoops });
123
+ writeDataset(options.output, samples, options.includeNoops);
124
+ console.log(`Exported ${samples.length} BC samples from ${chunks.length} chunks to ${options.output}`);
@@ -0,0 +1,192 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5
+ CODEX_HOME="${CODEX_HOME:-$HOME/.codex}"
6
+ CLAUDE_CONFIG_DIR="${CLAUDE_CONFIG_DIR:-$HOME/.claude}"
7
+ OPENCODE_CONFIG_DIR="${OPENCODE_CONFIG_DIR:-$HOME/.config/opencode}"
8
+ HERMES_HOME="${HERMES_HOME:-$HOME/.hermes}"
9
+ OPENCLAW_HOME="${OPENCLAW_HOME:-$HOME/.openclaw}"
10
+ MARKER="Generated by tui-gamepigeon scripts/install-adapters.sh"
11
+
12
+ usage() {
13
+ cat <<'EOF'
14
+ Usage: scripts/install-adapters.sh [--codex|--claude|--opencode|--hermes|--openclaw|--all]
15
+
16
+ Install the Arcade command for Codex, Claude Code, OpenCode, Hermes, OpenClaw, or all of them (the default).
17
+ EOF
18
+ }
19
+
20
+ install_codex=false
21
+ install_claude=false
22
+ install_opencode=false
23
+ install_hermes=false
24
+ install_openclaw=false
25
+
26
+ case "${1:---all}" in
27
+ --all)
28
+ install_codex=true
29
+ install_claude=true
30
+ install_opencode=true
31
+ install_hermes=true
32
+ install_openclaw=true
33
+ ;;
34
+ --codex)
35
+ install_codex=true
36
+ ;;
37
+ --claude)
38
+ install_claude=true
39
+ ;;
40
+ --opencode)
41
+ install_opencode=true
42
+ ;;
43
+ --hermes)
44
+ install_hermes=true
45
+ ;;
46
+ --openclaw)
47
+ install_openclaw=true
48
+ ;;
49
+ -h|--help)
50
+ usage
51
+ exit 0
52
+ ;;
53
+ *)
54
+ usage >&2
55
+ exit 2
56
+ ;;
57
+ esac
58
+
59
+ if [[ $# -gt 1 ]]; then
60
+ usage >&2
61
+ exit 2
62
+ fi
63
+
64
+ skill_targets=()
65
+ if [[ "$install_codex" == true ]]; then
66
+ skill_targets+=("$CODEX_HOME/skills/arcade")
67
+ fi
68
+ if [[ "$install_claude" == true ]]; then
69
+ skill_targets+=("$CLAUDE_CONFIG_DIR/skills/arcade")
70
+ fi
71
+ if [[ "$install_hermes" == true ]]; then
72
+ skill_targets+=("$HERMES_HOME/skills/arcade")
73
+ fi
74
+ if [[ "$install_openclaw" == true ]]; then
75
+ skill_targets+=("$OPENCLAW_HOME/skills/arcade")
76
+ fi
77
+
78
+ opencode_command="$OPENCODE_CONFIG_DIR/commands/arcade.md"
79
+ files_to_check=()
80
+ if [[ "$install_codex" == true || "$install_claude" == true ]]; then
81
+ for target in "${skill_targets[@]}"; do
82
+ files_to_check+=("$target/SKILL.md")
83
+ done
84
+ fi
85
+ if [[ "$install_opencode" == true ]]; then
86
+ files_to_check+=("$opencode_command")
87
+ fi
88
+
89
+ for generated_file in "${files_to_check[@]}"; do
90
+ if [[ -f "$generated_file" ]] && ! grep -Fq "$MARKER" "$generated_file"; then
91
+ echo "Refusing to replace an existing adapter: $generated_file" >&2
92
+ exit 1
93
+ fi
94
+ done
95
+
96
+ printf -v launcher_command 'bun %q --enable' "$ROOT_DIR/scripts/overlay.ts"
97
+
98
+ if [[ "$install_codex" == true || "$install_claude" == true ]]; then
99
+ for target in "${skill_targets[@]}"; do
100
+ mkdir -p "$target"
101
+ cat > "$target/SKILL.md" <<EOF
102
+ ---
103
+ name: arcade
104
+ description: Open or restore the native GamePigeon HTML overlay while the coding harness works.
105
+ ---
106
+
107
+ # Arcade
108
+
109
+ <!-- $MARKER -->
110
+
111
+ Execute this command exactly once:
112
+
113
+ \`$launcher_command\`
114
+
115
+ Do not inspect the user's repository. Do not spawn another agent or task. Do not run any other command.
116
+
117
+ GamePigeon opens as a borderless HTML overlay without a Dock or taskbar entry. Running Arcade again focuses the existing overlay.
118
+
119
+ On success, respond only with \`Arcade opened.\`. On failure, relay the launcher's error without attempting another launch method.
120
+ EOF
121
+ echo "Installed Arcade skill at $target"
122
+ done
123
+ fi
124
+
125
+ if [[ "$install_opencode" == true ]]; then
126
+ mkdir -p "$(dirname "$opencode_command")"
127
+ cat > "$opencode_command" <<EOF
128
+ ---
129
+ description: Open or restore the GamePigeon Arcade overlay
130
+ ---
131
+
132
+ <!-- $MARKER -->
133
+
134
+ !\`$launcher_command\`
135
+
136
+ The command opens GamePigeon as a borderless HTML overlay without a Dock or taskbar entry. Running \`/arcade\` again focuses the existing overlay.
137
+
138
+ Respond only with \`Arcade opened.\` when the launcher succeeds. If its output reports an error, relay that error without attempting another launch method.
139
+ EOF
140
+ echo "Installed Arcade command at $opencode_command"
141
+ fi
142
+
143
+ if [[ "$install_hermes" == true ]]; then
144
+ if [[ -f "$HERMES_HOME/config.yaml" ]]; then
145
+ if [[ -f "$HERMES_HOME/hermes-agent/venv/bin/python" ]]; then
146
+ "$HERMES_HOME/hermes-agent/venv/bin/python" "$ROOT_DIR/scripts/manage-hermes-hooks.py" "$ROOT_DIR"
147
+ else
148
+ python3 "$ROOT_DIR/scripts/manage-hermes-hooks.py" "$ROOT_DIR" || python "$ROOT_DIR/scripts/manage-hermes-hooks.py" "$ROOT_DIR"
149
+ fi
150
+ fi
151
+ fi
152
+
153
+ if [[ "$install_openclaw" == true ]]; then
154
+ mkdir -p "$OPENCLAW_HOME/hooks/arcade"
155
+ cat > "$OPENCLAW_HOME/hooks/arcade/HOOK.md" <<EOF
156
+ ---
157
+ name: arcade
158
+ description: "Open or restore the GamePigeon Arcade overlay"
159
+ metadata:
160
+ {
161
+ "openclaw":
162
+ {
163
+ "emoji": "🎮",
164
+ "events": ["command:new", "command:reset"],
165
+ "install": [{ "id": "arcade", "kind": "local", "label": "Local Arcade" }],
166
+ },
167
+ }
168
+ ---
169
+ # Arcade Hook
170
+ This hook manages the GamePigeon Arcade overlay lifecycle.
171
+ EOF
172
+
173
+ cat > "$OPENCLAW_HOME/hooks/arcade/handler.js" <<EOF
174
+ import { exec } from "node:child_process";
175
+
176
+ export default async function handle(event) {
177
+ const root = "$ROOT_DIR";
178
+ let action = "";
179
+ if (event.action === "new" || event.action === "reset") {
180
+ action = "--reset && bun " + root + "/scripts/overlay.ts --show";
181
+ }
182
+ if (action) {
183
+ exec("bun " + root + "/scripts/overlay.ts " + action);
184
+ }
185
+ }
186
+ EOF
187
+
188
+ if command -v openclaw &> /dev/null; then
189
+ openclaw hooks enable arcade || true
190
+ fi
191
+ echo "Installed and enabled OpenClaw hook at $OPENCLAW_HOME/hooks/arcade"
192
+ fi
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5
+ TEMP_DIR="$(mktemp -d)"
6
+ trap 'rm -rf "$TEMP_DIR"' EXIT
7
+
8
+ CODEX_HOME="$TEMP_DIR/codex" \
9
+ CLAUDE_CONFIG_DIR="$TEMP_DIR/claude" \
10
+ OPENCODE_CONFIG_DIR="$TEMP_DIR/opencode" \
11
+ HERMES_HOME="$TEMP_DIR/hermes" \
12
+ OPENCLAW_HOME="$TEMP_DIR/openclaw" \
13
+ bash "$ROOT_DIR/scripts/install-adapters.sh"
14
+
15
+ for skill_file in \
16
+ "$TEMP_DIR/codex/skills/arcade/SKILL.md" \
17
+ "$TEMP_DIR/claude/skills/arcade/SKILL.md" \
18
+ "$TEMP_DIR/hermes/skills/arcade/SKILL.md" \
19
+ "$TEMP_DIR/openclaw/skills/arcade/SKILL.md"; do
20
+ test -f "$skill_file"
21
+ grep -q '^name: arcade$' "$skill_file"
22
+ grep -Fq "bun $ROOT_DIR/scripts/overlay.ts --enable" "$skill_file"
23
+ grep -Fq 'Do not spawn another agent or task.' "$skill_file"
24
+ grep -Fq 'GamePigeon opens as a borderless HTML overlay' "$skill_file"
25
+ done
26
+
27
+ test -f "$TEMP_DIR/openclaw/hooks/arcade/HOOK.md"
28
+ test -f "$TEMP_DIR/openclaw/hooks/arcade/handler.js"
29
+
30
+ opencode_command="$TEMP_DIR/opencode/commands/arcade.md"
31
+ test -f "$opencode_command"
32
+ grep -q '^description: Open or restore the GamePigeon Arcade overlay$' "$opencode_command"
33
+ grep -Fq "!\`bun $ROOT_DIR/scripts/overlay.ts --enable\`" "$opencode_command"
34
+ grep -Fq 'Running `/arcade` again focuses the existing overlay.' "$opencode_command"
35
+
36
+ CODEX_ONLY="$TEMP_DIR/codex-only"
37
+ CLAUDE_UNUSED="$TEMP_DIR/claude-unused"
38
+ OPENCODE_UNUSED="$TEMP_DIR/opencode-unused"
39
+ CODEX_HOME="$CODEX_ONLY" \
40
+ CLAUDE_CONFIG_DIR="$CLAUDE_UNUSED" \
41
+ OPENCODE_CONFIG_DIR="$OPENCODE_UNUSED" \
42
+ bash "$ROOT_DIR/scripts/install-adapters.sh" --codex
43
+
44
+ test -f "$CODEX_ONLY/skills/arcade/SKILL.md"
45
+ test ! -e "$CLAUDE_UNUSED"
46
+ test ! -e "$OPENCODE_UNUSED"
47
+
48
+ CLI_CODEX_HOME="$TEMP_DIR/cli-codex"
49
+ CODEX_HOME="$CLI_CODEX_HOME" \
50
+ CLAUDE_CONFIG_DIR="$TEMP_DIR/cli-claude" \
51
+ OPENCODE_CONFIG_DIR="$TEMP_DIR/cli-opencode" \
52
+ HERMES_HOME="$TEMP_DIR/cli-hermes" \
53
+ OPENCLAW_HOME="$TEMP_DIR/cli-openclaw" \
54
+ bun "$ROOT_DIR/dist/index.js" install --codex
55
+ test -f "$CLI_CODEX_HOME/skills/arcade/SKILL.md"
56
+
57
+ OPENCODE_ONLY="$TEMP_DIR/opencode-only"
58
+ CODEX_UNUSED="$TEMP_DIR/codex-unused"
59
+ CLAUDE_UNUSED_2="$TEMP_DIR/claude-unused-2"
60
+ CODEX_HOME="$CODEX_UNUSED" \
61
+ CLAUDE_CONFIG_DIR="$CLAUDE_UNUSED_2" \
62
+ OPENCODE_CONFIG_DIR="$OPENCODE_ONLY" \
63
+ bash "$ROOT_DIR/scripts/install-adapters.sh" --opencode
64
+
65
+ test -f "$OPENCODE_ONLY/commands/arcade.md"
66
+ test ! -e "$CODEX_UNUSED"
67
+ test ! -e "$CLAUDE_UNUSED_2"
68
+
69
+ echo "adapter installer tests passed"
@@ -0,0 +1,39 @@
1
+ import yaml
2
+ import sys
3
+ import os
4
+
5
+ config_path = os.path.expanduser('~/.hermes/config.yaml')
6
+ if not os.path.exists(config_path):
7
+ print("Hermes config.yaml not found.")
8
+ sys.exit(0)
9
+
10
+ with open(config_path, 'r') as f:
11
+ config = yaml.safe_load(f) or {}
12
+
13
+ # Define our hooks under the proper event-name keys mapping to arrays of command objects
14
+ root_dir = sys.argv[1]
15
+ root_dir = sys.argv[1]
16
+ new_hooks = {
17
+ 'on_session_start': [
18
+ {'command': f"bun {root_dir}/scripts/overlay.ts --reset"},
19
+ {'command': f"bun {root_dir}/scripts/overlay.ts --show"}
20
+ ],
21
+ 'post_tool_call': [
22
+ {'command': f"bun {root_dir}/scripts/overlay.ts --pause"}
23
+ ],
24
+ 'on_session_end': [
25
+ {'command': f"bun {root_dir}/scripts/overlay.ts --quit"}
26
+ ]
27
+ }
28
+ existing_hooks = config.get('hooks', {})
29
+ for event, cmds in new_hooks.items():
30
+ existing = existing_hooks.get(event, [])
31
+ existing_cmds = {c['command'] for c in existing}
32
+ existing_hooks[event] = existing + [c for c in cmds if c['command'] not in existing_cmds]
33
+ config['hooks'] = existing_hooks
34
+ config['hooks_auto_accept'] = True
35
+
36
+ with open(config_path, 'w') as f:
37
+ yaml.safe_dump(config, f, default_flow_style=False)
38
+
39
+ print("Hermes hooks updated successfully.")