commandmate 0.2.2 → 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 +48 -52
- 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/repositories/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/current-output/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/logs/[filename]/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/logs/route.js +4 -4
- package/.next/server/app/api/worktrees/[id]/route.js +1 -1
- package/.next/server/app/api/worktrees/route.js +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/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 +13 -14
- 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 +1 -1
- 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 +4 -4
- package/dist/server/src/config/log-config.js +41 -0
- 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/prompt-detector.js +80 -23
- package/dist/server/src/lib/response-poller.js +29 -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/4343-ebe884a2a80eb033.js +0 -1
- package/.next/static/chunks/4851-45df4d388db5623f.js +0 -1
- package/.next/static/chunks/6568-c65d7e4d7db7655b.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/9325-9e98829c1e75f42f.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-7eb14f8043796805.js +0 -1
- package/.next/static/chunks/app/worktrees/[id]/page-912c3c4c66821d99.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/d4b58a1129eff6af.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/{HhG0EHeG9E4wTJ4sqnLdv → rppRTm2sRWa4sZE7ili8A}/_buildManifest.js +0 -0
- /package/.next/static/{HhG0EHeG9E4wTJ4sqnLdv → 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
|
|
|
@@ -16,11 +16,11 @@ exports.formatTimeRemaining = formatTimeRemaining;
|
|
|
16
16
|
exports.ALLOWED_DURATIONS = [3600000, 10800000, 28800000];
|
|
17
17
|
/** Default Auto-Yes duration (1 hour = 3600000ms) */
|
|
18
18
|
exports.DEFAULT_AUTO_YES_DURATION = 3600000;
|
|
19
|
-
/**
|
|
19
|
+
/** i18n translation keys for each duration value */
|
|
20
20
|
exports.DURATION_LABELS = {
|
|
21
|
-
3600000: '
|
|
22
|
-
10800000: '
|
|
23
|
-
28800000: '
|
|
21
|
+
3600000: 'autoYes.durations.1h',
|
|
22
|
+
10800000: 'autoYes.durations.3h',
|
|
23
|
+
28800000: 'autoYes.durations.8h',
|
|
24
24
|
};
|
|
25
25
|
/**
|
|
26
26
|
* Type guard: check whether a value is a valid AutoYesDuration.
|
|
@@ -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
|
+
}
|
|
@@ -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
|
|
@@ -8,6 +8,40 @@ exports.detectPrompt = detectPrompt;
|
|
|
8
8
|
exports.getAnswerInput = getAnswerInput;
|
|
9
9
|
const logger_1 = require("./logger");
|
|
10
10
|
const logger = (0, logger_1.createLogger)('prompt-detector');
|
|
11
|
+
/**
|
|
12
|
+
* Maximum number of lines to retain in rawContent.
|
|
13
|
+
* Tail lines are preserved (instruction text typically appears just before the prompt).
|
|
14
|
+
* @see truncateRawContent
|
|
15
|
+
*/
|
|
16
|
+
const RAW_CONTENT_MAX_LINES = 200;
|
|
17
|
+
/**
|
|
18
|
+
* Maximum number of characters to retain in rawContent.
|
|
19
|
+
* Tail characters are preserved.
|
|
20
|
+
* @see truncateRawContent
|
|
21
|
+
*/
|
|
22
|
+
const RAW_CONTENT_MAX_CHARS = 5000;
|
|
23
|
+
/**
|
|
24
|
+
* Truncate raw content to fit within size limits.
|
|
25
|
+
* Preserves the tail (end) of the content since instruction text
|
|
26
|
+
* typically appears just before the prompt at the end of output.
|
|
27
|
+
*
|
|
28
|
+
* Security: No regular expressions used -- no ReDoS risk. [SF-S4-002]
|
|
29
|
+
* String.split('\n') and String.slice() are literal string operations only.
|
|
30
|
+
*
|
|
31
|
+
* @param content - The content to truncate
|
|
32
|
+
* @returns Truncated content (last RAW_CONTENT_MAX_LINES lines, max RAW_CONTENT_MAX_CHARS characters)
|
|
33
|
+
*/
|
|
34
|
+
function truncateRawContent(content) {
|
|
35
|
+
const lines = content.split('\n');
|
|
36
|
+
const truncatedLines = lines.length > RAW_CONTENT_MAX_LINES
|
|
37
|
+
? lines.slice(-RAW_CONTENT_MAX_LINES)
|
|
38
|
+
: lines;
|
|
39
|
+
let result = truncatedLines.join('\n');
|
|
40
|
+
if (result.length > RAW_CONTENT_MAX_CHARS) {
|
|
41
|
+
result = result.slice(-RAW_CONTENT_MAX_CHARS);
|
|
42
|
+
}
|
|
43
|
+
return result;
|
|
44
|
+
}
|
|
11
45
|
/**
|
|
12
46
|
* Yes/no pattern definitions for data-driven matching.
|
|
13
47
|
* Each entry defines a regex pattern and its associated default option.
|
|
@@ -27,6 +61,32 @@ const YES_NO_PATTERNS = [
|
|
|
27
61
|
// (yes/no) - no default
|
|
28
62
|
{ regex: /^(.+)\s+\(yes\/no\)\s*$/m },
|
|
29
63
|
];
|
|
64
|
+
/**
|
|
65
|
+
* Creates a yes/no prompt detection result.
|
|
66
|
+
* Centralizes the repeated construction of yes_no PromptDetectionResult objects
|
|
67
|
+
* used by both YES_NO_PATTERNS matching and Approve pattern matching.
|
|
68
|
+
*
|
|
69
|
+
* @param question - The question text
|
|
70
|
+
* @param cleanContent - The clean content string
|
|
71
|
+
* @param rawContent - The raw content string (last 20 lines, trimmed)
|
|
72
|
+
* @param defaultOption - Optional default option ('yes' or 'no')
|
|
73
|
+
* @returns PromptDetectionResult with isPrompt: true and yes_no prompt data
|
|
74
|
+
*/
|
|
75
|
+
function yesNoPromptResult(question, cleanContent, rawContent, defaultOption) {
|
|
76
|
+
return {
|
|
77
|
+
isPrompt: true,
|
|
78
|
+
promptData: {
|
|
79
|
+
type: 'yes_no',
|
|
80
|
+
question,
|
|
81
|
+
options: ['yes', 'no'],
|
|
82
|
+
status: 'pending',
|
|
83
|
+
...(defaultOption !== undefined && { defaultOption }),
|
|
84
|
+
instructionText: rawContent,
|
|
85
|
+
},
|
|
86
|
+
cleanContent,
|
|
87
|
+
rawContent,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
30
90
|
/**
|
|
31
91
|
* Detect if output contains an interactive prompt
|
|
32
92
|
*
|
|
@@ -51,7 +111,8 @@ const YES_NO_PATTERNS = [
|
|
|
51
111
|
function detectPrompt(output, options) {
|
|
52
112
|
logger.debug('detectPrompt:start', { outputLength: output.length });
|
|
53
113
|
const lines = output.split('\n');
|
|
54
|
-
|
|
114
|
+
// [SF-003] [MF-S2-001] Expanded from 10 to 20 lines for rawContent coverage
|
|
115
|
+
const lastLines = lines.slice(-20).join('\n');
|
|
55
116
|
// Pattern 0: Multiple choice (numbered options with ❯ indicator)
|
|
56
117
|
// Example:
|
|
57
118
|
// Do you want to proceed?
|
|
@@ -68,21 +129,12 @@ function detectPrompt(output, options) {
|
|
|
68
129
|
return multipleChoiceResult;
|
|
69
130
|
}
|
|
70
131
|
// Patterns 1-4: Yes/no patterns (data-driven matching)
|
|
132
|
+
const trimmedLastLines = lastLines.trim();
|
|
71
133
|
for (const pattern of YES_NO_PATTERNS) {
|
|
72
134
|
const match = lastLines.match(pattern.regex);
|
|
73
135
|
if (match) {
|
|
74
136
|
const question = match[1].trim();
|
|
75
|
-
return
|
|
76
|
-
isPrompt: true,
|
|
77
|
-
promptData: {
|
|
78
|
-
type: 'yes_no',
|
|
79
|
-
question,
|
|
80
|
-
options: ['yes', 'no'],
|
|
81
|
-
status: 'pending',
|
|
82
|
-
...(pattern.defaultOption !== undefined && { defaultOption: pattern.defaultOption }),
|
|
83
|
-
},
|
|
84
|
-
cleanContent: question,
|
|
85
|
-
};
|
|
137
|
+
return yesNoPromptResult(question, question, trimmedLastLines, pattern.defaultOption);
|
|
86
138
|
}
|
|
87
139
|
}
|
|
88
140
|
// Pattern 5: Approve?
|
|
@@ -93,16 +145,7 @@ function detectPrompt(output, options) {
|
|
|
93
145
|
const content = approveMatch[1].trim();
|
|
94
146
|
// If there's content before "Approve?", include it in the question
|
|
95
147
|
const question = content ? `${content} Approve?` : 'Approve?';
|
|
96
|
-
return
|
|
97
|
-
isPrompt: true,
|
|
98
|
-
promptData: {
|
|
99
|
-
type: 'yes_no',
|
|
100
|
-
question: question,
|
|
101
|
-
options: ['yes', 'no'],
|
|
102
|
-
status: 'pending',
|
|
103
|
-
},
|
|
104
|
-
cleanContent: content || 'Approve?',
|
|
105
|
-
};
|
|
148
|
+
return yesNoPromptResult(question, content || 'Approve?', trimmedLastLines);
|
|
106
149
|
}
|
|
107
150
|
// No prompt detected
|
|
108
151
|
logger.debug('detectPrompt:complete', { isPrompt: false });
|
|
@@ -272,7 +315,7 @@ function isContinuationLine(rawLine, line) {
|
|
|
272
315
|
// the question line would be misclassified as a continuation line, causing
|
|
273
316
|
// questionEndIndex to remain -1 and Layer 5 SEC-001 to block detection.
|
|
274
317
|
const endsWithQuestion = line.endsWith('?') || line.endsWith('\uff1f');
|
|
275
|
-
const hasLeadingSpaces =
|
|
318
|
+
const hasLeadingSpaces = /^\s{2,}[^\d]/.test(rawLine) && !/^\s*\d+\./.test(rawLine) && !endsWithQuestion;
|
|
276
319
|
// Short fragment (< 5 chars, excluding question-ending lines)
|
|
277
320
|
const isShortFragment = line.length < 5 && !endsWithQuestion;
|
|
278
321
|
// Path string continuation: lines starting with / or ~, or alphanumeric-only fragments (2+ chars)
|
|
@@ -414,6 +457,18 @@ function detectMultipleChoicePrompt(output, options) {
|
|
|
414
457
|
// No clear question found - use a generic one
|
|
415
458
|
question = 'Please select an option:';
|
|
416
459
|
}
|
|
460
|
+
// Extract instruction text: full prompt block (context before question through all options/descriptions)
|
|
461
|
+
// Captures the complete AskUserQuestion block including option descriptions and navigation hints.
|
|
462
|
+
let instructionText;
|
|
463
|
+
if (questionEndIndex >= 0) {
|
|
464
|
+
const contextStart = Math.max(0, questionEndIndex - 19);
|
|
465
|
+
const blockLines = lines.slice(contextStart, effectiveEnd)
|
|
466
|
+
.map(l => l.trimEnd());
|
|
467
|
+
const joined = blockLines.join('\n').trim();
|
|
468
|
+
if (joined.length > 0) {
|
|
469
|
+
instructionText = joined;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
417
472
|
return {
|
|
418
473
|
isPrompt: true,
|
|
419
474
|
promptData: {
|
|
@@ -430,8 +485,10 @@ function detectMultipleChoicePrompt(output, options) {
|
|
|
430
485
|
};
|
|
431
486
|
}),
|
|
432
487
|
status: 'pending',
|
|
488
|
+
instructionText,
|
|
433
489
|
},
|
|
434
490
|
cleanContent: question.trim(),
|
|
491
|
+
rawContent: truncateRawContent(output.trim()), // Issue #235: complete prompt output (truncated) [MF-001]
|
|
435
492
|
};
|
|
436
493
|
}
|
|
437
494
|
/**
|
|
@@ -53,6 +53,25 @@ const MAX_POLLING_DURATION = 5 * 60 * 1000;
|
|
|
53
53
|
* @constant
|
|
54
54
|
*/
|
|
55
55
|
const RESPONSE_THINKING_TAIL_LINE_COUNT = 5;
|
|
56
|
+
/**
|
|
57
|
+
* Gemini auth/loading state indicators that should not be treated as complete responses.
|
|
58
|
+
* Braille spinner characters are shared with CLAUDE_SPINNER_CHARS in cli-patterns.ts.
|
|
59
|
+
* Extracted to module level for clarity and to avoid re-creation on each call.
|
|
60
|
+
*/
|
|
61
|
+
const GEMINI_LOADING_INDICATORS = [
|
|
62
|
+
'Waiting for auth',
|
|
63
|
+
'\u280b', '\u2819', '\u2839', '\u2838', '\u283c', '\u2834', '\u2826', '\u2827', '\u2807', '\u280f',
|
|
64
|
+
];
|
|
65
|
+
/**
|
|
66
|
+
* Creates an incomplete extraction result with empty response.
|
|
67
|
+
* Centralizes the repeated pattern of returning an in-progress/incomplete state.
|
|
68
|
+
*
|
|
69
|
+
* @param lineCount - Current line count for state tracking
|
|
70
|
+
* @returns ExtractionResult with empty response and isComplete: false
|
|
71
|
+
*/
|
|
72
|
+
function incompleteResult(lineCount) {
|
|
73
|
+
return { response: '', isComplete: false, lineCount };
|
|
74
|
+
}
|
|
56
75
|
/**
|
|
57
76
|
* Active pollers map: "worktreeId:cliToolId" -> NodeJS.Timeout
|
|
58
77
|
*/
|
|
@@ -335,11 +354,7 @@ function extractResponse(output, lastCapturedLine, cliToolId) {
|
|
|
335
354
|
// Prevents false blocking when completed thinking summaries appear in the response body.
|
|
336
355
|
const responseTailLines = response.split('\n').slice(-RESPONSE_THINKING_TAIL_LINE_COUNT).join('\n');
|
|
337
356
|
if (thinkingPattern.test(responseTailLines)) {
|
|
338
|
-
return
|
|
339
|
-
response: '',
|
|
340
|
-
isComplete: false,
|
|
341
|
-
lineCount: totalLines,
|
|
342
|
-
};
|
|
357
|
+
return incompleteResult(totalLines);
|
|
343
358
|
}
|
|
344
359
|
// CRITICAL FIX: Detect and skip Claude Code startup banner/screen
|
|
345
360
|
// The startup screen contains: ASCII art logo, version info, prompt, separator
|
|
@@ -366,20 +381,12 @@ function extractResponse(output, lastCapturedLine, cliToolId) {
|
|
|
366
381
|
!/^─+$/.test(trimmed);
|
|
367
382
|
});
|
|
368
383
|
if (contentLines.length === 0) {
|
|
369
|
-
return
|
|
370
|
-
response: '',
|
|
371
|
-
isComplete: false,
|
|
372
|
-
lineCount: totalLines,
|
|
373
|
-
};
|
|
384
|
+
return incompleteResult(totalLines);
|
|
374
385
|
}
|
|
375
386
|
}
|
|
376
387
|
else if ((hasBannerArt || hasVersionInfo || hasStartupTips || hasProjectInit) && response.length < 2000) {
|
|
377
388
|
// No user prompt found, but has banner characteristics - likely initial startup
|
|
378
|
-
return
|
|
379
|
-
response: '',
|
|
380
|
-
isComplete: false,
|
|
381
|
-
lineCount: totalLines,
|
|
382
|
-
};
|
|
389
|
+
return incompleteResult(totalLines);
|
|
383
390
|
}
|
|
384
391
|
}
|
|
385
392
|
// Gemini-specific check: ensure response contains actual content (✦ marker)
|
|
@@ -388,31 +395,13 @@ function extractResponse(output, lastCapturedLine, cliToolId) {
|
|
|
388
395
|
const bannerCharCount = (response.match(/[░███]/g) || []).length;
|
|
389
396
|
const totalChars = response.length;
|
|
390
397
|
if (bannerCharCount > totalChars * 0.3) {
|
|
391
|
-
return
|
|
392
|
-
response: '',
|
|
393
|
-
isComplete: false,
|
|
394
|
-
lineCount: totalLines,
|
|
395
|
-
};
|
|
398
|
+
return incompleteResult(totalLines);
|
|
396
399
|
}
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
const LOADING_INDICATORS = [
|
|
400
|
-
'Waiting for auth',
|
|
401
|
-
'⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏',
|
|
402
|
-
];
|
|
403
|
-
if (LOADING_INDICATORS.some(indicator => response.includes(indicator))) {
|
|
404
|
-
return {
|
|
405
|
-
response: '',
|
|
406
|
-
isComplete: false,
|
|
407
|
-
lineCount: totalLines,
|
|
408
|
-
};
|
|
400
|
+
if (GEMINI_LOADING_INDICATORS.some(indicator => response.includes(indicator))) {
|
|
401
|
+
return incompleteResult(totalLines);
|
|
409
402
|
}
|
|
410
403
|
if (!response.includes('✦') && response.length < 10) {
|
|
411
|
-
return
|
|
412
|
-
response: '',
|
|
413
|
-
isComplete: false,
|
|
414
|
-
lineCount: totalLines,
|
|
415
|
-
};
|
|
404
|
+
return incompleteResult(totalLines);
|
|
416
405
|
}
|
|
417
406
|
}
|
|
418
407
|
return {
|
|
@@ -460,11 +449,7 @@ function extractResponse(output, lastCapturedLine, cliToolId) {
|
|
|
460
449
|
};
|
|
461
450
|
}
|
|
462
451
|
// Response not yet complete (or is in thinking state)
|
|
463
|
-
return
|
|
464
|
-
response: '',
|
|
465
|
-
isComplete: false,
|
|
466
|
-
lineCount: totalLines,
|
|
467
|
-
};
|
|
452
|
+
return incompleteResult(totalLines);
|
|
468
453
|
}
|
|
469
454
|
/**
|
|
470
455
|
* Check for CLI tool response once
|
|
@@ -533,7 +518,8 @@ async function checkForResponse(worktreeId, cliToolId) {
|
|
|
533
518
|
const message = (0, db_1.createMessage)(db, {
|
|
534
519
|
worktreeId,
|
|
535
520
|
role: 'assistant',
|
|
536
|
-
|
|
521
|
+
// Issue #235: rawContent優先でDB保存 (rawContent contains complete prompt output)
|
|
522
|
+
content: promptDetection.rawContent || promptDetection.cleanContent,
|
|
537
523
|
messageType: 'prompt',
|
|
538
524
|
promptData: promptDetection.promptData,
|
|
539
525
|
timestamp: new Date(),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "commandmate",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "Git worktree management with Claude CLI and tmux sessions",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
"lucide-react": "^0.554.0",
|
|
49
49
|
"mermaid": "^11.12.2",
|
|
50
50
|
"next": "^14.2.35",
|
|
51
|
+
"next-intl": "^4.8.2",
|
|
51
52
|
"postcss": "^8.5.6",
|
|
52
53
|
"react": "^18.3.0",
|
|
53
54
|
"react-dom": "^18.3.0",
|