dsh-skill-hub 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.
Files changed (79) hide show
  1. package/README.md +146 -33
  2. package/lib/client.js +3477 -436
  3. package/lib/client.js.map +1 -1
  4. package/lib/index.js +2408 -366
  5. package/lib/types/client/SkillHubSettingsCard.d.ts +5 -0
  6. package/lib/types/client/api-config-scope.d.ts +6 -0
  7. package/lib/types/client/api.d.ts +44 -2
  8. package/lib/types/client/grouping.d.ts +64 -0
  9. package/lib/types/client/locales.d.ts +155 -18
  10. package/lib/types/client/market-catalog.d.ts +14 -0
  11. package/lib/types/client/panel/DisabledRow.d.ts +12 -0
  12. package/lib/types/client/panel/GroupSummary.d.ts +10 -0
  13. package/lib/types/client/panel/MarketView.d.ts +11 -0
  14. package/lib/types/client/panel/ScenesView.d.ts +11 -0
  15. package/lib/types/client/panel/SkillDetailView.d.ts +31 -0
  16. package/lib/types/client/panel/SkillHubPanel.d.ts +11 -3
  17. package/lib/types/client/panel/SkillRow.d.ts +11 -0
  18. package/lib/types/client/panel/SourceStatusBadge.d.ts +18 -0
  19. package/lib/types/client/panel/SourcesView.d.ts +10 -0
  20. package/lib/types/client/panel/TagEditorView.d.ts +29 -0
  21. package/lib/types/client/panel/dialogs.d.ts +73 -0
  22. package/lib/types/client/panel/format.d.ts +14 -0
  23. package/lib/types/client/panel/useSkillHub.d.ts +183 -0
  24. package/lib/types/client/settings-card.d.ts +21 -4
  25. package/lib/types/client/settings-form.d.ts +2 -0
  26. package/lib/types/client/settings-nav-icon.d.ts +8 -0
  27. package/lib/types/index.d.ts +6 -0
  28. package/lib/types/protocol.d.ts +428 -7
  29. package/lib/types/provider.d.ts +6 -1
  30. package/lib/types/repo.d.ts +133 -0
  31. package/lib/types/routes.d.ts +9 -4
  32. package/lib/types/skillfs.d.ts +19 -14
  33. package/lib/types/stats.d.ts +17 -4
  34. package/lib/types/store.d.ts +112 -6
  35. package/lib/types/update.d.ts +20 -0
  36. package/package.json +5 -3
  37. package/src/client/SkillHubSettingsCard.tsx +61 -8
  38. package/src/client/api-config-scope.test.ts +79 -0
  39. package/src/client/api-config-scope.ts +28 -4
  40. package/src/client/api.ts +239 -9
  41. package/src/client/grouping.test.ts +109 -0
  42. package/src/client/grouping.ts +120 -0
  43. package/src/client/index.tsx +5 -2
  44. package/src/client/locales.ts +314 -40
  45. package/src/client/market-catalog.ts +22 -0
  46. package/src/client/panel/DisabledRow.tsx +32 -0
  47. package/src/client/panel/GroupSummary.tsx +27 -0
  48. package/src/client/panel/MarketView.tsx +173 -0
  49. package/src/client/panel/ScenesView.tsx +75 -0
  50. package/src/client/panel/SkillDetailView.tsx +95 -0
  51. package/src/client/panel/SkillHubPanel.tsx +251 -257
  52. package/src/client/panel/SkillRow.tsx +54 -0
  53. package/src/client/panel/SourceStatusBadge.tsx +61 -0
  54. package/src/client/panel/SourcesView.tsx +104 -0
  55. package/src/client/panel/TagEditorView.tsx +89 -0
  56. package/src/client/panel/dialogs.tsx +185 -0
  57. package/src/client/panel/format.ts +31 -0
  58. package/src/client/panel/panel.module.css +209 -45
  59. package/src/client/panel/useSkillHub.ts +797 -0
  60. package/src/client/settings-card.module.css +44 -28
  61. package/src/client/settings-card.tsx +64 -8
  62. package/src/client/settings-form.ts +12 -0
  63. package/src/client/settings-nav-icon.ts +42 -0
  64. package/src/index.ts +19 -15
  65. package/src/protocol.ts +460 -7
  66. package/src/provider.test.ts +2 -4
  67. package/src/provider.ts +18 -14
  68. package/src/repo.test.ts +212 -0
  69. package/src/repo.ts +528 -0
  70. package/src/routes.test.ts +564 -52
  71. package/src/routes.ts +999 -153
  72. package/src/skillfs.test.ts +98 -16
  73. package/src/skillfs.ts +68 -33
  74. package/src/stats.test.ts +52 -17
  75. package/src/stats.ts +55 -18
  76. package/src/store.test.ts +205 -1
  77. package/src/store.ts +527 -24
  78. package/src/update.test.ts +73 -0
  79. package/src/update.ts +136 -0
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Shared dialog family for the panel: one overlay shell plus the specific
3
+ * dialogs (group-close conflict, sync/delete confirm, branch picker, and
4
+ * market sync selection). Every dialog closes on outside click and keeps
5
+ * the same role/aria shell; the panel owns all dialog state.
6
+ */
7
+ import { type JSX } from 'react';
8
+ import type { CollectionGroup, SkillTag } from '../../protocol.ts';
9
+ /** A group-close conflict waiting for the user's decision. */
10
+ export interface ConflictDialogState {
11
+ /** Group key: 'tag:<id>' or 'col:<name>'. */
12
+ key: string;
13
+ name: string;
14
+ /** Conflicting skill names (enabled here and in other groups). */
15
+ conflicts: string[];
16
+ }
17
+ /** A destructive/sync confirmation waiting for the user's decision. */
18
+ export interface ConfirmDialogState {
19
+ kind: 'sync' | 'delete';
20
+ repo: string;
21
+ /** Skills the action applies to. */
22
+ skills: string[];
23
+ }
24
+ /** Branch picker shown when a repo has no release and no pinned ref. */
25
+ export interface BranchChoiceState {
26
+ repo: string;
27
+ branches: string[];
28
+ selected: string;
29
+ }
30
+ /** Post-sync skill update dialog: which tracked skills to refresh. */
31
+ export interface MarketSyncDialogState {
32
+ repo: string;
33
+ ref: string;
34
+ skills: string[];
35
+ selected: ReadonlySet<string>;
36
+ }
37
+ /** Simple confirm dialog: title + text + optional skill list + cancel/confirm. */
38
+ export declare function ConfirmDialog(props: {
39
+ title: string;
40
+ text: string;
41
+ /** Optional skill names listed between the text and the buttons. */
42
+ items?: readonly string[];
43
+ confirmLabel: string;
44
+ /** Danger-styled confirm button (destructive actions). */
45
+ danger?: boolean;
46
+ onCancel: () => void;
47
+ onConfirm: () => void;
48
+ }): JSX.Element;
49
+ /** Group-close conflict: which skills stay on, and which other groups also enable them. */
50
+ export declare function ConflictDialog(props: {
51
+ dialog: ConflictDialogState;
52
+ tags: readonly SkillTag[];
53
+ collections: readonly CollectionGroup[];
54
+ onClose: () => void;
55
+ onKeepOn: () => void;
56
+ onCloseAll: () => void;
57
+ }): JSX.Element;
58
+ /** Branch picker for a repo without releases (pins the ref, then rescans). */
59
+ export declare function BranchChoiceDialog(props: {
60
+ choice: BranchChoiceState;
61
+ busy: boolean;
62
+ onSelect: (branch: string) => void;
63
+ onCancel: () => void;
64
+ onConfirm: () => void;
65
+ }): JSX.Element;
66
+ /** Post-sync selection: which tracked skills to batch-update to the new version. */
67
+ export declare function MarketSyncDialog(props: {
68
+ dialog: MarketSyncDialogState;
69
+ busy: boolean;
70
+ onToggle: (name: string, checked: boolean) => void;
71
+ onCancel: () => void;
72
+ onConfirm: () => void;
73
+ }): JSX.Element;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Shared display helpers for the panel views (skill rows, detail view, and
3
+ * dialogs): dot color styling, relative/absolute time text, and commit-SHA
4
+ * shortening. Kept in one place so the views render identically.
5
+ */
6
+ import type { CSSProperties } from 'react';
7
+ /** Dot inline style from the user-chosen color (undefined keeps the CSS default). */
8
+ export declare function dotStyle(color: string | undefined): CSSProperties | undefined;
9
+ /** Localized relative-time text for a Unix-ms timestamp. */
10
+ export declare function relativeTimeText(ms: number): string;
11
+ /** Localized absolute time for the detail meta (e.g. "2026/8/16 11:00:00"). */
12
+ export declare function formatDateTime(ms: number): string;
13
+ /** Short form of a commit SHA for display. */
14
+ export declare function shortSha(sha: string): string;
@@ -0,0 +1,183 @@
1
+ /**
2
+ * useSkillHub — the panel's state owner. Everything the skill hub panel
3
+ * knows (catalog, groups, sources, market, dialogs, drafts) plus every
4
+ * async flow lives here; the panel itself only renders. All hooks run
5
+ * unconditionally at the top, so the panel may early-return for the detail
6
+ * and tag-editor views without violating the rules of hooks.
7
+ */
8
+ import { type FormEvent } from 'react';
9
+ import type { CatalogResponse, CatalogSkill, DisabledSkill, GroupsResponse, HubConfig, MarketSourceRecord, RepoDiscoverResponse, RepoImportResponse, SkillDetail, SkillTag, SourceCheckResult, SourcesResponse, UpdateCheckResponse, WritableRoot } from '../../protocol.ts';
10
+ import type { SkillHubApi } from '../api.ts';
11
+ import { type SortKey, type GroupSwitchState } from '../grouping.ts';
12
+ import type { BranchChoiceState, ConfirmDialogState, ConflictDialogState, MarketSyncDialogState } from './dialogs.tsx';
13
+ /** One-shot self-update check state. */
14
+ type UpdateState = {
15
+ status: 'idle';
16
+ } | {
17
+ status: 'checking';
18
+ } | {
19
+ status: 'ready';
20
+ data: UpdateCheckResponse;
21
+ } | {
22
+ status: 'error';
23
+ message: string;
24
+ };
25
+ /** Repo scanner/import state (drives the market source preview). */
26
+ type RepoDiscoverState = {
27
+ status: 'idle';
28
+ } | {
29
+ status: 'scanning';
30
+ } | {
31
+ status: 'ready';
32
+ data: RepoDiscoverResponse;
33
+ } | {
34
+ status: 'error';
35
+ message: string;
36
+ };
37
+ /** Market sources list state. */
38
+ type MarketState = {
39
+ status: 'loading' | 'ready' | 'error';
40
+ repos: MarketSourceRecord[];
41
+ };
42
+ /** The hook's result: the panel's complete state + action surface. */
43
+ export type SkillHubState = ReturnType<typeof useSkillHub>;
44
+ export declare function useSkillHub(api: SkillHubApi): {
45
+ catalog: CatalogResponse | null;
46
+ loading: boolean;
47
+ loadError: string | null;
48
+ successBanner: string | null;
49
+ updateState: UpdateState;
50
+ repoDiscoverState: RepoDiscoverState;
51
+ scanningRepo: string | null;
52
+ repoSelected: ReadonlySet<string>;
53
+ repoImporting: boolean;
54
+ repoResult: RepoImportResponse | null;
55
+ search: string;
56
+ detail: SkillDetail | null;
57
+ detailLoading: boolean;
58
+ busyNames: ReadonlySet<string>;
59
+ batchBusy: boolean;
60
+ showForm: boolean;
61
+ formName: string;
62
+ formDesc: string;
63
+ formRoot: WritableRoot;
64
+ formBusy: boolean;
65
+ formMessage: {
66
+ kind: "error" | "success";
67
+ text: string;
68
+ } | null;
69
+ uses: ReadonlyMap<string, {
70
+ count: number;
71
+ lastUsed?: number;
72
+ }>;
73
+ hubConfig: HubConfig | null;
74
+ tab: "sources" | "scenes" | "market";
75
+ skillView: "flat" | "groups";
76
+ sourceFilter: string;
77
+ sortKey: SortKey;
78
+ marketState: MarketState;
79
+ marketCheck: Readonly<Record<string, {
80
+ repo: string;
81
+ ref?: string;
82
+ latestTag?: string;
83
+ updateAvailable: boolean;
84
+ commitSha: string;
85
+ throttled?: boolean;
86
+ error?: string;
87
+ }>>;
88
+ branchChoice: BranchChoiceState | null;
89
+ branchBusy: boolean;
90
+ marketSyncDialog: MarketSyncDialogState | null;
91
+ syncingMarket: string | null;
92
+ syncBusy: boolean;
93
+ newSourceName: string;
94
+ groupsState: GroupsResponse | null;
95
+ sourcesState: SourcesResponse | null;
96
+ sourceCheck: Readonly<Record<string, SourceCheckResult>>;
97
+ checkingSource: string | null;
98
+ syncingSource: string | null;
99
+ conflictDialog: ConflictDialogState | null;
100
+ confirmDialog: ConfirmDialogState | null;
101
+ deleteSkillDialog: string | null;
102
+ confirmClearTrash: boolean;
103
+ updateAllDialog: boolean;
104
+ editingTag: SkillTag | null;
105
+ editName: string;
106
+ membersDraft: ReadonlySet<string>;
107
+ newTagName: string;
108
+ tagBusy: boolean;
109
+ editSearch: string;
110
+ collapsedGroups: ReadonlySet<string>;
111
+ showLegend: boolean;
112
+ actionNames: Set<string>;
113
+ viewNames: Set<string>;
114
+ normalized: string;
115
+ origins: Record<string, string>;
116
+ sourceOptions: string[];
117
+ filtered: CatalogSkill[];
118
+ sorted: CatalogSkill[];
119
+ setLoadError: import("react").Dispatch<import("react").SetStateAction<string | null>>;
120
+ setSuccessBanner: import("react").Dispatch<import("react").SetStateAction<string | null>>;
121
+ setSearch: import("react").Dispatch<import("react").SetStateAction<string>>;
122
+ setDetail: import("react").Dispatch<import("react").SetStateAction<SkillDetail | null>>;
123
+ setShowForm: import("react").Dispatch<import("react").SetStateAction<boolean>>;
124
+ setFormName: import("react").Dispatch<import("react").SetStateAction<string>>;
125
+ setFormDesc: import("react").Dispatch<import("react").SetStateAction<string>>;
126
+ setFormRoot: import("react").Dispatch<import("react").SetStateAction<WritableRoot>>;
127
+ setFormMessage: import("react").Dispatch<import("react").SetStateAction<{
128
+ kind: "error" | "success";
129
+ text: string;
130
+ } | null>>;
131
+ setRepoSelected: import("react").Dispatch<import("react").SetStateAction<ReadonlySet<string>>>;
132
+ setTab: import("react").Dispatch<import("react").SetStateAction<"sources" | "scenes" | "market">>;
133
+ setSkillView: import("react").Dispatch<import("react").SetStateAction<"flat" | "groups">>;
134
+ setSourceFilter: import("react").Dispatch<import("react").SetStateAction<string>>;
135
+ setSortKey: import("react").Dispatch<import("react").SetStateAction<SortKey>>;
136
+ setBranchChoice: import("react").Dispatch<import("react").SetStateAction<BranchChoiceState | null>>;
137
+ setMarketSyncDialog: import("react").Dispatch<import("react").SetStateAction<MarketSyncDialogState | null>>;
138
+ setNewSourceName: import("react").Dispatch<import("react").SetStateAction<string>>;
139
+ setConflictDialog: import("react").Dispatch<import("react").SetStateAction<ConflictDialogState | null>>;
140
+ setConfirmDialog: import("react").Dispatch<import("react").SetStateAction<ConfirmDialogState | null>>;
141
+ setDeleteSkillDialog: import("react").Dispatch<import("react").SetStateAction<string | null>>;
142
+ setConfirmClearTrash: import("react").Dispatch<import("react").SetStateAction<boolean>>;
143
+ setUpdateAllDialog: import("react").Dispatch<import("react").SetStateAction<boolean>>;
144
+ setEditingTag: import("react").Dispatch<import("react").SetStateAction<SkillTag | null>>;
145
+ setEditName: import("react").Dispatch<import("react").SetStateAction<string>>;
146
+ setMembersDraft: import("react").Dispatch<import("react").SetStateAction<ReadonlySet<string>>>;
147
+ setNewTagName: import("react").Dispatch<import("react").SetStateAction<string>>;
148
+ setEditSearch: import("react").Dispatch<import("react").SetStateAction<string>>;
149
+ setShowLegend: import("react").Dispatch<import("react").SetStateAction<boolean>>;
150
+ toggleGroupCollapse: (key: string) => void;
151
+ checkUpdate: () => Promise<void>;
152
+ loadMarket: () => Promise<void>;
153
+ openDetail: (name: string) => Promise<void>;
154
+ toggle: (skill: CatalogSkill, enabled: boolean) => Promise<void>;
155
+ enableDisabled: (record: DisabledSkill) => Promise<void>;
156
+ batchToggleNames: (names: string[], enabled: boolean) => Promise<void>;
157
+ toggleGroup: (key: string, name: string, view: GroupSwitchState) => void;
158
+ resolveConflict: (closeAll: boolean) => Promise<void>;
159
+ runConfirmed: () => Promise<void>;
160
+ checkSources: (repo?: string) => Promise<void>;
161
+ requestSync: (repo: string, skills: string[]) => void;
162
+ requestDelete: (repo: string, skills: string[]) => void;
163
+ restoreTrash: (name: string) => Promise<void>;
164
+ clearTrash: () => Promise<void>;
165
+ requestDeleteSkill: (name: string) => void;
166
+ runDeleteSkill: () => Promise<void>;
167
+ createTag: (event: FormEvent) => Promise<void>;
168
+ deleteTag: (id: string) => Promise<void>;
169
+ saveTag: (id: string, name: string, memberNames: string[] | null) => Promise<void>;
170
+ addSource: (input: string) => Promise<void>;
171
+ addMarketSource: () => Promise<void>;
172
+ removeMarketSource: (repo: string) => Promise<void>;
173
+ scanRepo: (input: string) => Promise<void>;
174
+ confirmBranchChoice: () => Promise<void>;
175
+ toggleRepoSelected: (path: string, checked: boolean) => void;
176
+ importRepo: () => Promise<void>;
177
+ checkMarket: () => Promise<void>;
178
+ syncMarketSource: (repo: string) => Promise<void>;
179
+ confirmMarketSync: () => Promise<void>;
180
+ updateAll: () => Promise<void>;
181
+ create: (event: FormEvent) => Promise<void>;
182
+ };
183
+ export {};
@@ -1,8 +1,7 @@
1
1
  /**
2
- * Shared chrome for the plugin settings card, vendored from the dsh-web-ui
3
- * family bucket (packages/dsh-task-board/src/client/PluginSettingsCard.tsx):
4
- * a disclosure header naming the plugin and what its settings govern, the
5
- * controls inside, and the save that writes them. Renders nothing while the
2
+ * Shared chrome for the plugin settings card, aligned with the official
3
+ * dsh-client-ui-settings-plugins PluginCard design language: the same tokens,
4
+ * disclosure header, field rhythm, and copy. Renders nothing while the
6
5
  * namespace is unavailable — a deployment that does not compose the owning
7
6
  * plugin should show no trace of it.
8
7
  */
@@ -62,3 +61,21 @@ export interface SwitchFieldProps {
62
61
  onReset: () => void;
63
62
  }
64
63
  export declare function SwitchField(props: SwitchFieldProps): ReactElement;
64
+ /** One staged color field: a native color picker plus the hex draft text. */
65
+ export interface ColorFieldProps {
66
+ id: string;
67
+ label: string;
68
+ hint: string;
69
+ inheritLabel: string;
70
+ overriddenLabel: string;
71
+ resetLabel: string;
72
+ disabled: boolean;
73
+ /** Effective hex (#rrggbb) when overridden; empty string means inherit. */
74
+ text: string;
75
+ /** The default color the picker shows while inheriting (per-field). */
76
+ defaultColor: string;
77
+ overridden: boolean;
78
+ onEdit: (text: string) => void;
79
+ onReset: () => void;
80
+ }
81
+ export declare function ColorField(props: ColorFieldProps): ReactElement;
@@ -21,6 +21,8 @@ export interface FieldSpec {
21
21
  }
22
22
  /** A boolean field, edited through true/false draft text. */
23
23
  export declare function booleanField(field: string): FieldSpec;
24
+ /** A #rrggbb color field, edited through hex draft text. */
25
+ export declare function colorField(field: string): FieldSpec;
24
26
  /** Card-level state the chrome renders. */
25
27
  export interface CardShell {
26
28
  available: boolean;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * The settings shell renders section nav icons from a hard-coded id map and
3
+ * exposes no registrant icon option, so the skill-hub section falls back to
4
+ * the generic gear. This scoped DOM patch swaps in the official skill icon
5
+ * whenever the settings dialog re-renders.
6
+ */
7
+ /** Swap the settings nav gear for the skill icon and keep it in sync. */
8
+ export declare function applySettingsNavIcon(): () => void;
@@ -18,6 +18,12 @@ export interface Config {
18
18
  announceToAgent?: boolean;
19
19
  /** Master switch for the plugin (routes, prompt section). */
20
20
  enabled?: boolean;
21
+ /** Show per-skill invocation count chip. Default true. */
22
+ showUseCount?: boolean;
23
+ /** Show per-skill last-used relative time. Default true. */
24
+ showUseTime?: boolean;
25
+ /** Show group-header usage summaries (count + last used). Default true. */
26
+ showGroupSummary?: boolean;
21
27
  }
22
28
  export declare const Config: z<Config>;
23
29
  /** Model-facing announcement: plugin presence, capabilities, and limits. */