@cliangdev/flux-plugin 0.3.1-dev.ee3b5ee → 0.4.0-dev.0892a21
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/package.json +1 -1
- package/src/server/adapters/github/__tests__/criteria-deps.test.ts +1 -1
- package/src/server/adapters/github/__tests__/prd-crud.test.ts +8 -8
- package/src/server/adapters/github/adapter.ts +110 -88
- package/src/server/adapters/github/client.ts +4 -3
- package/src/server/adapters/github/helpers/index-store.ts +11 -7
- package/src/server/adapters/linear/adapter.ts +121 -105
- package/src/server/adapters/linear/client.ts +21 -14
- package/src/server/tools/__tests__/z-configure-github.test.ts +22 -10
- package/src/server/tools/__tests__/z-get-linear-url.test.ts +2 -2
- package/src/server/tools/configure-github.ts +43 -9
|
@@ -26,8 +26,6 @@ let configCommitted = false;
|
|
|
26
26
|
|
|
27
27
|
mock.module("@octokit/rest", () => ({
|
|
28
28
|
Octokit: class MockOctokit {
|
|
29
|
-
constructor(_opts: { auth: string }) {}
|
|
30
|
-
|
|
31
29
|
users = {
|
|
32
30
|
getAuthenticated: async () => {
|
|
33
31
|
if (!mockTokenValid) {
|
|
@@ -81,7 +79,7 @@ mock.module("@octokit/rest", () => ({
|
|
|
81
79
|
|
|
82
80
|
repos_getContent = {};
|
|
83
81
|
|
|
84
|
-
request = async (route: string,
|
|
82
|
+
request = async (route: string, _opts: any) => {
|
|
85
83
|
if (route === "GET /repos/{owner}/{repo}/contents/{path}") {
|
|
86
84
|
if (!mockRemoteConfigExists) {
|
|
87
85
|
const err: any = new Error("Not Found");
|
|
@@ -109,7 +107,7 @@ mock.module("@octokit/graphql", () => ({
|
|
|
109
107
|
},
|
|
110
108
|
{
|
|
111
109
|
defaults: (_opts: any) => {
|
|
112
|
-
return async (query: string,
|
|
110
|
+
return async (query: string, _vars: any) => {
|
|
113
111
|
if (query.includes("GetOwnerId")) {
|
|
114
112
|
return { user: { id: mockOwnerId } };
|
|
115
113
|
}
|
|
@@ -120,6 +118,14 @@ mock.module("@octokit/graphql", () => ({
|
|
|
120
118
|
},
|
|
121
119
|
};
|
|
122
120
|
}
|
|
121
|
+
if (query.includes("GetRepoId")) {
|
|
122
|
+
return { repository: { id: "REPO_NODE_ID" } };
|
|
123
|
+
}
|
|
124
|
+
if (query.includes("LinkProject")) {
|
|
125
|
+
return {
|
|
126
|
+
linkProjectV2ToRepository: { repository: { id: "REPO_NODE_ID" } },
|
|
127
|
+
};
|
|
128
|
+
}
|
|
123
129
|
throw new Error(`Unhandled GraphQL query: ${query}`);
|
|
124
130
|
};
|
|
125
131
|
},
|
|
@@ -170,12 +176,18 @@ describe("configure_github MCP Tool", () => {
|
|
|
170
176
|
|
|
171
177
|
describe("input validation", () => {
|
|
172
178
|
test("rejects missing token", async () => {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
+
const saved = process.env.FLUX_GITHUB_TOKEN;
|
|
180
|
+
delete process.env.FLUX_GITHUB_TOKEN;
|
|
181
|
+
try {
|
|
182
|
+
await expect(
|
|
183
|
+
configureGithubTool.handler({
|
|
184
|
+
owner: "testowner",
|
|
185
|
+
repo: "flux-tracking",
|
|
186
|
+
}),
|
|
187
|
+
).rejects.toThrow();
|
|
188
|
+
} finally {
|
|
189
|
+
if (saved !== undefined) process.env.FLUX_GITHUB_TOKEN = saved;
|
|
190
|
+
}
|
|
179
191
|
});
|
|
180
192
|
|
|
181
193
|
test("rejects missing owner", async () => {
|
|
@@ -49,7 +49,7 @@ describe("get_linear_url MCP Tool", () => {
|
|
|
49
49
|
test("returns URL for project ref", async () => {
|
|
50
50
|
// Get the adapter and mock its getLinearUrl method
|
|
51
51
|
const adapter = getAdapter() as any;
|
|
52
|
-
adapter.getLinearUrl = mock(async (
|
|
52
|
+
adapter.getLinearUrl = mock(async (_ref: string) => ({
|
|
53
53
|
url: "https://linear.app/myteam/project/my-project-abc123",
|
|
54
54
|
type: "project",
|
|
55
55
|
name: "My Project",
|
|
@@ -69,7 +69,7 @@ describe("get_linear_url MCP Tool", () => {
|
|
|
69
69
|
test("returns URL for issue identifier", async () => {
|
|
70
70
|
// Get the adapter and mock its getLinearUrl method
|
|
71
71
|
const adapter = getAdapter() as any;
|
|
72
|
-
adapter.getLinearUrl = mock(async (
|
|
72
|
+
adapter.getLinearUrl = mock(async (_ref: string) => ({
|
|
73
73
|
url: "https://linear.app/myteam/issue/ENG-42",
|
|
74
74
|
type: "issue",
|
|
75
75
|
identifier: "ENG-42",
|
|
@@ -51,7 +51,8 @@ async function validateToken(octokit: Octokit): Promise<string> {
|
|
|
51
51
|
try {
|
|
52
52
|
const { data } = await octokit.users.getAuthenticated();
|
|
53
53
|
return data.login;
|
|
54
|
-
} catch (
|
|
54
|
+
} catch (error: unknown) {
|
|
55
|
+
const err = error as { status?: number; message?: string };
|
|
55
56
|
const status = err.status ?? 0;
|
|
56
57
|
throw new Error(
|
|
57
58
|
`GitHub token validation failed (HTTP ${status}): ${err.message}. Ensure the token has 'repo' scope.`,
|
|
@@ -90,11 +91,12 @@ async function fetchRemoteConfig(
|
|
|
90
91
|
const data = response.data as { content: string; encoding: string };
|
|
91
92
|
const decoded = Buffer.from(data.content, "base64").toString("utf-8");
|
|
92
93
|
return JSON.parse(decoded) as SharedConfig;
|
|
93
|
-
} catch (
|
|
94
|
-
|
|
94
|
+
} catch (error: unknown) {
|
|
95
|
+
const status = (error as { status?: number }).status;
|
|
96
|
+
if (status === 404 || status === 403) {
|
|
95
97
|
return null;
|
|
96
98
|
}
|
|
97
|
-
throw
|
|
99
|
+
throw error;
|
|
98
100
|
}
|
|
99
101
|
}
|
|
100
102
|
|
|
@@ -124,8 +126,8 @@ async function ensureRepo(
|
|
|
124
126
|
try {
|
|
125
127
|
const { data } = await octokit.repos.get({ owner, repo });
|
|
126
128
|
return data.html_url;
|
|
127
|
-
} catch (
|
|
128
|
-
if (
|
|
129
|
+
} catch (error: unknown) {
|
|
130
|
+
if ((error as { status?: number }).status !== 404) throw error;
|
|
129
131
|
}
|
|
130
132
|
|
|
131
133
|
const { data } = await octokit.repos.createForAuthenticatedUser({
|
|
@@ -163,9 +165,14 @@ async function ensureLabels(
|
|
|
163
165
|
}
|
|
164
166
|
|
|
165
167
|
async function createProjectsBoard(
|
|
166
|
-
gql: (
|
|
168
|
+
gql: (
|
|
169
|
+
query: string,
|
|
170
|
+
vars: Record<string, unknown>,
|
|
171
|
+
) => Promise<Record<string, Record<string, unknown>>>,
|
|
167
172
|
ownerLogin: string,
|
|
168
173
|
title: string,
|
|
174
|
+
repoOwner: string,
|
|
175
|
+
repoName: string,
|
|
169
176
|
): Promise<{ id: string; url: string }> {
|
|
170
177
|
const ownerResult = await gql(
|
|
171
178
|
`query GetOwnerId($login: String!) { user(login: $login) { id } }`,
|
|
@@ -182,7 +189,29 @@ async function createProjectsBoard(
|
|
|
182
189
|
{ ownerId, title },
|
|
183
190
|
);
|
|
184
191
|
|
|
185
|
-
|
|
192
|
+
const project = projectResult.createProjectV2.projectV2 as unknown as {
|
|
193
|
+
id: string;
|
|
194
|
+
url: string;
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
const repoResult = await gql(
|
|
198
|
+
`query GetRepoId($owner: String!, $name: String!) {
|
|
199
|
+
repository(owner: $owner, name: $name) { id }
|
|
200
|
+
}`,
|
|
201
|
+
{ owner: repoOwner, name: repoName },
|
|
202
|
+
);
|
|
203
|
+
const repositoryId = repoResult.repository.id;
|
|
204
|
+
|
|
205
|
+
await gql(
|
|
206
|
+
`mutation LinkProject($projectId: ID!, $repositoryId: ID!) {
|
|
207
|
+
linkProjectV2ToRepository(input: { projectId: $projectId, repositoryId: $repositoryId }) {
|
|
208
|
+
repository { id }
|
|
209
|
+
}
|
|
210
|
+
}`,
|
|
211
|
+
{ projectId: project.id, repositoryId },
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
return project;
|
|
186
215
|
}
|
|
187
216
|
|
|
188
217
|
async function commitSharedConfig(
|
|
@@ -231,7 +260,10 @@ function writeProjectJson(params: {
|
|
|
231
260
|
|
|
232
261
|
async function runSetup(params: {
|
|
233
262
|
octokit: Octokit;
|
|
234
|
-
gql: (
|
|
263
|
+
gql: (
|
|
264
|
+
query: string,
|
|
265
|
+
vars: Record<string, unknown>,
|
|
266
|
+
) => Promise<Record<string, Record<string, unknown>>>;
|
|
235
267
|
ownerLogin: string;
|
|
236
268
|
owner: string;
|
|
237
269
|
repo: string;
|
|
@@ -254,6 +286,8 @@ async function runSetup(params: {
|
|
|
254
286
|
params.gql,
|
|
255
287
|
params.ownerLogin,
|
|
256
288
|
`Flux: ${params.repo}`,
|
|
289
|
+
params.owner,
|
|
290
|
+
params.repo,
|
|
257
291
|
);
|
|
258
292
|
|
|
259
293
|
const sharedConfig: SharedConfig = {
|