commandmate 0.2.1 → 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.
- package/.next/BUILD_ID +1 -1
- package/.next/app-build-manifest.json +47 -51
- package/.next/app-path-routes-manifest.json +1 -1
- package/.next/build-manifest.json +7 -7
- package/.next/cache/.tsbuildinfo +1 -1
- package/.next/cache/config.json +3 -3
- package/.next/cache/webpack/client-production/0.pack +0 -0
- package/.next/cache/webpack/client-production/1.pack +0 -0
- package/.next/cache/webpack/client-production/2.pack +0 -0
- package/.next/cache/webpack/client-production/index.pack +0 -0
- package/.next/cache/webpack/client-production/index.pack.old +0 -0
- package/.next/cache/webpack/edge-server-production/index.pack +0 -0
- package/.next/cache/webpack/server-production/0.pack +0 -0
- package/.next/cache/webpack/server-production/index.pack +0 -0
- package/.next/next-server.js.nft.json +1 -1
- package/.next/prerender-manifest.json +1 -1
- package/.next/required-server-files.json +1 -1
- package/.next/routes-manifest.json +1 -1
- package/.next/server/app/_not-found/page.js +1 -1
- package/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/api/external-apps/[id]/health/route.js.nft.json +1 -1
- package/.next/server/app/api/external-apps/[id]/route.js.nft.json +1 -1
- package/.next/server/app/api/external-apps/route.js.nft.json +1 -1
- package/.next/server/app/api/hooks/claude-done/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/clone/[jobId]/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/clone/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/excluded/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/restore/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/route.js +1 -1
- package/.next/server/app/api/repositories/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/scan/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/sync/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/auto-yes/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/auto-yes/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/cli-tool/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/current-output/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/current-output/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/files/[...path]/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/interrupt/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/kill-session/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/logs/[filename]/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/logs/[filename]/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/logs/route.js +4 -4
- package/.next/server/app/api/worktrees/[id]/logs/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/memos/[memoId]/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/memos/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/messages/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/prompt-response/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/respond/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/search/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/send/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/slash-commands/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/start-polling/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/tree/[...path]/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/tree/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/upload/[...path]/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/viewed/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/route.js +1 -1
- package/.next/server/app/api/worktrees/route.js.nft.json +1 -1
- package/.next/server/app/page.js +2 -4
- package/.next/server/app/page.js.nft.json +1 -1
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app/proxy/[...path]/route.js.nft.json +1 -1
- package/.next/server/app/worktrees/[id]/files/[...path]/page.js +1 -1
- package/.next/server/app/worktrees/[id]/files/[...path]/page.js.nft.json +1 -1
- package/.next/server/app/worktrees/[id]/files/[...path]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/page.js +3 -15
- package/.next/server/app/worktrees/[id]/page.js.nft.json +1 -1
- package/.next/server/app/worktrees/[id]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/terminal/page.js +3 -3
- package/.next/server/app/worktrees/[id]/terminal/page.js.nft.json +1 -1
- package/.next/server/app/worktrees/[id]/terminal/page_client-reference-manifest.js +1 -1
- package/.next/server/app-paths-manifest.json +7 -8
- package/.next/server/chunks/1287.js +4 -0
- package/.next/server/chunks/2683.js +1 -0
- package/.next/server/chunks/3348.js +1 -0
- package/.next/server/chunks/369.js +1 -0
- package/.next/server/chunks/3860.js +1 -0
- package/.next/server/chunks/4559.js +1 -0
- package/.next/server/chunks/4704.js +35 -0
- package/.next/server/chunks/5781.js +1 -0
- package/.next/server/chunks/5823.js +1 -0
- package/.next/server/chunks/5853.js +1 -0
- package/.next/server/chunks/6837.js +1 -0
- package/.next/server/chunks/7266.js +1 -0
- package/.next/server/chunks/7458.js +1 -0
- package/.next/server/chunks/7536.js +1 -1
- package/.next/server/chunks/8705.js +1 -0
- package/.next/server/chunks/8744.js +1 -0
- package/.next/server/chunks/9367.js +2 -2
- package/.next/server/chunks/9582.js +1 -0
- package/.next/server/functions-config-manifest.json +1 -1
- package/.next/server/middleware-build-manifest.js +1 -1
- package/.next/server/pages/500.html +1 -1
- package/.next/server/pages-manifest.json +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/chunks/1038-3509435b68c0967e.js +1 -0
- package/.next/static/chunks/216-f18f4a9d8b04a91e.js +1 -0
- package/.next/static/chunks/2330-0299b9879f4977d2.js +1 -0
- package/.next/static/chunks/4733-db0112b08802aaa7.js +1 -0
- package/.next/static/chunks/6140-389f1951062dcf4f.js +1 -0
- package/.next/static/chunks/{816-c254f4e2406e696a.js → 816-bb41b20a51ae924a.js} +1 -1
- package/.next/static/chunks/8216-00e20326f32abd12.js +1 -0
- package/.next/static/chunks/9234-b0304101384ca079.js +3 -0
- package/.next/static/chunks/app/layout-07755491d5d57242.js +1 -0
- package/.next/static/chunks/app/page-792c0577dc44e5e5.js +1 -0
- package/.next/static/chunks/app/worktrees/[id]/files/[...path]/page-ce9ac3658f2b7d91.js +1 -0
- package/.next/static/chunks/app/worktrees/[id]/page-9d77c6f755d08086.js +1 -0
- package/.next/static/chunks/app/worktrees/[id]/terminal/page-5d85a7e508ce36d3.js +1 -0
- package/.next/static/chunks/{webpack-4f85dcef6279c6ee.js → webpack-e6531fcf859d9451.js} +1 -1
- package/.next/static/css/4eca30cb81bc52b4.css +3 -0
- package/.next/trace +5 -5
- package/README.md +84 -82
- package/dist/server/src/config/auto-yes-config.js +53 -0
- package/dist/server/src/config/log-config.js +41 -0
- package/dist/server/src/lib/auto-yes-manager.js +6 -4
- package/dist/server/src/lib/claude-session.js +6 -0
- package/dist/server/src/lib/cli-patterns.js +41 -1
- package/dist/server/src/lib/cli-tools/codex.js +16 -3
- package/dist/server/src/lib/cli-tools/manager.js +0 -6
- package/dist/server/src/lib/log-manager.js +2 -6
- package/dist/server/src/lib/pasted-text-helper.js +58 -0
- package/dist/server/src/lib/prompt-detector.js +80 -23
- package/dist/server/src/lib/response-poller.js +30 -43
- package/package.json +2 -1
- package/.next/server/app/_not-found.html +0 -1
- package/.next/server/app/_not-found.meta +0 -6
- package/.next/server/app/_not-found.rsc +0 -10
- package/.next/server/app/index.html +0 -9
- package/.next/server/app/index.meta +0 -5
- package/.next/server/app/index.rsc +0 -8
- package/.next/server/app/worktrees/[id]/simple-terminal/page.js +0 -4
- package/.next/server/app/worktrees/[id]/simple-terminal/page.js.nft.json +0 -1
- package/.next/server/app/worktrees/[id]/simple-terminal/page_client-reference-manifest.js +0 -1
- package/.next/server/chunks/3053.js +0 -1
- package/.next/server/chunks/434.js +0 -1
- package/.next/server/chunks/4471.js +0 -2
- package/.next/server/chunks/6550.js +0 -1
- package/.next/server/chunks/8174.js +0 -23
- package/.next/server/chunks/8887.js +0 -1
- package/.next/server/pages/404.html +0 -1
- package/.next/static/chunks/2957-327e43ef4c12808f.js +0 -1
- package/.next/static/chunks/4343-ebe884a2a80eb033.js +0 -1
- package/.next/static/chunks/4851-45df4d388db5623f.js +0 -1
- package/.next/static/chunks/6568-38a33aa67d82e12b.js +0 -1
- package/.next/static/chunks/6725-f7607851b7d57eb1.js +0 -1
- package/.next/static/chunks/7648-325564a6e12a3257.js +0 -1
- package/.next/static/chunks/app/layout-4804cfba519283cf.js +0 -1
- package/.next/static/chunks/app/page-3926224c4cdf315b.js +0 -1
- package/.next/static/chunks/app/worktrees/[id]/files/[...path]/page-9e5adf57cbbbdf05.js +0 -1
- package/.next/static/chunks/app/worktrees/[id]/page-8bd88bdc29607413.js +0 -1
- package/.next/static/chunks/app/worktrees/[id]/simple-terminal/page-16feb3e86e42f4d1.js +0 -1
- package/.next/static/chunks/app/worktrees/[id]/terminal/page-be802baffc84dbd2.js +0 -1
- package/.next/static/css/28be35e4727ae7ef.css +0 -3
- package/.next/types/app/worktrees/[id]/simple-terminal/page.ts +0 -79
- package/dist/server/src/lib/claude-poller.js +0 -341
- /package/.next/static/chunks/{2117-d845c2cd62e344a6.js → 2117-e31fa477cb500950.js} +0 -0
- /package/.next/static/chunks/app/_not-found/{page-a9d04e58c81115ec.js → page-ac4e4463b39d0cf7.js} +0 -0
- /package/.next/static/chunks/{fd9d1056-bbe86e4ae099d5cd.js → fd9d1056-cfdf4f91f13d3485.js} +0 -0
- /package/.next/static/{oUD-A998xeBoez6zsrTH3 → rppRTm2sRWa4sZE7ili8A}/_buildManifest.js +0 -0
- /package/.next/static/{oUD-A998xeBoez6zsrTH3 → rppRTm2sRWa4sZE7ili8A}/_ssgManifest.js +0 -0
package/README.md
CHANGED
|
@@ -1,152 +1,154 @@
|
|
|
1
1
|
# CommandMate
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
> 「軽量。その場で完結。Claude Codeを、どこからでも動かす。」
|
|
3
|
+
[English](./README.md) | [日本語](./docs/ja/README.md)
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
> "Never miss a prompt — your development companion."
|
|
6
|
+
> "Lightweight. Self-contained. Run Claude Code from anywhere."
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+

|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
## What is this?
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
A development companion tool that manages Claude Code sessions per Git worktree and lets you send instructions from your browser.
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
During your commute, childcare breaks, or lunch — send the next instruction as easily as replying to an email, and keep your side projects moving forward.
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
- CLI の全機能を再現するものではなく、「入力待ち/未確認を見逃さず、すぐ指示を出す」ことに特化しています
|
|
16
|
+
## What it is NOT
|
|
18
17
|
|
|
19
|
-
|
|
18
|
+
- It is not a terminal replacement. It **complements** Claude Code
|
|
19
|
+
- It does not replicate all CLI features — it specializes in "never missing a prompt/confirmation and responding immediately"
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
## Target Users
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
Developers with Claude Code experience who want to continue personal projects alongside their day job.
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
- **ブラウザから指示送信** — スマホ・PCどちらからでもメッセージUIで操作
|
|
27
|
-
- **実行履歴・メモ** — ブランチごとの会話履歴を保持、メモ機能付き
|
|
28
|
-
- **Markdownログビューア** — Claude の詳細出力をMarkdownで閲覧
|
|
29
|
-
- **ファイルビュー** — ワークツリー内のファイルをブラウザから確認
|
|
30
|
-
- **Auto Yes モード** — 確認ダイアログ付きで自動承認を制御
|
|
31
|
-
- **リポジトリ削除** — 不要になったリポジトリをアプリ管理から解除(実ファイルは削除しません)
|
|
32
|
-
- **クローンURL登録** — HTTPS/SSH URLを指定してリポジトリをクローン・登録
|
|
33
|
-
- **Claude Code 特化** — Claude Code セッションの管理に最適化
|
|
34
|
-
- **レスポンシブUI** — デスクトップは2カラム、モバイルはタブベースで最適表示
|
|
25
|
+
## Key Features
|
|
35
26
|
|
|
36
|
-
|
|
27
|
+
- **Prompt/confirmation detection** — Real-time status display in the sidebar (idle/ready/running/waiting)
|
|
28
|
+
- **Send instructions from browser** — Operate via message UI from both mobile and desktop
|
|
29
|
+
- **Execution history & notes** — Retains conversation history per branch with note-taking support
|
|
30
|
+
- **Markdown log viewer** — View Claude's detailed output in Markdown format
|
|
31
|
+
- **File viewer** — Browse worktree files from the browser
|
|
32
|
+
- **Auto Yes mode** — Control automatic approval with a confirmation dialog
|
|
33
|
+
- **Repository removal** — Remove repositories from app management (actual files are not deleted)
|
|
34
|
+
- **Clone URL registration** — Clone and register repositories by specifying HTTPS/SSH URLs
|
|
35
|
+
- **Claude Code optimized** — Optimized for Claude Code session management
|
|
36
|
+
- **Responsive UI** — Two-column layout on desktop, tab-based layout on mobile
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|--------|-------------------|-------------------|
|
|
40
|
-
|  |  |  |
|
|
38
|
+
### Worktree Detail View (Message / Console / History)
|
|
41
39
|
|
|
42
|
-
|
|
40
|
+
| Desktop | Mobile (History) | Mobile (Terminal) |
|
|
41
|
+
|---------|-----------------|-------------------|
|
|
42
|
+
|  |  |  |
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
### Top Page (Mobile)
|
|
45
|
+
|
|
46
|
+

|
|
45
47
|
|
|
46
48
|
## Quick Start
|
|
47
49
|
|
|
48
|
-
###
|
|
50
|
+
### Prerequisites
|
|
49
51
|
|
|
50
|
-
- macOS / Linux
|
|
51
|
-
- Node.js v20
|
|
52
|
-
- Claude CLI
|
|
52
|
+
- macOS / Linux (Windows not supported due to tmux dependency)
|
|
53
|
+
- Node.js v20+, npm, git, tmux, openssl
|
|
54
|
+
- Claude CLI (optional)
|
|
53
55
|
|
|
54
|
-
###
|
|
56
|
+
### Installation
|
|
55
57
|
|
|
56
58
|
```bash
|
|
57
59
|
npm install -g commandmate
|
|
58
60
|
```
|
|
59
61
|
|
|
60
|
-
###
|
|
62
|
+
### Setup and Launch
|
|
61
63
|
|
|
62
64
|
```bash
|
|
63
|
-
commandmate init #
|
|
64
|
-
commandmate start --daemon #
|
|
65
|
+
commandmate init # Dependency check, environment setup, DB initialization
|
|
66
|
+
commandmate start --daemon # Start in background
|
|
65
67
|
```
|
|
66
68
|
|
|
67
|
-
|
|
69
|
+
Open http://localhost:3000 in your browser.
|
|
68
70
|
|
|
69
|
-
### CLI
|
|
71
|
+
### CLI Commands
|
|
70
72
|
|
|
71
|
-
|
|
|
72
|
-
|
|
73
|
-
| `commandmate init` |
|
|
74
|
-
| `commandmate init --defaults` |
|
|
75
|
-
| `commandmate start --daemon` |
|
|
76
|
-
| `commandmate start -p 3001` |
|
|
77
|
-
| `commandmate stop` |
|
|
78
|
-
| `commandmate status` |
|
|
73
|
+
| Command | Description |
|
|
74
|
+
|---------|-------------|
|
|
75
|
+
| `commandmate init` | Initial setup (interactive) |
|
|
76
|
+
| `commandmate init --defaults` | Initial setup (default values) |
|
|
77
|
+
| `commandmate start --daemon` | Start in background |
|
|
78
|
+
| `commandmate start -p 3001` | Start on a specific port |
|
|
79
|
+
| `commandmate stop` | Stop the server |
|
|
80
|
+
| `commandmate status` | Check status |
|
|
79
81
|
|
|
80
|
-
|
|
82
|
+
See the [CLI Setup Guide](./docs/en/user-guide/cli-setup-guide.md) for details.
|
|
81
83
|
|
|
82
|
-
###
|
|
84
|
+
### Mobile Access
|
|
83
85
|
|
|
84
|
-
`commandmate init`
|
|
86
|
+
Enabling external access via `commandmate init` sets `CM_BIND=0.0.0.0`. Access from the same LAN at `http://<your PC's IP>:3000`. For external access, we recommend authentication via a reverse proxy. See the [Security Guide](./docs/en/security-guide.md) for details.
|
|
85
87
|
|
|
86
|
-
##
|
|
88
|
+
## Developer Setup
|
|
87
89
|
|
|
88
|
-
|
|
90
|
+
For contributors or those building a development environment, use git clone.
|
|
89
91
|
|
|
90
92
|
```bash
|
|
91
93
|
git clone https://github.com/Kewton/CommandMate.git
|
|
92
94
|
cd CommandMate
|
|
93
|
-
./scripts/setup.sh #
|
|
95
|
+
./scripts/setup.sh # Auto-runs dependency check, env setup, build, and launch
|
|
94
96
|
```
|
|
95
97
|
|
|
96
|
-
###
|
|
98
|
+
### Manual Setup (for customization)
|
|
97
99
|
|
|
98
100
|
```bash
|
|
99
101
|
git clone https://github.com/Kewton/CommandMate.git
|
|
100
102
|
cd CommandMate
|
|
101
|
-
./scripts/preflight-check.sh #
|
|
103
|
+
./scripts/preflight-check.sh # Dependency check
|
|
102
104
|
npm install
|
|
103
|
-
./scripts/setup-env.sh #
|
|
105
|
+
./scripts/setup-env.sh # Interactive .env generation
|
|
104
106
|
npm run db:init
|
|
105
107
|
npm run build
|
|
106
108
|
npm start
|
|
107
109
|
```
|
|
108
110
|
|
|
109
|
-
> **Note**: `./scripts/*`
|
|
111
|
+
> **Note**: `./scripts/*` scripts are only available in the development environment. For global installs (`npm install -g`), use the `commandmate` CLI.
|
|
110
112
|
|
|
111
|
-
> **Note**:
|
|
113
|
+
> **Note**: Legacy environment variable names (`MCBD_*`) are still supported for backward compatibility, but using the new names (`CM_*`) is recommended.
|
|
112
114
|
|
|
113
115
|
## FAQ
|
|
114
116
|
|
|
115
|
-
**Q:
|
|
116
|
-
A:
|
|
117
|
+
**Q: Does everything run locally?**
|
|
118
|
+
A: The app, database, and sessions all run entirely locally. The only external communication is the Claude CLI's own API calls.
|
|
117
119
|
|
|
118
|
-
**Q:
|
|
119
|
-
A: Cloudflare Tunnel
|
|
120
|
+
**Q: How do I access it from my phone outside the house?**
|
|
121
|
+
A: You can use tunneling services like Cloudflare Tunnel. Within your home, simply connect your phone to the same Wi-Fi as your PC.
|
|
120
122
|
|
|
121
|
-
**Q: Claude Code
|
|
122
|
-
A: Claude Code
|
|
123
|
+
**Q: What about Claude Code's permissions?**
|
|
124
|
+
A: Claude Code's own permission settings apply as-is. This tool does not expand permissions. See [Trust & Safety](./docs/en/TRUST_AND_SAFETY.md) for details.
|
|
123
125
|
|
|
124
|
-
**Q: Windows
|
|
125
|
-
A:
|
|
126
|
+
**Q: Does it work on Windows?**
|
|
127
|
+
A: Not currently supported. macOS / Linux is required due to the tmux dependency. WSL2 has not been tested.
|
|
126
128
|
|
|
127
|
-
**Q:
|
|
128
|
-
A: Claude Code
|
|
129
|
+
**Q: Does it support CLI tools other than Claude Code?**
|
|
130
|
+
A: It supports Claude Code and Codex CLI. Thanks to the extensible Strategy pattern design, additional tools can be added in the future.
|
|
129
131
|
|
|
130
|
-
**Q:
|
|
131
|
-
A:
|
|
132
|
+
**Q: Can multiple people use it?**
|
|
133
|
+
A: Currently designed for individual use. Simultaneous multi-user access is not supported.
|
|
132
134
|
|
|
133
|
-
##
|
|
135
|
+
## Documentation
|
|
134
136
|
|
|
135
|
-
|
|
|
136
|
-
|
|
137
|
-
| [CLI
|
|
138
|
-
| [Web
|
|
139
|
-
| [
|
|
140
|
-
| [
|
|
141
|
-
| [
|
|
142
|
-
| [
|
|
143
|
-
| [
|
|
144
|
-
| [UI/UX
|
|
145
|
-
| [Trust & Safety](./docs/TRUST_AND_SAFETY.md) |
|
|
137
|
+
| Document | Description |
|
|
138
|
+
|----------|-------------|
|
|
139
|
+
| [CLI Setup Guide](./docs/en/user-guide/cli-setup-guide.md) | Installation and initial setup |
|
|
140
|
+
| [Web App Guide](./docs/en/user-guide/webapp-guide.md) | Basic web app operations |
|
|
141
|
+
| [Quick Start](./docs/en/user-guide/quick-start.md) | Using Claude Code commands |
|
|
142
|
+
| [Concept](./docs/en/concept.md) | Vision and problems solved |
|
|
143
|
+
| [Architecture](./docs/en/architecture.md) | System design |
|
|
144
|
+
| [Deployment Guide](./docs/en/DEPLOYMENT.md) | Production environment setup |
|
|
145
|
+
| [Migration Guide](./docs/en/migration-to-commandmate.md) | Migrating from MyCodeBranchDesk |
|
|
146
|
+
| [UI/UX Guide](./docs/en/UI_UX_GUIDE.md) | UI implementation details |
|
|
147
|
+
| [Trust & Safety](./docs/en/TRUST_AND_SAFETY.md) | Security and permissions |
|
|
146
148
|
|
|
147
149
|
## Contributing
|
|
148
150
|
|
|
149
|
-
|
|
151
|
+
Bug reports, feature suggestions, and documentation improvements are welcome. See [CONTRIBUTING.md](./CONTRIBUTING.md) for details.
|
|
150
152
|
|
|
151
153
|
## License
|
|
152
154
|
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Auto-Yes Configuration Constants
|
|
4
|
+
*
|
|
5
|
+
* Shared config for Auto-Yes duration settings.
|
|
6
|
+
* Used by both server (auto-yes-manager.ts, route.ts) and client
|
|
7
|
+
* (AutoYesConfirmDialog.tsx, AutoYesToggle.tsx) components.
|
|
8
|
+
*
|
|
9
|
+
* Issue #225: Duration selection feature
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.DURATION_LABELS = exports.DEFAULT_AUTO_YES_DURATION = exports.ALLOWED_DURATIONS = void 0;
|
|
13
|
+
exports.isAllowedDuration = isAllowedDuration;
|
|
14
|
+
exports.formatTimeRemaining = formatTimeRemaining;
|
|
15
|
+
/** Allowed Auto-Yes durations in milliseconds */
|
|
16
|
+
exports.ALLOWED_DURATIONS = [3600000, 10800000, 28800000];
|
|
17
|
+
/** Default Auto-Yes duration (1 hour = 3600000ms) */
|
|
18
|
+
exports.DEFAULT_AUTO_YES_DURATION = 3600000;
|
|
19
|
+
/** i18n translation keys for each duration value */
|
|
20
|
+
exports.DURATION_LABELS = {
|
|
21
|
+
3600000: 'autoYes.durations.1h',
|
|
22
|
+
10800000: 'autoYes.durations.3h',
|
|
23
|
+
28800000: 'autoYes.durations.8h',
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Type guard: check whether a value is a valid AutoYesDuration.
|
|
27
|
+
* Replaces `as AutoYesDuration` casts with a runtime-safe check.
|
|
28
|
+
*/
|
|
29
|
+
function isAllowedDuration(value) {
|
|
30
|
+
return typeof value === 'number' && exports.ALLOWED_DURATIONS.includes(value);
|
|
31
|
+
}
|
|
32
|
+
/** Milliseconds per second */
|
|
33
|
+
const MS_PER_SECOND = 1000;
|
|
34
|
+
/** Milliseconds per minute */
|
|
35
|
+
const MS_PER_MINUTE = 60000;
|
|
36
|
+
/** Milliseconds per hour */
|
|
37
|
+
const MS_PER_HOUR = 3600000;
|
|
38
|
+
/**
|
|
39
|
+
* Format remaining time as MM:SS (under 1 hour) or H:MM:SS (1 hour or more).
|
|
40
|
+
*
|
|
41
|
+
* Extracted from AutoYesToggle.tsx for direct testability and reuse.
|
|
42
|
+
* Negative remaining time is clamped to 0.
|
|
43
|
+
*/
|
|
44
|
+
function formatTimeRemaining(expiresAt) {
|
|
45
|
+
const remaining = Math.max(0, expiresAt - Date.now());
|
|
46
|
+
const hours = Math.floor(remaining / MS_PER_HOUR);
|
|
47
|
+
const minutes = Math.floor((remaining % MS_PER_HOUR) / MS_PER_MINUTE);
|
|
48
|
+
const seconds = Math.floor((remaining % MS_PER_MINUTE) / MS_PER_SECOND);
|
|
49
|
+
if (hours > 0) {
|
|
50
|
+
return `${hours}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
|
|
51
|
+
}
|
|
52
|
+
return `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
|
|
53
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Log Directory Configuration
|
|
4
|
+
* Issue #11: Centralized LOG_DIR constant
|
|
5
|
+
*
|
|
6
|
+
* Eliminates duplicate LOG_DIR definitions previously found in:
|
|
7
|
+
* - src/lib/log-manager.ts
|
|
8
|
+
* - src/app/api/worktrees/[id]/logs/[filename]/route.ts
|
|
9
|
+
*
|
|
10
|
+
* Dependency chain: log-config.ts -> env.ts -> db-path-resolver.ts (no circular dependency)
|
|
11
|
+
*
|
|
12
|
+
* @module log-config
|
|
13
|
+
*/
|
|
14
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
15
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
16
|
+
};
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.getLogDir = getLogDir;
|
|
19
|
+
const path_1 = __importDefault(require("path"));
|
|
20
|
+
const env_1 = require("../lib/env");
|
|
21
|
+
/**
|
|
22
|
+
* Get the log directory path.
|
|
23
|
+
*
|
|
24
|
+
* Resolution order:
|
|
25
|
+
* 1. CM_LOG_DIR environment variable (with MCBD_LOG_DIR fallback via getEnvByKey)
|
|
26
|
+
* 2. Default: `${process.cwd()}/data/logs`
|
|
27
|
+
*
|
|
28
|
+
* @returns Absolute path to the log directory
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```typescript
|
|
32
|
+
* import { getLogDir } from '../config/log-config';
|
|
33
|
+
*
|
|
34
|
+
* const logDir = getLogDir();
|
|
35
|
+
* // => '/path/to/project/data/logs' (default)
|
|
36
|
+
* // => '/custom/log/dir' (when CM_LOG_DIR is set)
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
function getLogDir() {
|
|
40
|
+
return (0, env_1.getEnvByKey)('CM_LOG_DIR') || path_1.default.join(process.cwd(), 'data', 'logs');
|
|
41
|
+
}
|
|
@@ -28,6 +28,7 @@ const auto_yes_resolver_1 = require("./auto-yes-resolver");
|
|
|
28
28
|
const tmux_1 = require("./tmux");
|
|
29
29
|
const manager_1 = require("./cli-tools/manager");
|
|
30
30
|
const cli_patterns_1 = require("./cli-patterns");
|
|
31
|
+
const auto_yes_config_1 = require("../config/auto-yes-config");
|
|
31
32
|
// =============================================================================
|
|
32
33
|
// Constants (Issue #138)
|
|
33
34
|
// =============================================================================
|
|
@@ -39,8 +40,6 @@ exports.MAX_BACKOFF_MS = 60000;
|
|
|
39
40
|
exports.MAX_CONSECUTIVE_ERRORS = 5;
|
|
40
41
|
/** Maximum concurrent pollers (DoS protection) */
|
|
41
42
|
exports.MAX_CONCURRENT_POLLERS = 50;
|
|
42
|
-
/** Timeout duration: 1 hour in milliseconds */
|
|
43
|
-
const AUTO_YES_TIMEOUT_MS = 3600000;
|
|
44
43
|
/**
|
|
45
44
|
* Number of lines from the end to check for thinking indicators (Issue #191)
|
|
46
45
|
* Matches detectPrompt()'s multiple_choice scan range (50 lines in prompt-detector.ts)
|
|
@@ -116,14 +115,17 @@ function getAutoYesState(worktreeId) {
|
|
|
116
115
|
}
|
|
117
116
|
/**
|
|
118
117
|
* Set the auto-yes enabled state for a worktree
|
|
118
|
+
* @param duration - Optional duration in milliseconds (must be an ALLOWED_DURATIONS value).
|
|
119
|
+
* Defaults to DEFAULT_AUTO_YES_DURATION (1 hour) when omitted.
|
|
119
120
|
*/
|
|
120
|
-
function setAutoYesEnabled(worktreeId, enabled) {
|
|
121
|
+
function setAutoYesEnabled(worktreeId, enabled, duration) {
|
|
121
122
|
if (enabled) {
|
|
122
123
|
const now = Date.now();
|
|
124
|
+
const effectiveDuration = duration ?? auto_yes_config_1.DEFAULT_AUTO_YES_DURATION;
|
|
123
125
|
const state = {
|
|
124
126
|
enabled: true,
|
|
125
127
|
enabledAt: now,
|
|
126
|
-
expiresAt: now +
|
|
128
|
+
expiresAt: now + effectiveDuration,
|
|
127
129
|
};
|
|
128
130
|
autoYesStates.set(worktreeId, state);
|
|
129
131
|
return state;
|
|
@@ -17,6 +17,7 @@ exports.stopClaudeSession = stopClaudeSession;
|
|
|
17
17
|
exports.restartClaudeSession = restartClaudeSession;
|
|
18
18
|
const tmux_1 = require("./tmux");
|
|
19
19
|
const cli_patterns_1 = require("./cli-patterns");
|
|
20
|
+
const pasted_text_helper_1 = require("./pasted-text-helper");
|
|
20
21
|
const child_process_1 = require("child_process");
|
|
21
22
|
const util_1 = require("util");
|
|
22
23
|
const execAsync = (0, util_1.promisify)(child_process_1.exec);
|
|
@@ -359,6 +360,11 @@ async function sendMessageToClaude(worktreeId, message) {
|
|
|
359
360
|
// Send message using sendKeys consistently (CONS-001)
|
|
360
361
|
await (0, tmux_1.sendKeys)(sessionName, message, false);
|
|
361
362
|
await (0, tmux_1.sendKeys)(sessionName, '', true);
|
|
363
|
+
// Issue #212: Detect [Pasted text] and resend Enter for multi-line messages
|
|
364
|
+
// MF-001: Single-line messages skip detection (+0ms overhead)
|
|
365
|
+
if (message.includes('\n')) {
|
|
366
|
+
await (0, pasted_text_helper_1.detectAndResendIfPastedText)(sessionName);
|
|
367
|
+
}
|
|
362
368
|
console.log(`Sent message to Claude session: ${sessionName}`);
|
|
363
369
|
}
|
|
364
370
|
/**
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Shared between response-poller.ts and API routes
|
|
5
5
|
*/
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.GEMINI_PROMPT_PATTERN = exports.CODEX_SEPARATOR_PATTERN = exports.CODEX_PROMPT_PATTERN = exports.CLAUDE_TRUST_DIALOG_PATTERN = exports.CLAUDE_SEPARATOR_PATTERN = exports.CLAUDE_PROMPT_PATTERN = exports.CODEX_THINKING_PATTERN = exports.CLAUDE_THINKING_PATTERN = exports.CLAUDE_SPINNER_CHARS = void 0;
|
|
7
|
+
exports.GEMINI_PROMPT_PATTERN = exports.MAX_PASTED_TEXT_RETRIES = exports.PASTED_TEXT_DETECT_DELAY = exports.PASTED_TEXT_PATTERN = exports.CODEX_SEPARATOR_PATTERN = exports.CODEX_PROMPT_PATTERN = exports.CLAUDE_TRUST_DIALOG_PATTERN = exports.CLAUDE_SEPARATOR_PATTERN = exports.CLAUDE_PROMPT_PATTERN = exports.CODEX_THINKING_PATTERN = exports.CLAUDE_THINKING_PATTERN = exports.CLAUDE_SPINNER_CHARS = void 0;
|
|
8
8
|
exports.detectThinking = detectThinking;
|
|
9
9
|
exports.getCliToolPatterns = getCliToolPatterns;
|
|
10
10
|
exports.stripAnsi = stripAnsi;
|
|
@@ -74,6 +74,44 @@ exports.CODEX_PROMPT_PATTERN = /^›\s*/m;
|
|
|
74
74
|
* Codex separator pattern
|
|
75
75
|
*/
|
|
76
76
|
exports.CODEX_SEPARATOR_PATTERN = /^─.*Worked for.*─+$/m;
|
|
77
|
+
/**
|
|
78
|
+
* Pasted text pattern
|
|
79
|
+
*
|
|
80
|
+
* Claude CLI displays this when it detects multi-line text paste in the
|
|
81
|
+
* ink-based TextInput. The pattern matches the folded display format.
|
|
82
|
+
*
|
|
83
|
+
* @example "[Pasted text #1 +46 lines]"
|
|
84
|
+
* @see Issue #212, #163
|
|
85
|
+
* @designNote PASTE-001: Pattern matches the start of the indicator only.
|
|
86
|
+
* The line count (+XX lines) is variable, so we match the fixed prefix
|
|
87
|
+
* to minimize false negatives. False positive risk is low because
|
|
88
|
+
* "[Pasted text #" is a unique format generated by Claude CLI's ink renderer.
|
|
89
|
+
* @designNote PASTE-001-FP (SF-S4-002): When used in skipPatterns,
|
|
90
|
+
* line-level matching could filter legitimate response lines if Claude's
|
|
91
|
+
* answer text happens to contain "[Pasted text #". This is unlikely and
|
|
92
|
+
* acceptable -- only the affected line would be lost.
|
|
93
|
+
*/
|
|
94
|
+
exports.PASTED_TEXT_PATTERN = /\[Pasted text #\d+/;
|
|
95
|
+
/**
|
|
96
|
+
* Pasted text detection delay (milliseconds)
|
|
97
|
+
*
|
|
98
|
+
* Wait time after sendKeys for tmux buffer to reflect [Pasted text] display.
|
|
99
|
+
*
|
|
100
|
+
* @see Issue #212
|
|
101
|
+
* @designNote PASTE-002: 500ms is the empirically measured time for
|
|
102
|
+
* Claude CLI's ink rendering to complete. capturePane({ startLine: -10 })
|
|
103
|
+
* reads only the last 10 lines since [Pasted text] appears in the most
|
|
104
|
+
* recent few lines.
|
|
105
|
+
*/
|
|
106
|
+
exports.PASTED_TEXT_DETECT_DELAY = 500;
|
|
107
|
+
/**
|
|
108
|
+
* Pasted text detection max retries
|
|
109
|
+
*
|
|
110
|
+
* @see Issue #212
|
|
111
|
+
* @designNote PASTE-003: 3 retries x 500ms = max 1500ms additional delay.
|
|
112
|
+
* Typically resolves on the first attempt (+500ms).
|
|
113
|
+
*/
|
|
114
|
+
exports.MAX_PASTED_TEXT_RETRIES = 3;
|
|
77
115
|
/**
|
|
78
116
|
* Gemini shell prompt pattern
|
|
79
117
|
*/
|
|
@@ -120,6 +158,7 @@ function getCliToolPatterns(cliToolId) {
|
|
|
120
158
|
/^\s*Tip:/, // Tip lines
|
|
121
159
|
/^\s*\?\s*for shortcuts/, // Shortcuts hint
|
|
122
160
|
/to interrupt\)/, // Part of "esc to interrupt" message
|
|
161
|
+
exports.PASTED_TEXT_PATTERN, // [Pasted text #N +XX lines] (Issue #212)
|
|
123
162
|
],
|
|
124
163
|
};
|
|
125
164
|
case 'codex':
|
|
@@ -141,6 +180,7 @@ function getCliToolPatterns(cliToolId) {
|
|
|
141
180
|
/^\s*└/, // Tree output (completion indicator)
|
|
142
181
|
/^\s*│/, // Continuation lines
|
|
143
182
|
/\(.*esc to interrupt\)/, // Interrupt hint
|
|
183
|
+
exports.PASTED_TEXT_PATTERN, // [Pasted text #N +XX lines] (Issue #212, defensive)
|
|
144
184
|
],
|
|
145
185
|
};
|
|
146
186
|
case 'gemini':
|
|
@@ -9,7 +9,15 @@ const base_1 = require("./base");
|
|
|
9
9
|
const tmux_1 = require("../tmux");
|
|
10
10
|
const child_process_1 = require("child_process");
|
|
11
11
|
const util_1 = require("util");
|
|
12
|
+
const pasted_text_helper_1 = require("../pasted-text-helper");
|
|
12
13
|
const execAsync = (0, util_1.promisify)(child_process_1.exec);
|
|
14
|
+
/**
|
|
15
|
+
* Extract error message from unknown error type (DRY)
|
|
16
|
+
* Same pattern as claude-session.ts getErrorMessage()
|
|
17
|
+
*/
|
|
18
|
+
function getErrorMessage(error) {
|
|
19
|
+
return error instanceof Error ? error.message : String(error);
|
|
20
|
+
}
|
|
13
21
|
/**
|
|
14
22
|
* Codex initialization timing constants
|
|
15
23
|
* T2.6: Extracted as constants for maintainability
|
|
@@ -79,7 +87,7 @@ class CodexTool extends base_1.BaseCLITool {
|
|
|
79
87
|
console.log(`✓ Started Codex session: ${sessionName}`);
|
|
80
88
|
}
|
|
81
89
|
catch (error) {
|
|
82
|
-
const errorMessage =
|
|
90
|
+
const errorMessage = getErrorMessage(error);
|
|
83
91
|
throw new Error(`Failed to start Codex session: ${errorMessage}`);
|
|
84
92
|
}
|
|
85
93
|
}
|
|
@@ -105,10 +113,15 @@ class CodexTool extends base_1.BaseCLITool {
|
|
|
105
113
|
await execAsync(`tmux send-keys -t "${sessionName}" C-m`);
|
|
106
114
|
// Wait a moment for the message to be processed
|
|
107
115
|
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
116
|
+
// Issue #212: Detect [Pasted text] and resend Enter for multi-line messages
|
|
117
|
+
// MF-001: Single-line messages skip detection (+0ms overhead)
|
|
118
|
+
if (message.includes('\n')) {
|
|
119
|
+
await (0, pasted_text_helper_1.detectAndResendIfPastedText)(sessionName);
|
|
120
|
+
}
|
|
108
121
|
console.log(`✓ Sent message to Codex session: ${sessionName}`);
|
|
109
122
|
}
|
|
110
123
|
catch (error) {
|
|
111
|
-
const errorMessage =
|
|
124
|
+
const errorMessage = getErrorMessage(error);
|
|
112
125
|
throw new Error(`Failed to send message to Codex: ${errorMessage}`);
|
|
113
126
|
}
|
|
114
127
|
}
|
|
@@ -135,7 +148,7 @@ class CodexTool extends base_1.BaseCLITool {
|
|
|
135
148
|
}
|
|
136
149
|
}
|
|
137
150
|
catch (error) {
|
|
138
|
-
const errorMessage =
|
|
151
|
+
const errorMessage = getErrorMessage(error);
|
|
139
152
|
console.error(`Error stopping Codex session: ${errorMessage}`);
|
|
140
153
|
throw error;
|
|
141
154
|
}
|
|
@@ -9,7 +9,6 @@ const claude_1 = require("./claude");
|
|
|
9
9
|
const codex_1 = require("./codex");
|
|
10
10
|
const gemini_1 = require("./gemini");
|
|
11
11
|
const response_poller_1 = require("../response-poller");
|
|
12
|
-
const claude_poller_1 = require("../claude-poller");
|
|
13
12
|
/**
|
|
14
13
|
* CLI Tool Manager (Singleton)
|
|
15
14
|
* Provides centralized access to all CLI tools
|
|
@@ -160,11 +159,6 @@ class CLIToolManager {
|
|
|
160
159
|
stopPollers(worktreeId, cliToolId) {
|
|
161
160
|
// Stop response-poller for all tools
|
|
162
161
|
(0, response_poller_1.stopPolling)(worktreeId, cliToolId);
|
|
163
|
-
// claude-poller is Claude-specific
|
|
164
|
-
if (cliToolId === 'claude') {
|
|
165
|
-
(0, claude_poller_1.stopPolling)(worktreeId);
|
|
166
|
-
}
|
|
167
|
-
// Future: Add other tool-specific pollers here if needed
|
|
168
162
|
}
|
|
169
163
|
}
|
|
170
164
|
exports.CLIToolManager = CLIToolManager;
|
|
@@ -16,11 +16,7 @@ exports.cleanupOldLogs = cleanupOldLogs;
|
|
|
16
16
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
17
17
|
const path_1 = __importDefault(require("path"));
|
|
18
18
|
const date_fns_1 = require("date-fns");
|
|
19
|
-
const
|
|
20
|
-
/**
|
|
21
|
-
* Log directory configuration (with fallback support - Issue #76)
|
|
22
|
-
*/
|
|
23
|
-
const LOG_DIR = (0, env_1.getEnvByKey)('CM_LOG_DIR') || path_1.default.join(process.cwd(), 'data', 'logs');
|
|
19
|
+
const log_config_1 = require("../config/log-config");
|
|
24
20
|
/**
|
|
25
21
|
* Get log directory for a CLI tool
|
|
26
22
|
*
|
|
@@ -28,7 +24,7 @@ const LOG_DIR = (0, env_1.getEnvByKey)('CM_LOG_DIR') || path_1.default.join(proc
|
|
|
28
24
|
* @returns Log directory path
|
|
29
25
|
*/
|
|
30
26
|
function getCliToolLogDir(cliToolId = 'claude') {
|
|
31
|
-
return path_1.default.join(
|
|
27
|
+
return path_1.default.join((0, log_config_1.getLogDir)(), cliToolId);
|
|
32
28
|
}
|
|
33
29
|
/**
|
|
34
30
|
* Ensure log directory exists
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Pasted text detection + Enter resend helper
|
|
4
|
+
*
|
|
5
|
+
* Shared helper for claude-session.ts and codex.ts to detect
|
|
6
|
+
* [Pasted text #N +XX lines] in tmux output and automatically
|
|
7
|
+
* resend Enter to start processing.
|
|
8
|
+
*
|
|
9
|
+
* @see Issue #212
|
|
10
|
+
* @designNote SF-001: Extracted as common helper to eliminate code duplication
|
|
11
|
+
* between claude-session.ts and codex.ts.
|
|
12
|
+
* @designNote MF-S4-001: sessionName is expected to be generated by
|
|
13
|
+
* getSessionName() in fixed format ('mcbd-{cli}-{worktreeId}').
|
|
14
|
+
* tmux.ts provides double-quote escaping as a defense layer.
|
|
15
|
+
*/
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.detectAndResendIfPastedText = detectAndResendIfPastedText;
|
|
18
|
+
const tmux_1 = require("./tmux");
|
|
19
|
+
const cli_patterns_1 = require("./cli-patterns");
|
|
20
|
+
const logger_1 = require("./logger");
|
|
21
|
+
// SF-S2-002: Logger is generated internally, not injected from callers
|
|
22
|
+
const logger = (0, logger_1.createLogger)('pasted-text');
|
|
23
|
+
/**
|
|
24
|
+
* Detect [Pasted text] in tmux buffer and resend Enter if found.
|
|
25
|
+
*
|
|
26
|
+
* Called after sending a multi-line message to handle Claude CLI's
|
|
27
|
+
* paste detection behavior. The caller is responsible for the
|
|
28
|
+
* message.includes('\n') guard condition.
|
|
29
|
+
*
|
|
30
|
+
* @param sessionName - tmux session name, generated by getSessionName()
|
|
31
|
+
* @returns Promise<void>
|
|
32
|
+
*
|
|
33
|
+
* @designNote PASTE-HELPER: Caller responsibilities vs helper responsibilities:
|
|
34
|
+
* Caller: message.includes('\n') guard + call timing control
|
|
35
|
+
* Helper: detect loop (setTimeout wait -> capturePane -> test -> Enter resend)
|
|
36
|
+
* @designNote SF-S2-005: Uses setTimeout+Promise pattern (project convention)
|
|
37
|
+
*/
|
|
38
|
+
async function detectAndResendIfPastedText(sessionName) {
|
|
39
|
+
for (let attempt = 0; attempt < cli_patterns_1.MAX_PASTED_TEXT_RETRIES; attempt++) {
|
|
40
|
+
// SF-S2-005: Project-standard delay pattern
|
|
41
|
+
await new Promise(resolve => setTimeout(resolve, cli_patterns_1.PASTED_TEXT_DETECT_DELAY));
|
|
42
|
+
// PASTE-002: Read only last 10 lines -- [Pasted text] appears in recent output
|
|
43
|
+
const output = await (0, tmux_1.capturePane)(sessionName, { startLine: -10 });
|
|
44
|
+
if (!cli_patterns_1.PASTED_TEXT_PATTERN.test((0, cli_patterns_1.stripAnsi)(output))) {
|
|
45
|
+
return; // Normal: no Pasted text detected
|
|
46
|
+
}
|
|
47
|
+
// Pasted text detected -- resend Enter
|
|
48
|
+
await (0, tmux_1.sendKeys)(sessionName, '', true);
|
|
49
|
+
if (attempt === cli_patterns_1.MAX_PASTED_TEXT_RETRIES - 1) {
|
|
50
|
+
// SF-S4-001: Log with attempt count for traceability
|
|
51
|
+
logger.warn('Pasted text detection: max retries reached', {
|
|
52
|
+
sessionName,
|
|
53
|
+
maxRetries: cli_patterns_1.MAX_PASTED_TEXT_RETRIES,
|
|
54
|
+
finalAttempt: attempt,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|