mstro-app 0.3.0 → 0.3.1
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/bin/mstro.js +65 -2
- package/dist/server/cli/headless/claude-invoker.d.ts.map +1 -1
- package/dist/server/cli/headless/claude-invoker.js +4 -3
- package/dist/server/cli/headless/claude-invoker.js.map +1 -1
- package/dist/server/cli/headless/mcp-config.js +2 -2
- package/dist/server/cli/headless/mcp-config.js.map +1 -1
- package/dist/server/cli/headless/runner.d.ts +6 -1
- package/dist/server/cli/headless/runner.d.ts.map +1 -1
- package/dist/server/cli/headless/runner.js +36 -4
- package/dist/server/cli/headless/runner.js.map +1 -1
- package/dist/server/cli/headless/types.d.ts +1 -1
- package/dist/server/cli/headless/types.d.ts.map +1 -1
- package/dist/server/cli/improvisation-session-manager.d.ts +2 -2
- package/dist/server/cli/improvisation-session-manager.d.ts.map +1 -1
- package/dist/server/cli/improvisation-session-manager.js +3 -2
- package/dist/server/cli/improvisation-session-manager.js.map +1 -1
- package/dist/server/index.js +6 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/mcp/bouncer-cli.js +53 -14
- package/dist/server/mcp/bouncer-cli.js.map +1 -1
- package/dist/server/mcp/bouncer-integration.d.ts +1 -1
- package/dist/server/mcp/bouncer-integration.d.ts.map +1 -1
- package/dist/server/mcp/bouncer-integration.js +70 -7
- package/dist/server/mcp/bouncer-integration.js.map +1 -1
- package/dist/server/mcp/security-audit.d.ts +3 -3
- package/dist/server/mcp/security-audit.d.ts.map +1 -1
- package/dist/server/mcp/security-audit.js.map +1 -1
- package/dist/server/mcp/server.js +3 -2
- package/dist/server/mcp/server.js.map +1 -1
- package/dist/server/services/analytics.d.ts +2 -2
- package/dist/server/services/analytics.d.ts.map +1 -1
- package/dist/server/services/analytics.js.map +1 -1
- package/dist/server/services/files.js +7 -7
- package/dist/server/services/files.js.map +1 -1
- package/dist/server/services/pathUtils.js +1 -1
- package/dist/server/services/pathUtils.js.map +1 -1
- package/dist/server/services/platform.d.ts +2 -2
- package/dist/server/services/platform.d.ts.map +1 -1
- package/dist/server/services/platform.js.map +1 -1
- package/dist/server/services/sentry.d.ts +1 -1
- package/dist/server/services/sentry.d.ts.map +1 -1
- package/dist/server/services/sentry.js.map +1 -1
- package/dist/server/services/terminal/pty-manager.d.ts +10 -0
- package/dist/server/services/terminal/pty-manager.d.ts.map +1 -1
- package/dist/server/services/terminal/pty-manager.js +32 -4
- package/dist/server/services/terminal/pty-manager.js.map +1 -1
- package/dist/server/services/websocket/file-explorer-handlers.js.map +1 -1
- package/dist/server/services/websocket/file-utils.d.ts +4 -0
- package/dist/server/services/websocket/file-utils.d.ts.map +1 -1
- package/dist/server/services/websocket/file-utils.js +27 -8
- package/dist/server/services/websocket/file-utils.js.map +1 -1
- package/dist/server/services/websocket/git-handlers.js +17 -17
- package/dist/server/services/websocket/git-handlers.js.map +1 -1
- package/dist/server/services/websocket/git-pr-handlers.js +3 -3
- package/dist/server/services/websocket/git-pr-handlers.js.map +1 -1
- package/dist/server/services/websocket/git-worktree-handlers.js +10 -10
- package/dist/server/services/websocket/git-worktree-handlers.js.map +1 -1
- package/dist/server/services/websocket/handler.js +1 -1
- package/dist/server/services/websocket/handler.js.map +1 -1
- package/dist/server/services/websocket/session-handlers.d.ts +1 -1
- package/dist/server/services/websocket/session-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/session-handlers.js +12 -11
- package/dist/server/services/websocket/session-handlers.js.map +1 -1
- package/dist/server/services/websocket/tab-handlers.js.map +1 -1
- package/dist/server/services/websocket/terminal-handlers.js +1 -1
- package/dist/server/services/websocket/terminal-handlers.js.map +1 -1
- package/dist/server/services/websocket/types.d.ts.map +1 -1
- package/dist/server/utils/agent-manager.d.ts +22 -2
- package/dist/server/utils/agent-manager.d.ts.map +1 -1
- package/dist/server/utils/agent-manager.js +2 -2
- package/dist/server/utils/agent-manager.js.map +1 -1
- package/dist/server/utils/port-manager.js.map +1 -1
- package/hooks/bouncer.sh +17 -3
- package/package.json +4 -2
- package/server/cli/headless/claude-invoker.ts +21 -16
- package/server/cli/headless/mcp-config.ts +8 -8
- package/server/cli/headless/runner.ts +32 -4
- package/server/cli/headless/types.ts +1 -1
- package/server/cli/improvisation-session-manager.ts +8 -7
- package/server/index.ts +15 -9
- package/server/mcp/bouncer-cli.ts +73 -20
- package/server/mcp/bouncer-integration.ts +99 -16
- package/server/mcp/security-audit.ts +4 -4
- package/server/mcp/server.ts +6 -5
- package/server/services/analytics.ts +3 -3
- package/server/services/files.ts +13 -13
- package/server/services/pathUtils.ts +2 -2
- package/server/services/platform.ts +5 -5
- package/server/services/sentry.ts +1 -1
- package/server/services/terminal/pty-manager.ts +36 -9
- package/server/services/websocket/file-explorer-handlers.ts +1 -1
- package/server/services/websocket/file-utils.ts +28 -9
- package/server/services/websocket/git-handlers.ts +34 -34
- package/server/services/websocket/git-pr-handlers.ts +6 -6
- package/server/services/websocket/git-worktree-handlers.ts +20 -20
- package/server/services/websocket/handler.ts +2 -2
- package/server/services/websocket/session-handlers.ts +31 -30
- package/server/services/websocket/tab-handlers.ts +1 -1
- package/server/services/websocket/terminal-handlers.ts +2 -2
- package/server/services/websocket/types.ts +2 -0
- package/server/utils/agent-manager.ts +6 -6
- package/server/utils/port-manager.ts +1 -1
- package/server/cli/headless/output-utils.test.ts +0 -225
- package/server/cli/headless/stall-assessor.test.ts +0 -165
- package/server/cli/headless/tool-watchdog.test.ts +0 -429
- package/server/mcp/bouncer-integration.test.ts +0 -161
- package/server/mcp/security-patterns.test.ts +0 -258
- package/server/services/platform.test.ts +0 -1304
- package/server/services/websocket/autocomplete.test.ts +0 -194
- package/server/services/websocket/handler.test.ts +0 -20
|
@@ -1,194 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import { AutocompleteService } from './autocomplete.js';
|
|
3
|
-
|
|
4
|
-
// Mock file system operations to avoid hitting real FS
|
|
5
|
-
vi.mock('node:fs', () => ({
|
|
6
|
-
existsSync: vi.fn(() => false),
|
|
7
|
-
readdirSync: vi.fn(() => []),
|
|
8
|
-
statSync: vi.fn(() => ({ isDirectory: () => false })),
|
|
9
|
-
}));
|
|
10
|
-
|
|
11
|
-
vi.mock('./file-utils.js', () => ({
|
|
12
|
-
CACHE_TTL_MS: 5000,
|
|
13
|
-
directoryCache: new Map(),
|
|
14
|
-
getFileType: vi.fn((path: string) => {
|
|
15
|
-
const ext = path.split('.').pop() || '';
|
|
16
|
-
return ext || 'unknown';
|
|
17
|
-
}),
|
|
18
|
-
isIgnored: vi.fn(() => false),
|
|
19
|
-
parseGitignore: vi.fn(() => []),
|
|
20
|
-
scanDirectoryRecursiveWithDepth: vi.fn(() => []),
|
|
21
|
-
}));
|
|
22
|
-
|
|
23
|
-
describe('AutocompleteService', () => {
|
|
24
|
-
// ========== Frecency ==========
|
|
25
|
-
|
|
26
|
-
describe('calculateFrecencyScore', () => {
|
|
27
|
-
it('returns 0 for unknown files', () => {
|
|
28
|
-
const svc = new AutocompleteService();
|
|
29
|
-
expect(svc.calculateFrecencyScore('nonexistent.ts')).toBe(0);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it('returns positive score for recently used files', () => {
|
|
33
|
-
const svc = new AutocompleteService();
|
|
34
|
-
svc.recordFileSelection('src/index.ts');
|
|
35
|
-
const score = svc.calculateFrecencyScore('src/index.ts');
|
|
36
|
-
expect(score).toBeGreaterThan(0);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it('increases score with more selections', () => {
|
|
40
|
-
const svc = new AutocompleteService();
|
|
41
|
-
svc.recordFileSelection('src/index.ts');
|
|
42
|
-
const score1 = svc.calculateFrecencyScore('src/index.ts');
|
|
43
|
-
|
|
44
|
-
svc.recordFileSelection('src/index.ts');
|
|
45
|
-
svc.recordFileSelection('src/index.ts');
|
|
46
|
-
const score3 = svc.calculateFrecencyScore('src/index.ts');
|
|
47
|
-
|
|
48
|
-
expect(score3).toBeGreaterThan(score1);
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it('decays score over time', () => {
|
|
52
|
-
const svc = new AutocompleteService();
|
|
53
|
-
|
|
54
|
-
// Record selection at a specific time
|
|
55
|
-
const now = Date.now();
|
|
56
|
-
vi.spyOn(Date, 'now').mockReturnValue(now);
|
|
57
|
-
svc.recordFileSelection('src/index.ts');
|
|
58
|
-
const recentScore = svc.calculateFrecencyScore('src/index.ts');
|
|
59
|
-
|
|
60
|
-
// Move forward 8 days (past the 7-day recency window)
|
|
61
|
-
vi.spyOn(Date, 'now').mockReturnValue(now + 8 * 24 * 60 * 60 * 1000);
|
|
62
|
-
const staleScore = svc.calculateFrecencyScore('src/index.ts');
|
|
63
|
-
|
|
64
|
-
expect(staleScore).toBeLessThan(recentScore);
|
|
65
|
-
vi.restoreAllMocks();
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it('handles initial frecency data in constructor', () => {
|
|
69
|
-
const svc = new AutocompleteService({
|
|
70
|
-
'src/main.ts': { count: 5, lastUsed: Date.now() },
|
|
71
|
-
});
|
|
72
|
-
expect(svc.calculateFrecencyScore('src/main.ts')).toBeGreaterThan(0);
|
|
73
|
-
});
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
// ========== recordFileSelection ==========
|
|
77
|
-
|
|
78
|
-
describe('recordFileSelection', () => {
|
|
79
|
-
it('creates new entry for first selection', () => {
|
|
80
|
-
const svc = new AutocompleteService();
|
|
81
|
-
svc.recordFileSelection('new-file.ts');
|
|
82
|
-
|
|
83
|
-
const data = svc.getFrecencyData();
|
|
84
|
-
expect(data['new-file.ts']).toBeDefined();
|
|
85
|
-
expect(data['new-file.ts'].count).toBe(1);
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it('increments count for existing entry', () => {
|
|
89
|
-
const svc = new AutocompleteService();
|
|
90
|
-
svc.recordFileSelection('file.ts');
|
|
91
|
-
svc.recordFileSelection('file.ts');
|
|
92
|
-
svc.recordFileSelection('file.ts');
|
|
93
|
-
|
|
94
|
-
const data = svc.getFrecencyData();
|
|
95
|
-
expect(data['file.ts'].count).toBe(3);
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
it('updates lastUsed timestamp', () => {
|
|
99
|
-
const svc = new AutocompleteService();
|
|
100
|
-
const before = Date.now();
|
|
101
|
-
svc.recordFileSelection('file.ts');
|
|
102
|
-
const data = svc.getFrecencyData();
|
|
103
|
-
expect(data['file.ts'].lastUsed).toBeGreaterThanOrEqual(before);
|
|
104
|
-
});
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
// ========== setFrecencyData / getFrecencyData ==========
|
|
108
|
-
|
|
109
|
-
describe('setFrecencyData / getFrecencyData', () => {
|
|
110
|
-
it('replaces frecency data', () => {
|
|
111
|
-
const svc = new AutocompleteService();
|
|
112
|
-
svc.recordFileSelection('old.ts');
|
|
113
|
-
|
|
114
|
-
const newData = {
|
|
115
|
-
'new.ts': { count: 10, lastUsed: Date.now() },
|
|
116
|
-
};
|
|
117
|
-
svc.setFrecencyData(newData);
|
|
118
|
-
|
|
119
|
-
expect(svc.getFrecencyData()).toBe(newData);
|
|
120
|
-
expect(svc.calculateFrecencyScore('old.ts')).toBe(0);
|
|
121
|
-
expect(svc.calculateFrecencyScore('new.ts')).toBeGreaterThan(0);
|
|
122
|
-
});
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
// ========== getFileCompletions ==========
|
|
126
|
-
|
|
127
|
-
describe('getFileCompletions', () => {
|
|
128
|
-
it('returns empty array when no files match', () => {
|
|
129
|
-
const svc = new AutocompleteService();
|
|
130
|
-
const results = svc.getFileCompletions('nonexistent', '/tmp/test');
|
|
131
|
-
expect(results).toEqual([]);
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
it('handles @ symbol prefix', () => {
|
|
135
|
-
const svc = new AutocompleteService();
|
|
136
|
-
// Should not throw when handling @ prefix
|
|
137
|
-
const results = svc.getFileCompletions('@src/index', '/tmp/test');
|
|
138
|
-
expect(Array.isArray(results)).toBe(true);
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
it('returns empty array on error', () => {
|
|
142
|
-
const svc = new AutocompleteService();
|
|
143
|
-
// Invalid working dir should return empty (caught by try/catch)
|
|
144
|
-
const results = svc.getFileCompletions('test', '/nonexistent/path');
|
|
145
|
-
expect(results).toEqual([]);
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
it('limits results to 15', () => {
|
|
149
|
-
// This is tested structurally — the code slices to 15
|
|
150
|
-
const svc = new AutocompleteService();
|
|
151
|
-
const results = svc.getFileCompletions('', '/tmp/test');
|
|
152
|
-
expect(results.length).toBeLessThanOrEqual(15);
|
|
153
|
-
});
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
// ========== Scoring logic (frecency weight formula) ==========
|
|
157
|
-
|
|
158
|
-
describe('frecency scoring formula', () => {
|
|
159
|
-
it('uses log2 for frequency weight', () => {
|
|
160
|
-
const svc = new AutocompleteService();
|
|
161
|
-
|
|
162
|
-
// count=1: log2(2) = 1
|
|
163
|
-
svc.setFrecencyData({ 'a.ts': { count: 1, lastUsed: Date.now() } });
|
|
164
|
-
const score1 = svc.calculateFrecencyScore('a.ts');
|
|
165
|
-
|
|
166
|
-
// count=7: log2(8) = 3
|
|
167
|
-
svc.setFrecencyData({ 'a.ts': { count: 7, lastUsed: Date.now() } });
|
|
168
|
-
const score7 = svc.calculateFrecencyScore('a.ts');
|
|
169
|
-
|
|
170
|
-
// Score should roughly triple (3x) since frequency goes from 1 to 3
|
|
171
|
-
expect(score7 / score1).toBeCloseTo(3, 0);
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
it('recency weight is ~1.0 for very recent files', () => {
|
|
175
|
-
const svc = new AutocompleteService();
|
|
176
|
-
svc.setFrecencyData({ 'a.ts': { count: 1, lastUsed: Date.now() } });
|
|
177
|
-
const score = svc.calculateFrecencyScore('a.ts');
|
|
178
|
-
|
|
179
|
-
// With recencyWeight ≈ 1, score ≈ log2(2) * (0.3 + 0.7*1) * 100 = 100
|
|
180
|
-
expect(score).toBeCloseTo(100, -1);
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
it('recency weight is ~0.3 for files used > 7 days ago', () => {
|
|
184
|
-
const svc = new AutocompleteService();
|
|
185
|
-
const eightDaysAgo = Date.now() - (8 * 24 * 60 * 60 * 1000);
|
|
186
|
-
svc.setFrecencyData({ 'a.ts': { count: 1, lastUsed: eightDaysAgo } });
|
|
187
|
-
const score = svc.calculateFrecencyScore('a.ts');
|
|
188
|
-
|
|
189
|
-
// With recencyWeight = max(0, 1 - 8*24/168) = 0
|
|
190
|
-
// score ≈ log2(2) * (0.3 + 0.7*0) * 100 = 30
|
|
191
|
-
expect(score).toBeCloseTo(30, -1);
|
|
192
|
-
});
|
|
193
|
-
});
|
|
194
|
-
});
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { readFileSync } from 'node:fs'
|
|
2
|
-
import { join } from 'node:path'
|
|
3
|
-
import { describe, expect, it } from 'vitest'
|
|
4
|
-
|
|
5
|
-
describe('WebSocket handler code quality', () => {
|
|
6
|
-
const handlerSource = readFileSync(
|
|
7
|
-
join(import.meta.dirname || __dirname, 'handler.ts'),
|
|
8
|
-
'utf-8'
|
|
9
|
-
)
|
|
10
|
-
|
|
11
|
-
it('does not use require() — ESM only', () => {
|
|
12
|
-
// Ensure no require() calls exist (the bug was require('fs').mkdirSync)
|
|
13
|
-
const requireCalls = handlerSource.match(/require\s*\(/g)
|
|
14
|
-
expect(requireCalls).toBeNull()
|
|
15
|
-
})
|
|
16
|
-
|
|
17
|
-
it('imports mkdirSync from fs at the top level', () => {
|
|
18
|
-
expect(handlerSource).toContain("import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'")
|
|
19
|
-
})
|
|
20
|
-
})
|