feat: Implement GitLabProvider for issue management using glab CLI

- Add GitLabProvider class for handling issue operations, label management, and MR checks.
- Implement methods for ensuring labels, creating issues, listing issues by label, and transitioning labels.
- Introduce a provider factory to auto-detect GitLab or GitHub based on the repository URL.
- Create project registration tool to validate repositories, create state labels, and log project entries.
- Enhance queue status and session health tools to support new session management features.
- Update task completion and task creation tools to support auto-chaining and improved session handling.
- Refactor task pickup tool to streamline model selection and session management.
This commit is contained in:
Lauren ten Hoor
2026-02-09 12:54:50 +08:00
parent d921b5c7bb
commit 8a79755e4c
16 changed files with 1578 additions and 242 deletions

View File

@@ -84,8 +84,10 @@ graph TB
subgraph "DevClaw Plugin"
TP[task_pickup]
TC[task_complete]
TCR[task_create]
QS[queue_status]
SH[session_health]
PR[project_register]
MS_SEL[Model Selector]
PJ[projects.json]
AL[audit.log]
@@ -102,8 +104,10 @@ graph TB
MS -->|calls| TP
MS -->|calls| TC
MS -->|calls| TCR
MS -->|calls| QS
MS -->|calls| SH
MS -->|calls| PR
TP -->|selects model| MS_SEL
TP -->|transitions labels| GL
@@ -116,8 +120,12 @@ graph TB
TC -->|closes/reopens| GL
TC -->|reads/writes| PJ
TC -->|git pull| REPO
TC -->|auto-chain dispatch| CLI
TC -->|appends| AL
TCR -->|creates issue| GL
TCR -->|appends| AL
QS -->|lists issues by label| GL
QS -->|reads| PJ
QS -->|appends| AL
@@ -127,6 +135,10 @@ graph TB
SH -->|reverts labels| GL
SH -->|appends| AL
PR -->|creates labels| GL
PR -->|writes entry| PJ
PR -->|appends| AL
CLI -->|sends task| DEV_H
CLI -->|sends task| DEV_S
CLI -->|sends task| DEV_O
@@ -271,8 +283,7 @@ sequenceDiagram
TP->>GL: glab issue view 42 --output json
GL-->>TP: { title: "Add login page", labels: ["To Do"] }
TP->>TP: Verify label is "To Do" ✓
TP->>MS: selectModel("Add login page", description, "dev")
MS-->>TP: { alias: "sonnet" }
TP->>TP: model from agent param (LLM-selected) or fallback heuristic
TP->>PJ: lookup dev.sessions.sonnet
TP->>GL: glab issue update 42 --unlabel "To Do" --label "Doing"
alt New session
@@ -294,38 +305,47 @@ sequenceDiagram
```
DEV sub-agent session → reads codebase, writes code, creates MR
DEV sub-agent session → reports back to orchestrator: "done, MR merged"
DEV sub-agent session → calls task_complete({ role: "dev", result: "done", ... })
```
This happens inside the OpenClaw session. DevClaw is not involved — the DEV sub-agent session works autonomously with the codebase.
This happens inside the OpenClaw session. The worker calls `task_complete` directly for atomic state updates. If the worker discovers unrelated bugs, it calls `task_create` to file them.
### Phase 5: DEV complete
### Phase 5: DEV complete (worker self-reports)
```mermaid
sequenceDiagram
participant A as Orchestrator
participant DEV as DEV Session
participant TC as task_complete
participant GL as GitLab
participant PJ as projects.json
participant AL as audit.log
participant REPO as Git Repo
participant QA as QA Session (auto-chain)
A->>TC: task_complete({ role: "dev", result: "done", projectGroupId: "-123", summary: "Login page with OAuth" })
DEV->>TC: task_complete({ role: "dev", result: "done", projectGroupId: "-123", summary: "Login page with OAuth" })
TC->>PJ: readProjects()
PJ-->>TC: { dev: { active: true, issueId: "42" } }
TC->>REPO: git pull
TC->>PJ: deactivateWorker(-123, dev)
Note over PJ: active→false, issueId→null<br/>sessions map PRESERVED
TC->>GL: glab issue update 42 --unlabel "Doing" --label "To Test"
TC->>GL: transition label "Doing" "To Test"
TC->>AL: append { event: "task_complete", role: "dev", result: "done" }
TC-->>A: { announcement: "✅ DEV done #42 — Login page with OAuth. Moved to QA queue." }
alt autoChain enabled
TC->>GL: transition label "To Test" → "Testing"
TC->>QA: dispatchTask(role: "qa", model: "grok")
TC->>PJ: activateWorker(-123, qa)
TC-->>DEV: { announcement: "✅ DEV done #42", autoChain: { dispatched: true, role: "qa" } }
else autoChain disabled
TC-->>DEV: { announcement: "✅ DEV done #42", nextAction: "qa_pickup" }
end
```
**Writes:**
- `Git repo`: pulled latest (has DEV's merged code)
- `projects.json`: dev.active=false, dev.issueId=null (sessions map preserved for reuse)
- `GitLab`: label "Doing" → "To Test"
- `audit.log`: 1 entry (task_complete)
- `GitLab`: label "Doing" → "To Test" (+ "To Test" → "Testing" if auto-chain)
- `audit.log`: 1 entry (task_complete) + optional auto-chain entries
### Phase 6: QA pickup
@@ -415,22 +435,24 @@ Every piece of data and where it lives:
```
┌─────────────────────────────────────────────────────────────────┐
GitLab (source of truth for tasks)
Issue Tracker (source of truth for tasks) │
│ │
│ Issue #42: "Add login page" │
│ Labels: [To Do | Doing | To Test | Testing | Done | ...] │
│ State: open / closed │
│ MRs: linked merge requests
│ Created by: orchestrator agent, DEV/QA sub-agents, or humans │
│ MRs/PRs: linked merge/pull requests │
│ Created by: orchestrator (task_create), workers, or humans
└─────────────────────────────────────────────────────────────────┘
↕ glab CLI (read/write)
↕ glab/gh CLI (read/write, auto-detected)
┌─────────────────────────────────────────────────────────────────┐
│ DevClaw Plugin (orchestration logic) │
│ │
│ task_pickup → model + label + dispatch + state (end-to-end)
│ task_complete → label transition + state update + git pull
│ task_pickup → model + label + dispatch + role instr (e2e)
│ task_complete → label + state + git pull + auto-chain
│ task_create → create issue in tracker │
│ queue_status → read labels + read state │
│ session_health → check sessions + fix zombies │
│ project_register → labels + roles + state init (one-time) │
└─────────────────────────────────────────────────────────────────┘
↕ atomic file I/O ↕ OpenClaw CLI (plugin shells out)
┌────────────────────────────────┐ ┌──────────────────────────────┐
@@ -454,7 +476,8 @@ Every piece of data and where it lives:
│ │
│ NDJSON, one line per event: │
│ task_pickup, task_complete, model_selection, │
│ queue_status, health_check, session_spawn, session_reuse
│ queue_status, health_check, session_spawn, session_reuse,
│ project_register │
│ │
│ Query with: cat audit.log | jq 'select(.event=="task_pickup")' │
└─────────────────────────────────────────────────────────────────┘
@@ -488,8 +511,10 @@ graph LR
subgraph "DevClaw controls (deterministic)"
L[Label transitions]
S[Worker state]
M[Model selection]
PR[Project registration]
SD[Session dispatch<br/>create + send via CLI]
AC[Auto-chaining<br/>DEV→QA, QA fail→DEV]
RI[Role instructions<br/>loaded per project]
A[Audit logging]
Z[Zombie cleanup]
end
@@ -497,14 +522,15 @@ graph LR
subgraph "Orchestrator handles"
MSG[Telegram announcements]
HB[Heartbeat scheduling]
IC[Issue creation via glab]
DEC[Task prioritization]
M[Model selection]
end
subgraph "Sub-agent sessions handle"
CR[Code writing]
MR[MR creation/review]
BUG[Bug issue creation]
TC_W[Task completion<br/>via task_complete]
BUG[Bug filing<br/>via task_create]
end
subgraph "External"
@@ -513,6 +539,28 @@ graph LR
end
```
## IssueProvider abstraction
All issue tracker operations go through the `IssueProvider` interface, defined in `lib/issue-provider.ts`. This abstraction allows DevClaw to support multiple issue trackers without changing tool logic.
**Interface methods:**
- `ensureLabel` / `ensureAllStateLabels` — idempotent label creation
- `listIssuesByLabel` / `getIssue` — issue queries
- `transitionLabel` — atomic label state transition (unlabel + label)
- `closeIssue` / `reopenIssue` — issue lifecycle
- `hasStateLabel` / `getCurrentStateLabel` — label inspection
- `hasMergedMR` — MR/PR verification
- `healthCheck` — verify provider connectivity
**Current providers:**
- **GitLab** (`lib/providers/gitlab.ts`) — wraps `glab` CLI
- **GitHub** (`lib/providers/github.ts`) — wraps `gh` CLI
**Planned providers:**
- **Jira** — via REST API
Provider selection is handled by `createProvider()` in `lib/providers/index.ts`. Auto-detects GitHub vs GitLab from the git remote URL.
## Error recovery
| Failure | Detection | Recovery |
@@ -525,6 +573,7 @@ graph LR
| Label out of sync | `task_pickup` verifies label before transitioning | Throws error if label doesn't match expected state. Agent reports mismatch. |
| Worker already active | `task_pickup` checks `active` flag | Throws error: "DEV worker already active on project". Must complete current task first. |
| Stale worker (>2h) | `session_health` flags as warning | Agent can investigate or `autoFix` can clear. |
| `project_register` fails | Plugin catches error during label creation or state write | Clean error returned. No partial state — labels are idempotent, projects.json not written until all labels succeed. |
## File locations

View File

@@ -6,9 +6,9 @@
|---|---|---|
| [OpenClaw](https://openclaw.ai) installed | DevClaw is an OpenClaw plugin | `openclaw --version` |
| Node.js >= 20 | Runtime for plugin | `node --version` |
| [`glab`](https://gitlab.com/gitlab-org/cli) CLI | GitLab issue/label management | `glab --version` |
| glab authenticated | Plugin calls glab for every label transition | `glab auth status` |
| A GitLab repo with issues | The task backlog lives in GitLab | `glab issue list` from your repo |
| [`glab`](https://gitlab.com/gitlab-org/cli) or [`gh`](https://cli.github.com) CLI | Issue tracker provider (auto-detected from remote) | `glab --version` or `gh --version` |
| CLI authenticated | Plugin calls glab/gh for every label transition | `glab auth status` or `gh auth status` |
| A GitLab/GitHub repo with issues | The task backlog lives in the issue tracker | `glab issue list` or `gh issue list` from your repo |
| An OpenClaw agent with Telegram | The orchestrator agent that will manage projects | Agent defined in `openclaw.json` |
## Setup steps
@@ -41,8 +41,10 @@ In `openclaw.json`, your orchestrator agent needs access to the DevClaw tools:
"allow": [
"task_pickup",
"task_complete",
"task_create",
"queue_status",
"session_health"
"session_health",
"project_register"
]
}
}]
@@ -50,79 +52,68 @@ In `openclaw.json`, your orchestrator agent needs access to the DevClaw tools:
}
```
The agent only needs the four DevClaw tools. Session management (`sessions_spawn`, `sessions_send`) is **not needed** — the plugin handles session creation and task dispatch internally via OpenClaw CLI. This eliminates the fragile handoff where agents had to correctly call session tools with the right parameters.
The agent needs the six DevClaw tools. Session management (`sessions_spawn`, `sessions_send`) is **not needed** — the plugin handles session creation and task dispatch internally via OpenClaw CLI. Workers (DEV/QA sub-agent sessions) also use `task_complete` and `task_create` directly for atomic self-reporting.
### 3. Create GitLab labels
### 3. Register your project
DevClaw uses these labels as a state machine. Create them once per GitLab project:
Tell the orchestrator agent to register a new project:
```bash
cd ~/git/your-project
glab label create "Planning" --color "#6699cc"
glab label create "To Do" --color "#428bca"
glab label create "Doing" --color "#f0ad4e"
glab label create "To Test" --color "#5bc0de"
glab label create "Testing" --color "#9b59b6"
glab label create "Done" --color "#5cb85c"
glab label create "To Improve" --color "#d9534f"
glab label create "Refining" --color "#f39c12"
```
> "Register project my-project at ~/git/my-project for group -1234567890 with base branch development"
### 4. Register a project
Add your project to `memory/projects.json` in the orchestrator's workspace:
The agent calls `project_register`, which atomically:
- Validates the repo and auto-detects GitHub/GitLab from remote
- Creates all 8 state labels (idempotent)
- Scaffolds role instruction files (`roles/<project>/dev.md` and `qa.md`)
- Adds the project entry to `projects.json` with `autoChain: false`
- Logs the registration event
```json
{
"projects": {
"<telegram-group-id>": {
"-1234567890": {
"name": "my-project",
"repo": "~/git/my-project",
"groupName": "Dev - My Project",
"deployUrl": "https://my-project.example.com",
"deployUrl": "",
"baseBranch": "development",
"deployBranch": "development",
"autoChain": false,
"dev": {
"active": false,
"issueId": null,
"startTime": null,
"model": null,
"sessions": {
"haiku": null,
"sonnet": null,
"opus": null
}
"sessions": { "haiku": null, "sonnet": null, "opus": null }
},
"qa": {
"active": false,
"issueId": null,
"startTime": null,
"model": null,
"sessions": {
"grok": null
}
"sessions": { "grok": null }
}
}
}
}
```
**Manual fallback:** If you prefer CLI control, you can still create labels manually with `glab label create` and edit `projects.json` directly. See the [Architecture docs](ARCHITECTURE.md) for label names and colors.
**Finding the Telegram group ID:** The group ID is the numeric ID of your Telegram supergroup (a negative number like `-1234567890`). You can find it via the Telegram bot API or from message metadata in OpenClaw logs.
### 5. Add the agent to the Telegram group
### 4. Add the agent to the Telegram group
Add your orchestrator bot to the Telegram group for the project. The agent will now receive messages from this group and can operate on the linked project.
### 6. Create your first issue
### 5. Create your first issue
Issues can be created in multiple ways:
- **Via the agent** — Ask the orchestrator in the Telegram group: "Create an issue for adding a login page"
- **Via glab CLI** — `cd ~/git/my-project && glab issue create --title "My first task" --label "To Do"`
- **Via GitLab UI** — Create an issue and add the "To Do" label
- **Via the agent** — Ask the orchestrator in the Telegram group: "Create an issue for adding a login page" (uses `task_create`)
- **Via workers** — DEV/QA workers can call `task_create` to file follow-up bugs they discover
- **Via CLI** — `cd ~/git/my-project && glab issue create --title "My first task" --label "To Do"` (or `gh issue create`)
- **Via web UI** — Create an issue and add the "To Do" label
The orchestrator agent and worker sessions can all create and update issues via `glab` tool usage.
### 7. Test the pipeline
### 6. Test the pipeline
Ask the agent in the Telegram group:
@@ -136,10 +127,7 @@ The agent calls `task_pickup`, which selects a model, transitions the label to "
## Adding more projects
Repeat steps 3-5 for each new project:
1. Create labels in the GitLab repo
2. Add an entry to `projects.json` with the new Telegram group ID
3. Add the bot to the new Telegram group
Tell the agent to register a new project (step 3) and add the bot to the new Telegram group (step 4). That's it — `project_register` handles labels and state setup.
Each project is fully isolated — separate queue, separate workers, separate state.
@@ -147,15 +135,18 @@ Each project is fully isolated — separate queue, separate workers, separate st
| Responsibility | Who | Details |
|---|---|---|
| GitLab label setup | You (once per project) | 8 labels, created via `glab label create` |
| Project registration | You (once per project) | Entry in `projects.json` |
| Label setup | Plugin (`project_register`) | 8 labels, created idempotently via `IssueProvider` |
| Role file scaffolding | Plugin (`project_register`) | Creates `roles/<project>/dev.md` and `qa.md` from defaults |
| Project registration | Plugin (`project_register`) | Entry in `projects.json` with empty worker state |
| Agent definition | You (once) | Agent in `openclaw.json` with tool permissions |
| Telegram group setup | You (once per project) | Add bot to group |
| Issue creation | Agent or worker sessions | Created via `glab` tool usage (or manually via GitLab UI) |
| Label transitions | Plugin | Atomic `--unlabel` + `--label` via glab |
| Model selection | Plugin | Keyword-based heuristic per task |
| Issue creation | Plugin (`task_create`) | Orchestrator or workers create issues from chat |
| Label transitions | Plugin | Atomic label transitions via issue tracker CLI |
| Model selection | Plugin | LLM-selected by orchestrator, keyword heuristic fallback |
| State management | Plugin | Atomic read/write to `projects.json` |
| Session management | Plugin | Creates, reuses, and dispatches to sessions via CLI. Agent never touches session tools. |
| Task completion | Plugin (`task_complete`) | Workers self-report. Auto-chains if enabled. |
| Role instructions | Plugin (`task_pickup`) | Loaded from `roles/<project>/<role>.md`, appended to task message |
| Audit logging | Plugin | Automatic NDJSON append per tool call |
| Zombie detection | Plugin | `session_health` checks active vs alive |
| Queue scanning | Plugin | `queue_status` queries GitLab per project |
| Queue scanning | Plugin | `queue_status` queries issue tracker per project |