tapback-cli 0.0.1

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,67 @@
1
+ # Commit and Push
2
+
3
+ 新しいブランチを作成し、変更をコミットしてプッシュし、PRを作成します。
4
+
5
+ ## ベースブランチ
6
+
7
+ - main
8
+
9
+ ## 手順
10
+
11
+ 1. **ブランチの作成**
12
+ - 現在のブランチ状況を確認
13
+ - 新しいブランチを作成するか、既存のブランチを使用するかをユーザーに確認
14
+ - ユーザーに新しいブランチ名を確認(提案する場合は feature/xxx, fix/xxx, docs/xxx などのプレフィックスを使用)
15
+ - ベースブランチから新しいブランチを作成(必要に応じてベースブランチを最新化)
16
+
17
+ 2. **変更のコミット**
18
+ - git status で変更内容を確認
19
+ - git diff で差分を確認
20
+ - 明確な指示がない限り変更されているファイルはすべて含める。 `git add .` を使用
21
+ - 変更内容に応じた適切なコミットメッセージを作成
22
+ - 変更をステージングしてコミット
23
+ - コミットメッセージには以下を含める:
24
+ - 簡潔な変更内容の要約
25
+ - フッターに以下を追加:
26
+
27
+ ```text
28
+ 🤖 Generated with [Claude Code](https://claude.com/claude-code)
29
+
30
+ Co-Authored-By: Claude <noreply@anthropic.com>
31
+ ```
32
+
33
+ 3. **リモートへのプッシュ**
34
+ - `git push -u origin {branch-name}` でリモートにプッシュ
35
+ - プッシュ後の状態を確認
36
+
37
+ 4. **PRの作成**
38
+ - `gh pr create --assignee @me` でPRを作成し、操作しているユーザーを担当者にアサイン
39
+ - PRのタイトルはコミットメッセージの要約を使用
40
+ - PRの本文には以下を含める:
41
+ - `## Summary` - 変更内容の要約(1-3行)
42
+ - `## Test plan` - テスト方法のチェックリスト
43
+ - フッターに以下を追加:
44
+
45
+ ```text
46
+ 🤖 Generated with [Claude Code](https://claude.com/claude-code)
47
+ ```
48
+
49
+ - 作成後、PRのURLをユーザーに表示
50
+
51
+ 5. **レビューコメントの追加**
52
+ - レビュアーに説明が必要な変更箇所がある場合、該当行にコメントを追加する
53
+ - 以下のコマンドで特定の行にコメントを追加:
54
+
55
+ ```bash
56
+ gh api repos/{owner}/{repo}/pulls/{PR番号}/comments \
57
+ -f body="コメント内容" \
58
+ -f path="ファイルパス" \
59
+ -F position=diff内の行位置 \
60
+ -f commit_id="$(git rev-parse HEAD)"
61
+ ```
62
+
63
+ ## 注意事項
64
+
65
+ - ブランチ名は機能や修正内容がわかるように命名する
66
+ - コミット前に必ず変更内容を確認する
67
+ - .env や credentials.json などの機密情報をコミットしないよう注意する
@@ -0,0 +1,43 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(swift build:*)",
5
+ "WebSearch",
6
+ "Bash(swiftformat:*)",
7
+ "Bash(git checkout:*)",
8
+ "Bash(git add:*)",
9
+ "Bash(git commit -m \"$\\(cat <<''EOF''\nUI改善とメニューバー簡素化\n\n- メニューバーをOpen WindowとQuitのみに簡素化\n- PINステータスにCircleインジケーター追加\n- PIN表示の改行問題を修正\n- デフォルトproxyPortsを空に変更\n- セッション・設定の永続化\n\nCo-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>\nEOF\n\\)\")",
10
+ "Bash(grep:*)",
11
+ "WebFetch(domain:github.com)",
12
+ "WebFetch",
13
+ "Bash(swift package update:*)",
14
+ "Bash(git pull:*)",
15
+ "Bash(git commit -m \"$\\(cat <<''EOF''\nfeat: Add custom quick buttons for mobile interface\n\n- Add QuickButton model with label and command\n- Add quickButtons to ServerManager with persistence\n- Add Quick Button Settings UI in Mac app\n- Render custom buttons in mobile web interface with blue color scheme\n\nUsers can configure custom command buttons \\(e.g., /commit, /push\\) that\nappear on the mobile interface for quick access to frequently used commands.\n\nCo-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>\nEOF\n\\)\")",
16
+ "Bash(git push:*)",
17
+ "Bash(gh run view:*)",
18
+ "Bash(git commit -m \"$\\(cat <<''EOF''\nfix: Update softprops/action-gh-release to v2\n\nCo-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>\nEOF\n\\)\")",
19
+ "Bash(tmux list-sessions:*)",
20
+ "Bash(tmux capture-pane:*)",
21
+ "Bash(claude --resume --help:*)",
22
+ "Bash(claude -r:*)",
23
+ "Bash(ls:*)",
24
+ "Bash(curl:*)",
25
+ "Bash(while read s)",
26
+ "Bash(done)",
27
+ "Bash(python3:*)",
28
+ "Bash(iconutil:*)",
29
+ "Bash(git commit -m \"$\\(cat <<''EOF''\nfeat: Add app icon\n\n- Add mobile+terminal design icon \\(smartphone with >_ prompt\\)\n- Configure Package.swift to include icon resource\n- Set app icon on launch in AppDelegate\n\nCo-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>\nEOF\n\\)\")",
30
+ "Bash(chmod:*)",
31
+ "Bash(npm install)",
32
+ "Bash(timeout 3 node:*)",
33
+ "Bash(lsof:*)",
34
+ "Bash(xargs kill:*)",
35
+ "Bash(git commit:*)",
36
+ "Bash(gh pr create:*)",
37
+ "Bash(gh pr checks:*)",
38
+ "Bash(npx prettier:*)",
39
+ "Bash(npm run format:check:*)",
40
+ "Bash(gh run list:*)"
41
+ ]
42
+ }
43
+ }
@@ -0,0 +1,19 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ lint:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+ - uses: actions/setup-node@v4
15
+ with:
16
+ node-version: 20
17
+ cache: npm
18
+ - run: npm ci
19
+ - run: npm run format:check
@@ -0,0 +1,29 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ jobs:
9
+ publish:
10
+ runs-on: ubuntu-latest
11
+ permissions:
12
+ contents: write
13
+ id-token: write
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+ - uses: actions/setup-node@v4
17
+ with:
18
+ node-version: 20
19
+ cache: npm
20
+ registry-url: https://registry.npmjs.org
21
+ - run: npm ci
22
+ - run: npm run format:check
23
+ - run: npm publish --provenance --access public
24
+ env:
25
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
26
+ - name: Create GitHub Release
27
+ env:
28
+ GH_TOKEN: ${{ github.token }}
29
+ run: gh release create ${{ github.ref_name }} --generate-notes
package/.prettierrc ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "singleQuote": true,
3
+ "trailingComma": "all",
4
+ "semi": true,
5
+ "printWidth": 100
6
+ }
package/CLAUDE.md ADDED
@@ -0,0 +1,84 @@
1
+ # CLAUDE.md
2
+
3
+ このファイルはClaude Codeがこのリポジトリで作業する際のガイダンスを提供します。
4
+
5
+ ## プロジェクト概要
6
+
7
+ Tapbackは、モバイル端末からClaude Code/Codexのターミナルを監視・操作するNode.js CLIツールです。tmuxセッションの出力をWebSocket経由でリアルタイム配信し、localhostで動作するWebアプリへのリバースプロキシ機能も提供します。
8
+
9
+ ## ビルドとテスト
10
+
11
+ ```bash
12
+ # 依存インストール
13
+ npm install
14
+
15
+ # 実行
16
+ node bin/cli.js
17
+
18
+ # ポート指定
19
+ node bin/cli.js 8080
20
+
21
+ # プロキシ付き
22
+ node bin/cli.js --proxy 3000:3001
23
+
24
+ # PIN無効
25
+ node bin/cli.js --no-pin
26
+ ```
27
+
28
+ ## アーキテクチャ
29
+
30
+ ### ディレクトリ構造
31
+
32
+ ```text
33
+ tapback-node/
34
+ ├── package.json # bin: tapback, dependencies
35
+ ├── bin/cli.js # エントリーポイント (#!/usr/bin/env node)
36
+ ├── src/
37
+ │ ├── server.js # Express + WebSocket サーバー (ポート9876)
38
+ │ ├── tmux.js # tmuxコマンド実行 (child_process.execFile)
39
+ │ ├── proxy.js # リバースプロキシ (localhost書き換え)
40
+ │ ├── claudeStatus.js # Claude Codeステータス管理 + hooks設置
41
+ │ ├── config.js # 設定ファイル (~/.tapback.json) 読み書き
42
+ │ └── html.js # モバイルUI HTML生成
43
+ ```
44
+
45
+ ### 主要コンポーネント
46
+
47
+ - **server.js**: Express + wsベースのサーバー。ターミナルUI、PIN認証(メイン/設定で別PIN)、WebSocket、設定API
48
+ - **tmux.js**: tmuxコマンド(capture-pane, send-keys, list-sessions, display-message)のPromiseラッパー
49
+ - **proxy.js**: http-proxyベースのリバースプロキシ。localhost参照を自動的にMacのIPに書き換え
50
+ - **claudeStatus.js**: Claude Codeフック設置、ステータス受信・保存。`~/.claude/settings.json`を更新
51
+ - **config.js**: `~/.tapback.json`による設定永続化(PIN有効/無効、プロキシポート、クイックボタン)
52
+ - **html.js**: モバイル向けレスポンシブWebUI、設定ページ、PIN認証ページ
53
+
54
+ ### 通信フロー
55
+
56
+ 1. モバイル → `/api/sessions` でtmuxセッション一覧取得
57
+ 2. モバイル → `/ws` WebSocket接続
58
+ 3. サーバー → 全tmuxセッションの出力を1秒ごとにブロードキャスト
59
+ 4. モバイル → WebSocketでコマンド送信 → `tmux.sendKeys`でtmuxに転送
60
+
61
+ ### 認証
62
+
63
+ - メインページ (`/`): 4桁PIN、Cookie(24h)。`--no-pin`または設定で無効化可能
64
+ - 設定ページ (`/settings`): 別の4桁PIN、常に有効
65
+
66
+ ## コーディング規約
67
+
68
+ - Node.js (CommonJS)
69
+ - 非同期処理はasync/awaitとPromise
70
+ - サーバーはExpress + ws
71
+
72
+ ## 依存関係
73
+
74
+ - **express**: HTTPサーバー
75
+ - **ws**: WebSocket
76
+ - **http-proxy**: リバースプロキシ
77
+ - **cookie-parser**: Cookie認証
78
+
79
+ ## 注意点
80
+
81
+ - tmuxコマンドは明示的に`session:0.0`を指定(複数ウィンドウ/ペーン対応のため)
82
+ - PATH設定で`/opt/homebrew/bin`と`/usr/local/bin`を追加(tmux検出用)
83
+ - プロキシはlocalhost参照を自動的にMacのIPに書き換え
84
+ - 設定ファイルは`~/.tapback.json`に保存
package/README.md ADDED
@@ -0,0 +1,71 @@
1
+ # Tapback
2
+
3
+ モバイル端末からClaude Code/Codexのターミナルを監視・操作するNode.js CLIツール。
4
+
5
+ tmuxセッションの出力をWebSocket経由でリアルタイム配信し、スマホからターミナルの閲覧・コマンド入力ができます。
6
+
7
+ ## 機能
8
+
9
+ - tmuxセッションのリアルタイム監視(1秒間隔)
10
+ - モバイルからのコマンド送信
11
+ - Claude Codeのステータス表示(hooks連携)
12
+ - localhostアプリのリバースプロキシ
13
+ - PIN認証(メインページ・設定ページで別PIN)
14
+ - Web設定画面(プロキシポート、クイックボタン)
15
+
16
+ ## インストール
17
+
18
+ ```bash
19
+ npm install
20
+ ```
21
+
22
+ ## 使い方
23
+
24
+ ```bash
25
+ # 基本起動(ポート9876)
26
+ node bin/cli.js
27
+
28
+ # ポート指定
29
+ node bin/cli.js 8080
30
+
31
+ # リバースプロキシ付き(localhost:3000 → :3001で外部公開)
32
+ node bin/cli.js --proxy 3000:3001
33
+
34
+ # PIN認証を無効化
35
+ node bin/cli.js --no-pin
36
+ ```
37
+
38
+ 起動すると以下が表示されます:
39
+
40
+ ```text
41
+ Tapback is running!
42
+ URL: http://192.168.x.x:9876/
43
+ Settings: http://192.168.x.x:9876/settings
44
+ PIN: 1234
45
+ Settings PIN: 5678
46
+ ```
47
+
48
+ スマホで表示されたURLにアクセスし、PINを入力してください。
49
+
50
+ ## 設定
51
+
52
+ 設定は`~/.tapback.json`に保存されます。Web UIの`/settings`からも編集可能です。
53
+
54
+ ```json
55
+ {
56
+ "pinEnabled": true,
57
+ "proxyPorts": { "3000": 3001 },
58
+ "quickButtons": [{ "label": "push", "command": "/commit-push" }]
59
+ }
60
+ ```
61
+
62
+ ## Claude Code連携
63
+
64
+ 起動時にClaude Codeのhooksを自動設置します(`~/.claude/hooks/tapback-status-hook.sh`)。
65
+ Claude Codeのセッション状態(starting/processing/idle/waiting/ended)がリアルタイムでモバイルUIに反映されます。
66
+
67
+ ## 必要環境
68
+
69
+ - Node.js 18+
70
+ - tmux
71
+ - macOS(ローカルIP取得にen0を使用)
package/bin/cli.js ADDED
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { createServer, getLocalIP } = require('../src/server');
4
+ const { createProxyServer } = require('../src/proxy');
5
+ const { installHooks } = require('../src/claudeStatus');
6
+ const config = require('../src/config');
7
+
8
+ // Load saved config
9
+ const cfg = config.load();
10
+
11
+ // CLI args override config
12
+ let port = 9876;
13
+ const proxyPorts = { ...cfg.proxyPorts };
14
+
15
+ for (let i = 2; i < process.argv.length; i++) {
16
+ const arg = process.argv[i];
17
+ if (arg === '--proxy' && process.argv[i + 1]) {
18
+ const [target, external] = process.argv[++i].split(':').map(Number);
19
+ if (target && external) proxyPorts[String(target)] = external;
20
+ } else if (arg === '--no-pin') {
21
+ cfg.pinEnabled = false;
22
+ } else if (/^\d+$/.test(arg)) {
23
+ port = Number(arg);
24
+ }
25
+ }
26
+
27
+ // Install Claude Code hooks
28
+ try {
29
+ installHooks(port);
30
+ console.log('[Tapback] Claude Code hooks installed');
31
+ } catch (e) {
32
+ console.warn('[Tapback] Failed to install hooks:', e.message);
33
+ }
34
+
35
+ const macIP = getLocalIP();
36
+ const firstExternalPort = Object.values(proxyPorts)[0];
37
+
38
+ const { pin, settingsPin } = createServer({
39
+ port,
40
+ pinEnabled: cfg.pinEnabled,
41
+ quickButtons: cfg.quickButtons,
42
+ appURL: firstExternalPort ? `http://${macIP}:${firstExternalPort}/` : null,
43
+ });
44
+
45
+ // Start proxy servers
46
+ for (const [target, external] of Object.entries(proxyPorts)) {
47
+ createProxyServer(Number(target), Number(external), macIP, proxyPorts);
48
+ }
49
+
50
+ console.log('');
51
+ console.log(' Tapback is running!');
52
+ console.log(` URL: http://${macIP}:${port}/`);
53
+ console.log(` Settings: http://${macIP}:${port}/settings`);
54
+ console.log(` PIN: ${pin} ${cfg.pinEnabled ? '' : '(disabled)'}`);
55
+ console.log(` Settings PIN: ${settingsPin}`);
56
+ console.log('');
57
+
58
+ process.on('SIGINT', () => process.exit(0));
59
+ process.on('SIGTERM', () => process.exit(0));
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "tapback-cli",
3
+ "version": "0.0.1",
4
+ "description": "Monitor and control tmux sessions from your mobile device",
5
+ "bin": {
6
+ "tapback": "./bin/cli.js"
7
+ },
8
+ "scripts": {
9
+ "start": "node bin/cli.js",
10
+ "format": "prettier --write .",
11
+ "format:check": "prettier --check ."
12
+ },
13
+ "keywords": [
14
+ "tmux",
15
+ "terminal",
16
+ "mobile",
17
+ "claude-code"
18
+ ],
19
+ "license": "MIT",
20
+ "dependencies": {
21
+ "chalk": "^5.3.0",
22
+ "cookie-parser": "^1.4.6",
23
+ "express": "^4.18.2",
24
+ "http-proxy": "^1.18.1",
25
+ "ws": "^8.16.0"
26
+ },
27
+ "devDependencies": {
28
+ "prettier": "^3.8.1"
29
+ }
30
+ }
@@ -0,0 +1,113 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const os = require('os');
4
+
5
+ class ClaudeStatusStore {
6
+ constructor() {
7
+ this.statuses = new Map(); // key: project_dir or session_id
8
+ }
9
+
10
+ update(status) {
11
+ const key = status.project_dir || status.session_id;
12
+ this.statuses.set(key, { ...status, timestamp: new Date().toISOString() });
13
+ // Clean up old (>1h)
14
+ const cutoff = Date.now() - 3600000;
15
+ for (const [k, v] of this.statuses) {
16
+ if (new Date(v.timestamp).getTime() < cutoff) this.statuses.delete(k);
17
+ }
18
+ }
19
+
20
+ getAll() {
21
+ return [...this.statuses.values()].sort(
22
+ (a, b) => new Date(b.timestamp) - new Date(a.timestamp),
23
+ );
24
+ }
25
+ }
26
+
27
+ function installHooks(port = 9876) {
28
+ const homeDir = os.homedir();
29
+ const hooksDir = path.join(homeDir, '.claude', 'hooks');
30
+ const hookScriptPath = path.join(hooksDir, 'tapback-status-hook.sh');
31
+ const settingsPath = path.join(homeDir, '.claude', 'settings.json');
32
+
33
+ fs.mkdirSync(hooksDir, { recursive: true });
34
+
35
+ const hookScript = `#!/bin/bash
36
+ # Tapback Status Hook for Claude Code
37
+ # Auto-installed by Tapback
38
+
39
+ set -e
40
+
41
+ input=$(cat)
42
+
43
+ hook_event_name=$(echo "$input" | jq -r '.hook_event_name // empty')
44
+ session_id=$(echo "$input" | jq -r '.session_id // empty')
45
+ cwd=$(echo "$input" | jq -r '.cwd // empty')
46
+ model=$(echo "$input" | jq -r '.model // empty')
47
+
48
+ if [ -z "$session_id" ]; then
49
+ exit 0
50
+ fi
51
+
52
+ case "$hook_event_name" in
53
+ "SessionStart") status="starting" ;;
54
+ "UserPromptSubmit") status="processing" ;;
55
+ "Stop") status="idle" ;;
56
+ "Notification")
57
+ notification_type=$(echo "$input" | jq -r '.notification_type // empty')
58
+ if [ "$notification_type" = "idle_prompt" ]; then
59
+ status="waiting"
60
+ else
61
+ exit 0
62
+ fi
63
+ ;;
64
+ "SessionEnd") status="ended" ;;
65
+ *) exit 0 ;;
66
+ esac
67
+
68
+ TAPBACK_URL="\${TAPBACK_URL:-http://localhost:${port}}"
69
+
70
+ curl -s -X POST "\${TAPBACK_URL}/api/claude-status" \\
71
+ -H "Content-Type: application/json" \\
72
+ -d "{\\"session_id\\":\\"$session_id\\",\\"status\\":\\"$status\\",\\"project_dir\\":\\"$cwd\\",\\"model\\":\\"$model\\"}" \\
73
+ >/dev/null 2>&1 || true
74
+
75
+ exit 0
76
+ `;
77
+
78
+ fs.writeFileSync(hookScriptPath, hookScript, { mode: 0o755 });
79
+
80
+ // Update settings.json
81
+ const hooksConfig = {};
82
+ const hookCommand = hookScriptPath;
83
+ for (const event of ['SessionStart', 'UserPromptSubmit', 'Stop', 'SessionEnd']) {
84
+ hooksConfig[event] = [{ hooks: [{ type: 'command', command: hookCommand }] }];
85
+ }
86
+ hooksConfig['Notification'] = [
87
+ { matcher: 'idle_prompt', hooks: [{ type: 'command', command: hookCommand }] },
88
+ ];
89
+
90
+ let settings = {};
91
+ try {
92
+ settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
93
+ } catch {}
94
+
95
+ const existing = settings.hooks || {};
96
+ for (const [key, value] of Object.entries(hooksConfig)) {
97
+ const arr = existing[key] || [];
98
+ const hasTapback = arr.some(
99
+ (item) =>
100
+ item.hooks &&
101
+ item.hooks.some((h) => h.command && h.command.includes('tapback-status-hook.sh')),
102
+ );
103
+ if (!hasTapback) {
104
+ existing[key] = [...arr, ...value];
105
+ }
106
+ }
107
+ settings.hooks = existing;
108
+
109
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
110
+ return true;
111
+ }
112
+
113
+ module.exports = { ClaudeStatusStore, installHooks };
package/src/config.js ADDED
@@ -0,0 +1,26 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const os = require('os');
4
+
5
+ const CONFIG_PATH = path.join(os.homedir(), '.tapback.json');
6
+
7
+ const DEFAULTS = {
8
+ pinEnabled: true,
9
+ proxyPorts: {}, // { "3000": 3001 } = localhost:3000 -> :3001
10
+ quickButtons: [], // [{ label, command }]
11
+ };
12
+
13
+ function load() {
14
+ try {
15
+ const data = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8'));
16
+ return { ...DEFAULTS, ...data };
17
+ } catch {
18
+ return { ...DEFAULTS };
19
+ }
20
+ }
21
+
22
+ function save(config) {
23
+ fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
24
+ }
25
+
26
+ module.exports = { load, save, CONFIG_PATH };