@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.
@@ -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, opts: any) => {
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, vars: any) => {
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
- await expect(
174
- configureGithubTool.handler({
175
- owner: "testowner",
176
- repo: "flux-tracking",
177
- }),
178
- ).rejects.toThrow();
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 (ref: string) => ({
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 (ref: string) => ({
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 (err: any) {
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 (err: any) {
94
- if (err.status === 404 || err.status === 403) {
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 err;
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 (err: any) {
128
- if (err.status !== 404) throw err;
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: (query: string, vars: any) => Promise<any>,
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
- return projectResult.createProjectV2.projectV2;
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: (query: string, vars: any) => Promise<any>;
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 = {