diff --git a/dashboard/package.json b/dashboard/package.json index 28a75ab0..92861bea 100644 --- a/dashboard/package.json +++ b/dashboard/package.json @@ -1,6 +1,6 @@ { "name": "workstacean-dashboard", - "version": "0.7.11", + "version": "0.7.12", "private": true, "type": "module", "scripts": { diff --git a/lib/plugins/github.ts b/lib/plugins/github.ts index 506d858b..01345120 100644 --- a/lib/plugins/github.ts +++ b/lib/plugins/github.ts @@ -248,6 +248,7 @@ export class GitHubPlugin implements Plugin { private server: ReturnType | null = null; private workspaceDir: string; private config!: GitHubConfig; + private projectMeta = new Map(); constructor(workspaceDir: string) { this.workspaceDir = workspaceDir; @@ -264,6 +265,7 @@ export class GitHubPlugin implements Plugin { console.log(`[github] Auth: ${usingApp ? "GitHub App (quinn[bot])" : "PAT (GITHUB_TOKEN)"}`); this.config = loadConfig(this.workspaceDir); + this.projectMeta = loadProjectMetaForSweep(this.workspaceDir); const webhookSecret = process.env.GITHUB_WEBHOOK_SECRET ?? ""; const port = parseInt(process.env.GITHUB_WEBHOOK_PORT ?? "8082", 10); @@ -276,6 +278,7 @@ export class GitHubPlugin implements Plugin { if (reloadDebounceTimer) clearTimeout(reloadDebounceTimer); reloadDebounceTimer = setTimeout(() => { this.config = loadConfig(this.workspaceDir); + this.projectMeta = loadProjectMetaForSweep(this.workspaceDir); const repoCount = this.config.autoTriage?.monitoredRepos?.length ?? 0; console.log(`[github] Config reloaded — ${repoCount} monitored repo(s)`); }, 300); @@ -606,6 +609,8 @@ export class GitHubPlugin implements Plugin { const topic = `message.inbound.github.${ctx.owner}.${ctx.repo}.${event}.${ctx.number}`; const replyTopic = `message.outbound.github.${ctx.owner}.${ctx.repo}.${ctx.number}`; + const repoSlug = `${ctx.owner}/${ctx.repo}`; + const meta = this.projectMeta.get(repoSlug); bus.publish(topic, { id: `${event}-${action}-${ctx.owner}-${ctx.repo}-${ctx.number}-${correlationId.slice(0, 8)}`, @@ -619,6 +624,8 @@ export class GitHubPlugin implements Plugin { skillHint: autoTriage.skillHint, trustTier, quarantine, + ...(meta?.projectPath ? { projectPath: meta.projectPath } : {}), + ...(meta?.slug ? { projectSlug: meta.slug } : {}), github: { event, action, diff --git a/package.json b/package.json index c034da98..ffda457a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "workstacean", - "version": "0.7.11", + "version": "0.7.12", "type": "module", "scripts": { "start": "bun run src/index.ts", diff --git a/src/executor/skill-dispatcher-plugin.ts b/src/executor/skill-dispatcher-plugin.ts index f0b9d1e8..3bf194ca 100644 --- a/src/executor/skill-dispatcher-plugin.ts +++ b/src/executor/skill-dispatcher-plugin.ts @@ -348,6 +348,11 @@ export class SkillDispatcherPlugin implements Plugin { text: content, durationMs: Date.now() - dispatchedAt, }); + + const gh = payload.github as { title?: string; owner?: string; repo?: string; number?: number; url?: string } | undefined; + if (skill === "bug_triage" && gh?.title && !isError && typeof payload.projectPath === "string") { + void this._fileTriageOnBoard(gh as Required> & typeof gh, content, payload.projectPath as string); + } }, }); @@ -596,6 +601,39 @@ export class SkillDispatcherPlugin implements Plugin { }); } + private async _fileTriageOnBoard( + github: { title: string; owner?: string; repo?: string; number?: number; url?: string }, + triageSummary: string | undefined, + projectPath: string, + ): Promise { + const apiKey = process.env.WORKSTACEAN_API_KEY; + const port = process.env.WORKSTACEAN_HTTP_PORT ?? "3000"; + const title = `[GH#${github.number}] ${github.title}`; + const description = [ + `GitHub: ${github.url ?? `${github.owner}/${github.repo}#${github.number}`}`, + "", + "## Quinn triage summary", + triageSummary ?? "(no triage output)", + ].join("\n"); + try { + const resp = await fetch(`http://localhost:${port}/api/board/features/create`, { + method: "POST", + headers: { + "Content-Type": "application/json", + ...(apiKey ? { "X-API-Key": apiKey } : {}), + }, + body: JSON.stringify({ projectPath, title, description, status: "backlog", source: "github-triage" }), + }); + if (resp.ok) { + console.log(`[skill-dispatcher] Filed GitHub triage on board: ${title}`); + } else { + console.warn(`[skill-dispatcher] Board filing failed: ${resp.status}`); + } + } catch (err) { + console.warn("[skill-dispatcher] Board filing error:", err); + } + } + private _publishFlowEvent(topic: string, item: FlowItemPayload): void { if (!this.bus) return; this.bus.publish(topic, {