@terasky/backstage-plugin-ai-rules 1.5.0 → 1.7.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 (34) hide show
  1. package/config.d.ts +11 -9
  2. package/dist/alpha.esm.js +30 -0
  3. package/dist/alpha.esm.js.map +1 -1
  4. package/dist/api/AiRulesClient.esm.js +27 -19
  5. package/dist/api/AiRulesClient.esm.js.map +1 -1
  6. package/dist/api/types.esm.js.map +1 -1
  7. package/dist/components/AgentConfigsComponent/AgentConfigsComponent.esm.js +91 -0
  8. package/dist/components/AgentConfigsComponent/AgentConfigsComponent.esm.js.map +1 -0
  9. package/dist/components/AgentSkillsComponent/AgentSkillsComponent.esm.js +225 -0
  10. package/dist/components/AgentSkillsComponent/AgentSkillsComponent.esm.js.map +1 -0
  11. package/dist/components/AiInstructionsComponent/AiInstructionsComponent.esm.js +13 -3
  12. package/dist/components/AiInstructionsComponent/AiInstructionsComponent.esm.js.map +1 -1
  13. package/dist/components/AiRulesComponent/AiRulesComponent.esm.js +327 -248
  14. package/dist/components/AiRulesComponent/AiRulesComponent.esm.js.map +1 -1
  15. package/dist/components/IgnoreFilesComponent/IgnoreFilesComponent.esm.js +86 -0
  16. package/dist/components/IgnoreFilesComponent/IgnoreFilesComponent.esm.js.map +1 -0
  17. package/dist/components/MCPServersComponent/MCPServersComponent.esm.js +4 -0
  18. package/dist/components/MCPServersComponent/MCPServersComponent.esm.js.map +1 -1
  19. package/dist/hooks/useAgentConfigs.esm.js +38 -0
  20. package/dist/hooks/useAgentConfigs.esm.js.map +1 -0
  21. package/dist/hooks/useAiRules.esm.js +13 -1
  22. package/dist/hooks/useAiRules.esm.js.map +1 -1
  23. package/dist/hooks/useIgnoreFiles.esm.js +38 -0
  24. package/dist/hooks/useIgnoreFiles.esm.js.map +1 -0
  25. package/dist/hooks/useSkills.esm.js +38 -0
  26. package/dist/hooks/useSkills.esm.js.map +1 -0
  27. package/dist/index.d.ts +143 -7
  28. package/dist/index.esm.js +3 -0
  29. package/dist/index.esm.js.map +1 -1
  30. package/dist/plugin.esm.js +24 -0
  31. package/dist/plugin.esm.js.map +1 -1
  32. package/dist/types.esm.js +7 -0
  33. package/dist/types.esm.js.map +1 -1
  34. package/package.json +11 -11
@@ -0,0 +1,38 @@
1
+ import { useApi } from '@backstage/core-plugin-api';
2
+ import { useState, useCallback, useEffect } from 'react';
3
+ import { aiRulesApiRef } from '../api/types.esm.js';
4
+ import { useEntity } from '@backstage/plugin-catalog-react';
5
+
6
+ const useIgnoreFiles = () => {
7
+ const api = useApi(aiRulesApiRef);
8
+ const { entity } = useEntity();
9
+ const [loading, setLoading] = useState(false);
10
+ const [error, setError] = useState();
11
+ const [files, setFiles] = useState([]);
12
+ const [hasGitUrl, setHasGitUrl] = useState(true);
13
+ const sourceLocation = entity.metadata?.annotations?.["backstage.io/source-location"];
14
+ const fetchFiles = useCallback(async () => {
15
+ if (!sourceLocation) {
16
+ setHasGitUrl(false);
17
+ return;
18
+ }
19
+ try {
20
+ setLoading(true);
21
+ setError(void 0);
22
+ const gitUrl = sourceLocation.replace("url:", "").replace(/\/tree\/(?:main|master)\/.*$/, "");
23
+ const response = await api.getIgnoreFiles(gitUrl);
24
+ setFiles(response.files);
25
+ } catch (err) {
26
+ setError(err.message);
27
+ } finally {
28
+ setLoading(false);
29
+ }
30
+ }, [api, sourceLocation]);
31
+ useEffect(() => {
32
+ fetchFiles();
33
+ }, [fetchFiles]);
34
+ return { loading, error, files, hasGitUrl, refetch: fetchFiles };
35
+ };
36
+
37
+ export { useIgnoreFiles };
38
+ //# sourceMappingURL=useIgnoreFiles.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useIgnoreFiles.esm.js","sources":["../../src/hooks/useIgnoreFiles.ts"],"sourcesContent":["import { useApi } from '@backstage/core-plugin-api';\nimport { useCallback, useEffect, useState } from 'react';\nimport { aiRulesApiRef } from '../api/types';\nimport type { AiRulesApi } from '../api/types';\nimport { IgnoreFile } from '../types';\nimport { useEntity } from '@backstage/plugin-catalog-react';\n\nexport const useIgnoreFiles = () => {\n const api = useApi(aiRulesApiRef) as AiRulesApi;\n const { entity } = useEntity();\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | undefined>();\n const [files, setFiles] = useState<IgnoreFile[]>([]);\n const [hasGitUrl, setHasGitUrl] = useState(true);\n\n const sourceLocation = entity.metadata?.annotations?.['backstage.io/source-location'];\n\n const fetchFiles = useCallback(async () => {\n if (!sourceLocation) {\n setHasGitUrl(false);\n return;\n }\n try {\n setLoading(true);\n setError(undefined);\n const gitUrl = sourceLocation\n .replace('url:', '')\n .replace(/\\/tree\\/(?:main|master)\\/.*$/, '');\n const response = await api.getIgnoreFiles(gitUrl);\n setFiles(response.files);\n } catch (err) {\n setError((err as Error).message);\n } finally {\n setLoading(false);\n }\n }, [api, sourceLocation]);\n\n useEffect(() => { fetchFiles(); }, [fetchFiles]);\n\n return { loading, error, files, hasGitUrl, refetch: fetchFiles };\n};\n"],"names":[],"mappings":";;;;;AAOO,MAAM,iBAAiB,MAAM;AAClC,EAAA,MAAM,GAAA,GAAM,OAAO,aAAa,CAAA;AAChC,EAAA,MAAM,EAAE,MAAA,EAAO,GAAI,SAAA,EAAU;AAC7B,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,SAAS,KAAK,CAAA;AAC5C,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAI,QAAA,EAA6B;AACvD,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAI,QAAA,CAAuB,EAAE,CAAA;AACnD,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAI,SAAS,IAAI,CAAA;AAE/C,EAAA,MAAM,cAAA,GAAiB,MAAA,CAAO,QAAA,EAAU,WAAA,GAAc,8BAA8B,CAAA;AAEpF,EAAA,MAAM,UAAA,GAAa,YAAY,YAAY;AACzC,IAAA,IAAI,CAAC,cAAA,EAAgB;AACnB,MAAA,YAAA,CAAa,KAAK,CAAA;AAClB,MAAA;AAAA,IACF;AACA,IAAA,IAAI;AACF,MAAA,UAAA,CAAW,IAAI,CAAA;AACf,MAAA,QAAA,CAAS,KAAA,CAAS,CAAA;AAClB,MAAA,MAAM,MAAA,GAAS,eACZ,OAAA,CAAQ,MAAA,EAAQ,EAAE,CAAA,CAClB,OAAA,CAAQ,gCAAgC,EAAE,CAAA;AAC7C,MAAA,MAAM,QAAA,GAAW,MAAM,GAAA,CAAI,cAAA,CAAe,MAAM,CAAA;AAChD,MAAA,QAAA,CAAS,SAAS,KAAK,CAAA;AAAA,IACzB,SAAS,GAAA,EAAK;AACZ,MAAA,QAAA,CAAU,IAAc,OAAO,CAAA;AAAA,IACjC,CAAA,SAAE;AACA,MAAA,UAAA,CAAW,KAAK,CAAA;AAAA,IAClB;AAAA,EACF,CAAA,EAAG,CAAC,GAAA,EAAK,cAAc,CAAC,CAAA;AAExB,EAAA,SAAA,CAAU,MAAM;AAAE,IAAA,UAAA,EAAW;AAAA,EAAG,CAAA,EAAG,CAAC,UAAU,CAAC,CAAA;AAE/C,EAAA,OAAO,EAAE,OAAA,EAAS,KAAA,EAAO,KAAA,EAAO,SAAA,EAAW,SAAS,UAAA,EAAW;AACjE;;;;"}
@@ -0,0 +1,38 @@
1
+ import { useApi } from '@backstage/core-plugin-api';
2
+ import { useState, useCallback, useEffect } from 'react';
3
+ import { aiRulesApiRef } from '../api/types.esm.js';
4
+ import { useEntity } from '@backstage/plugin-catalog-react';
5
+
6
+ const useSkills = () => {
7
+ const api = useApi(aiRulesApiRef);
8
+ const { entity } = useEntity();
9
+ const [loading, setLoading] = useState(false);
10
+ const [error, setError] = useState();
11
+ const [skills, setSkills] = useState([]);
12
+ const [hasGitUrl, setHasGitUrl] = useState(true);
13
+ const sourceLocation = entity.metadata?.annotations?.["backstage.io/source-location"];
14
+ const fetchSkills = useCallback(async () => {
15
+ if (!sourceLocation) {
16
+ setHasGitUrl(false);
17
+ return;
18
+ }
19
+ try {
20
+ setLoading(true);
21
+ setError(void 0);
22
+ const gitUrl = sourceLocation.replace("url:", "").replace(/\/tree\/(?:main|master)\/.*$/, "");
23
+ const response = await api.getSkills(gitUrl);
24
+ setSkills(response.skills);
25
+ } catch (err) {
26
+ setError(err.message);
27
+ } finally {
28
+ setLoading(false);
29
+ }
30
+ }, [api, sourceLocation]);
31
+ useEffect(() => {
32
+ fetchSkills();
33
+ }, [fetchSkills]);
34
+ return { loading, error, skills, hasGitUrl, refetch: fetchSkills };
35
+ };
36
+
37
+ export { useSkills };
38
+ //# sourceMappingURL=useSkills.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSkills.esm.js","sources":["../../src/hooks/useSkills.ts"],"sourcesContent":["import { useApi } from '@backstage/core-plugin-api';\nimport { useCallback, useEffect, useState } from 'react';\nimport { aiRulesApiRef } from '../api/types';\nimport type { AiRulesApi } from '../api/types';\nimport { AgentSkill } from '../types';\nimport { useEntity } from '@backstage/plugin-catalog-react';\n\nexport const useSkills = () => {\n const api = useApi(aiRulesApiRef) as AiRulesApi;\n const { entity } = useEntity();\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | undefined>();\n const [skills, setSkills] = useState<AgentSkill[]>([]);\n const [hasGitUrl, setHasGitUrl] = useState(true);\n\n const sourceLocation = entity.metadata?.annotations?.['backstage.io/source-location'];\n\n const fetchSkills = useCallback(async () => {\n if (!sourceLocation) {\n setHasGitUrl(false);\n return;\n }\n try {\n setLoading(true);\n setError(undefined);\n const gitUrl = sourceLocation\n .replace('url:', '')\n .replace(/\\/tree\\/(?:main|master)\\/.*$/, '');\n const response = await api.getSkills(gitUrl);\n setSkills(response.skills);\n } catch (err) {\n setError((err as Error).message);\n } finally {\n setLoading(false);\n }\n }, [api, sourceLocation]);\n\n useEffect(() => { fetchSkills(); }, [fetchSkills]);\n\n return { loading, error, skills, hasGitUrl, refetch: fetchSkills };\n};\n"],"names":[],"mappings":";;;;;AAOO,MAAM,YAAY,MAAM;AAC7B,EAAA,MAAM,GAAA,GAAM,OAAO,aAAa,CAAA;AAChC,EAAA,MAAM,EAAE,MAAA,EAAO,GAAI,SAAA,EAAU;AAC7B,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,SAAS,KAAK,CAAA;AAC5C,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAI,QAAA,EAA6B;AACvD,EAAA,MAAM,CAAC,MAAA,EAAQ,SAAS,CAAA,GAAI,QAAA,CAAuB,EAAE,CAAA;AACrD,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAI,SAAS,IAAI,CAAA;AAE/C,EAAA,MAAM,cAAA,GAAiB,MAAA,CAAO,QAAA,EAAU,WAAA,GAAc,8BAA8B,CAAA;AAEpF,EAAA,MAAM,WAAA,GAAc,YAAY,YAAY;AAC1C,IAAA,IAAI,CAAC,cAAA,EAAgB;AACnB,MAAA,YAAA,CAAa,KAAK,CAAA;AAClB,MAAA;AAAA,IACF;AACA,IAAA,IAAI;AACF,MAAA,UAAA,CAAW,IAAI,CAAA;AACf,MAAA,QAAA,CAAS,KAAA,CAAS,CAAA;AAClB,MAAA,MAAM,MAAA,GAAS,eACZ,OAAA,CAAQ,MAAA,EAAQ,EAAE,CAAA,CAClB,OAAA,CAAQ,gCAAgC,EAAE,CAAA;AAC7C,MAAA,MAAM,QAAA,GAAW,MAAM,GAAA,CAAI,SAAA,CAAU,MAAM,CAAA;AAC3C,MAAA,SAAA,CAAU,SAAS,MAAM,CAAA;AAAA,IAC3B,SAAS,GAAA,EAAK;AACZ,MAAA,QAAA,CAAU,IAAc,OAAO,CAAA;AAAA,IACjC,CAAA,SAAE;AACA,MAAA,UAAA,CAAW,KAAK,CAAA;AAAA,IAClB;AAAA,EACF,CAAA,EAAG,CAAC,GAAA,EAAK,cAAc,CAAC,CAAA;AAExB,EAAA,SAAA,CAAU,MAAM;AAAE,IAAA,WAAA,EAAY;AAAA,EAAG,CAAA,EAAG,CAAC,WAAW,CAAC,CAAA;AAEjD,EAAA,OAAO,EAAE,OAAA,EAAS,KAAA,EAAO,MAAA,EAAQ,SAAA,EAAW,SAAS,WAAA,EAAY;AACnE;;;;"}
package/dist/index.d.ts CHANGED
@@ -2,7 +2,6 @@ import * as _backstage_core_plugin_api from '@backstage/core-plugin-api';
2
2
  import React from 'react';
3
3
  import { Entity } from '@backstage/catalog-model';
4
4
  import * as react_jsx_runtime from 'react/jsx-runtime';
5
- import * as _backstage_frontend_plugin_api from '@backstage/frontend-plugin-api';
6
5
 
7
6
  declare const aiRulesPlugin: _backstage_core_plugin_api.BackstagePlugin<{
8
7
  root: _backstage_core_plugin_api.RouteRef<undefined>;
@@ -13,7 +12,14 @@ declare enum AIRuleType {
13
12
  CURSOR = "cursor",
14
13
  COPILOT = "copilot",
15
14
  CLINE = "cline",
16
- CLAUDE_CODE = "claude-code"
15
+ CLAUDE_CODE = "claude-code",
16
+ WINDSURF = "windsurf",
17
+ ROO_CODE = "roo-code",
18
+ CODEX = "codex",
19
+ GEMINI = "gemini",
20
+ AMAZON_Q = "amazon-q",
21
+ CONTINUE = "continue",
22
+ AIDER = "aider"
17
23
  }
18
24
  interface CursorRule {
19
25
  type: AIRuleType.CURSOR;
@@ -61,7 +67,73 @@ interface ClaudeCodeRule {
61
67
  content: string;
62
68
  title?: string;
63
69
  }
64
- type AIRule = CursorRule | CopilotRule | ClineRule | ClaudeCodeRule;
70
+ interface WindsurfRule {
71
+ type: AIRuleType.WINDSURF;
72
+ id: string;
73
+ filePath: string;
74
+ fileName: string;
75
+ gitUrl?: string;
76
+ content: string;
77
+ title?: string;
78
+ }
79
+ interface RooCodeRule {
80
+ type: AIRuleType.ROO_CODE;
81
+ id: string;
82
+ filePath: string;
83
+ fileName: string;
84
+ gitUrl?: string;
85
+ content: string;
86
+ title?: string;
87
+ mode?: string;
88
+ }
89
+ interface CodexRule {
90
+ type: AIRuleType.CODEX;
91
+ id: string;
92
+ filePath: string;
93
+ fileName: string;
94
+ gitUrl?: string;
95
+ content: string;
96
+ title?: string;
97
+ }
98
+ interface GeminiRule {
99
+ type: AIRuleType.GEMINI;
100
+ id: string;
101
+ filePath: string;
102
+ fileName: string;
103
+ gitUrl?: string;
104
+ content: string;
105
+ title?: string;
106
+ }
107
+ interface AmazonQRule {
108
+ type: AIRuleType.AMAZON_Q;
109
+ id: string;
110
+ filePath: string;
111
+ fileName: string;
112
+ gitUrl?: string;
113
+ content: string;
114
+ title?: string;
115
+ }
116
+ interface ContinueRule {
117
+ type: AIRuleType.CONTINUE;
118
+ id: string;
119
+ filePath: string;
120
+ fileName: string;
121
+ gitUrl?: string;
122
+ content: string;
123
+ title?: string;
124
+ alwaysApply?: boolean;
125
+ frontmatter?: Record<string, any>;
126
+ }
127
+ interface AiderRule {
128
+ type: AIRuleType.AIDER;
129
+ id: string;
130
+ filePath: string;
131
+ fileName: string;
132
+ gitUrl?: string;
133
+ content: string;
134
+ title?: string;
135
+ }
136
+ type AIRule = CursorRule | CopilotRule | ClineRule | ClaudeCodeRule | WindsurfRule | RooCodeRule | CodexRule | GeminiRule | AmazonQRule | ContinueRule | AiderRule;
65
137
  interface AIRulesResponse {
66
138
  rules: AIRule[];
67
139
  totalCount: number;
@@ -70,6 +142,49 @@ interface AIRulesResponse {
70
142
  interface AIRulesConfig {
71
143
  allowedRuleTypes?: AIRuleType[];
72
144
  }
145
+ interface IgnoreFile {
146
+ agent: string;
147
+ filePath: string;
148
+ content: string;
149
+ gitUrl?: string;
150
+ }
151
+ interface IgnoreFilesResponse {
152
+ files: IgnoreFile[];
153
+ totalCount: number;
154
+ }
155
+ interface AgentConfig {
156
+ agent: string;
157
+ filePath: string;
158
+ content: string;
159
+ language: 'yaml' | 'json' | 'typescript';
160
+ gitUrl?: string;
161
+ }
162
+ interface AgentConfigsResponse {
163
+ configs: AgentConfig[];
164
+ totalCount: number;
165
+ }
166
+ interface AgentSkill {
167
+ name: string;
168
+ description: string;
169
+ source: 'cross-client' | 'claude' | 'cursor';
170
+ sourceDirectory: string;
171
+ filePath: string;
172
+ content: string;
173
+ license?: string;
174
+ compatibility?: string;
175
+ metadata?: Record<string, string>;
176
+ allowedTools?: string[];
177
+ resources: {
178
+ scripts: string[];
179
+ references: string[];
180
+ assets: string[];
181
+ };
182
+ gitUrl?: string;
183
+ }
184
+ interface SkillsResponse {
185
+ skills: AgentSkill[];
186
+ totalCount: number;
187
+ }
73
188
 
74
189
  interface MCPServerConfig {
75
190
  type?: 'stdio' | 'remote';
@@ -83,7 +198,7 @@ interface MCPServerInfo {
83
198
  name: string;
84
199
  type: 'local' | 'remote';
85
200
  config: MCPServerConfig;
86
- source: 'cursor' | 'vscode' | 'claude';
201
+ source: 'cursor' | 'vscode' | 'claude' | 'windsurf' | 'cline';
87
202
  rawConfig: string;
88
203
  }
89
204
  interface MCPServersResponse {
@@ -100,15 +215,33 @@ interface AiInstructionsComponentProps {
100
215
  }
101
216
  declare const AiInstructionsComponent: ({ title }: AiInstructionsComponentProps) => react_jsx_runtime.JSX.Element;
102
217
 
218
+ interface IgnoreFilesComponentProps {
219
+ title?: string;
220
+ }
221
+ declare const IgnoreFilesComponent: ({ title }: IgnoreFilesComponentProps) => react_jsx_runtime.JSX.Element;
222
+
223
+ interface AgentConfigsComponentProps {
224
+ title?: string;
225
+ }
226
+ declare const AgentConfigsComponent: ({ title }: AgentConfigsComponentProps) => react_jsx_runtime.JSX.Element;
227
+
228
+ interface AgentSkillsComponentProps {
229
+ title?: string;
230
+ }
231
+ declare const AgentSkillsComponent: ({ title }: AgentSkillsComponentProps) => react_jsx_runtime.JSX.Element;
232
+
103
233
  interface AIRulesComponentProps {
104
234
  title?: string;
105
235
  }
106
236
  declare const isAIRulesAvailable: (entity: Entity) => boolean;
107
237
 
108
- declare const aiRulesApiRef: _backstage_frontend_plugin_api.ApiRef<AiRulesApi>;
238
+ declare const aiRulesApiRef: _backstage_core_plugin_api.ApiRef<AiRulesApi>;
109
239
  interface AiRulesApi {
110
240
  getAiRules(ruleTypes: string[]): Promise<AIRulesResponse>;
111
241
  getMCPServers(gitUrl: string): Promise<MCPServersResponse>;
242
+ getIgnoreFiles(gitUrl: string): Promise<IgnoreFilesResponse>;
243
+ getAgentConfigs(gitUrl: string): Promise<AgentConfigsResponse>;
244
+ getSkills(gitUrl: string): Promise<SkillsResponse>;
112
245
  }
113
246
 
114
247
  declare class AiRulesClient implements AiRulesApi {
@@ -128,7 +261,10 @@ declare class AiRulesClient implements AiRulesApi {
128
261
  private cleanGitUrl;
129
262
  getAiRules(ruleTypes: string[]): Promise<AIRulesResponse>;
130
263
  getMCPServers(gitUrl: string): Promise<MCPServersResponse>;
264
+ getIgnoreFiles(gitUrl: string): Promise<IgnoreFilesResponse>;
265
+ getAgentConfigs(gitUrl: string): Promise<AgentConfigsResponse>;
266
+ getSkills(gitUrl: string): Promise<SkillsResponse>;
131
267
  }
132
268
 
133
- export { AIRuleType, AIRulesComponent, AiInstructionsComponent, AiRulesClient, MCPServersComponent, aiRulesApiRef, aiRulesPlugin, isAIRulesAvailable };
134
- export type { AIRule, AIRulesComponentProps, AIRulesConfig, AIRulesResponse, AiInstructionsComponentProps, AiRulesApi, ClaudeCodeRule, ClineRule, CopilotRule, CursorRule, MCPServerConfig, MCPServerInfo, MCPServersComponentProps, MCPServersResponse };
269
+ export { AIRuleType, AIRulesComponent, AgentConfigsComponent, AgentSkillsComponent, AiInstructionsComponent, AiRulesClient, IgnoreFilesComponent, MCPServersComponent, aiRulesApiRef, aiRulesPlugin, isAIRulesAvailable };
270
+ export type { AIRule, AIRulesComponentProps, AIRulesConfig, AIRulesResponse, AgentConfig, AgentConfigsComponentProps, AgentConfigsResponse, AgentSkill, AgentSkillsComponentProps, AiInstructionsComponentProps, AiRulesApi, AiderRule, AmazonQRule, ClaudeCodeRule, ClineRule, CodexRule, ContinueRule, CopilotRule, CursorRule, GeminiRule, IgnoreFile, IgnoreFilesComponentProps, IgnoreFilesResponse, MCPServerConfig, MCPServerInfo, MCPServersComponentProps, MCPServersResponse, RooCodeRule, SkillsResponse, WindsurfRule };
package/dist/index.esm.js CHANGED
@@ -2,6 +2,9 @@ export { AIRulesComponent, aiRulesPlugin } from './plugin.esm.js';
2
2
  export { AIRuleType } from './types.esm.js';
3
3
  export { MCPServersComponent } from './components/MCPServersComponent/MCPServersComponent.esm.js';
4
4
  export { AiInstructionsComponent } from './components/AiInstructionsComponent/AiInstructionsComponent.esm.js';
5
+ export { IgnoreFilesComponent } from './components/IgnoreFilesComponent/IgnoreFilesComponent.esm.js';
6
+ export { AgentConfigsComponent } from './components/AgentConfigsComponent/AgentConfigsComponent.esm.js';
7
+ export { AgentSkillsComponent } from './components/AgentSkillsComponent/AgentSkillsComponent.esm.js';
5
8
  export { isAIRulesAvailable } from './components/AiRulesComponent/AiRulesComponent.esm.js';
6
9
  export { aiRulesApiRef } from './api/types.esm.js';
7
10
  export { AiRulesClient } from './api/AiRulesClient.esm.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;"}
1
+ {"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;"}
@@ -32,6 +32,30 @@ aiRulesPlugin.provide(
32
32
  }
33
33
  })
34
34
  );
35
+ aiRulesPlugin.provide(
36
+ createComponentExtension({
37
+ name: "IgnoreFilesComponent",
38
+ component: {
39
+ lazy: () => import('./components/IgnoreFilesComponent/IgnoreFilesComponent.esm.js').then((m) => m.IgnoreFilesComponent).then((Component) => (props) => React.createElement(Component, props))
40
+ }
41
+ })
42
+ );
43
+ aiRulesPlugin.provide(
44
+ createComponentExtension({
45
+ name: "AgentConfigsComponent",
46
+ component: {
47
+ lazy: () => import('./components/AgentConfigsComponent/AgentConfigsComponent.esm.js').then((m) => m.AgentConfigsComponent).then((Component) => (props) => React.createElement(Component, props))
48
+ }
49
+ })
50
+ );
51
+ aiRulesPlugin.provide(
52
+ createComponentExtension({
53
+ name: "AgentSkillsComponent",
54
+ component: {
55
+ lazy: () => import('./components/AgentSkillsComponent/AgentSkillsComponent.esm.js').then((m) => m.AgentSkillsComponent).then((Component) => (props) => React.createElement(Component, props))
56
+ }
57
+ })
58
+ );
35
59
 
36
60
  export { AIRulesComponent, aiRulesPlugin };
37
61
  //# sourceMappingURL=plugin.esm.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.esm.js","sources":["../src/plugin.ts"],"sourcesContent":["import { createPlugin, createComponentExtension } from '@backstage/core-plugin-api';\nimport { rootRouteRef } from './routes';\nimport React from 'react';\n\nexport const aiRulesPlugin = createPlugin({\n id: 'ai-rules',\n routes: {\n root: rootRouteRef,\n },\n});\n\nexport const AIRulesComponent = aiRulesPlugin.provide(\n createComponentExtension({\n name: 'AIRulesComponent',\n component: {\n lazy: () => import('./components/AiRulesComponent/AiRulesComponent').then(m => m.AIRulesComponent)\n .then(Component => (props: any) => React.createElement(Component, props)),\n },\n }),\n);\n\nexport const MCPServersComponent = aiRulesPlugin.provide(\n createComponentExtension({\n name: 'MCPServersComponent',\n component: {\n lazy: () => import('./components/MCPServersComponent/MCPServersComponent').then(m => m.MCPServersComponent)\n .then(Component => (props: any) => React.createElement(Component, props)),\n },\n }),\n);\n\nexport const AiInstructionsComponent = aiRulesPlugin.provide(\n createComponentExtension({\n name: 'AiInstructionsComponent',\n component: {\n lazy: () => import('./components/AiInstructionsComponent/AiInstructionsComponent').then(m => m.AiInstructionsComponent)\n .then(Component => (props: any) => React.createElement(Component, props)),\n },\n }),\n);"],"names":[],"mappings":";;;;AAIO,MAAM,gBAAgB,YAAA,CAAa;AAAA,EACxC,EAAA,EAAI,UAAA;AAAA,EACJ,MAAA,EAAQ;AAAA,IACN,IAAA,EAAM;AAAA;AAEV,CAAC;AAEM,MAAM,mBAAmB,aAAA,CAAc,OAAA;AAAA,EAC5C,wBAAA,CAAyB;AAAA,IACvB,IAAA,EAAM,kBAAA;AAAA,IACN,SAAA,EAAW;AAAA,MACT,MAAM,MAAM,OAAO,uDAAgD,CAAA,CAAE,IAAA,CAAK,OAAK,CAAA,CAAE,gBAAgB,CAAA,CAC9F,IAAA,CAAK,eAAa,CAAC,KAAA,KAAe,MAAM,aAAA,CAAc,SAAA,EAAW,KAAK,CAAC;AAAA;AAC5E,GACD;AACH;AAEmC,aAAA,CAAc,OAAA;AAAA,EAC/C,wBAAA,CAAyB;AAAA,IACvB,IAAA,EAAM,qBAAA;AAAA,IACN,SAAA,EAAW;AAAA,MACT,MAAM,MAAM,OAAO,6DAAsD,CAAA,CAAE,IAAA,CAAK,OAAK,CAAA,CAAE,mBAAmB,CAAA,CACvG,IAAA,CAAK,eAAa,CAAC,KAAA,KAAe,MAAM,aAAA,CAAc,SAAA,EAAW,KAAK,CAAC;AAAA;AAC5E,GACD;AACH;AAEuC,aAAA,CAAc,OAAA;AAAA,EACnD,wBAAA,CAAyB;AAAA,IACvB,IAAA,EAAM,yBAAA;AAAA,IACN,SAAA,EAAW;AAAA,MACT,MAAM,MAAM,OAAO,qEAA8D,CAAA,CAAE,IAAA,CAAK,OAAK,CAAA,CAAE,uBAAuB,CAAA,CACnH,IAAA,CAAK,eAAa,CAAC,KAAA,KAAe,MAAM,aAAA,CAAc,SAAA,EAAW,KAAK,CAAC;AAAA;AAC5E,GACD;AACH;;;;"}
1
+ {"version":3,"file":"plugin.esm.js","sources":["../src/plugin.ts"],"sourcesContent":["import { createPlugin, createComponentExtension } from '@backstage/core-plugin-api';\nimport { rootRouteRef } from './routes';\nimport React from 'react';\n\nexport const aiRulesPlugin = createPlugin({\n id: 'ai-rules',\n routes: {\n root: rootRouteRef,\n },\n});\n\nexport const AIRulesComponent = aiRulesPlugin.provide(\n createComponentExtension({\n name: 'AIRulesComponent',\n component: {\n lazy: () => import('./components/AiRulesComponent/AiRulesComponent').then(m => m.AIRulesComponent)\n .then(Component => (props: any) => React.createElement(Component, props)),\n },\n }),\n);\n\nexport const MCPServersComponent = aiRulesPlugin.provide(\n createComponentExtension({\n name: 'MCPServersComponent',\n component: {\n lazy: () => import('./components/MCPServersComponent/MCPServersComponent').then(m => m.MCPServersComponent)\n .then(Component => (props: any) => React.createElement(Component, props)),\n },\n }),\n);\n\nexport const AiInstructionsComponent = aiRulesPlugin.provide(\n createComponentExtension({\n name: 'AiInstructionsComponent',\n component: {\n lazy: () => import('./components/AiInstructionsComponent/AiInstructionsComponent').then(m => m.AiInstructionsComponent)\n .then(Component => (props: any) => React.createElement(Component, props)),\n },\n }),\n);\n\nexport const IgnoreFilesComponent = aiRulesPlugin.provide(\n createComponentExtension({\n name: 'IgnoreFilesComponent',\n component: {\n lazy: () => import('./components/IgnoreFilesComponent/IgnoreFilesComponent').then(m => m.IgnoreFilesComponent)\n .then(Component => (props: any) => React.createElement(Component, props)),\n },\n }),\n);\n\nexport const AgentConfigsComponent = aiRulesPlugin.provide(\n createComponentExtension({\n name: 'AgentConfigsComponent',\n component: {\n lazy: () => import('./components/AgentConfigsComponent/AgentConfigsComponent').then(m => m.AgentConfigsComponent)\n .then(Component => (props: any) => React.createElement(Component, props)),\n },\n }),\n);\n\nexport const AgentSkillsComponent = aiRulesPlugin.provide(\n createComponentExtension({\n name: 'AgentSkillsComponent',\n component: {\n lazy: () => import('./components/AgentSkillsComponent/AgentSkillsComponent').then(m => m.AgentSkillsComponent)\n .then(Component => (props: any) => React.createElement(Component, props)),\n },\n }),\n);\n"],"names":[],"mappings":";;;;AAIO,MAAM,gBAAgB,YAAA,CAAa;AAAA,EACxC,EAAA,EAAI,UAAA;AAAA,EACJ,MAAA,EAAQ;AAAA,IACN,IAAA,EAAM;AAAA;AAEV,CAAC;AAEM,MAAM,mBAAmB,aAAA,CAAc,OAAA;AAAA,EAC5C,wBAAA,CAAyB;AAAA,IACvB,IAAA,EAAM,kBAAA;AAAA,IACN,SAAA,EAAW;AAAA,MACT,MAAM,MAAM,OAAO,uDAAgD,CAAA,CAAE,IAAA,CAAK,OAAK,CAAA,CAAE,gBAAgB,CAAA,CAC9F,IAAA,CAAK,eAAa,CAAC,KAAA,KAAe,MAAM,aAAA,CAAc,SAAA,EAAW,KAAK,CAAC;AAAA;AAC5E,GACD;AACH;AAEmC,aAAA,CAAc,OAAA;AAAA,EAC/C,wBAAA,CAAyB;AAAA,IACvB,IAAA,EAAM,qBAAA;AAAA,IACN,SAAA,EAAW;AAAA,MACT,MAAM,MAAM,OAAO,6DAAsD,CAAA,CAAE,IAAA,CAAK,OAAK,CAAA,CAAE,mBAAmB,CAAA,CACvG,IAAA,CAAK,eAAa,CAAC,KAAA,KAAe,MAAM,aAAA,CAAc,SAAA,EAAW,KAAK,CAAC;AAAA;AAC5E,GACD;AACH;AAEuC,aAAA,CAAc,OAAA;AAAA,EACnD,wBAAA,CAAyB;AAAA,IACvB,IAAA,EAAM,yBAAA;AAAA,IACN,SAAA,EAAW;AAAA,MACT,MAAM,MAAM,OAAO,qEAA8D,CAAA,CAAE,IAAA,CAAK,OAAK,CAAA,CAAE,uBAAuB,CAAA,CACnH,IAAA,CAAK,eAAa,CAAC,KAAA,KAAe,MAAM,aAAA,CAAc,SAAA,EAAW,KAAK,CAAC;AAAA;AAC5E,GACD;AACH;AAEoC,aAAA,CAAc,OAAA;AAAA,EAChD,wBAAA,CAAyB;AAAA,IACvB,IAAA,EAAM,sBAAA;AAAA,IACN,SAAA,EAAW;AAAA,MACT,MAAM,MAAM,OAAO,+DAAwD,CAAA,CAAE,IAAA,CAAK,OAAK,CAAA,CAAE,oBAAoB,CAAA,CAC1G,IAAA,CAAK,eAAa,CAAC,KAAA,KAAe,MAAM,aAAA,CAAc,SAAA,EAAW,KAAK,CAAC;AAAA;AAC5E,GACD;AACH;AAEqC,aAAA,CAAc,OAAA;AAAA,EACjD,wBAAA,CAAyB;AAAA,IACvB,IAAA,EAAM,uBAAA;AAAA,IACN,SAAA,EAAW;AAAA,MACT,MAAM,MAAM,OAAO,iEAA0D,CAAA,CAAE,IAAA,CAAK,OAAK,CAAA,CAAE,qBAAqB,CAAA,CAC7G,IAAA,CAAK,eAAa,CAAC,KAAA,KAAe,MAAM,aAAA,CAAc,SAAA,EAAW,KAAK,CAAC;AAAA;AAC5E,GACD;AACH;AAEoC,aAAA,CAAc,OAAA;AAAA,EAChD,wBAAA,CAAyB;AAAA,IACvB,IAAA,EAAM,sBAAA;AAAA,IACN,SAAA,EAAW;AAAA,MACT,MAAM,MAAM,OAAO,+DAAwD,CAAA,CAAE,IAAA,CAAK,OAAK,CAAA,CAAE,oBAAoB,CAAA,CAC1G,IAAA,CAAK,eAAa,CAAC,KAAA,KAAe,MAAM,aAAA,CAAc,SAAA,EAAW,KAAK,CAAC;AAAA;AAC5E,GACD;AACH;;;;"}
package/dist/types.esm.js CHANGED
@@ -3,6 +3,13 @@ var AIRuleType = /* @__PURE__ */ ((AIRuleType2) => {
3
3
  AIRuleType2["COPILOT"] = "copilot";
4
4
  AIRuleType2["CLINE"] = "cline";
5
5
  AIRuleType2["CLAUDE_CODE"] = "claude-code";
6
+ AIRuleType2["WINDSURF"] = "windsurf";
7
+ AIRuleType2["ROO_CODE"] = "roo-code";
8
+ AIRuleType2["CODEX"] = "codex";
9
+ AIRuleType2["GEMINI"] = "gemini";
10
+ AIRuleType2["AMAZON_Q"] = "amazon-q";
11
+ AIRuleType2["CONTINUE"] = "continue";
12
+ AIRuleType2["AIDER"] = "aider";
6
13
  return AIRuleType2;
7
14
  })(AIRuleType || {});
8
15
 
@@ -1 +1 @@
1
- {"version":3,"file":"types.esm.js","sources":["../src/types.ts"],"sourcesContent":["export enum AIRuleType {\n CURSOR = 'cursor',\n COPILOT = 'copilot', \n CLINE = 'cline',\n CLAUDE_CODE = 'claude-code',\n}\n\nexport interface CursorRule {\n type: AIRuleType.CURSOR;\n id: string;\n filePath: string;\n fileName: string;\n gitUrl?: string;\n description?: string;\n globs?: string[];\n alwaysApply?: boolean;\n frontmatter?: Record<string, any>;\n content: string;\n}\n\nexport interface CopilotRule {\n type: AIRuleType.COPILOT;\n id: string;\n filePath: string;\n fileName: string;\n gitUrl?: string;\n content: string;\n order?: number; // Position in the file (legacy)\n title?: string;\n applyTo?: string;\n frontmatter?: Record<string, any>;\n}\n\nexport interface ClineRule {\n type: AIRuleType.CLINE;\n id: string;\n filePath: string;\n fileName: string;\n gitUrl?: string;\n content: string;\n title?: string;\n sections?: Array<{\n title: string;\n content: string;\n }>;\n}\n\nexport interface ClaudeCodeRule {\n type: AIRuleType.CLAUDE_CODE;\n id: string;\n filePath: string;\n fileName: string;\n gitUrl?: string;\n content: string;\n title?: string;\n}\n\nexport type AIRule = CursorRule | CopilotRule | ClineRule | ClaudeCodeRule;\n\nexport interface AIRulesResponse {\n rules: AIRule[];\n totalCount: number;\n ruleTypes: AIRuleType[];\n}\n\nexport interface AIRulesConfig {\n allowedRuleTypes?: AIRuleType[];\n}"],"names":["AIRuleType"],"mappings":"AAAO,IAAK,UAAA,qBAAAA,WAAAA,KAAL;AACL,EAAAA,YAAA,QAAA,CAAA,GAAS,QAAA;AACT,EAAAA,YAAA,SAAA,CAAA,GAAU,SAAA;AACV,EAAAA,YAAA,OAAA,CAAA,GAAQ,OAAA;AACR,EAAAA,YAAA,aAAA,CAAA,GAAc,aAAA;AAJJ,EAAA,OAAAA,WAAAA;AAAA,CAAA,EAAA,UAAA,IAAA,EAAA;;;;"}
1
+ {"version":3,"file":"types.esm.js","sources":["../src/types.ts"],"sourcesContent":["export enum AIRuleType {\n CURSOR = 'cursor',\n COPILOT = 'copilot',\n CLINE = 'cline',\n CLAUDE_CODE = 'claude-code',\n WINDSURF = 'windsurf',\n ROO_CODE = 'roo-code',\n CODEX = 'codex',\n GEMINI = 'gemini',\n AMAZON_Q = 'amazon-q',\n CONTINUE = 'continue',\n AIDER = 'aider',\n}\n\nexport interface CursorRule {\n type: AIRuleType.CURSOR;\n id: string;\n filePath: string;\n fileName: string;\n gitUrl?: string;\n description?: string;\n globs?: string[];\n alwaysApply?: boolean;\n frontmatter?: Record<string, any>;\n content: string;\n}\n\nexport interface CopilotRule {\n type: AIRuleType.COPILOT;\n id: string;\n filePath: string;\n fileName: string;\n gitUrl?: string;\n content: string;\n order?: number;\n title?: string;\n applyTo?: string;\n frontmatter?: Record<string, any>;\n}\n\nexport interface ClineRule {\n type: AIRuleType.CLINE;\n id: string;\n filePath: string;\n fileName: string;\n gitUrl?: string;\n content: string;\n title?: string;\n sections?: Array<{\n title: string;\n content: string;\n }>;\n}\n\nexport interface ClaudeCodeRule {\n type: AIRuleType.CLAUDE_CODE;\n id: string;\n filePath: string;\n fileName: string;\n gitUrl?: string;\n content: string;\n title?: string;\n}\n\nexport interface WindsurfRule {\n type: AIRuleType.WINDSURF;\n id: string;\n filePath: string;\n fileName: string;\n gitUrl?: string;\n content: string;\n title?: string;\n}\n\nexport interface RooCodeRule {\n type: AIRuleType.ROO_CODE;\n id: string;\n filePath: string;\n fileName: string;\n gitUrl?: string;\n content: string;\n title?: string;\n mode?: string;\n}\n\nexport interface CodexRule {\n type: AIRuleType.CODEX;\n id: string;\n filePath: string;\n fileName: string;\n gitUrl?: string;\n content: string;\n title?: string;\n}\n\nexport interface GeminiRule {\n type: AIRuleType.GEMINI;\n id: string;\n filePath: string;\n fileName: string;\n gitUrl?: string;\n content: string;\n title?: string;\n}\n\nexport interface AmazonQRule {\n type: AIRuleType.AMAZON_Q;\n id: string;\n filePath: string;\n fileName: string;\n gitUrl?: string;\n content: string;\n title?: string;\n}\n\nexport interface ContinueRule {\n type: AIRuleType.CONTINUE;\n id: string;\n filePath: string;\n fileName: string;\n gitUrl?: string;\n content: string;\n title?: string;\n alwaysApply?: boolean;\n frontmatter?: Record<string, any>;\n}\n\nexport interface AiderRule {\n type: AIRuleType.AIDER;\n id: string;\n filePath: string;\n fileName: string;\n gitUrl?: string;\n content: string;\n title?: string;\n}\n\nexport type AIRule =\n | CursorRule\n | CopilotRule\n | ClineRule\n | ClaudeCodeRule\n | WindsurfRule\n | RooCodeRule\n | CodexRule\n | GeminiRule\n | AmazonQRule\n | ContinueRule\n | AiderRule;\n\nexport interface AIRulesResponse {\n rules: AIRule[];\n totalCount: number;\n ruleTypes: AIRuleType[];\n}\n\nexport interface AIRulesConfig {\n allowedRuleTypes?: AIRuleType[];\n}\n\n// ─── Ignore Files ─────────────────────────────────────────────────────────────\n\nexport interface IgnoreFile {\n agent: string;\n filePath: string;\n content: string;\n gitUrl?: string;\n}\n\nexport interface IgnoreFilesResponse {\n files: IgnoreFile[];\n totalCount: number;\n}\n\n// ─── Agent Configs ────────────────────────────────────────────────────────────\n\nexport interface AgentConfig {\n agent: string;\n filePath: string;\n content: string;\n language: 'yaml' | 'json' | 'typescript';\n gitUrl?: string;\n}\n\nexport interface AgentConfigsResponse {\n configs: AgentConfig[];\n totalCount: number;\n}\n\n// ─── Agent Skills ─────────────────────────────────────────────────────────────\n\nexport interface AgentSkill {\n name: string;\n description: string;\n source: 'cross-client' | 'claude' | 'cursor';\n sourceDirectory: string;\n filePath: string;\n content: string;\n license?: string;\n compatibility?: string;\n metadata?: Record<string, string>;\n allowedTools?: string[];\n resources: {\n scripts: string[];\n references: string[];\n assets: string[];\n };\n gitUrl?: string;\n}\n\nexport interface SkillsResponse {\n skills: AgentSkill[];\n totalCount: number;\n}\n"],"names":["AIRuleType"],"mappings":"AAAO,IAAK,UAAA,qBAAAA,WAAAA,KAAL;AACL,EAAAA,YAAA,QAAA,CAAA,GAAS,QAAA;AACT,EAAAA,YAAA,SAAA,CAAA,GAAU,SAAA;AACV,EAAAA,YAAA,OAAA,CAAA,GAAQ,OAAA;AACR,EAAAA,YAAA,aAAA,CAAA,GAAc,aAAA;AACd,EAAAA,YAAA,UAAA,CAAA,GAAW,UAAA;AACX,EAAAA,YAAA,UAAA,CAAA,GAAW,UAAA;AACX,EAAAA,YAAA,OAAA,CAAA,GAAQ,OAAA;AACR,EAAAA,YAAA,QAAA,CAAA,GAAS,QAAA;AACT,EAAAA,YAAA,UAAA,CAAA,GAAW,UAAA;AACX,EAAAA,YAAA,UAAA,CAAA,GAAW,UAAA;AACX,EAAAA,YAAA,OAAA,CAAA,GAAQ,OAAA;AAXE,EAAA,OAAAA,WAAAA;AAAA,CAAA,EAAA,UAAA,IAAA,EAAA;;;;"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@terasky/backstage-plugin-ai-rules",
3
3
  "description": "AI Rules Visualizer Plugin for Backstage",
4
- "version": "1.5.0",
4
+ "version": "1.7.0",
5
5
  "main": "./dist/index.esm.js",
6
6
  "types": "./dist/index.d.ts",
7
7
  "license": "Apache-2.0",
@@ -38,11 +38,11 @@
38
38
  },
39
39
  "dependencies": {
40
40
  "@backstage/catalog-model": "^1.7.6",
41
- "@backstage/core-components": "^0.18.6",
42
- "@backstage/core-plugin-api": "^1.12.2",
43
- "@backstage/frontend-plugin-api": "^0.13.4",
44
- "@backstage/plugin-catalog-react": "^1.21.6",
45
- "@backstage/theme": "^0.7.1",
41
+ "@backstage/core-components": "^0.18.7",
42
+ "@backstage/core-plugin-api": "^1.12.3",
43
+ "@backstage/frontend-plugin-api": "^0.14.1",
44
+ "@backstage/plugin-catalog-react": "^2.0.0",
45
+ "@backstage/theme": "^0.7.2",
46
46
  "@material-ui/core": "^4.12.4",
47
47
  "@material-ui/icons": "^4.11.3",
48
48
  "@material-ui/lab": "4.0.0-alpha.61"
@@ -51,14 +51,14 @@
51
51
  "react": "^16.13.1 || ^17.0.0 || ^18.0.0"
52
52
  },
53
53
  "devDependencies": {
54
- "@backstage/cli": "^0.35.3",
55
- "@backstage/core-app-api": "^1.19.4",
56
- "@backstage/dev-utils": "^1.1.19",
57
- "@backstage/test-utils": "^1.7.14",
54
+ "@backstage/cli": "^0.35.4",
55
+ "@backstage/core-app-api": "^1.19.5",
56
+ "@backstage/dev-utils": "^1.1.20",
57
+ "@backstage/test-utils": "^1.7.15",
58
58
  "@testing-library/jest-dom": "^6.0.0",
59
59
  "@testing-library/react": "^14.0.0",
60
60
  "@testing-library/user-event": "^14.0.0",
61
- "msw": "^1.0.0",
61
+ "msw": "^2.12.4",
62
62
  "typescript": "^5.8.3"
63
63
  },
64
64
  "files": [