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 +3 -0
- package/bin/skillflow.mjs +38 -7
- package/package.json +1 -1
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
|
-
|
|
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
|
-
|
|
172
|
-
|
|
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.
|
|
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" },
|