@gaodes/pi-gitlab 0.3.0 → 0.4.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/.primecodex.json +3 -3
- package/CHANGELOG.md +40 -1
- package/README.md +4 -4
- package/package.json +1 -1
- package/skills/gitlab-assistant/SKILL.md +1 -1
- package/skills/gitlab-badge/SKILL.md +77 -0
- package/skills/gitlab-ci/SKILL.md +69 -0
- package/skills/gitlab-container/SKILL.md +67 -0
- package/skills/gitlab-discussion/SKILL.md +97 -0
- package/skills/gitlab-file/SKILL.md +86 -0
- package/skills/gitlab-group/SKILL.md +92 -0
- package/skills/gitlab-label/SKILL.md +64 -0
- package/skills/gitlab-milestone/SKILL.md +68 -0
- package/skills/gitlab-protected-branch/SKILL.md +61 -0
- package/skills/gitlab-release/SKILL.md +1 -1
- package/skills/gitlab-repo/SKILL.md +72 -0
- package/skills/gitlab-search/SKILL.md +47 -0
- package/skills/gitlab-variable/SKILL.md +69 -0
- package/skills/gitlab-vulnerability/SKILL.md +74 -0
- package/skills/gitlab-webhook/SKILL.md +79 -0
- package/skills/gitlab-wiki/SKILL.md +70 -0
- package/skills/gitlab-workflow/SKILL.md +1 -1
- package/src/commands/gitlab-doctor.ts +12 -4
- package/src/config/loader.ts +13 -1
- package/src/config/types.ts +4 -4
- package/src/index.ts +14 -1
- package/src/lib/env.ts +2 -3
- package/src/lib/projectCache.ts +17 -1
- package/src/lib/schemas.ts +1 -1
- package/src/tools/gitlab_ci_lint.ts +138 -0
- package/src/tools/gitlab_force_push_safe.ts +18 -10
- package/src/tools/gitlab_issue_close.ts +16 -4
- package/src/tools/gitlab_issue_create.ts +19 -5
- package/src/tools/gitlab_issue_list.ts +5 -5
- package/src/tools/gitlab_job_logs.ts +5 -5
- package/src/tools/gitlab_mr_bulk_approve.ts +4 -2
- package/src/tools/gitlab_mr_create.ts +1 -1
- package/src/tools/gitlab_mr_list.ts +5 -5
- package/src/tools/gitlab_mr_merge.ts +2 -4
- package/src/tools/gitlab_mr_view.ts +5 -5
- package/src/tools/gitlab_pipeline_run.ts +29 -10
- package/src/tools/gitlab_pipeline_status.ts +5 -5
- package/src/tools/gitlab_project_resolve.ts +5 -5
- package/src/tools/gitlab_release_create.ts +2 -3
- package/src/tools/gitlab_release_list.ts +4 -9
- package/src/tools/gitlab_release_view.ts +8 -3
- package/src/tools/gitlab_repo_view.ts +116 -0
- package/src/tools/gitlab_search_query.ts +221 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ExtensionAPI,
|
|
3
|
+
ExtensionContext,
|
|
4
|
+
} from "@earendil-works/pi-coding-agent";
|
|
5
|
+
import { Type } from "typebox";
|
|
6
|
+
import { requireSetup, setupRequiredResult } from "../lib/errors.js";
|
|
7
|
+
import { glab } from "../lib/glab.js";
|
|
8
|
+
import { resolveProject } from "../lib/projectFallback.js";
|
|
9
|
+
import { resolveProjectId } from "../lib/resolveProjectId.js";
|
|
10
|
+
import { OptionalProject } from "../lib/schemas.js";
|
|
11
|
+
|
|
12
|
+
interface ProjectInfo {
|
|
13
|
+
id: number;
|
|
14
|
+
name: string;
|
|
15
|
+
path: string;
|
|
16
|
+
path_with_namespace: string;
|
|
17
|
+
description?: string;
|
|
18
|
+
default_branch?: string;
|
|
19
|
+
visibility?: string;
|
|
20
|
+
web_url?: string;
|
|
21
|
+
ssh_url_to_repo?: string;
|
|
22
|
+
http_url_to_repo?: string;
|
|
23
|
+
created_at?: string;
|
|
24
|
+
last_activity_at?: string;
|
|
25
|
+
star_count?: number;
|
|
26
|
+
forks_count?: number;
|
|
27
|
+
open_issues_count?: number;
|
|
28
|
+
namespace?: { name?: string; path?: string; kind?: string };
|
|
29
|
+
topics?: string[];
|
|
30
|
+
archived?: boolean;
|
|
31
|
+
empty_repo?: boolean;
|
|
32
|
+
language?: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function registerGitlabRepoView(pi: ExtensionAPI) {
|
|
36
|
+
pi.registerTool({
|
|
37
|
+
name: "gitlab_repo_view",
|
|
38
|
+
label: "View Repository Info",
|
|
39
|
+
description:
|
|
40
|
+
"View detailed information about a GitLab project including URLs, stats, and metadata.",
|
|
41
|
+
parameters: Type.Object(
|
|
42
|
+
{
|
|
43
|
+
project: OptionalProject,
|
|
44
|
+
},
|
|
45
|
+
{ additionalProperties: false },
|
|
46
|
+
),
|
|
47
|
+
async execute(
|
|
48
|
+
_toolCallId,
|
|
49
|
+
params,
|
|
50
|
+
_signal,
|
|
51
|
+
_onUpdate,
|
|
52
|
+
ctx: ExtensionContext,
|
|
53
|
+
) {
|
|
54
|
+
try {
|
|
55
|
+
requireSetup(ctx.cwd);
|
|
56
|
+
} catch {
|
|
57
|
+
return setupRequiredResult();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const projectPath = await resolveProject(params.project, ctx.cwd);
|
|
61
|
+
const projectId = await resolveProjectId(projectPath);
|
|
62
|
+
|
|
63
|
+
const info = (await glab([
|
|
64
|
+
"api",
|
|
65
|
+
`projects/${projectId}`,
|
|
66
|
+
])) as ProjectInfo;
|
|
67
|
+
|
|
68
|
+
const lines = [
|
|
69
|
+
`## ${info.name}`,
|
|
70
|
+
"",
|
|
71
|
+
`| Field | Value |`,
|
|
72
|
+
`|---|---|`,
|
|
73
|
+
`| **Path** | \`${info.path_with_namespace}\` |`,
|
|
74
|
+
`| **ID** | ${info.id} |`,
|
|
75
|
+
`| **Visibility** | ${info.visibility ?? "-"} |`,
|
|
76
|
+
`| **Default branch** | \`${info.default_branch ?? "-"}\` |`,
|
|
77
|
+
`| **Language** | ${info.language ?? "-"} |`,
|
|
78
|
+
`| **Archived** | ${info.archived ? "Yes" : "No"} |`,
|
|
79
|
+
`| **Empty** | ${info.empty_repo ? "Yes" : "No"} |`,
|
|
80
|
+
`| **Stars** | ${info.star_count ?? 0} |`,
|
|
81
|
+
`| **Forks** | ${info.forks_count ?? 0} |`,
|
|
82
|
+
`| **Open issues** | ${info.open_issues_count ?? 0} |`,
|
|
83
|
+
...(info.created_at
|
|
84
|
+
? [
|
|
85
|
+
`| **Created** | ${new Date(info.created_at).toISOString().split("T")[0]} |`,
|
|
86
|
+
]
|
|
87
|
+
: []),
|
|
88
|
+
...(info.last_activity_at
|
|
89
|
+
? [
|
|
90
|
+
`| **Last activity** | ${new Date(info.last_activity_at).toISOString().split("T")[0]} |`,
|
|
91
|
+
]
|
|
92
|
+
: []),
|
|
93
|
+
];
|
|
94
|
+
|
|
95
|
+
if (info.description) {
|
|
96
|
+
lines.push("", `> ${info.description}`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (info.topics && info.topics.length > 0) {
|
|
100
|
+
lines.push(
|
|
101
|
+
"",
|
|
102
|
+
`**Topics:** ${info.topics.map((t) => `\`${t}\``).join(", ")}`,
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (info.web_url) {
|
|
107
|
+
lines.push("", `**URL:** ${info.web_url}`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
content: [{ type: "text", text: lines.join("\n") }],
|
|
112
|
+
details: { success: true, project: info },
|
|
113
|
+
};
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ExtensionAPI,
|
|
3
|
+
ExtensionContext,
|
|
4
|
+
} from "@earendil-works/pi-coding-agent";
|
|
5
|
+
import { Type } from "typebox";
|
|
6
|
+
import { requireSetup, setupRequiredResult } from "../lib/errors.js";
|
|
7
|
+
import { glab } from "../lib/glab.js";
|
|
8
|
+
import { limitRows } from "../lib/pagination.js";
|
|
9
|
+
import { resolveProject } from "../lib/projectFallback.js";
|
|
10
|
+
import { resolveProjectId } from "../lib/resolveProjectId.js";
|
|
11
|
+
import { OptionalProject } from "../lib/schemas.js";
|
|
12
|
+
|
|
13
|
+
interface SearchResult {
|
|
14
|
+
id?: number;
|
|
15
|
+
iid?: number;
|
|
16
|
+
name?: string;
|
|
17
|
+
title?: string;
|
|
18
|
+
path?: string;
|
|
19
|
+
path_with_namespace?: string;
|
|
20
|
+
description?: string;
|
|
21
|
+
filename?: string;
|
|
22
|
+
startline?: number;
|
|
23
|
+
data?: string;
|
|
24
|
+
username?: string;
|
|
25
|
+
state?: string;
|
|
26
|
+
web_url?: string;
|
|
27
|
+
ref?: string;
|
|
28
|
+
message?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function registerGitlabSearchQuery(pi: ExtensionAPI) {
|
|
32
|
+
pi.registerTool({
|
|
33
|
+
name: "gitlab_search_query",
|
|
34
|
+
label: "Search GitLab",
|
|
35
|
+
description:
|
|
36
|
+
"Search GitLab globally, within a group, or within a project. Supports projects, issues, merge requests, code blobs, commits, users, and wiki content.",
|
|
37
|
+
parameters: Type.Object(
|
|
38
|
+
{
|
|
39
|
+
query: Type.String({
|
|
40
|
+
description: "Search query string.",
|
|
41
|
+
minLength: 1,
|
|
42
|
+
maxLength: 200,
|
|
43
|
+
}),
|
|
44
|
+
scope: Type.Optional(
|
|
45
|
+
Type.Union(
|
|
46
|
+
[
|
|
47
|
+
Type.Literal("projects"),
|
|
48
|
+
Type.Literal("issues"),
|
|
49
|
+
Type.Literal("merge_requests"),
|
|
50
|
+
Type.Literal("milestones"),
|
|
51
|
+
Type.Literal("blobs"),
|
|
52
|
+
Type.Literal("commits"),
|
|
53
|
+
Type.Literal("users"),
|
|
54
|
+
Type.Literal("wiki_blobs"),
|
|
55
|
+
],
|
|
56
|
+
{
|
|
57
|
+
description:
|
|
58
|
+
"Search scope. Omit to search across common scopes (projects, issues, merge_requests, blobs).",
|
|
59
|
+
},
|
|
60
|
+
),
|
|
61
|
+
),
|
|
62
|
+
project: OptionalProject,
|
|
63
|
+
group: Type.Optional(
|
|
64
|
+
Type.String({
|
|
65
|
+
description: "Group ID. Set to restrict search to this group.",
|
|
66
|
+
}),
|
|
67
|
+
),
|
|
68
|
+
maxRows: Type.Optional(
|
|
69
|
+
Type.Number({
|
|
70
|
+
default: 25,
|
|
71
|
+
maximum: 100,
|
|
72
|
+
description: "Maximum results to return.",
|
|
73
|
+
}),
|
|
74
|
+
),
|
|
75
|
+
},
|
|
76
|
+
{ additionalProperties: false },
|
|
77
|
+
),
|
|
78
|
+
async execute(
|
|
79
|
+
_toolCallId,
|
|
80
|
+
params,
|
|
81
|
+
_signal,
|
|
82
|
+
_onUpdate,
|
|
83
|
+
ctx: ExtensionContext,
|
|
84
|
+
) {
|
|
85
|
+
try {
|
|
86
|
+
requireSetup(ctx.cwd);
|
|
87
|
+
} catch {
|
|
88
|
+
return setupRequiredResult();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const searchParams = new URLSearchParams();
|
|
92
|
+
searchParams.set("search", params.query);
|
|
93
|
+
|
|
94
|
+
// Determine endpoint based on scope
|
|
95
|
+
let endpoint: string;
|
|
96
|
+
let scopeDescription: string;
|
|
97
|
+
|
|
98
|
+
if (params.project) {
|
|
99
|
+
const projectPath = await resolveProject(params.project, ctx.cwd);
|
|
100
|
+
const projectId = await resolveProjectId(projectPath);
|
|
101
|
+
endpoint = `projects/${projectId}/search`;
|
|
102
|
+
scopeDescription = `project ${params.project}`;
|
|
103
|
+
} else if (params.group) {
|
|
104
|
+
endpoint = `groups/${params.group}/search`;
|
|
105
|
+
scopeDescription = `group ${params.group}`;
|
|
106
|
+
} else {
|
|
107
|
+
endpoint = "search";
|
|
108
|
+
scopeDescription = "global";
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Run searches — if no specific scope given, search across key scopes
|
|
112
|
+
const scopes = params.scope
|
|
113
|
+
? [params.scope]
|
|
114
|
+
: ["projects", "issues", "merge_requests", "blobs"];
|
|
115
|
+
|
|
116
|
+
const allResults: Array<{
|
|
117
|
+
scope: string;
|
|
118
|
+
items: SearchResult[];
|
|
119
|
+
}> = [];
|
|
120
|
+
|
|
121
|
+
for (const scope of scopes) {
|
|
122
|
+
searchParams.set("scope", scope);
|
|
123
|
+
try {
|
|
124
|
+
const results = (await glab([
|
|
125
|
+
"api",
|
|
126
|
+
`${endpoint}?${searchParams.toString()}`,
|
|
127
|
+
])) as SearchResult[];
|
|
128
|
+
const limited = limitRows(
|
|
129
|
+
Array.isArray(results) ? results : [],
|
|
130
|
+
params.maxRows ?? 25,
|
|
131
|
+
);
|
|
132
|
+
if (limited.length > 0) {
|
|
133
|
+
allResults.push({ scope, items: limited });
|
|
134
|
+
}
|
|
135
|
+
} catch {
|
|
136
|
+
// Scope may not be available at this level — skip silently
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (allResults.length === 0) {
|
|
141
|
+
return {
|
|
142
|
+
content: [
|
|
143
|
+
{
|
|
144
|
+
type: "text",
|
|
145
|
+
text: `No results found for "${params.query}" (${scopeDescription}).`,
|
|
146
|
+
},
|
|
147
|
+
],
|
|
148
|
+
details: {
|
|
149
|
+
success: true,
|
|
150
|
+
count: 0,
|
|
151
|
+
query: params.query,
|
|
152
|
+
scope: scopeDescription,
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const lines: string[] = [];
|
|
158
|
+
let totalCount = 0;
|
|
159
|
+
|
|
160
|
+
for (const group of allResults) {
|
|
161
|
+
totalCount += group.items.length;
|
|
162
|
+
lines.push(`\n### ${group.scope}`);
|
|
163
|
+
lines.push("");
|
|
164
|
+
|
|
165
|
+
for (const item of group.items) {
|
|
166
|
+
if (group.scope === "projects") {
|
|
167
|
+
lines.push(
|
|
168
|
+
`- **${item.name ?? item.path ?? "-"}** ${item.path_with_namespace ? `(\`${item.path_with_namespace}\`)` : ""} ${item.description ? `— ${item.description}` : ""}`,
|
|
169
|
+
);
|
|
170
|
+
} else if (group.scope === "issues") {
|
|
171
|
+
lines.push(
|
|
172
|
+
`- #${item.iid ?? "?"} **${item.title ?? "-"}** (${item.state ?? "?"}) ${item.web_url ?? ""}`,
|
|
173
|
+
);
|
|
174
|
+
} else if (group.scope === "merge_requests") {
|
|
175
|
+
lines.push(
|
|
176
|
+
`- !${item.iid ?? "?"} **${item.title ?? "-"}** (${item.state ?? "?"}) ${item.web_url ?? ""}`,
|
|
177
|
+
);
|
|
178
|
+
} else if (group.scope === "blobs") {
|
|
179
|
+
lines.push(
|
|
180
|
+
`- \`${item.filename ?? "?"}\`${item.startline ? `:${item.startline}` : ""} — ${item.ref ?? ""}`,
|
|
181
|
+
);
|
|
182
|
+
} else if (group.scope === "users") {
|
|
183
|
+
lines.push(
|
|
184
|
+
`- **${item.name ?? item.username ?? "-"}** (\`${item.username ?? "?"}\`)`,
|
|
185
|
+
);
|
|
186
|
+
} else if (group.scope === "commits") {
|
|
187
|
+
lines.push(
|
|
188
|
+
`- \`${(item.id ?? "").toString().slice(0, 8)}\` **${item.title ?? item.message ?? "-"}**`,
|
|
189
|
+
);
|
|
190
|
+
} else if (group.scope === "wiki_blobs") {
|
|
191
|
+
lines.push(
|
|
192
|
+
`- \`${item.filename ?? "?"}\`${item.startline ? `:${item.startline}` : ""}`,
|
|
193
|
+
);
|
|
194
|
+
} else if (group.scope === "milestones") {
|
|
195
|
+
lines.push(`- **${item.title ?? "-"}** (${item.state ?? "?"})`);
|
|
196
|
+
} else {
|
|
197
|
+
lines.push(
|
|
198
|
+
`- ${item.title ?? item.name ?? item.path ?? JSON.stringify(item)}`,
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return {
|
|
205
|
+
content: [
|
|
206
|
+
{
|
|
207
|
+
type: "text",
|
|
208
|
+
text: `Found **${totalCount}** results for "${params.query}" (${scopeDescription}):${lines.join("\n")}`,
|
|
209
|
+
},
|
|
210
|
+
],
|
|
211
|
+
details: {
|
|
212
|
+
success: true,
|
|
213
|
+
count: totalCount,
|
|
214
|
+
query: params.query,
|
|
215
|
+
scope: scopeDescription,
|
|
216
|
+
scopes: allResults.map((g) => g.scope),
|
|
217
|
+
},
|
|
218
|
+
};
|
|
219
|
+
},
|
|
220
|
+
});
|
|
221
|
+
}
|