@floomhq/floom 1.0.7 → 1.0.8
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 +22 -19
- package/dist/cli.js +59 -7
- package/dist/init.js +154 -6
- package/dist/publish.js +10 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,11 +4,13 @@ Publish AI skills from your terminal. Share them with a link. Add other people's
|
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
6
|
npx -y @floomhq/floom init my-skill.md
|
|
7
|
+
npx -y @floomhq/floom init brand-voice.md --template brand-voice
|
|
7
8
|
npx -y @floomhq/floom login
|
|
8
|
-
npx -y @floomhq/floom publish my-skill.md
|
|
9
|
+
npx -y @floomhq/floom publish my-skill.md --share teammate@example.com
|
|
9
10
|
npx -y @floomhq/floom share my-skill --add teammate@example.com
|
|
10
11
|
npx -y @floomhq/floom search review
|
|
11
12
|
npx -y @floomhq/floom add awesome-skill --setup
|
|
13
|
+
npx -y @floomhq/floom agent-prompt
|
|
12
14
|
npx -y @floomhq/floom setup --target claude --dry-run
|
|
13
15
|
npx -y @floomhq/floom list
|
|
14
16
|
npx -y @floomhq/floom library list
|
|
@@ -21,31 +23,32 @@ The package is designed for `npx -y @floomhq/floom ...`. Global installs expose
|
|
|
21
23
|
## Commands
|
|
22
24
|
|
|
23
25
|
- `npx -y @floomhq/floom login` — sign in with Google. New accounts are created on first login. Token stored at `~/.floom/config.json`.
|
|
24
|
-
- `floom init [file.md]` — create a starter skill file.
|
|
25
|
-
- `npx -y @floomhq/floom publish <file.md>` — upload a Markdown file. Optional `--public` / `--private` / `--unlisted`, `--type knowledge|instruction|workflow|skill`, `--installs-as <target>`,
|
|
26
|
-
- `floom share <slug>` — email-share one of your skills. Optional `--add <email>`, `--remove <email>`, and `--list`.
|
|
27
|
-
- `floom list` — show your published skills. Optional `--json`.
|
|
26
|
+
- `npx -y @floomhq/floom init [file.md]` — create a starter skill file. Optional `--template generic|brand-voice|pr-review|sales|support|onboarding`.
|
|
27
|
+
- `npx -y @floomhq/floom publish <file.md>` — scan and upload a Markdown file. Optional `--public` / `--private` / `--unlisted`, `--type knowledge|instruction|workflow|skill`, `--installs-as <target>`, `--skill-version <label>`, and `--share <email>`. `--share` sends the normal link by email; no account is needed to add unlisted or public links.
|
|
28
|
+
- `npx -y @floomhq/floom share <slug>` — email-share one of your skills. Optional `--add <email>`, `--remove <email>`, and `--list`.
|
|
29
|
+
- `npx -y @floomhq/floom list` — show your published skills. Optional `--json`.
|
|
28
30
|
- `npx -y @floomhq/floom add <url-or-slug>` — fetch a skill into `~/.claude/skills/<slug>.md`. Optional `--setup` connects Claude Code and `--force` replaces an existing local copy.
|
|
29
|
-
- `floom info <url-or-slug>` — show skill metadata. Optional `--json`.
|
|
30
|
-
- `floom search <query>` — search public skills and starter libraries. Optional `--library <slug>`, `--type knowledge|instruction|workflow|skill`, and `--json`.
|
|
31
|
-
- `floom
|
|
32
|
-
- `floom
|
|
33
|
-
- `floom
|
|
34
|
-
- `floom
|
|
35
|
-
- `floom
|
|
36
|
-
- `floom
|
|
37
|
-
- `floom library
|
|
38
|
-
- `floom library
|
|
39
|
-
- `floom library
|
|
40
|
-
- `floom
|
|
41
|
-
- `floom
|
|
31
|
+
- `npx -y @floomhq/floom info <url-or-slug>` — show skill metadata. Optional `--json`.
|
|
32
|
+
- `npx -y @floomhq/floom search <query>` — search public skills and starter libraries. Optional `--library <slug>`, `--type knowledge|instruction|workflow|skill`, and `--json`.
|
|
33
|
+
- `npx -y @floomhq/floom agent-prompt` — print the sentence to paste into Claude Code or Codex.
|
|
34
|
+
- `npx -y @floomhq/floom setup` — add Floom usage guidance to `CLAUDE.md` or `AGENTS.md`. Optional `--target claude|codex`, `--dry-run`, `--yes`.
|
|
35
|
+
- `npx -y @floomhq/floom connect` — alias for setup.
|
|
36
|
+
- `npx -y @floomhq/floom mcp` — print MCP setup commands for supported agent CLIs.
|
|
37
|
+
- `npx -y @floomhq/floom sync` — preview: pull your published, saved, and subscribed library skills into `~/.claude/skills/`.
|
|
38
|
+
- `npx -y @floomhq/floom watch` — preview: run sync repeatedly. Optional `--interval <seconds>`; minimum `10`.
|
|
39
|
+
- `npx -y @floomhq/floom library list` — list public starter libraries. Optional `--json`.
|
|
40
|
+
- `npx -y @floomhq/floom library create <slug> --name <name>` — create a personal or starter library. Optional `--public` / `--private` / `--unlisted`.
|
|
41
|
+
- `npx -y @floomhq/floom library add <library> <skill> [--folder <path>] [--tags a,b]` — add a skill to a library.
|
|
42
|
+
- `npx -y @floomhq/floom library subscribe <slug>` — subscribe to a public or unlisted library so sync can pull it locally.
|
|
43
|
+
- `npx -y @floomhq/floom move <slug> --folder <path>` — set your local folder override for a saved or library skill.
|
|
44
|
+
- `npx -y @floomhq/floom delete <url-or-slug>` — delete one of your published skills. Optional `--yes`.
|
|
42
45
|
- `npx -y @floomhq/floom doctor` — diagnose your Floom setup.
|
|
43
46
|
- `floom whoami` — show the signed-in account.
|
|
44
47
|
- `floom logout` — delete local credentials.
|
|
45
48
|
|
|
46
49
|
## Skill format
|
|
47
50
|
|
|
48
|
-
Optional YAML
|
|
51
|
+
Optional YAML frontmatter (`title`, `description`, `version`, `type`, `installs_as`), then freeform Markdown.
|
|
49
52
|
|
|
50
53
|
```markdown
|
|
51
54
|
---
|
package/dist/cli.js
CHANGED
|
@@ -76,10 +76,12 @@ function commandUsage() {
|
|
|
76
76
|
|
|
77
77
|
${c.dim("Publishing")}
|
|
78
78
|
${c.cyan("init")} ${c.dim("[path]")} Create a skill scaffold
|
|
79
|
+
${c.dim("Flags: --template generic|brand-voice|pr-review|sales|support|onboarding")}
|
|
79
80
|
${c.cyan("scan")} ${c.dim("<path>")} Check for secrets, injection, exfiltration
|
|
80
81
|
${c.cyan("publish")} ${c.dim("<path>")} Scan, publish, and print a share link
|
|
81
82
|
${c.dim("Flags: --public, --private, --type knowledge|instruction|workflow|skill")}
|
|
82
|
-
${c.dim(" --skill-version <label>")}
|
|
83
|
+
${c.dim(" --skill-version <label>, --share <email>")}
|
|
84
|
+
${c.dim(" --share emails the normal link; no account is needed to add it")}
|
|
83
85
|
${c.cyan("share")} ${c.dim("<slug>")} Email-share one of your skills
|
|
84
86
|
${c.dim("Flags: --add <email>, --remove <email>, --list")}
|
|
85
87
|
|
|
@@ -95,6 +97,8 @@ function commandUsage() {
|
|
|
95
97
|
${c.cyan("setup")} Configure Claude Code or Codex instructions
|
|
96
98
|
${c.dim("Alias: connect")}
|
|
97
99
|
${c.dim("Flags: --target claude|codex, --yes, --dry-run")}
|
|
100
|
+
${c.cyan("agent-prompt")} Print the sentence to paste into your agent
|
|
101
|
+
${c.dim("Alias: paste")}
|
|
98
102
|
${c.cyan("doctor")} Troubleshoot auth, API, and local folders
|
|
99
103
|
|
|
100
104
|
${c.dim("Advanced")}
|
|
@@ -108,6 +112,7 @@ function commandUsage() {
|
|
|
108
112
|
${c.bold("Examples")}
|
|
109
113
|
${c.cyan("npx -y @floomhq/floom add")} ${c.dim("https://floom.dev/s/ffas93ud --setup")}
|
|
110
114
|
${c.cyan("npx -y @floomhq/floom publish")} ${c.dim("support-tone.md --type instruction --public")}
|
|
115
|
+
${c.cyan("npx -y @floomhq/floom publish")} ${c.dim("support-tone.md --share teammate@example.com")}
|
|
111
116
|
${c.cyan("npx -y @floomhq/floom setup")} ${c.dim("--target claude --yes")}
|
|
112
117
|
|
|
113
118
|
${c.bold("Help")}
|
|
@@ -120,6 +125,7 @@ function commandUsage() {
|
|
|
120
125
|
process.stdout.write(out);
|
|
121
126
|
}
|
|
122
127
|
const ASSET_TYPES = new Set(["knowledge", "instruction", "workflow", "skill"]);
|
|
128
|
+
const INIT_TEMPLATES = new Set(["generic", "brand-voice", "pr-review", "sales", "support", "onboarding"]);
|
|
123
129
|
const INSTALL_TARGETS = new Set([
|
|
124
130
|
"claude_skill",
|
|
125
131
|
"memory",
|
|
@@ -140,7 +146,7 @@ function readFlagValue(argv, index, flag) {
|
|
|
140
146
|
return { value, nextIndex: index + 1 };
|
|
141
147
|
}
|
|
142
148
|
function parseFlags(argv) {
|
|
143
|
-
const out = { visibility: "unlisted", update: false, rest: [] };
|
|
149
|
+
const out = { visibility: "unlisted", update: false, shareEmails: [], explicitVisibility: false, rest: [] };
|
|
144
150
|
let visibilityFlag = null;
|
|
145
151
|
for (let i = 0; i < argv.length; i++) {
|
|
146
152
|
const a = argv[i] ?? "";
|
|
@@ -151,12 +157,15 @@ function parseFlags(argv) {
|
|
|
151
157
|
}
|
|
152
158
|
visibilityFlag = nextVisibility;
|
|
153
159
|
out.visibility = nextVisibility;
|
|
160
|
+
out.explicitVisibility = true;
|
|
154
161
|
}
|
|
155
162
|
else if (a === "--update") {
|
|
156
163
|
throw new FloomError(V1_NOT_AVAILABLE, "`floom publish --update` is planned for a later Floom release.");
|
|
157
164
|
}
|
|
158
165
|
else if (a === "--share" || a.startsWith("--share=")) {
|
|
159
|
-
|
|
166
|
+
const { value, nextIndex } = readFlagValue(argv, i, "--share");
|
|
167
|
+
out.shareEmails.push(...parseEmailList(value, "--share"));
|
|
168
|
+
i = nextIndex;
|
|
160
169
|
}
|
|
161
170
|
else if (a === "--type" || a.startsWith("--type=")) {
|
|
162
171
|
const { value, nextIndex } = readFlagValue(argv, i, "--type");
|
|
@@ -191,8 +200,31 @@ function parseFlags(argv) {
|
|
|
191
200
|
else
|
|
192
201
|
out.rest.push(a);
|
|
193
202
|
}
|
|
203
|
+
if (out.shareEmails.length > 0) {
|
|
204
|
+
out.shareEmails = dedupeEmails(out.shareEmails);
|
|
205
|
+
if (out.shareEmails.length > 200) {
|
|
206
|
+
throw new FloomError("Too many --share recipients.", "Use 200 email addresses or fewer.");
|
|
207
|
+
}
|
|
208
|
+
if (out.visibility === "private") {
|
|
209
|
+
throw new FloomError("`--private --share` would email a link recipients cannot open.", "Use `--unlisted --share` for invite emails, or `floom share <slug> --add <email>` for email-gated access after publishing.");
|
|
210
|
+
}
|
|
211
|
+
}
|
|
194
212
|
return out;
|
|
195
213
|
}
|
|
214
|
+
function parseEmailList(value, source) {
|
|
215
|
+
const emails = value.split(",").map((email) => email.trim().toLowerCase()).filter(Boolean);
|
|
216
|
+
if (emails.length === 0)
|
|
217
|
+
throw new FloomError(`Missing value for ${source}.`, `Try \`${source} teammate@example.com\`.`);
|
|
218
|
+
for (const email of emails) {
|
|
219
|
+
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
|
|
220
|
+
throw new FloomError(`Invalid email for ${source}: ${email}`, "Use an address like teammate@example.com.");
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return emails;
|
|
224
|
+
}
|
|
225
|
+
function dedupeEmails(emails) {
|
|
226
|
+
return [...new Set(emails)];
|
|
227
|
+
}
|
|
196
228
|
function parseShareFlags(argv) {
|
|
197
229
|
const out = { list: false, add: [], remove: [] };
|
|
198
230
|
for (let i = 0; i < argv.length; i++) {
|
|
@@ -232,16 +264,27 @@ function parseShareFlags(argv) {
|
|
|
232
264
|
}
|
|
233
265
|
function parseInitArgs(argv) {
|
|
234
266
|
let file;
|
|
235
|
-
|
|
267
|
+
let template = "generic";
|
|
268
|
+
for (let i = 0; i < argv.length; i++) {
|
|
269
|
+
const a = argv[i] ?? "";
|
|
270
|
+
if (a === "--template" || a.startsWith("--template=")) {
|
|
271
|
+
const { value, nextIndex } = readFlagValue(argv, i, "--template");
|
|
272
|
+
if (!INIT_TEMPLATES.has(value)) {
|
|
273
|
+
throw new FloomError(`Invalid --template: ${value}`, "Use one of: generic, brand-voice, pr-review, sales, support, onboarding.");
|
|
274
|
+
}
|
|
275
|
+
template = value;
|
|
276
|
+
i = nextIndex;
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
236
279
|
if (a.startsWith("--")) {
|
|
237
|
-
throw new FloomError(`Unknown flag: ${a}`, "Try `floom init skill.md`.");
|
|
280
|
+
throw new FloomError(`Unknown flag: ${a}`, "Try `floom init skill.md --template brand-voice`.");
|
|
238
281
|
}
|
|
239
282
|
if (file) {
|
|
240
283
|
throw new FloomError(`Unexpected argument: ${a}`, "Try `floom init skill.md`.");
|
|
241
284
|
}
|
|
242
285
|
file = a;
|
|
243
286
|
}
|
|
244
|
-
return file ? { file } : {};
|
|
287
|
+
return file ? { file, template } : { template };
|
|
245
288
|
}
|
|
246
289
|
function parseListFlags(argv) {
|
|
247
290
|
const out = { json: false };
|
|
@@ -566,6 +609,9 @@ function parseSingleFileArg(argv, usageHint) {
|
|
|
566
609
|
throw new FloomError("Missing file argument.", usageHint);
|
|
567
610
|
return file;
|
|
568
611
|
}
|
|
612
|
+
function agentPrompt() {
|
|
613
|
+
process.stdout.write("Use my installed Floom skills when they fit the task. Search ~/.claude/skills first.\n");
|
|
614
|
+
}
|
|
569
615
|
function sleep(ms, signal) {
|
|
570
616
|
if (signal.aborted)
|
|
571
617
|
return Promise.resolve();
|
|
@@ -655,7 +701,7 @@ async function main() {
|
|
|
655
701
|
return;
|
|
656
702
|
case "init": {
|
|
657
703
|
const flags = parseInitArgs(rest);
|
|
658
|
-
await init(flags.file);
|
|
704
|
+
await init(flags.file, flags.template);
|
|
659
705
|
return;
|
|
660
706
|
}
|
|
661
707
|
case "publish": {
|
|
@@ -674,6 +720,7 @@ async function main() {
|
|
|
674
720
|
...(flags.assetType ? { assetType: flags.assetType } : {}),
|
|
675
721
|
...(flags.installsAs ? { installsAs: flags.installsAs } : {}),
|
|
676
722
|
...(flags.version ? { version: flags.version } : {}),
|
|
723
|
+
...(flags.shareEmails.length > 0 ? { sharedWithEmails: flags.shareEmails } : {}),
|
|
677
724
|
});
|
|
678
725
|
return;
|
|
679
726
|
}
|
|
@@ -740,6 +787,11 @@ async function main() {
|
|
|
740
787
|
await setupAgent(flags);
|
|
741
788
|
return;
|
|
742
789
|
}
|
|
790
|
+
case "agent-prompt":
|
|
791
|
+
case "paste":
|
|
792
|
+
rejectArgs(rest, "Try `floom agent-prompt`.");
|
|
793
|
+
agentPrompt();
|
|
794
|
+
return;
|
|
743
795
|
case "watch": {
|
|
744
796
|
const flags = parseWatchFlags(rest);
|
|
745
797
|
await watch(flags.intervalSeconds);
|
package/dist/init.js
CHANGED
|
@@ -4,7 +4,8 @@ import { createInterface } from "node:readline/promises";
|
|
|
4
4
|
import { stdin as input, stdout as output } from "node:process";
|
|
5
5
|
import { c, symbols } from "./ui.js";
|
|
6
6
|
import { FloomError } from "./errors.js";
|
|
7
|
-
const
|
|
7
|
+
const TEMPLATES = {
|
|
8
|
+
generic: `---
|
|
8
9
|
title:
|
|
9
10
|
description:
|
|
10
11
|
version: 1.0
|
|
@@ -26,10 +27,156 @@ version: 1.0
|
|
|
26
27
|
|
|
27
28
|
|
|
28
29
|
# Examples
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
`,
|
|
31
|
+
"brand-voice": `---
|
|
32
|
+
title: Brand voice
|
|
33
|
+
description: Help an agent write in our company voice.
|
|
34
|
+
type: knowledge
|
|
35
|
+
installs_as: memory
|
|
36
|
+
version: 1.0
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
# Brand Voice
|
|
40
|
+
|
|
41
|
+
## Use when
|
|
42
|
+
- Writing customer-facing copy
|
|
43
|
+
- Rewriting drafts to match our tone
|
|
44
|
+
- Reviewing messaging before it ships
|
|
45
|
+
|
|
46
|
+
## Voice rules
|
|
47
|
+
- Sound clear, direct, and useful.
|
|
48
|
+
- Prefer concrete nouns and short sentences.
|
|
49
|
+
- Avoid hype, filler, and generic AI language.
|
|
50
|
+
|
|
51
|
+
## Words we use
|
|
52
|
+
- Replace this list with approved terms.
|
|
53
|
+
|
|
54
|
+
## Words we avoid
|
|
55
|
+
- Replace this list with banned or overused terms.
|
|
56
|
+
|
|
57
|
+
## Examples
|
|
58
|
+
- Before:
|
|
59
|
+
- After:
|
|
60
|
+
`,
|
|
61
|
+
"pr-review": `---
|
|
62
|
+
title: PR review
|
|
63
|
+
description: Review code changes with risk-first feedback.
|
|
64
|
+
type: workflow
|
|
65
|
+
installs_as: claude_skill
|
|
66
|
+
version: 1.0
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
# PR Review
|
|
70
|
+
|
|
71
|
+
## Use when
|
|
72
|
+
- Reviewing a pull request or diff
|
|
73
|
+
- Checking a change before merge
|
|
74
|
+
|
|
75
|
+
## Review order
|
|
76
|
+
1. Correctness and regressions
|
|
77
|
+
2. Security and data safety
|
|
78
|
+
3. Tests and missing edge cases
|
|
79
|
+
4. Maintainability
|
|
80
|
+
|
|
81
|
+
## Output
|
|
82
|
+
- Lead with findings.
|
|
83
|
+
- Include file paths and line references.
|
|
84
|
+
- Keep style comments out unless they affect behavior.
|
|
85
|
+
- If no issues are found, say that clearly and name any test gaps.
|
|
86
|
+
`,
|
|
87
|
+
sales: `---
|
|
88
|
+
title: Sales research
|
|
89
|
+
description: Prepare concise account research and outreach context.
|
|
90
|
+
type: workflow
|
|
91
|
+
installs_as: memory
|
|
92
|
+
version: 1.0
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
# Sales Research
|
|
96
|
+
|
|
97
|
+
## Use when
|
|
98
|
+
- Preparing for a prospect call
|
|
99
|
+
- Writing a relevant outbound message
|
|
100
|
+
- Summarizing account context for the team
|
|
101
|
+
|
|
102
|
+
## Gather
|
|
103
|
+
- Company
|
|
104
|
+
- Buyer persona
|
|
105
|
+
- Recent trigger
|
|
106
|
+
- Likely pain
|
|
107
|
+
- Existing tools or workflow
|
|
108
|
+
|
|
109
|
+
## Output
|
|
110
|
+
- 5 bullet account summary
|
|
111
|
+
- 3 likely pain points
|
|
112
|
+
- 2 tailored opener angles
|
|
113
|
+
- 1 clear next action
|
|
114
|
+
`,
|
|
115
|
+
support: `---
|
|
116
|
+
title: Support tone
|
|
117
|
+
description: Answer support tickets with a clear and calm company voice.
|
|
118
|
+
type: instruction
|
|
119
|
+
installs_as: memory
|
|
120
|
+
version: 1.0
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
# Support Tone
|
|
124
|
+
|
|
125
|
+
## Use when
|
|
126
|
+
- Replying to customer support messages
|
|
127
|
+
- Summarizing customer issues
|
|
128
|
+
- Drafting escalation notes
|
|
129
|
+
|
|
130
|
+
## Rules
|
|
131
|
+
- Acknowledge the issue in plain language.
|
|
132
|
+
- Give the next concrete step.
|
|
133
|
+
- Do not over-apologize.
|
|
134
|
+
- Do not invent product behavior.
|
|
135
|
+
- Escalate when data, billing, or account access is involved.
|
|
136
|
+
|
|
137
|
+
## Reply shape
|
|
138
|
+
1. Short acknowledgement
|
|
139
|
+
2. Direct answer or next step
|
|
140
|
+
3. What happens next
|
|
141
|
+
`,
|
|
142
|
+
onboarding: `---
|
|
143
|
+
title: Team onboarding
|
|
144
|
+
description: Help a new teammate understand how this team works.
|
|
145
|
+
type: knowledge
|
|
146
|
+
installs_as: memory
|
|
147
|
+
version: 1.0
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
# Team Onboarding
|
|
151
|
+
|
|
152
|
+
## Use when
|
|
153
|
+
- A new teammate asks how work gets done
|
|
154
|
+
- An agent needs company or team context
|
|
155
|
+
- Creating first-week task plans
|
|
156
|
+
|
|
157
|
+
## Team context
|
|
158
|
+
- Mission:
|
|
159
|
+
- Customers:
|
|
160
|
+
- Current priorities:
|
|
161
|
+
- Tools:
|
|
162
|
+
|
|
163
|
+
## How we work
|
|
164
|
+
- Decision rules:
|
|
165
|
+
- Review process:
|
|
166
|
+
- Communication norms:
|
|
167
|
+
- Definition of done:
|
|
168
|
+
|
|
169
|
+
## First-week checklist
|
|
170
|
+
- Read:
|
|
171
|
+
- Set up:
|
|
172
|
+
- Ask:
|
|
173
|
+
- Ship:
|
|
174
|
+
`,
|
|
175
|
+
};
|
|
176
|
+
export async function init(filename, template = "generic") {
|
|
31
177
|
const target = filename ?? "skill.md";
|
|
32
178
|
const filePath = resolve(process.cwd(), target);
|
|
179
|
+
const body = TEMPLATES[template];
|
|
33
180
|
const exists = await fileExists(filePath);
|
|
34
181
|
if (exists) {
|
|
35
182
|
if (!process.stdin.isTTY) {
|
|
@@ -44,7 +191,7 @@ export async function init(filename) {
|
|
|
44
191
|
}
|
|
45
192
|
}
|
|
46
193
|
try {
|
|
47
|
-
await writeFile(filePath,
|
|
194
|
+
await writeFile(filePath, body, "utf8");
|
|
48
195
|
}
|
|
49
196
|
catch (err) {
|
|
50
197
|
const code = err.code;
|
|
@@ -57,10 +204,11 @@ export async function init(filename) {
|
|
|
57
204
|
throw new FloomError(`Couldn't create ${target}: ${err.message}`);
|
|
58
205
|
}
|
|
59
206
|
process.stdout.write(`\n${symbols.ok} Created ${c.bold(basename(filePath))}\n`);
|
|
207
|
+
process.stdout.write(` ${c.dim(`Template: ${template}`)}\n`);
|
|
60
208
|
process.stdout.write(`\n ${c.bold("Next")}\n`);
|
|
61
209
|
process.stdout.write(` ${c.dim("1.")} Fill in the title, description, and instructions.\n`);
|
|
62
|
-
process.stdout.write(` ${c.dim("2.")} Check it: ${c.cyan(`floom scan ${shellQuote(target)}`)}\n`);
|
|
63
|
-
process.stdout.write(` ${c.dim("3.")} Publish: ${c.cyan(`floom publish ${shellQuote(target)} --
|
|
210
|
+
process.stdout.write(` ${c.dim("2.")} Check it: ${c.cyan(`npx -y @floomhq/floom scan ${shellQuote(target)}`)}\n`);
|
|
211
|
+
process.stdout.write(` ${c.dim("3.")} Publish: ${c.cyan(`npx -y @floomhq/floom publish ${shellQuote(target)} --public`)}\n\n`);
|
|
64
212
|
}
|
|
65
213
|
function shellQuote(value) {
|
|
66
214
|
if (/^[A-Za-z0-9_./:@%+=,-]+$/.test(value))
|
package/dist/publish.js
CHANGED
|
@@ -192,10 +192,15 @@ export async function publish(opts) {
|
|
|
192
192
|
spinner.stop();
|
|
193
193
|
const versionTag = version ? c.dim(` (${formatVersionLabel(version)})`) : "";
|
|
194
194
|
const titleLabel = data.title ? `"${data.title}"` : opts.file;
|
|
195
|
+
const invitedEmails = opts.sharedWithEmails ?? [];
|
|
195
196
|
process.stdout.write(`\n${symbols.ok} Published ${c.bold(titleLabel)}${versionTag}\n\n`);
|
|
197
|
+
process.stdout.write(` ${c.bold("Send this to someone:")}\n`);
|
|
196
198
|
process.stdout.write(` ${c.cyan(humanUrl)}\n\n`);
|
|
197
|
-
|
|
198
|
-
|
|
199
|
+
process.stdout.write(` ${c.bold("They run:")}\n`);
|
|
200
|
+
process.stdout.write(` ${c.cyan(`npx -y @floomhq/floom add ${humanUrl} --setup`)}\n\n`);
|
|
201
|
+
if (invitedEmails.length) {
|
|
202
|
+
process.stdout.write(` ${c.bold("Email invite:")}\n`);
|
|
203
|
+
process.stdout.write(` ${c.dim(invitedEmails.join(", "))}\n\n`);
|
|
199
204
|
}
|
|
200
205
|
let copied = false;
|
|
201
206
|
try {
|
|
@@ -206,14 +211,11 @@ export async function publish(opts) {
|
|
|
206
211
|
copied = false;
|
|
207
212
|
}
|
|
208
213
|
if (copied) {
|
|
209
|
-
process.stdout.write(` ${c.dim("Copied to clipboard.
|
|
214
|
+
process.stdout.write(` ${c.dim("Copied link to clipboard.")}\n\n`);
|
|
210
215
|
}
|
|
211
216
|
else {
|
|
212
217
|
process.stdout.write(` ${c.dim("Share it anywhere.")}\n\n`);
|
|
213
218
|
}
|
|
214
|
-
process.stdout.write(` ${c.bold("
|
|
215
|
-
process.stdout.write(` ${c.
|
|
216
|
-
process.stdout.write(` ${c.dim("2.")} Send the link.\n`);
|
|
217
|
-
process.stdout.write(` ${c.dim("3.")} Receiver runs ${c.cyan(`npx -y @floomhq/floom add ${humanUrl} --setup`)}\n`);
|
|
218
|
-
process.stdout.write(` ${c.dim("4.")} Agent reads the installed Markdown from the local skills folder.\n\n`);
|
|
219
|
+
process.stdout.write(` ${c.bold("Agent prompt:")}\n`);
|
|
220
|
+
process.stdout.write(` ${c.cyan("npx -y @floomhq/floom agent-prompt")}\n\n`);
|
|
219
221
|
}
|