standup-mr 0.1.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/LICENSE +21 -0
- package/README.md +72 -0
- package/dist/chunk-SKCJT2AY.js +551 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +105 -0
- package/dist/index.d.ts +151 -0
- package/dist/index.js +37 -0
- package/dist/mcp/server.js +468 -0
- package/mcp/README.md +33 -0
- package/package.json +54 -0
- package/skill/SKILL.md +102 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
ConfigError,
|
|
4
|
+
GitLabProvider,
|
|
5
|
+
buildReport,
|
|
6
|
+
glabHosts,
|
|
7
|
+
glabToken,
|
|
8
|
+
postWebhook,
|
|
9
|
+
resolveHost,
|
|
10
|
+
resolveToken,
|
|
11
|
+
toMarkdown
|
|
12
|
+
} from "./chunk-SKCJT2AY.js";
|
|
13
|
+
|
|
14
|
+
// src/cli/cli.ts
|
|
15
|
+
import { pathToFileURL } from "url";
|
|
16
|
+
import { parseArgs } from "util";
|
|
17
|
+
|
|
18
|
+
// src/cli/cli.constants.ts
|
|
19
|
+
var USAGE = `standup \u2014 standup notes from merge request state
|
|
20
|
+
|
|
21
|
+
Usage:
|
|
22
|
+
standup fetch [--host H] [--token T] [--lang en|tr] [--markdown]
|
|
23
|
+
standup post (--slack URL | --discord URL) [--text TEXT]
|
|
24
|
+
|
|
25
|
+
Credentials resolve as: flag, then GITLAB_HOST / GITLAB_TOKEN, then glab config.
|
|
26
|
+
--text defaults to '-', meaning read stdin.`;
|
|
27
|
+
|
|
28
|
+
// src/cli/cli.ts
|
|
29
|
+
async function readStdin() {
|
|
30
|
+
const chunks = [];
|
|
31
|
+
for await (const chunk of process.stdin) chunks.push(Buffer.from(chunk));
|
|
32
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
33
|
+
}
|
|
34
|
+
function connect(values) {
|
|
35
|
+
const host = resolveHost(values.host, process.env.GITLAB_HOST, glabHosts());
|
|
36
|
+
const token = resolveToken(host, values.token, process.env.GITLAB_TOKEN, glabToken);
|
|
37
|
+
return new GitLabProvider(host, token);
|
|
38
|
+
}
|
|
39
|
+
async function main(argv) {
|
|
40
|
+
const [command, ...rest] = argv;
|
|
41
|
+
if (!command || command === "--help" || command === "-h") {
|
|
42
|
+
process.stdout.write(`${USAGE}
|
|
43
|
+
`);
|
|
44
|
+
return command ? 0 : 1;
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
if (command === "fetch") {
|
|
48
|
+
const { values } = parseArgs({
|
|
49
|
+
args: rest,
|
|
50
|
+
options: {
|
|
51
|
+
host: { type: "string" },
|
|
52
|
+
token: { type: "string" },
|
|
53
|
+
lang: { type: "string", default: "en" },
|
|
54
|
+
markdown: { type: "boolean", default: false }
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
const report = await buildReport(connect(values), /* @__PURE__ */ new Date(), values.lang);
|
|
58
|
+
const output = values.markdown ? toMarkdown(report, values.lang) : JSON.stringify(report, null, 1);
|
|
59
|
+
process.stdout.write(`${output}
|
|
60
|
+
`);
|
|
61
|
+
return 0;
|
|
62
|
+
}
|
|
63
|
+
if (command === "post") {
|
|
64
|
+
const { values } = parseArgs({
|
|
65
|
+
args: rest,
|
|
66
|
+
options: {
|
|
67
|
+
slack: { type: "string" },
|
|
68
|
+
discord: { type: "string" },
|
|
69
|
+
text: { type: "string", default: "-" }
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
const url = values.slack ?? values.discord;
|
|
73
|
+
if (!url) {
|
|
74
|
+
process.stderr.write("Pass --slack URL or --discord URL.\n");
|
|
75
|
+
return 1;
|
|
76
|
+
}
|
|
77
|
+
const text = values.text === "-" ? await readStdin() : values.text;
|
|
78
|
+
if (!text.trim()) {
|
|
79
|
+
process.stderr.write("Nothing to post: empty text.\n");
|
|
80
|
+
return 1;
|
|
81
|
+
}
|
|
82
|
+
await postWebhook(url, text, values.slack ? "slack" : "discord");
|
|
83
|
+
return 0;
|
|
84
|
+
}
|
|
85
|
+
process.stderr.write(`Unknown command: ${command}
|
|
86
|
+
|
|
87
|
+
${USAGE}
|
|
88
|
+
`);
|
|
89
|
+
return 1;
|
|
90
|
+
} catch (error) {
|
|
91
|
+
const message = error instanceof ConfigError || error instanceof Error ? error.message : String(error);
|
|
92
|
+
process.stderr.write(`${message}
|
|
93
|
+
`);
|
|
94
|
+
return 1;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
var entry = process.argv[1];
|
|
98
|
+
if (entry && import.meta.url === pathToFileURL(entry).href) {
|
|
99
|
+
main(process.argv.slice(2)).then((code) => {
|
|
100
|
+
process.exitCode = code;
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
export {
|
|
104
|
+
main
|
|
105
|
+
};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
interface BucketInput {
|
|
2
|
+
draft: boolean;
|
|
3
|
+
pipeline: string | null;
|
|
4
|
+
unresolved: number;
|
|
5
|
+
updated: string;
|
|
6
|
+
}
|
|
7
|
+
interface PipelineInput {
|
|
8
|
+
project: string;
|
|
9
|
+
pipeline: string | null;
|
|
10
|
+
pipelineMissing?: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
type FetchLike = (url: string, init?: RequestInit) => Promise<Response>;
|
|
14
|
+
type Bucket = 'ready' | 'blocked' | 'draft' | 'stale';
|
|
15
|
+
interface Identity {
|
|
16
|
+
id: number;
|
|
17
|
+
username: string;
|
|
18
|
+
}
|
|
19
|
+
interface ActivityEvent {
|
|
20
|
+
at: string;
|
|
21
|
+
action: string;
|
|
22
|
+
project: string;
|
|
23
|
+
targetType: string;
|
|
24
|
+
title: string;
|
|
25
|
+
branch: string;
|
|
26
|
+
commits: number;
|
|
27
|
+
commitTitle: string;
|
|
28
|
+
}
|
|
29
|
+
interface MergeRequest {
|
|
30
|
+
project: string;
|
|
31
|
+
projectId: number;
|
|
32
|
+
iid: number;
|
|
33
|
+
title: string;
|
|
34
|
+
draft: boolean;
|
|
35
|
+
branch: string;
|
|
36
|
+
target: string;
|
|
37
|
+
updated: string;
|
|
38
|
+
url: string;
|
|
39
|
+
mergeStatus: string | null;
|
|
40
|
+
pipeline: string | null;
|
|
41
|
+
pipelineId: number | null;
|
|
42
|
+
unresolved: number;
|
|
43
|
+
pipelineMissing: boolean;
|
|
44
|
+
bucket: Bucket;
|
|
45
|
+
}
|
|
46
|
+
interface Review {
|
|
47
|
+
project: string;
|
|
48
|
+
iid: number;
|
|
49
|
+
title: string;
|
|
50
|
+
author: string;
|
|
51
|
+
updated: string;
|
|
52
|
+
draft: boolean;
|
|
53
|
+
url: string;
|
|
54
|
+
fresh: boolean;
|
|
55
|
+
approvedByMe: boolean;
|
|
56
|
+
}
|
|
57
|
+
interface Blocker {
|
|
58
|
+
project: string;
|
|
59
|
+
mr: number;
|
|
60
|
+
title: string;
|
|
61
|
+
job: string;
|
|
62
|
+
stage: string;
|
|
63
|
+
url: string;
|
|
64
|
+
errors: string[];
|
|
65
|
+
}
|
|
66
|
+
interface StandupReport {
|
|
67
|
+
user: string;
|
|
68
|
+
today: {
|
|
69
|
+
date: string;
|
|
70
|
+
label: string;
|
|
71
|
+
};
|
|
72
|
+
previous: {
|
|
73
|
+
date: string | null;
|
|
74
|
+
label: string | null;
|
|
75
|
+
gapDays: number | null;
|
|
76
|
+
eventCount: number;
|
|
77
|
+
};
|
|
78
|
+
previousEvents: ActivityEvent[];
|
|
79
|
+
todayEvents: ActivityEvent[];
|
|
80
|
+
myMrs: MergeRequest[];
|
|
81
|
+
reviews: Review[];
|
|
82
|
+
reviewPendingCount: number;
|
|
83
|
+
blockers: Blocker[];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
declare function classify(mr: BucketInput, today: Date, staleDays?: number): Bucket;
|
|
87
|
+
declare function markMissingPipelines(mrs: PipelineInput[]): void;
|
|
88
|
+
|
|
89
|
+
declare const STALE_DAYS = 14;
|
|
90
|
+
|
|
91
|
+
declare class ConfigError extends Error {
|
|
92
|
+
constructor(message: string);
|
|
93
|
+
}
|
|
94
|
+
declare function resolveHost(cliHost?: string, envHost?: string, glabHostList?: string[]): string;
|
|
95
|
+
declare function resolveToken(host: string, cliToken?: string, envToken?: string, glabLookup?: (host: string) => string): string;
|
|
96
|
+
declare function glabHosts(): string[];
|
|
97
|
+
declare function glabToken(host: string): string;
|
|
98
|
+
|
|
99
|
+
declare function isoDay(day: Date): string;
|
|
100
|
+
declare function label(day: Date, lang?: string): string;
|
|
101
|
+
declare function previousActiveDay(eventDates: Set<string>, today: Date): {
|
|
102
|
+
date: string | null;
|
|
103
|
+
gapDays: number | null;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
declare const PAYLOAD_FIELD: {
|
|
107
|
+
readonly slack: "text";
|
|
108
|
+
readonly discord: "content";
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
type WebhookKind = keyof typeof PAYLOAD_FIELD;
|
|
112
|
+
|
|
113
|
+
declare function postWebhook(url: string, text: string, kind?: WebhookKind, fetchImpl?: FetchLike): Promise<void>;
|
|
114
|
+
|
|
115
|
+
interface Provider {
|
|
116
|
+
getIdentity(): Promise<Identity>;
|
|
117
|
+
getEvents(since: Date): Promise<ActivityEvent[]>;
|
|
118
|
+
getMyMrs(today: Date): Promise<MergeRequest[]>;
|
|
119
|
+
getReviews(uid: number, today: Date): Promise<Review[]>;
|
|
120
|
+
getBlockers(mrs: MergeRequest[]): Promise<Blocker[]>;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
type Params = Record<string, string | number>;
|
|
124
|
+
declare class GitLabProvider implements Provider {
|
|
125
|
+
readonly host: string;
|
|
126
|
+
readonly api: string;
|
|
127
|
+
private readonly token;
|
|
128
|
+
private readonly fetchImpl;
|
|
129
|
+
constructor(host: string, token: string, fetchImpl?: FetchLike);
|
|
130
|
+
private url;
|
|
131
|
+
getJson<T>(path: string, params?: Params): Promise<T | null>;
|
|
132
|
+
getText(path: string): Promise<string>;
|
|
133
|
+
getPaged<T>(path: string, params?: Params, cap?: number): Promise<T[]>;
|
|
134
|
+
getIdentity(): Promise<Identity>;
|
|
135
|
+
projectPath(projectId: number): Promise<string>;
|
|
136
|
+
getEvents(since: Date): Promise<ActivityEvent[]>;
|
|
137
|
+
private countUnresolved;
|
|
138
|
+
private shapeMr;
|
|
139
|
+
getMyMrs(today: Date): Promise<MergeRequest[]>;
|
|
140
|
+
private approvedByMe;
|
|
141
|
+
getReviews(uid: number, today: Date): Promise<Review[]>;
|
|
142
|
+
getBlockers(mrs: MergeRequest[]): Promise<Blocker[]>;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
declare function toMarkdown(report: StandupReport, lang?: string): string;
|
|
146
|
+
|
|
147
|
+
declare function buildReport(provider: Provider, today: Date, lang?: string, lookbackDays?: number): Promise<StandupReport>;
|
|
148
|
+
|
|
149
|
+
declare function extractErrors(rawTrace: string, limit?: number): string[];
|
|
150
|
+
|
|
151
|
+
export { type ActivityEvent, type Blocker, type Bucket, ConfigError, type FetchLike, GitLabProvider, type Identity, type MergeRequest, type Provider, type Review, STALE_DAYS, type StandupReport, buildReport, classify, extractErrors, glabHosts, glabToken, isoDay, label, markMissingPipelines, postWebhook, previousActiveDay, resolveHost, resolveToken, toMarkdown };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
ConfigError,
|
|
4
|
+
GitLabProvider,
|
|
5
|
+
STALE_DAYS,
|
|
6
|
+
buildReport,
|
|
7
|
+
classify,
|
|
8
|
+
extractErrors,
|
|
9
|
+
glabHosts,
|
|
10
|
+
glabToken,
|
|
11
|
+
isoDay,
|
|
12
|
+
label,
|
|
13
|
+
markMissingPipelines,
|
|
14
|
+
postWebhook,
|
|
15
|
+
previousActiveDay,
|
|
16
|
+
resolveHost,
|
|
17
|
+
resolveToken,
|
|
18
|
+
toMarkdown
|
|
19
|
+
} from "./chunk-SKCJT2AY.js";
|
|
20
|
+
export {
|
|
21
|
+
ConfigError,
|
|
22
|
+
GitLabProvider,
|
|
23
|
+
STALE_DAYS,
|
|
24
|
+
buildReport,
|
|
25
|
+
classify,
|
|
26
|
+
extractErrors,
|
|
27
|
+
glabHosts,
|
|
28
|
+
glabToken,
|
|
29
|
+
isoDay,
|
|
30
|
+
label,
|
|
31
|
+
markMissingPipelines,
|
|
32
|
+
postWebhook,
|
|
33
|
+
previousActiveDay,
|
|
34
|
+
resolveHost,
|
|
35
|
+
resolveToken,
|
|
36
|
+
toMarkdown
|
|
37
|
+
};
|