refactor: migrate issue provider logic to task manager interface and implement GitHub/GitLab providers

This commit is contained in:
Lauren ten Hoor
2026-02-09 22:38:43 +08:00
parent 3a2e739a62
commit 4e10b171c1
10 changed files with 30 additions and 24 deletions

View File

@@ -14,12 +14,12 @@ import { writeFile, unlink } from "node:fs/promises";
import { join } from "node:path";
import { tmpdir } from "node:os";
import {
type IssueProvider,
type TaskManager,
type Issue,
type StateLabel,
STATE_LABELS,
LABEL_COLORS,
} from "../issue-provider.js";
} from "./task-manager.js";
const execFileAsync = promisify(execFile);
@@ -48,7 +48,7 @@ function toIssue(gh: GhIssue): Issue {
};
}
export class GitHubProvider implements IssueProvider {
export class GitHubProvider implements TaskManager {
private repoPath: string;
constructor(opts: GitHubProviderOptions) {

View File

@@ -10,12 +10,12 @@ import { writeFile, unlink } from "node:fs/promises";
import { join } from "node:path";
import { tmpdir } from "node:os";
import {
type IssueProvider,
type TaskManager,
type Issue,
type StateLabel,
STATE_LABELS,
LABEL_COLORS,
} from "../issue-provider.js";
} from "./task-manager.js";
const execFileAsync = promisify(execFile);
@@ -23,7 +23,7 @@ export type GitLabProviderOptions = {
repoPath: string;
};
export class GitLabProvider implements IssueProvider {
export class GitLabProvider implements TaskManager {
private repoPath: string;
constructor(opts: GitLabProviderOptions) {

View File

@@ -8,7 +8,7 @@
* Can be overridden with explicit `provider` option.
*/
import { execFileSync } from "node:child_process";
import type { IssueProvider } from "../issue-provider.js";
import type { TaskManager } from "./task-manager.js";
import { GitLabProvider } from "./gitlab.js";
import { GitHubProvider } from "./github.js";
import { resolveRepoPath } from "../utils.js";
@@ -34,7 +34,7 @@ function detectProvider(repoPath: string): "gitlab" | "github" {
}
export type ProviderWithType = {
provider: IssueProvider;
provider: TaskManager;
type: "github" | "gitlab";
};

View File

@@ -1,8 +1,8 @@
/**
* IssueProvider Abstract interface for issue tracker operations.
* TaskManager Abstract interface for issue tracker operations.
*
* GitLab is the first implementation (via glab CLI).
* Future providers: GitHub (via gh CLI), Jira (via API).
* GitHub (via gh CLI) and GitLab (via glab CLI) are the current implementations.
* Future providers: Jira (via API).
*
* All DevClaw tools operate through this interface, making it possible
* to swap issue trackers without changing tool logic.
@@ -41,7 +41,7 @@ export type Issue = {
web_url: string;
};
export interface IssueProvider {
export interface TaskManager {
/** Create a label if it doesn't exist (idempotent). */
ensureLabel(name: string, color: string): Promise<void>;
@@ -75,6 +75,12 @@ export interface IssueProvider {
/** Check if any merged MR/PR exists for a specific issue. */
hasMergedMR(issueId: number): Promise<boolean>;
/** Verify the provider is working (CLI available, auth valid, repo accessible). */
/** Verify the task manager is working (CLI available, auth valid, repo accessible). */
healthCheck(): Promise<boolean>;
}
/**
* Compatibility alias for backward compatibility.
* @deprecated Use TaskManager instead.
*/
export type IssueProvider = TaskManager;

View File

@@ -13,7 +13,7 @@ import fs from "node:fs/promises";
import path from "node:path";
import { readProjects, writeProjects, emptyWorkerState } from "../projects.js";
import { resolveRepoPath } from "../utils.js";
import { createProvider } from "../providers/index.js";
import { createProvider } from "../task-managers/index.js";
import { log as auditLog } from "../audit.js";
import { DEV_TIERS, QA_TIERS } from "../tiers.js";
import { DEFAULT_DEV_INSTRUCTIONS, DEFAULT_QA_INSTRUCTIONS } from "../templates.js";

View File

@@ -7,8 +7,8 @@ import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
import { jsonResult } from "openclaw/plugin-sdk";
import type { ToolContext } from "../types.js";
import { readProjects, getProject } from "../projects.js";
import { type StateLabel } from "../issue-provider.js";
import { createProvider } from "../providers/index.js";
import { type StateLabel } from "../task-managers/task-manager.js";
import { createProvider } from "../task-managers/index.js";
import { log as auditLog } from "../audit.js";
import { detectContext, generateGuardrails } from "../context-guard.js";

View File

@@ -8,8 +8,8 @@ import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
import { jsonResult } from "openclaw/plugin-sdk";
import type { ToolContext } from "../types.js";
import { readProjects, updateWorker, getSessionForModel } from "../projects.js";
import { type StateLabel } from "../issue-provider.js";
import { createProvider } from "../providers/index.js";
import { type StateLabel } from "../task-managers/task-manager.js";
import { createProvider } from "../task-managers/index.js";
import { log as auditLog } from "../audit.js";
export function createSessionHealthTool(api: OpenClawPluginApi) {

View File

@@ -14,8 +14,8 @@ import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
import { jsonResult } from "openclaw/plugin-sdk";
import { log as auditLog } from "../audit.js";
import { dispatchTask } from "../dispatch.js";
import { type StateLabel } from "../issue-provider.js";
import { createProvider } from "../providers/index.js";
import { type StateLabel } from "../task-managers/task-manager.js";
import { createProvider } from "../task-managers/index.js";
import { resolveRepoPath } from "../utils.js";
import {
deactivateWorker,

View File

@@ -13,9 +13,9 @@ import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
import { jsonResult } from "openclaw/plugin-sdk";
import type { ToolContext } from "../types.js";
import { readProjects } from "../projects.js";
import { createProvider } from "../providers/index.js";
import { createProvider } from "../task-managers/index.js";
import { log as auditLog } from "../audit.js";
import type { StateLabel } from "../issue-provider.js";
import type { StateLabel } from "../task-managers/task-manager.js";
const STATE_LABELS: StateLabel[] = [
"Planning",

View File

@@ -11,8 +11,8 @@
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
import { jsonResult } from "openclaw/plugin-sdk";
import { dispatchTask } from "../dispatch.js";
import { type StateLabel } from "../issue-provider.js";
import { createProvider } from "../providers/index.js";
import { type StateLabel } from "../task-managers/task-manager.js";
import { createProvider } from "../task-managers/index.js";
import { selectModel } from "../model-selector.js";
import { getProject, getWorker, readProjects } from "../projects.js";
import type { ToolContext } from "../types.js";