standup-mr 0.5.0 → 0.6.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.
- package/CHANGELOG.md +21 -0
- package/README.md +10 -1
- package/dist/{chunk-S2PFIUNI.js → chunk-MB7KWLUR.js} +8 -1
- package/dist/cli.js +14 -8
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -1
- package/dist/mcp/server.js +10 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,27 @@ 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.6.0] - 2026-09-02
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **Google Chat webhooks.** `--google-chat URL` on the CLI, `kind:
|
|
12
|
+
"google-chat"` in `post_standup_note`, and `chat.googleapis.com` added to
|
|
13
|
+
host inference so neither is usually needed. Google Chat takes the same
|
|
14
|
+
`{"text"}` body as Slack, so this was only ever a naming gap — but telling
|
|
15
|
+
someone to pass `--slack` for a Google Chat webhook is a hack, not a
|
|
16
|
+
feature.
|
|
17
|
+
|
|
18
|
+
Anything else that accepts a Slack-shaped body — Mattermost, Rocket.Chat, an
|
|
19
|
+
n8n or Zapier endpoint — already works by passing the slack kind.
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
- `standup post` builds its flags and its error message from the list of
|
|
24
|
+
implemented channels instead of a hand-written pair, so adding a channel is
|
|
25
|
+
one map entry. The MCP tool's `kind` enum is now tested against that same
|
|
26
|
+
list: a channel can no longer reach the CLI and quietly miss MCP.
|
|
27
|
+
|
|
7
28
|
## [0.5.0] - 2026-09-02
|
|
8
29
|
|
|
9
30
|
### Added
|
package/README.md
CHANGED
|
@@ -30,6 +30,10 @@ npx standup-mr fetch --markdown # structured digest
|
|
|
30
30
|
npx standup-mr fetch --lang tr # Turkish date labels
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
+
```bash
|
|
34
|
+
npx standup-mr fetch --markdown | npx standup-mr post --google-chat "$URL"
|
|
35
|
+
```
|
|
36
|
+
|
|
33
37
|
### Identity
|
|
34
38
|
|
|
35
39
|
| | GitHub | GitLab |
|
|
@@ -123,13 +127,18 @@ The server exposes three tools:
|
|
|
123
127
|
|---|---|
|
|
124
128
|
| `get_standup_data` | Reads the provider and returns the report as JSON. Optional `provider`, `host`, `lang`. |
|
|
125
129
|
| `get_note_instructions` | Returns the note-writing rules, so the assistant can write the note the way the skill would. |
|
|
126
|
-
| `post_standup_note` | Posts a finished note to a Slack or
|
|
130
|
+
| `post_standup_note` | Posts a finished note to a Slack, Discord or Google Chat webhook. |
|
|
127
131
|
|
|
128
132
|
`post_standup_note` reads the webhook URL from `STANDUP_WEBHOOK_URL` and never
|
|
129
133
|
takes it as an argument — anyone holding that URL can post to the channel, so
|
|
130
134
|
it belongs with the tokens, not in a transcript. The payload shape is inferred
|
|
131
135
|
from the URL host; `kind` is only needed when a proxy hides it.
|
|
132
136
|
|
|
137
|
+
Three shapes are implemented: `slack` and `google-chat` both post `{"text"}`,
|
|
138
|
+
`discord` posts `{"content"}`. Anything else that accepts a Slack-shaped body —
|
|
139
|
+
Mattermost, Rocket.Chat, an n8n or Zapier endpoint — works today by passing
|
|
140
|
+
`kind: "slack"`, or `--slack URL` on the CLI.
|
|
141
|
+
|
|
133
142
|
Outside MCP, the same rules are available on stdout:
|
|
134
143
|
|
|
135
144
|
```bash
|
|
@@ -187,7 +187,12 @@ function ghToken(host) {
|
|
|
187
187
|
}
|
|
188
188
|
|
|
189
189
|
// src/notify/notify.constants.ts
|
|
190
|
-
var PAYLOAD_FIELD = {
|
|
190
|
+
var PAYLOAD_FIELD = {
|
|
191
|
+
slack: "text",
|
|
192
|
+
discord: "content",
|
|
193
|
+
"google-chat": "text"
|
|
194
|
+
};
|
|
195
|
+
var WEBHOOK_KINDS = Object.keys(PAYLOAD_FIELD);
|
|
191
196
|
|
|
192
197
|
// src/notify/notify.ts
|
|
193
198
|
async function postWebhook(url, text, kind = "slack", fetchImpl = fetch) {
|
|
@@ -208,6 +213,7 @@ async function postWebhook(url, text, kind = "slack", fetchImpl = fetch) {
|
|
|
208
213
|
}
|
|
209
214
|
var WEBHOOK_HOSTS = {
|
|
210
215
|
"hooks.slack.com": "slack",
|
|
216
|
+
"chat.googleapis.com": "google-chat",
|
|
211
217
|
"discord.com": "discord",
|
|
212
218
|
"discordapp.com": "discord",
|
|
213
219
|
"ptb.discord.com": "discord",
|
|
@@ -1197,6 +1203,7 @@ export {
|
|
|
1197
1203
|
glabToken,
|
|
1198
1204
|
ghHosts,
|
|
1199
1205
|
ghToken,
|
|
1206
|
+
WEBHOOK_KINDS,
|
|
1200
1207
|
postWebhook,
|
|
1201
1208
|
inferWebhookKind,
|
|
1202
1209
|
buildUrl,
|
package/dist/cli.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
ConfigError,
|
|
4
|
+
WEBHOOK_KINDS,
|
|
4
5
|
buildReport,
|
|
5
6
|
connect,
|
|
6
7
|
findPackageRoot,
|
|
7
8
|
postWebhook,
|
|
8
9
|
readStandupSkillBody,
|
|
9
10
|
toMarkdown
|
|
10
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-MB7KWLUR.js";
|
|
11
12
|
|
|
12
13
|
// src/cli/cli.ts
|
|
13
14
|
import { realpathSync } from "fs";
|
|
@@ -21,7 +22,7 @@ var USAGE = `standup \u2014 standup notes from merge request state
|
|
|
21
22
|
Usage:
|
|
22
23
|
standup fetch [--provider github|gitlab] [--host H] [--token T]
|
|
23
24
|
[--lang en|tr] [--markdown]
|
|
24
|
-
standup post (--slack URL | --discord URL) [--text TEXT]
|
|
25
|
+
standup post (--slack URL | --discord URL | --google-chat URL) [--text TEXT]
|
|
25
26
|
standup mcp
|
|
26
27
|
standup instructions
|
|
27
28
|
|
|
@@ -73,22 +74,27 @@ async function main(argv) {
|
|
|
73
74
|
const { values } = parseArgs({
|
|
74
75
|
args: rest,
|
|
75
76
|
options: {
|
|
76
|
-
|
|
77
|
-
|
|
77
|
+
...Object.fromEntries(
|
|
78
|
+
WEBHOOK_KINDS.map((kind2) => [kind2, { type: "string" }])
|
|
79
|
+
),
|
|
78
80
|
text: { type: "string", default: "-" }
|
|
79
81
|
}
|
|
80
82
|
});
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
83
|
+
const urls = values;
|
|
84
|
+
const kind = WEBHOOK_KINDS.find((candidate) => urls[candidate]);
|
|
85
|
+
if (!kind) {
|
|
86
|
+
const flags = WEBHOOK_KINDS.map((candidate) => `--${candidate} URL`).join(" | ");
|
|
87
|
+
process.stderr.write(`Pass one of: ${flags}.
|
|
88
|
+
`);
|
|
84
89
|
return 1;
|
|
85
90
|
}
|
|
91
|
+
const url = urls[kind];
|
|
86
92
|
const text = values.text === "-" ? await readStdin() : values.text;
|
|
87
93
|
if (!text.trim()) {
|
|
88
94
|
process.stderr.write("Nothing to post: empty text.\n");
|
|
89
95
|
return 1;
|
|
90
96
|
}
|
|
91
|
-
await postWebhook(url, text,
|
|
97
|
+
await postWebhook(url, text, kind);
|
|
92
98
|
return 0;
|
|
93
99
|
}
|
|
94
100
|
if (command === "mcp") {
|
package/dist/index.d.ts
CHANGED
|
@@ -125,6 +125,7 @@ declare function previousActiveDays(eventDates: Set<string>, today: Date): Array
|
|
|
125
125
|
declare const PAYLOAD_FIELD: {
|
|
126
126
|
readonly slack: "text";
|
|
127
127
|
readonly discord: "content";
|
|
128
|
+
readonly 'google-chat': "text";
|
|
128
129
|
};
|
|
129
130
|
|
|
130
131
|
type WebhookKind = keyof typeof PAYLOAD_FIELD;
|
package/dist/index.js
CHANGED
package/dist/mcp/server.js
CHANGED
|
@@ -32,7 +32,12 @@ function packageVersion(moduleUrl) {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
// src/notify/notify.constants.ts
|
|
35
|
-
var PAYLOAD_FIELD = {
|
|
35
|
+
var PAYLOAD_FIELD = {
|
|
36
|
+
slack: "text",
|
|
37
|
+
discord: "content",
|
|
38
|
+
"google-chat": "text"
|
|
39
|
+
};
|
|
40
|
+
var WEBHOOK_KINDS = Object.keys(PAYLOAD_FIELD);
|
|
36
41
|
|
|
37
42
|
// src/notify/notify.ts
|
|
38
43
|
async function postWebhook(url, text2, kind = "slack", fetchImpl = fetch) {
|
|
@@ -53,6 +58,7 @@ async function postWebhook(url, text2, kind = "slack", fetchImpl = fetch) {
|
|
|
53
58
|
}
|
|
54
59
|
var WEBHOOK_HOSTS = {
|
|
55
60
|
"hooks.slack.com": "slack",
|
|
61
|
+
"chat.googleapis.com": "google-chat",
|
|
56
62
|
"discord.com": "discord",
|
|
57
63
|
"discordapp.com": "discord",
|
|
58
64
|
"ptb.discord.com": "discord",
|
|
@@ -1117,13 +1123,13 @@ async function runStandupTool(args, collector = collect) {
|
|
|
1117
1123
|
var WEBHOOK_URL_ENV = "STANDUP_WEBHOOK_URL";
|
|
1118
1124
|
var POST_TOOL_DESCRIPTION = `Posts a finished standup note to a chat webhook. This one has a side effect: it sends a message other people will see, so only call it on a note the user has agreed to send.
|
|
1119
1125
|
|
|
1120
|
-
The webhook URL is read from ${WEBHOOK_URL_ENV}, never from an argument \u2014 a webhook URL is a credential, since anyone holding it can post to the channel. Slack and Discord payload shapes are supported; the shape is inferred from the URL host, and \`kind\` is only needed for a proxied or self-hosted endpoint whose host gives nothing away. A missing URL, an unrecognised host, or a webhook that rejects the message comes back as an error rather than a silent success.`;
|
|
1126
|
+
The webhook URL is read from ${WEBHOOK_URL_ENV}, never from an argument \u2014 a webhook URL is a credential, since anyone holding it can post to the channel. Slack and Discord payload shapes are supported; the shape is inferred from the URL host, and \`kind\` is only needed for a proxied or self-hosted endpoint whose host gives nothing away \u2014 Mattermost and Rocket.Chat take the slack shape. A missing URL, an unrecognised host, or a webhook that rejects the message comes back as an error rather than a silent success.`;
|
|
1121
1127
|
var POST_TOOL_SCHEMA = {
|
|
1122
1128
|
text: z.string().min(1).describe(
|
|
1123
1129
|
"The note to post, as the chat should render it. Slack and Discord both take Markdown, so send the written note rather than the raw JSON from get_standup_data."
|
|
1124
1130
|
),
|
|
1125
|
-
kind: z.enum(["slack", "discord"]).optional().describe(
|
|
1126
|
-
'Which payload shape to send: slack
|
|
1131
|
+
kind: z.enum(["slack", "discord", "google-chat"]).optional().describe(
|
|
1132
|
+
'Which payload shape to send: slack and google-chat post {"text"}, discord posts {"content"}. Omit it \u2014 the shape is inferred from the webhook host. Pass it only when the host is not recognisable, e.g. a proxy in front of the real webhook.'
|
|
1127
1133
|
)
|
|
1128
1134
|
};
|
|
1129
1135
|
var INSTRUCTIONS_TOOL_DESCRIPTION = "Returns the note-writing rules \u2014 the playbook for turning get_standup_data output into a standup note a person would actually say out loud: how to group the previous day by theme, how to derive today from open merge requests, and how to report a blocker from its job log. Read-only, no arguments, no network. Call it once before writing the first note; the rules do not change between calls.";
|