@loopstack/github-oauth-example 0.3.1 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -4
- package/dist/tools/authenticate-github-task.tool.d.ts +13 -5
- package/dist/tools/authenticate-github-task.tool.d.ts.map +1 -1
- package/dist/tools/authenticate-github-task.tool.js +17 -17
- package/dist/tools/authenticate-github-task.tool.js.map +1 -1
- package/dist/workflows/github-agent.ui.yaml +6 -16
- package/dist/workflows/github-agent.workflow.d.ts +44 -40
- package/dist/workflows/github-agent.workflow.d.ts.map +1 -1
- package/dist/workflows/github-agent.workflow.js +143 -195
- package/dist/workflows/github-agent.workflow.js.map +1 -1
- package/dist/workflows/github-repos-overview.workflow.d.ts +31 -28
- package/dist/workflows/github-repos-overview.workflow.d.ts.map +1 -1
- package/dist/workflows/github-repos-overview.workflow.js +92 -115
- package/dist/workflows/github-repos-overview.workflow.js.map +1 -1
- package/package.json +6 -7
- package/src/tools/authenticate-github-task.tool.ts +30 -18
- package/src/workflows/__tests__/github-repos-overview-workflow.spec.ts +18 -44
- package/src/workflows/github-agent.ui.yaml +6 -16
- package/src/workflows/github-agent.workflow.ts +134 -128
- package/src/workflows/github-repos-overview.workflow.ts +103 -150
- package/dist/workflows/github-repos-overview.ui.yaml +0 -17
- package/src/workflows/github-repos-overview.ui.yaml +0 -17
|
@@ -1,14 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import {
|
|
3
|
-
BaseWorkflow,
|
|
4
|
-
Guard,
|
|
5
|
-
Initial,
|
|
6
|
-
InjectTool,
|
|
7
|
-
InjectWorkflow,
|
|
8
|
-
ToolResult,
|
|
9
|
-
Transition,
|
|
10
|
-
Workflow,
|
|
11
|
-
} from '@loopstack/common';
|
|
2
|
+
import { BaseWorkflow, Guard, Transition, Workflow } from '@loopstack/common';
|
|
12
3
|
import {
|
|
13
4
|
GitHubCreateIssueCommentTool,
|
|
14
5
|
GitHubCreateIssueTool,
|
|
@@ -46,98 +37,65 @@ import {
|
|
|
46
37
|
import { OAuthWorkflow } from '@loopstack/oauth-module';
|
|
47
38
|
import { AuthenticateGitHubTask } from '../tools/authenticate-github-task.tool';
|
|
48
39
|
|
|
49
|
-
|
|
50
|
-
uiConfig: __dirname + '/github-agent.ui.yaml',
|
|
51
|
-
})
|
|
52
|
-
export class GitHubAgentWorkflow extends BaseWorkflow {
|
|
53
|
-
@InjectTool({
|
|
54
|
-
provider: 'claude',
|
|
55
|
-
model: 'claude-sonnet-4-6',
|
|
56
|
-
system: `You are a helpful GitHub assistant with access to repository, issue, PR, code, actions,
|
|
57
|
-
and search tools. When a tool returns an unauthorized error, use authenticateGitHub
|
|
58
|
-
to let the user sign in, then retry. Be concise and format results using markdown.`,
|
|
59
|
-
tools: [
|
|
60
|
-
'gitHubListRepos',
|
|
61
|
-
'gitHubGetRepo',
|
|
62
|
-
'gitHubCreateRepo',
|
|
63
|
-
'gitHubListBranches',
|
|
64
|
-
'gitHubListIssues',
|
|
65
|
-
'gitHubGetIssue',
|
|
66
|
-
'gitHubCreateIssue',
|
|
67
|
-
'gitHubCreateIssueComment',
|
|
68
|
-
'gitHubListPullRequests',
|
|
69
|
-
'gitHubGetPullRequest',
|
|
70
|
-
'gitHubCreatePullRequest',
|
|
71
|
-
'gitHubMergePullRequest',
|
|
72
|
-
'gitHubListPrReviews',
|
|
73
|
-
'gitHubGetFileContent',
|
|
74
|
-
'gitHubCreateOrUpdateFile',
|
|
75
|
-
'gitHubListDirectory',
|
|
76
|
-
'gitHubGetCommit',
|
|
77
|
-
'gitHubListWorkflowRuns',
|
|
78
|
-
'gitHubTriggerWorkflow',
|
|
79
|
-
'gitHubGetWorkflowRun',
|
|
80
|
-
'gitHubSearchCode',
|
|
81
|
-
'gitHubSearchRepos',
|
|
82
|
-
'gitHubSearchIssues',
|
|
83
|
-
'gitHubGetAuthenticatedUser',
|
|
84
|
-
'gitHubListUserOrgs',
|
|
85
|
-
'authenticateGitHub',
|
|
86
|
-
],
|
|
87
|
-
})
|
|
88
|
-
llmGenerateText: LlmGenerateTextTool;
|
|
89
|
-
@InjectTool({ provider: 'claude' }) llmDelegateToolCalls: LlmDelegateToolCallsTool;
|
|
90
|
-
@InjectTool({ provider: 'claude' }) llmUpdateToolResult: LlmUpdateToolResultTool;
|
|
91
|
-
@InjectTool() authenticateGitHub: AuthenticateGitHubTask;
|
|
92
|
-
|
|
93
|
-
// GitHub Repos tools
|
|
94
|
-
@InjectTool() gitHubListRepos: GitHubListReposTool;
|
|
95
|
-
@InjectTool() gitHubGetRepo: GitHubGetRepoTool;
|
|
96
|
-
@InjectTool() gitHubCreateRepo: GitHubCreateRepoTool;
|
|
97
|
-
@InjectTool() gitHubListBranches: GitHubListBranchesTool;
|
|
98
|
-
|
|
99
|
-
// GitHub Issues tools
|
|
100
|
-
@InjectTool() gitHubListIssues: GitHubListIssuesTool;
|
|
101
|
-
@InjectTool() gitHubGetIssue: GitHubGetIssueTool;
|
|
102
|
-
@InjectTool() gitHubCreateIssue: GitHubCreateIssueTool;
|
|
103
|
-
@InjectTool() gitHubCreateIssueComment: GitHubCreateIssueCommentTool;
|
|
104
|
-
|
|
105
|
-
// GitHub Pull Requests tools
|
|
106
|
-
@InjectTool() gitHubListPullRequests: GitHubListPullRequestsTool;
|
|
107
|
-
@InjectTool() gitHubGetPullRequest: GitHubGetPullRequestTool;
|
|
108
|
-
@InjectTool() gitHubCreatePullRequest: GitHubCreatePullRequestTool;
|
|
109
|
-
@InjectTool() gitHubMergePullRequest: GitHubMergePullRequestTool;
|
|
110
|
-
@InjectTool() gitHubListPrReviews: GitHubListPrReviewsTool;
|
|
111
|
-
|
|
112
|
-
// GitHub Content / Git Ops tools
|
|
113
|
-
@InjectTool() gitHubGetFileContent: GitHubGetFileContentTool;
|
|
114
|
-
@InjectTool() gitHubCreateOrUpdateFile: GitHubCreateOrUpdateFileTool;
|
|
115
|
-
@InjectTool() gitHubListDirectory: GitHubListDirectoryTool;
|
|
116
|
-
@InjectTool() gitHubGetCommit: GitHubGetCommitTool;
|
|
117
|
-
|
|
118
|
-
// GitHub Actions tools
|
|
119
|
-
@InjectTool() gitHubListWorkflowRuns: GitHubListWorkflowRunsTool;
|
|
120
|
-
@InjectTool() gitHubTriggerWorkflow: GitHubTriggerWorkflowTool;
|
|
121
|
-
@InjectTool() gitHubGetWorkflowRun: GitHubGetWorkflowRunTool;
|
|
122
|
-
|
|
123
|
-
// GitHub Search tools
|
|
124
|
-
@InjectTool() gitHubSearchCode: GitHubSearchCodeTool;
|
|
125
|
-
@InjectTool() gitHubSearchRepos: GitHubSearchReposTool;
|
|
126
|
-
@InjectTool() gitHubSearchIssues: GitHubSearchIssuesTool;
|
|
127
|
-
|
|
128
|
-
// GitHub Users & Orgs tools
|
|
129
|
-
@InjectTool() gitHubGetAuthenticatedUser: GitHubGetAuthenticatedUserTool;
|
|
130
|
-
@InjectTool() gitHubListUserOrgs: GitHubListUserOrgsTool;
|
|
131
|
-
|
|
132
|
-
@InjectWorkflow() oAuth: OAuthWorkflow;
|
|
133
|
-
|
|
40
|
+
interface GitHubAgentState {
|
|
134
41
|
llmResult?: LlmGenerateTextResult;
|
|
135
42
|
llmMeta?: LlmResultMeta;
|
|
136
43
|
delegateResult?: LlmDelegateResult;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
@Workflow({
|
|
47
|
+
title: 'GitHub Agent',
|
|
48
|
+
description:
|
|
49
|
+
'An interactive chat agent with access to GitHub.\nAsk it to manage repositories, issues, pull requests, browse code, check CI/CD status,\nsearch across GitHub, and more. Handles OAuth authentication automatically when needed —\nthe agent detects unauthorized errors and launches authentication on its own.',
|
|
50
|
+
name: 'github_agent',
|
|
51
|
+
widget: __dirname + '/github-agent.ui.yaml',
|
|
52
|
+
})
|
|
53
|
+
export class GitHubAgentWorkflow extends BaseWorkflow<Record<string, unknown>, GitHubAgentState> {
|
|
54
|
+
constructor(
|
|
55
|
+
private readonly llmGenerateText: LlmGenerateTextTool,
|
|
56
|
+
private readonly llmDelegateToolCalls: LlmDelegateToolCallsTool,
|
|
57
|
+
private readonly llmUpdateToolResult: LlmUpdateToolResultTool,
|
|
58
|
+
private readonly authenticateGitHub: AuthenticateGitHubTask,
|
|
59
|
+
// GitHub Repos tools
|
|
60
|
+
private readonly gitHubListRepos: GitHubListReposTool,
|
|
61
|
+
private readonly gitHubGetRepo: GitHubGetRepoTool,
|
|
62
|
+
private readonly gitHubCreateRepo: GitHubCreateRepoTool,
|
|
63
|
+
private readonly gitHubListBranches: GitHubListBranchesTool,
|
|
64
|
+
// GitHub Issues tools
|
|
65
|
+
private readonly gitHubListIssues: GitHubListIssuesTool,
|
|
66
|
+
private readonly gitHubGetIssue: GitHubGetIssueTool,
|
|
67
|
+
private readonly gitHubCreateIssue: GitHubCreateIssueTool,
|
|
68
|
+
private readonly gitHubCreateIssueComment: GitHubCreateIssueCommentTool,
|
|
69
|
+
// GitHub Pull Requests tools
|
|
70
|
+
private readonly gitHubListPullRequests: GitHubListPullRequestsTool,
|
|
71
|
+
private readonly gitHubGetPullRequest: GitHubGetPullRequestTool,
|
|
72
|
+
private readonly gitHubCreatePullRequest: GitHubCreatePullRequestTool,
|
|
73
|
+
private readonly gitHubMergePullRequest: GitHubMergePullRequestTool,
|
|
74
|
+
private readonly gitHubListPrReviews: GitHubListPrReviewsTool,
|
|
75
|
+
// GitHub Content / Git Ops tools
|
|
76
|
+
private readonly gitHubGetFileContent: GitHubGetFileContentTool,
|
|
77
|
+
private readonly gitHubCreateOrUpdateFile: GitHubCreateOrUpdateFileTool,
|
|
78
|
+
private readonly gitHubListDirectory: GitHubListDirectoryTool,
|
|
79
|
+
private readonly gitHubGetCommit: GitHubGetCommitTool,
|
|
80
|
+
// GitHub Actions tools
|
|
81
|
+
private readonly gitHubListWorkflowRuns: GitHubListWorkflowRunsTool,
|
|
82
|
+
private readonly gitHubTriggerWorkflow: GitHubTriggerWorkflowTool,
|
|
83
|
+
private readonly gitHubGetWorkflowRun: GitHubGetWorkflowRunTool,
|
|
84
|
+
// GitHub Search tools
|
|
85
|
+
private readonly gitHubSearchCode: GitHubSearchCodeTool,
|
|
86
|
+
private readonly gitHubSearchRepos: GitHubSearchReposTool,
|
|
87
|
+
private readonly gitHubSearchIssues: GitHubSearchIssuesTool,
|
|
88
|
+
// GitHub Users & Orgs tools
|
|
89
|
+
private readonly gitHubGetAuthenticatedUser: GitHubGetAuthenticatedUserTool,
|
|
90
|
+
private readonly gitHubListUserOrgs: GitHubListUserOrgsTool,
|
|
91
|
+
private readonly oAuth: OAuthWorkflow,
|
|
92
|
+
) {
|
|
93
|
+
super();
|
|
94
|
+
}
|
|
137
95
|
|
|
138
|
-
@
|
|
139
|
-
async setup() {
|
|
140
|
-
await this.
|
|
96
|
+
@Transition({ to: 'waiting_for_user' })
|
|
97
|
+
async setup(state: GitHubAgentState): Promise<GitHubAgentState> {
|
|
98
|
+
await this.documentStore.save(
|
|
141
99
|
LlmMessageDocument,
|
|
142
100
|
{
|
|
143
101
|
role: 'user',
|
|
@@ -145,71 +103,119 @@ to let the user sign in, then retry. Be concise and format results using markdow
|
|
|
145
103
|
},
|
|
146
104
|
{ meta: { hidden: true } },
|
|
147
105
|
);
|
|
106
|
+
return state;
|
|
148
107
|
}
|
|
149
108
|
|
|
150
109
|
@Transition({ from: 'waiting_for_user', to: 'ready', wait: true, schema: z.string() })
|
|
151
|
-
async userMessage(payload: string) {
|
|
152
|
-
await this.
|
|
110
|
+
async userMessage(state: GitHubAgentState, payload: string): Promise<GitHubAgentState> {
|
|
111
|
+
await this.documentStore.save(LlmMessageDocument, {
|
|
153
112
|
role: 'user',
|
|
154
113
|
content: payload,
|
|
155
114
|
});
|
|
115
|
+
return state;
|
|
156
116
|
}
|
|
157
117
|
|
|
158
118
|
@Transition({ from: 'ready', to: 'prompt_executed' })
|
|
159
|
-
async llmTurn() {
|
|
160
|
-
const result
|
|
161
|
-
|
|
162
|
-
|
|
119
|
+
async llmTurn(state: GitHubAgentState): Promise<GitHubAgentState> {
|
|
120
|
+
const result = await this.llmGenerateText.call(
|
|
121
|
+
{},
|
|
122
|
+
{
|
|
123
|
+
config: {
|
|
124
|
+
provider: 'claude',
|
|
125
|
+
model: 'claude-sonnet-4-6',
|
|
126
|
+
system: `You are a helpful GitHub assistant with access to repository, issue, PR, code, actions,
|
|
127
|
+
and search tools. When a tool returns an unauthorized error, use authenticateGitHub
|
|
128
|
+
to let the user sign in, then retry. Be concise and format results using markdown.`,
|
|
129
|
+
tools: [
|
|
130
|
+
'github_list_repos',
|
|
131
|
+
'github_get_repo',
|
|
132
|
+
'github_create_repo',
|
|
133
|
+
'github_list_branches',
|
|
134
|
+
'github_list_issues',
|
|
135
|
+
'github_get_issue',
|
|
136
|
+
'github_create_issue',
|
|
137
|
+
'github_create_issue_comment',
|
|
138
|
+
'github_list_pull_requests',
|
|
139
|
+
'github_get_pull_request',
|
|
140
|
+
'github_create_pull_request',
|
|
141
|
+
'github_merge_pull_request',
|
|
142
|
+
'github_list_pr_reviews',
|
|
143
|
+
'github_get_file_content',
|
|
144
|
+
'github_create_or_update_file',
|
|
145
|
+
'github_list_directory',
|
|
146
|
+
'github_get_commit',
|
|
147
|
+
'github_list_workflow_runs',
|
|
148
|
+
'github_trigger_workflow',
|
|
149
|
+
'github_get_workflow_run',
|
|
150
|
+
'github_search_code',
|
|
151
|
+
'github_search_repos',
|
|
152
|
+
'github_search_issues',
|
|
153
|
+
'github_get_authenticated_user',
|
|
154
|
+
'github_list_user_orgs',
|
|
155
|
+
'authenticate_github',
|
|
156
|
+
],
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
);
|
|
160
|
+
return { ...state, llmResult: result.data, llmMeta: result.metadata as LlmResultMeta | undefined };
|
|
163
161
|
}
|
|
164
162
|
|
|
165
163
|
@Transition({ from: 'prompt_executed', to: 'awaiting_tools', priority: 10 })
|
|
166
164
|
@Guard('hasToolCalls')
|
|
167
|
-
async executeToolCalls() {
|
|
168
|
-
await this.
|
|
169
|
-
meta: { response:
|
|
165
|
+
async executeToolCalls(state: GitHubAgentState): Promise<GitHubAgentState> {
|
|
166
|
+
await this.documentStore.save(LlmMessageDocument, state.llmResult!.message, {
|
|
167
|
+
meta: { response: state.llmResult!.response, provider: state.llmMeta!.provider },
|
|
170
168
|
});
|
|
171
|
-
const result
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
169
|
+
const result = await this.llmDelegateToolCalls.call(
|
|
170
|
+
{
|
|
171
|
+
message: state.llmResult!.message,
|
|
172
|
+
callback: { transition: 'toolResultReceived' },
|
|
173
|
+
},
|
|
174
|
+
{ config: { provider: 'claude' } },
|
|
175
|
+
);
|
|
176
|
+
return { ...state, delegateResult: result.data };
|
|
176
177
|
}
|
|
177
178
|
|
|
178
|
-
hasToolCalls(): boolean {
|
|
179
|
-
return
|
|
179
|
+
hasToolCalls(state: GitHubAgentState): boolean {
|
|
180
|
+
return state.llmResult?.message.stopReason === 'tool_use';
|
|
180
181
|
}
|
|
181
182
|
|
|
182
183
|
@Transition({ from: 'awaiting_tools', to: 'awaiting_tools', wait: true, schema: z.record(z.string(), z.unknown()) })
|
|
183
|
-
async toolResultReceived(payload: Record<string, unknown>) {
|
|
184
|
-
const result
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
184
|
+
async toolResultReceived(state: GitHubAgentState, payload: Record<string, unknown>): Promise<GitHubAgentState> {
|
|
185
|
+
const result = await this.llmUpdateToolResult.call(
|
|
186
|
+
{
|
|
187
|
+
delegateResult: state.delegateResult!,
|
|
188
|
+
completedTool: payload,
|
|
189
|
+
},
|
|
190
|
+
{ config: { provider: 'claude' } },
|
|
191
|
+
);
|
|
192
|
+
return { ...state, delegateResult: result.data };
|
|
189
193
|
}
|
|
190
194
|
|
|
191
195
|
@Transition({ from: 'awaiting_tools', to: 'ready' })
|
|
192
196
|
@Guard('allToolsComplete')
|
|
193
|
-
async allToolsCompleteTransition() {
|
|
194
|
-
await this.
|
|
197
|
+
async allToolsCompleteTransition(state: GitHubAgentState): Promise<GitHubAgentState> {
|
|
198
|
+
await this.documentStore.save(LlmMessageDocument, {
|
|
195
199
|
role: 'user',
|
|
196
|
-
content:
|
|
200
|
+
content: state.delegateResult!.toolResults.map((tr) => ({
|
|
197
201
|
type: 'tool_result' as const,
|
|
198
202
|
toolCallId: tr.toolCallId,
|
|
199
203
|
content: tr.content ?? '',
|
|
200
204
|
isError: tr.isError ?? false,
|
|
201
205
|
})),
|
|
202
206
|
});
|
|
207
|
+
return state;
|
|
203
208
|
}
|
|
204
209
|
|
|
205
|
-
allToolsComplete(): boolean {
|
|
206
|
-
return
|
|
210
|
+
allToolsComplete(state: GitHubAgentState): boolean {
|
|
211
|
+
return state.delegateResult?.allCompleted ?? false;
|
|
207
212
|
}
|
|
208
213
|
|
|
209
214
|
@Transition({ from: 'prompt_executed', to: 'waiting_for_user' })
|
|
210
|
-
async respond() {
|
|
211
|
-
await this.
|
|
212
|
-
meta: { response:
|
|
215
|
+
async respond(state: GitHubAgentState): Promise<GitHubAgentState> {
|
|
216
|
+
await this.documentStore.save(LlmMessageDocument, state.llmResult!.message, {
|
|
217
|
+
meta: { response: state.llmResult!.response, provider: state.llmMeta!.provider },
|
|
213
218
|
});
|
|
219
|
+
return state;
|
|
214
220
|
}
|
|
215
221
|
}
|