@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.
Files changed (56) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +212 -0
  3. package/bin/mav.ts +31 -0
  4. package/dist/bin/mav.d.ts +2 -0
  5. package/dist/bin/mav.js +24 -0
  6. package/dist/bin/mav.js.map +1 -0
  7. package/dist/src/agent-launch.d.ts +15 -0
  8. package/dist/src/agent-launch.js +36 -0
  9. package/dist/src/agent-launch.js.map +1 -0
  10. package/dist/src/agent.d.ts +42 -0
  11. package/dist/src/agent.js +209 -0
  12. package/dist/src/agent.js.map +1 -0
  13. package/dist/src/config.d.ts +11 -0
  14. package/dist/src/config.js +32 -0
  15. package/dist/src/config.js.map +1 -0
  16. package/dist/src/current-session.d.ts +15 -0
  17. package/dist/src/current-session.js +17 -0
  18. package/dist/src/current-session.js.map +1 -0
  19. package/dist/src/index.d.ts +6 -0
  20. package/dist/src/index.js +90 -0
  21. package/dist/src/index.js.map +1 -0
  22. package/dist/src/process-cwd.d.ts +1 -0
  23. package/dist/src/process-cwd.js +24 -0
  24. package/dist/src/process-cwd.js.map +1 -0
  25. package/dist/src/session-manager.d.ts +16 -0
  26. package/dist/src/session-manager.js +102 -0
  27. package/dist/src/session-manager.js.map +1 -0
  28. package/dist/src/state.d.ts +19 -0
  29. package/dist/src/state.js +30 -0
  30. package/dist/src/state.js.map +1 -0
  31. package/dist/src/ui/app.d.ts +15 -0
  32. package/dist/src/ui/app.js +109 -0
  33. package/dist/src/ui/app.js.map +1 -0
  34. package/dist/src/ui/detail.d.ts +28 -0
  35. package/dist/src/ui/detail.js +137 -0
  36. package/dist/src/ui/detail.js.map +1 -0
  37. package/dist/src/ui/overview.d.ts +24 -0
  38. package/dist/src/ui/overview.js +267 -0
  39. package/dist/src/ui/overview.js.map +1 -0
  40. package/lua/mav/follow.lua +37 -0
  41. package/lua/mav/init.lua +92 -0
  42. package/lua/mav/state.lua +22 -0
  43. package/package.json +54 -0
  44. package/plugin/mav.lua +24 -0
  45. package/src/agent-launch.ts +43 -0
  46. package/src/agent.ts +243 -0
  47. package/src/config.ts +51 -0
  48. package/src/current-session.ts +34 -0
  49. package/src/index.ts +121 -0
  50. package/src/process-cwd.ts +31 -0
  51. package/src/session-manager.ts +122 -0
  52. package/src/state.ts +49 -0
  53. package/src/types/neo-blessed.d.ts +5 -0
  54. package/src/ui/app.ts +121 -0
  55. package/src/ui/detail.ts +164 -0
  56. 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":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAIxC,MAAM,OAAO,QAAQ;IACX,MAAM,CAAU,+BAA+B,GAAG,iBAAiB,CAAA;IACnE,MAAM,CAAU,4BAA4B,GAAG,EAAE,CAAA;IAEjD,MAAM,CAAU,cAAc,GAAG;QACvC,MAAM;QACN,YAAY;QACZ,eAAe;KACP,CAAA;IACF,MAAM,CAAU,sBAAsB,GAAG;QAC/C,kCAAkC;QAClC,eAAe;KACP,CAAA;IAEF,MAAM,CAAgB;IACtB,YAAY,CAAY;IACxB,cAAc,GAAwB,IAAI,CAAA;IAC1C,YAAY,GAAoC,IAAI,CAAA;IACpD,gBAAgB,GAAsC,IAAI,CAAA;IAC1D,YAAY,GAAG,EAAE,CAAA;IACjB,iBAAiB,GAAyC,IAAI,CAAA;IAC9D,wBAAwB,GAAkB,IAAI,CAAA;IAEtD,YAAY,MAAsB,EAAE,YAAwB;QAC1D,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;QACpB,IAAI,CAAC,YAAY,GAAG,YAAY,CAAA;IAClC,CAAC;IAEO,cAAc,CAAC,KAAa;QAClC,OAAO,CACL,QAAQ,CAAC,cAAc,CAAC,QAAQ,CAChC,KAAiD,CAChD;YACD,QAAQ,CAAC,sBAAsB,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CACvE,CAAA;IACH,CAAC;IAEO,oBAAoB,CAAC,KAAa;QACxC,OAAO,CACL,QAAQ,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;YACtE,gBAAgB,CAAC,UAAU,CAAC,KAAK,CAAC;YAClC,kBAAkB,CAAC,UAAU,CAAC,KAAK,CAAC;YACpC,YAAY,CAAC,UAAU,CAAC,KAAK,CAAC,CAC/B,CAAA;IACH,CAAC;IAEO,aAAa,CAAC,GAAW;QAC/B,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAA;QAC5C,IAAI,CAAC,IAAI;YAAE,OAAM;QAEjB,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;QACpD,cAAc,CAAC,IAAI,EAAE,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAA;IACjE,CAAC;IAEO,gCAAgC,CAAC,GAAW;QAClD,KAAK,MAAM,KAAK,IAAI,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,+BAA+B,CAAC,EAAE,CAAC;YAC3E,IAAI,CAAC,wBAAwB,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,IAAI,CAAA;QAClD,CAAC;IACH,CAAC;IAEO,sBAAsB;QAC5B,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,CAAC;YAC5B,OAAM;QACR,CAAC;QACD,YAAY,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAA;QACpC,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAA;IAC/B,CAAC;IAEO,iBAAiB;QACvB,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC;YACvB,OAAM;QACR,CAAC;QACD,IAAI,CAAC,cAAc,EAAE,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAA;QAC7C,IAAI,CAAC,YAAY,GAAG,EAAE,CAAA;QACtB,IAAI,CAAC,sBAAsB,EAAE,CAAA;IAC/B,CAAC;IAED,MAAM,CAAC,OAAqB;QAC1B,IAAI,CAAC,MAAM,EAAE,CAAA;QACb,IAAI,CAAC,cAAc,GAAG,OAAO,CAAA;QAC7B,IAAI,CAAC,YAAY,GAAG,EAAE,CAAA;QAEtB,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAA;QAE7C,MAAM,OAAO,GAAG,OAAO,CAAC,SAAS;aAC9B,IAAI,CAAC,EAAE,CAAC;aACR,OAAO,CAAC,qCAAqC,EAAE,EAAE,CAAC;aAClD,OAAO,CAAC,mCAAmC,EAAE,EAAE,CAAC,CAAA;QACnD,IAAI,IAAI,CAAC,wBAAwB,EAAE,CAAC;YAClC,MAAM,CAAC,KAAK,CAAC,SAAS,IAAI,CAAC,wBAAwB,GAAG,CAAC,CAAA;QACzD,CAAC;QACD,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAA;QACxB,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,CAAA;QAC7B,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;QAErB,IAAI,CAAC,YAAY,GAAG,CAAC,IAAY,EAAE,EAAE;YACnC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QACpB,CAAC,CAAA;QACD,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,YAAY,CAAC,CAAA;QAErC,IAAI,CAAC,gBAAgB,GAAG,CAAC,KAAc,EAAE,EAAE;YACzC,MAAM,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;gBAChC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC;gBACxB,CAAC,CAAC,OAAO,KAAK,KAAK,QAAQ;oBACzB,CAAC,CAAC,KAAK;oBACP,CAAC,CAAC,EAAE,CAAA;YACR,IAAI,CAAC,GAAG;gBAAE,OAAM;YAChB,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAA;YACvB,IAAI,CAAC,gCAAgC,CAAC,GAAG,CAAC,CAAA;YAC1C,IAAI,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC7B,IAAI,CAAC,YAAY,EAAE,CAAA;gBACnB,OAAM;YACR,CAAC;YACD,IAAI,CAAC,YAAY,IAAI,GAAG,CAAA;YAExB,IAAI,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC;gBAC3C,IAAI,CAAC,sBAAsB,EAAE,CAAA;gBAC7B,IAAI,CAAC,YAAY,GAAG,EAAE,CAAA;gBACtB,IAAI,CAAC,YAAY,EAAE,CAAA;gBACnB,OAAM;YACR,CAAC;YAED,IAAI,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC;gBACjD,IAAI,CAAC,sBAAsB,EAAE,CAAA;gBAC7B,IAAI,CAAC,iBAAiB,GAAG,UAAU,CAAC,GAAG,EAAE;oBACvC,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAA;oBAC7B,IAAI,CAAC,iBAAiB,EAAE,CAAA;gBAC1B,CAAC,EAAE,QAAQ,CAAC,4BAA4B,CAAC,CAAA;gBACzC,OAAM;YACR,CAAC;YAED,IAAI,CAAC,iBAAiB,EAAE,CAAA;QAC1B,CAAC,CAAA;QACD,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,gBAAgB,CAAC,CAAA;IACzC,CAAC;IAED,MAAM;QACJ,IAAI,IAAI,CAAC,cAAc,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YAC7C,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,YAAY,CAAC,CAAA;YAClD,IAAI,CAAC,YAAY,GAAG,IAAI,CAAA;QAC1B,CAAC;QACD,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC1B,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,EAAE,IAAI,CAAC,gBAAgB,CAAC,CAAA;YACvE,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAA;QAC9B,CAAC;QACD,IAAI,IAAI,CAAC,wBAAwB,EAAE,CAAC;YAClC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAA;QAC9C,CAAC;QACD,IAAI,CAAC,sBAAsB,EAAE,CAAA;QAC7B,IAAI,CAAC,YAAY,GAAG,EAAE,CAAA;QACtB,IAAI,CAAC,cAAc,GAAG,IAAI,CAAA;IAC5B,CAAC;IAED,IAAI,KAAU,CAAC;IACf,IAAI,KAAU,CAAC;IAEf,MAAM,CAAC,IAAY,EAAE,IAAY;QAC/B,IAAI,CAAC,cAAc,EAAE,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;IACzC,CAAC"}
@@ -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
@@ -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, {})