claude-flow 1.0.0 → 1.0.2
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 +9 -19
- package/bin/claude-flow +0 -0
- package/package.json +4 -4
- package/src/cli/cli-core.ts +1 -1
- package/bin/claude-flow-simple +0 -0
- package/bin/claude-flow-typecheck +0 -0
- package/scripts/check-links.ts +0 -274
- package/scripts/check-performance-regression.ts +0 -168
- package/scripts/claude-sparc.sh +0 -562
- package/scripts/coverage-report.ts +0 -692
- package/scripts/demo-task-system.ts +0 -224
- package/scripts/test-batch-tasks.ts +0 -29
- package/scripts/test-coordination-features.ts +0 -238
- package/scripts/test-mcp.ts +0 -251
- package/scripts/test-runner.ts +0 -571
- package/scripts/validate-examples.ts +0 -288
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# 🌊 Claude-Flow: Agent Orchestration Platform for Claude-Code
|
|
2
2
|
|
|
3
3
|
<div align="center">
|
|
4
4
|
|
|
@@ -12,9 +12,6 @@
|
|
|
12
12
|
|
|
13
13
|
</div>
|
|
14
14
|
|
|
15
|
-
<div align="center">
|
|
16
|
-
<img src="https://github.com/ruvnet/claude-code-flow/raw/main/assets/claude-flow-banner.png" alt="Claude-Flow Banner" width="800" />
|
|
17
|
-
</div>
|
|
18
15
|
|
|
19
16
|
## 🎯 **Transform Your Development Workflow**
|
|
20
17
|
|
|
@@ -31,14 +28,7 @@
|
|
|
31
28
|
- **🔒 Enterprise Ready**: Production-grade security, monitoring, and scaling
|
|
32
29
|
- **🌐 MCP Compatible**: Full Model Context Protocol support for tool integration
|
|
33
30
|
|
|
34
|
-
##
|
|
35
|
-
|
|
36
|
-
<div align="center">
|
|
37
|
-
|
|
38
|
-
[](https://youtu.be/demo-video)
|
|
39
|
-
[](https://demo.claude-flow.dev)
|
|
40
|
-
|
|
41
|
-
</div>
|
|
31
|
+
## 📦 **Installation**
|
|
42
32
|
|
|
43
33
|
```bash
|
|
44
34
|
# 🚀 Get started in 30 seconds
|
|
@@ -46,17 +36,17 @@ npx claude-flow init
|
|
|
46
36
|
npx claude-flow start
|
|
47
37
|
|
|
48
38
|
# 🤖 Spawn a research team
|
|
49
|
-
claude-flow agent spawn researcher --name "Senior Researcher"
|
|
50
|
-
claude-flow agent spawn analyst --name "Data Analyst"
|
|
51
|
-
claude-flow agent spawn implementer --name "Code Developer"
|
|
39
|
+
npx claude-flow agent spawn researcher --name "Senior Researcher"
|
|
40
|
+
npx claude-flow agent spawn analyst --name "Data Analyst"
|
|
41
|
+
npx claude-flow agent spawn implementer --name "Code Developer"
|
|
52
42
|
|
|
53
43
|
# 📋 Create and execute tasks
|
|
54
|
-
claude-flow task create research "Research AI optimization techniques"
|
|
55
|
-
claude-flow task list
|
|
44
|
+
npx claude-flow task create research "Research AI optimization techniques"
|
|
45
|
+
npx claude-flow task list
|
|
56
46
|
|
|
57
47
|
# 📊 Monitor in real-time
|
|
58
|
-
claude-flow status
|
|
59
|
-
claude-flow monitor
|
|
48
|
+
npx claude-flow status
|
|
49
|
+
npx claude-flow monitor
|
|
60
50
|
```
|
|
61
51
|
|
|
62
52
|
## 🏗️ **Core Features**
|
package/bin/claude-flow
CHANGED
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-flow",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "Advanced AI agent orchestration system for Claude",
|
|
3
|
+
"version": "1.0.2",
|
|
4
|
+
"description": "Advanced AI agent orchestration system for Claude Code",
|
|
5
5
|
"main": "src/cli/main.ts",
|
|
6
6
|
"bin": {
|
|
7
7
|
"claude-flow": "./bin/claude-flow"
|
|
@@ -35,9 +35,9 @@
|
|
|
35
35
|
"node": ">=16.0.0"
|
|
36
36
|
},
|
|
37
37
|
"files": [
|
|
38
|
-
"bin/",
|
|
38
|
+
"bin/claude-flow",
|
|
39
39
|
"src/",
|
|
40
|
-
"scripts/",
|
|
40
|
+
"scripts/install.js",
|
|
41
41
|
"README.md",
|
|
42
42
|
"LICENSE",
|
|
43
43
|
"deno.json"
|
package/src/cli/cli-core.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { red, green, yellow, blue, bold, cyan } from "https://deno.land/std@0.22
|
|
|
8
8
|
import { ensureDir } from "https://deno.land/std@0.224.0/fs/mod.ts";
|
|
9
9
|
import { join } from "https://deno.land/std@0.224.0/path/mod.ts";
|
|
10
10
|
|
|
11
|
-
export const VERSION = "1.0.
|
|
11
|
+
export const VERSION = "1.0.1";
|
|
12
12
|
|
|
13
13
|
interface CommandContext {
|
|
14
14
|
args: string[];
|
package/bin/claude-flow-simple
DELETED
|
Binary file
|
|
Binary file
|
package/scripts/check-links.ts
DELETED
|
@@ -1,274 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env deno run --allow-net --allow-read
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Documentation Link Checker
|
|
5
|
-
* Scans documentation files for broken links
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { walk } from "https://deno.land/std@0.220.0/fs/mod.ts";
|
|
9
|
-
|
|
10
|
-
interface LinkCheckResult {
|
|
11
|
-
file: string;
|
|
12
|
-
url: string;
|
|
13
|
-
status: 'ok' | 'broken' | 'timeout' | 'error';
|
|
14
|
-
statusCode?: number;
|
|
15
|
-
error?: string;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
interface ScanResult {
|
|
19
|
-
totalFiles: number;
|
|
20
|
-
totalLinks: number;
|
|
21
|
-
brokenLinks: LinkCheckResult[];
|
|
22
|
-
timeouts: LinkCheckResult[];
|
|
23
|
-
errors: LinkCheckResult[];
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const TIMEOUT_MS = 10000; // 10 seconds
|
|
27
|
-
const USER_AGENT = 'Claude-Flow Link Checker';
|
|
28
|
-
const MAX_CONCURRENT = 10;
|
|
29
|
-
|
|
30
|
-
// Links to skip (known to have issues with automated checking)
|
|
31
|
-
const SKIP_URLS = new Set([
|
|
32
|
-
'mailto:',
|
|
33
|
-
'tel:',
|
|
34
|
-
'javascript:',
|
|
35
|
-
'#',
|
|
36
|
-
'localhost',
|
|
37
|
-
'127.0.0.1',
|
|
38
|
-
'example.com',
|
|
39
|
-
'example.org',
|
|
40
|
-
]);
|
|
41
|
-
|
|
42
|
-
function extractLinks(content: string): string[] {
|
|
43
|
-
const links: string[] = [];
|
|
44
|
-
|
|
45
|
-
// Markdown links: [text](url)
|
|
46
|
-
const markdownLinkRegex = /\[([^\]]*)\]\(([^)]+)\)/g;
|
|
47
|
-
let match;
|
|
48
|
-
while ((match = markdownLinkRegex.exec(content)) !== null) {
|
|
49
|
-
links.push(match[2]);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// HTML links: <a href="url">
|
|
53
|
-
const htmlLinkRegex = /<a[^>]+href\s*=\s*['""]([^'""]+)['""][^>]*>/gi;
|
|
54
|
-
while ((match = htmlLinkRegex.exec(content)) !== null) {
|
|
55
|
-
links.push(match[1]);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// URL references: [ref]: url
|
|
59
|
-
const refLinkRegex = /^\s*\[([^\]]+)\]:\s*(.+)$/gm;
|
|
60
|
-
while ((match = refLinkRegex.exec(content)) !== null) {
|
|
61
|
-
links.push(match[2]);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return links;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function shouldSkipUrl(url: string): boolean {
|
|
68
|
-
// Skip relative links
|
|
69
|
-
if (!url.startsWith('http://') && !url.startsWith('https://')) {
|
|
70
|
-
return true;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Skip specific patterns
|
|
74
|
-
for (const skipPattern of SKIP_URLS) {
|
|
75
|
-
if (url.includes(skipPattern)) {
|
|
76
|
-
return true;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
return false;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
async function checkLink(url: string): Promise<{ status: number | null; error?: string }> {
|
|
84
|
-
try {
|
|
85
|
-
const controller = new AbortController();
|
|
86
|
-
const timeoutId = setTimeout(() => controller.abort(), TIMEOUT_MS);
|
|
87
|
-
|
|
88
|
-
const response = await fetch(url, {
|
|
89
|
-
method: 'HEAD', // Use HEAD to avoid downloading content
|
|
90
|
-
signal: controller.signal,
|
|
91
|
-
headers: {
|
|
92
|
-
'User-Agent': USER_AGENT,
|
|
93
|
-
},
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
clearTimeout(timeoutId);
|
|
97
|
-
|
|
98
|
-
return {
|
|
99
|
-
status: response.status,
|
|
100
|
-
};
|
|
101
|
-
} catch (error) {
|
|
102
|
-
if (error instanceof DOMException && error.name === 'AbortError') {
|
|
103
|
-
return { status: null, error: 'timeout' };
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
return {
|
|
107
|
-
status: null,
|
|
108
|
-
error: error.message,
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
async function scanFile(filePath: string): Promise<LinkCheckResult[]> {
|
|
114
|
-
const results: LinkCheckResult[] = [];
|
|
115
|
-
|
|
116
|
-
try {
|
|
117
|
-
const content = await Deno.readTextFile(filePath);
|
|
118
|
-
const links = extractLinks(content);
|
|
119
|
-
|
|
120
|
-
// Remove duplicates and filter
|
|
121
|
-
const uniqueLinks = [...new Set(links)].filter(url => !shouldSkipUrl(url));
|
|
122
|
-
|
|
123
|
-
// Check links with concurrency control
|
|
124
|
-
const semaphore = new Array(MAX_CONCURRENT).fill(0);
|
|
125
|
-
const promises = uniqueLinks.map(async (url) => {
|
|
126
|
-
// Wait for available slot
|
|
127
|
-
await new Promise<void>((resolve) => {
|
|
128
|
-
const checkSlot = () => {
|
|
129
|
-
const index = semaphore.findIndex(slot => slot === 0);
|
|
130
|
-
if (index !== -1) {
|
|
131
|
-
semaphore[index] = 1;
|
|
132
|
-
resolve();
|
|
133
|
-
} else {
|
|
134
|
-
setTimeout(checkSlot, 100);
|
|
135
|
-
}
|
|
136
|
-
};
|
|
137
|
-
checkSlot();
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
try {
|
|
141
|
-
const { status, error } = await checkLink(url);
|
|
142
|
-
|
|
143
|
-
let resultStatus: LinkCheckResult['status'];
|
|
144
|
-
if (error === 'timeout') {
|
|
145
|
-
resultStatus = 'timeout';
|
|
146
|
-
} else if (error) {
|
|
147
|
-
resultStatus = 'error';
|
|
148
|
-
} else if (status && status >= 200 && status < 400) {
|
|
149
|
-
resultStatus = 'ok';
|
|
150
|
-
} else {
|
|
151
|
-
resultStatus = 'broken';
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
return {
|
|
155
|
-
file: filePath,
|
|
156
|
-
url,
|
|
157
|
-
status: resultStatus,
|
|
158
|
-
statusCode: status || undefined,
|
|
159
|
-
error,
|
|
160
|
-
};
|
|
161
|
-
} finally {
|
|
162
|
-
// Release slot
|
|
163
|
-
const index = semaphore.findIndex(slot => slot === 1);
|
|
164
|
-
if (index !== -1) {
|
|
165
|
-
semaphore[index] = 0;
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
results.push(...await Promise.all(promises));
|
|
171
|
-
} catch (error) {
|
|
172
|
-
console.warn(`Failed to scan ${filePath}: ${error.message}`);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
return results;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
async function main(): Promise<void> {
|
|
179
|
-
console.log('Checking documentation links...\n');
|
|
180
|
-
|
|
181
|
-
const results: LinkCheckResult[] = [];
|
|
182
|
-
let fileCount = 0;
|
|
183
|
-
|
|
184
|
-
// Scan markdown and HTML files
|
|
185
|
-
const extensions = ['.md', '.html', '.htm'];
|
|
186
|
-
const directories = ['./docs', './README.md', './examples'];
|
|
187
|
-
|
|
188
|
-
for (const dir of directories) {
|
|
189
|
-
try {
|
|
190
|
-
const stat = await Deno.stat(dir);
|
|
191
|
-
if (stat.isFile) {
|
|
192
|
-
// Single file
|
|
193
|
-
const fileResults = await scanFile(dir);
|
|
194
|
-
results.push(...fileResults);
|
|
195
|
-
fileCount++;
|
|
196
|
-
} else if (stat.isDirectory) {
|
|
197
|
-
// Directory
|
|
198
|
-
for await (const entry of walk(dir, { exts: extensions })) {
|
|
199
|
-
if (entry.isFile) {
|
|
200
|
-
const fileResults = await scanFile(entry.path);
|
|
201
|
-
results.push(...fileResults);
|
|
202
|
-
fileCount++;
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
} catch (error) {
|
|
207
|
-
if (!(error instanceof Deno.errors.NotFound)) {
|
|
208
|
-
console.warn(`Failed to process ${dir}: ${error.message}`);
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// Analyze results
|
|
214
|
-
const scanResult: ScanResult = {
|
|
215
|
-
totalFiles: fileCount,
|
|
216
|
-
totalLinks: results.length,
|
|
217
|
-
brokenLinks: results.filter(r => r.status === 'broken'),
|
|
218
|
-
timeouts: results.filter(r => r.status === 'timeout'),
|
|
219
|
-
errors: results.filter(r => r.status === 'error'),
|
|
220
|
-
};
|
|
221
|
-
|
|
222
|
-
// Report results
|
|
223
|
-
console.log(`📊 Scan Summary:`);
|
|
224
|
-
console.log(` Files scanned: ${scanResult.totalFiles}`);
|
|
225
|
-
console.log(` Links checked: ${scanResult.totalLinks}`);
|
|
226
|
-
console.log(` Broken links: ${scanResult.brokenLinks.length}`);
|
|
227
|
-
console.log(` Timeouts: ${scanResult.timeouts.length}`);
|
|
228
|
-
console.log(` Errors: ${scanResult.errors.length}\n`);
|
|
229
|
-
|
|
230
|
-
// Report broken links
|
|
231
|
-
if (scanResult.brokenLinks.length > 0) {
|
|
232
|
-
console.log('❌ Broken Links:');
|
|
233
|
-
for (const result of scanResult.brokenLinks) {
|
|
234
|
-
console.log(` ${result.file}: ${result.url} (${result.statusCode})`);
|
|
235
|
-
}
|
|
236
|
-
console.log('');
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// Report timeouts
|
|
240
|
-
if (scanResult.timeouts.length > 0) {
|
|
241
|
-
console.log('⏱️ Timeouts:');
|
|
242
|
-
for (const result of scanResult.timeouts) {
|
|
243
|
-
console.log(` ${result.file}: ${result.url}`);
|
|
244
|
-
}
|
|
245
|
-
console.log('');
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
// Report other errors
|
|
249
|
-
if (scanResult.errors.length > 0) {
|
|
250
|
-
console.log('⚠️ Errors:');
|
|
251
|
-
for (const result of scanResult.errors) {
|
|
252
|
-
console.log(` ${result.file}: ${result.url} (${result.error})`);
|
|
253
|
-
}
|
|
254
|
-
console.log('');
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
// Summary
|
|
258
|
-
const totalIssues = scanResult.brokenLinks.length + scanResult.timeouts.length + scanResult.errors.length;
|
|
259
|
-
|
|
260
|
-
if (totalIssues === 0) {
|
|
261
|
-
console.log('✅ All links are working!');
|
|
262
|
-
} else {
|
|
263
|
-
console.error(`❌ Found ${totalIssues} link issues!`);
|
|
264
|
-
|
|
265
|
-
// Don't fail CI for timeouts or minor errors, only broken links
|
|
266
|
-
if (scanResult.brokenLinks.length > 0) {
|
|
267
|
-
Deno.exit(1);
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
if (import.meta.main) {
|
|
273
|
-
await main();
|
|
274
|
-
}
|
|
@@ -1,168 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env deno run --allow-read --allow-write
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Performance Regression Checker
|
|
5
|
-
* Compares current performance metrics against baseline
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
interface PerformanceMetric {
|
|
9
|
-
name: string;
|
|
10
|
-
value: number;
|
|
11
|
-
unit: string;
|
|
12
|
-
threshold: number; // Maximum allowed regression percentage
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
interface PerformanceReport {
|
|
16
|
-
timestamp: string;
|
|
17
|
-
gitHash: string;
|
|
18
|
-
metrics: PerformanceMetric[];
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const BASELINE_FILE = 'performance-baseline.json';
|
|
22
|
-
const CURRENT_RESULTS_FILE = 'performance-results.json';
|
|
23
|
-
const REGRESSION_THRESHOLD = 20; // 20% regression threshold
|
|
24
|
-
|
|
25
|
-
async function loadBaseline(): Promise<PerformanceReport | null> {
|
|
26
|
-
try {
|
|
27
|
-
const baselineData = await Deno.readTextFile(BASELINE_FILE);
|
|
28
|
-
return JSON.parse(baselineData);
|
|
29
|
-
} catch (error) {
|
|
30
|
-
if (error instanceof Deno.errors.NotFound) {
|
|
31
|
-
console.log('No baseline found, creating initial baseline...');
|
|
32
|
-
return null;
|
|
33
|
-
}
|
|
34
|
-
throw error;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
async function loadCurrentResults(): Promise<PerformanceReport> {
|
|
39
|
-
try {
|
|
40
|
-
const currentData = await Deno.readTextFile(CURRENT_RESULTS_FILE);
|
|
41
|
-
return JSON.parse(currentData);
|
|
42
|
-
} catch (error) {
|
|
43
|
-
console.error('Failed to load current performance results:', error.message);
|
|
44
|
-
Deno.exit(1);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
async function saveBaseline(report: PerformanceReport): Promise<void> {
|
|
49
|
-
await Deno.writeTextFile(BASELINE_FILE, JSON.stringify(report, null, 2));
|
|
50
|
-
console.log('Baseline updated successfully');
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function calculateRegression(baseline: number, current: number): number {
|
|
54
|
-
return ((current - baseline) / baseline) * 100;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function checkRegressions(baseline: PerformanceReport, current: PerformanceReport): {
|
|
58
|
-
regressions: Array<{ metric: string; regression: number; threshold: number }>;
|
|
59
|
-
hasRegressions: boolean;
|
|
60
|
-
} {
|
|
61
|
-
const regressions: Array<{ metric: string; regression: number; threshold: number }> = [];
|
|
62
|
-
|
|
63
|
-
for (const currentMetric of current.metrics) {
|
|
64
|
-
const baselineMetric = baseline.metrics.find(m => m.name === currentMetric.name);
|
|
65
|
-
|
|
66
|
-
if (!baselineMetric) {
|
|
67
|
-
console.log(`New metric detected: ${currentMetric.name}`);
|
|
68
|
-
continue;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const regression = calculateRegression(baselineMetric.value, currentMetric.value);
|
|
72
|
-
const threshold = currentMetric.threshold || REGRESSION_THRESHOLD;
|
|
73
|
-
|
|
74
|
-
if (regression > threshold) {
|
|
75
|
-
regressions.push({
|
|
76
|
-
metric: currentMetric.name,
|
|
77
|
-
regression,
|
|
78
|
-
threshold,
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return {
|
|
84
|
-
regressions,
|
|
85
|
-
hasRegressions: regressions.length > 0,
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function generateReport(
|
|
90
|
-
baseline: PerformanceReport,
|
|
91
|
-
current: PerformanceReport,
|
|
92
|
-
regressions: Array<{ metric: string; regression: number; threshold: number }>
|
|
93
|
-
): void {
|
|
94
|
-
console.log('\n=== Performance Regression Report ===\n');
|
|
95
|
-
|
|
96
|
-
console.log(`Baseline: ${baseline.timestamp} (${baseline.gitHash})`);
|
|
97
|
-
console.log(`Current: ${current.timestamp} (${current.gitHash})\n`);
|
|
98
|
-
|
|
99
|
-
if (regressions.length === 0) {
|
|
100
|
-
console.log('✅ No performance regressions detected!\n');
|
|
101
|
-
} else {
|
|
102
|
-
console.log('❌ Performance regressions detected:\n');
|
|
103
|
-
|
|
104
|
-
for (const regression of regressions) {
|
|
105
|
-
const baselineMetric = baseline.metrics.find(m => m.name === regression.metric)!;
|
|
106
|
-
const currentMetric = current.metrics.find(m => m.name === regression.metric)!;
|
|
107
|
-
|
|
108
|
-
console.log(` ${regression.metric}:`);
|
|
109
|
-
console.log(` Baseline: ${baselineMetric.value} ${baselineMetric.unit}`);
|
|
110
|
-
console.log(` Current: ${currentMetric.value} ${currentMetric.unit}`);
|
|
111
|
-
console.log(` Regression: ${regression.regression.toFixed(2)}% (threshold: ${regression.threshold}%)`);
|
|
112
|
-
console.log('');
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// Show all metrics for reference
|
|
117
|
-
console.log('📊 All Performance Metrics:\n');
|
|
118
|
-
|
|
119
|
-
for (const currentMetric of current.metrics) {
|
|
120
|
-
const baselineMetric = baseline.metrics.find(m => m.name === currentMetric.name);
|
|
121
|
-
|
|
122
|
-
if (baselineMetric) {
|
|
123
|
-
const regression = calculateRegression(baselineMetric.value, currentMetric.value);
|
|
124
|
-
const status = regression > (currentMetric.threshold || REGRESSION_THRESHOLD) ? '❌' : '✅';
|
|
125
|
-
|
|
126
|
-
console.log(` ${status} ${currentMetric.name}: ${currentMetric.value} ${currentMetric.unit} (${regression > 0 ? '+' : ''}${regression.toFixed(2)}%)`);
|
|
127
|
-
} else {
|
|
128
|
-
console.log(` 🆕 ${currentMetric.name}: ${currentMetric.value} ${currentMetric.unit} (new metric)`);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
console.log('');
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
async function main(): Promise<void> {
|
|
136
|
-
console.log('Checking for performance regressions...\n');
|
|
137
|
-
|
|
138
|
-
const baseline = await loadBaseline();
|
|
139
|
-
const current = await loadCurrentResults();
|
|
140
|
-
|
|
141
|
-
if (!baseline) {
|
|
142
|
-
console.log('No baseline found, establishing current results as baseline...');
|
|
143
|
-
await saveBaseline(current);
|
|
144
|
-
console.log('Baseline established successfully');
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
const { regressions, hasRegressions } = checkRegressions(baseline, current);
|
|
149
|
-
|
|
150
|
-
generateReport(baseline, current, regressions);
|
|
151
|
-
|
|
152
|
-
if (hasRegressions) {
|
|
153
|
-
console.error('Performance regressions detected! Please investigate and fix before merging.');
|
|
154
|
-
Deno.exit(1);
|
|
155
|
-
} else {
|
|
156
|
-
console.log('Performance check passed! 🎉');
|
|
157
|
-
|
|
158
|
-
// Update baseline if this is a main branch build
|
|
159
|
-
const branch = Deno.env.get('GITHUB_REF');
|
|
160
|
-
if (branch === 'refs/heads/main') {
|
|
161
|
-
await saveBaseline(current);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
if (import.meta.main) {
|
|
167
|
-
await main();
|
|
168
|
-
}
|