refactor: migrate issue provider logic to task manager interface and implement GitHub/GitLab providers
This commit is contained in:
@@ -14,12 +14,12 @@ import { writeFile, unlink } from "node:fs/promises";
|
|||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import { tmpdir } from "node:os";
|
import { tmpdir } from "node:os";
|
||||||
import {
|
import {
|
||||||
type IssueProvider,
|
type TaskManager,
|
||||||
type Issue,
|
type Issue,
|
||||||
type StateLabel,
|
type StateLabel,
|
||||||
STATE_LABELS,
|
STATE_LABELS,
|
||||||
LABEL_COLORS,
|
LABEL_COLORS,
|
||||||
} from "../issue-provider.js";
|
} from "./task-manager.js";
|
||||||
|
|
||||||
const execFileAsync = promisify(execFile);
|
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;
|
private repoPath: string;
|
||||||
|
|
||||||
constructor(opts: GitHubProviderOptions) {
|
constructor(opts: GitHubProviderOptions) {
|
||||||
@@ -10,12 +10,12 @@ import { writeFile, unlink } from "node:fs/promises";
|
|||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import { tmpdir } from "node:os";
|
import { tmpdir } from "node:os";
|
||||||
import {
|
import {
|
||||||
type IssueProvider,
|
type TaskManager,
|
||||||
type Issue,
|
type Issue,
|
||||||
type StateLabel,
|
type StateLabel,
|
||||||
STATE_LABELS,
|
STATE_LABELS,
|
||||||
LABEL_COLORS,
|
LABEL_COLORS,
|
||||||
} from "../issue-provider.js";
|
} from "./task-manager.js";
|
||||||
|
|
||||||
const execFileAsync = promisify(execFile);
|
const execFileAsync = promisify(execFile);
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ export type GitLabProviderOptions = {
|
|||||||
repoPath: string;
|
repoPath: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export class GitLabProvider implements IssueProvider {
|
export class GitLabProvider implements TaskManager {
|
||||||
private repoPath: string;
|
private repoPath: string;
|
||||||
|
|
||||||
constructor(opts: GitLabProviderOptions) {
|
constructor(opts: GitLabProviderOptions) {
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
* Can be overridden with explicit `provider` option.
|
* Can be overridden with explicit `provider` option.
|
||||||
*/
|
*/
|
||||||
import { execFileSync } from "node:child_process";
|
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 { GitLabProvider } from "./gitlab.js";
|
||||||
import { GitHubProvider } from "./github.js";
|
import { GitHubProvider } from "./github.js";
|
||||||
import { resolveRepoPath } from "../utils.js";
|
import { resolveRepoPath } from "../utils.js";
|
||||||
@@ -34,7 +34,7 @@ function detectProvider(repoPath: string): "gitlab" | "github" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type ProviderWithType = {
|
export type ProviderWithType = {
|
||||||
provider: IssueProvider;
|
provider: TaskManager;
|
||||||
type: "github" | "gitlab";
|
type: "github" | "gitlab";
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -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).
|
* GitHub (via gh CLI) and GitLab (via glab CLI) are the current implementations.
|
||||||
* Future providers: GitHub (via gh CLI), Jira (via API).
|
* Future providers: Jira (via API).
|
||||||
*
|
*
|
||||||
* All DevClaw tools operate through this interface, making it possible
|
* All DevClaw tools operate through this interface, making it possible
|
||||||
* to swap issue trackers without changing tool logic.
|
* to swap issue trackers without changing tool logic.
|
||||||
@@ -41,7 +41,7 @@ export type Issue = {
|
|||||||
web_url: string;
|
web_url: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface IssueProvider {
|
export interface TaskManager {
|
||||||
/** Create a label if it doesn't exist (idempotent). */
|
/** Create a label if it doesn't exist (idempotent). */
|
||||||
ensureLabel(name: string, color: string): Promise<void>;
|
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. */
|
/** Check if any merged MR/PR exists for a specific issue. */
|
||||||
hasMergedMR(issueId: number): Promise<boolean>;
|
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>;
|
healthCheck(): Promise<boolean>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compatibility alias for backward compatibility.
|
||||||
|
* @deprecated Use TaskManager instead.
|
||||||
|
*/
|
||||||
|
export type IssueProvider = TaskManager;
|
||||||
@@ -13,7 +13,7 @@ import fs from "node:fs/promises";
|
|||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { readProjects, writeProjects, emptyWorkerState } from "../projects.js";
|
import { readProjects, writeProjects, emptyWorkerState } from "../projects.js";
|
||||||
import { resolveRepoPath } from "../utils.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 { log as auditLog } from "../audit.js";
|
||||||
import { DEV_TIERS, QA_TIERS } from "../tiers.js";
|
import { DEV_TIERS, QA_TIERS } from "../tiers.js";
|
||||||
import { DEFAULT_DEV_INSTRUCTIONS, DEFAULT_QA_INSTRUCTIONS } from "../templates.js";
|
import { DEFAULT_DEV_INSTRUCTIONS, DEFAULT_QA_INSTRUCTIONS } from "../templates.js";
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|||||||
import { jsonResult } from "openclaw/plugin-sdk";
|
import { jsonResult } from "openclaw/plugin-sdk";
|
||||||
import type { ToolContext } from "../types.js";
|
import type { ToolContext } from "../types.js";
|
||||||
import { readProjects, getProject } from "../projects.js";
|
import { readProjects, getProject } from "../projects.js";
|
||||||
import { type StateLabel } from "../issue-provider.js";
|
import { type StateLabel } from "../task-managers/task-manager.js";
|
||||||
import { createProvider } from "../providers/index.js";
|
import { createProvider } from "../task-managers/index.js";
|
||||||
import { log as auditLog } from "../audit.js";
|
import { log as auditLog } from "../audit.js";
|
||||||
import { detectContext, generateGuardrails } from "../context-guard.js";
|
import { detectContext, generateGuardrails } from "../context-guard.js";
|
||||||
|
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|||||||
import { jsonResult } from "openclaw/plugin-sdk";
|
import { jsonResult } from "openclaw/plugin-sdk";
|
||||||
import type { ToolContext } from "../types.js";
|
import type { ToolContext } from "../types.js";
|
||||||
import { readProjects, updateWorker, getSessionForModel } from "../projects.js";
|
import { readProjects, updateWorker, getSessionForModel } from "../projects.js";
|
||||||
import { type StateLabel } from "../issue-provider.js";
|
import { type StateLabel } from "../task-managers/task-manager.js";
|
||||||
import { createProvider } from "../providers/index.js";
|
import { createProvider } from "../task-managers/index.js";
|
||||||
import { log as auditLog } from "../audit.js";
|
import { log as auditLog } from "../audit.js";
|
||||||
|
|
||||||
export function createSessionHealthTool(api: OpenClawPluginApi) {
|
export function createSessionHealthTool(api: OpenClawPluginApi) {
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|||||||
import { jsonResult } from "openclaw/plugin-sdk";
|
import { jsonResult } from "openclaw/plugin-sdk";
|
||||||
import { log as auditLog } from "../audit.js";
|
import { log as auditLog } from "../audit.js";
|
||||||
import { dispatchTask } from "../dispatch.js";
|
import { dispatchTask } from "../dispatch.js";
|
||||||
import { type StateLabel } from "../issue-provider.js";
|
import { type StateLabel } from "../task-managers/task-manager.js";
|
||||||
import { createProvider } from "../providers/index.js";
|
import { createProvider } from "../task-managers/index.js";
|
||||||
import { resolveRepoPath } from "../utils.js";
|
import { resolveRepoPath } from "../utils.js";
|
||||||
import {
|
import {
|
||||||
deactivateWorker,
|
deactivateWorker,
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|||||||
import { jsonResult } from "openclaw/plugin-sdk";
|
import { jsonResult } from "openclaw/plugin-sdk";
|
||||||
import type { ToolContext } from "../types.js";
|
import type { ToolContext } from "../types.js";
|
||||||
import { readProjects } from "../projects.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 { 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[] = [
|
const STATE_LABELS: StateLabel[] = [
|
||||||
"Planning",
|
"Planning",
|
||||||
|
|||||||
@@ -11,8 +11,8 @@
|
|||||||
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
||||||
import { jsonResult } from "openclaw/plugin-sdk";
|
import { jsonResult } from "openclaw/plugin-sdk";
|
||||||
import { dispatchTask } from "../dispatch.js";
|
import { dispatchTask } from "../dispatch.js";
|
||||||
import { type StateLabel } from "../issue-provider.js";
|
import { type StateLabel } from "../task-managers/task-manager.js";
|
||||||
import { createProvider } from "../providers/index.js";
|
import { createProvider } from "../task-managers/index.js";
|
||||||
import { selectModel } from "../model-selector.js";
|
import { selectModel } from "../model-selector.js";
|
||||||
import { getProject, getWorker, readProjects } from "../projects.js";
|
import { getProject, getWorker, readProjects } from "../projects.js";
|
||||||
import type { ToolContext } from "../types.js";
|
import type { ToolContext } from "../types.js";
|
||||||
|
|||||||
Reference in New Issue
Block a user