standup-mr 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,23 @@ All notable changes to standup-mr are recorded here. The format follows
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project uses
5
5
  [semantic versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [0.2.1] - 2026-08-31
8
+
9
+ ### Fixed
10
+
11
+ - A pull request whose CI timed out was reported as ready to merge. GitHub
12
+ reports a timed-out job with the conclusion `cancelled`, not `timed_out`, so
13
+ it normalised to a `canceled` pipeline — and a canceled pipeline was treated
14
+ as a green light. A canceled pipeline now blocks, on both providers: GitLab's
15
+ `canceled` status took the same path.
16
+ - Every error line in a GitHub blocker was reported twice. GitHub Actions echoes
17
+ a step's script inside a `##[group]Run …` block before running it, and those
18
+ echoed lines matched the error patterns. The body of a `Run` group is now
19
+ suppressed; other groups are still scanned, since they carry real output.
20
+
21
+ Both were found by testing 0.2.0 against the real GitHub Actions API — neither
22
+ was reachable from the test fixtures.
23
+
7
24
  ## [0.2.0] - 2026-08-31
8
25
 
9
26
  GitHub joins GitLab as a first-class provider, and the previous-day report
@@ -80,7 +80,7 @@ function previousActiveDays(eventDates, today) {
80
80
  // src/buckets/buckets.ts
81
81
  function classify(mr, today, staleDays = STALE_DAYS) {
82
82
  if (mr.draft) return "draft";
83
- if (mr.pipeline === "failed" || mr.unresolved > 0) return "blocked";
83
+ if (mr.pipeline === "failed" || mr.pipeline === "canceled" || mr.unresolved > 0) return "blocked";
84
84
  const age = Math.round(
85
85
  (Date.parse(`${isoDay(today)}T00:00:00Z`) - Date.parse(`${mr.updated}T00:00:00Z`)) / MS_PER_DAY
86
86
  );
@@ -274,7 +274,9 @@ var NOISE = /^(Cleaning up|Job failed: exit status|ERROR: Job failed|Uploading a
274
274
  var MAX_LINE = 200;
275
275
  var TIMESTAMP = /^\d{4}-\d{2}-\d{2}T[\d:.]+Z\s+/;
276
276
  var ERROR_MARKER = /^##\[error\]\s*/;
277
- var GROUP = /^##\[(group|endgroup)\]/;
277
+ var GROUP_START = /^##\[group\]/;
278
+ var GROUP_END = /^##\[endgroup\]/;
279
+ var RUN_GROUP = /^##\[group\]Run\s/;
278
280
 
279
281
  // src/trace/trace.ts
280
282
  function extractErrors(rawTrace, limit = 8) {
@@ -282,9 +284,18 @@ function extractErrors(rawTrace, limit = 8) {
282
284
  const cleaned = rawTrace.replace(ANSI, "").replace(SECTION, "");
283
285
  const seen = /* @__PURE__ */ new Set();
284
286
  const hits = [];
287
+ let suppressing = false;
285
288
  for (const rawLine of cleaned.split("\n")) {
286
289
  const stamped = rawLine.trim().replace(TIMESTAMP, "");
287
- if (GROUP.test(stamped)) continue;
290
+ if (GROUP_START.test(stamped)) {
291
+ suppressing = RUN_GROUP.test(stamped);
292
+ continue;
293
+ }
294
+ if (GROUP_END.test(stamped)) {
295
+ suppressing = false;
296
+ continue;
297
+ }
298
+ if (suppressing) continue;
288
299
  if (!stamped || NOISE.test(stamped) || !SIGNAL.test(stamped)) continue;
289
300
  const line = stamped.replace(ERROR_MARKER, "").trim();
290
301
  if (!line) continue;
package/dist/cli.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  connect,
6
6
  postWebhook,
7
7
  toMarkdown
8
- } from "./chunk-MIQ7FQS6.js";
8
+ } from "./chunk-DL2P3XR6.js";
9
9
 
10
10
  // src/cli/cli.ts
11
11
  import { realpathSync } from "fs";
package/dist/index.js CHANGED
@@ -36,7 +36,7 @@ import {
36
36
  resolveToken,
37
37
  toMarkdown,
38
38
  unreachable
39
- } from "./chunk-MIQ7FQS6.js";
39
+ } from "./chunk-DL2P3XR6.js";
40
40
  export {
41
41
  ApiError,
42
42
  ConfigError,
@@ -176,7 +176,7 @@ function previousActiveDays(eventDates, today) {
176
176
  // src/buckets/buckets.ts
177
177
  function classify(mr, today, staleDays = STALE_DAYS) {
178
178
  if (mr.draft) return "draft";
179
- if (mr.pipeline === "failed" || mr.unresolved > 0) return "blocked";
179
+ if (mr.pipeline === "failed" || mr.pipeline === "canceled" || mr.unresolved > 0) return "blocked";
180
180
  const age = Math.round(
181
181
  (Date.parse(`${isoDay(today)}T00:00:00Z`) - Date.parse(`${mr.updated}T00:00:00Z`)) / MS_PER_DAY
182
182
  );
@@ -197,7 +197,9 @@ var NOISE = /^(Cleaning up|Job failed: exit status|ERROR: Job failed|Uploading a
197
197
  var MAX_LINE = 200;
198
198
  var TIMESTAMP = /^\d{4}-\d{2}-\d{2}T[\d:.]+Z\s+/;
199
199
  var ERROR_MARKER = /^##\[error\]\s*/;
200
- var GROUP = /^##\[(group|endgroup)\]/;
200
+ var GROUP_START = /^##\[group\]/;
201
+ var GROUP_END = /^##\[endgroup\]/;
202
+ var RUN_GROUP = /^##\[group\]Run\s/;
201
203
 
202
204
  // src/trace/trace.ts
203
205
  function extractErrors(rawTrace, limit = 8) {
@@ -205,9 +207,18 @@ function extractErrors(rawTrace, limit = 8) {
205
207
  const cleaned = rawTrace.replace(ANSI, "").replace(SECTION, "");
206
208
  const seen = /* @__PURE__ */ new Set();
207
209
  const hits = [];
210
+ let suppressing = false;
208
211
  for (const rawLine of cleaned.split("\n")) {
209
212
  const stamped = rawLine.trim().replace(TIMESTAMP, "");
210
- if (GROUP.test(stamped)) continue;
213
+ if (GROUP_START.test(stamped)) {
214
+ suppressing = RUN_GROUP.test(stamped);
215
+ continue;
216
+ }
217
+ if (GROUP_END.test(stamped)) {
218
+ suppressing = false;
219
+ continue;
220
+ }
221
+ if (suppressing) continue;
211
222
  if (!stamped || NOISE.test(stamped) || !SIGNAL.test(stamped)) continue;
212
223
  const line = stamped.replace(ERROR_MARKER, "").trim();
213
224
  if (!line) continue;
@@ -955,7 +966,7 @@ async function collect(options = {}) {
955
966
  async function main() {
956
967
  const { McpServer } = await import("@modelcontextprotocol/sdk/server/mcp.js");
957
968
  const { StdioServerTransport } = await import("@modelcontextprotocol/sdk/server/stdio.js");
958
- const server = new McpServer({ name: "standup-mr", version: "0.2.0" });
969
+ const server = new McpServer({ name: "standup-mr", version: "0.2.1" });
959
970
  server.tool(
960
971
  "get_standup_data",
961
972
  "Collect merge-request-based standup data from GitLab or GitHub. Returns the previous working day activity, open merge requests or pull requests bucketed by state (ready / blocked / draft / stale), pending reviews, and the error lines from any failed pipeline or check.",
package/package.json CHANGED
@@ -1,11 +1,19 @@
1
1
  {
2
2
  "name": "standup-mr",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Standup notes from merge request state, not commit logs.",
5
- "keywords": ["standup", "gitlab", "github", "merge-request", "pull-request", "cli", "mcp"],
5
+ "keywords": [
6
+ "standup",
7
+ "gitlab",
8
+ "github",
9
+ "merge-request",
10
+ "pull-request",
11
+ "cli",
12
+ "mcp"
13
+ ],
6
14
  "license": "MIT",
7
15
  "author": {
8
- "name": "İlker Balcılar",
16
+ "name": "\u0130lker Balc\u0131lar",
9
17
  "email": "ilkerbalcilartr@gmail.com"
10
18
  },
11
19
  "repository": {
@@ -27,11 +35,22 @@
27
35
  }
28
36
  ],
29
37
  "type": "module",
30
- "engines": { "node": ">=20" },
31
- "bin": { "standup": "./dist/cli.js" },
38
+ "engines": {
39
+ "node": ">=20"
40
+ },
41
+ "bin": {
42
+ "standup": "./dist/cli.js"
43
+ },
32
44
  "main": "./dist/index.js",
33
45
  "types": "./dist/index.d.ts",
34
- "files": ["dist", "skills", "mcp/README.md", "README.md", "CHANGELOG.md", "LICENSE"],
46
+ "files": [
47
+ "dist",
48
+ "skills",
49
+ "mcp/README.md",
50
+ "README.md",
51
+ "CHANGELOG.md",
52
+ "LICENSE"
53
+ ],
35
54
  "scripts": {
36
55
  "build": "tsup",
37
56
  "test": "bun test",
@@ -50,6 +69,8 @@
50
69
  "@modelcontextprotocol/sdk": "^1.0.0"
51
70
  },
52
71
  "peerDependenciesMeta": {
53
- "@modelcontextprotocol/sdk": { "optional": true }
72
+ "@modelcontextprotocol/sdk": {
73
+ "optional": true
74
+ }
54
75
  }
55
76
  }