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:
@@ -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
|
||||
|
||||
|
||||
@@ -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 |
|
||||
|
||||
Reference in New Issue
Block a user