@foundation0/api 1.1.8 → 1.1.10

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.
@@ -204,7 +204,8 @@ describe("createExampleMcpServer request handling", () => {
204
204
  }
205
205
  });
206
206
 
207
- it("accepts repoName for projects tools (preferred over legacy repoRoot/processRoot)", async () => {
207
+ it("accepts repoName selectors in short and owner/repo format", async () => {
208
+ const originalCwd = process.cwd();
208
209
  const tempDir = await fs.mkdtemp(
209
210
  path.join(os.tmpdir(), "f0-mcp-server-reponame-"),
210
211
  );
@@ -216,114 +217,67 @@ describe("createExampleMcpServer request handling", () => {
216
217
  await fs.mkdir(path.join(tempDir, "projects", "beta", "docs"), {
217
218
  recursive: true,
218
219
  });
219
-
220
- const instance = createExampleMcpServer({ repoRoot: tempDir, repoName: "test" });
221
- const handler = getToolHandler(instance);
222
-
223
- const result = await handler(
224
- {
225
- method: "tools/call",
226
- params: {
227
- name: "projects.listProjects",
228
- arguments: {
229
- repoName: "test",
230
- },
231
- },
232
- },
233
- {},
220
+ await fs.mkdir(path.join(tempDir, ".git"), { recursive: true });
221
+ await fs.writeFile(
222
+ path.join(tempDir, ".git", "config"),
223
+ [
224
+ '[remote "origin"]',
225
+ "\turl = https://example.com/F0/adl.git",
226
+ "",
227
+ ].join("\n"),
234
228
  );
235
229
 
236
- expect(result.isError).toBe(false);
237
- const payload = JSON.parse(result.content?.[0]?.text ?? "{}");
238
- expect(payload.ok).toBe(true);
239
- expect(payload.result).toContain("adl");
240
- expect(payload.result).toContain("beta");
241
- } finally {
242
- await fs.rm(tempDir, { recursive: true, force: true });
243
- }
244
- });
245
-
246
- it("auto-detects repoRoot from process.cwd() when repoName is omitted", async () => {
247
- const tempDir = await fs.mkdtemp(
248
- path.join(os.tmpdir(), "f0-mcp-server-autoreporoot-"),
249
- );
250
- const originalCwd = process.cwd();
251
-
252
- try {
253
- await fs.mkdir(path.join(tempDir, "api"), { recursive: true });
254
- await fs.mkdir(path.join(tempDir, "projects", "adl", "docs"), {
255
- recursive: true,
256
- });
257
- await fs.mkdir(path.join(tempDir, "projects", "beta", "docs"), {
258
- recursive: true,
259
- });
260
-
261
230
  process.chdir(tempDir);
262
-
263
231
  const instance = createExampleMcpServer();
264
232
  const handler = getToolHandler(instance);
265
233
 
266
- const result = await handler(
234
+ const shortSelector = await handler(
267
235
  {
268
236
  method: "tools/call",
269
237
  params: {
270
238
  name: "projects.listProjects",
271
- arguments: {},
239
+ arguments: {
240
+ repoName: "adl",
241
+ },
272
242
  },
273
243
  },
274
244
  {},
275
245
  );
246
+ expect(shortSelector.isError).toBe(false);
247
+ const shortPayload = JSON.parse(shortSelector.content?.[0]?.text ?? "{}");
248
+ expect(shortPayload.ok).toBe(true);
249
+ expect(shortPayload.result).toContain("adl");
250
+ expect(shortPayload.result).toContain("beta");
276
251
 
277
- expect(result.isError).toBe(false);
278
- const payload = JSON.parse(result.content?.[0]?.text ?? "{}");
279
- expect(payload.ok).toBe(true);
280
- expect(payload.result).toContain("adl");
281
- expect(payload.result).toContain("beta");
282
- } finally {
283
- process.chdir(originalCwd);
284
- await fs.rm(tempDir, { recursive: true, force: true });
285
- }
286
- });
287
-
288
- it("still accepts legacy processRoot for backwards compatibility", async () => {
289
- const tempDir = await fs.mkdtemp(
290
- path.join(os.tmpdir(), "f0-mcp-server-processroot-"),
291
- );
292
- try {
293
- await fs.mkdir(path.join(tempDir, "api"), { recursive: true });
294
- await fs.mkdir(path.join(tempDir, "projects", "adl", "docs"), {
295
- recursive: true,
296
- });
297
-
298
- const instance = createExampleMcpServer();
299
- const handler = getToolHandler(instance);
300
-
301
- const result = await handler(
252
+ const fullSelector = await handler(
302
253
  {
303
254
  method: "tools/call",
304
255
  params: {
305
256
  name: "projects.listProjects",
306
257
  arguments: {
307
- processRoot: tempDir,
258
+ repoName: "F0/adl",
308
259
  },
309
260
  },
310
261
  },
311
262
  {},
312
263
  );
313
-
314
- expect(result.isError).toBe(false);
315
- const payload = JSON.parse(result.content?.[0]?.text ?? "{}");
316
- expect(payload.ok).toBe(true);
317
- expect(payload.result).toContain("adl");
264
+ expect(fullSelector.isError).toBe(false);
265
+ const fullPayload = JSON.parse(fullSelector.content?.[0]?.text ?? "{}");
266
+ expect(fullPayload.ok).toBe(true);
267
+ expect(fullPayload.result).toContain("adl");
268
+ expect(fullPayload.result).toContain("beta");
318
269
  } finally {
270
+ process.chdir(originalCwd);
319
271
  await fs.rm(tempDir, { recursive: true, force: true });
320
272
  }
321
273
  });
322
274
 
323
- it("normalizes legacy repoRoot when caller accidentally passes a project root", async () => {
275
+ it("auto-detects Git workspace from process.cwd() when repoName is omitted", async () => {
324
276
  const tempDir = await fs.mkdtemp(
325
- path.join(os.tmpdir(), "f0-mcp-server-reporoot-normalize-"),
277
+ path.join(os.tmpdir(), "f0-mcp-server-autoworkspace-"),
326
278
  );
279
+ const originalCwd = process.cwd();
280
+
327
281
  try {
328
282
  await fs.mkdir(path.join(tempDir, "api"), { recursive: true });
329
283
  await fs.mkdir(path.join(tempDir, "projects", "adl", "docs"), {
@@ -332,6 +286,17 @@ describe("createExampleMcpServer request handling", () => {
332
286
  await fs.mkdir(path.join(tempDir, "projects", "beta", "docs"), {
333
287
  recursive: true,
334
288
  });
289
+ await fs.mkdir(path.join(tempDir, ".git"), { recursive: true });
290
+ await fs.writeFile(
291
+ path.join(tempDir, ".git", "config"),
292
+ [
293
+ '[remote "origin"]',
294
+ "\turl = https://example.com/F0/adl.git",
295
+ "",
296
+ ].join("\n"),
297
+ );
298
+
299
+ process.chdir(tempDir);
335
300
 
336
301
  const instance = createExampleMcpServer();
337
302
  const handler = getToolHandler(instance);
@@ -341,9 +306,7 @@ describe("createExampleMcpServer request handling", () => {
341
306
  method: "tools/call",
342
307
  params: {
343
308
  name: "projects.listProjects",
344
- arguments: {
345
- repoRoot: path.join(tempDir, "projects", "adl"),
346
- },
309
+ arguments: {},
347
310
  },
348
311
  },
349
312
  {},
@@ -355,24 +318,41 @@ describe("createExampleMcpServer request handling", () => {
355
318
  expect(payload.result).toContain("adl");
356
319
  expect(payload.result).toContain("beta");
357
320
  } finally {
321
+ process.chdir(originalCwd);
358
322
  await fs.rm(tempDir, { recursive: true, force: true });
359
323
  }
360
324
  });
361
325
 
362
- it("uses server default repoRoot when repoName is omitted", async () => {
363
- const tempDir = await fs.mkdtemp(
364
- path.join(os.tmpdir(), "f0-mcp-server-default-reporoot-"),
326
+ it("ignores legacy processRoot/repoRoot payload fields and uses Git workspace", async () => {
327
+ const originalCwd = process.cwd();
328
+ const workspaceA = await fs.mkdtemp(
329
+ path.join(os.tmpdir(), "f0-mcp-server-legacy-fields-a-"),
330
+ );
331
+ const workspaceB = await fs.mkdtemp(
332
+ path.join(os.tmpdir(), "f0-mcp-server-legacy-fields-b-"),
365
333
  );
366
334
  try {
367
- await fs.mkdir(path.join(tempDir, "api"), { recursive: true });
368
- await fs.mkdir(path.join(tempDir, "projects", "adl", "docs"), {
335
+ await fs.mkdir(path.join(workspaceA, "api"), { recursive: true });
336
+ await fs.mkdir(path.join(workspaceA, "projects", "adl", "docs"), {
369
337
  recursive: true,
370
338
  });
371
- await fs.mkdir(path.join(tempDir, "projects", "beta", "docs"), {
339
+ await fs.mkdir(path.join(workspaceA, ".git"), { recursive: true });
340
+ await fs.writeFile(
341
+ path.join(workspaceA, ".git", "config"),
342
+ [
343
+ '[remote "origin"]',
344
+ "\turl = https://example.com/F0/adl.git",
345
+ "",
346
+ ].join("\n"),
347
+ );
348
+
349
+ await fs.mkdir(path.join(workspaceB, "api"), { recursive: true });
350
+ await fs.mkdir(path.join(workspaceB, "projects", "beta", "docs"), {
372
351
  recursive: true,
373
352
  });
374
353
 
375
- const instance = createExampleMcpServer({ repoRoot: tempDir });
354
+ process.chdir(workspaceA);
355
+ const instance = createExampleMcpServer();
376
356
  const handler = getToolHandler(instance);
377
357
 
378
358
  const result = await handler(
@@ -380,7 +360,10 @@ describe("createExampleMcpServer request handling", () => {
380
360
  method: "tools/call",
381
361
  params: {
382
362
  name: "projects.listProjects",
383
- arguments: {},
363
+ arguments: {
364
+ processRoot: workspaceB,
365
+ repoRoot: workspaceB,
366
+ },
384
367
  },
385
368
  },
386
369
  {},
@@ -390,13 +373,16 @@ describe("createExampleMcpServer request handling", () => {
390
373
  const payload = JSON.parse(result.content?.[0]?.text ?? "{}");
391
374
  expect(payload.ok).toBe(true);
392
375
  expect(payload.result).toContain("adl");
393
- expect(payload.result).toContain("beta");
376
+ expect(payload.result).not.toContain("beta");
394
377
  } finally {
395
- await fs.rm(tempDir, { recursive: true, force: true });
378
+ process.chdir(originalCwd);
379
+ await fs.rm(workspaceA, { recursive: true, force: true });
380
+ await fs.rm(workspaceB, { recursive: true, force: true });
396
381
  }
397
382
  });
398
383
 
399
- it("exposes mcp.workspace to explain server filesystem context", async () => {
384
+ it("exposes mcp.workspace with Git workspace context", async () => {
385
+ const originalCwd = process.cwd();
400
386
  const tempDir = await fs.mkdtemp(
401
387
  path.join(os.tmpdir(), "f0-mcp-server-workspace-"),
402
388
  );
@@ -405,8 +391,18 @@ describe("createExampleMcpServer request handling", () => {
405
391
  await fs.mkdir(path.join(tempDir, "projects", "adl", "docs"), {
406
392
  recursive: true,
407
393
  });
394
+ await fs.mkdir(path.join(tempDir, ".git"), { recursive: true });
395
+ await fs.writeFile(
396
+ path.join(tempDir, ".git", "config"),
397
+ [
398
+ '[remote "origin"]',
399
+ "\turl = https://example.com/F0/adl.git",
400
+ "",
401
+ ].join("\n"),
402
+ );
408
403
 
409
- const instance = createExampleMcpServer({ repoRoot: tempDir });
404
+ process.chdir(tempDir);
405
+ const instance = createExampleMcpServer();
410
406
  const handler = getToolHandler(instance);
411
407
 
412
408
  const result = await handler(
@@ -423,16 +419,21 @@ describe("createExampleMcpServer request handling", () => {
423
419
  expect(result.isError).toBe(false);
424
420
  const payload = JSON.parse(result.content?.[0]?.text ?? "{}");
425
421
  expect(payload.ok).toBe(true);
426
- expect(payload.result.repoRoot).toBe(path.resolve(tempDir));
427
- expect(payload.result.defaultRepoRoot).toBe(path.resolve(tempDir));
422
+ expect(payload.result.workspaceRoot).toBe(path.resolve(tempDir));
423
+ expect(payload.result.processRoot).toBe(path.resolve(tempDir));
424
+ expect(payload.result.defaultRepoName).toBe("adl");
425
+ expect(payload.result.defaultRepoFullName).toBe("F0/adl");
426
+ expect(payload.result.availableRepoNames).toContain("adl");
427
+ expect(payload.result.availableRepoNames).toContain("F0/adl");
428
428
  expect(payload.result.hasProjectsDir).toBe(true);
429
429
  expect(Array.isArray(payload.result.projects)).toBe(true);
430
430
  } finally {
431
+ process.chdir(originalCwd);
431
432
  await fs.rm(tempDir, { recursive: true, force: true });
432
433
  }
433
434
  });
434
435
 
435
- it("auto-detects repo root/name from .git/config when no repoRoot/repoName are provided", async () => {
436
+ it("auto-detects repo identity from .git/config when no repoName is provided", async () => {
436
437
  const originalCwd = process.cwd();
437
438
  const tempDir = await fs.mkdtemp(
438
439
  path.join(os.tmpdir(), "f0-mcp-server-git-detect-"),
@@ -470,11 +471,13 @@ describe("createExampleMcpServer request handling", () => {
470
471
  expect(workspace.isError).toBe(false);
471
472
  const payload = JSON.parse(workspace.content?.[0]?.text ?? "{}");
472
473
  expect(payload.ok).toBe(true);
473
- expect(payload.result.defaultRepoRoot).toBe(path.resolve(tempDir));
474
- expect(payload.result.repoRoot).toBe(path.resolve(tempDir));
474
+ expect(payload.result.workspaceRoot).toBe(path.resolve(tempDir));
475
+ expect(payload.result.processRoot).toBe(path.resolve(tempDir));
475
476
  expect(payload.result.defaultRepoName).toBe("adl");
477
+ expect(payload.result.defaultRepoFullName).toBe("F0/adl");
476
478
  expect(payload.result.repoName).toBe("adl");
477
479
  expect(payload.result.availableRepoNames).toContain("adl");
480
+ expect(payload.result.availableRepoNames).toContain("F0/adl");
478
481
 
479
482
  const list = await handler(
480
483
  {
@@ -538,6 +541,8 @@ describe("createExampleMcpServer request handling", () => {
538
541
  const payload = JSON.parse(workspace.content?.[0]?.text ?? "{}");
539
542
  expect(payload.ok).toBe(true);
540
543
  expect(payload.result.defaultRepoName).toBe("adl");
544
+ expect(payload.result.defaultRepoFullName).toBe("F0/adl");
545
+ expect(payload.result.availableRepoNames).toContain("F0/adl");
541
546
  } finally {
542
547
  process.chdir(originalCwd);
543
548
  await fs.rm(tempDir, { recursive: true, force: true });