@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 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>`, and `--skill-version <label>`.
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 setup` — add Floom usage guidance to `CLAUDE.md` or `AGENTS.md`. Optional `--target claude|codex`, `--dry-run`, `--yes`.
32
- - `floom connect` — alias for `floom setup`.
33
- - `floom mcp` — print MCP setup commands for supported agent CLIs.
34
- - `floom sync` — preview: pull your published, saved, and subscribed library skills into `~/.claude/skills/`.
35
- - `floom watch` — preview: run `floom sync` repeatedly. Optional `--interval <seconds>`; minimum `10`.
36
- - `floom library list` — list public starter libraries. Optional `--json`.
37
- - `floom library create <slug> --name <name>` create a personal or starter library. Optional `--public` / `--private` / `--unlisted`.
38
- - `floom library add <library> <skill> [--folder <path>] [--tags a,b]` add a skill to a library.
39
- - `floom library subscribe <slug>` subscribe to a public or unlisted library so sync can pull it locally.
40
- - `floom move <slug> --folder <path>` — set your local folder override for a saved or library skill.
41
- - `floom delete <url-or-slug>` — delete one of your published skills. Optional `--yes`.
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-ish frontmatter (`title`, `description`, `version`), then freeform Markdown.
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
- throw new FloomError(V1_NOT_AVAILABLE, "`floom publish --share` is planned for a later Floom release.");
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
- for (const a of argv) {
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 TEMPLATE = `---
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
- export async function init(filename) {
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, TEMPLATE, "utf8");
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)} --type instruction --public`)}\n\n`);
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
- if (data.visibility === "shared" && data.shared_with_emails?.length) {
198
- process.stdout.write(` ${c.dim(`Shared with ${data.shared_with_emails.join(", ")}`)}\n\n`);
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. Share it anywhere.")}\n\n`);
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("Next")}\n`);
215
- process.stdout.write(` ${c.dim("1.")} Test locally: ${c.cyan(`npx -y @floomhq/floom add ${humanUrl} --setup`)}\n`);
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@floomhq/floom",
3
- "version": "1.0.7",
3
+ "version": "1.0.8",
4
4
  "description": "Publish AI skills from your terminal. Share with a link.",
5
5
  "license": "MIT",
6
6
  "type": "module",