happyskills 0.33.0 → 0.34.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/CHANGELOG.md +14 -0
- package/package.json +1 -1
- package/src/api/repos.js +4 -2
- package/src/commands/search.js +21 -15
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.34.0] - 2026-04-09
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
- Make smart search (semantic + quality ranking) the default when a query is provided — `search "query"` now uses hybrid vector + full-text matching instead of keyword-only
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
- Add `--exact` flag to `search` command to opt out of smart search and use keyword-only matching
|
|
17
|
+
- Add `hint` field to `--exact --json` responses nudging users toward smart search for better results
|
|
18
|
+
|
|
19
|
+
## [0.33.1] - 2026-04-08
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
- Fix `search --smart` not sending auth token for unauthenticated-by-default requests — private and workspace skills were invisible even when logged in
|
|
23
|
+
|
|
10
24
|
## [0.33.0] - 2026-04-08
|
|
11
25
|
|
|
12
26
|
### Added
|
package/package.json
CHANGED
package/src/api/repos.js
CHANGED
|
@@ -82,7 +82,6 @@ const get_blob = (owner, repo, sha) => catch_errors(`Get blob ${owner}/${repo}/$
|
|
|
82
82
|
})
|
|
83
83
|
|
|
84
84
|
const semantic_search = (query, options = {}) => catch_errors('Semantic search failed', async () => {
|
|
85
|
-
const needs_auth = !!(options.workspace_slug || options.scope === 'mine' || options.scope === 'personal')
|
|
86
85
|
const body = {
|
|
87
86
|
q: query,
|
|
88
87
|
tags: options.tags ? options.tags.split(',').map(t => t.trim()).filter(Boolean) : null,
|
|
@@ -92,7 +91,10 @@ const semantic_search = (query, options = {}) => catch_errors('Semantic search f
|
|
|
92
91
|
workspace_slugs: options.workspace_slug ? options.workspace_slug.split(',').map(s => s.trim()).filter(Boolean) : null,
|
|
93
92
|
scope: options.workspace_slug ? null : (options.scope || null),
|
|
94
93
|
}
|
|
95
|
-
|
|
94
|
+
// Always attempt auth for smart search — if the user is logged in, the API
|
|
95
|
+
// returns public + accessible private skills. If not logged in, the client
|
|
96
|
+
// silently skips the auth header and the API defaults to public-only.
|
|
97
|
+
const [errors, data] = await client.post('/repos:semantic-search', body, { auth: true })
|
|
96
98
|
if (errors) throw errors[errors.length - 1]
|
|
97
99
|
return data
|
|
98
100
|
})
|
package/src/commands/search.js
CHANGED
|
@@ -10,6 +10,9 @@ const HELP_TEXT = `Usage: happyskills search [query] [options]
|
|
|
10
10
|
|
|
11
11
|
Search the skill registry.
|
|
12
12
|
|
|
13
|
+
When a query is provided, smart search (semantic + quality ranking) is used by
|
|
14
|
+
default. Use --exact to fall back to keyword-only matching.
|
|
15
|
+
|
|
13
16
|
Arguments:
|
|
14
17
|
query Search term (optional with --mine, --personal, or --workspace)
|
|
15
18
|
|
|
@@ -19,21 +22,22 @@ Options:
|
|
|
19
22
|
--personal Search only your personal workspace
|
|
20
23
|
--tags <tags> Filter by tags (comma-separated)
|
|
21
24
|
--type <type> Filter by type (skill, kit)
|
|
22
|
-
--
|
|
23
|
-
--limit <n> Max results (default: 10, max: 50)
|
|
24
|
-
--min-quality <n> Minimum quality score 0-100
|
|
25
|
+
--exact Use keyword-only matching instead of smart search
|
|
26
|
+
--limit <n> Max results (default: 10, max: 50)
|
|
27
|
+
--min-quality <n> Minimum quality score 0-100
|
|
25
28
|
--json Output as JSON
|
|
26
29
|
|
|
27
30
|
Aliases: s
|
|
28
31
|
|
|
29
32
|
Examples:
|
|
30
33
|
happyskills search deploy
|
|
34
|
+
happyskills search "REST API with Node.js"
|
|
31
35
|
happyskills search --mine
|
|
32
|
-
happyskills search "REST API with Node.js" --smart
|
|
33
36
|
happyskills search deploy --workspace acme
|
|
34
37
|
happyskills search --type kit
|
|
35
38
|
happyskills s --personal --json
|
|
36
|
-
happyskills search "deploy to AWS"
|
|
39
|
+
happyskills search "deploy to AWS" --limit 5
|
|
40
|
+
happyskills search deploy --exact`
|
|
37
41
|
|
|
38
42
|
const QUALITY_TIERS = [
|
|
39
43
|
{ min: 80, label: 'High quality', color: cyan },
|
|
@@ -140,7 +144,7 @@ const run_smart_search = (args, query, options) => catch_errors('Smart search fa
|
|
|
140
144
|
console.log(`\n${gray(`Showing ${items.length} result${items.length === 1 ? '' : 's'}. Install with: happyskills install <owner>/<name>`)}\n`)
|
|
141
145
|
})
|
|
142
146
|
|
|
143
|
-
const run_keyword_search = (args, query, options) => catch_errors('Keyword search failed', async () => {
|
|
147
|
+
const run_keyword_search = (args, query, options, { is_exact } = {}) => catch_errors('Keyword search failed', async () => {
|
|
144
148
|
const [errors, results] = await repos_api.search(query, options)
|
|
145
149
|
if (errors) throw e('Search failed', errors)
|
|
146
150
|
|
|
@@ -149,7 +153,9 @@ const run_keyword_search = (args, query, options) => catch_errors('Keyword searc
|
|
|
149
153
|
|
|
150
154
|
if (items.length === 0) {
|
|
151
155
|
if (args.flags.json) {
|
|
152
|
-
|
|
156
|
+
const data = { query, scope: effective_scope, results: [], count: 0 }
|
|
157
|
+
if (is_exact) data.hint = 'Smart search (default) uses semantic matching for better results. Remove --exact to use it.'
|
|
158
|
+
print_json({ data })
|
|
153
159
|
return
|
|
154
160
|
}
|
|
155
161
|
const context = query ? ` for "${query}"` : ''
|
|
@@ -165,7 +171,9 @@ const run_keyword_search = (args, query, options) => catch_errors('Keyword searc
|
|
|
165
171
|
version: item.latest_version || item.version || '-',
|
|
166
172
|
visibility: item.visibility || 'public'
|
|
167
173
|
}))
|
|
168
|
-
|
|
174
|
+
const data = { query, scope: effective_scope, results: mapped, count: mapped.length }
|
|
175
|
+
if (is_exact) data.hint = 'Smart search (default) uses semantic matching for better results. Remove --exact to use it.'
|
|
176
|
+
print_json({ data })
|
|
169
177
|
return
|
|
170
178
|
}
|
|
171
179
|
|
|
@@ -191,13 +199,11 @@ const run = (args) => catch_errors('Search failed', async () => {
|
|
|
191
199
|
|
|
192
200
|
const query = args._.join(' ') || null
|
|
193
201
|
const { mine, personal, workspace, tags, type } = args.flags
|
|
194
|
-
const
|
|
202
|
+
const is_exact = !!args.flags.exact
|
|
203
|
+
// --smart / -S accepted for backward compat (now the default when a query is provided)
|
|
204
|
+
const use_smart = query && !is_exact
|
|
195
205
|
const has_scope_flag = mine || personal || workspace
|
|
196
206
|
|
|
197
|
-
if (is_smart && !query) {
|
|
198
|
-
throw new UsageError('--smart requires a search query.')
|
|
199
|
-
}
|
|
200
|
-
|
|
201
207
|
if (!query && !has_scope_flag && !tags && !type) {
|
|
202
208
|
throw new UsageError('Please provide a search query or use --mine, --personal, or --workspace.')
|
|
203
209
|
}
|
|
@@ -221,7 +227,7 @@ const run = (args) => catch_errors('Search failed', async () => {
|
|
|
221
227
|
if (tags) options.tags = tags
|
|
222
228
|
if (type) options.type = type
|
|
223
229
|
|
|
224
|
-
if (
|
|
230
|
+
if (use_smart) {
|
|
225
231
|
const [errors] = await run_smart_search(args, query, options)
|
|
226
232
|
if (errors) throw e('Smart search failed', errors)
|
|
227
233
|
} else {
|
|
@@ -231,7 +237,7 @@ const run = (args) => catch_errors('Search failed', async () => {
|
|
|
231
237
|
kw_options.workspace = options.workspace_slug
|
|
232
238
|
delete kw_options.workspace_slug
|
|
233
239
|
}
|
|
234
|
-
const [errors] = await run_keyword_search(args, query, kw_options)
|
|
240
|
+
const [errors] = await run_keyword_search(args, query, kw_options, { is_exact })
|
|
235
241
|
if (errors) throw e('Search failed', errors)
|
|
236
242
|
}
|
|
237
243
|
}).then(([errors]) => { if (errors) { exit_with_error(errors); return } })
|