GitLab tools plugin for OpenCode - provides GitLab API access for merge requests, issues, pipelines, and more



A comprehensive GitLab API plugin for OpenCode that provides AI-powered access to GitLab's REST and GraphQL APIs. This plugin enables seamless interaction with merge requests, issues, pipelines, repositories, epics, snippets, audit events, and more through natural language commands.
- Features
- Architecture
- Installation
- Configuration
- Available Tools
- Usage Examples
- Development
- CI/CD Pipeline
- API Reference
- Contributing
- License
- 🔀 Merge Requests: Full CRUD operations, discussions, notes, changes, commits, and pipelines
- 📝 Issues: Create, read, update, and comment on issues with advanced filtering
- 🎯 Work Items: Unified interface for issues, epics, tasks, and other work tracking items
- 🚀 CI/CD Pipelines: Monitor, analyze, retry pipeline jobs, and validate CI/CD configurations
- 📦 Repository Operations: File management, commits, branches, tree navigation, and commit discussions
- 🔍 Advanced Search: Multi-scope search across projects, code, issues, merge requests, commits, users, milestones, and documentation
- 📊 Epics: Enterprise-level epic management with issue associations
- 💬 Snippets: Snippet discussions, notes, and comments management
- 🔗 Universal Discussions: Unified interface for discussions across all GitLab resources
- ✅ TODOs: Personal task management and notifications
- 🔒 Security: Vulnerability scanning and security report access
- 📚 Wiki: Wiki page content retrieval
- 🔍 Audit Events: Track project, group, and instance-level security events
- 🔧 Git Commands: Execute safe, read-only git operations
- 👥 Project Management: Member management and project details
- TypeScript: Full type safety with comprehensive type definitions
- ESM Support: Modern ES modules for optimal tree-shaking
- Zod Validation: Runtime schema validation for all API inputs
- GraphQL Support: Native GraphQL API support with type-safe mutations and queries for TODOs, Notes, Discussions, Auto-merge, and Security tools
- Cursor-Based Pagination: GraphQL-powered pagination with first/after and last/before cursors for efficient data fetching
- GID Validation: Automatic validation of GitLab Global IDs with descriptive error messages
- Error Handling: Robust error handling with detailed error messages
- Authentication: Multiple authentication methods (OAuth, API tokens)
- Rate Limiting: Built-in handling for GitLab API rate limits
- Caching: Efficient API response handling
- Modular Architecture: Clean separation of concerns with client and tool modules
- Comprehensive Testing: 180 tests with full coverage of all features
The following tools use GitLab's GraphQL API for enhanced functionality:
| Category | Tools | Benefits |
| --------------- | ----------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| TODOs | gitlab_list_todos, gitlab_get_todo_count | Cursor-based pagination, rich filtering |
| Notes | gitlab_list_notes, gitlab_get_note, gitlab_create_note | Unified interface for all resource types, efficient pagination |
| Discussions | gitlab_list_discussions, gitlab_get_discussion, gitlab_create_discussion, gitlab_resolve_discussion | Unified interface for all resource types, cursor-based pagination, code position support |
| Auto-merge | gitlab_set_mr_auto_merge | MWPS (Merge When Pipeline Succeeds), merge train support |
| Security | All vulnerability management tools | Type-safe GID validation, mutation support |
``mermaid
graph TB
subgraph "OpenCode Environment"
AI[AI Assistant]
Plugin[GitLab Plugin]
end
subgraph "Plugin Components"
Tools[Tool Definitions]
Client[GitLab API Client]
Auth[Authentication Manager]
Validator[Zod Schema Validator]
end
subgraph "GitLab API"
REST[REST API v4]
GraphQL[GraphQL API]
MR[Merge Requests]
Issues[Issues]
Pipelines[Pipelines]
Repos[Repositories]
Epics[Epics]
Security[Security]
end
AI -->|Natural Language| Plugin
Plugin --> Tools
Tools --> Validator
Validator --> Client
Client --> Auth
Auth --> REST
Auth --> GraphQL
REST --> MR
REST --> Issues
REST --> Pipelines
REST --> Repos
REST --> Epics
GraphQL --> Security
GraphQL --> MR
`
`mermaid
graph LR
subgraph "src/index.ts"
A[GitLabApiClient Class]
B[Authentication Functions]
C[Tool Definitions]
D[Plugin Export]
end
A --> A1[HTTP Methods]
A --> A2[Merge Request APIs]
A --> A3[Issue APIs]
A --> A4[Pipeline APIs]
A --> A5[Repository APIs]
A --> A6[Epic APIs]
A --> A7[Search APIs]
B --> B1[readTokenFromAuthStorage]
B --> B2[getGitLabClient]
C --> C1[69 Tool Definitions]
D --> A
D --> B
D --> C
`
`mermaid
sequenceDiagram
participant User
participant Plugin
participant AuthManager
participant Storage
participant GitLab
User->>Plugin: Initialize Plugin
Plugin->>AuthManager: getGitLabClient()
AuthManager->>AuthManager: Check GITLAB_TOKEN env
alt Token in Environment
AuthManager->>GitLab: Use env token
else No env token
AuthManager->>Storage: Read ~/.local/share/opencode/auth.json
Storage->>AuthManager: Return token
AuthManager->>GitLab: Use stored token
end
GitLab->>Plugin: API Response
Plugin->>User: Tool Result
`
- Node.js >= 18.0.0
- npm >= 9.0.0 or Bun
- GitLab account with API access
- GitLab Personal Access Token or OAuth token
`bashInstall the package
npm install @gitlab/opencode-gitlab-plugin
$3
`bash
Using npm
npm install git+https://gitlab.com/gitlab-org/editor-extensions/opencode-gitlab-plugin.gitUsing Bun
bun add git+https://gitlab.com/gitlab-org/editor-extensions/opencode-gitlab-plugin.git
`$3
`bash
Install specific version from npm
npm install @gitlab/opencode-gitlab-plugin@1.0.0Install from specific git tag
npm install git+https://gitlab.com/gitlab-org/editor-extensions/opencode-gitlab-plugin.git#v1.0.0Install from specific branch
npm install git+https://gitlab.com/gitlab-org/editor-extensions/opencode-gitlab-plugin.git#main
`$3
Add to your
package.json:`json
{
"dependencies": {
"@gitlab/opencode-gitlab-plugin": "^1.0.0"
}
}
`Then run:
`bash
npm install
`⚙️ Configuration
$3
`bash
Required: GitLab API Token
export GITLAB_TOKEN=glpat-xxxxxxxxxxxxxxxxxxxxOptional: Custom GitLab Instance (defaults to https://gitlab.com)
export GITLAB_INSTANCE_URL=https://gitlab.example.com
`$3
Add the following plugin to your opencode configuration
~/.config/opencode/opencode.json:`json
{
"$schema": "https://opencode.ai/config.json",
"plugin": ["@gitlab/opencode-gitlab-plugin"]
}
`$3
The plugin supports reading tokens from OpenCode's auth storage:
Location:
~/.local/share/opencode/auth.jsonFormat:
`json
{
"gitlab": {
"type": "oauth",
"access": "your-oauth-token"
}
}
`Or for API tokens:
`json
{
"gitlab": {
"type": "api",
"key": "glpat-xxxxxxxxxxxxxxxxxxxx"
}
}
`$3
1.
GITLAB_TOKEN environment variable (highest priority)
2. OpenCode auth storage (~/.local/share/opencode/auth.json)
3. Error if no token found🛠️ Available Tools
The plugin provides 64 tools organized into the following categories:
$3
| Tool | Description |
| --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
|
gitlab_get_merge_request | Get details of a specific merge request with title, description, state, author, assignees, reviewers, labels, and diff stats |
| gitlab_list_merge_requests | List merge requests with filtering by state, scope, and labels |
| gitlab_create_merge_request | Create a new merge request |
| gitlab_update_merge_request | Update merge request title, description, state, assignees, reviewers, and labels |
| gitlab_get_mr_changes | Get file changes/diffs for a merge request |
| gitlab_get_mr_details | Get additional MR details (commits or pipelines) with detail_type parameter |
| gitlab_list_merge_request_diffs | List file diffs with pagination support for large changesets |
| gitlab_set_mr_auto_merge | Enable auto-merge (MWPS) using GraphQL API when pipeline succeeds |$3
| Tool | Description |
| --------------------- | ---------------------------------------------------------------------------- |
|
gitlab_create_issue | Create a new issue with title, description, labels, assignees, and milestone |
| gitlab_get_issue | Get issue details including state, author, assignees, labels, and comments |
| gitlab_list_issues | List issues with filtering by state, labels, assignee, and milestone |$3
| Tool | Description |
| --------------------------- | ----------------------------------------------------------------------------- |
|
gitlab_get_epic | Get epic details with title, description, state, dates, and associated issues |
| gitlab_list_epics | List epics with filtering by state, author, and labels |
| gitlab_create_epic | Create a new epic in a group |
| gitlab_update_epic | Update epic title, description, labels, dates, and state |
| gitlab_manage_epic_issues | Manage issues linked to an epic (list, add, remove) with action parameter |$3
| Tool | Description |
| ---------------------------------- | ------------------------------------------------------------------------------- |
|
gitlab_list_pipelines | List pipelines with filtering by status, ref, and username |
| gitlab_get_pipeline | Get pipeline details with jobs and status |
| gitlab_list_pipeline_jobs | List all jobs in a pipeline with optional scope filtering |
| gitlab_get_job_log | Get the log output of a specific CI job |
| gitlab_retry_job | Retry a failed or canceled job |
| gitlab_get_pipeline_failing_jobs | Get only failed jobs for easier debugging |
| gitlab_lint_ci_config | Validate CI/CD YAML config with mode parameter (content or existing repository) |$3
| Tool | Description |
| ----------------------------- | ------------------------------------------------------ |
|
gitlab_get_file | Get file contents from any branch, tag, or commit |
| gitlab_get_commit | Get commit details with metadata, author, and stats |
| gitlab_list_commits | List commits with filtering by branch, path, and dates |
| gitlab_get_commit_diff | Get diff for a specific commit |
| gitlab_list_repository_tree | List files and directories at a given path |
| gitlab_list_branches | List all branches in a repository |
| gitlab_get_commit_comments | Get all commit comments in flat structure |$3
| Tool | Description |
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
gitlab_search | Unified search across all GitLab resources with scope-specific options (projects, issues, merge_requests, milestones, users, blobs, commits, notes, wiki_blobs, group_projects) |
| gitlab_documentation_search | Search GitLab official documentation at docs.gitlab.com |$3
| Tool | Description |
| ------------------------------ | -------------------------------------------------------- |
|
gitlab_get_work_item | Get a work item (unified model for issues, epics, tasks) |
| gitlab_list_work_items | List work items in a project or group |
| gitlab_get_work_item_notes | Get all comments for a work item |
| gitlab_create_work_item | Create a new work item |
| gitlab_update_work_item | Update work item title, description, state, and labels |
| gitlab_create_work_item_note | Add a comment to a work item |$3
| Tool | Description |
| ----------------------------------------- | ------------------------------------------------------------------------- |
|
gitlab_list_vulnerabilities | List security vulnerabilities with filtering by state, severity, and type |
| gitlab_get_vulnerability_details | Get detailed vulnerability information with remediation |
| gitlab_create_vulnerability_issue | Create an issue linked to vulnerabilities (GraphQL) |
| gitlab_dismiss_vulnerability | Dismiss vulnerability with reason (GraphQL) |
| gitlab_confirm_vulnerability | Confirm a vulnerability as valid (GraphQL) |
| gitlab_revert_vulnerability_to_detected | Revert vulnerability state (GraphQL) |
| gitlab_update_vulnerability_severity | Update vulnerability severity level (GraphQL) |
| gitlab_link_vulnerability_to_issue | Link vulnerabilities to existing issue (GraphQL) |Note: All GraphQL-based security tools include automatic GID (Global ID) format validation.
$3
| Tool | Description |
| ----------------------- | ------------------------------------------------------ |
|
gitlab_list_todos | List TODO items with cursor-based pagination (GraphQL) |
| gitlab_mark_todo_done | Mark TODOs as done with action parameter (one or all) |
| gitlab_get_todo_count | Get count of pending TODOs (GraphQL) |$3
| Tool | Description |
| ----------------------------- | ---------------------------------- |
|
gitlab_get_project | Get project details and metadata |
| gitlab_list_project_members | List all members of a project |
| gitlab_get_current_user | Get authenticated user information |$3
| Tool | Description |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
|
gitlab_list_discussions | List discussions (comment threads) on any GitLab resource (MRs, issues, epics, commits, snippets) with cursor pagination |
| gitlab_get_discussion | Get a specific discussion thread with all replies from any resource type |
| gitlab_create_discussion | Create a new discussion thread OR reply to an existing one (supports code-position comments for MRs and commits) |
| gitlab_resolve_discussion | Mark a discussion thread as resolved or unresolve it (MRs and issues only) |$3
| Tool | Description |
| -------------------- | --------------------------------------------------------------------------------------------------- |
|
gitlab_list_notes | List all notes/comments on any resource (MRs, issues, epics, snippets) with cursor-based pagination |
| gitlab_get_note | Get a single note by ID (issues and epics only) |
| gitlab_create_note | Add a simple comment to any resource (for thread replies, use gitlab_create_discussion) |$3
| Tool | Description |
| ----------------------------------- | -------------------------------------------------------- |
|
gitlab_list_project_audit_events | List audit events for a project (requires owner role) |
| gitlab_list_group_audit_events | List audit events for a group (requires owner role) |
| gitlab_list_instance_audit_events | List instance-level audit events (requires admin access) |$3
| Tool | Description |
| ----------------- | ------------------------------------------------------------------------------------------------------ |
|
run_git_command | Execute safe, read-only git commands (status, log, show, diff, blame, etc.) with security restrictions |$3
| Tool | Description |
| ---------------------- | ---------------------------------- |
|
gitlab_get_wiki_page | Get wiki page content and metadata |💡 Usage Examples
$3
`javascript
import gitlabPlugin from '@gitlab/opencode-gitlab-plugin';const plugin = await gitlabPlugin({});
// Create a new issue
const issue = await plugin.tool.gitlab_create_issue.execute({
project_id: 'my-group/my-project',
title: 'Fix authentication bug',
description:
'## Problem\n\nUsers cannot login with OAuth.\n\n## Steps to Reproduce\n1. Go to login page\n2. Click OAuth button\n3. Error occurs',
labels: 'bug,authentication,priority::high',
assignee_ids: [42],
milestone_id: 10,
due_date: '2025-12-31',
});
console.log(
Issue created: ${issue.web_url});// Add a comment to the issue (using unified notes tool)
await plugin.tool.gitlab_create_note.execute({
resource_type: 'issue',
project_id: 'my-group/my-project',
iid: issue.iid,
body: 'I will start working on this today.',
});
// List all issues with specific labels
const issues = await plugin.tool.gitlab_list_issues.execute({
project_id: 'my-group/my-project',
labels: 'bug',
state: 'opened',
});
`$3
`javascript
// Get merge request details
const mr = await plugin.tool.gitlab_get_merge_request.execute({
project_id: 'gitlab-org/gitlab',
mr_iid: 12345,
include_changes: true,
});// Get discussions (unified tool supports all resource types)
const discussions = await plugin.tool.gitlab_list_discussions.execute({
resource_type: 'merge_request',
project_id: 'gitlab-org/gitlab',
iid: 12345,
});
// Add a review comment (creates a new discussion)
await plugin.tool.gitlab_create_discussion.execute({
resource_type: 'merge_request',
project_id: 'gitlab-org/gitlab',
iid: 12345,
body: 'LGTM! Great work on this feature.',
});
// Reply to an existing discussion thread
await plugin.tool.gitlab_create_discussion.execute({
resource_type: 'merge_request',
project_id: 'gitlab-org/gitlab',
iid: 12345,
discussion_id: discussions.discussions.nodes[0].id,
body: 'Thanks for addressing the feedback!',
});
`$3
`javascript
// List recent pipelines
const pipelines = await plugin.tool.gitlab_list_pipelines.execute({
project_id: 'my-group/my-project',
status: 'failed',
limit: 5,
});// Get failed jobs
const failedJobs = await plugin.tool.gitlab_get_pipeline_failing_jobs.execute({
project_id: 'my-group/my-project',
pipeline_id: pipelines[0].id,
});
// Get job logs
for (const job of failedJobs) {
const log = await plugin.tool.gitlab_get_job_log.execute({
project_id: 'my-group/my-project',
job_id: job.id,
});
console.log(
Job ${job.name} failed with:\n${log});
}// Retry failed jobs
await plugin.tool.gitlab_retry_job.execute({
project_id: 'my-group/my-project',
job_id: failedJobs[0].id,
});
`$3
`javascript
// Create an epic
const epic = await plugin.tool.gitlab_create_epic.execute({
group_id: 'my-group',
title: 'Q1 2025 Features',
description: 'All features planned for Q1 2025',
start_date: '2025-01-01',
end_date: '2025-03-31',
labels: 'Q1,planning',
});// Add issues to epic
await plugin.tool.gitlab_add_issue_to_epic.execute({
group_id: 'my-group',
epic_iid: epic.iid,
issue_id: 123,
});
// List all issues in epic
const epicIssues = await plugin.tool.gitlab_list_epic_issues.execute({
group_id: 'my-group',
epic_iid: epic.iid,
});
// Add a comment (creates a new discussion)
await plugin.tool.gitlab_create_discussion.execute({
resource_type: 'epic',
group_id: 'my-group',
iid: epic.iid,
body: 'Epic created and issues linked successfully!',
});
`$3
`javascript
// Search for code containing specific patterns
const codeResults = await plugin.tool.gitlab_blob_search.execute({
search: 'async function processPayment',
project_id: 'my-group/my-project',
limit: 10,
});// Search for related issues
const issues = await plugin.tool.gitlab_issue_search.execute({
search: 'payment processing bug',
project_id: 'my-group/my-project',
state: 'opened',
});
// Get file content
const fileContent = await plugin.tool.gitlab_get_file.execute({
project_id: 'my-group/my-project',
file_path: 'src/payment/processor.ts',
ref: 'main',
});
`$3
`javascript
// Get TODO count (uses GraphQL)
const todoCount = await plugin.tool.gitlab_get_todo_count.execute({});// List pending TODOs with cursor-based pagination
const firstPage = await plugin.tool.gitlab_list_todos.execute({
state: 'pending',
type: 'MergeRequest',
first: 20,
});
// Get next page using cursor
if (firstPage.todos.pageInfo.hasNextPage) {
const nextPage = await plugin.tool.gitlab_list_todos.execute({
state: 'pending',
type: 'MergeRequest',
first: 20,
after: firstPage.todos.pageInfo.endCursor,
});
}
// Mark specific TODO as done
await plugin.tool.gitlab_mark_todo_done.execute({
todo_id: firstPage.todos.nodes[0].id,
});
// Mark all TODOs as done
await plugin.tool.gitlab_mark_all_todos_done.execute({});
`$3
`javascript
// Create a commit with multiple file operations
const commit = await plugin.tool.gitlab_create_commit.execute({
project_id: 'my-group/my-project',
branch: 'feature/new-api',
commit_message: 'feat: add new API endpoints',
actions: [
{
action: 'create',
file_path: 'src/api/v2/users.ts',
content: 'export const getUsers = async () => { ... }',
},
{
action: 'update',
file_path: 'src/api/index.ts',
content: 'export * from "./v2/users";',
},
{
action: 'delete',
file_path: 'src/api/deprecated.ts',
},
],
author_name: 'John Doe',
author_email: 'john@example.com',
});
`$3
`javascript
// Get merge request to retrieve current HEAD SHA
const mr = await plugin.tool.gitlab_get_merge_request.execute({
project_id: 'my-group/my-project',
mr_iid: 123,
});// Enable auto-merge when pipeline succeeds (GraphQL)
const result = await plugin.tool.gitlab_set_mr_auto_merge.execute({
project_id: 'my-group/my-project',
mr_iid: 123,
sha: mr.sha, // Required to prevent race conditions
strategy: 'MERGE_WHEN_CHECKS_PASS', // or 'ADD_TO_MERGE_TRAIN_WHEN_CHECKS_PASS'
});
console.log(
Auto-merge enabled: ${result.mergeRequest.autoMergeEnabled});
`$3
`javascript
// List vulnerabilities in a project
const vulnerabilities = await plugin.tool.gitlab_list_vulnerabilities.execute({
project_id: 'my-group/my-project',
state: 'detected',
severity: 'high',
report_type: 'sast',
});// Create an issue for critical vulnerabilities
const issue = await plugin.tool.gitlab_create_vulnerability_issue.execute({
project_path: 'my-group/my-project',
vulnerability_ids: ['gid://gitlab/Vulnerability/123', 'gid://gitlab/Vulnerability/124'],
});
// Dismiss a false positive
await plugin.tool.gitlab_dismiss_vulnerability.execute({
vulnerability_id: 'gid://gitlab/Vulnerability/125',
reason: 'FALSE_POSITIVE',
comment: 'This is a test file and not part of production code',
});
// Confirm a real vulnerability
await plugin.tool.gitlab_confirm_vulnerability.execute({
vulnerability_id: 'gid://gitlab/Vulnerability/126',
comment: 'Confirmed - needs immediate attention',
});
// Update severity based on assessment
await plugin.tool.gitlab_update_vulnerability_severity.execute({
vulnerability_ids: ['gid://gitlab/Vulnerability/127'],
severity: 'CRITICAL',
comment: 'Upgrading to critical - affects production authentication',
});
// Link vulnerabilities to existing issue
await plugin.tool.gitlab_link_vulnerability_to_issue.execute({
issue_id: 'gid://gitlab/Issue/42',
vulnerability_ids: ['gid://gitlab/Vulnerability/128', 'gid://gitlab/Vulnerability/129'],
});
`🔧 Development
$3
`
opencode-gitlab-plugin/
├── src/
│ ├── client/ # API client modules
│ │ ├── base.ts # Base client with HTTP & GraphQL methods
│ │ ├── security.ts # Security/vulnerability management
│ │ ├── issues.ts # Issue management
│ │ ├── merge-requests.ts # Merge request operations
│ │ ├── pipelines.ts # CI/CD pipeline operations
│ │ ├── repository.ts # Repository operations
│ │ ├── epics.ts # Epic management
│ │ ├── search.ts # Search operations
│ │ ├── todos.ts # TODO management
│ │ ├── wikis.ts # Wiki operations
│ │ ├── work-items.ts # Work item operations
│ │ ├── audit.ts # Audit events
│ │ ├── git.ts # Git operations
│ │ └── index.ts # Client exports
│ ├── tools/ # Tool definitions
│ │ ├── security.ts # Security tool definitions
│ │ ├── issues.ts # Issue tool definitions
│ │ ├── merge-requests.ts # MR tool definitions
│ │ ├── pipelines.ts # Pipeline tool definitions
│ │ ├── repository.ts # Repository tool definitions
│ │ ├── discussions-unified.ts # Unified discussion tools (4 tools)
│ │ ├── notes-unified.ts # Unified notes tools (3 tools)
│ │ └── ... # Other tool definitions
│ ├── index.ts # Main plugin entry point
│ ├── utils.ts # Utility functions
│ └── validation.ts # GID validation utilities
├── tests/ # Test suite (180 tests)
│ ├── client/ # Client tests
│ ├── tools/ # Tool tests
│ ├── validation.test.ts # Validation tests
│ └── utils.test.ts # Utility tests
├── dist/ # Compiled output (generated)
│ ├── index.js # ESM bundle
│ └── index.d.ts # TypeScript definitions
├── .husky/ # Git hooks
│ ├── commit-msg # Commitlint hook
│ └── pre-commit # Lint-staged hook
├── .gitlab-ci.yml # CI/CD pipeline configuration
├── package.json # Package metadata
├── tsconfig.json # TypeScript configuration
├── vitest.config.ts # Vitest test configuration
├── .eslintrc.json # ESLint configuration
├── .prettierrc.json # Prettier configuration
├── .commitlintrc.json # Commitlint configuration
├── .releaserc.json # Semantic-release configuration
├── CHANGELOG.md # Auto-generated changelog
├── INSTALLATION.md # Installation guide
└── README.md # This file
`$3
`bash
Clone the repository
git clone https://gitlab.com/gitlab-org/editor-extensions/opencode-gitlab-plugin.git
cd opencode-gitlab-pluginInstall dependencies
npm installBuild the plugin
npm run buildWatch mode for development
npm run devRun linting
npm run lintFix linting issues
npm run lint:fixFormat code
npm run formatCheck formatting
npm run format:check
`$3
The project uses Husky for Git hooks:
- pre-commit: Runs lint-staged to lint and format staged files
- commit-msg: Validates commit messages using commitlint
$3
This project follows Conventional Commits:
`
(): `Types:
-
feat: New feature
- fix: Bug fix
- docs: Documentation changes
- style: Code style changes (formatting, etc.)
- refactor: Code refactoring
- perf: Performance improvements
- test: Adding or updating tests
- build: Build system changes
- ci: CI/CD changes
- chore: Other changes (dependencies, etc.)
- revert: Revert a previous commitExamples:
`bash
git commit -m "feat: add support for GitLab wiki pages"
git commit -m "fix: handle empty API responses correctly"
git commit -m "docs: update installation instructions"
git commit -m "ci: add automated release workflow"
`$3
`bash
Build for production
npm run buildThe build process:
1. Compiles TypeScript to ESM
2. Generates type definitions
3. Outputs to dist/ directory
`$3
`bash
Run tests
npm testRun tests in watch mode
npm run test:watchRun tests with coverage
npm run test:coverage
`Test Coverage:
- 180 tests across 21 test files
- Client tests for all API methods (REST and GraphQL)
- Tool tests for all tool definitions
- Validation tests for GID utilities
- GraphQL method tests for Notes, Discussions, TODOs
- All tests passing ✅
🚀 CI/CD Pipeline
$3
`mermaid
graph LR
A[Test] --> B[Build]
B --> C[Release] A --> A1[Lint]
A --> A2[Format Check]
A --> A3[Unit Tests]
B --> B1[TypeScript Build]
B --> B2[Generate Types]
C --> C1[Semantic Release]
C --> C2[Publish to Registry]
C --> C3[Create Git Tag]
C --> C4[Update Changelog]
`$3
The
.gitlab-ci.yml defines the following stages:#### 1. Test Stage
- test:lint: Runs ESLint on source code
- test:format: Checks code formatting with Prettier
- test:unit: Runs unit tests (placeholder)
#### 2. Build Stage
- build: Compiles TypeScript and generates artifacts
- Uses tsup for bundling
- Generates ESM output
- Creates TypeScript definitions
- Artifacts expire in 1 week
#### 3. Release Stage
- release: Automated versioning and publishing
- Only runs on main branch
- Uses semantic-release
- Publishes to GitLab Package Registry
- Creates Git tags
- Updates CHANGELOG.md
- Artifacts never expire
$3
`mermaid
sequenceDiagram
participant Dev as Developer
participant Git as Git Repository
participant CI as GitLab CI
participant SR as Semantic Release
participant Reg as Package Registry Dev->>Git: Push to main branch
Git->>CI: Trigger pipeline
CI->>CI: Run tests
CI->>CI: Build package
CI->>SR: Run semantic-release
SR->>SR: Analyze commits
SR->>SR: Determine version
SR->>SR: Generate changelog
SR->>Git: Create tag & commit
SR->>Reg: Publish package
SR->>Git: Create GitLab release
`$3
The release process is fully automated using semantic-release:
1. Commit Analysis: Analyzes commit messages since last release
2. Version Calculation: Determines next version based on commit types
-
fix: → Patch version (1.0.x)
- feat: → Minor version (1.x.0)
- BREAKING CHANGE: → Major version (x.0.0)
3. Changelog Generation: Updates CHANGELOG.md
4. Package Publishing: Publishes to GitLab Package Registry
5. Git Tagging: Creates and pushes version tag
6. GitLab Release: Creates release notes on GitLab$3
The CI/CD pipeline uses the following variables:
-
CI_JOB_TOKEN: GitLab CI token (automatic)
- CI_PROJECT_ID: Project ID (automatic)
- CI_API_V4_URL: GitLab API URL (automatic)
- HUSKY: Set to 0 to disable hooks in CI📚 API Reference
$3
The core API client that handles all GitLab REST API interactions.
#### Constructor
`typescript
constructor(instanceUrl: string, token: string)
`#### HTTP Methods
`typescript
async fetch(method: string, path: string, body?: unknown): Promise
async fetchText(method: string, path: string): Promise
`#### GraphQL Methods
`typescript
/**
* Execute a GraphQL query or mutation
* @template T - The expected type of the data field in the GraphQL response
* @param query - The GraphQL query or mutation string
* @param variables - Optional variables for the query
* @returns The data from the GraphQL response
*/
async fetchGraphQL(query: string, variables?: Record): Promise
`Features:
- Full TypeScript type safety with generic return types
- Automatic error handling for both HTTP and GraphQL errors
- Support for query variables
- Cursor-based pagination support for large result sets
- Used by TODOs, Notes, Discussions, Auto-merge, and Security tools
Example:
`typescript
const result = await client.fetchGraphQL<{ vulnerability: { id: string } }>(
mutation($id: VulnerabilityID!) {,
{ id: 'gid://gitlab/Vulnerability/123' }
);
`Pagination Example:
`typescript
// First page
const page1 = await plugin.tool.gitlab_list_todos.execute({ first: 20 });
// Navigate with cursor
const page2 = await plugin.tool.gitlab_list_todos.execute({
first: 20,
after: page1.todos.pageInfo.endCursor,
});
`#### Project ID Encoding
`typescript
private encodeProjectId(projectId: string): string
`Handles URL encoding for project paths (e.g.,
gitlab-org/gitlab → gitlab-org%2Fgitlab)$3
#### readTokenFromAuthStorage
`typescript
function readTokenFromAuthStorage(): string | undefined;
`Reads GitLab token from OpenCode auth storage (
~/.local/share/opencode/auth.json).Supports:
- OAuth tokens:
{ type: "oauth", access: "token" }
- API tokens: { type: "api", key: "token" }#### getGitLabClient
`typescript
function getGitLabClient(): GitLabApiClient;
`Creates and returns a configured GitLab API client.
Priority:
1.
GITLAB_TOKEN environment variable
2. OpenCode auth storage
3. Throws error if no token found$3
#### isValidGid
`typescript
function isValidGid(gid: string, expectedType?: string): boolean;
`Validates GitLab Global ID (GID) format.
Parameters:
-
gid - The GID to validate (e.g., gid://gitlab/Vulnerability/123)
- expectedType - Optional expected resource type (e.g., 'Vulnerability', 'Issue')Returns:
true if valid, false otherwiseExample:
`typescript
isValidGid('gid://gitlab/Vulnerability/123'); // true
isValidGid('gid://gitlab/Issue/456', 'Issue'); // true
isValidGid('gid://gitlab/Vulnerability/123', 'Issue'); // false (wrong type)
isValidGid('invalid-gid'); // false
`#### validateGid
`typescript
function validateGid(gid: string, expectedType?: string): void;
`Validates GID format and throws descriptive error if invalid.
Parameters:
-
gid - The GID to validate
- expectedType - Optional expected resource typeThrows:
Error with descriptive message if GID format is invalidExample:
`typescript
validateGid('gid://gitlab/Vulnerability/123'); // No error
validateGid('invalid-gid'); // Throws: "Invalid GitLab Global ID: 'invalid-gid'. Expected format: gid://gitlab/ResourceType/{id}"
validateGid('gid://gitlab/Issue/123', 'Vulnerability'); // Throws: "Invalid GitLab Global ID of type 'Vulnerability'..."
`Usage in Security Tools:
All GraphQL-based security tools automatically validate GID parameters:
-
createVulnerabilityIssue() - validates vulnerability IDs
- dismissVulnerability() - validates vulnerability ID
- confirmVulnerability() - validates vulnerability ID
- revertVulnerability() - validates vulnerability ID
- updateVulnerabilitySeverity() - validates vulnerability IDs
- linkVulnerabilityToIssue() - validates both issue ID and vulnerability IDs$3
All tools use Zod for runtime validation:
`typescript
import { tool } from '@opencode-ai/plugin';
const z = tool.schema; // Zod v4 compatible// Example tool definition
gitlab_get_merge_request: tool({
description: 'Get details of a specific merge request',
args: {
project_id: z.string().describe('The project ID or URL-encoded path'),
mr_iid: z.number().describe('The internal ID of the merge request'),
include_changes: z.boolean().optional().describe('Include file changes'),
},
execute: async (args, ctx) => {
// Implementation
},
});
`$3
All API calls include comprehensive error handling:
`typescript
if (!response.ok) {
const errorText = await response.text();
throw new Error(GitLab API error ${response.status}: ${errorText});
}
`$3
All tool responses are JSON-formatted:
`typescript
return JSON.stringify(result, null, 2);
`🔐 Security Considerations
$3
- Environment Variables: Recommended for CI/CD and production
- Auth Storage: Convenient for local development
- Never commit tokens: Use
.gitignore for sensitive files$3
The plugin requires a GitLab token with appropriate scopes:
-
api: Full API access (recommended)
- read_api: Read-only access (limited functionality)
- read_repository: Repository read access
- write_repository: Repository write access (for commits)$3
GitLab API has rate limits:
- Authenticated requests: 2,000 requests per minute
- Unauthenticated requests: 10 requests per minute
The plugin does not implement rate limiting logic. Consider implementing retry logic in your application.
$3
The plugin enforces HTTPS for all API calls. HTTP URLs are not supported.
🤝 Contributing
Contributions are welcome! Please see our Contributing Guide for detailed guidelines on:
- Code style and conventions
- Development workflow
- Testing requirements
- Submitting merge requests
- Developer Certificate of Origin and License
Quick Start for Contributors:
1. Commit Messages: Use conventional commits format
`
feat(scope): add new feature
fix(scope): fix bug
docs(scope): update documentation
`2. Code Quality: Ensure all checks pass
`bash
npm run lint
npm test
``3. Testing: Add tests for new features
- GitLab Repository
- npm Package
- Issue Tracker
- Merge Requests
- Contributing Guide
- Changelog
- CI/CD Pipelines
- GitLab API Documentation
- OpenCode Plugin SDK
- OpenCode Team: For the plugin SDK and framework
- GitLab: For the comprehensive REST API
- Contributors: All contributors to this project
For questions, issues, or feature requests:
1. Check existing issues:
2. Create new issue: Use issue templates for bugs or features
3. Discussions: Use GitLab discussions for questions
This project is licensed under the MIT License - see the LICENSE file for details.
---
Made with ❤️ for the OpenCode community
---