@floomhq/floom 1.0.13 → 1.0.16
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/README.md +36 -30
- package/dist/cli.js +123 -228
- package/dist/doctor.js +117 -34
- package/dist/errors.js +1 -1
- package/dist/info.js +1 -1
- package/dist/init.js +87 -92
- package/dist/install.js +140 -66
- package/dist/library.js +2 -3
- package/dist/list.js +1 -1
- package/dist/login.js +81 -41
- package/dist/mcp.js +3 -4
- package/dist/package.js +313 -0
- package/dist/publish.js +51 -49
- package/dist/scan.js +18 -23
- package/dist/secrets.js +3 -29
- package/dist/setup.js +12 -14
- package/dist/sync-manifest.js +65 -16
- package/dist/sync.js +216 -172
- package/package.json +3 -2
- package/dist/targets.js +0 -16
package/dist/cli.js
CHANGED
|
@@ -3,7 +3,7 @@ import updateNotifier from "update-notifier";
|
|
|
3
3
|
import { login } from "./login.js";
|
|
4
4
|
import { publish } from "./publish.js";
|
|
5
5
|
import { whoami } from "./whoami.js";
|
|
6
|
-
import { init } from "./init.js";
|
|
6
|
+
import { init, INIT_TEMPLATES } from "./init.js";
|
|
7
7
|
import { deleteConfig, readConfig } from "./config.js";
|
|
8
8
|
import { list } from "./list.js";
|
|
9
9
|
import { install } from "./install.js";
|
|
@@ -22,6 +22,7 @@ import { printError, FloomError } from "./errors.js";
|
|
|
22
22
|
import { CLI_VERSION } from "./version.js";
|
|
23
23
|
const PKG = { name: "@floomhq/floom", version: CLI_VERSION };
|
|
24
24
|
const V1_NOT_AVAILABLE = "Not available in Floom Version 1.";
|
|
25
|
+
const CLI_COMMAND = "npx -y @floomhq/floom";
|
|
25
26
|
function usage() {
|
|
26
27
|
const out = `
|
|
27
28
|
${c.blue(" ________ ")}
|
|
@@ -30,22 +31,22 @@ function usage() {
|
|
|
30
31
|
${c.blue("/ __/ / / /_/ / /_/ / / / / / / ")}
|
|
31
32
|
${c.blue("/_/ /_/\\____/\\____/_/ /_/ /_/ ")}
|
|
32
33
|
|
|
33
|
-
${c.bold("Floom
|
|
34
|
+
${c.bold("Floom syncs your AI skills across agents and machines.")}
|
|
34
35
|
${c.dim("A skill is reusable knowledge, instructions, or a workflow for your AI agent.")}
|
|
35
36
|
${c.dim("Examples: brand voice, PR review checklist, sales research workflow.")}
|
|
36
37
|
|
|
37
|
-
${c.bold("
|
|
38
|
+
${c.bold("Start with one command:")}
|
|
38
39
|
|
|
39
40
|
${c.bold("1. I received a Floom link")}
|
|
40
41
|
${c.dim("Replace <skill-link> with the full Floom URL someone sent you:")}
|
|
41
42
|
${c.cyan("npx -y @floomhq/floom add")} ${c.dim("<skill-link> --setup")}
|
|
42
43
|
${c.dim('Then tell Claude Code: "Use my Floom skills when they fit this task."')}
|
|
43
44
|
|
|
44
|
-
${c.bold("2. I want
|
|
45
|
-
${c.cyan("npx -y @floomhq/floom init")} ${c.dim("my-skill
|
|
46
|
-
${c.dim("Write what your agent needs to know or do in my-skill.md.")}
|
|
45
|
+
${c.bold("2. I want my own synced skill library")}
|
|
46
|
+
${c.cyan("npx -y @floomhq/floom init")} ${c.dim("my-skill")}
|
|
47
|
+
${c.dim("Write what your agent needs to know or do in my-skill/SKILL.md.")}
|
|
47
48
|
${c.cyan("npx -y @floomhq/floom login")}
|
|
48
|
-
${c.cyan("npx -y @floomhq/floom publish")} ${c.dim("my-skill
|
|
49
|
+
${c.cyan("npx -y @floomhq/floom publish")} ${c.dim("my-skill --public")}
|
|
49
50
|
${c.dim("Floom scans it, prints a link, and copies the link when possible.")}
|
|
50
51
|
|
|
51
52
|
${c.bold("Good to know")}
|
|
@@ -55,8 +56,7 @@ function usage() {
|
|
|
55
56
|
|
|
56
57
|
${c.bold("Stuck?")}
|
|
57
58
|
${c.cyan("npx -y @floomhq/floom doctor")} ${c.dim("Find the problem")}
|
|
58
|
-
${c.cyan("npx -y @floomhq/floom scan my-skill
|
|
59
|
-
${c.cyan("npx -y @floomhq/floom init my-skill.md --template support")} ${c.dim("Start from an example")}
|
|
59
|
+
${c.cyan("npx -y @floomhq/floom scan my-skill")} ${c.dim("Check a skill before publishing")}
|
|
60
60
|
${c.cyan("npx -y @floomhq/floom commands")} ${c.dim("See every command")}
|
|
61
61
|
${c.dim("Step-by-step guide")} https://floom.dev/docs/getting-started
|
|
62
62
|
`;
|
|
@@ -64,16 +64,14 @@ function usage() {
|
|
|
64
64
|
}
|
|
65
65
|
function commandUsage() {
|
|
66
66
|
const out = `
|
|
67
|
-
${c.bold("Usage:")} ${c.cyan("npx -y @floomhq/floom")} ${c.dim("<command> [
|
|
68
|
-
${c.dim("
|
|
67
|
+
${c.bold("Usage:")} ${c.cyan("npx -y @floomhq/floom")} ${c.dim("<command> [flags]")}
|
|
68
|
+
${c.dim("After global install:")} ${c.cyan("floom")} ${c.dim("<command> [flags]")}
|
|
69
69
|
|
|
70
70
|
${c.bold("Commands")}
|
|
71
71
|
${c.dim("Skills")}
|
|
72
72
|
${c.cyan("add")} ${c.dim("<url>")} Install a skill into the local agent skills folder
|
|
73
73
|
${c.dim("Alias: install")}
|
|
74
|
-
${c.dim("Flags: --target claude|codex (default: claude), --setup, --force
|
|
75
|
-
${c.cyan("update")} ${c.dim("<url>")} Install the latest remote skill content, replacing the local copy
|
|
76
|
-
${c.dim("Alias: add --force")}
|
|
74
|
+
${c.dim("Flags: --target claude|codex (default: claude), --setup, --force")}
|
|
77
75
|
${c.cyan("search")} ${c.dim("<query>")} Find public skills and libraries
|
|
78
76
|
${c.cyan("info")} ${c.dim("<url>")} Show skill metadata
|
|
79
77
|
|
|
@@ -83,8 +81,7 @@ function commandUsage() {
|
|
|
83
81
|
${c.cyan("scan")} ${c.dim("<path>")} Check for secrets, injection, exfiltration
|
|
84
82
|
${c.cyan("publish")} ${c.dim("<path>")} Scan, publish, and print a share link
|
|
85
83
|
${c.dim("Flags: --public, --private, --type knowledge|instruction|workflow|skill")}
|
|
86
|
-
${c.dim(" --skill-version <label
|
|
87
|
-
${c.dim(" --share emails the normal link; no account is needed to add it")}
|
|
84
|
+
${c.dim(" --skill-version <label>")}
|
|
88
85
|
${c.cyan("share")} ${c.dim("<slug>")} Email-share one of your skills
|
|
89
86
|
${c.dim("Flags: --add <email>, --remove <email>, --list")}
|
|
90
87
|
|
|
@@ -99,13 +96,9 @@ function commandUsage() {
|
|
|
99
96
|
${c.dim("Agent setup")}
|
|
100
97
|
${c.cyan("setup")} Configure Claude Code or Codex instructions
|
|
101
98
|
${c.dim("Alias: connect")}
|
|
102
|
-
${c.dim("Flags: --target claude|codex, --yes, --dry-run
|
|
103
|
-
${c.dim(" From a repo, setup writes that repo's CLAUDE.md/AGENTS.md")}
|
|
104
|
-
${c.cyan("agent-prompt")} Print the sentence to paste into your agent
|
|
105
|
-
${c.dim("Alias: paste")}
|
|
106
|
-
${c.dim("Flags: --target claude|codex")}
|
|
99
|
+
${c.dim("Flags: --target claude|codex, --yes, --dry-run")}
|
|
107
100
|
${c.cyan("doctor")} Troubleshoot auth, API, and local folders
|
|
108
|
-
${c.dim("Flags: --target claude|codex
|
|
101
|
+
${c.dim("Flags: --target claude|codex (default: claude)")}
|
|
109
102
|
|
|
110
103
|
${c.dim("Advanced")}
|
|
111
104
|
${c.cyan("library")} Create, browse, and subscribe to libraries
|
|
@@ -113,14 +106,12 @@ function commandUsage() {
|
|
|
113
106
|
${c.cyan("move")} ${c.dim("<slug> --folder <path>")} Place a saved skill in a local folder
|
|
114
107
|
${c.cyan("mcp")} Print optional MCP setup guidance
|
|
115
108
|
${c.cyan("sync")} Preview pull of published, saved, and library skills
|
|
116
|
-
${c.dim("Flags: --target claude|codex")}
|
|
109
|
+
${c.dim("Flags: --target claude|codex (default: claude)")}
|
|
117
110
|
${c.cyan("watch")} Preview polling sync loop
|
|
118
|
-
${c.dim("Flags: --target claude|codex, --interval <seconds>")}
|
|
119
111
|
|
|
120
112
|
${c.bold("Examples")}
|
|
121
113
|
${c.cyan("npx -y @floomhq/floom add")} ${c.dim("https://floom.dev/s/ffas93ud --setup")}
|
|
122
|
-
${c.cyan("npx -y @floomhq/floom publish")} ${c.dim("support-tone
|
|
123
|
-
${c.cyan("npx -y @floomhq/floom publish")} ${c.dim("support-tone.md --share teammate@example.com")}
|
|
114
|
+
${c.cyan("npx -y @floomhq/floom publish")} ${c.dim("support-tone --type instruction --public")}
|
|
124
115
|
${c.cyan("npx -y @floomhq/floom setup")} ${c.dim("--target claude --yes")}
|
|
125
116
|
|
|
126
117
|
${c.bold("Help")}
|
|
@@ -133,8 +124,6 @@ function commandUsage() {
|
|
|
133
124
|
process.stdout.write(out);
|
|
134
125
|
}
|
|
135
126
|
const ASSET_TYPES = new Set(["knowledge", "instruction", "workflow", "skill"]);
|
|
136
|
-
const INIT_TEMPLATES = new Set(["generic", "brand-voice", "pr-review", "sales", "support", "onboarding"]);
|
|
137
|
-
const MAX_SHARE_RECIPIENTS = 25;
|
|
138
127
|
const INSTALL_TARGETS = new Set([
|
|
139
128
|
"claude_skill",
|
|
140
129
|
"memory",
|
|
@@ -155,7 +144,7 @@ function readFlagValue(argv, index, flag) {
|
|
|
155
144
|
return { value, nextIndex: index + 1 };
|
|
156
145
|
}
|
|
157
146
|
function parseFlags(argv) {
|
|
158
|
-
const out = { visibility: "unlisted", update: false,
|
|
147
|
+
const out = { visibility: "unlisted", update: false, rest: [] };
|
|
159
148
|
let visibilityFlag = null;
|
|
160
149
|
for (let i = 0; i < argv.length; i++) {
|
|
161
150
|
const a = argv[i] ?? "";
|
|
@@ -166,15 +155,12 @@ function parseFlags(argv) {
|
|
|
166
155
|
}
|
|
167
156
|
visibilityFlag = nextVisibility;
|
|
168
157
|
out.visibility = nextVisibility;
|
|
169
|
-
out.explicitVisibility = true;
|
|
170
158
|
}
|
|
171
159
|
else if (a === "--update") {
|
|
172
|
-
throw new FloomError(
|
|
160
|
+
throw new FloomError(V1_NOT_AVAILABLE, `\`${CLI_COMMAND} publish --update\` is planned for a later Floom release.`);
|
|
173
161
|
}
|
|
174
162
|
else if (a === "--share" || a.startsWith("--share=")) {
|
|
175
|
-
|
|
176
|
-
out.shareEmails.push(...parseEmailList(value, "--share"));
|
|
177
|
-
i = nextIndex;
|
|
163
|
+
throw new FloomError(V1_NOT_AVAILABLE, `\`${CLI_COMMAND} publish --share\` is planned for a later Floom release.`);
|
|
178
164
|
}
|
|
179
165
|
else if (a === "--type" || a.startsWith("--type=")) {
|
|
180
166
|
const { value, nextIndex } = readFlagValue(argv, i, "--type");
|
|
@@ -204,36 +190,13 @@ function parseFlags(argv) {
|
|
|
204
190
|
throw new FloomError("`--version` prints the Floom CLI version at the top level.", "For skill version labels, use `--skill-version <label>`.");
|
|
205
191
|
}
|
|
206
192
|
else if (a.startsWith("--")) {
|
|
207
|
-
throw new FloomError(`Unknown flag: ${a}`,
|
|
193
|
+
throw new FloomError(`Unknown flag: ${a}`, `Try \`${CLI_COMMAND} publish my-skill --type instruction --public\`.`);
|
|
208
194
|
}
|
|
209
195
|
else
|
|
210
196
|
out.rest.push(a);
|
|
211
197
|
}
|
|
212
|
-
if (out.shareEmails.length > 0) {
|
|
213
|
-
out.shareEmails = dedupeEmails(out.shareEmails);
|
|
214
|
-
if (out.shareEmails.length > MAX_SHARE_RECIPIENTS) {
|
|
215
|
-
throw new FloomError("Too many --share recipients.", `Use ${MAX_SHARE_RECIPIENTS} email addresses or fewer.`);
|
|
216
|
-
}
|
|
217
|
-
if (out.visibility === "private") {
|
|
218
|
-
throw new FloomError("`--private --share` would email a link recipients cannot open.", "Use `--unlisted --share` for invite emails, or `npx -y @floomhq/floom share <slug> --add <email>` for email-gated access after publishing.");
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
198
|
return out;
|
|
222
199
|
}
|
|
223
|
-
function parseEmailList(value, source) {
|
|
224
|
-
const emails = value.split(",").map((email) => email.trim().toLowerCase()).filter(Boolean);
|
|
225
|
-
if (emails.length === 0)
|
|
226
|
-
throw new FloomError(`Missing value for ${source}.`, `Try \`${source} teammate@example.com\`.`);
|
|
227
|
-
for (const email of emails) {
|
|
228
|
-
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
|
|
229
|
-
throw new FloomError(`Invalid email for ${source}: ${email}`, "Use an address like teammate@example.com.");
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
return emails;
|
|
233
|
-
}
|
|
234
|
-
function dedupeEmails(emails) {
|
|
235
|
-
return [...new Set(emails)];
|
|
236
|
-
}
|
|
237
200
|
function parseShareFlags(argv) {
|
|
238
201
|
const out = { list: false, add: [], remove: [] };
|
|
239
202
|
for (let i = 0; i < argv.length; i++) {
|
|
@@ -252,48 +215,48 @@ function parseShareFlags(argv) {
|
|
|
252
215
|
i = nextIndex;
|
|
253
216
|
}
|
|
254
217
|
else if (a.startsWith("--")) {
|
|
255
|
-
throw new FloomError(`Unknown flag: ${a}`,
|
|
218
|
+
throw new FloomError(`Unknown flag: ${a}`, `Try \`${CLI_COMMAND} share <slug> --add person@example.com\`.`);
|
|
256
219
|
}
|
|
257
220
|
else if (!out.slug) {
|
|
258
221
|
out.slug = a;
|
|
259
222
|
}
|
|
260
223
|
else {
|
|
261
|
-
throw new FloomError(`Unexpected argument: ${a}`,
|
|
224
|
+
throw new FloomError(`Unexpected argument: ${a}`, `Try \`${CLI_COMMAND} share <slug> --add person@example.com\`.`);
|
|
262
225
|
}
|
|
263
226
|
}
|
|
264
227
|
if (!out.slug)
|
|
265
|
-
throw new FloomError("Missing skill slug.",
|
|
228
|
+
throw new FloomError("Missing skill slug.", `Try \`${CLI_COMMAND} share <slug> --add person@example.com\`.`);
|
|
266
229
|
if (out.list && (out.add.length > 0 || out.remove.length > 0)) {
|
|
267
230
|
throw new FloomError("Conflicting share flags.", "Use --list by itself, or use --add/--remove.");
|
|
268
231
|
}
|
|
269
232
|
if (!out.list && out.add.length === 0 && out.remove.length === 0) {
|
|
270
|
-
throw new FloomError("Missing share action.",
|
|
233
|
+
throw new FloomError("Missing share action.", `Try \`${CLI_COMMAND} share <slug> --add person@example.com\`.`);
|
|
271
234
|
}
|
|
272
235
|
return out;
|
|
273
236
|
}
|
|
274
237
|
function parseInitArgs(argv) {
|
|
275
238
|
let file;
|
|
276
|
-
let template
|
|
239
|
+
let template;
|
|
277
240
|
for (let i = 0; i < argv.length; i++) {
|
|
278
241
|
const a = argv[i] ?? "";
|
|
279
242
|
if (a === "--template" || a.startsWith("--template=")) {
|
|
280
243
|
const { value, nextIndex } = readFlagValue(argv, i, "--template");
|
|
281
|
-
if (!INIT_TEMPLATES.
|
|
282
|
-
throw new FloomError(`Invalid --template: ${value}`,
|
|
244
|
+
if (!INIT_TEMPLATES.includes(value)) {
|
|
245
|
+
throw new FloomError(`Invalid --template: ${value}`, `Use one of: ${INIT_TEMPLATES.join(", ")}.`);
|
|
283
246
|
}
|
|
284
247
|
template = value;
|
|
285
248
|
i = nextIndex;
|
|
286
249
|
continue;
|
|
287
250
|
}
|
|
288
251
|
if (a.startsWith("--")) {
|
|
289
|
-
throw new FloomError(`Unknown flag: ${a}`,
|
|
252
|
+
throw new FloomError(`Unknown flag: ${a}`, `Try \`${CLI_COMMAND} init my-skill\`.`);
|
|
290
253
|
}
|
|
291
254
|
if (file) {
|
|
292
|
-
throw new FloomError(`Unexpected argument: ${a}`,
|
|
255
|
+
throw new FloomError(`Unexpected argument: ${a}`, `Try \`${CLI_COMMAND} init my-skill\`.`);
|
|
293
256
|
}
|
|
294
257
|
file = a;
|
|
295
258
|
}
|
|
296
|
-
return file ? { file, template } : {
|
|
259
|
+
return { ...(file ? { file } : {}), ...(template ? { template } : {}) };
|
|
297
260
|
}
|
|
298
261
|
function parseListFlags(argv) {
|
|
299
262
|
const out = { json: false };
|
|
@@ -301,10 +264,31 @@ function parseListFlags(argv) {
|
|
|
301
264
|
if (a === "--json")
|
|
302
265
|
out.json = true;
|
|
303
266
|
else if (a.startsWith("--")) {
|
|
304
|
-
throw new FloomError(`Unknown flag: ${a}`,
|
|
267
|
+
throw new FloomError(`Unknown flag: ${a}`, `Try \`${CLI_COMMAND} list --help\` for usage.`);
|
|
268
|
+
}
|
|
269
|
+
else {
|
|
270
|
+
throw new FloomError(`Unexpected argument: ${a}`, `Try \`${CLI_COMMAND} list --json\`.`);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return out;
|
|
274
|
+
}
|
|
275
|
+
function parseSyncFlags(argv) {
|
|
276
|
+
const out = {};
|
|
277
|
+
for (let i = 0; i < argv.length; i++) {
|
|
278
|
+
const a = argv[i] ?? "";
|
|
279
|
+
if (a === "--target" || a.startsWith("--target=")) {
|
|
280
|
+
const { value, nextIndex } = readFlagValue(argv, i, "--target");
|
|
281
|
+
if (value !== "claude" && value !== "codex") {
|
|
282
|
+
throw new FloomError("Invalid --target.", "Use claude or codex.");
|
|
283
|
+
}
|
|
284
|
+
out.target = value;
|
|
285
|
+
i = nextIndex;
|
|
286
|
+
}
|
|
287
|
+
else if (a.startsWith("--")) {
|
|
288
|
+
throw new FloomError(`Unknown flag: ${a}`, `Try \`${CLI_COMMAND} sync --target claude\`.`);
|
|
305
289
|
}
|
|
306
290
|
else {
|
|
307
|
-
throw new FloomError(`Unexpected argument: ${a}`,
|
|
291
|
+
throw new FloomError(`Unexpected argument: ${a}`, `Try \`${CLI_COMMAND} sync --target claude\`.`);
|
|
308
292
|
}
|
|
309
293
|
}
|
|
310
294
|
return out;
|
|
@@ -315,11 +299,11 @@ function parseInfoFlags(argv) {
|
|
|
315
299
|
if (a === "--json")
|
|
316
300
|
out.json = true;
|
|
317
301
|
else if (a.startsWith("--"))
|
|
318
|
-
throw new FloomError(`Unknown flag: ${a}`,
|
|
302
|
+
throw new FloomError(`Unknown flag: ${a}`, `Try \`${CLI_COMMAND} info <slug> --json\`.`);
|
|
319
303
|
else if (!out.slug)
|
|
320
304
|
out.slug = a;
|
|
321
305
|
else
|
|
322
|
-
throw new FloomError(`Unexpected argument: ${a}`,
|
|
306
|
+
throw new FloomError(`Unexpected argument: ${a}`, `Try \`${CLI_COMMAND} info <slug> --json\`.`);
|
|
323
307
|
}
|
|
324
308
|
return out;
|
|
325
309
|
}
|
|
@@ -328,7 +312,6 @@ function parseAddArgs(argv) {
|
|
|
328
312
|
let target;
|
|
329
313
|
let setup = false;
|
|
330
314
|
let force = false;
|
|
331
|
-
let json = false;
|
|
332
315
|
for (let i = 0; i < argv.length; i++) {
|
|
333
316
|
const a = argv[i] ?? "";
|
|
334
317
|
if (a === "--target" || a.startsWith("--target=")) {
|
|
@@ -342,73 +325,22 @@ function parseAddArgs(argv) {
|
|
|
342
325
|
else if (a === "--setup") {
|
|
343
326
|
setup = true;
|
|
344
327
|
}
|
|
345
|
-
else if (a === "--
|
|
346
|
-
json = true;
|
|
347
|
-
}
|
|
348
|
-
else if (a === "--force" || a === "--update") {
|
|
328
|
+
else if (a === "--force") {
|
|
349
329
|
force = true;
|
|
350
330
|
}
|
|
351
331
|
else if (a.startsWith("--")) {
|
|
352
|
-
throw new FloomError(`Unknown flag: ${a}`,
|
|
332
|
+
throw new FloomError(`Unknown flag: ${a}`, `Try \`${CLI_COMMAND} add <url-or-slug> --setup\`.`);
|
|
353
333
|
}
|
|
354
334
|
else if (slug) {
|
|
355
|
-
throw new FloomError(`Unexpected argument: ${a}`,
|
|
335
|
+
throw new FloomError(`Unexpected argument: ${a}`, `Try \`${CLI_COMMAND} add <url-or-slug> --setup\`.`);
|
|
356
336
|
}
|
|
357
337
|
else
|
|
358
338
|
slug = a;
|
|
359
339
|
}
|
|
360
340
|
if (!slug) {
|
|
361
|
-
throw new FloomError("Missing skill slug.",
|
|
362
|
-
}
|
|
363
|
-
if (json && setup) {
|
|
364
|
-
throw new FloomError("Conflicting add flags.", "Use `--json` for machine output or `--setup` for interactive agent setup.");
|
|
365
|
-
}
|
|
366
|
-
return target ? { slug, target, setup, force, json } : { slug, setup, force, json };
|
|
367
|
-
}
|
|
368
|
-
function parseTargetFlag(value) {
|
|
369
|
-
if (value !== "claude" && value !== "codex") {
|
|
370
|
-
throw new FloomError("Invalid --target.", "Use `claude` or `codex`.");
|
|
371
|
-
}
|
|
372
|
-
return value;
|
|
373
|
-
}
|
|
374
|
-
function parseDoctorArgs(argv) {
|
|
375
|
-
const out = { json: false };
|
|
376
|
-
for (let i = 0; i < argv.length; i++) {
|
|
377
|
-
const a = argv[i] ?? "";
|
|
378
|
-
if (a === "--json")
|
|
379
|
-
out.json = true;
|
|
380
|
-
else if (a === "--target" || a.startsWith("--target=")) {
|
|
381
|
-
const { value, nextIndex } = readFlagValue(argv, i, "--target");
|
|
382
|
-
out.target = parseTargetFlag(value);
|
|
383
|
-
i = nextIndex;
|
|
384
|
-
}
|
|
385
|
-
else if (a.startsWith("--"))
|
|
386
|
-
throw new FloomError(`Unknown flag: ${a}`, "Try `npx -y @floomhq/floom doctor --target codex --json`.");
|
|
387
|
-
else
|
|
388
|
-
throw new FloomError(`Unexpected argument: ${a}`, "Try `npx -y @floomhq/floom doctor --target codex --json`.");
|
|
389
|
-
}
|
|
390
|
-
return out;
|
|
391
|
-
}
|
|
392
|
-
function parseAgentPromptArgs(argv) {
|
|
393
|
-
const out = {};
|
|
394
|
-
for (let i = 0; i < argv.length; i++) {
|
|
395
|
-
const a = argv[i] ?? "";
|
|
396
|
-
if (a === "--target" || a.startsWith("--target=")) {
|
|
397
|
-
const { value, nextIndex } = readFlagValue(argv, i, "--target");
|
|
398
|
-
if (value !== "claude" && value !== "codex") {
|
|
399
|
-
throw new FloomError("Invalid --target.", "Use `claude` or `codex`.");
|
|
400
|
-
}
|
|
401
|
-
out.target = value;
|
|
402
|
-
i = nextIndex;
|
|
403
|
-
}
|
|
404
|
-
else if (a.startsWith("--")) {
|
|
405
|
-
throw new FloomError(`Unknown flag: ${a}`, "Try `npx -y @floomhq/floom agent-prompt --target codex`.");
|
|
406
|
-
}
|
|
407
|
-
else {
|
|
408
|
-
throw new FloomError(`Unexpected argument: ${a}`, "Try `npx -y @floomhq/floom agent-prompt --target codex`.");
|
|
409
|
-
}
|
|
341
|
+
throw new FloomError("Missing skill slug.", `Try: \`${CLI_COMMAND} add <url-or-slug> --setup\``);
|
|
410
342
|
}
|
|
411
|
-
return
|
|
343
|
+
return target ? { slug, target, setup, force } : { slug, setup, force };
|
|
412
344
|
}
|
|
413
345
|
function parseSearchFlags(argv) {
|
|
414
346
|
const out = { json: false };
|
|
@@ -431,7 +363,7 @@ function parseSearchFlags(argv) {
|
|
|
431
363
|
i = nextIndex;
|
|
432
364
|
}
|
|
433
365
|
else if (a.startsWith("--")) {
|
|
434
|
-
throw new FloomError(`Unknown flag: ${a}`,
|
|
366
|
+
throw new FloomError(`Unknown flag: ${a}`, `Try \`${CLI_COMMAND} search "support tone" --type instruction\`.`);
|
|
435
367
|
}
|
|
436
368
|
else {
|
|
437
369
|
terms.push(a);
|
|
@@ -446,11 +378,11 @@ function parseDeleteFlags(argv) {
|
|
|
446
378
|
if (a === "--yes" || a === "-y")
|
|
447
379
|
out.yes = true;
|
|
448
380
|
else if (a.startsWith("--"))
|
|
449
|
-
throw new FloomError(`Unknown flag: ${a}`,
|
|
381
|
+
throw new FloomError(`Unknown flag: ${a}`, `Try \`${CLI_COMMAND} delete <slug> --yes\`.`);
|
|
450
382
|
else if (!out.slug)
|
|
451
383
|
out.slug = a;
|
|
452
384
|
else
|
|
453
|
-
throw new FloomError(`Unexpected argument: ${a}`,
|
|
385
|
+
throw new FloomError(`Unexpected argument: ${a}`, `Try \`${CLI_COMMAND} delete <slug> --yes\`.`);
|
|
454
386
|
}
|
|
455
387
|
return out;
|
|
456
388
|
}
|
|
@@ -484,12 +416,33 @@ function parseSetupFlags(argv) {
|
|
|
484
416
|
i = nextIndex;
|
|
485
417
|
}
|
|
486
418
|
else if (a.startsWith("--")) {
|
|
487
|
-
throw new FloomError(`Unknown flag: ${a}`,
|
|
419
|
+
throw new FloomError(`Unknown flag: ${a}`, `Try \`${CLI_COMMAND} setup --target codex --dry-run\`.`);
|
|
488
420
|
}
|
|
489
421
|
else if (!out.file)
|
|
490
422
|
out.file = a;
|
|
491
423
|
else
|
|
492
|
-
throw new FloomError(`Unexpected argument: ${a}`,
|
|
424
|
+
throw new FloomError(`Unexpected argument: ${a}`, `Try \`${CLI_COMMAND} setup --target claude --yes\`.`);
|
|
425
|
+
}
|
|
426
|
+
return out;
|
|
427
|
+
}
|
|
428
|
+
function parseDoctorFlags(argv) {
|
|
429
|
+
const out = {};
|
|
430
|
+
for (let i = 0; i < argv.length; i++) {
|
|
431
|
+
const a = argv[i] ?? "";
|
|
432
|
+
if (a === "--target" || a.startsWith("--target=")) {
|
|
433
|
+
const { value, nextIndex } = readFlagValue(argv, i, "--target");
|
|
434
|
+
if (value !== "claude" && value !== "codex") {
|
|
435
|
+
throw new FloomError("Invalid --target.", "Use `claude` or `codex`.");
|
|
436
|
+
}
|
|
437
|
+
out.target = value;
|
|
438
|
+
i = nextIndex;
|
|
439
|
+
}
|
|
440
|
+
else if (a.startsWith("--")) {
|
|
441
|
+
throw new FloomError(`Unknown flag: ${a}`, `Try \`${CLI_COMMAND} doctor --target codex\`.`);
|
|
442
|
+
}
|
|
443
|
+
else {
|
|
444
|
+
throw new FloomError(`Unexpected argument: ${a}`, `Try \`${CLI_COMMAND} doctor --target claude\`.`);
|
|
445
|
+
}
|
|
493
446
|
}
|
|
494
447
|
return out;
|
|
495
448
|
}
|
|
@@ -548,21 +501,17 @@ function parseLibraryCreateFlags(argv) {
|
|
|
548
501
|
else if (a === "--unlisted")
|
|
549
502
|
out.visibility = "unlisted";
|
|
550
503
|
else if (a.startsWith("--")) {
|
|
551
|
-
throw new FloomError(`Unknown flag: ${a}`,
|
|
504
|
+
throw new FloomError(`Unknown flag: ${a}`, `Try \`${CLI_COMMAND} library create team-onboarding --name "Team onboarding"\`.`);
|
|
552
505
|
}
|
|
553
506
|
else if (!out.slug)
|
|
554
507
|
out.slug = a;
|
|
555
508
|
else
|
|
556
|
-
throw new FloomError(`Unexpected argument: ${a}`,
|
|
509
|
+
throw new FloomError(`Unexpected argument: ${a}`, `Try \`${CLI_COMMAND} library create <slug> --name <name>\`.`);
|
|
557
510
|
}
|
|
558
511
|
return out;
|
|
559
512
|
}
|
|
560
513
|
async function runLibrary(argv) {
|
|
561
514
|
const [subcommand, ...rest] = argv;
|
|
562
|
-
if (!subcommand || subcommand === "--json") {
|
|
563
|
-
await libraryList(parseListFlags(argv));
|
|
564
|
-
return;
|
|
565
|
-
}
|
|
566
515
|
switch (subcommand ?? "list") {
|
|
567
516
|
case "list": {
|
|
568
517
|
const flags = parseListFlags(rest);
|
|
@@ -572,9 +521,9 @@ async function runLibrary(argv) {
|
|
|
572
521
|
case "create": {
|
|
573
522
|
const flags = parseLibraryCreateFlags(rest);
|
|
574
523
|
if (!flags.slug)
|
|
575
|
-
throw new FloomError("Missing library slug.",
|
|
524
|
+
throw new FloomError("Missing library slug.", `Try \`${CLI_COMMAND} library create team-onboarding --name "Team onboarding"\`.`);
|
|
576
525
|
if (!flags.name)
|
|
577
|
-
throw new FloomError("Missing --name.",
|
|
526
|
+
throw new FloomError("Missing --name.", `Try \`${CLI_COMMAND} library create team-onboarding --name "Team onboarding"\`.`);
|
|
578
527
|
await libraryCreate({
|
|
579
528
|
slug: flags.slug,
|
|
580
529
|
name: flags.name,
|
|
@@ -587,10 +536,10 @@ async function runLibrary(argv) {
|
|
|
587
536
|
const flags = parseFolderTagFlags(rest);
|
|
588
537
|
const [librarySlug, skillSlug] = flags.rest;
|
|
589
538
|
if (!librarySlug || !skillSlug) {
|
|
590
|
-
throw new FloomError("Missing library or skill slug.",
|
|
539
|
+
throw new FloomError("Missing library or skill slug.", `Try \`${CLI_COMMAND} library add team-onboarding support-tone --folder support\`.`);
|
|
591
540
|
}
|
|
592
541
|
if (flags.rest.length > 2) {
|
|
593
|
-
throw new FloomError(`Unexpected argument: ${flags.rest[2]}`,
|
|
542
|
+
throw new FloomError(`Unexpected argument: ${flags.rest[2]}`, `Try \`${CLI_COMMAND} library add team-onboarding support-tone --folder support\`.`);
|
|
594
543
|
}
|
|
595
544
|
await libraryAddSkill({
|
|
596
545
|
librarySlug,
|
|
@@ -604,10 +553,10 @@ async function runLibrary(argv) {
|
|
|
604
553
|
case "rm": {
|
|
605
554
|
const [librarySlug, skillSlug] = rest;
|
|
606
555
|
if (!librarySlug || !skillSlug) {
|
|
607
|
-
throw new FloomError("Missing library or skill slug.",
|
|
556
|
+
throw new FloomError("Missing library or skill slug.", `Try \`${CLI_COMMAND} library remove team-onboarding support-tone\`.`);
|
|
608
557
|
}
|
|
609
558
|
if (rest.length > 2) {
|
|
610
|
-
throw new FloomError(`Unexpected argument: ${rest[2]}`,
|
|
559
|
+
throw new FloomError(`Unexpected argument: ${rest[2]}`, `Try \`${CLI_COMMAND} library remove team-onboarding support-tone\`.`);
|
|
611
560
|
}
|
|
612
561
|
await libraryRemoveSkill(librarySlug, skillSlug);
|
|
613
562
|
return;
|
|
@@ -615,9 +564,9 @@ async function runLibrary(argv) {
|
|
|
615
564
|
case "subscribe": {
|
|
616
565
|
const slug = rest[0];
|
|
617
566
|
if (!slug)
|
|
618
|
-
throw new FloomError("Missing library slug.",
|
|
567
|
+
throw new FloomError("Missing library slug.", `Try \`${CLI_COMMAND} library subscribe superpowers\`.`);
|
|
619
568
|
if (rest.length > 1) {
|
|
620
|
-
throw new FloomError(`Unexpected argument: ${rest[1]}`,
|
|
569
|
+
throw new FloomError(`Unexpected argument: ${rest[1]}`, `Try \`${CLI_COMMAND} library subscribe superpowers\`.`);
|
|
621
570
|
}
|
|
622
571
|
await librarySubscribe(slug);
|
|
623
572
|
return;
|
|
@@ -625,9 +574,9 @@ async function runLibrary(argv) {
|
|
|
625
574
|
case "unsubscribe": {
|
|
626
575
|
const slug = rest[0];
|
|
627
576
|
if (!slug)
|
|
628
|
-
throw new FloomError("Missing library slug.",
|
|
577
|
+
throw new FloomError("Missing library slug.", `Try \`${CLI_COMMAND} library unsubscribe superpowers\`.`);
|
|
629
578
|
if (rest.length > 1) {
|
|
630
|
-
throw new FloomError(`Unexpected argument: ${rest[1]}`,
|
|
579
|
+
throw new FloomError(`Unexpected argument: ${rest[1]}`, `Try \`${CLI_COMMAND} library unsubscribe superpowers\`.`);
|
|
631
580
|
}
|
|
632
581
|
await libraryUnsubscribe(slug);
|
|
633
582
|
return;
|
|
@@ -649,34 +598,11 @@ function parseWatchFlags(argv) {
|
|
|
649
598
|
out.intervalSeconds = interval;
|
|
650
599
|
i = nextIndex;
|
|
651
600
|
}
|
|
652
|
-
else if (a === "--target" || a.startsWith("--target=")) {
|
|
653
|
-
const { value, nextIndex } = readFlagValue(argv, i, "--target");
|
|
654
|
-
out.target = parseTargetFlag(value);
|
|
655
|
-
i = nextIndex;
|
|
656
|
-
}
|
|
657
|
-
else if (a.startsWith("--")) {
|
|
658
|
-
throw new FloomError(`Unknown flag: ${a}`, "Try `npx -y @floomhq/floom watch --target codex --interval 60`.");
|
|
659
|
-
}
|
|
660
|
-
else {
|
|
661
|
-
throw new FloomError(`Unexpected argument: ${a}`, "Try `npx -y @floomhq/floom watch --target codex --interval 60`.");
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
return out;
|
|
665
|
-
}
|
|
666
|
-
function parseSyncFlags(argv) {
|
|
667
|
-
const out = {};
|
|
668
|
-
for (let i = 0; i < argv.length; i++) {
|
|
669
|
-
const a = argv[i] ?? "";
|
|
670
|
-
if (a === "--target" || a.startsWith("--target=")) {
|
|
671
|
-
const { value, nextIndex } = readFlagValue(argv, i, "--target");
|
|
672
|
-
out.target = parseTargetFlag(value);
|
|
673
|
-
i = nextIndex;
|
|
674
|
-
}
|
|
675
601
|
else if (a.startsWith("--")) {
|
|
676
|
-
throw new FloomError(`Unknown flag: ${a}`,
|
|
602
|
+
throw new FloomError(`Unknown flag: ${a}`, `Try \`${CLI_COMMAND} watch --interval 60\`.`);
|
|
677
603
|
}
|
|
678
604
|
else {
|
|
679
|
-
throw new FloomError(`Unexpected argument: ${a}`,
|
|
605
|
+
throw new FloomError(`Unexpected argument: ${a}`, `Try \`${CLI_COMMAND} watch --interval 60\`.`);
|
|
680
606
|
}
|
|
681
607
|
}
|
|
682
608
|
return out;
|
|
@@ -697,10 +623,6 @@ function parseSingleFileArg(argv, usageHint) {
|
|
|
697
623
|
throw new FloomError("Missing file argument.", usageHint);
|
|
698
624
|
return file;
|
|
699
625
|
}
|
|
700
|
-
function agentPrompt(target = "claude") {
|
|
701
|
-
const folder = target === "codex" ? "~/.codex/skills" : "~/.claude/skills";
|
|
702
|
-
process.stdout.write(`Before recreating behavior from scratch, search ${folder} for matching Floom skills. If none fit, search Floom with \`npx -y @floomhq/floom search <query>\`, show useful matches, and ask before installing anything.\n`);
|
|
703
|
-
}
|
|
704
626
|
function sleep(ms, signal) {
|
|
705
627
|
if (signal.aborted)
|
|
706
628
|
return Promise.resolve();
|
|
@@ -712,10 +634,10 @@ function sleep(ms, signal) {
|
|
|
712
634
|
}, { once: true });
|
|
713
635
|
});
|
|
714
636
|
}
|
|
715
|
-
async function watch(intervalSeconds
|
|
637
|
+
async function watch(intervalSeconds) {
|
|
716
638
|
const cfg = await readConfig();
|
|
717
639
|
if (!cfg) {
|
|
718
|
-
throw new FloomError("Not signed in.",
|
|
640
|
+
throw new FloomError("Not signed in.", `Run \`${CLI_COMMAND} login\` before \`${CLI_COMMAND} watch\`, or use \`${CLI_COMMAND} add <link>\` without an account.`);
|
|
719
641
|
}
|
|
720
642
|
const controller = new AbortController();
|
|
721
643
|
let stopping = false;
|
|
@@ -729,9 +651,9 @@ async function watch(intervalSeconds, target = "claude") {
|
|
|
729
651
|
};
|
|
730
652
|
process.on("SIGINT", stop);
|
|
731
653
|
process.on("SIGTERM", stop);
|
|
732
|
-
process.stdout.write(`${symbols.bullet} Watching Floom
|
|
654
|
+
process.stdout.write(`${symbols.bullet} Watching Floom sync every ${intervalSeconds}s. Press Ctrl-C to stop.\n`);
|
|
733
655
|
while (!controller.signal.aborted) {
|
|
734
|
-
await sync({ spinner: false, quietUnchanged: true
|
|
656
|
+
await sync({ spinner: false, quietUnchanged: true });
|
|
735
657
|
await sleep(intervalSeconds * 1000, controller.signal);
|
|
736
658
|
}
|
|
737
659
|
}
|
|
@@ -751,10 +673,10 @@ async function main() {
|
|
|
751
673
|
// never block on update-notifier
|
|
752
674
|
}
|
|
753
675
|
}
|
|
754
|
-
// Subcommand --help: any rest arg = --help/-h/help → show top-level
|
|
676
|
+
// Subcommand --help: any rest arg = --help/-h/help → show top-level usage.
|
|
755
677
|
// Subcommands are simple enough that one help screen is fine for Version 1.
|
|
756
678
|
if (rest.includes("--help") || rest.includes("-h") || rest.includes("help")) {
|
|
757
|
-
|
|
679
|
+
usage();
|
|
758
680
|
return;
|
|
759
681
|
}
|
|
760
682
|
try {
|
|
@@ -768,7 +690,7 @@ async function main() {
|
|
|
768
690
|
commandUsage();
|
|
769
691
|
return;
|
|
770
692
|
case "commands":
|
|
771
|
-
rejectArgs(rest,
|
|
693
|
+
rejectArgs(rest, `Try \`${CLI_COMMAND} commands\`.`);
|
|
772
694
|
commandUsage();
|
|
773
695
|
return;
|
|
774
696
|
case "--version":
|
|
@@ -776,31 +698,31 @@ async function main() {
|
|
|
776
698
|
process.stdout.write(`${CLI_VERSION}\n`);
|
|
777
699
|
return;
|
|
778
700
|
case "login":
|
|
779
|
-
rejectArgs(rest,
|
|
701
|
+
rejectArgs(rest, `Try \`${CLI_COMMAND} login\`.`);
|
|
780
702
|
await login();
|
|
781
703
|
return;
|
|
782
704
|
case "logout":
|
|
783
|
-
rejectArgs(rest,
|
|
705
|
+
rejectArgs(rest, `Try \`${CLI_COMMAND} logout\`.`);
|
|
784
706
|
await deleteConfig();
|
|
785
707
|
process.stdout.write(`\n${symbols.ok} Signed out\n\n`);
|
|
786
708
|
return;
|
|
787
709
|
case "whoami":
|
|
788
|
-
rejectArgs(rest,
|
|
710
|
+
rejectArgs(rest, `Try \`${CLI_COMMAND} whoami\`.`);
|
|
789
711
|
await whoami();
|
|
790
712
|
return;
|
|
791
713
|
case "init": {
|
|
792
714
|
const flags = parseInitArgs(rest);
|
|
793
|
-
await init(flags.file, flags.template);
|
|
715
|
+
await init(flags.file, flags.template ? { template: flags.template } : {});
|
|
794
716
|
return;
|
|
795
717
|
}
|
|
796
718
|
case "publish": {
|
|
797
719
|
const flags = parseFlags(rest);
|
|
798
720
|
const file = flags.rest[0];
|
|
799
721
|
if (!file) {
|
|
800
|
-
throw new FloomError("Missing file argument.",
|
|
722
|
+
throw new FloomError("Missing file argument.", `Try: \`${CLI_COMMAND} publish my-skill\``);
|
|
801
723
|
}
|
|
802
724
|
if (flags.rest.length > 1) {
|
|
803
|
-
throw new FloomError(`Unexpected argument: ${flags.rest[1]}`,
|
|
725
|
+
throw new FloomError(`Unexpected argument: ${flags.rest[1]}`, `Try: \`${CLI_COMMAND} publish my-skill\``);
|
|
804
726
|
}
|
|
805
727
|
await publish({
|
|
806
728
|
file,
|
|
@@ -809,12 +731,11 @@ async function main() {
|
|
|
809
731
|
...(flags.assetType ? { assetType: flags.assetType } : {}),
|
|
810
732
|
...(flags.installsAs ? { installsAs: flags.installsAs } : {}),
|
|
811
733
|
...(flags.version ? { version: flags.version } : {}),
|
|
812
|
-
...(flags.shareEmails.length > 0 ? { sharedWithEmails: flags.shareEmails } : {}),
|
|
813
734
|
});
|
|
814
735
|
return;
|
|
815
736
|
}
|
|
816
737
|
case "scan": {
|
|
817
|
-
const file = parseSingleFileArg(rest,
|
|
738
|
+
const file = parseSingleFileArg(rest, `Try \`${CLI_COMMAND} scan my-skill\`.`);
|
|
818
739
|
await scanSkill(file);
|
|
819
740
|
return;
|
|
820
741
|
}
|
|
@@ -843,7 +764,7 @@ async function main() {
|
|
|
843
764
|
case "search": {
|
|
844
765
|
const flags = parseSearchFlags(rest);
|
|
845
766
|
if (!flags.query) {
|
|
846
|
-
throw new FloomError("Missing search query.",
|
|
767
|
+
throw new FloomError("Missing search query.", `Try: \`${CLI_COMMAND} search "support tone"\`.`);
|
|
847
768
|
}
|
|
848
769
|
await search({
|
|
849
770
|
query: flags.query,
|
|
@@ -860,20 +781,6 @@ async function main() {
|
|
|
860
781
|
...(flags.target ? { target: flags.target } : {}),
|
|
861
782
|
setup: flags.setup,
|
|
862
783
|
force: flags.force,
|
|
863
|
-
json: flags.json,
|
|
864
|
-
});
|
|
865
|
-
if (flags.setup) {
|
|
866
|
-
await setupAgent({ target: flags.target ?? "claude", dryRun: false, yes: true });
|
|
867
|
-
}
|
|
868
|
-
return;
|
|
869
|
-
}
|
|
870
|
-
case "update": {
|
|
871
|
-
const flags = parseAddArgs(rest);
|
|
872
|
-
await install(flags.slug, {
|
|
873
|
-
...(flags.target ? { target: flags.target } : {}),
|
|
874
|
-
setup: flags.setup,
|
|
875
|
-
force: true,
|
|
876
|
-
json: flags.json,
|
|
877
784
|
});
|
|
878
785
|
if (flags.setup) {
|
|
879
786
|
await setupAgent({ target: flags.target ?? "claude", dryRun: false, yes: true });
|
|
@@ -881,10 +788,7 @@ async function main() {
|
|
|
881
788
|
return;
|
|
882
789
|
}
|
|
883
790
|
case "sync":
|
|
884
|
-
|
|
885
|
-
const flags = parseSyncFlags(rest);
|
|
886
|
-
await sync(flags);
|
|
887
|
-
}
|
|
791
|
+
await sync(parseSyncFlags(rest));
|
|
888
792
|
return;
|
|
889
793
|
case "setup":
|
|
890
794
|
case "connect": {
|
|
@@ -892,15 +796,9 @@ async function main() {
|
|
|
892
796
|
await setupAgent(flags);
|
|
893
797
|
return;
|
|
894
798
|
}
|
|
895
|
-
case "agent-prompt":
|
|
896
|
-
case "paste": {
|
|
897
|
-
const flags = parseAgentPromptArgs(rest);
|
|
898
|
-
agentPrompt(flags.target ?? "claude");
|
|
899
|
-
return;
|
|
900
|
-
}
|
|
901
799
|
case "watch": {
|
|
902
800
|
const flags = parseWatchFlags(rest);
|
|
903
|
-
await watch(flags.intervalSeconds
|
|
801
|
+
await watch(flags.intervalSeconds);
|
|
904
802
|
return;
|
|
905
803
|
}
|
|
906
804
|
case "delete":
|
|
@@ -917,29 +815,26 @@ async function main() {
|
|
|
917
815
|
const flags = parseFolderTagFlags(rest);
|
|
918
816
|
const slug = flags.rest[0];
|
|
919
817
|
if (!slug) {
|
|
920
|
-
throw new FloomError("Missing skill slug.",
|
|
818
|
+
throw new FloomError("Missing skill slug.", `Try \`${CLI_COMMAND} move support-tone --folder support/tone\`.`);
|
|
921
819
|
}
|
|
922
820
|
if (flags.folder === undefined) {
|
|
923
821
|
throw new FloomError("Missing --folder.", "Use --folder <path> or --root. Add --tag or --tags when useful.");
|
|
924
822
|
}
|
|
925
823
|
if (flags.rest.length > 1) {
|
|
926
|
-
throw new FloomError(`Unexpected argument: ${flags.rest[1]}`,
|
|
824
|
+
throw new FloomError(`Unexpected argument: ${flags.rest[1]}`, `Try \`${CLI_COMMAND} move support-tone --folder support/tone\`.`);
|
|
927
825
|
}
|
|
928
826
|
await moveSkill({ slug, folder: flags.folder, tags: flags.tags });
|
|
929
827
|
return;
|
|
930
828
|
}
|
|
931
829
|
case "mcp":
|
|
932
|
-
rejectArgs(rest,
|
|
830
|
+
rejectArgs(rest, `Try \`${CLI_COMMAND} mcp\`.`);
|
|
933
831
|
printMcpSetup();
|
|
934
832
|
return;
|
|
935
833
|
case "doctor":
|
|
936
|
-
|
|
937
|
-
const flags = parseDoctorArgs(rest);
|
|
938
|
-
await doctor(flags);
|
|
939
|
-
}
|
|
834
|
+
await doctor(parseDoctorFlags(rest));
|
|
940
835
|
return;
|
|
941
836
|
default:
|
|
942
|
-
throw new FloomError(`Unknown command: ${cmd}`,
|
|
837
|
+
throw new FloomError(`Unknown command: ${cmd}`, `Run \`${CLI_COMMAND} --help\` to see available commands.`);
|
|
943
838
|
}
|
|
944
839
|
}
|
|
945
840
|
catch (e) {
|