skillflow-ai 0.2.0 → 0.3.0

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
@@ -28,6 +28,8 @@ npx skillflow-ai update # update everything you installed
28
28
  npx skillflow-ai themes # colour themes for the result
29
29
  npx skillflow-ai add <slug> --theme hijau-daun # result in that theme (THEME.md in the skill)
30
30
  npx skillflow-ai add <slug> --colors "#1F5E3B,#F2B33D" # or your own two colours
31
+ npx skillflow-ai add <slug> --antislop=core,ui,copywriting # Anti Slop rules in the skill (antislop/)
32
+ npx skillflow-ai antislop # the Anti Slop parts
31
33
  npx skillflow-ai remove <slug>
32
34
  npx skillflow-ai whoami
33
35
  npx skillflow-ai logout
@@ -35,4 +37,6 @@ npx skillflow-ai logout
35
37
 
36
38
  If you have a brand profile on the directory, every skill you add carries it as `BRAND.md`.
37
39
 
40
+ `--antislop` adds the [Anti Slop](https://github.com/miqdadbadjuber/anti-slop) rules (MIT) as an `antislop/` folder the skill reads before it starts. The core is always included; parts: `ui`, `copywriting`, `human`, `mobile`, `code`. `update` keeps the choice; `--antislop=off` removes it.
41
+
38
42
  The token stops working when your account leaves Pro, or when you revoke it on the directory.
package/bin/skillflow.mjs CHANGED
@@ -36,7 +36,7 @@ function parse(argv) {
36
36
  if (a.startsWith('--')) {
37
37
  const [k, v] = a.slice(2).split('=')
38
38
  if (v !== undefined) opts[k] = v
39
- else if (argv[i + 1] && !argv[i + 1].startsWith('-') && ['agent', 'dir', 'server', 'kind', 'token', 'theme', 'colors'].includes(k)) opts[k] = argv[++i]
39
+ else if (argv[i + 1] && !argv[i + 1].startsWith('-') && ['agent', 'dir', 'server', 'kind', 'token', 'theme', 'colors', 'antislop'].includes(k)) opts[k] = argv[++i]
40
40
  else opts[k] = true
41
41
  } else if (a === '-h') opts.help = true
42
42
  else args.push(a)
@@ -92,9 +92,21 @@ function themeQuery(opts) {
92
92
  return `?tema=${opts.theme}`
93
93
  }
94
94
 
95
- async function install(slug, dir, theme = '') {
95
+ // Anti Slop rules (antislop/ folder in the skill), sent as the same `?antislop=` the directory's ZIP link uses.
96
+ // undefined = not given, '' = off (also how --antislop=off clears a saved choice). The core is always part of it.
97
+ function antislopValue(opts) {
98
+ if (opts.antislop === undefined) return undefined
99
+ if (opts.antislop === true) return 'core'
100
+ const v = String(opts.antislop).toLowerCase().replace(/\s+/g, '')
101
+ if (['off', 'none', 'no'].includes(v)) return ''
102
+ if (!/^[a-z]+(,[a-z]+)*$/.test(v)) fail('--antislop takes parts separated by commas: --antislop=core,ui,copywriting (see: npx skillflow-ai antislop)')
103
+ return v
104
+ }
105
+
106
+ async function install(slug, dir, theme = '', antislop = '') {
96
107
  if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(slug)) fail(`"${slug}" is not a skill name.`)
97
- const { buf, version } = await api(`/api/skills/v1/skills/${slug}/bundle${theme}`, { raw: true })
108
+ const q = [theme.replace(/^\?/, ''), antislop ? 'antislop=' + encodeURIComponent(antislop) : ''].filter(Boolean).join('&')
109
+ const { buf, version } = await api(`/api/skills/v1/skills/${slug}/bundle${q ? '?' + q : ''}`, { raw: true })
98
110
  const entries = readZip(buf)
99
111
  // Bundles hold "<slug>/…". Anything outside that folder is ignored.
100
112
  const own = entries.filter(e => e.name === slug + '/' || e.name.startsWith(slug + '/'))
@@ -114,7 +126,7 @@ async function install(slug, dir, theme = '') {
114
126
  rmSync(final, { recursive: true, force: true })
115
127
  renameSync(tmp, final)
116
128
  const list = readJson(INSTALLED, {})
117
- list[final] = { slug, version: version || null, dir, theme: theme || null, at: new Date().toISOString() }
129
+ list[final] = { slug, version: version || null, dir, theme: theme || null, antislop: antislop || null, at: new Date().toISOString() }
118
130
  writePrivate(INSTALLED, list)
119
131
  return { final, version }
120
132
  }
@@ -126,12 +138,15 @@ const HELP = `skillflow ${VERSION}: install SkillFlow skills into your AI agent
126
138
  npx skillflow-ai list [--kind video] skills you can install
127
139
  npx skillflow-ai add <slug> [<slug>…] install into ~/.claude/skills
128
140
  --theme <name> | --colors "#hex,#hex" colour theme of the result (THEME.md)
141
+ --antislop=core,ui,copywriting Anti Slop rules in the skill (antislop/)
129
142
  --agent codex ~/.codex/skills instead
130
143
  --project ./.claude/skills (or ./.codex/skills) in this folder
131
144
  --dir <folder> any folder (Antigravity and other agents)
132
145
  npx skillflow-ai update [<slug>] update installed skills to the latest version
133
146
  --theme <name> | --colors … | --theme default change the colour theme while updating
147
+ --antislop=<parts> | --antislop=off change the Anti Slop rules while updating
134
148
  npx skillflow-ai themes list the colour themes
149
+ npx skillflow-ai antislop list the Anti Slop parts
135
150
  npx skillflow-ai remove <slug> [--dir <folder>] delete an installed skill
136
151
  npx skillflow-ai logout forget the token on this computer
137
152
  `
@@ -173,8 +188,9 @@ async function main() {
173
188
  if (!args.length) fail('Name a skill: npx skillflow-ai add <slug>')
174
189
  const dir = targetDir(opts)
175
190
  const theme = themeQuery(opts) ?? ''
191
+ const antislop = antislopValue(opts) ?? ''
176
192
  for (const slug of args) {
177
- const r = await install(slug, dir, theme)
193
+ const r = await install(slug, dir, theme, antislop)
178
194
  say(`✓ ${slug}${r.version ? ' v' + r.version : ''} → ${r.final}`)
179
195
  }
180
196
  break
@@ -187,17 +203,26 @@ async function main() {
187
203
  const latest = Object.fromEntries(skills.map(s => [s.slug, s.version]))
188
204
  // The theme a skill was installed with is kept; --theme / --colors replaces it (and reinstalls).
189
205
  const given = themeQuery(opts)
206
+ const givenAs = antislopValue(opts)
190
207
  let n = 0
191
208
  for (const [, r] of want) {
192
209
  if (!latest[r.slug]) { say(`- ${r.slug}: no longer in the catalog (left as it is)`); continue }
193
210
  const theme = given ?? (r.theme || '')
194
- if (latest[r.slug] === r.version && !opts.force && theme === (r.theme || '')) continue
195
- const res = await install(r.slug, r.dir, theme)
211
+ const antislop = givenAs ?? (r.antislop || '')
212
+ if (latest[r.slug] === r.version && !opts.force && theme === (r.theme || '') && antislop === (r.antislop || '')) continue
213
+ const res = await install(r.slug, r.dir, theme, antislop)
196
214
  say(`✓ ${r.slug} ${r.version || '?'} → ${res.version || latest[r.slug]}`); n++
197
215
  }
198
216
  say(n ? `${n} skill(s) updated.` : 'Everything is up to date.')
199
217
  break
200
218
  }
219
+ case 'antislop': {
220
+ const { antislop } = await api('/api/skills/v1/catalog')
221
+ if (!antislop || !antislop.length) { say('This directory has no Anti Slop rules yet (update the server).'); break }
222
+ for (const k of antislop) say(' ' + k)
223
+ say('\nUse with: npx skillflow-ai add <slug> --antislop=core,ui (the core is always included). Rules: https://github.com/miqdadbadjuber/anti-slop (MIT)')
224
+ break
225
+ }
201
226
  case 'themes': {
202
227
  const { themes } = await api('/api/skills/v1/catalog')
203
228
  if (!themes || !themes.length) { say('This directory has no colour themes yet.'); break }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillflow-ai",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Install SkillFlow AI skills into Claude Code, Codex or any agent folder with one command.",
5
5
  "type": "module",
6
6
  "bin": { "skillflow-ai": "bin/skillflow.mjs", "skillflow": "bin/skillflow.mjs" },