@victortomaili/skill-cli 0.1.0 → 0.1.1
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/CHANGELOG.md +7 -4
- package/package.json +1 -1
- package/src/cli.js +1 -1
- package/src/commands/trigger.js +48 -22
package/CHANGELOG.md
CHANGED
|
@@ -7,13 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
-
### Added
|
|
11
|
-
- "Star the repo" call to action in `skill --help` (with the `gh repo star` command) and the README.
|
|
12
|
-
|
|
13
10
|
### Planned
|
|
14
11
|
- Cursor adapter (`.cursor/rules` format) for `init -g` bootstrap.
|
|
15
12
|
- Per-agent hook adapters for automatic `/X` triggering (push model).
|
|
16
13
|
|
|
14
|
+
## [0.1.1] - 2026-07-07
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
- `skill trigger <name>` now loads a skill by exact name when no `triggers:` keyword matches. Skills without a `triggers:` field (e.g. description-triggered skills imported from the `skills` / `vercel-labs` ecosystem) were previously unreachable via `trigger`. A passive skill matched by name now shows an enable hint (`skill enable` / `skill cat`) instead of a dead-end "No active skill" message.
|
|
18
|
+
|
|
17
19
|
## [0.1.0] - 2026-07-07
|
|
18
20
|
|
|
19
21
|
### Added
|
|
@@ -46,5 +48,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
46
48
|
- Windows `npx` spawn runs with `shell:false` + an args array and rejects sources
|
|
47
49
|
containing shell metacharacters (`& | < > ^`).
|
|
48
50
|
|
|
49
|
-
[Unreleased]: https://github.com/victortomaili/skill-cli/compare/v0.1.
|
|
51
|
+
[Unreleased]: https://github.com/victortomaili/skill-cli/compare/v0.1.1...HEAD
|
|
52
|
+
[0.1.1]: https://github.com/victortomaili/skill-cli/compare/v0.1.0...v0.1.1
|
|
50
53
|
[0.1.0]: https://github.com/victortomaili/skill-cli/releases/tag/v0.1.0
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@victortomaili/skill-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Cross-agent skill manager — one global store, clean agent folders, activation via skill.config",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Victor Tomaili <victor@tomaili.com>",
|
package/src/cli.js
CHANGED
|
@@ -29,7 +29,7 @@ ${c.bold('Activation')}
|
|
|
29
29
|
${c.bold('Usage (agent)')}
|
|
30
30
|
${c.cyan('skill show')} ${c.gray('<name>')} metadata + path + triggers
|
|
31
31
|
${c.cyan('skill cat')} ${c.gray('<name>')} dump content to context
|
|
32
|
-
${c.cyan('skill trigger')} ${c.gray('<keyword>')} /X trigger
|
|
32
|
+
${c.cyan('skill trigger')} ${c.gray('<keyword|name>')} /X trigger or skill name → content
|
|
33
33
|
|
|
34
34
|
${c.bold('Maintenance')}
|
|
35
35
|
${c.cyan('skill update')} ${c.gray('[name|--all]')} refresh store from source
|
package/src/commands/trigger.js
CHANGED
|
@@ -4,43 +4,69 @@ import { readGlobalConfig, readProjectConfig, computeEffective } from '../lib/co
|
|
|
4
4
|
import { normalizeTrigger } from '../lib/frontmatter.js'
|
|
5
5
|
import { trunc } from '../lib/format.js'
|
|
6
6
|
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
// Dump a single skill's body into context. Shared by the trigger-keyword and
|
|
8
|
+
// exact-name code paths so both render identically.
|
|
9
|
+
function loadSingle(skill, via) {
|
|
10
|
+
const s = readSkill(skill.name)
|
|
11
|
+
if (!s) { console.error(c.red('Skill not readable: ' + skill.name)); process.exit(1) }
|
|
12
|
+
console.log(c.green('▼ skill: ') + c.bold(s.name) + c.gray(' (' + via + ')'))
|
|
13
|
+
console.log(c.gray('━'.repeat(56)))
|
|
14
|
+
console.log(s.body.trim())
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// `skill trigger <keyword|name>`:
|
|
18
|
+
// 1. keyword matches a trigger across ACTIVE skills
|
|
19
|
+
// single → dump content (drops into context)
|
|
20
|
+
// multi → candidate list (name + description); pick with `skill cat <name>`
|
|
21
|
+
// 2. no trigger match → exact NAME match among active skills. Lets you load
|
|
22
|
+
// description-triggered skills that have no `triggers:` field (e.g. skills
|
|
23
|
+
// imported from the `skills` / vercel-labs ecosystem) by name.
|
|
24
|
+
// 3. name matches only a PASSIVE skill → hint how to activate (or read via cat).
|
|
25
|
+
// 4. nothing → informational.
|
|
11
26
|
export function cmdTrigger(args) {
|
|
12
27
|
const keyword = normalizeTrigger(args[0] || '')
|
|
13
28
|
if (!keyword) {
|
|
14
|
-
console.error(c.red('Usage: skill trigger <keyword> (e.g. /research)'))
|
|
29
|
+
console.error(c.red('Usage: skill trigger <keyword|name> (e.g. /research, web-design-guidelines)'))
|
|
15
30
|
process.exit(1)
|
|
16
31
|
}
|
|
17
32
|
|
|
18
33
|
const installed = listStore()
|
|
19
34
|
const effective = computeEffective(installed, readGlobalConfig(), readProjectConfig())
|
|
20
|
-
|
|
35
|
+
|
|
36
|
+
// 1) trigger-keyword match across ACTIVE skills — a passive skill cannot trigger.
|
|
21
37
|
const candidates = installed.filter(s => effective.includes(s.name) && s.triggers.includes(keyword))
|
|
22
38
|
|
|
23
|
-
if (candidates.length
|
|
24
|
-
console.log(c.yellow('
|
|
25
|
-
console.log(
|
|
39
|
+
if (candidates.length >= 2) {
|
|
40
|
+
console.log(c.yellow('/' + keyword) + ' matched ' + c.bold(candidates.length + ' skills') + ' — pick one:')
|
|
41
|
+
console.log()
|
|
42
|
+
for (const s of candidates) {
|
|
43
|
+
console.log(' ' + c.cyan(s.name.padEnd(20)) + c.gray(' ' + trunc(s.description, 54)))
|
|
44
|
+
}
|
|
45
|
+
console.log()
|
|
46
|
+
console.log(c.gray('Pick: ') + 'skill cat <name>')
|
|
26
47
|
return
|
|
27
48
|
}
|
|
28
|
-
|
|
29
49
|
if (candidates.length === 1) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
50
|
+
loadSingle(candidates[0], 'triggered: /' + keyword)
|
|
51
|
+
return
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// 2) no trigger match → exact-name fallback among ACTIVE skills.
|
|
55
|
+
const byName = installed.filter(s => effective.includes(s.name) && s.name.toLowerCase() === keyword)
|
|
56
|
+
if (byName.length === 1) {
|
|
57
|
+
loadSingle(byName[0], 'name: ' + byName[0].name)
|
|
35
58
|
return
|
|
36
59
|
}
|
|
37
60
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
console.log('
|
|
61
|
+
// 3) name matches only a PASSIVE skill → it's installed but not active.
|
|
62
|
+
const passive = installed.find(s => !effective.includes(s.name) && s.name.toLowerCase() === keyword)
|
|
63
|
+
if (passive) {
|
|
64
|
+
console.log(c.yellow('Skill ') + c.bold(passive.name) + c.yellow(' is installed but not active.'))
|
|
65
|
+
console.log(c.gray(' Enable: ') + c.cyan('skill enable [-g] ' + passive.name) + c.gray(' · read now: ') + c.cyan('skill cat ' + passive.name))
|
|
66
|
+
return
|
|
42
67
|
}
|
|
43
|
-
console.log()
|
|
44
|
-
console.log(c.gray('Pick: ') + 'skill cat <name>')
|
|
45
|
-
}
|
|
46
68
|
|
|
69
|
+
// 4) nothing matched.
|
|
70
|
+
console.log(c.yellow('No active skill has the ') + c.cyan('/' + keyword) + c.yellow(' trigger.'))
|
|
71
|
+
console.log(c.gray(' Run skill list to see active skills and their triggers.'))
|
|
72
|
+
}
|