@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
package/src/lib/env.ts
CHANGED
|
@@ -6,14 +6,13 @@
|
|
|
6
6
|
* removed from extension load; seeding now lives in explicit doctor/setup flows.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { checkSetup } from "../config/guard.js";
|
|
10
9
|
import {
|
|
11
|
-
GLOBAL_SETTINGS_PATH,
|
|
12
10
|
ensureConfig,
|
|
11
|
+
GLOBAL_SETTINGS_PATH,
|
|
13
12
|
loadConfig,
|
|
14
13
|
} from "../config/loader.js";
|
|
15
14
|
|
|
16
|
-
export {
|
|
15
|
+
export { ensureConfig, GLOBAL_SETTINGS_PATH, loadConfig };
|
|
17
16
|
|
|
18
17
|
// ---------------------------------------------------------------------------
|
|
19
18
|
// Token access
|
package/src/lib/projectCache.ts
CHANGED
|
@@ -3,10 +3,26 @@ import { homedir } from "node:os";
|
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import type { ProjectCache } from "../config/types.js";
|
|
5
5
|
|
|
6
|
-
const CACHE_DIR = join(homedir(), ".pi", "agent", "cache", "pi-gitlab");
|
|
6
|
+
const CACHE_DIR = join(homedir(), ".pi", "agent", ".cache", "pi-gitlab");
|
|
7
7
|
const CACHE_PATH = join(CACHE_DIR, "projects.json");
|
|
8
|
+
const OLD_CACHE_DIR = join(homedir(), ".pi", "agent", "cache", "pi-gitlab");
|
|
9
|
+
const OLD_CACHE_PATH = join(OLD_CACHE_DIR, "projects.json");
|
|
10
|
+
|
|
11
|
+
/** One-time migration from old cache path to .cache */
|
|
12
|
+
function migrateOldCache(): void {
|
|
13
|
+
if (!existsSync(OLD_CACHE_PATH) || existsSync(CACHE_PATH)) return;
|
|
14
|
+
mkdirSync(CACHE_DIR, { recursive: true });
|
|
15
|
+
try {
|
|
16
|
+
const raw = readFileSync(OLD_CACHE_PATH, "utf-8");
|
|
17
|
+
const cache = JSON.parse(raw) as ProjectCache;
|
|
18
|
+
writeFileSync(CACHE_PATH, JSON.stringify(cache, null, 2));
|
|
19
|
+
} catch {
|
|
20
|
+
// If old cache is corrupt, skip migration
|
|
21
|
+
}
|
|
22
|
+
}
|
|
8
23
|
|
|
9
24
|
function readCache(): ProjectCache {
|
|
25
|
+
migrateOldCache();
|
|
10
26
|
if (!existsSync(CACHE_PATH)) {
|
|
11
27
|
return { version: 1, entries: {} };
|
|
12
28
|
}
|
package/src/lib/schemas.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { Type } from "typebox";
|
|
|
3
3
|
export const OptionalProject = Type.Optional(
|
|
4
4
|
Type.String({
|
|
5
5
|
description:
|
|
6
|
-
"Project path (e.g. '
|
|
6
|
+
"Project path (e.g. 'group/project') or numeric ID. Optional — falls back to CWD git remote, then settings default.",
|
|
7
7
|
}),
|
|
8
8
|
);
|
|
9
9
|
|
|
@@ -0,0 +1,138 @@
|
|
|
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 LintResult {
|
|
13
|
+
valid: boolean;
|
|
14
|
+
errors?: string[];
|
|
15
|
+
warnings?: string[];
|
|
16
|
+
merged_yaml?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function formatLintResult(result: LintResult) {
|
|
20
|
+
const lines: string[] = [];
|
|
21
|
+
|
|
22
|
+
if (result.valid) {
|
|
23
|
+
lines.push("✅ CI configuration is **valid**.");
|
|
24
|
+
} else {
|
|
25
|
+
lines.push("❌ CI configuration has **errors**.");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (result.errors && result.errors.length > 0) {
|
|
29
|
+
lines.push("");
|
|
30
|
+
lines.push("**Errors:**");
|
|
31
|
+
for (const err of result.errors) {
|
|
32
|
+
lines.push(`- ${err}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (result.warnings && result.warnings.length > 0) {
|
|
37
|
+
lines.push("");
|
|
38
|
+
lines.push("**Warnings:**");
|
|
39
|
+
for (const warn of result.warnings) {
|
|
40
|
+
lines.push(`- ${warn}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
content: [{ type: "text" as const, text: lines.join("\n") }],
|
|
46
|
+
details: {
|
|
47
|
+
success: true,
|
|
48
|
+
valid: result.valid,
|
|
49
|
+
errors: result.errors ?? [],
|
|
50
|
+
warnings: result.warnings ?? [],
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function registerGitlabCiLint(pi: ExtensionAPI) {
|
|
56
|
+
pi.registerTool({
|
|
57
|
+
name: "gitlab_ci_lint",
|
|
58
|
+
label: "Lint CI Configuration",
|
|
59
|
+
description:
|
|
60
|
+
"Validate .gitlab-ci.yml configuration via the GitLab CI lint API. Returns errors and warnings.",
|
|
61
|
+
parameters: Type.Object(
|
|
62
|
+
{
|
|
63
|
+
project: OptionalProject,
|
|
64
|
+
content: Type.Optional(
|
|
65
|
+
Type.String({
|
|
66
|
+
description:
|
|
67
|
+
"CI YAML content to validate. Omit to lint the project's existing .gitlab-ci.yml.",
|
|
68
|
+
}),
|
|
69
|
+
),
|
|
70
|
+
ref: Type.Optional(
|
|
71
|
+
Type.String({
|
|
72
|
+
description:
|
|
73
|
+
"Branch or tag ref for context when linting existing config.",
|
|
74
|
+
}),
|
|
75
|
+
),
|
|
76
|
+
},
|
|
77
|
+
{ additionalProperties: false },
|
|
78
|
+
),
|
|
79
|
+
async execute(
|
|
80
|
+
_toolCallId,
|
|
81
|
+
params,
|
|
82
|
+
_signal,
|
|
83
|
+
_onUpdate,
|
|
84
|
+
ctx: ExtensionContext,
|
|
85
|
+
) {
|
|
86
|
+
try {
|
|
87
|
+
requireSetup(ctx.cwd);
|
|
88
|
+
} catch {
|
|
89
|
+
return setupRequiredResult();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const projectPath = await resolveProject(params.project, ctx.cwd);
|
|
93
|
+
const projectId = await resolveProjectId(projectPath);
|
|
94
|
+
|
|
95
|
+
const endpoint = `projects/${projectId}/ci/lint`;
|
|
96
|
+
let content = params.content;
|
|
97
|
+
const ref = params.ref;
|
|
98
|
+
|
|
99
|
+
// If no content provided, fetch existing .gitlab-ci.yml
|
|
100
|
+
if (!content) {
|
|
101
|
+
try {
|
|
102
|
+
const fileRef = ref ?? "HEAD";
|
|
103
|
+
content = (await glab([
|
|
104
|
+
"api",
|
|
105
|
+
`projects/${projectId}/repository/files/.gitlab-ci.yml/raw?ref=${fileRef}`,
|
|
106
|
+
])) as string;
|
|
107
|
+
} catch {
|
|
108
|
+
return {
|
|
109
|
+
content: [
|
|
110
|
+
{
|
|
111
|
+
type: "text",
|
|
112
|
+
text: "Could not fetch `.gitlab-ci.yml` from the repository. Provide `content` parameter with the YAML to validate.",
|
|
113
|
+
},
|
|
114
|
+
],
|
|
115
|
+
details: {
|
|
116
|
+
success: false,
|
|
117
|
+
error: "file_not_found",
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const result = (await glab([
|
|
124
|
+
"api",
|
|
125
|
+
"-X",
|
|
126
|
+
"POST",
|
|
127
|
+
endpoint,
|
|
128
|
+
"-f",
|
|
129
|
+
`content=${content}`,
|
|
130
|
+
...(ref ? ["-f", `ref=${ref}`] : []),
|
|
131
|
+
"-f",
|
|
132
|
+
"include_merged_yaml=true",
|
|
133
|
+
])) as LintResult;
|
|
134
|
+
|
|
135
|
+
return formatLintResult(result);
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
1
2
|
import type {
|
|
2
3
|
ExtensionAPI,
|
|
3
4
|
ExtensionContext,
|
|
4
5
|
} from "@earendil-works/pi-coding-agent";
|
|
5
6
|
import { Type } from "typebox";
|
|
7
|
+
import { loadConfig } from "../config/loader.js";
|
|
8
|
+
import { requireConfirm } from "../lib/confirm.js";
|
|
6
9
|
import { requireSetup, setupRequiredResult } from "../lib/errors.js";
|
|
7
10
|
import { glab } from "../lib/glab.js";
|
|
8
|
-
import { requireConfirm } from "../lib/confirm.js";
|
|
9
|
-
import { loadConfig } from "../config/loader.js";
|
|
10
11
|
import { resolveProject } from "../lib/projectFallback.js";
|
|
11
12
|
import { resolveProjectId } from "../lib/resolveProjectId.js";
|
|
12
|
-
import { spawn } from "node:child_process";
|
|
13
13
|
|
|
14
14
|
export function registerGitlabForcePushSafe(pi: ExtensionAPI) {
|
|
15
15
|
pi.registerTool({
|
|
@@ -129,7 +129,12 @@ export function registerGitlabForcePushSafe(pi: ExtensionAPI) {
|
|
|
129
129
|
// Step 3: Force push
|
|
130
130
|
try {
|
|
131
131
|
await runGitPush(
|
|
132
|
-
[
|
|
132
|
+
[
|
|
133
|
+
"push",
|
|
134
|
+
"--force-with-lease",
|
|
135
|
+
remote,
|
|
136
|
+
`${params.branch}:${remoteBranch}`,
|
|
137
|
+
],
|
|
133
138
|
ctx.cwd,
|
|
134
139
|
);
|
|
135
140
|
} catch (err) {
|
|
@@ -185,7 +190,11 @@ export function registerGitlabForcePushSafe(pi: ExtensionAPI) {
|
|
|
185
190
|
text: `✅ Force-push complete: \`${params.branch}\` → \`${remote}/${remoteBranch}\`${isProtected ? "\nBranch re-protected." : ""}`,
|
|
186
191
|
},
|
|
187
192
|
],
|
|
188
|
-
details: {
|
|
193
|
+
details: {
|
|
194
|
+
success: true,
|
|
195
|
+
branch: remoteBranch,
|
|
196
|
+
wasProtected: isProtected,
|
|
197
|
+
},
|
|
189
198
|
};
|
|
190
199
|
},
|
|
191
200
|
});
|
|
@@ -221,10 +230,7 @@ async function reprotectBranch(
|
|
|
221
230
|
}
|
|
222
231
|
}
|
|
223
232
|
|
|
224
|
-
async function runGitPush(
|
|
225
|
-
args: string[],
|
|
226
|
-
cwd?: string,
|
|
227
|
-
): Promise<void> {
|
|
233
|
+
async function runGitPush(args: string[], cwd?: string): Promise<void> {
|
|
228
234
|
await new Promise<void>((resolve, reject) => {
|
|
229
235
|
const child = spawn("git", args, {
|
|
230
236
|
cwd,
|
|
@@ -240,7 +246,9 @@ async function runGitPush(
|
|
|
240
246
|
resolve();
|
|
241
247
|
return;
|
|
242
248
|
}
|
|
243
|
-
reject(
|
|
249
|
+
reject(
|
|
250
|
+
new Error(stderr.trim() || `git push exited with code ${code ?? -1}`),
|
|
251
|
+
);
|
|
244
252
|
});
|
|
245
253
|
});
|
|
246
254
|
}
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
ExtensionAPI,
|
|
3
|
+
ExtensionContext,
|
|
4
|
+
} from "@earendil-works/pi-coding-agent";
|
|
2
5
|
import { Type } from "typebox";
|
|
6
|
+
import { requireConfirm } from "../lib/confirm.js";
|
|
3
7
|
import { requireSetup, setupRequiredResult } from "../lib/errors.js";
|
|
4
8
|
import { glab } from "../lib/glab.js";
|
|
5
|
-
import { requireConfirm } from "../lib/confirm.js";
|
|
6
9
|
import { resolveProject } from "../lib/projectFallback.js";
|
|
7
10
|
import { resolveProjectId } from "../lib/resolveProjectId.js";
|
|
8
11
|
import { OptionalProject } from "../lib/schemas.js";
|
|
@@ -21,7 +24,13 @@ export function registerGitlabIssueClose(pi: ExtensionAPI) {
|
|
|
21
24
|
},
|
|
22
25
|
{ additionalProperties: false },
|
|
23
26
|
),
|
|
24
|
-
async execute(
|
|
27
|
+
async execute(
|
|
28
|
+
_toolCallId,
|
|
29
|
+
params,
|
|
30
|
+
_signal,
|
|
31
|
+
_onUpdate,
|
|
32
|
+
ctx: ExtensionContext,
|
|
33
|
+
) {
|
|
25
34
|
try {
|
|
26
35
|
requireSetup(ctx.cwd);
|
|
27
36
|
} catch {
|
|
@@ -38,7 +47,10 @@ export function registerGitlabIssueClose(pi: ExtensionAPI) {
|
|
|
38
47
|
])) as Record<string, unknown>;
|
|
39
48
|
|
|
40
49
|
const preview = `Close issue #${params.issueId}: **${issue.title}**\nProject: \`${projectPath}\``;
|
|
41
|
-
const blocked = requireConfirm(preview, {
|
|
50
|
+
const blocked = requireConfirm(preview, {
|
|
51
|
+
confirm: params.confirm,
|
|
52
|
+
dryRun: params.dryRun,
|
|
53
|
+
});
|
|
42
54
|
if (blocked) return blocked;
|
|
43
55
|
|
|
44
56
|
const result = await glab([
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
ExtensionAPI,
|
|
3
|
+
ExtensionContext,
|
|
4
|
+
} from "@earendil-works/pi-coding-agent";
|
|
2
5
|
import { Type } from "typebox";
|
|
6
|
+
import { requireConfirm } from "../lib/confirm.js";
|
|
3
7
|
import { requireSetup, setupRequiredResult } from "../lib/errors.js";
|
|
4
8
|
import { glab } from "../lib/glab.js";
|
|
5
|
-
import { requireConfirm } from "../lib/confirm.js";
|
|
6
9
|
import { resolveProject } from "../lib/projectFallback.js";
|
|
7
10
|
import { resolveProjectId } from "../lib/resolveProjectId.js";
|
|
8
11
|
import { OptionalProject } from "../lib/schemas.js";
|
|
@@ -16,7 +19,9 @@ export function registerGitlabIssueCreate(pi: ExtensionAPI) {
|
|
|
16
19
|
{
|
|
17
20
|
project: OptionalProject,
|
|
18
21
|
title: Type.String({ description: "Issue title" }),
|
|
19
|
-
description: Type.Optional(
|
|
22
|
+
description: Type.Optional(
|
|
23
|
+
Type.String({ description: "Issue description" }),
|
|
24
|
+
),
|
|
20
25
|
labels: Type.Optional(Type.Array(Type.String())),
|
|
21
26
|
assignee: Type.Optional(Type.String()),
|
|
22
27
|
milestone: Type.Optional(Type.String()),
|
|
@@ -26,7 +31,13 @@ export function registerGitlabIssueCreate(pi: ExtensionAPI) {
|
|
|
26
31
|
},
|
|
27
32
|
{ additionalProperties: false },
|
|
28
33
|
),
|
|
29
|
-
async execute(
|
|
34
|
+
async execute(
|
|
35
|
+
_toolCallId,
|
|
36
|
+
params,
|
|
37
|
+
_signal,
|
|
38
|
+
_onUpdate,
|
|
39
|
+
ctx: ExtensionContext,
|
|
40
|
+
) {
|
|
30
41
|
try {
|
|
31
42
|
requireSetup(ctx.cwd);
|
|
32
43
|
} catch {
|
|
@@ -38,7 +49,10 @@ export function registerGitlabIssueCreate(pi: ExtensionAPI) {
|
|
|
38
49
|
const projectId = await resolveProjectId(projectPath);
|
|
39
50
|
|
|
40
51
|
const preview = `Create issue **${params.title}**\nProject: \`${projectPath}\`${params.labels?.length ? `\nLabels: ${params.labels.join(", ")}` : ""}`;
|
|
41
|
-
const blocked = requireConfirm(preview, {
|
|
52
|
+
const blocked = requireConfirm(preview, {
|
|
53
|
+
confirm: params.confirm,
|
|
54
|
+
dryRun: params.dryRun,
|
|
55
|
+
});
|
|
42
56
|
if (blocked) return blocked;
|
|
43
57
|
|
|
44
58
|
const body: Record<string, unknown> = { title: params.title };
|
|
@@ -56,12 +56,12 @@ export function registerGitlabIssueList(pi: ExtensionAPI) {
|
|
|
56
56
|
ctx: ExtensionContext,
|
|
57
57
|
) {
|
|
58
58
|
try {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
59
|
+
requireSetup(ctx.cwd);
|
|
60
|
+
} catch {
|
|
61
|
+
return setupRequiredResult();
|
|
62
|
+
}
|
|
63
63
|
|
|
64
|
-
|
|
64
|
+
const projectPath = await resolveProject(params.project, ctx.cwd);
|
|
65
65
|
const projectId = await resolveProjectId(projectPath);
|
|
66
66
|
|
|
67
67
|
const query = new URLSearchParams();
|
|
@@ -39,12 +39,12 @@ export function registerGitlabJobLogs(pi: ExtensionAPI) {
|
|
|
39
39
|
ctx: ExtensionContext,
|
|
40
40
|
) {
|
|
41
41
|
try {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
42
|
+
requireSetup(ctx.cwd);
|
|
43
|
+
} catch {
|
|
44
|
+
return setupRequiredResult();
|
|
45
|
+
}
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
const projectPath = await resolveProject(params.project, ctx.cwd);
|
|
48
48
|
const projectId = await resolveProjectId(projectPath);
|
|
49
49
|
|
|
50
50
|
const logText = (await glab([
|
|
@@ -3,9 +3,9 @@ import type {
|
|
|
3
3
|
ExtensionContext,
|
|
4
4
|
} from "@earendil-works/pi-coding-agent";
|
|
5
5
|
import { Type } from "typebox";
|
|
6
|
+
import { requireConfirm } from "../lib/confirm.js";
|
|
6
7
|
import { requireSetup, setupRequiredResult } from "../lib/errors.js";
|
|
7
8
|
import { glab } from "../lib/glab.js";
|
|
8
|
-
import { requireConfirm } from "../lib/confirm.js";
|
|
9
9
|
import { resolveProject } from "../lib/projectFallback.js";
|
|
10
10
|
import { resolveProjectId } from "../lib/resolveProjectId.js";
|
|
11
11
|
import { OptionalProject } from "../lib/schemas.js";
|
|
@@ -85,7 +85,9 @@ export function registerGitlabMrBulkApprove(pi: ExtensionAPI) {
|
|
|
85
85
|
|
|
86
86
|
const lines: string[] = [];
|
|
87
87
|
if (succeeded.length > 0) {
|
|
88
|
-
lines.push(
|
|
88
|
+
lines.push(
|
|
89
|
+
`✅ Approved ${succeeded.length} MR(s): ${succeeded.map((r) => `!${r.mrIid}`).join(", ")}`,
|
|
90
|
+
);
|
|
89
91
|
}
|
|
90
92
|
if (failed.length > 0) {
|
|
91
93
|
lines.push(`\n❌ Failed ${failed.length}:`);
|
|
@@ -3,9 +3,9 @@ import type {
|
|
|
3
3
|
ExtensionContext,
|
|
4
4
|
} from "@earendil-works/pi-coding-agent";
|
|
5
5
|
import { Type } from "typebox";
|
|
6
|
+
import { requireConfirm } from "../lib/confirm.js";
|
|
6
7
|
import { requireSetup, setupRequiredResult } from "../lib/errors.js";
|
|
7
8
|
import { glab } from "../lib/glab.js";
|
|
8
|
-
import { requireConfirm } from "../lib/confirm.js";
|
|
9
9
|
import { resolveProject } from "../lib/projectFallback.js";
|
|
10
10
|
import { resolveProjectId } from "../lib/resolveProjectId.js";
|
|
11
11
|
import { OptionalProject } from "../lib/schemas.js";
|
|
@@ -57,12 +57,12 @@ export function registerGitlabMrList(pi: ExtensionAPI) {
|
|
|
57
57
|
ctx: ExtensionContext,
|
|
58
58
|
) {
|
|
59
59
|
try {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
60
|
+
requireSetup(ctx.cwd);
|
|
61
|
+
} catch {
|
|
62
|
+
return setupRequiredResult();
|
|
63
|
+
}
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
const projectPath = await resolveProject(params.project, ctx.cwd);
|
|
66
66
|
const projectId = await resolveProjectId(projectPath);
|
|
67
67
|
|
|
68
68
|
const query = new URLSearchParams();
|
|
@@ -3,9 +3,9 @@ import type {
|
|
|
3
3
|
ExtensionContext,
|
|
4
4
|
} from "@earendil-works/pi-coding-agent";
|
|
5
5
|
import { Type } from "typebox";
|
|
6
|
+
import { requireConfirm } from "../lib/confirm.js";
|
|
6
7
|
import { requireSetup, setupRequiredResult } from "../lib/errors.js";
|
|
7
8
|
import { glab } from "../lib/glab.js";
|
|
8
|
-
import { requireConfirm } from "../lib/confirm.js";
|
|
9
9
|
import { resolveProject } from "../lib/projectFallback.js";
|
|
10
10
|
import { resolveProjectId } from "../lib/resolveProjectId.js";
|
|
11
11
|
import { OptionalProject } from "../lib/schemas.js";
|
|
@@ -20,9 +20,7 @@ export function registerGitlabMrMerge(pi: ExtensionAPI) {
|
|
|
20
20
|
project: OptionalProject,
|
|
21
21
|
mrId: Type.Number({ description: "MR IID" }),
|
|
22
22
|
squash: Type.Optional(Type.Boolean({ default: false })),
|
|
23
|
-
removeSourceBranch: Type.Optional(
|
|
24
|
-
Type.Boolean({ default: false }),
|
|
25
|
-
),
|
|
23
|
+
removeSourceBranch: Type.Optional(Type.Boolean({ default: false })),
|
|
26
24
|
confirm: Type.Optional(Type.Boolean({ default: false })),
|
|
27
25
|
dryRun: Type.Optional(Type.Boolean({ default: false })),
|
|
28
26
|
},
|
|
@@ -32,12 +32,12 @@ export function registerGitlabMrView(pi: ExtensionAPI) {
|
|
|
32
32
|
ctx: ExtensionContext,
|
|
33
33
|
) {
|
|
34
34
|
try {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
requireSetup(ctx.cwd);
|
|
36
|
+
} catch {
|
|
37
|
+
return setupRequiredResult();
|
|
38
|
+
}
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
const projectPath = await resolveProject(params.project, ctx.cwd);
|
|
41
41
|
const projectId = await resolveProjectId(projectPath);
|
|
42
42
|
|
|
43
43
|
const mr = (await glab([
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
ExtensionAPI,
|
|
3
|
+
ExtensionContext,
|
|
4
|
+
} from "@earendil-works/pi-coding-agent";
|
|
2
5
|
import { Type } from "typebox";
|
|
6
|
+
import { requireConfirm } from "../lib/confirm.js";
|
|
3
7
|
import { requireSetup, setupRequiredResult } from "../lib/errors.js";
|
|
4
8
|
import { glab } from "../lib/glab.js";
|
|
5
|
-
import { requireConfirm } from "../lib/confirm.js";
|
|
6
9
|
import { resolveProject } from "../lib/projectFallback.js";
|
|
7
10
|
import { resolveProjectId } from "../lib/resolveProjectId.js";
|
|
8
11
|
import { OptionalProject } from "../lib/schemas.js";
|
|
@@ -16,13 +19,21 @@ export function registerGitlabPipelineRun(pi: ExtensionAPI) {
|
|
|
16
19
|
{
|
|
17
20
|
project: OptionalProject,
|
|
18
21
|
ref: Type.String({ description: "Branch or tag to run pipeline for" }),
|
|
19
|
-
variables: Type.Optional(
|
|
22
|
+
variables: Type.Optional(
|
|
23
|
+
Type.Object({}, { additionalProperties: true }),
|
|
24
|
+
),
|
|
20
25
|
confirm: Type.Optional(Type.Boolean({ default: false })),
|
|
21
26
|
dryRun: Type.Optional(Type.Boolean({ default: false })),
|
|
22
27
|
},
|
|
23
28
|
{ additionalProperties: false },
|
|
24
29
|
),
|
|
25
|
-
async execute(
|
|
30
|
+
async execute(
|
|
31
|
+
_toolCallId,
|
|
32
|
+
params,
|
|
33
|
+
_signal,
|
|
34
|
+
_onUpdate,
|
|
35
|
+
ctx: ExtensionContext,
|
|
36
|
+
) {
|
|
26
37
|
try {
|
|
27
38
|
requireSetup(ctx.cwd);
|
|
28
39
|
} catch {
|
|
@@ -34,15 +45,20 @@ export function registerGitlabPipelineRun(pi: ExtensionAPI) {
|
|
|
34
45
|
const projectId = await resolveProjectId(projectPath);
|
|
35
46
|
|
|
36
47
|
const preview = `Trigger pipeline for \`${params.ref}\`\nProject: \`${projectPath}\``;
|
|
37
|
-
const blocked = requireConfirm(preview, {
|
|
48
|
+
const blocked = requireConfirm(preview, {
|
|
49
|
+
confirm: params.confirm,
|
|
50
|
+
dryRun: params.dryRun,
|
|
51
|
+
});
|
|
38
52
|
if (blocked) return blocked;
|
|
39
53
|
|
|
40
54
|
const body: Record<string, unknown> = { ref: params.ref };
|
|
41
55
|
if (params.variables && Object.keys(params.variables).length > 0) {
|
|
42
|
-
body.variables = Object.entries(params.variables).map(
|
|
43
|
-
key,
|
|
44
|
-
|
|
45
|
-
|
|
56
|
+
body.variables = Object.entries(params.variables).map(
|
|
57
|
+
([key, value]) => ({
|
|
58
|
+
key,
|
|
59
|
+
value: String(value),
|
|
60
|
+
}),
|
|
61
|
+
);
|
|
46
62
|
}
|
|
47
63
|
|
|
48
64
|
const result = await glab([
|
|
@@ -50,7 +66,10 @@ export function registerGitlabPipelineRun(pi: ExtensionAPI) {
|
|
|
50
66
|
"-X",
|
|
51
67
|
"POST",
|
|
52
68
|
`projects/${projectId}/pipeline`,
|
|
53
|
-
...Object.entries(body).flatMap(([k, v]) => [
|
|
69
|
+
...Object.entries(body).flatMap(([k, v]) => [
|
|
70
|
+
"-f",
|
|
71
|
+
`${k}=${typeof v === "string" ? v : JSON.stringify(v)}`,
|
|
72
|
+
]),
|
|
54
73
|
]);
|
|
55
74
|
|
|
56
75
|
const pipeline = result as Record<string, unknown>;
|
|
@@ -49,12 +49,12 @@ export function registerGitlabPipelineStatus(pi: ExtensionAPI) {
|
|
|
49
49
|
ctx: ExtensionContext,
|
|
50
50
|
) {
|
|
51
51
|
try {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
requireSetup(ctx.cwd);
|
|
53
|
+
} catch {
|
|
54
|
+
return setupRequiredResult();
|
|
55
|
+
}
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
const projectPath = await resolveProject(params.project, ctx.cwd);
|
|
58
58
|
const projectId = await resolveProjectId(projectPath);
|
|
59
59
|
|
|
60
60
|
let pipeline: Pipeline;
|
|
@@ -38,12 +38,12 @@ export function registerGitlabProjectResolve(pi: ExtensionAPI) {
|
|
|
38
38
|
ctx: ExtensionContext,
|
|
39
39
|
) {
|
|
40
40
|
try {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
41
|
+
requireSetup(ctx.cwd);
|
|
42
|
+
} catch {
|
|
43
|
+
return setupRequiredResult();
|
|
44
|
+
}
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
const project = await resolveProject(params.project, ctx.cwd);
|
|
47
47
|
const id = await resolveProjectId(project, params.force);
|
|
48
48
|
return {
|
|
49
49
|
content: [
|
|
@@ -3,9 +3,9 @@ import type {
|
|
|
3
3
|
ExtensionContext,
|
|
4
4
|
} from "@earendil-works/pi-coding-agent";
|
|
5
5
|
import { Type } from "typebox";
|
|
6
|
+
import { requireConfirm } from "../lib/confirm.js";
|
|
6
7
|
import { requireSetup, setupRequiredResult } from "../lib/errors.js";
|
|
7
8
|
import { glab } from "../lib/glab.js";
|
|
8
|
-
import { requireConfirm } from "../lib/confirm.js";
|
|
9
9
|
import { resolveProject } from "../lib/projectFallback.js";
|
|
10
10
|
import { resolveProjectId } from "../lib/resolveProjectId.js";
|
|
11
11
|
import { OptionalProject } from "../lib/schemas.js";
|
|
@@ -14,8 +14,7 @@ export function registerGitlabReleaseCreate(pi: ExtensionAPI) {
|
|
|
14
14
|
pi.registerTool({
|
|
15
15
|
name: "gitlab_release_create",
|
|
16
16
|
label: "Create Release",
|
|
17
|
-
description:
|
|
18
|
-
"Create a new release for a tag. Requires confirmation.",
|
|
17
|
+
description: "Create a new release for a tag. Requires confirmation.",
|
|
19
18
|
parameters: Type.Object(
|
|
20
19
|
{
|
|
21
20
|
project: OptionalProject,
|
|
@@ -23,19 +23,14 @@ export function registerGitlabReleaseList(pi: ExtensionAPI) {
|
|
|
23
23
|
pi.registerTool({
|
|
24
24
|
name: "gitlab_release_list",
|
|
25
25
|
label: "List Releases",
|
|
26
|
-
description:
|
|
27
|
-
"List releases for a project with transparent pagination.",
|
|
26
|
+
description: "List releases for a project with transparent pagination.",
|
|
28
27
|
parameters: Type.Object(
|
|
29
28
|
{
|
|
30
29
|
project: OptionalProject,
|
|
31
30
|
sort: Type.Optional(
|
|
32
|
-
Type.Union(
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
Type.Literal("asc"),
|
|
36
|
-
],
|
|
37
|
-
{ default: "desc" },
|
|
38
|
-
),
|
|
31
|
+
Type.Union([Type.Literal("desc"), Type.Literal("asc")], {
|
|
32
|
+
default: "desc",
|
|
33
|
+
}),
|
|
39
34
|
),
|
|
40
35
|
maxRows: MaxRows,
|
|
41
36
|
},
|
|
@@ -44,14 +44,19 @@ export function registerGitlabReleaseView(pi: ExtensionAPI) {
|
|
|
44
44
|
])) as Record<string, unknown>;
|
|
45
45
|
|
|
46
46
|
const assets = release.assets as Record<string, unknown> | undefined;
|
|
47
|
-
const links =
|
|
47
|
+
const links =
|
|
48
|
+
(assets?.links as Array<Record<string, unknown>> | undefined) ?? [];
|
|
48
49
|
|
|
49
50
|
const lines: string[] = [];
|
|
50
51
|
lines.push(`## Release: ${release.tag_name}`);
|
|
51
52
|
if (release.name) lines.push(`**Name:** ${release.name}`);
|
|
52
|
-
lines.push(
|
|
53
|
+
lines.push(
|
|
54
|
+
`**Author:** ${(release.author as Record<string, unknown>)?.name ?? "-"}`,
|
|
55
|
+
);
|
|
53
56
|
if (release.created_at)
|
|
54
|
-
lines.push(
|
|
57
|
+
lines.push(
|
|
58
|
+
`**Created:** ${new Date(release.created_at as string).toISOString()}`,
|
|
59
|
+
);
|
|
55
60
|
if (release.upcoming_release) lines.push("**Upcoming release:** yes");
|
|
56
61
|
|
|
57
62
|
if (release.description) {
|