@yancyyu/openhermit 1.6.28 → 1.6.30

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 (177) hide show
  1. package/dist-renderer/assets/ProjectEditorOverlay-DsQt4FHy.js +52 -0
  2. package/dist-renderer/assets/{TeamGraphOverlay-Ba5njic5.js → TeamGraphOverlay-BjZC53xf.js} +1 -1
  3. package/dist-renderer/assets/{_basePickBy-BvnK-OC1.js → _basePickBy-CrWocIjq.js} +1 -1
  4. package/dist-renderer/assets/{_baseUniq-DmFYXx9G.js → _baseUniq-B6d8ysWi.js} +1 -1
  5. package/dist-renderer/assets/{arc-DX4ZQFY4.js → arc-DAIYCFP8.js} +1 -1
  6. package/dist-renderer/assets/{architectureDiagram-VXUJARFQ-DfYr3vEN.js → architectureDiagram-VXUJARFQ-B3UudXJh.js} +1 -1
  7. package/dist-renderer/assets/{blockDiagram-VD42YOAC-DuXdVeWn.js → blockDiagram-VD42YOAC-DbptKQ4W.js} +1 -1
  8. package/dist-renderer/assets/{c4Diagram-YG6GDRKO-Bw2nixXe.js → c4Diagram-YG6GDRKO-C4WQuZpV.js} +1 -1
  9. package/dist-renderer/assets/channel-DbjZvWii.js +1 -0
  10. package/dist-renderer/assets/{chunk-4BX2VUAB-DLiNGQoE.js → chunk-4BX2VUAB-Dp7fVpI_.js} +1 -1
  11. package/dist-renderer/assets/{chunk-55IACEB6-B1L_8VIF.js → chunk-55IACEB6-B8KGfbAy.js} +1 -1
  12. package/dist-renderer/assets/{chunk-B4BG7PRW-DaZMWKGk.js → chunk-B4BG7PRW-BG1oJrjA.js} +1 -1
  13. package/dist-renderer/assets/{chunk-DI55MBZ5-ku-dflJG.js → chunk-DI55MBZ5-DRmxNjht.js} +1 -1
  14. package/dist-renderer/assets/{chunk-FMBD7UC4-DV-mF1dP.js → chunk-FMBD7UC4-D6VLvy16.js} +1 -1
  15. package/dist-renderer/assets/{chunk-QN33PNHL-ByGcDFQ0.js → chunk-QN33PNHL-DZou1667.js} +1 -1
  16. package/dist-renderer/assets/{chunk-QZHKN3VN-7dv-Min8.js → chunk-QZHKN3VN-CghmasSh.js} +1 -1
  17. package/dist-renderer/assets/{chunk-TZMSLE5B-WdXL5fTu.js → chunk-TZMSLE5B-B7apcMPK.js} +1 -1
  18. package/dist-renderer/assets/classDiagram-2ON5EDUG-D_FGxxsl.js +1 -0
  19. package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-D_FGxxsl.js +1 -0
  20. package/dist-renderer/assets/clone-CJ1kxO2J.js +1 -0
  21. package/dist-renderer/assets/{cose-bilkent-S5V4N54A-CNcsvqPl.js → cose-bilkent-S5V4N54A-05e5uQDp.js} +1 -1
  22. package/dist-renderer/assets/{dagre-6UL2VRFP-DBNx4qqx.js → dagre-6UL2VRFP-B06bRykF.js} +1 -1
  23. package/dist-renderer/assets/{diagram-PSM6KHXK-BfVlT6sT.js → diagram-PSM6KHXK-CY7VYQ7c.js} +1 -1
  24. package/dist-renderer/assets/{diagram-QEK2KX5R-HvVjs0K6.js → diagram-QEK2KX5R-BjKEH7dD.js} +1 -1
  25. package/dist-renderer/assets/{diagram-S2PKOQOG-DYb_KnWS.js → diagram-S2PKOQOG-Bf4ELS1_.js} +1 -1
  26. package/dist-renderer/assets/{erDiagram-Q2GNP2WA-Ba-IgI5G.js → erDiagram-Q2GNP2WA-DJ753_L9.js} +1 -1
  27. package/dist-renderer/assets/{flowDiagram-NV44I4VS-2iDN8Kpj.js → flowDiagram-NV44I4VS-B71S-lC-.js} +1 -1
  28. package/dist-renderer/assets/{ganttDiagram-JELNMOA3-Byjf8Fa3.js → ganttDiagram-JELNMOA3-C_U42mSZ.js} +1 -1
  29. package/dist-renderer/assets/{gitGraphDiagram-V2S2FVAM-DbKvfZ_j.js → gitGraphDiagram-V2S2FVAM-DKUJU4Ns.js} +1 -1
  30. package/dist-renderer/assets/{graph-Enirf-f8.js → graph-DY3qbzqj.js} +1 -1
  31. package/dist-renderer/assets/{index-DY1zqsb6.js → index-BlOrAXp3.js} +551 -537
  32. package/dist-renderer/assets/{index-AjxP_rE_.js → index-Bs27J5gB.js} +1 -1
  33. package/dist-renderer/assets/{index-CtlzGepK.js → index-C8B_nKOF.js} +1 -1
  34. package/dist-renderer/assets/index-CmZPUEhS.css +1 -0
  35. package/dist-renderer/assets/{index-COZPUWJW.js → index-DLKyDr4T.js} +1 -1
  36. package/dist-renderer/assets/{index-DdhqolqE.js → index-Dhsk3_DD.js} +1 -1
  37. package/dist-renderer/assets/{index-ChR1D6ZF.js → index-GpUvV2xs.js} +1 -1
  38. package/dist-renderer/assets/{infoDiagram-HS3SLOUP-D6uicwz1.js → infoDiagram-HS3SLOUP-BNs0y3IG.js} +1 -1
  39. package/dist-renderer/assets/{journeyDiagram-XKPGCS4Q-DqwZsXlQ.js → journeyDiagram-XKPGCS4Q-CqPnw4UV.js} +1 -1
  40. package/dist-renderer/assets/{kanban-definition-3W4ZIXB7-fCDVhVUm.js → kanban-definition-3W4ZIXB7-SLlzcUJ2.js} +1 -1
  41. package/dist-renderer/assets/{layout-CPFgj98r.js → layout-BZLlNmbr.js} +1 -1
  42. package/dist-renderer/assets/{linear-CYiQ7Y3M.js → linear-qz6v45xy.js} +1 -1
  43. package/dist-renderer/assets/{mindmap-definition-VGOIOE7T-D31dS2KE.js → mindmap-definition-VGOIOE7T-B1-kmEWV.js} +1 -1
  44. package/dist-renderer/assets/{pieDiagram-ADFJNKIX-BOsCJfds.js → pieDiagram-ADFJNKIX-B8a02iNx.js} +1 -1
  45. package/dist-renderer/assets/{quadrantDiagram-AYHSOK5B-CYTVQCfr.js → quadrantDiagram-AYHSOK5B-BKv1Xfou.js} +1 -1
  46. package/dist-renderer/assets/{requirementDiagram-UZGBJVZJ-CODCFpkt.js → requirementDiagram-UZGBJVZJ-B3DUpZi2.js} +1 -1
  47. package/dist-renderer/assets/{sankeyDiagram-TZEHDZUN-Z4ce9ZtZ.js → sankeyDiagram-TZEHDZUN-DmPzuTsy.js} +1 -1
  48. package/dist-renderer/assets/{sequenceDiagram-WL72ISMW-CmS9TxhW.js → sequenceDiagram-WL72ISMW-Bo7RelRb.js} +1 -1
  49. package/dist-renderer/assets/{stateDiagram-FKZM4ZOC-o9k-ns3q.js → stateDiagram-FKZM4ZOC-1epX98gV.js} +1 -1
  50. package/dist-renderer/assets/{stateDiagram-v2-4FDKWEC3-CxHMyEt1.js → stateDiagram-v2-4FDKWEC3-03Ym9PTr.js} +1 -1
  51. package/dist-renderer/assets/{timeline-definition-IT6M3QCI-B6T3zrde.js → timeline-definition-IT6M3QCI-r6isC62H.js} +1 -1
  52. package/dist-renderer/assets/{treemap-GDKQZRPO-CVd5GNDw.js → treemap-GDKQZRPO-CGKpOUF2.js} +1 -1
  53. package/dist-renderer/assets/{xychartDiagram-PRI3JC2R-CleBrdqc.js → xychartDiagram-PRI3JC2R-t4-rwdAw.js} +1 -1
  54. package/dist-renderer/index.html +2 -2
  55. package/package.json +4 -1
  56. package/src/main/ipc/extensions.ts +353 -0
  57. package/src/main/server.ts +907 -184
  58. package/src/main/services/extensions/ExtensionFacadeService.ts +135 -0
  59. package/src/main/services/extensions/catalog/GlamaMcpEnrichmentService.ts +190 -0
  60. package/src/main/services/extensions/catalog/McpCatalogAggregator.ts +150 -0
  61. package/src/main/services/extensions/catalog/OfficialMcpRegistryService.ts +381 -0
  62. package/src/main/services/extensions/catalog/PluginCatalogService.ts +392 -0
  63. package/src/main/services/extensions/credentials/CredentialService.ts +343 -0
  64. package/src/main/services/extensions/install/McpInstallService.ts +407 -0
  65. package/src/main/services/extensions/install/PluginInstallService.ts +198 -0
  66. package/src/main/services/extensions/runtime/ClaudeCodeAdapter.ts +199 -0
  67. package/src/main/services/extensions/runtime/CodexAdapter.ts +100 -0
  68. package/src/main/services/extensions/runtime/CursorAdapter.ts +154 -0
  69. package/src/main/services/extensions/runtime/ExtensionsRuntimeAdapter.ts +172 -0
  70. package/src/main/services/extensions/runtime/GeminiAdapter.ts +91 -0
  71. package/src/main/services/extensions/runtime/HarnessInstallAdapter.ts +49 -0
  72. package/src/main/services/extensions/runtime/McpConfigStateReader.ts +209 -0
  73. package/src/main/services/extensions/runtime/OpenCodeAdapter.ts +91 -0
  74. package/src/main/services/extensions/runtime/adapterRegistry.ts +54 -0
  75. package/src/main/services/extensions/runtime/mcpDiagnosticsParser.ts +214 -0
  76. package/src/main/services/extensions/runtime/mcpRuntimeJson.ts +45 -0
  77. package/src/main/services/extensions/skills/SkillImportService.ts +155 -0
  78. package/src/main/services/extensions/skills/SkillMetadataParser.ts +323 -0
  79. package/src/main/services/extensions/skills/SkillPlanService.ts +411 -0
  80. package/src/main/services/extensions/skills/SkillReviewService.ts +73 -0
  81. package/src/main/services/extensions/skills/SkillRootsResolver.ts +49 -0
  82. package/src/main/services/extensions/skills/SkillScaffoldService.ts +89 -0
  83. package/src/main/services/extensions/skills/SkillScanner.ts +117 -0
  84. package/src/main/services/extensions/skills/SkillValidator.ts +69 -0
  85. package/src/main/services/extensions/skills/SkillsCatalogService.ts +92 -0
  86. package/src/main/services/extensions/skills/SkillsMutationService.ts +146 -0
  87. package/src/main/services/extensions/skills/SkillsWatcherService.ts +134 -0
  88. package/src/main/services/extensions/state/McpInstallationStateService.ts +42 -0
  89. package/src/main/services/extensions/state/PluginInstallationStateService.ts +281 -0
  90. package/src/main/services/identity/AgentTeamsIdentityStore.ts +218 -0
  91. package/src/main/services/runtime/providerAwareCliEnv.ts +60 -0
  92. package/src/main/services/session-intelligence/UsageTelemetryService.ts +33 -18
  93. package/src/main/services/team/ClaudeBinaryResolver.ts +469 -0
  94. package/src/main/services/team/ClaudeDoctorProbe.ts +0 -0
  95. package/src/main/services/team/cliFlavor.ts +54 -0
  96. package/src/main/services/teams-mvp/CollaborationBoardService.ts +310 -0
  97. package/src/main/services/teams-mvp/TaskDispatchService.ts +883 -95
  98. package/src/main/services/teams-mvp/TeamProvisioningService.ts +58 -19
  99. package/src/main/services/teams-mvp/TeamWorkspaceService.ts +25 -2
  100. package/src/main/services/teams-mvp/index.ts +3 -0
  101. package/src/main/utils/atomicWrite.ts +72 -0
  102. package/src/main/utils/childProcess.ts +554 -0
  103. package/src/main/utils/cliEnv.ts +54 -0
  104. package/src/main/utils/cliPathMerge.ts +97 -0
  105. package/src/main/utils/pathDecoder.ts +664 -0
  106. package/src/main/utils/pathValidation.ts +432 -0
  107. package/src/main/utils/shellEnv.ts +331 -0
  108. package/src/renderer/App.tsx +5 -0
  109. package/src/renderer/api/httpClient.ts +128 -0
  110. package/src/renderer/components/extensions/ExtensionStoreView.tsx +59 -34
  111. package/src/renderer/components/extensions/ExtensionsSubTabTrigger.tsx +1 -1
  112. package/src/renderer/components/extensions/common/ExtensionToast.tsx +141 -0
  113. package/src/renderer/components/extensions/common/HarnessSelector.tsx +71 -0
  114. package/src/renderer/components/extensions/env/EnvVarPanel.tsx +335 -0
  115. package/src/renderer/components/extensions/env/ProjectEnvPanel.tsx +239 -0
  116. package/src/renderer/components/extensions/mcp/CustomMcpServerDialog.tsx +14 -223
  117. package/src/renderer/components/extensions/mcp/McpServerDetailDialog.tsx +11 -0
  118. package/src/renderer/components/extensions/mcp/McpServersPanel.tsx +51 -1
  119. package/src/renderer/components/extensions/skills/SkillsPanel.tsx +1 -126
  120. package/src/renderer/components/layout/PaneContent.tsx +2 -0
  121. package/src/renderer/components/layout/SortableTab.tsx +1 -0
  122. package/src/renderer/components/layout/TabBarActions.tsx +12 -12
  123. package/src/renderer/components/schedules/SchedulesView.tsx +54 -22
  124. package/src/renderer/components/settings/sections/AdvancedSection.tsx +1 -1
  125. package/src/renderer/components/settings/sections/HarnessSection.tsx +2 -6
  126. package/src/renderer/components/settings/sections/TaskBusSection.tsx +144 -84
  127. package/src/renderer/components/sidebar/SidebarSessions.tsx +23 -0
  128. package/src/renderer/components/sidebar/WorkspaceBrowser.tsx +1 -7
  129. package/src/renderer/components/tasks/TasksView.tsx +343 -0
  130. package/src/renderer/components/team/HarnessSelect.tsx +71 -0
  131. package/src/renderer/components/team/TeamDetailView.tsx +55 -98
  132. package/src/renderer/components/team/dialogs/CreateTeamDialog.tsx +21 -12
  133. package/src/renderer/components/team/dialogs/EditTeamDialog.tsx +8 -13
  134. package/src/renderer/components/team/dialogs/LaunchTeamDialog.tsx +1 -1
  135. package/src/renderer/components/team/editor/EditorContextMenu.tsx +8 -23
  136. package/src/renderer/components/team/editor/EditorFileTree.tsx +0 -4
  137. package/src/renderer/components/team/editor/EditorSelectionMenu.tsx +1 -8
  138. package/src/renderer/components/team/editor/ProjectEditorOverlay.tsx +0 -10
  139. package/src/renderer/components/team/kanban/KanbanBoard.tsx +31 -65
  140. package/src/renderer/components/team/members/MemberDetailDialog.tsx +8 -33
  141. package/src/renderer/components/team/messages/MessageComposer.tsx +39 -3
  142. package/src/renderer/components/team/messages/MessagesPanel.tsx +100 -26
  143. package/src/renderer/components/team/messages/StatusBlock.tsx +2 -24
  144. package/src/renderer/components/team/schedule/ScheduleEmptyState.tsx +1 -1
  145. package/src/renderer/components/terminal/TerminalPanel.tsx +156 -0
  146. package/src/renderer/components/ui/MentionableTextarea.tsx +0 -1
  147. package/src/renderer/hooks/useExtensionsTabState.ts +2 -2
  148. package/src/renderer/store/slices/extensionsSlice.ts +42 -107
  149. package/src/renderer/store/slices/scheduleSlice.ts +21 -0
  150. package/src/renderer/store/slices/teamSlice.ts +67 -25
  151. package/src/renderer/types/tabs.ts +1 -0
  152. package/src/shared/types/api.ts +58 -0
  153. package/src/shared/types/extensions/index.ts +1 -0
  154. package/src/shared/types/extensions/mcp.ts +2 -0
  155. package/src/shared/types/extensions/plugin.ts +2 -1
  156. package/src/shared/types/extensions/skill.ts +7 -0
  157. package/src/shared/types/team.ts +104 -1
  158. package/src/shared/utils/providerExtensionCapabilities.ts +1 -1
  159. package/dist-renderer/assets/ProjectEditorOverlay-A4DZTvSy.js +0 -57
  160. package/dist-renderer/assets/channel-Pre42N5O.js +0 -1
  161. package/dist-renderer/assets/classDiagram-2ON5EDUG-CdJsTJsj.js +0 -1
  162. package/dist-renderer/assets/classDiagram-v2-WZHVMYZB-CdJsTJsj.js +0 -1
  163. package/dist-renderer/assets/clone-BjQBiNfj.js +0 -1
  164. package/dist-renderer/assets/index-BIOJremZ.css +0 -1
  165. package/src/features/recent-projects/main/adapters/input/http/registerRecentProjectsHttp.ts +0 -30
  166. package/src/features/recent-projects/main/adapters/output/presenters/DashboardRecentProjectsPresenter.ts +0 -27
  167. package/src/features/recent-projects/main/adapters/output/sources/ClaudeRecentProjectsSourceAdapter.ts +0 -91
  168. package/src/features/recent-projects/main/adapters/output/sources/CodexRecentProjectsSourceAdapter.ts +0 -326
  169. package/src/features/recent-projects/main/composition/createRecentProjectsFeature.ts +0 -43
  170. package/src/features/recent-projects/main/index.ts +0 -3
  171. package/src/features/recent-projects/main/infrastructure/cache/InMemoryRecentProjectsCache.ts +0 -34
  172. package/src/features/recent-projects/main/infrastructure/codex/CodexAppServerClient.ts +0 -116
  173. package/src/features/recent-projects/main/infrastructure/identity/RecentProjectIdentityResolver.ts +0 -20
  174. package/src/features/recent-projects/main/infrastructure/identity/normalizeIdentityPath.ts +0 -10
  175. package/src/renderer/components/extensions/apikeys/ApiKeyCard.tsx +0 -143
  176. package/src/renderer/components/extensions/apikeys/ApiKeyFormDialog.tsx +0 -282
  177. package/src/renderer/components/extensions/apikeys/ApiKeysPanel.tsx +0 -280
@@ -0,0 +1,323 @@
1
+ import * as path from 'node:path';
2
+
3
+ import { createLogger } from '@shared/utils/logger';
4
+ import YAML from 'yaml';
5
+
6
+ import type { ResolvedSkillRoot } from './SkillRootsResolver';
7
+ import type {
8
+ SkillCatalogItem,
9
+ SkillDetail,
10
+ SkillDirectoryFlags,
11
+ SkillEnvVarDef,
12
+ SkillInvocationMode,
13
+ SkillValidationIssue,
14
+ } from '@shared/types/extensions';
15
+
16
+ const logger = createLogger('Extensions:SkillParser');
17
+
18
+ const ALLOWED_FRONTMATTER_KEYS = new Set([
19
+ 'name',
20
+ 'description',
21
+ // Third-party skills often include a semantic version in frontmatter.
22
+ 'version',
23
+ 'license',
24
+ 'compatibility',
25
+ 'metadata',
26
+ 'allowed-tools',
27
+ 'required-env',
28
+ 'disable-model-invocation',
29
+ ]);
30
+
31
+ const LARGE_SKILL_FILE_BYTES = 50_000;
32
+
33
+ interface ParsedFrontmatter {
34
+ rawFrontmatter: string | null;
35
+ body: string;
36
+ data: Record<string, unknown>;
37
+ issues: SkillValidationIssue[];
38
+ }
39
+
40
+ interface BuildSkillInput {
41
+ skillDir: string;
42
+ folderName: string;
43
+ skillFile: string;
44
+ rawContent: string;
45
+ modifiedAt: number;
46
+ flags: SkillDirectoryFlags;
47
+ root: ResolvedSkillRoot;
48
+ }
49
+
50
+ export interface SkillRelatedFiles {
51
+ referencesFiles: string[];
52
+ scriptFiles: string[];
53
+ assetFiles: string[];
54
+ }
55
+
56
+ export class SkillMetadataParser {
57
+ parseCatalogItem(input: BuildSkillInput): SkillCatalogItem {
58
+ const { folderName, flags, modifiedAt, rawContent, root, skillDir, skillFile } = input;
59
+ const parsed = this.parseFrontmatter(rawContent);
60
+ const metadata = this.normalizeMetadata(parsed.data.metadata);
61
+ const name = this.readString(parsed.data.name);
62
+ const description = this.readString(parsed.data.description);
63
+ const issues = [...parsed.issues];
64
+ const fileBaseName = path.basename(skillFile);
65
+
66
+ if (!name) {
67
+ issues.push({
68
+ code: 'missing-name',
69
+ message: 'Skill frontmatter is missing a valid `name` field.',
70
+ severity: 'error',
71
+ });
72
+ }
73
+
74
+ if (!description) {
75
+ issues.push({
76
+ code: 'missing-description',
77
+ message: 'Skill frontmatter is missing a valid `description` field.',
78
+ severity: 'error',
79
+ });
80
+ }
81
+
82
+ if (name && folderName !== name) {
83
+ issues.push({
84
+ code: 'folder-name-mismatch',
85
+ message: `Folder name "${folderName}" does not match skill name "${name}".`,
86
+ severity: 'error',
87
+ });
88
+ }
89
+
90
+ if (fileBaseName !== 'SKILL.md') {
91
+ issues.push({
92
+ code: 'nonstandard-file-name',
93
+ message: `Using "${fileBaseName}" instead of the standard "SKILL.md".`,
94
+ severity: 'warning',
95
+ });
96
+ }
97
+
98
+ const unknownKeys = Object.keys(parsed.data).filter(
99
+ (key) => !ALLOWED_FRONTMATTER_KEYS.has(key)
100
+ );
101
+ if (unknownKeys.length > 0) {
102
+ issues.push({
103
+ code: 'unknown-frontmatter-keys',
104
+ message: `Unknown frontmatter keys: ${unknownKeys.join(', ')}.`,
105
+ severity: 'warning',
106
+ });
107
+ }
108
+
109
+ if (Buffer.byteLength(rawContent, 'utf8') > LARGE_SKILL_FILE_BYTES) {
110
+ issues.push({
111
+ code: 'large-skill-file',
112
+ message: 'SKILL.md is large and may be expensive to load into context.',
113
+ severity: 'warning',
114
+ });
115
+ }
116
+
117
+ if (flags.hasScripts) {
118
+ issues.push({
119
+ code: 'has-scripts',
120
+ message:
121
+ 'This skill includes a scripts directory. Review bundled scripts before trusting it.',
122
+ severity: 'info',
123
+ });
124
+ }
125
+
126
+ const allowedTools = this.readAllowedTools(parsed.data['allowed-tools']);
127
+ const requiredEnv = this.readRequiredEnv(parsed.data['required-env']);
128
+ if (allowedTools) {
129
+ issues.push({
130
+ code: 'allowed-tools-advisory',
131
+ message:
132
+ '`allowed-tools` is present, but this app does not enforce or verify runtime compatibility.',
133
+ severity: 'warning',
134
+ });
135
+ }
136
+
137
+ const compatibility = this.readString(parsed.data.compatibility);
138
+ if (
139
+ compatibility &&
140
+ /(network|internet|online|env|environment|api key|credential)/iu.test(compatibility)
141
+ ) {
142
+ issues.push({
143
+ code: 'compatibility-advisory',
144
+ message:
145
+ '`compatibility` mentions environment or network requirements that this app cannot verify.',
146
+ severity: 'warning',
147
+ });
148
+ }
149
+
150
+ const isValid = !issues.some((issue) => issue.severity === 'error');
151
+
152
+ return {
153
+ id: skillDir,
154
+ sourceType: 'filesystem',
155
+ name: name ?? folderName,
156
+ description: description ?? 'Invalid skill metadata',
157
+ folderName,
158
+ scope: root.scope,
159
+ rootKind: root.rootKind,
160
+ projectRoot: root.projectRoot,
161
+ discoveryRoot: root.rootPath,
162
+ skillDir,
163
+ skillFile,
164
+ license: this.readString(parsed.data.license),
165
+ compatibility,
166
+ metadata,
167
+ allowedTools,
168
+ invocationMode: this.readInvocationMode(parsed.data['disable-model-invocation']),
169
+ flags,
170
+ isValid,
171
+ issues,
172
+ modifiedAt,
173
+ requiredEnv,
174
+ };
175
+ }
176
+
177
+ parseDetail(
178
+ item: SkillCatalogItem,
179
+ rawContent: string,
180
+ relatedFiles: SkillRelatedFiles
181
+ ): SkillDetail {
182
+ const parsed = this.parseFrontmatter(rawContent);
183
+
184
+ return {
185
+ item,
186
+ body: parsed.body,
187
+ rawContent,
188
+ rawFrontmatter: parsed.rawFrontmatter,
189
+ referencesFiles: relatedFiles.referencesFiles,
190
+ scriptFiles: relatedFiles.scriptFiles,
191
+ assetFiles: relatedFiles.assetFiles,
192
+ };
193
+ }
194
+
195
+ private parseFrontmatter(rawContent: string): ParsedFrontmatter {
196
+ const content = rawContent.replace(/^/, '');
197
+ if (!content.startsWith('---')) {
198
+ return {
199
+ rawFrontmatter: null,
200
+ body: content,
201
+ data: {},
202
+ issues: [
203
+ {
204
+ code: 'missing-frontmatter',
205
+ message: 'SKILL.md is missing YAML frontmatter.',
206
+ severity: 'error',
207
+ },
208
+ ],
209
+ };
210
+ }
211
+
212
+ const match = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/u.exec(content);
213
+ if (!match) {
214
+ return {
215
+ rawFrontmatter: null,
216
+ body: content,
217
+ data: {},
218
+ issues: [
219
+ {
220
+ code: 'invalid-frontmatter',
221
+ message: 'Unable to parse YAML frontmatter block.',
222
+ severity: 'error',
223
+ },
224
+ ],
225
+ };
226
+ }
227
+
228
+ const rawFrontmatter = match[1];
229
+ const body = match[2] ?? '';
230
+
231
+ try {
232
+ const parsed = YAML.parse(rawFrontmatter);
233
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
234
+ return {
235
+ rawFrontmatter,
236
+ body,
237
+ data: {},
238
+ issues: [
239
+ {
240
+ code: 'invalid-frontmatter',
241
+ message: 'YAML frontmatter must be a mapping/object.',
242
+ severity: 'error',
243
+ },
244
+ ],
245
+ };
246
+ }
247
+
248
+ return {
249
+ rawFrontmatter,
250
+ body,
251
+ data: parsed as Record<string, unknown>,
252
+ issues: [],
253
+ };
254
+ } catch (error) {
255
+ logger.warn('Failed to parse skill frontmatter', error);
256
+ return {
257
+ rawFrontmatter,
258
+ body,
259
+ data: {},
260
+ issues: [
261
+ {
262
+ code: 'invalid-frontmatter',
263
+ message: 'YAML frontmatter contains invalid syntax.',
264
+ severity: 'error',
265
+ },
266
+ ],
267
+ };
268
+ }
269
+ }
270
+
271
+ private normalizeMetadata(value: unknown): Record<string, string> {
272
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
273
+ return {};
274
+ }
275
+
276
+ return Object.fromEntries(
277
+ Object.entries(value).map(([key, entryValue]) => [key, String(entryValue)])
278
+ );
279
+ }
280
+
281
+ private readString(value: unknown): string | undefined {
282
+ if (typeof value !== 'string') return undefined;
283
+ const trimmed = value.trim();
284
+ return trimmed ? trimmed : undefined;
285
+ }
286
+
287
+ private readAllowedTools(value: unknown): string | undefined {
288
+ if (typeof value === 'string') {
289
+ return value.trim() || undefined;
290
+ }
291
+ if (Array.isArray(value)) {
292
+ const tools = value.map((entry) => String(entry).trim()).filter(Boolean);
293
+ return tools.length > 0 ? tools.join(' ') : undefined;
294
+ }
295
+ return undefined;
296
+ }
297
+
298
+ private readInvocationMode(value: unknown): SkillInvocationMode {
299
+ return value === true ? 'manual-only' : 'auto';
300
+ }
301
+
302
+ private readRequiredEnv(value: unknown): SkillEnvVarDef[] | undefined {
303
+ if (!Array.isArray(value)) return undefined;
304
+ const defs: SkillEnvVarDef[] = [];
305
+ for (const entry of value) {
306
+ if (typeof entry === 'string') {
307
+ const name = entry.trim();
308
+ if (name) defs.push({ name, isRequired: true });
309
+ } else if (entry && typeof entry === 'object') {
310
+ const obj = entry as Record<string, unknown>;
311
+ const name = typeof obj.name === 'string' ? obj.name.trim() : '';
312
+ if (name) {
313
+ defs.push({
314
+ name,
315
+ description: typeof obj.description === 'string' ? obj.description.trim() : undefined,
316
+ isRequired: obj['is-required'] !== false && obj.isRequired !== false,
317
+ });
318
+ }
319
+ }
320
+ }
321
+ return defs.length > 0 ? defs : undefined;
322
+ }
323
+ }