@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
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { appendFileSync } from 'node:fs';
|
|
2
|
+
export class DetailUI {
|
|
3
|
+
static KEYBOARD_FLAGS_RESPONSE_PATTERN = /\x1b\[\?(\d+)u/g;
|
|
4
|
+
static PENDING_INPUT_FLUSH_DELAY_MS = 25;
|
|
5
|
+
static EXIT_SEQUENCES = [
|
|
6
|
+
'\x1d',
|
|
7
|
+
'\x1b[93;5u',
|
|
8
|
+
'\x1b[27;5;93~',
|
|
9
|
+
];
|
|
10
|
+
static EXIT_SEQUENCE_PATTERNS = [
|
|
11
|
+
/^\x1b\[93(?:::\d+)?;5(?::\d+)?u$/,
|
|
12
|
+
/^\x1b\[99;5u$/,
|
|
13
|
+
];
|
|
14
|
+
screen;
|
|
15
|
+
onExitDetail;
|
|
16
|
+
currentSession = null;
|
|
17
|
+
dataListener = null;
|
|
18
|
+
rawInputListener = null;
|
|
19
|
+
pendingInput = '';
|
|
20
|
+
pendingInputTimer = null;
|
|
21
|
+
keyboardEnhancementFlags = null;
|
|
22
|
+
constructor(screen, onExitDetail) {
|
|
23
|
+
this.screen = screen;
|
|
24
|
+
this.onExitDetail = onExitDetail;
|
|
25
|
+
}
|
|
26
|
+
isExitShortcut(input) {
|
|
27
|
+
return (DetailUI.EXIT_SEQUENCES.includes(input) ||
|
|
28
|
+
DetailUI.EXIT_SEQUENCE_PATTERNS.some((pattern) => pattern.test(input)));
|
|
29
|
+
}
|
|
30
|
+
isExitShortcutPrefix(input) {
|
|
31
|
+
return (DetailUI.EXIT_SEQUENCES.some((sequence) => sequence.startsWith(input)) ||
|
|
32
|
+
'\x1b[93::92;5u'.startsWith(input) ||
|
|
33
|
+
'\x1b[93::92;5:3u'.startsWith(input) ||
|
|
34
|
+
'\x1b[99;5u'.startsWith(input));
|
|
35
|
+
}
|
|
36
|
+
debugLogInput(str) {
|
|
37
|
+
const path = process.env.MAV_DEBUG_KEYS_PATH;
|
|
38
|
+
if (!path)
|
|
39
|
+
return;
|
|
40
|
+
const hex = Buffer.from(str, 'utf8').toString('hex');
|
|
41
|
+
appendFileSync(path, `raw=${JSON.stringify(str)} hex=${hex}\n`);
|
|
42
|
+
}
|
|
43
|
+
rememberKeyboardEnhancementFlags(str) {
|
|
44
|
+
for (const match of str.matchAll(DetailUI.KEYBOARD_FLAGS_RESPONSE_PATTERN)) {
|
|
45
|
+
this.keyboardEnhancementFlags = match[1] ?? null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
clearPendingInputTimer() {
|
|
49
|
+
if (!this.pendingInputTimer) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
clearTimeout(this.pendingInputTimer);
|
|
53
|
+
this.pendingInputTimer = null;
|
|
54
|
+
}
|
|
55
|
+
flushPendingInput() {
|
|
56
|
+
if (!this.pendingInput) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
this.currentSession?.write(this.pendingInput);
|
|
60
|
+
this.pendingInput = '';
|
|
61
|
+
this.clearPendingInputTimer();
|
|
62
|
+
}
|
|
63
|
+
attach(session) {
|
|
64
|
+
this.detach();
|
|
65
|
+
this.currentSession = session;
|
|
66
|
+
this.pendingInput = '';
|
|
67
|
+
const { input, output } = this.screen.program;
|
|
68
|
+
const safeLog = session.logBuffer
|
|
69
|
+
.join('')
|
|
70
|
+
.replace(/\x1b\[\?104[79][hl]|\x1b\[\?47[hl]/g, '')
|
|
71
|
+
.replace(/\x1b\[(?:>?\d*c|\?u|>q|\?\d+\$p)/g, '');
|
|
72
|
+
if (this.keyboardEnhancementFlags) {
|
|
73
|
+
output.write(`\x1b[=${this.keyboardEnhancementFlags}u`);
|
|
74
|
+
}
|
|
75
|
+
output.write('\x1b[?1l');
|
|
76
|
+
output.write('\x1b[H\x1b[2J');
|
|
77
|
+
output.write(safeLog);
|
|
78
|
+
this.dataListener = (data) => {
|
|
79
|
+
output.write(data);
|
|
80
|
+
};
|
|
81
|
+
session.on('data', this.dataListener);
|
|
82
|
+
this.rawInputListener = (chunk) => {
|
|
83
|
+
const str = Buffer.isBuffer(chunk)
|
|
84
|
+
? chunk.toString('utf8')
|
|
85
|
+
: typeof chunk === 'string'
|
|
86
|
+
? chunk
|
|
87
|
+
: '';
|
|
88
|
+
if (!str)
|
|
89
|
+
return;
|
|
90
|
+
this.debugLogInput(str);
|
|
91
|
+
this.rememberKeyboardEnhancementFlags(str);
|
|
92
|
+
if (this.isExitShortcut(str)) {
|
|
93
|
+
this.onExitDetail();
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
this.pendingInput += str;
|
|
97
|
+
if (this.isExitShortcut(this.pendingInput)) {
|
|
98
|
+
this.clearPendingInputTimer();
|
|
99
|
+
this.pendingInput = '';
|
|
100
|
+
this.onExitDetail();
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (this.isExitShortcutPrefix(this.pendingInput)) {
|
|
104
|
+
this.clearPendingInputTimer();
|
|
105
|
+
this.pendingInputTimer = setTimeout(() => {
|
|
106
|
+
this.pendingInputTimer = null;
|
|
107
|
+
this.flushPendingInput();
|
|
108
|
+
}, DetailUI.PENDING_INPUT_FLUSH_DELAY_MS);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
this.flushPendingInput();
|
|
112
|
+
};
|
|
113
|
+
input.on('data', this.rawInputListener);
|
|
114
|
+
}
|
|
115
|
+
detach() {
|
|
116
|
+
if (this.currentSession && this.dataListener) {
|
|
117
|
+
this.currentSession.off('data', this.dataListener);
|
|
118
|
+
this.dataListener = null;
|
|
119
|
+
}
|
|
120
|
+
if (this.rawInputListener) {
|
|
121
|
+
this.screen.program.input.removeListener('data', this.rawInputListener);
|
|
122
|
+
this.rawInputListener = null;
|
|
123
|
+
}
|
|
124
|
+
if (this.keyboardEnhancementFlags) {
|
|
125
|
+
this.screen.program.output.write('\x1b[=0u');
|
|
126
|
+
}
|
|
127
|
+
this.clearPendingInputTimer();
|
|
128
|
+
this.pendingInput = '';
|
|
129
|
+
this.currentSession = null;
|
|
130
|
+
}
|
|
131
|
+
show() { }
|
|
132
|
+
hide() { }
|
|
133
|
+
resize(cols, rows) {
|
|
134
|
+
this.currentSession?.resize(cols, rows);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=detail.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detail.js","sourceRoot":"","sources":["../../../src/ui/detail.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Widgets } from 'neo-blessed';
|
|
2
|
+
import type { SessionManager } from '../session-manager.js';
|
|
3
|
+
export declare class OverviewUI {
|
|
4
|
+
private static readonly STATUS_GROUPS;
|
|
5
|
+
private static readonly STATUS_COLORS;
|
|
6
|
+
private screen;
|
|
7
|
+
private manager;
|
|
8
|
+
private onSessionCreated?;
|
|
9
|
+
private listBox;
|
|
10
|
+
private promptOpen;
|
|
11
|
+
constructor(screen: Widgets.Screen, manager: SessionManager, onSessionCreated?: (session: SessionManager['selectedSession']) => void);
|
|
12
|
+
private bindKeys;
|
|
13
|
+
private showAddPrompt;
|
|
14
|
+
private showError;
|
|
15
|
+
private syncList;
|
|
16
|
+
private getOrderedSessions;
|
|
17
|
+
private moveSelection;
|
|
18
|
+
private getStatusLabel;
|
|
19
|
+
private getStatusRank;
|
|
20
|
+
show(): void;
|
|
21
|
+
isPromptOpen(): boolean;
|
|
22
|
+
resizeSelectedSession(): void;
|
|
23
|
+
hide(): void;
|
|
24
|
+
}
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import blessed from 'neo-blessed';
|
|
2
|
+
import { getAgentDefaults, resolveSessionArgs } from '../agent-launch.js';
|
|
3
|
+
export class OverviewUI {
|
|
4
|
+
static STATUS_GROUPS = [
|
|
5
|
+
{ status: 'running', label: 'Working' },
|
|
6
|
+
{ status: 'idle', label: 'Waiting' },
|
|
7
|
+
{ status: 'done', label: 'Complete' },
|
|
8
|
+
{ status: 'error', label: 'Failed' },
|
|
9
|
+
];
|
|
10
|
+
static STATUS_COLORS = {
|
|
11
|
+
running: 'cyan',
|
|
12
|
+
idle: 'yellow',
|
|
13
|
+
done: 'green',
|
|
14
|
+
error: 'red',
|
|
15
|
+
};
|
|
16
|
+
screen;
|
|
17
|
+
manager;
|
|
18
|
+
onSessionCreated;
|
|
19
|
+
listBox;
|
|
20
|
+
promptOpen = false;
|
|
21
|
+
constructor(screen, manager, onSessionCreated) {
|
|
22
|
+
this.screen = screen;
|
|
23
|
+
this.manager = manager;
|
|
24
|
+
this.onSessionCreated = onSessionCreated;
|
|
25
|
+
this.listBox = blessed.list({
|
|
26
|
+
parent: screen,
|
|
27
|
+
top: 0,
|
|
28
|
+
left: 0,
|
|
29
|
+
width: '100%',
|
|
30
|
+
height: '100%',
|
|
31
|
+
border: { type: 'line' },
|
|
32
|
+
label: ' AGENTS ',
|
|
33
|
+
tags: true,
|
|
34
|
+
style: {
|
|
35
|
+
selected: { bg: 'blue', fg: 'white' },
|
|
36
|
+
border: { fg: 'cyan' },
|
|
37
|
+
},
|
|
38
|
+
keys: true,
|
|
39
|
+
mouse: true,
|
|
40
|
+
});
|
|
41
|
+
this.bindKeys();
|
|
42
|
+
this.syncList();
|
|
43
|
+
manager.on('data', (sessionId) => {
|
|
44
|
+
if (sessionId !== this.manager.selectedSession?.id) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
this.syncList();
|
|
48
|
+
screen.render();
|
|
49
|
+
});
|
|
50
|
+
manager.on('exit', () => {
|
|
51
|
+
this.syncList();
|
|
52
|
+
screen.render();
|
|
53
|
+
});
|
|
54
|
+
manager.on('status', () => {
|
|
55
|
+
this.syncList();
|
|
56
|
+
screen.render();
|
|
57
|
+
});
|
|
58
|
+
manager.on('name', () => {
|
|
59
|
+
this.syncList();
|
|
60
|
+
screen.render();
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
bindKeys() {
|
|
64
|
+
this.listBox.key(['up', 'k'], () => {
|
|
65
|
+
if (this.manager.sessions.length === 0)
|
|
66
|
+
return;
|
|
67
|
+
this.moveSelection(-1);
|
|
68
|
+
this.syncList();
|
|
69
|
+
this.screen.render();
|
|
70
|
+
});
|
|
71
|
+
this.listBox.key(['down', 'j'], () => {
|
|
72
|
+
if (this.manager.sessions.length === 0)
|
|
73
|
+
return;
|
|
74
|
+
this.moveSelection(1);
|
|
75
|
+
this.syncList();
|
|
76
|
+
this.screen.render();
|
|
77
|
+
});
|
|
78
|
+
this.listBox.key('n', () => {
|
|
79
|
+
this.showAddPrompt();
|
|
80
|
+
});
|
|
81
|
+
this.listBox.key(['d', 'C-x'], () => {
|
|
82
|
+
const session = this.manager.selectedSession;
|
|
83
|
+
if (session) {
|
|
84
|
+
this.manager.removeSession(session.id);
|
|
85
|
+
this.syncList();
|
|
86
|
+
this.screen.render();
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
showAddPrompt() {
|
|
91
|
+
if (this.promptOpen)
|
|
92
|
+
return;
|
|
93
|
+
this.promptOpen = true;
|
|
94
|
+
const agentTypes = ['claude-code', 'codex', 'gemini-cli', 'copilot'];
|
|
95
|
+
const prompt = blessed.list({
|
|
96
|
+
parent: this.screen,
|
|
97
|
+
top: 'center',
|
|
98
|
+
left: 'center',
|
|
99
|
+
width: 40,
|
|
100
|
+
height: agentTypes.length + 4,
|
|
101
|
+
border: { type: 'line' },
|
|
102
|
+
label: ' Select agent type ',
|
|
103
|
+
items: agentTypes,
|
|
104
|
+
keys: true,
|
|
105
|
+
style: {
|
|
106
|
+
selected: { bg: 'blue', fg: 'white' },
|
|
107
|
+
border: { fg: 'green' },
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
const close = () => {
|
|
111
|
+
this.promptOpen = false;
|
|
112
|
+
prompt.destroy();
|
|
113
|
+
this.listBox.focus();
|
|
114
|
+
this.screen.render();
|
|
115
|
+
};
|
|
116
|
+
prompt.key('enter', () => {
|
|
117
|
+
const selectedIdx = prompt.selected ?? 0;
|
|
118
|
+
const selected = agentTypes[selectedIdx];
|
|
119
|
+
close();
|
|
120
|
+
const defaults = getAgentDefaults(selected);
|
|
121
|
+
const { args, newSessionId } = resolveSessionArgs(selected, defaults.args, undefined, false);
|
|
122
|
+
const session = this.manager.addSession({
|
|
123
|
+
type: selected,
|
|
124
|
+
cmd: defaults.cmd,
|
|
125
|
+
args,
|
|
126
|
+
cwd: process.cwd(),
|
|
127
|
+
});
|
|
128
|
+
session.baseArgs = defaults.args;
|
|
129
|
+
if (newSessionId != null) {
|
|
130
|
+
session.sessionId = newSessionId;
|
|
131
|
+
}
|
|
132
|
+
if (session.status === 'error') {
|
|
133
|
+
this.manager.removeSession(session.id);
|
|
134
|
+
this.showError(`'${defaults.cmd}' command not found.\nIs ${selected} installed?`);
|
|
135
|
+
this.syncList();
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
this.manager.selectSession(this.manager.sessions.length - 1);
|
|
139
|
+
this.syncList();
|
|
140
|
+
this.onSessionCreated?.(session);
|
|
141
|
+
});
|
|
142
|
+
prompt.key('escape', close);
|
|
143
|
+
prompt.focus();
|
|
144
|
+
this.screen.render();
|
|
145
|
+
}
|
|
146
|
+
showError(message) {
|
|
147
|
+
const overlay = blessed.box({
|
|
148
|
+
parent: this.screen,
|
|
149
|
+
top: 'center',
|
|
150
|
+
left: 'center',
|
|
151
|
+
width: 50,
|
|
152
|
+
height: message.split('\n').length + 4,
|
|
153
|
+
border: { type: 'line' },
|
|
154
|
+
label: ' Error ',
|
|
155
|
+
content: `\n ${message.split('\n').join('\n ')}`,
|
|
156
|
+
style: { border: { fg: 'red' }, label: { fg: 'red' } },
|
|
157
|
+
keys: true,
|
|
158
|
+
mouse: true,
|
|
159
|
+
});
|
|
160
|
+
overlay.key(['enter', 'escape', 'q'], () => {
|
|
161
|
+
overlay.destroy();
|
|
162
|
+
this.listBox.focus();
|
|
163
|
+
this.screen.render();
|
|
164
|
+
});
|
|
165
|
+
overlay.focus();
|
|
166
|
+
this.screen.render();
|
|
167
|
+
}
|
|
168
|
+
syncList() {
|
|
169
|
+
const orderedSessions = this.getOrderedSessions();
|
|
170
|
+
const items = [];
|
|
171
|
+
const selectedId = this.manager.selectedSession?.id;
|
|
172
|
+
let selectedDisplayIndex = -1;
|
|
173
|
+
for (const group of OverviewUI.STATUS_GROUPS) {
|
|
174
|
+
const sessions = orderedSessions.filter((session) => session.status === group.status);
|
|
175
|
+
if (sessions.length === 0)
|
|
176
|
+
continue;
|
|
177
|
+
items.push(` ${group.label}`);
|
|
178
|
+
for (const session of sessions) {
|
|
179
|
+
const statusIcon = session.status === 'running' ? '⣾'
|
|
180
|
+
: session.status === 'idle' ? '○'
|
|
181
|
+
: session.status === 'done' ? '✓'
|
|
182
|
+
: '✗';
|
|
183
|
+
const sessionTitle = session.displayName ?? session.id;
|
|
184
|
+
const sessionLabel = session.type ? `${sessionTitle} (${session.type})` : sessionTitle;
|
|
185
|
+
const content = `${statusIcon} ${sessionLabel} ${this.getStatusLabel(session.status)}`;
|
|
186
|
+
const color = OverviewUI.STATUS_COLORS[session.status];
|
|
187
|
+
items.push(` {${color}-fg}${content}{/${color}-fg}`);
|
|
188
|
+
if (session.id === selectedId) {
|
|
189
|
+
selectedDisplayIndex = items.length - 1;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
this.listBox.setItems(items);
|
|
194
|
+
if (selectedDisplayIndex >= 0) {
|
|
195
|
+
this.listBox.select(selectedDisplayIndex);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
getOrderedSessions() {
|
|
199
|
+
return [...this.manager.sessions].sort((a, b) => {
|
|
200
|
+
const rankDiff = this.getStatusRank(a.status) - this.getStatusRank(b.status);
|
|
201
|
+
if (rankDiff !== 0)
|
|
202
|
+
return rankDiff;
|
|
203
|
+
return a.id.localeCompare(b.id);
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
moveSelection(direction) {
|
|
207
|
+
const orderedSessions = this.getOrderedSessions();
|
|
208
|
+
const selectedId = this.manager.selectedSession?.id;
|
|
209
|
+
const currentIndex = selectedId
|
|
210
|
+
? orderedSessions.findIndex((session) => session.id === selectedId)
|
|
211
|
+
: -1;
|
|
212
|
+
const nextIndex = currentIndex === -1
|
|
213
|
+
? 0
|
|
214
|
+
: Math.max(0, Math.min(orderedSessions.length - 1, currentIndex + direction));
|
|
215
|
+
const nextSession = orderedSessions[nextIndex];
|
|
216
|
+
if (!nextSession)
|
|
217
|
+
return;
|
|
218
|
+
const managerIndex = this.manager.sessions.findIndex((session) => session.id === nextSession.id);
|
|
219
|
+
if (managerIndex >= 0) {
|
|
220
|
+
this.manager.selectSession(managerIndex);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
getStatusLabel(status) {
|
|
224
|
+
switch (status) {
|
|
225
|
+
case 'running':
|
|
226
|
+
return 'working';
|
|
227
|
+
case 'idle':
|
|
228
|
+
return 'waiting';
|
|
229
|
+
case 'done':
|
|
230
|
+
return 'complete';
|
|
231
|
+
case 'error':
|
|
232
|
+
return 'failed';
|
|
233
|
+
default:
|
|
234
|
+
return status;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
getStatusRank(status) {
|
|
238
|
+
switch (status) {
|
|
239
|
+
case 'running':
|
|
240
|
+
return 0;
|
|
241
|
+
case 'idle':
|
|
242
|
+
return 1;
|
|
243
|
+
case 'done':
|
|
244
|
+
return 2;
|
|
245
|
+
case 'error':
|
|
246
|
+
return 3;
|
|
247
|
+
default:
|
|
248
|
+
return 99;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
show() {
|
|
252
|
+
this.listBox.show();
|
|
253
|
+
this.listBox.focus();
|
|
254
|
+
this.syncList();
|
|
255
|
+
this.screen.render();
|
|
256
|
+
}
|
|
257
|
+
isPromptOpen() {
|
|
258
|
+
return this.promptOpen;
|
|
259
|
+
}
|
|
260
|
+
resizeSelectedSession() {
|
|
261
|
+
this.syncList();
|
|
262
|
+
}
|
|
263
|
+
hide() {
|
|
264
|
+
this.listBox.hide();
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
//# sourceMappingURL=overview.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overview.js","sourceRoot":"","sources":["../../../src/ui/overview.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
local M = {}
|
|
2
|
+
|
|
3
|
+
local function is_ignored_filetype(ignore_filetypes)
|
|
4
|
+
local current = vim.bo.filetype
|
|
5
|
+
for _, filetype in ipairs(ignore_filetypes or {}) do
|
|
6
|
+
if filetype == current then
|
|
7
|
+
return true
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
return false
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
function M.follow(session, opts)
|
|
15
|
+
if not session or type(session.cwd) ~= "string" or session.cwd == "" then
|
|
16
|
+
return false, "missing cwd"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
if is_ignored_filetype(opts.ignore_filetypes) then
|
|
20
|
+
return false, "ignored filetype"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
if vim.fn.isdirectory(session.cwd) ~= 1 then
|
|
24
|
+
return false, "missing directory"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
vim.cmd.lcd(vim.fn.fnameescape(session.cwd))
|
|
28
|
+
|
|
29
|
+
if opts.notify_on_switch then
|
|
30
|
+
local label = session.displayName or session.sessionId or "session"
|
|
31
|
+
vim.notify(string.format("mav: %s -> %s", label, session.cwd))
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
return true
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
return M
|
package/lua/mav/init.lua
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
local state = require("mav.state")
|
|
2
|
+
local follow = require("mav.follow")
|
|
3
|
+
|
|
4
|
+
local M = {}
|
|
5
|
+
|
|
6
|
+
local defaults = {
|
|
7
|
+
state_file = vim.fn.expand("~/.local/state/mav/current-session.json"),
|
|
8
|
+
auto_follow = true,
|
|
9
|
+
poll_interval_ms = 500,
|
|
10
|
+
notify_on_switch = false,
|
|
11
|
+
ignore_filetypes = {},
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
M._opts = vim.deepcopy(defaults)
|
|
15
|
+
M._timer = nil
|
|
16
|
+
M._last_updated = nil
|
|
17
|
+
M._last_session = nil
|
|
18
|
+
|
|
19
|
+
local function read_session()
|
|
20
|
+
return state.read(M._opts.state_file)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
local function cache_session(session)
|
|
24
|
+
M._last_session = session
|
|
25
|
+
M._last_updated = session and session.updatedAt or nil
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
function M.stop()
|
|
29
|
+
if not M._timer then
|
|
30
|
+
return
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
M._timer:stop()
|
|
34
|
+
M._timer:close()
|
|
35
|
+
M._timer = nil
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
function M.poll_once()
|
|
39
|
+
local session = read_session()
|
|
40
|
+
if not session then
|
|
41
|
+
return nil
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
if session.updatedAt == M._last_updated then
|
|
45
|
+
return M._last_session
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
cache_session(session)
|
|
49
|
+
|
|
50
|
+
if M._opts.auto_follow then
|
|
51
|
+
follow.follow(session, M._opts)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
return session
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
function M.follow_now()
|
|
58
|
+
local session = read_session()
|
|
59
|
+
if not session then
|
|
60
|
+
return nil
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
cache_session(session)
|
|
64
|
+
follow.follow(session, M._opts)
|
|
65
|
+
return session
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
function M.status()
|
|
69
|
+
return M._last_session or read_session()
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
function M.setup(opts)
|
|
73
|
+
M.stop()
|
|
74
|
+
M._opts = vim.tbl_deep_extend("force", vim.deepcopy(defaults), opts or {})
|
|
75
|
+
|
|
76
|
+
if not M._opts.auto_follow then
|
|
77
|
+
return M
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
M._timer = vim.uv.new_timer()
|
|
81
|
+
M._timer:start(
|
|
82
|
+
0,
|
|
83
|
+
M._opts.poll_interval_ms,
|
|
84
|
+
vim.schedule_wrap(function()
|
|
85
|
+
M.poll_once()
|
|
86
|
+
end)
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
return M
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
return M
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
local M = {}
|
|
2
|
+
|
|
3
|
+
function M.read(path)
|
|
4
|
+
local expanded = vim.fn.expand(path)
|
|
5
|
+
if vim.fn.filereadable(expanded) == 0 then
|
|
6
|
+
return nil
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
local lines = vim.fn.readfile(expanded)
|
|
10
|
+
if #lines == 0 then
|
|
11
|
+
return nil
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
local ok, data = pcall(vim.json.decode, table.concat(lines, "\n"))
|
|
15
|
+
if not ok or type(data) ~= "table" then
|
|
16
|
+
return nil
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
return data
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
return M
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@k1e1n04/mav",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Multi-agent view — manage multiple AI CLI sessions in one terminal",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"mav": "./dist/bin/mav.js"
|
|
8
|
+
},
|
|
9
|
+
"publishConfig": {
|
|
10
|
+
"access": "public"
|
|
11
|
+
},
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "https://github.com/k1e1n04/mav.git"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist/",
|
|
19
|
+
"src/",
|
|
20
|
+
"bin/",
|
|
21
|
+
"lua/",
|
|
22
|
+
"plugin/",
|
|
23
|
+
"LICENSE",
|
|
24
|
+
"README.md"
|
|
25
|
+
],
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "tsc && node scripts/postbuild.mjs",
|
|
28
|
+
"dev": "tsx bin/mav.ts",
|
|
29
|
+
"test": "vitest run",
|
|
30
|
+
"test:watch": "vitest",
|
|
31
|
+
"prepublishOnly": "pnpm test && pnpm build"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"commander": "13.1.0",
|
|
35
|
+
"js-yaml": "4.1.0",
|
|
36
|
+
"neo-blessed": "0.2.0",
|
|
37
|
+
"node-pty": "1.0.0",
|
|
38
|
+
"term.js": "^0.0.7"
|
|
39
|
+
},
|
|
40
|
+
"pnpm": {
|
|
41
|
+
"onlyBuiltDependencies": [
|
|
42
|
+
"esbuild",
|
|
43
|
+
"node-pty"
|
|
44
|
+
]
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@types/blessed": "^0.1.27",
|
|
48
|
+
"@types/js-yaml": "4.0.9",
|
|
49
|
+
"@types/node": "22.15.21",
|
|
50
|
+
"tsx": "4.19.3",
|
|
51
|
+
"typescript": "5.8.3",
|
|
52
|
+
"vitest": "3.2.0"
|
|
53
|
+
}
|
|
54
|
+
}
|
package/plugin/mav.lua
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
if vim.g.loaded_mav_plugin == 1 then
|
|
2
|
+
return
|
|
3
|
+
end
|
|
4
|
+
|
|
5
|
+
vim.g.loaded_mav_plugin = 1
|
|
6
|
+
|
|
7
|
+
vim.api.nvim_create_user_command("MavFollowNow", function()
|
|
8
|
+
require("mav").follow_now()
|
|
9
|
+
end, {})
|
|
10
|
+
|
|
11
|
+
vim.api.nvim_create_user_command("MavStatus", function()
|
|
12
|
+
local session = require("mav").status()
|
|
13
|
+
if not session then
|
|
14
|
+
print("mav: no selected session state")
|
|
15
|
+
return
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
print(string.format(
|
|
19
|
+
"%s (%s) -> %s",
|
|
20
|
+
session.displayName or session.sessionId or "unknown",
|
|
21
|
+
session.agentType or "unknown",
|
|
22
|
+
session.cwd or ""
|
|
23
|
+
))
|
|
24
|
+
end, {})
|