@floomhq/floom 3.0.0 → 3.0.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/dist/index.js +157 -43
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3040,6 +3040,21 @@ function emitJson(value) {
|
|
|
3040
3040
|
writeStdout(JSON.stringify(value, null, 2) + "\n");
|
|
3041
3041
|
}
|
|
3042
3042
|
|
|
3043
|
+
// src/lib/pipe-guard.ts
|
|
3044
|
+
var installed = false;
|
|
3045
|
+
function installPipeGuards() {
|
|
3046
|
+
if (installed) return;
|
|
3047
|
+
installed = true;
|
|
3048
|
+
const swallow = (err) => {
|
|
3049
|
+
if (err && err.code === "EPIPE") {
|
|
3050
|
+
process.exit(0);
|
|
3051
|
+
}
|
|
3052
|
+
throw err;
|
|
3053
|
+
};
|
|
3054
|
+
process.stdout.on("error", swallow);
|
|
3055
|
+
process.stderr.on("error", swallow);
|
|
3056
|
+
}
|
|
3057
|
+
|
|
3043
3058
|
// src/commands/login.ts
|
|
3044
3059
|
init_src();
|
|
3045
3060
|
import { spawn as spawn2 } from "node:child_process";
|
|
@@ -3238,7 +3253,7 @@ async function getMachineIdentity() {
|
|
|
3238
3253
|
}
|
|
3239
3254
|
|
|
3240
3255
|
// src/version.ts
|
|
3241
|
-
var VERSION = "3.0.
|
|
3256
|
+
var VERSION = "3.0.1";
|
|
3242
3257
|
|
|
3243
3258
|
// src/api-client.ts
|
|
3244
3259
|
var DEFAULT_TIMEOUT_MS = 2e4;
|
|
@@ -3289,12 +3304,12 @@ var HELP = "https://floom.dev/docs#troubleshooting";
|
|
|
3289
3304
|
function friendlyApiErrorMessage(code, raw, status) {
|
|
3290
3305
|
if (code === "AUTH_REQUIRED" || code === "TOKEN_INVALID" || status === 401) {
|
|
3291
3306
|
return `Not signed in.
|
|
3292
|
-
Run:
|
|
3307
|
+
Run: floom login
|
|
3293
3308
|
More help: ${HELP}`;
|
|
3294
3309
|
}
|
|
3295
3310
|
if (code === "TOKEN_EXPIRED") {
|
|
3296
3311
|
return `Your session has expired.
|
|
3297
|
-
Run:
|
|
3312
|
+
Run: floom login
|
|
3298
3313
|
More help: ${HELP}`;
|
|
3299
3314
|
}
|
|
3300
3315
|
if (code === "SKILL_ACCESS_DENIED" || status === 403) {
|
|
@@ -3303,7 +3318,7 @@ function friendlyApiErrorMessage(code, raw, status) {
|
|
|
3303
3318
|
}
|
|
3304
3319
|
if (code === "SKILL_NOT_FOUND" || status === 404) {
|
|
3305
3320
|
return `Skill not found. It may have been deleted or the slug is wrong.
|
|
3306
|
-
Run:
|
|
3321
|
+
Run: floom list
|
|
3307
3322
|
More help: ${HELP}`;
|
|
3308
3323
|
}
|
|
3309
3324
|
if (code === "RATE_LIMITED" || status === 429) {
|
|
@@ -3312,7 +3327,7 @@ function friendlyApiErrorMessage(code, raw, status) {
|
|
|
3312
3327
|
}
|
|
3313
3328
|
if (code === "CLI_VERSION_TOO_OLD") {
|
|
3314
3329
|
return `Your CLI is out of date.
|
|
3315
|
-
Run:
|
|
3330
|
+
Run: npm i -g @floomhq/floom@latest
|
|
3316
3331
|
More help: ${HELP}`;
|
|
3317
3332
|
}
|
|
3318
3333
|
if (code === "FILE_TOO_LARGE" || code === "BUNDLE_TOO_LARGE") {
|
|
@@ -3331,7 +3346,7 @@ async function api(path, opts = {}) {
|
|
|
3331
3346
|
if (opts.authRequired && !token) {
|
|
3332
3347
|
throw new FloomError(
|
|
3333
3348
|
"AUTH_REQUIRED",
|
|
3334
|
-
"Not signed in.\n Run:
|
|
3349
|
+
"Not signed in.\n Run: floom login\n More help: https://floom.dev/docs#troubleshooting"
|
|
3335
3350
|
);
|
|
3336
3351
|
}
|
|
3337
3352
|
let lastError = null;
|
|
@@ -4013,13 +4028,13 @@ async function writeSkillAtomically(root, skill) {
|
|
|
4013
4028
|
await rm(tempDir, { recursive: true, force: true });
|
|
4014
4029
|
await rm(replacedDir, { recursive: true, force: true });
|
|
4015
4030
|
await mkdir4(tempDir, { recursive: true });
|
|
4016
|
-
const
|
|
4031
|
+
const installed2 = [];
|
|
4017
4032
|
for (const file of skill.files) {
|
|
4018
4033
|
const safePath = safeRemotePath(file.path);
|
|
4019
4034
|
const dest = join7(tempDir, ...safePath.split("/"));
|
|
4020
4035
|
await mkdir4(dirname(dest), { recursive: true });
|
|
4021
4036
|
await writeFile3(dest, bytesForFile(file));
|
|
4022
|
-
|
|
4037
|
+
installed2.push([slug, ...safePath.split("/")].join(sep4));
|
|
4023
4038
|
}
|
|
4024
4039
|
let movedExisting = false;
|
|
4025
4040
|
try {
|
|
@@ -4053,7 +4068,7 @@ async function writeSkillAtomically(root, skill) {
|
|
|
4053
4068
|
await rm(tempDir, { recursive: true, force: true });
|
|
4054
4069
|
if (!movedExisting) await rm(replacedDir, { recursive: true, force: true });
|
|
4055
4070
|
}
|
|
4056
|
-
return
|
|
4071
|
+
return installed2.sort();
|
|
4057
4072
|
}
|
|
4058
4073
|
async function backupSkill(root, stamp, slug) {
|
|
4059
4074
|
slug = safeSkillSlug(slug);
|
|
@@ -4686,14 +4701,19 @@ async function pushCommand(pathArg, options = {}, deps = {}) {
|
|
|
4686
4701
|
wouldMutate: false,
|
|
4687
4702
|
syncedCount: 0,
|
|
4688
4703
|
plan: [],
|
|
4704
|
+
error: { code: "AUTH_REQUIRED", message: "Not signed in \u2014 log in first to publish." },
|
|
4689
4705
|
next: ["floom login"]
|
|
4690
4706
|
});
|
|
4707
|
+
process.exitCode = 1;
|
|
4691
4708
|
return;
|
|
4692
4709
|
}
|
|
4693
4710
|
log.blank();
|
|
4711
|
+
log.err("Not signed in \u2014 log in first to publish.");
|
|
4712
|
+
log.blank();
|
|
4694
4713
|
if (skills.length === 0) {
|
|
4695
4714
|
log.info("No local skill folders found on this machine.");
|
|
4696
4715
|
printNext([{ command: "floom login" }]);
|
|
4716
|
+
process.exitCode = 1;
|
|
4697
4717
|
return;
|
|
4698
4718
|
}
|
|
4699
4719
|
log.info(` Found ${skills.length} skill${skills.length === 1 ? "" : "s"} on this machine:`);
|
|
@@ -4704,6 +4724,7 @@ async function pushCommand(pathArg, options = {}, deps = {}) {
|
|
|
4704
4724
|
log.blank();
|
|
4705
4725
|
log.info("After login, run:");
|
|
4706
4726
|
log.command("floom push");
|
|
4727
|
+
process.exitCode = 1;
|
|
4707
4728
|
return;
|
|
4708
4729
|
}
|
|
4709
4730
|
const me = await fetchMe().catch(() => null);
|
|
@@ -4894,13 +4915,23 @@ async function pushExplicitPath(pathArg, options, pushApi, authed) {
|
|
|
4894
4915
|
}
|
|
4895
4916
|
if (!authed) {
|
|
4896
4917
|
if (json) {
|
|
4897
|
-
emitJson({
|
|
4918
|
+
emitJson({
|
|
4919
|
+
workspace: { name: "Library", signedIn: false },
|
|
4920
|
+
mode: "plan",
|
|
4921
|
+
applied: false,
|
|
4922
|
+
wouldMutate: false,
|
|
4923
|
+
syncedCount: 0,
|
|
4924
|
+
plan: [],
|
|
4925
|
+
error: { code: "AUTH_REQUIRED", message: "Not signed in \u2014 log in first to publish." },
|
|
4926
|
+
next: ["floom login"]
|
|
4927
|
+
});
|
|
4898
4928
|
} else {
|
|
4899
4929
|
log.blank();
|
|
4930
|
+
log.err("Not signed in \u2014 log in first to publish.");
|
|
4900
4931
|
log.info(`Found local skill at ${tildePath(folder)}.`);
|
|
4901
|
-
log.info("To publish it, sign in to Floom.");
|
|
4902
4932
|
printNext([{ command: "floom login" }]);
|
|
4903
4933
|
}
|
|
4934
|
+
process.exitCode = 1;
|
|
4904
4935
|
return;
|
|
4905
4936
|
}
|
|
4906
4937
|
const slug = basename(folder);
|
|
@@ -5177,10 +5208,42 @@ async function deleteCommand(slug, opts = {}, deps = { api }) {
|
|
|
5177
5208
|
}
|
|
5178
5209
|
|
|
5179
5210
|
// src/commands/list.ts
|
|
5211
|
+
init_src();
|
|
5180
5212
|
var MAX_HUMAN_ROWS = 20;
|
|
5181
5213
|
async function listCommand(opts = {}) {
|
|
5182
5214
|
const json = Boolean(opts.json);
|
|
5183
|
-
|
|
5215
|
+
if (!await readAuth()) {
|
|
5216
|
+
if (json) {
|
|
5217
|
+
emitJson({
|
|
5218
|
+
workspace: { name: "Library", signedIn: false },
|
|
5219
|
+
error: { code: "AUTH_REQUIRED", message: "Not signed in." },
|
|
5220
|
+
skills: [],
|
|
5221
|
+
next: ["floom login"]
|
|
5222
|
+
});
|
|
5223
|
+
} else {
|
|
5224
|
+
log.err("Not signed in.");
|
|
5225
|
+
printNext([{ command: "floom login" }]);
|
|
5226
|
+
}
|
|
5227
|
+
process.exitCode = 1;
|
|
5228
|
+
return;
|
|
5229
|
+
}
|
|
5230
|
+
let result;
|
|
5231
|
+
try {
|
|
5232
|
+
result = await api("/skills", { authRequired: true });
|
|
5233
|
+
} catch (e) {
|
|
5234
|
+
if (json) {
|
|
5235
|
+
const fe = e instanceof FloomError ? e : null;
|
|
5236
|
+
emitJson({
|
|
5237
|
+
workspace: { name: "Library", signedIn: true },
|
|
5238
|
+
error: { code: fe?.code ?? "INTERNAL_ERROR", message: e.message },
|
|
5239
|
+
skills: [],
|
|
5240
|
+
next: ["floom login"]
|
|
5241
|
+
});
|
|
5242
|
+
process.exitCode = 1;
|
|
5243
|
+
return;
|
|
5244
|
+
}
|
|
5245
|
+
throw e;
|
|
5246
|
+
}
|
|
5184
5247
|
const localSkills = await discoverSkills();
|
|
5185
5248
|
const installedBySlug = /* @__PURE__ */ new Map();
|
|
5186
5249
|
for (const skill of localSkills) {
|
|
@@ -6642,19 +6705,19 @@ async function installCommand(input, rawOpts = {}) {
|
|
|
6642
6705
|
}
|
|
6643
6706
|
if (planMode) {
|
|
6644
6707
|
const planAgents = await Promise.all(targets.map(async (t) => {
|
|
6645
|
-
const
|
|
6708
|
+
const installed2 = await existsAt(t.skillsDir, slug);
|
|
6646
6709
|
return {
|
|
6647
6710
|
name: t.agent,
|
|
6648
6711
|
scope: t.scope,
|
|
6649
6712
|
skillsDir: t.skillsDir,
|
|
6650
6713
|
path: join12(t.skillsDir, slug),
|
|
6651
|
-
action:
|
|
6714
|
+
action: installed2 ? "update" : "install",
|
|
6652
6715
|
status: "ok",
|
|
6653
6716
|
refusedReason: null,
|
|
6654
6717
|
fromVersion: null,
|
|
6655
6718
|
toVersion: shareData.skill.version.version_seq,
|
|
6656
6719
|
backupPath: null,
|
|
6657
|
-
message:
|
|
6720
|
+
message: installed2 ? `${shareData.skill.title} would be updated` : `${shareData.skill.title} would be installed`
|
|
6658
6721
|
};
|
|
6659
6722
|
}));
|
|
6660
6723
|
if (json) {
|
|
@@ -7048,8 +7111,8 @@ function npmLatest() {
|
|
|
7048
7111
|
}, 5e3);
|
|
7049
7112
|
});
|
|
7050
7113
|
}
|
|
7051
|
-
function significantlyOutdated(
|
|
7052
|
-
const a =
|
|
7114
|
+
function significantlyOutdated(installed2, latest) {
|
|
7115
|
+
const a = installed2.split(".").map((n) => Number.parseInt(n, 10));
|
|
7053
7116
|
const b = latest.split(".").map((n) => Number.parseInt(n, 10));
|
|
7054
7117
|
if ((b[0] ?? 0) > (a[0] ?? 0)) return true;
|
|
7055
7118
|
if ((b[0] ?? 0) === (a[0] ?? 0) && (b[1] ?? 0) > (a[1] ?? 0)) return true;
|
|
@@ -7922,61 +7985,108 @@ function aliasNotice(oldName, newName) {
|
|
|
7922
7985
|
log.notice(`"${oldName}" is now "${newName}" \u2014 both work for now.`);
|
|
7923
7986
|
}
|
|
7924
7987
|
var program = new Command();
|
|
7925
|
-
program.name("floom").description("Floom: one shared skill library, synced across every AI agent.").version(VERSION).addHelpCommand(false).helpOption(
|
|
7926
|
-
program.
|
|
7927
|
-
|
|
7928
|
-
|
|
7929
|
-
|
|
7988
|
+
program.name("floom").description("Floom: one shared skill library, synced across every AI agent.").version(VERSION).addHelpCommand(false).helpOption("-h, --help", "show grouped help").allowExcessArguments(true).showSuggestionAfterError(true).showHelpAfterError(false);
|
|
7989
|
+
program.helpInformation = function helpInformation() {
|
|
7990
|
+
printGroupedHelp();
|
|
7991
|
+
return "";
|
|
7992
|
+
};
|
|
7993
|
+
function helpOpt(cmd) {
|
|
7994
|
+
return cmd.helpOption("-h, --help", "show help for this command").allowExcessArguments(false);
|
|
7995
|
+
}
|
|
7996
|
+
program.action(async (_opts, cmd) => {
|
|
7997
|
+
const stray = cmd.args.filter((a) => !a.startsWith("-"));
|
|
7998
|
+
if (stray.length > 0) {
|
|
7999
|
+
const unknown = stray[0];
|
|
8000
|
+
log.err(`unknown command: ${unknown}`);
|
|
8001
|
+
const knownNames = program.commands.map((c) => c.name());
|
|
8002
|
+
const suggestion = closestCommand(unknown, knownNames);
|
|
8003
|
+
if (suggestion) log.info(`Did you mean: floom ${suggestion}?`);
|
|
8004
|
+
log.info("Run: floom --help");
|
|
8005
|
+
process.exitCode = 2;
|
|
7930
8006
|
return;
|
|
7931
8007
|
}
|
|
7932
8008
|
await dashboardCommand();
|
|
7933
8009
|
});
|
|
7934
|
-
|
|
7935
|
-
|
|
7936
|
-
|
|
7937
|
-
|
|
8010
|
+
function closestCommand(input, candidates) {
|
|
8011
|
+
let best = null;
|
|
8012
|
+
let bestDist = Infinity;
|
|
8013
|
+
for (const c of candidates) {
|
|
8014
|
+
const d = editDistance(input, c);
|
|
8015
|
+
if (d < bestDist) {
|
|
8016
|
+
bestDist = d;
|
|
8017
|
+
best = c;
|
|
8018
|
+
}
|
|
8019
|
+
}
|
|
8020
|
+
const threshold = Math.max(2, Math.floor(input.length * 0.4));
|
|
8021
|
+
return best && bestDist <= threshold ? best : null;
|
|
8022
|
+
}
|
|
8023
|
+
function editDistance(a, b) {
|
|
8024
|
+
if (a === b) return 0;
|
|
8025
|
+
if (a.length === 0) return b.length;
|
|
8026
|
+
if (b.length === 0) return a.length;
|
|
8027
|
+
const prev = Array(b.length + 1).fill(0).map((_, i) => i);
|
|
8028
|
+
const curr = Array(b.length + 1).fill(0);
|
|
8029
|
+
for (let i = 1; i <= a.length; i += 1) {
|
|
8030
|
+
curr[0] = i;
|
|
8031
|
+
for (let j = 1; j <= b.length; j += 1) {
|
|
8032
|
+
const cost = a.charCodeAt(i - 1) === b.charCodeAt(j - 1) ? 0 : 1;
|
|
8033
|
+
curr[j] = Math.min(curr[j - 1] + 1, prev[j] + 1, prev[j - 1] + cost);
|
|
8034
|
+
}
|
|
8035
|
+
for (let j = 0; j <= b.length; j += 1) prev[j] = curr[j];
|
|
8036
|
+
}
|
|
8037
|
+
return curr[b.length];
|
|
8038
|
+
}
|
|
8039
|
+
helpOpt(program.command("login").description("sign in to your Floom workspace")).action(loginCommand);
|
|
8040
|
+
helpOpt(program.command("logout").description("sign out on this machine")).action(logoutCommand);
|
|
8041
|
+
helpOpt(program.command("account").description("show account details").option("--json", "print account state as JSON")).action((opts) => accountCommand(opts));
|
|
8042
|
+
helpOpt(program.command("whoami").description("show account details (alias)").option("--json", "print account state as JSON")).action((opts) => {
|
|
7938
8043
|
aliasNotice("whoami", "account");
|
|
7939
8044
|
return accountCommand(opts);
|
|
7940
8045
|
});
|
|
7941
|
-
program.command("push [path]").description("publish local skills to the Library").option("--yes", "proceed with all safe changes").option("--json", "print the plan or result as JSON").option("--dry-run", "print the plan without changing anything").option("--exit-zero", "with --dry-run, force exit 0 even if the plan is non-empty").option("--no-secret-check", "skip the pre-publish secret scan").option("--concurrency <n>", "bulk push concurrency, 1-16", "6").action((path, opts) => pushCommand(path, opts));
|
|
7942
|
-
program.command("pull [skill]").description("update local agents from the Library").option("--agent <name>", "limit to one or more agents (repeatable, comma-separated)", collectAgent, []).option("--target <name>", "alias for --agent", collectAgent, []).option("--all-agents", "use every detected agent").option("--global", "use the user-level agent skills folder").option("--project", "use this project's agent skills folder").option("--all-scopes", "use both global and project-local copies").option("--yes", "skip confirmation for safe mutations").option("--json", "print the plan or result as JSON").option("--dry-run", "print the plan without changing anything").option("--exit-zero", "with --dry-run, force exit 0 even if the plan is non-empty").action((skill, opts) => {
|
|
8046
|
+
helpOpt(program.command("push [path]").description("publish local skills to the Library").option("--yes", "proceed with all safe changes").option("--json", "print the plan or result as JSON").option("--dry-run", "print the plan without changing anything").option("--exit-zero", "with --dry-run, force exit 0 even if the plan is non-empty").option("--no-secret-check", "skip the pre-publish secret scan").option("--concurrency <n>", "bulk push concurrency, 1-16", "6")).action((path, opts) => pushCommand(path, opts));
|
|
8047
|
+
helpOpt(program.command("pull [skill]").description("update local agents from the Library").option("--agent <name>", "limit to one or more agents (repeatable, comma-separated)", collectAgent, []).option("--target <name>", "alias for --agent", collectAgent, []).option("--all-agents", "use every detected agent").option("--global", "use the user-level agent skills folder").option("--global-only", "(deprecated) alias for --global").option("--project", "use this project's agent skills folder").option("--all-scopes", "use both global and project-local copies").option("--yes", "skip confirmation for safe mutations").option("--json", "print the plan or result as JSON").option("--dry-run", "print the plan without changing anything").option("--exit-zero", "with --dry-run, force exit 0 even if the plan is non-empty")).action((skill, opts) => {
|
|
7943
8048
|
if (Array.isArray(opts.target) && opts.target.length > 0) aliasNotice("--target", "--agent");
|
|
8049
|
+
if (opts.globalOnly) {
|
|
8050
|
+
aliasNotice("--global-only", "--global");
|
|
8051
|
+
opts.global = true;
|
|
8052
|
+
}
|
|
7944
8053
|
return pullCommand(skill, opts);
|
|
7945
8054
|
});
|
|
7946
|
-
program.command("sync").description("review both directions and resolve differences").option("--agent <name>", "limit to one or more agents (repeatable, comma-separated)", collectAgent, []).option("--target <name>", "alias for --agent", collectAgent, []).option("--all-agents", "use every detected agent").option("--global", "use the user-level agent skills folder").option("--project", "use this project's agent skills folder").option("--all-scopes", "use both global and project-local copies").option("--yes", "skip confirmation for safe mutations").option("--json", "print the plan or result as JSON").option("--dry-run", "print the plan without changing anything").option("--exit-zero", "with --dry-run, force exit 0 even if the plan is non-empty").action((opts) => {
|
|
8055
|
+
helpOpt(program.command("sync").description("review both directions and resolve differences").option("--agent <name>", "limit to one or more agents (repeatable, comma-separated)", collectAgent, []).option("--target <name>", "alias for --agent", collectAgent, []).option("--all-agents", "use every detected agent").option("--global", "use the user-level agent skills folder").option("--project", "use this project's agent skills folder").option("--all-scopes", "use both global and project-local copies").option("--yes", "skip confirmation for safe mutations").option("--json", "print the plan or result as JSON").option("--dry-run", "print the plan without changing anything").option("--exit-zero", "with --dry-run, force exit 0 even if the plan is non-empty")).action((opts) => {
|
|
7947
8056
|
if (Array.isArray(opts.target) && opts.target.length > 0) aliasNotice("--target", "--agent");
|
|
7948
8057
|
return syncCommand(opts);
|
|
7949
8058
|
});
|
|
7950
|
-
program.command("new <name>").description("create a ready-to-edit skill").option("--agent <name>", "create in this agent's skills folder").option("--all-scopes", "(rejected \u2014 new creates one skill in one location)").option("--yes", "skip confirmation when non-interactive").option("--json", "print the plan or result as JSON").option("--dry-run", "print the plan without creating anything").option("--exit-zero", "with --dry-run, force exit 0 even if the plan is non-empty").action((name, opts) => newCommand(name, opts));
|
|
7951
|
-
program.command("restore [skill]").description("restore a local skill from a safety backup").option("--agent <name>", "the agent whose copy to restore").option("--backup <timestamp>", "restore a specific backup snapshot").option("--list", "list all available backups").option("--yes", "skip confirmation").option("--json", "print the plan or result as JSON").option("--dry-run", "print the plan without restoring anything").option("--exit-zero", "with --dry-run, force exit 0 even if the plan is non-empty").action((skill, opts) => restoreCommand(skill, opts));
|
|
7952
|
-
program.command("list").description("list team skills and local install status").option("--json", "print the Library as JSON").action((opts) => listCommand(opts));
|
|
7953
|
-
program.command("library").description("list team skills and local install status (alias)").option("--json", "print the Library as JSON").action((opts) => listCommand(opts));
|
|
7954
|
-
program.command("delete [skill]").description("delete a skill from the Library").option("--yes", "skip the type-the-name confirmation").option("--json", "print the plan or result as JSON").option("--dry-run", "print the plan without deleting anything").option("--exit-zero", "with --dry-run, force exit 0 even if the plan is non-empty").action((skill, opts) => deleteCommand(skill, opts));
|
|
7955
|
-
program.command("remove [skill]").description("delete a skill from the Library (alias)").option("--yes", "skip the type-the-name confirmation").option("--json", "print the plan or result as JSON").option("--dry-run", "print the plan without deleting anything").option("--exit-zero", "with --dry-run, force exit 0 even if the plan is non-empty").action((skill, opts) => {
|
|
8059
|
+
helpOpt(program.command("new <name>").description("create a ready-to-edit skill").option("--agent <name>", "create in this agent's skills folder").option("--all-scopes", "(rejected \u2014 new creates one skill in one location)").option("--yes", "skip confirmation when non-interactive").option("--json", "print the plan or result as JSON").option("--dry-run", "print the plan without creating anything").option("--exit-zero", "with --dry-run, force exit 0 even if the plan is non-empty")).action((name, opts) => newCommand(name, opts));
|
|
8060
|
+
helpOpt(program.command("restore [skill]").description("restore a local skill from a safety backup").option("--agent <name>", "the agent whose copy to restore").option("--backup <timestamp>", "restore a specific backup snapshot").option("--list", "list all available backups").option("--yes", "skip confirmation").option("--json", "print the plan or result as JSON").option("--dry-run", "print the plan without restoring anything").option("--exit-zero", "with --dry-run, force exit 0 even if the plan is non-empty")).action((skill, opts) => restoreCommand(skill, opts));
|
|
8061
|
+
helpOpt(program.command("list").description("list team skills and local install status").option("--json", "print the Library as JSON")).action((opts) => listCommand(opts));
|
|
8062
|
+
helpOpt(program.command("library").description("list team skills and local install status (alias)").option("--json", "print the Library as JSON")).action((opts) => listCommand(opts));
|
|
8063
|
+
helpOpt(program.command("delete [skill]").description("delete a skill from the Library").option("--yes", "skip the type-the-name confirmation").option("--json", "print the plan or result as JSON").option("--dry-run", "print the plan without deleting anything").option("--exit-zero", "with --dry-run, force exit 0 even if the plan is non-empty")).action((skill, opts) => deleteCommand(skill, opts));
|
|
8064
|
+
helpOpt(program.command("remove [skill]").description("delete a skill from the Library (alias)").option("--yes", "skip the type-the-name confirmation").option("--json", "print the plan or result as JSON").option("--dry-run", "print the plan without deleting anything").option("--exit-zero", "with --dry-run, force exit 0 even if the plan is non-empty")).action((skill, opts) => {
|
|
7956
8065
|
aliasNotice("remove", "delete");
|
|
7957
8066
|
return deleteCommand(skill, opts);
|
|
7958
8067
|
});
|
|
7959
|
-
program.command("install <link>").description("install a skill from a share link").option("--agent <name>", "install into one or more agents (repeatable, comma-separated)", collectAgent, []).option("--target <name>", "alias for --agent", collectAgent, []).option("--all-agents", "install into every detected agent").option("--global", "install into the user-level agent skills folder").option("--project", "install into this project's agent skills folder").option("--all-scopes", "install into both global and project-local copies").option("--yes", "skip confirmation for safe mutations").option("--json", "print the plan or result as JSON").option("--dry-run", "print the plan without installing anything").option("--exit-zero", "with --dry-run, force exit 0 even if the plan is non-empty").action((link, opts) => installCommand(link, opts));
|
|
7960
|
-
program.command("add <link>").description("install a skill from a share link (alias)").option("--agent <name>", "install into one or more agents", collectAgent, []).option("--target <name>", "alias for --agent", collectAgent, []).option("--all-agents", "install into every detected agent").option("--global", "install into the user-level agent skills folder").option("--project", "install into this project's agent skills folder").option("--all-scopes", "install into both global and project-local copies").option("--yes", "skip confirmation for safe mutations").option("--json", "print the plan or result as JSON").option("--dry-run", "print the plan without installing anything").action((link, opts) => {
|
|
8068
|
+
helpOpt(program.command("install <link>").description("install a skill from a share link").option("--agent <name>", "install into one or more agents (repeatable, comma-separated)", collectAgent, []).option("--target <name>", "alias for --agent", collectAgent, []).option("--all-agents", "install into every detected agent").option("--global", "install into the user-level agent skills folder").option("--project", "install into this project's agent skills folder").option("--all-scopes", "install into both global and project-local copies").option("--yes", "skip confirmation for safe mutations").option("--json", "print the plan or result as JSON").option("--dry-run", "print the plan without installing anything").option("--exit-zero", "with --dry-run, force exit 0 even if the plan is non-empty")).action((link, opts) => installCommand(link, opts));
|
|
8069
|
+
helpOpt(program.command("add <link>").description("install a skill from a share link (alias)").option("--agent <name>", "install into one or more agents", collectAgent, []).option("--target <name>", "alias for --agent", collectAgent, []).option("--all-agents", "install into every detected agent").option("--global", "install into the user-level agent skills folder").option("--project", "install into this project's agent skills folder").option("--all-scopes", "install into both global and project-local copies").option("--yes", "skip confirmation for safe mutations").option("--json", "print the plan or result as JSON").option("--dry-run", "print the plan without installing anything")).action((link, opts) => {
|
|
7961
8070
|
aliasNotice("add", "install");
|
|
7962
8071
|
return installCommand(link, opts);
|
|
7963
8072
|
});
|
|
7964
|
-
program.command("rename-device <label>").description("set a friendly name for this machine").action((label) => renameDeviceCommand(label));
|
|
7965
|
-
program.command("rename-machine <label>").description("set a friendly name for this machine (alias)").action((label) => {
|
|
8073
|
+
helpOpt(program.command("rename-device <label>").description("set a friendly name for this machine")).action((label) => renameDeviceCommand(label));
|
|
8074
|
+
helpOpt(program.command("rename-machine <label>").description("set a friendly name for this machine (alias)")).action((label) => {
|
|
7966
8075
|
aliasNotice("rename-machine", "rename-device");
|
|
7967
8076
|
return renameDeviceCommand(label);
|
|
7968
8077
|
});
|
|
7969
|
-
program.command("status").description("inspect sync state across Library and agents").option("--agent <name>", "limit to one or more agents (repeatable, comma-separated)", collectAgent, []).option("--target <name>", "alias for --agent", collectAgent, []).option("--verbose", "show every skill in every agent").option("--json", "print status as JSON").action((opts) => {
|
|
8078
|
+
helpOpt(program.command("status").description("inspect sync state across Library and agents").option("--agent <name>", "limit to one or more agents (repeatable, comma-separated)", collectAgent, []).option("--target <name>", "alias for --agent", collectAgent, []).option("--verbose", "show every skill in every agent").option("--json", "print status as JSON")).action((opts) => {
|
|
7970
8079
|
if (Array.isArray(opts.target) && opts.target.length > 0) aliasNotice("--target", "--agent");
|
|
7971
8080
|
return statusCommand(opts);
|
|
7972
8081
|
});
|
|
7973
|
-
program.command("diff <skill>").description("compare local copies with the Library").option("--agent <name>", "limit to one agent").option("--json", "print the comparison as JSON").action((skill, opts) => diffCommand(skill, opts));
|
|
7974
|
-
program.command("doctor").description("diagnose auth, agents, manifests, and version").option("--json", "print diagnostics as JSON").action((opts) => doctorCommand(opts));
|
|
7975
|
-
program.command("mcp").description("run the local MCP server (for agent configuration)").action(mcpCommand);
|
|
8082
|
+
helpOpt(program.command("diff <skill>").description("compare local copies with the Library").option("--agent <name>", "limit to one agent").option("--json", "print the comparison as JSON")).action((skill, opts) => diffCommand(skill, opts));
|
|
8083
|
+
helpOpt(program.command("doctor").description("diagnose auth, agents, manifests, and version").option("--json", "print diagnostics as JSON")).action((opts) => doctorCommand(opts));
|
|
8084
|
+
helpOpt(program.command("mcp").description("run the local MCP server (for agent configuration)")).action(mcpCommand);
|
|
7976
8085
|
function collectAgent(value, previous) {
|
|
7977
8086
|
return [...previous, value];
|
|
7978
8087
|
}
|
|
7979
8088
|
async function main() {
|
|
8089
|
+
installPipeGuards();
|
|
7980
8090
|
detectJsonEarly();
|
|
7981
8091
|
try {
|
|
7982
8092
|
await program.parseAsync(process.argv);
|
|
@@ -7985,6 +8095,10 @@ async function main() {
|
|
|
7985
8095
|
log.err(e.message);
|
|
7986
8096
|
process.exit(1);
|
|
7987
8097
|
}
|
|
8098
|
+
const cerr = e;
|
|
8099
|
+
if (cerr && typeof cerr.code === "string" && cerr.code.startsWith("commander.")) {
|
|
8100
|
+
process.exit(typeof cerr.exitCode === "number" ? cerr.exitCode : 1);
|
|
8101
|
+
}
|
|
7988
8102
|
log.err(e.message ?? "Unknown error");
|
|
7989
8103
|
if (process.env.FLOOM_DEBUG) {
|
|
7990
8104
|
const raw = e?.stack ?? String(e);
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = "3.0.
|
|
1
|
+
export const VERSION = "3.0.1";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@floomhq/floom",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.1",
|
|
4
4
|
"description": "Floom CLI \u2014 one shared skill library, pulled into the AI agent you choose (Claude, Codex, Cursor, Gemini, OpenCode).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://floom.dev",
|