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