cgraphx 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. package/dist/.claude-template/hooks/precommit-check/precommit-check.cjs +90 -0
  2. package/dist/.claude-template/skills/cgraphx-guide/how-to-use.html +99 -78
  3. package/dist/.claude-template/skills/precommit-review/SKILL.md +50 -0
  4. package/dist/.claude-template/skills/run-api-test/SKILL.md +187 -0
  5. package/dist/.claude-template/skills/run-api-test/assets/template-test-report.md +103 -0
  6. package/dist/.claude-template/skills/run-api-test/assets/template-test-verify.jsonl +5 -0
  7. package/dist/.claude-template/skills/run-api-test/references/bru-run.md +60 -0
  8. package/dist/.claude-template/skills/run-api-test/references/db-verification.md +81 -0
  9. package/dist/.claude-template/skills/run-api-test/references/report-format.md +104 -0
  10. package/dist/.claude-template/skills/run-api-test/references/service-readiness.md +61 -0
  11. package/dist/.claude-template/skills/run-api-test/references/test-scope.md +64 -0
  12. package/dist/.claude-template/skills/write-api/SKILL.md +150 -0
  13. package/dist/.claude-template/skills/write-api/assets/template-api-spec.md +112 -0
  14. package/dist/.claude-template/skills/write-api/assets/template-request.bru +75 -0
  15. package/dist/.claude-template/skills/write-api/references/ai-prompts.md +133 -0
  16. package/dist/.claude-template/skills/write-api/references/api-spec-format.md +108 -0
  17. package/dist/.claude-template/skills/write-api/references/bru-format.md +144 -0
  18. package/dist/.claude-template/skills/write-api/references/collection-layout.md +81 -0
  19. package/dist/.claude-template/skills/write-api/references/environment-setup.md +105 -0
  20. package/dist/.claude-template/skills/write-api/references/interface-scope.md +74 -0
  21. package/dist/api-test/ai-fields.d.ts +37 -0
  22. package/dist/api-test/ai-fields.d.ts.map +1 -0
  23. package/dist/api-test/ai-fields.js +114 -0
  24. package/dist/api-test/ai-fields.js.map +1 -0
  25. package/dist/api-test/assemble.d.ts +76 -0
  26. package/dist/api-test/assemble.d.ts.map +1 -0
  27. package/dist/api-test/assemble.js +185 -0
  28. package/dist/api-test/assemble.js.map +1 -0
  29. package/dist/api-test/bru-cli-invoker.d.ts +72 -0
  30. package/dist/api-test/bru-cli-invoker.d.ts.map +1 -0
  31. package/dist/api-test/bru-cli-invoker.js +169 -0
  32. package/dist/api-test/bru-cli-invoker.js.map +1 -0
  33. package/dist/api-test/bru-report-parser.d.ts +24 -0
  34. package/dist/api-test/bru-report-parser.d.ts.map +1 -0
  35. package/dist/api-test/bru-report-parser.js +110 -0
  36. package/dist/api-test/bru-report-parser.js.map +1 -0
  37. package/dist/api-test/bru-runner.d.ts +101 -0
  38. package/dist/api-test/bru-runner.d.ts.map +1 -0
  39. package/dist/api-test/bru-runner.js +316 -0
  40. package/dist/api-test/bru-runner.js.map +1 -0
  41. package/dist/api-test/bru-writer.d.ts +52 -0
  42. package/dist/api-test/bru-writer.d.ts.map +1 -0
  43. package/dist/api-test/bru-writer.js +159 -0
  44. package/dist/api-test/bru-writer.js.map +1 -0
  45. package/dist/api-test/call-chain-extractor.d.ts +80 -0
  46. package/dist/api-test/call-chain-extractor.d.ts.map +1 -0
  47. package/dist/api-test/call-chain-extractor.js +179 -0
  48. package/dist/api-test/call-chain-extractor.js.map +1 -0
  49. package/dist/api-test/cli.d.ts +133 -0
  50. package/dist/api-test/cli.d.ts.map +1 -0
  51. package/dist/api-test/cli.js +1009 -0
  52. package/dist/api-test/cli.js.map +1 -0
  53. package/dist/api-test/config.d.ts +75 -0
  54. package/dist/api-test/config.d.ts.map +1 -0
  55. package/dist/api-test/config.js +406 -0
  56. package/dist/api-test/config.js.map +1 -0
  57. package/dist/api-test/db-query-cli.d.ts +51 -0
  58. package/dist/api-test/db-query-cli.d.ts.map +1 -0
  59. package/dist/api-test/db-query-cli.js +119 -0
  60. package/dist/api-test/db-query-cli.js.map +1 -0
  61. package/dist/api-test/enhance-prepare.d.ts +111 -0
  62. package/dist/api-test/enhance-prepare.d.ts.map +1 -0
  63. package/dist/api-test/enhance-prepare.js +425 -0
  64. package/dist/api-test/enhance-prepare.js.map +1 -0
  65. package/dist/api-test/enhance-write.d.ts +28 -0
  66. package/dist/api-test/enhance-write.d.ts.map +1 -0
  67. package/dist/api-test/enhance-write.js +145 -0
  68. package/dist/api-test/enhance-write.js.map +1 -0
  69. package/dist/api-test/errors.d.ts +48 -0
  70. package/dist/api-test/errors.d.ts.map +1 -0
  71. package/dist/api-test/errors.js +76 -0
  72. package/dist/api-test/errors.js.map +1 -0
  73. package/dist/api-test/field-extractor.d.ts +98 -0
  74. package/dist/api-test/field-extractor.d.ts.map +1 -0
  75. package/dist/api-test/field-extractor.js +327 -0
  76. package/dist/api-test/field-extractor.js.map +1 -0
  77. package/dist/api-test/impl-finder.d.ts +37 -0
  78. package/dist/api-test/impl-finder.d.ts.map +1 -0
  79. package/dist/api-test/impl-finder.js +54 -0
  80. package/dist/api-test/impl-finder.js.map +1 -0
  81. package/dist/api-test/index.d.ts +41 -0
  82. package/dist/api-test/index.d.ts.map +1 -0
  83. package/dist/api-test/index.js +124 -0
  84. package/dist/api-test/index.js.map +1 -0
  85. package/dist/api-test/java-parser.d.ts +89 -0
  86. package/dist/api-test/java-parser.d.ts.map +1 -0
  87. package/dist/api-test/java-parser.js +508 -0
  88. package/dist/api-test/java-parser.js.map +1 -0
  89. package/dist/api-test/md-writer.d.ts +49 -0
  90. package/dist/api-test/md-writer.d.ts.map +1 -0
  91. package/dist/api-test/md-writer.js +202 -0
  92. package/dist/api-test/md-writer.js.map +1 -0
  93. package/dist/api-test/parser-httpservice.d.ts +91 -0
  94. package/dist/api-test/parser-httpservice.d.ts.map +1 -0
  95. package/dist/api-test/parser-httpservice.js +271 -0
  96. package/dist/api-test/parser-httpservice.js.map +1 -0
  97. package/dist/api-test/report.d.ts +188 -0
  98. package/dist/api-test/report.d.ts.map +1 -0
  99. package/dist/api-test/report.js +522 -0
  100. package/dist/api-test/report.js.map +1 -0
  101. package/dist/api-test/snapshot.d.ts +26 -0
  102. package/dist/api-test/snapshot.d.ts.map +1 -0
  103. package/dist/api-test/snapshot.js +150 -0
  104. package/dist/api-test/snapshot.js.map +1 -0
  105. package/dist/api-test/test-history.d.ts +48 -0
  106. package/dist/api-test/test-history.d.ts.map +1 -0
  107. package/dist/api-test/test-history.js +122 -0
  108. package/dist/api-test/test-history.js.map +1 -0
  109. package/dist/api-test/types.d.ts +174 -0
  110. package/dist/api-test/types.d.ts.map +1 -0
  111. package/dist/api-test/types.js +13 -0
  112. package/dist/api-test/types.js.map +1 -0
  113. package/dist/api-test/verify-prepare.d.ts +30 -0
  114. package/dist/api-test/verify-prepare.d.ts.map +1 -0
  115. package/dist/api-test/verify-prepare.js +150 -0
  116. package/dist/api-test/verify-prepare.js.map +1 -0
  117. package/dist/api-test/verify-write.d.ts +31 -0
  118. package/dist/api-test/verify-write.d.ts.map +1 -0
  119. package/dist/api-test/verify-write.js +159 -0
  120. package/dist/api-test/verify-write.js.map +1 -0
  121. package/dist/dbquery/dump-schema.d.ts +46 -0
  122. package/dist/dbquery/dump-schema.d.ts.map +1 -0
  123. package/dist/dbquery/dump-schema.js +379 -0
  124. package/dist/dbquery/dump-schema.js.map +1 -0
  125. package/dist/installer/targets/claude.d.ts +15 -0
  126. package/dist/installer/targets/claude.d.ts.map +1 -1
  127. package/dist/installer/targets/claude.js +53 -0
  128. package/dist/installer/targets/claude.js.map +1 -1
  129. package/package.json +1 -1
  130. package/scripts/agent-eval/block-cgraphx-and-gitnexus-cli-hook.sh +0 -43
  131. package/scripts/agent-eval/block-cgraphx-cli-hook.sh +0 -32
  132. package/scripts/agent-eval/block-cgraphx-cli-settings.json +0 -16
  133. package/scripts/agent-eval/cli-vs-mcp-3arm.sh +0 -121
  134. package/scripts/agent-eval/multi-tool-eval.sh +0 -171
  135. package/scripts/agent-eval/parse-cli-vs-mcp.mjs +0 -232
  136. package/scripts/agent-eval/parse-multi-tool.mjs +0 -242
@@ -0,0 +1,522 @@
1
+ "use strict";
2
+ /**
3
+ * Report + list phase — Phase 6 of the api-test pipeline (T-60).
4
+ *
5
+ * Spec contract:
6
+ * - `generateReport(projectRoot, date)` aggregates the day's JSONL run
7
+ * history + the static `.md` catalog into a Markdown report written to
8
+ * `docs/api-test/report-YYYY-MM-DD.md`.
9
+ * - `listEndpoints(projectRoot, filters)` renders a terminal table of every
10
+ * endpoint discovered from the `.md` catalog, with the most recent run
11
+ * result joined from JSONL history.
12
+ *
13
+ * Aggregation rules (spec §5.6):
14
+ * - total endpoints = number of `.md` files (the catalog is the source of
15
+ * truth for "what interfaces exist").
16
+ * - passed / failed = count of JSONL lines with `passed: true` / `false`.
17
+ * - skip count = number of `.md` files whose frontmatter `status` is
18
+ * `skip`.
19
+ * - per-service rate = group JSONL runs by `service`, passed/total.
20
+ * - failure list = JSONL lines with `passed: false` (endpoint + error).
21
+ * - skip list = `.md` files with `status: skip` (endpoint + skip_reason).
22
+ * - avg duration = mean of `duration_ms` across all JSONL lines for the date.
23
+ * - p99 duration = nearest-rank 99th percentile of `duration_ms` (degenerates
24
+ * to max for small N — that's expected, not a bug).
25
+ *
26
+ * JSONL missing for the requested date → `ReportError` (CLI exit code 2).
27
+ *
28
+ * Independent subsystem — only depends on `node:fs` / `node:path` and the
29
+ * subsystem's own `test-history.ts` / `errors.ts` / `types.ts`. No imports
30
+ * from `src/extraction/` / `src/resolution/` / `src/db/`, and no third-party
31
+ * YAML / table libraries (frontmatter is parsed with a hand-rolled scalar
32
+ * parser; the terminal table is rendered with manual padding).
33
+ */
34
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
35
+ if (k2 === undefined) k2 = k;
36
+ var desc = Object.getOwnPropertyDescriptor(m, k);
37
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
38
+ desc = { enumerable: true, get: function() { return m[k]; } };
39
+ }
40
+ Object.defineProperty(o, k2, desc);
41
+ }) : (function(o, m, k, k2) {
42
+ if (k2 === undefined) k2 = k;
43
+ o[k2] = m[k];
44
+ }));
45
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
46
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
47
+ }) : function(o, v) {
48
+ o["default"] = v;
49
+ });
50
+ var __importStar = (this && this.__importStar) || (function () {
51
+ var ownKeys = function(o) {
52
+ ownKeys = Object.getOwnPropertyNames || function (o) {
53
+ var ar = [];
54
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
55
+ return ar;
56
+ };
57
+ return ownKeys(o);
58
+ };
59
+ return function (mod) {
60
+ if (mod && mod.__esModule) return mod;
61
+ var result = {};
62
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
63
+ __setModuleDefault(result, mod);
64
+ return result;
65
+ };
66
+ })();
67
+ Object.defineProperty(exports, "__esModule", { value: true });
68
+ exports.__INTERNAL_REPORT = void 0;
69
+ exports.generateReport = generateReport;
70
+ exports.reportFilePath = reportFilePath;
71
+ exports.listEndpoints = listEndpoints;
72
+ exports.renderListTable = renderListTable;
73
+ exports.readRunsForDate = readRunsForDate;
74
+ exports.parseJsonl = parseJsonl;
75
+ exports.readLatestRunByEndpoint = readLatestRunByEndpoint;
76
+ exports.readAllEndpoints = readAllEndpoints;
77
+ exports.parseFrontmatter = parseFrontmatter;
78
+ exports.aggregateReport = aggregateReport;
79
+ exports.percentile = percentile;
80
+ exports.renderReport = renderReport;
81
+ exports.renderTable = renderTable;
82
+ const fs = __importStar(require("node:fs"));
83
+ const path = __importStar(require("node:path"));
84
+ const test_history_1 = require("./test-history");
85
+ const errors_1 = require("./errors");
86
+ /** Default output root (relative to project root) — mirrors `cli.ts`. */
87
+ const DEFAULT_OUTPUT_DIR = 'docs/api-test';
88
+ // ─── Public API ────────────────────────────────────────────────────────────
89
+ /**
90
+ * Generate the Markdown report for a single day.
91
+ *
92
+ * Reads `<projectRoot>/.cgraphx/api-test/runs/<date>.jsonl` and every
93
+ * `docs/api-test/<service>/*.md` catalog file, aggregates statistics, and
94
+ * returns the Markdown content. Throws `ReportError` when the JSONL file
95
+ * for the requested date doesn't exist (the report has no run data to
96
+ * summarize — that's a hard error, not an empty report).
97
+ */
98
+ async function generateReport(projectRoot, date) {
99
+ const runs = readRunsForDate(projectRoot, date); // throws ReportError if missing
100
+ const endpoints = readAllEndpoints(projectRoot);
101
+ const data = aggregateReport(date, runs, endpoints);
102
+ return renderReport(data);
103
+ }
104
+ /**
105
+ * Resolve the report output path: `<projectRoot>/docs/api-test/report-<date>.md`.
106
+ */
107
+ function reportFilePath(projectRoot, date) {
108
+ return path.join(projectRoot, DEFAULT_OUTPUT_DIR, `report-${date}.md`);
109
+ }
110
+ /**
111
+ * List every catalog endpoint with its most recent run result, after filters.
112
+ *
113
+ * The "most recent run" is found by scanning every `*.jsonl` file in the runs
114
+ * directory and picking the entry with the largest `ran_at` for each endpoint.
115
+ * Endpoints with no run history get `lastRunResult = '—'`.
116
+ */
117
+ function listEndpoints(projectRoot, filters = {}) {
118
+ const endpoints = readAllEndpoints(projectRoot);
119
+ const lastRunByEndpoint = readLatestRunByEndpoint(projectRoot);
120
+ const rows = [];
121
+ for (const ep of endpoints) {
122
+ if (filters.service && ep.service !== filters.service)
123
+ continue;
124
+ if (filters.status && ep.status !== filters.status)
125
+ continue;
126
+ if (filters.svcId && ep.svcId !== filters.svcId)
127
+ continue;
128
+ const last = lastRunByEndpoint.get(ep.endpoint);
129
+ const lastRunResult = last === undefined
130
+ ? '—'
131
+ : (last.passed ? 'true' : 'false');
132
+ rows.push({
133
+ endpoint: ep.endpoint,
134
+ service: ep.service,
135
+ svcId: ep.svcId,
136
+ status: ep.status,
137
+ lastRunResult,
138
+ });
139
+ }
140
+ // Stable sort: by service, then endpoint — so re-runs produce identical output.
141
+ rows.sort((a, b) => a.service === b.service
142
+ ? a.endpoint.localeCompare(b.endpoint)
143
+ : a.service.localeCompare(b.service));
144
+ return rows;
145
+ }
146
+ /**
147
+ * Render `listEndpoints` output as a fixed-width terminal table with a header
148
+ * row and a separator line. Returns the empty string when there are no rows
149
+ * (the CLI prints a "no endpoints" notice instead).
150
+ */
151
+ function renderListTable(rows) {
152
+ if (rows.length === 0)
153
+ return '';
154
+ const headers = ['ENDPOINT', 'SERVICE', 'SVC_ID', 'STATUS', 'LAST_RUN_RESULT'];
155
+ const table = rows.map((r) => [
156
+ r.endpoint,
157
+ r.service,
158
+ r.svcId,
159
+ r.status,
160
+ r.lastRunResult,
161
+ ]);
162
+ return renderTable([headers, ...table]);
163
+ }
164
+ // ─── JSONL run history ─────────────────────────────────────────────────────
165
+ /**
166
+ * Read every `TestRun` from `<runsDir>/<date>.jsonl`. Throws `ReportError`
167
+ * when the file doesn't exist (the report cannot be summarized without run
168
+ * data — the spec says "选抛错,exit code 2").
169
+ */
170
+ function readRunsForDate(projectRoot, date) {
171
+ const filePath = path.join((0, test_history_1.runsDirPath)(projectRoot), `${date}.jsonl`);
172
+ let content;
173
+ try {
174
+ content = fs.readFileSync(filePath, 'utf8');
175
+ }
176
+ catch (err) {
177
+ if (err.code === 'ENOENT') {
178
+ throw new errors_1.ReportError(`No run history for ${date}: ${filePath}`);
179
+ }
180
+ throw err;
181
+ }
182
+ return parseJsonl(content);
183
+ }
184
+ /**
185
+ * Parse a JSONL string into `TestRun[]`. Blank lines are skipped; malformed
186
+ * lines throw `ReportError` (a corrupted history file shouldn't silently
187
+ * produce wrong stats).
188
+ */
189
+ function parseJsonl(content) {
190
+ const runs = [];
191
+ const lines = content.split(/\r?\n/);
192
+ for (let i = 0; i < lines.length; i++) {
193
+ const line = lines[i].trim();
194
+ if (line.length === 0)
195
+ continue;
196
+ let obj;
197
+ try {
198
+ obj = JSON.parse(line);
199
+ }
200
+ catch (err) {
201
+ throw new errors_1.ReportError(`Failed to parse JSONL line ${i + 1}: ${err instanceof Error ? err.message : String(err)}`);
202
+ }
203
+ runs.push(deserializeRun(obj));
204
+ }
205
+ return runs;
206
+ }
207
+ /** Convert a parsed JSONL object (snake_case keys) back into a `TestRun`. */
208
+ function deserializeRun(obj) {
209
+ return {
210
+ endpoint: String(obj.endpoint ?? ''),
211
+ service: String(obj.service ?? ''),
212
+ ranAt: String(obj.ran_at ?? ''),
213
+ httpStatus: Number(obj.http_status ?? 0),
214
+ bizCode: obj.biz_code === null || obj.biz_code === undefined ? null : String(obj.biz_code),
215
+ durationMs: Number(obj.duration_ms ?? 0),
216
+ passed: Boolean(obj.passed),
217
+ error: obj.error === null || obj.error === undefined ? null : String(obj.error),
218
+ };
219
+ }
220
+ /**
221
+ * Build a map of `endpoint → most recent TestRun` by scanning every JSONL
222
+ * file in the runs directory. Returns an empty map when the runs directory
223
+ * doesn't exist (a fresh project with no run history — `list` still works,
224
+ * every endpoint just shows `—`).
225
+ */
226
+ function readLatestRunByEndpoint(projectRoot) {
227
+ const dir = (0, test_history_1.runsDirPath)(projectRoot);
228
+ let files;
229
+ try {
230
+ files = fs.readdirSync(dir);
231
+ }
232
+ catch (err) {
233
+ if (err.code === 'ENOENT')
234
+ return new Map();
235
+ throw err;
236
+ }
237
+ const latest = new Map();
238
+ for (const file of files) {
239
+ if (!file.endsWith('.jsonl'))
240
+ continue;
241
+ const filePath = path.join(dir, file);
242
+ let content;
243
+ try {
244
+ content = fs.readFileSync(filePath, 'utf8');
245
+ }
246
+ catch {
247
+ continue;
248
+ }
249
+ for (const run of parseJsonl(content)) {
250
+ const prev = latest.get(run.endpoint);
251
+ if (prev === undefined || run.ranAt > prev.ranAt) {
252
+ latest.set(run.endpoint, run);
253
+ }
254
+ }
255
+ }
256
+ return latest;
257
+ }
258
+ // ─── .md catalog reading ───────────────────────────────────────────────────
259
+ /**
260
+ * Walk `docs/api-test/<service>/*.md` and parse each file's frontmatter into
261
+ * an `EndpointMd` record. Skips `report-*.md` files at the api-test root
262
+ * (those are this subsystem's own output, not catalog entries).
263
+ */
264
+ function readAllEndpoints(projectRoot) {
265
+ const outputDir = path.join(projectRoot, DEFAULT_OUTPUT_DIR);
266
+ let serviceEntries;
267
+ try {
268
+ serviceEntries = fs.readdirSync(outputDir, { withFileTypes: true });
269
+ }
270
+ catch (err) {
271
+ if (err.code === 'ENOENT')
272
+ return [];
273
+ throw err;
274
+ }
275
+ const endpoints = [];
276
+ for (const entry of serviceEntries) {
277
+ if (!entry.isDirectory())
278
+ continue;
279
+ const serviceDir = path.join(outputDir, entry.name);
280
+ let mdFiles;
281
+ try {
282
+ mdFiles = fs.readdirSync(serviceDir);
283
+ }
284
+ catch {
285
+ continue;
286
+ }
287
+ for (const mdFile of mdFiles) {
288
+ if (!mdFile.endsWith('.md'))
289
+ continue;
290
+ const mdPath = path.join(serviceDir, mdFile);
291
+ const content = fs.readFileSync(mdPath, 'utf8');
292
+ const fm = parseFrontmatter(content);
293
+ const svcId = fm.svc_id ?? '';
294
+ const method = fm.method ?? '';
295
+ const service = fm.service ?? entry.name;
296
+ const status = fm.status === 'skip' ? 'skip' : 'active';
297
+ const skipReason = fm.skip_reason ?? null;
298
+ // The endpoint identifier in JSONL is `<svc_id>_<method>` (the .bru/.md
299
+ // filename stem, minus extension). Derive it from the filename for
300
+ // resilience — frontmatter `method` could in theory be missing.
301
+ const endpoint = mdFile.replace(/\.md$/, '');
302
+ endpoints.push({ endpoint, svcId, service, method, status, skipReason, mdPath });
303
+ }
304
+ }
305
+ return endpoints;
306
+ }
307
+ // ─── Frontmatter parsing (hand-rolled, scalars only) ───────────────────────
308
+ /**
309
+ * Parse the YAML frontmatter block (the text between `---` lines at the top
310
+ * of a `.md` file) into a string-keyed map.
311
+ *
312
+ * Scalar `key: value` pairs are extracted. Quoted scalars (`"..."` / `'...'`)
313
+ * are unquoted. The literal `null` becomes `null` in the map. Block scalars
314
+ * (`|-` followed by indented lines) and nested lists (`key:` + `- item`) are
315
+ * NOT parsed — the report/list phase only needs scalar fields (`svc_id`,
316
+ * `service`, `method`, `status`, `skip_reason`). Lines that look like list
317
+ * items or continuation lines (leading whitespace) are skipped.
318
+ */
319
+ function parseFrontmatter(content) {
320
+ const result = {};
321
+ const m = /^---\r?\n([\s\S]*?)\r?\n---/.exec(content);
322
+ if (!m)
323
+ return result;
324
+ const fm = m[1] ?? '';
325
+ for (const line of fm.split(/\r?\n/)) {
326
+ // Skip indented lines (list items, block-scalar bodies, nested mappings).
327
+ if (/^\s/.test(line))
328
+ continue;
329
+ const kv = /^([a-z_]+):\s*(.*)$/.exec(line);
330
+ if (!kv)
331
+ continue;
332
+ const key = kv[1];
333
+ let value = kv[2] ?? '';
334
+ // An empty value means this key starts a block scalar or nested list
335
+ // (e.g. `dep_tables:` followed by ` - item` lines). The parser doesn't
336
+ // handle those — skip the key entirely so callers see `undefined`.
337
+ // YAML block-scalar indicators (`|`, `|-`, `|+`, `>`, `>-`, `>+`) are
338
+ // also skipped — the value lives on the following indented lines.
339
+ if (value === '' || /^[|>][+-]?$/.test(value))
340
+ continue;
341
+ // Strip surrounding quotes (single or double, with escape unescape for double).
342
+ if (value.length >= 2) {
343
+ if (value.startsWith('"') && value.endsWith('"')) {
344
+ value = value.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
345
+ }
346
+ else if (value.startsWith("'") && value.endsWith("'")) {
347
+ value = value.slice(1, -1).replace(/''/g, "'");
348
+ }
349
+ }
350
+ if (value === 'null') {
351
+ result[key] = null;
352
+ }
353
+ else {
354
+ result[key] = value;
355
+ }
356
+ }
357
+ return result;
358
+ }
359
+ // ─── Aggregation ───────────────────────────────────────────────────────────
360
+ /** Aggregate JSONL runs + `.md` catalog into `ReportData` for rendering. */
361
+ function aggregateReport(date, runs, endpoints) {
362
+ const totalEndpoints = endpoints.length;
363
+ const passedCount = runs.filter((r) => r.passed).length;
364
+ const failedCount = runs.filter((r) => !r.passed).length;
365
+ const skipCount = endpoints.filter((e) => e.status === 'skip').length;
366
+ // Per-service grouping — based on JSONL runs (the run population). A service
367
+ // with catalog endpoints but zero runs still appears with total=0, passed=0,
368
+ // passRate=0 so the gap is visible in the report.
369
+ const servicesInCatalog = new Set(endpoints.map((e) => e.service));
370
+ const servicesInRuns = new Set(runs.map((r) => r.service));
371
+ const allServices = new Set([...servicesInCatalog, ...servicesInRuns]);
372
+ const perService = [];
373
+ for (const service of allServices) {
374
+ const serviceRuns = runs.filter((r) => r.service === service);
375
+ const total = serviceRuns.length;
376
+ const passed = serviceRuns.filter((r) => r.passed).length;
377
+ perService.push({
378
+ service,
379
+ total,
380
+ passed,
381
+ passRate: total === 0 ? 0 : passed / total,
382
+ });
383
+ }
384
+ perService.sort((a, b) => a.service.localeCompare(b.service));
385
+ const failures = runs
386
+ .filter((r) => !r.passed)
387
+ .map((r) => ({ endpoint: r.endpoint, service: r.service, error: r.error }));
388
+ const skips = endpoints
389
+ .filter((e) => e.status === 'skip')
390
+ .map((e) => ({ endpoint: e.endpoint, service: e.service, skipReason: e.skipReason }));
391
+ const durations = runs.map((r) => r.durationMs);
392
+ const avgDurationMs = durations.length === 0
393
+ ? 0
394
+ : durations.reduce((a, b) => a + b, 0) / durations.length;
395
+ const p99DurationMs = percentile(durations, 0.99);
396
+ return {
397
+ date,
398
+ totalEndpoints,
399
+ passedCount,
400
+ failedCount,
401
+ skipCount,
402
+ perService,
403
+ failures,
404
+ skips,
405
+ avgDurationMs,
406
+ p99DurationMs,
407
+ };
408
+ }
409
+ /**
410
+ * Nearest-rank percentile. For a sample of size N, the P-th percentile (where
411
+ * `p` is a fraction in [0, 1]) is the value at the 1-indexed position
412
+ * `ceil(p * N)` of the sorted (ascending) sample. For N=0 returns 0. For
413
+ * small N this degenerates to the max — that's the documented behavior, not
414
+ * a bug.
415
+ */
416
+ function percentile(values, p) {
417
+ if (values.length === 0)
418
+ return 0;
419
+ const sorted = [...values].sort((a, b) => a - b);
420
+ const rank = Math.ceil(p * sorted.length);
421
+ const idx = Math.min(Math.max(rank, 1), sorted.length) - 1;
422
+ return sorted[idx] ?? 0;
423
+ }
424
+ // ─── Markdown rendering ────────────────────────────────────────────────────
425
+ /** Render `ReportData` as a Markdown document. */
426
+ function renderReport(data) {
427
+ const lines = [];
428
+ const pct = (n) => `${(n * 100).toFixed(1)}%`;
429
+ lines.push(`# API Test Report — ${data.date}`);
430
+ lines.push('');
431
+ lines.push('## 概览');
432
+ lines.push('');
433
+ lines.push('| 指标 | 值 |');
434
+ lines.push('| --- | --- |');
435
+ lines.push(`| 总接口数 | ${data.totalEndpoints} |`);
436
+ lines.push(`| 通过数 | ${data.passedCount} |`);
437
+ lines.push(`| 失败数 | ${data.failedCount} |`);
438
+ lines.push(`| Skip 数 | ${data.skipCount} |`);
439
+ lines.push(`| 平均耗时 | ${data.avgDurationMs.toFixed(0)} ms |`);
440
+ lines.push(`| P99 耗时 | ${data.p99DurationMs.toFixed(0)} ms |`);
441
+ lines.push('');
442
+ lines.push('## 按微服务分组通过率');
443
+ lines.push('');
444
+ if (data.perService.length === 0) {
445
+ lines.push('_(无微服务数据)_');
446
+ }
447
+ else {
448
+ lines.push('| 微服务 | 总数 | 通过 | 通过率 |');
449
+ lines.push('| --- | --- | --- | --- |');
450
+ for (const s of data.perService) {
451
+ lines.push(`| ${s.service} | ${s.total} | ${s.passed} | ${pct(s.passRate)} |`);
452
+ }
453
+ }
454
+ lines.push('');
455
+ lines.push('## 失败清单');
456
+ lines.push('');
457
+ if (data.failures.length === 0) {
458
+ lines.push('_(无失败用例)_');
459
+ }
460
+ else {
461
+ lines.push('| Endpoint | Service | Error |');
462
+ lines.push('| --- | --- | --- |');
463
+ for (const f of data.failures) {
464
+ lines.push(`| ${f.endpoint} | ${f.service} | ${f.error ?? ''} |`);
465
+ }
466
+ }
467
+ lines.push('');
468
+ lines.push('## Skip 清单');
469
+ lines.push('');
470
+ if (data.skips.length === 0) {
471
+ lines.push('_(无 skip 用例)_');
472
+ }
473
+ else {
474
+ lines.push('| Endpoint | Service | Skip Reason |');
475
+ lines.push('| --- | --- | --- |');
476
+ for (const s of data.skips) {
477
+ lines.push(`| ${s.endpoint} | ${s.service} | ${s.skipReason ?? ''} |`);
478
+ }
479
+ }
480
+ lines.push('');
481
+ return lines.join('\n');
482
+ }
483
+ // ─── Terminal table rendering (hand-rolled, no cli-table3) ─────────────────
484
+ /**
485
+ * Render a 2D string matrix as a fixed-width terminal table. The first row
486
+ * is treated as the header and separated from the body by a `---` divider
487
+ * line. Column widths are the max cell width in each column. Empty matrix
488
+ * returns the empty string.
489
+ */
490
+ function renderTable(rows) {
491
+ if (rows.length === 0)
492
+ return '';
493
+ const cols = rows[0].length;
494
+ const widths = new Array(cols).fill(0);
495
+ for (const row of rows) {
496
+ for (let c = 0; c < cols; c++) {
497
+ const cell = row[c] ?? '';
498
+ widths[c] = Math.max(widths[c], cell.length);
499
+ }
500
+ }
501
+ const pad = (cell, c) => (cell + ' '.repeat(widths[c])).slice(0, widths[c]);
502
+ const renderRow = (row) => row.map((cell, c) => pad(cell ?? '', c)).join(' ');
503
+ const header = renderRow(rows[0]);
504
+ const divider = widths.map((w) => '-'.repeat(w)).join(' ');
505
+ const body = rows.slice(1).map(renderRow).join('\n');
506
+ return [header, divider, body].filter((s) => s.length > 0).join('\n');
507
+ }
508
+ /** Internal helpers re-exported for tests. */
509
+ exports.__INTERNAL_REPORT = {
510
+ DEFAULT_OUTPUT_DIR,
511
+ reportFilePath,
512
+ parseJsonl,
513
+ deserializeRun,
514
+ readLatestRunByEndpoint,
515
+ readAllEndpoints,
516
+ parseFrontmatter,
517
+ aggregateReport,
518
+ percentile,
519
+ renderReport,
520
+ renderTable,
521
+ };
522
+ //# sourceMappingURL=report.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report.js","sourceRoot":"","sources":["../../src/api-test/report.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * L-T-50: snapshot — capture dep_tables row snapshots before `bru run`,
3
+ * stored as `.cgraphx/api-test/runs/<run-id>-before.json`.
4
+ *
5
+ * Part of the single-endpoint closed loop (enhance → snapshot → bru run → verify).
6
+ * Mechanical subcommand — no AI logic.
7
+ *
8
+ * Spec: docs/features/2026-07-07-api-test-skill-bruno自动化接口测试/api-test-layer2-spec.md §7.1 (snapshot is the
9
+ * realization of decision #2 option (d): skill calls snapshot before bru run, verify-prepare reads it)
10
+ */
11
+ export interface SnapshotResult {
12
+ endpoint: string;
13
+ runId: string;
14
+ depTables: string[];
15
+ snapshots: Record<string, unknown[]>;
16
+ snapshotAt: string;
17
+ beforeJsonPath: string;
18
+ errors: string[];
19
+ }
20
+ export declare function snapshot(projectRoot: string, endpoint: string, runId: string, service?: string): Promise<SnapshotResult>;
21
+ export declare const __INTERNAL_SNAPSHOT: {
22
+ DEFAULT_SERVICE: string;
23
+ RUNS_SUBDIR: string;
24
+ SNAPSHOT_LIMIT: number;
25
+ };
26
+ //# sourceMappingURL=snapshot.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snapshot.d.ts","sourceRoot":"","sources":["../../src/api-test/snapshot.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAcH,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,wBAAsB,QAAQ,CAC5B,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,MAAwB,GAChC,OAAO,CAAC,cAAc,CAAC,CAoDzB;AAuCD,eAAO,MAAM,mBAAmB;;;;CAI/B,CAAC"}