@k1e1n04/mav 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +212 -0
- package/bin/mav.ts +31 -0
- package/dist/bin/mav.d.ts +2 -0
- package/dist/bin/mav.js +24 -0
- package/dist/bin/mav.js.map +1 -0
- package/dist/src/agent-launch.d.ts +15 -0
- package/dist/src/agent-launch.js +36 -0
- package/dist/src/agent-launch.js.map +1 -0
- package/dist/src/agent.d.ts +42 -0
- package/dist/src/agent.js +209 -0
- package/dist/src/agent.js.map +1 -0
- package/dist/src/config.d.ts +11 -0
- package/dist/src/config.js +32 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/current-session.d.ts +15 -0
- package/dist/src/current-session.js +17 -0
- package/dist/src/current-session.js.map +1 -0
- package/dist/src/index.d.ts +6 -0
- package/dist/src/index.js +90 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/process-cwd.d.ts +1 -0
- package/dist/src/process-cwd.js +24 -0
- package/dist/src/process-cwd.js.map +1 -0
- package/dist/src/session-manager.d.ts +16 -0
- package/dist/src/session-manager.js +102 -0
- package/dist/src/session-manager.js.map +1 -0
- package/dist/src/state.d.ts +19 -0
- package/dist/src/state.js +30 -0
- package/dist/src/state.js.map +1 -0
- package/dist/src/ui/app.d.ts +15 -0
- package/dist/src/ui/app.js +109 -0
- package/dist/src/ui/app.js.map +1 -0
- package/dist/src/ui/detail.d.ts +28 -0
- package/dist/src/ui/detail.js +137 -0
- package/dist/src/ui/detail.js.map +1 -0
- package/dist/src/ui/overview.d.ts +24 -0
- package/dist/src/ui/overview.js +267 -0
- package/dist/src/ui/overview.js.map +1 -0
- package/lua/mav/follow.lua +37 -0
- package/lua/mav/init.lua +92 -0
- package/lua/mav/state.lua +22 -0
- package/package.json +54 -0
- package/plugin/mav.lua +24 -0
- package/src/agent-launch.ts +43 -0
- package/src/agent.ts +243 -0
- package/src/config.ts +51 -0
- package/src/current-session.ts +34 -0
- package/src/index.ts +121 -0
- package/src/process-cwd.ts +31 -0
- package/src/session-manager.ts +122 -0
- package/src/state.ts +49 -0
- package/src/types/neo-blessed.d.ts +5 -0
- package/src/ui/app.ts +121 -0
- package/src/ui/detail.ts +164 -0
- package/src/ui/overview.ts +306 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 mav contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
# mav — multi-agent view
|
|
2
|
+
|
|
3
|
+
`mav` is a wrapper CLI that lets you manage multiple AI coding assistant CLIs such as `claude-code`, `codex`, `gemini-cli`, and `copilot` from a single terminal window.
|
|
4
|
+
|
|
5
|
+
```text
|
|
6
|
+
┌── AGENTS ────────────────────────────────────────────────────────────────┐
|
|
7
|
+
│ Working │
|
|
8
|
+
│ ⣾ claude-code#1 working │
|
|
9
|
+
│ ⣾ codex#1 working │
|
|
10
|
+
│ Waiting │
|
|
11
|
+
│ ○ gemini-cli#1 waiting │
|
|
12
|
+
│ Complete │
|
|
13
|
+
│ ✓ copilot#1 complete │
|
|
14
|
+
└──────────────────────────────────────────────────────────────────────────┘
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Features
|
|
18
|
+
|
|
19
|
+
- See multiple AI agent sessions at a glance in one screen
|
|
20
|
+
- Overview groups sessions under `Working / Waiting / Complete / Failed`
|
|
21
|
+
- Open the selected agent in fullscreen with `→` / `Enter`
|
|
22
|
+
- Define startup agents freely with YAML
|
|
23
|
+
- No API key required, as it uses your existing CLI tools directly
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
### Homebrew (Recommended)
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
brew tap k1e1n04/mav https://github.com/k1e1n04/mav.git
|
|
31
|
+
brew install mav
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### npm / pnpm
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npm install -g @k1e1n04/mav
|
|
38
|
+
# or
|
|
39
|
+
pnpm add -g @k1e1n04/mav
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Build from Source
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
git clone https://github.com/k1e1n04/mav.git
|
|
46
|
+
cd mav
|
|
47
|
+
pnpm install
|
|
48
|
+
pnpm build
|
|
49
|
+
pnpm link --global
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Usage
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
mav # Start all agents from the config file
|
|
56
|
+
mav --agent claude-code # Start only the specified agent type
|
|
57
|
+
mav --config ./mav.yaml # Use a custom config file path
|
|
58
|
+
mav --version
|
|
59
|
+
mav --help
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
If no config file exists at `~/.config/mav/config.yaml`, `mav` starts only `claude`.
|
|
63
|
+
|
|
64
|
+
## Configuration
|
|
65
|
+
|
|
66
|
+
Create `~/.config/mav/config.yaml`.
|
|
67
|
+
|
|
68
|
+
```yaml
|
|
69
|
+
agents:
|
|
70
|
+
- type: claude-code
|
|
71
|
+
cmd: claude-launcher # Override the default "claude" command
|
|
72
|
+
args: []
|
|
73
|
+
- type: codex
|
|
74
|
+
# If cmd is omitted, the default "codex" command is used
|
|
75
|
+
- type: gemini-cli
|
|
76
|
+
cmd: gemini
|
|
77
|
+
- type: copilot
|
|
78
|
+
cmd: gh
|
|
79
|
+
args: []
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Default Commands by Agent Type
|
|
83
|
+
|
|
84
|
+
| type | default cmd |
|
|
85
|
+
|------|----------------|
|
|
86
|
+
| `claude-code` | `claude` |
|
|
87
|
+
| `codex` | `codex` |
|
|
88
|
+
| `gemini-cli` | `gemini` |
|
|
89
|
+
| `copilot` | `copilot` |
|
|
90
|
+
|
|
91
|
+
## Keybindings
|
|
92
|
+
|
|
93
|
+
| Key | Scope | Action |
|
|
94
|
+
|------|----------|------|
|
|
95
|
+
| `↑` `↓` | Overview | Move through the session list |
|
|
96
|
+
| `→` / `Enter` | Overview | Open the selected session in fullscreen |
|
|
97
|
+
| `Ctrl+]` | Detail | Return to Overview |
|
|
98
|
+
| `n` | Overview | Add a new session |
|
|
99
|
+
| `d` | Overview | Terminate and remove the selected session |
|
|
100
|
+
| `q` / `Ctrl+C` | Overview | Quit `mav` and terminate all sessions |
|
|
101
|
+
|
|
102
|
+
> **Note:** Overview is a progress dashboard. For input and full terminal interaction, use Detail mode. In Detail mode, keys including `←` are passed directly to the agent, so use `Ctrl+]` to return to Overview.
|
|
103
|
+
|
|
104
|
+
## Neovim Integration
|
|
105
|
+
|
|
106
|
+
`mav` writes the currently selected session to `~/.local/state/mav/current-session.json`.
|
|
107
|
+
This file is intended for editor integrations such as Neovim plugins that want to follow the active session's working directory.
|
|
108
|
+
|
|
109
|
+
### How the Neovim Plugin Is Provided
|
|
110
|
+
|
|
111
|
+
This repository now includes a Neovim plugin.
|
|
112
|
+
|
|
113
|
+
- `mav` publishes the selected session state as JSON
|
|
114
|
+
- the bundled Neovim plugin reads that file
|
|
115
|
+
- the plugin can follow the selected session by running `:lcd` into its live `cwd`
|
|
116
|
+
|
|
117
|
+
In other words, the stable interface is the state file:
|
|
118
|
+
|
|
119
|
+
```text
|
|
120
|
+
~/.local/state/mav/current-session.json
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
The JSON contains:
|
|
124
|
+
|
|
125
|
+
```json
|
|
126
|
+
{
|
|
127
|
+
"sessionId": "codex#1",
|
|
128
|
+
"agentType": "codex",
|
|
129
|
+
"displayName": "fix auth redirect",
|
|
130
|
+
"cwd": "/Users/you/Develop/project-a",
|
|
131
|
+
"updatedAt": "2026-05-23T12:34:56.000Z"
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
The plugin runtime is included in this repo under:
|
|
136
|
+
|
|
137
|
+
```text
|
|
138
|
+
lua/mav/
|
|
139
|
+
plugin/mav.lua
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Installation with LazyVim / lazy.nvim
|
|
143
|
+
|
|
144
|
+
Add this to `lua/plugins/mav.lua` in your Neovim config:
|
|
145
|
+
|
|
146
|
+
```lua
|
|
147
|
+
return {
|
|
148
|
+
{
|
|
149
|
+
"k1e1n04/mav",
|
|
150
|
+
opts = {
|
|
151
|
+
auto_follow = true,
|
|
152
|
+
poll_interval_ms = 500,
|
|
153
|
+
notify_on_switch = false,
|
|
154
|
+
ignore_filetypes = {},
|
|
155
|
+
},
|
|
156
|
+
config = function(_, opts)
|
|
157
|
+
require("mav").setup(opts)
|
|
158
|
+
end,
|
|
159
|
+
},
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### How to Use It
|
|
164
|
+
|
|
165
|
+
1. Start `mav`.
|
|
166
|
+
2. Install the plugin from this repo in Neovim using the `lazy.nvim` example above.
|
|
167
|
+
3. Restart Neovim or reload your plugin config.
|
|
168
|
+
4. Change the selected session in `mav`.
|
|
169
|
+
5. Neovim will follow the selected session's current `cwd`, including worktree moves while the agent is running.
|
|
170
|
+
|
|
171
|
+
The plugin exposes:
|
|
172
|
+
|
|
173
|
+
- `:MavFollowNow` to force an immediate refresh
|
|
174
|
+
- `:MavStatus` to print the current selected session name, agent type, and `cwd`
|
|
175
|
+
|
|
176
|
+
### Plugin Options
|
|
177
|
+
|
|
178
|
+
```lua
|
|
179
|
+
{
|
|
180
|
+
state_file = vim.fn.expand("~/.local/state/mav/current-session.json"),
|
|
181
|
+
auto_follow = true,
|
|
182
|
+
poll_interval_ms = 500,
|
|
183
|
+
notify_on_switch = false,
|
|
184
|
+
ignore_filetypes = {},
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
- `state_file`: path to the `mav` state file to watch
|
|
189
|
+
- `auto_follow`: whether polling should automatically update the current window cwd
|
|
190
|
+
- `poll_interval_ms`: polling interval in milliseconds
|
|
191
|
+
- `notify_on_switch`: show a Neovim notification when the followed session changes
|
|
192
|
+
- `ignore_filetypes`: filetypes where follow should be skipped
|
|
193
|
+
|
|
194
|
+
### Current Limitation
|
|
195
|
+
|
|
196
|
+
Live `cwd` tracking is implemented for macOS and Linux. Updates may lag slightly behind rapid directory changes because `mav` polls the agent process `cwd` on an interval and also consumes terminal current-directory sequences when available.
|
|
197
|
+
|
|
198
|
+
## Requirements
|
|
199
|
+
|
|
200
|
+
- Node.js 20+
|
|
201
|
+
- Supported platforms: macOS and Linux
|
|
202
|
+
|
|
203
|
+
## Contributing
|
|
204
|
+
|
|
205
|
+
1. Fork the repository and run `git checkout -b feat/your-feature`
|
|
206
|
+
2. Make your changes and add tests with `pnpm test`
|
|
207
|
+
3. Commit with `git commit -m "feat: your feature"`
|
|
208
|
+
4. Open a pull request
|
|
209
|
+
|
|
210
|
+
## License
|
|
211
|
+
|
|
212
|
+
MIT. See [LICENSE](LICENSE) for details.
|
package/bin/mav.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander'
|
|
3
|
+
import { readFileSync } from 'node:fs'
|
|
4
|
+
import { join, dirname } from 'node:path'
|
|
5
|
+
import { fileURLToPath } from 'node:url'
|
|
6
|
+
import { start } from '../src/index.js'
|
|
7
|
+
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
9
|
+
const __dirname = dirname(__filename)
|
|
10
|
+
const pkg = JSON.parse(
|
|
11
|
+
readFileSync(join(__dirname, '..', 'package.json'), 'utf-8')
|
|
12
|
+
) as { version: string }
|
|
13
|
+
|
|
14
|
+
const program = new Command()
|
|
15
|
+
|
|
16
|
+
program
|
|
17
|
+
.name('mav')
|
|
18
|
+
.description(
|
|
19
|
+
'Multi-agent view — manage multiple AI CLI sessions in one terminal'
|
|
20
|
+
)
|
|
21
|
+
.version(pkg.version)
|
|
22
|
+
.option('--agent <type>', 'Start only the specified agent type')
|
|
23
|
+
.option('--config <path>', 'Path to config file')
|
|
24
|
+
.action((options: { agent?: string; config?: string }) => {
|
|
25
|
+
start({
|
|
26
|
+
agentType: options.agent,
|
|
27
|
+
configPath: options.config,
|
|
28
|
+
})
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
program.parse()
|
package/dist/bin/mav.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { readFileSync } from 'node:fs';
|
|
4
|
+
import { join, dirname } from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { start } from '../src/index.js';
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = dirname(__filename);
|
|
9
|
+
const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8'));
|
|
10
|
+
const program = new Command();
|
|
11
|
+
program
|
|
12
|
+
.name('mav')
|
|
13
|
+
.description('Multi-agent view — manage multiple AI CLI sessions in one terminal')
|
|
14
|
+
.version(pkg.version)
|
|
15
|
+
.option('--agent <type>', 'Start only the specified agent type')
|
|
16
|
+
.option('--config <path>', 'Path to config file')
|
|
17
|
+
.action((options) => {
|
|
18
|
+
start({
|
|
19
|
+
agentType: options.agent,
|
|
20
|
+
configPath: options.config,
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
program.parse();
|
|
24
|
+
//# sourceMappingURL=mav.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mav.js","sourceRoot":"","sources":["../../bin/mav.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AACtC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AACxC,OAAO,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAA;AAEvC,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AACjD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAA;AACrC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CACpB,YAAY,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,cAAc,CAAC,EAAE,OAAO,CAAC,CACtC,CAAA;AAExB,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAA;AAE7B,OAAO;KACJ,IAAI,CAAC,KAAK,CAAC;KACX,WAAW,CACV,oEAAoE,CACrE;KACA,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC;KACpB,MAAM,CAAC,gBAAgB,EAAE,qCAAqC,CAAC;KAC/D,MAAM,CAAC,iBAAiB,EAAE,qBAAqB,CAAC;KAChD,MAAM,CAAC,CAAC,OAA4C,EAAE,EAAE;IACvD,KAAK,CAAC;QACJ,SAAS,EAAE,OAAO,CAAC,KAAK;QACxB,UAAU,EAAE,OAAO,CAAC,MAAM;KAC3B,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA;AAEJ,OAAO,CAAC,KAAK,EAAE,CAAA"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export declare const DEFAULT_CMDS: Record<string, {
|
|
2
|
+
cmd: string;
|
|
3
|
+
args: string[];
|
|
4
|
+
resumeArgs?: string[];
|
|
5
|
+
}>;
|
|
6
|
+
export declare function getAgentDefaults(type: string): {
|
|
7
|
+
cmd: string;
|
|
8
|
+
args: string[];
|
|
9
|
+
resumeArgs?: string[];
|
|
10
|
+
};
|
|
11
|
+
/** ツールごとのセッション管理方法を返す */
|
|
12
|
+
export declare function resolveSessionArgs(type: string, baseArgs: string[], savedSessionId: string | undefined, hasSavedState: boolean): {
|
|
13
|
+
args: string[];
|
|
14
|
+
newSessionId?: string;
|
|
15
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
export const DEFAULT_CMDS = {
|
|
3
|
+
'claude-code': { cmd: 'claude', args: [] },
|
|
4
|
+
'codex': { cmd: 'codex', args: [] },
|
|
5
|
+
'gemini-cli': { cmd: 'gemini', args: [] },
|
|
6
|
+
'copilot': { cmd: 'copilot', args: [] },
|
|
7
|
+
};
|
|
8
|
+
export function getAgentDefaults(type) {
|
|
9
|
+
return DEFAULT_CMDS[type] ?? { cmd: type, args: [] };
|
|
10
|
+
}
|
|
11
|
+
/** ツールごとのセッション管理方法を返す */
|
|
12
|
+
export function resolveSessionArgs(type, baseArgs, savedSessionId, hasSavedState) {
|
|
13
|
+
switch (type) {
|
|
14
|
+
case 'claude-code':
|
|
15
|
+
case 'gemini-cli': {
|
|
16
|
+
if (savedSessionId) {
|
|
17
|
+
return { args: [...baseArgs, '--resume', savedSessionId] };
|
|
18
|
+
}
|
|
19
|
+
const id = randomUUID();
|
|
20
|
+
return { args: [...baseArgs, '--session-id', id], newSessionId: id };
|
|
21
|
+
}
|
|
22
|
+
case 'copilot': {
|
|
23
|
+
const id = savedSessionId ?? randomUUID();
|
|
24
|
+
return { args: [...baseArgs, '--session-id', id], newSessionId: savedSessionId ? undefined : id };
|
|
25
|
+
}
|
|
26
|
+
case 'codex': {
|
|
27
|
+
if (hasSavedState) {
|
|
28
|
+
return { args: ['resume', '--last'] };
|
|
29
|
+
}
|
|
30
|
+
return { args: baseArgs };
|
|
31
|
+
}
|
|
32
|
+
default:
|
|
33
|
+
return { args: baseArgs };
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=agent-launch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-launch.js","sourceRoot":"","sources":["../../src/agent-launch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAExC,MAAM,CAAC,MAAM,YAAY,GAA2E;IAClG,aAAa,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,EAAE;IAC1C,OAAO,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE;IACnC,YAAY,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,EAAE;IACzC,SAAS,EAAE,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,EAAE;CACxC,CAAA;AAED,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,OAAO,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,CAAA;AACtD,CAAC;AAED,yBAAyB;AACzB,MAAM,UAAU,kBAAkB,CAChC,IAAY,EACZ,QAAkB,EAClB,cAAkC,EAClC,aAAsB;IAEtB,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,aAAa,CAAC;QACnB,KAAK,YAAY,CAAC,CAAC,CAAC;YAClB,IAAI,cAAc,EAAE,CAAC;gBACnB,OAAO,EAAE,IAAI,EAAE,CAAC,GAAG,QAAQ,EAAE,UAAU,EAAE,cAAc,CAAC,EAAE,CAAA;YAC5D,CAAC;YACD,MAAM,EAAE,GAAG,UAAU,EAAE,CAAA;YACvB,OAAO,EAAE,IAAI,EAAE,CAAC,GAAG,QAAQ,EAAE,cAAc,EAAE,EAAE,CAAC,EAAE,YAAY,EAAE,EAAE,EAAE,CAAA;QACtE,CAAC;QACD,KAAK,SAAS,CAAC,CAAC,CAAC;YACf,MAAM,EAAE,GAAG,cAAc,IAAI,UAAU,EAAE,CAAA;YACzC,OAAO,EAAE,IAAI,EAAE,CAAC,GAAG,QAAQ,EAAE,cAAc,EAAE,EAAE,CAAC,EAAE,YAAY,EAAE,cAAc,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAA;QACnG,CAAC;QACD,KAAK,OAAO,CAAC,CAAC,CAAC;YACb,IAAI,aAAa,EAAE,CAAC;gBAClB,OAAO,EAAE,IAAI,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE,CAAA;YACvC,CAAC;YACD,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAA;QAC3B,CAAC;QACD;YACE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAA;IAC7B,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
|
2
|
+
import type { AgentConfig } from './config.js';
|
|
3
|
+
export type SessionStatus = 'running' | 'idle' | 'done' | 'error';
|
|
4
|
+
export declare class AgentSession extends EventEmitter {
|
|
5
|
+
private static readonly IDLE_TIMEOUT_MS;
|
|
6
|
+
private static readonly DISPLAY_NAME_MAX_LENGTH;
|
|
7
|
+
private static readonly DISPLAY_NAME_MIN_LENGTH;
|
|
8
|
+
private static readonly CWD_POLL_INTERVAL_MS;
|
|
9
|
+
readonly id: string;
|
|
10
|
+
readonly type: string;
|
|
11
|
+
readonly cmd: string;
|
|
12
|
+
cwd: string;
|
|
13
|
+
displayName: string;
|
|
14
|
+
baseArgs: string[];
|
|
15
|
+
status: SessionStatus;
|
|
16
|
+
logBuffer: string[];
|
|
17
|
+
lastPrompt: string;
|
|
18
|
+
sessionId?: string;
|
|
19
|
+
private ptyProcess;
|
|
20
|
+
private exited;
|
|
21
|
+
private idleTimer;
|
|
22
|
+
private cwdPollTimer;
|
|
23
|
+
private displayNameLocked;
|
|
24
|
+
private initialInputBuffer;
|
|
25
|
+
constructor(config: AgentConfig, cols: number, rows: number);
|
|
26
|
+
/** 保存されたdisplayNameを復元し、以降の入力で上書きされないようにロックする */
|
|
27
|
+
restoreDisplayName(name: string): void;
|
|
28
|
+
write(data: string): void;
|
|
29
|
+
kill(): void;
|
|
30
|
+
resize(cols: number, rows: number): void;
|
|
31
|
+
private appendLog;
|
|
32
|
+
private setStatus;
|
|
33
|
+
private scheduleIdleTimer;
|
|
34
|
+
private clearIdleTimer;
|
|
35
|
+
private startCwdPolling;
|
|
36
|
+
private clearCwdPollTimer;
|
|
37
|
+
private updateDisplayNameFromInput;
|
|
38
|
+
private updateCwdFromOutput;
|
|
39
|
+
private updateCwd;
|
|
40
|
+
private static extractOsc7Cwd;
|
|
41
|
+
private static normalizeDisplayName;
|
|
42
|
+
}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
|
2
|
+
import * as pty from 'node-pty';
|
|
3
|
+
import { getProcessCwd } from './process-cwd.js';
|
|
4
|
+
const counters = {};
|
|
5
|
+
export class AgentSession extends EventEmitter {
|
|
6
|
+
static IDLE_TIMEOUT_MS = 1500;
|
|
7
|
+
static DISPLAY_NAME_MAX_LENGTH = 25;
|
|
8
|
+
static DISPLAY_NAME_MIN_LENGTH = 3;
|
|
9
|
+
static CWD_POLL_INTERVAL_MS = 1000;
|
|
10
|
+
id;
|
|
11
|
+
type;
|
|
12
|
+
cmd;
|
|
13
|
+
cwd;
|
|
14
|
+
displayName;
|
|
15
|
+
baseArgs = [];
|
|
16
|
+
status = 'running';
|
|
17
|
+
logBuffer = [];
|
|
18
|
+
lastPrompt = '';
|
|
19
|
+
sessionId;
|
|
20
|
+
ptyProcess;
|
|
21
|
+
exited = false;
|
|
22
|
+
idleTimer = null;
|
|
23
|
+
cwdPollTimer = null;
|
|
24
|
+
displayNameLocked = false;
|
|
25
|
+
initialInputBuffer = '';
|
|
26
|
+
constructor(config, cols, rows) {
|
|
27
|
+
super();
|
|
28
|
+
counters[config.type] = (counters[config.type] ?? 0) + 1;
|
|
29
|
+
this.id = `${config.type}#${counters[config.type]}`;
|
|
30
|
+
this.type = config.type;
|
|
31
|
+
this.cmd = config.cmd;
|
|
32
|
+
this.cwd = config.cwd ?? process.cwd();
|
|
33
|
+
this.displayName = `${config.type} ${counters[config.type]}`;
|
|
34
|
+
let proc;
|
|
35
|
+
try {
|
|
36
|
+
proc = pty.spawn(config.cmd, config.args, {
|
|
37
|
+
name: 'xterm-256color',
|
|
38
|
+
cols,
|
|
39
|
+
rows,
|
|
40
|
+
cwd: this.cwd,
|
|
41
|
+
env: process.env,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
46
|
+
this.exited = true;
|
|
47
|
+
this.status = 'error';
|
|
48
|
+
this.logBuffer.push(`Error: failed to spawn '${config.cmd}': ${msg}\r\n`);
|
|
49
|
+
process.nextTick(() => this.emit('exit', 1));
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
this.ptyProcess = proc;
|
|
53
|
+
this.ptyProcess.onData((data) => {
|
|
54
|
+
this.updateCwdFromOutput(data);
|
|
55
|
+
this.appendLog(data);
|
|
56
|
+
this.setStatus('running');
|
|
57
|
+
this.scheduleIdleTimer();
|
|
58
|
+
this.emit('data', data);
|
|
59
|
+
});
|
|
60
|
+
this.ptyProcess.onExit(({ exitCode }) => {
|
|
61
|
+
this.clearIdleTimer();
|
|
62
|
+
this.clearCwdPollTimer();
|
|
63
|
+
this.setStatus(exitCode === 0 ? 'done' : 'error');
|
|
64
|
+
this.exited = true;
|
|
65
|
+
this.ptyProcess = undefined;
|
|
66
|
+
this.emit('exit', exitCode);
|
|
67
|
+
});
|
|
68
|
+
this.startCwdPolling();
|
|
69
|
+
}
|
|
70
|
+
/** 保存されたdisplayNameを復元し、以降の入力で上書きされないようにロックする */
|
|
71
|
+
restoreDisplayName(name) {
|
|
72
|
+
this.displayName = name;
|
|
73
|
+
this.displayNameLocked = true;
|
|
74
|
+
this.emit('name', name);
|
|
75
|
+
}
|
|
76
|
+
write(data) {
|
|
77
|
+
if (this.exited)
|
|
78
|
+
return;
|
|
79
|
+
this.updateDisplayNameFromInput(data);
|
|
80
|
+
this.ptyProcess?.write(data);
|
|
81
|
+
}
|
|
82
|
+
kill() {
|
|
83
|
+
if (this.exited)
|
|
84
|
+
return;
|
|
85
|
+
this.clearIdleTimer();
|
|
86
|
+
this.clearCwdPollTimer();
|
|
87
|
+
this.ptyProcess?.kill();
|
|
88
|
+
}
|
|
89
|
+
resize(cols, rows) {
|
|
90
|
+
if (this.exited)
|
|
91
|
+
return;
|
|
92
|
+
this.ptyProcess?.resize(cols, rows);
|
|
93
|
+
}
|
|
94
|
+
appendLog(chunk) {
|
|
95
|
+
this.logBuffer.push(chunk);
|
|
96
|
+
if (this.logBuffer.length > 500) {
|
|
97
|
+
this.logBuffer.splice(0, this.logBuffer.length - 500);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
setStatus(nextStatus) {
|
|
101
|
+
if (this.status === nextStatus) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
this.status = nextStatus;
|
|
105
|
+
this.emit('status', nextStatus);
|
|
106
|
+
}
|
|
107
|
+
scheduleIdleTimer() {
|
|
108
|
+
if (this.exited)
|
|
109
|
+
return;
|
|
110
|
+
this.clearIdleTimer();
|
|
111
|
+
this.idleTimer = setTimeout(() => {
|
|
112
|
+
this.idleTimer = null;
|
|
113
|
+
if (this.exited)
|
|
114
|
+
return;
|
|
115
|
+
this.setStatus('idle');
|
|
116
|
+
}, AgentSession.IDLE_TIMEOUT_MS);
|
|
117
|
+
}
|
|
118
|
+
clearIdleTimer() {
|
|
119
|
+
if (!this.idleTimer) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
clearTimeout(this.idleTimer);
|
|
123
|
+
this.idleTimer = null;
|
|
124
|
+
}
|
|
125
|
+
startCwdPolling() {
|
|
126
|
+
if (this.cwdPollTimer || !this.ptyProcess) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
this.cwdPollTimer = setInterval(() => {
|
|
130
|
+
if (this.exited || !this.ptyProcess) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
this.updateCwd(getProcessCwd(this.ptyProcess.pid));
|
|
134
|
+
}, AgentSession.CWD_POLL_INTERVAL_MS);
|
|
135
|
+
}
|
|
136
|
+
clearCwdPollTimer() {
|
|
137
|
+
if (!this.cwdPollTimer) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
clearInterval(this.cwdPollTimer);
|
|
141
|
+
this.cwdPollTimer = null;
|
|
142
|
+
}
|
|
143
|
+
updateDisplayNameFromInput(data) {
|
|
144
|
+
if (this.displayNameLocked) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
this.initialInputBuffer += data;
|
|
148
|
+
// DCS/APC/PM/SOS シーケンス(Warp など一部ターミナルがstdinに送る)を除去してから
|
|
149
|
+
// ユーザー入力の改行を探す。これらのシーケンスは CR/LF を含むことがあり、
|
|
150
|
+
// そのまま処理すると表示名がターミナル固有の文字列で汚染される。
|
|
151
|
+
const cleaned = this.initialInputBuffer.replace(/\x1b[P_^X][^\x1b]*(?:\x1b\\)?/g, '');
|
|
152
|
+
const newlineIndex = cleaned.search(/\r|\n/);
|
|
153
|
+
if (newlineIndex === -1) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
const firstLine = cleaned.slice(0, newlineIndex);
|
|
157
|
+
this.initialInputBuffer = '';
|
|
158
|
+
const normalized = AgentSession.normalizeDisplayName(firstLine);
|
|
159
|
+
if (normalized.length < AgentSession.DISPLAY_NAME_MIN_LENGTH) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
this.displayNameLocked = true;
|
|
163
|
+
this.displayName = normalized;
|
|
164
|
+
this.emit('name', normalized);
|
|
165
|
+
}
|
|
166
|
+
updateCwdFromOutput(data) {
|
|
167
|
+
this.updateCwd(AgentSession.extractOsc7Cwd(data));
|
|
168
|
+
}
|
|
169
|
+
updateCwd(nextCwd) {
|
|
170
|
+
if (!nextCwd || nextCwd === this.cwd) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
this.cwd = nextCwd;
|
|
174
|
+
this.emit('cwd', nextCwd);
|
|
175
|
+
}
|
|
176
|
+
static extractOsc7Cwd(data) {
|
|
177
|
+
const matches = [...data.matchAll(/\x1b\]7;([^\x07\x1b]+)(?:\x07|\x1b\\)/g)];
|
|
178
|
+
const rawUrl = matches.at(-1)?.[1];
|
|
179
|
+
if (!rawUrl) {
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
try {
|
|
183
|
+
const url = new URL(rawUrl);
|
|
184
|
+
if (url.protocol !== 'file:') {
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
return decodeURIComponent(url.pathname || '');
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
static normalizeDisplayName(input) {
|
|
194
|
+
const withoutAnsi = input
|
|
195
|
+
.replace(/\x1b[P_^X][^\x1b]*(?:\x1b\\)?/g, '') // DCS, APC, PM, SOS(複数文字シーケンス)
|
|
196
|
+
.replace(/\x1b\][^\x07]*(?:\x07|\x1b\\)/g, '')
|
|
197
|
+
.replace(/\x1b\[[0-?]*[ -/]*[@-~]/g, '')
|
|
198
|
+
.replace(/\x1b[@-Z\\-_]/g, '');
|
|
199
|
+
const collapsed = withoutAnsi
|
|
200
|
+
.replace(/[\x00-\x1f\x7f]+/g, ' ')
|
|
201
|
+
.replace(/\s+/g, ' ')
|
|
202
|
+
.trim();
|
|
203
|
+
if (collapsed.length <= AgentSession.DISPLAY_NAME_MAX_LENGTH) {
|
|
204
|
+
return collapsed;
|
|
205
|
+
}
|
|
206
|
+
return `${collapsed.slice(0, AgentSession.DISPLAY_NAME_MAX_LENGTH - 3).trimEnd()}...`;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
//# sourceMappingURL=agent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent.js","sourceRoot":"","sources":["../../src/agent.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
2
|
+
import { load as yamlLoad } from 'js-yaml';
|
|
3
|
+
import { getAgentDefaults } from './agent-launch.js';
|
|
4
|
+
const DEFAULT_CONFIG = {
|
|
5
|
+
agents: [],
|
|
6
|
+
};
|
|
7
|
+
export function loadConfig(configPath) {
|
|
8
|
+
if (!existsSync(configPath)) {
|
|
9
|
+
return DEFAULT_CONFIG;
|
|
10
|
+
}
|
|
11
|
+
const raw = readFileSync(configPath, 'utf-8');
|
|
12
|
+
const parsed = yamlLoad(raw);
|
|
13
|
+
if (!parsed?.agents || !Array.isArray(parsed.agents)) {
|
|
14
|
+
return DEFAULT_CONFIG;
|
|
15
|
+
}
|
|
16
|
+
const agents = parsed.agents
|
|
17
|
+
.filter((a) => typeof a?.type === 'string' && a.type.length > 0)
|
|
18
|
+
.map((a) => {
|
|
19
|
+
const defaults = getAgentDefaults(a.type);
|
|
20
|
+
const cmd = typeof a.cmd === 'string' && a.cmd.trim().length > 0
|
|
21
|
+
? a.cmd
|
|
22
|
+
: defaults.cmd;
|
|
23
|
+
return {
|
|
24
|
+
type: a.type,
|
|
25
|
+
cmd,
|
|
26
|
+
args: Array.isArray(a.args) ? a.args : defaults.args,
|
|
27
|
+
resumeArgs: Array.isArray(a.resumeArgs) ? a.resumeArgs : defaults.resumeArgs,
|
|
28
|
+
};
|
|
29
|
+
});
|
|
30
|
+
return { agents };
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAClD,OAAO,EAAE,IAAI,IAAI,QAAQ,EAAE,MAAM,SAAS,CAAA;AAC1C,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AAcpD,MAAM,cAAc,GAAc;IAChC,MAAM,EAAE,EAAE;CACX,CAAA;AAED,MAAM,UAAU,UAAU,CAAC,UAAkB;IAC3C,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC5B,OAAO,cAAc,CAAA;IACvB,CAAC;IAED,MAAM,GAAG,GAAG,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAA;IAC7C,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAwE,CAAA;IAEnG,IAAI,CAAC,MAAM,EAAE,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;QACrD,OAAO,cAAc,CAAA;IACvB,CAAC;IAED,MAAM,MAAM,GAAkB,MAAM,CAAC,MAAM;SACxC,MAAM,CAAC,CAAC,CAAC,EAA+E,EAAE,CACzF,OAAO,CAAC,EAAE,IAAI,KAAK,QAAQ,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CACjD;SACA,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACT,MAAM,QAAQ,GAAG,gBAAgB,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA;QACzC,MAAM,GAAG,GAAG,OAAO,CAAC,CAAC,GAAG,KAAK,QAAQ,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC;YAC9D,CAAC,CAAC,CAAC,CAAC,GAAG;YACP,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAA;QAChB,OAAO;YACL,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,GAAG;YACH,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI;YACpD,UAAU,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,UAAU;SAC7E,CAAA;IACH,CAAC,CAAC,CAAA;IAEJ,OAAO,EAAE,MAAM,EAAE,CAAA;AACnB,CAAC"}
|