mstro-app 0.3.7 → 0.3.9
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/README.md +4 -8
- package/bin/mstro.js +54 -15
- package/dist/server/cli/headless/claude-invoker.d.ts.map +1 -1
- package/dist/server/cli/headless/claude-invoker.js +18 -9
- package/dist/server/cli/headless/claude-invoker.js.map +1 -1
- package/dist/server/cli/headless/headless-logger.d.ts +10 -0
- package/dist/server/cli/headless/headless-logger.d.ts.map +1 -0
- package/dist/server/cli/headless/headless-logger.js +66 -0
- package/dist/server/cli/headless/headless-logger.js.map +1 -0
- package/dist/server/cli/headless/mcp-config.d.ts.map +1 -1
- package/dist/server/cli/headless/mcp-config.js +6 -5
- package/dist/server/cli/headless/mcp-config.js.map +1 -1
- package/dist/server/cli/headless/runner.d.ts.map +1 -1
- package/dist/server/cli/headless/runner.js +4 -0
- package/dist/server/cli/headless/runner.js.map +1 -1
- package/dist/server/cli/headless/stall-assessor.d.ts +21 -0
- package/dist/server/cli/headless/stall-assessor.d.ts.map +1 -1
- package/dist/server/cli/headless/stall-assessor.js +74 -20
- package/dist/server/cli/headless/stall-assessor.js.map +1 -1
- package/dist/server/cli/headless/tool-watchdog.d.ts +0 -12
- package/dist/server/cli/headless/tool-watchdog.d.ts.map +1 -1
- package/dist/server/cli/headless/tool-watchdog.js +30 -9
- package/dist/server/cli/headless/tool-watchdog.js.map +1 -1
- package/dist/server/cli/headless/types.d.ts +8 -1
- package/dist/server/cli/headless/types.d.ts.map +1 -1
- package/dist/server/cli/improvisation-session-manager.d.ts +16 -0
- package/dist/server/cli/improvisation-session-manager.d.ts.map +1 -1
- package/dist/server/cli/improvisation-session-manager.js +94 -11
- package/dist/server/cli/improvisation-session-manager.js.map +1 -1
- package/dist/server/index.js +0 -4
- package/dist/server/index.js.map +1 -1
- package/dist/server/mcp/bouncer-cli.d.ts +3 -0
- package/dist/server/mcp/bouncer-cli.d.ts.map +1 -0
- package/dist/server/mcp/bouncer-cli.js +54 -0
- package/dist/server/mcp/bouncer-cli.js.map +1 -0
- package/dist/server/mcp/bouncer-integration.d.ts +2 -0
- package/dist/server/mcp/bouncer-integration.d.ts.map +1 -1
- package/dist/server/mcp/bouncer-integration.js +55 -39
- package/dist/server/mcp/bouncer-integration.js.map +1 -1
- package/dist/server/mcp/bouncer-sandbox.d.ts +60 -0
- package/dist/server/mcp/bouncer-sandbox.d.ts.map +1 -0
- package/dist/server/mcp/bouncer-sandbox.js +182 -0
- package/dist/server/mcp/bouncer-sandbox.js.map +1 -0
- package/dist/server/mcp/security-patterns.d.ts +6 -12
- package/dist/server/mcp/security-patterns.d.ts.map +1 -1
- package/dist/server/mcp/security-patterns.js +197 -10
- package/dist/server/mcp/security-patterns.js.map +1 -1
- package/dist/server/services/plan/composer.d.ts +4 -0
- package/dist/server/services/plan/composer.d.ts.map +1 -0
- package/dist/server/services/plan/composer.js +181 -0
- package/dist/server/services/plan/composer.js.map +1 -0
- package/dist/server/services/plan/dependency-resolver.d.ts +28 -0
- package/dist/server/services/plan/dependency-resolver.d.ts.map +1 -0
- package/dist/server/services/plan/dependency-resolver.js +152 -0
- package/dist/server/services/plan/dependency-resolver.js.map +1 -0
- package/dist/server/services/plan/executor.d.ts +91 -0
- package/dist/server/services/plan/executor.d.ts.map +1 -0
- package/dist/server/services/plan/executor.js +545 -0
- package/dist/server/services/plan/executor.js.map +1 -0
- package/dist/server/services/plan/parser.d.ts +11 -0
- package/dist/server/services/plan/parser.d.ts.map +1 -0
- package/dist/server/services/plan/parser.js +415 -0
- package/dist/server/services/plan/parser.js.map +1 -0
- package/dist/server/services/plan/state-reconciler.d.ts +2 -0
- package/dist/server/services/plan/state-reconciler.d.ts.map +1 -0
- package/dist/server/services/plan/state-reconciler.js +105 -0
- package/dist/server/services/plan/state-reconciler.js.map +1 -0
- package/dist/server/services/plan/types.d.ts +120 -0
- package/dist/server/services/plan/types.d.ts.map +1 -0
- package/dist/server/services/plan/types.js +4 -0
- package/dist/server/services/plan/types.js.map +1 -0
- package/dist/server/services/plan/watcher.d.ts +14 -0
- package/dist/server/services/plan/watcher.d.ts.map +1 -0
- package/dist/server/services/plan/watcher.js +69 -0
- package/dist/server/services/plan/watcher.js.map +1 -0
- package/dist/server/services/websocket/file-explorer-handlers.js +20 -0
- package/dist/server/services/websocket/file-explorer-handlers.js.map +1 -1
- package/dist/server/services/websocket/handler.d.ts +0 -1
- package/dist/server/services/websocket/handler.d.ts.map +1 -1
- package/dist/server/services/websocket/handler.js +28 -2
- package/dist/server/services/websocket/handler.js.map +1 -1
- package/dist/server/services/websocket/plan-handlers.d.ts +6 -0
- package/dist/server/services/websocket/plan-handlers.d.ts.map +1 -0
- package/dist/server/services/websocket/plan-handlers.js +494 -0
- package/dist/server/services/websocket/plan-handlers.js.map +1 -0
- package/dist/server/services/websocket/quality-handlers.d.ts +4 -0
- package/dist/server/services/websocket/quality-handlers.d.ts.map +1 -0
- package/dist/server/services/websocket/quality-handlers.js +470 -0
- package/dist/server/services/websocket/quality-handlers.js.map +1 -0
- package/dist/server/services/websocket/quality-persistence.d.ts +45 -0
- package/dist/server/services/websocket/quality-persistence.d.ts.map +1 -0
- package/dist/server/services/websocket/quality-persistence.js +187 -0
- package/dist/server/services/websocket/quality-persistence.js.map +1 -0
- package/dist/server/services/websocket/quality-service.d.ts +54 -0
- package/dist/server/services/websocket/quality-service.d.ts.map +1 -0
- package/dist/server/services/websocket/quality-service.js +816 -0
- package/dist/server/services/websocket/quality-service.js.map +1 -0
- package/dist/server/services/websocket/session-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/session-handlers.js +23 -0
- package/dist/server/services/websocket/session-handlers.js.map +1 -1
- package/dist/server/services/websocket/types.d.ts +2 -2
- package/dist/server/services/websocket/types.d.ts.map +1 -1
- package/package.json +3 -2
- package/server/cli/headless/claude-invoker.ts +21 -9
- package/server/cli/headless/headless-logger.ts +78 -0
- package/server/cli/headless/mcp-config.ts +6 -5
- package/server/cli/headless/runner.ts +4 -0
- package/server/cli/headless/stall-assessor.ts +101 -20
- package/server/cli/headless/tool-watchdog.ts +18 -9
- package/server/cli/headless/types.ts +10 -1
- package/server/cli/improvisation-session-manager.ts +118 -11
- package/server/index.ts +0 -4
- package/server/mcp/bouncer-cli.ts +73 -0
- package/server/mcp/bouncer-integration.ts +66 -44
- package/server/mcp/bouncer-sandbox.ts +214 -0
- package/server/mcp/security-patterns.ts +206 -10
- package/server/services/plan/composer.ts +199 -0
- package/server/services/plan/dependency-resolver.ts +179 -0
- package/server/services/plan/executor.ts +604 -0
- package/server/services/plan/parser.ts +459 -0
- package/server/services/plan/state-reconciler.ts +132 -0
- package/server/services/plan/types.ts +164 -0
- package/server/services/plan/watcher.ts +73 -0
- package/server/services/websocket/file-explorer-handlers.ts +20 -0
- package/server/services/websocket/handler.ts +28 -2
- package/server/services/websocket/plan-handlers.ts +592 -0
- package/server/services/websocket/quality-handlers.ts +570 -0
- package/server/services/websocket/quality-persistence.ts +250 -0
- package/server/services/websocket/quality-service.ts +975 -0
- package/server/services/websocket/session-handlers.ts +26 -0
- package/server/services/websocket/types.ts +62 -2
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
// Copyright (c) 2025-present Mstro, Inc. All rights reserved.
|
|
2
|
+
// Licensed under the MIT License. See LICENSE file for details.
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Quality Persistence — Persists quality config, reports, and history
|
|
6
|
+
* to .mstro/quality/ in the working directory.
|
|
7
|
+
*
|
|
8
|
+
* Files:
|
|
9
|
+
* .mstro/quality/config.json — Directory list (paths + labels)
|
|
10
|
+
* .mstro/quality/reports/<slug>.json — Latest full report per directory
|
|
11
|
+
* .mstro/quality/history.json — Score history entries for trend tracking
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
15
|
+
import { join } from 'node:path';
|
|
16
|
+
import type { QualityResults } from './quality-service.js';
|
|
17
|
+
|
|
18
|
+
// ============================================================================
|
|
19
|
+
// Types
|
|
20
|
+
// ============================================================================
|
|
21
|
+
|
|
22
|
+
export interface QualityDirectoryConfig {
|
|
23
|
+
path: string;
|
|
24
|
+
label: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface QualityConfig {
|
|
28
|
+
directories: QualityDirectoryConfig[];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface HistoryDirectoryEntry {
|
|
32
|
+
path: string;
|
|
33
|
+
score: number;
|
|
34
|
+
grade: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface QualityHistoryEntry {
|
|
38
|
+
timestamp: string;
|
|
39
|
+
overall: number;
|
|
40
|
+
grade: string;
|
|
41
|
+
directories: HistoryDirectoryEntry[];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
interface QualityHistory {
|
|
45
|
+
entries: QualityHistoryEntry[];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface QualityPersistedState {
|
|
49
|
+
directories: QualityDirectoryConfig[];
|
|
50
|
+
reports: Record<string, QualityResults>;
|
|
51
|
+
history: QualityHistoryEntry[];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// ============================================================================
|
|
55
|
+
// Helpers
|
|
56
|
+
// ============================================================================
|
|
57
|
+
|
|
58
|
+
const MAX_HISTORY_ENTRIES = 100;
|
|
59
|
+
|
|
60
|
+
function slugify(dirPath: string): string {
|
|
61
|
+
if (dirPath === '.' || dirPath === './') return '_root';
|
|
62
|
+
return dirPath.replace(/[/\\]/g, '_').replace(/^_+|_+$/g, '') || '_root';
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function ensureDir(dir: string): void {
|
|
66
|
+
if (!existsSync(dir)) {
|
|
67
|
+
mkdirSync(dir, { recursive: true });
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function readJson<T>(filePath: string, fallback: T): T {
|
|
72
|
+
try {
|
|
73
|
+
if (existsSync(filePath)) {
|
|
74
|
+
return JSON.parse(readFileSync(filePath, 'utf-8')) as T;
|
|
75
|
+
}
|
|
76
|
+
} catch {
|
|
77
|
+
// Corrupted or unreadable — return fallback
|
|
78
|
+
}
|
|
79
|
+
return fallback;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function writeJson(filePath: string, data: unknown): void {
|
|
83
|
+
try {
|
|
84
|
+
writeFileSync(filePath, JSON.stringify(data, null, 2));
|
|
85
|
+
} catch (error) {
|
|
86
|
+
console.error('[QualityPersistence] Error writing:', filePath, error);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// ============================================================================
|
|
91
|
+
// Quality Persistence
|
|
92
|
+
// ============================================================================
|
|
93
|
+
|
|
94
|
+
export class QualityPersistence {
|
|
95
|
+
private qualityDir: string;
|
|
96
|
+
private reportsDir: string;
|
|
97
|
+
private configPath: string;
|
|
98
|
+
private historyPath: string;
|
|
99
|
+
|
|
100
|
+
constructor(workingDir: string) {
|
|
101
|
+
this.qualityDir = join(workingDir, '.mstro', 'quality');
|
|
102
|
+
this.reportsDir = join(this.qualityDir, 'reports');
|
|
103
|
+
this.configPath = join(this.qualityDir, 'config.json');
|
|
104
|
+
this.historyPath = join(this.qualityDir, 'history.json');
|
|
105
|
+
ensureDir(this.reportsDir);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// ---- Config (directory list) ----
|
|
109
|
+
|
|
110
|
+
loadConfig(): QualityDirectoryConfig[] {
|
|
111
|
+
const config = readJson<QualityConfig>(this.configPath, { directories: [] });
|
|
112
|
+
return config.directories;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
saveConfig(directories: QualityDirectoryConfig[]): void {
|
|
116
|
+
writeJson(this.configPath, { directories });
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
addDirectory(path: string, label: string): void {
|
|
120
|
+
const dirs = this.loadConfig();
|
|
121
|
+
if (!dirs.some((d) => d.path === path)) {
|
|
122
|
+
dirs.push({ path, label });
|
|
123
|
+
this.saveConfig(dirs);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
removeDirectory(path: string): void {
|
|
128
|
+
const dirs = this.loadConfig().filter((d) => d.path !== path);
|
|
129
|
+
this.saveConfig(dirs);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// ---- Reports (latest per directory) ----
|
|
133
|
+
|
|
134
|
+
loadReport(dirPath: string): QualityResults | null {
|
|
135
|
+
const slug = slugify(dirPath);
|
|
136
|
+
const reportPath = join(this.reportsDir, `${slug}.json`);
|
|
137
|
+
return readJson<QualityResults | null>(reportPath, null);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
saveReport(dirPath: string, results: QualityResults): void {
|
|
141
|
+
const slug = slugify(dirPath);
|
|
142
|
+
const reportPath = join(this.reportsDir, `${slug}.json`);
|
|
143
|
+
writeJson(reportPath, results);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
loadAllReports(directories: QualityDirectoryConfig[]): Record<string, QualityResults> {
|
|
147
|
+
const reports: Record<string, QualityResults> = {};
|
|
148
|
+
for (const dir of directories) {
|
|
149
|
+
const report = this.loadReport(dir.path);
|
|
150
|
+
if (report) {
|
|
151
|
+
reports[dir.path] = report;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return reports;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// ---- History (trend tracking) ----
|
|
158
|
+
|
|
159
|
+
loadHistory(): QualityHistoryEntry[] {
|
|
160
|
+
const history = readJson<QualityHistory>(this.historyPath, { entries: [] });
|
|
161
|
+
return history.entries;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
appendHistory(results: QualityResults, dirPath: string): void {
|
|
165
|
+
const history = this.loadHistory();
|
|
166
|
+
|
|
167
|
+
// Find or create entry for this timestamp batch
|
|
168
|
+
// If the last entry was within 60 seconds, merge into it (for multi-dir scans)
|
|
169
|
+
const now = new Date();
|
|
170
|
+
const lastEntry = history[history.length - 1];
|
|
171
|
+
const lastTime = lastEntry ? new Date(lastEntry.timestamp).getTime() : 0;
|
|
172
|
+
const mergeWindow = 60_000; // 60 seconds
|
|
173
|
+
|
|
174
|
+
const dirEntry: HistoryDirectoryEntry = {
|
|
175
|
+
path: dirPath,
|
|
176
|
+
score: results.overall,
|
|
177
|
+
grade: results.grade,
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
if (lastEntry && now.getTime() - lastTime < mergeWindow) {
|
|
181
|
+
// Merge: update or add this directory in the last entry
|
|
182
|
+
const existing = lastEntry.directories.findIndex((d) => d.path === dirPath);
|
|
183
|
+
if (existing >= 0) {
|
|
184
|
+
lastEntry.directories[existing] = dirEntry;
|
|
185
|
+
} else {
|
|
186
|
+
lastEntry.directories.push(dirEntry);
|
|
187
|
+
}
|
|
188
|
+
// Recompute overall as average of all directories in this entry
|
|
189
|
+
const totalScore = lastEntry.directories.reduce((sum, d) => sum + d.score, 0);
|
|
190
|
+
lastEntry.overall = Math.round(totalScore / lastEntry.directories.length);
|
|
191
|
+
lastEntry.grade = gradeFromScore(lastEntry.overall);
|
|
192
|
+
lastEntry.timestamp = now.toISOString();
|
|
193
|
+
} else {
|
|
194
|
+
// New entry
|
|
195
|
+
history.push({
|
|
196
|
+
timestamp: now.toISOString(),
|
|
197
|
+
overall: results.overall,
|
|
198
|
+
grade: results.grade,
|
|
199
|
+
directories: [dirEntry],
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Trim to max entries
|
|
204
|
+
while (history.length > MAX_HISTORY_ENTRIES) {
|
|
205
|
+
history.shift();
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
writeJson(this.historyPath, { entries: history });
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// ---- Code Review (persisted per directory) ----
|
|
212
|
+
|
|
213
|
+
loadCodeReview(dirPath: string): { findings: Record<string, unknown>[]; summary: string; timestamp: string } | null {
|
|
214
|
+
const slug = slugify(dirPath);
|
|
215
|
+
const reviewPath = join(this.reportsDir, `${slug}-review.json`);
|
|
216
|
+
return readJson<{ findings: Record<string, unknown>[]; summary: string; timestamp: string } | null>(reviewPath, null);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
saveCodeReview(dirPath: string, findings: Record<string, unknown>[], summary: string): void {
|
|
220
|
+
const slug = slugify(dirPath);
|
|
221
|
+
const reviewPath = join(this.reportsDir, `${slug}-review.json`);
|
|
222
|
+
writeJson(reviewPath, { findings, summary, timestamp: new Date().toISOString() });
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// ---- Full state load ----
|
|
226
|
+
|
|
227
|
+
loadState(): QualityPersistedState {
|
|
228
|
+
const directories = this.loadConfig();
|
|
229
|
+
const reports = this.loadAllReports(directories);
|
|
230
|
+
const history = this.loadHistory();
|
|
231
|
+
|
|
232
|
+
// Merge persisted code reviews into reports
|
|
233
|
+
for (const dir of directories) {
|
|
234
|
+
const review = this.loadCodeReview(dir.path);
|
|
235
|
+
if (review && reports[dir.path]) {
|
|
236
|
+
reports[dir.path] = { ...reports[dir.path], codeReview: review.findings as unknown as QualityResults['codeReview'] };
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return { directories, reports, history };
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function gradeFromScore(score: number): string {
|
|
245
|
+
if (score >= 90) return 'A';
|
|
246
|
+
if (score >= 80) return 'B';
|
|
247
|
+
if (score >= 70) return 'C';
|
|
248
|
+
if (score >= 60) return 'D';
|
|
249
|
+
return 'F';
|
|
250
|
+
}
|