diff --git a/index.ts b/index.ts index 3c9fe3a..b137dde 100644 --- a/index.ts +++ b/index.ts @@ -29,14 +29,6 @@ const plugin = { qa: { type: "string", description: "QA engineer model" }, }, }, - glabPath: { - type: "string", - description: "Path to glab CLI binary. Defaults to 'glab' on PATH.", - }, - ghPath: { - type: "string", - description: "Path to gh CLI binary. Defaults to 'gh' on PATH.", - }, }, }, diff --git a/lib/gitlab.ts b/lib/gitlab.ts index efef586..002143e 100644 --- a/lib/gitlab.ts +++ b/lib/gitlab.ts @@ -25,7 +25,6 @@ const STATE_LABELS = [ export type StateLabel = (typeof STATE_LABELS)[number]; type GlabOptions = { - glabPath?: string; repoPath: string; }; @@ -33,8 +32,7 @@ async function glab( args: string[], opts: GlabOptions, ): Promise { - const bin = opts.glabPath ?? "glab"; - const { stdout } = await execFileAsync(bin, args, { + const { stdout } = await execFileAsync("glab", args, { cwd: opts.repoPath, timeout: 30_000, }); diff --git a/lib/providers/github.ts b/lib/providers/github.ts index 47a5c78..3f19b38 100644 --- a/lib/providers/github.ts +++ b/lib/providers/github.ts @@ -24,7 +24,6 @@ import { const execFileAsync = promisify(execFile); export type GitHubProviderOptions = { - ghPath?: string; repoPath: string; }; @@ -50,16 +49,14 @@ function toIssue(gh: GhIssue): Issue { } export class GitHubProvider implements IssueProvider { - private ghPath: string; private repoPath: string; constructor(opts: GitHubProviderOptions) { - this.ghPath = opts.ghPath ?? "gh"; this.repoPath = opts.repoPath; } private async gh(args: string[]): Promise { - const { stdout } = await execFileAsync(this.ghPath, args, { + const { stdout } = await execFileAsync("gh", args, { cwd: this.repoPath, timeout: 30_000, }); diff --git a/lib/providers/gitlab.ts b/lib/providers/gitlab.ts index 418e6fc..b641b27 100644 --- a/lib/providers/gitlab.ts +++ b/lib/providers/gitlab.ts @@ -20,21 +20,18 @@ import { const execFileAsync = promisify(execFile); export type GitLabProviderOptions = { - glabPath?: string; repoPath: string; }; export class GitLabProvider implements IssueProvider { - private glabPath: string; private repoPath: string; constructor(opts: GitLabProviderOptions) { - this.glabPath = opts.glabPath ?? "glab"; this.repoPath = opts.repoPath; } private async glab(args: string[]): Promise { - const { stdout } = await execFileAsync(this.glabPath, args, { + const { stdout } = await execFileAsync("glab", args, { cwd: this.repoPath, timeout: 30_000, }); @@ -76,7 +73,7 @@ export class GitLabProvider implements IssueProvider { const { promisify } = await import("node:util"); const execAsync = promisify(exec); - let cmd = `${this.glabPath} issue create --title "${title.replace(/"/g, '\\"')}" --description "$(cat ${tempFile})" --label "${label}" --output json`; + let cmd = `glab issue create --title "${title.replace(/"/g, '\\"')}" --description "$(cat ${tempFile})" --label "${label}" --output json`; if (assignees && assignees.length > 0) { cmd += ` --assignee "${assignees.join(",")}"`; } diff --git a/lib/providers/index.ts b/lib/providers/index.ts index 2975449..a51767b 100644 --- a/lib/providers/index.ts +++ b/lib/providers/index.ts @@ -11,12 +11,12 @@ import { execFileSync } from "node:child_process"; import type { IssueProvider } from "../issue-provider.js"; import { GitLabProvider } from "./gitlab.js"; import { GitHubProvider } from "./github.js"; +import { resolveRepoPath } from "../utils.js"; export type ProviderOptions = { provider?: "gitlab" | "github"; - glabPath?: string; - ghPath?: string; - repoPath: string; + repo?: string; + repoPath?: string; }; function detectProvider(repoPath: string): "gitlab" | "github" { @@ -39,16 +39,21 @@ export type ProviderWithType = { }; export function createProvider(opts: ProviderOptions): ProviderWithType { - const type = opts.provider ?? detectProvider(opts.repoPath); + const repoPath = opts.repoPath ?? (opts.repo ? resolveRepoPath(opts.repo) : null); + if (!repoPath) { + throw new Error("Either repoPath or repo must be provided to createProvider"); + } + + const type = opts.provider ?? detectProvider(repoPath); if (type === "github") { return { - provider: new GitHubProvider({ ghPath: opts.ghPath, repoPath: opts.repoPath }), + provider: new GitHubProvider({ repoPath }), type: "github", }; } return { - provider: new GitLabProvider({ glabPath: opts.glabPath, repoPath: opts.repoPath }), + provider: new GitLabProvider({ repoPath }), type: "gitlab", }; } diff --git a/lib/tools/project-register.ts b/lib/tools/project-register.ts index 65eef58..98efbf4 100644 --- a/lib/tools/project-register.ts +++ b/lib/tools/project-register.ts @@ -170,9 +170,7 @@ export function createProjectRegisterTool(api: OpenClawPluginApi) { const repoPath = resolveRepoPath(repo); // 3. Create provider and verify it works - const glabPath = (api.pluginConfig as Record)?.glabPath as string | undefined; - const ghPath = (api.pluginConfig as Record)?.ghPath as string | undefined; - const { provider, type: providerType } = createProvider({ glabPath, ghPath, repoPath }); + const { provider, type: providerType } = createProvider({ repo }); const healthy = await provider.healthCheck(); if (!healthy) { diff --git a/lib/tools/queue-status.ts b/lib/tools/queue-status.ts index 0743c75..107c133 100644 --- a/lib/tools/queue-status.ts +++ b/lib/tools/queue-status.ts @@ -11,7 +11,6 @@ import { type StateLabel } from "../issue-provider.js"; import { createProvider } from "../providers/index.js"; import { log as auditLog } from "../audit.js"; import { detectContext, generateGuardrails } from "../context-guard.js"; -import { resolveRepoPath } from "../utils.js"; export function createQueueStatusTool(api: OpenClawPluginApi) { return (ctx: ToolContext) => ({ @@ -69,11 +68,8 @@ export function createQueueStatusTool(api: OpenClawPluginApi) { const project = getProject(data, pid); if (!project) continue; - const repoPath = resolveRepoPath(project.repo); const { provider } = createProvider({ - glabPath: (api.pluginConfig as Record)?.glabPath as string | undefined, - ghPath: (api.pluginConfig as Record)?.ghPath as string | undefined, - repoPath, + repo: project.repo, }); // Fetch queue counts from issue tracker diff --git a/lib/tools/session-health.ts b/lib/tools/session-health.ts index b23b336..c767874 100644 --- a/lib/tools/session-health.ts +++ b/lib/tools/session-health.ts @@ -11,7 +11,6 @@ import { readProjects, updateWorker, getSessionForModel } from "../projects.js"; import { type StateLabel } from "../issue-provider.js"; import { createProvider } from "../providers/index.js"; import { log as auditLog } from "../audit.js"; -import { resolveRepoPath } from "../utils.js"; export function createSessionHealthTool(api: OpenClawPluginApi) { return (ctx: ToolContext) => ({ @@ -48,11 +47,8 @@ export function createSessionHealthTool(api: OpenClawPluginApi) { let fixesApplied = 0; for (const [groupId, project] of Object.entries(data.projects)) { - const repoPath = resolveRepoPath(project.repo); const { provider } = createProvider({ - glabPath: (api.pluginConfig as Record)?.glabPath as string | undefined, - ghPath: (api.pluginConfig as Record)?.ghPath as string | undefined, - repoPath, + repo: project.repo, }); for (const role of ["dev", "qa"] as const) { diff --git a/lib/tools/task-complete.ts b/lib/tools/task-complete.ts index 4f1b643..f11bdbc 100644 --- a/lib/tools/task-complete.ts +++ b/lib/tools/task-complete.ts @@ -16,6 +16,7 @@ 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 { resolveRepoPath } from "../utils.js"; import { deactivateWorker, getProject, @@ -24,7 +25,6 @@ import { readProjects, } from "../projects.js"; import type { ToolContext } from "../types.js"; -import { resolveRepoPath } from "../utils.js"; const execFileAsync = promisify(execFile); @@ -105,17 +105,12 @@ export function createTaskCompleteTool(api: OpenClawPluginApi) { ); } - const repoPath = resolveRepoPath(project.repo); const { provider } = createProvider({ - glabPath: (api.pluginConfig as Record)?.glabPath as - | string - | undefined, - ghPath: (api.pluginConfig as Record)?.ghPath as - | string - | undefined, - repoPath, + repo: project.repo, }); + const repoPath = resolveRepoPath(project.repo); + const output: Record = { success: true, project: project.name, diff --git a/lib/tools/task-create.ts b/lib/tools/task-create.ts index 658aadf..fa86d6c 100644 --- a/lib/tools/task-create.ts +++ b/lib/tools/task-create.ts @@ -13,7 +13,6 @@ 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 { resolveRepoPath } from "../utils.js"; import { createProvider } from "../providers/index.js"; import { log as auditLog } from "../audit.js"; import type { StateLabel } from "../issue-provider.js"; @@ -95,12 +94,8 @@ The issue is created with a state label (defaults to "Planning"). Returns the cr } // 2. Create provider - const repoPath = resolveRepoPath(project.repo); - const config = api.pluginConfig as Record | undefined; const { provider, type: providerType } = createProvider({ - glabPath: config?.glabPath as string | undefined, - ghPath: config?.ghPath as string | undefined, - repoPath, + repo: project.repo, }); // 3. Create the issue diff --git a/lib/tools/task-pickup.ts b/lib/tools/task-pickup.ts index 30f89f1..8215dd4 100644 --- a/lib/tools/task-pickup.ts +++ b/lib/tools/task-pickup.ts @@ -17,7 +17,6 @@ import { selectModel } from "../model-selector.js"; import { getProject, getWorker, readProjects } from "../projects.js"; import type { ToolContext } from "../types.js"; import { detectContext, generateGuardrails } from "../context-guard.js"; -import { resolveRepoPath } from "../utils.js"; export function createTaskPickupTool(api: OpenClawPluginApi) { return (ctx: ToolContext) => ({ @@ -96,15 +95,8 @@ export function createTaskPickupTool(api: OpenClawPluginApi) { } // 3. Fetch issue and verify state - const repoPath = resolveRepoPath(project.repo); const { provider } = createProvider({ - glabPath: (api.pluginConfig as Record)?.glabPath as - | string - | undefined, - ghPath: (api.pluginConfig as Record)?.ghPath as - | string - | undefined, - repoPath, + repo: project.repo, }); const issue = await provider.getIssue(issueId); diff --git a/openclaw.plugin.json b/openclaw.plugin.json index 9a0f007..78b5a0e 100644 --- a/openclaw.plugin.json +++ b/openclaw.plugin.json @@ -9,19 +9,23 @@ "type": "object", "description": "Model mapping per developer tier (junior, medior, senior, qa)", "properties": { - "junior": { "type": "string", "description": "Junior dev model (default: anthropic/claude-haiku-4-5)" }, - "medior": { "type": "string", "description": "Medior dev model (default: anthropic/claude-sonnet-4-5)" }, - "senior": { "type": "string", "description": "Senior dev model (default: anthropic/claude-opus-4-5)" }, - "qa": { "type": "string", "description": "QA engineer model (default: anthropic/claude-sonnet-4-5)" } + "junior": { + "type": "string", + "description": "Junior dev model (default: anthropic/claude-haiku-4-5)" + }, + "medior": { + "type": "string", + "description": "Medior dev model (default: anthropic/claude-sonnet-4-5)" + }, + "senior": { + "type": "string", + "description": "Senior dev model (default: anthropic/claude-opus-4-5)" + }, + "qa": { + "type": "string", + "description": "QA engineer model (default: anthropic/claude-sonnet-4-5)" + } } - }, - "glabPath": { - "type": "string", - "description": "Path to glab CLI binary. Defaults to 'glab' on PATH." - }, - "ghPath": { - "type": "string", - "description": "Path to gh CLI binary. Defaults to 'gh' on PATH." } } }