Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .changeset/forge-two-pass-adapter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@bradygaster/squad-sdk": minor
"@bradygaster/squad-cli": patch
---

feat(platform): hydrate work-item `body` via `getWorkItem` and route two-pass scan through the adapter

`WorkItem` now carries an optional `body`, populated by both `GitHubAdapter` (`gh issue view --json body`) and `AzureDevOpsAdapter` (`System.Description`). The watch `two-pass` capability hydrates actionable items through `adapter.getWorkItem` instead of a hardcoded `gh issue view`, so it works on Azure DevOps as well as GitHub.
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,8 @@
* TwoPass capability — lightweight list then hydrate actionable issues only.
*/

import { execFile } from 'node:child_process';
import { promisify } from 'node:util';
import type { WatchCapability, WatchContext, PreflightResult, CapabilityResult } from '../types.js';

const execFileAsync = promisify(execFile);

/** Labels that block autonomous execution. */
const BLOCKED_LABELS: ReadonlySet<string> = new Set([
'status:blocked', 'status:waiting-external', 'status:postponed',
Expand All @@ -19,7 +15,7 @@ export class TwoPassCapability implements WatchCapability {
readonly name = 'two-pass';
readonly description = 'Lightweight scan then hydrate only actionable issues';
readonly configShape = 'boolean' as const;
readonly requires = ['gh'];
readonly requires = [];
readonly phase = 'post-triage' as const;

async preflight(_context: WatchContext): Promise<PreflightResult> {
Expand All @@ -45,15 +41,18 @@ export class TwoPassCapability implements WatchCapability {
return true;
});

// Pass 2: hydrate actionable issues (fetch body + comments)
// Pass 2: hydrate actionable issues (fetch body) via the platform adapter.
// Some adapters (e.g. ADO) already populate body during listWorkItems,
// so skip the extra fetch when it's already present.
const hydrated: Array<{ number: number; title: string; body?: string }> = [];
for (const item of actionable) {
if (item.body !== undefined) {
hydrated.push({ number: item.id, title: item.title, body: item.body });
continue;
}
try {
const { stdout: detailJson } = await execFileAsync('gh', [
'issue', 'view', String(item.id),
'--json', 'number,title,body,labels,assignees',
], { maxBuffer: 5 * 1024 * 1024 });
hydrated.push(JSON.parse(detailJson));
const full = await context.adapter.getWorkItem(item.id);
hydrated.push({ number: full.id, title: full.title, body: full.body });
} catch {
hydrated.push({ number: item.id, title: item.title });
}
Expand Down
1 change: 1 addition & 0 deletions packages/squad-sdk/src/platform/azure-devops.ts
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ export class AzureDevOpsAdapter implements PlatformAdapter {
state: (fields['System.State'] as string) ?? '',
tags,
assignedTo: assignedTo?.displayName ?? assignedTo?.uniqueName,
body: typeof fields['System.Description'] === 'string' ? (fields['System.Description'] as string) : undefined,
url: wi._links?.html?.href ?? wi.url,
};
}
Expand Down
4 changes: 3 additions & 1 deletion packages/squad-sdk/src/platform/github.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export class GitHubAdapter implements PlatformAdapter {
async getWorkItem(id: number): Promise<WorkItem> {
const output = this.gh([
'issue', 'view', String(id), '--repo', this.repoFlag,
'--json', 'number,title,state,labels,assignees,url',
'--json', 'number,title,state,labels,assignees,url,body',
]);
const issue = parseJson<{
number: number;
Expand All @@ -76,6 +76,7 @@ export class GitHubAdapter implements PlatformAdapter {
labels: Array<{ name: string }>;
assignees: Array<{ login: string }>;
url: string;
body?: string;
}>(output);

return {
Expand All @@ -84,6 +85,7 @@ export class GitHubAdapter implements PlatformAdapter {
state: issue.state.toLowerCase(),
tags: issue.labels.map((l) => l.name),
assignedTo: issue.assignees[0]?.login,
body: issue.body,
url: issue.url,
};
}
Expand Down
1 change: 1 addition & 0 deletions packages/squad-sdk/src/platform/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export interface WorkItem {
state: string;
tags: string[];
assignedTo?: string;
body?: string;
url: string;
}

Expand Down