edgegate-mcp 0.2.0 → 0.2.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 +5 -2
- package/dist/server.js +9 -0
- package/dist/server.js.map +1 -1
- package/dist/tools/export_run_report.d.ts +35 -0
- package/dist/tools/export_run_report.js +327 -0
- package/dist/tools/export_run_report.js.map +1 -0
- package/dist/version.d.ts +2 -2
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/plugin.json +5 -3
- package/skills/edgegate-compare.md +31 -0
- package/skills/edgegate-export.md +34 -0
- package/skills/edgegate-status.md +4 -0
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ MCP server for [EdgeGate](https://edgegate.frozo.ai) — set up edge-AI regressi
|
|
|
4
4
|
|
|
5
5
|
## What does it do?
|
|
6
6
|
|
|
7
|
-
EdgeGate runs AI model regression tests on real Snapdragon hardware via Qualcomm AI Hub, then produces signed evidence bundles you can attach to CI gates. This npm package exposes EdgeGate's REST API as
|
|
7
|
+
EdgeGate runs AI model regression tests on real Snapdragon hardware via Qualcomm AI Hub, then produces signed evidence bundles you can attach to CI gates. This npm package exposes EdgeGate's REST API as 9 MCP tools, plus 4 bundled skills, so you can drive the whole flow from a prompt:
|
|
8
8
|
|
|
9
9
|
```
|
|
10
10
|
> Use the edgegate MCP to set up a CI gate for my MobileNet ONNX model.
|
|
@@ -68,6 +68,7 @@ See [docs/tools.md](./docs/tools.md) for the full tool reference. Quick list:
|
|
|
68
68
|
| `edgegate_get_audit_report` | Fetch the signed audit PDF |
|
|
69
69
|
| `edgegate_setup_github_action` | Generate the GitHub Actions workflow + secret commands |
|
|
70
70
|
| `edgegate_compare_runs` | Diff two runs — gate flips, metric deltas, per-device breakdown, REGRESSION / IMPROVEMENT / NEUTRAL verdict |
|
|
71
|
+
| `edgegate_export_run_report` | Save a complete run report as a markdown file to disk (returns the file path + preview) |
|
|
71
72
|
|
|
72
73
|
## Skills
|
|
73
74
|
|
|
@@ -76,7 +77,9 @@ Slash commands you can invoke directly:
|
|
|
76
77
|
- `/edgegate-init` — full onboarding flow (zero → CI gate)
|
|
77
78
|
- `/edgegate-gate` — trigger a run on an existing pipeline
|
|
78
79
|
- `/edgegate-status` — check a run's status + metrics
|
|
79
|
-
- `/edgegate-audit` — fetch the
|
|
80
|
+
- `/edgegate-audit` — fetch the evidence bundle for a run
|
|
81
|
+
- `/edgegate-compare` — diff two runs (auto-baseline) with REGRESSION/IMPROVEMENT/NEUTRAL verdict
|
|
82
|
+
- `/edgegate-export` — save a run report as a markdown file (for PR comments, Slack, compliance)
|
|
80
83
|
|
|
81
84
|
## License
|
|
82
85
|
|
package/dist/server.js
CHANGED
|
@@ -12,6 +12,7 @@ import { getReportHandler, getReportInputSchema } from "./tools/get_report.js";
|
|
|
12
12
|
import { getAuditReportHandler, getAuditReportInputSchema } from "./tools/get_audit_report.js";
|
|
13
13
|
import { setupGithubActionHandler, setupGithubActionInputSchema, } from "./tools/setup_github_action.js";
|
|
14
14
|
import { compareRunsHandler, compareRunsInputSchema, } from "./tools/compare_runs.js";
|
|
15
|
+
import { exportRunReportHandler, exportRunReportInputSchema, } from "./tools/export_run_report.js";
|
|
15
16
|
const TOOLS = [
|
|
16
17
|
{
|
|
17
18
|
name: "edgegate_setup_workspace",
|
|
@@ -70,6 +71,14 @@ const TOOLS = [
|
|
|
70
71
|
schema: compareRunsInputSchema,
|
|
71
72
|
handler: compareRunsHandler,
|
|
72
73
|
},
|
|
74
|
+
{
|
|
75
|
+
name: "edgegate_export_run_report",
|
|
76
|
+
description: "Download a human-readable markdown report for an EdgeGate run and save it to disk. " +
|
|
77
|
+
"Returns the absolute file path plus a preview of the first 30 lines. " +
|
|
78
|
+
"Optionally includes a run-vs-baseline diff section (include_diff=true).",
|
|
79
|
+
schema: exportRunReportInputSchema,
|
|
80
|
+
handler: exportRunReportHandler,
|
|
81
|
+
},
|
|
73
82
|
];
|
|
74
83
|
function getClient() {
|
|
75
84
|
const apiUrl = process.env.EDGEGATE_API_URL ?? "https://edgegateapi.frozo.ai";
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EACL,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,OAAO,EAAE,qBAAqB,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AAC9F,OAAO,EAAE,qBAAqB,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AAC9F,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzE,OAAO,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AACrF,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAC/E,OAAO,EAAE,qBAAqB,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AAC/F,OAAO,EACL,wBAAwB,EACxB,4BAA4B,GAC7B,MAAM,gCAAgC,CAAC;AACxC,OAAO,EACL,kBAAkB,EAClB,sBAAsB,GACvB,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EACL,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,OAAO,EAAE,qBAAqB,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AAC9F,OAAO,EAAE,qBAAqB,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AAC9F,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzE,OAAO,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AACrF,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAC/E,OAAO,EAAE,qBAAqB,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AAC/F,OAAO,EACL,wBAAwB,EACxB,4BAA4B,GAC7B,MAAM,gCAAgC,CAAC;AACxC,OAAO,EACL,kBAAkB,EAClB,sBAAsB,GACvB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,sBAAsB,EACtB,0BAA0B,GAC3B,MAAM,8BAA8B,CAAC;AAEtC,MAAM,KAAK,GAAG;IACZ;QACE,IAAI,EAAE,0BAA0B;QAChC,WAAW,EACT,6EAA6E;YAC7E,mFAAmF;QACrF,MAAM,EAAE,yBAAyB;QACjC,OAAO,EAAE,qBAAqB;KAC/B;IACD;QACE,IAAI,EAAE,0BAA0B;QAChC,WAAW,EACT,qFAAqF;YACrF,0EAA0E;QAC5E,MAAM,EAAE,yBAAyB;QACjC,OAAO,EAAE,qBAAqB;KAC/B;IACD;QACE,IAAI,EAAE,mBAAmB;QACzB,WAAW,EACT,iFAAiF;YACjF,wBAAwB;QAC1B,MAAM,EAAE,kBAAkB;QAC1B,OAAO,EAAE,cAAc;KACxB;IACD;QACE,IAAI,EAAE,uBAAuB;QAC7B,WAAW,EACT,8EAA8E;YAC9E,+BAA+B;QACjC,MAAM,EAAE,sBAAsB;QAC9B,OAAO,EAAE,kBAAkB;KAC5B;IACD;QACE,IAAI,EAAE,qBAAqB;QAC3B,WAAW,EAAE,8EAA8E;QAC3F,MAAM,EAAE,oBAAoB;QAC5B,OAAO,EAAE,gBAAgB;KAC1B;IACD;QACE,IAAI,EAAE,2BAA2B;QACjC,WAAW,EACT,6EAA6E;YAC7E,qBAAqB;QACvB,MAAM,EAAE,yBAAyB;QACjC,OAAO,EAAE,qBAAqB;KAC/B;IACD;QACE,IAAI,EAAE,8BAA8B;QACpC,WAAW,EACT,kFAAkF;YAClF,wBAAwB;QAC1B,MAAM,EAAE,4BAA4B;QACpC,OAAO,EAAE,wBAAwB;KAClC;IACD;QACE,IAAI,EAAE,uBAAuB;QAC7B,WAAW,EACT,2FAA2F;YAC3F,gGAAgG;YAChG,wFAAwF;YACxF,oDAAoD;QACtD,MAAM,EAAE,sBAAsB;QAC9B,OAAO,EAAE,kBAAkB;KAC5B;IACD;QACE,IAAI,EAAE,4BAA4B;QAClC,WAAW,EACT,qFAAqF;YACrF,uEAAuE;YACvE,yEAAyE;QAC3E,MAAM,EAAE,0BAA0B;QAClC,OAAO,EAAE,sBAAsB;KAChC;CACO,CAAC;AAEX,SAAS,SAAS;IAChB,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,8BAA8B,CAAC;IAC9E,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC;IAC5C,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CACb,4EAA4E;YAC1E,oEAAoE,CACvE,CAAC;IACJ,CAAC;IACD,OAAO,IAAI,cAAc,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;AAChD,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,MAAM,GAAG,IAAI,MAAM,CACvB,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,OAAO,EAAE,EAC1C,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,CAChC,CAAC;IAEF,MAAM,CAAC,iBAAiB,CAAC,sBAAsB,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC;QAC5D,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACvB,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,WAAW,EAAE,CAAC,CAAC,WAAW;YAC1B,WAAW,EAAE,eAAe,CAAC,CAAC,CAAC,MAAM,CAA4B;SAClE,CAAC,CAAC;KACJ,CAAC,CAAC,CAAC;IAEJ,MAAM,CAAC,iBAAiB,CAAC,qBAAqB,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QAC5D,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAC3D,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,OAAO;gBACL,OAAO,EAAE,IAAI;gBACb,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;aACtE,CAAC;QACJ,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC;QACjE,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACpB,OAAO;gBACL,OAAO,EAAE,IAAI;gBACb,OAAO,EAAE;oBACP,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,yBAAyB,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE;iBACtF;aACF,CAAC;QACJ,CAAC;QACD,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;QAC3B,8DAA8D;QAC9D,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,IAAW,CAAQ,CAAC;IACzD,CAAC,CAAC,CAAC;IAEH,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAChC,sCAAsC;IACtC,OAAO,CAAC,KAAK,CAAC,gBAAgB,OAAO,qBAAqB,CAAC,CAAC;AAC9D,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,sCAAsC;IACtC,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IAC7B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* edgegate_export_run_report — render a human-readable markdown report for a
|
|
3
|
+
* completed (or in-flight) EdgeGate run and write it to disk.
|
|
4
|
+
*
|
|
5
|
+
* Steps:
|
|
6
|
+
* 1. Fetch run detail via getRun
|
|
7
|
+
* 2. If the run is completed, fetch the evidence bundle via getRunBundle
|
|
8
|
+
* 3. Optionally fetch the run diff via getRunDiff (when include_diff=true)
|
|
9
|
+
* 4. Render a comprehensive markdown report
|
|
10
|
+
* 5. Write the file to disk (creating parent directories as needed)
|
|
11
|
+
* 6. Return the absolute file path + first ~30 lines of the report
|
|
12
|
+
*/
|
|
13
|
+
import { z } from "zod";
|
|
14
|
+
import { tmpdir } from "node:os";
|
|
15
|
+
import { EdgeGateClient } from "../client.js";
|
|
16
|
+
import type { ToolResult } from "./setup_workspace.js";
|
|
17
|
+
export declare const exportRunReportInputSchema: z.ZodObject<{
|
|
18
|
+
workspace_id: z.ZodString;
|
|
19
|
+
run_id: z.ZodString;
|
|
20
|
+
output_path: z.ZodOptional<z.ZodString>;
|
|
21
|
+
include_diff: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
22
|
+
}, "strip", z.ZodTypeAny, {
|
|
23
|
+
workspace_id: string;
|
|
24
|
+
run_id: string;
|
|
25
|
+
include_diff: boolean;
|
|
26
|
+
output_path?: string | undefined;
|
|
27
|
+
}, {
|
|
28
|
+
workspace_id: string;
|
|
29
|
+
run_id: string;
|
|
30
|
+
output_path?: string | undefined;
|
|
31
|
+
include_diff?: boolean | undefined;
|
|
32
|
+
}>;
|
|
33
|
+
export type ExportRunReportInput = z.infer<typeof exportRunReportInputSchema>;
|
|
34
|
+
export declare function exportRunReportHandler(client: EdgeGateClient, input: ExportRunReportInput): Promise<ToolResult>;
|
|
35
|
+
export { tmpdir as _tmpdir };
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* edgegate_export_run_report — render a human-readable markdown report for a
|
|
3
|
+
* completed (or in-flight) EdgeGate run and write it to disk.
|
|
4
|
+
*
|
|
5
|
+
* Steps:
|
|
6
|
+
* 1. Fetch run detail via getRun
|
|
7
|
+
* 2. If the run is completed, fetch the evidence bundle via getRunBundle
|
|
8
|
+
* 3. Optionally fetch the run diff via getRunDiff (when include_diff=true)
|
|
9
|
+
* 4. Render a comprehensive markdown report
|
|
10
|
+
* 5. Write the file to disk (creating parent directories as needed)
|
|
11
|
+
* 6. Return the absolute file path + first ~30 lines of the report
|
|
12
|
+
*/
|
|
13
|
+
import { z } from "zod";
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
import { homedir, tmpdir } from "node:os";
|
|
16
|
+
import { mkdir, stat, writeFile } from "node:fs/promises";
|
|
17
|
+
import { EdgeGateError } from "../client.js";
|
|
18
|
+
import { VERSION } from "../version.js";
|
|
19
|
+
export const exportRunReportInputSchema = z.object({
|
|
20
|
+
workspace_id: z.string().uuid(),
|
|
21
|
+
run_id: z.string().uuid(),
|
|
22
|
+
output_path: z
|
|
23
|
+
.string()
|
|
24
|
+
.optional()
|
|
25
|
+
.describe("Where to write the markdown file. Defaults to `./edgegate-run-{id-short}.md` in the " +
|
|
26
|
+
"current working directory. If a directory, the file is named `edgegate-run-{id-short}.md` " +
|
|
27
|
+
"inside it. Supports `~` and relative paths."),
|
|
28
|
+
include_diff: z
|
|
29
|
+
.boolean()
|
|
30
|
+
.optional()
|
|
31
|
+
.default(false)
|
|
32
|
+
.describe("When true, also fetches the run-vs-baseline diff and appends a diff section to the report."),
|
|
33
|
+
});
|
|
34
|
+
// Terminal statuses that should have a bundle available
|
|
35
|
+
const TERMINAL_STATUSES = new Set(["passed", "failed", "error"]);
|
|
36
|
+
export async function exportRunReportHandler(client, input) {
|
|
37
|
+
try {
|
|
38
|
+
const { workspace_id, run_id, output_path, include_diff } = input;
|
|
39
|
+
// --- 1. Fetch run detail ---
|
|
40
|
+
let run;
|
|
41
|
+
try {
|
|
42
|
+
run = await client.getRun(workspace_id, run_id);
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
if (err instanceof EdgeGateError) {
|
|
46
|
+
return {
|
|
47
|
+
isError: true,
|
|
48
|
+
content: [
|
|
49
|
+
{
|
|
50
|
+
type: "text",
|
|
51
|
+
text: `Could not fetch run ${run_id}: ${err.detail}`,
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
throw err;
|
|
57
|
+
}
|
|
58
|
+
// --- 2. Fetch evidence bundle (only for terminal runs) ---
|
|
59
|
+
let bundle = null;
|
|
60
|
+
if (TERMINAL_STATUSES.has(run.status)) {
|
|
61
|
+
try {
|
|
62
|
+
bundle = await client.getRunBundle(workspace_id, run_id);
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
if (err instanceof EdgeGateError && (err.status === 404 || err.status === 409)) {
|
|
66
|
+
// Bundle not ready yet — proceed without it
|
|
67
|
+
bundle = null;
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
throw err;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// --- 3. Optionally fetch diff ---
|
|
75
|
+
let comparison = null;
|
|
76
|
+
if (include_diff) {
|
|
77
|
+
try {
|
|
78
|
+
comparison = await client.getRunDiff(workspace_id, run_id);
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
if (err instanceof EdgeGateError && err.status === 404) {
|
|
82
|
+
// No diff yet — silently omit the section
|
|
83
|
+
comparison = null;
|
|
84
|
+
}
|
|
85
|
+
else if (err instanceof EdgeGateError) {
|
|
86
|
+
// Non-fatal — skip the diff section but note it
|
|
87
|
+
comparison = null;
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
throw err;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// --- 4. Render markdown ---
|
|
95
|
+
const markdown = renderReport(run, bundle, comparison, workspace_id);
|
|
96
|
+
// --- 5. Resolve output path ---
|
|
97
|
+
const idShort = run_id.slice(0, 8);
|
|
98
|
+
const defaultFilename = `edgegate-run-${idShort}.md`;
|
|
99
|
+
const resolvedPath = await resolveOutputPath(output_path, defaultFilename);
|
|
100
|
+
// Ensure parent directory exists
|
|
101
|
+
const parent = resolvedPath.slice(0, resolvedPath.lastIndexOf("/"));
|
|
102
|
+
if (parent) {
|
|
103
|
+
await mkdir(parent, { recursive: true });
|
|
104
|
+
}
|
|
105
|
+
// --- 6. Write the file ---
|
|
106
|
+
await writeFile(resolvedPath, markdown, "utf8");
|
|
107
|
+
// --- 7. Return result ---
|
|
108
|
+
const previewLines = markdown.split("\n").slice(0, 30).join("\n");
|
|
109
|
+
const header = `Wrote run report to ${resolvedPath}\n\n`;
|
|
110
|
+
return {
|
|
111
|
+
content: [{ type: "text", text: header + previewLines }],
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
catch (err) {
|
|
115
|
+
if (err instanceof EdgeGateError) {
|
|
116
|
+
return {
|
|
117
|
+
isError: true,
|
|
118
|
+
content: [{ type: "text", text: `EdgeGate returned ${err.status}: ${err.detail}` }],
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
throw err;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
// ─── Path resolution ──────────────────────────────────────────────────────────
|
|
125
|
+
async function resolveOutputPath(outputPath, defaultFilename) {
|
|
126
|
+
if (!outputPath) {
|
|
127
|
+
// Default: CWD / edgegate-run-{id-short}.md
|
|
128
|
+
return join(process.cwd(), defaultFilename);
|
|
129
|
+
}
|
|
130
|
+
// Expand ~ to home dir
|
|
131
|
+
let expanded = outputPath;
|
|
132
|
+
if (expanded.startsWith("~/")) {
|
|
133
|
+
expanded = join(homedir(), expanded.slice(2));
|
|
134
|
+
}
|
|
135
|
+
else if (expanded === "~") {
|
|
136
|
+
expanded = homedir();
|
|
137
|
+
}
|
|
138
|
+
// Resolve relative to CWD
|
|
139
|
+
const resolved = expanded.startsWith("/") ? expanded : join(process.cwd(), expanded);
|
|
140
|
+
// Check if it's an existing directory
|
|
141
|
+
try {
|
|
142
|
+
const s = await stat(resolved);
|
|
143
|
+
if (s.isDirectory()) {
|
|
144
|
+
return join(resolved, defaultFilename);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
// Doesn't exist yet — treat as file path
|
|
149
|
+
}
|
|
150
|
+
return resolved;
|
|
151
|
+
}
|
|
152
|
+
// ─── Rendering ────────────────────────────────────────────────────────────────
|
|
153
|
+
function statusBadge(status) {
|
|
154
|
+
const upper = status.toUpperCase();
|
|
155
|
+
switch (upper) {
|
|
156
|
+
case "PASSED": return "**PASSED**";
|
|
157
|
+
case "FAILED": return "**FAILED**";
|
|
158
|
+
case "RUNNING": return "_RUNNING_";
|
|
159
|
+
case "PENDING": return "_PENDING_";
|
|
160
|
+
case "ERROR": return "**ERROR**";
|
|
161
|
+
default: return `_${upper}_`;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
function wallClock(run) {
|
|
165
|
+
if (!run.completed_at || !run.created_at)
|
|
166
|
+
return "(pending)";
|
|
167
|
+
const startMs = new Date(run.created_at).getTime();
|
|
168
|
+
const endMs = new Date(run.completed_at).getTime();
|
|
169
|
+
const totalS = Math.round((endMs - startMs) / 1000);
|
|
170
|
+
const m = Math.floor(totalS / 60);
|
|
171
|
+
const s = totalS % 60;
|
|
172
|
+
return m > 0 ? `${m}m ${s}s` : `${s}s`;
|
|
173
|
+
}
|
|
174
|
+
function renderReport(run, bundle, comparison, workspaceId) {
|
|
175
|
+
const lines = [];
|
|
176
|
+
// ── Header ──
|
|
177
|
+
lines.push(`# EdgeGate Run Report`);
|
|
178
|
+
lines.push(``);
|
|
179
|
+
lines.push(`**Run ID:** \`${run.id}\``);
|
|
180
|
+
lines.push(`**Pipeline:** ${run.pipeline_name} (\`${run.pipeline_id}\`)`);
|
|
181
|
+
lines.push(`**Status:** ${statusBadge(run.status)}`);
|
|
182
|
+
lines.push(`**Trigger:** ${run.trigger}`);
|
|
183
|
+
lines.push(`**Created:** ${run.created_at}`);
|
|
184
|
+
lines.push(`**Completed:** ${run.completed_at ?? "(in flight)"}`);
|
|
185
|
+
lines.push(`**Wall clock:** ${wallClock(run)}`);
|
|
186
|
+
lines.push(``);
|
|
187
|
+
// ── Model ──
|
|
188
|
+
if (run.model_artifact_id || run.hub_model_id || run.hub_job_id) {
|
|
189
|
+
lines.push(`## Model`);
|
|
190
|
+
if (run.model_artifact_id)
|
|
191
|
+
lines.push(`- Artifact ID: \`${run.model_artifact_id}\``);
|
|
192
|
+
if (run.model_filename)
|
|
193
|
+
lines.push(`- Filename: ${run.model_filename}`);
|
|
194
|
+
if (run.hub_model_id)
|
|
195
|
+
lines.push(`- AI Hub model: \`${run.hub_model_id}\``);
|
|
196
|
+
if (run.hub_job_id)
|
|
197
|
+
lines.push(`- AI Hub job: \`${run.hub_job_id}\``);
|
|
198
|
+
lines.push(``);
|
|
199
|
+
}
|
|
200
|
+
// ── In-flight guard ──
|
|
201
|
+
const isComplete = TERMINAL_STATUSES.has(run.status) && (bundle !== null || run.gates_eval !== null);
|
|
202
|
+
if (!isComplete) {
|
|
203
|
+
lines.push(`## Status`);
|
|
204
|
+
lines.push(``);
|
|
205
|
+
lines.push(`_Run not yet complete — check back._`);
|
|
206
|
+
lines.push(``);
|
|
207
|
+
if (run.error_code) {
|
|
208
|
+
lines.push(`**Error code:** ${run.error_code}`);
|
|
209
|
+
if (run.error_detail)
|
|
210
|
+
lines.push(`**Error detail:** ${run.error_detail}`);
|
|
211
|
+
lines.push(``);
|
|
212
|
+
}
|
|
213
|
+
appendFooter(lines);
|
|
214
|
+
if (comparison)
|
|
215
|
+
appendDiffSection(lines, comparison);
|
|
216
|
+
return lines.join("\n");
|
|
217
|
+
}
|
|
218
|
+
// ── Metrics ──
|
|
219
|
+
const metrics = bundle?.normalized_metrics ?? run.normalized_metrics;
|
|
220
|
+
if (metrics && Object.keys(metrics).length > 0) {
|
|
221
|
+
lines.push(`## Metrics`);
|
|
222
|
+
lines.push(`| Metric | Value |`);
|
|
223
|
+
lines.push(`|---|---|`);
|
|
224
|
+
for (const [k, v] of Object.entries(metrics)) {
|
|
225
|
+
lines.push(`| ${k} | ${v} |`);
|
|
226
|
+
}
|
|
227
|
+
lines.push(``);
|
|
228
|
+
}
|
|
229
|
+
// ── Gate Results ──
|
|
230
|
+
const gatesEval = bundle?.gates_eval ?? run.gates_eval;
|
|
231
|
+
if (gatesEval && gatesEval.gates.length > 0) {
|
|
232
|
+
lines.push(`## Gate Results`);
|
|
233
|
+
lines.push(`| Metric | Operator | Threshold | Actual | Status |`);
|
|
234
|
+
lines.push(`|---|---|---|---|---|`);
|
|
235
|
+
for (const gate of gatesEval.gates) {
|
|
236
|
+
lines.push(renderGateRow(gate));
|
|
237
|
+
}
|
|
238
|
+
lines.push(``);
|
|
239
|
+
lines.push(`**Overall verdict:** ${gatesEval.passed ? "**PASSED**" : "**FAILED**"}`);
|
|
240
|
+
lines.push(``);
|
|
241
|
+
}
|
|
242
|
+
// ── Error info ──
|
|
243
|
+
if (run.error_code) {
|
|
244
|
+
lines.push(`## Error`);
|
|
245
|
+
lines.push(`- Code: ${run.error_code}`);
|
|
246
|
+
if (run.error_detail)
|
|
247
|
+
lines.push(`- Detail: ${run.error_detail}`);
|
|
248
|
+
lines.push(``);
|
|
249
|
+
}
|
|
250
|
+
// ── Evidence Bundle ──
|
|
251
|
+
const bundleArtifactId = bundle?.bundle_artifact_id ?? run.bundle_artifact_id;
|
|
252
|
+
if (bundleArtifactId) {
|
|
253
|
+
lines.push(`## Evidence Bundle`);
|
|
254
|
+
lines.push(`- Bundle artifact ID: \`${bundleArtifactId}\``);
|
|
255
|
+
lines.push(`- Download via API: \`GET /v1/workspaces/${workspaceId}/bundles/${bundleArtifactId}/artifact-url\`` +
|
|
256
|
+
` (returns short-lived signed URL)`);
|
|
257
|
+
lines.push(``);
|
|
258
|
+
}
|
|
259
|
+
// ── Diff section ──
|
|
260
|
+
if (comparison) {
|
|
261
|
+
appendDiffSection(lines, comparison);
|
|
262
|
+
}
|
|
263
|
+
appendFooter(lines);
|
|
264
|
+
return lines.join("\n");
|
|
265
|
+
}
|
|
266
|
+
function renderGateRow(gate) {
|
|
267
|
+
const statusIcon = gate.passed ? "✓ PASSED" : "✗ FAILED";
|
|
268
|
+
return `| ${gate.metric} | ${gate.operator} | ${gate.threshold} | ${gate.actual_value} | ${statusIcon} |`;
|
|
269
|
+
}
|
|
270
|
+
function appendDiffSection(lines, comparison) {
|
|
271
|
+
const diff = comparison.diff;
|
|
272
|
+
lines.push(`## Run-vs-Baseline Diff`);
|
|
273
|
+
lines.push(``);
|
|
274
|
+
lines.push(`**Candidate:** \`${comparison.current_run_id}\` ` +
|
|
275
|
+
`(${diff.current_completed_at ?? "in flight"})`);
|
|
276
|
+
lines.push(`**Baseline:** \`${comparison.previous_run_id ?? "—"}\` ` +
|
|
277
|
+
`(${diff.previous_completed_at ?? "—"})`);
|
|
278
|
+
lines.push(``);
|
|
279
|
+
if (diff.is_baseline) {
|
|
280
|
+
lines.push(`> **NO BASELINE** — this is the first completed run in this pipeline.`);
|
|
281
|
+
lines.push(``);
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
// Metrics
|
|
285
|
+
const metricKeys = Object.keys(diff.metric_deltas).sort();
|
|
286
|
+
if (metricKeys.length > 0) {
|
|
287
|
+
lines.push(`### Metrics`);
|
|
288
|
+
lines.push(`| Metric | Baseline | Candidate | Delta |`);
|
|
289
|
+
lines.push(`|---|---|---|---|`);
|
|
290
|
+
for (const k of metricKeys) {
|
|
291
|
+
const m = diff.metric_deltas[k];
|
|
292
|
+
const pct = m.delta_pct !== null
|
|
293
|
+
? `${m.delta_pct > 0 ? "+" : ""}${m.delta_pct.toFixed(1)}%`
|
|
294
|
+
: "—";
|
|
295
|
+
const delta = m.delta !== null ? `${m.delta > 0 ? "+" : ""}${m.delta.toFixed(2)} (${pct})` : "—";
|
|
296
|
+
lines.push(`| ${k} | ${m.previous ?? "—"} | ${m.current ?? "—"} | ${delta} |`);
|
|
297
|
+
}
|
|
298
|
+
lines.push(``);
|
|
299
|
+
}
|
|
300
|
+
// Gate flips
|
|
301
|
+
if (diff.gate_flips.length > 0) {
|
|
302
|
+
lines.push(`### Gate Status`);
|
|
303
|
+
lines.push(`| Gate | Baseline | Candidate | Transition |`);
|
|
304
|
+
lines.push(`|---|---|---|---|`);
|
|
305
|
+
for (const gf of diff.gate_flips) {
|
|
306
|
+
const baseIcon = gf.previous?.passed === true ? "✓" : gf.previous?.passed === false ? "✗" : "—";
|
|
307
|
+
const candIcon = gf.current?.passed === true ? "✓" : gf.current?.passed === false ? "✗" : "—";
|
|
308
|
+
const label = gf.transition === "regressed" ? "**REGRESSION** ✓→✗"
|
|
309
|
+
: gf.transition === "improved" ? "RECOVERY ✗→✓"
|
|
310
|
+
: gf.transition;
|
|
311
|
+
lines.push(`| ${gf.metric} | ${baseIcon} | ${candIcon} | ${label} |`);
|
|
312
|
+
}
|
|
313
|
+
lines.push(``);
|
|
314
|
+
}
|
|
315
|
+
// Audit
|
|
316
|
+
if (comparison.diff_sha256) {
|
|
317
|
+
lines.push(`**Diff SHA-256:** \`${comparison.diff_sha256}\` (signed, embedded in evidence bundle)`);
|
|
318
|
+
lines.push(``);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
function appendFooter(lines) {
|
|
322
|
+
lines.push(`---`);
|
|
323
|
+
lines.push(`_Generated by edgegate-mcp@${VERSION} on ${new Date().toISOString()}_`);
|
|
324
|
+
}
|
|
325
|
+
// Export the tmpdir helper so tests can use the same default resolution logic
|
|
326
|
+
export { tmpdir as _tmpdir };
|
|
327
|
+
//# sourceMappingURL=export_run_report.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"export_run_report.js","sourceRoot":"","sources":["../../src/tools/export_run_report.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAC1C,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAkB,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7D,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAIxC,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,CAAC,MAAM,CAAC;IACjD,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE;IAC/B,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE;IACzB,WAAW,EAAE,CAAC;SACX,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CACP,sFAAsF;QACpF,4FAA4F;QAC5F,6CAA6C,CAChD;IACH,YAAY,EAAE,CAAC;SACZ,OAAO,EAAE;SACT,QAAQ,EAAE;SACV,OAAO,CAAC,KAAK,CAAC;SACd,QAAQ,CACP,4FAA4F,CAC7F;CACJ,CAAC,CAAC;AAIH,wDAAwD;AACxD,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;AAEjE,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,MAAsB,EACtB,KAA2B;IAE3B,IAAI,CAAC;QACH,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,YAAY,EAAE,GAAG,KAAK,CAAC;QAElE,8BAA8B;QAC9B,IAAI,GAAc,CAAC;QACnB,IAAI,CAAC;YACH,GAAG,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;QAClD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,GAAG,YAAY,aAAa,EAAE,CAAC;gBACjC,OAAO;oBACL,OAAO,EAAE,IAAI;oBACb,OAAO,EAAE;wBACP;4BACE,IAAI,EAAE,MAAM;4BACZ,IAAI,EAAE,uBAAuB,MAAM,KAAK,GAAG,CAAC,MAAM,EAAE;yBACrD;qBACF;iBACF,CAAC;YACJ,CAAC;YACD,MAAM,GAAG,CAAC;QACZ,CAAC;QAED,4DAA4D;QAC5D,IAAI,MAAM,GAAqB,IAAI,CAAC;QACpC,IAAI,iBAAiB,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YACtC,IAAI,CAAC;gBACH,MAAM,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;YAC3D,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,GAAG,YAAY,aAAa,IAAI,CAAC,GAAG,CAAC,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,CAAC,EAAE,CAAC;oBAC/E,4CAA4C;oBAC5C,MAAM,GAAG,IAAI,CAAC;gBAChB,CAAC;qBAAM,CAAC;oBACN,MAAM,GAAG,CAAC;gBACZ,CAAC;YACH,CAAC;QACH,CAAC;QAED,mCAAmC;QACnC,IAAI,UAAU,GAAyB,IAAI,CAAC;QAC5C,IAAI,YAAY,EAAE,CAAC;YACjB,IAAI,CAAC;gBACH,UAAU,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;YAC7D,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,GAAG,YAAY,aAAa,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;oBACvD,0CAA0C;oBAC1C,UAAU,GAAG,IAAI,CAAC;gBACpB,CAAC;qBAAM,IAAI,GAAG,YAAY,aAAa,EAAE,CAAC;oBACxC,gDAAgD;oBAChD,UAAU,GAAG,IAAI,CAAC;gBACpB,CAAC;qBAAM,CAAC;oBACN,MAAM,GAAG,CAAC;gBACZ,CAAC;YACH,CAAC;QACH,CAAC;QAED,6BAA6B;QAC7B,MAAM,QAAQ,GAAG,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,YAAY,CAAC,CAAC;QAErE,iCAAiC;QACjC,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACnC,MAAM,eAAe,GAAG,gBAAgB,OAAO,KAAK,CAAC;QACrD,MAAM,YAAY,GAAG,MAAM,iBAAiB,CAAC,WAAW,EAAE,eAAe,CAAC,CAAC;QAE3E,iCAAiC;QACjC,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC,EAAE,YAAY,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC;QACpE,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,KAAK,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC3C,CAAC;QAED,4BAA4B;QAC5B,MAAM,SAAS,CAAC,YAAY,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;QAEhD,2BAA2B;QAC3B,MAAM,YAAY,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClE,MAAM,MAAM,GAAG,uBAAuB,YAAY,MAAM,CAAC;QACzD,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,YAAY,EAAE,CAAC;SACzD,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,aAAa,EAAE,CAAC;YACjC,OAAO;gBACL,OAAO,EAAE,IAAI;gBACb,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,qBAAqB,GAAG,CAAC,MAAM,KAAK,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC;aACpF,CAAC;QACJ,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC;AAED,iFAAiF;AAEjF,KAAK,UAAU,iBAAiB,CAC9B,UAA8B,EAC9B,eAAuB;IAEvB,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,4CAA4C;QAC5C,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,eAAe,CAAC,CAAC;IAC9C,CAAC;IAED,uBAAuB;IACvB,IAAI,QAAQ,GAAG,UAAU,CAAC;IAC1B,IAAI,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QAC9B,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAChD,CAAC;SAAM,IAAI,QAAQ,KAAK,GAAG,EAAE,CAAC;QAC5B,QAAQ,GAAG,OAAO,EAAE,CAAC;IACvB,CAAC;IAED,0BAA0B;IAC1B,MAAM,QAAQ,GAAG,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,QAAQ,CAAC,CAAC;IAErF,sCAAsC;IACtC,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC/B,IAAI,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC;YACpB,OAAO,IAAI,CAAC,QAAQ,EAAE,eAAe,CAAC,CAAC;QACzC,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,yCAAyC;IAC3C,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,iFAAiF;AAEjF,SAAS,WAAW,CAAC,MAAc;IACjC,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC;IACnC,QAAQ,KAAK,EAAE,CAAC;QACd,KAAK,QAAQ,CAAC,CAAE,OAAO,YAAY,CAAC;QACpC,KAAK,QAAQ,CAAC,CAAE,OAAO,YAAY,CAAC;QACpC,KAAK,SAAS,CAAC,CAAC,OAAO,WAAW,CAAC;QACnC,KAAK,SAAS,CAAC,CAAC,OAAO,WAAW,CAAC;QACnC,KAAK,OAAO,CAAC,CAAG,OAAO,WAAW,CAAC;QACnC,OAAO,CAAC,CAAQ,OAAO,IAAI,KAAK,GAAG,CAAC;IACtC,CAAC;AACH,CAAC;AAED,SAAS,SAAS,CAAC,GAAc;IAC/B,IAAI,CAAC,GAAG,CAAC,YAAY,IAAI,CAAC,GAAG,CAAC,UAAU;QAAE,OAAO,WAAW,CAAC;IAC7D,MAAM,OAAO,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,OAAO,EAAE,CAAC;IACnD,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,OAAO,EAAE,CAAC;IACnD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,GAAG,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;IACpD,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;IAClC,MAAM,CAAC,GAAG,MAAM,GAAG,EAAE,CAAC;IACtB,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC;AACzC,CAAC;AAED,SAAS,YAAY,CACnB,GAAc,EACd,MAAwB,EACxB,UAAgC,EAChC,WAAmB;IAEnB,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,eAAe;IACf,KAAK,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;IACpC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,iBAAiB,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC;IACxC,KAAK,CAAC,IAAI,CAAC,iBAAiB,GAAG,CAAC,aAAa,OAAO,GAAG,CAAC,WAAW,KAAK,CAAC,CAAC;IAC1E,KAAK,CAAC,IAAI,CAAC,eAAe,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACrD,KAAK,CAAC,IAAI,CAAC,gBAAgB,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;IAC1C,KAAK,CAAC,IAAI,CAAC,gBAAgB,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC;IAC7C,KAAK,CAAC,IAAI,CAAC,kBAAkB,GAAG,CAAC,YAAY,IAAI,aAAa,EAAE,CAAC,CAAC;IAClE,KAAK,CAAC,IAAI,CAAC,mBAAmB,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEf,cAAc;IACd,IAAI,GAAG,CAAC,iBAAiB,IAAI,GAAG,CAAC,YAAY,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC;QAChE,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACvB,IAAI,GAAG,CAAC,iBAAiB;YAAE,KAAK,CAAC,IAAI,CAAC,oBAAoB,GAAG,CAAC,iBAAiB,IAAI,CAAC,CAAC;QACrF,IAAI,GAAG,CAAC,cAAc;YAAI,KAAK,CAAC,IAAI,CAAC,eAAe,GAAG,CAAC,cAAc,EAAE,CAAC,CAAC;QAC1E,IAAI,GAAG,CAAC,YAAY;YAAM,KAAK,CAAC,IAAI,CAAC,qBAAqB,GAAG,CAAC,YAAY,IAAI,CAAC,CAAC;QAChF,IAAI,GAAG,CAAC,UAAU;YAAQ,KAAK,CAAC,IAAI,CAAC,mBAAmB,GAAG,CAAC,UAAU,IAAI,CAAC,CAAC;QAC5E,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjB,CAAC;IAED,wBAAwB;IACxB,MAAM,UAAU,GAAG,iBAAiB,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,KAAK,IAAI,IAAI,GAAG,CAAC,UAAU,KAAK,IAAI,CAAC,CAAC;IACrG,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACxB,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,sCAAsC,CAAC,CAAC;QACnD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC;YACnB,KAAK,CAAC,IAAI,CAAC,mBAAmB,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC;YAChD,IAAI,GAAG,CAAC,YAAY;gBAAE,KAAK,CAAC,IAAI,CAAC,qBAAqB,GAAG,CAAC,YAAY,EAAE,CAAC,CAAC;YAC1E,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACjB,CAAC;QACD,YAAY,CAAC,KAAK,CAAC,CAAC;QACpB,IAAI,UAAU;YAAE,iBAAiB,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;QACrD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAED,gBAAgB;IAChB,MAAM,OAAO,GAAG,MAAM,EAAE,kBAAkB,IAAI,GAAG,CAAC,kBAAkB,CAAC;IACrE,IAAI,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC/C,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACzB,KAAK,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;QACjC,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACxB,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YAC7C,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAChC,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjB,CAAC;IAED,qBAAqB;IACrB,MAAM,SAAS,GAAG,MAAM,EAAE,UAAU,IAAI,GAAG,CAAC,UAAU,CAAC;IACvD,IAAI,SAAS,IAAI,SAAS,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5C,KAAK,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QAC9B,KAAK,CAAC,IAAI,CAAC,qDAAqD,CAAC,CAAC;QAClE,KAAK,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;QACpC,KAAK,MAAM,IAAI,IAAI,SAAS,CAAC,KAAK,EAAE,CAAC;YACnC,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC;QAClC,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CACR,wBAAwB,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,YAAY,EAAE,CACzE,CAAC;QACF,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjB,CAAC;IAED,mBAAmB;IACnB,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC;QACnB,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACvB,KAAK,CAAC,IAAI,CAAC,WAAW,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC;QACxC,IAAI,GAAG,CAAC,YAAY;YAAE,KAAK,CAAC,IAAI,CAAC,aAAa,GAAG,CAAC,YAAY,EAAE,CAAC,CAAC;QAClE,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjB,CAAC;IAED,wBAAwB;IACxB,MAAM,gBAAgB,GAAG,MAAM,EAAE,kBAAkB,IAAI,GAAG,CAAC,kBAAkB,CAAC;IAC9E,IAAI,gBAAgB,EAAE,CAAC;QACrB,KAAK,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;QACjC,KAAK,CAAC,IAAI,CAAC,2BAA2B,gBAAgB,IAAI,CAAC,CAAC;QAC5D,KAAK,CAAC,IAAI,CACR,4CAA4C,WAAW,YAAY,gBAAgB,iBAAiB;YAClG,mCAAmC,CACtC,CAAC;QACF,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjB,CAAC;IAED,qBAAqB;IACrB,IAAI,UAAU,EAAE,CAAC;QACf,iBAAiB,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;IACvC,CAAC;IAED,YAAY,CAAC,KAAK,CAAC,CAAC;IACpB,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,SAAS,aAAa,CAAC,IAAoB;IACzC,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC;IACzD,OAAO,KAAK,IAAI,CAAC,MAAM,MAAM,IAAI,CAAC,QAAQ,MAAM,IAAI,CAAC,SAAS,MAAM,IAAI,CAAC,YAAY,MAAM,UAAU,IAAI,CAAC;AAC5G,CAAC;AAED,SAAS,iBAAiB,CAAC,KAAe,EAAE,UAAyB;IACnE,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC;IAC7B,KAAK,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;IACtC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CACR,oBAAoB,UAAU,CAAC,cAAc,MAAM;QACjD,IAAI,IAAI,CAAC,oBAAoB,IAAI,WAAW,GAAG,CAClD,CAAC;IACF,KAAK,CAAC,IAAI,CACR,mBAAmB,UAAU,CAAC,eAAe,IAAI,GAAG,MAAM;QACxD,IAAI,IAAI,CAAC,qBAAqB,IAAI,GAAG,GAAG,CAC3C,CAAC;IACF,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEf,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;QACrB,KAAK,CAAC,IAAI,CAAC,uEAAuE,CAAC,CAAC;QACpF,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,OAAO;IACT,CAAC;IAED,UAAU;IACV,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,IAAI,EAAE,CAAC;IAC1D,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1B,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAC1B,KAAK,CAAC,IAAI,CAAC,2CAA2C,CAAC,CAAC;QACxD,KAAK,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;QAChC,KAAK,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC;YAC3B,MAAM,CAAC,GAAG,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;YAChC,MAAM,GAAG,GACP,CAAC,CAAC,SAAS,KAAK,IAAI;gBAClB,CAAC,CAAC,GAAG,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG;gBAC3D,CAAC,CAAC,GAAG,CAAC;YACV,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;YACjG,KAAK,CAAC,IAAI,CACR,KAAK,CAAC,MAAM,CAAC,CAAC,QAAQ,IAAI,GAAG,MAAM,CAAC,CAAC,OAAO,IAAI,GAAG,MAAM,KAAK,IAAI,CACnE,CAAC;QACJ,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjB,CAAC;IAED,aAAa;IACb,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC/B,KAAK,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QAC9B,KAAK,CAAC,IAAI,CAAC,8CAA8C,CAAC,CAAC;QAC3D,KAAK,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;QAChC,KAAK,MAAM,EAAE,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACjC,MAAM,QAAQ,GAAG,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;YAChG,MAAM,QAAQ,GAAG,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;YAC9F,MAAM,KAAK,GAAG,EAAE,CAAC,UAAU,KAAK,WAAW,CAAC,CAAC,CAAC,oBAAoB;gBAChE,CAAC,CAAC,EAAE,CAAC,UAAU,KAAK,UAAU,CAAC,CAAC,CAAC,cAAc;oBAC/C,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC;YAClB,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,MAAM,MAAM,QAAQ,MAAM,QAAQ,MAAM,KAAK,IAAI,CAAC,CAAC;QACxE,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjB,CAAC;IAED,QAAQ;IACR,IAAI,UAAU,CAAC,WAAW,EAAE,CAAC;QAC3B,KAAK,CAAC,IAAI,CAAC,uBAAuB,UAAU,CAAC,WAAW,0CAA0C,CAAC,CAAC;QACpG,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjB,CAAC;AACH,CAAC;AAED,SAAS,YAAY,CAAC,KAAe;IACnC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAClB,KAAK,CAAC,IAAI,CAAC,8BAA8B,OAAO,OAAO,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;AACtF,CAAC;AAED,8EAA8E;AAC9E,OAAO,EAAE,MAAM,IAAI,OAAO,EAAE,CAAC"}
|
package/dist/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const VERSION = "0.2.
|
|
2
|
-
export declare const USER_AGENT = "edgegate-mcp/0.2.
|
|
1
|
+
export declare const VERSION = "0.2.2";
|
|
2
|
+
export declare const USER_AGENT = "edgegate-mcp/0.2.2";
|
package/dist/version.js
CHANGED
package/package.json
CHANGED
package/plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "edgegate",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Edge-AI regression gates from Claude Code — set up CI, run benchmarks, and fetch audit
|
|
3
|
+
"version": "0.2.2",
|
|
4
|
+
"description": "Edge-AI regression gates from Claude Code — set up CI, run benchmarks, compare runs, export reports, and fetch audit bundles from any prompt.",
|
|
5
5
|
"author": "Frozo / EdgeGate",
|
|
6
6
|
"homepage": "https://edgegate.frozo.ai",
|
|
7
7
|
"repository": "https://github.com/frozo-ai/edgegate-mcp",
|
|
@@ -21,6 +21,8 @@
|
|
|
21
21
|
"skills/edgegate-init.md",
|
|
22
22
|
"skills/edgegate-gate.md",
|
|
23
23
|
"skills/edgegate-status.md",
|
|
24
|
-
"skills/edgegate-audit.md"
|
|
24
|
+
"skills/edgegate-audit.md",
|
|
25
|
+
"skills/edgegate-compare.md",
|
|
26
|
+
"skills/edgegate-export.md"
|
|
25
27
|
]
|
|
26
28
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: edgegate-compare
|
|
3
|
+
description: Diff two EdgeGate runs in the same pipeline. Use when the user wants to know "what changed" between runs, "is this a regression", "compare these runs", or "show the delta vs main".
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# /edgegate-compare
|
|
7
|
+
|
|
8
|
+
The user wants to compare two EdgeGate runs and understand the verdict.
|
|
9
|
+
|
|
10
|
+
## Steps
|
|
11
|
+
|
|
12
|
+
1. **Identify the candidate run.** If the user gave you a `run_id`, use it. If they said "the latest run" or didn't specify, call `edgegate_get_report` to list recent runs and ask which one they mean.
|
|
13
|
+
|
|
14
|
+
2. **Identify the baseline.**
|
|
15
|
+
- If the user provided a `baseline_run_id`, use it.
|
|
16
|
+
- Otherwise, omit the field — `edgegate_compare_runs` auto-selects the most recent PASSED run from the same pipeline (excluding the candidate). This is almost always what users want.
|
|
17
|
+
|
|
18
|
+
3. **Call `edgegate_compare_runs`** with `workspace_id`, `run_id`, and optionally `baseline_run_id`.
|
|
19
|
+
|
|
20
|
+
4. **Lead with the verdict.** The tool returns one of:
|
|
21
|
+
- **REGRESSION** — at least one gate flipped ✓→✗ OR a lower-is-better metric increased by ≥ 25%. Call this out at the top. List which gates flipped and what metric jumped.
|
|
22
|
+
- **IMPROVEMENT** — at least one ✗→✓ gate recovery with no regressions. Briefly highlight what got better.
|
|
23
|
+
- **NEUTRAL** — no significant changes. Reassure the user the run is safe to merge.
|
|
24
|
+
- **NO BASELINE** — this is the first run in the pipeline (nothing to compare against).
|
|
25
|
+
|
|
26
|
+
5. **For PR comments:** suggest the user attach the metric deltas table + verdict line. The audit trail (signed diff SHA-256) is in the response and worth including for compliance.
|
|
27
|
+
|
|
28
|
+
## Failure modes
|
|
29
|
+
|
|
30
|
+
- **404 on the candidate** — wrong `run_id`. Ask the user to double-check or call `edgegate_get_report`.
|
|
31
|
+
- **NO BASELINE on a pipeline that should have runs** — only one run exists in that pipeline OR the prior runs never completed. Check via `edgegate_get_report` and explain.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: edgegate-export
|
|
3
|
+
description: Save an EdgeGate run report as a markdown file on disk. Use when the user wants to "export", "download", "save", or "share" a run report — e.g. for a PR comment, Slack message, or compliance record.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# /edgegate-export
|
|
7
|
+
|
|
8
|
+
The user wants the run report saved as a file they can share or attach.
|
|
9
|
+
|
|
10
|
+
## Steps
|
|
11
|
+
|
|
12
|
+
1. **Identify the run.** If they gave a `run_id`, use it. If not, call `edgegate_get_report` and ask which run.
|
|
13
|
+
|
|
14
|
+
2. **Pick the output path.**
|
|
15
|
+
- If the user said "save to Downloads" or similar, use `~/Downloads`.
|
|
16
|
+
- If they said "save next to my code" or didn't specify, default to the current working directory (the tool defaults to `./edgegate-run-{id-short}.md`).
|
|
17
|
+
- If they said "save as <name>", use that exact filename.
|
|
18
|
+
- The tool accepts directory paths (auto-appends a filename), file paths (used as-is), `~/` (expanded to home), and relative paths.
|
|
19
|
+
|
|
20
|
+
3. **Decide whether to include the diff.**
|
|
21
|
+
- If the user mentioned "for the PR" or "vs main" or "with the comparison", pass `include_diff: true` — adds the run-vs-baseline diff section to the report.
|
|
22
|
+
- Otherwise default `include_diff: false` — keeps the report focused on this run.
|
|
23
|
+
|
|
24
|
+
4. **Call `edgegate_export_run_report`** with the args.
|
|
25
|
+
|
|
26
|
+
5. **Confirm the file path** the tool returns and show the preview. Tell the user:
|
|
27
|
+
- The file is ready to attach to a PR comment, paste into Slack, or store with compliance records.
|
|
28
|
+
- If they want to also get a programmatic JSON version, point them at `edgegate_get_audit_report` for the bundle metadata.
|
|
29
|
+
|
|
30
|
+
## Failure modes
|
|
31
|
+
|
|
32
|
+
- **404 on the run** — wrong `run_id`. Check `edgegate_get_report`.
|
|
33
|
+
- **In-flight run** — the report omits gate results and bundle sections (they don't exist yet) but still writes a partial file noting the status. Tell the user to retry after the run completes.
|
|
34
|
+
- **Permission errors writing the file** — fall back to `/tmp/edgegate-run-{id}.md` and tell the user.
|
|
@@ -16,3 +16,7 @@ For PASSED runs, briefly summarize the metrics so the user has the numbers handy
|
|
|
16
16
|
## "Is this run a regression?"
|
|
17
17
|
|
|
18
18
|
If the user asks whether a run is a regression, or wants to see how it compares to the previous one, call `edgegate_compare_runs` with the `run_id` (and optionally a `baseline_run_id`). The tool auto-selects the baseline from the same pipeline when no explicit baseline is given. Read the **Verdict** section of the output — REGRESSION means at least one gate flipped ✓→✗ or a lower-is-better metric increased by ≥ 25%.
|
|
19
|
+
|
|
20
|
+
## "Save" or "export" the report
|
|
21
|
+
|
|
22
|
+
If the user asks to save, export, or download the run report as a file, call `edgegate_export_run_report` with the `workspace_id`, `run_id`, and optionally an `output_path`. Pass `include_diff: true` if they also want the baseline comparison included in the file. The tool writes a complete markdown file to disk and returns the absolute path.
|