dsh-skill-picker 0.3.3 → 0.3.4

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dsh-skill-picker",
3
3
  "description": "DSH Web GUI skill picker: a button beside the composer opens a searchable list of installed skills; picking one inserts the official `/skill-name` gesture into the input box, so the skill loads with your message (WorkBuddy-style skill invocation for DeepSeek Harness).",
4
- "version": "0.3.3",
4
+ "version": "0.3.4",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "exports": {
@@ -23,7 +23,8 @@
23
23
  "client": {
24
24
  "inject": [
25
25
  "@deepseek-ai/dsh-client-runtime",
26
- "@deepseek-ai/dsh-client-locale"
26
+ "@deepseek-ai/dsh-client-locale",
27
+ "@deepseek-ai/dsh-client-ui-input-trigger"
27
28
  ],
28
29
  "platform": "web"
29
30
  },
@@ -445,12 +445,21 @@ function SkillPickerButton(props) {
445
445
 
446
446
  /** Apply the browser half: register the picker into the composer tool row. */
447
447
  export function apply(ctx) {
448
- // Primary skill source: the official host skills API (the exact RPC ui-skill
449
- // feeds DSH's own `/` completion withsession-scoped, all skill layers).
448
+ // Resolve the trigger pipeline service at apply scope (not inside an effect),
449
+ // exactly like the official ui-skill plugin does in DSH 0.1.2-alpha.x the
450
+ // service lives in @deepseek-ai/dsh-client-ui-input-trigger and is only
451
+ // reachable from the plugin root context.
452
+ const inputTriggers = (typeof ctx.get === 'function' ? ctx.get('inputTriggers') : ctx.inputTriggers)
453
+
454
+ // Primary skill source: the official host skills API. In DSH 0.1.2-alpha.x
455
+ // the RPC moved from `connection.api.skills` (rc.x) to `remote.skills`
456
+ // (used by the official ui-skill plugin); try both before falling back.
450
457
  const listSkills = async (sessionId) => {
451
- const skills = ctx.connection?.api?.skills
458
+ const remoteSkills = ctx.remote?.skills
459
+ const connectionSkills = ctx.connection?.api?.skills
460
+ const skills = remoteSkills ?? connectionSkills
452
461
  if (skills === undefined || typeof skills.list !== 'function') {
453
- throw new Error('connection.api.skills unavailable')
462
+ throw new Error('skills RPC unavailable (remote.skills / connection.api.skills)')
454
463
  }
455
464
  const controller = new AbortController()
456
465
  const { result } = await skills.list({ sessionId }, controller.signal)
@@ -471,6 +480,29 @@ export function apply(ctx) {
471
480
  currentCwd = ''
472
481
  }
473
482
  }
483
+
484
+ // Host-route fallback: same scan the ⚡ panel uses (official provider roots).
485
+ const fetchHostSkills = async () => {
486
+ const cwd = typeof currentCwd === 'string' && currentCwd !== '' ? `?cwd=${encodeURIComponent(currentCwd)}` : ''
487
+ const res = await fetch(`/dsh-skill-picker/skills${cwd}`, { headers: { accept: 'application/json' } })
488
+ const json = await res.json()
489
+ if (!json.ok) throw new Error(json.error || 'bad response')
490
+ return Array.isArray(json.skills) ? json.skills : []
491
+ }
492
+
493
+ // Resolve skills by official RPC first, then the host scan route; never
494
+ // throws (returns [] when both fail) so `/` completion degrades gracefully.
495
+ const resolveSkills = async (sessionId) => {
496
+ try {
497
+ return await listSkills(sessionId)
498
+ } catch {
499
+ try {
500
+ return await fetchHostSkills()
501
+ } catch {
502
+ return []
503
+ }
504
+ }
505
+ }
474
506
  syncCwd()
475
507
  const unsubscribe = ctx.sessions.list.subscribe(syncCwd)
476
508
  ctx.effect(() => {
@@ -507,7 +539,7 @@ export function apply(ctx) {
507
539
  }
508
540
  const refreshNames = async (sessionId) => {
509
541
  try {
510
- const skills = await listSkills(sessionId)
542
+ const skills = await resolveSkills(sessionId)
511
543
  namesCache.set(sessionId, (Array.isArray(skills) ? skills : []).map((s) => s.name))
512
544
  notifyLexicon(sessionId)
513
545
  } catch {
@@ -520,7 +552,7 @@ export function apply(ctx) {
520
552
  name: 'skill-fuzzy',
521
553
  order: -10,
522
554
  async candidates(session, { query, signal }) {
523
- const skills = await listSkills(session.sessionId)
555
+ const skills = await resolveSkills(session.sessionId)
524
556
  if (signal.aborted) return []
525
557
  namesCache.set(session.sessionId, skills.map((s) => s.name))
526
558
  notifyLexicon(session.sessionId)
@@ -581,7 +613,13 @@ export function apply(ctx) {
581
613
  return { text: `/${candidate.name} ` }
582
614
  },
583
615
  }
584
- const unregister = ctx.inputTriggers.registerSource(source)
616
+ // inputTriggers is resolved at apply scope (see top of apply); fail loudly
617
+ // instead of silently dropping the fuzzy `/` source.
618
+ if (inputTriggers === undefined || typeof inputTriggers.registerSource !== 'function') {
619
+ console.warn('[dsh-skill-picker] inputTriggers service unavailable; fuzzy / completion disabled (⚡ panel still works)')
620
+ return
621
+ }
622
+ const unregister = inputTriggers.registerSource(source)
585
623
  return () => {
586
624
  unregister()
587
625
  namesCache.clear()