opencode-gitlab-dap 1.8.1 → 1.8.3
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/dist/index.cjs +130 -44
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +130 -44
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -3637,14 +3637,51 @@ async function safeRead(instanceUrl, token, scope, id, slug) {
|
|
|
3637
3637
|
return null;
|
|
3638
3638
|
}
|
|
3639
3639
|
}
|
|
3640
|
+
var MAX_RETRIES = 3;
|
|
3641
|
+
var RETRY_DELAY_MS = 1e3;
|
|
3642
|
+
async function sleep2(ms) {
|
|
3643
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
3644
|
+
}
|
|
3640
3645
|
async function appendToPage(instanceUrl, token, scope, id, slug, newContent) {
|
|
3646
|
+
for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
|
|
3647
|
+
const existing2 = await safeRead(instanceUrl, token, scope, id, slug);
|
|
3648
|
+
if (existing2 !== null) {
|
|
3649
|
+
try {
|
|
3650
|
+
await updateWikiPage(instanceUrl, token, scope, id, slug, existing2 + "\n\n" + newContent);
|
|
3651
|
+
return;
|
|
3652
|
+
} catch (err) {
|
|
3653
|
+
if (attempt < MAX_RETRIES - 1 && err.message?.includes("reference update")) {
|
|
3654
|
+
await sleep2(RETRY_DELAY_MS * (attempt + 1));
|
|
3655
|
+
continue;
|
|
3656
|
+
}
|
|
3657
|
+
throw err;
|
|
3658
|
+
}
|
|
3659
|
+
} else {
|
|
3660
|
+
try {
|
|
3661
|
+
await createWikiPage(instanceUrl, token, scope, id, slug, newContent);
|
|
3662
|
+
return;
|
|
3663
|
+
} catch (err) {
|
|
3664
|
+
if (err.message?.includes("Duplicate page") || err.message?.includes("reference update")) {
|
|
3665
|
+
await sleep2(RETRY_DELAY_MS * (attempt + 1));
|
|
3666
|
+
continue;
|
|
3667
|
+
}
|
|
3668
|
+
throw err;
|
|
3669
|
+
}
|
|
3670
|
+
}
|
|
3671
|
+
}
|
|
3641
3672
|
const existing = await safeRead(instanceUrl, token, scope, id, slug);
|
|
3642
3673
|
if (existing !== null) {
|
|
3643
3674
|
await updateWikiPage(instanceUrl, token, scope, id, slug, existing + "\n\n" + newContent);
|
|
3644
3675
|
} else {
|
|
3645
|
-
|
|
3676
|
+
throw new Error(`Failed to append to ${slug} after ${MAX_RETRIES} retries`);
|
|
3646
3677
|
}
|
|
3647
3678
|
}
|
|
3679
|
+
function validateProjectId(projectId) {
|
|
3680
|
+
if (!projectId.includes("/")) {
|
|
3681
|
+
return `Invalid project_id "${projectId}". Must be the full project path containing at least one slash (e.g., "my-group/my-project"), not just the project name.`;
|
|
3682
|
+
}
|
|
3683
|
+
return null;
|
|
3684
|
+
}
|
|
3648
3685
|
function resolveScope(args) {
|
|
3649
3686
|
if (args.scope === "groups" && args.group_id) {
|
|
3650
3687
|
return { scope: "groups", id: args.group_id };
|
|
@@ -3652,18 +3689,26 @@ function resolveScope(args) {
|
|
|
3652
3689
|
return { scope: "projects", id: args.project_id };
|
|
3653
3690
|
}
|
|
3654
3691
|
function makeMemoryTools(ctx) {
|
|
3692
|
+
function authAndValidate(projectId) {
|
|
3693
|
+
const auth = ctx.ensureAuth();
|
|
3694
|
+
if (!auth) throw new Error("GitLab authentication not available");
|
|
3695
|
+
const err = validateProjectId(projectId);
|
|
3696
|
+
if (err) throw new Error(err);
|
|
3697
|
+
return auth;
|
|
3698
|
+
}
|
|
3655
3699
|
return {
|
|
3656
3700
|
gitlab_memory_load: (0, import_plugin5.tool)({
|
|
3657
3701
|
description: "Load project memory to understand context, known facts, past decisions, and observed patterns.\nUse this at the start of complex tasks to check what is already known about the project.\nReturns accumulated knowledge from previous sessions.",
|
|
3658
3702
|
args: {
|
|
3659
|
-
project_id: z5.string().describe(
|
|
3703
|
+
project_id: z5.string().describe(
|
|
3704
|
+
'FULL project path with namespace (e.g., "gitlab-org/gitlab"). Must contain a slash. Never use just the project name.'
|
|
3705
|
+
),
|
|
3660
3706
|
type: z5.enum(["all", "facts", "decisions", "patterns"]).optional().describe('Which memory to load: "all" (default), "facts", "decisions", or "patterns"'),
|
|
3661
3707
|
scope: z5.enum(["projects", "groups"]).optional().describe("Scope (default: projects)"),
|
|
3662
3708
|
group_id: z5.string().optional().describe("Group path (required when scope is groups)")
|
|
3663
3709
|
},
|
|
3664
3710
|
execute: async (args) => {
|
|
3665
|
-
const auth =
|
|
3666
|
-
if (!auth) throw new Error("GitLab authentication not available");
|
|
3711
|
+
const auth = authAndValidate(args.project_id);
|
|
3667
3712
|
const { scope, id } = resolveScope(args);
|
|
3668
3713
|
const memType = args.type ?? "all";
|
|
3669
3714
|
const slugs = MEMORY_SLUGS[memType];
|
|
@@ -3686,15 +3731,16 @@ ${content}`);
|
|
|
3686
3731
|
gitlab_memory_record: (0, import_plugin5.tool)({
|
|
3687
3732
|
description: "Record a fact, decision, or pattern in project memory.\nFacts: stable truths about the project (e.g., deploy targets, tech stack, team conventions).\nDecisions: architectural choices with reasoning (why X was chosen over Y).\nPatterns: recurring observations that may evolve into skills over time.\nEntries are automatically timestamped and appended to the appropriate memory page.\nMultiple facts can be recorded in a single call by separating them with newlines.",
|
|
3688
3733
|
args: {
|
|
3689
|
-
project_id: z5.string().describe(
|
|
3734
|
+
project_id: z5.string().describe(
|
|
3735
|
+
'FULL project path with namespace (e.g., "gitlab-org/gitlab"). Must contain a slash. Never use just the project name.'
|
|
3736
|
+
),
|
|
3690
3737
|
type: z5.enum(["fact", "decision", "pattern"]).describe("Type of knowledge to record"),
|
|
3691
3738
|
content: z5.string().describe("The knowledge to record (markdown)"),
|
|
3692
3739
|
scope: z5.enum(["projects", "groups"]).optional().describe("Scope (default: projects)"),
|
|
3693
3740
|
group_id: z5.string().optional().describe("Group path (required when scope is groups)")
|
|
3694
3741
|
},
|
|
3695
3742
|
execute: async (args) => {
|
|
3696
|
-
const auth =
|
|
3697
|
-
if (!auth) throw new Error("GitLab authentication not available");
|
|
3743
|
+
const auth = authAndValidate(args.project_id);
|
|
3698
3744
|
const { scope, id } = resolveScope(args);
|
|
3699
3745
|
const slug = RECORD_SLUG[args.type];
|
|
3700
3746
|
if (!slug) return `Unknown memory type: ${args.type}`;
|
|
@@ -3722,14 +3768,15 @@ ${args.content}`;
|
|
|
3722
3768
|
gitlab_memory_recall: (0, import_plugin5.tool)({
|
|
3723
3769
|
description: "Search project knowledge for relevant information.\nSearches across all memory pages, session logs, and skills.\nUse this to check if something is already known before investigating.",
|
|
3724
3770
|
args: {
|
|
3725
|
-
project_id: z5.string().describe(
|
|
3771
|
+
project_id: z5.string().describe(
|
|
3772
|
+
'FULL project path with namespace (e.g., "gitlab-org/gitlab"). Must contain a slash. Never use just the project name.'
|
|
3773
|
+
),
|
|
3726
3774
|
query: z5.string().describe("What to search for"),
|
|
3727
3775
|
scope: z5.enum(["projects", "groups"]).optional().describe("Scope (default: projects)"),
|
|
3728
3776
|
group_id: z5.string().optional().describe("Group path (required when scope is groups)")
|
|
3729
3777
|
},
|
|
3730
3778
|
execute: async (args) => {
|
|
3731
|
-
const auth =
|
|
3732
|
-
if (!auth) throw new Error("GitLab authentication not available");
|
|
3779
|
+
const auth = authAndValidate(args.project_id);
|
|
3733
3780
|
const { scope, id } = resolveScope(args);
|
|
3734
3781
|
try {
|
|
3735
3782
|
const results = await searchWikiPages(
|
|
@@ -3753,7 +3800,9 @@ ${args.content}`;
|
|
|
3753
3800
|
gitlab_memory_log_session: (0, import_plugin5.tool)({
|
|
3754
3801
|
description: "Log a session summary including what was accomplished, what was learned, and any suggestions.\nUse this at the end of significant work sessions to preserve context for future sessions.",
|
|
3755
3802
|
args: {
|
|
3756
|
-
project_id: z5.string().describe(
|
|
3803
|
+
project_id: z5.string().describe(
|
|
3804
|
+
'FULL project path with namespace (e.g., "gitlab-org/gitlab"). Must contain a slash. Never use just the project name.'
|
|
3805
|
+
),
|
|
3757
3806
|
title: z5.string().describe('Brief session title (e.g., "fix-ai-gateway-healthcheck")'),
|
|
3758
3807
|
summary: z5.string().describe(
|
|
3759
3808
|
"Session summary in markdown (what happened, what was learned, what went wrong)"
|
|
@@ -3762,25 +3811,36 @@ ${args.content}`;
|
|
|
3762
3811
|
group_id: z5.string().optional().describe("Group path (required when scope is groups)")
|
|
3763
3812
|
},
|
|
3764
3813
|
execute: async (args) => {
|
|
3765
|
-
const auth =
|
|
3766
|
-
if (!auth) throw new Error("GitLab authentication not available");
|
|
3814
|
+
const auth = authAndValidate(args.project_id);
|
|
3767
3815
|
const { scope, id } = resolveScope(args);
|
|
3768
3816
|
const date = today();
|
|
3769
3817
|
const slug = `${PREFIX}/memory/sessions/${date}-${slugify(args.title)}`;
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
|
|
3818
|
+
for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
|
|
3819
|
+
try {
|
|
3820
|
+
await createWikiPage(auth.instanceUrl, auth.token, scope, id, slug, args.summary);
|
|
3821
|
+
return `Session logged: ${slug}`;
|
|
3822
|
+
} catch (err) {
|
|
3823
|
+
const msg = err.message ?? "";
|
|
3824
|
+
if (msg.includes("Duplicate page") || msg.includes("already exists") || msg.includes("422")) {
|
|
3825
|
+
try {
|
|
3826
|
+
await updateWikiPage(auth.instanceUrl, auth.token, scope, id, slug, args.summary);
|
|
3827
|
+
return `Session log updated: ${slug}`;
|
|
3828
|
+
} catch (err2) {
|
|
3829
|
+
if (attempt < MAX_RETRIES - 1 && err2.message?.includes("reference update")) {
|
|
3830
|
+
await sleep2(RETRY_DELAY_MS * (attempt + 1));
|
|
3831
|
+
continue;
|
|
3832
|
+
}
|
|
3833
|
+
return `Error logging session: ${err2.message}`;
|
|
3834
|
+
}
|
|
3835
|
+
}
|
|
3836
|
+
if (attempt < MAX_RETRIES - 1 && msg.includes("reference update")) {
|
|
3837
|
+
await sleep2(RETRY_DELAY_MS * (attempt + 1));
|
|
3838
|
+
continue;
|
|
3780
3839
|
}
|
|
3840
|
+
return `Error logging session: ${msg}`;
|
|
3781
3841
|
}
|
|
3782
|
-
return `Error logging session: ${err.message}`;
|
|
3783
3842
|
}
|
|
3843
|
+
return `Error logging session: failed after ${MAX_RETRIES} retries`;
|
|
3784
3844
|
}
|
|
3785
3845
|
})
|
|
3786
3846
|
};
|
|
@@ -3798,19 +3858,33 @@ function resolveScope2(args) {
|
|
|
3798
3858
|
}
|
|
3799
3859
|
return { scope: "projects", id: args.project_id };
|
|
3800
3860
|
}
|
|
3861
|
+
function validateProjectId2(projectId) {
|
|
3862
|
+
if (!projectId.includes("/")) {
|
|
3863
|
+
return `Invalid project_id "${projectId}". Must be the full project path containing at least one slash (e.g., "my-group/my-project"), not just the project name.`;
|
|
3864
|
+
}
|
|
3865
|
+
return null;
|
|
3866
|
+
}
|
|
3801
3867
|
function makeSkillTools(ctx) {
|
|
3868
|
+
function authAndValidate(projectId) {
|
|
3869
|
+
const auth = ctx.ensureAuth();
|
|
3870
|
+
if (!auth) throw new Error("GitLab authentication not available");
|
|
3871
|
+
const err = validateProjectId2(projectId);
|
|
3872
|
+
if (err) throw new Error(err);
|
|
3873
|
+
return auth;
|
|
3874
|
+
}
|
|
3802
3875
|
return {
|
|
3803
3876
|
gitlab_skill_list: (0, import_plugin6.tool)({
|
|
3804
3877
|
description: "List available project skills and optionally draft skills.\nSkills define step-by-step procedures for common tasks (e.g., incident retros, debugging, deployments).",
|
|
3805
3878
|
args: {
|
|
3806
|
-
project_id: z6.string().describe(
|
|
3879
|
+
project_id: z6.string().describe(
|
|
3880
|
+
'FULL project path with namespace (e.g., "gitlab-org/gitlab"). Must contain a slash. Never use just the project name.'
|
|
3881
|
+
),
|
|
3807
3882
|
include_drafts: z6.boolean().optional().describe("Also list draft skills (default: false)"),
|
|
3808
3883
|
scope: z6.enum(["projects", "groups"]).optional().describe("Scope (default: projects)"),
|
|
3809
3884
|
group_id: z6.string().optional().describe("Group path (required when scope is groups)")
|
|
3810
3885
|
},
|
|
3811
3886
|
execute: async (args) => {
|
|
3812
|
-
const auth =
|
|
3813
|
-
if (!auth) throw new Error("GitLab authentication not available");
|
|
3887
|
+
const auth = authAndValidate(args.project_id);
|
|
3814
3888
|
const { scope, id } = resolveScope2(args);
|
|
3815
3889
|
try {
|
|
3816
3890
|
const pages = await listWikiPages(auth.instanceUrl, auth.token, scope, id);
|
|
@@ -3840,14 +3914,15 @@ function makeSkillTools(ctx) {
|
|
|
3840
3914
|
gitlab_skill_load: (0, import_plugin6.tool)({
|
|
3841
3915
|
description: "Load a specific skill by name.\nSkills contain step-by-step instructions for common tasks.\nChecks published skills first, then falls back to draft skills.",
|
|
3842
3916
|
args: {
|
|
3843
|
-
project_id: z6.string().describe(
|
|
3917
|
+
project_id: z6.string().describe(
|
|
3918
|
+
'FULL project path with namespace (e.g., "gitlab-org/gitlab"). Must contain a slash. Never use just the project name.'
|
|
3919
|
+
),
|
|
3844
3920
|
name: z6.string().describe('Skill name (e.g., "incident-retro", "helm-rollback")'),
|
|
3845
3921
|
scope: z6.enum(["projects", "groups"]).optional().describe("Scope (default: projects)"),
|
|
3846
3922
|
group_id: z6.string().optional().describe("Group path (required when scope is groups)")
|
|
3847
3923
|
},
|
|
3848
3924
|
execute: async (args) => {
|
|
3849
|
-
const auth =
|
|
3850
|
-
if (!auth) throw new Error("GitLab authentication not available");
|
|
3925
|
+
const auth = authAndValidate(args.project_id);
|
|
3851
3926
|
const { scope, id } = resolveScope2(args);
|
|
3852
3927
|
try {
|
|
3853
3928
|
const page = await getWikiPage(
|
|
@@ -3879,7 +3954,9 @@ ${draft.content}`;
|
|
|
3879
3954
|
gitlab_skill_save: (0, import_plugin6.tool)({
|
|
3880
3955
|
description: "Create or update a skill.\nSkills define step-by-step procedures for common tasks.\nUse draft=true for skills that haven't been proven yet.",
|
|
3881
3956
|
args: {
|
|
3882
|
-
project_id: z6.string().describe(
|
|
3957
|
+
project_id: z6.string().describe(
|
|
3958
|
+
'FULL project path with namespace (e.g., "gitlab-org/gitlab"). Must contain a slash. Never use just the project name.'
|
|
3959
|
+
),
|
|
3883
3960
|
name: z6.string().describe('Skill name (e.g., "incident-retro")'),
|
|
3884
3961
|
content: z6.string().describe("Skill content in markdown"),
|
|
3885
3962
|
draft: z6.boolean().optional().describe("Save as draft skill (default: false)"),
|
|
@@ -3887,35 +3964,44 @@ ${draft.content}`;
|
|
|
3887
3964
|
group_id: z6.string().optional().describe("Group path (required when scope is groups)")
|
|
3888
3965
|
},
|
|
3889
3966
|
execute: async (args) => {
|
|
3890
|
-
const auth =
|
|
3891
|
-
if (!auth) throw new Error("GitLab authentication not available");
|
|
3967
|
+
const auth = authAndValidate(args.project_id);
|
|
3892
3968
|
const { scope, id } = resolveScope2(args);
|
|
3893
3969
|
const prefix = args.draft ? DRAFTS_PREFIX : SKILLS_PREFIX;
|
|
3894
3970
|
const slug = `${prefix}/${args.name}`;
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
return `Updated ${args.draft ? "draft " : ""}skill: ${args.name}`;
|
|
3898
|
-
} catch {
|
|
3971
|
+
const label = args.draft ? "draft " : "";
|
|
3972
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
3899
3973
|
try {
|
|
3900
|
-
await
|
|
3901
|
-
return `
|
|
3902
|
-
} catch
|
|
3903
|
-
|
|
3974
|
+
await updateWikiPage(auth.instanceUrl, auth.token, scope, id, slug, args.content);
|
|
3975
|
+
return `Updated ${label}skill: ${args.name}`;
|
|
3976
|
+
} catch {
|
|
3977
|
+
try {
|
|
3978
|
+
await createWikiPage(auth.instanceUrl, auth.token, scope, id, slug, args.content);
|
|
3979
|
+
return `Created ${label}skill: ${args.name}`;
|
|
3980
|
+
} catch (err) {
|
|
3981
|
+
const msg = err.message ?? "";
|
|
3982
|
+
if (msg.includes("Duplicate page") || msg.includes("reference update")) {
|
|
3983
|
+
await new Promise((r) => setTimeout(r, 1e3 * (attempt + 1)));
|
|
3984
|
+
continue;
|
|
3985
|
+
}
|
|
3986
|
+
return `Error saving skill: ${msg}`;
|
|
3987
|
+
}
|
|
3904
3988
|
}
|
|
3905
3989
|
}
|
|
3990
|
+
return `Error saving skill: failed after 3 retries`;
|
|
3906
3991
|
}
|
|
3907
3992
|
}),
|
|
3908
3993
|
gitlab_skill_promote: (0, import_plugin6.tool)({
|
|
3909
3994
|
description: "Promote a draft skill to published.\nMoves the skill from the drafts directory to the published skills directory.",
|
|
3910
3995
|
args: {
|
|
3911
|
-
project_id: z6.string().describe(
|
|
3996
|
+
project_id: z6.string().describe(
|
|
3997
|
+
'FULL project path with namespace (e.g., "gitlab-org/gitlab"). Must contain a slash. Never use just the project name.'
|
|
3998
|
+
),
|
|
3912
3999
|
name: z6.string().describe("Skill name to promote"),
|
|
3913
4000
|
scope: z6.enum(["projects", "groups"]).optional().describe("Scope (default: projects)"),
|
|
3914
4001
|
group_id: z6.string().optional().describe("Group path (required when scope is groups)")
|
|
3915
4002
|
},
|
|
3916
4003
|
execute: async (args) => {
|
|
3917
|
-
const auth =
|
|
3918
|
-
if (!auth) throw new Error("GitLab authentication not available");
|
|
4004
|
+
const auth = authAndValidate(args.project_id);
|
|
3919
4005
|
const { scope, id } = resolveScope2(args);
|
|
3920
4006
|
const draftSlug = `${DRAFTS_PREFIX}/${args.name}`;
|
|
3921
4007
|
const publishedSlug = `${SKILLS_PREFIX}/${args.name}`;
|