@floomhq/skills 0.2.1 → 0.2.3

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/dist/index.js CHANGED
@@ -2407,7 +2407,7 @@ function getApiBaseUrls(preferred) {
2407
2407
  }
2408
2408
 
2409
2409
  // src/version.ts
2410
- var VERSION = "0.2.1";
2410
+ var VERSION = "0.2.3";
2411
2411
 
2412
2412
  // src/api-client.ts
2413
2413
  var DEFAULT_TIMEOUT_MS = 2e4;
@@ -2598,6 +2598,14 @@ async function whoamiCommand() {
2598
2598
  log.info("Not logged in. Run: floom login");
2599
2599
  return;
2600
2600
  }
2601
+ try {
2602
+ await api("/me", { authRequired: true });
2603
+ } catch (e) {
2604
+ log.err(`Stored login is not accepted by the Floom API: ${e.message}`);
2605
+ log.info("Run: floom login");
2606
+ process.exitCode = 1;
2607
+ return;
2608
+ }
2601
2609
  log.heading("Logged in as:");
2602
2610
  log.kv("handle", `@${auth.handle}`);
2603
2611
  log.kv("email", auth.email);
@@ -2920,7 +2928,11 @@ async function readLock(projectDir) {
2920
2928
  try {
2921
2929
  const raw = await readFile7(join7(projectDir, "floom.lock"), "utf8");
2922
2930
  const parsed = JSON.parse(raw);
2923
- return parsed.schema_version === "0.1" ? parsed : { ...EMPTY };
2931
+ if (parsed.schema_version === "0.1") return parsed;
2932
+ const version = typeof parsed.schema_version === "string" ? parsed.schema_version : "missing";
2933
+ throw new Error(
2934
+ `LOCK_SCHEMA_UNSUPPORTED: floom.lock schema_version ${version} is not supported by this CLI. The existing floom.lock was left unchanged; migrate it or reinstall skills with the current CLI.`
2935
+ );
2924
2936
  } catch (e) {
2925
2937
  if (e.code === "ENOENT") return { ...EMPTY };
2926
2938
  throw e;
@@ -3349,7 +3361,7 @@ async function libraryLeaveCommand(librarySlug) {
3349
3361
  // src/commands/mcp.ts
3350
3362
  import { mkdtemp, mkdir as mkdir6, readdir as readdir4, readFile as readFile8, rename as rename3, rm as rm3, writeFile as writeFile4 } from "node:fs/promises";
3351
3363
  import { join as join10 } from "node:path";
3352
- import { homedir as homedir3, tmpdir as tmpdir3 } from "node:os";
3364
+ import { tmpdir as tmpdir3 } from "node:os";
3353
3365
  import { z as z2 } from "zod";
3354
3366
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3355
3367
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
@@ -3371,11 +3383,7 @@ async function resolveToken() {
3371
3383
  if (fromEnv) return fromEnv;
3372
3384
  const auth = await readAuth();
3373
3385
  if (auth?.token) return auth.token;
3374
- const authPath = join10(homedir3(), ".floom", "auth.json");
3375
- const raw = await readFile8(authPath, "utf8");
3376
- const parsed = JSON.parse(raw);
3377
- if (!parsed.token) throw new Error("Missing token in ~/.floom/auth.json");
3378
- return parsed.token;
3386
+ throw new Error("Authentication required. Run `floom login` or set FLOOM_API_TOKEN.");
3379
3387
  }
3380
3388
  async function apiWithToken(token, path, query) {
3381
3389
  const auth = await readAuth();
@@ -3466,14 +3474,15 @@ async function parseSkillBundle(bundle) {
3466
3474
  }
3467
3475
  }
3468
3476
  async function mcpCommand() {
3469
- const token = await resolveToken();
3470
3477
  const server = new McpServer({ name: "floom", version: VERSION });
3471
3478
  server.tool("search_skills", { query: z2.string().min(1), workspace: z2.string().optional(), library: z2.string().optional() }, async ({ query, workspace, library }) => {
3479
+ const token = await resolveToken();
3472
3480
  const workspaceSlug = workspace ?? library;
3473
3481
  const result = await apiWithToken(token, "/skills", { q: query, ...workspaceSlug ? { library: workspaceSlug } : {} });
3474
3482
  return { content: [{ type: "text", text: JSON.stringify(result) }] };
3475
3483
  });
3476
3484
  server.tool("get_skill", { ref: z2.string().min(3) }, async ({ ref }) => {
3485
+ const token = await resolveToken();
3477
3486
  const parsed = parseSkillRef(ref);
3478
3487
  if (!parsed) throw new Error("Invalid ref. Expected @owner/slug or workspace/slug");
3479
3488
  const meta = await apiWithToken(token, `/skills/${parsed.owner}/${parsed.slug}`);
@@ -3483,12 +3492,14 @@ async function mcpCommand() {
3483
3492
  return { content: [{ type: "text", text: JSON.stringify({ ref, meta, ...parsedBundle }) }] };
3484
3493
  });
3485
3494
  async function listWorkspaces() {
3495
+ const token = await resolveToken();
3486
3496
  const result = await apiWithToken(token, "/libraries");
3487
3497
  return { content: [{ type: "text", text: JSON.stringify(result) }] };
3488
3498
  }
3489
3499
  server.tool("list_workspaces", {}, listWorkspaces);
3490
3500
  server.tool("list_libraries", {}, listWorkspaces);
3491
3501
  server.tool("install_skill", { ref: z2.string().min(3), target: z2.enum(["claude", "codex", "cursor", "kimi", "opencode", "gemini"]) }, async ({ ref, target }) => {
3502
+ const token = await resolveToken();
3492
3503
  const installed = await installViaApi(token, ref, target);
3493
3504
  return { content: [{ type: "text", text: JSON.stringify(installed) }] };
3494
3505
  });
@@ -3508,6 +3519,9 @@ function textOf(result) {
3508
3519
  function pass(name, detail) {
3509
3520
  return { name, ok: true, detail };
3510
3521
  }
3522
+ function warn(name, detail) {
3523
+ return { name, ok: true, detail, status: "warn" };
3524
+ }
3511
3525
  function fail(name, detail) {
3512
3526
  return { name, ok: false, detail };
3513
3527
  }
@@ -3523,9 +3537,7 @@ async function doctorCommand(opts = {}) {
3523
3537
  const auth = await readAuth();
3524
3538
  const token = process.env.FLOOM_API_TOKEN?.trim() || auth?.token;
3525
3539
  if (!token) {
3526
- checks.push(fail("fresh_agent_token", "missing FLOOM_API_TOKEN and ~/.floom/auth.json"));
3527
- emitDoctor(checks, opts.json);
3528
- process.exit(1);
3540
+ checks.push(warn("fresh_agent_auth", "missing token; API-backed tool calls skipped"));
3529
3541
  }
3530
3542
  const cliPath = process.argv[1];
3531
3543
  if (!cliPath) {
@@ -3543,8 +3555,8 @@ async function doctorCommand(opts = {}) {
3543
3555
  env: {
3544
3556
  ...process.env,
3545
3557
  HOME: tmpHome,
3546
- FLOOM_API_TOKEN: token,
3547
3558
  FLOOM_SKILLS_DIR: tmpSkills,
3559
+ ...token ? { FLOOM_API_TOKEN: token } : {},
3548
3560
  ...auth?.apiUrl ? { FLOOM_API_URL: auth.apiUrl } : {}
3549
3561
  },
3550
3562
  stderr: "pipe"
@@ -3557,6 +3569,11 @@ async function doctorCommand(opts = {}) {
3557
3569
  const expected = ["get_skill", "install_skill", "list_workspaces", "search_skills"];
3558
3570
  const missing = expected.filter((name) => !toolNames.includes(name));
3559
3571
  checks.push(missing.length === 0 ? pass("mcp_tools", toolNames.join(", ")) : fail("mcp_tools", `missing ${missing.join(", ")}`));
3572
+ if (!token) {
3573
+ checks.push(warn("mcp_api_calls", "skipped because no FLOOM_API_TOKEN or ~/.floom/auth.json was available"));
3574
+ emitDoctor(checks, opts.json);
3575
+ return;
3576
+ }
3560
3577
  const workspaces = await client.callTool({ name: "list_workspaces", arguments: {} });
3561
3578
  checks.push(textOf(workspaces).length > 0 ? pass("mcp_list_workspaces", `${textOf(workspaces).length} chars`) : fail("mcp_list_workspaces", "empty response"));
3562
3579
  const search = await client.callTool({ name: "search_skills", arguments: { query: opts.query ?? "pdf" } });
@@ -3588,7 +3605,8 @@ function emitDoctor(checks, json) {
3588
3605
  log.heading("Floom doctor");
3589
3606
  for (const check of checks) {
3590
3607
  const detail = check.detail ? ` ${check.detail}` : "";
3591
- if (check.ok) log.ok(`${check.name}${detail}`);
3608
+ if (check.status === "warn") log.warn(`${check.name}${detail}`);
3609
+ else if (check.ok) log.ok(`${check.name}${detail}`);
3592
3610
  else log.err(`${check.name}${detail}`);
3593
3611
  }
3594
3612
  }