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.js
CHANGED
|
@@ -3468,14 +3468,51 @@ async function safeRead(instanceUrl, token, scope, id, slug) {
|
|
|
3468
3468
|
return null;
|
|
3469
3469
|
}
|
|
3470
3470
|
}
|
|
3471
|
+
var MAX_RETRIES = 3;
|
|
3472
|
+
var RETRY_DELAY_MS = 1e3;
|
|
3473
|
+
async function sleep2(ms) {
|
|
3474
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
3475
|
+
}
|
|
3471
3476
|
async function appendToPage(instanceUrl, token, scope, id, slug, newContent) {
|
|
3477
|
+
for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
|
|
3478
|
+
const existing2 = await safeRead(instanceUrl, token, scope, id, slug);
|
|
3479
|
+
if (existing2 !== null) {
|
|
3480
|
+
try {
|
|
3481
|
+
await updateWikiPage(instanceUrl, token, scope, id, slug, existing2 + "\n\n" + newContent);
|
|
3482
|
+
return;
|
|
3483
|
+
} catch (err) {
|
|
3484
|
+
if (attempt < MAX_RETRIES - 1 && err.message?.includes("reference update")) {
|
|
3485
|
+
await sleep2(RETRY_DELAY_MS * (attempt + 1));
|
|
3486
|
+
continue;
|
|
3487
|
+
}
|
|
3488
|
+
throw err;
|
|
3489
|
+
}
|
|
3490
|
+
} else {
|
|
3491
|
+
try {
|
|
3492
|
+
await createWikiPage(instanceUrl, token, scope, id, slug, newContent);
|
|
3493
|
+
return;
|
|
3494
|
+
} catch (err) {
|
|
3495
|
+
if (err.message?.includes("Duplicate page") || err.message?.includes("reference update")) {
|
|
3496
|
+
await sleep2(RETRY_DELAY_MS * (attempt + 1));
|
|
3497
|
+
continue;
|
|
3498
|
+
}
|
|
3499
|
+
throw err;
|
|
3500
|
+
}
|
|
3501
|
+
}
|
|
3502
|
+
}
|
|
3472
3503
|
const existing = await safeRead(instanceUrl, token, scope, id, slug);
|
|
3473
3504
|
if (existing !== null) {
|
|
3474
3505
|
await updateWikiPage(instanceUrl, token, scope, id, slug, existing + "\n\n" + newContent);
|
|
3475
3506
|
} else {
|
|
3476
|
-
|
|
3507
|
+
throw new Error(`Failed to append to ${slug} after ${MAX_RETRIES} retries`);
|
|
3477
3508
|
}
|
|
3478
3509
|
}
|
|
3510
|
+
function validateProjectId(projectId) {
|
|
3511
|
+
if (!projectId.includes("/")) {
|
|
3512
|
+
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.`;
|
|
3513
|
+
}
|
|
3514
|
+
return null;
|
|
3515
|
+
}
|
|
3479
3516
|
function resolveScope(args) {
|
|
3480
3517
|
if (args.scope === "groups" && args.group_id) {
|
|
3481
3518
|
return { scope: "groups", id: args.group_id };
|
|
@@ -3483,18 +3520,26 @@ function resolveScope(args) {
|
|
|
3483
3520
|
return { scope: "projects", id: args.project_id };
|
|
3484
3521
|
}
|
|
3485
3522
|
function makeMemoryTools(ctx) {
|
|
3523
|
+
function authAndValidate(projectId) {
|
|
3524
|
+
const auth = ctx.ensureAuth();
|
|
3525
|
+
if (!auth) throw new Error("GitLab authentication not available");
|
|
3526
|
+
const err = validateProjectId(projectId);
|
|
3527
|
+
if (err) throw new Error(err);
|
|
3528
|
+
return auth;
|
|
3529
|
+
}
|
|
3486
3530
|
return {
|
|
3487
3531
|
gitlab_memory_load: tool5({
|
|
3488
3532
|
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.",
|
|
3489
3533
|
args: {
|
|
3490
|
-
project_id: z5.string().describe(
|
|
3534
|
+
project_id: z5.string().describe(
|
|
3535
|
+
'FULL project path with namespace (e.g., "gitlab-org/gitlab"). Must contain a slash. Never use just the project name.'
|
|
3536
|
+
),
|
|
3491
3537
|
type: z5.enum(["all", "facts", "decisions", "patterns"]).optional().describe('Which memory to load: "all" (default), "facts", "decisions", or "patterns"'),
|
|
3492
3538
|
scope: z5.enum(["projects", "groups"]).optional().describe("Scope (default: projects)"),
|
|
3493
3539
|
group_id: z5.string().optional().describe("Group path (required when scope is groups)")
|
|
3494
3540
|
},
|
|
3495
3541
|
execute: async (args) => {
|
|
3496
|
-
const auth =
|
|
3497
|
-
if (!auth) throw new Error("GitLab authentication not available");
|
|
3542
|
+
const auth = authAndValidate(args.project_id);
|
|
3498
3543
|
const { scope, id } = resolveScope(args);
|
|
3499
3544
|
const memType = args.type ?? "all";
|
|
3500
3545
|
const slugs = MEMORY_SLUGS[memType];
|
|
@@ -3517,15 +3562,16 @@ ${content}`);
|
|
|
3517
3562
|
gitlab_memory_record: tool5({
|
|
3518
3563
|
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.",
|
|
3519
3564
|
args: {
|
|
3520
|
-
project_id: z5.string().describe(
|
|
3565
|
+
project_id: z5.string().describe(
|
|
3566
|
+
'FULL project path with namespace (e.g., "gitlab-org/gitlab"). Must contain a slash. Never use just the project name.'
|
|
3567
|
+
),
|
|
3521
3568
|
type: z5.enum(["fact", "decision", "pattern"]).describe("Type of knowledge to record"),
|
|
3522
3569
|
content: z5.string().describe("The knowledge to record (markdown)"),
|
|
3523
3570
|
scope: z5.enum(["projects", "groups"]).optional().describe("Scope (default: projects)"),
|
|
3524
3571
|
group_id: z5.string().optional().describe("Group path (required when scope is groups)")
|
|
3525
3572
|
},
|
|
3526
3573
|
execute: async (args) => {
|
|
3527
|
-
const auth =
|
|
3528
|
-
if (!auth) throw new Error("GitLab authentication not available");
|
|
3574
|
+
const auth = authAndValidate(args.project_id);
|
|
3529
3575
|
const { scope, id } = resolveScope(args);
|
|
3530
3576
|
const slug = RECORD_SLUG[args.type];
|
|
3531
3577
|
if (!slug) return `Unknown memory type: ${args.type}`;
|
|
@@ -3553,14 +3599,15 @@ ${args.content}`;
|
|
|
3553
3599
|
gitlab_memory_recall: tool5({
|
|
3554
3600
|
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.",
|
|
3555
3601
|
args: {
|
|
3556
|
-
project_id: z5.string().describe(
|
|
3602
|
+
project_id: z5.string().describe(
|
|
3603
|
+
'FULL project path with namespace (e.g., "gitlab-org/gitlab"). Must contain a slash. Never use just the project name.'
|
|
3604
|
+
),
|
|
3557
3605
|
query: z5.string().describe("What to search for"),
|
|
3558
3606
|
scope: z5.enum(["projects", "groups"]).optional().describe("Scope (default: projects)"),
|
|
3559
3607
|
group_id: z5.string().optional().describe("Group path (required when scope is groups)")
|
|
3560
3608
|
},
|
|
3561
3609
|
execute: async (args) => {
|
|
3562
|
-
const auth =
|
|
3563
|
-
if (!auth) throw new Error("GitLab authentication not available");
|
|
3610
|
+
const auth = authAndValidate(args.project_id);
|
|
3564
3611
|
const { scope, id } = resolveScope(args);
|
|
3565
3612
|
try {
|
|
3566
3613
|
const results = await searchWikiPages(
|
|
@@ -3584,7 +3631,9 @@ ${args.content}`;
|
|
|
3584
3631
|
gitlab_memory_log_session: tool5({
|
|
3585
3632
|
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.",
|
|
3586
3633
|
args: {
|
|
3587
|
-
project_id: z5.string().describe(
|
|
3634
|
+
project_id: z5.string().describe(
|
|
3635
|
+
'FULL project path with namespace (e.g., "gitlab-org/gitlab"). Must contain a slash. Never use just the project name.'
|
|
3636
|
+
),
|
|
3588
3637
|
title: z5.string().describe('Brief session title (e.g., "fix-ai-gateway-healthcheck")'),
|
|
3589
3638
|
summary: z5.string().describe(
|
|
3590
3639
|
"Session summary in markdown (what happened, what was learned, what went wrong)"
|
|
@@ -3593,25 +3642,36 @@ ${args.content}`;
|
|
|
3593
3642
|
group_id: z5.string().optional().describe("Group path (required when scope is groups)")
|
|
3594
3643
|
},
|
|
3595
3644
|
execute: async (args) => {
|
|
3596
|
-
const auth =
|
|
3597
|
-
if (!auth) throw new Error("GitLab authentication not available");
|
|
3645
|
+
const auth = authAndValidate(args.project_id);
|
|
3598
3646
|
const { scope, id } = resolveScope(args);
|
|
3599
3647
|
const date = today();
|
|
3600
3648
|
const slug = `${PREFIX}/memory/sessions/${date}-${slugify(args.title)}`;
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3649
|
+
for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
|
|
3650
|
+
try {
|
|
3651
|
+
await createWikiPage(auth.instanceUrl, auth.token, scope, id, slug, args.summary);
|
|
3652
|
+
return `Session logged: ${slug}`;
|
|
3653
|
+
} catch (err) {
|
|
3654
|
+
const msg = err.message ?? "";
|
|
3655
|
+
if (msg.includes("Duplicate page") || msg.includes("already exists") || msg.includes("422")) {
|
|
3656
|
+
try {
|
|
3657
|
+
await updateWikiPage(auth.instanceUrl, auth.token, scope, id, slug, args.summary);
|
|
3658
|
+
return `Session log updated: ${slug}`;
|
|
3659
|
+
} catch (err2) {
|
|
3660
|
+
if (attempt < MAX_RETRIES - 1 && err2.message?.includes("reference update")) {
|
|
3661
|
+
await sleep2(RETRY_DELAY_MS * (attempt + 1));
|
|
3662
|
+
continue;
|
|
3663
|
+
}
|
|
3664
|
+
return `Error logging session: ${err2.message}`;
|
|
3665
|
+
}
|
|
3666
|
+
}
|
|
3667
|
+
if (attempt < MAX_RETRIES - 1 && msg.includes("reference update")) {
|
|
3668
|
+
await sleep2(RETRY_DELAY_MS * (attempt + 1));
|
|
3669
|
+
continue;
|
|
3611
3670
|
}
|
|
3671
|
+
return `Error logging session: ${msg}`;
|
|
3612
3672
|
}
|
|
3613
|
-
return `Error logging session: ${err.message}`;
|
|
3614
3673
|
}
|
|
3674
|
+
return `Error logging session: failed after ${MAX_RETRIES} retries`;
|
|
3615
3675
|
}
|
|
3616
3676
|
})
|
|
3617
3677
|
};
|
|
@@ -3629,19 +3689,33 @@ function resolveScope2(args) {
|
|
|
3629
3689
|
}
|
|
3630
3690
|
return { scope: "projects", id: args.project_id };
|
|
3631
3691
|
}
|
|
3692
|
+
function validateProjectId2(projectId) {
|
|
3693
|
+
if (!projectId.includes("/")) {
|
|
3694
|
+
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.`;
|
|
3695
|
+
}
|
|
3696
|
+
return null;
|
|
3697
|
+
}
|
|
3632
3698
|
function makeSkillTools(ctx) {
|
|
3699
|
+
function authAndValidate(projectId) {
|
|
3700
|
+
const auth = ctx.ensureAuth();
|
|
3701
|
+
if (!auth) throw new Error("GitLab authentication not available");
|
|
3702
|
+
const err = validateProjectId2(projectId);
|
|
3703
|
+
if (err) throw new Error(err);
|
|
3704
|
+
return auth;
|
|
3705
|
+
}
|
|
3633
3706
|
return {
|
|
3634
3707
|
gitlab_skill_list: tool6({
|
|
3635
3708
|
description: "List available project skills and optionally draft skills.\nSkills define step-by-step procedures for common tasks (e.g., incident retros, debugging, deployments).",
|
|
3636
3709
|
args: {
|
|
3637
|
-
project_id: z6.string().describe(
|
|
3710
|
+
project_id: z6.string().describe(
|
|
3711
|
+
'FULL project path with namespace (e.g., "gitlab-org/gitlab"). Must contain a slash. Never use just the project name.'
|
|
3712
|
+
),
|
|
3638
3713
|
include_drafts: z6.boolean().optional().describe("Also list draft skills (default: false)"),
|
|
3639
3714
|
scope: z6.enum(["projects", "groups"]).optional().describe("Scope (default: projects)"),
|
|
3640
3715
|
group_id: z6.string().optional().describe("Group path (required when scope is groups)")
|
|
3641
3716
|
},
|
|
3642
3717
|
execute: async (args) => {
|
|
3643
|
-
const auth =
|
|
3644
|
-
if (!auth) throw new Error("GitLab authentication not available");
|
|
3718
|
+
const auth = authAndValidate(args.project_id);
|
|
3645
3719
|
const { scope, id } = resolveScope2(args);
|
|
3646
3720
|
try {
|
|
3647
3721
|
const pages = await listWikiPages(auth.instanceUrl, auth.token, scope, id);
|
|
@@ -3671,14 +3745,15 @@ function makeSkillTools(ctx) {
|
|
|
3671
3745
|
gitlab_skill_load: tool6({
|
|
3672
3746
|
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.",
|
|
3673
3747
|
args: {
|
|
3674
|
-
project_id: z6.string().describe(
|
|
3748
|
+
project_id: z6.string().describe(
|
|
3749
|
+
'FULL project path with namespace (e.g., "gitlab-org/gitlab"). Must contain a slash. Never use just the project name.'
|
|
3750
|
+
),
|
|
3675
3751
|
name: z6.string().describe('Skill name (e.g., "incident-retro", "helm-rollback")'),
|
|
3676
3752
|
scope: z6.enum(["projects", "groups"]).optional().describe("Scope (default: projects)"),
|
|
3677
3753
|
group_id: z6.string().optional().describe("Group path (required when scope is groups)")
|
|
3678
3754
|
},
|
|
3679
3755
|
execute: async (args) => {
|
|
3680
|
-
const auth =
|
|
3681
|
-
if (!auth) throw new Error("GitLab authentication not available");
|
|
3756
|
+
const auth = authAndValidate(args.project_id);
|
|
3682
3757
|
const { scope, id } = resolveScope2(args);
|
|
3683
3758
|
try {
|
|
3684
3759
|
const page = await getWikiPage(
|
|
@@ -3710,7 +3785,9 @@ ${draft.content}`;
|
|
|
3710
3785
|
gitlab_skill_save: tool6({
|
|
3711
3786
|
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.",
|
|
3712
3787
|
args: {
|
|
3713
|
-
project_id: z6.string().describe(
|
|
3788
|
+
project_id: z6.string().describe(
|
|
3789
|
+
'FULL project path with namespace (e.g., "gitlab-org/gitlab"). Must contain a slash. Never use just the project name.'
|
|
3790
|
+
),
|
|
3714
3791
|
name: z6.string().describe('Skill name (e.g., "incident-retro")'),
|
|
3715
3792
|
content: z6.string().describe("Skill content in markdown"),
|
|
3716
3793
|
draft: z6.boolean().optional().describe("Save as draft skill (default: false)"),
|
|
@@ -3718,35 +3795,44 @@ ${draft.content}`;
|
|
|
3718
3795
|
group_id: z6.string().optional().describe("Group path (required when scope is groups)")
|
|
3719
3796
|
},
|
|
3720
3797
|
execute: async (args) => {
|
|
3721
|
-
const auth =
|
|
3722
|
-
if (!auth) throw new Error("GitLab authentication not available");
|
|
3798
|
+
const auth = authAndValidate(args.project_id);
|
|
3723
3799
|
const { scope, id } = resolveScope2(args);
|
|
3724
3800
|
const prefix = args.draft ? DRAFTS_PREFIX : SKILLS_PREFIX;
|
|
3725
3801
|
const slug = `${prefix}/${args.name}`;
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
return `Updated ${args.draft ? "draft " : ""}skill: ${args.name}`;
|
|
3729
|
-
} catch {
|
|
3802
|
+
const label = args.draft ? "draft " : "";
|
|
3803
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
3730
3804
|
try {
|
|
3731
|
-
await
|
|
3732
|
-
return `
|
|
3733
|
-
} catch
|
|
3734
|
-
|
|
3805
|
+
await updateWikiPage(auth.instanceUrl, auth.token, scope, id, slug, args.content);
|
|
3806
|
+
return `Updated ${label}skill: ${args.name}`;
|
|
3807
|
+
} catch {
|
|
3808
|
+
try {
|
|
3809
|
+
await createWikiPage(auth.instanceUrl, auth.token, scope, id, slug, args.content);
|
|
3810
|
+
return `Created ${label}skill: ${args.name}`;
|
|
3811
|
+
} catch (err) {
|
|
3812
|
+
const msg = err.message ?? "";
|
|
3813
|
+
if (msg.includes("Duplicate page") || msg.includes("reference update")) {
|
|
3814
|
+
await new Promise((r) => setTimeout(r, 1e3 * (attempt + 1)));
|
|
3815
|
+
continue;
|
|
3816
|
+
}
|
|
3817
|
+
return `Error saving skill: ${msg}`;
|
|
3818
|
+
}
|
|
3735
3819
|
}
|
|
3736
3820
|
}
|
|
3821
|
+
return `Error saving skill: failed after 3 retries`;
|
|
3737
3822
|
}
|
|
3738
3823
|
}),
|
|
3739
3824
|
gitlab_skill_promote: tool6({
|
|
3740
3825
|
description: "Promote a draft skill to published.\nMoves the skill from the drafts directory to the published skills directory.",
|
|
3741
3826
|
args: {
|
|
3742
|
-
project_id: z6.string().describe(
|
|
3827
|
+
project_id: z6.string().describe(
|
|
3828
|
+
'FULL project path with namespace (e.g., "gitlab-org/gitlab"). Must contain a slash. Never use just the project name.'
|
|
3829
|
+
),
|
|
3743
3830
|
name: z6.string().describe("Skill name to promote"),
|
|
3744
3831
|
scope: z6.enum(["projects", "groups"]).optional().describe("Scope (default: projects)"),
|
|
3745
3832
|
group_id: z6.string().optional().describe("Group path (required when scope is groups)")
|
|
3746
3833
|
},
|
|
3747
3834
|
execute: async (args) => {
|
|
3748
|
-
const auth =
|
|
3749
|
-
if (!auth) throw new Error("GitLab authentication not available");
|
|
3835
|
+
const auth = authAndValidate(args.project_id);
|
|
3750
3836
|
const { scope, id } = resolveScope2(args);
|
|
3751
3837
|
const draftSlug = `${DRAFTS_PREFIX}/${args.name}`;
|
|
3752
3838
|
const publishedSlug = `${SKILLS_PREFIX}/${args.name}`;
|