skillflow-ai 0.1.0 → 0.2.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
@@ -25,6 +25,9 @@ npx skillflow-ai add <slug> --agent codex # into ~/.codex/skills
25
25
  npx skillflow-ai add <slug> --project # into ./.claude/skills of this folder
26
26
  npx skillflow-ai add <slug> --dir <folder> # any folder (Antigravity and others)
27
27
  npx skillflow-ai update # update everything you installed
28
+ npx skillflow-ai themes # colour themes for the result
29
+ npx skillflow-ai add <slug> --theme hijau-daun # result in that theme (THEME.md in the skill)
30
+ npx skillflow-ai add <slug> --colors "#1F5E3B,#F2B33D" # or your own two colours
28
31
  npx skillflow-ai remove <slug>
29
32
  npx skillflow-ai whoami
30
33
  npx skillflow-ai logout
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'].includes(k)) opts[k] = argv[++i]
39
+ else if (argv[i + 1] && !argv[i + 1].startsWith('-') && ['agent', 'dir', 'server', 'kind', 'token', 'theme', 'colors'].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)
@@ -76,9 +76,25 @@ function targetDir(opts) {
76
76
  return AGENTS[agent]
77
77
  }
78
78
 
79
- async function install(slug, dir) {
79
+ // Colour theme of the result, sent as the same `?tema=` the directory's ZIP link uses. undefined = not given,
80
+ // '' = no theme (the skill's own colours; also how --theme default clears a saved one).
81
+ function themeQuery(opts) {
82
+ if (opts.colors && opts.colors !== true) {
83
+ const c = String(opts.colors).split(',').map(s => s.trim())
84
+ if (c.length !== 2 || !c.every(x => /^#?[0-9a-fA-F]{6}$/.test(x))) fail('--colors takes two hex colours: --colors "#1F5E3B,#F2B33D"')
85
+ const hex = c.map(x => (x.startsWith('#') ? x : '#' + x).toUpperCase())
86
+ return `?tema=custom&utama=${encodeURIComponent(hex[0])}&aksen=${encodeURIComponent(hex[1])}`
87
+ }
88
+ if (opts.theme === undefined) return undefined
89
+ if (opts.theme === true) fail('Name a theme: --theme <name> (see: npx skillflow-ai themes)')
90
+ if (['default', 'none'].includes(opts.theme)) return ''
91
+ if (!/^[a-z-]{1,40}$/.test(opts.theme)) fail(`"${opts.theme}" is not a theme name. See: npx skillflow-ai themes`)
92
+ return `?tema=${opts.theme}`
93
+ }
94
+
95
+ async function install(slug, dir, theme = '') {
80
96
  if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(slug)) fail(`"${slug}" is not a skill name.`)
81
- const { buf, version } = await api(`/api/skills/v1/skills/${slug}/bundle`, { raw: true })
97
+ const { buf, version } = await api(`/api/skills/v1/skills/${slug}/bundle${theme}`, { raw: true })
82
98
  const entries = readZip(buf)
83
99
  // Bundles hold "<slug>/…". Anything outside that folder is ignored.
84
100
  const own = entries.filter(e => e.name === slug + '/' || e.name.startsWith(slug + '/'))
@@ -98,7 +114,7 @@ async function install(slug, dir) {
98
114
  rmSync(final, { recursive: true, force: true })
99
115
  renameSync(tmp, final)
100
116
  const list = readJson(INSTALLED, {})
101
- list[final] = { slug, version: version || null, dir, at: new Date().toISOString() }
117
+ list[final] = { slug, version: version || null, dir, theme: theme || null, at: new Date().toISOString() }
102
118
  writePrivate(INSTALLED, list)
103
119
  return { final, version }
104
120
  }
@@ -109,10 +125,13 @@ const HELP = `skillflow ${VERSION}: install SkillFlow skills into your AI agent
109
125
  npx skillflow-ai whoami who the token belongs to
110
126
  npx skillflow-ai list [--kind video] skills you can install
111
127
  npx skillflow-ai add <slug> [<slug>…] install into ~/.claude/skills
128
+ --theme <name> | --colors "#hex,#hex" colour theme of the result (THEME.md)
112
129
  --agent codex ~/.codex/skills instead
113
130
  --project ./.claude/skills (or ./.codex/skills) in this folder
114
131
  --dir <folder> any folder (Antigravity and other agents)
115
132
  npx skillflow-ai update [<slug>] update installed skills to the latest version
133
+ --theme <name> | --colors … | --theme default change the colour theme while updating
134
+ npx skillflow-ai themes list the colour themes
116
135
  npx skillflow-ai remove <slug> [--dir <folder>] delete an installed skill
117
136
  npx skillflow-ai logout forget the token on this computer
118
137
  `
@@ -153,8 +172,9 @@ async function main() {
153
172
  case 'add': {
154
173
  if (!args.length) fail('Name a skill: npx skillflow-ai add <slug>')
155
174
  const dir = targetDir(opts)
175
+ const theme = themeQuery(opts) ?? ''
156
176
  for (const slug of args) {
157
- const r = await install(slug, dir)
177
+ const r = await install(slug, dir, theme)
158
178
  say(`✓ ${slug}${r.version ? ' v' + r.version : ''} → ${r.final}`)
159
179
  }
160
180
  break
@@ -165,16 +185,27 @@ async function main() {
165
185
  if (!want.length) { say('Nothing installed yet.'); break }
166
186
  const { skills } = await api('/api/skills/v1/catalog')
167
187
  const latest = Object.fromEntries(skills.map(s => [s.slug, s.version]))
188
+ // The theme a skill was installed with is kept; --theme / --colors replaces it (and reinstalls).
189
+ const given = themeQuery(opts)
168
190
  let n = 0
169
191
  for (const [, r] of want) {
170
192
  if (!latest[r.slug]) { say(`- ${r.slug}: no longer in the catalog (left as it is)`); continue }
171
- if (latest[r.slug] === r.version && !opts.force) continue
172
- const res = await install(r.slug, r.dir)
193
+ 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)
173
196
  say(`✓ ${r.slug} ${r.version || '?'} → ${res.version || latest[r.slug]}`); n++
174
197
  }
175
198
  say(n ? `${n} skill(s) updated.` : 'Everything is up to date.')
176
199
  break
177
200
  }
201
+ case 'themes': {
202
+ const { themes } = await api('/api/skills/v1/catalog')
203
+ if (!themes || !themes.length) { say('This directory has no colour themes yet.'); break }
204
+ for (const t of themes) say(' ' + t)
205
+ say('\nUse one with: npx skillflow-ai add <slug> --theme ' + themes[0])
206
+ say('Or your own two colours: --colors "#1F5E3B,#F2B33D". Without either, the skill keeps its own colours.')
207
+ break
208
+ }
178
209
  case 'remove': {
179
210
  if (!args.length) fail('Name a skill: npx skillflow-ai remove <slug>')
180
211
  const list = readJson(INSTALLED, {})
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillflow-ai",
3
- "version": "0.1.0",
3
+ "version": "0.2.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" },