dsh-skill-hub 0.3.6 → 0.3.7

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.
Files changed (38) hide show
  1. package/lib/client.js +890 -356
  2. package/lib/client.js.map +1 -1
  3. package/lib/index.js +401 -47
  4. package/lib/types/client/SkillHubSettingsCard.d.ts +0 -2
  5. package/lib/types/client/api.d.ts +5 -1
  6. package/lib/types/client/helpers.d.ts +10 -0
  7. package/lib/types/client/locales.d.ts +30 -0
  8. package/lib/types/client/panel/DisabledRow.d.ts +2 -0
  9. package/lib/types/client/panel/SkillDetailView.d.ts +5 -1
  10. package/lib/types/client/panel/dialogs.d.ts +19 -0
  11. package/lib/types/client/panel/useSkillHub.d.ts +12 -1
  12. package/lib/types/client/settings-card.d.ts +0 -17
  13. package/lib/types/client/settings-form.d.ts +0 -1
  14. package/lib/types/protocol.d.ts +40 -17
  15. package/lib/types/repo.d.ts +4 -6
  16. package/lib/types/skillfs.d.ts +13 -0
  17. package/package.json +29 -28
  18. package/src/client/SkillHubSettingsCard.tsx +0 -2
  19. package/src/client/api.ts +18 -0
  20. package/src/client/helpers.ts +40 -0
  21. package/src/client/locales.ts +60 -0
  22. package/src/client/panel/DisabledRow.tsx +22 -7
  23. package/src/client/panel/MarketView.tsx +8 -1
  24. package/src/client/panel/ScenesView.tsx +1 -1
  25. package/src/client/panel/SkillDetailView.tsx +35 -11
  26. package/src/client/panel/SkillHubPanel.tsx +42 -4
  27. package/src/client/panel/SkillRow.tsx +5 -2
  28. package/src/client/panel/SourcesView.tsx +2 -17
  29. package/src/client/panel/dialogs.tsx +61 -0
  30. package/src/client/panel/panel.module.css +1 -8
  31. package/src/client/panel/useSkillHub.ts +97 -12
  32. package/src/client/settings-card.tsx +0 -42
  33. package/src/client/settings-form.ts +1 -5
  34. package/src/protocol.ts +42 -8
  35. package/src/repo.ts +71 -29
  36. package/src/routes.test.ts +22 -0
  37. package/src/routes.ts +181 -10
  38. package/src/skillfs.ts +188 -3
@@ -64,12 +64,22 @@ export declare const zh: {
64
64
  readonly 'market.branchTitle': "选择分支";
65
65
  readonly 'market.branchHint': "该仓库没有正式发布,选择要跟踪的分支(默认为 main):";
66
66
  readonly 'market.branchConfirm': "确定";
67
+ readonly 'market.versionTitle': "切换跟踪版本";
68
+ readonly 'market.versionText': "选择 {repo} 要跟踪的版本,切换后建议重新扫描并同步已装技能";
69
+ readonly 'market.versionCurrent': "当前 {ref}";
70
+ readonly 'market.versionReleases': "发布版";
71
+ readonly 'market.versionBranches': "分支";
72
+ readonly 'market.versionCustom': "自定义(tag / 分支 / commit)";
73
+ readonly 'market.versionConfirm': "切换到 {ref}";
74
+ readonly 'market.versionHint': "点击切换跟踪版本";
75
+ readonly 'market.unpinned': "未定版";
67
76
  readonly 'market.syncTitle': "同步本地技能";
68
77
  readonly 'market.syncText': "市场源已对齐到 {ref},要同步更新以下本地技能吗?";
69
78
  readonly 'market.syncNone': "该来源还没有导入本地技能。";
70
79
  readonly 'market.syncCancel': "跳过";
71
80
  readonly 'market.removeHint': "移除源不影响已安装的技能。";
72
81
  readonly 'repo.ready': "发现 {count} 个可导入技能";
82
+ readonly 'repo.truncated': "⚠ 仓库过大,仅展示部分技能(GitHub 截断),可缩小仓库或指定子目录";
73
83
  readonly 'repo.empty': "没有找到可导入技能。";
74
84
  readonly 'repo.selectAll': "全选";
75
85
  readonly 'repo.clearAll': "清空";
@@ -142,6 +152,26 @@ export declare const zh: {
142
152
  readonly 'row.enable': "启用";
143
153
  readonly 'row.delete': "移入回收站";
144
154
  readonly 'row.open': "查看 {name} 详情(回车)";
155
+ readonly 'row.duplicate': "重名";
156
+ readonly 'row.duplicateHint': "同名技能存在多个来源,当前显示为优先级最高的版本";
157
+ readonly 'row.copy': "复制";
158
+ readonly 'row.copyMention': "复制 $mention";
159
+ readonly 'row.copied': "已复制";
160
+ readonly 'row.copyPath': "复制路径";
161
+ readonly 'detail.copyMention': "复制 $mention";
162
+ readonly 'detail.copyPath': "复制路径";
163
+ readonly 'detail.copied': "已复制";
164
+ readonly 'diag.fix': "修复";
165
+ readonly 'diag.fixing': "修复中…";
166
+ readonly 'diag.fixed': "已修复";
167
+ readonly 'diag.fixConfirm': "用自动修复重写以下文件的 frontmatter?建议先备份:{path}";
168
+ readonly 'filter.invocation': "调用";
169
+ readonly 'filter.invocationAll': "全部调用";
170
+ readonly 'filter.modelOnly': "模型可调";
171
+ readonly 'filter.userOnly': "用户可调";
172
+ readonly 'filter.showing': "显示 {shown}/{total}(已启用){filtered}";
173
+ readonly 'filter.filteredSuffix': " · 已筛选";
174
+ readonly 'filter.shortened': "短描述 {count} 条,悬浮看全文";
145
175
  readonly 'delete.confirmTitle': "移入回收站?";
146
176
  readonly 'delete.confirmText': "确定把「{name}」移入回收站吗?可随时恢复。";
147
177
  readonly 'delete.confirm': "移入回收站";
@@ -9,4 +9,6 @@ export declare function DisabledRow(props: {
9
9
  record: DisabledSkill;
10
10
  busy: boolean;
11
11
  onEnable: () => void;
12
+ duplicate?: boolean;
13
+ onOpen?: () => void;
12
14
  }): JSX.Element;
@@ -4,7 +4,7 @@
4
4
  * presentation — every state value and action arrives as a prop, so the
5
5
  * panel stays the single owner of state.
6
6
  */
7
- import type { JSX } from 'react';
7
+ import { type JSX } from 'react';
8
8
  import type { GroupsResponse, HubConfig, SkillDetail, SourceCheckResult, SourcesResponse } from '../../protocol.ts';
9
9
  export interface SkillDetailViewProps {
10
10
  detail: SkillDetail;
@@ -20,6 +20,10 @@ export interface SkillDetailViewProps {
20
20
  checkingSource: string | null;
21
21
  syncingSource: string | null;
22
22
  loading: boolean;
23
+ /** Whether the skill is currently hub-disabled (served from the .disabled file). */
24
+ disabled?: boolean;
25
+ /** Re-enable a hub-disabled skill. */
26
+ onEnable?: () => void;
23
27
  onBack: () => void;
24
28
  /** Check one source repo for upstream updates. */
25
29
  onCheck: (repo: string) => void;
@@ -27,6 +27,16 @@ export interface BranchChoiceState {
27
27
  branches: string[];
28
28
  selected: string;
29
29
  }
30
+ /** Version picker for a market source: releases + branches + custom ref. */
31
+ export interface VersionChoiceState {
32
+ repo: string;
33
+ current?: string;
34
+ releases: string[];
35
+ branches: string[];
36
+ selected: string;
37
+ custom: string;
38
+ loading: boolean;
39
+ }
30
40
  /** Post-sync skill update dialog: which tracked skills to refresh. */
31
41
  export interface MarketSyncDialogState {
32
42
  repo: string;
@@ -63,6 +73,15 @@ export declare function BranchChoiceDialog(props: {
63
73
  onCancel: () => void;
64
74
  onConfirm: () => void;
65
75
  }): JSX.Element;
76
+ /** Version picker: switch a market source's tracked ref (release / branch / custom). */
77
+ export declare function VersionChoiceDialog(props: {
78
+ choice: VersionChoiceState;
79
+ busy: boolean;
80
+ onSelect: (ref: string) => void;
81
+ onCustom: (custom: string) => void;
82
+ onCancel: () => void;
83
+ onConfirm: () => void;
84
+ }): JSX.Element;
66
85
  /** Post-sync selection: which tracked skills to batch-update to the new version. */
67
86
  export declare function MarketSyncDialog(props: {
68
87
  dialog: MarketSyncDialogState;
@@ -9,7 +9,7 @@ import { type FormEvent } from 'react';
9
9
  import type { CatalogResponse, CatalogSkill, DisabledSkill, GroupsResponse, HubConfig, MarketSourceRecord, RepoDiscoverResponse, RepoImportProgressResponse, SkillDetail, SkillTag, SourceCheckResult, SourcesResponse, UpdateCheckResponse, WritableRoot } from '../../protocol.ts';
10
10
  import type { SkillHubApi } from '../api.ts';
11
11
  import { type SortKey, type GroupSwitchState } from '../grouping.ts';
12
- import type { BranchChoiceState, ConfirmDialogState, ConflictDialogState, MarketSyncDialogState } from './dialogs.tsx';
12
+ import type { BranchChoiceState, ConfirmDialogState, ConflictDialogState, MarketSyncDialogState, VersionChoiceState } from './dialogs.tsx';
13
13
  /** One-shot self-update check state. */
14
14
  type UpdateState = {
15
15
  status: 'idle';
@@ -76,6 +76,7 @@ export declare function useSkillHub(api: SkillHubApi): {
76
76
  tab: "sources" | "scenes" | "market";
77
77
  skillView: "flat" | "groups";
78
78
  sourceFilter: string;
79
+ invocationFilter: "user" | "model" | "all";
79
80
  sortKey: SortKey;
80
81
  marketState: MarketState;
81
82
  marketCheck: Readonly<Record<string, {
@@ -117,6 +118,8 @@ export declare function useSkillHub(api: SkillHubApi): {
117
118
  subdividedProjects: ReadonlySet<string>;
118
119
  showLegend: boolean;
119
120
  editMode: boolean;
121
+ versionDialog: VersionChoiceState | null;
122
+ versionBusy: boolean;
120
123
  actionNames: Set<string>;
121
124
  viewNames: Set<string>;
122
125
  normalized: string;
@@ -124,6 +127,7 @@ export declare function useSkillHub(api: SkillHubApi): {
124
127
  sourceOptions: string[];
125
128
  filtered: CatalogSkill[];
126
129
  sorted: CatalogSkill[];
130
+ shortenedCount: number;
127
131
  setLoadError: import("react").Dispatch<import("react").SetStateAction<string | null>>;
128
132
  setSuccessBanner: import("react").Dispatch<import("react").SetStateAction<string | null>>;
129
133
  setSearch: import("react").Dispatch<import("react").SetStateAction<string>>;
@@ -141,6 +145,7 @@ export declare function useSkillHub(api: SkillHubApi): {
141
145
  setTab: import("react").Dispatch<import("react").SetStateAction<"sources" | "scenes" | "market">>;
142
146
  setSkillView: import("react").Dispatch<import("react").SetStateAction<"flat" | "groups">>;
143
147
  setSourceFilter: import("react").Dispatch<import("react").SetStateAction<string>>;
148
+ setInvocationFilter: import("react").Dispatch<import("react").SetStateAction<"user" | "model" | "all">>;
144
149
  setSortKey: import("react").Dispatch<import("react").SetStateAction<SortKey>>;
145
150
  setBranchChoice: import("react").Dispatch<import("react").SetStateAction<BranchChoiceState | null>>;
146
151
  setMarketSyncDialog: import("react").Dispatch<import("react").SetStateAction<MarketSyncDialogState | null>>;
@@ -161,6 +166,7 @@ export declare function useSkillHub(api: SkillHubApi): {
161
166
  setEditSearch: import("react").Dispatch<import("react").SetStateAction<string>>;
162
167
  setShowLegend: import("react").Dispatch<import("react").SetStateAction<boolean>>;
163
168
  setEditMode: import("react").Dispatch<import("react").SetStateAction<boolean>>;
169
+ setVersionDialog: import("react").Dispatch<import("react").SetStateAction<VersionChoiceState | null>>;
164
170
  toggleGroupCollapse: (key: string) => void;
165
171
  toggleSubdivide: (key: string) => void;
166
172
  checkUpdate: () => Promise<void>;
@@ -177,6 +183,11 @@ export declare function useSkillHub(api: SkillHubApi): {
177
183
  requestDelete: (repo: string, skills: string[]) => void;
178
184
  restoreTrash: (name: string) => Promise<void>;
179
185
  clearTrash: () => Promise<void>;
186
+ fixingPaths: ReadonlySet<string>;
187
+ fixDiagnostic: (path: string) => Promise<void>;
188
+ clearListFilters: () => void;
189
+ openVersionDialog: (repo: string) => Promise<void>;
190
+ confirmVersionDialog: () => Promise<void>;
180
191
  requestDeleteSkill: (name: string) => void;
181
192
  runDeleteSkill: () => Promise<void>;
182
193
  requestDeleteGroup: (name: string, skillNames: string[]) => void;
@@ -30,23 +30,6 @@ export interface PluginSettingsCardProps {
30
30
  * @returns the card, or nothing while the namespace is still loading.
31
31
  */
32
32
  export declare function PluginSettingsCard(props: PluginSettingsCardProps): ReactElement | null;
33
- /** One staged boolean field: 继承 / 开 / 关. */
34
- export interface BooleanFieldProps {
35
- id: string;
36
- label: string;
37
- hint: string;
38
- inheritLabel: string;
39
- onLabel: string;
40
- offLabel: string;
41
- overriddenLabel: string;
42
- resetLabel: string;
43
- disabled: boolean;
44
- text: string;
45
- overridden: boolean;
46
- onEdit: (text: string) => void;
47
- onReset: () => void;
48
- }
49
- export declare function BooleanField(props: BooleanFieldProps): ReactElement;
50
33
  /**
51
34
  * A compact sliding switch for the card's master enable/disable control.
52
35
  * An empty text value means the field inherits its default; the switch still
@@ -98,7 +98,6 @@ export declare class CardForm {
98
98
  private specOf;
99
99
  private sectionValue;
100
100
  private baseValue;
101
- private userLayer;
102
101
  private stored;
103
102
  private publish;
104
103
  }
@@ -38,6 +38,8 @@ export declare const SKILL_HUB_API: {
38
38
  readonly sourceDelete: "/api/skill-hub/sources/delete";
39
39
  readonly sourceRestore: "/api/skill-hub/sources/restore";
40
40
  readonly sourceTrashClear: "/api/skill-hub/sources/trash/clear";
41
+ readonly diagnosticFix: "/api/skill-hub/diagnostic/fix";
42
+ readonly marketSourceVersions: "/api/skill-hub/market/source/versions";
41
43
  };
42
44
  /** User-level roots the hub may write to (matches dsh-skill-filesystem ranks 400/500). */
43
45
  export type WritableRoot = 'user-dsh' | 'user-agents';
@@ -67,6 +69,13 @@ export interface CatalogSkill {
67
69
  workspace?: string;
68
70
  /** 工作区显示标题(来自 workspace.json;无则回退为路径)。 */
69
71
  workspaceTitle?: string;
72
+ /** UI metadata from agents/openai.yaml (mirrors codex SkillInterface). */
73
+ displayName?: string;
74
+ shortDescription?: string;
75
+ brandColor?: string;
76
+ iconSmall?: string;
77
+ iconLarge?: string;
78
+ defaultPrompt?: string;
70
79
  }
71
80
  /** One disabled skill tracked by the hub sidecar (SKILL.md renamed away). */
72
81
  export interface DisabledSkill {
@@ -82,6 +91,16 @@ export interface DiagnosticEntry {
82
91
  path: string;
83
92
  root: string;
84
93
  reason: string;
94
+ /** Whether this diagnostic can be auto-fixed (e.g. unquoted colon). */
95
+ fixable?: boolean;
96
+ }
97
+ /** POST /api/skill-hub/diagnostic/fix — repair a fixable diagnostic in place. */
98
+ export interface DiagnosticFixRequest {
99
+ path: string;
100
+ }
101
+ export interface DiagnosticFixResponse {
102
+ ok: true;
103
+ path: string;
85
104
  }
86
105
  /** GET /api/skill-hub/catalog */
87
106
  export interface CatalogResponse {
@@ -96,6 +115,8 @@ export interface CatalogResponse {
96
115
  disabled: DisabledSkill[];
97
116
  /** Files in the writable roots the provider ignores, with reasons. */
98
117
  diagnostics: DiagnosticEntry[];
118
+ /** Skill names that appeared in multiple roots (first wins, others hidden). Mirrors codex name_counts. */
119
+ duplicateNames?: string[];
99
120
  }
100
121
  /** GET /api/skill-hub/skill */
101
122
  export interface SkillDetail {
@@ -112,6 +133,12 @@ export interface SkillDetail {
112
133
  updatedAt?: number;
113
134
  /** Markdown instruction body. */
114
135
  content: string;
136
+ displayName?: string;
137
+ shortDescription?: string;
138
+ brandColor?: string;
139
+ iconSmall?: string;
140
+ iconLarge?: string;
141
+ defaultPrompt?: string;
115
142
  }
116
143
  export interface SkillDetailResponse {
117
144
  ok: true;
@@ -342,6 +369,17 @@ export interface MarketSourceRefRequest {
342
369
  /** Release tag or branch name chosen for the repo. */
343
370
  ref: string;
344
371
  }
372
+ /** GET /api/skill-hub/market/source/versions?repo= — releases + branches for the version picker. */
373
+ export interface MarketSourceVersionsResponse {
374
+ ok: true;
375
+ repo: string;
376
+ /** Currently pinned ref, when one is recorded. */
377
+ current?: string;
378
+ /** Release tags, newest first (drafts excluded). */
379
+ releases: string[];
380
+ /** Branch names, default branch first. */
381
+ branches: string[];
382
+ }
345
383
  /** GET /api/skill-hub/market/check — update check over market sources. */
346
384
  export interface MarketCheckResponse {
347
385
  ok: true;
@@ -404,6 +442,8 @@ export interface RepoDiscoverResponse {
404
442
  /** Branch names to choose from when ref is null (default branch first). */
405
443
  branches?: string[];
406
444
  entries: RepoSkillEntry[];
445
+ /** GitHub truncated the tree; discovery is partial (mirrors codex walk_truncated). */
446
+ truncated?: boolean;
407
447
  }
408
448
  /** POST /api/skill-hub/repo/import — install selected repo skills. */
409
449
  export interface RepoImportRequest {
@@ -461,23 +501,6 @@ export interface RepoImportCancelResponse {
461
501
  jobId: string;
462
502
  status: 'cancelled' | 'done';
463
503
  }
464
- /** @deprecated 旧同步响应,保留供类型兼容,实际已由 Progress 代替 */
465
- export interface RepoImportLegacyResponse {
466
- ok: true;
467
- imported: Array<{
468
- name: string;
469
- origin: string;
470
- path: string;
471
- }>;
472
- skipped: Array<{
473
- name: string;
474
- reason: 'exists';
475
- }>;
476
- failed: Array<{
477
- name: string;
478
- error: string;
479
- }>;
480
- }
481
504
  /** GET /api/skill-hub/update — check the plugin's own latest GitHub release. */
482
505
  export interface UpdateCheckResponse {
483
506
  ok: true;
@@ -36,6 +36,7 @@ export declare class RepoFetchError extends Error {
36
36
  export declare function setGithubToken(token: string | undefined): void;
37
37
  /** Authorization header for api.github.com / raw.githubusercontent.com calls. */
38
38
  export declare function githubAuthHeaders(): Record<string, string>;
39
+ export declare function clearEtagCache(): void;
39
40
  /**
40
41
  * Build a RepoFetchError; when the response shows an exhausted rate limit,
41
42
  * report the reset time instead of a bare HTTP status.
@@ -48,15 +49,10 @@ export declare function repoSlug(ref: RepoRef): string;
48
49
  * Only github.com URLs are accepted in v1.
49
50
  */
50
51
  export declare function normalizeRepoInput(input: string): RepoRef | null;
51
- /**
52
- * Load repo metadata and the recursive git tree at the given ref. The meta
53
- * request always resolves the default branch (used when ref is absent); the
54
- * tree is fetched at `ref ?? default_branch` so an explicit branch/tag scans
55
- * the exact same content a later download would fetch.
56
- */
57
52
  export declare function loadRepoTree(repo: string, ref?: string, fetchImpl?: typeof fetch): Promise<{
58
53
  ref: string;
59
54
  tree: RepoTreeItem[];
55
+ truncated: boolean;
60
56
  }>;
61
57
  /** Collect every file inside a skill directory, including SKILL.md itself. */
62
58
  export declare function collectRepoSkillFiles(tree: readonly RepoTreeItem[], dir: string): RepoFile[];
@@ -94,6 +90,8 @@ export declare function cleanupLeftoverImportDirs(targetRoot: string): Promise<n
94
90
  * repo has no releases (GitHub's /releases/latest skips prereleases/drafts).
95
91
  */
96
92
  export declare function getLatestReleaseTag(repo: string, fetchImpl?: typeof fetch): Promise<string | undefined>;
93
+ /** Release tags of a repo, newest first (skips drafts, keeps prereleases). */
94
+ export declare function listRepoReleases(repo: string, fetchImpl?: typeof fetch): Promise<string[]>;
97
95
  /** Branch names of a repo, default branch first (one GitHub API request). */
98
96
  export declare function listRepoBranches(repo: string, fetchImpl?: typeof fetch): Promise<string[]>;
99
97
  /**
@@ -65,6 +65,10 @@ export declare function parseFrontmatter(text: string): {
65
65
  } | {
66
66
  error: string;
67
67
  };
68
+ /** Try to repair a SKILL.md's frontmatter in place (unquoted colon etc.). Returns the new file text or null when not fixable. */
69
+ export declare function repairFrontmatterFileText(text: string): string | null;
70
+ /** Repair one file on disk when its frontmatter is auto-fixable. Returns the new text. */
71
+ export declare function fixDiagnosticFile(path: string, home?: string): Promise<string>;
68
72
  /** One discoverable skill file in a scanned root. */
69
73
  export interface SkillEntry {
70
74
  /** Absolute path of the discovery file (SKILL.md or the flat .md). */
@@ -81,6 +85,15 @@ export interface SkillEntry {
81
85
  export declare function scanRoot(base: string): Promise<SkillEntry[]>;
82
86
  /** Scan one writable root. */
83
87
  export declare function listSkillEntries(root: WritableRoot, home?: string): Promise<SkillEntry[]>;
88
+ /** Read UI metadata from `agents/openai.yaml` beside a directory skill (mirrors codex SkillInterface). */
89
+ export declare function readSkillInterface(directory: string): Promise<{
90
+ displayName?: string;
91
+ shortDescription?: string;
92
+ brandColor?: string;
93
+ iconSmall?: string;
94
+ iconLarge?: string;
95
+ defaultPrompt?: string;
96
+ } | undefined>;
84
97
  /**
85
98
  * Walk up from cwd (max 32 levels) for a project marker (.dsh or .git);
86
99
  * falls back to cwd itself. The provider roots project skills here.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-skill-hub",
3
- "version": "0.3.6",
3
+ "version": "0.3.7",
4
4
  "description": "In-GUI skill hub for DeepSeek Harness (dsh): browse the full local skill catalog from the official ctx.skills registry (every root + third-party providers), toggle skills on/off, inspect bodies, surface frontmatter diagnostics, and scaffold new skills — plus a codex-style skill market (built-in catalog, upstream update checks, one-click update-all) with tracked source sync. The full manager beyond the read-only dsh-skill-manager browser.",
5
5
  "type": "module",
6
6
  "engines": {
@@ -65,7 +65,8 @@
65
65
  "0.1.1-rc.2",
66
66
  "0.1.2-alpha.2",
67
67
  "0.1.2-alpha.3",
68
- "0.1.2-alpha.4"
68
+ "0.1.2-alpha.4",
69
+ "0.1.2-alpha.5"
69
70
  ]
70
71
  },
71
72
  "capability": {
@@ -87,19 +88,19 @@
87
88
  },
88
89
  "peerDependencies": {
89
90
  "@deepseek-ai/cordis": "^4.0.1 || ^4.0.2",
90
- "@deepseek-ai/dsh-client-connection": ">=0.1.0-rc.7 <0.2.0-0 || >=0.1.1-rc.2 <0.2.0-0 || >=0.1.2-alpha.2 <0.2.0-0 || >=0.1.2-alpha.3 <0.2.0-0 || >=0.1.2-alpha.4 <0.2.0-0",
91
- "@deepseek-ai/dsh-client-locale": ">=0.1.0-rc.7 <0.2.0-0 || >=0.1.1-rc.2 <0.2.0-0 || >=0.1.2-alpha.2 <0.2.0-0 || >=0.1.2-alpha.3 <0.2.0-0 || >=0.1.2-alpha.4 <0.2.0-0",
92
- "@deepseek-ai/dsh-client-runtime": ">=0.1.0-rc.7 <0.2.0-0 || >=0.1.1-rc.2 <0.2.0-0 || >=0.1.2-alpha.3 <0.2.0-0 || >=0.1.2-alpha.4 <0.2.0-0",
93
- "@deepseek-ai/dsh-client-store": ">=0.1.2-alpha.2 <0.2.0-0 || >=0.1.2-alpha.3 <0.2.0-0 || >=0.1.2-alpha.4 <0.2.0-0",
94
- "@deepseek-ai/dsh-client-ui-input-trigger": ">=0.1.0-rc.7 <0.2.0-0 || >=0.1.1-rc.2 <0.2.0-0 || >=0.1.2-alpha.2 <0.2.0-0 || >=0.1.2-alpha.3 <0.2.0-0 || >=0.1.2-alpha.4 <0.2.0-0",
95
- "@deepseek-ai/dsh-client-ui-settings": ">=0.1.0-rc.7 <0.2.0-0 || >=0.1.1-rc.2 <0.2.0-0 || >=0.1.2-alpha.2 <0.2.0-0 || >=0.1.2-alpha.3 <0.2.0-0 || >=0.1.2-alpha.4 <0.2.0-0",
96
- "@deepseek-ai/dsh-client-ui-slots": ">=0.1.0-rc.7 <0.2.0-0 || >=0.1.1-rc.2 <0.2.0-0 || >=0.1.2-alpha.2 <0.2.0-0 || >=0.1.2-alpha.3 <0.2.0-0 || >=0.1.2-alpha.4 <0.2.0-0",
97
- "@deepseek-ai/dsh-host-webserver": ">=0.1.0-rc.7 <0.2.0-0 || >=0.1.1-rc.2 <0.2.0-0 || >=0.1.2-alpha.2 <0.2.0-0 || >=0.1.2-alpha.3 <0.2.0-0 || >=0.1.2-alpha.4 <0.2.0-0",
98
- "@deepseek-ai/dsh-session": ">=0.1.0-rc.7 <0.2.0-0 || >=0.1.1-rc.2 <0.2.0-0 || >=0.1.2-alpha.2 <0.2.0-0 || >=0.1.2-alpha.3 <0.2.0-0 || >=0.1.2-alpha.4 <0.2.0-0",
99
- "@deepseek-ai/dsh-session-query": ">=0.1.0-rc.7 <0.2.0-0 || >=0.1.1-rc.2 <0.2.0-0 || >=0.1.2-alpha.2 <0.2.0-0 || >=0.1.2-alpha.3 <0.2.0-0 || >=0.1.2-alpha.4 <0.2.0-0",
100
- "@deepseek-ai/dsh-settings": ">=0.1.0-rc.7 <0.2.0-0 || >=0.1.1-rc.2 <0.2.0-0 || >=0.1.2-alpha.2 <0.2.0-0 || >=0.1.2-alpha.3 <0.2.0-0 || >=0.1.2-alpha.4 <0.2.0-0",
101
- "@deepseek-ai/dsh-skill": ">=0.1.0-rc.7 <0.2.0-0 || >=0.1.1-rc.2 <0.2.0-0 || >=0.1.2-alpha.2 <0.2.0-0 || >=0.1.2-alpha.3 <0.2.0-0 || >=0.1.2-alpha.4 <0.2.0-0",
102
- "@deepseek-ai/dsh-system-prompt": ">=0.1.0-rc.7 <0.2.0-0 || >=0.1.1-rc.2 <0.2.0-0 || >=0.1.2-alpha.2 <0.2.0-0 || >=0.1.2-alpha.3 <0.2.0-0 || >=0.1.2-alpha.4 <0.2.0-0",
91
+ "@deepseek-ai/dsh-client-connection": ">=0.1.0-rc.7 <0.2.0-0 || >=0.1.1-rc.2 <0.2.0-0 || >=0.1.2-alpha.2 <0.2.0-0 || >=0.1.2-alpha.3 <0.2.0-0 || >=0.1.2-alpha.4 <0.2.0-0 || >=0.1.2-alpha.5 <0.2.0-0",
92
+ "@deepseek-ai/dsh-client-locale": ">=0.1.0-rc.7 <0.2.0-0 || >=0.1.1-rc.2 <0.2.0-0 || >=0.1.2-alpha.2 <0.2.0-0 || >=0.1.2-alpha.3 <0.2.0-0 || >=0.1.2-alpha.4 <0.2.0-0 || >=0.1.2-alpha.5 <0.2.0-0",
93
+ "@deepseek-ai/dsh-client-runtime": ">=0.1.0-rc.7 <0.2.0-0 || >=0.1.1-rc.2 <0.2.0-0 || >=0.1.2-alpha.3 <0.2.0-0 || >=0.1.2-alpha.4 <0.2.0-0 || >=0.1.2-alpha.5 <0.2.0-0",
94
+ "@deepseek-ai/dsh-client-store": ">=0.1.2-alpha.2 <0.2.0-0 || >=0.1.2-alpha.3 <0.2.0-0 || >=0.1.2-alpha.4 <0.2.0-0 || >=0.1.2-alpha.5 <0.2.0-0",
95
+ "@deepseek-ai/dsh-client-ui-input-trigger": ">=0.1.0-rc.7 <0.2.0-0 || >=0.1.1-rc.2 <0.2.0-0 || >=0.1.2-alpha.2 <0.2.0-0 || >=0.1.2-alpha.3 <0.2.0-0 || >=0.1.2-alpha.4 <0.2.0-0 || >=0.1.2-alpha.5 <0.2.0-0",
96
+ "@deepseek-ai/dsh-client-ui-settings": ">=0.1.0-rc.7 <0.2.0-0 || >=0.1.1-rc.2 <0.2.0-0 || >=0.1.2-alpha.2 <0.2.0-0 || >=0.1.2-alpha.3 <0.2.0-0 || >=0.1.2-alpha.4 <0.2.0-0 || >=0.1.2-alpha.5 <0.2.0-0",
97
+ "@deepseek-ai/dsh-client-ui-slots": ">=0.1.0-rc.7 <0.2.0-0 || >=0.1.1-rc.2 <0.2.0-0 || >=0.1.2-alpha.2 <0.2.0-0 || >=0.1.2-alpha.3 <0.2.0-0 || >=0.1.2-alpha.4 <0.2.0-0 || >=0.1.2-alpha.5 <0.2.0-0",
98
+ "@deepseek-ai/dsh-host-webserver": ">=0.1.0-rc.7 <0.2.0-0 || >=0.1.1-rc.2 <0.2.0-0 || >=0.1.2-alpha.2 <0.2.0-0 || >=0.1.2-alpha.3 <0.2.0-0 || >=0.1.2-alpha.4 <0.2.0-0 || >=0.1.2-alpha.5 <0.2.0-0",
99
+ "@deepseek-ai/dsh-session": ">=0.1.0-rc.7 <0.2.0-0 || >=0.1.1-rc.2 <0.2.0-0 || >=0.1.2-alpha.2 <0.2.0-0 || >=0.1.2-alpha.3 <0.2.0-0 || >=0.1.2-alpha.4 <0.2.0-0 || >=0.1.2-alpha.5 <0.2.0-0",
100
+ "@deepseek-ai/dsh-session-query": ">=0.1.0-rc.7 <0.2.0-0 || >=0.1.1-rc.2 <0.2.0-0 || >=0.1.2-alpha.2 <0.2.0-0 || >=0.1.2-alpha.3 <0.2.0-0 || >=0.1.2-alpha.4 <0.2.0-0 || >=0.1.2-alpha.5 <0.2.0-0",
101
+ "@deepseek-ai/dsh-settings": ">=0.1.0-rc.7 <0.2.0-0 || >=0.1.1-rc.2 <0.2.0-0 || >=0.1.2-alpha.2 <0.2.0-0 || >=0.1.2-alpha.3 <0.2.0-0 || >=0.1.2-alpha.4 <0.2.0-0 || >=0.1.2-alpha.5 <0.2.0-0",
102
+ "@deepseek-ai/dsh-skill": ">=0.1.0-rc.7 <0.2.0-0 || >=0.1.1-rc.2 <0.2.0-0 || >=0.1.2-alpha.2 <0.2.0-0 || >=0.1.2-alpha.3 <0.2.0-0 || >=0.1.2-alpha.4 <0.2.0-0 || >=0.1.2-alpha.5 <0.2.0-0",
103
+ "@deepseek-ai/dsh-system-prompt": ">=0.1.0-rc.7 <0.2.0-0 || >=0.1.1-rc.2 <0.2.0-0 || >=0.1.2-alpha.2 <0.2.0-0 || >=0.1.2-alpha.3 <0.2.0-0 || >=0.1.2-alpha.4 <0.2.0-0 || >=0.1.2-alpha.5 <0.2.0-0",
103
104
  "react": "^18.2.0",
104
105
  "react-dom": "^18.2.0"
105
106
  },
@@ -113,19 +114,19 @@
113
114
  },
114
115
  "devDependencies": {
115
116
  "@deepseek-ai/cordis": "^4.0.2",
116
- "@deepseek-ai/dsh-client-connection": "^0.1.2-alpha.4",
117
- "@deepseek-ai/dsh-client-locale": "^0.1.2-alpha.4",
118
- "@deepseek-ai/dsh-client-store": "^0.1.2-alpha.4",
119
- "@deepseek-ai/dsh-client-ui-input-trigger": "^0.1.2-alpha.4",
120
- "@deepseek-ai/dsh-client-ui-settings": "^0.1.2-alpha.4",
121
- "@deepseek-ai/dsh-client-ui-settings-plugins": "^0.1.2-alpha.4",
122
- "@deepseek-ai/dsh-client-ui-slots": "^0.1.2-alpha.4",
123
- "@deepseek-ai/dsh-host-webserver": "^0.1.2-alpha.4",
124
- "@deepseek-ai/dsh-session": "^0.1.2-alpha.4",
125
- "@deepseek-ai/dsh-session-query": "^0.1.2-alpha.4",
126
- "@deepseek-ai/dsh-settings": "^0.1.2-alpha.4",
127
- "@deepseek-ai/dsh-skill": "^0.1.2-alpha.4",
128
- "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.4",
117
+ "@deepseek-ai/dsh-client-connection": "^0.1.2-alpha.5",
118
+ "@deepseek-ai/dsh-client-locale": "^0.1.2-alpha.5",
119
+ "@deepseek-ai/dsh-client-store": "^0.1.2-alpha.5",
120
+ "@deepseek-ai/dsh-client-ui-input-trigger": "^0.1.2-alpha.5",
121
+ "@deepseek-ai/dsh-client-ui-settings": "^0.1.2-alpha.5",
122
+ "@deepseek-ai/dsh-client-ui-settings-plugins": "^0.1.2-alpha.5",
123
+ "@deepseek-ai/dsh-client-ui-slots": "^0.1.2-alpha.5",
124
+ "@deepseek-ai/dsh-host-webserver": "^0.1.2-alpha.5",
125
+ "@deepseek-ai/dsh-session": "^0.1.2-alpha.5",
126
+ "@deepseek-ai/dsh-session-query": "^0.1.2-alpha.5",
127
+ "@deepseek-ai/dsh-settings": "^0.1.2-alpha.5",
128
+ "@deepseek-ai/dsh-skill": "^0.1.2-alpha.5",
129
+ "@deepseek-ai/dsh-system-prompt": "^0.1.2-alpha.5",
129
130
  "@types/js-yaml": "^4.0.9",
130
131
  "@types/node": "^22.20.0",
131
132
  "@types/react": "~18.3.1",
@@ -14,8 +14,6 @@ import { ColorField, NumberField, PluginSettingsCard, SwitchField } from './sett
14
14
  import { booleanField, CardForm, colorField, numberField, type CardShell, type FieldState, type FormScope } from './settings-form.ts'
15
15
  // Single source for the TS-side dot defaults (the panel CSS mirrors these).
16
16
  import { DEFAULT_DOT_MODEL_COLOR, DEFAULT_DOT_USER_COLOR } from './panel/format.ts'
17
- // Re-export for any consumer that imported them from the card before the move.
18
- export { DEFAULT_DOT_MODEL_COLOR, DEFAULT_DOT_USER_COLOR }
19
17
 
20
18
  /** The card's projected state. */
21
19
  export interface SkillHubSettingsState extends CardShell {
package/src/client/api.ts CHANGED
@@ -19,6 +19,7 @@ import {
19
19
  type MarketSourceRequest,
20
20
  type MarketSourceResponse,
21
21
  type MarketSourcesResponse,
22
+ type MarketSourceVersionsResponse,
22
23
  type MarketSyncResponse,
23
24
  type RepoDiscoverResponse,
24
25
  type RepoImportCancelResponse,
@@ -193,6 +194,13 @@ export class SkillHubApi {
193
194
  return readJson<MarketSyncResponse>(response)
194
195
  }
195
196
 
197
+ /** Releases + branches of a market source for the version picker. */
198
+ async marketSourceVersions(repo: string): Promise<MarketSourceVersionsResponse> {
199
+ const url = SKILL_HUB_API.marketSourceVersions + '?repo=' + encodeURIComponent(repo)
200
+ const response = await fetchWithTimeout(url)
201
+ return readJson<MarketSourceVersionsResponse>(response)
202
+ }
203
+
196
204
  /** Pin a market source to an explicit ref (branch picker). */
197
205
  async setMarketSourceRef(repo: string, ref: string): Promise<MarketSourcesResponse> {
198
206
  const response = await fetchWithTimeout(SKILL_HUB_API.marketSourceRef, {
@@ -387,4 +395,14 @@ export class SkillHubApi {
387
395
  const response = await fetchWithTimeout(SKILL_HUB_API.sourceTrashClear, { method: 'POST' })
388
396
  return readJson<SourceTrashClearResponse>(response)
389
397
  }
398
+
399
+ /** Auto-fix a fixable diagnostic (e.g. unquoted colon). */
400
+ async fixDiagnostic(path: string): Promise<import('../protocol.ts').DiagnosticFixResponse> {
401
+ const response = await fetchWithTimeout(SKILL_HUB_API.diagnosticFix, {
402
+ method: 'POST',
403
+ headers: { 'content-type': 'application/json' },
404
+ body: JSON.stringify({ path } satisfies import('../protocol.ts').DiagnosticFixRequest),
405
+ })
406
+ return readJson<import('../protocol.ts').DiagnosticFixResponse>(response)
407
+ }
390
408
  }
@@ -21,3 +21,43 @@ export function errorMessage(error: unknown): string {
21
21
  return String(error)
22
22
  }
23
23
 
24
+ /**
25
+ * Whether a displayName is worth showing beside the kebab-case name.
26
+ * Pure case/punctuation variants (e.g. "Code Review" vs "code-review") are redundant.
27
+ */
28
+ export function isDisplayNameDistinct(name: string, displayName: string | undefined): displayName is string {
29
+ if (displayName === undefined) return false
30
+ const normalize = (s: string): string => s.toLowerCase().replace(/[^a-z0-9\u4e00-\u9fff]+/g, '')
31
+ return normalize(displayName) !== normalize(name)
32
+ }
33
+
34
+ /**
35
+ * Copy text to the clipboard. Uses the async API with a positioned-textarea
36
+ * fallback for insecure contexts. Resolves true only when the copy succeeded.
37
+ */
38
+ export async function copyTextToClipboard(text: string): Promise<boolean> {
39
+ if (navigator.clipboard?.writeText !== undefined) {
40
+ try {
41
+ await navigator.clipboard.writeText(text)
42
+ return true
43
+ } catch {
44
+ // Fall through to the legacy path below.
45
+ }
46
+ }
47
+ try {
48
+ const ta = document.createElement('textarea')
49
+ ta.value = text
50
+ ta.setAttribute('readonly', '')
51
+ ta.style.position = 'fixed'
52
+ ta.style.top = '0'
53
+ ta.style.opacity = '0'
54
+ document.body.appendChild(ta)
55
+ ta.select()
56
+ const ok = document.execCommand('copy')
57
+ ta.remove()
58
+ return ok
59
+ } catch {
60
+ return false
61
+ }
62
+ }
63
+